当前位置: 首页 > Web前端 > CSS

CSS解决方案

时间:2023-03-31 00:12:49 CSS

背景随着前端项目的复杂度越来越高,如何构建可维护、可重用、可配置的CSS代码成为了每个前端工程师需要思考的问题。问题本质:CSS最初是为了描述网页的风格而提出的,不具备编程语言的特性。因此,在前端工程化的道路上,CSS暴露了一些问题,阻碍了它:全局范围、没有模块复杂系统、多人协作的概念,容易出现风格冲突,难以维护;缺乏变量、函数等编程语言的特性,不利于通用属性、样式的抽象和复用;每个浏览器及其不同的版本是不同的。CSS语法支持的程度和支持方式的不一致,具体体现在是否支持某些功能上。同一个属性在不同的浏览器中有不同的属性名;根据不同状态渲染样式时(这里称为StateStyling),需要定义多个类,可读性差;.....针对这些问题,爱折腾的前端程序员探索了各种技术和解决方案。本文简单介绍常用的CSS技术,然后分享两种常见的CSS工程解决方案,希望能帮助像我一样对这些概念比较模糊的同学对此有一个系统的认识。BEMBEM(Block__Element--Modifier),是一种CSS命名约定,看例子:风景对应Block,天空,地面,人物对应Element,黄昏对应修饰符。不难看出,BEM的本质其实就是将HTML元素的层级关系和元素自身的状态结合起来,形成元素唯一的className,旨在解决全局作用域带来的样式冲突问题的CSS。但BEM毕竟是一个规范,而不是一个框架。Block、Element、Modifier的命名需要开发者思考。引用某位大牛的话:“命名和缓存失效是计算机领域最难的两件事”。可见BEM会增加开发人员的工作量。另外,当HTML结构复杂时,BEM形式的className会很长,可读性差,增加了代码文件的体积。CSSPreprocessorCSS预处理器(CSSPreprocessor)是一类CSS框架,旨在增强CSS语言功能,从而帮助开发者编写可重用和可维护的样式代码。主流的CSS预处理器包括:Sass、Less、Stylus,它们都以DSL的形式为开发者提供更强大的语言特性(Sass:.scss/.sass,Less:.less,Stylus:.styl),然后将它们编译成浏览器可以理解的.css文件。SassSass(SyntacticallyAwesomeStylesheets)被誉为世界上最成熟、最强大的CSS预处理器。没什么不对,Sass拥有庞大的用户群,活跃的社区和详细的文档是它的优势之一。最初基于Ruby,后来衍生出libSass和DartSass,使得Sass编译速度更快。Sass具有强大的功能。它为CSS扩展了变量、Mixin、继承、数学运算等编程语言功能,优化了CSS本身的语法。例如,适当使用嵌套可以使样式结构更加清晰;它提供了不会产生额外的http请求的导入,还提供了一组强大的内置函数。LessLess(LeanerStyleSheets)基于JS,其设计理念尽可能类似于CSS语法和函数式编程。Less甚至向后兼容CSS,这意味着在将旧项目迁移到Less时,您可以直接将CSS代码复制到.less文件中。当然,你仍然需要使用Less提供的功能进行修改,但这无疑减少了工作量。.所以Less上手快,但相对Less功能较弱。比如它没有提供类似Sass的@function功能,Mixin不适用于需要返回值的情况;又如Less的extend函数实际上是将Extended对象的样式复制到目标对象中,而不是像Sass那样为多个类定义相同的样式,造成代码冗余。例如:/*LessCode*/.header{padding:2px;字体粗细:粗体;}h1{.header;/*扩展.header样式*/font-size:42px;}h2{.header;/*扩展.header样式*/font-size:36px;}编译结果:.header{padding:2px;字体粗细:粗体;}h1{填充:2px;字体粗细:粗体;字体大小:42px;}h2{填充:2px;字体粗细:粗体;font-size:36px;}StylusStylus基于NodeJS,提供更强大的功能,同时适当地接近CSS语法。它看起来像是Sass和Less的结合。Stylus的语法是python风格,崇尚简洁,所以建议不要写花括号,当然这是可选的。查看Stylus代码:border-radius()-webkit-border-radius:arguments-moz-border-radius:argumentsborder-radius:argumentsbodyfont:12pxHelvetica,Arial,sans-serifa.button{border-radius:5px可以看到,Stylus在定义函数、变量或者mixins时,甚至不需要像sass一样添加$、@等符号,语法非常简洁。总的来说,Sass有详细的文档,成熟的社区,相对强大的功能和编译速度;Less向后兼容CSS,学习曲线平缓,迁移旧项目的难度较小,但功能不如Sass和Stylus强大;Stylus是最强大的Big,语法最简洁,但文档可读性较差。PostCSS此外,我们来谈谈PostCSS。PostCSS本质上是一个平台。该平台本身不对CSS做任何增强。它只是将CSS解析为AST并将其提供给插件。所有需要的功能都可以通过插件灵活定制(babel也是这样的Thought),比如Autoprefixer、类似babel-preset-env的PostCSSPresetEnv、CSSModules、stylelint等,你甚至可以自己写插件-插件。因此,也可以将上面三个替换成PostCSS,即找到对应的需要语法功能的PostCSS插件,如:postcss-partial-importpostcss-advanced-variablespostcss-nested...CSSModulesCSSModules是一个CSS模块化规范:通过为CSSRule生成一个唯一的类名,默认在本地制作每个CSSModule下的CSSRule。当然,也可以声明全局规则。CSS模块导出映射本地类名和全局类名:/*style.css*/.className{color:green;}importstylesfrom"./style.css";//import{className}from"./style.css";element.innerHTML='';此外,它还支持同一模块或不同模块内CSSRules之间的复合,提高了样式的复用性。常见的实现有webpack的css-loader,以及针对React优化的HOC版react-css-modules。//css-loader{test:/\.css$/,loader:'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'}//react-css-modulesimportReactfrom'react';从'react-css-modules'导入CSSModules;从'./table.css'导入样式;classTableextendsReact.Component{render(){returnA0

B0
;}}exportdefaultCSSModules(Table,styles);CSS-IN-JSCSS-IN-JS也是一个CSS工程解决方案。核心思想是CSS完全由JS管理,利用JS模块、变量、函数的概念提高CSS代码的可维护性。可重用性。常用的实现有:styled-components、glamorou、emotion等。//styled-componentsconstContainer=styled.div`text-align:center;颜色:${props=>props.color};`render(测试容器);//emotionimport{css,jsx}from'@emotion/core'constcolor='white'render(Hovertochangecolor.)你可以看到styled-components和emotion使用ES6TaggedTemplates语法分别调用styled和css函数。以styled函数为例,上面的代码会被编译成类似下面的代码:.color],//动态值functioncreateStyledRules(x0){return[`.css-Container-duiy4a{text-align:center;color:${x0}}`]})渲染时,styled会执行在动态值中发挥作用,并赋予它最新的道具。然后调用createStyledRules并传入动态值的结果,最后将createStyledRules生成的样式插入到样式表中,然后将生成的类名赋给div的className属性。CSS-IN-JS库的权衡在于运行时性能,因为可能需要在运行时解析模板字符串,根据props动态生成样式,并调用哈希算法生成唯一的CSS类名。不同库的性能差异体现在对这些操作的优化措施上,尽可能将这些操作提前到构建时间。Solutions实际项目中的CSS解决方案是“因地制宜”的,因为如何处理CSS是由项目中的实际需求和其他技术决定的。比如React项目会用到react-css-modules;结合ReactHOC的形式,让代码更加简洁;Vue项目将使用vue-loader、vue-style-loader;选择不同的CSS-IN-JS库,例如styled-components、emotions等。尽管存在差异,但CSS解决方案大致可分为两种类型:传统CSS、CSS-IN-JS。传统考虑到可维护性和可重用性,我们需要引入一个CSSPreprocessor。具体选择可以参考上面对Sass、Less、Stylus的概述。这里我们以Sass为例。然后利用sass的部分功能结合本地组织样式代号目录结构,例如:sass/||–基地/||–_reset.scss#重置/规范化||–_typography.scss#排版规则|...#等...||–组件/||–_buttons.scss#按钮||–_carousel.scss#轮播||–_cover.scss#封面||–_dropdown.scss#下拉||–_navigation.scss#导航|...#等...||–帮手/||–_variables.scss#Sass变量||–_functions.scss#Sass函数||–_mixins.scss#SassMixins||–_helpers.scss#类和占位符助手|...#等...||–布局/||–_grid.scss#网格系统||–_header.scss#标题||–_footer.scss#页脚||–_sidebar.scss#侧边栏||–_forms.scss#表格|...#等...||–页数/||–_home.scss#主页特定样式||–_contact.scss#联系人具体样式|...#等...||–主题/||–_theme.scss#默认主题||–_admin.scss#管理主题|...#等...||–供应商/||–_bootstrap.scss#引导||–_jquery-ui.scss#jQueryUI|...#等...||`–main.scss#主Sass文件,将这些partials导入main.scss另外,考虑样式代码的构建过程,如果与webpack结合使用,则需要使用sass-loader、css-loader、style-loader等以下配置仅供参考:module:{rules:[{test:/\.s[ac]ss$/i,include:path.resolve(__dirname,'src'),use:['style-loader','css-loader','sass-loader']}//...其他规则]}考虑到样式代码的代码拆分和缓存策略,在生产模式下一般会替换style-loader配合MiniCssExtractPlugin,这样就可以将css代码单独构建到一个文件中,而不是在运行时以的形式插入到文档中。模块:{规则:[{测试:/\.s[ac]ss$/i,包括:path.resolve(__dirname,'src'),使用:[process.env.NODE_ENV!=='production'?'style-loader':MiniCssExtractPlugin.loader,'css-loader','sass-loader']}//...其他规则]}最后,也许你的项目需要一些额外的特性,比如使用一些浏览器兼容性差的CSS语法需要被翻译成兼容的语法。此时还需要引入postcss及相关插件,如:module:{rules:[{test:/\.s[ac]ss$/i,include:path.resolve(__dirname,'src'),使用:[process.env.NODE_ENV!=='production'?'style-loader':MiniCssExtractPlugin.loader,'css-loader',{loader:'postcss-loader',options:{ident:'postcss',sourceMap:true,plugins:[postcssPresetEnv({browsers:BROWSERSLIST,}),],}},'sass-loader']}//...其他规则]}CSS-IN-JScss-in-js解决方案中最重要的是选择合适的库styled-components,emotion,glamorou,JSS......,根据自己项目的业务场景,选择的MVVM框架类型(React或Vue或Angular),开发团队级别等因素,选择最适合团队的css-in-js库,既可以提高开发效率,又可以降低迁移风险。PackageAsObjectAsTaggedTemplatesSSRRNSupportAgnosticDynamicBabel插件绑定情感Rockey-Rectypy-components???aphrodite?csx?csx?glam?glam?glam?glamor?glamor?glamor?glamor?glamor?迷人的styletron?pytyletron?pytyletron?当前-in-js仍然有一定的局限性:它对React应用程序更友好。虽然很多库都有Agnostic版本,也有针对vue的styled-components-vue和emotion-vue,但是在功能和编写上都不如React。另外,组织目录结构,抽象可复用代码,同样适用于CSS-IN-JS。可以参考上面Traditional方案中的目录结构和粒度。CSS-IN-JS在这方面可以做得更好,因为复用的粒度可以提升到组件级别。无论传统方案还是CSS-IN-JS方案,都可以通过服务端渲染的方式提取关键css,提高首屏渲染速度。大致思路是根据用户访问的路由加载相应的页面,通过React的contextapi获取页面相应的样式以style标签的形式插入到HTML文档的头部,以及在系统跳转时交给客户端控制,具体可以参考isomorphic-style-loader的实现。一些想法CSSSolutions会随着各种新技术的出现而不断变化。很多技术往往源于某个开发者的灵感。既然社区提出了某个想法,一些认同的人可能会尝试给出具体的实现。所以当我们有一天灵光一闪的时候,不要只是想想,勇敢的分享给别人或者尝试去实现,即使失败了,也能学到很多东西。进一步阅读CSS演变:从CSS、SASS、BEM、CSS模块到样式化组件CSS-in-JSemotion的权衡CSS-in-JS的下一代