GithubHelp home page GithubHelp logo

aralejs / aralejs.github.io Goto Github PK

View Code? Open in Web Editor NEW
1.4K 1.4K 333.0 12.47 MB

开放、简单、易用的前端基础类库

Home Page: http://aralejs.github.io

License: MIT License

HTML 24.27% JavaScript 42.59% CSS 33.14%

aralejs.github.io's People

Contributors

afc163 avatar bitdeli-chef avatar lepture avatar lizzie avatar popomore avatar sorrycc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aralejs.github.io's Issues

Widget 系分

Widget系分

Widget目前的思路是, 提供一定的生命周期, 在创建过程中调用相应模块提供的方法, 来构建出我们的组件.
通过主要有以下几个模块构成

核心模块

主要有下面几个模块, 其中对应的模块后面会有详细的解释

  1. Model
  2. Template
  3. DAParser
  4. Action
  5. Widget

Model

数据模型. 这个通过对数据的包装提供更灵活的方式进行使用. 目前的主要功能类似于Backbone.

API

  1. set
  2. get
  3. initialize
  4. has
    ….
var Model = require('widget').Model; 
var model = new Model({
    name: 'alipay',
    age: 20
});
var name = model.get('name');

Template

模板封装类. 通过提供Model和对应的template.创建出dom对象

API

  1. toDom();
var Tpl = require('widget').Template;
var srcNode = Tpl.toDom(tplStr, model);

DAParser

这个主要也是针对于data-api的一个扩展, 可以根据提供一个Dom元素, 会分析出来相应的数据模型.

API

  1. ATTRS 指定那些属性被parse. 如不指定, 则不进行parse.
  2. defaultParser 指定默认的parse功能. 这个讲覆盖默认的parse行为.
  3. parse parse 指定的dom.
    <div id="myDropDown">
        <a href="#" data-action-trigger="click">say</a>
        <div class="content" data-opt-width="200px" data-opt-height="300px">
        </div>
    </div>
    //创建一个parser实例, 并制定过滤属性
    var DAParser = require('widget').DAParser.createParser(['opt1', 'action']);
    var parserObj = DAParser.parse(srcNode);
    //如果我们的srcNode为上面的属性. 那么我们将返回类似下面的对象
    /**
        parserObj = {
            opts: {
                width: "200px",
                height: "300px"
            },
            action: {
                trigger: "click"
            }
        }
    **/

Action

行为扩展对象, 对我们组件的行为扩展抽象出Action. 后续的行为扩展都会被这个对象进行管理

API

  1. addAction 添加行为
  2. applyAll 执行所有的行为
    var action = require('widget').Action();
    action.addAction( function(srcNode, widget) {
        $(srcNode).click( function() {
            widget.show();
        });
    });
    action.applyAll();

Widget

widget核心类. 定义组件的基本生命周期, 并在生命周期中相应的阶段去组合上面相关模块的功能, 并提供相应的接口供用户进行扩展.

API

  1. initModel 初始化数据对象
  2. initTemplate 初始化模板
  3. beforeCreate srcNode已经生成完成后, 触发的操作
  4. parseAction 针对通过DAParser分析出来的action, 转换为组件可以识别的行为
  5. addAction 添加行为
  6. postCreate 组件已经初始化完成后自动触发的操作.
define('widget', [], function(require, exports, module) {
   var Base = require('base');
   var Model = require('./model.js'); 
   var Tpl = require('./template.js');
   var DAParser = require('./daparser.js');
   var Action = require('./action.js');
   module.exports = Base.exend({
       data-attrs: [],
       action: new Action(), 
       initialize: function(opts) {
           this.model = this.initModel(opts);
           this.srcNode = opts.srcNode || this.initTemplate(opts);
           var parsedObj = DAParser(this.data-arrts).parse(this.srcNode);//子类需要提供覆盖.
           this.setOptions(parsedObj.opts);
           this.setOptions(opts);

           //这里已经初始化完毕, 相关用户配置选项已经获取. 我么可以由用户在这类先扩展一部分操作, 比如
           //根据传入的参数, 去初始化其他对象.
           this.beforeCreate();
           this.parseActions(); //
           this.bind();
           this.trigger('initialized');
           this.postCreate();
       },
       initModel: function(opts) {
           //从初始化opt中找到和Model相关的属性, 来初始化Model
           //用户可以自己覆盖, 初始化自己的model
       },
       initTemplate: function() {
           // 从初始化的opt中找到是否有模板相应信息, 

       },
       beforeCreate: function() {

       },
       parseAction: function(acts) {
           //根据从domNode里面parse出来的action, 来形成真正的action.
           // 需要子类去覆盖.
       },
       addAction: function(action) {
           //如果当前组件已经初始化, 那么新加入的action, 会被立即执行
           this.action.addAction(action)
       },
       bind: function() {
           action.applyAll(this.srcNode, this);
       },
       postCreate: function() {
           //需要子类覆盖. 比如需要把srcNode插入相应的位置, 显示啥的.
       }
   });

Base 系分

由于涉及的代码比较多,记录在这里比较合适。

使用场景

  1. 创建一个类
  2. 添加事件支持
  3. 添加 Options 支持

竞争对手

  1. http://yuilibrary.com/yui/docs/guides/
  2. http://mootools.net/docs/core/Class/

API 设计

base 组件包含三个文件:

class.js  -- 提供 OOP
observable.js -- 事件
optionable.js  -- 选项
base.js

最常见的使用方式:

Switchable.js:

define(function(require, exports, module) {

    var Base = require('base');

    var Switchable = Base.extend({

        options: {
            zIndex: 9999,
            size: {
                 width: 300,
                 height: 400
            }
        },

        initialize: function(options) {
            this.setOptions(options);
        },

       show: function() {
           ...
       }
  });

 module.exports = Switchable;
});

Carousel.js:

define(function(require, exports, module) {

    var Switchable = require('./switchable');

    var Carousel = Switchable.extend({

        implements: ['XXable']

        options: {
            closeBtn: true,
            size: {
                 width: 800,
                 height: 600
            }
        },

        initialize: function(options) {
            this.superclass.initialize.apply(this, arguments);

            this.setOptions(options);
        },

       setContent: function() {
           ...
       }
  }, staticProperties);


   // Carousel.__meta  {  id, deps }

  // 还可以通过 implement 添加 instance 属性
  Carousel.implement({

      loadContent: function() {
      }

  });


 module.exports = Carousel;
});

使用实例:

define(function(require, exports, module) {

   var Carousel = require('carousel');

   var carousel = new Carousel(...);



});

XX.js:

define(function(require, exports, module) {

    var Base = require('base');

    var XX = Base.Class.create({

         inherits: YY,
         implements: Base.Observable,

         initialize: fn

    });

    XX.extend / implement

});

组件依赖

开发计划

  • 第一版:04.26 - 04.28
  • 文档等:05.02 - 05.04

在产品中加载的模块版本是在单独的文件配置的, 如何在seajs适配目前的版本映射关系.

在原来的版本处理机制是在一个js里面我们已经生成了依赖映射关系, 一般是在${artifactId}-${version}-index.js 文件中. 类似与下面的语句

deps.addDependency('aralex.xbox',['aralex.xbox-1.7.js','aralex.base-1.1.js','arale.class-1.0.js',
'arale.aspect-1.0.js','arale.tmpl-1.0.js','arale.event-1.1.js','arale.fx-1.1.js','arale.dom-1.1.js',
'arale.string-1.0.js','arale.hash-1.0.js','arale.array-1.1.js','arale.base-1.1.js']);

在这个配置文件中我们可以知道aralex.xbox这个moduleId对应的是aralex.xbox-1.7.js这个文件, 后面就是他的依赖关系.

这时候我们的写法 Loader.use('${artifactId}', function(){});

但是目前由于整个依赖关系声明到了依赖模块文件本身了(define函数中)目前的写法临时变为了 seajs.use('${artifactId}-${version}.js', function() {}); 就是我们目前可以不需要这个index.js文件了. 但是我们加载模块的时候必须
指明我们加载的版本和类型. 然后等这个文件加载完成后, 我们在去根据这个文件本身去parse他的依赖关系, 在进行后续的加载和执行. 那看看后续我们如何兼容原有的这个对应关系.

不应该存在 iframe shim 这个『组件』

其实应该提供自动处理被 select,flash 等覆盖问题的 Overlay 组件,所有的悬浮组件都可以继承/组合使用该组件。

而不是在每次需要悬浮层的时候,调用这个补丁工具处理。

2012-04-16 讨论

议题列表:

上半场(测试等人员都参加)一个小时内

  1. PRD 评审
  2. 系分概要宣讲
  3. 确定总时间点

下半场(开发留下)一个小时内

  1. 组件分工与时间点
  2. 无线分工与时间点
  3. 商讨缴费改造项目接入的可能性
  4. 确定系分究竟要做什么事,以及模板
  5. 其他议题(包括规范评审、demo 规范、将库 public 等)

2012.04.08 代码评审与风格讨论记录

规范

讨论的规范部分,已整理到 Wiki 文档:

请 Arale 项目成员仔细阅读,严格遵守。

组件

position

已经按照昨天的 code review 修改了一部分。还有些问题:

  1. obj 和 elem 变量名,推荐写全:object, element 缩写的问题,比如 el 还是 elem,很难统一,我们遵从 backbone 等社区的理念,写全好了。
{ element: xx, x: ..., y: ... }

对内的,可以适当简写,对外的,尽量全称。

  1. src/position.js 的 12 行和 76 行可以在加一个空行,用空行来表示区分大段落。
  2. 按照并运行下 arale/tools/gjslint, 将报错都解决掉,可用 fixstyle 自动修复。
  3. isIE6 提取出来,这样不用每次都判断:
var isIE6 = $.browser.msie && $.browser.version == 6.0;

先继续用 jquery 的,既然提供了,就大胆用。

  1. posConverter 函数的卫语句(参数类型检查),应该放到 wrapPinObj 中,此后 posConverter 接受到的参数肯定是对的。
  2. 还有几处,人肉说下。(10, 119, 123)
  3. 134 行的 eval ?
  4. 上面的调整后,特别是参数调整后,需要对应更新 README

Widgets 类组件的架构讨论

原来的系分文档:

https://github.com/alipay/arale/issues/22

目前组件的基类已经开发完成。目前面临的几个问题需要讨论?

组件的生命周期的粒度?

目前是

  1. view初始化
  2. view分析,主要是支持在组件结构上支持行为( 包括dom事件, 和组件本身的事件)的绑定
  3. 组件事件绑定
    还有在beforeCreate, postCreate这两个方法, 那我们是否还要进一步细化这些方法, 还是尽量把这些生命周期的控制, 交由组件自身来处理?

Widget接口命名

由于是所有组件的基类, 所以在命名上面,需要更加慎重。 对于目前的几个命名可能需要进一步讨论。

View的职责。

目前view支持支持组件对应的模板的渲染,和样式的处理, 在这View层上是否需要增加show和hide这些便利的方法?

Action的定义

目前的Action的抽象还没有很好的处理, 这块也是很关键,后续业务的分离很大一部分都需要使用到他。

事件绑定

目前在组件存在两类事件, dom事件,和组件本身的事件, 我们是否把这两类统一为一个接口, 还是想目前这样进行分开?

组件需要提供那些基本方法。

目前支持提供了基本的几个方法。 那么后续我们是不是可以尽量的来通过模块的方式来混入到组件中来提供相应的功能?

handy使用arale打包后的组件疑问

dist目录是打包工具自动生成的,handy如果使用arale组件时必须要使用dist目录中的组件,这样的话在开发handy组件时,就需要手动创建 dist目录,并且把所需的arale组件copy过来,对手动copy没有疑问,但是对在开发过程中手动创建dist目录存在疑问,这样是不是违背了dist目录的构建原则?会不会带来其它的问题?

组件编码风格讨论

之前在wiki建了,后来被合并到编码规范中。不过编码规范主要是js写法上的,还有一部分是类库的一个约定,先记录在这里,方便讨论

  • 组件样式命名,命名空间为ui-组件名
  • 组件引入样式 require('./组件名.css'),这部分要考虑组件继承的问题,以及样式可选项的问题
    • 组件继承的时候需覆盖父类的样式
    • 可以通过配置选择不引用组件样式
  • 通过mixin混入的组件不应该有构造函数

事件命名空间的疑问

今天在写storage时,向window注册了一个storage事件,用于监听数据存储,由此联想到一个问题,如果用户不小心从window移除了这个事件,我的storage就没有办法正常工作了。因此想到了jquery的事件命名空间$.bind('click.cashier',callback)。。
Events是否提供事件命名空间?还是使用通用类库提供的事件命名空间?

移动平台的delete保留字

昨天的讨论中提到handy Stoarge的deleteKey可以修改为Storage.delete,最后测试了一把,在pc端没有报错,但在移动平台却抛出一个Parse error异常,所以只能再改回Storage.deleteKey

srcNode以怎样的数据类型传入

srcNode是以jQuery对象或Zepto对象传入还是以原始dom对象传入还是以选择器传入?这个问题似乎之前没有确定。
var overlay = new Overlay({
srcNode: $('#J-login-overlay') / document.querySelector('#J-login-overlay') / '#J-login-overlay'
})

Base 中默认混入 Events/Options 等是否合适?

我理解的是 Base 就应该是最简单的一个面向对象的框架,Events 属于事件驱动设计。将这两者揉到一起感觉很别扭。这个设计,包括默认集成 options,和 arale 的 simple 原则 不一致。

如果说 this.parent() 是语法糖,那 options 实际上也是,况且 options 式的函数风格虽然好,但不一定适合每个人的习惯,甚至说和团队习惯、原有代码都有可能冲突。而且有的类可能第一个参数就是 options,类似 Backbone.View

我觉得像 Backbone 一样让开发者来选择是否混入 Events 和 Options 是比较好的做法。

模块组织粒度问题

像arale.array,arale.dom等模块应该如何组织?

  • 方案一
    这些模块不使用define这种commonJS形式规范来组织,只是维护上分为独立模块,把这些全部打包后,arale作为一个整体来向外发布。jQuery采用这种做法。
  • 方案二
    所有模块严格按照commonJS规范组织,是用define定义模块,通过exports或return向外暴露API,为了别人使用方便,最后通过arale.core这个模块把其他模块require进来后,进行整体命名空间和API的暴露。这样会让每个模块比较纯粹,但也会有代码冗余。

针对现在已经改动的模块,存在以下问题:

  • 使用define定义模块,但部分API的暴露还是通过向arale这个对象上挂载来实现的。

我的观点,要么直接不使用define,把代码整好,最后整个arale通过一个define来完成模块的封装,要么使用define,每个模块都严格遵循规范,为了达到与现在兼容,在arale.core进行组装,API封装等。

倾向于第二种。

Observable 系分

事件这块的扩展和jquery接口尽量保持一致. 主要功能也是在Jquery的事件基础上进行扩展.

竞争对手

  1. yui - EventTarget 功能强大, 但过于复杂.
  2. dojo - event. 功能强大, 但是接口不够简洁.

主要API

  1. on
  2. off
  3. trigger

使用场景

需要有关事件发布的场合都可以使用. 目前主要通过混入的方式. 在Widget默认会混入此对象. 所以只要继承与Widget的组件, 默认都会包含此功能.

Ember.js 学习心得

一边研究一边记录。

http://emberjs.com/

Introduction

What is Ember.js?

Ember.js 的目的有两点:

  1. 消除重复工作(Eliminate Boilerplate),比如从服务器获取数据、模板和数据的渲染、数据与 UI 层的同步等等。Ember 希望,使用者只需要指定数据源和模板,其他事情都交给 Ember 来搞定。(You specify your template once, and Ember.js makes sure it's always up to date.)
  2. 为 web app 提供架构。前端页面可分为两大类:web pages 和 web apps. 前者已有各种成熟解决方案(cms 等),Ember.js 为后者提供架构拆分:MVC 以及 state 管理等,结束意大利面条式代码,降低理解成本,提高可维护性。

How is Ember.js Different?

与 web pages 不同,web pages 每次刷新都需要从浏览器端去重新请求数据。

与 ajax 也不同,在页面中有多处需要更新时,ajax 要维持 UI 与数据的同步很麻烦。

Ember.js 希望:

  1. 服务器端专注提供 REST api,不需要干前端的活。
  2. 其他的,全交给前端在浏览器端搞定。

Ember.js at a Glance

Ember.js 有三个核心功能:

  1. Bindings 绑定
  2. Computed properties 计算式属性
  3. Auto-updating templates 自动更新式模板

功能 3 是我们最感兴趣的。(我们想让后端来写前端代码,前端只需提供开发模式、组件库、样式等前端工作,业务代码尽量让后端通过 js 能自己完成)

有些项目可能直接想引用arale.core文件, 从而直接引用core相关模块产生的全局变量

兼容性处理

  • 由于原来的产品已经很多直接对全局变量的引用($E, $, $$, $S, $A)等, 我们目前所有的模块都通过define包装, 那么这些模块只有通过require, seajs.use的方式才能够获取. 所以arale.core-2.0.js被加载完成后, 需要负责这些全局变量的产生, 使用户在加载完成这个js后直接就 可以使用这些变量, 而不需要再次use, 和require.

2012-03-28 讨论

预定时间:14:30 - 17:00
预定地点:义誉钱庄

议题:

  1. 目录结构的讨论
  2. 康辉实现的版本方案讨论
  3. 核心测试的讨论
  4. 模块具体划分的讨论

position code review

源码:https://github.com/alipay/arale/blob/master/lib/position/src/position.js

API 设计

pin 方法背后的抽象模型很好,赞。

源码实现

  1. pinObj 和 relativeObj 这两个参数名称,是否用 pinObject 和 baseObject(relativeObject) ? 参数名尽量用全称,特别是 public api.
  2. helper 方法放在最下面是否更好?更适合阅读。另外,直接用 function 就好,方法名也不需要加下划线。
  3. 17 行有个无用判断。
  4. xyConverter 可以放出去,减少闭包层级。
  5. pin 方法 return this 一下,直接链式
  6. 一起走读

文档书写

  1. 可以增加一张图来表示 pin 背后的模型,用户一旦理解了这个模型,参数什么的都不用解释了。
  2. 减少重复文案。

Arale 2.0 项目一期阶段性总结 2012.05.11

Arale 2.0 进入火热编码第二周,目前已完善基本的架构设计文档:


Arale 2.0 已开发完成的组件,目前已达 13 个,包括:

  • events: 纯粹的事件机制
  • class:像 MooTools 的 Class 一样,但比 MooTools 更好用的 OOP 模块
  • base:基础类,提供 events, options 自动设置等功能
  • jquery:DOM、Event、Ajax、Anim 操作类库,非常强大
  • zepto:现代浏览器下的精简版 jQuery,特别适合移动端
  • json: 感谢 Douglas Crockford 的伟大贡献
  • underscore: 非常实用的语言增强小类库
  • cookie: 简洁简单的 cookie 操作工具
  • handlebars: 地球上最好用的 JS 模板引擎,没有之一
  • store: 简洁实用的本地存储组件
  • swfobject: 搞 flash 同时又懂 js 的都知道它
  • moment: 小巧强大的日期操作组件
  • jasmine: 脚本测试框架里的文艺青年,虽然文艺,但很实用

还有两个组件快开发完毕,欢迎 code review:

  • iframe-shim: 我们讨厌 ie6,让 select 等再也不会胡乱跑出来
  • position:这是我见过的,最帅气的定位组件,简简单单的抽象模型,可以用来实现任何定位

开发中的组件还有:widget, overlay, switchable, dropdown 等等,欢迎阅读 README, 现在就给我们建议。

所有组件源码地址:https://github.com/alipay/arale/tree/master/lib


本周还就编码规范、注释风格等问题,在内部和社区进行了热烈讨论,最后形成的规范请看:

https://github.com/alipay/arale/wiki 里,开发规范部分

最后,感谢所有参与讨论的朋友们。因为有大家在一起,我相信 Arale 2.0 一定会越来越好。

确定 DATA-API 的具体规范

概要

DATA-ATTRIBUTE API 是一套 API 风格约定,遵循该风格约定的 Widget 组件,都可以直接从 Markup 初始化。

使用场景

以 Tabs 为例:


<div data-widget-type="Tabs" data-widget-options="{  }">

  <ul>
    <li data-widget-role="trigger">1</li>
    <li data-widget-role="trigger">2</li>
    <li data-widget-role="trigger">3</li>
  </ul>

   <div data-widget-role="panel">内容块</div>
   <div data-widget-role="panel">内容块</div>
   <div data-widget-role="panel">内容块</div>

</div>


参考

多级继承中options的处理

目前有一种情况就是,我们可能有多级继承, 那么子类有没有便捷的方法从祖先类上面继承下来options?
目前是在子类是通过遍也是可以处理的。但是这样还是有点不太方便啊。

seajs.use(['jquery', 'base'], function($, Base) {
    console.log(Base);
    var Person = Base.extend({
        options: {
            o1: 'p1',
            o2: 'p2',
            o3: 'p3'
        },
        initialize: function(options) {
            this.setOptions(options);
            console.log('person initialize');
        }
    });

    var Man = Person.extend({
        options: {
            o3: 'm1',
            o4: 'm2'
        },
        initialize: function(options) {
            Man.superclass.initialize.apply(this, arguments);
            console.log('Man initialize');
            this.setOptions(options);

        }
    });
    var Child = Man.extend({
        options: {
            o4: 'C1',
            o5: 'C2'
        },
        initialize: function(options) {
            //Child.superclass.initialize.apply(this, arguments);
            console.log('Child initialize');
this.mergeSuperOptions(options); //手动遍历?
            this.setOptions(options);
        },
        mergeSuperOptions: function(options) {      
            var _super = Child.superclass;
            var _options;
            while(_super) {
                if (_super.options) {
                    _options = _super.options;
                    for (var key in _options) {
                        if (_options.hasOwnProperty(key) && (!(key in options))) {
                            options[key] = _options[key]
                        }
                    }
                }
               _super = _super.constructor.superclass;
            }  
        }
    });
    var c = new Child({o4: 'mm1'}); 
    console.log(c.options);//如果默认{ o4="mm1", o5="C2"}. 通过merge 也就是我们期望的{o1:'p1', o2: 'p2', 'o3': 'm1', 'o4': 'mm1', 'o5': 'C2'}
});

Events 增加 mixTo 方法

eventsclass, optionsbase 中独立出来后,面临一个问题:

define(function(require) {

    var Events = require('events');

     function MyClass() { ... }

     // 如何将 Events 提供的功能,混入到 MyClass 中?

});

一种方式是,再引入 classbase 模块,然后通过 Class.createBase.extend 中的 Implements 属性来指定混入 Events:

define(function(require) {

    var Events = require('events');
     var Class = require('class');

  var MyClass = Class.create({
       Implements: Events,
       ...
   });

});

但对于很多工具类,比如 Position 或无线端的 Storage 等来说,并不是 function class, 用 Class.create 来创建意义不大。这种情况下,传统的一个解决思路是:

define(function(require) {

    var Events = require('events');
     var seed = require('seed');

     var Storage = {

         set: ...
         get:... 

     };

    // 通过 seed 或某个类,来提供 mix 等方法
    // 或者自己在 Storage 中实现一个 mix 方法
    seed.mix(Storage, Events.prototype);

});

很显然,通过 seed 或 util 等思路来解决,比较适合 YUI, KISSY 等一站式类库(可以直接放在 Y 和 S 中)。但对 Arale 的思路来说,这并不妥。Arale 没有所谓的 seed 或 core,要按上面的思路去做的话,会导致 Events 在使用时,得强依赖另一个 util 或 seed 组件,然而很可能仅仅只用到一个 mix 方法。

这个问题与颂赞等人讨论过,无线端不少工具类组件需要 Events,并不需要 Class 和 Base.

这问题一直萦绕心头,今天偶然看到一篇技术文章里提到 IoC(控制反转),莫名就联想到了这个问题,而且发现 jQuery 的 appendTo 等方法也有类似的意思,因此就想到:

不如让 Events 自己提供 mixTo 方法

立刻感觉云散花开,很自然的就可以:

define(function(require) {

     var Storage = {

         set: ...
         get:... 

     };

   // 让 Storage 方法拥有  on/off/trigger
    require('events').mixTo(Storage);


  return Storage;
});

代码明显精简、漂亮多了。更重要的是,让 Storage 的依赖减少,更解耦了。

想起陈皓的一篇文章:http://coolshell.cn/articles/7236.html

解耦,解耦,解耦。

Arale 类库的设计**里,也有一段很重要的:

不追求代码的零重复,更追求组件的独立性。

如果大家认可,我觉得接来下我们可以进一步约定,凡是可以 mix 给普通对象(比如 var obj = {})用的组件,比如 Events 这种,默认应该都提供 mixTo 方法。

iframe-shim code review

源码:https://github.com/alipay/arale/tree/master/lib/iframe-shim

API 设计

  1. 是否这样更好:
var Shim = require('iframe-shim');

var shim = new Shim(targetElement);

shim.sync(); // 第一次同步也交给开发者自己控制

show 和 hide 是否没必要了,开发者有 sync 就搞定了一切。

另外,感觉需要增加一个 destroy 方法,用来销毁创建的 iframe.

比如 overlay 里:

var Overlay = Widget.extend({

   initialize: function(srcNode) {

       // overlay 里只需要下面这句
       // 等以后 ie6 退出历史舞台了,只需删除这一句,和 initShim 方法就行
       this.initShim(srcNode);

   },

   initShim: function(srcNode) {
       var Shim = require('iframe-shim');
       var shim = new Shim(srcNode);

       this.on('show hidden', shim.sync);
       this.on('destroy', shim.destroy);
   }

});

代码实现

  1. useIframe 可以考虑用特性探测:

http://www.quchao.com/entry/detect-browser-by-features/

(function (win, doc) { 
    var isIE = !+'\v1',                                           // alt: !!-[1,] 
        isIE6 = isIE && !('maxHeight' in doc.body.style), 
        isIE8 = isIE && 'prototype' in Image, 
        isIE7 = isIE && !isIE6 && !isIE8, 
        //isIE9 = isIE && .1 === +(.09).toFixed(1), 
        isFF = !!doc.getBoxObjectFor || 'mozInnerScreenX' in win, // gecko 
        isOP = !!win.opera && !!win.opera.toString().indexOf('Opera'), 
        isOP9 = /^function \(/.test([].sort), 
        isWK = !!win.devicePixelRatio,                            // web-kit 
        isSF = /a/.__proto__ == '//',                             // safari 
        isCR = /s/.test(/a/.toString);                            // chrome 
})(window, document);

useIframe 或许直接叫 isIE6 更简单直白

$.browser 能不用则不用

  1. $(target).after(iframe) 心存质疑,或许直接插入到 body 中更好。target 有可能还没在 dom 树里,另外可能层级很深,放在之后,对性能有影响。
  2. 避免 xx.prototype = {} 的写法,会搞掉 constructor
  3. is(':hidden') 已经包含了高宽为 0
  4. position.pin 应该放在 this.show() 前面,都准备好后,再显示。
  5. 报错究竟有无必要?讨论
  6. 整体代码的组织,感觉可以更清晰化:
define(function(require, exports, module) {

  function Shim() {
  }

  Shim.prototype.sync = ...
  Shim.prototype.destroy = ...

  if (isIE6) {
      module.exports = Shim;
  } else {
      function noop() {
      }
      noop.prototype.sync = noop;
      noop.prototype.destroy = noop;
      module.exports = noop;
  }
});

代码注释

大讨论呀大讨论,JSDoc 有无必要。show 和 hide 这种方法的注释,实在是冗余呀。

如无必要,勿增注释。如有必要,注释勿少。

文档书写

  1. 增加 模块依赖
  2. demo 改成 演示页面?
  3. 最后增加 交流反馈?

以 events 的为模板

文档书写风格讨论,中西文之间的空格。

单元测试

挺不错的,test 里面的 Test 字符可以去掉。

example

真实代码与 pre code 的同步问题。

backbone 与 position 等模块对 jquery / zepto 的依赖如何做到可切换

目前 backbone 和 position 两个模块,源码里都有:

var $ = require('jquery');

打包后,在各自 deps 里,都添加上了 jquery 硬字符串。对移动端开发来说,需要再将 jquery 替换成 zepto, 麻烦。

我们是否可以约定,如果一个模块满足以下两点:

  1. 需要用到 DOM 操作类库
  2. 需要在 PC 和 Mobile 都适用

则在引用 DOM 操作类库时,必须采用以下写法:

var $ = require('$');

$ 的具体取值,由外部配置决定,比如在 arale 里,是:

seajs.config({
    alias: {
        '$': 'jquery/1.7.2/jquery'
    }
});

在 handy 里,则是:

seajs.config({
    alias: {
        '$': 'zepto/0.8.0/zepto'
    }
});

这在开发时,比较舒适了,测试也方便,切换 alias 就可以切换场景。

带来的问题是,我们之前的打包策略要求打包后的信息是完备的,不依赖开发时的 alias 配置,也就是打包后要生成两份不同的文件:

define('#backbone/0.9.2/backbone', ["#jquery/1.7.2/jquery", ...], ...);
define('#backbone/0.9.2/backbone', ["#zepto/0.8.0/zepto", ...], ...);

这两份文件,共用的 id 是同一个,这和 id 与文件名一致的约定冲突。我想到的解决办法是,backbone 打包后,生成两份文件:

dist/backbone/0.9.2/backbone.js
dist/backbone/0.9.2/backbone-mobile.js

内容分别是:

define('#backbone/0.9.2/backbone', ["#jquery/1.7.2/jquery", ...], ...);
define('#backbone/0.9.2/backbone-mobile', ["#zepto/0.8.0/zepto", ...], ...);

这要求移动端开发时,引入 backbone 时加上 mobile 标识:

var BB = require('backbone-mobile');

PC 端依旧是 require('backbone')

大家看看上面的方案是否有考虑不周的地方,欢迎提建议。

研发模式的思考

产品形态

  1. 前端涉及的产品形态,在业界可分为两大类:Web Pages 和 Web Apps 。
  2. Web Pages 是浏览类的,用户主要是来看的:以内容展现为主,辅有少量交互。前端提供基础类库,开发工具化、外包化。典型:首页、营销活动、频道等等。
  3. Web Apps 则以交互为主,用户主要是来用的。可分为两种:
    1. 体验类:包含大量交互,同时用户体验很重要。比如 GMail, 支付宝收银台、淘宝购物车等等。
    2. 功能类:包含大量交互,以功能为主,体验不是第一位的。比如后台系统、认证流程等。

目前遇到的问题

  1. Web Apps 的开发,前端投入了大量人力,但前端资源依旧存在潜在的瓶颈(比如新增加一条业务线时,很可能无前端去支持)。现有前后端配合的开发模式,沟通协作成本偏高,可维护性不够方便。在现有的研发模式下,前端自身的价值点也很难体现出来(花了大量时间在业务上,但得到的认可不多)。
  2. 感觉最核心的问题,依旧是前后端的解耦:如何让前后端的工作彼此更独立,如何让更合适的人做更合适的事,把事情做得更好。
  3. 对于体验类,目前业界有很多新兴的解决方案:Backbone, Ember, Knockout 等等,包括 YUI 的 App framework 等。这些解决方案,都希望后端专注于提供 REST 接口,其他的都交给前端来搞定。
  4. 对于功能类的,目前业界解决方案依旧是比较老的一套,比如 GWT 等,包括 ExtJS 也是希望能让后端搞定一切。

利用业界现有的解决方式:

  1. 要达成第 3 点,前端需要了解数据,以及深入把控住数据之外的业务逻辑,然后利用前端技术,完成开发。
  2. 要达成第 4 点,后端依旧需要有一定的前端技能,否则容易出性能、可维护性等问题而玩不下去。

这两种,需要前后通吃的 Web Developer 来达成。在阿里,目前很难。

我们期待的方式

  1. 让前端专注于 UI 层面、体验层面的开发。
  2. 让后端专注于数据、业务逻辑的开发。
  3. 第 1 点和第 2 点最好能并行。
  4. 能有一种很便捷的方式,将第 1 点和第 2 点的工作无缝衔接起来。

核心是:解藕,让更合适的人做更合适的事。

我们该怎么做

目前 Arale 的发展方向,可以很好的支撑:

  1. 浏览类的开发。只要把常见交互封装成 widgets, 做到 widgets 足够丰富易用,浏览类的就可以很快速的开发出来。
  2. 体验类的开发。Arale 会有类似 Backbone 的模块,或直接引入 Backbone 等应用框架。前端可以直接基于这些业界成熟方案去做,后端专注于提供 REST 接口就好。这对我们前端来说,刚开始可能会有学习成本,但熟悉之后,应该能比较好的胜任。

目前 Arale 没想清楚如何支撑第三种模式:

  1. 功能类的开发。后端直接基于 Arale,如何搞定所有开发?包括展现层?

还有一个很现实的问题,如果体验类的研发模式可行,意味着要投入大量前端资源去做,这对产品是利好的,但这一研发模式,很可能会加剧前端的资源瓶颈问题。有无可能培养后端成为体验类的前端开发?让部分后端也可以独立承担开发?

越想越觉得迷茫,我们期待的研发模式,究竟靠不靠谱?如果要做,具体究竟该如何做?
集思广益,大家发表点建议。

arale2.0模块书写(包括子模块)方式

目录结构

---arale.event
--------resources
------------event.js
------------core.js
------------object.js
------------store.js
------------chain.js
--------pom.xml

代码书写和组织

命名规则

  1. event.js 模块主文件以id来命名(arele.event-->event.js, arele.hahs-->hash.js), 如果一个模块对外发布多个文件, 别名文件的规则为event-layer1.js, event-layer2.js
  2. core.js, object.js, store.js, chain.js为子模块的命名.

代码基本书写

event.js

define(function(require, exports) {
  require('./object');
  require('./store');
  var chain = require('./chain');
  var core = require('./core');
  //如果主模块要暴漏子模块的接口, 可以使用
   exports.connect = core.connect;
   mixin(chain, module.exports); // 将某个子模块的所有对外接口 添加到父模块中
});

object.js

define(function(require, exports) {
  var arr = require('arele.array');
  //.........代码
  exports.object = function() {};
});

store.js

define(function(require, exports) {
    var hash = require('arele.hash');
    //.........代码
    exports.store = function() {};
});

chain.js

define(function(require, exports) {
    var base = require('arele.base');
    //.........代码
    exports.chain = function() {};
});

core.js

define(function(require, exports) {
    var arr = require('arele.array');
    var eo = require('./object');
    var store = require('./store');
    //.........代码
    exports.connect = function() {};
});

调试

  1. 可以在本地直接通过seajs来调试
  2. 打包调试

文件打包后模块代码组织形式

文件名

  1. arele.event-2.0-src.js, arele.event-2.0.js 主文件
  2. arele.event-layer1-2.0-src.js arale.event-layer1-2.0.js 包含别名文件

打包后代码具体代码组织形式

arele.event-2.0-src.js

define('arale.event-2.0', ['arale.dom-1.1', 'arale.array-1.1', 'arale.hash-1.1', 'arale.base-1.1'], function(require, exports, module) {
  require('arale.event-object-2.0');
  require('arale.event-store-2.0');
  var chain = require('arale.event-chain-2.0');
  var core = require('arale.event-core-2.0');
  //如果主模块要暴漏子模块的接口, 可以使用
   exports.connect = core.connect;
   mixin(chain, module.exports); // 将某个子模块的所有对外接口 添加到父模块中
});
define('arale.event-object-2.0', ['子模块的dependency由于他的运行是建立在主模块上的, 所以是空的'],  function(require, exports, module) {
   var arr = require('arele.array-1.1');
    //.........代码
    exports.object = function() {};
});
define('arele.event-store-2.0', [], function(require, exports) {
    var hash = require('arele.hash-1.1');
    //.........代码
    exports.store = function() {};
});
define('arale.chain-2.0', [], function(require, exports) {
    var hash = require('arele.hash-1.1');
    //.........代码
    exports.store = function() {};
});
define('arale.core-2.0', [], function(require, exports) {
    var arr = require('arele.array-1.1');
    var eo = require('arale.event-object-2.0');
    var store = require('./store');
    //.........代码
    exports.connect = function() {};
});

打包后文件相关说明

  1. 所有的模块(包括主主模块和子模块)都有单独的define. 其中的模块id都是全局唯一的, 其中子模块的id的构成是有主模块加子模块名字构成的id. (方便后续调试和bugfix等等).
  2. 对于包含别名的项目, 现在别名最终的id可能和子模块形式一样(后续看看用不用区分, 个人觉得子模块和别名模块性质差不多, 只不过别名模块是主动的可以对外被依赖)
  3. 打包后原来子模块的相对依赖都会改成全局依赖.

系统maven项目改造

我们之前考虑核心和组件的改造,之后考虑下系统代码按照现有的改造还是需要个性化。由于使用较广,尽量兼容现有场景,避免大范围的改造。

打包流程和发布方式讨论

Arale2项目打包流程和发布方式

项目目录结构(具体参看文件命名与目录结构)

下面的目录主要设计源代码和打包后需要存放等目录

arale核心项目

    arale/
         -- dist/
             -- xxx/1.0.0/..
         -- lib/
           -- xxx/
               -- src/
                  -- __config.js
                  -- widget.js
                  -- view.js
                  -- model.js
                  -- base.css
                  -- /                  

系统项目

    static/
      -- js/
        -- base/
          -- src/
            -- __config.js
            -- base.js
            -- cahser.js
            -- personal.js
            -- sites/
              -- bj.js
              -- hz.js
              -- sh.js
          -- /
        -- module/
      -- css
        -- base/
      -- release   # 含义同 dist

相关解释

  1. dist 对外发布文件目录. 根据在__config.js中dist的配置, 把需要发布的文件合并后会放到这里. 然后通过插件或者手动把里面的文件发布到对应的服务器上.
  2. 是本地打包时,存放的 dist 文件,不在版本控制里。
  3. /tmp 临时目录. 当我们在文件在打包过程中对源文件进行依赖分析, 映射替换, 变量替换等操作后, 文件存储的位置. 里面的文件
  4. __config.js 项目配置文件. 里面记录了相关项目版本, 文件发布等信息.

项目打包相关注意事项

  1. 不同的类型(arale, alipay, system)的项目, 我们打包的策略和执行相关的任务也是不同的, 策略的选择是在__config.js中parent的配置来决定的.
  2. tmp目录可以认为存储的是过滤过的源文件. 其中的过滤可能是版本替换, 映射替换, 或者对文件本身进行过滤(比如只有js文件才进行过滤)等其操作后的文件. 基本上是和源文件一一对应的.
  3. dist里面才是最终可以被用户使用的文件.

例证

下面我们将以一个系统项目为例子, 说明相关打包流程,和打包后的文件变化情况, 以及相关部署的操作.

源文件


/** __config.js **/
/** 相关不重要的已被省略, 具体的配置参看相关文档**/
define({
    ………..
    "version": '1.0.1-dev',
    "parent": '/config/cashier/1.1.0/', 
    "alias": {
        '_': 'ar/underscore/0.9.2/underscore.js',
        'xbox': 'ar/xbox/2.0/xbox.js',
                'sec': 'cashier/1.2/security/'
    },
    "dist": {
        'cashier.js': ['cashier.js', 'base.js'], // 在dist目录产生a.js, 并把b.js, c.js, d.js打包合并到a.js中
        'personal.js': ['personal.js', 'base.js']  
        'sites': ['sites/*.js']
    }, // 文件产生策略. 根据这个配置来确定对外部署的文件.
    "repository": "http://alipay.im/arale2/repos/", 
});

/** cashier.js **/
define(function(require, exports, module) {
     var xbox = require('xbox');
     var base = require('./base');
     var a = require('sec/a.js');
     var b = require('sec/b.js');

    exports.doSth = function() {
        //.…..
    };
});


/** personal.js **/

define(function(require, exports, module) {
    var _ = require('_');
    var base = require('./base');

    exports.doSth = function() {
        //.…..
    };
});

/** base.js **/
define(function(require, exports, module) {
    var jquery = require('jquery');

    exports.doSth = function() {
    //.…..
    };
});

打包后 中的文件

其中我们在打包后主要关注的就是dist目录的情况. 这个目录中内容主要是根据在 __config.js 中的配置来确定的. 下面就列出了按照上面的配置, 我们这个目录的相关内容, 和源码的变化.

    /
     -- /base/1.0.1-dev/cashier.js
     -- /base/1.0.1-dev/personal.js
     -- /base/1.0-1-dev/sites/
                        -- bj.js
                        -- hz.js
                        -- sh.js


/** cashier.js 

define('#cashier/base/1.0.1-dev/cashier.js', ['#casier/base/1.0.1-dev/base.js','#ar/jquery/1.7.2/jquery.js', '#ar/xbox/2.0/xbox.js', '#ar/widget/2.0/widget.js', '#ar/oo/2.0/class.js'], function(require, exports, module) {
    var xbox = require('#ar/xbox/2.0/xbox.js');
    var base = require('#cashier/1.0.1-dev/base.js');
    var a = require('#cashier/1.2/security/a.js');
    var b = require('#cashier/1.2/security/b.js');

    exports.doSth = function() {
    //.…..
    };
});
//base.js被合并进来了.
define('#cashier/base/1.0.1-dev/base.js', ['#ar/jquery/1.7.2/jquery.js'], function(require, exports, module) {
    var jquery = require('#ar/jquery/1.7.2/jquery.js');
    exports.doSth = function() {
    //.…..
    };
});


/** personal.js **/
define('#cashier/base/1.0.1-dev/personal.js', ['/casier/base/1.0.1-dev/base.js', '/ar/underscore/0.9.2/underscore.js', '/ar/jquery/1.7.2/jquery.js'], function(require, exports, module) {
    var _ = require('#ar/underscore/0.9.2/underscore.js');
    var base = require('#cashier/1.0.1-dev/base.js');

    exports.personal = function() {
        //.…..
    };
});

//base.js被合并进来了.
define('/cashier/base/1.0.1-dev/base.js', ['/ar/jquery/1.7.2/jquery.js'], function(require, exports, module) {
    var jquery = require('/ar/jquery/1.7.2/jquery.js');
    exports.doSth = function() {
        //.…..
    };
});

/** sites/下面的文件由于是资源文件, 没有变化. **/

其中在上面需要注意的是:

  1. define中的几个参数
    • id (/cashier/base/1.0.1-dev/cahsier.js) 的生成是基于目录形式的的, id前缀(cashier)的确定目前是根据package.js中的parent来确定的(也可以有其他的确认方法?)
    • dependencies 模块依赖. 这个的计算是通过本js的内部require分析出来的, 而且会进行传递分析. 也就是说我们require模块的依赖也会被计算进来.
  2. 别名替换 我们在项目开发的过程中可以进行相对依赖(本项目内模块的依赖require('./base.js'))模块名和全局依赖(require('xbox'))的替换. 其中相对依赖的模块我们会根据当前模块的信息替换成标准的模块id, 对于全局的我们是通过package.js中的alias配置进行替换. 所以在开发中我们对于全局模块可以使用便捷的, 短的名字, 然后在alias中配置具体的模块位置. 但是对已核心的模块, 我们会推荐相应的别名.
  3. alias继承 我们发现我们的package.js中并没有配置 jquery. 这个是由于我们会有一个全局的映射配置, 这个可以通过我们的parent属性间接得到. 但是我们本地也可以选择进行覆盖. 如果我们使用相同的key, 就会覆盖父类的定义. 默认情况下对于核心模块, 用户只需要直接使用即可.

发布

把dist目录中的文件发布到相应位置即可. 比如在这里. 我们可以把dist中的目录传递到线上cashier/下面

action

  1. 邵帅需要去确定后续我们发布目录的话, 可能的相关事项.

页面使用

seajs.use('/cashier/base/1.0.1{-dev}/base.js', function(base) {
    base.cashier();
});

// 
//在这里{-dev}在seajs.use的时候会去检查当前页面所在环境, 如果线上会-dev会被替换为'', 而在测试环境始终请求的是-dev.
// 其中需要理解的时候当1.0.1版本发布后这两个版本对应的模块文件是等价的(1.0.1-dev.js == 1.0.1.js) 

define函数支持把兼容模块注册到arale1.x模块集合中.

主要就是对模块在页面中的注册和查找的兼容性处理.

  1. 原有的注册机制是通过在loader代码中完成的. 目前期望通过define函数来完成.
  2. 可能需要加一个参数,看看如何控制define函数中的factory的执行. 原来的模块是只要加载就会执行.

handy 的建库与分工

  1. 在 github 上独立建库
  2. 让颂赞分工,组件包括:
组件名             类别
device             util      
network            util 
iscroll            util 
pintostart     util 
floatlayer     util 
slider           widget  
touchslider  widget 
swap             widget 
flip                     widget 
touch            widget 
touchview    widget 
datalist               biz 
datafilter             biz   
paychannel     biz
bizswitch      biz

需不需要增加个客户端特征判断的组件?

仅判断是否为 ie6 一项:

position 用的是 $.browser.msie && $.browser.version == 6.0

iframe-shim 用的是 !-[1,] && !('maxHeight' in document.body.style),这个过不了 gjslint

而我习惯用 !!window.VBArray && !window.XMLHttpRequest

加上这个组件后,统一掉所有的地方。

@lifesinger 不是组员选不了 label,难怪右栏空白一片~ ^_^

编码与文档风格讨论

看了下大家的代码,为了保证整体风格一致,有些地方需要达成一致:

声明多个变量时的写法

如果是纯声明,或非常简短的赋值,可以写成一行。比如:

var cache, event, all, list, i, len, rest = [], args; 

如果赋值语句较长,目前有一种写法是用逗号换行:

var $ = require('jquery'),
        position = require('position'),
        useIframe = $.browser.msie && Number($.browser.version) < 7;

还有一种写法是直接用 var 风格:

var $ = require('jquery');
var position = require('position');
var useIframe = $.browser.msie && Number($.browser.version) < 7;

我建议直接用 var 风格呀,这样无论写起来还是修改都更方便,用 closure compiler 等压缩后,会自动变成上面那种逗号分隔的。

没记错的话,前面那种逗号风格的兴起,是 NCZ 在2009 年介绍 YUI Compressor 压缩原理时提到的:

http://www.slideshare.net/nzakas/extreme-javascript-compression-with-yui-compressor

NCZ 倡导:为了帮助压缩工具实现更好的压缩,倡导一个函数内,尽量只保持一个 varreturn.

然而,目前高级压缩器,Closure Compiler 和 UglifyJS 等,都能自动处理 var 了,在压缩函数时,会自动做变量提升和 var 单一化。

因此尽量保持一个 var 的最佳实践,目前已不是最佳实践。大方向肯定是更人性化、更自然化的写法。优化交给工具就好。

最后,强烈建议:多个 var, 且非纯声明或简单赋值时,建议写成:

var $ = require('jquery');
var position = require('position');
var useIframe = $.browser.msie && Number($.browser.version) < 7;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.