2021SC@SDUSC
Hippy源码分析(三)—hippy-vue-css-loader
简述
此次接着上次分析内容。这次进行对css-parser.js的分析,这个文件一共有900行代码,若干个函数写的零零碎碎。比较难搞。
相对来说,这一部分的css-loader在整个分工负责的部分中不是那么重要。所以大致分析一下即可,学习一下代码思路和规范即可,在分析中尽量学一点知识。
开始分析
这个模块export的是parseCSS函数,按照读源码习惯,先大致浏览一下全局变量然后看parseCSS 内部定义。 这些变量声明了几个MAP,以对象键值对的形式绑定了一些信息。 DEGREE_UNIT 包含了三个度数的单位。 而commentRegexp 是一个正则表达式模板,应该是匹配注释的。 然后还有几个函数,是为parseCSS内部服务的:
trim 函数是对参数字符串进行“修剪”的,如果参数Truthy值为true那么将返回一个剪去了头尾空格的字符串,否则返回一个空字符串。 然后是addParent 函数,为节点及内部变量添加不可列举的父节点的引用,详细信息等在这个函数被调用的时候再分析。 然后是convertPxUnitToPt 函数,这个函数简单,就是单纯的返回数字部分,实际上也不是单位的转换,因为逻辑上是1:1的关系,只需要返回字符串中的parseFloat部分就好了。
之后就是敌方大将了,内部定义一共占据了800行代码,几乎全是一些函数的定义。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/28c8e0899a4b49628b3dc3123877f603.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6Z2e5LiA6Iis5bCG5p2l5pe2,size_9,color_FFFFFF,t_70,g_se,x_16) `Parse the CSS to be AST tree`,这里不用多说了,本来分析的就是css-loader,把css“翻译”为js语法树。而AST就是js比较底层的东西---抽象语法树。 这个函数有两个参数,第一个参数`css`是css源码。`options`可以看之前的分析,他应该是包含了平台的一些信息。除此以外声明了两个变量`lineno`和`column`,来标示位置,具体有何用,继续往下看。 再往后除了声明了一个errorsList数组外没有变量了,全是函数。并最终返回一个`addParent(stylesheet())`。
先说明内部一个重要的函数!: 很明显这是匹配正则表达式然后slice 掉匹配的内容—削减css 字符串变量。 可能乍一看这里还是不明白为什么重要,后续分析中有个“目前分析可得”,看到那里就明白了。 好,下面开始吧:
第一个函数是updatePosition ,就是单纯的把lineno 和column 调整到最后一行的开头或最后的位置,感觉像是光标的记录,但是具体作用还是要具体分析下去才可能知道(这里的~i,涉及二进制,测试过只有当i的值为-1时才会为false,而lastIndexOf 函数如果没有匹配到则会返回-1,这个时候column=1+str.length,即定义到这一行的结尾)。
position 函数: 定义start 对象,成员变量是lineno 和column ,根据ES6新特性,这里的colomn 写法实际上相当于column:column 。该函数返回了一个箭头函数,用以存储节点的位置信息。 后面就是在index.js里调用过的stylesheet 函数了,下面全部注意力分析它:(附index.js调用的地方.jpg) index.js中: parsed对象含有parseCSS内部定义的所有成员,而stylesheet是方法,返回的是一个对象,.rules指的是返回的对象里的rules变量,这个对象属性是通过rules 函数返回的。
rules 函数: whitespace函数内部匹配了css头部的空格字符串并且剪切掉,然后调用上面提到的updatePosition 函数来更新lineno 和column 。 rules初始化为一个空数组。 然后调用comments 函数并把rules作为参数传进去。 具体看下一个函数comments 。
comments 函数: 进一步声明rules,然后while循环c的Truth值,如果c 不为false 则添加到rules数组里,最后返回了rules,根据js的语法特性,这样返回会确实更新上一个代码域中的rules变量。 下一步再去看看comment 函数的代码。
comment 函数: (庆幸这里终于没有别的调用了。) 在这里pos 是一个箭头函数(见上文)。 如果css是以/* 开头的,则返回null ,显然此时c 不会被插入到rules 数组里。 再往后通过while循环把i定位到了css字符串中出现*/ 的地方。 然后声明一个str=css字符串截去开头的/* 和结尾的*/ 。 然后column+=2;然后再调用updatePosition 函数,由str参数决定更新lineno 和column 的值。 由css.slice(i)以及后面这几行包括return的代码,可以得出结论,这个方法是获取夹在"/*" 和 "*/"内部的注释。毕竟css中注释也是可以通过/**/来包裹多行注释的。
目前分析可得:
目前来看,css字符串中难免任何地方会出现/**/包裹的注释,而parse css to AST这个工作显然是不需要甚至排斥这些注解的,而现在已经分析完comments()、comment()的作用了,他们就是slice(截去)注解的。在实际寻找css的声明(实际css代码)时,随时(css-parser.js文件这里是通过while循环判断注解的存在的)调用comments()函数删掉那些注解。
可以理解为一层一层扒(正则表达式匹配确定位置然后保存起来或者slice掉)玉米皮(注解),而我们要的是玉米粒(真正的css代码,比如width: 100px;),这个例子还不够形象,因为注解会出现在任何地方而不是只出现在“玉米粒”的外层。
当然,在css中,我们是通过正则表达式匹配{}来确定代码块的,也就是css中除了注解,还有class和id的声明等,亦或者’@xxx’声明的代码块(AT规则,详见css语法)等。 通过阅读源代码发现(这里不全部把代码放出来了,实际上和comments()同理),定义的许多函数都是反复利用正则表达式匹配任何非css声明的任何字符串以及反复用slice函数削减css 变量。
下面可以看几个例子,这个例子同时会方便并确认我们的进一步分析。 下面是继续分析,就相当于例子了。
回到正题,继续分析:
然后回到调用comments()的地方继续分析,毕竟现在的分析结果还不能与index.js匹配起来: 这是扒去以"@“开头的css代码(AT规则,详见css语法): 如果是”@"开头则会匹配上一个函数并返回处理结果(毕竟不能把css定义扔掉只剩下 width:100px; 这类代码)。 看其中之一即可:atkeyframes : match的重要性不用多说了,match内容已经放在文章开头了。 好,继续:,如果这里的atrule 函数匹配不到呢?自然而然去匹配rule 函数。 那node 就=rule() 咯。 等等!发现什么了? type: 'rule' !,这与index.js里就匹配起来了呀。 这里应该就是存储的实际的css样式代码了。 不急,先看一下这里面调用的几个函数:selector 、declarations 显然这是匹配的选择器,css中选择器就不用多说了吧。 继而declarations 也不用多说了,大括号中就含有declaration(css装饰代码)了:
好,最后parseCSS :
rules 分析完了,stylesheet 也分析差不多了,addParent 也大体看了一下。 可以回到index.js里了: 这个parsed 也就不需要多说什么了。 然后rulesAst 就要出来了~ 过滤parsed.stylesheet.rules 过滤出type === 'rule' 的item,具体type==='rule’的item(通过上文分析,这里面存储的含有真正的css样式代码),然后map修改数组并返回修改后的数组,具体修改的就是那行value = translateColor(value, options) ,那就是颜色分析器color-parser.js 的事了,上篇文章分析过。 好,AST出来了,然后颜色也转换了。 css-loader差不多就完结了。
总结
ok,到今天,就可以和hippy-vue-css-loader说拜拜了。 这篇博文主要分析了css-parser.js,最后再整合了第(二)篇博文的内容,完整的理解了hippy-vue-css-loader。虽然代码量比较大,但是在某个点打通之后还是挺爽的。 现在了解到了写css-loader(针对于webpack),亦或者是其他loader的技巧: 除了熟悉精通css或其他本身语法,还要熟练掌握正则表达式,然后剩下的就是靠代码逻辑和自己的思维了。 通过此次分析,学习了一点正则表达式的知识,通过读源代码,对ES6特性有了更加熟练的掌握。 体验到了作者开发的思维和写代码的方式。在分析结束后感到代码结构清晰,对于自己的开发风格有一定的积极影响。
|