## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
vue2就不行
vue3中也可以按照vue2的方式来写,vue3是向下兼容的
index页面
name:
{{ name }}
age:
{{ age }}
test1
这里面的returb也可以这样写:
index页面
name:
{{ name }}
age:
{{ age }}
test1
而此时,页面中的内容只剩下return h('h1', '白马')
这里指定渲染的内容了
像是上面的代码,可以访问到setup中的变量
因为如果被async修饰了,那么它的返回值就不再是对象了,而是一个被promise包裹的对象,而你的模板不认识这个返回值
当初在使用vue2的时候,使用ref是为了给元素打标识之类的作用
在vue3中ref是个函数,原来的没有废掉
像是下面一样,定义了非响应式的数据:
一个人的信息:
姓名:{{ name }}
年龄:{{ age }}
这里的按钮点击了,在控制台输出的值会更改,但是页面中的值并没有发生改变,因为此时他不是响应式数据
虽然这里不是响应式数据了,但是也没有一些多余的getter,setter,当你想要使用响应式数据的时候,可以进行按需引入ref来使用
例如下面的:
一个人的信息:
姓名:{{ name }}
年龄:{{ age }}
爱好:{{ hobby }}
此时如果你直接输出一下hobby这个响应式对象,可以发现它是:
{
"__v_isShallow": false,
"dep": {
"w": 0,
"n": 0
},
"__v_isRef": true,
"_rawValue": "学习",
"_value": "学习"
}
这样的
而这里的RefImpl
的意思是引用的实现
标准的称呼为引用实现对象
,或是引用对象
,简称ref对象
在vue3的模板中,解析html部分的{{}}
内容时,它会自动.value
例如下面的代码:
setup () {
let job = ref({
salary: 30,
work: 'black man'
})
const getObj = () => {
console.log(job.value)
}
getObj()
}
这里输出的对象是:
![[Pasted image 20230603141346.png]]
这里的对象包装方式不同了
像是refimpl的实例对象,是通过getter,setter实现响应式的
ref处理对象的时候,会将里面的对象使用proxy来包装
对象类型的数据,内部会求助vue3中的一个新函数:reactive
作用:定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
语法:const 代理对象 = reactive(源对象)
接收一个对象(或数据),返回一个代理对象(proxy对象)
reactive定义的响应式数据是深层次的
内部基于es6的proxy实现,通过代理对象操作源对象内部数据进行操作
reactive函数处理不了基本类型
用reactive处理对象要比ref简单,像是下面这个:
setup () {
let job = ref({
salary: 30,
work: 'black man'
})
let company=reactive({
name:'武汉轻工大学',
year:4,
numbers:9000
})
const getObj = () => {
console.log(job.value)
console.log("company:")
console.log(company)
console.log(company.name)
}
getObj()
}
直接.
属性名即可,不用在.value之后再.
属性名
vue2的响应式:
实现原理:
Object.defineProperty
对属性的读取,修改进行拦截(数据劫持)Object.defineProperty(data,'count',{
get(){},
set(){}
})
测试vue2中为对象新增/删除属性是否能被监听到
name:{{ person.name }}
age:{{ person.age }}
gender:{{ person.gender }}
school:{{ person.school }}
sclary:{{ person.sclary }}
依次点击删除和添加:
![[Pasted image 20230603170440.png]]
对象是发生了改变,但是页面中没有改变
this.$set
来设置:this.$set(this.person,'sclary','3000')
那么此时添加属性,页面上也会有响应的
import Vue from 'vue'
// 使用它之前要引入
Vue.set(this.person, 'sclary', '3000')
Vue.set(this.person, 'sclary', '3000')
// 下面这个:
this.$delete(this.person, 'school')
// 或者:
Vue.delete(this.person,'school')
vue3的响应式:
实现原理:
实现一个proxy的响应式对象:
const person = {
name: 'zs',
age: 18
}
const p = new Proxy(person, {
get (target, propName) {
console.log('有人读取了')
return target[propName]
},
set (target, propName, value) {
console.log(`将${ propName }修改为${ value }`)
return target[propName] = value
}
})
在控制台尝试使用:
![[Pasted image 20230604000442.png]]
![[Pasted image 20230604000624.png]]
但是像上面的代码,捕获到了他的更新和读取,并没有捕获到它的删除和新增属性
其实proxy也可以监听删除:
const person = {
name: 'zs',
age: 18
}
const p = new Proxy(person, {
get (target, propName) {
console.log('有人读取了')
return target[propName]
},
set (target, propName, value) {
console.log(`将${ propName }修改为${ value }`)
target[propName] = value
},
deleteProperty (target, p) {
console.log(`删除属性${p}`)
delete target[p]
}
})
![[Pasted image 20230604002915.png]]
要注意,delete target[p]
这里的删除是有返回值的,其实把这里的删除返回即可:
deleteProperty (target, p) {
console.log(`删除属性${p}`)
return delete target[p]
}
关于proxy的增加属性:
get
是有人读取某个属性时会被调用set
有人修改属性,或追加
属性时会被调用deleteProperty
删除属性时调用虽然proxy可以监听到增删改查,但是vue3不是这样做的
![[Pasted image 20230604003421.png]]
let obj = { a: 1, b: 2 }
/* console.log('使用Object.defineProperties来实现映射:')
Object.defineProperties(obj, 'c', { get () { return 3 } }) Object.defineProperties(obj, 'c', { get () { return 4 } })*/
console.log('使用reflect来实现映射')
const x1 = Reflect.defineProperty(obj, 'c', {
get () {
return 3
}
})
console.log(x1)
const x2 = Reflect.defineProperty(obj, 'c', {
get () {
return 4
}
})
console.log(x2)
第一种方式使用Object.defineProperties
重复覆盖属性会报错,此时可以使用try,catrch来捕获,但是为了程序的壮行这样是否有些麻烦了
所以可以使用Reflect.defineProperty
来进行映射,它重复覆盖并不会出错,而且在每一次覆盖是都会有一个为boolean的返回值
let person = {
name: 'zs',
age: 24,
gender: '大三',
school: '武汉轻工大学'
}
const p = new Proxy(person, {
get (target, propName) {
console.log(`有人获取了${ propName }`)
return Reflect.get(target, propName)
},
set (target, propName, value) {
console.log(`有人修改了${ propName }`)
Reflect.set(target, propName, value)
},
deleteProperty (target, propName) {
console.log(`有人删除了p身上的${ propName }`)
return Reflect.deleteProperty(target, propName)
}})
从定义数据角度对比:
ref用来定义:基本类型数据
reactive用来定义:对象(或数组)类型数据
备注:ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive
转为代理对象
从原理角度对比:
ref通过Object.defineProperty()
的get
与set
来实现响应式(数据劫持)
reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据
从使用角度对比:
ref定义的数据:操作数据需要.value
,读取数据时,模板中直接读取不需要.value
reactive定义的数据:操作数据与读取数据:均不需要.value
如果你像是在下面的子组件中不写props:
父组件:
子组件:
注意,此时在子组件的props中是没有定义的
,但是在页面的控制台可以看到输出的this:
![[Pasted image 20230605234406.png]]
在vm的$attrs
上挂载了传入的值
到这里你可能想着,不定义props,直接传过来多方便,
但是,如果不定义props的话,你是无法限制传入的类型
在子组件中不去定义slot:
父组件:
这是一个slot的内容
子组件:
控制台中:
![[Pasted image 20230605234843.png]]
slot是存在的
使用多个插槽:
父组件:
张三
gender:5
子组件:
setup的执行时机:
在beforeCreate之前执行一次,this是undefined
setup的参数
props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
context:上下文对象
attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于this. a t t r s s l o t s : 收到的插槽内容 , 相当于 t h i s . attrs slots:收到的插槽内容,相当于this. attrsslots:收到的插槽内容,相当于this.slota
emit:分发自定义事件的函数,相当于this.$emit
例如在下面的例子中,父组件向子组件传值,但是子组件并没有定义props:
父组件:
你好
子组件:
子组件的内容
控制台输出:
![[Pasted image 20230611142633.png]]
如果此时在子组件中定义了props:
子组件的内容
此时setup中接收的第一个参数输出:
![[Pasted image 20230611143321.png]]
注意,此时它也是响应式对象
那么关于传入的其他形参:
父组件:
你好
这是第一个slot
自定义插槽
自定义插槽的第二个插槽
子组件:
子组件的内容
在控制台中查看:
![[Pasted image 20230611144611.png]]
性:
名:
名字:
![[Pasted image 20230611162743.png]]
一个简单的使用:
sum:{{ sum }}
当前的信息为:{{ msg }}
姓名:{{ person.name }}
年龄:{{ person.age }}
学校:{{ person.school }}
薪资:{{ person.job.salary }}
注意在vue3中只要对象使用active包裹,那么不管多深,都是可以监听到的
修改名字
修改年龄
修改学校
薪资增加
像是上面代码中,监听persojn的修改中,修改学校字段,此时在控制台查看:
![[Pasted image 20230612232234.png]]
注意,不论你是用ref还是reactive包裹对象,他都是无法监听到oldVal的,这是目前vue3中的问题
当你使用ref包裹对象时,它会自动使用reactive来包裹对象的
let sum = ref(0)
let msg = ref("你好")
watch([sum, msg], (newVal, oldVal) => {
console.log(newVal, oldVal)
})
在vue2中watch为配置对象,只能调用一次,而vue3中 ,watch为函数,可以调用多次
一上来就监听一次:
watch(sum, (newVal) => {
console.log(newVal)
}, { immediate: true })
强制开启了deep,你关闭了也没用
let person = reactive({
name: 'zs',
age: 24,
school: '武汉',
job: {
salary: '3000'
}
})
watch(() => person.name, (newVal, oldVal) => {
console.log('值发生改变了')
console.log(newVal)
})
![[Pasted image 20230612235713.png]]
此时你修改其它字段是不会被监听到