伴隨着碼良上線運行2年之際,今天正式提供了組件商城,也藉此來給你們推薦一下碼良,一個免費開源的網頁搭建系統,功能很是完善。碼良項目在公司內部已經支撐了3000屢次活動穩定運行中,能在本身公司內部搭建供運營活動相關支持,咱們提供源碼,docker兩種方式安裝。javascript
下面多圖預警
css
介紹
碼良是什麼
碼良是一個在線生成 H5 頁面並提供頁面管理和頁面編輯的平臺
,用於快速製做 H5 頁面。用戶無需掌握複雜的編程技術,經過簡單拖拽、少許配置便可製做精美的頁面,可用於營銷場景下的頁面製做。同時,也爲開發者提供了完備的編程接入能力
,經過腳本和組件的形式得到強大的組件行爲和交互控制能力。html
碼良爲誰而做
如上所述,碼良不只可服務於運營人員用來製做輕業務的營銷頁面,基於完備的編程接入能力,甚至能夠做爲開發者進行快速業務迭代的工具。 碼良的初心是建立一個開源免費的平臺,但願有才的人在平臺上貢獻本身的組件,腳本,設計模板頁面,若是須要後續能夠提供組件或腳本開發者設置免費或則收費使用
固然徹底也支持公司內部搭建使用。vue
核心功能演示
組件商城
提供第三方組件快速導入本身搭建的系統,也能夠把本身開發比較好的組件上傳到組件商城提供他人免費或者付費使用java
組件接入簡單
若是第三方組件知足不了你,咱們提供方便的腳手架建立組件,你能夠把一個小功能,一個頁面,一個項目作成一個組件,暴露參數給其餘同窗在編輯器裏面填寫 組件開發文檔node
圖爲組件開發套件使用動圖git
腳本擴展組件功能
每一個特定功能的組件能夠經過組合各類功能(點擊跳轉,打點等等任何邏輯)腳原本達到對組件功能的擴展。完成特定的功能,並友好的提供參數給到組件的使用者在編輯器上填寫。目前每一個腳本都是一個 Vue 對象,這對熟悉 Vue 的開發者很是友好,深刻了解請前往 碼良是如何設計高擴展的在線網頁製做平臺的 github
圖爲編輯器工做區界面 docker
組件動畫展現
運營活動對一些簡單的動畫提供支持,方便作一些入場和出場的動畫,提高活動的交互感,咱們使用了 Animate.css 提供的一套 CSS 動畫。並提供逐幀設置預覽,方便使用。下面提供簡單的展現 編程
合成組件展現
合成組件就是選擇已有的節點保存爲一個通用的組件,方便下次直接使用
使用組合組件
導出組合組件
模板頁面
頁面模板的目的和組合組件相似,都是提供已經作好的內容,運營快速選擇使用達到快速上線活動的目的,下面是簡單的演示
核心設計
下面會分享下咱們的核心設計,此次主要重點說明下面幾方面內容
- 咱們會介紹總體的架構來了解通常的編輯產生頁面的基本思路,基於數據編程。
- 咱們會介紹核心的組件如何設計,確保能夠自由擴展組件能力
- 咱們會介紹如何設計編輯器達到可自定義屬性的控制面板 <span style="color:red"> 備註(因爲總體項目實現使用的VUE,因此後面有部分介紹具體技術實現的時候會以VUE的使用角度說明。用其餘框架的自行腦補)</span>
總體架構
- 總體架構 總體架構相對簡單,核心就是定義一套標準的數據規範,提供一個編輯器去編輯這個數據,同時提供一個解析器去解析該數據,而後渲染出頁面,流程以下。
- 數據結構 經過上面的圖看到每一個頁面是由不少節點組成(node),每一個節點能夠嵌套子節點。而每一個節點包括的基本信息以下,<span style="color:red">備註文章後續提到的 nodeInfo 都是該節點對應的以下數據</span>
{ "id": "truck/button1l", "type": "truck/button", "label": "按鈕1l", "version": "0.1.4", "visible": true, "style": { "position": "absolute", "width": "100px", "height": "40px" }, "animate": [], "props": { "text": "輸入文字", "type": "danger", "click": [] }, "path": "https://ymm-maliang.oss-cn-hangzhou.aliyuncs.com/truck/button/0.1.4/index.js", "script": "", "events": [] }
每一個組件比較核心的元素由以下幾部分組成
- id 元素的惟一編號。方便代碼獲取和操做
- type 組件的類型。會根據不一樣的類型加載不一樣的腳本資源,而後運行加載完的腳本會建立一個VUE Component,而後會把這個Component 掛載到VUE全局,因爲每一個組件節點都是一個 動態的 Component 組件。這時候只須要修改動態組件的 :is 數據進行內容替換就行了。
- label 組件別名。方便運營理解使用
- version 組件版本。 每一個組件都是有本身的版本的。
- style 組件樣式
- props 組件參數。每一個組件都是有一些初始化參數的,這些參數都是營銷人員在編輯器裏面填寫的。這些參數就存放在這裏面,在擴展編輯器屬性能力裏面會詳細說明
- script 擴展腳本。每一個組件能夠插入一些腳本代碼擴展組件的功能。這些腳本建立的對象會 mixin 到該組件對象裏面,在組件設計裏面會詳細介紹
- event 組件綁定事件。 每一個組件能夠綁定常見dom事件。
- child 孩子節點。
- path 腳本路徑。 經過該路徑加載腳本建立組件對象。
上圖的頁面包括一個圖片,圖片下面兩個文字,圖片兄弟節點有個按鈕元素。對應頁面的詳細數據結構以下,能夠感覺下完整結構。
{ "id": "node", "type": "node", "visible": true, "style": { }, "props": {}, "child": [ { "id": "truck/image15j", "type": "truck/image", "label": "圖片15j", "version": "0.1.4", "visible": true, "style": { "position": "absolute", "width": "320px" }, "animate": [], "props": { "url": "https://ymm-maliang.oss-cn-hangzhou.aliyuncs.com/ymm-maliang/access/ymm_1533366999689.png", "click": [] }, "path": "https://ymm-maliang.oss-cn-hangzhou.aliyuncs.com/truck/image/0.1.4/index.js", "script": "", "events": [], "child": [ { "id": "truck/text3l", "type": "truck/text", "label": "文本3l", "version": "0.1.4", "visible": true, "style": { "position": "absolute" }, "animate": [], "props": { "text": "文字內容1", "click": [] }, "path": "https://ymm-maliang.oss-cn-hangzhou.aliyuncs.com/truck/text/0.1.4/index.js", "script": "", "events": [] }, { "id": "truck/text3l5g", "type": "truck/text", "label": "文本3l", "version": "0.1.4", "visible": true, "style": { "position": "absolute", "width": "114px" }, "animate": [], "props": { "text": "文字內容2", "click": [] }, "path": "https://ymm-maliang.oss-cn-hangzhou.aliyuncs.com/truck/text/0.1.4/index.js", "script": "", "events": [] } ] }, { "id": "truck/button1l", "type": "truck/button", "label": "按鈕1l", "version": "0.1.4", "visible": true, "style": { }, "animate": [], "props": { "text": "輸入文字", "type": "danger", "click": [] }, "path": "https://ymm-maliang.oss-cn-hangzhou.aliyuncs.com/truck/button/0.1.4/index.js", "script": "", "events": [] } ], "script": [], "animate": [], "version": "0.1.0", "events": [] }
一句話小結:頁面是由不少節點遞歸生成,每一個節點包含佈局,事件,腳本,參數,版本等信息,而後編輯器編輯這些信息,解析器解析這些信息。
組件設計
一個頁面都是由一個個遞歸嵌套的組件組成,組件是整個項目的最核心的一部分,爲了讓組件具備擴展能力,咱們對組件的功能使用了 mixin 方式,經過基礎組件邏輯+自定義腳本的形式來生成組件。下面介紹下總體<span style="color:red">組件結構</span>和<span style="color:red">初始化流程</span>,方便理解咱們是如何實現的。
- 上圖左部分能夠看到整個頁面都是由一個一個node節點組成,他們是一個樹狀結構,每一個node節點下面包含着一個組件對象作功能展現,下面是node節點的dom結構,能夠看到每一個節點都是遞歸節點,每一個節點內部都包含一個動態組件,每一個動態組件的經過nodeinfo.id爲key的組件進行渲染。
<div class="node" v-show="visible" :style="nodeInfo.style"> <component :is="nodeInfo.id" v-bind="nodeInfo.props" :ref="nodeInfo.id" :style="componentStyle"></component> <node v-if="nodeInfo.child" :info="item" v-for="item in nodeInfo.child " :key="item.id"></node> </div>
- 上圖右部分能夠看到渲染流程。爲了達到組件的高擴展性,每一個組件的功能包含兩個主要部分
- 組件代碼 ,每一個組件都是有特定參數和特定功能的腳本實現,好比 圖片,富文本,分享,九宮格等組件,組件代碼經過對於的type 和 path 參數去加載對於的腳本獲取對象。
- 組件經過編輯器添加的腳本 , 編輯器能夠爲每一個組件動態添加腳原本加強對組件的操做能力。以下操做,能夠看到一個組件能夠添加多個腳本。每一個腳本其實就是一個的vue組件,終這裏面的代碼會建立對象 mixin 到最終的vue組件裏面,因此你能夠爲組件擴展各類功能進行支持你的特殊業務。
一個節點的邏輯功能=組件邏輯+腳本1+腳本2+腳本3... 每一個組件在根據本身的類型加載對應js腳本後,會對該組件 nodeInfo.script 裏面的 邏輯進行mixin. 而後建立一個最終的組件註冊到Vue.component 裏面方便後續使用,核心代碼以下
// 經過加載到的組件腳本得到的全局對象建立vue對象 window['image_1.0.3'] load組件腳本運行後會生成的對象 var component = Vue.extend( window['image_1.0.3']) // 遍歷全部加入的腳本混合組件對象中 nodeInfo.script.forEach((value)=>{ component =component.extent(value) }) // 以節點id爲key,註冊最終組件對象 Vue.component(nodeInfo.id,component) // 修改該節點的動態組件 :is 參數爲 該節點id // done
一句話小結:經過不斷的mixin新的自定義腳本進來擴展組件能力
組件屬性編輯設計
屬性編輯主要目的是開發組件的人會暴露一些可配置的參數給運營人員在編輯器裏面填寫和修改。 好比選擇一個組件後再右側屬性面板能夠對這個組件進行一些屬性設置. 爲了便於維護和擴展,咱們以爲一個組件的可配置數據包括簡單數據,複雜邏輯數據,對應可編輯屬性的部分也分爲兩部分
- 編輯器提供基礎屬性編輯
- 編輯器能提供擴展編輯編輯能力,主要針對運營方便操做,特徵性的開發組件屬性的編輯功能,提供對運營友好的操做體驗
下面針對這兩塊比較核心的內容說明下咱們如何作的。
編輯器基礎屬性編輯能力
對於一個組件的開發者來講,一是定義該組件那些參數須要暴露到編輯器讓運營操做,二是定義該屬性對應的值經過什麼控件操做。 上文在總體架構數據結構中提到了每一個node節點都有一個 props 屬性,該屬性就是存放着該組件可配置的參數所配置的最終值,在初始化組件的時候會把這個 props的數據傳入組件進行初始化。而定義一個組件能接受那些參數則是在每一個Vue組件的props 屬性上定義, 而編輯器的做用就是經過編輯器去獲取到每一個對象定義的props,而後根據每一個參數的類型提供不一樣的編輯控件,好比 boolean 咱們會提供 切換按鈕,image 咱們會提供選擇圖片控件等等。擴展腳本一樣能夠擴展組件的可編輯屬性,下面是一個擴展腳本的例子。主要說明支持的那些類型,可定義的格式。總體流程以下。
下面咱們先看一下每一個組件可定義的props 例子。
/** * * @param type: 字段類型,支持原生類型以及【碼良輸入類型】 * * 碼良輸入類型: * input 單行輸入框 * text 多行輸入框 * enum 列表單選 需提供選項字段defaultList, 支持數組、map結構 * image 圖片選擇 * audio 音頻選擇 * video 視頻選擇 * richtext 富文本 * number 數字 * function 方法設置 * data json數據 * date 時間選擇 * checkbox 多選框 同enum 不提供defaultList字段時,輸入值爲布爾類型 * radio 單選框 同enum * */ return { props: { // 原生類型 foo: { type: String }, // 圖片輸入 fooImage: { type: String, editer: { type: 'image' } }, // 日期 fooDate: { editer: { type: 'date' } }, // checkbox 多選 fooCheckbox: { type: Array, // 此項必須爲Array default: () => { // 且需提供初始值 return [] // ['day', 'hour', 'min', 'sec'] }, editer: { label: '顯示精度', type: 'checkbox', defaultList: [ // array 形式的選項 'day', 'hour', 'min', 'sec', ] } }, // checkbox 布爾 fooCheckboxBool: { type: Boolean, // 此項必須爲Boolean editer: { type: 'checkbox' } }, // enum 含選項 fooEnum: { default: 'value1', type: String, editer: { label: '我是字段名', // 將字段名顯示爲可讀性更強的文本,不提供此項時,顯示字段名 desc: '我是幫助文本', // 爲字段提供提示信息,幫助理解字段的意義 type: 'enum', defaultList: { // map結構的選項 key爲值,value爲顯示文本 'value1': '條件1', 'value2': '條件2', 'value3': '條件3', } } }, // 條件屬性 ifFoo1: { type: [Number], default: 0, editer: { work: function () { return this.fooEnum == 'value1' // 只有當 `fooEnum` 字段取值爲 'value1' 時才顯示此項 }, label: '條件屬性1', type: 'number', } }, ifFoo2: { type: [Date, String], default: null, editer: { work: function () { return this.fooEnum == 'value2' // 只有當 `fooEnum` 字段取值爲 'value2' 時才顯示此項 }, label: '條件屬性2', type: 'date', } }, }, mounted: function () { console.log('hello ' + this.foo) console.log('hello ' + this.fooImage) // ... } }
上面腳本擴展的組件對應的增長的可配置的屬性以下圖。
這裏面的的主要設計在於每一個props屬性裏面添加了一個 editer字段進行該字段在編輯器環境下提供什麼組件對該屬性進行編輯。editer的字段主要包括以下。
{ label: '我是字段名', // 將字段名顯示爲可讀性更強的文本,不提供此項時,顯示字段名 desc: '我是幫助文本', // 爲字段提供提示信息,幫助理解字段的意義 type: 'enum', ignore: true, // 不在編輯器顯示 work:function(){ // 若是知足什麼條件纔會顯示 }, defaultList: { // map結構的選項 key爲值,value爲顯示文本 'value1': '條件1', 'value2': '條件2', 'value3': '條件3', } }
- label 在編輯器顯示的名稱
- desc 該字段在編輯器詳細描述
- type 編輯該屬性的組件類型
- ignore 負略在編輯器顯示,通常在該屬性提供了高級編輯模式須要隱藏掉默認的模式。
- work 一個方法,該方法返回true 會在編輯器顯示該屬性,一遍用於聯動隱藏和顯示一些編輯屬性
- defaultList 一些默認數據,通常提供單選,下拉等默承認選擇的值。
一句話小結:編輯器經過獲取每一個組件的props,遍歷每個屬性,按類型提供不一樣的操做控件,編輯生成最終的數據放到 nodeInfo.props上。
擴展編輯屬性能力
不少時候一個組件可配置的屬性按咱們的規劃來講就下面幾種類型。
/** * * @param type: 字段類型,支持原生類型以及【碼良輸入類型】 * * 碼良輸入類型: * input 單行輸入框 * text 多行輸入框 * enum 列表單選 需提供選項字段defaultList, 支持數組、map結構 * image 圖片選擇 * audio 音頻選擇 * video 視頻選擇 * richtext 富文本 * number 數字 * function 方法設置 * data json數據 * date 時間選擇 * checkbox 多選框 同enum 不提供defaultList字段時,輸入值爲布爾類型 * radio 單選框 同enum * */
若是按每一個類型提供一個基本的編輯組件就能完成90%的需求,不過在隨着組件的複雜度增長,每一個組件可配置的屬性變得千奇百怪,各類需求均可能。好比一個簡單的多選,原來的可選項只能寫死,如今須要本身請求接口獲取。但這些邏輯咱們不能作到統一的編輯器裏面,也不能作到組件裏面,因此只能在作組件的時候提供一種機制讓開發組件的同窗開發組件的同時,還能對這個組件開發一個自定義的編輯器,並能整合到咱們的屬性編輯面板中。 總體架構以下,最終效果能夠參考上圖的自定義面板部分
一個組件打包完通常會有兩個必要的腳本,一個是組件對應的js。一個是該組件對應編輯器的腳本js。 整個平臺對編輯器的功能擴展都是相通的,經過加載腳本,建立對象,註冊到Vue,而後經過動態組件渲染。對編輯器屬性的擴展也是同樣。加載對應組件的編輯器腳本,而後按相同的方法進行植入。這裏就不在細講。這裏簡單分享下咱們對一個組件的開發最終的結果。以下圖
- 組件開發過程當中的界面
- 組件發佈後在碼良編輯器裏面的樣子
官方網站
更多信息請到使用手冊中具體瞭解
官網: https://godspen.ymm56.com/
使用手冊: https://godspen.ymm56.com/doc/cookbook/introduce.html
在線體驗: https://godspen.ymm56.com/admin/#/home
私有部署: https://godspen.ymm56.com/doc/cookbook/install.html
組件商城: https://godspen.ymm56.com/shop/
開源代碼
國內鏡像: https://gitee.com/ymm-tech/gods-pen-admin 碼良管理後臺
https://gitee.com/ymm-tech/gods-pen-server 碼良服務端代碼
https://gitee.com/ymm-tech/gods-pen 碼良核心編輯器和預覽解析器
github: https://github.com/ymm-tech/gods-pen-admin 碼良管理後臺
https://github.com/ymm-tech/gods-pen-server 碼良服務端代碼
https://github.com/ymm-tech/gods-pen 碼良核心編輯器和預覽解析器