1.初识setup
<template>
<h1>一个人的信息</h1>
<h1>姓名: {{ name }}</h1>
<h1>年龄: {{ age }}</h1>
<h1>性别: {{ sex }}</h1>
<button @click="sayHello">vue3-sayHello</button>
<button @click="sayWelcome">vue2-sayWelcome</button>
<button @click="test1">vue2读取vue3中的数据和方法</button>
<button @click="test2">vue3读取vue2中的数据和方法</button>
<button @click="test">一个简单的测试</button>
</template>
<script>
// import { h } from 'vue'
export default {
name: 'App',
data() {
return {
sex: '男'
}
},
methods: {
sayWelcome() {
alert('欢迎哈哈哈~')
},
// 测试一下看能不能在vue2中调vue3中的数据和方法. 测试是可以调取的
test1() {
console.log(this.name)
console.log(this.sayHello)
this.sayHello()
}
},
// 此处只是测试一下setup,暂时不考虑响应式的问题
setup() {
// 数据
let name = '张三'
let age = 18
// 方法
function sayHello() {
alert(`我叫${name},我${age}岁了`)
}
// 在vue3中读取vue2中的数据和方法. 读不了,会报错
function test2() {
console.log(this.sex)
console.log(this.sayWelcome)
this.sayWelcome()
}
// 一个简单的测试
const test = () => {
console.log(111)
alert('我是用es6写的啊!')
}
// 返回一个对象(常用)
return {
name: name,
age: age,
sayHello: sayHello,
test2: test2,
test
}
// 返回一个函数(渲染函数)
// return () => h('h1','尚硅谷') // 页面中只显示“尚硅谷”三个字
}
}
</script>
2.ref函数
<template>
<h1>一个人的信息</h1>
<h1>姓名: {{ name }}</h1>
<h1>年龄: {{ age }}</h1>
<h1>职业: {{ job.type }}</h1>
<h1>薪水: {{ job.salary }}</h1>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
name: 'App',
setup() {
// 数据
let name = ref('张三')
let age = ref(18)
let job = ref({
type: '前端工程师',
salary: '30K'
})
let test = ref(123)
onMounted(() => {
console.log(test)
})
// 方法
function changeInfo() {
name.value = '李四'
age.value = 48
job.value.type = '后端工程师'
job.value.salary = '20K'
}
// 返回一个对象(常用)
return {
test,
name: name,
age: age,
job: job,
changeInfo: changeInfo
}
}
}
</script>
3.reactive
<template>
<h1>一个人的信息</h1>
<h1>姓名: {{ person.name }}</h1>
<h1>年龄: {{ person.age }}</h1>
<h1>职业: {{ person.job.type }}</h1>
<h1>薪水: {{ person.job.salary }}</h1>
<h1>测试的数据: {{ person.job.a.b.c }}</h1>
<h1>爱好: {{ person.hobbies }}</h1>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import { reactive } from 'vue' // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: 'App',
setup() {
// 数据
let person = reactive({
name: '张三',
age: 18,
job: {
type: '前端工程师',
salary: '30K',
a: {
b: {
c: 666
}
}
},
hobbies: ['抽烟', '喝酒', '玩游戏']
})
// 方法
function changeInfo() {
person.name = '李四'
person.age = 48
person.job.type = '后端工程师'
person.job.salary = '20K'
person.job.a.b.c = 999
person.hobbies[0] = '打飞机'
}
// 返回一个对象(常用)
return {
person: person,
changeInfo: changeInfo
}
}
}
</script>
4.vue3的响应式原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
// 源数据
let person = {
name: '张三',
age: 18
}
// vue2响应式原理
// let p = {}
// Object.defineProperty(p,'name',{
// configurable: true,
// // 读取name时调用
// get() {
// return person.name
// },
// // 修改name时调用
// set(value) {
// console.log('有人修改了name属性被我发现了,我要去更新界面')
// person.name = value
// }
// })
// Object.defineProperty(p,'age',{
// get() {
// return person.age
// },
// set(value) {
// console.log('有人修改了age属性被我发现了,我要去更新界面')
// person.age = value
// }
// })
// vue3响应式原理 Proxy是代理对象 Reflect是反射对象
/**
* 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写,属性的添加,属性的删除
* 通过Reflect(反射):对源对象的属性进行操作
*/
const p = new Proxy(person, {
// 读取
get(target, propName) {
console.log(`有人读取了p身上的${propName}属性`, target, propName) // target就是person源对象,propName是属性名
// return target[propName]
return Reflect.get(target, propName)
},
// 新增/修改
set(target, propName, value) {
console.log(`有人修改了p身上的${propName}属性,我要去更新页面了`)
// target[propName] = value
Reflect.set(target, propName, value)
},
// 删除
deleteProperty(target, propName) {
console.log(`有人删除了p身上的${propName}属性,我要去更新页面了`)
// return delete target[propName]
return Reflect.deleteProperty(target, propName)
}
})
let obj = { a: 1, b: 2 }
// 通过Object.defineProperty去操作
// try {
// Object.defineProperty(obj, 'c', {
// get() {
// return 3
// }
// })
// Object.defineProperty(obj, 'c', {
// get() {
// return 34
// }
// })
// } catch (error) {
// console.log(err)
// }
// 通过Reflect.defineProperty去操作
const x1 = Reflect.defineProperty(obj, 'c', {
get() {
return 3
}
})
console.log(x1)
const x2 = Reflect.defineProperty(obj, 'c', {
get() {
return 3
}
})
if (x2) {
console.log('某某某操作成功了!')
} else {
console.log('某某某操作失败了!')
}
// console.log('@@@')
</script>
</body>
</html>
5.setup的两个注意点
----------App.vue父组件----------
<template>
<Demo :msg="msg" school="尚硅谷" afanfan="小汤圆" @hello="showHelloMsg">
<div>哈哈哈</div>
<template v-slot:enen>
<div>嗯嗯嗯</div>
</template>
<template v-slot:heihei>
<div>嘿嘿嘿</div>
</template>
</Demo>
</template>
<script>
import { ref } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
import Demo from './components/demo.vue'
export default {
name: 'App',
components:{
Demo
},
setup() {
let msg = ref('hahah')
function showHelloMsg(value) {
alert(`你好啊,你触发了hello事件,我收到的参数是${value}`)
}
return {
msg,
showHelloMsg
}
}
}
</script>
----------demo.vue子组件-----------
<template>
<h1>一个人的信息</h1>
<h1>姓名: {{ person.name }}</h1>
<h1>年龄: {{ person.age }}</h1>
<h1>{{msg}}</h1>
<h1>{{afanfan}}</h1>
<button @click="test">测试触发一下Demo组件的Hello事件</button>
<!-- 哈哈哈 -->
<slot></slot>
<!-- 这里如果想显示父组件的东西,就要用到插槽slot -->
<!-- 嗯嗯嗯 -->
<slot name="enen"></slot>
<!-- 嘿嘿嘿 -->
<slot name="heihei"></slot>
</template>
<script>
import { reactive } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "demo",
props: ["msg", "school","afanfan"],
// emits: ["hello"],
beforeCreate() {
console.log("---beforeCreate---");
},
setup(props, context) {
console.log(context)
console.log('---setup---') // 在beforeCreate之前执行
console.log(props) // Proxy {msg: "hahah", school: "尚硅谷", afanfan: "小汤圆"}
// console.log(context.attrs) // 相当于vue2中的$attrs
// console.log(context.emit) // 触发自定义事件的
console.log(context.slots); // 插槽
// console.log(this) // undefind
// 数据
let person = reactive({
name: "张三",
age: 18,
});
// 方法
function test() {
context.emit("hello", 666);
}
// 返回一个对象(常用)
return {
person: person,
test: test,
};
},
};
</script>
6.computed计算属性
-----------App.vue----------
<template>
<Demo />
</template>
<script>
import Demo from './components/demo.vue' // ref处理基本类型数据 reactive处理引用类型数据
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 />
<span>全名:{{ person.fullName }}</span>
<br />
全名: <input type="text" v-model="person.fullName" />
</template>
<script>
import { reactive, computed } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "demo",
// vue2的计算属性写法
// computed: {
// fullName() {
// return this.person.firstName + "-" + this.person.lastName;
// },
// },
setup() {
// 数据
let person = reactive({
firstName: "张",
lastName: "三",
fullName: "",
});
// vue3的计算属性(没有考虑计算属性被修改的情况)
// person.fullName = computed(() => {
// return person.firstName + "-" + person.lastName;
// });
// 完整的写法(考虑读和写)
person.fullName = computed({
get() {
return person.firstName + "-" + person.lastName;
},
set(value) {
console.log(value); // 张df-三1123w
const newArr = value.split("-");
console.log(newArr); // ['张df','三1123w']
person.firstName = newArr[0];
person.lastName = newArr[1];
},
});
// 返回一个对象(常用)
return {
person,
};
},
};
</script>
7.watch函数
-----------App.vue------------
<template>
<Demo />
</template>
<script>
import Demo from './components/demo.vue' // ref处理基本类型数据 reactive处理引用类型数据
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 += '!'">点我加1</button>
<hr>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.j1.salary }}K</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"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "demo",
watch: {
// vue2简单写法
// sum(newVal, oldVal) {
// console.log("sum的值变了","newVal===", newVal,"oldVal===", oldVal);
// // 点击后打印 sum的值变了 newVal=== 1 oldVal=== 0
// },
// vue2完整写法
// sum: {
// immediate: true,
// deep: true,
// handler(newVal,oldVal) {
// console.log("sum的值变了","newVal===", newVal,"oldVal===", oldVal);
// // sum的值变了 newVal=== 0 oldVal=== undefined
// }
// }
},
setup() {
// 数据
let sum = ref(0);
let msg = ref("hello");
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
// let person = ref({
// name: '张三',
// age: 18
// })
// vue3监视一:ref所定义的响应式数据
// watch(sum, (newVal, oldVal) => {
// console.log("sum的值变了", "newVal===", newVal, "oldVal===", oldVal);
// });
// vue3监视二:ref所定义的多个响应式数据
// vue3里面可以调用多次watch函数,比如我既想监视sum,又想监视msg,那么可以写两个watch.但是比较麻烦
// watch(sum, (newVal, oldVal) => {
// console.log("sum的值变了", "newVal===", newVal, "oldVal===", oldVal);
// },{immediate: true,deep:true});
// watch(msg, (newVal, oldVal) => {
// console.log("msg的值变了", "newVal===", newVal, "oldVal===", oldVal);
// },{immediate: true,deep:true});
// 所以简单的写法是:
// watch([sum,msg], (newVal, oldVal) => {
// console.log("sum或msg的值变了", "newVal===", newVal, "oldVal===", oldVal);
// },{immediate: true});
// vue3监视三:reactive所定义的响应式数据的全部属性(用的多)
// 1.此处无法正确的获取oldVal(bug) 2.强制开启了深度监视(deep配置无效)
watch(person,(newVal, oldVal)=>{
console.log('person变化了',newVal,oldVal)
console.log(newVal.name)
},{deep:true}) // 此处的deep配置无效
// vue3监视四:reactive所定义的响应式数据的某一个属性
// watch(()=>person.age,(newVal, oldVal)=>{
// console.log('person的age变化了',newVal,oldVal)
// })
// vue3监视五:reactive所定义的响应式数据的某些属性
// watch([()=>person.name,()=>person.age],(newVal, oldVal)=>{
// console.log('person的name或age变化了',newVal,oldVal)
// })
// vue3监视六:特殊情况
// watch(()=>person.job,(newVal, oldVal)=>{
// console.log('person的job变化了',newVal,oldVal)
// },{deep:true}) // 此处由于监视的是reactive所定义的对象中的某个属性,所有deep配置有效
return {
sum,
msg,
person
};
},
};
</script>
8.watch监视ref数据的说明
<template>
<h2>当前求和为: {{ sum }}</h2>
<button @click="sum++">点我加1</button>
<hr />
<h2>当前的信息为: {{ msg }}</h2>
<button @click="msg += '!'">点我加1</button>
<hr />
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.j1.salary }}K</h2>
<button @click="person.name += '~'">修改姓名</button>
<button @click="person.age++">修改年龄</button>
<button @click="person.job.j1.salary++">涨薪</button>
</template>
<script>
import { ref, watch } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "demo",
setup() {
// 数据
let sum = ref(0);
let msg = ref("hello");
let person = ref({
name: "张三",
age: 18,
job: {
j1: {
salary: 20,
},
},
});
console.log(sum);
console.log(msg);
console.log(person);
// 注意:这里不能写成watch(sum.value,(a,b)=>{}),因为sum是基本类型数据,所以不能.value
watch(sum, (newVal, oldVal) => {
console.log("sum的值变了", "newVal===", newVal, "oldVal===", oldVal);
});
// 方法一
// watch(person.value, (newVal, oldVal) => {
// console.log("person的值变了", "newVal===", newVal, "oldVal===", oldVal);
// });
// 方法二
watch(person,(newVal,oldVal)=>{
console.log("person的值变了", "newVal===", newVal, "oldVal===", oldVal)
},{deep:true})
return {
sum,
msg,
person
};
},
};
</script>
9.watchEffect函数
<template>
<h2>当前求和为: {{ sum }}</h2>
<button @click="sum++">点我加1</button>
<hr />
<h2>当前的信息为: {{ msg }}</h2>
<button @click="msg += '!'">点我加1</button>
<hr />
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.j1.salary }}K</h2>
<button @click="person.name += '~'">修改姓名</button>
<button @click="person.age++">修改年龄</button>
<button @click="person.job.j1.salary++">涨薪</button>
<h1>watchEffect的值: {{ res }}</h1>
</template>
<script>
import { ref, reactive, watchEffect } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "demo",
setup() {
// 数据
let sum = ref(0);
let msg = ref("hello");
let res = ref(null)
let person = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20,
},
},
});
// 监视
// watch(sum, (newVal, oldVal) => {
// console.log("sum的值变了", "newVal===", newVal, "oldVal===", oldVal);
// });
// 0 20 watchEffct所指定的回调执行了
/**
* watch:既要指明监视的属性,也要指明监视的回调
* watchEffct: 不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
* watchEffct有点像computed:
* 1.computed注重的是计算出来的值(回调函数的返回值),所以必须要写返回值
* 2.watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
*/
watchEffect(()=>{
const x1 = sum.value
console.log(x1) // 0
const x2 = person.job.j1.salary
console.log(x2) // 20
console.log('watchEffct所指定的回调执行了')
res.value = x1 + x2 // 初始化为20 x1/x2增加 res也会跟着增加
})
return {
sum,
msg,
res,
person
};
},
};
</script>
10.生命周期
----------app.vue----------
<template>
<button @click="isShowDemo=!isShowDemo">切换隐藏/显示</button>
<Demo v-if="isShowDemo" />
</template>
<script>
import { ref } from "vue";
import Demo from "./components/demo.vue"; // ref处理基本类型数据 reactive处理引用类型数据
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"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "demo",
setup() {
console.log('---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,
};
},
// 通过配置项的形式使用生命周期钩子
// 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---");
// }
};
</script>
11.自定义hook
-----------App.vue-----------
<template>
<button @click="isShowDemo=!isShowDemo">切换隐藏/显示</button>
<Demo v-if="isShowDemo" />
</template>
<script>
import { ref } from "vue";
import Demo from "./components/demo.vue"; // ref处理基本类型数据 reactive处理引用类型数据
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>
<hr>
<h2>当前点击时鼠标的坐标为: x:{{ point.x }} y:{{ point.y }}</h2>
</template>
<script>
// import { ref, reactive, onMounted, onBeforeUnmount } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
import { ref } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
import usePoint from '../hooks/usePoint'
export default {
name: "demo",
setup() {
console.log('---setup---')
let sum = ref(0);
let point = usePoint()
// let point = reactive({
// x: 0,
// y: 0
// })
// function savePoint(event){
// console.log(event.pageX,event.pageY)
// point.x= event.pageX
// point.y= event.pageY
// }
// onMounted(()=> {
// window.addEventListener('click', savePoint)
// })
// onBeforeUnmount(()=> {
// window.removeEventListener('click', savePoint)
// })
return {
sum,
point
};
},
};
</script>
-----------/hooks/usePoint.js-----------------
import { reactive, onMounted, onBeforeUnmount } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
function savePoint() {
// 实现鼠标'打点'相关的数据
let point = reactive({
x: 0,
y: 0
})
// 实现鼠标'打点'相关的生命周期钩子
function savePoint(event) {
console.log(event.pageX, event.pageY)
point.x = event.pageX
point.y = event.pageY
}
onMounted(() => {
window.addEventListener('click', savePoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click', savePoint)
})
return point
}
export default savePoint
// hook本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2中的mixin
// Composition API: ref,reactive,onMounted,onBeforeUnmount...
// 自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂
12.toRef与toRefs
------------App.vue------------
<template>
<button @click="isShowDemo=!isShowDemo">切换隐藏/显示</button>
<Demo v-if="isShowDemo" />
</template>
<script>
import { ref } from "vue";
import Demo from "./components/demo.vue"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "App",
components: {
Demo,
},
setup() {
let isShowDemo = ref(true);
return {
isShowDemo
}
},
};
</script>
-----------demo.vue-------------
<template>
<!-- <h4>{{ person }}</h4> -->
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<!-- <h2>薪资toRef:{{ salary }}K</h2> -->
<h2>薪资toRefs:{{ job.j1.salary }}K</h2>
<button @click="name += '~'">修改姓名</button>
<button @click="age++">修改年龄</button>
<!-- <button @click="salary++">toRef涨薪</button> -->
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
// toRef:创建一个ref对象,其value值指向另一个对象中的某个属性
// toRefs:与toRef功能一致,但可以批量创建多个ref对象,语法:toRefs(person)
// import { reactive, toRef } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
import { reactive, toRefs } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "demo",
setup() {
let person = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20
}
}
})
const x = toRefs(person)
console.log(x) // {age:ObjectRefImpl{}...}
return {
// name: toRef(person, "name"),
// age: toRef(person, "age"),
// salary: toRef(person.job.j1, "salary")
// person,
...toRefs(person)
};
},
};
</script>
13.shallowReactive和shallowRef
--------------App.vue----------------
<template>
<button @click="isShowDemo=!isShowDemo">切换隐藏/显示</button>
<Demo v-if="isShowDemo" />
</template>
<script>
import { ref } from "vue";
import Demo from "./components/demo.vue"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "App",
components: {
Demo,
},
setup() {
let isShowDemo = ref(true);
return {
isShowDemo
}
},
};
</script>
--------------demo.vue---------------
<template>
<!-- <h4>{{ person }}</h4> -->
<h4>当前的x值是:{{ y.z }}</h4>
<!-- 有效果的 -->
<button @click="y={z:666}">点我替换x</button>
<!-- 没效果的 -->
<button @click="y.z++">点我替换y.z++</button>
<hr>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job.j1.salary }}K</h2>
<!-- 姓名可以修改 -->
<button @click="name += '~'">修改姓名</button>
<!-- 年龄可以修改 -->
<button @click="age++">修改年龄</button>
<!-- 薪资不能修改 -->
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
// 1.shallowReactive:如果有一个对象数据,结构数据比较深,但变化时只是最外层属性变化 ===>shallowReactive
// 2.shallowRef:如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来代替 ===> shallowRef
import { ref, reactive, toRefs, shallowReactive, shallowRef } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "demo",
setup() {
// let person = reactive({
// name: "张三",
// age: 18,
// job: {
// j1: {
// salary: 20
// }
// }
// })
// shallowReactive: 只处理对象最外层属性的响应式(浅响应式)
let person = shallowReactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20
}
}
})
let person111 = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 200
}
}
})
let x = ref(0)
// shallowRef: 只处理基本数据类型的响应式,不进行对象的响应式处理
// 我现在很明确,以后不会修改y里面的z
let y = shallowRef({
z:0
})
return {
person,
person111,
x,
y,
...toRefs(person), // 薪资改不了
// ...toRefs(person111), // 薪资改的了
};
},
};
</script>
14.readonly和shallowReadoniy
----------------App.vue----------------
<template>
<button @click="isShowDemo=!isShowDemo">切换隐藏/显示</button>
<Demo v-if="isShowDemo" />
</template>
<script>
import { ref } from "vue";
import Demo from "./components/demo.vue"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "App",
components: {
Demo,
},
setup() {
let isShowDemo = ref(true);
return {
isShowDemo
}
},
};
</script>
-------------demo.vue------------------
<template>
<h4>当前求和为:{{ sum }}</h4>
<button @click="sum++">点我++</button>
<hr>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job.j1.salary }}K</h2>
<button @click="name += '~'">修改姓名</button>
<button @click="age++">修改年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import { ref, reactive, toRefs, readonly, shallowReadonly } from "vue"; // ref处理基本类型数据 reactive处理引用类型数据
export default {
name: "demo",
setup() {
let sum = ref(0)
let person = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20
}
}
})
person = readonly(person) // person里的属性值都不能修改 ... 名字,年龄,涨薪 都点击不了
// person = shallowReadonly(person) // person第一层属性不能修改,深层次的可以改。也就是说,名字和年龄不能改,涨薪可以改
sum = shallowReadonly(sum) // sum++执行不了
return {
sum,
...toRefs(person)
};
},
};
</script>
15.toRaw和markRaw
<template>
<h4>当前求和为:{{ sum }}</h4>
<button @click="sum++">点我++</button>
<hr>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job.j1.salary }}K</h2>
<h3 v-show="person.car">座驾:{{ person.car }}</h3>
<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 { ref, reactive, toRefs, toRaw, markRaw } from "vue"; // toRaw 用的不多
export default {
name: "demo",
setup() {
let sum = ref(0)
let person = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20
}
},
car: '',
// axios: '' 用markRaw标记,不可变
})
// toRaw 只能处理reactive缔造的响应式对象,不能处理ref类型的数据
/**
* toRaw:
* 作用: 将一个由reactive生成的响应式对象转为普通对象
* 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
*/
function showRawPerson() {
console.log(person) // Proxy {name: "张三", age: 18, job: {…}}
const p = toRaw(person)
console.log(p) // {name: "张三", age: 18, job: {…}}
}
/**
* markRaw
* 作用:标记一个对象,使其永远不会再成为响应式对象
* 应用场景:
* 1.有些值不应被设置为响应式的,例如复杂的第三方类库等
* 2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提供性能
*/
function addCar() {
let car = { name: '奔驰', price: 40 }
// person.car = car
person.car = markRaw(car)
}
function changePrice() {
person.car.price++
console.log(person.car.price) // 页面显示的价格是40不会变化
}
return {
sum,
person,
...toRefs(person),
showRawPerson,
addCar,
changePrice
};
},
};
</script>
16.customRef
<template>
<input type="text" v-model="keyWord" />
<h3>{{ keyWord }}</h3>
</template>
<script>
// import { ref } from "vue";
import { customRef } from "vue"; // 作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制
export default {
name: "App",
setup() {
let timer = null
// 自定义一个ref名为: ref_afanfan
function ref_afanfan(value, time) {
// let timer = null
// console.log("---myRef---", value); // ---myRef--- hello
return customRef((track, trigger) => {
return {
get() {
console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`);
track(); // 通知vue追踪数据的变化(提前和get商量一下,让他认为这个value是有用的)
return value;
},
set(newValue) {
console.log(`有人把myRef这个容器中的数据改为了:${newValue}`);
// 防抖
clearTimeout(this.timer)
this.timer = setTimeout(() => {
value = newValue;
trigger(); // 通知vue去重新解析模板
}, time);
},
};
});
}
// let keyWord = ref('hello'); // 使用vue提供的ref
let keyWord = ref_afanfan("hello",500);
return {
timer,
keyWord,
};
},
};
</script>
17.provide与inject
----------App.vue------------
<template>
<div class="app">
<h3>我是app组件(祖) --- {{ name }} {{ price }}</h3>
<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,后代子组件都可以通过inject获取数据
// 父传子props 父传孙provide
provide("car", car);
return {
...toRefs(car),
};
},
};
</script>
<style>
.app {
background-color: #ccc;
padding: 10px;
}
</style>
---------child.vue-----------
<template>
<div class="child">
<h3>我是child组件(子)</h3>
<son/>
</div>
</template>
<script>
import son from './son.vue'
export default {
name: "child",
components: { son },
setup() {
return {
};
},
};
</script>
<style>
.child {
background-color: skyblue;
padding: 10px;
}
</style>
------------son.vue----------------
<template>
<div class="son">
<h3>我是son组件(孙)--- {{ car.name }}--- {{ car.price }}</h3>
</div>
</template>
<script>
import { inject } from 'vue'
export default {
name: "son",
setup() {
let car = inject('car')
console.log('===', car)
return {
car
};
},
};
</script>
<style>
.son {
background-color: orange;
padding: 10px;
}
</style>
18.响应式数据的判断
<template>
<!-- 在vue2中,组件必须有一个根标签;在vue3中,组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中 好处是减少标签层级,减小内存占用 -->
<h3>我是app组件</h3>
<h3>我是app组件</h3>
<h3>我是app组件</h3>
</template>
<script>
import { ref, reactive, toRefs, readonly, isRef, isReactive, isReadonly, isProxy } from "vue";
// isRef 检查一个值是否为ref对象
// isReactive // 检查一个对象是否由reactive创建的响应式代理
// isReadonly // 检查一个对象是否由readonly创建的只读代理
// isProxy // 检查一个对象是否由reactive或者readonlty方法创建的代理
export default {
name: "App",
components: { },
setup() {
let car = reactive({ name: '奔驰', price: '40W' })
let sum = ref(0)
let car2 = readonly(car)
console.log(isRef(sum)) // true
console.log(isReactive(car)) // true
console.log(isReadonly(car2)) // true
console.log(isProxy(car)) // true
console.log(isProxy(sum)) // false ref底层用的是Object.defineProperty() 不是proxy 所以这里为false
return {
...toRefs(car)
};
},
};
</script>
19.teleport传送
<template>
<div>
<button @click="isShow = true">点我弹个窗</button>
<!-- teleport:传送 to="html" 传送到html中去 to="body" 传送到body中去 -->
<!-- 此处的to="#afanfan" 在public里的index.html里有<div id="afanfan"></div> -->
<teleport to="#afanfan">
<div v-if="isShow" class="mask">
<div class="dialogStyle">
<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: rgb(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.dialogStyle {
width: 300px;
height: 300px;
background-color: green;
}
</style>