`Webpack` 是一个静态资源打包工具。

它会以一个或多个文件作为打包的入口,将我们整个项目所有文件编译组合成一个或多个文件输出出去。输出的文件就是编译好的文件,就可以在浏览器端运行了。我们将 `Webpack` 输出的文件叫做 `bundle`。

一、为什么需要webpack

1.为什么使用webpack

 ①传统的书写方式,加载太多脚本会导致网络瓶颈,如不小心改变JavaScript文件加载顺序,项目会崩溃,还会导致作用域问题、js文件太大无法做到按需加载、可读性和可维护性太低的问题。

②当今JavaScript、css的语法规范不断更新,但是浏览器的兼容性却不能同步的更新,开发者可以通过 webpack 预处理器进行编译,自由的使用 JS、CSS 等语言的新语法。

③Vue 脚手架vue-cli、React 脚手架create-react-app、Taro 脚手架taro-cli都是使用webpack,开发者掌握 webpack后,可以自由配置脚手架,根据项目需要,去调整 webpack 配置,以提高项目性能。

④拥有依赖管理、动态打包、代码分离、按需加载、代码压缩、静态资源压缩、缓存等配置;扩展性强,插件机制完善,开发者可自定义插件、loader;webpack 社区庞大,更新速度快,轮子丰富;

1.由于浏览器解析html的顺序是从上至下,引入第三方库时便会存在先后顺序问题;作用域是由于存在多个JS文件时,window下有可能挂载多个相同或不同变量

→解决方案:使用立即调用函数表达式 IIFE 来创建独立作用域

2.js文件过于庞大时无法做到按需加载,如引用第三方工具库lodash等

→解决:

①(依赖nodeJS实现)使用commonJs的module.exports来抛出模块代码,使用require来引入模块

②(不依赖nodeJS实现)可以使用browserify、requireJS等打包工具实现

在没有各个 webpack 搭建的脚手架(create-react-app、vue-cli 等等)之前,我们通过在 HTML5 文件里引入一个个 Javascript 文件来进行开发,这就可能导致并行请求数量过多、存在重复代码等问题。而通过 webpack,我们可以使用 import、require 来进行模块化开发。在 webpack 中一切皆模块,js、css、图片、字体都是模块,而且支持静态解析、按需打包、动态加载、代码分离等功能,帮助我们优化代码,提升性能。

2.什么是webpack?

Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。

Webpack是一个模块打包器。在Webpack中会将前端的所有资源文件(js/json/css/img/less/...)都作为模块处理。它将根据模块的依赖关系进行分析,生成对应的资源

3.五个核心概念:

【入口(entry)】:指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。【输出(output)】:在哪里输出文件,以及如何命名这些文件。【Loader】:处理那些非 JavaScript 文件(webpack 自身只能解析 JavaScript和json)。【插件(plugins)】执行范围更广的任务,从打包到优化都可以实现。【模式(mode)】,有生产模式production和开发模式development

对loader的理解:webpack 本身只能处理JS、JSON模块,如果要加载其他类型的文件(模块),就需要使用对应的loader 。它本身是一个函数,接受源文件作为参数,返回转换的结果。loader 一般以 xxx-loader 的方式命名,xxx 代表了这个 loader 要做的转换功能,比如 css-loader。对plugins的理解:插件可以完成一些loader不能完成的功能。

4.配置文件

webpack.config.js : 用于存储webpack配置信息。

二、webpack与竞品

Webpack :为处理资源管理和分割代码而生,可以包含任何类型的文件。灵活,插件 多。

Parcel :是 0 配置工具, 用户一般无需再做其他配置即可开箱即用。

Rollup:用标准化的格式(es6)来写代码,通过减少死代码尽可能地缩小包体积。 一般只用来打包JS。 

构建一个简单的应用并让它快速运行起来?使用 Parcel。

构建一个类库只需要导入很少第三方库?使用 Rollup。

构建一个复杂的应用,需要集成很多第三方库?需要代码分拆,使用静态资源文件, 还有 CommonJS 依赖?使用 webpack。

 Vite 将成 为 Vue 的现代标配。甚至最近新推出的 Petite Vue 从开发、编译、发布、Demo几 乎全都是使用 Vite 完成。Webpack、Vite 作为前端热门的工程化构建工具,它们都有各自的适用场景。

 三、安装webpack

1.本地安转

npm install --save-dev webpack

# 或指定版本

npm install --save-dev webpack@

如果你使用 webpack v4+ 版本,并且想要在命令行中调用 webpack,你还需要安装 CLI。

npm install --save-dev webpack-cli

对于大多数项目,我们建议本地安装。这可以在引入重大更新(breaking change)版本时,更容易分别升级项目。 通常会通过运行一个或多个 npm scripts 以在本地 node_modules 目录中查找安装的 webpack, 来运行 webpack:

"scripts": {

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

}

2.全局安装

npm install --global webpack

不推荐 全局安装 webpack。这会将你项目中的 webpack 锁定到指定版本,并且在使用不同的 webpack 版本的项目中, 可能会导致构建失败。

安装node.js https://nodejs.org/zh-cn/ 最新LTS版本验证node版本 node -v验证npm版本 npm -v(可选)执行 npm i webpack webpack -cli --global (全局)安装webapck在当前文件目录下执行 webpack -v 验证版本npm init -y 初始化package配置文件npm install webpack webpack-cli --save-dev 本地安装webpack及webpack-cli

四、运行webpack

在终端中输入 webpack 回车会生成dist文件夹即打包后的文件 (使用全局webpack)webpack --status detailed 查看详细打包信息(使用全局webpack)npx webpack 使用当前目录webpack

进入项目目录,运行webpack,执行指令:

npx webpack

生成了一个main.js文件

开发模式:npx webpack ./src/main.js --mode=development生产模式:npx webpack ./src/main.js --mode=production`npx webpack`: 是用来运行本地安装 `Webpack` 包的。`./src/main.js`: 指定 `Webpack` 从 `main.js` 文件开始打包,不但会打包 `main.js`,还会将其依赖也一起打包进来。`--mode=xxx`:指定模式(环境)。

五、自定义webpack配置

实际上, webpack-cli 给我们提供了丰富的终端命令行指令,可以通过npx webpack --help 查看帮助信息。

npx webpack --entry ./src/index.js --mode production 设置入口文件

可是命令行不方便也不直观,而且还不利于保存配置的内容,可以采取配置config的方式。因此,webpack 还给我们提供了通过配置文件,来自定义配置参数的能力。

可以在当前目录下新建webpack.config.js文件,用commonJs写法抛出配置模块:

const { resolve } = require('path'); //node内置核心模块,用来设置路径。

module.exports = {

//入口是指依赖关系图的开始,从入口开始寻找依赖,打包构建。webpack 允许一个或多个入口配置。

entry: './src/js/app.js', // 入口文件配置(精简写法)

/*完整写法:

entry:{

main:'./src/js/app.js'

}

*/

//输出则是用于配置 webpack 构建打包的出口,如打包的位置,打包的文件名等等。

output: { //输出配置

clean: true, //打包前清理 dist 文件夹,在每次构建前清理 /dist文件夹,这样只会生成用到的文件。

filename: 'bundle.js',//输出文件名

path: resolve(__dirname, './dist')//输出文件路径(绝对路径),require的path为nodeJS自带,__dirname可直接匹配当前目录

},

mode: 'development' //开发环境(二选一) webpack编译模式,区分生产测试环境

//mode: 'production' //生产环境(二选一)

// 文件监视改动,自动产出bundle.js

devtool: 'inline-source-map'//精准定位代码行数,便于查看。在开发模式下追踪代码

watch: true //观察模式,自动检测变化,但需要手动刷新浏览器,如果其中一个文件被更新,代码将被重新编译,所以不必再去手动运行整个构建。

};

使用 webpack-dev-server

webpack-dev-server支持文件打包后的本地代码实时更新,提高webpack开发效率。提供了一个基本的 web server,并且具有 live reloading(实时重新加载) 功能。可以自动检测文件变化,从而重新编译,帮助我们自动实现浏览器的刷新。

先安装:

npm install --save-dev webpack-dev-server

也可以通过npx webpack命令后缀添加--watch 实现编译时自动检测文件变化(需要手动刷新浏览器)

本地安装: npm install webpack-dev-server -D

修改配置文件,告知 dev server,从什么位置查找文件:

module.exports={

...

devServer:{

static:'./dist'

},

}

以上配置告知 webpack-dev-server,将 dist 目录下的文件作为 web 服务的根目录。默认 `Webpack` 会将文件打包输出到 `dist` 目录下,我们查看 `dist` 目录下文件情况就好了

执行命令:

npx webpack serve --open

执行 npx webpack --dev-server --watch也可以 npx webpack serve

webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。

六、自动引入资源

1.什么是插件?

随着应用程 序增长,如果继续手动管理,就会变得困难起来。然而,webpack在打包流程中产生的编译过程,可以借助插件实现某些功能,通过一些插件可以使这个过程更容易管控。

插件是 webpack 的核心功能。插件可以用于执行一些特定的任务,包括:打包优化,资源管理,注入环境变量等。Webpack自身也是构建于你在webpack 配置中用到的相同的插件系统之上。

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。 多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而 多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。

2.使用html-webpack-plugin

本地安装:

npm install html-webpack-plugin -D

config中添加配置:

const path = require('path')

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports={

entry:'./src/index.js',

output:{

// 打包前清理 dist 文件夹

clean: true,

filename:'bundle.js',

path:path.resolve(__dirname,'./dist')

},

mode:'development',

devtool: 'inline-source-map' ,

//插件配置集合

plugins:[

new HtmlWebpackPlugin()

],

}

new HtmlWebpackPlugin() 中也可以添加配置项

plugins:[

...

new HtmlWebpackPlugin({

template:'./index.html',//指向的html

filename:'app.html',//被打包后的html文件名

inject:'body'// js打包后的生成位置

}),

]

七、引入资源

1.Resource-发送单独文件并导出Url

修改webpack.config.js配置: 

const path = require('path')

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {

//...

// 配置资源文件

module: {

rules: [{

test: /\.png/,

type: 'asset/resource'

}]

},

//...

}

 在module中设置文件类型及资源类型,output端中增加打包后的资源文件夹配置:

output:{

filename:'bundle.js',

path:path.resolve(__dirname,'./dist'),

clean:true,

assetModuleFilename:'images/[contenthash][ext]',

},

2.inline-导出资源的Data Url

module:{

rules:[

...

{

test:/\.svg$/,

type:'asset/inline'

}

]

}

 实际展示出的文件的url为base64格式

3.source-导出资源的源代码

module:{

rules:[

...

{

test:/\.txt$/,

type:'asset/source'

}

]

}

4.asset-通用资源类型-自动选择data url或源文件

module:{

rules:[

...

{

test:/\.jpg$/,

type:'asset',

parser:{

dataUrlCondition:{

maxSize:4 * 1024 * 1024 //当图片大小大于4M时生成资源文件,否则为base64 url

}

}

}

]

}

可以通过配置临界值实现自动切换资源格式,更加灵活可控(默认大小为8kb)

八、管理资源

除了以上四个资源模块,我们还可以通过webpack-loader引入其他类型的文件。webpack只能解析JS、JSON文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,通过loader可以实现在js上加载css等其他文件。

在 webpack 的配置中,loader 有两个属性:

1. test 属性,识别出哪些文件会被转换。2. use 属性,定义出在进行转换时,应该使用哪个 loader。

const path = require('path');

module.exports = {

output: {

filename: 'my-first-webpack.bundle.js',

},

module: {

rules: [{ test: /\.txt$/, use: 'raw-loader' }],

},

};

//以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:

//test 和 use。这告诉 webpack 编译器(compiler) 如下信息:

//“嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为'.txt' 的路径」时,

//在你对它打包之前,先 use(使用) raw-loader 转换一下。

1.加载CSS

本地安装css-loader及style-loader npm install css-loader -D并在规则中添加css

module:{

rules:[

...

{

test:/\.css$/,

use:['style-loader','css-loader']

}

]

}

应保证 loader 的先后顺序: 'style loader' 在前,而 'css loader' 在后。如果 不遵守此约定,webpack 可能会抛出错误。 

2.抽离和压缩CSS

在多数情况下,我们也可以进行压缩CSS,以便在生产环境中节省加载时间,同时还可以将CSS文件抽离成一个单独的文件。

抽离

实现这个功能,需要mini-css-extract-plugin这个插件来帮忙,本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。

本地安装mini-css-extract-plugin : npm install mini-css-extract-plugin -D基于webpack5构建,需要webpack5环境下才能正常使用和style-loader不同,MiniCssExtractPlugin会生成link标签从而引入外部css

Webpack5学习指南

将 loader 与 plugin 添加到你的 webpack 配置文件中。通过require引入后在plugins里添加引用,同时在原有css规则中替换掉style-loader,npx webpack打包后生效。

plugins:[

new HtmlWebpackPlugin({

template:'./index.html',

filename:'app.html',

inject:'body'

}),

new MiniCssExtractPlugin()

]

module:{

rules:[

...

{

test:/\.css$/,

use:[MiniCssExtractPlugin.loader,'css-loader']

}

]

}

在plugins里调用MiniCssExtractPlugin时也可以添加filename来自定义生成目录及文件名。

contenthash会随机生成字符串的文件名:

plugins:[

...

new MiniCssExtractPlugin({

filename:'styles/[contenthash].css'

}),

],

压缩

本地安装css-minimizer-webpack-plugin: npm install css-minimizer-webpack-plugin -D和其他插件不同,需在optimization中添加配置,并且mode要切换为production

//优化配置

optimization:{

minimizer:[

new CssMinimizerPlugin(),

],

}

//生产模式

mode:'production'

3.加载images图像

假如,现在我们正在下载 CSS,但是像 background 和 icon 这样的图像,要如何处理呢?在 webpack 5 中,可以使用内置的 Asset Modules,可以将这些内容混入我们的系统中,在 css文件里也可以直接引用文件,修改style.css 和入口 index.js,依赖于资源模块

module:{

rules:[

...

{

test:/\.png$/,

type:'asset/resource',

generator:{

filename:'images/[contenthash][ext]'

}

}

]

}

打包后:

import './style.css'

4.加载fonts字体

?使用 Asset Modules 可以接收并加载任 何文件,然后将其输出到构建目录。这就是说,我们可以将它们用于任何类型的文 件,也包括字体。

module:{

rules:[

...

{

test:/\.(woff|woff2|eot|ttf|otf)$/,

type:'asset/resource'

}

]

}

在css中引用并创建字体图标库

@font-face {

font-family: 'iconfont';

src: url('../icon/iconfont.ttf') format('truetype');

}

5.加载数据

可以加载的有用资源还有数据,如 JSON 文件,CSV、TSV 和 XML。类似于 NodeJS,JSON 支持实际上是内置的,也就是说 import Data from loader 和 './data.json' 默认将正常运行。

根据数据格式安装不同的loader模块,比如要导入 CSV、TSV 和 XML,可以使用csv-loader和xml-loader。本地安装 csv-loader xml-loader : npm install csv-loader xml-loader -D在项目中引入csv、xml等数据格式时会自动转化为对象、数组等JS数据格式

module:{

rules:[

...

{

test:/\.(csv|tsv)$/,

use:'csv-loader'

},

{

test:/\.xml$/,

use:'xml-loader'

}

]

}

//现在,你可以 import 这四种类型的数据(JSON, CSV, TSV, XML)中的任何一种,

//所导入的 Data 变量,将包含可直接使用的已解析 JSON

6.自定义JSON模块parser

通过使用自定义 parser 替代特定的webpack loader,可以将任何toml、yaml或json5文件作为JSON导入。

本地安装  toml yaml json5 :npm install toml yaml json5 -D

引入模块:

const toml = require('toml')

const yaml = require('yaml')

const json5 = require('json5')

添加规则:

module:{

rules:[

...

{

test:/\.xml$/,

use:'xml-loader'

},

{

test:/\.toml$/,

type:'json',

parser:{

parse:toml.parse

}

},

{

test:/\.yaml$/,

type:'json',

parser:{

parse:yaml.parse

}

},

{

test:/\.json5$/,

type:'json',

parser:{

parse:json5.parse

}

}

]

}

7.使用babel-loader

webpack 自身可以自动加载JS文件,就像加载JSON文件一样,无需任何 loader。但加载的JS文件会原样输出,即使你的JS文件里包含ES6+的代码,也不会做任何转化。这时我们就需要Babel来帮忙。Babel 是一个 JavaScript 编译器,可以将 ES6+转化成ES5,将js代码转化以提供兼容度支持。在Webpack里使用Babel,需要使用babel-loader。

babel-loader: 在webpack里应用 babel 解析ES6的桥梁@babel/core: babel核心模块@babel/preset-env: babel预设,一组 babel 插件的集合

本地安装: babel-loader @babel/core @babel/preset-env : npm install babel-loader @babel/core @babel/preset-env -D

在 webpack 配置中,添加babel-loader到module中:

module:{

rules:[

...

{

test:/\.js$/,

exclude:/node_modules/,

use:{

loader: 'babel-loader',

options:{

presets:['@babel/preset-env']

}

}

}

]

}

注意:

需在exclude中排除node_modules包如需兼容async/await语法则还需要添加regeneratorRuntime模块。regeneratorRuntime是webpack打包生成的全局辅助函数,由babel生成,用于兼容async/await的语法。npm install --save @babel/runtime【这个包中包含了regeneratorRuntime,运行时需要】npm install --save-dev @babel/plugin-transform-runtime【这个插件会在需要regeneratorRuntime的地方自动require导包,编译时需要】

module:{

rules:[

...

{

test:/\.js$/,

exclude:/node_modules/,

use:{

loader: 'babel-loader',

options:{

presets:['@babel/preset-env'],

plugins:[

[

'@babel/plugin-transform-runtime'

]

]

}

}

}

]

}

九、代码分离

代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。项目中若存在多个入口文件时,则需要代码分离;若存在多个模块共用的代码时,也需要分离代码来防止重复打包。

代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。常用的代码分离方法有三种:

入口起点:使用 entry 配置手动地分离代码。防止重复:使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离 chunk。动态导入:通过模块的内联函数调用来分离代码。

1.入口起点

module.exports = {

entry: {

//将其他包命名在entry中

index: './src/index.js',

another: './src/another-module.js',

},

output: {

//并在output输出端中直接配置[name]对应entry中的key

filename: '[name].bundle.js'

//...

},

//...}

执行npx webpack后便会提示相应包名被分别打包且都被引入

存在一些隐患:

如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中。这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来。

2.防止重复

Entry dependencies

入口依赖,当图中两个模块共有lodash时,会抽离出来并取名为lodash。配置 dependOn option 选项,这样可以在多个 chunk 之间共享模块:

entry:{

index:{

import:'./src/index.js',

dependOn:'shared'

},

another:{

import:'./src/another-module.js',

dependOn:'shared'

},

shared:'lodash'

index:'./src/index.js',

another:'./src/another-module.js'

},

打包后多出的shared.budle.js即为定义中的模块,此时lodash是共用的,做到了模块的去重和分离

SplitChunksPlugin

可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。

配置时依旧可以采用独立命名

entry:{

index:'./src/index.js',

another:'./src/another-module.js'

},

在 optimization 优化配置项中添加 splitChunks

optimization:{

...

splitChunks:{

chunks:'all'

}

},

执行之后可以发现,使用 optimization.splitChunks 配置选项之后,index.bundle.js 和 another.bundle.js 中已经移除了重复的依赖模块。注意,插件将 lodash分离到单独的chunk,并且将其从main bundle 中移除,减轻了大小。

3.动态导入

当涉及到动态代码拆分时,webpack 提供了两个类似的技术。

第一种,也是推荐选择的方式是,使用符合ECMAScript提案的import()语法来实现动态导入,且不影响其他模块抽离方式。

async-module.js 假设为功能模块:

// async-module.js

function getComponent(){

return import('lodash').then(({default:_})=>{

const element = document.createElement('div')

element.innerHTML = _.join(['hello','webpack'],' ')

return element

})

}

getComponent().then((element)=>{

document.body.appendChild(element)

})

在入口文件中导入:

import './async-module'

第二种,则是webpack的遗留功能,使用 webpack 特定的 require.ensure。

4.懒加载

懒加载或按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

依旧通过import直接引入模块,区别在于:何时调用何时加载模块

const button = document.createElement('button')

button.textContent = '点击执行加法运算'

button.addEventListener('click', () => {

import(/* webpackChunkName: 'math' */ './math.js').then(({ add

}) => {

console.log(add(4, 5))

})

})

document.body.appendChild(button)

加上注释 webpackChunkName:'模块名' 后,可以定义打包后的模块名

import(/* webpackChunkName:'math' */'./math.js')

第一次加载完页面, math.bundle.js不会加载,当点击按钮后,才加载 math.bundle.js文件。

5.预获取/预加载模块

在声明import时,使用下面这些内置指令,可以让webpack输出“resource hint(资源提示)”,来告知浏览器:

prefetch(预获取):将来某些导航下可能需要的资源 ,即在浏览器网络空闲时再获取资源preload(预加载):当前导航下可能需要资源,和懒加载效果类似

prefetch

依旧在import引入时的注释中添加

const button = document.createElement('button')

button.textContent = '点击执行加法运算'

button.addEventListener('click',()=>{

import(/* webpackChunkName:'math',webpackPrefetch:true */'./math.js').then(({add})=>{

console.log(add(4,5));

})

})

document.body.appendChild(button)

加上注释 webpackPrefetch: true 后,可以告诉 webpack 执行预获取。会生成并追加到页面头部,指示着浏览器在闲置时间预取 math.js 文件。

我们发现,在还没有点击按钮时,math.bundle.js就已经下载下来了。同时,在 app.html可以看到,点击按钮,会立即调用已经下载好的 math.bundle.js文件中的 add 方法。

preload

与 prefetch 指令相比,preload 指令有许多不同之处:preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。浏览器支持程度不同。

十、缓存

打包后的dist文件部署到服务器以后便能被浏览器客户端所访问,因浏览器的解析特性会优先选择缓存资源,我们要确保文件发生更新时浏览器能识别到,所以需要对输出文件的文件名做处理。

1.输出文件的文件名

我们可以通过替换 output.filename 中的 substitutions 设置,来定义输出文件的名称。webpack提供了一种使用 substitution (可替换模板字符串)的方式,通过带括号字符串来模板化文件名,其中,[contenthash] substitution 将根据资源内容创建出唯一的hash。当资源内容发生变化时, [contenthash] 也会发生变化。

module.exports = {

output: {

filename: '[name].[contenthash].js',

},

};

2.缓存第三方库

将第三方库 (library)  (例如 lodash)  提取到单独的 vendor chunk 文件中,是比较推荐的做法,因为第三方库文件很少像本地源码那样频繁修改,所以我们可以利用client的长效缓存机制,命中缓存来消除请求,并减少向server 获取资源,同时保证client和server的代码一致。

在 optimization.splitChunks 添加cacheGroups 参数并构建:

optimization:{

...

splitChunks:{

...

cacheGroups:{

vendor:{

test:/[\\/]node_modules[\\/]/,

name:'vendors',

chunks:'all'

}

},

}

}

3.将js文件放到一个文件夹中

目前,全部 js 文件都在 dist文件夹根目录下,我们尝试把它们放到一个文件夹中, 这个其实也简单,在输出配置中修改 filename,即output.filename加上scripts前缀即可汇总js文件至指定文件夹下生成。

output:{

filename:'scripts/[name].[contenthash].js',

...

}

十一、拆分开发环境和生产环境配置

1.公共路径

publicPath 配置选项在各种场景中都非常有用,我们可以通过它来指定应用程序中所有资源的基础路径。

output:{

filename:'scripts/[name].[contenthash].js',

path:path.resolve(__dirname,'./dist'),

clean:true,

assetModuleFilename:'images/[contenthash][ext]',

publicPath:'http://localhost:8080/'

},

基于环境设置

在开发环境中,我们通常有一个assets/ 文件夹,它与索引页面位于同一级 别。这没太大问题,但是,如果我们将所有静态资源托管至 CDN,然后想在生产环境中使用呢? 想要解决这个问题,可以直接使用一个 environment variable(环境变量)。假设我们有一个变量 ASSET_PATH:

import webpack from 'webpack';

// 尝试使用环境变量,否则使用根路径

const ASSET_PATH = process.env.ASSET_PATH || '/';

export default {

output: {

publicPath: ASSET_PATH,

},

plugins: [

// 这可以帮助我们在代码中安全地使用环境变量

new webpack.DefinePlugin({

'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH),

}),

],

};

Automatic publicPath

有可能你事先不知道 publicPath 是什么,webpack 会自动根据 import.meta.url、 document.currentScript、 script.src 或者 self.location 变量设置 publicPath。你需要做的是将 output.publicPath 设为 ' auto':

module.exports = {

output: {

publicPath: 'auto',

},

};

注意:在某些情况下不支持 document.currentScript,例如:IE 浏览器,不得不引入一个 polyfill,例如 currentScript Polyfill。

2.环境变量

想要消除 webpack.config.js 在开发环境和生产环境之间的差异,需要环境变量(environment variable)。webpack命令行环境配置的 --env 参数,可以允许传入任意数量的环境变量。而webpack.config.js 中可以访问到这些环境变量。例如,--env production 或--env goal=local。

npx webpack --env production 可以通过命令传入变量区分环境

对于我们的 webpack 配置,有一个必须要修改之处。通常,module.exports 指向配置对象。要使用 env 变量,你必须将 module.exports(抛出对象)转换为函数后即可动态传参

//...

module.exports = (env) => {

return {

//...

// 根据命令行参数 env 来设置不同环境的 mode

mode: env.production ? 'production' : 'development',

//...

}

}

3.拆分配置文件

生产环境和开发环境使用的是一个配置文件,我们需要将这两个文件单独放到不同的配置文件中。如 webpack.config.dev.js(开发环境配置)和 webpack.config.prod.js(生产环境配置)。在项目根目录下创建一个配置文件夹config 来存放他们。

开发环境:执行 npx webpack serve -c config文件路径生产环境:执行 npx webpack -c config文件路径

4.npm命令

每次打包或启动服务时,都需要在命令行里输入一长串的命令。我们将父目录的 package.json、 node_modules 与 package-lock.json拷贝到当前目录下,

可以在package.json中配置命令来方便使用,配置 npm 脚本来简化命令行的输入。

"scripts": {

"start": "webpack serve -c ./config/webpack.config.js --env development",

"build": "webpack -c ./config/webpack.config.js --env production"

},

开发环境运行脚本:npm run start生产环境运行脚本:npm run build

关闭生产环境打包的文件过大警告提示:

performance:{

hints:false

}

5.提取公共配置

拆分出的配置文件存在大量重复配置代码时,可以单独创建一个公共配置文件,手动的将这些重复的代码单独提取到一个文件里。创建 webpack.config.common.js,配置公共的内容。

6.合并配置文件

配置文件拆分好后,我们又需要保证配置合并没用的问题,这时候就需要webpack-merge这个工具。

安装 npm install webpack-merge -D创建新的config文件用以合并

const {merge} = require('webpack-merge')

const commonConfig = require('./webpack.config.common')

const productionConfig = require('./webpack.config.prod')

const developmentConfig = require('./webpack.config.dev')

module.exports=((env)=>{

switch(true){

case env.development:

return merge(commonConfig,developmentConfig)

case env.production:

return merge(commonConfig,productionConfig)

default:

return new Error('No matching configuration was found')

}

})

相关文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。