50行代碼的MVVM,感受閉包的藝術(shù),50行mvvm

我們先看一下運(yùn)行結(jié)果

163f3a61871d9c2c?imageslim

name 和 age 被響應(yīng)式的渲染出來,在 2s 后我們修改了 name 的值,同樣能在頁面正確更新。愛掏網(wǎng) - it200.com

我們來看一下最小化的 MVVM 的源碼

class Vue{
    constructor(opt){
        this.opt = opt
        this.observe(opt.data)
        let root = document.querySelector(opt.el)
        this.compile(root)
    }
    // 為響應(yīng)式對(duì)象 data 里的每一個(gè) key 綁定一個(gè)觀察者對(duì)象
    observe(data){ 
        Object.keys(data).forEach(key => {
            let obv = new Observer() 
            data["_"+key] = data[key]
            // 通過 getter setter 暴露 for 循環(huán)中作用域下的 obv,閉包產(chǎn)生
            Object.defineProperty(data, key, {
                get(){
                    Observer.target && obv.addSubNode(Observer.target);
                    return data['_'+key]
                }, 
                set(newVal){
                    obv.update(newVal)
                    data['_'+key] = newVal
                }
            })
        })
    }
    // 初始化頁面,遍歷 DOM,收集每一個(gè)key變化時(shí),隨之調(diào)整的位置,以觀察者方法存放起來    
    compile(node){
        [].forEach.call(node.childNodes, child =>{
            if(!child.firstElementChild && /\{\{(.*)\}\}/.test(child.innerHTML)){
                let key = RegExp.$1.trim()
                child.innerHTML = child.innerHTML.replace(new RegExp('\\{\\{\\s*'+ key +'\\s*\\}\\}', 'gm'),this.opt.data[key]) 
                Observer.target = child
                this.opt.data[key] 
                Observer.target = null
            }
            else if (child.firstElementChild) 
            this.compile(child)
        })
    }    
}
// 常規(guī)觀察者類
class Observer{
    constructor(){
        this.subNode = []    
    }
    addSubNode(node){
        this.subNode.push(node)
    }
    update(newVal){
        this.subNode.forEach(node=>{
            node.innerHTML = newVal
        })
    }
}
復(fù)制代碼

【重點(diǎn)分析】接下來梳理一下這段代碼的思路,順便了解一下 MVVM 閉包的藝術(shù):

  • [observe 函數(shù)]:首先我們會(huì)對(duì)需要響應(yīng)式的 data 對(duì)象進(jìn)行 for 循環(huán)遍歷,為 data 的每一個(gè) key 映射一個(gè)觀察者對(duì)象
    • 在 ES6 中,for 循環(huán)每次執(zhí)行,都可以形成閉包,因此這個(gè)觀察者對(duì)象就存放在閉包中
    • 閉包形成的本質(zhì)是 內(nèi)層作用域中堆地址暴露,這里我們巧妙的用 getter/setter 函數(shù)暴露了 for 循環(huán)里的觀察者
  • [compile 函數(shù)]:我們從根節(jié)點(diǎn)向下遍歷 DOM,遇到 mustache 形式的文本,則映射成 data.key 對(duì)應(yīng)的值,同時(shí)記錄到觀察者中
    • 當(dāng)遍歷到 {{xxx}} 形式的文本,我們正則匹配出其中的變量,將它替換成 data 中的值
    • 為了滿足后續(xù)響應(yīng)式的更新,將該節(jié)點(diǎn)存儲(chǔ)在 key 對(duì)應(yīng)的觀察者對(duì)象中,我們用 getter 函數(shù)巧妙的操作了閉包
  • 在頁面初次渲染之后,后續(xù)的 eventLoop 中,如果修改了 key 的值,實(shí)際會(huì)通過 setter 觸發(fā)觀察者的 update 函數(shù),完成響應(yīng)式更新

附上述演示案例的完整代碼 github地址


html lang="en">
head>
  meta charset="UTF-8">
  meta name="viewport" content="width=device-width, initial-scale=1.0">
head>
body>
    div id='app'>
        h3>姓名h3>
        p>{{name}}p>
        h3>年齡h3>
        p>{{age}}p>
    div>
body>
html>
script>
document.addEventListener('DOMContentLoaded', function(){
    let opt = {el:'#app', data:{name:'檢索中...', age:30}}
    let vm = new Vue(opt)
    setTimeout(() => {
        opt.data.name = '王永峰'
    }, 2000);
}, false)
class Vue{
    constructor(opt){
        this.opt = opt
        this.observe(opt.data)
        let root = document.querySelector(opt.el)
        this.compile(root)
    }
    // 為響應(yīng)式對(duì)象 data 里的每一個(gè) key 綁定一個(gè)觀察者對(duì)象
    observe(data){ 
        Object.keys(data).forEach(key => {
            let obv = new Observer() 
            data["_"+key] = data[key]
            // 通過 getter setter 暴露 for 循環(huán)中作用域下的 obv,閉包產(chǎn)生
            Object.defineProperty(data, key, {
                get(){
                    Observer.target && obv.addSubNode(Observer.target);
                    return data['_'+key]
                }, 
                set(newVal){
                    obv.update(newVal)
                    data['_'+key] = newVal
                }
            })
        })
    }
    // 初始化頁面,遍歷 DOM,收集每一個(gè)key變化時(shí),隨之調(diào)整的位置,以觀察者方法存放起來    
    compile(node){
        [].forEach.call(node.childNodes, child =>{
            if(!child.firstElementChild && /\{\{(.*)\}\}/.test(child.innerHTML)){
                let key = RegExp.$1.trim()
                child.innerHTML = child.innerHTML.replace(new RegExp('\\{\\{\\s*'+ key +'\\s*\\}\\}', 'gm'),this.opt.data[key]) 
                Observer.target = child
                this.opt.data[key] 
                Observer.target = null
            }
            else if (child.firstElementChild) 
            this.compile(child)
        })
    }    
}
// 常規(guī)觀察者類
class Observer{
    constructor(){
        this.subNode = []    
    }
    addSubNode(node){
        this.subNode.push(node)
    }
    update(newVal){
        this.subNode.forEach(node=>{
            node.innerHTML = newVal
        })
    }
}
script>
復(fù)制代碼

代碼github地址


作者:maodayeyeye
原文發(fā)布時(shí)間:2024年06月12日 本文來源掘金如需轉(zhuǎn)載請(qǐng)緊急聯(lián)系作者
聲明:所有內(nèi)容來自互聯(lián)網(wǎng)搜索結(jié)果,不保證100%準(zhǔn)確性,僅供參考。如若本站內(nèi)容侵犯了原著者的合法權(quán)益,可聯(lián)系我們進(jìn)行處理。
發(fā)表評(píng)論
更多 網(wǎng)友評(píng)論0 條評(píng)論)
暫無評(píng)論

返回頂部

主站蜘蛛池模板: 韩国三级hd中文字幕| 欧美综合自拍亚洲综合图| 久久av高潮av无码av喷吹| 国产精品四虎在线观看免费| 男人的天堂av社区在线| 两个人看的视频播放www| 国产久热精品无码激情| 日本高清免费网站| 黄网站色视频大全免费观看| 九九热精品视频| 国产精品嫩草影院永久一| 欧美成a人片在线观看久| 91精品国产自产91精品| 亚洲精品国产精品乱码不卡√ | 日本电影100禁| 国产欧美日韩另类一区乌克兰| 亚洲国产欧美另类| 国产浮力第一影院| 日韩精品无码一本二本三本色| 欧美日韩你懂的| 无码专区aaaaaa免费视频| 欧美色图在线观看| 国产成人精品免费视频软件| 一本大道香蕉中文在线高清| 日韩一级二级三级| 免费无码又爽又刺激高潮| 国产亚洲成归v人片在线观看| 女人是男人的未来视频| 久久伊人成人网| 毛茸茸性XXXX毛茸茸毛茸茸| 又爽又黄又无遮挡网站| 麻豆tv入口在线看| 国产精品国产三级国快看| eeuss影院在线观看| 新婚之夜女警迎合粗大| 久久精品青草社区| 欧美换爱交换乱理伦片免费| 伊人青青草视频| 老熟妇高潮一区二区三区| 国产日韩欧美中文字幕| 337p色噜噜|