以同步方式使用requirejs(AMD)是错误的吗?

Isa*_*aac 7 javascript amd requirejs

我正在开发一个JavaScript重的Web应用程序; 如果没有JavaScript,整个应用程序都是无用的.我目前正在使用requirejs作为我的模块加载器,以及将r.js我的JS优化为生产中的单个文件的工具.

目前,在生产中我的标记看起来像这样;

<script src="/js/require.js"></script>
<script>
    require.config({
       // blah blah blah
    });

    require(['editor']); // Bootstrap the JavaScript code.
</script>
Run Code Online (Sandbox Code Playgroud)

但是,这会异步加载JavaScript,这会使页面呈现尽管在加载JavaScript之前无法使用; 我没有看到这一点.相反,我想像这样同步加载JavaScript ;

<script src="/js/bundle.js"></script><!-- combine require.js, config and editor.js -->
Run Code Online (Sandbox Code Playgroud)

这样,当呈现页面时,它是可用的.我已经读过所有现代浏览器都支持并行加载,这使我相信互联网上的大多数建议都建议避免这种方法,因为它阻止并行下载已经过时了.

然而;

  1. AMD(异步模块定义)暗示这不是必须使用requirejs的方式.
  2. 在开发中,我想将未组合的文件作为多个脚本标签插入,而不是单个缩小的文件;

    <script src="/js/require.js"></script>
    <script>/* require.config(...); */</script>
    <script src="/js/editor-dep-1.js"></script>
    <script src="/js/editor-dep-2.js"></script>
    <script src="/js/editor.js"></script>
    
    Run Code Online (Sandbox Code Playgroud)

    ...但是这在requirejs中显得非常繁琐(使用r.js生成虚假构建,获取依赖项的列表editor.js),感觉不对.

因此,我的问题如下;

  1. 我是否正确避免同步<script />建议过时?
  2. 以这种方式使用requirejs/AMD是错误的吗?
  3. 我错过了替代技术/方法/工具/模式吗?

Isa*_*aac 2

我决定采纳ljfranklin 的建议,完全废除 RequireJS。我个人认为 AMD 做的都是错误的,CommonJS(及其同步行为)是正确的选择;但这是另一个讨论的事了。

\n\n

我看到的一件事是转向Browserify,但在开发过程中,每次编译(因为它扫描所有文件并查找require()调用)花费的时间太长,我认为无法接受。

\n\n

最后,我推出了自己的定制解决方案。它是它基本上Browserify,但它要求您指定所有依赖项,而不是让 Browserify 自行解决。这意味着编译只需几秒而不是 30 秒。

\n\n

这就是 TL;DR。下面我就详细说说我是如何做到的。抱歉长度。希望这可以帮助某人......或者至少给某人一些启发!

\n\n
\n\n

首先,我有我的 JavaScript 文件。它们被写成 \xc3\xa0 la CommonJS,其限制是exports不能作为“全局”变量使用(你必须使用module.exports)。例如:

\n\n
var anotherModule = require(\'./another-module\');\n\nmodule.exports.foo = function () {\n    console.log(anotherModule.saySomething());\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后,我在配置文件中指定依赖项的有序列表(注意js/support.js,它会在稍后保存):

\n\n
{\n  "js": [\n    "js/support.js",\n    "js/jquery.js",\n    "js/jquery-ui.js",\n    "js/handlebars.js",\n    // ...\n    "js/editor/manager.js",\n    "js/editor.js"\n  ]\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后,在编译过程中,我映射了所有 JavaScript 文件(在js/目录中)映射到表单;

\n\n
define(\'/path/to/js_file.js\', function (require, module) {\n    // The contents of the JavaScript file\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

但这对于原始 JavaScript 文件来说是完全透明的;define下面我们提供了对、require等的所有支持module,这样,对于原始的 JavaScript 文件它就可以工作

\n\n

我使用 grunt 进行映射;首先将文件复制到build目录中(这样我就不会弄乱原始文件),然后重写文件。

\n\n
// files were previous in public/js/*, move to build/js/*\ngrunt.initConfig({\n    copy: {\n      dist: {\n        files: [{\n          expand: true,\n          cwd: \'public\',\n          src: \'**/*\',\n          dest: \'build/\'\n        }]\n      }\n    }\n});\n\ngrunt.loadNpmTasks(\'grunt-contrib-copy\');\n\ngrunt.registerTask(\'buildjs\', function () {\n    var path = require(\'path\');\n\n    grunt.file.expand(\'build/**/*.js\').forEach(function (file) {\n      grunt.file.copy(file, file, {\n        process: function (contents, folder) {\n          return \'define(\\\'\' + folder + \'\\\', function (require, module) {\\n\' + contents + \'\\n});\'\n        },\n        noProcess: \'build/js/support.js\'\n      });\n    });\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

我有一个 file /js/support.js,它定义了define()我包装每个文件的函数;这就是神奇发生的地方,因为它增加了对module.exports和 的支持require()在不到 40 行的时间内

\n\n
(function () {\n    var cache = {};\n\n    this.define = function (path, func) {\n        func(function (module) {\n            var other = module.split(\'/\');\n            var curr = path.split(\'/\');\n            var target;\n\n            other.push(other.pop() + \'.js\');\n            curr.pop();\n\n            while (other.length) {\n                var next = other.shift();\n\n                switch (next) {\n                case \'.\':\n                break;\n                case \'..\':\n                    curr.pop();\n                break;\n                default:\n                    curr.push(next);\n                }\n            }\n\n            target = curr.join(\'/\');\n\n            if (!cache[target]) {\n                throw new Error(target + \' required by \' + path + \' before it is defined.\');\n            } else {\n                return cache[target].exports;\n            }\n        }, cache[path] = {\n            exports: {}\n        });\n    };\n}.call(this));\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后,在开发过程中,我逐字迭代配置文件中的每个文件并将其输出为单独的文件<script />标签;一切都是同步的,没有任何事情被缩小,一切都很快。

\n\n
{{#iter scripts}}<script src="{{this}}"></script>\n{{/iter}}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这给了我;

\n\n
<script src="js/support.js"></script>\n<script src="js/jquery.js"></script>\n<script src="js/jquery-ui.js"></script>\n<script src="js/handlebars.js"></script>\n<!-- ... -->\n<script src="js/editor/manager.js"></script>\n<script src="js/editor.js"></script>\n
Run Code Online (Sandbox Code Playgroud)\n\n

在生产中,我使用UglifyJs缩小并组合 JS 文件。好吧,从技术上讲,我使用 UglifyJs 的包装器;小型消防车

\n\n
grunt.registerTask(\'compilejs\', function () {\n    var minifier = require(\'mini-fier\').create();\n\n    if (config.production) {\n      var async = this.async();\n      var files = bundles.js || [];\n\n      minifier.js({\n        srcPath: __dirname + \'/build/\',\n        filesIn: files,\n        destination: __dirname + \'/build/js/all.js\'\n      }).on(\'error\', function () {\n        console.log(arguments);\n        async(false);\n      }).on(\'complete\', function () {\n        async();\n      });\n    }\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

...然后在应用程序代码中,我将scripts(用于容纳要在视图中输出的脚本的变量)更改为[\'/build/js/all.js\'],而不是实际文件的数组。这给了我一个

\n\n
<script src="/js/all.js"></script> \n
Run Code Online (Sandbox Code Playgroud)\n\n

... 输出。同步、精简、相当快。

\n