diff --git a/flow/compiler.js b/flow/compiler.js index 1716c08bc8a..9ed21a848c0 100644 --- a/flow/compiler.js +++ b/flow/compiler.js @@ -17,6 +17,9 @@ declare type CompilerOptions = { shouldDecodeNewlines?: boolean; shouldDecodeNewlinesForHref?: boolean; + // support in weex + recyclable?: boolean; + // for ssr optimization compiler scopeId?: string; @@ -30,6 +33,7 @@ declare type CompilerOptions = { declare type CompiledResult = { ast: ?ASTElement; render: string; + '@render'?: string; staticRenderFns: Array; stringRenderFns?: Array; errors?: Array; diff --git a/src/compiler/codegen/events.js b/src/compiler/codegen/events.js index 1753a56cde2..4f7aa411b23 100644 --- a/src/compiler/codegen/events.js +++ b/src/compiler/codegen/events.js @@ -48,12 +48,18 @@ export function genHandlers ( // Generate handler code with binding params on Weex function genWeexHandler (params: Array, handlerCode: string) { - const wrapperArgs = params.filter(exp => simplePathRE.test(exp) && exp !== '$event') - const handlerParams = wrapperArgs.map(exp => ({ '@binding': exp })) - wrapperArgs.push('$event') - return '{' + - `handler:function(${wrapperArgs.join(',')}){${handlerCode}},\n` + - `params:${JSON.stringify(handlerParams)}` + + let innerHandlerCode = handlerCode + const exps = params.filter(exp => simplePathRE.test(exp) && exp !== '$event') + const bindings = exps.map(exp => ({ '@binding': exp })) + const args = exps.map((exp, i) => { + const key = `$_${i + 1}` + innerHandlerCode = innerHandlerCode.replace(exp, key) + return key + }) + args.push('$event') + return '{\n' + + `handler:function(${args.join(',')}){${innerHandlerCode}},\n` + + `params:${JSON.stringify(bindings)}\n` + '}' } diff --git a/src/platforms/weex/compiler/index.js b/src/platforms/weex/compiler/index.js index cbbc6381c71..89ed6ebd918 100644 --- a/src/platforms/weex/compiler/index.js +++ b/src/platforms/weex/compiler/index.js @@ -23,8 +23,28 @@ export const baseOptions: CompilerOptions = { isReservedTag, getTagNamespace, preserveWhitespace: false, + recyclable: false, staticKeys: genStaticKeys(modules) } -const { compile, compileToFunctions } = createCompiler(baseOptions) -export { compile, compileToFunctions } +const compiler = createCompiler(baseOptions) + +export function compile ( + template: string, + options?: CompilerOptions +): CompiledResult { + let generateAltRender = false + if (options && options.recyclable === true) { + generateAltRender = true + options.recyclable = false + } + const result = compiler.compile(template, options) + + // generate @render function for + if (options && generateAltRender) { + options.recyclable = true + const { render } = compiler.compile(template, options) + result['@render'] = render + } + return result +} diff --git a/src/platforms/weex/compiler/modules/recycle-list/index.js b/src/platforms/weex/compiler/modules/recycle-list/index.js index a492e73aae0..646f151e786 100644 --- a/src/platforms/weex/compiler/modules/recycle-list/index.js +++ b/src/platforms/weex/compiler/modules/recycle-list/index.js @@ -1,38 +1,42 @@ /* @flow */ -import { transformText } from './text' -import { transformVBind } from './v-bind' -import { transformVIf } from './v-if' -import { transformVFor } from './v-for' +import { postTransformText } from './text' +import { preTransformVBind } from './v-bind' +import { preTransformVIf } from './v-if' +import { preTransformVFor } from './v-for' import { postTransformVOn } from './v-on' let currentRecycleList = null +function shouldCompile (el: ASTElement, options: CompilerOptions) { + return options.recyclable || + (currentRecycleList && el !== currentRecycleList) +} + function preTransformNode (el: ASTElement, options: CompilerOptions) { if (el.tag === 'recycle-list') { currentRecycleList = el } - if (currentRecycleList) { - // TODO - transformVBind(el) - transformVIf(el, options) // and v-else-if and v-else - transformVFor(el, options) + if (shouldCompile(el, options)) { + preTransformVBind(el, options) + preTransformVIf(el, options) // also v-else-if and v-else + preTransformVFor(el, options) } } -function transformNode (el: ASTElement) { - if (currentRecycleList) { - // TODO +function transformNode (el: ASTElement, options: CompilerOptions) { + if (shouldCompile(el, options)) { + // do nothing yet } } -function postTransformNode (el: ASTElement) { - if (currentRecycleList) { +function postTransformNode (el: ASTElement, options: CompilerOptions) { + if (shouldCompile(el, options)) { // : transform children text into value attr if (el.tag === 'text') { - transformText(el) + postTransformText(el, options) } - postTransformVOn(el) + postTransformVOn(el, options) } if (el === currentRecycleList) { currentRecycleList = null diff --git a/src/platforms/weex/compiler/modules/recycle-list/text.js b/src/platforms/weex/compiler/modules/recycle-list/text.js index fb72c81b555..327810548d5 100644 --- a/src/platforms/weex/compiler/modules/recycle-list/text.js +++ b/src/platforms/weex/compiler/modules/recycle-list/text.js @@ -13,7 +13,7 @@ function genText (node: ASTNode) { return JSON.stringify(value) } -export function transformText (el: ASTElement) { +export function postTransformText (el: ASTElement, options: CompilerOptions) { // weex can only contain text, so the parser // always generates a single child. if (el.children.length) { diff --git a/src/platforms/weex/compiler/modules/recycle-list/v-bind.js b/src/platforms/weex/compiler/modules/recycle-list/v-bind.js index 425e7f67bf8..a940896df74 100644 --- a/src/platforms/weex/compiler/modules/recycle-list/v-bind.js +++ b/src/platforms/weex/compiler/modules/recycle-list/v-bind.js @@ -8,7 +8,7 @@ function parseAttrName (name: string): string { return camelize(name.replace(bindRE, '')) } -export function transformVBind (el: ASTElement) { +export function preTransformVBind (el: ASTElement, options: CompilerOptions) { for (const attr in el.attrsMap) { if (bindRE.test(attr)) { const name: string = parseAttrName(attr) diff --git a/src/platforms/weex/compiler/modules/recycle-list/v-for.js b/src/platforms/weex/compiler/modules/recycle-list/v-for.js index 29e6c6f5835..baa6de97ce5 100644 --- a/src/platforms/weex/compiler/modules/recycle-list/v-for.js +++ b/src/platforms/weex/compiler/modules/recycle-list/v-for.js @@ -3,7 +3,7 @@ import { forAliasRE, forIteratorRE } from 'compiler/parser/index' import { getAndRemoveAttr } from 'compiler/helpers' -export function transformVFor (el: ASTElement, options: CompilerOptions) { +export function preTransformVFor (el: ASTElement, options: CompilerOptions) { const exp = getAndRemoveAttr(el, 'v-for') if (!exp) { return diff --git a/src/platforms/weex/compiler/modules/recycle-list/v-if.js b/src/platforms/weex/compiler/modules/recycle-list/v-if.js index f01010a251d..d3c03c27e85 100644 --- a/src/platforms/weex/compiler/modules/recycle-list/v-if.js +++ b/src/platforms/weex/compiler/modules/recycle-list/v-if.js @@ -18,7 +18,7 @@ function getPrevMatch (el: ASTElement): any { } } -export function transformVIf (el: ASTElement, options: CompilerOptions) { +export function preTransformVIf (el: ASTElement, options: CompilerOptions) { if (hasConditionDirective(el)) { let exp const ifExp = getAndRemoveAttr(el, 'v-if') diff --git a/src/platforms/weex/compiler/modules/recycle-list/v-on.js b/src/platforms/weex/compiler/modules/recycle-list/v-on.js index 347d8d3c7c8..c2f4c19a708 100644 --- a/src/platforms/weex/compiler/modules/recycle-list/v-on.js +++ b/src/platforms/weex/compiler/modules/recycle-list/v-on.js @@ -1,6 +1,6 @@ /* @flow */ -const inlineStatementRE = /^\s*([A-Za-z_$0-9\.]+)*\s*\(\s*(([A-Za-z_$0-9\'\"]+)?(\s*,\s*([A-Za-z_$0-9\'\"]+))*)\s*\)$/ +const inlineStatementRE = /^\s*([A-Za-z_$0-9\['\."\]]+)*\s*\(\s*(([A-Za-z_$0-9\['\."\]]+)?(\s*,\s*([A-Za-z_$0-9\['\."\]]+))*)\s*\)$/ function parseHandlerParams (handler: ASTElementHandler) { const res = inlineStatementRE.exec(handler.value) @@ -9,7 +9,7 @@ function parseHandlerParams (handler: ASTElementHandler) { } } -export function postTransformVOn (el: ASTElement) { +export function postTransformVOn (el: ASTElement, options: CompilerOptions) { const events: ASTElementHandlers | void = el.events if (!events) { return diff --git a/test/weex/cases/cases.spec.js b/test/weex/cases/cases.spec.js index 291600dfb24..ed3abf60f46 100644 --- a/test/weex/cases/cases.spec.js +++ b/test/weex/cases/cases.spec.js @@ -68,5 +68,17 @@ describe('Usage', () => { describe('event', () => { it('click', createEventTestCase('event/click')) }) + + describe('recycle-list', () => { + it('text node', createRenderTestCase('recycle-list/text-node')) + it('attributes', createRenderTestCase('recycle-list/attrs')) + it('v-if', createRenderTestCase('recycle-list/v-if')) + it('v-else', createRenderTestCase('recycle-list/v-else')) + it('v-else-if', createRenderTestCase('recycle-list/v-else-if')) + it('v-for', createRenderTestCase('recycle-list/v-for')) + it('v-for-iterator', createRenderTestCase('recycle-list/v-for-iterator')) + it('v-on', createRenderTestCase('recycle-list/v-on')) + it('v-on-inline', createRenderTestCase('recycle-list/v-on-inline')) + }) }) diff --git a/test/weex/cases/recycle-list/attrs.vdom.js b/test/weex/cases/recycle-list/attrs.vdom.js new file mode 100644 index 00000000000..175d355fb6b --- /dev/null +++ b/test/weex/cases/recycle-list/attrs.vdom.js @@ -0,0 +1,33 @@ +({ + type: 'recycle-list', + attr: { + listData: [ + { type: 'A', count: 1, source: 'http://whatever.com/x.png' }, + { type: 'A', count: 2, source: 'http://whatever.com/y.png' }, + { type: 'A', count: 3, source: 'http://whatever.com/z.png' } + ], + templateKey: 'type', + alias: 'item' + }, + children: [{ + type: 'cell-slot', + attr: { templateType: 'A' }, + children: [{ + type: 'image', + attr: { + resize: 'cover', + src: { + '@binding': 'item.source' + } + } + }, { + type: 'text', + attr: { + lines: '3', + count: { + '@binding': 'item.count' + } + } + }] + }] +}) diff --git a/test/weex/cases/recycle-list/attrs.vue b/test/weex/cases/recycle-list/attrs.vue new file mode 100644 index 00000000000..b9de8f9e711 --- /dev/null +++ b/test/weex/cases/recycle-list/attrs.vue @@ -0,0 +1,23 @@ + + + + diff --git a/test/weex/cases/recycle-list/text-node.vdom.js b/test/weex/cases/recycle-list/text-node.vdom.js new file mode 100644 index 00000000000..5808ea0bfd4 --- /dev/null +++ b/test/weex/cases/recycle-list/text-node.vdom.js @@ -0,0 +1,37 @@ +({ + type: 'recycle-list', + attr: { + listData: [ + { type: 'A', dynamic: 'decimal', two: '2', four: '4' }, + { type: 'A', dynamic: 'binary', two: '10', four: '100' } + ], + templateKey: 'type', + alias: 'item' + }, + children: [{ + type: 'cell-slot', + attr: { templateType: 'A' }, + children: [{ + type: 'text', + attr: { + value: 'static' + } + }, { + type: 'text', + attr: { + value: { '@binding': 'item.dynamic' } + } + }, { + type: 'text', + attr: { + value: [ + 'one ', + { '@binding': 'item.two' }, + ' three ', + { '@binding': 'item.four' }, + ' five' + ] + } + }] + }] +}) diff --git a/test/weex/cases/recycle-list/text-node.vue b/test/weex/cases/recycle-list/text-node.vue new file mode 100644 index 00000000000..801f3168d5f --- /dev/null +++ b/test/weex/cases/recycle-list/text-node.vue @@ -0,0 +1,23 @@ + + + + diff --git a/test/weex/cases/recycle-list/v-else-if.vdom.js b/test/weex/cases/recycle-list/v-else-if.vdom.js new file mode 100644 index 00000000000..3bdcfb2be7b --- /dev/null +++ b/test/weex/cases/recycle-list/v-else-if.vdom.js @@ -0,0 +1,34 @@ +({ + type: 'recycle-list', + attr: { + listData: [ + { type: 'A' }, + { type: 'A' } + ], + templateKey: 'type', + alias: 'item' + }, + children: [{ + type: 'cell-slot', + attr: { templateType: 'A' }, + children: [{ + type: 'image', + attr: { + '[[match]]': 'item.sourceA', + src: { '@binding': 'item.sourceA' } + } + }, { + type: 'image', + attr: { + '[[match]]': '!(item.sourceA) && (item.sourceB)', + src: { '@binding': 'item.sourceB' } + } + }, { + type: 'image', + attr: { + '[[match]]': '!(!(item.sourceA) && (item.sourceB))', + src: { '@binding': 'item.placeholder' } + } + }] + }] +}) diff --git a/test/weex/cases/recycle-list/v-else-if.vue b/test/weex/cases/recycle-list/v-else-if.vue new file mode 100644 index 00000000000..bde4ccc65f8 --- /dev/null +++ b/test/weex/cases/recycle-list/v-else-if.vue @@ -0,0 +1,23 @@ + + + + diff --git a/test/weex/cases/recycle-list/v-else.vdom.js b/test/weex/cases/recycle-list/v-else.vdom.js new file mode 100644 index 00000000000..0a42c8c2bb1 --- /dev/null +++ b/test/weex/cases/recycle-list/v-else.vdom.js @@ -0,0 +1,28 @@ +({ + type: 'recycle-list', + attr: { + listData: [ + { type: 'A' }, + { type: 'A' } + ], + templateKey: 'type', + alias: 'item' + }, + children: [{ + type: 'cell-slot', + attr: { templateType: 'A' }, + children: [{ + type: 'image', + attr: { + '[[match]]': 'item.source', + src: { '@binding': 'item.source' } + } + }, { + type: 'image', + attr: { + '[[match]]': '!(item.source)', + src: { '@binding': 'item.placeholder' } + } + }] + }] +}) diff --git a/test/weex/cases/recycle-list/v-else.vue b/test/weex/cases/recycle-list/v-else.vue new file mode 100644 index 00000000000..086d39dbac0 --- /dev/null +++ b/test/weex/cases/recycle-list/v-else.vue @@ -0,0 +1,22 @@ + + + + diff --git a/test/weex/cases/recycle-list/v-for-iterator.vdom.js b/test/weex/cases/recycle-list/v-for-iterator.vdom.js new file mode 100644 index 00000000000..872ad661b38 --- /dev/null +++ b/test/weex/cases/recycle-list/v-for-iterator.vdom.js @@ -0,0 +1,46 @@ +({ + type: 'recycle-list', + attr: { + listData: [ + { type: 'A' }, + { type: 'A' } + ], + templateKey: 'type', + alias: 'item' + }, + children: [{ + type: 'cell-slot', + attr: { templateType: 'A' }, + children: [{ + type: 'div', + attr: { + '[[repeat]]': { + '@expression': 'item.list', + '@index': 'index', + '@alias': 'object' + } + }, + children: [{ + type: 'text', + attr: { + value: { + '@binding': 'object.name' + } + } + }, { + type: 'text', + attr: { + '[[repeat]]': { + '@expression': 'object', + '@alias': 'v', + '@key': 'k', + '@index': 'i' + }, + value: { + '@binding': 'v' + } + } + }] + }] + }] +}) diff --git a/test/weex/cases/recycle-list/v-for-iterator.vue b/test/weex/cases/recycle-list/v-for-iterator.vue new file mode 100644 index 00000000000..a6b7dc312e2 --- /dev/null +++ b/test/weex/cases/recycle-list/v-for-iterator.vue @@ -0,0 +1,24 @@ + + + + diff --git a/test/weex/cases/recycle-list/v-for.vdom.js b/test/weex/cases/recycle-list/v-for.vdom.js new file mode 100644 index 00000000000..19ed8935774 --- /dev/null +++ b/test/weex/cases/recycle-list/v-for.vdom.js @@ -0,0 +1,32 @@ +({ + type: 'recycle-list', + attr: { + listData: [ + { type: 'A' }, + { type: 'A' } + ], + templateKey: 'type', + alias: 'item' + }, + children: [{ + type: 'cell-slot', + attr: { templateType: 'A' }, + children: [{ + type: 'div', + attr: { + '[[repeat]]': { + '@expression': 'item.list', + '@alias': 'panel' + } + }, + children: [{ + type: 'text', + attr: { + value: { + '@binding': 'panel.label' + } + } + }] + }] + }] +}) diff --git a/test/weex/cases/recycle-list/v-for.vue b/test/weex/cases/recycle-list/v-for.vue new file mode 100644 index 00000000000..713d5c02866 --- /dev/null +++ b/test/weex/cases/recycle-list/v-for.vue @@ -0,0 +1,23 @@ + + + + diff --git a/test/weex/cases/recycle-list/v-if.vdom.js b/test/weex/cases/recycle-list/v-if.vdom.js new file mode 100644 index 00000000000..2a3d58784ad --- /dev/null +++ b/test/weex/cases/recycle-list/v-if.vdom.js @@ -0,0 +1,28 @@ +({ + type: 'recycle-list', + attr: { + listData: [ + { type: 'A' }, + { type: 'A' } + ], + templateKey: 'type', + alias: 'item' + }, + children: [{ + type: 'cell-slot', + attr: { templateType: 'A' }, + children: [{ + type: 'image', + attr: { + '[[match]]': 'item.source', + src: { '@binding': 'item.source' } + } + }, { + type: 'text', + attr: { + '[[match]]': '!item.source', + value: 'Title' + } + }] + }] +}) diff --git a/test/weex/cases/recycle-list/v-if.vue b/test/weex/cases/recycle-list/v-if.vue new file mode 100644 index 00000000000..bec7cdac8e7 --- /dev/null +++ b/test/weex/cases/recycle-list/v-if.vue @@ -0,0 +1,22 @@ + + + + diff --git a/test/weex/cases/recycle-list/v-on-inline.vdom.js b/test/weex/cases/recycle-list/v-on-inline.vdom.js new file mode 100644 index 00000000000..284871a4b2f --- /dev/null +++ b/test/weex/cases/recycle-list/v-on-inline.vdom.js @@ -0,0 +1,36 @@ +({ + type: 'recycle-list', + attr: { + listData: [ + { type: 'A' }, + { type: 'A' } + ], + templateKey: 'type', + alias: 'item' + }, + children: [{ + type: 'cell-slot', + attr: { templateType: 'A' }, + children: [{ + type: 'text', + event: ['click', { + type: 'longpress', + params: [{ '@binding': 'item.key' }] + }] + }, { + type: 'text', + event: [{ + type: 'appear', + params: [ + { '@binding': 'item.index' }, + { '@binding': 'item.type' } + ] + }], + attr: { value: 'Button' } + }, { + type: 'text', + event: [{ type: 'disappear' }], + attr: { value: 'Tips' } + }] + }] +}) diff --git a/test/weex/cases/recycle-list/v-on-inline.vue b/test/weex/cases/recycle-list/v-on-inline.vue new file mode 100644 index 00000000000..bf7ae7018d5 --- /dev/null +++ b/test/weex/cases/recycle-list/v-on-inline.vue @@ -0,0 +1,28 @@ + + + + diff --git a/test/weex/cases/recycle-list/v-on.vdom.js b/test/weex/cases/recycle-list/v-on.vdom.js new file mode 100644 index 00000000000..63a7c3c6aed --- /dev/null +++ b/test/weex/cases/recycle-list/v-on.vdom.js @@ -0,0 +1,24 @@ +({ + type: 'recycle-list', + attr: { + listData: [ + { type: 'A' }, + { type: 'A' } + ], + templateKey: 'type', + alias: 'item' + }, + children: [{ + type: 'cell-slot', + attr: { templateType: 'A' }, + children: [{ + type: 'text', + event: ['click', 'longpress'], + attr: { value: 'A' } + }, { + type: 'text', + event: ['touchend'], + attr: { value: 'B' } + }] + }] +}) diff --git a/test/weex/cases/recycle-list/v-on.vue b/test/weex/cases/recycle-list/v-on.vue new file mode 100644 index 00000000000..de5fc51bfb8 --- /dev/null +++ b/test/weex/cases/recycle-list/v-on.vue @@ -0,0 +1,26 @@ + + + +