成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

vue-loader 源碼解析系列之 selector

miqt / 3132人閱讀

摘要:當(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

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 時(shí),當(dāng)前深度就是 3, 處理到

時(shí),當(dāng)前深度就是 2 。

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 比如