/* @flow */
|
|
// https://github.com/Hanks10100/weex-native-directive/tree/master/component
|
|
import { mergeOptions, isPlainObject, noop } from 'core/util/index'
|
import Watcher from 'core/observer/watcher'
|
import { initProxy } from 'core/instance/proxy'
|
import { initState, getData } from 'core/instance/state'
|
import { initRender } from 'core/instance/render'
|
import { initEvents } from 'core/instance/events'
|
import { initProvide, initInjections } from 'core/instance/inject'
|
import { initLifecycle, callHook } from 'core/instance/lifecycle'
|
import { initInternalComponent, resolveConstructorOptions } from 'core/instance/init'
|
import { registerComponentHook, updateComponentData } from '../../util/index'
|
|
let uid = 0
|
|
// override Vue.prototype._init
|
function initVirtualComponent (options: Object = {}) {
|
const vm: Component = this
|
const componentId = options.componentId
|
|
// virtual component uid
|
vm._uid = `virtual-component-${uid++}`
|
|
// a flag to avoid this being observed
|
vm._isVue = true
|
// merge 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
|
}
|
|
vm._self = vm
|
initLifecycle(vm)
|
initEvents(vm)
|
initRender(vm)
|
callHook(vm, 'beforeCreate')
|
initInjections(vm) // resolve injections before data/props
|
initState(vm)
|
initProvide(vm) // resolve provide after data/props
|
callHook(vm, 'created')
|
|
// send initial data to native
|
const data = vm.$options.data
|
const params = typeof data === 'function'
|
? getData(data, vm)
|
: data || {}
|
if (isPlainObject(params)) {
|
updateComponentData(componentId, params)
|
}
|
|
registerComponentHook(componentId, 'lifecycle', 'attach', () => {
|
callHook(vm, 'beforeMount')
|
|
const updateComponent = () => {
|
vm._update(vm._vnode, false)
|
}
|
new Watcher(vm, updateComponent, noop, null, true)
|
|
vm._isMounted = true
|
callHook(vm, 'mounted')
|
})
|
|
registerComponentHook(componentId, 'lifecycle', 'detach', () => {
|
vm.$destroy()
|
})
|
}
|
|
// override Vue.prototype._update
|
function updateVirtualComponent (vnode?: VNode) {
|
const vm: Component = this
|
const componentId = vm.$options.componentId
|
if (vm._isMounted) {
|
callHook(vm, 'beforeUpdate')
|
}
|
vm._vnode = vnode
|
if (vm._isMounted && componentId) {
|
// TODO: data should be filtered and without bindings
|
const data = Object.assign({}, vm._data)
|
updateComponentData(componentId, data, () => {
|
callHook(vm, 'updated')
|
})
|
}
|
}
|
|
// listening on native callback
|
export function resolveVirtualComponent (vnode: MountedComponentVNode): VNode {
|
const BaseCtor = vnode.componentOptions.Ctor
|
const VirtualComponent = BaseCtor.extend({})
|
const cid = VirtualComponent.cid
|
VirtualComponent.prototype._init = initVirtualComponent
|
VirtualComponent.prototype._update = updateVirtualComponent
|
|
vnode.componentOptions.Ctor = BaseCtor.extend({
|
beforeCreate () {
|
// const vm: Component = this
|
|
// TODO: listen on all events and dispatch them to the
|
// corresponding virtual components according to the componentId.
|
// vm._virtualComponents = {}
|
const createVirtualComponent = (componentId, propsData) => {
|
// create virtual component
|
// const subVm =
|
new VirtualComponent({
|
componentId,
|
propsData
|
})
|
// if (vm._virtualComponents) {
|
// vm._virtualComponents[componentId] = subVm
|
// }
|
}
|
|
registerComponentHook(cid, 'lifecycle', 'create', createVirtualComponent)
|
},
|
beforeDestroy () {
|
delete this._virtualComponents
|
}
|
})
|
}
|