博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
avalonJS-源码阅读(前)
阅读量:6671 次
发布时间:2019-06-25

本文共 4898 字,大约阅读时间需要 16 分钟。

hot3.png

avalon模块加载

avalon自己实现了一套可被替换的模块加载系统(AMD loader)。具体什么是AMD loader可参看doJo官方博客关于AMD loader的,看完之后,再继续往下看,会比较清楚些。

模块加载配置

模块加载系统可替换原理参见,可替换的前提是前面加载的amd loader文件将definerequire函数暴露给window对象。具体如何替换可执行

avalon.config.plugins.loader(false)//不推荐,但有效//或者avalon.config({loader:false})

当然了,如果更改了amd loader的话,可不要用avalon.config.plugins.text|css(url)了。

avalon.config.nocache可用来配置是否去除缓存,测试的时候
avalon可通过配置avalon.config.[shim|paths],来加载一些不符合amd加载规则的数据源,例如加载csdn上的jquery等。
paths意味着地址。这里需要注意的是,paths的设定时,需要:,也就是全路径,不要像这样www.abc.com,而是http:www.abc.com
shim则是一个object。

//shim{//参考下面的数据结构    $moduleId:{        src:$url        exports:$fn|$string // string说明加载的js文件会在window下存放引用,例如jquery, "$"==>window["$"]        deps:...    }}

模块加载数据结构

模块加载所用到的数据先放出来,方便源码阅读推断。

//avalon.modules 存储模块信息{    $moduleId:{//默认值        id:$string//随机生成或指定        exports:$object|$string,//暴露出引用        deps:$array|$object,//依赖的部件 为$object时,会添加当前部件的加载状态,依赖的deps 又都会在avalon.modules下有各自的状态记录        state:$number//加载状态 2:加载过 1:正在加载        factory:$fn//模块本身,会被存放到factorys中,他的this指代window对象        args:$array//保存依赖模块的返回值    }}//factorys数组下的factory函数factory.delay=$fn//检查依赖,延迟加载factory.id=$string//用来debug用的

require实现

require(deps, factory, baseUrl)

require 默认id的生成是通过"callback"+setTimeout("1")来实现UniId。
require 函数会先调用loadSource遍历deps是否都加载完,没加载完的,会根据加载文件类型调用不同的函数去完成异步加载,并将其要加载的依赖放入loadings存储,通过各种合适的时机(例如,完成一个js模块的加载时)调用checkDeps来将loadings处理掉,更新依赖状态,进而加载自己。
这里需要注意的是,factory的this,指代window对象(不知为何)。

动态加载

动态加载划分为三种:js、css、text(文本),分别对应一个函数。并对url #?后缀进行删除。

加载css时,前面添加 css! 。

css加载

css分两种加载,先介绍一个简单的,复杂的则是通过AMD loader加载的。加载方式是构造<link href='...' rel='stylesheet' id='$sepcialURL'>并插入到head最上边。这个地方有个疑问是,css样式覆盖会按照后来覆盖前面的样式来吗?有待测试。

text加载

text的加载是通过ajax做的,并将结果赋值给exports,保存在avalon.modules下面。

js加载

js加载会有对前面提到的avalon.config.shim进行处理(说实在的这个处理只有看完源码后方能使用无误。纠结~),具体的加载功能由loadJS函数来完成。

loadJS通过创建<script class='$日期'>节点并插入到head上来加载js文件,当加载完成后,会将class属性改写掉,并将define函数定义的factory函数和回调函数执行一下。如果加载失败了,写个日志呗。

define实现

define(id, deps, factory)

define的实现会牵扯到用户传参的循环依赖,例如:加载a需要先加载b,加载b又需要先加载a。所以上面的数据结构$moduleIddeps就变的十分有用了。根据这个参数的关系,就可找到是否存在循环依赖。
define还会调用require函数来实现JS文件的加载。
define还是require函数的属性值哦。innerRequire.define=function(){...}

ready!

ready!是avalon内置模块,主要用来等待游览器扫完dom树之后,再执行。avalon.ready也就是基于此的,上下avalon.ready源码。

avalon.ready = function(fn) {    innerRequire("ready!", fn)//  require('ready!',fn);使用innerRequire是预防avalon的AMD loader被替代掉}

函数介绍

checkDeps

checkDeps是用来检查存放在loadings模块的依赖是否都完成加载,如果依赖都完成了加载,而自己没有,则去执行自己的factory工厂函数。这个函数用到了loop来跳过双for循环break问题,可以拿来借鉴下。

function checkDeps() {    //检测此JS模块的依赖是否都已安装完毕,是则安装自身    //只要loadings有一个    loop: for (var i = loadings.length, id; id = loadings[--i];) {        var obj = modules[id],            deps = obj.deps        for (var key in deps) {            if (ohasOwn.call(deps, key) && modules[key].state !== 2) {                continue loop            }        }        //如果deps是空对象或者其依赖的模块的状态都是2        if (obj.state !== 2) {            loadings.splice(i, 1) //必须先移除再安装,防止在IE下DOM树建完后手动刷新页面,会多次执行它            fireFactory(obj.id, obj.args, obj.factory)            checkDeps() //如果成功,则再执行一次,以防有些模块就差本模块没有安装好        }    }}

获取当前scriptURL

源码很棒且注释很全,直接上源码。作者司徒正美还在博文中进行了详细的讲解,有兴趣的可以去了解下。

//getCurrentScript(true);function getCurrentScript(base) {    // 参考 https://github.com/samyk/jiagra/blob/master/jiagra.js    var stack    try {        a.b.c() //强制报错,以便捕获e.stack    } catch (e) { //safari的错误对象只有line,sourceId,sourceURL        stack = e.stack        if (!stack && window.opera) {            //opera 9没有e.stack,但有e.Backtrace,但不能直接取得,需要对e对象转字符串进行抽取            stack = (String(e).match(/of linked script \S+/g) || []).join(" ")        }    }    if (stack) {        /**e.stack最后一行在所有支持的浏览器大致如下:         *chrome23:         * at http://113.93.50.63/data.js:4:1         *firefox17:         *@http://113.93.50.63/query.js:4         *opera12:http://www.oldapps.com/opera.php?system=Windows_XP         *@http://113.93.50.63/data.js:4         *IE10:         *  at Global code (http://113.93.50.63/data.js:4:1)         *  //firefox4+ 可以用document.currentScript         */        stack = stack.split(/[@ ]/g).pop() //取得最后一行,最后一个空格或@之后的部分        stack = stack[0] === "(" ? stack.slice(1, -1) : stack.replace(/\s/, "") //去掉换行符        return stack.replace(/(:\d+)?:\d+$/i, "") //去掉行号与或许存在的出错字符起始位置    }    var nodes = (base ? DOC : head).getElementsByTagName("script") //只在head标签中寻找    for (var i = nodes.length, node; node = nodes[--i]; ) {        if ((base || node.className === subscribers) && node.readyState === "interactive") {//subscribers="$"+(new Date-0)            return node.className = node.src        }    }}

一个有趣的加载测试

这个测试时关于加载顺序的。

Hello, {
{name}}

请问avalon什么时候将model刷进页面去的,为什么?和下面的代码有什么区别?

Hello, {
{name}}

做完这个练习,我相信你一定会对dom加载有个新的认识。

小结

整个实现,为factory函数依赖注入废了不少劲,不知道添上angularJS的annotate函数,会不会简单些。

转载于:https://my.oschina.net/myprogworld/blog/229414

你可能感兴趣的文章
虚拟主播上线:多模态将改变人机交互的未来
查看>>
Hyperledger Grid:一个用于分布式供应链解决方案的框架
查看>>
.NET或将引入类型类和扩展
查看>>
区块链现状:从谨慎和批判性思维看待它(第二部分)
查看>>
GitHub 重磅更新:无限私有仓库免费使用
查看>>
AlphaZero进化论:从零开始,制霸所有棋类游戏
查看>>
Swift 5进入发布倒计时
查看>>
Git 2.18版本已支持Git协议v2
查看>>
跨平台开发框架的大旗,究竟谁能扛起来?
查看>>
服务应该去版本化,不管是微服务还是SOA
查看>>
Hazelcast发布开源流处理引擎Jet
查看>>
最新版Scrum指南已发布
查看>>
2016年前端盘点合集
查看>>
React 16 Jest ES6级模拟 - 深入:了解模拟构造函数
查看>>
TextView中DrawableXXX图片无法设置大小的解决方案
查看>>
我的网站搭建: (第四天) 导航栏与页脚
查看>>
往"某度文库"上传资源之前,请先做好这些...
查看>>
mysql常用命令和脚本
查看>>
中国外交官有AI当参谋了!不过最后拍板的还是人类
查看>>
Spring Cloud Security系列教程一:入门
查看>>