Возьмите вас за руку, чтобы создать систему управления фоном React16 + Router + Redux-saga + Antd.

React.js

Реагировать на проект

содержание

один,стек технологий

два,Установка проекта

три,Архитектура проекта

Четыре,тестовое задание

Пятерки,развертывать

шесть,Вход в дом

Семь,Меню навигации

Восемь,Совместное использование состояния Redux-saga

9,И конфигурация

десять,конец

11,Ссылка на гитхаб


1. Стек технологий

Подробнее, пожалуйста, обратитесь кpackage.json

  • Create-react-app 3.0+
  • Yarn
  • React 16.9.0
  • Redux-saga
  • React Router
  • Less
  • Библиотека запросов Axios
  • Webpack 4.0+
  • ES6 + Babel
  • Antd @3.23.6

项目说明:

1. Маршрутизация ленивой загрузки

2. Неверный маршрут соответствует странице 404

3. Запрос API инкапсулирует класс инструмента Axios

4. redux-saga обрабатывает асинхронные запросы

5. полоса загрузки nprogress

6. Аутентификация маршрутизации

7. Совместное использование состояния в режиме декоратора, перенос формы

8. Добавьте функцию перехода маршрутизации в Redux

7. Интерфейс представляет собой API java-сервера, и он пока не будет извлекаться, поэтому проект не может нормально войти в систему. соответствующее требование. Наконец, redux-saga действительно ароматная~~~


2. Установка проекта

В этом проекте используется пакет зависимостей для управления пряжей, и пряжа должна быть установлена.

yarn install //安装依赖

yarn start //运行

3. Структура проекта

⊙ Структура каталогов

.
├─ config/            # Webpack 配置目录
├─ public/             # 模板文件
├─ dist/             # build 生成的生产环境下的项目
├─ scripts/             # Webpack环境变量配置
├─ src/              # 源码目录(开发都在这里进行)
│   ├─ assets/         # 放置需要经由 Webpack 处理的静态文件
│   ├─ components/     # 组件
│   │   ├─ Layout/       # 全局布局
│   │   ├─ PrivateRoute/ # 路由守卫
│   ├─ store/            # Redux-sagas
│   │   ├─ actions/      # (Actions)
│   │   ├─ reducers/     # (Reducers)
│   │   ├─ sagas/        # (Sagas)
│   │   ├─ index.js      # (Store文件管理)
│   ├── router/          # 路由(ROUTE)
│   ├── service/         # 服务(SERVICE,统一Api管理)
│   ├── utils/           # 工具库
│   ├── pages/           # 视图页(pages)
│   ├── index.js         # 启动文件
│   ├── App.js           # 主入口页
├── .gitignore       # (配置)需被 Git 忽略的文件(夹)
├── package.json      

4. Тест

Инструмент тестирования еще не добавлен


5. Развертывание

yarn build

После сборки, если вы хотите открыть его в локальной онлайн-среде, рекомендуется сначала установить локальный сервер http-server.

npm install http-server -g

После успешной установки запустите непосредственно в папке сборки, чтобы получить доступhttp-serverкоманда, чтобы открыть локальную среду службы. Вы также можете настроить порт самостоятельно. Конкретные команды см.http-server

Проблема с путем после упаковки приводит к тому, что страница становится пустой

После запуска локального сервера в каталоге сборки возможно, что проект пустой и ресурсы не могут быть загружены, просто настройте его в package.jsonhomepageсвойства в порядке

//package.json 文件增加配置
"homepage": ".",

6. Вход в дом

Файл записи в основном определяет страницу маршрутизации, поскольку этот проект представляет собой одностраничное приложение, поэтому в основной записи нужно только настроить маршрутизацию из трех модулей, корневой адрес маршрутизации./соответствовать<IndexLayout />,loginсоответствовать<Login />,404соответствовать<ErrorPage />

Чтобы маршрут загружался лениво, при импорте маршрута можно обернуть слой Компонента асинхронным компонентом, а чтобы найти соответствующее имя файла при загрузке, можно установить/* webpackChunkName: "name" */ОК, следующим образом

// 该文件为实现类似github页面加载的那个加载条
import LoadableComponent from '@/utils/LoadableComponent'
const Login = LoadableComponent(()=>import(/* webpackChunkName: "login" */ '@/pages/Login'))

Цитируется здесьHashRouterРежим маршрутизации, режим BrowserRouter требует фонового взаимодействия, в противном случае это вызовет пустую ошибку при обновлении текущего адреса маршрутизации во время упаковки.

входной файлApp.jsВыложи весь код

import React, { Component } from 'react'
import IndexLayout from '@/components/Layout/index'
import { connect } from 'react-redux';
import LoadableComponent from '@/utils/LoadableComponent'
import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom'

const Login = LoadableComponent(()=>import(/* webpackChunkName: "login" */ '@/pages/Login'))
const ErrorPage = LoadableComponent(()=>import(/* webpackChunkName: "errorPage" */ '@/pages/ErrorPage'))

//装饰器模式链接Redux数据,省略很多复杂代码,真香。注意:不要的对象可以传个null
@connect(
    state => ({
        id_token: state.loginReducer.id_token
    })
)
class App extends Component {
    render() {
        return (
            <Router>
                <Switch>
                    //此处重定向的地址为登录后的首页面地址
                    <Route exact path="/" render={ () => <Redirect to="/apply" push /> } />
                    
                    //此处404页面只是声明路由地址,暂未匹配路由
                    <Route path="/404" component={ ErrorPage } />
                    
                    //路由鉴权,如果没有id_token则跳转到登录页
                    <Route path="/login" render={() => {
                        return this.props.id_token ?  <Redirect to="/" /> : <Login />
                    }} />
                    
                    //登录后的主模板组件
                    <Route render={ () => <IndexLayout /> } />
                </Switch>
            </Router>
        )
    }
}

export default App


6. Меню навигации

В проекте левая и правая верстка, нет хедера, футера. Интерфейс относительно инновационный...

import React, { Component } from 'react'
import ContentMain from '@/components/Layout/ContentMain' //主内容组件
import SliderNav from  '@/components/Layout/SliderNav' //菜单栏组件
import { Layout } from 'antd'

const { Content, Sider } = Layout;

class IndexLayout extends Component {

    render() {
        return (
            <Layout>
                <Sider
                    collapsible
                    trigger={null}
                >
                    <SliderNav/>
                </Sider>
                <Layout>
                    <Content style={{background: '#f7f7f7'}}>
                        <ContentMain/>
                    </Content>
                </Layout>
            </Layout>
        )
    }
}

export default IndexLayout

Код строки меню не публикуется один за другим, содержимое немного длинное, а столбец маршрутизации строки меню имеет конфигурацию ввода подменю, если соответствующий массив маршрутизации настроен в соответствии с форматом. все导航菜单布局существуетComponentsбиблиотека компонентовLayoutкаталог папок. Вот основная информация о конфигурации маршрута панели навигации. Обратите внимание, что конфигурация маршрутизации здесь — это не та же информация, что и в предыдущей конфигурации маршрутизации, первая — это общий адрес маршрутизации, а маршрутизация — правильная.Contentвся маршрутная информация.

import React, { Component } from 'react'
import { withRouter, Switch, Redirect, Route } from 'react-router-dom'
import LoadableComponent from '@/utils/LoadableComponent'
//路由鉴权组件,包裹所有`Content`的页面,如果token失效,则跳回`Login页面`
import PrivateRoute from '@/components/PrivateRoute'

const Apply = LoadableComponent(()=>import(/* webpackChunkName: "apply" */ '@/pages/Apply'))
const Case = LoadableComponent(()=>import(/* webpackChunkName: "case" */ '@/pages/Case'))

@withRouter
class ContentMain extends Component {
    render () {
        return (
            <div style={{padding: '20px 32px'}}>
                <Switch>
                    <PrivateRoute exact path='/apply' component={ Apply }/>
                    <PrivateRoute exact path='/case' component={ Case }/>
                    
                    //404页面在这里匹配路由,就能正确匹配错误路由了
                    <Route render={ () => <Redirect to="/404" /> } />
                    <Redirect exact from='/' to='/apply'/>
                </Switch>
            </div>
        )
    }
}

export default ContentMain


Восьмое, совместное использование состояния Redux-saga

при принятии решения об использованииRedux-sagaРаньше также считалось цитироватьRedux-thunkделать разделение состояния, в конце концовRedux-thunkНачать намного проще. ноRedux-thunkСинхронный и асинхронный код необходимо размещать в одном файле, если слишком много одностраничных интерфейсов, это приведет к спагетти-коду, который не способствует пониманию и сопровождению. Вот решил процитироватьRedux-saga. Посередине много ям, потому что редко встречается на GithubRedux-sagaСравнивая полные серии предметов, которые можно взять напрокат, каждый раз, когда вы застрянете, вы будете читать различные материалы. Далее будут перечислены проблемы, с которыми легко столкнуться в процессе разработки, чтобы друзья, ссылающиеся на этот проект, меньше наступали на ямы...

1. Сначала вставьте файл конфигурации магазина напрямую

import { createStore, applyMiddleware, compose } from 'redux';
import createSagaMiddleware from 'redux-saga';
import reducer from './reducers';
import sagas from './sagas'
import { routerMiddleware } from 'react-router-redux';

const sagaMiddleware = createSagaMiddleware();
const createHistory = require('history').createHashHistory;
const history = createHistory();   // 初始化history
const routerWare = routerMiddleware(history);
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware, routerWare));
const store = createStore(
	reducer,
	enhancer
)

sagaMiddleware.run(sagas)

export default store

представлен здесьsagasклассифицируются по цитируемости страницsagaфайл, чтобы избежать записи всех асинхронных запросов в один и тот же файл. Поскольку в этом проекте всего три модуля: страница входа, страница приложения и страница обращения, целесообразно разделить его на три модуля.

// saga模块化引入
import { fork, all } from 'redux-saga/effects'

// 异步逻辑
import { loginSagas } from './login'
import { applySagas } from './apply'
import { caseSagas } from './case'

// 单一进入点,一次启动所有Saga
export default function* rootSaga() {
    yield all([
        fork(loginSagas),
        fork(applySagas),
        fork(caseSagas)
    ])
}

redux-sagaЯ не буду подробно останавливаться на правильном использовании .Студенты, которые хотят цитировать, могут перейти к введению официальной документации. Или возьмите исходный код этого проекта, следуйте за тыквой и нарисуйте совок, и напишите его несколько раз, и вы будете знать больше. Тем не менее, посылка здесь заключается в том, что вы должны сначала понятьEs6的Generator函数.

специфическийredux-sagaАсинхронные ссылки используются во многих местах проекта, я исправлю это, когда будет времяredux-sagaИспользовать учебник.

Вот две цитатыredux-sagaлегкая яма

  • Маршрут перехода после завершения асинхронного запроса
  • Асинхронный запрос redux-saga выполняется только один раз, например интерфейс подкачки, независимо от того, как вы разделите страницу, она будет загружена только в первый раз.

① Маршрут перехода после завершения асинхронного запроса

reactа такжеvueСкачок маршрутизации немного непоследовательный,vueОн инкапсулирует всю информацию о маршрутизации, если вы ссылаетесьvue-router, вы можете ссылаться на переход маршрута по своему усмотрению. ноreactразные, вjs文件в случае цитированияreact-routerможно прямо цитировать<Link></Link>илиthis.props.history.push('/login')способ перейти маршрут. но цитируетсяredux-sagaПосле того, как состояние передано, обычно требуется перейти к информации о маршрутизации после завершения асинхронного запроса, но здесьthis.props.history.push('/login')Этот способ не действует. Если вы хотите переключаться между маршрутами при совместном использовании состояния, требуется дополнительная настройка.

1. Первая установкаhistoryа такжеreact-router-redux

yarn add history react-router-redux

2. Вstoreвнутри кавычек


import createSagaMiddleware from 'redux-saga';
import { routerMiddleware } from 'react-router-redux';

const sagaMiddleware = createSagaMiddleware();
const createHistory = require('history').createHashHistory;
const history = createHistory(); //初始化history
const routerWare = routerMiddleware(history);

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
//这里是包裹两个中间件,saga和状态共享路由的中间件,想要在saga中跳转页面,routerWare这个中间件是必不可少的!!!
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware, routerWare));

3. Вsagaприменение в

import { push } from 'react-router-redux';
function* login() {
  if (...) {// 登录成功,路由跳转
    yield put(push('/login')) //Generator指令跳转
  }
}

②Асинхронный запрос redux-saga выполняется только один раз

На самом деле основная причина этого в том, чтоGenerator指令незнакомый Следующий код должен быть добавлен к каждому методу генератораwhile(true){}, с несколькимиGeneratorметодyield all([])Монитор, чтобы можно было делать по одномуsagaПосле того, как запрос задачи закончится, приходите в следующий раз(例如分页)Для одного и того же запроса он будет прослушивать каждый раз снова, а затем продолжать вводитьGenerator函数внутри, так что интерфейс запрашивается не только один раз.

Этот крошечный маленький жук действительно заставил меня сильно страдать...

function * getSearchRequest() {
    while(true){ //保持监听连接
        const resData = yield take(types.GET_SEARCH_DATA);
        const response = yield call(seachData, resData.payload)
        yield put(getSearchDataSuccess(response))
    }
}

function * getDetailRequest() {
    while(true){
        const resData = yield take(types.GET_DRAFT_DETAIL_REQUEST);
        const response = yield call(searchDetail, resData.payload)
        yield put(getDetailSuccess(response));
    }
}

export function * caseSagas() {
    yield all([
        fork(getSearchRequest),
        fork(getDetailRequest)
    ]);
}


Девять, конфигурация Antd

Этот проект относится к библиотеке компонентов пользовательского интерфейса Antd. Конечно, он слишком раздут, чтобы упаковывать и загружать все компоненты одновременно.Предложение по конфигурации загрузки по запросу, данное на официальном сайте, находится в проекте.yarn run ejectПредыдущий явно не соответствует пользовательской конфигурации большинства онлайн-кодов, поэтому необходимо настроитьantdЗагрузка по требованию, но также необходимо настроить отдельно

Здесь я поставлю все конфигурации Babel напрямую и решу две проблемы знаний. antd загружается по запросу и настраивается в режиме декоратора.

Режим декоратора должен сначала установить зависимости, а затем настроить babel.

yarn add babel-plugin-transform-decorators-legacy

//package.json
"babel": {
    "plugins": [
      [
        "@babel/plugin-proposal-decorators", //引用@connect、@withRouter装饰器模式必须配置babel
        {
          "legacy": true
        }
      ],
      [
        "import", //antd按需加载配置
        {
          "libraryName": "antd",
          "libraryDirectory": "es",
          "style": true //这里如果设置为true的话则为自定义主题,否则就是加载全部antd css样式
        }
      ]
    ]
  }

как указано выше, еслиantdзагружать конфигурацию по запросуstyleсобственностьtrueЕсли да, то настройка пользовательской темы еще не закончена, продолжаем...

существуетwebpack.config.jsфайл,

// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
    const loaders = ...
    if (preProcessor) {
        let loader = {
            loader: require.resolve(preProcessor),
            options: {
                sourceMap: true,
            },
        }
        if (preProcessor === "less-loader") {
            //以下为antd的所有主题配色,更多的变量可访问
            //https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less
            loader.options.modifyVars = {
                'primary-color': '#C89F64', //主题颜色
                'link-color': '#1DA57A', //主题link颜色
                'border-radius-base': '2px'
            }
            loader.options.javascriptEnabled = true
        }
        loaders.push(
            {
                loader: require.resolve('resolve-url-loader'),
                options: {
                    sourceMap: isEnvProduction && shouldUseSourceMap,
                },
            },
            loader
        );
    }
    return loaders;
};

10. Концовка

Весь проект фактически разработан, жаль только, что API-запрос нельзя открыть, а можно только дать ссылку. Ниже я приложу исходный код Github, включая проект дизайна, не так сложно посмотреть исходный код с UI. Если вы столкнетесь с тем же модулем, на который можно сослаться, я буду очень рад~~~

Приложение: React - это полностью компонентная концепция разработки. Все является компонентом. При работе над этим проектом времени было слишком мало. Этот проект занял шесть или семь рабочих дней, не считая времени на тестирование. Так что связь кода все еще немного серьезна, простите меня ~ я добавлю ее позже, когда будет много времени.Immutable.js, и оптимизируйте некоторые модули с помощью React-хуков, чтобы сделать их более привлекательными... хе-хе.

Наконец, продолжение следует. . .


11. Ссылка на гитхаб

GitHub.com/Эффективность Как…