之前一直是用vue进行的开发, 因而决定年后弄一弄react, 因此年后这段时间也就一直瞎弄react, 可算是看到成果了css
原本是想写一个 相似 Vue仿今日头条 那样的项目来入手, 后来又寻思还不如写个后台管理呢。 因而乎便开始捣鼓起来了。html
react
react-dom
react-router-dom
: react-router4之后 好像都是用这个东西了react-transition-group
: 用来作动画的redux
: 用来管理全局状态react-redux
: 用来管理全局状态redux-actions
: 用来建立action的,并且生成相关reducers的时候也不要写 switch/case 或 if/else 了,主要是方便。redux-thunk
: redux
的中间件, 用来处理咱们异步actionantd
: 随便找的一个比较经常使用的react-UI库跟react相关的主要就是这个几个了 至于webpack 配置,基本跟之前配置vue的基本没多大区别。vue
build
: 用来放置关于webpack的配置config
: 项目配置src
: 源码static
: 静态资源.babelrc
: babel配置postcss.config.js
: css配置src
下的目录结构actions
: 放redux中action相关的地方reducers
: 放redux中reducer相关的地方assets
: 项目静态资源components
: 经常使用的公共组件router
: 路由相关的配置store
: redux的配置styles
: 公共样式文件utils
: 工具类的封装view
: 全部页面的主体结构main.js
: 项目入口文件config.js
: 公共属性配置import React from 'react' const MyComponent = React.createClass({ render () { return ( <h2>我是React.createClass生成的组件</h2> ) } }) 复制代码
import React from 'react' class MyComponent from React.Component { render () { return ( <h2>我是React.Component生成的组件</h2> ) } } 复制代码
import React from 'react' const MyComponent = (props) => ( <h2>我是无状态函数式组件</h2> ) ReactDOM.render(<MyComponent name="Sebastian" />, mountNode) 复制代码
路由拦截这块费了挺长时间,原本是想找个相似vue的beforeRouter这个种钩子函数,发现没有。react
而后后面找到history
模块,发现有个这东西有个监听路由的方法,最开始就用这它,可是我忽然切成hash模式进行开发的时候,发现经过history.push(path, [state])
设置state属性的时候出了问题,这东西好像只能给history模式设置state属性,可是我有部分东西是经过设置state属性来进来的,因而便放弃了这个方法寻找新的方法。webpack
后面发现能够经过监听根路径的 componentWillReceiveProps
钩子函数 即可以达到监听的效果。ios
这钩子函数只要props改变便会触发,由于每次切换路由 location
的pathname
老是不一样的,全部只要切换路径便会触发这个这个钩子函数。这东西容易触发死循环,因此记得作好判断。git
class MainComponents extends React.Component { componentWillMount () { // 第一次进来触发 this.dataInit(this.props) } componentWillReceiveProps(nextProps){ // 之后每次变化props都会触发 // 若是死循环了 多是某个属性设置会更新props上属性,因此致使一直循环,这个时候记得作好判断 this.dataInit(nextProps) } render () { // 404 if (!isExistPath(allRoutes, pathname)) return <Redirect to='/error/404'/> //当前路径路由信息 let currRoute = getRoute(allRoutes, pathname) // 非白名单验证 if (!whiteList.some(path => path === pathname)) { // 登陆验证 if (!Cookie.get('Auth_Token')) { return <Redirect to={{ pathname: '/login' }} /> } // 获取用户信息 if (!user) { this.getUserInfo(() => { this.setRoutesByRole(this.props.user.roles) }) } } // 401 if (user && currRoute) { if (!isAuth(currRoute.role, user)) return <Redirect to='/error/401'/> } // 网页title document.title = currRoute.name } } 复制代码
用过vue的都知道咱们通常都是经过new Router({routes})
来集中管理路由表。可是react-router好像不能这么设置。最新的版本好像连嵌套都不行。 因而乎本身便着手简单的搭建了一个集中设置的版本 。不事后面我看到个插件好像是能够管理的 react-router-config,不过我也还没试过,也不知道可不可行。github
// 路由表 const allRoutes = [ { path: '/auth', login: true, layout: true, icon: 'user', name: '权限管理', role: ['admin'], component: _import_views('Auth') }, { path: '/error', login: true, layout: true, icon: 'user', name: 'ErrorPage', redirect: '/error/404', children: [ { path: '/error/404', component: _import_views('Error/NotFound'), name: '404'}, { path: '/error/401', component: _import_views('Error/NotAuth'), name: '401'} ] } ... ] // 根目录 <BrowserRouter> <Route path="/" component={MainComponents}/> </BrowserRouter> // MainComponents class MainComponents extends React.Component { render () { return ( <Switch> {renderRouteComponent(allRoutes.filter(route => !route.layout))} //不须要侧边栏等公共部分的路由页面 <Route path="/" component={ComponentByLayout}/> </Switch> ) } } // ComponentByLayout const ComponentByLayout = ({history}) => ( <Layout history={history}> <Switch> {renderRouteComponent(allRoutes.filter(route => route.layout))} </Switch> </Layout> ) // 路由渲染 const RouteComponent = route => <Route key={route.path} exact={route.exact || false} path={route.path} component={route.component} /> const renderRouteComponent = routes => routes.map((route, index) => { return route.children ? route.children.map(route => RouteComponent(route)) : RouteComponent(route) }) 复制代码
我想根据用户不一样的权限生成不一样的侧边栏。web
{ path: '/auth', login: true, layout: true, icon: 'user', name: '权限管理', role: ['admin'], component: _import_views('Auth') } 复制代码
根据这个路由role信息 跟用户的role信息匹配进行显示跟隐藏npm
这样来筛选出符合这个用户的路由表以及侧边栏(侧边栏根据路由表生成)
可是有个问题,由于咱们是须要登陆才能得知用户的权限信息,因此咱们得那个时候才能肯定路由是哪些。
可是那个时候路由已经设置完毕了。vue
里面的提供了 router.addRoutes
这个方法来供咱们动态设置路由,react
里面我也没找到关于这个api的,因而我便采起全部的路由都注册一遍,可是这样便产生一个问题。
以 /auth
为例,我自己是没有访问/auth
的权限,因此我侧边栏不会生成 /auth
这个列表选项。可是咱们在地址栏里面 访问 /auth
是能进入这个页面的的 (最好的办法就是压根就不生成这个路由)。因此这个设置实际上是有问题,目前我也没知道怎么动态生成路由的办法,暂时也只是在根目录
作了权限处理
按需加载的方法也很多,目前只尝试了第一种,由于我写Vue也是用import实现按需加载的,因此也就没去折腾了。
//asyncComponent.js import React from 'react' export default loadComponent => ( class AsyncComponent extends React.Component { state = { Component: null, } async componentDidMount() { if (this.state.Component !== null) return try { const {default: Component} = await loadComponent() this.setState({ Component }) }catch (err) { console.error('Cannot load component in <AsyncComponent />'); throw err } } render() { const { Component } = this.state return (Component) ? <Component {...this.props} /> : null } } ) // index.js import asyncComponent from './asyncComponent.js' const _import_ = file => asyncComponent(() => import(file)) _import_('components/Home/index.js') 复制代码
原理很简单:
我这里用到的是axios
, 用axios
作了个简单的拦截器
import axios from 'axios' import qs from 'qs' axios.defaults.withCredentials = true // 发送时 axios.interceptors.request.use(config => { // 发起请求,能够进行动画啥的 return config }, err => { return Promise.reject(err) }) // 响应时 axios.interceptors.response.use(response => response, err => Promise.resolve(err.response)) // 检查状态码 function checkStatus(res) { // 获得返回结果,结束动画啥的 if (res.status === 200 || res.status === 304) { return res.data } return { code: 0, msg: res.data.msg || res.statusText, data: res.statusText } return res } // 检查CODE值 function checkCode(res) { if (res.code === 0) { throw new Error(res.msg) } return res } export default { get(url, params) { if (!url) return return axios({ method: 'get', url: url, params, timeout: 30000 }).then(checkStatus).then(checkCode) }, post(url, data) { if (!url) return return axios({ method: 'post', url: url, data: qs.stringify(data), timeout: 30000 }).then(checkStatus).then(checkCode) } } 复制代码
这里主要用了 redux-actions
来建立action的 , 原生写法
// action const addTodo = text => ({ type: 'ADD_TODO', payload: { text, completed: false } }) // reducer const todos = (state = [], action) => { switch(action.type) { case 'ADD_TODO': return [...state, action.payload] ... default: return state } } 复制代码
用了 redux-actions
的写法
import { createAction, handleActions } from 'redux-actions' // action const addTodo = createAction('ADD_TODO') // reducer const todos = handleActions({ ADD_TODO: (state, action) => { return [...state, action.payload] } ... }, []) 复制代码
// 用redux-actions
简单明了
用了redux,这东西基本就不能少了, connect
主要是用来 链接 组件
跟 redux store
的, 就是让组件能获取redux store里面的 值
和 方法
connect([mapStateToProps], [mapDispatchToProps], [mergeProps],[options])
通常只用到前两个参数
mapStateToProps(state, ownProps)
: 获取store里面state指定数据,而后传递到指定组件, ownProps 组件自己的 propsmapDispatchToProps
: 这个是获取store里面的action方法, 而后传入指定组件用法
import toggleTodo from 'actions/todo' const mapStateToProps = state => ({ active: state.active }) const mapDispatchToProps = { onTodoClick: toggleTodo } connect(mapStateToProps, mapDispatchToProps)(Component) // 在Component组件中, 便能在 props 里面获取到 active 数据, 跟 onTodoClick 这个方法了 复制代码
connect
不少地方基本都要用到 因此也进行了封装
// connect.js import actions from 'src/actions' // 全部action import {connect} from 'react-redux' import {bindActionCreators} from 'redux' export default connect( state => ({state}), // 偷懒了, 每次把state里面全部的数据都返回了 dispatch => bindActionCreators(actions, dispatch) //合并全部action,而且传入dispatch, 那样咱们在组件里面调用action,就不在须要dispatch了 ) 复制代码
而后咱们把 connect.js
文件经过 webpack
的alias属性来进行配置
//配置别名映射 alias: { 'src': resolve('src'), 'connect': resolve('src/utils/connect') } 复制代码
而后咱们就能够在文件中以下引用
import React from 'react' import connect from 'connect' @connect // 经过装饰器调用 class Component extends React.Component { componentWillMount () { const {state, onTodoClick} = this.props console.log(state, onTodoClick) } } 复制代码
为了省事,我把store
里面全部的数据 和 action
都返回了。
在 vue
中 咱们通常都是经过设置 style标签的 scoped
属性来作到css模块化 可是在 react
中,我采用的 cssModules
来作css模块化
webpack
设置 css-loader
的modules
来开启css的模块化{ loader: 'css-loader', options: { modules: true, //是否开启 localIdentName: '[name]__[local]___[hash:base64:5]' // 转化出来的class名字结构 } }, 复制代码
import styles from './styles.css' export default () => ( <div className={styles.a}></div> ) //styles.css .a { color: #ff4747; } 复制代码
或者能够经过 react-css-modules
来更方便的控制class
类名
import styles from './styles.css' import CSSModules from 'react-css-modules' class Component extends React.Component { render () { return ( <div styleName='a b'></div> ) } } export default CSSModules(Component, styles, { allowMultiple: true //容许多个class一块儿使用 }) //styles.css .a { color: #ff4747; } .b { background: #f00; } 复制代码
这样咱们就能够经过字符串的方式传入 class
类名. 注意: 咱们添加时 再也不使用 className
了, 而是使用 styleName
了
class Bingding extends React.Component { state = { value: '' } handleInput = value => { this.setState({ value }) } render () { return ( <input type="text" value={this.state.value} onChange={e => {this.handleInput(e.target.value)}}/> <div>{this.state.value}</div> ) } } 复制代码
就是经过 onChange
事件 来触发 this.setState
从新渲染 render 方法
还有一些知识点 包括 动画
,生命周期
等等 就不过多介绍了。这些项目中基本多多少少都参和了一点。 开发中遇到的问题挺多的,最主要是react-router
配置的问题,怎么配置都感受不太好。 也同时但愿有人推荐几个全面的尤为是最新版本的react
开源项目。
项目启动步骤
国内比较火的两个框架,也勉强算是都接触了下,vue
我是一直在用的,react
算是年后刚接触的。 从我目前来看,vue
比react
开发起来确实要方便不少(可能用的比较多吧)。 由于vue
不少经常使用的都是内置的。而react
基本都要本身去寻找对应的模块。自己就只提供UI, 其余基本得自力更生。 主要是你常常一找能找着多个模块,你就不知道用哪一个,还得一个个试水。固然,react
的社区强大,这么都不是什么大问题。