Vue3 问题总结
vue3 获取组件实例
在 Vue 3 的 setup
函数中,this
不再指向组件实例,这与 Vue 2 的选项 API 有所不同。在 setup
中获取组件实例的方法是使用 getCurrentInstance
函数,该函数返回当前组件的实例。
import { getCurrentInstance } from 'vue';
export default {
setup() {
const instance = getCurrentInstance();
console.log(instance); // 打印组件实例
return {};
}
};
getCurrentInstance
返回的是组件实例对象的内部表示形式,包含了许多有用的属性和方法:
- proxy: 组件实例的代理对象(可以通过它访问
$props
、$emit
、$slots
等)。 - appContext: 当前应用的上下文。
- type: 组件的类型(即组件的定义对象)。
- props: 当前组件的属性对象。
- emit: 用于触发事件的方法。
在 setup
中 this
不再指向组件实例,但你可以通过 instance.proxy
来访问组件的属性和方法,例如:
import { getCurrentInstance } from 'vue';
export default {
setup() {
const instance = getCurrentInstance();
// 访问组件的 props
const props = instance.proxy.$props;
// 触发事件
instance.proxy.$emit('custom-event', 'event data');
return {};
}
};
nextTick
Composition API 中有了更直接的替代方法。可以使用 nextTick
函数,它可以从 vue
包中导入并在 setup
函数或其他 Composition API 钩子中使用。
<script setup>
import { ref, nextTick } from 'vue';
// 定义响应式状态
const count = ref(0);
// 增加计数的函数
function increment() {
count.value++;
nextTick(() => {
console.log('DOM updated');
});
}
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
setup 函数
<script>
import { ref, computed, watch, onMounted, nextTick, getCurrentInstance } from 'vue';
export default {
name: 'AppName',
setup() {
const instance = getCurrentInstance();
console.log('instance', instance); // 打印组件实例
// 定义响应式状态
const count = ref(0);
// 定义计算属性
const doubleCount = computed(() => count.value * 2);
// 定义方法
function increment() {
count.value++;
nextTick(() => {
console.log('DOM 更新');
});
}
// 侦听器
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`);
});
// 生命周期钩子
onMounted(() => {
console.log('Mounted instance:', instance);
console.log('Component props:', instance.proxy.$props);
});
return {
count,
doubleCount,
increment
};
}
};
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
</div>
</template>
在 App 全局挂载插件
- 声明插件
import type { App, Plugin } from 'vue';
interface MyPluginOptions {
message: string;
}
const MyPlugin: Plugin = {
install(app: App, options: MyPluginOptions) {
app.config.globalProperties.$myPluginMessage = options.message;
}
};
export default MyPlugin;
- 挂载到全局并传参
import { createApp } from 'vue';
import MyPlugin from './MyPlugin';
import App from './App.vue';
createApp(App).use(MyPlugin, { message: 'Hello, World!' }).mount('#app');
- 使用插件
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'MyComponent',
computed: {
message(): string {
return this.$myPluginMessage;
}
}
});
</script>
<template>
<div>{{ message }}</div>
</template>
<!--
setup 语法糖:
<script setup lang="ts">
import { getCurrentInstance, computed } from 'vue'
const instance = getCurrentInstance()
// 获取插件提供的全局属性
const message = computed(() => {
return instance.appContext.config.globalProperties.$myPluginMessage
})
</script>
-->
vue3 响应式数据
1. ref
ref
用于声明基本类型(如字符串、数字、布尔值)或对象类型的数据,使其成为响应式的。使用 ref
时,返回的是一个包含响应式数据的对象,该对象有一个 .value
属性来访问或修改其值。
import { ref } from 'vue';
const count = ref(0);
function increment() {
count.value++;
}
特点:
- 适用于基本类型和对象:可以用于基本类型(如字符串、数字)和对象。
- 通过
.value
访问值:需要通过.value
属性访问和修改其值。 - 自动解包:在模板中,
ref
会自动解包,不需要显式调用.value
。
2. reactive
reactive
用于声明复杂类型(如对象、数组)的响应式数据。它接收一个对象,并返回该对象的响应式代理。通过代理,可以直接访问和修改对象的属性,而无需使用 .value
。
import { reactive } from 'vue';
const state = reactive({
count: 0,
user: {
name: 'John Doe',
age: 30
}
});
function increment() {
state.count++;
}
function updateUserName(newName) {
state.user.name = newName;
}
特点:
- 适用于对象和数组:更适合用于复杂的嵌套对象和数组。
- 直接访问属性:可以直接访问和修改对象的属性,无需使用
.value
。 - 深度响应:
reactive
创建的是一个深度响应式对象,内部所有嵌套的属性都会被转换为响应式。
3. toRef 和 toRefs
在 Vue 3 中,toRef
和 toRefs
是两个用于处理响应式数据的工具函数。它们主要用于将响应式对象的属性转换为 ref
,以便在解构或传递属性时保持响应式特性。这对于处理复杂状态和组合多个状态非常有用。
toRef
用于将响应式对象的单个属性转换为ref
。这在你希望单独引用和操作响应式对象的某个属性时特别有用。
import { reactive, toRef } from 'vue';
const state = reactive({
count: 0,
user: {
name: 'John Doe',
age: 30
}
});
// 将 state 的 count 属性转换为 ref
const countRef = toRef(state, 'count');
function increment() {
countRef.value++;
}
console.log(countRef.value); // 输出 0
toRefs
用于将响应式对象的所有属性转换为ref
。这在你希望解构响应式对象时保持响应式特性特别有用。
import { reactive, toRefs } from 'vue';
const state = reactive({
count: 0,
user: {
name: 'John Doe',
age: 30
}
});
// 将 state 的所有属性转换为 ref
const { count, user } = toRefs(state);
function increment() {
count.value++;
}
function updateUserName(newName: string) {
user.value.name = newName;
}
console.log(count.value); // 输出 0
console.log(user.value.name); // 输出 'John Doe'
4. 示例
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<p>User: {{ user.name }} - {{ user.age }}</p>
<button @click="updateUserName">Update User Name</button>
</div>
</template>
<script setup lang="ts">
import { reactive, toRefs } from 'vue'
const state = reactive({
count: 0,
user: {
name: 'John Doe',
age: 30
}
})
const { count, user } = toRefs(state)
function increment() {
count.value++
}
function updateUserName() {
user.value.name = 'Jane Doe'
}
</script>
Teleport
<Teleport>
是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。
<button @click="open = true">Open Modal</button>
<Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>
ref 和 shallowRef 的区别
在 Vue 3 中,ref
和shallowRef
都是用于创建响应式引用的函数,但它们之间有一些区别。
1 ref
ref
是用于创建一个包装器对象,将普通 JavaScript 值转换为响应式对象。它返回一个带有.value
属性的对象,通过.value
属性来访问和修改包装的值。- 当响应式对象中包含嵌套的对象或数组时,
ref
会将整个对象或数组转换为响应式对象。 - 例如:
javascriptCopy codeimport { ref } from 'vue';
const obj = ref({ count: 0 });
console.log(obj.value.count); // 访问嵌套的属性
obj.value.count++; // 修改嵌套的属性
2. shallowRef
shallowRef
也是用于创建一个包装器对象,将普通 JavaScript 值转换为响应式对象。但与ref
不同的是,shallowRef
会对嵌套的对象或数组进行浅响应式转换。- 当使用
shallowRef
创建响应式对象时,只有对象的第一层属性会被转换为响应式对象,而不会递归地转换整个对象。 - 例如:
javascriptCopy codeimport { shallowRef } from 'vue';
const obj = shallowRef({ count: 0 });
console.log(obj.value.count); // 访问嵌套的属性
obj.value.count++; // 修改嵌套的属性
// 在这种情况下,只有第一层属性`count`会被转换为响应式对象,而不会递归转换整个对象。
3. 区别总结
ref
会递归地将整个对象或数组转换为响应式对象,而shallowRef
只会将对象的第一层属性转换为响应式对象,不会递归转换整个对象。- 当你需要对整个对象进行深层次的响应式转换时,应该使用
ref
;当你只需要对对象的第一层属性进行响应式转换时,可以使用shallowRef
。
ref 与 reactive 区别
1. ref
function ref(value) {
return createRef(value, false);
}
function createRef(rawValue, shallow) {
if (isRef(rawValue)) {
return rawValue;
}
return new RefImpl(rawValue, shallow);
}
class RefImpl {
constructor(value, __v_isShallow) {
this.__v_isShallow = __v_isShallow;
this.dep = void 0;
this.__v_isRef = true;
this._rawValue = __v_isShallow ? value : toRaw(value);
this._value = __v_isShallow ? value : toReactive(value);
}
get value() {
trackRefValue(this);
return this._value;
}
set value(newVal) {
const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal);
newVal = useDirectValue ? newVal : toRaw(newVal);
if (shared.hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal;
this._value = useDirectValue ? newVal : toReactive(newVal);
triggerRefValue(this, 4, newVal);
}
}
}
ref
是一个函数,用于创建一个包装器对象,将普通 JavaScript 值转换为响应式对象。它返回一个带有.value
属性的对象,这个属性用来访问和修改包装的值。ref
适用于简单的数据类型,如基本类型和对象。- 当你需要为基本类型数据或普通对象创建响应式引用时,应该使用
ref
。
javascriptCopy codeimport { ref } from 'vue';
const count = ref(0);
console.log(count.value); // 访问包装后的值
count.value++; // 修改包装后的值
2. reactive
function reactive(target) {
if (isReadonly(target)) {
return target;
}
return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
}
function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {
if (!shared.isObject(target)) {
{
warn(`value cannot be made reactive: ${String(target)}`);
}
return target;
}
if (target['__v_raw'] && !(isReadonly2 && target['__v_isReactive'])) {
return target;
}
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
const targetType = getTargetType(target);
if (targetType === 0 /* INVALID */) {
return target;
}
const proxy = new Proxy(target, targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);
proxyMap.set(target, proxy);
return proxy;
}
reactive
是一个函数,用于创建一个响应式代理对象,使对象的属性变化能够触发视图更新。它返回一个代理对象,你可以直接访问和修改这个对象的属性。reactive
适用于复杂的数据类型,如对象和数组。- 当你需要为对象或数组创建响应式代理时,应该使用
reactive
。
javascriptCopy codeimport { reactive } from 'vue';
const state = reactive({
count: 0,
message: 'Hello'
});
console.log(state.count); // 直接访问属性
state.count++; // 直接修改属性
3. 区别总结
ref
适用于简单的数据类型,提供了一个包装器对象,需要通过.value
访问和修改值。reactive
适用于复杂的数据类型,直接创建一个代理对象,可以直接访问和修改对象的属性。- 通常情况下,如果你只需要为一个简单的值创建响应式引用,可以使用
ref
;如果你需要创建一个响应式的对象或数组,应该使用reactive
。
首先 vue3 通过 reactive 的方法,利用 proxy 的代理对象解决大量在 vue2 中 Object.defineProperty 的痛点,极好的支持对象监听,但同时 proxy 并不能直接监听基础类型,需要每次都构建一个对象去用 Proxy 代理,这样就会造成极大的性能损耗。
因此 vue2 就提供了一个 ref 方法,通过返回一个简单的响应式对象,专门用于处理基础类型的数据响应。