mirror of
https://gitee.com/vuejs/vue.git
synced 2024-12-02 03:57:36 +08:00
feat(weex): partially support lifecycles of virtual component (#7242)
Update the `_init` and `_update` logic to partially support lifecycles. Add test cases for testing the lifecycle hooks and data update.
This commit is contained in:
parent
d544d052a9
commit
661bfe552e
@ -70,6 +70,7 @@ declare interface Component {
|
|||||||
_hasHookEvent: boolean;
|
_hasHookEvent: boolean;
|
||||||
_provided: ?Object;
|
_provided: ?Object;
|
||||||
_inlineComputed: ?{ [key: string]: Watcher }; // inline computed watchers for literal props
|
_inlineComputed: ?{ [key: string]: Watcher }; // inline computed watchers for literal props
|
||||||
|
// _virtualComponents?: { [key: string]: Component };
|
||||||
|
|
||||||
// private methods
|
// private methods
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ declare type InternalComponentOptions = {
|
|||||||
type InjectKey = string | Symbol;
|
type InjectKey = string | Symbol;
|
||||||
|
|
||||||
declare type ComponentOptions = {
|
declare type ComponentOptions = {
|
||||||
|
componentId?: string;
|
||||||
|
|
||||||
// data
|
// data
|
||||||
data: Object | Function | void;
|
data: Object | Function | void;
|
||||||
props?: { [key: string]: PropOptions };
|
props?: { [key: string]: PropOptions };
|
||||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -9796,9 +9796,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"weex-js-runtime": {
|
"weex-js-runtime": {
|
||||||
"version": "0.23.4",
|
"version": "0.23.5",
|
||||||
"resolved": "https://registry.npmjs.org/weex-js-runtime/-/weex-js-runtime-0.23.4.tgz",
|
"resolved": "https://registry.npmjs.org/weex-js-runtime/-/weex-js-runtime-0.23.5.tgz",
|
||||||
"integrity": "sha512-leBzBrpvbrKHvmwd00YjzxZAQrp6NWEbEt3jAtk5EINccxZzFhwZ8SpUmn0d5bspemFisT18eJDAavv4LgRxpw==",
|
"integrity": "sha512-94/bMUpCyZMsrq2codDPFatr5Ec8yKYKYNsfoshQOiQKTZY3pwqlfedtOKQNf6k7o4npEhdxDnHJwEVORtNylg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"weex-styler": {
|
"weex-styler": {
|
||||||
|
@ -125,7 +125,7 @@
|
|||||||
"typescript": "^2.6.1",
|
"typescript": "^2.6.1",
|
||||||
"uglify-js": "^3.0.15",
|
"uglify-js": "^3.0.15",
|
||||||
"webpack": "^3.10.0",
|
"webpack": "^3.10.0",
|
||||||
"weex-js-runtime": "^0.23.3",
|
"weex-js-runtime": "^0.23.5",
|
||||||
"weex-styler": "^0.3.0"
|
"weex-styler": "^0.3.0"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -150,7 +150,7 @@ function initData (vm: Component) {
|
|||||||
observe(data, true /* asRootData */)
|
observe(data, true /* asRootData */)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getData (data: Function, vm: Component): any {
|
export function getData (data: Function, vm: Component): any {
|
||||||
try {
|
try {
|
||||||
return data.call(vm, vm)
|
return data.call(vm, vm)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
// https://github.com/Hanks10100/weex-native-directive/tree/master/component
|
// https://github.com/Hanks10100/weex-native-directive/tree/master/component
|
||||||
|
|
||||||
import { mergeOptions } from 'core/util/index'
|
import { mergeOptions, isPlainObject, noop } from 'core/util/index'
|
||||||
|
import Watcher from 'core/observer/watcher'
|
||||||
import { initProxy } from 'core/instance/proxy'
|
import { initProxy } from 'core/instance/proxy'
|
||||||
import { initState } from 'core/instance/state'
|
import { initState, getData } from 'core/instance/state'
|
||||||
import { initRender } from 'core/instance/render'
|
import { initRender } from 'core/instance/render'
|
||||||
import { initEvents } from 'core/instance/events'
|
import { initEvents } from 'core/instance/events'
|
||||||
import { initProvide, initInjections } from 'core/instance/inject'
|
import { initProvide, initInjections } from 'core/instance/inject'
|
||||||
import { initLifecycle, mountComponent, callHook } from 'core/instance/lifecycle'
|
import { initLifecycle, callHook } from 'core/instance/lifecycle'
|
||||||
import { initInternalComponent, resolveConstructorOptions } from 'core/instance/init'
|
import { initInternalComponent, resolveConstructorOptions } from 'core/instance/init'
|
||||||
import { registerComponentHook, updateComponentData } from '../../util/index'
|
import { registerComponentHook, updateComponentData } from '../../util/index'
|
||||||
|
|
||||||
@ -55,8 +56,25 @@ function initVirtualComponent (options: Object = {}) {
|
|||||||
initProvide(vm) // resolve provide after data/props
|
initProvide(vm) // resolve provide after data/props
|
||||||
callHook(vm, 'created')
|
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', () => {
|
registerComponentHook(componentId, 'lifecycle', 'attach', () => {
|
||||||
mountComponent(vm)
|
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', () => {
|
registerComponentHook(componentId, 'lifecycle', 'detach', () => {
|
||||||
@ -65,25 +83,53 @@ function initVirtualComponent (options: Object = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// override Vue.prototype._update
|
// override Vue.prototype._update
|
||||||
function updateVirtualComponent (vnode: VNode, hydrating?: boolean) {
|
function updateVirtualComponent (vnode?: VNode) {
|
||||||
// TODO
|
const vm: Component = this
|
||||||
updateComponentData(this.$options.componentId, {})
|
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
|
// listening on native callback
|
||||||
export function resolveVirtualComponent (vnode: MountedComponentVNode): VNode {
|
export function resolveVirtualComponent (vnode: MountedComponentVNode): VNode {
|
||||||
const BaseCtor = vnode.componentOptions.Ctor
|
const BaseCtor = vnode.componentOptions.Ctor
|
||||||
const VirtualComponent = BaseCtor.extend({})
|
const VirtualComponent = BaseCtor.extend({})
|
||||||
|
const cid = VirtualComponent.cid
|
||||||
VirtualComponent.prototype._init = initVirtualComponent
|
VirtualComponent.prototype._init = initVirtualComponent
|
||||||
VirtualComponent.prototype._update = updateVirtualComponent
|
VirtualComponent.prototype._update = updateVirtualComponent
|
||||||
|
|
||||||
vnode.componentOptions.Ctor = BaseCtor.extend({
|
vnode.componentOptions.Ctor = BaseCtor.extend({
|
||||||
beforeCreate () {
|
beforeCreate () {
|
||||||
registerComponentHook(VirtualComponent.cid, 'lifecycle', 'create', componentId => {
|
// 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
|
// create virtual component
|
||||||
const options = { componentId }
|
// const subVm =
|
||||||
return new VirtualComponent(options)
|
new VirtualComponent({
|
||||||
})
|
componentId,
|
||||||
|
propsData
|
||||||
|
})
|
||||||
|
// if (vm._virtualComponents) {
|
||||||
|
// vm._virtualComponents[componentId] = subVm
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
registerComponentHook(cid, 'lifecycle', 'create', createVirtualComponent)
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
delete this._virtualComponents
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -70,13 +70,17 @@ export function registerComponentHook (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Updates the state of the component to weex native render engine.
|
// Updates the state of the component to weex native render engine.
|
||||||
export function updateComponentData (componentId: string, newData: Object) {
|
export function updateComponentData (
|
||||||
|
componentId: string,
|
||||||
|
newData: Object | void,
|
||||||
|
callback?: Function
|
||||||
|
) {
|
||||||
if (!document || !document.taskCenter) {
|
if (!document || !document.taskCenter) {
|
||||||
warn(`Can't find available "document" or "taskCenter".`)
|
warn(`Can't find available "document" or "taskCenter".`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (typeof document.taskCenter.updateData === 'function') {
|
if (typeof document.taskCenter.updateData === 'function') {
|
||||||
return document.taskCenter.updateData(componentId, newData)
|
return document.taskCenter.updateData(componentId, newData, callback)
|
||||||
}
|
}
|
||||||
warn(`Failed to update component data (${componentId}).`)
|
warn(`Failed to update component data (${componentId}).`)
|
||||||
}
|
}
|
||||||
|
@ -137,29 +137,45 @@ describe('Usage', () => {
|
|||||||
const id = String(Date.now() * Math.random())
|
const id = String(Date.now() * Math.random())
|
||||||
const instance = createInstance(id, code)
|
const instance = createInstance(id, code)
|
||||||
expect(tasks.length).toEqual(3)
|
expect(tasks.length).toEqual(3)
|
||||||
tasks.length = 0
|
|
||||||
instance.$triggerHook(2, 'create', ['component-1'])
|
|
||||||
instance.$triggerHook(2, 'create', ['component-2'])
|
|
||||||
instance.$triggerHook('component-1', 'attach')
|
|
||||||
instance.$triggerHook('component-2', 'attach')
|
|
||||||
expect(tasks.length).toEqual(2)
|
|
||||||
expect(tasks[0].method).toEqual('updateComponentData')
|
|
||||||
// expect(tasks[0].args).toEqual([{ count: 42 }])
|
|
||||||
expect(tasks[1].method).toEqual('updateComponentData')
|
|
||||||
// expect(tasks[1].args).toEqual([{ count: 42 }])
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
// check the render results
|
||||||
const target = readObject('recycle-list/components/stateful.vdom.js')
|
const target = readObject('recycle-list/components/stateful.vdom.js')
|
||||||
expect(getRoot(instance)).toEqual(target)
|
expect(getRoot(instance)).toEqual(target)
|
||||||
const event = getEvents(instance)[0]
|
|
||||||
tasks.length = 0
|
tasks.length = 0
|
||||||
fireEvent(instance, event.ref, event.type, {})
|
|
||||||
|
// trigger component hooks
|
||||||
|
instance.$triggerHook(
|
||||||
|
2, // cid of the virtual component template
|
||||||
|
'create', // lifecycle hook name
|
||||||
|
|
||||||
|
// arguments for the callback
|
||||||
|
[
|
||||||
|
'x-1', // componentId of the virtual component
|
||||||
|
{ start: 3 } // propsData of the virtual component
|
||||||
|
]
|
||||||
|
)
|
||||||
|
instance.$triggerHook(2, 'create', ['x-2', { start: 11 }])
|
||||||
|
|
||||||
|
// the state (_data) of the virtual component should be sent to native
|
||||||
|
expect(tasks.length).toEqual(2)
|
||||||
|
expect(tasks[0].method).toEqual('updateComponentData')
|
||||||
|
expect(tasks[0].args).toEqual(['x-1', { count: 6 }, ''])
|
||||||
|
expect(tasks[1].method).toEqual('updateComponentData')
|
||||||
|
expect(tasks[1].args).toEqual(['x-2', { count: 22 }, ''])
|
||||||
|
|
||||||
|
instance.$triggerHook('x-1', 'attach')
|
||||||
|
instance.$triggerHook('x-2', 'attach')
|
||||||
|
tasks.length = 0
|
||||||
|
|
||||||
|
// simulate a click event
|
||||||
|
// the event will be caught by the virtual component template and
|
||||||
|
// should be dispatched to virtual component according to the componentId
|
||||||
|
const event = getEvents(instance)[0]
|
||||||
|
fireEvent(instance, event.ref, 'click', { componentId: 'x-1' })
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// expect(tasks.length).toEqual(1)
|
// expect(tasks.length).toEqual(1)
|
||||||
// expect(tasks[0]).toEqual({
|
// expect(tasks[0].method).toEqual('updateComponentData')
|
||||||
// module: 'dom',
|
// expect(tasks[0].args).toEqual([{ count: 7 }])
|
||||||
// method: 'updateComponentData',
|
|
||||||
// args: [{ count: 43 }]
|
|
||||||
// })
|
|
||||||
instance.$destroy()
|
instance.$destroy()
|
||||||
resetTaskHook()
|
resetTaskHook()
|
||||||
done()
|
done()
|
||||||
@ -168,6 +184,39 @@ describe('Usage', () => {
|
|||||||
}).catch(done.fail)
|
}).catch(done.fail)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// it('component lifecycle', done => {
|
||||||
|
// global.__lifecycles = []
|
||||||
|
// compileWithDeps('recycle-list/components/stateful-lifecycle.vue', [{
|
||||||
|
// name: 'lifecycle',
|
||||||
|
// path: 'recycle-list/components/lifecycle.vue'
|
||||||
|
// }]).then(code => {
|
||||||
|
// const id = String(Date.now() * Math.random())
|
||||||
|
// const instance = createInstance(id, code)
|
||||||
|
// setTimeout(() => {
|
||||||
|
// const target = readObject('recycle-list/components/stateful-lifecycle.vdom.js')
|
||||||
|
// expect(getRoot(instance)).toEqual(target)
|
||||||
|
|
||||||
|
// instance.$triggerHook(2, 'create', ['y-1'])
|
||||||
|
// instance.$triggerHook('y-1', 'attach')
|
||||||
|
// instance.$triggerHook('y-1', 'detach')
|
||||||
|
// expect(global.__lifecycles).toEqual([
|
||||||
|
// 'beforeCreate undefined',
|
||||||
|
// 'created 0',
|
||||||
|
// 'beforeMount 1',
|
||||||
|
// 'mounted 1',
|
||||||
|
// 'beforeUpdate 2',
|
||||||
|
// 'updated 2',
|
||||||
|
// 'beforeDestroy 2',
|
||||||
|
// 'destroyed 2'
|
||||||
|
// ])
|
||||||
|
|
||||||
|
// delete global.__lifecycles
|
||||||
|
// instance.$destroy()
|
||||||
|
// done()
|
||||||
|
// }, 50)
|
||||||
|
// }).catch(done.fail)
|
||||||
|
// })
|
||||||
|
|
||||||
it('stateful component with v-model', done => {
|
it('stateful component with v-model', done => {
|
||||||
compileWithDeps('recycle-list/components/stateful-v-model.vue', [{
|
compileWithDeps('recycle-list/components/stateful-v-model.vue', [{
|
||||||
name: 'editor',
|
name: 'editor',
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
props: ['start'],
|
props: ['start'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
count: parseInt(this.start, 10) || 42
|
count: parseInt(this.start, 10) * 2 || 42
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
39
test/weex/cases/recycle-list/components/lifecycle.vue
Normal file
39
test/weex/cases/recycle-list/components/lifecycle.vue
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<template recyclable="true">
|
||||||
|
<div>
|
||||||
|
<text>{{number}}</text>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
module.exports = {
|
||||||
|
data () {
|
||||||
|
return { number: 0 }
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
try { __lifecycles.push('beforeCreate ' + this.number) } catch (e) {}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
try { __lifecycles.push('created ' + this.number) } catch (e) {}
|
||||||
|
this.number++
|
||||||
|
},
|
||||||
|
beforeMount () {
|
||||||
|
try { __lifecycles.push('beforeMount ' + this.number) } catch (e) {}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
try { __lifecycles.push('mounted ' + this.number) } catch (e) {}
|
||||||
|
this.number++
|
||||||
|
},
|
||||||
|
beforeUpdate () {
|
||||||
|
try { __lifecycles.push('beforeUpdate ' + this.number) } catch (e) {}
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
try { __lifecycles.push('updated ' + this.number) } catch (e) {}
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
try { __lifecycles.push('beforeDestroy ' + this.number) } catch (e) {}
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
try { __lifecycles.push('destroyed ' + this.number) } catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,29 @@
|
|||||||
|
({
|
||||||
|
type: 'recycle-list',
|
||||||
|
attr: {
|
||||||
|
append: 'tree',
|
||||||
|
listData: [
|
||||||
|
{ type: 'X' },
|
||||||
|
{ type: 'X' }
|
||||||
|
],
|
||||||
|
templateKey: 'type',
|
||||||
|
alias: 'item'
|
||||||
|
},
|
||||||
|
children: [{
|
||||||
|
type: 'cell-slot',
|
||||||
|
attr: { append: 'tree', templateType: 'X' },
|
||||||
|
children: [{
|
||||||
|
type: 'div',
|
||||||
|
attr: {
|
||||||
|
'@isComponentRoot': true,
|
||||||
|
'@componentProps': {}
|
||||||
|
},
|
||||||
|
children: [{
|
||||||
|
type: 'text',
|
||||||
|
attr: {
|
||||||
|
value: { '@binding': 'number' }
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
})
|
@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<recycle-list :list-data="longList" template-key="type" alias="item">
|
||||||
|
<cell-slot template-type="X">
|
||||||
|
<lifecycle></lifecycle>
|
||||||
|
</cell-slot>
|
||||||
|
</recycle-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// require('./lifecycle.vue')
|
||||||
|
module.exports = {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
longList: [
|
||||||
|
{ type: 'X' },
|
||||||
|
{ type: 'X' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -36,7 +36,7 @@ describe('framework APIs', () => {
|
|||||||
type: 'div',
|
type: 'div',
|
||||||
children: [{
|
children: [{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
attr: { value: '{"bundleUrl":"http://example.com/","a":1,"b":2,"env":{}}' }
|
attr: { value: '{"bundleUrl":"http://example.com/","a":1,"b":2,"env":{},"bundleType":"Vue"}' }
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -170,6 +170,7 @@ describe('framework APIs', () => {
|
|||||||
`, { bundleUrl: 'http://whatever.com/x.js' })
|
`, { bundleUrl: 'http://whatever.com/x.js' })
|
||||||
expect(JSON.parse(getRoot(instance).children[0].attr.value)).toEqual({
|
expect(JSON.parse(getRoot(instance).children[0].attr.value)).toEqual({
|
||||||
bundleUrl: 'http://whatever.com/x.js',
|
bundleUrl: 'http://whatever.com/x.js',
|
||||||
|
bundleType: 'Vue',
|
||||||
env: {
|
env: {
|
||||||
weexVersion: '0.10.0',
|
weexVersion: '0.10.0',
|
||||||
platform: 'Node.js'
|
platform: 'Node.js'
|
||||||
|
Loading…
Reference in New Issue
Block a user