本文记录一下 Vue 里常见的坑,为下一次 GitChat 做准备。
(更多…)分类: vue
-
![解决 [Vue warn]: You may have an infinite update loop in a component render function](https://blog.meathill.com/wp-content/uploads/2017/09/IMG_2850-1200x900.jpg)
解决 [Vue warn]: You may have an infinite update loop in a component render function
今天写着写着,突然发现控制台里有错误:
[Vue warn]: You may have an infinite update loop in a component render function这个问题很奇怪,之前从来没有遇到过。如果是我自己主导的项目,倒也好办,慢慢 debug 就是;偏偏在公司的项目里遇到这个问题,而公司项目的体系结构很复杂,我还没完全掌握。更恼火的是,因为体系复杂,debug 也非常困难,再加上尚无测试框架,这个难搞啊……
好死不死的,当时是下午3、4点钟,正好到了肚饿的时刻,结果又落入低血糖状态,真是屋漏偏逢连阴雨,船小又碰顶头风,饿得我脑仁生疼……
不过终于还是被我 Google + debug 出来。事实上是这样的,在
v-for循环当中,如果用方法或者计算属性对 vm.$data 的属性进行操作,理论上,可能因为修改到循环对象,诱发无限循环。此时 Vue 就会发出警告(并不是真的已经无限循环了)。例如这样一个组件,它里面是用
:checked + <label>实现的一组按钮。它有以下功能:- 为了能够分组,需要设置它们的
name属性 - 为了能够用
<label>控制<input>,需要给<input>设置id - 按钮可以被删除
于是我选择这样做:
<template> <div> <template v-for="(item, index) in items"> <input type="checkbox" :name="'my-component-' + selfIndex" :id="getID"> <label :for="getID(false)"> <button type="button" @click="remove(index)">×</button> </template> </div> </template> <script> let count = 0; export default { data() { return { selfIndex: 0, itemIndex: 0, } }, methods: { getID(increase = true) { // 注意,问题就出在这里 if (increase) { this.itemIndex++; } return `my-component-${this.selfIndex}-${this.itemIndex}`; }, }, beforeMount() { this.selfIndex = count; count++; } } </script>这里,为了能生成唯一 ID,我选择每次循环都对
vm.itemIndex++,这就会出现前面说的问题,存在隐患。解决的方案有两种,一种是把
itemIndex也放在局部变量里,使它不直接关联在组件上;另一种则是写一个全局的唯一 ID 生成函数,然后引用进来。原理都是一样的。
这两天听评书《乱世枭雄》,学到一句话“拉屎脸朝外”,形容讲义气,不知道咋联系的……
- 为了能够分组,需要设置它们的
-

Vue 里 $mount 方法的作用
今天由同学问道:“vue文档里说如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。这个方法在项目中有什么用呢,是一个组件可以挂载到多个dom上就这一个作用吗?”
我的答案如下:
有些时候你创建 vm 的时候 DOM 可能还没准备好,或者写 vm 的时候不确定它要挂在哪个 DOM 上,这个时候就有用了。
当项目大的时候,很多情况是开发的时候估计不到的。比如 A 类里面想使用一个 B 类,但是 B 类有 10 个子类,对应不同场景,你写 A 类的时候根本不知道产品中会使用哪个 B 的子类,所以就要想办法把控制权交出去,这就叫“控制反转”——即控制权从开发者的手上,交到使用者的手上。
我觉得 $mount 这里也是类似的,就是你写了一个 Vue 类,但是不能控制使用场景。所以就要有一个 $mount 方法,由使用者控制,绑到目标 DOM 上。
他又问:“懂了,还有一个问题是一个组件能不能利用$mount属性挂载到多个dom上,我刚刚试了一下是可以渲染,这种用法常见吗??”
我答:
我没这么做过,不过我认为是合理的。
我觉得这个跟 Vue 的定位有关。Vue 和 React、Angular 不一样,后者是大公司企业级产品。Vue 其实很想取代 jQuery,所以这方面其实跟 jq 的插件是很相似的。
-

Vue 笔记:事件
onload需要侦听
load<template> <img src="/path/to/img" @load="onLoad"> </template> <script> export default { methods: { onLoad(event) { // do something } } } </script>drop需要阻止
dragover的默认行为,不然不触发。<template> <div class="drop-area" @dragover.prevent="onDragOver" @drop="onDrop"> </template> <script> export default { methods: { onDragOver(event) { // 此时仍然可以处理 }, onDrop(event) { console.log(event.dataTransfer); } } } </script> -

小试 Element UI
不确定写多长,写先结论吧:暂时不推荐使用。原因如下:
- 影响使用的小 Bug 有点多
- 需要重新学习一门语言
接下来详述。
从前司离职之后,我开始更新技术栈。离开惯用的 Backbone,考虑再三,投入 Vue 怀抱。选择 Vue,而不是竞品 Angular、React 有三个理由:
- 文档友好,社区活跃。
- 模块拆分的很好,学习曲线平缓。
- 基于标准化技术,可以最大限度的避免浪费。
不过实操之后发现,Vue 与我惯用的 Bootstrap 有些冲突,主要在于:
- Bootstrap 对过渡效果和切换的操作依赖于样式,比如
.active、.in。Vue 在处理模板时会把当前样式先缓存起来,然后根据数据增删绑定的样式。此时就可能出问题,tab 页切不动或者动画突然打断之类的。 - Bootstrap 会广播特定的事件,这些事件无法被 Vue 捕获,只能在
mounted()的钩子里手工绑定。
于是我觉得,既然根基(jQuery)变了,最好把整条线都更新了吧。左右看了看,准备先试下 Element UI。这是饿了么推出的基于 Vue2.0 的组件库,目测组件齐全,文档详细,而且直接以 2.0 为基础,符合我追新的想法。
实际用了之后……唉……有点……遗憾。项目地址。
首先,Element UI 把所有组件都封装了,包括布局,比如
<el-row>、<el-col>,我觉得这样太过了。从现实经验来看,布局元素几乎不可能够用,别人总要补充一些。封装的元素我不太知道最终生成的代码是什么样的,也就不好操作,总不能审查元素一个一个看吧?——对了,Element 的文档里缺少样式列表,也是个问题。封装的另一个问题,所有元素都要通过后期渲染,总让我感觉不舒服。以及,我几乎无论干什么都要查文档,几乎没法直接动手,这和我选择 Vue 的初衷是相违背的。
接下来,小 Bug,有点多。除去布局和提示之类,我只用到3个组件:
<el-button>、<el-table>、<el-pagination>,结果就遇到4个 bug,浪费很多时间去调试,有两个我给他们开了 issue,还有两个懒得弄了。这里列一下吧:<el-button :loading="scope.row.fetching">无法把 loading 绑定到数据的.fetching属性上<el-pagination>设置total不更新视图<el-pagination>更新total之后再次广播current-change事件,导致重复刷新<el-table>里每行的ref属性没法正确生成数组
2017-04-04 更新:
经过 issue 沟通,我的理解的确有些问题,
- Vue 绑定属性的时候,如果该属性层次较深,比如
a.b.c,就不会修改它的getter/setter,于是会失去响应式更新的特性;同时也不会报错。 <el-table>不是通过v-for渲染的,自然ref的表现也就正常了。
可能别的组件很健壮吧,我运气不好。
总之,我觉得就目前这个版本,1.2.5,来看,Element UI 还没到让人放心用并且用得好的程度。
下一次我可能会选别家的再试下,或者继续用 Bootstrap 然后自己拼些小组件出来——我这次就是想找个有 loading 的 button 才找 UI 库的。
啊,最后,还是感谢 Element UI 团队,感谢饿了么。希望你们再接再厉,相信将来这套库会更好。
-

Electron + Vuex 导致视图无法自动刷新的问题
前几天开发 Meart 的过程中,碰到一个棘手的问题:
- 修改数据,视图不变
- 切换视图,再回到原来的视图,数据刷新
- 通过 Vue devtool 查看 Vuex,有数据提交
- 点击最新提交点,或者 “commit all”,之后所有操作正常
因为我对 Electron 和 Vue 都不是很了解,所以这个问题困扰我很长时间。我尝试了各种办法,包括
.splice(),let data = this.data; this.data = data;,但是都没有效果。后来实在没办法了跑到 Vue 论坛里发帖求助。没想到很快就得到回复,对方虽然没用过 Electron,但答案看起来方向是对的:
remote.getGlobal('var')得到的对象不是一般意义上的对象,Vue 没有办法给它加上getter/setter,所以无法实现响应式。我尝试了一下果然如此,用
require('data.json')替换从主线程中取值,就一切问题都解决了。或者把.getGlobal('data')取出来的值,深度Object.assign({}, source)一下也可以。看来有必要补一下 ES5 里面 Object 新增函数的知识了。

