Vue 开发笔记

# Vue 侦听器进阶:immediate 与 deep

官方文档:计算属性和侦听器 (opens new window)

# handler 方法和 immediate 属性

watch: {
  firstName: {
    handler(newName, oldName) {
      this.fullName = newName + ' ' + this.lastName;
    },
    immediate: true
  }
}
1
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
  }
}
1
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
  }
}
1
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;
1
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>
1
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>
1
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) {

    }
}
1
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 },
    },
  ],
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

这样可以从 props 中获得参数,而不需要监控 $router 对象。