最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • vue3.0系列—一个issue

    正文概述 掘金(不讲码德)   2021-03-09   672

    vue3.0系列—响应式

    vue3.0系列-渲染流程

    vue3.0系列-新特性

    前言

    这个其实不算是vue3的内容,是在vue2版本的一个isse。是在阅读vue3源码的时候发现的issue,但是觉得比较有趣,就在这里记录下

    链接:issue

    问题

    代码如下:

     <div class="box1" v-if="expand">
        <i @click="expand = false, countA++">Expand is True</i>
      </div>
      <div class="box2" v-if="!expand" @click="expand = true, countB++">
        <i>Expand is False</i>
      </div>
      <div>
        countA: {{countA}}
      </div>
      <div>
        countB: {{countB}}
      </div>
    

    正常情况:

    • 点击box1i标签,expand变成faslebox1隐藏,box2显示,countA+1
    • 然后点击box2expand变成truebox2隐藏,box1显示,countB+1

    真实情况:

    • 点击box1i标签,expand变成truebox1显示,box2隐藏,countA+1,countB+1
    • 继续点击,还是上面的情况

    可以去该链接实际操作一下: Reproduction link

    产生原因

    从上面的结果上看,仿佛点击一次,box1box2的click事件都触发了。这个是为什么呢?

    尤大大在该issue回答到:

    The inner click event on fires, triggering a 1st update on nextTick (microtask) The microtask is processed before the event bubbles to the outer div. During the update, a click listener is added to the outer div. Because the DOM structure is the same, both the outer div and the inner element are reused. The event finally reaches outer div, triggers the listener added by the 1st update, in turn triggering a 2nd update. This is quite tricky in fix, and other libs that leverages microtask for update queueing also have this problem (e.g. Preact). React doesn't seem to have this problem because they use a synthetic event system (probably due to edge cases like this).To work around it, you can simply give the two outer divs different keys to force them to be replaced during updates. This would prevent the bubbled event to be picked up

    啥意思呢,就是,

    点击i标签之后,事件会向上冒泡,冒泡到box1标签,

    但是冒泡也是微任务,甚至这个微任务执行还在nextTick之后。

    然后在vue做update的时候,发现box1box2标签的type一样,且没有key,就复用了box1的dom,再将box1propschildren换成box2的,则box2的click事件也重新绑定到了box1上。

    接下来事件冒泡到box1的dom上,触发click事件。所以造成了上面的现象。

    所以可以通过加key处理这个问题。

    可以看下vue3中判断是否为同一节点的代码:

     function isSameVNodeType(n1: VNode, n2: VNode): boolean {
      return n1.type === n2.type && n1.key === n2.key
    }
    

    解决方案

    在vue3中其实不会有这个问题了,因为vue3会为v-if添加默认的key,也就不会复用节点。

    那在vue2中怎么解决的呢?

    vue会为event回调包装一层,返回一个invoke,后续移除事件也是移除的包装函数:

    function createInvoker(
      initialValue: EventValue,
      instance: ComponentInternalInstance | null
    ) {
      const invoker: Invoker = (e: Event) => {
        const timeStamp = e.timeStamp || _getNow()
        if (timeStamp >= invoker.attached - 1) {
          callWithAsyncErrorHandling(
            patchStopImmediatePropagation(e, invoker.value),
            instance,
            ErrorCodes.NATIVE_EVENT_HANDLER,
            [e]
          )
        }
      }
      invoker.value = initialValue
      invoker.attached = getNow()
      return invoker
    }
    

    initialValue就是绑定的事件回调。

    可以看到invoke函数有一个attached的变量,这个变量的值是vue在做渲染或者更新时的时间戳

    在触发事件回调的时候,会判断事件触发的时间戳是否大于缓存的时间戳,大于则触发回调。

    如果是冒泡的事件,因为捕获-触发-冒泡一系列过程,事件的时间戳是不变的,而vue触发update是会重新执行patchEvent的,创建新的invoke包装函数。也就会更新ttached的值,从而冒泡的时间戳会小于invoke缓存的事件戳,即不会重复触发事件。

    总结

    从这个issue上,可以更好的理解下面三个知识点

    1. vue在更新时会尽量复用节点
    2. 事件捕获-冒泡是微任务
    3. 事件捕获-触发-冒泡的一系列过程中,e是同一个

    这应该是vue3.0系列的最后一篇了,后续有想法再做补充。也总算是完成对vue3.0源码阅读后的总结了。

    也欢迎大家指出问题,提出建议。


    起源地下载网 » vue3.0系列—一个issue

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元