Yêu cầu kiểu nút cho javascript trong trình duyệt?


83

Có thư viện nào dành cho javascript trong trình duyệt cung cấp tính linh hoạt / mô-đun / dễ sử dụng giống như của Node requirekhông?

Để cung cấp thêm chi tiết: lý do requirerất tốt là nó:

  1. Cho phép mã được tải động từ các vị trí khác (theo ý kiến ​​của tôi thì tốt hơn về mặt phong cách so với việc liên kết tất cả mã của bạn trong HTML)
  2. Nó cung cấp một giao diện nhất quán để xây dựng các mô-đun
  3. Rất dễ để các mô-đun phụ thuộc vào các mô-đun khác (ví dụ: vì vậy tôi có thể viết một API yêu cầu jQuery để tôi có thể sử dụng jQuery.ajax()
  4. Javascript đã tải có phạm vi , có nghĩa là tôi có thể tải var dsp = require("dsp.js");và tôi có thể truy cập dsp.FFT, điều này sẽ không ảnh hưởng đến cục bộ của tôivar FFT

Tôi vẫn chưa tìm thấy một thư viện làm điều này một cách hiệu quả. Các giải pháp thay thế tôi có xu hướng sử dụng là:

  • coffeescript-concat - đủ dễ dàng để yêu cầu các j khác, nhưng bạn phải biên dịch nó, có nghĩa là nó kém tuyệt vời hơn để phát triển nhanh (ví dụ: xây dựng các API trong thử nghiệm)

  • RequestJS - Nó phổ biến, đơn giản và giải quyết được 1-3, nhưng thiếu phạm vi là một sự phá vỡ thỏa thuận thực sự (tôi tin rằng head.js tương tự ở chỗ nó thiếu phạm vi, mặc dù tôi chưa bao giờ có dịp sử dụng nó. Tương tự như vậy, LABj có thể tải và .wait()thực hiện các vấn đề phụ thuộc một cách đơn giản, nhưng nó vẫn không thực hiện phạm vi)

Theo như tôi có thể nói, dường như có nhiều giải pháp để tải javascript động và / hoặc không đồng bộ, nhưng chúng có xu hướng gặp phải các vấn đề về phạm vi giống như chỉ tải j từ HTML. Hơn bất cứ điều gì khác, tôi muốn một cách để tải javascript hoàn toàn không gây ô nhiễm không gian tên chung, nhưng vẫn cho phép tôi tải và sử dụng các thư viện (giống như yêu cầu của nút).

CẬP NHẬT NĂM 2020: Các mô-đun hiện là tiêu chuẩn trong ES6 và kể từ giữa năm 2020 được hầu hết các trình duyệt hỗ trợ . Mô-đun hỗ trợ cả tải đồng bộ và không đồng bộ (sử dụng Promise). Khuyến nghị hiện tại của tôi là hầu hết các dự án mới nên sử dụng mô-đun ES6 và sử dụng trình chuyển tiếp để chuyển về một tệp JS duy nhất cho các trình duyệt cũ.

Theo nguyên tắc chung, băng thông ngày nay cũng thường rộng hơn nhiều so với khi tôi hỏi câu hỏi này ban đầu. Vì vậy, trong thực tế, bạn có thể chọn một cách hợp lý để luôn sử dụng một bộ chuyển tiếp với các mô-đun ES6 và tập trung nỗ lực của bạn vào hiệu quả mã hơn là mạng.

CHỈNH SỬA TRƯỚC (hoặc nếu bạn không thích mô-đun ES6): Kể từ khi viết bài này, tôi đã sử dụng rộng rãi RequestJS (hiện đã có tài liệu rõ ràng hơn nhiều). Yêu cầuJS thực sự là sự lựa chọn đúng đắn theo ý kiến ​​của tôi. Tôi muốn làm rõ cách thức hoạt động của hệ thống đối với những người đang bối rối như tôi:

Bạn có thể sử dụng requiretrong phát triển hàng ngày. Một mô-đun có thể là bất kỳ thứ gì được trả về bởi một hàm (thường là một đối tượng hoặc một hàm) và được xác định phạm vi như một tham số. Bạn cũng có thể biên dịch dự án của mình thành một tệp duy nhất để triển khai bằng cách sử dụng r.js(trong thực tế, điều này hầu như luôn nhanh hơn, mặc dù requirecó thể tải các tập lệnh song song).

Sự khác biệt cơ bản giữa RequestJS và yêu cầu kiểu nút như Browserify (một dự án thú vị do tjameson đề xuất) sử dụng là cách các mô-đun được thiết kế và yêu cầu:

  • RequestJS sử dụng AMD (Định nghĩa mô-đun không đồng bộ). Trong AMD, requirelấy một danh sách các mô-đun (tệp javascript) để tải và một hàm gọi lại. Khi nó đã tải từng mô-đun, nó sẽ gọi lệnh gọi lại với mỗi mô-đun như một tham số cho lệnh gọi lại. Do đó, nó thực sự không đồng bộ và do đó rất phù hợp với web.
  • Node sử dụng CommonJS. Trong CommonJS, requirelà một lệnh gọi chặn tải một mô-đun và trả về nó dưới dạng một đối tượng. Điều này hoạt động tốt cho Node vì các tệp được đọc khỏi hệ thống tệp, hệ thống này đủ nhanh, nhưng hoạt động kém trên web vì tải tệp đồng bộ có thể mất nhiều thời gian hơn.

Trong thực tế, nhiều nhà phát triển đã sử dụng Node (và do đó là CommonJS) trước khi họ thấy AMD. Ngoài ra, nhiều thư viện / mô-đun được viết cho CommonJS (bằng cách thêm mọi thứ vào một exportsđối tượng) chứ không phải cho AMD (bằng cách trả lại mô-đun từ definehàm). Do đó, rất nhiều nhà phát triển Node-quay-web muốn sử dụng thư viện CommonJS trên web. Điều này có thể thực hiện được vì việc tải từ <script>thẻ đang bị chặn. Các giải pháp như Browserify lấy các mô-đun CommonJS (Node) và gói chúng lại để bạn có thể bao gồm chúng bằng các thẻ script.

Do đó, nếu bạn đang phát triển dự án nhiều tệp của riêng mình cho web, tôi thực sự khuyên bạn nên dùng RequestJS, vì nó thực sự là một hệ thống mô-đun cho web (mặc dù công bằng mà nói, tôi thấy AMD tự nhiên hơn CommonJS nhiều). Gần đây, sự khác biệt đã trở nên ít quan trọng hơn, vì bây giờ RequestJS cho phép bạn sử dụng cú pháp CommonJS về cơ bản. Ngoài ra, RequestJS có thể được sử dụng để tải các mô-đun AMD trong Node (mặc dù tôi thích node-amd-loader hơn ).


1
Lưu ý RequestJS thực sự hỗ trợ mô-đun và có thể mở rộng phạm vi. Kể từ khi hỏi, tôi đã sử dụng nó rộng rãi hơn một chút. Theo ý kiến ​​của tôi, nó được đóng gói đầy đủ chức năng nhưng cần đọc nhiều tài liệu để sử dụng hiệu quả và nó cần một số hình thức tải đồng bộ lớp đầu tiên trước khi hoàn hảo.
Alex Churchill

1
Sự phân biệt của bất đồng bộ có ý nghĩa không? bất cứ khi nào tôi yêu cầu mã, về cơ bản tôi bị chặn không thể tiếp tục vì nó xác định các chức năng và như vậy tôi cần phải làm bất cứ điều gì ...
Michael

Câu trả lời:


17

Kiểm tra ender . Nó làm được rất nhiều điều này.

Ngoài ra, Browserify cũng khá tốt. Tôi đã sử dụng request-kiss ¹ và nó hoạt động. Có lẽ là những người khác.

Tôi không chắc về RequestJS. Nó không giống như của nút. Bạn có thể gặp sự cố khi tải từ các vị trí khác, nhưng nó có thể hoạt động. Miễn là có một phương thức cung cấp hoặc một cái gì đó có thể được gọi.

TL; DR - Tôi khuyên bạn nên duyệt qua hoặc yêu cầu hôn.


Cập nhật:

1: request-kiss hiện đã chết và tác giả đã gỡ bỏ. Tôi đã sử dụng RequestJS mà không gặp vấn đề gì. Tác giả của Requi -kiss đã viết pakmanagerpakman . Tiết lộ đầy đủ, tôi làm việc với nhà phát triển.

Cá nhân tôi thích RequiJS hơn. Việc gỡ lỗi dễ dàng hơn nhiều (bạn có thể có các tệp riêng biệt đang phát triển và một tệp được triển khai duy nhất trong phiên bản sản xuất) và được xây dựng trên một "tiêu chuẩn" vững chắc.


Tuyệt, tôi chưa thử duyệt qua, nhưng có vẻ như chính xác những gì tôi cần.
Alex Churchill

Liên kết đến yêu cầu nụ hôn dường như đã chết. Tìm kiếm đơn giản (lại) không dẫn đến đâu - nó đã đi đâu?
Joel Purra

@JoelPurra - request-kiss đã bị xóa và được thay thế bằng pakmanager. Tôi khuyên bạn nên request-js ngay bây giờ. Tôi đã cập nhật câu trả lời.
beatgammit

câu trả lời hay ở đây anh bạn :), nhớ kiểm tra câu hỏi tôi vừa làm tương tự như câu hỏi này (nhưng khác cùng một lúc)? stackoverflow.com/questions/43237875/…
Webeng

16

Tôi đã viết một tập lệnh nhỏ cho phép tải không đồng bộ và đồng bộ các tệp Javascript, có thể hữu ích ở đây. Nó không có phụ thuộc và tương thích với Node.js & CommonJS. Việc cài đặt khá dễ dàng:

$ npm install --save @tarp/require

Sau đó, chỉ cần thêm các dòng sau vào HTML của bạn để tải mô-đun chính:

<script src="/node_modules/@tarp/require/require.min.js"></script>
<script>Tarp.require({main: "./scripts/main"});</script>

Bên trong mô-đun chính của bạn (tất nhiên và bất kỳ mô-đun phụ nào), bạn có thể sử dụng nó require()như bạn đã biết từ CommonJS / NodeJS. Bạn có thể tìm thấy toàn bộ tài liệu và mã trên GitHub .


1
Điều này thật tuyệt vời. Cảm ơn bạn đã viết bài này!
OldTimeGuitarGuy

Cảm ơn bạn, @OldTimeGuitarGuy :)
Torben

10

Một biến thể của câu trả lời tuyệt vời Ilya Kharlamov , với một số mã để làm cho nó hoạt động tốt với các công cụ dành cho nhà phát triển chrome.

//
///- REQUIRE FN
// equivalent to require from node.js
function require(url){
    if (url.toLowerCase().substr(-3)!=='.js') url+='.js'; // to allow loading without js suffix;
    if (!require.cache) require.cache=[]; //init cache
    var exports=require.cache[url]; //get from cache
    if (!exports) { //not cached
            try {
                exports={};
                var X=new XMLHttpRequest();
                X.open("GET", url, 0); // sync
                X.send();
                if (X.status && X.status !== 200)  throw new Error(X.statusText);
                var source = X.responseText;
                // fix (if saved form for Chrome Dev Tools)
                if (source.substr(0,10)==="(function("){ 
                    var moduleStart = source.indexOf('{');
                    var moduleEnd = source.lastIndexOf('})');
                    var CDTcomment = source.indexOf('//@ ');
                    if (CDTcomment>-1 && CDTcomment<moduleStart+6) moduleStart = source.indexOf('\n',CDTcomment);
                    source = source.slice(moduleStart+1,moduleEnd-1); 
                } 
                // fix, add comment to show source on Chrome Dev Tools
                source="//@ sourceURL="+window.location.origin+url+"\n" + source;
                //------
                var module = { id: url, uri: url, exports:exports }; //according to node.js modules 
                var anonFn = new Function("require", "exports", "module", source); //create a Fn with module code, and 3 params: require, exports & module
                anonFn(require, exports, module); // call the Fn, Execute the module
                require.cache[url]  = exports = module.exports; //cache obj exported by module
            } catch (err) {
                throw new Error("Error loading module "+url+": "+err);
            }
    }
    return exports; //require returns object exported by module
}
///- END REQUIRE FN

Cảm ơn Lucio! Tôi đã tìm kiếm một giải pháp tối thiểu như thế này trong một thời gian dài. Tôi đã mở rộng nó để hỗ trợ các đường dẫn tương đối. Xem bên dưới.
Trausti Kristjansson

8

Tôi nhận ra có thể có những người mới bắt đầu đang tìm cách sắp xếp mã của họ. Đây là năm 2020 và nếu bạn đang xem xét một ứng dụng JS mô-đun, bạn nên bắt đầu với npmWebpack ngay bây giờ.

Dưới đây là một số bước đơn giản để bắt đầu:

  1. Trong gốc dự án của bạn, hãy chạy npm init -yđể khởi tạo một dự án npm
  2. Tải xuống gói mô-đun Webpack: npm install webpack webpack-cli
  3. Tạo tệp index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>App</title>
</head>
<body>

    <script src="_bundle.js"></script>
</body>
</html>

Đặc biệt chú ý đến _bundle.jstệp - đây sẽ là tệp JS cuối cùng được tạo bởi webpack, bạn sẽ không sửa đổi trực tiếp (tiếp tục đọc).

  1. Tạo một <project-root>/app.jstrong đó bạn sẽ nhập các mô-đun khác:
const printHello = require('./print-hello');

printHello();
  1. Tạo một print-hello.jsmô-đun mẫu :
module.exports = function() {
    console.log('Hello World!');
}
  1. Tạo <project-root>/webpack.config.jsvà sao chép-dán nội dung sau:
var path = require('path');

module.exports = {
  entry: './app.js',
  output: {
    path: path.resolve(__dirname),
    filename: '_bundle.js'
  }
};

Trong đoạn mã trên, có 2 điểm:

  • mục nhập app.jslà nơi bạn sẽ viết mã JS của mình. Nó sẽ nhập các mô-đun khác như hình trên.
  • đầu ra _bundle.jslà gói cuối cùng của bạn được tạo bởi webpack. Đây là những gì html của bạn sẽ thấy ở cuối.

-7. Mở của bạn package.jsvà thay thế scriptsbằng lệnh sau:

  "scripts": {
    "start": "webpack --mode production -w"
  },
  1. Và cuối cùng chạy kịch bản đồng hồ app.jsvà tạo ra các _bundle.jstập tin bằng cách chạy: npm start.
  2. Thưởng thức mã hóa!

1
vào năm 2020, điều này sẽ được đánh dấu là câu trả lời đúng
13rnd

5
(function () {
    // c is cache, the rest are the constants
    var c = {},s="status",t="Text",e="exports",E="Error",r="require",m="module",S=" ",w=window;
    w[r]=function R(url) {
        url+=/.js$/i.test(url) ? "" : ".js";// to allow loading without js suffix;
        var X=new XMLHttpRequest(),module = { id: url, uri: url }; //according to the modules 1.1 standard
        if (!c[url])
            try {
                X.open("GET", url, 0); // sync
                X.send();
                if (X[s] && X[s] != 200) 
                    throw X[s+t];
                Function(r, e, m, X['response'+t])(R, c[url]={}, module); // Execute the module
                module[e] && (c[url]=module[e]);
            } catch (x) {
                throw w[E](E+" in "+r+": Can't load "+m+S+url+":"+S+x);
            }
        return c[url];
    }
})();

Tốt hơn là không được sử dụng trong sản xuất vì bị chặn. (Trong node.js, request () là một lệnh gọi chặn cũng được).


không nên là "export: {}" thuộc tính của "module"? và cuộc gọi được (R, module.exports, mô-đun)
Lucio M. Tato

@ LucioM.Tato Tôi không chắc, không thể thấy bất kỳ đề cập nào về module.exports trong tiêu chuẩn Mô-đun 1.1 . Bạn luôn có thể gọi yêu cầu (module.id) để có được xuất khẩu
Ilya Kharlamov

Đúng. Bạn nói đúng, tôi đã nghĩ đến việc triển khai mô-đun node.js. Tôi đã thêm một số sửa đổi đối với mã trong câu trả lời thực sự hay của bạn, để làm cho nó hoạt động tốt với Chrome Dev Tools (Tôi đang sử dụng nó làm IDE thời gian gỡ lỗi). Tôi sẽ đăng mã như một câu trả lời khác cho câu hỏi này, trong trường hợp hữu ích cho người khác.
Lucio M. Tato


1

Yêu cầu sơ khai - cung cấp tính năng tuân thủ nút requiretrong trình duyệt, giải quyết cả mô-đun và đường dẫn tương đối. Sử dụng kỹ thuật tương tự như TKRequire (XMLHttpRequest). Mã kết quả có thể duyệt hoàn toàn, trong đó require-stubcó thể dùng để thay thế cho watchify.


0

Đây là phần mở rộng cho câu trả lời tuyệt vời của Lucio M. Tato cho phép tải đệ quy các mô-đun với các đường dẫn tương đối.

Đây là một dự án github để chứa giải pháp và một ví dụ về cách sử dụng nó:

https://github.com/trausti/TKRequire.js

Để sử dụng TKRequire.js, hãy đưa dòng sau vào tiêu đề của bạn

<script type = "text / javascript" src = "./ TKRequire.js"> </script>

Sau đó, tải các mô-đun giống như trong node.js:

var MyModule = request ("./ relative / path / to / MyModule.js");


Cảm ơn Trausti. Nếu đang sử dụng javascript, bạn nên kiểm tra github.com/luciotato/LiteScript (beta). PD: Bạn đang chơi CC Saga ở cấp độ nào? : P
Lucio M. Tato

liên kết đã chết, làm thế nào chúng tôi có thể lấy và xem mã của bạn?
PA.
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.