使用 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 的打包入口文件,以及打包完成后输出的文件名,代码如下:

  1. + module.exports = {
  2. + entry: "./src/main.js",
  3. + output: {
  4. + filename: "js/bundle.js",
  5. + },
  6. + module: {}
  7. + plugins: []
  8. + }

在接着,我们打开目录观察一下项目,有下面这几个文件目录:

  • 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:

  1. module: {
  2. + rules:[
  3. + {
  4. + test: /\.(png|jpg|gif)$/, // 处理文件后缀名以png、jpg、gif结尾的文件
  5. + use: [
  6. + {
  7. + loader: "file-loader", // 使用 file-loader 这个 loader
  8. + options: { // 选项配置
  9. + name: "[hash].[ext]", // 处理过后的文件名
  10. + esModule: false, // 是否开启模块化
  11. + },
  12. + },
  13. + ],
  14. + },
  15. + ]
  16. }

当然我们还可以使用 url-loader 来处处理,url-loader 用法请查看官网,这里不做讲解。url-loader 文档

这里需要注意的一个点是,在最新版本中的 file-loader 已经默认启动了模块化,我们需要把 esModule 设置成 false, 不然呢,打包过后的结果就会变成下面这个样子。

  1. <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:

  1. module: {
  2. rules:[
  3. + {
  4. + test: /\.less$/,
  5. + use: [
  6. + {
  7. + loader: "style-loader",
  8. + },
  9. + {
  10. + loader: "css-loader",
  11. + },
  12. + {
  13. + loader: "less-loader",
  14. + },
  15. + ],
  16. + },
  17. ]
  18. }

这里需要注意的是,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:

  1. + const VueLoaderPlugin = require("vue-loader/lib/plugin");
  2. module: {
  3. rules:[
  4. + {
  5. + test: /\.vue$/,
  6. + loader: "vue-loader",
  7. + },
  8. ]
  9. },
  10. plugins: [
  11. + new VueLoaderPlugin(),
  12. ],

编写完 loader 后,根据官方文档的提示,我们还需要在 配置文件中引入 vue-loader/lib/plugin ,并且在把这个插件添加到 plugins 中,代码如下:

  1. + const VueLoaderPlugin = require("vue-loader/lib/plugin");
  2. plugins: [
  3. + new VueLoaderPlugin(),
  4. ],

官方文档声明,必须要引入这个插件,否则无法正常处理 .vue 文件。

根据官方文档的事例中,我们发现,还需要对 .vue 模板中的 css 做处理,需要使用到一个 loader 叫做 vue-style-loader。我们来安装这个 loader。在命令终端输入 yarn add vue-style-loader --dev 来安装。

安装完成后,我们来编写相关的规则,代码如下:

  1. module: {
  2. rules:[
  3. + // 它会应用到普通的 `.css` 文件
  4. + // 以及 `.vue` 文件中的 `<style>` 块
  5. + {
  6. + test: /\.css$/,
  7. + use: ["vue-style-loader", "css-loader"],
  8. + },
  9. {
  10. test: /\.less$/,
  11. use: [
  12. {
  13. loader: "style-loader",
  14. },
  15. {
  16. loader: "css-loader",
  17. },
  18. {
  19. loader: "less-loader",
  20. },
  21. ],
  22. },
  23. ]
  24. }

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:

  1. module: {
  2. rules:[
  3. + // 它会应用到普通的 `.js` 文件
  4. + // 以及 `.vue` 文件中的 `<script>` 块
  5. + {
  6. + test: /\.js$/,
  7. + loader: "babel-loader",
  8. + },
  9. ]
  10. }

eslint-loader 检查代码规范

接下来,我们需要安装 eslint-loader 来帮我们进行代码的检查,官方文档:https://webpack.js.org/loaders/eslint-loader/ 。 我们根据官方文档,我们需要安装 eslint 和 eslint-loader 。在命令行输入 yarn add eslint eslint-loader --dev 来安装。

完成安装后,我们写入规则,代码如下:

webpack.common.js:

  1. module: {
  2. rules:[
  3. // 它会应用到普通的 `.js` 文件
  4. // 以及 `.vue` 文件中的 `<script>` 块
  5. {
  6. test: /\.js$/,
  7. loader: "babel-loader",
  8. },
  9. + {
  10. + test: /\.(vue|js)$/, // 需要检查代码的文件类型
  11. + exclude: /node_modules/, // 不需要检查的目录文件
  12. + loader: "eslint-loader", // 使用 eslint-loader
  13. + enforce: "pre",
  14. + include: [__dirname + "/src"], // 要检查的目录
  15. + },
  16. ]
  17. }

至此,我们所有的 loader 已经写完了。

配置 eslint 有两种方法

  • 通过 yarn eslint --init来初始化一个配置文件
  • 在 package.json 中的 eslintConfig 编写规则

模板中已经提供了一个简答的校验规则了,所以我们这里只需要安装相关 loader 就行了。

或许你可能有疑问,要如何校验这些 loader 是否写的真确?我们可以在启动开发服务器时,去校验这些 loader 和 plugins 是否正确。

plugins

下面,我们来安装几个 plugins 让我们打包更加便捷。

HtmlWebpackPlugin

我们首先打开 public 下的 index.html 文件,可以看到 title 和 strong 标签中使用了一个叫做 htmlWebpackPlugin.options.title 的内容。这个是 HtmlWebpackPlugin 插件的作用之一。

使用 webpack 实现一个简易版的 vue 项目打包 - 图1

HtmlWebpackPlugin 插件的作用是,在打包完成之后,自动生成一个 html 文件到输出目录中,并且还会自动引入打包编译之后的 js 主文件,可以看到这个插件的 github官网上有文档说明,我们也可以指定一个模板生成 html 文件。在 HtmlWebpackPlugin 配置中,我们还可以设置一些参数,供 index.html 使用,webpack 会一同把它编译。

我们在命令行终端输入 yarn add html-webpack-plugin --dev来安装这个插件。

安装完成后,在 webpack.common.js 文件中,先引入这个插件,并且在把这个插件添加到 plugins 中,代码如下:

webpack.common.js:

  1. + const HtmlWebpackPlugin = require("html-webpack-plugin");
  2. plugins: [
  3. new VueLoaderPlugin(),
  4. + new HtmlWebpackPlugin({
  5. + title: "webpack-vue-template", // 网站 title
  6. + template: "./public/index.html", // 使用指定的模板
  7. + }),
  8. ]

webpack.DefinePlugin

我们打开到 public 下的 index.html 文件,我们看到 link 标签中,有一个 <%= BASE_URL %> 这是个什么东东?

使用 webpack 实现一个简易版的 vue 项目打包 - 图2

其实这是一个全局变量,这是 webpack.DefinePlugin 提供的功能,它可以用来定义全局变量,在 webpack 打包的时候会对这些变量做替换。

它不需要引入而外的插件,只需要引入 webpack 就可以了,代码如下:

webpack.common.js:

  1. + const webpack = require("webpack");
  2. const VueLoaderPlugin = require("vue-loader/lib/plugin");
  3. const HtmlWebpackPlugin = require("html-webpack-plugin");

再接着,写入 plugins , 代码如下:

  1. plugins: [
  2. new VueLoaderPlugin(),
  3. new HtmlWebpackPlugin({
  4. title: "webpack-vue-template",
  5. template: "./public/index.html",
  6. }),
  7. + new webpack.DefinePlugin({
  8. + BASE_URL: JSON.stringify("./"),
  9. + }),
  10. ],

至此,我们第一步的工作就完成了,我们已经编写完 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

  1. // 引入这个插件,在最新版本中,它不在返回一个函数,而是返回一个对象,我们需要进行结构操作。
  2. const { merge } = require("webpack-merge");
  3. // 引入我们抽离出来的公共模块
  4. const common = require("./webpack.common");
  5. // 合拼 common ,并且编写新的内容,覆盖原基础文件
  6. module.exports = merge(common, {
  7. mode: "development", // 设置为 development 觅食
  8. devtool: "cheap-eval-module-source-map", // 开启source-map
  9. devServer: { // 启动服务器
  10. host: "localhost", // 主机地址
  11. port: "6060", // 端口
  12. open: true, // 是否自动打开浏览器
  13. contentBase: "public", // 告诉服务器从public目录提供静态文件
  14. },
  15. });

好了,编写完后,我们在命令行终端,输入 yarn webpack-dev-server --open --config webpack.dev.js 来启动我们的服务器。在这个启动服务器的时,我们可以校验,我们之前写的 loader 和 plugins 是否有问题。启动的过程需要稍等一会,因为 eslint 会去校验代码,如果我们的代码写的不规范,控制台会给出提示。

如果一切顺利,系统会打开默认浏览器并且帮我打开http://localhost:6060/,效果如下:

使用 webpack 实现一个简易版的 vue 项目打包 - 图3

接下来,我们来实现热更新的功能,实现热更新,需要配合 webpack 提供的一个 叫 HotModuleReplacementPlugin 的模块,我们需要在 devServer 中配置 hot 为 true 、并且引入 webpack,然后在 plugins 中实例化这个函数。代码如下:

  1. + const webpack = require("webpack");
  2. // 引入这个插件,在最新版本中,它不在返回一个函数,而是返回一个对象,我们需要进行结构操作。
  3. const { merge } = require("webpack-merge");
  4. // 引入我们抽离出来的公共模块
  5. const common = require("./webpack.common");
  6. // 合拼 common ,并且编写新的内容,覆盖原基础文件
  7. module.exports = merge(common, {
  8. mode: "development", // 设置为 development 觅食
  9. devtool: "cheap-eval-module-source-map", // 开启source-map
  10. devServer: {
  11. // 启动服务器
  12. host: "localhost", // 主机地址
  13. port: "6060", // 端口
  14. open: true, // 是否自动打开浏览器
  15. + hot: true, // 开启热更新
  16. contentBase: "public", // 告诉服务器从public目录提供静态文件
  17. },
  18. + plugins: [new webpack.HotModuleReplacementPlugin()],
  19. });

接着,我们重新启动服务,输入 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

  1. const path = require("path");
  2. // 引入这个插件,在最新版本中,它不在返回一个函数,而是返回一个对象,我们需要进行结构操作。
  3. const { merge } = require("webpack-merge");
  4. // 引入我们抽离出来的公共模块
  5. const commmon = require("./webpack.common");
  6. // 引入 clean-webpack-plugin,它的实例方法是个对象,我们需要结构操作。
  7. const { CleanWebpackPlugin } = require("clean-webpack-plugin");
  8. // 引入 copy-webpack-plugin
  9. const copyPlugin = require("copy-webpack-plugin");
  10. // 合拼 common ,并且编写新的内容,覆盖原基础文件
  11. module.exports = merge(commmon, {
  12. mode: "production", // 设置为production模式
  13. output: {
  14. filename: "bundle.js", // 打包过后的js文件名
  15. path: path.resolve(__dirname, "dist"), // 输出的目录
  16. },
  17. plugins: [new CleanWebpackPlugin(), new copyPlugin(["public"])],
  18. });

编写完毕过后,我们在终端输入 yarn webpack --config webpack.prod.js进行编译,稍等一会,编译完成后,我们在根目录下会出现一个 dist 目录,我们打开 dist 目录下的每个文件,来看看编译过后是什么样子的。我们打开到 bundle.js,我们可以看到代码已经被压缩了,我们可以启动一个服务器,来看看是否打包过后的内容是否可运行。

优化

至此,我们所有的任务都已经完成了。我们接下来,来优化一下,我们每当启动服务器或者打包的时候,都要输入一大串命令,闲得很麻烦,我们可以在 package.json 中写 script 脚本。代码如下:

package.json

  1. + "scripts": {
  2. + "serve": "webpack-dev-server --open --config webpack.dev.js",
  3. + "build": "webpack --config webpack.prod.js",
  4. + "lint": "eslint --ext .js,.vue src"
  5. + },

这样,我们就不需要输入这么多繁琐的命令了。

至此,所有的任务都已经完成了,有错误或者不明白的地方,可以在下面提出,欢迎纠正,谢谢~