事情缘由
近段时间在做基于scratch3.0的改造项目。基于scratch-gui改造,项目本身已经很大了,然后里面还要用到scratch-blocks,scratch-vm,scratch-render等外部第三方项目。官方的配置是所有的东西打入一个lib中,所有的html都使用这一个lib。
现在有一个需求是:h5页面仅仅展示scratch做出来的作品,但是目前加载很慢,需要优化。
scratch原生的打包配置如下([email protected])
打包结果全部js在lib.min中,有26M左右
优化思路
第一阶段(不更改代码,仅仅做分包的优化):
- 利用webpack optimization.splitChunks的vendors配置将所有的第三方包提取到vendors中【本人额外配置了一个所有入口都使用的第三方包bundle:‘vender.min'】;
- 利用webpack optimization.splitChunks的default配置【默认的配置】自动提取各个入口js的共用代码组成bundle的功能。
- 分离出manifest文件,确保没有更改的包打包结果不会更改。
上面基本都利用了默认的配置【不配置的属性即使用默认值】,webpack默认的配置如下
module.exports = { //... optimization: { splitChunks: { chunks: 'async', minSize: 30000, maxSize: 0, minChunks: 1, maxAsyncRequests: 6, maxInitialRequests: 4, automaticNameDelimiter: '~', automaticNameMaxLength: 30, cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true } } } } };
优化配置如下,
打包结果如下:chunks中
其中vendors*.js 和 blocksonly~compatibilitytesting~gui~player.js即是默认的vendors和default配置提取出来的bundle。
是小了一点,但是项目还是太大了,特别是vendor.min.js。查看包发现工程引用的第三方模块中用到很多相同的模块
解决办法:使用别名
resolve: { symlinks: false, extensions: ['.js', '.jsx', '.json'], alias: { // 别名,防止node_modules多个地方引入同样的包会打多份 'scratch-l10n': path.resolve(__dirname, './node_modules/scratch-l10n'), 'scratch-blocks': path.resolve(__dirname, './node_modules/scratch-blocks'), 'scratch-render': path.resolve(__dirname, './node_modules/scratch-render'), 'scratch-svg-renderer': path.resolve(__dirname, './node_modules/scratch-svg-renderer'), 'scratch-audio': path.resolve(__dirname, './node_modules/scratch-audio'), 'immutable': path.resolve(__dirname, './node_modules/immutable') } },
打包后结果
小了3M。到目前为止,如果不更改代码基本已经无法再压缩了。
第二阶段:更改代码,一切与展示作品无关的东西都剥离后打包
- 配置了公用vendors,避免默认的vendors(默认情况是不用更改的,本人的项目比较特殊,原因看后面的描述)
- player代码和gui的代码分开,去掉player中不必要的代码引入
这一阶段player的代码进行精简,不和blocksonly / compatibilitytesting / gui公用一套代码。项目后面加上了一些hash.
由于项目中需要对scratch-blocks做更改,将scratch-blocks作为项目的git子模块。
不配置vendors则和默认配置等同,等同于如下代码
vendors配置如下:
配置没有配置vendors和配置了vendors的对比(后面的就是配置了vendors)
没有配置vendors,发现子模块scratch-blocks在多处打包,没有被提取出来。原因是我把scratch-blocks作为项目的子模块,不再是node_modules中的第三方模块。使用了别名配置
'scratch-blocks': path.resolve(__dirname, './scratch-blocks'),
默认的default配置貌似没法提取出来,可能原因是这个子模块的引用不像别的代码直接通过路径能找到,是通过scratch-blocks的package.json的main指定的【这个后面好好研究一下???】。
为了能提取这个scratch-blocks,本人便修改了vendors。去掉了只匹配“node_moddules”;只要三个以上chunks用到的模块都提取到vendors。
配置vendors打包后的结果可以看到,在网页端访问player页面,只需要引入三个js
vendor.min.938f6588ea10bb385ceb.js 7844K
vendors~blocksonly~compatibilitytesting~gui~player.b694ee6564cf5e22ed72.js 2998K
player.cb540c9b28aafd8d9503.js 95K
一共就10M多一点,比起之前要加载26M要好很多了。
这里面有一个坑:按照webpack默认的default和vendors配置会自动提取公用代码生成新bundle,然后自动被html-webpack-plugin配置的html引用。但是,3.X的html-webpack-plugin如果不指定chunk,且在HtmlWebpackPlugin中显式配置chunk的名称,则不起作用。
比如:
上面的vendors~blocksonly~***.js文件生成了,但是没有被引入到index.html代码中,导致访问index.html缺少这个文件直接展示不出来。
3.x的配置例子如下
一旦指定splitChunks.name的名称,那么所有的入口必然引用这个bundle,无法生成各自入口的个性化bundle。所以建议升级html-webpack-plugin到4
纯属个人经验,有错误请大家多指正!