项目技术栈
React + Webpack4
浏览器兼容性:较高版本的FireFox,Chrome以及IE9+(注意:坑从此中来)
问题描述
项目在IE9中很多Css Style无法正确的显示,而在其他浏览器中无此类问题。
最好的解决方案当然是径自宣布不支持IE9,其实这是合理的,微软自己都不再维护IE系列,如果死抱着旧的浏览器不放,意味着安全得不到保障并且用户也无法享受到现代浏览器新特性带来的丝滑快感。从前端开发者角度来说,现代前端框架都是建立在较现代浏览器基础至上的,比如Vue,一诞生就宣布不支持IE8及以下版本。再加上IE系列特别是IE10以下浏览器的市场占有率,也实在没必要去费力不讨好。
但是用户公司大量的使用着IE9,不论是React还是我们用到的第三方控件库都是宣称支持IE9+的,如果项目不支持也说不过去。
那么废话不多说了,继续谈谈解决问题的思路。
问题原因
其他浏览器是好的,但是IE显示有问题。这是前端界面中的经典句式,听到这种描述的第一反应就是赶紧看看坑爹的IE浏览器在支持哪个特定css特性上出了幺蛾子。想当年我误入前端界,掉的一个坑就是IE的。
请参阅我的前作:IE9 中行内元素word-wrap失效分析
然而这次却是截然不同,通过与chrome浏览器中样式的对比,我发现IE9下,对应样式根本就没有加载进来!
一个很自然的推断就是会不会css文件因为某种原因比如网络问题没有下载完全,对比过服务器和IE端css文件发现是一样的,并没有丢失内容的现象。
一度感到很匪夷所思,甚至怀疑我会不会用IE浏览器debug (除了解bug基本很少使用IE Developer Tool)。
忽然隐约想起以前很早之前看的一些文章,IE9浏览器好像对大尺寸的css有些约束,想到这一层,越想越觉得有道理,问题可能就出在这里。于是就这方面的问题google了一下,发现的确是这样。
Does IE9 have a file size limit for CSS?
规则 (Rule) 如下,我就不翻译了:
- A sheet may contain up to 65534 rules
- A document may use up to 4095 stylesheets
- @import nesting is limited to 4095 levels (due to the 4095 stylesheet limit)
其实我有点不太确定怎么样算一条rule,还需要再深挖一下。但是我看的另一篇文章提到过IE9一个css文件中不能超过4000+ selectors.
另外也有一些文章提到单个css文件的大小不能超过250k。官方规则中并没有提到这一条,不过我猜250k大小的css文件大概也早突破4000个selector了吧。
尽管对官方规则的具体细节还有点疑惑,但是通过阅读这些材料我们至少了解到IE9对css的selector或是大小是有限制的。知道这条对今后遇到这种问题对症下药也足够了。
看一下项目中webpack打包好的css文件:
可以发现的确有一个文件很大,达到了281k,经查是我们引用的第三方控件库。
We Get It!!
解决方案
第一个想法是文件能不能压缩?或者这个控件库我们是不是没有按需加载?
查看了一下webpack.config.js,该做的其实都做了。Webpack4中用MiniCssExtractPlugin来做css文件的提取与压缩。而第三方控件库也有相应的按需加载配置。这条路是走到头了。
敲黑板了:
第二个想法就是webpack有没有相应的插件可以把这个大尺寸的css再分割成多个css文件。哎,又Google了一下还真让我找到了:
Using webpack to generate your CSS is fun for some definitions of fun. Unfortunately the fun stops when you have a large app and need IE9 support because IE9 will ignore any more than ~4000 selectors in your lovely generated CSS bundle. The solution is to split your CSS bundle smartly into multiple smaller CSS files. Now you can.™ Supports source-maps.
根据文档,把这个插件添加到webpack.config.prd.js中:
const CSSSplitWebpackPlugin = require('css-split-webpack-plugin').default;
...
plugins: [
...
new CSSSplitWebpackPlugin({
size: 4000,
filename: 'dist/css/[name]-[part].[ext]'
}),
...
]
我们在插件中定的size是4000,意味着如果css文件中的Rule超过了4000也就是IE9的上限,我们就将文件分割。
运行结果如下:
测试结果通过,之前被丢弃的样式被IE9成功加载!
尾声(潜在风险)
尽管最大的痛点已经解决了,但是在运行过程中还是出了一个很有意思小bug。项目中有个”另存为”按钮,点击会弹出一个框,可以确定或者取消来关闭对话框。但是在关闭过程中发现一些莫名奇妙的动画效果。经过调查,原因如下:
- Webpack中OptimizeCSSAssetsPlugin插件为节省字节数,重新命名@keyframes,例如: @keyframes a, @keyframes b 等等。在单个文件中从a到z没有什么问题。
- 我们之前引入的 css-split-webpack-plugin 将css文件一分为二,两个分割后的css文件中都有动画效果,都是以a,b,c这样的格式命名。导致一些不属于该对话框的动画效果被意外添加了进来。
最后的解决方案其实也很简单,在OptimizeCSSAssetsPlugin的option中规定压缩css文件中不改变@keyframes的名字。
Issue: cssnano renames css keyframes to “a”
optimization: {
minimizer: [
....
new OptimizeCSSAssetsPlugin({
cssProcessor: require('cssnano')({
reduceIdents: false
})
})
],
....
}
问题解决!!
最后,真心希望客户赶紧升级单位的IE系统,你好我好大家好!