与成千上的Facebook工程师在这纷繁复杂的产品线中相互协作, 代码质量通常面临独特的挑战。
我们不仅需要处理庞大的数据库, 还需要跟上时代发展的节奏 —— 新增功能的上线、 已有功能的改善, 甚至是产品重组的需要。
对于CSS来说,这意味着成千上万的文件在一个不断变化的状态中。
即使我们已经尽力通过代码回顾、风格指导,甚至是重构等方式去确保CSS代码的质量,然而无意的小错误依然可以让我们措手不及。
直到最近,我们使用的还是一款我们自主开发的CSS检测器(CSS linter)去获取一些基本的错误以保障代码风格的一致。尽管它已经达到了我们的目的,但我们需要的是更强大的解决方案。
旧的CSS检测器基本上是一些正则表达式搜索和替换规则。正确解析CSS不是一个简单的问题,和供应商扩展和定制规格的变化,会计是更具挑战性。
下面是一个旧代码的例子:
preg_match_all(
// 这条规则匹配的是没有前置选择器的属性选择器
'/\/\*.*?\*\/|\{[^}]*\}|\s(\[[^\]]+\])/s',
$data,
$matches,
PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
foreach ($matches as$match) {
if (isset($match[1])) {
raiseError(...);
}
维护这种神秘的匹配规则是极其无趣的。且不说它很难改变和理解,性能也是一个问题。对于每个规则来说,我们不得不一遍又一遍的去遍历整个输入,匹配不同的正则表达式。
我们决定新的解决方案是基于一个真实的,遵循规范的CSS解析器。 由于代码必须对这种类型的语法有效解析器工作,我们不能再把我们整个CSS代码库作为一个巨大的文本。 这种新方法将显著改善CSS代码质量,它可以捕捉到那些导致错误渲染或者被浏览器忽略的无效代码。
一些潜伏在我们巨大代码库阴影中的代码可能向下面一样:
{
display: none:
background-color: #8B1D3;
padding: 10px,10px,0,0;
opacity:1.0f;
}
你能发现细微的差别吗? 属性名输入错误,不正确的十六进制,错误的分隔符——浏览器会忽略这些,这是与我们开发人员的意图相去甚远。
它意识到没多久 PostCSS 将是一个伟大的工作,一个合适的工具,基于标准的CSS解析器与一个优秀的模块化的体系结构。 然后,我们选择 Stylelint 作为我们的CSS检测器。 它基于PostCSS,非常灵活,并且有很好的支持。
类似于基于JavaScript解析器/过滤器的Esprima 和 ESLint,PostCSS Stylelint给你访问整个AST(抽象语法树)的权限。 AST节点可以方便地使用任何条件去访问任何节点:我们使用正确的类名了吗?我们包括正确的抽象了吗? 有弃用或不受支持的扩展吗? 有本地化问题吗?
在下面这个例子中,我们将遍历所有的声明,并找到所有的首字母大写("text-transform: uppercase")的片段。
root.walkDecls(node => {
if(node.prop === 'text-transform'&& node.value ==='uppercase') {
report({
...
});
}
});
我们甚至可以解析和解构低级的函数例如linear-gradient。 这里有一个更复杂的例子:找到linear-gradient函数,并检查它的第一个参数。
// disallow things like linear-gradient(top, blue, green) w. incorrect first
valueroot.walkDecls(node => {
const parsedValue = styleParser(node.value);
parsedValue.walk(valueNode => {
if(valueNode.type ==='function'&& valueNode.value ==='linear-gradient') {
const firstValueInGradient = styleParser.stringify(valueNode.nodes[0]);
if (disallowedFirstValuesInGradient.indexOf(firstValueInGradient) > -1) {
report({
...
});
}
}
});
});
所有无论这些CSS代码如何格式化,如何规则、声明和如何绑定,都非常容易理解和更新。
我们可以非常欣喜的看到,通过使用PostCSS and Stylelint,当我们处理起例如纠正错别字、关键帧数、!important、复杂选择器、非标准属性等任何常见及潜在问题时都变得得心应手起来~
我们可以通过一个非常便利的内部插件机制(built-in plugin mechanism)来自定义我们的规则。 我们现有的插件如下:
我们为这些Stylelint插件制定了很多和 规则 和 附属条例,试图将它们释放给某个独立的仓库或者是某个线程的Stylelint团队。
我们的静态分析过程中一个非常重要的方面是自动格式化。 linter支持修补,如果不一致时,它会询问你是否需要根据规则进行替换。 这可能是一个强大和节省时间的概念。 提交时你想要做的最后一件事是看到检测错误,然后回去修复它们,尤其当此项工作是非常平常的工作,比如字母重新排序规则。通常更好的做法是让一个自动格式化的工具去完成其工作,以节省开发时间。
不幸的是,Stylelint没有内置的自动格式化(可以说,linter不应该关心这样一个任务),所以我们必须重新添加及实现的一些现有Stylelint规则去提升我们对基础设施的支持。同时,我们正在开发一种潜在的,在未来可以为所有用户服务的通用Stylelint。
我们的老版的CSS检测器问题之一是,我们没有单元测试。 这是一个至关重要的事情,因为我们需要对任意的文本进行解析。 在使用Stylelint重写我们的检测器,我们添加了测试每一个规则,确保规则可以获取正确的东西,忽略了无关紧要的东西,并建议合适的替代品。
我们使用一个可信的且易于理解的Jest框架 ,测试起来是这样的:
test.ok('div { background-image: linear-gradient( 0deg, blue, green 40%, red ); }',
'linear gradient with valid syntax');
test.notOk('a { background: linear-gradient(top, blue, green); }',
message,
'linear-gradient with invalid syntax');
这个高质量的CSS重写只是第一步。 有很多有用的规则我们计划添加(内置和自定义的)——所有这些旨在抓住常见错误,执行最佳实践和控制代码风格惯例。 我们已经使用JavaScript(通过ESLint)所以我们没有理由不应该用它去处理CSS。
目前检测器已经与Phabricator集成,成为了我们的代码协作工具,所有的的警告/建议都在修订当中。
这使得CSS检测器在工作流中迈进了非常大的一步。
有一个合适的CSS解析器的另一大好处是,它可以收集有关代码库的准确的统计数据。 最近使用的属性/值是什么? 也许他们应该删除或替换(通过电线发送更少的字节)。 最流行的颜色/字体大小/ 层级是什么? 也许他们应该被抽象成可重用的组件或变量。 “最重”选择器是什么? 这里也许有一个性能问题等。
所有的这些可以帮助它提高性能和维护。
值得一提的是CSS-in-JS概念 在React社区逐渐流行起来。现在的情况是,一个CSS检测器的情况在很大程度上还是作为一个无关紧要的CSS文件检测器。它目前旨在解析传统CSS文件,以后可能渐渐的去适应解析在JS中定义为CSS代码段。不过,通过PostCSS的JS API,处理起这些来应该是相当简单的。
虽然我们正在Facebook中积极探索CSS-in-JS的方法,它还处在早期的实验阶段,我们仍然有很大的CSS代码库维护。
我们很高兴使用开源工具,并尽可能多得到的反馈。衷心希望它可以提供一个坚实的现代规则和准则,让每个人去使用和相互合作。
谢谢JS infra和Webspeed团队,和任何一个帮助重写的朋友,Stylelint 的 David Clark和Richard Hallows。以及使postcss这种神奇的工具成为可能的整个社区。
这是一个真诚合作、共同努力的结果。
声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com
支持全球约2.4万个城市地区天气查询,如:天气实况、逐日天气预报、24小时历史天气等
支持识别各类商场、超市及药店的购物小票,包括店名、单号、总金额、消费时间、明细商品名称、单价、数量、金额等信息,可用于商品售卖信息统计、购物中心用户积分兑换及企业内部报销等场景
涉农贷款地址识别,支持对私和对公两种方式。输入地址的行政区划越完整,识别准确度越高。
根据给定的手机号、姓名、身份证、人像图片核验是否一致
通过企业关键词查询企业涉讼详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。