日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > vue >内容正文

vue

baidumap vue 判断范围_vue 数据渲染

發(fā)布時間:2023/12/20 vue 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 baidumap vue 判断范围_vue 数据渲染 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
本文轉(zhuǎn)載于 SegmentFault 社區(qū)社區(qū)專欄:山外de樓作者:山外de樓


??

前言

?vue 是如何將編譯器中的代碼轉(zhuǎn)換為頁面真實元素的?這個過程涉及到模板編譯成 AST 語法樹,AST 語法樹構(gòu)建渲染函數(shù),渲染函數(shù)生成虛擬 dom,虛擬 dom 編譯成真實 dom 這四個過程。本文著重分析后兩個過程。
?

整體流程

解讀代碼之前,先看一張 vue 編譯和渲染的整體流程圖:vue 會把用戶寫的代碼中的 標(biāo)簽中的代碼解析成 AST 語法樹,再將處理后的 AST 生成相應(yīng)的 render 函數(shù),render 函數(shù)執(zhí)行后會得到與模板代碼對應(yīng)的虛擬 dom,最后通過虛擬 dom 中新舊 vnode 節(jié)點的對比和更新,渲染得到最終的真實 dom。有了這個整體的概念我們再來結(jié)合源碼分析具體的數(shù)據(jù)渲染過程。
??

從 vm.$mount 開始

vue 中是通過 mount 實例方法去掛載 vm 的,數(shù)據(jù)渲染的過程就發(fā)生在vm.mount 階段。在這個方法中,最終會調(diào)用 mountComponent方法來完成數(shù)據(jù)的渲染。我們結(jié)合源碼看一下其中的幾行關(guān)鍵代碼: updateComponent = () => {
vm._update(vm._render(), hydrating) // 生成虛擬dom,并更新真實dom
}這是在 mountComponent 方法的內(nèi)部,會定義一個 updateComponent方法,在這個方法中 vue 會通過 vm._render()函數(shù)生成虛擬 dom,并將生成的 vnode 作為第一個參數(shù)傳入 vm._update()函數(shù)中進而完成虛擬 dom 到真實 dom 的渲染。第二個參數(shù) hydrating是跟服務(wù)端渲染相關(guān)的,在瀏覽器中不需要關(guān)心。這個函數(shù)最后會作為參數(shù)傳入到 vue 的 watch 實例中作為 getter函數(shù),用于在數(shù)據(jù)更新時觸發(fā)依賴收集,完成數(shù)據(jù)響應(yīng)式的實現(xiàn)。這個過程不在本文的介紹范圍內(nèi),在這里只要明白,當(dāng)后續(xù) vue 中的 data 數(shù)據(jù)變化時,都會觸發(fā) updateComponent 方法,完成頁面數(shù)據(jù)的渲染更新。具體的關(guān)鍵代碼如下: new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
// 觸發(fā)beforeUpdate鉤子
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
hydrating = false

// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
// 觸發(fā)mounted鉤子
callHook(vm, 'mounted')
}
return vm
}代碼中還有一點需要注意的是,在代碼結(jié)束處,會做一個判斷,當(dāng) vm 掛載成功后,會調(diào)用 vue 的 mounted 生命周期鉤子函數(shù)。這也就是為什么我們在 mounted 鉤子中執(zhí)行代碼時,vm 已經(jīng)掛載完成的原因。
?

vm._render()

接下來具體分析 vue 生成虛擬 dom 的過程。前面說了這一過程是調(diào)用 vm._render()方法來完成的,該方法的核心邏輯是調(diào)用 vm.$createElement 方法生成 vnode,代碼如下:vnode = render.call(vm._renderProxy, vm.$createElement)其中 vm.renderProxy是個代理,代理 vm,做一些錯誤處理,vm.$createElement是創(chuàng)建 vnode 的真正方法,該方法的定義如下:vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)可見最終調(diào)用的是 createElement方法來實現(xiàn)生成 vnode 的邏輯。在進一步介紹 createElement 方法之前,我們先理清楚兩個個關(guān)鍵點:1. render的函數(shù)來源2. vnode到底是什么

render 方法的來源

在 vue 內(nèi)部其實定義了兩種 render 方法的來源,一種是如果用戶手寫了 render 方法,那么 vue 會調(diào)用這個用戶自己寫的 render 方法,即下面代碼中的 vm.$createElement;另外一種是用戶沒有手寫 render 方法,那么vue內(nèi)部會把 template 編譯成 render 方法,即下面代碼中的 vm._c。不過這兩個 render 方法最終都會調(diào)用 createElement 方法來生成虛擬 dom。// 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) vnode 類vnode 就是用一個原生的 js 對象去描述 dom 節(jié)點的類。因為瀏覽器操作 dom 的成本是很高的,所以利用 vnode 生成虛擬 dom 比創(chuàng)建一個真實 dom 的代價要小很多。vnode 類的定義如下:export default class VNode {
tag: string | void; // 當(dāng)前節(jié)點的標(biāo)簽名
data: VNodeData | void; // 當(dāng)前節(jié)點對應(yīng)的對象
children: ?Array; // 當(dāng)前節(jié)點的子節(jié)點
text: string | void; // 當(dāng)前節(jié)點的文本
elm: Node | void; // 當(dāng)前虛擬節(jié)點對應(yīng)的真實dom節(jié)點
..../*創(chuàng)建一個空VNode節(jié)點*/export const createEmptyVNode = (text: string = '') => {const node = new VNode()
node.text = text
node.isComment = truereturn node
}/*創(chuàng)建一個文本節(jié)點*/export function createTextVNode (val: string | number) {return new VNode(undefined, undefined, undefined, String(val))
}
....可以看到 vnode 類中仿照真實 dom 定義了很多節(jié)點屬性和一系列生成各類節(jié)點的方法。通過對這些屬性和方法的操作來達到模仿真實 dom 變化的目的。

createElement

有了前面兩點的知識儲備,接下來回到 createElement 生成虛擬 dom 的分析。createElement 方法中的代碼很多,這里只介紹跟生成虛擬 dom 相關(guān)的代碼。該方法總體來說就是創(chuàng)建并返回一個 vnode 節(jié)點。在這個過程中可以拆分成三件事情:1. 子節(jié)點的規(guī)范化處理;2. 根據(jù)不同的情形創(chuàng)建不同的 vnode 節(jié)點類型;3. vnode 創(chuàng)建后的處理。下面開始分析這 3 個步驟:
子節(jié)點的規(guī)范化處理
if (normalizationType === ALWAYS_NORMALIZE) {
children = normalizeChildren(children)
} else if (normalizationType === SIMPLE_NORMALIZE) {
children = simpleNormalizeChildren(children)
}為什么會有這個過程,是因為傳入的參數(shù)中的子節(jié)點是 any 類型,而 vue 最終生成的虛擬 dom 實際上是一個樹狀結(jié)構(gòu),每一個 vnode 可能會有若干個子節(jié)點,這些子節(jié)點應(yīng)該也是 vnode 類型。所以需要對子節(jié)點處理,將子節(jié)點統(tǒng)一處理成一個 vnode 類型的數(shù)組。同時還需要根據(jù) render 函數(shù)的來源不同,對子節(jié)點的數(shù)據(jù)結(jié)構(gòu)進行相應(yīng)處理。
創(chuàng)建 vnode 節(jié)點
這部分邏輯是對 tag 標(biāo)簽在不同情況下的處理,梳理一下具體的判斷case如下:1. 如果傳入的 tag 標(biāo)簽是字符串,則進一步進入下列第 2 點和第 3 點判斷,如果不是字符串則創(chuàng)建一個組件類型 vnode 節(jié)點。2. 如果是內(nèi)置的標(biāo)簽,則創(chuàng)建一個相應(yīng)的內(nèi)置標(biāo)簽 vnode 節(jié)點。3. 如果是一個組件標(biāo)簽,則創(chuàng)建一個組件類型 vnode 節(jié)點。4. 其他情況下,則創(chuàng)建一個命名空間未定義的 vnode 節(jié)點。 let vnode, ns
if (typeof tag === 'string') {
let Ctor
// 獲取tag的名字空間
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)

// 判斷是否是內(nèi)置的標(biāo)簽,如果是內(nèi)置的標(biāo)簽則創(chuàng)建一個相應(yīng)節(jié)點
if (config.isReservedTag(tag)) {
// platform built-in elements
if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.nativeOn)) {
warn(
`The .native modifier for v-on is only valid on components but it was used on .`,
context
)
}
vnode = new VNode(
config.parsePlatformTagName(tag), data, children,
undefined, undefined, context
)
// 如果是組件,則創(chuàng)建一個組件類型節(jié)點
// 從vm實例的option的components中尋找該tag,存在則就是一個組件,創(chuàng)建相應(yīng)節(jié)點,Ctor為組件的構(gòu)造類
} else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
// component
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

//其他情況,在運行時檢查,因為父組件可能在序列化子組件的時候分配一個名字空間
vnode = new VNode(
tag, data, children,
undefined, undefined, context
)
}
} else {
// direct component options / constructor
// tag不是字符串的時候則是組件的構(gòu)造類,創(chuàng)建一個組件節(jié)點
vnode = createComponent(tag, data, context, children)
}vnode 創(chuàng)建后的處理這部分同樣也是一些 if/else 分情況的處理邏輯:1. 如果 vnode 成功創(chuàng)建,且是一個數(shù)組類型,則返回創(chuàng)建好的 vnode 節(jié)點2. 如果 vnode 成功創(chuàng)建,且有命名空間,則遞歸所有子節(jié)點應(yīng)用該命名空間3. 如果 vnode 沒有成功創(chuàng)建則創(chuàng)建并返回一個空的 vnode 節(jié)點 if (Array.isArray(vnode)) {
// 如果vnode成功創(chuàng)建,且是一個數(shù)組類型,則返回創(chuàng)建好的vnode節(jié)點
return vnode
} else if (isDef(vnode)) {
// 如果vnode成功創(chuàng)建,且名字空間,則遞歸所有子節(jié)點應(yīng)用該名字空間
if (isDef(ns)) applyNS(vnode, ns)
if (isDef(data)) registerDeepBindings(data)
return vnode
} else {
// 如果vnode沒有成功創(chuàng)建則創(chuàng)建空節(jié)點
return createEmptyVNode()
}

vm._update()

vm._update()做的事情就是把 vm._render()生成的虛擬 dom 渲染成真實 dom。_update()方法內(nèi)部會調(diào)用 vm.__patch__ 方法來完成視圖更新,最終調(diào)用的是 createPatchFunction方法,該方法的代碼量和邏輯都非常多,它定義在 src/core/vdom/patch.js文件中。下面介紹下具體的 patch 流程和流程中用到的重點方法:

重點方法

1. createElm:該方法會根據(jù)傳入的虛擬 dom 節(jié)點創(chuàng)建真實的 dom 并插入到它的父節(jié)點中2. sameVnode:判斷新舊節(jié)點是否是同一節(jié)點。3. patchVnode:當(dāng)新舊節(jié)點是相同節(jié)點時,調(diào)用該方法直接修改節(jié)點,在這個過程中,會利用 diff 算法,循環(huán)進行子節(jié)點的的比較,進而進行相應(yīng)的節(jié)點復(fù)用或者替換。4. updateChildren方法:diff 算法的具體實現(xiàn)過程
?

patch 流程

第一步:
判斷舊節(jié)點是否存在,如果不存在就調(diào)用 createElm() 創(chuàng)建一個新的 dom 節(jié)點,否則進入第二步判斷。 if (isUndef(oldVnode)) {
// empty mount (likely as component), create new root element
isInitialPatch = true
createElm(vnode, insertedVnodeQueue)
}
第二步:
通過 sameVnode() 判斷新舊節(jié)點是否是同一節(jié)點,如果是同一個節(jié)點則調(diào)用 patchVnode() 直接修改現(xiàn)有的節(jié)點,否則進入第三步判斷。const isRealElement = isDef(oldVnode.nodeType)
if (!isRealElement && sameVnode(oldVnode, vnode)) {
// patch existing root node
/*是同一個節(jié)點的時候直接修改現(xiàn)有的節(jié)點*/
patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
}
第三步:
如果新舊節(jié)點不是同一節(jié)點,則調(diào)用 createElm()創(chuàng)建新的 dom,并更新父節(jié)點的占位符,同時移除舊節(jié)點。else {
....
createElm(
vnode,
insertedVnodeQueue,
// extremely rare edge case: do not insert if old element is in a
// leaving transition. Only happens when combining transition +
// keep-alive + HOCs. (#4590)
oldElm._leaveCb ? null : parentElm,
nodeOps.nextSibling(oldElm)
)
// update parent placeholder node element, recursively
/*更新父的占位符節(jié)點*/
if (isDef(vnode.parent)) {
let ancestor = vnode.parent
const patchable = isPatchable(vnode)
while (ancestor) {
for (let i = 0; i < cbs.destroy.length; ++i) {
cbs.destroy[i](ancestor) /*調(diào)用destroy回調(diào)*/
}
ancestor.elm = vnode.elm
if (patchable) {
for (let i = 0; i < cbs.create.length; ++i) {
cbs.create[i](emptyNode, ancestor) /*調(diào)用create回調(diào)*/
}
// #6513
// invoke insert hooks that may have been merged by create hooks.
// e.g. for directives that uses the "inserted" hook.
const insert = ancestor.data.hook.insert
if (insert.merged) {
// start at index 1 to avoid re-invoking component mounted hook
for (let i = 1; i < insert.fns.length; i++) {
insert.fns[i]()
}
}
} else {
registerRef(ancestor)
}
ancestor = ancestor.parent
}
}
// destroy old node
if (isDef(parentElm)) {
removeVnodes([oldVnode], 0, 0) /* 刪除舊節(jié)點 */
} else if (isDef(oldVnode.tag)) {
invokeDestroyHook(oldVnode) /* 調(diào)用destroy鉤子 */
}
}第四步:返回 vnode.elm,即最后生成的虛擬 dom 對應(yīng)的真實 dom,將 vm.$el 賦值為這個 dom 節(jié)點,完成掛載。其中重點的過程在第二步和第三步中,特別是 diff 算法對新舊節(jié)點的比較和更新很有意思。

其他注意點

sameVnode 的實際應(yīng)用

在 patch 的過程中,如果兩個節(jié)點被判斷為同一節(jié)點,會進行復(fù)用。這里的判斷標(biāo)準(zhǔn)是:1. key 相同2. tag(當(dāng)前節(jié)點的標(biāo)簽名)相同3. isComment(是否為注釋節(jié)點)相同4. data 的屬性相同平時寫 vue 時會遇到一個組件中用到了 A 和 B 兩個相同的子組件,可以來回切換。有時候會出現(xiàn)改變了 A 組件中的值,切到 B 組件中,發(fā)現(xiàn) B 組件的值也被改變成和 A 組件一樣了。這就是因為 vue 在 patch 的過程中,判斷出了 A 和 B 是 sameVnode,直接進行復(fù)用引起的。根據(jù)源碼的解讀,可以很容易地解決這個問題,就是給 A 和 B 組件分別加上不同的 key 值,避免 A 和 B 被判斷為同一組件。

虛擬 DOM 如何映射到真實的 DOM 節(jié)點

vue 為平臺做了一層適配層,瀏覽器平臺的代碼在 /platforms/web/runtime/node-ops.js。不同平臺之間通過適配層對外提供相同的接口,虛擬 dom 映射轉(zhuǎn)換真實 dom 節(jié)點的時候,只需要調(diào)用這些適配層的接口即可,不需要關(guān)心內(nèi)部的實現(xiàn)。最后通過上述的源碼和實例的分析,我們完成了 Vue 中數(shù)據(jù)渲染的完整解讀。
-?END - 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結(jié)

以上是生活随笔為你收集整理的baidumap vue 判断范围_vue 数据渲染的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。