随着前端开发复杂度增加,原生开发模式显得越来越笨重,前端框架也层出不穷。

MVC 和 MVVM

MVC

MVC是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。

  • Model(模型):数据。
  • View(视图):用户界面。
  • Controller(控制器):业务逻辑。

MVVM

MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。

采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。

React Component class编程

React 是一个 用于构建用户界面的 JavaScript 库,注重于 View 层。

React Component 并没有严格的M,V区分,只是模糊的定义了几块内容:

  • state: 数据存放
  • render: 用户界面
  • setState | forceUpdate: 渲染用户界面

所以我们的代码逻辑是这样的:

  1. 定义state
  2. 根据state编写render
  3. render中加入事件,修改state,且渲染用户界面

以上1,2两步完成后,我们就不再需要关心render,因为render依赖state,我们只需要关心如何修改state,然后需渲染时,setState | forceUpdate就可以了。

生命周期 componentDidMount 也是很重要的,它再组件完成后只执行一次, 可以用于请求数据,然后设置state。

渲染页面(setState):state -> view。

书写思路清晰的代码,要清晰的知道数据的流向,我们这样设计。

  • 初始化阶段:框架自动渲染一次 -> componentDidMount -> 手动渲染
  • 用户操作:操作 -> 修改state -> 手动渲染

总结:写好render和state对应的规则后,只需要专心与如何修改state,然后执行渲染即可。

例子:列表请求

请求与请求参数的分离也是代码清晰程度的重要一部分。

setState最重要的还有第二个参数,是设置成功后的回调函数。React的state可以让我们专心开发某一块,例如我们写一个列表

state = {
page: 1,
dataList: null,
} // 请求列表
fetchDataList = () => {
const { page } = this.state;
let data = '通过page参数请求得到的数据'; // 通过请求得到数据
this.setState({ dataList: data });
} // 翻页
handlePageChange = (page) => {
this.setState({ page }, this.fetchDataList);
}

写一个请求方法,请求得到的参数完全从state中获取,得到数据后会setState渲染页面,所以我们只需要专心致志于设置state,在回调中发送请求。这样,一切都看起来那么清晰。

特殊使用

由于 state 是引用类型,所以我们可以使用 this.state.xx = xx 来修改数据,React 官方并不推荐此种修改方式,因为此方法并没有渲染页面,并不能直接的感受到数据的变化。

了解了 React 渲染机制后,只要清晰我们再做什么,也可以使用此种方法修改数据,并且大量能减少代码量。

例如:页面上有两个按钮,一个按钮记录此按钮点击次数,另一个按钮点击后,才会显示第一个按钮的点击次数。

使用常规setState方式,需要两个变量计数。

state = {
clickCount: 0;
viewCount: 0;
} btn1Click = () => {
this.setState({clickCount: this.state.clickCount +1 });
} btn2Click = () => {
this.setState({viewCount: this.state.clickCount });
} render() {
return <div>{this.state.viewCount}</div>
}

如果使用隐士赋值,只需要一个变量,并且再需要渲染的时候手动渲染。

state = {
count: 0;
} btn1Click: () => {
this.state.count++;
} btn2Click: () => {
this.forceUpdate(); // 强制渲染 相当于 this.setState({})
} render() {
return <div>{this.state.viewCount}</div>
}

当然,这种方式要在对 React 渲染机制清晰后再使用。

这就体现了React的灵活性,按需渲染。

React Hooks 函数式编程

React 16.7推出了 React Hooks 函数式编程。不用传统的类方式,写法大有不同。

首先看渲染机制,Component方式,渲染后,只执行了render方法,类里面的其他方法不会执行。而 React Hooks 函数式编程 每次渲染,都会把整个函数执行一遍,并提供了一个数据存放地 useState。

useState

// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);

setCount 用来设置 count 并且渲染页面,且只有这一种渲染方式,这就意味着,我们不能像 Component 那样灵活的按需渲染了。

useEffect

useEffect(function () {
// do sth..
}, [])

useEffect 第一个参数是一个函数,满足条件后会触发。第二个参数是个数组,如果是个空数组则只执行一次第一个参数函数(相当于componentDidMount),如果里面放变量,执行一次后,以后每次渲染后就监听变量有没有改变,如果改变就执行第一个函数。

与 class 方式的对比

对比 React.Component 和 React Hooks,它们都有存放数据的state,通过state渲染页面的render,和手动渲染的方法setState或者setXXX。

不同的是,React.Component有setState成功后的回调,React Hooks没有。

例如使用 React Hooks 执行下面代码

setCount(2);
console.log(count);

count拿到的总是设置前的值。

useState、useEffect代码设计

看到知乎上一句话:先做什么再做什么这种callback的写法是倾向于命令式,而使用hooks编写代码则更倾向于声明式.你不需要去指定你要的动作发生的时机, 而是声明一个条件或者依赖来让React来决定正确的执行时间点。

所以我们要转变思路,不要去控制何时渲染页面,因为每一次set都会渲染页面,需要的是在useEffect里写条件,让React自己决定渲染。

如请求改造如下

const [page, setPage] = useEffect(1);          // 请求参数 page
const [pageSize, setPageSize] = useEffect(20); // 请求参数 pageSize
const [type, setType] = useEffect(1); // 请求参数 type
const [dataList, setDataList] = useEffect(1); // 请求得到的数据 useEffect(function () {
fetchDataList();
}, [page, pageSize, type]); const fetchDataList = function () {
let data = '通过page pageSize type请求到的数据';
setDataList(data);
}

组件第一次执行或者page,pageSize,type改变,就会请求数据,然后set新数据渲染页面。

上面代码基本上满足了我们需要,然后在极端情况下,即使请求参数改变,也不需要发请求。对此我们需要另外设置一个变量控制是否发请求。

const [sendRequest, setSendRequest] = useEffect(0);  // 控制发请求

useEffect(function () {
fetchDataList();
}, [sendRequest]); const handlePageChange = (page) => {
setPage(page);
setSendRequest(Math.random());
}

但是这种写法还是运用了命令式,违背了React Hooks本意,不推荐。推荐规则写在useEffect中。

渲染优化

不管是 class 方式还是函数式编程,都需要关心一个问题:合理渲染。

class 方式在每次 setState 或者 forceUpdate 都会执行render函数渲染。

函数式编程方式 在useState中每次set新数据后,就会重新执行整个函数并渲染。

React 重要特征是,一般情况下,父组件渲染,子组件也会渲染。所以在顶层容器中,要合理渲染,尽可能的抽成更小的组件,防止不必要的渲染。

class 方式中,state只放与rander有关的变量,无关的可以放在class外,减少setState的使用。函数式编程一样,和return无关的变量可以放在函数外。

whosmeya.com

从 React 架构开始讲解 useState、useEffect 编程设计的更多相关文章

  1. react hooks 全面转换攻略(一) react本篇之useState,useEffect

    useState 经典案例: import { useState } from 'react'; function Example() { const [count, setCount] = useS ...

  2. helloworld讲解cocos2d-x的编程思路与要点

    用helloworld讲解cocos2d-x的编程思路与要点 本文以cocos2d-x的helloworld为例,讲解cocos2d-x引擎的特点和要点,2.2为了展示新功能,把包括屏幕自适应在内的新 ...

  3. react hooks & component will unmount & useEffect & clear up

    react hooks & component will unmount & useEffect & clear up useEffect & return === u ...

  4. useState & useEffect

    useState & useEffect https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/ https://react ...

  5. 【React】react学习笔记02-面向组件编程

    react学习笔记02-面向组件编程 面向组件编程,直白来说,就是定义组件,使用组件. 以下内容则简单介绍下组建的声明与使用,直接复制demo观测结果即可. 步骤: 1.定义组件   a.轻量组件-函 ...

  6. 从CompletableFuture到异步编程设计

    从CompletableFuture到异步编程设计,笔者就分为2部分来分享CompletableFuture异步编程设计,前半部分总结下CompletableFuture使用实践,后半部分分享下Com ...

  7. IT第十天 - String和StringBuffer的比较、编程设计技巧整理、本周总结 ★★★

    IT第十天 上午 String 1.String在进行多次的+扩展时,会严重的降低处理效率,因为String长度是不可变的,在进行+运算改变字符串时,会自动创建很多临时字符串,并不是在原字符串上追加, ...

  8. [原创].NET 分布式架构开发实战之二 草稿设计

    原文:[原创].NET 分布式架构开发实战之二 草稿设计 .NET 分布式架构开发实战之二 草稿设计 前言:本篇之所以称为草稿设计,是因为设计的都是在纸上完成的.反映了一个思考的过程. 本篇的议题如下 ...

  9. Android弹幕编程设计实现的解决方案(一)

     Android弹幕编程设计实现的解决方案(一) 在现在的一些视频类网站.视频类直播网站,比如A站和B站,当视频在播放的时候,会在屏幕上出现一些滚动的字幕,这些字幕是UGC,通常是用户的评论,称之 ...

随机推荐

  1. libevent(五)使用例子

    客户端: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/t ...

  2. Pandas切片操作:很容易忽视的SettingWithCopyWarning

    Pandas是一个强大的分析结构化数据的工具集,主要用于数据挖掘和数据分析,同时也提供数据清洗功能. 很多初学者在数据的选取,修改和切片时经常面临一些困惑.这是因为Pandas提供了太多方法可以做同样 ...

  3. 基于OpenCV的KNN算法实现手写数字识别

    基于OpenCV的KNN算法实现手写数字识别 一.数据预处理 # 导入所需模块 import cv2 import numpy as np import matplotlib.pyplot as pl ...

  4. [js进阶1]-数据类型

    基本数据类型 js 总的有7中数据类型,包括基本类型和引用类型 基本类型 6 种 number boolean string null undefiend symbol 前5种类型统称为原始类型 sy ...

  5. python地图投影转换

    一.投影包osr与proj4的使用 1.osr投影转换示例 from osgeo import osr,ogr#定义投影#wgs84source=osr.SpatialReference()sourc ...

  6. QML设计飘散效果

    1,目标及展示 首先希望实现文字.图片.控件等在触发后,呈现飘散并消失的效果.在QT例程<Qt Quick Particles Examples>是一个海星点击鼠标后呈现打散的效果,这个效 ...

  7. Spring Boot Admin简介及实践

    问题 在若干年前的单体应用时代,我们可以相对轻松地对整个业务项目进行健康检查.指标监控.配置管理等等项目治理.如今随着微服务的发展,我们将大型单体应用按业务模型进行划分,以此形成众多小而自治的微服务, ...

  8. Android广播机制(2)

    目录 发送自定义广播 发送标准广播 步骤 跨进程广播 步骤 发送有序广播 使用本地广播 实例 本地广播的优势 发送自定义广播 发送标准广播 步骤 1.定义一个广播接收器来接收此广播,新建MyBroad ...

  9. 800+Java后端经典面试题,希望你找到自己理想的Offer呀~

    前言 在茫茫的互联网海洋中寻寻觅觅,我收藏了800+道Java经典面试题,分享给你们.建议大家收藏起来,在茶余饭后拿出来读一读,以备未雨绸缪之需.另外,面试题答案的话,我打算后面慢慢完善在github ...

  10. tp5 一次性插入大量数据时分批处理

    如题,加入$arr 中有一万多条数据,如果直接使用insert插入的话就会报错,此时可以使用limit分批插入 $result = Db::connect($this->dbconfig()) ...