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; } }