JavaScript中是否存在类似于@import
CSS的内容,允许您在另一个JavaScript文件中包含JavaScript文件?
旧版本的JavaScript没有导入,包含或要求,因此开发了许多不同的方法来解决这个问题.
但自2015年(ES6)以来,JavaScript已经有了ES6模块标准来导入Node.js中的模块,大多数现代浏览器也支持这些模块.
为了与旧版浏览器兼容,可以使用构建和/或转换工具.
ES6模块自v8.5起,Node.js中支持ECMAScript(ES6)模块,带有--experimental-modules
标志.涉及的所有文件必须具有.mjs
扩展名.
// module.mjs
export function hello() {
return "Hello";
}
// main.mjs
import { hello } from 'module'; // or './module'
let val = hello(); // val is "Hello";
自 Safari 10.1,Chrome 61,Firefox 60和Edge 16 以来,浏览器已经支持直接加载ECMAScript模块(不需要像Webpack这样的工具).检查caniuse的当前支持.
// hello.mjs
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/
动态导入允许脚本根据需要加载其他脚本:
有关详情,请访问https://developers.google.com/web/updates/2017/11/dynamic-import
Node.js需要仍然在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加载您可以使用AJAX调用加载其他脚本,然后使用eval
它来运行它.这是最简单的方法,但由于JavaScript沙箱安全模型,它仅限于您的域.使用eval
也打开了通往错误,黑客和安全问题的大门.
与Dynamic Imports一样,您可以fetch
使用promises 加载一个或多个脚本,使用Promise来控制脚本依赖项的执行顺序,使用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)}`) })jQuery加载
在jQuery的库提供加载功能在同一行:
$.getScript("my_lovely_script.js", function() {
alert("Script loaded but not necessarily executed.");
});
动态脚本加载
您可以将带有脚本URL的脚本标记添加到HTML中.为了避免jQuery的开销,这是一个理想的解决方案.
该脚本甚至可以驻留在不同的服务器上.此外,浏览器评估代码.该标签可以被注入或者网页
,或者只是收盘前插入
标签.
以下是一个如何工作的示例:
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)
}
此函数将在页面的head部分的末尾添加一个新标记,其中该
src
属性设置为作为第一个参数赋予函数的URL.
这两个解决方案都在JavaScript疯狂:动态脚本加载中进行了讨论和说明.
检测脚本何时执行现在,您必须了解一个大问题.这样做意味着您远程加载代码.现代Web浏览器将加载文件并继续执行当前脚本,因为它们异步加载所有内容以提高性能.(这适用于jQuery方法和手动动态脚本加载方法.)
这意味着如果你直接使用这些技巧,在你要求加载后,你将无法在下一行使用新加载的代码,因为它仍然会加载.
例如: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.它的工作原理!混乱...
那该怎么办呢?
好吧,你可以在我给你的链接中使用作者建议的黑客.总之,对于匆忙的人,他在加载脚本时使用事件来运行回调函数.因此,您可以使用远程库将所有代码放在回调函数中.例如:
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);
请注意,脚本可以在DOM加载之后或之前执行,具体取决于浏览器以及是否包含该行script.async = false;
.有一篇关于Javascript加载的文章,讨论了这个问题.
截至本答案的上方所提到的,许多开发人员使用编译/ transpilation工具(S),如包裹,的WebPack,或者巴贝尔在自己的项目,允许他们使用即将推出的JavaScript语法,旧的浏览器提供向后兼容性,合并文件,缩小,执行代码拆分等
如果有人正在寻找更高级的东西,请尝试RequireJS.您将获得额外的好处,例如依赖关系管理,更好的并发性,并避免重复(即,多次检索脚本).
您可以在"模块"中编写JavaScript文件,然后在其他脚本中将它们作为依赖项引用.或者您可以将RequireJS用作简单的"go get this script"解决方案.
例:
将依赖项定义为模块:
一些-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是依赖some-dependency.js的 "主"JavaScript文件
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文件,因此您可以在加载后立即使用新加载的文件中包含的函数,我认为它适用于所有浏览器.
您需要jQuery.append()
在页面元素上使用,即:
$("head").append('');
但是,此方法也存在一个问题:如果导入的JavaScript文件中发生错误,Firebug(以及Firefox错误控制台和Chrome开发人员工具)也会错误地报告其位置,如果您使用Firebug跟踪这是一个大问题JavaScript错误很多(我这样做).由于某种原因,Firebug根本不知道新加载的文件,因此如果该文件中出现错误,它会报告它发生在您的主HTML文件中,并且您将无法找到错误的真正原因.
但如果这对您来说不是问题,那么这种方法应该可行.
我实际上编写了一个名为$ .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(''); import_js_imported.push(script); } } }); })(jQuery);
所以你需要做的就是导入JavaScript:
$.import_js('/path_to_project/scripts/somefunctions.js');
我也在Example中做了一个简单的测试.
它包括一个main.js
主HTML文件,然后在脚本中main.js
使用$.import_js()
导入称为一个附加文件included.js
,它定义了这个功能:
function hello() { alert("Hello world!"); }
在包括之后included.js
,hello()
调用该函数,您就会收到警报.
(这个答案是对e-satisf'评论的回应).
另一种方式,在我看来更清晰,是制作同步Ajax请求而不是使用标记.这也是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); } }); }
然后,您可以在代码中使用它,因为您通常使用包含:
require("/scripts/subscript.js");
并且能够在下一行中从所需脚本调用函数:
subscript.doSomethingCool();
对你来说有一个好消息.很快您就可以轻松加载JavaScript代码了.它将成为导入JavaScript代码模块的标准方式,并将成为核心JavaScript本身的一部分.
您只需编写import cond from 'cond.js';
以加载cond
从文件命名的宏cond.js
.
因此,您不必依赖任何JavaScript框架,也不必显式地进行Ajax调用.
参考:
静态模块分辨率
模块加载器
可以动态生成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");
声明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;
也许您可以使用我在此页面上找到的此功能如何在JavaScript文件中包含JavaScript文件?:
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
在其响应中设置标头.
要点:http://gist.github.com/284442.
以下是Facebook为普遍使用的Like按钮做广告的一般化版本:
如果你想要纯JavaScript,你可以使用document.write
.
document.write('');
如果使用jQuery库,则可以使用该$.getScript
方法.
$.getScript("another_script.js");
您还可以使用PHP组装脚本:
档案main.js.php
:
// Main JavaScript code goes here
此处显示的大多数解决方案都意味着动态加载.我正在搜索一个编译器,它将所有依赖的文件组合成一个输出文件.与Less/Sass预处理器相同,处理CSS @import
at-rule.由于我没有找到这种类似的东西,我写了一个简单的工具来解决这个问题.
所以这里是编译器,https://github.com/dsheiko/jsic,它可以$import("file-path")
安全地替换所请求的文件内容.这是相应的Grunt插件:https://github.com/dsheiko/grunt-jsic.
在jQuery master分支上,他们只是简单地将原子源文件连接成一个intro.js
以outtro.js
.开头和结尾的单个文件.这不适合我,因为它没有提供源代码设计的灵活性.看看它如何与jsic一起使用:
SRC/main.js
var foo = $import("./Form/Input/Tel");
SRC /表格/输入/ Tel.js
function() { return { prop: "", method: function(){} } }
现在我们可以运行编译器了:
node jsic.js src/main.js build/mail.js
并获得组合文件
建立/ main.js
var foo = function() { return { prop: "", method: function(){} } };
如果您要加载JavaScript文件的意图是使用导入/包含文件中的函数,您还可以定义全局对象并将函数设置为对象项.例如:
A = {};
A.func1 = function() { console.log("func1"); }
A.func2 = function() { console.log("func2"); }
A.func1(); A.func2();
在HTML文件中包含脚本时,您需要小心.订单应如下所示:
这应该做:
xhr = new XMLHttpRequest(); xhr.open("GET", "/soap/ajax/11.0/connection.js", false); xhr.send(); eval(xhr.responseText);
或者不是在运行时包括在上载之前使用脚本连接.
我使用链轮(我不知道是否还有其他链轮).您可以在单独的文件中构建JavaScript代码,并包含由Sprockets引擎作为包括处理的注释.对于开发,您可以按顺序包含文件,然后生产以合并它们...
也可以看看:
介绍Sprockets:JavaScript依赖关系管理和连接
如果您正在使用Web Workers并希望在worker的范围内包含其他脚本,则提供有关向head
标记添加脚本等的其他答案将不适合您.
幸运的是,Web Workers有自己的importScripts
功能,这是Web Worker范围内的全局功能,是浏览器本身的原生功能,因为它是规范的一部分.
或者,作为对您的问题突出显示的第二高投票答案,RequireJS还可以处理包含Web Worker中的脚本(可能调用importScripts
自身,但具有一些其他有用的功能).
我有一个简单的问题,但我对这个问题的回答感到困惑.
我不得不在另一个JavaScript文件(main.js)中使用一个JavaScript文件(myvariables.js)中定义的变量(myVar1).
为此,我做了如下:
以正确的顺序加载HTML文件中的JavaScript代码,首先是myvariables.js,然后是main.js:
文件:myvariables.js
var myVar1 = "I am variable from myvariables.js";
文件:main.js
// ... function bodyReady() { // ... alert (myVar1); // This shows "I am variable from myvariables.js", which I needed // ... } // ...
如您所见,我在另一个JavaScript文件中的一个JavaScript文件中使用了一个变量,但我不需要在另一个JavaScript文件中包含一个变量.我只需要确保在第二个JavaScript文件之前加载第一个JavaScript文件,并且第一个JavaScript文件的变量可以在第二个JavaScript文件中自动访问.
这节省了我的一天.我希望这有帮助.
在@import
实现CSS-像JavaScript语法进口可能使用的工具,如通过其特殊的混合.mix
文件类型(见这里).我想应用程序只是在内部使用上述方法之一,但我不知道.
从文件的混合文档.mix
:
混合文件只是带有.mix的.js或.css文件.在文件名中.混合文件只是扩展了普通样式或脚本文件的功能,并允许您导入和组合.
这是一个.mix
将多个.js
文件合并为一个文件的示例文件:
// scripts-global.mix.js // Plugins - Global @import "global-plugins/headroom.js"; @import "global-plugins/retina-1.1.0.js"; @import "global-plugins/isotope.js"; @import "global-plugins/jquery.fitvids.js";
Mixture将其输出为scripts-global.js
缩小版本(scripts-global.min.js
).
注意:除了将它用作前端开发工具之外,我与Mixture无任何关联.我看到一个.mix
JavaScript文件在运行中(在其中一个Mixture样板中)并且有点困惑("你能做到这一点吗?"我想到了自己)时遇到了这个问题.然后我意识到这是一个特定于应用程序的文件类型(有点令人失望,同意).然而,认为这些知识可能对其他人有所帮助.
更新:混合物现在是免费的(离线).
更新:混合物现已停产.旧的混合物发布仍然可用
我通常的方法是:
var require = function (src, cb) { cb = cb || function () {}; var newScriptTag = document.createElement('script'), firstScriptTag = document.getElementsByTagName('script')[0]; newScriptTag.src = src; newScriptTag.async = true; newScriptTag.onload = newScriptTag.onreadystatechange = function () { (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') && (cb()); }; firstScriptTag.parentNode.insertBefore(newScriptTag, firstScriptTag); }
它工作得很好,不会为我使用页面重新加载.我已经尝试过AJAX方法(其他一个答案),但它似乎对我来说效果不佳.
下面解释代码如何为那些好奇的代码工作:实际上,它创建了一个新的脚本标记(在第一个之后).它将其设置为异步模式,因此它不会阻止其余代码,但在readyState(要加载的内容的状态)更改为"已加载"时调用回调.
用现代语言来说就是这样
function loadJs( url ){ return new Promise( resolve => { const script = document.createElement( "script" ); script.src = url; script.onload = resolve; document.head.appendChild( script ); }); }
var js = document.createElement("script"); js.type = "text/javascript"; js.src = jsFilePath; document.body.appendChild(js);
我编写了一个简单的模块,可以自动执行在JavaScript中导入/包含模块脚本的工作.有关代码的详细说明,请参阅博客文章JavaScript require/import/include模块.
// ----- USAGE ----- require('ivar.util.string'); require('ivar.net.*'); require('ivar/util/array.js'); require('http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js'); ready(function(){ //Do something when required scripts are loaded }); //-------------------- var _rmod = _rmod || {}; //Require module namespace _rmod.LOADED = false; _rmod.on_ready_fn_stack = []; _rmod.libpath = ''; _rmod.imported = {}; _rmod.loading = { scripts: {}, length: 0 }; _rmod.findScriptPath = function(script_name) { var script_elems = document.getElementsByTagName('script'); for (var i = 0; i < script_elems.length; i++) { if (script_elems[i].src.endsWith(script_name)) { var href = window.location.href; href = href.substring(0, href.lastIndexOf('/')); var url = script_elems[i].src.substring(0, script_elems[i].length - script_name.length); return url.substring(href.length+1, url.length); } } return ''; }; _rmod.libpath = _rmod.findScriptPath('script.js'); //Path of your main script used to mark //the root directory of your library, any library. _rmod.injectScript = function(script_name, uri, callback, prepare) { if(!prepare) prepare(script_name, uri); var script_elem = document.createElement('script'); script_elem.type = 'text/javascript'; script_elem.title = script_name; script_elem.src = uri; script_elem.async = true; script_elem.defer = false; if(!callback) script_elem.onload = function() { callback(script_name, uri); }; document.getElementsByTagName('head')[0].appendChild(script_elem); }; _rmod.requirePrepare = function(script_name, uri) { _rmod.loading.scripts[script_name] = uri; _rmod.loading.length++; }; _rmod.requireCallback = function(script_name, uri) { _rmod.loading.length--; delete _rmod.loading.scripts[script_name]; _rmod.imported[script_name] = uri; if(_rmod.loading.length == 0) _rmod.onReady(); }; _rmod.onReady = function() { if (!_rmod.LOADED) { for (var i = 0; i < _rmod.on_ready_fn_stack.length; i++){ _rmod.on_ready_fn_stack[i](); }); _rmod.LOADED = true; } }; _.rmod = namespaceToUri = function(script_name, url) { var np = script_name.split('.'); if (np.getLast() === '*') { np.pop(); np.push('_all'); } if(!url) url = ''; script_name = np.join('.'); return url + np.join('/')+'.js'; }; //You can rename based on your liking. I chose require, but it //can be called include or anything else that is easy for you //to remember or write, except "import", because it is reserved //for future use. var require = function(script_name) { var uri = ''; if (script_name.indexOf('/') > -1) { uri = script_name; var lastSlash = uri.lastIndexOf('/'); script_name = uri.substring(lastSlash+1, uri.length); } else { uri = _rmod.namespaceToUri(script_name, ivar._private.libpath); } if (!_rmod.loading.scripts.hasOwnProperty(script_name) && !_rmod.imported.hasOwnProperty(script_name)) { _rmod.injectScript(script_name, uri, _rmod.requireCallback, _rmod.requirePrepare); } }; var ready = function(fn) { _rmod.on_ready_fn_stack.push(fn); };
虽然这些答案很棒,但是存在一个简单的"解决方案",因为脚本加载已存在,它将覆盖大多数人的99.999%的用例.只需在需要它的脚本之前包含所需的脚本.对于大多数项目,确定需要哪些脚本以及按什么顺序排列并不需要很长时间.
如果script2需要script1,这确实是做这样的事情的绝对最简单的方法.我很惊讶没有人提出这个问题,因为这是几乎每一个案例中最明显和最简单的答案.
我来到这个问题是因为我正在寻找一种简单的方法来维护一组有用的JavaScript插件.在看到这里的一些解决方案之后,我想出了这个:
设置一个名为"plugins.js"(或extensions.js或者你有什么)的文件.将插件文件与一个主文件保持在一起.
plugins.js将有一个名为"pluginNames []"的数组,我们将遍历每个(),然后为每个插件追加一个标记到头部
//当我们添加或删除插件文件时设置要更新的数组var pluginNames = ["lettering","fittext","butterjam"等]; //每个插件的一个脚本标签$ .each(pluginNames,function(){$('head').append('');});
手动调用你头脑中的一个文件:
我发现即使所有的插件都按照它们应该的方式放入头标签,但当你点击页面或刷新时,它们并不总是被浏览器运行.
我发现在PHP包中编写脚本标记更可靠.您只需编写一次,这与使用JavaScript调用插件一样重要.
此脚本将JavaScript文件添加到任何其他标记的顶部:
(function () { var li = document.createElement('script'); li.type = 'text/javascript'; li.src= "https://img.devbox.cn/3cccf/16086/243/367c4539743ff9b8.png" alt="结果"/>
`script.onreadystatechange`将永远不会触发。它特定于[`XMLHttpRequest`](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest)。例如,如果因为您实际上是使用XMLHttpRequest加载脚本而触发了,那将导致您的回调被频繁调用。因此,您应该删除该行。
40> gm2008..:如果您使用Angular,则插件模块$ ocLazyLoad可以帮助您完成此任务。
以下是其文档中的一些引文:
使用多个文件加载一个或多个模块和组件:
$ocLazyLoad.load(['testModule.js', 'testModuleCtrl.js', 'testModuleService.js']);加载具有多个文件的一个或多个模块,并在必要时指定类型:注:使用requireJS样式格式(例如,开头为js!)时,请勿指定文件扩展名。使用一个或另一个。
$ocLazyLoad.load([ 'testModule.js', {type: 'css', path: 'testModuleCtrl'}, {type: 'html', path: 'testModuleCtrl.html'}, {type: 'js', path: 'testModuleCtrl'}, 'js!testModuleService', 'less!testModuleLessFile' ]);您可以加载外部库(不是角度库):
$ocLazyLoad.load(['testModule.js', 'bower_components/bootstrap/dist/js/bootstrap.js', 'anotherModule.js']);您还可以加载CSS和模板文件:
$ocLazyLoad.load([ 'bower_components/bootstrap/dist/js/bootstrap.js', 'bower_components/bootstrap/dist/css/bootstrap.css', 'partials/template1.html' ]);
41> jasonleonhar..:
使用与Node.js一起使用的ES6导入和导出模块用
.mjs
扩展名代替文件名.js
建立档案
touch main.mjs lib.mjsmain.js
import { add } from './lib.mjs'; console.log(add(40, 2));lib.mjs
export let add = (x,y) => { return x + y }跑
node --experimental-modules main.js