react+react-router+redux+react-redux构建一个简单应用
完整的demo代码:https://gitee.com/dgx/demo-react 演示:http://dgx.gitee.io/demo-react/build/index.html#/ 一.基本知识我们已经学习了react的语法使用,react-router和react的配合使用,redux通过react-redux的结合使用,下面我们要组合起来,开发一个简单的应用。 二.应用结构index.html(我们的单页开发核心静态页面) index.js(应用渲染首页面) App.js(核心页面) rootRedux.js(合并所有状态,对外接口) indexRedux.js(根状态树) page/(目录,存放路由页面组件) page/login/LoginReactRedux.js(登录页面组件被react-redux封装,我们的首页显示页面,需要用户登录) page/login/Login.js(登录页面组件) page/login/LoginRedux.js(登录页面reducer) 我们不在创建action的页面,不会去分离出去,我们的业务只是demo使用 其他路由页面构建类似...(包含登录页面,主页面,关于我们,新闻中心四个页面作为演示) tpl/(目录,存放公用组件,用于路由页面公用显示使用) 我们的ajax处理都会利用setTimeout去模拟,同样也不使用action可以用函数的中间件,因为应用非常简单 三.创建初始化应用利用我们的create-react-app 项目名 来创建,执行下面创建我们的demo-react应用: create-react-app demo-react 删除一些不要的东西,让我们的项目看起来尽可能简洁: index.html <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"> <meta name="theme-color" content="#000000"> <title>React App</title> </head> <body> <div id="root"></div> </body> </html> index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; ReactDOM.render(<App />,document.getElementById('root')); registerServiceWorker(); App.js import React,{ Component } from 'react'; class App extends Component { render() { return ( <div className="App"> demo </div> ); } } export default App; 四.安装需要的依赖我们的项目需要: react-router: npm install react-router react-router-dom:(我们实际使用的路由模块) npm install react-router-dom redux: npm install redux react-redux: npm install react-redux 等待完成... 我们的使用版本: 我们采用的react16.x和react-router4.x,不同的版本使用是有区别的,尤其路由使用上 五.创建目录结构和文件page下存放我们路由使用的页面组件 六.创建路由页面和搭载路由1.创建页面这时候我们已经可以看到显示demo的页面,我们开始创建我们的页面: login/Login.js import React,{ Component } from 'react'; //=====组件===== class Login extends Component { render() { return ( <div> <h3>登录页面</h3> <div> 用户名<input type="text" /> </div> <div> 密码<input type="text" /> </div> <div> <button onClick={this.goLogin}>登录</button> </div> </div> ); } goLogin(){ alert("开始登录") } componentDidMount() { console.log("Login渲染完毕") } } export default Login home/Home.js import React,{ Component } from 'react'; //=====组件===== class Home extends Component { render() { return ( <div> <h3>主页</h3> </div> ); } componentDidMount() { console.log("Home渲染完毕") } } export default Home about/About.js import React,{ Component } from 'react'; //=====组件===== class About extends Component { render() { return ( <div> <h3>关于我们</h3> </div> ); } componentDidMount() { console.log("About渲染完毕") } } export default About news/News.js import React,{ Component } from 'react'; //=====组件===== class News extends Component { constructor(props) { super(props); // 设置 initial state this.state = { list: [ {id:1,title:"a",con:"caaaaaaaaaaaaaaaa"},{id:2,title:"b",con:"cbbbbbbbbbbb"},{id:3,title:"c",con:"cccccccccccccc"},{id:4,title:"d",con:"cddddddddddddd"},{id:5,title:"e",con:"ceeeeeeeeeeee"} ] }; } render() { return ( <div> <h3>新闻页面</h3> <ul> { this.state.list.map(function(item,i){ return <li key={item.id}> <a>{item.title}</a> <span>{item.con}</span> </li> }) } </ul> </div> ); } componentDidMount() { console.log("News渲染完毕") } } export default News 2.搭载路由我们把页面创建完毕,在index.js配置路由: index.js: import React from 'react'; import ReactDOM from 'react-dom'; import {BrowserRouter as Router} from 'react-router-dom'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; ReactDOM.render( <Router> <App /> </Router>,document.getElementById('root')); registerServiceWorker(); App.js完成我们路由和页面的使用: App.js: import React,{ Component } from 'react'; import { Route,Link } from 'react-router-dom'; import Login from './page/login/Login.js'; import Home from './page/home/Home.js'; import About from './page/about/About.js'; import News from './page/news/News.js'; class App extends Component { render() { return ( <div className="App"> <ul> <li> <Link to="/">登录</Link> </li> <li> <Link to="/Home">主页</Link> </li> <li> <Link to="/About">关于我们</Link> </li> <li> <Link to="/News">新闻页面</Link> </li> </ul> <div> <Route exact path="/" component={Login}/> <Route exact path="/Home" component={Home}/> <Route path="/About" component={About}/> <Route path="/News" component={News}/> </div> </div> ); } } export default App; 我们预览页面,就可以看到大概了: 七.redux和应用配合创建我们的redux文件: rootRedux.js(合并所有状态,对外接口): import { combineReducers } from 'redux'; //全局reducer import isLogin from './indexRedux.js' //子reducer //合并reducer var rootRedux = combineReducers({ isLogin }) export default rootRedux indexRedux.js(根状态树)我们存放登录状态,默认是未登录: //reducer var isLogin=false; function indexRedux(state = isLogin,action) { switch (action.type) { case "GO_LOGIN": //登录 return true case "OUT_LOGIN": //退出登录 return false default: return state } } export default indexRedux index.js使用redux: import React from 'react'; import ReactDOM from 'react-dom'; import {BrowserRouter as Router} from 'react-router-dom'; //redux 和react-redux(关联react和redux) import { createStore } from 'redux'; import { Provider } from 'react-redux'; //reducers 状态树state和逻辑操作 import rootRedux from './rootRedux.js' import App from './App'; import registerServiceWorker from './registerServiceWorker'; //创建状态树和设置 //生成状态树对象 const store = createStore(rootRedux); //start 状态树应用到全局 通过Provider ReactDOM.render( <Provider store={store}> <Router> <App /> </Router> </Provider>,document.getElementById('root')); registerServiceWorker(); 我们为news创建reducer,把list放入在reducer中, NewsRedux.js: //reducer var newsinit={ list:[ {id:1,con:"ceeeeeeeeeeee"} ] }; function NewsRedux(state = newsinit,action) { switch (action.type) { case "SORT_REVERSE": //倒叙显示 var arr=state.list; var arr2=[]; for(var i=arr.length-1;i>=0;i--){ arr2.push(arr[i]) } return Object.assign({},state,{list:arr2}) default: return state } } export default NewsRedux News.js移除构造函数的json设置: import React,{ Component } from 'react'; //=====组件===== class News extends Component { constructor(props) { super(props); } render() { return ( <div> <h3>新闻页面</h3> <ul> </ul> </div> ); } componentDidMount() { console.log("News渲染完毕") } } export default News rootRedux.js引入news的reducer: import { combineReducers } from 'redux'; //全局reducer import isLogin from './indexRedux.js' //子reducer import NewsRedux from './page/news/NewsRedux.js' //合并reducer var rootRedux = combineReducers({ isLogin,NewsRedux }) export default rootRedux 八.利用react-redux链接react组件和redux我们先以news的处理作为操作,首先用react-redux封装News.js组件: 创建NewsReactRedux.js: import { connect } from 'react-redux'; //=====引入组件===== import News from './News.js' //=====react-redux 封装组件===== // 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的? function mapStateToProps(state) { return { list: state.NewsRedux.list }; } // 哪些 action 创建函数是我们想要通过 props 获取的? function mapDispatchToProps(dispatch) { return { SORTREVERSE:function(){ dispatch({type:"SORT_REVERSE"}) } }; } //封装传递state和dispatch var NewsReactRedux = connect(mapStateToProps,mapDispatchToProps)(News); export default NewsReactRedux 我们把redux的状态数据和action全部发射给News组件,我们在里面使用即可: News.js import React,{ Component } from 'react'; //=====组件===== class News extends Component { constructor(props) { super(props); } render() { return ( <div> <h3>新闻页面</h3> <ul> { this.props.list.map(function(item,i){ return <li key={item.id}> <a>{item.title}</a> <span>{item.con}</span> </li> }) } </ul> <button onClick={this.SORTREVERSE.bind(this)}>倒叙显示</button> </div> ); } SORTREVERSE(){ this.props.SORTREVERSE(); } componentDidMount() { console.log("News渲染完毕") } } export default News App.js使用封装后的组件News: import React,Link } from 'react-router-dom'; import Login from './page/login/Login.js'; import Home from './page/home/Home.js'; import About from './page/about/About.js'; import NewsReactRedux from './page/news/NewsReactRedux.js'; class App extends Component { render() { return ( <div className="App"> <ul> <li> <Link to="/">登录</Link> </li> <li> <Link to="/Home">主页</Link> </li> <li> <Link to="/About">关于我们</Link> </li> <li> <Link to="/News">新闻页面</Link> </li> </ul> <div> <Route exact path="/" component={Login}/> <Route exact path="/Home" component={Home}/> <Route path="/About" component={About}/> <Route path="/News" component={NewsReactRedux}/> </div> </div> ); } } export default App; 九.登录处理/地址就是我们的登录页面,我们点击登录跳转就可以了,不过我们会把用户的登录状态存放在indexRedux.js中,我们不把这个状态存如cookie类似的本地,所以 我们刷新页面退出即可,我们只是模拟的处理: 如果存放在了cookie我们要如何处理,这时候在进入网站我们可以调用一个方法去获取cookie的登录状态,不管是什么我们都会执行action把redux的登录状态改为这个! 因为Login.js要和redux结合,我们要修改登录状态,我们还要结合router去手动跳转到主页, LoginReactRedux.js: import { connect } from 'react-redux'; //=====引入组件===== import Login from './Login.js' //=====react-redux 封装组件===== // 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的? function mapStateToProps(state) { return {} } // 哪些 action 创建函数是我们想要通过 props 获取的? function mapDispatchToProps(dispatch) { return { GOLOGIN:function(username,password,history){ console.log("用户名"+username) console.log("密码"+password) setTimeout(function(){ dispatch({type:"GO_LOGIN"}) history.push("/Home") },1000) } }; } //封装传递state和dispatch var LoginReactRedux = connect(mapStateToProps,mapDispatchToProps)(Login); export default LoginReactRedux login.js import React,{ Component } from 'react'; //=====组件===== class Login extends Component { render() { return ( <div> <h3>登录页面</h3> <div> 用户名<input type="text" ref="username" /> </div> <div> 密码<input type="password" ref="password" /> </div> <div> <button onClick={this.goLogin.bind(this)}>登录</button> </div> </div> ); } goLogin(){ this.props.GOLOGIN(this.refs.username.value,this.refs.password.value,this.props.history); } componentDidMount() { console.log("Login渲染完毕") } } export default Login App.js: import React,Link } from 'react-router-dom'; import LoginReactRedux from './page/login/LoginReactRedux.js'; import Home from './page/home/Home.js'; import About from './page/about/About.js'; import NewsReactRedux from './page/news/NewsReactRedux.js'; class App extends Component { render() { return ( <div className="App"> <ul> <li> <Link to="/">登录</Link> </li> <li> <Link to="/Home">主页</Link> </li> <li> <Link to="/About">关于我们</Link> </li> <li> <Link to="/News">新闻页面</Link> </li> </ul> <div> <Route exact path="/" component={LoginReactRedux}/> <Route exact path="/Home" component={Home}/> <Route path="/About" component={About}/> <Route path="/News" component={NewsReactRedux}/> </div> </div> ); } } export default App; 十.退出登录处理我们在/Home加一个按钮就是退出按钮他和我们的登录处理相反: HomeReactRedux.js: import { connect } from 'react-redux'; //=====引入组件===== import Home from './Home.js' //=====react-redux 封装组件===== // 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的? function mapStateToProps(state) { return {} } // 哪些 action 创建函数是我们想要通过 props 获取的? function mapDispatchToProps(dispatch) { return { OUTLOGIN:function(history){ dispatch({type:"OUT_LOGIN"}) history.push("/") } }; } //封装传递state和dispatch var HomeReactRedux = connect(mapStateToProps,mapDispatchToProps)(Home); export default HomeReactRedux Home.js: import React,{ Component } from 'react'; //=====组件===== class Home extends Component { render() { return ( <div> <h3>主页</h3> <div> <button onClick={this.outLogin.bind(this)}>退出登录</button> </div> </div> ); } outLogin(){ this.props.OUTLOGIN(this.props.history); } componentDidMount() { console.log("Home渲染完毕") } } export default Home 十一.权限处理1.显示级别我们的网站在/地址是处在登录页面,这时候我们应该只有登录框,在进入主页之后会看到跳转链接,我们要获取我们的登录状态,还控制一些标签的显示: 我们在App.js存放了我们的导航,我们只需要对这个组件利用react-redux做一次封装,拿到状态,利用style去处理即可: 我们把导航提出到组件,并且react-redux做封装,在App.js使用 Nav.js: import React,Link } from 'react-router-dom'; class Nav extends Component { render() { return ( <ul style={{display:this.props.isLogin?"block":"none"}}> <li style={{display:this.props.isLogin?"none":"block"}}> <Link to="/">登录</Link> </li> <li> <Link to="/Home">主页</Link> </li> <li> <Link to="/About">关于我们</Link> </li> <li> <Link to="/News">新闻页面</Link> </li> </ul> ); } } export default Nav; NavReactRedux.js: import { connect } from 'react-redux'; //=====引入组件===== import Nav from './Nav.js' //=====react-redux 封装组件===== // 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的? function mapStateToProps(state) { return { isLogin:state.isLogin } } // 哪些 action 创建函数是我们想要通过 props 获取的? function mapDispatchToProps(dispatch) { return {}; } //封装传递state和dispatch var NavReactRedux = connect(mapStateToProps,mapDispatchToProps)(Nav); export default NavReactRedux App.js我们使用封装后导航: import React,Link } from 'react-router-dom'; import NavReactRedux from './NavReactRedux.js'; import LoginReactRedux from './page/login/LoginReactRedux.js'; import HomeReactRedux from './page/home/HomeReactRedux.js'; import About from './page/about/About.js'; import NewsReactRedux from './page/news/NewsReactRedux.js'; class App extends Component { render() { return ( <div className="App"> <NavReactRedux /> <div> <Route exact path="/" component={LoginReactRedux}/> <Route exact path="/Home" component={HomeReactRedux}/> <Route exact path="/About" component={About}/> <Route exact path="/News" component={NewsReactRedux}/> </div> </div> ); } } export default App; 我们测试是没有问题的,我们在显示一级的权限做的差不多了! 2.逻辑级别如果用户直接输入地址怎么办?所以我们在路由对应的页面都要加入登录状态的判断,然后处理是留在当前页面还是跳到登录页面: 我们登录后的页面只有三个,我们先对Home做一个处理,其他的类似: HomeReactRedux.js: import { connect } from 'react-redux'; //=====引入组件===== import Home from './Home.js' //=====react-redux 封装组件===== // 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的? function mapStateToProps(state) { return { isLogin:state.isLogin } } // 哪些 action 创建函数是我们想要通过 props 获取的? function mapDispatchToProps(dispatch) { return { OUTLOGIN:function(history){ dispatch({type:"OUT_LOGIN"}) history.push("/") } }; } //封装传递state和dispatch var HomeReactRedux = connect(mapStateToProps,mapDispatchToProps)(Home); export default HomeReactRedux Home.js import React,{ Component } from 'react'; import {Redirect} from 'react-router-dom'; //=====组件===== class Home extends Component { render() { if(this.props.isLogin==false){ return <Redirect to="/" /> } return ( <div> <h3>主页</h3> <div> <button onClick={this.outLogin.bind(this)}>退出登录</button> </div> </div> ); } outLogin(){ this.props.OUTLOGIN(this.props.history); } componentDidMount() { console.log("Home渲染完毕") } } export default Home 其他路由页面同理!!! 十二.刷新问题我们本地存储可以使用cookie还可以使用localstorage,我们刷新应用就获取localstorage对登录状态的设置,然后action即可! 不过不管是cookie还是localstorage如果用户浏览器的安全级别高就完蛋了,我们存放在这个2个里面哪一个都会遇到这个问题。 我们或许可以这样做,在刷新我们就向后台发送一个请求,这个请求会返回用户是否在登录中和返回用户的一些信息,根据状态我们用手动方法跳转链接。 十三.404这个其实使用的就是router为我们提供的 Switch 组件: import React,Link,Switch } from 'react-router-dom'; import NavReactRedux from './NavReactRedux.js'; import LoginReactRedux from './page/login/LoginReactRedux.js'; import HomeReactRedux from './page/home/HomeReactRedux.js'; import About from './page/about/About.js'; import NewsReactRedux from './page/news/NewsReactRedux.js'; import NotFind from './page/notFind/NotFind.js'; class App extends Component { render() { return ( <div className="App"> <NavReactRedux /> <div> <Switch> <Route exact path="/" component={LoginReactRedux}/> <Route exact path="/Home" component={HomeReactRedux}/> <Route exact path="/About" component={About}/> <Route exact path="/News" component={NewsReactRedux}/> <Route component={NotFind}/> </Switch> </div> </div> ); } } export default App; 十四.打包我们执行下面命令: npm run build 打包后文件index.html删除 / 我们打开index.html会出现问题,提示404,我们可以把路由处理改为: HashRouter方式 index.js: import React from 'react'; import ReactDOM from 'react-dom'; import {HashRouter as Router} from 'react-router-dom'; //redux 和react-redux(关联react和redux) import { createStore } from 'redux'; import { Provider } from 'react-redux'; //reducers 状态树state和逻辑操作 import rootRedux from './rootRedux.js' import App from './App.js'; import registerServiceWorker from './registerServiceWorker'; //创建状态树和设置 //生成状态树对象 const store = createStore(rootRedux); //start 状态树应用到全局 通过Provider ReactDOM.render( <Provider store={store}> <Router> <App /> </Router> </Provider>,document.getElementById('root')); registerServiceWorker(); (打包后文件index.html删除 /) 完整的demo代码:https://gitee.com/dgx/demo-react (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |