How to: Configure the cross domain for ajax in IIS 7.x

REF:

[1] http://technet.microsoft.com/en-US/sysinternals/bb763179.aspx

 

web.config file content:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name=”Access-Control-Allow-Origin” value=”*” />
<add name=”Access-Control-Allow-Methods” value=”OPTIONS,POST,GET” />
<add name=”Access-Control-Allow-Headers” value=”x-requested-with” />
<add name=”Access-Control-Allow-Credentials” value=”true” />
</customHeaders>
</httpProtocol>
</system.webServer>

IIS跨域设置,用localhost进自己的网站进不去,用127.0.0.1可以

http://blog.csdn.net/l863784757/article/details/12870381

 

一、进本地网站用localhost进去后不能获取数据,而用127.0.0.1进去则可以

解决办法:1.打开IIS,选择Default Web Site,

 

再选择HTTP响应标头

添加

Access-Control-Allow-Credentials:true

Access-Control-Allow-Headers:origin,x-requested-with,content-type

Access-Control-Allow-Methods:POST,GET,OPTIONS

Access-Control-Allow-Origin:*

四项

最终结果是

然后我用localhost和127.0.0.1都可以打开

ASP.NET开发web应用遇到的javascript跨域请求问题

解决方案

不提倡跨域的post请求。

0.jquery中ajax的跨域方案jsonp

.ashx代码

 

  1. using System; 
  2. using System.Collections.Generic; 
  3. using System.Linq; 
  4. using System.Web; 
  5.  
  6. namespace KB.DSN.Web.API.Tokens 
  7.     /// <summary> 
  8.     /// Summary description for Get 
  9.     /// </summary> 
  10.     public class Get : IHttpHandler 
  11.     { 
  12.  
  13.  
  14.         public void ProcessRequest(HttpContext context) 
  15.         { 
  16.             setresponsecontext(context); 
  17.             var token = KB.DSN.BusinessAccess.UniqueCommunicationCode.GenerateUniqueCommunicationCode(); 
  18.  
  19.             var outputobject = new 
  20.             { 
  21.                 Head = new Models.KBJsonHeadResponse(), 
  22.                 Body = new { Token = token } 
  23.             }; 
  24.  
  25.             var outputjsonstring = Newtonsoft.Json.JsonConvert.SerializeObject(outputobject); 
  26.  
  27.              
  28.             context.Response.Write(context.Request.QueryString[“callback”]+“(“+outputjsonstring+“)”); 
  29.  
  30.         } 
  31.  
  32.         private void setresponsecontext(HttpContext context) 
  33.         { 
  34.             
  35.             context.Response.ContentEncoding = System.Text.Encoding.UTF8; 
  36.             context.Response.ContentType = “application/json”
  37.         } 
  38.  
  39.         public bool IsReusable 
  40.         { 
  41.             get 
  42.             { 
  43.                 return false
  44.             } 
  45.         } 
  46.     } 

html页面

 

  1. function getToken_jsonp(){ 
  2.         $.ajax({ 
  3.    
  4.     url: “http://192.168.0.111/api/tokens/get.ashx”, 
  5.           type: “get”,  
  6.     dataType: “jsonp”, 
  7.     jsonp: “callback”, 
  8.     async: false, 
  9.      
  10.           contentType: “application/json”, 
  11.           success: function(data){ 
  12.       //alert(“getToken success”); 
  13.             $(“#token”).text($.toJSON(data)); 
  14.             //console.log(data); 
  15.        
  16.           }, 
  17.     error:function(){ 
  18.         alert(“getToken fail”); 
  19.     } 
  20.         }); 
  21.  
  22.       } 

jsonp只支持GET请求,不支持POST请求,就算你写了POST,它会自动转换为GET请求,把你的data放在querystring中。

1.修改web.config文件

整个应用都支持跨域的请求。

web.config

  1. <system.webServer> 
  2.  
  3.   <httpProtocol> 
  4.     <customHeaders> 
  5.       <add name=“Access-Control-Allow-Methods” value=“OPTIONS,POST,GET”/> 
  6.       <add name=“Access-Control-Allow-Headers” value=“x-requested-with”/> 
  7.       <add name=“Access-Control-Allow-Origin” value=“*” /> 
  8.     </customHeaders> 
  9.   </httpProtocol> 
  10. </system.webServer> 

html page

  1. function addContact() { 
  2.            var contact = new Object(); 
  3.            contact.FirstName = $(“#firstName”).attr(“value”); 
  4.            contact.LastName = $(“#lastName”).attr(“value”); 
  5.            contact.PhoneNo = $(“#phoneNo”).attr(“value”); 
  6.            contact.EmailAddress = $(“#emailAddress”).attr(“value”); 
  7.            $.ajax({ 
  8.                url: “http://localhost:10401/api/contacts/AddContact.ashx”, 
  9.                type: “POST”, 
  10.  
  11.                dataType: “json”, 
  12.                data: $.toJSON(contact), 
  13.   
  14.                success: function () { loadAllContacts(); } 
  15.            }); 
  16.        } 

 这种方式不能设置contentType: “application/json”,否则会提示

Request header field Content-Type is not allowed by Access-Control-Allow-Headers.”

去掉ajax中的contentType设置就可以了!

 

 想要设置contentType也可以,需要将web.config文件中的

<add name=“Access-Control-Allow-Headers” value=“x-requested-with”/>

修改为

<add name=“Access-Control-Allow-Headers” value=“x-requested-with,content-type”/>

 在II6中web.config不支持system.webServer配置节,所以需要在IIS中设置httprequestheader。将web.config文件中的自定义头加入IIS的设置中。

FindContact.ashx

  1. /// <summary> 
  2.    /// Summary description for FindContact 
  3.    /// </summary> 
  4.    public class FindContact : IHttpHandler 
  5.    { 
  6.  
  7.        public void ProcessRequest(HttpContext context) 
  8.        { 
  9.            context.Response.ContentEncoding = Encoding.UTF8; 
  10.            context.Response.ContentType = “application/json”
  11.  
  12.            var stream = context.Request.InputStream; 
  13.            var reader = new StreamReader(stream); 
  14.            var input=reader.ReadToEnd(); 
  15.  
  16.            var o = Newtonsoft.Json.JsonConvert.DeserializeObject<Models.Contact>(input); 
  17.            var list = new List<Models.Contact>(); 
  18.            list.Add(o); 
  19.            list.Add(o); 
  20.            context.Response.Write(Newtonsoft.Json.JsonConvert .SerializeObject ( list )); 
  21.        } 
  22.  
  23.        public bool IsReusable 
  24.        { 
  25.            get 
  26.            { 
  27.                return false
  28.            } 
  29.        } 
  30.    } 

 

2.在请求中设置HttpHeader

针对单个请求。

FindContact.ashx

  1. /// <summary> 
  2.    /// Summary description for FindContact 
  3.    /// </summary> 
  4.    public class FindContact : IHttpHandler 
  5.    { 
  6.  
  7.        public void ProcessRequest(HttpContext context) 
  8.        { 
  9.            context.Response.ContentEncoding = Encoding.UTF8; 
  10.            context.Response.ContentType = “application/json”
  11.  
  12.            var stream = context.Request.InputStream; 
  13.            var reader = new StreamReader(stream); 
  14.            var input=reader.ReadToEnd(); 
  15.  
  16.            var o = Newtonsoft.Json.JsonConvert.DeserializeObject<Models.Contact>(input); 
  17.            var list = new List<Models.Contact>(); 
  18.            list.Add(o); 
  19.            list.Add(o); 
  20.  
  21.            #region 支持跨域请求 
  22.            context.Response.ClearHeaders(); 
  23.            string origin = context.Request.Headers[“Origin”]; 
  24.            context.Response.AppendHeader(“Access-Control-Allow-Origin”
  25.                string.IsNullOrEmpty(origin) ? “*” : origin); 
  26.            string requestHeaders = context.Request.Headers[“Access-Control-Request-Headers”]; 
  27.            context.Response.AppendHeader(“Access-Control-Allow-Headers”
  28.                string.IsNullOrEmpty(requestHeaders) ? “*” : requestHeaders); 
  29.            context.Response.AppendHeader(“Access-Control-Allow-Methods”“POST, OPTIONS”); 
  30.            #endregion 
  31.  
  32.            context.Response.Write(Newtonsoft.Json.JsonConvert .SerializeObject ( list )); 
  33.        } 
  34.  
  35.        public bool IsReusable 
  36.        { 
  37.            get 
  38.            { 
  39.                return false
  40.            } 
  41.        } 
  42.    } 

html page

  1. function addContact() {
  2.            var contact = new Object();
  3.            contact.FirstName = $(“#firstName”).attr(“value”);
  4.            contact.LastName = $(“#lastName”).attr(“value”);
  5.            contact.PhoneNo = $(“#phoneNo”).attr(“value”);
  6.            contact.EmailAddress = $(“#emailAddress”).attr(“value”);
  7.            $.ajax({
  8.                url: “http://localhost:10401/api/contacts/AddContact.ashx”,
  9.                type: “POST”,
  10.                dataType: “json”,
  11.                data: $.toJSON(contact),
  12.                success: function () { loadAllContacts(); }
  13.            });
  14.        }

3.使用代理

假设你有两个web应用:一个应用放html页面,给用户提供界面;一个应用放服务,使用.ASHX处理请求。

你在html应用中使用ajax请求ashx应用的接口,就是ajax跨域请求。

你可以在html应用中写一些后台代码,在代码中向ashx应用提交数据。

然后你的html应用的页面中将收集到的数据发送到html应用的后台代码中,由后台代码发送数据到ashx应用,这就不是ajax跨域请求了。

在html应用中的后台代码就被叫做“代理”,代理html应用到ashx应用的请求。

 

参考文档

1.implementing-cors-support-in-asp-net-web-apis

2.Implementing CORS support in ASP.NET Web APIs – take 2

3.说说JSON和JSONP,也许你会豁然开朗,含jQuery用例

4.Cross-Origin Resource Sharing

5.cannot POST to IHTTPHandler class, origin not allowed by Access-Control-Allow-Origin

6.Secure Cross-Domain Communication in the Browser

7.HTTP access control (CORS)

8.jQuery File Uploader, Cross Domain Requests, and ASHX Web Services

9.jquery跨域访问解决方案

submit data to server by javascript POST when cross domain

Result:

[1] asynchronous post does not support
[2] synchronous post is OK
[3] create a iframe to post data is OK

Reffer:

[1] http://blog.163.com/wger_163_dh/blog/static/14264615520130243568649/
[2] http://hi.baidu.com/gguoyu/item/aa501b0ed6a0fb8e03ce1b66

[3] http://www.w3.org/TR/access-control/

[4] http://dev.w3.org/2006/waf/access-control/

Content:

 

利用form表单跨域post

继续阅读“submit data to server by javascript POST when cross domain”

Javascript: 通过图片url获取图片blob对象

为什么要这样做呢?

  1. 无需让用户下载图片后再通过 input file 上传;
  2. chrome插件可以直接右键点击页面上的图片,直接上传图片;
  3. 都是为了增加用户体验!

思路

  1. 通过ajax请求图片,得到图片的二进制数据
  2. 组合Uint8Array和BlobBuilder,得到图片的blob对象
  3. 增加fileName和fileType,伪装成File对象

 

实现代码

[codesyntax lang="javascript"]
/**
 * 将符合字节流的string转化成Blob对象
 * 
 * @param {String} data
 * @return {Blob} 
 * @api public
 */
function binaryToBlob(data){
    var bb =newBlobBuilder();
    var arr =newUint8Array(data.length);
    for(var i =0, l = data.length; i < l; i++){
        arr[i]= data.charCodeAt(i);
    }
    bb.append(arr.buffer);
    return bb.getBlob();
};

/**
 * 根据URL获取图片的Blob对象
 * 
 * @param {String} url
 * @return {Blob} 
 * @api public
 */
function getImageBlob(url){
    var r =newXMLHttpRequest();
    r.open("GET", url,false);
    // 详细请查看: https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest#Receiving_binary_data
    // XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
    r.overrideMimeType('text/plain; charset=x-user-defined');
    r.send(null);
    var blob = binaryToBlob(r.responseText);
    blob.name = blob.fileName = url.substring(url.lastIndexOf('/')+1);
    blob.fileType ="image/jpeg";//"image/octet-stream";
    return blob;
};

/**
 * 将dataUrl转化成Blob对象
 * 
 * @param {String} dataurl
 * @return {Blob} 
 * @api public
 */
function dataUrlToBlob(dataurl){
    // data:image/jpeg;base64,xxxxxx
    var datas = dataurl.split(',',2);
    var blob = binaryToBlob(atob(datas[1]));
    blob.fileType = datas[0].split(';')[0].split(':')[1];
    blob.name = blob.fileName ='pic.'+ blob.fileType.split('/')[1];
    return blob;
};
[/codesyntax]

javascript中的arguments

function aaa(sTemplate) {
 var args = arguments;
 var s = sTemplate;

  s = s.replace(/\%\%/g, “%”);

  for (var i = 1; i < args.length; i++)
    s = s.replace( new RegExp(“\%” + i + “\%”, “g”), args[i] )

  return s;
}

arguments虽然不是数组,但可以当作数组使用,下标由 0 开始,所以:

arguments[0]  表示接收的第一个参数

arguments[1]  表示接收的第二个参数

……

如此类推……

这样就可以实现不同参数调用同一个函数了~

当然,前提是函数设置了对该参数的处理方法,不然还是白搭

 

arguments 属性

为当前执行的 function 对象返回一个arguments 对象。

 

function.arguments

function 参数是当前执行函数的名称,可以省略。

 

arguments 对象的 length 属性包含了传递给函数的参数的数目。arguments 对象所包含的单个参数,访问方法与数组中所包含的参数的访问方法相同。

 

摘抄了一个示例,说明了 arguments 属性的用法。可以做下参考:

function ArgTest(){

   var i, s, numargs = arguments.length;

   s = numargs; 

   if (numargs < 2){

      s += ” argument was passed to ArgTest. It was “;

   }else{

      s += ” arguments were passed to ArgTest. They were ” ;

   

   for (i = 0; i < numargs; i++){

         s += arguments[i] + ” “;

   }

   return(s);

}

 

引用:

一、巧用arguments

    在 Javascript 的函数中有个名为 arguments 的类数组对象。它看起来是那么的诡异而且名不经传,但众多的 Javascript 库都使用着它强大的功能。所以,它的特性需要每个 Javascript 程序员去熟悉它。

    在每个函数中,都有个名为 arguments 的变量,它以类似数组的形式保存了当前调用的参数。而它实际上并不是个数组,使用 typeof arguments 语句尝试会返回“object”(对象),所以它不能像 Array 一样使用 push 和 pop 等方法。即便如此,仍然可以使用下标以及长度属性(length)获取它的值。

 

二、编写灵活的函数

   虽看起来名不经传,但的确 arguments 是非常有用的对象。比如,你可以让函数处理不定数目的参数。在 Dean Edwards 写的 base2 库中,有个叫 format 的函数充分发挥了这一特性:

 

function format(string) {

  var args = arguments;

  var pattern = new RegExp(“%([1-” + arguments.length + “])”, “g”);

  return String(string).replace(pattern, function(match, index) {

    return args[index];

  });

};

 

    replace这个函数的第二个参数可以为一个函数,函数的第一个参数可以为匹配了的文本,第二个参数为第几个匹配的值,返回值为要进行替换的文本

这个函数实现了模板替换,你可以在要动态替换的地方使用 %1 到 %9 标记,然后其余的参数就会依次替换这些地方。例如:

format(“And the %1 want to know whose %2 you %3”, “papers”, “shirt”, “wear”);

上面的脚本就会返回

“And the papers want to know whose shirt you wear” 。

在这里需要注意的是,即便在 format 函数定义中,我们仅定义了个名为 string 的参数。而 Javascript 不管函数自身定义的参数数量,它都允许我们向一个函数传递任意数量的参数,并将这些参数值保存到被调用函数的 arguments 对象中。

 

三、转换成实际数组

    虽然 arguments 对象并不是真正意义上的 Javascript 数组,但是我们可以使用数组的 slice 方法将其转换成数组,类似下面的代码

var args = Array.prototype.slice.call(arguments);

call(obj,当前函数使用的参数列表)

call方法第一个参数为一个对象,这个传进去的对象将调用slice函数.因为arguments不是一个数组,所以不能直接调用slice方法,所以只能使用”对象冒充”方法了

这样,数组变量 args 包含了所有 arguments 对象包含的值。

 

四、使参数构建函数

    使用 arguments 对象能够简短我们编写的 Javascript 代码量。下面有个名为 makeFunc 的函数,它根据你提供的函数名称以及其他任意数目的参数,然后返回个匿名函数。此匿名函数被调用时,合并的原先被调用的参数,并交给指定的函数运行然后返回其返回值。

function makeFunc() {

  var args = Array.prototype.slice.call(arguments);

  var func = args.shift();

  return function() {

    return func.apply(null, args.concat(Array.prototype.slice.call(arguments)));

  };

}

arguments有一个不可枚举的属性callee(不能用for in读出,可用HasOwnProterty(name)来判断),arguments.callee为正被执行的 Function 对象。slice时己把当前函数指针copy了过去,所以args的第一个元素为函数类型

makeFunc 的第一个参数指定需要调用的函数名称(是的,在这个简单的例子中没有错误检查),获取以后从 args 中删除。makeFunc 返回一个匿名函数,它使用函数对象的(Function Object)apply 方法调用指定的函数。

apply 方法的第一个参数指定了作用域,基本上的作用域是被调用的函数。不过这样在这个例子中看起来会有点复杂,所以我们将其设定成 null ;其第二个参数是个数组,它指定了其调用函数的参数。makeFunc 转换其自身的 arguments 并连接匿名函数的 arguments,然后传递到被调用的函数。

有种情况就是总是要有个输出的模板是相同的,为了节省每次是使用上面提到的 format 函数并指定重复的参数,我们可以使用 makeFunc 这个工具。它将返回一个匿名函数,并自动生成已经指定模板后的内容:

var majorTom = makeFunc(format, “This is Major Tom to ground control. I’m %1.”);

你可以像这样重复指定 majorTom 函数:

majorTom(“stepping through the door”);

majorTom(“floating in a most peculiar way”);

那么当每次调用 majorTom 函数时,它都会使用第一个指定的参数填写已经指定的模板。例如上述的代码返回:

“This is Major Tom to ground control. I’m stepping through the door.”

“This is Major Tom to ground control. I’m floating in a most peculiar way.”

 

五、自引用的函数

    您可能会认为这很酷,先别急着高兴,后面还有个更大的惊喜。它(arguments)还有个其他非常有用的属性:callee 。arguments.callee 包含了当前调用函数的被引用对象。那么我们如何使用这玩意做些的事情?arguments.callee 是个非常有用的调用自身的匿名函数。

下面有个名为 repeat 的函数,它的参数需要个函数引用和两个数字。第一个数字表示运行的次数,而第二个函数定义运行的间隔时间(毫秒为单位)。下面是相关的代码:

function repeat(fn, times, delay) {

  return function() {

    if(times– > 0) {

      fn.apply(null, arguments);

      var args = Array.prototype.slice.call(arguments);

      var self = arguments.callee;

      setTimeout(function(){self.apply(null,args)}, delay);

    }

  };

}

repeat 函数使用 arguments.callee 获得当前引用,保存到 self 变量后,返回个匿名函数重新运行原本被调用的函数。最后使用 setTimeout 以及配合个匿名函数实现延迟执行。

作为个简单的说明,比如会在通常的脚本中,编写下面的提供个字符串并弹出个警告框的简单函数:

function comms(s) {

  alert(s);

}

好了,后来我改变了我的想法。我想编写个“特殊版本”的函数,它会重复三次运行每次间隔两秒。那么使用我的 repeat 函数,就可以像这样做到:

var somethingWrong = repeat(comms, 3, 2000);

somethingWrong(“Can you hear me, major tom?”);

结果就犹如预期的那样,弹出了三次警告框每次延时两秒。

 

最后,arguments 即便不会经常被用到,甚至显得有些诡异,但是它上述的那些惊艳的功能(不仅仅是这些!)值得你去了解它。

Hello,Travis CI

http://dreamhead.blogbus.com/logs/227268386.html

 

Hello,Travis CI

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://www.blogbus.com/dreamhead-logs/227268386.html

Travis CI,是一个专门为开源项目打造的持续集成环境。

如果你有一个放在github上的开源项目,Travis CI简直就是一个完美的CI选择。下面以Moco为例,说明如何在自己的项目里添加Travis CI支持。

实际上,只要采用的是“标准工具”,支持Travis CI就简单得一塌糊涂。第一步,我们要在自己项目的根目录下添加一个文件.travis.yml。

language: java
jdk:
– oraclejdk7
– openjdk7
– openjdk6
(.travis.yml)

很容易理解,首先,我们告诉Travis CI,我们的语言是什么。这样,它会根据你的语言为你选择构建工具。对于Moco而言,构建工具是gradle,这是Java世界的新标准了,Travis CI会自动识别出来,我不需要额外告诉它什么。当然,它为Java项目支持的另外两个选择是MavenAnt。如果你的项目是不同的语言,可以参考Travis CI的文档,找到适合自己的配置。

接下里的JDK是要告诉Travis CI,我要在哪些环境下测试。比如这里用了三个JDK,分别是Oracle JDK 7、OpenJDK 7和OpenJDK 6。这样一来,当我们提交代码时,Travis CI会在三个不同环境运行我们的测试,以此保证项目的版本兼容。

此外,还要额外说一下,它是如何选择运行目标的。对于Moco这样提供了Gradle支持的项目,Travis CI缺省情况下会执行gradle check,这是Gradle进行检查的缺省做法,它会编译源文件、处理资源文件、运行测试等等。在运行这个任务之前,Travis CI还会运行gradle assemble用以安装项目中用到的依赖。所有这些都是可以修改的,可以参考了Travis CI的构建配置文档

有了这个脚本还不够,我们还要让Travis CI知道我们的项目,用自己的github账号登录Travis CI。经过认证之后,选择自己的账号配置,你会看到自己的所有的开源项目都列在那里。如果没有看到项目,请自行同步(Sync Now)。剩下的就很简单了,把要支持的Travis CI项目从OFF状态拨成ON。实际上,Travis CI是给github设置一个钩子。当有代码提交上来的时候,它会告诉Travis CI,Travis CI就可以开始构建了。

好,设置完毕,尝试提交代码吧!当代码提交到github上之后,你应该可以看到你的项目开始构建了。哦,对了,项目的Travis CI地址应该等同于github上项目的地址,比如,Moco的github地址是

https://github.com/dreamhead/moco

它的Travis CI地址就是

https://travis-ci.org/dreamhead/moco

最后,再来一个小技巧,为了让别人知道我们项目和Travis CI有关系,我们可以在自己项目的README上给出Travis CI的构建标识。在你的Travis CI项目页面上,你应该可以找到一个配置选项。写下本文时,它就是右上角那个小齿轮,里面有个Status Images。根据自己README格式选择一个,粘贴到README文件里。

好,大功告成,我们的开源项目也有了自己的CI。

利用Travis CI 让你的github项目持续构建(Node.js为例)

http://www.cnblogs.com/whitewolf/archive/2013/04/14/3019838.html

 

Travis CI 是目前新兴的开源持续集成构建项目,它与jenkins,GO的很明显的特别在于采用yaml格式,简洁清新独树一帜。目前大多数的github项目都已经移入到Travis CI的构建队列中,据说Travis CI每天运行超过4000次完整构建。对于做开源项目或者github的使用者,如果你的项目还没有加入Travis CI构建队列,那么我真的想对你说out了。

下面是本人的构建历史:

  搭建Travis CI build,需要你有个github账号和github项目:

1:用github账号登陆Travis CI.

2 :在右上角你的账户名点击进入 account,在Repositories tab页点击Sync now同步你的github项目,

3:选中项目将默认的off改变为on开启项目的持续集成。

4:在你项目的根目录建立一个.travis.yml文件,内容为:

language: node_js

node_js:

  • 0.4

  • 0.6

5: 在打开你的node.js的package.json文件,确保加入script/test节点:

“scripts”: {
“test”: “XXXX”
},

这里你可惜选择mak或者jasmine-node等node.js测试框架的测试命令。并且可以把依赖加入package的depends

6:在你项目中运行npm test,确保正常工作。

7: check in你的code到github,代开tracivs ci界面等待其同步并运行你的build构建。

 

如果你需要将你的build构建状态放在一个显眼的位置或者项目readme,你可以在首页My Repositories中找到项目并设置中复制状态图片code,形如:

https://travis-ci.org/greengerong/qing.png?branch=master” alt=”Build Status” />

Travs CI 支持多中语言如ruby,java的maven,gradle,Go等请参见文档Travis Docs.

在上面提到的travis.yml文件中我们还可以加入build前后执行脚本,形如:

before_script:

  • before_command_1

  • before_command_2

after_script:

  • after_command_1

  • after_command_2

 

将你的开源项目加入Travis CI队列吧,很容易让你的项目加入持续集成,持续构建队列。

 

 

模块化高扩展性的前端框架 KISSY

src: http://yiminghe.iteye.com/blog/1829334

注:本文为 2013 年 3 月程序员杂志同名文章的完整版。

介绍

伴随着淘宝的快速发展,诞生已三年多的 KISSY 也取得了巨大的成长。 目前应用于阿里集团的多个业务团队,特别是淘宝,天猫,一淘的绝大多数业务都采用了 KISSY, 满足了从前台的 web page 到后台的 web app 再到移动端应用(起步阶段)的广泛需求。

在阿里集团以外也有不少公司和个人使用 KISSY,交流旺旺群成员已将近千人, 在 github 源码库也时常有外部人员参与提交 issue,发起 pull request,另外在 oschina 等开源社区也受到了一定的关注。

淘宝在 2012 年 12 月 25 号发布了 KISSY 最新版本 1.3, 在这篇文章中我将对 KISSY 1.3 结合在淘宝的实践做一次全面概述,希望能对想了解淘宝前端技术的朋友有所帮助。

为什么选择 KISSY

KISSY 作为国内一个完全自底向上开发起来的框架,诞生三年来历经淘宝的各种业务变化的考验,体现了 KISSY 的可扩展性,高稳定性和可维护性。 它在以下方面具有一定优势:

  • 拥有大量的中文文档: 方便不熟悉英文的国内开发者快速入门学习。
  • 在国内具备一定的社区规模: 通过旺旺群,google group,微博,官方网站等用户随时可以和开发人员快速交流。
  • 开发便捷: 在模块化,组件以及工具辅助方面形成了一套完善的机制,可以高效应对日常的需求开发。
  • 综合借鉴国际先进的框架类库设计:
    • 在吸收 jquery 稳定的 dom 核心之外提供了模块化,按需加载的基础架构。
    • 在学习 yui 坚实的架构之外着重于组件开发,积累了一批贴近淘宝应用的组件集。
    • 在研究 extjs 优秀的组件设计之外探索轻量级,可扩展的组件开发,同时满足 web app 与 web page 的需求。
  • 应用场景广泛: 在各种设备上都有所支持。
    • 可以使用 seed 和 core 简单快速地搭建页面
    • 也可以进一步使用 KISSY 的大量组件构建富客户端应用
    • 也可以使用统一的 API 开发移动应用
    • 更可以利用 KISSY 灵活的模块化机制高效进行大规模团队协作

不过由于 KISSY 开发时间还不长,在组件完备性以及可测试性上有待进一步完善,我们坚信这种情况会随着团队的不断努力而逐步解决。

架构

KISSY 的架构由淘宝复杂多变的业务决定,在松耦合、无污染、模块化的核心前提下,也从众多优秀类库和框架的思路得到一些启发。 如图1所示:

图1: KISSY 整体架构图

KISSY-architecture

  1. 最底层的 seed 是类似目前流行的 AMD 模块化机制实现,为了更高效地共享通用组件加入了自动 combo 的支持, 另外 seed 也包含一些像 each(循环数组以及对象),mix(合并对象),param(编码对象为 url), ready(等待 dom ready)等常用的静态工具方法以及 Path,Uri,Promise,UA 等模块化需要用到的基础类。
  2. seed 之上的第二层是处理 dom 兼容性的核心模块,其中每个模块都是由更小的模块打包合并而来。 特殊之处在于 KISSY 把一些不标准浏览器的兼容代码单独抽取成内部模块,用户可用的外部模块会根据不同的设备分别依赖不同的内部模块。 虽然加载不同的具体实现模块代码,但是最终提供给用户的是一致的模块名,api 以及渐进增强的能力。 例如用户使用 event 模块,在 touch 设备上会加载 event/touch 模块从而可以使用一些手势事件, 而在 ie<9 下会加载 event/hashchange 模块,以类似 es5-shim 的方式来补全浏览器的能力,提供给用户统一的 api。
  3. 第三层为组件架构层。提供包括模拟 es5 属性管理,插件和多继承机制的 rich-base 模块, 所有 UI 组件渲染机制的基类 component 模块以及具备一定逻辑的模板引擎 xtemplate。
  4. 第四层为独立可用的 KISSY 组件,用户可自由组合继承搭建最终页面。包括:
    • 工具模块,例如拖放,调整大小,操作 swf,操作样式表,mvc(model,router)架构等。
    • UI 组件,例如弹窗,菜单,标签,日历等。
  5. 最外层为 KISSY gallery,KISSY 社区开发的一些通用模块会放入 gallery 中, 从而可以方便得在所有使用 KISSY 的团队间共享模块。 如果该模块确实十分通用则会经过重构放入 KISSY 自身。

模块化机制

介绍

由于淘宝业务的复杂,为了提高代码的可维护性和重用性,KISSY 在早期就引入了简单的模块化机制,并且随着前端技术的发展而不断改进。 KISSY 1.3 的模块化机制与目前的 AMD 规范比较类似,并根据淘宝自身业务特点加入了自动 combo 功能。

注:combo 举例:请求 a.tbcdn.cn/??a.js,b.js 相当于把 a.tbcdn.cn/a.js 与 a.tbcdn.cn/b.js 的文件内容合并后返回。

KISSY 之外的每个模块必须属于一个包,一个包内可以有很多相关模块,它们具备相同的加载基地址。 包的设计一方面通过约定优先配置的原则可以减少同一个包内多个模块的请求路径配置, 另一方面也方便了部署在不同地址的多个应用间互相调用模块。 所以开发应用前请先配置包地址,例如:

Js代码  收藏代码
  1. KISSY.config(‘packages’, {  
  2.     myapp: {  
  3.         base:‘./x’  
  4.     }  
  5. });  

 

之后在 x 目录下建立 myapp 目录,并在 myapp 目录下新建模块 a 对应的代码文件: a.js

Js代码  收藏代码
  1. KISSY.add(function(S,JSON){  
  2.     return JSON.stringify({a:‘ok’});  
  3. },{  
  4.     requires:[‘json’]  
  5. });  

 

以及依赖 a 的入口主模块 main 的代码文件: main.js

Js代码  收藏代码
  1. KISSY.add(function(S,DOM,a){  
  2.     S.ready(function(){  
  3.         DOM.text(document.body,a);  
  4.     });  
  5. },{  
  6.     requires:[‘dom’,‘./a’]  
  7. });  

 

应用模块可依赖 KISSY 自身模块,例如以上的 dom json。

最后新建 index.html 来调用应用模块:

Js代码  收藏代码
  1. // … 上述包配置  
  2.   
  3. KISSY.use(‘myapp/main’,function(){  
  4.     alert(‘page loaded’);  
  5. });  

 

工具支持

打开上述 index.html 会发现链接数不少,有从淘宝 cdn 的链接也有本地的链接。 因此 KISSY 提供了配套工具 KISSY Module Compiler 进行脚本打包以及抽取依赖后用 cdn combo 来解决这个问题, 对工具有兴趣可查看参考资料中的链接地址,以下仅作简单介绍:

脚本打包

如果应用脚本非常多,可以用 module compiler 将入口模块 main 以及其依赖的所有应用模块打包压缩到 main-min.js 中, 同时 module compiler 会生成一份依赖描述文件:

Js代码  收藏代码
  1. KISSY.config(‘modules’,{  
  2.     ‘myapp/main’:{  
  3.         requires:[‘dom’,‘json’]  
  4.     }  
  5. });  

 

接着设置 KISSY 启用 combo 模式,并载入上述的依赖描述文件:

Js代码  收藏代码
  1. KISSY.config(‘combine’,true);  

 

最后修改 index.html 引用 seed-min.js,打开网络面板后会发现现在只产生两个链接请求:

Js代码  收藏代码
  1. http://a.tbcdn.cn/s/kissy/??dom/base,json/native.js  
  2.   
  3. http://localhost/myapp/main-min.js  

 

注:在 ie<9 等非标准浏览器下第一个链接地址会加上 dom/ie json/json2 等脚本地址

抽取依赖

如果应用脚本不多,并且自身服务器也支持 combo 的话,那么可以采用更加灵活的抽取依赖后全部 combo 的解决方案。 这时 module compiler 会做两件事情:

  1. 补全模块名,例如 a.js 变为
    Js代码  收藏代码
    1. KISSY.add(‘myapp/a’,function(S,JSON){  
    2.     return JSON.stringify({a:‘ok’});  
    3. },{  
    4.     requires:[‘json’]  
    5. });  
  2. 将各个模块的依赖收集为一个单独的文件,例如
    Js代码  收藏代码
    1. KISSY.config(‘modules’,{  
    2.     ‘myapp/main’:{  
    3.         requires:[‘dom,’,‘./a’]  
    4.     },  
    5.     ‘myapp/a’:{  
    6.         requires:[‘json’]  
    7.     }  
    8. });  

接着设置 KISSY 启用 combo 模式,并载入上述的依赖描述文件:

Js代码  收藏代码
  1. KISSY.config(‘combine’,true);  

访问 index.html,打开网络面板会发现同样只产生两个链接请求:

Js代码  收藏代码
  1. http://a.tbcdn.cn/s/kissy/??dom/base,json/native.js  
  2.   
  3. http://localhost/myapp/??a.js,main.js  

KISSY-PIE

KISSY 还提供了一套基于约定的前端打包解决方案 KISSY-PIE,通过统一的约定,提升应用的可维护性,将大家从重复的打包脚本的编写中解放出来。

KISSY-PIE 包括了以下功能:

  • JS(KISSY 的模块编译,HTML 模板到 KISSY 模块编译,代码压缩)
  • CSS(基于 CSS-Combo 的合并,压缩)
  • Less
  • Sass

并且在命令行之外还提供了 web 操作界面,如图2所示:

图2: KISSY-PIE 打包界面

kissy-pie

组件机制

核心

KISSY 目前包括众多即开即用的组件, 包括工具性质的例如 dd(拖放),resizable(调整大小),swf(插入flash),stylesheet(操作样式表)等 和 UI 性质的例如 overlay(弹窗),menu(菜单),menubutton(菜单按钮),imagezoom(放大镜),editor(编辑器), tabs(标签),tree(树)等。 这些组件都基于公共的 rich-base 以及 component 模块: rich-base 和 component 模块充分利用了 javascript 语言的 mixin 和原型链继承, 提供了属性绑定,类继承,扩展以及插件等特性。

其中的重点是 Component,它是所有 UI 组件的基类,提供了两种通用的渲染方式:

  1. 组件实例由 javascript 完全渲染 dom 树。其中对于 menu 等组件在 javascript 完全渲染的情况亦可以通过 json 初始化内部嵌套组件。例如
    Js代码  收藏代码
    1. KISSY.use(‘menu’,function(S,Menu){  
    2.     // javascript 渲染一个菜单到 body  
    3.     new Menu({  
    4.         children:[{  
    5.             content:‘item1’  
    6.         }]  
    7.     }).render();  
    8. });  
  2. 从已有的 dom 树节点得到组件实例。

下面以 KISSY 中常用的一个组件 Overlay 为例讲解下,首先看图3类结构图:

图3: Overlay 类结构图

overlay

Overlay 继承自 Component,然后静态地由一些分散的功能类扩展而来,包括定位功能类,对齐功能类,关闭功能类,遮罩层功能类, 这些功能类底层利用 javascript 的 mixin 功能将自己的方法和属性汇入到 Overlay 类中,使得最终的 Overlay 具备了这些能力。

组件在运行时也可以选择性依赖某些插件模块,将插件的功能注入到组件中去, 既避免了一个组件过于功能繁多导致的文件过大,又大大增强了组件的可扩展性。

以下为 Overlay 的使用示例代码:

Js代码  收藏代码
  1. KISSY.use(‘overlay,component/plugin/resize’,function(S,Overlay,ReizePlugin){  
  2.   
  3.     // 完全由 javascript 将组件实例渲染到 body 中  
  4.     new Overlay({  
  5.         content:‘test’  
  6.     }).render();  
  7.   
  8.     // 从已有的 dom 树节点生成 Overlay 实例  
  9.     var overlay = new Overlay({  
  10.         srcNode:‘#existing’  
  11.     }).render();  
  12.   
  13.     // 运行时加入调整大小的插件能力  
  14.     overlay.plug(new ReizePlugin({  
  15.         handles:[‘t’]  
  16.     }));  
  17.   
  18. });  

Brix

除了 KISSY 自身的组件机制,一淘开发人员根据自己的应用特点在组件开发和使用上找寻了另一条途径。即 Brix 解决方案:

  • 基于统一的渲染方式:模板(tmpl)和数据(data)产生html片段后使用 innerHTML 到 DOM 节点中.
  • 提取子模板,结合数据的更新,达到局部刷新,开发者不需要再关心页面的表现,而专心于数据的变化。
  • DOM 节点自定义属性设置组件标志,Brix 提供 Pagelet 按照统一的方式实例化组件。

图4: Brix 类库结构

brix

设备普适性

当前 javascript 的使用范围越来越广,平台包括 pc 浏览器与读屏器,nodejs,移动端浏览器以及各种外壳,window8 等。 KISSY 也尽量在各个平台给予支持,保证统一的开发体验。

在 nodejs 上 KISSY 通过调整模块加载器使得 KISSY 可以直接将自身的模块加载到 nodejs 中使用。例如

  • 可以使用 KISSY 的 UA 解析模块来分析日志中的 UA 串
  • 可以载入 jsdom 模块在 nodejs 环境下做单元测试
  • 用 xtemplate 在服务器端渲染前端模板
  • 使用 KISSY 的 htmlparser,color 等工具模块

经统计约有 1000 万残疾人用户在使用淘宝,其中不少是受影响最大的盲人朋友,他们实际上是通过读屏器来和淘宝交互, KISSY 组件通过遵循 WAI-ARAI 规范来给视力受损的朋友提供无障碍的访问环境,网上人人平等。

KISSY 也计划对 windows8 进行支持,在即将发布的 tmall windows8 app 中仅仅通过使用 KISSY 的模块化机制以及一些语法糖 API 就可以达到和平常开发一样的高效。

随着用户越来越多地在移动设备上购物产生交易,移动应用在流量的比重上也越来越多,KISSY 及时适配移动设备, 目前在两方面进行了支持:

  1. 根据设备条件加载。对于移动端由于网络速度等原因对于文件大小比较敏感,KISSY 为了保证一致的 API 又不能随意删减功能, 因而采取了独立兼容模块的方法来保持瘦身。在架构一节也阐述过,KISSY 对基础核心模块中的兼容非标准浏览器的模块进行选择性加载, 同时移动设备上都是标准浏览器,从而可以大大减少实际下载到用户设备上的代码大小。部分组件也实行了拆分,将 pc 的功能交互独立到单独的模块,在移动触摸设备上只加载触摸交互需要用到的模块。
  2. 渐进增强 API。对于触屏设备,很多交互是建立在手势操作上。而手势操作除了在 safari 上有 gesture 事件做有限支持外, 在 android 上则完全没有对应事件。 得益于 KISSY 易于扩展的事件机制,KISSY 在底层多点触摸 touch 事件的基础上模拟出了 tap rotate pinch 等触屏设备上独有的事件,这对于用户则是透明,用户完全可以把这些事件当做原生事件来使用,例如
    Js代码  收藏代码
    1. KISSY.use(‘event’,function(S,Event){  
    2.     // 监听 div 上的 tap 事件  
    3.     Event.on(‘#div’,‘tap’,function(){  
    4.     });  
    5. });  

ZOOJS

另外淘宝北京团队还基于 KISSY 核心打造了专门面向 Web 无线设备的一整套工具库 ZOOJS, 包含控件级的事件支持、触屏行为的封装、富控件、皮肤、App的基础架构等。 这套独特的 Web 无线解决方案力争将 HTML5 和 CSS3 的优势发挥至最佳,做到即调即用。

测试与持续集成

KISSY 经过三年的开发代码库已经相当庞大了,模块间还常常有依赖关系,修改代码有牵一发而动千钧的后果。 为了应对此问题,KISSY 也在逐渐完善单元测试,自动化测试与持续集成。

单元测试

KISSY 一个模块的常见目录结构如图5所示:

图5: 模块目录结构

KISSY test

其中 tests 目录下为测试资源,runner 目录下启动测试的 html 文件,specs 下为对应模块的单元测试代码。 KISSY 采用的单元测试框架为 jasmine ,测试代码举例如下:

Js代码  收藏代码
  1. describe(‘S.mix’,function(){  
  2.     it(‘works for simple case’,function(){  
  3.         expect(S.mix({x:1},{y:1})).toEqual({x:1,y:1});  
  4.     });  
  5. });  

然后打开启动测试的 html 文件 即可看到图6所示的单元测试结果:

图6: 单元测试结果

run test

持续集成

为了提高测试效率,KISSY 还依赖 travis 平台和 phamtomjs 进行持续集成测试。 每次提交代码都会在 travis 平台上启动 phantomjs 来运行 KISSY 所有模块的单元测试代码。 如图7所示:

图7: travis 平台上的 KISSY

travis

淘宝应用场景举例

淘宝目前的绝大多数页面已经采用 KISSY 搭建,这次我选取两个大家使用比较多的应用来介绍下 KISSY 在淘宝的实践:

店铺页面

店铺页面是商家店铺的门户,除了淘宝页头之下都可以由商家自定义内容,如图8所示:

图8:典型的店铺页面

shop

可以看出页面本身就是区块化的组织,在程序内部也是分成很多个模块,例如店内搜索模块,宝贝分类模块,销量统计模块等, 每个模块负责页面一块区域的交互实现,这些模块又会调用 KISSY 的模块来实现自身的逻辑。 而每个商家的店铺可能使用到的区块并不相同,这也意味着每个商家店铺所用到的程序模块也不相同。

店铺模块与 KISSY 模块依赖关系如图9所示:

图9:店铺模块与 KISSY 模块的关系

shop-mods

在实际开发中配置店铺应用为一个包,其内的所有模块都放入这个包内,最后由页面初始化脚本加载当前店铺需要的模块列表。例如:

Js代码  收藏代码
  1. KISSY.use(‘shop/search,shop/category,…’);  

在线上会发出两个 combo 请求:一个为店铺页面需要的应用模块集,一个为 KISSY 自己的模块集,例如

Js代码  收藏代码
  1. http://a.tbcdn.cn/s/kissy/1.3.0/??dom/base.js,event/base.js,overlay.js…  
  2.   
  3. http://a.tbcdn.cn/p/shop/??search.js,category.js…  

宝贝详情应用

宝贝详情页面用来展示商家单个商品的详细信息,评价,成交趋势等信息,并为下一步购买做准备,是目前淘宝访问量最大的页面。

该应用和店铺应用紧密相关,从页面可以看出,很多区块和店铺页面对应区块相同,实际上在代码层面也是引用同一份模块, 首先宝贝详情应用和店铺应用一样加载当前页面用到的店铺模块,然后再加载这个页面本身的应用模块。 但是宝贝详情业务本身逻辑十分复杂,若像店铺应用一样也是采用模块 combo 的方式则会导致请求 url 过长, 进而 KISSY 会对过长的 combo url 拆分成多个短的 url,反而适得其反。 因此这里会把宝贝详情页自身的模块打包合并,将自己模块的主模块和依赖模块都合并到主模块中去, 最终线上会发出三个请求,其中两个为 combo 请求,一个为非 combo 请求,例如:

Js代码  收藏代码
  1. http://a.tbcdn.cn/s/kissy/1.3.0/??dom/base.js,event/base.js,overlay.js…  
  2.   
  3. http://a.tbcdn.cn/p/shop/??search.js,category.js…  
  4.   
  5. http://a.tbcdn.cn/p/detail/main.js  

总结

KISSY 才刚刚处于成长初期,相对于国外成熟框架尚有不小差距,不过依然承受住了淘宝复杂多变业务的考验。 下一步 KISSY 会继续完善基础组件例如 date,datasource,selector,graphics 等, 重构已有组件例如 switchable,calendar 等, 增加 package manager 方便基于 KISSY 模块的提交与共享,补全测试用例,实现代码覆盖率检测,不断提高运行稳定性。 将来我们坚信 KISSY 会随着淘宝的发展而继续成长,也会随着国内外前端技术的不断发展而不断进步。 希望有兴趣的你也能加入到 KISSY 开发中,一起学习进步,享受成长的乐趣。


参考资料:

KISSY: http://docs.kissyui.com/

KISSY Module Compiler: http://docs.kissyui.com/docs/html/tutorials/tools/module-compiler/index.html

KISSY Module Compiler Node 版本:https://github.com/daxingplay/ModuleCompiler

OSCHINA 访谈:http://www.oschina.net/question/28_71454

nginx combo: https://github.com/perusio/nginx-http-concat

KISSY-PIE:https://github.com/maxbbn/front-build

BRIX:http://etaoux.github.com/brix/

ZOOJS:http://zoojs.org/doc/

KISSY on Travis:https://travis-ci.org/kissyteam/kissy

使用Kissy Pie快速构建—kissy1.4最佳实践探索

kissy1.2模块文件打包问题

来看个第一次使用kissy1.2来构建项目时,需要进行的步骤:

从上图来看,构建的成本主要在构建的环境搭建和build.xml上。

  • 从环境上,需要安装ant以及知道ant的基本使用方法;
  • 从build.xml构建文件上,没办法拿来即用,需要根据项目的目录情况做修改,期间出错的概率颇高

明河收到的关于kissy1.2的问题中,发布前合并问题最为突出,运行ant来打包模块文件时抛异常,对ant的使用和kissy的loader打包机制不是很理解,就很容易不知所措。开发者需要额外的时间研究如何使用ant构建项目文件,如何使用kissy的Module Compiler。

有没有什么办法让开发者从构建编写中解脱出来呢?

Kissy Pie

使用Kissy Pie快速构建kissy项目

Kissy Pie基于nodeJs的专门用于kissy项目构建的工具,由文龙同学为首的自动化工具小组完成,已经实现的功能有:创建项目目录和文件、打包kissy1.2的模块脚本、打包css文件、压缩js和css、快速生成发布目录、编译less等。
经过业务线的尝试使用,Kissy Pie的确可以提高不少前端效率,而使用Kissy Pie的学习成本比使用ant打包低了很多,同时少了构建脚本调试的过程。

Kissy Pie能为你做些什么?
  • 快速创建kissy1.2的assets目录
  • 快速打包模块文件
  • 自动压缩js、css
  • less打包
  • css combo
  • 实时监控打包
  • ……(后续会有更多功能)
Kissy Pie相对于ant构建的优势总结


(PS:FB即Kissy Pie)

安装Kissy Pie

1.安装NodeJSnpm
Kissy Pie工具依托于Nodejs,Nodejs的安装非常的简单,明河就不再累述。
npm的安装,请看《深入浅出Node.js(二):Node.js&NPM的安装与配置》
2.安装Kissy Pie
运行如下命令,会从npm的仓库中拉取Kissy Pie包:

npm install kissy-pie -g;

接下来通过一个demo工程讲解fb的使用。

初始化工程目录

假设新项目名为kissy-pie-demo,对应有个kissy-pie-demo目录。
使用ki init命名初始化项目目录结构:

会生成如下目录:

- app                           // root of app
    ├ common                    // 通用脚本与样式, 可直接引用,独立打包
    ├ utils                     // 通用组件, 使用时打包入page使用, 一般不单独引用
    ├ docs                   //接口文档,非必须
    ├ tools                   //项目工具,非必须
    └ fb.json                   // 应用的配置, fb 应用根路径的标识

下面我们创建个项目的demo页面脚本。

使用ki add命名创建页面目录(注:原文有点问题,下面的命令应为ki add)
ki add demo/1.0


来看下创建的demo目录结构:

- app                           // root of app
    ├ demo                    // demo页面脚本
    │   ├ 1.0                   // page 版本 目录
    │   │   ├ test              // 测试用例 目录
    │   │   ├ page              // page 的入口文件目录, 打包,引用的入口
    │   │   │    ├ mods              // page的模块目录
    │   │   ├ fb-build.sh       // 打包快捷方式
    │   │   ├ fb-build.bat      // 打包快捷方式
    │   │   └ fb.page.json      // page 相关配置

(PS:在kissy pie中约定版本目录比如1.0,为源码目录。)

工程的目录结构已经构建出来了,接下来我们尝试写一些demo脚本。

创建demo脚本

一个demo业务模块脚本:demo/1.0/page/mods/header.js

KISSY.add(function(S,form) {
    return function(){
        form();
        alert('header of refund.');
    }
});

一个demo入口模块脚本:demo/1.0/page/init.js

KISSY.add(function(S, Header) {
    return function(){
        Header();
        return 'this is demo page.';
    }
}, {requires:['./mods/header']});

(PS:此入口脚本依赖mods/header.js。)

使用ki build命令打包出发布目录

发布目录指是以时间为命名的模块打包后的目录,比如项目发布日期为20120802,那么就打包出对应的目录,供线上实际引用(线上不直接引用源码,除非开启调试)。

运行ki build demo/1.0 -t 20120802

打包 1.0(源码) 的 demo(当前PageName) 模块脚本到时间戳目录 ‘20120802’

生成的目录的结构如下:

- app                           // root of app
    ├ demo                    // demo页面脚本
    │   ├ 1.0                   // page 版本 目录
    │   ├ 20120802        //发布目录
    │   │   ├ page
    │   │   │    ├ init.js    //模块打包后的脚本
    │   │   │    ├ init-min.js //压缩后的合并脚本(线上实际引用脚本)
    │   │   ├ build.json

线上加载的是20120802目录下打包后文件init-min.js,而当进行调试时,可以快速切到1.0源码目录。

整个打包过程,比使用kissy的module compiler快速很多,同时用户不需要额外写一个build.xml构建文件。

接下来我们在demo页面中引用上述构建的assets文件。

创建个demo页面

在页面中引入common/package-config.js包配置文件
包配置

FB.config({
    path:'.',   // 基路径(模块路径寻址的基点)
    name:'demo',   // 页面名(会拼入模块js路径中)
    version:'1.0',   // 源码目录名(fb的目录约定版本号目录即源码目录),也会拼入模块js路径中
    pub:'20120802'  //发布目录即模块打包后目录(当页面中引入的kissy-min.js,会去引用该文件夹的模块js,比如init-min.js)
});

use入口模块

KISSY.use('page/init',function(S,Apply){
    var msg = Apply();
    S.log(msg);
})

完整demo页面代码请看demo.html

默认加载模块情况:

加载的是20120802目录时间戳下的打包后文件。

页面的url后加上ks-debug后(开启调试):


加载的是1.0目录下的源码文件。

使用Kissy Pie还可以打包less和css文件

新建个page/style.less,代码如下:

@import './mods/header.less';

再新建个page/mods/header.less,代码如下:

body{
    background-color: #ccc;
}

再次运行ki build demo/1.0 -t 20120802,less文件自动编译成css文件!
(ps:除了less外,还支持css模块文件打包,思路差不多,在page/style.css中import mods/下css文件。)

使用批处理文件快速构建

使用Kissy Pie创建页面目录后,会在目录下自动生成fb-build.batfb-build.sh

打开fb-build.bat,编辑成如下代码:

ki build demo/1.0 -t 20120802

双击fb-build.bat,就可以快速构建该页面脚本,无需进入命令行工具。

Kissy Pie工具的使用就到这里,更多的使用技巧,请看文档

Kissy Pie构建的目录总揽

关于common目录

common为静态资源引用目录,有些脚本可能我们并不需要异步加载,同时又是应用级(即每个页面都会引用到)的文件,比如页头公用脚本等。

关于utils目录

utils为应用公用组件目录,比如demo页面都会用到form(表单组件),可以放到此目录,业务模块use时可以这么处理:

KISSY.add(function(S,form) {
},{requires:['utils/form']});

Kissy Pie会自动将utils的代码打包进去。

参考:

https://github.com/neekey/generator-kissy-pie