亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深度剖析?Vue3?在瀏覽器的運(yùn)行原理

 更新時(shí)間:2022年09月20日 09:16:34   作者:黃勇超???????  
這篇文章主要介紹了深度剖析Vue3在瀏覽器的運(yùn)行原理,文章通過圍繞主題展開相關(guān)詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

前言

上一講深度解析 Vue3 的響應(yīng)式機(jī)制我們學(xué)習(xí)了 Vue 響應(yīng)式的大致原理,響應(yīng)式就是可以把普通的 JavaScript 對(duì)象包裹成響應(yīng)式對(duì)象,這樣,我們對(duì)對(duì)象做的修改,響應(yīng)式都能夠監(jiān)聽到,并且執(zhí)行 effect 內(nèi)部注冊(cè)的函數(shù)來執(zhí)行數(shù)據(jù)修改之后的效果

那今天我就跟你聊一下 Vue 在瀏覽器里是如何運(yùn)行的,照例我們還是對(duì)著 Vue 3 的源碼來學(xué)習(xí),不過源碼復(fù)雜,為了幫助你理解主要邏輯,我會(huì)直接把源碼簡(jiǎn)化再演示

好了廢話不多說,我們馬上開始;前端框架需要處理的最核心的兩個(gè)流程,就是首次渲染和數(shù)據(jù)更新后的渲染

首次渲染

我們知道,想要啟動(dòng)一個(gè) Vue 項(xiàng)目,只需要從 Vue 中引入 createApp,傳入 App 組件,并且調(diào)用 createApp 返回的 App 實(shí)例的 mount 方法,就實(shí)現(xiàn)了項(xiàng)目的啟動(dòng);這個(gè)時(shí)候 Vue 也完成了首次渲染,代碼邏輯如下:

所以 createApp 就是項(xiàng)目的初始化渲染入口

這里就有一個(gè)看代碼的小技巧,分享給你,我們首次查看源碼的時(shí)候,可以先把一些無用的信息刪除,方便自己梳理主體的邏輯???Vue 代碼,和今天主題無關(guān)的無用信息有哪些,COMPAT 代碼是用來兼容 Vue 2 的,DEV 代碼是用來調(diào)試的,我們可以把這些代碼刪除之后,得到下面的簡(jiǎn)化版 createApp 源碼

再看思路就比較清晰了

我們使用 ensureRenderer 返回的對(duì)象去創(chuàng)建 app,并且重寫了app.mount 方法;在 mount 方法內(nèi)部,我們查找 mount 傳遞的 DOM 元素,并且調(diào)用 ensureRenderer 返回的 mount 方法,進(jìn)行初始化渲染

如下圖所示:

之前我們講過要會(huì) TypeScript,這時(shí)你就能感受到 TypeScript 的好處了,現(xiàn)在即使我們不知道 app.mount 是什么邏輯,也能知道這個(gè)函數(shù)的參數(shù)只能是 Element、ShadowRoot 或者 string 三者之一,也就很好理解內(nèi)部的 normalizeContainer 就是把你傳遞的參數(shù)統(tǒng)一變?yōu)闉g覽器的 DOM 元素,Typescript 類型帶來的好處,我們?cè)谧x源碼的時(shí)候會(huì)一直感受得到

export const createApp = (...args) => {
    const app = ensureRenderer().createApp(...args);
    const { mount } = app;

    // 重寫mount
    app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
      const container = normalizeContainer(containerOrSelector);
      if (!container) return;
      const component = app._component;

      if (!isFunction(component) && !component.render && !component.template) {
        component.template = container.innerHTML;
      }
      container.innerHTML = "";
      const proxy = mount(container, false, container instanceof SVGElement);

      if (container instanceof Element) {
        container.removeAttribute("v-cloak");

        container.setAttribute("data-v-app", "");
      }
      return proxy;
    };
    return app;
  };

我們繼續(xù)深入了解 ensureRenderer 方法,以及 ensureRenderer 方法返回的 createApp方法

這里 ensureRenderer 函數(shù),內(nèi)部通過 createRenderer 函數(shù),創(chuàng)建了一個(gè)瀏覽器的渲染器,并且緩存了渲染器 renderer,這種使用閉包做緩存的方式,你在日常開發(fā)中也可以借鑒這種思路

createRenderer 函數(shù),我們?cè)谧远x渲染器那一講里學(xué)到過,傳遞的 rendererOptions 就是瀏覽器里面標(biāo)簽的增刪改查 API:

// 瀏覽器dom操作
import { nodeOps } from "./nodeOps"; // 瀏覽器dom屬性更新
import { patchProp } from "./patchProp";
import { createRenderer } from "@vue/runtime-core";
const rendererOptions = extend({ patchProp }, nodeOps);
let renderer: Renderer<Element | ShadowRoot> | HydrationRenderer;
function ensureRenderer() {
  return (
    renderer ||
    ((renderer = createRenderer < Node),
    Element | (ShadowRoot > rendererOptions))
  );
}

可以看到,createRenderer 函數(shù)傳遞的參數(shù)是 nodeOps 和 patchProp 的合并對(duì)象

我們繼續(xù)進(jìn)入 nodeOps 和 pathProp 也可以看到下面的代碼,寫了很多方法;通過 ensureRenderer 存儲(chǔ)這些操作方法后,createApp 內(nèi)部就可以脫離具體的渲染平臺(tái)了,這也是 Vue 3 實(shí)現(xiàn)跨端的核心邏輯:

export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = { 
  insert: (child, parent, anchor) => { parent.insertBefore(child, anchor || null) 
  },
  remove: child => { 
    const parent = child.parentNode 
    if (parent) { 
      parent.removeChild(child) 
      } 
    },
    createElement: (tag, isSVG, is, props): Element => { 
      const el = isSVG ? doc.createElementNS(svgNS, tag) : doc.createElement(tag, is ? { is } : undefined) 
      if (tag === 'select' && props && props.multiple != null) { 
        ;(el as HTMLSelectElement).setAttribute('multiple', props.multiple) 

        }
        return el 
        },
        createText: text => doc.createTextNode(text), 
        createComment: text => doc.createComment(text), 
        setText: (node, text) => { node.nodeValue = text },
        setElementText: (el, text) => { el.textContent = text },
        parentNode: node => node.parentNode as Element | null, 
        nextSibling: node => node.nextSibling, 
        querySelector: selector => doc.querySelector(selector), 
        ... 
}

然后我們就需要進(jìn)入到 rumtime-core 模塊去看下 createRenderer 是如何工作的

createRenderer 是調(diào)用 baseCreateRenderer 創(chuàng)建的,baseCreateRenderer 函數(shù)內(nèi)部有十幾個(gè)函數(shù),代碼行數(shù)合計(jì) 2000 行左右,這也是我們學(xué)習(xí) Vue 源碼最復(fù)雜的一個(gè)函數(shù)了

按前面簡(jiǎn)化源碼的思路,先把工具函數(shù)的實(shí)現(xiàn)折疊起來,精簡(jiǎn)之后代碼主要邏輯其實(shí)很簡(jiǎn)單

我們一起來看

首先獲取了平臺(tái)上所有的 insert、remove 函數(shù),這些函數(shù)都是 nodeOps 傳遞進(jìn)來的,然后定義了一些列 patch、mount、unmount 函數(shù),通過名字我們不難猜出,這就是 Vue 中更新、渲染組件的工具函數(shù),比如mountElement 就是渲染 DOM 元素、mountComponent 就是渲染組件 updateComponent 就是更新組件

export function createRenderer<
    HostNode = RendererNode,
    HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
    return baseCreateRenderer < HostNode, HostElement > (options)
}
function baseCreateRenderer() {
    const { insert: hostInsert,
        remove: hostRemove,
        patchProp: hostPatchProp,
        createElement: hostCreateElement,
        createText: hostCreateText,
        createComment: hostCreateComment,
        setText: hostSetText,
        setElementText: hostSetElementText,
        parentNode: hostParentNode,
        nextSibling: hostNextSibling,
        setScopeId: hostSetScopeId = NOOP,
        cloneNode: hostCloneNode,
        insertStaticContent: hostInsertStaticContent
    } = options
    const patch = () =>... //一個(gè)函數(shù)
    const processText = () =>...
    const processCommentNode = () =>...
    const processElement = () =>...
    const mountElement = () =>...
    const mountChildren = () =>...
    const patchElement = () =>...
    const patchBlockChildren = () =>...
    const patchProps = () =>...
    const processComponent = () =>...
    const mountComponent = () =>...
    const updateComponent = () =>...
    const setupRenderEffect = () =>...
    const patchChildren = () =>...
    const patchKeyedChildren = () =>...
    const unmount = () =>...
    const unmountComponent = () =>...
    const unmountComponent = () =>...
    const unmountComponent = () =>...
    const unmountComponent = () =>...
    const render: RootRenderFunction = (vnode, container, isSVG) => {
        if (vnode == null) {
            if (container._vnode) {
                unmount(container._vnode, null, null, true)
            }
        } else {
            patch(container._vnode || null, vnode, container, null, null, null, isSV }
        flushPostFlushCbs() container._vnode = vnode
    } return {
        render, hydrate, createApp: createAppAPI(render, hydrate)
    }
}

整個(gè) createApp 函數(shù)的執(zhí)行邏輯如下圖所示:

最后返回的 createApp 方法,實(shí)際上是 createAPI 的返回值,并且給 createAPI 傳遞了render 方法;render 方法內(nèi)部很簡(jiǎn)單,就是判斷 container 容器上有沒有 _vnode 屬性,如果有的話就執(zhí)行 unmout 方法,沒有的話就執(zhí)行 patch 方法,最后把 vnode 信息存儲(chǔ)在 container._vnode 上

那 createAppAPI 又做了什么呢?我們繼續(xù)進(jìn)入 createAppAPI 源碼,看下面的代碼;內(nèi)部創(chuàng)建了一個(gè) app 對(duì)象,app 上注冊(cè)了我們熟悉的 use、component 和 mount 等方法:

export function createAppAPI<HostElement>(render: RootRenderFunction, hydrate?: RootHydrateFunction): CreateAppFunction<HostElement> {
    return function createApp(rootComponent, rootProps = null) {
        const context = createAppContext()
        let isMounted = false const app: App = (context.app = {
            _context: context, _instance: null,
            use(plugin: Plugin, ...options: any[]) ,
            component(name: string, component?: Component): any {
                if (!component) {
                    return context.components[name]
                }
                context.components[name] = component return app
            },
            directive(name: string, directive?: Directive) 
            mount(rootContainer: HostElement, isHydrate?: boolean, isSVG?: boolean): any {
                if (!isMounted) {
                    const vnode = createVNode(rootComponent as ConcreteComponent, rootProps)
                    vnode.appContext = context // 核心的邏輯
                    if (isHydrate && hydrate) {
                        hydrate(vnode as VNode<Node, Element>, rootContainer as any)
                    } else {
                        render(vnode, rootContainer, isSVG)
                    }
                    return getExposeProxy(vnode.component!) || vnode.component!.proxy
                }
            },
            provide(key, value) { context.provides[key as string] = value return app }
        }) return app
    }
}

可以看到 mount 內(nèi)部執(zhí)行的是傳遞進(jìn)來的 render 方法,也就是上面的 render 方法

container 就是我們 app.mount 中傳遞的 DOM 元素,對(duì) DOM 元素進(jìn)行處理之后,執(zhí)行 patch 函數(shù)實(shí)現(xiàn)整個(gè)應(yīng)用的加載

所以我們的下一個(gè)任務(wù)就是需要搞清楚 patch 函數(shù)的執(zhí)行邏輯

patch 函數(shù)

patch 傳遞的是 container._vnode,也就是上一次渲染緩存的 vnode、本次渲染組件的vnode,以及容器 container;下面就是 patch 函數(shù)的代碼,核心代碼我添加了注釋;其中 n1 是上次渲染的虛擬 DOM,n2 是下次要渲染的虛擬 DOM

首先可以把 n1 和 n2 做一次判斷,如果虛擬 DOM 的節(jié)點(diǎn)類型不同,就直接 unmount 之前的節(jié)點(diǎn);因?yàn)楸热缰笆?Button 組件,現(xiàn)在要渲染 Container 組件,就沒有計(jì)算 diff的必要,直接把 Button 組件銷毀再渲染 Container 即可

如果 n1 和 n2 類型相同,比如都是 Button 組件或者都是 div 標(biāo)簽,我們需要判斷具體的類型再去執(zhí)行不同的函數(shù),比如 processText、processFragment、processElement 以及 processComponent 等函數(shù);

看第 55 行,這里的 ShapeFlags 用到了位運(yùn)算的知識(shí),我們后面會(huì)通過刷算法題的方式介紹,暫時(shí)我們只需要知道,ShapeFlags 可以幫助我們快速判斷需要操作的類型就可以了

const patch: PatchFn = (
    n1,
    n2,
    container,
    anchor = null,
    parentComponent = null,
    parentSuspense = null,
    isSVG = false,
    slotScopeIds = null,
    optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
) => { // 兩次虛擬dom完全一樣 啥也不用干
    if (n1 === n2) { return }// 虛擬dom節(jié)點(diǎn)類型不一樣, unmount老的虛擬dom,并且n1賦值null if (n1 && !isSameVNodeType(n1, n2)) { anchor = getNextHostNode(n1) unmount(n1, parentComponent, parentSuspense, true)n1 = null }// n2是要渲染的虛擬dom,我們獲取type,ref和shapeFlag const { type, ref, shapeFlag } = n2 switch (type) { case Text: // 文本
    processText(n1, n2, container, anchor) break case Comment: // 注釋
    processCommentNode(n1, n2, container, anchor) break case Static: // 靜態(tài)節(jié)點(diǎn)
    if (n1 == null) {
        mountStaticNode(n2, container, anchor, isSVG)
    } else if (__DEV__) {
        patchStaticNode(n1, n2, container, isSVG)
    }
    break 
    case Fragment: processFragment(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized)
break default: // 運(yùn)運(yùn)算判斷操作類型
if (shapeFlag & ShapeFlags.ELEMENT) { // html標(biāo)簽
    processElement(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized)
} else if (shapeFlag & ShapeFlags.COMPONENT) { // 組件
    processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized)
} else if (shapeFlag & ShapeFlags.TELEPORT) {
    (type as typeof TeleportImpl).process(
        n1 as TeleportVNode, n2 as TeleportVNode,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds, optimized, internals
    )
} else if (
    __FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE
) {
    ;
    (type as typeof SuspenseImpl).process(
        n1,
        n2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized,
        internals
    )
}
else if (__DEV__) { warn('Invalid VNode type:', type, `(${typeof type})`) }
}// set ref if (ref != null && parentComponent) { setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2) }

代碼的整體執(zhí)行邏輯如下圖所示:

我們首次渲染的 App 是一個(gè)組件,所以要執(zhí)行的就是 processComponent 方法

processComponent 方法

那我們繼續(xù)進(jìn)入到 processComponent 代碼內(nèi)部,看下面的代碼。首次渲染的時(shí)候,n1就是 null,所以會(huì)執(zhí)行 mountComponent;如果是更新組件的時(shí)候,n1 就是上次渲染的 vdom,需要執(zhí)行 updateComponent

const processComponent = (
    n1: VNode | null,
    n2: VNode,
    container: RendererElement,
    anchor: RendererNode | null,
    parentComponent: ComponentInternalInstance | null,
    parentSuspense: SuspenseBoundary | null,
    isSVG: boolean,
    slotScopeIds: string[] | null,
    optimized: boolean) => {
    n2.slotScopeIds = slotScopeIds
    if (n1 == null) {
        if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
            ; (parentComponent!.ctx as KeepAliveContext).activate(n2, container, anchor, isSVG, optimized)
        }
        else {
            mountComponent(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized)
        }
    } else {
        updateComponent(n1, n2, optimized)
    }
}

updateComponent 是虛擬 DOM 的邏輯,我們會(huì)在下一講詳細(xì)剖析,這一講主要講首次渲染的過程

所以我們進(jìn)入 mountComponent 函數(shù)中,可以看到 mountComponent 函數(shù)內(nèi)部會(huì)對(duì)組件的類型進(jìn)行一系列的判斷,還有一些對(duì) Vue 2 的兼容代碼,核心的渲染邏輯就是 setupComponent 函數(shù)和 setupRenderEffect 函數(shù)

import { setupComponent } from './component'
const mountComponent: MountComponentFn = () => {
    // 2.x compat may pre-creaate the component instance before actually 
    // mounting 
    const compatMountInstance = __COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
    const instance: ComponentInternalInstance = compatMountInstance || (
        initialVNode.component = createComponentInstance(initialVNode, parentComponent, parentSuspense)
    ) // resolve props and slots for setup context 
    if (!(__COMPAT__ && compatMountInstance)) {
        setupComponent(instance)
    } (instance,
        initialVNode,
        container,
        anchor,
        parentSuspense,
        isSVG, o
    ptimized 
    )
if (__DEV__) { popWarningContext() endMeasure(instance, `mount`) }
}

setupComponent 和 setupRenderEffect,它倆又做了點(diǎn)什么呢?可以參考下面的示意圖這兩個(gè)實(shí)現(xiàn)組件首次渲染的函數(shù):

setupComponent

首先看 setupComponent,要完成的就是執(zhí)行我們寫的 setup 函數(shù)

可以看到,內(nèi)部先初始化了 props 和 slots,并且執(zhí)行 setupStatefulComponent 創(chuàng)建組件,而這個(gè)函數(shù)內(nèi)部從 component 中獲取 setup 屬性,也就是 script setup 內(nèi)部實(shí)現(xiàn)的函數(shù),就進(jìn)入到我們組件內(nèi)部的reactive、ref 等函數(shù)實(shí)現(xiàn)的邏輯了

export function setupComponent(
    instance: ComponentInternalInstance,
    isSSR = false) { isInSSRComponentSetup = isSSR const { props, children } = instance.vnode const isStateful = isStatefulComponent(instance) initProps(instance, props, isStateful, isSSR) initSlots(instance, children) const setupResult = isStateful ? setupStatefulComponent(instance, isSSR) : undefined isInSSRComponentSetup = false return setupResult } function setupStatefulComponent(instance: ComponentInternalInstance, isSSR: boolean) {
        const Component = instance.type as ComponentOptions // 執(zhí)行setup 
        const { setup } = Component if (setup) {
            const setupContext = (instance.setupContext = setup.length > 1 ? createSetupContext(instance) : null)
            setCurrentInstance(instance) pauseTracking()
            const setupResult = callWithErrorHandling(
                setup,
                instance,
                ErrorCodes.SETUP_FUNCTION,
                [instance.props, setupContext])
            if (isPromise(setupResult)) {
                setupResult.then(
                    unsetCurrentInstance, unsetCurrentInstance
                )
            } else {
                handleSetupResult(instance, setupResult, isSSR)
            }
        } else {
            finishComponentSetup(instance, isSSR)
        }
    }
export function callWithErrorHandling(
    fn: Function,
    instance: ComponentInternalInstance | null,
    type: ErrorTypes, args?: unknown[]
) {
    let res
    try { res = args ? fn(...args) : fn() } catch (err) { handleError(err, instance, type) } return res
}

setupRenderEffect

另一個(gè) setupRenderEffect 函數(shù),就是為了后續(xù)數(shù)據(jù)修改注冊(cè)的函數(shù),我們先梳理一下核心的實(shí)現(xiàn)邏輯

組件首次加載會(huì)調(diào)用 patch 函數(shù)去初始化子組件,注意 setupRenderEffect 本身就是在 patch 函數(shù)內(nèi)部執(zhí)行的,所以這里就會(huì)遞歸整個(gè)虛擬 DOM 樹,然后觸發(fā)生命周期 mounted,完成這個(gè)組件的初始化

頁面首次更新結(jié)束后,setupRenderEffect 不僅實(shí)現(xiàn)了組件的遞歸渲染,還注冊(cè)了組件的更新機(jī)制

在下面的核心代碼中,我們通過 ReactiveEffect 創(chuàng)建了 effect 函數(shù),這個(gè)概念上一講我們手寫過,然后執(zhí)行 instance.update 賦值為 effect.run 方法,這樣結(jié)合 setup 內(nèi)部的 ref 和 reactive 綁定的數(shù)據(jù),數(shù)據(jù)修改之后,就會(huì)觸發(fā) update 方法的執(zhí)行,內(nèi)部就會(huì) componentUpdateFn,內(nèi)部進(jìn)行遞歸的 patch 調(diào)用執(zhí)行每個(gè)組件內(nèi)部的 update 方法實(shí)現(xiàn)組件的更新

if (!instance.isMounted) {
    patch(null, subTree, container, anchor, instance, parentSuspense, isSVG)
} else {
    // updateComponent 
}
// create reactive effect for rendering 
const effect = new ReactiveEffect(componentUpdateFn, () => queueJob(instance.update),
    instance.scope // track it in component's effect scope 
)
const update = (instance.update = effect.run.bind(effect) as SchedulerJob)
update.id = instance.uid
update()

這樣我們就實(shí)現(xiàn)了整個(gè) Vue 的渲染和更新流程

到此這篇關(guān)于深度剖析 Vue3 在瀏覽器的運(yùn)行原理的文章就介紹到這了,更多相關(guān)Vue3 運(yùn)行原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue實(shí)現(xiàn)zip文件下載

    vue實(shí)現(xiàn)zip文件下載

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)zip文件下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 第一次在Vue中完整使用AJAX請(qǐng)求和axios.js的實(shí)戰(zhàn)記錄

    第一次在Vue中完整使用AJAX請(qǐng)求和axios.js的實(shí)戰(zhàn)記錄

    AJAX是現(xiàn)代Web開發(fā)的一個(gè)關(guān)鍵部分,盡管它一開始看起來令人生畏,但在你的武庫中擁有它是必須的,下面這篇文章主要給大家介紹了關(guān)于第一次在Vue中完整使用AJAX請(qǐng)求和axios.js的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • vue-cli創(chuàng)建的項(xiàng)目,配置多頁面的實(shí)現(xiàn)方法

    vue-cli創(chuàng)建的項(xiàng)目,配置多頁面的實(shí)現(xiàn)方法

    下面小編就為大家分享一篇vue-cli創(chuàng)建的項(xiàng)目,配置多頁面的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • Vue3偵聽器的實(shí)現(xiàn)原理詳情

    Vue3偵聽器的實(shí)現(xiàn)原理詳情

    這篇文章主要介紹了Vue3偵聽器的實(shí)現(xiàn)原理詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • vue如何使用router.meta.keepAlive對(duì)頁面進(jìn)行緩存

    vue如何使用router.meta.keepAlive對(duì)頁面進(jìn)行緩存

    這篇文章主要介紹了vue如何使用router.meta.keepAlive對(duì)頁面進(jìn)行緩存問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 一次用vue3簡(jiǎn)單封裝table組件的實(shí)戰(zhàn)過程

    一次用vue3簡(jiǎn)單封裝table組件的實(shí)戰(zhàn)過程

    之所以封裝全局組件是為了省事,所有的目的,全都是為了偷懶,下面這篇文章主要給大家介紹了關(guān)于用vue3簡(jiǎn)單封裝table組件的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • vue動(dòng)態(tài)修改頁面title的兩種方法

    vue動(dòng)態(tài)修改頁面title的兩種方法

    本文主要介紹了vue動(dòng)態(tài)修改頁面title的兩種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • 關(guān)于Element-UI可編輯表格的實(shí)現(xiàn)過程

    關(guān)于Element-UI可編輯表格的實(shí)現(xiàn)過程

    這篇文章主要介紹了關(guān)于Element-UI可編輯表格的實(shí)現(xiàn)過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • 基于Vue el-autocomplete 實(shí)現(xiàn)類似百度搜索框功能

    基于Vue el-autocomplete 實(shí)現(xiàn)類似百度搜索框功能

    本文通過代碼給大家介紹了Vue el-autocomplete 實(shí)現(xiàn)類似百度搜索框功能,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-10-10
  • vue3.x ref()語法糖賦值方式

    vue3.x ref()語法糖賦值方式

    這篇文章主要介紹了vue3.x ref()語法糖賦值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03

最新評(píng)論