使用 webpack 实现一个简易版的 vue 项目打包
目标
使用 webpack 实现一个使用 Vue CLI 创建出来的 Vue 项目的打包,有所不同的是,我删除了一些关于 webpack 的配置,还有路由等。模板已经放到github上了。
目标是要实现,开发服务器(实现热加载)、ESlint 检查、打包编译。
思路是,先安装、编写所需要用到的 loader,以及相关的插件,然后安装 webpack-dev-serve 并且 配合 webpack.HotModuleReplacementPlugin 来实现热更新,最后处理 copy 文件,完成最后的打包编译。
webpack 是什么
webpack 是一个前端的打包工具,它的思想主要是模块化,它把所有的静态文件都视为模块,最终会按照配置文件中的规则,生成优化过后的代码。
准备工作
去 github 上把这个项目模板下载到本地webpack-vue-template。
下载完后,我们先在控制台安装 webpack、webpack-cli 这两个包,进入项目命令行终端,输入 yarn add webpack webpack-cli --dev ,安装完成后,我们打开 webpack.common.js ,来编写一些基础的配置。
我们先指定 webpack 的打包入口文件,以及打包完成后输出的文件名,代码如下:
+ module.exports = {+ entry: "./src/main.js",+ output: {+ filename: "js/bundle.js",+ },+ module: {}+ plugins: []+ }
在接着,我们打开目录观察一下项目,有下面这几个文件目录:
- public 静态资源文件夹
- src 源文件目录
- package.json package file
- README.md
- webpack.common.js webpack 配置 通用文件
- webpack.dev.js 开发环境配置
- webpack.prod.js 生产环境配置
loader
loader 机制,是 webpack 的核心,webpack 它不知道如何处理 js 文件以外的文件,所有我们借助 loader 来帮助我们实现除了 js 文件以外的文件的转换编译等。
我们观察一下 src 目录,src 目录下一共有这几种类型的文件:png、less、vue、js。明白了该处理什么文件类型后,我们就可以开始编写 相关的 loader 了。
file-loader 处理 .png 文件
我们首先来处理一下 png 文件,我们需要 file-loader 来帮我们进行编译。file-loader 官方文档:https://webpack.js.org/loaders/file-loader/。
在控制台输入 yarn add file-loader --dev 来安装 file-loader 这个 loader,安装完成后,我们打开 webpack.common.js 来编写以下 file-loader 这个任务,其实,你通过官方文档的介绍,可能已经明白了 file-loader 的使用了。我们在 module 中写入配置,代码如下:
webpack.common.js:
module: {+ rules:[+ {+ test: /\.(png|jpg|gif)$/, // 处理文件后缀名以png、jpg、gif结尾的文件+ use: [+ {+ loader: "file-loader", // 使用 file-loader 这个 loader+ options: { // 选项配置+ name: "[hash].[ext]", // 处理过后的文件名+ esModule: false, // 是否开启模块化+ },+ },+ ],+ },+ ]}
当然我们还可以使用 url-loader 来处处理,url-loader 用法请查看官网,这里不做讲解。url-loader 文档
这里需要注意的一个点是,在最新版本中的 file-loader 已经默认启动了模块化,我们需要把 esModule 设置成 false, 不然呢,打包过后的结果就会变成下面这个样子。
<img alt="Vue logo" src="[object Module]" />
less-loader 处理 .less 文件
接下来,我们处理一下以 less 文件。同样,官网有提供相应的 loader 。less-loader 官方文档:https://webpack.js.org/loaders/less-loader/。但是光只有 less-loader 是不够的,因为 less-loader 只会帮助我们把 less 转换成 css ,并不会帮我们挂载在页面中 head 中。所以我们还需要 css-loader style-loader 这两个 loader 来帮助我们实现。
我们在控制台输入 yarn add less-loader less css-loader style-loader --dev来安装这几个 loader
安装完成后,我们在 module 中编写规则,代码如下:
webpack.common.js:
module: {rules:[+ {+ test: /\.less$/,+ use: [+ {+ loader: "style-loader",+ },+ {+ loader: "css-loader",+ },+ {+ loader: "less-loader",+ },+ ],+ },]}
这里需要注意的是,loader 是从下往上,从右往左执行的。我们需要先执行 less-loader ,要把 less 转换成 css 给 css-loader 去处理,然后 css-loader 处理完后,交给 style-loader 处理,style-loader 会把处理后的结果,帮我们挂载到页面的 head 中。
vue-lodaer 处理 .vue 文件
接下来,我们来处理 .vue 类型的文件。我们需要用到一个 vue-loader 它是 vue/cli 官方提供的一个 loader ,专门处理以一种名为单文件组件 (SFCs)的格式撰写的 Vue 组件。Vue Loader 官方文档
首先,我们需要安装 vue-loader ,同时还要安装一个插件 vue-template-compiler 。在命令行终端输入:yarn add vue-loadervue-template-compiler --dev。
安装完成后,我们来编写相应的规则,代码如下:
webpack.common.js:
+ const VueLoaderPlugin = require("vue-loader/lib/plugin");module: {rules:[+ {+ test: /\.vue$/,+ loader: "vue-loader",+ },]},plugins: [+ new VueLoaderPlugin(),],
编写完 loader 后,根据官方文档的提示,我们还需要在 配置文件中引入 vue-loader/lib/plugin ,并且在把这个插件添加到 plugins 中,代码如下:
+ const VueLoaderPlugin = require("vue-loader/lib/plugin");plugins: [+ new VueLoaderPlugin(),],
官方文档声明,必须要引入这个插件,否则无法正常处理 .vue 文件。
根据官方文档的事例中,我们发现,还需要对 .vue 模板中的 css 做处理,需要使用到一个 loader 叫做 vue-style-loader。我们来安装这个 loader。在命令终端输入 yarn add vue-style-loader --dev 来安装。
安装完成后,我们来编写相关的规则,代码如下:
module: {rules:[+ // 它会应用到普通的 `.css` 文件+ // 以及 `.vue` 文件中的 `<style>` 块+ {+ test: /\.css$/,+ use: ["vue-style-loader", "css-loader"],+ },{test: /\.less$/,use: [{loader: "style-loader",},{loader: "css-loader",},{loader: "less-loader",},],},]}
babel-loader 处理 js 文件
接下来,我们需要处理 .vue 文件 和 js 文件中 js 的代码,我们可以使用 babel-loader 这个 loader 来帮助我们处理 ,官方文档:https://webpack.docschina.org/loaders/babel-loader/ 官方文档中提到,还需要安装 @babel/core @babel/preset-env 这两个包。我们根据官网文档的要求,在命令行终端输入 yarn add babel-loader @babel/core @babel/preset-env --dev。
安装完成后,开始编写 loader 规则 ,代码如下:
webpack.common.js:
module: {rules:[+ // 它会应用到普通的 `.js` 文件+ // 以及 `.vue` 文件中的 `<script>` 块+ {+ test: /\.js$/,+ loader: "babel-loader",+ },]}
eslint-loader 检查代码规范
接下来,我们需要安装 eslint-loader 来帮我们进行代码的检查,官方文档:https://webpack.js.org/loaders/eslint-loader/ 。 我们根据官方文档,我们需要安装 eslint 和 eslint-loader 。在命令行输入 yarn add eslint eslint-loader --dev 来安装。
完成安装后,我们写入规则,代码如下:
webpack.common.js:
module: {rules:[// 它会应用到普通的 `.js` 文件// 以及 `.vue` 文件中的 `<script>` 块{test: /\.js$/,loader: "babel-loader",},+ {+ test: /\.(vue|js)$/, // 需要检查代码的文件类型+ exclude: /node_modules/, // 不需要检查的目录文件+ loader: "eslint-loader", // 使用 eslint-loader+ enforce: "pre",+ include: [__dirname + "/src"], // 要检查的目录+ },]}
至此,我们所有的 loader 已经写完了。
配置 eslint 有两种方法
- 通过
yarn eslint --init来初始化一个配置文件 - 在 package.json 中的 eslintConfig 编写规则
模板中已经提供了一个简答的校验规则了,所以我们这里只需要安装相关 loader 就行了。
或许你可能有疑问,要如何校验这些 loader 是否写的真确?我们可以在启动开发服务器时,去校验这些 loader 和 plugins 是否正确。
plugins
下面,我们来安装几个 plugins 让我们打包更加便捷。
HtmlWebpackPlugin
我们首先打开 public 下的 index.html 文件,可以看到 title 和 strong 标签中使用了一个叫做 htmlWebpackPlugin.options.title 的内容。这个是 HtmlWebpackPlugin 插件的作用之一。

HtmlWebpackPlugin 插件的作用是,在打包完成之后,自动生成一个 html 文件到输出目录中,并且还会自动引入打包编译之后的 js 主文件,可以看到这个插件的 github官网上有文档说明,我们也可以指定一个模板生成 html 文件。在 HtmlWebpackPlugin 配置中,我们还可以设置一些参数,供 index.html 使用,webpack 会一同把它编译。
我们在命令行终端输入 yarn add html-webpack-plugin --dev来安装这个插件。
安装完成后,在 webpack.common.js 文件中,先引入这个插件,并且在把这个插件添加到 plugins 中,代码如下:
webpack.common.js:
+ const HtmlWebpackPlugin = require("html-webpack-plugin");plugins: [new VueLoaderPlugin(),+ new HtmlWebpackPlugin({+ title: "webpack-vue-template", // 网站 title+ template: "./public/index.html", // 使用指定的模板+ }),]
webpack.DefinePlugin
我们打开到 public 下的 index.html 文件,我们看到 link 标签中,有一个 <%= BASE_URL %> 这是个什么东东?

其实这是一个全局变量,这是 webpack.DefinePlugin 提供的功能,它可以用来定义全局变量,在 webpack 打包的时候会对这些变量做替换。
它不需要引入而外的插件,只需要引入 webpack 就可以了,代码如下:
webpack.common.js:
+ const webpack = require("webpack");const VueLoaderPlugin = require("vue-loader/lib/plugin");const HtmlWebpackPlugin = require("html-webpack-plugin");
再接着,写入 plugins , 代码如下:
plugins: [new VueLoaderPlugin(),new HtmlWebpackPlugin({title: "webpack-vue-template",template: "./public/index.html",}),+ new webpack.DefinePlugin({+ BASE_URL: JSON.stringify("./"),+ }),],
至此,我们第一步的工作就完成了,我们已经编写完 loader 和 plugins 了。
webpack-dev-server
接下来,我们需要安装 webpack-dev-serve 并且 配合 webpack.HotModuleReplacementPlugin 来启动一个开发服务器并且实现热更新。
我们在命令行终端输入:yarn add webpack-dev-server --dev ,安装完成后,我们还需要安装 webpack-merge,它的作用主要是帮助我们合拼覆盖原有的配置。在终端输入 yarn add webpack-merge --dev完成安装。
其实我们之前的所有的工作,都是在 webpack.common.js 中编写的,我们把通用的部分,抽离出了一个文件,接着,我们可以通过 merge 在指定的环境帮我们合拼覆盖原有的配置。
我们打开 webpack.dev.js 文件,开始编写启动服务器这个任务,代码如下:
webpack.dev.js
// 引入这个插件,在最新版本中,它不在返回一个函数,而是返回一个对象,我们需要进行结构操作。const { merge } = require("webpack-merge");// 引入我们抽离出来的公共模块const common = require("./webpack.common");// 合拼 common ,并且编写新的内容,覆盖原基础文件module.exports = merge(common, {mode: "development", // 设置为 development 觅食devtool: "cheap-eval-module-source-map", // 开启source-mapdevServer: { // 启动服务器host: "localhost", // 主机地址port: "6060", // 端口open: true, // 是否自动打开浏览器contentBase: "public", // 告诉服务器从public目录提供静态文件},});
好了,编写完后,我们在命令行终端,输入 yarn webpack-dev-server --open --config webpack.dev.js 来启动我们的服务器。在这个启动服务器的时,我们可以校验,我们之前写的 loader 和 plugins 是否有问题。启动的过程需要稍等一会,因为 eslint 会去校验代码,如果我们的代码写的不规范,控制台会给出提示。
如果一切顺利,系统会打开默认浏览器并且帮我打开http://localhost:6060/,效果如下:

接下来,我们来实现热更新的功能,实现热更新,需要配合 webpack 提供的一个 叫 HotModuleReplacementPlugin 的模块,我们需要在 devServer 中配置 hot 为 true 、并且引入 webpack,然后在 plugins 中实例化这个函数。代码如下:
+ const webpack = require("webpack");// 引入这个插件,在最新版本中,它不在返回一个函数,而是返回一个对象,我们需要进行结构操作。const { merge } = require("webpack-merge");// 引入我们抽离出来的公共模块const common = require("./webpack.common");// 合拼 common ,并且编写新的内容,覆盖原基础文件module.exports = merge(common, {mode: "development", // 设置为 development 觅食devtool: "cheap-eval-module-source-map", // 开启source-mapdevServer: {// 启动服务器host: "localhost", // 主机地址port: "6060", // 端口open: true, // 是否自动打开浏览器+ hot: true, // 开启热更新contentBase: "public", // 告诉服务器从public目录提供静态文件},+ plugins: [new webpack.HotModuleReplacementPlugin()],});
接着,我们重新启动服务,输入 yarn webpack-dev-server --open --config webpack.dev.js,服务启动后,我们修改一下 App.vue 这个文件,修改一下 msg 这个属性,我们点击保存,回到浏览器,页面内容已经修改成了我们修改后的内容了,这就证明热更新是没有问题的。 效果如下:
视频的地址是:https://raw.githubusercontent.com/Aisen-cai/Test/master/webpack-dev-server.mov
处理打包上线
接下来,我们来处理最后的任务,编写打包上线的编译模块的内容。这个流程我们需要用到 clean-webpack-plugin、copy-webpack-plugin 这两个插件,clean-webpack-plugin 帮助我们自动清除目录,copy-webpack-plugin 帮助我们拷贝一些文件到指定目录。
在控制台输入 yarn add clean-webpack-plugin copy-webpack-plugin --dev来安装。安装完成后,编写以下代码:
webpack.prod.js
const path = require("path");// 引入这个插件,在最新版本中,它不在返回一个函数,而是返回一个对象,我们需要进行结构操作。const { merge } = require("webpack-merge");// 引入我们抽离出来的公共模块const commmon = require("./webpack.common");// 引入 clean-webpack-plugin,它的实例方法是个对象,我们需要结构操作。const { CleanWebpackPlugin } = require("clean-webpack-plugin");// 引入 copy-webpack-pluginconst copyPlugin = require("copy-webpack-plugin");// 合拼 common ,并且编写新的内容,覆盖原基础文件module.exports = merge(commmon, {mode: "production", // 设置为production模式output: {filename: "bundle.js", // 打包过后的js文件名path: path.resolve(__dirname, "dist"), // 输出的目录},plugins: [new CleanWebpackPlugin(), new copyPlugin(["public"])],});
编写完毕过后,我们在终端输入 yarn webpack --config webpack.prod.js进行编译,稍等一会,编译完成后,我们在根目录下会出现一个 dist 目录,我们打开 dist 目录下的每个文件,来看看编译过后是什么样子的。我们打开到 bundle.js,我们可以看到代码已经被压缩了,我们可以启动一个服务器,来看看是否打包过后的内容是否可运行。
优化
至此,我们所有的任务都已经完成了。我们接下来,来优化一下,我们每当启动服务器或者打包的时候,都要输入一大串命令,闲得很麻烦,我们可以在 package.json 中写 script 脚本。代码如下:
package.json
+ "scripts": {+ "serve": "webpack-dev-server --open --config webpack.dev.js",+ "build": "webpack --config webpack.prod.js",+ "lint": "eslint --ext .js,.vue src"+ },
这样,我们就不需要输入这么多繁琐的命令了。
至此,所有的任务都已经完成了,有错误或者不明白的地方,可以在下面提出,欢迎纠正,谢谢~
