组件化开发的优势
利于团队协作开发
利于组件复用
利于SPA单页面应用开发
……
React中的组件化开发:
没有明确全局和局部的概念「可以理解为都是局部组件,不过可以把组件注册到React上,这样每个组件中只要导入React即可使用」
函数组件
类组件
Hooks组件:在函数组件中使用React Hooks函数
// views/FunctionComponent.jsx
const FunctionComponent = function FunctionComponent() {
return
我是函数组件
;
};
export default FunctionComponent;
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import FunctionComponent from '@/views/FunctionComponent';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>
>
);
// 赋值属性默认值
FunctionComponent.defaultProps = {
x: 0,
title: '',
arr: [],
num: 0
};
// 设置属性规则
FunctionComponent.propTypes = {
x: PropTypes.number,
title: PropTypes.string.isRequired,
arr: PropTypes.array,
num: PropTypes.any
};
props:获取传递的属性和children,它是被冻结的,不能新增/修改/删除...
但是我们可以给传递的属性做规则校验[是否为必传、默认值、类型]
+ 给函数设置静态的私有属性:defaultProps、propTypes...
+ 设置其他规则,需要依赖官方的插件:import PropTypes from "prop-types";
+ 如果规则校验没有通过。则控制台会保持警告,但是不影响属性的获取
https://www.npmjs.com/package/prop-types
// index.jsx
root.render(
<>
我是插槽信息1
我是插槽信息2
>
);
方法一
// views/FunctionComponent.jsx
import React from "react";
const FunctionComponent = function FunctionComponent(props) {
let children = React.Children.toArray(props.children),
headSlots = children.filter(item => item.props.slot === 'head'),
footSlots = children.filter(item => item.props.slot === 'foot');
return
{headSlots}
我是组件内容
{footSlots}
;
};
export default FunctionComponent;
props.children的值可能没有,或者是一个值,再或者是一个数组,为了方便处理,我们可以把其转换为数组
基于React.Children对象中提供的方法,处理传递的children属性值
+ toArray
+ forEach/map ->React.Children.forEach(children,()=>{})
+ count
+ ...
方法二
在当下前端开发中,组件化开发是必然的,所以我们一定要具备"抽离、分析、封装"组件的能力,而且需要让组件具备很强的"复用性"[满足更多的需求]
设计开发中这款组件的时候,需要具备很多"不确定性"
+ 需要调用组件的时候告诉组件,这样组件内部才能基于传递的不同信息,呈现出不同的效果
+ 主要就是基于 "属性和插槽"给组件传递信息
+ 如果需要传递一些数据值,则基于不同的属性传递即可,组件内部接受属性且做规则校验!!
+ 如果需要传递一些更复杂的内容(比如DOM元素),则基于插槽把内容传递进来!!
所以属性和插槽是用来增强一个组件的复用性的!!
不具备状态、生命周期函数、ref等内容
第一次渲染完毕,除非父组件控制其重新渲染,否则内容不会再更新
优势:渲染速度快
弊端:静态组件,无法实现组件动态更新
const Demo = function Demo(props) {
console.log("R");
let num = 0
return
{num}
----
{props.x}
}
export default Demo;
函数组件原来:只有第一次调用了Demo函数组件,后来点击的都是onClick函数,所以Demo不会再次创建虚拟DOM,所以也不会更新渲染,所以它就是静态组件
const fn = function () {
let x = 10;
return function () {
x++;
console.log(x);
}
}
let f = fn();//fn只执行了一次,其余都是f()执行
f();//11
f();//12
默认情况下,函数组件是一个静态组件:组件第一次渲染完毕后,组件无法进行更新,这样渲染的内容也就无法进行改变
原因:函数组件的每一次渲染和更新,都是要把函数重新执行,[产生新的闭包&重新编译出新的 VirtualDMO];默认情况下,函数第一次执行完毕后,我们没有办法基于内部的某些操作,让函数 可以重新执行(只能是修改第一次闭包中的先关信息),所以也就导致了函数组件无法更新!
如果我们想让函数组件更新,[静态组件动态化],需要怎么做:
@1 找一个可以让函数组件重新执行的办法即可,==>React中的Hooks函数
@2 如果让父组件更新,这样对应的子组件也会更新[传递最新的属性属性值进来]
import React from 'react';
import ReactDOM from 'react-dom/client';
import Demo from './Demo3';
let x = 100
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
)
setInterval(() => {
x += 100
root.render(
)
}, 1000)
在一段程序,完整流程内的某个阶段,我们可以单独做一些事情:这些函数是框架内部提供的,我们只需要在指定的函数内部做自己要做的事,框架在处理中,会帮我们把对象的函数执行
类组件:new 构造函数执行,创建类组件的实例,并且把含包含children的props作为实参传递进去[传递到构造函数中]
+ getDefaultProps 初始化属性[含规则校验]
+ 执行contruction
+ gteInitalState 初始化状态[把视图中需要的数据,放在this.state中==>构建Model数据层]
+ [钩子] 执行componrntWillMount[第一次渲染之前]
注意:此钩子函数是不安全的,未来可能要被移除,如果一点要使用,前面好设置UNSAFE(UNSAFE_componentWillMount),但是是在React.StrictMode模式下,会抛出警告错误
+ [钩子] 执行render[组件渲染:所以我们需要把组件的视图放在render函数中返回]
+ [钩子] 执行componentDidMount[第一次渲染完毕(可以获取到真实的DOM元素)]
+ 此时可以获取到真实的DOM元素了
+ 还可以在此处异步获取服务器数据,然后让组件更新渲染真实的内容!!原本应该在 componentWillMount中获取服务器数据,没有渲染就获取数据,提高性能,但是因为 不 安全,也可以在constructor中,初试完数据后获取数据,但是函数组件没有constructor
+ 有些需求还需要设置定时器/监听器,在或者用一些其他的插件等
+ ....
附:
exports.func = function func() {
}
exprots.sum= function sum(){
}
// module.exports = {
// func: function func() {
// }
// }
let Tc = require("./test");//Commonjs
console.log(Tc);
import Tc from './test';//ES6
console.log(Tc);
import {sum,func} from './text'
创建一个类,并继承React.Component/PureComponent,且具备render这个钩子函数,用来构建view视图!!
import { Component } from "react";
class Demo extends Component {
//this->创建的实例
/* 初始化属性 */
static defaultProps = {//静态私有属性
name: "",
age: 18
}
render() {
return
}
}
export default Demo
如果写了constructor 第一行一定要写super[class做了继承后的语法要求]
super()把父类(Component)中私有的继承给子类私有[类似于class继承]
React.Component.call(this)把父类当做普通函数执行,this指向子类的实例
this.props=props;
this.context=context;
this.refs={};
this.updater=...;
处理完毕后,this实例上就挂载了4个属性
可以在constructor中初始化状态
constructor如果不写,则:
+ React内部会把四个属性,挂载到this实例上[且都是赋好值的]
+ 可以直接在外面,基于state={...}初始化状态
总结super的原理; 把父类Component当做普通函数执行,让this变成子类的实例,让实例上挂载4个属性,super传了props就是挂载到实例上(this.props)
// constructor(props) {
// //经过初始化后的属性对象
// super(props)//this.props=props
// // console.log(props, this.props);
// this.state = {
// num: 10
// }
// }
/* 初始状态 */
state = {
num: 10
}
// componentWillMount() {
// console.log("componentWillMount");
// }
UNSAFE_componentWillMount() {//解除警告
console.log("componentWillMount");
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
)
如果在类组件内部,基于某些操作,让组件更新
setState 修改状态[内部]
+ [钩子] 执行shouldComponentUpdate 是否允许更新返回TRUE则继续,返回FALSE则结束
在此阶段,THIS上的属性和状态还未更改
+ [钩子] 执行componentWillUpdate[不安全的] 更新之前
修改THIS上的属性和状态信息
+ [钩子] 执行render
+ [钩子] 执行componentDidUpdate
+ 触发setState中的callback执行
/* 初始状态 */
state = {
num: 10
};
// componentWillMount() {
// console.log("componentWillMount");
// }
shouldComponentUpdate() {
console.log("shouldComponentUpdate");
return true
}
UNSAFE_componentWillUpdate() {//解除警告
console.log("componentWillUpdate");
}
componentDidUpdate() {
console.log("componentDidUpdate");
//第一次渲染结束
//只要视图更新,它就会执行,和setState里面的回调函数不一样,callback只有在上面第一个参数更新以后,才会执行。如果有100个状态,我只需要其中一个更新,只能用callback
}
render() {
console.log("render");
let { num } = this.state
return
{num}
}
}
forceUpdate 强制更新
会直接跳过shouldComponentUpdate函数,强制视图更新
+ [钩子] 执行componentWillUpdate
+ [钩子] 执行render
+ [钩子] 执行componentDidUpdate 更新完毕
+ 触发forceUpdate中的callback执行
/* 初始状态 */
state = {
num: 10
};
// componentWillMount() {
// console.log("componentWillMount");
// }
shouldComponentUpdate() {
console.log("shouldComponentUpdate");
}
UNSAFE_componentWillUpdate() {//解除警告
console.log("componentWillUpdate");
}
componentDidUpdate() {
console.log("componentDidUpdate");
//第一次渲染结束
}
render() {
console.log("render");
let { num } = this.state
return
{num}
}
}
---------
如果是父组件更新,则子组件也是更新的[外部]
+ [钩子] 执行componentWillReceiveProps[不安全]
+ [钩子] 执行shouldComponentUpdate
+ ....
---------
如果组件销毁
+ [钩子] 执行componentWillUnmpunt
销毁组件
函数组件
不具备“状态、ref、周期函数”等内容,第一次渲染完毕后,无法基于组件内部的操作来控制其更新,因此称之为静态组件!
但是具备属性及插槽,父组件可以控制其重新渲染!
渲染流程简单,渲染速度较快!
基于FP(函数式编程)思想设计,提供更细粒度的逻辑组织和复用!
类组件
具备“状态、ref、周期函数、属性、插槽”等内容,可以灵活的控制组件更新,基于钩子函数也可灵活掌控不同阶段处理不同的事情!
渲染流程繁琐,渲染速度相对较慢!
基于OOP(面向对象编程)思想设计,更方便实现继承等!
React Hooks 组件,就是基于 React 中新提供的 Hook 函数,可以让函数组件动态化!