创建项目:npx create-react-app my-app
打开项目: cd my-app
启动项目:npm start
暴露配置项:npm run eject
理解npx create-react-app my-app 指令
npx是npm的附带产物
执行 npx create-react-app my-app命令流程如下:
create-react-app是react的官方脚手架
README.md--文档
public--存放静态资源
src--具体代码
App.js--根组件
index.css--全局样式
index.js--入口文件
package.json---npm 依赖
config-项目的具体配置
项目中config文件夹一般是隐藏掉的,如果想要出现可使用yarn eject,一旦执行,不可恢复
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<h1>hello React</h1>,
document.getElementById('root')
);
React负责逻辑控制,数据->VDOM,是react的核心库
ReactDom渲染实际Dom,VDOM->DOM
React可以使用jsx来描述ui
<script type="text/babel">
//script上有type="text/babel"属性, 将意味babel将接管所有的代码解析
const reactDivElement = (
<div>
hello jsx
<span>i am child span</span>
</div>
)
const root1 = ReactDOM.createRoot(document.getElementById("root1"))
root1.render(reactDivElement)
</script>
babel解析JSX原理
babel会监听全局的document.contentLoad,具体流程如下:
babel-loader把jsx编译成相应的js对象.
React.createElement再把这个JS对象构造成React需要的虚拟dom
jsx是一种javascript的语法扩展,其语言比较像模板语言,但实际上完全是javaScript内部实现的,jsx可以很好的描述UI,能够有效地提高开发效率
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
//基本使用
const name = "react"
const jsx = <div>hello,{name}</div>
ReactDOM.render(
jsx,
document.getElementById('root')
);
函数也是合法的表达式
import React from 'react';
import ReactDOM from 'react-dom';
//函数
const user = {
firstName:"Harry",
lastName:"Potter"
}
function formatName(name){
return name.firstName+" "+ name.lastName
}
const jsx = <div>{formatName(user)}</div>
ReactDOM.render(
jsx,
document.getElementById('root')
);
jsx也是js对象,也是合法表达式
import React from 'react';
import ReactDOM from 'react-dom';
const greet = <div>good</div>
const jsx = <div>{greet}</div>
ReactDOM.render(
jsx,
document.getElementById('root')
);
import React from 'react';
import ReactDOM from 'react-dom';
//条件语句
const show = true;//false;
const greet = <div>good</div>;
const jsx = (
<div>
{
show?greet:"登录"
}
{show&&greet}
</div>
)
ReactDOM.render(
jsx,
document.getElementById('root')
);
数组作为一组子元素对待,数组中存放一组jsx用于显示列表数据
import React from 'react';
import ReactDOM from 'react-dom';
//数组
const a = [0,1,2]
const jsx = (
<div>
<ul>
{
//diff时候,首先比较type,然后是key,所以同级同类型元素,key值必须唯一
a.map(item=>(
<li key={item}>{item}</li>
))
}
</ul>
</div>
)
ReactDOM.render(
jsx,
document.getElementById('root')
);
属性:静态值用双引号,动态值用花括号,class、for要特殊处理
注意:class是保留字,如果要增加class,需要使用ClassName
import React from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg'
const jsx = (
<div>
<img src = {logo} style={{width:100}} className="img"/>
</div>
)
ReactDOM.render(
jsx,
document.getElementById('root')
);
css模块化,创建index.css
import React from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg'
import './index.css'
const jsx = (
<div className = {styles.app}>
<img
src = {logo}
className = "logo"
style={{width:"50px",height:"30px"}}
alt="这个一个图片"
/>
</div>
)
ReactDOM.render(
jsx,
document.getElementById('root')
);
或者npm install sass -D
import React from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg'
import styles from './index.module.scss'
const jsx = (
<div className = {styles.app}>
<img
src = {logo}
className={styles.logo}
style={{width:"50px",height:"30px"}}
alt="这个一个图片"
/>
</div>
)
ReactDOM.render(
jsx,
document.getElementById('root')
);
组件,从概念上类似javaScript函数,它可以接受任何形式的入参(props),并返回用于描述页面展示内容的React元素,组件有两种形式:class组件和function组件
class组件(有状态组件)通常拥有状态和生命周期。(18以后很少使用,比较推崇无状态组件)
import React,{Component} from "react"
export default class ClassComponent extends Component{
constructor(props){
super(props);
//使用state属性维护状态,在构造函数中初始化状态
this.state = {
date:new Date()
}
}
componentDidMount(){
//组件挂载之后启动定时器每秒更新状态
this.timerID = setInterval(()=>{
//使用setState方法更新状态
this.setState({
date:new Date()
})
},1000)
}
componentWillUnmount(){
//组件卸载前停止定时器
clearInterval(this.timerID)
}
componentDidUpdate(){
console.log("compoentDidUpdate");
}
render(){
return (
<div>
{this.state.date.toLocaleTimeString()}
</div>
)
}
}
// 上述代码可简写为
import React,{Component} from "react"
export default class ClassComponent extends Component{
state = {
date:new Date()
}
componentDidMount(){
//组件挂载之后启动定时器每秒更新状态
this.timerID = setInterval(()=>{
//使用setState方法更新状态
this.setState({
date:new Date()
})
},1000)
}
componentWillUnmount(){
//组件卸载前停止定时器
clearInterval(this.timerID)
}
componentDidUpdate(){
console.log("compoentDidUpdate");
}
render(){
return (
<div>
{this.state.date.toLocaleTimeString()}
</div>
)
}
}
函数组件(function组件)通常无状态和生命周期(react16.8开始引入hooks,函数组件也能拥有状态和相应的生命周期)
hook 允许开发者在不写类组件的情况下,生成状态(state以及一些其他曾经是类组件专属的东西
useState会返回一个数组,里面有两个成员
以初始化为值的变量 。在调用的时候可以传递函数,也可以传递具体的值。如果传递的是函数,则会直接执行这个函数,将返回值作为初始化的状态。
注意: 虽然在初始化的时候允许传递函数(纯函数),我们也尽量不要传递函数,因为初始化只会执行一次。初始化不仅需要纯函数,还得是没有任何参数 【因为react在调用你的这个initial function的时候是不会给你传参数】
修改该变量的函数。这个函数的调用会导致函数组件的重新渲染。调用该函数的时候,可以直接传递一个值,也可以传递一个函数。如果你传递的是一个函数,则react会将上一次的状态传递给你 帮助你,进行计算。(如果你传递的是一个函数,react会将这个函数放到一个队列里面等待执行,那如果我们想每次都时时拿到上一次的值,我们得写成一个函数 。此时状态得更新是批量进行的。
useEffect:处理副作用
副作用:完全不依赖React功能的外部操作【这些外部操作不经过react的手,但是却对react产生了一些影响】
例如:
1.http请求
2.dom操作
3.异步请求多数都是会产生副作用的
虽然我们不是所有的副作用操作都是在useEffect里进行,但是官方建议我们尽可以将副作用放在useEffect中运行。 因为副作用操作是会产生意外之外的结果,如果我们想要更好的追踪副作用的执行时机,就可以将副作用都归纳进useEffect里方便追踪
useEffect(setup,dependencies)
setup:初始化的意思,是一个函数
dependencies:依赖,是一个数组
useEffect的执行时机
**setup函数会有一个返回值,这个返回值称为清理函数,清理函数会在组件卸载的时候执行 **
实际场景
1.http请求
2.访问真实dom
副作用的清除
1.dom事件的绑定清除
2.计时器绑定清除
下面是useState、useEffect实例:
import React,{useState,useEffect} from 'react'
export function FunctionComponent(props){
const [date,setDate] = useState(new Date());
useEffect(()=>{
const timer = setInterval(()=>{
setDate(new Date());
},1000)
return()=>clearInterval(timer);
},[])
return(
<div>
<h3>FunctionComponent</h3>
<p>{date.toLocaleTimeString()}</p>
</div>
)
}
setState只有在合成事件和生命周期函数中是异步的,在原生事件和
setTimeout都是同步的,这里的异步其实是批量更新。
可以把useEffect Hook看做componentDidMount、componentDidUpdate和componentwillUnmount这是三个组合
生命周期方法,用于在组件不同阶段执行自定义功能。在组件被创建并插入到DOM时(挂载中阶段)组件更新时,组件取消挂载或从DOM中删除时,都可以使用的生命周期方法
ReactV16.3之前的生命周期
v17可能会废弃的三个生命周期函数用getDerivedStateFormProps替代,目前使用的话加上UNSAFE_:
componentWillMount
componentWillReceiveProps
componentWillUpdate
static getDerivedStateFromProps
getDerivedStateFromProps会在render方法之前调用,并在初始化挂载及后续更新时都会被调用。它应返回一个对象来更新state,如果返回的为null,不更新任何内容
注意:不管什么原因,每次渲染前都会触发这个方法,相对于UNSAFE_componentWillReceiveProps而言,后者仅在父组件重新渲染时触发,而不在内部调用setState时触发
getSnapshotBeforeUpdate
getSnapshotBeforeUpdate在render之后,在componentDidUpdate之前触发,会在最近一次渲染输出(提交到DOM节点)之前调用。它使得组件能够在发生更改之前从DOM中捕获一些信息。此生命周期的任何返回值都将作为参数传递给componentDidUpdate(prevProps,prevState,snapshot)
如果不想手动给将要废弃的生命周期添加UNSAFE_前缀,可以使用下面的命令
npx react-codemd react-unsafe-lifecycles
代码学习:
import React ,{Component, component} from "react"
import PropTypes from 'prop-types'
export default class LifeCyclePage extends Component{
static defaultProps = {
msg:"omg"
}
static propTypes = {
msg:PropTypes.string.isRequired
}
constructor(props){
super(props)
this.state = {
count:0
}
console.log("constructor",this.state.count)
}
static getDerivedStateForProps(props,state){
const {count} = state
console.log("getDerivedStaticFormProps",count)
return count<5 ?null :{count:0}
}
getSnapshotBeforeUpdate(prevProps,prevState,snapshot){
const {count} = prevState
console.log("getSnapshotBeforeUpdate",count)
return null
}
componentDidMount(){
console.log("componentDidMount",this.state.count)
}
componentWillUnmount(){
console.log("componentWillUnmount",this.state.count)
}
componentDidUpdate(){
console.log("componentDidUpdate",this.state.count)
}
shouldComponentUpdate(nextProps,nextState){
const {count} = nextState;
console.log("shouldComponentUpdate",count,nextState.count)
return count !==3
}
setCount = ()=>{
this.setState({
count:this.state.count+1
})
}
render(){
const {count} = this.state;
console.log("render",this.state);
return (
<div>
<h1>LifeCyclePage</h1>
<p>{count}</p>
<button onClick={this.setCount}>改变count</button>
<Child count = {count}/>
</div>
)
}
}
class Child extends Component{
UNSAFE_componentWillReceiveProps(nextProps){
//在已挂载的组件接收新的 props 之前被调⽤
console.log("componentWillReceiveProps")
}
componentWillUnmount(){
//组件卸载之前
console.log("componentWillUnmount")
}
render(){
return(
<div
style={{border:"1px solid black",margin:"10px",padding:"10px" }}
>
我是child组件
<div>child count:{this.props.count}</div>
</div>
)
}
}
代码下载地址:https://gitee.com/JingYaBei/my-app.git