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

小白使用 vue2.6.10 的 v-for 遇到的一个问题从而引发了一个对于这一行业的思考,希望有人可以开导我

  •  
  •   zazalu · 2019-08-08 21:07:20 +08:00 · 8463 次点击
    这是一个创建于 1713 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我遇到的问题是这样的,我描述下:

    问题 1:

    父组件使用"props"传递一个数组"items"给子组件,

    子组件使用 v-for 处理这个"items"来循环打印"item"中的 content 字段内容,

    过了一段时间后,我会去触发一次父组件的 items 更新,类似如下代码:

    ```
    this.items = this.items.concat([{id:11, content: "第十一条通知"}])
    ```

    因为 vue 官方文档说过,如果是非变异方法改变数组内容,直接使用"replace a array"的方式,也就是我上面的方式.

    所以我认为我的代码是没问题的.

    但是事实却痛击了我, 不知道为啥 v-for 循环并没有刷新数据.

    于是我先使用{{items}}检查了 props 的数据是否已经更新.

    结果是{{items}}确实已经更新了第十一条通知的内容进去.

    并且我使用 slice 方法去更新数组内容,是可以的, 就唯独 concat 方法不行

    问题 2:

    问题 2 是我在问题 1 后出现的一个新问题.

    我将{{items}}放到了 v-for 所在组件的后面(问题 1 中我是放在最前面验证的), 这时候神奇的事情发生了.

    v-for 居然更新了内容.

    所以 vue 内部到底发生了什么... 我该怎么去解... 文档我也上下翻了好几次, google,baidu 了类似问题,但是很遗憾没有和我遇到一样问题的伙伴出现

    问题 3:

    我依旧不服, 想着在单元测试中也试试会不会存在这个问题,

    所以我使用 vue-cli 创建了一个超级简单的 vue 单组件项目作为我的单元测试项目, 然后我将一些核心代码移了过去,重新测试了下

    (我强调下, 单元测试的代码肯定是和我真正项目中用的是一样的! )

    结果是没有任何问题, v-for 工作完全正常!

    我.................................................................................



    总结:

    到最后, 我没法搞定这个问题(我采用的是问题 2 中描述的折中但是非常不优雅的写法), 感觉内心非常的难受, 关电脑, 回宿舍, 简单的吃了下晚饭, 吃着吃着差点掉眼泪 , 感觉写代码也至少一年多了, 这类问题以前在使用 jsp 的时候其实也遇到过类似的. 结果,一年过去了, 我连这种问题都没法搞定, 还谈什么设计模式,算法呢, 一年来感觉压根没有多大提升, 除了找资料和看英文文档能力感觉比以前强了以外, 能做的无非只有 crud.

    这么一个问题, 搞得我焦头烂额,浪费了一下午+晚上几个小时的青春. 我想真诚的问下各位前辈们, 是不是我这种人不适合做程序员
    第 1 条附言  ·  2019-08-09 11:03:23 +08:00
    太好了!!!! 我复现了! 总算不用把我所有的垃圾代码曝光了(脸皮厚...)

    完整复现 demo 已上传! 按照我写的复线流程操作可以复现!

    https://github.com/zazaluMonster/array-replace-problem-in-vue-v-for-demo

    希望有大佬可以帮忙! vue 内部到底发生了什么!
    第 2 条附言  ·  2019-08-09 11:20:38 +08:00
    环境:

    ubuntu18.04

    chrome Version 75.0.3770.142 (Official Build) unknown (64-bit) (直接黏贴了 chrome 汇报的版本)

    node -v
    v10.16.0

    npm -v
    6.10.1

    "core-js": "^2.6.5",

    "iview": "^3.4.2",

    "vue": "^2.6.10"
    第 3 条附言  ·  2019-08-09 11:46:07 +08:00
    我看很多人说无法复现. 所以我上传了 gif 图来表现我自己的情况.
    第 4 条附言  ·  2019-08-09 14:12:55 +08:00
    感谢 @crs0910 提供的在线演示 demo

    https://codesandbox.io/s/wwu3n

    虽然代码实现不同, 但是问题本质是一样的(比我的更精简)
    第 5 条附言  ·  2019-08-09 15:01:16 +08:00
    已解决问题根源, 是手动操作 DOM 结构, 导致 vue 懵逼的问题.(现在留有的唯一疑惑是 vue 不给出一些警告或者提示)
    121 条回复    2019-08-10 14:01:42 +08:00
    1  2  
    coang
        101
    coang  
       2019-08-09 14:49:59 +08:00
    vue 文档 对象 /数组变更 列明 concat 不能用 和直接赋值 vue 是无法监测的
    crs0910
        102
    crs0910  
       2019-08-09 14:50:49 +08:00
    @zazalu #97
    @leemove #95 那是因为我刚刚在切换 vue 的版本,哈哈,给你们带来困扰了。2.2.x 是会报错的,不用 catch
    zazalu
        103
    zazalu  
    OP
       2019-08-09 14:52:02 +08:00
    @crs0910 恩恩 我随便找了个低版本的,切换成 2.1.10,看到报错了
    Error: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
    leemove
        104
    leemove  
       2019-08-09 14:52:29 +08:00
    @crs0910 可以看到报错吧 应该是 insertBefore 错了吧 我觉得我分析的没毛病的
    crs0910
        105
    crs0910  
       2019-08-09 14:53:01 +08:00
    @zazalu #103 对的,新版本会先判断父节点有没有变化,没有才会 insert,所以也不报错了。但是不更新视图。
    leemove
        106
    leemove  
       2019-08-09 14:53:31 +08:00
    @zazalu 这个问题不是一个简单的问题,是因为第三方依赖带来的错误,这种问题的分析是很复杂的,没必要因为踩了一个坑就灰心丧气,至少你现在已经明白了许多关于 vue 的新知识,下次就不会采坑了.
    mars0prince
        107
    mars0prince  
       2019-08-09 14:54:08 +08:00
    不明白你的例子是什么意思,最好有代码演示一下
    你提供的在线演示 demo,是因为你把最后一个元素删掉了啊,vue 比较 children 时候需要添加 vnode,在最后一个元素前面 insert,你把它从 dom 上删了,还不改 vdom,肯定插不进去嘛,不信你把 p 标签用 div 包起来就没问题了
    zazalu
        108
    zazalu  
    OP
       2019-08-09 14:54:13 +08:00
    @leemove 看到了,但是为什么高版本反而去掉了报错呢.. 我感觉我因为这个问题浪费了一天的时间(非纯前端 er,一个打杂半吊子程序员)
    mars0prince
        109
    mars0prince  
       2019-08-09 14:55:55 +08:00
    不要去手动操作 dom,会导致 vdom 和 dom 不一致的情况,说了很多次了,这个不是 bug,是你瞎用
    zazalu
        110
    zazalu  
    OP
       2019-08-09 14:56:37 +08:00
    @mars0prince 我的例子里是 iview 的 Drawer 组件使用的 transfer-dom.js 的第 27 行代码,使用 js 手动操作了 DOM 结构.不过问题也算找到根源了哈哈
    zazalu
        111
    zazalu  
    OP
       2019-08-09 14:57:06 +08:00
    @mars0prince 恩恩好的(委屈.)
    zazalu
        112
    zazalu  
    OP
       2019-08-09 14:57:52 +08:00
    @leemove 谢谢, 我又可以继续 code 了
    mars0prince
        113
    mars0prince  
       2019-08-09 14:58:45 +08:00
    @zazalu 多看看源码就知道了,vue 源码还是很简单的
    SilentDepth
        114
    SilentDepth  
       2019-08-09 15:02:47 +08:00
    @zazalu #108 同不理解为什么把这里 if 掉还不给个提示,父节点发生变化不应当是个常见场景啊
    leemove
        115
    leemove  
       2019-08-09 15:06:40 +08:00
    @SilentDepth
    @zazalu
    他的代码主要设计还是有一些前提的,可能没有过多考虑手动修改 dom 的场景.
    santom
        116
    santom  
       2019-08-09 17:53:12 +08:00
    感觉是不是哪里写法可能有什么问题 (包括 不限于组件、传值、逻辑...)写过的项目中 列表加载更多 这些 等等 都是用 concat 去拼接的 复杂数据 vue 默认不会帮你监控变化值,所以出现了$set 这么个东西 但是我理解的 concat 重新赋值实际对于 vue 来说 是将值完全变了的 ( concat 返回新数组,也就是新引用,并非原数据),这样应该是可以监控到的
    zhouyg
        117
    zhouyg  
       2019-08-09 17:56:54 +08:00
    很好的问题,一下子直击最底层的技术实现,非常值得学习,对 vdom 和 dom 的理解很有帮助哈😄
    zhouyg
        118
    zhouyg  
       2019-08-09 17:57:51 +08:00
    感谢楼主提供的坑
    zazalu
        119
    zazalu  
    OP
       2019-08-09 19:05:42 +08:00 via Android
    @santom 是 iview 的手动 dom 操作导致 vue 懵逼,同时 2.6 版本又不做警告,如上述 2.2 版本左右的都会报错
    yiyi11
        120
    yiyi11  
       2019-08-10 13:59:44 +08:00 via Android
    我认为你非常适合当程序员。
    yiyi11
        121
    yiyi11  
       2019-08-10 14:01:42 +08:00 via Android
    至于“浪费一下午加晚上几小时的青春”,写一天的 crud 也不见得无悔青春。
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   969 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 43ms · UTC 20:34 · PVG 04:34 · LAX 13:34 · JFK 16:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.