что такое два
- Во-первых, dva — это решение для потока данных, основанное на redux и redux-saga.Чтобы упростить процесс разработки, dva имеет дополнительный встроенный реагирующий маршрутизатор и выборку, поэтому его также можно рассматривать как облегченную структуру приложения.
- Дети, изучавшие React, знают, что в нем много технологических стеков, поэтому всякий раз, когда вы используете React, вам нужно вводить множество модулей, а затем dva интегрирует эти модули, чтобы сформировать определенную архитектурную спецификацию. Интеграция ссылок и кодов, которые реагируют, часто требует от нас написания, например, некоторых зависимостей, некоторого ReactDOM.render, который необходимо написать, введения саги, консольных инструментов редуктора, пакетов провайдеров и т. д. Значительно повысить эффективность нашей разработки
- Добавлены подписки для сбора действий из других источников, например: операции с клавиатурой, полосы прокрутки, веб-сокеты, маршрутизация и т. д.
- dva разработан на react-redux + dva-core разработан на основе redux-saga + roadhog разработан на основе webpack для упаковки и запуска сервисов
- Поток данных (на основе избыточности, так же, как реакция-редукция)
- Введите URL-адрес для отображения соответствующего компонента. Компонент использует диспетчеризацию для запуска функции в действии. Если он синхронный, он входит в редьюсер модели для изменения состояния. Если он асинхронный, например, выборка, данные будут перехвачены. эффект для получения данных через взаимодействие с сервером.Измените состояние, и то же состояние подключает модель и данные состояния к компоненту через подключение
Простой и быстрый проект dva
шаг:
- npm install dva-cli -g
- dva new dva-quickstart
- Структура каталогов
src
index.js (входной файл)
app = dva(opts)
Создайте приложение и верните экземпляр dva. (Примечание: dva поддерживает несколько экземпляров).
const app = dva({
history, // 指定给路由用的 history,默认是 hashHistory
initialState, // 指定初始数据,优先级高于 model 中的 state
onError, // effect 执行错误或 subscription 通过 done 主动抛错时触发,可用于管理全局出错状态。
onAction, // 在 action 被 dispatch 时触发
onStateChange, // state 改变时触发,可用于同步 state 到 localStorage,服务器端等
onReducer, // 封装 reducer 执行。比如借助 redux-undo 实现 redo/undo
onEffect, // 封装 effect
onHmr, // 热替换相关
extraReducers, // 指定额外的 reducer,比如 redux-form 需要指定额外的 form reducer
extraEnhancers, // 指定额外的 StoreEnhancer ,比如结合 redux-persist 的使用
});
Здесь вы можете настроить параметры для следующих хуков Здесь вы можете преобразовать историю хэшей в историю браузера.
import createHistory from 'history/createBrowserHistory';
const app = dva({
history: createHistory(),
});
app.use(hooks)
Вы также можете настроить хуки и зарегистрировать другие плагины.
import createLoading from 'dva-loading';
...
app.use(createLoading(opts));
app.model
В проекте обычного React-redux + Redux-Saga мы сначала построим 4 папки, которые являются папками действий, редуктора, саги, компонентов и служб, которые получают данные запроса, а также представлены в входном файле. Многие промежуточные программы, поставщика , Подключите и т. Д. Чтобы связать эти папки, в модели ниже и значительно снижают усилия по развитию.
-
Пространство имен Пространство имён модели и атрибуты его в глобальном состоянии, только строки, не поддерживаемые многоуровневым пространством имён. Сопоставимо с ключом этой модели Внутри компонента передайте CONNECT + этот ключ будет введен MODEL
import { connect } from 'dva'
- состояние Это начальное значение значения состояния, а приоритет ниже, чем у app.dva({})
const app = dva({
initialState: { count: 1 },
});
app.model({
namespace: 'count',
state: 0,
});
1 в это время
- редуктор Процессор действий, который обрабатывает синхронные действия и используется для вычисления последнего состояния, так же, как редюсер в редукции. dva инкапсулирует redux. Он будет проходить через функцию редукторов в модальном режиме. Каждая клавиша является редуктором. Конечно, он добавляет пространство имен, редуктор и эффект, соответствующие типу действия.
- эффект Обработчик действий, который обрабатывает асинхронные действия на основе Redux-saga. Эффект относится к побочному эффекту. Согласно функциональному программированию, к эффектам относятся операции, отличные от вычислений, обычно операции ввода-вывода, чтение и запись базы данных. Определите эффекты в формате ключ/значение. Используется для обработки асинхронных операций и бизнес-логики без прямого изменения состояния. Запускается действием, может запускать действие, может взаимодействовать с сервером, может получать глобальные данные о состоянии и т. д. Через генерировать доходность и общий вызов, поставить, взятьКаждый, взятьЛатест, взять в саге
- вызов для запуска асинхронной операции
- put эквивалентен диспетчеру, запускающему редуктор для изменения состояния
['setQuery']: [function*() {}, { type: 'takeEvery'}],
- takeEvery监听action的每次变化执行(默认)
- takeLatest监听action最近一次的变化
- take监听一次action留着,后面执行动作
- Зачем разделять синхронные и асинхронные?Обратите внимание, что редукторы должны быть чистыми функциями, поэтому одни и те же входные данные должны давать одинаковые выходные данные, и они не должны вызывать никаких побочных эффектов. Кроме того, для каждого расчета должны использоваться неизменяемые данные.Просто понимается эта особенность, что каждая операция возвращает новые данные (независимые, чистые), поэтому можно использовать такие функции, как горячая перезагрузка и путешествие во времени.
Эффекты называются побочными эффектами, и в нашем приложении наиболее распространенными являются асинхронные операции. Это происходит из концепции функционального программирования, это называется побочными эффектами, потому что делает нашу функцию нечистой, один и тот же вход не обязательно дает тот же результат.
Чтобы контролировать работу побочных эффектов, dva вводит редукционные саги внизу для управления асинхронным процессом.Из-за концепции генератора он преобразует асинхронную запись в синхронную, тем самым преобразуя эффекты в чистые функции. Что касается того, почему мы так одержимы чистыми функциями, если вы хотите узнать больше, вы можете прочитать «В основном адекватное руководство по FP» или его китайский перевод «Руководства по функциональному программированию JS».
Преимущества чистых функций: функции извлекаются и не связаны с бизнесом.
更有利于单元测试
无副作用(side-effect),不会修改作用域外的值,使代码好调试
执行顺序不会对系统造成影响
剥离出业务逻辑,好复用
- Куда пошло действие?Действие запускается в дистрибутиве компонента, а dva инкапсулирует redux.Он будет проходить функцию редукторов в модальном режиме один раз, и каждая клавиша является редуктором.Конечно, он добавляет пространство имен, соответствующее типу действия.редуктор, эффект
const { dispatch } = this.props;
dispatch({
type: 'app/updateState' ,
payload: {
opacityTop: 'none',//控制top的透明度
hiddenDivDisplay: 'none',//控制隐藏头部的display
footerDisplay: 'none'//控制footer的display
}
});
-
Подписки Определите подписку в формате ключ/значение. подписка — это подписка, используемая для подписки на источник данных с последующей отправкой соответствующего действия по мере необходимости. Выполняется в app.start(), источник данных может быть текущим временем, подключением к веб-сокету сервера, вводом с клавиатуры, изменениями геолокации, изменениями истории маршрутизации и т. д.
格式为 ({ dispatch, history }, done) => unlistenFunction。 注意:如果要使用 app.unmodel(),subscription 必须返回 unlisten 方法,用于取消数据订阅。
app.router
Импортируйте маршруты напрямую или импортируйте только компоненты в многостраничных приложениях.app.router(require('./router'));
app.router(() => <App />);
app.start
Запустите приложение. selector является необязательным, если параметр selector отсутствует, он вернет функцию, которая возвращает элемент JSX. селектор является корневым элементом
app.start('#root');
mock---.roadhogrc.mock.js
Сервер roadhog поддерживает фиктивную функцию, аналогичную dora-plugin-proxy, настроенную в .roadhogrc.mock.js, поддерживает обновление в реальном времени на основе требуемого динамического анализа, поддерживает синтаксис ES6 и удобные подсказки об ошибках. После небольшой настройки в конфигурационном файле (синтаксис узла) вы можете получать данные через простой запрос на выборку.
.roadhogrc.mock.js
export default {
// 支持值为 Object 和 Array
'GET /api/users': { users: [1,2] },
// GET POST 可省略
'/api/users/1': { id: 1 },
// 支持自定义函数,API 参考 express@4
'POST /api/users/create': (req, res) => { res.end('OK'); },
// Forward 到另一个服务器
'GET /assets/*': 'https://assets.online/',
// Forward 到另一个服务器,并指定子路径
// 请求 /someDir/0.0.50/index.css 会被代理到 https://g.alicdn.com/tb-page/taobao-home, 实际返回 https://g.alicdn.com/tb-page/taobao-home/0.0.50/index.css
'GET /someDir/(.*)': 'https://g.alicdn.com/tb-page/taobao-home',
};
Если это приложение с несколькими интерфейсами, используйте mockjs для имитации данных в фиктивной папке, а затем выполните обход файла и импортируйте его в файл конфигурации.
mock->user.js
const qs = require('qs');
const mockjs = require('mockjs'); //导入mock.js的模块
const Random = mockjs.Random; //导入mock.js的随机数
// 数据持久化 保存在global的全局变量中
let tableListData = {};
if (!global.tableListData) {
const data = mockjs.mock({
'data|100': [{
'id|+1': 1,
'name': () => {
return Random.cname();
},
'mobile': /1(3[0-9]|4[57]|5[0-35-9]|7[01678]|8[0-9])\d{8}/,
}],
page: {
total: 100,
current: 1,
},
});
tableListData = data;
global.tableListData = tableListData;
} else {
tableListData = global.tableListData;
}
module.exports = {
//post请求 /api/users/ 是拦截的地址 方法内部接受 request response对象
'GET /users' (req, res) {
setTimeout(() => {
res.json({ //将请求json格式返回
success: true,
data,
page: '123',
});
}, 200);
},
.roadhogrc.mock.js
const mock = {}
require('fs').readdirSync(require('path').join(__dirname + '/mock')).forEach(function(file) {
Object.assign(mock, require('./mock/' + file))
})
module.exports = mock
.webpackrc
Формат JSON, комментарии разрешены, значение по умолчанию логических элементов конфигурации — false.Поддерживает настройку через webpack.config.js в закодированном виде, но не рекомендуется, т.к. вызвать проблемы совместимости.
- запись: установить файл записи
- disableCSSModules: установить, следует ли модульизовать css
- публичный путь:
- outputPublic:
- дополнительные плагины Babel Настройте дополнительные плагины Babel. Плагин babel может только добавлять, перезаписывать и удалять нельзя. Например, при одновременном использовании antd и dva обычно необходимо соответствие:
"extraBabelPlugins": [
"transform-runtime",
"dva-hmr",
["import", { "libraryName": "antd", "libraryDirectory": "lib", "style": "css" }]
]
- прокси Настройте прокси, подробности см. в разделе webpack-dev-server#proxy. Если вы хотите проксировать запросы на другие серверы, вы можете настроить это следующим образом:
"proxy": {
"/api": {
"target": "http://jsonplaceholder.typicode.com/",
"changeOrigin": true,
"pathRewrite": { "^/api" : "" }
}
}
-
многостраничный Настройте, следует ли применять многостраничность. Многостраничные приложения будут автоматически извлекать общие части как common.js и common.css.
-
определять Настройте подключаемый модуль DefinePlugin веб-пакета, значение define будет автоматически обработано JSON.stringify.
-
окружение Настройка для конкретной среды. Переменная среды для сервера — это разработка, а переменная среды для сборки — это производство. Предотвращение избыточности в производственной среде.
"extraBabelPlugins": ["transform-runtime"],
"env": {
"development": {
"extraBabelPlugins": ["dva-hmr"]
}
}
- тема Настройка темы на самом деле является модификациейVars с меньшим количеством. Поддерживает конфигурацию двумя способами: объект и путь к файлу. Установите глобальный стиль в сочетании с antd.
"theme": {
"@primary-color": "#1DA57A"
}
/
"theme": "./node_modules/abc/theme-config.js"
повышение ранга
два/динамический (ленивая загрузка)
Используется в router.js для динамической загрузки моделей и компонентов. app: экземпляр dva, необходимый для загрузки моделей модели: обещание возвратамножествофункция, Promise возвращает модель dva компонент: функция, которая возвращает Promise, Promise возвращает React Component
модульность css
Ввести свой собственный инкапсулированный af-webpack в roadhog, который использует css-loader и конфигурацию .webpackrc для модуляции css, инкапсулирует слой результата css js и добавляет случайный хэш к имени класса, чтобы имя класса не конфликтовало, если вы хотите быть глобальным, просто добавьте: глобальный
Делитесь глобальной информацией с моделями
Если в текущем приложении загружено более одной модели и операция выбора выполняется в одном из эффектов, можно получить состояние в другом:
*foo(action, { select }) {
const { a, b } = yield select();
}
Динамическое расширение модели
- Обратите внимание, что каждая модель в dva на самом деле является обычным объектом JavaScript, который можно перезаписать с помощью object.assign.
- Сгенерировать модель через фабричную функцию
function createModel(options) {
const { namespace, param } = options;
return {
namespace: `demo${namespace}`,
states: {},
reducers: {},
effects: {
*foo() {
// 这里可以根据param来确定下面这个call的参数
yield call()
}
}
};
}
const modelA = createModel({ namespace: 'A', param: { type: 'A' } });
const modelB = createModel({ namespace: 'A', param: { type: 'B' } });
- Сделать это можно с помощью библиотеки dva-model-extend от сообщества dva.
многозадачность
- Параллельное выполнение задач
const [result1, result2] = yield all([
call(service1, param1),
call(service2, param2)
])
- конкурс задач
const { data, timeout } = yield race({
data: call(service, 'some data'),
timeout: call(delay, 1000)
});
if (data)
put({type: 'DATA_RECEIVED', data});
else
put({type: 'TIMEOUT_ERROR'});
Связь между моделями
Что, если здесь что-то сделать внутри компонента? Передать разрешение модели
new Promise((resolve, reject) => {
dispatch({ type: 'reusable/addLog', payload: { data: 9527, resolve, reject } });
})
.then((data) => {
console.log(`after a long time, ${data} returns`);
});
Межмодельное общение в модели
try {
const result = yield call(service1);
yield put({ type: 'service1Success', payload: result });
resolve(result);
}
catch (error) {
yield put({ type: 'service1Fail', error });
reject(ex);
}
Анализ исходного кода
roadhog
roadhog в основном полагается на собственный упакованный af-webpack
Получите конфигурацию и проверку webpackrc
В папке getUserConfig получить конфигурацию напрямую через содержимое json
Проверьте каждый элемент в конфигурацииВызовите метод watch.js в index и js для мониторинга нашего файла конфигурации, а папка для прослушивания использует пакет chokidar.модульность css
Благодаря конфигурации нашего файла конфигурации и суждению среды значение хеш-функции динамически добавляется к задней части имени класса.dva
Изучив его package.json, можно увидеть, что ниже приведена только инкапсуляция fetch, redux, routerdva/dynamic
- Сначала используем promise.all для загрузки входящей модели и компонента.Сначала определяем есть ли модель.Если модели нет, то компонент будет отправлен напрямую.Если модель есть, то модель будет динамически загружаться здесь( регистрацияМодель)Здесь мы используем app.model для регистрации, конечно, мы также можем использовать этот метод для расширения метода удаления app.unmodel.
- Куда пропал компонент?Здесь видно, что на самом деле мы также можем передать параметр в нашу динамику, который является компонентом, загружаемым по умолчанию, то есть LoadingComponent, Используйте загрузку жизненного цикла компонента по умолчанию для управления настройками компонента, который мы передайте и назначьте его AsyncComponent.
- Тогда почему this.state.AsyncComponent = AsyncComponent, такой способ написания Не допускайте слишком высокой скорости рендеринга, в результате чего компонент по умолчанию не висит на нем, а напрямую рендерит асинхронный компонент.
- Когда async имеет значение null, отображается только компонент по умолчанию, из чего мы видим, что компонент является обязательным параметром.
index.js
Отсюда видно, что здесь написаны провайдер, рендер и т.д.И это делается в app.start, и маршрутизатор должен быть зарегистрирован перед запуском.Точно так же при инициализации dva({}) промежуточное ПО регистрируется и возвращается объект приложения.Используйте routerReducer реакции-маршрутизатора-редукса, чтобы выполнять переходы маршрутизации для действий и возвращать маршрутизацию компонентам для использования.dva-core
Через package.json мы знаем, что это инкапсуляция redux-sagamodel
- Сначала проверьте 5 API в checkModel
- Префикс каждой модели с ключом в prefixNamespace, чтобы его можно было использовать в качестве типа действия для отправки.
- Зарегистрируйте промежуточное ПО и сагу в createStore
- Реализовать функции редуктора и эффекта в getSaga getReducer
- Здесь он также возвращает объект приложения для реализации конкретных функций запуска и использованияc Из неустановленной функции видно, что он также сохраняет модель в магазине и инициирует удаление модели через диспетчеризацию.
- subscription Object.prototype.hasOwnProperty.call(subs, key) или используйте метод прототипа, чтобы определить, является ли ключ собственным свойством subs Если это свободный атрибут, то получить значение, соответствующее атрибуту (это функция) Вызовите эту функцию, передав свойства отправки и истории. история — это история, дополненная redux-router, а отправка имеет префикс Dispatch (app._store.dispatch, model)
- prefixedDispatch: префикс пространства имен с методом отправки