美文网首页
react 创建新的项目以及react-router4.xx 配

react 创建新的项目以及react-router4.xx 配

作者: Lyan_2ab3 | 来源:发表于2018-08-17 10:31 被阅读0次

    背景日常絮叨

    看到这个文章 大部分也是react 有基础或者刚入门的小伙伴,react 目前比较流行 欢欣的一个库,下面让我们一步一步run 起来

    路由跳转是指在同步保持浏览器URL的过程中渲染页面中的视图。React Router 让你声明式的操作路由跳转。声明式路由方法允许你控制应用中的数据流.

    创建新的react 项目

    • npm install -g create-react-app
    • create-react-app myapp //myapp 是你的项目名称
    • cd myapp

    但是打开项目会发现,一些与webpack相关的东西被隐藏掉了,只需要做单独的配置键入下面的命令

    • npm run eject

    在运行之前 可以安装一个react-router

    • npm install --save react-router-dom
    • npm install 安装依赖
    • npm run start
      没有意外的情况下,都可以运行起来,提醒下 如果 你修改 项目之后 在执行npm run eject 可能会有报错的情况,如果需要还是提早执行。

    为了方便管理复用,我一般都会拆分组件,src 里面 创建 文件page route component ,如下图:

    1.jpeg

    react-router

    • 上面的都准备好之后,我们就开始路由的配置,react-router,一般我们用到路由分为:
      • 路由的基本的跳转
      • 嵌套路由
      • 带路径参数的嵌套路由

    路由组件:BrowserRouter 和HashRouter
    路径匹配的组件: Route 和 Switch
    导航组件: Link

    • 一般基础的路由
    export default (
      <Router history={historyConfig}>
       <div>
          <ul className="nav">
          <li><Link to="/">App</Link></li>
          <li><Link to="/About">About</Link></li>
          <li><Link to="/User">User</Link></li>
              <li><Link to="/Detail">Detail</Link></li>
         </ul>
        <hr />
        <Route exact path="/" component={Home} />
        <Route path="/About" component={About} />
        <Route path="/User" component={User} />
        <Route path="/Detail" component={DeatilComponent} />
    </div>
      </Router>
    );
    

    Router组件决定了我们使用html5 history api,
    Route组件定义了路由的匹配规则和渲染内容,
    使用 Link 组件进行路由之间的导航。
    使用 exact 属性来定义路径是不是精确匹配。

    Router

    我们都知道 React Router API 中有两种类型的路由

    <BrowserRouter> http://localhost:300/home 比较常用
    <HashRouter> 哈希路由 http://localhost:300/#/home

    • 如果有服务器端的动态支持,建议使用 BrowserRouter,否则建议使用 HashRouter。
    import {
        BrowserRouter as Router,
        Route,
        Link
    } from 'react-router-dom'
    
    

    将 BrowserRouter 修改为 HashRouter 就可以了,基本不需要修改其他东西。

    Route常用属性

    主要职责是当Route的位置和路径匹配的时候渲染对应的ui

    exact、path以及component属性
    Route会向component组件传一个参数,包含属性match,location,history。match属性对象又包含url,path,params等属性。比较常用的就是match的url属性,可以继续基于url指定组件里面的Link标签要链接到的url,从而显示对应的组件。

    Route写在哪里,当Route匹配到URL的时候,相应的组件就会在那里进行渲染。component,render,children,Route的这三个属性写一个就行,不能同时都写。precendence order: component > render > children.

    注意:children中的元素不管是否匹配到URL都会渲染,不过没有匹配到的Route向children的函数中传的值是null,只有匹配到的时候才会有值。

    • 横向导航栏:使用Routed 的 children属性。
      侧边菜单栏:左边是Link,右边把Route写在右边展示区域的div里面,匹配到的时候进行渲染。
    <Route>是如何渲染的?

    当路由 match 成功之后,route 根据

    • 1、component : 一个React组件。当带有component参数的route匹配成功后,route会返回一个新的元素,其为component参数所对应的React组件(使用React.createElement创建)。

    • 2、render : 一个返回React element的函数[注5]。当匹配成功后调用该函数。该过程与传入component参数类似,并且对于行级渲染与需要向元素传入额外参数的操作会更有用。

    • 3、children : 一个返回React element的函数。与上述两个参数不同,无论route是否匹配当前location,其都会被渲染。


    • Route向component组件进行参数传递
      组件:
      const Topics = ( {match} ) => ()
      路由:
      <Route path="/topics" component={Topics}/>
      路由Route向组件传的参数:
      {history:{},
      location:{},
      match:{}}.
      执行到 const Topics = ( {match} ) => ()的时候会将参数对象
      赋值给对象{match},所以此时组件实际接受的参数只有match对象。
    • history 插件

    $ npm install --save history
    createBrowserHistory

    import createBrowserHistory from 'history/createBrowserHistory';
    const historyConfig = createBrowserHistory({
    basename: '/' + AREA_ENV

    });

    history有三种使用方式:
    • createBrowserHistory 现代浏览器使用 目前用的最多的

    createBrowserHistory({
    basename: '', // 基链接
    forceRefresh: false, // 是否强制刷新整个页面
    keyLength: 6, // location.key的长度
    getUserConfirmation: (message,callback) => callback(window.confirm(message)) // 跳转拦截函数 })

    • createMemoryHistory 手机端使用

    createMemoryHistory({
    initialEntries: ['/'], // 初始载入路径,和MemoryRouter中的initialEntries是一样的 initialIndex: 0, // initialEntries初始载入索引
    keyLength: 6, // location.key的长度
    getUserConfirmation: null // 路由跳转拦截函数 })

    • createHashHistory 老版本浏览器使用,暂不介绍

    Switch :

    渲染与该地址匹配的第一个子节点 <Route> 或者 <Redirect>。外面包一层Switch 和不用Switch 有什么不同呢?

    • 用Switch 包含 只渲染一个路由,如果不用Switch则全部的路由都要渲染出来

    下面的代码 所有的路由 都会渲染出来,那么如果有些需求比如 侧栏,面包屑那么我们只能选择一个 路由渲染出来

        <Route exact path="/" component={Home} />
        <Route path="/Detail" component={PrivateRoute}  />
        <Route path="/About" component={About} />
        <Route path="/User" component={User} />
    

    所以使用 Switch 实现

    <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/Detail" component={PrivateRoute}  />
        <Route path="/About" component={About} />
        <Route path="/User" component={User} />
    </Switch>
    
    

    Link:

    导航到指定的路由


    react-router 的基本原理:

    实现URl 和UI界面的同步,其中在react-router中,URL对应location 对象,而UI对应的是react components 来决定,这样就是 location 和 components 的同步的问题。

    • react-router 主要依赖于history
    // 内部的抽象实现
    function createHistory(options={}) {
      ...
      return {
        listenBefore, // 内部的hook机制,可以在location发生变化前执行某些行为,AOP的实现
        listen, // location发生改变时触发回调
        transitionTo, // 执行location的改变
        push, // 改变location
        replace,
        go,
        goBack,
        goForward,
        createKey, // 创建location的key,用于唯一标示该location,是随机生成的
        createPath,
        createHref,
        createLocation, // 创建location
      }
    }
    
    • 1、当页面路由发生变化的时候;history 底层支持pushState, 将参数传输到 createLocation 方法中,返回 location 的结构如下:
    location = {
      pathname, // 当前路径,即 Link 中的 to 属性
      search, // search
      hash, // hash
      state, // state 对象
      action, // location 类型,在点击 Link 时为 PUSH,浏览器前进后退时为 POP,调用 replaceState 方法时为 REPLACE
      key, // 用于操作 sessionStorage 存取 state 对象
    };
    
    • 2、将location对象作为参数传入到 TransitionTo方法中,执行loaction 的变化,然后调用 window.location.hash 或者window.history.pushState() 修改了应用的 URL, 同时触发了 history.listen 的监听。
    • 3、更新location之后,然后系统调用 matchRoutes 方法配置 出与当前location对象匹配的一个子集。
    • 4、 匹配成功之后开始渲染 -> 渲染组件 更新UI(内部具体经过要后续研究)
    检测url 前进:
    • createBrowserHistory: pushState、replaceState
    • createHashHistory: location.hash=*** location.replace()
    • createMemoryHistory: 在内存中进行历史记录的存储

    hashChange 监听window.location.hash 的变化,hash 发生变化,浏览器更新url,同时history 栈中会产生一条新的记录。
    在 react-router 内部注册了 window.addEventListener('hashchange', listener, false) 事件监听器, listener 内部可以通过 hash fragment 获取到当前 URL 对应的 location 对象

    pushState: window.history.pushState(state, title, path)方法 ,改变浏览器的url,通过location.state 来获取到 state,在 react-router内部将该对象存储到了 sessionStorage 中

    检测url 回退:
    • createBrowserHistory: popstate
    • createHashHistory: hashchange
    • createMemoryHistory: 因为是在内存中操作,跟浏览器没有关系,不涉及UI层面的事情,所以可以直接进行历史信息的回退

    路由匹配原理:

    路由有三个属性决定是否匹配一个URL;

    • 嵌套关系
    • 路径语法
    • 优先级

    1、嵌套关系

    当一个给定的 URL 被调用时,整个集合中(命中的部分)都会被渲染。嵌套路由被描述成一种树形结构。React Router 会深度优先遍历整个路由配置来寻找一个与给定的 URL 相匹配的路由。

    2、路径语法

    • paramName – 匹配一段位于 /?# 之后的 URL。 命中的部分将被作为一个参数
    • () – 在它内部的内容被认为是可选的
    • ** – 匹配任意字符(非贪婪的)直到命中下一个字符或者整个 URL 的末尾,并创建一个 splat 参数
    <Route path="/hello/:name">         // 匹配 /hello/michael 和 /hello/ryan
    <Route path="/hello(/:name)">       // 匹配 /hello, /hello/michael 和 /hello/ryan
    <Route path="/files/*.*">           // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg
    
    

    3、优先级

    路由是自顶向下匹配路由,确保前一个路由不会匹配后一个路由的路径


    BrowserRouter 和HashRouter 区别

    BrowserRouter:

    vue:mode:history
    react: <BrowserRouter>

    pushState, replaceState会改变当前路径,但是他不会导致单页面的重新渲染,我们所使用时,页面的渲染是由react或vue中的Router中监听了路由的变化

    // 监听路由变化
    this.unlisten = props.history.listen(location => {
      if (this._isMounted) {
          this.setState({ location });
      } else {
          this._pendingLocation = location;
      }
    });
    // 以下就是Route在当路由发生变化时做的渲染
    {props.match
      ? children
        ? typeof children === "function"
          ? __DEV__
            ? evalChildrenDev(children, props, this.props.path)
            : children(props)
          : children
        : component
        ? React.createElement(component, props)
        : render
        ? render(props)
        : null
      : typeof children === "function"
      ? __DEV__
        ? evalChildrenDev(children, props, this.props.path)
        : children(props)
      : null}
    
    

    当刷新页面时,浏览器会向服务器请求example.com/list,服务器实际会去找根目录下list.html这个文件,发现找不到,因为实际上我们的服务器并没有这样的 物理路径/文件 或没有配置处理这个路由,所有内容都是通过React-Router去渲染React组件,自然会报404错误。这种情况我们可以通过配置Nginx或通过自建Node服务器来解决。

    hashHistory:

    vue:mode:hash
    react: <HashRouter>

    从BrowserRouter.js和HashRouter.js文件中可以看到,history对象是由history插件生成的

    // BrowserRouter.js
    import { createBrowserHistory as createHistory } from "history";
    history = createHistory(this.props);
     
    // 用于createHistory传入的配置对象参数,也说明了这个配置是有父级传递的,而不是BrowserRouter自身的
    BrowserRouter.propTypes = {
       basename: PropTypes.string,
       children: PropTypes.node,
       forceRefresh: PropTypes.bool,
       getUserConfirmation: PropTypes.func,
       keyLength: PropTypes.number
    };
     
    // HashRouter.js
    import { createHashHistory as createHistory } from "history";
    history = createHistory(this.props);
     
    // 用于createHistory传入的配置对象参数
    HashRouter.propTypes = {
       basename: PropTypes.string,
       children: PropTypes.node,
       getUserConfirmation: PropTypes.func,
       hashType: PropTypes.oneOf(["hashbang", "noslash", "slash"])
    };
    
    

    createMemoryHistory:

    • Memory history 不会在地址栏被操作或读取。这就解释了我们是如何实现服务器渲染的。同时它也非常适合测试和其他的渲染环境(像 React Native )。和另外两种history的一点不同是你必须创建它,这种方式便于测试。

    React-router 按需加载方式:

    一: create-react-app

    • 创建一个异步组件 AsyncComponent
    import React from 'react';
    
    export default function (getComponent) {
      return class AsyncComponent extends React.Component {
        static Component = null;
        state = { Component: AsyncComponent.Component };
    
        componentWillMount() {
          if (!this.state.Component) {
            getComponent().then(({default: Component}) => {
              AsyncComponent.Component = Component
              this.setState({ Component })
            })
          }
        }
        render() {
          const { Component } = this.state
          if (Component) {
            return <Component {...this.props} />
          }
          return null
        }
      }
    }
    
    
    • 使用异步组件:我们将使用asyncComponent动态导入我们想要的组件。
    import asyncComponent from './asyncComponent'
    const Login = asyncComponent(() => load('login/login'))
    const LayoutPage = asyncComponent(() => load('layout/layout'))
    const NoticeDeatil = asyncComponent(() => load('noticeDetail/noticeDetail'))
    export const appRouterMap = [
        {path:"/login",name:"Login",component:Login,auth:false},
        {path:"/web",name:"LayoutPage",component:LayoutPage,auth:false},
        {path:"/notice/:id",name:"NoticeDeatil",component:NoticeDeatil,auth:false},
    ]
    

    二、借助react-loadable来实现按需加载

    1、利用react-loadable这个高级组件,要做到实现按需加载这一点

    第三种 bundle-loader 按需加载方式


    不管vue 还是react 都可以使用

    hash路由:

    hash原理是触发了onhashchange 事件,

    hash —— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。比如这个 URL:http://www.abc.com/#/hello,hash 的值为 #/hello。
    它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。

    window.onhashchange = function(event){
        console.log(event.oldURL, event.newURL);
        let hash = location.hash.slice(1);
        document.body.style.color = hash; 
    }
    

    前端路由的核心,就在于 —— 改变视图的同时不会向后端发出请求。

    history路由:

    history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。

    • pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL;

    • pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;

    • pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;

    history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,http://www.abc.com/book/id。如果后端缺少对 /book/id 的路由处理,将返回 404 错误。
    Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”

    Vue-Router HTML5 History 模式

    相关文章

      网友评论

          本文标题:react 创建新的项目以及react-router4.xx 配

          本文链接:https://www.haomeiwen.com/subject/xejobftx.html