avalon.oniui
avalon.oniui copied to clipboard
avalon工程化相关
怎么评价淘宝 UED 的 Midway Framework? http://www.zhihu.com/question/23512853/answer/27523986?utm_source=weibo&utm_medium=weibo_share&utm_content=share_answer&utm_campaign=share_button
http://wenku.baidu.com/view/9f260449bed5b9f3f90f1c83.html
rank,前端开发跨界者@FEX 细节不描述,自行 Google 或 Baidu,资料很多。 我个人的经验认为,大量代码主要有 3 个方面问题: 程序结构上与代码管理问题。这也是最重要的问题,怎么样多人并行开发,并且相互之前物理模块与组件相互不影响。 其次是开发一致性与更快定位问题。包括 Logging, Debugging 等。也包括软件开发代码一致性问题,比如共性的浏览器兼容、代码的一致性都是属于此类。 其他的 Utility 工具函数或 UI 组件本质上应该不算是框架,但可能占据了框架的绝大多数代码,也就是解决复用性问题。
avalon工程化的相关提案
前端领域技术问题比较少,工程问题比较多。avalon通过“操作数据即操作DOM”的理念,已经将最复杂的DOM,BOM摒蔽掉,那么技术问题就更少了。
现在在观看大家写的代码,基本上是这个patten:每一个模块,最开始部分是各种require,然后是一个ajax请求, 在数据回来后,定义一个VM,将它数据mix进去,最后是扫描。
这样的写法是导致我们的逻辑充满了异步,并且看不到VM到底定义什么属性。由于我们的业务非常复杂,VM定义也非常长,在页面上看到一个属性, 跑回JS文件里找它,也非常难找(当然这可以借助快捷键来查找)。
为此,我制定如下规则:
每一个JS文件组织如下:
//变量名以字母的顺序 _(私有)最前, $(非监控,系统级)随后, 大写字母(常量)第三,小写字母最后
var aaa = require("xxx")
var bbb = require("tmp.string")
var ccc = require("xxx2")
var vmodel = avalon.define("test", function(vm){
//vm的属性必须提前定义好
//也是以字母的顺序定义
//VM的属性分两种,
//一种是数据,在不确定的情况下使用伪数据作值,数组用[],字符串用空字符串,布尔用false,数组用零,
//如果是一个子对象,那么它的数据也像上面那样用依数据填空,真数据则直接用
//另一种是方法与$watch回调,
vm.aaa = 0 //这里加注释
vm.arr = [] //这里加注释
vm.bbb = "" //这里加注释
vm.bool = false //这里加注释
vm.obj = {
aaa: ""
}
vm.set = function(){}
})
jQuery.ajax(url, function(data){
for(var i in data){
if(vmodel.hasOwnerProperty(i) )
vmodel[i] = data[i] //添加真数据
}
}
})
avalon.scan()
如此一来,后来维护的人就非常方便查找属性,也一眼就知道有什么功能
原来是在VM上定义一个空对象,然后在AJAX回调里直接vm.data = data 这会造成多一层的子对象,并且我们要debug一下,才知道这个data里有什么属性
现在直接自己在vm中以预定义,由于业务的原因,可以这个define内部非常长 那么怎么排这些属性与方法就要有个基准了,没有比这顺序更好了
var a = {
perPages: 10, //每页包含多少条目
showPages: 10, //要显示的页面的数量,从1开始
currentPage: 1, //当前被高亮的页面,从1开始
_currentPage: 1,
totalItems: 200, //总条目数
totalPages: 0, //总页数,通过Math.ceil(vm.totalItems / vm.perPages)求得
pages: [], //要显示的页面组成的数字数组,如[1,2,3,4,5,6,7]
nextText: ">",
prevText: "<",
ellipseText: "…",
firstPage: 0, //当前可显示的最小页码,不能小于1
lastPage: 0, //当前可显示的最大页码,不能大于totalPages
alwaysShowNext: false, //总是显示向后按钮
alwaysShowPrev: false, //总是显示向前按钮
showJumper: false, //是否显示输入跳转台
getTemplate: function(tmpl, opts) {
return tmpl
},
onJump: function(){}, //页面跳转时触发的函数
getTitle: function(a) {
switch (a) {
case "first":
return "Go To First Page"
case "prev":
return "Go To Previous Page"
case "next":
return "Go To Next Page"
case "last":
return "Go To Last Page"
default:
return "Go to page " + a + ""
}
}
}
var names = Object.keys(a).sort(function(k1, k2){
return k1.localeCompare(k2)
})
var b = {}
for(var i =0, n = names.length; i < n; i++){
var key = names[i]
b[key] = a[key]
console.log(key+":"+a[key]+",")
}
处理配置顺序问题,包括注释
var fs = require('fs'),
path = require('path');
var lstatSync = (process.platform === "win32" ? "stat" : "lstat") + 'Sync';
var basePath = 'avalon.oniui/'; // 工程目录
function findout(str, char) {
var rs = 0;
for (var i = 0, l = str.length; i < l; i ++) {
if (str[i] === char) {
rs ++;
}
}
return rs;
}
fs.readdirSync(basePath).forEach(function(d) {
if (fs.existsSync(path.join(basePath, d, 'avalon.' + d + '.js'))) {
fs.readdirSync(path.join(basePath, d)).forEach(function(file) {
if (/avalon\.\w+\.js/.test(file)) {
var content = fs.readFileSync(path.join(basePath, d, file), 'utf-8');
if (~content.indexOf('widget.defaults =')) {
var options = {},
keys = [], curKey, tmp = [], tag = 1,
lines = content.split('\n'),
startLine = content.substring(0, content.indexOf('widget.defaults =')).split('\n').length,
total = 0,
output = [],
num = startLine,
topComment = [];
while (!~lines[num].indexOf(':')) {
topComment.push(lines[num]);
num ++;
}
for(num = startLine; lines[num] && tag; num ++, total ++) {
tag = tag + findout(lines[num], '{') - findout(lines[num], '}');
if (tag) {
tmp.push(lines[num]);
var match = lines[num].match(/\S*"?(\w+)"?\S*:/i);
if (match) {
var newKey = match[0].match(/"?(\w+)"?\S*:/i)[1];
if (newKey && lines[num].trim().indexOf(newKey) < 2) {
keys.push(newKey);
options[newKey] = [];
if (curKey && options[curKey].length) {
while (options[curKey].length && options[curKey][options[curKey].length - 1].trim().indexOf('//') === 0) {
options[newKey].push(options[curKey].pop());
}
} else if (topComment.length) {
options[newKey] = topComment;
}
curKey = newKey;
}
}
if (curKey) {
options[curKey].push(lines[num]);
}
}
}
keys.sort();
keys.forEach(function(key, index) {
var lastLine = options[key][options[key].length - 1],
ds = lastLine.split('//'),
code = ds[0].trim();
if (index === keys.length - 1) {
if (code[code.length - 1] == ',') {
if (ds.length > 1) {
options[key][options[key].length - 1] = lastLine.replace(/\,\s*\/\//, function(a){
return a.substring(1);
});
} else {
options[key][options[key].length - 1] = lastLine.substring(0, lastLine.lastIndexOf(','));
}
}
} else {
if (code[code.length - 1] != ',') {
if (ds.length > 1) {
options[key][options[key].length - 1] = lastLine.replace(/\s*\/\//, function(a){
return ',' + a;
});
} else {
options[key][options[key].length - 1] += ',';
}
}
}
output = output.concat(options[key]);
});
lines.splice(startLine, total - 1, output.join('\n'));
fs.writeFileSync(path.join(basePath, d, file.replace('.js', '.fixed.js')), lines.join('\n'), 'utf-8');
}
}
});
}
});
兼容大部分情况,会在同目录生成一个*.fixed.js的文件。
怕有特殊情况,人为合并代码。
- 工程上只维护一套代码(工程友好)
- 写码时感觉不到框架的存在(框架透明)
- 首次访问是html完整输出(SEO、性能友好)
- 页面跳转是ajax化请求(交互体验友好)
- 跳转后的url直接刷新输出的是html(交互体验、性能友好)
目前比较接近这些需求的,就是bigpipe+quickling+pushState这一套了,框架成本很高
gulp 的另一个修改代码后实时更新页面插件browsersync, http://www.browsersync.io/ 与livereload 不同, browsersync 不需要浏览器自动刷新页面,就能更新css的修改,做SPA非常方便, 就因为这点livereload插件用了一段时间做SPA就不用了,刷新页面后不方便 http://www.html5dw.com/building-with-gulp/
//https://github.com/fex-team/fis-kernel/blob/master/lib/util.js#L442-L453
这个read函数,读取任何源码都会被处理为utf-8在内存中运行,等到工具处理完内容,再发布的时候,又可以再转换成任意编码 构建工具先抹平编码差异,这样无论工程师开发中无论ide怎样设置,都不会有问题,gbk源码和utf-8/BOM源码可以很好的混在一起开发。
http://www.html5dw.com/building-with-gulp/
几年前苹果的 UI Guidelines 里说过:移动端触控区域最小为 44 x 44px,特殊情况下至少有一侧能达到 44px。这几天 Review 内部原型的时候发现好多应该用 padding 扩大的空间都被 margin 给代替了,还有些其它写法导致可操作区域很小。#妈蛋,在 PC 上开发代码的童鞋完全就木有 care 过这个问题# [拜拜]
http://saebbs.com/forum.php?mod=viewthread&tid=28688
JS知识点