Лучшие практики для строительства крупных проектов с MOBX

JavaScript

Следующий:

Лучшие практики для создания больших проектов с помощью mobx (2)

последовательность

mobxЭто адаптивная структура управления данными, основанная на шаблоне наблюдателя.reduxЭто восходящая звезда.

Существует мнение, чтоmobxНе подходит для строительства больших проектов из-заmobxСлишком гибкие функции. Быть гибким означает быть свободным, что является ахиллесовой пятой при разработке крупных и все более сложных проектов.reduxВ противном случае это единственный источник данных,reducerчистая функция, толькоdispatchНесколько функций, таких как статус модификации, обеспечивают высокую степень единообразия формата написания кода.

В этой статье не будет обсуждатьсяmobxдетали использования, будут использоваться только полностьюmobxИсходя из преимуществ формат разработки унифицирован для обеспечения ремонтопригодности разработки масштабных проектов.

mobxПреимущества чрезвычайно превосходны, объектно-ориентированное программирование, реактивное программирование,mutableМетод обработки данных и возможность точного обновления компонентов здесь не обсуждаются.

недостатки мобкса

  • 0. Данные могут быть определены где угодно. Может быть определен в компоненте вместоstateроль; также может быть определена в отдельномstoreВнутри
  • 1. Логика взаимодействия с пользователем может быть написана в методе, объявленном компонентом, или вstoreвнутри объявленного метода.
  • 2. Взаимодействие с пользователем часто включает несколькоstoreобработка данных,storeМежду ними может быть сформирована сеть перекрестных ссылок.
  • 3.storeЧасто разделенные на страницы и модули, разбросанные повсюду, нелегко управлять единообразно.
  • 4.storeИнстанцированные время и путь не поддаются контролю.
  • 5, когда один примерstoreПоскольку бизнес-изменения должны поддерживать несколько экземпляров, преобразование чрезвычайно сложно.
  • 6. Не дружит с рендерингом на стороне сервера.nodeКогда терминал считывает данные для заполнения страницы, ему также необходимо сохранить данные на странице, чтобы внешний интерфейс мог восстановить данные из данных в исходное состояние.store(redxuизcreateStoreЕстественная поддержка со стороныinitialStateвозможность восстановления данных)

Столкнувшись с вышеуказанными проблемами, у большинства людей возникаетmobxВиды не подходят для больших проектов.

решение

В авторском использованииmobx+reactПосле выполнения многих средних и крупных интерфейсных проектов я ненавидел эти недостатки и постепенно исследовал некоторые решения для решения вышеуказанных проблем.

1. Наслоение

Для решения таких вопросов, как определение данных, совместное использование данных и предотвращение логического кода, структура проекта сначала состоит из нескольких уровней.

  • Элементы разделены по страницам
  • страница согласноstores,actions,viewsделится на три слоя
  • storesОпределите методы работы каждой модели данных и данных на странице, и каждое хранилище не зависит друг от друга.
  • viewsслой как слой представления, получаяstoresВведенные данные отвечают за рендеринг
  • actionsСлой обрабатывает логику взаимодействия, ссылаясь на каждыйstoreвызов метода для обновления данных иmobxАвтоматически активировать обновление представления

Вышеупомянутое является типичнымmvcИерархическая структура таким образом во многом решает проблемные пункты 0, 1, 2.

2. Единственный источник данных

На первом этапе трансформации ремонтопригодность проекта можно охарактеризовать как шаг вперед.

но страницаstoreа такжеactionНеобходимость вручную создавать экземпляры и вручную внедрять их в каждый компонент страницы — настоящее бремя. а такжеstoreСоздание экземпляра бесплатное, а управление более запутанное. 3, 4 и 5 не решены.

Следовательно, необходимо разработать библиотеку управления состоянием, в основном реализующую следующие функции

  • storeа такжеactionавтоматическая загрузка поиска.storeа такжеactionРазмещены на страницах, просматриваются через какой-то механизм
  • все найденоstoreа такжеactionАвтоматически создавать и формировать глобально уникальный источник данных
  • storeПредоставьте элементы конфигурации для настройки одного или нескольких экземпляров, чтобы уменьшить рабочую нагрузку по модификации кода из-за изменений в требованиях.
  • Создание экземпляра по запросуstore. например, посещение страницыA, просто создайте экземплярAзависит от страницыstore
Найти механизм

storeа такжеactionЕсть два способа поиска, один черезwebpackкоторый предоставилrequire.contextДинамически импортировать файлы из определенного каталогаstoreа такжеactionМодули, второй — загрузить через шаблон декоратора. Псевдокод выглядит следующим образом

    //webpack 
    require.context('./',true,/^(.+\/)*stores\/(.+)\.(t|j)sx?$/i)
    
    //装饰器
    @store({
        path:'pageA.storeA', //在全局store中的访问路径
        type:'singleton'|'multi' // 声明单例还是多实例
    })
    class StoreA{
        
    }
    
    // store装饰器的实现
    let store = (config) => target => {
      target['__storeType'] = config.type //保存
      App['__stores'] = App['__stores'] || [] //App为状态管理类
      App['__stores'].push({ target, path: config.path})
      return target;
    }

получить всеstoreПосле информации вы можетеstoresа такжеactionsОбрабатывай, собирай глобально уникальныеrootStoreсейчас,actionОбработка такая же.

Создание экземпляра по запросу

Если вы стремитесь к производительности, рассмотрите возможность реализации такой функции. Реализация может использовать атрибут доступа при доступе кstoreсвойства, а затем динамическое создание экземпляров. Псевдокод выглядит следующим образом

    Object.defineProperty(rootAction, 'storeA', {
          configurable: true,
          enumerable: true,
          get() {
            StoreA['__instance'] = StoreA['__instance'] || new StoreA()
            return StoreA['__instance']
          },
          set() {
            throw Error("can not set store")
          }
        })

Управление библиотекой через такое состояние, мы решили 3,4,5, 6 вопросов по рендерингу на стороне сервера, или простой обработкойrootStoreчтобы восстановить.

3. Оптимизация опыта разработки

(1) путь объявляется автоматически

декоратор сверху@storeнужно указывать вручнуюstoreсуществуетrootStoreМожет ли узел вstoreИмя каталога, в котором находится файл, имя файла,storeКак насчет прямого сопоставления имен классов и другой информации с соответствующими структурами?

Ответ положительный, просто напишитеbabelПлагин преобразования, который анализирует и заменяет абстрактное синтаксическое дерево файла во время компиляции и автоматически заполняет его.@storeизpathсвойства в порядке. (В авторском проекте используетсяtsпри условииts transformerвыполнить ту же функцию)

(2) Леса
  • Поскольку структура страницы остается очень однородной, независимо от того,storeдокумент,actionфайл илиjsx,cssфайл с более или менее шаблонным кодом. Чтобы автоматизировать процесс разработки, можно разработать инструменты формирования шаблонов для автоматического создания скелетов страниц. Один из них — повысить эффективность разработки, а другой — стандартизировать процесс разработки.
  • Если в проекте используетсяtsЕсли да, то эта глобальная автоматическая загрузка формируетсяstoreИнформация о типе утеряна. Поэтому вам нужно автоматически сгенерировать файл объявления типа (.d.ts), чтобы помочь получить лучший опыт разработки.

4. Ограничения спецификации разработки

Последняя тема — как более строго регламентировать способ написания кода.

Даже если мы ограничим бизнес-логику толькоactionЭто внутренне решается, но в конце концов это устная договоренность. Старые члены всегда имеют диаграмму, чтобы облегчить запись логики вviewСкорее всего, это произойдет с кодом, когда новый участник только что присоединился.

Таким образом, нам необходимо обеспечить механизм, гарантирующий, чтоactionвнутренний звонокstoreметод логической обработки, в то время как вactionза пределамиstoreВызовы недействительны, и в среде разработки выдается предупреждение.

Если вы думаете, что этот вопрос очень прост, это может быть потому, что вы не поняли его ключевой момент. Решение обсуждается ниже на примере.

    //声明一个store
    class StoreA{
        age = null;
        
        setAge(age){
            this.age = age;
        }
    }
    
    //声明一个action
    class ActionA{
        //调用store方法
        setAge(age){
            this.storeA.setAge(age); //有效
        }
    }
    
    //组件内
    storeA.setAge(age)  //无效

Для приведенного выше сценария способ обработки относительно прост. нужно только

  • объявить переменнуюflag
  • в инстанцированииstoreа такжеactionПри отдельном переносе методов экземпляра
  • actionустановить перед вызовом методаflagдляtrue,воплощать в жизньactionметод, затем установитеflagдляfalse.
  • такstoreметод, если вactionДоступ при вызовеflagдляtrue, доступ в другом местеflagдляfalse.
  • правильноstoreУпаковка метода относительно проста, судя поflag,дляtrueвыполнять операции с данными дляfalseДелайте дружеские советы

После вышеперечисленных шагов предельная обработка сцены синхронизации завершена.

Но в реальном проекте большое количество асинхронных операций, еслиactionКак показано ниже, как это должно быть?

     class ActionA{
        //调用store方法
        async setAge(age){
            await saveAge(url); //接口调用
            this.storeA.setAge(age); //有效
        }
    }

В настоящее времяstoreA.setAgeХотя вactionвнутри, но доступ кflagно этоfalse, план провалился.

Хотя работать с синхронными операциями так просто, асинхронные операции представляют собой огромную проблему. Текущую тему можно абстрагировать следующим образом

    如何实现在同一个方法内的调用(包括同步操作, setTimeout、promise、rAF、各种事件等异步操作的回调内...)都能访问到同一个上下文(true),而在这个方法外访问到的是另一个(false)

В сердце моем смутный ответ, если вactionСохраняйте этот контекст при вызове, а затем извлекайте этот контекст в различных асинхронных обратных вызовах для реализации функции. Но это ужасная вещь, а это значит, что нам нужно проксировать все асинхронные вызовы, другими словами, нам нужно переопределить нативный метод, чтобы сделать такое!

Это казалось трудным, пока я не узналzone.js.

zone.js

Коротко представить,zone.jsдаangularосновные компоненты каркаса,angularиспользоватьzone.jsПрослушивайте все асинхронные события, которые могут привести к изменению данных.

Этот промежуток немного великоват, почему он появился снова?angular.

Все в порядке, давайте снова представим его.zone.jsОписаноJavaScriptКонтекст процесса выполнения, который может постоянно передаваться между асинхронными задачами.

Дело в том, что это предложение, позвольте мне перевести его,zonejsМожет сохранять вызовы в рамках одного и того же метода (синхронного или асинхронного) с доступом к одному и тому же объекту контекста. Разве это не решает нашу проблему?

используй сейчасzonejsрешить нашу предыдущую задачу. код показывает, как показано ниже

    //这里并没有阐述zone.js如何使用,如果看过zonejs文档应该很容易理解下面的代码所做的事情
    const zone = Zone.root.fork({
      name: '__mobx__zone'
    });
    
    //包装action的setAge方法,使得action内的方法调用访问到Zone.current都为zone
    let oldFn = ActionA.setAge
    ActionA.setAge = (...args) => {
      return zone.run(oldFn, context, args)
    }
    
    //包装store的方法,判断Zone.current是否为zone,如果在action之外调用则为Zone.root
     let oldFn = StoreA.setAge
    StoreA.setAge = (...args) => {
      if(Zone.current === zone){
        return oldFn.apply(context,args)
      }else{
          //在action外调用store方法触发警告
          console.error('invalid call')
      }
    }
    
    //以上的包装方法均在内部处理,不暴露在业务代码中

использоватьzone.jsМы легко реализуем нужные нам функции, которые можно найти при грубом просмотре исходного кода.zone.jsЭто прокси насилия, котороеapi.

С помощью вышеуказанных шагов мы можем с радостью принятьmobxСоздавайте и повторяйте большие проекты.

конец

В этой статье не содержится слишком много деталей кода, посколькуmobxКак им пользоваться, тоже не поясняется. Данная статья посвящена решению проблемы использованияmobxпроблемы, которые могут возникнуть в процессе, и попытаться стандартизировать стиль кода членов, чтобы при использованииmobxПри разработке проекта можно в наибольшей степени гарантировать унификацию формата кода, а также снизить стоимость обслуживания проекта.
Как разработать и поддерживать крупномасштабный проект — большая тема, это должно быть сделано на основе заключения контракта или предписания определенных спецификаций, а затем создания конкретных проектов в соответствии с бизнес-сценарием, в котором он находится.