解决 webpack 打包后 z-index 重新计算的问题

背景

与 PC 端共同开发一个页面,页面由 PC 端提供,内部 iframe 则由我们前端提供。开发时候遇到了一个问题,webpack 打包后 css 的 z-index 值与原始值不符,导致 iframe 里面的 toast 被外面 z-index 较小的 dialog 覆盖。更改 toast 的 z-index,发现没起作用,页面上的 z-index 依然是之前的值,而不是 css 中赋予的值。给 z-index 加上 !important 后依然无效,查资料发现是 OptimizeCssAssetsPlugin 调用 cssProcessor cssnano 对 z-index 进行了重新计算导致的。

这本来是 webpack 插件的一个善举(让 z-index 数值更加合理),但是具体情况来看,这里显然不需要这个 “善举”。

解决方案

解决方案按照网上的资料,可以在 OptimizeCssAssetsPlugin 插件中关掉 cssnano 对 z-index 的重新计算(cssnano 称为 rebase)。

代码语言:javascript
复制
new OptimizeCSSPlugin({
    cssProcessor: require('cssnano'),
    cssProcessorOptions: {
        discardComments: {removeAll: true},
        // 避免 cssnano 重新计算 z-index
        safe: true
    },
    canPrint: false
})

cssnano 将 z-index rebase 归类为 unsafe,只有在单个网页的 css 全部写入一个 css 文件,并且不通过 JavaScript 进行改动时是 safe。

参考: http://cssnano.co/optimisations/zindex/

cssnano 默认进行 z-index rebase。

unsafe (potential bug) 优化项默认不开启应该比较友好。

另外一个方案

以上是网上提供的方案,而且亲测有效,但是由于项目太大,因为其中一个小功能改了整个项目的 css 处理策略,难免有些担心会影响到其它页面。思考再三,决定不改 webpack 配置。

观察之前项目中使用的框架,在生成 dialog 或者 toast 的时候,即使在 webpack 插件对 css 进行处理之后,其 z-index 依然是很大的。

比如 element-ui 下 的 popup-manager.js 中首先设置 zIndex 为 2000,然后在 openModal 的时候动态添加 css 到 DOM 中,并且改变 zIndex 的值,而在浏览器中观察弹框的 z-index,果然是没有经过 cssnano rebase 的。

于是仿照 element-ui 的做法,把 z-index 相关的 css 用 js 动态插入到 DOM 中,就完美地解决了这个问题,并且没有对其它项目产生影响。

代码语言:javascript
复制
// 改变 toast 的 z-index
(function addToastStyle () {
    let nod = document.createElement('style')
    let str = `.mint-toast{z-index:2009;}`
    nod.type = 'text/css'
    nod.appendChild(document.createTextNode(str))
    document.getElementsByTagName('head')[0].appendChild(nod)
})()

总结

webpack 在对代码进行打包之前,会扫描所有的模块,建立模块之间的依赖树,而插件的运作时机也是相对于此时的静态代码,因此用 js 动态插入 css,webpack 显然不会知道要插入的 css 是什么样的,因此动态插入的 css 内容就不会经过插件的处理,也就避免了 OptimizeCssAssetsPlugin 的 “善举”。