前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >关于 Vue 响应式原理的困惑

关于 Vue 响应式原理的困惑

作者头像
HelloVass
发布于 2018-09-12 02:22:27
发布于 2018-09-12 02:22:27
2K00
代码可运行
举报
文章被收录于专栏:Hellovass 的博客Hellovass 的博客
运行总次数:0
代码可运行

需求描述

需要将用户信息的 UI(下文用 UserInfo 来代替) 写成一个 Vue 组件,达到重用的目的。

UserInfo 组成

  • 用户信息的模板
  • 获取用户信息的逻辑
  • 样式

大概长这样

请先忽略丑陋的UI,显示的元素主要就俩:

  • 头像
  • 昵称

服务端返回的数据格式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
    "data":{
        "id":"2",
        "type":"user",
        "attributes":{
            "nick_name":"HelloVass",
            "avatar":"https://wx.qlogo.cn/mmopen/vi_32/DYAIOgq83epZOhVL6QcUqJjEo7mqSpiamWRAaX1lB9dV79PzfOA5CMzBlBmCUfKibb2JyMQ0Rubic9OLMzjBRS9Gw/132",
            "score":0,
            "data":{
                "gender":1,
                "city":"Hangzhou",
                "province":"Zhejiang",
                "country":"China"
            }
        }
    }
}

UserInfo 的代码非常简单

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<template>

  <div class="user-container">
    <!--背景-->
    <image
      class="user-bg"
      mode="aspectFill"
      :src="userInfo.avatar"
    >
    </image>
     <!--用户信息-->
    <div class="user-info">
      <!--头像-->
      <image
        class="user-avatar"
        :src="userInfo.avatar"
      ></image>
      <!--昵称-->
      <div
        class="user-nickname"
      >{{userInfo.nick_name}}
      </div>
    </div>
  </div>
</template>

<script>
  // 用户Api
  import UsersApi from "@/network/users-api";

  export default {
      
    name: "UserHeader",

    data() {
      return {
        result: Object
      };
    },

    computed: {
      // 用户是否登录
      isLogin() {
        return this.$store.getters.isLogin;
      },
	  // 用户信息
      userInfo() {
        return this.result.data.attributes;
      }
    },

    created() {
        
      // 未登录,return
      if (!this.isLogin) {
        return;
      }
        
      // 已经登录,直接从服务器获取用户数据
      UsersApi.getUserInfo()
        .then(res => {
          this.result = res;
        });
    }
  };
</script>

// 省略样式
<style scoped lang="scss">
</style>

看起来似乎没什么问题,实验一下

哦豁,凉凉

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
VM116:1 thirdScriptError
Cannot read property 'attributes' of undefined;at pages/users/main page lifeCycleMethod onReady function
TypeError: Cannot read property 'attributes' of undefined
    at VueComponent.userInfo (http://127.0.0.1:46848/appservice/static/js/pages/users/main.js:367:30)
    at Watcher.get (http://127.0.0.1:46848/appservice/static/js/vendor.js:2635:25)
    at Watcher.evaluate (http://127.0.0.1:46848/appservice/static/js/vendor.js:2742:21)
    at VueComponent.computedGetter [as userInfo] (http://127.0.0.1:46848/appservice/static/js/vendor.js:2971:17)
    at VueComponent.render (http://127.0.0.1:46848/appservice/static/js/pages/users/main.js:399:17)
    at VueComponent.Vue._render (http://127.0.0.1:46848/appservice/static/js/vendor.js:3785:22)
    at VueComponent.updateComponent (http://127.0.0.1:46848/appservice/static/js/vendor.js:2305:21)
    at Watcher.get (http://127.0.0.1:46848/appservice/static/js/vendor.js:2635:25)
    at new Watcher (http://127.0.0.1:46848/appservice/static/js/vendor.js:2624:12)
    at mountComponent (http://127.0.0.1:46848/appservice/static/js/vendor.js:2309:17)

console 里直接报错了,而 UserInfo 也没有正常渲染出来,why?

冷静分析

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
data() {
     return {
       result: Object
     };
   },

因为服务端返回的数据遵循标准 JSONApi 格式(有时候嵌套层级会比较深),而我这里想偷懒,就定义了一个 result,并没有定义 result 里的具体字段,并给他们赋值。

按照我的思路

目前只需要 nick_name 和 avatar 两个字段的值,而这两个字段嵌套的比较深,我不希望在 template 里写这样的绑定代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<template>

  <div class="user-container">
    <!--背景-->
    <image
      class="user-bg"
      mode="aspectFill"
      :src="result.data.attributes.avatar"
    >
    </image>
     <!--用户信息-->
    <div class="user-info">
      <!--头像-->
      <image
        class="user-avatar"
        :src="result.data.attributes.avatar"
      ></image>
      <!--昵称-->
      <div
        class="user-nickname"
      >{{result.data.attributes.nick_name}}
      </div>
    </div>
  </div>
</template>

太丑陋了!!!

于是,我在计算属性中定义了一个 userInfo() 方法,将 result.data.attributes 作为它的返回值,当 getUserInfo 方法获取到服务器上的数据后,进行一个this.result = res 操作,这样,计算属性 userInfo 依赖的 result 更新了,userInfo 也会更新,也就完成了UI的渲染。这一切是多么美好啊!

但是为什么没有按照我的剧本演呢?

这就涉及我的知识盲区了,Vue 是如何追踪数据变化,实现响应式编程的?

遇事不顺找 Google,这里我找到三篇比较有参考价值的文章:

第一篇文章提到了变化检测的问题,

受限于JS及废弃的Object.observe,Vue不能检测到对象属性的添加或删除。由于Vue会在初始化实例时对属性执行getter/setter转化的过程,所以属性必须在data对象上保存才能被转换,如此,才可以让它是响应的。例如: > new Vue({ > data:{ > a:1 > } > }) > /* < !-- vm.a 是响应的 --> */ > > vm.b = 2 > /* < !-- vm.b 是非响应的 --> */ >

Vue不允许在已创建的实例上动态添加新的根级响应式属性。但是可以使用Vue.set(object,key,value)方法将响应属性添加到嵌套的对象上: > /*< !-- 一定要在实例化之前添加! -- > */ > Vue.set(vm.someObject, 'b', 2) >

第二篇,也就是vue官方的说明:

还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除: > var vm = new Vue({ > data: { > a: 1 > } > }) > // `vm.a` 现在是响应式的 > > vm.b = 2 > // `vm.b` 不是响应式的 >

对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于: > var vm = new Vue({ > data: { > userProfile: { > name: 'Anika' > } > } > }) >

你可以添加一个新的 age 属性到嵌套的 userProfile 对象: > Vue.set(vm.userProfile, 'age', 27) >

最后捋一捋思路,为什么会发生错误呢?

当页面中的 image、div 渲染是,userInfo 数据肯定还没获取到,但是这时候 userInfo() 方法里 result.data.attrbutes 的 result.data 还没有定义,所以就会报错 Cannot read property 'attributes'

解决方案

别偷懒,按照后端返回的 JSON 的格式初始化 data 里的字段,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
data() {
      return {
        result: {
          data: {
            id: Number,
            type: String,
            attributes: Object
          }
        }
      };
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-07-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Vue的数据响应式原理
“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。
刘亦枫
2020/03/19
8600
vueweb端响应式布局_vue响应式原理图文详解「建议收藏」
Vue最显著的特性之一便是不太引人注意的响应式系统(reactivity system)。模型层(model)只是普通JS对象,修改它则更新视图(view)。这会让状态管理变得非常简单且直观,不过理解它的工作原理以避免一些常见的问题也是很重要的。
全栈程序员站长
2022/09/02
1.6K0
vue的双向绑定原理_vue2双向绑定原理
今天要讲的内容是Web前端框架vue.js中的一个细节,注意是细节哦,稍不留神就掉坑里了。
全栈程序员站长
2022/11/02
9390
vue的双向绑定原理_vue2双向绑定原理
深入浅出Vue响应式原理
Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接,不过理解其工作原理同样重要,这样你可以避开一些常见的问题。----官方文档 本文将针对响应式原理做一个详细介绍,并且带你实现一个基础版的响应式系统。本文的代码请猛戳Github博客
浪里行舟
2019/07/11
1K0
深入浅出Vue响应式原理
Vue基础:响应式
Vue不是框架(前端框架往往需要解决路由、试图管理、数据持久化等流程),Vue只关注视图层。
奋飛
2019/08/15
1.2K0
图解 Vue 响应式原理
最近部门分享,有同学提到了 Vue 响应式原理,大家在讨论时,发现一些同学对这一知识理解还不够深入,不能形成一个闭环,为了帮助大家理解这个问题,我重新过了一下 Vue 源码,并整理了多张流程图,便于大家理解。
全栈程序员站长
2022/08/12
2330
图解 Vue 响应式原理
探索 Vue.js 响应式原理
从字面意思可以看出,具有“响应式”特征的事物会根据条件变化,使得目标自动作出对应变化。比如在“响应式布局”中,页面根据不同设备尺寸自动显示不同样式。
pingan8787
2021/01/11
1.6K0
实现简易的 Vue 响应式
我们首先封装一个响应式处理的方法 defineReactive,通过 defineProperty 这个方法重新定义对象属性的 get 和 set 描述符,来实现对数据的劫持,每次 读取数据 的时候都会触发 get ,每次 更新数据 的时候都会触发 set ,所以我们可以在 set 中触发更新视图的方法 update 来实现一个基本的响应式处理。
PHP开发工程师
2022/03/23
4860
实现简易的 Vue 响应式
~<Strong>-要-模-拟</Strong> Vue 响应式原理
https://juejin.cn/post/6946120511713705992
zz_jesse
2021/05/07
5160
[Vue] v-model 绑定对象不实时更新
在最近参与的一个项目中,前端用到了 vue.js 框架,期间有个功能需要动态的向一个被绑定的对象中添加属性。但是在实际应用中问题出现了:在向对象中添加属性后,与对象绑定的组件内容却未发生变化,必须要再次刷新组件,其内容才会变为更改后的内容
做棵大树
2022/09/27
2.6K0
[Vue] v-model 绑定对象不实时更新
百行代码实现 Vue 2 响应式
如果他本来就是引用数据类型,通过上图可以看出在获取more.link的时候并没有触发 获取值这个操作,说明并没有监听到,还有就是在赋值时,类型为引用类型时就会发现没有响应式,所以这里可以使用递归进行处理,修改如下:
用户8087287
2022/10/31
8830
百行代码实现 Vue 2 响应式
【Vuejs】952- 一文带你了解vue2之响应式原理
在面试的过程中也会问到:请阐述vue2的响应式原理?,凡是出现阐述或者理解,一般都是知无不言言无不尽,知道多少说多少。接下来,我来谈谈自己的理解,切记不要去背,一定要理解之后,用自己的语言来描述出来。
pingan8787
2021/05/14
9770
【Vuejs】952- 一文带你了解vue2之响应式原理
Vue2和Vue3响应式原理实现的核心
Vue.js 是一个开源的渐进式 JavaScript 前端框架,主要用于构建用户界面和单页应用程序(SPA)。Vue.js 可以轻松地与其他库或现有项目集成使用,并被认为是开发响应式数据驱动的现代 Web 应用的一种有效方式。
九仞山
2023/10/14
8590
VUE源码解读之响应式系统及Watcher的调度实现
鉴于目前 vue3 还没有正式发布,而且 vue2 里面的一些实现思想还是很有参考价值的,于是这篇原理性讲解还是 vue2 的,希望对你有启发~
winty
2020/04/01
9700
从零手写 Vue 之响应式系统
之前的文章把响应式系统基本讲完了,没看过的同学可以看一下 vue.windliang.wang/。这篇文章主要是按照 Vue2 源码的目录格式和调用过程,把我们之前写的响应式系统移动进去。
windliang
2022/09/23
3070
从零手写 Vue 之响应式系统
Vue响应式系统原理并实现一个双向绑定
我们知道Dep.target在创建Watcher的时候是null,并且它只是起到一个标记的作用,当我们创建Watcher实例的时候,我们的Dep.target就会被赋值到Watcher实例,进而放入target栈中,我们这里调用的是pushTarget函数:
yyds2026
2022/10/19
3790
2. 「vue@2.6.11 源码分析」数据驱动视图(响应式)
vue 最核心的卖点是数据驱动和组件。浏览器原生提供的交互是通过dom api来修改dom元素,由于浏览器兼容性问题后面的框架如jquery对原生的api进行了一层的封装以屏蔽浏览器的差异性,但并未作出实质的改变。想想这个过程,通常是数据发生变化,js根据变化的情况进行判断而后操作dom。dom变动的本质实际根本上实际是由数据驱动,我在第一家公司数字政通(egova)首次接触了的此类框架knockout。
tinyant
2023/02/24
5770
2. 「vue@2.6.11 源码分析」数据驱动视图(响应式)
Vue.js-深入响应式原理
hello,各位宝宝,最近还好吗?最近生活平静吗?还是有惊喜?不管怎样,心态要平和。不管谁来,不管谁走,都是命运的安排~
用户3258338
2019/07/19
1.6K0
Vue.js-深入响应式原理
Vue响应式依赖收集原理分析-vue高级必备
在 Vue 的初始化阶段,_init 方法执行的时候,会执行 initState(vm) ,它的定义在 src/core/instance/state.js 中。在初始化 data 和 props option 时我们注意 initProps 和 initData 方法中都调用了 observe 方法。通过 observe (value),就可以将数据变成响应式。
yyds2026
2022/10/26
6010
vue源码分析-响应式系统(三)
在之前介绍数据代理章节,我们已经详细介绍过Vue数据代理的技术是利用了Object.defineProperty,Object.defineProperty让我们可以方便的利用存取描述符中的getter/setter来进行数据的监听,在get,set钩子中分别做不同的操作,达到数据拦截的目的。然而Object.defineProperty的get,set方法只能检测到对象属性的变化,对于数组的变化(例如插入删除数组元素等操作),Object.defineProperty却无法达到目的,这也是利用Object.defineProperty进行数据监控的缺陷,虽然es6中的proxy可以完美解决这一问题,但毕竟有兼容性问题,所以我们还需要研究Vue在Object.defineProperty的基础上如何对数组进行监听检测。
yyzzabc123
2022/10/17
4390
相关推荐
Vue的数据响应式原理
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验