vue源码分析1-new Vue做了哪些操作
栏目: JavaScript · 发布时间: 6年前
内容简介:首先我们可以看到vue的源码在github上有,大家可以克隆下来。1.现在我们来分析下 new Vue都做了哪些操作我们都知道new关键字在js中代表实例化一个对象,而vue实际上是一个类,类在js中是用Function来实现的,在源码的
首先我们可以看到vue的源码在github上有,大家可以克隆下来。 git地址 我们主要看src下的内容。
1.现在我们来分析下 new Vue都做了哪些操作
var app = new Vue({ el: '#app', mounted:{ console.log(this.message) } data: { message: 'Hello Vue!' } }) 复制代码
我们都知道new关键字在js中代表实例化一个对象,而vue实际上是一个类,类在js中是用Function来实现的,在源码的
src/core/instance/index.js中
import { initMixin } from './init' import { stateMixin } from './state' import { renderMixin } from './render' import { eventsMixin } from './events' import { lifecycleMixin } from './lifecycle' import { warn } from '../util/index' // 声明Vue类 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) } // 将Vue类传入各种初始化方法 initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue) export default Vue 复制代码
可以看到vue只能通过new关键字初始化,然后会调用this._init方法,同时把options参数传入,_init是vue原型上的一个方法。那么接下来我们看下_init方法做了些什么。在initMixin中定义了_init方法。
src/core/instance/init.js中
Vue.prototype._init = function (options?: Object) { // this指向Vue的实例,所以这里是将Vue的实例缓存给vm变量 const vm: Component = this //定义uid vm._uid = uid++ let startTag, endTag /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { startTag = `vue-perf-start:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` mark(startTag) } // a flag to avoid this being observed vm._isVue = true // merge options 合并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 */ if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = 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) // 初始化state initProvide(vm) // resolve provide after data/props callHook(vm, 'created') /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { vm._name = formatComponentName(vm, false) mark(endTag) measure(`vue ${vm._name} init`, startTag, endTag) } if (vm.$options.el) { vm.$mount(vm.$options.el)// 调用vm上的$mount方法去挂载 } } 复制代码
Vue 初始化主要就干了几件事情,合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等等。
当我们在chrome的console中可以看到打印出来的data.message的值。试想一下,为什么我们能够输出这个值呢?带着这个问题我们来看看initState
src/core/instance/state.js
export function initState (vm: Component) { vm._watchers = [] //如果定义了options,则始初化options, const opts = vm.$options if (opts.props) initProps(vm, opts.props) //如果有props,则初始化initProps if (opts.methods) initMethods(vm, opts.methods)//如果有methods,则初始化initMethods if (opts.data) { 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) } } 复制代码
我们具体看下初始化的initData.
function initData (vm: Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' //判断data类型是否是一个function,并赋值给vm._data ? getData(data, vm) : data || {} if (!isPlainObject(data)) { //判断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 ) } // 拿到对象的keys,props,methods const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length 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 */) } 复制代码
当我们的data是一个函数的时候,会去调用getData,然后在getData中返回data。
export function getData (data: Function, vm: Component): any { pushTarget() try { return data.call(vm, vm) } catch (e) { handleError(e, vm, `data()`) return {} } finally { popTarget() } } 复制代码
我们拿到对象的keys,props,methods时进行一个循环判断,我们在data上定义的属性值有没有在props中也被定义,如果有,则报警告,这是因为我们定义的所有值最终都会绑定到vm上,通过proxy实现,调用proxy,将_data作为sourceKey传入。
export function proxy (target: Object, sourceKey: string, key: string) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] //vm._data.key会访问这 } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) } 复制代码
通过这个对象定义了一个get,set。当我们访问this.message时也就是访问了this._data.message。所以我们能获取到this.message的值。
以上是个人通过视频、查看各位大佬的文章总结的,后续还会有其他文章发布,如有不足之处,欢迎大家来讨论。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 「从源码中学习」Vue源码中的JS骚操作
- jQuery源码学习:异步操作--Callbacks
- B 站源码泄露?这是—什么—操作(黑人问号)?
- Kylin页面System操作源码解读 原 荐
- Flink 源码阅读笔记(十三):双流操作的实现
- RxJava2源码分析(二):操作符原理分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
TCP/IP网络编程
[韩] 尹圣雨 / 金国哲 / 人民邮电出版社 / 2014-7 / 79.00元
第一部分主要介绍网络编程基础知识。此部分主要论述Windows和Linux平台网络编程必备基础知识,未过多涉及不同操作系统特性。 第二部分和第三部分与操作系统有关。第二部分主要是Linux相关内容,而第三部分主要是Windows相关内容。从事Windows编程的朋友浏览第二部分内容后,同样可以提高技艺。 第四部分对全书内容进行总结,包含了作者在自身经验基础上总结的学习建议,还介绍了网络......一起来看看 《TCP/IP网络编程》 这本书的介绍吧!