文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Vue编译优化的实现流程是什么

2023-07-05 01:47

关注

本篇内容主要讲解“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值来表示:

我们可以新建一个枚举类型来表示这些值:

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 )    ]))}

在这段代码中,当ctx.title属性变化时,内容为静态文本的p标签节点也会跟着渲染一次,这很明显式不必要的。因此,我们可以使用“静态提升”,即将静态节点,提取到渲染函数之外,这样渲染函数在执行的时候,只是保持了对静态节点的引用,而不会重新创建虚拟节点。

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

除了静态节点,对于静态props我们也可以将其进行静态提升处理。

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

预字符化

除了对节点进行静态提升外,我们还可以对于纯静态的模板进行预字符化。对于这样一个模板:

<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    ]));}

这么做的优势:

缓存内联事件处理函数

当为组件添加内联事件时,每次新建一个组件,都会为该组件重新创建并绑定一个新的内联事件函数,为了避免这方面的无意义开销,我们可以对内联事件处理函数进行缓存。

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的优点:

到此,相信大家对“Vue编译优化的实现流程是什么”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯