【学习打卡】第6天 【2022版】Vue3 系统入门与项目实战第六讲
课程名称: 2022持续升级 Vue3 从入门到实战 掌握完整知识体系
课程章节: Composition API
主讲老师: Dell
课程内容:
今天学习的内容包括:
ref,reactive响应式的引用原理:通过proxy对数据进行封装,当数据变化时,触发模板等内容的更新。
ref处理基本数据类型:proxy,变成proxy({value:’’})这样的一个响应式的引用
reactive处理非基本数据类型:proxy,变成proxy({name:’’})这样的一个响应式的引用
ref:将简单类型的数据变成响应式
reactive:将数组、对象变成响应式
toRefs:将响应式的对象(proxy({ name: ‘abc’ })),转变成 { name: proxy({ value: ‘abc’ }) },这样对name进行解构操作,name也是响应式的。若在其上取不存在的属性,获取的是undefined
toRef:在对象上取不存在的属性时,找不到的话不会像toRefs一下拿到的是undefined,而是给这个属性一个默认的空值(不推荐使用)
attrs:None-Props 属性
slots:插槽,获取到slot.defaul
emit:相当于传统语法中的this.$emit的作用
watch 侦听器
1、具有一定的惰性,只有变化时才执行
2、参数可以拿到当前值和原始值。watch(侦听内容, (当前值,原始值)=> {…})
3、watch侦听的是getter/effect函数、ref、响应式对象、或者这些类型的数组
4、若侦听的是reactive类型, 变为函数。如watch( () => nameObj.name, (cur, per) => {…} )
5、侦听多个数据 。如 watch( [() => obj.name, () => obj.age], ([nameCur, ageCur], [namePre, agePre]) => {…} )
watchEffect侦听器: watchEffect(()=> { … })
1、立即执行,没有惰性;
2、不需要传递需要侦听的内容,自动感知代码依赖;不需要传递多个参数,只需要传递一个回调函数
3、不能获取之前数据的值
课程收获:
6.1 心得:
为什么要用setup(),之前老语法在面对比较复杂的组件去开发的时候,数据,方法都是去分开写的,这就导致,维护,可读性差;
setup()何时去执行,beforecreate
之前像是methods,return {} 这些全部被setup()去包裹,那么如果你想要去使用一些数据,与方法,需要将这些内容,return出去,也就是说,暴露出去,组件就可以来使用这些内容了
this是不是指向的就是实例化的对象,但是由于,此时未实例化,所以,this 指向为空
但是当组件实例化完成之后,然后我就可以去调用setup() 了,因为setup在实例化完成之前,就创建好了
setup:created 实例被完全初始化之前执行的函数,所以用不了this
setup(props,context){
return {
name:‘dell’,
handClick:()=> {
alert(123)
}
}
}
6.2/6.3 心得:
ref、reactive 响应式的引用:
原理:通过 proxy 对数据进行封装,当数据变化时,触发模板等内容的更新
ref:处理基础类型的数据
const { ref } = Vue;
// proxy,‘dell’变成proxy({value:‘dell’}) 这样一个响应式引用
let name = ref(‘dell’);
name.value = ‘lee’;
reactive:处理非基础类型的数据
const { reactive } = Vue;
// proxy,{ name:‘dell’}变成proxy({ name:‘dell’}) 这样一个响应式引用
const nameObject =reactive({ name:‘dell’});
nameObject.name = ‘lee’;
让数据只读 使用readonly() 函数
如果直接解构setup里reactive()的数组或者对象是在外面不可用的,因为reactive只是将数组/对象变成了响应式,但是没有将里面的每一项变成响应式,单独的只是一个字符串/数字等。
如果要想解构,需要用到toRefs()
函数
他将proxy({name: 'cmy', age: '18' })
===>
proxy({
name: proxy({ value: 'cmy' }),
age: proxy({value: 18})
})
外面可以直接引用name、age 相当于引用name.value、age.value
readonly:使响应式应用只读,不可修改
const copyName = readonly(nameObj);
toRefs:可以进行解构(reactive无法进行解构)
// proxy({ name:‘dell’}) => { name:proxy({ value:‘dell’})}
const { name } = toRefs(nameObj);
return { name }
6.4/6.5 心得
toRefs() 如果引用的数据里面没有某个字段,他不会给默认值,而是会说明该字段是undefined
如果不想要避免这种情况,可以使用toRef()函数。使用如下图,它会使不存在的也具有响应式
但不建议使用该方法,也不建议使用添加数据中没有的字段,若要使用,就在数据中心添加该字段,给一个空值。
<!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>
<script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
//ref,reactive 响应式的引用
//原理,通过proxy对数据进行封装,当数据变化时,出发模板等内容的更新
//ref 处理基础类型的数据,如字符串,数字等(不适合数组对象等)
//reactive 处理非基础类型的数据
const app = Vue.createApp({
template: `
<div>{{nameArray[0]}}</div>
`,
setup(props,context) {
// const { ref } = Vue
// //proxy,'dell'变成 proxy({value:'dell'})这样的响应式引用
// let name = ref('dell');
// setTimeout(() => {
// name = 'lee'
// }, 2000);
// return { name }
// const { reactive } = Vue
// //proxy,'dell'变成 proxy({value:'dell'})这样的响应式引用
// let nameObj = reactive({name:'dell'});
// setTimeout(() => {
// nameObj.name = 'lee'
// }, 2000);
// return { nameObj }
// const { reactive } = Vue
// //proxy,'dell'变成 proxy(['dell'])这样的响应式引用
// let nameArray = reactive(['dell']);
// setTimeout(() => {
// nameArray[0] = 'lee'
// }, 2000);
// return { nameArray }
const { reactive, toRefs } = Vue
//proxy,'dell'变成 proxy(['dell'])这样的响应式引用
let nameObj = reactive({name:'dell',age:23});
setTimeout(() => {
nameObj.name = 'lee'
}, 2000);
//toRefs proxy({ name:'dell',age:23})转化成{
// name: proxy({ value: 'dell'}),
// age: proxy({ value: 23})
// }
const { name } = toRefs(nameObj)
return { name }
//readOnly不允许修改
// const { readOnly } = Vue
// let nameReadOnly = readOnly(['dell']);
// setTimeout(() => {
// nameReadOnly[0] = 'lee'
// }, 2000);
// return { nameReadOnly }
}
})
const vm = app.mount('#root')
</script>
</html>
6.5 心得:
setup(props,context){ }
context共有三个值 attrs,slots,emit
attrs 接收No-props属性
slots 是插槽, slots.defalut() 【例子如下图】他接收了父组件传来的值parent,以前的用法如mounted函数中的例子
emit: 代替this.$emit 子组件向外出发事件
const { attrs,slots,emit } = context;
<!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>
<script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
//toRef
const app = Vue.createApp({
methods: {
handleChange(){
alert('change')
}
},
template: `
<child app="app" @change="handleChange">parent</child>
`
});
app.component('child',{
template: `
<div @click="handleClick">12122</div>
`,
setup(props, context) {
const { h } = Vue
const { attrs, slots, emit } = context
console.log('attrs',attrs.app)
console.log('slots',slots.default())
//return () => h ('div',{},slot.default())
function handleClick() {
emit('change')
}
return { handleClick }
}
})
const vm = app.mount('#root')
</script>
</html>
6.6/6.7 心得:
reactive来定义非基础类型
// 和list相关的代码
const listRelative = ()=>{
const {reactive} = Vue
const list = reactive([])
handleClick = (inputvalue)=>{
if(inputvalue){
list.push(inputvalue)
}
}
return {
list,
handleClick
}
}
// 和输入框相关的代码
const inputRef = ()=>{
const {ref}= Vue
const inputValue = ref('')
handleInput = (e)=>{
inputValue.value = e.target.value
}
return {
inputValue,
handleInput
}
}
const app = Vue.createApp({
template:`
<div>
<div>
<input :value="inputValue" @input="handleInput"/>
<button @click="handleClick(inputValue)">提交</button>
</div>
<ul>
<li v-for="(item,index) in list">{{item}}</li>
</ul>
</div>
`,
setup(){
const {list,handleClick} = listRelative()
const {inputValue,handleInput} = inputRef()
return {
list,
inputValue,
handleInput,
handleClick
}
}
})
const vm = app.mount('#root')
如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数;
如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递,并且事件对象的名称必须是$event;
6.8 心得:
具备get和set的计算属性
const app = Vue.createApp({
setup() {
const { reactive, computed } = Vue;
const countObj = reactive({ count: 0});
const handleClick = () => {
countObj.count += 1;
}
let countAddFive = computed({
get: () => {
return countObj.count + 5;
},
set: (param) => {
countObj.count = param - 5;
}
})
setTimeout(() => {
countAddFive.value = 100;
}, 3000)
return { countObj, countAddFive, handleClick }
},
template: `
<div>
<span @click="handleClick">{{countObj.count}}</span> -- {{countAddFive}}
</div>
`,
});
如果是只用get,则计算属性传入一个函数,
如果是要用get和set,则计算属性要传一个对象。
6.9/6.10 心得:
watch监听器:
具有一定的惰性 lazy (首次页面展示时不会执行)
参数可以拿到原始和当前值
setup(){
const { ref,watch } = Vue;
const name = ref(‘lee’);
watch(name,(currentValue,preValue)=> {
console.log(currentValue,preValue)
})
return { name }
}
监听reactive复杂类型的数据,需要用箭头函数的方式监听
setup(){
const { reactive,watch } = Vue;
const nameObj = reactive({ name:‘lee’ });
watch(()=> nameObj name,(currentValue,preValue)=> {
console.log(currentValue,preValue)
})
cosnt { name } = toRefs(nameObj);
return { name }
}
可以侦听多个数据的变化,用一个侦听器承载(用数组来操作)
setup(){
const { reactive,watch } = Vue;
const nameObj = reactive({ name:‘lee’,englishName:‘dell’ });
watch([()=> nameObj name,()=> nameObj englishName],( [curName,curEng],[ preName,preEng ] )=> {
console.log(curName,preName,‘——’,curEng,preEng )
})
cosnt { name } = toRefs(nameObj);
return { name }
}
watchEffect 侦听器,偏向于 effect (更适合异步的请求)
1、立即执行,没有惰性 immediate
2、不需要传递你要侦听的内容,自动会感知代码依赖,不需要传递很多参数,只要传递一个回调函数
3、不能获取之前数据的值
setup(){
cosnt { watchEffect } = Vue;
watchEffect (()=>{
console.log(nameObj.name)
console.log(nameObj.englishName)
})
}
停止监听器(watch和watchEffect通用)
setup(){
cosnt { watchEffect } = Vue;
const stop = watchEffect (()=>{
console.log(nameObj.name)
console.log(nameObj.englishName)
setTimeout(()=>{
stop();
},5000)
})
}
watch也可以做非惰性的监听(配置{ immediate:true })
setup(){
const { reactive,watch } = Vue;
const nameObj = reactive({ name:‘lee’,englishName:‘dell’ });
watch([()=> nameObj name,()=> nameObj englishName],( [curName,curEng],[ preName,preEng ] )=> {
console.log(curName,preName,‘——’,curEng,preEng )
},{ immediate:true })
cosnt { name } = toRefs(nameObj);
return { name }
}
6.11 心得:
beforeMount => onBeforeMount
mounted => onMounted
beforeUpdate => onBeforeUpdate
beforeUnmount => onBeforeUnmount // 组件从页面移除的时候执行该方法
unmounted => onUnmounted // 组件从页面移除之后执行该方法
setup的生命周期中没有beforeCreate,created两个函数,因为setup执行的时间就在这两个函数之间
新的生命周期:
onRenderTracked:每次渲染之后重新收集响应式依赖的时候会自动执行的函数
onRenderTriggerd:每次触发页面重新渲染时候自动执行的函数
setup中生命周期函数的写法
const app = Vue.createApp({
// beforeMount => onBeforeMount
// mounted => onMounted
// beforeUpdate => onBeforeUpdate
// beforeUnmount => onBeforeUnmount
// unmouted => onUnmounted
setup() {
const {
ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
onRenderTracked, onRenderTriggered
} = Vue;
const name = ref('dell')
onBeforeMount(() => {
console.log('onBeforeMount')
})
onMounted(() => {
console.log('onMounted')
})
onBeforeUpdate(() => {
console.log('onBeforeUpdate')
})
onUpdated(() => {
console.log('onUpdated')
})
// 每次渲染后重新收集响应式依赖
onRenderTracked(() => {
console.log('onRenderTracked')
})
// 每次触发页面重新渲染时自动执行
onRenderTriggered(() => {
console.log('onRenderTriggered')
})
const handleClick = () => {
name.value = 'lee'
}
return { name, handleClick }
},
template: `
<div @click="handleClick">
{{name}}
</div>
`,
});
生命周期函数的新写法 ,beforeCreate ,created 没有新写法。因为setup()执行在他俩之间
6.12 心得:
const app = Vue.createApp({
setup(){
const{ provide }= Vue;
provide('name', 'dell');
return {}
},
template:
<div>
<child/>
</div>
});
app. component('child',{
setup(){
const { inject }= Vue;
const name = inject('name', 'hello');
return { name }
},
template: '<div>{{name}}</div>'
})
单向数据流:谁的值由谁更改(子组件不能更改父组件的数据)
父组件传递方法:
const app = Vue.createApp({
setup(){
const { provide, ref }= Vue;
const name = ref('dell');
provide('name', name);
provide('changeName',(value) => {
name.value = value;
})
return{}
},
子组件调用父组件的方法:
app.component('child',{
setup(){
const { inject }=Vue;
const name = inject('name');
const changeName = inject('changeName');
const handleClick=()=>{
changeName('Lee');
}
return { name, handleClick}
},
template: '<div @click="handleClick">{{name}}</div>'
想要获取ref绑定的dom,需要const hello = ref(null),再由hello.vue获取到dom
// CompositionAPI 的语法下,获取真实的 DOM 元素节点
const app = Vue.createApp({
setup(){
const { ref, onMounted}=Vue;
const hello = ref(null);
onMounted(() => {
console.log(hello.value);
})
return { hello0 }
},
共同学习,写下你的评论
评论加载中...
作者其他优质文章