1. 程式人生 > >Vue原始碼解析(一)

Vue原始碼解析(一)

使用vue已經大半年時間了,寫起來已經很順手了。比起之前的jquery時代,vue配合webpack的使用,讓前端程式碼整體性更強,程式碼量也比之前少了一些。用了一段時間後,也想了解Vue具體是如何實現這樣的一個mvvm框架的,經過一段時間的掙扎,終於開始了原始碼解析。ps:本次解讀的vue原始碼版本為2.3.3,本人水平有限,如果解析有誤的地方,請批評指正。

首先,不管採用什麼方式引入vue,直接script引入,requireJs引入等等等,vue都會進行相關的初始化工作。

function Vue$3 (options) {
  if ("development" !== 'production'
&& !(this instanceof Vue$3) ) { warn('Vue is a constructor and should be called with the `new` keyword'); } this._init(options); } initMixin(Vue$3); stateMixin(Vue$3); eventsMixin(Vue$3); lifecycleMixin(Vue$3); renderMixin(Vue$3); //全域性Api的程式碼在比較靠下的位置 initGlobalAPI(Vue$3);

定義了Vue$3這個類,接著呼叫了initMixin、stateMixin、eventsMixin、lifecycleMixin、renderMixin、initGlobalAPI幾個方法。

var uid$1 = 0;

function initMixin (Vue) {
  Vue.prototype._init = function (options) {
    var vm = this;
    // a uid
    vm._uid = uid$1++;

    var startTag, endTag;
    /* istanbul ignore if */
    if ("development" !== 'production' && config.performance && mark) {
      startTag = "vue-perf-init:"
+ (vm._uid); endTag = "vue-perf-end:" + (vm._uid); mark(startTag); } // a flag to avoid this being observed vm._isVue = true; // merge options if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options); } else { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ); } /* istanbul ignore else */ { initProxy(vm); } // expose real self vm._self = vm; initLifecycle(vm); initEvents(vm); initRender(vm); callHook(vm, 'beforeCreate'); initInjections(vm); // resolve injections before data/props initState(vm); initProvide(vm); // resolve provide after data/props callHook(vm, 'created'); /* istanbul ignore if */ if ("development" !== 'production' && config.performance && mark) { vm._name = formatComponentName(vm, false); mark(endTag); measure(((vm._name) + " init"), startTag, endTag); } if (vm.$options.el) { vm.$mount(vm.$options.el); } }; }

initMixin方法,定義了Vue$3的_init的方法。startTag, endTag,mark為開啟performance模式同時瀏覽器支援Performance.mark()方法時,在瀏覽器開發工具中對元件進行效能追蹤。根據_isComponent是否為true呼叫initInternalComponent或mergeOptions進行options合併。

function initLifecycle (vm) {
  var options = vm.$options;

  // locate first non-abstract parent
  var parent = options.parent;
  if (parent && !options.abstract) {
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent;
    }
    parent.$children.push(vm);
  }

  vm.$parent = parent;
  vm.$root = parent ? parent.$root : vm;

  vm.$children = [];
  vm.$refs = {};

  vm._watcher = null;
  vm._inactive = null;
  vm._directInactive = false;
  vm._isMounted = false;
  vm._isDestroyed = false;
  vm._isBeingDestroyed = false;
}

生命週期初始化,找到第一個非抽象parent,在$children陣列中新增vm。$children$refs 等設定初始值。

function initEvents (vm) {
  vm._events = Object.create(null);
  vm._hasHookEvent = false;
  // init parent attached events
  var listeners = vm.$options._parentListeners;
  if (listeners) {
    updateComponentListeners(vm, listeners);
  }
}

事件初始化,如果有父元件監聽呼叫updateComponentListeners更新監聽。

function initRender (vm) {
  vm._vnode = null; // the root of the child tree
  vm._staticTrees = null;
  var parentVnode = vm.$vnode = vm.$options._parentVnode; // the placeholder node in parent tree
  var renderContext = parentVnode && parentVnode.context;
  vm.$slots = resolveSlots(vm.$options._renderChildren, renderContext);
  vm.$scopedSlots = emptyObject;
  // bind the createElement fn to this instance
  // so that we get proper render context inside it.
  // args order: tag, data, children, normalizationType, alwaysNormalize
  // internal version is used by render functions compiled from templates
  vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };
  // normalization is always applied for the public version, used in
  // user-written render functions.
  vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };
}

渲染初始化,呼叫resolveSlots方法返回vm子元件slots。渲染初始化結束後callHook(vm, ‘beforeCreate’)方法下發beforeCreate事件。

function initInjections (vm) {
  var result = resolveInject(vm.$options.inject, vm);
  if (result) {
    Object.keys(result).forEach(function (key) {
      /* istanbul ignore else */
      {
        defineReactive$$1(vm, key, result[key], function () {
          warn(
            "Avoid mutating an injected value directly since the changes will be " +
            "overwritten whenever the provided component re-renders. " +
            "injection being mutated: \"" + key + "\"",
            vm
          );
        });
      }
    });
  }
}

注入初始化。

function initState (vm) {
  vm._watchers = [];
  var opts = vm.$options;
  if (opts.props) { initProps(vm, opts.props); }
  if (opts.methods) { initMethods(vm, opts.methods); }
  if (opts.data) {
    initData(vm);
  } else {
    observe(vm._data = {}, true /* asRootData */);
  }
  if (opts.computed) { initComputed(vm, opts.computed); }
  if (opts.watch) { initWatch(vm, opts.watch); }
}

初始化props、methods、data、computed、watch。

function initProvide (vm) {
  var provide = vm.$options.provide;
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide;
  }
}

初始化provide。provide初始化結束後,呼叫callHook(vm, ‘created’)方法下發created事件。

if (vm.$options.el) {
      vm.$mount(vm.$options.el);
}

最後,如果option中el存在,掛載元素到對應的el上,之後進行元件的渲染。

相關推薦

Vue原始碼解析

使用vue已經大半年時間了,寫起來已經很順手了。比起之前的jquery時代,vue配合webpack的使用,讓前端程式碼整體性更強,程式碼量也比之前少了一些。用了一段時間後,也想了解Vue具體是如何實現這樣的一個mvvm框架的,經過一段時間的掙扎,終於開始了原始

Spring原始碼解析——元件註冊1

一、@Configuration&@Bean給容器中註冊元件 public class Person { private String name; private Integer age; public Person() { } public

認真的 Netty 原始碼解析

本文又是一篇原始碼分析文章,其實除了 Doug Lea 的併發包原始碼,我是不太愛寫原始碼分析的。 本文將介紹 Netty,Java 平臺上使用最廣泛的 NIO 包,它是對 JDK 中的 NIO 實現的一層封裝,讓我們能更方便地開發 NIO 程式。其實,Netty 不僅僅是 NIO 吧,但是,基本上大家

Vue原始碼分析:入口檔案

Vue原始碼分析(一):入口檔案   首先開啟命令列,從github下載原始碼,下載到自己的工作目錄。 git clone https://github.com/vuejs/vue.git   這裡我下載的是2.5.17版本的,vue 原始碼是由各種模組用 rollup 工具

EventBus原始碼解析—訂閱過程

1.EventBus原始碼解析(一)—訂閱過程 2.EventBus原始碼解析(二)—釋出事件和登出流程 前言 最近發現EventBus用起來是真的方便,本來對於EventBus我對於這個框架的原始碼的閱讀的優先順序是比較低的,因為這個框架不像OkHttp,Gli

vue原始碼解讀

vue原始碼的目錄結構 vue原始碼根目錄下有很多資料夾,以下是各資料夾存放的檔案說明 Vue     |— build 打包相關的配置檔案,其中最重要的是config.js。主要是根據不同的入口,打包為不同的檔案。  &nbs

【MapReduce詳解及原始碼解析】——分片輸入、Mapper及Map端Shuffle過程

title: 【MapReduce詳解及原始碼解析(一)】——分片輸入、Mapper及Map端Shuffle過程 date: 2018-12-03 21:12:42 tags: Hadoop categories: 大資料 toc: true 點選檢視我的部落格:Josonlee’

Redis5.0原始碼解析----------簡單動態字串SDS

基於Redis5.0 Redis 沒有直接使用 C 語言傳統的字串表示(以空字元結尾的字元陣列,以下簡稱 C 字串), 而是自己構建了一種名為簡單動態字串(simple dynamic string,SDS)的抽象型別, 並將 SDS 用作 Redis 的預設字串表示。

OkHttp原始碼解析

簡單使用 OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); Request request = new Request.Builder() .url("www.bai

OKHttp 3.10原始碼解析:執行緒池和任務佇列

OKhttp是Android端最火熱的網路請求框架之一,它以高效的優點贏得了廣大開發者的喜愛,下面是OKhttp的主要特點: 1.支援HTTPS/HTTP2/WebSocket 2.內部維護執行緒池佇列,提高併發訪問的效率 3.內部維護連線池,支援多路複用,減少連線建立開銷 4.

SLF4J原始碼解析

提出問題 閱讀原始碼之前,首先提幾個問題 SLF4J是如何整合不同的日誌框架的 Class Path中為什麼只能有且僅有一種日誌框架的binding 這段文字摘錄自官網:In your code, in addition to slf4j-api-1.8.0-beta2.jar, y

Java容器——HashMapJava8原始碼解析

一 概述 HashMap是最常用的Java資料結構之一,是一個具有常數級別的存取速率的高效容器。相對於List,Set等,結構相對複雜,本篇我們先對HashMap的做一個基本說明,對組成元素和構造方法進行介紹。 二 繼承關係 首先看HashMap的繼承關係,比較簡單,實現了Map和序列化

jQuery深入之原始碼解析

總體架構 可以看出來jQuery主要有三個模組: 入口模組、功能模組、底層支援模組。 - 入口模組 在構造jQuery物件模組中,如果在呼叫建構函式建立jQuery物件時,會呼叫選擇器

python 原始碼解析

為了看懂 python 原始碼 ,特地學了 c++ ,依然看不懂,看了個大概,先留個坑,慢慢填。 先從 python dict 物件開始看起。 python dict 物件  是鍵值對的 一種結構,類似於 java 的hashmap 物件。 dictd 物件 , 每個鍵值對 ,

ElasticSearch原始碼解析:轉篇介紹中文分詞的文章

轉自:http://www.cnblogs.com/flish/archive/2011/08/08/2131031.html  基於CRF(Conditional Random Field)分詞演算法 論文連結:http://nlp.stanford.edu/pubs/

ActiveMQ原始碼解析:聊聊broker

一、Broker     訊息佇列核心,相當於一個控制中心,負責路由訊息、儲存訂閱和連線、訊息確認和控制事務 1.Broker介面     定義了一些獲取broker本身相關資訊,新增connection、destination、session、訊息生產者、控制事務

StringBuffer和StringBuilder原始碼解析--構造方法

前幾天接到阿里巴巴的電話面試,被虐的一塌糊塗。當時問我列印字串可以用這種方式System.out.println(“a” + “b” + “c”),但是一般我們不用這種方式,而要用StringBuff

jdk原始碼解析

1、先說一下原始碼解析的過程:JDK-->JRE-->JVM(以openJDK代替)注意:這裡要了解jdk和jre和jvm他們分別是什麼?以及他們的關係才可以繼續。這裡先上一章從網上下載的關係圖方便理解 2、筆者本地的jdk是oraclejdk,jvm所

vue 原始碼學習 目錄結構和構建過程簡介

Flow vue框架使用了Flow作為型別檢查,來保證專案的可讀性和維護性。vue.js的主目錄下有Flow的配置.flowconfig檔案,還有flow目錄,指定了各種自定義型別。 在學習原始碼前可以先看下Flow的語法 官方文件 目錄結構 vue.js原始碼主要在src下 src ├── com

Spark原始碼解析

RDD之getNarrowAncestors內部方法分析 最近開始spark的原始碼攻關,其實看原始碼一直是我最怕的東西,因為太多、太雜、太深導致不能夠很好的把我脈絡導致每次最後都放棄。有人跟我說看原