浅析 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 相关文章推荐
JavaScript学习笔记整理_简单实现枚举类型,扑克牌应用
Sep 19 Javascript
Angular.js中用ng-repeat-start实现自定义显示
Oct 18 Javascript
bootstrap select插件封装成Vue2.0组件
Apr 17 Javascript
文本溢出插件jquery.dotdotdot.js使用方法详解
Jun 22 jQuery
jQuery实现滚动效果
Nov 17 jQuery
vue下拉菜单组件(含搜索)的实现代码
Nov 25 Javascript
elementUI vue this.$confirm 和el-dialog 弹出框 移动 示例demo
Jul 03 Javascript
p5.js码绘“跳动的小正方形”的实现代码
Oct 22 Javascript
js实现移动端吸顶效果
Jan 08 Javascript
JavaScript常用进制转换及位运算实例解析
Oct 14 Javascript
解决pycharm双击但是无法打开的情况
Oct 31 Javascript
低门槛开发iOS、Android、小程序应用的前端框架详解
Oct 16 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
一个显示某段时间内每个月的方法 返回由这些月份组成的数组
2012/05/16 PHP
关于PHP结束标签的使用细节探讨及联想
2013/03/04 PHP
PHP的Yii框架中过滤器相关的使用总结
2016/03/29 PHP
CI框架整合smarty步骤详解
2016/05/19 PHP
Laravel框架源码解析之反射的使用详解
2020/05/14 PHP
再谈javascript图片预加载技术(详细演示)
2011/03/12 Javascript
JavaScript call apply使用 JavaScript对象的方法绑定到DOM事件后this指向问题
2011/09/28 Javascript
让AJAX不依赖后端接口实现方案
2012/12/03 Javascript
JS版的date函数(和PHP的date函数一样)
2014/05/12 Javascript
express的中间件cookieParser详解
2014/12/04 Javascript
轻松实现Bootstrap图片轮播
2020/04/20 Javascript
jQuery实现可展开折叠的导航效果示例
2016/09/12 Javascript
JavaScript的new date等日期函数在safari中遇到的坑
2016/10/24 Javascript
第一次记录Bootstrap table学习笔记(1)
2017/05/18 Javascript
js HTML5 canvas绘制图片的方法
2017/09/08 Javascript
bootstrap fileinput插件实现预览上传照片功能
2018/01/23 Javascript
vue动态子组件的两种实现方式
2019/09/01 Javascript
jQuery+css实现的点击图片放大缩小预览功能示例【图片预览 查看大图】
2020/05/29 jQuery
Python进行数据提取的方法总结
2016/08/22 Python
python实现比较文件内容异同
2018/06/22 Python
Python统计纯文本文件中英文单词出现个数的方法总结【测试可用】
2018/07/25 Python
python 简单照相机调用系统摄像头实现方法 pygame
2018/08/03 Python
详解python Todo清单实战
2018/11/01 Python
Python for循环搭配else常见问题解决
2020/02/11 Python
pandas 强制类型转换 df.astype实例
2020/04/09 Python
解决pycharm安装第三方库失败的问题
2020/05/09 Python
浅谈anaconda python 版本对应关系
2020/10/07 Python
使用BeautifulSoup4解析XML的方法小结
2020/12/07 Python
Pandas DataFrame求差集的示例代码
2020/12/13 Python
Html5上传图片 移动端、PC端通用代码
2016/06/08 HTML / CSS
文秘求职信范文
2014/04/10 职场文书
健康教育评估方案
2014/05/25 职场文书
分公司总经理岗位职责
2014/08/03 职场文书
政风行风评议工作总结
2014/10/21 职场文书
2014年建筑工作总结
2014/11/26 职场文书
体育委员竞选稿
2015/11/21 职场文书