当前位置: 首页 > news >正文

Vue进阶(一篇进入Vue3的世界)

文章目录

  • 一、初识Vue3
  • 二、Vue3新语法糖setup
  • 三、响应式数据函数
    • 3.1 ref函数
    • 3.2 reactive函数
    • 3.3 ref和reactive函数的异同
  • 四、Vue3的响应式原理
  • 五、语法更新
    • 5.1 Vue3使用computed计算属性
    • 5.2 Vue3使用watch监视属性的注意点
      • 5.2.1 监视ref对象
      • 5.2.2 监视reactive对象
      • 5.2.3 监视嵌套属性
      • 5.2.4 避免循环引用:
    • 5.3 watchEffect函数
  • 六、Vue3的生命周期
    • 6.1 生命周期钩子函数
    • 6.2 生命周期的变化
  • 七、Vue3新增
    • 7.1 toRef与toRefs
    • 7.2 shallowReactive与shallowRef
    • 7.3 readonly与shallowReadonly
    • 7.4 toRaw与markRaw
    • 7.5 customRef
    • 7.6 provide与inject
    • 7.7 Teleport
    • 7.8 Suspense

一、初识Vue3

Vue3是一款流行的JavaScript框架Vue的最新版本,它于2020年9月正式发布。Vue3主要着重于性能的提升、更好的开发体验以及更好的代码组织方式。

Vue3的主要特性和改进:

  1. 更快的性能:Vue3对虚拟DOM进行了重构,通过优化渲染和更新算法,可以显著提高性能。
  2. 更小的包大小:Vue3对代码进行了精简和优化,使其比Vue2更小。
  3. Composition APIVue3引入了Composition API,这是一种新的API风格,提供了更好的代码组织方式,使得开发人员可以更好地管理代码复杂度。
  4. 改进的TypeScript支持:Vue3通过TypeScript提供了更好的类型检查支持,这有助于减少代码错误和提高开发效率。
  5. TeleportVue3引入了Teleport,这是一种新的组件传送方式,可以方便地将组件渲染到DOM中的任何位置。
  6. 其他改进:Vue3还提供了许多其他改进,包括更好的错误处理、更好的调试支持、更好的跨平台支持等。

总体来说,Vue3是一个更强大、更易于使用和更快的框架,它为开发人员提供了更好的工具来构建现代Web应用程序。

二、Vue3新语法糖setup

setupVue3引入的新选项,用于定义组件的状态和行为。在Vue2中,我们使用datacomputedmethods等选项来定义组件的状态和行为,但在Vue 3中,setup选项被引入,以提供更清晰的代码组织和更灵活的API

setup函数接收两个参数:propscontextprops是组件的属性对象,可以在函数内部使用,context是一个对象,包含了一些属性和方法,如attrsslotsemit等,用于访问组件的属性、插槽和自定义事件。

setup函数内部,我们可以使用refreactivecomputed等函数来定义组件的响应式数据和计算属性,还可以使用普通的JavaScript语法来定义方法和计算逻辑。例如:

import { ref, reactive, computed } from 'vue'
export default {props: {message: String},setup(props, context) {const count = ref(0)const state = reactive({name: 'Tom',age: 18})const doubleCount = computed(() => count.value * 2)function increment() {count.value++}return {count,state,doubleCount,increment}}
}

在模板中,我们可以直接访问setup函数返回的响应式数据、计算属性和方法。例如:

<template><div><p>Count: {{ count }}</p><p>Double Count: {{ doubleCount }}</p><p>Name: {{ state.name }}, Age: {{ state.age }}</p><button @click="increment">Increment</button></div>
</template>

setupVue3中定义组件状态和行为的主要方式,它提供了更灵活、更清晰的API,可以帮助我们更好地组织代码。

三、响应式数据函数

3.1 ref函数

ref函数用于创建一个包装器对象,使得基本类型的值也可以响应式地更新。它接受一个初始值作为参数,并返回一个包装器对象,可以通过.value来访问包装的值,如下所示:

import { ref } from 'vue'
const count = ref(0)
console.log(count.value) // 0
count.value++ // 更新值
console.log(count.value) // 1

在模板中使用ref时,可以直接访问.value属性,如下所示:

<template><div>Count: {{ count.value }}</div>
</template>
<script>
import { ref } from 'vue'
export default {setup() {const count = ref(0)return {count}}
}
</script>

3.2 reactive函数

reactive函数用于创建一个响应式的对象。它接受一个普通的JavaScript对象作为参数,并返回一个响应式代理对象,可以通过访问代理对象的属性来触发更新。例如:

import { reactive } from 'vue'
const state = reactive({count: 0,message: 'Hello, Vue 3!'
})
console.log(state.count) // 0
state.count++ // 更新对象属性
console.log(state.count) // 1

在模板中使用响应式对象时,可以直接访问对象的属性,如下所示:

<template><div>Count: {{ state.count }}Message: {{ state.message }}</div>
</template>
<script>
import { reactive } from 'vue'
export default {setup() {const state = reactive({count: 0,message: 'Hello, Vue 3!'})return {state}}
}
</script>

3.3 ref和reactive函数的异同

refreactiveVue3中用于创建响应式数据的两个函数,它们有一些相同点和不同点。

相同点:

  1. 都可以创建响应式数据,使得数据状态的更新更加自然和高效。
  2. 在使用上都可以像普通对象一样访问和修改属性。
  3. 都可以在setup函数内部使用。

不同点:

  1. ref函数用于创建一个包装器对象,使得基本类型的值也可以响应式地更新,而reactive函数用于创建一个响应式的对象。
  2. 使用ref函数创建的响应式数据在访问时需要通过.value来访问包装的值,而使用reactive函数创建的响应式数据直接访问对象属性即可。
  3. ref函数只能包装一个值,而reactive函数可以包装一个对象,可以包含多个属性。
  4. ref函数返回的是一个包装器对象,而reactive函数返回的是一个响应式代理对象。
  5. ref函数还有一些其他的API,如.unref().isRef()等,用于对包装器对象进行操作和判断,而reactive函数则没有。

refreactive函数是Vue 3中用于创建响应式数据的两种常用方式,它们使得数据状态的更新更加自然和高效。使用哪种方式取决于需要创建的数据类型,如果是基本类型,可以使用ref,如果是对象类型,可以使用reactive

四、Vue3的响应式原理

Vue3的响应式原理与Vue2有所不同。Vue2使用了Object.defineProperty来实现响应式,而Vue3则使用了ES6Proxy对象。

Vue3中,当我们创建一个响应式的数据对象时,Vue3会使用一个叫做reactive的函数将该对象转换成响应式对象。reactive函数会使用Proxy对象对原始数据对象进行代理,拦截对该对象的所有访问和修改操作,并且在必要的时候触发响应式更新。

当我们对响应式对象进行属性的访问或修改时,Proxy对象会拦截这些操作,并通知Vue3进行响应式更新。这个过程中,Vue3会使用一个叫做effect的函数来收集依赖,然后在属性发生变化时触发依赖的更新。

effect函数是Vue3中实现副作用的核心函数,我们可以通过调用它来创建响应式的副作用函数。当我们创建一个副作用函数时,effect函数会自动执行该函数,并且在副作用函数中访问到的所有响应式数据都会被自动收集为依赖。当依赖发生变化时,副作用函数会自动被重新执行。

总的来说,Vue3的响应式原理与Vue2有所不同,但是它仍然是基于数据劫持的原理来实现响应式的。Vue3使用Proxy对象和effect函数来实现数据劫持和响应式更新,使得代码更加简洁、高效,并且可以更好地支持TypeScript等现代JavaScript特性。

五、语法更新

5.1 Vue3使用computed计算属性

Vue3中,可以使用computed函数来定义计算属性。computed函数接受一个函数作为参数,这个函数返回的值就是计算属性的值。

下面是一个示例:

import { computed } from 'vue'
export default {setup() {const count = ref(0)const doubledCount = computed(() => count.value * 2)return {count,doubledCount}}
}

在这个示例中,我们使用computed函数定义了一个计算属性doubledCount,它的值是count的两倍。count是一个响应式变量,使用ref函数创建。

computed函数的参数中,我们定义了一个箭头函数,它返回了count.value * 2。由于count是一个响应式变量,所以当count的值改变时,doubledCount的值也会自动更新。

setup函数中,我们将countdoubledCount返回给模板使用。在模板中,我们可以通过{{ count }}{{ doubledCount }}来获取它们的值。

5.2 Vue3使用watch监视属性的注意点

5.2.1 监视ref对象

Vue3中,ref对象是一个包装器,它包含一个value属性和一些辅助方法。当我们创建一个ref对象时,我们实际上是在创建一个带有value属性的JavaScript对象。因此,在监视ref对象时,我们需要监视的是ref对象的value属性而不是ref对象本身。这就意味着,当我们调用watch函数时,需要传入ref对象的value属性而不是ref对象本身。例如:

import { ref, watch } from 'vue';
const count = ref(0);
watch(count.value, (newValue, oldValue) => {console.log(`count changed from ${oldValue} to ${newValue}`);
});

在上面的例子中,我们监视了count.value的变化,而不是count对象本身的变化。

5.2.2 监视reactive对象

Vue3中,reactive对象是一个响应式对象,它可以包含多个属性。当我们创建一个reactive对象时,我们实际上是在创建一个带有多个属性的JavaScript对象。因此,在监视reactive对象时,我们需要监视整个对象而不是它的某个属性。例如:

import { reactive, watch } from 'vue';
const state = reactive({count: 0,message: 'Hello World!'
});
watch(state, (newValue, oldValue) => {console.log('state changed', newValue, oldValue);
});

在上面的例子中,我们监视了整个state对象的变化,而不是state.countstate.message的变化。

5.2.3 监视嵌套属性

当监视reactive对象的嵌套属性时,需要在watch函数的选项对象中指定deep选项为true,否则只会监听属性的赋值操作,而不会监听属性内部的变化。例如:

import { reactive, watch } from 'vue';
const state = reactive({nested: {count: 0}
});
watch(() => state.nested,(newValue, oldValue) => {console.log('nested state changed', newValue, oldValue);},{ deep: true }
);

5.2.4 避免循环引用:

如果reactive对象中包含循环引用,那么watch函数会陷入无限递归,导致程序崩溃。因此,在使用watch函数时需要注意避免循环引用的问题。例如:

import { reactive, watch } from 'vue';
const state = reactive({a: 1,b: {c: 2}
});
// 此处会陷入无限递归,导致程序崩溃
state.b.d = state;
watch(state,(newValue, oldValue) => {console.log('state changed', newValue, oldValue);},{ deep: true }
);

5.3 watchEffect函数

Vue3中,watchEffect函数可以用来监视响应式变量的变化并执行副作用函数。当响应式变量发生变化时,副作用函数会被重新执行。

下面是一个示例:

import { reactive, watchEffect } from 'vue'
export default {setup() {const state = reactive({count: 0})watchEffect(() => {console.log('Count changed to:', state.count)})function increment() {state.count++}return {state,increment}}
}

在这个示例中,我们使用reactive函数创建了一个响应式对象state,其中包含一个属性count。然后我们使用watchEffect函数监视了state.count的变化,并在控制台中打印了它的值。

setup函数中,我们将stateincrement函数返回给模板使用。在模板中,我们可以通过{{ state.count }}来获取count的值,并通过increment函数来增加count的值。

当我们在模板中点击增加按钮时,state.count的值会发生变化,watchEffect函数会被重新执行并在控制台中打印新的值。这个过程是自动触发的,不需要显式地定义一个watcher函数。

六、Vue3的生命周期

6.1 生命周期钩子函数

  1. beforeCreate: 在实例被创建之初,组件的属性和方法还没有初始化。

在这个阶段,Vue实例已经被创建,但是datamethods还没有被初始化。此时,我们可以做一些初始化工作,例如配置一些全局变量等等。

const app = Vue.createApp({beforeCreate() {console.log("beforeCreate");},created() {console.log("created");},
});
app.mount("#app");
  1. created: 在实例创建之后,组件的属性和方法已经初始化完成,但是DOM元素还没有被挂载。

在这个阶段,Vue实例已经被创建,并且datamethods已经被初始化。此时,我们可以做一些异步操作,例如通过ajax获取数据。

const app = Vue.createApp({created() {console.log("created");axios.get("/api/data").then((response) => {this.data = response.data;});},
});
app.mount("#app");
  1. beforeMount: 在DOM元素被挂载到页面之前,该钩子函数会被触发。

在这个阶段,Vue实例已经被创建,并且datamethods已经被初始化,但是DOM元素还没有被挂载。此时,我们可以做一些DOM操作。

const app = Vue.createApp({data() {return {message: "Hello, world!",};},beforeMount() {console.log("beforeMount");const title = document.createElement("h1");title.textContent = this.message;document.body.appendChild(title);},
});
app.mount("#app");
  1. mounted: 在DOM元素被挂载到页面之后,该钩子函数会被触发。

在这个阶段,Vue实例已经被创建,并且datamethods已经被初始化,DOM元素也已经被挂载到页面上。此时,我们可以做一些DOM操作,例如绑定事件监听器等等。

const app = Vue.createApp({data() {return {count: 0,};},mounted() {console.log("mounted");this.$refs.button.addEventListener("click", () => {this.count++;});},
});
app.mount("#app");
  1. beforeUpdate: 在组件更新之前,该钩子函数会被触发。

在这个阶段,Vue实例的data发生了改变,但是这些改变还没有被更新到DOM上。此时,我们可以做一些 DOM操作。

const app = Vue.createApp({data() {return {message: "Hello, world!",};},beforeUpdate() {console.log("beforeUpdate");const title = document.querySelector("h1");title.textContent = this.message.toUpperCase();},
});
app.mount("#app");
setInterval(() => {app.message = "Hello, Vue.js 3!";
}, 1000);
  1. updated: 在组件更新之后,该钩子函数会被触发。

在这个阶段,Vue实例的data发生了改变,且这些改变已经被更新到DOM上。此时,我们可以做一些DOM操作。

const app = Vue.createApp({data() {return {message: "Hello, world!",};},updated() {console.log("updated");},
});
app.mount("#app");
setInterval(() => {app.message = "Hello, Vue!";
}, 1000);
  1. beforeUnmount: 在实例被销毁之前,该钩子函数会被触发。

在这个阶段,Vue实例即将被销毁,但是DOM元素还存在。此时,我们可以做一些清理工作,例如取消事件监听器、清除定时器等等。

const app = Vue.createApp({data() {return {count: 0,};},mounted() {this.timer = setInterval(() => {this.count++;}, 1000);},beforeUnmount() {clearInterval(this.timer);},
});
app.mount("#app");
  1. unmounted: 在实例被销毁之后,该钩子函数会被触发。

在这个阶段,Vue实例已经被销毁,DOM元素也已经被移除。此时,我们不能再访问Vue实例的属性和方法。

const app = Vue.createApp({data() {return {message: "Hello, world!",};},beforeUnmount() {console.log("beforeUnmount");},unmounted() {console.log("unmounted");},
});
app.mount("#app");
setTimeout(() => {app.unmount();
}, 5000);

6.2 生命周期的变化

Vue3相较于Vue2的生命周期函数一些变化和改进:

  1. beforeCreatecreated被合并为beforeCreatebeforeMountmounted被合并为onMountedbeforeUpdateupdated被合并为onUpdatedbeforeDestroydestroyed被合并为 onBeforeUnmountonUnmounted。这种变化使得生命周期函数名称更加简洁,并且每个阶段只有一个函数,更容易理解和使用。
  2. 新增了两个生命周期函数:onBeforeMountonBeforeUpdate,分别在组件渲染之前被调用。
  3. 新增了一个生命周期函数:onRenderTrackedonRenderTriggered,用于调试组件渲染性能问题。这两个函数需要与Vue3的新特性Reactivity结合使用。

Vue3的生命周期函数相比Vue2更加简洁、清晰,并且新增了一些实用的函数。

七、Vue3新增

7.1 toRef与toRefs

toReftoRefsVue3中的两个新函数,用于创建响应式对象。

  • toRef接收两个参数,第一个参数是响应式对象,第二个参数是该对象的属性名。它会返回一个新的ref对象,该对象指向原始对象的属性,并且任何对该属性的修改都会被响应式地更新到原始对象上。

例如,假设有一个包含name属性的对象person

import { ref, toRef } from 'vue';
const person = {name: 'Alice'
};
const nameRef = toRef(person, 'name');
console.log(nameRef.value); // 'Alice'
nameRef.value = 'Bob';
console.log(person.name); // 'Bob'

在上面的例子中,nameRef是一个指向person.name的ref对象。通过修改nameRef.valueperson.name的值也被修改了。

  • toRefs则接收一个响应式对象,并将其转换为一个包含相同属性名的对象,每个属性都是ref对象。这样做的好处是可以在模板中方便地使用对象的属性而不需要使用.value

例如:

import { reactive, toRefs } from 'vue';
const person = reactive({name: 'Alice',age: 30
});
const personRefs = toRefs(person);
console.log(personRefs.name.value); // 'Alice'
personRefs.age.value++;
console.log(person.age); // 31

在上面的例子中,personRefs是一个包含nameage属性的对象,每个属性都是一个ref对象。可以像访问普通对象一样访问personRefs的属性,而不需要使用.value

7.2 shallowReactive与shallowRef

shallowReactiveshallowRefVue3中的两个新函数,用于创建浅层响应式对象。

  • shallowReactive函数可以将一个普通对象转换成一个响应式对象。与reactive不同的是,shallowReactive只会将对象的第一层属性转换成响应式对象,而不会递归转换嵌套对象的属性。这个函数的作用是可以在需要响应式对象的时候避免不必要的性能开销。

例如,假设有一个包含嵌套对象的普通对象:

import { shallowReactive } from 'vue';
const person = {name: 'Alice',age: 30,address: {city: 'Beijing',country: 'China'}
};
const shallowPerson = shallowReactive(person);
console.log(shallowPerson.address); // {city: "Beijing", country: "China"}
console.log(shallowPerson.address.city); // "Beijing"

在上面的例子中,shallowPerson是一个响应式对象,但是address属性并没有被递归地转换成响应式对象。

  • shallowRef函数可以将一个普通的值转换成一个ref对象。与ref不同的是,shallowRef只会将值转换成一个ref对象,而不会递归地将值的属性转换成响应式对象。

例如:

import { shallowRef } from 'vue';
const person = {name: 'Alice',age: 30
};
const shallowPersonRef = shallowRef(person);
console.log(shallowPersonRef.value); // {name: "Alice", age: 30}
console.log(shallowPersonRef.value.name); // "Alice"

在上面的例子中,shallowPersonRef是一个ref对象,但是它的值person并没有被递归地转换成响应式对象。

7.3 readonly与shallowReadonly

readonlyshallowReadonlyVue3中的两个新函数,用于创建只读对象。

  • readonly函数可以将一个普通对象转换成一个只读对象。与reactive不同的是,readonly会将对象的所有属性都转换成只读属性,这样就不能修改对象的属性值了。

例如,假设有一个包含可修改属性的对象:

import { readonly } from 'vue';
const person = {name: 'Alice',age: 30
};
const readonlyPerson = readonly(person);
readonlyPerson.name = 'Bob'; // Error: Cannot assign to read only property 'name' of object '#<Object>'

在上面的例子中,readonlyPerson是一个只读对象,尝试修改它的属性值会抛出错误。

  • shallowReadonly函数可以将一个普通对象转换成一个浅层只读对象。与shallowReactive类似,shallowReadonly只会将对象的第一层属性转换成只读属性,而不会递归转换嵌套对象的属性。

例如:

import { shallowReadonly } from 'vue';
const person = {name: 'Alice',age: 30,address: {city: 'Beijing',country: 'China'}
};
const shallowReadonlyPerson = shallowReadonly(person);
console.log(shallowReadonlyPerson.address); // {city: "Beijing", country: "China"}
shallowReadonlyPerson.address.city = 'Shanghai'; // OK
shallowReadonlyPerson.address = {city: 'Shanghai', country: 'China'}; // Error: Cannot assign to read only property 'address' of object '#<Object>'

在上面的例子中,shallowReadonlyPerson是一个只读对象,但是address属性的属性值是可以修改的,因为它并没有被转换成只读对象。但是尝试修改shallowReadonlyPersonaddress属性本身的值会抛出错误。

7.4 toRaw与markRaw

toRawmarkRawVue3中的两个函数,用于在响应式对象和普通对象之间转换,并标记一个对象为“原始”对象,即不会被转换成响应式对象。

  • toRaw函数可以将一个响应式对象转换成一个普通对象,返回的对象是这个响应式对象的原始对象,不再是响应式对象。

例如:

import { reactive, toRaw } from 'vue';
const person = reactive({name: 'Alice',age: 30
});
const rawPerson = toRaw(person);
console.log(rawPerson === person); // false
console.log(rawPerson.name); // 'Alice'
console.log(rawPerson.age); // 30

在上面的例子中,rawPerson是一个普通对象,它的值与person相同,但是它已经不是响应式对象了。

  • markRaw函数可以标记一个对象为“原始”对象,这个对象不会被转换成响应式对象。

例如:

import { reactive, markRaw } from 'vue';
const person = reactive({name: 'Alice',age: 30,address: markRaw({city: 'Beijing',country: 'China'})
});
console.log(person.address.city); // 'Beijing'
person.address.city = 'Shanghai'; // OK
person.address = {city: 'Shanghai', country: 'China'}; // OK
person.address.city = 'Beijing'; // OK

在上面的例子中,address属性被标记为原始对象,所以它不会被转换成响应式对象。在这个例子中,我们可以修改person.address.city的值,也可以修改person.address的值,但是这个对象本身不会成为响应式对象,也就不会触发视图的更新。

7.5 customRef

customRefVue3新增的一个函数,用于创建自定义的ref。它的用法类似于ref函数,但是可以自定义ref的读写行为。

customRef接受一个工厂函数作为参数,这个函数必须返回一个对象,这个对象包含getset两个函数。get函数用于获取ref的值,set函数用于设置ref的值。这两个函数可以是同步的也可以是异步的,只要它们返回的值是正确的就可以。

例如,下面的例子是一个使用customRef实现的计数器,每次设置值时会在控制台输出一条消息:

import { customRef } from 'vue';
function createCounter() {let count = 0;return customRef((track, trigger) => ({get() {track();return count;},set(value) {count = value;console.log(`count is now ${count}`);trigger();}}));
}
const counter = createCounter();
console.log(counter.value); // 0
counter.value = 1; // count is now 1
console.log(counter.value); // 1

在上面的例子中,createCounter函数返回一个使用customRef创建的ref对象,它的读写行为由传递给customRef的函数决定。在这个例子中,我们定义了一个计数器,每次设置值时会在控制台输出一条消息,并且会调用trigger函数来通知Vue更新视图。

需要注意的是,customRef返回的是一个包含value属性的对象,与ref不同,它不直接返回值。所以在使用customRef时需要使用counter.value来获取和设置值。

customRef还有一个可选的第二个参数,它是一个布尔值,用于指示是否需要进行深度代理。默认值为false,表示不进行深度代理。如果将这个值设置为true,则可以对复杂的对象进行深度代理。

例如:

import { customRef } from 'vue';
function createDeepRef(obj) {return customRef((track, trigger) => ({get() {track();return obj;},set(value) {obj = value;trigger();}}), true);
}
const obj = { name: 'John', age: 20 };
const deepRef = createDeepRef(obj);
console.log(deepRef.value); // { name: 'John', age: 20 }
deepRef.value.name = 'Mike';
console.log(deepRef.value); // { name: 'Mike', age: 20 }

在上面的例子中,使用customRef创建了一个深度代理的ref对象,可以对其内部的对象进行修改。注意,修改内部的对象不会触发trigger函数,因为Vue无法监测到对象内部的属性的变化。

7.6 provide与inject

provideinjectVue3新增的两个API,用于在父子组件之间传递数据,相比于props和事件,它们可以更方便地在组件树中的任意层次之间进行数据传递,而不需要一级一级地手动传递。

provide函数用于在父组件中提供数据,接受一个keyvalue作为参数,key必须是一个Symbol类型的值,用于标识这个提供的数据,value可以是任何类型的值,可以是基本类型、对象、函数等等。

例如:

import { provide } from 'vue';
export default {setup() {provide(Symbol('message'), 'Hello, World!');// or provide('message', 'Hello, World!'); // It is recommended to use a Symbol as the key to avoid naming conflicts}
}

在上面的例子中,我们使用provide函数在父组件中提供了一个值为Hello, World!的数据,key是一个Symbol类型的值。

inject函数用于在子组件中注入数据,接受一个key作为参数,这个key必须是在父组件中通过provide函数提供的key。如果父组件中没有提供这个key,则inject函数会返回undefined

例如:

import { inject } from 'vue';
export default {setup() {const message = inject(Symbol('message'));// or const message = inject('message');console.log(message); // 'Hello, World!'}
}

在上面的例子中,我们使用inject函数在子组件中注入了父组件提供的值为Hello, World!的数据,key是一个Symbol类型的值。

需要注意的是,provideinject并不是响应式的,它们只是提供了一个方便的方式在组件之间传递数据。如果需要在子组件中监听父组件提供的数据变化,可以使用响应式数据来实现。

7.7 Teleport

TeleportVue3中新增的组件,它可以让你在组件树中的任何位置渲染元素,而不需要受到父组件的限制。这个组件通常用于实现弹出层或者模态框等组件,可以将它的内容渲染到指定的DOM节点上。

Teleport 的用法类似于普通的组件,只需要将需要渲染的内容放在 <Teleport> 标签中即可。其中,to 属性用于指定要渲染到的目标节点,可以是DOM节点或者Vue组件。例如:

<template><button @click="showModal = true">Show Modal</button><Teleport to="#modal"><div v-if="showModal" class="modal"><h2>Hello World</h2><button @click="showModal = false">Close</button></div></Teleport>
</template>
<script>
import { ref } from 'vue';
export default {setup() {const showModal = ref(false);return {showModal,};},
};
</script>

在上面的例子中,我们使用 Teleport 组件将弹出层渲染到页面上的一个 DOM 节点上,而不是作为父组件的子元素。具体来说,我们给 <Teleport> 标签添加了一个 to 属性,值为 #modal,这个值对应着一个 id 为 modal 的 DOM 节点。然后在 <Teleport> 中我们渲染了一个包含弹出层内容的 div 元素。当用户点击 "Show Modal" 按钮时,showModal 变量会被设置为 true,这个弹出层就会被渲染到 #modal 节点上。

需要注意的是,Teleport 组件在渲染时并不会改变组件的父子关系,所以在渲染时并不会受到父组件的CSS样式的影响,这样可以更灵活地控制样式。此外,由于 Teleport 是异步渲染的,所以它的内容可能不会立即出现,需要等待 Vue完成异步操作后才能正确地渲染。

7.8 Suspense

SuspenseVue3中的一个新特性,它可以让你在异步加载组件时,显示一些占位符或者loading状态,直到组件加载完成后再显示真正的内容。类似于React中的 Suspense 组件。

Suspense可以用于异步加载组件或者异步加载数据,当组件或者数据还没有准备好时,可以显示一个loading状态或者占位符,等待异步操作完成后再显示真正的内容。这个特性在提高用户体验方面非常有用,可以让用户感觉应用更加流畅。

Vue3中,Suspense组件通常和async setup()函数一起使用,可以将异步操作放在async setup()函数中,等待异步操作完成后再显示真正的组件内容。下面是一个例子:

<template><Suspense><template #default><HelloWorld /></template><template #fallback><div>Loading...</div></template></Suspense>
</template>
<script>
import { defineAsyncComponent } from 'vue';const HelloWorld = defineAsyncComponent(() =>import('./components/HelloWorld.vue')
);
export default {components: {HelloWorld,},
};
</script>

在上面的例子中,我们使用了 Suspense 组件包裹了一个异步加载的 HelloWorld 组件。当 HelloWorld 组件还没有加载完成时,会显示一个loading状态,等到 HelloWorld 组件加载完成后再显示真正的内容。在 Suspense 中,我们可以使用 defaultfallback 两个插槽来分别指定真正的组件内容和loading状态或者占位符。

需要注意的是,Suspense 组件需要配合异步加载的组件或者数据一起使用才能发挥作用,如果没有异步加载的需求,使用 Suspense 可能会带来额外的开销。此外,在使用 Suspense 组件时,需要确保被包裹的异步组件能够正常返回一个组件,否则会导致显示异常。

相关文章:

Vue进阶(一篇进入Vue3的世界)

文章目录一、初识Vue3二、Vue3新语法糖setup三、响应式数据函数3.1 ref函数3.2 reactive函数3.3 ref和reactive函数的异同四、Vue3的响应式原理五、语法更新5.1 Vue3使用computed计算属性5.2 Vue3使用watch监视属性的注意点5.2.1 监视ref对象5.2.2 监视reactive对象5.2.3 监视嵌…...

功能测试的分类,分别有什么作用?

目录 前言 一、链接测试 二、表单测试 三、搜索测试 四、删除测试 五、cookies/session测试 六、数据库测试 七、峰值测试/容量测试 八、相容性测试/安全测试 前言 功能测试主要包括链接测试、表单测试、搜索测试、删除测试、cookies、session测试、数据库测试等部分…...

51单片机学习笔记_14 红外遥控

红外传感器 遥控器通过红外 LED 发送调制后的信号&#xff0c;开发板上的红外接收模块接收遥控器的红外线。 单工异步&#xff0c;940nm 波长&#xff08;还有一种 250nm 的N&#xff0c;可见光&#xff09;&#xff0c;EC 通信标准。 38KHz&#xff1a;红外线频率。 IN&…...

【我是土堆 - Pytorch教程】 知识点 学习总结笔记(五)

此文章为【我是土堆 - Pytorch教程】 知识点 学习总结笔记&#xff08;五&#xff09;包括&#xff1a;完整的模型训练套路&#xff08;一&#xff09;、完整的模型训练套路&#xff08;二&#xff09;、完整的模型训练套路&#xff08;三&#xff09;、利用GPU训练&#xff08…...

JUC篇:CopyOnWriteArrayList的应用与原理

系列文章目录 JUC篇&#xff1a;volatile可见性的实现原理 JUC篇&#xff1a;synchronized的应用和实现原理 JUC篇&#xff1a;用Java实现一个简单的线程池 JUC篇&#xff1a;java中的线程池 JUC篇&#xff1a;ThreadLocal的应用与原理 JUC篇&#xff1a;Java中的并发工具类 文…...

【总结】爬虫1-requests

爬虫1-requests 1. requests的基本用法 requests需要提前导入&#xff0c;才能使用 1.1 请求网络数据&#xff1a;requests.get(请求地址) response requests.get(https://cd.zu.ke.com/zufang)1.2 设置解码方法&#xff08;罗马的是需要设置 - 一定要在获取请求结果之前设…...

基于springboot实现学生综合成绩测评系统【源码】分享

基于springboot实现学生综合成绩测评系统演示开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包…...

uniapp初体验———uView组件库的使用与钉钉小程序的运行

这周学长给了我一个校企合作的项目&#xff0c;要求是用uniapp开发&#xff0c;最终打包成钉钉小程序&#xff0c;不过我并不会uniapp&#xff0c;也是学了一段时间&#xff0c;开始写项目&#xff0c;中间也遇到过很多问题&#xff0c;比如开发者工具还有如何运行到开发者工具…...

初始Go语言2【标识符与关键字,操作符与表达式,变量、常量、字面量,变量作用域,注释与godoc】

文章目录Go语言基础语法标识符与关键字操作符与表达式变量、常量、字面量变量类型变量声明变量初始化常量字面量变量作用域注释与godoc注释的形式注释的位置go docgodocGo语言基础语法 标识符与关键字 go变量、常量、自定义类型、包、函数的命名方式必须遵循以下规则&#xff…...

Vue计算属性详解

目录 ​编辑 1、什么是计算属性 2、为什么要有计算属性 1. 为什么不是使用模板语法 2. 为什么不是使用method对于复杂逻辑 3. 什么时候要用计算属性 4. 定义计算属性fullName 5. 计算属性的配置项 1、什么是计算属性 写在computed对象中的属性&#xff0c;本质上是…...

rk3568-AD按键驱动调试

rk3568-AD按键驱动调试转载请备注&#xff1a;daisy.skye的博客_CSDN博客-Qt,嵌入式,Linux领域博主dts设备树节点 /rk356x_linux_220118/kernel/arch/arm64/boot/dts/rockchip/rk3568.dtsi 板级设备树dts /home/scooper/jkD7/20221221/ido_evb3568_v2_android11_sdk/kernel/…...

Docker三剑客之swarm

一、什么是docker swarm Swarm是Docker公司推出的用来管理docker集群的平台&#xff0c;几乎全部用GO语言来完成的开发的&#xff0c;代码开源在https://github.com/docker/swarm&#xff0c; 它是将一群Docker宿主机变成一个单一的虚拟主机&#xff0c;Swarm使用标准的Docker…...

Lucene Solr Elasticsearch三者之间的关系,怎么选?

Lucene简介&#xff1a; Lucene主要用于构建文本搜索应用程序&#xff0c;包括Web搜索引擎、桌面搜索工具和商业应用程序。它提供了诸如单词分析、查询解析、搜索结果排序等功能&#xff0c;可以轻松地在大量文档中快速搜索和查找相关信息。 Lucene具有以下特点&#xff1a; …...

为你的网站加上Loading等待加载效果吧 | Loading页面加载添加教程

为你的网站加上Loading等待加载效果吧 | Loading页面加载添加教程 效果图 : 教程开始 新建一个loading样式css 将以下代码放进去 然后引用这个文件 code #Loadanimation{ background-color:#fff; height:100%; width:100%; position:fixed; z-index:1; ma…...

Redis安装和配置

网上有海量的Redis文章&#xff0c;写的都很详细。这里就是简单记录一下自己查aof问题过程中遇到的问题&#xff0c;主要是aof文件所在目录在redis.conf里的位置 1。在ubuntu16上安装Redis sudo apt-get install -y redis-server 2。修改redis配置 sudo vim /etc/redis/re…...

MobTech|如何使用秒验

什么是秒验&#xff1f; 秒验是MobTech公司提供的一款实现一键验证功能的产品&#xff0c;从根源上降低企业验证成本&#xff0c;有效提高拉新转化率&#xff0c;降低因验证带来的流失率&#xff0c;3秒完成手机号验证&#xff08;一键登录&#xff09;。 秒验主要整合了三大…...

CSS实现自动分页打印同时每页保留重复的自定义内容

当需要打印的内容过长时系统会产生自动分割页面&#xff0c;造成样式不太美观。使用CSS的 media print 结合 <table> 可以实现对分页样式的可控。效果如下&#xff1a; 假设有50条数据&#xff0c;打印时系统会自动分成两页&#xff0c;同时每页保留自定义的header和foo…...

基于prometheus的监控告警怎么实现?

基于 Prometheus 的监控告警实现一般需要以下几个步骤&#xff1a; 安装和配置 Prometheus&#xff1a;安装 Prometheus 并配置好需要监控的目标。可以使用 Prometheus 的配置文件&#xff08;prometheus.yml&#xff09;来指定需要监控的目标&#xff0c;例如服务、主机、容器…...

2007年4月全国计算机等级考试二级JAVA笔试试题及答案

2007年4月全国计算机等级考试二级JAVA笔试试题及答案 一、选择题 &#xff08;1&#xff09;已知一棵二叉树前序遍历和中序遍历分别为ABDEGCFH和DBGEACHF&#xff0c;则该二叉树的后序遍历为 A&#xff0e;GEDHFBCA B&#xff0e;DGEBHFCA C&#xff0e;ABCDEFGH D&#xff0e;…...

灌水玩玩 ChatGPT AIGC生成的有栈协同程序实现(例子)

CO&#xff1a; 你是一名 C/C 高级软件工程师。 请使用 stackful 协程&#xff0c;实现一个 Sleep 随眠的协同程序&#xff0c;注意并非 stackless 协程&#xff0c;不允许使用 C/C 17 以上的语言标准实现&#xff0c;允许使用 boost 基础框架类库。 ChatGPT&#xff1a; 好的…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...

针对药品仓库的效期管理问题,如何利用WMS系统“破局”

案例&#xff1a; 某医药分销企业&#xff0c;主要经营各类药品的批发与零售。由于药品的特殊性&#xff0c;效期管理至关重要&#xff0c;但该企业一直面临效期问题的困扰。在未使用WMS系统之前&#xff0c;其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...

AD学习(3)

1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分&#xff1a; &#xff08;1&#xff09;PCB焊盘&#xff1a;表层的铜 &#xff0c;top层的铜 &#xff08;2&#xff09;管脚序号&#xff1a;用来关联原理图中的管脚的序号&#xff0c;原理图的序号需要和PCB封装一一…...

Selenium 查找页面元素的方式

Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素&#xff0c;以下是主要的定位方式&#xff1a; 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...

今日行情明日机会——20250609

上证指数放量上涨&#xff0c;接近3400点&#xff0c;个股涨多跌少。 深证放量上涨&#xff0c;但有个小上影线&#xff0c;相对上证走势更弱。 2025年6月9日涨停股主要行业方向分析&#xff08;基于最新图片数据&#xff09; 1. 医药&#xff08;11家涨停&#xff09; 代表标…...