如何在JavaScript文件中引用另一个JavaScript文件,类似CSS的@import
?
如何在JavaScript文件中引用另一个JavaScript文件,类似CSS的@import
?
JavaScript的旧版本没有import、include或require,因此已经开发了许多不同的方法来解决这个问题。
但是自2015年(ES6)以来,JavaScript拥有ES6模块标准来在Node.js中导入模块,这也得到了大多数现代浏览器的支持。
为了兼容旧版浏览器,可以使用像Webpack和Rollup这样的构建工具和/或像Babel这样的转译工具。
ECMAScript(ES6)模块从v8.5开始在Node.js中得到支持,需要使用--experimental-modules
标志,在至少Node.js v13.8.0之后则不需要该标志。要启用"ESM"(与Node.js之前的CommonJS风格模块系统["CJS"]相对应),您可以在package.json
中使用"type": "module"
或将文件扩展名命名为.mjs
。(类似地,使用Node.js之前的CJS模块编写的模块可以命名为.cjs
,如果您的默认值为ESM,则可行。)
使用package.json
:
{
"type": "module"
}
然后是 module.js
:
export function hello() {
return "Hello";
}
main.js
:import { hello } from './module.js';
let val = hello(); // val is "Hello";
使用.mjs
,你将拥有module.mjs
:
export function hello() {
return "Hello";
}
main.mjs
:import { hello } from './module.mjs';
let val = hello(); // val is "Hello";
自Safari 10.1、Chrome 61、Firefox 60和Edge 16起,浏览器直接支持加载ECMAScript模块(不需要像Webpack等工具)。请在caniuse查看当前的支持情况。无需使用Node.js的.mjs
扩展名,浏览器完全忽略模块/脚本文件扩展名。
<script type="module">
import { hello } from './hello.mjs'; // Or the extension could be just `.js`
hello('world');
</script>
// hello.mjs -- or the extension could be just `.js`
export function hello(text) {
const div = document.createElement('div');
div.textContent = `Hello ${text}`;
document.body.appendChild(div);
}
请访问https://jakearchibald.com/2017/es-modules-in-browsers/了解更多内容。
动态导入允许脚本在需要时加载其他脚本:
<script type="module">
import('hello.mjs').then(module => {
module.hello('world');
});
</script>
请访问https://developers.google.com/web/updates/2017/11/dynamic-import了解更多信息。
旧的CJS模块风格在Node.js中仍被广泛使用,它是module.exports
/require
系统。
// mymodule.js
module.exports = {
hello: function() {
return "Hello";
}
}
// server.js
const myModule = require('./mymodule');
let val = myModule.hello(); // val is "Hello"
JavaScript有其他方法可以在浏览器中包含外部JavaScript内容,而无需预处理。
您可以使用AJAX调用加载附加脚本,然后使用eval
运行它。这是最直接的方法,但由于JavaScript沙箱安全模型的限制,它仅限于您的域。使用eval
还会带来错误、黑客和安全问题。
像动态导入一样,您可以使用fetch
调用和Fetch Inject库来加载一个或多个脚本,并使用承诺来控制脚本依赖项的执行顺序:
fetchInject([
'https://cdn.jsdelivr.net/momentjs/2.17.1/moment.min.js'
]).then(() => {
console.log(`Finish in less than ${moment().endOf('year').fromNow(true)}`)
})
$.getScript("my_lovely_script.js", function() {
alert("Script loaded but not necessarily executed.");
});
您可以将包含脚本 URL 的 script 标签添加到 HTML 中,这是避免使用 jQuery 的开销的理想解决方案。
脚本甚至可以驻留在不同的服务器上。此外,浏览器评估代码。script 标签可以注入到网页 head 或者插入到闭合的 body 标签前面。
以下是示例说明:
function dynamicallyLoadScript(url) {
var script = document.createElement("script"); // create a script DOM node
script.src = url; // set its src to the provided URL
document.head.appendChild(script); // add it to the end of the head section of the page (could change 'head' to 'body' to add it to the end of the body section instead)
}
<script>
标签,其中src
属性设置为作为第一个参数给出的URL。这两种解决方案都在JavaScript Madness: Dynamic Script Loading中讨论和说明。
my_lovely_script.js
包含 MySuperObject
:var js = document.createElement("script");
js.type = "text/javascript";
js.src = jsFilePath;
document.body.appendChild(js);
var s = new MySuperObject();
Error : MySuperObject is undefined
然后你按下F5重新加载页面。它就可以工作了!有点令人困惑...
那么该怎么办呢?
好的,你可以使用我给你的链接中作者建议的hack方法。简而言之,对于急于解决问题的人,他使用一个事件来运行回调函数,当脚本被加载时会执行该事件。所以你可以将所有使用远程库的代码放在回调函数中。例如:
function loadScript(url, callback)
{
// Adding the script tag to the head as suggested before
var head = document.head;
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
// Then bind the event to the callback function.
// There are several events for cross browser compatibility.
script.onreadystatechange = callback;
script.onload = callback;
// Fire the loading
head.appendChild(script);
}
然后您需要在lambda函数中加载脚本之后编写要使用的代码:
var myPrettyCode = function() {
// Here, do whatever you want
};
loadScript("my_lovely_script.js", myPrettyCode);
script.async = false;
这一行。有一篇关于JavaScript加载的优秀文章讨论了这个问题。
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "". Strict MIME type checking is enforced for module scripts per HTML spec.
将hello.mjs
更改为hello.js
后问题得以解决。 - Dan如果有人想要更高级的功能,可以尝试使用RequireJS。你将获得诸如依赖管理、更好的并发和避免重复(即多次检索脚本)等额外优点。
你可以将JavaScript文件编写为"模块",然后在其他脚本中引用它们作为依赖项。或者,你可以使用RequireJS作为一个简单的“获取此脚本”的解决方案。
示例:
将依赖项定义为模块:
some-dependency.js
define(['lib/dependency1', 'lib/dependency2'], function (d1, d2) {
//Your actual script goes here.
//The dependent scripts will be fetched if necessary.
return libraryObject; //For example, jQuery object
});
implementation.js 是你的 "主" JavaScript 文件,它依赖于 some-dependency.js
require(['some-dependency'], function(dependency) {
//Your script goes here
//some-dependency.js is fetched.
//Then your script is executed
});
来自GitHub自述文件的摘录:
RequireJS可以加载普通的JavaScript文件以及更具定义性的模块。它被优化用于在浏览器中使用,包括在Web Worker中使用,但也可以在其他JavaScript环境(如Rhino和Node)中使用。它实现了异步模块API。
RequireJS使用普通的脚本标记来加载模块/文件,因此应该易于调试。它可以简单地用于加载现有的JavaScript文件,所以你可以将其添加到现有项目中,而无需重写你的JavaScript文件。
...
实际上有一种方式可以加载JavaScript文件,而不是异步加载,因此您可以在加载后立即使用新加载的文件中包含的函数,并且我认为它适用于所有浏览器。
您需要在页面的<head>
元素上使用jQuery.append()
,也就是:
$("head").append($("<script></script>").attr("src", url));
/* Note that following line of code is incorrect because it doesn't escape the
* HTML attribute src correctly and will fail if `url` contains special characters:
* $("head").append('<script src="' + url + '"></script>');
*/
但如果这对您不是问题,那么这种方法应该可以解决。
实际上,我编写了一个名为$.import_js()的jQuery插件,它使用了这种方法:
(function($)
{
/*
* $.import_js() helper (for JavaScript importing within JavaScript code).
*/
var import_js_imported = [];
$.extend(true,
{
import_js : function(script)
{
var found = false;
for (var i = 0; i < import_js_imported.length; i++)
if (import_js_imported[i] == script) {
found = true;
break;
}
if (found == false) {
$("head").append($('<script></script').attr('src', script));
import_js_imported.push(script);
}
}
});
})(jQuery);
所以,您只需要做的是导入JavaScript:
$.import_js('/path_to_project/scripts/somefunctions.js');
main.js
的文件,然后main.js
中的脚本使用$.import_js()
导入了一个名为included.js
的附加文件,该文件定义了这个函数:function hello()
{
alert("Hello world!");
}
included.js
后,立即调用了 hello()
函数,然后您会收到警报提示。我认为更加简洁的方法是,使用同步 Ajax 请求而不是使用 <script>
标签。这也是 Node.js 处理包含文件的方式。
以下是使用 jQuery 的示例:
function require(script) {
$.ajax({
url: script,
dataType: "script",
async: false, // <-- This is the key
success: function () {
// all good...
},
error: function () {
throw new Error("Could not load script " + script);
}
});
}
你可以像使用include一样在代码中使用它:
require("/scripts/subscript.js");
并能够在下一行调用所需脚本中的函数:
subscript.doSomethingCool();
可以从其他 JavaScript 代码内动态生成 JavaScript 标签,并将其附加到 HTML 文档中。这将加载指定的 JavaScript 文件。
function includeJs(jsFilePath) {
var js = document.createElement("script");
js.type = "text/javascript";
js.src = jsFilePath;
document.body.appendChild(js);
}
includeJs("/path/to/some/file.js");
js.type
?(即使回到Chrome 66,浏览器也没有任何问题理解服务器提供的MIME类型)。 - personal_cloud声明 import
是在 ECMAScript 6 中出现的。
语法
import name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import name , { member [ , [...] ] } from "module-name";
import "module-name" as name;
function include(filename)
{
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.src = filename;
script.type = 'text/javascript';
head.appendChild(script)
}
这里是一个没有使用jQuery的同步版本:
function myRequire( url ) {
var ajax = new XMLHttpRequest();
ajax.open( 'GET', url, false ); // <-- the 'false' makes it synchronous
ajax.onreadystatechange = function () {
var script = ajax.response || ajax.responseText;
if (ajax.readyState === 4) {
switch( ajax.status) {
case 200:
eval.apply( window, [script] );
console.log("script loaded: ", url);
break;
default:
console.log("ERROR: script not loaded: ", url);
}
}
};
ajax.send(null);
}
请注意,要使其跨域工作,服务器需要在响应头中设置allow-origin
标头。
我刚刚用 Prototype 编写了以下 JavaScript 代码,用于操作 DOM:
var require = (function() {
var _required = {};
return (function(url, callback) {
if (typeof url == 'object') {
// We've (hopefully) got an array: time to chain!
if (url.length > 1) {
// Load the nth file as soon as everything up to the
// n-1th one is done.
require(url.slice(0, url.length - 1), function() {
require(url[url.length - 1], callback);
});
} else if (url.length == 1) {
require(url[0], callback);
}
return;
}
if (typeof _required[url] == 'undefined') {
// Haven't loaded this URL yet; gogogo!
_required[url] = [];
var script = new Element('script', {
src: url,
type: 'text/javascript'
});
script.observe('load', function() {
console.log("script " + url + " loaded.");
_required[url].each(function(cb) {
cb.call(); // TODO: does this execute in the right context?
});
_required[url] = true;
});
$$('head')[0].insert(script);
} else if (typeof _required[url] == 'boolean') {
// We already loaded the thing, so go ahead.
if (callback) {
callback.call();
}
return;
}
if (callback) {
_required[url].push(callback);
}
});
})();
使用方法:
<script src="prototype.js"></script>
<script src="require.js"></script>
<script>
require(['foo.js','bar.js'], function () {
/* Use foo.js and bar.js here */
});
</script>