Некоторое время назад мы также изучили и объяснили основы использования фреймворка Redux, но многие студенты в группе по обмену отзывались о том, что фреймворк redux сложен для понимания.После прочтения я все еще был в замешательстве и не знал, как Чтобы начать, многие студенты обратились к выбору использования структуры dva. Фактически, фреймворк dva представляет собой комбинацию фреймворка redux и фреймворка redux-saga.Он переупаковывает несколько часто используемых фреймворков обработки данных, что обеспечивает удобство для пользователей с точки зрения использования.Давайте кратко представим базовый API и базовое использование фреймворка dva.
Демонстрация запущенных рендеров
Подобно объяснению фреймворка Redux, автор по-прежнему предоставляет два классических демонстрационных примера, CounterApp и TodoList, чтобы помочь новичкам лучше понять и использовать
Демонстрационный адрес
- CounterApp
- TodoList
Происхождение слова два
У D.Va есть мощный мех с двумя полностью автоматическими термоядерными пушками ближнего действия, двигателями, которые позволяют меху перепрыгивать через врагов или препятствия, и защитной матрицей, которая может защищать от дальних атак спереди. — из Овервотч.
официальный адрес два
GitHub.com/But VA Technologies/DV Ah/No…
двухъядерный API
- app = dva(opts)
Создайте приложение и верните экземпляр dva (Примечание: dva поддерживает несколько экземпляров)
opts
Содержит следующую конфигурацию:
-
history
: История, назначенная для маршрутизации, по умолчанию — hashHistory. -
initialState
: Укажите исходные данные, приоритет выше, чем состояние в модели, по умолчанию {}
Если настроеноhistory
Для browserHistory создание объекта dva можно записать следующим образом
import createHistory from 'history/createBrowserHistory';
const app = dva({
history: createHistory(),
})
Кроме того, для простоты использования, все хуки также могут быть настроены в опциях, ниже перечислены все настраиваемые свойства:
const app = dva({
history,
initialState,
onError,
onAction,
onStateChange,
onReducer,
onEffect,
onHmr,
extraReducers,
extraEnhancers,
})
- app.use(hooks)
Настройте хуки или зарегистрируйте плагины. (Плагин наконец-то возвращает хуки)
например, регистрацияdva-loading
Примеры плагинов:
import createLoading from 'dva-loading'
...
app.use(createLoading(opts))
hooks
Содержит следующие элементы конфигурации:
1,onError((err, dispatch) => {})
Запускается, когда ошибка выполнения эффекта или подписка активно выдает ошибку завершения, которую можно использовать для управления глобальным состоянием ошибки.
Уведомление: Подписка не добавляет try...catch, поэтому если есть ошибка, то нужно активно кидать ошибку через второй параметр done
пример:
app.model({
subscriptions: {
setup({ dispatch }, done) {
done(e)
},
},
})
Если мы используем компоненты antd, простейшая глобальная обработка ошибок обычно делает это:
import { message } from 'antd'
const app = dva({
onError(e) {
message.error(e.message, 3)
},
})
2,onAction(fn | fn[])
Запускается при отправке действия, используется для регистрации промежуточного ПО Redux. Функция поддержки или формат массива функций
Например, мы хотим печатать логи через redux-logger:
import createLogger from 'redux-logger';
const app = dva({
onAction: createLogger(opts),
})
3.onStateChange(fn)
state
Запускается при изменении, может использоваться для синхронизации состояния с локальным хранилищем, на стороне сервера и т. д.
4.onReducer(fn)
Инкапсулируйте выполнение редюсера, например, реализацию повтора/отмены с помощью redux-undo:
import undoable from 'redux-undo';
const app = dva({
onReducer: reducer => {
return (state, action) => {
const undoOpts = {};
const newState = undoable(reducer, undoOpts)(state, action);
// 由于 dva 同步了 routing 数据,所以需要把这部分还原
return { ...newState, routing: newState.present.routing };
},
},
})
5.onEffect(fn)
Инкапсулирует выполнение эффекта. Напримерdva-loading
На основании этого реализована автоматическая обработка состояния загрузки
6.onHmr(fn)
Горячая замена, в настоящее время используется дляbabel-plugin-dva-hmr
7.extraReducers
Укажите дополнительные редукторы, такие какredux-form
Необходимо указать дополнительные редукторы формы:
import { reducer as formReducer } from 'redux-form'
const app = dva({
extraReducers: {
form: formReducer,
},
})
- app.model(model)
Регистрация модели, эта операция является основной операцией в dva, которая будет подробно объяснена ниже.
- app.unmodel(namespace)
Отмените регистрацию модели, очистите редукторы, эффекты и подписки. Если подписка не вернет функцию unlisten, использование app.unmodel выдаст предупреждение⚠️
- app.router(({ history, app }) => RouterConfig)
Зарегистрируйте таблицу маршрутизации, этот шаг операции также очень важен в dva.
// 注册路由
app.router(require('./router'))
// 路由文件
import { Router, Route } from 'dva/router';
import IndexPage from './routes/IndexPage'
import TodoList from './routes/TodoList'
function RouterConfig({ history }) {
return (
<Router history={history}>
<Route path="/" component={IndexPage} />
<Route path='/todoList' components={TodoList}/>
</Router>
)
}
export default RouterConfig
Конечно, если мы хотим решить проблему динамической загрузки компонентов, наш файл роутинга можно записать и так:
import { Router, Switch, Route } from 'dva/router'
import dynamic from 'dva/dynamic'
function RouterConfig({ history, app }) {
const IndexPage = dynamic({
app,
component: () => import('./routes/IndexPage'),
})
const Users = dynamic({
app,
models: () => [import('./models/users')],
component: () => import('./routes/Users'),
})
return (
<Router history={history}>
<Switch>
<Route exact path="/" component={IndexPage} />
<Route exact path="/users" component={Users} />
</Switch>
</Router>
)
}
export default RouterConfig
вdynamic(opts)
Опция содержит три элемента конфигурации:
-
opts
- app: экземпляр dva, необходимый для загрузки моделей
- модели: функция, которая возвращает массив обещаний, обещание возвращает модель dva
- Компонент: возвращает функцию обещания, обещание возвращает React Compot
-
app.start(selector?)
Запустите приложение, селектор необязателен, если параметр селектора отсутствует, оно вернет функцию, которая возвращает элемент JSX.
app.start('#root')
Так когда не добавлять селектор? Общие сценарии включают в себя тестирование, поддержку node, react-native и интернационализацию i18n.
Например, пример поддержки интернационализации через react-intl:
import { IntlProvider } from 'react-intl'
...
const App = app.start()
ReactDOM.render(<IntlProvider><App /></IntlProvider>, htmlElement)
Базовый слой в фреймворке dva: Модель
Ниже приведен простой общийmodel
запись файла
/** Created by guangqiang on 2017/12/17. */
import queryString from 'query-string'
import * as todoService from '../services/todo'
export default {
namespace: 'todo',
state: {
list: []
},
reducers: {
save(state, { payload: { list } }) {
return { ...state, list }
}
},
effects: {
*addTodo({ payload: value }, { call, put, select }) {
// 模拟网络请求
const data = yield call(todoService.query, value)
console.log(data)
let tempList = yield select(state => state.todo.list)
let list = []
list = list.concat(tempList)
const tempObj = {}
tempObj.title = value
tempObj.id = list.length
tempObj.finished = false
list.push(tempObj)
yield put({ type: 'save', payload: { list }})
},
*toggle({ payload: index }, { call, put, select }) {
// 模拟网络请求
const data = yield call(todoService.query, index)
let tempList = yield select(state => state.todo.list)
let list = []
list = list.concat(tempList)
let obj = list[index]
obj.finished = !obj.finished
yield put({ type: 'save', payload: { list } })
},
*delete({ payload: index }, { call, put, select }) {
const data = yield call(todoService.query, index)
let tempList = yield select(state => state.todo.list)
let list = []
list = list.concat(tempList)
list.splice(index, 1)
yield put({ type: 'save', payload: { list } })
},
*modify({ payload: { value, index } }, { call, put, select }) {
const data = yield call(todoService.query, value)
let tempList = yield select(state => state.todo.list)
let list = []
list = list.concat(tempList)
let obj = list[index]
obj.title = value
yield put({ type: 'save', payload: { list } })
}
},
subscriptions: {
setup({ dispatch, history }) {
// 监听路由的变化,请求页面数据
return history.listen(({ pathname, search }) => {
const query = queryString.parse(search)
let list = []
if (pathname === 'todoList') {
dispatch({ type: 'save', payload: {list} })
}
})
}
}
}
Объект модели содержит 5 важных свойств:
- namespace
Пространство имен модели, которое также является ее атрибутом в глобальном состоянии, может использовать только строки и не поддерживает передачу.
способ создания многоуровневых пространств имен
- state
Начальное значение редуктора, приоритет ниже переданного в dva()opts.initialState
Например:
const app = dva({
initialState: { count: 1 },
});
app.model({
namespace: 'count',
state: 0,
})
В это время вapp.start()
После того, как state.count равен 1
- reducers
Определите редюсер в формате ключ/значение для обработки синхронных операций, единственное место, где состояние может быть изменено, запускается действием
Формат(state, action) => newState
или[(state, action) => newState, enhancer]
namespace: 'todo',
state: {
list: []
},
// reducers 写法
reducers: {
save(state, { payload: { list } }) {
return { ...state, list }
}
}
- effects
Определите эффекты в формате ключ/значение. Используется для обработки асинхронных операций и бизнес-логики без прямого изменения состояния. Запускается действием, может запускать действие, может взаимодействовать с сервером, может получать глобальные данные о состоянии и т. д.
Уведомление:Идея дизайна модуля эффектов в фреймворке dva исходит отredux-saga
рамки, если учащиесяredux-saga
Если вы не знакомы с фреймворком, вы можете проверить авторское объяснение redux-saga:woo woo Краткое описание.com/afraid/7 wipe 18 oh 8 of…
Формат*(action, effects) => void
или[*(action, effects) => void, { type }]
Существует четыре типа типа:
1,takeEvery
2,takeLatest
3.throttle
4.watcher
// effects 写法
effects: {
*addTodo({ payload: value }, { call, put, select }) {
// 模拟网络请求
const data = yield call(todoService.query, value)
console.log(data)
let tempList = yield select(state => state.todo.list)
let list = []
list = list.concat(tempList)
const tempObj = {}
tempObj.title = value
tempObj.id = list.length
tempObj.finished = false
list.push(tempObj)
yield put({ type: 'save', payload: { list }})
},
*toggle({ payload: index }, { call, put, select }) {
// 模拟网络请求
const data = yield call(todoService.query, index)
let tempList = yield select(state => state.todo.list)
let list = []
list = list.concat(tempList)
let obj = list[index]
obj.finished = !obj.finished
yield put({ type: 'save', payload: { list } })
},
*delete({ payload: index }, { call, put, select }) {
const data = yield call(todoService.query, index)
let tempList = yield select(state => state.todo.list)
let list = []
list = list.concat(tempList)
list.splice(index, 1)
yield put({ type: 'save', payload: { list } })
},
*modify({ payload: { value, index } }, { call, put, select }) {
const data = yield call(todoService.query, value)
let tempList = yield select(state => state.todo.list)
let list = []
list = list.concat(tempList)
let obj = list[index]
obj.title = value
yield put({ type: 'save', payload: { list } })
}
}
- subscriptions
Определите подписку в формате ключа / значения, подписка - это подписка, используемая для подписки на источник данных, а затем отправлять соответствующие действия по мере необходимости
Выполняется в app.start(), источник данных может быть текущим временем, подключением к веб-сокету сервера, вводом с клавиатуры, изменениями геолокации, изменениями истории маршрутизации и т. д.
Формат({ dispatch, history }, done) => unlistenFunction
Уведомление: если вы хотите использовать app.unmodel(), подписка должна возвращать метод unlisten для отмены подписки на данные.
// subscriptions 写法
subscriptions: {
setup({ dispatch, history }) {
// 监听路由的变化,请求页面数据
return history.listen(({ pathname, search }) => {
const query = queryString.parse(search)
let list = []
if (pathname === 'todoList') {
dispatch({ type: 'save', payload: {list} })
}
})
}
}
Разница между использованием фреймворка dva и прямым использованием redux
- использовать избыточность
Actions.js Файл
export const REQUEST_TODO = 'REQUEST_TODO';
export const RESPONSE_TODO = 'RESPONSE_TODO';
const request = count => ({type: REQUEST_TODO, payload: {loading: true, count}});
const response = count => ({type: RESPONSE_TODO, payload: {loading: false, count}});
export const fetch = count => {
return (dispatch) => {
dispatch(request(count));
return new Promise(resolve => {
setTimeout(() => {
resolve(count + 1);
}, 1000)
}).then(data => {
dispatch(response(data))
})
}
}
файл редуктора.js
import { REQUEST_TODO, RESPONSE_TODO } from './actions';
export default (state = {
loading: false,
count: 0
}, action) => {
switch (action.type) {
case REQUEST_TODO:
return {...state, ...action.payload};
case RESPONSE_TODO:
return {...state, ...action.payload};
default:
return state;
}
}
файл app.js
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actions from './actions';
const App = ({fetch, count, loading}) => {
return (
<div>
{loading ? <div>loading...</div> : <div>{count}</div>}
<button onClick={() => fetch(count)}>add</button>
</div>
)
}
function mapStateToProps(state) {
return state;
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actions, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
файл index.js
import { render } from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux'
import thunkMiddleware from 'redux-thunk';
import reducer from './app/reducer';
import App from './app/app';
const store = createStore(reducer, applyMiddleware(thunkMiddleware));
render(
<Provider store={store}>
<App/>
</Provider>
,
document.getElementById('app')
)
- использовать два
файл model.js
export default {
namespace: 'demo',
state: {
loading: false,
count: 0
},
reducers: {
request(state, payload) {
return {...state, ...payload};
},
response(state, payload) {
return {...state, ...payload};
}
},
effects: {
*'fetch'(action, {put, call}) {
yield put({type: 'request', loading: true});
let count = yield call((count) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(count + 1);
}, 1000);
});
}, action.count);
yield put({
type: 'response',
loading: false,
count
});
}
}
}
файл app.js
import React from 'react'
import { connect } from 'dva';
const App = ({fetch, count, loading}) => {
return (
<div>
{loading ? <div>loading...</div> : <div>{count}</div>}
<button onClick={() => fetch(count)}>add</button>
</div>
)
}
function mapStateToProps(state) {
return state.demo;
}
function mapDispatchToProps(dispatch) {
return {
fetch(count){
dispatch({type: 'demo/fetch', count});
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
файл index.js
import dva from 'dva';
import model from './model';
import App from './app';
const app = dva();
app.use({});
app.model(model);
app.router(() => <App />);
app.start();
Мы находим структуру кода асинхронного счетчика двумя указанными выше способами:
-
Использование редукса должно быть разделено
action
модули иreducer
модуль -
два будет
action
а такжеreducer
упаковано вmodel
, асинхронный процесс обрабатывает Генератор
Суммировать
В этой статье в основном объясняется разработка общих API-интерфейсов и некоторые навыки использования в среде dva.Если вы хотите просмотреть более подробные API-интерфейсы, обратитесь к официальной документации dva:github.com/dvajs/dva
Если студенты все еще не знают, как использовать фреймворк dva после прочтения учебника, рекомендуется запустить демонстрационный пример, предоставленный автором, чтобы совместить обучение
Предложение автора: Учащиеся могут поделиться тем, что автор объяснил ранее.редукционная структураСравните с фреймворком два, чтобы научиться понимать, чтобы различия и связи между ними были яснее.
время благосостояния
- Адрес OneM авторского проекта с открытым исходным кодом React Native (разработан путем построения фреймворка в соответствии со стандартами корпоративной разработки):GitHub.com/LightIntensity-…: добро пожаловать друзьяstar
- Краткая домашняя страница автора: содержит более 60 технических статей, связанных с разработкой RN.у-у-у. Краткое описание.com/U/023338566…Добро пожаловать друзья:уделять больше внимания,как более
- Автор группы технического обмена React Native QQ:620792950Приглашаем друзей присоединиться к группе для обмена и обучения
- дружеское напоминание:При разработке возникли технические проблемы, связанные с RN, и друзья могут присоединиться к группе обмена (620792950), задавайте вопросы в группе, общайтесь и учитесь друг у друга. Группа обмена также регулярно обновляет последние учебные материалы RN для всех, спасибо за вашу поддержку!