From 172712a7d60b2ed8229078c75c9a297e4fa73f79 Mon Sep 17 00:00:00 2001 From: terrykingcha Date: Wed, 13 Apr 2016 17:18:05 +0800 Subject: [PATCH] * [jsfm] merge many things --- .../lib/__test__/assets/dynamic-type.input | 27 ++ .../lib/__test__/assets/dynamic-type.output | 10 + .../lib/__test__/assets/repeat-index.input | 41 ++ .../lib/__test__/assets/repeat-index.output | 22 + .../lib/__test__/assets/require.input | 2 +- .../lib/__test__/assets/transformer1.input | 2 +- .../lib/__test__/assets/transformer2.input | 2 +- src/js-framework/lib/__test__/test.js | 32 ++ src/js-framework/lib/api/__test__/methods.js | 6 +- src/js-framework/lib/api/methods.js | 9 +- src/js-framework/lib/app/__test__/bundle.js | 2 +- src/js-framework/lib/app/ctrl.js | 43 +- src/js-framework/lib/app/dom.js | 6 + src/js-framework/lib/app/event.js | 4 +- src/js-framework/lib/framework.js | 3 +- src/js-framework/lib/vm/__test__/vm.js | 387 ++++++++++-------- src/js-framework/lib/vm/compiler.js | 47 ++- src/js-framework/package.json | 11 +- 18 files changed, 438 insertions(+), 218 deletions(-) create mode 100644 src/js-framework/lib/__test__/assets/dynamic-type.input create mode 100644 src/js-framework/lib/__test__/assets/dynamic-type.output create mode 100644 src/js-framework/lib/__test__/assets/repeat-index.input create mode 100644 src/js-framework/lib/__test__/assets/repeat-index.output diff --git a/src/js-framework/lib/__test__/assets/dynamic-type.input b/src/js-framework/lib/__test__/assets/dynamic-type.input new file mode 100644 index 0000000000..bb9359afb2 --- /dev/null +++ b/src/js-framework/lib/__test__/assets/dynamic-type.input @@ -0,0 +1,27 @@ +define('@weex-component/dynamic-type', function (require, exports, module) { + +; + module.exports = { + data: function () {return { + type: 'text' + }} + } + +;module.exports.template = { + "type": "container", + "children": [ + { + "type": function () {return this.type}, + "style": { + "width": 200, + "height": 200 + } + } + ] +} + +;}) + +// require module + +bootstrap('@weex-component/dynamic-type') diff --git a/src/js-framework/lib/__test__/assets/dynamic-type.output b/src/js-framework/lib/__test__/assets/dynamic-type.output new file mode 100644 index 0000000000..d6881a4956 --- /dev/null +++ b/src/js-framework/lib/__test__/assets/dynamic-type.output @@ -0,0 +1,10 @@ +{ + type: 'container', + children: [{ + type: 'text', + style: { + width: 200, + height: 200 + } + }] +} diff --git a/src/js-framework/lib/__test__/assets/repeat-index.input b/src/js-framework/lib/__test__/assets/repeat-index.input new file mode 100644 index 0000000000..eea9a840c2 --- /dev/null +++ b/src/js-framework/lib/__test__/assets/repeat-index.input @@ -0,0 +1,41 @@ +define('@weex-component/repeat-index', function (require, exports, module) { + +; + module.exports = { + data: function () {return { + titlelist: [ + {title: 'Hello World1'}, + {title: 'Hello World2'} + ] + }} + } + + +;module.exports.style = { + "title": { + "fontSize": 26, + "color": "#FF0000" + } +} + +;module.exports.template = { + "type": "container", + "children": [ + { + "type": "text", + "repeat": function () {return this.titlelist}, + "classList": [ + "title" + ], + "attr": { + "value": function () {return this.INDEX + ': ' + this.title} + } + } + ] +} + +;}) + +// require module + +bootstrap('@weex-component/repeat-index') diff --git a/src/js-framework/lib/__test__/assets/repeat-index.output b/src/js-framework/lib/__test__/assets/repeat-index.output new file mode 100644 index 0000000000..4d4991acb6 --- /dev/null +++ b/src/js-framework/lib/__test__/assets/repeat-index.output @@ -0,0 +1,22 @@ +{ + type: 'container', + children: [{ + type: 'text', + style: { + color: '#FF0000', + fontSize: 26 + }, + attr: { + value: '0: Hello World1' + } + }, { + type: 'text', + style: { + color: '#FF0000', + fontSize: 26 + }, + attr: { + value: '1: Hello World2' + } + }] +} diff --git a/src/js-framework/lib/__test__/assets/require.input b/src/js-framework/lib/__test__/assets/require.input index 8d74d46f09..a6da3064f1 100644 --- a/src/js-framework/lib/__test__/assets/require.input +++ b/src/js-framework/lib/__test__/assets/require.input @@ -31,4 +31,4 @@ define('@weex-component/foo', function (require, exports, module) { ;}) // require module -bootstrap('@weex-component/foo', {"transformerVersion":"0.1.99"}) \ No newline at end of file +bootstrap('@weex-component/foo', {"transformerVersion":"0.1.5"}) \ No newline at end of file diff --git a/src/js-framework/lib/__test__/assets/transformer1.input b/src/js-framework/lib/__test__/assets/transformer1.input index 581a40737f..349d0b62d9 100644 --- a/src/js-framework/lib/__test__/assets/transformer1.input +++ b/src/js-framework/lib/__test__/assets/transformer1.input @@ -24,5 +24,5 @@ define('@weex-component/foo', function (require, exports, module) { bootstrap('@weex-component/foo', { - 'transformerVersion': '0.1.99' + 'transformerVersion': '0.1.5' }) \ No newline at end of file diff --git a/src/js-framework/lib/__test__/assets/transformer2.input b/src/js-framework/lib/__test__/assets/transformer2.input index 60830c63f2..82cff5dab3 100644 --- a/src/js-framework/lib/__test__/assets/transformer2.input +++ b/src/js-framework/lib/__test__/assets/transformer2.input @@ -24,5 +24,5 @@ define('@weex-component/foo', function (require, exports, module) { bootstrap('@weex-component/foo', { - 'transformerVersion': '0.1.0' + 'transformerVersion': '0.0.1' }) \ No newline at end of file diff --git a/src/js-framework/lib/__test__/test.js b/src/js-framework/lib/__test__/test.js index eb66c731b4..7c329e846a 100644 --- a/src/js-framework/lib/__test__/test.js +++ b/src/js-framework/lib/__test__/test.js @@ -423,6 +423,22 @@ describe('test input and output', function () { delete allDocs[name] }) + it('repeat with index case', function () { + var name = 'repeat-index' + var inputCode = readInput(name) + var outputCode = readOutput(name) + var doc = new Document(name) + allDocs[name] = doc + + framework.createInstance(name, inputCode) + var expected = eval('(' + outputCode + ')') + var actual = doc.toJSON() + expect(actual).eql(expected) + + framework.destroyInstance(name) + delete allDocs[name] + }) + it('if-refresh case', function () { var name = 'if-refresh' var inputCode = readInput(name) @@ -479,6 +495,22 @@ describe('test input and output', function () { delete allDocs[name] }) + it('dynamic type case', function () { + var name = 'dynamic-type' + var inputCode = readInput(name) + var outputCode = readOutput(name) + var doc = new Document(name) + allDocs[name] = doc + + framework.createInstance(name, inputCode) + var expected = eval('(' + outputCode + ')') + var actual = doc.toJSON() + expect(actual).eql(expected) + + framework.destroyInstance(name) + delete allDocs[name] + }) + it('click case', function () { var name = 'click' var inputCode = readInput(name) diff --git a/src/js-framework/lib/api/__test__/methods.js b/src/js-framework/lib/api/__test__/methods.js index 74a1ee4776..ca2c5c06ec 100644 --- a/src/js-framework/lib/api/__test__/methods.js +++ b/src/js-framework/lib/api/__test__/methods.js @@ -68,7 +68,6 @@ describe('built-in', () => { }) beforeEach(() => { - vm._app.differ = Promise.resolve() requireSpy.reset() moduleSpy.reset() }) @@ -80,7 +79,10 @@ describe('built-in', () => { describe('common apis', () => { it('$', () => { - expect(vm.$('a')).to.deep.equal(vm._ids.a.el) + global.nativeLog = sinon.spy() + expect(vm.$('a')).to.deep.equal(vm._ids.a.vm) + expect(global.nativeLog.callCount).to.be.equal(1) + global.nativeLog = undefined }) it('$el', () => { diff --git a/src/js-framework/lib/api/methods.js b/src/js-framework/lib/api/methods.js index ebbe31a6b6..c8a6a1117c 100644 --- a/src/js-framework/lib/api/methods.js +++ b/src/js-framework/lib/api/methods.js @@ -10,16 +10,17 @@ import {typof, extend} from '../util' */ /** - * find the element by id + * @deprecated use $vm instead + * find the vm by id * Note: there is only one id in whole component - * @alias $el * @param {string} id - * @return {Element} + * @return {Vm} */ export function $(id) { + nativeLog('the Vm#$ api is deprecated, please use Vm#$vm instead') const info = this._ids[id] if (info) { - return info.el + return info.vm } } diff --git a/src/js-framework/lib/app/__test__/bundle.js b/src/js-framework/lib/app/__test__/bundle.js index b1b9a2c3c4..3074091b00 100644 --- a/src/js-framework/lib/app/__test__/bundle.js +++ b/src/js-framework/lib/app/__test__/bundle.js @@ -186,7 +186,7 @@ describe('parsing a bundle file', () => { const ready = sinon.spy() before(() => { - global.needTransformerVersion = '~0.1.3' + global.needTransformerVersion = '~0.1' app.define('@weex-component/main', (require, exports, module) => { module.exports = { template: componentTemplate, diff --git a/src/js-framework/lib/app/ctrl.js b/src/js-framework/lib/app/ctrl.js index 67c467cb9e..9a942a31cf 100644 --- a/src/js-framework/lib/app/ctrl.js +++ b/src/js-framework/lib/app/ctrl.js @@ -99,24 +99,37 @@ export function updateActions(addonTasks) { } } -export function fireEvent(ref, type, e) { +export function fireEvent(ref, type, e, domChanges) { + if (Array.isArray(ref)) { + ref.some((ref) => { + return this.fireEvent(ref, type, e) !== false + }) + return + } + const el = this.doc.getRef(ref) + if (el) { perf.start('manage event', ref + '-' + type) e = e || {} e.type = type e.target = el e.timestamp = Date.now() - this.eventManager.fire(el, type, e) + if (domChanges) { + updateElement(el, domChanges) + } + const result = this.eventManager.fire(el, type, e) perf.end('manage event', ref + '-' + type) this.updateActions() - } else { - return new Error(`invalid element reference "${ref}"`) + return result } + + return new Error(`invalid element reference "${ref}"`) } export function callback(callbackId, data, ifLast) { const callback = this.callbacks[callbackId] + if (typeof callback === 'function') { callback(data) // data is already a object, @see: lib/framework.js @@ -125,9 +138,10 @@ export function callback(callbackId, data, ifLast) { } this.updateActions() - } else { - return new Error(`invalid callback id "${callbackId}"`) + return } + + return new Error(`invalid callback id "${callbackId}"`) } export function refreshData(data) { @@ -140,7 +154,20 @@ export function refreshData(data) { extend(vm, data) } this.updateActions([createAction('refreshFinish', [])]) - } else { - return new Error(`invalid data "${data}"`) + return + } + + return new Error(`invalid data "${data}"`) +} + +function updateElement(el, changes) { + const attrs = changes.attrs || {} + for (const name in attrs) { + el.setAttr(name, attrs) + } + const style = changes.style || {} + for (const name in style) { + el.setStyle(name, style[name]) } } + diff --git a/src/js-framework/lib/app/dom.js b/src/js-framework/lib/app/dom.js index 4400ac2058..49c4fe9f1f 100644 --- a/src/js-framework/lib/app/dom.js +++ b/src/js-framework/lib/app/dom.js @@ -460,6 +460,9 @@ function removeIfExisted(node) { } Element.prototype.setAttr = function (key, value) { + if (this.attr[key] === value) { + return + } this.attr[key] = value if (this.attached) { const renderer = this.getRenderer() @@ -470,6 +473,9 @@ Element.prototype.setAttr = function (key, value) { } Element.prototype.setStyle = function (key, value) { + if (this.style[key] === value) { + return + } this.style[key] = value if (this.attached) { const renderer = this.getRenderer() diff --git a/src/js-framework/lib/app/event.js b/src/js-framework/lib/app/event.js index f333066232..ad03240ff5 100644 --- a/src/js-framework/lib/app/event.js +++ b/src/js-framework/lib/app/event.js @@ -51,7 +51,7 @@ EventManager.prototype.fire = function (el, type, e) { el = target.el handler = target.events[type] if (typeof handler === 'function') { - handler.call(el, e) + return handler.call(el, e) } } -} \ No newline at end of file +} diff --git a/src/js-framework/lib/framework.js b/src/js-framework/lib/framework.js index e42c1c4d07..12cab6a1ee 100644 --- a/src/js-framework/lib/framework.js +++ b/src/js-framework/lib/framework.js @@ -28,14 +28,13 @@ var instanceMap = {} * * @param {string} instanceId * @param {string} code - * @param {object} [options] option `debug` enable print log + * @param {object} [options] option `HAS_LOG` enable print log * @param {object} [data] */ export function createInstance(instanceId, code, options, data) { var instance = instanceMap[instanceId] options = options || {} - /* istanbul ignore if */ config.debug = options.debug var result diff --git a/src/js-framework/lib/vm/__test__/vm.js b/src/js-framework/lib/vm/__test__/vm.js index fb78aa9149..7335d5c011 100644 --- a/src/js-framework/lib/vm/__test__/vm.js +++ b/src/js-framework/lib/vm/__test__/vm.js @@ -277,7 +277,10 @@ describe('generate virtual dom for a single vm', () => { expect(vm._app).equal(app) expect(vm.x).eql('') - expect(vm.list).eql([{uid: 1, x: 1}, {uid: 2, x: 2}, {uid: 3}]) + expect(vm.list).eql([ + {uid: 1, x: 1, INDEX: 0}, + {uid: 2, x: 2, INDEX: 1}, + {uid: 3, INDEX: 2}]) var el = doc.body expect(el.type).eql('container') @@ -347,7 +350,7 @@ describe('generate virtual dom for a single vm', () => { {type: 'prev'}, { shown: function () {return this.x % 2 === 0}, - repeat: function () {return this.list}, trackBy: 'uid', + repeat: function () {return this.list}, type: 'image', attr: {src: function () {return this.x}} }, {type: 'next'} @@ -366,7 +369,10 @@ describe('generate virtual dom for a single vm', () => { expect(vm._app).equal(app) expect(vm.x).eql('') - expect(vm.list).eql([{uid: 1, x: 1}, {uid: 2, x: 2}, {uid: 3}]) + expect(vm.list).eql([ + {uid: 1, x: 1, INDEX: 0}, + {uid: 2, x: 2, INDEX: 1}, + {uid: 3, INDEX: 2}]) var el = doc.body expect(el.type).eql('container') @@ -432,9 +438,9 @@ describe('generate virtual dom for sub vm', () => { showbar1: false, showbar2: false, bar2list: [{ - index: 'bar2-1' + id: 'bar2-1' },{ - index: 'bar2-2' + id: 'bar2-2' }] } }, @@ -449,7 +455,7 @@ describe('generate virtual dom for sub vm', () => { {type: 'bar2', shown: function () {return this.showbar2}, repeat: function () {return this.bar2list}, - id: function () {return this.index}, + id: function () {return this.id}, component: true } ] @@ -804,7 +810,7 @@ describe('generate virtual dom for sub vm', () => { children: [{type: 'bar', component: true, attr: { x: function () {return this.a}, y: function () {return this.b} - }, repeat: function () {return this.list}, trackBy: 'uid'}] + }, repeat: function () {return this.list}}] } } customComponentMap.bar = { @@ -970,7 +976,7 @@ describe('generate dom actions', () => { destroyDocument('test') }) - it('received create body and add element actions', (done) => { + it('received create body and add element actions', () => { var handler = sinon.spy() customComponentMap.foo = { @@ -996,33 +1002,27 @@ describe('generate dom actions', () => { } var vm = new Vm('foo', {_app: app}) + var el = { + ref: '_root', + type: 'container', + attr: {a: 1, b: 2}, style: {c: 3, d: 4, e: 6}, + event: ['click'] + } - checkReady(vm, () => { - - var el = { - ref: '_root', - type: 'container', - attr: {a: 1, b: 2}, style: {c: 3, d: 4, e: 6}, - event: ['click'] - } - - expect(spy.args.length).eql(1) - expect(spy.args[0]).eql(['bar', 'createBody', el]) - - expect(doc.eventManager.add.args.length).eql(1) - expect(doc.eventManager.add.args[0][0].ref).eql('_root') - expect(doc.eventManager.add.args[0][1]).eql('click') - expect(doc.eventManager.add.args[0][2]).is.a.function + expect(spy.args.length).eql(1) + expect(spy.args[0]).eql(['bar', 'createBody', el]) - expect(handler.args.length).eql(0) - doc.eventManager.add.args[0][2]() - expect(handler.args.length).eql(1) + expect(doc.eventManager.add.args.length).eql(1) + expect(doc.eventManager.add.args[0][0].ref).eql('_root') + expect(doc.eventManager.add.args[0][1]).eql('click') + expect(doc.eventManager.add.args[0][2]).is.a.function - done() - }) + expect(handler.args.length).eql(0) + doc.eventManager.add.args[0][2]() + expect(handler.args.length).eql(1) }) - it('received actions for a template with if & repeat', (done) => { + it('received actions for a template with if & repeat', () => { customComponentMap.foo = { template: { @@ -1031,7 +1031,7 @@ describe('generate dom actions', () => { {type: 'prev'}, { shown: function () {return this.x % 2 === 0}, - repeat: function () {return this.list}, trackBy: 'uid', + repeat: function () {return this.list}, type: 'image', attr: {src: function () {return this.x}} }, {type: 'next'} @@ -1046,69 +1046,63 @@ describe('generate dom actions', () => { } var vm = new Vm('foo', {_app: app}) + var el = {ref: '_root', type: 'container', attr: {}, style: {}} + var prev = {ref: '3', type: 'prev', attr: {}, style: {}} + var img = {ref: '10', type: 'image', attr: { src: 2 }, style: {}} + var next = {ref: '13', type: 'next', attr: {}, style: {}} - checkReady(vm, () => { + expect(spy.args.length).eql(4) + expect(spy.args[0]).eql(['bar', 'createBody', el]) + expect(spy.args[1]).eql(['bar', 'addElement', '_root', prev, -1]) + expect(spy.args[2]).eql(['bar', 'addElement', '_root', img, 1]) + expect(spy.args[3]).eql(['bar', 'addElement', '_root',next, -1]) + + vm.list[1].x = 3 + differ.flush() - var el = {ref: '_root', type: 'container', attr: {}, style: {}} - var prev = {ref: '3', type: 'prev', attr: {}, style: {}} - var img = {ref: '10', type: 'image', attr: { src: 2 }, style: {}} - var next = {ref: '13', type: 'next', attr: {}, style: {}} - - expect(spy.args.length).eql(4) - expect(spy.args[0]).eql(['bar', 'createBody', el]) - expect(spy.args[1]).eql(['bar', 'addElement', '_root', prev, -1]) - expect(spy.args[2]).eql(['bar', 'addElement', '_root', img, 1]) - expect(spy.args[3]).eql(['bar', 'addElement', '_root',next, -1]) - - vm.list[1].x = 3 - differ.flush() - - // [1, 3, undefined] - expect(spy.args.length).eql(5) - expect(spy.args[4]).eql(['bar', 'removeElement', '10']) - - vm.list[1].x = 12 - differ.flush() - - // [1, !12, undefined] - expect(spy.args.length).eql(6) - img.ref = '14' - img.attr.src = 12 - expect(spy.args[5]).eql(['bar', 'addElement', '_root', img, 1]) - - vm.x = 'other string value' - differ.flush() - - expect(spy.args.length).eql(6) - - vm.list.push({uid: 4, x: 4}) - vm.list.push({uid: 5}, {uid: 6, x: 6}) - differ.flush() - - // [1, !12, undefined, !4, undefined, !6] - var img2 = {ref: '17', type: 'image', attr: { src: 4 }, style: {}} - var img3 = {ref: '22', type: 'image', attr: { src: 6 }, style: {}} - expect(spy.args.length).eql(8) - expect(spy.args[6]).eql(['bar', 'addElement', '_root', img2, 2]) - expect(spy.args[7]).eql(['bar', 'addElement', '_root', img3, 3]) - - var temp1 = vm.list[1] // 12 - var temp2 = vm.list[5] // 6 - // vm.list.splice(0, 6, temp2, {uid: 7, x: 7}, temp1) - vm.list = [] - vm.list.push(temp2, {uid: 7, x: 7}, temp1) - differ.flush() - - // [!6, 7, !12] - expect(spy.args.length).eql(10) - expect(spy.args[8]).eql(['bar', 'removeElement', '17']) - expect(spy.args[9]).eql(['bar', 'moveElement', '22', '_root', 1]) + // [1, 3, undefined] + expect(spy.args.length).eql(5) + expect(spy.args[4]).eql(['bar', 'removeElement', '10']) - done() - }) + vm.list[1].x = 12 + differ.flush() + + // [1, !12, undefined] + expect(spy.args.length).eql(6) + img.ref = '14' + img.attr.src = 12 + expect(spy.args[5]).eql(['bar', 'addElement', '_root', img, 1]) + + vm.x = 'other string value' + differ.flush() + + expect(spy.args.length).eql(6) + + vm.list.push({uid: 4, x: 4}) + vm.list.push({uid: 5}, {uid: 6, x: 6}) + differ.flush() + + // [1, !12, undefined, !4, undefined, !6] + var img2 = {ref: '17', type: 'image', attr: { src: 4 }, style: {}} + var img3 = {ref: '22', type: 'image', attr: { src: 6 }, style: {}} + expect(spy.args.length).eql(8) + expect(spy.args[6]).eql(['bar', 'addElement', '_root', img2, 2]) + expect(spy.args[7]).eql(['bar', 'addElement', '_root', img3, 3]) + + var temp1 = vm.list[1] // 12 + var temp2 = vm.list[5] // 6 + // vm.list.splice(0, 6, temp2, {uid: 7, x: 7}, temp1) + vm.list = [] + vm.list.push(temp2, {uid: 7, x: 7}, temp1) + differ.flush() + + // [!6, 7, !12] + expect(spy.args.length).eql(10) + expect(spy.args[8]).eql(['bar', 'removeElement', '17']) + expect(spy.args[9]).eql(['bar', 'moveElement', '22', '_root', 1]) }) - it('received actions for element updates', (done) => { + it('received actions for element updates', () => { customComponentMap.foo = { template: { @@ -1124,26 +1118,71 @@ describe('generate dom actions', () => { } var vm = new Vm('foo', {_app: app}) + var length = spy.args.length - checkReady(vm, () => { + vm.x = '' + differ.flush() + expect(spy.args.length - length).eql(0) + + vm.x = 'other string value' + differ.flush() + var change = ['bar', 'updateAttrs', '3', {src: 'other string value'}] + expect(spy.args.length - length).eql(1) + expect(spy.args[length]).eql(change) + }) + + it('received no action when no virtual dom different', () => { + + customComponentMap.foo = { + template: { + type: 'container', children: [ + { + type: 'text', + shown: function () {return this.name.length > 3}, + attr: { + value: function () {return this.name.toUpperCase()} + } + } + ] + }, + data: { + name: 'Mike' + } + } + + var vm = new Vm('foo', {_app: app}) + var length = spy.args.length - var length = spy.args.length + expect(doc.body.pureChildren.length).eql(1) + var text = doc.body.pureChildren[0] + expect(text.attr.value).eql('MIKE') - vm.x = '' - differ.flush() - expect(spy.args.length - length).eql(0) + var initCalls = spy.args.length - vm.x = 'other string value' - differ.flush() - var change = ['bar', 'updateAttrs', '3', {src: 'other string value'}] - expect(spy.args.length - length).eql(1) - expect(spy.args[length]).eql(change) + vm.name = 'MiKe' + differ.flush() - done() - }) + expect(spy.args.length).eql(initCalls) + + text.setAttr('value', 'MIKE') + differ.flush() + + expect(spy.args.length).eql(initCalls) + + text.setAttr('value', 'STEVE') + differ.flush() + + expect(spy.args.length).eql(initCalls + 1) + expect(spy.args[initCalls]).eql([ + 'bar', 'updateAttrs', text.ref, {value: 'STEVE'}]) + + vm.name = 'Steve' + differ.flush() + + expect(spy.args.length).eql(initCalls + 1) }) - it('received actions for components', (done) => { + it('received actions for components', () => { customComponentMap.foo = { template: { @@ -1159,24 +1198,18 @@ describe('generate dom actions', () => { } var vm = new Vm('foo', {_app: app}) - - checkReady(vm, () => { - - expect(spy.args.length).eql(4) - var el = {ref: '_root', type: 'container', attr: {}, style: {}} - expect(spy.args[0]).eql(['bar', 'createBody', el]) - el = {ref: '3', type: 'container', attr: {}, style: {}} - expect(spy.args[1]).eql(['bar', 'addElement', '_root', el, -1]) - el = {ref: '4', type: 'aaa', attr: {}, style: {}} - expect(spy.args[2]).eql(['bar', 'addElement', '3', el, -1]) - el = {ref: '5', type: 'bbb', attr: {}, style: {}} - expect(spy.args[3]).eql(['bar', 'addElement', '3', el, -1]) - - done() - }) + expect(spy.args.length).eql(4) + var el = {ref: '_root', type: 'container', attr: {}, style: {}} + expect(spy.args[0]).eql(['bar', 'createBody', el]) + el = {ref: '3', type: 'container', attr: {}, style: {}} + expect(spy.args[1]).eql(['bar', 'addElement', '_root', el, -1]) + el = {ref: '4', type: 'aaa', attr: {}, style: {}} + expect(spy.args[2]).eql(['bar', 'addElement', '3', el, -1]) + el = {ref: '5', type: 'bbb', attr: {}, style: {}} + expect(spy.args[3]).eql(['bar', 'addElement', '3', el, -1]) }) - it('received actions for complicated components', (done) => { + it('received actions for complicated components', () => { customComponentMap.foo = { data: () => { @@ -1187,7 +1220,7 @@ describe('generate dom actions', () => { children: [{type: 'bar', component: true, attr: { x: function () {return this.a}, y: function () {return this.b} - }, repeat: function () {return this.list}, trackBy: 'uid'}] + }, repeat: function () {return this.list}}] } } customComponentMap.bar = { @@ -1206,27 +1239,22 @@ describe('generate dom actions', () => { var vm = new Vm('foo', {_app: app}) - checkReady(vm, () => { - - // jscs:disable - // expect(spy.args[0]).eql([ 'bar', 'createBody', 'container' ]) - expect(spy.args[0]).eql([ 'bar', 'createBody', { ref: '_root', type: 'container', attr: {}, style: {} }]) - expect(spy.args[1]).eql([ 'bar', 'addElement', '_root', { ref: '5', type: 'container', attr: {}, style: {} }, 0 ]) - expect(spy.args[2]).eql([ 'bar', 'addElement', '5', { ref: '6', type: 'aaa', attr: {a: 1}, style: {} }, -1 ]) - expect(spy.args[3]).eql([ 'bar', 'addElement', '5', { ref: '7', type: 'bbb', attr: {b: 2}, style: {} }, -1 ]) - expect(spy.args[4]).eql([ 'bar', 'addElement', '_root', { ref: '8', type: 'container', attr: {}, style: {} }, 1 ]) - expect(spy.args[5]).eql([ 'bar', 'addElement', '8', { ref: '9', type: 'aaa', attr: {a: 2}, style: {} }, -1 ]) - expect(spy.args[6]).eql([ 'bar', 'addElement', '8', { ref: '10', type: 'bbb', attr: {b: 2}, style: {} }, -1 ]) - expect(spy.args[7]).eql([ 'bar', 'addElement', '_root', { ref: '11', type: 'container', attr: {}, style: {} }, 2 ]) - expect(spy.args[8]).eql([ 'bar', 'addElement', '11', { ref: '12', type: 'aaa', attr: {a: 3}, style: {} }, -1 ]) - expect(spy.args[9]).eql([ 'bar', 'addElement', '11', { ref: '13', type: 'bbb', attr: {b: 2}, style: {} }, -1 ]) - // jscs:enable - - done() - }) + // jscs:disable + // expect(spy.args[0]).eql([ 'bar', 'createBody', 'container' ]) + expect(spy.args[0]).eql([ 'bar', 'createBody', { ref: '_root', type: 'container', attr: {}, style: {} }]) + expect(spy.args[1]).eql([ 'bar', 'addElement', '_root', { ref: '5', type: 'container', attr: {}, style: {} }, 0 ]) + expect(spy.args[2]).eql([ 'bar', 'addElement', '5', { ref: '6', type: 'aaa', attr: {a: 1}, style: {} }, -1 ]) + expect(spy.args[3]).eql([ 'bar', 'addElement', '5', { ref: '7', type: 'bbb', attr: {b: 2}, style: {} }, -1 ]) + expect(spy.args[4]).eql([ 'bar', 'addElement', '_root', { ref: '8', type: 'container', attr: {}, style: {} }, 1 ]) + expect(spy.args[5]).eql([ 'bar', 'addElement', '8', { ref: '9', type: 'aaa', attr: {a: 2}, style: {} }, -1 ]) + expect(spy.args[6]).eql([ 'bar', 'addElement', '8', { ref: '10', type: 'bbb', attr: {b: 2}, style: {} }, -1 ]) + expect(spy.args[7]).eql([ 'bar', 'addElement', '_root', { ref: '11', type: 'container', attr: {}, style: {} }, 2 ]) + expect(spy.args[8]).eql([ 'bar', 'addElement', '11', { ref: '12', type: 'aaa', attr: {a: 3}, style: {} }, -1 ]) + expect(spy.args[9]).eql([ 'bar', 'addElement', '11', { ref: '13', type: 'bbb', attr: {b: 2}, style: {} }, -1 ]) + // jscs:enable }) - it('received actions for add a tree element', (done) => { + it('received actions for add a tree element', () => { customComponentMap.foo = { template: { @@ -1243,50 +1271,45 @@ describe('generate dom actions', () => { var vm = new Vm('foo', {_app: app}) - checkReady(vm, () => { - - expect(spy.args.length).eql(7) - // body, r, r.a, r.b, r.b.d, r.b.e(tree), r.b.f, r.c - - expect(spy.args[0][2].ref).eql('_root') - expect(spy.args[0][2].type).eql('r') - - expect(spy.args[1][3].ref).eql('3') - expect(spy.args[1][3].type).eql('a') - expect(spy.args[1][2]).eql('_root') - expect(spy.args[1][4]).eql(-1) - - expect(spy.args[2][3].ref).eql('4') - expect(spy.args[2][3].type).eql('b') - expect(spy.args[2][2]).eql('_root') - expect(spy.args[2][4]).eql(-1) - - expect(spy.args[3][3].ref).eql('5') - expect(spy.args[3][3].type).eql('d') - expect(spy.args[3][2]).eql('4') - expect(spy.args[3][4]).eql(-1) - - expect(spy.args[4][3].ref).eql('6') - expect(spy.args[4][3].type).eql('e') - expect(spy.args[4][2]).eql('4') - expect(spy.args[4][4]).eql(-1) - expect(spy.args[4][3].children).eql([ - {ref: '7', type: 'g', attr: {}, style: {}}, - {ref: '8', type: 'h', attr: {}, style: {}}, - {ref: '9', type: 'i', attr: {}, style: {}}]) - - expect(spy.args[5][3].ref).eql('10') - expect(spy.args[5][3].type).eql('f') - expect(spy.args[5][2]).eql('4') - expect(spy.args[5][4]).eql(-1) - - expect(spy.args[6][3].ref).eql('11') - expect(spy.args[6][3].type).eql('c') - expect(spy.args[6][2]).eql('_root') - expect(spy.args[6][4]).eql(-1) - - done() - }) + expect(spy.args.length).eql(7) + // body, r, r.a, r.b, r.b.d, r.b.e(tree), r.b.f, r.c + + expect(spy.args[0][2].ref).eql('_root') + expect(spy.args[0][2].type).eql('r') + + expect(spy.args[1][3].ref).eql('3') + expect(spy.args[1][3].type).eql('a') + expect(spy.args[1][2]).eql('_root') + expect(spy.args[1][4]).eql(-1) + + expect(spy.args[2][3].ref).eql('4') + expect(spy.args[2][3].type).eql('b') + expect(spy.args[2][2]).eql('_root') + expect(spy.args[2][4]).eql(-1) + + expect(spy.args[3][3].ref).eql('5') + expect(spy.args[3][3].type).eql('d') + expect(spy.args[3][2]).eql('4') + expect(spy.args[3][4]).eql(-1) + + expect(spy.args[4][3].ref).eql('6') + expect(spy.args[4][3].type).eql('e') + expect(spy.args[4][2]).eql('4') + expect(spy.args[4][4]).eql(-1) + expect(spy.args[4][3].children).eql([ + {ref: '7', type: 'g', attr: {}, style: {}}, + {ref: '8', type: 'h', attr: {}, style: {}}, + {ref: '9', type: 'i', attr: {}, style: {}}]) + + expect(spy.args[5][3].ref).eql('10') + expect(spy.args[5][3].type).eql('f') + expect(spy.args[5][2]).eql('4') + expect(spy.args[5][4]).eql(-1) + + expect(spy.args[6][3].ref).eql('11') + expect(spy.args[6][3].type).eql('c') + expect(spy.args[6][2]).eql('_root') + expect(spy.args[6][4]).eql(-1) }) it.skip('connected with native callback when init', function (done) { diff --git a/src/js-framework/lib/vm/compiler.js b/src/js-framework/lib/vm/compiler.js index 467a3a71c4..a413ec49d7 100644 --- a/src/js-framework/lib/vm/compiler.js +++ b/src/js-framework/lib/vm/compiler.js @@ -86,7 +86,10 @@ export function _generate(target, parentEl, context) { this._checkRepeat(target, fragBlock, repeatId, latestItemId) - list.forEach((item) => { + list.forEach((item, index) => { + if (typeof item === 'object') { + item.INDEX = index + } this._generate(target, fragBlock, {repeat: item}) }) @@ -121,9 +124,34 @@ export function _generate(target, parentEl, context) { return } + let typeGetter = target.type + let type = typeGetter + + if (typeof typeGetter === 'function') { + type = typeGetter.call(subContext) + + if (!context.hasOwnProperty('type')) { + const newContext = {type: type} + const fragBlock = subContext._createBlock(parentEl) + + if (parentEl.element && parentEl.children) { + parentEl.children.push(fragBlock) + } + + subContext._watch(typeGetter, (value) => { + subContext._removeBlock(fragBlock, true) + subContext._generate(target, fragBlock, {type: value}) + }) + + subContext._generate(target, fragBlock, newContext) + + return + } + } + let isComponent - if (this._app && this._app.customComponentMap && target.type) { - isComponent = this._app.customComponentMap[target.type] + if (this._app && this._app.customComponentMap && type) { + isComponent = this._app.customComponentMap[type] } else { isComponent = target.component @@ -131,7 +159,7 @@ export function _generate(target, parentEl, context) { if (isComponent) { const Vm = this.constructor - const subVm = new Vm(target.type, subContext, parentEl, undefined, { + const subVm = new Vm(type, subContext, parentEl, undefined, { 'hook:init': function () { subContext._setId(target.id, null, this) }, @@ -148,7 +176,7 @@ export function _generate(target, parentEl, context) { return } - const element = subContext._generateElement(target, parentEl) + const element = subContext._generateElement(type, target, parentEl) const treeMode = target.append === 'tree' if (!treeMode) { subContext._attachTarget(element, parentEl) @@ -166,16 +194,16 @@ export function _generate(target, parentEl, context) { * @param {object} template * @param {object} dest */ -export function _generateElement(template, dest) { +export function _generateElement(type, template, dest) { this._applyNaitveComponentOptions(template) let element if (dest.ref === '_documentElement') { // if its parent is documentElement then it's a body - element = this._createBody(template.type) + element = this._createBody(type) } else { - element = this._createElement(template.type) + element = this._createElement(type) } // TODO it was a root element when not in a fragment if (!this._rootEl) { @@ -255,6 +283,9 @@ export function _checkRepeat(target, fragBlock, repeatId, latestItemId) { value.forEach((item, index) => { const key = item[`__wx_repeat_${repeatId}__`] const reused = reusedMap[key] + if (typeof item === 'object') { + item.INDEX = index + } if (reused) { if (reused.item === reusedList[0]) { reusedList.shift() diff --git a/src/js-framework/package.json b/src/js-framework/package.json index 660bd39f65..20d69fd0eb 100644 --- a/src/js-framework/package.json +++ b/src/js-framework/package.json @@ -1,21 +1,20 @@ { "name": "weex-jsframework", - "version": "0.13.3", + "version": "0.13.4", "description": "JS Framework for Weex solution which is a extendable cross-platform solution for dynamic programming and publishing projects", "main": "index.js", "scripts": { "dev": "webpack --watch --config ./webpack.config.js", "build": "webpack --config ./webpack.config.js", - "compress": "uglifyjs dist/index.js -o dist/index.mini.js", + "compress": "uglifyjs dist/index.js -o dist/index.min.js", "lint": "jscs --config .jscsrc polyfill/*.js polyfill/__test__/*.js lib/*.js lib/__test__/*.js lib/app/*.js lib/app/__test__/*.js lib/vm/*.js lib/vm/__test__/*.js", "test": "mocha --compilers js:babel-core/register polyfill/__test__/*.js lib/__test__/*.js lib/**/__test__/*.js", "cover": "babel-node node_modules/isparta/bin/isparta cover --report text node_modules/mocha/bin/_mocha -- --reporter dot lib/__test__/*.js lib/**/__test__/*.js", - "ci": "npm run lint && npm run cover", - "release": "./release.sh" + "ci": "npm run lint && npm run cover" }, "repository": { "type": "git", - "url": "git@github.com:alibaba/weex_js-framework.git" + "url": "git@github.com:alibaba/weex.git" }, "author": [ { @@ -71,6 +70,6 @@ "webpack": "~1.12.12" }, "optionalDependencies": { - "weex-transformer": "~0.1.7" + "weex-transformer": "~0.1" } }