Vue中避免数据被"依赖收集"的几种方式😗😗😗

写在开头

在业务开发中,我们经常会遇到一些不需要进行依赖收集的场景,例如:接口返回的庞大数据量、第三方库的实例对象、不需要响应化的常量等等。

下面我们来盘点一下在 Vue 中有哪些方式能避免数据被依赖收集

在data外部定义

我们知道定义在 data 中的数据就会被 Vue 进行依赖收集,使其具有响应化的特性,那么只要我们将数据定义在 data 外部自然就不会被依赖收集了。例如:

javascript 复制代码
<script>
const name = '橙某人';

export default {
  data() {
    return {};
  }
}
</script>

当然,这是最简单的方式,但同时也会容易引起一些不必要的问题,如同一个页面内多次复用同一个组件会造成数据相互干扰。

Child.vue 文件:

javascript 复制代码
<script>
const name = '橙某人';

export default {
  data() {
    return {};
  },
  methods: {
    updateName(value) {
      name = value;
    },
    showName() {
      console.log(name);
    }
  }
}
</script>

App.vue 文件:

javascript 复制代码
<template>
  <div>
    <child1 ref="child1" />
    <child2 ref="child2" />
    <button @click="handleClick1">点击1</button>
    <button @click="handleClick2">点击2</button>
  </div>
</template>

<script>
import Child from "./components/Child.vue";

export default {
  components: { Child },
  data() {
    return {};
  },
  methods: {
    handleClick1() {
      this.$refs.child1.updateName("橙某人-update");
    },
    handleClick2() {
      this.$refs.child2.showName(); // 橙某人-update
    },
  },
};
</script>

这也就是为什么 Vue 组件中的 data 数据必须是一个函数的原因,能起到一个互相隔绝的作用。😲

对象新属性

第二种避免数据被依赖收集是利用 Vue 不能检测对象属性的添加或删除的特性。

例如:

javascript 复制代码
<template>
  <div>
    {{ person.name }}
    <button @click="handleClick">点击</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      person: {},
    };
  },
  methods: {
    handleClick() {
      this.person.name = "橙某人";
      console.log(this);
    },
  },
};
</script>

当我们触发修改后,页面数据并不会实时响应,因为它并没有被依赖收集。通过这种方式,有时我们可以直接把第三方库的实例对象挂载在 this 身上,方便在其他方法中继续对实例对象的使用。

不止是对象,数组我们同样可以利用 Vue 不能检测通过下标添加数组项的特性。😗

_ 和 $

而第三种方式是使用特定符号,如以 $ 或者 _ 开头定义数据。因为 Vue2 中的属性和 API 方法都是以这些符号开头的,为了防止名称上的冲突造成异常,在 data 中定义的数据,Vue 默认是不对以这些符号开头的数据进行依赖收集的,并且如果在 template 中使用控制台将会抛出警告提示。

javascript 复制代码
<script>
export default {
  data() {
    return {
      $name: null,
    };
  },
  methods: {
    handleClick() {
      this.$name = '橙某人-update';
      console.log(this);
    },
  },
};
</script>

这种方式如果不看初始定义,本质和"对象新属性"的方式是一致的。

而如果你的初始定义不是空值,有默认值,那么初始值的访问可以通过 this.$data.xxx 或者 this._data.xxx

源码中的位置:vue/src/core/instance/state.ts 文件中的 initData 方法。

__ob__ 和 __v_skip

而接下来两种方式来自 Vue 源码中的判断,它们比较适合对象形式的数据。

源码中的位置:vue/src/core/observer/index.ts 文件中的 observe 方法。

其一,将需要避免被依赖收集的数据对象,添加一个 __ob__ 的属性,并设置它的值为一个 Observer 实例。

由于 Observer 对象并不是 Vue 的公开 API,所以通常在业务中是不能直接访问的,为此小编也不推荐使用这种方式。但一定要玩玩看,也不是没有办法😗,例如:

Child.vue 文件:

javascript 复制代码
<template>
  <button @click="show">查看</button>
</template>

<script>
export default {
  props: ["Observer"],
  data() {
    return {
      person: {
        age: 18,
        __ob__: this.Observer,
      },
    };
  },
  methods: {
    show() {
      console.log(this.person);
    },
  }
}
</script>

App.vue 文件:

javascript 复制代码
<template>
  <div>
    <child :Observer="person.__ob__" />
    <button @click="show">展示</button>
  </div>
</template>

<script>
import Child from "./components/Child.vue";

export default {
  components: { Child },
  data() {
    return {
      person: {
        name: "橙某人",
      },
    };
  },
  methods: {
    show() {
      console.log(this.person);
    },
  },
};
</script>

你会发现子组件的 age 属性并没有被依赖收集,我们是通过父组件的响应函数据得到 Observer 对象,并传给了子组件使用,阻止了数据的响应化。

其二,将需要避免被依赖收集的数据对象,添加一个 __v_skip 的属性,并设置它的值为 true

这种方式就很简单了,推荐使用。

javascript 复制代码
<script>
export default {
  data() {
    return {
      person: {
        name: "橙某人",
        __v_skip: true,
      },
    };
  },
};
</script>

Object.freeze

Vue 文档 中介绍数据绑定和响应时,特意标注了对于经过 Object.freeze() 方法的对象无法进行更新响应。

Object.freeze 是ES5新增的方法,可以冻结一个对象,防止对象被修改。

javascript 复制代码
<template>
  <button @click="handleClick">点击</button>
</template>

<script>
export default {
  data() {
    return {
      person: null,
    };
  },
  methods: {
    handleClick() {
      this.person = Object.freeze({ name: "橙某人" });
      console.log(this.person);
    },
  },
};
</script>

这也是一个不错的方式,但是可惜就是被冻结的对象是不能被修改了。😤

class的私有属性(#)

最后一种方式,我们可以利用 class 的私有属性来完成。

javascript 复制代码
<template>
  <button @click="handleClick">点击</button>
</template>

<script>
export class PersonProxy {
  #data;
  constructor(data) {
    this.#data = data;
  }
  get data() {
    return this.#data;
  }
}

export default {
  data() {
    return {
      person: null,
    };
  },
  methods: {
    handleClick() {
      this.person = new PersonProxy({ name: "橙某人" });
      console.log(this.person);
      console.log(this.person.data);
    },
  },
};
</script>

这种方式对于避免数据被依赖收集,而且还需要修改其中的数据是非常有效的,也比较推荐使用这种方式。


至此,本篇文章就写完啦,撒花撒花。

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。

老样子,点赞+评论=你会了,收藏=你精通了。

相关推荐
轻口味1 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王2 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发2 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀2 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪3 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef4 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6415 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻5 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云5 小时前
npm淘宝镜像
前端·npm·node.js