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

我有一个关于将node_modules包含到HTML网站的最佳实践的问题。

假设我在node_modules文件夹中有Bootstrap。现在对于网站的生产版本,我如何包括引导脚本和位于node_modules文件夹内的CSS文件?将Bootstrap留在该文件夹中并执行如下操作是否有意义?

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

或者我必须在gulp文件中添加规则,然后将这些文件复制到dist文件夹中?或者最好让gulp以某种方式完全从我的HTML文件中删除本地引导,并将其替换为CDN版本?

242381 次浏览

通常,您不希望向外部世界公开任何关于服务器结构的内部路径。你所能做的就是在你的服务器中创建一个/scripts静态路由,它从文件所在的目录中获取文件。所以,如果你的文件在"./node_modules/bootstrap/dist/"。然后,页面中的script标签看起来就像这样:

<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的问题和当前答案中所指示的。但是,概念还是一样的。你可以找到文件在服务器驱动器上的物理位置,并使用express.static()创建app.use()来创建这些文件的伪路径,这样你就不会将实际的服务器文件系统组织暴露给客户端。


如果你不想创建这样的静态路由,那么你最好将公共脚本复制到你的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');
});

我会使用path npm模块,然后做这样的事情:

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

重要的是:我们使用path。使用系统不可知的方式使路径连接,即在Windows和Unix中,我们有不同的路径分隔符(/和\)

目录'node_modules'可能不在当前目录中,所以你应该动态解析路径。

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

我做了下面的更改,以AUTO-INCLUDE索引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>
<% } %>

正如jfriend00提到的,你不应该公开你的服务器结构。你可以将你的项目依赖文件复制到类似public/scripts的文件中。你可以像这样用dep-linker执行很容易:

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

我想用一个更简单的解决方案来更新这个问题。创建到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

或者像这样在窗户上:

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

现在一个get请求:

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

我没有找到任何干净的解决方案(我不想暴露所有node_modules的源代码),所以我只是写了一个Powershell脚本来复制它们:

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


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

如果你想要一个快速简单的解决方案(并且你已经安装了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']);

这样做的缺点是它不是自动化的。然而,如果你所需要的只是复制一些脚本和样式(并保存在一个列表中),这应该可以完成工作。

这是我在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>

祝你好运…

要在html中使用node_modules中的多个文件,我发现最好的方法是将它们放入一个数组,然后循环它们,使它们对web客户端可见,例如使用node_modules中的filepond模块:

const filePondModules = ['filepond-plugin-file-encode', 'filepond-plugin-image-preview', 'filepond-plugin-image-resize', 'filepond']
filePondModules.forEach(currentModule => {
let module_dir = require.resolve(currentModule)
.match(/.*\/node_modules\/[^/]+\//)[0];
app.use('/' + currentModule, express.static(module_dir + 'dist/'));
})

然后在html(或布局)文件中,像这样调用它们:

    <link rel="stylesheet" href="/filepond/filepond.css">
<link rel="stylesheet" href="/filepond-plugin-image-preview/filepond-plugin-image-preview.css">
...
<script src="/filepond-plugin-image-preview/filepond-plugin-image-preview.js" ></script>
<script src="/filepond-plugin-file-encode/filepond-plugin-file-encode.js"></script>
<script src="/filepond-plugin-image-resize/filepond-plugin-image-resize.js"></script>
<script src="/filepond/filepond.js"></script>

如果你要链接很多文件,创建一个白名单,然后使用sendFile():

app.get('/npm/:pkg/:file', (req, res) => {
const ok = ['jquery','bootstrap','interactjs'];
if (!ok.includes(req.params.pkg)) res.status(503).send("Not Permitted.");
res.sendFile(__dirname + `/node_modules/${req.params.pkg}/dist/${req.params.file}`);
});

例如,你可以安全地链接到/npm/bootstrap/ bootstrap .js, /npm/bootstrap/ bootstrap .css等等。

顺便说一句,我很想知道是否有一种方法可以使用express.static来加入白名单