前言

vue-next是Vue3的源码仓库,Vue3采用lerna做package的划分,而响应式能力@vue/reactivity被划分到了单独的一个package中。

如果我们想把它集成到React中,可行吗?来试一试吧。

使用示例

话不多说,先看看怎么用的解解馋吧。

// store.tsimport { reactive, computed, effect } from '@vue/reactivity';

export const state = reactive({  count: 0,});

const plusOne = computed(() => state.count + 1);

effect(() => {  console.log('plusOne changed: ', plusOne);});

const add = () => (state.count += 1);

export const mutations = {  // mutation  add,};

export const store = {  state,  computed: {    plusOne,  },};

export type Store = typeof store;
// Index.tsximport { Provider, useStore } from 'rxv'import { mutations, store, Store } from './store.ts'function Count() {  const countState = useStore((store: Store) => {    const { state, computed } = store;    const { count } = state;    const { plusOne } = computed;

    return {      count,      plusOne,    };  });

  return (    <Card hoverable style={{ marginBottom: 24 }}><h1>计数器h1><div className="chunk"><div className="chunk">store中的count现在是 {countState.count}div><div className="chunk">computed值中的plusOne现在是 {countState.plusOne.value}div><Button onClick={mutations.add}>addButton>div>Card>  );}

export default () => {  return (    <Provider value={store}><Count />Provider>  );};

可以看出,store的定义只用到了@vue/reactivity,而rxv只是在组件中做了一层桥接,连通了Vue3和React,然后我们就可以尽情的使用Vue3的响应式能力啦。

预览

gif

可以看到,完美的利用了reactive、computed的强大能力。

分析

从这个包提供的几个核心api来分析:

effect(重点)

effect其实是响应式库中一个通用的概念:观察函数,就像Vue2中的Watcher,mobx中的autorunobserver一样,它的作用是收集依赖

它接受的是一个函数,它会帮你执行这个函数,并且开启依赖收集,

这个函数内部对于响应式数据的访问都可以收集依赖,那么在响应式数据被修改后,就会触发更新。

最简单的用法

const data = reactive({ count: 0 })effect(() => {    // 就是这句话 访问了data.count    // 从而收集到了依赖    console.log(data.count)})

data.count = 1// 控制台打印出1

那么如果把这个简单例子中的

() => {    // 就是这句话 访问了data.count    // 从而收集到了依赖    console.log(data.count)}

这个函数,替换成React的组件渲染,是不是就能达成响应式更新组件的目的了?

reactive(重点)

响应式数据的核心api,这个api返回的是一个proxy,对上面所有属性的访问都会被劫持,从而在get的时候收集依赖(也就是正在运行的effect),在set的时候触发更新。

ref

对于简单数据类型比如number,我们不可能像这样去做:

let data = reactive(2)// ?oopsdata = 5

这是不符合响应式的拦截规则的,没有办法能拦截到data本身的改变,只能拦截到data身上的属性的改变,所以有了ref。

const data = ref(2)// ?okdata.value= 5

computed

计算属性,依赖值更新以后,它的值也会随之自动更新。其实computed内部也是一个effect。

拥有在computed中观察另一个computed数据、effect观察computed改变之类的高级特性。

实现

从这几个核心api来看,只要effect能接入到React系统中,那么其他的api都没什么问题,因为它们只是去收集effect的依赖,去通知effect触发更新。

effect接受的是一个函数,而且effect还支持通过传入schedule参数来自定义依赖更新的时候需要触发什么函数,如果我们把这个schedule替换成对应组件的更新呢?要知道在hook的世界中,实现当前组件强制更新可是很简单的:

useForceUpdate

export const useForceUpdate = () => {  const [, forceUpdate] = useReducer(s => s + 1, 0);  return forceUpdate;};

这是一个很经典的自定义hook,通过不断的把状态+1来强行让组件渲染。

rxv的核心api: useStore接受的也是一个函数selector,它会让用户自己选择在组件中需要访问的数据。

那么思路就显而易见了:

  1. selector包装在effect中执行,去收集依赖。
  2. 指定依赖发生更新时,需要调用的函数是当前正在使用useStore的这个组件的forceUpdate强制渲染函数。

这样不就实现了数据变化,组件自动更新吗?

简单的看一下核心实现

useStore和Provider

import React, { useContext } from 'react';import { useForceUpdate, useEffection } from './share';

type Selector = (store: T) => S;const StoreContext = React.createContext(null);const useStoreContext = () => {const contextValue = useContext(StoreContext);if (!contextValue) {throw new Error('could not find store context value; please ensure the component is wrapped in a ',    );  }return contextValue;};/** * 在组件中读取全局状态 * 需要通过传入的函数收集依赖 */export const useStore = (selector: Selector): S => {  const forceUpdate = useForceUpdate();  const store = useStoreContext();  const effection = useEffection(() => selector(store), {    scheduler: forceUpdate,    lazy: true,  });  const value = effection();  return value;};export const Provider = StoreContext.Provider;

这个option是传递给Vue3的effectapi,

scheduler规定响应式数据更新以后应该做什么操作,这里我们使用forceUpdate去让组件重新渲染。

lazy表示延迟执行,后面我们手动调用effection来执行

{  scheduler: forceUpdate,  lazy: true,}

再来看下useEffectionuseForceUpdate

import { useEffect, useReducer, useRef } from 'react';import { effect, stop, ReactiveEffect } from '@vue/reactivity';

export const useEffection = (...effectArgs: Parameters<typeof effect>) => {  // 用一个ref存储effection  // effect函数只需要初始化执行一遍  const effectionRef = useRef();if (!effectionRef.current) {    effectionRef.current = effect(...effectArgs);  }// 卸载组件后取消effectconst stopEffect = () => {    stop(effectionRef.current!);  };  useEffect(() => stopEffect, []);return effectionRef.current};export const useForceUpdate = () => {const [, forceUpdate] = useReducer(s => s + 1, 0);return forceUpdate;};

也很简单,就是把传入的函数交给effect,并且在组件销毁的时候停止effect而已。

流程

  1. 先通过useForceUpdate在当前组件中注册一个强制更新的函数。
  2. 通过useContext读取用户从Provider中传入的store。
  3. 再通过Vue的effect去帮我们执行selector(store),并且指定scheduler为forceUpdate,这样就完成了依赖收集。
  4. 那么在store里的值更新了以后,触发了scheduler也就是forceUpdate,我们的React组件就自动更新啦。

就简单的几行代码,就实现了在React中使用@vue/reactivity中的所有能力。

优点:

  1. 直接引入@vue/reacivity,完全使用Vue3的reactivity能力,拥有computed, effect等各种能力,并且对于Set和Map也提供了响应式的能力。后续也会随着这个库的更新变得更加完善的和强大。
  2. vue-next仓库内部完整的测试用例。
  3. 完善的TypeScript类型支持。
  4. 完全复用@vue/reacivity实现超强的全局状态管理能力。
  5. 状态管理中组件级别的精确更新。
  6. Vue3总是要学的嘛,提前学习防止失业!

缺点:

  1. 由于需要精确的收集依赖全靠useStore,所以selector函数一定要精确的访问到你关心的数据。甚至如果你需要触发数组内部某个值的更新,那你在useStore中就不能只返回这个数组本身。

举一个例子:

function Logger() {  const logs = useStore((store: Store) => {    return store.state.logs.map((log, idx) => (      <p className="log" key={idx}>        {log}p>    ));  });

  return (    <Card hoverable><h1>控制台h1><div className="logs">{logs}div>Card>  );}

这段代码直接在useStore中返回了整段jsx,是因为map的过程中回去访问数组的每一项来收集依赖,只有这样才能达到响应式的目的。

源码地址

https://github.com/sl1673495/react-composition-api

react usecontext_Vue3原理实战运用,我用40行代码把他装进了React做状态管理相关推荐

  1. 无人车系统(十一):轨迹跟踪模型预测控制(MPC)原理与python实现【40行代码】

    前面介绍的PID,pure pursuit方法,Stanley方法都只是利用当前的系统误差来设计控制器.人们对这些控制器的设计过程中都利用了构建模型对无人车未来状态的估计(或者说利用模型估计未来的运动 ...

  2. python人脸识别毕业设计-Python 40行代码实现人脸识别功能

    前言 很多人都认为人脸识别是一项非常难以实现的工作,看到名字就害怕,然后心怀忐忑到网上一搜,看到网上N页的教程立马就放弃了.这些人里包括曾经的我自己.其实如果如果你不是非要深究其中的原理,只是要实现这 ...

  3. 40行代码的人脸识别实践【转】

    转自:http://blog.csdn.net/xingchenbingbuyu/article/details/68482838?ref=myrecommend 版权声明:本文为博主原创文章,转载请 ...

  4. python爬上市公司信息_实战项目 1:5 行代码爬取国内所有上市公司信息

    实战项目 1:5 行代码爬取国内所有上市公司信息 Python入门爬虫与数据分析 在正式开始这门专栏课的学习之前,我们先来看一个简单的爬虫案例.兴趣是最好的老师,当你对爬虫产生兴趣的时候,才会更有动力 ...

  5. 人脸识别闸机python_Python 40行代码实现人脸识别功能

    前言 很多人都认为人脸识别是一项非常难以实现的工作,看到名字就害怕,然后心怀忐忑到网上一搜,看到网上N页的教程立马就放弃了.这些人里包括曾经的我自己.其实如果如果你不是非要深究其中的原理,只是要实现这 ...

  6. 王垠的「40 行代码」真如他说的那么厉害吗?

    "我有什么资格说话呢?如果你要了解我的本事,真的很简单:我最精要的代码都放在 GitHub 上了.但是除非接受过专门的训练,你绝对不会理解它们的价值.你会很难想象,这样一片普通人看起来像是玩 ...

  7. 王垠的「40 行代码」

    "我有什么资格说话呢?如果你要了解我的本事,真的很简单:我最精要的代码都放在 GitHub 上了.但是除非接受过专门的训练,你绝对不会理解它们的价值.你会很难想象,这样一片普通人看起来像是玩 ...

  8. python画人脸编程怎么写_Python 40行代码实现人脸识别功能

    前言 很多人都认为人脸识别是一项非常难以实现的工作,看到名字就害怕,然后心怀忐忑到网上一搜,看到网上N页的教程立马就放弃了.这些人里包括曾经的我自己.其实如果如果你不是非要深究其中的原理,只是要实现这 ...

  9. java selenium_java+selenium,40行代码完成支付宝账单爬取

    java+selenium,40行代码完成支付宝账单爬取 需要jar selenium-server-4.0.0-alpha-5.jar 需要驱动 chromedriver.exe 驱动需要和浏览器版 ...

最新文章

  1. 前台传list到后台_实学:Java开发自己的博客系统-第十八篇(后台侧边栏菜单)...
  2. Hystrix断路器(五)
  3. answer my questions from the book构建之法.
  4. qt获取开发板ip地址_qt获取网络ip地址的类
  5. Afterthought 原来是这样的啊。。。。
  6. cdr放大后内容消失了_今日推荐:AI智能图片清晰放大神器强势来袭,简直无敌了...
  7. mysql double 转 字符串_没想到!在MySQL数据库中的数据有这三种类型!
  8. Windows域策略 设置客户端服务启动状态 【全域策略生效】
  9. 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
  10. paddle serving
  11. 普罗米修斯 感染组合表
  12. 微信小程序布局及嵌套地图
  13. 【idea使用】主题文字修改
  14. 02组团队项目-Alpha冲刺-4/6
  15. mysql建表测试_测试必备mysql技能2:mysql建表
  16. 47、建筑设置避难间时,对避难间的要求
  17. Java Jsp+mysql实现企业财务管理系统(普通职工/管理员 员工、公司资产、经营、费用管理)
  18. ASP.NET Core 技术内幕与项目实战读后感
  19. 傻瓜式解决pycrypto安装错误
  20. 漏刻有时数据可视化Echarts组件开发(27):盒须图(箱线图)前后端php交互的实战案例

热门文章

  1. Simulink工作区无法保存To workspace模块的数据解决办法
  2. oracle 输出到页面,利用Xming 将Linux 图形界面输出到Windows上
  3. java eden space_JVM虚拟机20:内存区域详解(Eden Space、Survivor Space、Old Gen、Code Cache和Perm Gen)...
  4. 亿联本科java_厦门亿联2018面试题
  5. vue项目中使用mock(一)
  6. dw中html中无法使用js,在Dreamweaver中调用JavaScript行为
  7. 程序员如何快速成长为IT精英
  8. java+jsp+sqlserver 2008+Tomcat实现一个简单的搜索引擎
  9. 计算机仿真在机械应用,浅谈计算机仿真在机械的应用.doc
  10. call/apply以及this指向的理解