笔记(二)
1 Tree Shaking
- 只支持ES Module(静态引入),也就是用import export导入导出。
- 作用:一个模块中有多个内容,通过Tree Shaking可以控制只导出使用过(引用)的内容(方法),未使用过的不会打包到main.js文件中。
1.1 优化(optimization)配置与使用
- optimization配置项:
开发环境:
webpack配置
module.export = {
//优化配置项
optimization: {
//把运行时用到webpack的源代码或运行时用到的代码
//放到runtime的一个Chunk里边去
runtimeChunk: {
name: 'runtime'
},
//设置为true,package.json中可以
//定义一些文件不会被tree Shaking
usedExports: true,
splitChunks: {
//代码分割打包配置
}
}
}
packag.json:
{
"name":xx,
//false表示对所有模块都进行Tree Shanking
"sideEffects": false,
//或设置为数组,放入忽略样式模块
"sideEffects": [
"*.css"
]
..
}
例子:
index.js:
//只引入add方法
import { add } from './math.js'
add(1, 2)
development(开发环境下)
main.js
/*! exports provided: add, minus */
/*! exports used: add */
production(上线时)
main.js中无minus这个方法(因为没有被使用)
2.1 Development和Production模式的区分打包
- 将webpack.prod.js、webpack.dev.js、webpack.common.js三个配置文件放在build目录,记得修改output的dist路径。
webpack.common.js
plugins :{
//传参数root表示清空上一级目录中的dist
new CleanWebpackPlugin({
root: path.resolve(_direname, '../')
})
},
output: {
//表示打包后的js文件名
filename: '[name].js',
//表示输出路径,要加绝对路径,所以引用path模块
path: path.resolve(__dirname, '../dist')
}
- development开发环境下,生成的source-map文件会比较详细。(devtool:‘cheap-module-eval-source-map’,optimization:{usedExports: true})
package.json:
"scripts": {
"watch": "webpack --watch",
//开发时:
"dev": "webpack-dev-server --config webpack.dev.js",
//上线时:
"build": "webpack --config webpack.prod.js"
}
- 创建webpack.common.js把公共部分放一起。安装使用webpack-merge。
安装:
npm install webpack-merge -D
使用:
(webpack.dev.js)
const merge = require('webpack-merge')
const commonConfig = require('./webpack.common.js')
const devConfig = {
//把module.exports对象中的内容都丢过来
}
module.exports = merge(devConfig, commonConfig)
2.2 Webpack和Code Splitting(代码分割)
- Code Splitting:本质就是将项目js文件分类拆分成多个js文件,使页面可以异步加载多个js文件(一般最多5个,入口页面3个),不需要变动的部分缓存,提高复用率,后续业务逻辑发生变化,只需重新加载修改后main.js中的代码即可。
- 如何分类:例如缓存组中vendors放置的是node_module中用到模块的js文件,这部分代码在后续会用到,可以缓存起来,提高复用率。
- 三种方法:入口点(手动拆分)、防止重复(利用SplitChunksPlugin)和动态导入(模块内联函数调用拆分代码)
- 入口点(手动拆分)
安装:
npm install lodash
手动拆分代码:
webpack.common.js:
entry: {
lodash: './src/lodash.js',
main: './src/index.js'
}
- 防止重复(SplitChunksPlugins)
- 使用splitChunksplugins实现同步和异步的代码分割:
配置:
common.config.js:
module.exports = {
optimization: {
splitChunks: {
//不区分同步异步实现代码分割
chunks: 'all'
}
}
}
- 动态导入
异步导入加载lodash:
index.js:
function getComponent() {
return import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) => {
const element = document.createElement('div');
element.innerHTML = _.join(['Zeg', 'Tsai'], '_');
return element;
})
}
getComponent().then(element => {
document.body.appendChild(element);
})
2.3 cacheGroups(缓存组)
- 分为:vendors/default,vendors优先级默认更高。
- 使用SplitChunksPlugin进行代码分割时,被引用的依赖模块(/node_modules/)的js文件会被打包到缓存组中vendors这一项,默认命名规则:vendors~引用的模块.js。其他默认规则。
2.4 SplitChunksPlugin配置参数
https://webpack.js.org/plugins/split-chunks-plugin/#defaults
- 动态引入时配置语法:,目的就是改变异步拆分生成的文件名。
- 关于拆分后js文件名字:同步可以直接在cacheGroups中的分组vendors或default中添加filename改变打包后的文件名。而异步则需通过
import(/* webpackChunkName: '' */ 'jquery').then()
改名。
异步加载修改生成文件名字步骤:
1. 利用自带的动态引入模块插件:
.babelrc:
{
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}
2. 将缓存组的两项都设为false
common.config.js:
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
//设为false,目的是不让默认配置缓存组匹配修改名称
//使得动态引入插件能成功修改生成文件的名字
//都设为false是为了让动态引入插件生效
vendors: false,
default: false
}
}
}
}
3. 在引入时加入/* webpackChunkName:"" */
index.js:
function getComponent() {
//将分割的文件起名为lodash.js
return import(/* webpackChunkName:"lodash" */ 'lodash').then(({ default: _ }) => {
let element = document.createElement('div');
element.innerHTML = _.join(['Dell', 'Lee'], '_');
return element;
})
}
getComponent().then(element => {
document.body.appendChild(element);
});
- SplitChunksPlugin默认配置项:
module.exports = {
//...
optimization: {
splitChunks: {
//只对异步代码有效
chunks: 'async',
//默认大于29.29kb模块才会实现分割
minSize: 30000,
//有设置的话,可能使代码进一步拆分
maxSize: 0,
//引用次数(大于一次就会分割打包)
minChunks: 1,
//最大异步请求(分割数)
maxAsyncRequests: 5,
//入口文件最多请求3个
maxInitialRequests: 3,
//生成文件名的连接符
automaticNameDelimiter: '~',
//文件名长度
automaticNameMaxLength: 30,
name: true,
//缓存组
cacheGroups: {
//同步代码这部分代码值不能都为false
//引用node_modules中的模块会打包到vendors这个组
vendors: {
test: /[\\/]node_modules[\\/]/,
//同时符合条件时的优先级(-10优先级高)
priority: -10,
//自己加的,同步有效
filename: 'vendors.js'
},
//默认打包到这个组
default: {
minChunks: 2,
priority: -20,
//会复用已打包的模块
reuseExistingChunk: true,
//自己加的,只对同步有效
filename: 'common.js'
}
}
}
}
};
3 Lazy Loading 懒加载
- 入口页面只加载部分js和html,只有在满足(触发)某些条件时,再会使页面加载一些js、html等等的文件。
3.1 Chunk
- 可以理解为引用次数
懒加载的例子:
index.js:
function getComponent() {
return import(/* webpackChunkName:"lodash" */ 'lodash').then(({ default: _ }) => {
let element = document.createElement('div');
element.innerHTML = _.join(['Zeg', 'Tsai'], '_');
return element;
})
}
document.addEventListener('click', () => {
getComponent().then(element => {
document.body.appendChild(element);
});
})
ES7:
async function getComponent() {
const { default: _ } = await import(/* webpackChunkName:"lodash" */ 'lodash')
const element = document.createElement('div');
element.innerHTML = _.join(['Zeg', 'Tsai'], '_');
return element;
}
document.addEventListener('click', () => {
getComponent().then(element => {
document.body.appendChild(element);
});
})
3.2 打包分析,Preloading,Prefetching
https://github.com/webpack/analyse
https://webpack.js.org/guides/code-splitting/#bundle-analysis
- 打包分析:利用工具分析打包是否合理。
package.json:
"dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js"
生成stats.json
需要进入webpack.github.io/analyse/
在线工具打包分析
- prefetch:将来某些导航可能需要资源。
preload:当前导航期间可能需要资源。
错误地使用webpackPreload实际上会损害性能,因此在使用它时要小心。 - 使用:/* webpackPrefetch: true /
/ webpackPreload: true */ - 与prefetch相比,Preload指令有很多不同之处:
1. 预加载的块开始与父块并行加载。
父块完成加载后,将启动Prefetch的块。
2. 预加载的块具有中等优先级并立即下载。
浏览器空闲时下载Prefetch的块。
3. 父组块应立即请求预加载的块。
未来的任何时候都可以使用Prefetch的块。
4. 浏览器支持是不同的。
4 Shimming(匀场模块,填隙?)
- 作用:把一些全局依赖(如jQuery)以标识符($)的变量形式作为引入使用。
4.1 ProvidePlugin(自带插件)
- 由于模块间有相互独立性,引用外部的包时,在别人包内添加自定添加的一些标识符(例如$表示引入jQuery)会很麻烦,通过这个插件可以创建一些通用标识符,在所有模块中使用该标识符,即代表引入该模块。
例子:
common.config.js:
const webpack = require('webpack');
const commonConfig = {
entry:...
...
plugins: [
new webpack.ProvidePlugin({
//只要模块使用$就会自动引入
$: 'jquery',
//只要使用_join就会到lodash中找到该函数(方法)
_join: ['lodash', 'join']
})
]
}
- webpack中默认this指向的是自身的模块对象,非window,使用imports-loader可以解决这个问题。
按照:
npm install imports-loader -D
使用:
{
//兼容低版本的浏览器,配置文件在.babelrc中
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader'
}, {
//使模块中的this指向window
loader: 'imports-loader?this=>window'
}]
}