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


    资源定位-动态加载

    通过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成功实现了。

  • 相关阅读:
    Zookeeper安装部署
    dubbo/dubbox部署资料收集
    Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
    ios开发--清理缓存
    从零开始,让你的框架支持CocoaPods
    iOS 开发-- Runtime 1小时入门教程
    iOS 开发--github的demo
    linux 防火墙iptables简明教程
    利用BBRSACryptor实现iOS端的RSA加解密
    iOS开发--数组
  • 原文地址:https://www.cnblogs.com/zzd0916/p/12240871.html
Copyright © 2020-2023  润新知