摘要:当前正在处理的节点,以及该节点的和等信息。源码解析之一整体分析源码解析之三写作中源码解析之四写作中作者博客作者作者微博
前言笔者系 vue-loader 贡献者之一(#16)
vue-loader 源码解析系列之一,阅读该文章之前,请大家首先参考大纲 vue-loader 源码解析系列之 整体分析
selector 做了什么const path = require("path") const parse = require("./parser") const loaderUtils = require("loader-utils") module.exports = function (content) { // 略 const query = loaderUtils.getOptions(this) || {} // 略 const parts = parse(content, filename, this.sourceMap, sourceRoot, query.bustCache) let part = parts[query.type] // 略 this.callback(null, part.content, part.map) }
大家可以看到,selector的代码非常简单,
通过 parser 将 .vue 解析成对象 parts, 里面分别有 style, script, template。可以根据不同的 query, 返回对应的部分。
很明显那么这个 parser 完成了分析分解 .vue 的工作,那么让我们继续深入 parser
const compiler = require("vue-template-compiler") const cache = require("lru-cache")(100) module.exports = (content, filename, needMap, sourceRoot, bustCache) => { const cacheKey = hash(filename + content) // 略 let output = cache.get(cacheKey) if (output) return output output = compiler.parseComponent(content, { pad: "line" }) if (needMap) { // 略去了生成 sourceMap 的代码 } cache.set(cacheKey, output) return output }
同样的,为了方便读者理解主要流程,笔者去掉了部分代码。
从上面代码可以看到,.vue 解析的工作其实是交给了 compiler.parseComponent 去完成,那么我们需要继续深入 compiler。
注意,这里 vue-template-compiler 并不是 vue-loader 的一部分,从 vue-template-compiler 的 npm 主页可以了解到, vue-template-compiler 原来是 vue 本体的一部分
并不是一个多带带的 package。通过查看文档可知,compiler.parseComponent 的逻辑在 vue/src/sfc/parser.js 里。
源码如下
parseComponent 做了什么/** * Parse a single-file component (*.vue) file into an SFC Descriptor Object. */ export function parseComponent ( content: string, options?: Object = {} ): SFCDescriptor { const sfc: SFCDescriptor = { template: null, script: null, styles: [], customBlocks: [] } let depth = 0 let currentBlock: ?(SFCBlock | SFCCustomBlock) = null function start ( tag: string, attrs: Array, unary: boolean, start: number, end: number ) { // 略 } function checkAttrs (block: SFCBlock, attrs: Array ) { // 略 } function end (tag: string, start: number, end: number) { // 略 } function padContent (block: SFCBlock | SFCCustomBlock, pad: true | "line" | "space") { // 略 } parseHTML(content, { start, end }) return sfc }
parseComponent 里面有以下变量
处理对象 sfc
把 .vue 里的 css, javaScript, html 抽离出来之后,存放到找个这个对象里面
变量 depth
当前正在处理的节点的深度,比方说,对于 foo
currentBlock
当前正在处理的节点,以及该节点的 attr 和 content 等信息。
函数 start
遇到 openTag 节点时,对 openTag 的相关处理。逻辑不是很复杂,读者可以直接看源码。有一点值得注意的是,style 是用 array 形式存储的
函数 end
遇到 closeTag 节点时,对 closeTag 的相关处理。
函数 checkAttrs
对当前节点的 attrs 的相关处理
函数 parseHTML
这是和一个外部的函数,传入了 content (其实也就是 .vue 的内容)以及由 start和 end 两个函数组成的对象。看来,这个 parseHTML 之才是分解分析 .vue 的关键
跟之前一样,我们要继续深入 parseHTML 函数来分析,它到底对 .vue 做了些什么,源码如下
parseHTML 做了什么export function parseHTML (html, options) { const stack = [] const expectHTML = options.expectHTML const isUnaryTag = options.isUnaryTag || no const canBeLeftOpenTag = options.canBeLeftOpenTag || no let index = 0 let last, lastTag while (html) { last = html if (!lastTag || !isPlainTextElement(lastTag)) { // 这里分离了template } else { // 这里分离了style/script } // 略 // 前进n个字符 function advance (n) { // 略 } // 解析 openTag 比如 function parseStartTag () { // 略 } // 处理 openTag function handleStartTag (match) { // 略 if (options.start) { options.start(tagName, attrs, unary, match.start, match.end) } } // 处理 closeTag function parseEndTag (tagName, start, end) { // 略 if (options.start) { options.start(tagName, [], false, start, end) } if (options.end) { options.end(tagName, start, end) } } }
深入到这一步,我想再提醒一下读者,selector的目的是将 .vue 中的 template, javaScript, css 分离出来。带着这个目的意识,我们再来审视这个 parseHTML。
parseHTML 整个函数的组成是:
一个 while 循环
在 while 循环中,存在两个大的分支,一个用来分析 template ,一个是用来分析 script 和 style。
函数 advance
向前跳过文本
函数 parseStartTag
判断当前的 node 是不是 openTag
函数 handleStartTag
处理 openTag, 这里就用到了之前提到的 start() 函数
函数 parseEndTag
判断当前的 node 是不是 closeTag,同时这里也用到了 end() 函数
通过以上各个函数的组合,在while循环中就将 sfc 分割成了三个不同的部分,读者可以对比我的注释和源码自行解读源码逻辑。
顺便在这里吐个槽,很明显这里的 parseHTML 是函数名是有问题的,parseHTML 应该叫做 parseSFC 比较合适。
vue-loader 源码解析之一 整体分析
vue-loader 源码解析之三 style-compiler (写作中)
vue-loader 源码解析之四 template-compiler (写作中)
作者博客
作者github
作者微博
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/90275.html
摘要:笔者系贡献者之一官方说明简单来说就是将文件变成,然后放入浏览器运行。部分首先分析部分从做右到左,也就是被先后被和处理过了。源码解析之二源码解析之三写作中源码解析之四写作中作者博客作者微博 笔者系 vue-loader 贡献者(#16)之一 官方说明 vue-loader is a loader for Webpack that can transform Vue components ...
摘要:前几天在如何创建一个中提到我要吐槽一下,于是今天我就来吐槽了先来看一段官网的定义啥意思就是官方推荐一个应该只做一件事情,如果对于一个文件有多次处理,可以把这些处理放在不同的里面进行链式调用。 前几天在如何创建一个webpack loader中提到我要吐槽一下vue-loader,于是今天我就来吐槽了 先来看一段webpack官网的定义: do only a single taskLo...
摘要:返回值为,如果能查找到元素,则将元素以数组的形式返回,否则返回空数组排除不合法的。的第一个字符为,并且为标签。如果存在,则查找下选择器为的所有子元素。正则表达式为如果没有指定标签名,则获取标签名。包裹元素的即为所需要获取的。 经过前面三章的铺垫,这篇终于写到了戏肉。在用 zepto 时,肯定离不开这个神奇的 $ 符号,这篇文章将会看看 zepto 是如何实现 $ 的。 读Zepto源码...
摘要:源码中接受个参数,空参数,这个会直接返回一个空的对象,。,这是一个标准且常用法,表示一个选择器,这个选择器通常是一个字符串,或者等,表示选择范围,即限定作用,可为,对象。,会把普通的对象或对象包装在对象中。介绍完入口,就开始来看源码。 欢迎来我的专栏查看系列文章。 init 构造器 前面一讲总体架构已经介绍了 jQuery 的基本情况,这一章主要来介绍 jQuery 的入口函数 jQu...
阅读 2398·2021-11-23 09:51
阅读 2402·2021-11-11 17:21
阅读 3057·2021-09-04 16:45
阅读 2346·2021-08-09 13:42
阅读 2157·2019-08-29 18:39
阅读 2831·2019-08-29 14:12
阅读 1229·2019-08-29 13:49
阅读 3323·2019-08-29 11:17