简体   繁体   English

实现与 node.js 兼容的客户端“要求”function

[英]Implementation of a client-side 'require' function compatible with node.js

Having decided to implement all of my Javascript libraries as modules compatible with node.js, I've now put together a simple 'require' function to be used on the client side of things.决定将我所有的 Javascript 库实现为与 node.js 兼容的模块后,我现在已经将一个简单的“要求”function 放在一起,以便在事物的客户端使用。 I do realize that there are already plenty of very good implementations out there of this, but most seem fairly heavy-handed so I'm just opting to go with the "roll-your-own" approach.我确实意识到已经有很多非常好的实现,但大多数看起来相当笨拙,所以我只是选择 go 与“自己动手”的方法。

Here's my 'require.js' file:这是我的“require.js”文件:

/*
    A client-side script loading class designed to be compatible with node.js
*/

function require(filename)
{
    try
    {
        var
            script = null,
            ajax = new XMLHttpRequest(),        
            index = filename.toLowerCase().indexOf('.js');
        if(index <= 0 || index != filename.length - 3)
            filename += '.js';
        ajax.onload = function() 
        { 
            script = this.responseText;
        };  
        ajax.open('GET', filename, false);
        ajax.send();
        return _isolate_script_(script);
    }
    catch(error)
    {
        return null;
    }
}   

function _isolate_script_(_script_)
{   
    return (new Function
    (
        'var exports = {}, module = {exports : null}; ' 
        + _script_ + 
        '; return module.exports || exports;'
    )).call();
}

Example module ('example.js'):示例模块('example.js'):

/*
    Example of a multiple-class export file
*/

exports.metallic = function(value)
{
    return (Math.sqrt(4 + value * value) + value) / 2;
}

exports.inverse_metallic = function(value)
{
    return exports.metallic(-value);
}

Example of a module consumer ('main.js'):模块使用者('main.js')的示例:

function main()
{
    var
        example = require('example');
    if(example != null)
    {   
        var
            value = Math.floor(Math.random() * 100) + 1;
        alert
        (
            'example.metallic(' + value + ') = ' + example.metallic(value) 
            + '\n' + 
            'example.inverse_metallic(' + value + ') = ' + example.inverse_metallic(value)
        );
    }
    else
        alert('Error: cannot load "example"');
}

Finally, a basic HTML file that invokes our page logic:最后,调用我们的页面逻辑的基本 HTML 文件:

<!DOCTYPE html>
<html>
    <head>
        <script src = 'require.js'></script>
        <script src = 'main.js'></script>
    </head>
    <body onload = "main()"></body>
</html>

So my specific question is simply whether or not I've implemented everything correctly, and moreover if the framework and use-cases seem kosher enough?所以我的具体问题只是我是否正确地实现了所有东西,而且框架和用例是否看起来足够干净?

It seems that you are trying to reimplement Browserify : 看来您正在尝试重新实现Browserify

Browsers don't have the require method defined, but Node.js does. 浏览器没有定义require方法,但是Node.js有。 With Browserify you can write code that uses require in the same way that you would use it in Node. 使用Browserify可以编写使用require的代码,就像在Node中使用它一样。

If you want to implement a similar thing yourself then take a look at the Browserify source code. 如果您想自己实现类似的功能,请查看Browserify源代码。

See: https://github.com/substack/node-browserify 参见: https : //github.com/substack/node-browserify

I'm just going to answer this myself. 我将自己回答。

The main issue is that older browsers sometimes freeze-up with synchronous ajax requests. 主要问题是,较旧的浏览器有时会因同步ajax请求而冻结。 Simply launching the entire 'main' function in it's own thread seems to be a good solution for that. 简单地在自己的线程中启动整个“ main”功能似乎是一个很好的解决方案。 That is, if some unforeseen issue arises with loading scripts then the page itself may not be responsive, but the browser should at least be unaffected. 也就是说,如果在加载脚本时出现一些不可预见的问题,则页面本身可能没有响应,但是浏览器至少应不受影响。 In other words, something along the lines of this: 换句话说,与此类似:

<!DOCTYPE html>
<html>
    <head>
        <script src = 'require.js'></script>
        <script src = 'main.js'></script>
    </head>
    <body onload = "setInterval(main)"></body>
</html>

As to concerns about throughput, I've now tested this with huge amounts of data (~100 MB) and it appears to make little difference on performance as to whether the code was loaded with a script tag or via the ajax call. 关于吞吐量,我现在已经用大量数据(〜100 MB)对其进行了测试,对于代码是通过脚本标签还是通过ajax调用加载,似乎对性能没有太大影响。 All major browsers on two different operating systems and the results were pretty much the same. 所有主要浏览器都在两种不同的操作系统上运行,结果几乎相同。 So unless I see convincing evidence to the contrary I'm just going to assume this to be the rule rather than the exception. 因此,除非我看到令人信服的证据,否则我将假设这是规则,而不是例外。

All that said, I'm certainly still open to any comments or criticisms that may be. 话虽这么说,我当然仍然对可能的任何评论或批评持开放态度。

If you want module caching as well try this:如果您还想要模块缓存,请尝试以下操作:

let parent = "";
let cache = {};

/**
 * Load a JavaScript text synchronously.
 *
 * @param url The url.
 */
function cludge(url) {
    /* resolve and check */
    if (parent !== "")
        url = new URL(url, parent).href;
    let map = cache[url];
    if (map !== undefined)
        return map;
    map = {};
    cache[url] = map;

    /* load and execute */
    let back = parent;
    try {
        parent = url;
        let request = new XMLHttpRequest();
        request.open('GET', url, false);
        request.send();
        let fun = new Function("exports", "require", request.responseText);
        fun(map, cludge);
    } finally {
        parent = back;
    }
    return map;
}

But the name resolution is not as elaborate as in nodejs.但是名称解析不像 nodejs 那样复杂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM