欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue中的虛擬dom分享

 更新時(shí)間:2024年12月26日 11:17:08   作者:歸途風(fēng)景111  
虛擬DOM是一種用JavaScript對象來描述真實(shí)DOM的技術(shù),通過diff算法實(shí)現(xiàn)高效的DOM更新,提高頁面性能,Vue通過render函數(shù)和VNode實(shí)現(xiàn)虛擬DOM,結(jié)合diff算法減少DOM操作,提升用戶體驗(yàn)

一、什么是虛擬DOM

通過js創(chuàng)建一個(gè)Object對象來模擬真實(shí)DOM結(jié)構(gòu),這個(gè)對象包含標(biāo)簽名 (tag)、屬性 (attrs) 和子元素對象 (children) 三個(gè)屬性,通過vue中的render()函數(shù)把虛擬dom編譯成真實(shí)dom,在通過appendChild()添加到頁面中。

先來看看下面的這兩個(gè)例子

  • 第一個(gè):
      class Element {
            constructor(tagName, attrs, children) {
                this.tagName = tagName;
                this.attrs = attrs || {};
                this.children = children || [];
            }
            render() {
                //這個(gè)函數(shù)是用來生成真實(shí)DOM的,最后會把return的結(jié)果添加到頁面中去
            }
        }
  • 第二個(gè):
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul id="list">
        <li class="a">txt_a</li>
        <li class="a">txt_b</li>
    </ul>
</body>
<script>
    //創(chuàng)建Virtual Dom Tree
    let ul = Element('ul', {  id: 'list'}, [
            Element('li', {class: 'a'}, ['txt_a']),
            Element('li', {class: 'b'}, ['txt_b'])
        ]);
        let ulDom=ul.render(); //生成真實(shí)Dom
        document.body.appendChild(ulDom)
        
</script>

</html>

創(chuàng)建虛擬DOM就是為了更好將虛擬的節(jié)點(diǎn)渲染到頁面視圖中,所以虛擬DOM對象的節(jié)點(diǎn)與真實(shí)DOM的屬性一一照應(yīng)

在vue中如何應(yīng)用虛擬DOM的

  • 定義真實(shí)DOM
<div id="app">
    <p class="p">節(jié)點(diǎn)內(nèi)容</p>
    <h3>{{ foo }}</h3>
</div>
  • 實(shí)例化vue
const app = new Vue({
    el:"#app",
    data:{
        foo:"foo"
    }
})
  • vue源碼中render函數(shù)渲染虛擬DOM
(function anonymous() {
 with(this){return _c('div',{attrs:{"id":"app"}},[_c('p',{staticClass:"p"},
       [_v("節(jié)點(diǎn)內(nèi)容")]),_v(" "),_c('h3',[_v(_s(foo))])])}})

通過VNode,vue可以對這顆抽象樹進(jìn)行創(chuàng)建節(jié)點(diǎn),刪除節(jié)點(diǎn)以及修改節(jié)點(diǎn)的操作,經(jīng)過diff算法得出一些需要修改的最小單位,再更新視圖,減少了dom操作,提高了性能

二、為什么需要虛擬DOM

DOM是很慢的,其元素非常龐大,頁面的性能問題,大部分都是由DOM操作引起的

真實(shí)的DOM節(jié)點(diǎn),哪怕一個(gè)最簡單的div也包含著很多屬性,可以打印出來直觀感受一下:

由此可見,操作DOM的代價(jià)仍舊是昂貴的,頻繁操作還是會出現(xiàn)頁面卡頓,影響用戶的體驗(yàn)。

舉個(gè)例子

你用傳統(tǒng)的原生api或jQuery去操作DOM時(shí),瀏覽器會從構(gòu)建DOM樹開始從頭到尾執(zhí)行一遍流程

當(dāng)你在一次操作時(shí),需要更新10個(gè)DOM節(jié)點(diǎn),瀏覽器沒這么智能,收到第一個(gè)更新DOM請求后,并不知道后續(xù)還有9次更新操作,因此會馬上執(zhí)行流程,最終執(zhí)行10次流程

而通過VNode,同樣更新10個(gè)DOM節(jié)點(diǎn),虛擬DOM不會立即操作DOM,而是將這10次更新的diff內(nèi)容保存到本地的一個(gè)js對象中,最終將這個(gè)js對象一次性attach到DOM樹上,避免大量的無謂計(jì)算

  • vue中虛擬 DOM 最大的優(yōu)勢是 diff 算法,減少 JavaScript 操作真實(shí) DOM 的帶來的性能消耗。雖然這一個(gè)虛擬 DOM帶來的一個(gè)優(yōu)勢,但并不是全部。
  • 其它框架中也有虛擬 DOM概念, 最大的優(yōu)勢在于抽象了原本的渲染過程,實(shí)現(xiàn)了跨平臺的能力,而不僅僅局限于瀏覽器的 DOM,可以是安卓和 IOS 的原生組件,可以是近期很火熱的小程序,也可以是各種GUI。

三、如何實(shí)現(xiàn)虛擬DOM

首先可以看看vue中VNode的結(jié)構(gòu)

  • vnode.js
export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component's scope
  functionalContext: Component | void; // only for functional component root nodes
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node
  raw: boolean; // contains raw HTML? (server only)
  isStatic: boolean; // hoisted static node
  isRootInsert: boolean; // necessary for enter transition check
  isComment: boolean; // empty comment placeholder?
  isCloned: boolean; // is a cloned node?
  isOnce: boolean; // is a v-once node?

  constructor (
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions
  ) {
    /*當(dāng)前節(jié)點(diǎn)的標(biāo)簽名*/
    this.tag = tag
    /*當(dāng)前節(jié)點(diǎn)對應(yīng)的對象,包含了具體的一些數(shù)據(jù)信息,是一個(gè)VNodeData類型,可以參考VNodeData類型中的數(shù)據(jù)信息*/
    this.data = data
    /*當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn),是一個(gè)數(shù)組*/
    this.children = children
    /*當(dāng)前節(jié)點(diǎn)的文本*/
    this.text = text
    /*當(dāng)前虛擬節(jié)點(diǎn)對應(yīng)的真實(shí)dom節(jié)點(diǎn)*/
    this.elm = elm
    /*當(dāng)前節(jié)點(diǎn)的名字空間*/
    this.ns = undefined
    /*編譯作用域*/
    this.context = context
    /*函數(shù)化組件作用域*/
    this.functionalContext = undefined
    /*節(jié)點(diǎn)的key屬性,被當(dāng)作節(jié)點(diǎn)的標(biāo)志,用以優(yōu)化*/
    this.key = data && data.key
    /*組件的option選項(xiàng)*/
    this.componentOptions = componentOptions
    /*當(dāng)前節(jié)點(diǎn)對應(yīng)的組件的實(shí)例*/
    this.componentInstance = undefined
    /*當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)*/
    this.parent = undefined
    /*簡而言之就是是否為原生HTML或只是普通文本,innerHTML的時(shí)候?yàn)閠rue,textContent的時(shí)候?yàn)閒alse*/
    this.raw = false
    /*靜態(tài)節(jié)點(diǎn)標(biāo)志*/
    this.isStatic = false
    /*是否作為跟節(jié)點(diǎn)插入*/
    this.isRootInsert = true
    /*是否為注釋節(jié)點(diǎn)*/
    this.isComment = false
    /*是否為克隆節(jié)點(diǎn)*/
    this.isCloned = false
    /*是否有v-once指令*/
    this.isOnce = false
  }

  // DEPRECATED: alias for componentInstance for backwards compat.
  /* istanbul ignore next https://github.com/answershuto/learnVue*/
  get child (): Component | void {
    return this.componentInstance
  }
}

這里對VNode進(jìn)行稍微的說明:

  • 所有對象的 context 選項(xiàng)都指向了 Vue 實(shí)例
  • elm 屬性則指向了其相對應(yīng)的真實(shí) DOM 節(jié)點(diǎn)vue是通過createElement生成VNode

源碼create-element.js

export function createElement (
  context: Component,
  tag: any,
  data: any,
  children: any,
  normalizationType: any,
  alwaysNormalize: boolean
): VNode | Array<VNode> {
  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)
}

上面可以看到createElement 方法實(shí)際上是對 _createElement 方法的封裝,對參數(shù)的傳入進(jìn)行了判斷

export function _createElement(
    context: Component,
    tag?: string | Class<Component> | Function | Object,
    data?: VNodeData,
    children?: any,
    normalizationType?: number
): VNode | Array<VNode> {
    if (isDef(data) && isDef((data: any).__ob__)) {
        process.env.NODE_ENV !== 'production' && warn(
            `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
            'Always create fresh vnode data objects in each render!',
            context`
        )
        return createEmptyVNode()
    }
    // object syntax in v-bind
    if (isDef(data) && isDef(data.is)) {
        tag = data.is
    }
    if (!tag) {
        // in case of component :is set to falsy value
        return createEmptyVNode()
    }
    // support single function children as default scoped slot
    if (Array.isArray(children) &&
        typeof children[0] === 'function'
    ) {
        data = data || {}
        data.scopedSlots = { default: children[0] }
        children.length = 0
    }
    if (normalizationType === ALWAYS_NORMALIZE) {
        children = normalizeChildren(children)
    } else if ( === SIMPLE_NORMALIZE) {
        children = simpleNormalizeChildren(children)
    }
 // 創(chuàng)建VNode
}

可以看到_createElement接收5個(gè)參數(shù):

  1. context 表示 VNode 的上下文環(huán)境,是 Component 類型
  2. tag 表示標(biāo)簽,它可以是一個(gè)字符串,也可以是一個(gè) Component
  3. data 表示 VNode 的數(shù)據(jù),它是一個(gè) VNodeData 類型
  4. children 表示當(dāng)前 VNode的子節(jié)點(diǎn),它是任意類型的
  5. normalizationType 表示子節(jié)點(diǎn)規(guī)范的類型,類型不同規(guī)范的方法也就不一樣,主要是參考 render 函數(shù)是編譯生成的還是用戶手寫的

根據(jù)normalizationType 的類型,children會有不同的定義

if (normalizationType === ALWAYS_NORMALIZE) {
    children = normalizeChildren(children)
} else if ( === SIMPLE_NORMALIZE) {
    children = simpleNormalizeChildren(children)
}

simpleNormalizeChildren方法調(diào)用場景是 render 函數(shù)是編譯生成的

normalizeChildren方法調(diào)用場景分為下面兩種:

render 函數(shù)是用戶手寫的

編譯 slot、v-for 的時(shí)候會產(chǎn)生嵌套數(shù)組

無論是simpleNormalizeChildren還是normalizeChildren都是對children進(jìn)行規(guī)范(使children 變成了一個(gè)類型為 VNode 的 Array),這里就不展開說了

規(guī)范化children的源碼位置在:src/core/vdom/helpers/normalzie-children.js

在規(guī)范化children后,就去創(chuàng)建VNode

let vnode, ns
// 對tag進(jìn)行判斷
if (typeof tag === 'string') {
  let Ctor
  ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
  if (config.isReservedTag(tag)) {
    // 如果是內(nèi)置的節(jié)點(diǎn),則直接創(chuàng)建一個(gè)普通VNode
    vnode = new VNode(
      config.parsePlatformTagName(tag), data, children,
      undefined, undefined, context
    )
  } else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
    // component
    // 如果是component類型,則會通過createComponent創(chuàng)建VNode節(jié)點(diǎn)
    vnode = createComponent(Ctor, data, context, children, tag)
  } else {
    vnode = new VNode(
      tag, data, children,
      undefined, undefined, context
    )
  }
} else {
  // direct component options / constructor
  vnode = createComponent(tag, data, context, children)
}

createComponent同樣是創(chuàng)建VNode

源碼位置:src/core/vdom/create-component.js

export function createComponent (
  Ctor: Class<Component> | Function | Object | void,
  data: ?VNodeData,
  context: Component,
  children: ?Array<VNode>,
  tag?: string
): VNode | Array<VNode> | void {
  if (isUndef(Ctor)) {
    return
  }
 // 構(gòu)建子類構(gòu)造函數(shù) 
  const baseCtor = context.$options._base

  // plain options object: turn it into a constructor
  if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor)
  }

  // if at this stage it's not a constructor or an async component factory,
  // reject.
  if (typeof Ctor !== 'function') {
    if (process.env.NODE_ENV !== 'production') {
      warn(`Invalid Component definition: ${String(Ctor)}`, context)
    }
    return
  }

  // async component
  let asyncFactory
  if (isUndef(Ctor.cid)) {
    asyncFactory = Ctor
    Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
    if (Ctor === undefined) {
      return createAsyncPlaceholder(
        asyncFactory,
        data,
        context,
        children,
        tag
      )
    }
  }

  data = data || {}

  // resolve constructor options in case global mixins are applied after
  // component constructor creation
  resolveConstructorOptions(Ctor)

  // transform component v-model data into props & events
  if (isDef(data.model)) {
    transformModel(Ctor.options, data)
  }

  // extract props
  const propsData = extractPropsFromVNodeData(data, Ctor, tag)

  // functional component
  if (isTrue(Ctor.options.functional)) {
    return createFunctionalComponent(Ctor, propsData, data, context, children)
  }

  // extract listeners, since these needs to be treated as
  // child component listeners instead of DOM listeners
  const listeners = data.on
  // replace with listeners with .native modifier
  // so it gets processed during parent component patch.
  data.on = data.nativeOn

  if (isTrue(Ctor.options.abstract)) {
    const slot = data.slot
    data = {}
    if (slot) {
      data.slot = slot
    }
  }

  // 安裝組件鉤子函數(shù),把鉤子函數(shù)合并到data.hook中
  installComponentHooks(data)

  //實(shí)例化一個(gè)VNode返回。組件的VNode是沒有children的
  const name = Ctor.options.name || tag
  const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
    data, undefined, undefined, undefined, context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
  )
  if (__WEEX__ && isRecyclableComponent(vnode)) {
    return renderRecyclableComponentTemplate(vnode)
  }

  return vnode
}

這里在稍微填一下createComponent生成VNode的三個(gè)關(guān)鍵流程:

  1. 構(gòu)造子類構(gòu)造函數(shù)Ctor
  2. installComponentHooks安裝組件鉤子函數(shù)
  3. 實(shí)例化 vnode

心得:

  • createElement 創(chuàng)建 VNode 的過程,每個(gè) VNode 有 children,children
  • 每個(gè)元素也是一個(gè)VNode,這樣就形成了一個(gè)虛擬樹結(jié)構(gòu),用于描述真實(shí)的DOM樹結(jié)構(gòu)

理論

想要理解虛擬dom首先要知道什么是虛擬dom?

虛擬dom可以簡單的用一句話概括,就是用普通的js對象來描述DOM結(jié)構(gòu),因?yàn)椴皇钦鎸?shí)DOM,所以稱之為虛擬DOM。

為什么要用虛擬DOM來描述真實(shí)的DOM呢?

創(chuàng)建真實(shí)DOM成本比較高,如果用 js對象來描述一個(gè)dom節(jié)點(diǎn),成本比較低,另外我們在頻繁操作dom是一種比較大的開銷。所以建議用虛擬dom來描述真實(shí)dom。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • VUE 使用中踩過的坑

    VUE 使用中踩過的坑

    本篇是我對vue使用過程中以及對一些社區(qū)朋友提問我的問題中做的一些總結(jié),感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧
    2018-02-02
  • vue2.x集成百度UEditor富文本編輯器的方法

    vue2.x集成百度UEditor富文本編輯器的方法

    這篇文章主要為大家詳細(xì)介紹了vue2.x集成百度UEditor富文本編輯器的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • vue.js動(dòng)畫中的js鉤子函數(shù)的實(shí)現(xiàn)

    vue.js動(dòng)畫中的js鉤子函數(shù)的實(shí)現(xiàn)

    這篇文章主要介紹了vue.js動(dòng)畫中的js鉤子函數(shù)的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • vue.js Router嵌套路由

    vue.js Router嵌套路由

    這篇文章主要介紹vue.js Router嵌套路由,平時(shí)訪問首頁,里面有新聞?lì)惖?home/news,還有信息類的/home/message這時(shí)候就需要使用到嵌套路由,下面我們就來具體學(xué)習(xí)嵌套路由的原理,需要的朋友可以參考一下
    2021-10-10
  • vue中axios利用blob實(shí)現(xiàn)文件瀏覽器下載方式

    vue中axios利用blob實(shí)現(xiàn)文件瀏覽器下載方式

    這篇文章主要介紹了vue中axios利用blob實(shí)現(xiàn)文件瀏覽器下載方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Vue 開發(fā)音樂播放器之歌手頁右側(cè)快速入口功能

    Vue 開發(fā)音樂播放器之歌手頁右側(cè)快速入口功能

    這篇文章主要介紹了Vue 開發(fā)音樂播放器之歌手頁右側(cè)快速入口功能,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-08-08
  • Vue腳手架搭建及創(chuàng)建Vue項(xiàng)目流程的詳細(xì)教程

    Vue腳手架搭建及創(chuàng)建Vue項(xiàng)目流程的詳細(xì)教程

    Vue腳手架指的是vue-cli,它是一個(gè)快速構(gòu)建**單頁面應(yīng)用程序(SPA)**環(huán)境配置的工具,cli是(command-line-interfac)命令行界面,下面這篇文章主要給大家介紹了關(guān)于Vue腳手架搭建及創(chuàng)建Vue項(xiàng)目流程的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • Vue.use的原理和設(shè)計(jì)源碼探究

    Vue.use的原理和設(shè)計(jì)源碼探究

    這篇文章主要為大家介紹了Vue.use的原理和設(shè)計(jì)源碼探究詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • 詳解Vue中的Props與Data細(xì)微差別

    詳解Vue中的Props與Data細(xì)微差別

    這篇文章主要介紹了詳解Vue中的Props與Data細(xì)微差別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • Vue3 axios配置以及cookie的使用方法實(shí)例演示

    Vue3 axios配置以及cookie的使用方法實(shí)例演示

    這篇文章主要介紹了Vue3 axios配置以及cookie的使用方法,需要的朋友可以參考下
    2023-02-02

最新評論