Vue虛擬Dom到真實(shí)Dom的轉(zhuǎn)換
再有一顆樹形結(jié)構(gòu)的Javascript對(duì)象后, 我們需要做的就是講這棵樹跟真實(shí)Dom樹形成映射關(guān)系。我們先回顧之前的mountComponnet 方法:
export function mountComponent(vm, el) {
vm.$el = el
...
callHook(vm, 'beforeMount')
...
const updateComponent = function () {
vm._update(vm._render())
}
...
}
我們已經(jīng)執(zhí)行完了vm._render 方法拿到了VNode, 現(xiàn)在將它作為參數(shù)傳給vm._update 方法并執(zhí)行。 vm._update這個(gè)方法的作用就是將VNode 轉(zhuǎn)為真實(shí)的Dom, 不過(guò)它有兩個(gè)執(zhí)行時(shí)機(jī):
首次渲染
當(dāng)執(zhí)行new Vue 到此時(shí)就是首次渲染了, 會(huì)將傳入的Vnode對(duì)象映射為真實(shí)的Dom。
更新頁(yè)面
數(shù)據(jù)變化會(huì)驅(qū)動(dòng)頁(yè)面發(fā)生變化, 這也是vue最獨(dú)特的特性之一, 數(shù)據(jù)改變之前和之后生成兩份VNode進(jìn)行比較, 而怎么樣在舊的VNode上做最小的改動(dòng)去渲染頁(yè)面,這樣一個(gè)diff算法還是挺復(fù)雜的。 如果再?zèng)]有先說(shuō)清楚數(shù)據(jù)響應(yīng)式是怎么回事之前,直接將diff對(duì)理解vue 的整體流程不太好。 所以這章分析首次渲染后, 下一章就是數(shù)據(jù)響應(yīng)式, 之后才是diff比較。
先來(lái)看看vm._update方法的定義:
Vue.prototype._update = function(vnode) {
... 首次渲染
vm.$el = vm.__patch__(vm.$el, vnode) // 覆蓋原來(lái)的vm.$el
...
}
這里的 vm. e l 是 之 前 在 = = m o u n t C o m p o n e n t = = 方 法 內(nèi) 就 掛 載 的 , 一 個(gè) 真 實(shí) 的 = = D o m = = 元 素 。 首 次 渲 染 會(huì) 傳 入 v m . el 是之前在 ==mountComponent== 方法內(nèi)就掛載的, 一個(gè)真實(shí)的==Dom==元素。 首次渲染會(huì)傳入 vm. el是之前在==mountComponent==方法內(nèi)就掛載的,一個(gè)真實(shí)的==Dom==元素。首次渲染會(huì)傳入vm.el 以及得到的VNode, 所以看下vm.patch 定義:
Vue.prototype.__patch__ = createPatchFunction({ nodeOps, modules })
patch 是createPatchFunction 方法內(nèi)部返回的一個(gè)方法, 它接受一個(gè)對(duì)象:
nodeOps屬性:封裝了操作原生Dom 的一些方法的集合, 如:創(chuàng)建、插入,移除這些, 我們到使用的地方咋詳解。
modules 屬性: 創(chuàng)建真實(shí)Dom 也需要生成它的如class/attrs/style 等屬性。 modules 是一個(gè)數(shù)組集合,數(shù)組的每一項(xiàng)都是這些屬性對(duì)應(yīng)的鉤子方法, 這些屬性的創(chuàng)建,更新,銷毀等都有對(duì)應(yīng)鉤子方法。 當(dāng)某一時(shí)刻需要做某件事,執(zhí)行對(duì)應(yīng)的鉤子即可。 比如它們都有create 這個(gè)鉤子方法, 如將這些create 鉤子收集到一個(gè)數(shù)組內(nèi), 需要在真實(shí)Dom上創(chuàng)建這些屬性時(shí),依次執(zhí)行數(shù)組的每一項(xiàng),也就是依次創(chuàng)建了它們。
PS: 這里modules 屬性內(nèi)的鉤子方法是區(qū)分平臺(tái)的, web, weex 以及 SSR 它們調(diào)用VNode 方法方式并不相同, 所以vue在這里又使用了函數(shù)柯里化這個(gè)騷操作, 在createPatchFunction 內(nèi)將平臺(tái)的差異化磨平, 從而 patch 方法只用接收新舊node即可。
生成Dom
這里大家記住一句話即可, 無(wú)論VNode 是什么類型的節(jié)點(diǎn), 只有三種類型的節(jié)點(diǎn)會(huì)被創(chuàng)建并插入到Dom中: 元素節(jié)點(diǎn),注釋節(jié)點(diǎn), 和文本節(jié)點(diǎn)。
我們接著看下createPatchFunction 它返回一個(gè)怎樣的方法:
export function createPatchFunction(backend) {
...
const { modules, nodeOps } = backend // 解構(gòu)出傳入的集合
return function (oldVnode, vnode) { // 接收新舊vnode
...
const isRealElement = isDef(oldVnode.nodeType) // 是否是真實(shí)Dom
if(isRealElement) { // $el是真實(shí)Dom
oldVnode = emptyNodeAt(oldVnode) // 轉(zhuǎn)為VNode格式覆蓋自己
}
...
}
}
首次渲染時(shí)沒(méi)有oldVnode, oldVnode 就是 $el, 一個(gè)真實(shí)的dom, 經(jīng)過(guò)emptyNodeAt(odVnode) 方法包裝:
function emptyNodeAt(elm) {
return new VNode(
nodeOps.tagName(elm).toLowerCase(), // 對(duì)應(yīng)tag屬性
{}, // 對(duì)應(yīng)data
[], // 對(duì)應(yīng)children
undefined, //對(duì)應(yīng)text
elm // 真實(shí)dom賦值給了elm屬性
)
}
包裝后的:
{
tag: 'div',
elm: '<div id="app"></div>' // 真實(shí)dom
}
-------------------------------------------------------
nodeOps:
export function tagName (node) { // 返回節(jié)點(diǎn)的標(biāo)簽名
return node.tagName
}
在將傳入的==$el== 屬性轉(zhuǎn)為了VNode 格式之后,我們繼續(xù):
export function createPatchFunction(backend) {
...
return function (oldVnode, vnode) { // 接收新舊vnode
const insertedVnodeQueue = []
...
const oldElm = oldVnode.elm //包裝后的真實(shí)Dom <div id='app'></div>
const parentElm = nodeOps.parentNode(oldElm) // 首次父節(jié)點(diǎn)為<body></body>
createElm( // 創(chuàng)建真實(shí)Dom
vnode, // 第二個(gè)參數(shù)
insertedVnodeQueue, // 空數(shù)組
parentElm, // <body></body>
nodeOps.nextSibling(oldElm) // 下一個(gè)節(jié)點(diǎn)
)
return vnode.elm // 返回真實(shí)Dom覆蓋vm.$el
}
}
------------------------------------------------------
nodeOps:
export function parentNode (node) { // 獲取父節(jié)點(diǎn)
return node.parentNode
}
export function nextSibling(node) { // 獲取下一個(gè)節(jié)點(diǎn)
return node.nextSibing
}
createElm 方法開始生成真實(shí)的Dom, VNode 生成真實(shí)的Dom 的方式還是分為元素節(jié)點(diǎn)和組件兩種方式, 所以我們使用上一章生成的VNode分別說(shuō)明。
1. 元素節(jié)點(diǎn)生成Dom
{ // 元素節(jié)點(diǎn)VNode
tag: 'div',
children: [{
tag: 'h1',
children: [
{text: 'title h1'}
]
}, {
tag: 'h2',
children: [
{text: 'title h2'}
]
}, {
tag: 'h3',
children: [
{text: 'title h3'}
]
}
]
}
大家可以先看下這個(gè)流程圖有個(gè)印象即可, 再接下來(lái)看具體實(shí)現(xiàn)時(shí)思路會(huì)清晰很多(這里先借用網(wǎng)上的一張圖):

開始Dom, 來(lái)看下它的定義:
function createElm(vnode, insertedVnodeQueue, parentElm, refElm, nested, ownerArray, index) {
...
const children = vnode.children // [VNode, VNode, VNode]
const tag = vnode.tag // div
if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
return // 如果是組件結(jié)果返回true,不會(huì)繼續(xù),之后詳解createComponent
}
if(isDef(tag)) { // 元素節(jié)點(diǎn)
vnode.elm = nodeOps.createElement(tag) // 創(chuàng)建父節(jié)點(diǎn)
createChildren(vnode, children, insertedVnodeQueue) // 創(chuàng)建子節(jié)點(diǎn)
insert(parentElm, vnode.elm, refElm) // 插入
} else if(isTrue(vnode.isComment)) { // 注釋節(jié)點(diǎn)
vnode.elm = nodeOps.createComment(vnode.text) // 創(chuàng)建注釋節(jié)點(diǎn)
insert(parentElm, vnode.elm, refElm); // 插入到父節(jié)點(diǎn)
} else { // 文本節(jié)點(diǎn)
vnode.elm = nodeOps.createTextNode(vnode.text) // 創(chuàng)建文本節(jié)點(diǎn)
insert(parentElm, vnode.elm, refElm) // 插入到父節(jié)點(diǎn)
}
...
}
------------------------------------------------------------------
nodeOps:
export function createElement(tagName) { // 創(chuàng)建節(jié)點(diǎn)
return document.createElement(tagName)
}
export function createComment(text) { //創(chuàng)建注釋節(jié)點(diǎn)
return document.createComment(text)
}
export function createTextNode(text) { // 創(chuàng)建文本節(jié)點(diǎn)
return document.createTextNode(text)
}
function insert (parent, elm, ref) { //插入dom操作
if (isDef(parent)) { // 有父節(jié)點(diǎn)
if (isDef(ref)) { // 有參考節(jié)點(diǎn)
if (ref.parentNode === parent) { // 參考節(jié)點(diǎn)的父節(jié)點(diǎn)等于傳入的父節(jié)點(diǎn)
nodeOps.insertBefore(parent, elm, ref) // 在父節(jié)點(diǎn)內(nèi)的參考節(jié)點(diǎn)之前插入elm
}
} else {
nodeOps.appendChild(parent, elm) // 添加elm到parent內(nèi)
}
} // 沒(méi)有父節(jié)點(diǎn)什么都不做
}
這算一個(gè)比較重要的方法,因?yàn)楹芏嗟胤綍?huì)用到。
依次判斷是否是元素節(jié)點(diǎn), 注釋節(jié)點(diǎn),文本節(jié)點(diǎn), 分別創(chuàng)建它們?nèi)缓蟛迦氲礁腹?jié)點(diǎn)里面, 這里主要介紹創(chuàng)建元素節(jié)點(diǎn), 另外兩個(gè)并沒(méi)有復(fù)雜的邏輯。 我們接下來(lái)看下:createChild 方法定義:
function createChild(vnode, children, insertedVnodeQueue) {
if(Array.isArray(children)) { // 是數(shù)組
for(let i = 0; i < children.length; ++i) { // 遍歷vnode每一項(xiàng)
createElm( // 遞歸調(diào)用
children[i],
insertedVnodeQueue,
vnode.elm,
null,
true, // 不是根節(jié)點(diǎn)插入
children,
i
)
}
} else if(isPrimitive(vnode.text)) { //typeof為string/number/symbol/boolean之一
nodeOps.appendChild( // 創(chuàng)建并插入到父節(jié)點(diǎn)
vnode.elm,
nodeOps.createTextNode(String(vnode.text))
)
}
}
-------------------------------------------------------------------------------
nodeOps:
export default appendChild(node, child) { // 添加子節(jié)點(diǎn)
node.appendChild(child)
}
開始創(chuàng)建子節(jié)點(diǎn), 遍歷VNode 的每一項(xiàng), 每一項(xiàng)還是使用之前的createElm方法創(chuàng)建Dom。 如果某一項(xiàng)又是數(shù)組,繼續(xù)調(diào)用createChild創(chuàng)建某一項(xiàng)的子節(jié)點(diǎn); 如果某一項(xiàng)不是數(shù)組, 創(chuàng)建文本節(jié)點(diǎn)并將它添加到父節(jié)點(diǎn)內(nèi)。 像這樣使用遞歸的形式將嵌套的VNode全部創(chuàng)建為真實(shí)的Dom。
在看一遍流程圖, 應(yīng)該就能減少大家很多疑惑了(這里先借用網(wǎng)上一章圖):

簡(jiǎn)單來(lái)說(shuō)就是由里向外的挨個(gè)創(chuàng)建出真實(shí)的Dom, 然后插入到它的父節(jié)點(diǎn)內(nèi),最后將創(chuàng)建好的Dom插入到body內(nèi), 完成創(chuàng)建的過(guò)程, 元素節(jié)點(diǎn)的創(chuàng)建還是比較簡(jiǎn)單的, 接下來(lái)看下組件式怎么創(chuàng)建的。
組件VNode生成Dom
{ // 組件VNode
tag: 'vue-component-1-app',
context: {...},
componentOptions: {
Ctor: function(){...}, // 子組件構(gòu)造函數(shù)
propsData: undefined,
children: undefined,
tag: undefined
},
data: {
on: undefined, // 原生事件
hook: { // 組件鉤子
init: function(){...},
insert: function(){...},
prepatch: function(){...},
destroy: function(){...}
}
}
}
-------------------------------------------
<template> // app組件內(nèi)模板
<div>app text</div>
</template>
首先看張簡(jiǎn)易流程圖, 留個(gè)影響即可,方便理清之后的邏輯順序(這里借用網(wǎng)上一張圖):

使用上一章組件生成VNode , 看下在createElm 內(nèi)創(chuàng)建組件Dom分支邏輯是怎么樣的:
function createElm(vnode, insertedVnodeQueue, parentElm, refElm) {
...
if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { // 組件分支
return
}
...
執(zhí)行createComponent 方法, 如果是元素節(jié)點(diǎn)不會(huì)返回任何東西,所以是undefined , 會(huì)繼續(xù)走接下來(lái)的創(chuàng)建元節(jié)點(diǎn)的邏輯。 現(xiàn)在是組件, 我們看下createComponent 的實(shí)現(xiàn):
function createComponent(vnode, insertedVnodeQueue, parentElm, refElm) {
let i = vnode.data
if(isDef(i)) {
if(isDef(i = i.hook) && isDef(i = i.init)) {
i(vnode) // 執(zhí)行init方法
}
...
}
}
首先會(huì)將組件的vnode.data賦值給i, 是否有這個(gè)屬性就能判斷是否是組件vnode。 之后的if(isDef(i = i.hook) && isDef(i = i.init)) 集判斷和賦值為一體, if 內(nèi)的i(vnode) 就是執(zhí)行的組件init(vnode)方法。 這個(gè)時(shí)候我們來(lái)看下組件的init 鉤子方法做了什么:
import activeInstance // 全局變量
const init = vnode => {
const child = vnode.componentInstance =
createComponentInstanceForVnode(vnode, activeInstance)
...
}
activeInstance 是一個(gè)全局的變量, 再update 方法內(nèi)賦值為當(dāng)前實(shí)例, 再當(dāng)前實(shí)例做 patch 的過(guò)程中作為了組件的父實(shí)例傳入, 在子組件的initLifecycle時(shí)構(gòu)建組件關(guān)系。 將createComponentInsanceForVnode 執(zhí)行的結(jié)果賦值給了vnode.componentInstance, 所以看下它的返回的結(jié)果是什么:
export createComponentInstanceForVnode(vnode, parent) { // parent為全局變量activeInstance
const options = { // 組件的options
_isComponent: true, // 設(shè)置一個(gè)標(biāo)記位,表明是組件
_parentVnode: vnode,
parent // 子組件的父vm實(shí)例,讓初始化initLifecycle可以建立父子關(guān)系
}
return new vnode.componentOptions.Ctor(options) // 子組件的構(gòu)造函數(shù)定義為Ctor
}
再組件的init 方法內(nèi)首先執(zhí)行craeeteComponentInstanceForVnode方法, 這個(gè)方法的內(nèi)部就會(huì)將子組件的構(gòu)造函數(shù)實(shí)例化, 因?yàn)樽咏M件的構(gòu)造函數(shù)繼承了基類Vue的所有能力, 這個(gè)時(shí)候相當(dāng)于執(zhí)行new Vue({…}) , 接下來(lái)又會(huì)執(zhí)行==_init方法進(jìn)行一系列的子組件的初始化邏輯, 回到_init== 方法內(nèi), 因?yàn)樗麄冎g還是有些不同的地方:
Vue.prototype._init = function(options) {
if(options && options._isComponent) { // 組件的合并options,_isComponent為之前定義的標(biāo)記位
initInternalComponent(this, options) // 區(qū)分是因?yàn)榻M件的合并項(xiàng)會(huì)簡(jiǎn)單很多
}
initLifecycle(vm) // 建立父子關(guān)系
...
callHook(vm, 'created')
if (vm.$options.el) { // 組件是沒(méi)有el屬性的,所以到這里咋然而止
vm.$mount(vm.$options.el)
}
}
----------------------------------------------------------------------------------------
function initInternalComponent(vm, options) { // 合并子組件options
const opts = vm.$options = Object.create(vm.constructor.options)
opts.parent = options.parent // 組件init賦值,全局變量activeInstance
opts._parentVnode = options._parentVnode // 組件init賦值,組件的vnode
...
}
前面都還是執(zhí)行的好好的, 最后卻因?yàn)闆](méi)有el屬性, 所以沒(méi)有掛載,createComponentInstanceForVnode 方法執(zhí)行完畢。 這個(gè)時(shí)候我們回到組件的init方法, 補(bǔ)全剩下的邏輯:
const init = vnode => {
const child = vnode.componentInstance = // 得到組件的實(shí)例
createComponentInstanceForVnode(vnode, activeInstance)
child.$mount(undefined) // 那就手動(dòng)掛載唄
}
我們?cè)趇nit 方法內(nèi)手動(dòng)掛載這個(gè)組件, 接著又會(huì)執(zhí)行組件的==render()== 方法得到組件內(nèi)元素節(jié)點(diǎn)VNode , 然后執(zhí)行vm._update(), 執(zhí)行組件的 patch 方法, 因?yàn)?$mount 方法傳入的是 undefined, oldVnode 也是undefinned, 會(huì)執(zhí)行__patch_ 內(nèi)的這段邏輯:
return function patch(oldVnode, vnode) {
...
if (isUndef(oldVnode)) {
createElm(vnode, insertedVnodeQueue)
}
...
}
這次執(zhí)行createElm 是沒(méi)有傳入第三個(gè)參數(shù)父節(jié)點(diǎn)的, 那組件創(chuàng)建好的Dom放哪生效了? 沒(méi)有父節(jié)點(diǎn)頁(yè)要生成Dom不是, 這個(gè)時(shí)候執(zhí)行的是組件的 patch , 所以參數(shù)vnode 就是組件內(nèi)元素節(jié)點(diǎn)的vnode了:
<template> // app組件內(nèi)模板
<div>app text</div>
</template>
-------------------------
{ // app內(nèi)元素vnode
tag: 'div',
children: [
{text: app text}
],
parent: { // 子組件_init時(shí)執(zhí)行initLifecycle建立的關(guān)系
tag: 'vue-component-1-app',
componentOptions: {...}
}
}
很明顯這個(gè)時(shí)候不是組件了, 即使是組件也沒(méi)關(guān)系, 大不了還是執(zhí)行一遍createComponent 創(chuàng)建組件的邏輯, 因?yàn)榭倳?huì)有組件是由元素節(jié)點(diǎn)組成的。 這個(gè)時(shí)候我們執(zhí)行一遍創(chuàng)建元素節(jié)點(diǎn)的邏輯, 因?yàn)闆](méi)有第三個(gè)參數(shù)父節(jié)點(diǎn), 所以組件的Dom雖然創(chuàng)建好了, 并不會(huì)在這里插入。 請(qǐng)注意這個(gè)時(shí)候組件的init 已經(jīng)完成, 但是組件的createComponent 方法并沒(méi)有完成, 我們補(bǔ)全它的邏輯:
function createComponent(vnode, insertedVnodeQueue, parentElm, refElm) {
let i = vnode.data;
if (isDef(i)) {
if (isDef(i = i.hook) && isDef(i = i.init)) {
i(vnode) // init已經(jīng)完成
}
if (isDef(vnode.componentInstance)) { // 執(zhí)行組件init時(shí)被賦值
initComponent(vnode) // 賦值真實(shí)dom給vnode.elm
insert(parentElm, vnode.elm, refElm) // 組件Dom在這里插入
...
return true // 所以會(huì)直接return
}
}
}
-----------------------------------------------------------------------
function initComponent(vnode) {
...
vnode.elm = vnode.componentInstance.$el // __patch__返回的真實(shí)dom
...
}
無(wú)論是嵌套多么深的組件, 遇到組件后就執(zhí)行 init, 在init 的 patch 過(guò)程中又遇到嵌套組件, 那就再執(zhí)行嵌套組件的init, 嵌套組件完成 __patch__后將真是的Dom插入到它的父節(jié)點(diǎn)內(nèi), 接著執(zhí)行完外層組件的 patch 又插入到它的父幾點(diǎn)內(nèi), 最后插入到body 內(nèi), 完成嵌套組件的創(chuàng)建過(guò)程, 總之還是一個(gè)由里及外的過(guò)程。
在回過(guò)頭看這張圖, 相信會(huì)很好理解了:

再將本章最初的mountComponent 之后的邏輯補(bǔ)全:
export function mountComponent(vm, el) {
...
const updateComponent = () => {
vm._update(vm._render())
}
new Watcher(vm, updateComponent, noop, {
before() {
if(vm._isMounted) {
callHook(vm, 'beforeUpdate')
}
}
}, true)
...
callHook(vm, 'mounted')
return vm
}
接下來(lái)會(huì)將 updateComponent 傳入到一個(gè)Watcher 的類中, 這個(gè)類是干嘛的,我們下一章在介紹。 接下來(lái)執(zhí)行mounted 鉤子方法。 至此new vue 的整個(gè)流程就全部走完了。 我們回顧下從new Vue 開始執(zhí)行的順序:
new Vue ==> vm._init() ==> vm.$mount(el) ==> vm._render() ==> vm.update(vnode)
最后我們以一個(gè)問(wèn)題來(lái)結(jié)束本章的內(nèi)容:
父子兩個(gè)組件同時(shí)定義了 beforeCreate, created, beforeMounte, mounted 四個(gè)鉤子, 它們的執(zhí)行順序是怎樣的?
解答:
首先會(huì)執(zhí)行父組件的初始化過(guò)程, 所以會(huì)依次執(zhí)行beforeCreate, created, 在執(zhí)行掛載前又會(huì)執(zhí)行beforeMount鉤子, 不過(guò)在生成真實(shí)dom 的 __patch__過(guò)程中遇到嵌套子組件后又會(huì)轉(zhuǎn)為去執(zhí)行子組件的初始化鉤子beforeCreate, created, 子組件在掛載前會(huì)執(zhí)行beforeMounte, 再完成子組件的Dom創(chuàng)建后執(zhí)行 mounted。 這個(gè)父組件的 patch 過(guò)程才算完成, 最后執(zhí)行父組件的mounted 鉤子, 這就是它們的執(zhí)行順序。 如下:
parent beforeCreate
parent created
parent beforeMounte
child beforeCreate
child created
child beforeMounte
child mounted
parent mounted
到此這篇關(guān)于Vue虛擬Dom到真實(shí)Dom的轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)Vue虛擬Dom到真實(shí)Dom內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue實(shí)現(xiàn)模糊查詢filter()實(shí)例詳解
因?yàn)榻赵趯W(xué)習(xí)并使用VUE,客戶有一個(gè)要求,要輸入框可模糊查詢并帶有下拉提示的應(yīng)用,數(shù)據(jù)從接口取,下面這篇文章主要給大家介紹了關(guān)于Vue實(shí)現(xiàn)模糊查詢filter()的相關(guān)資料,需要的朋友可以參考下2023-04-04
Vue中使用JsonView來(lái)展示Json樹的實(shí)例代碼
這篇文章主要介紹了Vue之使用JsonView來(lái)展示Json樹的實(shí)例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
Vue請(qǐng)求java服務(wù)端并返回?cái)?shù)據(jù)代碼實(shí)例
這篇文章主要介紹了Vue請(qǐng)求java服務(wù)端并返回?cái)?shù)據(jù)代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
關(guān)于eslint和prettier格式化沖突問(wèn)題
這篇文章主要介紹了eslint和prettier格式化沖突問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08
vue自定義實(shí)例化modal彈窗功能的實(shí)現(xiàn)
這篇文章主要介紹了vue自定義實(shí)例化modal彈窗,Vue.extend 屬于Vue的全局 api,在實(shí)際業(yè)務(wù)開發(fā)中我們很少使用,因?yàn)橄啾瘸S玫?nbsp;Vue.component寫法使用 extend 步驟要更加繁瑣一些,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下2022-09-09
詳解element-ui級(jí)聯(lián)菜單(城市三級(jí)聯(lián)動(dòng)菜單)和回顯問(wèn)題
這篇文章主要介紹了詳解element-ui級(jí)聯(lián)菜單(城市三級(jí)聯(lián)動(dòng)菜單)和回顯問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
vue 使用async寫數(shù)字動(dòng)態(tài)加載效果案例
這篇文章主要介紹了vue 使用async寫數(shù)字動(dòng)態(tài)加載效果案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07

