百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

那些Vue开发遇到的坑---响应式系统

zhezhongyun 2025-05-08 08:04 28 浏览

Vue是目前使用较为广泛的前端框架之一。相比React,Vue更容易学习上手。毕竟在React中万物皆JavaScript。这让一些习惯于编写HTML+JavaScript的程序员不太乐于接受。相比之下,Vue的模板语法它不香么。

当然,Vue同样支持类似于JSX的语法的渲染函数,但是相信我,只要你学了模板语法,你就会放弃渲染函数。

有的同学可能会提到AngularJS,这里就要说道,Vue的一些语法设计的确参考了AngularJS,但是Vue的API设计相对AngularJS要简单的多,学习成本更低。

而且,Vue在设计过程中解决了很多AngularJS存在的问题,包括Vue对数据流的控制都会让你的代码更加清晰易懂,让你可以在使用框架或者阅读别人代码的时候少说几句F**k(这个不完全保证)。



虽然Vue上手容易,但这并不代表你可以轻而易举的完全掌握它,要想真正了解并熟练这个框架,它的一些底层原理还是要了解一二的,这同样有助于开阔你的编程思路。今天我们就先介绍一下Vue最独特的特性之一--------响应式系统(这句话是抄官方的)。

Vue的响应式指的是你在一个页面中展示了一个变量的值,当这个变量的值由于一些操作发生改变时,Vue会自动在无需刷新界面的前提下帮你把新的值展示到相应的位置,当然这个过程不需要你自己写任何的dom刷新渲染的代码(我觉得我说的够通俗易懂了,再看不懂,请你去看尤雨溪的官方解说,保证你更看不懂)。

为了实现这一效果,Vue做了很多你不知道的事(不然怎么会不用你写一行代码,因为他们替你写了)。接下来的解说涉及一些Vue和JavaScript的基础知识,比如Object.defineProperty等,不太了解的同学请复制Object.defineProperty并打开浏览器粘贴到检索栏按下回车看看这到底是啥。

虽然Vue3版本弃用defineProperty改用proxy,但是响应式系统的主要思路还是没有变的,所以此处提到的defineProperty是Vue2的实现方法,这点请小伙伴们注意哦。



VUE·响应式原理

一个Vue实例具备一个名为data的数据对象,对象中包含了当前Vue实例所需要的数据,当一个Vue实例生成时,Vue的响应式系统会递归的将data的property通过Object.defineProperty转换为getter/setter。

你可以理解为响应式系统对每一个实例数据绑定了getter/setter函数,要获取数据需要通过调用getter函数,为数据写入新值则需要调用setter函数。

每一个Vue实例还对应一个watcher实例(看名字就是知道这是拿来监听的)。这个watcher实例会记录与它对应的Vue实例的所接触过的所有数据。在此之后如果某条数据发生改变,那么必将通过setter函数去设置新值,这时watcher会监听到这一变化,然后通知用到这个数据的Vue实例进行重新渲染,更新新值到页面上,整个流程如下图:

引自:
https://cn.vuejs.org/v2/guide/reactivity.html


上面那段话可能会比较晦涩难懂,因此我准备了下面这段话:我们以一个按钮为例,按钮上显示了一个由变量定义的字,当点击按钮时按钮上的文字会发生改变,代码如下:

<template>    <div>        <button type="button" @click="message='Do not click me!'">{{message}}</button>    </div></template><script>  export default {    name: "demo",    data() {      return {        message: 'Click Me!'      }    }  }</script>

从代码中我们可以看到,这个Vue实例包含一个按钮和一个名为message的数据,在按钮上的字通过调用message来展示。

当这个Vue实例被注册时,我们的响应式系统会为message设置一对getter/setter函数,然后这个Vue实例会去一个叫做watcher的地方登记他用到的变量,这里它登记的就是message,它告诉watcher,我用到了message,当他改变的时候请及时告诉我。Watcher就在小本本上记下来了,并且和message的getter/setter函数保持联系,当我们点击按钮,按钮的click事件改变了message的值,这时会先调用setter函数,setter函数在改变message的值的同时会通知watcher,watcher收到这一消息之后就会通知Vue实例,告诉他,你用到的message变了,Vue实例收到这一消息就会重新渲染按钮,把新的message值显示在按钮上,至此,一次响应式更新完成了。

那些VUE开发遇到的坑响应式系统

Vue的响应式系统非常好用,开发者甚至可以不懂得DOM的渲染相关知识就能完成一个响应式页面的开发,但是,我们日常开发总不可能是都像教程里的demo一样简简单单清清楚楚,一个庞大的web系统会有复杂的组件嵌套引用,组件之间有着复杂的数据交互,偶尔经常就会出现bug,而且有时候你在你的代码中找不到任何问题(那是你以为),然后就会百思不得其解为什么我的数据没有及时更新到页面上!!!一定是Vue有问题,破框架!!!!

好了,吐槽完之后我们还是老老实实看看,到底那里出了问题,为什么你的代码没有按照预期的运行。

今天我就为大家分析一下,在利用Vue进行开发的时候,为什么有些数据的变化不会被及时监听到并触发相关组件从新渲染。

对象类型在JavaScript中是一个引用类型,与基本类型不同,对象是按照引用访问的。因此,如果你想在Vue中监听到一下对象类型变量的变化时,你需要一些额外的操作,就比如下面这几行代码:

<template>    <div>        {{message.content}}        <button type="button" @click="message.content='clicked'">click message</button>    </div></template><script>  export default {    name: "demo",    data() {      return {        message: {}      }    }  }</script>

以上代码渲染了一个按钮,并且声明了一个名为message的空的对象变量,意图是想要在点击按钮时,为message对象设置contact属性的值为‘clicked’。当我们开始运行我们的代码并在页面上点击按钮时,页面上并没有按照我们预期的展示出message的content属性值。然后作为一个程序员,你可能就要开始打debugger一步一步的调试,然后你会发现,你的代码并没有写错,在调试器中,message的属性确实改变了,并且按照预期被设置为‘clicked’,但是,为什么页面毫无反应,预期的‘clicked’字符串去哪里了?

其实,这是由于Vue虽然在初始化的时候向watcher注册了message, watcher中并没有记录一个后续添加的content属性,除非你重新为message赋值否则Vue是无法监听到message的content属性的。

Vue的开发者当然不可能这么无情的让你换个写法,所以他们提供了一个set函数,这个函数可以保证你为message添加的属性也是响应式的,那么就可以让代码按照你的要求执行了,具体实现如下:

<template>    <div>        {{message.content}}        <button type="button" @click="click">click message</button>    </div></template><script>  export default {    name: "demo",    data() {      return {        message: {}      }    },    methods:{      click(){        this.$set(this.message,'content','clicked')      }    }  }</script>

除了以上的情况,还有另外一种常见情形,就是当一个组件接收一些来自父组件的变量时,如果这个变量是一个对象,你同样无法使用Vue实例的watch去监听到他,在此情况下,可以通过设置deep为true来实现对象的深度监听,如下:

<template>    <div>        {{message.content}}    </div></template><script>  export default {    name: "demo",    props:{      message:{        default:null,      }    },    watch:{      message:{        deep:true,        handler(val){          //message发生改变后的一些逻辑处理        }      }    }  }</script>

值得提醒的是,数组类型在JavaScript中也是一个比较特殊的数据类型,与对象类型相似,数组也是引用类型,因此在开发中也会遇到和对象类型相似的问题,解决方法也是大同小异,此处就不多加说明。


END


关于作者:夏夏,前端工程师,参与普元DevOps产品开发,以及微服务、容器云等产品开发,负责前端页面设计、架构搭建等工作。善于架构搭建、组件封装及相关算法设计。

关于EAWorld:微服务,DevOps,数据治理,移动架构原创技术分享。

相关推荐

Chinese vice premier calls for multilateralism at Davos

DAVOS,Switzerland,Jan.21(Xinhua)--ChineseVicePremierDingXuexiangdeliveredaspeechatthe...

用C++ Qt手把手打造炫酷汽车仪表盘

一、项目背景与核心价值在车载HMI(人机交互界面)开发领域,虚拟仪表盘是智能座舱的核心组件。本项目基于C++Qt框架实现一个具备专业级效果的时速表模块,涵盖以下技术要点:Qt图形绘制核心机制(QPa...

系列专栏(八):JS的第七种基本类型Symbols

ES6作为新一代JavaScript标准,已正式与广大前端开发者见面。为了让大家对ES6的诸多新特性有更深入的了解,MozillaWeb开发者博客推出了《ES6InDepth》系列文章。CSDN...

MFC界面开发工具BCG v31.1 - 增强功能区、工具箱功能

点击“了解更多”获取工具亲爱的BCGSoft用户,我们非常高兴地宣布BCGControlBarProfessionalforMFC和BCGSuiteforMFCv31.2正式发布!新版本支...

雅居乐上调出售吉隆坡项目保留金,预计亏损扩大至6.64亿元

1月2日,雅居乐集团(03383.HK)发布有关出售一家附属公司股权披露交易的补充公告。此前雅居乐集团曾公告,2023年11月8日(交易时段后),集团子公司AgileRealEstateDeve...

Full text: Address by Vice Premier Ding Xuexiang&#39;s at World Economic Forum Annual Meeting 2025

DAVOS,Switzerland,Jan.21(Xinhua)--ChineseVicePremierDingXuexiangonTuesdaydeliveredasp...

手机性能好不好 GPU玄学曲线告诉你

前言各位在看测试者对手机进行评测时或许会见过“安卓玄学曲线”,所谓中的安卓玄学曲线真名为“ProfileGPURendering”。大多数情况下,在系统“开发者选项中被称为“GPU显示配置文件”或...

小迈科技 X Hologres:高可用的百亿级广告实时数仓建设

通过本文,我们将会介绍小迈科技如何通过Hologres搭建高可用的实时数仓。一、业务介绍小迈科技成立于2015年1月,是一家致力以数字化领先为优势,实现业务高质量自增长的移动互联网科技公司。始...

vue3新特征和所有的属性,方法汇总及其对应源码分析

vue3新特征汇总与源码分析(备注:vue3使用typescript编写)何为应用?constapp=Vue.createApp({})app就是一个应用。应用的配置和应用的API就是app应用...

China&#39;s stability redefines global trade in a volatile era

ContainersareunloadedatQingdaoPort,eastChina'sShandongProvince,December10,2024.[Photo/X...

QML 实现图片帧渐隐渐显轮播

前言所谓图片帧渐隐渐显轮播就是,一组图片列表,当前图片逐渐改变透明度隐藏,同时下一张图片逐渐改变透明度显示,依次循环,达到渐隐渐显的效果,该效果常用于图片展示,相比左右自动切换的轮播方式来说,这种方式...

前端惊魂夜:我竟在CSS里写出了JavaScript?

凌晨两点,写字楼里只剩下我工位上的一盏孤灯。咖啡杯见底,屏幕的光映在疲惫的眼镜片上。为了实现一个极其复杂的动态渐变效果,我翻遍了MDN文档,试遍了所有已知的CSS技巧,却始终差那么一口气。“要是CSS...

10 个派上用场的 Flutter 小部件

尝试学习一门新语言可能会令人恐惧和厌烦。很多时候,我们希望我们知道早先存在的某些功能。在今天的文章中,我将告诉你我希望早点知道的最方便的颤振小部件。SpacerSpacer创建一个可调整的空白空...

让我的 Flutter 代码整洁 10 倍的 5 种

如果你曾在Flutter中使用过SingleTickerProviderStateMixin来制作动画,猜猜怎么着?你已经使用过Mixin了——恭喜你,你已经处于一段你甚至不知道的关...

daisyUI - 主题漂亮、代码纯净!免费开源的 Tailwind CSS 组件库

漂亮有特色的CSS组件库,组件代码非常简洁,也支持深度定制主题、定制组件,可以搭配Vue/React等框架使用。关于daisyUIdaisyUI是一款极为流行的CSSUI组件库,...