предисловие
В последнее время я изучаю мини-программы WeChat, в процессе обучения я увиделwxapp-mallЯ думаю, что этот проект апплета WeChat очень хорош, пользовательский интерфейс очень маленький и свежий, поэтому я клонировал его для изучения и исследования, в процессе просмотра исходного кода я обнаружил, что он не сложен, и это действительно удивило меня, что он использует много кода для достижения богатых функций.Итак, я подумал, будет ли сложно использовать react-native для создания такого небольшого проекта, не говоря уже о том, что написание набора кода может запускать Android и ios на заодно (маленькие программы тоже...), или написать давай поиграем? Имея в виду эту идею, я прямоreact-native init Написать проект(๑•̀ㅂ•́)و✧
Давай сначала сделаем движущуюся картинку, дэндэнддэн~~
Техническая основа и компоненты
- react "16.0.0"
- react-native "0.51.0"
- mobx: "3.4.1"
- mobx-react: "4.3.5"
- react-navigation: "1.0.0-beta.21"
- react-native-scrollable-tab-view: "0.8.0"
- react-native-easy-toast: "1.0.9"
- react-native-loading-spinner-overlay: "0.5.2"
Зачем использовать Мобкс?
Mobx — это расширяемый инструмент управления состоянием, более простой, чем react-redux, и более быстрый для начала работы. В этом небольшом проекте из-за отсутствия интерфейса фоновой службы используются все локальные поддельные данные для имитации реализации.Просмотр товаров => добавить в корзину => оформить заказ => пустая корзина => восстановить исходное состояние товараВ таком процессе Mobx используется для управления всеми данными и статусом продуктов (независимо от того, выбраны они или не добавлены в корзину), чтобы все страницы могли обмениваться данными и изменять статус продуктов, данных и продуктов между страницами. обновляются синхронно. Конкретно как использовать Mobx для достижения этого процесса я поделюсь опытом использования и некоторыми встречающимися ямами ниже.
Начинать
Сначала запустите реактивный проект, а затем используйте пряжу или npm для установки всех зависимостей и компонентов. Потому что использование Mobx будет использовать ES7декоратор, так тоже устанавливайbabel-plugin-transform-decorators-legacyЭтот плагин, а затем добавить что-то в файл .babelrc.
{
"presets": ["react-native"],
"plugins": ["transform-decorators-legacy"]
}
Структура проекта
|-- android
|-- ios
|-- node_modules
|-- src
|-- common // 公用组件
|-- img // 静态图片
|-- mobx // mobx store
|-- newGoods.js // 首页新品数据
|-- cartGoods.js // 购物车数据
|-- categoryGoods.js // 分类页数据
|-- store.js // store仓库,管理数据状态
|-- scene
|-- Cart // 购物车页面
|-- Category // 分类页
|-- Home // 首页
|-- ItemDetail // 商品信息页
|-- Mine // 我的页面
|-- Root.js // root.js主要内容是配置react-navigation(导航器)
|-- index.js // 主入口
В файле Root.js конфигурация и использование реагирующей навигации могут относиться к следующему.официальная документация а такжеэтот блог, которые очень подробно написаны.Ответы на вопросы про react-navigation я нашел в этих двух статьях.Здесь актуальная настройка react-navigation,способы использования,разметка страницы и написание компонентов в проекте не будет подробно описаны здесь. , потому что они относительно просты, и больше о некоторой логике и методах для Mobx для достижения функций,screenКомпоненты в папке написаны с комментариями (°ー°〃)
Давайте поговорим в основном о Mobx.
Давайте сначала посмотрим на конкретный процесс, реализованный с помощью Mobx, см. следующую анимацию (⊙﹏⊙)
ps: возможно картинка слишком большая, загрузка немного медленная, пожалуйста, подождите...
1. Хранение и поиск данных
Все они смоделированы и реализованы с поддельными данными.В начале напишите структуру поддельных данных, например:
"data":
[{
"name": "那么大西瓜",
"price": "2.0",
"image": require('../img/a11.png'),
"count": 0,
"isSelected": true
},...]
существуетMobxв папкемагазин.js,Здесь в основном нужно хранить и управлять данными обо всех товарах, используемых приложением.логикаа такжеусловиеПереход от компонента к отдельному тестируемому блоку, доступному на каждой странице
import { observable, computed, action } from 'mobx'
import cartGoods from './cartGoods'
import newGoods from './newGoods'
import categoryGoods from './catetgoryGoods'
/**
* 根store
* @class RootStore
* CartStore 为购物车页面的数据
* NewGoodsStore 为首页的数据
* categoryGoodsStore 为分类页的数据
*/
class RootStore {
constructor() {
this.CartStore = new CartStore(cartGoods,this)
this.NewGoodsStore = new NewGoodsStore(newGoods,this)
this.categoryGoodsStore = new categoryGoodsStore(categoryGoods,this)
}}
Class CartStore{
@observable allDatas = {}
constructor(data,rootStore) {
this.allDatas = data
this.rootStore = rootStore
}
}
Class NewGoodsStore{
...跟上面一样
}
Class categoryGoodsStore{
...跟上面一样
}
// 返回RootStore实例
export default new RootStore()
используется здесьRootStore
Чтобы создать экземпляр всех магазинов (корзина, домашняя страница, страница категории имеет собственный магазин),
Таким образом, магазины могут управляться и управляться через RootStore, чтобы реализовать взаимную связь между ними и обмениваться ссылками.
Во-вторых, для хранения данных используется Mobx.@observableМетод заключается в том, чтобы сделать данные наблюдателем.Когда пользователь манипулирует представлением и вызывает изменение данных, обратите внимание на сотрудничество с предоставленным реактивным мобксом.@observerВид может обновляться автоматически, что очень удобно.
Кроме того, чтобы внедрить Rootstore Mobx в реактивные компоненты, передайтеmobx-reactкоторый предоставилProviderпонял, вRoot.jsДалее я написал следующее:
// 全局注册并注入mobx的Rootstore实例,首页新品,分类页,商品详情页,购物车页面都要用到store
import {Provider} from 'mobx-react'
// 获取store实例
import store from './mobx/store'
const Navigation = () => {
return (
<Provider rootStore={store}>
<Navigator/>
</Provider>
)}
После внедрения экземпляра Rootstore в дерево компонентов он используется непосредственно в компоненте?this.props.rootStoreВы можете получить это?
«Нет», нам также нужно добавить некоторые гаджеты к компонентам, использующим Rootstore, вHomeScreen.js
(Домашняя страница) выглядит следующим образом:
import { inject, observer } from 'mobx-react'
@inject('rootStore') // 缓存rootStore,也就是在Root.js注入的
@observer // 将react组件转变为响应式组件, 数据改变自动触发render函数
export default class HomeScreen extends Component {
......
}
добавлен@inject('rootStore')
, мы можем с удовольствием использоватьthis.props.rootStore
Давайте получим данные, которые мы хотим ^_^ , таким же образом, в информации о продукте, странице категории, странице корзины js, нам также нужно использовать@inject('rootStore')
Чтобы добиться сбора данных, а затем шаг за шагом передать данные своим подкомпонентам.
2. Реализация добавления в корзину
На главной странице и странице категории вы можете щелкнуть, чтобы перейти на страницу информации о продукте, а затем добавить его в корзину.
Реализация:
Под itemDetail.js, то есть под страницей информации о товаре, логика добавления в корзину следующая:
addCart(value) {
if(this.state.num == 0) {
this.refs.toast.show('添加数量不能为0哦~')
return;
}
// 加入购物车页面的列表上
// 点一次,购物车数据同步刷新
this.updateCartScreen(value)
this.refs.toast.show('添加成功^_^请前往购物车页面查看')
}
// 同步更新购物车页面的数据
updateCartScreen (value) {
let name = this.props.navigation.state.params.value.name;
// 判断购物车页面是否存在同样名字的物品
let index;
if(this.props.rootStore.CartStore)
index = this.props.rootStore.CartStore.allDatas.data.findIndex(e => (e.name === name))
// 不存在
if(index == -1) {
this.props.rootStore.CartStore.allDatas.data.push(value)
// 加入CartStore里
// 并让购物车icon更新
let length = this.props.rootStore.CartStore.allDatas.data.length
this.props.rootStore.CartStore.allDatas.data[length - 1].count += this.state.num}
else {
// 增加对应name的count
this.props.rootStore.CartStore.allDatas.data[index].count += this.state.num
}
}
Проще говоря, сначала получите название фрукта, а затем определите, есть ли фрукт с таким же названием в CartStore Mobx.Если есть, увеличьте количество соответствующего имени, если нет, добавьте данные в CartStore и переключиться на покупки.Когда отображается страница корзины, представление будет обновляться синхронно, чтобы увидеть фрукты, которые были добавлены в корзину.
3. Измените статус продукта и синхронно обновите представление.
Когда пользователь оперирует статусом товара на странице корзины покупок и данные изменяются, представление будет обновляться синхронно.
Например, увеличение количества предметов, уменьшение данных, выбранное состояние, выбор всех предметов и удаление предметов, общая цена будет меняться в зависимости от количества предметов.
Картинка снова здесь~~
Для достижения вышеуказанных функций основное использование Mobx предусмотреноactionметод, действие используется для изменения состояния, то есть используется действие для изменения различных состояний продукта (количество, выбранное состояние...), эти действия я написал вstore.js
изCartStore类
, вставьте код ниже
// 购物车store
class CartStore {
@observable allDatas = {}
constructor(data,rootStore) {
this.allDatas = data
this.rootStore = rootStore
}
//加
@action
add(money) {
this.allDatas.totalMoney += money
}
// 减
@action
reduce(money) {
this.allDatas.totalMoney -= money
}
// checkbox true
@action
checkTrue(money) {
this.allDatas.totalMoney += money
}
// checkbox false
@action
checkFalse(money) {
if(this.allDatas.totalMoney <=0 )
return
this.allDatas.totalMoney -= money
}
// 全选
@action
allSelect() {
if(this.allDatas.isAllSelected) {
// 重置totalMoney
this.allDatas.totalMoney = 0
this.allDatas.data.forEach(e=> {
this.allDatas.totalMoney += e.count * e.price})}
else {
this.allDatas.totalMoney = 0
}}
// check全选
@action
check() {
// 所有checkbox为true时全选才为true
let allTrue = this.allDatas.data.every(v => ( v.isSelected === true ))
if(allTrue) {
this.allDatas.isAllSelected = true
}else {
this.allDatas.isAllSelected = false
}}
// 删
@action
delect(name) {
this.allDatas.data = this.allDatas.data.filter (e => (e.name !== name ))
}
// 总价格
@computed get totalMoney() {
let money = 0;
let arr = this.allDatas.data.filter(e => (e.isSelected === true))
arr.forEach(e=> (money += e.price * e.count))
return money
}}
Вся логика модификации статуса товара находится в приведенном выше коде, среди которых totalMoney использует Mobx@computedМетод totalMoney зависит от данных данных CartStore, то есть данных о товарах, но при изменении значения данных он будет пересчитываться и возвращаться. Если вы понимаете vue, это эквивалентно вычисляемому свойству vue.
4. Расчет товара
Логика расчета товара и очистки корзины написана наCartCheckOut.js
Внутри процесс реализации очень прост, вставляем код:
// 付款
pay() {
Alert.alert('您好',`总计:¥ ${this.props.mobx.CartStore.totalMoney}`,
{text: '确认支付', onPress: () => this.clear()},
{text: '下次再买', onPress: () => null}],{ cancelable: false })}
// 清空购物车
clear() {
this.setState({visible: !this.state.visible})
setTimeout(()=>{
this.setState({ loadText: '支付成功!欢迎下次光临!' })
setTimeout(()=> { this.setState({ visible: false },
()=>{ this.props.mobx.CartStore.allDatas.data = []
// 把所有商品count都变为0
this.props.mobx.NewGoodsStore.allDatas.data.forEach(e=> e.count = 0)
this.props.mobx.categoryGoodsStore.allDatas.data.forEach( e => {
e.detail.forEach(value => { value.count = 0 })
})
})},1500)},2000)}
Здесь в основном используют setTimeout и некоторые методы для имитации реализацииОплата => Оплата завершена => Пустая корзина => Восстановить статус товара.
Что ж, этот процесс завершен, ха-ха.
5. Встречаются небольшие ямы
1. Я написал неупорядоченный метод массива, который полезенArray.isArray()
Этот метод используется, чтобы определить, является ли это массивом.Однако, когда я использую эту неупорядоченную функцию, когда я хочу использовать ее, чтобы испортить массив в хранилище, я обнаруживаю, что она не была выполнена, что очень странно. тогда я напрямую используюArray.isArray()
Этот метод используется для оценки массива в хранилище, и возвращаемое значение всегда ложно. . . Так что я был ошеломлен. . . Позже я зашел в официальную документацию Mobx и, наконец, нашел ее.Отвечать. Получается, что хранящийся в хранилище массив не является настоящим массивом, аobverableArray
, если вы хотите, чтобыArray.isArray()
Если это считается правдой, необходимо добавить файл .slice()
метод илиArray.from()
Все будет хорошо.
2. Аналогично, это тоже проблема obverableArray. На странице корзины я использовал FlatList для отображения товаров в корзине.Сначала, когда я добавлял товары в корзину, я обнаружил, что страница корзины не обновлялась. С вышеописанным опытом наступления на яму я думаю, что это вызвано obverableArray, потому что данные FlatList получают реальный массив, поэтому я использую этот метод:
@computed get dataSource() {
return this.props.rootStore.CartStore.allDatas.data.slice();
}
...
<FlatList data={this.dataSource} .../>
Таким образом, вид корзины покупок может обновляться автоматически, вофициальная документацияТоже написано выше.
3. Еще один вызван собственной невнимательностью. После того, как я закончил писать этот проект, когда я вышел поиграть с моим другом, я отправил его своему другу на просмотр.Когда он удалил продукт, он обнаружил, что не может удалить его сверху вниз, но он можно удалить снизу вверх. Позже я использовал симулятор, чтобы проверить то же самое, поэтому я пошел посмотреть логику удаления продукта и обнаружил, что проблемы нет.Потом я посмотрел данные магазина и обнаружил, что он может обновляться синхронно, но представление не обновлялось, поэтому я пошел в FlatList, чтобы найти причину. , Наконец, причина найдена, в основном в keyExtractor, нет возможности использовать индекс массива, а имя используется в качестве ключа, что то есть значение ключа здесь должно быть достаточно стабильным и не может быть привязано к индексу (index).Set key, который также является одним из синтаксиса реакции. Поскольку мой метод удаления элементов на самом деле основан на имени, а не на индексе, поэтому будет ошибка, когда индекс будет использоваться в качестве ключа элемента FlatList.
_keyExtractor = (item,index)=> {
// 千万别用index,不然在删购物车数据时,如果从第一个item开始删会产生节点渲染错乱的bug
return item.name
}
напиши в конце
Суммировать
На то, чтобы написать его от и до, ушло около недели.В общем, я чувствую, что использовать react-native для написания такого проекта торгового центра сложнее, чем реализовать небольшую программу, в основном потому, что для написания компонентов требуется немного больше времени, и здесь Мне также потребовалось некоторое время, чтобы смоделировать процесс покупки с помощью Mobx.AndroidУпакованный в apk, он отлично работает на моем эмуляторе и на Android-телефоне моего друга, багов пока не обнаружено,IOSПоскольку у меня нет MAC, поэтому я пока не упаковывал и не тестировал TT. Я надеюсь, что квалифицированные друзья смогут его клонировать и помочь мне протестировать. Если есть проблема, вы можете упомянуть об этом. Спасибо. ты очень ヽ(✿゚▽゚)ノ
Прикрепите адрес проекта на гитхабе:GitHub.com/shooter РА О/…(Если вам интересно, я надеюсь, что вы можете нажать на звездочку и подбодрить вас, спасибо!)
Спасибо
Этот небольшой проект был вдохновленwxapp-mall, На базе этого апплета оптимизирована логика покупок и некоторые интерактивные модификации. Некоторые пользовательские интерфейсы и значки также следуют этому апплету, и я также получил разрешение оригинального автора, большое спасибо. Кроме того, я хотел бы особо поблагодаритьШоу ДжерриШоуКарта фруктов и логотип приложения для меня, а также спасибо Кесону за помощь в тестировании и предоставление предложений.
Это мой первый блог на Nuggets, и это также мой первый проект с открытым исходным кодом.Есть недостатки, я надеюсь, что вы сможете вынести меня и дать несколько советов, спасибо!
Кроме того, сегодня канун Рождества, счастливого кануна Рождества ~