1、第三方包重复引用解决方案
主项目依赖了lodash
// package.json
{
"name": "project-name",
"version": "1.0.0",
"dependencies": {
"lodash": "^4.17.21",
"test": "^1.0.2"
}
}
主项目依赖了test,test又依赖lodash
// node_modules/test/package.json
{
"name": "test",
"version": "1.0.2",
"dependencies": {
"lodash": "4.17.19"
}
}
├── node_modules
│ ├──lodash@4.17.21
│ ├──test
│ │ ├── node_modules
| | │ ├──lodash@4.17.19
在编译构建期间,两个版本的 lodash 依赖都会打包到产物中,导致第三方包重复引用。
查看两者是否兼容
1.在 webpack 配置中设置 resolve.alias
// webpack.config.js
alias: {
lodash: path.resolve(__dirname, './node_modules/lodash'),
}
2.yarn resolutions 统一版本
(注意, 该 resolution 字段只能在项目的根目录下设置)
然后 yarn install 即可
// package.json
{
"name": "project-name",
"version": "1.0.0",
"dependencies": {
"lodash": "^4.17.21",
"test": "^1.0.2"
}
"resolutions": {
"lodash": "4.17.21",
}
}
3、 cdn外部引入
externals加载外部资源
操作对象
configureWebpack: {
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
'element-ui': 'ELEMENT',
echarts: 'echarts'
},
2、删除不必要的第三方包
在这个例子中,
在第三方包的 package.json 中正确配置了 sideEffects 的情况下,
代码最后打包的时候被删掉了,
没有正确配置就没有删掉
3、 tree-sharking
应为lodash antd 体积过大,检查按需引入,treeshaking有没有生效
使用webpack插件 例如 tree-sharking进行剔除无关的依赖加载
使用terser进行代码压缩,给执行时间长的loader加 cache-loader,
可以使得下次打包就会使用 node_modules/.cache 里的
依赖于ES6的模块特性,ES6模块依赖关系是确定的,和运行时的状态无关,
可以进行可靠的静态分析,这就是 tree-shaking 的基础
export default 导出的是一个对象,无法通过静态分析判断出一个对象的哪些变量未被使用,
所以 tree-shaking 只对使用 export 导出的变量生效
这也是函数式编程越来越火的原因,因为可以很好利用 tree-shaking 精简项目的体积,
也是 vue3 全面拥抱了函数式编程的原因之一
tree-shaking需要正确配置sideEffects
"sideEffects": [
"*.css",
"*.scss"
]
.css和.scss文件都列为没有副作用,因为它们只是导出了样式,
没有对其他模块产生影响,因此可以被tree-shaking移除。
cache-loader
提高Webpack构建性能的loader,提高构建速度
4、Cache invalidation
具体优化:
https://vdavid-gilbertson.medium.com/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
// contenthash 的结果是根据最终生成的文件内容生成 hash,
可以确保在资源内容没有变更的情况下,保持文件名的一致,避免缓存失效。
module.exports = {
output: {
filename: '[name].[contenthash:8].js,
}
}
contenthash可以与协商缓存和强缓存配合使用,以实现更好的缓存效果
5、开启gzip
npm i -D compression-webpack-plugin
"use strict";
const path = require("path");
const CompressionWebpackPlugin = require("compression-webpack-plugin");
const productionGzipExtensions = ["js", "css"];
function resolve(dir) {
return path.join(__dirname, dir);
}
const name = "品牌联盟";
module.exports = {
lintOnSave: process.env.NODE_ENV === "development",
configureWebpack: {
name: name,
resolve: {
alias: {
"@": resolve("src"),
"@i": resolve("src/api"),
"@c": resolve("src/components"),
"@v": resolve("src/pages"),
"@s": resolve("src/static"),
"@u": resolve("src/utils")
}
},
plugins: [
new CompressionWebpackPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"), //匹配文件名
threshold: 10240, //对10K以上的数据进行压缩
minRatio: 0.8,
deleteOriginalAssets: false //是否删除源文件
})
]
}
};
6、路由懒加载
路由改成按需加载
webpackchunkname
Vue中运用import的懒加载语句以及webpack的魔法注释,
在项目进行webpack打包的时候,
对不同模块进行代码分割,在首屏加载时,
用到哪个模块再加载哪个模块,实现懒加载进行页面的优化。
// 通过webpackChunkName设置分割后代码块的名字
const Home = () => import(/* webpackChunkName: "home" */ "@/views/home/index.vue");
7、组件懒加载
web.dev官方文档
https://web.dev/code-splitting-suspense/?utm_source=lighthouse&utm_medium=devtools
8、UI组件库按需加载
import dialogInfo from '@/components/dialogInfo';
export default {
name: 'homeView',
components: {
dialogInfo
}
}
const dialogInfo = () => import(/* webpackChunkName: "dialogInfo" */ '@/components/dialogInfo');
export default {
name: 'homeView',
components: {
dialogInfo
}
}
9、splitChunks 分割代码
splitChunks插件用于将代码库拆分成更小的块,
以便更好地利用浏览器的缓存机制,
从而提高页面加载速度和性能
cacheGroups是一些缓存组的配置项,
- chunks选项指定代码块的类型,all表示打包所有类型的代码块。
- priority用于指定缓存组的优先级,
- enforce用于强制将模块打包到指定的代码块中,
- reuseExistingChunk用于控制是否重用已经存在的代码块
- minChunks: 用于指定在多少个模块中出现的模块才能被打包到代码块中。
默认值为1。例如,如果设置为2,则表示只有在两个或两个以上的模块中出现的模块才会被打包到代码块中 - maxAsyncRequests: 用于指定按需加载的代码块最多可以并行加载的次数。默认值为Infinity。
- maxInitialRequests: 用于指定初始页面加载时,最多可以并行加载的代码块数量。默认值为3。
- automaticNameDelimiter: 用于指定自动生成的代码块名称中的分隔符。默认值为~。
- automaticNameMaxLength: 用于指定自动生成的代码块名称的最大长度。默认值为64。
- test: 可以使用正则表达式、字符串或函数来匹配模块名称,只有匹配到的模块才会被打包到对应的代码块中。
optimization.splitChunks
使用 Webpack 拆分块的 100% 正确方法
https://david-gilbertson.medium.com/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
10、DLL动态链接库
11、terser-webpack-plugin
多线程压缩JS
可以通过删除无用代码、混淆代码和压缩代码等方式来减小JavaScript文件的大小
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
//...
optimization: {
minimize: process.env.NODE_ENV === 'production',
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
},
output: {
comments: false,
},
},
}),
],
},
};
compress选项用于删除console语句,output选项用于移除注释
minimize选项需要设置为true,以启用代码压缩。
terser-webpack-plugin会消耗一定的计算资源,
因此在开发环境中,可以暂时关闭代码压缩功能,以提高构建速度
minimize: process.env.NODE_ENV === 'production',
12、降低图片资源体积
webp
https://www.zhangxinxu.com/wordpress/2014/07/introduce-svg-sprite-technology/
图片压缩和svg压缩
imagemin-webpack-plugin插件等。
13、不同的环境不同的配置
五个关键字eval,source-map,cheap,module,inline的任意组合
eval: 使用eval包裹模块代码
source-map: 产生.map文件
cheap: 不包含列信息(关于列信息的解释下面会有详细介绍)也不包含loader的sourcemap
module: 包含loader的sourcemap(比如jsx to js ,babel的sourcemap)
inline: 将.map作为DataURI嵌入,不单独生成.map文件(这个配置项比较少见)
在开发环境中我们使用:cheap-module-eval-source-map
在生产环境中我们使用:cheap-module-source-map。
eval-source-map组合使用是指将.map以DataURL的形式引入到打包好的模块中,
//1)源码映射 会单独生成一个sourcemap文件 出错了会标识当前的列和行 大和全
devtool:"source-map", //增加映射文件 可以帮我们调试源代码
// 2)不会产生单独的文件 但是可以显示行和列
devtool:'eval-source-map',
//3) 不会产生列 但是是一个单独的映射文件
devtool:'cheap-module-source-map', //产生后你可以保留起来
//4)不会产生文件 集成在打包后的文件中 不会产生列
devtool:"cheap-module-eval-source-map",
//map文件,文件映射,方便浏览器控制台调式
productionSourceMap:process.env.NODE_ENV==='production'?false:true
14、webpack-bundle-analyzer
webpack-bundle-analyzer分析打包后的资源包
查看vuecli的配置文件
#根据mode,分别生成开发环境、生产环境的配置
vue inspect --mode production > output.js
#输入命令后,在根目录会生产一个output.js文件
项目上线
第一种
npm i serve -g
服务器容器
serve dist
第二种
node创建web服务,vue打包生成的dist文件夹
托管为静态文件