Sự khác biệt giữa Require.js và chỉ cần tạo phần tử <script> trong DOM là gì? [đóng cửa]


138

Sự khác biệt giữa việc sử dụng Require.JS amd chỉ đơn giản là tạo một <script>phần tử trong DOM?

Sự hiểu biết của tôi về Require.JS là nó cung cấp khả năng tải các phụ thuộc, nhưng điều này có thể không đơn giản được thực hiện bằng cách tạo một <script>phần tử tải tệp JS bên ngoài cần thiết không?

Ví dụ: giả sử tôi có hàm doStuff(), yêu cầu hàm needMe(). doStuff()là trong tệp bên ngoài do_stuff.js, trong khi needMe()ở tệp bên ngoài need_me.js.

Làm điều này theo cách Require.JS:

define(['need_me'],function(){
    function doStuff(){
        //do some stuff
        needMe();
        //do some more stuff
    }
});

Làm điều này bằng cách đơn giản là tạo một phần tử script:

function doStuff(){
    var scriptElement  = document.createElement('script');
    scriptElement.src = 'need_me.js';
    scriptElement.type = 'text/javascript';
    document.getElementsByTagName('head')[0].appendChild(scriptElement);

    //do some stuff
    needMe();
    //do some more stuff
}

Cả hai công việc này. Tuy nhiên, phiên bản thứ hai không yêu cầu tôi tải tất cả thư viện Require.js. Tôi thực sự không thấy bất kỳ sự khác biệt chức năng ...


1
Điều gì về bộ nhớ đệm trình duyệt, có yêu cầu can thiệp vào nó?
Muhammad Umer

Tôi đang mở lại điều này bởi vì nó yêu cầu sự khác biệt giữa hai điều rất giống nhau. Nó có thể được trả lời một cách khách quan, và tôi không thấy ý kiến ​​liên quan đến nó ở đâu.
RamenChef

Câu trả lời:


43

Đây là bài viết hay trên ajaxian.com về lý do tại sao sử dụng nó:

RequireJS: Tải JavaScript không đồng bộ

  • một số loại # bao gồm / nhập / yêu cầu
  • khả năng tải phụ thuộc lồng nhau
  • dễ sử dụng cho nhà phát triển nhưng sau đó được hỗ trợ bởi một công cụ tối ưu hóa giúp triển khai

2
Tôi đã đọc những thứ đó, nhưng bây giờ tôi nghĩ về nó nhiều hơn tôi nhận ra rằng ý tưởng về các phụ thuộc lồng nhau có thể đạt được bằng cách viết các thẻ <script>. Cảm ơn.
maxedison

37
"Dễ sử dụng cho nhà phát triển" không thể xa hơn sự thật. Nó chắc chắn có một đường cong học tập dốc cho bạn và bất cứ ai khác sẽ đến làm việc trong dự án đó.
Sahat Yalkabov

3
@TwilightPony Tôi cho rằng bản thân mình không sáng sủa và đòi hỏi tôi không thực sự là một điều khó khăn. Nó loại bỏ bạn phải lo lắng về sự phụ thuộc và tăng tốc trang. Mã của bạn trở nên nội tuyến hơn với lập trình phía máy chủ trong cách bạn khai báo các phụ thuộc mà cá nhân tôi thấy mới mẻ và đơn giản. Cú pháp là tối thiểu và được đóng bởi thiết kế sau đó đặt lộ trình cho sản xuất để dễ dàng kết hợp các tập lệnh của bạn. Trên đầu gỡ lỗi đó giống như khai báo tĩnh. Không chắc chắn những gì dễ dàng hơn thế. Cách khác khó hơn nhiều như tôi đã làm theo cách khác.
Jason Sebring

Tôi đang đấu tranh. Đặc biệt với các mô-đun cố gắng tự gắn vào các đối tượng toàn cầu. (Mô-đun phản ứng) ...
geilt 3/03/2015

1
Các bình luận trên trang đó thực sự khiến tôi cảm thấy rằng một người nên chạy trốn và không hướng tới yêu cầu. Đặc biệt là cái gần phía dưới liên kết đến stevesouder.com/tests/require.php
Dave Kanter

52

Require.JS cung cấp những lợi thế gì so với việc tạo một phần tử trong DOM?

Trong ví dụ của bạn, bạn đang tạo thẻ script không đồng bộ, điều đó có nghĩa là needMe()chức năng của bạn sẽ được gọi trước khi tệp Need_me.js hoàn tất tải. Điều này dẫn đến các ngoại lệ chưa được phát hiện trong đó chức năng của bạn không được xác định.

Thay vào đó, để làm cho những gì bạn đề xuất thực sự hoạt động, bạn cần phải làm một cái gì đó như thế này:

function doStuff(){
    var scriptElement  = document.createElement('script');
    scriptElement.src = 'need_me.js';
    scriptElement.type = 'text/javascript';

    scriptElement.addEventListener("load", 
        function() { 
            console.log("script loaded - now it's safe to use it!");

            // do some stuff
            needMe();
            //do some more stuff

        }, false);

    document.getElementsByTagName('head')[0].appendChild(scriptElement);

}

Có thể cho rằng, có thể tốt nhất hoặc không nên sử dụng trình quản lý gói như RequireJS hoặc sử dụng chiến lược thuần JavaScript như đã trình bày ở trên. Mặc dù ứng dụng Web của bạn có thể tải nhanh hơn, nhưng việc gọi chức năng và tính năng trên trang web sẽ chậm hơn vì nó liên quan đến việc chờ tài nguyên tải trước khi hành động đó có thể được thực hiện.

Nếu một ứng dụng Web được xây dựng dưới dạng một ứng dụng một trang, thì hãy xem xét rằng mọi người sẽ không thực sự tải lại trang rất thường xuyên. Trong những trường hợp này, tải trước mọi thứ sẽ giúp trải nghiệm dường như nhanh hơn khi thực sự sử dụng ứng dụng. Trong những trường hợp này, bạn đúng, người ta chỉ có thể tải tất cả các tài nguyên chỉ bằng cách đưa các thẻ script vào phần đầu hoặc phần thân của trang.

Tuy nhiên, nếu xây dựng một trang web hoặc một ứng dụng Web theo mô hình truyền thống hơn, nơi một chuyển đổi từ trang này sang trang khác, khiến tài nguyên được tải lại, một cách tiếp cận tải lười biếng có thể giúp tăng tốc các chuyển đổi này.


10

Một số lý do rất quan trọng khác tại sao sử dụng RequireJS có ý nghĩa:

  1. Quản lý các phụ thuộc của riêng bạn nhanh chóng sụp đổ cho các dự án lớn.
  2. Bạn có thể có nhiều tệp nhỏ như bạn muốn và không phải lo lắng về việc theo dõi các phụ thuộc hoặc thứ tự tải.
  3. RequireJS cho phép viết toàn bộ ứng dụng mô-đun mà không cần chạm vào đối tượng cửa sổ.

Lấy từ ý kiến ​​của rmurphey ở đây trong Gist này .

Các lớp trừu tượng có thể là một cơn ác mộng để học và điều chỉnh, nhưng khi nó phục vụ một mục đích và thực hiện tốt, nó chỉ có ý nghĩa.


9
Bạn vẫn phải quản lý tất cả các yêu cầu và xác định các câu lệnh, tệp cấu hình, xung đột với các hệ thống và thư viện khác chưa triển khai đặc tả AMD, v.v. Tôi đã thử sử dụng Require.JS trong dự án nút-webkit và Require.JS đã chiến đấu với tôi từng bước một ... Ngược lại, chỉ đơn giản là sắp xếp các tập lệnh theo một cách nhất định ... Tất nhiên, bạn trở nên lười biếng với Require.JS, đó là lý do tại sao tôi đã thử làm cho nó hoạt động. :)
jmort253

Tôi hoàn toàn đồng ý với @ jmort253, đó là một cuộc đấu tranh lúc ban đầu, nhưng bây giờ tôi rất thích nó. Cả ba điểm đều đúng! Và AMDifying một thư viện không nên quá khó khăn ... hoặc sử dụng shim.
Huyền thoại

0

Đây là một ví dụ cụ thể hơn.

Tôi đang làm việc trong một dự án với 60 tệp. Chúng tôi có 2 chế độ khác nhau để chạy nó.

  1. Tải một phiên bản nối, 1 tệp lớn. (Sản xuất)

  2. Tải tất cả 60 tệp (phát triển)

Chúng tôi đang sử dụng trình tải để chúng tôi có một tập lệnh trong trang web

<script src="loader.js"></script>

Mặc định đó là chế độ # 1 (tải một tệp được nối lớn). Để chạy ở chế độ # 2 (các tệp riêng biệt), chúng tôi đặt một số cờ. Nó có thể là bất cứ điều gì. Một khóa trong chuỗi truy vấn. Trong ví dụ này, chúng tôi chỉ làm điều này

<script>useDebugVersion = true;</script>
<script src="loader.js"></script>

loader.js trông giống như thế này

if (useDebugVersion) {
   injectScript("app.js");
   injectScript("somelib.js");
   injectScript("someotherlib.js");
   injectScript("anotherlib.js");
   ... repeat for 60 files ...
} else {
   injectScript("large-concatinated.js");
}

Tập lệnh xây dựng chỉ là một tệp .sh trông như thế này

cat > large-concantinated.js app.js somelib.js someotherlib.js anotherlib.js

Vân vân...

Nếu một tệp mới được thêm vào, chúng tôi có thể sẽ sử dụng chế độ # 2 vì chúng tôi đang phát triển, chúng tôi phải thêm một injectScript("somenewfile.js") dòng vào loader.js

Sau đó, để sản xuất, chúng tôi cũng phải thêm somenewfile.js vào tập lệnh xây dựng của chúng tôi. Một bước chúng ta thường quên và sau đó nhận được thông báo lỗi.

Bằng cách chuyển sang AMD, chúng tôi không phải chỉnh sửa 2 tệp. Vấn đề giữ loader.js và tập lệnh xây dựng đồng bộ biến mất. Sử dụng r.jshoặcwebpack nó chỉ có thể đọc mã để xây dựnglarge-concantinated.js

Nó cũng có thể xử lý các phụ thuộc, ví dụ: chúng tôi có 2 tệp lib1.js và lib2.js được tải như thế này

injectScript("lib1.js");
injectScript("lib2.js");

lib2 cần lib1. Nó có mã bên trong mà làm một cái gì đó như

lib1Api.installPlugin(...);

Nhưng vì các tập lệnh được chèn được tải không đồng bộ, không có gì đảm bảo chúng sẽ tải theo đúng thứ tự. 2 tập lệnh này không phải là tập lệnh AMD nhưng sử dụng các tệp.j.j chúng ta có thể nói với nó các phụ thuộc của chúng

require.config({
    paths: {
        lib1: './path/to/lib1',
        lib2: './path/to/lib2',
    },
    shim: {
        lib1: {
            "exports": 'lib1Api',
        },
        lib2: {
            "deps": ["lib1"],
        },
    }
});

Tôi mô-đun của chúng tôi sử dụng lib1 chúng tôi làm điều này

define(['lib1'], function(lib1Api) {
   lib1Api.doSomething(...);
});

Bây giờ allow.js sẽ tiêm các tập lệnh cho chúng tôi và nó sẽ không tiêm lib2 cho đến khi lib1 được tải vì chúng tôi đã nói với nó lib2 phụ thuộc vào lib1. Nó cũng sẽ không khởi động mô-đun của chúng tôi sử dụng lib1 cho đến khi cả lib2 và lib1 được tải.

Điều này làm cho sự phát triển trở nên tốt đẹp (không có bước xây dựng, không phải lo lắng về việc tải đơn hàng) và nó làm cho việc sản xuất trở nên tốt đẹp (không cần cập nhật tập lệnh xây dựng cho mỗi tập lệnh được thêm vào).

Là một phần thưởng bổ sung, chúng tôi có thể sử dụng plugin babel của webpack để chạy babel qua mã cho các trình duyệt cũ hơn và một lần nữa chúng tôi không phải duy trì tập lệnh xây dựng đó.

Lưu ý rằng nếu Chrome (trình duyệt lựa chọn của chúng tôi) bắt đầu hỗ trợ import thực sự thì có lẽ chúng tôi sẽ chuyển sang phát triển nhưng điều đó sẽ không thực sự thay đổi bất cứ điều gì. Chúng tôi vẫn có thể sử dụng gói web để tạo một tệp được nối và chúng tôi có thể sử dụng nó để chạy babel qua mã cho tất cả các trình duyệt.

Tất cả điều này có được bằng cách không sử dụng thẻ script và sử dụng AMD

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.