Làm thế nào để bao gồm các tập lệnh nằm trong thư mục node_modules?


275

Tôi có một câu hỏi liên quan đến thực tiễn tốt nhất để node_modulesđưa vào một trang web HTML.

Hãy tưởng tượng tôi có Bootstrap trong node_modulesthư mục của mình . Bây giờ đối với phiên bản sản xuất của trang web, làm cách nào để bao gồm tập lệnh Bootstrap và các tệp CSS nằm trong node_modulesthư mục? Liệu có ý nghĩa gì khi để Bootstrap trong thư mục đó và làm một cái gì đó như sau?

<script src="./node_modules/bootstrap/dist/bootstrap.min.js"></script>

Hoặc tôi sẽ phải thêm các quy tắc vào tệp gulp của mình để sau đó sao chép các tệp đó vào thư mục dist của tôi? Hoặc tốt nhất là để gulp bằng cách nào đó loại bỏ hoàn toàn bootstrap cục bộ khỏi tệp HTML của tôi và thay thế nó bằng phiên bản CDN?


2
chủ đề liên quan stackoverflow.com/questions/24394379/ . Vì vậy, đây là nhiệm vụ @Palak Bhansali Giả sử chỉ cần một người, liệu nó có biện minh cho việc cung cấp cho mục đích duy nhất này không, ví dụ như một ứng dụng gulp express không phải là bootstrap trực tiếp từ npm, cần truy cập vào các tệp trong gulp, hiện đang ở trong nút_modules. Điều này có biện minh cho một trường hợp sử dụng duy nhất cho việc cần cung cấp ngay bây giờ cho mục đích duy nhất này không? Đó là vấn đề tôi đang chạy vào. Tôi đã sử dụng trình soạn thảo, npm, gulp, grunt, không muốn cá nhân cung cấp và cũng không muốn lẩm bẩm trên ứng dụng này.
blamb

Câu hỏi tuyệt vời cần một giải pháp trực quan!
Sydwell

Câu trả lời:


297

Thông thường, bạn không muốn tiết lộ bất kỳ đường dẫn nội bộ nào về cách máy chủ của bạn được cấu trúc với thế giới bên ngoài. Những gì bạn có thể là tạo một /scriptstuyến tĩnh trong máy chủ của mình để tìm nạp các tệp của nó từ bất kỳ thư mục nào mà chúng xảy ra để cư trú. Vì vậy, nếu các tệp của bạn nằm trong "./node_modules/bootstrap/dist/". Sau đó, thẻ script trong các trang của bạn trông giống như thế này:

<script src="/scripts/bootstrap.min.js"></script>

Nếu bạn đang sử dụng express với nodejs, một tuyến tĩnh cũng đơn giản như sau:

app.use('/scripts', express.static(__dirname + '/node_modules/bootstrap/dist/'));

Sau đó, mọi yêu cầu trình duyệt từ /scripts/xxx.jssẽ tự động được tải từ distthư mục của bạn tại __dirname + /node_modules/bootstrap/dist/xxx.js.

Lưu ý: Các phiên bản NPM mới hơn đặt nhiều thứ hơn ở cấp cao nhất, không được lồng quá sâu, vì vậy nếu bạn đang sử dụng phiên bản NPM mới hơn, thì tên đường dẫn sẽ khác với chỉ định trong câu hỏi của OP và trong câu trả lời hiện tại. Nhưng, khái niệm vẫn giống nhau. Bạn tìm ra nơi các tập tin có thể chất nằm trên ổ đĩa máy chủ của bạn và bạn thực hiện app.use()với express.static()để thực hiện một pseudo-đường dẫn đến các tập tin, do đó bạn không được phơi bày việc tổ chức hệ thống tập tin máy chủ thực sự cho khách hàng.


Nếu bạn không muốn tạo một tuyến tĩnh như thế này, thì có lẽ tốt hơn hết bạn chỉ nên sao chép các tập lệnh công khai vào một đường dẫn mà máy chủ web của bạn coi là /scriptshoặc bất kỳ chỉ định cấp cao nhất nào bạn muốn sử dụng. Thông thường, bạn có thể biến phần sao chép này trong quá trình xây dựng / triển khai của mình.


Nếu bạn muốn chỉ công khai một tệp cụ thể trong một thư mục và không phải mọi thứ được tìm thấy trong thư mục đó, thì bạn có thể tự tạo các tuyến riêng lẻ cho mỗi tệp thay vì sử dụng express.static()như:

<script src="/bootstrap.min.js"></script>

Và mã để tạo ra một tuyến đường cho điều đó

app.get('/bootstrap.min.js', function(req, res) {
    res.sendFile(__dirname + '/node_modules/bootstrap/dist/bootstrap.min.js');
});

Hoặc, nếu bạn vẫn muốn phân định các tuyến đường cho các tập lệnh với /scripts, bạn có thể làm điều này:

<script src="/scripts/bootstrap.min.js"></script>

Và mã để tạo ra một tuyến đường cho điều đó

app.get('/scripts/bootstrap.min.js', function(req, res) {
    res.sendFile(__dirname + '/node_modules/bootstrap/dist/bootstrap.min.js');
});

2
Tôi có cần sao chép chúng theo cách thủ công không hoặc có cách nào để làm điều đó không? Tôi nghĩ bằng cách nào đó tốt nhất sẽ là sao chép hoàn toàn thư mục bootstrap vào /scriptsvì theo cách đó, mọi phụ thuộc bên trong mô-đun sẽ được duy trì.
Chris

@phpheini - có lẽ đây sẽ giúp: stackoverflow.com/questions/18966485/... và điều này npmjs.com/package/grunt-copy
jfriend00

1
@RobertOschler - Không, nó không kiểm tra dups. express.static()gửi trận đấu đầu tiên mà nó tìm thấy. Vì mỗi express.static()câu lệnh chỉ tạo một đường dẫn phù hợp có thể, nên không có sự trùng lặp từ một express.static()câu lệnh. Nếu bạn có nhiều express.static()câu lệnh mà mỗi câu lệnh có thể có một trận đấu, thì câu lệnh đầu tiên trong mã của bạn là câu lệnh có trận đấu sẽ được sử dụng. Ngoài ra, bạn thực sự không nên có nhiều tệp có cùng tên và đường dẫn tương đối có thể truy cập bằng express.static()câu lệnh. Thực hành tốt nhất là không làm điều đó.
jfriend00

1
@RobertOschler - Bạn phải rất, rất cẩn thận về những gì bạn cung cấp quyền truy cập không được kiểm soát. Nói chung, bạn chỉ nên trỏ express.static()vào một thư mục CHỈ chứa các tệp công khai (bao gồm các thư mục con). Vì vậy, tôi thực sự không thể tưởng tượng một dự án nơi có rất nhiều distthư mục được xuất bản. Điều đó có vẻ rất bất thường với tôi. Có lẽ bạn muốn tạo một tuyến đường cụ thể đến một tệp cụ thể hoặc đến 2-3 tệp. Trong trường hợp đó, bạn có trách nhiệm định hướng các tệp đó. script.jsDù sao bạn cũng sẽ không có bốn tập tin nào tốt cả.
jfriend00

1
@RobertOschler - Nếu bạn không yêu cầu một đường dẫn duy nhất ở phía trước chúng để khớp, thì không có cách nào để biết mã nào sẽ phục vụ khi /script.jstrình duyệt yêu cầu. Vì vậy, câu trả lời trừu tượng là bạn không nên cho phép tồn tại tên tệp trùng lặp vì đây không phải là vấn đề có thể giải quyết được trừ khi bạn yêu cầu một tiền tố duy nhất cho mỗi thư mục tệp. Sau đó, tên gốc trùng lặp không phải là một vấn đề. Để có câu trả lời cụ thể hơn với đề xuất chi tiết, bạn cần đăng câu hỏi của mình với chi tiết chính xác.
jfriend00

18

Tôi sẽ sử dụng mô-đun đường dẫn npm và sau đó làm một cái gì đó như thế này:

var path = require('path');
app.use('/scripts', express.static(path.join(__dirname, 'node_modules/bootstrap/dist')));

QUAN TRỌNG: chúng tôi sử dụng path.join để tạo đường dẫn tham gia bằng cách sử dụng hệ thống bất khả tri của hệ thống, tức là trên windows và unix, chúng tôi có các dấu tách đường dẫn khác nhau (/ và)


đánh dấu trùng lặp như trên, path.join chỉ là một biện pháp được thêm vào, nhưng vẫn là giải pháp tương tự bằng cách thêm một đường dẫn tĩnh vào thư mục node_modules.
blamb

2
"Path.join chỉ là một biện pháp bổ sung, nhưng vẫn là giải pháp tương tự" không phải là giải pháp tương tự. Nói một cách chính xác, nó sử dụng kết nối cụ thể của hệ thống (ghi nhớ / và \ dải phân cách trong cửa sổ và unix)
Alexander Egorov

4
OK, tôi thấy quan điểm của bạn, vâng, luôn luôn tốt khi có hệ thống bất khả tri, tôi không biết đó là cái gì. Cảm ơn đã chỉ ra rằng. Sẽ là khôn ngoan khi thêm câu đó vào câu trong câu trả lời của bạn, thay vì chỉ cung cấp mã :-), ví dụ: giải thích mã của bạn.
blamb

10

Như jfriend00 đã đề cập, bạn không nên để lộ cấu trúc máy chủ của mình. Bạn có thể sao chép các tệp phụ thuộc dự án của bạn vào một cái gì đó như public/scripts. Bạn có thể làm điều này rất dễ dàng với dep-linker như thế này:

var DepLinker = require('dep-linker');
DepLinker.copyDependenciesTo('./public/scripts')
// Done

1
Các kịch bản srcsau đó sẽ là một cái gì đó như /scripts/[package-name]/dist/file.min.js.
Jacob Ford

7

Nếu bạn muốn một giải pháp nhanh chóng và dễ dàng (và bạn đã cài đặt gulp).

Trong tôi, gulpfile.jstôi chạy một tác vụ sao chép đơn giản để đặt bất kỳ tệp nào tôi có thể cần vào ./public/modules/thư mục.

gulp.task('modules', function() {
    sources = [
      './node_modules/prismjs/prism.js',
      './node_modules/prismjs/themes/prism-dark.css',
    ]
    gulp.src( sources ).pipe(gulp.dest('./public/modules/'));
});

gulp.task('copy-modules', ['modules']);

Nhược điểm của điều này là nó không tự động. Tuy nhiên, nếu tất cả những gì bạn cần là một vài tập lệnh và kiểu được sao chép (và được giữ trong một danh sách), thì điều này sẽ thực hiện công việc.


Người ta có thể thêm điều này trong gói.json của họ trong các tập lệnh 'scripts': {'install':'yarn gulp copy-modules'}, giả sử sợi là trình quản lý gói và gulp được cài đặt trong gói.
Todd

6

Thư mục 'node_modules' có thể không có trong thư mục hiện tại, vì vậy bạn nên giải quyết đường dẫn một cách linh hoạt.

var bootstrap_dir = require.resolve('bootstrap')
                           .match(/.*\/node_modules\/[^/]+\//)[0];
app.use('/scripts', express.static(bootstrap_dir + 'dist/'));

+1, mặc dù tôi không nghĩ rằng nó sẽ hoạt động trong mọi trường hợp - mô đun được liên kết egan npm không có trong thư mục con node_modules
Alasdair McLeay

3

Tôi muốn cập nhật câu hỏi này với một giải pháp dễ dàng hơn. Tạo một liên kết tượng trưng đến node_modules.

Cách dễ nhất để cấp quyền truy cập công khai cho node_modules là tạo một liên kết tượng trưng chỉ đến node_modules từ trong thư mục công cộng của bạn. Symlink sẽ làm cho nó như thể các tập tin tồn tại bất cứ nơi nào liên kết được tạo ra.

Ví dụ: nếu máy chủ nút có mã để phục vụ các tệp tĩnh

app.use(serveStatic(path.join(__dirname, 'dist')));

và __dirname đề cập đến / path / to / app để các tệp tĩnh của bạn được cung cấp từ / path / to / app / dist

và node_modules nằm ở / path / to / app / node_modules, sau đó tạo một liên kết tượng trưng như thế này trên mac / linux:

ln -s /path/to/app/node_modules /path/to/app/dist/node_modules

hoặc như thế này trên windows:

mklink /path/to/app/node_modules /path/to/app/dist/node_modules

Bây giờ nhận được yêu cầu cho:

node_modules/some/path 

sẽ nhận được phản hồi với tệp tại

/path/to/app/dist/node_modules/some/path 

đó thực sự là tập tin tại

/path/to/app/node_modules/some/path

Nếu thư mục của bạn tại / path / to / app / dist không phải là một vị trí an toàn, có lẽ do sự can thiệp từ quá trình xây dựng với gulp hoặc grunt, thì bạn có thể thêm một thư mục riêng cho liên kết và thêm một lệnh gọi ServStatic mới, chẳng hạn như:

ln -s /path/to/app/node_modules /path/to/app/newDirectoryName/node_modules

và trong nút thêm:

app.use(serveStatic(path.join(__dirname, 'newDirectoryName')));

1
Nhưng sau đó, bạn đã chuyển ứng dụng của mình sang một đường dẫn khác và symlink không hợp lệ. Hoặc bạn muốn chạy ứng dụng của mình trên một máy khác (test / prod), bạn sẽ cần tạo lại ứng dụng đó. Bất kỳ người đóng góp cho ứng dụng của bạn phải làm như vậy. Nó thêm một số bảo trì trên các giải pháp trên.
mati.o

@ mati.o Còn liên kết tượng trưng tương đối thì sao? Tôi thích giải pháp này, nhưng chỉ với các liên kết tương đối.
Lukáš Bednařík

3

Tôi đã không tìm thấy bất kỳ giải pháp sạch nào (tôi không muốn tiết lộ nguồn của tất cả các nút_modules của mình) vì vậy tôi chỉ viết một tập lệnh Powershell để sao chép chúng:

$deps = "leaflet", "leaflet-search", "material-components-web"

foreach ($dep in $deps) {
    Copy-Item "node_modules/$dep/dist" "static/$dep" -Recurse
}

1

Tôi đã thực hiện các thay đổi dưới đây để TỰ ĐỘNG-BAO GỒM các tệp trong chỉ mục html. Vì vậy, khi bạn thêm một tệp vào thư mục, nó sẽ tự động được chọn từ thư mục mà không cần bạn phải đưa tệp vào index.html

//// THIS WORKS FOR ME 
///// in app.js or server.js

var app = express();

app.use("/", express.static(__dirname));
var fs = require("fs"),

function getFiles (dir, files_){
    files_ = files_ || [];
    var files = fs.readdirSync(dir);
    for (var i in files){
        var name = dir + '/' + files[i];
        if (fs.statSync(name).isDirectory()){
            getFiles(name, files_);
        } else {
            files_.push(name);
        }
    }
    return files_;
}
//// send the files in js folder as variable/array 
ejs = require('ejs');

res.render('index', {
    'something':'something'...........
    jsfiles: jsfiles,
});

///--------------------------------------------------

///////// in views/index.ejs --- the below code will list the files in index.ejs

<% for(var i=0; i < jsfiles.length; i++) { %>
   <script src="<%= jsfiles[i] %>"></script>
<% } %>

1

Đây là những gì tôi đã thiết lập trên expressmáy chủ của mình :

// app.js
const path = require('path');
const express = require('express');
const expressApp  = express();
const nm_dependencies = ['bootstrap', 'jquery', 'popper.js']; // keep adding required node_modules to this array.
nm_dependencies.forEach(dep => {
  expressApp.use(`/${dep}`, express.static(path.resolve(`node_modules/${dep}`)));
});

<!-- somewhere inside head tag -->
<link rel="stylesheet" href="bootstrap/dist/css/bootstrap.css" />

<!-- somewhere near ending body tag -->
<script src="jquery/dist/jquery.js" charset="utf-8"></script>
<script src="popper.js/dist/popper.js" charset="utf-8"></script>
<script src="bootstrap/dist/js/bootstrap.js" charset="utf-8"></script>

Chúc may mắn...

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.