new Vue时框架干了啥?

1、传入options,调用Vue初始函数,如:

new Vue({

    data(){

        return {},

    }

}) ,时会调用Vue._init(options)方法。

2、_init方法会完成对生命周期、时间、渲染函数、state初始化,然后将vm挂载到el上,源码如下:

Step1: /vue/scr/core/instance/index.js

function Vue (options) {

  if (process.env.NODE_ENV !== 'production' &&

    !(this instanceof Vue)

  ) {

    warn('Vue is a constructor and should be called with the `new` keyword')

  }

this._init(options)

}

Step2: /vue/src/core/instance/init.js

...

import { initState } from './state'

...

Vue.prototype._init = function(options:Object){

    const vm:Component = this; //将vm指向this

    //此处省去一些代码,对options做了一些判断

    vm._self = vm

    initLifecycle(vm) //初始化生命周期

    initEvents(vm) //初始化事件

    initRender(vm) //初始化渲染函数

    callHook(vm, 'beforeCreate') //执行beforeCreate 钩子

    initInjections(vm) // resolve injections before data/props

    initState(vm) //初始化数据观测

    initProvide(vm) // resolve provide after data/props

    callHook(vm, 'created') //执行created钩子

    if (vm.$options.el) {

          vm.$mount(vm.$options.el) //判定options中是否有el选项,如有则将vue挂载

    }

}

vue响应性是其最核心的功能,其中很重要的一个部分是实现对数据的监听,其中vue2.X是运用ES5 Object.defineProperty特性来进行数据监听的,为了便于理解,我们看如下范例:

var object = { name: "wh", age: "18" };

function defineReactive(object,key){

  var val = object[key];

  Object.defineProperty(object, key, {

    get() {

      console.log("set" + key);

      const value = val;

      return value;

    },

    set(newVal) {

        console.log("set" + key);

        val = newVal;

    },

  });

}

var keys = Object.keys(object);

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

  defineReactive(object,keys[i]);

}

object.name = "kongzhi";

console.log(object.name);

object.age = "20";

console.log(object.age);

console.log(object.name);

运行后结果为:

setname

kongzhi

setage

20

kongzhi

通过上述代码运行,我们可以发现,运用Object.defineProperty对数据进行劫持后,每当调用数据则会触发get方法,而每当改变数据则会调用set方法。

VUE是通过对监听器、订阅器和订阅者的实现来实现响应性的,源码如下:

next Step1:/vue/src/core/instance/state.js

import { 

  set,

  del,

  observe,

  defineReactive,

  toggleObserving

} from /vue/src/core/observer/index

export function initState (vm: Component) {

  //vm 即为vue实例

  vm._watchers = []

  const opts = vm.$options

  if (opts.props) initProps(vm, opts.props)

  if (opts.methods) initMethods(vm, opts.methods)

  if (opts.data) {

    //若存在data 选项则执行initDate

    initData(vm)

  } else {

    observe(vm._data = {}, true /* asRootData */)

  }

  if (opts.computed) initComputed(vm, opts.computed)

  if (opts.watch && opts.watch !== nativeWatch) {

    initWatch(vm, opts.watch)

  }

}

function initData (vm: Component) {

  let data = vm.$options.data

  //data选项类型是否为function,禁止订阅器订阅

  data = vm._data = typeof data === 'function'

    ? getData(data, vm)

    : data || {}

  if (!isPlainObject(data)) {

    data = {}

    process.env.NODE_ENV !== 'production' && warn(

      'data functions should return an object:\n' +

      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',

      vm

    )

  }

  // proxy data on instance

  const keys = Object.keys(data)

  const props = vm.$options.props

  const methods = vm.$options.methods

  let i = keys.length

  //判定data 和 methods、props 选项是否重名,若存在重名则抛出警告。

  while (i--) {

    const key = keys[i]

    if (process.env.NODE_ENV !== 'production') {

      if (methods && hasOwn(methods, key)) {

        warn(

          `Method "${key}" has already been defined as a data property.`,

          vm

        )

      }

    }

    if (props && hasOwn(props, key)) {

      process.env.NODE_ENV !== 'production' && warn(

        `The data property "${key}" is already declared as a prop. ` +

        `Use prop default value instead.`,

        vm

      )

    } else if (!isReserved(key)) {

      proxy(vm, `_data`, key)

    }

  }

  // 重点:调用observe函数对data进行监听

  observe(data, true /* asRootData */)

}

next Step2:/vue/src/coreobserver/index.js

export function observe (value: any, asRootData: ?boolean): Observer | void {

  if (!isObject(value) || value instanceof VNode) {

    return

  }

  let ob: Observer | void

  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {

    ob = value.__ob__

  } else if (

    shouldObserve &&

    !isServerRendering() &&

    (Array.isArray(value) || isPlainObject(value)) &&

    Object.isExtensible(value) &&

    !value._isVue

  ) {

    //若data未被监听,则添加监听器

    ob = new Observer(value)

  }

  if (asRootData && ob) {

    ob.vmCount++

  }

  return ob

}

//监听器类

export class Observer {

  value: any;

//监听器

  dep: Dep;

  vmCount: number; // number of vms that have this object as root $data

  constructor (value: any) {

    this.value = value

    //初始化观测器

    this.dep = new Dep()

    this.vmCount = 0

    //为data添加__ob__属性

    def(value, '__ob__', this)

  //实现数组监听

    if (Array.isArray(value)) {

      if (hasProto) {

        protoAugment(value, arrayMethods)

      } else {

        copyAugment(value, arrayMethods, arrayKeys)

      }

      this.observeArray(value)

    } else {

      //实现对象监听

      this.walk(value)

    }

  }

  /**

  * Walk through all properties and convert them into

  * getter/setters. This method should only be called when

  * value type is Object.

  */

  walk (obj: Object) {

    const keys = Object.keys(obj)

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

      //遍历data对象属性,添加监听器

      defineReactive(obj, keys[i])

    }

  }

}

/**

* Define a reactive property on an Object.

*/

export function defineReactive (

  obj: Object,

  key: string,

  val: any,

  customSetter?: ?Function,

  shallow?: boolean

) {

  //初始化订阅器

  const dep = new Dep()

//...省略一堆代码

//若值为对象则递归添加监听器

  let childOb = !shallow && observe(val)

  Object.defineProperty(obj, key, {

    enumerable: true,

    configurable: true,

    get: function reactiveGetter () {

      const value = getter ? getter.call(obj) : val

      if (Dep.target) {

        //若数据被调用,则订阅者者收集订阅器

        dep.depend()

        if (childOb) {

          childOb.dep.depend()

          if (Array.isArray(value)) {

            dependArray(value)

          }

        }

      }

      return value

    },

    set: function reactiveSetter (newVal) {

      const value = getter ? getter.call(obj) : val

      /* eslint-disable no-self-compare */

      if (newVal === value || (newVal !== newVal && value !== value)) {

        return

      }

      /* eslint-enable no-self-compare */

      if (process.env.NODE_ENV !== 'production' && customSetter) {

        customSetter()

      }

      // #7981: for accessor properties without setter

      if (getter && !setter) return

      if (setter) {

        setter.call(obj, newVal)

      } else {

        val = newVal

      }

    //若数据被修改,则对新数据重新添加观测器

      childOb = !shallow && observe(newVal)

    //调用监听器notify方法

      dep.notify()

    }

  })

}

next Step2:/vue/src/core/observer/dep.js

export default class Dep {

  static target: ?Watcher;

  id: number;

  subs: Array<Watcher>;

  constructor () {

    this.id = uid++

    this.subs = []

  }

  //监听器可以实现对监听者添加和删除

  addSub (sub: Watcher) {

    this.subs.push(sub)

  }

  removeSub (sub: Watcher) {

    remove(this.subs, sub)

  }

  depend () {

    if (Dep.target) {

      Dep.target.addDep(this)

    }

  }

  //当数据被修改,调用notify方法

  notify () {

    // stabilize the subscriber list first

    const subs = this.subs.slice()

    if (process.env.NODE_ENV !== 'production' && !config.async) {

      // subs aren't sorted in scheduler if not running async

      // we need to sort them now to make sure they fire in correct

      // order

      subs.sort((a, b) => a.id - b.id)

    }

    for (let i = 0, l = subs.length; i < l; i++) {

      //循环遍历监听者,调用监听者update()方法

      subs[i].update()

    }

  }

}

next Step3:/vue/src/core/observer/watcher.js

  update () {

    /* istanbul ignore else */

    if (this.lazy) {

      this.dirty = true

    } else if (this.sync) {

      //若为异步则调用run方法

      this.run()

    } else {

      queueWatcher(this)

    }

  }

  /**

  * Scheduler job interface.

  * Will be called by the scheduler.

  */

  run () {

    if (this.active) {

      const value = this.get()

      if (

        value !== this.value ||

        // Deep watchers and watchers on Object/Arrays should fire even

        // when the value is the same, because the value may

        // have mutated.

        isObject(value) ||

        this.deep

      ) {

        // set new value

        const oldValue = this.value

        this.value = value

        if (this.user) {

          try {

            //触发监听者回调函数更新视图

            this.cb.call(this.vm, value, oldValue)

          } catch (e) {

            handleError(e, this.vm, `callback for watcher "${this.expression}"`)

          }

        } else {

          //触发监听者回调函数更新视图

          this.cb.call(this.vm, value, oldValue)

        }

      }

    }

  }

next Step4:/vue/src/core/observer/scheduler.js

import { queueWatcher } from './scheduler'

export const MAX_UPDATE_COUNT = 100

const queue: Array<Watcher> = []

const activatedChildren: Array<Component> = []

let has: { [key: number]: ?true } = {}

let circular: { [key: number]: number } = {}

let waiting = false

let flushing = false

let index = 0

export function queueWatcher (watcher: Watcher) {

  const id = watcher.id

  if (has[id] == null) {

    //判断监听者id是否存在,若不存在,则加入has

    has[id] = true

    if (!flushing) {

      //若还未刷新则将监听者加入队列

      queue.push(watcher)

    } else {

      // if already flushing, splice the watcher based on its id

      // if already past its id, it will be run next immediately.

      let i = queue.length - 1

      while (i > index && queue[i].id > watcher.id) {

        i--

      }

    将最后一个监听者替换此前监听者

      queue.splice(i + 1, 0, watcher)

    }

    // queue the flush

    if (!waiting) {

      waiting = true

      if (process.env.NODE_ENV !== 'production' && !config.async) {

        flushSchedulerQueue()

        return

      }

      nextTick(flushSchedulerQueue)

    }

  }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容