![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iZxYDO0IDZ2YTY3YWO5YTM3QWOmR2YygjZ3ITOwMGMy8CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
此全家桶用到的库有:redux, react-redux, immutable.js, redux-actions, redux-saga,react-router,此外用的是 ES6 语法,给公司后端同学看的一个简单入门和对项目技术栈的了解
React
React 是一个专注 view 层的(UI层)的 JavaScript 库,它比传统的操作 DOM 的 JavaScript 库性能高很多,并且非常灵活,可以与其他框架很好的配合,通过 React 构建组件,使得代码更加容易得到复用。然而 React 本身做的东西比较小,在大型应用中通常要用到它的周边产品
JSX在 React 中推荐使用 JSX 语法,JSX 是 JavaScript 语法的扩展,它使得组件的构建和数据交互更加简洁和方便,先介绍一下 JSX 语法
1. 书写格式,直接在 JS 里面书写 HTML 标签
let a = <div>welcome div</div>
2. 当有多个标签时,需要使用一个元素包裹它,代码可以自由缩进
let a = <div>
<div>welcome div</div>
<span>span</span>
</div>
3. 单标记必须闭合
<img />
4. 类名用 className, style 必须写成 json 形式
<div style={{width:'100px', height:'200px'}}></div>
5. JSX 语法里面写 JS 代码,代码块用 {}
7. 事件名采用驼峰命名法,单词首字母大写
onclick -> onClick
onchange -> onChange
React 开发模式 1. 直接引入文件,react.js 它是是 React 的核心库,react-dom.js 提供与 DOM 相关的功能 babel.min.js 的作用是将 JSX 语法转为 JavaScript 语法
2. 基于webppack
React 语法定义 React 组件推荐使用 ES6 的语法,ES6 稍后介绍
1. 定义组件
首先用 class 来定义一个组件,组件名称首字母必须大写,继承
React.Component
class Title extends React.Component{
render(){
return <h3> welcome React </h3>
}
}
调用 Title 组件,并把它渲染到页面中中,ReactDOM.render() 方法用于将模板转为 HTML 语言,并插入指定的 DOM 节点
ReactDOM.render(
<Title />,
document.querySelector('#app')
)
2. 组件的参数
我们给 Title 组件传递一个参数 name,在Title 组件内部就可以通过 this.props.name拿到,props 是只读不能修改的
class Title extends React.Component{
render(){
const name = this.props.name
return <h3> welcome {name} </h3>
}
}
ReactDOM.render(
<Title name='React'/>,
document.querySelector('#app')
)
3. 组件的状态
React 将组件看成一个状态机,一开始有一个初始状态,然后用户互动,导致状态变化,从而触发重新渲染 UI ,也就是状态更新时视图跟着更新
class Toggle extends React.Component{
constructor(props){
super(props);
this.state={
show:false
};
}
handleClick =() =>{
this.setState({
show:!this.state.show
});
}
render(){
let showStr = this.state.show ? 'block':'none';
return (<div>
<input onClick={this.handleClick} type="button" value="按钮" />
<div className="box" style={{display:showStr}} />
</div>)
}
}
ReactDOM.render(<Toggle />,document.querySelector('#app'));
4. 组件的生命周期
componentWillMount 组件挂载前
componentDidMount 组件挂载后
componentWillUpdate 组件更新前
componentDidUpdate 组件更新后
componentWillUnmount 组件卸载
此外,React 还提供两种特殊状态的处理函数。
componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用
异步获取数据一般放在 componentDidMount 生命周期中,此时组件已经完成挂载,加载数据成功之后触发组件重新渲染
5. 组件之间通信
- 父级 -> 子级,通过 this.props 传递,父级调用子级的时候,给子级传递 msg 参数,如<Child msg={这里父级数据} />,子组件 Child 通过 this.props.msg 可以拿到父级传递过来的数据 ”这里父级数据“
- 子级 -> 父级,通过回调函数传递参数的形式实现,父级给子级传递一个回调函数,如<Child fnCallBack={这里父级一个函数} />,在子组件 Child 里面执行这个函数,传入参数,this.props.fnCallBack(传入子级数据),这样父级就拿到了子组件传递过来的参数
class Child extends React.Component{
constructor(props){
super(props);
this.state={
msg:'from child msg'
};
}
componentDidMount(){
this.props.fnCallback(this.state.msg);
}
render(){
return (<div>
<h3 >我是子级 ->{this.state.msg}</h3>
</div>)
}
}
class Parent extends React.Component{
constructor(props){
super(props);
this.state={
msg:''
};
}
getMsg =(msg) =>{
this.setState({
msg:msg
});
}
render(){
return (
<div>
<h3>我是父级 -> {this.state.msg || '没有数据'}</h3>
<Child fnCallback={this.getMsg}/>
</div>
)
}
}
ReactDOM.render(
<Parent />,
document.querySelector('#app')
)
6. 表单
通过回调函数获取表单的值,ev.target获取表单元素
class MyInput extends React.Component{
constructor(props){
super(props);
this.state={
msg:''
};
}
handleChange = (ev) =>{
let val = ev.target.value;
this.setState({
msg:val
});
}
render(){
return (<div>
<input type="text" onChange={this.handleChange} />
<strong>{this.state.msg}</strong>
</div>)
}
}
对 React 的基础认识这里只介绍这么多,更多详细介绍请参考 React 官方文档
ES6
ES6 介绍一些常用的方法,更多内容可以参考 ECMAScript 6 入门
1. 新增声明变量的方法 const, let, class, import
2. 变量的解构赋值:可用于提取 json 对象中的数据如 const { key1, key2 } = this.props
3. 字符串的扩展:(``)模板字符串,其中引入变量用${}
4. 函数的扩展:
- rest参数 function f(...a){}
- 箭头函数,f = (参数) => 代码块,箭头函数没有自己的this,而是引用外层的this,如果代码块部分多余一条语句,使用retrun {},如果返回一个对象,用大括号包起来({}),箭头函数大大简化了回调函数:[1,2,3].map(x => x * x)
5. 数组的扩展
- 数组的扩展运算符 ...,代替apply方法,将数组转为函数的参数
Math.max.apply(null,[14, 3, 77])
等同于 Math.max(...[14, 3, 77])
等同于 Math.max(14, 3, 77);
- 合并数组[1,2,...more]
- 数组的深拷贝
const a1 = [1,2];
const a2 = [...a1];
a1[1] = 3;
console.log(a2)} //[1,2]
- 数组的 includes() 方法可代替 indexof()
[1,2,NaN].includes(NaN) //ture
[1,2,NaN].indexOf(NaN) //-1 indexOf()内部使用 === 判断
6. Set:类似于数组,但成员的值是唯一的
- 数组去重
const arr = [1,2,3,4,5,5,5];
const set = [...new Set(arr)]; //[1,2,3,4,5]
- 获取两个数组的并集和交集
let a = new Set([1,2,3]);
let b = new Set([3,4,5]);
并集:let union = [...new Set([...a,...b])] //[1,2,3,4,5]
交集:let intersect = [...new Set([...a].filter(x => b.has(x)))]; //[3]
tips:通过数组的辅助迭代器也可以实现
forEach() 会遍历数组中所有的值并忽略回调函数返回的值
every() 会一直运行直到回调函数返回 false
some() 会一直运行直到回调函数返回 true
every() 和 some() 中特殊的返回值和 for 循环中的 break 语句类似,它们会提前终止遍历
const a = [1,2,3,4,5];
const b = [1,2,3];
交集:const ab = a.filter(ca => b.some(cb => cb === ca)) //[1,2,3]
差集:const a_b = a.filter(ca => !b.some(cb => cb === ca)) //[4,5]
7. promise:一个对象,获取异步操作的消息
var promise = new Promise(function(resolve,reject){
//...code
if(/*异步函数调用成功*/){
resolve(value) //异步操作的结果,作为参数传递出去
}else{
reject(error)
}
})
promise.then(function(){value},function(){error})
8. Iterator: 遍历器,为各种数据结构提供一个统一的,简便的访问接口,部署在Symbol.iterator属性上面
- 原生具备Iterator接口的数据结构有:Array,Map,Set,String,TypedArray,函数的arguments对象,NodeList对象。
- - Iterator接口主要供 for...of消费,for..of 可代替数组实例的 forEach,且可与continue,break,return配合使用,for...in 循环读取键名,for...of 循环读取键值
9. Generator 函数:是一个状态机,封装了多个内部状态,执行返回一个遍历器对象,只有调用 next() 方法才会遍历下一个内部状态
function* generator(){
yeild 表达式
yeild ..
}
10. async函数:返回一个promise对象,可用then方法指定下一步操作
async function(){
await 表达式
await ..
}
immutable.js
提供了通过结构共享实现不可变的、持久的集合:
- 不可变:一个集合一旦创建,在其他时间是不可更改的
- 持久的:新的集合可以基于之前的结合创建并产生突变,原来的集合在新集合创建之后仍然是可用的
- 结构共享:新的集合尽可能通过之前集合相同的结构创建,最小程度地减少复制操作来提高性能
不可变性使得追踪改变非常容易,改变会产生新的对象,因此我们仅需要检查对象的引用是否改变,不可变数据提供了一种更简单的方式来追踪对象的改变,这正是我们实现
shouldComponentUpdate
所需要的。这将会提供可观的性能提升。参考 React 优化性能
将javascript对象转化为不可变数据类型,提供了List, Stack, Map, OrderedMap, Set, OrderedSet, Record等7种不可变的数据结构,代替深拷贝,但不是基于递归复制,性能高很多,详细介绍参考 官方文档
- fromJs(),将javascript的Object,Array转化为Map和List,与javascript Object不同的是,Immutable Maps的键可以是任意类型的参数
- toJS(),将immutable深度转换,toJSON()相对为浅转换,toArray(),转换为数组,toObject(),转换为对象
- get(key),set(key,value),通过键名获取和设值方法,getIn(keypath),setIn(keypath,value),通过键或索引的路径获取和设置嵌套集合的值,另外还有update(key,updater),updateIn()
Redux
Redux 是用来解决component -> action -> reducer -> state 的单向数据流转的问题。
Redux 是一个提供可预测化状态管理的容器,主要包含store,reducer,action三个概念,应用中的 state 以对象树的形式存储在 store 中,state是只读的,惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象
。通过 dispatch action来改变 state,描述 action如何改变 state 的函数叫 reducer,更多参考 Redux 中文文档
1. action
- 可参考action设计规范, type,payload,error.meta,其中 type 是必须的
- 异步action,通过中间件实现
2. reducer
- reducer必须是纯函数
- 可以拆分成多个reducer,通过combineReducers合并
3. store
- 单一Store
- 根据reducer创建 store, createStore(reducer,initialState)
this.props.dispatch({
type: 'GETDATA',
payload: data
})
redux-actions
简化reducer处理函数,handleActions(reducerMap, defaultState)
import { handleActions } from 'redux-actions'
import { fromJS } from 'immutable'
const initialState = fromJS({
loading: true,
data: {},
})
const reducer = handleActions(
{
['GETDATA']: (state, action) => {
return state.set('data', fromJS(action.payload))
.set('loading',false)
}
},
initialState
)
export default reducer
redux-saga
可以把所有的业务逻辑都放在 saga 里,那么 reducer,action.component 可以很纯粹,只干他们原本要做的事情
sagas 是用于处理 redux 的异步操作的中间件,通过 genarator 函数创建,每个任务通过yeild Effects 完成,Effects是javascript对象,包含了sagas middleware执行的信息,Redux-saga 中文文档
1. 创建一个中间件,将 sagas 与redux store建立连接,方法为createSagaMiddleware(...sagas),其中sagas为 Generator 函数列表的数组
const sagaMiddleware = createSagaMiddleware(...sagas)
const store = createStore({
reducer,
initialState,
applyMiddleware(sagaMiddleware)
})
2. saga辅助函数, takeEvery(pattern,saga,...args),在发起的action与pattern匹配时,触发saga,允许并发
import {takeEvery} form 'redux-saga'
function* watch(){
yeild* takeEvery('FETCH_USER',fetchUser)
}
takeLatest(pattern,saga,...args), 只会触发最后那次Action, 防止多次点击提交
3. Effect创造器
- take(pattern),middleware 等到 action 匹配 pattern
- put(action),异步发送action到store
- call(fn,...args),middleware调用fn(...args),fn为一个generator函数或者返回一个promise的普通函数
- fork(fn,...args),无阻塞调用,返回一个task
- cancel(task),取消之前的fork任务
- select(selector,...args),通过selector获取store state的数据,如果参数为空,将获取整个store state
import { call, put} from 'redux-saga/effects'
import { takeEvery } from 'redux-saga'
function* getUserData() {
try {
const res = yield call(get,'http://...') //get为一个返回 promise 的普通函数
if (res.status !== 'success') {
//错误处理
return
}
const userData = res.content
yield put({
type: 'global/set-user',
payload: userData || {}
})
} catch (error) {
console.log(error)
}
}
function* defaultSaga() {
const watchers = yield [
takeEvery('global/getUserData', getUserData),
]
return
}
export default defaultSaga
React-redux
react-redux 用于连接react 和 redux, 使得react 能够访问redux的store
-
组件,能够使你的整个app访问到<Provider/>
中的数据:Redux store
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
2.
connect
方法能够让你把组件和
store
连接起来。
function mapStateToProps(state) {
return {
data: state
};
}
function mapDispatchToProps(dispatch) {
return {
dispatch
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
React Router
React Router 保持 UI 与 URL 同步。它拥有简单的 API 与强大的功能例如代码缓冲加载、动态路由匹配、以及建立正确的位置过渡处理 ,详细参考 React Router 中文文档 ,英文文档
history 监听浏览器地址的变化,并解析url转化为 location 对象,然后 router 使用它匹配到路由,渲染对应的组件
动态路由:Route可以定义 getChildRoutes, getIndexRoute, getComponents 这几个异步执行函数,React Router 会逐渐匹配 URL 并只加载对应页面所需的路径配置和组件
- 获取URL参数:
1.通过 params 获取
<Link to='/inbox/message/:id' />
this.props.params.id
2. 通过 location 的 query 对象获取
hashHistory.push(/fool?bar=baz)
hashHistory.push(/fool,query: { bar: ' baz'} )
<Link to={{ path : ' /fool ' , query : { bar : 'baz' }}}>
this.props.location.query.bar
3. 通过 location 的 state 对象获取,state 传递的参数不会在地址栏公开
hashHistory.push(/fool,state: { bar: ' baz'} )
this.props.location.state.bar
- 跳转前确认
每个路由都有
Enter
和
Leave
钩子,用户进入或离开该路由时触发
下面是一个常见的应用,在用户离开页面时提示用户确认是否离开
routerWillLeave(),返回 false 取消跳转,否则返回一个字符串,提示用户
componentDidMount() {
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
}
routerWillLeave = (nextLocation) => {
if (!this.state.isSaved)
return '确认要离开?';
}
以上初步罗列了部分知识点,后续会慢慢补充知识点和实例