放肆青春的博客
首页
前端
算法
网络
面试
技术
后端
运维
杂项
数据库
工具
网址
电脑
个人
文章
  • 分类
  • 标签
  • 归档
github (opens new window)
gitee (opens new window)

放肆青春

一个前端菜鸟的技术成长之路
首页
前端
算法
网络
面试
技术
后端
运维
杂项
数据库
工具
网址
电脑
个人
文章
  • 分类
  • 标签
  • 归档
github (opens new window)
gitee (opens new window)
  • 前端

    • 前端 概览
    • 前端汇总

    • front 博文

    • front 项目总结

    • front 高级

    • front tools

  • vue

    • vue 概览
    • vue 汇总

    • vue 博文

    • vue 项目总结

    • vue 高级

      • vue 原理汇总
      • vue3新特性
      • watch原理
      • computed原理
      • vue响应式原理
      • vue双向数据绑定
        • vue 双向数据绑定
          • 实现双向绑定流程
          • 原理图
          • 数据变化更新视图
          • 视图变化更新数据
          • 5.2 vue 监听数组变化三部曲
          • 5.1 直接给一个数组项赋值,Vue 能检测到变化吗?
          • Proxy 和 Object.defineProperty 的对比
          • 双向绑定方法
        • vue 单向数据流
          • 21. 怎样理解 Vue 的单向数据流?
        • v-model 解析
          • v-model 原理
          • 自定义组件支持 v-model
      • key原理
      • 虚拟DOM
      • diff算法
      • keep-alive原理
      • nextTick原理
      • scoped原理
      • vue模板编译
      • vue渲染机制
      • vue api原理
      • vue实例方法原理
      • vue源码
  • html

    • html 概览
    • html 汇总

    • html 博文

  • css

    • css 概览
    • css 汇总

    • css 博文

    • sass

    • less

  • js

    • javascript 概览
    • JS 汇总

    • ES6

    • JS 博文

    • JS 工具

  • node

    • node 概览
    • node 汇总

    • node 框架

    • node 博文

  • react

    • react 概览
    • react 汇总

    • react 博文

    • react 高级

  • 微信小程序

    • 微信小程序 概览
    • 微信小程序总结
    • 微信小程序文章
    • 微信小程序 博文

    • 微信小程序 高级

  • 微信公众号

    • 微信公众号 概览
    • 微信公众号总结
    • 微信公众号文章
  • 多端开发

    • 多端开发
    • dsbridge 概览
    • jsbridge 概览
    • webview
    • uniapp

      • uniapp 概览
    • taro

      • taro 概览
    • flutter

      • flutter 概览
      • flutter 环境搭建
    • electron

      • electron 概览
  • front
放肆青春
2020-07-09

vue双向数据绑定

# vue 双向数据绑定

概念:数据变化更新视图,视图变化更新数据

原理:vue 的双向绑定是由数据劫持结合发布者-订阅者模式实现的

# 实现双向绑定流程

  1. 监听器 Observer: new Vue()首先执行初始化,对 data 执行响应化处理,这个过程发生 Observe 中

用来劫持并监听所有属性,如果有变动的,就通知订阅者。

  1. 解析器 Compile:同时对模板执行编译,找到其中动态绑定的数据,从 data 中获取并初始化视图,这个过程发生在 Compile 中

可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

  1. 订阅者 Watcher:同时定义⼀个更新函数和 Watcher,将来对应数据变化时 Watcher 会调用更新函数

可以收到属性的变化通知并执行相应的函数,从而更新视图。

  1. 依赖收集器 Dep:由于 data 的某个 key 在⼀个视图中可能出现多次,所以每个 key 都需要⼀个管家 Dep 来管理多个 Watcher, 将来 data 中数据⼀旦发生变化,会首先找到对应的 Dep,通知所有 Watcher 执行更新函数

# 原理图

image

# 数据变化更新视图

数据劫持:

  1. vue2.0 通过 Object.defineProperty()来劫持对象属性的 setter 和 getter 操作,在数据变动时发布消息给订阅者,触发相应的监听回调。

  2. vue3.0 通过 Proxy 来劫持各个属性的 setter 和 getter 操作,在数据变动时发布消息给订阅者,触发相应的监听回调

# 视图变化更新数据

事件监听

  1. input 监听 input 事件;

  2. checkbox 、 radio、select 监听 change 事件;

# 5.2 vue 监听数组变化三部曲

  • 第一步:先获取原生 Array 的原型方法,因为拦截后还是需要原生的方法帮我们实现数组的变化。

  • 第二步:对 Array 的原型方法使用 Object.defineProperty 做一些拦截操作。

  • 第三步:把需要被拦截的 Array 类型的数据原型指向改造后原型。

# 5.1 直接给一个数组项赋值,Vue 能检测到变化吗?

由于 JavaScript 的限制,Vue 不能检测到以下数组的变动:

当你利用索引直接设置一个数组项时,例如: vm.items[indexOfItem] = newValue 当你修改数组的长度时,例如: vm.items.length = newLength

为了解决第一个问题,Vue 提供了以下操作方法:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue);
// vm.$set,Vue.set的一个别名
vm.$set(vm.items, indexOfItem, newValue);
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue);
1
2
3
4
5
6

为了解决第二个问题,Vue 提供了以下操作方法:
// Array.prototype.splice vm.items.splice(newLength)

# Proxy 和 Object.defineProperty 的对比

  1. Proxy 是直接代理对象;而 Object.defineProperty 只能劫持对象的属性,

  2. Proxy 能监听对象的新增和删除操作;Object.defineProperty 不能监听对象的新增和删除操作,通过 Vue.set()和 Vue.delete 来实现响应式的。

  3. Proxy 可以直接监听数组的变化;Object.defineProperty 本身是有监控数组下标变化的能力的,只是在 Vue 的实现中,从性能/体验的性价比考虑,没有使用

  4. Proxy 支持 13 种拦截操作,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的。

  5. Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的;而 Object.defineProperty 只能遍历对象属性直接修改;

  6. Proxy 兼容性差;Object.defineProperty 兼容性好,支持 IE9,

  7. Proxy 有性能问题但是有新标准性能红利,从长远来看,JS 引擎会继续优化 Proxy

提示

注意:Object.defineProperty 本身是可以监控到数组下标的变化的,但是在 Vue 中,从性能/体验的性价比考虑(如果数组中有成千上万的数据),尤大大就弃用了这个特性 Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。数组的索引也是属性,所以我们是可以监听到数组元素的变化的

# 双向绑定方法

实现数据绑定的做法有大致如下几种:

发布者-订阅者模式(backbone.js) 脏值检查(angular.js) 数据劫持(vue.js)

# vue 单向数据流

# 21. 怎样理解 Vue 的单向数据流?

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
有两种常见的试图改变一个 prop 的情形 :

  • 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
  data: function() {
    return {
      counter: this.initialCounter
    }
  }
1
2
3
4
5
6
  • 这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性
props: ['size'],
  computed: {
    normalizedSize: function() {
      return this.size.trim().toLowerCase()
    }
  }
1
2
3
4
5
6

# v-model 解析

v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。

语法糖,简单来说就是『便捷写法』。

v-model 是双向绑定:你可以用 v-model 指令在表单 <input>、<textarea> 及 <select>元素上创建双向数据绑定。

v-model 是单向数据流

单项数据流:子组件不能改变父组件传递给它的 prop 属性,推荐的做法是它抛出事件,通知父组件自行改变绑定的值。

『单向数据流』总结起来其实也就 8 个字:『数据向下,事件向上』。

# v-model 原理

# 自定义组件支持 v-model

在定义 vue 组件时,你可以提供一个 model 属性,用来定义该组件以何种方式支持 v-model。

model 属性本身是有默认值的:

// 默认的 model 属性
export default {
  model: {
    prop: "value", // 代表 v-model 绑定的prop名
    event: "input", // 代码 v-model 通知父组件更新属性的事件名
  },
};
1
2
3
4
5
6
7

此时的 v-model="foo" 就完全等价于 :value="foo" 加上 @input="foo = $event"

也可以自定义:

// 默认的 model 属性
export default {
  model: {
    prop: "ame",
    event: "zard",
  },
};
1
2
3
4
5
6
7

此时的v-model="foo" 就等价于 :ame="foo" 加上 @zard="foo = $event"。

自定义组件:

<template>
  <div>
    我们是TI{{ ame }}冠军
    <el-button @click="playDota2(1)">加</el-button>
    <el-button @click="playDota2(-1)">减</el-button>
  </div>
</template>
<script>
export default {
  props: {
    ame: {
      type: Number,
      default: 8,
    },
  },
  model: {
    // 自定义v-model的格式
    prop: "ame", // 代表 v-model 绑定的prop名
    event: "zard", // 代码 v-model 通知父组件更新属性的事件名
  },
  methods: {
    playDota2(step) {
      const newYear = this.ame + step;
      this.$emit("zard", newYear);
    },
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

使用该组件:

<template>
  <dota v-model="ti"></dota>
</template>
<script>
export default {
  data() {
    return {
      ti: 8,
    };
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12

参考:https://juejin.cn/post/7049135444310622245 (opens new window)

更新时间: 2/11/2022, 11:23:32 AM
vue响应式原理
key原理

← vue响应式原理 key原理→

最近更新
01
前端权限管理
02-24
02
vue2指令
02-24
03
vue2 hook
02-24
更多文章>
Theme by Vdoing | Copyright © 2019-2022 放肆青春
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式