vue源码解读0-2

上篇文章已经对index.js中的基本调用情况做了说明,接下来的几篇将对各个函数做仔细的分析,能力有限,文章中不足之处,希望大家能够指正!

上篇中提到在instance/vue中使用了9个高阶函数来构建(install)Vue构造函数(并不会调用该构造函数的进行初始化的过程),一切等在使用new Vue({….})的时候将一个全新的对象作为函数内this的值,返回该新对象作为结果(函数 调用中构造函数调用的方法)

function Vue (options) {
  this._init(options)

}

创建函数中函数申明的创建的方法(涉及知识函数声明的提升),this为函数方法调用的接收者,一般为构造函数调用的方式 new Vue()

initMixin(Vue)

Mixin-mix in( 混入加入) 可能是作者取这个名字的原因吧(只是妄加猜测,具体已作者本人意图为准)

import initMixin from ‘./internal/init’ 会在internal/init中就会存在default的export接下来的分析将会从这个开始着手



逐行代码的分析如

let uid = 0

设置了uid只在当前的块中有效,let具体可以在预热解读中有说明

export default function (Vue) {
.....
}

export default 匿名函数具体用法可以在预热解读中找到,接下来的也即是function中的内容,接收参数为Vue

 Vue.prototype._init = function (options){
    .....
  }

Vue也即是传入进来的参数(函数名),函数会自带一个默认的prototype的属性在新建立之前几乎为空,当使用new创建Vue的实例的时候,会得到自动分配的原型对象,存在User的prototype例如我们使用 var vm=new Vue({})来初始化构造方法的时候(先查找自身的属性再去原型链中进行查找)

对于javaScript的继承机制基于原型链(ES5),javaScript的实例对象由构造函数与在实例件共享的原型对象组成,对于原型用的较多的1个创建的方法(m.prototype)与2个获取原型的方法(obj.getPrototypeOf(m)和obj. _ proto _).其中

 options = options || {}

判断options是否为null,0,-0,undefined,false,”,NaN等情况(以上也即是js的7大假值),当options不为假则直接执行赋值,否则为{}。(涉及赋值运算符优先级,||运算时当左边为假才会执行右边;左右options的不一样);

    this.$el = null
    this.$parent = options.parent
    this.$root = this.$parent? this.$parent.$root : this
    this.$children = []
    this.$refs = {}       // child vm references
    this.$els = {}        // element references
    this._watchers = []   // all watchers as an array
    this._directives = [] // all directives

this实例化之后也即是Vue对象,未指定调用接收者为undefined;先来了解下基本的含义,在后面涉及到会仔细介绍:

$parent存在的话则为父实例; $root:当前组件树的根 Vue 实例。如果当前实例没有父实例为自身。$children 当前实例的直接子组件。

$refs:一个对象,包含注册有 v-ref 的子组件。\$els对象中包含注册有 v-el 的 DOM 元素。

  // a uid
    this._uid = uid++ //上文中定义的let uid

    // a flag to avoid this being observed  设置标志避免被检测到
    this._isVue = true

    // events bookkeeping  事件统计
    this._events = {}            // registered callbacks
    this._eventsCount = {}      // for $broadcast optimization  //$broadcast的优化

    // fragment instance properties fragment实例属性
    this._isFragment = false
    this._fragment =         // @type {DocumentFragment}
    this._fragmentStart =    // @type {Text|Comment}
    this._fragmentEnd = null // @type {Text|Comment}

    // lifecycle state  生命周期状态
    this._isCompiled =
    this._isDestroyed =
    this._isReady =
    this._isAttached =
    this._isBeingDestroyed =
    this._vForRemoving = false
    this._unlinkFn = null

各个实例到底什么意思,相信也很困惑,这里只要稍微有印象即可在之后的分析与学习中会逐步解释

  // context:
    // if this is a transcluded component, context
    // will be the common parent vm of this instance
    // and its host.
    如果这是一个嵌入式的组件,上下文将是这个实例共有父实例(或宿主)
    this._context = options._context || this.$parent

    // scope:
    // if this is inside an inline v-for, the scope
    // will be the intermediate scope created for this
    // repeat fragment. this is used for linking props
    // and container directives.
    如果这是在一个内联的v-for,将由这个循环的片段产生中间的作用域范围,被用在链接父组件的数据和指令容器
    this._scope = options._scope

    // fragment:
    // if this instance is compiled inside a Fragment, it
    // needs to reigster itself as a child of that fragment
    // for attach/detach to work properly.
    如果这个实例在某个片段里已经编译,需要在该片段上进行注册,利于attach或detach的正常工作
    this._frag = options._frag
    if (this._frag) {
      this._frag.children.push(this)
    }

    // push self into parent / transclusion host
    如果存在父实例则将其建立双方的链接
    if (this.$parent) {
      this.$parent.$children.push(this)
    }

    // merge options.
    合并options,含有一个mergeOptions的函数
    options = this.$options = mergeOptions(
      this.constructor.options,
      options,
      this
    )

import { mergeOptions } from ‘../../util/index’

export * from ‘./lang‘
export * from ‘./env‘
export * from ‘./dom‘
export * from ‘./options‘  //options
export * from ‘./component‘
export * from ‘./debug‘
export { defineReactive } from ‘../observer/index‘

export * 也即是将所有的标记过的均导出

在options.js中可以看到

  /**
 * Merge two option objects into a new one.
 * Core utility used in both instantiation and inheritance.
 * 主要用于在实例化与继承
 * @param {Object} parent
 * @param {Object} child
 * @param {Vue} [vm] - if vm is present, indicates this is
 *                     an instantiation merge.
 *
  options = this.$options = mergeOptions(
      this.constructor.options,
      options,
      this
    )
 */
export function mergeOptions (parent, child, vm) {

}

下面均是该函数内的代码片段

  guardComponents(child)
  guardProps(child)
  function guardComponents (options){
   ...
  }

对于guardComponents主要用作options中的组件构造,下文的代码为guardComponents中的代码


var vm = new Vue({
  el: ‘...‘,
  data:{},
  components: {
  ‘a‘:{},
  ‘b‘:{}
  }
})
 if (options.components) {
    ......
  }

如果在options中存在components的存在,则会进行下部分的代码

 var components = options.components =
      guardArrayAssets(options.components)

赋值语句从右至左,使用guardArrayAssets函数将数组形式的转化为键值对的形式



guardArrayAssets:

function guardArrayAssets (assets) {
//assets 也即是传递过来的options.components
//1.components:{‘s‘:{},‘d‘:{}}
//2.componets:[{‘name‘:‘...‘,‘id‘:‘...‘}]
//3.?
  if (isArray(assets)) {
    var res = {}
    var i = assets.length
    var asset
    //数组循环取值组成键值对的形式 key值由id决定
    while (i--) {
      asset = assets[i]
      var id = typeof asset === ‘function‘
        ? ((asset.options && asset.options.name) || asset.id)
        : (asset.name || asset.id)
        //id异常情况
      if (!id) {
        process.env.NODE_ENV !== ‘production‘ && warn(
          ‘Array-syntax assets must provide a "name" or "id" field.‘
        )
      } else {
      //规整为key-value的形式
        res[id] = asset
      }
    }
    return res
  }
  return assets
}

可以看出有3种方式填写的option.components,主要目的是规整为字典的形式便于后面的直接调用

下面回到guardComponents

 var components = options.components =
      guardArrayAssets(options.components)

var ids = Object.keys(components)

这里用到了一个Object.keys方法,获取规整后的components的键值数组

The Object.keys() method returns an array of a given object’s own enumerable properties, in the same order as that provided by a for…in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).

返回一个枚举所有对象属性的数组,类似于for-in 枚举(并不保证按对象的顺序输各个属性 ,不可预测的顺序unpredicted order)

接下来飘逸与自然的for循环如下:


 for (var i = 0, l = ids.length; i < l; i++) {
      var key = ids[i]
      if (commonTagRE.test(key) || reservedTagRE.test(key)) {
        process.env.NODE_ENV !== ‘production‘ && warn(
          ‘Do not use built-in or reserved HTML elements as component ‘ +
          ‘id: ‘ + key
        )
        continue
      }
      // record a all lowercase <-> kebab-case mapping for
      // possible custom element case error warning
      if (process.env.NODE_ENV !== ‘production‘) {
        map[key.replace(/-/g, ‘‘).toLowerCase()] = hyphenate(key)
      }
      def = components[key]
      if (isPlainObject(def)) {
        components[key] = Vue.extend(def)
      }
    }

其中commonTagRE与reservedTagRE为options.js中导入的两个属性

import { commonTagRE, reservedTagRE } from ‘./component‘

export const commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/i

export const reservedTagRE = /^(slot|partial|component)$/i

const为es6中的关键字,表示不可以修改常量只在当前模块中有效,想要在其他模块中引用也即是利用前面提到的export命令,不会提升,必须先申明后使用

变量的提升:某一作用域范围内

console.info(v) ==> var v

var v=’tev’ console.info(v)

v=’tev’

正则表达式中:

/i (忽略大小写)

/g (全文查找出现的所有匹配字符)

/m (多行查找)

/gi(全文查找、忽略大小写)

/ig(全文查找、忽略大小写)

键值中

 process.env.NODE_ENV !== ‘production‘ && warn(
          ‘Do not use built-in or reserved HTML elements as component ‘ +
          ‘id: ‘ + key
        )

不要使用保留的slot,partial,component与Html的标签作为键值

def = components[key]
      if (isPlainObject(def)) {
        components[key] = Vue.extend(def)
      }

使用vue.extend定义组件,如下例子将更好解释

 components:{
    ‘my-component‘:{
      template:‘<div>A custom component!</div>‘
    }
  },

html页面中使用<\my-component><\/\my-component>等同于

 components:[{
    // ‘id‘:‘my-component‘,
    ‘name‘:‘my-component‘,
    ‘template‘:‘<div>A custom component!</div>‘
    }
  ],

等同于:

var MyComponent = Vue.extend({
  template: ‘<div>A custom component!</div>‘
})
Vue.component(‘my-component‘, MyComponent)

上面的代码中,这里涉及到两个isplainObject与Vue.extend,x下面将对其进分析

 if (isPlainObject(def)) {
        components[key] = Vue.extend(def)
      }
import {
  isArray,
  isPlainObject,
} from ‘./lang‘
/**
 * Strict object type check. Only returns true
 * for plain JavaScript objects.
 *
 * @param {*} obj
 * @return {Boolean}
 */
//使用toString()方法判断类型,可以表面toString对null的判断方法,如下图所示

var toString = Object.prototype.toString
var OBJECT_STRING = ‘[object Object]‘
export function isPlainObject (obj) {
  return toString.call(obj) === OBJECT_STRING
}

/**
 * Array type check.
 *
 * @param {*} obj
 * @return {Boolean}
 */
//也即是调用Array方法中的isArray方法
export const isArray = Array.isArray

Vue.extend在global-api.js中在接下来的中会分析



感觉跑偏了很远这样流水式的分析要知道自己要回到哪个地方

mergeOptions

export function mergeOptions (parent, child, vm) {
//在Options之前将options:components与props定义好
  guardComponents(child)
  guardProps(child)
  ....
}

Vue.prototype._init

 Vue.prototype._init = function (options){
 ...
 options = this.$options = mergeOptions(
      this.constructor.options,
      options,
      this
    )
}

props的定义

A list/hash of attributes that are exposed to accept data from the parent component(从父组件中获得数据). It has a simple Array-based syntax (数组形式)and an alternative Object-based(对象形式) syntax that allows advanced configurations such as type checking, custom validation and default values(对象形式用于高级的设置如 类型检查,自定义验证,默认值等).

 guardProps(child)将所有的props规格化为基于对象的格式(虽然支持数组与对象的两种形式),child也即是为init中传入的options
 props: [‘size‘, ‘myMessage‘]
  props: [{‘name‘:‘size‘},{‘name‘:‘myMessage‘}],
  props: {
    // 只检测类型
    size: Number,
    // 检测类型 + 其它验证
    name: {
      type: String,
      required: true,
      // 双向绑定
      twoWay: true
    }
  }
function guardProps (options) {
 var props = options.props
  var i, val
  if (isArray(props)) { //为数组类型
    options.props = {}
    i = props.length
    while (i--) {
      val = props[i]
      if (typeof val === ‘string‘) {
      //为String类型的时候将其值设置为空 ‘size‘:null
        options.props[val] = null

      } else if (val.name) {
      //取val.name
        options.props[val.name] = val
      }
    }
  } else if (isPlainObject(props)) {
    var keys = Object.keys(props)
    i = keys.length
    while (i--) {

      val = props[keys[i]]
      if (typeof val === ‘function‘) { //{ 初始为Object类型 {} }
        props[keys[i]] = { type: val }
      }
    }
  }
 }
时间: 2024-10-10 11:06:56

vue源码解读0-2的相关文章

vue源码解读预热-0

vueJS的源码解读 vue源码总共包含约一万行代码量(包括注释)特别感谢作者Evan You开放的源代码,访问地址为Github 代码整体介绍与函数介绍预览 代码模块分析 代码整体思路 总体的分析 从图片中可以看出的为采用IIFE(Immediately-Invoked Function Expression)立即执行的函数表达式的形式进行的代码的编写 常见的几种插件方式: (function(,){}(,))或(function(,){})(,)或!function(){}()等等,其中必有

vue源码解读(一)Observer/Dep/Watcher是如何实现数据绑定的

欢迎star我的github仓库,共同学习~目前vue源码学习系列已经更新了5篇啦~ https://github.com/yisha0307/... 快速跳转: Vue的双向绑定原理(已完成) 说说vue中的Virtual DOM(已完成) React diff和Vue diff实现差别 Vue中的异步更新策略(已完成) Vuex的实现理解 Typescript学习笔记(持续更新ing) Vue源码中闭包的使用(已完成) 介绍 最近在学习vue和vuex的源码,记录自己的一些学习心得.主要借鉴

vue源码解读-目录结构

目录结构 ├── scripts ------------------------------- 构建相关的文件,一般情况下我们不需要动│ ├── git-hooks ------------------------- git钩子│ ├── alias.js -------------------------- 别名配置│ ├── config.js ------------------------- rollup配置的文件│ ├── build.js ---------------------

vue源码解读1

前言 vue是一个非常典型的MVVM框架,它的核心功能一是双向数据绑定系统,二是组件化开发系统.那么本文是以一种通俗易懂的的角度来实现一个简单 的双向数据绑定系统,如果你用过vue却对vue的实现原理不太清楚,或者没用过vue想学习vue那我相信看完本文你会的vue的实现有一个比较简单明确的了解.不过如果哪块有 错误,还望指出. 本文的实现目标: input标签和{{text}}的内容与data中的text值保持一致,实现双向绑定 <div id="app"> <in

vueJs源码解读0-1

vue源码解读-1 在github上下载到源码的后在src的目录下也即是该所有分块的源文件的地址所在的地方,使用webstrom在file–>Settings–>languages&Frameworks中选择javascript使用ECMAScript6 1. index.js import Vue from './instance/vue' import installGlobalAPI from './global-api' import { inBrowser, devtools

AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager

做ios开发,AFNetworking 这个网络框架肯定都非常熟悉,也许我们平时只使用了它的部分功能,而且我们对它的实现原理并不是很清楚,就好像总是有一团迷雾在眼前一样. 接下来我们就非常详细的来读一读这个框架的代码,我们的目标就是理解了它的思想之后,能够明白我们的请求是如何实现的,我们的代码哪里还需要进行改进,如果能够更进一步,我们能够总结出一套适合大部分应用的网络架构思想. 能够让一些人从中受益. 我们先来看看整个框架的文件系统,我们先不对每个文件的作用进行说明,在整个源码解读最后的一篇中我

AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization

本篇是AFNetworking 3.0 源码解读的第四篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization 这次主要讲AFURLResponseSerialization(HTTP响应)这一个类的知识. 这是一个协议,只要遵守这个协议,就要实现N

AfNetworking 3.0源码解读

做ios开发,AFNetworking 这个网络框架肯定都非常熟悉,也许我们平时只使用了它的部分功能,而且我们对它的实现原理并不是很清楚,就好像总是有一团迷雾在眼前一样. 接下来我们就非常详细的来读一读这个框架的代码,我们的目标就是理解了它的思想之后,能够明白我们的请求是如何实现的,我们的代码哪里还需要进行改进,如果能够更进一步,我们能够总结出一套适合大部分应用的网络架构思想. 能够让一些人从中受益. 我们先来看看整个框架的文件系统,我们先不对每个文件的作用进行说明,在整个源码解读最后的一篇中我

AFNetworking 3.0 源码解读 总结

终于写完了 AFNetworking 的源码解读.这一过程耗时数天.当我回过头又重头到尾的读了一篇,又有所收获.不禁让我想起了当初上学时的种种情景.我们应该对知识进行反复的记忆和理解.下边是我总结的 AFNetworking 中能够学到的知识点. 1.枚举(enum) 使用原则:当满足一个有限的并具有统一主题的集合的时候,我们就考虑使用枚举.这在很多框架中都验证了这个原则.最重要的是能够增加程序的可读性. 示例代码: /** * 网络类型 (需要封装为一个自己的枚举) */ typedef NS