ant-design-vue的使用及常见问题

ant-design-vue的使用及常见问题

2020年07月15日 阅读:350 字数:1697 阅读时长:4 分钟

ant-design-vue按需引入组件、图标,定制主题以及表格高度自适应

盘一下那些使用ant-design-vue组件库的经验和常见问题,包括v1、v3版本

1. v1版本

使用的是ant-design-vue@1.7.8 + vue@2.6.14 + vue-cli@5

1.1. 按需引入

使用按需引入插件,"babel-plugin-import": "^1.13.3"

1.1.1. 组件按需引入

修改babel.config.js文件,配置 babel-plugin-import

module.exports = {
  plugins: [
    [
      'import',
      {
        'libraryName': 'ant-design-vue',
        'libraryDirectory': 'es',
        'style': true // `style: true` 会加载 less 文件
      }
    ]
  ]
}

src/vendor/ant-design.js

import Vue from 'vue'

import {
  // Base,
  // Affix,
  // Anchor,
  // AutoComplete,
  Alert,
  // Avatar,
  // BackTop,
  // Badge,
  Breadcrumb,
  Button,
  // Calendar,
  Card,
  // Collapse,
  // Carousel,
  Cascader,
  Checkbox,
  Col,
  DatePicker,
  Divider,
  Dropdown,
  // Form,
  FormModel,
  Icon,
  Input,
  InputNumber,
  // Layout,
  List,
  // LocaleProvider,
  message,
  Menu,
  // Mentions,
  Modal,
  notification,
  Pagination,
  // Popconfirm,
  // Popover,
  // Progress,
  Radio,
  // Rate,
  Row,
  Select,
  // Slider,
  Spin,
  // Statistic,
  Steps,
  // Switch,
  Table,
  Transfer,
  Tree,
  // TreeSelect,
  Tabs,
  Tag,
  // TimePicker,
  // Timeline,
  Tooltip,
  Upload,
  // Drawer,
  // Skeleton,
  // Comment,
  ConfigProvider,
  Empty,
  // Result,
  // Descriptions,
  // PageHeader,
  Space,
  version
} from 'ant-design-vue'

const components = [
  // Base,
  // Affix,
  // Anchor,
  // AutoComplete,
  Alert,
  // Avatar,
  // BackTop,
  // Badge,
  Breadcrumb,
  Button,
  // Calendar,
  Card,
  // Collapse,
  // Carousel,
  Cascader,
  Checkbox,
  Col,
  DatePicker,
  Divider,
  Dropdown,
  // Form,
  FormModel,
  Icon,
  Input,
  InputNumber,
  // Layout,
  List,
  // LocaleProvider,
  Menu,
  // Mentions,
  Modal,
  Pagination,
  // Popconfirm,
  // Popover,
  // Progress,
  Radio,
  // Rate,
  Row,
  Select,
  // Slider,
  Spin,
  // Statistic,
  Steps,
  // Switch,
  Table,
  Transfer,
  Tree,
  // TreeSelect,
  Tabs,
  Tag,
  // TimePicker,
  // Timeline,
  Tooltip,
  Upload,
  // Drawer,
  // Skeleton,
  // Comment,
  ConfigProvider,
  Empty,
  // Result,
  // Descriptions,
  // PageHeader,
  Space
]

export default {
  /**
   * Vue use注册
   * @param {Object} Vue
   */
  install: function() {
    components.map(function(component) {
      Vue.use(component)
    })

    Vue.prototype.$message = message
    Vue.prototype.$notification = notification
    Vue.prototype.$info = Modal.info
    Vue.prototype.$success = Modal.success
    Vue.prototype.$error = Modal.error
    Vue.prototype.$warning = Modal.warning
    Vue.prototype.$confirm = Modal.confirm
    Vue.prototype.$destroyAll = Modal.destroyAll
  },
  version
}

process.env.NODE_ENV !== 'production' && console.warn('[Antd] Antd使用按需加载, 若缺失, 请到此处修改.')

在main.js中引入

import Antdv from '@/vendor/ant-design'

Vue.use(Antdv)

1.1.2. 图标按需引入

处理1.2.0 之后全量引入了所有图标,bundle体积过大的问题

大概思路是自己搞一个icon引入文件,用别名把icon的默认库替换一下

如果打包报错:查看alias配置以及ant-icons.js导出是否正确

vue.config.js

module.exports = {
  configureWebpack: {
    resolve: {
      alias: {
        '@': resolve('src'),
        // 大概思路是自己搞一个icon引入文件,用别名把icon的默认库替换一下
        // @see https://github.com/ant-design/ant-design/issues/12011
        '@ant-design/icons/lib/dist$': resolve('./src/vendor/ant-icons.js')
      }
    }
  }
}

src/vendor/ant-icons.js

// export what antd other components need
export { default as CloseOutline } from '@ant-design/icons/lib/outline/CloseOutline'
export { default as CheckOutline } from '@ant-design/icons/lib/outline/CheckOutline'
export { default as LoadingOutline } from '@ant-design/icons/lib/outline/LoadingOutline'
export { default as CheckCircleOutline } from '@ant-design/icons/lib/outline/CheckCircleOutline'
export { default as InfoCircleOutline } from '@ant-design/icons/lib/outline/InfoCircleOutline'
export { default as CloseCircleOutline } from '@ant-design/icons/lib/outline/CloseCircleOutline'
export { default as ExclamationCircleOutline } from '@ant-design/icons/lib/outline/ExclamationCircleOutline'
export { default as CheckCircleFill } from '@ant-design/icons/lib/fill/CheckCircleFill'
export { default as InfoCircleFill } from '@ant-design/icons/lib/fill/InfoCircleFill'
export { default as CloseCircleFill } from '@ant-design/icons/lib/fill/CloseCircleFill'
export { default as ExclamationCircleFill } from '@ant-design/icons/lib/fill/ExclamationCircleFill'
export { default as UpOutline } from '@ant-design/icons/lib/outline/UpOutline'
export { default as DownOutline } from '@ant-design/icons/lib/outline/DownOutline'
export { default as LeftOutline } from '@ant-design/icons/lib/outline/LeftOutline'
export { default as RightOutline } from '@ant-design/icons/lib/outline/RightOutline'
export { default as RedoOutline } from '@ant-design/icons/lib/outline/RedoOutline'
export { default as CalendarOutline } from '@ant-design/icons/lib/outline/CalendarOutline'
export { default as SearchOutline } from '@ant-design/icons/lib/outline/SearchOutline'
export { default as BarsOutline } from '@ant-design/icons/lib/outline/BarsOutline'
export { default as StarFill } from '@ant-design/icons/lib/fill/StarFill'
export { default as FilterOutline } from '@ant-design/icons/lib/outline/FilterOutline'
export { default as CaretUpOutline } from '@ant-design/icons/lib/outline/CaretUpOutline'
export { default as CaretDownOutline } from '@ant-design/icons/lib/outline/CaretDownOutline'
export { default as PlusOutline } from '@ant-design/icons/lib/outline/PlusOutline'
export { default as FileOutline } from '@ant-design/icons/lib/outline/FileOutline'
export { default as FolderOpenOutline } from '@ant-design/icons/lib/outline/FolderOpenOutline'
export { default as FolderOutline } from '@ant-design/icons/lib/outline/FolderOutline'
export { default as PaperClipOutline } from '@ant-design/icons/lib/outline/PaperClipOutline'
export { default as PictureOutline } from '@ant-design/icons/lib/outline/PictureOutline'
export { default as EyeOutline } from '@ant-design/icons/lib/outline/EyeOutline'
export { default as DeleteOutline } from '@ant-design/icons/lib/outline/DeleteOutline'
export { default as UploadOutline } from '@ant-design/icons/lib/outline/uploadOutline'
export { default as EllipsisOutline } from '@ant-design/icons/lib/outline/EllipsisOutline'

// export what you need
// @see node_modules/@ant-design/icons/lib/index.es.js
export { default as LockOutline } from '@ant-design/icons/lib/outline/LockOutline'
export { default as InboxOutline } from '@ant-design/icons/lib/outline/InboxOutline'
export { default as UserOutline } from '@ant-design/icons/lib/outline/UserOutline'
export { default as RollbackOutline } from '@ant-design/icons/lib/outline/RollbackOutline'
export { default as PlusSquareOutline } from '@ant-design/icons/lib/outline/PlusSquareOutline'
export { default as MinusSquareOutline } from '@ant-design/icons/lib/outline/MinusSquareOutline'
export { default as ArrowDownOutline } from '@ant-design/icons/lib/outline/ArrowDownOutline'
export { default as EditOutline } from '@ant-design/icons/lib/outline/EditOutline'

process.env.NODE_ENV !== 'production' && console.warn('[Antd] Icon使用按需加载, 若缺失, 请到此处修改.')

1.2. 自定义主题色

vue.config.js

module.exports = {
  css: {
    loaderOptions: {
      less: {
        lessOptions: {
          // If you are using less-loader@5 please spread the lessOptions to options directly
          modifyVars: {
            'primary-color': '#1FCAC4'
          },
          javascriptEnabled: true
        }
      }
    }
  }
}

1.3. 表格高度自适应

使用vue自定义指令实现

const install = function(Vue) {
  Vue.directive('ant-height-adaptive-table', adaptive)
}

if (window.Vue) {
  window['ant-height-adaptive-table'] = adaptive
  Vue.use(install); // eslint-disable-line
}

/**
 * How to use
 * <el-table height="100px" v-el-height-adaptive-table="{bottomOffset: 30}">...</el-table>
 * el-table height is must be set
 * bottomOffset: 30(default)   // The height of the table from the bottom of the page.
 */

const doResize = (el, binding, vnode) => {
  const { componentInstance: $table } = vnode

  const { value } = binding

  const bottomOffset = (value && value.bottomOffset) || 30

  if (!$table) return

  const $tableHead = el.querySelector('.ant-table-thead') || { clientHeight: 0 }
  const top = el.getBoundingClientRect().top

  if (top === 0) return

  const height = window.innerHeight - top - $tableHead.clientHeight - bottomOffset
  setTimeout(() => {
    // 设置table/list的高度
    const $tableBody = el.querySelector('.ant-table-body')
    if (!$tableBody) return
    $tableBody.style.height = height + 'px'
    binding.value.setHeight(height)
  }, 0)
}

export default {
  bind(el, binding, vnode) {
    el.resizeListener = debounce(() => {
      doResize(el, binding, vnode)
    }, 100)
    // parameter 1 is must be "Element" type
    window.addEventListener('resize', el.resizeListener)
    vnode.componentInstance.$handleResize = function() {
      return doResize(el, binding, vnode)
    }
  },
  inserted(el, binding, vnode) {
    doResize(el, binding, vnode)
  },
  unbind(el) {
    window.removeEventListener('resize', el.resizeListener)
  },
  install
}

在组件中使用

<template>
    <a-table
      v-ant-height-adaptive-table="{bottomOffset: 105, setHeight: handleResizeTable}"
      :scroll="{y: scrollY}"
    />
</template>

<script>
import antHeightAdaptiveTable from '@/directive/ant-table'

export default {
    data() {
        return {
            scrollY: 300
        }
    },
    directives: {
    antHeightAdaptiveTable
  },
  methods: {
    /**
     * 响应式设置表格高度
     * @param {Number} height 表格高度
     */
    handleResizeTable(height) {
      this.scrollY = height
    }
  }
}
</script>

1.4. 动态主题

不像v3版本提供了 CSS Variable 版本以支持动态切换主题能力,v1需要用到webpack-theme-color-replacer插件,通过动态生成引入主题色的CSS规则来达到样式的覆盖。

详情可以参考:vueComponent/ant-design-vue-pro

也可以查看另一篇文章:webpack下的动态主题实现方案 - Timeless's博客 (timelessq.com)

2. v3版本

使用的是ant-design-vue@3.2.15 + vue@3.2.45 + vite@4

2.1. 按需引入

需要用到vite插件:unplugin-vue-components

vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    Components({
      resolvers: [
        AntDesignVueResolver({
          resolveIcons: true
        })
      ],
      dts: './src/components.d.ts'
    })
  ]
})

2.2. 表格高度自适应

和v1版本类似,也是使用自定义指令,但是vue3的写法不太一样

import { debounce } from 'lodash'
import type { DirectiveBinding, VNodeTypes } from 'vue'

interface DirectiveElement extends HTMLElement {
  resizeListener: any
}

/**
 * How to use
 * <el-table height="100px" v-el-height-adaptive-table="{bottomOffset: 30}">...</el-table>
 * el-table height is must be set
 * bottomOffset: 30(default)   // The height of the table from the bottom of the page.
 */
const doResize = (el: HTMLElement, binding: DirectiveBinding, vnode: VNodeTypes) => {
  const { value } = binding

  const bottomOffset = (value && value.bottomOffset) || 65
  const $header = el.querySelector('.ant-table-thead') || {
    clientHeight: 0
  }

  const top = el.getBoundingClientRect().top
  if (top === 0) return

  let height = window.innerHeight - top - $header.clientHeight - bottomOffset
  height = height < 200 ? 200 : height
  setTimeout(() => {
    // 设置table/list的高度
    const $body = (el.querySelector('.ant-table-body') ||
      el.querySelector('.ant-list .ant-spin-nested-loading') ||
      el.querySelector('.ant-list .ant-row')) as HTMLElement
    if (!$body) return

    $body.style.height = Math.floor(height) + 'px'
    binding.value.setHeight(Math.floor(height))
  }, 0)
}

export default {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el: DirectiveElement, binding: DirectiveBinding, vnode: VNodeTypes) {
    el.resizeListener = debounce(() => {
      doResize(el, binding, vnode)
    }, 100)
    // parameter 1 is must be "Element" type
    window.addEventListener('resize', el.resizeListener)
  },
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el: DirectiveElement, binding: DirectiveBinding, vnode: VNodeTypes) {
    doResize(el, binding, vnode)
  },
  /**
   * 在绑定元素的父组件
   * 及他自己的所有子节点都更新后调用
   * @summary 函数防抖处理因组件尺寸变化,视图未改变完成,就触发计算高度不正确的问题
   */
  updated: debounce((el, binding, vnode) => {
    doResize(el, binding, vnode)
  }, 100),
  // 绑定元素的父组件卸载后调用
  unmounted(el: DirectiveElement) {
    window.removeEventListener('resize', el.resizeListener)
  }
}

推荐阅读

恰饭区

评论区 (0)

0/500

还没有评论,快来抢第一吧