首頁>技術>

目的:

仿Element-UI中的基礎表格實現表格渲染,自定義列模板,排序,單元測試

靜態效果:
<template>  <div></div></template><script>export default {};</script><style lang="scss" scoped></style>

JSX改版ElTable
<script>export default {  props: {    ...  },  computed: {    ...  },  render() {    return (      <table>        <thead>          <tr>            {this.columns.map((column) => {              return <th key={column.label}>{column.label}</th>;            })}          </tr>        </thead>        <tbody>          {/*透過遍歷rows後再次遍歷每行row的物件的key來填充keys.len長度的td*/}          {this.rows.map((data, index) => {            const tds = Object.keys(data).map((key) => {              return <td key={key}>{data[key]}</td>;            });            return <tr key={index}>{tds}</tr>;          })}        </tbody>      </table>    );  },};</script>

自定義列模板普通VNode設定v-slot:default="scope"後的VNode開始調整
<script>export default {  props: {    ...  },  computed: {    columns() {      return this.$slots.default.map(({ data: { attrs, scopedSlots } }) => {        const column = { ...attrs };        // 判斷是否存在scopedSlots        if (scopedSlots) {          // 為column物件增加用於渲染相關的renderCell屬性          column.renderCell = (row, i) => (            // 訪問作用域插槽,返回若干 VNode,並且可以透過default傳遞引數            <div>{scopedSlots.default({ row, $index: i })}</div>          );        } else {          // 預設渲染label          column.renderCell = (row) => <div>{row[column.prop]}</div>;        }        return column;      });    },  },  render() {    return (      <table>        <thead>          <tr>            {this.columns.map((column) => {              return <th key={column.label}>{column.label}</th>;            })}          </tr>        </thead>        <tbody>          {this.data.map((row, rowIndex) => {            return (              <tr key={rowIndex}>                {this.columns.map((column, columnIndex) => {                  return (                    <td key={columnIndex}>                      {column.renderCell(row, rowIndex)}                    </td>                  );                })}              </tr>            );          })}        </tbody>      </table>    );  },};</script>

排序
<script>export default {  data() {    return {      // 排序欄位      orderField: "",      // 排序方式      orderBy: "desc",    };  },  props: {    ...  },  computed: {    columns() {      ...    },  },  created() {    // 預設處理排序    this.columns.forEach((column) => {      // 獲取排序標識sortable      const hasSortable = Object.prototype.hasOwnProperty.call(        column,        "sortable"      );      if (hasSortable && column.prop && !this.orderField) {        // 執行排序函式        this.sort(column.prop, this.orderBy);      }    });  },  methods: {    sort(filed, by) {      // 更新排序列和方式      this.orderField = filed;      this.orderBy = by;      // 排序      this.data.sort((a, b) => {        const v1 = a[filed];        const v2 = b[filed];        if (typeof v1 === "number") {          return this.orderBy === "desc" ? v2 - v1 : v1 - v2;        } else {          return this.orderBy === "desc"            ? v2.localeCompare(v1)            : v1.localeCompare(v2);        }      });    },    // th進行排序的事件函式    taggleSort(filed) {      const by = this.orderBy === "desc" ? "asc" : "desc";      this.sort(filed, by);    },  },  render() {    return (      <table>        <thead>          <tr>            {this.columns.map((column) => {              // 獲取排序標識              const hasSortable = Object.prototype.hasOwnProperty.call(                column,                "sortable"              );              // 是否支援排序,無prop得不支援              if (hasSortable && column.prop) {                let orderArrow = "↑↓";                if (this.orderField === column.prop) {                  orderArrow = this.orderBy === "desc" ? "↓" : "↑";                }                return (                  <th                    key={column.label}                    onClick={() => {                      this.taggleSort(column.prop);                    }}                  >                    {column.label}                    <span>{orderArrow}</span>                  </th>                );              } else {                return <th key={column.label}>{column.label}</th>;              }            })}          </tr>        </thead>        <tbody>          ...        </tbody>      </table>    );  },};</script>

單元測試

未透過腳手架配置單元測試的執行1,2

1. 安裝 Jest 和 Vue Test Utils 等相關依賴

npm install --save-dev jest @vue/test-utils vue-jest babel-core@^7.0.0-bridge.0 babel-jest jest-serializer-vue

2. 在package.json配置執行命令

{  "scripts": {    "test": "jest"  }}

3. Jest擴充套件配置: jest.config.js

module.exports = {    moduleFileExtensions: ["js", "json", "vue"],    transform: {        ".*\\.(vue)$": "vue-jest",        "^.+\\.js$": "<rootDir>/node_modules/babel-jest",    },    // 處理 webpack 別名    moduleNameMapper: {        "^@/(.*)$": "<rootDir>/src/$1",    },    // 快照的序列化工具    snapshotSerializers: ["jest-serializer-vue"],    // 配置覆蓋率    collectCoverage: true,    collectCoverageFrom: ["**/src/**/*.{js,vue}", "!**/node_modules/**"],    coverageReporters: ["html", "text"],};

4. 單元測試

/* eslint-disable no-undef */import { mount } from '@vue/test-utils';import ElTable from '@/components/ElTable.vue';import ElTableColumn from '@/components/ElTableColumn.vue';describe('ElTable.vue', () => {    test('基礎表格', () => {        const template = `        <el-table :data="tableData" style="width: 100%">            <el-table-column prop="date" label="日期" width="180"></el-table-column>            <el-table-column prop="name" label="姓名" width="180"></el-table-column>            <el-table-column prop="address" label="地址"></el-table-column>        </el-table>        `        const comp = {            template,            components: {                ElTable,                ElTableColumn,            },            data() {                return {                    tableData: [                        {                            date: "2016-05-02",                            name: "王小虎",                            address: "上海市普陀區金沙江路 1518 弄",                        },                        {                            date: "2016-05-04",                            name: "李小虎",                            address: "上海市普陀區金沙江路 1517 弄",                        },                        {                            date: "2016-05-01",                            name: "張小虎",                            address: "上海市普陀區金沙江路 1519 弄",                        },                        {                            date: "2016-05-03",                            name: "趙小虎",                            address: "上海市普陀區金沙江路 1516 弄",                        },                    ],                }            },        }        const wrapper = mount(comp)        expect(wrapper.find('table').exists()).toBe(true)        expect(wrapper.findAll('th').length).toBe(3)        expect(wrapper.findAll('tbody>tr').length).toBe(4)        expect(wrapper.find('tbody>tr').text()).toMatch('上海市普陀區金沙江路 1518 弄')    });});

摘錄自開課吧學習資料 :

1. stmts是語句覆蓋率(statement coverage):是不是每個語句都執⾏了?

2. Branch分⽀覆蓋率(branch coverage):是不是每個if程式碼塊都執⾏了?

3. Funcs函式覆蓋率(function coverage):是不是每個函式都調⽤了?

4. Lines⾏覆蓋率(line coverage):是不是每⼀⾏都執⾏了?

測試報告

執行測試命令後在專案根目錄生成覆蓋報告(coverage),透過檢視報告完善單元測試

14
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • mysql 常用命令