Vue源码学习(七):合并生命周期(混入Vue.Mixin)

vue,源码,学习,合并,生命周期,混入,mixin · 浏览次数 : 133

小编点评

**3.核心方法3.1.utils/index.js** ```javascript export const HOOKS = [ "beforeCreated", "created", "beforeMount", "mounted", "beforeUpdate", "updated", "beforeDestory", "destroyed", ]; let starts = {}; starts.data = function(parentVal, childVal) { return childVal; }; starts.computed = function() {}; starts.watch = function() {}; starts.methods = function() {}; 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]) { options[key] = starts[key](parent[key], child[key]); } else { options[key] = child[key]; } } return options; } ``` **3.2.init.js** ```javascript 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]) { options[key] = starts[key](parent[key], child[key]); } else { options[key] = child[key]; } } return options; } ```

正文

好家伙,

 

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中

 

 

 

与Vue源码学习(七):合并生命周期(混入Vue.Mixin)相似的内容:

Vue源码学习(七):合并生命周期(混入Vue.Mixin)

好家伙, 1.使用场景 现在来,来想一下,作为一个使用Vue的开发者,假设现在我们要使用created(),我们会如何使用 1.1. .vue文件中使用 {{ message }}

Vue源码学习(八):生命周期调用

好家伙, Vue源码学习(七):合并生命周期(混入Vue.Mixin) 书接上回,在上一篇中,我们已经实现了合并生命周期 现在,我们要在我们的初始化过程中,注册生命周期 1.项目目录 红框为本篇涉及到的.js文件 2.先来看 /utils/index.js export const HOOKS =[

Vue源码学习(十一):计算属性computed初步学习

好家伙, 1.Computed实现原理 if (opts.computed) { initComputed(vm,opts.computed); } function initComputed(vm, computed) { // 存放计算属性的watcher const watchers = vm

Vue源码学习(一):数据劫持(对象类型)

好家伙,了解一下Vue如何实现数据劫持 1.Vue中data的使用 首先,我得搞清楚这玩意的概念,我们先从vue的使用开始吧 想想看,我们平时是如何使用vue的data部分的? 无非是这两种情况 (你可千万不要带着惊讶的表情说"啊!原来有两种写法的吗") //函数写法 data() { return

Vue源码学习(二):渲染第一步,模板解析

好家伙, 1.去哪了 在正式内容之前,我们来思考一个问题, 当我们使用vue开发页面时,中的内容是如何变成我们网页中的内容的? 它会经历四步: 解析模板:Vue会解析中的内容,识别出其中的指令、插值表达式({{}}),以及其他元素和属性。

Vue源码学习(三):渲染第二步,创建ast语法树

好家伙,书接上回 在上一篇Vue源码学习(二):渲染第一步,模板解析中,我们完成了模板解析 现在我们继续,将模板解析的转换为ast语法树 1.前情提要 代码已开源https://github.com/Fattiger4399/analytic-vue.git手动调试一遍, 胜过我

Vue源码学习(四):渲染第三步,将ast语法树转换为渲染函数

好家伙, Vue源码学习(三):渲染第二步,创建ast语法树, 在上一篇,我们已经成功将 我们的模板 转换为ast语法树 接下来我们继续进行操作 1.方法封装 由于代码太多,为了增加代码的可阅读性 我们先将代码进行封装 index.js import { generate } f

Vue源码学习(六):(支线)渲染函数中with(),call()的使用以及一些思考

好家伙, 昨天,在学习vue源码的过程中,看到了这个玩意 嘶,看不太懂,研究一下 1.上下文 这段出现vue模板编译的虚拟node部分 export function renderMixin(Vue) { Vue.prototype._c = function () { //创建标签 return

Vue源码学习(五):渲染第四步,生成虚拟dom并将其转换为真实dom

好家伙, 前情提要: 在上一篇我们已经成功将ast语法树转换为渲染函数 现在我们继续 1.项目目录 代码已开源https://github.com/Fattiger4399/analytic-vue.git手动调试一遍, 胜过我解释给你听一万遍 新增文件:vnode/index.js vnode/p

Vue源码学习(十):关于dep和watcher使用的一些思考

好家伙, 前面想了好久,都没想明白为什么要dep和watcher打配合才能实现数据-视图同步 为什么要多一个依赖管理这样的东西 给每个数据绑个watcher(xxfunction),然后,数据变了,调set,然后调xxfunction,不就行了, 然后今天突然想明白了,不是为什么要这么干,而是必须这