We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
最近在做vue高仿网易云音乐的项目,在做的过程中发现音乐表格这个组件会被非常多的地方复用,而且需求比较复杂的和灵活。
它需要支持:
hideColumns参数, 自定义需要隐藏哪些列。
highLightText,传入字符串,数据中命中的字符串高亮。
首先 看一下我们平常的table写法。
<el-table :data="tableData" style="width: 100%"> <el-table-column prop="index" label=" " width="180"> </el-table-column> <el-table-column prop="name" label="音乐标题" width="180"> </el-table-column> <el-table-column prop="artistsText" label="歌手"> </el-table-column> </el-table>
这是官网的写法,假设我们传入了 hideColumns: ['index', 'name'],我们需要在模板里隐藏的话```
<el-table :data="tableData" style="width: 100%"> <el-table-column +++ v-if="!hideColumns.includes('index')" prop="index" label=" " width="180"> </el-table-column> <el-table-column +++ v-if="!hideColumns.includes('name')" prop="name" label="音乐标题" width="180"> </el-table-column> <el-table-column +++ v-if="!hideColumns.includes('address')" prop="artistsText" label="歌手"> </el-table-column> </el-table>
这种代码非常笨,所以我们肯定是接受不了的,我们很自然的联想到平常用v-for循环,能不能套用在这个需求上呢。 首先在data里定义columns
data() { return { columns: [{ prop: 'index', label: '', width: '50' }, { prop: 'artistsText', label: '歌手' }, { prop: 'albumName', label: '专辑' }, { prop: 'durationSecond', label: '时长', width: '100', }] } }
然后我们在computed中计算hideColumns做一次合并
computed: { showColumns() { const { hideColumns } = this return this.columns.filter(column => { return !this.hideColumns.find((prop) => prop === column.prop) }) }, },
那么模板里我们就可以简写成
<el-table :data="songs" > <template v-for="(column, index) in showColumns"> <el-table-column :key="index" // 混入属性 v-bind="column" > </el-table-column> </template> </el-table>
注意 v-bind="column"这行, 相当于把column中的所有属性混入到table-column中去,是一个非常简便的方法。
v-bind="column"
这样需求看似解决了,很美好。 但是我们忘了非常重要的一点,slotScopes这个东西!
slotScopes
比如音乐时长我们需要format一下,
<el-table-column> <template> <span>{{ $utils.formatTime(scope.row.durationSecond) }}</span> </template> </el-table-column>
但是我们现在把columns都写到script里了,和template分离开来了,我暂时还不知道有什么方法能把sciprt里写的模板放到template里用,所以先想到一个可以解决问题的方法。就是在template里加一些判断。
sciprt
template
<el-table v-bind="$attrs" v-if="songs.length" :data="songs" @row-click="onRowClick" :cell-class-name="tableCellClassName" style="width: 99.9%" > <template v-for="(column, index) in showColumns"> <!-- 需要自定义渲染的列 --> <el-table-column v-if="['durationSecond'].includes(column.prop)" :key="index" v-bind="column" > <!-- 时长 --> <template v-else-if="column.prop === 'durationSecond'"> <span>{{ $utils.formatTime(scope.row.durationSecond) }}</span> </template> </el-table-column> <!-- 普通列 --> <el-table-column v-else :key="index" v-bind="column" > </el-table-column> </template> </el-table>
又一次的需求看似解决了,很美好。
鸡你太美这个歌名,我们在搜索框输入鸡你 我们需要把
鸡你太美
鸡你
<span>鸡你太美</span>
转化为
<span> <span class="high-light">鸡你</span> 太美 </span>
我们在template里找到音乐标题这行,写下这端代码:
<template v-else-if="column.prop === 'name'"> <span>{{this.genHighlight(scope.row.name)}}</span> </template>
methods: { genHighlight(text) { return <span>xxx</span> } }
我发现无从下手了, 因为jsx最终编译成的是return vnode的方法,genHighlight执行以后返回的是vnode,但是你不能直接把vnode放到template里去。
所以我们要统一环境,直接使用jsx渲染我们的组件,文档可以参照 babel-plugin-transform-vue-jsx vuejs/jsx
import ElTable from 'element-ui/lib/table' data() { const commonHighLightSlotScopes = { scopedSlots: { default: (scope) => { return ( <span>{this.genHighlight(scope.row[scope.column.property])}</span> ) } } } return { columns: [{ prop: 'name', label: '音乐标题', ...commonHighLightSlotScopes }, { prop: 'artistsText', label: '歌手', ...commonHighLightSlotScopes }, { prop: 'albumName', label: '专辑', ...commonHighLightSlotScopes }, { prop: 'durationSecond', label: '时长', width: '100', scopedSlots: { default: (scope) => { return ( <span>{this.$utils.formatTime(scope.row.durationSecond)}</span> ) } } }] } }, methods: { genHighlight(title = '') { ...省去一些细节 const titleSpan = matchIndex > -1 ? ( <span> {beforeStr} <span class="high-light-text">{hitStr}</span> {afterStr} </span> ) : title; return titleSpan; }, }, render() { const elTableProps = ElTable.props // 从$attrs里提取作为prop的值 // 这里要注意的点是驼峰命名法(camelCase)和短横线命名法(kebab-case) // 都是可以被组件接受的,虽然elTable里的prop定义的属性叫cellClassName // 但是我们要把cell-class-name也解析进prop里 const { props, attrs } = genPropsAndAttrs(this.$attrs, elTableProps) const tableAttrs = { attrs, on: { ...this.$listeners, ['row-click']: this.onRowClick, }, props: { ...props, cellClassName: this.tableCellClassName, data: this.songs, }, style: { width: '99.9%' } } return this.songs.length ? ( <el-table {...tableAttrs} > {this.showColumns.map((column, index) => { const { scopedSlots, ...columnProps } = column return ( <el-table-column key={index} props={columnProps} scopedSlots={scopedSlots} > </el-table-column> ) })} </el-table> ) : null }
attrs: this.$attrs 注意这句话,我们在template里可以通过 v-bind="$attrs"去透传外部传进来的所有属性, 但是在jsx中我们必须分类清楚传给el-table的attrs和props 比如el-table接受data这个prop,如果你放在attrs里传进去,那么就失效了。
attrs: this.$attrs
v-bind="$attrs"
attrs
props
data
这个我暂时也没找到特别好的解决方法,只能先引用去拿elTable上的props去进行比对$attrs,取交集。
import ElTable from 'element-ui/lib/table' // 从$attrs里提取作为prop的值 // 这里要注意的点是驼峰命名法(camelCase)和短横线命名法(kebab-case) // 都是可以被组件接受的,虽然elTable里的prop定义的属性叫cellClassName // 但是我们要把cell-class-name也解析进prop里 const { props, attrs } = genPropsAndAttrs(this.$attrs, elTableProps)
可以看到代码中模板的部分少了很多重复的判断,维护性和扩展性都更强了,jsx可以说是复杂组件的终极解决方案,但是要真正的封装好一个高阶组件,要做的还非常多。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
背景介绍
最近在做vue高仿网易云音乐的项目,在做的过程中发现音乐表格这个组件会被非常多的地方复用,而且需求比较复杂的和灵活。
预览地址
源码地址
图片预览
需求分析
它需要支持:
hideColumns参数, 自定义需要隐藏哪些列。
highLightText,传入字符串,数据中命中的字符串高亮。
首先 看一下我们平常的table写法。
这是官网的写法,假设我们传入了 hideColumns: ['index', 'name'],我们需要在模板里隐藏的话```
这种代码非常笨,所以我们肯定是接受不了的,我们很自然的联想到平常用v-for循环,能不能套用在这个需求上呢。
首先在data里定义columns
然后我们在computed中计算hideColumns做一次合并
那么模板里我们就可以简写成
注意
v-bind="column"
这行, 相当于把column中的所有属性混入到table-column中去,是一个非常简便的方法。script配合template的解决方案
这样需求看似解决了,很美好。
但是我们忘了非常重要的一点,
slotScopes
这个东西!比如音乐时长我们需要format一下,
但是我们现在把columns都写到script里了,和template分离开来了,我暂时还不知道有什么方法能把
sciprt
里写的模板放到template
里用,所以先想到一个可以解决问题的方法。就是在template里加一些判断。又一次的需求看似解决了,很美好。
高亮文字匹配需求分析
但是新需求又来了!!根据传入的 highLightText 去高亮某些文字,我们分析一下需求
鸡你太美
这个歌名,我们在搜索框输入鸡你
我们需要把
转化为
我们在template里找到音乐标题这行,写下这端代码:
我发现无从下手了, 因为jsx最终编译成的是return vnode的方法,genHighlight执行以后返回的是vnode,但是你不能直接把vnode放到template里去。
jsx终极解决方案
所以我们要统一环境,直接使用jsx渲染我们的组件,文档可以参照
babel-plugin-transform-vue-jsx
vuejs/jsx
attrs: this.$attrs
注意这句话,我们在template里可以通过v-bind="$attrs"
去透传外部传进来的所有属性,但是在jsx中我们必须分类清楚传给el-table的
attrs
和props
比如el-table接受
data
这个prop,如果你放在attrs里传进去,那么就失效了。这个我暂时也没找到特别好的解决方法,只能先引用去拿elTable上的props去进行比对$attrs,取交集。
可以看到代码中模板的部分少了很多重复的判断,维护性和扩展性都更强了,jsx可以说是复杂组件的终极解决方案,但是要真正的封装好一个高阶组件,要做的还非常多。
The text was updated successfully, but these errors were encountered: