Что такое два
два это прежде всегоreduxа такжеredux-saga, а для того, чтобы упростить разработку, в dva есть дополнительные встроенныеreact-routerа такжеfetch, поэтому его также можно понимать как облегченную структуру приложений.
Задачи, которые решает Два
После периода самостоятельного изучения или обучения каждый должен быть в состоянии понять концепцию избыточности и признать, что такое управление потоком данных может сделать приложение более управляемым, а логику — более ясной. Но тогда обычно возникает такой вопрос: слишком много понятий, а редюсер, сага, экшен все разнесены (в отдельные файлы).
- Проблема с переключением файлов. Проекты Redux обычно делятся на редюсер, экшн, сагу, компонент и т. д. Стоимость переключения файлов, вызванная их хранением в подкаталогах, относительно велика.
- Неудобно организовывать бизнес-модель (или модель предметной области). Например, после того, как мы напишем список пользователей, чтобы написать список продуктов, нам нужно скопировать много файлов.
- Сагу создавать хлопотно, каждый раз, когда вы слушаете действие, вам нужно пройти процесс fork -> watcher -> worker
- вход создает проблемы. могу видеть этоredux entryНапример, в дополнение к созданию редукционного хранилища, настройке промежуточного программного обеспечения, инициализации маршрута, привязке хранилища провайдера, инициализации саги и HMR редуктора, компонента и саги. Это пример реального проекта с редуксом, который выглядит сложнее.
Преимущества Два
- Простота в освоении и использовании, всего 6 API-интерфейсов, особенно удобных для пользователей Redux,Используйте с умиПосле этого снижается до 0 API
- концепция вяза, упорядочивает модели по редукторам, эффектам и подпискам
- Механизм плагина,Напримерdva-loadingМожет автоматически обрабатывать состояние загрузки, нет необходимости снова и снова писать showLoading и hideLoading
- Поддержка HMR, на основеbabel-plugin-dva-hmrРеализовать HMR для компонентов, маршрутов и моделей
Недостатки Два
- Будущее весьма неопределенно.После того, как dva@3 предложила план годом ранее, чиновник почти перестал поддерживаться.
- Для подавляющего большинства сценариев, которые не особенно сложны,В настоящее время может быть заменен крючками
Применимые сценарии для Два
- Бизнес-сценарий: Между компонентами много коммуникаций, бизнес сложный, и проект требует введения управления состоянием.
- Технологический сценарий: используйте проект написания React Class Component
Два основных понятия
- Поток данных на основе концепции Redux. Взаимодействие пользователя или поведение браузера инициирует действие через диспетчеризацию. Если это синхронное поведение, оно напрямую изменит состояние через редукторы. Если это асинхронное поведение (которое можно назвать побочным эффектом), оно сначала вызовет Эффекты, а затем перетекают к редукторам, чтобы окончательно изменить состояние.
-
На основе основных концепций Redux. включать:
- Данные состояния, обычно объект JavaScript, должны обрабатываться как неизменяемые данные каждый раз во время работы, чтобы гарантировать, что каждый раз это новый объект без ссылочной связи, чтобы обеспечить независимость от состояния и облегчить тестирование и отслеживание изменений.
- Поведение действия, простой объект JavaScript, это единственный способ изменить состояние.
- диспетчеризация, функция, которая запускает действие для изменения состояния.
- Редуктор — это чистая функция, описывающая, как изменить данные, она принимает два параметра: существующий результат и данные, переданные действием, а новое состояние получается через операцию.
- Эффекты (Side Effects) Побочные эффекты, обычно проявляющиеся в виде асинхронных операций. dva Чтобы контролировать работу побочных эффектов, нижний слой вводитredux-sagasВыполнять асинхронное управление потоком из-за использованияСвязанные концепции генератора, поэтому преобразуйте асинхронную запись в синхронную, тем самым преобразуя эффекты в чистые функции.
- Подключите функцию, которая связывает состояние с представлением
-
другие концепции
- Подписка, подписка, отисточникПолучите данные, а затем отправьте требуемое действие в соответствии с условиями.elm. Источником данных может быть текущее время, подключение к веб-сокету сервера, ввод с клавиатуры, изменения геолокации, изменения истории маршрутизации и т. д.
- Маршрутизатор, интерфейсная маршрутизация, экземпляр dva предоставляет метод маршрутизатора для управления маршрутизацией, используяreact-router.
- Компоненты маршрута, компоненты, которые не имеют ничего общего с логикой данных. Компоненты, для которых обычно требуется Connect Model, — это Route Components, которые организованы в каталоге /routes/, а каталог /components/ — это чистый компонент (Presentational Components, подробности см.Метод проектирования компонентов)
Простейшая структура приложения Два
Без модели
import dva from 'dva';
const App = () => <div>Hello dva</div>;
// 创建应用
const app = dva();
// 注册视图
app.router(() => <App />);
// 启动应用
app.start('#root');
с моделью
// 创建应用
const app = dva();
app.use(createLoading()) // 使用插件
// 注册 Model
app.model({
namespace: 'count',
state: 0,
reducers: {
add(state) { return state + 1 },
},
effects: {
*addAfter1Second(action, { call, put }) {
yield call(delay, 1000);
yield put({ type: 'add' });
},
},
});
// 注册视图
app.router(() => <ConnectedApp />);
// 启动应用
app.start('#root');
Основной принцип Dva и некоторые ключевые реализации
Введение
- Весь проект dva управляется lerna.Найдите файл записи, соответствующий модулю, в package.json каждого пакета, а затем просмотрите соответствующий исходный код.
- dva — это функция, которая возвращает объект приложения.
- В настоящее время основная часть исходного кода dva состоит из двух частей: dva и dva-core. Первый использует компонент высокого порядка React-redux для реализации уровня представления, а второй использует redux-saga для решения уровня модели.
dva
dva делает три важные вещи:
- Прокси-маршрутизатор и методы запуска, создание экземпляра объекта приложения
- Вызовите метод запуска dva-core и одновременно визуализируйте представление.
- Соединение из реакции на редукцию выполняется с помощью реакции-редукции.
// dva/src/index.js
export default function (opts = {}) {
// 1. 使用 connect-react-router 和 history 初始化 router 和 history
// 通过添加 redux 的中间件 react-redux-router,强化了 history 对象的功能
const history = opts.history || createHashHistory();
const createOpts = {
initialReducer: {
router: connectRouter(history),
},
setupMiddlewares(middlewares) {
return [routerMiddleware(history), ...middlewares];
},
setupApp(app) {
app._history = patchHistory(history);
},
};
// 2. 调用 dva-core 里的 create 方法 ,函数内实例化一个 app 对象。
const app = create(opts, createOpts);
const oldAppStart = app.start;
// 3. 用自定义的 router 和 start 方法代理
app.router = router;
app.start = start;
return app;
// 3.1 绑定用户传递的 router 到 app._router
function router(router) {
invariant(
isFunction(router),
`[app.router] router should be function, but got ${typeof router}`,
);
app._router = router;
}
// 3.2 调用 dva-core 的 start 方法,并渲染视图
function start(container) {
// 对 container 做一系列检查,并根据 container 找到对应的DOM节点
if (!app._store) {
oldAppStart.call(app);
}
const store = app._store;
// 为HMR暴露_getProvider接口
// ref: https://github.com/dvajs/dva/issues/469
app._getProvider = getProvider.bind(null, store, app);
// 渲染视图
if (container) {
render(container, store, app, app._router);
app._plugin.apply('onHmr')(render.bind(null, container, store, app));
} else {
return getProvider(store, this, this._router);
}
}
}
function getProvider(store, app, router) {
const DvaRoot = extraProps => (
<Provider store={store}>{router({ app, history: app._history, ...extraProps })}</Provider>
);
return DvaRoot;
}
function render(container, store, app, router) {
const ReactDOM = require('react-dom'); // eslint-disable-line
ReactDOM.render(React.createElement(getProvider(store, app, router)), container);
}
Мы также можем обнаружить, что приложение инициализируется через create(opts, createOpts), где opts — это конфигурация, предоставляемая пользователю, createOpts — это конфигурация, предоставляемая разработчику, а настоящий метод создания реализован в dva-core.
dva-core
dva-core выполняет основные функции:
-
Завершите создание экземпляра приложения с помощью метода create и предоставьте доступ к трем интерфейсам использования, модели и запуска.
-
делается стартовым методом
-
store 的初始化 -
models 和 effects 的封装,收集并运行 sagas -
运行所有的 model.subscriptions -
暴露 app.model、app.unmodel、app.replaceModel 三个接口
dva-core create
эффект:Завершите создание экземпляра приложения и предоставьте доступ к трем интерфейсам использования, модели и запуска.
// dva-core/src/index.js
const dvaModel = {
namespace: '@@dva',
state: 0,
reducers: {
UPDATE(state) {
return state + 1;
},
},
};
export function create(hooksAndOpts = {}, createOpts = {}) {
const { initialReducer, setupApp = noop } = createOpts; // 在dva/index.js中构造了createOpts对象
const plugin = new Plugin(); // dva-core中的插件机制,每个实例化的dva对象都包含一个plugin对象
plugin.use(filterHooks(hooksAndOpts)); // 将dva(opts)构造参数opts上与hooks相关的属性转换成一个插件
const app = {
_models: [prefixNamespace({ ...dvaModel })],
_store: null,
_plugin: plugin,
use: plugin.use.bind(plugin), // 暴露的use方法,方便编写自定义插件
model, // 暴露的model方法,用于注册model
start, // 原本的start方法,在应用渲染到DOM节点时通过oldStart调用
};
return app;
}
dva-core start
эффект:
- Инкапсулируйте модели и эффекты, собирайте и запускайте саги
- Завершите инициализацию магазина
- запустить все model.subscriptions
- Предоставьте три интерфейса: app.model, app.unmodel и app.replaceModel.
function start() {
const sagaMiddleware = createSagaMiddleware();
const promiseMiddleware = createPromiseMiddleware(app);
app._getSaga = getSaga.bind(null);
const sagas = [];
const reducers = { ...initialReducer };
for (const m of app._models) {
// 把每个 model 合并为一个reducer,key 是 namespace 的值,value 是 reducer 函数
reducers[m.namespace] = getReducer(m.reducers, m.state, plugin._handleActions);
if (m.effects) {
// 收集每个 effects 到 sagas 数组
sagas.push(app._getSaga(m.effects, m, onError, plugin.get('onEffect'), hooksAndOpts));
}
}
// 初始化 Store
app._store = createStore({
reducers: createReducer(),
initialState: hooksAndOpts.initialState || {},
plugin,
createOpts,
sagaMiddleware,
promiseMiddleware,
});
const store = app._store;
// Extend store
store.runSaga = sagaMiddleware.run;
store.asyncReducers = {};
// Execute listeners when state is changed
const listeners = plugin.get('onStateChange');
for (const listener of listeners) {
store.subscribe(() => {
listener(store.getState());
});
}
// Run sagas, 调用 Redux-Saga 的 createSagaMiddleware 创建 saga中间件,调用中间件的 run 方法所有收集起来的异步方法
// run方法监听每一个副作用action,当action发生的时候,执行对应的 saga
sagas.forEach(sagaMiddleware.run);
// Setup app
setupApp(app);
// 运行 subscriptions
const unlisteners = {};
for (const model of this._models) {
if (model.subscriptions) {
unlisteners[model.namespace] = runSubscription(model.subscriptions, model, app, onError);
}
}
// 暴露三个 Model 相关的接口,Setup app.model and app.unmodel
app.model = injectModel.bind(app, createReducer, onError, unlisteners);
app.unmodel = unmodel.bind(app, createReducer, reducers, unlisteners);
app.replaceModel = replaceModel.bind(app, createReducer, reducers, unlisteners, onError);
/**
* Create global reducer for redux.
*
* @returns {Object}
*/
function createReducer() {
return reducerEnhancer(
combineReducers({
...reducers,
...extraReducers,
...(app._store ? app._store.asyncReducers : {}),
}),
);
}
}
}
маршрутизация
В предыдущем методе dva.start мы увидели createOpts и узнали, что соответствующий метод вызывался в разное время при запуске dva-core.
import * as routerRedux from 'connected-react-router';
const { connectRouter, routerMiddleware } = routerRedux;
const createOpts = {
initialReducer: {
router: connectRouter(history),
},
setupMiddlewares(middlewares) {
return [routerMiddleware(history), ...middlewares];
},
setupApp(app) {
app._history = patchHistory(history);
},
};
где initialReducer и setupMiddlewares вызываются при инициализации хранилища, а затем вызывается setupApp
Вы можете увидеть конфигурацию редуктора и промежуточного программного обеспечения, связанного с маршрутизатором. И connectRouter, и routerMiddleware используют библиотеку connect-react-router. Основная идея состоит в том, чтобы рассматривать переходы маршрутизации как особое действие.
Различия между Dva и React, React-Redux, Redux-Saga
Родной ответ
В соответствии с официальными рекомендациями React, если должно происходить взаимодействие между несколькими компонентами, то состояние (т.е. данные) поддерживается на минимальном родительском узле контракта этого компонента,
то есть
И он сам не поддерживает никакого состояния, и он полностью определяется родительским узлом, передающим реквизиты для определения его представления, что является существованием чистой функции, а именно: Чистый компонент
React-Redux
По сравнению с приведенным выше рисунком, несколько очевидных улучшений:
- Из него извлекается состояние и логика страницы, которые становятся самостоятельным хранилищем, а логика страницы — редюсером.
- Оба являются Pure Component, и к ним очень удобно добавлять обёртку через метод connect для установления соединения с хранилищем: через диспетчеризацию можно внедрить действие в хранилище, чтобы запросить изменение состояния хранилища, а при в то же время подписывайтесь на состояние хранилища. Изменение, как только состояние изменяется, подключенный компонент также обновляется.
- Процесс использования диспетчеризации для отправки действий в хранилище может быть перехвачен, естественно сюда могут быть добавлены различные Middleware для реализации различных пользовательских функций, например: логирование
Таким образом, каждая часть выполняет свои обязанности, степень связанности ниже, степень повторного использования выше, а масштабируемость лучше.
Redux-Saga
Потому что мы можем использовать Middleware для перехвата действий, чтобы асинхронные сетевые операции были очень удобными, просто делаем Middleware, здесь мы используем библиотеку классов redux-saga, например:
- Нажмите кнопку «Создать задачу», чтобы инициировать действие с типом == addTodo.
- saga перехватывает это действие и инициирует http-запрос.Если запрос будет успешным, он продолжит отправлять действие типа == addTodoSucc редюсеру, указывая, что создание прошло успешно, в противном случае отправить действие типа == addTodoFail
Dva
С предвосхищением предыдущих трех шагов появление Dva произойдет естественным образом.Как говорится на официальном сайте Dva, Dva основана на передовом опыте React + Redux + Saga и имеет три вклада в улучшение опыта кодирования:
- Объедините магазин и сагу в одну концепцию модели и запишите ее в файл js.
- Добавлены подписки для сбора действий из других источников, таких как действия клавиатуры и т. д.
- Модель написана очень просто, аналогично DSL (предметно-ориентированному языку), что может улучшить погружение в программирование и повысить эффективность.
соглашение о конфигурации
app.model({
namespace: 'count',
state: {
record: 0,
current: 0,
},
reducers: {
add(state) {
const newCurrent = state.current + 1;
return { ...state,
record: newCurrent > state.record ? newCurrent : state.record,
current: newCurrent,
};
},
minus(state) {
return { ...state, current: state.current - 1};
},
},
effects: {
*add(action, { call, put }) {
yield call(delay, 1000);
yield put({ type: 'minus' });
},
},
subscriptions: {
keyboardWatcher({ dispatch }) {
key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) });
},
},
});
Идеи, лежащие в основе Dva, которые стоит изучить
Справочник API Дваchoo, концепция исходит от вяза.
- Философия Чу: программирование должно быть легким и увлекательным, а API должен выглядеть простым и удобным в использовании.
We believe programming should be fun and light, not stern and stressful. It's cool to be cute; using serious words without explaining them doesn't make for better results - if anything it scares people off. We don't want to be scary, we want to be nice and fun, and thencasually_be the best choice around._Real casually.
We believe frameworks should be disposable, and components recyclable. We don't want a web where walled gardens jealously compete with one another. By making the DOM the lowest common denominator, switching from one framework to another becomes frictionless. Choo is modest in its design; we don't believe it will be top of the class forever, so we've made it as easy to toss out as it is to pick up.
We don't believe that bigger is better. Big APIs, large complexities, long files - we see them as omens of impending userland complexity. We want everyone on a team, no matter the size, to fully understand how an application is laid out. And once an application is built, we want it to be small, performant and easy to reason about. All of which makes for easy to debug code, better results and super smiley faces.
- Концепты от Elm:
- Подписка, подписка, отисточникДля получения данных источником данных может быть текущее время, подключение к серверу через веб-сокет, ввод с клавиатуры, изменения геолокации, изменения истории маршрутизации и т. д.
приложение
Разработка и выбор архитектуры клиентского приложения Alipay
Добро пожаловать в "Byte Front End ByteFE" Контактный адрес электронной почты для доставки резюме "tech@bytedance.com"