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