Vue


默认标题

<p><a href="https://juejin.cn/post/7090328834318270494#heading-7">https://juejin.cn/post/7090328834318270494#heading-7</a></p> <hr /> <p>我想实现一个场景,希望1秒后 app 的内容变成 hello Vue3</p> <pre><code class="language-javascript">const $app = document.querySelector('#app') let state = { text: 'hello fatfish' } function effect() { $app.innerText = state.text } effect() setTimeout(() =&amp;gt; { state.text = 'hello Vue3' }, 1000)</code></pre> <p>这个简单,只要拦截state对象,在对text进行取值时,收集effect函数依赖,然后text设置值时,把收集的effect函数执行一波就可以 核心只有两步:</p> <ul> <li>第一步:收集依赖(effect函数),在读取key时,将effect函数存储起来</li> <li>第二步:设置值时,将依赖(effect函数)执行</li> </ul> <pre><code class="language-javascript">const $app = document.querySelector('#app') const bucket = new Set() const state = new Proxy({ text: 'hello fatfish' }, { get (target, key) { const value = target[ key ] // 第一步:收集依赖,在读取key时,将effect函数存储起来 bucket.add(effect) console.log(`get ${key}: ${value}`) return value }, set (target, key, newValue) { console.log(`set ${key}: ${newValue}`) target[ key ] = newValue // 第二步:设置值时,将依赖执行 bucket.forEach((fn) =&amp;gt; fn()) } }) function effect() { console.log('执行了effect') $app.innerText = state.text } effect() setTimeout(() =&amp;gt; { state.text = 'hello Vue3' }, 1000) </code></pre> <p>功能是实现了,但这里收集依赖是写死的函数名字effect,只要稍微变化一下题目,就不行了</p> <pre><code class="language-javascript">&amp;lt;div id=&amp;quot;container&amp;quot;&amp;gt; &amp;lt;div id=&amp;quot;app1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;div id=&amp;quot;app2&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;/div&amp;gt; const $app1 = document.querySelector('#app1') const $app2 = document.querySelector('#app2') const state = { text: 'hello fatfish', text2: 'hello fatfish2' } // 改变app1的值 function effect1() { console.log('执行了effect') $app1.innerText = state.text } // 改变app2的值 function effect2() { console.log('执行了effect2') $app2.innerText = state.text2 } // 1秒钟之后两个div的值要分别改变 setTimeout(() =&amp;gt; { state.text = 'hello Vue3' state.text2 = 'hello Vue3-2' }, 1000) </code></pre> <p>大意了,应该把 effect 依赖函数通过某种机制,主动注册到桶中,这样无论你是匿名函数亦或者是具名函数都一视同仁</p> <pre><code class="language-javascript">const $app1 = document.querySelector('#app1') const $app2 = document.querySelector('#app2') const bucket = new Set() let activeEffect // 变化点: // 通过effect函数来主动收集依赖 const effect = function (fn) { // 每执行一次,将当前fn赋值给activeEffect,这样在fn中触发读取操作时,就可以被收集进bucket中了 activeEffect = fn // 主动执行一次很重要,必不可少 fn() } const state = new Proxy({ text: 'hello fatfish', text2: 'hello fatfish2' }, { get(target, key) { const value = target[key] // 变化点:由版本1的effect变成了activeEffect,从而不再依赖具体的函数名字 bucket.add(activeEffect) console.log(`get ${key}: ${value}`) return value }, set(target, key, newValue) { console.log(`set ${key}: ${newValue}`) target[key] = newValue bucket.forEach((fn) =&amp;gt; fn()) } }) effect(function effect1() { console.log('执行了effect1') $app1.innerText = state.text }) effect(function effect2() { console.log('执行了effect2') $app2.innerText = state.text2 }) setTimeout(() =&amp;gt; { state.text = 'hello Vue3' state.text2 = 'hello Vue3-2' }, 1000)</code></pre> <p>有个问题,给 state 增加一个之前不存在的属性,bucket 却会把收集的依赖执行一次,是不是有点浪费? 能否做到 effect 中依赖了 state 的什么值,其值改变了回调才会被执行?</p>

页面列表

ITEM_HTML