Przeglądaj źródła

feat(weex): generate "@render" function for weex recycle-list (#6987)

* feat($compiler): support to generate @render function for weex recycle-list

Compile the template twice with different options for weex platform if
the “recyclable” flag is passed. Generate both normal render function
and “@render” function for recycle-list.

Adjust function names and arguments in recycle-list compiler.

* test(weex): add test cases for <recycle-list>
Hanks 8 lat temu
rodzic
commit
0c11aa8add
28 zmienionych plików z 588 dodań i 30 usunięć
  1. 4 0
      flow/compiler.js
  2. 12 6
      src/compiler/codegen/events.js
  3. 22 2
      src/platforms/weex/compiler/index.js
  4. 20 16
      src/platforms/weex/compiler/modules/recycle-list/index.js
  5. 1 1
      src/platforms/weex/compiler/modules/recycle-list/text.js
  6. 1 1
      src/platforms/weex/compiler/modules/recycle-list/v-bind.js
  7. 1 1
      src/platforms/weex/compiler/modules/recycle-list/v-for.js
  8. 1 1
      src/platforms/weex/compiler/modules/recycle-list/v-if.js
  9. 2 2
      src/platforms/weex/compiler/modules/recycle-list/v-on.js
  10. 12 0
      test/weex/cases/cases.spec.js
  11. 33 0
      test/weex/cases/recycle-list/attrs.vdom.js
  12. 23 0
      test/weex/cases/recycle-list/attrs.vue
  13. 37 0
      test/weex/cases/recycle-list/text-node.vdom.js
  14. 23 0
      test/weex/cases/recycle-list/text-node.vue
  15. 34 0
      test/weex/cases/recycle-list/v-else-if.vdom.js
  16. 23 0
      test/weex/cases/recycle-list/v-else-if.vue
  17. 28 0
      test/weex/cases/recycle-list/v-else.vdom.js
  18. 22 0
      test/weex/cases/recycle-list/v-else.vue
  19. 46 0
      test/weex/cases/recycle-list/v-for-iterator.vdom.js
  20. 24 0
      test/weex/cases/recycle-list/v-for-iterator.vue
  21. 32 0
      test/weex/cases/recycle-list/v-for.vdom.js
  22. 23 0
      test/weex/cases/recycle-list/v-for.vue
  23. 28 0
      test/weex/cases/recycle-list/v-if.vdom.js
  24. 22 0
      test/weex/cases/recycle-list/v-if.vue
  25. 36 0
      test/weex/cases/recycle-list/v-on-inline.vdom.js
  26. 28 0
      test/weex/cases/recycle-list/v-on-inline.vue
  27. 24 0
      test/weex/cases/recycle-list/v-on.vdom.js
  28. 26 0
      test/weex/cases/recycle-list/v-on.vue

+ 4 - 0
flow/compiler.js

@@ -17,6 +17,9 @@ declare type CompilerOptions = {
   shouldDecodeNewlines?:  boolean;
   shouldDecodeNewlinesForHref?: boolean;
 
+  // support <recycle-list> 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<string>;
   stringRenderFns?: Array<string>;
   errors?: Array<string>;

+ 12 - 6
src/compiler/codegen/events.js

@@ -48,12 +48,18 @@ export function genHandlers (
 
 // Generate handler code with binding params on Weex
 function genWeexHandler (params: Array<any>, 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` +
     '}'
 }
 

+ 22 - 2
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 <recycle-list>
+  if (options && generateAltRender) {
+    options.recyclable = true
+    const { render } = compiler.compile(template, options)
+    result['@render'] = render
+  }
+  return result
+}

+ 20 - 16
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)) {
     // <text>: 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

+ 1 - 1
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 <text> can only contain text, so the parser
   // always generates a single child.
   if (el.children.length) {

+ 1 - 1
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)

+ 1 - 1
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

+ 1 - 1
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')

+ 2 - 2
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

+ 12 - 0
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'))
+  })
 })
 

+ 33 - 0
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'
+        }
+      }
+    }]
+  }]
+})

+ 23 - 0
test/weex/cases/recycle-list/attrs.vue

@@ -0,0 +1,23 @@
+<template>
+  <recycle-list :list-data="longList" template-key="type" alias="item">
+    <cell-slot template-type="A">
+      <image resize="cover" :src="item.source">
+      <text lines="3" v-bind:count="item.count"></text>
+    </cell-slot>
+  </recycle-list>
+</template>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        longList: [
+          { 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' }
+        ]
+      }
+    }
+  }
+</script>
+

+ 37 - 0
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'
+        ]
+      }
+    }]
+  }]
+})

+ 23 - 0
test/weex/cases/recycle-list/text-node.vue

@@ -0,0 +1,23 @@
+<template>
+  <recycle-list :list-data="longList" template-key="type" alias="item">
+    <cell-slot template-type="A">
+      <text>static</text>
+      <text>{{item.dynamic}}</text>
+      <text>one {{item.two}} three {{ item.four }} five</text>
+    </cell-slot>
+  </recycle-list>
+</template>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        longList: [
+          { type: 'A', dynamic: 'decimal', two: '2', four: '4' },
+          { type: 'A', dynamic: 'binary', two: '10', four: '100' }
+        ]
+      }
+    }
+  }
+</script>
+

+ 34 - 0
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' }
+      }
+    }]
+  }]
+})

+ 23 - 0
test/weex/cases/recycle-list/v-else-if.vue

@@ -0,0 +1,23 @@
+<template>
+  <recycle-list :list-data="longList" template-key="type" alias="item">
+    <cell-slot template-type="A">
+      <image v-if="item.sourceA" :src="item.sourceA"></image>
+      <image v-else-if="item.sourceB" :src="item.sourceB"></image>
+      <image v-else :src="item.placeholder"></image>
+    </cell-slot>
+  </recycle-list>
+</template>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        longList: [
+          { type: 'A' },
+          { type: 'A' }
+        ]
+      }
+    }
+  }
+</script>
+

+ 28 - 0
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' }
+      }
+    }]
+  }]
+})

+ 22 - 0
test/weex/cases/recycle-list/v-else.vue

@@ -0,0 +1,22 @@
+<template>
+  <recycle-list :list-data="longList" template-key="type" alias="item">
+    <cell-slot template-type="A">
+      <image v-if="item.source" :src="item.source"></image>
+      <image v-else v-bind:src="item.placeholder"></image>
+    </cell-slot>
+  </recycle-list>
+</template>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        longList: [
+          { type: 'A' },
+          { type: 'A' }
+        ]
+      }
+    }
+  }
+</script>
+

+ 46 - 0
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'
+          }
+        }
+      }]
+    }]
+  }]
+})

+ 24 - 0
test/weex/cases/recycle-list/v-for-iterator.vue

@@ -0,0 +1,24 @@
+<template>
+  <recycle-list :list-data="longList" template-key="type" alias="item">
+    <cell-slot template-type="A">
+      <div v-for="(object, index) in item.list" :key="index">
+        <text>{{object.name}}</text>
+        <text v-for="(v, k, i) in object" :key="k">{{v}}</text>
+      </div>
+    </cell-slot>
+  </recycle-list>
+</template>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        longList: [
+          { type: 'A' },
+          { type: 'A' }
+        ]
+      }
+    }
+  }
+</script>
+

+ 32 - 0
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'
+          }
+        }
+      }]
+    }]
+  }]
+})

+ 23 - 0
test/weex/cases/recycle-list/v-for.vue

@@ -0,0 +1,23 @@
+<template>
+  <recycle-list :list-data="longList" template-key="type" alias="item">
+    <cell-slot template-type="A">
+      <div v-for="panel in item.list" :key="panel.id">
+        <text>{{panel.label}}</text>
+      </div>
+    </cell-slot>
+  </recycle-list>
+</template>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        longList: [
+          { type: 'A' },
+          { type: 'A' }
+        ]
+      }
+    }
+  }
+</script>
+

+ 28 - 0
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'
+      }
+    }]
+  }]
+})

+ 22 - 0
test/weex/cases/recycle-list/v-if.vue

@@ -0,0 +1,22 @@
+<template>
+  <recycle-list :list-data="longList" template-key="type" alias="item">
+    <cell-slot template-type="A">
+      <image v-if="item.source" :src="item.source"></image>
+      <text v-if="!item.source">Title</text>
+    </cell-slot>
+  </recycle-list>
+</template>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        longList: [
+          { type: 'A' },
+          { type: 'A' }
+        ]
+      }
+    }
+  }
+</script>
+

+ 36 - 0
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' }
+    }]
+  }]
+})

+ 28 - 0
test/weex/cases/recycle-list/v-on-inline.vue

@@ -0,0 +1,28 @@
+<template>
+  <recycle-list :list-data="longList" template-key="type" alias="item">
+    <cell-slot template-type="A">
+      <text v-on:click="toggle()" @longpress="toggle(item.key)"></text>
+      <text @appear="onappear(item.index, 'static', item.type, $event)">Button</text>
+      <text @disappear="onappear(25, 'static')">Tips</text>
+    </cell-slot>
+  </recycle-list>
+</template>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        longList: [
+          { type: 'A' },
+          { type: 'A' }
+        ]
+      }
+    },
+    methods: {
+      hide () {},
+      toggle () {},
+      onappear () {}
+    }
+  }
+</script>
+

+ 24 - 0
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' }
+    }]
+  }]
+})

+ 26 - 0
test/weex/cases/recycle-list/v-on.vue

@@ -0,0 +1,26 @@
+<template>
+  <recycle-list :list-data="longList" template-key="type" alias="item">
+    <cell-slot template-type="A">
+      <text v-on:click="handler" @longpress="move">A</text>
+      <text @touchend="move">B</text>
+    </cell-slot>
+  </recycle-list>
+</template>
+
+<script>
+  module.exports = {
+    data () {
+      return {
+        longList: [
+          { type: 'A' },
+          { type: 'A' }
+        ]
+      }
+    },
+    methods: {
+      handler () {},
+      move () {}
+    }
+  }
+</script>
+