[万字逐步详解]使用 webpack 打包 vue 项目(基础生产环境)
Posted GoldenaArcher
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[万字逐步详解]使用 webpack 打包 vue 项目(基础生产环境)相关的知识,希望对你有一定的参考价值。
[万字逐步详解]使用 webpack 打包 vue 项目(基础生产环境)
本次项目属于练手项目,使用的是 vue-cli 新建的新手引导页面,项目本身没有特别复杂的逻辑,而且这里也不涉及到 Vue 的学习,只是会通过逐步分解完成使用 webpack 对项目完成最终的打包。
本次会使用到的 插件(plugins) 和 加载器(loaders) 有:
-
webpack & webpack-cli
-
vue-loader & vue-template-compiler
-
style-loader & less & less-loader
这个根据具体的项目需求可以修改,这里使用了 LESS,所以需要额外搭配 less & less-loader
只用 css 的可以只使用 style-loader
-
file-loader & url-loader
这里也存在二选一的关系,url-loader 起到的是优化的作用,它的 fallback 是 file-loader,所以使用 url-loader 必须安装 file-loader
反之只用 file-loader 可以不用安装 url-loader
-
htmlWebpackPlugin
解决了 HTML 文件,也就是入口文件的迁徙和模板问题
-
clean-webpack-plugin
自动清除之前编译好的文件
-
copy-webpack-plugin
满足了迁徙其他静态文件的需求
项目分析
结构如下,需要编译的文件有 vue, less 和 javascript,需要压缩的文件有打包后的 JavaScript 和 HTML 文件。
注*:这个项目是一个使用 vue-cli 创建的初始项目,只不过移除了脚手架中自动打包的部分。
实现基础功能
这一部分主要是实现一些基础功能,这个项目已经是一个初始化好了的 node 项目,否则需要使用 init
命令去进行初始化。
安装 webpack 和 webpack-cli
D:\\\\vue-app-base>npm i webpack webpack-cli -D
i
是 install 的缩写,-D
是 --save-dev
的缩写,这样就将 webpack 和 webpack-cli 以开发依赖安装好了。
查看 package.json
文件,就会发现 devDependiencies 已经更新:
{
"devDependencies": {
"webpack": "^5.41.0",
"webpack-cli": "^4.7.2"
}
}
这时候可以新添一条脚本命令让 npm 去运行 webpack:
{
"script": {
"build": "webpack"
}
}
这个时候尝试在命令行运行 npm run build
会得出大量的报错结果,但是这可以证明命令可以正常被运行。
D:\\\\vue-app-base>npm run build
> vue-app-base@0.1.0 build D:\\\\vue-app-base
> webpack
assets by status 0 bytes [cached] 1 asset
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
ERROR in main
Module not found: Error: Can't resolve './src' in 'D:\\\\vue-app-base
# 省去大量报错信息
webpack 5.41.0 compiled with 1 error and 1 warning in 163 ms
添加配置文件
最后在根目录下新建 webpack.config.js
作为 webpack 的入口,并且套入模板,对打包模式、入口和出口文件进行配置。
webpack 的配置文件可以导出一个对象,通过导出对象的属性就可以完成相应的配置选项。
// npm原有的模块
const path = require('path');
module.exports = {
// 入口文件
entry: './src/main.js',
// 导出的部分
output: {
// 导出的文件名
filename: 'bundle.js',
// 导出的文件名会放置的路径
path: path.resolve(__dirname, 'dist/'),
},
};
这个时候再去运行 npm run build
,会得出下面的结果:
D:\\\\vue-app-base>npm run build
> vue-app-base@0.1.0 build D:\\\\vue-app-base
> webpack
assets by status 4.16 KiB [cached] 1 asset
runtime modules 718 bytes 3 modules
cacheable modules 770 bytes
./src/main.js 169 bytes [built] [code generated]
./src/App.vue 537 bytes [built] [code generated] [1 error]
./src/style.less 64 bytes [built] [code generated] [1 error]
# 省略掉一堆报错信息
webpack 5.41.0 compiled with 4 errors and 1 warning in 216 ms
这次调出了 4 个 errors 和 1 个 warning,这是件好事儿,至少把所有的报错消息一个个的列了出来,这样也有可以开始 debug 的点了。最怕的不是报错信息多,而是报错信息没有头绪,不知道从哪开始。
webpack 给出了项目中的几个问题,分别是:
-
WARNING in configuration
这个是说 mode 没有设置,会默认以生产环境进行开发
-
ERROR in ./src/App.vue 1:0
这个提示就很明显了,webpack 说:
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
需要一个 Vue 的 loader 去解决问题
-
ERROR in ./src/main.js 1:0-21
这个问题应该也是一样的,具体报错信息如下:
Module not found: Error: Can't resolve 'vue' in 'D:\\\\vue-app-base\\src'
-
ERROR in ./src/style.less 1:0
这个问题是一样的,也是缺少 loader:
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
-
ERROR in bundle.js from Terser
这个问题是属于 output 文件的问题了,先解决上面打包的问题,再看看这个问题会不会出现
mode 的问题
这个问题很好解决,现在这个阶段,暂时会修改 scripts 中的命令行参数,去设置成 development 这个开发模式:
{
"scripts": {
"build": "webpack --mode=development"
}
}
同样再运行 npm run build
,就发现,报错信息居然少了一个:
webpack 5.41.0 compiled with 3 errors in 103 ms
最后一个 ERROR in bundle.js from Terser 居然消失了。那也就是说,现在剩下的 3 个报错都是开发时的报错,那就可以着手准备开放的 loader 和 plugin 的实现了。
另外,这个时候虽然还报错,但是 dist 目录已经能够显示出来了,证明 webpack 至少运行过一遍,并且导出了打包文件——这次可以确认 webpack 的安装和基础配置都没有问题了:
vue loader 及相关
vue loader 是 webpack 的插件,这里的配置也都是跟着官方文档实现的。
-
安装
vue-loader
和vue-template-compiler
npm install -D vue-loader vue-template-compiler # 突然意识到 cannot find Vue 的错误是因为我没有下载 package.json 里面列举的 Vue # 于是重新做一次下载,将需要的依赖包下载好 npm install
-
按照官方文档的内容,重新编写
webpack.config.js
的内容// 新增内容 const { VueLoaderPlugin } = require('vue-loader'); module.exports = { // 新增内容 module: { rules: [{ test: /\\.vue$/, loader: 'vue-loader' }], }, plugins: [new VueLoaderPlugin()], };
-
继续跟着官方文档,下载和安装所需要的依赖以及重新编写
webpack.config.js
# babel 负责解析 JavaScript npm install -D babel-loader @babel/core # 其他两个负责解析CSS npm install -D vue-style-loader css-loader # 这也是必须的插件,否则会报错 # 官方文档里没有列出来,如果你那里出现不同的报错,一定要看报错信息 npm i -D @vue/cli-plugin-babel
// 新增内容 const { VueLoaderPlugin } = require('vue-loader'); module.exports = { // module里面的东西为新增内容 module: { rules: [ // 解析Vue文件 { test: /\\.vue$/, loader: 'vue-loader' }, // 它会应用到普通的 `.js` 文件 // 以及 `.vue` 文件中的 `<script>` 块 { test: /\\.js$/, loader: 'babel-loader', }, // 它会应用到普通的 `.css` 文件 // 以及 `.vue` 文件中的 `<style>` 块 { test: /\\.css$/, use: ['vue-style-loader', 'css-loader'], }, ], }, plugins: [ // 请确保引入这个插件! new VueLoaderPlugin(), ], };
-
运行
npm run build
,这时候就只剩下两个报错信息了,一个是 png 打包报错(缺少图片处理),一个是 less 打包报错(缺少 less)
解决 CSS 的加载
CSS 加载的内容是 webpack 官方提供的:less-loader,所以这里也会跟着步骤一步一步解决报错信息
-
下载必须的依赖包
# 这是 style-loader,是下面配置需要用到的 npm install style-loader --save-dev # 这是 less 相关的内容 npm install less less-loader --save-dev
-
修改配置文件
注*:这里官方文档用的是 loader,但是不知道为什么使用 loader 会报错,遂改为了 use。如果你用 use 会报错,那就试试看 loader。webpack 的版本变化可能会让使用变得非常困难……
module.exports = { module: { rules: [ // 其他地方不变 // 对 less 文件进行处理 { test: /\\.less$/i, use: [ // compiles Less to CSS 'style-loader', 'css-loader', 'less-loader', ], }, ], }, };
-
运行
npm run build
进行检查这次只剩下一个 png 的问题了
解决图片加载的问题
-
下载对应的 loader,这里用的是 file-loader
D:\\\\vue-app-base>npm i -D file-loader
-
修改配置文件
module.exports = { output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), // 新增内容,修改路径,将其改为相对路径而不使用绝对路径 publicPath: './', }, module: { rules: [ // 省略其他的配置 // 针对 png 图片进行处理 { test: /\\.png$/i, // 如果像上面一样直接使用 use: "file-loader" // 会出现图片资源为 "[object Module]" 的问题 // 这是因为新版的 file-loader 自动开启了 ES模块 // 手动关闭即可正确显示 use: { loader: 'file-loader', options: { esModule: false, }, }, }, ], }, };
注*:这里的 file-loader 有一些特殊的属性,为了能够正确的显示图片,一定要将 esModule 设置为 false
这是由于 file-loader 版本的不同造成的,低版本环境下默认 esModule 是关闭的,所以可以正确显示。但是我现在使用的版本是 6.2,默认就是开启 esModule 的。当 esModule 开启的时候,图片引入的路径是
"[object Module]"
,而不是src="./26bd867dd65e26dbc77d1e151ffd36e0.png"
。 -
尝试打包
终于没有任何错误信息了,看起来是已经打包成功。
D:\\\\vue-app-base>npm run build > vue-app-base@0.1.0 build D:\\\\vue-app-base > webpack --mode=development asset bundle.js 566 KiB [emitted] (name: main) asset 26bd867dd65e26dbc77d1e151ffd36e0.png 6.69 KiB [emitted] [immutable] [from: src/assets/logo.png] (auxiliary name: main)runtime modules 2.04 KiB 6 modules modules by path ./node_modules/core-js/ 147 KiB modules by path ./node_modules/core-js/internals/*.js 85.2 KiB 114 modules modules by path ./node_modules/core-js/modules/*.js 62 KiB 36 modules modules by path ./src/ 16.1 KiB 20 modules modules by path ./node_modules/style-loader/dist/runtime/*.js 4.12 KiB 4 modules modules by path ./node_modules/vue-style-loader/lib/*.js 6.74 KiB ./node_modules/vue-style-loader/lib/addStylesClient.js 6.09 KiB [built] [code generated] ./node_modules/vue-style-loader/lib/listToStyles.js 671 bytes [built] [code generated] ./node_modules/vue/dist/vue.runtime.esm.js 222 KiB [built] [code generated] ./node_modules/vue-loader/lib/runtime/componentNormalizer.js 2.71 KiB [built] [code generated] ./node_modules/@babel/runtime/helpers/esm/typeof.js 726 bytes [built] [code generated] ./node_modules/css-loader/dist/runtime/api.js 1.75 KiB [built] [code generated] webpack 5.41.0 compiled successfully in 3548 ms
并且也能看到打包的目录下出现了新的图片:
注*:如果图片比较小的情况下,可以使用 url-loader,这样可以减少 http 请求的数量。
随后,手动复制黏贴和修改了一下 index.html,看一下渲染结果:
所有的内容都可以正常显示,可以开始静态资源的处理了。
url-loader,图片的优化
-
下载对应的 loader,这里用的是 file-loader
D:\\\\vue-app-base>npm install url-loader --save-dev
-
修改配置文件
不过说起来,vue 的 logo 正好卡在 8kb 这个尺寸,所以这大概是有什么约定俗成的规矩的?
注意,这里要将 file-loader 修改为 url-loader,而不能叠加使用 url-loader 和 file-loader 两个,原因是因为 url-loader 的 fallback 函数就是 file-loader,同时使用两个 loader 的话,url-loader 会对原本图片文件的路径进行 base64 的转换。这就会导致转换出来的图片不可见——转换的是图片路径,而非图片。
具体的讨论在 url-loader 的一个 issue 里有:url-loader is encoding path instead of image。
也算是踩了个雷学到了个知识点。
module.exports = { module: { rules: [ { test: /\\.png$/i, use: [ { // 原本是 file-loader,这里要修改为 url-loader loader: 'url-loader', options: { // 这个上限是官方文档设立的,这里就不改了 limit: 8192, // 同理,不设置为false打开的会是 esModule esModule: false, }, }, ], }, ], }, };
解决静态文件的加载
胜利的曙光已经近在眼前,下一步只需要解决静态资源的问题就好了。这里的静态资源指的是 public 下的 index.html 和 favicon.ico。首先需要将 public 下面的内容 cv 到 dist 下,随后还要处理一下 HTML 中的模板文件。
这里使用的插件是 htmlWebpackPlugin,这个也是 Vue 的原生项目中的提示:
<title><%= htmlWebpackPlugin.options.title %></title>
具体的配置可以查看 github 上的说明:
html-webpack-plugin
-
下载安装插件
D:\\\\vue-app-base>npm i -D html-webpack-plugin
-
修改配置文件
这里就是根据官方文档来修改配置了。
注*:BASE_URL 是环境变量,所以需要使用 deinfeProperty 去设置
// 新导入的两个依赖 const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { plugins: [ // 设置 webpack 所要的基础配置 new webpack.DefinePlugin({ BASE_URL: JSON.stringify('./'), }), // 空的狗欧早寒素仅会生成一个 index.html,会引入合适的文件,但是入口DOM节点 app 不见了,所以需要其他的配置 new HtmlWebpackPlugin({ template: 'public/index.html', favicon: 'public/favicon.ico', }), ], };
注 2*:新学到了一招,也可以直接在 html-webpack-plugin 里面直接设置:
new HtmlWebpackPlugin({ template: 'public/index.html', templateParameters: { BASE_URL: '/', }, });
-
运行
npm run build
打包此时的结果如下:
可以看到 favicon.ico 也被打包好了,index.html 的内容如下:
可以看到,标题(title), Logo 都已经被动态修改了,而且内容也可以正常渲染:
自动清除输出目录
现在每次编写还是会重写 dist 中的内容,虽然不会影响结果,但是可能会导致之前产出的文件遗留在文件夹中,导致不必要的浪费。这里会使用 clean-webpack-plugin 这个插件去解决这个问题,npmjs 的地址为:clean-webpack-plugin
注*:默认这个版本会将所有输出目录下的内容全都清空。
-
下载安装插件
npm install --save-dev clean-webpack-plugin
-
修改配置
这个配置改起来还是很快的,直接引用即可。
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { plugins: [ // 前面的配置不变 new CleanWebpackPlugin(), ], };
-
运行
npm run build
打包我运行了一下成功了,接下来就修改另外一个问题了。
复制静态文件
之前在配置 html-webpack-plugin 这个插件的时候,我又加了一个 favicon.ico,然后就发现,Vue 项目原本就有一个 favicon.ico,这里又加了一个,就出现了重复代码:
在上面调用 clean-webpack-plugin 的时候,我把原本的 favicon 配置去掉了,又出现图片没有被迁徙的问题。这里就找一个插件,进行静态资源的迁徙。
这里用的插件是在 webpack 官网上找到的一个插件:copy-webpack-plugin
-
下载安装插件
npm install copy-webpack-plugin --save-dev
-
修改配置
这里只需要迁徙 favicon.ico,所以就只设定了这一个文件,不过 copy-webpack-plugin 实际上可以迁徙单个文件,也可以迁徙整个文件夹
// 日常加引用 const CopyPlugin = require('copy-webpack-plugin'); module.exports = { plugins: [ // 其余不变 // 这里只迁徙了 favico.ico 这个文件 new CopyPlugin({ patterns: [{ from: 'public/favicon.ico', to: './' }], }), ], };
这样就完成了对 favicon.ico 文件的迁徙
最后
将 package.json 中的 build 修改一下:
// 之前是development
"build": "webpack --mode=production",
在运行打包命令 npm run build
,webpack 就会根据生产环境对打包的文件进行自动压缩:
HTML 的入口文件:
打包后的 bundle.js 文件:
这样的话,一个基础的生产环境打包功能就实现了。
下一步的目标是学习使用 webpack-dev-server 对开发环境进行配置——毕竟我这里使用的是 live server 插件实现的热更新,但是并不能保证所有的人都用一个编辑器/IDE,更别说同一个插件了。
同样,tree shaking 和 code splitting 也是需要考虑的问题,这也会在下一篇内容进行学习和配置。
最后,Linting 应该也是需要加上的,保持代码风格的统一对于后期的维护还是很有帮助的。
以上是关于[万字逐步详解]使用 webpack 打包 vue 项目(基础生产环境)的主要内容,如果未能解决你的问题,请参考以下文章
[万字逐步详解]使用 webpack-dev-server + ESLint 配置 vue 项目的开发环境