>[success] # 响应性API
~~~
1.目前'setup' 的返回对象参数可以直接渲染到模板上,问题是数据不是响应式,想让数据变成响应式
需要使用'3.x' 提供的响应式API
~~~
>[info] ## Ref
~~~
1.接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property .value
来理解官方这句话'ref() 返回的是value reference (包装对象)并且这个对象value属性指向包装的值让其具备了响应'
2.注意第一条说的是'value' 属性指向了包装对象并且是value具备了响应
2.1.数据操作的是使用是'xxx.value'
2.2.但在模板中简化了操作流程因此可以直接'xxx'不需要调用'value' 属性
3.一般定义基本类型的响应事数据使用'ref' 常见的基本类型'String','Number','BigInt','Boolean','Symbol','Null'
'Undefined'
4.如果你绑定的是对象非上面提出的基本类型他将会调用'reactive ',此时'.value'返回的是一个'proxy' 对象,
关于这一点稍后会详细解释,现在简单看一下如果"const per = ref({name:'z'})" 包裹的是对象相对的你的修改
你需要'per.value.name = "zz" '依然要通过'value' 并且此时'per.value' 是一个'prxoy'对象
5.注意下面案例中其实我们使用const定义参数也间接想表达'ref'包裹的基础类型已经是一个对象(是一个ref对象)
~~~
>[danger] ##### 案例
~~~
<template>
<div >
<button @click="print">触发事件</button>
{{str}}
<input v-model="str" />
</div>
</template>
<script>
import {ref,reactive} from 'vue'
export default {
name: 'testlison',
setup(prop, ctx) {
const str = ref('z')
const num = ref(1)
const boolean = ref(false)
const ull = ref(null)
const un = ref(undefined)
const print = ()=>{
str.value = "q"
num.value = 100
boolean.value = true
ull.value = {name:'zz'}
un.value = 1000
}
return { str,print}
},
~~~
>[danger] ##### 是所有情况下ref 在模板渲染时候都不用.value么
~~~
1.答案是否定的,只有当'ref'包裹的对象是直接从setup返回的可以省略'value',但是如果是嵌套的依然
需要你在模板中使用value属性
~~~
~~~html
<template>
<div>
<span>{{ count }}</span>
<button @click="count ++">Increment count</button>
<!-- 因为count ref对象被二次包裹在nested中因省略value 响应改变是不生效 -->
<!-- <button @click="nested.count ++">Nested Increment count</button> -->
<!-- 正确写法 -->
<button @click="nested.count.value ++">Nested Increment count</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return {
count,
nested: {
count
}
}
}
}
</script>
~~~
>[info] ## reactive
~~~
1.'reactive' 返回对象的响应式副本,并且他响应转换是'深层'的它影响所有嵌套 property,基于'Proxy'实现,
和 '2.x' 的 Vue.observable()等同
2.解释上面官方的话
2.1.定义对象 时候要使用我并且会返回一个'proxy'对象这个对象具有响应式
2.2.'深层'就是这个对象内部嵌套多深所有嵌套的属性都能影响到举个例子
"const obj =reactive( {name:'ww',per:{age:1100}})" 从obj的name属性到per甚至你的per.age属性都能被我代理了
并且无论你是delete删除还是说新增新的属性都可以被响应到,'reactive'的返回的'proxy'对象可以被响应,如果你操
作原对象不享受这个待遇
2.3.内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
3.现在可以在进一步理解'ref' 如果传值是对象内部会调用'reactive' 这一点了(注意说的是ref对对象会内部自动调用'reactive')
4.当你从组件中的data()返回一个对象时,它是由reactive()在内部做出反应的
~~~
>[danger] ##### 案例
~~~
<template>
<div >
<button @click="print">触发事件</button>
{{proxyObj.count}}
{{proxyObj.name}}
</div>
</template>
<script>
import {ref,reactive} from 'vue'
export default {
name: 'testlison',
setup(prop, ctx) {
// 要注意了你直接操作obj 是没有响应效果
const obj = {count:0}
// 你需操作我我是响应的
const proxyObj = reactive(obj)
const print = ()=>{
proxyObj.count ++
// 感动了吧以前你还需要$set 现在不用了
proxyObj.name = "ww"
}
return { proxyObj,print}
},
}
</script>
<style scoped lang="less"></style>
~~~
>[danger] reactive 包裹一个对象包含ref的对象 -- Ref Unwrapping(包装对象的自动展开)
~~~
1.当一个ref作为一个响应式对象的属性被访问或改变时,它会自动打开内部值,这样它的行为就像一个普通的属性
简单的说'当一个包装对象被作为另一个响应式对象的(属性)引用的时候也会被自动展开'
2.理解官方这句话就是 现在我定义了一个ref对象,'const count =ref(0)' =>我将ref对象做一个对象的属性
'const obj = {count}'=>此时我使用是'obj.count.value' 但是如果我用reactive 包裹着含有ref属性的对象即
'const state = reactive(obj)' 他会自动将这个value给打开使用效果变成'state.count'不用再加value属性了
在进一步抽象理解当还有ref属性的对象被reactive包裹实际上效果是'const state = reactive(count:count.value)'
const count = ref(0)
const obj = reactive({
count
})
console.log(obj.count) // 0
obj.count++
console.log(obj.count) // 1
console.log(count.value) // 1
count.value++
console.log(obj.count) // 2
console.log(count.value) // 2
~~~
~~~
<template>
<div>
<!-- count 和 rcount.count 同时都改变了 -->
<span>{{ count }}--{{rcount.count}}</span>
<button @click="print">Increment count</button>
</div>
</template>
<script>
import { ref ,reactive} from 'vue'
export default {
setup() {
const count = ref(0)
// 这里是{count}用一个对象吧包裹了ref
// 注意不是直接一个reactive 包裹count 错误写法reactiv(count)
const rcount = reactive({count})
const print=() =>{
rcount.count ++
}
return {
rcount,
count,
print
}
}
}
</script>
~~~
>[danger] ##### 非对象中的属性包含ref对象并且用reactive包裹
~~~
1.'当一个包装对象被作为另一个响应式对象的(属性)引用的时候也会被自动展开' 但是当从数组或本地集合类型(如
Map)访问ref时,不会执行展开操作
~~~
~~~
const books = reactive([ref('Vue 3 Guide')])
// need .value here 这里依旧需要value
console.log(books[0].value)
const map = reactive(new Map([['count', ref(0)]]))
// need .value here 这里依旧需要value
console.log(map.get('count').value)
~~~
>[danger] ##### 上面 reactive 包裹 ref 情况过于复杂我该怎么做
~~~
1.以上这些关于包装对象的细节可能会让你觉得有些复杂,但实际使用中你只需要记住一个基本的规则:
只有当你直接以变量的形式引用一个包装对象的时候才会需要用 .value 去取它内部的值 —— 在模版中你甚至
不需要知道它们的存在。
~~~
>[info] ## 现在对比reactive与ref-细节
~~~
1.ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式)
2.如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象
3.ref内部: 通过给value属性添加getter/setter来实现对数据的劫持
4.reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据
5.ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value)
~~~
>[info] ## ref 和 reactive 设计出现
~~~
1.我们知道在 JavaScript 中,原始值类型如 string 和 number 是只有值,没有引用的。如果在一个函数中返
回一个字符串变量,接收到这个字符串的代码只会获得一个值,是无法追踪原始变量后续的变化的
~~~
>[danger] ##### 我应该什么时候去使用这两个Api
~~~
1.最简单就是基本类型用'ref' ,引用类型用'reactive' ,并且这也是依赖于你的编程习惯,在定义变量的时候
是属于喜欢单独每一项定义还是使用用对象定义
~~~
* 举个例子单独每一项定义
~~~
import { ref, onMounted, onUnmounted } from "vue";
export function useMousePosition() {
const x = ref(0);
const y = ref(0);
function update(e) {
x.value = e.pageX;
y.value = e.pageY;
}
onMounted(() => {
window.addEventListener("mousemove", update);
});
onUnmounted(() => {
window.removeEventListener("mousemove", update);
});
return { x, y };
}
~~~
* 对象定义
~~~
function useMousePosition() {
const pos = reactive({
x: 0,
y: 0
});
// ...
return toRefs(pos);
}
~~~
[参考文章链接](https://www.danvega.dev/blog/2020/02/12/vue3-ref-vs-reactive/)
- Vue--基础篇章
- Vue -- 介绍
- Vue -- MVVM
- Vue -- 创建Vue实例
- Vue -- 模板语法
- Vue -- 指令用法
- v-cloak -- 遮盖
- v-bind -- 标签属性动态绑定
- v-on -- 绑定事件
- v-model -- 双向数据绑定
- v-for -- 只是循环没那么简单
- 小知识点 -- 计划内属性
- key -- 属性为什么要加
- 案例说明
- v-if/v-show -- 显示隐藏
- v-for 和 v-if 同时使用
- v-pre -- 不渲染大大胡语法
- v-once -- 只渲染一次
- Vue -- class和style绑定
- Vue -- filter 过滤器
- Vue--watch/computed/fun
- watch -- 巧妙利用watch思想
- Vue -- 自定义指令
- Vue -- $方法
- Vue--生命周期
- Vue -- 专属ajax
- Vue -- transition过渡动画
- 前面章节的案例
- 案例 -- 跑马灯效果
- 案例 -- 选项卡内容切换
- 案例-- 筛选商品
- 案例 -- 搜索/删除/更改
- 案例 -- 用computed做多选
- 案例 -- checked 多选
- Vue--组件篇章
- component -- 介绍
- component -- 使用全局组件
- component -- 使用局部组件
- component -- 组件深入
- component -- 组件传值父传子
- component -- 组件传值子传父
- component -- 子传父语法糖拆解
- component -- 父组件操作子组件
- component -- is 动态切换组件
- component -- 用v-if/v-show控制子组件
- component -- 组件切换的动画效果
- component -- slot 插槽
- component -- 插槽2.6
- component -- 组件的生命周期
- component -- 基础组件全局注册
- VueRouter--获取路由参数
- VueRouter -- 介绍路由
- VueRouter -- 安装
- VueRouter -- 使用
- VueRouter--router-link简单参数
- VueRouter--router-link样式问题
- VueRouter--router-view动画效果
- VueRouter -- 匹配优先级
- vueRouter -- 动态路由
- VueRouter -- 命名路由
- VueRouter -- 命名视图
- VueRouter--$router 获取函数
- VueRouter--$route获取参数
- VueRouter--路由嵌套
- VueRouter -- 导航守卫
- VueRouter -- 写在最后
- Vue--模块化方式结构
- webpack--自定义配置
- webpack -- 自定义Vue操作
- VueCli -- 3.0可视化配置
- VueCli -- 3.0 项目目录
- Vue -- 组件升级篇
- Vue -- 组件种类与组件组成
- Vue -- 组件prop、event、slot 技巧
- Vue -- 组件通信(一)
- Vue -- 组件通信(二)
- Vue -- 组件通信(三)
- Vue -- 组件通信(四)
- Vue -- 组件通信(五)
- Vue -- 组件通信(六)
- Vue -- bus非父子组件通信
- Vue -- 封装js插件成vue组件
- vue组件分装 -- 进阶篇
- Vue -- 组件封装splitpane(分割面板)
- UI -- 正式封装
- Vue -- iview 可编辑表格案例
- Ui -- iview 可以同时编辑多行
- Vue -- 了解递归组件
- UI -- 正式使用递归菜单
- Vue -- iview Tree组件
- Vue -- 利用通信仿写一个form验证
- Vue -- 使用自己的Form
- Vue -- Checkbox 组件
- Vue -- CheckboxGroup.vue
- Vue -- Alert 组件
- Vue -- 手动挂载组件
- Vue -- Alert开始封装
- Vue -- 动态表单组件
- Vue -- Vuex组件的状态管理
- Vuex -- 参数使用理解
- Vuex -- state扩展
- Vuex -- getters扩展
- Vuex--mutations扩展
- Vuex -- Action 异步
- Vuex -- plugins插件
- Vuex -- v-model写法
- Vuex -- 更多
- VueCli -- 技巧总结篇
- CLI -- 路由基础
- CLI -- 路由升级篇
- CLI --异步axios
- axios -- 封装axios
- CLI -- 登录写法
- CLI -- 权限
- CLI -- 简单权限
- CLI -- 动态路由加载
- CLI -- 数据性能优化
- ES6 -- 类的概念
- ES6类 -- 基础
- ES6 -- 继承
- ES6 -- 工作实战用类数据管理
- JS -- 适配器模式
- ES7 -- 装饰器(Decorator)
- 装饰器 -- 装饰器修饰类
- 装饰器--修饰类方法(知识扩展)
- 装饰器 -- 装饰器修饰类中的方法
- 装饰器 -- 执行顺序
- Reflect -- es6 自带版本
- Reflect -- reflect-metadata 版本
- 实战 -- 验证篇章(基础)
- 验证篇章 -- 搭建和目录
- 验证篇章 -- 创建基本模板
- 验证篇章 -- 使用
- 实战 -- 更新模型(为了迎合ui升级)
- 实战 -- 模型与接口对接
- TypeSprict -- 基础篇章
- TS-- 搭建(一)webpack版本
- TS -- 搭建(二)直接使用
- TS -- 基础类型
- TS -- 枚举类型
- TS -- Symbol
- TS -- interface 接口
- TS -- 函数
- TS -- 泛型
- TS -- 类
- TS -- 类型推论和兼容
- TS -- 高级类型(一)
- TS -- 高级类型(二)
- TS -- 关于模块解析
- TS -- 声明合并
- TS -- 混入
- Vue -- TS项目模拟
- TS -- vue和以前代码对比
- TS -- vue简单案例上手
- Vue -- 简单弄懂VueRouter过程
- VueRouter -- 实现简单Router
- Vue-- 原理2.x源码简单理解
- 了解 -- 简单的响应式工作原理
- 准备工作 -- 了解发布订阅和观察者模式
- 了解 -- 响应式工作原理(一)
- 了解 -- 响应式工作原理(二)
- 手写 -- 简单的vue数据响应(一)
- 手写 -- 简单的vue数据响应(二)
- 模板引擎可以做的
- 了解 -- 虚拟DOM
- 虚拟dom -- 使用Snabbdom
- 阅读 -- Snabbdom
- 分析snabbdom源码 -- h函数
- 分析snabbdom -- init 方法
- init 方法 -- patch方法分析(一)
- init 方法 -- patch方法分析(二)
- init方法 -- patch方法分析(三)
- 手写 -- 简单的虚拟dom渲染
- 函数表达解析 - h 和 create-element
- dom操作 -- patch.js
- Vue -- 完成一个minVue
- minVue -- 打包入口
- Vue -- new实例做了什么
- Vue -- $mount 模板编译阶段
- 模板编译 -- 分析入口
- 模板编译 -- 分析模板转译
- Vue -- mountComponent 挂载阶段
- 挂载阶段 -- vm._render()
- 挂载阶段 -- vnode
- 备份章节
- Vue -- Nuxt.js
- Vue3 -- 学习
- Vue3.x --基本功能快速预览
- Vue3.x -- createApp
- Vue3.x -- 生命周期
- Vue3.x -- 组件
- vue3.x -- 异步组件???
- vue3.x -- Teleport???
- vue3.x -- 动画章节 ??
- vue3.x -- 自定义指令 ???
- 深入响应性原理 ???
- vue3.x -- Option API VS Composition API
- Vue3.x -- 使用set up
- Vue3.x -- 响应性API
- 其他 Api 使用
- 计算属性和监听属性
- 生命周期
- 小的案例(一)
- 小的案例(二)-- 泛型
- Vue2.x => Vue3.x 导读
- v-for 中的 Ref 数组 -- 非兼容
- 异步组件
- attribute 强制行为 -- 非兼容
- $attrs 包括 class & style -- 非兼容
- $children -- 移除
- 自定义指令 -- 非兼容
- 自定义元素交互 -- 非兼容
- Data选项 -- 非兼容
- emits Option -- 新增
- 事件 API -- 非兼容
- 过滤器 -- 移除
- 片段 -- 新增
- 函数式组件 -- 非兼容
- 全局 API -- 非兼容
- 全局 API Treeshaking -- 非兼容
- 内联模板 Attribute -- 非兼容
- key attribute -- 非兼容
- 按键修饰符 -- 非兼容
- 移除 $listeners 和 v-on.native -- 非兼容
- 在 prop 的默认函数中访问 this -- ??
- 组件使用 v-model -- 非兼容
- 渲染函数 API -- ??
- Slot 统一 ??
- 过渡的 class 名更改 ???
- Transition Group 根元素 -- ??
- v-if 与 v-for 的优先级对比 -- 非兼容
- v-bind 合并行为 非兼容
- 监听数组 -- 非兼容