React入门全解

vue和react编程模型对比

React的特点:能不做的,我都不做
Vue的特点:能帮你做的,我都帮你做
在这里插入图片描述

vue的编程模型

一个对象,对应一个虚拟DOM,当对象的属性改变时,属性相关的DOM节点全部更新
Vue为了其他考量,也引入了虚拟DOM和DOM diff

react的编程模型

每次触发dom diff 都会触发一次函数调用
一个对象,对应一个虚拟DOM,
另一个对象,对应另一个虚拟DOM,
对比两个虚拟DOM,找不同(DOM diff)最后局部更新DOM`

带着问题学React

  1. 两种方式引入React & ReactDOM
  2. React.createElement(‘div’ | 函数 | 类 )
  3. 类组件、函数组件如何获取外部数据props
  4. 类组件、函数组件如何获取内部数据state
  5. 类组件如何绑定事件,可以直接 fn= ()=>{}
  6. 函数组件如何绑定事件,函数组件内部不用this
  7. vue特点,react特点

引入react

cjs - CommonJS

  • 是Node.js支持的模块规范

umd - 统一模块定义

  • 兼容各种模块规范(含浏览器)

理论上优先使用umd,同时支持Node.js和浏览器
最新的模块规范是使用import和export关键字

新建项目

// 全局安装
yarn global add create-reacrt-app
// 初始化目录
create-react-app react-demo-1
// 进入目录
cd react-demo-1
// webpack让JS默认走babel-loader

React组件

Element V.S. Component

对比React元素和函数组件

// App1 是一个 React 元素
App1 = React.createElement('div', null, n)
// App2 是一个 React 函数组件 
App2 = ()=> React.createElement('div', null, n)
// 函数app2是延迟执行的代码,会在被调用的时候执行
// 同步异步关注的是得到结果的时机,现在主要是说执行的时机
  1. React元素
    createElement的返回值element可以代表一个div
    但element并不是真正的div(DOM对象)
    所以我们一般称element为虚拟DOM对象

  2. ()=>React元素
    返回element的函数,也可以代表一个div
    这个函数可以多次执行,每次得到最新的虚拟div
    React会对比两个虚拟div,找出不同,局部更新视图,找不同的DOM算法叫做 DOM Diff 算法

  3. 一个返回React元素的函数就是组件

React的两种组件函数组件

// 一、函数组件
function Welcome(props){
	return <h1>Hello, {props.name}</h1>;
}
// 需要return 出去
•使用方法 <Welcome name="frank"/>

// 二、类组件
class Welcome extends React.Component {
	render() { 
		return <h1>Hello, {this.props.name}</h1>
	}
}
// 需要return 出去
•使用方法 <Welcome name="frank"/>

props(外部数据)

Props的作用
  1. 接受外部数据
    (1) 只能读不能写
    (2) 外部数据由父组件传递
  2. 接受外部函数
    (1) 在恰当的时机,调用该函数
    (2) 该函数一般是父组件的函数
props的一些原则规范
  1. 改props的值:外部属性 由外部更改,不要在外部修改props数据
  2. 改props的属性: 外部的数据,不应该从内部改值
  3. 数据应该由原处修改。
UNSAFE_componentWillReceiveProps钩子
  • 当组件接受新的props时,就会触发此钩子
  • 不推荐使用这个钩子
props例
// 类组件
class Son extends React.Component {
  // 初始化 props,可通过this.props获得 外部数据 对象 的地址
  constructor(props){
	super(props)
  }
  render() {
    return (
      <div className="Son">
        // 使用组件,并自定义数据名 messageForSon 通过this.props.messageForSon读取
        我是儿子,我爸对我说「{this.props.messageForSon}// vue 通过在属性前加冒号:编写JS,react通过中括号{}编写JS
        <Grandson messageForGrandson="孙贼你好" />
      </div>
    );
  }
}

// 函数组件
const Grandson = props => {
  return (
  	// 数据会作为第一个参数传递过来的,通过.自定义名获取数据
    <div className="Grandson">
      我是孙子,我爸对我说「{props.messageForGrandson}// 通过props接收
    </div>
  );
};

state(内部数据)

读用 this.state
onClick=()=>{
	this.setState({x: this.state.x + 1})
	// 此时的x为0,而不是1 因为它不会马上改变X的值,而是执行完后再改变
	console.log(x)
}
this.state.xxx.xx.x
写用 this.setState(???,fn)

this.setState(newState,fn)

this.setState((state,props)=> newState,fn)
函数组件通过 React.useState(0) 更新UI
  • useState[1] 不可变数据:无法改变旧数据
  • 用this.xxx读 , this.setxxx写
setState类组件注意事项
  1. 调用setState才会触发UI更新,每次UI更新就会触发DOM diff重新对比新旧数据,检测当前页面的setState数据是否改变
  2. setState会异步的触发更新UI
  3. 因为React没有和vue监听data那样监听state
  4. 不要直接修改旧state this.setState(this.state) 不可变数据
  5. 可以 setState({n:state.n+1})
setState类组件的属性

可以单独修改属性,其他属性会沿用上一次的值

  constructor() {
    super();
    this.state = {
      n: 0,
      m: 0
    };
  }
  addN() {
    this.setState({ n: this.state.n + 1 });
    // 单独修改n,m 会被覆盖为 undefined 吗? // 不会
  }
  addM() {
    this.setState({ m: this.state.m + 1 });
    // 单独修改m,n 会被覆盖为 undefined 吗? // 不会
  }
写的时候会 shallow merge(浅合并)

setState 会自动将新state与旧state进行一级合并

  1. 类组件的setState会自动合并第一层属性,但是不会合并第二层属性
  2. 使用object.assign或者…扩展符
  3. 函数组件的setX则完全不会合并属性,第一层/第二层都不会合并
  4. 所以用react处理数据,经常需要用…获取其他数据
setState函数组件的属性
    <button onClick={()=>setState({n: state.n + 1})}/> 
    // m = NAN
    // 单独修改n,m 会被覆盖为 undefined 吗? // 会
    
    <button onClick={()=>setState({m: state.m + 1})}/>
     // n = NAN
    // 单独修改m,n 会被覆盖为 undefined 吗? // 会
    
    // 需要将对象的值都放在里面
    <button onClick={()=>setState({...state, m: state.m + 1})}/> // n = NAN
  }
React.useState(0)函数组件注意事项
  • 函数组件需要通过React.useState(0)更新UI,因为react是通过dom diff对比数据来更新界面的,不触发DOM diff,他就不会更新UI
  • 跟类组件类似的地方 - 也要通过setX(新值)来更新UI
  • 跟类组件不同的地方 - 没有this

生命周期

  1. constructor() - 是在这里初始化 state
  2. static getDerivedStateFromProps()
  3. shouldComponentUpdate() - return false 阻止更新
  4. render() - 创建虚拟DOM
  5. getSnapshotBeforeUpdate()
  6. componentDidMount() - 组件已出现在页面
  7. componentDidUpdate() - 组件已更新
  8. componentWillUnmount() - 组件将销毁
  9. static getDerivedStateFromError()
  10. componentDidCatch()

constructor的作用

用途

  1. 初始化props
  2. 初始化state,但此时不能调用setState
  3. 用来写 bind this
constructor(){
	super()
	call = this. onClick.bind(this)
}

// 新语法写法,等同于上面代码
onClick = ()=> {}
constructor(){ }

shouldComponentUpdate的作用

用途

  1. 返回true表示不阻止UI更新
  2. 返回false表示阻止UI更新

面试常问

  • shouldComponentUpdate有什么用?
  • 它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活地设置返回值,以避免不必要的更新

使用 React.PureComponent 自动对比新旧数据

自带了shouldComponentUpdate - PureComponent 会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就 render。
在这里插入图片描述

render

用途

  1. 展示视图 - return (<div></div>)
  2. 如果有两个以上根元素,需要用<React.Fragment>包起</React.Fragment>
  3. 可以缩写成<></>

一些技巧

  1. render里面可以写if…else / ?: / map循环
  2. 不能直接写for循环

示例
在这里插入图片描述

componentDidMount()

用途

  1. 在元素插入页面后执行代码,这些代码依赖DOM
  2. 例:获取DIV高度
  3. 官方推荐:发起加载数据的AJAX请求
  4. 首次渲染会执行次钩子

componentDidUpdate()

如果用户退出登录 可以在此周期里请求
用途

  1. 在视图更新后执行代码
  2. 也可以发起AJAX请求,用于更新数据
  3. 首次渲染时不会执行此钩子
  4. 在此处setState可能会引起无线循环,除非放在if里
  5. 若 shouldComponentUpdate返回 false, 则不会触发此钩子

componentWillUnmount()

用途

  1. 组件被移出页面后被销毁时执行代码
  2. unmount过的组件不会再次mount

注意

  • 如果在componentDidMount() 里面监听了window scroll,就要在componentWillUnmount()取消监听
  • 如果在componentDidMount() 里面创建了Timer,就要在componentWillUnmount()取消Timer
  • 如果在componentDidMount() 里面创建了AJAX请求,就要在componentWillUnmount()取消请求

React事件

写法

类组件

class Son extends React.Component{
	// 解决方案一 这是将函数当做对象放在了cnstructor上,直接写函数是语法糖
	addN = () => this.setState({n: this.state.n + 1});
	// 完整写法如下
	constructor(){
		this.addN = ()=> this.setState({n: this.state.n + 1})
	} 

	// 解决方案二
	addN(){
		this.setState({n: this.state.n + 1})
	}
	// 完整写法如下
	addN: functioin(){
		this.setState({n: this.state.n + 1})
	}

所有函数的this都是参数,由调用决定,是可变的
箭头函数的this是不变的因为箭头函数不接受this

函数组件

  • 函数里没有this,指向调用者的this,因为this是调用者传给函数的参数
  const Grandson = () => {
  const [n, setN] = React.useState(0);
  const [m, setM] = React.useState(0);
  return (
    <div className="Grandson">
      孙子 n:{n}
      // 此时的onClick是一个回调
      <button onClick={() => setN(n + 1)}>n+1</button>
      m:{m}
      <button onClick={() => setM(m + 1)}>m+1</button>
    </div>
  );
};

两种方式创建Class组件

ES5创建方式

因为ES5不支持class,才会有这种方式

import React from 'react'
const A = React.createClass({
render() {
return (
<div>hi</div>
) }
})
export default A

ES6创建方式

import React from 'react';
class B extends React.Component {
	constructor(props) {
		super(props);
	}
	render() {
		return (
			<div>hi</div>
		) 
	}
}
export default B;
// extends constructor super 强行记忆

转译成ES5

webpack+babel将ES6翻译成ES5

使用函数组件代替类组件

对,上面讲的都是class组件,下面才是推荐使用的函数组件

函数组件面临两个问题

  1. 函数组件没有state
  2. 函数组件没有生命周期

React在V16.8.0给出的解决方案是=> Hooks API

以下是两个Hooks API

  1. 通过useState Hook 解决 state问题
  2. 通过useEffect Hook 解决 生命周期问题
    在这里插入图片描述

useEffec模拟生命周期

模拟componentDidMount
useEffect(()=>{ console.log(' 第一次渲染 ') },[])
模拟componentDidUpdate
useEffect(()=>{ console.log(' 任意属性变更 ')})
useEffect(()=>{ console.log('n变了 ')}, [n])
模拟componentWillUnmount
useEffect(()=>{
	console.log(' 第一次渲染 ')
	return ()=>{
		console.log(' 组件销毁了 ')
	}
})
其他生命周期怎么模拟
constructor
  • 函数组件执行的时候 ,相当于执行了constructor
shouldComponentUpdate

使用React.memo和useMemo可以解决

render

函数组件的返回值就是render的返回值

useUpdate

  1. 第一次由undefined变成0时不执行函数
  2. 之后发生变化时,执行函数
// 封装自定义hook
// 接收两个参数,并执行
const useUpdate = (fn,ref) =>{
	const [count,setCount] = useState(0)
	useEffect(()=>{
		setCount(x=>x+1)
	},ref)
	// 控制第一次渲染页面时不执行 setCount
	useEffect(()=>{
		if(count > 1){
			fn()
		}
	}, [count,fn])
}

// html
const App = (props) => {
	//把f推给react监听
	const [f,setf] = useState(0)
	const hit = ()=>{
		setF(f+1)
	}
	// 调用自定义hook传两个参数,第一个传fn,第二个传f
	useUpdate(()=>{
		console.log('变了')
	},f)
	return (<div>{n}<button onClick={hit}>+1</button></div>)
}

这就是自定义Hook
———————————————————————————————————————————————————————————————————

JSX - 通过babel-loader转译成JS

  1. babel-loader里集成了jsx-loader
  2. babel-loader被webpack内置了
  3. vue-loader没有被webpack内置

JSX - JS扩展版

X表示扩展
JSX的使用:

  1. 将XML转译为React.createElement
  2. 使用{}插入JS代码
  3. create-reate-app默认将JS当做JSX处理

Vue和React的区别

//Vue的vue-loader
vue封装好了指令
.vue文件里写 template标签 script标签 style标签 ,通过vue-loader变成一个构造选项

// React的JSX
// 把
<button onClick="add">+1</button> //变成
React.createElement('button', {onClick....}, '+1')
// react里需要写原生JS
// React有JSX
// 实际上jsx-loader被babel-loader取代了,而babel-loader被webpack内置了

使用JSX的注意事项

// 注意className
使用className指定作用域

return (
	<div className="作用域名"></div>	
)
{}
// 标签里面的JS代码都要用{}包起来
// 对象则  {{name:'frank'}} 外面的括号是JSX语法

// return 的内容要用()包起来
return (
	<div>App组件</div>
)

// html
Vue的HTML写在<template>里
React把HTML混在JS/JSX文件里

JSX的条件判断

// 可以这样写
const Component = () => {
const content = (
	<div>
		{ n%2===0 ? <div>偶数n </div> : <span>奇数n</span> }
	</div>
)
return content
}
// 还可以这样写
const Component = () => {
const inner = n%2===0 ? <div>偶数n </div> : <span>奇数n </span>
const content = (
	<div>
		{ inner }
	</div>
)
return content
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Supernova_gu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值