redux 中间件
中间件提供第三方插件的模式,自定义拦挡
action
->reducer
的过程。变为action
->middlewares
->reducer
。这种机制能够让咱们扭转数据流,实现如异步action
,action
过滤,日志输入,异样报告等性能
redux-logger
:提供日志输入redux-thunk
:解决异步操作redux-promise
:解决异步操作,actionCreator
的返回值是promise
如何配置 React-Router 实现路由切换
(1)应用<Route>
组件
路由匹配是通过比拟 <Route>
的 path 属性和以后地址的 pathname 来实现的。当一个 <Route>
匹配胜利时,它将渲染其内容,当它不匹配时就会渲染 null。没有门路的 <Route>
将始终被匹配。
// when location = {pathname: '/about'}
<Route path='/about' component={About}/> // renders <About/>
<Route path='/contact' component={Contact}/> // renders null
<Route component={Always}/> // renders <Always/>
(2)联合应用 <Switch>
组件和 <Route>
组件
<Switch>
用于将 <Route>
分组。
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
<Switch>
不是分组 <Route>
所必须的,但他通常很有用。一个 <Switch>
会遍历其所有的子 <Route>
元素,并仅渲染与以后地址匹配的第一个元素。
(3)应用 <Link>、<NavLink>、<Redirect>
组件
<Link>
组件来在你的应用程序中创立链接。无论你在何处渲染一个<Link>
,都会在应用程序的 HTML 中渲染锚(<a>
)。
<Link to="/">Home</Link>
// <a href='/'>Home</a>
是一种非凡类型的 当它的 to 属性与以后地址匹配时,能够将其定义为 ” 沉闷的 ”。
// location = {pathname: '/react'}
<NavLink to="/react" activeClassName="hurray">
React
</NavLink>
// <a href='/react' className='hurray'>React</a>
当咱们想强制导航时,能够渲染一个 <Redirect>
,当一个<Redirect>
渲染时,它将应用它的 to 属性进行定向。
React-Router 如何获取 URL 的参数和历史对象?
(1)获取 URL 的参数
- get 传值
路由配置还是一般的配置,如:'admin'
,传参形式如:'admin?id='1111''
。通过 this.props.location.search
获取 url 获取到一个字符串'?id='1111'
能够用 url,qs,querystring,浏览器提供的 api URLSearchParams 对象或者本人封装的办法去解析出 id 的值。
- 动静路由传值
路由须要配置成动静路由:如 path='/admin/:id'
,传参形式,如'admin/111'
。通过this.props.match.params.id
获得 url 中的动静路由 id 局部的值,除此之外还能够通过useParams(Hooks)
来获取
- 通过 query 或 state 传值
传参形式如:在 Link 组件的 to 属性中能够传递对象 {pathname:'/admin',query:'111',state:'111'};
。通过this.props.location.state
或this.props.location.query
来获取即可,传递的参数能够是对象、数组等,然而存在毛病就是只有刷新页面,参数就会失落。
(2)获取历史对象
- 如果 React >= 16.8 时能够应用 React Router 中提供的 Hooks
import {useHistory} from "react-router-dom";
let history = useHistory();
2. 应用 this.props.history 获取历史对象
let history = this.props.history;
React 设计思路,它的理念是什么?
(1)编写简略直观的代码
React 最大的价值不是高性能的虚构 DOM、封装的事件机制、服务器端渲染,而是申明式的直观的编码方式。react 文档第一条就是申明式,React 使创立交互式 UI 变得轻而易举。为利用的每一个状态设计简洁的视图,当数据扭转时 React 能无效地更新并正确地渲染组件。以申明式编写 UI,能够让代码更加牢靠,且不便调试。
(2)简化可复用的组件
React 框架外面应用了简化的组件模型,但更彻底地应用了组件化的概念。React 将整个 UI 上的每一个功能模块定义成组件,而后将小的组件通过组合或者嵌套的形式形成更大的组件。React 的组件具备如下的个性∶
- 可组合:简略组件能够组合为简单的组件
- 可重用:每个组件都是独立的,能够被多个组件应用
- 可保护:和组件相干的逻辑和 UI 都封装在了组件的外部,不便保护
- 可测试:因为组件的独立性,测试组件就变得不便很多。
(3) Virtual DOM
实在页面对应一个 DOM 树。在传统页面的开发模式中,每次须要更新页面时,都要手动操作 DOM 来进行更新。DOM 操作十分低廉。在前端开发中,性能耗费最大的就是 DOM 操作,而且这部分代码会让整体我的项目的代码变得难 以保护。React 把实在 DOM 树转换成 JavaScript 对象树,也就是 Virtual DOM,每次数据更新后,从新计算 Virtual DOM,并和上一次生成的 Virtual DOM 做比照,对发生变化的局部做批量更新。React 也提供了直观的 shouldComponentUpdate 生命周期回调,来缩小数据变动后不必要的 Virtual DOM 比照过程,以保障性能。
(4)函数式编程
React 把过来一直反复构建 UI 的过程形象成了组件,且在给定参数的状况下约定渲染对应的 UI 界面。React 能充分利用很多函数式办法去缩小冗余代码。此外,因为它自身就是简略函数,所以易于测试。
(5)一次学习,随处编写
无论当初正在应用什么技术栈,都能够随时引入 React 来开发新个性,而不须要重写现有代码。
React 还能够应用 Node 进行服务器渲染,或应用 React Native 开发原生挪动利用。因为 React 组件能够映射为对应的原生控件。在输入的时候,是输入 Web DOM,还是 Android 控件,还是 iOS 控件,就由平台自身决定了。所以,react 很不便和其余平台集成
React 组件命名举荐的形式是哪个?
通过援用而不是应用来命名组件 displayName。
应用 displayName 命名组件:
export default React.createClass({displayName: 'TodoApp', // ...})
React 举荐的办法:
export default class TodoApp extends React.Component {// ...}
在 React 中,何为 state
State 和 props 相似,但它是公有的,并且齐全由组件本身管制。State 实质上是一个持有数据,并决定组件如何渲染的对象。
props 是什么
- react 的核心思想是组件化,页面被分成很多个独立,可复用的组件
- 而组件就是一个函数,能够承受一个参数作为输出值,这个参数就是 props,所以 props 就是从内部传入组件外部的数据
- 因为 react 的单向数据流模式,所以 props 是从父组件传入子组件的数据
Redux 中异步的申请怎么解决
能够在 componentDidmount 中间接进⾏申请⽆须借助 redux。然而在⼀定规模的项⽬中, 上述⽅法很难进⾏异步流的治理, 通常状况下咱们会借助 redux 的异步中间件进⾏异步解决。redux 异步流中间件其实有很多,当下支流的异步中间件有两种 redux-thunk、redux-saga。
(1)应用 react-thunk 中间件
redux-thunk长处:
- 体积⼩: redux-thunk 的实现⽅式很简略, 只有不到 20 ⾏代码
- 使⽤简略: redux-thunk 没有引⼊像 redux-saga 或者 redux-observable 额定的范式, 上⼿简略
redux-thunk缺点:
- 样板代码过多: 与 redux 自身⼀样, 通常⼀个申请须要⼤量的代码, ⽽且很多都是反复性质的
- 耦合重大: 异步操作与 redux 的 action 偶合在⼀起, 不⽅便治理
- 性能孱弱: 有⼀些理论开发中常⽤的性能须要⾃⼰进⾏封装
应用步骤:
- 配置中间件,在 store 的创立中配置
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk'
// 设置调试工具
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
// 设置中间件
const enhancer = composeEnhancers(applyMiddleware(thunk)
);
const store = createStore(reducer, enhancer);
export default store;
- 增加一个返回函数的 actionCreator,将异步申请逻辑放在外面
/** 发送 get 申请,并生成相应 action,更新 store 的函数 @param url {string} 申请地址 @param func {function} 真正须要生成的 action 对应的 actionCreator @return {function} */
// dispatch 为主动接管的 store.dispatch 函数
export const getHttpAction = (url, func) => (dispatch) => {axios.get(url).then(function(res){const action = func(res.data)
dispatch(action)
})
}
- 生成 action,并发送 action
componentDidMount(){var action = getHttpAction('/getData', getInitTodoItemAction)
// 发送函数类型的 action 时,该 action 的函数领会主动执行
store.dispatch(action)
}
(2)应用 redux-saga 中间件
redux-saga长处:
- 异步解耦: 异步操作被被转移到独自 saga.js 中,不再是掺杂在 action.js 或 component.js 中
- action 解脱 thunk function: dispatch 的参数仍然是⼀个纯正的 action (FSA),⽽不是充斥“⿊魔法”thunk function
- 异样解决: 受害于 generator function 的 saga 实现,代码异样 / 申请失败 都能够间接通过 try/catch 语法间接捕捉解决
- 性能强⼤: redux-saga 提供了⼤量的 Saga 辅助函数和 Effect 创立器供开发者使⽤, 开发者⽆须封装或者简略封装即可使⽤
- 灵便: redux-saga 能够将多个 Saga 能够串⾏ / 并⾏组合起来, 造成⼀个⾮常实⽤的异步 flow
- 易测试,提供了各种 case 的测试⽅案,包含 mock task,分⽀笼罩等等
redux-saga缺点:
- 额定的学习老本: redux-saga 不仅在使⽤难以了解的 generator function, ⽽且无数⼗个 API, 学习老本远超 redux-thunk, 最重要的是你的额定学习老本是只服务于这个库的, 与 redux-observable 不同,redux-observable 尽管也有额定学习老本然而背地是 rxjs 和⼀整套思维
- 体积庞⼤: 体积略⼤, 代码近 2000 ⾏,min 版 25KB 左右
- 性能过剩: 实际上并发管制等性能很难⽤到, 然而咱们仍然须要引⼊这些代码
- ts ⽀持不敌对: yield ⽆法返回 TS 类型
redux-saga 能够捕捉 action,而后执行一个函数,那么能够把异步代码放在这个函数中,应用步骤如下:
- 配置中间件
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import createSagaMiddleware from 'redux-saga'
import TodoListSaga from './sagas'
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const sagaMiddleware = createSagaMiddleware()
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware)
);
const store = createStore(reducer, enhancer);
sagaMiddleware.run(TodoListSaga)
export default store;
- 将异步申请放在 sagas.js 中
import {takeEvery, put} from 'redux-saga/effects'
import {initTodoList} from './actionCreator'
import {GET_INIT_ITEM} from './actionTypes'
import axios from 'axios'
function* func(){
try{
// 能够获取异步返回数据
const res = yield axios.get('/getData')
const action = initTodoList(res.data)
// 将 action 发送到 reducer
yield put(action)
}catch(e){console.log('网络申请失败')
}
}
function* mySaga(){
// 主动捕捉 GET_INIT_ITEM 类型的 action,并执行 func
yield takeEvery(GET_INIT_ITEM, func)
}
export default mySaga
- 发送 action
componentDidMount(){const action = getInitTodoItemAction()
store.dispatch(action)
}
什么是 React Fiber?
Fiber 是 React 16 中新的协调引擎或从新实现外围算法。它的次要指标是反对虚构 DOM 的增量渲染。React Fiber 的指标是进步其在动画、布局、手势、暂停、停止或重用等方面的适用性,并为不同类型的更新调配优先级,以及新的并发原语。
React Fiber 的指标是加强其在动画、布局和手势等畛域的适用性。它的次要个性是增量渲染: 可能将渲染工作宰割成块,并将其扩散到多个帧中。
参考前端 react 面试题具体解答
diff 虚构 DOM 比拟的规定
-
【旧虚构 DOM】与【新虚构 DOM】中雷同 key
若虚构 DOM 中的内容没有产生扭转,间接应用旧的虚构 DOM
若虚构 DOM 中的内容产生扭转了,则生成新实在的 DOM,随后替换页面中之前的实在 DOM
-
【旧虚构 DOM】中未找到 与【新虚构 DOM】雷同的 key
依据数据创立实在 DOM,随后渲染到页面
什么是 state
- 在组件初始化的时候 通过 this.state 给组件设置一个初始化的 state,第一次 render 的时候会用 state 来渲染组件
- 通过 this.setState 办法来更新 state
React-Router 4 怎么在路由变动时从新渲染同一个组件?
当路由变动时,即组件的 props 产生了变动,会调用 componentWillReceiveProps 等生命周期钩子。那须要做的只是:当路由扭转时,依据路由,也去申请数据:
class NewsList extends Component {componentDidMount () {this.fetchData(this.props.location);
}
fetchData(location) {const type = location.pathname.replace('/', '') ||'top'
this.props.dispatch(fetchListData(type))
}
componentWillReceiveProps(nextProps) {if (nextProps.location.pathname != this.props.location.pathname) {this.fetchData(nextProps.location);
}
}
render () {...}
}
利用生命周期 componentWillReceiveProps,进行从新 render 的预处理操作。
React 16 中新生命周期有哪些
对于 React16 开始利用的新生命周期:能够看出,React16 自上而下地对生命周期做了另一种维度的解读:
- Render 阶段:用于计算一些必要的状态信息。这个阶段可能会被 React 暂停,这一点和 React16 引入的 Fiber 架构(咱们前面会重点解说)是无关的;
- Pre-commit 阶段:所谓“commit”,这里指的是“更新真正的 DOM 节点”这个动作。所谓 Pre-commit,就是说我在这个阶段其实还并没有去更新实在的 DOM,不过 DOM 信息曾经是能够读取的了;
- Commit 阶段:在这一步,React 会实现实在 DOM 的更新工作。Commit 阶段,咱们能够拿到实在 DOM(包含 refs)。
与此同时,新的生命周期在流程方面,依然遵循“挂载”、“更新”、“卸载”这三个狭义的划分形式。它们别离对应到:
-
挂载过程:
- constructor
- getDerivedStateFromProps
- render
- componentDidMount
-
更新过程:
- getDerivedStateFromProps
- shouldComponentUpdate
- render
- getSnapshotBeforeUpdate
- componentDidUpdate
-
卸载过程:
- componentWillUnmount
React-Router 怎么设置重定向?
应用 <Redirect>
组件实现路由的重定向:
<Switch>
<Redirect from='/users/:id' to='/users/profile/:id'/>
<Route path='/users/profile/:id' component={Profile}/>
</Switch>
当申请 /users/:id
被重定向去 '/users/profile/:id'
:
- 属性
from: string
:须要匹配的将要被重定向门路。 - 属性
to: string
:重定向的 URL 字符串 - 属性
to: object
:重定向的 location 对象 - 属性
push: bool
:若为真,重定向操作将会把新地址退出到拜访历史记录外面,并且无奈回退到后面的页面。
react 中 key 的作用
简略的说:key 是虚构 DOM 中的一种标识,在更新显示是 key 起到了极其重要的作用
简单的说:当状态中的数据产生扭转的时候,react 会依据【新数据】生成【新的虚构 DOM】,随后 react 进行【新虚构 DOM】和【旧的虚构 DOM】的 diff 比拟,而在这个比拟过程中 key 就是起到是要害中用
如何将两个或多个组件嵌入到一个组件中?
能够通过以下形式将组件嵌入到一个组件中:
class MyComponent extends React.Component{render(){
return(
<div>
<h1>Hello</h1>
<Header/>
</div>
);
}
}
class Header extends React.Component{render(){
return
<h1>Header Component</h1>
};
}
ReactDOM.render(<MyComponent/>, document.getElementById('content')
);
如何有条件地向 React 组件增加属性?
对于某些属性,React 十分聪慧,如果传递给它的值是虚值,能够省略该属性。例如:
var InputComponent = React.createClass({render: function () {
var required = true;
var disabled = false;
return <input type="text" disabled={disabled} required={required} />;
},
});
渲染后果:
<input type="text" required>
另一种可能的办法是:
var condition = true;
var component = <div value="foo" {...(condition && { disabled: true})} />;
diff 算法如何比拟?
- 只对同级比拟,跨层级的 dom 不会进行复用
- 不同类型节点生成的 dom 树不同,此时会间接销毁老节点及子孙节点,并新建节点
- 能够通过 key 来对元素 diff 的过程提供复用的线索
- 单节点 diff
- 单点 diff 有如下几种状况:
- key 和 type 雷同示意能够复用节点
- key 不同间接标记删除节点,而后新建节点
- key 雷同 type 不同,标记删除该节点和兄弟节点,而后新创建节点
在调用 setState 之后产生了什么
-
状态合并,触发和谐:
setState 函数之后,会将传入的参数对象与以后的状态合并,而后登程调用过程
-
依据新的状态构建虚构 dom 树
通过和谐过程,react 会高效的依据新的状态构建虚构 DOM 树,筹备渲染整个 UI 页面
-
计算新老树节点差别,最小化渲染
得倒新的虚构 DOM 树后,会计算出新老树的节点差别,会依据差别对界面进行最小化渲染
-
按需更新
在差别话计算中,react 能够绝对精确的晓得哪些地位产生了扭转以及该如何扭转,这保障按需更新,而不是发表从新渲染
Redux 中的 connect 有什么作用
connect 负责连贯 React 和 Redux
(1)获取 state
connect 通过 context 获取 Provider 中的 store,通过 store.getState()
获取整个 store tree 上所有 state
(2)包装原组件
将 state 和 action 通过 props 的形式传入到原组件外部 wrapWithConnect 返回—个 ReactComponent 对 象 Connect,Connect 重 新 render 内部传入的原组件 WrappedComponent,并把 connect 中传入的 mapStateToProps,mapDispatchToProps 与组件上原有的 props 合并后,通过属性的形式传给 WrappedComponent
(3)监听 store tree 变动
connect 缓存了 store tree 中 state 的状态,通过以后 state 状态 和变更前 state 状态进行比拟,从而确定是否调用 this.setState()
办法触发 Connect 及其子组件的从新渲染