Vue 3 ref 和 reactive 完整指南

引言

Vue 3 引入了新的响应式 API,其中包括 refreactive 两个核心概念。这两个 API 提供了创建和管理响应式数据的新方法。接下来将深入探讨 refreactive 的用法、内部实现以及一些高级用法。

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); // 输出: 0
count.value = 1;
console.log(count.value); // 输出: 1
console.log(count.value); // 输出: 0

count.value = 1;
console.log(count.value); // 输出: 1
console.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); // 输出: 0
state.count = 1;
console.log(state.count); // 输出: 1
console.log(state.count); // 输出: 0

state.count = 1;
console.log(state.count); // 输出: 1
console.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. refreactive 的内部实现

ref 的内部实现

ref 的内部实现基于一个封装了实际值的对象,这个对象实现了 getset 方法,以便在读取和设置值时能够自动触发依赖更新。

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; } }); }

tracktrigger

tracktrigger 是响应式系统的核心部分,用于收集依赖和触发更新。

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. 高级用法

使用 toRefstoRef

toRefstoRef 用于将一个响应式对象中的某个属性转换为 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); // 输出: 1
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); // 输出: 1
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); // 输出: 1

使用 unrefisRef

unref 用于解包 ref,如果传入的是 ref,则返回其 .value;如果不是 ref,则直接返回原值。

import { 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
import { 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
import { 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

使用 shallowRefshallowReactive

shallowRefshallowReactive 用于创建浅层的响应式引用。

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);

使用 watchwatchEffect

watchwatchEffect 用于监听响应式数据的变化。

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}`);
});
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 组件示例,展示了如何使用 refreactive

创建 Vue 应用

确保你已经安装了 Vue CLI:

npm install -g @vue/cli
npm install -g @vue/cli
npm install -g @vue/cli

初始化项目

vue create my-app
cd my-app
vue create my-app
cd my-app
vue 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 serve
npm run serve
npm run serve

现在你可以打开浏览器并访问 http://localhost:8080/ 来查看你的应用。

结论

通过上述内容,你应该能够更好地理解 Vue 3 中 refreactive 的工作原理及其用法。这两个 API 是 Vue 3 中非常强大的工具,它们使得响应式编程变得更加简单和直观。如果你有任何疑问或者需要进一步的帮助,请随时在评论区留言!!!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
Life must be lived with love, happiness, and dreams.
人生一定要有爱,有快乐,有梦想
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容