前端路由库

前端路由的原理大致相同:当页面的URL发生变化时,页面的显示结果可以根据URL的变化而变化,但是页面不会刷新。

要实现URL变化页面不刷新有两种方法:通过hash实现、通过History API实现。

1. 实现方法

  • hash实现原理

    改变页面的hash值不会刷新页面,而hashchange的事件,可以监听hash的变化,从而在hash变化时渲染新页面。

  • History API实现原理

    History API中pushState、replaceState方法会改变当前页面url,但是不会伴随着刷新,但是调用这两个方法改变页面url没有事件可以监听。有个history库增强了history API,采用发布订阅模式来对url的变化作出反映。其暴露出一个listen方法来添加订阅者,通过重写push、replace方法,使得这两个方法调用时通知订阅者,从而在url变化时渲染新页面。

2. react-route库

2.1 基本结构

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

export default function App() {
  return (
    
      
      
); } function Home() { return

Home

; } function About() { return

About

; } function Users() { return

Users

; }

react-router使用的基本结构是:

  1. 外层使用包裹整个app,主要类型有,分别对应上面两种实现方法;首先把location、history对象(增强的)通过react context API注入到子组件中,然后在中会调用history.listen方法监听location变化,当location变化时采用setState改变location触发子组件的更新。
  2. 标签做导航用,点击时会调用history.pushhistory.replace方法,并改变context中的location。
  3. context变化导致重新渲染,找到匹配的渲染。
  4. Route组件根据Swtich的匹配结果渲染component,并通过React context API将location、history对象注入到子组件。

2.2 StaticRouter

服务端渲染时页面是静态的,没有state,不能通过state改变去触发子组件更新。在服务端是根据req.url来渲染页面的,其基本使用方式如下:

import http from "http";
import React from "react";
import ReactDOMServer from "react-dom/server";
import { StaticRouter } from "react-router-dom";

import App from "./App.js";

http
  .createServer((req, res) => {
    const context = {};

    const html = ReactDOMServer.renderToString(
      
        
      
    );
        //重定向时触发
    if (context.url) {
      res.writeHead(context.status, {
        Location: context.url
      });
      res.end();
    } else {
      res.write(`
      
      
${html}
`); res.end(); } }) .listen(3000);

​ 在中没有使用history库了,而是创建了一个简单的history对象,其对应history库创建的history对象,但是其中的方法大多数为空的,例如:

handleListen = () => {};

只是为了将history传递时不报错。其中的push和replace方法是有效的,调用时会给context.url、context.location赋值。如上所示,但context.url有值时会重定向。

由于内部会

return ;

而statusContext属性在客户端渲染时不存在,可以通过这个条件去增加返回码:

 {
        if (staticContext) staticContext.status = status;
        // Redirect会调用push或replace
        return ;
      }}
    />

2.3 静态路由 React Router Config

import { renderRoutes } from "react-router-config";

const routes = [
  {
    component: Root,
    routes: [
      {
        path: "/",
        exact: true,
        component: Home
      },
      {
        path: "/child/:id",
        component: Child,
        routes: [
          {
            path: "/child/:id/grand-child",
            component: GrandChild
          }
        ]
      }
    ]
  }
];

const Root = ({ route }) => (
  

Root

{/* child routes won't render without this */} {renderRoutes(route.routes)}
); const Home = ({ route }) => (

Home

); const Child = ({ route }) => (

Child

{/* child routes won't render without this */} {renderRoutes(route.routes, { someProp: "these extra props are optional" })}
); const GrandChild = ({ someProp }) => (

Grand Child

{someProp}
); //renderRoutes方法对routes进行map生成 ReactDOM.render( {/* kick it all off with the root route */} {renderRoutes(routes)} , document.getElementById("root") );

3. Universal Router库

Universal Router是一个轻量化的静态路由库,可以使用在客户端和服务端。

client端的处理:

  1. 引入history库,通过history.location获得当前location并进行初始渲染。
  2. 调用history.listen监听url变化,url变化时触发重新渲染函数。
  3. 渲染函数中首先得到location.pathname,调用router.resolve({pathname})得到匹配的route,最后调用render方法进行渲染。

server端的处理:

  1. 服务端没有url状态的变化,可以直接从req.path的的得到路由信息
  2. 调用router.resolve({pathname})得到匹配的route,最后调用render方法进行渲染。

路由配置代码的基本结构:

const routes = [
  { path: '/one', action: () => '

Page One

' }, { path: '/two', action: () => '

Page Two

' }, { path: '(.*)', action: () => '

Not Found

' } ] //context this.context = { router: this, ...options.context } const router = new UniversalRouter(routes, {context,resolveRoute}) //resolve的参数pathnameOrContext // const context = { // ...this.context, // ...(typeof pathnameOrContext === 'string' // ? { pathname: pathnameOrContext } // : pathnameOrContext), // } router.resolve({ pathname: '/one' }).then(result => { document.body.innerHTML = result // renders:

Page One

})
  • 首先通过routes定义静态路由,path属性是必须的,action是resolve时默认的调用函数

     function resolveRoute(context, params) {
       if (typeof context.route.action === 'function') {
         return context.route.action(context, params)
       }
       return undefined
     }
  • 生成router实例,此时可以通过resolveRoute option定义router.resolve时的逻辑,通过context添加自定义的方法和属性。
  • 调用router.resolve去匹配pathname,该函数的参数都会加到context属性上,函数内部返回resolveRoute(context, params)的返回值。

权限管理:

  • context对象上有next方法,调用context.next()会遍历resolve其子路由,调用context.next(true)会遍历resolve所有剩余路由。
  • resolve得到的返回值为undefined时将会尝试匹配其子路由,得到的返回值为null时将会尝试匹配其兄弟路由
const middlewareRoute = {
  path: '/admin',
  action(context) {
    if (!context.user) {
      return null // route does not match (skip all /admin* routes)
    }
    if (context.user.role !== 'Admin') {
      return 'Access denied!' // return a page (for any /admin* urls)
    }
    return undefined // or `return context.next()` - try to match child routes
  },
  children: [/* admin routes here */],
}

你可能感兴趣的:(react-router4,javascript)