全棧之路 —— Vue自定義表格組件

We should separate Structure, Presentation, and Behavior. -- The Golden Rule

2019年上半年囱井,間間斷斷寫了一些頁面,也為我的全棧打上了前端這塊拼圖。

這篇文章,我會先介紹一下我對前端的理解前翎,然后用vue框架寫一個自定義的表格的demo杈帐。

A Quick Glimpse

在公司里,我做了一些后臺管理服務(wù)竞思,也開發(fā)了一些數(shù)據(jù)分析工具。后臺管理服務(wù)對頁面的要求不高钞护,python可以用jinja盖喷,java(kotlin)可以用ftl,來渲染動態(tài)頁面难咕,開發(fā)快速课梳,簡單實用距辆。數(shù)據(jù)分析工具的頁面就會比較復(fù)雜,PM也比較看重頁面的美觀和交互暮刃,我用了當(dāng)下流行的vue框架跨算,頁面交互處理起來確實更方便。

我自己在寫前端頁面的時候椭懊,有一個豁然開朗的時間點诸蚕,關(guān)于黃金法則“結(jié)構(gòu)和表現(xiàn)相分離”。了解法則之前氧猬,我專注于實現(xiàn)頁面價值背犯,在代碼的結(jié)構(gòu)上思考的不多,然后頁面進行增量和迭代時都痛苦不堪盅抚;了解之后漠魏,寫代碼就有了一定的理論指導(dǎo):

  • html和css的分離。讓盒子模型一下子非常清晰了妄均,拿到PM的原型圖柱锹,先分幾個大塊,結(jié)構(gòu)就基本確定了丰包;
  • js和css的分離禁熏。讓頁面事件變得非常清晰,js基本只負(fù)責(zé)click邑彪,input和select等幾個用戶主動交互的事件瞧毙;
  • css對表現(xiàn)的絕對控制。讓苛刻的PM也喜笑顏開锌蓄,掌握一些基本style (display, position等)升筏,就能完全滿足PM關(guān)于位置撑柔、顏色瘸爽、大小等等的任性要求;
  • 還不滿足铅忿?HTML5中的media tag剪决,加上pixi.js庫,多媒體和動畫也不在話下檀训。

理論核心 + 不斷實踐柑潦,在應(yīng)用or業(yè)務(wù)層就感覺非常棒了。

A Brief Instance

在做數(shù)據(jù)分析工具的時候峻凫,最常寫的就是畫圖和表格渗鬼。這里做一個table demo,分享一下所思所學(xué)荧琼。

在開始寫代碼之前譬胎,我們先想一下表格的常見屬性差牛。

  • 不固定的列數(shù)。有五列的表堰乔,也有九列的表偏化,表頭的名字也經(jīng)常換,表頭有時會需要一些注釋镐侯;
  • 篩選的列侦讨。有些列需要能夠篩選;
  • 排序的列苟翻。有些列需要能夠排序韵卤;
  • 個性化的單元格。有的單元格可能需要特殊處理袜瞬。

所以怜俐,我首先把表格分成了header和body,每一個單元格都是一個對象邓尤,具有單元格的一些屬性拍鲤。

在渲染數(shù)據(jù)的過程中,為了響應(yīng)篩選和排序汞扎,采用行列索引的方式依次渲染單元格季稳。為了篩選和排序互不影響,就簡單地采用了全部排序的方式澈魄。

想清楚了這些問題之后景鼠,我們就可以開始寫代碼了。

說到開始寫代碼痹扇,前端demo代碼有一點很有意思铛漓,因為即時重啟的緣故,不用測試代碼就有直觀的反饋鲫构,讓每一行代碼都有一個功能點浓恶,很棒!


一個DEMO

那结笨,就開始吧包晰。

  1. 直接使用vue create創(chuàng)建一個新的項目。
vue create custom-table
# 安裝一些必要的依賴炕吸,bootstrap伐憾,jquery,fontawesome之類的
yarn add bootstrap jquery
# 為了讓vue更好的使用全局的jquery赫模,webpack提供的plugin树肃,這里可以簡單的創(chuàng)建一個vue.config.js
touch vue.config.js
  1. 在App.vue中寫出表格的結(jié)構(gòu)。
<!-- 表的結(jié)構(gòu) -->
<table class="table table-bordered">
    <thead>
      <th></th>
      <th v-for="(cell, colIndex) in header" :key="colIndex">
        <span v-text="cell.value"></span>
        <info-element v-if="cell.info" :info="cell.info" />
        <filter-element v-if="cell.filter" />
        <sort-element v-if="cell.sort" />
      </th>
    </thead>
    <tbody>
      <tr v-for="(row, rowIndex) in body" :key="rowIndex">
        <td v-text="rowIndex"></td>
        <td v-for="(cell, colIndex) in row" :key="colIndex">
          <cell-element :cell="cell" />
        </td>
      </tr>
    </tbody>
</table>
// 表的數(shù)據(jù)
props: {
    header: {
      type: Array,
      require: true,
      default: () => {
        return [
          { value: "col1", info: "這是第一列" },
          { value: "col2", filter: true },
          { value: "col3", sort: true }
        ];
      }
    },
    body: {
      type: Array,
      require: true,
      default: () => {
        return [
          [{ value: "col1", color: "red" }, { value: "col2", type: "percent"}, { value: "col3", type: "float" }],
          [{ value: "col1" }, { value: "col2" }, { value: "col3" }]
        ];
      }
    }
}

這里可以感受到vue語法糖的優(yōu)雅瀑罗,for和if很好的嵌入胸嘴,通過數(shù)據(jù)來改變DOM結(jié)構(gòu)莉钙。
也能感受我的設(shè)計意圖,header中的屬性可以決定表頭的特殊功能(篩選筛谚、排序磁玉、注釋);body中的屬性可以去改變單元格的表現(xiàn)(css、format)驾讲。

  1. 有了清晰的結(jié)構(gòu)蚊伞,就開始組件補全計劃吧。
    用FilterElement來說吮铭,table父組件中會有若干個filter組件时迫,每一個filter組件輸入rowIndex和對應(yīng)colIndex的value([{rowIndex: rowIndex, value: value}]或者簡單寫成[[rowIndex, value]]),輸出經(jīng)過篩選之后的rowIndex;
    同時,為了讓多列同時篩選谓晌,我們?nèi)〔煌M件輸出值的并集掠拳。
    想清楚這兩個細(xì)節(jié)之后,代碼就順理成章了纸肉。
    在table父組件中溺欧,我們有:
<!-- 子組件之間的數(shù)據(jù)傳遞 -->
<filter-element
  v-if="cell.filter"
  :column-data="getAllColumnData(colIndex)"
  @filterRows="filterRows(colIndex, $event)"
/>
data() {
    return {
        // key是colIndex, value是fitleredRowIndexes, 用來最后取并集
        filterBuffer: {}
    }
},
methods: {
    // 獲取表格一列的值,和sortElement的方法一樣
    getAllColumnData: function(colIndex) {
        let tmpArray = [];
        for (let rowIndex = 0; rowIndex < this.body.length; rowIndex++) {
            tmpArray.push([rowIndex, this.body[rowIndex][colIndex].value]);
        }
        return tmpArray;
    },
    // 用filterBuffer來保存某一列的排序索引
    filterRows: function(colIndex, filteredRowIndexes) {
        this.$set(this.filterBuffer, colIndex, filteredRowIndexes);
    }
}

在FilterElement子組件中柏肪,我們有:

<div class="inline-block">
  <button
    id="filterEle"
    class="btn dropdown-toggle like-text-btn"
    data-toggle="dropdown"
    aria-haspopup="true"
    aria-expanded="false"
  />
  <div class="dropdown-menu fit-view" aria-labelledby="filterEle" @click.stop>
    <div
      class="dropdown-item"
      v-for="(column, index) in allDataSet"
      :key="index"
    >
      <input :value="column" v-model="checkValue" type="checkbox" />
      <span v-text="column" />
    </div>
  </div>
</div>
computed: {
    // 獲得列的數(shù)據(jù)
    allDataArray: function() {
        let tmpArray = [];
        for (let i = 0; i < this.columnData.length; i++) {
            tmpArray.push(this.columnData[i][1]);
        }
        return tmpArray;
    },
    // 去重數(shù)據(jù)
    allDataSet: function() {
        return Array.from(new Set(this.allDataArray));
    },
    // 篩選后的行索引
    filteredRowIndexes: function() {
        let tmpArray = [];
        for (let i = 0; i < this.columnData.length; i++) {
            if (this.checkValue.includes(this.allDataArray[i])) {
                tmpArray.push(this.columnData[i][0]);
            }
        }
        return tmpArray;
    }
},
watch: {
    // 監(jiān)聽用戶的篩選事件
    checkValue: function() {
        this.$emit("filterRows", this.filteredRowIndexes);
    }
}

在寫篩選組件時姐刁,有很多值得思考的地方:

  • 關(guān)于去重元素,如果是primitive data烦味,我們可以直接使用Set聂使;那如果是對象,就需要hash去重谬俄,還可能要考慮“與”和“或”的關(guān)系柏靶;
  • 無論js、java溃论、還是c屎蜓,在做對象遍歷的時候都沒有python那種“自在”的感覺。不過js在性能上好像有這樣的關(guān)系蔬芥,for > for-of > forEach > filter > map > for-in梆靖;
  • 對象的深控汉、淺拷貝笔诵,確實比python需要要花更多的心思;
  • 全選 / 全部選 / checkbox的不確定態(tài)姑子;
  1. 完成所有組件之后乎婿,demo就完成了。全部代碼在我的GIT REPO里街佑,歡迎大家查看谢翎。

A Short Summary

前端三板斧捍靠,HTML、CSS 和 JS森逮,隨著實踐也掌握的越來越多榨婆。JS的對象和原型,CSS的loader和parser褒侧,Vue的生命周期和狀態(tài)管理良风,都略知一二。學(xué)習(xí)會讓人開心闷供,但也會讓人迷惘烟央,因為在這個焦慮的社會,價值還是太重要了歪脏。共勉 ~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疑俭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子婿失,更是在濱河造成了極大的恐慌钞艇,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件豪硅,死亡現(xiàn)場離奇詭異香璃,居然都是意外死亡,警方通過查閱死者的電腦和手機舟误,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門葡秒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嵌溢,你說我怎么就攤上這事眯牧。” “怎么了赖草?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵学少,是天一觀的道長。 經(jīng)常有香客問我秧骑,道長版确,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任乎折,我火速辦了婚禮绒疗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘骂澄。我一直安慰自己吓蘑,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著磨镶,像睡著了一般溃蔫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上琳猫,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天伟叛,我揣著相機與錄音,去河邊找鬼脐嫂。 笑死痪伦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的雹锣。 我是一名探鬼主播网沾,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蕊爵!你這毒婦竟也來了辉哥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤攒射,失蹤者是張志新(化名)和其女友劉穎醋旦,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體会放,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡饲齐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了咧最。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捂人。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖矢沿,靈堂內(nèi)的尸體忽然破棺而出滥搭,到底是詐尸還是另有隱情,我是刑警寧澤捣鲸,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布瑟匆,位于F島的核電站,受9級特大地震影響栽惶,放射性物質(zhì)發(fā)生泄漏愁溜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一外厂、第九天 我趴在偏房一處隱蔽的房頂上張望冕象。 院中可真熱鬧,春花似錦酣衷、人聲如沸交惯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽席爽。三九已至,卻和暖如春啊片,著一層夾襖步出監(jiān)牢的瞬間只锻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工紫谷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留齐饮,地道東北人。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓笤昨,卻偏偏與公主長得像祖驱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瞒窒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 基于Vue的一些資料 內(nèi)容 UI組件 開發(fā)框架 實用庫 服務(wù)端 輔助工具 應(yīng)用實例 Demo示例 element★...
    嘗了又嘗閱讀 1,142評論 0 1
  • UI組件 element- 餓了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的組件庫 m...
    小姜先森o0O閱讀 9,412評論 0 72
  • UI組件 element- 餓了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的組件庫 m...
    王喂馬_閱讀 6,448評論 1 77
  • 一:什么是閉包捺僻?閉包的用處? (1)閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)崇裁。在本質(zhì)上匕坯,閉包就 是將函數(shù)內(nèi)部和函數(shù)外...
    xuguibin閱讀 9,543評論 1 52
  • vue概述 在官方文檔中,有一句話對Vue的定位說的很明確:Vue.js 的核心是一個允許采用簡潔的模板語法來聲明...
    li4065閱讀 7,193評論 0 25