avalon.oniui icon indicating copy to clipboard operation
avalon.oniui copied to clipboard

avalon工程化相关

Open RubyLouvre opened this issue 10 years ago • 13 comments

怎么评价淘宝 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

RubyLouvre avatar Jul 03 '14 06:07 RubyLouvre

http://wenku.baidu.com/view/9f260449bed5b9f3f90f1c83.html

RubyLouvre avatar Jul 10 '14 17:07 RubyLouvre

rank,前端开发跨界者@FEX 细节不描述,自行 Google 或 Baidu,资料很多。 我个人的经验认为,大量代码主要有 3 个方面问题: 程序结构上与代码管理问题。这也是最重要的问题,怎么样多人并行开发,并且相互之前物理模块与组件相互不影响。 其次是开发一致性与更快定位问题。包括 Logging, Debugging 等。也包括软件开发代码一致性问题,比如共性的浏览器兼容、代码的一致性都是属于此类。 其他的 Utility 工具函数或 UI 组件本质上应该不算是框架,但可能占据了框架的绝大多数代码,也就是解决复用性问题。

RubyLouvre avatar Jul 14 '14 01:07 RubyLouvre

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内部非常长 那么怎么排这些属性与方法就要有个基准了,没有比这顺序更好了

RubyLouvre avatar Jul 23 '14 12:07 RubyLouvre

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]+",")
}

RubyLouvre avatar Jul 24 '14 07:07 RubyLouvre

处理配置顺序问题,包括注释

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的文件。

怕有特殊情况,人为合并代码。

EdwonLim avatar Jul 24 '14 10:07 EdwonLim

  1. 工程上只维护一套代码(工程友好)
  2. 写码时感觉不到框架的存在(框架透明)
  3. 首次访问是html完整输出(SEO、性能友好)
  4. 页面跳转是ajax化请求(交互体验友好)
  5. 跳转后的url直接刷新输出的是html(交互体验、性能友好)

目前比较接近这些需求的,就是bigpipe+quickling+pushState这一套了,框架成本很高

RubyLouvre avatar Jul 30 '14 07:07 RubyLouvre

image image

image image

image image

image

image image image image

RubyLouvre avatar Oct 15 '14 11:10 RubyLouvre

image

RubyLouvre avatar Oct 16 '14 09:10 RubyLouvre

gulp 的另一个修改代码后实时更新页面插件browsersync, http://www.browsersync.io/ 与livereload 不同, browsersync 不需要浏览器自动刷新页面,就能更新css的修改,做SPA非常方便, 就因为这点livereload插件用了一段时间做SPA就不用了,刷新页面后不方便 http://www.html5dw.com/building-with-gulp/

RubyLouvre avatar Oct 22 '14 03:10 RubyLouvre

//https://github.com/fex-team/fis-kernel/blob/master/lib/util.js#L442-L453

这个read函数,读取任何源码都会被处理为utf-8在内存中运行,等到工具处理完内容,再发布的时候,又可以再转换成任意编码 构建工具先抹平编码差异,这样无论工程师开发中无论ide怎样设置,都不会有问题,gbk源码和utf-8/BOM源码可以很好的混在一起开发。

RubyLouvre avatar Oct 24 '14 03:10 RubyLouvre

http://www.html5dw.com/building-with-gulp/

RubyLouvre avatar Oct 24 '14 03:10 RubyLouvre

几年前苹果的 UI Guidelines 里说过:移动端触控区域最小为 44 x 44px,特殊情况下至少有一侧能达到 44px。这几天 Review 内部原型的时候发现好多应该用 padding 扩大的空间都被 margin 给代替了,还有些其它写法导致可操作区域很小。#妈蛋,在 PC 上开发代码的童鞋完全就木有 care 过这个问题# [拜拜]

RubyLouvre avatar Nov 05 '14 15:11 RubyLouvre

http://saebbs.com/forum.php?mod=viewthread&tid=28688

JS知识点

RubyLouvre avatar Nov 05 '14 15:11 RubyLouvre