活動頁,是各個互聯網公司一個頭疼的問題。爲了跟上對手的腳步,須要時不時就要搞點花樣。頻繁重複的做業對於前端團隊來說是一件很是頭疼的事情。活動發佈系統是迫切須要的,讓運營人員本身經過這個配置化活動頁發佈系統完成活動的發佈。javascript
可是配置化活動發佈系統對靈活性,擴展性,維護性都具備很大的挑戰。像阿里,騰訊,京東都有各自的活動發佈配置系統。可是對於咱們這種小團隊人手短缺,質量又沒法與巨頭相提並論,實在是不小的挑戰。更況且咱們還要同時兼容3個活動方的平臺(展示相同,業務邏輯和交互形式不一樣),對於咱們來講更加是一個不小的挑戰。html
咱們的技術難點並非如何製做出精美的活動頁,而是如何去抽象各個組件的模型,以便於讓運營能夠經過各類基礎UI組件來實現變幻無窮活動頁知足各類腦洞大開的活動場景。前端
活動頁上的一個圖片,一樣是圖片,他能夠是一個平鋪的廣告入口,能夠是分會場入口,能夠是一個調查問卷的入口,也能夠是一個報名入口,同時也多是某個參與活動的報名入口。java
用戶點擊後會有多種不一樣的交互形式,跳轉新頁面,彈出彈層,錨點到指定位置,甚至是提交表單。git
展示形式也會多種多樣,輪播,滑動,平鋪,堆疊。github
針對不一樣的用途,難以預測的交互行爲,和不一樣的展示形式,這將會是一個很是複雜而龐大的組件,若是咱們開發成一個組件,之後運營說不定有會想出什麼倒黴點子,開發多個組件,會有不少重複,所以,咱們設計的系統如何支撐他們的業務?如何儘量減小二次開發的工做量,下降維護成本,將是這套系統須要考慮的重中之重。json
如何去構建一個易於維護,易於擴展的系統,實際上是一件很難的事情,雖然我如今說個人設計是能夠作到易於維護,易於擴展的,可是保不齊之後會被運營和產品的腦洞打臉。可是架構是漸進的。儘量的抽象,解耦各個模型,而設計模式就是這方面最好的指導。redux
每個組件將會有本身的行爲,UI,以及本身的交互邏輯,咱們能夠將其分爲展示形式與交互行爲。展示形式是組件在客戶那裏的樣子,交互邏輯就是用戶操做的時候進行的一系列業務邏輯。這兩個邏輯單元組成一個基本的組件: 設計模式
咱們將一個基本組件單元分解成3個組件 -- UI組件(展示形式),交互組件(交互邏輯),組件單元(基本單位)。組件單元包含UI組件和交互組件。所以咱們就能夠經過使用不一樣的UI組件和交互組件組合的方式來組裝出來具備各類不一樣展示形式,不一樣交互邏輯的前端組件了。這一方法叫作 -- 橋接模式(實現代碼)。即:將抽象部分與它的實現部分分離,使它們均可以獨立地變化。同時還使用了組合模式(實現代碼)。數據結構
所以咱們對於每個基本組件單元就能夠設計一個下面的數據結構:
{
name: '組件名稱',
id: '組件ID',
type: '組件類型',
uiComp: {
name: 'UI組件名稱',
style: ''
},
logicComp: '交互組件名稱'
}
複製代碼
使用Vue根據這個結構構建出頁面就是以下代碼:
<template>
<Element>
<template :is="ui組件名稱" :style="組件樣式"></template>
<template :is="交互組件名稱"></template>
</Element>
</template>
<script> export default { ... } </script>
複製代碼
這裏遵循了單一職責原則,UI組件僅負責展示,交互組件負責交互反饋。實現了UI與邏輯的隔離。若是未來有新的交互邏輯,咱們就增長一個邏輯組件,若是增長了UI展示,就加一個UI組件,任何UI組件均可以和任意同一個
單元組件內的
交互組件相互組合。也就知足了里氏替換原則。
經過Vue的template組件,咱們很好的實現了組件的動態組合,咱們能夠認爲template就是一個抽象工廠(代碼實現)。根據咱們的須要爲咱們提供不一樣的組件。而在單元組件中根本不須要關心它具體是什麼。全部的基本組件單元均被抽象成一個高階組件
可是由於UI展示和邏輯是單獨的兩個不一樣組件,那麼咱們如何將他們打通呢?由於UI組件才能接受到用戶的動做,而動做的反饋都在交互邏輯組件中。中介模式(代碼實現),即:用一箇中介對象來封裝一系列的對象交互。中介者使各對象不須要顯式地相互引用,從而使其耦合鬆散,並且能夠獨立地改變它們之間的交互。這個中介對象就是咱們的負責組合UI組件和交互組件的組件單元組件,它經過Vue的props來向不一樣的組件分發元數據與結果數據:
<template>
<Element>
<template :is="ui組件名稱" :style="組件樣式" :props="用於顯示的結果數據" @action="用戶操做回調函數"></template>
<template :is="交互組件名稱" :props="元數據" :payload="數據載體" @init="初始化完成回調函數" @finish="用戶操做響應回調"></template>
</Element>
</template>
<script> export default { data() { return { '元數據', '結果數據', '數據載體' } }, methods: { '初始化完成回調函數' (payload) { if (payload.type === 'ok') { this.'結果數據' = payload.data; } }, '用戶操做回調函數' (payload) { if (payload.type === '幹了什麼') { this.'數據載體' = payload.data; } }, '用戶操做響應回調' (payload) { if (payload.type === '結果狀態') { this.'結果數據' = payload.data; } } } } </script>
複製代碼
業務邏輯組件:
<template>
<Element>
<Toast />
</Element>
</template>
<script> export default { props: { '元數據': Object }, watch: { payload: { handle() { this.$emit('finish', action) }, deep: true } }, mounted() { this.$emit('init', action) } } </script>
複製代碼
這裏藉助了Vue的props實現了觀察者模式(代碼實現),子組件經過觀察props的變化來經過中介(單元組件)向各個子組件(ui組件,邏輯組件)進行通訊。同時,每個子組件又是一個訪問者模式(代碼實現)的訪問者實現,經過payload相似redux中action的使用,咱們就起到了統一接口的目的,從而也就實現了多態。
目前,這個系統還不是很強大,甚至過於簡單,有可能有些人會提出質疑,我仍是那句話,架構是漸進的。隨着運營人員的腦洞愈來愈大,產品的膽子愈來愈大,這個系統也會不斷成長,裏面的東西也會變得愈來愈複雜。
這個設計並無像其餘大廠那樣提供功能強大而複雜功能全面的定製化組件,而是須要經過增長新組件(水平擴展)的方式來對系統進行擴展。這主要是本着【開放封閉原則】,對修改說【不】,改展示形式,就增長新的UI組件,改業務邏輯,就增長新的邏輯組件,這樣才能夠保持系統不至於過於臃腫和複雜以及難以維護和擴展。保持着每一個組件都是很簡單,而且邏輯和顯示分離,遵循接口隔離原則。這樣任何人加入這個項目,均可以很容易的接手系統進行擴展。並且即很大程度上下降了活動頁的開發成本,也避免了絕大多數的重複勞動,同時延長了系統的使用週期,最大化壓榨系統的價值。