在纯JS+HTML项目中集成FullCalendar库时遭遇MIME类型加载错误的问题求助
我正尝试把fullcalendar.io库集成到一个纯JavaScript+HTML项目里,但这个简单的应用却难住了我。我遇到的错误如下:
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/octet-stream". Strict MIME type checking is enforced for module scripts per HTML spec.
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.
我已经尝试过的解决办法
一开始我以为是路径问题——毕竟我是动态导入的库,可能对它和它的依赖处理、导入得不够充分,我还从纯JS切换到了page.js插件,但情况没有任何改变。
然后我觉得可能是Web服务器配置的问题,我先是用了Express.js(本来觉得它简洁,很适合我的场景),折腾了半天还是没解决,又换成了Nginx,但错误信息还是没变。不过有时候会出现导入错误,我觉得这可能是我把项目搞乱导致的。
其他尝试的操作:
- 清除浏览器缓存
- 把fullcalendar相关文件移到不同的文件夹,甚至直接放到根目录
- 在不同浏览器(火狐/Chrome)中测试,但在Postman里能正常显示日历
- 在另一个环境(不带Web服务器的纯HTML+JS)中使用fullcalendar,结果是可以正常运行的
之前使用的Express服务器代码
import express from 'express'; import { join, dirname } from 'path'; import path from 'path'; import history from 'connect-history-api-fallback'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const app = express(); const port = 8888; app.use(express.static(join(__dirname, 'public'), { setHeaders: (res, filePath) => { if (filePath.endsWith('.js') || filePath.endsWith('.mjs')) { res.setHeader('Content-Type', 'application/javascript'); } if (filePath.endsWith('.html')) { res.setHeader('Content-Type', 'text/html'); } } })); app.use(history({index: 'index.html'})); app.use(express.static(join(__dirname, 'src'))); app.use(express.static(join(__dirname, 'src/views'))); app.use(express.static(join(__dirname, 'src/views/make_appointment'))); app.use(express.static(join(__dirname, 'src/views/appointments'))); app.use(express.static(join(__dirname, 'src/views/users'))); app.use('/node_modules', express.static(path.join(__dirname, 'node_modules'))); app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); });
后来切换到的Nginx配置
worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 8888; server_name localhost; root I:/pet-appointment/Project-fe/src/; index index.html; location / { try_files $uri /index.html; } # Serve HTML views location /calendar { try_files /views/calendar.html /index.html; } types { application/javascript js mjs; } # Serve .js files with the correct MIME type location ~* \.js$ { default_type application/javascript; add_header Content-Type application/javascript; } location /node_modules/ { root I:/pet-appointment/Project-fe; add_header Content-Type application/javascript; autoindex on; } location ~* \.(js|css|json|ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|otf|mp4|webm|ogv|ogg)$ { add_header Access-Control-Allow-Origin *; } } }
项目结构(仅展示必要文件)
Project/src/views:
- calendar.html
- calendar.js
Project/src:
- index.html
- navigation_bar.mjs(作为组件在每个页面使用,我没试过去掉它,但觉得这不是问题根源)
Calendar.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>FullCalendar Test</title> <script src="./calendar.js"></script> <!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/6.1.8/index.global.min.css" /> --> </head> <body> <div id="calendar"></div> </body> </html>
Calendar.js
import { Calendar } from '/node_modules/@fullcalendar/core'; import interactionPlugin from '/node_modules/@fullcalendar/interaction'; import dayGridPlugin from '/node_modules/@fullcalendar/daygrid'; import timeGridPlugin from '/node_modules/@fullcalendar/timegrid'; import listPlugin from '/node_modules/@fullcalendar/list'; document.addEventListener('DOMContentLoaded', function() { var calendarEl = document.getElementById('calendar'); var calendar = new Calendar(calendarEl, { plugins: [ interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin ], headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' }, initialDate: '2018-01-12', navLinks: true, // can click day/week names to navigate views editable: true, dayMaxEvents: true, // allow "more" link when too many events events: [ { title: 'All Day Event', start: '2018-01-01', }, { title: 'Long Event', start: '2018-01-07', end: '2018-01-10' }, { groupId: 999, title: 'Repeating Event', start: '2018-01-09T16:00:00' }, { groupId: 999, title: 'Repeating Event', start: '2018-01-16T16:00:00' }, { title: 'Conference', start: '2018-01-11', end: '2018-01-13' }, { title: 'Meeting', start: '2018-01-12T10:30:00', end: '2018-01-12T12:30:00' }, { title: 'Lunch', start: '2018-01-12T12:00:00' }, { title: 'Meeting', start: '2018-01-12T14:30:00' }, { title: 'Happy Hour', start: '2018-01-12T17:30:00' }, { title: 'Dinner', start: '2018-01-12T20:00:00' }, { title: 'Birthday Party', start: '2018-01-13T07:00:00' }, { title: 'Click for Google', url: 'http://google.com/', start: '2018-01-28' } ] }); calendar.render(); });
Index.html
<!DOCTYPE html> <html lang="en"> <head> <title>Appointments</title> <!-- <script src="/router.js"></script> --> <link rel="stylesheet" href="/style.css"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="/navigation-bar.mjs" type="text/javascript" defer></script> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script> <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> <link rel="stylesheet" type="text/css" href="/progress_bar.css"> <!-- <script src="script.js"></script> --> </head> <body> <header-component></header-component> <div id="app"></div> </body> </html> <style> .bd-placeholder-img { font-size: 1.125rem; text-anchor: middle; -webkit-user-select: none; -moz-user-select: none; user-select: none; } @media (min-width: 768px) { .bd-placeholder-img-lg { font-size: 3.5rem; } } .pt-8 { padding-top: 8px; } .center { text-align: center; } .container { width: fit-content; margin: auto; } .border { border: 1px solid black; } .success { color: green; } .error { color: red; } </style>
Package.json
{"name": "pet-frontend", "version": "1.0.0", "description": "A JavaScript project", "type": "module", "main": "./dist/index.js", "scripts": { "start": "node server.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@fullcalendar/core": "^6.1.8", "@fullcalendar/daygrid": "^6.1.8", "@fullcalendar/interaction": "^6.1.8", "@fullcalendar/list": "^6.1.8", "@fullcalendar/timegrid": "^6.1.8", "connect-history-api-fallback": "^2.0.0", "express": "^4.18.2" } }
备注:内容来源于stack exchange,提问作者nokecmec

