新进webpack打包系列四(终)

208 阅读8分钟

文章内容输出来源:拉勾大前端高薪训练营

- HMR

- 引入背景

	- webpack-dev-server自动刷新的问题

		- 自动刷新导致页面状态丢失

	- 期望的结果

		- 页面不刷新的前提下,模块也可以及时更新

	- 计算机行业经常听到热拔插的名词

		- 在一个正在运行的机器上随时插拔设备,机器运行状态不受影响,插上的设备可以立即开始工作

- 全称

	- Hot Module Replacement

		- 模块热替换

			- 应用运行过程中实时替换某个模块,应用运行状态不收影响

- 是webpack中最强大的功能之一
- 极大程度提高了开发者的效率
- 使用HMR

	- 开启HMR

		- 已集成在webpack-dev-server中
		- 两种方式

			- 命令行开启

				- webpack-dev-server --hot

			- 文件配置

				- const path = require ('path') 

const webpack = require ('webpack') const { CleanWebpackPlugin }= require ('clean-webpack-plugin') const HtmlWebpackPlugin = require ('html-webpack-plugin') const CopyWebpackPlugin = require ('copy-webpack-plugin')

module.exports = { mode: 'none', // 指定webpack打包的入口路径,指定相对路径时,'./'不能省略 entry: './src/main.js', // 设置输出文件的位置 output: { // 设置输出文件名称 filename: 'bundle.js', // 指定输出文件所在目录,不可使用相对路径 path: path.join(__dirname,'output') // 默认值是个空字符串:代表网站的根目录。这里的/线不能省略 // publicPath: 'dist/', }, //用于配置开发过程中的辅助工具。也就是与source-map功能相关的配置
devtool: 'source-map' // 专门用于webpack-dev-server做选项配置的 devServer: { hot: true, // 额外为开发服务器指定查找资源目录 contentBase: './', proxy: { '/api': { // http://localhost:8080/api/usersapi.github.com/api/users target: 'api.github.com', // http://localhost:8080/api/usersapi.github.com/users pathRewrite: { '^/api': '' }, // 不能使用localhost:8080 作为请求 GitHub的主机名 changeOrigin: true } } }, module: { // 针对于其他资源模块的加载规则的配置 rules: [ // 每个规则对象都需要配置两个属性: test和use { // 正则表达式,用于匹配我们在打包过程中所遇到的文件路径 test: /.css/, // 用来指定test匹配到的文件需要使用的loader,配置多个loader时执行顺序是从后往前执行 use: [ 'style-loader', 'css-loader' ] }, // 每个规则对象都需要配置两个属性: test和use { // 正则表达式,用于匹配我们在打包过程中所遇到的文件路径 test: /.png/, // 用来指定test匹配到的文件需要使用的loader,配置多个loader时执行顺序是从后往前执行 use: { loader: 'url-loader', options: { limit: 10 * 1024, // 10KB,url-loader只将10KB以下的文件转换为Data URL形式,超过此大小的仍然交给file-loader完成加载 } } } , // 每个规则对象都需要配置两个属性: test和use { // 正则表达式,用于匹配我们在打包过程中所遇到的文件路径 test: /.html/, // 用来指定test匹配到的文件需要使用的loader,配置多个loader时执行顺序是从后往前执行 use: { loader: 'html-loader' } } , // 每个规则对象都需要配置两个属性: test和use { // 正则表达式,用于匹配我们在打包过程中所遇到的文件路径 test: /.md/, // 用来指定test匹配到的文件需要使用的loader,配置多个loader时执行顺序是从后往前执行 use: [ 'html-loader', './markdown-loader', ] } ] }, plugins: [ new CleanWebpackPlugin(), new webpack.HotModuleReplacementPlugin(), // 用于生成 index.html文件 new HtmlWebpackPlugin({ // 指定自动生成的html的标题 title: 'Webpack Plugin Sample', // 设置元数据标签 meta: { viewport: 'width=device-width' }, // 指定自动生成的html的模板文件 template: './src/index.html', }) , // 用于生成 about.html文件 new HtmlWebpackPlugin({ // 设置生成html的文件名称,默认为index.html filename: 'about.html', }) , // 用于拷贝资源文件。在开发阶段最好不要使用这个插件。 // 开发中会频繁执行打包任务,假设如果拷贝的文件较多或比较大。 // 每次执行开销较大,并且打包速度也会降低。建议打生产包时再放开使用 //new CopyWebpackPlugin([ // 支持通配符 // 'public/**', // 也支持相对路径 // 'public', //]) , ] }

	- yarn webpack-dev-server --open

		- js还是会导致自动刷新页面

- webpack HMR的疑问❓

	- 为什么样式文件的热更新开箱即用❓

		- 因为style-loader实现了热替换逻辑

	- JS为啥不像css那样由某个Loader统一处理❓

		- JS代码无规律可循

	- 我的项目没有手动处理, JS照样可以热替换❓

		- 你使用了框架。通过脚手架创建的项目内部都集成了HMR方案

	- 总结

		- 我们需要手动处理JS模块更新后的热替换

- HMR APIS

	- JS模块的热替换

		- main.js

			- import createEditor from './editor' 

import background from './better.png' import './global.css' const editor = createEditor() document. body.appendchild(editor) const img = new Image() img.src = background document.body. appendchild(img)

module.hot.accept('./editor', () ⥤ { // console.log('editor 模块更新了,需要这里手动处理热替换') const value = lastEditor. innerHTML document.body.removechild(lastEditor) const newEditor = createEditor() newEditor.innerHTML = value document.body.appendchild(newEditor) lastEditor = newEditor }) - yarn webpack-dev-server

				- OK~

	- 图片模块的热替换

		- import createEditor from './editor' 

import background from './better.png' import './global.css' const editor = createEditor() document. body.appendchild(editor) const img = new Image() img.src = background document.body. appendchild(img)

module.hot.accept('./editor', () ⥤ { // console.log('editor 模块更新了,需要这里手动处理热替换') const value = lastEditor. innerHTML document.body.removechild(lastEditor) const newEditor = createEditor() newEditor.innerHTML = value document.body.appendchild(newEditor) lastEditor = newEditor }) module.hot.accept('./better.png', () ⥤ { img.src = background console.log(background) }) - yarn webpack-dev-server

			- OK~

- HMR注意事项

	- 1,处理HMR的代码报错会导致自动刷新

		- 是因为使用了hot。可以考虑使用hotOnly,这样即使处理热替换的代码报错也不会自动刷新
		- hot: true  ⥤ hotOnly: true

	- 2,没启用HMR的情况下,HMR APIS 会报错

		- import createEditor from './editor' 

import background from './better.png' import './global.css' const editor = createEditor() document. body.appendchild(editor) const img = new Image() img.src = background document.body. appendchild(img)

module.hot && module.hot.accept('./editor', () ⥤ { // console.log('editor 模块更新了,需要这里手动处理热替换') const value = lastEditor. innerHTML document.body.removechild(lastEditor) const newEditor = createEditor() newEditor.innerHTML = value document.body.appendchild(newEditor) lastEditor = newEditor }) module.hot && module.hot.accept('./better.png', () ⥤ { img.src = background console.log(background) })

	- 3,代码中多了一些与业务无关的代码

		- 去掉热替换配置
		- yarn webpack
		- 查看打包文件

			- 发现: if(false){}

		- 总结: 不会有任何影响

- 生产环境优化

- 生产环境跟开发环境有着很大的差异

	- 生产环境注重运行效率
	- 开发环境只注重开发效率

- webpack4推出了mode的用法

	- none
	- development
	- production

- webpack也建议我们为不同的工作环境创建不同的配置
- 不同环境下的配置

	- 配置文件根据环境不同导出不同配置

		- const webpack = require( "webpack')

const CleanWebpackPlugin } = require("clean-webpack-plugin') const htmlWebpackPlugin = require('html-webpack-plugin') const CopywebpackPlugin = require('copy-webpack-plugin') module.exports = (env, argv) ⥤ { const config = { // 开发配置here }

 if (env =production") {
   config.mode = 'production'
   config.devtool =false
   config.plugins =[       ...config.plugins,       new CleanWebpackPlugin()       new CopywebpackPlugin(['public'])
   ]
}
return config

} - 开发: yarn webpack --env development - 生产: yarn webpack --env production

	- 一个环境对应一个配置文件

		- webpack.common.js
		- webpack.dev.js
		- webpack.prod.js

			- const common = require('./webpack. common')

const merge = require('webpack-merge') const CleanWebpackPlugin = require( 'clean-webpack-plugin') const CopyWebpackPlugin = require(' copy-webpack-plugin') module. exports = merge(common, { mode: 'production', plugins: [ new CleanWebpackPlugin() new CopyWebpackPlugin(['public']) ] })

		- yarn add webpack-merge --dev
		- yarn webpack --config webpack.prod.js

	- DefinePlugin

		- 在webpack新增的production模式下面,内部就开启了很多通用的优化功能
		- 主要的优化配置之一
		- 为代码注入全局成员

			- 会注入:process.env.NODE_ENV

		- 启用

			- webpack.config.js

				- const webpack = require('webpack')

module. exports ={ mode: 'none', entry: './src/main.js', output: { filename: 'bundle. js }, plugins: [ new webpack.DefinePlugin({ API_BASE_URL: 'api.example.com' }) ] }

			- main.js

				-  console.Iog(API_BASE_URL)

			- yarn webpack

				- 打包后代码语法未通过
				- webpack.config.js

					- const webpack = require('webpack')

module. exports ={ mode: 'none', entry: './src/main.js', output: { filename: 'bundle. js }, plugins: [ new webpack.DefinePlugin({ API_BASE_URL: '"api.example.com"' }) ] } - const webpack = require('webpack') module. exports ={ mode: 'none', entry: './src/main.js', output: { filename: 'bundle. js }, plugins: [ new webpack.DefinePlugin({ API_BASE_URL:JSON.Stringify('api.example.com') }) ] }

				- yarn webpack

					- OK~

- Tree-shaking

	- 摇树🌲
	- 摇掉代码中未引用的部分

		- 即未引用代码(dead code)

	- webpack生产模式优化中就有这个功能

		- 检测未引用的代码并移除它们

	- 使用

		- components.js

			- export const Button =()⥤{
return document.createElement( 'button')
console. Log('dead-code')

} export const Link =() ⥤{ return document.createELement('a') } export const Heading = level ⥤ { return document.createElement('h' + level) }

		- index.js

			-  import { Button } from './components' 

document.body.appendChild(Button())

		- yarn webpack --mode production

			- 经检查:未引用的代码被移除了

		- 需要注意

			- Tree Shaking不是指某个配置选项,而是一组功能搭配使用后的优化效果
			- 这个功能会在production模式自动开启

		- 非生产模式使用

			- webpack.config.js

				- module.exports = { 
 mode: 'none'
 entry: './src/index.js', 
 output: {
     filename: 'bundle.js'
 },
 optimization: {
     // 表示在输出结果中只导出那些外部使用了的成员,负责标记枯树叶
     usedExports: true,
     // 负责摇掉那些枯树叶
     minimize: true
 }

} - yarn webpack

			- 合并模块函数(Scope Hoisting)

				- concatenateModules

					- module.exports = { 
 mode: 'none'
 entry: './src/index.js', 
 output: {
     filename: 'bundle.js'
 },
 optimization: {
     // 表示在输出结果中只导出那些外部使用了的成员,负责标记枯树叶
     usedExports: true,
     // 尽可能的将所有模块合并输出到一个函数中,
     // 这样既提升了运行效率,又减少了代码的体积
     //又称为作用域提升。webpack3提出的特性
     concatenateModules: true,
     // 负责摇掉那些枯树叶
     minimize: true
 }

}

			- Tree-shaking & Babel

				- 传言如果使用BabelTree-shaking会失效
				- Tree Shaking前提是ES Modules

					- 也就是由webpack打包的代码必须使用ESM
					- webpack在打包所有模块之前,先是将模块根据配置交给不同的Loader去处理。
					- 最后再将所有Loader处理的结果打包到一起
					- 为了转换代码中ECMAScript新特性

						- 很多时候选择babel-loader进行处理
						- babel-loader处理时可能产生这种转换

							- ES ModulesCommonJS

				- webpack.config.js

					- module. exports ={
mode: 'none,
 entry: './src/index.js',
 output: {
     filename: 'bundle js
 },
 module: {
     rules: [
        {
          test:/\.js$/,
          use: {
             loader: 'babel-loader',
             options:{
                presets: ['@babel/preset-env']
              }
           },
           optimization: {
              //模快只导出被使用的成员
              usedExports: true
              //尽可能合并每一个模块到一个函数中
              // concatenateModules: true,
              //压缩输出结果
              // minimize: true
           }
         }
      ]
 }

} - yarn webpack

						- Ok:Tree Shaking并未失效

							- 因为在最新版本的babel-loader中已自动帮我们关闭了ES Modues转换的插件

					- 强制使用ES Modules转换为CommonJS

						- module. exports ={
mode: 'none,
 entry: './src/index.js',
 output: {
     filename: 'bundle js
 },
 module: {
     rules: [
        {
          test:/\.js$/,
          use: {
             loader: 'babel-loader',
             options:{
                presets: [
                   ['@babel/preset-env', { modules: 'commonjs' }]
                ]
              }
           },
           optimization: {
              //模快只导出被使用的成员
              usedExports: true
              //尽可能合并每一个模块到一个函数中
              // concatenateModules: true,
              //压缩输出结果
              // minimize: true
           }
         }
      ]
 }

} - yarn webpack

							- OK: Tree Shaking失效了

	- webpack 生产模式打包时自动开启

- sideEffects(副作用)

- webpack4中新增
- 允许通过配置的方式去标识代码是否有副作用,从而为tree Shaking提供更大的压缩空间
- 副作用是指模块执行时除了导出成员之外所作的事情
- 一般用于npm包标记是否有副作用
- Tree Shaking 跟 sideEffects并非因果关系,且二者并无什么关系
- webpack.config.js

	- module. exports ={
mode: 'none,
 entry: './src/index.js',
 output: {
     filename: 'bundle js
 },
 module: {
     rules: [
        {
          test:/\.js$/,
          use: {
             loader: 'babel-loader',
             options:{
                presets: [
                   ['@babel/preset-env', { modules: 'commonjs' }]
                ]
              }
           },
           optimization: {
              // 开启副作用特性
              sideEffects: true
              //模快只导出被使用的成员
              // usedExports: true
              //尽可能合并每一个模块到一个函数中
              // concatenateModules: true,
              //压缩输出结果
              // minimize: true
           }
         }
      ]
 }

}

- package.json

	- 添加sideEffects属性并设置为false

		- 标识当前package.json所影响的项目当中的所有代码都没有副作用

	- yarn webpack

		- Ok:成功去掉没有副作用的代码

- 注意

	- 使用sideEffects的前提就是确保你的代码真的没有副作用
	- package.json

		- "sideEffects": false ⥤ "sideEffects": [

"./src/extend.js", "*.css" ]

	- yarn webpack

		- OK: 有副作用的代码正常被打包

- Code Splitting (代码分包,代码分割)

- webpack打包弊端

	- 所有代码最终都被打包在一起
	- 导致bundle体积过大
	- 并非每个模块在启动时都是必要的
	- HTTP1.1缺陷

		- 同域并行请求限制
		- 每次请求都有一定的延迟
		- 请求的Header浪费带宽流量

- 解决:分包,按需加载

	- 多入口打包(Multi Entry)

		- 适用于多页应用程序

			- 一个页面对应一个大包入口
			- 不同页面的公共部分单独提取

		- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js', album: './src/album. js' }, output: { filename: '[name].bundle. js' }, module: { rules: [ { test: 八.csss/, use: [ 'style-loader', 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin() new HtmlWebpackPlugin({ title: 'Multi Entry' template: './src/index.html', filename: 'index.html' }), new HtmlWebpackPlugin({ title: 'Multi Entry', template: './src/aibum. html', filename: 'album.html" }) } } - yarn webpack

			- OK: 但是两个打包生成的html都引用了所有的模块
			- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js', album: './src/album. js' }, output: { filename: '[name].bundle. js' }, module: { rules: [ { test: 八.csss/, use: [ 'style-loader', 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin() new HtmlWebpackPlugin({ title: 'Multi Entry' template: './src/index.html', filename: 'index.html', chunks: ['index'] }), new HtmlWebpackPlugin({ title: 'Multi Entry', template: './src/aibum. html', filename: 'album.html", chunks: ['album'] }) } } - yarn webpack

				- Ok~

		- 提取公共模块(Split Chunks)

			- 不同入口肯定会有公共模块
			- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js', album: './src/album. js' }, output: { filename: '[name].bundle. js' }, optimization: { splitChunks: { chunks: 'all' } }, module: { rules: [ { test: 八.csss/, use: [ 'style-loader', 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin() new HtmlWebpackPlugin({ title: 'Multi Entry' template: './src/index.html', filename: 'index.html', chunks: ['index'] }), new HtmlWebpackPlugin({ title: 'Multi Entry', template: './src/aibum. html', filename: 'album.html", chunks: ['album'] }) } } - yarn webpack

				- Ok: 除了两个模块外也生成了公共模块

	- 动态导入(Dynamic Imports)

		- 按需加载

			- 加载数据(不做描述)
			- 加载模块

				- 需要用到某个模块时,再去加载这个模块

		- 动态导入的模块会被自动分包
		- 相对多入口打包,更为灵活
		- 使用

			- index.js

				- 静态导入

					- import posts from './posts/posts'

import album from './album/album' const render = () ⥤ { const hash = window.location.hash || 'posts' const mainElement = document.queryselector('.main') mainElement.innerHTML = '' if (hash === 'posts') { mainElement.appendchild(posts()) } else if { mainElement.appendchild(album()) } } render() window. addEventListener('hashchange', render) - 问题: 无论何时两个模块都会加载

				- 动态导入

					- // import posts from './posts/posts'

// import album from './album/album' const render = () ⥤ { const hash = window.location.hash || 'posts' const mainElement = document.queryselector('.main') mainElement.innerHTML = '' if (hash === 'posts') { // mainElement.appendchild(posts()) import('./posts/posts').then(({ default: posts })⥤{ mainElement.appendChild(posts()) }) } else if { // mainElement.appendchild(album())
import('./album/album').then(({ default: album })⥤{ mainElement.appendChild(album()) }) } } render() window. addEventListener('hashchange', render) - yarn webpack

						- OK: 打包后生成了三个包,一个主入口,一个按需加载的,一个公共的

- 魔法注释

- // import posts from './posts/posts'

// import album from './album/album' const render = () ⥤ { const hash = window.location.hash || 'posts' const mainElement = document.queryselector('.main') mainElement.innerHTML = '' if (hash === 'posts') { // mainElement.appendchild(posts()) import(/* webpackChunkName: 'posts' /'./posts/posts').then(({ default: posts })⥤{ mainElement.appendChild(posts()) }) } else if { // mainElement.appendchild(album())
import(/
webpackChunkName: 'album' */'./album/album').then(({ default: album })⥤{ mainElement.appendChild(album()) }) } } render() window. addEventListener('hashchange', render) - yarn webpack

	- OK~

- 按需打包到同一个文件中

	- // import posts from './posts/posts'

// import album from './album/album' const render = () ⥤ { const hash = window.location.hash || 'posts' const mainElement = document.queryselector('.main') mainElement.innerHTML = '' if (hash === 'posts') { // mainElement.appendchild(posts()) import(/* webpackChunkName: 'components' /'./posts/posts').then(({ default: posts })⥤{ mainElement.appendChild(posts()) }) } else if { // mainElement.appendchild(album())
import(/
webpackChunkName: 'components' */'./album/album').then(({ default: album })⥤{ mainElement.appendChild(album()) }) } } render() window. addEventListener('hashchange', render) - yarn webpack

		- Ok: 生成components文件和一个公共模块文件

- 提取CSS到单个文件中

- MiniCssExtractPlugin

	- 可以将css代码从打包结果中提取出来的插件
	- 用它可以实现css模块的按需加载
	- 使用

		- yarn add mini-css-extract-plugin --dev
		- webpack.config.js

			- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js', album: './src/album. js' }, output: { filename: '[name].bundle. js' }, module: { rules: [ { test: 八.csss/, use: [ // 'style-loader', // 将样式通过style标签注入html中 MiniCssExtractPlugin.loader, //用于 将单独打包的css文件通过link方式注入 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Dynamic Import' template: './src/index.html', filename: 'index.html' }) new MiniCssExtractPlugin(), } } - yarn webpack

				- OK~

- OptimizeCssAssetsWebpackPlugin(压缩输出的CSS文件)

	- yarn add optimize-css-assets-webpack-plugin --dev
	- webpack.config.js

		- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js', album: './src/album. js' }, output: { filename: '[name].bundle. js' }, module: { rules: [ { test: 八.csss/, use: [ // 'style-loader', // 将样式通过style标签注入html中 MiniCssExtractPlugin.loader, //用于 将单独打包的css文件通过link方式注入 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Dynamic Import' template: './src/index.html', filename: 'index.html' }), new MiniCssExtractPlugin(), new OptimizeCssAssetsWebpackPlugin(), } } - yarn webpack

			- OK~

	- 推荐用法

		- webpack.config.js

			- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js', album: './src/album. js' }, output: { filename: '[name].bundle. js' }, optimization: { minimizer: [ new OptimizeCssAssetsWebpackPlugin() ] }, module: { rules: [ { test: 八.csss/, use: [ // 'style-loader', // 将样式通过style标签注入html中 MiniCssExtractPlugin.loader, //用于 将单独打包的css文件通过link方式注入 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Dynamic Import' template: './src/index.html', filename: 'index.html' }), new MiniCssExtractPlugin(), // new OptimizeCssAssetsWebpackPlugin(), } } - yarn webpack

				- OK: JS未自动压缩了:(
				- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') const TerserWebpackPlugin = require('terser-webpack-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js', album: './src/album. js' }, output: { filename: '[name].bundle. js' }, optimization: { minimizer: [ new TerserWebpackPlugin(), new OptimizeCssAssetsWebpackPlugin() ] }, module: { rules: [ { test: 八.csss/, use: [ // 'style-loader', // 将样式通过style标签注入html中 MiniCssExtractPlugin.loader, //用于 将单独打包的css文件通过link方式注入 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Dynamic Import' template: './src/index.html', filename: 'index.html' }), new MiniCssExtractPlugin(), // new OptimizeCssAssetsWebpackPlugin(), } } - yarn add terser-webpack-plugin --dev

- 输出文件名Hash(substitution)

- 一般部署前端资源文件时,都会启用服务器的静态资源缓存

	- 这样对于用户的浏览器而言,可以缓存住静态资源,后续不会再发起静态资源请求了
	- 提高了应用响应速度
	- 开启客户端静态资源缓存的问题

		- 如果缓存策略中缓存失效时间过短,效果不太明显
		- 过期时间设置比较长

			- 应用更新后重新部署,没法及时看见更新效果

- 生产模式下,文件名使用Hash

	- 生产模式下,文件名使用Hash
	- 不用担心文件更新后的问题
	- 几种hass

		- 普通hash

			- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js', album: './src/album. js' }, output: { filename: '[name]-[hash].bundle. js' }, module: { rules: [ { test: 八.csss/, use: [ // 'style-loader', // 将样式通过style标签注入html中 MiniCssExtractPlugin.loader, //用于 将单独打包的css文件通过link方式注入 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Dynamic Import' template: './src/index.html', filename: 'index.html' }) new MiniCssExtractPlugin({ filename: '[name]-[hash].bundle.css' }), } } - 整个项目级别的,项目中任何一个地方发生改动,hash值会发生变化

		- chunk hash

			- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js', album: './src/album. js' }, output: { filename: '[name]-[chunkhash].bundle. js' }, module: { rules: [ { test: 八.csss/, use: [ // 'style-loader', // 将样式通过style标签注入html中 MiniCssExtractPlugin.loader, //用于 将单独打包的css文件通过link方式注入 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Dynamic Import' template: './src/index.html', filename: 'index.html' }) new MiniCssExtractPlugin({ filename: '[name]-[chunkhash].bundle.css' }), } } - Chunck级别的hash,一旦chunk涉及内容改变,对应chunk的hash会发生变化 - 相对普通hash,chunk hash更精准一些。

		- content hash

			- 文件级别的hash,是根据文件内容生成的hash
			- 相比普通hash 和 chunk hash,content hash是更优的hash方式

				- 最适合解决缓存问题的

			- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js', album: './src/album. js' }, output: { filename: '[name]-[contenthash].bundle. js' }, module: { rules: [ { test: 八.csss/, use: [ // 'style-loader', // 将样式通过style标签注入html中 MiniCssExtractPlugin.loader, //用于 将单独打包的css文件通过link方式注入 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Dynamic Import' template: './src/index.html', filename: 'index.html' }) new MiniCssExtractPlugin({ filename: '[name]-[contenthash].bundle.css' }), } }

	- 控制hash长度

		- const { CleanWebpackPlugin }  =require("clean-webpack-plugin') 

const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') module. exports ={ mode: 'none' entry: { index:'./src/index. js',$$ album: './src/album. js' }, output: { filename: '[name]-[contenthash:8].bundle. js' }, module: { rules: [ { test: 八.csss/, use: [ // 'style-loader', // 将样式通过style标签注入html中 MiniCssExtractPlugin.loader, //用于 将单独打包的css文件通过link方式注入 'css-loader' ] } ] }, plugins: { new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Dynamic Import' template: './src/index.html', filename: 'index.html' }) new MiniCssExtractPlugin({ filename: '[name]-[contenthash:8].bundle.css' }), } }