写在最前面
温故而知新,勤能补拙,一遍学不会就多学几遍。
这次学习的是 【智能社】React合集(经典课程升级版+精通Hook) 课程。
边学习边记录
快速访问
React 基础
- 虚拟 DOM(比直接操作 DOM 性能高)
- JSX(依赖 React 不能单独使用)
- 组件 Component
- Redux 数据管理
- React-Nactive 编写原生移动应用
- React-Server 服务端渲染 React 组件
在网页中使用 React 和 JSX
raw.githubusercontent.com/reactjs/rea…
<div id="root"></div>
<script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
ReactDOM.render(<h1>Hello, world!</h1>, document.getElementById('root'));
</script>
使用官网脚手架创建 React 应用
- 安装
npx create-react-app my-app
- 运行
yarn start
yarn 是 Facebook 推出的包管理工具,所以懂。也可以使用 npm start 运行。
优化
- 根目录
.git文件夹可删除
组件
- 组件的功能应该单一和简单化
- 好的组件不是设计出来的,是改出来的
- 子组件间数据公用,由父组件管理数据,尽量不使用
redux - 组件职责,组件自己搞定自己的事情,而非父级处理
- 推荐
return写法
return (
//
);
- 花括号
{}可传任何内容(字符串、变量、数组、循环、组件等)
className={`btn ${btn.active==='active'?'active':''}`}
使用 ES6 的 class 来定义组件
src/components/Welcome.js
class Welcome extends React.Component {
constructor(...args){
super(...args);
}
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
事件
正确绑定到组件 this 需要使用 ES6 的 bind 方法
fun(){}
...
<h1 onClick={this.fun.bind(this)}>Hello</h1>
状态 state
- 变化就会触发
render再渲染 - 只能在
constructor初始化 - 所有变化的数据都放在状态里
设置
constructor(...args){
super(...args);
this.state = {
age:18
};
}
读取
{this.state.age}
更新
this.setState({
a:this.state.age+1
});
属性 props
- 组件传参
- 只读数据不能修改
设置
<Welcome name="OY"/>
读取
<h1>Hello, {this.props.name}</h1>
渲染触发条件
state状态更新props(父组件state以属性的方式传递给子组件,父组件更新state就会触发子组件再渲染)
class Parent extends React.Component {
constructor(...args){
super(...args);
this.state = {
age:18
};
}
fun(){
this.setState({
a:this.state.age+1
});
}
render() {
return (
<>
<h1 onClick={this.fun.bind(this)}>Hello</h1>
<Child age={this.state.age}/>
</>
);
}
}
3. 强制渲染 forceUpdate()
可以在不使用 state 的情况下强制渲染,不建议使用!
class Parent extends React.Component {
constructor(...args){
super(...args);
this.age = 18;
}
fun(){
this.age++;
this.forceUpdate();
}
render() {
return (
<>
<h1 onClick={this.fun.bind(this)}>Hello</h1>
<Child age={this.age}/>
</>
);
}
}
生命周期
- 创建 Mount:
- 构造器
constructor rendercomponentDidMount最常用适合获取数据
- 更新 Update:
shouldComponentUpdate确定是否更新(重要)render3个触发条件(state、props、forceUpdate())componentDidUpdate最常用适合获取数据更新
- 卸载 Unmount:
componentWillUnmount
一套代码说清楚全部
class Demo extends React.Component {
constructor(...args){
super(...args);
console.log('[创建] - 构造器');
this.state = {
age:18,
show:true
}
}
componentDidMount(){
console.log('[创建] - 完成,最常用适合 fetch/axios 获取数据');
}
shouldComponentUpdate(nextProps, nextState){
console.log(this.state.age, nextState.age);
return true;
}
componentDidUpdate(){
console.log('[更新] - 完成,最常用适合 fetch/axios 获取数据更新');
}
componentDidUpdate(){
console.log('[卸载] - 将要,没有完成时');
}
add(){
this.setState({
age:this.state.age+1
});
}
show(){
this.setState({
show:!this.state.show
});
}
render() {
console.log('[创建]/[更新] - 渲染');
return (
<>
<h1>{this.state.age}</h1>
<button onClick={this.add.bind(this)}>Add</button>
{this.state.show?(<Child />):''}
<button onClick={this.show.bind(this)}>Show</button>
</>
);
}
}
函数组件 rfce
- FC === Function Component
- 方便(人性最爱)
this===undefined- 函数组件默认无:实例
this、状态state、引用ref、方法 hook一套工具函数的集合,函数组件专用,状态、引用、方法都可以实现
src/components/Welcome.js
function Welcome(props) {
return (
<h1>Hello, {props.name}</h1>
);
}
React hook
React版本必须16.8+,无需特意安装hookhook一套工具函数的集合,函数组件专用,状态、引用、方法都可以实现- 一定要放在
函数组件的第一层
状态 state ush
useState
属性 props
- 作为函数的参数获取
props值
设置
<Welcome name="OY"/>
复制代码
读取
function Welcome(props) {
return (
<h1>Hello, {props.name}</h1>
);
}
使用组件 imp
import Dialog from './components/Welcome'
ReactDOM.render((
<>
<Welcome name="Old Yellow"/>
</>
), document.getElementById('root'));
遍历数据
- 单行
render() {
let arr = [1,2,3];
return (
arr.map((item,index)=><div key={index}>{item}</div>)
);
}
- 换行
()
render() {
let arr = [1,2,3];
return (
arr.map((item,index)=>(
<div key={index}>{item}</div>
))
);
}
- 组合
{}
render() {
let arr = [1,2,3];
return (
<>
{arr.map((item,index)=>(
<div key={index}>{item}</div>
))}
<h1>Hello, OY!</h1>
</>
);
}
组件嵌套
function Parent() {
return (
<ul>
<Child/>
<Child/>
<Child/>
</ul>
);
}
function Child() {
return (
<li>li</li>
);
}
组件引用
- 起名字
ref
<input ref="input1" type="text"/>
- 引用
refs数组
this.refs
实例
class Demo extends React.Component {
constructor(...args){
super(...args);
this.state = {
num:0
}
}
add(){
this.setState({
num:Number(this.refs.input1.value)+Number(this.refs.input2.value)
});
}
render() {
return (
<>
<input ref="input1" type="text"/>
+
<input ref="input2" type="text"/>
=
{this.state.num}
<button onClick={this.add.bind(this)}>Add</button>
</>
);
}
}
组件通信
- 父找子
- 子找父
- 非父子级
父找子
父组件想修改子组件的状态 state,可以给子组件定义 ref,父组件通过 ref 再引用子组件的方法来更新子组件的状态。
父组件
class Demo extends React.Component {
constructor(...args){
super(...args);
}
add(){
// 父组件通过 `ref` 来引用子组件的 `add` 方法来更新子组件的状态。
this.refs.child1.add(Number(this.refs.input1.value));
}
render() {
return (
<>
<h2>Parent</h2>
<input ref="input1" type="text"/>
<button onClick={this.add.bind(this)}>Add</button>
<Child ref="child1"/>
</>
);
}
}
子组件
class Child extends React.Component {
constructor(...args){
super(...args);
this.state = {
num:0
}
}
add(n){
this.setState({
num:this.state.num+n
});
}
render() {
return (
<>
<h2>Child</h2>
{this.state.num}
</>
);
}
}
子找父
子组件想修改父组件的状态 state,可以将父组件的 this 通过 props 属性传递给子组件,这样子组件就可以使用父组件的的方法来修改父组件的状态。
<Child parent={this}/>
父组件
class Demo extends React.Component {
constructor(...args){
super(...args);
this.state = {
num:0
}
}
add(n){
this.setState({
num:this.state.num+n
});
}
render() {
return (
<>
<h2>Parent</h2>
{this.state.num}
<Child parent={this}/>
</>
);
}
}
子组件
class Child extends React.Component {
constructor(...args){
super(...args);
}
add(){
// 将父组件的 `this` 通过 `props` 属性传递给子组件,使用父组件的的方法来修改父组件的状态。
this.props.parent.add(Number(this.refs.input1.value));
}
render() {
return (
<>
<h2>Child</h2>
<input ref="input1" type="text"/>
<button onClick={this.add.bind(this)}>Add</button>
</>
);
}
}
Redux
非父子级
路由
- 通过不同地址,展现不同组件
- 路由都是通过
history实现的
react-router-dom
react-router 的 DOM 绑定版
安装
yarn add react-router-dom
npm install --save react-router-dom@5.2.0
三大对象
Router包裹在最外层,就像数组的[]包裹Route路由配置Link路由跳转的链接
Router
推荐放在 index.js 内
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router} from 'react-router-dom';
import App from './App';
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
);
Route
路由表 同样 推荐放在 App.js 内
import {Route, Link} from 'react-router-dom';
import Index from './components/Index';
import Work from './components/Work';
function App() {
return (
<>
<Link to="/">Index</Link>
<Link to="/work">Work</Link>
{/* 路由表 */}
<Route path="/" exact component={Index} />
<Route path="/work" component={Work} />
</>
);
}
export default App;
Link
其实会生成一个 a 链接,需要用户点击
<Link to="/">Index</Link>
<Link to="/work">Work</Link>
带更多参数
<Link to={{
pathname:'/',
search:'?name=oy',
hash:'#ag1'
}}>Index</Link>
Redirect
自动跳转,无需点击,适合判断跳转
<Redirect to="/login">Login</Redirect>
路由参数
- 设置
<Route path="/article/:id" component={Article} />
- 链接
<Link to="/article/1">01</Link>
<Link to="/article/2">02</Link>
...
- 读取参数
props.match.params
function Article(props) {
let {id} = props.match.params;
return (
<div>
{id}
</div>
)
}
路由嵌套
- 路由表可以写在任何组件内
- 所谓嵌套就是在子组件内再写路由表
- 嵌套规则 article article/1 article/1/1 article/1/1/1
- 解耦合建议使用
props.match里的path来配置,可以实现无限极嵌套
App.js
import {Route, Link} from 'react-router-dom';
import Article from './components/Article/Article';
function App() {
return (
<>
{/* 路由表 */}
<ul>
<li><Link to="/article">Article</Link></li>
</ul>
<Route path="/article" component={Article} />
</>
);
}
export default App;
Article.js 嵌套子路由 Article_1.js
{${path}/1}等于来自上级目录的/article/加上1
import React from 'react'
import {Route, Link} from 'react-router-dom';
import Article_1 from './Article_1';
function Article(props) {
let {path} = props.match;
return (
<>
<h2>Article</h2>
{/* 路由表 */}
<ul>
<li><Link to={`${path}/1`}>Article_1</Link></li>
</ul>
<Route path={`${path}/1`} component={Article_1} />
</>
)
}
export default Article
Article_1.js 再嵌套子路由 Article_1_1.js
{${path}/1}等于来自上级目录的/article/1/加上1
import React from 'react'
import {Route, Link} from 'react-router-dom';
import Article_1_1 from './Article_1_1';
function Article_1(props) {
let {path} = props.match;
return (
<>
<h2>Article1</h2>
{/* 路由表 */}
<ul>
<li><Link to={`${path}/1`}>Article_1_1</Link></li>
</ul>
<Route path={`${path}/1`} component={Article_1_1} />
</>
)
}
export default Article_1
Article_1_1.js
import React from 'react'
function Article_1_1() {
return (
<>
<h2>Article1_1</h2>
</>
)
}
export default Article_1_1
路由跳转
push堆栈尾添加pop堆栈尾取出replace替换
通过 this.props.history 实现各种跳转
this.props.history.go(7)或返回this.props.history.go(-7)this.props.history.goBack()等于this.props.history.go(-1)this.props.history.goForward()等于this.props.history.go(1)this.props.history.push('/article')push带参数 this.props.history.push({ pathname:'/', search:'?name=oy', hash:'#ag1' })this.props.history.replace('/article')无法回退replace带参数 this.props.history.replace({ pathname:'/', search:'?name=oy', hash:'#ag1' })
Redux
state状态管理容器,管理数据- 单向数据流,只能去不能回来(
Vue是双向) - 组件之间共通数据,适合大型应用
- 其实可以独立使用
- 数据不需要共享,就放组件里
单向数据流
react-redux
redux和react-redux都需要安装redux是本体,react-redux是桥梁
安装
yarn add redux react-redux
npm install --save redux react-redux
createStore
import { createStore } from 'redux'- 推荐新建
store.js配置 - 组合状态对象,多个
reducer可以使用combineReducers - 使用
combineReducers后,一个action也会让所有reducer都运行,但是性能不影响
多个 reducer 以及 action 都是写在 store.js 里的
import { createStore, combineReducers } from 'redux'
function person(state = {name:'oy',age:18}, action) {
switch (action.type) {
case 'add':
return {
...state,
age: state.age + 1,
};
case 'rename':
return {
...state,
name: action.value,
};
default:
return state
}
}
function person2(state = {name2:'oy2',age2:28}, action) {
switch (action.type) {
case 'add':
return {
...state,
age2: state.age2 + 1,
};
default:
return state
}
}
export default createStore(combineReducers({person,person2}));
Provider
- 用于包裹整个程序,实现跨组件共享数据
- 也是在
index.js使用 import { Provider } from 'react-redux'
index.js 引入 store.js
import { Provider } from 'react-redux'
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App city="shanghai" />
</Provider>,
document.getElementById('root')
);
connect
- 起链接作用,用来读取和修改数据
connect参数里state是来自redux的store数据connect参数里props来自父级<App city="shanghai" />- 合并后
redux的store数据全部会进入props里 connect里封装action,用来实现修改数据import { connect } from 'react-redux'
import { connect } from 'react-redux'
function App(props) {
const add = () => {
props.add();
}
return (
<>
{props.person.age}
<button onClick={add}>+</button>
</>
);
}
export default connect((state,props)=>{
return Object.assign({},props,state);
},{
add(){
return{
type:'add'
}
}
})(App);
Proxy
-
通过代理,实现跨域访问
-
安装
yarn add http-proxy-middleware -
新建
src/setupProxy.js -
配置 const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) { app.use( '/api', createProxyMiddleware({ target: 'http://localhost:8080', changeOrigin: true, }) ); app.use( '/admin', createProxyMiddleware({ target: 'http://localhost:8080', changeOrigin: true, }) ); }; -
例:请求访问
/admin实际访问的是http://localhost:8080/admin
导出
- 经常遇到导出生产环境后
css或者img路径错误404报错 - 一级和二级目录都支持
- 页面一刷新就没了必须要用
HashRouter
持续更新