react路由深度解析

先看一段代码能否秒懂很重要

这是app.js 全局js的入口

 1 import React from 'react'
 2 import { render } from 'react-dom'
 3 import { Router, browserHistory } from 'react-router'
 4 
 5 import withExampleBasename from '../withExampleBasename'
 6 import './stubs/COURSES'
 7 
 8 const rootRoute = {
 9   childRoutes: [ {   //子路由
10     path: '/',
11     component: require('./components/App'),
12     childRoutes: [
13       require('./routes/Calendar'),  //当前目录下有这么几个子路由
14       require('./routes/Course'),
15       require('./routes/Grades'),
16       require('./routes/Messages'),
17       require('./routes/Profile')
18     ]
19   } ]
20 }
21 
22 render((
23   <Router
24     history={withExampleBasename(browserHistory, __dirname)}
25     routes={rootRoute}
26   />
27 ), document.getElementById('example'))

以上面的calendar为例子 calendar下面有component 和index.js 同样其他如course grades等下面也一样

index.js

1 module.exports = {
2   path: 'calendar',
3   getComponent(nextState, cb) {
4     require.ensure([], (require) => {
5       cb(null, require('./components/Calendar'))
6     })
7   }
8 }

component下面有calendar.js

 1 import React, { Component } from 'react'
 2 
 3 class Calendar extends Component {
 4   render() {
 5     const events = [
 6       { id: 0, title: 'essay due' }
 7     ]
 8 
 9     return (
10       <div>
11         <h2>Calendar</h2>
12         <ul>
13           {events.map(event => (
14             <li key={event.id}>{event.title}</li>
15           ))}
16         </ul>
17       </div>
18     )
19   }
20 }
21 
22 module.exports = Calendar

怎么样找到诀窍了吗 /routes/calendar/index.js +component/calendar.js /routes/grades/index.js +component/grades.js

找到规律了吧 !!

来一个稍微复杂的路由

(这是全局的变量入口) COURSE.JS

 1 global.COURSES = [
 2   {
 3     id: 0,
 4     name: 'React Fundamentals',
 5     grade: 'B',
 6     announcements: [
 7       {
 8         id: 0,
 9         title: 'No class tomorrow',
10         body: 'There is no class tomorrow, please do not show up'
11       }
12     ],
13     assignments: [
14       {
15         id: 0,
16         title: 'Build a router',
17         body: 'It will be easy, seriously, like 2 hours, 100 lines of code, no biggie',
18         grade: 'N/A'
19       }
20     ]
21 
22   },
23 
24   {
25     id: 1,
26     name: 'Reusable React Components',
27     grade: 'A-',
28     announcements: [
29       {
30         id: 0,
31         title: 'Final exam next wednesday',
32         body: 'You had better prepare'
33       }
34     ],
35     assignments: [
36       {
37         id: 0,
38         title: 'PropTypes',
39         body: 'They aren\'t for you.',
40         grade: '80%'
41       },
42       {
43         id: 1,
44         title: 'Iterating and Cloning Children',
45         body: 'You can totally do it.',
46         grade: '95%'
47       }
48     ]
49   }
50 ]

Course路由下面有子路由 他的目录结构 /routes/course/index.js +component(course.js+nav.js+dashborad.js)+ routers/(assignments + announcement +grade.js)

瞬间有点凌乱 有木有 往下看就明白了

course.js

 1 /*globals COURSES:true */
 2 import React, { Component } from 'react'
 3 import Dashboard from './Dashboard'
 4 import Nav from './Nav'
 5 
 6 const styles = {}
 7 
 8 styles.sidebar = {
 9   float: 'left',
10   width: 200,
11   padding: 20,
12   borderRight: '1px solid #aaa',
13   marginRight: 20
14 }
15 
16 class Course extends Component {
17   render() {
18     let { sidebar, main, children, params } = this.props
19     let course = COURSES[params.courseId]   //这是啥? 根据前面导航条转过来的
20 
21     let content
22     if (sidebar && main) {  //根据条件 初始化组件内容
23       content = (
24         <div>
25           <div className="Sidebar" style={styles.sidebar}>
26             {sidebar}
27           </div>
28           <div className="Main" style={{ padding: 20 }}>
29             {main}
30           </div>
31         </div>
32       )
33     } else if (children) {
34       content = children
35     } else {
36       content = <Dashboard />   //走的是这一步
37     }
38 
39     return (
40       <div>
41         <h2>{course.name}</h2>
42         <Nav course={course} />  //吸进来的一个组件  确定是去哪里  announcement  assignment  grades
43         {content}
44       </div>
45     )
46   }
47 }
48 
49 module.exports = Course

貌似还有点迷糊 继续往下看吧

nav.js

 1 import React, { Component } from 'react'
 2 import { Link } from 'react-router'
 3 
 4 const styles = {}
 5 
 6 styles.nav = {
 7   borderBottom: '1px solid #aaa'
 8 }
 9 
10 styles.link = {
11   display: 'inline-block',
12   padding: 10,
13   textDecoration: 'none'
14 }
15 
16 styles.activeLink = {
17   ...styles.link,
18   color: 'red'
19 }
20 
21 class Nav extends Component {
22   render() {
23     const { course } = this.props
24     const pages = [
25       [ 'announcements', 'Announcements' ],
26       [ 'assignments', 'Assignments' ],
27       [ 'grades', 'Grades' ]
28     ]
29 
30     return (
31       <nav style={styles.nav}>
32         {pages.map((page, index) => (
33           <Link
34             key={page[0]}
35             activeStyle={index === 0 ? { ...styles.activeLink, paddingLeft: 0 } : styles.activeLink}
36             style={index === 0 ? { ...styles.link, paddingLeft: 0 } : styles.link}
37             to={`/course/${course.id}/${page[0]}`}   //这里是3个导航的跳转   /course/${course.id}/announcement   去往announcements assignments grades
38           >{page[1]}</Link>
39         ))}
40       </nav>
41     )
42   }
43 }
44 
45 export default Nav

来看一下router/announcement(index.js + component + router)

index.js 文件

 1 module.exports = {
 2   path: 'announcements',
 3 
 4   getChildRoutes(partialNextState, cb) {
 5     require.ensure([], (require) => {
 6       cb(null, [
 7         require('./routes/Announcement')  //这又是一个子路由
 8       ])
 9     })
10   },
11 
12   getComponents(nextState, cb) {
13     require.ensure([], (require) => {
14       cb(null, {
15         sidebar: require('./components/Sidebar'),
16         main: require('./components/Announcements')  //依赖的组件
17       })
18     })
19   }
20 }   

看到这里是否有点明白了 就像洋葱一层一层拨动我的心。。。还没完呢

component/announcement.js

 1 import React, { Component } from 'react'
 2 
 3 class Announcements extends Component {
 4   render() {
 5     return (
 6       <div>
 7         <h3>Announcements</h3>
 8         {this.props.children || <p>Choose an announcement from the sidebar.</p>}  //这个props在哪里找呀
 9       </div>
10     )
11   }
12 }
13 
14 module.exports = Announcements

component/sidebar.js

 1 import React, { Component } from 'react'
 2 import { Link } from 'react-router'
 3 
 4 class AnnouncementsSidebar extends Component {
 5   render() {
 6     let { announcements } = COURSES[this.props.params.courseId]   //注意这种es6的语法  取得是里面announcements的直
 7 
 8     return (
 9       <div>
10         <h3>Sidebar Assignments</h3>
11         <ul>
12           {announcements.map(announcement => (
13             <li key={announcement.id}>
14               <Link to={`/course/${this.props.params.courseId}/announcements/${announcement.id}`}>
15                 {announcement.title}   //这个到了最后了   
16               </Link>
17             </li>
18           ))}
19         </ul>
20       </div>
21     )
22   }
23 }
24 
25 module.exports = AnnouncementsSidebar

还没完呢 还有个 router-anouncement/index.js + announcement.js 的路由

看index.js 里面的内容

1 module.exports = {
2   path: ':announcementId',   //终于真相大白了  !!
3 
4   getComponent(nextState, cb) {
5     require.ensure([], (require) => {
6       cb(null, require('./components/Announcement'))
7     })
8   }
9 }

在看最里面的组件announcement.js

 1 import React, { Component } from 'react'
 2 
 3 class Announcement extends Component {
 4   render() {
 5     let { courseId, announcementId } = this.props.params   //这个很关键  怎么着呢  说白了就是 地址的参数 ${courseId}  ${announceId}
 6     let { title, body } = COURSES[courseId].announcements[announcementId]
 7 
 8     return (
 9       <div>
10         <h4>{title}</h4>
11         <p>{body}</p>
12       </div>
13     )
14   }
15 }
16 
17 module.exports = Announcement

我们大概可以看出来 这个路由到底是如何运作了 还有些细节问题我们继续看

整理一下思路 再出发

1. 第一个app.js 确定根目录 /

2. /calendar /course /message /profile 这都是二级目录 下面都有一个 index.js 指定当前的路径 如course/index.js 指定path:course;

3./course/${courseiD}/announcement/${announcementId} /course/${courseiD}/assignment/${assignmentId} 这是 深层次的

4./course/calendar course/grades 这是浅层次的

最后是关键的全局组件

app.js

 1 import React, { Component } from 'react'
 2 import Dashboard from './Dashboard'
 3 import GlobalNav from './GlobalNav'
 4 
 5 class App extends Component {
 6   render() {
 7     return (
 8       <div>
 9         <GlobalNav />
10         <div style={{ padding: 20 }}>
11           {this.props.children || <Dashboard courses={COURSES} />}  //如果该属性没有children   就render dashboard
12         </div>
13       </div>
14     )
15   }
16 }
17 
18 module.exports = App

dashboard.js

 1 import React, { Component } from 'react'
 2 import { Link } from 'react-router'
 3 
 4 class Dashboard extends Component {
 5   render() {
 6     const { courses } = this.props
 7 
 8     return (
 9       <div>
10         <h2>Super Scalable Apps</h2>
11         <p>
12           Open the network tab as you navigate. Notice that only the amount of
13           your app that is required is actually downloaded as you navigate
14           around. Even the route configuration objects are loaded on the fly.
15           This way, a new route added deep in your app will not affect the
16           initial bundle of your application.
17         </p>
18         <h2>Courses</h2>{' '}
19         <ul>
20           {courses.map(course => (
21             <li key={course.id}>
22               <Link to={`/course/${course.id}`}>{course.name}</Link>   //这里会根据当前id去不同的course页面
23             </li>
24           ))}
25         </ul>
26       </div>
27     )
28   }
29 }
30 
31 export default Dashboard

globalNav.js 全局导航

 1 import React, { Component } from 'react'
 2 import { Link } from 'react-router'
 3 
 4 const dark = 'hsl(200, 20%, 20%)'
 5 const light = '#fff'
 6 const styles = {}
 7 
 8 styles.wrapper = {
 9   padding: '10px 20px',
10   overflow: 'hidden',
11   background: dark,
12   color: light
13 }
14 
15 styles.link = {
16   padding: 11,
17   color: light,
18   fontWeight: 200
19 }
20 
21 styles.activeLink = {
22   ...styles.link,
23   background: light,
24   color: dark
25 }
26 
27 class GlobalNav extends Component {
28 
29   constructor(props, context) {
30     super(props, context)
31     this.logOut = this.logOut.bind(this)
32   }
33 
34   logOut() {
35     alert('log out')
36   }
37 
38   render() {
39     const { user } = this.props
40 
41     return (
42       <div style={styles.wrapper}>
43         <div left' }}>
44           <Link to="/"  '}   // 回到根目录
45           <Link to="/calendar"  '}
46           <Link to="/grades"  '}
47           <Link to="/messages"  '}
48         </div>
49         <div right' }}>
50           <Link /profile">{user.name}</Link> <button onClick={this.logOut}>log out</button>
51         </div>
52       </div>
53     )
54   }
55 }
56 
57 GlobalNav.defaultProp= {
58   user: {
59     id: 1,
60     name: 'Ryan Florence'
61   }
62 }
63 
64 export default GlobalNav

好吧 现在头绪出来了吧 我反正有点眉目了

1.首先是index.html 有个globalnav / /calendar /grades /message /profile 分别去向 主页 日历页 年级页 信息页 详情页

2.如果 找不到props.children 就会渲染course 也会根据当前的courseid去往相应页

好吧 今天就这么多 让我好好捋捋。。。