предисловие
Через три месяца я наконец завершил первый этап проекта с моими друзьями, и я много получил. Но поскольку это первый раз, когда мы полностью используем его в формальном проектеReact
Для разработки у нас не хватило опыта построения предыдущего проекта, из-за чего мы столкнулись с некоторыми ограничениями при оптимизации проекта, и мы могли выбирать только какие-то второстепенные пути.
Далее я разберусь и "заполню пробелы" по всему процессу разработки на этот раз. Надеюсь, это тоже будет вам полезно. Если в тексте есть какие-то ошибки или неточности, пожалуйста, дайте мне несколько советов от большие ребята (в этой статье в основном рассказывается о миддл и бекстейджах). Процесс сборки проекта).
Кроме того, техническая итерация всех аспектов внешнего интерфейса сейчас выполняется немного быстрее, и некоторые методы написания между разными версиями отличаются, поэтому я буду отмечать версии зависимостей, которые я использую в настоящее время, чтобы различать их.
Подготовить
1. Инициализировать проект
Начиная с npm версии 5.2, была добавлена команда npx, а узел поставляется с npm.По умолчанию здесь у всех установлен узел (как интерфейс, не должно быть ситуации, когда он не установлен).
npx create-react-app react-demo
или
npm install -g create-react-app
create-react-app react-demo
2. Установите необходимые зависимости (необязательно)
-
react-router-dom
Обеспечивает функциональность маршрутизации.
// react-router-dom@5.2.0 npm install react-router-dom
-
redux
,react-redux
,redux-thunk
redux
: статус менеджера (статус данных).react-redux
:react-redux
даredux
Тот, что упакован авторомreact
Специальная библиотека.redux-thunk
: Пусть оригинал принимает только объектstore.dispatch
Становится объектом/методом, который может получать, и если метод получен, метод автоматически выполняется без срабатыванияredux
изstore
возобновить.// redux@4.0.5、react-redux@7.2.1、redux-thunk@2.3.0 npm install redux react-redux redux-thunk
-
redux-devtools-extension
store
Средства отладки управления данными.// redux-devtools-extension@2.13.8 npm install redux-devtools-extension
-
immer
,use-immer
immer
: реализует неизменяемые структуры данных для js.use-immer
:поставкаuseImmer
метод.// immer@7.0.9、use-immer@0.4.1 npm install immer use-immer
-
react-app-rewired
,customize-cra
,react-app-rewire-multiple-entry
react-app-rewired
: это инструмент для изменения конфигурации CRA, обеспечивающий функцию изменения конфигурации проекта без раскрытия конфигурации проекта.customize-cra
: Предоставляет вспомогательный метод для измененияwebpack
конфигурация.react-app-rewire-multiple-entry
: добавить многостраничную запись.// react-app-rewired@2.1.6、customize-cra@1.0.0、react-app-rewire-multiple-entry@2.2.0 npm install react-app-rewired customize-cra --save-dev
-
dotenv-cli
Буду
.env
Переменные среды в файле загружаются вprocess.env
.// dotenv-cli@4.0. npm install dotenv-cli --save-dev
-
less
,less-loader
less
:CSS
язык предварительной обработки.less-loader
:webpack
Будуless
скомпилировано вcss
изloader
.// less@3.12.2、less-loader@7.0.1 npm install less less-loader --save-dev
Конфигурация с несколькими средами
В процессе разработки у проекта будет несколько сред: разработка, тестирование, UAT, производство и т. д. Мы развернем проект в соответствии с каждой средой. В это время нам нужно пройтиdotenv
будет соответствовать среде.env
Переменные среды в файле загружаются вprocess.env
, чтобы получить конфигурацию с несколькими средами (Официальный сайт).
-
Создайте следующие файлы в корневом каталоге проекта и добавьте переменные среды.
BASE_URL
:-
.env
: Используется для установки некоторой общей конфигурации. -
.env.development
: Конфигурация среды разработки, добавить переменные:REACT_APP_BASE_URL=development.api.com
-
.env.test
: Конфигурация тестовой среды, добавить переменные:REACT_APP_BASE_URL=test.api.com
-
.env.production
: Конфигурация производственной среды, добавить переменные:REACT_APP_BASE_URL=production.api.com
-
-
Исправлять
package.json
в файлеscripts
:
"scripts": {
"start": "react-app-rewired start",
"build:dev": "dotenv -e .env.development react-app-rewired build",
"build:test": "dotenv -e .env.test react-app-rewired build",
"build:prod": "dotenv -e .env.production react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
-
Создайте новый в корневом каталоге
config-overrides.js
документ.когда мы проходим
react-app-rewired
При запуске/упаковке проекта он будет читаться первымconfig-overrides.js
Соответствующая конфигурация в оригиналеwebpack
Правила конфигурации изменяются, а затем упаковываются. Здесь мы сначала создаем пустойconfig-overrides.js
файл, а затем настроить его в соответствии с потребностями проекта. -
тестовое задание
С приведенной выше конфигурацией мы можем пройти
process.env
Получите доступ к переменным среды в конфигурации и запустите/упакуйте код каждой среды в соответствии с различными командами компиляции.воплощать в жизнь
npm start
, Стартап проект. существуетApp.js
печатать вprocess.env.REACT_APP_BASE_URL
, мы можем увидеть вывод консолиdevelopment.api.com
(Другие среды могут быть протестированы сами по себе, поэтому я не буду здесь вдаваться в подробности. Кроме того, если вы измените файлы переменных среды илиconfig-overrides.js
Содержимое в том, что необходимо повторно выполнитьnpm start
вступит в силу).
Многовходовая конфигурация
Потому что наш проект разделен на множество терминалов (официальный сайт, две промежуточные станции ПК, одна серверная часть ПК и одна мобильная часть). Для фона, как в случае с ПК, так и с мобильным терминалом (мобильный терминал является кастрированной версией версии для ПК), некоторые методы помощи, бизнес-логика и т. Д. Согласованы, поэтому мы помещаем два терминала в одно и то же. разработка в рамках проекта.
Для достижения этой цели нам необходимоwebpack
Конфигурация расширена для поддержки нескольких записей.
1. Добавьте файлы, связанные с мобильными устройствами
Добавьте связанные файлы в корневой каталог проекта:
-
index-m.js
: Файл записи мобильного терминала. -
public/mobile.html
: Мобильныйhtml
файл шаблона. -
AppMobile.js
: Добавьте соответствующие компоненты конфигурации мобильного терминала вindex-m.js
импортировать в.
и оригиналindex.js
,public/index.html
,App.js
Он используется как связанный файл на стороне ПК.
Исправлятьconfig-overrides.js
:
const { override } = require('customize-cra')
const multipleEntry = require('react-app-rewire-multiple-entry')([
{
entry: 'src/index-m.js',
template: 'public/mobile.html',
outPath: '/mobile.html'
}
])
const customWebpack = () => config => {
multipleEntry.addMultiEntry(config)
return config
}
module.exports = {
webpack: override(
customWebpack(),
)
}
Здесь мы добавили входной файл мобильного терминала к исходномуwebpack
Идет настройка, давайте проверим ее ниже (вы можетеApp.js
а такжеAppMobile.js
Добавьте к нему некоторый контент, главное, чтобы вам было удобно различать ПК-терминал или мобильный терминал).
сделай это сноваnpm start
, посетить отдельноhttp://localhost:3002/index.html
а такжеhttp://localhost:3002/mobile.html
(Здесь каждый обращается в соответствии с портом, работающим на их собственных проектах). Получилось очень красиво~.
2. Настройте прокси
Но вопрос в том, что наша первоначальная цель состоит в том, чтобы различать несколько терминалов, а не превращать его в многостраничное приложение, его суть все же одностраничное приложение, что нам делать?
Не волнуйтесь, не торопитесь~
мы сейчас прошлиindex.html
а такжеmobile.html
суффикс для различения, если вы можете использовать порт или имя домена для различения, можно ли решить эту проблему?Моя идея состоит в том, чтобы передатьNginx
Настройте прокси (Nginx
Для получения соответствующих базовых знаний см.:Введение в основы Nginx, практика с нуля до единицы) для доступа к разным портам в соответствии с разными портами. Лучше действовать, пробовать~
Добавить кNginx
настроить и перезапуститьsudo nginx -s reload
:
server {
listen 8888;
server_name localhost;
location / {
proxy_pass http://127.0.0.1:3002;
}
}
server {
listen 8889;
server_name localhost;
location ~ ^[^.]*$ {
rewrite /(.*) /mobile.html break;
proxy_pass http://127.0.0.1:3002;
}
location / {
proxy_pass http://127.0.0.1:3002;
}
}
доступhttp://localhost:8888/
а такжеhttp://localhost:8889/
, вроде без проблем, но...
3. Изменить веб-сокет
Когда я был тайно счастлив, реальность сильно ударила меня.
Когда я открыл консоль, я увидел сообщение об ошибке, как показано на рисунке ниже (почему у одного 404, а у другого 200, я не знаю, если кто знает, просветите, пожалуйста):
Этот результат является ошибкой горячего обновления, вызвавшей сбой, все изменения необходимо обновить вручную, но все предупреждения не работают, это, безусловно, неприемлемо (в конце концовreact
В процессе разработки соответствующие предупреждения все еще очень полезны).
Сначала давайте посмотрим наnetwork
серединаsockjs-node
Запрошенная информация:
Причина сбоя соединения здесь, потому что наш проект фактически работает в3002
на порту, и когда мы используемNginx
После настройки прокси мы получаем доступ8888
порт и8889
порт. Итак, нам просто нужно продолжать отправлятьsockjs-node
Запрошенный порт по-прежнему3002
решит эту проблему.
То есть нам нужно подтвердить, что при созданииwebsocket
Как настроить при подключенииurl
, что, в свою очередь, устанавливает для него желаемое значение.
Найдя причину и решение, можно вздохнуть с облегчением (по крайней мере, с направлением). Давайте посмотрим на местоположение конкретной ошибки (нажмите на сообщение об ошибке, выводимое консольюwebpackHotDevClient.js:60
):
Увидев это, неужели вдруг осознаешь, что успех не за горами! Из этого кода мы можем узнать, что если установленоWDS_SOCKET_HOST
а такжеWDS_SOCKET_PORT
, то сразу взять соответствующее значение; если не задано, взять текущее имя домена доступа и порт. Окончательное сращиваниеwebsocket
связаныurl
.
Итак, нам нужно только указать в конфигурации средыWDS_SOCKET_HOST
а такжеWDS_SOCKET_PORT
Эта проблема может быть решена для доменного имени и порта, которые мы хотим. существует.env.development
Добавьте в файл следующее (горячее обновление обычно нужно использовать только в среде разработки):
WDS_SOCKET_HOST=127.0.0.1
WDS_SOCKET_PORT=3002
Перезапустите проектnpm start
, посетить отдельноhttp://localhost:8888/
а такжеhttp://localhost:8889/
Нет ошибки, большие заслуги меня ~
библиотека компонентов пользовательского интерфейса
В проекте были выбраны Ant Design и Ant Design Mobile в качестве библиотеки компонентов пользовательского интерфейса для ПК и мобильных устройств соответственно.
Установить:
// antd@4.6.6、antd-mobile@2.3.4
npm install antd antd-mobile --save
1. antd
существуетindex.js
введен вantd
файл стиляimport 'antd/dist/antd.less'
.
antd по умолчанию поддерживает встряхивание дерева на основе ES-модулей, а для js-части это представлено напрямую.
import { Button } from 'antd'
Это будет иметь эффект загрузки по требованию.
2. antd-mobile
В настоящее времяantd-mobile
Загрузка по требованию также должна быть реализована вручную.
Исправлятьconfig-overrides.js
документ(Официальный сайт):
const { override, fixBabelImports } = require('customize-cra')
// ...
module.exports = {
webpack: override(
customWebpack(),
fixBabelImports('import', {
libraryName: 'antd-mobile',
style: true
})
)
}
Повторно запустите проект, а затем просто начните сantd-mobile
Просто импортируйте модуль, вам не нужно импортировать стили отдельно.
3. Добавлено меньше поддержки и пользовательских тем
Добавлено меньше поддержки
После выполнения двух вышеуказанных шагов вы обнаружите, что стиль компонента не работает, когда вы обращаетесь к компоненту для тестирования. Это связано с тем, что проекты, созданные CRA, по умолчанию не поддерживают компиляцию меньшего количества файлов, поэтому нам нужно передатьconfig-overrides.js
расширятьwebpack
соответствующая конфигурация. (вот что нужно установитьless
а такжеless-loader
Да, он был установлен ранее, поэтому я его здесь пропущу) (Кроме того, недавноAntd
Официальный сайт также постоянно обновляется.Пример исходного официального сайта:addLessLoader
, теперь изменено на импортcraco-less
чтобы помочь загрузить меньше стилей и изменить переменные, вы можете попробовать их все).
полныйconfig-overrides.js
:
const { override, addLessLoader, fixBabelImports } = require('customize-cra')
const multipleEntry = require('react-app-rewire-multiple-entry')([
{
entry: 'src/index-m.js',
template: 'public/mobile.html',
outPath: '/mobile.html'
}
])
const customWebpack = () => config => {
multipleEntry.addMultiEntry(config)
return config
}
module.exports = {
webpack: override(
customWebpack(),
addLessLoader({
lessOptions: {
javascriptEnabled: true,
modifyVars: {
}
}
}),
fixBabelImports('import', {
libraryName: 'antd-mobile',
style: true
})
)
}
Запустите проект, соответствующий стиль компонента может вступить в силу.
Пользовательские темы
В приведенной выше конфигурации мы можем передатьmodifyVars
Исправлятьantd
а такжеantd-mobile
стили темы, такие как: цвет темы, цвет текста, закругленные углы и т. д.
Пример:
// ...
addLessLoader({
lessOptions: {
javascriptEnabled: true,
modifyVars: {
'@primary-color': '#4085F5',
'@text-color': 'rgba(0, 0, 0, 0.65)',
'@brand-primary': '#4085F5',
'@fill-body': '#F7F8FA',
'@color-text-base': '#333333',
},
}
})
//...
CSS Module
использовать[name].module.css
Поддержка схемы именования файловCSS Module
и обычный CSS.CSS Module
Разрешить автоматическое создание[filename]\_[classname]\_\_[hash]
Уникальное имя класса в формате для области действия CSS.
В проекте мы используем Less в качестве препроцессора, и пусть он поддерживаетCSS Module
необходимо изменитьwebpack
Конфигурация. К счастью, выше мы использовалиreact-app-rewired
Измените соответствующую конфигурацию CRA в видеaddLessLoader
способ сделать некоторые расширения. а такжеaddLessLoader
По умолчанию он был изменен для насwebpack
связанные вless-loader
конфигурации, поэтому мы можем напрямую использовать[name].module.less
.
Мы можем просто посмотреть наaddLessLoader
Эта часть исходного кода:
- Имя обработанного стиля по умолчанию в формате
[local]--[hash:base64:5]
; - Различают по двум закономерностям
.less
а также.module.less
Два типа.
Теперь переходим непосредственно к тесту.
новыйstyle/base.module.less
:
.baseContainer {
padding: 50px;
.title {
color: pink;
}
.fontSize {
font-size: 20px;
}
}
ИсправлятьApp.js
:
import React from 'react'
import style from './style/base.module.less'
function App() {
return (
<div className={style.baseContainer}>
<div className={`${style.title} ${style.fontSize}`}>App</div>
</div>
)
}
export default App
Просмотрите текущие результаты:
Для более подробного использования, пожалуйста, обратитесь к официальному сайту:портал.
маршрутизация
1. Конфигурация статической маршрутизации
использовалVue
студенты знают, что вVue
в,vue-router
Предоставляет нам множество удобств, таких как настройка статической маршрутизации и защита навигации. пока вReact
Здесь все это необходимо реализовать вручную самостоятельно.
React
Зависимости конфигурации статической маршрутизации уже предоставлены большими парнями на github:react-router-config
.
Установитьreact-router-config
,ЭтоReact
Помощник по настройке статического маршрута для маршрутизаторов.
// react-router-config@5.1.1
npm install react-router-config --save
Он предоставляет два метода:matchRoutes
а такжеrenderRoutes
, мы в основном смотрим наrenderRoutes
.
Сначала посмотрите на исходный код (node_modules/reactrouter-config/modules/renderRoutes.js
):
import React from "react";
import { Switch, Route } from "react-router";
function renderRoutes(routes, extraProps = {}, switchProps = {}) {
return routes ? (
<Switch {...switchProps}>
{routes.map((route, i) => (
<Route
key={route.key || i}
path={route.path}
exact={route.exact}
strict={route.strict}
render={props =>
route.render ? (
route.render({ ...props, ...extraProps, route: route })
) : (
<route.component {...props} {...extraProps} route={route} />
)
}
/>
))}
</Switch>
) : null;
}
export default renderRoutes;
Содержание исходного кода очень простое, т.routes
провестиmap
иметь дело с. У некоторых студентов могут возникнуть вопросы: почему мы так загружаем маршруты? Я так понимаю, чтобы сделать наши программы более лаконичными и управляемыми.
более лаконичный: может имитироватьVue
Метод записи маршрута посередине реализует статическую конфигурацию маршрута, а структура маршрута ясна и ясна; в то же время, когда в проекте несколько макетов, мы можем написать маршрут и зарегистрировать маршрут более четко. .
более контролируемый:существуетReact
Внутри, думаю, все сталкивались с проблемой утечек памяти, один из самых частых примеров — при переходе на страницу интерфейсный запрос не завершается, а в callback выполняются страничные операции. В это время возникает проблема с утечкой памяти. Что касается этой проблемы, моя идея состоит в том, чтобы отменить незавершенный запрос при переходе страницы (в зависимости от деловой ситуации). А действие «отменить» можно поместить вrenderRoutes
(по-моему это навигационная стража).
новыйroute/renderRoutes.js
и скопируйте исходный код в пакет зависимостей, чтобы мы могли расширить его по мере необходимости.
новыйroute/index.js
:
const routes = [
{ path: '/login', exact: true, component: Login},
{ path: '/404', component: NotFound},
{
path: '/goods', component: GoodsLayout,
routes: [
{ path: '/goods/list', component: GoodsList}
]
},
{
component: BasicLayout,
routes: [
{ path: '/', exact: true, component: Home},
{ path: '/home2', exact: true, component: Home2 },
{ path: '/home3', exact: true, component: Home3 }
]
}
]
На этом настройка статической маршрутизации завершена. Для получения подробной информации о том, как его использовать, пожалуйста, обратитесь к github.react-router-config
введение.
2. Расширение
С вышеуказаннымrenderRoutes.js
После этого мы реализуем и управляем большинством функций маршрутизации унифицированным образом, таких как аутентификация маршрутизации, запросы на отмену и т.д.
Просто посмотрите на реализацию маршрутной аутентификации, изменитеrenderRoutes.js
:
import React from 'react'
import { Switch, Route } from 'react-router'
function renderRoutes(routes, authed, extraProps = {}, switchProps = {}) {
return routes ? (
<Switch {...switchProps}>
{routes.map((route, i) => (
<Route
key={route.key || i}
path={route.path}
exact={route.exact}
strict={route.strict}
render={props => {
if (route.auth && !route.auth.includes(authed)) {
// 进行拦截
}
return route.render ? (
route.render({ ...props, ...extraProps, route: route })
) : (
<route.component {...props} {...extraProps} route={route} />
)
}
}
/>
))}
</Switch>
) : null
}
export default renderRoutes
Вот, проходяauthed
(права текущего пользователя), вrender
Суждение в методе: если у пользователя есть разрешение на вход на целевую страницу, она будет прыгать нормально, иначе она будет перехвачена и обработана соответствующим образом.
Что касается того, как следует оценивать аутентификацию маршрутизации в реальной ситуации, необходимо разработать бизнес-логику.
Axios отменить запрос
существуетreact
Внутри я считаю, что многие студенты столкнулись с проблемой утечки памяти, в основном из-за обновления статуса, выполняемого после выгрузки страницы. Обычно он выполняется после обратного вызова асинхронного запроса.setData
сопутствующие операции.
В текущем проекте мы используем большеaxios
,axios
Предоставляет нам два способа отмены. Просто посмотрите документацию:
Из документации мы можем знать, что первый способ может отменить несколько запросов, а второй — только определенный. Так что здесь я сразу выберу первый (лично я думаю, что большую часть времени в практических приложениях отменяются множественные запросы, поэтому я в основном смотрю на первый).
1. Простые аксиомы пакетов
Установить:
// axios@0.19.2、express@4.17.1
npm install axios express --save
новыйserver/index.js
:
Сначала подготовьте два интерфейса и установите 2-секундную задержку для удобства тестирования.
const express = require('express')
const app = express()
app.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (req.method == 'OPTIONS') {
res.send(200)
} else next()
})
app.get('/api/test', function (req, res) {
setTimeout(() => {
res.send('测试结果')
}, 2000)
})
app.post('/api/test2', function (req, res) {
setTimeout(() => {
res.send('测试结果')
}, 2000)
})
let server = app.listen(9999, function () {
let host = server.address().address
let port = server.address().port
console.log('访问地址为:', host, port)
})
новыйutils/request
:
import axios from 'axios'
const service = axios.create({
baseURL: 'http://127.0.0.1:9999',
timeout: 20000
})
service.interceptors.request.use(
(config) => {
return config
},
(error) => {
return Promise.reject(error)
}
)
service.interceptors.response.use(
(response) => {
return response
},
(error) => {
return Promise.reject(error)
}
)
export default service
новыйmodel/index.js
:
import fetch from '../utils/request.js'
class Model {
api(options = {}) {
if (!options.method) options.method = 'get'
return new Promise((resolve, reject) => {
let request
let config = {
method: options.method,
url: options.url,
}
switch (options.method) {
case 'get':
request = fetch({
...config,
params: options.params
})
break
case 'post':
request = fetch({
...config,
data: options.data
})
break
default:
}
request
.then(response => {
resolve(response)
}).catch(error => {
reject(error)
})
})
}
get (options = {}) {
options.method = 'get'
return this.api(options)
}
post (options = {}) {
options.method = 'post'
return this.api(options)
}
}
export default Model
новыйmodel/common.js
:
import Model from './index'
class Common extends Model {
getTest(options = {}) {
options.url = '/api/test'
return this.get(options)
}
getTest2(options = {}) {
options.url = '/api/test2'
return this.post(options)
}
}
const commonModel = new Common()
export default commonModel
использовать:
import React, { useState } from 'react'
import { Button } from 'antd'
import commonModel from '@/model/common'
function Index2() {
const [test, setTest] = useState(0)
const sendRequest = () => {
commonModel.getTest().then((data) => {
setTimeout(() => {
setTest(1)
}, 1000)
})
commonModel.getTest2().then((data) => {
setTest(123)
})
}
return (
<div>
<div>概览2</div>
<div>{test}</div>
<Button onClick={sendRequest}>send request</Button>
</div>
)
}
export default Index2
Нажмите кнопку, чтобы отправить запрос, отобразится страницаtest
Значение сначала 123, затем 1, затемaxios
Конфигурация прошла успешно.
Имитируем утечку памяти: открываем консоль, нажимаем кнопку отправки запроса и переходим на другие страницы в течение двух секунд, видим следующие ошибки:
Это связано с тем, что когда мы переходим на страницу, запрос предыдущей страницы не заканчивается, и срабатывает обновление состояния, что затем приводит к утечке памяти. Ниже мы решим эту задачу.
2. axios.CancelToken
Исправлятьutils/request.js
:
// ...
const getCancelToken = () => {
let CancelToken = axios.CancelToken
let source = CancelToken.source()
return source
}
// ...
service.interceptors.request.use(
(config) => {
config.cancelToken = config.cancel_token
return config
},
(error) => {
return Promise.reject(error)
}
)
// ...
export {
getCancelToken
}
Исправлятьmodel/index.js
:
// ...
return new Promise((resolve, reject) => {
let request
let config = {
method: options.method,
url: options.url,
}
if (options.cancel_token) config.cancel_token = options.cancel_token
switch (options.method) {
//...
Изменить страницу:
import React, { useEffect, useRef, useState } from 'react'
import { Button, Space } from 'antd'
import commonModel from '@/model/common'
import { getCancelToken } from '@/utils/request'
function Index2() {
const source = useRef(getCancelToken())
const [test, setTest] = useState(0)
useEffect(() => {
return () => {
source.current.cancel('取消请求')
source.current = null
}
}, [])
const sendRequest = () => {
commonModel.getTest({
cancel_token: source.current.token
}).then((data) => {
setTimeout(() => {
setTest(1)
}, 1000)
})
commonModel.getTest2({
cancel_token: source.current.token
}).then((data) => {
setTest(123)
})
}
return (
<div>
<div>概览2</div>
<div>{test}</div>
<Space>
<Button onClick={sendRequest}>send request</Button>
</Space>
</div>
)
}
export default Index2
Повторив вышеописанный процесс проверки, мы обнаружим, что консоль выводит дважды: «Отменить запрос», и проблема с утечкой памяти больше не возникает.
Просто разберитесь с идеями:
- Передайте его методу, который должен «отменить запрос».
cancel_token
; - пройти через
model/index.js
перейти кutils/request.js
; -
utils/request.js
перениматьcancel_token
, и добавить вaxios
в конфигурации перехватчика запросов. - Когда страница будет удалена, позвоните
cancel
Способ отменить запрос.
Было бы слишком сложно сделать это для каждой страницы. Есть ли общее место для обработки «запросов на отмену»?
Ответ да~, первое действие по отмене запроса выполняется при выгрузке страницы (изменение маршрута) В сочетании с вышеизложеннымrenderRoutes
, можно ли решить эту проблему?
3. Единая обработка
Идея состоит в том, чтобы использовать идентификатор, чтобы различать API, которым необходимо отменить запрос при переключении маршрута; добавьте эти отмеченные API.cancel_token
; Наконец, отменить запрос этих API при переключении маршрута.
новыйutils/global.js
, для храненияcancel_token
а такжеcancel
метод.
let global = {
source: {
token: null,
cancel: null
},
timestamp: null
}
const changeGlobal = (key, value) => {
global[key] = value
}
export {
global,
changeGlobal
}
Исправлятьmodal/common.js
, через переменнуюneedCancel
Отметьте, какие интерфейсы необходимо «отменить».
import Model from './index'
class Common extends Model {
getTest(options = {}) {
options.url = '/api/test'
options.needCancel = true
return this.get(options)
}
getTest2(options = {}) {
options.url = '/api/test2'
options.needCancel = true
return this.post(options)
}
}
const commonModel = new Common()
export default commonModel
Исправлятьmodal/index.js
, добавить в отмеченный интерфейсcancel_token
.
import fetch from '../utils/request.js'
import { global } from '../utils/global'
class Model {
// ...
if (options.needCancel && global.source) config.cancel_token = global.source.token
switch (options.method) {
// ...
Исправлятьroute/renderRoutes.js
,
import React from 'react'
import { Switch, Route, Redirect } from 'react-router'
import { getCancelToken } from '@/utils/request'
import { global, changeGlobal } from '../utils/global'
function renderRoutes(routes, authed, extraProps = {}, switchProps = {}) {
return routes ? (
<Switch {...switchProps}>
{routes.map((route, i) => (
<Route
key={route.key || i}
path={route.path}
exact={route.exact}
strict={route.strict}
render={props => {
if (route.auth && !route.auth.includes(authed)) {
// 进行拦截
}
// 生成新的 cancel_token
if (global.source.token && global.source.cancel) global.source.cancel('取消请求')
changeGlobal('source', getCancelToken())
changeGlobal('timestamp', new Date().getTime())
// ...
Повторите описанный выше процесс проверки, и все готово~
Суммировать
Вещи ведут к другому, один за другим проектом, занят некогда черпать воду, ┭┮﹏┭┮ ... но хорошая новость в том, что, хоть и нон-стоп пишет дело, но всегда все же что-то в проекте собрано. Скрытность, статьи написаны какие-то грубые, знания относительно разрознены, после чего со временем постараюсь улучшить. Маленькие партнеры имеют любые предложения комментировать прямо в гостевой книге О ~
Продолжение следует...