摘要:當(dāng)前正在處理的節(jié)點(diǎn),以及該節(jié)點(diǎn)的和等信息。源碼解析之一整體分析源碼解析之三寫作中源碼解析之四寫作中作者博客作者作者微博
前言筆者系 vue-loader 貢獻(xiàn)者之一(#16)
vue-loader 源碼解析系列之一,閱讀該文章之前,請(qǐng)大家首先參考大綱 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的代碼非常簡(jiǎn)單,
通過 parser 將 .vue 解析成對(duì)象 parts, 里面分別有 style, script, template??梢愿鶕?jù)不同的 query, 返回對(duì)應(yīng)的部分。
很明顯那么這個(gè) parser 完成了分析分解 .vue 的工作,那么讓我們繼續(xù)深入 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 解析的工作其實(shí)是交給了 compiler.parseComponent 去完成,那么我們需要繼續(xù)深入 compiler。
注意,這里 vue-template-compiler 并不是 vue-loader 的一部分,從 vue-template-compiler 的 npm 主頁(yè)可以了解到, vue-template-compiler 原來是 vue 本體的一部分
并不是一個(gè)多帶帶的 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 里面有以下變量
處理對(duì)象 sfc
把 .vue 里的 css, javaScript, html 抽離出來之后,存放到找個(gè)這個(gè)對(duì)象里面
變量 depth
當(dāng)前正在處理的節(jié)點(diǎn)的深度,比方說,對(duì)于 foo
currentBlock
當(dāng)前正在處理的節(jié)點(diǎn),以及該節(jié)點(diǎn)的 attr 和 content 等信息。
函數(shù) start
遇到 openTag 節(jié)點(diǎn)時(shí),對(duì) openTag 的相關(guān)處理。邏輯不是很復(fù)雜,讀者可以直接看源碼。有一點(diǎn)值得注意的是,style 是用 array 形式存儲(chǔ)的
函數(shù) end
遇到 closeTag 節(jié)點(diǎn)時(shí),對(duì) closeTag 的相關(guān)處理。
函數(shù) checkAttrs
對(duì)當(dāng)前節(jié)點(diǎn)的 attrs 的相關(guān)處理
函數(shù) parseHTML
這是和一個(gè)外部的函數(shù),傳入了 content (其實(shí)也就是 .vue 的內(nèi)容)以及由 start和 end 兩個(gè)函數(shù)組成的對(duì)象。看來,這個(gè) parseHTML 之才是分解分析 .vue 的關(guān)鍵
跟之前一樣,我們要繼續(xù)深入 parseHTML 函數(shù)來分析,它到底對(duì) .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 } // 略 // 前進(jìn)n個(gè)字符 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 分離出來。帶著這個(gè)目的意識(shí),我們?cè)賮韺徱曔@個(gè) parseHTML。
parseHTML 整個(gè)函數(shù)的組成是:
一個(gè) while 循環(huán)
在 while 循環(huán)中,存在兩個(gè)大的分支,一個(gè)用來分析 template ,一個(gè)是用來分析 script 和 style。
函數(shù) advance
向前跳過文本
函數(shù) parseStartTag
判斷當(dāng)前的 node 是不是 openTag
函數(shù) handleStartTag
處理 openTag, 這里就用到了之前提到的 start() 函數(shù)
函數(shù) parseEndTag
判斷當(dāng)前的 node 是不是 closeTag,同時(shí)這里也用到了 end() 函數(shù)
通過以上各個(gè)函數(shù)的組合,在while循環(huán)中就將 sfc 分割成了三個(gè)不同的部分,讀者可以對(duì)比我的注釋和源碼自行解讀源碼邏輯。
順便在這里吐個(gè)槽,很明顯這里的 parseHTML 是函數(shù)名是有問題的,parseHTML 應(yīng)該叫做 parseSFC 比較合適。
vue-loader 源碼解析之一 整體分析
vue-loader 源碼解析之三 style-compiler (寫作中)
vue-loader 源碼解析之四 template-compiler (寫作中)
作者博客
作者github
作者微博
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90275.html
摘要:筆者系貢獻(xiàn)者之一官方說明簡(jiǎn)單來說就是將文件變成,然后放入瀏覽器運(yùn)行。部分首先分析部分從做右到左,也就是被先后被和處理過了。源碼解析之二源碼解析之三寫作中源碼解析之四寫作中作者博客作者微博 筆者系 vue-loader 貢獻(xiàn)者(#16)之一 官方說明 vue-loader is a loader for Webpack that can transform Vue components ...
摘要:前幾天在如何創(chuàng)建一個(gè)中提到我要吐槽一下,于是今天我就來吐槽了先來看一段官網(wǎng)的定義啥意思就是官方推薦一個(gè)應(yīng)該只做一件事情,如果對(duì)于一個(gè)文件有多次處理,可以把這些處理放在不同的里面進(jìn)行鏈?zhǔn)秸{(diào)用。 前幾天在如何創(chuàng)建一個(gè)webpack loader中提到我要吐槽一下vue-loader,于是今天我就來吐槽了 先來看一段webpack官網(wǎng)的定義: do only a single taskLo...
摘要:返回值為,如果能查找到元素,則將元素以數(shù)組的形式返回,否則返回空數(shù)組排除不合法的。的第一個(gè)字符為,并且為標(biāo)簽。如果存在,則查找下選擇器為的所有子元素。正則表達(dá)式為如果沒有指定標(biāo)簽名,則獲取標(biāo)簽名。包裹元素的即為所需要獲取的。 經(jīng)過前面三章的鋪墊,這篇終于寫到了戲肉。在用 zepto 時(shí),肯定離不開這個(gè)神奇的 $ 符號(hào),這篇文章將會(huì)看看 zepto 是如何實(shí)現(xiàn) $ 的。 讀Zepto源碼...
摘要:由命名路由與子路由構(gòu)成整體結(jié)構(gòu),我們用它構(gòu)建如下頁(yè)面。以下兩張圖說明路由和子路由是如何工作的。繼續(xù)修改好友信息的路由部分添加好友信息為組件添加動(dòng)態(tài)路由為動(dòng)態(tài)路由添加為路徑參數(shù)添加數(shù)據(jù)下發(fā)為組件添加,并使用它。 不使用vue-router的情況 代碼官方給出下面的例子在不使用vue-router的情況下來實(shí)現(xiàn)一個(gè)路由。該示例結(jié)合了H5歷史管理API、單文件組件、JS模塊相關(guān)內(nèi)容來實(shí)現(xiàn)路由...
摘要:源碼中接受個(gè)參數(shù),空參數(shù),這個(gè)會(huì)直接返回一個(gè)空的對(duì)象,。,這是一個(gè)標(biāo)準(zhǔn)且常用法,表示一個(gè)選擇器,這個(gè)選擇器通常是一個(gè)字符串,或者等,表示選擇范圍,即限定作用,可為,對(duì)象。,會(huì)把普通的對(duì)象或?qū)ο蟀b在對(duì)象中。介紹完入口,就開始來看源碼。 歡迎來我的專欄查看系列文章。 init 構(gòu)造器 前面一講總體架構(gòu)已經(jīng)介紹了 jQuery 的基本情況,這一章主要來介紹 jQuery 的入口函數(shù) jQu...
閱讀 2439·2021-11-23 09:51
閱讀 2471·2021-11-11 17:21
閱讀 3110·2021-09-04 16:45
閱讀 2397·2021-08-09 13:42
閱讀 2230·2019-08-29 18:39
閱讀 2897·2019-08-29 14:12
閱讀 1299·2019-08-29 13:49
閱讀 3373·2019-08-29 11:17