vue源码

为什么data是函数,而components是对象?

data函数如果没有返回值会报错吗?

答:会

vue源码位置:src\core\instance\state.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
// // 看这里!这里有一个判断 isPlainObject(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
)
}
。。。
。。。
。。。
// observe data
observe(data, true /* asRootData */)
}

Vue源码位置:packages\weex-template-compiler\build.js

1
2
3
function isPlainObject (obj) {
return _toString.call(obj) === '[object Object]'
}

data函数执行之后将返回值重新赋值给data,如果data不是个objectname就会报出一个警告

为什么props定义的数据不能和data同名?

vue源码路径:vue\src\core\instance\state.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

// proxy data on instance
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)
}
}

可以看到先获取datakey,然后再去调用hasOwn方法去判断methodsprops里面是否有重复的key,有的话就会发出警告。最后都通过了之后就会调用proxy(vm, '_data', key),用封装的proxy方法对vm实例做了一层代理,使得我们可以直接通过this直接调用key

vue2代理的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}

export function proxy (target: Object, sourceKey: string, key: string) {
// 去掉_props,vm._props.xxx => vm.xxx,可以直接访问
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
// 代理,
Object.defineProperty(target, key, sharedPropertyDefinition)
}

使用的是Object.defineProperty来实现的

Vue的初始化

Vue源码位置:src\core\instance\index.js

1
2
3
4
5
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

最先执行的是initMixin(),我们看看里面有啥。

Vue源码位置:src\core\instance\init.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//init.js

// 初始化
vm._self = vm
// 生命周期
initLifecycle(vm)
// 事件
initEvents(vm)
// 渲染
initRender(vm)
// 面试题:beforeCreae和created之间做了什么?或者说二者有什么区别?
// 答:初始化injected,初始化state,初始化provide
callHook(vm, 'beforeCreate')
// 初始化inject
initInjections(vm) // resolve injections before data/props
// 初始化state,state里面有data,computed,method,props,watch
initState(vm)
// 初始化provide
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')

initState()是从state.js文件里面导入的,我们再去看看里面有什么。

src\core\instance\state.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
// 初始化props
if (opts.props) initProps(vm, opts.props)
// 初始化方法
if (opts.methods) initMethods(vm, opts.methods)
// 初始化data
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
// 初始化computed
if (opts.computed) initComputed(vm, opts.computed)
// 初始化watch
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}

可见在beforeCreatecreated之间初始化了inject,props,methods,data,computed,watch

基于Vue2的proxy,自己实现一个代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const noop = {} 

const propertyDefinition = {
enumerable:true,
configurable:true,
get:noop,
set:noop
}

const Proxy = function(target,sourceKey,key){
propertyDefinition.get = function getter() {
return target[sourceKey][key]
}

propertyDefinition.set = function setter (val) {
target[sourceKey][key] = val
}

Object.defineProperty(target,key,propertyDefinition)
}

function Vue(data){
this._data = data
Object.keys(this._data).forEach(key => {
Proxy(this,'_data',key)
})
}

const vueIns = new Vue({a:1,b:2})

vueIns._data.b = 'hhh'
vueIns.a = 2222
console.log(vueIns._data.a)
console.log(vueIns.b)

Vue的实例为什么不能挂载在body或根节点上?挂载了会报错吗?

答:挂载节点会被虚拟dom生成的Dom替换,会报错

Vue源码位置:src\platforms\web\entry-runtime-with-compiler.js

1
2
3
4
5
6
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn(
`Do not mount Vue to <html> or <body> - mount to normal elements instead.`
)
return this
}

什么是虚拟节点,简述虚拟dom构成?

vue和react虚拟dom的区别?


vue源码
https://zouhualu.github.io/20220222/vue源码/
作者
花鹿
发布于
2022年2月22日
许可协议