第三节:react脚手架的使用及类组件、函数组件、生命周期剖析
一. react脚手架
1. 什么是脚手架
编程中提到的脚手架(Scaffold),其实是一种工具,帮我们可以快速生成项目的工程化结构;
🚩每个项目作出完成的效果不同,但是它们的基本工程化结构是相似的;
🚩既然相似,就没有必要每次都从零开始搭建,完全可以使用一些工具,帮助我们生产基本的工程化模板;
🚩不同的项目,在这个模板的基础之上进行项目开发或者进行一些配置的简单修改即可;
🚩这样也可以间接保证项目的基本机构一致性,方便后期的维护;
总结:脚手架让项目从搭建到开发,再到部署,整个流程变得快速和便捷。
2. 常用框架的脚手架
对于现在比较流行的三大框架都有属于自己的脚手架:
(1) Vue的脚手架:@vue/cli(基于webpack) 或者基于vite
(2) Angular的脚手架:@angular/cli
(3) React的脚手架:create-react-app
它们的作用都是帮助我们生成一个通用的目录结构,并且已经将我们所需的工程环境配置好,上述三个框架的脚手架除了vue可以基于vite,其它两个都是基于webpack来构建的。
3. react脚手架安装和使用
(1). 安装node环境,建议 16+
(2). 运行指令全局进行安装 【npm install create-react-app -g】
(3). 运行指令 【create-react-app xxx】 ,其中xxx代表项目名称,里面不能有大写
4. 项目目录分析
5. 了解PWA
(1).整个目录结构都非常好理解,只是有一个PWA相关的概念:
PWA全称Progressive Web App,即渐进式WEB应用;
一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用;
随后添加上 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能;
这种Web存在的形式,我们也称之为是 Web App;
(2).PWA解决了哪些问题呢?
可以添加至主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏;
实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能;
实现了消息推送;
等等一系列类似于Native App相关的功能;
(3).更多PWA相关的知识,可以自行去学习更多;
https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps
6. 脚手架中webpack
(1).React脚手架默认是基于Webpack来开发的; 但是,很奇怪:我们并没有在目录结构中看到任何webpack相关的内容?
原因是React脚手架将webpack相关的配置隐藏起来了(其实从Vue CLI3开始,也是进行了隐藏);
(2) 如果我们希望看到webpack的配置信息,应该怎么来做呢?
我们可以执行一个package.json文件中的一个脚本:"eject": "react-scripts eject"; 这个操作是不可逆的,所以在执行过程中会给与我们提示; 脚手架中的webpack 【npm run eject】
二. 类组件/函数组件
1. 组件相关概念
(1). 组件化思想的应用
有了组件化的思想,我们在之后的开发中就要充分的利用它。
尽可能的将页面拆分成一个个小的、可复用的组件。
这样让我们的代码更加方便组织和管理,并且扩展性也更强。
(2). 分类
React的组件相对于Vue更加的灵活和多样,按照不同的方式可以分成很多类组件:
根据组件的定义方式,可以分为:函数组件(Functional Component )和类组件(Class Component);
根据组件内部是否有状态需要维护,可以分成:无状态组件(Stateless Component )和有状态组件(Stateful Component);
根据组件的不同职责,可以分成:展示型组件(Presentational Component)和容器型组件(Container Component);
(3). 用途
这些概念有很多重叠,但是他们最主要是关注数据逻辑和UI展示的分离:
函数组件、无状态组件、展示型组件主要关注UI的展示;
类组件、有状态组件、容器型组件主要关注数据逻辑;
2. 类组件
(1).类组件的定义有如下要求:
组件的名称是大写字符开头(无论类组件还是函数组件)
类组件需要继承自 React.Component (或者 PureComponent)
类组件必须实现render函数
(2). 在ES6之前,可以通过create-react-class 模块来定义类组件,但是目前官网建议我们使用ES6的class类定义。
使用class定义一个组件:
A. constructor是可选的,我们通常在constructor中初始化一些数据;
B. this.state中维护的就是我们组件内部的数据;
C. render() 方法是 class 组件中唯一必须实现的方法;
(3). 返回值
当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:
A. React 元素:
通常通过 JSX 创建 (通过jsx编写的代码就会被编译成React.createElement, 所以返回的就是一个React元素)。
例如,<div /> 会被 React 渲染为 DOM 节点,<MyComponent /> 会被 React 渲染为自定义组件;
无论是 <div /> 还是 <MyComponent /> 均为 React 元素。
B. 数组或 fragments:使得 render 方法可以返回多个元素。
C. Portals:可以渲染子节点到不同的 DOM 子树中。
D. 字符串或数值类型:它们在 DOM 中会被渲染为文本节点
E. 布尔类型或 null:什么都不渲染。
查看代码
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = { msg: "hello ypf" };
}
render() {
//返回值说明
// 1. react元素
// return <div>App</div>;
// 2.组件或者fragments(后续学习)
// return ["abc", "cba", "nba"]
return [<h1>h1元素</h1>, <h2>h2元素</h2>, <div>哈哈哈</div>];
// 3. 字符串和数字类型
// return "hello ypf1";
// return true;
}
}
export default App;
3. 函数组件
(1).函数组件是使用function来进行定义的函数,只是这个函数会返回和类组件中render函数返回一样的内容。
(2).函数组件有自己的特点(当然,后面还有hooks,就不一样了):
A. 没有生命周期,也会被更新并挂载,但是没有生命周期函数;
B. this关键字不能指向组件实例(因为没有组件实例);
C. 没有内部状态(state);
// 函数组件
function App() {
// 返回值和类组件中的render是一致的
return <h1>我是函数组件</h1>;
}
export default App;
四. 生命周期
1. 常用生命周期函数
A. componentDidMount函数:组件已经挂载到DOM上时,就会回调;
B. componentDidUpdate函数:组件已经发生了更新时,就会回调;
C. componentWillUnmount函数:组件即将被移除时,就会回调;
注:谈React生命周期时,主要谈的类的生命周期,因为函数式组件是没有生命周期函数的;(后面我们可以通过hooks来模拟一些生命周期的回调)
2. 功能剖析
(1).Constructor
如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。 constructor中通常只做两件事情:
A. 通过给 this.state 赋值对象来初始化内部的state; B. 为事件绑定实例(this);
(2).componentDidMount
componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用。
componentDidMount中通常进行哪里操作呢?
A. 依赖于DOM的操作可以在这里进行;
B. 在此处发送网络请求就最好的地方;(官方建议)
C. 可以在此处添加一些订阅(会在componentWillUnmount取消订阅)
(3).componentDidUpdate
componentDidUpdate() 会在更新后会被立即调用,首次渲染不会执行此方法。
A.当组件更新后,可以在此处对 DOM 进行操作;
B.如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求;(例如,当 props 未发生变化时,则不会执行网络请求)。
(4).componentWillUnmount
componentWillUnmount() 会在组件卸载及销毁之前直接调用。
A.在此方法中执行必要的清理操作;
例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等;
如图 1
3. 不常用生命周期
(1).getDerivedStateFromProps:state 的值在任何时候都依赖于 props时使用;该方法返回一个对象来更新state;
(2).getSnapshotBeforeUpdate:在React更新DOM之前回调的一个函数,可以获取DOM更新前的一些信息(比如说滚动位置);
(3).shouldComponentUpdate:该生命周期函数很常用,但是我们等待讲性能优化时再来详细讲解
查看代码
import React, { Component } from "react";
class HelloWorld extends Component {
constructor() {
console.log("---------HelloWorld constructor-----------------");
super();
this.state = { msg: "hello world" };
}
changeMsg() {
this.setState({ msg: "hello world2" });
}
render() {
const { msg } = this.state;
return (
<div>
<h2>{msg}</h2>
<button onClick={() => this.changeMsg()}>修改子组件文本</button>
</div>
);
}
// 下面是生命周期函数
// 1. 组件被渲染到DOM:被挂载到DOM
componentDidMount() {
console.log("---------HelloWorld componentDidMount-----------------");
}
// 2. 组件的DOM被更新完成:DOM发生更新
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("---------HelloWorld componentDidUpdate-----------------");
console.log(prevProps, prevState, snapshot);
}
// 3. 组件从DOM中卸载掉
componentWillUnmount() {
console.log("---------HelloWorld componentWillUnmount-----------------");
}
// 不常用的生命周期补充
shouldComponentUpdate() {
return true;
}
getSnapshotBeforeUpdate() {
console.log("---------HelloWorld getSnapshotBeforeUpdate-----------------");
return { scrollPosition: 1000 };
}
}
export default HelloWorld;
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。