1.概述
学习element-ui的源码能很好的提升vue的水平,但是element-ui中el-table的源码很复杂,并且组件的写法用的是render函数方法,而不是vue单文件,这让阅读它源码变得很困难,所以我在学习的时候,为了掌握el-table的核心思想,我做了2件事,第1件,对它的代码进行简化,删除太多细节代码.第2件,写一个html把所有代码放在html中运行
2.目标
实现下面el-table组件的使用,包含通过prop显示列,通过template自定义列
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js"></script>
</head>
<body>
<div id="app">
<el-table :data="tableData">
<el-table-column prop="name" label="姓名"></el-table-column>
<el-table-column label="地址">
<template slot-scope="scope">{{scope.row.address}}</template>
</el-table-column>
</el-table>
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
tableData: [{
name: '张三',
address: '成都市青羊区清源路1号'
}, {
name: '李四',
address: '成都市青羊区清源路2号'
}]
}
})
</script>
3.步骤
3.1 定义状态管理器
仿造vuex的结构,创建一个TableStore类,定义commit方法和mutations对象
// 状态管理器
class TableStore {
constructor(){
this.states = {
data: null, //table的数据
columns: [] //table的列定义
}
}
commit(name, ...args){//调用mutations
this.mutations[name].apply(this, [this.states].concat(args))
}
}
//mutations的定义
TableStore.prototype.mutations = {
//设置table的数据
setData(states, data) {
states.data = data
},
//插入列定义
insertColumn(states, column) {
states.columns.push(column)
}
}
3.2 定义el-table组件
负责:
- 初始化状态管理器
- 通过默认插槽,接受table-column组件
- 使用table-header组件和table-body组件
Vue.component('el-table',{
template: `<div class="el-table">
<!-- 隐藏列: slot里容纳table-column -->
<div class="hidden-columns">
<slot></slot>
</div>
<!-- 表头 -->
<div class="el-table__header-wrapper">
<!--表头组件-->
<table-header :store="store"></table-header>
</div>
<!-- 表体 -->
<div class="el-table__body-wrapper">
<!--表体组件-->
<table-body :store="store"></table-body>
</div>
</div>`,
props: ['data'],//table数据
data(){
return {
store: new TableStore() //状态管理器
}
},
watch: {
data: {
immediate: true,
handler(value) {
// 将data添加到状态管理器中,供 table-body 使用
this.store.commit('setData', value)
}
}
}
})
3.3 定义el-table-column组件
负责:生成列定义(包含表头名称,列字段名,渲染方法),放到状态管理器中,提供给table-header组件和table-body组件使用
Vue.component('el-table-column',{
template: `<div></div>`,
props: ['label', 'prop'],
computed: {
owner() {// 寻找拥有table的外层组件
return this.$parent;
}
},
created() {
// 生成列定义
let column = {
label: this.label,//列表头显示名称
property: this.prop,//列用到的字段名称
renderCell: null//渲染用的方法
};
let renderCell = column.renderCell;
let _self = this;
// 生成列的渲染方法
column.renderCell = function (createElement, data) {
// 有插槽的情况
if (_self.$scopedSlots.default) {
//渲染作用域插槽
renderCell = () => _self.$scopedSlots.default(data);
//使用效果:
//<template slot-scope="{row}">
//<span>{{row.address}}</span>
//</template>
}else{
// 没有插槽的情况
renderCell = function () {
let { row } = data
let property = column.property;
// 直接返回时间紧
return row[property]
}
/*实现效果:<div className="cell">张三</div>*/
}
//生成一个render函数
return createElement('div', {
'class': {
cell: true
}
},renderCell())
}
//生成列定义
this.columnConfig = column
},
mounted(){
// 将列定义添加到状态管理器中,供 table-body table-header 使用
this.owner.store.commit('insertColumn', this.columnConfig)
}
})
3.4定义table-header组件
负责根据状态管理器中列定义,渲染列表的表头
Vue.component('table-header',{
props: ['store'],
computed: {
columns() { //获取状态管理器中的列定义
return this.store.states.columns;
}
},
render(createElement) { //通过createElement创建vNode
/*
效果:
<table class="el-table__header">
<thead>
<th><div>姓名</div></th>
<th><div>地址</div></th>
</thead>
</table>
*/
return createElement('table',{class: {'el-table__header': true}}, [
createElement('thead',
this.columns.map(column=>{
return createElement('th',[createElement('div',column.label)])
})
)
])
}
})
3.5 定义table-body组件
负责通过状态管理器中的列定义和数据,渲染表体数据
// table-body组件
Vue.component('table-body',{
props: ['store'],
computed: {
data() { //获取状态管理器中的列表数据
return this.store.states.data;
},
columns() { //获取状态管理器中的列定义
return this.store.states.columns;
}
},
render(createElement) { //通过createElement创建vNode
/*
效果:
<table class="el-el-table__body">
<tr>
<td>...</td>
</tr>
</table>
*/
return createElement('table',{class: {'el-el-table__body': true}},
this.data.map((row)=>{
return createElement('tr', this.columns.map(column=>{
return createElement('td',[column.renderCell.call(null,createElement,{row})])
}))
})
)
},
})
4 效果和完整代码
预览
5.进一步学习
vue对象的render方法学习
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论