V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiaome
V2EX  ›  Vue.js

vue 遇到了一个奇怪的问题.jpg

  •  
  •   xiaome · 2018-07-18 17:34:19 +08:00 · 2119 次点击
    这是一个创建于 2102 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天遇到个比较奇怪的问题,导致了死循环,请教了一系列大佬之后最终判断为 vue 视图重绘了。 记录一下,也想请求一下各位大佬更好的解决方案, 这里感谢各位了。

    贴上代码, 如果需要执行请注释

    <html>
    <head>
      <title>测试</title>
      <link href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
      <div id="app">
        <div class="alert alert-primary" v-for="item in datas">
          <ul v-for="month in item.months">
            <li>{{month.month}} - {{month.name}}</li>
            <!-- 循环方法 -->
            <li>{{lastMonthStaff(item)}}</li>
          </ul>
        </div>
      </div>
      <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
      <script>
        var app = new Vue({
          el: '#app',
          data: {
            datas: [
              {
                title: 2018,
                months: [
                  { month: 1, name: '12asds' },
                  { month: 2, name: 'adsfsdf' },
                  { month: 3, name: 'zcvzcv' },
                  { month: 4, name: 'zvcc' },
                  { month: 5, name: 'eqr124' },
                  { month: 6, name: 'asdfsd' },
                  { month: 7, name: 'dsafds' },
                  { month: 8, name: 'adsfsdfv' },
                  { month: 9, name: 'yiyu' },
                  { month: 10, name: 'cxvcxv' },
                ]
              },
              {
                title: 2019,
                months: [
                  { month: 1, name: 'uteye' },
                  { month: 2, name: 'dfgh' },
                  { month: 3, name: 'AF' },
                  { month: 4, name: 'FAGSGH' },
                  { month: 5, name: 'DSGS' },
                  { month: 6, name: '' },
                  { month: 7, name: 'SFDG' },
                  { month: 8, name: 'DSFG' },
                  { month: 9, name: 'SDFG' },
                  { month: 10, name: 'FDHDG' },
                ]
              },
              {
                title: 2020,
                months: [
                  { month: 1, name: 'FGHGF' },
                  { month: 2, name: 'FGH' },
                  { month: 3, name: 'FDH' },
                  { month: 4, name: 'FGHCVN' },
                  { month: 5, name: 'BVCN' },
                  { month: 6, name: '' },
                  { month: 7, name: 'UIY' },
                  { month: 8, name: 'DSHFM' },
                  { month: 9, name: 'FSD' },
                  { month: 10, name: 'DFSFDG' },
                ]
              },
            ],
            allotUsers: []
          },
          methods: {
            isShow(val) {
              switch (val) {
                case undefined:
                  return false
                case '':
                  return false
                case null:
                  return false
                default:
                  return true
              }
            },
            lastMonthStaff (row) {
              for (var obj of row.months) {
                if (obj.month === new Date().getMonth()) {
                  if (this.isShow(obj.name)) {
                    // 触发方法
                    this.setAllotUsers(obj.name)
                    // 输出上月 name
                    return obj.name
                  } else {
                    return '无'
                  }
                }
              }
            },
            setAllotUsers(val) {
              console.log(val)
              // 会触发视图更新导致死循环
              this.allotUser.push(val)
    
              // 想的笨办法解决方案
              this.allotUsers[this.allotUsers.length] = val
            },
          }
        })
      </script>
    </body>
    </html>
    
    10 条回复    2018-08-22 09:47:09 +08:00
    noe132
        1
    noe132  
       2018-07-18 18:42:10 +08:00   ❤️ 1
    vue 确实会在控制台报错 render loop.
    你的 setAllotUsers 循环执行了 1000 多次后被 vue 叫停了。
    setAllotUsers 会触发 render,render 会调用 lastMonthStaff,于是就死循环了。

    设置数组的属性不会被 vue 监测到所以不会有问题。
    你的 view 编译成的 render 函数中调用了 lastMonthStaff,
    lastMonthStaff 调用了 setAllotUsers
    setAllotUsers 调用了 allotUsers,被 vue 感觉到了(对,跟踪你的依赖)
    让 vue 以为 allotUsers 的更改会对 view 造成变化,需要重新 render,于是重新 render 又造成了属性变化。

    我的建议是,数据的处理,放在 view 渲染之前。不要再 view 里添加修改数据的代码,这样会让逻辑变得不够清晰。因为 render 函数并不是你调用的,你不清楚具体什么时候 render 函数会被调用。render 函数应该是无副作用的。

    具体 vue 是怎么跟踪依赖的,这还要翻翻 vue 的源码才知道了
    xiaochocking
        2
    xiaochocking  
       2018-07-18 18:46:05 +08:00
    在 template 里调用函数修改数据本来就是不对的
    chairuosen
        3
    chairuosen  
       2018-07-18 18:50:42 +08:00
    在 get 里不要 set,在 render 里只调 get
    luoway
        4
    luoway  
       2018-07-18 19:24:54 +08:00   ❤️ 1
    v2ex 帖子不能删,过一年楼主回来看这个 isShow 会感到羞愧的
    ioc
        5
    ioc  
       2018-07-18 19:31:50 +08:00 via Android
    @luoway 可以删的
    panyanyany
        6
    panyanyany  
       2018-07-18 22:03:47 +08:00
    @luoway #4 你不说还没注意到,这个 isShow 确实有点意思……
    billyu
        7
    billyu  
       2018-07-19 06:49:16 +08:00 via Android
    @panyanyany hh break 被吃了
    yinjunjian0
        8
    yinjunjian0  
       2018-07-19 10:14:30 +08:00
    isShow 有丶东西
    dengshen
        9
    dengshen  
       2018-08-22 09:37:29 +08:00
    @billyu 已经 return 没必要在写 break 了 如果用了标准 eslint 的话写 break 反而会报错
    billyu
        10
    billyu  
       2018-08-22 09:47:09 +08:00
    @dengshen #9 嗯嗯 是的 多谢提醒
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2741 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 12:54 · PVG 20:54 · LAX 05:54 · JFK 08:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.