React 学习笔记
特点
- 声明式
- 基于组件
入门
基本使用
html
<div id="root"></div>
js
/*
* 创建 react 元素
* 1. 元素名称
* 2. 元素属性
* 3. 第三个及其以后的参数都是元素的子节点
*/
const title = React.createElement("h1", null, "hello react")
/*
* 1. 要渲染的 react 元素
* 2. 挂载点
*/
ReactDOM.render(title, document.getElementById("root"))
给添加属性
const title = React.createElement("h1", { title: '我是标题', id: 'p1' }, "hello react")
元素属性里添加子节点
const title = React.createElement("h1", { title: '我是标题', id: 'p1', React.createElement('span', null, '我是 span 节点') }, "hello react")
脚手架使用 React
- 导入 react 和 react-dom
- 调用 React.createElement() 创建 react 元素
- 调用 ReactDOM.render() 渲染 react 元素到页面中
import React from 'react'
import ReactDOM from 'react-dom'
const title = React.createElement('h1', null, 'hello react')
ReactDOM.render(title, document.getElementById('root'))
JSX
注意点:
React元素的属性名使用驼峰命名法
特殊属性名: class->className for->htmlFor tabindex->tabIndex
没有子节点的 React 元素可以用 /> 结束,比如 <span /> 表示 <span></span>
推荐使用小括号包裹 JSX
概述
- createElement() 不好用,不直观
- JSX 避免了这个缺点
- JSX 和 html 几乎一样
- JSX = JavaScript XML
基本使用
import React from 'react'
import ReactDOM from 'react-dom'
const title = <h1> Hello JSX </h1>
ReactDOM.render(title, document.getElementById('root'))
import React from 'react'
import ReactDOM from 'react-dom'
const dv = (
<h1> hello dv </h1>
)
const title = <h1 className="title"> Hello JSX </h1>
ReactDOM.render(title, document.getElementById('root'))
使用 Js 表达式
const name = 'Jack'
const dv = (
<h1> my name is {name}
)
条件渲染
const isLoading = true
const loadData = () => {
if (isLoading) {
return <div>loading...</div>
}
return <div>success</div>
}
const _loadData = () => {
return isLoading ? (<div>loading...</div>) : (<div>success</div>)
}
const __loadData = () => {
return isLoading && (<div>loading...</div>)
}
const title = (
<h1>{loadData()}</h1>
)
列表渲染
const songs = [
{id: 1, name: 'one'},
{id: 2, name: 'two'}
]
const list = (
<ul>
{songs.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
)
样式处理
import './css/index.css'
// 行内样式
const line = (
<h1 style={{color: 'red', backgroundColor: 'white'}}>
行内样式
</h1>
)
// 使用类名的方式添加样式
const line2 = (
<h2 className="title"></h2>
)
组件
组件创建
- 使用函数创建
- 使用类创建组件
- 使用函数创建组件
- 函数名称必须以大写字母开头
- 函数组件必须有返回值,表示该组件的结构
- 如果返回值为 null,表示不渲染任何内容
function Hello() {
return (
<div>函数组件</div>
)
}
/**
* 简洁一点的写法
* const Hello = () => <div>函数组件</div>
* /
ReactDOM.render(<Hello />, root)
- 使用类创建组件
类组件: 使用 ES6 的 class 创建的组件
- 类名称必须大写字母开头
- 类组件应继承 ReactComponent 父类,从而可以使用父类中提供的方法或属性
- 类组件必须提供 render()
- render() 必须有返回值,表示该组件结构
class Hello extends React.Component {
render() {
return (
<div>类组件</div>
)
}
}
ReactDOM.render(<Hello />, root)
组件抽离为单独文件
- 创建 Hello.js
- 在 Hello.js 中导入 React
- 创建组件
- 在 Hello.js 中导出该组件
5 在 index.js 中导出 Hello 组件- 渲染组件
Hello.js
import React from 'react'
class Hello extends React.Component {
render() {
return (
<div>抽离组件</div>
)
}
}
export default Hello
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import Hello from './Hello'
ReactDOM.render(<Hello />, root)
事件处理
React 事件处理与 DOM 类似
语法: on+事件名称={事件处理程序},如 onClick={()=>{}}
React 事件采用驼峰命名法,比如 onClick、onFocus
简单 demo
class App extends React.Component {
handle() {
console.log('单击事件触发了')
}
render() {
return (
<button onClick={this.handle}>click me</button>
)
}
}
事件对象
可以通过事件处理程序的参数获取事件对象
React 中的事件对象叫做: 合成事件(对象)
合成事件:兼容所有游览器,无需担心跨游览器兼容性问题
class App extends React.Component {
handle(e) {
// 阻止游览器默认行为
e.preventDefault()
console.log('a 标签点击了')
}
render() {
return (
<a href="http://itcast.cn" onClick={this.handle}>传智</button>
)
}
}
有状态组件和无状态组件
函数组件叫无状态组件,类组件又叫做有状态组件
状态(state)即数组
函数组件没有自己的状态,只负责数据展示(静)
类组件有自己的状态,负责更新 UI ,让页面动起来
- state 基本使用
状态 state 即数据,是组件内部的私有数据,只能在组件内部使用
state 的值是对象,表示一个组件中可以有多个数据
状态是私有的,只能在组件内部使用
通过 this.state 获取状态
class App extends React.Component {
constructor() {
super()
this.state = {
count: 0
}
}
/**
* 简化语法
* state = {
* count: 0
* }
*/
handle(e) {
// 阻止游览器默认行为
e.preventDefault()
console.log('a 标签点击了')
}
render() {
/**
* 请不要直接修改, this.state.count += 1 是错误的
*/
return (
<h1>计数器 {this.state.count}</h1>
<button onClick={() => {
this.setState({count: this.state.count +1})
}}>+1</button>
)
}
}
从 JSX 中抽离事件处理程序
- 如果 JSX 中掺杂了过多 js 代码,会显得非常混乱
class App extends React.Component {
constructor() {
super()
this.state = {
count: 0
}
}
incr(e) {
// 事件处理程序的 this 是 undefined
this.setState({count: this.state.count + 1})
}
render() {
/**
* 请不要直接修改, this.state.count += 1 是错误的
*/
return (
<h1>计数器 {this.state.count}</h1>
// c 这是不行的
/**
* 箭头函数自身是不绑定 this 的,因此可写成 onClick={() => this.incr()}
*/
/**
* 利用 es5 的 bind(),将事件处理程序中的 this 与组件实例绑定到一起
* 需要在 constructor() 中改动
* constructor() {
* super()
* this.incr = this.incr.bind(this)
* }
*
* 这样后就可以 <button onClick={this.incr}>+1</button>
*/
/**
* 事件处理程序高阶用法
* incr = () => {
* this.setState({count: this.state.count + 1})
* }
*/
)
}
}
表单处理
表单是可输入的,也就是有自己的可变状态
React 希望所有的可变状态都保存到 state 中,并且只能通过 setState() 修改
- 受控组件
class App extends React.Component {
state = {
txt: '',
city: 'bj'
}
/**
* 处理 textarea、select 也是这样的
* 处理复选框流程是为每个选项设置一个 state ,通过检查 state 确认是否选中,但属性是 checked
*/
handleChange = e => {
this.setState({
txt: e.target.value
})
}
handleCity = e => {
this.setState({
city: e.target.value
})
}
render() {
return (
<div>
<input type="text" value={this.state.txt} onChange={this.handleChange}>
<select value={this.state.city} onChange={this.handleCity}>
</div>
)
}
}
万能表单处理 handle,但需要为每个表单项添加 name 属性
这能简化代码量
handle = e => {
const target = e.target
const value = target.type == 'checkbox' ? target.checked : target.value
const name = target = name
this.setState({
[name]: value
})
}
- 非受控组件
借助于 ref,使用元素 DOM 方式来获取表单元素值
ref 作用: 获取 DOM 或组件
class App extends React.Component {
constructor() {
super()
this.txtRef = React.createRef()
}
getTxt = () => {
console.log(this.txtRef.current.value)
}
render() {
return (
<div>
<input type="text" ref={this.txtRef} />
<button onClick={this.getTxt}>获取文本框的值</button>
</div>
)
}
}
组件通讯
组件是封闭的,要接收组件的数据应该通过 props 来实现
props 作用: 接收传递给组件的数据
传递数据: 函数组件通过参数 props 来传递数据,类组件通过 this.props 接收数据
props 是只读对象,不能对其进行修改
- 函数接收数据
function Hello(props) {
console.log(props)
return (
<div>{props.name}</div>
)
}
/**
* 使用: <Hello name="jack">
*/
- 类接收数据
class Hello extends React.Component {
render() {
return (
<div>{this.props.age}</div>
)
}
}
/**
* 使用: <Hello age={19}>
*/
注意,如果使用类组件时写了构造函数,应该把 props 传递给 super(),否则无法在构造函数中获取到 props(注意,不影响 render(),只对构造函数有影响)
class Hello extends React.Component {
constructor(props) {
super(props)
console.log(props)
}
render() {
return null
}
}
三种通讯方式
- 父组件传递数据给子组件
class Parent extends React.Component {
state = {
lastName: 'wang'
}
render() {
return (
<Child name={this.state.lastName}>
)
}
}
const Child = props => {
return (
子组件接收到的数据是: {props.name}
)
}
- 子组件传递数据给父组件
思路: 利用回调函数,子组件调用,将要传递的数据作为回调函数的参数
class Parent extends React.Component {
getChildMsg = data => {
console.log(data)
}
render() {
return (
<Child getMsg={this.getChildMsg} />
)
}
}
class Child extends React.Component {
state = {
msg: '刷抖音'
}
click = () {
this.props.getMsg(this.state.msg)
}
render() {
return (
<button onClick={this.click}>点我,向父组件传递数据</button>
)
}
}
- 兄弟组件通讯
将共享组件提升到最近的公共父组件中,由公共父组件管理这个状态
思想: 状态提升
// 父组件
class Counter extends React.Component {
state = {
count: 0
}
// 提供修改状态的方法
onIncr() = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
<Child1 count={this.state.count} />
<Child2 onIncr={this.onIncr} />
)
}
}
const Child1 = props => {
return <h1>计数器: {props.count}
}
const Child2 = props => {
return <button onClick={() => props.incr()}>+1<button>
}
多层组件传递数据
const { Provider, Consumer } = React.createContext()
class App extends React.Component {
render() {
<Provider value="pink">
<Node />
</Provider>
}
}
const Node = props => {
/**
* 虽然写法怪异,但还是得按照他的要求来写
*/
return (
<Consumer>
{
data => <span>我是子节点 -- {data}</span>
}
</Consumer>
)
}
props 属性深入
children 属性
当组件标签有子节点时才会有 children 属性
const App = props => {
return (
<div>
<h1>组件标签的子节点:</h1>
{props.children}
</div>
)
}
ReactDOM.render(<App>我是子节点</App>, document.getElementById('root'))
props 默认值
const App = props => {
return (
<h1>此处展示 props 的默认值: {props.pageSize}
)
}
App.defaultProps = {
pageSize: 10
}
ReactDOM.render(<App />, document.getElementById('root'))
声明周期
-
创建时
constructor() -> render() -> componentDidMount() -
组件更新时机
-
- New props
-
- setState()
-
- forceUpdate()
React 路由
现代的前端应用大多是 SPA (单页应用程序)
前端路由是一套映射规则,在 React 中,是 URL路径 与 组件 之间的关系
基本使用
- 安装 react-router-dom
- 导入路由的三个核心组件: Router/Route/Link
- 使用 Router 组件包裹整个应用
- 使用 Link 组件作为导航菜单(路由入口)
- 使用 Route 组件配置路由规则和要展示的组件(路由出口)
- 安装
cnpm install react-router-dom --save
- 使用
// 导入组件
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
const First = () => <p>页面一的内容</p>
// 使用 Router 组件包裹整个应用
const App = () => (
<Router>
<div>
<h1>React 路由基础</h1>
{/* 指定路由入口 */}
<Link to="/first">页面一</Link>
{/* 指定路由出口 */}
<Route path="/first" component={First}>
</div>
</Router>
)
编程式导航
其实就是通过 JS 代码来实现页面跳转
handle = () => {
this.props.history.push('/home')
}
返回 n 个页面(整数或负数)
this.props.history.go(1)
默认路由
<Route path="/" component={Home}>
路由匹配模式
默认情况下,React 使用模糊匹配模式
给 Route 组件添加 exact 属性,让其变为精确匹配模式
<Route exact path="/" component={Home}>