react(二)

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;
原文地址:https://www.cnblogs.com/luckyzs/p/13178924.html