JavaScript 执行顺序与作用域/依赖关系问题
1.定位问题::昨天发现自己的博客头像、背景图片,网站的图标都失效了,初步推断是后台html自定义代码问题
2.备份数据::把博客侧边栏公告,定制css,页首html,页尾html都做了备份,然后重头开始一点点配置,先引入JS文件:
<script src="https://cdn.jsdelivr.net/gh/BNDong/Cnblogs-Theme-SimpleMemory@v2.1.7/dist/simpleMemory.js" defer></script>
然后再一点点把元素都加回上去
3.最后发现是我的代码中一言和博客园配置写在一个script中产生了问题
4.使用通义千问AI辅助理解,它对我的问题解答如下:
你提到“为什么这里分成两块 <script> 就没问题,写成一块就有问题”,这是一个非常典型的 JavaScript 执行顺序与作用域/依赖关系 问题。我们来具体分析。
📌 你的代码结构(简化后):
<script>window.cnblogsConfig = { ... }; </script>
<script>
fetch('https://v1.hitokoto.cn')
.then(...)
.catch(console.error);
</script>
这两段脚本分别做了两件事:
- 第一段:定义全局配置对象
window.cnblogsConfig,供博客园主题模板使用。 - 第二段:通过
fetch获取一言 API 数据,并操作 DOM 元素(如#hitokoto)。
✅ 为什么分开写就没问题?
因为博客园的页面模板是 先加载你的配置(cnblogsConfig),再执行页面渲染逻辑,最后才执行你写的其他脚本(比如获取一言数据并插入 DOM)。
也就是说:
- 第一个
<script>设置了配置 → 博客园系统读取它 → 渲染侧边栏、页脚等。 - 第二个
<script>在 DOM 已经生成后运行 → 能正确找到#hitokoto元素 → 成功更新内容。
这是 符合执行时序 的。
❌ 如果合并成一块,为什么会出问题?
假设你这样写:
<script>window.cnblogsConfig = { ... };
fetch('https://v1.hitokoto.cn')
.then(response => response.json())
.then(data => {
const hitokoto = document.querySelector('#hitokoto');
// ...
});
</script>
问题在于:这段脚本很可能在 DOM 完全构建之前就执行了!
特别是 #hitokoto 这个元素,是由博客园根据 cnblogsConfig 动态生成的,而这个生成过程发生在你的 <script> 执行之后。
所以:
- 你设置
cnblogsConfig✔️ - 紧接着立刻去查
#hitokoto❌(此时 DOM 还没生成,返回null) - 导致后续操作失败(比如
.href报错)
💡 换句话说:
fetch那部分代码跑得太早了,DOM 还没准备好。
✅ 正确做法(即使合并,也要等 DOM ready)
如果你非要写在一个 <script> 里,必须确保操作 DOM 的代码 在页面渲染完成后执行。可以用以下方式:
方法 1:监听 DOMContentLoaded
window.cnblogsConfig = { /* ... */ };
document.addEventListener('DOMContentLoaded', () => {
fetch('https://v1.hitokoto.cn')
.then(response => response.json())
.then(data => {
const hitokoto = document.querySelector('#hitokoto');
if (hitokoto) {
hitokoto.innerHTML =<a href="https://hitokoto.cn/?uuid=${data.uuid}">${data.hitokoto}</a>;
}
})
.catch(console.error);
});
方法 2:用 setTimeout 延迟(不推荐,但有时有效)
window.cnblogsConfig = { /* ... */ };
setTimeout(() => {
// 假设 500ms 后 DOM 已生成
fetch(...).then(...)
}, 500);
但最可靠的是 方法 1 或 保持两个 <script> 分开 —— 因为博客园的渲染机制决定了配置脚本和后续操作脚本天然需要分离。
🔍 补充:博客园的执行流程(推测)
- 加载用户自定义
<script>(包括cnblogsConfig) - 博客园 JS 引擎读取
window.cnblogsConfig - 动态生成 HTML 结构(包括
#hitokoto元素) - 页面继续加载,执行后续
<script>(如你的 fetch)
所以 分开写天然符合这个流程,合并则打破时序。
✅ 总结
| 情况 | 是否可行 | 原因 |
|---|---|---|
分成两个 <script> |
✅ 可行 | 配置先生效,DOM 生成后再操作 |
合并成一个 <script> |
❌ 可能失败 | fetch 立即执行,但目标 DOM 尚未创建 |
合并 + DOMContentLoaded |
✅ 可行 | 等 DOM 准备好再操作 |
所以,不是“不能合并”,而是“合并后必须等待 DOM 就绪”。
建议:保持分开写,清晰、安全、符合博客园的设计预期。