webpack5入门基础

webpack 是代码编译工具,有入口、出口、loader 和插件;webpack 是一个用于 JavasScript 应用程序的静态模块打包工具;当 webpack 处理应用程序时会递归构建一个依赖关系图(dependency graph),其中包含应用程序的每一个模块,然后将这个模块打包成一个或者多个 bundle。

webpack 的本质是模块化打包工具,前端所有的资源都应当看成是一个模块,通过 webpack 的核心机制 loader 来处理,然后借助插件机制 plugin 来形成一个繁荣的生态;

学习可参考 webpack 中文文档;webpack 版本:5.73.0

一、为什么使用 webpack

1、解决作用域问题

问题原因
传统的 JavaScript 文件引入方式会在 window 对象上面绑定全局的变量,这也会严重的污染 window 对象,使得 window 对象变的臃肿;
解决办法
1、我们早期使用管理项目资源的工具 Grunt 和 Gulp ,他们是将所有项目文件拼接在一起,其实是使用了 JavaScript 的立即执行函数来解决作用域的问题,立即执行函数简称 IIFE ;当脚本被封装在 IIFE 内部时,我们可以安全的拼接和组合文件而不必担心作用域问题。

;(function(){
    
    
	let test = 'xxx'
})()
console.log(test) //is not defined

let res = (function(){
    
    
	return 'xxx'
})()
console.log(res) //xxx

上面是一个立即执行函数,如果在立即执行函数外面调用 test,test 是 ‘is not defined’ ,说明在立即执行函数中的变量是不能够在外部访问的,这样就不会污染到 window;如果想暴露一些东西给 window,则可以使用一个变量,将自执行函数赋值给这个变量,然后就可以在 window 中访问自执行函数的返回值;

2、代码拆分问题

问题原因
使用立即执行函数,将所有代码整合到同一个文件中,会造成代码体积过大,构建和加载的适合效率很慢,所有不得不对代码进行拆分;
解决办法
commonJS 运行是基于 node.js 环境的,commonJS 的问世引入了一个 require 的机制,它允许我们在当前文件中去加载和使用某个模块,只导入需要的模块;

const add = (a,b) => {
    
    
	return a+b
}
const minus = (a,b) => {
    
    
	return a-b
}
module.exports = {
    
    
	add,
	minus
}
//引入 serve.js
const math = require(./math.js)
console.log(math.add(1,2)) //3

上面代码我们通过 module.exports 来暴露对象,使用页面通过 require 来引入整个 module.exports 暴露出来的对象;需要注意的是 node 需要在 node.js 环境中运行,所以想查看这个效果需要开启 node 服务:打开控制台,找到需要运行的 JavaScript 文件 serve.js,执行 node serve.js 就可以在控制台看到结果;

3、让浏览器支持模块

1、借助 require.js (不够简洁)

//add.js
const add =(a,b)=> {
    
    
	return a+b
}
define([],function(){
    
    
	return add;
})
//html 引入requirejs data-main绑定一个入口文件
<script src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js" data-main="main"></script>
//main.js
require([./add.js],function(add){
    
    
	console.log(add(4,1))
})

定义 add 方法,通过 define 暴露出来,define 第一个参数是一个数组,里面填写所需要依赖的文件路径;在 html 页面引入 require.js 文件,通过 data-main 来绑定入口文件;在入口文件 main 中,使用require 方法,第一个参数是所需要的依赖文件,第二个参数是个方法,你可以在方法中使用add方法,并返回,这个返回值在浏览器中是可以查看的;
2、借助 ECMAScript 标准(浏览器支持不完整)

//add.js
const add =(a,b)=> {
    
    
	return a+b
}
export default add;
//html 
<script type="module>
	import add from './add.js'
</script>

或者

//add.js
export const add =(a,b)=> {
    
    
	return a+b
}
//html 
<script type="module">
	import {
    
     add } from './add.js'
</script>

这里需要声明 script 的 type = 'module';同时使用 export(输出多个)和 export default (输出单个)暴露出来的方法引入时也有一点差别;

上面这些手段虽然都能解决对应的问题,但是会比较麻烦,这里我们就引出 了更加强大的工具 webpack ;它可以打包 JavaScript 应用程序,支持 ES 模块化标准和 commonJS,可以扩展支持图片、字体文件、样式文件等静态资源打包;

4、构建工具对比

1、Webpack:适合一些复杂的应用,可以集成很多第三方库,可以拆分代码,使用静态资源文件,支持 commonJS、esmodule 等模块化模式;
2、Parcel:零配置,用户无需做其他的配置,开箱即用;适合简单的应用,并且可以快速的运行起来;
3、Rollup:用标准化的格式来编写代码(ES6)通过减少无用的代码来缩小包的体积;一般只能用来打包 JavaScript;适合一些类库并且只需要引入很少的第三方库;(Vue,React 框架)
4、Vite:基于 esmodule 构建,可以按需编译,热模块更新;可以和 Vue3 完美结合;

二、webpack 学习起步

1、安装

安装 webpack 之前需要确保已经安装了 node.js 的最新版本(参考:node版本升级);然后使用 npm 包管理工具来安装 webpack:
1、全局安装

//全局安装webpack webpack-cli
npm install webpack webpack-cli --global 
//查看webpack是否安装成功
webpack -v

不建议使用全局安装 webpack,那样不利于不同项目中使用不同版本的 webpack,也不利于项目的协调开发;
2、本地项目安装

//安装npm包管理配置文件package.json
npm init -y
//局部安装webpack webpack-cli
npm install webpack webpack-cli --save-dev

本地项目安装之前需要闲创建一个 npm 包管理配置文件;安装好 webpack 之后本地目录中会生成 node_modules 文件夹,里面就我们引入的依赖包;(切记,文件名不可以是webpack)

webpack-cli 不是必须的,只是用来处理命令行参数的工具;

2、运行打包

1、如果是在全局安装的 webpack 直接在控制台执行 webpack 就可以开始打包了;

webpack

2、如果是局部安装的 webpack,上面的运行命令就不行了,因为局部安装的并没有加入到系统环境变量中,所以控制台找不到 webpack 指令;这个时候我们需要使用下面的命令来运行;

npx webpack

npx 依托于 npm ,有了 npm 就可以直接使用 npx;npx 的作用是表示我们可以观察当前文件夹里面是否有我们想要去运行的命令,如果没有就会在这个目录的上一层目录中查找;

注意:运行打包可以在任意文件夹下面运行,运行之后生成的 dist 文件夹会在运行打包的文件夹下面;

3、自定义 webpack 配置

在根目录下创建 webpack.config.js 文件,用来配置 webpack 的配置项;

const path = require('path')
module.exports = {
    
    
	entry:'./src/index.js', //入口文件路径
	output:{
    
    
		filename:'bundle.js',//打包后的文件名
		path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置
		clean:true //每次打包前清空dist文件夹
	}
}

为了获取绝对路径,我们需要引入 node.js 的 path 模块;通过 resolve 来解析路径,_dirname (两个下划线)表示当前文件的物理路径,也就是 webpack.config.js 文件的上一级文件夹;第二个参数是指定打包文件保存的文件夹;

打包之后的 bundle.js 在 html 文件中通过标签引入就可以正常使用了;

4、自动引入资源

我们可以使用 webpack 插件 HtmlWebpackPlugin 来自动引入打包后的文件,这也就可以避免手动修改文件的路径; HtmlWebpackPlugin 插件会为你生成一个新的 HTML 文件在 dist 文件夹下,并且自动引入打包后的入口文件(script 标签),以及 CSS( head中的标签内);
1、安装插件

npm install --save-dev html-webpack-plugin

2、配置
在 webpack.config.js 文件中加新的配置:

const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    
    
	entry:'./src/index.js',
	output:{
    
    
		filename:'bundle.js',//打包后的文件名
		path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置
		clean:true //每次打包前清空dist文件夹
	},
	plugins:[
		new HtmlWebpackPlugin({
    
    
			tempalte:'./index.html',// 模板,根据指定模板生成新的html
			filename:'app.html',//生成文件的名称
			inject:'body'//指定script标签位置
		})
	]
}

这样 dist 文件夹中就会生成 bundle.js 的同时还会生成一个 app.html 文件,这个 app.html 文件就是根据 index.html 文件为模板生成的,并且文件自动引入 bundle.js ;

5、mode 选项

为了能在每次修改之后能够自动编译,并且让浏览器自动刷新,我们可以搭建一个开发环境来实现;
1、source map 实现代码调试
在 webpack.config.js 添加下面配置项,实现精准定位 bug 行数;

devtool:'inline-source-map'

2、自动编译
在初次编译的时候在命令行后面加一个 watch,这也内容修改后控制台就会自动编译了;

npx webpack --watch

3、webpack-dev-server
webpack-dev-server 提供了一个基本的 web server 并且具有实时重新加载的功能;
安装

npm install webpack-dev-server -D

配置

devServer:{
    
    
	statis:"./dist" //server根目录
}

启动

npx webpack server
//或者
npx webpack-dev-server
//自动打开浏览器
npx webpack-dev-server --open

这里可以启动一个服务,一般是 http://localhost:8080/,然后在浏览器访问这个地址就可以实现自动更新浏览器了;

webpack-dev-server 实际上并没有输出任何的物理文件,它把打包后的 bundle 文件保存在内存里面,这也我们的开发效率提高了,webpack 的编译效率也提高了;

6、资源模块 module

在 webpack 出现之前,前端人员会使用 Grunt 、Gulp 等工具来处理资源,将 src 文件夹的文件移动到 dist 或者 build 目录中;然而 webpack 最出色的功能除了引入 js 还可以使用内置的资源模块;asset modules 来引入任何的其他类型资源,它允许 webpack 打包其他的文件(字体、图标);

资源模块有四种类型:asset modules type

asset/resource:发送一个单独的文件并导出URL;
asset/inline:导出一个资源的 Data URL;
asset/source:导出资源的源代码;
asset:导出一个资源的 Data URL 和发送一个单独文件之间自动选择;

6.1、resource 资源

在 webpack.config.js 新增 module 配置项;添加 rules 规则,通过 test 加上正则匹配指定类型的文件;

module:{
    
    
	rules:[{
    
    //规则
		test:/\.png$/, //正则定义加载文件的类型
		type:'asset/resource'
	}]
}
//页面使用
import imgSrc from './assets/test.png'

这个时候在页面上引用的时候就会获取到图片的路径;并且在 dist 文件夹下面可以看到我们导出的图片资源;

如果想修改图片存放位置和文件名可以进行如下操作:

output:{
    
    
	filename:'bundle.js',//打包后的文件名
	path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置
	clean:true, //每次打包前清空dist文件夹
	assetModuleFilename:'images/[contenthash][ext]' //contenthash 根据文件的内容生成一个hash字符串,ext表示扩展名
},

或者在 module 中加一个 generator

module:{
    
    
	rules:[{
    
    //规则
		test:/\.png$/, //正则定义加载文件的类型
		type:'asset/resource',
		generator:{
    
    
			filename:'images/[contenthash][ext]'
		}
	}]
}

注意:如果两处同时设置了,那么 generator 的优先级会更高;

6.2、inline 资源

在 dist 文件夹下面是看不到图片资源的,因为这种模式只导出了资源的 URL;这个 URL 是 base64 格式的资源路径;

6.3、source 资源

可以获取文本的内容,常用来获取 txt 文件的内容;

6.4、通用资源类型 asset

在 inline 和 resource 之间自由选择,默认情况下小于 8kb 的文件将会视为 inline 模块类型,否则视为 resource 模块类型;也可以通过设置 parser.dataUrlCondition.maxSize 来修改默认文件大小;

module:{
    
    
	rules:[{
    
    //规则
		test:/\.png$/, //正则定义加载文件的类型
		type:'asset',
		parser:{
    
    //自定义解析器里面的时间
			dataUrlCondition:{
    
    
				maxSize:4*1024*1024
			}
		},
		generator:{
    
    
			filename:'images/[contenthash][ext]'
		}
	}]
}

7、loader

webpack 除了可以使用资源模块来引入外部资源,还可以使用 loader 来引入其他类型的文件;webpack 只能理解 js 和 json 类型的文件,这是 webpack 自带的能力, loader 可以让 webpack 去解析其他类型的文件并且将这些文件转化为有效的模块,供应用程序使用;

loader 的定义在 module rules 下面定义一个 test 来识别那些文件被转换,use 属性定义在转化的时候使用那个 loader 来进行转化;

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

上面这段配置的意思是:webpack 在通过 import、require 去解析一个 .test 文件的时候,在对文件进行打包之前先使用 row-loader 转化一下;

7.1、加载 css

1、处理 css
安装 css-loader style-loader

//将css识别转化,让webpack可识别
npm i css-loader -D
//把css放置到页面 header 标签里面
npm i style-loader -D

安装成功之后在 webpack.config.js 文件的 module 下新增 rules :

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

多个 loader 可以在 use 里面以数组的形成传入,loader 执行顺序从 use 数组的后面往前面执行,先执行的 loader 会将结果返回传递给下一个 loader;并且这个先后执行顺序必须正确,否则不生效;需要先转化 css ,然后将 css 放到页面上面;

2、处理 less
安装 less-loader

npm i less-loader less -D

安装成功之后在 webpack.config.js 文件的 module 下新增 rules :

rules:[
	{
    
    
		test:/\.(css|less)$/,
		use:['style-loader','css-loader','less-loader']
	}
]
7.2、抽离和压缩 css

1、抽离
上面我们通过 loader 将 css 放到了 HTML 中,下面我们看一下将 css 单独放在一个文件中,然后通过 link 标签去加载;
安装插件 mini-css-extract-plugin

//webpack5 下才有这个插件
npm i mini-css-extract-plugin -D

在 webpack.config.js 引入插件

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
//插件使用
plugins:{
    
    
	new MiniCssExtractPlugin({
    
    
		filename:'styles/[contenthash].css' //制定打包后的css存放位置
	})
},
module:{
    
    
	rules:[{
    
    
		test:/\.(css|less)$/,
		use:['MiniCssExtractPlugin.loader','css-loader','less-loader']
	}]
}

使用插件的 loader MiniCssExtractPlugin.loader 替换掉 style-loader,这也 dist 文件夹会新增一个 styles 文件夹 ,打包后的 css 就会放在这个文件夹内,并且在 dist/index.html 文件内自动引入;

2、压缩
安装插件 css-minimizer-webpack-plugin

npm i css-minimizer-webpack-plugin -D

在 webpack.config.js 引入插件:

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
//这个插件不是在plugins中,而是在优化配置中做设置
mode:'production',
optimization:{
    
    
	minimizer:[
		new CssMinimizerPlugin()
	]
}

注意:这个时候的 mode 必须是 production;

7.3、加载 fonts 字体

可以直接借助 asset module 来接收和载入任何类型的资源;

module:{
    
    
	rules:[
		{
    
    
			test:/\.(woff|woff2|eot|ttf|otf)$/i,
			type:'asset/resource'
		}
	]
}
7.4、加载数据

json 是默认可以正常导入的,但是要导入 CSV、TSV 和 XML 类型的文件数据则需要 loader 来帮忙;
安装 csv-loader xml-loader

npm i csv-loader xml-loader

安装成功之后在 webpack.config.js 文件的 module 下新增 rules :

module:{
    
    
	rules:[
		{
    
    
			test:/\.(csv|tsv)$/i,
			use:['csv-loader']
		},
		{
    
    
			test:/\.xml$/i,
			use:['xml-loader']
		}
	]
}

然后页面引入这些类型的文件,就可以正常访问了,XML文件会转化成 js 对象,CSV 文件会转化成一个数组;

7.5、自定义 JSON 模块 parser

通过使用自定义 parser 替换特定的 webpack loader ,将 toml、yaml、json5 文件作为 json 模块导入;
安装 toml yaml json5

npm i toml yaml json5 -D

安装成功之后在 webpack.config.js 文件的 module 下新增 rules :

const toml = require('toml')
const yaml= require('yaml')
const json5 = require('json5')
module:{
    
    
	rules:[
		{
    
    
			test:/\.toml$/i,
			type:'json',
			parser:{
    
    
				parse: toml.parse
			}
		},
		{
    
    
			test:/\.yaml$/i,
			type:'json',
			parser:{
    
    
				parse: yaml.parse
			}
		},
		{
    
    
			test:/\.json5$/i,
			type:'json',
			parser:{
    
    
				parse: json5.parse
			}
		}
	]
}

8、babel-loader

webpack 只能做 js 打包,但是无法转化 js 代码; babel-loader 的主要任务是将 ES6 转化成低版本浏览器可以使用的代码;这里需要先安装三个包:

babel-loader:在 webpack 里面使用 babel 解析 ES6 的桥梁
@babel/core:babel 的核心模块
@babel/parset-env:babel 预设,一组 babel 插件的集合(将很多插件安装到一个插件里)

npm i babel-loader @babel/core @babel/parset-env -D

安装成功之后还需要安装 regeneratorRuntime 插件,这个是 webpack 打包生成的全局辅助函数,由 babel 生成,用于兼容 async/await 语法;

//包含regeneratorRuntime 插件运行的时候需要的内容
npm i @babel/runtime -D
//需要regeneratorRuntime 的地方自动require导包,然后编译的时候需要它
npm i @babel/plugin-transform-runtime -D

在 webpack.config.js 文件的 module 下新增 rules :

module:{
    
    
	rules:[
		{
    
    
			test:/\.js$/i,
			exclude:/node_module/,//不打包node_module里面的js
			use:{
    
    
				loader: 'babel-loader',
				options:{
    
    //参数
					presets:['@babel/preset-env'],
					plugins:[
						['@babel/plugin-transform-runtime']
					]
				}
			}
		}
	]
}

9、代码分离

代码分离是 webpack 最主要的特性之一,可以将代码分离到不同的 bundle 中;分离后的文件我们可以按需加载、并行加载;代码分离可以获取最小的 bundle ,可以控制资源加载的优先级,如果使用合理可以极大的节省加载时间;常用分离方式有三种:

9.1、配置入口节点

使用 entry 配置手动的分离代码;这种方法的问题是如果有多个入口,那么这些多个入口共享的文件会分别在每个包里重复打包;

const path = require('path')
entry:{
    
    
	index:'./src/index.js',
	other:'./src/other.js'
},
output:{
    
    
	filename:'[name].bundle.js', //name可以获取到entry里面入口的key
	path:path.resolve(__dirname,'./dist'),
}

多个入口,对应打包就会打包出多个出口,但是如果 index 和 other 同时使用了 lodash 包,那么在打包的时候会分别将 lodash 包加到 index 和 other 文件中;

9.2、防止重复

使用 Entry dependencies 或者 SplitChunkPlugin 去重和分离代码,配置 dependOn option 选项,这也可以在多个模块之间直接共享模块;
1、 Entry dependencies

const path = require('path')
entry:{
    
    
	index:{
    
    
		import:'./src/index.js',
		dependOn:'shared'
	},
	other:{
    
    
		import:'./src/other.js',
		dependOn:'shared'
	},
	shared:'lodash' //配置需要共享的模块
},
output:{
    
    
	filename:'[name].bundle.js', //name可以获取到entry里面入口的key
	path:path.resolve(__dirname,'./dist'),
}

将 lodash 模块单独打包在 shared 包中,让 index 和 other 共享;
2、SplitChunkPlugin

const path = require('path')
entry:{
    
    
	index:'./src/index.js',
	other:'./src/other.js'
},
output:{
    
    
	filename:'[name].bundle.js', //name可以获取到entry里面入口的key
	path:path.resolve(__dirname,'./dist'),
},
optimization:{
    
    
	splitChunks:{
    
    
		chunks:'all'
	}
}

这种方法会自动帮们做代码分割处理;

9.3、动态导入

当涉及到动态代码拆分时,webpack 提供了两种方法:import() 语法实现动态导入、webpack 遗留功能 require.ensure;这里推荐使用第一种,所以也只介绍第一种方法;
1、import

function get(){
    
    
	return import('lodash').then((default:_)=>{
    
    
		return _.join(['hello','webpack'],' ')
	})
}
get.then(res=>{
    
    
	console.log(res)
})

import 函数调用完成之后返回的是一个 Promise,所以可以直接使用 then 来链式调用;静态导入和动态导入是可以同时工作的;

下面是动态导入的两个比较好的应用:

9.3.1、懒加载

懒加载也叫按需加载,是优化网页的一种方式;它主要是将代码在一些逻辑断点处分离开,在完成某些操作之后立即引入需要的代码模块;这也能加快应用程序初始加载速度,也能减轻代码的体积;

//math.js
export add(a,b){
    
    
	return a+b
}
//页面使用
const button = document.createElement('button')
button.textContent = '+'
button.addEventListener('click',()=>{
    
    
	import(/*webpackChunkName:'math'*/'./math.js').then({
    
    add}=>{
    
     //注释这一段是修改打包后文件的名称
		console.log(add(1,2))
	})
})
document.body.appendChild(button)

上面这个例子,webpack 会将 math.js 打包成一个公共文件 math.bundle.js,但是在页面初始化的时候这个文件不回被加载,当点击按钮的时候才会被加载出来;
注意:我们可以在 import 引入资源的时候添加注释来为这个文件打包的时候命名:webpackChunkName:'math'

9.3.2、预加载模块

webpack4.6.0 以上版本增加了对预获取和预加载的支持;在声明 import 时,使用下面指令可以让 webpack 输出资源提示,来告诉浏览器:

prefetch:预获取,将来某些导航下可能需要的资源
preload: 预加载,当前导航下可能需要的资源

//math.js
export add(a,b){
    
    
	return a+b
}
//页面使用
const button = document.createElement('button')
button.textContent = '+'
button.addEventListener('click',()=>{
    
    
	import(/*webpackChunkName:'math', webpackPrefetch:true*/'./math.js').then({
    
    add}=>{
    
     //注释这一段是修改打包后文件的名称
		console.log(add(1,2))
	})
})
document.body.appendChild(button)

在引入注释处加上webpackPrefetch:true ,这也在打包的时候就会将 math.bundle.js 文件放到页面的 link 标签里面,并且标明是 prefetch 类型的引入文件;这也浏览器就会在首页内容都加载完毕之后网络空闲的时候去加载 math.bundle.js;

preload 方法加载的效果和懒加载的差不多,都是在操作之后需要的时候才会下载对应资源;
prefetch 方法是在浏览器空闲的时候预先加载好可能需要的资源,与操作无关;

10、缓存

由于获取资源比较耗费时间,浏览器会使用一个缓存机制,通过命中缓存以降低网络流量,是网站加载速度更快;然而在部署新版本的时候不改变资源文件名浏览器可能会认为你没有更新,就会使用缓存版本;

10.1、配置输出文件名
output:{
    
    
	filename:'[name].[contenthash].js'
}

在打包时输出文件名增加一个动态 hash 字符串,这也每次打包的文件名就不回重复了;

10.2、缓存第三方库

将第三方库(lodash)单独提取到一个固定名称的文件中,因为这些库一般不回做修改,所以可以利用缓存机制消除请求,减少向 server 获取资源;(目标是第三方共享文件)

optimization:{
    
    
	splitChunks:{
    
    
		cacheGroups:{
    
    
			vendor:{
    
    
				test:/[\\/]node_module[\\/]/,
				name:'vendors',
				chunks:'all'
			}
		}
	}
}

这样所有第三方的包就都被放到 vindors.bundle.js 中了;

10.3、将所有的 js 文件放到一个文件夹中
output:{
    
    
	filename:'scripts/[name].[contenthash].js'
}

11、拆分开发环境和生产环境的配置

11.1、公共路径(publicPath)

我们可以使用公共路径来指定应用程序中所有资源的基础路径;默认值是空字符串:“” ,webpack-dev-server 也会默认从 publicPath 为基准,使用它来决定在哪个目录下启用服务,来访问 webpack 输出的文件。

output:{
    
    
	publishPath:'/' //也可以是其他路径
}
11.2、环境变量

环境变量可以消除 webpack.config.js 在开发环境和生产环境之间的差异;webpack 的命令行 npx webpack --env 参数 允许你传入任意数量的环境变量,在 webpack.config.js 文件中可以访问到这个变量;想要访问环境变量 env 必须将 module.exports 转换成一个函数;
将当前打包环境变量设置为 production:

npx webpack --env production
//也可以携带一个 key 、value
npx webpack --env production --env global=local

在 webpack.config.js 文件中获取环境变量:

module.exports = (env) =>{
    
    
	console.log(env)
	return {
    
    
		mode: env.production ? 'production' : 'development'
	}
}

结果:

{
    
     WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, production: true }
//携带参数
{
    
    
  WEBPACK_BUNDLE: true,
  WEBPACK_BUILD: true,
  production: true,
  global: 'local'
}

js 压缩
webpack 本身可以对 js 文件进行压缩,但是如果我们配置了 css 压缩,那么原有的 js 压缩就会失效,这里我们看一下怎么配置 js 压缩:
安装插件 terser-weboack-plugin

npm i terser-weboack-plugin -D

在 webpack.config.js 文件中引入使用:

//压缩
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-weboack-plugin')
optimization:{
    
    
	minimizer:{
    
    
		new CssMinimizerPlugin()
		new TerserPlugin()
	}
}

上面这俩压缩插件只会在生产环境中生效,开发环境中不会压缩;

11.3、拆分配置文件

拆分配置文件的目的就是将生产环境和开发环境的配置文件分开,单独配置:webpack.config.dev.js、webpack.config.prod.js ;然后统一放到配置文件夹中;
1、开发环境

//webpack.config.dev.js
module.exports = {
    
    
	ebtry:{
    
    
		index:'./src/index.js'
	},
	output:{
    
    
		filename:'scripys/[name].js',
		path:path.resolve(__dirname,'../dist'),
		clean:true,
		assetModuleFilename:'images/[contenthash][ext]'
	},
	mode:'development',
	devtool:'inline-source-map',
	devServer:{
    
    
		static:'./dist'
	},
	optimization:{
    
    
		splitChunks:{
    
    
			cacheGroups:{
    
    
				vendor:{
    
    
					test:/[\\/]node_module[\\/]/,
					name:'vendors',
					chunks:'all'
				}
			}
		}
	}
}

开发环境不需要清理服务器缓存,不需要 publicPath,mode 可以直接设置为 development,需要 devtool,不需要压缩相关配置;

我们可以在控制台运行这个配置:

npx webpack -c ./config/webpack.config.dev.js

由于提前设置的 output.path = path.resolve(__dirname,'../dist') 这个时候打包的 dist 文件夹会替换 config 同级的 dist 文件夹;这个 dist 就可以用于开发环境代码的部署;
2、生产环境

//webpack.config.prod.js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-weboack-plugin')
module.exports = {
    
    
	ebtry:{
    
    
		index:'./src/index.js'
	},
	output:{
    
    
		filename:'scripys/[name].js',
		path:path.resolve(__dirname,'../dist'),
		clean:true,
		assetModuleFilename:'images/[contenthash][ext]',
		publicPath:'/'
	},
	mode:'production',
	optimization:{
    
    
		minimizer:{
    
    
			new CssMinimizerPlugin()
			new TerserPlugin()
		},
		splitChunks:{
    
    
			cacheGroups:{
    
    
				vendor:{
    
    
					test:/[\\/]node_module[\\/]/,
					name:'vendors',
					chunks:'all'
				}
			}
		}
	},
	performance:{
    
    //关闭提示信息
		hints:false
	}
}
11.4、合并配置文件

由于配置文件开发环境和生产环境中有很多相同的配置,我们可以把相同配置提取到 webpack.config.common.js 文件中;然后再将配置合并:
安装包 webpack-merge

npx i webpack-merge -D

在 config 文件夹下新增 webpack.config.js 文件:

const {
    
     merge } = require('webpack-merge')
const common = require('./webpack.config.common.js')
const prod= require('./webpack.config.prod.js')
const dev= require('./webpack.config.dev.js')
module.exports = (env) => {
    
    
	switch(true){
    
    
		case env.development:
			return merge(common,dev);
		case env.production:
			return merge(common,prod);
	}
}
11.5、npm 脚本

每次打包或者启动服务时,都需要在命令行输入一长串的命令,这里我们配置 npm 来简化命令行;
1、在项目中的 package.json 文件中

"scripts":{
    
    
	"start": "npx webpack serve -c ./config/webpak.config.js --env development"
}

在配置 npm 的时候,我们可以省略 npm 或者 npx

"scripts":{
    
    
	"start": "webpack serve -c ./config/webpak.config.js --env development"
}

2、在命令行运行

npm run start

由于改写命令行的时候在后面传入了环境变量,所以这个时候 start 就代表执行 development 环境的打包;

入门部分就到这里了,后面会继续深入学习 webpack!

猜你喜欢

转载自blog.csdn.net/weixin_43299180/article/details/125842672