Як включити скрипти, розташовані всередині папки node_modules?


275

У мене є питання щодо найкращої практики щодо включення node_modulesна веб-сайт HTML.

Уявіть, що у мене в node_modulesпапці є Bootstrap . Тепер для виробничої версії веб-сайту, як би я включив скрипт Bootstrap і CSS-файли, розташовані всередині node_modulesпапки? Чи є сенс залишити Bootstrap всередині цієї папки і зробити щось на зразок наступного?

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

Або мені доведеться додати правила до свого файлу gulp, який потім копіює ці файли в мою папку dist? Або найкраще дозволити gulp якось повністю видалити локальний завантажувальний файл з мого HTML-файлу та замінити його версією CDN?


2
пов'язана з темою stackoverflow.com/questions/24397379 / ... . Отож ось questino @Palak Bhansali Припускаючи лише необхідність, чи це виправдовує реалізацію bower для цієї єдиної мети, скажімо, наприклад, gulp express додаток, що виконує завантаження завантажувального пристрою безпосередньо з npm, потребуючи доступу до файлів у gulp, які знаходяться в node_modules. Чи це виправдовує єдиний випадок використання необхідності бункер для єдиної мети? У цьому проблема, з якою вони стикаються. Я вже використовую композитор, npm, gulp, grunt, dont want buwer особисто, і не хочу бурчати в цьому додатку.
блам

Відмінне запитання, яке потребує інтуїтивного рішення!
Сідвелл

Відповіді:


297

Зазвичай, ви не хочете розкривати будь-який свій внутрішній шлях про те, як ваш сервер структурований до зовнішнього світу. Що ви можете зробити, це зробити /scriptsстатичний маршрут на вашому сервері, який витягує його файли з будь-якого каталогу, в якому вони трапляються. Отже, якщо ваші файли є "./node_modules/bootstrap/dist/". Тоді тег сценарію на ваших сторінках виглядає приблизно так:

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

Якщо ви використовували Express з nodejs, статичний маршрут такий же простий, як і цей:

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

Тоді будь-які запити веб-переглядача від /scripts/xxx.jsавтоматично надходитимуться з вашого distкаталогу за адресою __dirname + /node_modules/bootstrap/dist/xxx.js.

Примітка: новіші версії NPM ставлять більше речей на найвищий рівень, не вкладені настільки глибоко, тому якщо ви використовуєте більш нову версію NPM, то імена шляхів будуть іншими, ніж зазначено у запитанні ОП та в поточній відповіді. Але, концепція все одно та сама. Ви дізнаєтесь, де файли знаходяться фізично на вашому серверному накопичувачі, і ви зробите app.use()з, express.static()щоб зробити псевдо-шлях до цих файлів, щоб ви не піддавали клієнту фактичну організацію файлової системи сервера.


Якщо ви не хочете робити такий статичний маршрут, то вам, мабуть, краще просто скопіювати загальнодоступні скрипти на шлях, який ваш веб-сервер трактує як /scriptsабо будь-яке позначення верхнього рівня, яке ви хочете використовувати. Зазвичай ви можете зробити це копіювання частиною процесу збирання / розгортання.


Якщо ви хочете опублікувати лише один конкретний файл у загальнодоступному каталозі, а не все, що знаходиться в ньому, з ним, ви можете вручну створити окремі маршрути для кожного файлу, а не використовувати express.static()такі, як:

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

І код для створення маршруту для цього

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

Або, якщо ви хочете все-таки розмежувати маршрути для сценаріїв /scripts, ви можете зробити це:

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

І код для створення маршруту для цього

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

2
Чи потрібно мені копіювати їх вручну або існує спосіб Grunt? Я думаю, що якось найкраще було б повністю скопіювати папку завантажувальної програми, /scriptsтому що будь-які залежності всередині модуля будуть підтримуватися.
Кріс

@phpheini - можливо , це допоможе: stackoverflow.com/questions/18966485 / ... і цього npmjs.com/package/grunt-copy
jfriend00

1
@RobertOschler - Ні, це не перевіряє дупфи. express.static()надсилає перше знайдене відповідність. Оскільки кожен express.static()оператор створює лише один можливий шлях узгодження, не повинно бути дублікатів з одного express.static()оператора. Якщо у вас є кілька express.static()заяв про те, що кожен може відповідати, то першим у вашому коді є той, чий збіг буде використаний. Крім того, у вас дійсно не повинно бути декілька файлів з тим самим іменем та відносним шляхом, доступними express.static()висловлюваннями. Найкраща практика - цього не робити.
jfriend00

1
@RobertOschler - Ви повинні бути дуже-дуже уважними щодо того, до чого надаєте нерегульований доступ. Загалом, вам слід вказати лише express.static()на каталог, який ТОЛЬКО містить загальнодоступні файли (включають підкаталоги). Отже, я не можу реально уявити собі проект, де було distвидано багато каталогів. Це здається мені дуже незвичним. Можливо, ви хочете зробити певний маршрут до певного файлу або до 2-3 файлів. У такому випадку ви несете відповідальність за неоднозначність цих файлів. Мати чотири script.jsфайли все одно не принесе вам користі .
jfriend00

1
@RobertOschler - Якщо ви не зробите для них унікальний шлях, щоб відповідати, просто немає жодного способу дізнатися код, який потрібно подавати, коли /script.jsзапит переглядає браузер. Отже, абстрактна відповідь полягає в тому, що ви не повинні допускати існування дублікатів назви файлів, оскільки це не вирішувана проблема, якщо вам не потрібен унікальний префікс для кожного каталогу файлів. Тоді дублювання кореневих імен все одно не є проблемою. Для отримання більш конкретної відповіді з детальною рекомендацією вам потрібно буде розмістити власне запитання з точними подробицями.
jfriend00

18

Я б використав модуль npm path, а потім зробив щось подібне:

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

ВАЖЛИВО: ми використовуємо path.join для приєднання шляхів до системного агностичного шляху, тобто у Windows та Unix у нас є різні роздільники шляху (/ і)


позначте дублікат, як зазначено вище, path.join - це лише додана міра, але все-таки таке ж рішення, додавши статичний шлях до dir_модулів node_modules.
блам

2
"path.join - це лише додаткова міра, але все одно те саме рішення", не те саме рішення. Строго кажучи, він використовує системне приєднання (майте на увазі / та \ роздільники у windows та unix)
Олександр Єгоров

4
Гаразд, я бачу вашу думку, так, це завжди добре мати систему агностика, я не знав, що це було. Дякуємо, що вказали на це. Було б розумно додати це до речення у своїй відповіді, а не просто вказати код :-), наприклад, поясніть свій код.
блам

10

Як зазначає jfriend00, ви не повинні виставляти структуру вашого сервера. Ви можете скопіювати файли залежності вашого проекту на щось подібне public/scripts. Ви можете зробити це дуже легко за допомогою dep-linker, як це:

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

1
Сценарії srcs тоді будуть чимось на зразок /scripts/[package-name]/dist/file.min.js.
Джейкоб Форд

7

Якщо ви хочете швидкого та простого рішення (і у вас встановлений глоток).

У своєму виконанні gulpfile.jsя запускаю просту задачу для копіювання, яка вміщує будь-які файли, які мені можуть знадобитися, в ./public/modules/каталог.

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']);

Мінусом цього є те, що він не автоматизований. Однак якщо все, що вам потрібно, - це кілька скопійованих (і збережених у списку) декількох сценаріїв та стилів, це повинно зробити цю роботу.


Можна було б додати це у своєму package.json у сценаріях 'scripts': {'install':'yarn gulp copy-modules'}, якщо припустити, що пряжа є менеджером пакунків, а gulp встановлений у пакеті.
Тодд

6

Каталог 'node_modules' може не бути в поточному каталозі, тому вам слід вирішити шлях динамічно.

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

+1, хоча я не думаю, що це спрацює у всіх випадках - модуль, пов'язаний з egan npm, який не є в підпапці node_modules
Alasdair McLeay

3

Я хочу оновити це питання простішим рішенням. Створіть символічне посилання на node_modules.

Найпростіший спосіб надати публічний доступ до node_modules - це створити символічне посилання, що вказує на ваші node_modules з вашого загальнодоступного каталогу. Символьне посилання зробить це так, ніби файли існують там, де створено посилання.

Наприклад, якщо сервер вузлів має код для обслуговування статичних файлів

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

і __dirname посилається на / path / to / app, щоб ваші статичні файли подавалися з / path / to / app / dist

і node_modules знаходиться у / path / to / app / node_modules, тоді створіть подібне посилання на mac / linux:

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

або так у Windows:

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

Тепер отримайте запит на:

node_modules/some/path 

отримає відповідь із файлом за адресою

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

що насправді файл у

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

Якщо ваш каталог за адресою / path / to / app / dist не є безпечним місцеположенням, можливо, через втручання в процес збирання з gulp або grunt, ви можете додати окремий каталог для посилання та додати новий serveStatic call, такий як:

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

і у вузлі додайте:

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

1
Але потім ви перемістили додаток іншим шляхом і символьне посилання недійсне. Або ви хочете запустити додаток на іншій машині (тест / прод), вам потрібно буде створити його заново. Будь-який учасник вашої програми повинен зробити те саме. Це додає певного ремонту вищезазначеним рішенням.
mati.o

@ mati.o Що щодо відносних символьних посилань? Мені подобається це рішення, але лише з відносними посиланнями.
Lukáš Bednařík

3

Я не знайшов чистих рішень (я не хочу виставляти джерело всіх своїх node_modules), тому я просто написав сценарій Powershell, щоб скопіювати їх:

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

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

1

Я зробив наведені нижче зміни для автоматичного включення файлів в індексі html. Так що коли ви додасте файл у папку, він автоматично вибирається з папки, без того, щоб ви включали файл у 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

Це те, що я встановив на своєму expressсервері:

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

Щасти...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.