资讯专栏INFORMATION COLUMN

无工具,js直接在网页上实现模块化

Dionysus_go / 2645人阅读

摘要:大概原理就是异步加载一堆脚本文件,个人项目可以试试,企业项目使用请深思熟虑后再进行尝试。我了解到的方法来自于设计模式,作者是张容铭,因为觉得这个挺好玩的,给大家分享一下。

大概原理就是异步加载一堆脚本文件,个人项目可以试试,企业项目使用请深思熟虑后再进行尝试。
我了解到的方法来自于《javascript设计模式》,作者是张容铭,因为觉得这个挺好玩的,给大家分享一下。

既然要直接在网页上实现模块化,肯定需要异步添加脚本文件,所以需要解决两个麻烦的问题

依赖的模块也有依赖怎么办

如何知道异步添加的js文件加载的状态

//main.js 不用关注细节,快速了解下调用方式即可
m.module(["dom/index"], function (dom) {
    var test = document.getElementById("test")
    console.log(dom(test).attr("id"))
    dom(test).css({
        "background": "#000",
        "color": "#fff"
    })
})
......
//dom/css.js
m.module("dom/css", ["shared/util"], function (util) {
    return function (ele, css, value) {
        if (util.isString(css)) {
            if (util.isNumber(value)) {
                return ele.style.css = value
            }
            return ele.style.css
        } else {
            for (var k in css) {
                ele.style[k] = css[k]
            }
        }
    }
})
......
//shared/util.js
m.module("shared/util", function () {
    return {
        isNumber: function (num) {
            return num === (0 || num) && num.constructor === Number
        },
        isString: function (str) {
            return str === ("" || str) && str.constructor === String
        }
    }
})

下面就开始实现这个暴露出来的module函数

遵守规则

将模块的实现隐藏起来,创建一个闭包

(function(m){
    var m = m()
})(function(){
    window.m = {}
})
工具函数

添加两个工具函数,loadScript和getUrl

//加载脚本
var loadScript = function (src) {
        var _script = document.createElement("script")
        _script.type = "text/javascript"
        _script.async = true
        _script.src = src

        document.getElementsByTagName("head")[0].appendChild(_script)
    },
    //为地址添加一个.js
    getUrl = function (moduleName) {
        return String(moduleName).replace(/.js$/g, "") + ".js"
    }
module函数的实现

通过上面的图片示例可以了解到,module函数包括了创建和调用模块的功能,它拥有三个参数

url 地址

deps 数据类型为数组的依赖模块

callback 该模块的主函数

获取参数

m.module=function(){
    var args = [].slice.call(arguments),
        //取最后一个参数,即callback
        callback = args.pop(),
        //获取依赖,且数据类型为数组
        deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [],
        //地址
        url = args.length ? args.pop() : null
...

这就是完整module函数的实现,初看很复杂,别急,module的关键就在于下面的两个函数(loadModule和setModule),凡是异步原理都是在和大脑作对,习惯就是新世界的大门,不要拘泥于阅读顺序

m.module = function () {
    var args = [].slice.call(arguments),
        callback = args.pop(),
        deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [],
        url = args.length ? args.pop() : null
        
        params = [],//依赖模块序列,主函数(回调函数)的使用的参数
        depsCount = 0,//该模块未加载完毕的依赖数量
        i = 0
        
    if (deps.length) {
        while (i < deps.length) {
            //闭包保存i
            (function (i) {
                //这样每个脚本执行的module都会有一个depsCount
                depsCount++
                //loadModule初始化是不会调用它的回调函数(缓冲器)的(可以先翻到下面看loadModule的实现)
                //但它会把回调函数添加到moduleCache中去,同时加载该依赖的脚本
                loadModule(deps[i], function (mod) {
                    //这里的mod是依赖模块的输出
                    params[i] = mod
                    //等于0的时候就会执行自己的回调
                    depsCount--  
                    if (depsCount === 0) {
                        //将依赖模块的输出添加到callback的参数中,这样主函数就可以直接使用参数进行调用
                        setModule(url, params, callback)
                    }
                })
            })(i)
            i++
        }
    } else {
        //一旦依赖走到底部,也就是一个脚本文件里的模块没有了依赖(可以先看看下面setModule)
        //loadModule初始化添加到moduleCache的回调就会执行,而depsCount就会-1
        setModule(url, [], callback)
    }
}

如果没有依赖的话,会直接执行setModule,该模块如果是被依赖的模块,就会调用loadModule缓存的缓冲器,也就是它的回调函数
可以先看看loadModule和setModule的实现

if(deps.length){
    ...
}else{
    setModule(url, [], callback)
}

添加一个moduleCache变量,用于缓存模块

//闭包内部
var moduleCache = {}
var setModule = function (moduleName, params, callback) {
    var _module, fn
    if (moduleCache[moduleName]) {
        _module = moduleCache[moduleName]
        _module.status = "loaded"
        //export是模块的输出
        _module.exports = callback ? callback.apply(_module, params) : null
        while (fn = _module.onload.shift()) {
            //执行回调,并将自己的模块输出到缓冲器中
            fn(_module.exports)
        }
    } else {
        callback && callback.apply(null, params)
    }
}
//这里参数callback不是主函数,而是保存的缓冲器,详细翻回module的完整函数
var loadModule = function (moduleName, callback) {
    var _module
    //已初始化
    if (moduleCache[moduleName]) {
        _module = moduleCache[moduleName]
        if (_module.status === "loaded") {
            //有就直接从moduleCache缓存中获取
            setTimeout(callback(_module.exports), 4)
        } else {
            _module.onload.push(callback)
        }
    } else {
        //初始化
        moduleCache[moduleName] = {
            //地址,也可以叫模块名称
            moduleName: moduleName,
            status: "loading",
            //该模块return 的输出
            exports: null,
            onload: [callback]
        }
        //添加脚本
        loadScript(getUrl(moduleName))
    }
}

第一次写文章,如果觉得不好理解或者有行文不严谨的地方可以发下评论或者私信我修改
有帮助的话给我个赞哦

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104043.html

相关文章

  • Google推出的爬虫新神器:Pyppeteer,神挡杀神,佛挡杀佛!

    摘要:注意,是叫做,不是。两款浏览器同根同源,它们有着同样的,但配色不同,由蓝红绿黄四种颜色组成,而由不同深度的蓝色构成。另外是基于的新特性实现的,所以它的一些执行也支持异步操作,效率相对于来说也提高了。是否响应信号,一般是命令,默认是。 如果大家对 Python 爬虫有所了解的话,想必你应该听说过 Selenium 这个库,这实际上是一个自动化测试工具,现在已经被广泛用于网络爬虫中来应对 ...

    Fundebug 评论0 收藏0
  • 2017年前端框架、类库、工具大比拼

    摘要:相比于开发人员的数量,目前框架类库和工具的数量似乎更多一些。本文将会讨论目前最为流行的客户端框架类库和工具以及它们之间的基本差异。典型的类库包括字符串处理日期元素事件动画网络请求等功能。所以不需要明确的区分类库框架和工具。 相比于JavaScript开发人员的数量,目前JavaScript框架、类库和工具的数量似乎更多一些。截至2017年5月,GitHub上的快速搜索显示,有超过110...

    Alliot 评论0 收藏0
  • 库 - 收藏集 - 掘金

    摘要:历时一年,官方已经新增章节,我们再次发力,于下雨,飘雪,红包雨,碰撞球,自定义掘金效果展示有没有兴趣继续看下去,直接看下颜值是第一步了。 【福利】成为专业程序员路上用到的各种优秀资料、神器及框架 - 工具资源 - 掘金本文是鄙人工作这几年随手收集整理的一些自认为还不错的资料,成长的道理上需要积累,这么长时间了,是时候放出来分享下了,或许能帮助到你。 欢迎点赞,让更多人看到,让福利普照。...

    qc1iu 评论0 收藏0
  • 一文带你重新认识 Docker 与 Kubernetes

    摘要:公司始于名为的平台即服务供应商。跨多个机器之间协调这些容器需要额外的工具,这称之为容器编排。的核心优势是为应用程序开发人员提供了用于编排无状态容器的强大工具。有无数的文章都在讨论和比较Docker、Kubernetes 以及Mesos。如果你是初学者,那么你可能会认为这三个开源项目正为了称霸容器界而殊死搏斗。虽然这三种技术都使得使用容器部署、管理和伸缩应用成为可能,但实际上它们各自解决了不同...

    baishancloud 评论0 收藏0
  • 从自编码器到生成对抗网络:一文纵览监督学习研究

    摘要:就在最近,这项技术在流行地无监督学习数据集上实现了非常好的结果。虽然这项工作并不针对无监督学习,但是它可以用作无监督学习。利用替代类别的无监督学习视觉表征使用图像不行来创建非常大的替代类。 如今深度学习模型都需要在大规模的监督数据集上训练。这意味着对于每一个数据,都会有一个与之对应的标签。在很流行的 ImageNet 数据集中,其共有一百万张带人工标注的图片,即 1000 类中的每一类都有 ...

    MageekChiu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<