浏览器接受"classic" js 脚本标签,但不接受 ES6 模块 — 带有节点 http 服务器的 MIME 错误



我想玩ES6模块,所以我决定将Node用作一个简单的web服务器,以避免在本地执行时首次遇到的所有与CORS相关的错误。现在我在浏览器中遇到了与MIME类型相关的错误,我无法完全理解。

这是我的server.js文件:

const http = require('http'),
url  = require('url'),
fs   = require('fs');
http.createServer((req, res) => {
const q = url.parse(req.url, true),
filename = "." + q.pathname;
fs.readFile(filename, (err, data) => {
if (err) {
res.writeHead(404, {'Content-Type': 'text/html'});
return res.end("404 Not Found");
}  
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
return res.end();
});
}).listen(8080);

如果我试图在浏览器中访问我的index.html文件,其中包含以下代码:

<!DOCTYPE html>
<html>
<body> 
<!-- Works -->
<script src="index.js"></script>
<!-- Doesn't work -->
<script src="module.js" type="module"></script>
</body>
</html>

我得到以下错误:

加载模块脚本失败:服务器以非JavaScript MIME类型的"text/html"。

type="text/javascript"属性对模块标记也没有影响。我读到所有ES6模块默认情况下都是"延迟"的,这意味着它们在HTML完全解析之前不会执行。我想这就是问题所在,但我不知道如何相应地修改我的server.js文件来修复它。非常感谢您的帮助!如果不是太不切实际,我宁愿不拉任何NPM包。

编辑:由于我访问了http://localhost:8080/index.html,我认为发送类型为text/html的标头是正确的。但是如果我console.log(url.parse(req.url, true));,我现在看到延迟的脚本标记触发createServer回调。记录的对象明确显示它是JS模块的文件名。

一旦我修复了有故障的标头,我将返回一个工作示例。

解决方案:我增加了两个模块;path和mime。我在url.parse()返回的路径的扩展上使用了mime.getType()

const http = require('http'),
url  = require('url'),
fs   = require('fs'),
mime = require('mime'),
path = require('path');
http.createServer((req, res) => {
const q = url.parse(req.url, true),
filename = "." + q.pathname;
fs.readFile(filename, (err, data) => {
if (err) {
res.writeHead(404, {'Content-Type': 'text/html'});
return res.end("404 Not Found");
}  
res.writeHead(200, {'Content-Type': mime.getType(path.extname(filename))});
res.write(data);
return res.end();
});
}).listen(8080);
res.writeHead(200, {'Content-Type': 'text/html'});

这意味着每个文件都有一个text/htmlmimetype。

当浏览器请求您的模块时,您将返回一个Content-Type: text/html标头-根据HTML规范,这是无效的:

如果满足以下任何条件,则抛出"NetworkError"DOMException:

  • 响应的类型为"error">
  • 响应的状态不是正常状态
  • 从响应的标头列表中提取MIME类型的结果不是JavaScript MIME类型

据我所知,"经典"脚本没有这个限制。

为了解决这个问题,您需要使用适当的MIME类型从服务器发回JavaScript模块,例如text/javascript:

res.writeHead(200, {'Content-Type': 'text/javascript'});

不过,我建议您使用Express之类的东西,而不是直接使用http模块——您可以开箱即用地获得很多功能,我相信它会为您设置标题。

最新更新