vue3新擬態(tài)組件庫開發(fā)流程之table組件源碼分析
基礎表格
首先開發(fā)table組件之前,先想好要用什么樣式的api,因為筆者在生產(chǎn)工作中用的都是element,所以前面幾個組件風格和element類似,但是這次不打算用element的風格了,打算換一種,直接展示:
我們期望用戶這樣使用:
<script setup>
const dataList = [
{
id: 1,
name: '《JavaEE企業(yè)應用實戰(zhàn)》',
author: 'dev1ce',
price: '10.22',
desc: '書中最后講解的項目案例,涵蓋從前期設計到最終實施的整個過程,對全書知識點進行串聯(lián)和鞏固,使讀者融會貫通,掌握Java Web開發(fā)的精髓。'
},
{
id: 2,
name: '《代碼整潔之道》',
author: 'R0bert',
price: '10.22',
desc: '整潔代碼并非遵循一組規(guī)則編寫的。不可能因為學習一套金規(guī)玉律就成為軟件大師。專業(yè)精神和手工藝來自于推動規(guī)則形成的價值。'
},
{
id: 3,
name: '《ECMAScript 6 入門教程》',
author: 'y1feng',
price: '10.22',
desc: '本書是一本開源的 JavaScript 語言教程,全面介紹 ECMAScript 6 新引入的語法特性。'
},
]
const columnsList = [
{
title: '書名',
key: 'name'
},
{
title: '作者',
key: 'author'
},
{
title: '價格',
key: 'price'
},
{
title: '簡介',
key: 'desc'
}
]
</script>
<template>
<sanorin-table :columns="columnsList" :data="dataList"/>
</template>依照這個寫出以下代碼
<script setup>
import { ref, computed } from 'vue'
import { useProp, useNeumorphism } from '../mixin/neumorphism'
const props = defineProps({
...useProp,
...{
data: { // dataList
type: Array,
default: () => [],
},
columns: { // columnsList
type: Array,
default: () => [],
},
}
})
const { baseStyleObject } = useNeumorphism(props)
let styleObject = computed(() => ({
// '--font-size': `${props.size-26}px`, '--line-height': `${props.size-20}px`, '--limit-size': `${props.size-28}px`
}))
</script>
<template>
<div :style="{...baseStyleObject,...styleObject}">
<table style="">
<thead>
<tr class="neumorphism">
<!-- 表頭循環(huán) -->
<th v-for="col in columns" :key="col.key">{{col.title}}</th>
</tr>
</thead>
<tbody>
<!-- 表體循環(huán) -->
<tr class="neumorphism" v-for="row in data" :key="row.id">
<td v-for="col in columns" :key="col.key">
<span>
{{row[col.key]}}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'sanorin-table',
}
</script>
<style scoped>
@import "../../style/index.css";
@import "../../style/neumorphism.css";
table {
width: 100%;
/* border-collapse: collapse; */
empty-cells: show;
border-collapse:separate;
border-spacing:0px 10px;
}
table td,
table th {
color: var(--text-color);
padding: 8px 16px 8px 16px;
text-align: left;
}
table th {
color: var(--text-back-color) !important;
font-weight: 600;
white-space: nowrap;
}
table tr{
margin-top: 20px;
}
</style>最后出來的效果就是:

然后實現(xiàn)了這個后我們開始做后面的,先從固定表頭開始。
固定表頭
固定表頭有三種方法,詳見我的另一篇文章http://www.dbjr.com.cn/article/91143.htm
這里先采用第一種,以后不能滿足需求了再改成后面的方法。
效果和代碼如下:

<script setup>
import { ref, computed } from 'vue'
import { useProp, useNeumorphism } from '../mixin/neumorphism'
const props = defineProps({
...useProp,
...{
data: { // dataList
type: Array,
default: () => [],
},
columns: { // columnsList
type: Array,
default: () => [],
},
}
})
const { baseStyleObject } = useNeumorphism(props)
let styleObject = computed(() => ({
}))
</script>
<template>
<div class="san-table scrollbar" :style="{...baseStyleObject,...styleObject}">
<table>
<thead>
<tr class="neumorphism">
<!-- 表頭循環(huán) -->
<th v-for="col in columns" :key="col.key">{{col.title}}</th>
</tr>
</thead>
<tbody>
<!-- 表體循環(huán) -->
<tr class="neumorphism" v-for="row in data" :key="row.id">
<td v-for="col in columns" :key="col.key">
<span>
{{row[col.key]}}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'sanorin-table',
}
</script>
<style scoped>
@import "../../style/index.css";
@import "../../style/neumorphism.css";
.san-table{
padding: 0px 20px 20px 20px;
height: 200px;
position: relative;
width: 700px;
overflow: auto;
}
table {
width: 100%;
table-layout: fixed;
empty-cells: show;
border-collapse:separate;
border-spacing:0px 10px;
}
thead {
position: sticky;
top: 10px;
}
thead:before{
position: absolute;
content: '';
width: calc(100% + var(--shadow-blur) * 2);
transform: translate(calc(var(--shadow-blur) * -1) , -10px);
height: 20px;
background-color: var(--main-color);
z-index: -1;
}
table td,
table th {
color: var(--text-color);
padding: 8px 16px 8px 16px;
text-align: left;
}
table th {
color: var(--text-back-color) !important;
font-weight: 600;
white-space: nowrap;
}
</style>高度/流體高度
可以為 Table 設置一個高度。(height)
當數(shù)據(jù)量動態(tài)變化時,可以為 Table 設置一個最大高度。(maxHeight) 通過設置max-height屬性為 Table 指定最大高度。此時若表格所需的高度大于最大高度,則會顯示一個滾動條。
只要在sanorin-table元素中定義了height或者maxHeight屬性,即可實現(xiàn)固定表頭的表格,而不需要額外的代碼。
代碼如下:
<script setup>
import { ref, computed, reactive } from 'vue'
import { useProp, useNeumorphism } from '../mixin/neumorphism'
const props = defineProps({
...useProp,
...{
data: { // dataList
type: Array,
default: () => [],
},
columns: { // columnsList
type: Array,
default: () => [],
},
height: {
type: Number,
},
maxHeight: {
type: Number,
}
}
})
// 高度設置
let tableHeightStyleObj = computed(() => {
let styleObj = ((e) => {
if (e.maxHeight) return { maxHeight: e.maxHeight + 'px' }
if (e.height) return { height: e.height + 'px' }
return {}
})({...props})
return styleObj
})
const { baseStyleObject } = useNeumorphism(props)
let styleObject = computed(() => ({
...tableHeightStyleObj.value
}))
</script>
<template>
<div class="san-table scrollbar" :style="{...baseStyleObject,...styleObject}">
<table>
<colgroup>
<col v-for="(col, index) in columns" :key="index">
</colgroup>
<thead>
<tr class="neumorphism">
<th v-for="col in columns" :key="col.key"> {{col.title}} </th>
</tr>
</thead>
<tbody>
<tr class="neumorphism" v-for="row in data" :key="row.id">
<td v-for="col in columns" :key="col.key"> {{row[col.key]}} </td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'sanorin-table',
}
</script>
<style scoped>
@import "../../style/index.css";
@import "../../style/neumorphism.css";
.san-table{
padding: 0px 20px 20px 20px;
position: relative;
width: 700px;
overflow: auto;
}
table {
width: 100%;
table-layout: fixed;
empty-cells: show;
border-collapse:separate;
border-spacing:0px 10px;
}
thead {
position: sticky;
top: 10px;
}
thead:before{
position: absolute;
content: '';
width: calc(100% + var(--shadow-blur) * 2);
transform: translate(calc(var(--shadow-blur) * -1) , -10px);
height: 20px;
background-color: var(--main-color);
z-index: -1;
}
table td,
table th {
color: var(--text-color);
padding: 8px 16px 8px 16px;
text-align: left;
}
table th {
color: var(--text-back-color) !important;
font-weight: 600;
white-space: nowrap;
}
</style>自定義列寬
接下來加入寬度控制,希望在columns 傳入的數(shù)組對象內(nèi)加入寬度,示例如下:
const columnsList = [
{
title: '書名',
key: 'name',
width: 100,
},
{
title: '作者',
key: 'author',
width: 100,
},
{
title: '價格',
key: 'price',
width: 100,
},
{
title: '簡介',
key: 'desc',
minWidth: 350,
}
]希望達到以下效果
1、含有width的列,寬度固定,不隨瀏覽器寬度變化而變化
2、含有minWidth的列,在大于設定值時,自動填充 table 剩余寬度,小于設定值時,固定該寬度
3、不包含width和minWidth的列,自動填充 table 剩余寬度
根據(jù)我們的需求,我們需要單獨控制每一列的寬度展示,并在瀏覽器寬度變化時實時的重新計算并且重新渲染列。
首先定義出一個方法,用來計算每一列在當前情況下所要的寬度,再綁定要dom上。然后,每次表格變化/瀏覽器寬度變化時候就能實時響應改變Table的寬度了。
const initColumns = () => {
// 計算每一列在當前情況下所要的寬度
}
watch(() => props.columns, () => { initColumns() });
onMounted(() => {
nextTick(() => {
initColumns();
on(window, 'resize', throttle(() => initColumns(), 400));
});
});
onBeforeUnmount(() => off(window, 'resize', () => initColumns()));全部代碼:
<script setup>
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { on, off } from '../../utils/listener'
import { throttle } from "../../utils/debounce&throttle"
import { useProp, useNeumorphism } from '../mixin/neumorphism'
const table = ref() // 與html中ref=""對應,定位dom元素
const props = defineProps({
...useProp,
...{
data: { // dataList
type: Array,
default: () => [],
},
columns: { // columnsList
type: Array,
default: () => [],
},
height: { // height
type: Number,
},
maxHeight: { // 流體高度
type: Number,
},
minUnsetWidth: { // 未設置寬度時最小寬度
type: Number,
default: 80
}
}
})
// 高度設置
let tableHeightStyleObj = computed(() => {
let styleObj = ((e) => {
if (e.maxHeight) return { maxHeight: e.maxHeight + 'px' }
if (e.height) return { height: e.height + 'px' }
return {}
})({...props})
return styleObj
})
// 列寬設置
let col = ref([])
const { columns, minUnsetWidth } = props
const _min_column_width = minUnsetWidth // 未設置寬度時最小寬度
const initColumns = () => {
col.value = (() => {
let _total_width = table.value.offsetWidth // 表格dom元素總寬度
let _needed_minWidth = columns.reduce((t, v) => { // 需要的最小寬度
t += v.width || v.minWidth || _min_column_width
return t
}, 0)
// 需要的最小寬度比總寬度大,則取minWidth即可
if (_needed_minWidth >= _total_width) return columns.reduce((t, v) => {
let n = v.width || v.minWidth || _min_column_width
t = [...t, n]
return t
}, [])
// 需要的最小寬度比總寬度大,則要把minWidth加權(quán),權(quán)重為(未分配的寬度 / minWidth之和)
let _unassigned_width = columns.reduce((t, v) => {
t += v.minWidth || 0
return t
}, 0)
let _assigned_width = _needed_minWidth - _unassigned_width
let _width_power = (_total_width - _assigned_width) / _unassigned_width
return columns.reduce((t, v) => {
let n = v.width || (v.minWidth ? (_width_power * v.minWidth).toFixed(2) : _min_column_width)
t = [...t, n]
return t
}, [])
})()
}
watch(() => props.columns, () => { initColumns() })
const throttleInitColumns = () => throttle(() => initColumns(), 400)
onMounted(() => {
nextTick(() => {
initColumns()
on(window, 'resize', throttleInitColumns)
})
})
onBeforeUnmount(() => off(window, 'resize', throttleInitColumns))
const { baseStyleObject } = useNeumorphism(props)
let styleObject = computed(() => ({
...tableHeightStyleObj.value
}))
</script>
<template>
<div class="san-table scrollbar" :style="{...baseStyleObject,...styleObject}">
<table ref="table">
<colgroup>
<col v-for="(item, index) in col" :key="index" :width="`${item}px`">
</colgroup>
<thead>
<tr class="neumorphism">
<th v-for="col in columns" :key="col.key"> {{col.title}} </th>
</tr>
</thead>
<tbody>
<tr class="neumorphism" v-for="row in data" :key="row.id">
<td v-for="col in columns" :key="col.key"> {{row[col.key]}} </td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'sanorin-table',
}
</script>
<style scoped>
@import "../../style/index.css";
@import "../../style/neumorphism.css";
.san-table{
padding: 0px 20px 20px 20px;
position: relative;
overflow: auto;
}
table {
width: 100%;
table-layout: fixed;
empty-cells: show;
border-collapse:separate;
border-spacing:0px 10px;
}
thead {
position: sticky;
top: 10px;
}
thead:before{
position: absolute;
content: '';
width: calc(100% + var(--shadow-blur) * 2);
transform: translate(calc(var(--shadow-blur) * -1) , -10px);
height: 20px;
background-color: var(--main-color);
z-index: -1;
}
table td,
table th {
color: var(--text-color);
padding: 8px 16px 8px 16px;
text-align: left;
word-break:break-all;
}
table th {
color: var(--text-back-color) !important;
font-weight: 600;
white-space: nowrap;
}
</style>其中用到的兩個js,防抖節(jié)流和注冊監(jiān)聽這里也放下吧
/* 防抖節(jié)流函數(shù) */
let timeout = null // 創(chuàng)建一個標記用來存放定時器的返回值
let count = 0;
export function debounce(fn, wait = 1000, immediate = false) {
return function () {
const args = arguments;
if (immediate) {
if (count == 0) {
fn.apply(this, arguments)
count++;
} else {
if (timeout) {
clearTimeout(timeout) // 每當用戶輸入的時候把前一個 setTimeout clear 掉
}
timeout = setTimeout(() => {
fn.apply(this, arguments)
}, wait)
}
} else {
if (timeout) {
clearTimeout(timeout) // 每當用戶輸入的時候把前一個 setTimeout clear 掉
}
timeout = setTimeout(() => {
fn.apply(this, arguments)
}, wait)
}
}()
}
let canRun = true;
let count1 = 0;
export function throttle(fn, wait = 1000, immediate = true) {
return function () {
if (immediate) {
if (count1 == 0) {
fn.apply(this, arguments);
count1++;
} else {
if (canRun) {
canRun = false
setTimeout(function () {
fn.apply(this, arguments)
canRun = true
}, wait);
}
}
} else {
if (!canRun) return
canRun = false
setTimeout(function () {
fn.apply(this, arguments)
canRun = true
}, wait);
}
}()
}/**
* 綁定事件 on(element, event, handler)
*/
export const on = (element, event, handler) => {
if (document.addEventListener) {
if (element && event && handler) {
element.addEventListener(event, handler, false)
}
}
}
/**
* 解綁事件 off(element, event, handler)
*/
export const off = (element, event, handler) => {
if (document.removeEventListener) {
if (element && event) {
element.removeEventListener(event, handler, false)
}
}
}序號
自定義內(nèi)容(slot)
固定列
全選
展開
到此這篇關于vue3新擬態(tài)組件庫開發(fā)流程——table組件源碼的文章就介紹到這了,更多相關vue3 table組件源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Vue Object.defineProperty及ProxyVue實現(xiàn)雙向數(shù)據(jù)綁定
這篇文章主要介紹了Vue Object.defineProperty及ProxyVue實現(xiàn)雙向數(shù)據(jù)綁定,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-09-09
vue下載excel的實現(xiàn)代碼后臺用post方法
這篇文章主要介紹了vue下載excel的實現(xiàn)代碼,后臺用post方法,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值 ,需要的朋友可以參考下2019-05-05

