vue原理

Vue原理

未經(jīng)允許 禁止轉(zhuǎn)載

MVVM 數(shù)據(jù)驅(qū)動視圖

傳統(tǒng)組件只是靜態(tài)渲染,更新還要依賴于操作DOM

vue MVVM听绳,數(shù)據(jù)驅(qū)動視圖

react setState醒叁,數(shù)據(jù)驅(qū)動視圖

MVVM:Modev-View-ViewModel

1. Model:可以理解為vue里的data對象

2. View:頁面臂寝,DOM

3. ViewModel:Vue層送淆,處理一些DOM監(jiān)聽税产、指令操作等

MVVM就是通過vue監(jiān)聽怕轿、指令操作等方法修改data進(jìn)而渲染頁面


Vue響應(yīng)式

vue3.0之前

核心API:Object.defineProperty

Object.defineProperty基本用法:

var data = {}
var name = 'zhangsan'
Object.defineProperty(data,'name',{
    get:function(){
        console.log('get')
        return name
    },
    set:function(newVal){
        console.log('set')
        name = newVal
    }
})
console.log(data.name)  //get zhangsan
data.name = 'liu'       //set
console.log(data.name)  //get liu

功能稍完整的Object.defineProperty:

//定義data
const data = {
    name: 'zhangsan',
    age: 14,
    city:{      //需要深度監(jiān)聽
        id: 010,
        name: '北京'
    }
}
//監(jiān)聽函數(shù)
function observer(target) {
    //判斷傳入對象參數(shù)是否為object
    if (typeof target !== 'object' || target == null) {
        return target
    }
    //對傳入對象進(jìn)行for in遍歷
    for (let key in target) {
        //target--對象;key--對象屬性;target[key]--屬性值
        defineProperty(target, key, target[key])
    }
}

//obj.defineProperty
function defineProperty(target, key, value) {
    //深度監(jiān)聽
    observer(value)

    Object.defineProperty(target, key, {
        get() {
            return value
        },
        set(newVal) {
            if (newVal !== value) {
                //深度監(jiān)聽
                observer(value)

                value = newVal
                update()
            }
        }
    })
}

//update
function update() {
    console.log('update')
}
observer(data)
data.name = 'liu'
data.age = 15

Object.defineProperty的缺點:

1. 深度監(jiān)聽需要遞歸到底偷崩,一次性計算量大。
2. 無法監(jiān)聽新增/刪除屬性撞羽。(需要用Vue.set/Vue.delete)

虛擬DOM和diff算法

1. vdom是實現(xiàn)vue和react的重要基石

2. diff算法是vdom中最核心阐斜、最關(guān)鍵的部分

用JS模擬DOM結(jié)構(gòu)

<!-- DOM -->
<div class='container' id='div1'>
   <p>vdom</p>
   <ul style='font-size: 14px;'>
       <li>a</li>
   </ul>
</div>



//用JS模擬DOM
{
   tag: 'div',
   props:{
       id: 'div1',
       className: 'container'
   },
   children: [
       {
           tag: 'p',
           children: 'vdom'
       },
       {
           tag: 'ul',
           props: {
               style: 'font-size: 14px'
           },
           children: [
               {
                   tag: 'li',
                   children: 'a'
               }
           ]
       }
   ]
}

vue、react的vdom參考了snabbdom

//snabbdom:
var container = document.getElementById('container')
var vnode = h(...)    snabbdom通過h方法模擬出DOM結(jié)構(gòu)賦值給vnode
patch(container,vnode)  //把vnode結(jié)構(gòu)賦值給container

snabbdom重點:1诀紊、h函數(shù)谒出;2、vnode數(shù)據(jù)結(jié)構(gòu)邻奠;3笤喳、patch函數(shù)

vdom總結(jié):

1. 用JS模擬DOM結(jié)構(gòu)(vnode)

2. 新舊vnode進(jìn)行對比,得出最小的更新范圍碌宴,最后更新DOM

3. 數(shù)據(jù)驅(qū)動視圖的模式下杀狡,有效控制DOM操作

diff算法

樹diff的時間復(fù)雜度O(n的3次方)

  1. 遍歷tree1
  2. 遍歷tree2
  3. 排序

1000個節(jié)點要計算1億次

優(yōu)化時間復(fù)雜度到O(n)

  1. 只比較同一層級,不跨級比較
  2. tag不相同贰镣,則直接刪除重建呜象,不再深度比較
  3. tag和key膳凝,兩者都相同,則認(rèn)為是相同節(jié)點恭陡,不再深度比較

1000個節(jié)點只需計算1000次

snabbdom源碼解讀

1. h函數(shù)

h函數(shù)一般接受3個參數(shù):sel(元素標(biāo)簽)蹬音、data(元素屬性)、children(子內(nèi)容)休玩。也可單獨接受其中一兩個參數(shù)著淆。

h函數(shù)返回執(zhí)行一個vnode函數(shù),參數(shù)包括sel,data,children,text(如果children為字符串拴疤,則用text顯示該字符串),undefined

2. vnode函數(shù)

返回一個對象牧抽,包含sel、data遥赚、children扬舒、text、elm(該DOM節(jié)點)凫佛、key

3. patch函數(shù)

  1. 接受的第一個參數(shù)為element||vnode讲坎,第二個參數(shù)為vnode。
  2. 執(zhí)行pre hook生命周期愧薛。
  3. 判斷第一個參數(shù)是否為vnode晨炕,不是的話(傳入的第一個參數(shù)為一個DOM)則創(chuàng)建一個空vnode關(guān)聯(lián)到這個DOM元素。

4. sameVnode函數(shù)

執(zhí)行sameVnode判斷兩個vnode的key和ele是否都相等毫炉,相等的話則執(zhí)行patchVnode函數(shù)瓮栗,不相同則刪除銷毀舊的vnode,然后用新的vnode重建瞄勾。

5. patchVnode函數(shù)

  1. 執(zhí)行prepatch hook生命周期鉤子费奸。
  2. 設(shè)置新vnode的ele,把舊vnode的ele賦給新的进陡。
  3. 判斷新舊children

6. addVnodes函數(shù)

有舊的children愿阐,沒有新的,則添加vnode

7. removeVnodes函數(shù)

沒有舊的children趾疚,有新的缨历,則移除vnode

8. updateVnode函數(shù)

對比children,如開始和開始作對比糙麦,如果相同辛孵,則執(zhí)行patchVnode函數(shù),執(zhí)行后index會進(jìn)行累加或者累減赡磅,直到對比完成魄缚。

如果幾種對比方式(start-start,end-end,start-end,end-start)都未命中,則用key和sel進(jìn)行對比仆邓,如果都相等則執(zhí)行patchVnode函數(shù)鲜滩。

從這里可以看出v-for使用key的重要性伴鳖,不使用key的話無法做對比,直接銷毀舊的創(chuàng)建新的徙硅,另外key如果是隨機(jī)數(shù)或者index則也無法對比榜聂。所以key是有必要寫的且不能亂寫

diff算法總結(jié)

1. patchVnode

2. addVnodes removeVnodes

3. updateChildren(key的重要性,可回答v-for為什么要有key:因為vdom的diff算法會對比select節(jié)點和key是否相同嗓蘑,相同則繼續(xù)深入對比须肆,不相同則重建,所以key是必要的)

vdom和diff算法總結(jié)

vdom核心概念很重要:h桩皿、vnode豌汇、patch、diff泄隔、key等拒贱。

vdom存在價值更重要:數(shù)據(jù)驅(qū)動視圖,控制DOM操作佛嬉。


模板編譯

with語法

const obj = {
   a: 1,
   b: 2
}
console.log(obg.a)
console.log(obg.b)
console.log(obg.c)  //undefined


//使用with語法逻澳,打破了作用域規(guī)則,能改變{}內(nèi)自由變量的查找方式
//將自由變量當(dāng)做obj的屬性來查找
with(obj){
   console.log(a)  //1
   console.log(b)  //2
   console.log(c)  //報錯
}

1.模板不是html暖呕,因為包含一些指令斜做、插值,直接放在瀏覽器里是不能執(zhí)行的

2.html是標(biāo)記性語言湾揽,只有js才能實現(xiàn)判斷循環(huán)

3.因此瓤逼,模板一定是轉(zhuǎn)換成js代碼,即模板編譯

模板編譯流程

  1. 模板編譯為rander函數(shù)库物,執(zhí)行rander函數(shù)返回vnode
  2. 基于vnode再執(zhí)行patch和diff
  3. 使用webpack vue-loader霸旗,會在開發(fā)環(huán)境下編譯模板

使用rander代替template

Vue.component('component',{
    rander: function(createElement){    
        return createElment(    //vnode
            'h'+this.level,
            [
                createElement('a',{
                    attrs:{
                        name: 'headerId',
                        href: '#'+'headerId'
                    }
                },'this is a tag')
            ]
        )
    }
})

模板編譯總結(jié)

  1. with語法
  2. 模板到rander函數(shù),再到vnode艳狐,再到渲染和更新
  3. vue組件可以使用rander替代template

組件 渲染/更新過程

涉及vue原理三大知識點:

1.響應(yīng)式:監(jiān)聽data屬性 getter setter

2.模板編譯: 模板到rander函數(shù) 再到vnode

3.vdom: patch(elm,vnode)和patch(vnode,newVnode)

1. 初次渲染過程

1.解析模板為rander函數(shù)(在webpack的vue-loader定硝、vue-cli環(huán)境下已完成)

2.觸發(fā)響應(yīng)式,監(jiān)聽data屬性getter setter

3.執(zhí)行rander函數(shù)生成vnode毫目,執(zhí)行patch(elem,vnode)

2. 更新過程

1.修改data,觸發(fā)setter(此前在getter已經(jīng)被監(jiān)聽)

2.重新執(zhí)行rander函數(shù)生成newVnode诲侮,執(zhí)行patch(vnode,newVnode)

3. 異步渲染

vue是異步渲染镀虐,修改data一次性提交,能提高性能

$nextTick相關(guān)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沟绪,一起剝皮案震驚了整個濱河市刮便,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绽慈,老刑警劉巖恨旱,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辈毯,死亡現(xiàn)場離奇詭異,居然都是意外死亡搜贤,警方通過查閱死者的電腦和手機(jī)谆沃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仪芒,“玉大人唁影,你說我怎么就攤上這事〉嗝” “怎么了据沈?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長饺蔑。 經(jīng)常有香客問我锌介,道長,這世上最難降的妖魔是什么猾警? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任掏湾,我火速辦了婚禮,結(jié)果婚禮上肿嘲,老公的妹妹穿的比我還像新娘融击。我一直安慰自己,他們只是感情好雳窟,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布尊浪。 她就那樣靜靜地躺著,像睡著了一般封救。 火紅的嫁衣襯著肌膚如雪拇涤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天誉结,我揣著相機(jī)與錄音鹅士,去河邊找鬼。 笑死惩坑,一個胖子當(dāng)著我的面吹牛掉盅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播以舒,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼趾痘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蔓钟?” 一聲冷哼從身側(cè)響起永票,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后侣集,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體键俱,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年世分,在試婚紗的時候發(fā)現(xiàn)自己被綠了编振。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡罚攀,死狀恐怖党觅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斋泄,我是刑警寧澤杯瞻,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站炫掐,受9級特大地震影響魁莉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜募胃,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一旗唁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痹束,春花似錦检疫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至论巍,卻和暖如春烛谊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嘉汰。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工丹禀, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鞋怀。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓双泪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親接箫。 傳聞我的和親對象是個殘疾皇子攒读,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

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