From 5cca28a8d091a9306aa26707abf7015d203cad8d Mon Sep 17 00:00:00 2001
From: tjz <415800467@qq.com>
Date: Sat, 16 Jun 2018 21:30:41 +0800
Subject: [PATCH] feat: add list component

---
 components/_util/props-util.js                |   3 +
 components/index.js                           |   6 +-
 components/list/Item.jsx                      | 143 ++++++
 .../__tests__/__snapshots__/demo.test.js.snap | 486 ++++++++++++++++++
 components/list/__tests__/demo.test.js        |   3 +
 components/list/demo/basic.md                 |  53 ++
 components/list/demo/grid.md                  |  48 ++
 components/list/demo/index.vue                |  57 ++
 components/list/demo/infinite-load.md         |  95 ++++
 .../list/demo/infinite-virtualized-load.md    |  95 ++++
 components/list/demo/loadmore.md              |  86 ++++
 components/list/demo/resposive.md             |  55 ++
 components/list/demo/simple.md                |  69 +++
 components/list/demo/vertical.md              |  73 +++
 components/list/index.en-US.md                |  46 ++
 components/list/index.jsx                     | 249 +++++++++
 components/list/index.zh-CN.md                |  46 ++
 components/list/style/bordered.less           |  41 ++
 components/list/style/index.js                |   7 +
 components/list/style/index.less              | 207 ++++++++
 components/list/style/responsive.less         |  42 ++
 components/style.js                           |   1 +
 site/components.js                            |   6 +-
 site/demo.js                                  |   1 +
 tests/__snapshots__/index.test.js.snap        |   1 +
 25 files changed, 1916 insertions(+), 3 deletions(-)
 create mode 100644 components/list/Item.jsx
 create mode 100644 components/list/__tests__/__snapshots__/demo.test.js.snap
 create mode 100644 components/list/__tests__/demo.test.js
 create mode 100644 components/list/demo/basic.md
 create mode 100644 components/list/demo/grid.md
 create mode 100644 components/list/demo/index.vue
 create mode 100644 components/list/demo/infinite-load.md
 create mode 100644 components/list/demo/infinite-virtualized-load.md
 create mode 100644 components/list/demo/loadmore.md
 create mode 100644 components/list/demo/resposive.md
 create mode 100644 components/list/demo/simple.md
 create mode 100644 components/list/demo/vertical.md
 create mode 100644 components/list/index.en-US.md
 create mode 100644 components/list/index.jsx
 create mode 100644 components/list/index.zh-CN.md
 create mode 100644 components/list/style/bordered.less
 create mode 100644 components/list/style/index.js
 create mode 100644 components/list/style/index.less
 create mode 100644 components/list/style/responsive.less

diff --git a/components/_util/props-util.js b/components/_util/props-util.js
index a78030095..0a1cf7d6b 100644
--- a/components/_util/props-util.js
+++ b/components/_util/props-util.js
@@ -59,6 +59,9 @@ const getSlots = (ele) => {
   return slots
 }
 const getSlotOptions = (ele) => {
+  if (ele.fnOptions) { // 函数式组件
+    return ele.fnOptions
+  }
   let componentOptions = ele.componentOptions
   if (ele.$vnode) {
     componentOptions = ele.$vnode.componentOptions
diff --git a/components/index.js b/components/index.js
index ee1b260f1..a9779713a 100644
--- a/components/index.js
+++ b/components/index.js
@@ -60,7 +60,7 @@ import { default as InputNumber } from './input-number'
 
 import { default as Layout } from './layout'
 
-// import { default as List } from './list'
+import { default as List } from './list'
 
 import { default as LocaleProvider } from './locale-provider'
 
@@ -163,6 +163,9 @@ const components = [
   Layout.Footer,
   Layout.Sider,
   Layout.Content,
+  List,
+  List.Item,
+  List.Item.Meta,
   LocaleProvider,
   Menu,
   Menu.Item,
@@ -247,6 +250,7 @@ export {
   Input,
   InputNumber,
   Layout,
+  List,
   LocaleProvider,
   Menu,
   Modal,
diff --git a/components/list/Item.jsx b/components/list/Item.jsx
new file mode 100644
index 000000000..212047ff4
--- /dev/null
+++ b/components/list/Item.jsx
@@ -0,0 +1,143 @@
+import PropTypes from '../_util/vue-types'
+import classNames from 'classnames'
+import { getSlotOptions, getComponentFromProp, isEmptyElement } from '../_util/props-util'
+import { Col } from '../grid'
+import { ListGridType } from './index'
+
+export const ListItemProps = {
+  prefixCls: PropTypes.string,
+  extra: PropTypes.any,
+  actions: PropTypes.arrayOf(PropTypes.any),
+  grid: ListGridType,
+}
+
+export const ListItemMetaProps = {
+  avatar: PropTypes.any,
+  description: PropTypes.any,
+  prefixCls: PropTypes.string,
+  title: PropTypes.any,
+}
+
+export const Meta = {
+  functional: true,
+  name: 'AListItemMeta',
+  __ANT_LIST_ITEM_META: true,
+  render (h, context) {
+    const { props, slots, listeners } = context
+    const slotsMap = slots()
+    const {
+      prefixCls = 'ant-list',
+    } = props
+    const avatar = props.avatar || slotsMap.avatar
+    const title = props.title || slotsMap.title
+    const description = props.description || slotsMap.description
+    const content = (
+      <div class={`${prefixCls}-item-meta-content`}>
+        {title && <h4 class={`${prefixCls}-item-meta-title`}>{title}</h4>}
+        {description && <div class={`${prefixCls}-item-meta-description`}>{description}</div>}
+      </div>
+    )
+    return (
+      <div {...{ on: listeners }} class={`${prefixCls}-item-meta`}>
+        {avatar && <div class={`${prefixCls}-item-meta-avatar`}>{avatar}</div>}
+        {(title || description) && content}
+      </div>
+    )
+  },
+
+}
+
+function getGrid (grid, t) {
+  return grid[t] && Math.floor(24 / grid[t])
+}
+
+export default {
+  name: 'AListItem',
+  Meta,
+  props: ListItemProps,
+  inject: {
+    listContext: { default: {}},
+  },
+
+  render () {
+    const { grid } = this.listContext
+    const { prefixCls = 'ant-list', $slots, $listeners } = this
+    const classString = `${prefixCls}-item`
+    const extra = getComponentFromProp(this, 'extra')
+    const actions = getComponentFromProp(this, 'actions')
+    const metaContent = []
+    const otherContent = []
+
+    ;($slots.default || []).forEach((element) => {
+      if (!isEmptyElement(element)) {
+        if (getSlotOptions(element).__ANT_LIST_ITEM_META) {
+          metaContent.push(element)
+        } else {
+          otherContent.push(element)
+        }
+      }
+    })
+
+    const contentClassString = classNames(`${prefixCls}-item-content`, {
+      [`${prefixCls}-item-content-single`]: (metaContent.length < 1),
+    })
+    const content = otherContent.length > 0 ? (
+      <div class={contentClassString}>
+        {otherContent}
+      </div>) : null
+
+    let actionsContent
+    if (actions && actions.length > 0) {
+      const actionsContentItem = (action, i) => (
+        <li key={`${prefixCls}-item-action-${i}`}>
+          {action}
+          {i !== (actions.length - 1) && <em class={`${prefixCls}-item-action-split`}/>}
+        </li>
+      )
+      actionsContent = (
+        <ul class={`${prefixCls}-item-action`}>
+          {actions.map((action, i) => actionsContentItem(action, i))}
+        </ul>
+      )
+    }
+
+    const extraContent = (
+      <div class={`${prefixCls}-item-extra-wrap`}>
+        <div class={`${prefixCls}-item-main`}>
+          {metaContent}
+          {content}
+          {actionsContent}
+        </div>
+        <div class={`${prefixCls}-item-extra`}>{extra}</div>
+      </div>
+    )
+
+    const mainContent = grid ? (
+      <Col
+        span={getGrid(grid, 'column')}
+        xs={getGrid(grid, 'xs')}
+        sm={getGrid(grid, 'sm')}
+        md={getGrid(grid, 'md')}
+        lg={getGrid(grid, 'lg')}
+        xl={getGrid(grid, 'xl')}
+        xxl={getGrid(grid, 'xxl')}
+      >
+        <div {...{ on: $listeners }} class={classString}>
+          {extra && extraContent}
+          {!extra && metaContent}
+          {!extra && content}
+          {!extra && actionsContent}
+        </div>
+      </Col>
+    ) : (
+      <div {...{ on: $listeners }} class={classString}>
+        {extra && extraContent}
+        {!extra && metaContent}
+        {!extra && content}
+        {!extra && actionsContent}
+      </div>
+    )
+
+    return mainContent
+  },
+}
diff --git a/components/list/__tests__/__snapshots__/demo.test.js.snap b/components/list/__tests__/__snapshots__/demo.test.js.snap
new file mode 100644
index 000000000..a23c0b0f5
--- /dev/null
+++ b/components/list/__tests__/__snapshots__/demo.test.js.snap
@@ -0,0 +1,486 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders ./components/list/demo/basic.md correctly 1`] = `
+<div class="ant-list ant-list-split">
+  <div class="ant-spin-nested-loading">
+    <div class="ant-spin-container">
+      <div class="ant-list-item">
+        <div class="ant-list-item-meta">
+          <div class="ant-list-item-meta-avatar"><span class="ant-avatar ant-avatar-image ant-avatar-circle"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span></div>
+          <div class="ant-list-item-meta-content">
+            <h4 class="ant-list-item-meta-title">
+              <a href="https://vuecomponent.github.io/ant-design/">Ant Design Title 1</a>
+            </h4>
+            <div class="ant-list-item-meta-description">Ant Design, a design language for background applications, is refined by Ant UED Team</div>
+          </div>
+        </div>
+      </div>
+      <div class="ant-list-item">
+        <div class="ant-list-item-meta">
+          <div class="ant-list-item-meta-avatar"><span class="ant-avatar ant-avatar-image ant-avatar-circle"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span></div>
+          <div class="ant-list-item-meta-content">
+            <h4 class="ant-list-item-meta-title">
+              <a href="https://vuecomponent.github.io/ant-design/">Ant Design Title 2</a>
+            </h4>
+            <div class="ant-list-item-meta-description">Ant Design, a design language for background applications, is refined by Ant UED Team</div>
+          </div>
+        </div>
+      </div>
+      <div class="ant-list-item">
+        <div class="ant-list-item-meta">
+          <div class="ant-list-item-meta-avatar"><span class="ant-avatar ant-avatar-image ant-avatar-circle"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span></div>
+          <div class="ant-list-item-meta-content">
+            <h4 class="ant-list-item-meta-title">
+              <a href="https://vuecomponent.github.io/ant-design/">Ant Design Title 3</a>
+            </h4>
+            <div class="ant-list-item-meta-description">Ant Design, a design language for background applications, is refined by Ant UED Team</div>
+          </div>
+        </div>
+      </div>
+      <div class="ant-list-item">
+        <div class="ant-list-item-meta">
+          <div class="ant-list-item-meta-avatar"><span class="ant-avatar ant-avatar-image ant-avatar-circle"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span></div>
+          <div class="ant-list-item-meta-content">
+            <h4 class="ant-list-item-meta-title">
+              <a href="https://vuecomponent.github.io/ant-design/">Ant Design Title 4</a>
+            </h4>
+            <div class="ant-list-item-meta-description">Ant Design, a design language for background applications, is refined by Ant UED Team</div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`renders ./components/list/demo/grid.md correctly 1`] = `
+<div class="ant-list ant-list-split ant-list-grid">
+  <div class="ant-spin-nested-loading">
+    <div class="ant-spin-container">
+      <div class="ant-row" style="margin-left: -8px; margin-right: -8px;">
+        <div class="ant-col-6" style="padding-left: 8px; padding-right: 8px;">
+          <div class="ant-list-item">
+            <div class="ant-list-item-content ant-list-item-content-single">
+              <div class="ant-card ant-card-bordered">
+                <div class="ant-card-head">
+                  <div class="ant-card-head-wrapper">
+                    <div class="ant-card-head-title">Title 1</div>
+                  </div>
+                </div>
+                <div class="ant-card-body">Card content</div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="ant-col-6" style="padding-left: 8px; padding-right: 8px;">
+          <div class="ant-list-item">
+            <div class="ant-list-item-content ant-list-item-content-single">
+              <div class="ant-card ant-card-bordered">
+                <div class="ant-card-head">
+                  <div class="ant-card-head-wrapper">
+                    <div class="ant-card-head-title">Title 2</div>
+                  </div>
+                </div>
+                <div class="ant-card-body">Card content</div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="ant-col-6" style="padding-left: 8px; padding-right: 8px;">
+          <div class="ant-list-item">
+            <div class="ant-list-item-content ant-list-item-content-single">
+              <div class="ant-card ant-card-bordered">
+                <div class="ant-card-head">
+                  <div class="ant-card-head-wrapper">
+                    <div class="ant-card-head-title">Title 3</div>
+                  </div>
+                </div>
+                <div class="ant-card-body">Card content</div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="ant-col-6" style="padding-left: 8px; padding-right: 8px;">
+          <div class="ant-list-item">
+            <div class="ant-list-item-content ant-list-item-content-single">
+              <div class="ant-card ant-card-bordered">
+                <div class="ant-card-head">
+                  <div class="ant-card-head-wrapper">
+                    <div class="ant-card-head-title">Title 4</div>
+                  </div>
+                </div>
+                <div class="ant-card-body">Card content</div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`renders ./components/list/demo/infinite-load.md correctly 1`] = `
+<div infinite-scroll-distance="10" class="demo-infinite-container">
+  <div class="ant-list ant-list-split">
+    <div class="ant-spin ant-spin-show-text"><span class="ant-spin-dot"><i></i><i></i><i></i><i></i></span></div>
+  </div>
+</div>
+`;
+
+exports[`renders ./components/list/demo/infinite-virtualized-load.md correctly 1`] = `
+<div class="ant-list ant-list-split">
+  <div class="ant-spin-nested-loading">
+    <div class="ant-spin-container">
+      <div data-v-caa2d078="" class="virtual-scroller" infinite-scroll-distance="10" style="height: 400px;">
+        <div data-v-caa2d078="" class="item-container">
+          <div data-v-caa2d078="" class="items"></div>
+        </div>
+        <div data-v-b329ee4c="" data-v-caa2d078="" tabindex="-1" class="resize-observer">
+          <object style="display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;" aria-hidden="true" tabindex="-1" type="text/html" data="about:blank"></object>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`renders ./components/list/demo/loadmore.md correctly 1`] = `
+<div class="demo-loadmore-list ant-list ant-list-split ant-list-loading ant-list-something-after-last-item">
+  <div class="ant-spin-nested-loading">
+    <div>
+      <div class="ant-spin ant-spin-spinning ant-spin-show-text"><span class="ant-spin-dot"><i></i><i></i><i></i><i></i></span></div>
+    </div>
+    <div class="ant-spin-container ant-spin-blur">
+      <div style="min-height: 53;"></div>
+    </div>
+  </div>
+  <div style="text-align: center; margin-top: 12px; height: 32px; line-height: 32px;">
+    <button type="button" class="ant-btn ant-btn-default"><span>loading more</span></button>
+  </div>
+</div>
+`;
+
+exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
+<div class="ant-list ant-list-split ant-list-grid">
+  <div class="ant-spin-nested-loading">
+    <div class="ant-spin-container">
+      <div class="ant-row" style="margin-left: -8px; margin-right: -8px;">
+        <div class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8" style="padding-left: 8px; padding-right: 8px;">
+          <div class="ant-list-item">
+            <div class="ant-list-item-content ant-list-item-content-single">
+              <div class="ant-card ant-card-bordered">
+                <div class="ant-card-head">
+                  <div class="ant-card-head-wrapper">
+                    <div class="ant-card-head-title">Title 1</div>
+                  </div>
+                </div>
+                <div class="ant-card-body">Card content</div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8" style="padding-left: 8px; padding-right: 8px;">
+          <div class="ant-list-item">
+            <div class="ant-list-item-content ant-list-item-content-single">
+              <div class="ant-card ant-card-bordered">
+                <div class="ant-card-head">
+                  <div class="ant-card-head-wrapper">
+                    <div class="ant-card-head-title">Title 2</div>
+                  </div>
+                </div>
+                <div class="ant-card-body">Card content</div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8" style="padding-left: 8px; padding-right: 8px;">
+          <div class="ant-list-item">
+            <div class="ant-list-item-content ant-list-item-content-single">
+              <div class="ant-card ant-card-bordered">
+                <div class="ant-card-head">
+                  <div class="ant-card-head-wrapper">
+                    <div class="ant-card-head-title">Title 3</div>
+                  </div>
+                </div>
+                <div class="ant-card-body">Card content</div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8" style="padding-left: 8px; padding-right: 8px;">
+          <div class="ant-list-item">
+            <div class="ant-list-item-content ant-list-item-content-single">
+              <div class="ant-card ant-card-bordered">
+                <div class="ant-card-head">
+                  <div class="ant-card-head-wrapper">
+                    <div class="ant-card-head-title">Title 4</div>
+                  </div>
+                </div>
+                <div class="ant-card-body">Card content</div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8" style="padding-left: 8px; padding-right: 8px;">
+          <div class="ant-list-item">
+            <div class="ant-list-item-content ant-list-item-content-single">
+              <div class="ant-card ant-card-bordered">
+                <div class="ant-card-head">
+                  <div class="ant-card-head-wrapper">
+                    <div class="ant-card-head-title">Title 5</div>
+                  </div>
+                </div>
+                <div class="ant-card-body">Card content</div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8" style="padding-left: 8px; padding-right: 8px;">
+          <div class="ant-list-item">
+            <div class="ant-list-item-content ant-list-item-content-single">
+              <div class="ant-card ant-card-bordered">
+                <div class="ant-card-head">
+                  <div class="ant-card-head-wrapper">
+                    <div class="ant-card-head-title">Title 6</div>
+                  </div>
+                </div>
+                <div class="ant-card-body">Card content</div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`renders ./components/list/demo/simple.md correctly 1`] = `
+<div>
+  <h3 style="margin-bottom: 16px;">Default Size</h3>
+  <div class="ant-list ant-list-split ant-list-bordered ant-list-something-after-last-item">
+    <div class="ant-list-header">
+      <div>Header</div>
+    </div>
+    <div class="ant-spin-nested-loading">
+      <div class="ant-spin-container">
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Racing car sprays burning fuel into crowd.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Japanese princess to wed commoner.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Australian walks 100km after outback crash.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Man charged over missing wedding girl.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Los Angeles battles huge wildfires.</div>
+        </div>
+      </div>
+    </div>
+    <div class="ant-list-footer">
+      <div>Footer</div>
+    </div>
+  </div>
+  <h3 style="margin: 16px 0px;">Small Size</h3>
+  <div class="ant-list ant-list-sm ant-list-split ant-list-bordered ant-list-something-after-last-item">
+    <div class="ant-list-header">
+      <div>Header</div>
+    </div>
+    <div class="ant-spin-nested-loading">
+      <div class="ant-spin-container">
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Racing car sprays burning fuel into crowd.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Japanese princess to wed commoner.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Australian walks 100km after outback crash.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Man charged over missing wedding girl.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Los Angeles battles huge wildfires.</div>
+        </div>
+      </div>
+    </div>
+    <div class="ant-list-footer">
+      <div>Footer</div>
+    </div>
+  </div>
+  <h3 style="margin: 16px 0px;">Large Size</h3>
+  <div class="ant-list ant-list-lg ant-list-split ant-list-bordered ant-list-something-after-last-item">
+    <div class="ant-list-header">
+      <div>Header</div>
+    </div>
+    <div class="ant-spin-nested-loading">
+      <div class="ant-spin-container">
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Racing car sprays burning fuel into crowd.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Japanese princess to wed commoner.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Australian walks 100km after outback crash.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Man charged over missing wedding girl.</div>
+        </div>
+        <div class="ant-list-item">
+          <div class="ant-list-item-content ant-list-item-content-single">Los Angeles battles huge wildfires.</div>
+        </div>
+      </div>
+    </div>
+    <div class="ant-list-footer">
+      <div>Footer</div>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`renders ./components/list/demo/vertical.md correctly 1`] = `
+<div class="ant-list ant-list-vertical ant-list-lg ant-list-split ant-list-something-after-last-item">
+  <div class="ant-spin-nested-loading">
+    <div class="ant-spin-container">
+      <div class="ant-list-item">
+        <div class="ant-list-item-extra-wrap">
+          <div class="ant-list-item-main">
+            <div class="ant-list-item-meta">
+              <div class="ant-list-item-meta-avatar"><span class="ant-avatar ant-avatar-image ant-avatar-circle"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span></div>
+              <div class="ant-list-item-meta-content">
+                <h4 class="ant-list-item-meta-title">
+                  <a href="https://vuecomponent.github.io/ant-design/">ant design part 0</a>
+                </h4>
+                <div class="ant-list-item-meta-description">Ant Design, a design language for background applications, is refined by Ant UED Team.</div>
+              </div>
+            </div>
+            <div class="ant-list-item-content">
+              We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.
+            </div>
+            <ul class="ant-list-item-action">
+              <li><span><i class="anticon anticon-star-o" style="margin-right: 8px;"></i>
+        156
+      </span><em class="ant-list-item-action-split"></em></li>
+              <li><span><i class="anticon anticon-like-o" style="margin-right: 8px;"></i>
+        156
+      </span><em class="ant-list-item-action-split"></em></li>
+              <li><span><i class="anticon anticon-message-o" style="margin-right: 8px;"></i>
+        2
+      </span></li>
+            </ul>
+          </div>
+          <div class="ant-list-item-extra">
+            <img width="272" alt="logo" src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png">
+          </div>
+        </div>
+      </div>
+      <div class="ant-list-item">
+        <div class="ant-list-item-extra-wrap">
+          <div class="ant-list-item-main">
+            <div class="ant-list-item-meta">
+              <div class="ant-list-item-meta-avatar"><span class="ant-avatar ant-avatar-image ant-avatar-circle"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span></div>
+              <div class="ant-list-item-meta-content">
+                <h4 class="ant-list-item-meta-title">
+                  <a href="https://vuecomponent.github.io/ant-design/">ant design part 1</a>
+                </h4>
+                <div class="ant-list-item-meta-description">Ant Design, a design language for background applications, is refined by Ant UED Team.</div>
+              </div>
+            </div>
+            <div class="ant-list-item-content">
+              We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.
+            </div>
+            <ul class="ant-list-item-action">
+              <li><span><i class="anticon anticon-star-o" style="margin-right: 8px;"></i>
+        156
+      </span><em class="ant-list-item-action-split"></em></li>
+              <li><span><i class="anticon anticon-like-o" style="margin-right: 8px;"></i>
+        156
+      </span><em class="ant-list-item-action-split"></em></li>
+              <li><span><i class="anticon anticon-message-o" style="margin-right: 8px;"></i>
+        2
+      </span></li>
+            </ul>
+          </div>
+          <div class="ant-list-item-extra">
+            <img width="272" alt="logo" src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png">
+          </div>
+        </div>
+      </div>
+      <div class="ant-list-item">
+        <div class="ant-list-item-extra-wrap">
+          <div class="ant-list-item-main">
+            <div class="ant-list-item-meta">
+              <div class="ant-list-item-meta-avatar"><span class="ant-avatar ant-avatar-image ant-avatar-circle"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span></div>
+              <div class="ant-list-item-meta-content">
+                <h4 class="ant-list-item-meta-title">
+                  <a href="https://vuecomponent.github.io/ant-design/">ant design part 2</a>
+                </h4>
+                <div class="ant-list-item-meta-description">Ant Design, a design language for background applications, is refined by Ant UED Team.</div>
+              </div>
+            </div>
+            <div class="ant-list-item-content">
+              We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.
+            </div>
+            <ul class="ant-list-item-action">
+              <li><span><i class="anticon anticon-star-o" style="margin-right: 8px;"></i>
+        156
+      </span><em class="ant-list-item-action-split"></em></li>
+              <li><span><i class="anticon anticon-like-o" style="margin-right: 8px;"></i>
+        156
+      </span><em class="ant-list-item-action-split"></em></li>
+              <li><span><i class="anticon anticon-message-o" style="margin-right: 8px;"></i>
+        2
+      </span></li>
+            </ul>
+          </div>
+          <div class="ant-list-item-extra">
+            <img width="272" alt="logo" src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png">
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="ant-list-pagination">
+    <ul unselectable="unselectable" class="ant-pagination">
+      <li title="Previous Page" aria-disabled="true" class="ant-pagination-disabled ant-pagination-prev">
+        <a class="ant-pagination-item-link"></a>
+      </li>
+      <li title="1" tabindex="0" class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active">
+        <a>1</a>
+      </li>
+      <li title="2" tabindex="0" class="ant-pagination-item ant-pagination-item-2">
+        <a>2</a>
+      </li>
+      <li title="3" tabindex="0" class="ant-pagination-item ant-pagination-item-3">
+        <a>3</a>
+      </li>
+      <li title="4" tabindex="0" class="ant-pagination-item ant-pagination-item-4">
+        <a>4</a>
+      </li>
+      <li title="5" tabindex="0" class="ant-pagination-item ant-pagination-item-5">
+        <a>5</a>
+      </li>
+      <li title="6" tabindex="0" class="ant-pagination-item ant-pagination-item-6">
+        <a>6</a>
+      </li>
+      <li title="7" tabindex="0" class="ant-pagination-item ant-pagination-item-7">
+        <a>7</a>
+      </li>
+      <li title="8" tabindex="0" class="ant-pagination-item ant-pagination-item-8">
+        <a>8</a>
+      </li>
+      <li title="Next Page" tabindex="0" class=" ant-pagination-next">
+        <a class="ant-pagination-item-link"></a>
+      </li>
+      <!---->
+    </ul>
+  </div>
+</div>
+`;
diff --git a/components/list/__tests__/demo.test.js b/components/list/__tests__/demo.test.js
new file mode 100644
index 000000000..fb75d12a3
--- /dev/null
+++ b/components/list/__tests__/demo.test.js
@@ -0,0 +1,3 @@
+import demoTest from '../../../tests/shared/demoTest'
+
+demoTest('list')
diff --git a/components/list/demo/basic.md b/components/list/demo/basic.md
new file mode 100644
index 000000000..82560b895
--- /dev/null
+++ b/components/list/demo/basic.md
@@ -0,0 +1,53 @@
+<cn>
+#### 基础列表
+基础列表。
+</cn>
+
+<us>
+#### Basic list
+Basic list.
+</us>
+
+```html
+<template>
+  <a-list
+    itemLayout="horizontal"
+    :dataSource="data"
+  >
+    <a-list-item slot="renderItem" slot-scope="item, index">
+      <a-list-item-meta
+        description="Ant Design, a design language for background applications, is refined by Ant UED Team"
+      >
+        <a slot="title" href="https://vuecomponent.github.io/ant-design/">{{item.title}}</a>
+        <a-avatar slot="avatar" src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
+      </a-list-item-meta>
+    </a-list-item>
+  </a-list>
+</template>
+<script>
+const data = [
+  {
+    title: 'Ant Design Title 1',
+  },
+  {
+    title: 'Ant Design Title 2',
+  },
+  {
+    title: 'Ant Design Title 3',
+  },
+  {
+    title: 'Ant Design Title 4',
+  },
+]
+export default {
+  data () {
+    return {
+      data,
+    }
+  },
+}
+</script>
+<style>
+
+</style>
+```
diff --git a/components/list/demo/grid.md b/components/list/demo/grid.md
new file mode 100644
index 000000000..6904cfc51
--- /dev/null
+++ b/components/list/demo/grid.md
@@ -0,0 +1,48 @@
+<cn>
+#### 栅格列表
+可以通过设置 `List` 的 `grid` 属性来实现栅格列表,`column` 可设置期望显示的列数。
+</cn>
+
+<us>
+#### Grid
+Creating a grid list by setting the `grid` property of List
+</us>
+
+```html
+<template>
+  <a-list
+    :grid="{ gutter: 16, column: 4 }"
+    :dataSource="data"
+  >
+    <a-list-item slot="renderItem" slot-scope="item, index">
+      <a-card :title="item.title">Card content</a-card>
+    </a-list-item>
+  </a-list>
+</template>
+<script>
+const data = [
+  {
+    title: 'Title 1',
+  },
+  {
+    title: 'Title 2',
+  },
+  {
+    title: 'Title 3',
+  },
+  {
+    title: 'Title 4',
+  },
+]
+export default {
+  data () {
+    return {
+      data,
+    }
+  },
+}
+</script>
+<style>
+
+</style>
+```
diff --git a/components/list/demo/index.vue b/components/list/demo/index.vue
new file mode 100644
index 000000000..043c5e56f
--- /dev/null
+++ b/components/list/demo/index.vue
@@ -0,0 +1,57 @@
+<script>
+import Basic from './basic'
+import Grid from './grid'
+import InfiniteLoad from './infinite-load'
+import InfiniteVirtualizedLoad from './infinite-virtualized-load'
+import Loadmore from './loadmore'
+import Resposive from './resposive'
+import Simple from './simple'
+import Vertical from './vertical'
+
+import CN from '../index.zh-CN.md'
+import US from '../index.en-US.md'
+
+const md = {
+  cn: `# 列表
+  通用列表。
+## 何时使用
+最基础的列表展示,可承载文字、列表、图片、段落,常用于后台数据展示页面。
+        ## 代码演示`,
+  us: `# List
+  Simple List.
+
+## When To Use
+
+A list can be used to display content related to a single subject. The content can consist of multiple elements of varying type and size.
+  ## Examples
+  `,
+}
+export default {
+  category: 'Components',
+  type: 'Data Display',
+  title: 'List',
+  subtitle: '列表',
+  cols: 1,
+  render () {
+    return (
+      <div>
+        <md cn={md.cn} us={md.us}/>
+        <Basic />
+        <Grid />
+        <Loadmore />
+        <Resposive />
+        <Simple />
+        <Vertical />
+        <InfiniteLoad />
+        <InfiniteVirtualizedLoad />
+        <api>
+          <template slot='cn'>
+            <CN/>
+          </template>
+          <US/>
+        </api>
+      </div>
+    )
+  },
+}
+</script>
diff --git a/components/list/demo/infinite-load.md b/components/list/demo/infinite-load.md
new file mode 100644
index 000000000..c680a8ea2
--- /dev/null
+++ b/components/list/demo/infinite-load.md
@@ -0,0 +1,95 @@
+<cn>
+#### 滚动加载
+结合 [vue-infinite-scroll](https://github.com/ElemeFE/vue-infinite-scroll) 实现滚动自动加载列表。
+</cn>
+
+<us>
+#### Scrolling loaded
+The example of infinite load with [vue-infinite-scroll](https://github.com/ElemeFE/vue-infinite-scroll).
+</us>
+
+```html
+<template>
+<div 
+  class="demo-infinite-container" 
+  v-infinite-scroll="handleInfiniteOnLoad" 
+  :infinite-scroll-disabled="busy"
+  :infinite-scroll-distance="10"
+>
+  <a-list
+    :dataSource="data"
+  >
+    <a-list-item slot="renderItem" slot-scope="item, index">
+      <a-list-item-meta :description="item.email">
+        <a slot="title" :href="item.href">{{item.name.last}}</a>
+        <a-avatar slot="avatar" src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
+      </a-list-item-meta>
+      <div>Content</div>
+    </a-list-item>
+    <a-spin v-if="loading && !busy" class="demo-loading" />
+  </a-list>
+</div>
+</template>
+<script>
+import reqwest from 'reqwest'
+import infiniteScroll from 'vue-infinite-scroll'
+const fakeDataUrl = 'https://randomuser.me/api/?results=5&inc=name,gender,email,nat&noinfo'
+export default {
+  directives: { infiniteScroll },
+  data () {
+    return {
+      data: [],
+      loading: false,
+      busy: false,
+    }
+  },
+  mounted () {
+    this.getData((res) => {
+      this.data = res.results
+    })
+  },
+  methods: {
+    getData  (callback) {
+      reqwest({
+        url: fakeDataUrl,
+        type: 'json',
+        method: 'get',
+        contentType: 'application/json',
+        success: (res) => {
+          callback(res)
+        },
+      })
+    },
+    handleInfiniteOnLoad  () {
+      const data = this.data
+      this.loading = true
+      if (data.length > 14) {
+        this.$message.warning('Infinite List loaded all')
+        this.busy = true
+        this.loading = false
+        return
+      }
+      this.getData((res) => {
+        this.data = data.concat(res.results)
+        this.loading = false
+      })
+    },
+  },
+}
+</script>
+<style>
+.demo-infinite-container {
+  border: 1px solid #e8e8e8;
+  border-radius: 4px;
+  overflow: auto;
+  padding: 8px 24px;
+  height: 300px;
+}
+.demo-loading {
+  position: absolute;
+  bottom: 40px;
+  width: 100%;
+  text-align: center;
+}
+</style>
+```
diff --git a/components/list/demo/infinite-virtualized-load.md b/components/list/demo/infinite-virtualized-load.md
new file mode 100644
index 000000000..141761665
--- /dev/null
+++ b/components/list/demo/infinite-virtualized-load.md
@@ -0,0 +1,95 @@
+<cn>
+#### 滚动加载无限长列表
+结合 [vue-virtual-scroller](https://github.com/Akryum/vue-virtual-scroller) 实现滚动加载无限长列表,带有虚拟化([virtualization](https://blog.jscrambler.com/optimizing-react-rendering-through-virtualization/))功能,能够提高数据量大时候长列表的性能。
+可以结合 [vue-infinite-scroll](https://github.com/ElemeFE/vue-infinite-scroll) 实现滚动自动加载无限长列表。
+`virtualized` 是在大数据列表中应用的一种技术,主要是为了减少不可见区域不必要的渲染从而提高性能,特别是数据量在成千上万条效果尤为明显。
+</cn>
+
+<us>
+#### Infinite & virtualized
+An example of infinite list & virtualized loading using [vue-virtual-scroller](https://github.com/Akryum/vue-virtual-scroller). 
+`Virtualized` rendering is a technique to mount big sets of data. It reduces the amount of rendered DOM nodes by tracking and hiding whatever isn't currently visible.
+</us>
+
+```html
+<template>
+  <a-list>
+    <virtual-scroller
+      style="height: 400px"
+      :items="data"
+      item-height="73"
+      v-infinite-scroll="handleInfiniteOnLoad"
+      :infinite-scroll-disabled="busy"
+      :infinite-scroll-distance="10"
+    >
+      <a-list-item slot-scope="{item}">
+        <a-list-item-meta :description="item.email">
+          <a slot="title" :href="item.href">{{item.name.last}}</a>
+          <a-avatar slot="avatar" src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
+        </a-list-item-meta>
+        <div>Content {{item.index}}</div>
+      </a-list-item>
+    </virtual-scroller>
+    <a-spin v-if="loading"  class="demo-loading" />
+  </a-list>
+</template>
+<script>
+import reqwest from 'reqwest'
+import infiniteScroll from 'vue-infinite-scroll'
+import { VirtualScroller } from 'vue-virtual-scroller'
+const fakeDataUrl = 'https://randomuser.me/api/?results=10&inc=name,gender,email,nat&noinfo'
+export default {
+  directives: { infiniteScroll },
+  data () {
+    return {
+      data: [],
+      loading: false,
+      busy: false,
+    }
+  },
+  mounted () {
+    this.getData((res) => {
+      this.data = res.results.map((item, index) => ({ ...item, index }))
+    })
+  },
+  methods: {
+    getData  (callback) {
+      reqwest({
+        url: fakeDataUrl,
+        type: 'json',
+        method: 'get',
+        contentType: 'application/json',
+        success: (res) => {
+          callback(res)
+        },
+      })
+    },
+    handleInfiniteOnLoad  () {
+      const data = this.data
+      this.loading = true
+      if (data.length > 100) {
+        this.$message.warning('Infinite List loaded all')
+        this.busy = true
+        this.loading = false
+        return
+      }
+      this.getData((res) => {
+        this.data = data.concat(res.results).map((item, index) => ({ ...item, index }))
+        this.loading = false
+      })
+    },
+  },
+  components: {
+    'virtual-scroller': VirtualScroller,
+  },
+}
+</script>
+<style>
+.demo-loading {
+  position: absolute;
+  bottom: 40px;
+  width: 100%;
+  text-align: center;
+}
+</style>
+```
diff --git a/components/list/demo/loadmore.md b/components/list/demo/loadmore.md
new file mode 100644
index 000000000..672f8ca55
--- /dev/null
+++ b/components/list/demo/loadmore.md
@@ -0,0 +1,86 @@
+<cn>
+#### 加载更多
+可通过 `loadMore` 属性实现加载更多功能。
+</cn>
+
+<us>
+#### Load more
+Load more list with `loadMore` property.
+</us>
+
+```html
+<template>
+  <a-list
+    class="demo-loadmore-list"
+    :loading="loading"
+    itemLayout="horizontal"
+    :dataSource="data"
+  >
+    <div v-if="showLoadingMore" slot="loadMore" :style="{ textAlign: 'center', marginTop: '12px', height: '32px', lineHeight: '32px' }">
+      <a-spin v-if="loadingMore" />
+      <a-button v-else @click="onLoadMore">loading more</a-button>
+    </div>
+    <a-list-item slot="renderItem" slot-scope="item, index">
+      <a slot="actions">edit</a>
+      <a slot="actions">more</a>
+      <a-list-item-meta
+        description="Ant Design, a design language for background applications, is refined by Ant UED Team"
+      >
+        <a slot="title" href="https://vuecomponent.github.io/ant-design/">{{item.name.last}}</a>
+        <a-avatar slot="avatar" src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
+      </a-list-item-meta>
+      <div>content</div>
+    </a-list-item>
+  </a-list>
+</template>
+<script>
+import reqwest from 'reqwest'
+
+const fakeDataUrl = 'https://randomuser.me/api/?results=5&inc=name,gender,email,nat&noinfo'
+
+export default {
+  data () {
+    return {
+      loading: true,
+      loadingMore: false,
+      showLoadingMore: true,
+      data: [],
+    }
+  },
+  mounted () {
+    this.getData((res) => {
+      this.loading = false
+      this.data = res.results
+    })
+  },
+  methods: {
+    getData  (callback) {
+      reqwest({
+        url: fakeDataUrl,
+        type: 'json',
+        method: 'get',
+        contentType: 'application/json',
+        success: (res) => {
+          callback(res)
+        },
+      })
+    },
+    onLoadMore () {
+      this.loadingMore = true
+      this.getData((res) => {
+        this.data = this.data.concat(res.results)
+        this.loadingMore = false
+        this.$nextTick(() => {
+          window.dispatchEvent(new Event('resize'))
+        })
+      })
+    },
+  },
+}
+</script>
+<style>
+.demo-loadmore-list {
+  min-height: 350px;
+}
+</style>
+```
diff --git a/components/list/demo/resposive.md b/components/list/demo/resposive.md
new file mode 100644
index 000000000..00a5bdd70
--- /dev/null
+++ b/components/list/demo/resposive.md
@@ -0,0 +1,55 @@
+<cn>
+#### 响应式的栅格列表
+响应式的栅格列表。尺寸与 [Layout Grid](https://vuecomponent.github.io/ant-design/components/grid-cn/#Col) 保持一致。
+</cn>
+
+<us>
+#### Responsive grid list
+Responsive grid list. The size property is as same as [Layout Grid](https://vuecomponent.github.io/ant-design/components/grid/#Col).
+</us>
+
+```html
+<template>
+  <a-list
+    :grid="{ gutter: 16, xs: 1, sm: 2, md: 4, lg: 4, xl: 6, xxl: 3 }"
+    :dataSource="data"
+  >
+    <a-list-item slot="renderItem" slot-scope="item, index">
+      <a-card :title="item.title">Card content</a-card>
+    </a-list-item>
+  </a-list>
+</template>
+<script>
+const data = [
+  {
+    title: 'Title 1',
+  },
+  {
+    title: 'Title 2',
+  },
+  {
+    title: 'Title 3',
+  },
+  {
+    title: 'Title 4',
+  },
+  {
+    title: 'Title 5',
+  },
+  {
+    title: 'Title 6',
+  },
+]
+
+export default {
+  data () {
+    return {
+      data,
+    }
+  },
+}
+</script>
+<style>
+
+</style>
+```
diff --git a/components/list/demo/simple.md b/components/list/demo/simple.md
new file mode 100644
index 000000000..76324fb9f
--- /dev/null
+++ b/components/list/demo/simple.md
@@ -0,0 +1,69 @@
+<cn>
+#### 简单列表
+列表拥有大、中、小三种尺寸。
+通过设置 `size` 为 `large` `small` 分别把按钮设为大、小尺寸。若不设置 `size`,则尺寸为中。
+可通过设置 `header` 和 `footer`,来自定义列表头部和尾部。
+</cn>
+
+<us>
+#### Simple list
+Ant Design supports a default list size as well as a large and small size.
+If a large or small list is desired, set the size property to either large or small respectively. Omit the size property for a list with the default size.
+Customizing the header and footer of list by setting `header` and `footer` property.
+</us>
+
+```html
+<template>
+<div>
+  <h3 :style="{ marginBottom: '16px' }">Default Size</h3>
+  <a-list
+    bordered
+    :dataSource="data"
+  >
+    <a-list-item slot="renderItem" slot-scope="item, index">{{item}}</a-list-item>
+    <div slot="header">Header</div>
+    <div slot="footer">Footer</div>
+  </a-list>
+  <h3 :style="{ margin: '16px 0' }">Small Size</h3>
+  <a-list
+    size="small"
+    bordered
+    :dataSource="data"
+  >
+    <a-list-item slot="renderItem" slot-scope="item, index">{{item}}</a-list-item>
+    <div slot="header">Header</div>
+    <div slot="footer">Footer</div>
+  </a-list>
+  <h3 :style="{ margin: '16px 0' }">Large Size</h3>
+  <a-list
+    size="large"
+    bordered
+    :dataSource="data"
+  >
+    <a-list-item slot="renderItem" slot-scope="item, index">{{item}}</a-list-item>
+    <div slot="header">Header</div>
+    <div slot="footer">Footer</div>
+  </a-list>
+</div>
+</template>
+<script>
+const data = [
+  'Racing car sprays burning fuel into crowd.',
+  'Japanese princess to wed commoner.',
+  'Australian walks 100km after outback crash.',
+  'Man charged over missing wedding girl.',
+  'Los Angeles battles huge wildfires.',
+]
+
+export default {
+  data () {
+    return {
+      data,
+    }
+  },
+}
+</script>
+<style>
+
+</style>
+```
diff --git a/components/list/demo/vertical.md b/components/list/demo/vertical.md
new file mode 100644
index 000000000..c631b2c10
--- /dev/null
+++ b/components/list/demo/vertical.md
@@ -0,0 +1,73 @@
+<cn>
+#### 竖排列表样式
+通过设置 `itemLayout` 属性为 `vertical` 可实现竖排列表样式。
+</cn>
+
+<us>
+#### Vertical
+Setting `itemLayout` property with `vertical` to create a vertical list.
+</us>
+
+```html
+<template>
+  <a-list
+    itemLayout="vertical"
+    size="large"
+    :pagination="pagination"
+    :dataSource="listData"
+  >
+    <a-list-item slot="renderItem" slot-scope="item, index" key="item.title">
+      <template slot="actions" v-for="{type, text} in actions">
+        <span :key="type">
+          <a-icon :type="type" style="margin-right: 8px" />
+          {{text}}
+        </span>
+      </template>
+      <img slot="extra" width="272" alt="logo" src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png" />
+      <a-list-item-meta
+        :description="item.description"
+      >
+        <a slot="title" :href="item.href">{{item.title}}</a>
+        <a-avatar slot="avatar" :src="item.avatar" />
+      </a-list-item-meta>
+      {{item.content}}
+    </a-list-item>
+  </a-list>
+</template>
+<script>
+const listData = []
+for (let i = 0; i < 23; i++) {
+  listData.push({
+    href: 'https://vuecomponent.github.io/ant-design/',
+    title: `ant design part ${i}`,
+    avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
+    description: 'Ant Design, a design language for background applications, is refined by Ant UED Team.',
+    content: 'We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.',
+  })
+}
+
+const pagination = {
+  onChange: (page) => {
+    console.log(page)
+  },
+  pageSize: 3,
+}
+
+export default {
+  data () {
+    return {
+      listData,
+      pagination,
+      actions: [
+        { type: 'star-o', text: '156' },
+        { type: 'like-o', text: '156' },
+        { type: 'message-o', text: '2' },
+      ],
+    }
+  },
+}
+</script>
+<style>
+
+</style>
+```
diff --git a/components/list/index.en-US.md b/components/list/index.en-US.md
new file mode 100644
index 000000000..76b468dbc
--- /dev/null
+++ b/components/list/index.en-US.md
@@ -0,0 +1,46 @@
+
+## API
+
+### List
+
+| Property | Description | Type | Default |
+| -------- | ----------- | ---- | ------- |
+| bordered | Toggles rendering of the border around the list | boolean | false |
+| footer | List footer renderer | string\|slot | - |
+| grid | The grid type of list. You can set grid to something like {gutter: 16, column: 4} | object | - |
+| header | List header renderer | string\|slot | - |
+| itemLayout | The layout of list, default is `horizontal`, If a vertical list is desired, set the itemLayout property to `vertical` | string | - |
+| loading | Shows a loading indicator while the contents of the list are being fetched | boolean\|[object](https://vuecomponent.github.io/ant-design/components/spin/#API) | false |
+| loadMore | Shows a load more content | string\|slot | - |
+| pagination | Pagination [config](https://vuecomponent.github.io/ant-design/components/pagination/#API), hide it by setting it to false | boolean \| object | false |
+| split | Toggles rendering of the split under the list item | boolean | true |
+| renderItem | Custom item renderer, slot="renderItem" and slot-scope="item, index"  | (item, index) => vNode |  | - |
+
+### List grid props
+
+| Property | Description | Type | Default |
+| -------- | ----------- | ---- | ------- |
+| column | column of grid | number | - |
+| gutter | spacing between grid | number | 0 |
+| size | Size of list | `default` \| `middle` \| `small` | `default` |
+| xs | `<576px` column of grid | number | - |
+| sm | `≥576px` column of grid | number | - |
+| md | `≥768px` column of grid | number | - |
+| lg | `≥992px` column of grid | number | - |
+| xl | `≥1200px` column of grid | number | - |
+| xxl | `≥1600px` column of grid | number | - |
+
+### List.Item
+
+| Property | Description | Type | Default |
+| -------- | ----------- | ---- | ------- |
+| actions | The actions content of list item. If `itemLayout` is `vertical`, shows the content on bottom, otherwise shows content on the far right. | Array\<vNode>\|slot | - |
+| extra | The extra content of list item. If `itemLayout` is `vertical`, shows the content on right, otherwise shows content on the far right. | string\|slot | - |
+
+### List.Item.Meta
+
+| Property | Description | Type | Default |
+| -------- | ----------- | ---- | ------- |
+| avatar | The avatar of list item | slot | - |
+| description | The description of list item | string\|slot | - |
+| title | The title of list item | string\|slot | - |
diff --git a/components/list/index.jsx b/components/list/index.jsx
new file mode 100644
index 000000000..7e0c04f14
--- /dev/null
+++ b/components/list/index.jsx
@@ -0,0 +1,249 @@
+import PropTypes from '../_util/vue-types'
+import classNames from 'classnames'
+import omit from 'omit.js'
+import { SpinProps } from '../spin'
+import LocaleReceiver from '../locale-provider/LocaleReceiver'
+import defaultLocale from '../locale-provider/default'
+
+import Spin from '../spin'
+import Pagination from '../pagination'
+import { Row } from '../grid'
+
+import Item from './Item'
+import { initDefaultProps, getComponentFromProp } from '../_util/props-util'
+import { cloneElement } from '../_util/vnode'
+
+export { ListItemProps, ListItemMetaProps } from './Item'
+
+export const ColumnCount = ['', 1, 2, 3, 4, 6, 8, 12, 24]
+
+export const ColumnType = ['gutter', 'column', 'xs', 'sm', 'md', 'lg', 'xl', 'xxl']
+
+export const ListGridType = {
+  gutter: PropTypes.number,
+  column: PropTypes.oneOf(ColumnCount),
+  xs: PropTypes.oneOf(ColumnCount),
+  sm: PropTypes.oneOf(ColumnCount),
+  md: PropTypes.oneOf(ColumnCount),
+  lg: PropTypes.oneOf(ColumnCount),
+  xl: PropTypes.oneOf(ColumnCount),
+  xxl: PropTypes.oneOf(ColumnCount),
+}
+
+export const ListSize = ['small', 'default', 'large']
+
+export const ListProps = () => ({
+  bordered: PropTypes.bool,
+  dataSource: PropTypes.any,
+  extra: PropTypes.any,
+  grid: PropTypes.shape(ListGridType).loose,
+  itemLayout: PropTypes.string,
+  loading: PropTypes.oneOfType([PropTypes.bool, SpinProps()]),
+  loadMore: PropTypes.any,
+  pagination: PropTypes.any,
+  prefixCls: PropTypes.string,
+  rowKey: PropTypes.any,
+  renderItem: PropTypes.any,
+  size: PropTypes.oneOf(ListSize),
+  split: PropTypes.bool,
+  header: PropTypes.any,
+  footer: PropTypes.any,
+  locale: PropTypes.object,
+})
+
+export default {
+  Item,
+  name: 'AList',
+
+  props: initDefaultProps(ListProps(), {
+    dataSource: [],
+    prefixCls: 'ant-list',
+    bordered: false,
+    split: true,
+    loading: false,
+    pagination: false,
+  }),
+  provide () {
+    return {
+      listContext: this,
+    }
+  },
+  data () {
+    this.keys = []
+    this.defaultPaginationProps = {
+      current: 1,
+      pageSize: 10,
+      onChange: (page, pageSize) => {
+        const { pagination } = this
+        this.paginationCurrent = page
+        if (pagination && pagination.onChange) {
+          pagination.onChange(page, pageSize)
+        }
+      },
+      total: 0,
+    }
+    return {
+      paginationCurrent: 1,
+    }
+  },
+  methods: {
+    renderItem2 (item, index) {
+      const { dataSource, $scopedSlots, rowKey } = this
+      let key
+      const renderItem = this.renderItem || $scopedSlots.renderItem
+      if (typeof rowKey === 'function') {
+        key = rowKey(dataSource[index])
+      } else if (typeof rowKey === 'string') {
+        key = dataSource[rowKey]
+      } else {
+        key = dataSource.key
+      }
+
+      if (!key) {
+        key = `list-item-${index}`
+      }
+
+      this.keys[index] = key
+
+      return renderItem(item, index)
+    },
+
+    isSomethingAfterLastTtem () {
+      const { pagination } = this
+      const loadMore = getComponentFromProp(this, 'loadMore')
+      const footer = getComponentFromProp(this, 'footer')
+      return !!(loadMore || pagination || footer)
+    },
+
+    renderEmpty (contextLocale) {
+      const locale = { ...contextLocale, ...this.locale }
+      return <div class={`${this.prefixCls}-empty-text`}>{locale.emptyText}</div>
+    },
+  },
+
+  render () {
+    const {
+      bordered,
+      split,
+      itemLayout,
+      pagination,
+      prefixCls,
+      grid,
+      dataSource,
+      size,
+      loading,
+      $listeners,
+      $slots,
+      paginationCurrent,
+    } = this
+    const loadMore = getComponentFromProp(this, 'loadMore')
+    const footer = getComponentFromProp(this, 'footer')
+    const header = getComponentFromProp(this, 'header')
+    const children = $slots.default || []
+    let loadingProp = loading
+    if (typeof loadingProp === 'boolean') {
+      loadingProp = {
+        spinning: loadingProp,
+      }
+    }
+    const isLoading = (loadingProp && loadingProp.spinning)
+
+    // large => lg
+    // small => sm
+    let sizeCls = ''
+    switch (size) {
+      case 'large':
+        sizeCls = 'lg'
+        break
+      case 'small':
+        sizeCls = 'sm'
+        break
+      default:
+        break
+    }
+    const classString = classNames(prefixCls, {
+      [`${prefixCls}-vertical`]: itemLayout === 'vertical',
+      [`${prefixCls}-${sizeCls}`]: sizeCls,
+      [`${prefixCls}-split`]: split,
+      [`${prefixCls}-bordered`]: bordered,
+      [`${prefixCls}-loading`]: isLoading,
+      [`${prefixCls}-grid`]: grid,
+      [`${prefixCls}-something-after-last-item`]: this.isSomethingAfterLastTtem(),
+    })
+    const paginationProps = {
+      ...this.defaultPaginationProps,
+      total: dataSource.length,
+      current: paginationCurrent,
+      ...pagination || {},
+    }
+    const largestPage = Math.ceil(
+      paginationProps.total / paginationProps.pageSize,
+    )
+    if (paginationProps.current > largestPage) {
+      paginationProps.current = largestPage
+    }
+    const { class: cls, style, onShowSizeChange = () => {}, ...restProps } = paginationProps
+    const paginationContent = pagination ? (
+      <div class={`${prefixCls}-pagination`}>
+        <Pagination
+          {...{
+            props: omit(restProps, ['onChange']),
+            class: cls, style,
+            on: { change: this.defaultPaginationProps.onChange, showSizeChange: onShowSizeChange },
+          }}
+        />
+      </div>
+    ) : null
+
+    let splitDataSource = [...dataSource]
+    if (pagination) {
+      if (
+        dataSource.length >
+        (paginationProps.current - 1) * paginationProps.pageSize
+      ) {
+        splitDataSource = [...dataSource].splice(
+          (paginationProps.current - 1) * paginationProps.pageSize,
+          paginationProps.pageSize,
+        )
+      }
+    }
+
+    let childrenContent
+    childrenContent = isLoading && (<div style={{ minHeight: 53 }} />)
+    if (splitDataSource.length > 0) {
+      const items = splitDataSource.map((item, index) => this.renderItem2(item, index))
+      const childrenList = items.map((child, index) => cloneElement(child, {
+        key: this.keys[index],
+      }),
+      )
+
+      childrenContent = grid ? (
+        <Row gutter={grid.gutter}>{childrenList}</Row>
+      ) : childrenList
+    } else if (!children && !isLoading) {
+      childrenContent = (
+        <LocaleReceiver
+          componentName='Table'
+          defaultLocale={defaultLocale.Table}
+          scopedSlots={
+            { default: this.renderEmpty }
+          }
+        />
+      )
+    }
+    const paginationPosition = paginationProps.position || 'bottom'
+
+    return (
+      <div class={classString} {...{ on: $listeners }}>
+        {(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent}
+        {header && <div class={`${prefixCls}-header`}>{header}</div>}
+        <Spin {...{ props: loadingProp }}>
+          {childrenContent}
+          {children}
+        </Spin>
+        {footer && <div class={`${prefixCls}-footer`}>{footer}</div>}
+        {loadMore || (paginationPosition === 'bottom' || paginationPosition === 'both') && paginationContent}
+      </div>
+    )
+  },
+}
diff --git a/components/list/index.zh-CN.md b/components/list/index.zh-CN.md
new file mode 100644
index 000000000..e79d29fa7
--- /dev/null
+++ b/components/list/index.zh-CN.md
@@ -0,0 +1,46 @@
+
+## API
+
+### List
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| bordered | 是否展示边框 | boolean | false |
+| footer | 列表底部 | string\|slot | - |
+| grid | 列表栅格配置 | object | - |
+| header | 列表头部 | string\|slot | - |
+| itemLayout | 设置 `List.Item` 布局, 设置成 `vertical` 则竖直样式显示, 默认横排 | string | - |
+| loading | 当卡片内容还在加载中时,可以用 `loading` 展示一个占位 | boolean\|[object](https://vuecomponent.github.io/ant-design/components/spin-cn/#API) | false |
+| loadMore | 加载更多 | string\|slot | - |
+| pagination | 对应的 `pagination` [配置]((https://vuecomponent.github.io/ant-design/components/pagination-cn/#API)), 设置 `false` 不显示 | boolean\|object | false |
+| size | list 的尺寸 | `default` \| `middle` \| `small` | `default` |
+| split | 是否展示分割线 | boolean | true |
+| renderItem | 自定义`Item`函数,也可使用slot="renderItem" 和 slot-scope="item, index" | (item, index) => vNode |  | - |
+
+### List grid props
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| column | 列数 | number | - |
+| gutter | 栅格间隔 | number | 0 |
+| xs | `<576px` 展示的列数 | number | - |
+| sm | `≥576px` 展示的列数 | number | - |
+| md | `≥768px` 展示的列数 | number | - |
+| lg | `≥992px` 展示的列数 | number | - |
+| xl | `≥1200px` 展示的列数 | number | - |
+| xxl | `≥1600px` 展示的列数 | number | - |
+
+### List.Item
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| actions | 列表操作组,根据 `itemLayout` 的不同, 位置在卡片底部或者最右侧 | Array\<vNode>/|slot | - |
+| extra | 额外内容, 通常用在 `itemLayout` 为 `vertical` 的情况下, 展示右侧内容; `horizontal` 展示在列表元素最右侧 | string\|slot | - |
+
+### List.Item.Meta
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| avatar | 列表元素的图标 | slot | - |
+| description | 列表元素的描述内容 | string\|slot | - |
+| title | 列表元素的标题 | string\|slot | - |
diff --git a/components/list/style/bordered.less b/components/list/style/bordered.less
new file mode 100644
index 000000000..e3175b8a6
--- /dev/null
+++ b/components/list/style/bordered.less
@@ -0,0 +1,41 @@
+.@{list-prefix-cls}-bordered {
+  border-radius: @border-radius-base;
+  border: 1px solid @border-color-base;
+  .@{list-prefix-cls}-header {
+    padding-left: 24px;
+    padding-right: 24px;
+  }
+
+  .@{list-prefix-cls}-footer {
+    padding-left: 24px;
+    padding-right: 24px;
+  }
+
+  .@{list-prefix-cls}-item {
+    border-bottom: 1px solid @border-color-split;
+    padding-left: 24px;
+    padding-right: 24px;
+  }
+
+  .@{list-prefix-cls}-pagination {
+    margin: 16px 24px;
+  }
+
+  &.@{list-prefix-cls}-sm {
+    .@{list-prefix-cls}-item {
+      padding-left: 16px;
+      padding-right: 16px;
+    }
+    .@{list-prefix-cls}-header,
+    .@{list-prefix-cls}-footer {
+      padding: 8px 16px;
+    }
+  }
+
+  &.@{list-prefix-cls}-lg {
+    .@{list-prefix-cls}-header,
+    .@{list-prefix-cls}-footer {
+      padding: 16px 24px;
+    }
+  }
+}
diff --git a/components/list/style/index.js b/components/list/style/index.js
new file mode 100644
index 000000000..c76d8c508
--- /dev/null
+++ b/components/list/style/index.js
@@ -0,0 +1,7 @@
+import '../../style/index.less'
+import './index.less'
+
+// style dependencies
+import '../../spin/style'
+import '../../pagination/style'
+import '../../grid/style'
diff --git a/components/list/style/index.less b/components/list/style/index.less
new file mode 100644
index 000000000..11e55b6c9
--- /dev/null
+++ b/components/list/style/index.less
@@ -0,0 +1,207 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+
+@list-prefix-cls: ~"@{ant-prefix}-list";
+
+.@{list-prefix-cls} {
+  .reset-component;
+  position: relative;
+  * {
+    outline: none;
+  }
+  &-pagination {
+    margin-top: 24px;
+    text-align: right;
+  }
+  &-more {
+    margin-top: 12px;
+    text-align: center;
+    button {
+      padding-left: 32px;
+      padding-right: 32px;
+    }
+  }
+  &-spin {
+    text-align: center;
+    min-height: 40px;
+  }
+  &-empty-text {
+    color: @text-color-secondary;
+    font-size: @font-size-base;
+    padding: 16px;
+    text-align: center;
+  }
+  &-item {
+    align-items: center;
+    display: flex;
+    padding-top: 12px;
+    padding-bottom: 12px;
+    &-meta {
+      align-items: flex-start;
+      display: flex;
+      flex: 1;
+      font-size: 0;
+      &-avatar {
+        margin-right: 16px;
+      }
+      &-content {
+        flex: 1 0;
+      }
+      &-title {
+        color: @text-color;
+        margin-bottom: 4px;
+        font-size: @font-size-base;
+        line-height: 22px;
+        > a {
+          color: @text-color;
+          transition: all .3s;
+          &:hover {
+            color: @primary-color;
+          }
+        }
+      }
+      &-description {
+        color: @text-color-secondary;
+        font-size: @font-size-base;
+        line-height: 22px;
+      }
+    }
+    &-content {
+      display: flex;
+      flex: 1;
+      justify-content: flex-end;
+    }
+    &-content-single {
+      justify-content: flex-start;
+    }
+    &-action {
+      font-size: 0;
+      flex: 0 0 auto;
+      margin-left: 48px;
+      padding: 0;
+      list-style: none;
+      & > li {
+        display: inline-block;
+        color: @text-color-secondary;
+        cursor: pointer;
+        padding: 0 8px;
+        position: relative;
+        font-size: @font-size-base;
+        line-height: 22px;
+        text-align: center;
+      }
+      & > li:first-child {
+        padding-left: 0;
+      }
+      &-split {
+        background-color: @border-color-split;
+        margin-top: -7px;
+        position: absolute;
+        top: 50%;
+        right: 0;
+        width: 1px;
+        height: 14px;
+      }
+    }
+    &-main {
+      display: flex;
+      flex: 1;
+    }
+  }
+
+  &-header,
+  &-footer {
+    padding-top: 12px;
+    padding-bottom: 12px;
+  }
+
+  &-empty {
+    color: @text-color-secondary;
+    padding: 16px 0;
+    font-size: 12px;
+    text-align: center;
+  }
+
+  &-split &-item {
+    border-bottom: 1px solid @border-color-split;
+    &:last-child {
+      border-bottom: none;
+    }
+  }
+
+  &-split &-header {
+    border-bottom: 1px solid @border-color-split;
+  }
+
+  &-loading &-spin-nested-loading {
+    min-height: 32px;
+  }
+
+  &-something-after-last-item &-item:last-child {
+    border-bottom: 1px solid @border-color-split;
+  }
+
+  &-lg &-item {
+    padding-top: 16px;
+    padding-bottom: 16px;
+  }
+
+  &-sm &-item {
+    padding-top: 8px;
+    padding-bottom: 8px;
+  }
+
+  &-vertical &-item {
+    display: block;
+    &-extra-wrap {
+      display: flex;
+    }
+    &-main {
+      display: block;
+      flex: 1;
+    }
+    &-extra {
+      margin-left: 58px;
+    }
+    &-meta {
+      margin-bottom: 16px;
+      &-avatar {
+        display: none;
+      }
+      &-title {
+        color: @heading-color;
+        margin-bottom: 12px;
+        font-size: @font-size-lg;
+        line-height: 24px;
+      }
+    }
+    &-content {
+      display: block;
+      color: @text-color;
+      font-size: @font-size-base;
+      margin-bottom: 16px;
+    }
+    &-action {
+      margin-left: auto;
+      > li {
+        padding: 0 16px;
+        &:first-child {
+          padding-left: 0;
+        }
+      }
+    }
+  }
+
+  &-grid &-item {
+    border-bottom: none;
+    padding-top: 0;
+    padding-bottom: 0;
+    margin-bottom: 20px;
+    &-content {
+      display: block;
+    }
+  }
+}
+
+@import './bordered';
+@import './responsive';
diff --git a/components/list/style/responsive.less b/components/list/style/responsive.less
new file mode 100644
index 000000000..a1eba05ca
--- /dev/null
+++ b/components/list/style/responsive.less
@@ -0,0 +1,42 @@
+@media screen and (max-width: @screen-md) {
+  .@{list-prefix-cls} {
+    &-item {
+      &-action {
+        margin-left: 24px;
+      }
+    }
+  }
+
+  .@{list-prefix-cls}-vertical {
+    .@{list-prefix-cls}-item {
+      &-extra {
+        margin-left: 24px;
+      }
+    }
+  }
+}
+
+@media screen and (max-width: @screen-xs) {
+  .@{list-prefix-cls} {
+    &-item {
+      flex-wrap: wrap;
+      &-action {
+        margin-left: 12px;
+      }
+    }
+  }
+
+  .@{list-prefix-cls}-vertical {
+    .@{list-prefix-cls}-item {
+      &-extra-wrap {
+        flex-wrap: wrap-reverse;
+      }
+      &-main {
+        min-width: 220px;
+      }
+      &-extra {
+        margin-left: 0;
+      }
+    }
+  }
+}
diff --git a/components/style.js b/components/style.js
index b2e783035..640ee4c18 100644
--- a/components/style.js
+++ b/components/style.js
@@ -45,3 +45,4 @@ import './upload/style'
 import './layout/style'
 import './form/style'
 import './anchor/style'
+import './list/style'
diff --git a/site/components.js b/site/components.js
index 6422abf9b..8b262e351 100644
--- a/site/components.js
+++ b/site/components.js
@@ -24,7 +24,7 @@ import {
   Input,
   InputNumber,
   Layout,
-  // List,
+  List,
   LocaleProvider,
   message,
   Menu,
@@ -100,7 +100,9 @@ Vue.component(Layout.Header.name, Layout.Header)
 Vue.component(Layout.Footer.name, Layout.Footer)
 Vue.component(Layout.Sider.name, Layout.Sider)
 Vue.component(Layout.Content.name, Layout.Content)
-// Vue.component(List.name, List)
+Vue.component(List.name, List)
+Vue.component(List.Item.name, List.Item)
+Vue.component(List.Item.Meta.name, List.Item.Meta)
 Vue.component(LocaleProvider.name, LocaleProvider)
 Vue.component(Menu.name, Menu)
 Vue.component(Menu.Item.name, Menu.Item)
diff --git a/site/demo.js b/site/demo.js
index 23694d32d..3e028ce0f 100644
--- a/site/demo.js
+++ b/site/demo.js
@@ -46,3 +46,4 @@ export { default as tree } from 'antd/tree/demo/index.vue'
 export { default as layout } from 'antd/layout/demo/index.vue'
 export { default as form } from 'antd/form/demo/index.vue'
 export { default as anchor } from 'antd/anchor/demo/index.vue'
+export { default as list } from 'antd/list/demo/index.vue'
diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap
index 9adcc0cc1..be8c0de07 100644
--- a/tests/__snapshots__/index.test.js.snap
+++ b/tests/__snapshots__/index.test.js.snap
@@ -29,6 +29,7 @@ Array [
   "Input",
   "InputNumber",
   "Layout",
+  "List",
   "LocaleProvider",
   "Menu",
   "Modal",