React入门
目录
React 官方中文文档 – 用于构建用户界面的 JavaScript 库 (reactjs.org)
React入门
React 官方中文文档 – 用于构建用户界面的 JavaScript 库 (reactjs.org)
Vue跟React的异同点
相同点
- 1.都使用了虚拟dom
- 2.组件化开发
- 3.都是单向数据流(父子组件之间,不建议子修改父传下来的数据)
- 4.都支持服务端渲染
不同点
- 1.React的JSX,Vue的template
- 2.数据变化,React手动(setState),Vue自动(初始化已响应式处理,Object.defineProperty)
- 3.React单向绑定,Vue双向绑定
- 4.React的Redux,Vue的Vuex
Vue小建议
「自我检验」熬夜总结 50 个 Vue 知识点,全都会你就是神!!! (qq.com)
1. 不需要响应式的数据应该怎么处理?
// 方法一:将数据定义在data之外
data(){
// 不需要做响应式处理的数据
this.list = {xxxxx}
return{}
}
// 方法二:Object.freeze()
data(){
return {
list1: Object.freeze({xxxxxxx})
}
}
2. Key
-
不建议用
index
做key
-
不建议用随机数做
key
3. 数据结构
- new Map();
- new Set();
React 教程
1、安装React
CDN
<!-- 引入react核心库-->
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作dom-->
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转换为js -->
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">
// 一定要写babel
// 编写jsx语法
</script>
<!-- 用于props的类型校验 -->
<script src=https://cdn.staticfile.org/prop-types/15.5.10/prop-types.js ></script>
脚手架安装
# npx
npx create-react-app my-app
cd my-app
npm start
# ts
yarn create react-app my-app-ts --template typescript
# 全局安装
npm i -g create-react-app
2、类的基础知识
2.1 类的基础知识
// 基础知识
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
study() {
console.log(this) // this 指向的是实例对象
console.log('我在学习')
}
}
const p1 = new Person('张三', 18);
const p2 = new Person('李四', 18);
console.log(p1)
console.log(p2)
p2.study()
2.2 类的继承
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
speak() {
// speak方法放在了哪里?类的原型对象上,供实例使用
// 通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name},我的年龄是${this.age}`)
}
}
// 创建一个Student类,继承Person类
class Student extends Person{
constructor(name, age, grade) {
super(name, age);
this.grade = grade
}
// 子类重写父类的方法
speak() {
console.log(`我叫${this.name},我的年龄是${this.age},我在读${this.grade}年级`)
}
study() {
console.log('我在学习')
}
}
const s1 = new Student('小张', 15, '高一')
console.log(s1)
s1.speak()
s1.study()
2.3 类的继承-偏向于react中props
class Person {
constructor(props) {
this.name = props.name
this.age = props.age
}
speak() {
// speak方法放在了哪里?类的原型对象上,供实例使用
// 通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name},我的年龄是${this.age}`)
}
}
// 创建一个Student类,继承Person类
class Student extends Person{
constructor(props) {
super(props);
this.grade = props.grade
}
// 子类重写父类的方法
speak() {
console.log(`我叫${this.name},我的年龄是${this.age},我在读${this.grade}年级`)
}
study() {
console.log('我在学习')
}
}
// 创建student实例
const s1 = new Student({
name: '小张',
age: 18,
grade: '高二'
})
console.log(s1)
s1.speak()
s1.study()
2.4 this的指向问题
// 改变方法中this的指向 babel会启动严格模式 此时的 window会是undefined
// script type="text/javascript" 时window打印没问题
function demo1() {
console.log(this)
}
demo1()
const x = demo1.bind({a:1, b:2})
x()
3、简单的组件
- function 函数组件【推荐】
- class 类组件
// 简单的函数组件
function Demo1() {
return <h1>Hello</h1>
}
// 类组件 需要继承React.Component
class DemoClass extends React.Component {
render() {
return <h2>Class</h2>
}
}
// 不使用jsx 时的写法
function Demo3() {
const li1 = React.createElement('li', {name: 'Jack'}, 'list-1')
const li2 = React.createElement('li', {name: 'Jack'}, 'list-2')
return React.createElement('ul', {name: 'Jack'}, li1, li2)
}
// 渲染组件
ReactDOM.render(
<div>
<Demo3/>
<Demo1/>
<DemoClass/>
</div>, // 标签必须闭合
document.getElementById('example')
)
react中类组件的渲染过程
执行了ReactDOM.render() 之后,发生了什么?
-
- React解析组件标签,找到了 MyComponent组件
-
- 发现组件时使用类定义的,随后new出来该类的实例,并通过该实例调用到原型对象上的render方法
-
- 将render返回的虚拟DOM转为真是DOM,随后呈现在页面中
// 创建类组件
class MyComponent extends React.Component {
// render是放在哪里? -- MyComponent的原型对象上,供实例使用
// render中的this是谁? -- MyComponent的实例对象
render() {
console.log('render中的this:', this)
return <div>
<h2>我是类定义的组件(适用于【复杂组件】的定义)</h2>
</div>
}
}
// 渲染组件到页面
ReactDOM.render(
<MyComponent/>,
document.getElementById('example')
)
4、jsx语法规则
- 定义虚拟DOM时,不要写引号
- 标签中混入js表达式时要用{}
- 样式的类名指定不要用class,要用className
- 内联样式,要用style={{key: value}}的形式去写,样式名用驼峰形式
- 只有一个跟标签
- 标签必须闭合
- 标签首字母解析规则
- 若小写首字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错
- 若大写首字母开头,react就去渲染对应的组件,若组件没有定义,则报错
5、区分【js语句(代码)】与【js表达式】
-
表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式:
- a
- a+b
- demo(1)
- arr.map()
- function test(){}
-
语句(代码):
下面这些都是语句(代码):
- if(){}
- for(){}
- switch(){case: xxx}
6、模块和组件、模块化和组件化得理解
模块
- 理解:向外提供特定功能的js程序,一般就是一个js文件
- 为什么要拆分成模块:随着业务逻辑增加,代码越累越多切复杂
- 作用:复用js,简化js的编写,提高js运行效率
组件
- 理解:用来实现局部功能效果的代码和资源的集合(html/js/css/image等等)
- 为什么:一个界面的功能更负责
- 作用:复用编码,简化项目编码,提高运行效率
模块化
当应用的js都是以模块来编写的,这个应用就是一个模块化的应用
组件化
当应用是以组件的方式来实现的,这个应用就是一个组件化的应用
7、 组件中的样式
style 需要使用驼峰写法
<!--
style 需要使用驼峰写法
font-size -> fontSize
-->
ReactDOM.render(<div>
<h2 className="red">Hello</h2>
<h2 style={{color: 'green', fontSize: '30px'}}>Hello</h2>
</div>, document.getElementById('app'))
8、组件实例的三大核心属性
- prps
- 每个组件都会有props属性【只读】
- 组件标签的所有属性都保存在props中
- 作用:通过标签属性从组件外 向组件内传递数据(只读 read only)
- 对props中的属性值进行类型限制和必要性限制
- state
- 更改state:setState()方法,不可直接更改数据
- refs
- 给DOM元素添加ref属性
- 给类组件添加ref属性
既然是组件实例的核心属性,由于function函数组件没有实例,所以不能使用state和refs,但是可以使用props【函数组件可以以传参的形式来接收props】
8、类组件
8.1 state
8.1.1 基本用法
// 类组件 有状态
class Hello extends React.Component {
constructor(props) {
super(props); // 写了constructor必须写super
this.state = {
count: 0
}
}
render() {
return <div>
<h2>Hello, {this.state.count}</h2>
</div>
}
}
// 简写方式
class Hello1 extends React.Component{
state = {
count: 1
}
render() {
return <h3>{this.state.count}</h3>
}
}
8.1.2 处理this指向的几种方法
- 【推荐】定义方法时使用箭头函数,因为箭头函数没有自己的this
- 在onclick方法处通过bind改变this指向
- 在构造函数中,将原型对象中的方法赋值给实例对象【直接在class中写的方法会出现在实例对象的原型对象中】这样在调用的时候调用的是实例对象的方法,this即指向实例对象
// 类组件 有状态
class Hello extends React.Component {
constructor(props) {
super(props);
// 方法2. 可在构造函数中将发放赋值给实例对象
// this.handleAddCount = this.handleAddCount.bind(this)
this.state = {
count: 0
}
}
/**
* 这种写法中的this指向的是按钮的onclick
* 可在onClick={this.handleAddCount.bind(this)} 绑定this
*/
handleAddCount() {
console.log(this)
let {count} = this.state;
this.setState({
count: ++count
})
}
// 方法3. 方法使用箭头函数:因为箭头函数没有this,会继承父级的this
handleAddCount1 = () => {
console.log(this)
let {count} = this.state;
this.setState({
count: ++count
})
}
render() {
return <div>
<h2>Hello, {this.state.count}</h2>
<button onClick={this.handleAddCount}> +1 [错误写法]</button><br/><br/>
{/* 方法1. 可在onClick={this.handleAddCount.bind(this)} 绑定this*/}
<button onClick={this.handleAddCount.bind(this)}>onclick bind +1</button><br/><br/>
<button onClick={this.handleAddCount1}>箭头函数+1</button>
</div>
}
}
ReactDOM.render(
<Hello/>,
document.getElementById('example')
)
8.2 类组件渲染列表数据到页面及按钮点击方法
/**
* 类组件
* state 用法
*/
class ListClass extends React.Component{
constructor(props) {
super(props);
this.state = {
arr: [1,2]
}
}
handleAddList = () => {
const arr = this.state.arr;
this.setState({
arr: [...arr, arr.length + 1]
})
}
render() {
return <div>
<ul>
{
this.state.arr.map(item => {
return <li>{item}</li>
})
}
</ul>
<button onClick={this.handleAddList}>添加一个</button>
</div>
}
}
ReactDOM.render(<ListClass/>, document.getElementById('app'))
8.2 props
8.2.1 类组件中props基本用法
class Example extends React.Component{
render() {
const {name, age} = this.props
return <div>
{name}--{age}
</div>
}
}
ReactDOM.render(
<div>
<Example name="Jack" age={18}/>
<Example name="Tom" age={20}/>
</div>,
document.getElementById('example')
)
8.2.2 批量传递props
// 批量传递props
const p = {name: 'Jerry', age: 21}
ReactDOM.render(
<div>
<Example {...p}/>
</div>,
document.getElementById('example')
)
8.2.3 对类组件props进行限制
需要引入 prop-types.js
自React v15.5 起,React.PropTypes 已移入另一个包中
class Person extends React.Component{
render() {
const {name, age} = this.props
return <div>
{name}--{age}
</div>
}
}
// 下面这种写法会将 propTypes 和 defaultProps放在Person类中
// 对props进行限制
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
speak: PropTypes.func // 限制为函数
}
// props默认值
Person.defaultProps = {
age: 20
}
console.log(new Person())
// 批量传递props
const p = {name: 'Jerry', age: 21}
ReactDOM.render(
<div>
<Person name="Jack" age={18}/>
<Person name="Tom" age={20}/>
<Person {...p}/>
</div>,
document.getElementById('example')
)
将props的限制 propTypes和defaultProps写在类里面
注意:static 将属性加到类本身上,如果不写static会加载类的实例对象上
class Person extends React.Component{
// 对props进行限制
static propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number
}
// props默认值
static defaultProps = {
age: 20
}
render() {
const {name, age} = this.props
return <div>
{name}--{age}
</div>
}
}
ReactDOM.render(
<div>
<Person name="Jack" age={18}/>
<Person name="Tom" age={20}/>
</div>,
document.getElementById('example')
)
8.2.4 函数组件中使用props
function Person(props) {
const {name, age} = props
return <div>
{name}--{age}
</div>
}
// 对props进行限制
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number
}
// props默认值
Person.defaultProps = {
age: 20
}
ReactDOM.render(
<div>
<Person name="Jack" age={18}/>
<Person name="Tom" age={20}/>
</div>,
document.getElementById('example')
)
8.3 ref
- 勿过度使用 Refs
8.3.1 String 类型的 refs
过时的api,可能会在未来版本中被弃用
class Example extends React.Component {
handleClick = () => {
const {input1} = this.refs
alert(input1.value)
}
showMsg2 = () => {
const {input2} = this.refs
alert(input2.value)
}
render() {
return (<div>
<input ref="input1" type="text" placeholder="ref1"/>
<button onClick={this.handleClick}>获取数据</button>
<input ref="input2" onBlur={this.showMsg2} type="text" placeholder="ref1"/>
</div>)
}
}
ReactDOM.render(
<Example/>,
document.getElementById('example')
)
8.3.2 回调函数 refs
如果 ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null
,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。
内联函数回调
class Example extends React.Component {
handleClick = () => {
const {input1} = this
alert(input1.value)
}
showMsg2= () => {
const {input2} = this
alert(input2.value)
}
render() {
return (<div>
<input ref={currentNode => this.input1 = currentNode} type="text"/>
<button onClick={this.handleClick}>获取数据</button>
<input ref={currentNode => this.input2 = currentNode} onBlur={this.showMsg2} type="text"/>
</div>)
}
}
ReactDOM.render(
<Example/>,
document.getElementById('example')
)
不使用内联回调的方式
class Example extends React.Component {
handleClick = () => {
const {input1} = this
alert(input1.value)
}
showMsg2= () => {
const {input2} = this
alert(input2.value)
}
saveInput = (c) => {
this.input1 = c
}
saveInput2 = (c) => {
this.input2 = c
}
render() {
return (<div>
<input ref={this.saveInput} type="text" placeholder=""/>
<button onClick={this.handleClick}>获取数据</button>
<input ref={this.saveInput2} onBlur={this.showMsg2} type="text" placeholder=""/>
</div>)
}
}
ReactDOM.render(
<Example/>,
document.getElementById('example')
)
8.3.3 React.createRef() 创建
Refs 是使用 React.createRef()
创建的,并通过 ref
属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。
React.createRef()
调用后可以返回一个容器,容器可以存储被ref所标识的节点,该容器是专人专用的【需要几个创建几个】
class Example extends React.Component {
myRef = React.createRef()
handleClick = () => {
console.log(this)
const {myRef} = this
alert(myRef.current.value)
}
render() {
return (<div>
<input ref={this.myRef} type="text" placeholder=""/>
<button onClick={this.handleClick}>获取数据</button>
</div>)
}
}
ReactDOM.render(
<Example/>,
document.getElementById('example')
)
9、函数组件
使用React Hooks
- useState() 进行状态管理
- useEffect
/**
* 函数组件中没有状态及this
* 可以使用 react Hooks实现
*/
import {useEffect, useState} from 'react'
import { Button, Divider } from 'antd'
function Home () {
const [count, setCount] = useState(0);
/**
* 没有第二个参数的时候会多次调用
* 为空数组的时候 调用一次后不再调用
* 数组中包含某个参数的时候,参数改变的时候会调用 如:[count]
*/
useEffect(() => {
console.log('home-useEffect');
}, [])
const handleCount = () => {
setCount(preCount => ++preCount)
}
function handleCount1() {
setCount(preCount => ++preCount)
}
return(
<div>
<Divider>Home</Divider>
<h2 onClick={handleCount}>{count}</h2>
<Button type='primary' onClick={handleCount1}>{count}</Button>
</div>
)
}
export default Home
10、受控组件和非受控组件
- 受控组件: 表单数据是由 React 组件来管理的
- 非受控组件: 表单数据将交由 DOM 节点来处理
10.1 高阶函数
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
-
若A函数,接收的参数是一个函数,那么A函数就可以称之为高阶函数
-
若A函数,调用的返回值依然是一个函数,那么A函数就可以称之为高阶函数
常见的高阶函数有:Promise,setTimeout,arr.map()等等
10.2 表单组件实例
使用非受控组件实现
class Example extends React.Component {
handleSubmit = (evnet) => {
evnet.preventDefault()
const {username, password} = this
console.log(`用户名是${username.value},密码是${password.value}`)
}
render() {
return (<form onSubmit={this.handleSubmit}>
用户名 <input ref={c => {this.username = c}} type="text" placeholder="用户名"/>
密码 <input ref={c => {this.password = c}} type="password" placeholder="密码"/>
<button>登录</button>
</form>)
}
}
ReactDOM.render(
<Example/>,
document.getElementById('example')
)
使用受控组件实现
class Example extends React.Component {
state = {
username: '',
password: ''
}
saveUsername = (event) => {
this.setState({
username: event.target.value
})
}
savePassword = (event) => {
this.setState({
password: event.target.value
})
}
handleSubmit = (event) => {
event.preventDefault()
const {username, password} = this
console.log(`用户名是${username.value},密码是${password.value}`)
}
render() {
return (<form onSubmit={this.handleSubmit}>
用户名 <input onChange={this.saveUsername } type="text" placeholder="用户名"/>
密码 <input onChange={this.savePassword} type="password" placeholder="密码"/>
<button>登录</button>
</form>)
}
}
ReactDOM.render(
<Example/>,
document.getElementById('example')
)
使用受控组件实现-优化
class Example extends React.Component {
state = {
username: '',
password: ''
}
saveFormData = (dataType) => {
return (event) => {
this.setState({
[dataType]: event.target.value
})
}
}
handleSubmit = (event) => {
event.preventDefault()
const {username, password} = this.state
console.log(`用户名是${username},密码是${password}`)
}
render() {
return (<form onSubmit={this.handleSubmit}>
用户名 <input onChange={this.saveFormData('username') } type="text" placeholder="用户名"/>
密码 <input onChange={this.saveFormData('password')} type="password" placeholder="密码"/>
<button>登录</button>
</form>)
}
}
ReactDOM.render(
<Example/>,
document.getElementById('example')
)
11、生命周期钩子
生命周期图谱 React lifecycle methods diagram (wojtekmaj.pl)
文档 State & 生命周期 – React (reactjs.org)
-
componentWillMount
-
componentDidMount
-
shouldComponentUpdate
-
componentWillUpdate
-
componentDidUpdate
-
componentWillUnmount
class Example extends React.Component {
constructor(props) {
console.log('constructor')
super(props);
this.state = {
count: 0
}
}
// 渲染前
componentWillMount() {
console.log('componentWillMount')
}
// 渲染后
componentDidMount() {
console.log('componentDidMount')
}
// 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
shouldComponentUpdate() {
console.log('shouldComponentUpdate')
return true // false时不会更新数据
}
// 更新前
componentWillUpdate() {
console.log('componentWillUpdate')
}
// 更新后
componentDidUpdate() {
console.log('componentWillUpdate')
}
// 卸载前
componentWillUnmount() {
console.log('componentWillUnmount')
}
handleClick = () => {
this.setState({
count: ++this.state.count
})
}
render() {
console.log('render')
return (<div>
<h1>{this.state.count}</h1>
<button onClick={this.handleClick}>count+1</button>
</div>)
}
}
ReactDOM.render(
<Example/>,
document.getElementById('example')
)
14、路由
/router/index.js
import React, {Component} from 'react'
import {Route, BrowserRouter, Switch, Link} from 'react-router-dom';
// 引入页面
import Home from "../pages/Home";
import About from "../pages/About";
import Welcome from "../pages/Welcome";
import Student from "../pages/student";
// 导航组件
class LinkPage extends Component {
render() {
return (
<div>
我是导航组件<br/>
<Link to="/">Welcome</Link><br/>
<Link to="/home">Home</Link><br/>
<Link to="/about">About</Link><br/>
<Link to="/student/1234">Student</Link>
</div>
);
}
}
// 路由
const Root = () => (
<BrowserRouter>
{/*导航组件*/}
<LinkPage/>
<Switch>
<Route exact path="/" component={Welcome}/>
<Route path="/home" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/student/:id" component={Student}/>
</Switch>
</BrowserRouter>
)
export default Root
/index.js
import React from "react";
import ReactDOM from 'react-dom';
import Root from "./router/index";
ReactDOM.render(
<Root/>,
document.getElementById('root')
)