包含了 Tree Shaking,Code Spliting,打包环境区分,缓存,shimming 等内容,继续扩展 Webpack 的基础知识面。
Tree Shaking
<!--index.js--> import { add } from './math' add(1,2)
<!--math.js--> export const add = (a,b) => { console.log(a,b) return a + b } export const minus = (a,b) =>{ console.log(a,b) return a - b }
执行npx webpack
打包后,打算dist/main.js
可以看不到没有用到的minus方法也一并打包了,这种就造成我们打包的文件过于冗余,Tree Shaking
就可以帮助我们解决这个问题,但是只能解决ES模块导入,像require其他导入方式就不行,因为ES是底层是导入静态资源,require是动态资源。
在mode
为development下配置增加
<!--webpack.config.js-->
module.exports = {
mode: 'development',
optimization: {
usedExports: true //使用模块导入
}
}
配置后可以看到只到导出了add方法
那import './index.css'
文件怎么办,不就是会被忽略了,这个时候我们需要在package.json
增加忽略的配置
{
...
"sideEffects": false,
}
如果是生成环境mode
为production下,就不需要增加optimization
配置,因为生成环境默认帮我们增加了,但是sideEffects
还是要配置的。
Develoment 和 Production 模式的区分打包
项目上线我们就需要把mode
Develoment 切换为 Production,我们可以在包文件分别配置不同的打包命令
{
"scripts": {
"dev": "webpack-dev-server --config ./build/webpack.dev.js",
"build": "webpack --config ./build/webpack.prod.js"
},
}
两种模式有很多共总的打包配置,我们可以使用webpack-merge
抽离公用的配置,新建文件夹build,在该目录新建三个文件webpack.dev.js
webpack.prod.js
webpack.common.js
<!--webpack.common.js-->
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
main: './src/index.js', //入口文件 默认:src/index.js
},
output: { //出口文件 默认: dist/main.js
filename: '[name].js', //输出的文件名
path: path.resolve(__dirname, 'dist') //输出的路径,只能是绝对路径
},
module: {
...
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html'
})
]
}
<!--webpack.dev.js--> const commonCongif = require('./webpack.common') const merge = require("webpack-merge"); const devCongif = { mode: 'development', devtool: 'eval-cheap-module-source-map', devServer: { contentBase: './dist', port: 9000, //服务端口号 open: true, //首次打包编译自动打开浏览器 hot: true, hotOnly: true, }, optimization: { usedExports: true }, plugins: [ new webpack.HotModuleReplacementPlugin() ] }
module.exports = merge(commonCongif,devCongif)
<!--webpack.prod.js-->
const commonCongif = require('./webpack.common')
const merge = require("webpack-merge");
const prodCongif = {
mode: 'production',
devtool: 'cheap-module-source-map',
}
module.exports = merge(commonCongif,prodCongif)
Code Splitting
Code Splitting
可以实现代码切割,按需加载。
- 不使用
Code Splitting
方式:首次访问页面时,加载main.js(2mb),当页面业务逻辑发生改变时,又要加载2mb的内容
//index.js
import _ from 'loadsh' //1mb
console.log(_.join(['a','b','c'],'***'))
//此省略一万行业务代码
console.log(_.join(['a','b','c'],'***'))
- 使用
Code Splitting
方式:main.js被拆成lodash.js(1mb),main.js(1mb),当页面业务逻辑发生改变时,只要加载main.js即可(1mb)
<!--loadsh.js--> import _ from 'loadsh' //1mb window._ = _ <!--index.js--> console.log(_.join(['a','b','c'],'***')) //此省略一万行业务代码 console.log(_.join(['a','b','c'],'***'))
//webpack.commom.js
module.exports = {
entry: {
loadsh: './src/loadsh.js',
main: './src/index.js', //入口文件 默认:src/index.js
},
}
webpack中实现代码分割,有两种方式:
- 1.同步代码:只需要在webpack.commom.js中坐optimization的配置
- 2.异步代码(类似import导入),无需做任何配置,会自动进行代码分割 // 同步导入
import _ from 'loadsh' //1mb console.log(_.join(['a','b','c'],'***')) //此省略一万行业务代码 console.log(_.join(['a','b','c'],'***')) //异步导入 function getComponent() { return import('loadsh').then(({ default: _ }) => { var elem = document.createElement('div') elem.innerHTML = _.join(['a','b','c'],'***') return elem }) }
getComponent().then(elem => { document.body.appendChild(elem) })
//webpack.common.js
module.exports = {
...
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
splitChunks默认配置详解
optimization: {
splitChunks: {
chunks: 'all', //代码切割的类型: async: 只切割异步代码 initial:只切割同步代码 all:两种都切割
minSize: 30000, //切割的文件最小要求,单位kb
maxSize: 0,//切割的文件最大要求,单位kb,超过部分会继续切分
minChunks: 1, //最小需要切割的次数需求:至少需要切割一次
maxAsyncRequests: 6,//按需加载时并行请求的最大数量。
maxInitialRequests: 4,//入口点的最大并行请求数。
automaticNameDelimiter: '~',//默认情况下,webpack将使用块的来源和名称生成名称(例如vendors~main.js)。此选项使您可以指定用于生成名称的定界符。
cacheGroups: {//缓存组,同步切割时候会走该模块区分切割组
defaultVendors: {
test: /[\\/]node_modules[\\/]/, //配置的模块:node_modules文件夹下的模块切割
priority: -10 //切割优先级
},
default: { //如果没有达到上方的,就走到该默认组切割
minChunks: 2,
priority: -20, //切割优先级
reuseExistingChunk: true //如果已存在切割模块,忽略这个组的切割
}
}
}
}
Lazy Loading 懒加载
懒加载可以实现按需加载,主要是通过import()来实现,如下点击事件才按需加载某个包 //异步导入
function getComponent() { return import(/* webpackChunkName: "lodash" */'loadsh').then(({ default: _ }) => { var elem = document.createElement('div') elem.innerHTML = _.join(['a','b','c'],'***') return elem }) }
document.addEventListener('click', () => { getComponent().then(elem => { document.body.appendChild(elem) }) })
Chunk
每个包生成的js文件就是一个chunk,minChunks
配置就和这个有关minChunks: 1 最小需要切割的次数需求:至少需要切割一次,比如lodash文件切割至少一次。
打包分析
我们可以打包生成相关的json文件,利用第三方平台来查看分析我们打包的文件的各个方面
- 步骤一:配置生成打包分析文件stats.json
<!--package.json-->
"scripts": {
"dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js"
}
- 步骤二:利用第三方分析打包:http://webpack.github.io/analyse/
预取/预加载模块Preloading和Prefetching
在声明 import
时,使用下面这些内置指令,可以让 webpack
输出 "resource hint(资源提示)",来告知浏览器:
prefetch
(预取):将来某些导航下可能需要的资源preload
(预加载):当前导航下可能需要资源 下面这个 prefetch 的简单示例中:
<!--loadsh.js-->
function loadsh () {
var elem = document.createElement('div')
elem.innerHTML = 'test'
}
export default loadsh
<!--index.js-->
//推荐写法:异步导入的方式可以提高代码的使用率,可以在浏览器控制面板Coverage看到使用率提高
document.addEventListener('click', () => {
import(/*webpackPrefetch: true */ './loadsh').then(({default: _}) => {
_()
})
})
查看浏览器加载的资源,资源会在主核心加载完毕后加载了该模块,在点击的时候直接从缓存直接提取出来
css文件的代码切割
MiniCssExtractPlugin切割css
webpack
在做打包的时候会把css
文件打包在js
里,我们就需要借助插件来帮我们对css文件进行切割
可对
css
的引入文件进行代码分割会把
css
打包成单独的一个文件这个插件适合在
production
模式下做打包
注意tree shaking的代码分割,如果不设置会忽略css代码切割导致切割失败
optimize-css-assets-webpack-plugin对css进行代码压缩,具体详情配置查看文档,配置文件代码如下
const commonCongif = require('./webpack.common') const merge = require("webpack-merge"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const prodCongif = { mode: 'production', devtool: 'cheap-module-source-map', module: { rules: [ { test: /\.sass$/, use: [MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 2 // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader // modules: true //按模块化引入 } }, 'sass-loader', 'postcss-loader' ] }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', //css文件切割插件 }) ] }
module.exports = merge(commonCongif,prodCongif)
压缩css文件
1.安装
npm install optimize-css-assets-webpack-plugin --save-dev
2.使用优化配置
<!--webpack.prod.js-->
const optimizeCss = require('optimize-css-assets-webpack-plugin');
optimization: {
minimizer: [
new optimizeCss({}) //进行css代码代码
]
},
Webpack 与浏览器缓存( Caching )
当我们访问浏览器的时候,第一次请求资源会从服务器拿去,当再次访问的时候,相同的文件名我们会直接从缓存中提取。但是我们会想,如果当我们修改了代码推上去之后,当前用户已经访问了,当用户再次刷新文件名字还是一样,它就不会更新最新的代码,而是从浏览器缓存中直接读取旧的。webpack
的output
相关配置就可以帮助我们解决这个问题,让用户可以重新加载已经修改最新的代码模块。
<!--webpack.prod.js-->
output: {
filename: '[name].[contenthash].js', //输出的文件名
chunkFilename: '[name].[contenthash].js' //chunk文件生成的名字
},
contenthash
可以在打包的时候监听代码是否改变,改变则生成新的hash
,不变则用上次的
本文使用 mdnice 排版