闭包总是在不经意间出现。
1 问题发现
收到一个问题,路由切换之后再回到之前的页面,页面上有个组件每次会增加一个。
切换前
多次切换后
看了下代码,mounted的时候会添加这个组件。说明添加的逻辑就是触发问题的位置。
<script>
import TestComp from './components/TestComp.vue';
const showList = {
inList: []
}
const noShowList = {
inList: []
}
export default {
name: 'Page1',
components: {},
data() {
return {
list: this.$route.query.show ? noShowList : showList
}
},
computed: {},
mounted() {
this.list.inList = [TestComp, ...this.list.inList]
},
methods: {}
}
</script>
2 问题溯源
看修改的逻辑,是对原始的引用对象进行了修改。这里产生了一个想法:这里有闭包保存了状态,避开了Vue的新建-销毁逻辑?
由于触发是路由切换,所以直接杀到vue-router官网。果然,对于异步组件的处理是很明确的:Vue Router will only fetch it when entering the page for the first time, then use the cached version.
vue-router官网说明
所以这份存起来的模板对象上有个闭包,是程序员自己写出来的。
3 解决方案
知道了有闭包,解决起来就简单了:将这个对象放到data函数里。这样确保每次加载组件对象的时候,都生成新的对象。
<script>
import TestComp from './components/TestComp.vue';
export default {
name: 'Page1',
components: {},
data() {
const showList = {
inList: []
}
const noShowList = {
inList: []
}
return {
list: this.$route.query.show ? noShowList : showList
}
},
computed: {},
mounted() {
this.list.inList = [TestComp, ...this.list.inList]
},
methods: {}
}
</script>
4 拓展思考
对于Vue3而言,这种问题好像不会存在于setup中,毕竟作为函数上下文,每次调用都是最新的。不过如果还有在用optionalAPI的人就有难了。
5 总结
实战中莫名其妙的闭包问题往往是与框架机制挂钩的,所以脱离实际环境去谈论闭包很多时候得到的都是形而上学的认识。