webpack 多entry的坑和解决方案

更新内容的时候已经到了2020.03.06,当时实际问题产生和解决大概实在2018年,如今webpack已经到了5的版本,既然以前开过头还是梳理下内容,因为新冠肺炎在家呆着,没远程办公就在家看看整理下

多entry介绍

多entry只是相对于单页应用来说,常规的网站每个页面都是一个entry,单页和多页各有好坏,具体的看自己的场景适合哪种。
个人偏向多entry + entry内的单页应用,按大模块划分为一个个entry,entry内再做单页应用,当然小项目就不必要这么做…

过程中遇到的问题比下面这些要多的多,主要记录几个印象深刻的

预发布和生产等不同环境因为cdn地址不同需要重复构建的问题

项目有很多套运行环境,比如 开发环境、测试环境、预发布环境 。每一套环境cdn地址是不同的,所以导致不同环境都需要重新构建。

前期方案:

大致三个步骤,处理之后可以解决资源路径在不同环境下的正常引用。

  1. 动态配置 __webpack_public_path__ 可以解决js文件中导入的资源文件路径;
    1
    2
    3
    4
    5
    6
    7
    // publicPath.js 读取全局变量 
    __webpack_public_path__ = `${window.CDN_PATH || ''}/`; // eslint-disable-line

    // entry, 为每一个entry注入该文件
    {
    index: ['publicPath.js', 'index.js']
    }
  1. ExtractText提取的css文件中的路径通过配置ExtractTextPlugin styleLoader publicPath = ‘../‘ 相对路径
    1
    2
    3
    4
    5
    ExtractTextPlugin.extract({
    use: loaders,
    fallback: 'vue-style-loader',
    publicPath: '../' // css内资源变更为相对路径
    });
  1. 调整entry的key为一层路径转化文件名中的/保持文件路径都是平级,结合第二条相对路径,即可保证css正确加载图片等资源文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    由于entry文件路径存在多级路径
    所以先转换entry名为平级,如 index/user.js
    转换entry key为 index/user 到 index_user.

    // 保证打包后文件结构:
    js
    1.js
    index_user.js
    css
    1.css
    2.css
    index_user.css
    img
    xx.png
    xx.jpg

升级支持css内联打包后再次出现问题:
内联后内联css文件中的路径是相对的html文件,而原本的css相对路径都是基于css文件路径,再次导致css图片、字体等404

最终方案:

  • 不同环境依旧使用不同的publicPath配置多次构建。
  • 如果不需要内联可以采用方案一。
  • 采用发布系统做路径替换也可以解决。
  • 或者测试自动化发布流程接入之后就无所谓构建两次。

这个问题是在前期项目发布工程不够完善的情况下,不同环境测试都需要研发来协助编辑,费事费力,后面自动化之后这些问题也可以忽略不计。

ExtractText 导致 style-loader丢失的问题

ExtractText 提取css后导致styleLoader模块丢失,最终需要异步插入style标签地方,会call不到模块,无法加载异步样式

参考:
https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/456
https://github.com/lavas-project/lavas/issues/85

解决方案如下两种:

  1. 通过配置 ExtractTextPlugin allChunks: true ,提取所有css,就不会再需要styleLoader来动态插入 style 标签了。
  2. 在entry文件手动导入 style-loader/lib/addStyles 和 css-loader/lib/css-base,Vue项目也可以导入 vue-style-loader/lib/addStylesClient 和 css-loader/lib/css-base;

ExtractText 多entry无法按需加载css的问题

问题:
多entry下,extractText 提取css无法按需加载,allChunks 只能配置 true。

官方文档要求在使用了CommonsChunkPlugin插件之后只能配置allChunks为true,实际CommonsChunkPlugin提取资源也是产生了新的entry,而我们的实际开发场景必然会需要按需的操作。

不支持原因:
模块 moduleA 包含css,同时被entry1同步导入,entry2异步导入。如下图:
测试图片

extractText 使用的提取规则:
如果模块被同步导入过就提取,这样就会导致css被提取到entry1.css, entry2 在异步导入的时候丢失了 a.css,这也是官方要求多entry必须allChunks的原因。

解决方案:

修改插件源码调整提取规则只要被异步导入过就不提取,这样也会有问题, entry1的css会变成js执行插入,如果是关键首屏css样式,会导致一次明显的页面闪动。
最终通过 Async.css 中@import ‘a.css’,postcss 处理的时候做内容复制,属于不同module,做到维护一份源码

webpack4 mini-css-extract
在webpack4的时代 mini-css-extract 插件已经解决了这个问题,会在最后对每个同步和异步chunk提取出css文件,也就是官方说的 异步加载,不重复编译,性能更好
个人觉得如果可以针对异步的chunk配置是否提取出css文件会更好,可以让每个异步模块少一个css请求,当然随着http2通道复用,多个小文件的下载也不见得更慢。

顺便说下 CommonsChunkPlugin 配置需要顺序的问题

参考:
https://stackoverflow.com/questions/35184240/webpack-error-in-commonschunkplugin-while-running-in-normal-mode-its-not-allow/45424072#45424072

最后

在webpack3时代 ExtractText、CommonsChunkPlugin 都是相当的难以使用,CommonsChunkPlugin 会发现不管如何配置都很难控制到自己想要的精细程度,缺少提取优先级也有些难以理解,现在新的版本部分问题已经不复存在或者有了更好的解决方案…