react(二)
一、create-react-app(脚手架)
是Facebook官方推出的一个款react脚手架
1.1 环境
需要安装node 升级到最新版本
1.2 安装
npm install -g create-react-app
create-react-app --version
cd 指定目录
create-react-app 项目名
npm start
1.3 目录结构
my-app/
README.md
node_modules/
package.json
.gitignore
public/
favicon.ico
index.html
manifest.json
src/
App.css
App.js
App.test.js
index.css
index.js
logo.svg
manifest.json 指定了开始页面 index.html,一切的开始都从这里开始,所以这个是代码执行的源头。
二、传参
2.1 正向传参
// 父组件
// 传递参数text:<Son text="你好"/>
import React, {Component} from 'react';
import Son from "./son"
class Parent extends Component {
render() {
return (
<div>
<Son text="你好"/>
</div>
);
}
}
export default Parent;
// 子组件
// 接受参数text:this.props.text
import React, {Component} from 'react';
class Son extends Component {
render() {
return (
<div>
son--{this.props.text}
</div>
);
}
}
export default Son;
2.2 反向传参
// 父组件
// 接受fufun,修改自身setState.text为传递过来的text
import React, {Component} from 'react';
import Son from "./son"
class Parent extends Component {
constructor(props){
super(props)
this.state={
text:"我是parent默认值",
}
}
dataFun=(text)=>{
this.setState({
text:text
})
}
render() {
return (
<div>
parent---{this.state.text}
<Son text="你好" fufun={this.dataFun}/>
</div>
);
}
}
export default Parent;
// 子组件
// 点击事件传递参数fufun,携带数据sonText:
// 方式一:
// this.props.fufun.bind(this,this.state.sonText)
// 方式二:
// ()=>{this.props.fufun(this.state.sonText)}
import React, {Component} from 'react';
class Son extends Component {
constructor(props){
super(props)
this.state={
num:123,
sonText:"son数据"
}
}
render() {
return (
<div>
<button onClick={this.props.fufun.bind(this,this.state.sonText)}>点我将数据发送给parent</button>
<button onClick={()=>{this.props.fufun(this.state.sonText)}}>点我将数据发送给parent</button>
</div>
);
}
}
export default Son;
2.3 同级传参:pubsub-js
// 安装
cnpm install --save pubsub-js
import Pubsub from "pubsub-js"
// 同级传参数
Pubsub.Pubsub.publish("evt",this.state.num)
// 同级取参数
Pubsub.subscribe("evt",(msg,data)=>{
console.log("phone",data)
})
三、数据请求与json-server
# json-server:模拟数据
npm install json-server -g
# axios:数据请求
npm install --save axios
# 项目根目录新建mook目录,创建data.json,写入json数据
cd mook
json-server data.json --port 4000
# 页面中引用data.json
import React, {Component,Fragment} from 'react';
import axios from "axios"
class Home extends Component {
constructor(props){
super(props)
this.state={
arr:[]
}
}
componentDidMount() {
this.ajaxfun()
}
ajaxfun=()=>{
axios.get("http://localhost:4000/arr").then((ok)=>{
console.log(ok)
this.setState({
arr:ok.data
})
})
}
render() {
return (
<Fragment>
{this.state.arr.map((v,i)=>{
return <p key={i}>{v.name}</p>
})}
</Fragment>
);
}
}
export default Home;
四、跨域
4.1 正向代理--开发环境
一个位于客户端和目标服务器之间的代理服务器,为了获取目标服务器的内容,客户端向代理服务器发送一个请求,代理服务器帮助我们去目标服务器里获取数据并且返给我们。
4.2 反向代理--上线环境
可以通过代理服务器来接受网络上的请求连接,然后将这个请求转发给内部的网络服务器,并且把这个服务器上得到的数据返回给请求的客户端。
4.3 模拟请求真实的网络接口
中国天气网中的数据
# 首先修改文件 node_modules/react-scripts/config/webpackDevServer.config.js
# 找到proxy,修改变量
proxy:{
"/api":{
target:"http://www.weather.com.cn/data/cityinfo/",
changeOrigin:true,
"pathRewrite":{
"^/api":"/"
}
}
}
import React, {Component} from 'react';
import axios from 'axios'
class Weather extends Component {
componentDidMount() {
axios.get("/api/101320101.html").then((ok)=>{
console.log(ok)
})
}
render() {
return (
<div>
</div>
);
}
}
export default Weather;
五、路由
根据url的不同来切换对应的组件,实现spa(单页面应用),在页面切换的时候不会刷新,更加接近原生体验
学习版本:v5版本
5.1 下载路由
# 下载路由
npm install --save react-router-dom --功能多
npm install --save react-router -- 核心包
5.2 两种路由
react-router:提供了核心的API
react-router-dom:提供了更多的选项
5.3 路由模式
hash模式 -- HashRouter
- hash模式带#号,刷新的时候页面不会丢失
browser -- BrowserRouter
- 历史记录模式,没有#号,他是通过历史记录api来进行路由切换的,刷新会丢失,本地模式不会
5.4 引用路由模块
// 找到index.js
// 引入BrowserRouter,包裹<App />
import {BrowserRouter,HashRouter} from "react-router-dom"
5.5 路由模式包裹根组件
# BrowserRouter
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
# HashRouter
ReactDOM.render(
<React.StrictMode>
<HashRouter>
<App />
</HashRouter>
</React.StrictMode>,
document.getElementById('root')
);
5.6 使用
// App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
// 引入组件
import Home from "./components/Home"
import Parent from "./components/parent"
import Son from "./components/Son"
import Weather from "./components/Weather";
// 引入路由
import {Route} from "react-router-dom"
// 地址连接时
// BrowserRouter
// http://localhost:3000/son
// HashRouter
// http://localhost:3000/#/son
function App() {
return (
<div className="App">
<header className="App-header">
// 配置路由
<Route path="/home" component={Home}/>
<Route path="/parent" component={parent}/>
<Route path="/son" component={Son}/>
<Route path="/weather" component={Weather}/>
</header>
</div>
);
}
export default App;
5.7 路由导航
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Home from "./components/Home"
import Parent from "./components/parent"
import Weather from "./components/Weather";
import Son from "./components/son";
// 多配置一个Link 或者 NavLink
import {Route,Link,NavLink} from "react-router-dom"
function App() {
return (
<div className="App">
<header className="App-header">
<div>
// 使用link标签
<Link to="/home">点我去home</Link>
<Link to="/parent">点我去parent</Link>
<Link to="/son">点我去son</Link>
<Link to="/weather">点我去weather</Link>
</div>
<div>
// NavLink:声明式导航
// 使用时添加了class="active"
// 可以选中的导航动态设置样式
<NavLink to="/home">点我去home</NavLink>
<NavLink to="/parent">点我去parent</NavLink>
<NavLink to="/son">点我去son</Link>
<NavLink to="/weather">点我去weather</NavLink>
</div>
<Route path="/home" component={Home}/>
<Route path="/parent" component={Parent}/>
<Route path="/son" component={Son}/>
<Route path="/weather" component={Weather}/>
</header>
</div>
);
}
export default App;
5.8 exact
如果path是'/',会匹配全路径,对应的路由每次都会匹配到。为了防止这种效果没加入exact。
<Route path="/home" exact component={Home}/>
5.9 Switch
唯一渲染
import {Route,Link,NavLink,Switch} from "react-router-dom"
<Switch>
<Route path="/home" component={Home}/>
<Route path="/home" component={Home}/>
</Switch>
5.10 重定向
import {Route,Link,NavLink,Switch,Redirect} from "react-router-dom"
<Redirect><Route from="/" to="/home" exact/></Redirect>
5.11 二级路由
# 目录components下新建home目录,home下新建HomeA.js,然后rcc
# 目录components下新建home目录,home下新建HomeB.js,然后rcc
# home.js
import React, {Component} from 'react';
import {Route,NavLink} from "react-router-dom"
import HomeA from "../home/HomeA"
import HomeB from "../home/HomeB"
class Home extends Component {
render() {
return (
<NavLink to="/home/homeA">HomeA</NavLink>
<NavLink to="/home/homeB">homeB</NavLink>
<Route path="/home/homeA" component={HomeA}/>
<Route path="/home/homeB" component={HomeB}/>
);
}
}
export default Home;
5.12 withRouter(高阶组件)
不是路由切换的组件也具有路由的三个属性(location,match,history)
高阶组件(HOC)
参数是一个组件,同时返回的也是一个组件,这类组件我们成为高阶组件。
// 引入
import {Route,withRouter} from "react-router-dom"
// 包裹
export default withRouter(App);
路由式导航
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Home from "./components/Home"
import Parent from "./components/parent"
import Weather from "./components/Weather";
import Son from "./components/son";
import {Route,NavLink,withRouter} from "react-router-dom"
function App() {
// history.listen((link)=>{
// Link.pathname 切换路径
// })
props.history.listen((link)=>{
console.log(link)
})
console.log(props)
return (
<div className="App">
<header className="App-header">
<div>
// 声明式导航
<NavLink to="/home">点我去home</NavLink>
<NavLink to="/parent">点我去parent</NavLink>
<NavLink to="/son">点我去son</NavLink>
<NavLink to="/weather">点我去weather</NavLink>
// 编程式导航
<button onClick={()=>{props.history.push("/home")}}>点我去home</button>
<button onClick={()=>{props.history.push("/parent")}}>点我去parent</button>
<button onClick={()=>{props.history.push("/son")}}>点我去son</button>
<button onClick={()=>{props.history.push("/weather")}}>点我去weather</button>
</div>
<Route path="/home" component={Home}/>
<Route path="/parent" component={Parent}/>
<Route path="/son" component={Son}/>
<Route path="/weather" component={Weather}/>
</header>
</div>
);
}
export default withRouter(App);
路由式传参
params方式进行传参
1、需要在路由规则中设置传递的接收参数。形如::xxx
2、发送参数。直接在跳转路径后进行编写
3、 接收参数。props.match.params.参数名
4、优势。刷新地址,参数依然存在。
5、缺点。只能传递字符串。参数较多时,url过长变丑陋
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Home from "./components/Home"
import {Route,NavLink,withRouter} from "react-router-dom"
function App() {
// history.listen((link)=>{
// Link.pathname 切换路径
// })
props.history.listen((link)=>{
console.log(link)
})
return (
<div className="App">
<header className="App-header">
<div>
<NavLink to="/home/我是参数">点我去home</NavLink>
</div>
<Route path="/home/:id" component={Home}/>
</header>
</div>
);
}
export default withRouter(App);
import React, {Component,Fragment} from 'react';
class Home extends Component {
componentDidMount() {
// 接收参数
console.log(this.props.match.params.id)
}
render() {
return (
);
}
}
export default Home;
query方式传参
1、不需要在路由规则中传递参数的配置
2、直接发送数据
3、使用this.props.location.query.xxx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Home from "./components/Home"
import {Route,NavLink,withRouter} from "react-router-dom"
function App() {
// history.listen((link)=>{
// Link.pathname 切换路径
// })
props.history.listen((link)=>{
console.log(link)
})
return (
<div className="App">
<div>
<NavLink to={{pathname:"/home",query:{name:"小明"}}}>
点我去home
</NavLink>
</div>
<Route path="/home/:id" component={Home}/>
</div>
);
}
export default withRouter(App);
import React, {Component,Fragment} from 'react';
class Home extends Component {
componentDidMount() {
// 接收参数
console.log(this.props.location.query.name)
}
render() {
return (
);
}
}
export default Home;
六、Hook
Hook 是 React 16.7新增特性。可以让无状态组件使用状态。
在React开发中,状态的管理必不可少。
以前进行状态管理,需要使用类组件或者redux等来管理。
// 引用Hook中的useState
import React,{useState} from 'react';
import logo from './logo.svg';
import './App.css';
// 类组件方式
class App extends React.Component{
constructor(props){
super(props)
this.state={
text:"我是状态数据"
}
}
render(){
return (
<div>
hello --- {this.state.text}
</div>
)
}
}
// 无状态组件方式
// 可以使用Hook中的useState进行实现
// useState定义一个状态,与类组件的状态不同,函数组件的状态可以是对象也可以是基础类型数据
// userState返回的是一个数组。有两个元素。
// 第一个是当前状态值,第二个是对象表明用于更改状态的函数(类似setState)
function App(props) {
let [val,setVal] = userState(0)
return (
<div className="App">
<div>
使用数据:{val}
<button onClick={()={setVal(val+1)}}>点我进行数据修改</button>
</div>
</div>
);
}
// 多个状态
// 1、声明对象类型的状态
function App(props) {
// 1、声明对象类型的状态
let [val,setVal] = userState({
vala:0,
valb:1,
valc:2
})
return (
<div className="App">
<div>
使用数据:{val.vala} -- {val.valb} -- {val.valc}
</div>
</div>
);
}
// 2、多次声明(推荐使用)
function App(props) {
let [vala,setVala] = userState(0)
let [valb,setValb] = userState(1)
let [valc,setValc] = userState(2)
return (
<div className="App">
<div>
使用数据:{vala} -- {valb} -- {valc}
</div>
</div>
);
}
export default App;
七、redux
7.1 概念
-
javascript提供的一个可预测性的状态容器
-
可预测性:给一个固定的输入,必定会等到一个结果
-
集中管理react中多个组件的状态
-
是一个专门的状态管理库
-
需求场景。
- 某个组件的状态需要共享的时候
- 一个组件需要改变另外一个组件状态的时候
- 组件中的状态需要在任何地方都可以拿到
-
三大原则
- 单一的数据源:整个react中的状态都会被统一管理到store
- state是只读的。不能直接改变state,而是通过触发redux中特定方法进行修改
- 使用纯函数来执行修改操作:action来改变redux中的state
7.2 下载
npm install --save redux
7.3 数据读取
# src 新建 redux 新建 reducer.js
# 创建数据
var obj = [
{name:"xixi",age:18}
]
export function data(state=obj[0].age,action){
switch (action.type) {
default:
return state;
break;
}
}
# redux 新建 store.js
import {createStore} from "redux"
import {data} from "./reducer"
export var store = createStore(data)
# 新建 Home.js
# App.js 引入 Home
// Home.js
import React, {Component} from 'react';
import {store} from './store';
class Home extends Component {
constructor(props){
super(props)
this.state={
num:store.getState()
}
}
render() {
return (
<div>
home -- {this.state.num}
</div>
);
}
}
export default Home;
7.4 数据修改
# src - redux - 新建 action.js
// 增加
export const add=(num)=>{
return {type:"ADD",data:num}
}
// 减少
export const del=(num)=>{
return {type:"DEL",data:num}
}
# reducer.js
var obj = [
{name:"xixi",age:18}
]
export function data(state=obj[0].age,action){
switch (action.type) {
case "ADD":
return state+action.data
break;
case "DEL":
return state-action.data
break;
default:
return state;
break;
}
}
# home.js
import React, {Component} from 'react';
import {store} from './store';
import * as action from './action';
class Home extends Component {
constructor(props){
super(props)
this.state={
num:store.getState()
}
}
componentDidMount(){
// 监听和触发页面的修改操作
store.subscibe(()=>{
this.setState({
num:store.getState()
})
})
}
render() {
return (
<div>
home -- {this.state.num}
<button onClick={()=>{store.dispatch(action.add(1))}}>点我加1</button>
<button onClick={()=>{store.dispatch(action.add(2))}}>点我加2</button>
<button onClick={()=>{store.dispatch(action.del(1))}}>点我减1</button>
<button onClick={()=>{store.dispatch(action.del(2))}}>点我减2</button>
</div>
);
}
}
export default Home;