⚡前端vue项目的开发过程的各种坑哈✔️😁📌
我在使用vue开发的时候会发现有各种各样的bug ✔️🎈总结 Vue 项目中遇到过的常见的“坑”及其解决方法😁😁

- 🎈😁在日常前端开发中遇到过的各种bug和坑也比较多,随着开发过的项目越来越多,遇到过的各种问题或者bug也越来越多了,简单的总结出来放在博客里,希望小伙伴们少走弯路哈哈。
- 好久之前也写开发中遇到过的一些bug和解决方法✔️:
📌(1)vue项目启动cannot get,并且还有报错信息.. - Mahmud*小麦* - 博客园
📌(2)Vue报错Syntax Error:TypeError: this.getOptions is not a function的解决方法~ - Mahmud*小麦* - 博客园
📌(3)vue eslint 报错 error “Component name “*****“ should always be multi-word”,该怎么办? - Mahmud*小麦* - 博客园
📌(4)vue项目安装lees-loader报错:Module build failed: Error: Cannot find module 'less' - Mahmud*小麦* - 博客园
📌(7)vue项目打包后运行index.html,会报错"because its MIME type... - Mahmud*小麦* - 博客园
📌(8)cnpm使用 install报错throw err;^Error: Cannot find module 'fs/promises - Mahmud*小麦* - 博客园
- 🏀🥎接下来是我自己总结出来的实际开发中遇到过的一些问题或者Bug+解决方法✔️✔️✔️。
1. 大量数据渲染导致卡顿
现象:渲染几千个列表项或表格,页面滚动卡顿,像在看PPT一样,用户体验没那么好😥~
解决方法:
(1)使用虚拟滚动组件:虚拟滚动技术,只渲染可见区域的数据(如
vue-virtual-scroller、vue-virtual-scroll-list),【长列表、大数据表格、聊天记录等等】***可以推荐给大家了解:npm install vxe-table@next插件哈(2)分页加载:按需获取数据【后台管理系统、数据统计报表、移动端列表(滚动加载更多也算分页变种哈哈】
(3)列表里包含设备缩略图,我用了
v-lazy指令实现懒加载,只有进入视口的图片才发起请求,减少了页面网络并发和内存占用:【 如电商首页,产品列表等等】(4)事件防抖/节流优化高频操作:=> 对表格的sort-change、filter-change等频繁触发的事件做防抖/节流优化,避免用户快速点击时频繁触发接口请求和 DOM 更新。
~~我觉得以上的方法来可以有效的优化前端页面或者列表大量数据渲染导致的卡顿问题 (如果还有哪些方法的话开源博友们评论评论 +哈哈😄)
2. 刷新页面状态丢失
现象:用户登录后保存 token 到 store,刷新页面后 store 重置,token 丢失。
分析与解决方法: 后来我发现 Vuex 数据存在内存里,刷新页面内存被释放,数据就没了。解决方法是把数据同步存到 localStorage/sessionStorage(本地),刷新时再读回来 。过程:(1)将需要持久化的数据同步到
localStorage/sessionStorage(2)使用插件vuex-persistedstate(Vuex)或 Pinia 的持久化插件



// vuex 插件示例
import createPersistedState from 'vuex-persistedstate'
plugins: [createPersistedState({ paths: ['user', 'token'] })]
之前我写过一个项目中遇到过的关于vue2/3的页面刷新数据丢失的小文章,当时我也是持久化方案来解决的✔️,大家参考一哈【持久化存储】VUE2 vuex-persistedstate插件♾️VUE3 pinia的持久化存储pinia-plugin-persistedstate的使用♨️✔️💯 - Mahmud*小麦* - 博客园
3. 对象新增 / 删除属性无法触发视图更新(Vue 2)
问题:直接给
data中的对象添加新属性,视图不刷新
data() {
return { user: { name: '张三' } }
},
mounted() {
this.user.age = 18 // 不会触发更新
}
原因:Vue 2 使用
Object.defineProperty,无法监听属性新增/删除。解决方法:可以使用 原生(数据劫持)
Vue.set或this.$set,或替换整个对象。
this.$set(this.user, 'age', 18)
// 或者
this.user = { ...this.user, age: 18 }
Vue 3的话小伙伴们要注意哈:就是使用
reactive创建的响应式对象,同样需要避免解构丢失响应性。应使用toRefs。
//比如说:
const state = reactive({ name: '李四', age: 20 })
// ❌ 解构会丢失响应
const { name, age } = state
// ✅ 使用 toRefs
const { name, age } = toRefs(state)
4. 数组索引赋值 / 修改长度不更新视图(Vue 2)
//比如说:
this.list[0] = 'new value' // 不更新
this.list.length = 0 // 不更新
解决方法:这样的情况下使用js数组变异方法(push, pop, shift, unshift, splice等原生方法)或 $set:
this.$set(this.list, 0, 'new value')
this.list.splice(0) // 清空数组
5. 异步更新导致的 DOM 未及时渲染
比如说:修改数据后立即获取 DOM 内容,拿到的是旧值
this.message = 'hello'
console.log(this.$el.textContent) // 可能是旧值
解决方法:使用
nextTick(就是因为 Vue 的数据变化到 DOM 更新是异步批量的,我们修改数据后不能立即获取最新的 DOM。nextTick 的作用就是把回调延迟到 DOM 更新完成后执行,拿到最新的值)一句话:nextTick 就是让你等 DOM 更新完了再干活
this.message = 'hello'
this.$nextTick(() => {
console.log(this.$el.textContent) // 更新后的值
})
6. 在 created 中操作 DOM:created 中调用 this.$refs.xxx 或获取元素尺寸,得到 undefined 或 0。
原因:
created时模板尚未渲染,DOM 还未生成。解决:DOM 相关操作放到
mounted中。若需要操作异步数据渲染后的 DOM,配合nextTick
7. 回调函数中的 this 丢失:
//比如说哈
mounted() {
setTimeout(function() {
console.log(this.message) // this 指向 window
}, 1000)
}
解决方法:使用箭头函数,或提前保存
this
setTimeout(() => {
console.log(this.message)
}, 1000)
// 或
const that = this
setTimeout(function() { console.log(that.message) }, 1000) // 就可以获取啦
8. 动态路由参数变化,组件不重新渲染
现象:比如说 从
/user/1跳转到/user/2,页面数据不变(因为同一组件被复用)。解决方法:监听
$route变化并重新获取数据(或 也可以在组件内使用beforeRouteUpdate导航守卫~~建议线试试哈哈😁)
watch: {
'$route'(to, from) {
this.fetchData(to.params.id)
}
}
9. params 传参刷新丢失
现象:比如说
this.$router.push({ name: 'detail', params: { id: 1 } }) ===>刷新页面后$route.params为空。原因:
params不会体现在 URL 路径中(除非配置动态段),刷新后路由重新匹配,参数丢失。解决方法:将参数放在
query中(如?id=1)或在路由配置中声明动态段path: '/detail/:id'
10. 路由懒加载导致首次加载白屏时间过长
现象:首屏加载大量 JS 文件,请求瀑布流明显。
解决:合理拆分路由,配合 webpack 魔法注释命名 chunk,并使用预加载(prefetch)策略
//比如说 这样:~~
const Detail = () => import(/* webpackChunkName: "detail" */ './Detail.vue')
11. vue中scoped 样式无法覆盖子组件内部样式
现象:在父组件中想修改子组件内部某个元素的样式,写 CSS 无效。
原因:
scoped会给当前组件的所有元素加上唯一属性(如data-v-xxx),并且样式选择器也会带上该属性,但子组件的根元素不会带上父组件的属性(仅子组件自己的scoped会带上它自己的属性)。解决方法:使用深度选择器
>>>或/deep/或::v-deep(注意哈)
//比如说 样式 中 css
<style scoped>
.parent >>> .child-class {
color: red;
}
/* 或 */
::v-deep .child-class { ... }
</style>
12. v-for 中不使用 key
现象:列表顺序改变或筛选时,组件状态错乱、动画异常、渲染体验不好的 情况。
原因:Vue 默认“就地复用”策略导致 DOM 重用,状态错位。
解决方法:始终为
v-for提供唯一的key(优先用唯一 id,不要用 index)
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
13. 大量数据渲染导致卡顿
现象:
渲染几千个列表项或表格,页面滚动卡顿。解决方法:
(1)使用虚拟滚动组件(如
vue-virtual-scroller、vue-virtual-scroll-list)(2)分页加载
(3)对不可见区域使用
v-if延迟渲染
14. 打包后静态资源路径错误(404)
现象:
npm run build后部署,图片、CSS 背景图、字体文件 404。原因:默认
publicPath是'/',部署到子目录时路径不对应。解决方法:在
vue.config.js中配置publicPath:
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? '/my-sub-path/' : '/'
}
15. 项目打包chunk 文件过大
现象:打包后某个 js 文件几 MB,首屏加载慢。
解决方法:
(1)路由懒加载
(2)使用
splitChunks将第三方库分离(3)分析 bundle 大小:
vue-cli-service build --report
// vue.config.js
configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
}
}
}
}
}
16. vue打包之后页面时空白的
在config文件夹下的index.js中,找到
assetsPublicPath: '/'
然后改成
assetsPublicPath: './'
17. 项目打包的时候会发现打包的文件里面有很多非常大的.map后缀的文件,那我们要怎么去操作,让在项目进行打包的时候不要生成这些文件
在
config/index.js里面找到
productionSourceMap: true,
//改成false
productionSourceMap: false,
18.注释:项目部署前 console的调试问题
在开发的时候,我们会用到很多console的调试,在开发完成的时候,又不想一个个去注释,所以在打包的时候可以在build文件夹中找到webpack.prod.conf.js文件中添加以下代码
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false,
//过滤console.log-start
drop_console: true,
pure_funcs: ['console.log']
//过滤console.log-end
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
19. vue打包的时候,elementUI的图标变成了小方框无法显示
vue打包的时候,elementUI的图标变成了小方框无法显示的解决办法,比如说:哟咻额老项目或者的版本的浏览器中这种情况会频繁出现的~
在vue的build文件夹找到utils.js文件,然后在下面这个位置添加 publicPath: '../../'
function generateLoaders (loader, loaderOptions) { const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader,px2remLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader',
publicPath: '../../' //加这个
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
20. vue消除url上面的#符号
在router的index.js的里面添加
mode:'history',
export default new Router({
mode:'history',//加入这个
routes: [
{
path: '/cxyz',
name: 'cxyz',
component: cxyz,
meta:{requireAuth:true}
},
]
})
21. 大屏使用window.onresize来监听屏幕的宽高
只会跑一次,多个页面使用会导致失效,所以视情况而定
mounted() {
var that = this;
window.onresize = function(){ // 定义窗口大小变更通知事件
that.screenWidth = document.documentElement.clientWidth; //窗口宽度
that.screenHeight = document.documentElement.clientHeight; //窗口高度
};
}
22. props / emit 中直接修改props
现象:子组件直接修改父组件传递的prop,控制台报错。解决方法:遵循单向数据流,通过
$emit触发事件让父组件修改
props: ['value'],
methods: {
change() {
this.value = 123 // ❌
}
}
正确姿势:应该是这样写~~
// 子组件
props: ['value'],
methods: {
update(val) {
this.$emit('input', val) // Vue 2 的 v-model 对应 input 事件
}
}

浙公网安备 33010602011771号