Webpack学习笔记(二)(优化配置)

webpack优化配置

1、开发环境性能优化

1.1 HMR(模块热替换)

hot module replacement 热模块替换 / 模块热替换
作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)极大提升了构建速度
代码:只需要在 devServer 中设置 hot 为 true,就会自动开启HMR功能(只能在开发模式下使用)

devServer: {
   contentBase: resolve(__dirname, 'build'),
   compress: true,
   port: 3000,
   open: true,
   // 开启HMR功能
   // 修改webpack配置,新配置要生效,需重新webpack服务
   hot: true
},
  • 样式文件:可以使用HMR功能,因为style-loade内部实现了
  • js文件:默认不能使用HMR功能
    实现HMR需修改js代码,添加支持HMR功能代码(注意:HMR功能只能处理非入口js文件的其他文件 )
// module.hot为true 说明开始了HMR功能  => 让HMR功能代码生效
if (module.hot) {
    // 此方法监听print.js文件的变化,一旦发生变化,其他模块不会重新打包构建,会执行后面的回调函数
    module.hot.accept('./print.js',function(){
        print()
    })
}
  • html文件:默认不能使用HMR功能,同时会到导致 问题:html文件不能热更新了
    解决:修改entry入口,将html文件引入(不需要HMR功能,只有一个文件,不需要优化)
entry: ['./src/index.js','./src/index.html'],

1.2 source-map

source-map: 一种提供源代码到构建后代码映射技术(如果构建后代码出错了,通过映射可以追踪源代码错误)
参数:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

 devtool: 'source-map'
  • source-map:外部,错误代码准确信息 和 源代码的错误位置
  • inline-source-map:内联,只生成一个内联 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、外部生成文件build.js.map,内联没有 2、内联构建速度更快

开发/生产环境可做的选择:
开发环境:需要考虑速度快,调试更友好

  • 速度快( eval > inline > cheap >… )
    eval-cheap-souce-map
    eval-source-map
  • 调试更友好
    souce-map
    cheap-module-souce-map
    cheap-souce-map

最终得出最好的两种方案:

  • eval-source-map(完整度高,内联速度快)(vue react 脚手架默认使用)
  • eval-cheap-module-souce-map(错误提示忽略列但是包含其他信息,内联速度快)

生产环境:需要考虑源代码要不要隐藏,调试要不要更友好
内联会让代码体积变大,所以在生产环境不用内联
隐藏源代码

  • nosources-source-map 全部隐藏
  • hidden-source-map 只隐藏源代码,会提示构建后代码错误信息

最终得出最好的两种方案

  • source-map(最完整)
  • cheap-module-souce-map(错误提示一整行忽略列)

2、生产环境性能优化

2.1 oneOf

根据文件类型加载对应的loader,只要能匹配一个即可退出,优化打包速度。

 module:{
 	rules:[{
 		oneOf:[
          	{
              test:/\.css$/,
              use:[...commonCssLoader]
          	},
          	{
              test:/\.less/,
              use:[
                 ...commonCssLoader,
                  'less-loader'
             	],
			}
		]
    }]
 }

2.2 缓存

babel缓存:将 babel 处理后的资源缓存起来(哪里的 js 改变就更新哪里,其他 js 还是用之前缓存的资源),让第二次打包构建速度更快

{
	loader:'babel-loader',
     options:{
	     // 预设:指示babel做怎样的兼容性处理
	     presets:[
	     [
	         '@babel/preset-env',
	         {
	             // 按需加载
	             useBuiltIns:'usage',
	             // 指定core-js版本
	             corejs:{
	                 version:2
	             },
	             // 指定兼容性做到那个版本浏览器
	             targets:{
	                 chrome:'60',
	                 firefox:'60',
	                 ie:'9',
	                 safari:'10',
	                 edge:'17'
	             }
	         }
	     ]
	     ],
	     // 开启babel缓存,第二次构建会读取之前的缓存
	     cacheDirectory: true
	}
}

文件资源缓存

  • hash:每次webpack构建是会生成唯一的hash值
    问题:因为js跟css同时使用一个hash值,重新打包,所有文件的 hsah 值都改变,会导致所有缓存失效。(可能只改动了一个文件)
  • chunkhash:根据chunk生成的hash值,如果打包来源于同一个chunk,那么hash值就一样
    问题:js跟css的hash值还是一样的因为css是在js中被引入的,所以同属于一个chunk
  • contenthash:根据文件的内容生成hash值,不同文件hash值不一样 => 让代码上线运行缓存更好使用

2.3多进程打包

多进程打包:某个任务消耗时间较长会卡顿,多进程可以同一时间干多件事,效率更高。

优点:提升打包速度,缺点:是每个进程的开启和交流都会有开销(babel-loader消耗时间久,所以使用thread-loader针对其进行优化)

{
  test: /\.js$/,
  exclude: /node_modules/,
  use: [
    /* 
      thread-loader会对其后面的loader(这里是babel-loader)开启多进程打包。 
      进程启动大概为600ms,进程通信也有开销。(启动的开销比较昂贵,不要滥用)
      只有工作消耗时间比较长,才需要多进程打包
    */
    {
      loader: 'thread-loader',
      options: {
        workers: 2 // 进程2个
      }
    },
    {
      loader: 'babel-loader',
      options: {
        presets: [
          [
            '@babel/preset-env',
            {
              useBuiltIns: 'usage',
              corejs: { version: 3 },
              targets: {
                chrome: '60',
                firefox: '50'
              }
            }
          ]
        ],
        // 开启babel缓存
        // 第二次构建时,会读取之前的缓存
        cacheDirectory: true
      }
    }
  ]
},

2.4 externals

externals:让某些库不打包,通过 cdn 引入

externals: {
	// 拒绝jquery被打包,在index.html中通过cdn引进<script src="cdn引入">
    // 写法:忽略库名:pm包名
    jquery: jQuery
}

2.5 dll

dll:让某些库单独打包,后直接引入到 build 中。可以在 code split 分割出 node_modules 后再用 dll 更细的分割,优化代码运行的性能。

webpack.dll.js 配置:(将 jquery 单独打包)

/*
  node_modules的库会打包到一起,但是很多库的时候打包输出的js文件就太大了
  使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包

  当运行webpack时,默认查找webpack.config.js配置文件
  需求:需要运行webpack.dll.js文件
    --> webpack --config webpack.dll.js(运行这个指令表示以这个配置文件打包)
*/
const { resolve } = require('path');
const { webpack } = require('webpack');
module.exports = {
    entry: {
        // 最终打包生产的name:要打包的库为jquery
        jquery: ['jquery']
    },
    output: {
        // 输出出口指定
        filename: '[name].js', //name就是jquery
        path: resolve(__dirname, 'dll'), // 打包到dll目录下
        library:'[name]_[hash]', // 打包的库里面向外暴露出去的内容叫什么名字
    },
    plugins:[
        // 打包生成一个manifest.json ==> 提供jquery的映射关系,告诉webpack jquery之后不需要再打包和暴露内容的名称
        new webpack.DllPlugin({
            name:'[name]_[hash]', // 映射库的暴露的内容名称
            path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
        })
    ],
    mode: 'production'
};

webpack.config.js 配置:(告诉 webpack 不需要再打包 jquery,并将之前打包好的 jquery 跟其他打包好的资源一同输出到 build 目录下)

// externals: {
    //     // 拒绝jquery被打包,通过cdn引进<script src="cdn引入">
    //     // 写法:忽略库名:pm包名
    //     jquery: jQuery
    // }

2.6 tree-shaking

tree sharking:去除无用代码
前提:1、必须使用es6模块化,2、开启production环境
作用:减少代码体积
在page.json中配置 sideEffects":false 所有代码都没有副作用,都可进行tree shaking
问题:可能会把css / @babel/polyfill 副作用文件干掉
解决:“sideEffects”:[".css",".less"]

2.7 code split(代码分割)

code split: 代码分隔 并行加载 速度更快

  1. 多入口拆分
entry:{
   main: './src/js/index.js',
   test: './src/js/tree.js',
},
output:{
    // [name]:取文件名
    filename:'js/[name].[contenthash:10].js',
    path:resolve(__dirname, "build")
},
  1. optimization
/**
 * 可以将node_modules中代码单独打包成一个chunk最终输出
 * 自动分析多入口chunk中,有没有公共文件,如果有会单独打包成单独一个chunk
 *  */ 
optimization:{
    splitChunks: {
        chunks: 'all'
    }
},
  1. import 动态导入语法
// import 动态导入语法:能将某个文件单独打包
import (/*webpackChunkName: 'tree' */'./tree')
.then((result)=>{
    console.log(result)
  }
).catch(()=>{
  console.log('文件加载失败')
})

2.8 懒加载 | 预加载

正常加载:可以认为是并行加载,同一时间加载多个文件
懒加载:当文件需要使用时才加载
预加载:会在使用前提前加载js文件,等其他资源加载完毕,等浏览器空闲了,在偷偷加载资源

 document.getElementById('btn').onclick = () => {
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./tree').then(({mul})=>{
    // eslint-disable-next-line
    console.log(mul(4,5))
  })
}; 

2.9 PWA

PWA:渐进式web应用,使用 serviceworker 和 workbox 技术,离线可访问,兼容性较差
webpack.config.js中配置

const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); // 引入插件

// plugins中加入:
new WorkboxWebpackPlugin.GenerateSW({
  /*
    1. 帮助serviceworker快速启动
    2. 删除旧的 serviceworker

    生成一个 serviceworker 配置文件
  */
  clientsClaim: true,
  skipWaiting: true
})

index.js 中还需要写一段代码来激活它的使用:

/**
 * 1、问题:eslint 不认识 window navigator等全局变量
 * 解决:需要修改package.json中eslintConfig配置
 * "evn":{
 *    "browser":true 支持浏览器全局变量
 * }
 * 2、sw代码必须运行在服务器上
 *    => nodejs
 *    => npm i serve -g    serve -s build启动服务器,将build目录下的所有资源作为静态资源暴露出去
 * */
// 注册serviceWorker
// 处理兼用性问题
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(() => {
        console.log('sw注册成功');
      }).catch(() => {
        console.log('sw注册失败');
      });
  });
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值