1. 程式人生 > >【Vue原理】Mixins

【Vue原理】Mixins

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧

【Vue原理】Mixins - 原始碼版

今天探索的是 mixins 的原始碼,mixins 根據不同的選項型別會做不同的處理

篇幅會有些長,你知道的,有很多種選項型別的嘛,但不是很難。只是涉及原始碼難免會有些煩,

不過這篇文章也不是給你直接看的,是為了可以讓你學習原始碼的時候提供微薄幫助而已

如果不想看原始碼的,可以看我的白話版

【Vue原理】Mixin - 白話版

我們也是要帶著兩個問題開始

1、什麼時候開始合併

2、怎麼合併

如果你覺得排版難看,請點選下面原文連結 或者 關注公眾號【神仙朱】


什麼時候合併

合併分為兩種

1、全域性mixin 和 基礎全域性options 合併

這個過程是先於你呼叫 Vue 時發生的,也是必須是先發生的。這樣mixin 才能合併上你的自定義 options

Vue.mixin = function(mixin) {
    this.options = mergeOptions(
        this.options, mixin
    );
    return this
};

基礎全域性options 是什麼?

就是 components,directives,filters 這三個,一開始就給設定在了 Vue.options 上。所以這三個是最先存在全域性options

Vue.options = Object.create(null);

['component','directive','filter'].forEach(function(type) {
    Vue.options[type + 's'] = Object.create(null);
});

這一步,是呼叫 Vue.mixin 的時候就馬上合併了,然後這一步完成 以後,舉個栗子

公眾號

全域性選項就變成下面這樣,然後每個Vue例項都需要和這全域性選項合併 公眾號

2、全域性options和 自定義options合併

在呼叫Vue 的時候,首先進行的就是合併

function Vue(options){
    vm.$options = mergeOptions(
        { 全域性component,
                 全域性directive,
                 全域性filter 等....},
        options , vm
    );

    // ...處理選項,生成模板,掛載DOM 等....
}

options 就是你自己傳進去的物件引數,然後跟 全域性options 合併,全域性options 是哪些,也已經說過了

公眾號


怎麼合併

上面的程式碼一直出現一個函式 mergeOptions,他便是合併的重點

來看原始碼

1、mergeOptions

function mergeOptions(parent, child, vm) {    

    // 遍歷mixins,parent 先和 mixins 合併,然後在和 child 合併
    if (child.mixins) {        

        for (var i = 0, l = child.mixins.length; i < l; i++) {
            parent = mergeOptions(parent, child.mixins[i], vm);
        }
    }    
    
    var options = {}, key;    

    // 先處理 parent 的 key,
    for (key in parent) {
        mergeField(key);
    }    

    // 遍歷 child 的key ,排除已經處理過的 parent 中的key
    for (key in child) {        
        if (!parent.hasOwnProperty(key)) {
            mergeField(key);
        }
    }    

    // 拿到相應型別的合併函式,進行合併欄位,strats 請看下面
    function mergeField(key) {    

        // strats 儲存著各種欄位的處理函式,否則使用預設處理
        var strat = strats[key] || defaultStrat;    

        // 相應的欄位處理完成之後,會完成合並的選項
        options[key] = strat(parent[key], child[key], vm, key);
    }    
    return options
}

這段程式碼看上去有點繞,其實無非就是

1、先遍歷合併 parent 中的key,儲存在變數options

2、再遍歷 child,合併補上 parent 中沒有的key,儲存在變數options

3、優先處理 mixins ,但是過程跟上面是一樣的,只是遞迴處理而已

在上面例項初始化時的合併, parent 就是全域性選項,child 就是元件自定義選項,因為 parent 權重比 child 低,所以先處理 parent 。

“公司開除程式猿,也是先開始作用較低。。”

重點其實在於 各式各樣的處理函式 strat,下面將會一一列舉

2、defaultStrats

這段函式言簡意賅,意思就是優先使用元件的options

元件options>元件 mixin options>全域性options

var defaultStrats= function(parentVal, childVal) {        
    return childVal === undefined ?        
            parentVal :
            childVal
};

3、data

我們先預設 data 的值是一個函式,簡化下原始碼 ,但是其實看上去還是會有些複雜

不過我們主要了解他的工作過程就好了

1、兩個data函式 組裝成一個函式

2、合併 兩個data函式執行返回的 資料物件

strats.data = function(parentVal, childVal, vm) {    

    return mergeDataOrFn(
        parentVal, childVal, vm
    )
};

function mergeDataOrFn(parentVal, childVal, vm) {    

    return function mergedInstanceDataFn() {        

        var childData = childVal.call(vm, vm) 

        var parentData = parentVal.call(vm, vm)        

        if (childData) {            

            return mergeData(childData, parentData)

        } else {            

            return parentData
        }
    }
}

function mergeData(to, from) {    

    if (!from) return to    

    var key, toVal, fromVal;    

    var keys = Object.keys(from);   

    for (var i = 0; i < keys.length; i++) {

        key = keys[i];
        toVal = to[key];

        fromVal = from[key];    

        // 如果不存在這個屬性,就重新設定
        if (!to.hasOwnProperty(key)) {
            set(to, key, fromVal);
        }      

        // 存在相同屬性,合併物件
        else if (typeof toVal =="object" && typeof fromVal =="object) {
            mergeData(toVal, fromVal);
        }
    }    
    return to
}

4、生命鉤子

把所有的鉤子函式儲存進陣列,重要的是陣列子項的順序

順序就是這樣

[    
    全域性 mixin - created,
    元件 mixin-mixin - created,
    元件 mixin - created,
    元件 options - created
]

所以當陣列執行的時候,正序遍歷,就會先執行全域性註冊的鉤子,最後是 元件的鉤子

function mergeHook(parentVal, childVal) {    

    var arr;

    arr = childVal ?  

        // concat 不只可以拼接陣列,什麼都可以拼接
        ( parentVal ?  
            // 為什麼parentVal 是個陣列呢

            // 因為無論怎麼樣,第一個 parent 都是{ component,filter,directive}
            // 所以在這裡,合併的時候,肯定只有 childVal,然後就變成了陣列
            parentVal.concat(childVal) : 

            ( Array.isArray(childVal) ? childVal: [childVal] )
        ) :
        parentVal  

    return arr

}

strats['created'] = mergeHook;
strats['mounted'] = mergeHook;
// ... 等其他鉤子

5、component、directives、filters

我一直覺得這個是比較好玩的,這種型別的合併方式,我是從來沒有在專案中使用過的

原型疊加

兩個物件並沒有進行遍歷合併,而是把一個物件直接當做另一個物件的原型

這種做法的好處,就是為了保留兩個相同的欄位且能訪問,避免被覆蓋

學到了學到了.....反正我是學到了

strats.components=
strats.directives=

strats.filters = function mergeAssets(
    parentVal, childVal, vm, key
) {    
    var res = Object.create(parentVal || null);    

    if (childVal) { 
        for (var key in childVal) {
            res[key] = childVal[key];
        }   
    } 
    return res
}

就是下面這種,層層疊加的原型

公眾號

6、watch

watch 的處理,也是合併成陣列,重要的也是合併順序,跟 生命鉤子一樣

這樣的鉤子

[    
    全域性 mixin - watch,
    元件 mixin-mixin - watch,
    元件 mixin - watch,
    元件 options - watch
]

按照正序執行,最後執行的 必然是元件的 watch

strats.watch = function(parentVal, childVal, vm, key) { 

    if (!childVal) {        
        return Object.create(parentVal || null)
    }    

    if (!parentVal)  return childVal

    var ret = {};    

    // 複製 parentVal 到 ret 中
    for (var key in parentVal) {
       ret[key] = parentVal[key];
    }    

    for (var key$1 in childVal) {        

        var parent = ret[key$1];        
        var child = childVal[key$1];        

        if (!Array.isArray(parent)) {
            parent = [parent];
        }
        ret[key$1] = parent ? parent.concat(child) : 
                ( Array.isArray(child) ? child: [child] );

    }    
    return ret
};

7、props、computed、methods

這幾個東西,是不允許重名的,合併成物件的時候,不是你死就是我活

重要的是,以誰的為主?必然是元件options 為主了

比如

元件的 props:{ name:""}

元件mixin 的 props:{ name:"", age: "" }

那麼 把兩個物件合併,有相同屬性,元件的 name 會替換 mixin 的name

trats.props = 
strats.methods = 
strats.inject = 

strats.computed = function(parentVal, childVal, vm, key) {    

    if (!parentVal) return childVal

    var ret = Object.create(null);   


    // 把 parentVal 的欄位 複製到 ret 中
    for (var key in parentVal) {
       ret[key] = parentVal[key];
    }    

    if (childVal) {        
        for (var key in childVal) {
           ret[key] = childVal[key];
        }
    }    

    return ret

};

其實在白話版裡面,就已經測試了很多例子,整個執行的流程也描述很清楚了,這裡就是放個原始碼供參考

相關推薦

Vue原理Mixins

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Mixins - 原始碼版 今天探索的是 mixins 的原始碼,mixins 根據不同的選項型別會做不同的處理 篇幅會有些長,你知道的,有很多種選項型別的嘛,但不是很難。只是涉及原始碼難免會有些煩, 不過這篇文

Vue原理Watch

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Watch - 原始碼版 今天繼續探索 Watch 原始碼,廢話不多說了 帶著我的幾個疑問開始 1、什麼時候初始化 2、怎麼確定監聽哪些值 3、深度監聽怎麼回事 4、怎麼觸發我的函式 這些問題的答案會摻

Vue原理Props

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Props - 原始碼版 今天記錄 Props 原始碼流程,哎,這東西,就算是研究過了,也真是會隨著時間慢慢忘記的。 幸好我做了詳細的文章,忘記了什麼的,回憶起來必然是很快的。 好的,回到正題,Props 請你

Vue原理依賴更新

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】依賴更新 - 原始碼版 如果對依賴收集完全沒有概念的同學,可以先看我這篇白話版 響應式原理 - 白話版 我們已經講過了 依賴收集 【Vue原理】依賴收集 - 原始碼版之基本資料型別 【Vue

Vue原理VNode

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】VNode - 原始碼版 今天就來探索 VNode 的原始碼,VNode 是 Vue2 渲染機制中很重要的一部分,是深入Vue 必須瞭解的部分 我們以4個問題來開始我們的探索 1、vnode 是什麼及其作用

Vue原理Event

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Event - 原始碼版 之 自定義事件 Vue 的自定義事件很簡單,就是使用 觀察者模式 進行事件的監聽和分發 Vue 封裝的這個觀察者模式,可以說是很完善了,這個可以獨立抽取出來的在其他專案中使用的程式碼,

Vue原理NextTick

寫文章不容易,點個讚唄兄弟 專注 Vue 原始碼分享,文章分為白話版和 原始碼版,白話版助於理解工作原理,原始碼版助於瞭解內部詳

Vue原理Render

寫文章不容易,點個讚唄兄弟 專注 Vue 原始碼分享,文章分為白話版和 原始碼版,白話版助於理解工作原理,原始碼版助於瞭解內部詳

Vue原理Diff

寫文章不容易,點個讚唄兄弟 <br> <br> 專注 Vue 原始碼分享,文章分為白話版和 原始碼版,

vue.js入門

emp 寫到 logs 組件 images href one mooc 渲染 慕課網視頻學習筆記:http://www.imooc.com/learn/694 1.將html、js、css寫到一個後綴名.vue的文件中,區分這三種類型是通過<template>、

底層原理四位計算機的原理及其實現

一點 led燈 waiting lean div rm2 src and nvt 你是否想過,計算機為什麽會加減乘除?或者更直接一點,計算機的原理到底是什麽? Waitingforfriday有一篇詳細的教程,講解了如何自己動手,制作一臺四位計算機。從中可以看到,二進制、數

計算機原理程序執行過程

進程 cnblogs div 空間 時間片 chat 內存管理 tro alt 本章主要介紹程序執行過程中操作系統、CPU都幹了什麽 運行前 程序在運行前,只是在硬盤上待著,此時就是一堆二進制代碼而已,沒有任何作用。 程序只有進入了內存才能運行,但是要進入內存,則需要服從操

計算機原理CPU部分.md

工作 信號 通過 臃腫 流水線 處理 2.6 操作 ade 本文由CPU阿甘改編而得,主要講的是系統啟動和程序執行時CPU做的工作。 CPU的構成 中央處理器(CPU,Central Processing Unit)由運算器、控制器、Cache等。 控制器:主要是對指令進

組成原理第一章 計算機系統概述

表示 運算 傳遞 intro 指令 掌握 周期 style 主存 重點掌握:MAR和MDR的含義,主存容量大小、CPU執行時間的計算,性能指標CPI、MIPS、主頻等等。 1. 存儲單元:CPU訪問存儲器的基本單位,每個單元有一個地址。通常是字節大小的整數倍。 2. CPU

編譯原理c++實現自下而上語法分析器

不可 acm times style size PC -i 表達式 鏈接 寫在前面:本博客為本人原創,嚴禁任何形式的轉載!本博客只允許放在博客園(.cnblogs.com),如果您在其他網站看到這篇博文,請通過下面這個唯一的合法鏈接轉到原文! 本博客全網唯一合法URL:ht

計數原理UVA11538 Chess Queen

put typename return col nbsp 題意 putchar cst putc 傳送門 Description   給你一個n*m的棋盤,在棋盤上放置一黑一白兩個皇後,求兩個皇後能夠互相攻擊的方案個數 Input   多組數據,每組數據包括:

MongoDB 復制集 第 二 部 之選舉原理

command primary red and 優先權 mongo mongodb 主機 set 目錄: 1·復制與選舉的原理與驗證2·oplog 日誌調整3·配置復制集的優先級4·部署認證的復制5·總結 復制與選舉的原理: 上一篇文章搭建了多臺實例,部署成復制集,

Memcached 主主復制 + Keepalived 高可用架構附上原理

ima pki nag ali event 主服務器 figure tel outer 目錄: 1·Memcached 主主復制概念2·Memcached 高可用的實現3·案例部署4·總結 Memcached 主主復制概念 (1)主主復制概念: Memcached

Windows原理非同步IO-_APC(非同步過程呼叫)

// 同步IO的缺點是, 在讀寫檔案時, 如果檔案太大, 或者讀寫的時間太長, 就會在讀寫函式中 // 阻塞住. // 非同步IO解決了這個問題, 非同步IO讀寫檔案時, 檔案再大也不會阻塞住 // 但是非同步IO要完成這樣的特性是有一點付出的 // 非同步讀寫檔案後, 需要通過一些方式

Windows原理IO非同步-等待事件物件

/* 同步IO的缺點是, 在讀寫檔案時, 如果檔案太大, 或者讀寫的時間太長, 就會在讀寫函式中 阻塞住. 非同步IO解決了這個問題, 非同步IO讀寫檔案時, 檔案再大也不會阻塞住 但是非同步IO要完成這樣的特性是有一點付出的 非同步讀寫檔案後, 需要通過一些方式才能知道檔案讀寫(IO