文章目录
组合式API
优点:
关键词:复用、独立封装
同样功能代码集中,可复用;
数据响应系统独立封装使用方便,数据流转清晰,代码可读性强;
使用示例:
<script type="module">
import { createApp, reactive, onMounted, onUnmounted, toRefs } from './node_modules/vue/dist/vue.esm-browser.js'
const app = createApp({ // createApp({}):创建APP实例
// data() {} 必为函数,不接受对象
setup () {
// setup() ,composition api 的入口,在beforeCreated\created之间实现,所以相关数据处理均可放置于setup中;
// setup的参数:(props, context = {attrs, emit, slots})
// const position = useMousePosition()
const { x, y } = useMousePosition()
return { // 返回对象在组件任意位置可以使用
x,
y
}
}
})
console.log(app)
const position = reactive({ // reactive({}):创建响应式对象
x: 0,
y: 0
})
app.mount('#app')
</script>
生命周期钩子函数
选项 API 生命周期选项和组合式 API 之间的映射
beforeCreate -> use setup()
created -> use setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeUnmount -> onBeforeUnmount
unmounted -> onUnmounted(destroyed => unmounted)
errorCaptured -> onErrorCaptured
renderTracked -> onRenderTracked (render函数调用触发调用,包含首次触发)
renderTriggered -> onRenderTriggered (render函数调用触发调用,不包含首次触发)
三个响应式函数简述:reactive-ref-toRefs
关键词:收集依赖track、触发依赖trigger、effect定义依赖、发布-订阅者模式;
思想:
同vue2自定义事件一样是“发布-订阅者模式”(watch为“观察者模式”),定义全局变量targetMap( new WeakMap() )作为调度中心,结构为:
WeakMap {
target: Map{
key: Set [effect1(), ......, effectn()]
}
}
targetMap:new WeakMap(), key目标对象,value -> depsMap;弱引用,失去引用后可以销毁
depsMap:new Map(), key目标对象的属性名称,value -> dep;
dep:new Set(), value -> effect函数
reactive
对target进行代理,属性的下一层在获取get时再代理处理;
- 接收一个参数,判断是否为对象
- 创建proxy对象handler,设置get、set、deleteProperty;get时调用track收集依赖,set时调用trigger触发依赖;
- 返回proxy对象
ref
可把基本类型数据转为响应式对象;const count = ref(0) // 返回一个对象,{value: 0}
- 参数为基本类型值的话,内部会创建一个只有value属性的对象,改对象value属性具有getter(收集依赖)和setter(触发更新)参数为对象,内部调用reactive;
ref() // undefined
ref([]) // 空数组
- 参数为对象,内部调用reactive;
ref和reactive对比:
reactive | ref | |
---|---|---|
参数 | 对象 | 其他基本类型 |
返回 | 响应式对象(不可解构) | {value: ‘xx’}的响应式对象,操作数据’.value’ |
重新赋值对象 | 丢失响应式 | 保持响应式 |
toRefs
将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的ref。代理对象被解构的时候,相当于定义对应key值变量来接收对象的key值;而基本类型赋值,就是在内存中再复制一份,所以此处被解构的x\y是两个基本变量,与代理对象无关;
- 内部创建一个新的对象
- 遍历传入的代理对象的所有属性,值都转为响应式对象;
- 挂载到新的对象上,然后返回新的对象
简单实现:
effect定义时触发track收集依赖;
const isObject = val => typeof val === 'object' && val !== null
const convert = val => isObject(val) ? reactive(val): val
const hasOwnProperty = Object.prototype.hasOwnProperty
const hasOwn = (target, key) => hasOwnProperty.call(target, key)
let activeEffect = null
let targetWeakMap = new WeakMap()
/*
1、判断是否是对象,不是的话直接返回
2、创建一个handler对象,返回一个proxy对象new Proxy(target, handler)
*/
export function reactive (target) {
if(!isObject(target)) return target
const handler = {
get: function(target, key, receiver){
// 1、收集依赖;
// 2、返回Reflect映射后的数据,某属性可能是对象,对其进行下一层处理,即获取普通value或reactive后的value;
track(target, key)
const result = Reflect.get(target, key, receiver)
return convert(result)
},
set: function(target, key, value, receiver){
// 1、value对比是否改变,无改变直接返回,改变进行下一步
// 2、调整value,通知依赖
const oldV = Reflect.get(target, key, receiver); // getter this指向receiver
if(oldV === value) return true;
const res = Reflect.set(target, key, value, receiver)
trigger(target, key)
return res
},
deleteProperty: function(target, key){
const hadKey = hasOwn(target, key)
const result = Reflect.deleteProperty(target, key) // true:属性存在删除成功 + 属性不存在;false:属性unconfigurable,如Object.freeze({foo: 1})处理过的属性
if (hadKey && result) {
// 触发更新
trigger(target, key)
}
return result
}
}
return new Proxy(target, handler)
}
/*
考虑到ref操作value不是很便利,常再次封装处理一层value
1、ref过的响应式对象ret
2、target是对象 -> reactive;其他,返回一个 对象 r,操作对象的value值
*/
export function ref(raw){
if(isObject(raw) && raw.__is_ref) return
let value = convert(raw) // 如果是对象,reactive
const r = {
__is_ref: true,
get value() {
track(r, 'value') // track 的 target 为 r,返回的对象;
return value;
},
set value(newV) {
if(newV === value) return true;
raw = newV;
value = convert(raw) // 对raw赋值,然后重新转换raw,得到的值再复制给value
trigger(r, 'value')
return true;
}
}
return r;
}
/*
实现一个reactive之后的proxy(数组、obj)的每个属性都变成响应式,借由ref(任何类型)
params: proxy
return: 属性处理过响应式的obj
*/
export function toRefs(proxy){
const ret = proxy instanceof Array ? new Array(proxy.length): {}
for(let key in proxy){
ret[key] = toProxyRef(proxy, key)
}
return ret
}
export function toProxyRef(proxy, key){
const r = {
__is_ref: true,
get value() {
return proxy[key];
},
set value(newV) {
proxy[key] = newV
return true;
}
}
return r;
}
export function computed(getter){
const result = ref()
effect(() => result.value = getter())
return result
}
// effect生效之后,删除属性,再赋值属性,effect依旧有效;即deleteProperty并不会删除依赖
export function effect(callback) {
activeEffect = callback
callback() // 执行回调,会访问get响应式对象属性,track
activeEffect = null
}
export function track(target, key){
/*
1、没有依赖可收集,直接return;
2、收集依赖 weakMap - Map - set;无对应key创建对应下一级变量类型并赋值,有则到set收集
*/
if(!activeEffect) return
let depsMap = targetWeakMap.get(target)
if(!depsMap) targetWeakMap.set(target, (depsMap = new Map()))
let deps = depsMap.get(key)
if(!deps) depsMap.set(key, (deps = new Set()))
deps.add(activeEffect)
}
export function trigger(target, key){
/*
1、查找target\key的依赖是否存在,存在则循环触发,不存在则return
*/
let depsMap = targetWeakMap.get(target)
if(!depsMap) return
let deps = depsMap.get(key)
if(!deps) return
deps.forEach(effect => {
effect()
});
}
Proxy、Reflect使用
- Reflect,反射,用来获取或设置对象中的成员;如果Reflect和Object有相同方法,建议使用Reflect;
- Proxy第二个参数对象中set、deleteProperty中需返回布尔类型的值,严格模式下,如果不返回值,默认返回undefined即false会出现Type Error异常;
- Proxy和Reflect的receiver参数
Proxy中的receiver:Proxy或者继承Proxy的对象
Reflect中的receiver:如果target对象中设置了getter,getter中的this指向receiver
vue3中receiver都会传递,为保证this获取值正确;
vue3其他响应式组件:
computed
接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。
简化模板的代码,缓存计算的结果,当数据变化再重新计算;
const activeCount = computed(() => {
return todos.filter(item => !item.completed).length
})
console.log('activeCount.value ---> ', activeCount.value); // activeCount.value ---> 2
watch
监听器:监听响应式数据变化,执行回调
参数:(data, handler, {deep, immediate})
返回值:取消监听的函数
第一个参数调整:侦听器数据源可以是一个具有返回值的 getter 函数,也可以直接是一个 ref;数组包含多个源
watchEffect
watch的简化版本,也用来监视数据的变化
接收一个函数作为参数,监听函数内响应式数据的变化;无第二个回调函数参数;
返回值:取消监听的函数