webpack 构建打包工具

1,为什么要学webpack?

webpack 是一款可用于 前端项目构建和打包的工具,我们先来看看在webpack之前,我们是如何来在web中运行js

  • 1.1,使用多个脚本文件存放js代码

  • 但是,代码自上向下执行,js文件可能相互之间存在 依赖关系 ,所以顺序很重要,一旦 顺序错乱 ,页
    面可能就会出现问题
  • 导致:开发受限制,代码难以扩展
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>引入多个js文件时</title>
</head>
<body>
    <div>HTML代码</div>
    <div>代码自上向下执行,js文件可能相互之间存在依赖关系,所以顺序很重要,一旦顺序错乱,页面可能就会出现问题</div>
    <div>导致:开发受限制,代码难以扩展</div>
</body>
<!-- 外部CDN资源 外部的js文件 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.core.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/js/bootstrap.min.js"></script>
<!-- 本地js文件,可能对上面的外部CDN资源文件存在依赖关系 -->
<script src="./本地js文件/index1.js"></script>
<script src="./本地js文件/index2.js"></script>
<script src="./本地js文件/index3.js"></script>
<script src="./本地js文件/index4.js"></script>
<script src="./本地js文件/index5.js"></script>
</html>
  • 1.2,使用一个脚本文件存放js代码

如果我们将js代码放入一个js文件并引入:

  • 作用域问题
    • 例如多个js片段可能都会在window对象上定义全局变量,可能造成全局变量污染,而且会使 window对象变的臃肿
  • 文件太大
    • 如果js文件分为多个引入,页面会随着js文件的逐个加载而逐渐显示内容
    • 但是,全部放入一个js文件,会造成网络瓶颈,要等待js文件全部加载完成,才会显示页面,页面会造成短暂白屏,用户体验差
  • 可读性差
    • 所有代码放在一起,可读性差,过于混乱,臃肿
  • 可维护性差
    • 所有代码放在一起,维护时可能会对其他代码造成影响
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>将js代码放入一个js文件并引入</title>
</head>
<body>
	// 全部js代码放入一个文件
    <script src="./本地js文件/all.js"></script>
</body>
</html>
  • 1.3,早期如何解决作用域问题 IIFE

早先前,我们使用 GruntGulp 两个工具来管理我们项目的资源。
这两个工具称为任务执行器,它们将所有项目文件拼接在一起。利用了立即调用函数表达式(IIFE)-Immediatelyinvokedfunctionexpressions, 解决了大型项目的作用域问题;
当脚本文件被封装在 IIFE 内部时,你可以安全地拼接或安全地组合所有文件,而不必担心作用域冲突。

在这里插入图片描述
IIFE 解决作用域的方式:

// gulp 和 grunt 利用IIFE解决作用域问题
// 利用IIFE立即执行函数,每个脚本都在自己的作用域中执行,不会造成作用域冲突问题
// 避免了造成全局变量污染,window对象臃肿
; (function () { var myName = '小蛤蟆' })()
console.log(myName) //报错,因为函数内部变量,外部无法访问

// 如果想把函数内部变量暴露在window中
let result = (function () {
    var myName = '小蛤蟆'
    return myName
})()
// 使用脚本内的变量,且不会造成全局变量污染
// 将 IIFE 分配给一个变量,存储的不是IIFE自身,而是存储 IIFE 执行后返回的结果
console.log(result)

利用 IIFE 这样虽然解决了代码的作用于问题,但是:

  • js代码放在一个文件中,修改一处,就会重新构建整个文件
  • 如果我们只是需要使用这个文件中的一个方法,却需要引入构建整个文件
  • 会导致我们需要大规模的加载没有用到的代码块,无法实现按需引用构建
  • 1.4,拆分代码

面对上面情况,这个时候,node.js 的模块化就派上了用处。
node.js 所遵循的 COMMON JS 规范,它引入了 require 机制,它允许你在当前文件中 加载和使用某个模块
在这里插入图片描述
导入需要的每个模块,这一开箱即用的功能,帮助我们解决了代码拆分的问题。
CommonJS 没有浏览器支持,循环引用存在问题。同步执行的模块解析加载器速度很慢。虽然 CommonJS 是 Node.js 项目的绝佳解决方案,但浏览器不支持模块,我们似乎又遇到了新问题。

  • 在早期,我们可以使用 BrowserifyRequireJs 等打包工具编写能够在浏览器中运行的 CommonJS 模块。
  • 目前,我们还有一个选择,就是来自 Web 项目的好消息是,模块正在成为ECMAScript 标准的官方功能。ES6的模块化导入导出 。然而,浏览器支持不完整,版本迭代速度也不够快,还是推荐上面两个早期模块实现。早期的任务构建工具基于 Google 的 Closure 编译器,要求我们手动在顶部声明所有的依赖,开发体验不好。
  • 1.5,Webpack 搞定一切

    • webpack 是一个工具,可以 打包 你的 JavaScript 应用程序(支持 ESM 和 CommonJS),可以扩展为支持许多不同的静态资源,例如:images、fonts、stylesheets
    • webPack 关心性能和加载时间;它始终在改进或添加新功能,例如:异步地加载和预先加载代码文件,以便为你的项目和用户提供最佳体验。
    • webpack处理资源管理和分割代码 而生,可以包含任何类型的文件。灵活,插件多。
    • **webpack 运行在 node.js 环境中
      在这里插入图片描述

2,下载使用 webpack

  • 2.1,下载 webpack

npm i webpack webpack-cli --save--dev

  • 同时下载webpackwebpack-cli这两个构建工具
  • --save--dev,下载在 开发环境下 ,因为我们通常只是使用webpack作为开发时的构建工具
  • 2.2,运行 webpack

npx webpack

  • 使用 npx 来查找运行 webpack 是因为,当前目录下可能没有 webpack,而npx会自动帮我们向上层目录查找webpack
  • 运行webpack后,会发现 webpack 帮助我们进行了打包
  • 多出来的 dist 目录就是 webpack 帮助我们打包后生成的文件
  • 运行 npx webpack --stats detailed 指令,可以看到 打包信息,例如入口文件从何而来等

3,自定义webpack配置

webpack-cli 给我们提供了很多终端命令行指令,可以通过 webpack--help 来查看。
但是,命令很太多,不方便不直观,而且还不利于保存配置的内容。
因此,webpack给我们提供了 通过配置文件,来自定义配置参数的能力

在根目录下创建 webpack.config.js 文件

  • webpack.config.js 文件的文件名是固定的webpack 打包时会自动查找该文件
const path = require('path')
module.exports = {
// 配置入口文件
	entry: './src/index.js',
// 配置出口文件
	output: {
// 配置出口文件名
  		filename: 'bundle.js',
// 配置出口文件的目录路径 目录名 (因为是决定路径,所以采用node的path模块,计算路径)
  		 path: path.resolve(__dirname, './dist')
	 },
	 mode: 'none'
}

4,自动引入资源

  • 4.1,使用插件

webpack有非常强大的插件,可也帮助我们更方便的管理项目

  • 4.2,HtmlWebpackPlugin 插件

根据指定html文件基础上,自动生成新的html文件 在打包目录,并自动引入js文件

  • npm i htmlwebpackplugin -D,下载插件到开发环境
  • webpack.config.js 配置文件中,引入
    • let HtmlWebpackPlugin = require('html-webpack-plugin')
  • 配置插件:
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist'),
  },
  mode: 'none',
  plugins: [
    new HtmlWebpackPlugin({
      // 模板文件,基于该模板生产新的html文件
      template: './index.html',
      // 新生成文件名
      filename: 'app.html',
      // 引入js文件的script标签位置
      inject: 'body'
    })
  ]
}
  • 自动引入,记得把之前的手动引入去除掉
  • 4.3,每次重新打包,清空原来的打包目录

每次重新打包,原来的打包文件仍然存在,我们可以通过 webpack 配置,每次打包,清空之前的打包文件

const path = require('path')
module.exports = {
 ...
 output: {
   filename: 'bundle.js',
   path: path.resolve(__dirname, './dist'),
   // 每次打包,清空之前的打包文件
   clean: true
 },
 ...
}

5,搭建开发环境

目前为止,我们打开项目的方式比较麻烦,只能右击在浏览器打开或将文件路径复制到浏览器地址栏打开,比较麻烦,现在我们设置一个开发环境,帮助我们更好的维护和开发项目。

  • 5.1,mode 选项

开发模式: mode:"development"

  • mode的值设置为development,代表编译模式为开发模式
module.exports = {
	mode:"development"
}
  • 5.2,使用 source map,代码索引

在我们运行webpack搭建的项目时,js代码是合并压缩后的,在浏览器中报错,也是报的合并文件中的错误,有时候我们并不清楚具体的错误是在合并前的哪个文件那个位置。

devtool:"inlin-source-map"代码索引,实现精准定位

  • 可以更方便的帮助我们 调试、查找错误
module.exports = {
	devtool:"inlin-source-map"
}
  • 5.3,watch mode观察模式,自动更新编译

之前我们编译代码都是 npx webpack 但是,每次代码更新完,都需要重新进行编译

  • npx webpack --watch,代码监测,,代码变化后,自动进行编译
  • 5.4,webpack-dev-server实时重新加载页面

虽然上面实现了webpack自动检测代码编译,但是自动编译后,我们还是要手动打开并刷新页面

实时加载刷新页面:webpack-dev-server

  • 下载插件:npm i webpack-dev-server -D
  • 配置根目录,告诉 devserver 从什么位置查找文件
  • 执行命令:npx webpack serve --open
module.exports = {
	devServer:{
		// 配置告知 webpack-dev-server ,将 dist 目录下的文件作为 web 服务的根目录
		static:"./dist"
	}
}
  • webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serveserver 中,就好像它们是挂载在 server 根路径上的真实文件一样(但其实并没有一直访问监视根路径的文件)。

6,资源模块

目前为止,我们可以使用webpack工具来处理我们的html、js文件的打包,除此之外,我们还可以使用webpack来处理我们的其他类型资源,例如images资源。
在 webpack 出现之前,前端开发人员会使用 grunt 和 gulp 等工具来处理资源,并将它们/src 文件夹移动到 /dist 或 /build 目录中。webpack 最出色的功能之一就是,除了引入 JavaScript,还可以内置 的资源模块 Asset Modules 引入任何其他类型的文件
资源模块(asset module)是一种模块类型,它允许我们应用Webpack来 打包其他资源文件(如字体,图标等) 资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些loader:

  • asset/resource 发送一个单独的文件并导出 URL
  • asset/inline 导出一个资源的 data URI。
  • asset/source 导出资源的 源代码
  • asset 在导出一个 data URI 和发送一个单独的 文件 之间 自动选择
  • 总之,就是帮助我们把引入的资源自动打包到打包后的文件目录中,并自动引入。

asset/resource 模块使用配置:

module.exports = {
	output: {
       		filename: 'bundle.js',
      		path: path.resolve(__dirname, './dist'),
      	    clean: true,
      		//生成的文件名和路径
     	    assetModuleFilename: 'images/[contenthash][ext]'
   	},
   	module:{
		rules:[
				 {
					//匹配资源类型
					test:/\.png$/,
					//采用的模块 asset/resource 模块,生成路径,会被打包到打包目录内
					type:'asset/resource',
					//生成的文件名和路径,该方式生成的路径优先级更高,可以覆盖output里面的方式
					generator:{
						filename:'images/[contenthash][ext]'
					}
		   		},
				{
				test:/\.jpg$/,
				// asset/inline 模块,会生成一个data url 路径,类似于网路路径,不会被打包到本地的打包目录
				type:'asset/inline',
				 },
				{
				test:/\.txt$/,
				// asset/source 模块,会将资源源代码导出,不会被放入打包文件
				type:'asset/source'
				},
				{
				test:/\.jpg$/,
				// asset 模块,在resource 和 inline 之间自动选择,选择依据是文件大小是否达到条件
				type:'asset'
				// 文件超过一定大小时才会resource,否则inline,这里设置条件大小
				parser:{
					dataUrlCondition:{
						maxSize:4*1024*1024	// 超出4M才会resource,打包入打包目录
					}
				}
				}
		]
	}
		
}

然后就可以引入文件,打包时会自动打包资源文件,并引入

// 这时候就可以把资源当成模块去引入,asset/resource模块 它会自动生成一个路径
import imgSrc from './assets/me.png'
let img = document.createElement('img')
img.src = imgSrc
document.body.appendChild(img)
  • 使用该模块后,可以把资源当成模块去引入
  • asset/resource:模块: 它会自动生成一个 url 路径 ,文件存在于打包目录
  • asset/inline 模块:生成一个 dataUrl,文件不在打包目录
  • asset/source 模块:引入资源源码,文件不在打包目录
  • asset 模块:设置一个条件(文件大小),根据条件在 resourceinline 之间自动选择

7,管理资源

除了资源模块,我们还可以通过 loader 引入其他类型的文件。

  • 7.1,什么是 loader?

一种加载器,需要下载但不需要引入,能让 webpack 去处理其他类型的文件,并将它们转换为 模块,以供程序使用,以及被添加到依赖图中。

上面我们使用了 资源模块 来处理资源,可以 让资源像模块一样被引入使用,这一点 loader 也可以做到

  • loader的定义:
module.exports = {
	module:{
		// 在webpack的配置中,loader有两个属性
		rules:[
			{
				// test属性,识别出哪些 import 或 require 的文件需要编译
				test:/\.txt$/,
				// use属性,在编译转换时,使用哪个 loader 来处理
				use:'raw-loader'
			},
			{
				// 同一类资源,多种格式
				test:/\.(css|less|scss)$/,
				// loader 可以是多个,它是从后向前,链式调用的
				// less转换为css,css处理后再传给style
				use:['style-loader','css-loader','less-loader']
			},
		]
		
	}
}

以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性: test 和 use 。这告诉 webpack 编译器(compiler) 如下信息:
“嘿,webpack 编译器,当你碰到「在 require() / import 语句中被解析为’.txt’ 的路径」时,在你对它打包之前,先 use(使用) raw-loader 转换一下。”

  • 根据正则匹配相应文件的后缀,让指定类型文件使用指定的loader
  • 7.2,加载css loader

如果想在JavaScript模块中import使用一个css文件,就需要先安装

  • npm i ~ -D
  • style-loader
  • css-loader
  • less-loader
  • 等…都可以用来处理css 资源
  • use:['style-loader','css-loader','less-loader'] , 多个 loader 可以链式调用,但是是从后向前 逆序调用
  • 被使用的css代码会 style样式的格式,被添加到html文档中
let path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
    module: {
        rules: [
            {
                test: /\.(css|less)$/,
                // 多种loader,逆序链式调用
                use: ['style-loader', 'css-loader', 'less-loader']
            }
        ]
    }
}
import './style.css'
import './style.less'
// 使用了 loader 可以把css文件引入到js中,当做模块使用它
let div2 = document.createElement('div')
// add 后面括号里是样式文件里面的类名
div2.classList.add('fontColor')
document.body.appendChild(div2)
let body_ = document.body
body_.classList.add('bgc')
  • 7.3,抽离和压缩CSS

    • mini-css-extract-plugin是一个基于webpack5css压缩插件

多数情况下,我们也可以进行压缩CSS,以便在生产环境中节省加载时间,同时还可以将CSS文件抽离成一个单独的文件。实现这个功能,需要 mini-css-extract-plugin 这个插件来帮忙。

  • 下载插件:npm i mini-css-extract-plugin -D
  • 引入插件:在webpack配置文件中引入
    • const MiniCssExtractPlugin = require('mini-css-extract-plugin')
  • 配置抽离后的文件名和路径
    • new MiniCssExtractPlugin({filename:'styles/[contenthash].css'})
    • 文件抽离出来后,就会放在打包目录中
  • 调用插件:
    • use:[MiniCssExtractPlugin.loader,'css-loader','less-loader']
//引入插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
	plugins: [
	   // 插件配置  文件抽离出来后,就会放在打包目录中的styles文件夹中
       new MiniCssExtractPlugin({
           filename:'styles/[contenthash].css'
       })
    ],
	module: {
		rules:[
  			 {
         		test: /\.(css|less)$/,
         		// 使用插件
         		use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
     		 }
       ]
   }
  • 7.4,背景图、icon加载图片

    • css 中直接引用文件即可
  • 7.5,加载 fonts 文件

加载font字体文件,使用的是 资源模块resource 模块。

  • 首先准备好字体文件
  • 配置 loader
    • { test: /\.(woff|woff2|eot|ttf|otf)/, type:'asset/resource' }
  • css 文件中配置字体样式,定义类名
  • html元素添加类名
  • 7.6,加载数据

除了资源,可加载的还有数据,如 JSON、CSV、TSV、XML 等,而 JSON 数据是内置支持的。

处理 CSV、TSV、XML 数据

  • npm i csv-loader xml-loader -D
    • xml-loader 处理xml数据
    • csv-loader 处理CSV、TSV数据
  • 配置 loader
  •  module: {
    	rules: [
    		{
    			test: /\.(csv|tsv)$/i,
    			use: ['csv-loader'],
    		},
    		{
    			test: /\.xml$/i,
    			use: ['xml-loader'],
    		},
    	]
    }
    
  • 配置完,就可以直接在jsimport数据,当做模块使用
  • 7.7,自定义 JSON 模块

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

  • 下载插件 :npm i toml yamljs json5 -D
  • webpack配置文件中引入插件、配置插件
  • 然后就可以在js文件中import引入数据,当作模块使用
  • 配置代码如下:
const toml = require('toml');
const yaml = require('yamljs');
const json5 = require('json5');
module.exports = {
   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,JS代码转换,babel-loader

前面的章节里,我们应用css-loader编译了css文件,xml-loader 编译了 xml 文件,那么js文件需要编译吗?
正常情况下是不需要的,但是有些时候,浏览器版本过低,无法编译ES6语法,这就需要我们提前将ES6代码转换为ES5代码。
webpack自身具有自动加载js文件的能力,但是要将ES6代码转换为ES5代码,就需要使用 babel-loader 来帮忙了。

babel-loader 可以将ES6+的代码转换为ES5代码

  • 下载loader
    • npm i babel-loader -D,在webpack中应用babel解析ES6的桥梁
    • npm i @babel/core -Dbabel核心模块
    • npm i @babel/preset-env -Dbabel预设,一组babel插件的集合
    • npm i @babel/runtime,这个包中包含了 regeneratorRuntime 函数,由babel生成,用于兼容 async/await的语法。
    • npm i @babel/plugin-transform-runtime ,这个插件会在需要regeneratorRuntime的地方 自动require导包,编译时需要
  • webpack中配置babel
module: {
	rules: [
		{
			test: /\.js$/,
			exclude: /node_modules/,
			use: {
				loader: 'babel-loader',
				options: {
					presets: ['@babel/preset-env']
					plugin:[
						[
							'@babel/plugin-transform-runtime'
						]
					]
				}
			}
		}
	]
}

9,代码分离

代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的bundle,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。
常用的代码分离方法有三种:

  • 入口起点: 使用entry配置多个入口文件,手动分离代码。
  • 防止重复: 使用Entrydependencies或者SplitChunksPlugin去重和分离chunk
  • 动态导入: 通过模块的内联函数调用来分离代码
  • 9.1,入口起点

这是迄今为止最简单直观的分离代码的方式。不过,这种方式手动配置较多,并有一些隐患,我们将会解决这些问题。
现在,我们有两个入口文件,且都使用了同一个模块。

// index.js
// 引入使用了a模块
......
// another.js
// 也引入使用了a模块
......

配置多入口:

module.exports = {
   entry: {
	   index: './src/index.js',
       another: './src/another_module.js'
   		},
   output: {
   		//这里的name是上面入口文件的key,即index、another
       filename: '[name].bundle.js',
	}
} 
  • 配置好后,打包,打包目录下生成了两个js文件,且在app.html文件中都被引入
  • 两个入口文件中都引入了a模块
  • 缺点: 如果多个入口文件之间包含一些重复的模块,那些重复模块都会被引入到各个bundle 中。,造成了重复引入
  • 不能动态地将核心应用程序逻辑中的代码拆分出来
  • 9.2,防止重复

  • 入口依赖:
    • 配置dependon选项:,这样可以在多个打包模块之间共享模块:
    • module.exports = {
        entry: {
        	// 打包生成两个js文件
        	index: {
        		import: './src/index.js',
        		dependOn: 'shared',
        	},
        	another: {
        		import: './src/another-module.js',
        		dependOn: 'shared',
        	},
        	// 两个文件中需要共享的模块,多个就是数组,名字由引入时from后面的名字决定
        	shared: ['lodash','./src/helloWorld.js']
        }
      }
      

除此之外,SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk中,或者提取到一个新生成的 chunk。让我们使用这个插件,将之前的示例中重复的 lodash 模块去除:

entry: {
	index: './src/index.js',
	another: './src/another-module.js'
},
optimization: {
	splitChunks: {
		chunks: 'all',
	},
},

通过防止重复的方式分离代码:

  • 共享某些模块的入口文件,不会在打包后生成的js文件中都引入这些模块
  • 被共享的模块,会被单独打包成一个js文件
  • 这些文件都会被自动引入到html文档中
  • 9.3,动态引入

使用符合ECMAScript提案的 import() 语法来实现动态引入。

  • 9.4,懒加载

  • 9.5,预加载

10,缓存

为什么要缓存?我们webpack打包后的/dist目录,会被部署到服务器,而client(通常是浏览器)技能访问服务器的网站及其资源。而访问服务器网站资源这一步通常是比较消耗时间的。这就是为什么浏览器会使用 缓存 技术的原因。
通过 命中缓存,可以降低网络流量,使网站加载速度更快,而我们部署新版本时,如果资源文件的名字没有发生变化,浏览器可能认为它没有被更新,就会使用它的缓存版本。 由于缓存的存在,当我们更新了代码,浏览器却没有获取最新代码时,就会显得比较棘手。
而下面的配置,就是确保webpack编译生成的文件能被客户端缓存,代码变化后,能够请求到新的文件。

  • 10.1,输出文件的文件名

  • filename : '[name][contenthash].js'
  • [name],第一段名字根据入口文件命名
  • [contenthash],第二段根据内容生成哈希字符串
  • 这样,无论是文件名变化还是文件内容变化,打包生成的文件名都会变化,就会被客户端重新获取
module.exports = {
	output: {
		filename: '[name].[contenthash].js',
	},
};
  • 10.2,缓存第三方库

因为第三方库的文件,并不会像我们的本地代码一样频繁发生改变,所以我们经常将它们放在一个单独的 vendor chunk 文件中。来利用client 的长效缓存机制,命中缓存来消除对它的请求。减少资源请求。

module.exports = {
	optimization: {
		// 代码分离
		splitChunks: {
			// 缓存组
			cacheGroups: {
				vendor: {
					// 将node_modules中的第三方代码单独打包到一个文件中
					test: /[\\/]node_modules[\\/]/,
					name: 'vendors',
					chunks: 'all',
				},
			},
		},
	}
}
  • 10.3,将js文件都打包到一个文件夹中

module.exports = {
//...
	output: {
		// 只需要在文件打包名字之前,加上一个文件夹路径即可
		filename: 'scripts/[name].[contenthash].js',
		//...
	},
//...
}
  • 11,拆分开发环境和生产环境配置

现在,我们可以通过修改 mode 选项的值,来实现 开发环境生产环境的切换,但很多配置在开发环境和生产环境存在不一致的情况。比如开发环境没有必要设置缓存,生产环境需要设置公共路径等等。

  • 11.1,公共路径

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

  • 基于环境配置
    在开发环境中,我们通常有一个 assets/文件夹,用来存放静态资源,它与索引页面位于同一级别。
    但是,如果我们将所有的静态资源托管在 CDN,然后想在生产环境中使用该怎么办呢?
    想要解决这个问题,可以直接用一个 环境变量(environment variable),假设我们有一个变量:
import webpack from 'webpack';
// 尝试使用环境变量(CDN路径),否则使用根路径,有CDN就用CDN,没有CDN就用本地静态资源
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),
		}),
	],
};

webpack 会自动根据import.meta.url 、 document.currentScript 、 script.src 或者self.location 变量设置 publicPath。你需要做的是将 output.publicPath设为 'auto'

module.exports = {
	output: {
		// 自动设置环境变量
		publicPath: 'auto',
	},
};
  • 11.2,环境变量

想要消除webpack.config.js开发环境和生产环境之间的差异,可能需要使用到环境变量。
想要设置并传入环境变量,可以在命令行打包的时候,用 **--env**来配置,在webpack.config.js中可以访问到这些环境变量。

  • --env production,定义一个环境变量 production,默认值为 true
  • --env global=zxf,定义一个环境变量 global,值为 zxf
  • 如果我们传入了 production 变量,就代表是生产环境,代码就会压缩,否则旧代码生产模式,代码不会被压缩
  • webpack默认会压缩代码,但是如果我们使用了例如:CssMinimizerPlugin css 代码压缩,那默认的文件压缩就可能失效,可以使用terserWebpackPlugin插件压缩js文件

但是,要在webpack.config.js中接收到这个值,要将 module.exports 转换为一个函数,他的参数就是env对象

//...
module.exports = (env) => {
	// env中包含了打包时定义传入的环境变量
	return {
		//...
		// 根据命令行参数 env 来设置不同环境的 mode
		mode: env.production ? 'production' : 'development',
		//...
	}
}
  • 11.3,拆分环境配置

现在,我们的配置文件是同一个文件,现在根据不同的环境配置文件拆分成不同的文件,根据不同的环境使用不同的配置文件,把这些配置文件放入一个文件夹中。

  • 文件夹config
  • 开发环境配置webpack.config.dev.js
  • 生产环境配置webpack.config.prod.js
  • 运行开发环境npx webpack serve -c ./config/webpack.config.dev.js
  • 运行生产环境npx webpack -c ./config/webpack.config.prod.js
  • 11.4,npm脚本

环境配置文件拆分后,执行对应的环境配置,要执行很长的命令,我们可以修改 package.json 文件,来 利用npm脚本来简化命令行的输入,这时还可以省略npx

{
	"scripts": {
		"start": "webpack serve -c ./config/webpack.config.dev.js",
		"build": "webpack -c ./config/webpack.config.prod.js"
	}
}
  • 执行开发环境脚本npm run start
  • 执行生产环境脚本npm run build
  • 11.4,提取公共配置

两个环境配置文件中,有大量的相同代码,
可以提取相同代码到一个文件中webpack.config.common.js
然后,在不同的环境配置文件中,保留自己特有的配置

  • 11.5,合并配置文件

配置文件拆分后,使用 webpack-merge 工具来合并文件。

  • 创建一个webpack.config.js文件,用来导出合并后的配置文件
  • npm i webpack-merge -D,下载工具
  • 引入工具以及公共配置其他环境配置
    • const { merge } = require('webpack-merge')
    • const conmonConfig = require('./webpack.config.common.js')
    • const envConfig = require('./webpack.config.envConfig.js')
  • 环境判断,将当前环境配置和公共配置合并,返回出去
    • return merge(conmonConfig,envConfig)
const { merge } = require('webpack-merge')
const commonConfig = require('./webpack.config.common.js')
const productionConfig = require('./webpack.config.prod.js')
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:
			throw new Error('No matching configuration was found!');
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值