前言
此篇主要手寫 Vue2.0 源碼-Mixin 混入原理javascript
上一篇我們主要介紹了 Vue 異步更新原理 核心是運用 nextTick 實現異步隊列 此篇主要包含 Mixin 混入 這是 Vue 裏面很是關鍵的一個 api 在 Vue 初始化的時候起到了合併選項的重要做用java
適用人羣: 沒時間去看官方源碼或者看源碼看的比較懵而不想去看的同窗git
正文
Vue.mixin({ created() { console.log("我是全局混入"); }, }); // Vue實例化 let vm = new Vue({ el: "#app", data() { return { a: { a: { a: { b: 456 } } }, aa: 1, bb: 2, }; }, created() { console.log("我是本身的"); }, template: `<div id="a">hello 這是我本身寫的Vue{{name}} </div>`, }); 複製代碼
當咱們在 Vue 裏面想要複用一段業務代碼邏輯時常常用到的就是混入的方法 可是對於混入的原理 混入的前後順序以及不一樣選項的合併策略你們是否都清楚呢 讓咱們一塊兒來手寫一遍就都清楚了github
1.定義全局 Mixin 函數
// src/global-api/mixin.js import {mergeOptions} from '../util/index' export default function initMixin(Vue){ Vue.mixin = function (mixin) { // 合併對象 this.options=mergeOptions(this.options,mixin) }; } }; 複製代碼
新建 global-api 文件夾 把 mixin 定義爲 Vue 的全局方法 核心方法就是利用 mergeOptions 把傳入的選項混入到本身的 options 上面api
// src/index.js import { initMixin } from "./init.js"; // Vue就是一個構造函數 經過new關鍵字進行實例化 function Vue(options) { // 這裏開始進行Vue初始化工做 this._init(options); } // 此作法有利於代碼分割 initMixin(Vue); export default Vue; 複製代碼
而後在 Vue 的入口文件裏面引入 initMixin 方法數組
2.mergeOptions 方法
// src/util/index.js // 定義生命週期 export const LIFECYCLE_HOOKS = [ "beforeCreate", "created", "beforeMount", "mounted", "beforeUpdate", "updated", "beforeDestroy", "destroyed", ]; // 合併策略 const strats = {}; //生命週期合併策略 function mergeHook(parentVal, childVal) { // 若是有兒子 if (childVal) { if (parentVal) { // 合併成一個數組 return parentVal.concat(childVal); } else { // 包裝成一個數組 return [childVal]; } } else { return parentVal; } } // 爲生命週期添加合併策略 LIFECYCLE_HOOKS.forEach((hook) => { strats[hook] = mergeHook; }); // mixin核心方法 export function mergeOptions(parent, child) { const options = {}; // 遍歷父親 for (let k in parent) { mergeFiled(k); } // 父親沒有 兒子有 for (let k in child) { if (!parent.hasOwnProperty(k)) { mergeFiled(k); } } //真正合並字段方法 function mergeFiled(k) { if (strats[k]) { options[k] = strats[k](parent[k], child[k]); } else { // 默認策略 options[k] = child[k] ? child[k] : parent[k]; } } return options; } 複製代碼
咱們先着重看下 mergeOptions 方法 主要是遍歷父親和兒子的屬性 進行合併 若是合併的選項有本身的合併策略 那麼就是用相應的合併策略app
再來看看 咱們這裏的生命週期的合併策略 mergeHook 很明顯是把所有的生命週期都各自混入成了數組的形式依次調用異步
3.生命週期的調用
// src/lifecycle.js export function callHook(vm, hook) { // 依次執行生命週期對應的方法 const handlers = vm.$options[hook]; if (handlers) { for (let i = 0; i < handlers.length; i++) { handlers[i].call(vm); //生命週期裏面的this指向當前實例 } } } 複製代碼
把$options 上面的生命週期依次遍歷進行調用函數
// src/init.js Vue.prototype._init = function (options) { const vm = this; // 這裏的this表明調用_init方法的對象(實例對象) // this.$options就是用戶new Vue的時候傳入的屬性和全局的Vue.options合併以後的結果 vm.$options = mergeOptions(vm.constructor.options, options); callHook(vm, "beforeCreate"); //初始化數據以前 // 初始化狀態 initState(vm); callHook(vm, "created"); //初始化數據以後 // 若是有el屬性 進行模板渲染 if (vm.$options.el) { vm.$mount(vm.$options.el); } }; 複製代碼
在 init 初始化的時候調用 mergeOptions 來進行選項合併 以後在須要調用生命週期的地方運用 callHook 來執行用戶傳入的相關方法post
// src/lifecycle.js export function mountComponent(vm, el) { vm.$el = el; // 引入watcher的概念 這裏註冊一個渲染watcher 執行vm._update(vm._render())方法渲染視圖 callHook(vm, "beforeMount"); //初始渲染以前 let updateComponent = () => { vm._update(vm._render()); }; new Watcher( vm, updateComponent, () => { callHook(vm, "beforeUpdate"); //更新以前 }, true ); callHook(vm, "mounted"); //渲染完成以後 } 複製代碼
在 mountComponent 方法裏面調用相關的生命週期 callHook
4.混入的思惟導圖
小結
至此 Vue 的混入原型已經手寫完畢 其實最核心的就是對象合併以及不一樣選項的合併策略 目前只是演示了生命週期的合併策略 後續到組件的時候會講到組件相關的合併策略 你們能夠看着思惟導圖本身動手寫一遍核心代碼哈 遇到不懂或者有爭議的地方歡迎評論留言