# 1、webpack 基础用法
# webpack 是什么
前端的工程化涉及:
- 如何使用模块化来组织代码? esm cmd
- 转义预处理器 less sass postcss 转成 css
- es6+ => es5
- 开启开发服务环境,便于调试;
- 上线前要进行代码压缩,性能优化相关
- 为团队约定代码风格
- 集成单元测试
- 集成 CI/CD
- ...
webpack 本质就是一个模块打包构建工具,它会从⼊口模块出发,识别出源码中的模块化导⼊语句,递归地找出⼊口⽂件的所有依赖,将⼊口和其所有的依赖打包到⼀个单独的⽂件中
# 安装
不推荐使用全局安装!!!
# 安装webpack V4+版本时,需要额外安装webpack-cli
npm install webpack webpack-cli -D
# 安装指定版本
npm install webpack@<version> -D
# 检查版本
webpack -v
# 卸载
npm uninstall webpack webpack-cli
# 启动 webpack 执行构建
- 方式 1:
npx webpack
- 如果直接命令行 webapck,因为没有全局安装,则会报错
- 使用 npx,则会到当前项目下的 node_modules 中去查找 webpack 执行
# npx帮助我们在项⽬目中的node_modules⾥里里查找webpack npx webpack -v # ./node_modules/.bin/webpack -v
- 方式 2
- 指定 pkg 命令脚本
# webpack 配置文件
零配置:webpack4 号称 0 配置启动,但其实是个坑,限制太多了,根本不能满足日常的开发需求,还是需要自己动手配置
配置文件叫 webpack.config.js,不写,其也会默认内置。
// 默认配置
const path = require("path");
module.exports = {
// 必填,webpack执行构建入口
entry: "./src/index.js",
output: {
// 将所有依赖的模块合并输出到main.js
filename: "main.js",
// 输出文件的存储路径,必须是绝对路径
path: path.resolve(__dirname, "./dist")
}
};
指定配置文件读取位置:
"dev": "webpack --config ./config/webpack.test.js"
# 基础配置
module.exports = {
entry: "./src/index.js", //打包⼊口⽂件 output: "./dist", //输出结构
mode: "production", //打包环境
module: {
rules: [
//loader模块处理理
{
test: /\.css$/,
use: "style-loader"
}
]
},
plugins: [new HtmlWebpackPlugin()] //插件配置 };
};
# mode
Mode ⽤来指定当前的构建环境:
- production
- development
- none 设置 mode 可以⾃动触发 webpack 内置的函数,达到优化的效果
# entry
指定 webpack 打包⼊口⽂件:Webpack 执行构建的第⼀步将从 Entry 开始,可抽象成输入。有 3 种指定方式:
- 字符串:只会产出 1 个打包后的 bundle 文件,里面包含了 index.js 的代码片段(chunk)
// 单⼊口 SPA,本质是个字符串 entry: { main: "./src/index.js"; } // 相当于简写,打包出 main.js entry: "./src/index.js";
- 数组:也只会生成 1 个 bundle,但里面包含了 a.js、b.js 的代码片段(chunk)
entry: ["./src/a.js", "./src/b.js"];
- 对象:
// 多⼊口 entry是个对象 entry: { index: "./src/index.js", login: "./src/login.js" }
# output
打包转换后的⽂件输出到磁盘位置:输出结果,在 Webpack 经过⼀系列处理并得出最终想要的代码后输出结果。
output: {
filename: "bundle.js", // 输出⽂件的名称
path: path.resolve(__dirname, "dist") // 输出⽂件到磁盘的⽬录,必须是绝对路径
},
// 多⼊口的处理
output: {
filename: "[name][chunkhash:8].js", // 利用占位符,⽂件名称不要重复
path: path.resolve(__dirname, "dist") // 输出⽂件到磁盘的⽬录,必须是绝对路径
},
filename,是指打包生成的入口文件的名称,而 chunkFilename,是入口文件的 bundle 中引入的其他 bundle 的名称。
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].js',
chunkFilename: '[name].chunk,js'
},
# module、chunk、bundle3 者的关系
module
在开发中的所有的资源(.js、.css、.png)都是 module,是 webpack 打包前的概念。chunk
是 webpack 在进行模块的依赖分析的时候,代码分割出来的代码块。一个 chunk 可能包含若干 module。bundle
最终输出到用户端的 chunk,被称之为 bundle;- 一般一个 chunk 对应一个 bundle
- 只有在配置了 sourcemap 时,才会出现一个 chunk 对应多个 bundle 的情况。
- 而在 entry 指定数组,多个 chunk 会打包到一个 bundle 中。
- 一般一个 chunk 对应一个 bundle
# webpackBootstrap 启动函数
打包后的代码是 webpackBootstrap 启动函数,实际是一个自执行函数:
- webapckBootstrap 函数,定义了 webpack_require ,帮助我们处理模块化语句
- webapckBootstrap 函数,接收一个对象,即打包的文件代码
/******/ (function(modules) {
// webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {}; // The require function
/******/
/******/ /******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if (installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/
} // Create a new module (and put it into the cache)
/******/ /******/ var module = (installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/
}); // Execute the module function
/******/
/******/ /******/ modules[moduleId].call(
module.exports,
module,
module.exports,
__webpack_require__
); // Flag the module as loaded
/******/
/******/ /******/ module.l = true; // Return the exports of the module
/******/
/******/ /******/ return module.exports;
/******/
} // expose the modules object (__webpack_modules__)
/******/
/******/
/******/ /******/ __webpack_require__.m = modules; // expose the module cache
/******/
/******/ /******/ __webpack_require__.c = installedModules; // define getter function for harmony exports
/******/
/******/ /******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if (!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
enumerable: true,
get: getter
});
/******/
}
/******/
}; // define __esModule on exports
/******/
/******/ /******/ __webpack_require__.r = function(exports) {
/******/ if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, {
value: "Module"
});
/******/
}
/******/ Object.defineProperty(exports, "__esModule", { value: true });
/******/
}; // create a fake namespace object // mode & 1: value is a module id, require it // mode & 2: merge all properties of value into the ns // mode & 4: return value when already ns object // mode & 8|1: behave like require
/******/
/******/ /******/ /******/ /******/ /******/ /******/ __webpack_require__.t = function(
value,
mode
) {
/******/ if (mode & 1) value = __webpack_require__(value);
/******/ if (mode & 8) return value;
/******/ if (
mode & 4 &&
typeof value === "object" &&
value &&
value.__esModule
)
return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, "default", {
enumerable: true,
value: value
});
/******/ if (mode & 2 && typeof value != "string")
for (var key in value)
__webpack_require__.d(
ns,
key,
function(key) {
return value[key];
}.bind(null, key)
);
/******/ return ns;
/******/
}; // getDefaultExport function for compatibility with non-harmony modules
/******/
/******/ /******/ __webpack_require__.n = function(module) {
/******/ var getter =
module && module.__esModule
? /******/ function getDefault() {
return module["default"];
}
: /******/ function getModuleExports() {
return module;
};
/******/ __webpack_require__.d(getter, "a", getter);
/******/ return getter;
/******/
}; // Object.prototype.hasOwnProperty.call
/******/
/******/ /******/ __webpack_require__.o = function(object, property) {
return Object.prototype.hasOwnProperty.call(object, property);
}; // __webpack_public_path__
/******/
/******/ /******/ __webpack_require__.p = ""; // Load entry module and return exports
/******/
/******/
/******/ /******/ return __webpack_require__(
(__webpack_require__.s = "./src/index.js")
);
/******/
})(
/************************************************************************/
/******/ {
/***/ "./src/index.js":
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/*! no static exports found */
/***/ function(module, exports) {
eval(
"console.log('lalallal')\n\n//# sourceURL=webpack:///./src/index.js?"
);
/***/
}
/******/
}
);
# loader
- 在 Webpack 里,一切皆模块,一个模块对应着一个⽂件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
- webpack 默认只支持 js、json 格式的文件,
- 因此,当 webpack 处理到不认识的模块时,需要在 webpack 中的 module 处进行配置,当检测到是什么格式的模块,就使用什么 loader 来处理。
loader 执行顺序是:自后往前,自下向上
css-loader 分析 css 模块之间的关系,并合成⼀个 css,将其转成 js 字符串放到 bundle 文件中去,style-loader 会从 bundle 文件中把 css 的字符串提取出来,在 html 文件头部创建 style 标签,将 css 字符串放进去,从而让浏览器可以解析 css。
{
test: /\.css$/,
use: [
{
loader: "style-loader",
options: {
injectType: "singletonStyleTag" // 将所有的style标签合并成⼀个
},
},
"css-loader"
]
}
# plugin
plugin 可以在 webpack 运行到某个阶段的时候,帮你做⼀些事情,类似于⽣命周期的概念
扩展插件,在 Webpack 构建流程中的特定时机注⼊入扩展逻辑,来改变构建结果或做你想要的事情。
作⽤于整个构建过程
- HtmlWebpackPlugin:会在打包结束后,⾃动⽣成⼀个 html ⽂件,并把打包⽣成的 js 模块引⼊到该 html 中。
- clean-webpack-plugin