## 环境
创建环境
npx create-react-app project_name
启动
npm start / yarn start
npm run build
弹出设置
npm run eject
安装个别包
npm i moudle_name --save
yarn add module_name
npm i module_name --save-dev
yarn add -D module_name
初始安装
npm i
yarn
## React/ReactDOM
ReactDOM.render(<App />, document.getElementById('root'))
JSX是什么?
React.createElement的语法糖
JSX <-- --> React.createElement转换关系
Babel编译工具
<A props1={1} props2="2">
<B>abc<B>
<C />
</A>
React.createElement(组件对象, {props1, props2}, ...children)
组件和元素
A <A />
JSX表达式的写法
{表达式}
表达式只能写一句语句,不能写if / for / while
可以写的:变量,常量,函数调用
<A>{表达式}</A>
children位置:
数字/字符串/数组(数字,字符串)
布尔类型/null/undefined/Symbol
不能写object在children位置
<A props1={表达式} />
<A name={'abc'} age={12} />
表达式输出数组
<A>{items.map((item, index) => <B key={index}>{item.name}</B>)}</A>
items = [<B key='b'>1</B>, <C key='c'>2</C>, <D key='d'>3</D>]
<A>{items}</A>
HTML元素的属性
<br/> <a></a> 符合JSX语法,Xml标签必须闭合
HTML转JSX
html里属性全是小写,JSX里属性要符合驼峰 colspan --> colSpan, autoplay --> autoPlay
特殊:class --> className, for --> htmlFor
值:
style='width: 120px; font-size: 1em'
style={{width: 120, fontSize: '1em'}}
有JSX为HTML添加的属性
defaultValue
事件
onclick='alert(1)'
onClick={() => {}}
this.handleClick.bind(this)
onClick={this.handleClick}
条件渲染
三目运算符: 条件? 真 : 假
开关:条件 && <元素 />
JSX注释
<A>
//<B /> 错
{/* <B />*/}
<C
{/*props="c"*/} 错
// props1=‘123’
>
</A>
自定义组件
组件名字:首字母大写
<link />
<Link />
obj.a = function() { return <div />}
<obj.a />
class组件 类组件
function组件 函数组件 无状态组件
区别:1 类组件有状态,函数组件没有状态 2 类组件有生命周期函数,函数组件没有
class A extends React.Component {
constructor(props) {
super(props)
...
}
render() {
return null
}
}
props: this.props.属性名
state
定义:state做什么的--内部状态,state是object
初始化:构造方法/成员的声明部分,不应在其他成员函数里给state赋值
class A {
state = {count: 0}
constructor(props) {
super(props)
this.state = {count: 0}
}
componentDidMount() {
this.state = {count: 0} //错
}
}
改变state值,通过 this.setState
执行this.setState后会render(this.render方法从来不会主动调用)
this.setState的效果是异步的(this.state不会在setState后马上改变)
this.setState(updater, callback)
updater:
1. object:是state的object内容一部分
state = {a: 1, b: 2}
this.setState({b: 3}) --> 把{a: 1, b: 2}和{b: 3}合并
Object.assign({a: 1, b: 2}, {b: 3}) --> {a:1, b:3}
this.setState({c:5}) --> {a:1, b:3, c:5}
state = {a: [1,2,3], b: {name: 'zhangsan', age: 20}}
不要这样操作:
this.state.a.push(4)
this.setState({a: this.state.a})
this.state.b.age = 21
this.setState({b: this.state.b})
推荐方式(浅拷贝后setState):
this.setState({a: [...this.state.a, 4]})
this.setState({a: this.state.a.concat(4)})
this.setState({b: {...this.state.b, age: 21}})
this.setState({b: Object.assign({}, this.state.b, {age: 21})})
注意情况
state = {a:1, b:2}
this.setState({a: 10})
this.setState({b: 20})
Object.assign({}, this.state, {a:10}, {b:20})
state = {a:1, b:2}
this.setState({a: 10}) // 无效
this.setState({a: 20})
Object.assign({}, this.state, {a:10}, {a:20})
2. 函数形式 (state, props) => 部分state object
state = {count: 0}
// 错
this.setState({count: this.state.count + 1})
this.setState({count: this.state.count + 1})
Object.assign({}, this.state, {count: this.state.count + 1},{count: this.state.count + 1})
state = {count: 1}
// 对
this.setState((state) => ({count: state.count + 1}))
this.setState((state) => ({count: state.count + 1}))
state = {count: 2}
setState第二个参数callback,参数可选
() => {
// render之后执行的内容
// 认为跟componentDidUpdate执行的时间一致
}
生命周期:
三个阶段
挂载
**constructor** 初始化state和其他变量
static getDerivedStateFromProps
**render** 一定会执行,不要做跟render无关事情(例如发起异步请求)
**componentDidMount** 有副作用的操作(绑定全局事件,启动计时器,发起ajax请求)
更新
static getDerivedStateFromProps
shouldComponentUpdate
**render** 不一定会执行
getSnapshotBeforeUpdate
**componentDidUpdate** dom元素真正更新,发起ajax,this.setState有条件使用
卸载
**componentWillUnmount** 清除副作用
this.forceUpdate() 强制render(),跳过shouldComponentUpdate
## Ref
作用:获得dom元素或者获得JSX元素,不要用原生dom查询方式,比如document.querySelect()
场景:
- 管理焦点,选择文本,播放媒体
- 强制动画
- 集成第三方的dom库
Ref几种方式:
1)class组件里
this.ref = React.createRef()
<A ref={this.ref}>
<video ref={this.ref}>
this.ref.current ...
2)回调函数 (两种组件都可以)
<video ref={(el) => this.ref = el}>
this.ref ...
3)函数组件使用,hook方式
const ref = React.useRef()
<video ref={this.ref}>
ref.current
## Portal
作用:在挂载点之外渲染
场景:对话框,浮层
ReactDOM.createPortal(<元素 />, 挂载点dom)
render() {
return <div onClick={() => {/*Portal内的点击能冒泡到这里*/}}>...{ReactDOM.createPortal(<元素 />, 挂载点dom)}</div>
}
## React事件
取消默认动作 preventDefault
取消冒泡 stopPropagation
onChange
## React数据传输
- 父传子,通过属性
- 子传父,通过回调函数
<Parent/>
function Parent() {
const [a, setA] = useState(0)
return <div><Child prop1={a} onChangeValue={() => setA(10)}/></div>
}
## 函数组件
function Component(props) {
props.a
}
function Component({a, b, c}) {
return <div />
}
## 钩子函数Hook
Hook使用规则
1. 只能在函数组件或自定义Hook函数里调用钩子函数
2. 只能在函数的第一层级调用钩子函数(不要在if ,for, while, switch调用)
自定义Hook要求:use开头驼峰,useSearchParams
function useSearchParams() {
const location = useLocation()
return location.search
}
React.useState
let [state, setState] = useState(初始值)
setState(updater)
updater: 函数或者其他值
React.useEffect(callback, 依赖关系)
参数
callback
函数内容就是执行副作用
return部分是取消副作用
挂载的时候一定回执行callback,卸载一定会执行return的函数
更新时,先卸载,再执行副作用
依赖关系
undefined,每次都执行
[],从不执行
[a, b] a或b值和上次渲染不同的时候会执行
## React-Router-Dom 5.x
组件三大类
- 路由器 BrowserRouter/HashRouter
- 路由 Switch/Route/Redirect
- 导航 Link/NavLink
函数
- withRouter 转换普通组件成为带路由参数的组件
- 钩子函数
- useHistory
- useLocation
- useRouteMatch
- useParams
实现无刷新路由的原理
history.pushState/replaceState, window.onpopstate
HashRouter和BrowserRouter区别
在不支持history api的旧浏览器上要使用无刷新路由,或者在不支持路径重写的server环境里
Apache/nginx rewrite
发布单页面应用的时候需要注意什么?
要web server配置url rewrite规则
react-router-dom使用
顶级元素<BrowserRouter> / <HashRouter>
Route
- path - 匹配路径
- 静态路径
- 动态路径,参数以:开头,match.params.参数名
- ? + *
- 用正则表达式 :参数(正则) 比如 /:id([\d]+)
- 渲染
- render - 函数
- children - 函数
- component - 组件定义
- 直接JSX写在子元素位置
- exact/strict/sensitive
Switch
内部的子路由元素最靠前匹配的路由会生效,其后的路由被忽略
显示404
<Switch>
<Route path='...'>
<Route path='...'>
<Route path='...'>
<Route component={NotFoundView}/>
</Switch>
Link
- to
- 字符串
- location 可以设置location.state
- (location) => 新的location
- replace
NavLink
是特殊的Link,可以根据是否匹配当前的路由显示不同的UI
- activeClassName/activeStyle
- className/style 的值可以是回调函数 isActive => 返回className或者style对象
- exact
不要用<a>做跳转
跳转方式
JSX形式:<Redirect >属性基本和Link一样,push
编程形式:history.push / replace,这里history对象是react-router提供的,不是window.history
机试准备
准备一个干净的开发环境
npx create-react-app project-name
cd project-name
code .
// git bash
rm src/*
touch src/index.js
index.js
import 'bootstrap/dist/css/bootstrap.min.css'
ReactDOM.render()
yarn add react-router-dom axios bootstrap
yarn start
导航条界面
```
<ul class="nav justify-content-center nav-tabs">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Active</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled">Disabled</a>
</li>
</ul>
```
左侧导航条
```
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Active</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled">Disabled</a>
</li>
</ul>
```
占位符
```
<span class="placeholder col-6"></span>
<span class="placeholder w-75"></span>
<span class="placeholder" style="width: 25%;"></span>
```