GithubHelp home page GithubHelp logo

mt's Introduction

MT

MT是手机腾讯网前端团队开发维护的一个专注于移动端的、带有增量更新特色的js模块管理框架

我们的官网是https://mtjs.github.io

我们的github:https://github.com/mtjs/mt,如果觉得MT是个靠谱的项目,请给我们star,您的支持是我们最大的动力

为什么使用MT

在快速迭代版本过程中,我们有时候只修改了某个js中的几行代码,却需要用户下载整个js文件,这在重视流量的移动端显得非常浪费,mt独创的增强更新算法实现了修改多少代码就只下载修改代码的功能,为用户和公司节省大量流量

比如某次修改我们需要修改下面的代码,在mt mthelloworld 后面加上ok两个字符,即把

        define('mthelloworld', [], function () {
                    console.log('mt helloworld');
        });
        }
    

修改为

        define('mthelloworld', [], function () {
                    console.log('mt helloworld ok');
        });
        }
    

通常情况下我们需要用户下载整个这个模块的js代码,但是如果使用了mt,用户只需要下载一下一行代码:

       [[0,50],'ok',[52,90]]
    

这就是MT的主要作用,在版本更新的时候能做到字符级别的增量更新,为用户和公司节省流量,真正做到了无更新不下载!

MT增量更新技术方案介绍

总体流程介绍

增量更新依赖于localstorage,所以浏览器必须支持localstorage。android和ios两大平台目前都支持。

增量更新流程如下图所示:

localstorage里面存储的上个版本的js内容和版本号,当本次版本号和上次版本号不一致的时候,mt拼接出增量文件url去拉取增量文件,并和上个版本的js内容合并生成新版本内容。整个方案得核心在于增量文件得计算和合并,接下来介绍mt支持的2种增量更新算法。

基于chunk的增量更新算法

在mt1.0里面,增量文件的计算和增量文件和旧版本内容的合并主要基于chunk算法,这个算法的原理是通过将js分块并滚动比较取得两个版本内容,获取增量文件。具体得算法设计请看下面这个PDF:

mt1.0js增量更新技术实现

基于编辑距离计算的增量更新算法

mt1.0的chunk算法基于分块计算,增量更新的精确度依赖于chunk的大小,在实际使用中总是会有不少代码需要冗余下载,为此在mt2.0里面增加路基于编辑距离计算的增量更新算法,具体的实现方案的PDF如下:

mt2.0js基于编辑距离计算的增量更新技术实现

mixdiff:基于编辑距离计算,chunk两种算法的增量更新

编辑距离计算可以精确到字符,但是需要用一个矩阵来存储字符,本身会占用很大的内存,基本上比较难于用在生产环境里, 所以我们的mixdiff融合了以上两种算法,提高了算法的性能,并能实现字符级别的增量更新。 mixdif其实就是:对于比较字符串比较短的字符用lcs来计算增量文件,对于比较长的字符串用chunkdiff来找出2个字符串的最大公共子字符串,然后用这个字符串将新旧2个字符串都切成前缀、公共子串、后缀。 然后分别用2个前缀,后缀为参数递归调用mixdiff来实现增量文件计算的方式。流程图如下:

程序流程图如下:

MT的COMBO介绍

作为一个基于AMD规范的模块管理框架,mt还提供灵活的combo支持.mt的combo支持包含一下几种方式:

冷combo

冷combo就是在打包混淆的时候把多个不同的模块打包进同一个js,前台下载的时候直接下载这个js,比如打包配置如下:

    {
                 './release/{pv}/base-{fv}.js': {
                        files: ['./js/init.js','./js/util.js']
                 },
                 './release/{pv}/page/p1-{fv}.js': {
                    files: ['./js/page/p1.js']
                 },
                 './release/{pv}/page/p2-{fv}.js': {
                    files: ['./js/page/p2.js']
                 },
                 './release/{pv}/page/p3-{fv}.js': {
                    files: ['./js/page/p3.js']
                 }
    }

可以看到我们的init,util模块被打到base.js里,达到冷combo的目的

热combo,半热combo

半热combo是相对冷combo来说的,除了走打包实现冷combo以外,我们还支持通过前台配置来实现半热combo或热combo

combo:{
                         //是否启用combo
                        cb:true,
                        //哪些模块的js走半热combo一块下载
                       //,这里数组的每个项是要一起下载的模块
                        conf:['init,util','p1,p2,p3']

}

上面的代码,我们设置了combo的cb为true,说明走combo. conf的配置则设置了哪些模块是要走combo一起下载的, 即使打包脚本没有把他们打在一起。 为了看效果,我们先把cb设为false,conf设置为空数组,表示不走combo:

                          combo:{
                           //是否启用combo
                           cb:flase,
                           //哪些模块的js走半热combo一块下载
                           //,这里数组的每个项是要一起下载的模块
                           conf:[]
                       }

我们看下网络请求:

可以看到base.js,p1.js,p2.js,p3.js是分开下载的,说明没有走combo

然后设置了combo的cb为true,说明走combo. 我们看下网络请求:

可以看到base.js,p1.js是分开下载的,而p2.js,p3.js是一起下载的,这是因为mt2.0自己分析了依赖,把某个模块共同依赖一起下载了,这个例子里面p1依赖了p2,p3两个模块 所以p2,p3被一起下载了,这就是热combo!

这时候我们想,我想让p1,p2,p3一次就下载了,怎么弄?很简单,我们只要设置combo.conf为如下:

combo:{
//是否启用combo
cb:true,
//哪些模块的js走半热combo一块下载
//,这里数组的每个项是要一起下载的模块
conf:['init,util','p1,p2,p3']

}

我们看下网络请求:

ok,p1,p2,p3一次就下载了!!,这就是半热combo,需要配置一下conf.

MT的异常和统计回调

为了方便统计和及时清理本地存储,mt还提供了本地存储异常和统计两种回调。通过设施g_config的storeInc对象的statFunc,storeExFunc两个函数,可以设置统计和本地存储异常回调 , statFunc在请求每个js的时候触发,便于统计每个js的请求情况,storeExFunc在写本地存储异常回调, 将脚本内容写入本地存储出现异常的时候调用,用来提供给业务清理本地存储

storeInc:{
            //统计回调,统计脚本请求情况,jsUrl是js地址,
            //mode是请求模式,full:表示全量请求,
            //inc表示增量请求,local表示从本地存储读取
            'statFunc':function(jsUrl,mode){
                console.log('get '+jsUrl+' from '+mode);
            },
            //写本地存储异常回调,将脚本内容写入本地存储
            //出现异常的时候调用,用来提供给业务清理本地存储
            //,storekey表示写如的key
            'storeExFunc':function(storeKey){
                console.log('set store item '+storeKey+' exception');
            },
            'store': true,
            'inc': true,
            'proxy':true,
            'debug': false
},

快速上手

到这里我们基本上对mt有了一个基本的了解,下面我们通过一个例子来快速上手,并通过这个例子来看看mt做增量更新的效果(本例我们可以在demo目录下的quickstart里找到):

基于AMD的模块定义

mt首先是一个基于amd规范得模块管理框架,所以模块的定义我们实用了最简单的一种方式:


        define('p1', ['p2', 'p3'], function (p2, p3) {
            var o = {
                k: 'v'
            };
            return o;
        });

用define来定义模块,其中第一个参数是模块id,第二个参数是依赖,第三个参数是方法定义,返回值是该模块的定义

mt映射和回调配置

跟其他模块管理框架一样,mt也有自己的模块到文件映射、增量更新配置、版本配置、回调配置等,下面是本例是我一个配置:


var g_config =  {
    jsmap:{
        'init': 'base.js',
        'util': 'base.js',
        'p1': 'page/p1.js',
        'p2': 'page/p2.js',
        'p3': 'page/p3.js'
    },
    storeInc:{
        //统计回调,统计脚本请求情况,jsUrl是js地址,
        //mode是请求模式,full:表示全量请求,
        //inc表示增量请求,local表示从本地存储读取
        'statFunc':function(jsUrl,mode){
            console.log('get '+jsUrl+' from '+mode);
        },
        //写本地存储异常回调,将脚本内容写入本地
        //存储出现异常的时候调用,用来提供给业务
        //清理本地存储,storekey表示写如的key
        'storeExFunc':function(storeKey){
            console.log('set store item '+storeKey+' exception') ;
        },
        'store': true,
        'inc': true,
        'proxy':true,
        'debug': false
    },
    //是否走combo,同时支持conf指定哪几个js是合并下载的

    combo:{cb:true,conf:["init,util","p1,p2,p3"]},
    testEnv: false,
    staticPath: '/release',
    serverDomain: 'http://localhost:6600',
    buildType: 'project',
    ver: '2014053000002'  //版本号

};

在2014053000002版本,我们的p2代码如下:


define('p2', [], function () {
        console.log('p2 ok!');
        document.write('p2 ok!');
        });
}

打包

mt的打包主要是用mt自己的mtbuild.js来做的,功能主要是根据规则压缩混淆合并js,同时生成上个版本的增量文件。我们运行demo/quickstart目录下的build.sh ,其实是执行mtbuild.js命令:

node ../../js/mtbuild.js test.html build.conf  lcs

第三个参数说明走编辑距离计算增量更新算法,你也可以设置成chunk走chunk算法

启动增量服务

mt目前除了mt build生成增量文件以外,还提供了在服务端生成增量文件的server,包括java,nodejs两个版本,这里我们用以下nodejs版本。到js目录下执行命令

 node storeincServer.js lcs ../demo/quickstart 

第2个参数说明走lcs增量更新算法,你也可以设置成chunk走老算法,第三个参数是根目录,这里设置成../demo/quickstart

效果演示

打开chrome(必须支持localstorage),输入地址:http://localhost:6600/test.html ,可以看到请求的是全量的js

本地存储里的内容是2014053000002版本的:

接着我们修改p2.js代码,加上"lcs"这3个字 :

define('p2', [], function () { console.log('p2 ok!'); document.write('p2 ok lcs!'); });

然后重新运行命令

node ../../js/mtbuild.js test.html build.conf lcs

这时候生成2014053000003版本代码,打开chrome(必须支持localstorage), 输入地址:http://localhost:6600/test.html ,这时候可以看到请求的内容是增量的,并且精确到了字符级别:

我们来看下同样是这个修改,如果我们走chunk算法,会是什么样子。 我们需要重新走一遍上边的流程,但是把build.sh命令的lcs参数改成chunk,启动storeincServer时的lcs也改成chunk, 这里就不罗嗦步骤了,我们直接看看走chunk是的网络请求:

相对chunk算法,基于lcs算法的能更加精确

一个完整的webapp demo

通过上一个例子,我们大概了解mt的功能和原理,并对增量更新效果有了一个基本的认识。下面我们再来看一个基于mt做手机单页面webapp的例子,这个例子里面我会用到以下几个东西:

  • 基于jetty的java版本的增量更新服务
  • pm.js:一个基于hashChange的单页面路由
  • mtpl:一个高性能小体积的js模版引擎
  • ratchet:一个针对移动的css框架

本例其实是ratchet自带例子里的movie finder的一个改造,这里简化一下原例,并接入mt实现增量更新。

我们用jetty作为server,把demo下的mtwebapp目录放到jetty的webapps目录下,mtwebapp本身已经包含所有的java类(打包成mt.jar).

servlet配置

java版本的增量更新代理是一个servlet,所以我们需要再web.xml里配置:

    <display-name>StoreIncServlet</display-name>
    <servlet-name>StoreIncServlet</servlet-name>
    <servlet-class>com.storeinc.StoreIncServlet</servlet-class>
    <init-param>
    <param-name>jsPath</param-name>
    <param-value>/Users/waynelu/nginxhtmls/jetty/webapps/mtwebapp/</param-value>
    </init-param>
    <init-param>
    <param-name>chunkSize</param-name>
    <param-value>12</param-value>
    </init-param>
    <init-param>
    <param-name>diffAlg</param-name>
    <param-value>lcs</param-value>
    </init-param>
    </servlet>
    <servlet-mapping>
    <servlet-name>StoreIncServlet</servlet-name>
    <url-pattern>/storeinc/*</url-pattern>
    </servlet-mapping>
     

jsPath:js存放目录

chunkSize:chunk算法的块长度

diffAlg:增量更新算法,可以为chunk或者lcs

运行demo

到jetty/bin地下运行:

    ./jetty.sh start
    

为了看到增量更新效果,我们在mtwebapp里放了index.jsp和index1.jsp两个文件,分别对应2014071600018,2014071500017两个版本的js.在地址栏里输入:http://localhost:8080/mtwebapp/index.jsp 和 http://localhost:8080/mtwebapp/index1.jsp ,我们可以看到这两个版本增量更新的效果

java相关代码在java目录下

谁在使用mt

NBA
nba
</a>
</span>
</td>

<td>
<span>
<a href="http://infoapp.3g.qq.com/g/s?sid=AV-STHlv9Fb_E6Jb0P0_Pdtn&amp;aid=movie&amp;i_f=225">
    <div class="img-tit">爱电影</div>
    <img alt="爱电影" pl="1" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/imovie.png">

</a>
    </span>
    </td>

    <td>
    <span>
<a href="http://gp.3g.qq.com/g/s?sid=AV-STHlv9Fb_E6Jb0P0_Pdtn&amp;aid=ifinance&amp;i_f=271#fund/0">
    <div class="img-tit">爱理财</div>
<img alt="爱理财" pl="1" src="http://3gimg.qq.com/wap30/info/info5/img/ifinance.png">

</a>
   </span>
   </td>

   <td>
   <span>
<a href="http://infoapp.3g.qq.com/g/s?aid=medialib&amp;iarea=98&amp;i_f=237">
    <div class="img-tit">悦读</div>
    <img pl="1" alt="悦读" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/iread.jpg">

</a>
 </a>
  </span>
  </td>

  <td>
  <span>
<a href="http://infoapp.3g.qq.com/g/s?aid=touchauto&amp;iarea=98&amp;i_f=238">
    <div class="img-tit">车典</div>
    <img pl="1" alt="车典" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/autolib.jpg">

</a>
 </span>
 </td>
 </tr>
 <tr>
 <td>
 <span>
<a href="http://infoapp.3g.qq.com/g/s?aid=carshow&amp;iarea=98&amp;i_f=239">
    <div class="img-tit">秀车</div>
    <img pl="1" alt="秀车" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/carshow.jpg">

</a>
 </span>
 </td>

 <td>
 <span>
<a href="http://live.3g.qq.com/g/touch2/?&amp;iarea=98&amp;i_f=240#home">
    <div class="img-tit">爱直播</div>
    <img pl="1" alt="爱直播" src="http://3glogo.gtimg.com/wap30/info/info5/img/app/ilive.jpg">

</a>
</span>
</td>

<td>
<span>
    <a href="http://infoapp.3g.qq.com/g/s?aid=sportpicguess">
        <div class="img-tit">体育猜图</div>
        <img pl="1" alt="体育猜图" width="100px" src="http://3gimg.qq.com/wap30/info/info5/img/caitu.png">
    </a>
   </span>
   </td>

   <td>
   <span>
    <a href="http://infoapp.3g.qq.com/g/s?&aid=common_webapp&webapp=nbaky">
        <div class="img-tit">狂言NBA</div>
        <img width="100px" alt="狂言NBA" src="http://infopic.gtimg.com/info/images/2014/7/140436769816885735.png">

    </a>
</span>
</td>

<td>
<span>
    <a href="http://infoapp.3g.qq.com/g/s?&aid=common_webapp&webapp=jiecao">
        <div class="img-tit">节操新闻</div>
        <img width="100px" alt="狂言NBA" src="http://infopic.gtimg.com/info/images/2014/3/139597306809381261.png">

    </a>
 </span>
 </td>

 </tr>
 </table>

我们的github:https://github.com/mtjs/mt , 如果觉得MT是个靠谱的项目,请给我们star,您的支持是我们最大的动力

Licence

MIT License

mt's People

Contributors

huobazi avatar luyongfugx avatar qbaty avatar zhaoda 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

mt's Issues

现有的diff的建议

  1. 逐字符比较,尤其如果是js的话,会很慢。还是建议依据特殊字符分割后再作diff,当然这样前端的merge方法也得改,可以前端先改,作兼容,不是大问题。比如以';'作为分隔符,假设平均一句代码有10个字符,则问题的规模缩小为原来的1/100。
  2. 现在的diff是依据的最naive的lcs计算算法,时间复杂度是O(M_N)(M,N分别为src与target的序列长度)。Myers的diff算法可能更好,时间复杂度为O((M+N)_D)(D为src与target的距离长度),尤其是在src与target相近时时间复杂度接近O(M+N)。浏览器里边我测试了大概4000多字符的代码(逐字符比较),修改了5句代码,先前的算法花的时间是3000-4000ms,Myers的算法是20-30ms。

https://github.com/nighca/diff-merge/blob/master/lib/myers-compare.js
https://neil.fraser.name/software/diff_match_patch/myers.pdf

关于此库的情况

是否还有维护
是否还能作用, 适用当前场景
如果不适用, 则有无替代方案

mt2.0的规划

开始前先说一下感谢的话:),最近有一些朋友给我发了很多关于mt的改进建议,其中有@nighca @yulongheli 两位同学都给除了增量更新改进算法建议,@nighca同学甚至还写出了实现,@yulongheli则给出了使用过程中的很多建议,在这里特别感谢这两位同学。

下面进入正题,说下我们2.0的要加进来的一些特征:
1.增加本地存储满的异常回调,便于业务自己按自己的规则清理本地存储
2.增加统计回调,方便业务统计有多少js请求走了本地存储,增量更新,全量更新,以评估
带来的流量节省
3.增量更新算法增加基于lcs的diff,merge协议支持
4.增量更新支持combo增量更新,即支持一个请求增量更新几个js
5.更加强大的demo,会用到手腾自有的spa小模块pm:)

暂时就是这些了,欢迎大家提更多的意见:)

现在还有维护?

发现下载后的demo是有问题的,比如编译文件的时候写死了一个需要zepto.min.js的文件,注释掉zepto.min.js后才能顺利编译通。,在试用java的中的代码时候会出现代码计算错位的问题,导致js load回来报错!

压缩代码时变量名替换对增量更新的影响

讨论一下:

我看了打包的代码,会调用Uglify.minify对代码进行合并压缩,在这个过程中UglifyJS会进行变量名替换。

这样的可能结果是,如下代码:

var xxx = 1;
var yyy = 2;
var zzz = 3;

变量名替换后为:

var a = 1,
    b = 2,
    c = 3;

而修改代码为:(只改动了一行代码)

var yyy = 2;
var zzz = 3;

后,其对应的变量名替换后代码生成为:

var a = 2,
    b = 3;

这意味着变量名替换后的代码中所有用到变量yyy、zzz的地方都变成了a、b(老代码中是b、c),即所有用到该变量的块(切片后的chunk)都会因此被认为是新块。即一行代码的改动,因为变量名替换行为,导致了比实际更大的增量计算结果。

考虑这些,若算法不改变,不对最终代码进行变量名替换会不会在客户端反而获得更好的体验。

或者考虑改进算法(增量计算/变量名替换的算法)

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.