Le contenu de cet article concerne l'application multi-composants du framework réactif Pastate.js. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer
Il s'agit du premier tutoriel du. série de pâtes Chapitre 2, bienvenue pour y prêter attention et continuer à mettre à jour.
Dans ce chapitre, nous ajoutons plus d'informations à la structure d'état du chapitre précédent et utilisons plusieurs composants pour organiser l'application de collage.
Nous regroupons les données d'informations personnelles de base du chapitre précédent dans un objet avec l'attribut state.basicInfo
, et ajoutons l'attribut state
à address
pour enregistrer les informations d'adresse personnelle :
const initState = { basicInfo: { name: 'Peter', isBoy: true, age: 10 }, address: { country: 'China', city: 'Guangzhou' } }
En raison des limitations du langage JavaScript, Paste ne peut pas détecter l'ajout de nouvelles propriétés à l'objet via l'affectation pour convertir automatiquement les nouvelles propriétés en nœuds réactifs. Par conséquent, vous devez définir tous les attributs d'état que vous devez utiliser dans initState. Vous pouvez initialiser les valeurs d'attribut sur null ou sur un tableau vide. Ce qui suit est un mauvais exemple :
const initState = { basicInfo: ..., address: ... } const store = new Pastore(initState) const state = store.state state.hobby = 'coding' // 错误,state.hobby 属性不具有受 pastate 控制,不具有响应式特点
Même si cette fonctionnalité est prise en charge, il sera difficile pour les développeurs de comprendre pleinement la structure de l'État, ce qui rendra l'application difficile à développer et maintenir, nous devrions donc définir complètement la structure de l'état dans initState.
Nous utilisons d'abord un moyen simple et temporaire pour construire des sous-composants :
... /** @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> ) } }
Comme vous pouvez voir, le composant BasicInfoView fait directement référence à la valeur de store.state.basicInfo et le composant AddressView fait directement référence à la valeur de store.state.address. Modifiez ensuite le composant parent AppView d'origine, imbriquez ces deux sous-composants et ajoutez une méthode pour modifier la valeur de 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> ) } } ...
C'est fait ! Lançons-le :
Cliquez sur le bouton et tout semble bien ! Nous utilisons les outils de développement React de Chrome pour observer le rendu de chaque composant lorsque l'état change. Ouvrez les outils de développement du navigateur, sélectionnez la balise de réaction et cochez Mettre en surbrillance les mises à jour. Lorsque le composant est restitué, il sera encadré par une case colorée.
Essayons-le en cliquant sur le bouton decrease age
de la page. Le résultat du re-rendu du composant est le suivant :
Nous pouvons constater que lorsque seul state.basicInfo.age change, les trois composants AppView, BasicInfoView et AddressView seront restitués, même si les données référencées par AddressView n'a pas changé ! Il s'agit d'une situation courante dans le rendu multi-composants React. Lorsque les composants d'application sont simples et que les niveaux d'imbrication ne sont pas nombreux, nous n'aurons pas l'impression que ce mode aura un impact évident, mais lorsque la relation d'imbrication des composants d'application devient plus importante ; complexe Parfois, cela entraînera des risques de performance, et nous devons y prêter attention.
Introduisez d'abord les deux états différents dans le magasin : store.imState et store.state , vous peut essayer de comprendre :
store.imState est l'entité de données de l'état de l'application. Elle est gérée par coller en utilisant le mécanisme immuable. Le nœud est mis à jour, les "références" de tous les ancêtres du nœud seront mises à jour. Chaque valeur de nœud de imState est un type de wrapper (String, Number, Boolean, Object, Array) sauf null ou non défini.
store.state est le 响应式影子
de store.imState Vous pouvez directement attribuer et modifier n'importe quel nœud de store.state, et coller appliquera la modification. résultats dans le magasin .imState et déclenchement des mises à jour de manière asynchrone.
Ou simplifié aux deux points suivants :
store.imState est utilisé pour restituer la vue
store.state est utilisé pour manipuler des données
Ces deux concepts sont utiles pour les personnes qui n'ont pas utilisé Redux et qui n'ont pas compris les principes de vue.js. Cela peut être un peu difficile à comprendre. Mais ce n'est pas grave. Ne pas comprendre ces deux concepts ne vous empêche pas d'utiliser coller Vous ne pouvez pas du tout ressentir l'existence d'imState lorsque vous utilisez coller. L'idée du collage est d'encapsuler des concepts complexes afin que vous puissiez implémenter des fonctions complexes de manière simple.
Si vous souhaitez comprendre les principes détaillés du collage, vous pouvez consulter le chapitre sur les principes.
Lorsqu'un composant est connecté au magasin, le magasin transmettra imState aux accessoires du composant
.state, donc nous pouvons recevoir l'état dans les accessoires du composant AppView et changer la classe de base du composant AppView en composant pur de réaction PureComponent, activant ainsi l'effet de rendu à la demande du composant :
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 传递给子组件: <BasicInfoView state={state.basicInfo}/>
。对于不直接与 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 直接连接的子组件时,这种按需渲染的策略可以大幅提高应用的渲染性能。
从 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> ) } }
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 和视图进行太详细的划分。
同样,我们可以使用 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 管理框架
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!