webpack 是什么?
它是一个模块打包工具
Loader 是什么?
webpack 不能识别 JavaScript 之外的文件,需要 loader 对它时行识别
file-loader
file-loader 就是在 JavaScript 代码里 import/require 一个文件时,会将该文件生成到输出目录,并且在 JavaScript 代码里返回该文件的地址。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | module.exports = {module: {
 rules: [
 {
 test: /\.(png|jpg|jpeg|gif|svg)/,
 use: {
 loader: "file-loader",
 options: {
 
 name: "[name].[ext]",
 outputPath: "images/",
 },
 },
 },
 ],
 },
 };
 
 | 
url-loader
url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | module.exports = {module: {
 rules: [
 {
 test: /\.(png|jpg|gif)$/,
 use: [
 {
 loader: "url-loader",
 options: {
 limit: 8192,
 },
 },
 ],
 },
 ],
 },
 };
 
 | 
处理 Sass
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | module.exports = {module: {
 rules: [
 {
 test: /\.scss$/,
 use: [
 
 {
 loader: "style-loader",
 },
 {
 loader: "css-loader",
 },
 {
 loader: "sass-loader",
 },
 {
 loader: "postcss-loader",
 options: {
 plugins: [require("autoprefixer")({})],
 },
 },
 ],
 },
 ],
 },
 };
 
 | 
处理 CSS
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | module.exports = {module: {
 rules: [
 {
 test: /\.(css)$/,
 use: [
 {
 loader: "style-loader",
 },
 { loader: "css-loader" },
 ],
 },
 ],
 },
 };
 
 | 
Plugins 是什么?
在 webpack 运行到某个时刻的时做一些事情
HtmlWebpackPlugin
会在打包结束后,自动生成一个 html 文件,并把打包生成的 JS 文件自动引入到这个 html 文件里
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | var HtmlWebpackPlugin = require("html-webpack-plugin");var path = require("path");
 
 module.exports = {
 entry: "index.js",
 output: {
 path: path.resolve(__dirname, "./dist"),
 filename: "index_bundle.js",
 },
 plugins: [
 new HtmlWebpackPlugin({
 template: "./src/index.html",
 }),
 ],
 };
 
 | 
CleanWebpackPlugin
用于在下一次打包时清除之前打包的文件
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | const { CleanWebpackPlugin } = require("clean-webpack-plugin");var path = require("path");
 
 module.exports = {
 entry: "index.js",
 output: {
 path: path.resolve(__dirname, "./dist"),
 filename: "index_bundle.js",
 },
 plugins: [new CleanWebpackPlugin()],
 };
 
 | 
SourceMap
可以将编译后的代码映射回原始源代码,用来追踪到 error(错误) 和 warning(警告) 在源代码中的原始位置。
| 12
 3
 4
 5
 6
 7
 8
 
 | module.exports = {devtool: "source-map",
 };
 
 
 
 
 
 
 | 
最佳实践
| 12
 
 | devtool:"cheap-module-eval-source-map",devtool:"cheap-module-source-map",
 
 | 
devServer
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | let path = require("path");
 module.exports = {
 
 devServer: {
 contentBase: path.join(__dirname, "dist"),
 open: true,
 port: 9000,
 },
 };
 
 | 
Hot Module Replacement
它允许在项目在修改的时候,无需完全刷新页面。
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | const webpack = require("webpack");
 module.exports = {
 devServer: {
 hot: true,
 hotOnly: true,
 },
 plugins: [new webpack.HotModuleReplacementPlugin()],
 };
 
 | 
使用 Bable 编译 ES6
bable 转 ES6 相关:
- babel-loader: 负责 es6 语法转化
- babel-preset-env: 包含 es6、7 等版本的语法转化规则
- babel-polyfill: es6 内置方法和函数转化垫片
- babel-plugin-transform-runtime: 避免 polyfill 污染全局变量
需要注意的是, babel-loader和babel-polyfill。前者负责语法转化,比如:箭头函数;后者负责内置方法和函数,比如:new Set()。
| 12
 
 | npm install --save-dev babel-loader @babel/corenpm install @babel/preset-env --save-dev
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | module: {rules: [
 {
 test: /\.js$/,
 exclude: /node_modules/,
 loader: "babel-loader",
 options: {
 presets: ["@babel/preset-env"],
 },
 },
 ];
 }
 
 | 
如果需要对Promise、map 之类的方法做兼容处理则需要安装babel-polyfill
| 1
 | npm install --save @babel/polyfill
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | module: {rules: [
 {
 test: /\.js$/,
 exclude: /node_modules/,
 loader: "babel-loader",
 options: {
 presets: [
 ["@babel/preset-env"],
 {
 targets: {
 
 edge: "17",
 firefox: "60",
 chrome: "67",
 safari: "11.1",
 },
 useBuiltIns: "usage",
 },
 ],
 },
 },
 ];
 }
 
 | 
注意:使用 "useBuiltIns": "usage"后,不需要在项目里在引入import '@babel/polyfill'
默认polyfill会污染全局变量,如果开发一个第三方模块或者库的时候则需要用@babel/runtime
| 12
 
 | npm install --save @babel/runtimenpm install --save @babel/runtime-corejs2
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | module: {rules: [
 {
 test: /\.js$/,
 exclude: /node_modules/,
 loader: "babel-loader",
 options: {
 plugins: [
 [
 "@babel/plugin-transform-runtime",
 {
 absoluteRuntime: false,
 corejs: 2,
 helpers: true,
 regenerator: true,
 useESModules: false,
 },
 ],
 ],
 },
 },
 ];
 }
 
 | 
如果配置文件较多时,可以项目内新建一个.babelrc,然后把options里的内容放进去
.babelrc
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | {"plugins" : [
 [
 "@babel/plugin-transform-runtime",
 {
 "absoluteRuntime": false,
 "corejs": 2,
 "helpers": true,
 "regenerator": true,
 "useESModules": false
 }
 ]
 ]
 }
 
 | 
wepback.config.js
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | module: {rules: [
 {
 test: /\.js$/,
 exclude: /node_modules/,
 loader: "babel-loader",
 },
 ];
 }
 
 | 
Tree Shaking
清除到代码中无用的js代码,只支持ES Module,也就是只支持import(静态引入)方式引入,不支持CommonJS(动态引入)的方式引入
在mode是production时就会进行Tree Shaking,为了方便你的调试可以在配置文件中加入如下代码:
| 12
 3
 
 | optimization: {usedExports: true;
 }
 
 | 
package.json
| 12
 3
 4
 5
 
 | 
 sideEffects: false
 sideEffects: ["@babel/polyfill"]
 sideEffects: ["*.css"]
 
 | 
Code Splitting
异步的代码 (import)webpack会自动的进行代码分割,同步代码则需要进行如下配置:
| 12
 3
 4
 5
 
 | optimization: {splitChunks: {
 chunks: "all";
 }
 }
 
 | 
SplitChunksPlugin 配置详解
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | module.exports = {
 optimization: {
 splitChunks: {
 chunks: "async",
 minSize: 30000,
 minRemainingSize: 0,
 maxSize: 0,
 minChunks: 1,
 maxAsyncRequests: 6,
 maxInitialRequests: 4,
 automaticNameDelimiter: "~",
 automaticNameMaxLength: 30,
 cacheGroups: {
 
 vendors: {
 
 test: /[\\/]node_modules[\\/]/,
 priority: -10,
 },
 default: {
 
 minChunks: 2,
 priority: -20,
 reuseExistingChunk: true,
 },
 },
 },
 },
 };
 
 | 
如果对代码进行代码分割,它会去看cacheGroups.vendors这个参数,如果是false则不会进行代码分割,反之,如果引入的库是cacheGroups.vendors.test里的,则会代码分割,如果不是cacheGroups.vendors.test里的,会去看cacheGroups.default这个参数,如果是false则不会进行代码分割,反之则会
为什么 splitChunks.chunks 的默认值是async
因为异步的代码才能提高打包的性能,而同步代码则只能增加缓存
CSS Code Splitting
| 1
 | npm install --save-dev mini-css-extract-plugin
 | 
webpack.config.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 module.exports = {
 plugins: [
 new MiniCssExtractPlugin({
 filename: "[name].css",
 chunkFilename: "[id].css",
 }),
 ],
 module: {
 rules: [
 {
 test: /\.css$/,
 use: [
 {
 loader: MiniCssExtractPlugin.loader,
 options: {
 publicPath: "../",
 },
 },
 "css-loader",
 ],
 },
 ],
 },
 };
 
 | 
prefetch/preload module
在声明 import 时,使用下面这些内置指令,可以让 webpack 输出 “resource hint(资源提示)”,来告知浏览器:
- prefetch(预取):当核心代业务代码加载完成后,空闲时候后在加载需要的资源
- preload(预加载):和核心代业务代码一起进行并行加载
下面这个 prefetch 的简单示例中,有一个 HomePage 组件,其内部渲染一个 LoginButton 组件,然后在点击后按需加载 LoginModal 组件。
LoginButton.js
这会生成 <link rel="prefetch" href="login-modal-chunk.js"> 并追加到页面头部,指示着浏览器在闲置时间预取 login-modal-chunk.js 文件。