Django项目中JavaScript导入外部库致代码失效问题求助
问题
我正在给Django项目的网站添加聊天功能,需求是每条消息容器后显示「多久之前」的时间提示。
遇到两个问题:
- 导入非JavaScript内置的外部库(比如date-fns的
formatDistanceToNow)后,代码完全失效;移除导入语句后,所有功能恢复正常。 - 添加导入语句时,PyCharm编辑器会改变变量的颜色(例如从紫色变为白色)。
相关代码
import {formatDistanceToNow} from "date-fns"; /** * Variables */ let chatName = '' let chatSocket = null let chatWindowUrl = window.location.href let chatRoomUuid = Math.random().toString(36).slice(2, 12) /** * Elements */ const chatElement = document.querySelector('#chat') const chatOpenElement = document.querySelector('#chat_open') const chatJoinElement = document.querySelector('#chat_join') const chatIconElement = document.querySelector('#chat_icon') const chatWelcomeElement = document.querySelector('#chat_welcome') const chatRoomElement = document.querySelector('#chat_room') const chatNameElement = document.querySelector('#chat_name') const chatLogElement = document.querySelector('#chat_log') const chatInputElement = document.querySelector('#chat_message_input') const chatSubmitElement = document.querySelector('#chat_message_submit') /** * Functions */ function getCookie(name) { var cookieValue = null if (document.cookie && document.cookie != '') { var cookies = document.cookie.split(';') for (var i = 0; i < cookies.length; i ++) { var cookie = cookies[i].trim() if (cookie.substring(0, name.length + 1) === (name + '=')){ cookieValue = decodeURIComponent(cookie.substring(name.length + 1)) break } } } return cookieValue } function scrollToBottom() { // Scroll to the bottom of the chat log chatLogElement.scrollTop = chatLogElement.scrollHeight; } function formatTimeDifference(timeDifference) { const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24)); const hours = Math.floor((timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60)); if (days > 0) { return `${days} day${days > 1 ? 's' : ''} ago`; } else if (hours > 0) { return `${hours} hour${hours > 1 ? 's' : ''} ago`; } else { return `${minutes} minute${minutes > 1 ? 's' : ''} ago`; } } // Function to update the timestamp text function updateTimestamps() { const timestamps = document.querySelectorAll('.timestamp'); timestamps.forEach(timestamp => { const created_at = new Date(timestamp.dataset.createdat); // Calculate the time difference and format it using formatDistanceToNow const formattedTimeDifference = formatDistanceToNow(created_at); // Set the timestamp text with the updated time timestamp.textContent = formattedTimeDifference + " ago"; }); } function sendMessage() { chatSocket.send(JSON.stringify({ 'type': 'message', 'message': chatInputElement.value, 'name': chatName })) chatInputElement.value = '' } function onChatMessage(data) { console.log('onChatMessage', data); const message = document.getElementById("chat_message_input").value; if (data.type === 'chat_message') { // Create a container div for the chat message const container = document.createElement('div'); container.classList.add('flex', 'w-full', 'mt-2', 'space-x-3', 'max-w-d'); // Apply CSS classes // Create the initials circle div const initialsDiv = document.createElement('div'); initialsDiv.classList.add('flex-shrink-0', 'h-10', 'w-10', 'rounded-full', 'bg-gray-300', 'text-center', 'pt-2'); // Apply CSS classes initialsDiv.textContent = data.initials; // Set initials // Create the message container div const messageContainer = document.createElement('div'); messageContainer.classList.add('message-container',); // Apply CSS classes // Create the message text paragraph const messageText = document.createElement('p'); messageText.classList.add('message-text', 'text-sm'); // Apply CSS classes messageText.textContent = data.message; // Set message text // Create the timestamp span const timestampSpan = document.createElement('span'); const created_at = new Date(data.created_at) const now = new Date() const timeDifference = now - created_at; const formattedTimeDifference = formatTimeDifference(timeDifference) timestampSpan.classList.add('timestamp'); // Apply CSS classes timestampSpan.textContent = formattedTimeDifference + ' ago'; // Set timestamp text // Append elements to the hierarchy messageContainer.appendChild(messageText); container.appendChild(initialsDiv); container.appendChild(messageContainer); const timestampWrapper = document.createElement('div'); timestampWrapper.classList.add('timestamp-wrapper'); timestampWrapper.appendChild(timestampSpan); // Append the dynamically created container to your chat log chatLogElement.appendChild(container); chatLogElement.appendChild(timestampWrapper); scrollToBottom() document.getElementById("chat_message_input").value = ""; } } async function joinChatRoom() { console.log('joinChatRoom') chatName = chatNameElement.value console.log('Join as:', chatName) console.log('Room uuid:', chatRoomUuid) const data = new FormData() data.append('name', chatName) data.append('url', chatWindowUrl) await fetch(`/api/create-room/${chatRoomUuid}/`, { method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken') }, body: data }) .then(function(res) { return res.json() }) .then(function (data){ console.log('data', data) }) chatSocket = new WebSocket(`ws://${window.location.host}/ws/${chatRoomUuid}/`) chatSocket.onmessage = function (e) { console.log('onMessage') onChatMessage(JSON.parse(e.data)) } chatSocket.onopen = function (e) { console.log('onOpen - chat socket was opened') } chatSocket.onclose = function (e) { console.log('onClose - chat socket was closed') } } /** * Event Listeners */ chatOpenElement.onclick = function (e) { e.preventDefault() chatIconElement.classList.add('hidden') chatWelcomeElement.classList.remove('hidden') return false } chatJoinElement.onclick = function (e) { e.preventDefault() chatWelcomeElement.classList.add('hidden') chatRoomElement.classList.remove('hidden') joinChatRoom() return false } chatSubmitElement.onclick = function (e) { e.preventDefault() sendMessage() return false } document.getElementById("chat_message_input").addEventListener("keydown", function (event) { if (event.key === "Enter") { event.preventDefault(); // Prevent the Enter key from creating a new line // Call a function to send the message here sendMessage(); } }); setInterval(updateTimestamps, 60000); // Update every minute (adjust as needed)
解决方案
1. 代码失效的核心原因与修复
浏览器默认不支持在普通脚本文件中直接使用ES模块的import语法,这就是导入date-fns后代码崩溃的原因。有两种修复方式:
方式一:用CDN引入全局版date-fns
在HTML页面的<head>或<body>末尾添加date-fns的CDN脚本:
<script src="https://cdn.jsdelivr.net/npm/date-fns@2.30.0/index.min.js"></script>
然后修改JS代码:
- 移除顶部的
import语句 - 在
updateTimestamps函数里改用全局的dateFns对象调用方法:const formattedTimeDifference = dateFns.formatDistanceToNow(created_at);
注意:formatDistanceToNow本身会返回「5 minutes ago」这类带ago的字符串,要删掉代码里的+ " ago",避免重复。
方式二:将脚本声明为ES模块
在引入你的聊天JS文件的HTML标签中添加type="module":
<script type="module" src="/static/js/chat.js"></script>
这种方式需要先通过npm install date-fns安装依赖,且确保Django能正确处理静态资源的模块解析,纯静态部署场景更推荐CDN方式。
2. PyCharm变量颜色变化的问题
这是编辑器的正常行为:当识别到文件使用ES模块语法(import)时,PyCharm会切换到ES模块的作用域解析规则,变量高亮颜色会相应调整,不影响代码功能,解决导入问题后可忽略该现象。
额外建议
你已经自己实现了formatTimeDifference函数,如果不想引入外部库,完全可以继续使用这个函数,既避免了导入问题,也减少了外部依赖。
内容的提问来源于stack exchange,提问作者KNenov
相关产品推荐
相关产品推荐

