Vue数据绑定不当导致死循环的问题记录

—— 通过一次错误的数据绑定探究Vue的数据绑定机制

momo314相同方式共享非商业用途署名转载

是这样,今天遇到一个问题。

在一个Vue项目中,有这么一个数据绑定模板:

<!--模板-->
<template>
  <div class="hello">
    <ul>
      <a :href="formatUrl1(article)">{{ article.name }}</a>
    </ul>
  </div>
</template>
// vue model
export default {
    name: 'HelloWorld',
    data () {
        return {
            article: {
                id: 1,
                name: '3000ways',
                url: 'http://3000ways.com/articles/{id}'
            }
        }
    }
}

很简单对不对!就这么简单一个页面,怎么可能会有问题呢?

我也是这么想的,根本不用测试,就是这么自信!

打包! -> 没问题! -> 发版! -> 成功! -> 访问! -> 页面挂了!

页面挂了!页面挂了!

显示空白页面,页面处于loading状态,且并没有发送任何网络请求,控制台没有报错信息。

本地运行测试:果然是产生了循环调用,报错如下:

  [Vue warn]: You may have an infinite update loop in a component render function.
 found in

 ---> <HelloWorld> at src\components\HelloWorld.vue
        <App> at src\App.vue
          <Root>

为什么打包后控制台没有报错信息,而在本地运行的时候能看到错误信息呢?

目测是因为本地运行时是直接解释源码执行,同时vue在调试时会输出一些更为详细错误信息,而在打包之后为了运行效率和包的大小而去除了一些只有在开发环境才会用到的相关功能,导致我们不能看到最为详细的调试信息。(不确定,待大神指导)

最终通过日志排查,定位到是方法 formatUrl1 会被无限次的循环调用,产生死循环,导致页面一致处于loading状态,无法加载。

// 方法示例:忽略不合理逻辑
function formatUrl1 (item) {
    console.log('url formatting.')
    item.url = item.url.replace(/{id}/gi, item.id)
    if (item.url.indexOf('?') > 0) {
        item.url = item.url.replace(/\?[\s\S]*$/ig, `?r=${Math.random()}`)
    } else {
        item.url = `${item.url}?r=${Math.random()}`
    }
    console.log('url formatted: ' + item.url)
    return item.url
}

一眼看去,没找到问题。。。又看了一边,还是没毛病。。。

不管了,重写!


// 方法示例:忽略不合理逻辑
function formatUrl2 (item) {
    console.log('url formatting.')
    let link = item.url.replace(/{id}/gi, item.id)
    console.log('url formatted: ' + link)
    return link
}

测试通过,问题完美解决!但是。。。

为什么呢?

对比 方法1 和 方法3 ,可以说,最大的区别应该就是:方法一直接对参数传入的对象进行操作;而方法二通过对参数传入的对象进行计算二得到了一个临时变量,并没有改变传入的参数对象。

可是猜测毕竟是猜测,如何验证呢?于使我们写了方法3:

function formatUrl3 (item) {
    console.log('url formatting.')
    item.url = item.url.replace(/{id}/gi, item.id)
    console.log('url formatted: ' + item.url)
    return item.url
}

可以看到:

  • 方法1 由于自身逻辑问题,每次执行时都会导致参数对象发生改变,而且也产生了死循环,循坏输出方法日志,最终开发环境会报错:infinite update loop
  • 方法2 完全不会对参数对象产生任何影响,方法日志只输出了 1 次
  • 方法3 相比方法1进行了逻辑简化,只会在方法第一次调用时改变参数对象,后续调用不会再进行更改,对应结果,方法日志输出了两次

这个对比应该就很明显了,结合Vue双向绑定的特点,我们完全可以推测:

对于拥有参数为 vue-model 对象的方法,该方法将会依赖于此参数对象,每当 vue-model 中对应的值发生变化后,都会重新执行此方法以计算出新的值。

✎﹏ 本文来自于 momo314和他们家的猫,文章原创,转载请注明作者并保留原文链接。