核心:
- entry(打包入口)
- output(打包后的文件处理)
- loader(对各类资源的处理)
- plugin(利用插件对webpack进行扩展和加强)
- mode(针对开发环境和生产环境的区分处理)
其余常用:
- devServer(热更新)
- resolve(模块解析)
- optimization(优化)
- devtool(源码调试)
优化手段:
- stats分析
- 速度分析
- 体积分析
- tree-shaking
- scope-hoisting
- 多进程构建
- 并行压缩
- 预编译资源模块
- 提高二次构建速度
- css的tree-shaking
- webpack图片压缩
- 动态polufil
- entry打包入口
定义打包的入口
module.exports = {
entry: './src/index.js'
}
// 多入口
module.exports = {
entry: {
index: './src/index.js',
list: './src/index.js'
}
}
- output输出
编译后文件输出到磁盘的相关配置
module.exports = {
output: {
filename: '[name]_[chunkhash:8].js', // 单个文件可以直接,多入口利用占位符保证文件统一
path: path.join(__dirname, '../dist'), // 写入文件磁盘路径
publicPath: 'https://cdn.example.com/assets/' // 使用CDN,给全部文件引入模板文件时加上路径前缀
}
}
- loader(资源解析转换)
webpack原生只支持js和json,利用loader,对不一样的文件类型支持,转换成有效的模块
// 1、解析es6和jsx
module.exports = {
module: {
rules: [
{
test: /\.(j|t)sx?$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
}
}
// 2、解析css文件
// style-loader 将样式经过style标签插入模板文件的head中
// css-loader 用于加载css文件,转换成commonJs对象
{
test: /.css$/,
use: ['style-loader', 'css-loader']
}
// 3、解析less
// less-loader
{
test: /.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'less-loader',
options: { // 可配置属性,修改变量的值,通常利用来修改UI库的主题样式,如下是antd主题样式配置
modifyVars: {
'@primary-color': '#ec7259',
},
javascriptEnabled: true, // 支持内联的js
}
}
]
}
// 4、图片和字体解析
/* file-loader:解析图片, 字体等
url-loader:也可处理图片和字体,并可设置较小资源自动base64,url-loader比file-loader功能强大 */
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: 'static/img/[name].[hash:8].[ext]', // [ext]代表文件后缀名
}
}
]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 8192,
name: 'static/fonts/[name].[hash:8].[ext]'
}
}
// 5、移动端适配
/* px2rem-loader: 把px转换成rem,配合lib-flexible使用 */
{
loader: 'px2rem-loader',
options: {
remUnit: 75, // 1rem=多少像素
remPrecision: 8, // rem的小数点后位数
}
}
// 6、css前缀补齐
/* postcss-loader:用于浏览器适配,某些css3属性浏览器不支持须要加前缀,它会自动针对不一样浏览器加不一样的属性前缀 */
{
loader: 'postcss-loader',
options: {
plugin: ()=> [autoprefixer()] // 安装autoprefixer
}
}
- plugin(利用插件对webpack进行扩展和加强)
- 解决loader没法完成的事
- 由于插件能够携带参数/选项,因此要向 plugins属性传入new实例 简单示例
const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin") // 自动生成index.html
const { CleanWebpackPlugin } = require("clean-webpack-plugin") // 每次打包清空文件夹
const MiniCssExtractPlugin = require("mini-css-extract-plugin") // css分离
const TerserWebpackPlugin = require("terser-webpack-plugin") // 压缩js(在optimization中配置)
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin") // 压缩css
// 1、 页面打包
new HtmlWebpackPlugin({ // 产出html文件,编译时会读取模板文件
filename: '../dist/index.html', // 指定生成的模板文件名及路径
template: path.join(__dirname, '../index.html'), // 指定要使用的模板文件
inject: true, // 指定的chunk会自动注入html文件中
chunks: ['index'], // 为了避免缓存,可以在指定产出的资源后面添加hash值
minify: { // 代码的最小输出
collapseWhitespace: true, // 删除空格,但不会删除script、style和textarea中的空格
preserveLineBreaks: false, // 是否保留换行符
minifyCss: true, // css压缩
minifyJs: true, // js压缩
removeComments: true // 删除注释,可是会保留script和style中的注释
}
}),
// 2、文件清理
/*
每次打包文件到dist,首先要清理dist内部文件,或直接删除dist文件夹,防止文件重复 咱们可利用rimraf dist
但此方式不太优雅,咱们可使用clean-webpack-plugin,清理dist内部文件 */
new CleanWebpackPlugin(),
// 3、css剥离
/*
css代码默认打包在js文件中,但有时候css变了,js没变,或者相反,这是不利于缓存的,
咱们能够把css剥离出来单独生成文件,去作缓存处理 */
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css', // 代码块名字
chunkFilename: "[id].css" // 异步加载用
}),
// 4、css压缩
/* 既然css单独剥离出来,就要作压缩 */
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/g,
// cssProcessor: require("cssnao") // cssnano 压缩和优化的css插件
})
// 5、基础库分离
/*
咱们常把一些不太变化的静态资源放在cdn上,而后在模版文件里引入
在webpack也提供了插件支持,可直接配置插入
假如咱们分离react和react-dom
*/
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
plugins: [
new HtmlWebpackExternalsPlugin({
externals: [
{
module: 'react',
entry: 'https://cdn.cn/16.8.0/react.min.js',
global: 'React',
},
{
module: 'react-dom',
entry: 'https://cdn.cn/16.8.0/react-dom.min.js',
global: 'ReactDOM',
}
]
})
]
- mode(针对开发环境和生产环境的区分处理)
对应三个属性:
- development 开发模式
- production 生产模式
- none 无
webpack会针对不一样环境直接作一些优化工做,例如production模式下会进行,tree-shaking和scope-hosting
- devServer(热更新)
devServer: {
historyApiFallback: true, // 单页面程序 刷新浏览器会出现404,缘由是它经过这个路径(好比: /search/list)来访问后台,因此会出现404,而把historyApiFallback设置为true那么全部的路径都执行index.html
host: '192.168.1.1', // 域名
open: true, // 支持自动打开浏览器
hot: true, // 模块热替换,在前端代码变更的时候无需整个刷新页面,只把变化的部分替换掉
inline: false, // inline选项会为入口页面添加“热加载”功能,即代码改变后从新加载页面
port: 8080, // 端口
proxy: proxyConfig._proxy, // 代理后端服务
before(app) { // 其余中间件以前, 提供执行自定义中间件
apiMocker(app, path.resolve('./mocks/mock.js'), // 举例:可用来作mock数据
proxyConfig._proxy);
}
}
- resolve(模块解析)
设置模块如何被解析
常用属性:
- alias 建立 import 或 require 的别名,来确保模块引入变得更简单
- extensions 自动解析肯定的扩展
- mainFileds 当从 npm 包中导入模块时,决定在 package.json 中使用哪一个字段导入模块
- moudles 告诉 webpack 解析模块时应该搜索的目录
resolve: {
alias: {
'@': path.resolve('src'),
'@nm': path.resolve('node_modules')
},
extensions: ['.js', '.jsx', '.json'],
moudles: [path.resolve(__dirname, 'node_modules')]
}
- optimization(优化)
1、提取公共资源
- 项目多页面的时候,大多数页面使用的基础库或依赖都是同样的,这时每一个页面都单独打包一份,对资源是一种浪费,打包后体积较大,页面加载时间长,
- 因此咱们能够把公共资源提取出来单独打包,访问多页面时利用缓存机制,只加载一次,达到优化目的
- splitChunks,代替以前的CommonsChunkPlugin,公共资源分离vendors
2、公共文件分离
- 一些公共的工具函数类文件,咱们能够经过限制被调用的次数来决定是否把它分离出来
- 利用splitChunks 公共文件分离commons
3、提取webpack的模块化信息清单
- 模块信息清单在每次有模块变动(hash 变动)时都会变动, 因此把这部分代码单独打包出来, 配合后端缓存策略,
- 避免某个模块的变化致使包含在模块化信息中的模块缓存失效
- 具体使用runtimeChunk
optimization: {
runtimeChunk: {
name: 'manifest',
},
splitChunks: {
minSize: 50000 // 分离的包的体积大小
cacheGroups: {
vendors: {
test: /(react|react-dom)/, //正则匹配要分离的文件
name: 'vendors',
chunks: 'all', // 肯定对何种引入方式的文件进行分离
minChunks: 1, // 最小使用的次数
priority: 10, // 多个缓存组时,须要有优先级排列,优先使用哪一个进行分离
},
commons: { // 分离公共文件
name: 'commons',
chunks: 'all',
minChunks: 2,
priority: 5,
},
},
},
minimizer: [ // 放优化的插件
new TerserWebpackPlugin({ // 压缩js
parallel: true, // 开启多进程压缩
cache: true // 开启缓存(压缩过的不压缩)
}),
new OptimizeCssAssetsWebpackPlugin({ // 压缩css
assetNameRegExp: /\.css$/g,
// cssProcessor: require("cssnao")
})
]
},
- devtool(源码调试)
- 控制是否生成,以及如何生成 source map
- 咱们要进行一个配置以方便咱们在测试环境进行问题定位,源码调试的加强
关键字定义:
- eval 模块都使用 eval() 包裹执行,而且都有 //@ sourceURL(指向的是原文件index.js,调试的时候,根据sourceUrl找到的index.js文件的)
- source map 产生.map文件(这个map文件会和原始文件作一个映射,调试的时候,就是经过这个.map文件去定位原来的代码位置的 )
- cheap 不包含列的信息,(假如代码运行出现了错误,控制台报出了,error,咱们点击定位到具体源码的时候,就只能定位到行,而不能定位到具体的列)
- inline .map文件做为dataUrl嵌入到打包文件,而不单独生成
- moudle 包含loader的sourcemap(调试的代码不会被转换,会保留原始代码语法)
几种关键字进行组合就造成了具体的用法,不一样用法对构建速度是有影响的,基本状况你越清晰容易的看到原始的代码,构建速度就越慢,调试的方便性和构建速度上你们能够本身权衡一下
// 开发环境
devtool: 'cheap-module-eval-source-map' // 原始源代码(仅限行)
// 生产环境,通常不进行设置
优化手段
- 优化分析 stats
// 构建完成后会生成json文件,显示构建的一些信息,时间,各模块的体积等
scripts: {
'build: stats': 'webpack --config build/webpack.prod.config.js --json > stats.json',
}
- 速度分析
分析每一个插件和loader的耗时状况 使用举例
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasureWebpackPlugin();
module.exports = smp.wrap(merge(webpackConfigBase, webpackConfigProd));
可以看到每一个插件和loader的耗时状况, 若是耗时较长,会以红字提示,咱们就能够具体分析那个地方为何时间长,能够用别的插件替换之类的去作构建速度优化
- 体积分析
以图形大小的形式,更加直观的看到各个模块插件所占用的体积
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins: [
new BundleAnalyzerPlugin({
analyzerPort: 8919 //打包构建后体积分析图展现的窗口
}),
],
- tree-shaking
- 字面意思是摇晃树,就是把树上坏掉的叶子摇下来,就是死码清除(即没有被用到的代码)
- 某个模块或文件,的某个方法被用到了,整个模块都会被打包都bundle文件中去,tree-shaking会把没有用到的方法去除,在uglify阶段清除
- 仅支持es6语法 webpack 4 中设置 production 默认开启了此项优化
- 多进程构建
- webpack构建是一个涉及文件的读写的过程,若是项目很是复杂,构建时间就会加长
- 而webpack运行在nodejs上是单线程模型,同一时间只能处理一个任务
- 咱们是否可让webpack同时进行多任务处理呢 happypack和thread loader给咱们提供了方案
- thread loader由官方提供
- thread loader放在最上面,就会在最后执行,以前的loader会在一个单独的worker池中运行
- 每一个 worker 都是一个单独的有 600ms 限制的 node.js 进程,从而实现了多进程的构建,下降构建时间 举例
rules: [
{
test: /.js$/,
use: [
{
loader: 'thread-loader',
options: {
workers: 3, // 产生的 worker 的数量,默认是 cpu 的核心数
}
}
]
},
]
- 构建异常,中断处理
构建过程当中,有时会出现构建异常报错的状况,咱们能够经过某些方法捕获,以及自定义一些逻辑
plugins: [
// 主动捕获构建错误
function () {
this.hooks.done.tap('done', (stats) => {
if (stats.compilation.errors
&& process.argv.indexOf('--watch' == -1)) {
console.log('error', stats.compilation.errors);
// 能够作一个构建系统的日志,在此处上报错误缘由
process.exit(12); // 自定义错误code码
}
});
},
],
- 并行压缩
- webpack4 推荐使用 terser-webpack-plugin 开启 parallel
- uglify-webpack-plugin也支持并行压缩,但不支持es6,不作具体介绍,有兴趣的同窗自行查询
optimization: {
minimizer: [
new TerserPluginWebpack({
parallel: 4, // 开启 不主动指定的话,默认数值是当前电脑cpu数量的2倍减1
})
],
}
- 分包,预编译资源模块
问题 以前的分包存在问题
- externals 分包会打出太多的script标签
- splitchunk 分包须要一个分析的时间
解决
- 预编译资源模块
- 将react,react-dom redux 等基础包和业务基础包打包成一个文件
方法
- dll-plugin进行分包,
- dllreferenceplugin 对mainfest.json(对分离包的描述)引用,将预编译的模块加载进来
- 利用add-asset-html-webpack-plugin把生成文件插入模版
// package.json
"scripts": {
"dll": "webpack --config build/webpack.dll.js" // 打包前只需执行一次,只要基础包不变则不须要执行
},
// webpack.dll.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: { // 指定要分离的包
library: [ // 基础包
'react',
'react-dom',
],
},
output: {
filename: "[name]_[hash].dll.js",
path: path.resolve(__dirname, './library'),
library: "[name]_[hash]", //包名称 注意此名需和下面的DllPlugin,name名一致
},
plugins: [
new webpack.DllPlugin({
name: "[name]_[hash]",
path: path.resolve(__dirname, './library/[name].json')
})
]
}
// webpack.prod.js
plugins: [
...
// 将给定的 JS文件添加到 webpack 配置的文件中,并插入到模版文件中
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, '../build/library/*.dll.js'),
}),
//
new webpack.DllReferencePlugin({
manifest: require('../build/library/library.json')
}),
]
- 提高二次构建后的速度
缓存插件 hard-source-webpack-plugin
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
plugins: [
...
new HardSourceWebpackPlugin()
]
]}
- css的tree-shaking(Remove unused CSS)
- 清除css无用代码
- 早期PurifyCSSPlugin,它主要的做用是能够去除没有用到的CSS代码,相似JS的Tree Shaking,现已再也不维护
- 如今使用purgecss-webpack-plugin,配合mini-css-extract-plugin使用
const PATHS = {
src: path.join(__dirname, '../src')
}
plugin: [
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }), // 注意是绝对路径匹配
}),
]
- webpack 图片压缩
- 使图片压缩更加自动化
- 基于node库的imagemin
/* 支持定制选项,
可引入第三方插件
支持多个图片格式 */
{
test: /.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name:'[name]_[hash:8].[ext]'
}
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false,
},
pngquant: {
quality: [0.65, 0.90],
speed: 4
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75
}
}
},
],
},
- 动态polyfill服务
polyfill-service
- 获取浏览器的useragent判断支持状况,
- 动态的返回浏览器不支持的新特性
- 官方提供了cdnhttps://polyfill.io/v3/polyfill.min.js
- 也能够基于官方自建polyfill服务,更加自由的配置属性(好比,指定只判断promise的支持程度) 例如 https://polyfill.alicdn.com/polyfill.min.js?features=Promise
基础配置
解析js
解析css
解析less
解析图片
解析字体
前缀补齐
移动端适配
目录清理
页面打包
css抽离
异常主动捕获提升构建速度
resolve缩小文件的搜索范围
使用DllPlugin减小基础模块编译次数
thread loader 多进程构建
terser并行压缩
hard-source-webpack-plugin提高二次构建速度
dll分包,预编译资源模块减少构建体积
公共资源分离
Tree Shaking
scope-hoisting
js,css,字体,图片压缩
动态polyfill
css的tree-shaking
代码分割,按需引入,import动态加载提高开发体验
sourcemap源码调试
devserver热更新
友好错误提示保证稳定安全
代码压缩混淆