vue學(xué)習(xí)篇5之函數(shù)柯里化與渲染模型

概念
1旭贬、柯里化:一個函數(shù)原本有多個參數(shù)怔接,只傳入一個參數(shù),生成一個新函數(shù)稀轨,由新函數(shù)接收剩下的參數(shù)來運行得到結(jié)構(gòu)蜕提。
2、偏函數(shù):一個函數(shù)原本有多個參數(shù)靶端,只傳入一部分參數(shù),生成一個新函數(shù)凛膏,由新函數(shù)接收剩下的參數(shù)來運行得到結(jié)構(gòu)杨名,
3、高階函數(shù):一個函數(shù)參數(shù)是一個函數(shù)猖毫,該函數(shù)對參數(shù)這個函數(shù)進行加工台谍,得到一個函數(shù),這個加工函數(shù)就是高階函數(shù)吁断。

為什么要使用柯里化趁蕊?
為了提升性能,使用柯里化可以緩存一部分能力。
使用兩個案例來說明:
例1.判斷元素標簽
Vue本質(zhì)上是使用HTML的字符串作為模板的间护,將字符串的模板轉(zhuǎn)換為抽象語法樹(AST)雳攘,在轉(zhuǎn)換為VNode
第一階段:模板-----AST
第二階段:AST----VNode
第三階段:VNode----真實DOM
最耗性能的是字符串解析(模板----AST)

在Vue中每一個標簽可以是真正的HTML標簽,也可以是自定義組件任柜,是怎么區(qū)分的卒废?
在Vue源碼中其實將所有可以用的HTML標簽已經(jīng)存起來了

//假設(shè)這里只考慮幾個標簽
let tags='div,p,a,img,ul,li'.split(',')
//需要一個函數(shù)判斷一個標簽是否為內(nèi)置標簽
function isHTMLTag(tagName){
  tagName = tagName.toLowerCase()
  for(let i = 0; i < tags.length; i++){
    if (tagName === tags[i]) return true
  }
  return false
}
//如果有6種內(nèi)置標簽,而模板中有十個標簽需要判斷宙地,那么就需要執(zhí)行60次循環(huán)摔认。是很耗性能的


//vue中使用函數(shù)柯里化解決
let tags='div,p,a,img,ul,li'.split(',')
function makeMap(keys) {
  let set = {}
  tags.forEach(key => {  //一共就需要遍歷一次 不需要每次判斷時都調(diào)用
    set[key] = true
  })
  return function (tagName) {
    return !!set[tagName.toLowerCase()]  //!!當沒有找到返回undefined的時候 !!undefined轉(zhuǎn)為布爾值false
  }
}
let isHTMLTag = makeMap(tags) //返回函數(shù)
//isHTMLTag = function (tagName) {
//      return !!set[tagName.toLowerCase()]
//}
console.log(isHTMLTag('div'))  //true
console.log(isHTMLTag('ol'))  //false

2.虛擬DOM的render方法
簡化模擬Vue初始化頁面模板渲染

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewpoort" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>text</title>
</head>
<body>
    <div id="root">
        <div>
            <div>{{name}}</div>
            <div>{{age}}</div>
            <div>hello3</div>
            <ul>
                <li>1</li>
                <li>2</li>
                <li>3</li>
            </ul>
        </div>
    </div>
    <script>
        function JGVue (options) {
            this._data = options.data
            let elm =document.querySelector(options.el)  //vue中是字符串 這里是DOM(簡化的)
            this._template = elm
            this._parent = elm.parentNode  //拿到父元素
            this.mount()  //掛載
        }
        JGVue.prototype.mount = function () {
            //需要提供一個render方法生成虛擬DOM
            this.render = this.createRenderFn()  //帶有緩存

            this.mountComponent()
        }
        JGVue.prototype.mountComponent = function () {
            //執(zhí)行mountComponent()函數(shù)
            let mount =  () => {
                this.update(this.render())
            }
            mount.call(this)  //本質(zhì)應(yīng)該交給watcher來調(diào)用,但是還沒講到watcher
        }

        /*
         在真正的Vue中使用了二次提交的設(shè)計結(jié)構(gòu)
         1.在頁面中的DOM和虛擬DOM是一一一對應(yīng)的關(guān)系
         2.當數(shù)據(jù)改變先由AST和數(shù)據(jù)生成新的VNode
         3.將舊的VNode和新的VNode比較(diff算法)宅粥,更新
        */

        //這里是生成render函數(shù)参袱,目的是緩存抽象語法樹(這里使用虛擬DOM來模擬)
        JGVue.prototype.createRenderFn = function () {
            let ast = getVNode(this._template)  //拿到虛擬DOM
            //Vue中:將AST和數(shù)據(jù)data合成生成VNode
            //這里:帶有{{}}的VNode + date生成含有數(shù)據(jù)的VNode
            return function render () {
                //將帶有{{}}的VNode轉(zhuǎn)換為帶有數(shù)據(jù)的VNode
                let _tmp = combine(ast, this._data)
                return _tmp
            }
        }
        //將虛擬DOM渲染到頁面中,diff算法就在這里秽梅,將數(shù)據(jù)改動生成的VNode和之前的VNode作比較
        JGVue.prototype.update = function (vnode) {
            //簡化抹蚀,直接生把虛擬DOM轉(zhuǎn)換成真正的DOM 替換到頁面中去,不用diff算法
            //父元素.replaceChild(新元素, 舊元素)
            let realDOM = parseVNode(vnode)
            this._parent.replaceChild(realDOM, document.querySelector('#root'))
            //這是算法是不負責任的风纠,每次會將頁面中的DOM全部替換
        }

        /*虛擬DOM構(gòu)造函數(shù)*/
        class VNode {
            constructor (tag, data, value, type) {
                this.tag = tag && tag.toLowerCase()
                this.data = data
                this.value = value
                this.type = type
                this.children = []
            }
            appendChild (vnode) {
                this.children.push(vnode)
            }
        }
        /*由HTMLDOM生成虛擬DOM 將這個函數(shù)當做compiler函數(shù)*/
        function getVNode(node) {
            let nodeType = node.nodeType
            let _vnode = null
            if (nodeType === 1) { //元素
                let nodeName = node.nodeName
                let attrs = node.attributes
                let _attrsObj = {}
                for (let i = 0; i < attrs.length; i++) { //attrs[i]屬性節(jié)點(nodeType === 2)
                    _attrsObj[attrs[i].nodeName] = attrs[i].nodeValue
                }
                _vnode = new VNode(nodeName, _attrsObj, undefined, nodeType)

                //考慮node的子元素
                let childNodes = node.childNodes
                for (let i = 0; i < childNodes.length; i++) {
                    _vnode.appendChild(getVNode(childNodes[i]))  //遞歸
                }
            } else if (nodeType === 3) {  //文本
                _vnode = new VNode(undefined, undefined, node.nodeValue, nodeType)
            }
            return _vnode
        }

        /*由虛擬DOM轉(zhuǎn)換成真正的DOM*/
        function parseVNode (vnode) {
            let type = vnode.type
            let _node = null
            if (type === 3) {
                _node = document.createTextNode(vnode.value)  //創(chuàng)建文本節(jié)點
            } else if (type === 1) {
                _node = document.createElement(vnode.tag)

                //屬性
                let data = vnode.data
                Object.keys(data).forEach(key => {
                    let attrName = key
                    let attrValue = data[key]
                    _node.setAttribute(attrName, attrValue)
                })

                //子元素
                let children = vnode.children
                children.forEach(subvnode => {
                    _node.appendChild(parseVNode(subvnode))  //遞歸轉(zhuǎn)換子元素
                })
            }
            return _node
        }

        let kuohaoReg = /\{\{(.+?)\}\}/g  //匹配大括號表達式{{}}正則
        /*根據(jù)路徑訪問對象成員 {{a.b.c}}*/
        function getValueByPath (obj, path) {
            let paths = path.split('.')  //[a,b,c]
            let res = obj
            let prop
            while (prop = paths.shift()) {
                res = res[prop]
            }
            return res
        }
        /*將帶有{{}}的VNode與數(shù)據(jù)結(jié)合况鸣,得到填充數(shù)據(jù)的VNode。 模擬由抽象語法樹去生成VNode*/
        function combine (vnode, data) {
            let _tag = vnode.tag
            let _data = vnode.data
            let _value = vnode.value
            let _type = vnode.type
            let _children = vnode.children

            let _vnode = null
            if (_type === 3) {  //文本節(jié)點
                //對文本處理
                _value = _value.replace(kuohaoReg, function(_, g) {  //第一個參數(shù)返回{{name}}竹观, 第二個參數(shù)表示正則的n個組 這個/\{\{(.+?)\}\}/g只有一個組(.+?)镐捧,顯示大括號里的內(nèi)容name
                    return getValueByPath(data, g.trim())
                })
                _vnode = new VNode(_tag, _data, _value, _type)
            } else if (_type === 1) { //元素節(jié)點
                _vnode = new VNode(_tag, _data, _value, _type)
                //子元素
                _children.forEach(subvnode => {
                    _vnode.appendChild(combine(subvnode, data))
                })
            }
            return _vnode
        }

        let app = new JGVue({
            el: '#root',
            data: {
                name: '張三',
                age: 18
            }
        })
    </script>
</body>
</html>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市臭增,隨后出現(xiàn)的幾起案子懂酱,更是在濱河造成了極大的恐慌,老刑警劉巖誊抛,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件列牺,死亡現(xiàn)場離奇詭異,居然都是意外死亡拗窃,警方通過查閱死者的電腦和手機瞎领,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來随夸,“玉大人九默,你說我怎么就攤上這事”龆荆” “怎么了驼修?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我乙各,道長墨礁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任耳峦,我火速辦了婚禮恩静,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妇萄。我一直安慰自己蜕企,他們只是感情好,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布冠句。 她就那樣靜靜地躺著轻掩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪懦底。 梳的紋絲不亂的頭發(fā)上唇牧,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機與錄音聚唐,去河邊找鬼丐重。 笑死,一個胖子當著我的面吹牛杆查,可吹牛的內(nèi)容都是我干的扮惦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼亲桦,長吁一口氣:“原來是場噩夢啊……” “哼崖蜜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起客峭,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤豫领,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后舔琅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體等恐,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年备蚓,在試婚紗的時候發(fā)現(xiàn)自己被綠了课蔬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡郊尝,死狀恐怖购笆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情虚循,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站横缔,受9級特大地震影響铺遂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茎刚,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一襟锐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧膛锭,春花似錦粮坞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至奢入,卻和暖如春筝闹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腥光。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工关顷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人武福。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓议双,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捉片。 傳聞我的和親對象是個殘疾皇子平痰,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容