文章目录
什么是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
- 什么是状态管理?
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
翻译:状态管理主要管理js的状态数据(变量),让这些状态数据可以跨组件共享,如果哪一个组件更新状态,可以很方便追踪。redux模块包提供的状态管理方案可以脱离react框架去使用。
flux - redux - vuex
- 什么是react框架状态管理?
react-redux模块包,提供的功能专注于react框架。
核心概念:核心三个对象以及三者的关系。
state对象:保存数据对象,不能直接修改。分发action去更改state。
reducer对象:一个普通的函数,充当“通知者”的角色,通知action去更改state。
编写专门的函数来决定每个 action 如何改变应用的 state,这个函数被叫做 reducer。
action对象:一个普通字面量对象{},充当了“劳动者”的角色,主要负责把state更改成新值。
仓库store要通过订阅subscribe,才能被组件使用。
仓库接收到组件的订阅后,仓库分发去action,被reducer捕获。reducer根据action的type的不同,让action执行相应的更改。
- 三大原则
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