文章目錄
- 發布訂閱模式優化
- 優化思路思考
- 理解發布訂閱模式(自定義事件)
- 收集更新函數
- 觸發更新函數
- 6.5 總結
- 總結
- 寫在最后
- 本期推薦
歡迎各位小伙伴們!
為大家推薦一款刷題神奇哦 點擊鏈接訪問牛客網
各大互聯網大廠面試真題。從基礎到入階乃至原理刨析類面試題 應有盡有,趕快來裝備自己吧!助你面試穩操勝券,solo全場面試官
上節中我們提到下方更新的問題:無法做到精準更新
<div id="app"><p v-text="name"></p><p v-text="age"></p><p v-text="name"></p>
</div>
<script>let data = {name: '小蘭同學',age: 18,height: 180}// 遍歷每一個屬性Object.keys(data).forEach((key) => {// key 屬性名// data[key] 屬性值// data 原對象defineReactive(data, key, data[key])})function defineReactive(data, key, value) {Object.defineProperty(data, key, {get() {return value},set(newVal) {// 數據發生變化,操作dom進行更新if (newVal === value) {return}value = newValcompile()}})}// 編譯函數function compile() {let app = document.getElementById('app')// 1.拿到app下所有的子元素const nodes = app.childNodes // [text, input, text]//2.遍歷所有的子元素nodes.forEach(node => {// nodeType為1為元素節點if (node.nodeType === 1) {const attrs = node.attributesArray.from(attrs).forEach(attr => {const dirName = attr.nodeNameconst dataProp = attr.nodeValueconsole.log( dirName,dataProp)if (dirName === 'v-text') {console.log(`更新了${dirName}指令,需要更新的屬性為${dataProp}`)node.innerText = data[dataProp]}})}})}// 首次渲染compile()
</script>
發布訂閱模式優化
優化思路思考
1.數據更新之后實際上需要執行的代碼是什么?
node.innerText = data[dataProp]
為了保存當前的node和dataProp,我們再次設計一個函數執行利用閉包函數將每一次編譯函數執行時候的node和dataProp都緩存下來,所以每一次數據變化之后執行的是這樣的一個更新函數
() => {node.innerText = data[dataProp]
}
2.一個響應式數據可能會有多個視圖部分都需要依賴,也就是響應式數據變化之后,需要執行的更新函數可能不止一個,如下面的代碼所示,name屬性有倆個div元素都使用了它,所以當name變化之后,倆個div節點都需要得到更新,那屬性和更新函數之間應該是一個一對多的關系
<div id="app"><div v-text="name"></div><div v-text="name"></div><p v-text="age"></p><p v-text="age"></p>
</div><script>let data = {name: 'cp',age: 18}
</script>
經過分析我們可以得到下面的存儲架構圖,每一個響應式屬性都綁定了相對應的更新函數,是一個一對多的關系,數據發生變化之后,只會再次執行和自己綁定的更新函數
理解發布訂閱模式(自定義事件)
理解發布訂閱,關鍵是理解
一對多
1. 從瀏覽器事件說起
dom綁定事件的方式,我們學過倆種
- dom.onclick = function(){}
- dom.addEventListener(‘click’,()=>{})
這倆種綁定方式的區別是,第二種方案可以實現同一個事件綁定多個回調函數,很明顯這是一個一對多的場景,既然瀏覽器也叫作事件,我們試著分析下瀏覽器事件綁定實現的思路
-
首先addEventListenr是一個函數方法,接受倆個參數,分別是
事件類型
和回調函數
-
因為是一個事件綁定多個回調函數,那在內存里大概會有這樣的一個數據結構
{click: ['cb1','cb2',....],input: ['cb1','cb2',...] }
-
觸發事件執行,瀏覽器因為有鼠標鍵盤輸入可以觸發事件,大概的思路是通過事件名稱找到與之關聯的回調函數列表,然后遍歷執行一遍即可
ok,我們分析了瀏覽器事件的底層實現思路,那我們完全可以自己模仿一個出來,事件的觸發,我們也通過設計一個方法來執行
2. 實現簡單的發布訂閱
// 增加dep對象 用來收集依賴和觸發依賴
const dep = {map: Object.create(null),// 收集collect(dataProp, updateFn) {if (!this.map[dataProp]) {this.map[dataProp] = []}this.map[dataProp].push(updateFn)},// 觸發trigger(dataProp) {this.map[dataProp] && this.map[dataProp].forEach(updateFn => {updateFn()})}
}
收集更新函數
在編譯函數執行的時候,我們把用于更新dom的更新函數收集起來
// 編譯函數function compile() {let app = document.getElementById('app')// 1.拿到app下所有的子元素const nodes = app.childNodes // [text, input, text]//2.遍歷所有的子元素nodes.forEach(node => {// nodeType為1為元素節點if (node.nodeType === 1) {const attrs = node.attributes// 遍歷所有的attrubites找到 v-modelArray.from(attrs).forEach(attr => {const dirName = attr.nodeNameconst dataProp = attr.nodeValueconsole.log(dirName, dataProp)if (dirName === 'v-text') {console.log(`更新了${dirName}指令,需要更新的屬性為${dataProp}`)node.innerText = data[dataProp]// 收集更新函數dep.collect(dataProp, () => {node.innerText = data[dataProp]})}})}})}
觸發更新函數
當屬性發生變化的時候,我們通過屬性找到對應的更新函數列表,然后依次執行即可
function defineReactive(data, key, value) {Object.defineProperty(data, key, {get() {return value},set(newValue) {// 更新視圖if (newValue === value) returnvalue = newValue// 再次編譯要放到新值已經變化之后只更新當前的keydep.trigger(key)}})
}
6.5 總結
- 了解了發布訂閱模式的基礎形態
- 了解發布訂閱可以解決什么樣的具體問題(精準更新)
總結
-
數據響應式的實現無非是對象屬性攔截,我們使用
Object.defineProperty
來實現,在vue3中使用Proxy
對象代理方案進行了優化,解決了Object.defineProperty存在的缺陷 -
observe
對象指的是把數據處理成響應式的對象watcher
指的其實就是數據變化之后的更新函數 (Vue中的watcher有兩種,一種是用來更新視圖的watcher,一種是通過watch配置項聲明的watcher)dep
指的就是使用發布訂閱實現的收集更新函數和觸發更新函數的對象 -
指令實現的核心無非是通過模板編譯找到標識然后把數據綁上去,等到數據變化之后再重新放一次
-
發布訂閱模式的本質是解決一對多的問題,在vue中實現數據變化之后的精準更新
寫在最后
?原創不易,還希望各位大佬支持一下\textcolor{blue}{原創不易,還希望各位大佬支持一下}原創不易,還希望各位大佬支持一下
👍 點贊,你的認可是我創作的動力!\textcolor{green}{點贊,你的認可是我創作的動力!}點贊,你的認可是我創作的動力!
?? 收藏,你的青睞是我努力的方向!\textcolor{green}{收藏,你的青睞是我努力的方向!}收藏,你的青睞是我努力的方向!
?? 評論,你的意見是我進步的財富!\textcolor{green}{評論,你的意見是我進步的財富!}評論,你的意見是我進步的財富!
本期推薦
【內容簡介】
本書以理論結合編程開發為原則,使用Python作為開發語言,講解化算法的原理和應用,詳細介紹了Python基礎、Gurobi 優化器、線性規劃、整數規劃、多目標優化、動態規劃、圖與網絡分析、智能優化算法。對于算法部分的每一種算法都包含原理和編程實踐,使讀者對化算法的認識更加深入。
本書分為 3 篇共 9 章。第 1 篇(第 1~3 章)是化算法與編程基礎:第 1 章介紹了什么是化算法及其在生產和生活中的應用;第 2章介紹Python編程基礎和Python數據分析庫及繪圖庫;第 3章講解Gurobi 優化器的基礎和高級特性。第 2篇(第 4~6章)是數學規劃方法:第 4章詳細講解線性規劃的知識,包括單純形法、內點法、列生成法、拉格朗日乘子法、對偶問題;第 5 章講解整數規劃解法的分支定界法和割平面法;第 6 章講解多目標優化的概念及基于單純形法的目標規劃法。第 3 篇(第 7~9 章)是啟發式算法:第 7 章介紹動態規劃算法;第 8 章講解圖與網絡分析,介紹*小生成樹、短路徑、網絡流、路徑規劃等問題的建模;第 9 章講解了粒子群算法和遺傳算法求解各種類型優化算法問題的方法。
本書內容豐富,實例典型,實用性強,適合各個層次從事化算法研究和應用的人員,尤其適合有一定算法基礎而沒有編程基礎的人員閱讀。
【作者簡介】
蘇振裕,廈門大學金融學碩士,現任SHEIN 智慧供應鏈資深算法工程師。知乎專欄《從推公式到寫代碼》作者,運籌優化論壇(optimize.fun)創建人。在大數據、人工智能、運籌優化和供應鏈方面,具有多年的相關算法研究應用經驗。