React 核心技术分析—让跨端动态化成为可能

简介: React 优势分析

一 前言

React 在跨端动态化方向上有着得天独厚的优势,这和 React 本身的设计是密不可分的,目前市面上有很多成熟的方案,比如:

1 React Native 是一个经典动态化方案。React Native 逻辑层是由 JS 处理的,而渲染是由 Native 完成的,这使得 React Native 能够保持 React 开发特性,同样又有原生渲染的性能。

2 Taro React 也是一个不错的跨端解决方案,Taro 3 由重编译,轻运行时,变成了轻编译,重运行时,这使得 React 运行时的代码能够真正的运行到由 Taro 构建的 web 或者小程序应用中。

3 目前很多大厂自己也有一套以 React 为 DSL 的动态化框架,它们有一个共性就是,保持 JSX 灵活的语法特性,不仅如此,还可以保持 React 的活性,也就是说,同时保持了和 React 一样的 api 设计和语法规范。

如上是 React 在跨端领域落地的方案,为什么 React 在跨端领域这么受欢迎呢?接下来我们具体展开说说。

二 React 技术核心优势分析

1 数据驱动模型

React 和 Redux 的数据通信架构模型都和 Flux 架构类似,我们先来看看 Flux ,Flux 是 Facebook 提出的一种前端应用架构模式,它本身并不是一个 UI 框架,而是一种以单向数据流为核心思想的设计理念。

在 Flux 思想中有三个组成部分,那就是 dispatcher,store,和 view。下面来看一下三者的职责。

  • dispatcher: 其中更改数据,分发事件,就是由 dispatcher 来实现的。
  • store : store 为数据层,负责保存数据,并且相应事件,更新数据源。
  • view :view 层可以订阅更新,当数据发生更新的时候,负责通知视图重新渲染 UI。

在 React 应用中,setState 为更改视图的工具,state 为数据层,view 为视图层,如果想要更改视图,那么通过 setState 改变 state ,在重新渲染组件得到新的 element ,接下来交给浏览器渲染就可以了。

这种数据驱动模式一定程度上,并不受到平台的影响,或者说受到平台影响较小,因为在整个数据流过程中,从 setState 的触发,到 state 改变,再到组件 render 得到 element,接下来形成新的虚拟 DOM ,都是在 js 层完成的,而最后涉及到渲染绘制的时候,在通过不同的平台做差异化处理就可以了,比如在 web 交给浏览器去绘制,在移动端交给 Native 去绘制。

1.png

2 从 JSX 到虚拟 DOM

JSX 的灵活性也是 React 在跨端领域备受欢迎的原因之一,JSX 会被 babel 编译成 React element 对象形式,也就是当我们在组件中写了一个页面结构,但是本质上就是一个对象结构。比如我们在页面中这么写:

/* 子组件 */
function Children(){
   
   
    return <div>子组件</div>
}
/* 父组件 */
function Index(){
   
   
    const element = <div>
        <p> hello,React </p>
        <Children />
    </div>
    console.log(element,'element')
    return element
}

我们先来看看经过 babel 和 createElement 处理后,会变成什么样子:

2.jpeg

通过上面可以看到当 Index 渲染的时候,会把当前组件视图层所有元素转换成 element 对象,最终形成 element 对象结构如下所示:

3.png

相比 template 模板形式,JSX 的优势非常明显:

第一个就是通过 JSX 方式,一些设计模式会变得非常灵活,比如组合模式,render props 模式,这些模式的本质就是更灵活的组装 element 对象。在 template 模式中,组合模式需要通过 slot 插槽来实现,如果多个 slot 嵌套的,会让 template 结构变得非常复杂,难以理解。

第二个原因就是 JSX 语法本质上就是 JS ,所以对于写法非常灵活,包括在视图层写判断,循环,抽象状态等,而 template 一般都会有写法的限制,比如 vue 中,必须遵循 vue 的模版逻辑,在微信小程序中亦是如此。

第三个就是 JSX 对于数据的预处理能力非常好,开发者可以在 render 函数中,对于数据进行格式化处理,比如一些来源于父组件的 props ,把数据处理成视图层需要的结构,再进行渲染。在 vue 可能会用过滤器或者计算属性解决这个问题。但是在小程序中会变得很棘手,有一些数据的处理,比如依赖小程序通过监听器的方式,对数据进行处理,然后通过 setData 的方式,将 props 中的数据,映射到 data 中,这无疑是一种性能上的浪费。

在 web 领域,因为驱动视图都是由 js 来完成的,所以少了很多通信成本,但是在跨端领域就不是这样子了,跨端无论是 webview 渲染还是 native 渲染都有一定的通信成本。

webview 渲染得益于微信小程序那套通信模型,一些数据需要通过桥的方式通信,而且在这其中,可能还需要将数据进行序列化之后传递,这其中的每个环节都需要通信成本,在微信小程序文档中,也对 setData 的都有严格的限定,也就是不建议频繁使用 setData ,同样也不建议将所有数据都 setData ,setData 只更新相关视图的部分。

Native 渲染,以 RN 为例子,通信模型下需要 C++ 做中间层,一方面与 Native 进行通信,另外一方面与 JS 进行通信,所以也会有一定的通信成本,通过渲染形成树结构,也是要通过通信方式传递给 Native ,Native 是根据这个进行渲染绘制的。

还有一点就是 template 需要由独立的模版解析器去解析,再转化成虚拟 DOM ,这样就多了一道工序。

JSX 和 template 相比流程:

4.png

如上说到了 JSX 的优势,我们再看一下 element 转化成 fiber 的流程。fiber 架构是 React 的核心,fiber 上保存了当前节点的信息,fiber 树能够直接反映出整个 React 应用的原貌,跨端应用也可以利用 React fiber 这一点,在跨端应用中,也可以存活 fiber 树,不过在浏览器端,fiber 上保存了真实 DOM 信息,但是在跨端应用中,就可以保存其他有关视图元素的信息。

element 转化成 fiber 后:

5.png

Taro 3 中可以选择运行时的 Vue 和 React 做基础开发框架,当选择 React 开发小程序后,整个应用还是能够正常运行 React 应用,保持 React 的活性,这是为什么呢?

虽然微信小程序是采用 webview 的方式,但是对于原生 DOM 的操作,小程序并没有给开发者开口子,也就是说小程序里如果想要使用 React 框架,就不能使用 DOM 的相关操作,也就不能直接操作 DOM 元素,既然不能操作 DOM ,那么 React fiber 如何处理的呢?

原来在 Taro React 中,会改变 reconciler 中涉及到 DOM 操作的部分,我们来看看部分改动:

taro-react/src/reconciler.ts

import Reconciler, {
    
     HostConfig } from 'react-reconciler'
/* 引入 taro 中兼容的 document 对象 */
import {
    
     document, TaroElement, TaroText } from '@tarojs/runtime'
const hostConfig = {
    
    
   /* 创建元素 */ 
   createInstance (type) {
    
    
    return document.createElement(type)
  },
   /* 创建文本 */
  createTextInstance (text) {
    
    
    return document.createTextNode(text)
  },
  /* 插入元素 */
  appendChild (parent, child) {
    
    
    parent.appendChild(child)
  },
  ...
}

这个是 taro 对 react-reconciler 的改动,在 react-reconciler 中,HostConfig 里面包含了所有有关真实 DOM 的操作,taro reconciler 会通过向 tarojs/runtime 引入 document 的方式,来劫持原生 DOM 中的 document,然后注入兼容好的方法,这样的话,当 fiber 操作 DOM 的时候,本质上是使用 Taro 提供了方法。

接下来 taro 就可以通过递归的方式插入自己伪造的 DOM 元素树,这样就可以在小程序中正常运行 React 框架了。

3 独立事件系统

React 做跨端还有一个好处,就是 React 有一套独立的事件系统,在我之前的文章中,也讲到过 React 事件系统的原理。React 事件系统的设计,能够把原生 DOM 元素和事件执行函数隔离开来,统一管理事件,这样事件的触发,由 DOM 层面变成了 JS 层面。为 React 做跨平台兼容提供了技术支撑。

上面说到了 React 的事件系统做的很好的一点就是,事件并非直接绑定在原生的 DOM 上,而是由 React 的事件系统统一控制。这样在跨端应用中,就可以有不同的平台独立处理这些事件。

三 React 能为跨端动态化做些什么?

React 到底了跨端动态化做了些什么呢? 我们来从内到外来分析一下:

1 React 语法做 DSL

React 对跨端领域的贡献。第一种就是以 React 作为 DSL 的跨端方案,对于 DSL 可能有些同学比较陌生,什么是 DSL 。

DSL 即「Domain Specific Language」,中文一般译为领域特定语言。

这种方案保留了 React 的语法,比如说 JSX 和 React 完全一致,包括有渲染函数 render,触发更新的方法 setState 等,但是不具有 React 的活性,也就是形似神不似,其内部并不是由 React 系统驱动,这种方案,最后会被编译成 JS 文件,接下来只要由 JS 引擎解析 JS,再由端上进行绘制就可以了。

以 React 作为 DSL 的应用,有一个好处就是不需要预编译处理。

比如微信小程序有自己 wxml,但是 wxml 只是一个模板,最后想要被 JS 识别,就必须进行预编译,编译成 JS 能够识别的抽象语法树形式。

还有一个优点是 React DSL 一般都采用 css in js 作为样式处理方法,所以也不需要用专门的解释器去解析 css 样式。

但是这种方案也有一些弊端,就是 React 的一些新特性或者新功能,可能无法正常使用,比如说 hooks,Suspense 等。

2 保留 React 运行时

还有一种就是类似 Taro 的解决方案,用 React 做为跨端方案,不仅仅是神似,而且还是形似,也就是 React 完全应用于运行时,这依赖于 React 框架一些良好的特性,比如 react-reconciler 对 DOM 方法的隔离 hostConfig,或者独立的事件系统等。

但是也需要对跨端做一些兼容处理,比如像 Taro react 中对 reconciler 的兼容。

这种方式因为是 React 运行时,所以可以用 React 提供的 API,也能使用 React 的特性。

3 React 新领域延伸

还有就是像 React Native ,本质上是官方 React 在跨端领域的解决方案。react 在 web 端用的是 ReactDOM.render 或者 createRoot 创建整个应用,在 RN 中则是用的是 AppRegistry.registerComponent 。

React Native 像是 web 的功能,在 Native 端重新实现一遍。

四 总结

本文介绍了 React 做跨端动态化的一些优势分析,列举出一些经典通过 React 实现跨端动态化的案例,浅析了原理,感兴趣的同学可以尝试一下用 RN ,Taro 等技术从零到一开发一个简单的移动端应用,相信会有很多收获。

相关文章
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
223 2
|
2月前
|
Rust 前端开发 JavaScript
前端技术新探索:从React到WebAssembly的高效之路
前端技术新探索:从React到WebAssembly的高效之路
88 1
|
2月前
|
前端开发 JavaScript Android开发
Flutter 与 React Native - 详细深入对比分析(2024 年)
Flutter和React Native是两大跨平台框架,各有优缺点。Flutter性能优越,UI灵活,使用Dart;React Native生态广泛,适合JavaScript开发。
671 5
Flutter 与 React Native - 详细深入对比分析(2024 年)
|
2月前
|
JavaScript 前端开发 算法
前端优化之超大数组更新:深入分析Vue/React/Svelte的更新渲染策略
本文对比了 Vue、React 和 Svelte 在数组渲染方面的实现方式和优缺点,探讨了它们与直接操作 DOM 的差异及 Web Components 的实现方式。Vue 通过响应式系统自动管理数据变化,React 利用虚拟 DOM 和 `diffing` 算法优化更新,Svelte 通过编译时优化提升性能。文章还介绍了数组更新的优化策略,如使用 `key`、分片渲染、虚拟滚动等,帮助开发者在处理大型数组时提升性能。总结指出,选择合适的框架应根据项目复杂度和性能需求来决定。
|
2月前
|
前端开发 JavaScript 开发者
探索现代Web前端技术:React框架入门
【10月更文挑战第9天】 探索现代Web前端技术:React框架入门
|
2月前
|
前端开发 数据安全/隐私保护
前端技术实战:React Hooks 实现表单验证
【10月更文挑战第1天】前端技术实战:React Hooks 实现表单验证
|
4月前
|
存储 JavaScript 前端开发
探索React状态管理:Redux的严格与功能、MobX的简洁与直观、Context API的原生与易用——详细对比及应用案例分析
【8月更文挑战第31天】在React开发中,状态管理对于构建大型应用至关重要。本文将探讨三种主流状态管理方案:Redux、MobX和Context API。Redux采用单一存储模型,提供预测性状态更新;MobX利用装饰器语法,使状态修改更直观;Context API则允许跨组件状态共享,无需第三方库。每种方案各具特色,适用于不同场景,选择合适的工具能让React应用更加高效有序。
93 0
|
4月前
|
移动开发 前端开发 JavaScript
使用React Native进行跨平台移动开发:技术探索与实践
【8月更文挑战第10天】React Native以其跨平台、高性能、易学习等优势,在移动开发领域取得了显著的成果。通过合理使用React Native,开发者可以更加高效地开发出高质量、低成本的移动应用。然而,在享受React Native带来的便利的同时,我们也需要关注其潜在的挑战和限制,并通过不断学习和实践来提升我们的开发能力。
|
4月前
|
前端开发 Java Spring
Spring与Angular/React/Vue:当后端大佬遇上前端三杰,会擦出怎样的火花?一场技术的盛宴,你准备好了吗?
【8月更文挑战第31天】Spring框架与Angular、React、Vue等前端框架的集成是现代Web应用开发的核心。通过RESTful API、WebSocket及GraphQL等方式,Spring能与前端框架高效互动,提供快速且功能丰富的应用。RESTful API简单有效,适用于基本数据交互;WebSocket支持实时通信,适合聊天应用和数据监控;GraphQL则提供更精确的数据查询能力。开发者可根据需求选择合适的集成方式,提升用户体验和应用功能。
101 0
|
4月前
|
开发者 缓存 数据库
【性能奇迹】Wicket应用的极速重生:揭秘那些让开发者心跳加速的调优秘技!
【8月更文挑战第31天】在软件开发中,性能优化是确保应用快速响应和高效运行的关键。本书《性能调优:Apache Wicket应用的速度提升秘籍》详细介绍了如何优化Apache Wicket应用,包括代码优化、资源管理、数据库查询优化、缓存策略及服务器配置等方面。通过减少不必要的组件渲染、优化SQL查询、使用缓存和调整服务器设置等方法,本书帮助开发者显著提升Wicket应用的性能,确保其在高并发和数据密集型场景下的稳定性和响应速度。
48 0