关于表单元素 input 上的 v-model 绑定
一直以来我都有个错觉,认为在 input 等输入类型的表单元素上,v-model 指令绑定的是 value 属性和 change 事件,直到后来自己封装某个自定义指令的时候(需要手动触发一下双向绑定),使用 dispatch 分发 change 事件并没有触发绑定值的更新,遂去翻看文档,发现了以下内容。

看来有些事情还是不能想当然,多看文档总是对的。。。
总结: 官方文档中,input 元素上的 v-model 绑定的是
value属性和input事件。
Axios 提交 Date 类型数据时的时区自动变更问题
问题描述
在使用 Axios 提交 Date 类型的数据时,发现请求体中的时间总是与代码中的时间不一致,这使我非常困惑。这个问题是在某次改项目中的 bug 时发现的,排查了很久。
根本原因
Axios 对于提交的 Date 类型的数据,会将其序列化为时间字符串。 这里涉及到 JavaScript 语言在序列化 Date 对象时的默认行为:
JavaScript 语言在序列化 Date 对象时(如通过 JSON.stringify),会调用 Date.prototype.toISOString() 方法,将日期转换为 ISO 8601 格式(如 2025-05-04T01:00:00.000Z)。其中的 Z 表示 UTC 时间(0 时区),而本地时间(如东八区)会被自动转换为 UTC 时间(减去 8 小时)。如图:

为什么要这样做?JavaScript 语言这样做的合理性: Date 对象的 ISO 序列化是为了标准化时间表示,便于跨系统传输。UTC 时间是国际通用标准,因此 JavaScript 的行为是合理的。
解决方案
- 不要直接提交 Date 类型的数据,而是将 Date 对象转换为本地时间字符串,然后再提交。
- 后端进行时区处理
关于使用 CSS order 属性在不改变 dom 结构的情况下改变元素的显示顺序
是这样的,最近我在维护一个项目的时候遇到这样一个需求,页面上有一个元素,它的内部有两个子元素,它们的dom结构从上到下是 A -> B,现在我希望让 B 显示在父盒子的左侧, A 显示在右侧,并且 A 的宽度是父盒子的宽度减去B的宽度(也就是 A 要自适应剩余的宽度)。
这种情况下使用 flex 布局 + order 属性 正好可以解决这个问题。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>Document</title>
<style>
.container {
display: flex;
width: 300px;
color: #fff;
}
.item1 {
order: 2;
flex: 1;
background-color: blue;
}
.item2 {
order: 1;
width: 50px;
background-color: red;
}
</style>
</head>
<body>
<div class="container">
<div class="item1">item1</div>
<div class="item2">item2</div>
</div>
</body>
</html>
关于 重复创建 Vue 应用时报错 Cannot read properties of null (reading 'nextSibling') 的问题
问题描述
在新项目测试发版阶段,页面控制台频繁抛出 Cannot read properties of null (reading 'nextSibling') 错误。
- 错误栈定位到 Vue 内部的
removeFragment函数,具体触发点是hostNextSibling(cur)调用(该函数内部本质是访问cur.nextSibling); - 错误仅在 “服务端修改前端打包产物后” 出现,本地开发环境未复现;
- 排查发现,页面中
#app容器被重复挂载了 Vue 应用实例,且第二次挂载时会触发第一次应用的卸载流程,报错发生在卸载流程的Fragment节点清理阶段。
根本原因
1. 错误触发的技术链路
Vue 中 “重复挂载应用到同一 DOM 元素” 会触发 「先卸载旧应用、再挂载新应用」 的逻辑,而报错的核心是旧应用卸载时 removeFragment 函数的遍历逻辑异常:
removeFragment函数的作用: 清理Fragment对应的连续 DOM 片段,从起始节点cur(通常是 Fragment 子节点的 el)到终止节点end(通常是 Fragment 的 anchor 锚点),通过while (cur !== end)来控制循环边界;javascriptconst removeFragment = (cur, end) => { let next // 边界控制 while (cur !== end) { next = hostNextSibling(cur) // 报错触发点 hostRemove(cur) cur = next } hostRemove(end) }- 异常场景: 由于旧应用的虚拟 DOM(
n1)记录的cur/end与真实 DOM 状态脱节(如end已被提前移除、cur遍历链断裂),导致cur永远无法等于end,循环持续执行直到cur变为null,此时访问cur.nextSibling直接报错。
2. 重复创建应用的根源
重复创建应用的本质是 Vue 入口文件 index.js 被浏览器重复加载并执行,而诱因是服务端的缓存逻辑配置不当:
服务端为了缓存控制,会在
index.html前端打包产物的index.jsURL 后添加随机参数(如?v=xxxx),用于强制浏览器更新新版本资源;但项目中存在「代码分割(Code Splitting)」或「异步加载逻辑」,拆分出的子 chunk(如
chunk-xxx.js)可能会引用index.js;浏览器会将不同参数的
index.js判定为不同资源 ,重复下载并执行 —— 而index.js中包含createApp(App).use(router).mount('#app')的入口逻辑,最终导致同一页面创建并挂载多个 Vue 应用实例。
解决方案
- 避免重复创建 Vue 应用,必要时先进行卸载再进行挂载。(服务端取消缓存控制逻辑或前端应用在挂载前先进行卸载)
- 如果重复创建了 Vue 应用,且又没有先进行卸载,那么要避免根组件中可能出现的
Fragment情况,比如: 书写多个根节点,或者组件内书写多个根节点等。