项目webpack2.x迁移到4.x预研笔记

公司项目使用webpack2.x,搭建得比较早,没有用vue-cli(没啥影响),这是大背景。

本项目构建时主要的依赖包:

"dependencies": {
    "babel-core": "^6.26.0",
    "es6-promise": "^4.2.4",
    "eslint-config-vue": "^2.0.2",
    "vue": "^2.5.16",
    "vue-router": "^2.8.1",
    "webpack-cli": "^3.1.0",
    "webpack-dev-middleware": "^3.1.3",
    "webpack-hot-middleware": "^2.22.2"
  },
"devDependencies": {
    "babel-eslint": "^7.2.3",
    "babel-loader": "^7.1.4",
    "babel-polyfill": "^6.23.0",
    "babel-preset-es2015": "^6.22.0",
    "babel-preset-es2016": "^6.24.1",
    "babel-preset-stage-3": "^6.22.0",
    "copy-webpack-plugin": "^4.5.2",
    "css-loader": "^0.28.11",
    "ejs-loader": "^0.3.1",
    "eslint": "^3.18.0",
    "eslint-loader": "^2.0.0",
    "eslint-plugin-html": "^4.0.5",
    "eslint-plugin-vue": "^2.1.0",
    "extract-text-webpack-plugin": "^4.0.0-beta.0",
    "file-loader": "^1.1.11",
    "html-loader": "^1.0.0-alpha.0",
    "html-webpack-plugin": "^4.0.0-alpha",
    "less": "^3.0.4",
    "less-loader": "^4.1.0",
    "lodash": "^4.17.4",
    "node-sass": "^4.9.0",
    "sass-loader": "^6.0.7",
    "style-loader": "^0.21.0",
    "stylus": "^0.54.5",
    "stylus-loader": "^3.0.2",
    "url-loader": "^1.0.1",
    "vue-loader": "^15.2.6",
    "vue-template-compiler": "^2.5.16",
    "webpack": "^4.16.2",
    "webpack-dev-server": "^3.1.1"
  },

这些只是依赖包的其中一部分,每个点拿出来都可以是一个深入研究的问题,因此这里主要阐述一下各种包的主要逻辑与联系(其实就是各种坑)。

vue、vue-loader、vue-template-compiler :

先看vue-template-compiler的官方说明:

This package can be used to pre-compile Vue 2.0 templates into render functions to avoid runtime-compilation overhead and CSP restrictions.
You will only need it if you are writing build tools with very specific needs. In most cases you should be using vue-loader or vueify instead, both of which use this package internally.

一般情况下,vue-loader与vueify自身会在内部使用此包,只有在比较特殊的情况下才需要用到。单文件组件(.vue为扩展名的文件)就是这种情况的其中一种。

*在使用vue-template-compiler的时候,版本号要和vue版本号一致。

vue-router :

在这里把vue-router拿出来说,是因为在这里踩了一个坑。在这次预研任务里,主要目标就是理解webpack打包构建的原理和过程。在网上看了诸多笔记和教程,没有一篇提到过与vue-router发生冲突或者报错的。但是项目在升级webpack4后页面数据<router-view></router-view>的部分渲染不出来,报错如下:
项目webpack2.x迁移到4.x预研笔记
项目webpack2.x迁移到4.x预研笔记
在网上搜索了很多,以及去github看了issue,没有定向到问题所在。总结出大概方向应该是解析器版本没对上,然后vue-loader什么的,一个个版本都试过了也没有用。
最后把vue-router从2.5.x升级到最新2.8.1,立马解决。

extract-text-webpack-plugin与mini-css-extract-plugin :

extract-text-webpack-plugin主要是为了抽离css样式,防止将样式打包在js中引起页面样式加载错乱的现象。
webpack升4之后,extract-text-webpack-plugin的作者一度没有时间更新插件,这时比较多人推荐的是mini-css-extract-plugin,两者作用接近。
现在,extract-text-webpack-plugin的4.0beta版本已经出来了,与webpack4兼容性良好。因为项目用的是这个,所以继续沿用。

总结在升级依赖时有三个点:

1.更新工具/依赖的时候,第一步一定是尽可能把所有核心的依赖全部都升到最新,一般可以解决问题(其实已经做到)。
2.出现报错,首先考虑更新有直接关系的依赖,而不是回想自己之前的更新是否不对。(我就是一直在想是否vue-loader/html-webpack-plugin...等版本不吻合,卡了很久,其实就是vue-router没更新,当然这主要是自己的问题,被网上资料引导了,没看到任何相关问题,就一直没往这方面考虑)。
3.如果需要了解一个依赖包,第一选择是上github,筛选对自己有用的问题;其次再去搜索网上的资源。不建议在没有读懂原理的时候就直接套用网上的配置,即使一时生效,往后可能也是个大坑。

webpack4.x:SplitChunkPLugin是如何分包的?官方文档

直接采用了官方文档的example 1,如下:

splitChunks: {
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'initial',
          minChunks: 2
        }
      }
    }

然后打包(run build),运行成功,耗时从240s缩减为110s,主要的vendor.js体积也从540k减小到了440k左右。

run dev,可以正常运行,但是过不了多久,就会报内存溢出:

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

JavaScript堆内存不足,这里说的 JavaScript 其实就是 Node,Node 是基于V8引擎的,在一般的后端开发语言中,在基本的内存使用上没有什么限制,但是实际上,在 Node 中通过 JavaScript 使用内存时只能使用部分内存(64位系统下约为1.4 GB,32位系统下约为0.7 GB)所以当前端项目庞大的话,webpack 编译时就会占用很多的系统资源,如果超出了V8对 Node 默认的内存限制大小就会出现这个错误,那怎么解决呢?V8依然提供了选项让我们使用更多的内存。Node 在启动时可以传递 --max-old-space-size 或 --max-new-space-size 来调整内存大小的使用限制。

如果遇到 Node 无法分配足够内存给 JavaScript 的情况,可以用这个办法来放宽V8默认的内存限制,避免在执行过程中稍微多用了一些内存就轻易崩溃,既然知道了解决办法那就好办了,在 package.json 里,我们直接把上面v8提供的选项参数直接写入scripts 字段的 node 命令后就好了,示例如下:

"scripts": {
    "dev": "node --max_old_space_size=4096 build/dev-server.js",
    "build": "node --max_old_space_size=4096 build/build.js"
  }

直接在 node 后面写上 --max_old_space_size=4096 就好了,我这里设置的内存大小是4G,这个具体的大小可以根据自己的项目情况来设置就好了。然后再重新运行 npm run dev/build 就可以正常运行和打包了。

最后:打开F12,会发现一些页面子组件还没有被渲染的时候,js方法会被预先加载出来。这里初步推测与splitChunk的配置有关,今后有机会继续研究一下,会在这里续写。

相关推荐