Webpack是一款模块打包工具,可以把多个文件打包成一个或几个文件,它不仅能打包JS文件, 还能打包css, image等静态资源。当然,在默认情况下,它只打包JS文件和JSON文件,因为它只认识JS文件和JSON文件。  

mkdir webpack-demo
cd webpack-demo
npm init -y

  创建webpack-demo项目,并创建package.json 文件,管理项目依赖和脚本命令。npm install webpack webpack-cli --save-dev,安装webpack。webpack4把webpack命令抽离出来,形成了一个单独的包webpack-cli ,安装它,才能在命令行中使用webpack命令。为什么要单独形成一个包呢?因为webpack-cli 还提供了两个其它的功能,init 和 migrate命令,使我们快速创建webpack配置和提供升级迁移。不过这两个基本不用,了解一下就可以了。只要记住安装webpack的同时安装上webpack-cli就可以了。mkdir src 存放源文件,touch src/index.js, touch src/component.js 文件,component.js 文件

export default (text = 'hello world') => {
const element = document.createElement('div');
element.innerHTML = text; return element;
}

  index.js 文件

import component from './component';
document.body.appendChild(component());

  npx webpack 生成了dist 目录,打包成功了。npm5之后,npx可以执行命令。webpack是怎么打包的?不是要写配置文件吗?webpack4提供零配置,没有配置文件,也能打包。webpack进行打包时,如果没有配置文件,默认入口是src/index.js,默认输出目录是dist,文件名是main.js. 但有一个WARNING,

  没有设置mode,webpack4 提供了一个mode 配置项 ,用什么模式进行打包,模式不同,打包后的文件内容不同。它有三个选项development, production, none. 看名字也就知道了,development 开发模式,这种模式下有利于开发,production,生产模式,这有利于生产环境。none就是什么都不做,很少用到它。mode 可以在命令行进行配置,   npx webpack --mode development ,查看一下dist/main.js,没有压缩。npx webpack --mode produciton,生成的代码压缩了。开发模式下,没有压缩,而生产模式下,压缩代码。和我们想的是一样的,线上的代码都是压缩代码,开发肯定不压缩,要不然没有调试。 随着指令越来越长,手动输入就比较麻烦了,可以使用npm run 命令,在package.json的scripts的字段中

  "scripts": {
"dev": "webpack --mode development",
"build": "webpack --mode production"
}

  npm run build或npm run dev都能打包成功。怎么验证一下打包后的文件是正确的?手动建一个html 文件,script 引入 dist/main.js,这肯定没有问题。但有了webpack后,能自动化就自动化。webpack提供一个插件html-webpack-plugin,它会创建html文件,并自动引入打包后文件。npm i html-webpack-plugin -D,不过,这要写webpack配置文件了,零配置此时无能为力了。webpack有production和development两种mode,这也提醒我们要针对不同的环境,提供不同的配置,一种用于开发,一种用于生产。基本有两种实现方式,一个配置文件和多个配置文件。一个配置文件时,执行webpack命令时,提供环境变量,配置文件中获取到环境变量,根据不同的值,提供不同的配置。webpack  --env production,production就是环境变量, 如果没有设置值,那就true. 此时,配置文件中要exports 出一个函数,函数的第一个参数就环境变量,函数返回配置对象。新建webpack.config.js,webpack默认的配置文件名,webpack打包的时候,自动会找webpack.config.js文件,

module.exports = (env) => {
if (env === 'production') {
return ({
// 生产环境配置英
})
} else {
return ({
// 开发环境配置英
})
}
}

  多个配置文件就是为每一个环境提供一个配置文件,执行webapck命令时,用--config指定使用哪一个配置文件。我更倾向于使用多个配置文件,简洁明了。新建webpack.dev.config.js.  配置文件有几个重要的概念,entry, output, module, plugins。

  entry:打包的入口文件,就是webpack在进行打包的时候,从哪个文件开始。

  output:打包后的文件放到什么地方,以及文件名是什么

  module:处理哪些模块,用什么规则处理,规则就是指定loaders。loaders就是告诉webpack怎么处理不认识的文件,把它转化成JS,因为webpack只认识js文件, 只有转化成JS,webpack才能进行打包

  plugins:打包过程其它的事情,比如压缩,生成html文件,扩展了webpack的功能

  由于有零配置,webpack提供了默认的entry和output, 如果觉得ok,配置文件中可以只写module 和plugins,mode也可以在配置文件中配置,npm run 命令中的 --mode去掉,webpack.dev.config.js如下

const  HtmlWebpackPlugin  = require("html-webpack-plugin");
module.exports = {
mode: 'development',
plugins: [
new HtmlWebpackPlugin(),
],
};

  如果觉得不ok 的话,可以写entry 和output, 把它覆盖掉。webpack.dev.config.js如下

const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin'); // 引入插件 module.exports = {
mode: 'development',
entry: path.join(__dirname, 'src/index.js'),
output: {
path: path.join(__dirname, 'dist'),
filename: 'main.js'
},
plugins: [
new htmlWebpackPlugin()
]
}

  output的path要使用绝对路径,因此使用了Node.js内置path模块。plugins是个数组,每一个用到的插件都是数组中的一项。具体到每一个插件呢?插件都会暴露出构造函数,通过new 调用,就可以使用,如果插件还有配置项,就给构造函数传递一个配置对象作为参数。dev命令改成

 "dev": "webpack --config webpack.dev.config.js",

  npm run dev 打包成功,查看一下,没有问题。

  但前端不止JS,还有CSS,图片等。想要把页面更美观一点,写一点CSS,在src下新建style.css

body {
background: cornsilk;
}

  并index.js引入,打包,发现wepack 不认识CSS,要配置loader。处理CSS两个基本loader,css-loader和style-loader, npm i css-loader style-loader -D

module.exports = {
mode: 'development',
module: {
rules: [
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
],
},
plugins: [
new HtmlWebpackPlugin(),
],
};

  当使用多个loader来处理同一个文件时,要注意loader的执行顺序,它是按照从右到左,从下到上的顺序执行。css-loader 先执行,然后把执行结果交给style-loader,最终style-loader返回js,这样webpack才能打包。css-loader:将css的代码按照commonJS的规范转化成js,将css代码输出到打包后的JS文件中。style-loader是把包含css内容的JS代码,挂载到页面的<style> 标签中。style 是window对象属性。

  当在一个js文件中引入css文件时,css-loader会把css代码放到最后的打包文件中。这时如果新建一个html页面,用src引入bundle.js,css文件并不生效,页面并没有样式。需要使用style-loader,重新进行打包,这时html引入新的打包后的文件,样式显示,查看页面源代码,可以发现header中有了style 标签,原来这就是style-loader的作用。它将样式挂载到window对象的style 属性。

  处理图片,webpack内置Asset Modules(资源模块),代替了url-loader, file-laoder

{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
}

  Asset Modules有几种type: asset/resource: 创建一个单独的文件并导出它的url,此时如果你import image或在css中url(image), 这个image会作为一个单独的文件被打包到输出目录。 asset/inline: 导出一个资源的data url,如果资源是图片,它会打包成base64编码,内联到打包生成的文件中。asset/source: 导出资源的源代码,把文件当作一个字符串,比如import txt文本。asset: 在导出一个data url 和创建一个单独文件之间进行选择,到底选择哪一种形为,依赖资源的大小。通过parse进行设置

{
test: /\.(png|jpg)$/,
type: "asset",
parser: { dataUrlCondition: { maxSize: 8000 } },
}

  Dataurl 就是base64编码,也就是说,当资源大于8kb时,输出为单独的文件,小于8kb,输出base64编码,直接内嵌入打包后文件中。在style.cs中引入背景图片

body {
background: cornsilk;
background-image: url("./logo.png");
background-repeat: no-repeat;
background-position: center;
}

  也可以在js中直接引入图片

import src from "./logo.png";

export default (text = "Hello world") => {
const element = document.createElement("div");
element.innerHTML = text;
const img = document.createElement('img');
img.src = src;
element.append(img)
return element;
};

  找一张图片,命名为logo.png, 放到src中,npm run dev 打包,没有问题。

  字体的处理和图片一样,也是使用内置的资源模块

{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
}

  配置好后,修改代码还是比较麻烦了,修改一下,就要npm run dev一下,怎么自动打包,并刷新浏览器页面呢?webpack-dev-server,npm i webpack-dev-server -D,script 命令改一下。

"dev": "webpack server --config webpack.dev.config.js"

  npm run dev, 浏览器localhost:8080, 打开页面,改变样式,页面实时刷新。webpack-dev-sever是webpack自带的一个小型服务器,它会监测每个文件的变动,每当有文件改动时,就会重新打包,然后浏览器自动刷新页面,能时时看到代码的变动,这是所谓的liveload 或hotload(热更新)。webpack-dev-server 打包后文件是放到内存中的,而不像npm run build 把文件打包到硬盘上。想要访问服务器的文件,路径是http://[devServer.host]:[devServer.port]/[output.publicPath]/[output.filename],host默认是localhost, port是8080, publicPath默认是/, filename就是打包后文件。所以http://localhost:8080/就可以访问服务器的内容。如果想要配置webpack-dev-sever,配置文件提供了devServer 配置项,比如把端口改为9000

devServer: {
port: 9000, // 设置端口号
stats: 'errors-only', // 只有产生错误的时候,才显示错误信息,日志的输出等级是error.
overlay: true // 当有编译错误的时候,在浏览器页面上显示。
}

  npm run dev,重启服务器,这时看到项目启动在9000端口下。

  webpack-dev-server  还有两个配置项需要注意一下:

  static: webpack-dev-server 会把所有的静态文件(css, js, img 等)进行打包,放到服务器根目录下,供我们访问。但如果访问的资源不是由webpack-dev-server 打包生成的,它就找不到了,此时就要指定这些静态资源,比如index.html 文件,所在的位置,也就是static,否则就会显示404. 如果不使用webpack-html-plugin, webpack 是不会打包生成index.html的, 需要手动创建index.html, 这时index.html 就不是webpack-dev-server 打包生成的资源,就要指定它的位置。在浏览器中输入localhost:8080, 实际是向webpack-dev-server 请求index.html文件,如果webpack-dev-server 并没有生成这个文件,它就会static指定的目录去找。如果static没有配置,它会到项目根目录去找。由于都使用webpack-html-plugin,static也基本用不到。

  proxy: 代理,解决前端跨域问题,如axios.post(‘/api/login’), /api 就是标识,然后我们再在proxy 配置项里面给这个标识配置一个代理到的真实路径,如 ‘/api’ : ‘http://102.03.34.58/api’, 那么当我们调用接口的时候,实际上变成了http://102.03.34.58/api/login, 代理配置中的真实路径,就是会替换到请求中的标识

module.exports = {
devServer: {
proxy: {
'/api': 'http://102.03.34.58/api'
},
},
}    

  但有时候,接口没有那么规范,可能有的以api 开始,有的没有api, '/api' 标识还可以是一个对象

proxy: {
'/api': {
target: 'http://102.03.34.58',
pathRewrite: { '^/api': '' }
}
}

  target 是请求的服务器地址,后面没有api, 使用这种方式配置以后,代理会在前端请求中的/api前面加上target, 相当于还是请求了 http://102.03.34.58/api/login,所以这里增加了pathRewrite 路径重写,所有将/api/xxx 变成 /xxx, 去掉/api,所以最后真实的请求路径中 http://102.03.34.58/login. pathRewrite 中的属性是正则表达式,^以什么开始, 值 呢?就是匹配到的路径重写成会什么。

  proxy 中的属性'/api', 是前端发送请求时,请求接口中的url要加的参数,当真正发送请求时,webpack 服务中配置的代理碰到api 就会拦截,然后把它变成我们配置的真实的路径

  在开发过程中,如果有代码报错,怎么找错呢?使用source maps, 因为webpack在打包之后,包裹你的代码到模块中,并且会注入额外的代码,以让程序运行,最麻烦的地方是,打包之后,只生成一个文件。这时,如果程序运行后有问题,浏览器开发工具,只会把问题定位到打包后的文件中,而不是打包之前的源代码,你也不知道具体哪个文件出错了。使用source maps后就不一样了,问题会直接定位到源代码出错的文件中,因为source maps文件就是源代码和打包后的代码之间的映射文件。配置soure maps,也很简单,devtools: ‘eval-source-map’。source  maps有很多类型,挑选适合自己的。

devtool: "eval-source-map"

  假设在index.js中,console.log 写成了conso, 报错如下

  点击第二行的eval()就可以定位到源代码。

  开发环境的配置基本差不多了,那就再配置一下生产环境。新建webpack.prod.config.js,最基本的配置mode: production,和 html-webapck-plugin.

const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: 'production',
plugins: [
new HtmlWebpackPlugin(),
]
};

  script 的build命令改成

 "build": "webpack --config webpack.prod.config.js"

  抽离css到单独的文件。因为如果CSS在JS会造成页面的闪动。使用mini-css-extract-plugin 。它能把多个css文件打包成一个,因此,它有一个loader来处理抽取的工作。插件拿到loader生成的内容,输出一个单独的css文件。npm i mini-css-extract-plugin -D

const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: 'production',
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'style.css'
})
]
};

  css-loader 把 css文件按照commonJS 规范输出为JS,MiniCssExtractPlugin.loader 再把它抽取出来,最后MiniCssExtractPlugin把它输出为一个单独的CSS文件,命名为style.css。由于CSS中有背景图,还要处理一下

module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.(png|jpg)$/,
type: "asset",
parser: { dataUrlCondition: { maxSize: 8000 } },
}
]
},

  npm run build, 可以看到css输出到一个单独的文件中了。但CSS 并没有压缩,使用css-minimizer-webpack-plugin 进行压缩,npm i css-minimizer-webpack-plugin -D

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = {
mode: 'production',
module: {...},
plugins: [...],
optimization: {
minimizer: [
`...`,
new CssMinimizerPlugin(),
],
}
};

  module 和 plugins没有变化,这里就用...代替了。配置css压缩时,注意`...` 放到前面,因为在webpack5中,生产模式下,它自动会开启一些优化功能,比如JS的压缩。如要直接写css的压缩插件,就会把原有的优化功能覆盖掉。`...`  表示继承存在的压缩功能(extend existing minimizers)。

  处理JS代码。在开发环境下,并没有处理JS文件,因为自己开发用的浏览器一般都是最新的,绝大数JS功能都实现了,但生产环境可能要处理一下。因为用户使用什么浏览器,控制不了,有可能是旧版本的浏览器。处理JS文件用的是babel,npm i babel-loader, @babel/core, @babel/preset-env 等。

{
test: /\.js$/,
exclude: /node_modules)/,
use: "babel-loader"
}

  创建babel的配置文件 babel.config.json

presets: [
[
"@babel/preset-env",
{
"modules": false
}
]
]

  babel的配置有点复杂,我有一篇专门的文章来介绍Babel 7

  如果想要线上环境debug,可以配置生产模式下source map

 devtool: "source-map"

  代码分割

  动态导入:使用import()语法,

 import(/* webpackChunkName: "optional-name" */ "./module").then(
module => {...}
).catch(
error => {...}
);

  在src下面新建lazy.js

export default "Hello from lazy";

  然后index.js

export default (text = "Hello world") => {
const element = document.createElement("div");
element.innerHTML = text; element.onclick = () =>
import("./lazy")
.then((lazy) => {
element.textContent = lazy.default;
})
.catch((err) => console.error(err));
return element;
};

  注意使用lazy.default。当lazy.js用commonJS书写时,也要使用default。npm run build, 打包文件中多了958.js

  如要想要改变动态导入的模块输出的文件名,可以

import(/* webpackChunkName: "lazy" */ "./lazy").then()

  也可以配置output.chunkFilename, 这个以后再说。

  打包文件分离

  多入口,提取公共模块,多入口就是配置文件中的entry可以是一个对象 {index: ‘./src/index’,  main: ‘./src/mian’}, 有几个入口文件,就输出几个bundle 文件。output filename: [name].js, 抽取css时,css的name也要改成[name].css, 每一个入口中所包含的css都会打包成一个单获的css文件。新建一个print.js

import './style.css'
export default function print() {
console.log('print');
}

   配置文件如下

const path = require('path');
module.exports = {
mode: 'production',
entry: {
main: './src/index.js',
print: './src/print.js'
},
output: {
filename: "[name].js", // 双引号
path: path.join(__dirname, "dist")
},
module: {...},
plugins: [
new HtmlWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css" // 双引号
})
],
......
};

  npm run build,  dist 目录有点混乱了,output现在有一个clean配置,打包之前,它会先清理dist目录。

    output: {
filename: "[name].js", // 双引号
path: path.join(__dirname, "dist"),
clean: true
},

  但这种简单粗暴的多入口打包有个问题, 如果每一个入口中都引入机同的包,比如lodash,  那每一个入口对应的bundle都包含lodash,体积太大。npm i lodash, 并在index.js 和print.js 中引入,npm run build

  两个解决办法,一是dependOn,

    entry: {
main: {
import: './src/index.js',
dependOn: 'shared',
},
print: {
import: './src/print.js',
dependOn: 'shared',
},
shared: 'lodash',
}, optimization: {
minimizer: [
`...`,
new CssMinimizerPlugin(),
],
runtimeChunk: 'single',
},

  一种是SplitChunksPlugin, 提取公共模块

   optimization: {
minimizer: [
       `...`,
new CssMinimizerPlugin(),
],
splitChunks: {
chunks: 'all',
}
},

  抽取出来的模块名是webapck默认的。 也可以手动控制打包分离

splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendor",
chunks: "all",
},
},
},

  cache缓存

  文件名使用[contenthash], [name]-[contenthash].js. contenthash就是文件内容变,hash值就会变。output下的filename改为

chunkFilename: "[name].[contenthash].js",
filename: "[name].[contenthash].js",
assetModuleFilename: "[name].[contenthash][ext][query]"

  chunkFilename,就是没有entry中定义,而生成的chunk的文件名,比如异步加载的文件,splitChunks生成的文件。filename 就是entry中定义的入口。assetModuleFilename: 就是图片等用assetmodule处理的文件。同时,css文件名

new MiniCssExtractPlugin({
filename: "[name].[contenthash].css",
}),
  但有时,文件内容没有变化,conenthash仍然会有变化,因为在入口代码块中,webpack有特定的模板,特别是运行时和manifest,最好把这些模版抽取出来形成一个单独的代码块(chunk)。

optimization 配置加一个 runtimeChunk: 'single' 配置
 optimization: {
runtimeChunk: 'single',
}

  此时如果添加一个模块,比如a.js,然后引入index.js,再打包,你会发现,所有的contenthash都发生变化,为什么呢?理论上,vendor不应该发生变化啊?这是因为module.ids增加了optimization.moduleIds 设为 'deterministic'

optimization: {
moduleIds: 'deterministic',
runtimeChunk: 'single',    
}

  整个optimization

optimization: {
moduleIds: 'deterministic',
runtimeChunk: 'single', 
minimizer: [
`...`,
new CssMinimizerPlugin(),
],
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendor",
chunks: "all",
},
},
}, },

  在一个典型的webpack打包的应用中,有三种代码,你写的代码,第三方库,webpack运行时runtime和manifest 。代码在浏览器中运行时,wepack 需要runtime 和manifest来链接模块,它们包含加载和解析模块的逻辑。当模块进行解析时,它来连接这些模块。已经下载到浏器中的模块,需要连接在一起,它才能执行。懒加载的模块需要解析。

  打包之后,整个应用变成了一个压缩文件,也有可能分为几个文件,webpack是怎么管理程序运行需要的模块之间的交互的呢?这就是menifest data的作用。当webpack进入,解析,打包你的应用时,它会记录所有module 详细的信息,这些信息的集合就是manifest。当打包后的文件时进入浏览器运行时,webpack runtime 就是依据这些信息来解析和加载模块。不论你使用什么模块语法,import 或require 都会变成__webpack_require__, __webpack_require__ 指向是模块的标识符。使用manifiest data,runtime能够知道向哪里去找标识符后面的module。

  resolve解析

  当webpack去查找一个模块时,如果是相对路径引入,它会转化成绝对路径,寻找模块的位置。如果是引入的第三方模块(模块路径),它会先到配置文件中的resolve.modules定义的目录去查找。默认情况下,resolve.module指的就是node_modules,就像下面的配置一样

resolve: {
modules: ['node_modules']
}

  如果想让webpack去其它目录找第三言模块,则要把其它目录的路径配置到resolve.modules中,并且放在node_modules前面。.

resolve: {
modules: [path.resolve(__dirname, 'src/downloaded_libs'), 'node_modules']
},

  使用resolve.alias可以创建alias路径来代替原始的模块路径

resolve: {
alias: {
react: path.join(__dirname, 'node_modules/react'),
'react-dom': path.join(__dirname, 'node_modules/react-dom')
}
}

  指定react和react-dom的别名路径,当webpack遇到react和react-dom时,它只会到当前项目的node_modules里面去找,可以解决npm link时, react 和react-dom 从不同的项目中获取的问题。alias也能创建文件别名路径

resolve: {
  alias: {
    CssFolder: path.resolve(__dirname, 'src/stylesheets/')
  }
}

 import application from "../../../stylesheets/application.scss" 变成了 import application from "CssFolder/application.scss"

  通过alias和modules的配置,如果成功解析到路径,webpack就要看这个路径指向的是文件还是目录。如果是文件,并且有扩展名,文件就能找到了,如果没有扩展名,那就要找resolve.extensions中配置的扩展名。

resolve: {
extensions: ['.js', '.jsx']
}

  如果路径指向的是一个目录,webpack先找package.json, 如果有,它会找resolve.mainFields定义的字段,

 resolve: {
mainFields: ['browser', 'module', 'main'],
},

  然后拿着这些字段到package.json去匹配,看package.json中有没有定义这些字段,第一个匹配成功的就是要找的文件的路径。如果没有pacakge.json或resolve.mainFields返回的是无效的路径,就要查找

  resolve.mainFiles定义的文件名,文件名如果没有扩展名,还是找resolve.extensions中配置的扩展名。
 
  webpack的工作过程

  webpack有一个入口文件,入口文件是一个module,它有可能通过import指向另外的module,另外的module又有可能指向另外的module,层层import,因此webpack在打包的的过程中,它也会根据这种import关系,遍历整个import, 构建出整个项目的依赖树,整个项目的依赖树构建完成,也就意味着,项目中所需要的代码都包含进来了,包含的过程中也对代码进行处理,生成打包文件。代码处理的依据就是配置文件。

  当import 一个文件,webpack会到文件目录中找这个模块,使用的是resolve 配置,resovle 就是调整模块解析算法的。 如果解析失败,会报错,如果解析成功,就会找对应的loader进行处理,loader处理完成后,就会把处理完后的代码放到打包文件中。在整个打包构建的过程中,webpack 会暴露一些event出来,webpack插件可以拦截这些event,做一些事情,比如抽离css到一个单独的文件。

  每一个模块都解析完之后,webpack就会输出打包文件。打包文件就是在浏览器中执行的webpack的运行时,还包括manifest,它列出了将要加载的打包文件。

  当然在打包过程,你还可以设置分离点,生成单独的包。

  在内部,webpack通过chunks 管理打包过程,chunks是一小段代码,这些代码包含在打包文件中,能够在webpack的输出中看到,输出的文件称为chunks。

  注意点

  webpack每打包一次就会生成一个[hash], 项目中只要有一个文件发生变化,webpack就需要重新打包,生成一个新的[hash], [hash]值就会发生变化,所有打包文件名字中[hash]值就会发生变化。

  [chunkhash],就是每一个chunk有一个hash. 比如有两个入口文件,一个是index.js,它import index.css, 一个admin.js, 它import admin.css. 如果进行打包, index.js和index.css是一个chunkhash,  admin.js 和admin.css是一个chunkhash, 但这两个chunkhash是不同的hash。如果此时改掉了index.css,那么重新打包后的index.js 和index.css 这两个文件都会有新的chunkhash, 文件名发生变化。

  contenthash, 为每一个文件单独计算hash值,哪个文件内容发生变化,打包时,哪个文件的对应的hash值就会发生变化。

  webpack打包速度提升

  1,安装分析插件:基于时间的分析工具speed-measure-webpack-plugin, 基于产物内容的分析工具webpack-bundler-analyzer

  2, 提升编译阶段的效率:a, 减小编译的模块,使用IgnorePlugin, 最典型的是moment包,它配置了多语言,实际上只用一个语言。

module.exports = {
// ...
plugins: [
// ...
new webpack.IgnorePlugin(/^\.\/(?!zh-tw|en-gb)/, /moment[\/\\]locale$/)
]
}

  安装引用类库的模块,比如引入loadash/es

  使用DLLPlugin:将项目所依赖的框架模块单独构建打包,与普通的模块打包区分开

    b, 提升单个模块的构建速度。include 和exclude,babel-loader。需要注意,优先使用exclude, 在include 和exclude 基础上noParse. ts编译,去掉检查

    c,并行构建:中小型项目,并行构建可以并不适应,因为多线程之间的通信,大型项目可以使用thread-loader,  parallel-webapck

  3,提高打包过程中的效率: splunk chunk 和tree shaking: 以default模式引用的模块并不能tree shaking。引入单个对象的方式,无论是import * as ... 还是 import {...}, 都可以tree shaking. side effect ,babel 7 之后,module 选项是auto, 就是不会再把ESM转化成commonJS 了

  4,开启缓存,babel-loader缓存,使用cache-loader

  5 webpack5 持久化缓存:

  webpack 5 会跟踪每一个模块的依赖项,fileDependencies, ContextDependienices, missingDependencies, 当模块本身或其依赖项发生变化时,webpack 会找到所受影响的模块,重新进行构建。还可以配置失效。webpack5 会忽略插件的缓存设置,由引擎自身提供构建各环节的缓存的读写逻辑

Webpack5的更多相关文章

  1. webpack5文档解析(上)

    webpack5 声明:所有的文章demo都在我的仓库里 webpack5 起步 概念 webpack是用于编译JavaScript模块. 一个文件依赖另一个文件,包括静态资源(图片/css等),都会 ...

  2. 从零开始使用 webpack5 搭建 react 项目

    本文的示例项目源码可以点击 这里 获取 一.前言 webpack5 也已经发布一段时间了,其模块联邦.bundle 缓存等新特性值得在项目中进行使用.经过笔者在公司实际项目中的升级结果来看,其提升效果 ...

  3. Webpack5构建速度提升令人惊叹,早升级早受益

    为什么要升级? webpack4用的好好的,运行稳定,为什么要升级到webpack5, 每次升级,都要经历一场地震,处理许多loader和plugin API的破坏性改变. 请给我们一个充分的升级理由 ...

  4. 基于webpack5封装的cli工具packx

    安装 用 npm / yarn 安装: $ npm install -D packx $ yarn add -D packx 特性 基于 webpack5 支持 less,sass 支持 spa/mp ...

  5. 轻松搞定webpack5.x

    源码地址:https://gitee.com/cyp926/webpack-project.git "webpack": "^5.46.0", "we ...

  6. 学习 Webpack5 之路(优化篇)

    一.前言 从 0 到 1 学习的朋友可参考前置学习文章: 学习 Webpack5 之路(基础篇) 学习 Webpack5 之路(实践篇) 前置文章 学习 Webpack5 之路(基础篇) 对 webp ...

  7. 手把手教你如何使用 webpack5 的模块联邦新特性

    想象一下,在webpack5还没出来前,前端使用第三方组件库,例如使用 dayjs 日期处理库,都是通过 npm i dayjs -s 安装 dayjs 模块到项目里,然后就可以通过 require ...

  8. webpack5学习

    目录 1. Why Webpack? 2. Webpack上手 2.1 Webpack功能 2.2 需要安装的包 2.3 简易命令 3. Webpack配置文件 3.1 局部webpack打包 3.2 ...

  9. 从0开始基于Webpack5 搭建HTML+Less 前端工程

              基于Webpack5 搭建HTMl+Less的前端项目 新建一个文件夹(比如命名为webpack) 用编辑器打开该文件夹,并在编辑器的终端执行 npm init -y 自动创建pa ...

  10. Webpack干货系列 | Webpack5 怎么处理字体图标、图片资源

    程序员优雅哥(youyacoder)简介:十年程序员,呆过央企外企私企,做过前端后端架构.分享vue.Java等前后端技术和架构. 本文摘要:主要讲解在不需要引入额外的loader的条件下运用Webp ...

随机推荐

  1. three.js案例-web3d三维地图大屏炫酷3D地图下钻地图-附源码

    炫酷3D地图效果如下: 代码注释非常详细: create() { // 添加雾,随着距离线性增大,只能看到一个小是视野范围内的场景,地图缩小很多东西就会看不清 //this.scene.fog = n ...

  2. 解决小程序uni-app echars层级过高问题

    使用 force-use-old-canvas="false" 使用微信小程序的cover-view会有很多问题,并且不一定生效,只需要在canvas的标签内添加 force-us ...

  3. JDK源码阅读-------自学笔记(十一)(java.lang.String包装类)

    核心要点 String 类对象代表不可变的Unicode字符序列,因此我们可以将String对象称为"不可变对象" String的核心就是char[]字符串,外部写的string对 ...

  4. Sphinx 自动化文档

    目录 文章目录 目录 Sphinx 入门 reStructuredText 语法格式 标题.列表.正文.要点 表格 代码块 引用其他模块文件 引用静态图片 Sphinx Sphinx 是一个工具,它使 ...

  5. 5GC 关键技术之网络切片

    目录 文章目录 目录 前文列表 网络切片的需求来自于业务对网络提出的差异化要求 基于 3 大业务场景的切片 基于切片资源访问对象的切片 网络切片的商业价值 网络切片的底层技术支撑 网络切片的粒度 网络 ...

  6. 分布式任务调度内的 MySQL 分页查询优化

    作者:vivo 互联网数据库团队- Qiu Xinbo 本文主要通过图示介绍了用主键进行分片查询的过程,介绍了主键分页查询存在SQL性能问题,如何去创建高效的索引去优化主键分页查询的SQL性能问题.对 ...

  7. springcloud整合stream解决项目升级的多个消息中间件的收发问题

    cloud stream (一)简介Spring Cloud Stream 是一个用来为微服务应用构建消息驱动能力的框架.它可以基于 Spring Boot 来创建独立的.可用于生产的 Spring ...

  8. Django视图的请求与响应

    1.请求对象 (1)请求方式 print(request.method) (2)请求数据 (3)请求路径 # HttpRequest.path: 表示请求的路径(不含get参数) # HttpRequ ...

  9. Linux之命令提示符的颜色设置

    1.临时设置 执行下面的命令: export PS1="[\[\e[34;1m\]\u@\[\e[0m\]\[\e[32;1m\]\H\[\e[0m\] \[\e[31;1m\]\w\[\e ...

  10. go强大的垃圾回收机制。

    一.C语言中返回函数中局部变量值和指针(1) 在C语言中,一个函数可以直接返回函数中定义的局部变量,其实在函数返回后,局部变量是被系统自动回收的,因为局部变量是分配在栈空间,那为什么还可以返回局部变量 ...