vue 源码阅读 (一)
今天围观了知乎上有一起争执,并在评论中看到如下对话:
A 说: 例如自定义组件名大写,而不是Element的前缀形式,更易于在模板中区分原生HTML标签和自定义组件。再如iView禁用input可以这么写<input disable/>, Element文档是这么写的<input :disable-true>,哪个更清晰呢?;二是详细精美的文档,早期甚至好过Element的文档。B 说:1. 组件名支持大驼峰命名法,这个 Vue 官网里就有提, 我不知道评论者怎么会认为这是组件库的特色了,任何组件都可以这么写。 2. input 的 disable,同样 Vue 文档也有提到,只是写法不同,这也算特点?
组件命名
之前在 vue 1.0 时代,写过一篇 组件之驼峰
属性值
第二点有点意外,特意做了个实验
官方文档写的比较隐蔽,在升级指南中有提及,看完后不是很理解,于是翻了下 vue 2.2.1 源码,主要涉及以下几处:
进入生命周期
vue/src/core/instance/init.js
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
...
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initState(vm) // <==== 初始化状态
initInjections(vm)
callHook(vm, 'created')
...
}
}
初始化状态
vue/src/core/instance/state.js
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props) // <==== 初始化属性
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch) initWatch(vm, opts.watch)
}
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {}
const props = vm._props = {}
// cache prop keys so that future props updates can iterate using Array
// instead of dynamic object key enumeration.
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// root instance props should be converted
observerState.shouldConvert = isRoot
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm) // 校验属性值
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
if (isReservedProp[key]) {
warn(
`"${key}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
defineReactive(props, key, value, () => {
if (vm.$parent && !observerState.isSettingProps) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwritten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${key}"`,
vm
)
}
})
} else {
defineReactive(props, key, value)
}
// static props are already proxied on the component's prototype
// during Vue.extend(). We only need to proxy props defined at
// instantiation here.
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
observerState.shouldConvert = true
}
校验属性
vue/src/core/util/props.js
export function validateProp (
key: string,
propOptions: Object,
propsData: Object,
vm?: Component
): any {
const prop = propOptions[key]
const absent = !hasOwn(propsData, key)
let value = propsData[key]
// handle boolean props
if (isType(Boolean, prop.type)) {
if (absent && !hasOwn(prop, 'default')) {
value = false
} else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) {
value = true
}
}
// check default value
if (value === undefined) {
value = getPropDefaultValue(vm, prop, key)
// since the default value is a fresh copy,
// make sure to observe it.
const prevShouldConvert = observerState.shouldConvert
observerState.shouldConvert = true
observe(value)
observerState.shouldConvert = prevShouldConvert
}
if (process.env.NODE_ENV !== 'production') {
assertProp(prop, key, value, vm, absent)
}
return value
}
是否存在
vue/src/shared/util.js
const hasOwnProperty = Object.prototype.hasOwnProperty
export function hasOwn (obj: Object, key: string): boolean {
return hasOwnProperty.call(obj, key)
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。