中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue編譯優化的實現流程是什么

發布時間:2023-01-30 09:12:31 來源:億速云 閱讀:128 作者:iii 欄目:開發技術

本篇內容主要講解“Vue編譯優化的實現流程是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Vue編譯優化的實現流程是什么”吧!

    動態節點收集與補丁標志

    1.傳統diff算法的問題

    對于一個普通模板文件,如果只是標簽中的內容發生了變化,那么最簡單的更新方法很明顯是直接替換標簽中的文本內容。但是diff算法很明顯做不到這一點,它會重新生成一棵虛擬DOM樹,然后對兩棵虛擬DOM樹進行比較。很明顯,與直接替換標簽中的內容相比,傳統diff算法需要做很多無意義的操作,如果能夠去除這些無意義的操作,將會省下一筆很大的性能開銷。其實,只要在模板編譯時,標記出哪些節點是動態的,哪些是靜態的,然后再通過虛擬DOM傳遞給渲染器,渲染器就能根據這些信息,直接修改對應節點,從而提高運行時性能。

    2.Block和PatchFlags

    對于一個傳統的模板:

    <div>
        <div>
            foo
        </div>
        <p>
            {{ bar }}
        </p>
    </div>

    在這個模板中,只用{{ bar }}是動態內容,因此在bar變量發生變化時,只需要修改p標簽內的內容就行了。因此我們在這個模板對于的虛擬DOM中,加入patchFlag屬性,以此來標簽模板中的動態內容。

    const vnode = {
        tag: 'div',
        children: [
            { tag: 'div', children: 'foo' },
            { tag: 'p', children: ctx.bar, patchFlag: 1 },
        ]
    }

    對于不同的數值綁定,我們分別用不同的patch值來表示:

    • 數字1,代表節點有動態的textContent

    • 數字2,代表節點有動態的class綁定

    • 數字3,代表節點有動態的style綁定

    • 數字4,其他&hellip;

    我們可以新建一個枚舉類型來表示這些值:

    enum PatchFlags {
        TEXT: 1,
        CLASS,
        STYLE,
        OTHER
    }

    這樣我們就在虛擬DOM的創建階段,將動態節點提取出來:

    const vnode = {
        tag: 'div',
        children: [
            { tag: 'div', children: 'foo' },
            { tag: 'p', children: ctx.bar, patchFlag: PatchFlags.TEXT },
        ],
        dynamicChildren: [
            { tag: 'p', children: ctx.bar, patchFlag: PatchFlags.TEXT },
        ]
    }

    3.收集動態節點

    首先我們創建收集動態節點的邏輯。

    const dynamicChildrenStack = []; // 動態節點棧
    let currentDynamicChildren = null; // 當前動態節點集合
    function openBlock() {
        // 創建一個新的動態節點棧
    	dynamicChildrenStack.push((currentDynamicChildren = []));
    }
    function closeBlock() {
        // openBlock創建的動態節點集合彈出
        currentDynamicChildren = dynamicChildrenStack.pop();
    }

    然后,我們在創建虛擬節點的時候,對動態節點進行收集。

    function createVNode(tag, props, children, flags) {
        const key = props && props.key;
        props && delete props.key;
        const vnode = {
            tag,
            props,
            children,
            key,
            patchFlags: flags
        }
        if(typeof flags !== 'undefined' && currentDynamicChildren) {
            currentDynamicChildren.push(vnode);
        }
        return vnode;
    }

    然后我們修改組件渲染函數的邏輯。

    render() {
        return (openBlock(), createBlock('div', null, [
            createVNode('p', { class: 'foo' }, null, 1),
            createVNode('p', { class: 'bar' }, null)
        ]));
    }
    function createBlock(tag, props, children) {
        const block = createVNode(tag, props, children);
        block.dynamicChildren = currentDynamicChildren;
        closeBlock();
        return block;
    }

    4.渲染器運行時支持

    function patchElement(n1, n2) {
        const el = n2.el = n1.el;
        const oldProps = n1.props;
        const newProps = n2.props;
        // ...
        if(n2.dynamicChildren) {
            // 如果有動態節點數組,直接更新動態節點數組
            patchBlockChildren(n1, n2);
        } else {
            patchChildren(n1, n2, el);
        }
    }
    function pathcBlockChildren(n1, n2) {
        for(let i = 0; i < n2.dynamicChildren.length; i++) {
            patchElement(n1.dynamicChildren[i], n2.dynamicChildren[i]);
        }
    }

    由于我們標記了不同的動態節點類型,因此我們可以針對性的完成靶向更新。

    function patchElement(n1, n2) {
        const el = n2.el = n1.el;
        const oldProps = n1.props;
        const newProps = n2.props;
        if(n2.patchFlags) {
            if(n2.patchFlags === 1) {
                // 只更新內容
            } else if(n2.patchFlags === 2) {
                // 只更新class
            } else if(n2.patchFlags === 3) {
                // 只更新style
            } else {
                // 更新所有
                for(const k in newProps) {
                    if(newProps[key] !== oldProps[key]) {
                    	patchProps(el, key, oldProps[k], newProps[k]);
                    }
                }
                for(const k in oldProps) {
                    if(!key in newProps) {
                        patchProps(el, key, oldProps[k], null);
                    }
                }
            }
        }
        patchChildren(n1, n2, el);
    }

    5.Block樹

    組件的根節點必須作為Block角色,這樣,從根節點開始的所有動態子代節點都會被收集到根節點的dynamicChildren數組中。除了根節點外,帶有v-if、v-for這種結構化指令的節點,也會被作為Block角色,這些Block角色共同構成一棵Block樹。

    靜態提升

    假設有以下模板

    <div>
        <p>
            static text
        </p>
        <p>
            {{ title }}
        </p>
    </div>

    默認情況下,對應的渲染函數為:

    function render() {
        return (openBlock(), createBlock('div', null, [
            createVNode('p', null, 'static text'),
            createVNode('p', null, ctx.title, 1 /* TEXT */)
        ]))
    }

    在這段代碼中,當ctx.title屬性變化時,內容為靜態文本的p標簽節點也會跟著渲染一次,這很明顯式不必要的。因此,我們可以使用“靜態提升”,即將靜態節點,提取到渲染函數之外,這樣渲染函數在執行的時候,只是保持了對靜態節點的引用,而不會重新創建虛擬節點。

    const hoist1 = createVNode('p', null, 'static text');
    function render() {
        return (openBlock(), createBlock('div', null, [
            hoist1,
            createVNode('p', null, ctx.title, 1 /* TEXT */)
        ]))
    }

    除了靜態節點,對于靜態props我們也可以將其進行靜態提升處理。

    const hoistProps = { foo: 'bar', a: '1' };
    function render() {
        return (openBlock(), createBlock('div', null, [
            hoist1,
            createVNode('p', hoistProps, ctx.title, 1 /* TEXT */)
        ]))
    }

    預字符化

    除了對節點進行靜態提升外,我們還可以對于純靜態的模板進行預字符化。對于這樣一個模板:

    <templete>
    	<p></p>
        <p></p>
        <p></p>
        <p></p>
        <p></p>
        ...
        <p></p>
        <p></p>
        <p></p>
        <p></p>
    </templete>

    我們完全可以將其預處理為:

    const hoistStatic = createStaticVNode('<p></p><p></p><p></p><p></p>...<p></p><p></p><p></p><p></p>');
    render() {
        return (openBlock(), createBlock('div', null, [
    		hoistStatic
        ]));
    }

    這么做的優勢:

    • 大塊的靜態內容可以通過innerHTML直接設置,在性能上具有一定優勢

    • 減少創建虛擬節點帶來的額外開銷

    • 減少內存占用

    緩存內聯事件處理函數

    當為組件添加內聯事件時,每次新建一個組件,都會為該組件重新創建并綁定一個新的內聯事件函數,為了避免這方面的無意義開銷,我們可以對內聯事件處理函數進行緩存。

    function render(ctx, cache) {
        return h(Comp, {
            onChange: cache[0] || cache[0] = ($event) => (ctx.a + ctx.b);
        })
    }

    v-once

    v-once指令可以是組件只渲染一次,并且即使該組件綁定了動態參數,也不會更新。它與內聯事件一樣,也是使用了緩存,同時通過setBlockTracking(-1)阻止該VNode被Block收集。

    v-once的優點:

    • 避免組件更新時重新創建虛擬DOM帶來的性能開銷

    • 避免無用的Diff開銷

    到此,相信大家對“Vue編譯優化的實現流程是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    vue
    AI

    昭通市| 阿荣旗| 秀山| 云阳县| 德安县| 淄博市| 浠水县| 开封市| 额济纳旗| 安图县| 马龙县| 周宁县| 瑞丽市| 烟台市| 威信县| 石家庄市| 巨野县| 高雄县| 丰镇市| 阳东县| 伊吾县| 昔阳县| 南安市| 寿光市| 冀州市| 鄂托克旗| 滨州市| 江陵县| 和政县| 香格里拉县| 高安市| 姜堰市| 磴口县| 光山县| 绥棱县| 原平市| 阿坝县| 灵山县| 武穴市| 繁峙县| 三亚市|