前言:本文基于weboack4.x,主要涉及webpack4 基本概念、基本配置和实际项目打包优化。关于概念方面参考官网,常用配置来自于网络资源,在文末有相关参考链接,实践部分基于自己的项目进行优化配置。
webpack 四大概念
entry
定义编译打包入口文件。类型:字符串(单入口)、对象(多入口)
entry: './src/index.js'
// 等同于
entry: {
main: './src/index.js'
}
output
1、filename:[name]
: 对应着entry中对象的key[id]
: 内部的chumk id[hash]
: 每次打包编译的唯一hash,改动会影响整个项目的打包,缓存失效[chunkhash]
:对应着每个入口文件计算而来的hash,唯一,文件之间互不影响[contenthash]
: contenthash = (moduleId + content) 生成的hash。同一文件中,修改某个module影响其他module。例如,js代码中引入css文件,修改js文件造成css文件的hash改变
2、path: 是配置输出文件存放在本地的目录,字符串类型,是绝对路径puclicPath: 对构建出的资源进行异步加载(图片,文件) 时候的路径前缀, 可以看作静态文件托管在cdn
3、chunkFilename: 决定了非入口(non-entry) chunk 文件的名称,如按需加载、异步加载
Module
配置Loader
rules 数组,包含多个处理文件的 loader 配置
- 条件匹配: 通过test、include、exclude三个配置来命中Loader要应用的规则文件。
- 应用规则: 对选中后的文件通过use配置项来应用loader,可以应用一个loader或者按照从后往前的顺序应用一组loader。同时还可以分别给loader传入参数。
- 重置顺序: 一组loader的执行顺序默认是从有道左执行,通过exforce选项可以让其中一个loader的执行顺序放到最前或者是最后。
loader配置属性
test:类型正则、字符串、数组。匹配文件
use:类型字符串、对象、数组【loader字符串或对象】。对选中的文件应用loader。
对象中包含:loader、options:loader的具体配置参数、enforce: 改变该loader的执行的顺序【pre/post】
include: 指定需要处理的文件。类型:一般为路径、可以是数组类型。
exclude:排除不需要处理的文件。类型:一般为路径、可以是数组类型。
noParse: 排除对没有采用模块化的文件解析和处理,类似:jQuery。 类型:RegExp, [RegExp], function其中一个。
parser:可以更细粒度的配置哪些模块语法【AMD、CommonJS、ES6等】是否需要解析
Plugin
Plugin 是用来扩展Webpack 功能的,通过在构建流程里注入钩子实现,它为Webpack 带
来了很大的灵活性。主要在打包的某个阶段执行该插件,对文件进行处理,类似于钩子函数
tree shaking
触发treeshking条件:
1.需要代码是es module规范的并且使用解构赋值的方式引入,
2.开启optimization.usedExports:true
来标记使用和未使用的模块,
- 使用压缩的插件进行删除未使用代码。
webpack4的mode设置为production,默认开启optimization.usedExports和使用代码压缩
注:
1、tree shaking 不能作用于有副作用side-effect的代码
如果所有代码没有副作用,在package.json 中添加
sideEffects: false
如果存在副作用代码/模块,
sideEffects: [
".src/some-side-effectful-file.js",
"*.css"
]
"side effect(副作用)" 的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供 export。
2、不要意外地将 ES 模块编译成 CommonJS 模块。如果你使用 Babel 的时候,采用了 babel-preset-env 或者 babel-preset-es2015,请检查这些预置的设置。默认情况下,它们会将 ES 的导入和导出转换为 CommonJS 的 require 和 module.exports,可以通过设置.babelrc中 { modules: false } 选项来禁用它
HMR (Hot Module replacement)模块热替换
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {'^/api' : ''}
},
context: ['/api', '/online'], //匹配多个路径,同时代理到同一个站点
target: 'http://localhost:3000'
},
hot: true,
hotOnly: true
}
code spiliting
把代码分离到不同的 bundle 中,可以按需加载或并行加载这些文件
实现方式:
1、入口配置:entry 入口使用多个入口文件 =》 存在重复引用的模块
2、抽取公有代码:使用 SplitChunksPlugin 抽取公有代码,取代CommonsChunkplugin
webpack.config.js 配置:
```
optimizition: {
splitChunks: {
chunks: 'all'
}
}
```
3、动态加载 :动态加载一些代码 =》 ECMAScript 提案 的 import() 语法
方式1 + 方式2 需要配合使用,才能达到代码抽离的效果
prefetch && preload
prefetch(预取):将来某些导航下可能需要的资源
preload(预加载):当前导航下可能需要资源
import(/* webpackPrefetch: true */ 'LoginModel');
/ webpackPrefetch: true /:把主加载流程加载完毕,在空闲时在加载其他,等再点击其他时,只需要从缓存中读取即可,性能更好。推荐使用,提高代码利用率。把一些交互后才能用到的代码写到异步组件里,通过懒加载的形式,去把这块的代码逻辑加载进来,性能提升,页面访问速度更快。
/ webpackPreload: true /: 和主加载流程一起并行加载。
lazy loading
import() 异步加载加载的模块,开启代码分割后,会被单独打包在一个文件中
路由懒加载
const Login = () => import('./components/login')
模块异步加载
import(/* webpackChunkName: "vendor"*/ './page/vendor.js').then(({default: _}) => {// todo})
公共代码提取
mini-css-extract-plugin 用于提取公共css,取代 webpack 3 的 extract-text-webpack-plugin
注:一般适用于生产环境,在开发环境会导致HMR功能缺失;在开发环境,使用style-loader
使用方式:
const MiniCssExtractPlugin=require('mini-css-extract-plugin')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 代码压缩
// modules 中
// css,scss,sass,less
{
test:/\.(sa|sc|c)ss$/,
use: [
process.env.NODE_ENV === 'development' ? 'style-loader': MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
]
}
optimization: {
minimizer: [new OptimizeCssAssetsPlugin({})]
},
//plugins中
new MiniCssExtractPlugin({
filename: "[name].css"
})
DllPlugin & DllReferencePlugin
防止第三方包多次编译打包。 第一次打包时,把第三方模块单独打包生成一个文件 vendors.dll.js,之后在打包时就可以直接从 vendors.dll.js 中引入之前打包好的第三方模块
实现过程:
1、编写一个用于生成动态链接库的配置文件
2、运行生成动态链接库和对应的.mainfest.json映射文件
3、在webpack.config.js中使用动态链接库。这样第二次编译打包会从json文件中找相应的模块
webpack.dll.config.js
module.exports = {
entry: {
react: ['react'] //react模块打包到一个动态连接库
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].dll.js', //输出动态连接库的文件名称
library: '_dll_[name]' //全局变量名称
},
plugins: [
new webpack.DllPlugin({
name: '_dll_[name]', //和output.library中一致,值就是输出的manifest.json中的 name值
path: path.join(__dirname, 'dist', '[name].manifest.json')
})
]
}
webpack.config.js
plugins: [
new webpack.DllReferencePlugin({
manifest: require(path.join(__dirname, 'dist', 'react.manifest.json')),
})
],
提高webpack的构建(打包/build)速度
- 多入口情况下,使用SplitChunk来提取公共代码
- 通过externals配置来提取常用库
- 利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
- 使用Happypack 实现多线程加速编译
- 使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
- 使用Tree-shaking和Scope Hoisting来剔除多余代码
模式分离
webpack 作为模块化打包工具, 常用来对项目进行打包进行本地调试和发布到线上,所以无论是自己在项目配置使用webpack还是使用开发框架的脚手架进行开发,都需要区分开发和生产环境。在webpack4 的配置项中添加了 mode
属性,可以用来区分两者模式。
下面是开发和生产模式下一些默认配置和区别:
development 模式下,默认开启了NamedChunksPlugin 和NamedModulesPlugin方便调试,提供了更完整的错误信息,更快的重新编译的速度。
production 模式下,由于提供了splitChunks和minimize,代码就会自动分割、压缩、优化,同时 webpack 也会自动帮你 Scope hoisting 和 Tree-shaking
优化划分
体积优化
工具 - 打包文件可视化
使用 webpack-bundle-analyzer, 可直观的看出打包后每个模块所占比例和大小
使用方法:
- 安装
npm i -D webpack-bundle-analyzer
2.在 package.json -> script 中添加启动命令
"analyz": "cross-env NODE_ENV=prodution npm_config_report=true npm run build"
3.在 webpack.pro.conf.js -> plugin 添加以下代码,可以改变启动时的端口等配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
new BundleAnalyzerPlugin({analyzerPort: 8089})
关于使用方法详细可参考:webpack实践-webpack-bundle-analyzer使用
具体方案
路由懒加载
import xxxx from '@componets/xxx' => const xxx = () => require('@componets/xxx')
模块化按需加载
对于一些类似antd、element-ui、eCharts等库,可以按需引入,没有必要全局引入,具体方法见官方文档
对于loadash等API依赖工具,结合lodash-webpack-plugin和babel-plugin-lodash,实现按需引入,把需要的API一次性引入,并挂载在全局上
// 从lodash 中统一引入你需要的方法
import _ from 'lodash'
export default {
cloneDeep: _.cloneDeep,
debounce: _.debounce,
throttle: _.throttle,
size: _.size,
pick: _.pick,
isEmpty: _.isEmpty
}
// 注入到全局
import _ from '@helper/lodash.js'
Vue.prototype.$_ = _
// vue 组件内运用
this.$_.debounce()
外部模块使用CDN
对于类似Jquery等大而使用较少的库,可以在index.html使用cdn引入;如果顾虑到可能造成外部攻击等问题,可以下载成为本地资源,再引入
速度优化
实现方法
缩短路径,减小文件搜索范围
1、缩小文件搜索范围或者指定特定的文件夹位置
2、排除不需要进行处理的文件
......
使用并发压缩插件
不使用默认的 UglifyJs,使用并行压缩工具 webpack-parallel-uglify-plugin
......
具体参考:webpack 打包优化之速度篇
实践
vue 项目
我是在我之前开发一大型项目使用webpack进行项目优化
注:此项目使用的webpack版本为2.x,不是最新版本4.x,由于项目比较复杂,升级带来的潜在问题可能比较多,时间精力有限,暂时未升级
项目技术和库:vue全家桶 + vue-cli + webpack + jQuery + element-UI + eCharts ...
按照上文中的常用配置,就打包速度和体积进行了优化,从而导致页面加载速度得到一定提升
图片上传失败,显示不出来 【手动捂脸】
最后
由于本文参考了许多相关文章,并加以自己的理解和实战,如有不妥之处,请多包涵并指出,谢谢
参考:
webpack实践——webpack-bundle-analyzer 的使用
从基础到实战 手摸手带你掌握新版Webpack4.0详解 教你看文档
webpack 4.0 基础到实战配置github
webpack 官方文档
关于webpack的面试题总结
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。