React 脚手架介绍以及路由基本信息

create-react-app创建项目

npm install -g create-react-app
create-react-app react-cli

目录结构介绍

图中红色框是我后来加的,暂时可以不考虑。

React 脚手架介绍以及路由基本信息_第1张图片
public:里面包含了我们项目中的启动页面,react比较适合单页面项目应用开发。
  • favico.ico: 这是用来表示:快捷方式 小图标。详情可以访问文章
  • index.html: 初始页面。
  • manifest.json: wepack打包优化相关,本人没有研究过。
src:里面包含项目文件,最重要的是下面两个文件
  • index.js 项目初始执行的js。内部进行页面元素的渲染操作。
  • App.js index.js初始加载的组件。

至于为什么初始加载的index.html和index.js。我们可以将项目中的配置文件显示出来

在当前目录下执行下面代码:

npm run eject //必须在当前本地没有code change的情况下去执行,不然可能会报错,因为我在用的时候就报错了。

然后我们会看到目录结构发生变化:

React 脚手架介绍以及路由基本信息_第2张图片

如图两个红色框都是新加的。不过目前我最关心的是path.js。下面是部分代码:

module.exports = {
  dotenv: resolveApp('.env'),
  appBuild: resolveApp('build'),
  appPublic: resolveApp('public'),
  appHtml: resolveApp('public/index.html'),
  appIndexJs: resolveApp('src/index.js'),
  appPackageJson: resolveApp('package.json'),
  appSrc: resolveApp('src'),
  yarnLockFile: resolveApp('yarn.lock'),
  testsSetup: resolveApp('src/setupTests.js'),
  appNodeModules: resolveApp('node_modules'),
  publicUrl: getPublicUrl(resolveApp('package.json')),
  servedPath: getServedPath(resolveApp('package.json')),
};
如图可以看出为什么index.html和index.js是初始执行文件。

路由例子

提出路由问题

单页面应用上的hash值变化会导致页面上渲染上不同的elements。

第一需要监控hash值的变化。

第二hash值的变化需要引起当前主组件内的state变化。(这一点相信不难理解,可以访问前一篇文章)。

不使用React Router

//index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(, document.getElementById('root'))
//App.js
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import About from "./containers/about";
import Inbox from "./containers/inbox";
import Home from "./containers/home";

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {route: window.location.hash.substr(1)};
    }

    componentDidMount() {
        window.addEventListener('hashchange', () => { //监控hash值的变化
            this.setState({
                route: window.location.hash.substr(1) //更新state值
            })
        })
    }

    render() {
        let Child
        switch (this.state.route) { //判断state值,渲染不同的组件
            case '/about':
                Child = About;
                break;
            case '/inbox':
                Child = Inbox;
                break;
            default:
                Child = Home;
        }

        return (
            
        )
    }
}

export default App;
// containers/about.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class About extends Component {
    render() {
        return (
            
About
); } } export default About; // containers/home.js import React, { Component } from 'react'; import ReactDOM from 'react-dom'; class Home extends Component { render() { return (
Home
); } } export default Home; // containers/inbox.js import React, { Component } from 'react'; import ReactDOM from 'react-dom'; class Inbox extends Component { render() { return (
Inbox
); } } export default Inbox;
源码地址: https://github.com/rodchen-king/react/tree/v0.1/react-cli

React-Route

React Router 是一个基于 React 之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步。

npm install [email protected] --save

此处说明一下:因为当前的react版本是16.3.2,因为react16的不能兼容 react-router 2/3, 如果要用 react-router2/3,需将react降级到15,所以我这里用的[email protected][email protected]

举例:

import React from 'react';
import ReactDOM from 'react-dom'
import { Router, Route, Link } from "react-router";
import './index.css';

const App = React.createClass({
    render() {
        return (
            

App

  • About
  • Inbox
{this.props.children}
) } }) const About = React.createClass({ render() { return

About

} }) const Inbox = React.createClass({ render() { return (

Inbox

{this.props.children || "Welcome to your Inbox"}
) } }) const Message = React.createClass({ render() { return

Message {this.props.params.id}

} }) ReactDOM.render(( ), document.getElementById('root'))

路由基本配置

ReactDOM.render((
    
        
            
            
                
            
        
    
), document.getElementById('root'));

Router

React Router 的重要组件。它能保持UI和URL的同步,其内部包含多个路由。

children (required)

一个或多个的 Route 或 PlainRoute。当 history 改变时, 会匹配出 Route 的一个分支,并且渲染这个分支中配置的组件,渲染时保持父 route 组件嵌套子 route 组件。

routes
children 的别名。

const routes = (
    
    
        
    
);

ReactDOM.render((
    

    
), document.getElementById('root'))
对于children和routes之间的区别没有研究过,二者任意一个存在,路由都可以运行。 还有其他的props,这种就需要自己拓展了。

Route

Route 是用于声明路由映射到应用程序的组件层。

    
    
        
    
path
URL 中的路径。它会以父组件路由为基础,除非它是从/开始的, 将它变成一个绝对路径。
父子组件如何判断,可以看出内部的Route组件都是可能出现的子组件。

    
    
        
    

这个例子中中,/App/about可以访问到About组件,但是/App/inbox访问不到InBox组件。因为路由path以/开头。

component
当匹配到URL时,单个的组件会被渲染。它可以被父route 组件的this.props.children渲染。这个知识点很重要,因为父子组件通过路由渲染的过程中,是需要一个地方渲染对应组件内容。所以每个父组件都会设置this.props.children来划定一个渲染子组件的区域。

const App = React.createClass({
    render() {
        return (
            

App

  • About
  • Inbox
{this.props.children}
) } })
components
Route 可以定义一个或多个已命名的组件,当路径匹配到 URL 时,它们作为 name:component 对的一个对象去渲染。它们可以被父route组件的this.props[name] 渲染。
这里的含义是:子路由的变化会导致父组件的多处变化。下面例子是父组件中有两块不相接的区域会随着路由的变化而变化。这里其实将子组件按照路由的变化然后传入到父组件的props中。

import React from 'react';
import ReactDOM from 'react-dom'
import {Router, Route, Link} from "react-router";
import './index.css';

const App = React.createClass({
    render() {
        const { main, sidebar } = this.props
        return (
            

App

{main}
  • groups
  • users
{sidebar}
) } }) const Groups = React.createClass({ render() { return

Groups

} }) const GroupsSidebar = React.createClass({ render() { return (

GroupsSidebar

) } }) const Users = React.createClass({ render() { return

Users

} }) const UsersSidebar = React.createClass({ render() { return (

UsersSidebar

{this.props.children}

) } }) const Profile = React.createClass({ render() { return (

Profile {this.props.params.id}

) } }) const routes = ( ) ReactDOM.render(( ), document.getElementById('root'))

Link

允许用户浏览应用的主要方式。 以适当的 href 去渲染一个可访问的锚标签。
可以知道哪个 route 的链接是激活状态的,并可以自动为该链接添加 activeClassName 或 activeStyle。
to
跳转链接的路径,如 /users/123。
query
已经转化成字符串的键值对的对象。
hash
URL 的 hash 值,如 #a-hash。
注意:React Router 目前还不能管理滚动条的位置,并且不会自动滚动到 hash 对应的元素上。如果需要管理滚动条位置,可以使用 scroll-behavior 这个库。
state
保存在 location 中的 state。
activeClassName
当某个 route 是激活状态时, 可以接收传入的 className。失活状态下是默认的 class。
activeStyle
当某个 route 是激活状态时,可以将样式添加到链接元素上。

添加首页

首页就是父组件初始渲染的页面,也可以说是父组件的默认渲染页面,使用 IndexRoute 来设置一个默认页面。IndexRoute对应的组件将会渲染到App中的this.props.children处。

    
    
    
        
    

页面跳转

当我们修改了url之后,如下访问messages/1的路由从/inbox/messages/:id -> /messages/:id,这个时候为了兼容旧的url,则需要使用Redirect:

    
    
    
        
        
    

进入和离开的Hook

Route 可以定义 onEnter 和 onLeave 两个 hook ,这些hook会在页面跳转确认时触发一次。这些 hook 对于一些情况非常的有用,例如权限验证或者在路由跳转前将一些数据持久化保存起来。但是下面例子中我的测试,二者不不能共存,如果二者同时存在,onLeave没有作用。
import React from 'react';
import ReactDOM from 'react-dom'
import {Router, Route, Link} from "react-router";
import './index.css';
import IndexRoute from "react-router/es6/IndexRoute";
import Redirect from "react-router/es6/Redirect";

const App = React.createClass({
    render() {
        return (
            

App

  • About
  • Inbox
{this.props.children}
) } }) const About = React.createClass({ render() { return

About

} }) const Inbox = React.createClass({ render() { return (

Inbox

{this.props.children || "Welcome to your Inbox"}
) } }) const Message = React.createClass({ render() { return

Message {this.props.params.id}

} }) const Dashboard = React.createClass({ render() { return

Hello!

} }) const EnterHook = function(RouterState, RedirectFunction, callback) { console.log("进入about"); } const LeaveHook = function(RouterState) { console.log("离开about"); } const routes = ( ); ReactDOM.render(( ), document.getElementById('root'))

替换的配置方式

因为 route 一般被嵌套使用,所以使用 JSX 这种天然具有简洁嵌套型语法的结构来描述它们的关系非常方便。然而,如果你不想使用 JSX,也可以直接使用原生 route 数组对象。
const routes = [
    { path: '/',
        component: App,
        indexRoute: { component: Dashboard },
        childRoutes: [
            { path: 'about', component: About },
            { path: 'inbox',
                component: Inbox,
                childRoutes: [
                    { path: '/messages/:id', component: Message }
                ]
            }
        ]
    }
]

路由匹配

         // matches /hello/michael and /hello/ryan
       // matches /hello, /hello/michael, and /hello/ryan
           // matches /files/hello.jpg and /files/hello.html
            // matches /files/hello.jpg and /files/path/to/file.jpg

Index Links

IndexLink的意义在于只有当前的路由匹配的情况下才会显示激活状态,下面举个例子:路由匹配到/inbox,此时下面第一个和第三个link都显示已激活。但是第二个不会,因为当前的路由是/inbox,不是/。只有当路由为/,第二个link才会激活。
  • About
  • About
  • Inbox

browserHistory

React Router 是建立在 history 之上的。 简而言之,一个 history 知道如何去监听浏览器地址栏的变化, 并解析这个 URL 转化为 location 对象, 然后 router 使用它匹配到路由,最后正确地渲染对应的组件。

常用的 history 有三种形式, 但是你也可以使用 React Router 实现自定义的 history。

browserHistory
hashHistory

createMemoryHistory

在我没有使用任何的方式的情况下,路由形如下面:

http://localhost:3000/inbox#/?_k=iawvme

然后修改代码如下,加上history={browserHistory}

路由变化就是:http://localhost:3000/inbox


动态路由

React Router 里的路径匹配以及组件加载都是异步完成的,不仅允许你延迟加载组件,并且可以延迟加载路由配置。在首次加载包中你只需要有一个路径定义,路由会自动解析剩下的路径。
Route 可以定义 getChildRoutes,getIndexRoute 和 getComponents 这几个函数。它们都是异步执行,并且只有在需要时才被调用。我们将这种方式称之为 “逐渐匹配”。 React Router 会逐渐的匹配 URL 并只加载该 URL 对应页面所需的路径配置和组件。
const CourseRoute = {
  path: 'course/:courseId',

  getChildRoutes(location, callback) {
    require.ensure([], function (require) {
      callback(null, [
        require('./routes/Announcements'),
        require('./routes/Assignments'),
        require('./routes/Grades'),
      ])
    })
  },

  getIndexRoute(location, callback) {
    require.ensure([], function (require) {
      callback(null, require('./components/Index'))
    })
  },

  getComponents(location, callback) {
    require.ensure([], function (require) {
      callback(null, require('./components/Course'))
    })
  }
}

这里的思想很简单,我们来类比一下:

const routeConfig = [
    { path: '/',
        component: App,
        indexRoute: { component: Dashboard },
        childRoutes: [
            { path: 'about', component: About },
            { path: 'inbox',
                component: Inbox,
                childRoutes: [
                    { path: '/messages/:id', component: Message }
                ]
            }
        ]
    }
]
我们以/为例:getChildRoutes方法回去访问子路由的文件,首先说明这里的子路由文件是一个路由,不是一个简单的组件。子路由文件的格式和上面的类似,也可能会有这几个方法。另外两个方法访问的就是具体的组件了。

跳转前确认

React Router 提供一个 routerWillLeave 生命周期钩子,这使得 React 组件可以拦截正在发生的跳转,或在离开 route 前提示用户。routerWillLeave 返回值有以下两种:

return false 取消此次跳转

return 返回提示信息,在离开 route 前提示用户进行确认。

需要先引入mixins: [ Lifecycle ]

const About = React.createClass({
    mixins: [ Lifecycle ],

    routerWillLeave(nextLocation) {
        return 'Your work is not saved! Are you sure you want to leave?'
    },

    render() {
        return 

About

} })

获取路由参数

组件的生命周期内我们可以通过this.props.params获取到。

React 脚手架介绍以及路由基本信息_第3张图片


写在最后:react 路由知识还有许多东西需要探索,但是可以先了解基本知识再去拓展,对于版本的升级可以上网简单搜索一下,应该就可以解决了。最后吐槽一下react的生态圈真的不如angular。

你可能感兴趣的:(React,React)