浅析 Vue 3.0 的组装式 API(一)


Posted in Javascript onAugust 31, 2020

(一)响应式数据

1. 简单例子

从最简单的数据绑定开始,在 Vue 2.0 中,我们这样将一个数据绑定到模板的指定位置:

在组件创建参数的 data 构造函数中返回一个用来绑定的数据对象,其中有个 now 字段,会被渲染到模板内的 .app > p 内。

<template>
  <div class="app">
    <h1>Hello world!</h1>
    <p>Now is: {{now.toString()}}</p>
  </div>
</template>

<script>
// Vue 2.0
export default {
  data() {
    return {
      now: new Date(),
    };
  },
};
</script>

用 Vue3 的组装 API 实现的话,则是这样:

// Vue 3.0
export default {
  setup() {
    return {
      now: new Date(),
    };
  },
};

2. 更新数据

奇怪,看起来好像没啥区别,只是把 data 改成了 setup 吗?

并不是,假如我们现在对这个 DEMO 做个小改动,让它每秒钟刷新一次时间,用 Vue2 大概是这样实现:

// Vue 2.0
export default {
  data() {
    return {
      now: new Date(),
    };
  },
  mounted() {
    setInterval(() => this.now = new Date(), 1000);
  },
};

而 Vue3 的等效实现则为:

// Vue 3.0
import { ref, onMounted } from 'vue';

export default {
  setup() {
    const now = ref(new Date());
    onMounted(() => {
      setInterval(() => now.value = new Date(), 1000);
    });
    return {
      now,
    };
  },
};

3. 对比分析

写了太多 Vue 的我们可能已经忘了,Vue2 的代码从标准 JS 模块的角度来看有多奇怪:

  • mounted 中修改的 this.now 数据是在哪创建的?我们在模块 default 对象的成员里并没有找到对应字段,倒是在 data 内返回的另一个对象中有这个字段;
  • data 中返回的 now 也不是真正的 this.now,而是 this.now 的初始值,在 data setInterval 修改 now 并不能更新渲染出来的时间;
  • 如果想复用这个数据和它的更新逻辑,你必须将这样的结构单独写一份,然后通过特殊的 mixin 函数混入到当前组件的构造参数内。

这一切,是因为整个模块 default 对象其实是 vm 对象的构造参数。其背后隐藏了对象的创建逻辑,在构造对象时构造参数中的一些不同层级的字段被绑定到了 vm 对象上。

不少新手可能都犯过一个错误,在 data 中返回的数据字段和 propsmethods 或者 computed 中的字段命名撞车(尤其是使用名为 data 的字段),在编码阶段并不能被 IDE 直接发现。就是因为上面的原因,这些字段创建时隶属于不同的位置,在之后构造时才被绑在了同一个对象上,导致了运行时才能发现的冲突。

Vue3 中,改成提供 refreactivetoRefonMounted 等函数的形式实现,例子中:

  • setup 中看到的 now 即是用于绑定的 this.now
  • 修改 now.value 即可看到页面状态的更新;
  • 如果要封装这份数据处理,只需要将 nowonMounted 处理提取到同一个函数内,再将 now 返回即可,不再需要黑盒的 mixin 处理。

可以说 Vue3 是直接将响应数据的创建决定权、生命周期的通知回调,都通过 API 的形式交给了开发者,更直观明了和可控。

4. API 说明

下面详细说说常用的几个响应式数据相关 API:ref, reactive toRefs

(1) ref

上面例子中使用到的 ref,可以将一个数据包装成响应式数据代理对象。

const count = ref(0);
console.log(count.value); // => 0

count.value++;
console.log(count.value); // => 1

当你修改代理对象的 count.value 属性时,模板中使用到 count 的位置将响应数据的变化,更新视图中的数据状态。

(2) reactive

对于对象的响应式封装,使用 ref 稍显麻烦:

const state = ref({
  count: 0,
});
console.log(state.value.count); // => 0

state.value.count++;
console.log(state.value.count); // => 1

这时可以改为使用 reactive,像操作普通对象的字段一样修改 count 即可更新视图:

const state = reactive({
  count: 0,
});
console.log(state.count); // => 0

state.count++;
console.log(state.count); // => 1

对代理对象 state 添加新的字段也可触发视图更新。

(3) toRefs

有时候,对象的名字过长,我们想直接在模板内使用对象内部字段,直接使用解构是不行的:

import { reactive } from 'vue';

export default {
  setup() {
    const position = reactive({
      x: 0,
      y: 0,
    });
    return {
      // 错误,解构出来的 x, y 并没有响应式代理。绑定到模板上后,数据变化无法触发视图更新
      ...position,
    };
  },
};

这个情况下,使用 toRefs 处理后再解构赋值即可:

import { reactive, toRefs } from 'vue';

export default {
  setup() {
    const position = reactive({
      x: 0,
      y: 0,
    });
    return {
      ...toRefs(position),
    };
  },
};

但需要注意,toRefs 只处理调用时 position 的现有字段,如果在之后对 position 增加新字段,将无法触发视图更新。

以上就是浅析 Vue 3.0 的组装式 API(一)的详细内容,更多关于Vue 组装式 API的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
jQuery 各种浏览器下获得日期区别
Dec 22 Javascript
Javascript 中介者模式实例
Dec 16 Javascript
基于jQuery试卷自动排版系统
Jul 18 Javascript
jquery 合并内容相同的单元格(示例代码)
Dec 13 Javascript
两种JS实现屏蔽鼠标右键的方法
Aug 20 Javascript
js的三种继承方式详解
Jan 21 Javascript
bootstrap为水平排列的表单和内联表单设置可选的图标
Feb 15 Javascript
利用jQuery异步上传文件的插件用法详解
Jul 19 jQuery
js防刷新的倒计时代码 js倒计时代码
Sep 06 Javascript
微信小程序使用progress组件实现显示进度功能【附源码下载】
Dec 12 Javascript
手写Node静态资源服务器的实现方法
Mar 20 Javascript
浅谈react-router@4.0 使用方法和源码分析
Jun 04 Javascript
vue中v-model对select的绑定操作
Aug 31 #Javascript
Vue v-for中的 input 或 select的值发生改变时触发事件操作
Aug 31 #Javascript
vue绑定数字类型 value为数字的实例
Aug 31 #Javascript
彻底搞懂并解决vue-cli4中图片显示的问题实现
Aug 31 #Javascript
详解Node.JS模块 process
Aug 31 #Javascript
谈谈JavaScript令人迷惑的==与+
Aug 31 #Javascript
vue自定义指令限制输入框输入值的步骤与完整代码
Aug 30 #Javascript
You might like
东方红 - 来复式再生机的修复
2021/03/02 无线电
PHP实现MVC开发得最简单的方法――模型
2007/04/10 PHP
php遍历文件夹和文件列表示例分享
2014/03/11 PHP
PHP文字转图片功能原理与实现方法分析
2017/08/31 PHP
JS禁用浏览器退格键实现思路及代码
2013/10/29 Javascript
node.js 使用ejs模板引擎时后缀换成.html
2015/04/22 Javascript
Javascript中String的常用方法实例分析
2015/06/13 Javascript
充分发挥Node.js程序性能的一些方法介绍
2015/06/23 Javascript
在AngularJS框架中处理数据建模的方式解析
2016/03/05 Javascript
AngularJs入门教程之环境搭建+创建应用示例
2016/11/01 Javascript
利用js+css+html实现固定table的列头不动
2016/12/08 Javascript
vue-router跳转页面的方法
2017/02/09 Javascript
JavaScript异步上传图片文件的实例代码
2017/07/04 Javascript
extract-text-webpack-plugin用法详解
2019/02/14 Javascript
JavaScript 性能提升之路(推荐)
2019/04/10 Javascript
详解JavaScript的数据类型以及数据类型的转换
2019/04/20 Javascript
Layui 导航默认展开和菜单栏选中高亮设置的方法
2019/09/04 Javascript
小程序实现按下录音松开识别语音
2019/11/22 Javascript
jQuery实现数字华容道小游戏(实例代码)
2020/01/16 jQuery
JS判断数组是否包含某元素实现方法汇总
2020/06/24 Javascript
[57:29]Alliance vs KG 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/17 DOTA
Python安装模块的常见问题及解决方法
2018/02/05 Python
pyinstaller参数介绍以及总结详解
2019/07/12 Python
Django项目后台不挂断运行的方法
2019/08/31 Python
PyTorch预训练的实现
2019/09/18 Python
python3中利用filter函数输出小于某个数的所有回文数实例
2019/11/24 Python
Windows10+anacond+GPU+pytorch安装详细过程
2020/03/24 Python
详解Pycharm第三方库的安装及使用方法
2020/12/29 Python
英国最大的美妆产品在线零售商之一:Beauty Bay
2017/09/29 全球购物
幼师自我鉴定
2014/02/01 职场文书
公司门卫的岗位职责
2014/02/19 职场文书
整顿机关作风心得体会
2014/09/10 职场文书
2015感人爱情寄语
2015/02/26 职场文书
史上最牛的辞职信
2015/02/28 职场文书
2016年社区党支部公开承诺书
2016/03/25 职场文书
Mysql中mvcc各场景理解应用
2022/08/05 MySQL