大家好,很高兴又见面了,我是前端进阶,由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发! CSSinjs emotion排名第二的维护者Sam所在公司弃用了cssinjs方案,引起了不小的讨论。1。概述精读 原文很有条理,先从cssinjs优点说起,再转而谈到缺点,说明了cssinjs这个新事物拥有明显的优点与缺点;然后从性能问题作为切入点,说明自己所在的公司为什么不得不抛弃cssinjs;最后告诉读者目前自己的解决方案是cssmodules。 编译时cssinjs方案 之后还有一点儿延展性思考,即目前还诞生了一批编译时cssinjs方案,但面对性能问题时依然徒劳。让我们花点儿时间了解下作者的具体思路吧。2。cssinjs的优缺点 cssinjs作为一个理念较新的开发思路,拥有如下几个明显的优缺点。2。1优点无全局样式冲突:就像js文件天然支持模块化的好处一样,原生css因为没有模块化能力,天然容易导致全局样式污染,如果不是特意用BEM方式命名,想要避免冲突就只能借助cssinjs了。(cssmodules也一样能做到)与js代码合在一起:天然融合进js代码方便模块化管理,使css可以与某个局部模块绑定。(cssmodules也一样能做到,只是必须单独拆一个样式文件)能将js变量应用到样式上:虽然css变量也能解决这个问题,但不如cssinjs那么直观,inlinestyle也能解决这个问题,但会产生大量重复的局部样式,且这个优势cssmodules做不到。2。2缺点cssinjs运行时解析的实现版本增加了运行时性能压力,尤其在React18调度机制模式下,存在无法解决的性能问题(运行时插入样式会导致React渲染暂停,浏览器解析一遍样式,渲染再继续,然后浏览器又解析一遍样式)。增加了包体积。相比原生或者cssmodules方案来说,增加了运行时框架代码8kb左右。让ReactDevTools结构变得复杂,因为cssinjs会包裹额外的React组件层用来实现样式插入。 除了上述缺点外,cssinjs还有三点深度使用后才能察觉的坑:多个不同(甚至是相同)版本的cssinjs库同时加载时可能导致错误。笔者用styledcomponents就遇到了类似问题,甚至语法会产生不兼容的情况,虽然这些问题都可以被解决,但花费的额外时间需要计算一样,相比cssinjs得到的收益是否值得。样式插入优先级无法自定义:这就导致产生样式覆盖时,业务对样式覆盖的优先级无法产生稳定的预期。class优先级由header定义顺序决定,而非className的字符顺序决定,而header定义顺序又由资源加载与cssinjs插入执行时机决定,导致业务几乎不可能有稳定的样式覆盖顺序。这里产生的问题就是业务代码不断增多的!important定义。不同React版本的SSR,cssinjs需要适配不同的实现,这对框架作者不太友好。 除了性能问题以外,其他问题都可以忍,但偏偏在性能问题上,cssinjs遇到了无解的场景。3。无解的性能问题 cssinjs无解的性能问题 第一条缺点提到的运行时解析,是cssinjs方案永远跨不过去的困境,即便对于编译时cssinjs方案来说,也免不了在渲染时做额外的逻辑执行拖慢渲染速度:functionApp(){return;就是这种代码导致了性能问题} 原因是当React重渲染组件时,需要重新解析样式定义,并序列化className,当渲染非常频繁时会导致明显的性能瓶颈,而解决方法是把样式定义抽出来,但这样就损失了第三个优点,即无法读取js变量了:constmyCsscss({backgroundColor:blue,width:100,height:100,}); 不得不说React的渲染机制实在是太有问题了,如果换成SolidJS这个问题就好办了,因为运行时的样式代码仅会运行一次,组件重渲染也不会导致这段解析代码被重复执行,此时cssinjs在样式变化时再做一次精确样式更新,性能问题就可以被解决了。4。换成cssmodules cssmodules cssmodules同时支持优点一和二,而优点三可以通过一些特定语法糖绕过:通过:import:export伪类做css变量的导入导出,用webpackloader实现js中引用css变量,用cssvariable实现css引用js变量。 所以当性能问题是绕不过去的话题,而cssmodules在性能最优的情况下,有一些曲线方案可以同时支持cssinjs的优点,也就能理解为什么作者要弃用cssinjs了。5。包体积真的变大了吗 原文谈到的cssinjs增加了816kb其实是在强行堆缺点了,除非你的项目只有一行css定义。如果我们只考虑传输时的包体积与HTML中样式定义数量,而忽略运行时产生的性能负担,那么cssinjs在大型项目无疑是最优的。 打包体积 原因就是cssinjs样式是按需插入的,没有渲染的组件就不会插入样式。甚至渲染了的组件也不一定会插入样式,因为cssinjs可以对包含相同样式定义的场景做className合并,类似于webpack打包时,可以把不同模块公共代码抽到一个chunk里。6。编译时cssinjs方案是出路吗 理论上是出路,但限制了cssinjs的灵活性。从vanillaextract等编译时cssinjs框架来看,确实解决了运行时cssinjs性能问题,但带来了更多语法限制,比如必须预先定义样式再使用:import{style}fromvanillaextractcssconstmyStylestyle({display:flex,paddingTop:3px})constApp() 编译时cssinjs想要做到通用性,只能提供一个className,这样就不受任何框架和环境的限制了,但这样也限制了声明语法的灵活性,显然不可以用内联方式定义样式。 而且这种编译时的方案本质上和cssmodules是一样的,背后都是定义了一些静态样式名,只是说这些样式问题以。sass定义还是。ts定义,如果用。ts定义,配合编译工具可以使代码原生import的更加舒服。 所以使用了编译时cssinjs方案,本质上还是抛弃了运行时cssinjs,投向了变种的cssmodules阵营。7。总结 cssinjs本身方向是对的,即把css与js融合,但太过灵活的运行时cssinjs方案遇到了几乎不可解的性能问题,编译时的cssinjs方案可能是更好的出路。 cssinjs这个名字本身就表示它拥有injs的灵活性,而编译时cssinjs方案本质因为是cssmodule,所以不可避免拥有一些比较奇怪的限制,如果js里的代码不能像真的js一样灵活,可能还不如回到。scss或者。less的后缀更好理解一些。参考资料原文链接:https:github。comascodersweekly 原文作者:黄子毅