1. Уведомление
Полнотекстовый стек технологий
- Основная библиотека: React-Native@0.54.0
- Маршрутная навигация: React-Native-Navigation
- Управление состоянием: Redux, Redux-Thunk, Redux-Saga, Redux-persist
- Статический тест: поток
Эта статья подходит для студентов, которые имеют некоторый опыт использования семейства React, но не очень хорошо знакомы с настройкой приложения с нуля и хотят попробовать создать приложение с нуля.
Это моя собственная ситуация, я участвовал в проекте в середине, и я никогда не чувствовал, что контролирую общую ситуацию, поэтому в этот раз, воспользовавшись возможностью реконструкции проекта, я также следовал конфигурации с нуля и Я записал это. Я надеюсь поделиться с моими одноклассниками. Давайте учиться вместе. Если что-то не так, я надеюсь, что вы укажете на это, или если есть лучший способ улучшить, добро пожаловать в общение.
Если у вас есть время, лучше всего сделать это самостоятельно, что более полезно для того, как построить настоящий проект.
Весь проект был загружен на github. Ленивые практические студенты могут просто клонировать его и следовать. Добро пожаловать, чтобы совершенствоваться вместе. Текущая первоначальная идея состоит в том, чтобы помочь некоторым студентам. Если позже будет время, он может быть улучшен в более надежный RN Базовая структура, вы можете напрямую клонировать проект разработки
Здесь представлены только конфигурация и основное использование для каждой библиотеки или контента.
Физическая среда: mac, xcode
Студенты оконной системы тоже могут его посмотреть, но вам нужно хорошо поработать со средой разработки симулятора самостоятельно.
2. Быстро создайте приложение RN
Если среда базовой конфигурации RN не настроена, щелкните ссылку выше, чтобы перейти на официальный веб-сайт для настройки.
react-native init ReactNativeNavigationDemo
cd ReactNativeNavigationDemo
react-native run-ios
Поскольку с самого начала планировалось использовать React-Native-Navigation в качестве навигационной библиотеки, название немного длинное, давайте выберем то, что вам больше нравится.
После успеха вы увидите этот интерфейс
В это время вы можете посмотреть на структуру каталогов, RN автоматически интегрирует файлы конфигурации babel, git и flow, что очень удобно.
3. Навигация по маршруту: React-Native-Navigation
Зачем использовать React Native Navigation вместо React Navigation?
В настоящее время это единственный плагин, который использует собственный код для реализации навигатора.После использования анимация push/pop навигатора будет отделена от потока js и обработана потоком собственного пользовательского интерфейса, а эффект вырезания экрана будет таким же плавным. как оригинальная экология.Не будет эффекта заикания анимации нарезки экрана навигатора, вызванного рендерингом потока js, а также в плагине есть встроенная панель вкладок, которая реализует оригинальную экологическую версию.
Студенты, хорошо владеющие английским языком, могут посмотреть официальный документ, но если вы действительно его не понимаете, вы можете посмотреть на картинку ниже.
iOS должен использовать xcode.Если вы этого не сделали, вам может показаться, что это немного сложно, поэтому я запустил процесс и сделал снимок экрана.
Что касается настройки андроида, то документация очень понятная, поэтому запускать не буду.
1. Установка
yarn add react-native-navigation@latest
2. Добавьте файл проекта xcode
Файл пути на рисунке относится к./node_modules/react-native-navigation/ios/ReactNativeNavigation.xcodeproj
3. Добавьте файл проекта, добавленный выше, в библиотеку.
4. Добавьте путь
$(SRCROOT)/../node_modules/react-native-navigation/iosПомните, что точка 5 на картинке установлена вrecursive
5. Измените файл ios/[имя приложения]/AppDelegate.m.
Замените все содержимое файла следующим кодом
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "RCCManager.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
#ifdef DEBUG
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
[[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation launchOptions:launchOptions];
return YES;
}
@end
6. Основное использование
1. Сначала создайте несколько новых страниц, структура как на рисунке
cd src
mkdir home mine popularize
touch home/index.js mine/index.js popularize/index.js
каждыйindex.jsФайлы имеют одинаковую структуру, очень простую
import React, { Component } from 'react';
import { Text, View } from 'react-native';
type Props = {};
export default class MineHome extends Component<Props> {
render() {
return (
<View>
<Text>MineHome</Text>
</View>
);
}
}
2,src/index.jsРегистрация всех страниц, единое управление
import { Navigation } from 'react-native-navigation';
import Home from './home/index';
import PopularizeHome from './popularize/index';
import MineHome from './mine/index';
// 注册所有的页面
export function registerScreens() {
Navigation.registerComponent('home',() => home);
Navigation.registerComponent('popularize',() => popularize);
Navigation.registerComponent('mine',() => mine);
}
Вставьте предложение здесь.Если вы хотите представить Redux, вы можете напрямую перейти в магазин и поставщик здесь.
export function registerScreens(store,Provider) {
Navigation.registerComponent('home',() => PageOne,store,Provider)
}
3.App.jsФайл изменяет способ запуска приложения и немного изменяет стиль страницы.
import { Navigation } from 'react-native-navigation';
import { registerScreens } from './src/screen/index';
// 执行注册页面方法
registerScreens();
// 启动app
Navigation.startTabBasedApp({
tabs: [
{
label: 'home',
screen: 'home',
title: '首页',
icon: require('./src/assets/home.png'),
},
{
screen: 'popularize',
title: '推广',
icon: require('./src/assets/add.png'),
iconInsets: {
top: 5,
left: 0,
bottom: -5,
right: 0
},
},
{
label: 'mine',
screen: 'mine',
title: '我',
icon: require('./src/assets/mine.png'),
}
],
appStyle: {
navBarBackgroundColor: '#263136',//顶部导航栏背景颜色
navBarTextColor: 'white'//顶部导航栏字体颜色
},
tabsStyle: {
tabBarButtonColor: '#ccc',//底部按钮颜色
tabBarSelectedButtonColor: '#08cb6a',//底部按钮选择状态颜色
tabBarBackgroundColor: '#E6E6E6'//顶部条背景颜色
}
});
Запустите приложение, текущий интерфейс, который может видеть симулятор
7, переход на страницу и параметры передачи
Создайте новый в экранной/домашней папкеNextPage.jsфайл, не забудьтеsrc/screen/index.jsЗарегистрируйтесь на этой странице
Navigation.registerComponent('nextPage', () => NextPage, store, Provider);
затем вsrc/screen/home/index.jsДобавьте кнопку перехода в файл и передайте данные реквизита
4. Управление состоянием: Redux
редукционная китайская документация
1. Инициализация
1. Установка
yarn add redux react-redux
2. Строительство каталога
В настоящее время существует два распространенных способа создания каталогов:
Один из них — написать действие и редьюсер одной и той же страницы в одной и той же папке (это можно назвать компонентизацией), как показано ниже.
Второй — поместить все экшены в одну папку и все редукторы в одну папку для унифицированного управления
Эти два метода имеют свои преимущества и недостатки, я не буду их здесь рассматривать, здесь я использую второй метод.
Одна операция лютая как тигр, сначала создай разные папки и файлы
cd src
mkdir action reducer store
touch action/index.js reducer/index.js store/index.js
touch action/home.js action/mine.js action/popularize.js
touch reducer/home.js reducer/mine.js reducer/popularize.js
После ввода вышеуказанной команды структура каталогов должна выглядеть так: Каждая страница имеет свои собственные файлы действий и редьюсеров, но все они создаютсяindex.jsЦентрализованное управление выходными файлами
Что касается порядка, в котором создаются эти три части контента, теоретически сначала должно быть хранилище, затем редюсер, а затем действие.
Но после того, как много напишешь, становится легче писать по желанию, какое тебе удобно писать первым.
По своим привычкам я люблю писать с нуля, например Если объединённый редюсер будет внедряться в магазин, то сначала я напишу редьюсер.
import combinedReducer from '../reducer'
Но перед написанием редьюсера кажется, что сначала нужно ввести действие, поэтому я могу запустить, чтобы сначала написать действие.
Правильный порядок написания здесь не обсуждается, я пока буду писать по своим привычкам.
3. действие
Мне нравится централизованная модель управления, поэтому все действия я буду осуществлять централизованно. index.jsфайл как общий вывод
Здесь определены все константы типа действия.
// home页面
export const HOME_ADD = 'HOME_ADD';
export const HOME_CUT = 'HOME_CUT';
// mine页面
export const MINE_ADD = 'MINE_ADD';
export const MINE_CUT = 'MINE_CUT';
// popularize页面
export const POPULARIZE_ADD = 'POPULARIZE_ADD';
export const POPULARIZE_CUT = 'POPULARIZE_CUT';
Затем перейдите к записи файла action.js других соответствующих страниц, здесь в качестве примера используется только домашняя страница, другие страницы не будут записаны, откройтеaction/home.jsдокумент
import * as actionTypes from './index';
export function homeAdd(num) {
return {
type: actionTypes.HOME_ADD,
num
}
}
export function homeCut(num) {
return {
type: actionTypes.HOME_CUT,
num
}
}
Возвращается самый простой объект действия
4. Редуктор
Сначала напишите редьюсер для домашней страницы и откройте его.reducer/home.jsдокумент
То же самое и с другими страницами
import * as actionTypes from '../action/index';
// 初始state,我先随手定义了几个,后面可能会用到
const initState = {
initCount: 0,
name: '',
age: '',
job: ''
}
export default function count(state = initState, action) {
switch (action.type) {
case actionTypes.HOME_ADD:
return {
...state,
...action.initCount:
}
case actionTypes.HOME_CUT:
return {
...state,
...action.initCount
}
default:
return state;
}
}
Затем объедините все дочерние страницы редуктора вreducer/index.jsфайл для централизованного вывода
import homeReducer from './home';
import popularizeReducer from './popularize';
import mineReducer from './mine';
const combineReducers = {
home: homeReducer,
popularize: popularizeReducer,
mine: mineReducer
}
export default combineReducers
5. Создайте магазин
После создания редуктора откройтеstore/index.jsдокумент
import {createStore } from 'redux';
import combineReducers from '../reducer/index';
const store = createStore(combineReducers)
export default store;
это так просто
6. Магазин инъекций
Учащиеся, которые использовали redux, знают, что на сцене есть react-redux, он предоставляет методы Provider и connect.
Как упоминалось ранее, способ внедрения react-native-navigation в redux почти такой же. Но каждую подстраницу необходимо внедрить в магазин, провайдер
src/index.jsизменить, как показано ниже
import { Navigation } from 'react-native-navigation';
import Home from './home/index';
import PopularizeHome from './popularize/index';
import MineHome from './mine/index';
// 注册所有的页面
export function registerScreens(store, Provider) {
Navigation.registerComponent('home', () => Home, store, Provider);
Navigation.registerComponent('popularize', () => PopularizeHome, store, Provider);
Navigation.registerComponent('mine', () => MineHome, store, Provider);
}
App.jsИзменить метод выполнения регистрации страницы
import { Provider } from 'react-redux';
import store from './src/store/index';
// 执行注册页面方法
registerScreens(store, Provider);
2. Испытайте Редукс
Теперь, чтобы испытать сокращение, откройтеsrc/screen/home/index.jsдокумент
импортировать два метода
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
действие импорта
import * as homeActions from '../../action/home';
Определите два метода и соедините их
function mapStateToProps(state) {
return {
home: state.home
};
}
function mapDispatchToProps(dispatch) {
return {
homeActions: bindActionCreators(homeActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Теперь распечатайте на страницеinitCountДавайте посмотрим, насколько подключенный компонент и подстраница, перепрыгнутая из компонента через отправку маршрута, могут пройти черезthis.propsполучить данные
src/screen/home/index.jsПолный код выглядит следующим образом
import React, { Component } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as homeActions from '../../action/home';
type Props = {};
class Home extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text>Home</Text>
<Text>initCount: {this.props.home.initCount}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#ccc',
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
function mapStateToProps(state) {
return {
home: state.home
};
}
function mapDispatchToProps(dispatch) {
return {
homeActions: bindActionCreators(homeActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Как вы можете видеть на странице, данные в дереве состояний были прочитаны, а initCount равен 0.
Попробуем снова добавить и вычесть действия
src/screen/home/index.jsПолный код выглядит следующим образом
import React, { Component } from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as homeActions from '../../action/home';
type Props = {};
class Home extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text>Home</Text>
<Text>initCount: {this.props.home.initCount}</Text>
<TouchableOpacity
style={styles.addBtn}
onPress={() => {
this.props.homeActions.homeAdd({
initCount: this.props.home.initCount + 2
});
}}
>
<Text style={styles.btnText}>加2</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.cutBtn}
onPress={() => {
this.props.homeActions.homeCut({
initCount: this.props.home.initCount - 2
});
}}
>
<Text style={styles.btnText}>减2</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#ccc',
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
addBtn: {
backgroundColor: 'green',
marginVertical: 20,
width: 200,
height: 59,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 10
},
cutBtn: {
backgroundColor: 'red',
width: 200,
height: 59,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 10
},
btnText: {
fontSize: 18,
color: 'white'
}
});
function mapStateToProps(state) {
return {
home: state.home
};
}
function mapDispatchToProps(dispatch) {
return {
homeActions: bindActionCreators(homeActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Нажатие обеих кнопок теперь должно давать обратную связь
Теперь давайте проверим следующее.После того, как эта страница изменила состояние в магазине, будет ли другая страница майна синхронизирована, то есть будут ли общие данные общими?
src/mine/index.jsФайл изменен следующим образом
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as homeActions from '../../action/home';
type Props = {};
class MineHome extends Component<Props> {
render() {
return (
<View>
<Text>initCount: {this.props.home.initCount}</Text>
</View>
);
}
}
function mapStateToProps(state) {
return {
home: state.home
};
}
function mapDispatchToProps(dispatch) {
return {
homeActions: bindActionCreators(homeActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(MineHome);
читать те же данные на этой страницеthis.props.home.initCount, а затем добавьте или вычтите данные на первой домашней странице, а затем посмотрите на мою страницу, вы обнаружите, что initCount также изменяется синхронно
То есть: мы уже занимаемся госуправлением
Вы счастливы здесь?Хотя редукс немного сбивает с толку, если вы будете следовать ему, у вас должен быть определенный план.
5. Отслеживание статуса: Redux-логгер
В это время мы обнаружим, что, хотя состояние является общим, в настоящее время нет способа отслеживать состояние и изменения состояния, вызванные каждым шагом операции.
Но ведь нельзя каждый раз вручную выводить статус в консоль, верно?
Пришло время играть в redux-logger
Выглядит это так, и автоматически выводит статус до и после каждого действия отправки в консоль
Смотрите официальную документацию по конкретному использованию, это очень просто, достаточно перейти к коду
Установить
yarn add redux-logger
Он используется в качестве промежуточного программного обеспечения, пожалуйста, обратитесь к использованию промежуточного программного обеспечения.редукционная китайская документациячек
store/index.jsФайл изменен следующим образом
import { createStore, applyMiddleware } from 'redux';
import combineReducers from '../reducer/index';
import logger from 'redux-logger';
const store = createStore(combineReducers, applyMiddleware(logger));
export default store;
Command+R, чтобы обновить симулятор, снова нажмите +2 и посмотрите, выглядит ли консоль следующим образом?
Далее, каждый раз, когда отправляется действие, консоль автоматически распечатывает его.
6. Асинхронное управление: Redux-Thunk
базовое понимание
Что такое redux-thunk, пожалуйста, переместитеredux-thunk github
Отправная точка: компонент должен быть безразличен к синхронным или асинхронным действиям, и нет необходимости явно передавать диспетчеризацию при вызове асинхронных действий.
Используя указанное промежуточное ПО, функция создания действия может возвращать функции в дополнение к объекту действия. В этот момент функция создания действия становится преобразователь
Когда создатель действия возвращает функцию, эта функция будет выполняться промежуточным программным обеспечением Redux Thunk. Эта функция не обязательно должна быть чистой, она также может иметь побочные эффекты, включая выполнение асинхронных запросов API. Эта функция также может отправлять действия, как и определенные ранее синхронные действия. > Преимущество thunk в том, что его результат может быть отправлен снова
Установить
yarn add redux-thunk
ввести в магазин
В качестве промежуточного программного обеспечения он используется так же, как и вышеупомянутый регистратор,stroe/index.jsимпортировать напрямую
import thunk from 'redux-thunk';
middleware.push(thunk);
Как пользоваться
action/home.jsФайл изменен следующим образом
import post from '../utils/fetch';
export function getSomeData() {
return dispatch => {
post('/get/data',{}, res => {
const someData = res.data.someData;
dispatch({
type: actionTypes.HOME_GET_SOMEDATA,
someData
})
})
}
}
Не по теме: инкапсуляция сообщения функции запроса
Вставьте здесь предложение об инкапсуляции поста функции запроса (следующее является упрощенной версией, сохранена только основная идея)
cd src
mkdir utils
touch utils/fetch.js
Общие методы и функции инкапсулированы в папке utils.
utils/fetch.jsФайл выглядит следующим образом
export default function post(url, data, sucCB, errCB) {
// 域名、body、header等根据各自项目配置,还有部分安全,加密方面的设置,
const host = 'www.host.com';
const requestUrl = `${host}/${url}`;
const body = {};
const headers = {
'Content-Type': 'application/json',
'User-Agent': ''
};
// 用的是fetch函数
fetch(requestUrl, {
method: 'POST',
headers: headers,
body: body
}).then(res => {
if (res && res.status === 200) {
return res.json();
} else {
throw new Error('server');
}
}).then(res => {
// 精简版判断
if(res && res.code === 200 && res.enmsg === 'ok') {
// 成功后的回调
sucCB(res);
}else {
// 失败后的回调
errCB(res);
}
}).catch(err => {
// 处理错误
})
}
Семь, асинхронное управление: Redux-Saga
Основные понятия, пожалуйста, переместите
Readme | Китайская документация Redux-saga
Отправная точка: необходимо декларативно выразить сложный асинхронный поток данных (например, длинная форма процесса, повторная попытка после сбоя запроса и т. д.), а императивный преобразователь имеет ограниченную выразительность для сложного асинхронного потока данных.
Установить
yarn add redux-saga
Создать файл саги
Порядок создания немного похож на редукторы
Сначала мы создаем папки и файлы, связанные с сагой, и, наконец, внедряем их в хранилище.
cd src
mkdir saga
touch saga/index.js saga/home.js saga/popularize.js saga/mine.js
Сначала изменитьsaga/home.jsдокумент
import { put, call, takeLatest } from 'redux-saga/effects';
import * as actionTypes from '../action/index';
import * as homeActions from '../action/home';
import * as mineActions from '../action/mine';
import post from '../utils/fetch';
function getSomeThing() {
post('/someData', {}, res => {}, err => {});
}
// 这个函数中的请求方法都是随手写的,没引入真实API,
function* getUserInfo({ sucCb, errCB }) {
try {
const res = yield call(getSomeThing());
const data = res.data;
yield put(homeActions.getSomeData())
yield put(homeActions.setSomeData(data))
yield call(sucCb);
} catch (err) {
yield call(errCB, err);
}
}
export const homeSagas = [
takeLatest(actionTypes.HOME_GET_SOMEDATA, getUserInfo)
]
saga/mine.jsдокумент
export const mineSagas = []
saga/popularize.jsдокумент
export const popularizeSagas = []
saga/index.jsФайл используется как общий выходной порт, и модификация выглядит следующим образом.
import { all } from 'redux-saga/effects';
import { homeSagas } from './home';
import { mineSagas } from './mine';
import { popularizeSagas } from './popularize';
export default function* rootSaga() {
yield all([...homeSagas, ...mineSagas, ...popularizeSagas]);
}
ввести сагу в магазин
store/index.jsмодификация файла
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../saga/index';
// 生成saga中间件
const sagaMiddleware = createSagaMiddleware(rootSaga);
middleware.push(sagaMiddleware);
Восемь: Сохранение данных: Redux-persist
GitHub - rt2zz/redux-persist: persist and rehydrate a redux store
Как следует из названия, постоянство данных обычно используется для хранения информации для входа и других данных, которые необходимо хранить локально.
Поскольку данные в хранилище будут возвращаться к исходному состоянию initState в редюсере каждый раз, когда приложение будет повторно открываться, такие данные, как информация для входа, должны храниться постоянно.
AsyncStorage, поставляемый с RN, может реализовать эту функцию, но он громоздкий в использовании, и он не внедряется в хранилище, поэтому нет возможности добиться унифицированного управления состоянием, поэтому выходит redux-persist.
Установить
yarn add redux-persist
ввести в магазин
store/index.jsПолный код файла выглядит следующим образом
import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import createSagaMiddleware from 'redux-saga';
import { persistStore, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/es/storage';
import combineReducers from '../reducer/index';
import rootSaga from '../saga/index';
const persistConfig = {
key: 'root',
storage,
// 白名单:只有mine的数据会被persist
whitelist: ['mine']
};
// 对reducer数据进行persist配置
const persistReducer = persistCombineReducers(persistConfig, combineReducers);
const sagaMiddleware = createSagaMiddleware();
// 中间件
const createStoreWithMiddleware = applyMiddleware(
thunk,
sagaMiddleware,
logger
)(createStore);
const configuerStore = onComplete => {
let store = createStoreWithMiddleware(persistReducer);
let persistor = persistStore(store, null, onComplete);
sagaMiddleware.run(rootSaga);
return { persistor, store };
};
export default configuerStore;
В этом месте промежуточное ПО больше не рассматривается как массив, а записывается непосредственно в метод applyMiddleware.
Магазин больше не экспортируется напрямую, но соответствует функция configuerStore, которая генерирует магазин везде.
App.jsВведение файла также необходимо немного изменить
import configuerStore from './src/store/index';
const { store } = configuerStore(() => { });
Девятое: статическое тестирование: поток
Чтобы быть в курсе, вот ссылка для чтения в первую очередьReact Native Заполнение ямы — поток (дополнительно)
10. Последствия
На данный момент мы представили redux-logger, redux-thunk, redux-saga, redux-persist.
Основная кодовая база разработки настроена
Проект загружен на github, добро пожаловать в звезду
Далее идут некоторые вспомогательные конфигурации, которые можно использовать в качестве разработки, такие как Flow, Babel (уже настроен при инициализации RN), Eslint и т.д.
Кроме того, поскольку это приложение, конечной целью, конечно же, является размещение в App Store и на основных рынках Android.Позже я могу поделиться некоторым контентом о Jiguang push jPush, горячем обновлении CodePush, обзоре загрузки пакетов и т. д.
Спасибо за ваше терпение, чтобы увидеть здесь, и я надеюсь, что вы узнаете что-то!
Если вы не очень заняты, пожалуйста, закажите звезду⭐[Портал блога Github], это большое поощрение для автора.
Мне нравится делать записи в процессе обучения. Я делюсь некоторыми своими накоплениями и размышлениями о начальном этапе. Я надеюсь общаться с вами и добиваться прогресса. Дополнительные статьи см.[блог амандакелаке на Github]