众所周知,在项目中如果在资源加载请求还未完成的时候,由于阻塞机制,会出现首页白屏的问题,产生很差的用户体验。本文以react为例,提供一个解决方法。
解决原理:使用 onreadystatechange 去监听 readyState,在资源加载完成之前加载一个只有框架的静态页面,页面不请求数据。当数据请求完成之后再将路由切换到真实的首页。
废话不多说,上代码:
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import React from 'react' import ReactDom from 'react-dom' import {Provider} from 'react-redux' import {BrowserRouter as Router, Route} from 'react-router-dom' import configureStore from './store' import Index from './containers/Index.js' import FirstScreen from './containers/FirstScreen.js' export const store = configureStore() function listen () { if (document.readyState == 'complete') { // 资源加载完成 ReactDom.render( <Provider store={store}> <Router> <Route path="/" component={Index}/> </Router> </Provider>, document.getElementById('root') ) } else { // 资源加载中 ReactDom.render( <Provider store={store}> <Router> <Route path="/" component={FirstScreen}/> </Router> </Provider>, document.getElementById('root') ) } } document.onreadystatechange = listen |
其中Index.js就是你的真实首页,FirstScreen.js就是只有框架的静态页。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
Index.js import React, {Component} from 'react' import PropTypes from 'prop-types' import {connect} from 'react-redux' import {store} from '../main' import {bindActionCreators} from 'redux' import {getLocalTime} from '../actions/localTime' import LocalTime from '../components/LocalTime' import '../static/css/Index.css' class Index extends Component { /** * constructor() React组件的构造函数在挂载之前被调用。 * 在实现React.Component子类的构造函数时, * 应该super(props)在任何其他语句之前调用。 * 否则,this.props会在构造函数中定义,这可能会导致错误。 */ constructor (props) { super(props) this.realTime = this.realTime.bind(this) } realTime () { setInterval(() => { store.dispatch(getLocalTime()) }, 1000) } /** * componentWillMount()会在组件render之前立即被调用,并且永远都只执行一次。 * 由于这个方法始终只执行一次,所以如果在这里定义了setState方法之后,页面永远都只会在加载前更新一次。 */ componentWillMount () { } /** * componentDidMount()在组件被装载后立即被调用。 * 在这个时候之后组件已经生成了对应的DOM结构。 * 可以在这个方法中执行setTimeout, setInterval,接口调用等。 */ componentDidMount () { this.realTime() } /** * componentWillReceiveProps()在组件接收到一个新的prop时被执行。 * 这个方法在初始化render时不会被调用。 */ componentWillReceiveProps () { } /** * 返回一个布尔值。在组件接收到新的props或者state时被执行。 * 在初始化时或者使用forceUpdate时不被执行。 * 如果shouldComponentUpdate返回false, * render()则会在下一个state change之前被完全跳过,componentWillUpdate和 componentDidUpdate也不会被执行 */ shouldComponentUpdate (nextProps, nextState) { return true } /** * componentWillUpdate()在组件接收到新的props或者state但还没有render时被执行。 * 在初始化时不会被执行。 */ componentWillUpdate (nextProps, nextState) { } /** * componentDidUpdate()在组件完成更新后立即执行。 * 在初始化时不会被执行。一般会在组件完成更新后被使用。 * 可以用来 clearInterval。 */ componentDidUpdate (prevProps, prevState) { clearInterval(this.realTime()) } /** * render()函数应该是纯粹的,这意味着它不会修改组件状态, * 每次调用时都会返回相同的结果,并且不会直接与浏览器交互 */ render () { const {localTime} = this.props return ( <div className='main'> <LocalTime localTime={localTime} getLocalTime={getLocalTime}></LocalTime> </div> ) } } Index.propTypes = { localTime: PropTypes.string, getLocalTime: PropTypes.func } // 将state绑定到props const mapStateToProps = (state, ownProps) => { const {localTime} = state return { localTime: localTime.localTime } } // 将action绑定到props上 const mapDispatchToProps = (dispatch, ownProps) => { return { getLocalTime: bindActionCreators(getLocalTime, dispatch) } } // 通过react-redux提供的connect方法将我们需要的state中的数据和actions中的方法绑定到props上 export default connect(mapStateToProps, mapDispatchToProps)(Index) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
FirstScreen.js import React, {Component} from 'react' import {connect} from 'react-redux' import '../static/css/FirstScreen.css' class FirstScreen extends Component { constructor (props) { super(props) } render () { return ( <div className='firstScreen'> 我是首屏空白页 </div> ) } } export default connect()(FirstScreen) |
示例代码托管在GitHub上:https://github.com/skillnull/TheCurrentTime