react&&redux

什么是react

React 是一个用于构建用户界面的 JAVASCRIPT 库。

React 主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。

React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。

React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。

React有哪些特点?

1.声明式设计 −React采用声明范式,可以轻松描述应用。

2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。

3.灵活 −React可以与已知的库或框架很好地配合。

4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

安装react

npm install -g create-react-app

创建reate项目

create-react-app my-app

JSX: js + html 在传统网页渲染时, 通过DOM原生操作查找html标签并设置,

js 和 html是分离状态, 而jsx中html和js是一体不分离的, js可以嵌套html, html中可以嵌套js

我们不需要一定使用 JSX,但它有以下优点:

1,JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。

2,它是类型安全的,在编译过程中就能发现错误。

3,使用 JSX 编写模板更加简单快速

jsx中不能使用if else判断, 但可以用三目运算

元素标签如皋有for属性,但是for属性和jsfor循环会冲突,使用for属性采用htmlfor

class属性会与js es6 中的类关键字产生冲突 所以使用calss属性变成className

什么是React组件?

概念: 组件就是对代码中的数据和函数的封装结构,从根本上来说react组件就是一个类


react组件有两种创建方式: 
1, 通过ES5的function构造函数创建
      function MyCom1() {
            return <h1>这是第一个组件模板</h1>
      }
2, 通过ES6的类语法创建
      class MyCom2 extends React.Component{
            render(){
                return <h1>这是第二个组件模板</h1>
            }
       }

react组件由state状态可以分为有状态组件和无状态组件

有状态组件: class定义组件,并在构造器中定义了state对象 可以定义状态 也可以添加生命周期函数


无状态组件: 1, function构造函数定义的组件  不能定义状态,不能添加声明周期函数

            2, class定义组件但没有实现构造器

将jsx模板渲染到指定位置

ReactDOM.render() 参数1 文件的模板 或者组件 参数二 文件渲染的dom节点;

组件更新

this.setState({})

注意: setState()是对state对象的更新合并, 把参数对象合并到构造器中的state对象,原有的字段会更新,没有的字段会添加;

组件中的声明周期函数 带*表示生命周期已经被替代,

* componentWillMount   组件将要渲染的时候执行,在初始化state之后执行,但是这个是老的生命周期,被DidMount替代

componentDidMount  组件被挂载后调用(插入 DOM 树中)

componentWillReceivePorps(nextProps)组件加载时不调用,组件接受新的props时调用  参数为更新之后的props;

shouldComponentUpdate(nextProps, nextState) 组件是否需要更新,如果需要更新return true,不需要更新return false; nextProps 更新后的props和state

* componentWillUpdate(nextProps, nextState)  数据将要更新的时候调用  参数代表更新之后状态;

componentDidUpdate(prevProps, prevState)   数据更新完毕之后调用,参数为更新之前的数据;

componentWillUnmount  组件将要卸载的时候调用;

生命周期执行流程

https://www.cnblogs.com/jpwz/p/12411646.html

 渲染阶段 
1 先执行constructor
2 再去执行getDefaultProps 接受传递该组件的数据,在执行constructor参数为props,并且可以直接使用
3 在去执行getInitialState() 初始化state, ===>this.state = {}
4 再去执行将要渲染页面 componentWillMount()
5 再去执行render函数
6 再去执行componentDidMount() 渲染完毕的钩子函数

2 更新阶段
Updating(更新阶段:涉及5个钩子函数)
componentWillReceivePorps(nextProps)组件加载时不调用,组件接受新的props时调用
shouldComponentUpdate(nextProps, nextState)组件接收到新的props或者state时调用,return true就会更新dom(使用diff算法更新),return false能阻止更新(不调用render)
componentWillUpdata(nextProps, nextState)组件加载时不调用,只有在组件将要更新时才调用,此时可以修改state
componentDidUpdate()组件加载时不调用,组件更新完成后调用react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行

.3、Unmounting(卸载阶段:涉及1个钩子函数)
 componentWillUnmount() 组件渲染之后调用,只调用一次

组件传值

    父传子

    在子组件上面添加自定义属性,子组件可以通过props这个参数接受到传递的数据;

    <MyCom MyProps={this.state.name}></Mycom>

     在子组件的render函数使用this.props绑定传递属性

     子传父

     在父组件中定义一个修改方法,将这个修改方法通过自定义属性传递到子组件,子组件调用相应的修改方法 传递的实参 父组件就可以在修改方法中将数据通过setState接受数据;

     <button onClick={event=>{this.props.myChange(event,this.state.title)}}>233</button>

     插槽传值

     在组件标签的内容区域 都会传递到组件的props.children 字段中,如果插槽内容过多的时候this.props.children是一个数组,我们也可以同过下标或者key属性辨别所传递是插槽是存放到那个位置;
      <SonCom>
            {this.state.msg}
                <h1>10</h1>
                <h2>你好</h2>
        </SonCom>

双向绑定

在react 实现双绑定 我们需要同时实现 value 和 onChange 事件 来完成双向绑定 如果仅仅使用value 则需要替换成 defaultValue 来替换value;

<input type="text" value={this.state.name} onChange={this.changeData.bind(this,"name")}/>

create-react-app 脚手架的使用

1 react-router-dom5.0以上的版本

组件包含:Route组件对应的是一个路由匹配:包含path属性(路由地址)和component属性(对应的路由页面)
Switch组件:包含所有路由匹配规则
Redirect组件重定向组件 from to属性
Link组件:跳转组件to属性
HashRouter组件 使用的是hash模式

1 在router/index.js 创建路由匹配组件,并且路由组件导出

export default fuction MyRouter(){
  return (
      <switch>
        <route path componet=></router>
      </swich>
  )
}

2 在App.js导入路由组件 作为路由页面展示的一个容器
import routerView from “./router/index”
<Link to=""></Link>

3 在入口文件对app组件进行使用哈希模式或者浏览器模式
import {HashRouter,BrowserRouter} from "react-router-dom";

//BrowserRouter  浏览器模式

<HashRouter>
<App/>
</HashRouter>

ref

创建一个ref

constructor(props) {
        super(props);
        this.MyRef = {
            one: React.createRef()  //创建一个ref
        }
        this.state = {
        }
    }

在组件中添加ref 属性获取dom

   <p ref={this.MyRef.one}>{this.props.name}</p>
   <p ref={dom=>this.MyRef.one=dom}></p>

关于 react-router-dom


Route组件:配置一个路由和路由所对应的组件

Switch组件:包含所有路由组件对象

Redirect组件 重定向组件

Link 组件 路由跳转组件 有一个to属性添加路径;

NavLink  组件 可以在路由跳转的时候添加类型 activeClassName

Route:path属性添加路由

component:路由所对应的组件页面

exact:确切的,只要当当前页面的路径为/home,才会匹配到对应的页面

例如当前/home, 直接在浏览器写/home/2路径,也可以匹配到对应的页面

如果加上exact,只有路径为/home,才能匹配到

路由跳转

使用Link 组件的to 属性 可以是个路由地址,也可以是一个对象{ pathname:"" search:""}

获取查询字段

this.props.location.search

当Link标签to属性是一个对象的时候,我们也可以通过自定义对象传递数据,但是数据仅仅作用于从这个页面跳转过去之后,如果再次刷新页面,则数据就会丢失;

let obj = {
            pathname: "/login",
            //自定义的数据,仅存在于从当前路由跳转到目标路由的时候;
            myData: {
                title: "标题",
                text: "你猜",
            },
        };
        
        

 获取自定义数据


this.props.location.myData

动态路由

设置动态路由

<Route path="/home/:id">

获取动态路由的值

this.props.match.params.id

通过js事件跳转路由

通过this.props.history 进行页面跳转

//在历史记录中添加一条新记录,并跳转页面; 与Link组件的to属性保持一致,也可以传递自定义数据;

this.props.history.push()

//替换当前路由页面,并不会产生历史记录;

this.props.history.replace()  

//向后跳转

this.props.history.back()

过度动画

react中没有自定义的过度动画,我们可以使用 react-transition-group 插件 来实现过度动画;

import { CSSTransition } from "react-transition-group";  //导出动画组件,并包裹在需要做动画的元素上;

<CSSTransition in={this.state.show} timeout={200000} classNames="myfade" appear={true}>

in 属性 控制元素的显示与隐藏 并执行规定的动画效果;

timeout 动画执行的时间,也可以在transition 中设置;

classNames  组件做动画的基类名称 在css中设置相应基类的出场 和离场动画;

appear={true}  规定元素在被创建的时候使用使用css入场动画;

元素入场的动画效果(css)

// myfade 为动画组件的 基类 也就是calssNames的属性值;

myfade-enter{}   //进入动画前的样式;

myfide-enter-active{}  //进入动画中的样式,在此处设置transition 并设置上动画结束的样式(这点与vue不同)

myfide-enter-done{}   //动画结束之后的样式;

元素立场的动画

myfide-exit{}   //离场前动画

myfide-exit-active{}  //离场中的样式
 
myfide-exit-done{}   //离场后的样式;

组件/元素渲染的动画效果

.myfade-appear{}  //渲染之前的动画效果;

.myfide-appear-active{}  //渲染中的动画效果和结束样式;


redux 状态管理

redux是专门为react设计的状态管理工具。虽然专门为react设计,但是也可以脱离react独自使用。

redux的状态管理思想是:将整个应用的所有状态都集中在一个唯一的store中进行管理,

store中的状态只能通过设定的方法进行修改。这样做的好处是可以保证store中的状态都是以可预测的形式进行变化,易维护,易监听。

redux是专门为react设计的状态管理工具。虽然专门为react设计,但是也可以脱离react独自使用。

redux的状态管理思想是:将整个应用的所有状态都集中在一个唯一的store中进行管理,store中的状态只能通过设定的方法进行修改。这样做的好处是可以保证store中的状态都是以可预测的形式进行变化,易维护,易监听。


在react项目中使用redux要下载redux和react-redux两个依赖项。


react-redux把状态映射到子组件 分发 reducer

redux  创建reducer action store等   

redux-thunk  thunk处理发送请求异步。

https://www.jianshu.com/p/728a1afce96d
  1. 什么是状态管理?
    Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
    翻译:状态管理主要管理js的状态数据(变量),让这些状态数据可以跨组件共享,如果哪一个组件更新状态,可以很方便追踪。redux模块包提供的状态管理方案可以脱离react框架去使用。

flux - redux - vuex

  1. 什么是react框架状态管理?
    react-redux模块包,提供的功能专注于react框架。

核心概念:核心三个对象以及三者的关系。
state对象:保存数据对象,不能直接修改。分发action去更改state。
reducer对象:一个普通的函数,充当“通知者”的角色,通知action去更改state。
编写专门的函数来决定每个 action 如何改变应用的 state,这个函数被叫做 reducer。
action对象:一个普通字面量对象{},充当了“劳动者”的角色,主要负责把state更改成新值。

仓库store要通过订阅subscribe,才能被组件使用。

仓库接收到组件的订阅后,仓库分发去action,被reducer捕获。reducer根据action的type的不同,让action执行相应的更改。

  1. 三大原则
    3.1. 单一数据源:当有多个reducers时,虽然每个reducer有自己的state,但最终还是通过combineReducers()合并到一个object tree。
    3.2. state是只读的。所以每次更改state,都不是直接修改state,而是返回一个新的state对象。
    3.3. 在redux中修改state的唯一方式:通过reducer来修改。reducer就是一个普通函数(纯函数)
    为了描述 action 如何改变 state tree ,你需要编写 reducers。

异步action文档:

redux-thunk:  https://github.com/reduxjs/redux-thunk
redux-promise: https://github.com/redux-utilities/redux-promise


http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html
https://github.com/redux-utilities/redux-actions

store.dispatch()可以分发同步,异步action。
安装redux-thunk模块,通过applyMiddleware()把redux-thunk模块和仓库store联系起来。联系起来后,store.dispatch()才可以分发异步的action功能。默认情况下store.dispatch()只可以分发同步action。

定义异步action,内部在数据获取后,再去调用同步action。只要同步action被调用后,会被reducer被捕获到。reducer再根据action的type去更新state。

redux-promise模块只提供让store拥有分发异步action的能力。它没有提供定义异步action能力。找到拥有创建异步action的模块包(redux-actions:拥有创建异步action的能力。),所以说redux-promise要和redux-actions配合使用,才达到了redux-thunk模块包相同的功能。

+++++++++++++++++++++

创建步骤

第一步: 创建仓库

1, 下载安装 redux 模块 和 react-redux模块
	npm install redux --save
	npm install react-redux --save

2,创建数据仓库文件 src/store/index.js 从redux导入创建函数creatStore, 合并函数combineReducers
	import { createStore, combineReducers ,applyMiddleWare} from "redux";

3, 定义reducer函数
	function countReducer(state = 0, action){
    	    switch (action.type) {
       	        case "ADD":
           		return state + action.number
			break
  		case "min":
           		return state - action.number
			break
        	        default:
           		return state 
    	    }
	}
4, 把多个reducer函数合并成一个
	var reducers = combineReducers({
    		count: countReducer,
	})
5, 创建并导出数据仓库
	export default createStore(reducers)

第二步: 读取仓库数据

1, 在入口文件index.js中导入状态仓库store和状态更新供应组件Provider 
	import { Provider } from "react-redux";
	import store from "./store/index";

2, 在入口文件index.js中,使用Provider 包裹App根组件, 并设置store
	<Provider store={store}>
      	        <App />
  	</Provider>

3, 在使用状态管理的组件中导入状态仓库联合函数connect (高阶组件)
	import { connect } from "react-redux";

4, 在导出组件之前,创建状态数据映射函数映射状态数据
	function mapState(store){
    	        return {
        		name: store.name,
    	        }
	}

5, 在导出组件之前, 使用联合函数connect把仓库中的状态数据拼接到组件的props中
	MyCom= connect(mapState)(MyCom)

6, 在组件中通过this.props调用状态仓库中的数据
	{this.props.name}

第三步: 更新仓库数据

1, 在需要更新数据的组件中导入状态仓库
	import store from "../../store/index"

2, 在需要更新的位置, 更新仓库中的数据
	store.dispatch({
           	    type: "Add",
            	value: this.refs.input.value
       	 })

附加: 监听仓库中数据的更新
	store.subscribe(()=>{
            	    console.log("数据已更新")
        	})
	
注:  store.subscribe() 添加监听后会返回一个函数, 调用返回的函数可以结束监听

在action中发送异步请求(需要使用redux-thunk)

import { createStore, combineReducers ,applyMiddleWare} from "redux";
import ReduxThunk from "redux-thunk"
let store = createStore(reducers,applyMiddleWare(ReduxThunk ))  // 参数二写入中间件;

导入axios 之后, 在action.js中

function queryData() {
    return async dispatch => {
        let res = axios.get("/myApi/api/RoomApi/live");
        return dispatch(roomList(res.data.data));
    }
}

也可以使用promise

function queryData() {
    // 返回一个dispatch 函数,参数式dis
    return async dispatch => {
	return new Promise((resolve,reject)=>{
	axios.get("/myApi/api/RoomApi/live").then(res=>{
		dispatch(roomList(res.data.data));
		})
	})
    }
}

修改reducer传值

 connect() 返回一个改造函数,把本组件改造之后导出
 把状态传进组件的props 的函数 产生为仓库对象,返回值一个对象;
export default connect(state => ({
    s1: state.str,
    roomList: state.roomList
}), dispatch => ({
    changeStr(v) { dispatch(changeStr(v)) },
    clearstr: () => dispatch(clearstr()),
    queryData: () => dispatch(queryData())
}))(First);


在组件中使用  this.props.changeStr("test") 进行修改reducer中的状态; 


代理服务器的配置

1, 下载安装代理模块
	npm install http-proxy-middleware --save

2, 在src目录中新建setupProxy.js文件,在文件中放入如下代码:

const { createProxyMiddleware } = require('http-proxy-middleware')

module.exports = function (app) {
    app.use('/api',createProxyMiddleware(
        {
            target: 'http://127.0.0.1:3000',
            changeOrigin: true,
            pathRewrite: {
                "^/api": ""
            }
        })
    )
}


附加: 
设置项目端口号:  node_modules/react-scripts/scripts/start.js
也可以通过指令 npm install eject 把服务器配置暴漏出来, 修改scripts/start.js

redux 持久化存储

function ageReducer(state = localStorage.getItem("age") * 1 || 0, action) {
    switch (action.type) {
        case "ADD":
            localStorage.setItem("age", state + 1);
            return state + 1;
        default:
            return state;
    }
}

使用 redux-persist 持久化存储

在store.js 中添加以下代码

import { createStore, applyMiddleware } from "redux";
import ReduxThunk from "redux-thunk";
import reducers from "./reducers";

import { persistStore, persistReducer } from 'redux-persist';
import session from 'redux-persist/lib/storage/session';  //sessionStorage存储
import session from 'redux-persist/lib/storage";  //localStorage存储;


const persistConfig = {
    key: 'root',  //根组件
    storage: session,  //配置需要存储的方式
    whitelist: ["ageReducer"]  //配置需要持久保存的状态;
};

const myPersistReducer = persistReducer(persistConfig, reducers);

const store = createStore(myPersistReducer, applyMiddleware(ReduxThunk))

export const persistor = persistStore(store);

export default store;

在index.js 中注入persist

import { Provider } from "react-redux";
import store from "./store/store";
import { persistor } from "./store/store"
import { PersistGate } from 'redux-persist/lib/integration/react';


ReactDOM.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App />
    </PersistGate>
  </Provider>,
  document.getElementById('root')
);

https://segmentfault.com/a/1190000018150177

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值