Làm cách nào để bao gồm một tệp JavaScript trong một tệp JavaScript khác?


5194

Có một cái gì đó trong JavaScript tương tự như @importtrong CSS cho phép bạn bao gồm một tệp JavaScript bên trong một tệp JavaScript khác?



82
@Daniel, tôi không muốn sử dụng cuộc gọi AJAX.
Alec thông minh

13
Tại sao không khai báo tệp đã nhập trước tệp khác yêu cầu, chỉ cần sử dụng scriptthẻ theo thứ tự ?
falsarella

6
@Claudiu Điều đó sẽ không giúp nhập bất cứ thứ gì, nhưng nó cũng hoạt động tốt. Nếu bạn có một tệp JS phụ thuộc vào một tệp JS khác, trước tiên chỉ cần khai báo các thẻ script của các tệp phụ thuộc, vì vậy sau này sẽ có các phụ thuộc được tải. Nếu bạn có một tình huống không phải là một cách tiếp cận có thể, câu trả lời ở đây sẽ hữu ích.
falsarella

1
lợi thế thực tế của việc này là gì? dù bằng cách nào, cơ sở mã phụ thuộc vào tệp javascript sẽ không tải và bắt đầu hoạt động trong mọi trường hợp nó không được tải!
Ciasto piekarz

Câu trả lời:


4472

Các phiên bản cũ của JavaScript không có nhập, bao gồm hoặc yêu cầu, vì vậy nhiều cách tiếp cận khác nhau cho vấn đề này đã được phát triển.

Nhưng kể từ năm 2015 (ES6), JavaScript đã có tiêu chuẩn mô-đun ES6 để nhập mô-đun trong Node.js, cũng được hầu hết các trình duyệt hiện đại hỗ trợ .

Để tương thích với các trình duyệt cũ hơn, hãy xây dựng các công cụ như WebpackRollup và / hoặc các công cụ dịch mã như Babel có thể sử dụng .

Mô-đun ES6

Các mô-đun ECMAScript (ES6) đã được hỗ trợ trong Node.js kể từ v8.5, với --experimental-modulescờ và vì ít nhất Node.js v13.8.0 không có cờ. Để bật "ESM" (so với hệ thống mô-đun kiểu CommonJS trước đây của Node.js ["CJS"]), bạn có thể sử dụng "type": "module"trong package.jsonhoặc cung cấp cho các tệp phần mở rộng .mjs. (Tương tự, các mô-đun được viết bằng mô-đun CJS trước đó của Node.js có thể được đặt tên .cjsnếu mặc định của bạn là ESM.)

Sử dụng package.json:

{
    "type": "module"
}

Sau đó module.js:

export function hello() {
  return "Hello";
}

Sau đó main.js:

import { hello } from './module.js';
let val = hello();  // val is "Hello";

Sử dụng .mjs, bạn có module.mjs:

export function hello() {
  return "Hello";
}

Sau đó main.mjs:

import { hello } from './module.mjs';
let val = hello();  // val is "Hello";

Mô-đun ECMAScript trong trình duyệt

Các trình duyệt đã hỗ trợ tải trực tiếp các mô-đun ECMAScript (không yêu cầu công cụ như Webpack) kể từ Safari 10.1, Chrome 61, Firefox 60 và Edge 16. Kiểm tra hỗ trợ hiện tại tại caniuse . Không cần sử dụng .mjstiện ích mở rộng của Node.js ; trình duyệt hoàn toàn bỏ qua phần mở rộng tập tin trên các mô-đun / tập lệnh.

<script type="module">
  import { hello } from './hello.mjs'; // Or it could be simply `hello.js`
  hello('world');
</script>
// hello.mjs -- or it could be simply `hello.js`
export function hello(text) {
  const div = document.createElement('div');
  div.textContent = `Hello ${text}`;
  document.body.appendChild(div);
}

Đọc thêm tại https://jakearchibald.com/2017/es-modules-in-browsers/

Nhập động trong trình duyệt

Nhập động cho phép tập lệnh tải các tập lệnh khác khi cần:

<script type="module">
  import('hello.mjs').then(module => {
      module.hello('world');
    });
</script>

Đọc thêm tại https://developers.google.com/web/updates/2017/11/dynamic-import

Node.js yêu cầu

Kiểu mô-đun CJS cũ hơn, vẫn được sử dụng rộng rãi trong Node.js, là module.exports/require system.

// mymodule.js
module.exports = {
   hello: function() {
      return "Hello";
   }
}
// server.js
const myModule = require('./mymodule');
let val = myModule.hello(); // val is "Hello"   

Có nhiều cách khác để JavaScript đưa nội dung JavaScript bên ngoài vào các trình duyệt không yêu cầu tiền xử lý.

Đang tải AJAX

Bạn có thể tải một tập lệnh bổ sung với một cuộc gọi AJAX và sau đó sử dụng evalđể chạy nó. Đây là cách đơn giản nhất, nhưng nó bị giới hạn trong miền của bạn do mô hình bảo mật hộp cát JavaScript. Sử dụng evalcũng mở ra cánh cửa cho các lỗi, hack và các vấn đề bảo mật.

Đang tải

Giống như Nhập khẩu động, bạn có thể tải một hoặc nhiều tập lệnh bằng một fetchcuộc gọi bằng cách sử dụng lời hứa để kiểm soát thứ tự thực hiện cho các phụ thuộc tập lệnh bằng thư viện Fetch Tiêm :

fetchInject([
  'https://cdn.jsdelivr.net/momentjs/2.17.1/moment.min.js'
]).then(() => {
  console.log(`Finish in less than ${moment().endOf('year').fromNow(true)}`)
})

Đang tải jQuery

Các jQuery thư viện cung cấp chức năng tải trong một dòng :

$.getScript("my_lovely_script.js", function() {
   alert("Script loaded but not necessarily executed.");
});

Đang tải tập lệnh động

Bạn có thể thêm thẻ tập lệnh với URL tập lệnh vào HTML. Để tránh chi phí hoạt động của jQuery, đây là một giải pháp lý tưởng.

Kịch bản thậm chí có thể nằm trên một máy chủ khác. Hơn nữa, trình duyệt đánh giá mã. Các <script>thẻ có thể được tiêm vào một trong hai trang web <head>, hoặc chèn ngay trước thẻ đóng </body>thẻ.

Đây là một ví dụ về cách thức này có thể hoạt động:

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)
}

Hàm này sẽ thêm một <script>thẻ mới vào cuối phần đầu của trang, trong đó srcthuộc tính được đặt thành URL được cung cấp cho hàm làm tham số đầu tiên.

Cả hai giải pháp này đều được thảo luận và minh họa trong JavaScript Madness: Dynamic Script Loading .

Phát hiện khi tập lệnh đã được thực thi

Bây giờ, có một vấn đề lớn bạn phải biết về. Làm điều đó có nghĩa là bạn tải mã từ xa . Các trình duyệt web hiện đại sẽ tải tệp và tiếp tục thực thi tập lệnh hiện tại của bạn vì chúng tải mọi thứ không đồng bộ để cải thiện hiệu suất. (Điều này áp dụng cho cả phương thức jQuery và phương thức tải tập lệnh động thủ công.)

Điều đó có nghĩa là nếu bạn sử dụng các thủ thuật này trực tiếp, bạn sẽ không thể sử dụng mã mới được tải của mình dòng tiếp theo sau khi bạn yêu cầu nó được tải , bởi vì nó sẽ vẫn đang tải.

Ví dụ: my_lovely_script.jschứa 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

Sau đó, bạn tải lại các trang nhấn F5. Và nó hoạt động! Gây nhầm lẫn...

Vậy giờ làm gì với nó ?

Chà, bạn có thể sử dụng bản hack mà tác giả gợi ý trong liên kết tôi đưa cho bạn. Tóm lại, đối với những người vội vàng, anh ta sử dụng một sự kiện để chạy chức năng gọi lại khi tập lệnh được tải. Vì vậy, bạn có thể đặt tất cả mã bằng thư viện từ xa trong chức năng gọi lại. Ví dụ:

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);
}

Sau đó, bạn viết mã bạn muốn sử dụng SAU kịch bản được tải trong hàm lambda :

var myPrettyCode = function() {
   // Here, do whatever you want
};

Sau đó, bạn chạy tất cả:

loadScript("my_lovely_script.js", myPrettyCode);

Lưu ý rằng tập lệnh có thể thực thi sau khi DOM được tải hoặc trước đó, tùy thuộc vào trình duyệt và liệu bạn có bao gồm dòng không script.async = false;. Có một bài viết tuyệt vời về tải Javascript nói chung về vấn đề này.

Hợp nhất / tiền xử lý mã nguồn

Như đã đề cập ở đầu câu trả lời này, nhiều nhà phát triển sử dụng (các) công cụ xây dựng / dịch mã như Parcel, Webpack hoặc Babel trong các dự án của họ, cho phép họ sử dụng cú pháp JavaScript sắp tới, cung cấp khả năng tương thích ngược cho các trình duyệt cũ hơn, kết hợp các tệp, thu nhỏ, thực hiện tách mã, v.v.


130
Không, nhưng ai đó sử dụng thứ gì đó tiên tiến như Rhino nếu không sẽ không hỏi câu hỏi này.
e-satis

192
Để hoàn thành, có một cách thứ ba: Trong một số giải pháp nhất định khi bạn kiểm soát cả hai tệp javascript, bạn chỉ có thể tạo 1 tệp javascript khổng lồ kết hợp nội dung của cả hai tệp.
cóc

20
Không nên "document.createEuity (" my_lovely_script.js ");" trong ví dụ là "document.createEuity (" script ")"?
Russell Silva

14
Làm thế nào để eval mở cánh cửa cho hack nếu đó là mã của bạn mà bạn đang thực thi?
Vince Panuccio

6
Câu trả lời của bạn yêu cầu một số sửa đổi cho IE ( onreadystatechangesự kiện và thuộc readyStatetính). Ngoài ra, tải tập lệnh động không được hưởng lợi từ các máy quét tải trước của broswer's. Đề xuất bài viết HTML5Rocks này: html5rocks.com/en/tutorials/speed/script-loading
ruhong

570

Nếu bất cứ ai đang tìm kiếm một cái gì đó cao cấp hơn, hãy thử RequireJS . Bạn sẽ nhận được các lợi ích bổ sung như quản lý phụ thuộc, đồng thời tốt hơn và tránh trùng lặp (nghĩa là truy xuất tập lệnh nhiều lần).

Bạn có thể viết các tệp JavaScript của mình trong "các mô-đun" và sau đó tham chiếu chúng dưới dạng các phụ thuộc trong các tập lệnh khác. Hoặc bạn có thể sử dụng RequireJS như một giải pháp "đi lấy tập lệnh" đơn giản này.

Thí dụ:

Xác định các phụ thuộc dưới dạng các mô-đun:

một số phụ thuộc.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
});

exec.js là tệp JavaScript "chính" của bạn phụ thuộc vào một số phụ thuộc.j

require(['some-dependency'], function(dependency) {

    //Your script goes here
    //some-dependency.js is fetched.   
    //Then your script is executed
});

Trích từ GitHub README:

RequireJS tải các tệp JavaScript đơn giản cũng như các mô-đun được xác định rõ hơn. Nó được tối ưu hóa để sử dụng trong trình duyệt, kể cả trong Web Worker, nhưng nó có thể được sử dụng trong các môi trường JavaScript khác, như Rhino và Node. Nó triển khai API mô-đun không đồng bộ.

RequireJS sử dụng các thẻ script đơn giản để tải các mô-đun / tệp, vì vậy nó sẽ cho phép gỡ lỗi dễ dàng. Nó có thể được sử dụng đơn giản để tải các tệp JavaScript hiện có, vì vậy bạn có thể thêm nó vào dự án hiện tại của mình mà không phải viết lại các tệp JavaScript của mình.

...


1
@aaaidan: Lý do của MattDmo cộng với việc nó dựa vào một thư viện bên ngoài mà dựa vào câu trả lời được chấp nhận.
David Mulder

1
Để khắc phục các yêu cầu mới nhất, js mới nhất sẽ là js góc cạnh, ổn định hơn và dễ sử dụng cùng với các tính năng HTML phong phú và ràng buộc khác.
zeeshan

5
-1: Những trừu tượng đó - "some_dependency" - thực sự kém, với các chỉ mục thêm vào sự nhầm lẫn. Tôi đấu tranh để hiểu một ví dụ mã làm việc trông như thế nào. Nếu tác giả cung cấp ví dụ làm việc, hầu như bất kỳ ai cũng có thể điều chỉnh và khái quát nó theo nhu cầu của mình.
Tegiri Nenashi

1
không hoạt động tốt với gói MVC và tập lệnh với tính năng thu nhỏ tự động
Triynko

1
'thêm .. w / o phải viết lại các tệp JavaScript của bạn'; mát mẻ, nhưng chính xác như thế nào? Hay gây. câu trả lời gợi ý thêm một 'yêu cầu' (không phải 'requestjs'?) vào tập lệnh chính + 'xác định' như vậy cho mọi tệp phụ thuộc, đúng không? Nhưng đó là viết lại nên cái gì không viết lại? -biết mã từ RequireJS cho phép tập tin ea tạo defs toàn cầu như bình thường? -Phải không? Tôi chỉ cố gắng đó, + (quên?) <Script src = " requirejs.org/docs/release/2.2.0/comments/require.js "> </ script>, sau đó 'requirejs ([' GoogleDrive.com/host /..',..THER,feft(a,b) {mycode}) ': Fireorms nói rằng mọi phụ thuộc được tải NHƯNG defs của họ không được xác định trong mycode & after.
Destiny Architect

195

Thực sự một cách để tải tệp JavaScript không đồng bộ, vì vậy bạn có thể sử dụng các chức năng có trong tệp mới tải ngay sau khi tải và tôi nghĩ rằng nó hoạt động trong tất cả các trình duyệt.

Bạn cần sử dụng jQuery.append()trên thành <head>phần của trang của bạn, đó là:

$("head").append('<script type="text/javascript" src="' + script + '"></script>');

Tuy nhiên, phương pháp này cũng có một vấn đề: nếu xảy ra lỗi trong tệp JavaScript đã nhập, Fireorms (và cả Firefox Error Console và Chrome Developer Tools ) sẽ báo cáo vị trí của nó không chính xác, đó là một vấn đề lớn nếu bạn sử dụng Fireorms để theo dõi Lỗi JavaScript rất nhiều (tôi làm). Firebird chỉ đơn giản là không biết về tệp mới được tải vì một số lý do, vì vậy nếu xảy ra lỗi trong tệp đó, nó báo cáo rằng nó đã xảy ra trong HTML chính của bạn tệp và bạn sẽ gặp khó khăn khi tìm ra lý do thực sự gây ra lỗi.

Nhưng nếu đó không phải là vấn đề với bạn, thì phương pháp này sẽ hoạt động.

Tôi thực sự đã viết một plugin jQuery có tên $ .import_js () sử dụng phương thức này:

(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 type="text/javascript" src="' + script + '"></script>');
                import_js_imported.push(script);
            }
        }
    });

})(jQuery);

Vì vậy, tất cả những gì bạn cần làm để nhập JavaScript là:

$.import_js('/path_to_project/scripts/somefunctions.js');

Tôi cũng đã thực hiện một thử nghiệm đơn giản cho điều này tại Ví dụ .

Nó bao gồm một main.jstệp trong HTML chính và sau đó tập lệnh được main.jssử dụng $.import_js()để nhập một tệp bổ sung được gọi là included.jsđịnh nghĩa hàm này:

function hello()
{
    alert("Hello world!");
}

Và ngay sau khi bao gồm included.js, hello()chức năng được gọi và bạn nhận được cảnh báo.

(Câu trả lời này là để đáp lại bình luận của e-satis).


Tôi đang thử phương pháp này, nhưng không hiệu quả với tôi, phần tử không xuất hiện trong thẻ head.
trang web

16
@juanpastas - sử dụng jQuery.getScript, theo cách đó bạn không phải lo lắng về việc viết plugin ...
MattDMo

6
Hmm, theo bài viết này , việc thêm một scriptphần tử vào headsẽ khiến nó chạy không đồng bộ, trừ khi asyncđược đặt riêng false.
Flimm

1
Không nên biến tập lệnh có mã hóa thực thể html? Nếu liên kết chứa ", mã sẽ bị
hỏng

1
@Flimm thân mến, tôi và nhóm của tôi cảm ơn vì nhận xét của bạn và cá nhân tôi nợ bạn một cốc bia bằng tiền nếu chúng ta gặp nhau trực tiếp
Alex

155

Một cách khác, theo tôi là sạch hơn nhiều, là tạo một yêu cầu Ajax đồng bộ thay vì sử dụng <script>thẻ. Đó cũng là cách xử lý Node.js bao gồm.

Đây là một ví dụ sử dụng 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);
        }
    });
}

Sau đó, bạn có thể sử dụng nó trong mã của mình vì bạn thường sử dụng bao gồm:

require("/scripts/subscript.js");

Và có thể gọi một hàm từ tập lệnh được yêu cầu trong dòng tiếp theo:

subscript.doSomethingCool(); 

1
Giải pháp tốt, đầu bao gồm không đồng bộ, giải pháp ajax hoạt động.
Matteo Conta

17
Như một người khác đã đề cập, allowjs.org thực hiện điều này và cũng có một trình biên dịch trước để đặt các tệp js lại với nhau để chúng tải nhanh hơn. Bạn có thể muốn kiểm tra nó.
Ariel

2
Đã tìm thấy Tôi có thể gỡ lỗi bằng cách thêm lệnh này vào cuối tệp cho Chrome: // @ sourceURL = view_index.js
Todd Vance

11
unfortunatelly, async: false hiện không được dùng trong jQuery. Có thể phá vỡ trong tương lai, vì vậy tôi sẽ tránh.
sqram

3
@katsh Chúng tôi không sử dụng các đối tượng jqXHR ở đây. Trích dẫn của bạn dường như không sao lưu bình luận trước đó của bạn nói rằng async: falseđược cho là không được chấp nhận. Không phải vậy! Như trích dẫn của bạn, chỉ có những thứ liên quan đến jqXHR.
Zero3

101

Có một tin tốt cho bạn. Bạn sẽ sớm có thể tải mã JavaScript một cách dễ dàng. Nó sẽ trở thành một cách tiêu chuẩn để nhập các mô-đun mã JavaScript và sẽ là một phần của chính JavaScript cốt lõi.

Bạn chỉ cần viết import cond from 'cond.js';để tải một macro có tên condtừ một tệp cond.js.

Vì vậy, bạn không cần phải dựa vào bất kỳ khung JavaScript nào và bạn cũng không phải thực hiện các cuộc gọi Ajax một cách rõ ràng .

Tham khảo:


12
yêu cầu / nhập trên jsfile đã quá lâu trong thời gian tới. (IMO).
rwheadon

6
@rwheadon yeah có vẻ kinh khủng vì đây không phải là một phần của ngôn ngữ! Làm thế nào js mọi người có được bất cứ điều gì được thực hiện là ngoài tôi! Tôi mới biết điều này và đây có vẻ là điều điên rồ nhất (trong số rất nhiều) sự điên rồ
JonnyRaa

@ jonny-leed Ngay cả khi không tải mô-đun tích hợp, JavaScript trong trình duyệt đủ linh hoạt để chúng tôi có thể triển khai thư viện như RequireJS để quản lý mô-đun.
Keen

8
Giữa năm 2015- Vẫn chưa được triển khai trong bất kỳ trình duyệt nào, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Kẻ

Vâng, bạn đúng. Bây giờ tôi bắt đầu cảm thấy tồi tệ vì điều này :(. Thouh, một khi được triển khai trong tất cả các trình duyệt thì đây sẽ là một tính năng tuyệt vời được tích hợp trong javascript.
Imdad 7/07/2015

96

Có thể tự động tạo thẻ JavaScript và gắn nó vào tài liệu HTML từ bên trong mã JavaScript khác. Điều này sẽ tải tệp JavaScript được nhắm mục tiêu.

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");

8
@ e-satis - Trên thực tế, đây là một lợi thế, tập lệnh đồng bộ hóa sẽ bị chặn. Ngựa cho các khóa học, nhưng 9 lần trong 10 bạn muốn tùy chọn không chặn.
annakata

@Svitlana - các yếu tố kịch bản được tạo như thế này là không đồng bộ. Hiện tại điều này có thể được xem là khai thác lỗ hổng để nó có thể không phải là bằng chứng trong tương lai, tôi chưa thấy bất cứ điều gì trong bất kỳ tiêu chuẩn nào làm rõ điều này.
annakata

Thật ra tôi lấy lại, tôi nhận thấy bạn đang nối vào cơ thể chứ không phải đầu.
annakata

@annakata: sự khác biệt là gì? BTW, yahoo đề nghị thêm các thẻ script vào phần cuối của cơ thể, vì vậy không có gì sai với nó khi nối thêm động.
Svitlana Maksymchuk

7
@ e-satis không đồng bộ là tốt vì nó sẽ không đóng băng trang của bạn. Sử dụng gọi lại để được thông báo khi hoàn thànhjs.onload = callback;
Vitim.us

74

Tuyên bố importtrong ECMAScript 6.

Cú pháp

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;

... nhưng không được hỗ trợ bởi bất kỳ trình duyệt nào cho đến nay, theo bảng tương thích trên trang bạn liên kết đến.
Zero3

6
Bây giờ bạn có thể viết mã ES6 và biên dịch nó với Babel.js ( babeljs.io ) cho dù hệ thống mô-đun hiện tại ưa thích của bạn là gì (CommonJS / AMD / UMD): babeljs.io/docs/usage/modules
Jeremy Harris

@ Zero3 Rõ ràng IE mới (Edge) là duy nhất
Julian Avar

năm 2019 IE vẫn không hỗ trợ điều này dưới mọi hình thức. Tại sao microsoft phải chống lại khả năng sử dụng?
GreySage

61

Có lẽ bạn có thể sử dụng chức năng này mà tôi tìm thấy trên trang này Làm cách nào để đưa tệp JavaScript vào tệp 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)
}

5
Sẽ hữu ích để thêmscript.onload = callback;
Vitim.us

@SvitlanaMaksymchuk vì vậy, nếu tôi không sử dụng var, biến sẽ là toàn cầu?
Francisco Corrales Morales

@FranciscoCorrales có.
Christopher Chiche

Nó kết thúc ở toàn cầu có hoặc không có var :)
Vedran Maricevic.


54

Đây là phiên bản đồng bộ không có 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);
}

Lưu ý rằng để có được tên miền chéo hoạt động này, máy chủ sẽ cần đặt allow-origintiêu đề trong phản hồi của nó.


Chức năng tuyệt vời! Tải JavaScript trước khi bất kỳ JS bổ sung nào được viết sau phần thân. Rất quan trọng khi tải nhiều tập lệnh.
tfont

3
@heinob: Tôi có thể làm gì để nó hoạt động cho tên miền chéo? (tải tập lệnh từ http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js)
user2284570

@ user2284570: Nếu bạn là chủ sở hữu của tên miền nước ngoài: đặt tiêu đề `allow-origin 'trong câu trả lời của máy chủ. Nếu bạn không phải là chủ sở hữu: không có gì. Lấy làm tiếc! Đó là chính sách chéo nguồn gốc.
heinob

2
@ user2284570: Tôi hiểu nhận xét của bạn theo cách đó, rằng bạn không phải là chủ sở hữu của tên miền mà bạn muốn tải tập lệnh. Trong trường hợp đó, bạn chỉ có thể tải tập lệnh thông qua <script>thẻ được chèn , không qua XMLHttpRequest.
heinob

2
Đối với những người dự định sử dụng điều này trong Firefox (nói về kịch bản imacros), hãy thêm dòng này vào đầu tệp:const XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");
Kwestion

49

Tôi vừa viết mã JavaScript này (sử dụng Prototype để thao tác 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);
        }
    });
})();

Sử dụng:

<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>

Gist: http://gist.github.com/284442 .


7
jrburke đã viết điều này là RequireJS. Github: allowjs.org/docs/requirements.html
Mike Caron

Đây không phải là đặt tập lệnh được tải bên ngoài phạm vi yêu cầu () được gọi sao? Có vẻ như eval () là cách duy nhất để làm điều đó trong phạm vi. Hoặc là có một cách khác?
trusktr

44

Đây là phiên bản tổng quát về cách Facebook thực hiện nó cho nút Like phổ biến của họ:

<script>
  var firstScript = document.getElementsByTagName('script')[0],
      js = document.createElement('script');
  js.src = 'https://cdnjs.cloudflare.com/ajax/libs/Snowstorm/20131208/snowstorm-min.js';
  js.onload = function () {
    // do stuff with your dynamically loaded script
    snowStorm.snowColor = '#99ccff';
  };
  firstScript.parentNode.insertBefore(js, firstScript);
</script>

Nếu nó hoạt động cho Facebook, nó sẽ làm việc cho bạn.

Lý do tại sao chúng tôi tìm kiếm scriptphần tử đầu tiên thay vì headhoặc bodylà vì một số trình duyệt không tạo phần tử nếu thiếu, nhưng chúng tôi đảm bảo có scriptphần tử - phần tử này. Đọc thêm tại http: //www.jspotypes.com/the-ridicificent-case-of-adding-a-script-element/ .


3
Chết tiệt Một số phương pháp ở đây cũng hoạt động, nhưng trong một thiết lập động, điều này hoạt động tốt nhất.
tfont

tôi đã mở rộng kịch bản của bạn để loại bỏ chế độ công khai
Kamil Dąbrowski

39

Nếu bạn muốn trong JavaScript thuần, bạn có thể sử dụng document.write.

document.write('<script src="myscript.js" type="text/javascript"></script>');

Nếu bạn sử dụng thư viện jQuery, bạn có thể sử dụng $.getScriptphương thức này.

$.getScript("another_script.js");

8
sẽ không document.write loại bỏ mọi thứ khác?
Eisa Adil

30

Bạn cũng có thể tập hợp các tập lệnh của mình bằng PHP :

Tập tin main.js.php:

<?php
    header('Content-type:text/javascript; charset=utf-8');
    include_once("foo.js.php");
    include_once("bar.js.php");
?>

// Main JavaScript code goes here

16
Âm thanh giống như vấn đề là giữ tất cả những thứ này trong javascript ở mặt trước
Ariel

1
Cảm ơn đã nhắc nhở điều này. Bạn cũng có thể có các thẻ <script> PHP viết trong tiêu đề HTML của mình, để các tệp js bạn cần (và chỉ những tệp đó) sẽ được tải.
Rolf

29

Hầu hết các giải pháp hiển thị ở đây ngụ ý tải động. Thay vào đó, tôi đã tìm kiếm một trình biên dịch tập hợp tất cả các tệp phụ thuộc vào một tệp đầu ra. Giống như các bộ tiền xử lý Ít / Sass xử lý @importtheo quy tắc CSS . Vì tôi không tìm thấy bất cứ điều gì tử tế kiểu này, tôi đã viết một công cụ đơn giản để giải quyết vấn đề.

Vì vậy, đây là trình biên dịch, https://github.com/dsheiko/jsic , thay thế $import("file-path")cho nội dung tệp được yêu cầu một cách an toàn. Đây là plugin Grunt tương ứng : https://github.com/dsheiko/grunt-jsic .

Trên nhánh chính của jQuery, họ chỉ cần ghép các tệp nguồn nguyên tử thành một tệp duy nhất bắt đầu intro.jsvà kết thúc bằng outtro.js. Điều đó không phù hợp với tôi vì nó không cung cấp sự linh hoạt trong thiết kế mã nguồn. Kiểm tra cách nó hoạt động với jsic:

src / main.js

var foo = $import("./Form/Input/Tel");

src / Form / Input / Tel.js

function() {
    return {
          prop: "",
          method: function(){}
    }
}

Bây giờ chúng ta có thể chạy trình biên dịch:

node jsic.js src/main.js build/mail.js

Và lấy tập tin kết hợp

xây dựng / main.js

var foo = function() {
    return {
          prop: "",
          method: function(){}
    }
};

2
Vì bài đăng này tôi đã đưa ra giải pháp tốt hơn nhiều - trình biên dịch mô-đun CommonJS - github.com/dsheiko/cjsc Vì vậy, bạn có thể chỉ cần viết các mô-đun CommonJs hoặc NodeJs và truy cập lẫn nhau mà vẫn giữ chúng trong phạm vi tách biệt. Lợi ích: Không cần nhiều yêu cầu HTTP ảnh hưởng đến hiệu suất Bạn không cần gói thủ công mã mô-đun - đó là trách nhiệm của trình biên dịch (vì vậy khả năng đọc mã nguồn tốt hơn) Bạn không cần bất kỳ thư viện bên ngoài nào Tương thích với UMD- và các mô-đun NodeJs (ví dụ: bạn có thể giải quyết jQuery, Xương sống dưới dạng mô-đun mà không cần chạm vào mã của chúng)
Dmitry Sheiko

26

Nếu bạn có ý định tải tệp JavaScript đang sử dụng các chức năng từ tệp đã nhập / bao gồm , bạn cũng có thể xác định một đối tượng toàn cục và đặt các hàm làm các mục đối tượng. Ví dụ:

global.js

A = {};

file1.js

A.func1 = function() {
  console.log("func1");
}

file2.js

A.func2 = function() {
  console.log("func2");
}

main.js

A.func1();
A.func2();

Bạn chỉ cần cẩn thận khi đưa các tập lệnh vào tệp HTML. Thứ tự nên như dưới đây:

<head>
  <script type="text/javascript" src="global.js"></script>
  <script type="text/javascript" src="file1.js"></script>
  <script type="text/javascript" src="file2.js"></script>
  <script type="text/javascript" src="main.js"></script>
</head>

22

Điều này nên làm:

xhr = new XMLHttpRequest();
xhr.open("GET", "/soap/ajax/11.0/connection.js", false);
xhr.send();
eval(xhr.responseText);

5
evalchuyện gì với nó vậy. Từ Crockford , " evallà xấu. evalHàm này là tính năng bị lạm dụng nhiều nhất của JavaScript. Tránh nó. evalCó bí danh. Không sử dụng hàm Functiontạo. Không chuyển chuỗi sang setTimeouthoặc setInterval." Nếu bạn chưa đọc "JavaScript: Bộ phận tốt" của anh ấy thì hãy ra ngoài và làm ngay bây giờ. Bạn sẽ không hối tiếc.
MattDMo

13
@MattDMo "Ai đó nói nó tệ" thực sự không phải là một cuộc tranh luận.
Casey

4
@emodendroket Tôi hiểu rằng bạn không biết Douglas Crockford là ai.
MattDMo

13
@MattDMo Tôi hoàn toàn biết anh ấy là ai, nhưng anh ấy là một con người, không phải là một vị thần.
Casey

2
@tggagne: Tôi có thể làm gì để nó hoạt động cho tên miền chéo? (tải tập lệnh từ http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js)
user2284570

21

Hoặc thay vì bao gồm trong thời gian chạy, sử dụng tập lệnh để ghép nối trước khi tải lên.

Tôi sử dụng Sprockets (Tôi không biết nếu có người khác). Bạn xây dựng mã JavaScript của mình trong các tệp riêng biệt và bao gồm các nhận xét được xử lý bởi công cụ Sprockets như bao gồm. Để phát triển, bạn có thể bao gồm các tệp tuần tự, sau đó để sản xuất hợp nhất chúng ...

Xem thêm:


Browserify là phổ biến hơn nhiều so với Sprockets.
Dan Dascalescu

18

Tôi đã có một vấn đề đơn giản, nhưng tôi đã gặp khó khăn khi trả lời câu hỏi này.

Tôi đã phải sử dụng một biến (myVar1) được xác định trong một tệp JavaScript (myvariables.js) trong một tệp JavaScript khác (main.js).

Đối với điều này, tôi đã làm như dưới đây:

Đã tải mã JavaScript trong tệp HTML, theo đúng thứ tự, trước tiên là myvariables.js, sau đó là main.js:

<html>
    <body onload="bodyReady();" >

        <script src="myvariables.js" > </script>
        <script src="main.js" > </script>

        <!-- Some other code -->
    </body>
</html>

Tập tin: myvariables.js

var myVar1 = "I am variable from myvariables.js";

Tệp: main.js

// ...
function bodyReady() {
    // ...
    alert (myVar1);    // This shows "I am variable from myvariables.js", which I needed
    // ...
}
// ...

Như bạn đã thấy, tôi đã sử dụng một biến trong một tệp JavaScript trong một tệp JavaScript khác, nhưng tôi không cần đưa một biến vào một tệp khác. Tôi chỉ cần đảm bảo rằng tệp JavaScript đầu tiên được tải trước tệp JavaScript thứ hai và, các biến của tệp JavaScript đầu tiên có thể truy cập được trong tệp JavaScript thứ hai, tự động.

Điều này đã cứu ngày của tôi. Tôi hi vọng cái này giúp được.


Vấn đề với câu trả lời này là nó không giống như thế import. Bạn cần một tệp HTML để lấy nội dung từ tệp js này sang tệp khác.
Tự sát

Rất đúng. Đó là những gì một số câu trả lời khác đang có nó như là giải pháp. Tuy nhiên, tôi có hai tệp JS, một tệp nên sử dụng các biến khác của JS. Không có node.js, next.js, express.js, v.v., vì vậy, nhập OR yêu cầu sẽ không hoạt động, chỉ có các tệp .js đơn giản.
Manohar Reddy Poreddy

Tuy nhiên, có thể hiểu được câu hỏi thực sự là yêu cầu nhập tệp javascript trong chính tệp khác, không chỉ truy cập các biến của tệp bằng tệp HTML. Chẳng hạn, giả sử bạn đang tạo chức năng cho một nút và bạn sử dụng ba tệp js khác nhau, một để xử lý nhấp chuột, một cho xử lý di chuột, một cho các cuộc gọi lại cho hai người còn lại. Sẽ rất hữu ích khi nhập hai js khác trong chính js thứ ba và chỉ có một <script>thẻ. Điều này có thể giúp với tổ chức. Câu trả lời này đơn giản không phải là những gì câu hỏi được yêu cầu, và không lý tưởng trong bối cảnh này.
Tự sát

Khi tôi tìm kiếm giải pháp cho vấn đề của mình, tôi đã tìm kiếm đúng cách, tuy nhiên Google nghĩ rằng đây là trang chính xác nên tôi đã hạ cánh ở đây. Đó là lý do tại sao tôi viết câu trả lời của mình ở đây. Hình như, tôi đã làm đúng, do 15 upvote ở trên. Để không lãng phí thời gian của người khác, tôi đã bắt đầu rõ ràng về kịch bản của mình là gì - đã đặt mã HTML lên hàng đầu - có vẻ như đã được giúp đỡ, vì bạn đã trả lời & nói html không phải là kịch bản của bạn. Mong rằng được làm rõ.
Manohar Reddy Poreddy

Vâng - thực sự có vẻ như một số người đã tìm kiếm một vấn đề hơi khác, đây là một giải pháp và đã tìm thấy vấn đề này. Mặc dù đây không nhất thiết là câu trả lời cho câu hỏi này, nhưng chắc chắn nhất là câu trả lời cho câu hỏi tương tự khác. Như vậy, không có câu trả lời nào ở đây vì nó giúp những người đang tìm kiếm câu trả lời đó. Cảm ơn đã trả lời, đã làm rõ nó.
Tự sát

16

Trong trường hợp bạn đang sử dụng Web Worker và muốn bao gồm các tập lệnh bổ sung trong phạm vi của công nhân, các câu trả lời khác được cung cấp về việc thêm tập lệnh vào headthẻ, v.v. sẽ không hiệu quả với bạn.

May mắn thay, Web Worker có importScriptschức năng riêng của họ , đó là chức năng toàn cầu trong phạm vi của Web Worker, có nguồn gốc từ chính trình duyệt vì nó là một phần của đặc tả .

Ngoài ra, là câu trả lời được bình chọn cao thứ hai cho các câu hỏi nổi bật của bạn , RequireJS cũng có thể xử lý bao gồm các tập lệnh bên trong Công nhân web (có thể importScriptstự gọi , nhưng với một vài tính năng hữu ích khác).


16

Trong ngôn ngữ hiện đại với kiểm tra nếu tập lệnh đã được tải, nó sẽ là:

function loadJs(url){
  return new Promise( (resolve, reject) => {
    if (document.querySelector(`head > script[src="${src}"]`) !== null) return resolve()
    const script = document.createElement("script")
    script.src = url
    script.onload = resolve
    script.onerror = reject
    document.head.appendChild(script)
  });
}

Cách sử dụng (async / await):

try { await loadJs("https://.../script.js") } 
catch(error) {console.log(error)}

hoặc là

await loadJs("https://.../script.js").catch(err => {})

Cách sử dụng (Hứa hẹn):

loadJs("https://.../script.js").then(res => {}).catch(err => {})

Đẹp. Giải pháp tốt.
Naftali aka Neal

Súc tích và chỉ cần ES5, và thanh lịch tránh chính thức có cuộc gọi lại. Cảm ơn bạn Dmitry!
Eureka

Wow, hoạt động trong trình duyệt mà không cần máy chủ. pi.js = đơn giản var pi = 3.14. gọi hàm loadJS () qualoadJs("pi.js").then(function(){ console.log(pi); });
zipzit

14

Các @importcú pháp để đạt được CSS-như JavaScript nhập khẩu là có thể sử dụng một công cụ như Hỗn hợp đặc biệt của họ thông qua .mixloại tập tin (xem ở đây ). Tôi tưởng tượng ứng dụng chỉ đơn giản là sử dụng một trong những phương pháp đã nói ở trên, mặc dù tôi không biết.

Từ tài liệu Hỗn hợp trên .mixcác tệp:

Trộn các tệp chỉ đơn giản là các tệp .js hoặc .css với .mix. trong tên tập tin. Một tập tin hỗn hợp chỉ đơn giản là mở rộng chức năng của một tập tin kiểu hoặc tập lệnh thông thường và cho phép bạn nhập và kết hợp.

Đây là một .mixtệp ví dụ kết hợp nhiều .jstệp thành một:

// 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";

Hỗn hợp xuất ra điều này dưới dạng scripts-global.jsvà cũng là một phiên bản rút gọn ( scripts-global.min.js).

Lưu ý: Tôi không liên kết với Mixture theo bất kỳ cách nào, ngoài việc sử dụng nó làm công cụ phát triển giao diện người dùng. Tôi đã bắt gặp câu hỏi này khi thấy một .mixtệp JavaScript đang hoạt động (trong một trong các bảng mẫu hỗn hợp) và hơi bối rối với nó ("bạn có thể làm điều này không?" Tôi tự nghĩ). Sau đó, tôi nhận ra rằng đó là một loại tệp dành riêng cho ứng dụng (hơi thất vọng, đồng ý). Tuy nhiên, hình dung kiến ​​thức có thể hữu ích cho những người khác.

CẬP NHẬT : Hỗn hợp hiện miễn phí (ngoại tuyến).

CẬP NHẬT : Hỗn hợp hiện đang ngưng. Phát hành hỗn hợp cũ vẫn có sẵn


Điều này sẽ là tuyệt vời nếu nó là một mô-đun nút.
b01

@ b01 Nghe có vẻ như là một thử thách;) Giá như tôi có thời gian ... có lẽ ai đó khác?
Isaac Gregson


13

Phương pháp thông thường của tôi là:

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);
}

Nó hoạt động rất tốt và không sử dụng tải lại trang cho tôi. Tôi đã thử phương pháp AJAX (một trong những câu trả lời khác) nhưng dường như nó không hoạt động tốt với tôi.

Đây là một lời giải thích về cách mã hoạt động cho những người tò mò: về cơ bản, nó tạo ra một thẻ script mới (sau cái đầu tiên) của URL. Nó đặt nó ở chế độ không đồng bộ để nó không chặn phần còn lại của mã, nhưng gọi lại khi readyState (trạng thái của nội dung sẽ được tải) thay đổi thành 'được tải'.


13

Mặc dù những câu trả lời này rất hay, nhưng có một "giải pháp" đơn giản đã xuất hiện kể từ khi tải tập lệnh tồn tại và nó sẽ bao gồm 99,999% trong hầu hết các trường hợp sử dụng của mọi người. Chỉ cần bao gồm tập lệnh bạn cần trước tập lệnh yêu cầu. Đối với hầu hết các dự án, không mất nhiều thời gian để xác định tập lệnh nào là cần thiết và theo thứ tự nào.

<!DOCTYPE HTML>
<html>
    <head>
        <script src="script1.js"></script>
        <script src="script2.js"></script>
    </head>
    <body></body>
</html>

Nếu script2 yêu cầu script1, đây thực sự là cách dễ nhất tuyệt đối để làm một cái gì đó như thế này. Tôi rất ngạc nhiên không ai đưa ra điều này, vì đó là câu trả lời rõ ràng và đơn giản nhất sẽ được áp dụng trong gần như mọi trường hợp.


1
Tôi đoán cho nhiều người bao gồm cả bản thân mình, chúng tôi cần tên tệp động.
Stuart McIntyre

1
@StuartMcIntyre trong trường hợp đó, đặt thuộc tính src của thẻ script khi chạy và chờ sự kiện onload (tất nhiên sẽ có giải pháp tốt hơn trong trường hợp đó vào năm 2019).
KthProg

12

Tôi đã viết một mô-đun đơn giản tự động hóa công việc nhập / bao gồm các tập lệnh mô-đun trong JavaScript. Để được giải thích chi tiết về mã, hãy tham khảo bài đăng trên blog yêu cầu / nhập / bao gồm các mô-đun .

// ----- 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);
};

10

Tập lệnh này sẽ thêm một tệp JavaScript vào đầu bất kỳ <script>thẻ nào khác :

(function () {
    var li = document.createElement('script'); 
    li.type = 'text/javascript'; 
    li.src= "http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"; 
    li.async=true; 
    var s = document.getElementsByTagName('script')[0]; 
    s.parentNode.insertBefore(li, s);
})();

10

Ngoài ra còn có Head.js . Nó rất dễ dàng để đối phó với:

head.load("js/jquery.min.js",
          "js/jquery.someplugin.js",
          "js/jquery.someplugin.css", function() {
  alert("Everything is ok!");
});

Như bạn thấy, nó dễ hơn Require.js và thuận tiện như $.getScriptphương thức của jQuery . Nó cũng có một số tính năng nâng cao, như tải có điều kiện, phát hiện tính năng và nhiều hơn nữa .


10

Có rất nhiều câu trả lời tiềm năng cho câu hỏi này. Câu trả lời của tôi rõ ràng là dựa trên một số trong số họ. Đây là những gì tôi đã kết thúc sau khi đọc qua tất cả các câu trả lời.

Vấn đề với $.getScriptvà thực sự là bất kỳ giải pháp nào khác yêu cầu gọi lại khi tải xong là nếu bạn có nhiều tệp sử dụng nó và phụ thuộc vào nhau, bạn không còn cách nào để biết khi nào tất cả các tập lệnh đã được tải (một khi chúng được lồng vào nhau trong nhiều tập tin).

Thí dụ:

file3.js

var f3obj = "file3";

// Define other stuff

file2.js:

var f2obj = "file2";
$.getScript("file3.js", function(){

    alert(f3obj);

    // Use anything defined in file3.
});

file1.js:

$.getScript("file2.js", function(){
    alert(f3obj); //This will probably fail because file3 is only guaranteed to have loaded inside the callback in file2.
    alert(f2obj);

    // Use anything defined in the loaded script...
});

Bạn đã đúng khi nói rằng bạn có thể chỉ định Ajax để chạy đồng bộ hoặc sử dụng XMLHttpRequest , nhưng xu hướng hiện tại dường như là không dùng các yêu cầu đồng bộ, do đó bạn có thể không nhận được hỗ trợ trình duyệt đầy đủ ngay bây giờ hoặc trong tương lai.

Bạn có thể thử sử dụng $.whenđể kiểm tra một mảng các đối tượng bị trì hoãn, nhưng bây giờ bạn đang thực hiện việc này trong mọi tệp và tệp 2 sẽ được xem là được tải ngay khi $.whenthực thi không phải khi thực hiện cuộc gọi lại, vì vậy tệp 1 vẫn tiếp tục thực thi trước khi tệp 3 được tải . Điều này thực sự vẫn có cùng một vấn đề.

Tôi quyết định đi lùi thay vì tiến lên. Cảm ơn bạn document.writeln. Tôi biết đó là điều cấm kỵ, nhưng miễn là nó được sử dụng đúng cách thì nó hoạt động tốt. Bạn kết thúc với mã có thể được gỡ lỗi dễ dàng, hiển thị trong DOM chính xác và có thể đảm bảo thứ tự các phụ thuộc được tải chính xác.

Tất nhiên bạn có thể sử dụng $ ("body"). Append (), nhưng sau đó bạn không còn có thể gỡ lỗi chính xác nữa.

LƯU Ý: Bạn phải sử dụng điều này chỉ trong khi trang đang tải, nếu không bạn sẽ có một màn hình trống. Nói cách khác, luôn đặt cái này trước / bên ngoài document . Yet . Tôi chưa thử nghiệm bằng cách này sau khi trang được tải trong một sự kiện nhấp chuột hoặc bất cứ điều gì tương tự, nhưng tôi chắc chắn rằng nó sẽ thất bại.

Tôi thích ý tưởng mở rộng jQuery, nhưng rõ ràng là bạn không cần.

Trước khi gọi document.writeln, nó kiểm tra để đảm bảo tập lệnh chưa được tải bằng cách đánh giá tất cả các thành phần tập lệnh.

Tôi giả sử rằng một tập lệnh không được thực thi đầy đủ cho đến khi document.readysự kiện của nó được thực thi. (Tôi biết việc sử dụng document.readylà không bắt buộc, nhưng nhiều người sử dụng nó và xử lý việc này là một biện pháp bảo vệ.)

Khi các tệp bổ sung được tải, các document.readycuộc gọi lại sẽ được thực hiện theo thứ tự sai. Để giải quyết vấn đề này khi tập lệnh thực sự được tải, tập lệnh đã nhập nó được nhập lại chính nó và việc thực thi bị dừng lại. Điều này khiến cho tệp khởi tạo bây giờ được document.readythực hiện gọi lại sau bất kỳ tập lệnh nào mà nó nhập.

Thay vì cách tiếp cận này, bạn có thể cố gắng sửa đổi jQuery readyList, nhưng đây có vẻ là một giải pháp tồi tệ hơn.

Giải pháp:

$.extend(true,
{
    import_js : function(scriptpath, reAddLast)
    {
        if (typeof reAddLast === "undefined" || reAddLast === null)
        {
            reAddLast = true; // Default this value to true. It is not used by the end user, only to facilitate recursion correctly.
        }

        var found = false;
        if (reAddLast == true) // If we are re-adding the originating script we do not care if it has already been added.
        {
            found = $('script').filter(function () {
                return ($(this).attr('src') == scriptpath);
            }).length != 0; // jQuery to check if the script already exists. (replace it with straight JavaScript if you don't like jQuery.
        }

        if (found == false) {

            var callingScriptPath = $('script').last().attr("src"); // Get the script that is currently loading. Again this creates a limitation where this should not be used in a button, and only before document.ready.

            document.writeln("<script type='text/javascript' src='" + scriptpath + "'></script>"); // Add the script to the document using writeln

            if (reAddLast)
            {
                $.import_js(callingScriptPath, false); // Call itself with the originating script to fix the order.
                throw 'Readding script to correct order: ' + scriptpath + ' < ' + callingScriptPath; // This halts execution of the originating script since it is getting reloaded. If you put a try / catch around the call to $.import_js you results will vary.
            }
            return true;
        }
        return false;
    }
});

Sử dụng:

Tệp 3:

var f3obj = "file3";

// Define other stuff
$(function(){
    f3obj = "file3docready";
});

Tệp2:

$.import_js('js/file3.js');
var f2obj = "file2";
$(function(){
    f2obj = "file2docready";
});

Tệp1:

$.import_js('js/file2.js');

// Use objects from file2 or file3
alert(f3obj); // "file3"
alert(f2obj); // "file2"

$(function(){
    // Use objects from file2 or file3 some more.
    alert(f3obj); //"file3docready"
    alert(f2obj); //"file2docready"
});

Đây chính xác là những gì câu trả lời hiện được chấp nhận: chỉ là không đủ.
Tự sát

Sự khác biệt chính là nếu thiếu phần phụ thuộc, nó sẽ thêm thẻ script cho phần phụ thuộc, sau đó thêm thẻ script cho tệp gọi và đưa ra lỗi dừng thực thi. Điều này làm cho các tập lệnh được xử lý và chạy theo đúng thứ tự và loại bỏ sự cần thiết của các cuộc gọi lại. Vì vậy, tập lệnh phụ thuộc sẽ được tải và thực thi trước tập lệnh gọi trong khi hỗ trợ lồng.
xoănhairedgenius

Đối với "xu hướng hiện tại dường như là không tán thành các yêu cầu đồng bộ", điều này chỉ đúng vì nhiều nhà phát triển đã sử dụng sai chúng và khiến người dùng chờ đợi một cách không cần thiết. Tuy nhiên, nếu yêu cầu được đồng bộ một cách tự nhiên, như một câu lệnh Bao gồm, thì Ajax đồng bộ đơn giản là tốt. Tôi đã tạo một hàm mở rộng JavaScript chung để thực thi đồng bộ các chức năng như đọc một tệp không phải là một phần của JavaScript bằng cách chạy tệp "máy chủ" PHP và thấy nó rất hữu ích khi viết ứng dụng bằng JavaScript. Không có máy chủ web cục bộ nào là cần thiết cho Ajax như vậy.
David Spector

10

Có một số cách để triển khai các mô-đun trong Javascript, Dưới đây là 2 cách phổ biến nhất:

Mô-đun ES6

Các trình duyệt chưa hỗ trợ hệ thống sửa đổi này, vì vậy để bạn sử dụng cú pháp này, bạn phải sử dụng một gói như webpack. Dù sao, sử dụng một gói là tốt hơn bởi vì điều này có thể kết hợp tất cả các tệp khác nhau của bạn thành một tệp (hoặc liên quan đến cặp đôi). Điều này sẽ phục vụ các tệp từ máy chủ đến máy khách nhanh hơn vì mỗi yêu cầu HTTP có một số chi phí liên quan đi kèm với nó. Do đó, bằng cách giảm yêu cầu HTTP quá mức, chúng tôi cải thiện hiệu suất. Dưới đây là một ví dụ về các mô-đun ES6:

// main.js file

export function add (a, b) {
  return a + b;
}

export default function multiply (a, b) {
  return a * b;
}


// test.js file

import {add}, multiply from './main';   // for named exports between curly braces {export1, export2}
                                        // for default exports without {}

console.log(multiply(2, 2));  // logs 4

console.log(add(1, 2));  // logs 3

CommonJS (được sử dụng trong NodeJS)

Hệ thống điều chỉnh này được sử dụng trong NodeJS. Về cơ bản, bạn thêm xuất khẩu của mình vào một đối tượng được gọi là module.exports. Sau đó bạn có thể truy cập đối tượng này thông qua a require('modulePath'). Điều quan trọng ở đây là nhận ra rằng các mô-đun này đang được lưu trữ, vì vậy nếu bạn require()một mô-đun nhất định hai lần, nó sẽ trả về mô-đun đã tạo.

// main.js file

function add (a, b) {
  return a + b;
}

module.exports = add;  // here we add our add function to the exports object


// test.js file

const add = require('./main'); 

console.log(add(1,2));  // logs 3

9

Tôi đã đến với câu hỏi này bởi vì tôi đang tìm kiếm một cách đơn giản để duy trì một bộ sưu tập các plugin JavaScript hữu ích. Sau khi thấy một số giải pháp ở đây, tôi đã nghĩ ra điều này:

  1. Thiết lập một tệp có tên là "plugins.js" (hoặc extend.js hoặc bất cứ điều gì bạn muốn). Giữ các tệp plugin của bạn cùng với một tệp chủ đó.

  2. plugins.js sẽ có một mảng được gọi là pluginNames[]chúng tôi sẽ lặp lại each(), sau đó gắn một <script>thẻ vào phần đầu cho mỗi plugin

//set array to be updated when we add or remove plugin files
var pluginNames = ["lettering", "fittext", "butterjam", etc.];

//one script tag for each plugin
$.each(pluginNames, function(){
    $('head').append('<script src="js/plugins/' + this + '.js"></script>');
});
  1. Gọi thủ công chỉ một tệp trong đầu của bạn:
    <script src="js/plugins/plugins.js"></script>

NHƯNG:

Mặc dù tất cả các plugin được thả vào thẻ đầu theo cách chúng nên, chúng không luôn được trình duyệt chạy khi bạn nhấp vào trang hoặc làm mới.

Tôi đã thấy đáng tin cậy hơn khi chỉ viết các thẻ script trong PHP bao gồm. Bạn chỉ phải viết nó một lần và điều đó cũng tương tự như gọi plugin bằng JavaScript.


@will, giải pháp của bạn trông sạch hơn nhiều so với của tôi và tôi lo lắng tôi có thể gặp một số lỗi nếu tôi sử dụng của tôi, vì nó sử dụng .append (). Vì vậy, để sử dụng điều này, bạn chỉ có thể gọi chức năng đó một lần cho mỗi tệp plugin bạn muốn đưa vào?
rgb_life
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.