Vue原始碼學習(七):合併生命週期(混入Vue.Mixin)

養肥胖虎發表於2023-09-19

好傢伙,

 

1.使用場景

現在來,來想一下,作為一個使用Vue的開發者,假設現在我們要使用created(),我們會如何使用

1.1.  .vue檔案中使用

<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>

<script>
export default {
  created() {
    this.message = 'Hello, created() in single file component!';
  },
  data() {
    return {
      message: ''
    };
  }
};
</script>

 

1.2.    Vue例項中使用

<!DOCTYPE html>
<html>
<head>
  <title>Vue created() example</title>
</head>
<body>
  <div id="app">
    <h1>{{ message }}</h1>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        message: ''
      },
      created() {
        this.message = 'Hello, created() in Vue instance!';
      }
    });
  </script>
</body>
</html>

 

 

1.3.   混入

 Vue.Mixin({ //全域性
            created:function a(){
                console.log('a----1')
            }
        })

 

那麼如果我這樣去定義

<script>
        Vue.Mixin({ //全域性
            created:function a(){
                console.log('a----1')
            }
        })
        Vue.Mixin({ //全域性
            created:function b(){
                console.log('b----2')
            }
        })
        let vm = new Vue({
            el: '#app', //編譯模板
            // data: {
            // },
            data() {
                // console.log(this)
                return {
                    msg: 'hello',
                    a: {
                        b: 99
                    },
                    list: [1, 2, 3],
                    arr: [{
                        a: 1
                    }]
                }
            },
            created(){
                console.log(555)
            }
        })

    </script>

是否會報錯呢?

答案是不會

對於created()鉤子函式,在每個Vue例項建立時,會依次執行全域性混入函式中定義的created()方法和例項本身定義的created()方法。

當Vue例項被建立時,它會先執行全域性混入函式的對應生命週期鉤子函式,然後再執行例項本身的生命週期鉤子函式。

因此,在你的程式碼中,全域性混入函式中的created()會在例項的created()之前執行,且會按照它們在全域性混入函式中的定義順序執行

這樣的設計允許開發者在多個地方定義相同的生命週期鉤子函式,以實現不同的功能擴充套件和邏輯處理。

同時,由於生命週期鉤子函式的執行順序已經確定,開發者可以根據需要合理安排程式碼邏輯

 

最後,也說明,created()定義的方法被合併處理了,所以我們要把這個"合併"實現

 

 

2.專案上下文

老樣子,先看看專案更新了哪些東西

程式碼已開源https://github.com/Fattiger4399/analytic-vue.git

 

 

2.1.Vue入口檔案index.js中

新增全域性方法

 

2.2. global-api/index.js

import { mergeOptions } from "../utils/index"

export function initGlobApi(Vue) {
    //原始碼
    //Vue.options ={created:[a,b,c],watch:{a,b}}
    Vue.options ={}
    Vue.Mixin = function (mixin) { // {}
        //原始碼
        //{created:[a,b,c],watch:[a,b]}
        //物件的合併
        console.log(999)
        this.options = mergeOptions(this.options,mixin)
        console.log(Vue.options,"||this is vue.options")
    }
}

此處涉及我們的核心方法mergeOptions

 

這方法要實現一個怎麼樣的效果?

 Vue.Mixin({ //全域性
            created: function a() {
                console.log('a----1')
            }
        })
        Vue.Mixin({ //全域性
            created: function b() {
                console.log('b----2')
            }
        })
        let vm = new Vue({
            el: '#app', //編譯模板
            // data: {
            // },
            data() {
                // console.log(this)
                return {
                    msg: 'hello',
                    a: {
                        b: 99
                    },
                    list: [1, 2, 3],
                    arr: [{
                        a: 1
                    }]
                }
            },
            created() {
                    console.log(555)
            }
        })

將上述所有與created()有關的方法

最後合併到一個物件當中去

 

3.核心方法

3.1.utils/index.js

來到我們全篇最核心也是最難的部分

//物件合併 {created:[]}
export const HOOKS =[
    "beforeCreated",
    "created",
    "beforeMount",
    "mounted",
    "beforeUpdate",
    "updated",
    "beforeDestory",
    "destroyed",
]
// 策略模式
let starts ={}
starts.data =function(parentVal,childVal){
    return childVal
} //合併data
starts.computed =function(){} //合併computed
starts.watch =function(){} //合併watch
starts.methods =function(){} //合併methods
//遍歷生命週期
HOOKS.forEach(hooks=>{
    starts[hooks] = mergeHook
})

function mergeHook(parentVal,childVal){
    if(childVal){
        if(parentVal){
            //把子元素合併進去
            return parentVal.concat(childVal)
        }else{
            return [childVal] //[a]
        }
    }else{
        return parentVal
    }
}

export function mergeOptions(parent, child) {
    console.log(parent,child,'||this is parent and child in mergeOptions()')
    const options ={}
    //判斷父親
    for(let key in parent){
        console.log(key,'||this is key')

        mergeField(key)
    }
    //判斷兒子
    for(let key in child){
        console.log(key,'||this is key')
        mergeField(key)
    }
    function mergeField(key){
        //根據key 策略模式
        if(starts[key]){ //created {created:[a]}
            options[key] =starts[key](parent[key],child[key])
        }else{
            options[key] = child[key]
        }
    }
    return options
}

 

前端設計模式之策略模式 - 掘金 (juejin.cn)

這玩意要看懂,必須先把這玩意學了,策略模式

一句話概括策略模式是一種行為型設計模式,它允許在執行時根據不同的情境選擇並應用不同的演算法或行為(不是條件判斷)

 

挖個坑,後面會補一章策略模式

 

//物件合併 {created:[]}
export const HOOKS =[
    "beforeCreated",
    "created",
    "beforeMount",
    "mounted",
    "beforeUpdate",
    "updated",
    "beforeDestory",
    "destroyed",
]
// 策略模式
let starts ={}
starts.data =function(parentVal,childVal){
    return childVal
} //合併data
starts.computed =function(){} //合併computed
starts.watch =function(){} //合併watch
starts.methods =function(){} //合併methods
//遍歷生命週期
HOOKS.forEach(hooks=>{
    starts[hooks] = mergeHook
})

function mergeHook(parentVal,childVal){
    if(childVal){
        if(parentVal){
            //把子元素合併進去
            return parentVal.concat(childVal)
        }else{
            return [childVal] //[a]
        }
    }else{
        return parentVal
    }
}

這裡定義常量HOOKS包含了一組生命週期鉤子的名字

隨後建立starts物件,用於儲存各個不同屬性的不同合併策略

至於mergeHook,這就是個簡單的合併方法,不用多解釋了


再來看下半部分

export function mergeOptions(parent, child) {
    console.log(parent,child,'||this is parent and child in mergeOptions()')
    const options ={}
    //判斷父親
    for(let key in parent){
        console.log(key,'||this is key')
        mergeField(key)
    }
    //判斷兒子
    for(let key in child){
        console.log(key,'||this is key')
        mergeField(key)
    }
    function mergeField(key){
        //根據key 選擇不同策略區處理
        if(starts[key]){ //created {created:[a]}
            options[key] =starts[key](parent[key],child[key])
        }else{
            options[key] = child[key]
        }
    }
    return options
}
mergeOptions將父項和子項合併成一個新的物件

這個你真的得親自上手除錯一下



3.2.init.js

這句就是將在option合併Vue.option中並返回給vm.$options
(option為new Vue時帶的引數)

最後,看看效果

將方法都合併到了created中

 

 

 

相關文章