我是靠谱客的博主 细腻毛巾,最近开发中收集的这篇文章主要介绍从源码入手来分析Vue中常用的写法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

从源码入手来分析Vue中常用的写法(持续更新中···)
如何调试源码请参考另一篇文章(https://blog.csdn.net/seeyousayhi/article/details/108067185)

先说几个高频点

      • 1.v-for中key的作用和工作原理
      • 2.v-if和v-for优先级问题

1.v-for中key的作用和工作原理

工作中v-for非常常见,那你知道写key的时候发生了什么吗,一起来看一下
源码位置:vuesrccorevdompatch.js (updateChildren) 也就是diff发生的地方
先上例子:
在一秒后插入999这个数字

// template部分
<p v-for="item in list">{{item}}</p>

// script部分
	data() {
		return { list: [1, 2, 3, 4, 5 ] }
	},
	mounted() {
		setTimeout(() => {
		// 在3前面插入999
			this.list.splice(2,0, 999)
		}, 1000)
	}

diff的时候我们可以认为就是新老数组之间的比较:
在这里插入图片描述

不用key结果图解:

在这里插入图片描述
使用key:

// 第一次循环 patch 1
1	2	3	4	5
1	2	999	3	4	5

// 第二次循环patch 2
2	3	4	5
2	999	3	4	5

// 第三次循环patch 3
3	4	5
999	3	4	5

// 第四次循环patch 4
3	4
999	3	4

// 第五次循环patch 3
3
999	3

// 老数组循环之后即全部完成,新数组中剩下999,找到关系,创建999并插入到3前面

可以看出来不使用key的时候,老数组和新数组 是按顺序来比较的,1 和1 ,2和2,3和999 ,4和3 以此类推。
使用了key之后 1和1,2和2,然后是5和5,4和4,3和3 ,最后剩下一个999,此时比较的顺序发生了变化!!
疑问来了,为什么会是这样呢???
从源码中找答案,我们进去看一下:
在这里插入图片描述
核心方法 updateChildren,里面发生的就是比较规则,updateChildren主要作⽤是⽤⼀种较⾼效的⽅式⽐对新旧两个VNode的children得出最⼩操作补丁。执⾏⼀个双循环是传统⽅式,vue中针对web场景特点做了特别的算法优化。
从方法刚开始的定义我们可以大致了解到,首先是定义了4个index,和4个vnode,分别是新老数组左右头尾两侧都有⼀个下标(index)和vnode本身(vnode)的这么一个标记,如图:
在这里插入图片描述

在遍历过程中这⼏个变量都会向中间靠拢。 当oldStartIdx > oldEndIdx或者newStartIdx > newEndIdx时结束循环,并伴随着vnode的移动,来判断追加还是删除。
循环开始时进入while中,走完前两个isUndefined会进入到else if(sameVnode)的方法中。
让我们进去看看:
在这里插入图片描述
当我们不写key的时候,key的值就是
undefined
,undefined === undefined 是成立的,此时我们比较的是同级元素tag标签也成立,isComment不是注释也成立,后面一样的也成立,会认为此时我们比较的是相同元素,而后在实际的dom更新函数中执行到3和999的时候,值不相同,强制更新,再后面4和3也不同,强制更新。
反观写key的时候,上面进行的比较 1和1的key相同,2和2的key相同,3和999的key不同,此时不会发生更新,而是发现还有相同的index,就进入到newEndIndex和oldEndIndex的else if 循环当中, 从后面开始比较了,直到剩下一个999,dom才更新。

结论
1.最重要的一点,key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以判断两个节点是否是同一个,从而避免频繁更新 不同的元素,减少DOM操作,使得性能更加高效。
2.若不设置或者不合理设置key,在列表更新时会引发一些意外的bug。
3.vue中使用相同标签名元素的过渡切换时,也会使用到key,其目的也是为了可以让vue区分它们,否则vue只会替换其内部属性而不会触发过渡效果。

2.v-if和v-for优先级问题

先上代码,需求是根据list里面的 isShow来判断显隐,来看看这种写法的渲染函数是怎样的,打印一下render内容

<div id="app">
        <div>
            <div v-if="item.isShow" v-for="item in list">{{item.id}}</div>
        </div>
    </div>
    <script>
      let vm = new Vue({
            el: '#app',
            data: {
                list: [{ id: 1, isShow: true },{ id: 2, isShow: false }]
            }
        })
      console.log(vm.$options.render)
      // 打印结果

下面是打印结果,发现一堆_开头的函数,都是vue底层实现的内部方法。
_l 是渲染列表函数,_c 是创建标签,_e是空函数

function anonymous() {
            with(this) {
                return _c('div', {
                    attrs: {
                        "id": "app"
                    }
                }, [_c('div', _l((list),
                    function(item) {
                        return (item.isShow) ? _c('div', [_v(_s(item.id))]) : _e()
                    }), 0), _v(" "), _c('div', [_v(_s(renderContent))])])
            }
        }

可以看出来是先执行_l 进行了一轮循环,然后判断item.isShow来进入函数_c还是函数_e,此时能发现是for循环的优先级比较高,然后是if

换另一种写法,通过computer计算属性来filter出要循环的列表

<div id="app">
        <div>
            <!-- <div v-if="item.isShow" v-for="item in list">{{item.id}}</div> -->
            <div v-for="option in options">{{option.id}}</div>
        </div>
    </div>

    <script>
        let vm = new Vue({
            el: '#app',
            data: {
                list: [{ id: 1, isShow: true }, { id: 2, isShow: false }],
            },
            computed: {
                options() {
                    return this.list.filter(item => item.isShow)
                }
            }
        })
        console.log(vm.$options.render);

再来看一下render函数

 with(this) {
                return _c('div', {
                    attrs: {
                        "id": "app"
                    }
                }, [_c('div', _l((options), function(option) {
                    return _c('div', [_v(_s(option.id))])
                }), 0)])
            }

通过比较,发现利用了computed 就跳过了判断 ,直接_l ,很显然这种方法是很好的
再通过源码来看一下for与if优先级的比较,源码位置在 src/compiler/codegen/index.js

export function genElement (el: ASTElement, state: CodegenState): string {
  if (el.parent) {
    el.pre = el.pre || el.parent.pre
  }

  if (el.staticRoot && !el.staticProcessed) {
    return genStatic(el, state)
  } else if (el.once && !el.onceProcessed) {
    return genOnce(el, state)
  } else if (el.for && !el.forProcessed) {
    return genFor(el, state)
  } else if (el.if && !el.ifProcessed) {
    return genIf(el, state)
  } else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
    return genChildren(el, state) || 'void 0'
  } else if (el.tag === 'slot') {
    return genSlot(el, state)
  } else {

从源码的实现上来看 for的优先级就是高于if,如果两者写在同级的话,执行顺序就是先循环再判断,因为渲染的时候会执行很多次,就会浪费一些性能,我们在平常写的时候就可以避免这种写法!!!
结论:
1.vue在实现上 v-for 优先于 v-if被解析
2.如果两者写在同级,每次渲染都会先循环再判断,浪费了性能

最后

以上就是细腻毛巾为你收集整理的从源码入手来分析Vue中常用的写法的全部内容,希望文章能够帮你解决从源码入手来分析Vue中常用的写法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(67)

评论列表共有 0 条评论

立即
投稿
返回
顶部