webpack3.x升级4.x时遇到的问题

背景

vue-cli3 发布之前,使用 vue-cli2 构建的 vue 项目是基于 webpack3.x 的,伴随着项目的版本迭代,功能逐渐增多,文件也逐渐增多,打包时间从最初的 5 分钟,最久的时候 19 分半钟。

因为使用 ts 的缘故,每次打包 ts-loader 需要进行类型检查,导致耗时剧增。权衡利弊下,将 ts-loader 的类型检查禁用后(transpileOnly 选项设置为 true),打包时间锐减到 2 分半钟。

随着时间的推移,项目进一步迭代,打包时间再次高涨到 9 分钟,恰逢 webpack4 对于打包性能的提升,于是就有了这篇升级日志:

起步

整体思路:

  1. 升级项目依赖
  2. 跑通开发环境打包
  3. 跑通生成环境打包

升级项目依赖

一开始的项目依赖升级的思路是,先升级 webpack 到最新的版本,然后逐步升级 loaderpluginbabel 等依赖。但是基于之前其他项目升级 webpack4 的过程,发现逐步升级会耗费一部分时间在新旧依赖的问题上,于是决定激进一点,使用 ncu 一次性将 package.json 中包含的项目依赖升级到最新版本(部分依赖可以不升级)。

# 安装 ncu
npm i -g ncu

# 检查项目依赖的版本与最新版本,列出清单
ncu

# 将 `package.json` 中包含的依赖的版本号,更新到最新
ncu -u

# 删除旧的依赖,安装新的依赖
rm -rf node_modules
rm package-lock.json
npm i

跑通开发环境打包

npm run dev

运行过程中遇到的问题:

  1. vue-loader was used without the corresponding plugin.

    vue-loader 如何从 v14 迁移至 v15

    报错日志:

    ERROR in ./src/App.vue
    Module Error (from ./node_modules/vue-loader/lib/index.js):
    vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
    @ ./src/main.ts 2:0-28 45:23-26
    @ multi ./build/dev-client ./src/main.ts
    ERROR in ./src/page/supplier/preferential/full-discount/view.vue?vue&type=script&lang=ts&
    Module not found: Error: Can't resolve '../../../../interface/coupon.coupon_code_view' in '/Users/yourProjectPath/src/page/supplier/preferential/full-discount'
    @ ./src/page/supplier/preferential/full-discount/view.vue?vue&type=script&lang=ts& 19:0-70
    @ ./src/page/supplier/preferential/full-discount/view.vue
    @ ./src/router/supplier/preferential.ts
    @ ./src/router/supplier/index.ts
    @ ./src/router/index.ts
    @ ./src/main.ts
    @ multi ./build/dev-client ./src/main.ts

    原因:

    vue-loader 升级到 v15.x 以后,需要搭配 VueLoaderPlugin 使用

    如何解决:

    // webpack.base.confg.js
    const { VueLoaderPlugin } = require('vue-loader');
    
    module.exports = {
        // ...
        plugins: [
            new VueLoaderPlugin()
        ]
    }
  2. Component template requires a root element, rather than just text.

    Module Error (from ./node_modules/vue-loader/lib/loaders/templateLoader.js):
    (Emitted value instead of an instance of Error) 
    
    Errors compiling template:
    
    Component template requires a root element, rather than just text.
    
    1   |  
        |   
    2   |  #app
        |  ^^^^
    3   |    router-view
        |  ^^^^^^^^^^^^^
@ ./src/App.vue?vue&type=template&id=7ba5bd90&lang=pug& 1:0-238 1:0-238
@ ./src/App.vue
@ ./src/main.ts
@ multi ./build/dev-client ./src/main.ts
```

```log
Module Error (from ./node_modules/vue-loader/lib/loaders/templateLoader.js):
(Emitted value instead of an instance of Error) 

Errors compiling template:

text "div
el-card" outside root element will be ignored.
```

**原因:**

引用自 `vue-loader` 文档:

> 注意有些模板的 loader 会导出一个编译好的模板函数而不是普通的 HTML,诸如 pug-loader。为了向 Vue 的模板编译器传递正确的内容,你必须换用一个输出普通 HTML 的 loader。例如,为了支持 `