引言
Vue 3 引入了新的响应式 API,其中包括 ref
和 reactive
两个核心概念。这两个 API 提供了创建和管理响应式数据的新方法。接下来将深入探讨 ref
和 reactive
的用法、内部实现以及一些高级用法。
1. ref
的基本用法
ref
用于创建一个基本类型的响应式引用。
import { ref } from 'vue';const count = ref(0);import { ref } from 'vue'; const count = ref(0);import { ref } from 'vue'; const count = ref(0);
访问 ref
的值
ref
的值可以通过 .value
属性来访问和修改。
console.log(count.value); // 输出: 0count.value = 1;console.log(count.value); // 输出: 1console.log(count.value); // 输出: 0 count.value = 1; console.log(count.value); // 输出: 1console.log(count.value); // 输出: 0 count.value = 1; console.log(count.value); // 输出: 1
在模板中使用 ref
在 Vue 3 的模板中,可以直接使用 ref
而不需要 .value
。
<template><div>Count: {{ count }}<button @click="increment">Increment</button></div></template><script>import { ref } from 'vue';export default {setup() {const count = ref(0);function increment() {count.value++;}return {count,increment};}};</script><template> <div> Count: {{ count }} <button @click="increment">Increment</button> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const count = ref(0); function increment() { count.value++; } return { count, increment }; } }; </script><template> <div> Count: {{ count }} <button @click="increment">Increment</button> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const count = ref(0); function increment() { count.value++; } return { count, increment }; } }; </script>
2. reactive
的基本用法
reactive
用于创建一个对象类型的响应式引用。
import { reactive } from 'vue';const state = reactive({count: 0,message: 'Hello Vue!'});import { reactive } from 'vue'; const state = reactive({ count: 0, message: 'Hello Vue!' });import { reactive } from 'vue'; const state = reactive({ count: 0, message: 'Hello Vue!' });
访问 reactive
的值
reactive
的值可以直接访问和修改。
console.log(state.count); // 输出: 0state.count = 1;console.log(state.count); // 输出: 1console.log(state.count); // 输出: 0 state.count = 1; console.log(state.count); // 输出: 1console.log(state.count); // 输出: 0 state.count = 1; console.log(state.count); // 输出: 1
在模板中使用 reactive
在 Vue 3 的模板中,可以直接使用 reactive
的属性。
<template><div>Count: {{ state.count }}Message: {{ state.message }}<button @click="increment">Increment</button></div></template><script>import { reactive } from 'vue';export default {setup() {const state = reactive({count: 0,message: 'Hello Vue!'});function increment() {state.count++;}return {state,increment};}};</script><template> <div> Count: {{ state.count }} Message: {{ state.message }} <button @click="increment">Increment</button> </div> </template> <script> import { reactive } from 'vue'; export default { setup() { const state = reactive({ count: 0, message: 'Hello Vue!' }); function increment() { state.count++; } return { state, increment }; } }; </script><template> <div> Count: {{ state.count }} Message: {{ state.message }} <button @click="increment">Increment</button> </div> </template> <script> import { reactive } from 'vue'; export default { setup() { const state = reactive({ count: 0, message: 'Hello Vue!' }); function increment() { state.count++; } return { state, increment }; } }; </script>
3. ref
和 reactive
的内部实现
ref
的内部实现
ref
的内部实现基于一个封装了实际值的对象,这个对象实现了 get
和 set
方法,以便在读取和设置值时能够自动触发依赖更新。
function ref(initialValue) {let cleanupFn;const r = {value: initialValue,effect: new ReactiveEffect(() => {cleanupFn && cleanupFn();cleanupFn = trackRef(r);})};// trackRef 用于收集依赖function trackRef(ref) {if (activeEffect) {track(activeEffect, ref);return () => {trigger(activeEffect, ref);};}}// ReactiveEffect 用于创建响应式效果class ReactiveEffect {constructor(fn) {this.fn = fn;this.deps = [];}run() {activeEffect = this;try {this.fn();} finally {activeEffect = undefined;}}}r.effect.run();return r;}function ref(initialValue) { let cleanupFn; const r = { value: initialValue, effect: new ReactiveEffect(() => { cleanupFn && cleanupFn(); cleanupFn = trackRef(r); }) }; // trackRef 用于收集依赖 function trackRef(ref) { if (activeEffect) { track(activeEffect, ref); return () => { trigger(activeEffect, ref); }; } } // ReactiveEffect 用于创建响应式效果 class ReactiveEffect { constructor(fn) { this.fn = fn; this.deps = []; } run() { activeEffect = this; try { this.fn(); } finally { activeEffect = undefined; } } } r.effect.run(); return r; }function ref(initialValue) { let cleanupFn; const r = { value: initialValue, effect: new ReactiveEffect(() => { cleanupFn && cleanupFn(); cleanupFn = trackRef(r); }) }; // trackRef 用于收集依赖 function trackRef(ref) { if (activeEffect) { track(activeEffect, ref); return () => { trigger(activeEffect, ref); }; } } // ReactiveEffect 用于创建响应式效果 class ReactiveEffect { constructor(fn) { this.fn = fn; this.deps = []; } run() { activeEffect = this; try { this.fn(); } finally { activeEffect = undefined; } } } r.effect.run(); return r; }
reactive
的内部实现
reactive
的内部实现基于 Proxy 对象。它创建了一个新的代理对象,该对象拦截了原始对象的操作,并在读取或修改属性时触发依赖更新。
function reactive(target) {return new Proxy(target, {get(target, key) {track(target, key);return target[key];},set(target, key, value) {target[key] = value;trigger(target, key);return true;}});}function reactive(target) { return new Proxy(target, { get(target, key) { track(target, key); return target[key]; }, set(target, key, value) { target[key] = value; trigger(target, key); return true; } }); }function reactive(target) { return new Proxy(target, { get(target, key) { track(target, key); return target[key]; }, set(target, key, value) { target[key] = value; trigger(target, key); return true; } }); }
track
和 trigger
track
和 trigger
是响应式系统的核心部分,用于收集依赖和触发更新。
let activeEffect;function track(target, key) {if (activeEffect) {// 收集依赖activeEffect.deps.push({ target, key });}}function trigger(target, key) {if (activeEffect) {// 触发更新for (const dep of activeEffect.deps) {if (dep.target === target && dep.key === key) {dep.callback();}}}}let activeEffect; function track(target, key) { if (activeEffect) { // 收集依赖 activeEffect.deps.push({ target, key }); } } function trigger(target, key) { if (activeEffect) { // 触发更新 for (const dep of activeEffect.deps) { if (dep.target === target && dep.key === key) { dep.callback(); } } } }let activeEffect; function track(target, key) { if (activeEffect) { // 收集依赖 activeEffect.deps.push({ target, key }); } } function trigger(target, key) { if (activeEffect) { // 触发更新 for (const dep of activeEffect.deps) { if (dep.target === target && dep.key === key) { dep.callback(); } } } }
4. 高级用法
使用 toRefs
和 toRef
toRefs
和 toRef
用于将一个响应式对象中的某个属性转换为 ref
。
import { reactive, toRefs, toRef } from 'vue';const state = reactive({ count: 0 });const { count } = toRefs(state); // 解构赋值const countRef = toRef(state, 'count'); // 单独转换countRef.value++; // 修改 `count` 的值console.log(state.count); // 输出: 1import { reactive, toRefs, toRef } from 'vue'; const state = reactive({ count: 0 }); const { count } = toRefs(state); // 解构赋值 const countRef = toRef(state, 'count'); // 单独转换 countRef.value++; // 修改 `count` 的值 console.log(state.count); // 输出: 1import { reactive, toRefs, toRef } from 'vue'; const state = reactive({ count: 0 }); const { count } = toRefs(state); // 解构赋值 const countRef = toRef(state, 'count'); // 单独转换 countRef.value++; // 修改 `count` 的值 console.log(state.count); // 输出: 1
使用 unref
和 isRef
unref
用于解包 ref
,如果传入的是 ref
,则返回其 .value
;如果不是 ref
,则直接返回原值。
import { ref, unref, isRef } from 'vue';const count = ref(0);const num = 1;console.log(unref(count)); // 输出: 0console.log(unref(num)); // 输出: 1console.log(isRef(count)); // 输出: trueconsole.log(isRef(num)); // 输出: falseimport { ref, unref, isRef } from 'vue'; const count = ref(0); const num = 1; console.log(unref(count)); // 输出: 0 console.log(unref(num)); // 输出: 1 console.log(isRef(count)); // 输出: true console.log(isRef(num)); // 输出: falseimport { ref, unref, isRef } from 'vue'; const count = ref(0); const num = 1; console.log(unref(count)); // 输出: 0 console.log(unref(num)); // 输出: 1 console.log(isRef(count)); // 输出: true console.log(isRef(num)); // 输出: false
使用 shallowRef
和 shallowReactive
shallowRef
和 shallowReactive
用于创建浅层的响应式引用。
import { shallowRef, shallowReactive } from 'vue';const count = shallowRef(0);const state = shallowReactive({ count: 0 });import { shallowRef, shallowReactive } from 'vue'; const count = shallowRef(0); const state = shallowReactive({ count: 0 });import { shallowRef, shallowReactive } from 'vue'; const count = shallowRef(0); const state = shallowReactive({ count: 0 });
使用 markRaw
markRaw
用于标记一个对象使其不再成为响应式的。
import { markRaw } from 'vue';const nonReactiveObj = markRaw({ someProp: 1 });import { markRaw } from 'vue'; const nonReactiveObj = markRaw({ someProp: 1 });import { markRaw } from 'vue'; const nonReactiveObj = markRaw({ someProp: 1 });
使用 toRaw
toRaw
用于获取一个响应式对象的原始值。
import { reactive, toRaw } from 'vue';const state = reactive({ a: 1 });const rawState = toRaw(state);import { reactive, toRaw } from 'vue'; const state = reactive({ a: 1 }); const rawState = toRaw(state);import { reactive, toRaw } from 'vue'; const state = reactive({ a: 1 }); const rawState = toRaw(state);
使用 watch
和 watchEffect
watch
和 watchEffect
用于监听响应式数据的变化。
import { watch, ref, watchEffect } from 'vue';const count = ref(0);// 使用 watchwatch(count, (newVal, oldVal) => {console.log(`Count changed from ${oldVal} to ${newVal}`);});// 使用 watchEffectwatchEffect(() => {console.log(`Current count is ${count.value}`);});import { watch, ref, watchEffect } from 'vue'; const count = ref(0); // 使用 watch watch(count, (newVal, oldVal) => { console.log(`Count changed from ${oldVal} to ${newVal}`); }); // 使用 watchEffect watchEffect(() => { console.log(`Current count is ${count.value}`); });import { watch, ref, watchEffect } from 'vue'; const count = ref(0); // 使用 watch watch(count, (newVal, oldVal) => { console.log(`Count changed from ${oldVal} to ${newVal}`); }); // 使用 watchEffect watchEffect(() => { console.log(`Current count is ${count.value}`); });
5. 示例代码
下面是一个完整的 Vue 3 组件示例,展示了如何使用 ref
和 reactive
。
创建 Vue 应用
确保你已经安装了 Vue CLI:
npm install -g @vue/clinpm install -g @vue/clinpm install -g @vue/cli
初始化项目
vue create my-appcd my-appvue create my-app cd my-appvue create my-app cd my-app
编辑 src/App.vue
<template><div id="app"><h1>{{ message }}</h1><p>Count: {{ count }}</p><button @click="increment">Increment</button></div></template><script>import { ref, reactive, toRefs, toRef, unref, isRef } from 'vue';export default {setup() {const state = reactive({ count: 0, message: 'Welcome to Vue 3!' });const countRef = toRef(state, 'count');function increment() {countRef.value++;}return {...toRefs(state), // 解构赋值countRef,increment};}};</script><template> <div id="app"> <h1>{{ message }}</h1> <p>Count: {{ count }}</p> <button @click="increment">Increment</button> </div> </template> <script> import { ref, reactive, toRefs, toRef, unref, isRef } from 'vue'; export default { setup() { const state = reactive({ count: 0, message: 'Welcome to Vue 3!' }); const countRef = toRef(state, 'count'); function increment() { countRef.value++; } return { ...toRefs(state), // 解构赋值 countRef, increment }; } }; </script><template> <div id="app"> <h1>{{ message }}</h1> <p>Count: {{ count }}</p> <button @click="increment">Increment</button> </div> </template> <script> import { ref, reactive, toRefs, toRef, unref, isRef } from 'vue'; export default { setup() { const state = reactive({ count: 0, message: 'Welcome to Vue 3!' }); const countRef = toRef(state, 'count'); function increment() { countRef.value++; } return { ...toRefs(state), // 解构赋值 countRef, increment }; } }; </script>
运行应用
npm run servenpm run servenpm run serve
现在你可以打开浏览器并访问 http://localhost:8080/
来查看你的应用。
结论
通过上述内容,你应该能够更好地理解 Vue 3 中 ref
和 reactive
的工作原理及其用法。这两个 API 是 Vue 3 中非常强大的工具,它们使得响应式编程变得更加简单和直观。如果你有任何疑问或者需要进一步的帮助,请随时在评论区留言!!!
暂无评论内容