diff --git a/flow/component.js b/flow/component.js
index bf984cd6dc..b873bc2450 100644
--- a/flow/component.js
+++ b/flow/component.js
@@ -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
diff --git a/flow/options.js b/flow/options.js
index 3ae6509df6..e4c6ed1f96 100644
--- a/flow/options.js
+++ b/flow/options.js
@@ -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 };
diff --git a/package-lock.json b/package-lock.json
index 1135b8d669..0f3b963dd5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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": {
diff --git a/package.json b/package.json
index fe58ff2634..ecbb0fd9d3 100644
--- a/package.json
+++ b/package.json
@@ -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": {
diff --git a/src/core/instance/state.js b/src/core/instance/state.js
index e39fb2e0a5..01e1e8720d 100644
--- a/src/core/instance/state.js
+++ b/src/core/instance/state.js
@@ -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) {
diff --git a/src/platforms/weex/runtime/recycle-list/virtual-component.js b/src/platforms/weex/runtime/recycle-list/virtual-component.js
index ed370d8c9f..e5498d2689 100644
--- a/src/platforms/weex/runtime/recycle-list/virtual-component.js
+++ b/src/platforms/weex/runtime/recycle-list/virtual-component.js
@@ -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
}
})
}
diff --git a/src/platforms/weex/util/index.js b/src/platforms/weex/util/index.js
index cffc2ede79..77bc318c55 100755
--- a/src/platforms/weex/util/index.js
+++ b/src/platforms/weex/util/index.js
@@ -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}).`)
}
diff --git a/test/weex/cases/cases.spec.js b/test/weex/cases/cases.spec.js
index 532544cdd1..e5825ec743 100644
--- a/test/weex/cases/cases.spec.js
+++ b/test/weex/cases/cases.spec.js
@@ -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',
diff --git a/test/weex/cases/recycle-list/components/counter.vue b/test/weex/cases/recycle-list/components/counter.vue
index c0298dee6e..79b8189b03 100644
--- a/test/weex/cases/recycle-list/components/counter.vue
+++ b/test/weex/cases/recycle-list/components/counter.vue
@@ -10,7 +10,7 @@
props: ['start'],
data () {
return {
- count: parseInt(this.start, 10) || 42
+ count: parseInt(this.start, 10) * 2 || 42
}
},
methods: {
diff --git a/test/weex/cases/recycle-list/components/lifecycle.vue b/test/weex/cases/recycle-list/components/lifecycle.vue
new file mode 100644
index 0000000000..03ee65ada5
--- /dev/null
+++ b/test/weex/cases/recycle-list/components/lifecycle.vue
@@ -0,0 +1,39 @@
+
+
+ {{number}}
+
+
+
+
diff --git a/test/weex/cases/recycle-list/components/stateful-lifecycle.vdom.js b/test/weex/cases/recycle-list/components/stateful-lifecycle.vdom.js
new file mode 100644
index 0000000000..5264259f14
--- /dev/null
+++ b/test/weex/cases/recycle-list/components/stateful-lifecycle.vdom.js
@@ -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' }
+ }
+ }]
+ }]
+ }]
+})
diff --git a/test/weex/cases/recycle-list/components/stateful-lifecycle.vue b/test/weex/cases/recycle-list/components/stateful-lifecycle.vue
new file mode 100644
index 0000000000..c01a96b6d1
--- /dev/null
+++ b/test/weex/cases/recycle-list/components/stateful-lifecycle.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
diff --git a/test/weex/runtime/framework.spec.js b/test/weex/runtime/framework.spec.js
index 449d877260..5a8daed461 100644
--- a/test/weex/runtime/framework.spec.js
+++ b/test/weex/runtime/framework.spec.js
@@ -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'