计算和监视

概要

计算属性(Computed Properties)和监视器(Watchers)是Vue.js中用于响应式数据处理的两种重要机制,它们有一些区别和各自的使用场景和用法。

  • 计算属性适合用于对现有数据进行复杂的计算、转换或筛选操作,并将结果作为属性暴露给模板使用. 计算属性适合处理基于现有数据的计算,具有缓存机制,只有在依赖数据变化时才重新计算.
  • 监视器适用于处理特定响应式数据的变化,并进行更复杂和灵活的操作,例如发送网络请求、触发副作用等. 监视器适合监听特定数据的变化,并在数据变化时执行自定义操作,适用于处理复杂的业务逻辑和副作用

根据具体的需求和场景,选择使用计算属性或监视器来处理响应式数据,以提高代码的可读性和维护性.

计算属性

  • 定义方式:通过在Vue实例的computed选项中声明。
  • 特点:计算属性是基于其依赖的响应式数据进行计算,并缓存计算结果。只有当依赖的响应式数据发生变化时,才会重新计算,否则会直接返回缓存的结果。
  • 使用场景:适合用于根据现有的响应式数据进行复杂的计算、转换或筛选操作,并将结果作为属性暴露给模板使用。
  • 优点:提供了对响应式数据的自动依赖追踪和缓存机制,避免重复计算,提高性能。
  • 缺点:不适合用于处理异步操作或依赖多个异步操作结果的场景。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>{{ fullName }}</div>
</div>

<script>
// 创建Vue实例
new Vue({
el: '#app',
// 在Vue实例中定义计算属性
computed: {
fullName() {
return this.firstName + ' ' + this.lastName;
}
}
});
</script>
</body>
</html>

监视器

  • 定义方式:通过在Vue实例的watch选项中声明。
  • 特点:监视器会在一个或多个响应式数据发生变化时执行特定的回调函数,可以进行更复杂和灵活的操作,例如异步请求、数据处理等。
  • 使用场景:适合用于监听特定响应式数据的变化并执行相应的操作,例如发送网络请求、触发副作用等。
  • 优点:灵活性高,可以处理异步操作和多个响应式数据之间的关系。
  • 缺点:需要手动编写回调函数,并对数据的变化进行逻辑判断,增加了代码复杂性。
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
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>{{ fullName }}</div>
</div>

<script>
// 创建Vue实例
new Vue({
el: '#app',
methods: {
fetchUser(firstName) {
// 异步请求数据
// ...
}
},
// 在Vue实例中定义监视器
watch: {
firstName(newVal, oldVal) {
// 响应firstName的变化,执行相应的操作
this.fetchUser(newVal);
}
},
...
});
</script>
</body>
</html>

对于计算属性和监视器, 在chatgpt上有一个简单的总结描述:

  • 计算属性适合处理依赖关系较简单的计算,提供自动的依赖追踪和缓存机制。
  • 监视器适合处理复杂的业务逻辑,可以处理异步操作和多个响应式数据之间的关系。
  • 如果需要进行复杂的计算,优先选择计算属性;如果需要执行异步操作或处理多个响应式数据之间的关系,选择监视器。
  • 使用合适的机制可以提高代码的可读性

关于监视器替换计算属性的场景, 例如希望在firstname值变化2秒之后才改变fullname的值, 那么使用计算属性应该怎么做?

1
2
动作: 在fullname中设置一个定时器?
问题: 那么fullname返回什么值?

这里就引出了上面所描述的计算属性的使用场景, 其无法用于异步场景(比较少见), 而监视器则往往用于此类场景中.

监视特征

vue函数

这里临时插入一些箭头函数的基础知识, 箭头函数一般用于以下情况:

  1. 回调函数:当需要定义回调函数,并且希望函数内部的 this 绑定到定义时的上下文,而不是运行时的上下文时,可以使用箭头函数。

    1
    2
    const numbers = [1, 2, 3, 4, 5];
    const doubledNumbers = numbers.map((number) => number * 2);
  2. 简化函数表达式:当需要定义简短、简洁的函数表达式时,可以使用箭头函数来减少冗余的代码。

    1
    const greet = (name) => `Hello, ${name}!`;
  3. 保留父级上下文:当需要在函数内部访问父级上下文(如在嵌套函数中访问外部函数的 this)时,可以使用箭头函数来确保函数内部的 this 与外部函数的 this 保持一致。

    1
    2
    3
    4
    5
    6
    7
    8
    const obj = {
    name: 'John',
    greet: function() {
    setTimeout(() => {
    console.log(`Hello, ${this.name}!`); // 箭头函数保留了外部函数的上下文
    }, 1000);
    }
    };

需要注意的是,箭头函数没有自己的 this 绑定,它继承父级作用域的 this 值。因此,箭头函数不适合用作构造函数(不能使用 new 关键字调用)或定义对象方法(需要动态绑定 this)。另外,箭头函数也不适合在需要动态绑定 this 的回调函数中使用(如事件处理程序),因为它们无法动态改变 this 的指向。

好了, 现在让我们回到正题, 关于vue中的函数, 有如下两个小原则:

  • a. 所有被vue管理的函数, 最好写成普通函数, 此时this指向vm或者组件实例对象
  • b. 相反, 不被vue管理的函数, 如定时器回调, ajax回调, promise回调, 最好写成箭头函数, 这样才能确保this指向vm或组件实例对象.

深度监视

在 Vue 的 Watch 中,deep 是一个选项,用于指定是否深度观察(递归观察)被监听的对象或数组的变化。当 deep 设置为 true 时,Vue 将会递归遍历被监听的属性,并为每个嵌套属性添加监听器。这使得当被监听的对象或数组中的任何属性发生变化时,都能触发 Watch 的回调函数。

深度观察非常适用于监听对象或数组的变化,特别是当对象或数组中的属性是动态添加的或嵌套层级较深时。它可以确保当被监听对象中的属性发生变化时,能够及时地检测到,并执行相应的逻辑处理。

下面是一个示例,展示了如何使用 watchdeep 选项:

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
<template>
<div>
<h2>Deep Watch Example</h2>
<input v-model="user.name" type="text" placeholder="Enter your name" />
</div>
</template>

<script>
export default {
data() {
return {
user: {
name: 'John',
age: 25,
},
};
},
watch: {
user: {
handler(newValue, oldValue) {
console.log('User object changed:', newValue, oldValue);
},
deep: true, // 开启深度观察
},
},
};
</script>

在上述示例中,我们监听了 user 对象的变化,并设置 deep: true,这样当 user 对象中的 name 属性发生变化时,会触发 Watch 的回调函数。例如,当我们在输入框中修改用户名时,控制台将打印出相应的变化信息。

需要注意的是,由于深度观察需要递归遍历对象或数组,因此会带来一定的性能开销。因此,只有在必要时才应该使用深度观察,并且要谨慎使用,避免监听过深的嵌套层级,以免影响应用的性能。

生命周期

五大阶段

在 Vue.js 中,组件的生命周期由一系列的阶段组成,每个阶段都有相应的回调函数。以下是 Vue.js 的生命周期阶段及其对应的回调函数:

  1. 创建阶段(Creation Phase):

    • beforeCreate:在实例被创建之前调用,此时组件的数据观测和事件还未初始化。
    • created:在实例创建完成后调用,此时组件的数据观测已经完成,可以访问到 datamethods,但尚未挂载到 DOM 上。
  2. 挂载阶段(Mounting Phase):

    • beforeMount:在组件挂载之前调用,此时模板已经编译完成,但尚未替换成真实的 DOM。
    • mounted:在组件挂载到 DOM 后调用,此时组件已经被渲染到页面上,可以进行 DOM 操作。
  3. 更新阶段(Updating Phase):

    • beforeUpdate:在组件更新之前调用,发生在数据更新时,但尚未重新渲染视图。
    • updated:在组件更新完成后调用,此时 DOM 已经更新,可以进行 DOM 操作。
  4. 销毁阶段(Destruction Phase):

    • beforeDestroy:在组件销毁之前调用,此时组件仍然完全可用。
    • destroyed:在组件销毁后调用,此时组件已经被销毁,所有的事件监听器和子组件被移除。
  5. 激活与停用阶段(Activation and Deactivation Phase):

    • activated:在组件被激活时调用,通常在使用 Vue Router 进行路由切换时触发。
    • deactivated:在组件被停用时调用,通常在使用 Vue Router 进行路由切换时触发。

这些生命周期阶段和回调函数提供了一些钩子函数,使开发者可以在组件不同的生命周期中执行相应的操作。例如,在 created 钩子函数中可以进行初始化数据的操作,而在 mounted 钩子函数中可以进行 DOM 操作或请求数据的操作。

下面是生命周期各个阶段的图例:

vue-life-cycle

vue3

在 Vue 3 中,一些生命周期钩子函数的名称发生了变化,以更好地与 Composition API 和语义一致。以下是 Vue 3 中生命周期钩子函数的改变:

  1. 创建阶段(Creation Phase):

    • beforeCreate -> setup:在组件实例创建之前执行的函数,用于设置组件的状态、响应式数据等。它是 Composition API 的一部分,取代了 Vue 2 中的 beforeCreate 钩子函数。
  2. 挂载阶段(Mounting Phase):

    • beforeMount -> onBeforeMount:在组件挂载到 DOM 之前执行的函数。
    • mounted -> onMounted:在组件挂载到 DOM 之后执行的函数。
  3. 更新阶段(Updating Phase):

    • beforeUpdate -> onBeforeUpdate:在组件更新之前执行的函数。
    • updated -> onUpdated:在组件更新之后执行的函数。
  4. 销毁阶段(Destruction Phase):

    • beforeDestroy -> onBeforeUnmount:在组件销毁之前执行的函数。
    • destroyed -> onUnmounted:在组件销毁之后执行的函数。
  5. 激活与停用阶段(Activation and Deactivation Phase):

    • activated -> onActivated:在组件被激活时执行的函数。
    • deactivated -> onDeactivated:在组件被停用时执行的函数。

这些生命周期钩子函数的改变是为了更好地与 Composition API 配合使用,并提供更清晰、一致的命名。在 Vue 3 中,推荐使用 Composition API 来组织和管理组件的逻辑。除了上述的钩子函数变化,Vue 3 还引入了许多新的 API 和特性,例如 onRenderTrackedonRenderTriggered 等,用于更细粒度地追踪组件的渲染过程。

请注意,如果使用了 Vue 3 的 Options API,仍然可以使用 Vue 2 中的生命周期钩子函数,但建议在新的项目中使用 Composition API。

vue版本文件

差异

Vue.js和Vue Runtime(例如vue.runtime.esm.js)是Vue框架的两个不同版本。

  1. Vue.js:
    • Vue.js是完整版的Vue框架,包含编译器和运行时的构建。
    • 它支持模板编译和运行时渲染。
    • 在开发环境中,它包括警告和调试信息,体积较大。
    • Vue.js适用于大多数常规的简单开发场景,特别是在使用单文件组件(SFC)和模板语法进行开发时。

其示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<title>Vue.js Example</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{ message }}</h1>
</div>

<script>
new Vue({
el: '#app',
data: {
message: 'Hello, Vue.js!'
}
});
</script>
</body>
</html>
  1. Vue Runtime(例如vue.runtime.esm.js):
    • Vue Runtime是轻量级版本的Vue框架,只包含运行时构建。
    • 它不包含警告和调试信息,体积较小。

其示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<title>Vue Runtime + Compiler Example</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.runtime.esm.js"></script>
</head>
<body>
<div id="app">
<h1>{{ message }}</h1>
</div>

<script>
const app = Vue.createApp({
data() {
return {
message: 'Hello, Vue Runtime + Compiler!'
};
}
});

app.mount('#app');
</script>
</body>
</html>

总结:

  • 如果你想使用Vue的完整特性,并在HTML中使用模板语法进行开发,使用Vue.js版本是合适的选择。
  • 如果你在自定义构建过程中需要更细粒度的控制,或者使用了自定义的构建工具,使用Vue Runtime版本是合适的选择。

vue版本

Vue.js 是一个完整的 Vue 框架,它包含了 Vue 的核心库、模板编译器以及其它一些常用的功能和工具。Vue.js 的文件一般包括以下几个:

  1. vue.js:完整版的 Vue.js,包含了核心库、模板编译器和运行时构建。示例:

    1
    <script src="vue.js"></script>
  2. vue.min.js:压缩版本的 Vue.js,与 vue.js 功能相同,只是文件体积更小。示例:

    1
    <script src="vue.min.js"></script>

Vue Runtime 是 Vue.js 的运行时构建,它不包含模板编译器,因此无法在运行时编译模板。Vue Runtime 的文件一般以 runtime 命名,常见的文件包括:

  1. vue.runtime.js:Vue Runtime 的完整版。示例:

    1
    <script src="vue.runtime.js"></script>
  2. vue.runtime.min.js:Vue Runtime 的压缩版本。示例:

    1
    <script src="vue.runtime.min.js"></script>
  3. vue.runtime.esm.js:使用 ES 模块语法导出的 Vue Runtime。示例:

    1
    <script type="module" src="vue.runtime.esm.js"></script>

需要根据项目需求和构建工具的要求选择合适的 Vue.js 或 Vue Runtime 文件。如果需要在运行时编译模板,应该选择包含模板编译器的完整版 Vue.js;如果在构建时预编译模板,或手动渲染模板,或与构建工具配合使用,可以选择 Vue Runtime 版本。

请注意,上述文件示例仅供参考,具体的文件名和路径可能因版本和项目配置而有所不同。请根据自己的实际情况来选择和引入相应的文件。

不同环境

vue为了应对不同的环境推出了不同的vue版本, 我们以vue.js为例, 列举不同环境下vue.js的变种:

  1. vue.js: 这是 Vue.js 的完整版,包含了模板编译器和运行时构建。它适用于在浏览器环境中直接使用,可以通过 <script> 标签引入。可以在开发阶段使用,也可以用于构建独立的 Vue.js 应用。

  2. vue.common.js: 这是一个用于构建工具的 CommonJS 版本,不包含模板编译器。它可以在构建时与构建工具(如 webpack)配合使用,以实现更高效的构建和打包。它适用于构建 Vue.js 组件、库或服务端渲染应用。

  3. vue.esm.js: 这是一个使用 ES 模块语法的完整版,包含了模板编译器和运行时构建。它适用于现代构建工具和 ES 模块环境,可以与诸如 webpack、Rollup 等工具一起使用。它提供了更强大的静态分析和优化能力,并且支持 Tree-Shaking(摇树优化)。

  4. vue.esm.browser.js: 这是一个针对浏览器的 ES 模块版本,包含了模板编译器和运行时构建。它适用于浏览器环境中使用 ES 模块语法的项目,可以直接在浏览器中通过 <script type="module"> 标签引入。

总结使用场景:

  • 如果在浏览器环境中直接使用 Vue.js,可以选择引入 vue.js 文件。
  • 如果需要在构建工具中使用 CommonJS 模块规范,可以选择引入 vue.common.js 文件。
  • 如果使用现代构建工具和 ES 模块环境,可以选择引入 vue.esm.js 文件。
  • 如果在浏览器环境中使用 ES 模块语法,可以选择引入 vue.esm.browser.js 文件。

需要根据具体的项目需求和使用环境选择合适的 Vue.js 文件。如果使用构建工具,建议根据构建工具的要求和配置选择对应的版本,以获得更好的构建性能和优化效果。