最近公司的一个开发项目, 后端用的是 nodejs. 这两天需要打包给客户演示, 就让公司一个小伙把之前 3D 机房的打包工具移植过来. 打包之后, 发现原本在开发环境下的跑的好好的项目, 不能访问了. 出现项目的首页不能访问的问题:
- can not get file index.html
- express.static
问题出在哪儿?
nodejs 后端的用了 express,index.html 是一个静态文件. 我们知道, 通过 Express 内置的 express.static 可以方便地托管静态文件, 例如图片, CSS,JavaScript 文件等.
将静态资源文件所在的目录作为参数传递给 express.static 中间件就可以提供静态资源文件的访问了. 例如, 假设在 public 目录放置了图片, CSS 和 JavaScript 文件, 可以使用如下代码:
app.use(express.static('public'));
所以, 找到项目中的代码, 查看 static 调用的地方, 和上面一行代码很一样:
app.use(express.static('public'));
到此, 我已经发现了问题, 我告诉小伙伴, 这个地方不用相对路径可以解决这个问题. 由于打包时间限制, 我让小伙伴先简单处理下, 打完包之后, 在来整理下思路:
app.use(express.static('resource/public'));
当然最重要的是, 这个问题其实不难, 自己多钻研下, 很容易发现问题, 也就不会出这个问题, 所以小伙伴自己打手心吧.
恩, 你没看错, 这个地方还是相对目录. 后续产品中会改成比较好的一种情况.
express.static 方法解析
事实上, express.static 方法如果传入的是相对路径, express 会自己把他转换为绝对路径, 我们可以查看下源代码, 在 express.js 找到如下代码:
exports.static = require('serve-static');
说明 static 调用了 serve-static 这个包, 直接找到这个包, 查看 index.js, 可以看到代码, 下面列出重要的两行
- ...
- var resolve = require('path').resolve
- ...
- opts.root = resolve(root)
- ...
这两行就是, express 把相对目录转换成绝对目录的代码, 可以看出, 最终使用的 path 这个内置对象的 resolve 方法, 继续往下看.
path 对象的 resolve 方法
直接查看这个方法的 api 文档, 如下:
https://nodejs.org/api/path.html#path_path_resolve_paths
下面是这个方法的解释:
The path.resolve() method resolves a sequence of paths or path segments into an absolute path.
啥意思呢? 就是这个方法把一系列的 paths 或者 path segments 组织成一个绝对路径, 比如
- path.resolve('/foo','bar');
- // return /foo/bar
详细的说明请自行参考文档, 这个地方有一句话需要特别注意:
If after processing all given path segments an absolute path has not yet been generated, the current working directory is used.
啥意思, 就是如果处理完了所有的 path segments, 也没有生成一个绝对路径, 就要使用 当前工作目录 (current working directory). 比如:
- path.resolve('bar');
- // 加上 /Users/terry 是当前工作目录, return /Users/terry/bar
api 文档中一个比较复杂的示例 (此处注意 resolve 的时候, 从右到左, 参考文档了解详情):
- path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
- // if the current working directory is /home/myself/node,
- // this returns '/home/myself/node/wwwroot/static_files/gif/image.gif'
现在的问题是, 啥是当前工作目录.
nodejs 当前工作目录 current working directory
nodejs 当前工作目录是启动 Node 的目录. 也就是说, 从哪个目录进去启动 node, 就返回哪个目录.
注意, 这个目录不是指 js 文件所在的目录
通过 process.cwd() 方法可以获取当前工作目录.
下面通过一个示例来介绍这个当前工作目录, 假如在 / Users/terry/Documents/JSWorkspace 目录下写一个 js 文件, test.js, 代码只有一行:
console.log(process.cwd());
此时如果, 在目录 / Users/terry/Documents/JSWorkspace 下面执行命令 :node test.js 输出如下:
/Users/terry/Documents/JSWorkspace
但是如果在在目录 / Users/terry/Documents / 下面执行命令: node ./JSWorkspace/test.js, 输出的结果是:
/Users/terry/Documents
因此可以看出你在那个目录执行 node 命令, 当前目录就是那个目录.
回到之前的打包的问题, 由于在开发阶段, 一般都是直接在 js 文件所在目录执行 node 命令, 所以相对目录写的是相对于当前 js 文件的目录没有问题.
可是打包之后, node 的执行放到了 js 目录的上一层去了. 此时相对目录 "public" 不在是相对于 js 文件的相对目录, 而是相对于上一层的, 自然就找不到这个文件夹了, 从而也找不到该文件夹下的 index.html 文件.
如何解决
解决的方法:
在前面已经说过了, 改这个相对目录. 但这种方法很蹩脚. 因为, 启动 node 命令的目录可能会变; 而是如果这应该, 开发阶段的 node 命令执行也需要跟着改. 总之不是兼容性很好的方法.
直接使用绝对路径. 但是这个绝对路径在不同的机器上又不一样, 该如何解决呢? 可以考虑使用全局变量__dirname.
全局变量__dirname
查看 api 文档 https://nodejs.org/api/modules.html#modules_dirname
看到解释如下:
The directory name of the current module. This is the same as the path.dirname() https://nodejs.org/api/path.html#path_path_dirname_path of the __filename https://nodejs.org/api/modules.html#modules_filename .
啥意思呢, 及时返回 nodejs 的 js 文件的所在目录.
有了这个变量之后, 我们就可以用如下代码解决这个问题.
app.use(express.static(__dirname + '/public'));
来源: https://www.cnblogs.com/flyfox1982/p/9239741.html