# vue3_api
**Repository Path**: 120901/vue3_api
## Basic Information
- **Project Name**: vue3_api
- **Description**: No description available
- **Primary Language**: JavaScript
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-08-20
- **Last Updated**: 2021-11-03
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Vue3.0
## setup
`setup 函数是一个新的组件选项。作为在组件内使用Composition API的入口点`
* 调用时机
- 创建组件实例,然后初始化props, 紧接着就调用setup函数。从生命周期钩子的视角来看,它会在beforeCreate钩子之前被调用
* 模板中使用
- 如果setup返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文
* 渲染函数 / JSX中使用
- setup也可以返回一个函数,函数中也能使用当前setup函数作用域中的响应式数据
* 参数
- 该函数接收props作为其第一个参数(不能使用解构的方法使用)
- 第二个参数是context,提供了一个上下文对象,选择性的暴露了一些property attrs/slots/emit(可以解构使用)
```
export default {
props: {
name: String
},
setup (props) {
console.log(props.name)
}
}
```
**注意props对象是响应式的,watchEffer或watch会观察和响应props的更新**
```
export default {
props: {
name: String
},
setup (props) {
watchEffect(() => {
console.log(`name is:` + props.name)
})
}
}
// 解构的写法会散失响应性
setup({ name }) // xxxx
```
* this的用法
- this在setup()中不可用
* 类型定义
```
interface Data {
[key: string]: unknown
}
interface SetupContext {
attr: Data
slots: Slots
emit: (event: string, ...args: unknown[]) => void
}
function setup(props: Data, context: SetupContext): Data
```
## 响应式系统API
### reactive
- 接收一个普通对象然后返回该普通对象的响应式代理。 等同于Vue.observable()
### ref
* 接受一个参数值并返回一个响应式且可改变的ref对象。ref对象拥有一个指向内部值得单一属性.value
```
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
```
**如果传入ref的是一个对象,将调用reactive方法进行深层响应转换**
* 模板中访问
- 将ref作为渲染上下文的属性返回(即在setup()返回的对象中)并在模板中使用时,会自动解套,无需在模板内额外书写.value
```
{{ count }}
```
* 作为响应式对象的属性访问
- 当ref作为reactive对象的property被访问或修改时,也将自动解套value值,其行为类似普通属性
```
const count = ref(0)
const state = reactive({ count })
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1
```
- 如果将一个新的ref分配给现有的ref,将替换旧的ref
```
const otherCount = ref(2)
state.count = otherCount
console.log(state.count) // 2
console.log(count.value) // 1
```
**注意当嵌套在reactive中时,ref才会解套。从Arrya或者Map等原生集合类中访问ref时,不会自动解套,需要使用.value**
* 类型定义
```
interface Ref {
value: T
}
function ref(value: T): Ref
// 有时我们可能需要为ref做一个较为复杂的类型标注。我们可以通过在调用ref时传递泛型参数来覆盖默认推导
const foo = ref('foo') // foo的类型为: Ref
foo.value = 123 // 能够通过
```
### computed
* 传入一个getter函数,返回一个默认不可手动修改的ref对象
```
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // error
```
* 传入一个拥有get和set函数的对象,创建一个可手动修改的计算状态
```
const count = ref(1)
const plusOne = couputed({
get: () => count.value + 1,
set: (val) => {
count.value = val -1
}
})
plusOne.value = 1
console.log(count.value) // 0
```
* 类型定义
```
// 只读的
function computed(getter: ()=> T):Readonly[>>
// 可更改的
function computed(options: {
get: () => T
set: (value: T) => void
})
```
### readonly
* 传入一个对象(响应式或普通)或ref, 返回一个原始对象的只读代理。一个只读的代理是“深层的”,对象内部任何嵌套的属性也都是只读的。
```
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// 依赖追踪
console.log(copy.count)
})
// original上的修改会触发copy上的侦听
original.count++
// 无法修改copy并会被警告
copy.count++ // warning
```
### watchEffecct
* 立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数
```
const count = ref(0)
watchEffect(() => console.log(count.value))
setTimeout(() => {
count.value++ // -> 打印出 1
}, 100)
```
* 停止侦听: 当watchEffect在组件的setup()函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。
```
// 在一些情况下,也可以显式调用返回值以停止侦听
const stop = watch(() => { /* ... */ })
// 之后
stop()
```
* 清除副作用: 有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除(即完成之前状态已经改变了)。所以侦听副作用传入的函数可以接收一个onInvalidate函数作入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:
- 副作用即将重新执行时
- 侦听器被停止(如果在setup()或生命周期钩子函数中使用了watchEffect,则在卸载组件时)
```
watchEffect((onInvalidate) => {
const token = performAsyncOperation(id.value)
onInvalidate(() => {
// id改变时 或 停止侦听时
// 取消之前的异步操作
token.cacel()
})
})
```
**在执行数据请求时,副作用函数往往是一个异步函数**
```
const data = ref(null)
watchEffect(async () => {
data.value = await fetchData(props.id)
})
```
* 副作用刷新时机
- Vue的响应式系统会存在副作用函数,并异步地刷新他们,这样可以避免同一个tick中多个状态改变导致的不必要的重复调用。在核心的具体实现中,组件的更新函数也是一个被侦听的副作用。当一个用户定义的副作用函数进入队列时,会在所有的组件更新后执行:
```
]{{ count }}
// 同步运行
watchEffect(
() => {}, {flush: 'sync'}
)
// 组件更新前执行
watchEffect(
() => {}, {flush: 'pre'}
)
```
### watch
* 完全等效于2.x this.$watch。watch需要侦听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,也就是说仅在侦听的源变更时才执行回调
* 对比watchEffect,watch允许我们:
- 懒执行函数
- 更明确哪些状态的改变会触发侦听器重新运行执行函数
- 访问侦听状态变化前后的值
* 侦听单个数据源
- 侦听器的数据源可以是一个拥有返回值的getter函数,也可以是ref
```
// getter
const state = reacitve({ count: 0 })
watch(
() => state.count,
(count, prevCount) => { /* ... */ }
)
// ref
const count = ref(0)
watch(count, (count, prevCount) => { /*...*/ })
```
* 侦听多个数据源
- 数组的形式
```
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar])=> {...})
```
* 与watchEffect的共同点
- watch 和 watchEffect 在停止侦听, 清除副作用 (相应地 onInvalidate 会作为回调的第三个参数传入),副作用刷新时机 和 侦听器调试 等方面行为一致.
## 生命周期钩子函数
* 与2.x版本生命周期相对应的组合式API
- ~~beforeCreate~~ ==> setup()
- ~~created~~ ==> setup()
- ~~beforeMount~~ ==> onBeforeMount
- ~~mounted~~ ==> onMounted
- ~~beforeUpdate~~ ==> onBeforeUpdate
- ~~updated~~ ==> onUpdated
- ~~beforeDestroy~~ ==> onBeforeUnmount
- ~~destroyed~~ ==> onUnmounted
- ~~errorCaptured~~ ==> onErrorCaptured
* 新增的钩子函数
- onRenderTracked
- onRenderTriggered
- 都接收一个DebuggerEvent
```
export default {
onRenderTracked (e) { debugger }
// 检查哪个依赖性导致组件重新渲染
}
```
* hooks使用方式: 只能在setup内调用
```
import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
setup () {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
```
## 依赖注入
* provide和inject提供依赖注入,功能类似2.x的provide/inject,都只能在当前活动组件实例的setup中调用
```
import { provide, inject } from 'vue'
cont ThemeSymbol = Symbol()
const Ancestor = {
setup () {
provide(ThemeSymbol, 'dark')
}
}
const Descendent = {
setup () {
const theme = inject(ThemeSymbol, 'light' /* optional default value */ )
}
}
```
**inject接受一个可选的默认值作为第二个参数。如果未提供默认值,并且在provide上下文中未找到该属性,则inject返回undefined**
* 注入的响应式
- 可以使用ref来保证provide和inject之间值得响应
```
// 提供者
const themeRef = ref('dark)
provide(ThemeSymbol, themeRef)
// 使用者
const theme = inject(ThemeSymbol, ref('light'))
watchEffect(() => {
console.log(`theme set to: ${theme.value}`)
})
```
**如果注入一个响应式对象,则它得状态变化也可以被侦听**
## 模板refs
```
```
**模板 ref 仅在渲染初始化后才能访问**
* 在v-for中使用
```
```
## 响应式系统工具集
* unref: 如果参数是一个ref则返回它得value,否则返回参数本身。它是val = isRef(val) ? val.value : val的语法糖
```
function useFoo(x: number | Ref) {
const unwrapped = unref(x) // unwrapped 一定是 number 类型
}
```
* toRef: 可以用来为一个reactive对象的属性创建一个ref。这个ref可以被传递并且能够保持响应性
```
const state = reactive({
foo: 1,
bar: 2
})
const fooRef = toRef(state, 'foo')
fooRef.value++
console.log(state.foo) // 2
state.foo++
console.log(fooRef.value) // 3
// 将一个prop中的属性作为ref传给组合逻辑函数
setup(props) {
useSomeFeature(toRef(props, 'foo'))
}
```
* toRefs: 把一个响应式对象转换成普通对象,该普通对象的每个property都是一个ref,和响应式对象property一一对应
```
const state = reactive({
foo: 1,
bar: 2
})
const stateAsRefs = toRefs(state)
/*
stateAsRefs的类型如下:
{
foo: Ref,
bar: Ref
}
*/
// ref对象与原属性的引用是"链接"上的
state.foo++
console.log(stateAsRefs.foo.value) // 2
stateAsRefs.foo.value++
console.log(state.foo) // 3
```
**让组件可以解构/扩展(...操作符)使用**
```
function useFeatureX() {
const state = reactive({ foo: 1, bar: 2 })
}
return toRefs(state)
export default {
setup() {
// 可以解构,不会丢失响应性
const { foo, bar } = useFeatureX()
return {
foo,
bar
}
}
}
```
* isRef: 检查一个值是否为一个ref对象
* isProxy: 检查一个对象是否由reactive或者readyonly方法创建的代理
* isReactive: 检查一个对象是否是由reactive创建的响应式代理
* isReadonly: 检查一个对象是否是由readonly创建的只读代理
## 高级响应式系统API
......