成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

vue 源碼學(xué)習(xí)筆記三 vue中如何生成虛擬DOM

VioletJack / 1580人閱讀

摘要:調(diào)用了方法,參數(shù)是拿到后,判斷類型是否為,如果有多個(gè),則是模板上有多個(gè)根節(jié)點(diǎn),觸發(fā)告警。

vm._render 生成虛擬dom

我們知道在掛載過(guò)程中, $mount 會(huì)調(diào)用 vm._update和vm._render 方法,vm._updata是負(fù)責(zé)把VNode渲染成真正的DOM,vm._render方法是用來(lái)把實(shí)例渲染成VNode,這里的_render是實(shí)例的私有方法,和前面我們說(shuō)的vm.render不是同一個(gè),先來(lái)看下vm._render定義,vm._render是通過(guò)renderMixin(Vue)掛載的,定義在src/core/instance/render.js

// 簡(jiǎn)化版本
Vue.prototype._render = function (): VNode {
  const vm: Component = this
  const { render, _parentVnode } = vm.$options
  ...
  // render self
  let vnode
  try {
    // _renderProxy生產(chǎn)環(huán)境下是vm
    // 開(kāi)發(fā)環(huán)境可能是proxy對(duì)象
    vnode = render.call(vm._renderProxy, vm.$createElement) // 近似vm.render(createElement)
  } catch (e) {...}
  // return empty vnode in case the render function errored out
  if (!(vnode instanceof VNode)) {
    if (process.env.NODE_ENV !== "production" && Array.isArray(vnode)) {...}
    vnode = createEmptyVNode()
  }
  // set parent
  vnode.parent = _parentVnode
  return vnode
}

先緩存vm.$options.rendervm.$options._parentVnode,vm.$options.render是在上節(jié)的$mount中通過(guò)comileToFunctions方法將template/el編譯來(lái)的。

vnode = render.call(vm._renderProxy, vm.$createElement)調(diào)用了render方法,參數(shù)是vm._renderProxy,vm.$createElement

拿到vnode后,判斷類型是否為VNode,如果有多個(gè)vnode,則是模板上有多個(gè)根節(jié)點(diǎn),觸發(fā)告警。

掛載vnode父節(jié)點(diǎn),最后返回vnode

簡(jiǎn)要概括,vm._render函數(shù)最后是通過(guò)render執(zhí)行了createElement方法并返回vnode;下面就來(lái)具體看下vm._renderProxy,vm.$createElement,vnode

vm._renderProxy

首先來(lái)看下vm._renderProxy,vm._renderProxy是在_init()中掛載的:

Vue.prototype._init = function (options?: Object) {
  ...
  if (process.env.NODE_ENV !== "production") {
    // 對(duì)vm對(duì)一層攔截處理,當(dāng)使用vm上沒(méi)有的屬性時(shí)將告警      
    initProxy(vm)
  } else {
    vm._renderProxy = vm
  }
  ...
}

如果是生產(chǎn)環(huán)境,vm._renderProxy直接就是vm;開(kāi)發(fā)環(huán)境下,執(zhí)行initProxy(vm),找到定義:

initProxy = function initProxy (vm) {
  if (hasProxy) {
    // determine which proxy handler to use
    const options = vm.$options
    const handlers = options.render && options.render._withStripped
      ? getHandler
      : hasHandler
    // 對(duì)vm對(duì)一層攔截處理
    vm._renderProxy = new Proxy(vm, handlers)
  } else {
    vm._renderProxy = vm
  }
}

先判斷當(dāng)前是否支持Proxy(ES6新語(yǔ)法),支持的話會(huì)實(shí)例化一個(gè)Proxy, 當(dāng)前例子用的是hasHandler(只要判斷是否vm上有無(wú)屬性即可),這樣每次通過(guò)vm._renderProxy訪問(wèn)vm時(shí),都必須經(jīng)過(guò)這層代理:

// 判斷對(duì)象是否有某個(gè)屬性
const hasHandler = {
  has (target, key) {
    // vm中是否有key屬性
    const has = key in target
    // 當(dāng)key是全局變量或者key是私有屬性且key沒(méi)有在$data中,允許訪問(wèn)該key
    const isAllowed = allowedGlobals(key) ||
      (typeof key === "string" && key.charAt(0) === "_" && !(key in target.$data))
    // 沒(méi)有該屬性且不允許訪問(wèn)該屬性時(shí)發(fā)起警告
    if (!has && !isAllowed) {
      if (key in target.$data) warnReservedPrefix(target, key)
      else warnNonPresent(target, key)
    }
    return has || !isAllowed
  }
}

所以,_render中的vnode = render.call(vm._renderProxy, vm.$createElement),實(shí)際上是執(zhí)行vm._renderProxy.render(vm.$createElement)

Virtual DOM 虛擬dom

vue.2.0中引入了virtual dom ,大大提升了代碼的性能。所謂virtual dom ,就是用js對(duì)象去描述一個(gè)dom節(jié)點(diǎn),這比真實(shí)創(chuàng)建dom快很多。在vue中,Virtual dom是用類vnode來(lái)表示,vnodesrc/core/vdom/vnode.js中定義,有真實(shí)dom上也有的屬性,像tag/text/key/data/children等,還有些是vue的特色屬性,在渲染過(guò)程也會(huì)用到.

vm.$createElement

vue文檔中介紹了render函數(shù),第一個(gè)參數(shù)就是createElement,之前的例子轉(zhuǎn)換成render函數(shù)就是:

{{ message }}
// 轉(zhuǎn)換成render: render: function (createElement) { return createElement("div", { attrs: { id: "app" }, }, this.message) }

可以看出,createElement就是vm.$createElement

找到vm.$createElement定義,在initRender方法中,

// bind the createElement fn to this instance
// so that we get proper render context inside it.
// args order: tag, data, children, normalizationType, alwaysNormalize
// internal version is used by render functions compiled from templates
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
// normalization is always applied for the public version, used in
// user-written render functions. 
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)

看到這里定義了2個(gè)實(shí)例方法都是調(diào)用的createElement,一個(gè)是用于編譯生成的render方法,一個(gè)是用于手寫render方法,createElement最后會(huì)返回Vnode,來(lái)看下createElement的定義:

export function createElement (
  context: Component, //vm實(shí)例
  tag: any,
  data: any, //可以不傳
  children: any,// 子節(jié)點(diǎn)
  normalizationType: any,
  alwaysNormalize: boolean
) {
  // 參數(shù)判斷,不傳data時(shí),要把children,normalizationType參數(shù)往前移
  if (Array.isArray(data) || isPrimitive(data)) {
    normalizationType = children
    children = data
    data = undefined
  }
  if (isTrue(alwaysNormalize)) {
    normalizationType = ALWAYS_NORMALIZE
  }
  return _createElement(context, tag, data, children, normalizationType)
}

先經(jīng)過(guò)參數(shù)重載,根據(jù)alwaysNormalize傳不同的normalizationType,調(diào)用_createElement(),實(shí)際上createElement是提前對(duì)參數(shù)做了一層處理
這里的參數(shù)重載有個(gè)小點(diǎn)值得注意,normalizationType是關(guān)系到后面children的扁平處理,沒(méi)有children則不需要對(duì)normalizationType賦值,childrennormalizationType就都是空值

_createElement()

首先校驗(yàn)data,data是響應(yīng)式的,調(diào)用createEmptyVNode直接返回注釋節(jié)點(diǎn):

export const createEmptyVNode = (text: string = "") => {
  const node = new VNode()
  node.text = text
  node.isComment = true//注釋vnode
  return node
}

處理tag,沒(méi)有tag時(shí)也返回注釋節(jié)點(diǎn)

key做基礎(chǔ)類型校驗(yàn)

當(dāng)children中有function類型作slot處理,此處先不作分析

對(duì)childrennormalize 變成vnode一維數(shù)組,有2種不同的方式:normalizeChildrensimpleNormalizeChildren

創(chuàng)建vnode

simpleNormalizeChildren

normalizeChildrensimpleNormalizeChildren是2種對(duì)children扁平化處理的方法,先來(lái)看下simpleNormalizeChildren定義:

export function simpleNormalizeChildren (children: any) {
  for (let i = 0; i < children.length; i++) {
    // 把嵌套數(shù)組拍平成一維數(shù)組
    if (Array.isArray(children[i])) {
      return Array.prototype.concat.apply([], children)
    }
  }
  return children
}

如果chilren中有一個(gè)是數(shù)組則將整個(gè)children作為參數(shù)組用concat連接,可以得到每個(gè)子元素都是vnodechildren,這適用于只有一級(jí)嵌套數(shù)組的情況

normalizeChildren
export function normalizeChildren (children: any): ?Array {
  // 判斷是否基礎(chǔ)類型,是:創(chuàng)建文本節(jié)點(diǎn),否:判斷是否數(shù)組,是:作normalizeArrayChildren處理
  return isPrimitive(children)
    ? [createTextVNode(children)]
    : Array.isArray(children)
      ? normalizeArrayChildren(children)
      : undefined
}

普通的children處理:最后也是返回一組一維vnode的數(shù)組,當(dāng)children是Array時(shí),執(zhí)行normalizeArrayChildren

normalizeArrayChildren

代碼較長(zhǎng),此處就不貼了,可以自己對(duì)照源碼來(lái)分析:

定義res

遍歷children,當(dāng)children[i]是空或者是布爾值,跳過(guò)該次循環(huán)

如果children[i]還是個(gè)數(shù)組,再對(duì)children[i]作normalizeArrayChildren處理

if (Array.isArray(c)) {
  if (c.length > 0) {
    c = normalizeArrayChildren(c, `${nestedIndex || ""}_${i}`)// 返回vnode數(shù)組
    // merge adjacent text nodes 
    // 優(yōu)化:如果c的第一個(gè)vnode和children上一次處理的vnode都是文本節(jié)點(diǎn)可以合并成一個(gè)vnode
    if (isTextNode(c[0]) && isTextNode(last)) {
      res[lastIndex] = createTextVNode(last.text + (c[0]: any).text)
      c.shift()
    }
    res.push.apply(res, c)
  }
} else if (){...}

children[i]是基礎(chǔ)類型時(shí)

} else if (isPrimitive(c)) {
  // 當(dāng)c是基礎(chǔ)類型時(shí)
  // children上一次處理的vnode是文本節(jié)點(diǎn),則合并成一個(gè)文本節(jié)點(diǎn)
  if (isTextNode(last)) {
    // merge adjacent text nodes
    // this is necessary for SSR hydration because text nodes are
    // essentially merged when rendered to HTML strings
    // 這是SSR hydration所必需的,因?yàn)槲谋竟?jié)點(diǎn)渲染成html時(shí)基本上都是合并的
    res[lastIndex] = createTextVNode(last.text + c)
  } else if (c !== "") {
    // convert primitive to vnode
    res.push(createTextVNode(c))// c不為空直接創(chuàng)建文本節(jié)點(diǎn)
  }
} else {

其它情況,children[i]是vnode時(shí),

} else {// 當(dāng)c是vnode時(shí)
  if (isTextNode(c) && isTextNode(last)) {
    // merge adjacent text nodes
    res[lastIndex] = createTextVNode(last.text + c.text)
  } else {
    // default key for nested array children (likely generated by v-for)
    // 特殊處理,先略過(guò)
    if (isTrue(children._isVList) &&
      isDef(c.tag) &&
      isUndef(c.key) &&
      isDef(nestedIndex)) {
      c.key = `__vlist${nestedIndex}_${i}__`
    }
    // push到res上
    res.push(c)
  }
}

最后返回一組vnode

主要有2個(gè)點(diǎn),一是normalizeArrayChildren的遞歸調(diào)用,二是文本節(jié)點(diǎn)的合并

創(chuàng)建vnode

創(chuàng)建vnode,并返回

判斷tag類型,為字符串時(shí):

let Ctor
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
// 判斷tag是否是原生標(biāo)簽
if (config.isReservedTag(tag)) {
  // platform built-in elements
  vnode = new VNode(
    config.parsePlatformTagName(tag), data, children,
    undefined, undefined, context
  )
} else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, "components", tag))) {
  // component組件部分先略過(guò)
  vnode = createComponent(Ctor, data, context, children, tag)
} else {
  // unknown or unlisted namespaced elements
  // check at runtime because it may get assigned a namespace when its
  // parent normalizes children
  // 未知標(biāo)簽,創(chuàng)建vnode
  vnode = new VNode(
    tag, data, children,
    undefined, undefined, context
  )
}

tag不是字符串類型時(shí),vnode = createComponent(tag, data, context, children),先略過(guò)

最后再對(duì)生成的vnode作校驗(yàn),返回vnode

小結(jié)

到此為止,我們分析了vm._render方法和_createElement方法,知道了創(chuàng)建vnode的整個(gè)過(guò)程,在$mount中的 vm._update(vm._render(), hydrating)vm._render返回了vnode,再傳入vm._update中,由vm._update渲染成真實(shí)dom。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/101023.html

相關(guān)文章

  • Vue2.5筆記Vue的模版

    摘要:模版語(yǔ)法中的模版是基于的模版語(yǔ)法,所有的模版都是合法的,所以能被遵循規(guī)范的瀏覽器和解析器解析。表達(dá)式模版中不僅僅可以進(jìn)行簡(jiǎn)單的數(shù)據(jù)綁定操作,我們還可以在模版中進(jìn)行簡(jiǎn)單的表達(dá)式。我們也簡(jiǎn)單的敘述了模版編譯的整個(gè)流程。 我們?cè)谏弦黄f(shuō)到如何把 Vue 實(shí)例中的數(shù)據(jù)顯示到視圖中,就會(huì)需要用到我們的模版,我們只是簡(jiǎn)單的使用了一些,模版其實(shí)還有很多其他的特性。今天我們就來(lái)看看模版的其他特性。 模...

    shevy 評(píng)論0 收藏0
  • 關(guān)于Vue2一些值得推薦的文章 -- 五、六月份

    摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥(niǎo)雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...

    sutaking 評(píng)論0 收藏0
  • 關(guān)于Vue2一些值得推薦的文章 -- 五、六月份

    摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥(niǎo)雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...

    khs1994 評(píng)論0 收藏0
  • 2017-09-24 前端日?qǐng)?bào)

    摘要:前端日?qǐng)?bào)精選未來(lái)布局之星更快地構(gòu)建使用預(yù)解析以及深入的虛擬原理原來(lái)與是這樣阻塞解析和渲染的怎樣把網(wǎng)站升級(jí)到中文視頻從談函數(shù)式與響應(yīng)式編程葉俊星系列三之煙花效果實(shí)現(xiàn)掘金的故事解剖表情動(dòng)圖的構(gòu)成設(shè)計(jì)系列傳統(tǒng)遞歸和尾調(diào)用的實(shí)現(xiàn)前端架構(gòu)經(jīng) 2017-09-24 前端日?qǐng)?bào) 精選 未來(lái)布局之星Grid更快地構(gòu)建DOM: 使用預(yù)解析, async, defer 以及 preload_JavaScri...

    yuanzhanghu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<