# vue源码 组件
# 组件注册方式
本质是调用extend
创建子类构造器
# 全局注册
- 在全局实例化之前调用,注册后可以在任何新建的vue实例中调用
Vue.component('my-test', {
template: '<div>{{test}}</div>',
data () {
return {
test: 1212
}
}
})
var vm = new Vue({
el: '#app',
template: '<div id="app"><my-test><my-test/></div>'
})
# 局部注册
var myTest = {
template: '<div>{{test}}</div>',
data () {
return {
test: 1212
}
}
}
var vm = new Vue({
el: '#app',
component: {
myTest
}
})
# 注册过程
initAssetRegisters
/core/global-api/assets.js
export function initAssetRegisters (Vue: GlobalAPI) {
/**
* Create asset registration methods.
*/
ASSET_TYPES.forEach(type => {//component,directive,filter
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
if (!definition) {
return this.options[type + 's'][id] //直接返回构造函数
} else {
...
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
definition = this.options._base.extend(definition)//子
}
...
this.options[type + 's'][id] = definition
return definition
}
}
})
}
# $parent and $children
initLifecycle
/src/core/instance/lifecycle.js
export function initLifecycle (vm: Component) {
const options = vm.$options
// locate first non-abstract parent
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
...
# 异步组件
- 创建
// 全局注册:
Vue.component('asyncComponent', function(resolve, reject) {
require(['./test.vue'], resolve)
})
// 局部注册:
var vm = new Vue({
el: '#app',
template: '<div id="app"><asyncComponent></asyncComponent></div>',
components: {
asyncComponent: (resolve, reject) => require(['./test.vue'], resolve),
// 另外写法
asyncComponent: () => import('./test.vue'),
}
})
createComponent
/src/core/vdom/create-component.js
export function createComponent (
Ctor: Class<Component> | Function | Object | void,
data: ?VNodeData,
context: Component,
children: ?Array<VNode>,
tag?: string
): VNode | Array<VNode> | void {
if (isUndef(Ctor)) {
return
}
const baseCtor = context.$options._base
// plain options object: turn it into a constructor 针对局部组件创建子类构造器
if (isObject(Ctor)) {
Ctor = baseCtor.extend(Ctor)
}
...
// async component
let asyncFactory
if (isUndef(Ctor.cid)) {
asyncFactory = Ctor
// 工厂函数
Ctor = resolveAsyncComponent(asyncFactory, baseCtor)
if (Ctor === undefined) {
return createAsyncPlaceholder(
asyncFactory,
data,
context,
children,
tag
)
}
}
...
const name = Ctor.options.name || tag
const vnode = new VNode(// 子
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
data, undefined, undefined, undefined, context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
...
return vnode
}
props
// extract props
const propsData = extractPropsFromVNodeData(data, Ctor, tag)
extractPropsFromVNodeData
/vdom/helpers/extract-props.js
export function extractPropsFromVNodeData (
data: VNodeData,
Ctor: Class<Component>,
tag?: string
): ?Object {
const propOptions = Ctor.options.props
if (isUndef(propOptions)) {
return
}
const res = {}
// attr 针对编译生成的render函数
// props 自定义的render函数
const { attrs, props } = data
if (isDef(attrs) || isDef(props)) {
for (const key in propOptions) {
const altKey = hyphenate(key)
if (process.env.NODE_ENV !== 'production') {
const keyInLowerCase = key.toLowerCase() // 驼峰命名
if (
key !== keyInLowerCase &&
attrs && hasOwn(attrs, keyInLowerCase)
) {
tip(
`Prop "${keyInLowerCase}" is passed to component ` +
`${formatComponentName(tag || Ctor)}, but the declared prop name is` +
` "${key}". ` +
`Note that HTML attributes are case-insensitive and camelCased ` +
`props need to use their kebab-case equivalents when using in-DOM ` +
`templates. You should probably use "${altKey}" instead of "${key}".`
)
}
}
checkProp(res, props, key, altKey, true) ||
checkProp(res, attrs, key, altKey, false)
}
}
return res
}
resolveAsyncComponent
/vdom/helpers/resolve-async-component.js
export function resolveAsyncComponent (
factory: Function,
baseCtor: Class<Component>
): Class<Component> | void {
...
if (owner && !isDef(factory.owners)) {
...
const resolve = once((res: Object | Class<Component>) => {
// cache resolved
factory.resolved = ensureCtor(res, baseCtor)
// invoke callbacks only if this is not a synchronous resolve
// (async resolves are shimmed as synchronous during SSR)
if (!sync) {
forceRender(true)
} else {
owners.length = 0
}
})
// once 保证只执行一次,利用闭包特性
const reject = once(reason => {
process.env.NODE_ENV !== 'production' && warn(
`Failed to resolve async component: ${String(factory)}` +
(reason ? `\nReason: ${reason}` : '')
)
if (isDef(factory.errorComp)) {
factory.error = true
forceRender(true)
}
})
...
// 传入
const res = factory(resolve, reject)
...
// return in case resolved synchronously
return factory.loading
? factory.loadingComp
: factory.resolved
}
}
- 异步组件加载成功
ensureCtor
function ensureCtor (comp: any, base) {
if (
comp.__esModule ||
(hasSymbol && comp[Symbol.toStringTag] === 'Module')
) {
comp = comp.default
}
return isObject(comp)
? base.extend(comp) // 子
: comp
}
forceRender
->$forceUpdate()
const forceRender = (renderCompleted: boolean) => {
for (let i = 0, l = owners.length; i < l; i++) {
(owners[i]: any).$forceUpdate()
}
....
}
# 函数式组件
- 无状态、无实例化、中间层,只处理数据,染开销低,本质是对组件的一个外部包装
- demo
//对象
var test1 = {
props: ['msg'],
render: function (createElement, context) {
return createElement('h1', this.msg)
}
}
var test2 = {
props: ['msg'],
render: function (createElement, context) {
return createElement('h2', this.msg)
}
}
// 函数式组件
Vue.component('test3', {
// 函数式组件的标志 functional设置为true
functional: true,
props: ['msg'],
render: function (createElement, context) {
var get = function() {
return test1
}
return createElement(get(), context)
}
})
// 使用
<test3 :msg="msg" id="test">
</test3>
new Vue({
el: '#app',
data: {
msg: 'test'
}
})
//结果
<h2>test</h2>
functional = true
作为区别 /src/core/vdom/create-component.js
// functional component
if (isTrue(Ctor.options.functional)) {
return createFunctionalComponent(Ctor, propsData, data, context, children)
}
createFunctionalComponent
/src/core/vdom/create-functional-component.js
export function createFunctionalComponent (
Ctor: Class<Component>,
propsData: ?Object,
data: VNodeData,
contextVm: Component,
children: ?Array<VNode>
): VNode | Array<VNode> | void {
const options = Ctor.options
const props = {}
const propOptions = options.props
if (isDef(propOptions)) {
for (const key in propOptions) {
props[key] = validateProp(key, propOptions, propsData || emptyObject)
}
} else {
if (isDef(data.attrs)) mergeProps(props, data.attrs)
if (isDef(data.props)) mergeProps(props, data.props)
}
const renderContext = new FunctionalRenderContext(
data,
props,
children,
contextVm,
Ctor
)
const vnode = options.render.call(null, renderContext._c, renderContext)
...
}
FunctionalRenderContext
定义和别的组件不同的render
FunctionalRenderContext(){
...
this._c = (a, b, c, d) => createElement(contextVm, a, b, c, d, needNormalization)
...
}