苦逼前端

webpack4 抽离公共css产生的问题

Javascript2018-11-16 14:13

webpack4对css的默认处理是同步引用的[s]css合并成一个文件,其他异步引用的分别单独打成独立文件,
但是在[s]css文件中使用@import引用的公共模块并不会被抽离出来合并到同步引用的[s]css合并成的文件中,这导致引用了大量的重复代码,可以看看sass-loader其中一个issue对此问题的讨论
并且我们希望同步引用的[s]css文件最终打包成两个css,一个是通用的common.css,另一个是业务入口相关的page.css, 这样会使common.css中的代码脱离业务,缓存更加长期、稳定

所以我们考虑实现这么一个webpack plugin: 首先解决使用css entry带来的问题, 然后将其他所有打包出来的css文件(page.css和异步css)用css entry打包出来的文件(common.css)全文替换

然后我们遇到了以下问题:

问题一

  • sass-loader默认的配置options.outputStyle = 'nested'会将:
body{
    background: green;
}

变成:

body{
    background: green; }

所以我们要把它设置成options.outputStyle = 'expanded'来保持原状来解决如下场景:

  1. a.scss里面@import ./common.css
  2. sass-loader会将./common.css拿到a.scss里面
  3. 然后包含./common.cssa.scss经过sass-loader处理变成了}上移一行的状态
  4. ./common.css未经sass-loader处理维持原状
  5. 从编译后的a.scss(包含了./common.css)里面不能搜索到./common.css的内容,导致删除重复部分失败
    注意:即使设置成options.outputStyle = 'expanded',sass-loader仍然会对源文件进行修改,比如有多个空行(非注释中)会给你变成一个,还有一些代码优化,小数点补0等,所以终极办法是让css也走sass-loader

问题二

vue单文件引用的common.scss文件中的空行被删除以及嵌套结构的缩进被改写:

html{
    background: red;
}

body{
    background: black;
}

@-webkit-keyframes bounce {
    from,
    to {
        transform: translateY(3rem) scaleY(0.98);
    }
    80% {
        transform: translateY(2rem) scaleY(1.02);
    }
}

变成:

html{
    background: red;
}
body{
    background: black;
}
@-webkit-keyframes bounce {
from,
  to {
    transform: translateY(3rem) scaleY(0.98);
}
80% {
    transform: translateY(2rem) scaleY(1.02);
}
}

我们发现,html、body、@-webkit-keyframes bounce中间的空行被删除同时嵌套结构缩进被改写,而我们的common.scss并未被做如上改动,导致公共内容匹配不成功
猜测是vue-loader做的如上处理,经过查看源码发现vue-loader/lib/loaders/stylePostLoader.js中有如下代码:

//vue-loader@15.4.2
//vue-loader/lib/loaders/stylePostLoader.js

const { code, map, errors } = compileStyle({
    source,
    filename: this.resourcePath,
    id: `data-v-${query.id}`,
    map: inMap,
    scoped: !!query.scoped,
    trim: true
});

我们把trim: true改为trim: false果然就不会做上述处理了。然而直接修改vue-loader显然是不可行方案,我们只能继续查看该配置项的内容,通过使用相同的配置来处理我们的common.scss,这样就可以在编译完成阶段匹配到公共的样式代码块。

继续追查代码发现,这个compileStyle方法是由@vue/component-compiler-utils提供,而最终是通过postcss实现的该trim功能,所以我们给scss文件也配置postcss的trim功能:

{
    loader: postcssLoader,
    options: {
        plugins: () => [
            require('autoprefixer')({
                ...browserList
            }),
            //use same trim as vue-loader's to split common css
            css => {
                css.walk(({ type, raws }) => {
                    if (type === 'rule' || type === 'atrule') {
                        raws.before = raws.after = '\n';
                    }
                });
            }
        ]
    }
}

至此,已经可以完美从其他编译后的css代码中剔除common.scss中的代码块了

相关库的版本:
webpack: 4.20.2
sass-loader: 7.1.0

评论(0)
  • 暂无评论,求挽尊...
还可输入200个字