共计 16577 个字符,预计需要花费 42 分钟才能阅读完成。
1. 为什么应用 Webpack(利用场景)
- 反对新个性语言版本的编译
- 针对 javascript 模块化打包
- 针对所有资源,例如款式、图片、字体等进行模块化
对于 1、2 两点,grunt、gulp 等构建工具能够很好的解决,然而无奈解决第 3 点。Webpack 可能解决前端整体的模块化能力。
2. 具备的能力
- 模块打包器(Module bundler)—— 自身反对 js 模块化
- 加载器(loader)—— 利用 babel 等进行语言个性编译,转换
- 代码拆分(Code Splitting)—— 文件按需加载
- 资源模块(Asset Module)—— 反对加载 css、字体等资源
3. 根本配置
3.1 装置
npm install webpack --save-dev | |
npm install webpack-cli --save-dev |
3.2 入口文件:webpack.config.js
配置:mode
- production 生产模式下,Webpack 会主动优化打包后果;(例如:代码的压缩混同等)
- development 开发模式下,Webpack 会主动优化打包速度,增加一些调试过程中的辅助;
- none 模式下,Webpack 就是运行最原始的打包,不做任何额定解决;
配置:entry
entry 能够是相对路径,也能够是绝对路径
// entry 能够是多入口 | |
entry: { | |
index: './src/pages/index/index.js', | |
album: './src/pages/album/album.js', | |
work: './src/pages/work/work.js' | |
}, |
配置:output
output 必须是绝对路径
entry: {index: './src/main.js'}, | |
output: {filename: '[name]-[contenthash:8].bundle.js',// contenthash:8 最罕用,用于防止缓存问题 | |
path: path.join(__dirname, 'dist') | |
}, |
配置:cache
是否关上构建缓存
3.3 资源加载逻辑
能够将 js 作为入口文件,在 js 中 import css 等资源
- js 驱动整个前端利用
- 合乎资源加载逻辑,js 须要这些资源
- 保障前端我的项目的开发资源不缺失
webpack 兼容的几种模块化规范:ES Modules、AMD、COmmonJS
webpack 加载模块的几种形式:
- @import,@import(css)文件时
- css 中的 background:url()函数
- html 中 src 属性、a 标签的 href(须要配置)
{ | |
test: /.html$/, | |
use: { | |
loader: 'html-loader', | |
options: {attrs: ['img:src', 'a:href'] | |
} | |
} | |
} |
3.4 loader/plugin 比照,自定义实现 loader/plugin
- loader:用于资源加载并解决各种语言的转换 / 编译(例如将 es6+/ts 转换为 js,css 加载等);
- plugin:用于资源加载以外的其余打包 / 压缩 / 文件解决等性能;
参考文章:前端工程化 7
3.5 loader 加载资源文件
loader:大抵分为三类
- 编译转换
- 文件操作
- 代码查看
loader:css 款式编译(留神 loader 程序,css-loader 第一个执行)
- 依赖:
> npm install css-loader --save-dev | |
> npm install style/core --save-dev |
- 配置:
module: { | |
rules: [ | |
{ | |
test: /.css$/, // 匹配文件 | |
use: [// use 指定应用到的 loader | |
'style-loader', // 将 css-loader 转换后的后果放到 style 标签外面 | |
'css-loader' // 先执行 css-loader,而且是从后往前执行,所以须要放到上面 | |
] | |
} | |
] | |
} |
loader:编译 ES6+ => babel
- 依赖:
> npm install babel-loader --save-dev => babel 转换平台 | |
> npm install @babel/core --save-dev => babel 外围模块 | |
> npm install @babel/preset-env --save-dev => babel 转换语言包,env 示意 es6+ 全量包 |
- 配置:
- babel-loader:
{// 转换 js 代码,es6+=>es5 | |
test: /\.js$/, | |
exclude: /node_modules/, | |
use: 'babel-loader' | |
}, |
- babel.config.js(配置能够提取进去:.babelrc、.babelrc.js、babel.config.js、package.json 文件):
// .babelrc 只会影响本我的项目中的代码;babel.config.js 会影响整个我的项目中的代码,蕴含 node_modules 中的代码 | |
// 举荐应用:babel.config.js | |
module.exports = { | |
presets: ['@babel/preset-env'] | |
} |
- babel 罕用相干插件阐明:
> babel-loader => webpack 中转换 babel 的工具,相当于一个平台不做具体的工作 | |
> @babel/core => babel 转换语言的外围性能,外围 api 等 | |
> @babel/preset-env => babel 转换语言的内容包,包含 es6+ 所有个性 | |
// 以下内容待确认!> @babel/cli => 使 node 环境反对 es6 语法;@babel/core 也能是 node 环境反对 es6 语法;> @babel/polyfill =>(一些全局办法和变量)Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API,比方 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的办法(比方 Object.assign)都不会转码。举例来说,ES6 在 Array 对象上新增了 Array.from 办法。Babel 就不会转码这个办法。如果想让这个办法运行,必须应用 babel-polyfill,为以后环境提供一个垫片。babel-polyfill 相对来说比拟大。> @babel/runtime => 如果不想用 babel-polyfill 净化全局环境,就是用 babel-runtime+babel-plugin-transform-runtime;> @babel/plugin-transform-runtime => babel-plugin-transform-runtime 依赖于 babel-runtime | |
> @babel/eslint-parser => babel + eslint 的解析器(如同也没有应用?)> 插件名调换:@babel/core === 等同于 babel-core |
loader:文件 / 图片解决
- 依赖:
> npm install file-loader --save-dev | |
> npm install url-loader --save-dev |
- 性能:
- file-loader 一般解决
{// 将图片文件复制到另一个目录 | |
test: /\.(png|svg|jpg|gif)$/, | |
loader: 'file-loader', | |
options: {name: '[name]-[contenthash:8].[ext]', | |
outputPath: 'images', | |
esModule: false // 新版 loader 须要配置,否则会产生谬误:img src=[object Module] | |
}, | |
}, |
- url-loader 和 file-loader 二选一
url-loader 对 file-loader 有依赖,须要提前装置;
url-loader 将图片编码为 Base64 文件:对于小文件座大小限度进行解决,缩小申请次数。
{// 将小于 10kb 的图片编码成 base64 | |
test: /\.(png|svg|jpg|gif)$/, | |
use: { | |
loader: 'url-loader', // url-loader 对 file-loader 有依赖,须要提前装置 | |
options: {name: '[name]-[contenthash:8].[ext]', | |
outputPath: 'images', | |
limit: 10 * 1024, // 10 KB 对文件大小进行限度,超过就不转换 | |
esModule: false // 新版 loader 须要配置,否则会产生谬误:img src=[object Module] | |
}, | |
} | |
} |
loader:html 解决(用得少,应用其余插件代替)
依赖:
> npm install html-loader --save-dev
3.6 plugins 插件解决其余工作
plugin:主动生成 html 文件插件
- 依赖:html-webpack-plugin
- 性能:
- 根底配置
// 用于生成 index.html | |
new HtmlWebpackPlugin({ | |
title: 'index 首页', | |
meta: {viewport: 'width=device-width'}, | |
minify: {removeAttributeQuotes: true // 移除属性的引号}, | |
inject: true, // script 是否至于 body 底部 | |
template: './src/templates/index.html', | |
filename: 'index.html', | |
// cache: false, | |
chunks: ['index'], // 指定加载 js 文件,默认全副加载 | |
// showErrors: true, // 如果 webpack 编译呈现谬误,webpack 会将错误信息包裹在一个 pre 标签内,属性的默认值为 true,也就是显示错误信息。}), |
- 在 html 模板中应用变量:
<title>Home - <%= htmlWebpackPlugin.options.title %></title>
plugin:依据目录主动生成 html 文件
- 依赖:auto-web-plugin
plugin:主动革除输入目录插件
- 依赖:clean-webpack-plugin
plugin:拷贝文件的插件
- 依赖:copy-webpack-plugin
const {CleanWebpackPlugin} = require('clean-webpack-plugin') | |
const CopyWebpackPlugin = require('copy-webpack-plugin') | |
module.exports = merge(common, { | |
mode: 'production', | |
plugins: [new CleanWebpackPlugin(), | |
new CopyWebpackPlugin({ | |
patterns: [{ from: "./public/*.ico", to: "./"}, | |
], | |
}) | |
] | |
}) |
4. 加强体验
4.1、热更新
计划 1:webpack + browser-sync(本地磁盘可见文件)
- 依赖:
> npm install browser-sync --save-dev
- 性能:
- 主动编译,监控源文件;应用 webpack 自带配置
// 1、npm scripts 中 => webpack 携带命令行参数或者增加配置项 | |
> webpack --watch | |
// 2、webpack.config.js => webpack 配置项 | |
watch: true, // 监听所有文件的更改,主动构建主动刷新浏览器 |
- 主动刷新浏览器,监控打包文件;应用工具 browser-sync
> npm install browser-sync --save-dev
"scripts": { | |
"dev": "webpack --config webpack.dev.js", // 配置项配了 watch 就能够不带参数 --wacth | |
"sync": "browser-sync dist --files'**/*'", // 带参数 --files | |
} |
- 须要同时启用两个命令行:
webpack-sample> npm run dev | |
webpack-sample> npm run sync |
计划 2:webapck-dev-server 工具(内存中看不到文件)
- 依赖:
> npm install webapck-dev-server --save-dev
- 性能:集成了主动编译和主动刷新浏览器,留神 watch 和 target 的配置
- 根本配置 :
1. contentBase => 根本门路 | |
2. open => 主动关上浏览器 | |
3. host => 虚拟主机地址 | |
4. port => 端口号 | |
5. proxy(代理)=> 代理配置 |
- 残缺示例:
// watch: true, // 热更新计划 1:监听源码文件更改主动构建,监听磁盘文件主动刷新浏览器(配合 browser-sync 刷新浏览器)target: "web", // webpack5 的一个 bug,须要关上能力主动刷新浏览器,参考:https://blog.csdn.net/xiaolongbaobushibao/article/details/116664883 | |
// 应用 devServer 是不必关上 watch | |
// 应用 devServer 常见不刷新浏览器状况:devServer: { // 热更新计划 2:本地开发服务器,监听源码文件主动构建和刷新浏览器,打包后果存在内存而不是磁盘中 | |
host: '127.0.0.1', | |
port: '8080', | |
// hot: true, // 模块热替换,如果热替换失败就主动回到页面主动刷新 | |
// hotOnly: true, // 模块热替换,如果热替换失败不会主动刷新页面 | |
contentBase: path.resolve(__dirname,'dist'),// 指定动态资源门路的根目录,须要对应 output:path 为 dist,open: true, // 主动关上浏览器 | |
// watchContentBase: true, | |
inline: true | |
// proxy: { // 配置代理,避免跨域问题 | |
// '/api': { | |
// target: 'https://api.github.com', // http://localhost:8080/api/users -> https://api.github.com/api/users | |
// pathRewrite: { // http://localhost:8080/api/users -> https://api.github.com/users | |
// '^/api': '' | |
// }, | |
// // 不能应用 localhost:8080 作为申请 GitHub 的主机名 | |
// changeOrigin: true | |
// } | |
// } | |
}, |
- webpack dev server 不能只能刷新浏览器的问题
- webpack5 的一个 bug,参考:https://blog.csdn.net/xiaolongbaobushibao/article/details/116664883
// webpack.config.js 中增加配置项 | |
target: "web", |
- 提取了专用代码,一些配置门路谬误等问题;例如:查看 optimization、path、publicPath、contentBase 等配置项
热更新最佳实际(集体认为):
- 本地开发最后调试阶段(dev)=> 应用 webpack-dev-server,只有浏览器内存能看到代码
-
本地测试或者联调阶段(dev/pre)=> 应用 webpack + browser-sync,能看到本地代码
因为开发阶段很多文件没有合并压缩,跟生产环境差别太大。用 webpack-dev-server 有些货色测不进去。
- 预上线、生产上线阶段(sit、prd)=> 应用文件合并、压缩过后的残缺代码
4.2、模块热加载
webapck-dev-server 中 HMR 模块的热更新:
注意事项:
- HMR 须要新增一些代码,用来手动解决 JS 模块的热替换、图片模块热替换等(因为不同的业务场景须要解决的数据不同)。
- HMR 新增代码与业务无关,所以会减少肯定工作量;而且写起来比拟麻烦,倡议联合框架应用欠缺的 HMR 计划。
- HMR 新增代码与业务无关,然而在 webpack 打包过后其实是删除了的,对生产环境没有影响。
应用场景:
- 在页面中有一个编辑器,在编辑器中输出内容而后再批改款式。webapck-dev-server 负责刷新整体页面,如果每次批改了款式后页面刷新,编辑器中的内容就会失落。HMR 插件能实现在页面不刷新的状况下,部分模块的更新。
- 在开发过程中频繁批改页面某个模块的款式,例如:背景图片。
如何应用:
- 命令行参数:webpack-dev-server –hot
- 配置文件:
// 1、引入 webpack | |
const webpack = require('webpack') | |
// 2、plugins 外面增加内置插件:plugins: [new webpack.HotModuleReplacementPlugin() | |
... | |
], | |
// 3、devServer 中增加配置项:// 留神:在热替换手动解决 js 热替换,如果报错热替换会启动失败;// 留神:应用 hotOly,热替换失败也不会主动刷新页面。devServer: { | |
// 可能呈现的谬误 1:解决的热替换模块有谬误,应用 hotOnly 配置 | |
// hot: true, // 模块热替换,如果热替换失败就主动回到页面主动刷新 | |
hotOnly: true, // 模块热替换,如果热替换失败不会主动刷新页面便于定位问题 | |
... | |
} |
解决 JS 模块的热替换
- webpack 中的热替换 api:module.hot.accept 用于注册模块处理函数
- 示例场景:批改编辑器模块代码后,热更新编辑器模块
// 可能呈现的谬误 2:plugins 没有开启配置会报错,须要在热替换代码中对 module.hot 进行判断 | |
if (module.hot) { | |
let lastEditor = editor | |
// 1、编辑器的热替换 | |
// 参数 1:模块 | |
// 参数 2:处理函数 => 实现的性能:简略来说就是用 js 代码更新一下模块内须要更新的内容(重要),例如上面更新了编辑器。module.hot.accept('./editor', () => { | |
// 移除原来编辑器,记录数据 | |
const value = lastEditor.innerHTML | |
document.body.removeChild(lastEditor) | |
// 更新原来的数据到新的编辑器 | |
const newEditor = createEditor() | |
newEditor.innerHTML = value | |
document.body.appendChild(newEditor) | |
lastEditor = newEditor | |
}) | |
// 2、图片的热替换 | |
module.hot.accept('./better.png', () => { | |
img.src = background | |
console.log(background) | |
}) | |
} |
- 可能呈现的谬误:
- 可能呈现的谬误 1:解决的热替换模块有谬误,应用 hotOnly 配置
- 可能呈现的谬误 2:plugins 没有开启配置会报错,须要在热替换代码中对 module.hot 进行判断
解决图片模块的热替换
// 2、图片的热替换 | |
module.hot.accept('./better.png', () => { | |
img.src = background | |
console.log(background) | |
}) |
VUE + HMR 计划
参考:[本人网上找]()
REACT + HMR 计划
参考:[本人网上找]()
4.3、配置 Source Map(便于查看调试代码)
记录代码转换前后的映射关系
Source Map 配置计划
webpack 中的 devTool,依照以下 3 中规定配置:
- eval => 应用 eval 执行模块代码,没有生成对应的.map 文件;只能看到谬误对应的文件名称;
- source-map => 蕴含谬误的行列信息
- cheap => 不蕴含谬误的列信息
- module => 不要 loader 解决源码
- inline => 将.map 文件以 data-url 的模式生成放到 url 中,很占体积
带 eval 不生成.map 文件:
- eval:构建速度最快,只能定位谬误文件是哪个;
- eval-source-map:谬误文件名称 + 谬误的行列;
- cheap-eval-source-map:谬误文件名称 + 谬误的行;
- cheap-module-eval-source-map:谬误文件名称 + 谬误的行 + 代码未被 loader 解决;
不带 eval 都生成了.map 文件:
- source-map:谬误文件名称 + 谬误的行列;
- cheap-source-map:谬误文件名称 + 谬误的行 + 代码未被 loader 解决;
- hidden-source-map:代码中看不见 source-map 源码(适宜开发第三方工具,须要的时候再应用);
- nosources-source-map:看不到源码,只能看到谬误行列号(生产环境能够爱护源码);
带 inline 不生成.map 文件,将其放到 url 中(不举荐应用 inline):
- inline-source-map:将 source-map 以 data-url 形式嵌入到 url 中,导致代码体积变大;
最佳实际:
-
开发模式:eval-cheap-module-source-map//webpack5 中这几个单词程序不能变,前提是:
1、代码每行不超过 80 字符
2、Loader 转换过后代码差别大,所以须要看源码
3、这种模式启动慢,然而从新打包速度快
4、留神:webpack5 中对这几个单词程序有要求,必须是:- ^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$
- eval-cheap-module-source-map => ok
- cheap-module-eval-source-map => old 这种就会报错
-
生产模式:none、nosources-source-map
1、这样能够防止裸露源码
4.4、配置门路别名
alias: {'@': resolve('src'), | |
'@config': resolve('config') | |
} |
4.5、配置波及到的门路:publicPath(未完待续,待整顿)
- __dirname
- process.cwd()
- path.join(__dirname, ‘dist’)
- path.resolve(__dirname, ‘dist’)
- ‘./dist’
- ‘/dist/’
// ...
5. 生产环境配置
5.1 依据环境增加不同的配置
计划 1:依据命令行参数判断
如何判断环境: 命令行 –env production 参数会传递给 env;
> webpack --env production
接管参数:
webpack 除了能够间接导出配置;还能够导出一个 function,返回配置:
module.exports = config | |
module.exports = (env, args)=> config |
- 命令行 webpack –env production 参数会传递给 function 的参数 env;
- 能够在 function 中对 env 环境进行判断:
// webpack 提供了一个函数 | |
// 参数 1:env | |
// 参数 2:args | |
// 返回值:配置项 | |
module.exports = (env, args) => { | |
const config = {//...} | |
// env 承受参数:webpack --env production | |
if (env === 'production') { | |
config.mode = 'production' | |
config.devtool = false | |
config.plugins = [ | |
...config.plugins, | |
new CleanWebpackPlugin(), | |
new CopyWebpackPlugin(['public']) | |
] | |
} | |
return config | |
} |
计划 2:提取专用配置到文件,将配置独立到三个文件:=> 最好用
- 依赖:webpack-merge 插件
> npm install webpack-merge --save-dev => 业余用于合并 webpack 配置项对象的插件
- 性能:将配置独立到三个文件,prod、dev 合并专用配置
> webpack.common.js // 专用配置 | |
> webpack.prod.js // 生产环境 | |
> webpack.dev.js // 开发环境 |
- 例如独自配置 prd 环境的 js,合并 webpack.common.js 中的配置
// 合并配置,比拟业余的模块:webpack-merge | |
// 合并配置,最好不要用 Object.assign,它在复制对象时前面的配置会齐全笼罩掉后面的配置 | |
const {merge} = require('webpack-merge') | |
const common = require('./webpack.common.js') | |
const {CleanWebpackPlugin} = require('clean-webpack-plugin') | |
const CopyWebpackPlugin = require('copy-webpack-plugin') | |
module.exports = merge(common, { | |
mode: 'production', | |
plugins: [new CleanWebpackPlugin(), | |
new CopyWebpackPlugin(['public']) | |
] | |
}) |
计划 3:plugin:DefinePlugin,向打包文件中注入环境变量在 webpack 打包时判断
- 依赖:无,webpack 内置插件:webpack.DefinePlugin
- 性能:DefinePlugin 插件能够向 webpack 打包文件中注入 js 代码间接应用,例如:
plugins: [ | |
new webpack.DefinePlugin({ | |
// 值要求的是一个代码片段 | |
TEST_URL: 'https://api.example.com',API_BASE_URL: JSON.stringify('https://api.example.com') | |
}) | |
] |
在源码中应用变量,打包到 bundle.js ==>
// console.log(TEST_URL),留神没有引号,不是字符串 | |
console.log(https://api.example.com) | |
// console.log(API_BASE_URL),所以须要 JSON.stringify() | |
console.log('https://api.example.com') |
- DefinePlugin 配合 process.end.NODE_ENV 应用
- process.end.NODE_ENV 获取执行环境
- process 是在 nodejs 环境中的全局变量,只能在 webpack.config.js 中应用;
- 想要在源码中应用 process 判断环境,须要应用插件:DefinePlugin 将 process 变量注入到打包文件。
- 以下示例,将环境变量注入到源码 boudle.js:process.env.NODE_ENV
new webpack.DefinePlugin({PRODUCTION: JSON.stringify(true), | |
VERSION: JSON.stringify('0.0.1'), | |
API_BASE_URL: JSON.stringify('https://api.example.com') | |
'process.env': {NODE_ENV: JSON.stringify(process.env.NODE_ENV) | |
} | |
}); |
- process.end.NODE_ENV 应用时的兼容性问题,在 npm scripts 中增加:
windows/linux 兼容:
cross-env NODE_ENV=development
windows:
SET NODE_ENV=development
os x/linux:
export NODE_ENV=development
6. 优化打包性能
6.1 Tree Shaking 性能
根本应用
将未应用的代码解决掉,合并模块等性能;没有依赖项。
- 生产环境下主动启用
- 其余环境下开启配置
module.exports = { | |
mode: 'none', | |
entry: './src/index.js', | |
output: {filename: 'bundle.js'}, | |
optimization: { | |
// 模块只导出被应用的成员 | |
usedExports: true, | |
// 压缩输入后果,usedExports 开启后会移除未被应用的成员 | |
// minimize: true, | |
// 尽可能合并每一个模块到一个函数中(Scop Hosting)concatenateModules: true, | |
} | |
} |
Tree Shaking 和 Babel(未完待续)
据说是用 babel 时,tree shaking 会生效?
- tree shaking 前提是 ESM Modules,由 webpack 打包的代码必须是用 EMS
- babel preset-env 这个插件就是将 es6+ 转换为 es5,而且它是将 ESM => 转换为 CommonJS 的形式,导致 tree shaking 生效(Babel 到底是将什么 ESM 转换为 CommonJS,待梳理
~~~) - 最新版本的 Babel 主动敞开了 ESM 转换插件,ESM 并没有转换;所以 tree shaking 能够失常工作。
rules: [ | |
{ | |
test: /\.js$/, | |
use: { | |
loader: 'babel-loader', | |
options: { | |
presets: [ | |
// 如果 Babel 加载模块时曾经转换了 ESM,则会导致 Tree Shaking 生效;最新版本的 Babel 主动敞开了 ESM 转换插件 | |
// ['@babel/preset-env', { modules: 'commonjs'}] // 强制将 ESM 转换为 CommonJS,tree shaking 生效 | |
// ['@babel/preset-env', { modules: false}] // 强制敞开转换插件 | |
// 也能够应用默认配置,也就是 auto,这样 babel-loader 会主动敞开 ESM 转换 | |
['@babel/preset-env', { modules: 'auto'}] | |
] | |
} | |
} | |
} | |
] |
Tree Shaking 与 sideEffects(未完待续,应用场景待钻研)
sideEffects 应用场景:
如果有定义了很多模块(例如多个组件),但实际上只应用了一个;打包的时候须要删除多余的代码,能够用 sideEffects 配置。
- 生产环境下主动启用
- 开发环境下如下配置
sideEffects 配置
webpack.config.js 下的配置
// webpack.config.js 下的配置 | |
optimization: {sideEffects: true,// 关上移除未应用的模块} |
package.json 下的配置:
// package.json 的配置和 webpack 下的配置意义不同,此处只是标识代码没有副作用 | |
"sideEffects": false // 标识代码没有副作用,webpack 在打包时会删掉有副作用的代码 |
6.2 多入口打包 => 解决文件过大的问题
计划 1:多个 html-webpack-plugin
- 依赖:
> npm install html-webpack-plugin --save-dev
- 性能:
entry 定义多个入口并应用多个 HtmlWebpackPlugin() 插件;具体参考示例源码;
计划 2:应用 auto-web-plugin 插件依照目录生成 html
- 依赖:
> npm install auto-web-plugin --save-dev
- 性能:(未完待续)
// ...
6.3 CodeSplit 代码分片,按需加载 => 解决文件过大的问题
- 依赖:无
- 性能:应用动静加载语句:import(‘./components/banner.js’).then(banner => {})
- 实际:通常和路由相结合,不须要其余配置
const render = () => { | |
const hash = window.location.hash || '#posts' | |
const mainElement = document.querySelector('.main') | |
mainElement.innerHTML = ''if (hash ==='#posts') {// mainElement.appendChild(posts()) | |
// /* webpackChunkName: 'components' */ 为魔法正文,可用将两个模块合并到同一个文件名中 | |
import(/* webpackChunkName: 'components' */'./posts/posts').then(({default: posts}) => {mainElement.appendChild(posts()) | |
}) | |
} else if (hash === '#album') {// mainElement.appendChild(album()) | |
import(/* webpackChunkName: 'components' */''./album/album').then(({default: album}) => {mainElement.appendChild(album()) | |
}) | |
} | |
} | |
render() | |
window.addEventListener('hashchange', render) |
- 配合 VUE-ROUTER 示例:(未完待续)
const routes = [ | |
{ | |
path: '/', | |
name: 'Index', | |
component: Index | |
}, | |
{ | |
path: '/detail/:id', | |
name: 'Detail', | |
props: true, | |
// route level code-splitting | |
// this generates a separate chunk (about.[hash].js) for this route | |
// which is lazy-loaded when the route is visited. | |
component: () => import(/* webpackChunkName: "detail" */ '../views/Detail.vue') | |
} | |
] |
6.4 splitChunks,提取 js 公共代码
- 依赖:无,间接在 optimization 中配置
- 性能:取代了 webpack4 中的 CommonsChunkPlugin,默认值是按需加载的 chunks;参考:https://webpack.docschina.org/plugins/split-chunks-plugin/
optimization: { | |
splitChunks: { | |
// 主动提取所有公共模块到独自 bundle;可选配置有:all,async 和 initial。// 设置为 all 可能特地弱小,因为这意味着 chunk 能够在异步和非异步 chunk 之间共享 | |
chunks: 'all' | |
} | |
}, |
6.5 MiniCssExtractPlugin,提取 CSS 到单个文件
- 依赖:
> npm install mini-css-extract-plugin --save-dev => 将款式寄存到独自的 css 文件中,取代 style-loader | |
> npm install optimize-css-assets-webpack-plugin --save-dev => 手动关上 css 压缩代码 | |
> npm install terser-webpack-plugin --save-dev => 手动关上 js 压缩代码 |
- 性能:
- 将 css 代码寄存到独自的 css 文件中,是否也能够合并 css 文件?(待钻研:https://webpack.docschina.org/plugins/mini-css-extract-plugin/)
- 配合 css-loader,不再须要 style-loader;
const MiniCssExtractPlugin = require('mini-css-extract-plugin') | |
// ... | |
optimization: { | |
minimizer: [// 应用了 minimizer,webpack 认为不再启动主动压缩 js 代码 | |
new TerserWebpackPlugin(), // 手动关上 js 压缩代码 | |
new OptimizeCssAssetsWebpackPlugin() // 手动关上 css 压缩代码] | |
}, | |
module: { | |
rules: [ | |
{ | |
test: /\.css$/, | |
use: [ | |
// 'style-loader', // 将款式通过 style 标签注入 | |
MiniCssExtractPlugin.loader, // 将款式寄存到独自的 css 文件中 | |
'css-loader' | |
] | |
} | |
] | |
}, | |
plugins: [ | |
...,new MiniCssExtractPlugin()] |
6.6 ExtractTextPlugin、MiniCssExtractPlugin 哪个更好用?
- MiniCssExtractPlugin 基于 webpack v4 的新个性(模块类型)构建,并且须要 webpack 4 能力失常工作。
-
与 extract-text-webpack-plugin 相比,MiniCssExtractPlugin:
https://webpack.docschina.org/plugins/mini-css-extract-plugin/
- 异步加载
- 没有反复的编译(性能)
- 更容易应用
- 特地针对 CSS 开发
6.7 文件名 hash,解决缓存带来的问题
webpack 中有三种 hash:
- 我的项目级别:filename: ‘[name]-[hash].bundle.js’ —— 我的项目中任何改变都会批改 hash
- Chunk 级别:filename: ‘[name]-[chunkhash].bundle.js’ —— 同一路 chunk 改变会批改 hash
- 内容级别:filename: ‘[name]-[contenthash].bundle.js’ —— 文件发生变化时才会扭转 hash
最佳计划: 应用 contenthash:8,并且前面能够指定位数:8 位。
output: {filename: '[name]-[contenthash:8].bundle.js' | |
}, | |
... | |
plugins: [ | |
..., | |
new MiniCssExtractPlugin({filename: '[name]-[contenthash:8].bundle.css' | |
}) | |
] |
6.8 Happypack:多线程并行打包
webpack4 之后举荐应用 thread-loader;而且 happypack 曾经没怎么保护了,和 vue-loader 配合也会有一些配置问题;
6.9 其它进步构建性能的计划
官网倡议:https://webpack.docschina.org/guides/build-performance/
如何对 webpack 构建性能进行剖析:
参考文档:https://juejin.cn/post/6911519627772329991
> npm install --save-dev speed-measure-webpack-plugin => 剖析 webpack 打包速度 | |
> npm install --save-dev webpack-bundle-analyzer => 剖析 webpack 打包模块大小 |
晋升构建速度计划(次要的几个):
- 通过应用 include 字段减小打包范畴
- 应用 DllPlugin 为更改不频繁的代码生成独自的打包文件;配置参考:https://webpack.docschina.org/plugins/dll-plugin/
- 多应用分片按需加载性能,SplitChunksPlugin 配置;
- 应用 thread-loader,性能相似于 happypack;创立多线程打包,配置参考:https://webpack.docschina.org/loaders/thread-loader/#root
1. 请仅在耗时的操作中应用 thread-loader,因为:在 worker 池中运行的 loader 是受到限制的;具体参考文档。2. 不要应用太多的 worker,因为 Node.js 的 runtime 和 loader 都有启动开销。最小化 worker 和 main process(主过程) 之间的模块传输。过程间通信 (IPC, inter process communication) 是十分耗费资源的。
- 应用配置项:cache,缓存生成的 webpack 模块和 chunk,来改善构建速度。cache 会在开发 模式被设置成 type: ‘memory’ 而且在 生产 模式 中被禁用。参考:https://webpack.docschina.org/configuration/other-options/#cache
- 开发阶段尽量应用 webpack-dev-server 在内存中构建
- 配置 devTool 生成映射文件时抉择适合的选项
- // …
7. 示例代码
https://gitee.com/ymcdhr/e-demo/tree/master/webpack-sample
8. 参考资料:
https://webpack.docschina.org/guides/
https://webpack.docschina.org/guides/build-performance/
特地鸣谢:拉勾教育前端高薪训练营