Test
[HTML]
纯文本查看
复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <!-- # new packages\vue\index.html --> <div id="app"></div> <!-- 引入 vue.global.js --> <script src="./dist/vue.global.js"></script> <script> // reactive 响应式 const { createApp, reactive, computed, effect } = Vue // 组件定义 const XnComponent = { template: ` <button @click="increment">Count is {{ state.count }}, double is {{ state.double }}</button> `, // 数据处理 setup(){ const state = reactive({ count: 0, double: computed(() => state.count * 2) }) function increment(){ state.count++ } effect(() => { // 副作用 console.log('数字修改了', state.count) }) return { state, increment, } }, } // 创建app 挂载组件 XnComponent 到 #app createApp().mount(XnComponent, '#app') </script> |
Vue3 响应式原理 Proxy
test Proxy: MDN
监听(代理)数组 报错(Reflect处理)/ 二次触发 和 多次触发 get/set 问题
代理对象 对象嵌套问题, 不会触发深层次的 set 问题
[JavaScript]
纯文本查看
复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ///new Proxy(obj, handler)// Object 类型// let obj = {name: 'xn213'}let obj = {name: 'xn213', location: {city: '北京'}}// Array 类型// let obj = [1, 2, 3]let xn = new Proxy(obj, { get(target, key){ console.log('获取值:', key) // return target[key] return Reflect.get(target, key, ) }, set(target, key, val){ console.log('修改值:', key, val) // target[key] = val // return true // 这里return true 可简单解决数组报错的问题 return Reflect.set(target, key, val) }})// 对象// xn.name = 'xn2113'// console.log(xn.name) // xn2113 |
// 1. 对象嵌套 不触发
// 修改 location 的 city 不会触发city set(){}
// // 获取值: location
// // 获取值: location
// // 杭州
xn.location.city = '杭州'
console.log(o.location.city) // 杭州
// 2. 数组多次触发
// xn.push(4)
/** push从后面添加, 会触发两次
* 获取值 push
* 获取值 length
* 修改值 3 4
* 修改值 length 4
*/
// xn.unshift(4)
/** unshift 从前面添加, 就会触发不止两次
* 获取值 unshift
* 获取值 length
* 获取值 2
* 修改值 3 3
* 获取值 1
* 修改值 2 2
* 获取值 0
* 修改值 1 1
* 修改值 0 4
* 修改值 length 4
*/
手写 reactive()
// 设置两个缓存 旧查新 新查旧
[JavaScript]
纯文本查看
复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | let toProxy = new WeakMap() // 根据原始数据查询响应后的数据let toRaw = new WeakMap() // 根据响应后的数据查询原始数据// 定义 handler对象 > baseHandlerconst baseHandler = { set(target, key){ // 收集依赖 后返回数据 track(target, key) }, set(target, key, val){ // 触发更新 后返回数据 trigger(target, key, info) }}// 定义 reactivefunction reactive(target){ // 查询缓存 ... // 响应式 observed = new Proxy(target, baseHandler) // 设置缓存 ... // 返回(观测到的)响应后的对象 return observed} |
[JavaScript]
纯文本查看
复制代码
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | // 收集依赖function track(target, key){}// 触发更新function trigger(target, key, info){}复制代码↓function effect(fn, options={}){}复制代码↓function createReactiveEffect(fn, options){}复制代码↓function computed(fn){}复制代码simple little demo 完整: xn-vue3.jsdemo.html <div id="app"></div> <button id="btn">点我, 又长大了...</button> <script src="./xn-vue3.js"></script> <script> const root = document.querySelector('#app') const btn = document.querySelector('#btn') const obj = reactive({ name: '本猿', age: '213' }) let double = computed(() => obj.age * 2) effect(() => { root.innerHTML = ` <h1>${obj.name}今年${obj.age}岁了, 乘以2是${double.value}</h1> ` }) btn.addEventListener('click', () => { obj.age++ }, false) </script>复制代码xn-vue3.js// 设置两个缓存let toProxy = new WeakMap() // 根据原始数据查询响应后的数据let toRaw = new WeakMap() // 根据响应后的数据查询原始数据const isObject = val => val !== null && typeof val === 'object'// handler 对象,其属性是当执行一个操作时定义代理的行为的函数const baseHandler = { get(target, key){ const res = Reflect.get(target, key) // 收集依赖 track(target, key) // 递归查找 // 这里暂作简单判断, 可以严谨用isObject 工具 // return typeof res == 'object' ? reactive(res) : res return isObject(res) ? reactive(res) : res }, set(target, key, val){ const info = {oldValue: target[key], newValue: val} const res = Reflect.set(target, key, val) // 触发更新 trigger(target, key, info) return res }}function reactive(target){ // 查询缓存 let observed = toProxy.get(target) if(observed){ return observed } if(toRaw.get(target)){ return target } // 响应式 observed = new Proxy(target, baseHandler) // 设置缓存 toProxy.set(target, observed) toRaw.set(observed, target) return observed}let effectStack = []let targetMap = new WeakMap() // 存储 effect// {// target: {// age: [] (set),// name: [effect]// }// }function trigger(target, key, info){ // 触发更新 const depsMap = targetMap.get(target) if(depsMap === undefined){ return } const effects = new Set() const computedRunners = new Set() if(key){ let deps = depsMap.get(key) deps.forEach(effect => { if(effect.computed){ computedRunners.add(effect) }else{ effects.add(effect) } }) } // const run = effect => effect() effects.forEach(effect => effect()) computedRunners.forEach(effect => effect())}function track(target, key){ let effect = effectStack[effectStack.length - 1] if(effect){ let depsMap = targetMap.get(target) if(depsMap === undefined){ depsMap = new Map() targetMap.set(target, depsMap) } let dep = depsMap.get(key) if(dep === undefined){ dep = new Set() depsMap.set(key, dep) } if(!dep.has(effect)){ dep.add(effect) effect.deps.push(dep) } }}function effect(fn, options={}){ let e = createReactiveEffect(fn, options) // 没有考虑 computed e() return e}// 高阶函数function createReactiveEffect(fn, options){ const effect = function effect(...args){ return run(effect, fn, args) } effect.deps = [] effect.computed = options.computed effect.lazy = options.lazy return effect}function run(effect, fn, args){ if(effectStack.indexOf(effect) === -1){ try { effectStack.push(effect) return fn(...args) } finally { effectStack.pop() // 清空 } }}function computed(fn){ // 首次不运行computed const runner = effect(fn, {computed: true, lazy: true}) return { effect: runner, get value(){ return runner() } }} |
链接:https://juejin.cn/post/6844904001646002183