开发环境性能优化
优化打包构建速度
HMR优化代码调试source-map
生产环境性能优化
优化打包构建速度
oneOf babel 缓存 多进程打包 externals dll 优化代码运行的性能 缓存(hash,chunkhash,contenthash) tree shaking code split 懒加载和预加载 pwa
优化 开发环境 打包构建速度
HMR hot module replacement 热模块替换/模块热替换
作用:一个模块发生变化,只会重新打包这一个模块 而不是重新打包所有,极大提升构建速度
样式文件: 可以使用HMR功能,因为style-loader内部实现了 js文件: 默认不能使用HMR功能 -->解决:需要修改js代码,添加支持HMR功能的代码。注意,HMR功能对js的处理,只能处理非入口js文件的其他文件。
- if(module.hot){
- //一旦module.hot是true,说明开启HMR功能,让HMR功能代码生效
- module.hot.accept('./xxx.js',function(){
- //此方法会监听print.js文件的变化,一旦发生变化,其他默认不会重新打包构建
- //会执行后面的回调函数
- xxx();
- })
- }
html文件: 默认不能使用HMR功能,同时会导致问题:html文件不能热更新了 解决:改 entry:['入口js','html'] ,但html文件只有一个,所以不用做HMR功能
- devServer:{
- //项目构建后的目录
- contentBase: resolve(__dirname,'build'),
- //启用gzip压缩
- compress:true,
- //端口号
- port:3000,
- //自动打开浏览器
- open:true
- }
优化 开发环境 代码调试
source-map 一种提供源代码到构建后代码映射的技术
如果构建后代码出错了,通过映射可以追踪到错误的代码
- webpack.config.js
- devtools:'source-map'
- //其他 参数 [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
- source-map : 外部
- 错误代码的准确信息和错误位置
- inline-source-map : 内联
- 错误代码的准确信息和错误位置
- hidden-source-map : 外部
- 错误代码的错误原因 但没有错误位置,不能追踪到源代码的错误,只能提示到构建后代码的位置
- eval-source-map : 内联
- 每一个文件都生成对应的source-map,都在eval
- 错误代码的准确信息和错误位置
- nosources-source-map : 外部
- 能找到错误代码的准确信息 但没有任何源代码信息
- cheap-source-map : 外部
- 错误代码的准确信息和错误位置 只精确到行,不精确到列
- cheap-module-source-map : 外部
- 错误代码的准确信息和错误位置
- module 会将 loader 的 source-map加入
- 内联 和 外部的区别 :
- 1.外部生成了文件但内联没有生成
- 2.内联构建速度更快
开发环境:速度快,调试更友好
- 速度快 eval>inline>cheap>...
- 调试更友好 souce-map>cheap-module-souce-map>cheap-souce-map
- 所以 一般用eval-source-map
生产环境:源代码要不要隐藏,调试要不要友好?内联会让体积编码,所以一般不用内联
- nosources-source-map 隐藏源代码
- hidden-source-map 只隐藏源代码,会提示构建购代码错误信息
- --> source-map /cheap-module-souce-map
优化生产环境
oneOf
rules里中有许多个loader,这样会导致每个文件都会被所有的loader过一遍,有些能处理,有些处理不了。所以可以利用oneOf达到以下loader只会匹配到第一个。但需要注意,不能有两个loader同时处理同一个文件
- webpack.config
- module.exports={
- //....
- module:{
- rule:[
- //正常来讲,一个文件只能被一个loader处理
- //当一个文件要被多个loader处理,那么一定要指定loader的执行顺序
- // 先执行eslint 再执行babel
- {
- test:/\.js$/,
- exclude:/node_modules/,
- //优先执行
- enforce:'pre',
- loader:'eslint-loader',
- options:{
- fix:true
- }
- },
- {
- oneOf:[
- {
- test: /\.css$/,
- use:[
- ...commonCssLoader
- ]
- },
- {
- test:/\.less$/,
- use:[
- ...commonCssLoader,'less-loader'
- ]
- },
- {
- test:/\.js$/,
- exclude:/node_modules/,
- loader:'babel-loader',
- options:{
- // 预设 :指示babel做怎样的兼容性处理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加载
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪个版本的浏览器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ]
- }
- },
- {
- test:/\.(png|jpg|gif)/,
- loader:'url-loader',
- enModule:true,
- options:{
- limit:8*1024,
- name: '[hash:10].[ext]',
- outputpath:''
- }
- },
- {
- test:/\.html$/,
- loader:'html-loader'
- },
- {
- exclude:/\.(js|less|css|png|jpg|gif)/,
- loader:'file-loader',
- options:{
- name:'[hash:10].[ext]'
- }
- }
- ]
- }
- ]
- },
- }
缓存
1.babel缓存-->第二次打包更快
- {
- test:/\.js$/,
- exclude:/node_modules/,
- loader:'babel-loader',
- options:{
- // 预设 :指示babel做怎样的兼容性处理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加载
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪个版本的浏览器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ],
- //第二次构建时,会读取之前的缓存
- cacheDirectory: true
- }
- },
2.文件资源缓存-->上线缓存优化
- hash:每次webpack构建会生成一个唯一hash值
- 问题: 因为js和css同时使用一个hash值,如果重新打包,会导致所有缓存失效,可能我却只改了一个文件,
- chunkhash:根据chunk生成hash值,如果打包来源于同一个chunk,那么hash值也一样
- 问题:js和css的hash值还是一样的。
- 原因:css是由js引入的,所以属于同一个chunk
- contenthash: 根据文件内容生成hash值,
- webpack.config.js
tree shaking
去除应用程序中没有使用的代码
- 前提:
- 1.必须使用es6模块化
- 2.开启production模式
- 在package.json中配置
- "sideEffects":false 所有代码都没有副作用,都可以镜像tree sharking
- 问题 可能会把css/@babel/polyfille 干掉
- "sideEffects": ["*.css","*.less"] 哪些文件不 tree sharking
code split
1.入口配置
- 单入口 //单页面应用
- entry:'./src/js/index.js'
- 多入口 //多页面应用
- entry:{
- index:'./src/js/index.js',
- test:'./src/js/test.js'
- }
2.optimization
- module.exports={
- //...
- // 可以将nodemudules中的代码单独打包成一个chunk最终输出
- // 还可以自动分析多入口chunk中,有没有公共的文件,如果有会打包成一个单独的chunk
- optimization:{
- splitChunks:{
- chunks:'all'
- }
- }
- }
3.import 动态导入语法,能将某个文件单独打包
通过js代码,让某个文件被单独打包成一个chunk,通过注释可以固定此文件的名称
- import(/*webpackChunkName: 'xxxName' */'./xx/xxx.js')
- .then(res =>{
- //加载成功
- })
- .catch(()=>{
- //加载失败
- })
懒加载和预加载
1.懒加载 当文件需要用时才加载
import 动态导入语法
- document.getElementById('btn').onclick = function(){
- import(/*webpackChunkName: 'xxxName' */'./xx/ss.js')
- .then(res=>{
- //干啥干啥
- })
- }
2.预加载 webpackPrefetch:true
./xx/ss.js 已经被加载了,点击的时候再从缓存中加载,
- document.getElementById('btn').onclick = function(){
- import(/*webpackChunkName: 'xxxName',webpackPrefetch:true */'./xx/ss.js')
- .then(res=>{
- //干啥干啥
- })
- }
正常加载可以认为是并行加载(同一时间加载多个文件) 预加载prefectch 等其他资源加载完毕,浏览器空闲了,再偷偷加载资源 兼容性比较差 慎用
PWA 渐进式网络开发应用程序
网络离线可访问
webbox-->webbox-webpack-plugin
- const WebboxWebpackPlugin = require('webbox-webpack-plugin')
- module.exports={
- plugins:[
- new WebboxWebpackPlugin.GenerateSW({
- /*
- 1.帮助 serviceWorker 快速启动
- 2.删除旧的 serviceWorker
- 生成一个 serviceWorker 配置文件
- */
- clientsClaim:true,
- skipWaiting:true
- })
- ]
- }
- index.js 中注册serviceworker
- //处理兼容性
- if('serviceWorker' in navigator){
- window.addEventListener('load',()=>{
- navigator.serviceWorker
- .register('./service-work.js')
- .then(()=>{
- //成功
- })
- .catch(()=>{
- //失败
- })
- })
- }
- 1.可能会出现问题 eslint不认识window和navigator
- 解决 package.json中eslintConfig中配置
- "env":{
- "browser":true //支持浏览器端的变量
- }
- 2. sw代码必须运行在服务器上
- -->node.js
- --> npm i serve -g
- serve -s build 启动一个服务器将build下的资源作为静态资源暴露出去
多进程打包
thread-loader 一般给babel-loader用
但需要注意
进程启动大约需500ms,进程间通信也有开销。只有工作消耗时间比较长,才需要多进程打包
- {
- test:/\.js$/,
- exclude:/node_modules/,
- use:[
- //'thread-loader',
- {
- loader:'thread-loader',
- options:{
- workers: 2 //进程2个
- }
- }
- {
- loader:'babel-loader',
- options:{
- // 预设 :指示babel做怎样的兼容性处理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加载
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪个版本的浏览器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ],
- //第二次构建时,会读取之前的缓存
- cacheDirectory: true
- }
- }
- ]
- },
externals
- module.exports={
- externals:{
- //忽略/拒绝 库名 -- npm 包名
- //可以在index.html中引入cdn
- }
- }
dll 动态连接
使用dll技术 对某些库(第三方库) 进行单独打包
- 指令 webpack --config webpack.dll.js
- webpack.dll.js
- const {resolve} = require('path')
- const webpack = require('webpack')
- module.exports = {
- entry:{
- //最终打包生成的[name]-->jquery
- //['jquery']-->要打包的库是jquery
- jquery:['jquery']
- },
- ouput:{
- filename:'[name].js',
- path:resolve(__dirname,'dll'),
- library:'[name]_[hash]' //打包的库里面向外暴露出去的内容叫什么名字
- },
- plugin:[
- new webpacl.DllPlugin({
- //打包生成一个manifest.json --> 提供和jquery映射
- name: '[name]_[hash]',//映射库的暴露内容名称
- path:resolve(__dirname,'dll/manifest.json')
- })
- ],
- mode:'production'
- }
- webpack.config.js
- const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
- module.exports={
- plugins:[
- //告诉webpack哪些库不参与打包,同时名称也得变
- new webpack.DllReferencePlugin({
- manifest: resolve(__dirname,'dll/manifest.json')
- }),
- //将某个文件打包输出出去,并在html中引入该资源
- new AddAssetHtmlWebpackPlugin({
- filepath:resolve(__dirname,'dll/jquery.js')
- })
- ]
- }
编辑推荐