suchen

Heim  >  Fragen und Antworten  >  Hauptteil

javascript - js文件如何按需加载?

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

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

高洛峰高洛峰2773 Tage vor657

Antworte allen(5)Ich werde antworten

  • 黄舟

    黄舟2017-04-11 10:25:52

    其实按需加载不是很难,你只需要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依然是青龙偃月刀一样的存在,大神拿来配好之后各种牛逼,前后端编译通吃。

    Antwort
    0
  • 巴扎黑

    巴扎黑2017-04-11 10:25:52

    webpack可以实现按需加载

    Antwort
    0
  • 迷茫

    迷茫2017-04-11 10:25:52

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

    Antwort
    0
  • PHP中文网

    PHP中文网2017-04-11 10:25:52

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

    Antwort
    0
  • 巴扎黑

    巴扎黑2017-04-11 10:25:52

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

    Antwort
    0
  • StornierenAntwort