16

Webpack5正式发布也有很长长长一段时间了,上手了一段时候后发现真香。webpack5的新特性使得我们在配置上比以往版本更加方便了,构建速度也有了质的飞跃。本文着重为大家讲解从 0 到 1 搭建 vue3 + webpack 5开发环境的过程中遇到的疑问。

项目地址: webpack5-vue3

demo地址: https://zhouyupeng.github.io/webpack5-vue3/#/

先看一下配置好的项目目录结构

├─build
│  ├─webpack.base.conf.js 
│  ├─webpack.dev.conf.js   
│  ├─webpack.prod.conf.js 
│  ├─webpack.rules.conf.js 
├─node_modules
├─public
|  |-index.html
└─src
|  ├─api
|  ├─assets
|  ├─components
|  ├─filters
|  ├─plugins
|  ├─router
|  ├─store
|  ├─style
|  ├─utils
|  ├─views
|  |-App.vue
|  |-main.ts
|-.env.dev
|-.env.test
|-.env.prod
|-.gitigore
|-babel.config.js
|-package.json
|-postcss.config.js

接下来,我们使用 Webpack5 从0搭建一个完整的 Vue3 的开发环境!

环境(environment)

webpack 5 运行于 Node.js v10.13.0+ 的版本。

本文涉及到依赖的版本号

├── webpack           5.43
├── webpack-cli       4.7.2
├── node              14.17.0

初始化目录

第一步: 创建目录并且初始化 package.json

mkdir webpack5-vue3 && cd webpack5-vue3
yarn init -y

第二步: 安装webpack三件套

yarn add webpack webpack-cli webpack-dev-server -D

注意:

  1. -D 等价于 --save-dev; 开发环境时所需依赖
  2. -S 等价于 --save; 生产环境时所需依赖

第三步:初始化目录和文件

  • 创建./build/webpack.config.js文件和./src/main.js文件并且写上webpack打包配置代码

    // webpack.config.js
    const path = require('path');
    module.exports = {
      entry: path.resolve(__dirname, '../src/main.js'), // 入口
      output: {
          path: path.resolve(__dirname, '../dist'),
          filename: './js/[name].[chunkhash].js',
          publicPath: './',
      },
    }
  • 在package.json的scripts里写上打包命令

    "build": "webpack --config ./build/webpack.config.js --mode production --progress --color -w"

    参数详解

  • --config或-c: 提供 webpack 配置文件的路径,例如 ./webpack.config.js
  • --mode:配置环境也可写在配置文件里 不配置mode 默认production模式打包
  • --progress: 启用在构建过程中打印编译进度
  • --color: 启用控制台颜色
  • --watch或-w: 监听文件变化

运行打包脚本yarn build看到webpack运行并且打包成功了。

配置开发服务器

webpack5 + webpack-cli4 启动开发服务器命令与之前有所变化,从 webpack-dev-server 转变为 webpack serve,

"build": "webpack serve --config ./build/webpack.config.js --mode development --progress --color",

运行起来后出现了另外的问题。改了代码控制台重新编译,但是热更新无效。

看一下无效的启动和有效启动之后的 network 截图对比:
gPONlR.png
gPxobq.png

网上找了一圈,说删除 package.json 里的 browserslist可以热更,截止我写这篇文章安装的依赖版本时不行,
最后在issues看到解决办法,按照官方给出的解释好像只会在webpack-dev-server@4.X中修复这个问题,这里安装beta最新版本yarn add webpack-dev-server@lastest -D,运行代码发现热更新成功。
安装webpack-dev-server 4.X版本后配置发生了很多改变,废弃了很多以前的配置
4.x配置点我点我或者看这里

分环境打包

在我们平时项目开发中,一般都会有:开发环境、测试环境、预发布环境和生产环境。现在来对 webpack 的配置文件进行环境拆分。

拆分文件
├─build
│  ├─webpack.base.conf.js   //公共配置
│  ├─webpack.dev.conf.js    //mode为development配置
│  ├─webpack.prod.conf.js   //mode为production配置
│  ├─webpack.rules.conf.js  //loader配置
配置环境变量

使用dotenv来按需加载不同的环境变量,VUE CLI3的环境变量也是使用的这个插件

  • 安装dotenv插件

    yarn add dotenv -D
  • 修改webpack.base.conf.js

    //...
    const envMode = process.env.envMode
    require('dotenv').config({ path: `.env.${envMode}` })
    // 正则匹配以 VUE_APP_ 开头的 变量
    const prefixRE = /^VUE_APP_/
    let env = {}
    // 只有 NODE_ENV,BASE_URL 和以 VUE_APP_ 开头的变量将通过 webpack.DefinePlugin 静态地嵌入到客户端侧的代码中
    for (const key in process.env) {
      if (key == 'NODE_ENV' || key == 'BASE_URL' || prefixRE.test(key)) {
          env[key] = JSON.stringify(process.env[key])
      }
    }
    
    plugins: [
      //...
      new webpack.DefinePlugin({ // 定义环境变量
          'process.env': {
              ...env
          }
      })
    ]
    
  • 修改package.json

    "scripts": {
    "dev": "cross-env envMode=dev webpack serve --config ./build/webpack.dev.conf.js  --color",
    "build": "cross-env envMode=prod webpack --config build/webpack.prod.conf.js  --color",
    "build:test": "cross-env envMode=test webpack --config build/webpack.prod.conf.js  --color"
    },
  • 配置变量文件
.env.dev
.env.test
.env.prod

配置完成后可以像VUE CLI3一样使用环境变量了点我点我

配置核心功能

将 ES6+ 转 ES5

由于有些浏览器无法解析 ES6+ 等高级语法,故需要将其转化为浏览器能够解析的低版本语法

yarn add @babel/core @babel/preset-env babel-loader -D
yarn add core-js -S

loader配置

// webpack.rules.conf.js
rules: [
    {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
            loader: 'babel-loader',
        }
    }, 
]

Babel 配置文件
Babel的配置文件是Babel执行时默认会在当前目录寻找的文件,主要有.babelrc,.babelrc.js,babel.config.js和package.json。它们的配置项都是相同,作用也是一样的,只需要选择其中一种。
推荐使用后缀名是js配置文件,因为可以使用js做一些逻辑处理,适用性更强。

// babel.config.js
const presets = [
    ["@babel/preset-env", {
        "useBuiltIns": 'usage', // 这里配置usage 会自动根据你使用的方法以及你配置的浏览器支持版本引入对于的方法。
        "corejs": "3.11.0" // 指定 corejs 版本 
    }]
]
const plugins = [
]
module.exports = {
    plugins,
    presets

}
产出HTML

安装 html-webpack-plugin 插件处理 index.html 文件,此插件的功能是根据提供的模板文件,自动生成正确的项目入口文件,并把 webpack 打包的 js 文件自动插入其中

yarn add html-webpack-plugin -D

plugins配置

webpack.base.conf.js
new HtmlWebpackPlugin({
    template: path.resolve(__dirname, '../public/index.html'),
    filename: 'index.html',
    title: 'webpack5+vue3',
    minify: {
        html5: true, // 根据HTML5规范解析输入
        collapseWhitespace: true, // 折叠空白区域
        preserveLineBreaks: false,
        minifyCSS: true, // 压缩文内css
        minifyJS: true, // 压缩文内js
        removeComments: false // 移除注释
    },
    files: prodMode ? cdn.prod : cdn.dev //CDN引入文件配置
}),

这里的index.html源文件放在../public/文件夹里。
注意: 配置动态网页标题时,需将模板中的 <title> 标签里的内容改成 <%= htmlWebpackPlugin.options.title %>
CDN引入js

<% for (var i in
htmlWebpackPlugin.options.files&&htmlWebpackPlugin.options.files.js) { %>
<script src="<%= htmlWebpackPlugin.options.files.js[i] %>"></script>
<% } %>
添加 css 和 sass 支持
yarn add style-loader css-loader -D
yarn add node-sass sass-loader -D
yarn add autoprefixer postcss-loader -D 

loader配置

//webpack.rules.conf.js
{
    test: /\.(css|scss|sass)$/,
    use: [
        'style-loader',
        'css-loader',
        'postcss-loader',
        'sass-loader'
    ]
}

loader 从右到左(或从下到上)地取值(evaluate)/执行(execute)。在上面的示例中,从 sass-loader 开始执行,最后以 style-loader 为结束。

配置 alias 别名

创建 import 或 require 的别名,来确保模块引入变得更简单

// webpack.base.conf.js
resolve: {
    alias: {
        "@": path.resolve(__dirname, "../src"),
        assets: path.resolve(__dirname, '../src/assets/'),
        img: path.resolve(__dirname, '../src/assets/img'),
        utils: path.resolve(__dirname, '../src/utils'),
        api: path.resolve(__dirname, '../src/api'),
    },
},
处理图片等静态资源

Webpack5 之前我们处理静态资源比如PNG 图片、SVG 图标等等,需要用到url-loader,file-loader,raw-loader。Webpack5 提供了内置的静态资源构建能力,我们不需要安装额外的 loader,仅需要简单的配置就能实现静态资源的打包和分目录存放。这三个loader在github上也停止了更新。

webpack5使用四种新增的资源模块(Asset Modules)替代了这些loader的功能。

asset/resource 将资源分割为单独的文件,并导出url,就是之前的 file-loader的功能.
asset/inline 将资源导出为dataURL(url(data:))的形式,之前的 url-loader的功能.
asset/source 将资源导出为源码(source code). 之前的 raw-loader 功能.
asset 自动选择导出为单独文件或者 dataURL形式(默认为8KB). 之前有url-loader设置asset size limit 限制实现。

//webpack.rules.conf.js
{
    test: /\.(png|jpg|svg|gif)$/,
    type: 'asset/resource',
    generator: {
        // [ext]前面自带"."
        filename: 'assets/[hash:8].[name][ext]',
    },
}

更多配置点我点我

打包时清除上次构建dist目录

webpack5.20以下版本清除dist文件内容一般使用插件 clean-webpack-plugin, 5.20版本以后output新增特性clean,用于清除dist文件

//webpack.prod.conf.js
module.exports = {
  //...
  output: {
    clean: true, // Clean the output directory before emit.
  },
};

更多配置点我点我

静态资源输出到根目录
yarn add copy-webpack-plugin -D

当某些文件不需要经过webpack打包处理而直接使用,这里我们可以使用 copy-webpack-plugin 这个插件,在构建的时候,将 public/ 的静态资源直接复制到 dist根目录下

//webpack.prod.conf.js
new copyWebpackPlugin({
    patterns: [{
        from: path.resolve(__dirname, "../public"),
        to: './',
        globOptions: {
            dot: true,
            gitignore: true,
            ignore: ["**/index.html*"],
        }
    }]
}),
提取样式文件

本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。

yarn add mini-css-extract-plugin -D
//webpack.prod.conf.js
plugins: [
//...
    new miniCssExtractPlugin({
        filename: './css/[name].[contenthash].css',
        chunkFilename: './css/[id].[contenthash].css',
    })
],

修改webpack.rules.conf.js

{
    test: /\.(css|scss|sass)$/,
    use: [
        !prodMode ? 'style-loader'
            : {
                loader: MiniCssExtractPlugin.loader,
                options: {
                    publicPath: '../',
                },
            },
        'css-loader',
        'postcss-loader',
        'sass-loader',
    ],
},

识别 .vue文件

yarn add vue-loader@next @vue/compiler-sfc -D
yarn add vue@next -S

注意:

  • vue-loader:它是基于 webpack 的一个的 loader 插件,解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,vue 3.x需要安装vue-loader@next。
  • @vue/compiler-sfc: Vue 2.x 时代,需要 vue-template-compiler 插件处理 .vue 内容为 ast , Vue 3.x 则变成 @vue/compiler-sfc 。

修改webpack配置

// webpack.rules.conf.js
rules: [
    {
        test: /\.vue$/,
        use: [
            'vue-loader'
        ]
    }
]

//webpack.base.conf.js
const { VueLoaderPlugin } = require('vue-loader/dist/index');
plugins: [
    new VueLoaderPlugin()
]

添加App.vue

<template>
    <div>
        <p class="title">{{title}}</p>
    </div>
</template>
<script>
import { defineComponent, ref } from 'vue';
export default defineComponent({
    setup() {
        const title = ref('渐进式JavaScript 框架');

        return {
            title
        }
    }
})
</script>
<style scoped>
.title{
    color: #000;
}
</style>

修改main.js

import { createApp } from 'vue' // Vue 3.x 引入 vue 的形式
import App from './App.vue' // 引入 APP 页面组建
 
const app = createApp(App) // 通过 createApp 初始化 app
app.mount('#root') // 将页面挂载到 root 节点

跑一遍代码,运行成功。

安装Vue全家桶

 yarn add vue-router@4 vuex@next axios -S

vw适配

yarn add postcss-loader postcss-px-to-viewport -D

新建postcss.config.js文件

'postcss-px-to-viewport': {
    unitToConvert: 'px', // 需要转换的单位,默认为"px"
    viewportWidth: 750, //  设计稿的视口宽度
    unitPrecision: 5, // 单位转换后保留的精度
    propList: ['*'], // 能转化为vw的属性列表
    viewportUnit: 'vw', //  希望使用的视口单位
    fontViewportUnit: 'vw', // 字体使用的视口单位
    selectorBlackList: ['.ignore', '.hairlines', '.ig-'], // 需要忽略的CSS选择器
    minPixelValue: 1, // 最小的转换数值,如果为1的话,只有大于1的值会被转换
    mediaQuery: false, // 媒体查询里的单位是否需要转换单位
    replace: true, // 是否直接更换属性值,而不添加备用属性
    include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换,例如只转换 'src/mobile' 下的文件 (include: /\/src\/mobile\//)
    landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
    landscapeUnit: 'vw', // 横屏时使用的单位
    landscapeWidth: 568 // 横屏时使用的视口宽度
}

去掉生产环境console.log

使用TerserWebpackPlugin来进行去除console.log,压缩JS,webpack5之后自带最新的terser-webpack-plugin,无需再重新安装原文点我点我

//webpack.prod.conf.js
minimizer: [
    new TerserPlugin({
        // 多进程
        parallel: true,
        //删除注释
        extractComments: false,
        terserOptions: {
            compress: { // 生产环境去除console
                drop_console: true,
                drop_debugger: true,
            },
        },
    })
],

编译缓存

Webpack5 内置 FileSystem Cache 能力加速二次构建,可以通过以下配置来实现

cache: {
    type: 'filesystem',
    // 可选配置
    buildDependencies: {
        config: [__filename],  // 当构建依赖的config文件(通过 require 依赖)内容发生变化时,缓存失效
    },
    name: '',  // 配置以name为隔离,创建不同的缓存文件,如生成PC或mobile不同的配置缓存
    ...,
},

配置好后第二次构建速度快的飞起。
注意事项:

  • cache 的属性 type 会在开发模式下被默认设置成 memory,而且在生产模式中被禁用,所以如果想要在生产打包时使用缓存需要显式的设置。
  • 为了防止缓存过于固定,导致更改构建配置无感知,依然使用旧的缓存,默认情况下,每次修改构建配置文件都会导致重新开始缓存。当然也可以自己主动设置 version 来控制缓存的更新。

集成 Vant

yarn add vant@next -S
  • 按需引入

    yarn add babel-plugin-import -D
  • 修改配置

    // babel.config.js
    const plugins = [
      ['import', {
          libraryName: 'vant',
          libraryDirectory: 'es',
          style: true
      }, 'vant']
    ]
  • vant 适配vw

修改postcss.config.js

const path = require('path');
module.exports = ({file}) => {
    const designWidth = file.includes(path.join('node_modules', 'vant')) ? 375 : 750;
    return {
        plugins: {
            'postcss-px-to-viewport': {
                unitToConvert: 'px', // 需要转换的单位,默认为"px"
                viewportWidth: designWidth, //  设计稿的视口宽度
                unitPrecision: 5, // 单位转换后保留的精度
                propList: ['*'], // 能转化为vw的属性列表
                viewportUnit: 'vw', //  希望使用的视口单位
                fontViewportUnit: 'vw', // 字体使用的视口单位
                selectorBlackList: ['.ignore', '.hairlines', '.ig-'], // 需要忽略的CSS选择器
                minPixelValue: 1, // 最小的转换数值,如果为1的话,只有大于1的值会被转换
                mediaQuery: false, // 媒体查询里的单位是否需要转换单位
                replace: true, // 是否直接更换属性值,而不添加备用属性
                include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换,例如只转换 'src/mobile' 下的文件 (include: /\/src\/mobile\//)
                landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
                landscapeUnit: 'vw', // 横屏时使用的单位
                landscapeWidth: 568 // 横屏时使用的视口宽度
            }

        }
    }
}

wepback的可视化资源分析插件

yarn add webpack-bundle-analyzer -D

用来分析哪些模块引入了哪些代码,进行有目的性的优化代码
在打包脚本后面加--analyzeyarn build --analyze,无需另外配置。新版本webpack-cli已经支持了。原文

最后

不知道大家看完这篇文章,学废了吗。文章中若是有错误或者不准确的地方,欢迎大家指出讨论。


zhou_web
1.6k 声望1.1k 粉丝