自研模块加载器(四) 模块资源定位-异步加载

资源定位-动态加载

通过resolve方法进行异步解析,完整解析如下图所示:

根据上篇文章startUp.js代码,我们继续完善本章动态加载资源的代码。

(function(global) {
    var startUp = global.startUp = {
        version: ‘1.0.1‘
    }

    var data = {}; // 获取当前模块加载器配置信息
    var cache = {}; // 缓存

    //模块的生命周期
    var status = {
        FETCHED: 1,
        SAVED: 2,
        LOADING: 3,
        LOADED: 4,
        EXECUTING: 5,
        EXECUTED: 6
    }

    // 静态工具类,判断是否为数组
    var isArray = function(obj) {
        return toString.call(obj) === "[object Array]";
    }

    //检测别名
    function parseAlias(id) {
        var alias = data.alias; // data为配置项
        return alias && toString(alias[id]) ? alias[id] : id;
    }

    // 不能以"/" ":"开头 结尾必须是一个"/" 后面跟随任意一个字符
    var PATHS_RE = /^([^\/:]+)(\/.+)$/;

    // 检测是否存在短路径
    function parsePaths(id) {
        var paths = data.paths;
        if(paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) {
            id = paths[m[1]] + m[2]
        }
        return id;
    }

    function normalize(path) {
        var last = path.length - 1;
        var lastC = path.charAt(last);
        return (lastC === ‘/‘ || path.substring(last - 2) === ‘.js‘) ? path : path + ‘.js‘;

    }

    // 添加根目录
    function addBase(id ,uri) {
        var result;
        // 相对路径处理
        if(id.charAt(0) === ‘.‘) {
            result = realPath((uri ? uri.match(/[^?]*\//)[0] : data.cwd) + id);
        } else {
            result = data.cwd + id;
        }
        return result;
    }

    var DOT_RE = /\/.\//g; // /a/b/.c/./d => /a/b/c/d
    var DOUBBle_DOT_RE = /\/[^/]+\/\.\.\//;  // /a/b/c../../d => a/d

    // 规范路径
    function realPath(path) {
        console.log(path)
        path = path.replace(DOT_RE, ‘/‘);
        while(path.match(DOUBBle_DOT_RE)) {
            path = path.replace(DOUBBle_DOT_RE, ‘/‘)
        }
        return path;
    }

    // 入口函数
    startUp.use = function(list, callback){
        // 检测有没有预加载模块
        Module.preload(function() {
            Module.use(list, callback, data.cwd + "_use" +cid()) // 虚拟根目录
        })
    }

    // 生成绝对路径  child / parent
    startUp.resolve = function(child, parent) {
        if(!child) return "";
        child = parseAlias(child); // 检测是否有别名
        child = parsePaths(child); // 检测是否有路径别名,依赖模块中引入包的模块路径 require("app/c")
        child = normalize(child); // 检测是否有后缀
        return addBase(child, parent); // 添加根目录
    }

    startUp.request = function(url, callback) {
        var node = document.createElement(‘script‘);
        node.src = url;
        document.body.appendChild(node);
        node.onload = function() {
            // 加载完成后清空页面上动态增加的标签
            node.onload = null;
            document.body.removeChild(node);
            callback();
        }
    }

    // 构造函数
    function Module(uri, deps) {
        this.uri = uri;
        this.deps = deps || []; // [‘a.js‘, ‘b.js‘] 依赖项
        this.exports = null;
        this.status = 0;
        this._waitings = {};
        this._remain = 0; // 依赖性数量
    }

    // 分析主干(左子树,右子树)上的依赖
    Module.prototype.load = function (){
        var m = this;  // 主干上的实例对象
        m.status = status.LOADING; // 设置构造函数的状态为 LOADING => 3 正在加载模块项
        var uris = m.resolve();  // 获取主干上依赖
        var len  = m._remain = uris.length;

        // 加载主干上的依赖
        var seed;
        for(var i = 0; i<len; i++) {
            seed = Module.get(uris[i]); //创建缓存信息
            if(seed.status < status.LOADED) {  // LOADED == 4 准备加载执行当前模块
                seed._waitings[m.uri] = seed._waitings[m.uri] || 1

            } else {
                seed._remain--;
            }
        }

        // 依赖加载完 m._remain == 0
        if(m._remain == 0) {
            // 获取模块的接口对象
            // m.onload()
        }

        // 准备执行根目录下的依赖列表中的模块
        var requestCache = {};
        for(var i = 0; i < len; i++) {
            seed = Module.get(uris[i]); // 获取cache上的缓存
            if(seed.status < status.FETCHED) {
                seed.fetch(requestCache)
            }
        }

        for(uri in requestCache) {
            requestCache[uri]();
        }

    }

    // 拉取依赖列表中的模块 a.js ,b.js
    Module.prototype.fetch = function(requestCache) {
        var m = this;
        m.status = status.FETCHED;
        var uri = m.uri; // a.js绝对路径和b.js的绝对路径
        requestCache[uri] = sendRequest; // 动态创建script 标签

        function sendRequest() {
            startUp.request(uri, onRequest); // 动态加载script
        }

        function onRequest() {  // 事件函数
            // if(anonymousMeta){  // 模块的数据更新
            //     m.save(uri, anonymousMeta)
            // }
            m.load(); //递归 模块加载策略 deps
        }
    }

    // 资源定位,获取资源的地址
    Module.prototype.resolve = function(){
        var mod = this; //
        var ids = mod.deps; // [‘a.js‘, ‘b.js‘]
        var uris = [];
        for(var i = 0; i<ids.length; i++) {
            uris[i] = startUp.resolve(ids[i], mod.uri); //依赖项 子树/主干
        }
        console.log(uris)
        return uris;
    }

    /*
     *    模块的静态属性和方法
    */

    // 模块定义
    Module.define = function (factory) {

    }

    // 检测缓存对象是否有当前模块信息
    Module.get = function (uri, deps) {
        return cache[uri] || (cache[uri] = new Module(uri, deps));
    }

    Module.use = function (deps, callback, uri) {
        var m  = Module.get(uri, isArray(deps) ? deps : [deps]);

        console.log(m)
        // 模块加载完成执行回调
        m.callback = function() {

        }
        m.load();
    }

    var _cid = 0;

    function cid() {
        return _cid++;
    }

    // 初始化预加载资源
    data.preload = [];

    //     获取当前文档的URL
    data.cwd = document.URL.match(/[^?]*\//)[0];

    Module.preload = function(callback) {
        var length = data.preload.length;
        if(!length) callback();
    }

    global.define = Module.define;

})(this)

新增c.js 用于测试c.js有没有加载成功

console.log("c.js");

修改index.html,引入c.js

<!DOCTYPE html>
<html>
<head>
    <title>自研模块加载器</title>
</head>
<body>

    <script src="./startUp.js"></script>
    <script>
        startUp.use([‘./a.js‘, ‘b.js‘, ‘c.js‘], function() {
            console.log(‘startUp...‘)
        })
    </script>
</body>
</html>

最终element如图所示,清空了动态加载的script标签

控制台成功输出了c.js

动态加载js成功实现了。

原文地址:https://www.cnblogs.com/zzd0916/p/12240871.html

时间: 2024-11-08 06:46:48

自研模块加载器(四) 模块资源定位-异步加载的相关文章

js模块化/js模块加载器/js模块打包器

之前对这几个概念一直记得很模糊,也无法用自己的语言表达出来,今天看了大神的文章,尝试根据自己的理解总结一下,算是一篇读后感. 大神的文章:http://www.css88.com/archives/7628(大神的文章写的很详细,建议先看完大神的文章) 一.js模块化 什么是js模块化,我们从历史说起. 1.一开始我们怎么写脚本?就是在html文件中用<script></script>写代码 这种方式的缺点:代码复用靠复制,基本是全局变量. 2.后来我们用js文件写代码,用<

【Cocos2d-Js基础教学(5)资源打包工具的使用及资源的异步加载处理】

[转载]http://www.cnblogs.com/zisou/p/cocos2dx-js5.html   TexturePacker是纹理资源打包工具,支持Cocos2dx的游戏资源打包. 如果用过的同学可以直接看下面的资源的异步加载处理 首先为什么用TexturePacker? 1,节省图片资源实际大小 2,减少我们游戏中大量资源带来的内存负荷 3,方便游戏对纹理资源的内存管理 游戏开发到后期,我们或许都会遇到的瓶颈就是需要对游戏包进行"瘦身"和内存优化.那么使用TextureP

自研模块加载器(一) 模块系统概述与自定义模块规范书写规定

模块系统概述 CommonJs/AMD/CMD/ES6 Modules 什么是模块化? 模块化就是把系统分离成独立的功能的方法,需要什么功能,就加载什么功能 当一个系统越来越复杂时候,我们会遇到这些问题 1. 命名冲突 2. 文件依赖 使用模块化开发可以避免以上问题,并提升开发效率 1. 可维护性 2. 可复用性 在生产角度,模块化是一种生产方式,这种生产方式效率高,维护成本低. 模块化开发演变 1. 全局函数 早期开发中,将重复的代码封装成函数,将多个函数放在一个文件中. 缺点: 污染全局变量

图片高效加载(二) 图片的异步加载

图片的异步加载是利用AsynTask类对图像进行后台加载完成后再给ImageView,先转载一篇前人的较好的总结后面再添加一些自己的见解和贴上完整的实现demo. 前面的转自:https://my.oschina.net/rengwuxian/blog/183802 摘要: 有没有过这种体验:你在Android手机上打开了一个带有含图片的ListView的页面,用手猛地一划,就见那ListView嘎嘎地卡,仿佛每一个新的Item都是顶着阻力蹦出来的一样?看完这篇文章,你将学会怎样避免这种情况的发

自研模块加载器(二) 加载器结构与设计导论

结构导论 模块部分 数据初始化: 加载器中设计来一个名为Module的构造函数,每个模块都是此构造函数实例对象.构造函数中给实例对象扩展了"未来"所需用到的属性和方法. 模块存储: 加载器中设计来一个名为cache的缓存对象,每个文件(模块)都会存储在cache对象中. 具体存储方式: { "当前模块的绝对路径" : new Module()}, 注意: 当前模块的绝对路径是通过资源部分,资源定位方法实现的. 每个模块创建都先初始化数据,存储在缓存对象中. 资源部分

Sea.js 是一个模块加载器

1 模块定义define define(function(require,exports,module){ //require 引入需要的模块如jquery等 //var $ = require('./jquery'); //exports可以把方法或属性暴露给外部 exports.name = 'hi'; exports.hi = function(){ alert('hello'); } //module提供了模块信息 }); 2 使用定义好的模块seajs.use <!doctype ht

实现简单的 JS 模块加载器

实现简单的 JS 模块加载器 按需加载是前端性能优化的一个重要手段,按需加载的本质是从远程服务器加载一段JS代码(这里主要讨论JS,CSS或者其他资源大同小异),该JS代码就是一个模块的定义,如果您之前有去思考过按需加载的原理,那你可能已经知道按需加载需要依赖一个模块加载器.它可以加载所有的静态资源文件,比如: JS 脚本 CSS? 脚本 图片 资源 如果你了解 webpack,那您可以发现在 webpack 内部,它实现了一个模块加载器.模块加载器本身需要遵循一个规范,当然您可以自定义规范,大

seajs2.3学习日志 简单尝试模板+数据合并、模块异步加载、非标准CMD模式定义define模块

今天继续尝试seajs 2.3的版本,那做点什么demo好呢,就来一个简单是数据模板吧,然后通过其他一些细节深入学习 先看看目录结构,按照官方demo架设 index.html只是简单入口文件和seajs的配置项,最下面有一个seajs.use加载crontroller模块,然后回调暴露的combine方法 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>

AMD加载器实现笔记(二)

AMD加载器实现笔记(一)中,我们实现了一个简易的模块加载器.但到目前为止这个加载器还并不能称为AMD加载器,原因很简单,我们还不支持AMD规范中的config配置.这篇文章中我们来添加对config的中baseUrl和packages的支持.API设计如下: 1 require.config({ 2 baseUrl: "./", 3 packages: [{ 4 name: "more", 5 location: "./more" 6 }, {