从0搭建webpack5+vue3开发环境

343 阅读8分钟

前言

一直以来都在使用基于各种脚手架创建的项目进行开发,很多webpack的配置这些脚手架都已经帮我们封装好了。一般情况下,脚手架提供的能力已经足够我们使用了,有些不适用的地方看看文档改一下配置也能满足。最近一直挺好奇脚手架这个“黑盒”内部的webpack配置是怎么做的,于是就想自己搭一套相关的配置,学习一下。
文章不会过多介绍webpack相关的各种配置原理,只是简单记录整个过程,希望能对大家有所帮助。
话不多说,我们开始吧。

正式开始

在开始之前请确保我们已经安装了node和npm。推荐使用nvm来安装和管理node版本,本次我使用的node版本是14.20.0

一、初始化空项目

新建一个空白文件夹,然后执行yarn的初始化命令。

mkdir vue3
cd vue3
yarn init -y

执行完我们就得到了一个只有package.json文件的空项目。

image.png

二、安装webpack依赖

接下来我们就要安装webpack相关的依赖了。在终端执行

yarn add webpack webpack-cli -D

安装的时候发现速度非常慢,我们可以在项目下新增一个.npmrc文件,指定下npm镜像源

image.png 安装好以后,package.json文件的devDependencies下就会有我们刚刚安装好的依赖和对应的版本号

image.png

三、增加webpack配置文件,完善基本配置

在项目根目录下新建一个webpack.config.js文件,写一点基本的配置。

const path = require('path');

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js',
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'js/[name].[contenthash:8].js',
    }
}

在根目录下新建src文件夹,新建一个index.js文件,写点基本的代码

console.log('hello world');

package.json文件增加scripts字段,配置build命令

{
  "name": "vue3",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0"
  }
}

在终端执行yarn build,然后就能看到生成的打包好的文件啦

image.png 这个时候我们发现生成的文件里面有很多注释之类的代码,文件体积很大,我们需要修改webpack配置的modeproduction
这个mode我们在开发的时候希望是development,在打包的时候是production,我们可以在scripts里指定,这样就不需要每次手动修改了
修改package.json文件

"scripts": {
    "build": "NODE_ENV=production webpack"
}

然后,修改webpack.config.js文件

// ...

module.exports = {
    mode: process.env.NODE_ENV,
    // ...
}

再次执行yarn build,这个时候生成的代码就很干净了

image.png

四、使用clean-webpack-plugin,清理旧的打包文件

上一步最后,我们发现dist目录下有两个main.xxx.js文件,这是因为我们打包了两次,所以生成了两个。我们希望每次打包都自动删掉上次生成的文件,不要有额外的文件干扰我们。
要实现这个功能,我们只需要安装一个webpack的插件即可。
在终端下执行yarn add clean-webpack-plugin -D,然后在webpack.config.js文件里引入这个插件

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    // ...
    plugins: [
        new CleanWebpackPlugin(),
    ],
}

再次执行yarn build,这个时候我们发现dist目录下就只有刚刚打包好的这个文件了。

image.png

image.png

五、增加index.html入口文件

经过上面的步骤,我们已经可以打包基本的js文件了。接下来,我们需要新增一个index.html文件,让它引入我们的js文件,在浏览器里显示我们需要的内容。
在项目根目录下新增一个template目录,新增一个html文件。

image.png 安装html-webpack-plugin插件,它能帮助我们自动引入打包生成好的js、css等文件到html文件中。 在终端执行yarn add html-webpack-plugin -D,然后在webpack.config.js文件里引入它

// ...
const HTMLWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    // ...
    plugins: [
        new CleanWebpackPlugin(),
        new HTMLWebpackPlugin({
            template: './template/index.html',
        }),
    ],
}

再次执行yarn build,我们发现dist目录下多了个index.html文件,在浏览器打开它,发现控制台输出了hello world

image.png

image.png 注意:这里的在浏览器打开html文件需要以web server的方式打开,而不是以file的方式打开。以file的方式打开,使用的是文件协议,在浏览器里的url是以file开头的

image.png
如果是以web server的方式打开,浏览器里的url应该是以http(s)开头的。

六、使用webpack-dev-server进行本地调试

上面提到我们需要以http server的方式打开我们的html文件,为了更好的本地开发体验,我们可以使用webpack-dev-server来进行本地的开发调试。
打开终端,执行yarn add webpack-dev-server -D,安装所需依赖。
修改package.json文件,增加dev命令

"scripts": {
  "dev": "NODE_ENV=development webpack serve",
  "build": "NODE_ENV=production webpack"
}

我们可以修改下webpack-dev-server的配置,让它自动帮我们打开浏览器,并且增加source map的配置,提升本地开发体验。打开webpack.config.js文件,配置devtooldevServer

// ...

module.exports = {
    // ...
    devtool: 'eval-source-map',
    devServer: {
        open: true,
    }
}

执行yarn dev,在浏览器里自动打开了http://localhost:8080这个页面,打开控制台,我们发现他输出了hello world,这就是我们在index.js里写的代码

image.png

七、使用vue来编写前端代码

经过上面的几步,我们的项目已经有了一个基本的雏形了,可以本地开发,也可以打包。接下来我们就要使用vue来编写前端代码,实现各种炫酷的界面和功能了。
打开终端,依次执行yarn add vueyarn add vue-loader -D
src目录下新建一个app.vue文件,写一点基础的代码

<template>
    <div>hello vue</div>
</template>

<script>
export default {
  name: "app"
}
</script>

<style scoped>

</style>

index.js里引入vue来渲染这个组件。(别忘了在index.html里加上idroot的元素)

import { createApp } from 'vue';
import App from './app';

const app = createApp(App);

app.mount('#root');

然后,我们需要在webpack.config.js里增加module.rules来支持解析vue文件

// ...

module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader',
            }
        ],
    },
}

执行yarn dev,我们发现页面报错了

image.png 根据报错信息,我们发现是webpack没找到app.vue这个文件,这是因为我们没写文件的后缀,而webpack默认只会以.js .json .wasm这三个后缀来找,所以找不到app.vue这个文件。

image.png 要解决这个问题,我们需要修改下resolve.extensions的配置
打开webpack.config.js文件,新增resolve相关的配置

// ...

module.exports = {
    // ...
    resolve: {
        extensions: ['...', '.vue'],
    },
}

再次执行yarn dev,还是报错

image.png 这里报了两个错误,我们先看第二个错误,他的意思很明显,我们需要在webpack.config.js里引入VueLoaderPlugin,我们在vue-loader的官方文档查找VueLoaderPlugin这个关键词,可以找到相关的说明。因为我们的vue-loader版本高于14,所以我们要手动引入这个插件

image.png 按照官方文档的说明进行配置,修改完配置后,我们重新执行yarn dev,发现项目启动成功了。页面上显示出了hello vue,但是浏览器的控制台报了一个warning(第一个warning是我安装的浏览器插件导致的,可以忽略)

image.png 点进去这个链接,我们发现需要通过webpackDefinePlugin来注入这两个变量

image.png 修改webpack.config.js,增加DefinePlugin配置

// ...
const { DefinePlugin } = require('webpack');

module.exports = {
    // ...
    plugins: [
        // ...
        new DefinePlugin({
            __VUE_OPTIONS_API__: 'true',
            __VUE_PROD_DEVTOOLS__: 'false',
        }),
    ],
}

再次yarn dev启动项目,warning就消失啦

八、编写样式美化界面

前端的世界必然离不开css,我们的项目已经可以使用vue来编写界面了,接下来就要用css来美化它们了,让我们编写css的配置来实现一个五彩斑斓的黑的效果吧。
安装相关loader,在终端执行yarn add css-loader style-loader -D;修改webpack.config.jsmodule.rules

// ...

module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /.vue$/,
                loader: 'vue-loader',
            },
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader',
                ],
            },
        ],
    },
}

app.vue里随便写点样式

<template>
<div class="container">hello vue</div>
</template>

<script>
export default {
  name: "app"
}
</script>

<style scoped>
.container {
  color: #38f;
  font-size: 24px;
}
</style>

重新yarn dev,页面上就有了样式效果了。

image.png 一般情况下,我们都会用sass或者less这样的语言来写样式,这里以sass为例
执行yarn add sass sass-loader -D安装相应的依赖,再改下webpack.config.js配置 module.rules

// ...

module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /.vue$/,
                loader: 'vue-loader',
            },
            {
                test: /.s?css$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader',
                ],
            },
        ],
    },
}

我们把app.vuestyle标签的lang属性设置成scss

<style lang="scss" scoped>
.container {
  color: #38f;
  font-size: 24px;
}
</style>

重新yarn dev,页面显示正常,完美!

九、使用mini-css-extract-plugin抽离css代码

上一步我们成功完成了css相关的配置,我们使用了style-loader来帮助我们引入我们编写的样式,它会把我们的css代码以style标签的方式插入到index.html文件的head

image.png 当我们的代码越来越多的时候,所有的css代码全部在index.html里会让这个文件非常大,很臃肿。这个时候我们可以用mini-css-extract-plugin来帮我们把这些css代码抽离成单独的文件。
打开终端,执行yarn add mini-css-extract-plugin -D,修改webpack.config.js,用MiniCssExtractPlugin.loader替换掉style-loader

// ...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    // ...
    module: {
        rules: [
            // ...
            {
                test: /.s?css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'sass-loader',
                ],
            },
        ],
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: 'css/[name].[contenthash:8].css'
        }),
    ],
}

再次执行yarn dev,打开浏览器控制台,这个时候我们的css代码就是通过单独的css文件来引入的了

image.png

十、引入ant-design-vue组件库

一般我们都会引入一些业界成熟的组件库,具体使用哪一个就凭大家的喜好,这里我们来引入ant-design-vue,具体的引入方式大家参考官方文档,这里就不再赘述了。

十一、使用webpack asset module处理静态资源

除了代码以外,我们的项目里还会需要引入各种静态资源,例如图片、字体等,接下来我们就编写对应的配置来处理这类资源。
webpack v4版本我们通常需要借助file-loaderurl-loader来实现,而webpack v5已经内置了这些能力,不再需要借助第三方的loader了,具体可以查看官方文档

修改webpack.config.js

// ...

module.exports = {
  // ...
  module: {
    rules: [
      // ...
      {
        test: /.jpe?g|png|gif|webp$/,
        type: 'asset',
        generator: {
          filename: 'assets/image/[hash:8][ext]'
        }
      },
      {
        test: /\.eot|woff|woff2|ttf|svg|otf$/,
        type: 'asset',
        generator: {
          filename: 'assets/font/[hash:8][ext]',
        }
      },
    ]
  },
};

十二、编写splitChunks配置,合理拆分代码

经过上面的步骤,我们的项目已经具备基本的开发、调试、打包能力了,但是还有一点小小的问题可以优化一下。
打开浏览器的控制台,切换到network,我们可以发现,现在生成的产物只有一个js文件和一个css文件,而且这个js文件的体积非常大,这是因为我们项目里引用的依赖例如vueant-design-vue的代码也在这个文件里。

image.png 一般而言,我们引用的第三方依赖的版本都是不会经常变化的,而我们如果把第三方依赖和我们的业务代码生成到一个文件里,那么每次业务代码改变,生成的hash就变了,浏览器会重新加载整个文件,对这些第三方依赖而言,其实是被重复加载了。
所以,为了不重复加载这些依赖,提高网页性能,常见的做法是把它们单独拆出来,生成单独的文件,这样就能利用浏览器缓存的能力
为了实现上面的功能,我们只需修改webpacksplitChunks的配置即可

// ...

module.exports = {
  // ...
  optimization: {
    splitChunks: {
      chunks: 'all',
        cacheGroups: {
          vendor: {
            test: /[\/]node_modules[\/]/,
            // filename: 'chunks/vendor.js',
            reuseExistingChunk: true,
            name: (module) => {
              const regExp = /[\/]node_modules[\/](.*?)[\/]/g;
              if (/javascript/ig.test(module.type)) {
                const packageName = module.context.match(/[\/]node_modules[\/](.*?)([\/]|$)/)[1];
                return `npm.${packageName.replace('@', '')}`;
              }
              const identifier = module.identifier();
              const packageName = identifier.match(regExp).pop().replace(/node_modules|[\/]/ig, '');
              return `npm.${packageName.replace('@', '')}`;
            }
          }
        }
      }
    }
}

上面代码的大致意思就是对于node_modules下面的模块,我们会拿到它的包名,然后加上npm.的前缀,例如ant-design-vue,最后的名字就是npm.ant-design-vue.[contenthash:8].js这样的格式。通过这样的方式,我们就能把它们分离出来。想要进一步了解splitChunks的内容,大家可以参考官方文档
再次执行yarn dev,打开浏览器控制台的network部分,看看我们配置的效果吧

image.png

结尾

以上就是本文的全部内容了,其实还有很多可以完善的地方,例如eslintunit testcommitlintmock等,本次就先讲到这里,预知后事如何,请听下回分解~
喜欢的话,就点赞收藏吧~
大家觉得有不对的地方也欢迎在评论区留言~