Vue3
1 新建项目
1.1 vue-cli创建
vue -V 查看vue版本,必须高于4.5.0
启动测试
cd vue3_test
npm run serve
运行成功
1.2 vite创建
命令
1.3 分析工程结构
main.js
//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'
/* //创建应用实例对象--app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)
//挂载
app.mount('#app') */
createApp(App).mount('#app')
App.vue
2 常用的Composition API
2.1 setup
App.vue
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="sayHello">说话</button>
</template>
<script>
export default {
name: 'App',
//此处只是测试一下setup,暂时不考虑响应式的问题
setup() {
let name = '张三'
let age = 20
//方法
function sayHello() {
alert(`我叫${name},我年龄是${age}`)
}
//返回一个对象(常用)
return {
name,
age,
sayHello
}
}
}
</script>
2.2 ref处理基本类型
修改基本类型
App.vue
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import {ref} from 'vue'
export default {
name: 'App',
setup() {
let name = ref('张三')
let age = ref(18)
//方法
function changeInfo() {
name.value = '李四'
age.value = 48
}
//返回一个对象(常用)
return {
name,
age,
changeInfo
}
}
}
</script>
测试原理
修改对象类型
App.vue
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h3>工作种类:{{job.type}}</h3>
<h3>薪水:{{job.salary}}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import {ref} from 'vue'
export default {
name: 'App',
setup() {
let name = ref('张三')
let age = ref(18)
let job = ref({
type: '前端工程师',
salary: '30k'
})
//方法
function changeInfo() {
name.value = '李四'
age.value = 48,
console.log(job.value)
job.value.type = '后端工程师',
job.value.salary = '60k'
}
//返回一个对象(常用)
return {
name,
age,
job,
changeInfo
}
}
}
</script>
2.3 reactive
App.vue
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h3>工作种类:{{job.type}}</h3>
<h3>薪水:{{job.salary}}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import {ref,reactive} from 'vue'
export default {
name: 'App',
setup() {
let name = ref('张三')
let age = ref(18)
let job = reactive({
type: '前端工程师',
salary: '30k'
})
//方法
function changeInfo() {
name.value = '李四'
age.value = 48,
console.log(job)
job.type = '后端工程师',
job.salary = '60k'
}
//返回一个对象(常用)
return {
name,
age,
job,
changeInfo
}
}
}
</script>
数组类型
App.vue
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h3>工作种类:{{job.type}}</h3>
<h3>薪水:{{job.salary}}</h3>
<h3>爱好:{{hobby}}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import {ref,reactive} from 'vue'
export default {
name: 'App',
setup() {
let name = ref('张三')
let age = ref(18)
let job = reactive({
type: '前端工程师',
salary: '30k'
})
let hobby = reactive(['抽烟','喝酒','打游戏'])
//方法
function changeInfo() {
name.value = '李四'
age.value = 48,
console.log(job)
job.type = '后端工程师',
job.salary = '60k',
hobby[0] = '学习'
}
//返回一个对象(常用)
return {
name,
age,
job,
changeInfo,
hobby
}
}
}
</script>
全部用reactive实现
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<h3>工作种类:{{person.job.type}}</h3>
<h3>薪水:{{person.job.salary}}</h3>
<h3>爱好:{{person.hobby}}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import {reactive} from 'vue'
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: 18,
job: {
type: '前端工程师',
salary: '30k'
},
hobby: ['吃饭','睡觉']
})
//方法
function changeInfo() {
person.name = '李四'
person.age = 48,
person.job.type = '后端工程师',
person.job.salary = '60k',
person.hobby[0] = '学习'
}
//返回一个对象(常用)
return {
person,
changeInfo
}
}
}
</script>
2.4 vue3的响应式原理
vue2的响应式
vue3的响应式
利用reactive解决vue2中存在的问题
App.vue
<template>
<h1>一个人的信息</h1>
<h2 v-show="person.name">姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<h2 v-show="person.sex">性别:{{person.sex}}</h2>
<h3>工作种类:{{person.job.type}}</h3>
<h3>薪水:{{person.job.salary}}</h3>
<h3>爱好:{{person.hobby}}</h3>
<button @click="changeInfo">修改人的信息</button>
<button @click="addSex">添加一个sex属性</button>
<button @click="deleteName">删除一个name属性</button>
</template>
<script>
import {reactive} from 'vue'
export default {
name: 'App',
setup() {
let person = reactive({
name: '张三',
age: 18,
job: {
type: '前端工程师',
salary: '30k'
},
hobby: ['吃饭','睡觉']
})
//方法
function changeInfo() {
person.name = '李四'
person.age = 48,
person.job.type = '后端工程师',
person.job.salary = '60k',
person.hobby[0] = '学习'
}
function addSex(){
person.sex = '男'
}
function deleteName(){
delete person.name
}
//返回一个对象(常用)
return {
person,
changeInfo,
addSex,
deleteName
}
}
}
</script>
vue3实现原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
let person = {
name: 'John',
age: 30
}
//模拟Vue2中实现响应式
//#region
/* let p = {}
Object.defineProperty(p, 'name', {
configurable: true,
get() {//有人读取name时调用
return person.name;
},
set(val) {//有人修改name时调用
console.log('有人修改了name属性,我发现了,我要去更新界面!');
person.name = val;
}
});
Object.defineProperty(p, 'age', {
configurable: true,
get() {//有人读取age时调用
return person.age;
},
set(val) {//有人修改age时调用
console.log('有人修改了age属性,我发现了,我要去更新界面!');
person.age = val;
}
}); */
//#endregion
//模拟Vue3中实现响应式
//#region
const p = new Proxy(person,{
//有人读取p的某个属性时调用
get(target,propName){
console.log(`有人读取了p身上的${propName}属性`)
//propName 是一个变量,target[propName] 相当于从对象中取propName变量对应的值
return Reflect.get(target,propName)
},
//有人修改p的某个属性时调用、或给p追加某个属性时调用
set(target,propName,value){
console.log(`有人修改了p身上的${propName}属性属性,我发现了,我要去更新界面!`);
Reflect.set(target,propName,value)
},
//有人删除p的某个属性时调用
defineProperty(target,propName){
console.log(`有人删除了p身上的${propName}属性属性,我发现了,我要去更新界面!`);
return Reflect.defineProperty(target,propName)
}
})
//#endregion
let obj = {a:1,b:2}
//通过Object.defineProperty去操作
/* 这个地方会报错,所以使用try,catch */
try {
Object.defineProperty(obj, 'c', {
get(){
return 3
}
})
Object.defineProperty(obj, 'c', {
get(){
return 4
}
})
} catch (error) {
console.log(error);
}
//通过Object.defineProperty去操作
/* 代码不会报错,只会返回true和false */
const x1 = Reflect.defineProperty(obj, 'c', {
get(){
return 3
}
})
const x2 = Reflect.defineProperty(obj, 'c', {
get(){
return 4
}
})
if(x2){
console.log('某某某操作成功了!');
}else{
console.log('某某某操作失败了!');
}
</script>
</body>
</html>
2.5 reactive对比ref
2.6 setup的两个注意点
App.vue
<template>
<Demo></Demo>
</template>
<script>
import Demo from './components/Demo'
export default {
name: 'App',
components: {
Demo
}
}
</script>
Demo.vue
<template>
<h1>一个人的信息</h1>
<h2 v-show="person.name">姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
</template>
<script>
import {reactive} from 'vue'
export default {
name: 'App',
beforeCreate() {
console.log('---beforeCreate---');
},
setup() {
console.log('---setup---',this)
let person = reactive({
name: '张三',
age: 18
})
//返回一个对象(常用)
return {
person
}
}
}
</script>
运行结果
App.vue
<template>
<Demo @hello="showHelloMsg" msg="你好啊" school="sgg">
<template v-slot:qwe >
<span>sgg</span>
</template>
<template v-slot:ase >
<span>sgg</span>
</template>
</Demo>
</template>
<script>
import Demo from './components/Demo'
export default {
name: 'App',
components: {
Demo
},
setup() {
function showHelloMsg(value) {
alert(`你好啊,你触发了Hello事件,我收到了参数是:${value}!`)
}
return {
showHelloMsg
}
}
}
</script>
Demo.vue
<template>
<h1>一个人的信息</h1>
<h2 v-show="person.name">姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<button @click="test">测试触发一下Demo组件的Hello</button>
</template>
<script>
import {reactive} from 'vue'
export default {
name: 'App',
props: ['msg','school'],
emits:['hello'],
setup(props,context) {
// console.log('---setup---',props)
// console.log('---setup---',context)
// console.log('---setup---',context.attrs) //相当于Vue2中的$attrs(父组件向子组件中传值,如果子组件不用props接收,就会放到attrs中,类似于捡漏)
// console.log('---setup---',context.emit)//触发自定义事件的
console.log('---setup---',context.slots) //插槽
//数据
let person = reactive({
name: '张三',
age: 18
})
//方法
function test() {
context.emit('hello',666)
}
//返回一个对象(常用)
return {
person,
test
}
}
}
</script>
2.7 计算属性与监视
computed函数
App.vue
<template>
<Demo></Demo>
</template>
<script>
import Demo from './components/Demo'
export default {
name: 'App',
components: {
Demo
},
}
</script>
Demo.vue
<template>
<h1>一个人的信息</h1>
姓 : <input type="text" v-model="person.firstName">
<br>
名 : <input type="text" v-model="person.lastName">
<br>
全名: <input type="text" v-model="person.fullName">
</template>
<script>
import {reactive,computed} from 'vue'
export default {
name: 'App',
setup() {
//数据
let person = reactive({
firstName: '张',
lastName: '三'
})
//计算属性--简写(没有考虑计算属性被修改的情况)
/* person.fullName = computed(() => {
return person.firstName + '-' +person.lastName
}) */
//计算属性--完整写法(考虑读和写)
person.fullName = computed({
get() {
return person.firstName + '-' +person.lastName
},
set(val) {
let arr = val.split('-')
person.firstName = arr[0]
person.lastName = arr[1]
}
})
//返回一个对象(常用)
return {
person
}
}
}
</script>
watch函数
App.vue
<template>
<Demo></Demo>
</template>
<script>
import Demo from './components/Demo'
export default {
name: 'App',
components: {
Demo
},
}
</script>
Demo.vue
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">点我+1</button>
<hr>
<h2>{{msg}}</h2>
<button @click="msg += '!'">修改信息</button>
<hr>
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<h2>薪资:{{person.job.j1.salary}}</h2>
<button @click="person.name += '~'">修改姓名</button>
<button @click="person.age++">增长年龄</button>
<button @click="person.job.j1.salary++">涨薪</button>
</template>
<script>
import {ref,reactive,watch} from 'vue'
export default {
name: 'App',
setup() {
//数据
let sum = ref(0)
let msg = ref('你好')
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
//情况一:监视ref所定义的一个响应式数据
/* watch(sum,(newValue,oldValue)=>{
console.log('sum变了',newValue,oldValue)
},{immediate:true}) */
//情况二:监视ref所定义的多个响应式数据
/* watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或者msg变了',newValue,oldValue)
},{immediate:true}) *//* immediate :一上来先执行一次 */
/*
情况三:监视reactive所定义的一个响应式数据的全部属性
注意1:此处无法正确的获取oldValue
注意2:强制开启了深度监视(deep配置无效) ---我测试了一下,好像deep=false的情况下,可以监视不到修改了
*/
/* watch(person,(newValue,oldValue)=>{
console.log('person变了',newValue,oldValue)
},{deep:false}) //此处的deep配置无效 */
//情况四:监视reactive所定义的一个响应式数据的某个属性
/* watch(() => person.name,(newValue,oldValue)=>{
console.log('person.name变了',newValue,oldValue)
}) */
//情况五:监视reactive所定义的一个响应式数据的某些属性
/* watch([() => person.name,() => person.age],(newValue,oldValue)=>{
console.log('person的name或者age变了',newValue,oldValue)
}) */
//特殊情况
watch(() => person.job,(newValue,oldValue)=>{
console.log('person.job变了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive所定义的对象中的某个属性(属性的值是一个对象),所以deep配置有效
//返回一个对象(常用)
return {
sum,
msg,
person
}
}
}
</script>
情况二:监视ref所定义的多个响应式数据
情况三出现的问题
watchEffect函数
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">点我+1</button>
<hr>
<h2>{{msg}}</h2>
<button @click="msg += '!'">修改信息</button>
<hr>
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<h2>薪资:{{person.job.j1.salary}}</h2>
<button @click="person.name += '~'">修改姓名</button>
<button @click="person.age++">增长年龄</button>
<button @click="person.job.j1.salary++">涨薪</button>
</template>
<script>
import {ref,reactive,watch,watchEffect} from 'vue'
export default {
name: 'App',
setup() {
//数据
let sum = ref(0)
let msg = ref('你好')
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
//监视
/* watch(sum,(newValue,oldValue) => {
console.log('sum的值变化了',newValue,oldValue)
},{immediate: true}) */
watchEffect(() => {
const x1 = sum.value
const x2 = person.job.j1.salary
console.log('watchEffect所指定的回调执行了')
})
//返回一个对象(常用)
return {
sum,
msg,
person
}
}
}
</script>
2.8 生命周期
App.vue
<template>
<button @click="isShowDemo = !isShowDemo">切换隐藏/显示</button>
<Demo v-if="isShowDemo"></Demo>
</template>
<script>
import {ref} from 'vue'
import Demo from './components/Demo'
export default {
name: 'App',
components: {
Demo
},
setup() {
let isShowDemo = ref(true)
return {
isShowDemo
}
},
}
</script>
Demo.vue
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">点我+1</button>
</template>
<script>
import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
export default {
name: 'App',
setup() {
//数据
let sum = ref(0)
//通过组合式API的形式去使用生命周期钩子
onBeforeMount(() => {
console.log('------onBeforeMount----')
}),
onMounted(() => {
console.log('------onMounted----')
})
onBeforeUpdate(() => {
console.log('------onBeforeUpdate----')
})
onUpdated(() => {
console.log('------onUpdated----')
})
onBeforeUnmount(() => {
console.log('------onBeforeUnmount----')
})
onUnmounted(() => {
console.log('------onUnmounted----')
})
//返回一个对象(常用)
return {
sum
}
},
//通过配置项的形式使用生命周期钩子
//#region
/* beforeCreate() {
console.log('----beforeCreate----')
},
created() {
console.log('----created----')
},
beforeMount() {
console.log('----beforeMount----')
},
mounted() {
console.log('----mounted----')
},
beforeUpdate() {
console.log('----beforeUpdate----')
},
updated() {
console.log('----updated----')
},
beforeUnmount() {
console.log('----beforeUnmount----')
},
unmounted() {
console.log('----unmounted----')
} */
//#endregion
}
</script>
2.9 自定义hook函数
App.vue
<template>
<Demo></Demo>
</template>
<script>
import {ref} from 'vue'
import Demo from './components/Demo'
export default {
name: 'App',
components: {
Demo
}
}
</script>
usePoint.js
类似于java中封装了一个函数
import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function (){
//实现鼠标“打点”相关的数据
let point = reactive({
x: 0,
y: 0
})
//实现鼠标“打点”相关的方法
function savePoint(event) {
point.x = event.pageX
point.y = event.pageY
}
//实现鼠标“打点”相关的生命周期钩子
onMounted(() => {
window.addEventListener('click', savePoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click', savePoint)
})
return point
}
Demo.vue
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">点我+1</button>
<hr>
<h2>当前点击时鼠标的坐标为: x : {{point.x}} , y: {{point.y}}</h2>
</template>
<script>
import {ref} from 'vue'
import userPoint from '../hooks/usePoint'
export default {
name: 'App',
setup() {
//数据
let sum = ref(0)
let point = userPoint()
//返回一个对象(常用)
return {
sum,
point
}
}
}
</script>
2.10 toRef
App.vue
<template>
<Demo></Demo>
</template>
<script>
import {ref} from 'vue'
import Demo from './components/Demo'
export default {
name: 'App',
components: {
Demo
}
}
</script>
Demo.vue
<template>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}</h2>
<button @click="name += '~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import {reactive,toRef,toRefs} from 'vue'
export default {
name: 'App',
setup() {
//数据
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
//返回一个对象(常用)
return {
//toRef的作用是指向person中属性的引用地址,类似于浅拷贝
/* name:toRef(person, 'name'),
age:toRef(person, 'age'),
salary:toRef(person.job.j1, 'salary') */
/* 使用toRefs进行整体返回,但是只能暴露第一层的属性 */
...toRefs(person)
}
}
}
</script>
3 其他的Composition API
3.1 shallowReactive和shallowRef
Demo.vue
<template>
<h4>当前的x值是: {{x.p}}</h4>
<button @click="x.p++">点我x+1</button>
<hr>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}</h2>
<button @click="name += '~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import {reactive,toRef,toRefs,shallowReactive,shallowRef} from 'vue'
export default {
name: 'App',
setup() {
//数据 shallow:浅层次的
// let person = shallowReactive({ //只考虑第一层数据的响应式
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
// let x = shallowRef(0) 如果是基本数据类型,shallowRef和ref没有区别
let x = shallowRef({ //如果是对象类型,shallowRef的value仍然是object,不是proxy对象,所以无法进行响应式
p: 0
})
//返回一个对象(常用)
return {
/* 使用toRefs进行整体返回,但是只能暴露第一层的属性 */
...toRefs(person),
x
}
}
}
</script>
3.2 readonly与shallowReadonly
Demo.vue
<template>
<h4>当前求和为:{{sum}}</h4>
<button @click="sum++">点我x+1</button>
<hr>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}</h2>
<button @click="name += '~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import {reactive,toRefs,ref,readonly,shallowReadonly} from 'vue'
export default {
name: 'App',
setup() {
//数据
let sum = ref(0)
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
/* 数据不能修改,页面也不会刷新,作用场景例子:使用别人给的数据,不能修改 */
person = readonly(person) //不允许所有值修改
//person = shallowReadonly(person) //不允许第一层数据修改
sum = readonly(sum) //不允许值修改
//sum = shallowReadonly(sum) //不允许第一层数据修改
//返回一个对象(常用)
return {
/* 使用toRefs进行整体返回,但是只能暴露第一层的属性 */
...toRefs(person),
sum
}
}
}
</script>
3.3 toRaw和markRaw
Demo.vue
<template>
<h4>当前求和为:{{sum}}</h4>
<button @click="sum++">点我x+1</button>
<hr>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}</h2>
<h2>车信息:{{person.car}}</h2>
<button @click="name += '~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
<button @click="showRawPerson">输出最原始的person</button>
<button @click="addCar">给人添加一台车</button>
<button @click="person.car.name += '!'">车换名</button>
<button @click="changePrice ">车增值</button>
</template>
<script>
import {reactive,toRefs,ref,toRaw,markRaw} from 'vue'
export default {
name: 'App',
setup() {
//数据
let sum = ref(0)
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
function showRawPerson() {
let p = toRaw(person)
console.log(p)
}
function addCar() {
let car = {name:'宝马',price: 40}
person.car = markRaw(car)
}
function changePrice() {
person.car.price++
console.log(person.car.price)
}
//返回一个对象(常用)
return {
/* 使用toRefs进行整体返回,但是只能暴露第一层的属性 */
...toRefs(person),
sum,
person,
showRawPerson,
addCar,
changePrice
}
}
}
</script>
3.4 customRef
作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制。
实现防抖
App.vue
<template>
<input type="text" v-model="keyWord">
<h3>{{keyWord}}</h3>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name: 'App',
setup() {
//自定义一个ref---名为:myRef
function myRef(value,delay) {
let timer
return customRef((track,trigger) => {
return {
get(){
console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`);
track() //通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
return value
},
set(newValue) {
console.log(`有人把myRef这个容器中的数据改为了${newValue}`)
clearTimeout(timer)
timer = setTimeout(() => {
value = newValue
trigger() //通知Vue去重新解析模板
}, delay);
}
}
})
}
// let keyWord = ref('hello') 使用Vue提供的ref
let keyWord = myRef('hello',500) //使用程序员自定义的ref
return {
keyWord
}
}
}
</script>
3.5 provide 和 inject
App.vue
<template>
<div class="app">
<h3>我是App组件(祖), {{name}} --- {{price}}</h3>
<Child></Child>
</div>
</template>
<script>
import {reactive,toRefs,provide} from 'vue'
import Child from './components/Child.vue'
export default {
name: 'App',
components: {
Child
},
setup() {
let Car = reactive({name: '奔驰',price: '40W'})
provide('car', Car) //给自己的后代组件传递数据
return {
...toRefs(Car)
}
}
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
Child.vue
<template>
<div class="child">
<h3>我是Child组件(子)</h3>
<Grandson></Grandson>
</div>
</template>
<script>
import Grandson from './Grandson.vue'
export default {
name: 'Child',
components: {
Grandson
}
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>
Grandson.vue
<template>
<div class="grandson">
<h3>我是Grandson组件(孙), {{car.name}} -- {{car.price}}</h3>
</div>
</template>
<script>
import {inject} from 'vue'
export default {
name: 'Grandson',
setup() {
let car = inject('car')
return {
car
}
}
}
</script>
<style>
.grandson{
background-color: orange;
padding: 10px;
}
</style>
3.6 响应式数据的判断
App.vue
<template>
<div class="app">
<h3>我是App组件</h3>
</div>
</template>
<script>
import {ref,reactive,toRefs,readonly,isRef,isReactive,isReadonly,isProxy} from 'vue'
export default {
name: 'App',
setup() {
let car = reactive({name: '奔驰',price: '40W'})
let sum = ref(0)
let car2 = readonly(car)
console.log(isRef(sum))
console.log(isReactive(car))
console.log(isReadonly(car2))
console.log(isProxy(car))
console.log(isProxy(car2))
return {
...toRefs(car)
}
}
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
4 Composition API的优势
5 新的组件
5.1 Fragment
5.2 Teleport
App.vue
<template>
<div class="app">
<h3>我是App组件</h3>
<Child></Child>
</div>
</template>
<script>
import Child from './components/Child.vue'
export default {
name: 'App',
components: {
Child
},
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
Child.vue
<template>
<div class="child">
<h3>我是Child组件</h3>
<Grandson></Grandson>
</div>
</template>
<script>
import Grandson from './Grandson.vue'
export default {
name: 'Child',
components: {
Grandson
}
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>
Grandson.vue
<template>
<div class="grandson">
<h3>我是Grandson组件</h3>
<Dialog></Dialog>
</div>
</template>
<script>
import {inject} from 'vue'
import Dialog from './Dialog.vue'
export default {
name: 'Grandson',
components: {
Dialog
},
}
</script>
<style>
.grandson{
background-color: orange;
padding: 10px;
}
</style>
Dialog.vue
<template lang="">
<div>
<button @click="isShow = true">点我弹窗</button>
<teleport to='body' >
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<h4>一些内容</h4>
<h4>一些内容</h4>
<h4>一些内容</h4>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name: 'Dialog',
setup() {
let isShow = ref(false)
return {
isShow,
}
}
}
</script>
<style >
.mask{
position: absolute;
top: 0; bottom: 0; left:0 ; right: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.dialog{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
text-align: center;
width: 300px;
height: 300px;
background-color: green;
}
</style>
5.3 Suspense
App.vue
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child></Child>
</template>
<template v-slot:fallback>
<h3>加载中...</h3>
</template>
</Suspense>
</div>
</template>
<script>
// import Child from './components/Child.vue' //静态引入
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(() => import('./components/Child')) //动态引入/异步引入
export default {
name: 'App',
components: {
Child
},
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
Child.vue
<template>
<div class="child">
<h3>我是Child组件</h3>
{{sum}}
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name: 'Child',
async setup() {
let sum = ref(0)
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve({sum})
}, 1000)
})
return await p
}
}
</script>
<style>
.child{
background-color: skyblue;
padding: 10px;
}
</style>