vue源码分析1-new Vue做了哪些操作
栏目: JavaScript · 发布时间: 7年前
内容简介:首先我们可以看到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源码分析(二):操作符原理分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Release It!
Michael T. Nygard / Pragmatic Bookshelf / 2007-03-30 / USD 34.95
“Feature complete” is not the same as “production ready.” Whether it’s in Java, .NET, or Ruby on Rails, getting your application ready to ship is only half the battle. Did you design your system to......一起来看看 《Release It!》 这本书的介绍吧!