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

栏目: CSS · 发布时间: 5年前

内容简介:大概原理就是异步加载一堆脚本文件,个人项目可以试试,企业项目使用请深思熟虑后再进行尝试。我了解到的概念来自于《javascript设计模式》,作者是张容铭,因为觉得这个挺好玩的,给大家分享一下。既然要直接在网页上实现模块化,肯定需要异步添加脚本文件,所以需要解决两个麻烦的

大概原理就是异步加载一堆脚本文件,个人项目可以试试,企业项目使用请深思熟虑后再进行尝试。

我了解到的概念来自于《javascript设计模式》,作者是张容铭,因为觉得这个挺好玩的,给大家分享一下。

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

  1. 依赖的模块也有依赖怎么办
  2. 如何知道异步添加的js文件加载的状态

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

无工具,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) {
            //闭包保存,这样每个脚本执行的module都会有一个depsCount,等于0的时候就会执行自己的回调
            (function (i) {
                //需要加载的依赖数量+1
                depsCount++
                //loadModule初始化是不会调用它的回调函数的(可以先翻到下面看loadModule的实现)
                //但它会把回调函数添加到moduleCache中去,同时加载该依赖啊的脚本
                loadModule(deps[i], function (mod) {
                    //这里的mod是依赖模块的输出
                    params[i] = mod
                    depsCount--
                    //将依赖模块的输出添加到callback的参数中,这样主函数就可以直接使用参数进行调用
                    if (depsCount === 0) {
                        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))
    }
}

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


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Hackers

Hackers

Steven Levy / O'Reilly Media / 2010-5-30 / USD 21.99

This 25th anniversary edition of Steven Levy's classic book traces the exploits of the computer revolution's original hackers -- those brilliant and eccentric nerds from the late 1950s through the ear......一起来看看 《Hackers》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具