最近这个几个月都在跟着coderwhy老师,系统性的学习Vue3的新知识,和新特性。首先,先感谢coderwhy老师讲的非常的好,真的很赞,我这种脑子转的很慢的人,都可以跟上他的节奏,非常感谢老师。
老师教的好,但是并不代表我就以前完全的吸收了,代码这个东西,不长期练习,总是会忘的,所以自己打算做个总结,忘的时候,在瞄一眼吧。好了,开始正题吧!
Vue3最大的特色 就是 Composition API
咯。
Composition API
: 一组低侵入式的、函数式的 API,它使我们能够更灵活地「组合」组件的逻辑。
如果写过react的话,就感觉很熟悉了。
Vue3中的 Composition API 和 Vue2中的Options API的对比
主要的区别就是,vue2中Options API
的对一个功能的逻辑代码比较分散,当项目大了,就非常的难以维护。 而在Vue3中的Composition API
就很好的解决了,此类问题,阅读性更加的强,如果项目大了,还可单独提出来,封装成一个函数,放在其他的文件。
来了来了, GO GO GO!
这里Vue3使用ts重构的,所以我也用ts代码来展示。
composition API提供的函数都是在setup中使用的,所以我们需要了解setup这个函数。
setup函数,就像Options API中使用, 增加一个属性
<script lang="ts">
//defineComponent 函数是用来支持ts语法的
import { defineComponent } from 'vue'
export default defineComponent({
setup() {
}
})
</script>
export default defineComponent({
beforeCreate() {
console.log('beforeCreate', this);
},
created() {
console.log('created', this)
},
setup() {
console.log('setup', this);
}
})
在Vue2中,我们知道,beforeCreate是最先执行的生命周期函数。 那么,上面的代码呢?
结果
上面的截图,我们可以发现,setup中是最先执行的,并且其中的this为undefined,是因为setup的调用发生在data property, computed property或methods解析之前,所以在setup中拿去不到。
所以,千万注意,不能在setup中使用this
setup接受两个参数
第一个参数:props 父组件传递过来的
第二个参数:是一个对象,包含三个可用的属性
export default defineComponent({
props: {
message: {
type: String,
require: true,
default: '默认值'
}
},
setup(props) {
console.log(props);
}
})
需要注意的时,我们接受父组件传递出来的props,还是跟Options API中一样的定义Props,又因为在setup中拿不到this,所以第一个参数提供props,为了让我们拿到props
第二个参数: 是一个对象,包含:
attrs
:所有的非props的attribute(id, class等等)在组件上写的属性,但是不是为了传递下来emit
: 提供子组件发出事件给父组件(因为不能通过this.$emit这种方式来触发)expose
: 这个暂时不知道,请大佬告知
slots
: 父组件传递过来的插槽使用(只会在h函数
中使用到)代码示例:
// attrs
//父组件
<Son message="james" class="my-son"/>
//子组件
export default defineComponent({
props: {
message: {
type: String,
require: true,
default: '默认值'
}
},
setup(props, {attrs}) {
//class是非attribute
console.log(attrs) // Proxy {class: "my-son", __vInternal: 1}
}
})
#slots 怎么使用,目前还没接触到,如果有遇到,在补充(尴尬,其实是自己不会,研究了一下,不会用)
// emit
//父组件
<template>
<div id="nav">
//接受子组件派发的事件
<Son @getSonFn="useSonFn" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import Son from '@/views/Son.vue'
export default defineComponent({
components: {
Son
},
setup() {
const useSonFn = () => {
console.log(1111);
}
return {
useSonFn
}
}
})
</script>
//子组件 Son.vue
<template>
<div id="nav">
<button @click="btn">点击,派发事件</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
emits: ['getSonFn'],
setup(props, {emit}) {
const btn = () => { //点击按钮,派发getSonFn事件,给父组件
emit('getSonFn') //当然后面也可以传递参数
}
return {
btn
}
}
})
</script>
setup函数跟Vue2的data函数一样,必须返回一个对象。
对象的属性值,用于渲染template模板上。
返回对象的响应式副本。(传入一个对象返回一个基于原对象的响应式代理,即返回一个proxy)
类型声明:
type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
基本使用:
import { defineComponent, reactive } from 'vue'
interface InfoType {
name: string;
age: number;
}
export default defineComponent({
setup() {
//泛型 InfoType
const info = reactive<InfoType>({name: 'james', age: 12})
return {}
}
})
解释:
reactive函数处理数据之后,数据再次被使用时就会进行依赖收集
接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property .value
。
类型声明:
interface Ref<T> {
value: T
}
function ref<T>(value: T): Ref<T>
基本使用:
import { ref } from 'vue'
const count = ref(0) //基本数据类型 会进行类型推断(这里就为number)
//等价于
const count = ref<number>(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
<template>
<div>{{count}}div> //解包
template>
解释:
解包操作(浅层解包)
const count = ref<number>(123)
const countInfo = { //使用一个普通对象包裹
countInfo
}
return { //返回普通对象
countInfo
}
//这里,它不会接解包
<div>{{countInfo.countInfo}}</div> //错误的写法
<div>{{countInfo.countInfo.value}}</div> //正确的写法
const name = ref('james')
//把name放在一个reactive函数包裹的对象中
const info = reactive({
name
})
//这里,它会进行解包
<div>{{info.name}}</div> //正确的写法
获取一个对象 (响应式或纯对象) 或 ref并返回原始代理的只读代理
。只读代理是深层的:访问的任何嵌套 property 也是只读的。
简单来说, readonly函数接受reactive()
和ref()
创建的属性,然后返回的对象只能读, 不能写。
基本使用:
import { readonly } from 'vue'
const name1 = reactive({
name: 'james'
})
const readonlyName = readonly(name1)
//readonlyName 就不能被修改了,会被报警告
const name1 = ref('kobe')
const readonlyName = readonly(name1)
使用场景:
封装一个组件,内部的值,readonly包裹传递过去,允许内部修改reactive和ref属性,但是不允许外部修改。
检查对象是否由reactive
或readonly
创建的proxy。
如果是,返回true, 否则,返回false
基本使用:
const info = reactive<InfoType>({name: 'james', age: 12})
const readonlyInfo = readonly(info)
const is = isProxy(readonlyInfo) //true
检查对象是否是 reactive
创建的响应式 proxy。
如果 proxy 是 readonly
创建的,但还包装了由 reactive
创建的另一个 proxy,它也会返回 true
。
基本使用:
import { reactive, isReactive, readonly } from 'vue'
export default {
setup() {
const state = reactive({
name: 'John'
})
// 从普通对象创建的只读代理
const plain = readonly({
name: 'Mary'
})
console.log(isReactive(plain)) // -> false
// 从响应式代理创建的只读代理
const stateCopy = readonly(state)
console.log(isReactive(stateCopy)) // -> true
}
}
检查对象是否是由readonly
创建的只读代理。
返回reactive或readonly代理的原始对象(不建议保留对原始对象的持久引用,谨慎使用)
创建一个响应式代理,它跟踪其自身property的响应式,但是不执行嵌套对象的深层响应式转化(深层还是原生对象)
基本使用:
const obj = shallowReactive({name: 'james', obj: {age: '12'}})
//这里就只监听第一层对象,第二层对象就监听不到了,还是原生的对象
//reactive就不一样了,可以随时监听多层
创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换 (暴露原始值)。