watch侦听器
Vue.js 中的侦听器(Watcher)是 Vue提供的一种响应式系统的核心机制之一。
监听数据的变化,并在数据发生变化时执行相应的回调函数。
目的:数据变化能够自动更新到视图中
原理:
Vue 的侦听器通过观察对象的属性,并在属性值发生变化时触发回调函数来实现。在 Vue 内部,侦听器通过使用 JavaScript 对象的 Object.defineProperty
方法来实现对数据的监听,从而在数据发生变化时能够及时地通知相关的观察者(Watcher),进而更新视图。
功能:
- 监听数据变化: 侦听器能够监听数据的变化,包括数据的添加、修改、删除等操作。
- 执行回调函数: 当数据发生变化时,侦听器会执行事先注册的回调函数,从而触发相应的操作。
用法:
在 Vue 组件中,你可以使用侦听器来监视数据的变化,并执行相应的操作。你可以在 watch
选项中定义侦听器,也可以直接使用 $watch
方法来创建侦听器。
通过 watch
选项:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
};
},
watch: {
count(newValue, oldValue) {
console.log('Count changed from', oldValue, 'to', newValue);
}
},
methods: {
increment() {
this.count++;
}
}
};
</script>
通过 $watch
方法:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
};
},
mounted() {
this.$watch('count', (newValue, oldValue) => {
console.log('Count changed from', oldValue, 'to', newValue);
});
},
methods: {
increment() {
this.count++;
}
}
};
</script>
优缺点:
优点:
- 响应式更新: 侦听器能够实现数据的响应式更新,当数据发生变化时,相关的视图会自动更新。
- 灵活性: 侦听器的使用非常灵活,你可以在任何地方监听任何数据的变化,并执行相应的操作。
缺点:
- 性能消耗: 对大量数据进行监听可能会导致性能下降,因为每个被监听的数据都会创建一个侦听器,消耗一定的内存和 CPU 资源。
- 复杂性: 当应用变得复杂时,过多的侦听器可能会导致代码难以维护和理解。
以下是一个简化的流程图来展示 Vue.js 中侦听器的底层原理:
+---------------------+
| 数据对象 |
| (Data Object) |
+----------+----------+
|
v
+----------+----------+
| Observer 监听器 |
| (Observer) |
+----------+----------+
|
v
+----------+----------+
| Watcher 侦听器 |
| (Watcher) |
+----------+----------+
|
v
+----------+----------+
| 更新视图 |
| (Update View) |
+---------------------+
在这个流程图中,有以下几个关键组件:
-
数据对象(Data Object): 这是 Vue.js 中的数据对象,可以是一个 Vue 实例的数据对象,也可以是 Vue 组件中的数据对象。数据对象中的数据发生变化时,需要通知到相关的侦听器。
-
Observer 监听器(Observer): Observer 监听器负责监听数据对象中数据的变化。它通过递归遍历数据对象中的所有属性,并对每个属性使用
Object.defineProperty
方法添加 getter 和 setter,从而实现对数据的监控。 -
Watcher 侦听器(Watcher): Watcher 侦听器是 Vue.js 中的侦听器组件,负责订阅数据对象中的某个属性,并在该属性的值发生变化时执行回调函数。每个 Watcher 侦听器都与一个表达式相关联,当表达式的值发生变化时,Watcher 侦听器会得到通知,并执行相应的回调函数。
-
更新视图(Update View): 更新视图是指当数据对象的某个属性发生变化时,需要更新对应的视图内容。这通常包括重新渲染组件或更新页面中的某个 DOM 元素。
具体流程如下:
-
当数据对象中的某个属性发生变化时,Observer 监听器会检测到变化,并通知相关的 Watcher 侦听器。
-
Watcher 侦听器接收到变化通知后,会执行事先注册的回调函数。这个回调函数可以是更新视图的操作,也可以是其他需要在数据变化时执行的操作。
-
更新视图操作会根据具体情况执行,比如重新渲染组件或更新页面中的某个 DOM 元素,以反映数据变化后的最新状态。
数据变化能够自动更新到视图中
在 Vue 3 中,watch
函数用于监听响应式数据的变化,并在数据发生变化时执行回调函数。下面详细说明 watch
函数的原理、参数及其含义,以及返回结果:
原理
watch
函数的原理是通过创建一个响应式的 watcher(观察者),来监听数据的变化。当被监听的数据发生变化时,watcher 将会执行注册的回调函数。
参数及其含义
watch
函数接受三个参数:
-
要监听的数据(可选): 这是一个可以是响应式数据对象、ref 对象、reactive 对象或计算属性等。当该数据发生变化时,触发监听器执行回调函数。
-
回调函数(必需): 这是当被监听的数据发生变化时需要执行的函数。回调函数接受两个参数:新值(
newValue
)和旧值(oldValue
),分别表示数据变化后的值和变化前的值。 -
选项对象(可选): 这是一个可选的选项对象,用于配置监听器的行为。常见的选项包括:
deep
:是否深度监听对象内部值的变化,默认为false
。immediate
:是否在初始化时立即执行回调函数,默认为false
。flush
:指定 watcher 的触发时机,默认为'pre'
,可选的值有'pre'
(在 DOM 更新之前触发)和'post'
(在 DOM 更新之后触发)。- 等等其他选项,可以根据具体需求进行配置。
返回结果
watch
函数的返回结果是一个取消监听的函数,可以通过调用该函数来停止对数据的监听。如果要停止监听多个数据,可以将返回的函数保存在变量中,然后在需要取消监听时调用它。
示例
下面是一个示例,演示了如何使用 watch
函数:
import { ref, watch } from 'vue';
const count = ref(0);
// 监听 count 的变化
const stopWatch = watch(count, (newValue, oldValue) => {
console.log('Count changed from', oldValue, 'to', newValue);
}, {
immediate: true,
deep: true
});
// 修改 count 的值
count.value = 1; // 输出: "Count changed from 0 to 1"
// 停止监听 count 的变化
stopWatch();
在这个示例中,我们使用 watch
函数监听了 count
的变化,当 count
的值发生变化时,会执行回调函数并打印变化前后的值。同时,我们在选项中设置了 immediate: true
,表示在初始化时立即执行回调函数。最后,我们调用返回的函数 stopWatch
来停止对 count
的监听。
reactive, ref响应式
reactive
函数是 Vue 3 中用于创建响应式对象的函数之一。它的作用是将普通的 JavaScript
对象转换为响应式对象,使得对象的属性可以被 Vue 追踪,并在属性发生变化时触发相应的更新。
原理
reactive
函数的原理是通过Proxy 对象来实现的。它会包装传入的普通对象,创建一个代理对象,这个代理对象拦截了对原对象属性的访问、赋值、删除等操作,在这些操作发生时,会通知相关的依赖进行更新。
参数
reactive
函数接受一个普通的 JavaScript 对象作为参数,将其转换为响应式对象。
返回值
reactive
函数的返回值是一个代理对象,这个代理对象拦截了对原对象属性的访问、赋值、删除等操作。
示例
下面是一个使用 reactive
函数创建响应式对象的示例:
import { reactive } from 'vue';
const obj = {
count: 0
};
// 使用 reactive 函数将普通对象转换为响应式对象
const reactiveObj = reactive(obj);
// 修改响应式对象的属性
reactiveObj.count++; // 响应式地更新了 count 属性
console.log(reactiveObj.count); // 输出: 1
在这个示例中,首先定义了一个普通的 JavaScript 对象 obj
,然后使用 reactive
函数将其转换为响应式对象 reactiveObj
。接着,我们通过修改 reactiveObj
的属性 count
,并在控制台输出结果,可以看到 count
的值被成功更新为 1,这说明 reactive
函数成功地创建了一个响应式对象,并实现了对属性的监听和更新。
ref
函数是 Vue 3 中用于创建响应式引用的函数之一。它的作用是将普通的 JavaScript 值转换为响应式对象,使得这个值可以被 Vue 追踪,并在值发生变化时触发相应的更新。下面是关于 ref
函数的详细说明:
原理
ref
函数的原理是通过一个封装了传入值的响应式对象来实现的。它返回的对象具有一个value
属性,这个value
属性存储着传入的值,并且这个对象上的value
属性是响应式的,当这个属性的值发生变化时,相关的依赖会被触发更新。
参数
ref
函数接受一个普通的 JavaScript 值作为参数,将其转换为响应式引用。
返回值
ref
函数的返回值是一个包含 value
属性的对象,这个 value
属性存储着传入的值,并且是响应式的。
示例
下面是一个使用 ref
函数创建响应式引用的示例:
import { ref } from 'vue';
// 使用 ref 函数创建响应式引用
const count = ref(0);
// 修改响应式引用的值
count.value++; // 响应式地更新了值
console.log(count.value); // 输出: 1
在这个示例中,我们使用 ref
函数创建了一个响应式引用 count
,并初始化其值为 0。然后,我们通过修改 count.value
的值,触发了响应式更新,并在控制台输出结果,可以看到 count.value
的值被成功更新为 1,这说明 ref
函数成功地创建了一个响应式引用,并实现了对值的监听和更新。
对比
reactive
和 ref
是 Vue 3 中常用的两种创建响应式数据的方式,它们各自有着不同的优缺点,适用于不同的场景:
reactive
的优缺点:
优点:
- 适用于复杂对象:
reactive
可以将整个对象转换为响应式对象,适用于对复杂对象进行监听和更新。 - 自动深度响应式:
reactive
会递归地将对象的所有属性转换为响应式,因此在嵌套对象中也能实现深度响应式。 - 直观性: 在处理对象时,
reactive
更符合直觉,因为可以直接操作对象的属性。
缺点:
- 性能开销较大: 对于大型对象或嵌套层级较深的对象,
reactive
的性能开销较大,因为它需要递归地追踪所有属性的变化。 - 不适合基本类型:
reactive
更适用于对象类型,对于基本类型的数据并不是最佳选择。
ref
的优缺点:
优点:
- 性能优化:
ref
只会追踪基本类型数据或简单对象的变化,因此性能开销相对较小。 - 直接访问值: 使用
ref
创建的对象,直接访问值更加方便,不需要通过.value
来获取值。 - 更灵活: 可以在需要时将普通的 JavaScript 值转换为响应式值,非常灵活。
缺点:
- 不支持深度监听:
ref
不支持深度监听,只能追踪其value
属性的变化,对于嵌套对象的监听需要额外处理。 - 不适用于复杂对象: 对于复杂的对象结构,
ref
并不直观,也不方便进行监听和更新。
总结:
- 如果需要监听复杂对象或嵌套对象的变化,并且不在意性能开销,可以选择使用
reactive
。 - 如果需要更轻量级的响应式数据,并且关注性能优化,可以选择使用
ref
。 - 在实际开发中,根据具体的需求和场景来选择合适的响应式方式,有时也会同时使用
reactive
和ref
来达到最佳效果。
计算属性computed
计算属性声明成一个方法, 实际是这个方法的返回值
computed
函数是 Vue.js
中一个非常重要的概念,它用于声明计算属性。计算属性是基于响应式依赖进行缓存的,只有在依赖发生变化时才会重新计算。
原理:
-
依赖追踪: 当组件渲染时,Vue 会执行
computed
函数,并在执行过程中访问所依赖的响应式属性或其他计算属性。Vue 会在内部追踪这些依赖关系。 -
缓存计算结果: Vue 会将计算属性的结果缓存起来,只有当其依赖发生变化时才会重新计算。这样可以确保在多次访问同一个计算属性时,只进行一次计算,提高性能。
-
响应式更新: 如果计算属性依赖的响应式属性发生变化,Vue 会自动重新计算计算属性的值,并触发相应的更新操作,确保视图与数据保持同步。
参数及含义:
computed
函数接收一个对象作为参数,对象的每个键值对都表示一个计算属性的定义,其中:
- 键(key): 计算属性的名称。
- 值(value): 计算属性的定义,可以是一个函数或包含
get
和set
方法的对象。
如果值是一个函数,则该函数会被当作计算属性的 getter 方法。如果需要设置计算属性的值,可以通过定义 set
方法来实现。
返回结果:
计算属性的返回结果是根据其定义的 getter 函数或 get
方法计算得出的值。这个值会被缓存起来,并在其依赖发生变化时更新。计算属性的返回结果可以直接在模板中使用,就像普通的属性一样。
总的来说,computed
函数的原理是基于响应式依赖进行缓存的,参数包括计算属性的名称和定义,返回结果是根据定义计算得出的值。通过计算属性,我们可以方便地定义复杂的逻辑,并确保在需要时进行自动更新。
假设我们有一个 Vue 组件,其中包含一个计算属性来计算商品的总价,计算属性依赖于商品的价格和数量。下面是一个基本的示例代码:
<template>
<div>
<h2>商品信息</h2>
<p>商品名称:{{ productName }}</p>
<p>单价:{{ price }}</p>
<p>数量:<input type="number" v-model="quantity"></p>
<p>总价:{{ totalPrice }}</p>
</div>
</template>
<script>
export default {
data() {
return {
productName: '手机',
price: 1000, // 单价
quantity: 1, // 数量
};
},
computed: {
// 计算商品总价
totalPrice() {
return this.price * this.quantity;
},
},
};
</script>
在上面的示例中,我们定义了一个 totalPrice
计算属性,它通过 price
和 quantity
计算出商品的总价。当 price
或 quantity
发生变化时,totalPrice
会自动重新计算,并更新到视图中。
这个示例展示了 computed
函数的使用方式:定义一个计算属性,然后通过 getter 方法来计算属性的值。在模板中,我们可以直接使用计算属性的名称来获取其值,而不需要手动调用计算方法。
这样,Vue 会在内部自动追踪依赖关系,并确保在依赖发生变化时,及时更新计算属性的值。这种方式能够简化代码,并提高性能。
Vite
简单的类比,Vite相当于Maven或者Vue Cli可以快速的生成一个项目脚手架,并引入部分基础依赖
Vite(法语意为 “快速的”,发音 /vit/,发音同
“veet”)是一种新型前端构建工具,能够显著提升前端开发体验。它主要由两部分组成:一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
Vite 是一个基于 Rollup 和浏览器原生 ES 模块支持的快速开发服务器,用于构建现代 Web 应用。它提供了一组命令行工具来帮助你初始化、开发、构建和测试你的项目。以下是 Vite 的常用命令及其参数、含义和作用:
1. vite init
-
参数:
<app-name>
:要创建的项目名称。--template <template-name>
:指定项目模板,默认为vue
,可选的模板有vue
、vue-ts
、react
、react-ts
、preact
、lit-element
、svelte
、vanilla
。--force
:强制在非空目录中创建项目。
-
含义: 初始化一个新的 Vite 项目。
2. vite dev
-
参数:
- 无
-
含义: 启动开发服务器,用于开发模式下的实时预览和热模块替换。
3. vite build
-
参数:
--base <path>
:指定构建输出的基础路径,默认为/
。--outDir <dir>
:指定构建输出的目录,默认为dist
。--assetsDir <dir>
:指定构建输出的静态资源目录,默认为_assets
。--assetsInlineLimit <num>
:指定是否将小于指定大小的静态资源内联,默认为4096
字节。--sourcemap
:是否生成 source map,默认为false
。
-
含义: 打包生产环境的项目代码,生成优化过的静态资源文件。
4. vite serve
-
参数:
- 无
-
含义: 启动生产服务器,用于在本地预览生产环境构建的应用。
5. vite preview
-
参数:
- 无
-
含义: 在生产环境下预览应用。
6. vite inspect
-
参数:
- 无
-
含义: 查看 Vite 配置的内部信息。
7. vite optimize
-
参数:
- 无
-
含义: 优化项目的构建输出。
这些是 Vite 常用的命令及其参数、含义和作用。通过这些命令,你可以方便地进行项目初始化、开发、构建和测试。
Vite项目
Vite 项目通常具有以下目录结构:
my-vite-project/
├── node_modules/ # 依赖的 Node.js 模块
├── public/ # 公共静态资源
│ ├── favicon.ico # 网站图标
├── src/ # 项目源代码
│ ├── assets/ # 静态资源文件(图片、字体等)
│ ├── components/ # 组件文件
│ ├── App.vue # 根组件
│ └── main.js # 项目入口文件
|—— index.html # 入口html文件
├── .gitignore # Git 忽略文件配置
├── package.json # 项目配置文件
├── vite.config.js # Vite 配置文件
└── README.md # 项目说明文档
下面是各部分的作用:
-
node_modules/
:存放项目依赖的 Node.js 模块,通常使用 npm 或者 Yarn 等包管理工具进行安装。 -
public/
:存放公共静态资源文件,如网站图标(favicon.ico
)。这里的内容会被直接复制到最终构建的输出目录中。 -
src/
:存放项目的源代码文件。assets/
:存放静态资源文件,如图片、字体等。components/
:存放 Vue 组件文件。App.vue
:根组件,是整个 Vue 应用的入口组件。main.js
:项目的入口文件,用于初始化 Vue 应用,并挂载根组件到 DOM 中。
-
.gitignore
:Git 忽略文件配置,指定哪些文件或目录不应纳入版本控制。 -
package.json
:项目配置文件,包含项目的元数据(如名称、版本号、作者等)以及项目依赖的 Node.js 模块列表。 -
vite.config.js
:Vite 的配置文件,用于配置构建、开发服务器等选项。 -
README.md
:项目说明文档,通常包含项目的简要介绍、使用方法、贡献指南等信息。
Vite 项目是如何运行起来的呢?
-
当执行
vite dev
命令时,Vite 会读取项目中的vite.config.js
文件,并根据配置启动开发服务器。 -
开发服务器会监听文件变化,当文件发生变化时,会重新构建相关的模块,并通过浏览器的热模块替换功能实时更新页面。
-
当执行
vite build
命令时,Vite 会读取项目中的vite.config.js
文件,并根据配置进行项目的打包构建。 -
构建完成后,Vite 会生成优化过的静态资源文件,并将其输出到指定的输出目录中,供部署到生产环境使用。
通过以上步骤,Vite 项目就可以成功运行起来,并且可以在开发和生产环境中高效地构建和运行。
运行流程
npm run dev之后,vite会依据main.js中的逻辑,执行一系列初始化操作,最终会将各种组件渲染到index.html页面上
自定义组件
在企业级项目中,不再以视图作为单元,而是组件
需要将操作,数据和视图都封装成一个组件,可以在项目的其他地方调用
全局注册组件
组件需要先注册才能够使用
通过全家注册,就能在任意位置使用组件了
要在 Vue.js 中全局注册组件,你可以在项目的入口文件(通常是main.js
)或者在一个单独的文件中注册组件,然后在入口文件中引入这个文件。
以下是在入口文件中全局注册组件的示例:
// main.js
import Vue from 'vue';
import App from './App.vue';
import MyComponent from './components/MyComponent.vue'; // 引入要注册的组件
Vue.component('my-component', MyComponent); // 全局注册组件,可以使用 <my-component> 标签
new Vue({
render: h => h(App),
}).$mount('#app');
在上面的例子中,我们先引入了要注册的组件 MyComponent.vue
,然后使用 Vue.component
方法将其全局注册为 my-component
组件。这样在整个应用中就可以直接使用 <my-component>
标签来引用这个组件了。
另外,如果你希望在单独的文件中注册组件,可以按照以下步骤进行:
-
创建一个新的文件,比如
globalComponents.js
。 -
在这个文件中注册组件并导出:
// globalComponents.js import Vue from 'vue'; import MyComponent from './components/MyComponent.vue'; Vue.component('my-component', MyComponent);
-
在入口文件中引入这个文件即可:
// main.js import Vue from 'vue'; import App from './App.vue'; import './globalComponents'; // 引入全局组件注册文件 new Vue({ render: h => h(App), }).$mount('#app');
这样就可以在 Vue.js 中全局注册组件了。
props
将组件类比成方法,那么props就是传入方法的参数
在 Vue.js 中,props
是用于父组件向子组件传递数据的一种机制。它允许父组件通过属性的形式向子组件传递数据,子组件可以接收并在其内部使用这些数据。以下是关于props
的详细说明:
原理
当父组件向子组件传递数据时,可以通过在子组件的标签上使用属性的形式传递数据。子组件通过在自己的选项中定义 props
属性,来接收传递过来的数据。
作用
- 在父子组件之间传递数据。
- 实现组件的可复用性,使得组件可以接受不同的数据,从而达到灵活配置的目的。
使用方法
-
在子组件中通过
props
属性定义接收的数据:// ChildComponent.vue <script> export default { props: { message: String, // 接收一个名为 message 的 String 类型的 prop count: { type: Number, // 接收一个名为 count 的 Number 类型的 prop default: 0, // 默认值为 0 }, items: { // 接收一个名为 items 的 Array 类型的 prop type: Array, required: true, // 必传 prop }, }, // 组件逻辑... } </script>
-
在父组件中通过子组件的标签属性传递数据:
<!-- ParentComponent.vue --> <template> <div> <child-component :message="parentMessage" :count="parentCount" :items="parentItems" /> </div> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent, }, data() { return { parentMessage: 'Hello from parent', parentCount: 10, parentItems: ['item1', 'item2', 'item3'], }; }, }; </script>
代码示例
下面是一个简单的例子,演示了如何在父组件中向子组件传递数据:
<!-- ParentComponent.vue -->
<template>
<div>
<child-component :message="parentMessage" />
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
data() {
return {
parentMessage: 'Hello from parent',
};
},
};
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
props: {
message: String,
},
};
</script>
在这个例子中,父组件 ParentComponent
中定义了一个 parentMessage
数据,并通过 child-component
标签的 message
属性向子组件传递了这个数据。子组件 ChildComponent
中通过 props
接收并使用了这个数据,最终渲染到页面上。