如何包括位于node_modules文件夹内的脚本?


275

我有一个关于node_modules纳入HTML网站的最佳做法的问题。

假设我的node_modules文件夹中有Bootstrap 。现在,对于网站的正式版,我将如何在node_modules文件夹内添加Bootstrap脚本和CSS文件?将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,咕unt声,也不想亲自鞠躬,也不想在此应用程序上咕gr咕either。
blamb

优秀的问题,需要一个直观的解决方案!
Sydwell

Answers:


297

通常,您不希望向外界公开服务器内部结构的任何内部路径。您可以做的是/scripts在服务器中建立一条静态路由,从它们碰巧驻留的任何目录中获取文件。因此,如果文件位于"./node_modules/bootstrap/dist/"。然后,页面中的脚本标签如下所示:

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

如果您对nodejs使用express,那么静态路由就这么简单:

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

然后,来自的所有浏览器请求/scripts/xxx.js都将自动从您的dist目录中获取__dirname + /node_modules/bootstrap/dist/xxx.js

注意:较新版本的NPM会将更多内容放在顶层,而不是嵌套得太深,因此,如果您使用较新版本的NPM,则路径名将不同于OP的问题和当前答案中指示的名称。但是,概念仍然相同。您可以找到文件在服务器驱动器上的物理位置,并app.use()使用它们express.static()来建立这些文件的伪路径,这样就不会向客户端公开实际的服务器文件系统组织。


如果您不想创建这样的静态路由,那么最好将公共脚本复制到Web服务器确实要处理的路径/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因为这样可以保持模块内部的所有依赖关系。
克里斯(Chris)


1
@RobertOschler-不,它不检查公仔。 express.static()发送找到的第一个匹配项。由于每个express.static()语句仅创建一个可能的匹配路径,因此一个express.static()语句不应重复。如果您有多个express.static()语句,每个语句都可能有一个匹配项,那么代码中的第一个语句就是将使用其匹配项的语句。另外,您实际上不应该有多个名称相同且相对路径可由express.static()语句访问的文件。最佳做法是不这样做。
jfriend00

1
@RobertOschler-您必须非常非常小心地提供不受限制的访问权限。通常,您只应指向express.static()仅包含公共文件(包括子目录)的目录。因此,我无法想象一个项目中dist发布了许多目录。这对我来说似乎很不寻常。也许您想对特定文件或2-3个文件进行特定路由。在这种情况下,您有责任消除这些文件的歧义。script.js无论如何,拥有四个文件对您没有任何好处。
jfriend00 '18

1
@RobertOschler-如果您不要求在它们前面添加唯一路径以进行匹配,那么任何代码都无法/script.js通过浏览器请求在何时提供服务。因此,抽象的答案是您不应该存在重复文件名的情况,因为这不是一个可解决的问题,除非您为每个文件目录都要求一个唯一的前缀。这样,重复的根名称就不是问题了。要获得具有具体建议的更具体答案,您需要发布自己的问题,并提供确切的详细信息。
jfriend00 '18

18

我将使用路径npm模块,然后执行以下操作:

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

重要提示:我们使用path.join来使用系统不可知的方式进行路径连接,即在Windows和Unix上,我们使用不同的路径分隔符(/和)


如上标记重复项,path.join只是一个增加的措施,但是通过将静态路径添加到node_modules目录仍然是相同的解决方案。
blamb

2
“ path.join只是一个附加的度量,但仍然是相同的解决方案”而不是相同的解决方案。严格来说,它使用系统特定的联接(请记住/和\和Windows和Unix中的\分隔符)
Alexander Egorov

4
好的,我明白你的意思了,是的,与系统无关的问题总是好的,我不知道那是事实。感谢您指出了这一点。明智的做法是将其添加到答案中的句子中,而不是仅提供代码:-),例如,解释您的代码。
blamb

10

如jfriend00所述,您不应公开服务器结构。您可以将项目依赖项文件复制到public/scripts。您可以使用dep-linker 轻松完成此操作,如下所示:

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

1
脚本src的内容将类似于/scripts/[package-name]/dist/file.min.js
雅各布·福特

7

如果您想要一个快速简便的解决方案(并且已安装gulp)。

在我中,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'},假设yarn是包管理器并且gulp安装在包中。
托德

6

目录“ node_modules”可能不在当前目录中,因此您应动态解析路径。

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

+1,尽管我认为这并非在所有情况下都有效-例如,不在node_modules子文件夹中的npm链接模块
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调用,例如:

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

并在节点中添加:

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

1
但是,然后您已将应用程序移动到其他路径,并且符号链接无效。或者您想在其他机器上运行您的应用程序(测试/生产),则需要再次创建它。应用程序的任何贡献者都必须做同样的事情。它为上述解决方案增加了一些维护。
mati.o 16/09/19

@ mati.o相对符号链接呢?我喜欢这种解决方案,但只能使用相对的符号链接。
卢卡什·贝德纳

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>

祝好运...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.