本篇文章给大家分享的内容是关于Pastate.js 响应式框架之多组件应用 ,有着一定的参考价值,有需要的朋友可以参考一下
这是 pastate 系列教程的第二章,欢迎关注,持续更新。
这一章,我们在上一章的 state 结构中添加多一些信息,并用多个组件来组织 pastate 应用。
更新 state 结构
我们把上一章的个人基本信息数据包装为 state.basicInfo
属性的对象,并向 state
中添加 address
属性,保存个人地址信息:
const initState = { basicInfo: { name: 'Peter', isBoy: true, age: 10 }, address: { country: 'China', city: 'Guangzhou' } }
由于 JavaScript 语言的限制,pastate 不能检测到通过赋值来为对象添加新属性,以自动把新属性转化为响应式节点。所以你应该在 initState 中把需要用到的 state 属性都定义出来,把属性值初始化为 null 或空数组都是可以的。下面是个错误的例子:
const initState = { basicInfo: ..., address: ... } const store = new Pastore(initState) const state = store.state state.hobby = 'coding' // 错误,state.hobby 属性不具有受 pastate 控制,不具有响应式特点
即使支持这种特性,它也会使开发者难以完全把握 state 的结构,导致应用难以开发和维护,所以我们应该在 initState 里对 state 的结构进行完整的定义。
分别开发 basicInfo 和 address 的视图组件
我们先使用一种简单临时的方式来构建子组件:
... /** @type {initState} */ const state = store.state; class BasicInfoView extends Component { render(){ return ( <p style={{padding: 10, margin: 10}}> <strong>Basic info:</strong><br/> My name is {state.basicInfo.name}.<br/> I am a {state.basicInfo.isBoy == true ? "boy" : "girl"}.<br/> I am {state.basicInfo.age} years old.<br/> </p> ) } }
class AddressView extends Component { render(){ return ( <p style={{padding: 10, margin: 10}}> <strong>Address:</strong><br/> My country is {state.address.country}.<br/> My city is {state.address.city}.<br/> </p> ) } }
可以看到,BasicInfoView 组件直接引用 store.state.basicInfo 的值,AddressView 组件直接引用 store.state.address 的值。接着修改原来的 AppView 父组件,把这两个子组件嵌套进去,同时增加一个方法来修改 address.city
的值:
... class AppView extends Component { increaseAge(){ state.basicInfo.age += 1 } decreaseAge(){ state.basicInfo.age -= 1 } changeCity(){ state.address.city += '!' } render() { return ( <p style={{padding: 10, margin: 10, display: "inline-block"}}> <BasicInfoView /> <AddressView /> <button onClick={this.decreaseAge}> decrease age </button> <button onClick={this.increaseAge}> increase age </button> <button onClick={this.changeCity}> change city </button> </p> ) } } ...
完成!让我们运行一下:
点击按钮,看起来一切正常!我们通过 Chrome 的 react dev tools 来观察一下当 state 改变时,各个组件的渲染情况。打开浏览器的开发者工具,选择 react 标签,勾选上 Highlight Updates, 这时当组件重新渲染时,会被带颜色的方框框起来。
我们点击页面上 decrease age
按钮试试,组件重新渲染的结果如下:
我们可以发现,当只有 state.basicInfo.age 更改时,AppView、BasicInfoView 和 AddressView 3个组件都会被重新渲染,即使 AddressView 所引用的数据没有发生任何改变!这是 react 多组件渲染的通常情况,当应用组件简单、嵌套层级不多时,我们不会感觉到这种模式会带来什么明显的影响;但是当应用组件的嵌套关系变得比较复杂的时候,会带来性能隐患,我们需要来关注这个问题。
store.imState 与 store.state
先介绍一下 store 中的两个不同的 state:store.imState 和 store.state ,你可以尝试了解一下:
store.imState 是应用状态的数据实体,它被 pastate 使用 immutable 的机制进行管理,当节点的内容更新时,该节点的所有祖先的“引用”都会被更新。imState 的每个节点值除了 null 或 undefined 外,都是包装类型(String, Number, Boolean, Object, Array)。
store.state 是 store.imState 的
响应式影子
, 可以对 store.state 任何节点进行直接赋值修改,pastate 会把修改结果作用到 store.imState,并异步触发视图更新。
或者简化为以下两点:
store.imState 用来渲染视图
store.state 用来操作数据
这两个概念对于没有使用过 redux 和没了解过 vue.js 原理的人来说可能有点难以理解。不过没关系,不理解这两个概念并不妨碍你使用 pastate,你可以在使用 pastate 的过程中完全感觉不到 imState 的存在。pastate 的理念就是封装复杂概念,让你可以用一种简单的方式去实现复杂的功能。
如果你想要理解 pastate 的详细原理,可以查看原理章节。
使用 props 接收 imState,实现组件的按需渲染
当一个 component 与 store 连接时,store 会把 imState 传递到 component 的 props
.state 中,因此我们可以在 AppView 组件的 props 中接收 state,同时把 AppView 组件的基类改为 react 纯组件 PureComponent,这样就开启了组件按需渲染效果:
import React, { PureComponent } from 'react'; // 1. 改用 PureComponent 代替 Component ... class AppView extends PureComponent { // 1. 改用 PureComponent ... render() { /** @type {initState} */ let state = this.props.state; // 2. 从 props 接收 state return ( <p style={{padding: 10, margin: 10, display: "inline-block"}}> {/** 3. 把 state 的子节点传递给对于的子组件 */} <BasicInfoView state={state.basicInfo}/> <AddressView state={state.address}/> ... </p> ) } } ...
注意上面代码的第3点注释,我们把 state 数据的子节点通过 props 传递给子组件: dd060b8dbff0e68e466b47a5601465f4
。对于不直接与 store 直接连接的子组件,我们同样也需要修改为从
props 获取 state, 并把组件的基类改成 PureComponent:
class BasicInfoView extends PureComponent { // 1. 基类改为 PureComponent render(){ let state = this.props.state; // 2. 从 props 接收 state return ( <p style={{padding: 10, margin: 10}}> <strong>Basic info:</strong><br/> {/** 3. 这里的 state 是 basicInfo 对象 */} My name is {state.name}.<br/> I am a {state.isBoy == true ? "boy" : "girl"}.<br/> I am {state.age} years old.<br/> </p> ) } }
class AddressView extends PureComponent { // 1. 基类改为 PureComponent render(){ let state = this.props.state; // 2. 从 props 接收 state return ( <p style={{padding: 10, margin: 10}}> <strong>Address:</strong><br/> {/** 3. 这里的 state 是 address 对象 */} My country is {state.country}.<br/> My city is {state.city}.<br/> </p> ) } }
可以看到,分配到子组件的 props 中的 state 是 根state 的子节点。因此在 BasicInfoView 中的 this.props.state 是 basicInfo 对象, 而在 AddressView 中的 this.props.state 是 address 对象。
完成!我们来看看运行效果!
点击
decrease age
按钮或increase age
按钮,我们看到的组件重新渲染情况是:
点击
change city
按钮,我们看到的组件重新渲染情况是:
Amazing!可以看到当我们点击按钮改变 state 节点时,只有引用被改变的 state 节点的组件才会进行重新渲染, 我们成功地实现了多组件按需渲染的效果!当应用具有大量不与 store 直接连接的子组件时,这种按需渲染的策略可以大幅提高应用的渲染性能。
使用 imState 渲染视图的注意事项
从 props 中接收到的 state 的每个节点都是特殊的包装类型 , 当需要在 if(...)
语句或 ... ? A : B
使用其布尔值结果时, 需要使用 ==
进行显式比较来获取,如下
class BasicInfoView extends PureComponent { render() { /** @type {initState['basicInfo']} */ let state = this.props.state; return ( <p style={{ padding: 10, margin: 10 }}> {state.isBoy == true ? "boy" : "girl"} {/* 正确 */} {state.isBoy ? "boy" : "girl"} {/* 错误 */} {state.age != 0 ? "Not 0" : "0"} {/* 正确 */} {state.age ? "Not 0" : "0"} {/* 错误 */} </p> ) } }
了解 PureComponent
React 的 PureComponent 会在渲染前对新的 props / state 与老的 props / state 进行浅层比较( shallow comparison),仅当发现 props / state 发生改变时,才执行重新渲染。浅层比较即是比较 props / state 的根级属性值是否改变,如果属性值是数组 / 对象类型,比较的结果使其引用是否相等:
console.log(["a"] == ["a"]) // 结果是 false let a = ["a"] console.log(a == a) // 结果是 true
console.log({a: "a"} == {a: "a"}) // 结果是 false let a = {a: "a"} console.log(a == a) // 结果是 true
Pastate 符合 immutable data 规范的 state 数据,可以确保当某个 state 节点改变时,其祖先节点的引用都会进行更新,所以可以配合使用 PureComponent 实现高效的按需渲染。
按需渲染时需要对 state 的结构进行模块化设计,如果把所有的属性都放在 state 根节点上,就没法实现按需渲染了:
// 注意:这样的 state 设计无法实现子组件的按需渲染 initState = { name: 'Peter', isBoy: true, age: 10, country: 'China', city: 'Guangzhou' }
当然,只有当应用的 state 比较复杂且对 state 的操作比较繁多时候,才会体现按需渲染对性能的提升;当应用比较简单的时候,不一定要对 state 和视图进行太详细的划分。
子组件 state 的 intelliSense
同样,我们可以使用 jsDoc 注释让子组件中 state 的具有智能提示,如下:
class BasicInfoView extends PureComponent { render(){ /** @type {initState['basicInfo']} */ let state = this.props.state; ... } }
class AddressView extends PureComponent { render(){ /** @type {initState['address']} */ let state = this.props.state; ... } }
请使用 xxx['xxx'] 的格式指明对象的子节点: /** @type {initState['address']} */
。在 vs code 里,暂时无法使用 xxx.xxx 的嵌套格式指定一个变量的类型。
单实例子组件
如果某组件只在视图中出现一次,那么这种组件被称为单实例组件。这种组件可以把对子组件设计的 state 操作函数简单地封装在子组件内部,提高组件的内聚性,便于维护管理。下面以 BasicInfoView 为例,把操作按钮移入子组件,并把两个操作函数移入子组件:
... class BasicInfoView extends PureComponent { increaseAge(){ state.basicInfo.age += 1 } decreaseAge(){ state.basicInfo.age -= 1 } render(){ /** @type {initState['basicInfo']} */ let state = this.props.state; return ( <p style={{padding: 10, margin: 10}}> ... <button onClick={this.decreaseAge}> decrease age </button> <button onClick={this.increaseAge}> increase age </button> </p> ) } } ...
同样,你也可以对 AddressView 做类似的处理。
下一章, 我们将会介绍如何在 pastate 中渲染和操作 state 中的数组。
这是 pastate 系列教程的第二章,欢迎关注,持续更新。
这一章,我们在上一章的 state 结构中添加多一些信息,并用多个组件来组织 pastate 应用。
相关推荐:
Pastate.js 之响应式 react state 管理框架
以上是Pastate.js 响应式框架之多组件应用 的详细内容。更多信息请关注PHP中文网其他相关文章!

JavaScript是现代网站的核心,因为它增强了网页的交互性和动态性。1)它允许在不刷新页面的情况下改变内容,2)通过DOMAPI操作网页,3)支持复杂的交互效果如动画和拖放,4)优化性能和最佳实践提高用户体验。

C 和JavaScript通过WebAssembly实现互操作性。1)C 代码编译成WebAssembly模块,引入到JavaScript环境中,增强计算能力。2)在游戏开发中,C 处理物理引擎和图形渲染,JavaScript负责游戏逻辑和用户界面。

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。

Python更适合数据科学和自动化,JavaScript更适合前端和全栈开发。1.Python在数据科学和机器学习中表现出色,使用NumPy、Pandas等库进行数据处理和建模。2.Python在自动化和脚本编写方面简洁高效。3.JavaScript在前端开发中不可或缺,用于构建动态网页和单页面应用。4.JavaScript通过Node.js在后端开发中发挥作用,支持全栈开发。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

Dreamweaver CS6
视觉化网页开发工具

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

SublimeText3汉化版
中文版,非常好用