3.react-router 和react-router-dom的区别 https://blog.csdn.net/sinat_17775997/article/details/69218382javascript
6.redux-thunkcss
redux-thunk实现了相关异步流程内聚到redux的流程中,实现middleware的功能,也便于项目的开发与维护,避免冗余代码。而实现的方式即是改写redux中的dispatch API,使其能够除PlainObject外,接受一个函数做为参数。html
//action.js const custom = (data) => { type: 'SET_CUSTOM_DATA', data: data } dispatch(custom({})) //redux-thunk const custom = (data) => { return async (dispatch, getState) => { //异步操做 dispatch({ type: 'SET_CUSTOM_DATA', data: data }) } }
dispatch(custom({}))
7. react-redux中<Provider>组件的做用,以及如何实现异步加载reducervue
import { Provider } from 'react-redux' import { createStore } from 'redux' import todoApp from './reducers' import App from './components/App' let store = createStore(todoApp); render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ) //App的全部子组件就默认均可以拿到state //它的原理是React组件的context属性 class Provider extends Component { //store放在了上下文对象context上面。而后,子组件就能够从context拿到store getChildContext() { return { store: this.props.store }; } render() { return this.props.children; } } Provider.childContextTypes = { store: React.PropTypes.object } //子组件调用 class VisibleTodoList extends Component { componentDidMount() { const { store } = this.context; this.unsubscribe = store.subscribe(() => this.forceUpdate() ); } render() { const props = this.props; const { store } = this.context; const state = store.getState(); // ... } } VisibleTodoList.contextTypes = { store: React.PropTypes.object }
//react组件的context属性应用,将store嵌入,经过this.context.store获取
//injectReducer在项目中异步加载reducers,具体的reducer能够在生命周期函数中加载进去
8. react-reduxjava
export default connect(function (state, props){ /*return { ...state, name: [state.name, props.name] };*/ // state, props若是同名,能够经过这个函数决定是获取哪一个 return state.user; }, { setName(name){ return { type: SET_NAME, name }; }, addAge(n){ return { type: ADD_AGE, n } } })(App);
9. 高阶组件node
//withHoc.js import React, { Component } from 'react'; export default (params) => (WrappedComponent) => { return class From extends Component { //方便使用react-devtool调试时显示不一样组件名 static displayName = `From(${WrappedComponent.name || WrappedComponent.displayName})` render() { return ( <div className="withHoc"> <div>{params}</div> <WrappedComponent {...this.props} {...this.state}></WrappedComponent> </div> ); } } } //a.js import React, { Component } from 'react'; import withHoc from './withHoc.js'; class A extends Component { render() { return ( <div className="A"> <div>A component</div> </div> ); } } export default withHoc('a')(A);
10. withRouterreact
目的就是让被修饰的组件能够从属性中获取history
,location
,match
,
路由组件能够直接获取这些属性,而非路由组件就必须经过withRouter
修饰后才能获取这些属性了,
好比 <Route path='/' component={App}/>
webpack
App
组件就能够直接获取路由中这些属性了,可是,若是App
组件中若是有一个子组件Foo
,那么Foo
就不能直接获取路由中的属性了,必须经过withRouter
修饰后才能获取到。git
//用于js实现路由跳转 this.props.history.push('/chat)
11. Redux DevTools 扩展的使用说明github
if (process.env.NODE_ENV === 'development') { const devToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION__ if (typeof devToolsExtension === 'function') { enhancers.push(devToolsExtension()) } } const composedEnhancers = compose( applyMiddleware(...middlewares), ...enhancers ) //或者 const enhancers = [] let composeEnhancers = compose // 在development模式,使用redux-devtools-extension if (process.env.NODE_ENV === 'development') { if (typeof window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ === 'function') { composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ } } const store = createReduxStore( makeRootReducer(), initialState, composeEnhancers( applyMiddleware(...middleware), ...enhancers ) )
12. 经常使用npm包
connected-react-router core-js
reselect // computed
redux-undo // 撤销,重作
react-loadable //懒加载
13. px2rem
// config-overrides.js const px2rem=require('postcss-px2rem-exclude') module.exports = { webpack: override( // 用js的方式导入antd及其样式:style为true表示导入antd.less; 为false表示不使用js的方式导入antd.less或antd.css;为'css'表示使用antd.css; fixBabelImports("import", { libraryName: "antd-mobile", libraryDirectory: "es", style: true // 为false或css会致使addLessLoader失效 }), addLessLoader({ javascriptEnabled: true, // modifyVars: { "@primary-color": "#D24545" } // 深红色 }), addPostcssPlugins([ px2rem({ remUnit: 75, // 仅排除对antd-mobile的px2rem转化 exclude: /node_modules\/antd-mobile/i }) ]), disableEsLint() // 取消eslint检查,加快yarn start速度 ), devServer: overrideDevServer( // dev server plugin watchAll() ) }
// 某一项不想转为rem
border: 1px solid #ccc; /*no*/
vue-cli3用的是postcss-plugin-px2rem; 实现原理同样。重要!! 若是个别地方不想转化px。能够简单的使用大写的 PX 或 Px
14. withRouter致使组件重复渲染
React Router 4 把Route看成普通的React组件,能够在任意组件内使用Route;
withRouter 为非路由组件提供了location,history,match三个参数;可是有时会发现有些接口会重复调用,这个是因为组件从新渲染的缘由
通过Redux connect后的Home
组件,在更新阶段,会使用浅比较,可是因为Route组件致使这个失效
componentWillReceiveProps(nextProps, nextContext) { warning( !(nextProps.location && !this.props.location), '<Route> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.' ) warning( !(!nextProps.location && this.props.location), '<Route> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.' ) // 注意这里,computeMatch每次返回的都是一个新对象,如此一来,每次Route更新,setState都会从新设置一个新的match对象 this.setState({ match: this.computeMatch(nextProps, nextContext.router) }) } render() { const { match } = this.state const { children, component, render } = this.props const { history, route, staticContext } = this.context.router const location = this.props.location || route.location // 注意这里,这是传递给Route中的组件的属性 const props = { match, location, history, staticContext } if (component) return match ? React.createElement(component, props) : null if (render) return match ? render(props) : null if (typeof children === 'function') return children(props) if (children && !isEmptyChildren(children)) return React.Children.only(children) return null }
这样,每次Route
更新(componentWillReceiveProps被调用),都将建立一个新的match;
致使Redux的浅比较失败,进而触发组件的从新渲染
解决方法:1⃣️. connected-react-router 2⃣️. 直接引用history.js文件
15.input 在IE11,不触发onchange
<input type='text' value={this.state.value} onCompositionStart={this.handleComposition} onCompositionUpdate={this.handleComposition} onCompositionEnd={this.handleComposition} onChange={this.handleChange} /> //前2个事件都在onChange以前触发,onCompositionEnd是在onChange以后触发。 //若是直接输入完成是不会触发这三个事件的,只有onChange事件。好比直接输入英文 // ie11下中文输入法会不触发onChange,因此也须要setState,不然此时会发现中文输入进去后输入框没有变换 /** * 中文输入法,选词 */ handleComposition = (e) => { this.isOnComposition = e.type !== 'compositionend' if (!this.isOnComposition) { //ie11不触发onchange致使中文不展现 this.setState({ value: e.target.value }) this.handleInputAndSearch(e.target.value) }
16. 若是map的组件为受控组件,则使用索引并不会产生问题,可是若是为非受控组件,例如input等,则会因为复用标签元素致使value并未更改
17. setState机制(https://github.com/sisterAn/blog/issues/26)
批处理的缘由,举例来讲,若是咱们在浏览器中click
处理,都Child
和Parent
调用setState
,咱们不想从新渲染Child
两次
补充,这里输出 0,0,3,4
componentDidMount() { this.setState((prevState, props) => ({ val: prevState.val + 1 })) console.log(this.state.val) this.setState((prevState, props) => ({ val: prevState.val + 1 })) console.log(this.state.val) setTimeout(() => { this.setState({val: this.state.val + 1}); console.log(this.state.val); // 第 3 次 log this.setState({val: this.state.val + 1}); console.log(this.state.val); // 第 4 次 log }, 0); }
// 0,1 "logbefore", 2 "log",3,4 componentDidMount() { this.setState((prevState, props) => ({ val: prevState.val + 1 })) console.log(this.state.val) Promise.resolve().then(() => { console.log(this.state.val, 'logbefore'); this.setState({val: this.state.val + 1}); console.log(this.state.val, 'log'); }) setTimeout(() => { this.setState({val: this.state.val + 1}); console.log(this.state.val); // 第 3 次 log this.setState({val: this.state.val + 1}); console.log(this.state.val); // 第 4 次 log }, 0); }
源码路径(v16.9.0)
/react/packages/react-test-renderer/src/ReactShallowRenderer.js
class Updater { constructor(renderer) { this._renderer = renderer; this._callbacks = []; } //... enqueueSetState(publicInstance, partialState, callback, callerName) { this._enqueueCallback(callback, publicInstance); const currentState = this._renderer._newState || publicInstance.state; if (typeof partialState === 'function') { partialState = partialState.call( publicInstance, currentState, //这里确保每次的state都是当时最新的 publicInstance.props, ); } // Null and undefined are treated as no-ops. if (partialState === null || partialState === undefined) { return; } this._renderer._newState = { ...currentState, ...partialState, }; this._renderer.render(this._renderer._element, this._renderer._context); } }
/react/src/renderers/shared/stack/reconciler/ReactUpdates.js
function enqueueUpdate(component) { ensureInjected(); // Various parts of our code (such as ReactCompositeComponent's // _renderValidatedComponent) assume that calls to render aren't nested; // verify that that's the case. (This is called by each top-level update // function, like setState, forceUpdate, etc.; creation and // destruction of top-level components is guarded in ReactMount.) if (!batchingStrategy.isBatchingUpdates) {// 仍是根据isBatchingUpdates batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } dirtyComponents.push(component); if (component._updateBatchNumber == null) { component._updateBatchNumber = updateBatchNumber + 1; } }