React 全家桶

一、create-react-app 脚手架

对于现在比较流行的三大框架都有属于自己的脚手架(目前这些脚手架都是使用node编写的,并且都是基于webpack的):

  • Vue的脚手架:vue-cli
  • Angular的脚手架:angular-cli
  • React的脚手架:create-react-app

它们的作用都是帮助我们生成一个通用的目录结构,并且已经将我们所需的工程环境配置好。

1.1 创建项目并启动

  1. 全局安装
    第一步,全局安装:npm i -g create-react-app
    第二步,切换到想创项目的目录,使用命令:create-react-app hello-react
    第三步,进入项目文件夹:cd hello-react
    第四步,启动项目:npm start
  2. npx
    npx create-react-app react_app
    cd react_app
    npm run start

注意:项目名称不能使用大写字母

1.2 项目结构

React 全家桶_第1张图片

1.3 创建TS项目

create-react-app --template typescript

npx create-react-app --template typescript

1.4 了解PWA

全称Progressive Web App,即渐进式WEB应用

  • 一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用
  • 随后添加上 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能
  • 这种Web存在的形式,称之为是 Web App

解决问题

  • 可以添加至主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏
  • 实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能
  • 实现了消息推送
  • 等等一系列类似于Native App相关的功能

二、AntDesign 库的使用

antd 的 JS 代码默认支持基于 ES modules 的 tree shaking,对于 js 部分,直接引入 import { Button } from 'antd' 就会有按需加载的效果

2.1 在 create-react-app 中使用

// 1. 安装
npm i antd

// 2. 修改 src/index.js,引入 antd/dist/antd.css
import 'antd/dist/antd.css'

// 3. 使用
import React from 'react'
import { Button } from 'antd'
import './App.css'

const App = () => (
  <div className="App">
    <Button type="primary">Button</Button>
  </div>
)

export default App

2.2 craco.config.js 配置

// 安装
npm install @craco/craco

/* package.json */
"scripts": {
-   "start": "react-scripts start",
-   "build": "react-scripts build",
-   "test": "react-scripts test",
+   "start": "craco start",
+   "build": "craco build",
+   "test": "craco test",
}

2.2.1 修改 antd 主题色

  1. 安装 craco-less
    npm install craco-less

  2. craco.config.js 配置

    const CracoLessPlugin = require('craco-less')
    module.exports = {
      plugins: [
        {
          plugin: CracoLessPlugin,
          options: {
            lessLoaderOptions: {
              lessOptions: {
                modifyVars: { '@primary-color': '#1DA57A' },
                javascriptEnabled: true
              }
            }
          }
        }
      ]
    }
    
  3. index.js 引入
    import 'antd/dist/antd.less'

2.2.2 配置别名

const path = require('path')
const resolve = dir => path.resolve(__dirname, dir)

module.exports = {
  webpack: {
    alias: {
      '@': resolve('src')
    }
  }
}

三、react-router

官网

3.1 基本使用

React Router的版本4开始,路由不再集中在一个包中进行管理了:

  • react-router是router的核心部分代码
  • react-router-dom是用于浏览器的
  • react-router-native是用于原生应用的

安装react-router:

  • 安装react-router-dom会自动帮助我们安装react-router的依赖
    npm install react-router-dom

React 全家桶_第2张图片

3.2 路由组件

3.2.1

相当于 vue 的 router-link,渲染成 a 标签。

区别:
NavLink 比 Link 拥有更多的属性,如:exact、className、activeClassName…

<NavLink className="list-group-item" to="/home">Home</NavLink>    

/* 封装 */
// MyNavLink 组件
import React from 'react'
import { NavLink } from 'react-router-dom'

// 传过来的 body 内容也在 this.props.children 中
return <NavLink className="list-group-item" activeClassName="linkActive" {...this.props} />

// 使用的组件
<MyNavLink to="/home">Home</MyNavLink>
<MyNavLink to="/about">About</MyNavLink>

3.2.2

可切换的路由组件,里面只显示一个路由组件

// v5 版本及之前
<Switch>
  <Route path="/about" component={About} />
  <Route path="/home" component={Home} />
  <Redirect to="/about" />
</Switch>

// v6 版本
<Routes>
  <Route path="/about" component={About} />
  <Route path="/home" component={Home} />
  <Redirect to="/about" />
</Routes>

注意: v6 版本已移除 Switch,改用 Routes

3.2.3

重定向组件

3.2.4

路由渲染组件

import About from './About'

// v5 版本及之前
<Route path="/about" component={About} />

// v6 版本
<Route path="/about" element={<About />} />
3.2.5

相当于 vue 的 router-view,BrowserRouter 是 history 模式,HashRouter 是 hash 模式

区别:

  1. BrowserRouter 用的是H5的 history API,不兼容 IE9及以下版本;HashRouter 用的是 URL 的哈希值
  2. BrowserRouter 对路由的 state 参数没有任何影响,因为 state 保存在 history 对象中;HashRouter 刷新后会导致 state 参数丢失
  3. HashRouter 可以解决一些路径错误相关的问题,如:样式丢失

3.2.6

向外暴露 withRouter 包装产生的组件(跟 connect 类似),让非路由组件可以访问到路由组件的 API,内部向组件传递路由组件特有的属性:history/location/match

class NavFooter extends React.Component {
  render() {
    const navList = router 
    // 当前请求的路径
    const { pathname } = this.props.location
    return (
      <div>{ pathname }</div>
    )
  }
}
export default withRouter(NavFooter)

注意: v6 版本已移除

3.3 嵌套路由

// 父级组件
import React, { PureComponent } from 'react'
import { NavLink, Route, Routes } from 'react-router-dom'

import About from './About'
import Profile from './Profile'

function AboutOne() {
  return <div>1</div>
}

function History() {
  return <div>1</div>
}

export default class RouterPage extends PureComponent {
  render() {
    return (
      <> 
        <NavLink to="/about">关于</NavLink>
        <NavLink to="/profile">我的</NavLink>
        <Routes>
          <Route path="/about/*" element={<About />}>
            {/* 此处不能再用 Routes 包裹 */}
            <Route exact path="" element={<AboutOne />} />
            <Route path="history" element={<History />} />
          </Route>
          <Route path="/profile" element={<Profile />} />
        </Routes>
      </>
    )
  }
}
// About.js
import React, { PureComponent } from 'react'
import { NavLink, Outlet } from 'react-router-dom'

export default class AboutPage extends PureComponent {
  render() {
    return (
      <>
        <div>
          <NavLink to="/about">关于</NavLink>
          <NavLink to="/about/history">历史</NavLink>
        </div>
        <Outlet />
      </>
    )
  }
}

3.4 路由组件三大属性(v5 及之前版本)

3.4.1 match

  1. params: {} // 接收动态路由传递的参数
  2. path: “” // 路由参数渲染前路径,不包括 ?后,如 /home/:id/:title
  3. url: “” // 路由参数渲染后路径,不包括 ?后,如 /home/1/标题
/* 动态路由传参 */
// 传数据的组件
<NavLink to={`/home/${1}/标题`}>Home</NavLink> 
<Route path="/home/:id/:title" component={Home} />

// 接收数据的组件
render() {
  const { id, title } = this.props.match.params 
}

3.4.2 location

  1. pathname: “” // 路由参数渲染后路径,不包括 ?后,如 /home/1/标题
  2. search: '“” // 路径 ?(包括)之后的字符串
  3. state: {} // 主要用来传数据
// 传数据的组件
<NavLink to='/home?name=sunny&age=18'>Home</NavLink> // 传的参数是公开的
<NavLink to={{ 
  pathname: '/home', 
  state: {name: 'sunny', age: 18},
  search: '?name=sunny&age=18'
}}>Home</NavLink> // 传的参数是加密的

<Route path="/home" component={Home} />

// 接收数据的组件
import qs from 'querystring'
/*
 * qs.stringify(obj) 把对象转为路径
 * qs.parse(str) 把路径转为对象
 */

render() {
  const { search } = this.props.location
  const { name, age } = qs.parse(search.slice(1))
  const { name } = this.props.location.state
}

3.4.3 history

相当于 vue 的 $router,常用五个属性如下:

  1. push(path, [state]) // 路由跳转,this.props.history.push({path: ‘/home’, query: {name: ‘zs’}})
  2. replace(path, [state]) // 路由跳转,替换当前页
  3. go(n)
  4. goBack()
  5. goForward()

3.5 react-router-config

路由管理
npm instaall react-router-config

// router > index.js 定义
import Home from '../pages/home'
import About, { AboutHisotry, AboutCulture, AboutContact, AboutJoin } from '../pages/about'
import Profile from '../pages/profile'

const routes = [
  {
    path: '/',
    exact: true,
    component: Home
  },
  {
    path: '/about',
    component: About,
    routes: [
      {
        path: '/about',
        exact: true,
        component: AboutHisotry
      },
      {
        path: '/about/culture',
        component: AboutCulture
      },
      {
        path: '/about/contact',
        component: AboutContact
      },
      {
        path: '/about/join',
        component: AboutJoin
      }
    ]
  },
  {
    path: '/profile',
    component: Profile
  }
]

export default routes
// 使用
import { renderRoutes } from 'react-router-config'
import routes from '@/router'

// 一级路由出口
{renderRoutes(routes)}

// 二级路由出口
{renderRoutes(this.props.route.routes)}

四、redux

4.1 JavaScript纯函数

4.1.1 定义

  1. 确定的输入,一定会产生确定的输出
  2. 函数在执行过程中,不能产生副作用

4.1.2 分析

React 全家桶_第3张图片

为什么纯函数在函数式编程中非常重要呢?

  • 可以安心的写和安心的用
  • 写的时候保证了函数的纯度,只是实现自己的业务逻辑即可,不需要关心传入的内容或者依赖其他的外部变量
  • 在用的时候,确定输入内容不会被任意篡改,并且确定的输入,一定会有确定的输出

4.2 Redux 的三大核心概念

4.2.1 store

单一数据源

  • 整个应用程序的state被存储在一颗object tree中,并且这个object tree只存储在一个 store 中
  • Redux并没有强制让我们不能创建多个Store,但是那样做并不利于数据的维护
  • 单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改

4.2.2 action

改变 state 的唯一途径

  • 唯一修改state的方法一定是触发action,不要试图在其他地方通过任何的方式来修改state
  • 保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心race condition(竟态)的问题

4.2.3 reducer

加工 state,返回新的 state

  • 通过reducer将 旧state和 actions联系在一起,并且返回一个新的state
  • 随着应用程序的复杂度增加,可以将reducer拆分成多个小的reducers,分别操作不同state tree的一部分
  • 所有的reducer都应该是纯函数,不能产生任何的副作用
import { createStore } from 'redux'

const initState = {
  count: 0
}

// 创建 reducer
function reducer(state = initState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return {...state, count: state.count + 1}
    default:
      return state
  }
}

// 创建 store,传入一个 reducer
const store = createStore(reducer)

// 分发 action 前订阅 store 发生变化
store.subscribe(() => {
  console.log(store.getState().count) // 1
})

// 分发 action
store.dispatch({ type: 'INCREMENT' })

4.3 Redux 结构划分

一般放4个文件

// src > store 文件夹下

// 1. index.js
import { createStore } from 'redux'
import reducer from './reducer'

const store = createStore(reducer)
export default store

// 2. constants.js 定义 action 常量
export const ADD_NUMBER = 'ADD_NUMBER'

// 3. reducer.js
import { ADD_NUMBER } from './constants'
const initState = {
  count: 0
}

export default function  reducer(state = initState, action) {
  switch (action.type) {
    case ADD_NUMBER:
      return {...state, count: state.count + action.num}
    default:
      return state
  }
}

// 4. actionCreators.js
import { ADD_NUMBER } from './constants'

export const addNumber = (num) => ({
  type: ADD_NUMBER,
  num
})

使用

import store from '@/store'
import { addNumber } from '@/store/actionCreators'

store.subscribe(() => {
  console.log(store.getState())
})

store.dispatch(addNumber(5))

4.4 react-redux 简化使用 redux

用来简化 react 应用中使用 redux 的一个插件

4.4.1 组件两大类

  1. UI 组件
    a. 只负责 UI 的呈现,不带有任何业务逻辑
    b. 通过 props 接收数据(一般数据和函数)
    c. 不使用任何 Redux 的 API
    d. 一般保存在 components 文件夹下
  2. 容器组件
    a. 负责管理数据和业务逻辑,不负责 UI 的呈现
    b. 使用 Redux 的 API
    c. 一般保存在 containers 文件夹下

4.4.2 相关 API

  1. Provider
    让所有组件都可以得到 state 数据

    // index.js
    import { Provider } from 'react-redux'
    import store from '@/store'
    
    ReactDOM.render(
      (
        <Provider store={store}>
          <App />
        </Provider>
      ),
      document.getElementById('root')
    )
    
  2. connect(mapStateToprops, mapDispatchToProps)(Counter)

    • mapStateToprops(function):将外部的数据(即 state 对象)转换为 UI 组件的标签属性
    • mapDispatchToProps(obiect): 将分发 action 的函数转换为 UI 组件的标签属性
    • Counter: UI 组件
    // 1.引入连接函数
    import { connect } from 'react-redux'
    
    // 2.引入 action 函数
    import { increment, decrement } from '@/store/actionCreators'
    
    // 3.引入 UI 组件
    import Counter from './components/counter'
    
    // 4.向外暴露连接App组件的包装组件
    export default connect(
      state => ({count: state}),
      { increment, decrement }
    )(Counter)
    

4.4.3 自定义connect函数

// context.js
import { createContext } from 'react'

export const StoreContext = createContext()
// connect.js
import React, { PureComponent } from 'react'
import { StoreContext } from './context'

export function connect(mapStateToProps, mapDispatchToProps) {
  return function(WrappedComponent) {
    return class extends PureComponent {
      static contextType = StoreContext

      state = {
        storeState: mapStateToProps(this.context.getState())
      }
      
      componentDidMount() {
        this.unsubscribe = this.context.subscribe(() => {
          this.setState({
            storeState: mapStateToProps(this.context.getState())
          })
        })
      }

      componentWillUnmount() {
        this.unsubscribe()
      }

      render() {
        return <WrappedComponent 
                 {...this.props}
                 {...mapStateToProps(this.context.getState())}
                 {...mapDispatchToProps(this.context.dispatch)}
                />
      }
    }
  }
}
// App.js
import React, { PureComponent } from 'react'
import { connect } from '@/utils/connect'

class Home extends PureComponent {
  render() {
    return (
      <>
        <div>Home State: {this.props.count}</div>
        <hr />
        <button onClick={() => this.props.incrementAction()}>+1</button>
      </>
    )
  }
}

const mapStateToProps = state => ({
  count: state.count
})

const mapDispatchToProps = dispatch => ({
  incrementAction() {
    dispatch({ type: 'increment' })
  }
})

export default connect(mapStateToProps, mapDispatchToProps)(Home)
// index.js
import store from '@/store'
import { StoreContext } from '@/utils/context'

ReactDOM.render(
  <StoreContext.Provider value={store}>
    <App />
  </StoreContext.Provider>,
  document.getElementById('root')
)

4.4.4 hooks

import React, { memo, useEffect } from 'react'
import { useDispatch, useSelector, shallowEqual } from 'react-redux'

import { getTopBannerAction } from './store/actionCreators'

// hook 形式
export default memo(function DiscoverRecommend() {
  // shallowEqual 的作用是分析引用的redux数据进行浅层比较, 性能优化
  const {topBanners} = useSelector(state => ({
    topBanners: state.recommend.topBanners
  }), shallowEqual)
  console.log(topBanners);

  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(getTopBannerAction())
  }, [dispatch])


  return (
    <div>
      DiscoverRecommend
    </div>
  )
})

4.5 redux-thunk 处理 redux 异步操作

默认不支持异步操作(dispatch 只支持传入对象),需安装 redux-thunk 中间件(可以让dispatch支持传入函数)

// store.js
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducer.js'

const store = createStore(reducer, applyMiddleware(thunk))

export default store

第一种写法:

// actionCreators.js
export const incrementAction = dispatch => {
  setTimeout(() => {
    dispatch({ type: 'INCREMENT' })
  }, 300)
}

// App.js
import { incrementAction } from '@/store/actionCreators'

const mapStateToProps = state => ({
  count: state.count
})

const mapDispatchToProps = dispatch => ({
  incrementAction() => {
    dispatch(incrementAction)
  }
})

export default connect(mapStateToProps, mapDispatchToProps)(Home)

第二种写法(推荐):

// actionCreators.js
export const incrementAction = (num) => {
  return dispatch => {
    setTimeout(() => {
      dispatch({ type: 'INCREMENT', num })
    }, 300)
  }
}

export const decrementAction = (num) => ({
  type: 'DECREMENT',
  num
})

// App.js
import { incrementAction, decrementAction } from '@/store/actionCreators'

export default connect(state => ({
  count: state.count
}), 
{ incrementAction, decrementAction })(Home)

4.6 redux-saga 处理 redux 异步操作

npm install redux-saga

// store.js
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'

import reducer from './reducer'
import saga from './saga'

// 创建sagaMiddleware中间件
const sagaMiddleware = createSagaMiddleware()

const store = createStore(reducer, composeEnhancers(applyMiddleware(sagaMiddleware)))
sagaMiddleware.run(saga)

export default store
// actionCreators.js
export const getList = () => ({
  type: 'GETLIST'
})

export const changeBannersAction = (banners) => ({
  type: 'CHANGE_BANNERS',
  banners
})

export const changeRecommendAction = (recommends) => ({
  type: 'CHANGE_RECOMMEND',
  recommends
})
// saga.js
import { takeEvery, put, all } from 'redux-saga/effects'
import {
  changeBannersAction,
  changeRecommendAction
} from './actionCreators'

function* fetchHomeMultidata() {
  const res = yield fetch.get('http://127.0.0.1:8000/home/multidata')
  const banners = res.data.data.banner.list
  const recommends = res.data.data.recommend.list
  // yield put(changeBannersAction(banners))
  // yield put(changeRecommendAction(recommends))
  yield all([
    yield put(changeBannersAction(banners)),
    yield put(changeRecommendAction(recommends))
  ])
}

function* mySaga() {
  // takeLatest takeEvery区别:
  // takeLatest: 依次只能监听一个对应的action
  // takeEvery: 每一个都会被执行
  yield all([
    takeEvery('GETLIST', fetchHomeMultidata),
    // takeLatest(ADD_NUMBER, fetchHomeMultidata),
  ])
}

export default mySaga

4.7 使用 redux 调试工具

需先安装 chrome 浏览器插件(Redux Devtools)

4.7.1 下载工具依赖包

npm install --save-dev redux-devtools-extension

使用

// store.js
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import reducer from './reducer'

const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)))

export default store

4.7.2 直接使用

// store.js
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducer'

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))

export default store

4.8 核心 API

4.8.1 createStore(reducer, [preloadedState])

创建包含指定 reducer 的 store 对象

参数

  1. reducer (Function): 接收两个参数,分别是当前的 state 树和要处理的 action,返回新的 state 树
  2. [preloadedState] (any): 初始时的 state

返回值
(Store): 保存了应用所有 state 的对象。改变 state 的惟一方法是 dispatch action。你也可以 subscribe 监听 state 的变化,然后更新 UI

import { createStore } from 'redux'

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return state.concat([action.text])
    default:
      return state
  }
}

const store = createStore(todos, ['Use Redux'])

store.dispatch({
  type: 'ADD_TODO',
  text: 'Read the docs'
})

console.log(store.getState())
// [ 'Use Redux', 'Read the docs' ]

4.8.2 subscribe(listener)

添加一个变化监听器。更新 UI 视图

// 订阅
const unsubscribe = store.subscribe(() => {
  console.log(store.getState())
})

// 取消订阅
unsubscribe()

4.8.3 applyMiddleware(…middlewares)

应用中间件扩展 Redux

4.8.4 combineReducers(reducers)

整合 reducer,把多个 reducer 函数整合成一个对象

// reducers.js
import { combineReducers } from 'redux'

function counter(state = 0, action) {
  console.log('counter', state, action)
}

function comments(state = [], action) {
  console.log('comments', state, action)
}

export default combineReducers({
  counter,
  comments
})

// store.js
import reducers from './reducers'

export default createStore(
  reducers,
  composeWithDevTools(applyMiddleware(thunk)) // 应用上异步中间件
)

4.9 自定义实现 createStore

function createStore(reducer) {
  let state = reducer(undefined, {}) // 初始化state
  const list = []
  
  function subscribe(callback) { // 收集回调函数
    list.push(callback)
  }
  
  function dispatch(action) {
    state = reducer(state, action)
    for (let i in list) { // 执行回调函数
      list[i] && list[i]()
    }
  }
  
  function getState() {
    return state
  }

  return {
    subscribe,
    dispatch,
    getState
  }
}

五、React SSR

5.1 SSR和同构

5.1.1 SSR

  • SSR(Server Side Rendering,服务端渲染),指的是页面在服务器端已经生成了完成的HTML页面结构,不需要浏览器解析
  • 对应的是CSR(Client Side Rendering,客户端渲染),我们开发的SPA页面通常依赖的就是客户端渲染
  • 早期的服务端渲染包括PHP、JSP、ASP等方式,但是在目前前后端分离的开发模式下,前端开发人员不太可能再去学习PHP、JSP等技术来开发网页
  • 不过我们可以借助于Node来帮助我们执行JavaScript代码,提前完成页面的渲染

5.1.2 同构

  • 一套代码既可以在服务端运行又可以在客户端运行,这就是同构应用
  • 同构是一种SSR的形态,是现代SSR的一种表现形式
  • 当用户发出请求时,先在服务器通过SSR渲染出首页的内容
  • 但是对应的代码同样可以在客户端被执行
  • 执行的目的包括事件绑定等以及其他页面切换时也可以在客户端被渲染

5.2 使用React SSR

  • 使用React SSR主要有两种方式:

    • 方式一:手动搭建一个SSR框架;
    • 方式二:使用已经成熟的SSR框架:Next.js
  • 安装Next.js框架的脚手架:
    npm install –g create-next-app

  • 创建Next.js项目
    create-next-app next-demo

5.3 路由

Next.js默认已经给我们配置好了路由映射关系:

  • 路径和组件的映射关系
  • 这个映射关系就是在pages中配置相关的组件都会自动生成对应的路径
  • 默认page/index.js是页面的默认路径

页面跳转
React 全家桶_第4张图片

5.4 样式

方式一:全局样式引入
方式二:module.css
方式三:默认集成styled-jsx
React 全家桶_第5张图片

方式四:其他css in js方案,比如styled-components

  • 引入相关的依赖
  • 创建和编辑 .babelrc文件
    npm install styled-components
    npm install -D babel-plugin-styled-components
    React 全家桶_第6张图片

5.5 路由的嵌套及传参

路由的嵌套(子路由):

  • 文件夹的嵌套,最后就可以形成子路由

路由的传参:

  • Next.js中无法通过 /user/:id的方式传递参数
  • 只能通过 /user?id=123的方式来传递参数

传递参数有两种办法:

  1. Link中的路径
  2. Router.push(pathname, query)

React 全家桶_第7张图片
React 全家桶_第8张图片

六、Umi

6.1 脚手架

# 1.通过官方工具创建项目
npx @umijs/create-umi-app

# 2.安装依赖
npm install

# 3.启动项目
npm start

6.2 路由

umi 会根据 pages 目录自动生成路由配置。需要注释.umirc.js,routes相关,否则自动配置不生效

6.2.1 基础路由

React 全家桶_第9张图片

6.2.2 重定向

// pages/index.tsx 重定向到 film
import React from 'react' 
import { Redirect } from 'umi' 
export default () => <Redirect to="/film"/>
// 在 film 中的 _layout.tsx
import { Redirect } from 'umi' 
export default function Film(props) {
  const { pathname } = props.location
  if (pathname=== '/film' || pathname=== '/film/') {
    return <Redirect to="/film/nowplaying" /> 
  }
  return <div>{ props.children }</div>
}

6.2.3 嵌套路由

React 全家桶_第10张图片

6.2.4 动态路由

React 全家桶_第11张图片

6.2.5 路由拦截

// center.tsx 
import React from 'react' 
const Center = () => { 
  return <div> <h1>center</h1> </div>
}

Center.wrappers = ['@/wrappers/auth'] 
export default Center
// wrappers/auth.tsx
import React from 'react' 
import { Redirect } from 'umi' 
export default (props:any) => { 
  const isLogin = localStorage.getItem('token') 
  if (isLogin) { 
    return <div>{ props.children }</div>
  } else { 
    return <Redirect to="/login" />
  } 
}

6.2.6 hash 模式

// .umirc.js 

export default { 
  history: { 
    type: 'hash' 
  } 
}

6.3 dva 集成

React 全家桶_第12张图片

6.3.1 同步

// models/tabbar.js 
export default { 
  // 命名空间 
  namespace: 'tabbar', 
  state: { 
    isShow: true
  },
  // 处理state--同步 
  reducers: { 
    // reducer 简写, type类型是show的时候自动处理 
    show(state, {payload}) { 
      return { ...state, ...payload } 
    }
  },
  // yield表示后面的方法执行完以后 call表示调用一个api接口 
  // put表示一个派发
  effects: { 
    *showEffect(payload, {put}) { 
      yield put({ type:'show', payload:{ isShow:true } }) 
    }
  }
}
import { connect, useDispatch } from 'dva' 
function Detail(props) { 
  const dispatch = useDispatch() 
  useEffect(() => { 
    dispatch({ type: "tabbar/showEffect" // 命名空间tabbar }) 
  }, [])
  return <div> Detail </div> 
}

export default connect(state => state.tabbar)(Detail)

6.3.2 异步

import { getNowplaying } from '@/util/getNowplaying'

export default { 
  // 命名空间 
  namespace: 'list', 
  state: { 
    list: []
  },
  // 处理state--同步 
  reducers: { 
    changeList(state, {payload}) { 
      return { ...state, ...payload } 
    }
  },
  effects: { 
    *getListEffect(payload, {put}) {
      const res = yield call(getNowplaying, 'test-by-kerwin')
      yield put({ type:'changeList', payload:{ list: res } }) 
    }
  }
}
// util/getNowplaying
import { fetch } from 'dva' // dva 内置的 fetch

export async function getNowplaying(value) { 
  console.log(value) // value 是call的第二个参数 
  const res = await fetch('api') 
  const result = await res.json() 
  return result.coming 
}

你可能感兴趣的:(#,React,学习,react.js,javascript,前端)