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;
|
||||
_provided: ?Object;
|
||||
_inlineComputed: ?{ [key: string]: Watcher }; // inline computed watchers for literal props
|
||||
// _virtualComponents?: { [key: string]: Component };
|
||||
|
||||
// private methods
|
||||
|
||||
|
@ -11,6 +11,8 @@ declare type InternalComponentOptions = {
|
||||
type InjectKey = string | Symbol;
|
||||
|
||||
declare type ComponentOptions = {
|
||||
componentId?: string;
|
||||
|
||||
// data
|
||||
data: Object | Function | void;
|
||||
props?: { [key: string]: PropOptions };
|
||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -9796,9 +9796,9 @@
|
||||
}
|
||||
},
|
||||
"weex-js-runtime": {
|
||||
"version": "0.23.4",
|
||||
"resolved": "https://registry.npmjs.org/weex-js-runtime/-/weex-js-runtime-0.23.4.tgz",
|
||||
"integrity": "sha512-leBzBrpvbrKHvmwd00YjzxZAQrp6NWEbEt3jAtk5EINccxZzFhwZ8SpUmn0d5bspemFisT18eJDAavv4LgRxpw==",
|
||||
"version": "0.23.5",
|
||||
"resolved": "https://registry.npmjs.org/weex-js-runtime/-/weex-js-runtime-0.23.5.tgz",
|
||||
"integrity": "sha512-94/bMUpCyZMsrq2codDPFatr5Ec8yKYKYNsfoshQOiQKTZY3pwqlfedtOKQNf6k7o4npEhdxDnHJwEVORtNylg==",
|
||||
"dev": true
|
||||
},
|
||||
"weex-styler": {
|
||||
|
@ -125,7 +125,7 @@
|
||||
"typescript": "^2.6.1",
|
||||
"uglify-js": "^3.0.15",
|
||||
"webpack": "^3.10.0",
|
||||
"weex-js-runtime": "^0.23.3",
|
||||
"weex-js-runtime": "^0.23.5",
|
||||
"weex-styler": "^0.3.0"
|
||||
},
|
||||
"config": {
|
||||
|
@ -150,7 +150,7 @@ function initData (vm: Component) {
|
||||
observe(data, true /* asRootData */)
|
||||
}
|
||||
|
||||
function getData (data: Function, vm: Component): any {
|
||||
export function getData (data: Function, vm: Component): any {
|
||||
try {
|
||||
return data.call(vm, vm)
|
||||
} catch (e) {
|
||||
|
@ -2,13 +2,14 @@
|
||||
|
||||
// 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 { initState } from 'core/instance/state'
|
||||
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, mountComponent, callHook } from 'core/instance/lifecycle'
|
||||
import { initLifecycle, callHook } from 'core/instance/lifecycle'
|
||||
import { initInternalComponent, resolveConstructorOptions } from 'core/instance/init'
|
||||
import { registerComponentHook, updateComponentData } from '../../util/index'
|
||||
|
||||
@ -55,8 +56,25 @@ function initVirtualComponent (options: Object = {}) {
|
||||
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', () => {
|
||||
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', () => {
|
||||
@ -65,25 +83,53 @@ function initVirtualComponent (options: Object = {}) {
|
||||
}
|
||||
|
||||
// override Vue.prototype._update
|
||||
function updateVirtualComponent (vnode: VNode, hydrating?: boolean) {
|
||||
// TODO
|
||||
updateComponentData(this.$options.componentId, {})
|
||||
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 () {
|
||||
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
|
||||
const options = { componentId }
|
||||
return new VirtualComponent(options)
|
||||
})
|
||||
// const subVm =
|
||||
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.
|
||||
export function updateComponentData (componentId: string, newData: Object) {
|
||||
export function updateComponentData (
|
||||
componentId: string,
|
||||
newData: Object | void,
|
||||
callback?: Function
|
||||
) {
|
||||
if (!document || !document.taskCenter) {
|
||||
warn(`Can't find available "document" or "taskCenter".`)
|
||||
return
|
||||
}
|
||||
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}).`)
|
||||
}
|
||||
|
@ -137,29 +137,45 @@ describe('Usage', () => {
|
||||
const id = String(Date.now() * Math.random())
|
||||
const instance = createInstance(id, code)
|
||||
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(() => {
|
||||
// check the render results
|
||||
const target = readObject('recycle-list/components/stateful.vdom.js')
|
||||
expect(getRoot(instance)).toEqual(target)
|
||||
const event = getEvents(instance)[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(() => {
|
||||
// expect(tasks.length).toEqual(1)
|
||||
// expect(tasks[0]).toEqual({
|
||||
// module: 'dom',
|
||||
// method: 'updateComponentData',
|
||||
// args: [{ count: 43 }]
|
||||
// })
|
||||
// expect(tasks[0].method).toEqual('updateComponentData')
|
||||
// expect(tasks[0].args).toEqual([{ count: 7 }])
|
||||
instance.$destroy()
|
||||
resetTaskHook()
|
||||
done()
|
||||
@ -168,6 +184,39 @@ describe('Usage', () => {
|
||||
}).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 => {
|
||||
compileWithDeps('recycle-list/components/stateful-v-model.vue', [{
|
||||
name: 'editor',
|
||||
|
@ -10,7 +10,7 @@
|
||||
props: ['start'],
|
||||
data () {
|
||||
return {
|
||||
count: parseInt(this.start, 10) || 42
|
||||
count: parseInt(this.start, 10) * 2 || 42
|
||||
}
|
||||
},
|
||||
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',
|
||||
children: [{
|
||||
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' })
|
||||
expect(JSON.parse(getRoot(instance).children[0].attr.value)).toEqual({
|
||||
bundleUrl: 'http://whatever.com/x.js',
|
||||
bundleType: 'Vue',
|
||||
env: {
|
||||
weexVersion: '0.10.0',
|
||||
platform: 'Node.js'
|
||||
|
Loading…
Reference in New Issue
Block a user