Vue渲染函数实战

记录使用VUE渲染函数&JSX 进行对element ui进行二次封装 实战

  1. 最近项目中由于table表格中列很多,并且都是动态的(展示字段由后端返回多少就展示多少),所以就出现一个问题,当列中有很多需要文本溢出打点展示的字段时,就会导致页面由于dom元素过多渲染耗时很久(2s-3s多)。所以对它进行了优化。
  2. 优化第一版:
    (1).列表分批次渲染
    (2).element-ui el-popover 组件的弃用(改成使用html原生标签的title属性,来展示文案溢出时的所有文案)
    问题:
    (1).进行上述优化后,发现原生的title展示内容,无法复制(鼠标移开立马就消失无法选中文案进行复制)
    解决方案:
    (1).所以就想到写了一个公共方法,实现用户双击内容自动复制,方法如下:
// 使用到了vue-clipboard2 插件
// copyText.js
export default = {
     
	install(vue){
     
		vue.prototype.handleCopyContent = (value) =>{
     
			vue.$copyText(value.replace(/(^\s*)|(\s*$)/g, '')).then(
			    function (e) {
     
			      Message({
     
			        message: '复制成功',
			        type: 'success',
			      })
			    },
			    function (e) {
     
			      Message({
     
			        message: '复制失败',
			        type: 'error',
			      })
			    }
			 )
		}
	}
}
// main.js 
import VueClipboard from 'vue-clipboard2'
import copyText from './copyText.js'
Vue.use(VueClipboard)
Vue.use(copyText)

使用:this.handleCopyContent(text)
3. 优化第二版
(1). 由于优化第一版之后,发现由于列表可能会有其他操作按钮,或者用户自己比较经常点击列表,会经常误触发复制功能,用户体验不太好。所以就优化了第二个版本,支持右键全选文案,再复制。
这里我选择直接对 element-ui el-table-column 组件进行二次封装,这样就不需要每次都再使用的地方去调用全局方法,并且使用的是render函数的写法,因为这样写起来更加灵活,代码如下:

// m-table-column.js
import Vue from 'vue'
import store from '@/store'


const MTableColumn = Vue.component('m-table-column', {
     
  functional: true, // 函数式组件
  render(h, context) {
     
    if (context.data.attrs) {
      // 当前组件标签上的html属性   例如: 

则attrs为 {id:'test',title:'123'} context.data.attrs.align = 'left' // 此处设置默认为左对齐 } else { context.data.attrs = { align: 'left' } } if (context.props.label === '序号') { // 为序号列增加表头 context.data.attrs.label = '序号' } else { //为其他列 增加右键选中复制功能 context.data.copyStutas = true const handleContext = function (e) { if (context.data.copyStutas) { e.preventDefault() //取消默认事件 context.data.copyStutas = false } else { context.data.copyStutas = true } let text if (window.getSelection) { //window.getSelection 方法会返回一个选中对象 具体自行百度 text = window.getSelection() } else if (document.selection) { // document.selection 获取选中dom节点 createRange 方法是创建一个选中光标 此处为了兼容所有浏览器 text = document.selection.createRange() } text.selectAllChildren(e.currentTarget) // 选中所有的内容 document.execCommand('Copy') // 将选中的内容增加到复制板中 execCommand 方法是执行一个对当前文档/当前选择/给出范围的命令 该方法非常强大,有很多作用。具体自行百度 } const filterDictDataName = (conf_key, dict_key) => { let idcArr = store.getters.dataList[dict_key] let curItem = idcArr && idcArr.find(item => item.conf_key == conf_key) return curItem ? curItem['conf_value'] : '' } // 数据字典过滤 const t_dataListFieltter = (code, data) => { let result = null; switch (typeof data) { case 'string': case 'number': result = filterDictDataName(data, code) return result || '-' case 'object': result = store.state.user.dataList[code].filter(item => data.includes(item.conf_key)) return result ? result.map(item => item.conf_value).join(',') : '-' } } // 是否有传入dictCode 属性 有则使用传入的dictCode 进行数据字典配对 let dictCode = context.data.attrs.dictCode // 传入的title let propTitle = context.data.attrs.title // 传递给子组件作用域插槽处理 if (context.data.scopedSlots && context.data.scopedSlots.default) { const scopedSlots = context.data.scopedSlots.default context.data.scopedSlots.default = (props) => { let text = '' if (propTitle) text = propTitle else { // 出现嵌套字段 eg: 列表取值为 scope.row.logData.hotName 暂时只支持1级嵌套 if (props.column.property.includes('.')) { let filedArr = props.column.property.split('.') text = dictCode ? t_dataListFieltter(dictCode, props.row[filedArr[0]][filedArr[1]]) : props.row[filedArr[0]][filedArr[1]] } else { text = dictCode ? t_dataListFieltter(dictCode, props.row[props.column.property]) : props.row[props.column.property] } } const fontLenPx = text ? String(text).length * 12 : 0 const hasTitle = (fontLenPx > (props.column.realWidth - 12)) return h('div', { on: { contextmenu: handleContext }, style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', width: '100%' }, attrs: { title: hasTitle ? text : '' } }, scopedSlots(props)) // 将作用域插槽内的数据传入 } } else { if (!context.data.scopedSlots) context.data.scopedSlots = { } context.data.scopedSlots.default = (props) => { let text = '' if (propTitle) text = propTitle else { // 出现嵌套字段 eg: 列表取值为 scope.row.logData.hotName if (props.column.property.includes('.')) { let filedArr = props.column.property.split('.') text = dictCode ? t_dataListFieltter(dictCode, props.row[filedArr[0]][filedArr[1]]) : props.row[filedArr[0]][filedArr[1]] } else { text = dictCode ? t_dataListFieltter(dictCode, props.row[props.column.property]) : props.row[props.column.property] } } const fontLenPx = text ? String(text).length * 12 : 0 const hasTitle = (fontLenPx > (props.column.realWidth - 12)) return h('div', { on: { contextmenu: handleContext }, style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', width: '100%' }, attrs: { title: hasTitle ? text : '' } }, props.row[props.column.property]) // 将作用域插槽内的数据传入 } } } return h( 'el-table-column', context.data, // 将当前组件的所有数据 事件 等透传到 el-table-column 组件内 context.children // 将当前组件内的匿名插槽 传到 el-table-column 组件 ) } }) export default MTableColumn // main.js import MTableColumn from './m-table-column.js' Vue.component('m-table-column',MTableColumn) // xx.vue // 使用:与el-table-column 组件一样的用法 <m-table-column></m-table-column>

你可能感兴趣的:(Vue)