Создайте приложение Mini Fruit Mall с помощью React-Native+Mobx

внешний интерфейс JavaScript React.js React Native MobX
Создайте приложение Mini Fruit Mall с помощью React-Native+Mobx

предисловие

В последнее время я изучаю мини-программы 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, и это также мой первый проект с открытым исходным кодом.Есть недостатки, я надеюсь, что вы сможете вынести меня и дать несколько советов, спасибо!

Кроме того, сегодня канун Рождества, счастливого кануна Рождества ~