有朋友提出一看来是懵逼的,根本不知道什么是面向UI编程的思想,下面是我之前写的博客,描述的这中思想,下面为地址,参考下就明了很多了。
1. 前端思想实现:面向UI编程
2. 面向UI编程框架:ui.js框架思路详细设计
时隔第一次被UI思路激励,到现在1.0的粗糙版本发布,掐指一算整整半年了。半年之间,有些细节不断推翻重做,再推翻再重做。时隔今日,终于能先出来个东西了,这个版本很粗糙,主体功能大概能实现了,但是还是有很多很多的问题。不过有问题没事,可以进行修改完善,这是相对轻松的问题,最艰难的从无到有的创造才是最艰难的。好了,不废话了,我们直接进入正题 --- UI.js功能介绍。
首先介绍几个概念
分布式:原来的概念只有分布式计算,它研究如何把一个需要非常巨大的计算能力才能解决的问题分成许多小的部分,然后把这些部分分配给许多计算机进行处理,最后把这些计算结果综合起来得到最终的结果。这边要实现的概念是,
分布式协作开发:就是将一个项目分拆多个组件,可从不同的服务器上拉取,分布协作开发
容器:容器用来包装或装载物品的贮存器,从web角度来讲,web容器是应用服务器中位于组件和平台之间的接口集合。
组件:供装配整台机器、构件或元件的零件组合或者在电子或机械设备中组装在一起形成一个功能单元的一组元件,我们这里讨论的组件是由html+js+css乃至其他资源组合,完善的能够实现一个功能且能重复使用的组合元件。
配置管理:提供一套基础机制,通过配置文件修改,来进行对项目的管理
统一管理和注入:本框架中将项目中所有的接口进行统一配置和管理,包括组件所需组件的配置和注入。
首先为推荐的项目布局:
app ---存放容器的目录
component ---存放组件的目录
testCom ---组件名称目录
css ---组件样式
img ---组件使用图片
js ---组件使用的脚本
tpl ---组件的html标签
description.txt ---组件的描述说明
rely ---存放ui.js和ui.config文件的目录
resources ---存放静态资源的目录
css ---全局使用样式库
img ---全局使用的图片库
lib ---第三方引动库
图片:
结构布局的思想主要使用的分类和拆分思想
将类似结构的构造进行分离归纳,这样对于每个布局的查找和分布都很好
将组件和容器分开来,是为了防止高耦合,改动容器和组件,不会对页面其他布局和组件影响
配置文件如下:
ui.config({ //注入页面容器 container:{ //名称:容器地址+是否装载(PS:如果都为true只会选择第一个模板容器) "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:{ //组件名:组件模板+组件样式+组件脚本+接口注入+组件是否装载 ===> 该处可优化针对本地项目和分布式开发进行优化 "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] }, //容器组件映射关系 选择的容器名称:{"页面容器":"所加载的组件"} 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", } } });
针对配置文件的解释:
1. container
为页面布局容器,该tpl模板中全部为ui设计好的容器,框架加载的时候首先会去查找开发者设置使用的哪一种布局,然后解析模板中的容器。该容器是为了整个页面换布局设计的。如果整个网站需要转换页面布局,传统的做法就是重新设计重新编码,然后搞完了重新部署。而该框架的设计思路,就是你重新设计一套布局容器,然后在该注入选项中注入你的新的布局容器,然后重新配置新的容器和适配组件的绑定关系,然后就会自动加载新的布局。
代码实例(全部使用ui-con的值来设置容器名称):
<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
该项为了全局统一管理接口。因为传统的页面开发中,如果一个接口换了,整个项目中所有用到该接口的ajax等各种请求都必须一个一个的换,所以该处将所有的接口提取出来,做一个全局管理。并且只要组件所需解口进行了注入配置,开发者只需要在每个组件的js中在use的第一个data参数中就可以拿到该接口地址。
组件注入接口实例(data中会有注入的接口地址):
use(function(data,that){ var tempObj ={ //reader为一些初始化需要的操作,有时候会有注册事件等,或者一些预操作,该方法加载完成后会直接跑起来 reader:function(){ that = this; that.firm.testLoad(); }, //注入所有的选择器,方便选择器变化,直接修改该对象中的选择器,而不需要全局去更改 selector:{ testBtn:"#testBtn", //按钮 }, //注入page中所有的事件,统一管理,建议命名规范:事件_命名,例 click_login registerEle:{ }, //注入所有ajax请求,页面所有请求,将在这里统一管理,建议命名规范:ajax_命名,例 ajax_login ajaxRequest:{ }, //处理所有回调函数,针对一个请求,处理一个回调 callback:{ }, //临时缓存存放区域,仅针对本页面,如果跨页面请存放cookie或者localstorage等 //主要解决有时候会使用页面控件display来缓存当前页面的一些数据 temp:{ }, /* * 业务使用区域,针对每个特别的业务去串上面所有的一个个原子 * 因为上面所有的方法,只是做一件事,这边可以根据业务进行串服务,很简单的 * */ firm:{ testLoad:function(){ console.log("获取接口的值:"+data.interface.interface1) } } }; ui.component.reader(tempObj); });
3. component
注入的所有组件。该组件有2中形态。针对本项目(待优化中)和针对分布式开发项目。该组件的配置为:组件模板+组件样式+组件脚本+接口注入+组件是否装载。配置好了这5个参数之后,框架会针对配置文件,进行是否装载,接口注入,和组件装载,该方式已经实现。如果作为本地项目的话,可以配置更精简一点,只要格式按照我的布局文件写,可以直接配置一个组件的根目录就好了,就会自动寻找,不过该设计待优化中,还没有做好。这块加载组件模板用的技术就是ajax技术,css和script用的动态标签。所以如果做分布式,请求组件的时候,就必须做跨域处理了,这边就不详细描述,如果该组件做的功能请求的接口也支持跨域,那么这个功能就在其他项目中就可以实现了,做到了真正的分布式协作开发了,而本项目就是作为一个流量入口,接入互联网上所有分布式开发的资源了
4. con_com
该配置文件为容器-组件绑定关系,为你每个注入的容器配置所需要加载的组件,该项可以根据容器进行配置,也必须配置。如果所配置的组件,在组件配置文件中未注入,框架不会直接停止解析,而是会在控制台抛错,告诉你该组件配置的问题,然后继续加载其他正确的容器-组件映射正确的模块功能,详细配置见配置文件。
ui.js框架核心代码粗略解析
代码结构使用,因为在一个核心的代码中肯定会遇到封装很多的工具,比如dom操作,一般的tool,ajax等工具。我的选择结构就是将所有的工具,以参数的形式注入到匿名函数中,然后匿名函数执行的就是ui.js的核心功能。代码结构如下:
(function(window,document,rely){ //rely就是注入的所有使用的工具,修改起来相对轻松,核心中使用这些工具,不需要在乎这些方法怎么实现,只需要知道这些方法会达成我的目的就好了})(window,document,(function(){ var tool={}; var ajax={}; return { tool=tool, ajax=ajax } })());
2. ui.config为入口进行配置文件的解析
/* * 内部使用一些快捷标识符 * 0. _ 代表dom操作 * 1. $ 代表全局注入的工具类 * 2. $1 代表注入的ajax类库 * 3. $2 代表模板处理类库 * 4. $3 代表所有错误处理信息 * * * */ var _ = relyObj.dom,$ = relyObj.tool,$1 = relyObj.ajax,$2 = relyObj.template,$3 =relyObj.errMsg,$4 =relyObj.htmlModule,that={},ui,UI_global; ui = UI_global = { //对所有参数进行处理 config:function(configObject){ // 1. 初始化数据池 ui.dataPool.initPool(); if(configObject === undefined){ throw new Error($3.gloErr.noConfig); }; // 2. 将配置文件存储到数据池中,做备用 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. 处理配置容器和组件映射关系,取得所有容器所要加载组件的信息 var temp = ui.dataPool.getData_glo("private","pageConName"); //取得配置文件中关于当前容器中的容器-组件对应关系 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. 数据中转池的核心(以对象方式存数数据,而不是数组,因为数据需要遍历,增加很多压力),这个是最重要的,因为整个框架所依赖的数据处理都在这里面,它还没有很完善一些细节还是需要推敲的,比如说以后拒绝开发者修改一些内部使用中的数据,因为这些数据是框架内部处理使用,而非外部,修改不当容易造成整体崩溃。它规定一些重要的参数,以及向数据池增加参数和得到数据池的参数的方法和规定
/* * 数据中转池核心处理库(以对象方式去存储,这样方便快捷的取数据) * 数据格式: * 1. mapping(name + uuid )映射,每个name对应一个uuid 注:每加载一个页面容器生成一个uuid,每次加载一个组件,生成一个组件的uuid,尽量,按需创建和使用 * 2. pool(uuid + data )映射,每个uuid对应储存一段数据 * a. interface,注入接口信息 * b. transfer,流转过来的数据 * 3. pageContainer,存储当前加载的页面中所有的页面容器 * 4. global_temp,全局存储的临时数据 * 5. config,存储用户配置的参数 * 6. private,存储UI.js本身处理过的数据,内部使用 (需要加载的组件) * * */ dataPool:{ //初始化数据池,将数据池都清空 initPool:function(){ that["dataCenter"]={ mapping:{}, //数据例子: "module":"ffb71d7f-995b-4898-8e4b-b283a4fe6253" pool:{}, //数据例子:"ffb71d7f-995b-4898-8e4b-b283a4fe6253":{索要存的对象} pageContainer:{}, //数据例子:"name":"http://xxxxx.com" global_temp:{}, //数据例子:"Test":"123321" 做全局缓存用 config:{}, //数据例子:值为config中的配置参数值 private:{} //数据例子:内部使用参数,处理ui.js的内部流程 }; }, //检查是否为数据池全局拒绝修改的参数 (待定) 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; }, /* * 数据池设置数据池全局的值(该方法为了固定数据池的参数,防止数据错乱) http://www.cnblogs.com/GerryOfZhong/p/6383175.html