有朋友提出一看來是懵逼的,根本不知道什么是面向UI編程的思想,下面是我之前寫的博客,描述的這中思想,下面為地址,參考下就明了很多了。
1. 前端思想實(shí)現(xiàn):面向UI編程
2. 面向UI編程框架:ui.js框架思路詳細(xì)設(shè)計(jì)
時(shí)隔第一次被UI思路激勵(lì),到現(xiàn)在1.0的粗糙版本發(fā)布,掐指一算整整半年了。半年之間,有些細(xì)節(jié)不斷推翻重做,再推翻再重做。時(shí)隔今日,終于能先出來個(gè)東西了,這個(gè)版本很粗糙,主體功能大概能實(shí)現(xiàn)了,但是還是有很多很多的問題。不過有問題沒事,可以進(jìn)行修改完善,這是相對(duì)輕松的問題,最艱難的從無到有的創(chuàng)造才是最艱難的。好了,不廢話了,我們直接進(jìn)入正題 --- UI.js功能介紹。
首先介紹幾個(gè)概念
分布式:原來的概念只有分布式計(jì)算,它研究如何把一個(gè)需要非常巨大的計(jì)算能力才能解決的問題分成許多小的部分,然后把這些部分分配給許多計(jì)算機(jī)進(jìn)行處理,最后把這些計(jì)算結(jié)果綜合起來得到最終的結(jié)果。這邊要實(shí)現(xiàn)的概念是,
分布式協(xié)作開發(fā):就是將一個(gè)項(xiàng)目分拆多個(gè)組件,可從不同的服務(wù)器上拉取,分布協(xié)作開發(fā)
容器:容器用來包裝或裝載物品的貯存器,從web角度來講,web容器是應(yīng)用服務(wù)器中位于組件和平臺(tái)之間的接口集合。
組件:供裝配整臺(tái)機(jī)器、構(gòu)件或元件的零件組合或者在電子或機(jī)械設(shè)備中組裝在一起形成一個(gè)功能單元的一組元件,我們這里討論的組件是由html+js+css乃至其他資源組合,完善的能夠?qū)崿F(xiàn)一個(gè)功能且能重復(fù)使用的組合元件。
配置管理:提供一套基礎(chǔ)機(jī)制,通過配置文件修改,來進(jìn)行對(duì)項(xiàng)目的管理
統(tǒng)一管理和注入:本框架中將項(xiàng)目中所有的接口進(jìn)行統(tǒng)一配置和管理,包括組件所需組件的配置和注入。
首先為推薦的項(xiàng)目布局:
app ---存放容器的目錄
component ---存放組件的目錄
testCom ---組件名稱目錄
css ---組件樣式
img ---組件使用圖片
js ---組件使用的腳本
tpl ---組件的html標(biāo)簽
description.txt ---組件的描述說明
rely ---存放ui.js和ui.config文件的目錄
resources ---存放靜態(tài)資源的目錄
css ---全局使用樣式庫
img ---全局使用的圖片庫
lib ---第三方引動(dòng)庫
圖片:
結(jié)構(gòu)布局的思想主要使用的分類和拆分思想
將類似結(jié)構(gòu)的構(gòu)造進(jìn)行分離歸納,這樣對(duì)于每個(gè)布局的查找和分布都很好
將組件和容器分開來,是為了防止高耦合,改動(dòng)容器和組件,不會(huì)對(duì)頁面其他布局和組件影響
配置文件如下:
ui.config({ //注入頁面容器 container:{ //名稱:容器地址+是否裝載(PS:如果都為true只會(huì)選擇第一個(gè)模板容器) "layout1":["app/demoPage1.tpl",true], "layout2":["app/demoPage2.tpl",false] }, //注入接口 interface:{ "interface1":"www.123.com/interface1111111", "interface2":"www.123.com/interface2", "interface3":"www.123.com/interface3", "interface4":"www.123.com/interface4", "interface5":"www.123.com/interface555555555555", }, //注入組件 component:{ //組件名:組件模板+組件樣式+組件腳本+接口注入+組件是否裝載 ===> 該處可優(yōu)化針對(duì)本地項(xiàng)目和分布式開發(fā)進(jìn)行優(yōu)化 "test":["component/test/tpl/test.tpl","component/test/css/test.css","component/test/js/test.js",["interface1","interface2"],true], "test1":["component/test1/tpl/test1.tpl","component/test1/css/test1.css","component/test1/js/test1.js",["interface5","interface2"],true], "test2":["component/test2/tpl/test2.tpl","component/test2/css/test2.css","component/test2/js/test2.js",["interface1","interface2"],true], "test3":["component/test3/tpl/test3.tpl","component/test3/css/test3.css","component/test3/js/test3.js",["interface1","interface2"],true], "test4":["component/test4/tpl/test4.tpl","component/test4/css/test4.css","component/test4/js/test4.js",["interface1","interface2"],true], "test5":["component/test5/tpl/test5.tpl","component/test5/css/test5.css","component/test5/js/test5.js",["interface1","interface2"],true] }, //容器組件映射關(guān)系 選擇的容器名稱:{"頁面容器":"所加載的組件"} con_com:{ layout1:{ con1:"test", con2:"test1", con3:"test2", con4:"test3", con5:"test4", con6:"test5", }, layout2:{ con1:"test", con2:"test", con3:"test", con4:"test", con5:"test", con6:"test", } } });
針對(duì)配置文件的解釋:
1. container
為頁面布局容器,該tpl模板中全部為ui設(shè)計(jì)好的容器,框架加載的時(shí)候首先會(huì)去查找開發(fā)者設(shè)置使用的哪一種布局,然后解析模板中的容器。該容器是為了整個(gè)頁面換布局設(shè)計(jì)的。如果整個(gè)網(wǎng)站需要轉(zhuǎn)換頁面布局,傳統(tǒng)的做法就是重新設(shè)計(jì)重新編碼,然后搞完了重新部署。而該框架的設(shè)計(jì)思路,就是你重新設(shè)計(jì)一套布局容器,然后在該注入選項(xiàng)中注入你的新的布局容器,然后重新配置新的容器和適配組件的綁定關(guān)系,然后就會(huì)自動(dòng)加載新的布局。
代碼實(shí)例(全部使用ui-con的值來設(shè)置容器名稱):
<div style="" ui-con="con1"></div> <div style="" ui-con="con2"></div> <div style="margin:0 auto;width:1000px;padding:0;border:0;"> <div style="display: inline-block;width: 755px;" ui-con="con3"></div> <div style="display: inline-block;vertical-align: top;width:200px;padding:10px;" ui-con="con4"></div> </div> <div style="" ui-con="con5"></div> <div style="" ui-con="con6"></div>
2. interface
該項(xiàng)為了全局統(tǒng)一管理接口。因?yàn)閭鹘y(tǒng)的頁面開發(fā)中,如果一個(gè)接口換了,整個(gè)項(xiàng)目中所有用到該接口的ajax等各種請(qǐng)求都必須一個(gè)一個(gè)的換,所以該處將所有的接口提取出來,做一個(gè)全局管理。并且只要組件所需解口進(jìn)行了注入配置,開發(fā)者只需要在每個(gè)組件的js中在use的第一個(gè)data參數(shù)中就可以拿到該接口地址。
組件注入接口實(shí)例(data中會(huì)有注入的接口地址):
use(function(data,that){ var tempObj ={ //reader為一些初始化需要的操作,有時(shí)候會(huì)有注冊事件等,或者一些預(yù)操作,該方法加載完成后會(huì)直接跑起來 reader:function(){ that = this; that.firm.testLoad(); }, //注入所有的選擇器,方便選擇器變化,直接修改該對(duì)象中的選擇器,而不需要全局去更改 selector:{ testBtn:"#testBtn", //按鈕 }, //注入page中所有的事件,統(tǒng)一管理,建議命名規(guī)范:事件_命名,例 click_login registerEle:{ }, //注入所有ajax請(qǐng)求,頁面所有請(qǐng)求,將在這里統(tǒng)一管理,建議命名規(guī)范:ajax_命名,例 ajax_login ajaxRequest:{ }, //處理所有回調(diào)函數(shù),針對(duì)一個(gè)請(qǐng)求,處理一個(gè)回調(diào) callback:{ }, //臨時(shí)緩存存放區(qū)域,僅針對(duì)本頁面,如果跨頁面請(qǐng)存放cookie或者localstorage等 //主要解決有時(shí)候會(huì)使用頁面控件display來緩存當(dāng)前頁面的一些數(shù)據(jù) temp:{ }, /* * 業(yè)務(wù)使用區(qū)域,針對(duì)每個(gè)特別的業(yè)務(wù)去串上面所有的一個(gè)個(gè)原子 * 因?yàn)樯厦嫠械姆椒ǎ皇亲鲆患?,這邊可以根據(jù)業(yè)務(wù)進(jìn)行串服務(wù),很簡單的 * */ firm:{ testLoad:function(){ console.log("獲取接口的值:"+data.interface.interface1) } } }; ui.component.reader(tempObj); });
3. component
注入的所有組件。該組件有2中形態(tài)。針對(duì)本項(xiàng)目(待優(yōu)化中)和針對(duì)分布式開發(fā)項(xiàng)目。該組件的配置為:組件模板+組件樣式+組件腳本+接口注入+組件是否裝載。配置好了這5個(gè)參數(shù)之后,框架會(huì)針對(duì)配置文件,進(jìn)行是否裝載,接口注入,和組件裝載,該方式已經(jīng)實(shí)現(xiàn)。如果作為本地項(xiàng)目的話,可以配置更精簡一點(diǎn),只要格式按照我的布局文件寫,可以直接配置一個(gè)組件的根目錄就好了,就會(huì)自動(dòng)尋找,不過該設(shè)計(jì)待優(yōu)化中,還沒有做好。這塊加載組件模板用的技術(shù)就是ajax技術(shù),css和script用的動(dòng)態(tài)標(biāo)簽。所以如果做分布式,請(qǐng)求組件的時(shí)候,就必須做跨域處理了,這邊就不詳細(xì)描述,如果該組件做的功能請(qǐng)求的接口也支持跨域,那么這個(gè)功能就在其他項(xiàng)目中就可以實(shí)現(xiàn)了,做到了真正的分布式協(xié)作開發(fā)了,而本項(xiàng)目就是作為一個(gè)流量入口,接入互聯(lián)網(wǎng)上所有分布式開發(fā)的資源了
4. con_com
該配置文件為容器-組件綁定關(guān)系,為你每個(gè)注入的容器配置所需要加載的組件,該項(xiàng)可以根據(jù)容器進(jìn)行配置,也必須配置。如果所配置的組件,在組件配置文件中未注入,框架不會(huì)直接停止解析,而是會(huì)在控制臺(tái)拋錯(cuò),告訴你該組件配置的問題,然后繼續(xù)加載其他正確的容器-組件映射正確的模塊功能,詳細(xì)配置見配置文件。
ui.js框架核心代碼粗略解析
代碼結(jié)構(gòu)使用,因?yàn)樵谝粋€(gè)核心的代碼中肯定會(huì)遇到封裝很多的工具,比如dom操作,一般的tool,ajax等工具。我的選擇結(jié)構(gòu)就是將所有的工具,以參數(shù)的形式注入到匿名函數(shù)中,然后匿名函數(shù)執(zhí)行的就是ui.js的核心功能。代碼結(jié)構(gòu)如下:
(function(window,document,rely){ //rely就是注入的所有使用的工具,修改起來相對(duì)輕松,核心中使用這些工具,不需要在乎這些方法怎么實(shí)現(xiàn),只需要知道這些方法會(huì)達(dá)成我的目的就好了})(window,document,(function(){ var tool={}; var ajax={}; return { tool=tool, ajax=ajax } })());
2. ui.config為入口進(jìn)行配置文件的解析
/* * 內(nèi)部使用一些快捷標(biāo)識(shí)符 * 0. _ 代表dom操作 * 1. $ 代表全局注入的工具類 * 2. $1 代表注入的ajax類庫 * 3. $2 代表模板處理類庫 * 4. $3 代表所有錯(cuò)誤處理信息 * * * */ var _ = relyObj.dom,$ = relyObj.tool,$1 = relyObj.ajax,$2 = relyObj.template,$3 =relyObj.errMsg,$4 =relyObj.htmlModule,that={},ui,UI_global; ui = UI_global = { //對(duì)所有參數(shù)進(jìn)行處理 config:function(configObject){ // 1. 初始化數(shù)據(jù)池 ui.dataPool.initPool(); if(configObject === undefined){ throw new Error($3.gloErr.noConfig); }; // 2. 將配置文件存儲(chǔ)到數(shù)據(jù)池中,做備用 ui.dataPool.setData_glo("config",configObject); // 3. 檢查加載頁面容器,加載到body中 $.each(configObject.container,function(value,key){ if(value[1] === true){ ui.dataPool.setData_glo("private",{"pageConName":key}); ui.container.loadContainer(key,value[0],function(data){ _("body").html(data); dealWithCom(); return; }); } }); //處理組件方法 var dealWithCom = function(){ // 4. 處理配置容器和組件映射關(guān)系,取得所有容器所要加載組件的信息 var temp = ui.dataPool.getData_glo("private","pageConName"); //取得配置文件中關(guān)于當(dāng)前容器中的容器-組件對(duì)應(yīng)關(guān)系 var tempS = ui.dataPool.getData_glo("config","con_com",temp); // 5. 判斷組件是否存在,存在即加載組件 $.each(tempS,function(value,key){ //判斷組件是否配置 var getComInfo = ui.component.isExist_com(value); if(getComInfo){ if (getComInfo[4]){ ui.component.loadComponent(value,getComInfo[0]); }else { var height = _("[ui-con='"+key+"']").css("height"); _("[ui-con='"+key+"']").html($4.loadErr("line-height:"+height)); } }else { console.log($3.component.comConfig(value)); } }); }; },
3. 數(shù)據(jù)中轉(zhuǎn)池的核心(以對(duì)象方式存數(shù)數(shù)據(jù),而不是數(shù)組,因?yàn)閿?shù)據(jù)需要遍歷,增加很多壓力),這個(gè)是最重要的,因?yàn)檎麄€(gè)框架所依賴的數(shù)據(jù)處理都在這里面,它還沒有很完善一些細(xì)節(jié)還是需要推敲的,比如說以后拒絕開發(fā)者修改一些內(nèi)部使用中的數(shù)據(jù),因?yàn)檫@些數(shù)據(jù)是框架內(nèi)部處理使用,而非外部,修改不當(dāng)容易造成整體崩潰。它規(guī)定一些重要的參數(shù),以及向數(shù)據(jù)池增加參數(shù)和得到數(shù)據(jù)池的參數(shù)的方法和規(guī)定
/* * 數(shù)據(jù)中轉(zhuǎn)池核心處理庫(以對(duì)象方式去存儲(chǔ),這樣方便快捷的取數(shù)據(jù)) * 數(shù)據(jù)格式: * 1. mapping(name + uuid )映射,每個(gè)name對(duì)應(yīng)一個(gè)uuid 注:每加載一個(gè)頁面容器生成一個(gè)uuid,每次加載一個(gè)組件,生成一個(gè)組件的uuid,盡量,按需創(chuàng)建和使用 * 2. pool(uuid + data )映射,每個(gè)uuid對(duì)應(yīng)儲(chǔ)存一段數(shù)據(jù) * a. interface,注入接口信息 * b. transfer,流轉(zhuǎn)過來的數(shù)據(jù) * 3. pageContainer,存儲(chǔ)當(dāng)前加載的頁面中所有的頁面容器 * 4. global_temp,全局存儲(chǔ)的臨時(shí)數(shù)據(jù) * 5. config,存儲(chǔ)用戶配置的參數(shù) * 6. private,存儲(chǔ)UI.js本身處理過的數(shù)據(jù),內(nèi)部使用 (需要加載的組件) * * */ dataPool:{ //初始化數(shù)據(jù)池,將數(shù)據(jù)池都清空 initPool:function(){ that["dataCenter"]={ mapping:{}, //數(shù)據(jù)例子: "module":"ffb71d7f-995b-4898-8e4b-b283a4fe6253" pool:{}, //數(shù)據(jù)例子:"ffb71d7f-995b-4898-8e4b-b283a4fe6253":{索要存的對(duì)象} pageContainer:{}, //數(shù)據(jù)例子:"name":"http://xxxxx.com" global_temp:{}, //數(shù)據(jù)例子:"Test":"123321" 做全局緩存用 config:{}, //數(shù)據(jù)例子:值為config中的配置參數(shù)值 private:{} //數(shù)據(jù)例子:內(nèi)部使用參數(shù),處理ui.js的內(nèi)部流程 }; }, //檢查是否為數(shù)據(jù)池全局拒絕修改的參數(shù) (待定) checkParam_glo:function(name){ var data =["interface","config","private"],len = data.length; while(len--> 0){ if(name === data[len]){ throw new Error($3.pool.forbidUpdate(name)); } } return true; }, /* * 數(shù)據(jù)池設(shè)置數(shù)據(jù)池全局的值(該方法為了固定數(shù)據(jù)池的參數(shù),防止數(shù)據(jù)錯(cuò)亂) http://www.cnblogs.com/GerryOfZhong/p/6383175.html