# Vue 侦听器进阶:immediate 与 deep
官方文档:计算属性和侦听器 (opens new window)
# handler 方法和 immediate 属性
watch: {
firstName: {
handler(newName, oldName) {
this.fullName = newName + ' ' + this.lastName;
},
immediate: true
}
}
2
3
4
5
6
7
8
immediate:true
代表如果在 wacth 里声明了 firstName 之后,就会立即先去执行里面的 handler 方法,如果为 false 就跟我们以前的效果一样,不会在绑定的时候就执行。
# deep 属性
watch: {
obj: {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
deep: true
}
}
2
3
4
5
6
7
8
9
deep 的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了,任何修改 obj 里面任何一个属性都会触发这个监听器里的 handler。
优化,我们可以是使用字符串形式监听。
watch: {
'obj.a': {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
// deep: true
}
}
2
3
4
5
6
7
8
9
这样 Vue.js 才会一层一层解析下去,直到遇到属性 a,然后才给 a 设置监听函数。
# Vue 错误捕获并在 overlay 中展示
在无法使用控制台的场景下,为了方便调试,简单做了一个错误遮罩层,只用了 ES6 原生 JS。
const DebugPlugin = {
install: function(Vue, options) {
Vue.prototype.$overlay = function(errorMsg) {
if (!document.getElementById("vue-overlay")) {
const overlay = document.createElement("div");
overlay.setAttribute("id", "vue-overlay");
overlay.style.position = "fixed";
overlay.style.left = "0";
overlay.style.right = "0";
overlay.style.top = "0";
overlay.style.bottom = "0";
overlay.style.backgroundColor = "rgba(0,0,0,0.5)";
overlay.style.color = "#fff";
overlay.style.zIndex = "9999";
overlay.innerHTML = errorMsg;
document.body.appendChild(overlay);
} else {
const overlay = document.getElementById("vue-overlay");
overlay.innerHTML += errorMsg;
}
};
Vue.config.errorHandler = function(err, vm, info) {
Vue.prototype.$overlay(`<div style="color:red;padding:10px;">${err}</div>`);
Vue.prototype.$overlay(`<div style="color:red;padding:10px;">${info}</div>`);
};
Vue.config.warnHandler = function(msg, vm, trace) {
Vue.prototype.$overlay(`<div style="color:yellow;padding:10px;">${msg}</div>`);
Vue.prototype.$overlay(`<div style="color:yellow;padding:10px;">${trace}</div>`);
};
},
};
export default DebugPlugin;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Vue scoped CSS 与深度作用选择器 /deep/
使用 scoped 后,父组件的样式将不会渗透到子组件中。
例如(无效):
<template>
<div id="app">
<el-input class="text-box" v-model="text"></el-input>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
text: "hello",
};
},
};
</script>
<style lang="less" scoped>
.text-box {
input {
width: 166px;
text-align: center;
}
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
解决方法:
使用深度作用选择器 /deep/
<template>
<div id="app">
<el-input v-model="text" class="text-box"></el-input>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
text: "hello",
};
},
};
</script>
<style lang="less" scoped>
.text-box {
/deep/ input {
width: 166px;
text-align: center;
}
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
官方文档:https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors (opens new window)
# Vue 获取路由参数变化
当组件被设置为常驻内存(keep-alive),会极大提高组件切换的性能,每次加载都不需要重新执行一遍生命周期。但是问题也来了,如何保证数据的重新渲染呢?对于路由传参,似乎也被忽视了。
经测试,Vue 普通组件也存在这个问题,这是 Vue 的 diff 机制导致的,为了性能最优化,不会执行多余的渲染操作。
# 暴力方式解决
目前有个比较暴力的解决方式,使用 Vue 自带的 watch 机制,监控 $router 对象。
watch: {
$route (to, from) {
}
}
2
3
4
5
# 更优的实现
在最新版本的 vue-router 中,官方推荐使用 props 将组件和路由解耦,具体方式如下:
const User = {
props: ["id"],
template: "<div>User {{ id }}</div>",
};
const router = new VueRouter({
routes: [
{ path: "/user/:id", component: User, props: true },
// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
{
path: "/user/:id",
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false },
},
],
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
这样可以从 props 中获得参数,而不需要监控 $router 对象。