今天使用iview画页面时遇到一个很简单但是搞了很久的bug,加了一个控件
<user-select
style="width:200px"
@on-change="handleSelect"
ref="user"
v-model="form.userCode">
</user-select>
然后使用$refs调用组件属性
this.$refs.user.initData();
然后神奇的地方出现了,不管怎么该,一直报Error in mounted hook: "TypeError: Cannot read property 'initData' of undefined"错误,这个方法之前一直用的呀,各种猜想为啥在这里不行了。
最后发现一件神奇的事情,这个组件换个位置就可以正常工作了,然后放回原处就不行了,然后我想,难道这是iview的bug,某两个控件不能紧跟着放在一起?然后又转过头来想,不可能呀,整个vue生态都这么成熟了,不应该会存在这么低级的错误呀!
然后突然发现,这个控件是所在的位置多了一个v-if判断,难道是因为渲染的顺序导致的?
<Row :gutter="32" v-if="type=='2'">
<Col span="12>
<FormItem label="用户选择" prop="userCode" >
<user-select
style="width:200px"
@on-change="handleSelect"
ref="user"
v-model="form.userCode">
</user-select>
<FormItem>
</Col>
<Row>
突然感觉像发现新大陆一样欣喜若狂,果然删掉了v-if属性之后能够正常工作了。
然后想应该是vue的加载渲染机制有关,优先渲染html代码,此时js代码还没有执行,所以动态属性还没有值,因此被v-if包裹的代码块没有被封装,此时执行js初始化代码的时候,调ref组件自然是undefined的。
大胆这么才想后,继续实验猜想,如果真像想的那样,那按照v-if和v-show的区别,v-show会在加载html的时候加载完整代码,只是不展示,那这样的话,执行js代码的时候,组件应该是存在的。所以尝试将v-if改成v-show,果然代码执行成功了。
另一起再回顾一下v-if和v-show的区别:
- 手段:v-if是通过控制dom节点的存在与否来控制元素的显隐;v-show是通过设置DOM元素的display样式,block为显示,none为隐藏;
- 编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换;
- 编译条件:v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译(编译被缓存?编译被缓存后,然后再切换的时候进行局部卸载); v-show是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素保留;
- 性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;
基于以上区别,因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。