webpack.config.js的各种配置:entry,module,plugins,devServer,devtool,mode,resolve
1. 基础认知
事实上随着前端的快速发展,目前前端的开发已经变的越来越复杂了
- 比如开发过程中需要通过
模块化的方式
来开发; - 比如也会使用一些
高级的特性来加快开发效率或者安全性
,比如通过ES6+、TypeScript开发脚本逻辑, 通过sass、less等方式来编写css样式代码; - 比如开发过程中,希望
实时的监听文件的变化
来并且反映到浏览器
上,提高开发的效率; - 比如开发完成后还需要
将代码进行压缩、合并以及其他相关的优化
; - 等等….
- 比如开发过程中需要通过
但是对于很多的前端开发者来说,并不需要思考这些问题,日常的开发中根本就没有面临这些问题:
这是因为目前前端开发我们通常都会直接使用三大框架来开发:
Vue、React、Angular
;但是事实上,这三大框架的创建过程都是
借助于脚手架(CLI)
的;事实上Vue-CLI、create-react-app、Angular-CLI都是
基于webpack
支持模块化、less、 TypeScript、打包优化等的;
1.1. Webpack到底是什么呢?
官方的解释:
webpack is a static module bundler for modern JavaScript applications.
webpack是一个静态的模块化打包工具,为现代的JavaScript应用程序;
对上面的解释进行拆解:
打包bundler
:webpack可以将代码进行打包,所以它是一个打包工具静态的static
:这样表述的原因是我们最终可以将代码打包成最终的静态资源(部署到静态服务器);模块化module
:webpack默认支持各种模块化开发,ES Module、CommonJS、AMD等;现代的modern
:正是因为现代前端开发面临各种各样的问题,才催生了webpack的出现和发展;
1.2. Webpack的使用前提
webpack的官方文档是https://webpack.js.org/
- webpack的中文官方文档是https://webpack.docschina.org/
- DOCUMENTATION:文档详情,也是我们最关注的
Webpack的运行是依赖Node环境的
- 所以需要先安装Node.js,并且同时会安装npm;(也可以使用nvm或者n来管理Node版本);
- Node官方网站:https://nodejs.org/
1.3. Webpack的安装
webpack的安装目前分为两个:
webpack、webpack-cli
那么它们是什么关系呢?
执行webpack命令,会执行node_modules下的.bin目录下的webpack;
webpack在执行时是依赖webpack-cli的,如果没有安装就会报错;
而webpack-cli中代码执行时,才是真正利用webpack进行编译和打包的过程;
所以在安装webpack时,需要同时安装webpack-cli(第三方的脚手架事实上是没有使用webpack-cli的,而是类似于自己的vue-service-cli的东西)
安装
1
2
3
4
5# 全局安装
npm install webpack webpack-cli –g
# 局部安装
npm install webpack webpack-cli --save-dev
1.4. Webpack的默认打包
通过webpack进行打包,之后运行打包之后的代码
- 在目录下直接执行
webpack
命令
- 在目录下直接执行
生成一个dist文件夹,里面存放一个main.js的文件,就是打包之后的文件:
- 这个文件中的代码被压缩了;
- 代码中依然存在ES6的语法,比如箭头函数、const等,这是因为默认情况下webpack并不清楚打包后的文件是否需要转成ES5之前的语法,需要通过babel来进行转换和设置;
代码是可以正常进行打包的,但是有一个问题,webpack是如何确定入口的呢?
- 事实上,当运行webpack时,webpack会查找当前目录下的 src/index.js作为入口;
- 所以,如果当前项目中没有存在src/index.js文件,那么会报错;
当然,也可以通过配置来指定入口和出口
1
npx webpack --entry ./src/main.js --output-path ./build
2. 实验代码
2.1. 创建package.json文件
用于管理项目的信息、库依赖等
定位到项目根目录下,运行命令
1 | npm init -y |
2.2. 安装webpack
–save-dev 特指开发环境下的安装包
1 | npm install webpack webpack-cli --save-dev |
2.3. 创建文件
根目录下创建文件夹src,src中创建文件夹js,js中创建 math.js 和 format.js,src目录下创建index.js
1 | //===============/src/js/math.js==================== |
2.4. 创建scripts脚本
package.json 中
1 | "scripts": { |
2.5. 执行脚本命令
1 | npm run build |
会在根目录下生成dist文件夹,代码浓缩在main.js中,调用 dist/main.js
1 |
|
3. Webpack配置文件
- 在通常情况下,webpack需要打包的项目是非常复杂的,并且需要一系列的配置来满足要求,默认配置必然 是不可以的。
- 可以在根目录下创建一个webpack.config.js文件,来作为webpack的配置文件:
- 继续执行
npm run build
命令,依然可以正常打包
1 | // node 内置核心模块,用来处理路径问题 |
4. 指定配置文件
如果配置文件并不是webpack.config.js的名字,而是其他的名字呢?
- 比如将webpack.config.js修改成了 wk.config.js;
- 这个时候可以通过 –config 来指定对应的配置文件;
但是每次这样执行命令来对源码进行编译,会非常繁琐,所以可以在package.json中增加一个新的脚本:
1
2
3"scripts":{
"build": "webpack --config wk.config.js"
}
5. Webpack的依赖图
- webpack到底是如何对项目进行打包的呢?
- 事实上webpack在处理应用程序时,它会根据命令或者配置文件找到入口文件;
- 从入口开始,会生成一个 依赖关系图,这个依赖关系图会包含应用程序中所需的所有模块(比如.js文件、css文件、图片、字 体等);
- 然后遍历图结构,打包一个个模块(根据文件的不同使用不同的loader来解析);
6. Vue项目加载的文件有哪些呢?
JavaScript的打包:
将ES6转换成ES5的语法;
TypeScript的处理,将其转换成JavaScript;
Css的处理:
- CSS文件模块的加载、提取;
- Less、Sass等预处理器的处理;
资源文件img、font:
- 图片img文件的加载;
- 字体font文件的加载;
HTML资源的处理:
- 打包HTML资源文件;
处理vue项目的SFC文件
- .vue文件;
7. 编写案例代码
- 创建一个component.js
- 通过JavaScript创建了一个元素,并且希望给它设置一些样式;
1 | import '../css/style.css'; |
此时,需要将 component.js 引入 index.js 的依赖中
1 | import {sum} from './math'; |
运行 npm run build
仍不行
1 | ERROR in ./src/css/style.css 1:0 |
7.1. css-loader的使用
此时需要一个loader来加载这个css文件,但是loader是什么呢?
- loader 可以用于对模块的源代码进行转换;
- 可以将css文件也看成是一个模块,通过import来加载这个模块的;
- 在加载这个模块时,webpack其实并不知道如何对其进行加载,必须制定对应的loader来完成这个功能;
那么需要一个什么样的loader呢?
- 对于加载css文件来说,需要一个可以读取css文件的loader;
- 这个loader最常用的是css-loader;
css-loader的安装
1
npm install css-loader -D
7.2. css-loader的使用方案
- 如何使用这个loader来加载css文件呢? 有三种方式:
- 内联方式;
- CLI方式(webpack5中不再使用);
- 配置方式;
7.2.1. 内联方式
内联方式使用较少,因为不方便管理;
在引入的样式前加上使用的loader,并且使用!分割;
1 | import "css-loader!../css/style.css"; |
7.2.2. CLI方式
- webpack5的文档中已经没有了–module-bind;
- 实际应用中也比较少使用,因为不方便管理;
7.2.3. 配置方式
配置方式表示的意思是在webpack.config.js文件中写明配置信息:
- module.rules中允许配置多个loader);
- 这种方式可以更好的表示loader的配置,也方便后期的维护,同时也对各个Loader有一个全局的概览;
module.rules的配置如下:
rules属性对应的值是一个数组:
[rules]
数组中存放的是一个个的rule,Rule是一个对象,对象中可以设置多个属性:
test属性
:用于对 resource(资源)进行匹配的,通常会设置成正则表达式;use属性
:对应的值时一个数组:[UseEntry]
UseEntry是一个对象,可以通过对象的属性来设置一些其他属性
loader
:必须有一个 loader属性,对应的值是一个字符串;options
:可选的属性,值是一个字符串或者对象,值会被传入到loader中;query
:目前已经使用options来替代;
传递字符串(如:use: [ ‘style-loader’ ])是 loader 属性的简写方式(如:use: [ { loader: ‘style-loader’} ]);
loader属性
:rules.use: [ { loader } ] 的简写。
1 | const { resolve } = require("path"); |
7.3. style-loader
通过css-loader加载了css文件,但是css在代码中并没有生效(页面没有效果)。
这是为什么呢?
- 因为css-loader只是负责将.css文件进行解析,并不会将解析之后的css插入到页面中;
- 如果希望再完成插入style的操作,那么还需要style-loader;
7.3.1. 安装style-loader
1 | npm i style-loader -D |
7.3.2. 配置style-loader
如何使用style-loader:
- 在配置文件中,添加style-loader;
- 注意:因为loader的执行顺序是从下到上,所以需要将style-loader写到css-loader的前面;
重新执行编译npm run build,可以发现打包后的css已经生效了:
- 目前css是通过页内样式的方式添加进来的;
- 应该将css抽取到单独的文件中,并且进行压缩等操作;
1 | const { resolve } = require("path"); |
到此,在index.html中引入 build/js/index.js
就可以显示div和其样式
7.4. 如何处理less文件?
在开发中,可能会使用less、sass、stylus的预处理器来编写css样式,效率会更高
那么,如何可以让环境支持这些预处理器呢?
- 首先需要确定,less、sass等编写的css需要通过工具转换成普通的css;
比如编写如下的less样式:
src/css/content.less
1
2
3
4
5@fontSize: 30px;
.content{
font-size: @fontSize;
}引入component.js中
1
2
3
4
5
6
7
8
9
10
11
12import '../css/style.css';
import '../css/content.less';
function add(){
const div = document.createElement("div");
div.innerHTML = "Hello webpack";
div.className="title content";
return div;
}
document.body.appendChild(add());
7.4.1. Less工具处理
使用less工具来完成它的编译转换:
1 | npm install less -D |
执行如下命令:
1 | npx lessc ./src/css/title.less title.css |
或者使用vscode插件 Easy Less
- Auto-compile LESS to CSS on save
7.4.2. less-loader处理
但是在项目中会编写大量的css,它们如何可以自动转换呢?
可以使用less-loader,来自动使用less工具转换less到css;
1
npm install less-loader -D
配置webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33const { resolve } = require("path");
module.exports={
//入口文件
entry: './src/js/index.js',
//输出文件
output: {
// 输出文件名
filename: 'index.js',
// 输出文件路径配置
path: resolve(__dirname,"build/js")
},
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
"css-loader"
]
},
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
"less-loader"
]
}
]
},
mode: 'development'
}运行命令,less就自动转换成css,并且页面也会生效
1
npm run build
8. PostCSS 兼容性
什么是PostCSS呢?
- PostCSS是一个通过JavaScript来转换样式的工具;
- 这个工具可以进行一些CSS的转换和适配,比如
自动添加浏览器前缀
、css样式的重置
; - 但是实现这些功能,需要借助于PostCSS对应的插件;
如何使用PostCSS呢?
- 第一步:查找PostCSS在构建工具中的扩展,比如webpack中的postcss-loader;
- 第二步:选择需要添加PostCSS相关的插件;
8.1. 命令行使用postcss
安装:postcss、postcss-cli
1
npm install postcss postcss-cli -D
编写一个需要添加前缀的css:
在上面的网站中查询一些添加css属性的样式;
1
2
3
4
5
6
7:fullscreen{
}
.title{
use-select: none;
}
8.2. 插件autoprefixer
安装autoprefixer:
1
npm i autoprefixer -D
直接使用使用postcss工具,并且制定使用autoprefixer
1
npx postcss --use autoprefixer -o end.css ./src/css/style.css
转化之后的css样式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14:-webkit-full-screen{
}
:fullscreen{
}
.title{
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9jc3Mvc3R5bGUuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztBQUVBOztBQUZBOztBQUVBOztBQUVBO0lBQ0kseUJBQWlCO09BQWpCLHNCQUFpQjtZQUFqQixpQkFBaUI7QUFDckIiLCJmaWxlIjoiZW5kLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIjpmdWxsc2NyZWVue1xuICAgIFxufVxuXG4udGl0bGV7XG4gICAgdXNlci1zZWxlY3Q6IG5vbmU7XG59Il19 */
8.3. postcss-loader
https://github.com/postcss/postcss
真实开发中必然不会直接使用命令行工具来对css进行处理,而是可以借助于构建工具:
- 在webpack中使用postcss就是使用postcss-loader来处理的;
我们来安装postcss-loader:
1
npm i postcss-loader -D
修改加载css的loader:
注意:因为postcss需要有对应的插件才会起效果,所以需要配置它的plugin;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17{
test: /\.css$/,
use: [
"style-loader",
"css-loader",
{
loader: 'postcss-loader',
options:{
postcssOptions: {
plugins: [
require("autoprefixer")
]
}
}
}
]
}
8.4. 单独的postcss配置文件
当然,可以将配置信息放到一个单独的文件中进行管理:
在根目录下创建postcss.config.js
1
2
3
4
5module.exports = {
plugins: [
require("autoprefixer")
]
}在 webpack.config.js 中加载 postcss-loader
1
2
3
4
5
6
7
8{
test: /\.css$/,
use: [
"style-loader",
"css-loader",
"postcss-loader"
]
}
8.5. postcss-preset-env
事实上,在配置postcss-loader时,并不需要使用autoprefixer。
可以使用另外一个插件:
postcss-preset-env
- postcss-preset-env也是一个postcss的插件;
- 它可以将一些现代的CSS特性,转成大多数浏览器认识的CSS,并且会根据目标浏览器或者运行时环境添加所需的polyfill;
- 也包括会
自动添加autoprefixer
;
首先,安装postcss-preset-env:
1
npm install postcss-preset-env -D
之后,修改掉之前的autoprefixer即可:
https://github.com/csstools/postcss-preset-env
1
2
3
4
5
6
7
8
9const postcssPresetEnv = require('postcss-preset-env');
module.exports = {
plugins: [
postcssPresetEnv({
browsers: 'last 4 versions'
})
]
}运行命令
1
npm run build
得到兼容的css
1
2
3
4
5
6
7
8
9
10
11
12
13
14.title{
color: red;
font-weight: 700;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.content {
font-size: 30px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
9. 打包图片
- 在项目中使用图片,比较常见的使用图片的方式是两种:
- img元素,
设置src属性
; - 其他元素(比如div),
设置background-image的css属性
;
- img元素,
src/js/component.js
1 | import '../css/style.css'; |
9.1. file-loader
要处理jpg、png等格式的图片,使用 file-loader:
- file-loader的作用就是处理 import/require()方式引入的一个文件资源,并且会将它放到输出的文件夹中;
安装file-loader:
1
npm i file-loader -D
配置处理图片的rule:
1
2
3
4
5
6
7
8
9
10{
test: /\(jpe?g|gif|png|svg)$/,
use: [
{
loader: 'file-loader'
}
],
// 这个属性如果没有设置,会生成多张图片
type: 'javascript/auto'
}
9.2. 文件的命名规则
处理后的文件名称按照一定的规则进行显示:
比如保留原来的文件名、扩展名,同时为了防止重复,包含一个hash值等;
使用PlaceHolders来完成,webpack给我们提供了大量的PlaceHolders来显示不同的内容:
最常用的placeholder:
[ext]
:处理文件的扩展名;[name]
:处理文件的名称;[hash]
:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制);[contentHash]
:在file-loader中和[hash]结果是一致的;[hash:<length>]
:截图hash的长度,默认32个字符太长了;[path]
:文件相对于webpack配置文件的路径;
9.3. 设置文件的名称
- 那么可以按照如下的格式编写:
- 这个也是vue的写法;
1 | { |
9.4. 设置文件的存放路径
- 刚才通过 img/ 已经设置了文件夹,这个也是vue、react脚手架中常见的设置方式:
- 其实按照这种设置方式就可以了;
- 当然也可以通过outputPath来设置输出的文件夹;
1 | { |
9.5. url-loader
- url-loader 和 file-loader的工作方式是相似的,但是可以将较小的文件,转成
base64的URI
9.5.1. 安装url-loader:
1 | npm install url-loader -D |
9.5.2. 配置文件
1 | { |
显示结果是一样的,并且图片可以正常显示;
但是在dist文件夹中,看不到图片文件:
默认情况下url-loader会将所有的图片文件转成base64编码
1
<img src="data:image/jpeg;base64,/9j/...//2Q==">
9.5.3. url-loader的limit
开发中往往是小的图片需要转换,大的图片直接使用图片
- 小的图片转换base64之后可以和页面一起被请求,减少不必要的请求过程;
- 而大的图片也进行转换,反而会影响页面的请求速度;
如何限制哪些大小的图片转换和不转换呢?
- url-loader有一个options属性limit,可以用于设置转换的限制;
- 下面的代码 8kb以下的图片会进行base64编码;
1 | { |
9.6. asset module type
在webpack5之前,加载资源需要使用一些loader,比如raw-loader 、url-loader、file-loader;
在webpack5开始,我们可以直接使用资源模块类型(asset module type),来替代上面的这些loader;
资源模块类型(asset module type)**,通过添加 4 种新的模块类型,来替换所有这些 loader: **
asset/resource
发送一个单独的文件并导出 URL。之前通过使用file-loader
实现;asset/inline
导出一个资源的 data URI。之前通过使用url-loader
实现;asset/source
导出资源的源代码。之前通过使用raw-loader
实现;asset
在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现;
9.6.1. 使用
比如加载图片,可以使用下面的方式:
1
2
3
4{
test: /\.(jpe?g|png|gif|svg)$/,
type: 'asset/resource'
}但是,如何可以自定义文件的输出路径和文件名呢?
方式一:修改output,添加assetModuleFilename属性;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24output: {
// 输出文件名
filename: 'js/index.js',
// 输出文件路径配置
path: resolve(__dirname,"build"),
assetModuleFilename: 'img/[name]_[hash:6][ext]'
},
module: {
rules: [
{
test: /\.(css|less)$/,
use: [
"style-loader",
"css-loader",
"postcss-loader",
"less-loader"
]
},
{
test: /\.(jpe?g|gif|png|svg)$/,
type: 'asset/resource'
}
]
}方式二:在rule中,添加一个generator属性,并且设置filename;
1
2
3
4
5
6
7{
test: /\.(jpe?g|gif|png|svg)$/,
type: 'asset/resource',
generator: {
filename: 'img/[name]_[hash:6][ext]'
}
}
9.6.2. url-loader的limit效果
- 需要两个步骤来实现:
- 步骤一:将type修改为asset;
- 步骤二:添加一个parser属性,并且制定dataUrl的条件,添加maxSize属性;
1 | { |
10. 打包字体
10.1. 加载字体文件
如果某些特殊的字体或者字体图标,会引入很多字体相关的文件,这些文件的处理也是一样的
从阿里图标库中下载了几个字体图标:
在component中引入,并且添加一个i元素用于显示字体图标:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import '../css/style.css';
import '../css/content.less';
import img1 from '../img/1.jpg';
import img2 from '../img/2.jpeg';
import '../font/iconfont.css';
function add(){
const div = document.createElement("div");
const iEle = document.createElement("i");
iEle.className="iconfont icon-ashbin";
div.appendChild(iEle);
div.className = "title";
return div;
}
document.body.appendChild(add());
10.2. 字体的打包
- 这个时候打包会报错,因为无法正确的处理eot、ttf、woff等文件:
- 使用file-loader来处理,也可以直接使用webpack5的资源模块类型来处理;
1 | { |
11. Plugin
Webpack的另一个核心是Plugin,官方有这样一段对Plugin的描述:
While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management and injection of environment variables.
上面表达的含义翻译过来就是:
- Loader是用于
特定的模块类型
进行转换; - Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等;
- Loader是用于
11.1. CleanWebpackPlugin
每次修改了一些配置,重新打包时,自动删除原文件夹 - CleanWebpackPlugin;
首先,安装这个插件:
1
npm i clean-webpack-plugin -D
之后在插件中配置:
1
2
3
4
5
6const {CleanWebpackPlugin} = require("clean-webpack-plugin");
module.exports={
plugins: [
new CleanWebpackPlugin()
]
}
11.2. HtmlWebpackPlugin
另外还有一个不太规范的地方:
- 目前HTML文件是编写在根目录下的,而最终打包的 build 文件夹中是没有index.html文件的
- 在进行项目部署的时,必然也是需要有对应的入口文件index.html;
- 所以需要对index.html进行打包处理;
对HTML进行打包处理,使用插件:HtmlWebpackPlugin;
1
npm install html-webpack-plugin -D
配置文件
1
2
3
4
5
6
7
8
9const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports={
plugins: [
new HtmlWebpackPlugin({
title: 'example'
})
]
}
11.2.1. 生成index.html分析
现在自动在build文件夹中,生成了一个index.html的文件:
该文件中也自动添加了打包的 index.js文件;
1
2
3
4
5
6
7
8
9
10
11
<html>
<head>
<meta charset="utf-8">
<title>example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script defer src="js/index.js"></script>
</head>
<body>
</body>
</html>
这个文件是如何生成的呢?
默认情况下是根据ejs的一个模板来生成的;
在html-webpack-plugin的源码中,有一个default_index.ejs模块;
11.2.2. 自定义HTML模板
加入一些比较特别的内容:
- 比如添加一个noscript标签,在用户的JavaScript被关闭时,给予响应的提示;
- 比如在开发vue或者react项目时,我们需要一个可以挂载后续组件的根标签
<div id="app"></div>
;
创建index.html模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<!DOCTYPE html>
<html>
<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, minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>
We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavasSript enabled. Please enable it to continue.
</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
11.2.3. 自定义模板数据填充
一些类似这样的语法
<%= 变量 %>
,是EJS模块填充数据的方式。在配置HtmlWebpackPlugin时,我们可以添加如下配置:
template
:指定我们要使用的模块所在的路径;title
:在进行htmlWebpackPlugin.options.title读取时,就会读到该信息;
1
2
3
4
5
6
7
8
9
10const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports={
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
title: 'Hello Webpack'
})
]
}
11.2.4. DefinePlugin
但是,这个时候编译还是会报错,因为模块中还使用到一个BASE_URL的常量:
这是因为在编译template模块时,有一个BASE_URL:
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
;- 但是并没有设置过这个常量值,所以会出现没有定义的错误;
- 这个时候可以使用DefinePlugin插件;
DefinePlugin允许在编译时创建配置的全局常量,是一个webpack内置的插件(不需要单独安装):
这个时候,编译template就可以正确的编译了,会读取到BASE_URL的值;
1
2
3
4
5
6
7
8
9const {DefinePlugin} = require("webpack");
module.exports={
plugins: [
new DefinePlugin({
BASE_URL: "'./'"
})
]
}
11.2.5. 生成的index.html
1 |
|
11.3. CopyWebpackPlugin
在vue的打包过程中,如果将一些文件放到public的目录下,希望将这些内容复制到 build 文件夹中,使用CopyWebpackPlugin来完成;
安装CopyWebpackPlugin插件:
1
npm i copy-webpack-plugin -D
配置CopyWebpackPlugin:
- 复制的规则在patterns中设置;
- from:设置从哪一个源中开始复制;
- to:复制到的位置,可以省略,会默认复制到打包的目录下;
- globOptions:设置一些额外的选项,其中可以编写需要忽略的文件:
- .DS_Store:mac目录下回自动生成的一个文件;
- index.html:也不需要复制,因为我们已经通过HtmlWebpackPlugin完成了index.html的生成;
1 | const CopyPlugin = require("copy-webpack-plugin"); |
12. Mode配置
Mode配置选项,可以告知webpack使用响应模式的内置优化:
- 默认值是 production;
- 可选值有:’none’ | ‘development’ | ‘production’;
这几个选项有什么样的区别呢?
选项 描述 development 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development,为模块和chunk启用有效的名 production 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production,为模块和chunk启用确定性的混淆名称,FlagDependencyUsagePlugin,FlagIncludeC混开始Plugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin 和 TerserPlugin none 不使用任何默认优化选项 源码出错提示
1
2
3
4
5
6
7
8module.exports = {
// 设置模式
// development 开发阶段
// production 打包上线阶段
mode: 'development',
// 设置source-map,建立js映射文件,方便调试代码和错误
devtool: 'source-map'
}
12.1. Mode: development
https://v4.webpack.js.org/configuration/mode/#mode-development
1 | // webpack.development.config.js |
12.2. Mode: production
1 | // webpack.production.config.js |
12.3. Mode: none
1 | // webpack.custom.config.js |
13. Babel
13.1. 为什么需要babel?
事实上,在开发中很少直接去接触babel,但是babel对于前端开发来说,目前是不可缺少的一部分:
- 开发中,想要使用ES6+的语法,想要使用TypeScript,开发React项目,它们都是离不开Babel的;
- 学习Babel对于理解代码从编写到线上的转变过程至关重要;
那么,Babel到底是什么呢?
Babel是一个工具链,主要用于旧浏览器或者环境中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript;
包括:语法转换、源代码转换等;
1
2
3
4
5[1,2,3].map(item=>console.log(item););
[1,2,3].map(function(item){
console.log(item);
});
13.2. Babel命令行使用
babel本身可以作为
一个独立的工具
(和postcss一样),不和webpack等构建工具配置来单独使用如果希望在命令行尝试使用babel,需要安装如下库:
@babel/core
:babel的核心代码,必须安装;@babel/cli
:可以在命令行使用babel;1
npm install @babel/cli @babel/core -D
使用babel来处理源代码:
src
:是源文件的目录;--out-dir
:指定要输出的文件夹dist;--out-file
:指定要输出的文件;1
npx babel src --out-dir dist
13.3. 插件的使用
需要转换箭头函数,那么就可以使用
箭头函数转换相关的插件
:1
2
3npm install @babel/plugin-transform-arrow-functions -D
npx babel src/js/math.js --out-dir dist --plugins=@babel/plugin-transform-arrow-functions查看转换后的结果:
const 并没有转成 var
这是因为 plugin-transform-arrow-functions,并没有提供这样的功能;
需要使用 plugin-transform-block-scoping 来完成这样的功能;
1
2
3npm install @babel/plugin-transform-block-scoping -D
npx babel src/js/math.js --out-file math.js --plugins=@babel/plugin-transform-arrow-functions,@babel/plugin-transform-block-scoping
13.4. Babel的预设preset
但是如果要转换的内容过多,一个个设置是比较麻烦的,可以使用预设(preset)
安装@babel/preset-env预设:
1
npm install @babel/preset-env -D
执行如下命令:
1
npx babel src/js/math.js --out-file math.js --presets=@babel/preset-env
13.5. Babel的底层原理
babel是如何做到将一段代码(ES6、TypeScript、React)
转成
另外一段代码(ES5)的呢?- 从一种源代码(原生语言)转换成另一种源代码(目标语言),这是什么的工作呢?
- 就是
编译器
,事实上可以将babel看成就是一个编译器。 - Babel编译器的作用就是将源代码,转换成浏览器可以直接识别的另外一段源代码;
Babel也拥有编译器的工作流程:
- 解析阶段(Parsing)
- 转换阶段(Transformation)
- 生成阶段(Code Generation)
13.6. Babel编译器执行原理
Babel的执行阶段
当然,这只是一个简化版的编译器工具流程,在每个阶段又会有自己具体的工作
13.7. babel-loader
在实际开发中,通常会在构建工具中通过配置babel来对其进行使用的,比如在webpack中
安装相关的依赖:
如果之前已经安装了@babel/core,那么这里不需要再次安装;
1
npm install babel-loader @babel/core
可以设置一个规则,在加载js文件时,使用babel:
1
2
3
4
5
6
7
8
9
10module.exports={
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader'
}
]
}
}
13.8. 指定使用的插件
1 | module.exports={ |
13.9. babel-preset
如果一个个去安装使用插件,那么需要手动来管理大量的babel插件,可以直接给webpack提供一个 preset,webpack会根据我们的预设来加载对应的插件列表,并且将其传递给babel
比如常见的预设有三个:
- env
- react
- TypeScript
安装preset-env:
1
npm install @babel/preset-env
配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15module.exports={
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
presets: [
"@babel/preset-env"
]
}
}
]
}
}
13.10. Babel的配置文件
可以将babel的配置信息放到一个独立的文件中,babel提供了两种配置文件的编写:
babel.config
.json(或者.js,.cjs,.mjs)文件;.babelrc
.json(或者.babelrc,.js,.cjs,.mjs)文件;
它们两个有什么区别呢?
- 目前很多的项目都采用了多包管理的方式(babel本身、element-plus、umi等);
.babelrc.json
:早期使用较多的配置方式,但是对于配置Monorepos项目是比较麻烦的;babel.config
.json(babel7):可以直接作用于Monorepos项目的子包,更加推荐;
根目录下新建 babel.config.js
1 | module.exports = { |
14. Vue打包
14.1. Vue源码的打包
安装插件
1
npm i vue@next
编写代码
src/js/index.js
1
2
3
4
5
6
7
8
9
10
11
12import { createApp } from "vue";
const app = createApp({
template: `<h2>{{message}}</h2>`,
data(){
return {
message: 'Hello Vue'
}
}
});
app.mount("#app");运行
1
npm run build
界面上是没有效果的:
并且查看运行的控制台,会发现如下的警告信息;
1
2runtime-core.esm-bundler.js:38 [Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue. Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".
at <App>
14.2. Vue打包后不同版本解析
vue(.runtime).global(.prod).js
- 通过浏览器中的
<script src="...">
直接使用; - 通过CDN引入和下载的Vue版本就是这个版本;
- 会暴露一个全局的Vue来使用;
- 通过浏览器中的
vue(.runtime).esm-browser(.prod).js
- 用于通过原生 ES 模块导入使用 (在浏览器中通过
<script type="module">
来使用)
- 用于通过原生 ES 模块导入使用 (在浏览器中通过
vue(.runtime).esm-bundler.js
- 用于 webpack,rollup 和 parcel 等构建工具;
- 构建工具中默认是vue.runtime.esm-bundler.js;
- 如果需要解析模板template,那么需要手动指定vue.esm-bundler.js;
vue.cjs(.prod).js
- 服务器端渲染使用;
- 通过require()在Node.js中使用;
14.3. 运行时+编译器 vs 仅运行时
在Vue的开发过程中有
三种方式
来编写DOM元素:- 方式一:template模板的方式;
- 方式二:render函数的方式,使用h函数来编写渲染的内容;
- 方式三:通过.vue文件中的template来编写模板;
它们的模板分别是如何处理的呢?
- 方式二中的h函数可以直接返回一个
虚拟节点
,也就是Vnode节点
; - 方式一和方式三的template都需要有特定的代码来对其进行解析:
- 方式三.vue文件中的template可以通过在
vue-loader
对其进行编译和处理; - 方式一种的 template 必须要
通过源码中一部分代码
来进行编译;
- 方式三.vue文件中的template可以通过在
- 方式二中的h函数可以直接返回一个
所以,Vue选择版本的时候分为
运行时+编译器
vs仅运行时
- 运行时+编译器包含了对template模板的编译代码,更加完整,但是也更大一些;
- 仅运行时没有包含对template版本的编译代码,相对更小一些;
14.4. 修改引入模块
1 | import { createApp } from "vue/dist/vue.esm-bundler"; |
14.5. 全局标识的配置
控制台还有另外的一个警告:
1
2
3runtime-core.esm-bundler.js:4947 Feature flags __VUE_OPTIONS_API__, __VUE_PROD_DEVTOOLS__ are not explicitly defined. You are running the esm-bundler build of Vue, which expects these compile-time feature flags to be globally injected via the bundler config in order to get better tree-shaking in the production bundle.
For more details, see https://link.vuejs.org/feature-flags.在GitHub上的文档中可以找到说明:
https://github.com/vuejs/core/tree/main/packages/vue#bundler-build-feature-flags
1
2
3
4
5
6Bundler Build Feature Flags
Starting with 3.0.0-rc.3, esm-bundler builds now exposes global feature flags that can be overwritten at compile time:
__VUE_OPTIONS_API__ (enable/disable Options API support, default: true)
__VUE_PROD_DEVTOOLS__ (enable/disable devtools support in production, default: false)
The build will work without configuring these flags, however it is strongly recommended to properly configure them in order to get proper tree-shaking in the final bundle. To configure these flags:这是两个特性的标识,一个是使用Vue的Options,一个是Production模式下是否支持devtools工具;
虽然他们都有默认值,但是强烈建议手动对他们进行配置;
1
2
3
4
5
6
7
8
9
10
11
12const { DefinePlugin } = require("webpack");
module.exports = {
plugins:[
new DefinePlugin({
BASE_URL: "'./'",
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
})
]
}
14.6. VSCode对SFC文件的支持
真实开发中多数情况下我们都是使用SFC( single-file components (单文件组件) )。
VSCode对SFC的支持:
- 插件一:
Vetur
,从Vue2开发就一直在使用的VSCode支持Vue的插件; - 插件二:
Vue Language Features (Volar)
,官方推荐的插件(后续会基于Volar开发官方的VSCode插件);
- 插件一:
14.7. 编写App.vue代码
14.7.1. src/vue/App.vue
1 | <template> |
14.7.2. src/js/index.js
引入App.vue
1 | import { createApp } from "vue"; |
14.7.3. 安装插件
1 | npm i vue-loader@next -D |
14.7.4. webpack.config.js
1 | module.exports = { |
14.7.5. 运行
1 | npm run build |
错误信息
1 | ERROR in ./src/vue/App.vue |
在 webpack.config.js 中引入 VueLoaderPlugin
1 | const { VueLoaderPlugin} = require("vue-loader"); |
运行之后还有错误
1 | ERROR in ./src/vue/App.vue?vue&type=style&index=0&id=5e019a2f&lang=css |
无法解析style中的css代码
在 webpack.config.js 中添加 css-loader,style-loader
1 | module.exports = { |
14.7.6. css scoped 原理
当.vue 文件中的 style 标签有scoped 属性时,它的 css 样式 只作用于当前组件中的元素
css scoped 的 工作流程如下:
- 使用
vue-loader
处理 .vue 文件,根据.vue文件的请求路径和文件内容,生成 .vue 文件的hash值
, 如:89kjg85f; - 如果.vue 文件的某一个style 标签有 scoped 属性,为.vue 文件
生成一个 scopedID
,scopedID 的格式为data-v-hash
, 如:data-v-89kjg85f; - 使用 vue-loader 从.vue 文件中
获取style区域块
(scoped) 的样式内容(字符串) ;- 如果使用了 less 或者 sass,要使用 less-loader 或者 sass-loader 处理样式内容,使样式内容变为浏览器可识别的css样式;
- 然后使用
PostCSS
提供的 parser 处理样式内容,为样式内容中的每一个 css选择器添加 [data-v-hash]
; - 再使用
css-loader
; - 最后使用
style-loader
把 css 样式添加到 head 中- 或者通过 miniCssExtractPlugin 将 css 样式 提取一个公共的 css 文件中
- 经历上述过程,style(scoped)中的样式就变成了组件的私有样式
14.7.7. 加载组件
src/vue/Hello.vue
1 | <template> |
src/vue/App.vue
引入 Hello.vue
1 | <template> |
15. 搭建本地服务器
- 运行项目可以由两个操作:
- npm run build,编译相关的代码;
- 通过live server或者直接通过浏览器,打开index.html代码,查看效果;
- 这个过程经常操作会影响开发效率,希望当文件发生变化时,可以自动的完成编译和展示;
- 为了完成自动编译,webpack提供了几种可选的方式:
- webpack watch mode;
- webpack-dev-server(常用);
- webpack-dev-middleware ;
15.1. Webpack watch
webpack提供了watch模式:
在该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译;
不需要手动去运行 npm run build指令了;
如何开启watch呢?
方式一:在导出的配置中,添加 watch: true;
方式二:在启动webpack的命令中,添加 –watch的标识;
1
2
3"script": {
"watch": "webpack --watch"
}
运行命名
1
npm run watch
15.2. webpack-dev-server
watch 方式可以监听到文件的变化,但是事实上它本身是没有自动刷新浏览器的功能的:
- 当然,可以在VSCode中使用live-server来完成这样的功能;
- 但是,在不适用live-server的情况下,可以具备live reloading(实时重新加载)的功能;
安装webpack-dev-server
1
npm i webpack-dev-server -D
修改配置文件,告知 dev server,从什么位置查找文件:
webpack.config.js
1
2
3
4
5
6
7
8devServer: {
// 从什么位置查找文件
// 查找 favicon.ico
static: resolve(__dirname,'public'),
compress: true,
open: true,
hot: true
}package.json
1
2
3"scripts": {
"serve": "webpack serve"
}运行命令
1
npm run serve
终端输出
1
2
3
4
5
6
7
8
9
10<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/
<i> [webpack-dev-server] On Your Network (IPv4): http://192.168.0.190:8080/
<i> [webpack-dev-server] On Your Network (IPv6): http://[fe80::1]:8080/
<i> [webpack-dev-server] Content not from webpack is served from 'xx/public' directory
asset js/index.js 755 KiB [emitted] (name: main) 1 related asset
asset css/main.css 2.54 KiB [emitted] (name: main) 1 related asset
asset index.html 686 bytes [emitted]
Entrypoint main 758 KiB (819 KiB) = css/main.css 2.54 KiB js/index.js 755 KiB 2 auxiliary assets
runtime modules 56.3 KiB 30 moduleswebpack-dev-server 在编译之后
不会写入到任何输出文件
。而是将文件保留在内存中
:- 事实上webpack-dev-server使用了一个库叫memfs(memory-fs webpack自己写的)
15.3. 认识模块热替换(HMR)
什么是HMR呢?
HMR的全称是Hot Module Replacement,翻译为模块热替换;
模块热替换是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面;
HMR通过如下几种方式,来提高开发的速度:
- 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失;
- 只更新需要变化的内容,节省开发的时间;
- 修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式;
如何使用HMR呢?
- 默认情况下,webpack-dev-server已经支持HMR,只需要开启即可;
- 在不开启HMR的情况下,修改了源代码之后,整个页面会自动刷新,使用的是live reloading;
15.3.1. 开启HMR
webpack.config.js
1
2
3devServer: {
hot: true
}浏览器终端
1
2[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.
[HMR] Waiting for update signal from WDS...当修改某一个模块的代码时,依然是刷新的整个页面:
- 这是因为需要去指定哪些模块发生更新时,进行HMR;
15.3.2. 案例
在 src/js 下新建 math.js
1 | export function sum(a,b){ |
src/js/index.js
1 | import {sum} from './math'; |
修改 math.js,浏览器终端输出
1 | [HMR] Updated modules: |
15.3.3. 框架的HMR
- 在开发其他项目时,是否需要经常手动去写入 module.hot.accpet相关的API呢?
- 比如开发Vue、React项目,我们修改了组件,希望进行热更新,这个时候应该如何去操作呢?
- 事实上社区已经针对这些有很成熟的解决方案了:
- 比如vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验;
- 比如react开发中,有React Hot Loader,实时调整react组件(目前React官方已经弃用了,改成使用react- refresh);
修改App.vue,浏览器终端输出
1 | [HMR] Updated modules: |
修改 Hello.vue,浏览器终端输出
1 | [HMR] Updated modules: |
15.3.4. HMR的原理
那么HMR的原理是什么呢?如何可以做到只更新一个模块中的内容呢?
- webpack-dev-server会创建两个服务:
提供静态资源的服务(express)
和Socket服务(net.Socket)
; express server
负责直接提供静态资源的服务
(打包后的资源直接被浏览器请求和解析);
- webpack-dev-server会创建两个服务:
HMR Socket Server,是一个socket的长连接:
- 长连接有一个最好的好处是建立连接后
双方可以通信
(服务器可以直接发送文件到客户端); - 当服务器监听到对应的模块发生变化时,会生成两个文件
.json
(manifest文件)和.js
文件(update chunk); - 通过长连接,可以直接将这两个文件
主动发送给客户端
(浏览器); - 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新;
- 长连接有一个最好的好处是建立连接后
15.3.5. HMR原理图
- Http连接:
- 客户端发送http请求 -> 和服务器建立连接 -> 服务器做出响应 -> 服务器断开连接
- Socket连接:
- 即时通信:微信,聊天,直播
- 客户端和服务器之间存在一条通道,双方可以互发信息
15.4. hotOnly、host配置
host设置主机地址:
- 默认值是localhost;
- 如果希望其他地方也可以访问,可以设置为 0.0.0.0;
localhost 和 0.0.0.0 的区别
- localhost:本质上是一个域名,通常情况下会被解析成127.0.0.1;
- 127.0.0.1:
回环地址
(Loop Back Address),表达的意思其实是主机自己发出去的包,直接被自己接收;- 正常的数据库包经常 应用层 - 传输层 - 网络层 - 数据链路层 - 物理层 ;
- 而回环地址,是在网络层直接就被获取到了,是不会经常数据链路层和物理层的;
- 比如监听 127.0.0.1时,在同一个网段下的主机中,通过ip地址是不能访问的;
- 0.0.0.0:监听IPV4上所有的地址,再根据端口找到不同的应用程序;
- 比如监听 0.0.0.0时,在同一个网段下的主机中,通过ip地址是可以访问的;
15.5. port、open、compress
port设置监听的端口,默认情况下是8080
open是否打开浏览器:
- 默认值是false,设置为true会打开浏览器;
- 也可以设置为类似于 Google Chrome等值;
compress是否为静态文件开启gzip compression:
默认值是false,可以设置为true;
设为true时,浏览器的请求头信息
1
2
3
4Accept-Encoding: gzip, deflate, br
Connection: Upgrade
Host: localhost:3000
Upgrade: websocket
15.6. Proxy
https://webpack.docschina.org/configuration/dev-server/#devserverproxy
proxy是开发中非常常用的一个配置选项,它的目的设置代理来
解决跨域访问
的问题:- 比如一个api请求是 http://localhost:8888,但是本地启动服务器的域名是 http://localhost:8000,这个时候发送网络请求就会出现跨域的问题;
- 那么可以将请求先发送到一个代理服务器,代理服务器和API服务器没有跨域的问题,就可以解决跨域问题了;
进行如下的设置
target
:表示的是代理到的目标地址,比如 /api-hy/moment会被代理到 http://localhost:8888/api-hy/moment;pathRewrite
:默认情况下,/api-hy 也会被写入到URL中,如果希望删除,可以使用pathRewrite;secure
:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false;changeOrigin
:它表示是否更新代理后请求的headers中host地址;
15.6.1. 案例
webpack.config.js
1 | devServer: { |
安装axios
1 | npm i axios |
在 src/js/index.js
中编写请求
1 | const axios = require("axios"); |
新建node服务器
安装koa npm i koa koa-router
编写服务 src/js/node.js
1 | const Koa = require("koa"); |
启动服务
1 | node src/js/node.js |
浏览器输出
1 | data: {result: Array(4)} |
15.6.2. changeOrigin的解析
- 这个 changeOrigin官方说的非常模糊,通过查看源码发现其实是要修改代理请求中的headers中的host属性:
- 因为真实的请求,其实是需要通过 http://localhost:8888来请求的;
- 但是因为使用了代码,默认情况下它的值时 http://localhost:8000;
- 如果需要修改,那么可以将changeOrigin设置为true即可
15.6.3. 源码
node_modules/http-proxy/lib/http-proxy/common.js 中添加打印信息
1 | if (options.changeOrigin) { |
设置不同,host值不同
changeOrigin: false
1 | =====changeOrigin====== |
changeOrigin: true
1 | =====changeOrigin====== |
15.7. historyApiFallback
https://webpack.docschina.org/configuration/dev-server/#devserverhistoryapifallback
historyApiFallback是开发中一个非常常见的属性,它主要的作用是解决SPA页面在路由跳转之后,进行页面刷新时,返回404的错误
boolean值
默认是false
如果设置为true,那么在刷新时,返回404错误时,会自动返回 index.html 的内容;
object类型的值,可以配置rewrites属性:
- 可以配置from来匹配路径,决定要跳转到哪一个页面;
事实上devServer中实现historyApiFallback功能是通过connect-history-api-fallback库的:
- 可以查看 connect-history-api-fallback 文档
1 | devServer: { |
16. resolve模块解析
resolve用于设置模块如何被解析:
在开发中会有各种各样的模块依赖,这些模块可能来自于自己编写的代码,也可能来自第三方库;
resolve可以帮助webpack从每个 require/import 语句中,找到需要引入到合适的模块代码;
webpack 使用 enhanced-resolve 来解析文件路径;
webpack能解析三种文件路径:
绝对路径
- 由于已经获得文件的绝对路径,因此不需要再做进一步解析。
相对路径
- 在这种情况下,使用 import 或 require 的资源文件所处的目录,被认为是上下文目录;
- 在 import/require 中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径;
模块路径
- 在 resolve.modules中指定的所有目录检索模块;
- 默认值是 [‘node_modules’],所以默认会从node_modules中查找文件;
- 可以通过设置别名的方式来替换初识模块路径,通过alias的配置;
- 在 resolve.modules中指定的所有目录检索模块;
16.1. 确实文件还是文件夹
如果是一个文件:
如果文件具有扩展名,则直接打包文件;
否则,将使用 resolve.extensions 选项作为文件扩展名解析;
如果是一个文件夹:
会在文件夹中根据 resolve.mainFiles配置选项中指定的文件顺序查找;
- resolve.mainFiles的默认值是 [‘index’];
- 再根据 resolve.extensions来解析扩展名;
16.2. extensions和alias配置
extensions是解析到文件时自动添加扩展名:
默认值是 [‘.wasm’, ‘.mjs’, ‘.js’, ‘.json’];
如果代码中想要添加加载 .vue 或者 jsx 或者 ts 等文件时,必须自己写上扩展名;
另一个非常好用的功能是配置别名alias:
- 特别是当我们项目的目录结构比较深的时候,或者一个文件的路径可能需要 ../../../这种路径片段;
- 可以给某些常见的路径起一个别名;
1 | module.exports = { |
此时,可以在代码中使用 alias
1 | import app from '@/vue/App'; |
17. 不同开发环境
17.1. 区分不同开发环境
目前所有的webpack配置信息都是放到一个配置文件中的:webpack.config.js
- 当配置越来越多时,这个文件会变得越来越不容易维护;
- 并且某些配置是在开发环境需要使用的,某些配置是在生成环境需要使用的,当然某些配置是在开发和生成环 境都会使用的;
- 所以,最好对配置进行划分,方便维护和管理;
那么,在启动时如何可以区分不同的配置呢?
- 方案一:编写两个不同的配置文件,开发和生成时,分别加载不同的配置文件即可;
- 方式二:使用相同的一个入口配置文件,通过设置参数来区分它们;
17.2. 不同配置文件
根目录下新建 config 文件夹,并创建三个配置文件
- webpack.comm.conf.js
- webpack.dev.conf.js
- webpack.prod.conf.js
修改 package.json
1 | "scripts": { |
安装依赖
1 | npm i webpack-merge -D |
17.3. webpack.comm.config.js
1 | const { resolve } = require("path"); |
17.4. webpack.dev.config.js
1 | const { resolve } = require("path"); |
17.5. webpack.prod.config.js
1 | const { resolve } = require("path"); |
17.6. 开发环境
1 | npm run serve |
17.7. 生产环境
1 | npm run build |