loader源码分析CSS Scoped的实现(4)

export default postcss.plugin('add-id', (options: any) => (root: Root) => { const id: string = options const keyframes = Object.create(null) root.each(function rewriteSelector(node: any) { node.selector = selectorParser((selectors: any) => { selectors.each((selector: any) => { let node: any = null // 处理 '>>>' 、 '/deep/'、::v-deep、pseudo等特殊选择器时,将不会执行下面添加属性选择器的逻辑 // 为当前选择器添加一个属性选择器[id],id即为传入的scopeId selector.insertAfter( node, selectorParser.attribute({ attribute: id }) ) }) }).processSync(node.selector) }) })

由于我对于PostCSS的插件开发并不是很熟悉,这里只能大致整理,翻翻文档了,相关API可以参考Writing a PostCSS Plugin

至此,我们就知道了第二个问题的答案:通过selector.insertAfter为当前styles下的每一个选择器添加了属性选择器,其值即为传入的scopeId。由于只有当前组件渲染的DOM节点上上面存在相同的属性,从而就实现了css scoped的效果。

小结

回过头来整理一下vue-loader的工作流程

首先需要在webpack配置中注册VueLoaderPlugin

在插件中,会复制当前项目webpack配置中的rules项,当资源路径包含query.lang时通过resourceQuery匹配相同的rules并执行对应loader时

插入一个公共的loader,并在pitch阶段根据query.type插入对应的自定义loader

准备工作完成后,当加载*.vue时会调用vue-loader,

一个单页面组件文件会被解析成一个descriptor对象,包含template、script、styles等属性对应各个标签,

对于每个标签,会根据标签属性拼接src?vue&query引用代码,其中src为单页面组件路径,query为一些特性的参数,比较重要的有lang、type和scoped

如果包含lang属性,会匹配与该后缀相同的rules并应用对应的loaders

根据type执行对应的自定义loader,template将执行templateLoader、style将执行stylePostLoader

在templateLoader中,会通过vue-template-compiler将template转换为render函数,在此过程中,

会将传入的scopeId追加到每个标签的segments上,最后作为vnode的配置属性传递给createElemenet方法,

在render函数调用并渲染页面时,会将scopeId属性作为原始属性渲染到页面上

在stylePostLoader中,通过PostCSS解析style标签内容,同时通过scopedPlugin为每个选择器追加一个[scopeId]的属性选择器

由于需要Vue源码方面的支持(vue-template-compiler编译器),CSS Scoped可以算作为Vue定制的一个处理原生CSS全局作用域的解决方案。除了 css scoped之外,vue还支持css module,我打算在下一篇整理React中编写CSS的博客中一并对比整理。

小结

最近一直在写React的项目,尝试了好几种在React中编写CSS的方式,包括CSS Module、Style Component等方式,感觉都比较繁琐。相比而言,在Vue中单页面组件中写CSS要方便很多。

本文主要从源码层面分析了Vue-loader,整理了其工作原理,感觉收获颇丰

webpack中Rules.resourceQuery和pitch loader的使用

Vue单页面文件中css scoped的实现原理

PostCSS插件的作用

虽然一直在使用webpack和PostCSS,但也仅限于勉强会用的阶段,比如我甚至从来没有过编写一个PostCSS插件的想法。尽管目前大部分项目都使用了封装好的脚手架,但对于这些基础知识,还是很有必要去了解其实现的。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wjfsfz.html