javascript - js文件如何按需加载?
高洛峰
高洛峰 2017-04-11 10:23:52
0
5
666

AMD的框架比如requireJS和seaJS,项目不想引用。想自己写JS脚本来按需加载,并且处理好依赖关系,目前想到的方法是callback(利用script的onload事件来执行回调)

各位大神有没有其他比较优雅办法来实现按需加载?

高洛峰
高洛峰

拥有18年软件开发和IT教学经验。曾任多家上市公司技术总监、架构师、项目经理、高级软件工程师等职务。 网络人气名人讲师,...

reply all(5)
黄舟

其实按需加载不是很难,你只需要2个东西就能办到

  1. 一个模块加载器

  2. 一些符合模块加载器语法的代码

首先实现一个模块加载器其实并不复杂,不到50行就能办到,但是如果你想要一个功能完善的,可配置,具有完善的生态和社区的模块加载器。在几年前是requirejsseajs,现在是browserifywebpackwebpackbrowserify在模块加载方面相比requirejsseajs更高级的地方是在node环境下自动进行依赖分析。

让我们来实现个简单的兼容commonJS规范的模块加载器吧。

(function (global) {
    var factoryMap = {};
    var resMap = {};
    var moduleMaps = {};
    
    window.resMap = resMap;

    // define函数定义
    define = function (id, factory) {
      factoryMap[id] = factory;
    }

    // require函数定义
    require = function (id) {

     var mod = moduleMaps[id] = {
       exports: {}
     }

     var factory = factoryMap[id];
    
     if (!factory) {
        throw 'Cannot find module `' + id + '`';
     }
     
     var ret = factory.apply(mod, [require, mod.exports, mod]);

     if (ret) {
       mod.exports = ret;
     }

     return mod.exports;
    }
  })(this);

对于这样的模块加载器你就得这样来用。

// 模块1 依赖 模块二的内容
  define('module1', function (require, exports, module) {
//    console.log(1);
    var module2 = require('module2');
    var module3 = require('module3');
    var module5 = require('module5');
    var module6 = require('module6');

    console.log('module1 --> module2', module2);
    console.log('module1 --> module3', module3);
    console.log('module1 --> module5', module5);
    console.log('module1 --> module6', module6);

    module.exports = {
      moduleName: 'module1'
    }
  })

  // 模块二 依赖 模块三的内容
  define('module2', function (require,  exports, module) {
    var module6 = require('module6');

    console.log('module2 --> module6', module6);

    module.exports = {
      moduleName: 'module2'
    }
  })

  // 模块三
  define('module3', function (require,  exports, module) {
//    console.log('3');
    var module4 = require('module4');
    var module5 = require('module5');
    var module2 = require('module2');

    console.log('module3 --> module5', module5);
    console.log('module3 --> module2', module2);
    console.log('module3 --> module4', module4);

    module.exports = {
      moduleName: 'module3'
    }
  })

  define('module4', function (require,  exports, module) {
    var module6 = require('module6');

    console.log('module4 --> module6', module6);

    module.exports = {
      moduleName: 'module4'
    }
  });

  define('module5', function (require, exports, module) {
    var module2 = require('module2');
    var module6 = require('module6');

    console.log('module5 --> module2', module2);
    console.log('module5 --> module6', module6);

    module.exports = {
      moduleName: 'module5'
    }
  })

  define('module6', function (require, exports, module) {

    module.exports = {
      moduleName: 'module6'
    }
  })


  require('module1');

大家可以把所有代码放到浏览器里面实验一下。。。

下面说些有关模块加载器的题外话。有兴趣的可以继续往下读。。


如果大家很好奇webpack的产出的话,也会发现webpack产出的bundle的最顶部带有的模块加载器也类似于上面的代码。

但是我们的代码相比browserify,相比requirejs或者seajs一定还少些什么。

  1. 一个模块的依赖必须等到运行的时候才知道,如果缺乏相关模块的定义,那就只能报错

  2. 无法自动加载需要的模块

  3. 没有依赖分析工具

seajsrequirejs只实现了第一点和第二点。seajs会在运行define的callback函数之前从其源代码里面找出所有的require定义,比如

define('module1', function () {
   var $ = require('jquery')
});

在执行define的时候,seajs已经知道了module1依赖jquery,然后就自动按照配置发送个http请求去下载定义为jquery的这个模块。

对于requirejs来说,模块的依赖已经在代码中声明好了,只需要读到模块名称,然后根据配置生成路径去下载即可。

但是无论是requirejs还是seajs,它们都有一个致命弱点 ---- 一个模块的依赖竟然是开发者自己去写!!

在这个飞速发展,各个领域都朝向自动化的时代是远远不够的!

于是,Github上有个人搞了一个browserify。
使用node来跑依赖分析,自动帮你把各个模块的依赖都搞定,从此只需要写require即可。

虽然用browserify确实爽,但是既然用了node来搞js的依赖分析,那还不如直接把所有的依赖分析都弄了算了。。。

于是这个时候,有2个团队做了这个事情。

  1. FIS3

  2. webpack

但是为啥FIS没流行下来,主要还是当时FIS搞出了这个功能,但是并没多少人知道,大家对FIS的印象还只停留在FIS2那个时代。。不过现在FIS3已经非常吊了。(原谅我的硬广告(。・`ω´・) )

不过webpack还是火了。

虽然webpackfis都有代码依赖分析的功能,但是webpack依然和browserify一样,把所有的模块都打包在一个文件里(也是有方法分开的)。

FIS是给每一个文件都包裹一个在文章最上面的给的那样

define('xxx', function () {
    // source code
})

这样的语法,再手动引入一个mod.js ---简易模块加载器,并再入口的html里面自动生成一对script标签来实现模块加载。

关键是这样复杂的功能竟然是分成了3个插件的,还要自己手动装!!!(能火才怪 - - )

不过FIS依然是青龙偃月刀一样的存在,大神拿来配好之后各种牛逼,前后端编译通吃。

巴扎黑

webpack可以实现按需加载

迷茫

可以使用ajax,比如jq里面的$.getScript()

洪涛

其实最好的建议就用现成的工具,如果非想自己写一个,思路参考下script吧,代码简单也清晰

巴扎黑

自己实现也不难,主要思路就是有个config配置需要动态加载js库的path,然后在运行时,由你的加载器,动态的创 <script src='js_Path'/>标签,并把把它append到<head/>中。你的加载器能自适应市面上主流的几种规范即可,比如global var,AMD,Commonjs等。

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template