js模块化的历史 | 特点 | 简单实现

1.模块化的历史

概括来讲,以下是演变史

最先是将变量,方法定义在全局,我想用啥就定义啥。

其次是将变量,方法封装在一个函数里面,然后对外暴露一个接口的方式提供服务。(此时已经有模块化的雏形了)

最终比如common.js的模块化,你有一个变量,把它挂在module上面,然后把它export出去,其实和函数,对外抛出一个return,其实是相似的一个东西。

其实目前模块化处在的交错的时代,比如引入有import,require,目前还没有一个统一的规范化标准。

2.各模块化的特点

AMD特点:依赖前置

//定义
define(id?,depencies?,factory);
       
define('foo',['utils','bar'],function(utils,bar)) {
    utils.add(1,2);
    return {
        name:'foo'
    }
}

//全局配置
rj.config({ paths : {
    'jquery': 'url'
}});

define('a',function(){
    console.log('a load');
    return {
        run: function(){console.log('a run')}
    }
})

define('b',function(){
    console.log('b load');
    return {
        run: function(){console.log('b run')}
    }
})

require(['a','b'],function(a,b){
    console.log('a,b引入了');
    a.run();
    b.run();
})

CMD特点:就近依赖/按需引入

define('a',function(require,exports,module){
    console.log('a load');
    exports.run = () => {
    	console.log('a run');
    }
})

define('b',function(require,exports,module){
    console.log('b load');
    exports.run = () => {
    	console.log('b run');
    }
})

define('main',function(require,exports,module){
    console.log('main run');
    const a = require('a');
    a.run();
    const b = require('b');
    b.run();
})

seajs.use('main')

commonJs特点:值拷贝,缓存机制

//缓存机制
// a.js
var name = 'morrain'
var age = 18
exports.name = name
exports.getAge = function(){
    return age
}
// b.js
var a = require('a.js')
console.log(a.name) // 'morrain'
a.name = 'rename'
var b = require('a.js')
console.log(b.name) // 'rename'

//值拷贝
//a.js
var name = 'morrain'
var age = 18
exports.name = name
exports.age = age
exports.setAge = function(a){
    age = a
}
// b.js
var a = require('a.js')
console.log(a.age) // 18
a.setAge(19)
console.log(a.age) // 18

3.自写一个requreJs的实例

参照了某位大佬的实现后,自己参照着使用例子又重新写了一遍实现

const defineMap = new Map();

//定义模块,存到一个defineMap里面
window.define = (id,deps,factory) => {
    //简单判断一下几个参数 --此处逻辑待优化
    if(factory){
        defineMap.set(id, {id , deps , factory});
    }else{
        const emptyList = [];
        defineMap.set(id, {id , emptyList, deps});
    }
}


const rjOptions = {paths: {}};
//全局引用依赖方式
const rj = window.rj = {};
rj.config = (options) => {
    for(let key in options){
        rjOptions.paths[key] = options[key];
    }
}

window.require = (deps,callback) => {
    return new Promise((resolve,reject)=>{
        Promise.all(deps.map(dep=>{
            //走 CDN
            if (rjOptions.paths[dep]) return cdnImport(rjOptions.paths[dep]);

            //走 本地
            //各种各样情况,检查是否已获取js文件
            const promise = !isExistUrl(getUrl(dep)) ? loadUrl(getUrl(dep)) : Promise.resolve();
            return promise.then(()=>{
                const {deps , factory} = defineMap.get(dep);
                if(deps.length > 0){
                    return require(deps,factory);
                }else{
                    return factory(null);
                }
            })
        })).then(resolve,reject);
    }).then(factorys => callback(...factorys));
}

//加载文件到html页面
const loadUrl = (url) => {
    return new Promise((resolve,reject)=>{
        const script = document.createElement('script');
        const head = document.getElementsByTagName('head')[0];
        script.url = url;
        script.type = 'javascript';
        script.async = true;
        script.onload = resolve;
        script.onerror = reject;
        head.append(script);
    })
}

//获取依赖的相对路径
const getUrl = (dep) => {
    const p = location.pathname;
    return p.slice(0, p.lastIndexOf('/')) + '/' + dep + '.js';
  }

// From CDN 加载资源
const cdnImport = (url) => {
    return new Promise((resove, reject) => {
      System.import(url).then(resove, reject)
    })
}

// 判断js文件是否已引入
const isExistUrl = (url) => {
    const srciptNodes = Array.prototype.slice.call(document.getElementsByTagName('script'));
    const list = srciptNodes.map(scriptNode=>{
        return scriptNode.baseURI
    })
    return list.indexOf(url) > 0 ? true : false;
}

1.定义一个全局define和一个defineMap用来存放依赖(很简单,不细说了)

const defineMap = new Map();

//定义模块,存到一个defineMap里面
window.define = (id,deps,factory) => {
    //简单判断一下几个参数 --此处逻辑待优化
    if(factory){
        defineMap.set(id, {id , deps , factory});
    }else{
        const emptyList = [];
        defineMap.set(id, {id , emptyList, deps});
    }
}

2.定义一个全局rj和一个rjOptions用来存放全局依赖(很简单,不细说了)

const rjOptions = {paths: {}};
//全局引用依赖方式
const rj = window.rj = {};
rj.config = (options) => {
    for(let key in options){
        rjOptions.paths[key] = options[key];
    }
}

3.定义一个全局require用来引入依赖

window.require = (deps,callback) => {
    return new Promise((resolve,reject)=>{
        Promise.all(deps.map(dep=>{
            //走 CDN
            if (rjOptions.paths[dep]) return cdnImport(rjOptions.paths[dep]);

            //走 本地
            //各种各样情况,检查是否已获取js文件
            const promise = !isExistUrl(getUrl(dep)) ? loadUrl(getUrl(dep)) : Promise.resolve();
            return promise.then(()=>{
                const {deps , factory} = defineMap.get(dep);
                if(deps.length > 0){
                    return require(deps,factory);
                }else{
                    return factory(null);
                }
            })
        })).then(resolve,reject);
    }).then(factorys => callback(...factorys));
}

4.其中第一个参数deps是一个依赖数组,第二个参数callback是回调函数

5.第一步new一个Promise,需要等promise状态完成后,才执行回调函数

return new Promise((resolve,reject)=>{

6.因为依赖是异步加载的,所以需要用一个Promise.all包裹起来

Promise.all(deps.map(dep=>{

7.判断依赖是属于外部引入,还是本地自定义,分情况走CDN和本地

            //走 CDN
            if (rjOptions.paths[dep]) return cdnImport(rjOptions.paths[dep]);

            //走 本地
            //各种各样情况,检查是否已获取js文件
            const promise = !isExistUrl(getUrl(dep)) ? loadUrl(getUrl(dep)) : Promise.resolve();
            return promise.then(()=>{
                const {deps , factory} = defineMap.get(dep);
                if(deps.length > 0){
                    return require(deps,factory);
                }else{
                    return factory(null);
                }
            })

8.本地依赖需要判断js文件是否引入

isExistUrl(getUrl(dep))

9.继续执行promise任务,如果还有依赖,怎么重复进行require,否则返回factory的结果

                const {deps , factory} = defineMap.get(dep);
                if(deps.length > 0){
                    return require(deps,factory);
                }else{
                    return factory(null);
                }

10.Promise.all将factory返回结果集收集起来,作为参数,执行callback回调

.then(factorys => callback(...factorys))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值