doc
This commit is contained in:
1026
docs/chapter4/01_Composition API_常用部分.md
Normal file
1026
docs/chapter4/01_Composition API_常用部分.md
Normal file
File diff suppressed because it is too large
Load Diff
464
docs/chapter4/02_Composition API_其它部分.md
Normal file
464
docs/chapter4/02_Composition API_其它部分.md
Normal file
@@ -0,0 +1,464 @@
|
||||
# 2. Composition API(其它部分)
|
||||
|
||||
## 1) shallowReactive 与 shallowRef
|
||||
|
||||
- shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式)
|
||||
- shallowRef: 只处理了value的响应式, 不进行对象的reactive处理
|
||||
|
||||
- 什么时候用浅响应式呢?
|
||||
- 一般情况下使用ref和reactive即可
|
||||
- 如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
|
||||
- 如果有一个对象数据, 后面会产生新的对象来替换 ===> shallowRef
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<h2>App</h2>
|
||||
|
||||
<h3>m1: {{m1}}</h3>
|
||||
<h3>m2: {{m2}}</h3>
|
||||
<h3>m3: {{m3}}</h3>
|
||||
<h3>m4: {{m4}}</h3>
|
||||
|
||||
<button @click="update">更新</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, ref, shallowReactive, shallowRef } from 'vue'
|
||||
/*
|
||||
shallowReactive与shallowRef
|
||||
shallowReactive: 只处理了对象内最外层属性的响应式(也就是浅响应式)
|
||||
shallowRef: 只处理了value的响应式, 不进行对象的reactive处理
|
||||
总结:
|
||||
reactive与ref实现的是深度响应式, 而shallowReactive与shallowRef是浅响应式
|
||||
什么时候用浅响应式呢?
|
||||
一般情况下使用ref和reactive即可,
|
||||
如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
|
||||
如果有一个对象数据, 后面会产生新的对象来替换 ===> shallowRef
|
||||
*/
|
||||
|
||||
export default {
|
||||
|
||||
setup () {
|
||||
|
||||
const m1 = reactive({a: 1, b: {c: 2}})
|
||||
const m2 = shallowReactive({a: 1, b: {c: 2}})
|
||||
|
||||
const m3 = ref({a: 1, b: {c: 2}})
|
||||
const m4 = shallowRef({a: 1, b: {c: 2}})
|
||||
|
||||
const update = () => {
|
||||
// m1.b.c += 1
|
||||
// m2.b.c += 1
|
||||
|
||||
// m3.value.a += 1
|
||||
m4.value.a += 1
|
||||
}
|
||||
|
||||
return {
|
||||
m1,
|
||||
m2,
|
||||
m3,
|
||||
m4,
|
||||
update,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 2) readonly 与 shallowReadonly
|
||||
|
||||
- readonly:
|
||||
- 深度只读数据
|
||||
- 获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。
|
||||
- 只读代理是深层的:访问的任何嵌套 property 也是只读的。
|
||||
- shallowReadonly
|
||||
- 浅只读数据
|
||||
- 创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换
|
||||
- 应用场景:
|
||||
- 在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<h2>App</h2>
|
||||
<h3>{{state}}</h3>
|
||||
<button @click="update">更新</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, readonly, shallowReadonly } from 'vue'
|
||||
/*
|
||||
readonly: 深度只读数据
|
||||
获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。
|
||||
只读代理是深层的:访问的任何嵌套 property 也是只读的。
|
||||
shallowReadonly: 浅只读数据
|
||||
创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换
|
||||
应用场景:
|
||||
在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除
|
||||
*/
|
||||
|
||||
export default {
|
||||
|
||||
setup () {
|
||||
|
||||
const state = reactive({
|
||||
a: 1,
|
||||
b: {
|
||||
c: 2
|
||||
}
|
||||
})
|
||||
|
||||
// const rState1 = readonly(state)
|
||||
const rState2 = shallowReadonly(state)
|
||||
|
||||
const update = () => {
|
||||
// rState1.a++ // error
|
||||
// rState1.b.c++ // error
|
||||
|
||||
// rState2.a++ // error
|
||||
rState2.b.c++
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
update
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 3) toRaw 与 markRaw
|
||||
|
||||
- toRaw
|
||||
- 返回由 `reactive` 或 `readonly` 方法转换成响应式代理的普通对象。
|
||||
- 这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发界面更新。
|
||||
- markRaw
|
||||
- 标记一个对象,使其永远不会转换为代理。返回对象本身
|
||||
- 应用场景:
|
||||
- 有些值不应被设置为响应式的,例如复杂的第三方类实例或 Vue 组件对象。
|
||||
- 当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能。
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<h2>{{state}}</h2>
|
||||
<button @click="testToRaw">测试toRaw</button>
|
||||
<button @click="testMarkRaw">测试markRaw</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
/*
|
||||
toRaw: 得到reactive代理对象的目标数据对象
|
||||
*/
|
||||
import {
|
||||
markRaw,
|
||||
reactive, toRaw,
|
||||
} from 'vue'
|
||||
export default {
|
||||
setup () {
|
||||
const state = reactive<any>({
|
||||
name: 'tom',
|
||||
age: 25,
|
||||
})
|
||||
|
||||
const testToRaw = () => {
|
||||
const user = toRaw(state)
|
||||
user.age++ // 界面不会更新
|
||||
|
||||
}
|
||||
|
||||
const testMarkRaw = () => {
|
||||
const likes = ['a', 'b']
|
||||
// state.likes = likes
|
||||
state.likes = markRaw(likes) // likes数组就不再是响应式的了
|
||||
setTimeout(() => {
|
||||
state.likes[0] += '--'
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
testToRaw,
|
||||
testMarkRaw,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 4) toRef
|
||||
|
||||
- 为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的
|
||||
- 区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响
|
||||
- 应用: 当要将 某个prop 的 ref 传递给复合函数时,toRef 很有用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<h2>App</h2>
|
||||
<p>{{state}}</p>
|
||||
<p>{{foo}}</p>
|
||||
<p>{{foo2}}</p>
|
||||
|
||||
<button @click="update">更新</button>
|
||||
|
||||
<Child :foo="foo"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
/*
|
||||
toRef:
|
||||
为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的
|
||||
区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响
|
||||
应用: 当要将某个 prop 的 ref 传递给复合函数时,toRef 很有用
|
||||
*/
|
||||
|
||||
import {
|
||||
reactive,
|
||||
toRef,
|
||||
ref,
|
||||
} from 'vue'
|
||||
import Child from './Child.vue'
|
||||
|
||||
export default {
|
||||
|
||||
setup () {
|
||||
|
||||
const state = reactive({
|
||||
foo: 1,
|
||||
bar: 2
|
||||
})
|
||||
|
||||
const foo = toRef(state, 'foo')
|
||||
const foo2 = ref(state.foo)
|
||||
|
||||
const update = () => {
|
||||
state.foo++
|
||||
// foo.value++
|
||||
// foo2.value++ // foo和state中的数据不会更新
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
foo,
|
||||
foo2,
|
||||
update,
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
Child
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<h2>Child</h2>
|
||||
<h3>{{foo}}</h3>
|
||||
<h3>{{length}}</h3>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, Ref, toRef } from 'vue'
|
||||
|
||||
const component = defineComponent({
|
||||
props: {
|
||||
foo: {
|
||||
type: Number,
|
||||
require: true
|
||||
}
|
||||
},
|
||||
|
||||
setup (props, context) {
|
||||
const length = useFeatureX(toRef(props, 'foo'))
|
||||
|
||||
return {
|
||||
length
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function useFeatureX(foo: Ref) {
|
||||
const lenth = computed(() => foo.value.length)
|
||||
|
||||
return lenth
|
||||
}
|
||||
|
||||
export default component
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 5) customRef
|
||||
|
||||
- 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
|
||||
- 需求: 使用 customRef 实现 debounce 的示例
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<h2>App</h2>
|
||||
<input v-model="keyword" placeholder="搜索关键字"/>
|
||||
<p>{{keyword}}</p>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
/*
|
||||
customRef:
|
||||
创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
|
||||
|
||||
需求:
|
||||
使用 customRef 实现 debounce 的示例
|
||||
*/
|
||||
|
||||
import {
|
||||
ref,
|
||||
customRef
|
||||
} from 'vue'
|
||||
|
||||
export default {
|
||||
|
||||
setup () {
|
||||
const keyword = useDebouncedRef('', 500)
|
||||
console.log(keyword)
|
||||
return {
|
||||
keyword
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
实现函数防抖的自定义ref
|
||||
*/
|
||||
function useDebouncedRef<T>(value: T, delay = 200) {
|
||||
let timeout: number
|
||||
return customRef((track, trigger) => {
|
||||
return {
|
||||
get() {
|
||||
// 告诉Vue追踪数据
|
||||
track()
|
||||
return value
|
||||
},
|
||||
set(newValue: T) {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => {
|
||||
value = newValue
|
||||
// 告诉Vue去触发界面更新
|
||||
trigger()
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## 6) provide 与 inject
|
||||
|
||||
- provide` 和 `inject` 提供依赖注入,功能类似 2.x 的 `provide/inject
|
||||
|
||||
- 实现跨层级组件(祖孙)间通信
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<h1>父组件</h1>
|
||||
<p>当前颜色: {{color}}</p>
|
||||
<button @click="color='red'">红</button>
|
||||
<button @click="color='yellow'">黄</button>
|
||||
<button @click="color='blue'">蓝</button>
|
||||
|
||||
<hr>
|
||||
<Son />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { provide, ref } from 'vue'
|
||||
/*
|
||||
- provide` 和 `inject` 提供依赖注入,功能类似 2.x 的 `provide/inject
|
||||
- 实现跨层级组件(祖孙)间通信
|
||||
*/
|
||||
|
||||
import Son from './Son.vue'
|
||||
export default {
|
||||
name: 'ProvideInject',
|
||||
components: {
|
||||
Son
|
||||
},
|
||||
setup() {
|
||||
|
||||
const color = ref('red')
|
||||
|
||||
provide('color', color)
|
||||
|
||||
return {
|
||||
color
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<h2>子组件</h2>
|
||||
<hr>
|
||||
<GrandSon />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import GrandSon from './GrandSon.vue'
|
||||
export default {
|
||||
components: {
|
||||
GrandSon
|
||||
},
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<h3 :style="{color}">孙子组件: {{color}}</h3>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { inject } from 'vue'
|
||||
export default {
|
||||
setup() {
|
||||
const color = inject('color')
|
||||
|
||||
return {
|
||||
color
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 7) 响应式数据的判断
|
||||
|
||||
- isRef: 检查一个值是否为一个 ref 对象
|
||||
- isReactive: 检查一个对象是否是由 `reactive` 创建的响应式代理
|
||||
- isReadonly: 检查一个对象是否是由 `readonly` 创建的只读代理
|
||||
- isProxy: 检查一个对象是否是由 `reactive` 或者 `readonly` 方法创建的代理
|
||||
|
||||
259
docs/chapter4/03_手写组合API.md
Normal file
259
docs/chapter4/03_手写组合API.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# 3. 手写组合API
|
||||
|
||||
## 1) shallowReactive 与 reactive
|
||||
|
||||
```js
|
||||
const reactiveHandler = {
|
||||
get (target, key) {
|
||||
|
||||
if (key==='_is_reactive') return true
|
||||
|
||||
return Reflect.get(target, key)
|
||||
},
|
||||
|
||||
set (target, key, value) {
|
||||
const result = Reflect.set(target, key, value)
|
||||
console.log('数据已更新, 去更新界面')
|
||||
return result
|
||||
},
|
||||
|
||||
deleteProperty (target, key) {
|
||||
const result = Reflect.deleteProperty(target, key)
|
||||
console.log('数据已删除, 去更新界面')
|
||||
return result
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
自定义shallowReactive
|
||||
*/
|
||||
function shallowReactive(obj) {
|
||||
return new Proxy(obj, reactiveHandler)
|
||||
}
|
||||
|
||||
/*
|
||||
自定义reactive
|
||||
*/
|
||||
function reactive (target) {
|
||||
if (target && typeof target==='object') {
|
||||
if (target instanceof Array) { // 数组
|
||||
target.forEach((item, index) => {
|
||||
target[index] = reactive(item)
|
||||
})
|
||||
} else { // 对象
|
||||
Object.keys(target).forEach(key => {
|
||||
target[key] = reactive(target[key])
|
||||
})
|
||||
}
|
||||
|
||||
const proxy = new Proxy(target, reactiveHandler)
|
||||
return proxy
|
||||
}
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
|
||||
/* 测试自定义shallowReactive */
|
||||
const proxy = shallowReactive({
|
||||
a: {
|
||||
b: 3
|
||||
}
|
||||
})
|
||||
|
||||
proxy.a = {b: 4} // 劫持到了
|
||||
proxy.a.b = 5 // 没有劫持到
|
||||
|
||||
|
||||
/* 测试自定义reactive */
|
||||
const obj = {
|
||||
a: 'abc',
|
||||
b: [{x: 1}],
|
||||
c: {x: [11]},
|
||||
}
|
||||
|
||||
const proxy = reactive(obj)
|
||||
console.log(proxy)
|
||||
proxy.b[0].x += 1
|
||||
proxy.c.x[0] += 1
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 2) shallowRef 与 ref
|
||||
|
||||
```js
|
||||
/*
|
||||
自定义shallowRef
|
||||
*/
|
||||
function shallowRef(target) {
|
||||
const result = {
|
||||
_value: target, // 用来保存数据的内部属性
|
||||
_is_ref: true, // 用来标识是ref对象
|
||||
get value () {
|
||||
return this._value
|
||||
},
|
||||
set value (val) {
|
||||
this._value = val
|
||||
console.log('set value 数据已更新, 去更新界面')
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
自定义ref
|
||||
*/
|
||||
function ref(target) {
|
||||
if (target && typeof target==='object') {
|
||||
target = reactive(target)
|
||||
}
|
||||
|
||||
const result = {
|
||||
_value: target, // 用来保存数据的内部属性
|
||||
_is_ref: true, // 用来标识是ref对象
|
||||
get value () {
|
||||
return this._value
|
||||
},
|
||||
set value (val) {
|
||||
this._value = val
|
||||
console.log('set value 数据已更新, 去更新界面')
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/* 测试自定义shallowRef */
|
||||
const ref3 = shallowRef({
|
||||
a: 'abc',
|
||||
})
|
||||
ref3.value = 'xxx'
|
||||
ref3.value.a = 'yyy'
|
||||
|
||||
|
||||
/* 测试自定义ref */
|
||||
const ref1 = ref(0)
|
||||
const ref2 = ref({
|
||||
a: 'abc',
|
||||
b: [{x: 1}],
|
||||
c: {x: [11]},
|
||||
})
|
||||
ref1.value++
|
||||
ref2.value.b[0].x++
|
||||
console.log(ref1, ref2)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 3) shallowReadonly 与 readonly
|
||||
|
||||
```js
|
||||
const readonlyHandler = {
|
||||
get (target, key) {
|
||||
if (key==='_is_readonly') return true
|
||||
|
||||
return Reflect.get(target, key)
|
||||
},
|
||||
|
||||
set () {
|
||||
console.warn('只读的, 不能修改')
|
||||
return true
|
||||
},
|
||||
|
||||
deleteProperty () {
|
||||
console.warn('只读的, 不能删除')
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
自定义shallowReadonly
|
||||
*/
|
||||
function shallowReadonly(obj) {
|
||||
return new Proxy(obj, readonlyHandler)
|
||||
}
|
||||
|
||||
/*
|
||||
自定义readonly
|
||||
*/
|
||||
function readonly(target) {
|
||||
if (target && typeof target==='object') {
|
||||
if (target instanceof Array) { // 数组
|
||||
target.forEach((item, index) => {
|
||||
target[index] = readonly(item)
|
||||
})
|
||||
} else { // 对象
|
||||
Object.keys(target).forEach(key => {
|
||||
target[key] = readonly(target[key])
|
||||
})
|
||||
}
|
||||
const proxy = new Proxy(target, readonlyHandler)
|
||||
|
||||
return proxy
|
||||
}
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
/* 测试自定义readonly */
|
||||
/* 测试自定义shallowReadonly */
|
||||
const objReadOnly = readonly({
|
||||
a: {
|
||||
b: 1
|
||||
}
|
||||
})
|
||||
const objReadOnly2 = shallowReadonly({
|
||||
a: {
|
||||
b: 1
|
||||
}
|
||||
})
|
||||
|
||||
objReadOnly.a = 1
|
||||
objReadOnly.a.b = 2
|
||||
objReadOnly2.a = 1
|
||||
objReadOnly2.a.b = 2
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 4) isRef, isReactive 与 isReadonly
|
||||
|
||||
```js
|
||||
/*
|
||||
判断是否是ref对象
|
||||
*/
|
||||
function isRef(obj) {
|
||||
return obj && obj._is_ref
|
||||
}
|
||||
|
||||
/*
|
||||
判断是否是reactive对象
|
||||
*/
|
||||
function isReactive(obj) {
|
||||
return obj && obj._is_reactive
|
||||
}
|
||||
|
||||
/*
|
||||
判断是否是readonly对象
|
||||
*/
|
||||
function isReadonly(obj) {
|
||||
return obj && obj._is_readonly
|
||||
}
|
||||
|
||||
/*
|
||||
是否是reactive或readonly产生的代理对象
|
||||
*/
|
||||
function isProxy (obj) {
|
||||
return isReactive(obj) || isReadonly(obj)
|
||||
}
|
||||
|
||||
|
||||
/* 测试判断函数 */
|
||||
console.log(isReactive(reactive({})))
|
||||
console.log(isRef(ref({})))
|
||||
console.log(isReadonly(readonly({})))
|
||||
console.log(isProxy(reactive({})))
|
||||
console.log(isProxy(readonly({})))
|
||||
```
|
||||
23
docs/chapter4/04_Composition VS Option.md
Normal file
23
docs/chapter4/04_Composition VS Option.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# 4. Composition API VS Option API
|
||||
|
||||
## 1) Option API的问题
|
||||
|
||||
- 在传统的Vue OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 ,滚动条反复上下移动
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 2) 使用Compisition API
|
||||
|
||||
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
|
||||

|
||||
@@ -1 +0,0 @@
|
||||
# TODO LIST
|
||||
Reference in New Issue
Block a user