Vue 学习笔记(二十五):webpack 相关

  1. Webpack 是什么
  2. Vue CLI Webpack 相关

Webpack

更多参考官方文档

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

Webpack

如图,Webpack 可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求。

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

如果你还不熟悉 webpack,请阅读核心概念打包器对比

安装

前提,本地支持 node.js
安装最新版本 npm install --save-dev webpack
如果你使用 webpack 4+ 版本,你还需要安装 CLI。 npm install --save-dev webpack-cli

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

1
2
3
"scripts": {
"start": "webpack --config webpack.config.js"
}

当你在本地安装 webpack 后,你能够从 node_modules/.bin/webpack 访问它的 bin 版本。

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

核心概念

webpack 是高度可配置的。四个核心概念:

入口(entry)
输出(output)
loader
插件(plugins)

入口(entry)

入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

每个依赖项随即被处理,最后输出到称之为 bundles 的文件中,我们将在下一章节详细讨论这个过程。

可以通过在 webpack 配置中配置 entry 属性,来指定一个入口起点(或多个入口起点)。默认值为 ./src。

接下来我们看一个 entry 配置的最简单例子:

webpack.config.js

1
2
3
module.exports = {
entry: './path/to/my/entry/file.js'
};

根据应用程序的特定需求,可以以多种方式配置 entry 属性。可以了解更多配置方式

出口(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output 字段,来配置这些处理过程:

webpack.config.js

1
2
3
4
5
6
7
8
9
const path = require('path');

module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};

通过 output.filename 和 output.path 属性,来告诉 webpack bundle 的名称,以及我们想要 bundle 生成(emit)到哪里。可能你想要了解在代码最上面导入的 path 模块是什么,它是一个 Node.js 核心模块,用于操作文件路径。

生成(emitted 或 emit) 是“生产(produced)”或“释放(discharged)”的特殊术语

更多 output 可配置的特性
更多 output 概念

loader

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。

本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。

loader 能够 import 导入任何类型的模块(例如 .css 文件),这是 webpack 特有的功能,其他打包程序或任务执行器的可能并不支持。我们认为这种语言扩展是有很必要的,因为这可以使开发人员创建出更准确的依赖关系图。

在更高层面,在 webpack 的配置中 loader 有两个目标:

  1. test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
  2. use 属性,表示进行转换时,应该使用哪个 loader。

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const path = require('path');

const config = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
};

module.exports = config;

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

在 webpack 配置中定义 loader 时,要定义在 module.rules 中,而不是 rules。在定义错误时 webpack 会给出严重的警告。

插件(plugins)

loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

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

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};

module.exports = config;

插件列表

模式

通过选择 development 或 production 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化

1
2
3
module.exports = {
mode: 'production'
};

Getting Started

从零开始一个基本的构建过程

Vue CLI Webpack 相关

简单的配置方式

webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象:

1
2
3
4
5
6
7
8
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
new MyAwesomeWebpackPlugin()
]
}
}

该对象将会被 webpack-merge 合并入最终的 webpack 配置。

有些 webpack 选项是基于 vue.config.js 中的值设置的,所以不能直接修改。例如,应该修改 vue.config.js 中的 outputDir 选项而不是 output.path;应该修改 vue.config.js 中的 publicPath 选项而不是 output.publicPath。因为 vue.config.js 中的值会被用在配置里的多个地方,以确保所有的部分都能正常工作在一起。
:::

如果需要基于环境有条件地配置行为,或者想要直接修改配置,那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。在函数内,你可以直接修改配置,或者返回一个将会被合并的对象:

1
2
3
4
5
6
7
8
9
10
// vue.config.js
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
// 为生产环境修改配置...
} else {
// 为开发环境修改配置...
}
}
}

链式操作 (高级) Chaining (Advanced)

Vue CLI 内部的 webpack 配置是通过 webpack-chain 维护的。这个库提供了一个 webpack 原始配置的上层抽象,使其可以定义具名的 loader 规则和具名插件,并有机会在后期进入这些规则并对它们的选项进行修改。

它允许更细粒度的控制其内部配置。 常见修改 vue.config.js 中的 chainWebpack

修改 Loader 选项

1
2
3
4
5
6
7
8
9
10
11
12
13
// vue.config.js
module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
// 修改它的选项...
return options
})
}
}

对于 CSS 相关 loader 来说,推荐使用 css.loaderOptions 而不是直接链式指定 loader。这是因为每种 CSS 文件类型都有多个规则,而 css.loaderOptions 可以确保你通过一个地方影响所有的规则。

添加一个新的 Loader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// vue.config.js
module.exports = {
chainWebpack: config => {
// GraphQL Loader
config.module
.rule('graphql')
.test(/\.graphql$/)
.use('graphql-tag/loader')
.loader('graphql-tag/loader')
.end()
// 你还可以再添加一个 loader
.use('other-loader')
.loader('other-loader')
.end()
}
}

替换一个规则里的 Loader

如果你想要替换一个已有的基础 loader,例如为内联的 SVG 文件使用 vue-svg-loader 而不是加载这个文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// vue.config.js
module.exports = {
chainWebpack: config => {
const svgRule = config.module.rule('svg')

// 清除已有的所有 loader。
// 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。
svgRule.uses.clear()

// 添加要替换的 loader
svgRule
.use('vue-svg-loader')
.loader('vue-svg-loader')
}
}

修改插件选项

1
2
3
4
5
6
7
8
9
10
// vue.config.js
module.exports = {
chainWebpack: config => {
config
.plugin('html')
.tap(args => {
return [/* 传递给 html-webpack-plugin's 构造函数的新参数 */]
})
}
}

你需要熟悉 webpack-chain 的 API阅读一些源码以便了解如何最大程度利用好这个选项,但是比起直接修改 webpack 配置,它的表达能力更强,也更为安全。

比方说你想要将 index.html 默认的路径从 /Users/username/proj/public/index.html 改为 /Users/username/proj/app/templates/index.html。通过参考 html-webpack-plugin 能看到一个可以传入的选项列表。可以在下列配置中传入一个新的模板路径来改变它:

1
2
3
4
5
6
7
8
9
10
11
// vue.config.js
module.exports = {
chainWebpack: config => {
config
.plugin('html')
.tap(args => {
args[0].template = '/Users/username/proj/app/templates/index.html'
return args
})
}
}

可以通过接下来要讨论的工具 vue inspect 来确认变更。

审查项目的 webpack 配置

Inspecting the Project’s Webpack Config

因为 @vue/cli-service 对 webpack 配置进行了抽象,所以理解配置中包含的东西会比较困难,尤其是当你打算自行对其调整的时候。

vue-cli-service 暴露了 inspect 命令用于审查解析好的 webpack 配置。那个全局的 vue 可执行程序同样提供了 inspect 命令,这个命令只是简单的把 vue-cli-service inspect 代理到了你的项目中。

该命令会将解析出来的 webpack 配置、包括链式访问规则和插件的提示打印到 stdout。

你可以将其输出重定向到一个文件以便进行查阅:

1
vue inspect > output.js

它输出的并不是一个有效的 webpack 配置文件,而是一个用于审查的被序列化的格式。

也可以通过指定一个路径来审查配置的一小部分:

1
2
# 只审查第一条规则
vue inspect module.rules.0

或者指向一个规则或插件的名字:

1
2
vue inspect --rule vue
vue inspect --plugin html

最后,你可以列出所有规则和插件的名字:

1
2
vue inspect --rules
vue inspect --plugins

以一个文件的方式使用解析好的配置

有些外部工具可能需要通过一个文件访问解析好的 webpack 配置,比如那些需要提供 webpack 配置路径的 IDE 或 CLI。在这种情况下你可以使用如下路径:

1
<projectRoot>/node_modules/@vue/cli-service/webpack.config.js

该文件会动态解析并输出 vue-cli-service 命令中使用的相同的 webpack 配置,包括那些来自插件甚至是你自定义的配置。