做有态度的前端团队

网易FEG前端团队

浅谈NIE组件加载方式的变化和原理

这里主要会说说小组组件新旧加载方式的实现方式、优缺点,以及未来展望


旧的方式

一、调用方式:

    nie.use(["nie.util.share","nie.util.video"],function(){
        //do something
    });

典型的AMD加载方式,就是按需加载,异步回调,代表作:requirejs

二、实现原理:

    //以下为简单实现原理,并非代码全部
    nie.use = function(modules,callback){
        //定义异步请求组件js成功数
        var flags = 0;
        //循环遍历组件名字,发起请求
        for(var i=0;i<modules.length;i++){
            //getScript为请求一个js文件,成功后回调,具体代码不罗列了
            getScript("http://res.nie.netease.com/comm/js/"+modules[i]+".js",function(){
                //每次请求后,成功数+1
                flags++;
                //判断成功总数大于等于组件数,调用成功回调
                if(flags >= modules.length)callback();
            });
        }
    }

从代码实现不难看出,组件数越多,同时发起的请求数就越多(用一下requirejs就知道了),但这不符合web优化的原则,所以加载方式上做了一些调整来解决这个问题

三、调整方式:

1、利用md5加密,把一次需要加载的组件的名字加密,然后请求md5文件名的脚本

    nie.use  = function(modules,callback){
        
        var link = md5(modules.join(""));
        getScript("http://res.nie.netease.com/comm/cache/"+link+".js",callback);
    }

可能会奇怪?那这个加密了的md5文件的js文件哪来的?

2、服务器生成对应的md5文件

假设你需要请求"util.share"和"util.video"两个组件,加密后是:abcdejjdkjj(随便敲的),那么需要在服务器执行一段脚本

  • 读取util.share.jsutil.video.js两个文件,合并成为一个文件
  • 将文件名join一下,md5加密,把加密后的字符串作为合并后的文件名保存
  • 合并后文件存在cache文件夹下

四、优缺点:

AMD的方式在于按需要加载,利用上面的方式解决多次请求的问题。
如果你想加载某个组合的组件,那么需要先在服务器跑一下脚本生成这个文件出来,想更新某个组件的时候,需要把对应的相关的组合文件重新生成一遍,这样不太方便提供给随心所欲的调用者,异步调用,偶尔会出现以下的问题

    //函数A和B,都需要使用video组件,习惯性的就会这样写了
    function A(){
        nie.use(["nie.video"],function(){
            //do something
        });
    }
    function B(){
        nie.use(["nie.video"],function(){
            //do something
        });
    }
    A();
    B();

新的方式

一、调用方式:

    nie.define(function(){
        var share = nie.require("nie.util.share");
        var video = nie.require("nie.util.video");
        //do something
    });

典型的CMD加载方式,依赖前置,就是你引用组件的时候,就需要在这之前就加载好了,代表作:seajs

二、实现原理:

    //简单实现,并非全部
    nie.define = function(callback){
        //定义异步请求组件js成功数
        var flags = 0;
        //提取出nie.require的内容,callback.toString实际就是函数内容转为字符串
        var requireList = callback.toString().match(/nie\.require\([^\)]+\)/g);
        //抽出组件明
        for(var i=0;i<requireList.length;i++){
            requireList[i] = requireList[i].match(/\(([^\)]+)\)/i)[1].replace(/'|"/g,"");
        }
        //循环遍历组件名字,发起请求
        for(var i=0;i<requireList.length;i++){
            //getScript为请求一个js文件,成功后回调,具体代码不罗列了
            getScript("http://res.nie.netease.com/comm/js/"+requireList[i]+".js",function(){
                //每次请求后,成功数+1
                flags++;
                //判断成功总数大于等于组件数,调用成功回调
                if(flags >= requireList.length)callback();
            });
        }
    }

跟上面的旧方式一样,都会出现同一个问题,会同时发起很多个请求,所以,做了以下几件事优化

三、调整方式:

1、推动SA那边做CDNCombo事情

让CDN支持线上文件合并,比如请求??nie.util.share.js,nie.util.video.js,CDN自动合并文件然后返回

2、请求方式改动

    //简单实现,并非全部
    nie.define = function(callback){
        //定义异步请求组件js成功数
        var flags = 0;
        //提取出nie.require的内容,callback.toString实际就是函数内容转为字符串
        var requireList = callback.toString().match(/nie\.require\([^\)]+\)/g);
        //抽出组件明
        for(var i=0;i<requireList.length;i++){
            requireList[i] = requireList[i].match(/\(([^\)]+)\)/i)[1].replace(/'|"/g,"");
        }
        //拼接URL
        var link = "http://res.nie.netase.com/comm/js/??" + requireList.join(",");
        getScript(link,function(){
            callback();
        });
    }

四、优缺点:

CMD方式依赖前置,写代码的时候就会感觉是同步执行,不用担忧异步请求和回调的问题,上面还可以添加去重、以及已经求求过的不请求等功能,减少不必要的请求;推动CDNCombo后,不再需要用服务器跑脚本,调用者也能随意组合组件请求。
这种方式就需要先请求完组件后再执行主体代码逻辑,执行时机会对比AMD来说慢,开发时候会容易规避AMD的一些写法问题。

五、展望与未来:

目前的组件加载方式,实现了类似seajs的功能,模块加载和组件加载,之后会把jquery也拆出来,只保留核心库,其它都按需要加载,不再全部打包成一个文件

PS:组件加载方式源码链接->GitLab

手机阅读请扫描下方二维码:

none
上一篇:WEUI实践   下一篇:图片懒加载