Heim > Fragen und Antworten > Hauptteil
AMD的框架比如requireJS和seaJS,项目不想引用。想自己写JS脚本来按需加载,并且处理好依赖关系,目前想到的方法是callback(利用script的onload事件来执行回调)
各位大神有没有其他比较优雅办法来实现按需加载?
黄舟2017-04-11 10:25:52
其实按需加载不是很难,你只需要2个东西就能办到
一个模块加载器
一些符合模块加载器语法的代码
首先实现一个模块加载器其实并不复杂,不到50行就能办到,但是如果你想要一个功能完善的,可配置,具有完善的生态和社区的模块加载器。在几年前是requirejs
和seajs
,现在是browserify
和webpack
。webpack
和browserify
在模块加载方面相比requirejs
和seajs
更高级的地方是在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
一定还少些什么。
一个模块的依赖必须等到运行的时候才知道,如果缺乏相关模块的定义,那就只能报错
无法自动加载需要的模块
没有依赖分析工具
在seajs
和requirejs
只实现了第一点和第二点。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个团队做了这个事情。
FIS3
webpack
但是为啥FIS没流行下来,主要还是当时FIS搞出了这个功能,但是并没多少人知道,大家对FIS的印象还只停留在FIS2那个时代。。不过现在FIS3已经非常吊了。(原谅我的硬广告(。・`ω´・) )
不过webpack
还是火了。
虽然webpack
和fis
都有代码依赖分析的功能,但是webpack
依然和browserify
一样,把所有的模块都打包在一个文件里(也是有方法分开的)。
FIS
是给每一个文件都包裹一个在文章最上面的给的那样
define('xxx', function () {
// source code
})
这样的语法,再手动引入一个mod.js
---简易模块加载器,并再入口的html
里面自动生成一对script
标签来实现模块加载。
关键是这样复杂的功能竟然是分成了3个插件的,还要自己手动装!!!(能火才怪 - - )
不过FIS依然是青龙偃月刀一样的存在,大神拿来配好之后各种牛逼,前后端编译通吃。
巴扎黑2017-04-11 10:25:52
自己实现也不难,主要思路就是有个config配置需要动态加载js库的path,然后在运行时,由你的加载器,动态的创 <script src='js_Path'/>标签,并把把它append到<head/>中。你的加载器能自适应市面上主流的几种规范即可,比如global var,AMD,Commonjs等。