Практика Таро - Углубленный опыт практики развития и краткое изложение

внешний интерфейс React.js
Практика Таро - Углубленный опыт практики развития и краткое изложение

предисловие

Таро из Bump Lab следует за вамиReactграмматическийМультитерминальный план развития,TaroВ настоящее время он был открытым исходным кодом в течение определенного периода времени и широко приветствовался и беспокоился разработчиками интерфейса. до настоящего времениstarЧисло превысило 11,7 тыс., более 200 вопросов все еще открыты, а закрыты более 700. Видно, что много разработчиков, которые используют и участвуют в обсуждениях. В настоящее время Taro поддерживает апплет WeChat, H5, RN, апплет Alipay и апплет Baidu. Taro, который постоянно дорабатывается, также совместим с большим количеством терминалов и добавляет поддержку некоторых новых функций.

Возвращаясь к теме, в этой статье в основном рассказывается о практике углубленного развития Таро.Основываясь на нашем опыте и резюме использования Таро в реальных проектах, мы сначала поговорим о Таро.Почему стоит использовать синтаксис React, а затем изКод организации проекта Таро,Управление статусом данных,оптимизация производительноститак же какСовместимость с несколькими терминаламиНесколько других аспектов, иллюстрирующих опыт глубокой практики развития Таро.

Почему стоит использовать синтаксис React

Это видно по двум аспектам: во-первых, нативный метод разработки небольших программ недостаточно дружелюбен, либо недостаточно инженерен, а разрабатывать какие-то масштабные проекты будет очень сложно, что в основном отражается в следующем точки:

  • Страница или компонент апплета должны одновременно содержать 4 файла, поэтому при разработке функционального модуля ему необходимо переключаться между несколькими файлами.
  • Sass, Less и более новый синтаксис ES Next нельзя использовать напрямую без специальной предварительной обработки файлов.
  • Строковый шаблон слишком слабый Строковый шаблон апплета имитируетVue, но не обеспечиваетVueСтолько синтаксического сахара, при реализации какой-то более сложной обработки писать очень хлопотно, хотя и обеспечиваетwxsВ качестве дополнения, но опыт все еще очень плохой
  • Отсутствие наборов тестов, неспособность написать тестовый код для обеспечения качества проекта, а также неспособность выполнять непрерывную интеграцию и автоматическую упаковку.

Нативный метод разработки не является дружественным, и, естественно, есть более эффективная альтернатива. Поэтому мы обратили свое внимание на три популярных на рынке интерфейсных фреймворка.React,Vue,Angular.AngularПопулярность в стране невысокая, мы сначала исключили эту грамматическую спецификацию. На рынке уже есть несколько отличных проектов с открытым исходным кодом для Vue-подобных сред разработки апплетов, и технологический стек в нашем отделе в основном React, затемСпецификация синтаксиса ReactЭто, естественно, стало нашим первым выбором. Кроме того, у нас есть следующие соображения:

  • React — очень популярный фреймворк с большой аудиторией, использование которого также может снизить стоимость обучения при разработке небольших программ.
  • Идея и механизм реализации управляемого данными обновления шаблонов для небольших программ, подобных React
  • React использует JSX в качестве собственного шаблона.По сравнению со строковыми шаблонами, JSX более свободен, естественнее и выразительнее.Ему не нужно полагаться на различные синтаксические сахара строковых шаблонов, а также можно выполнять сложную обработку.
  • У самого React есть кросс-конечное решение для реализации ReactNative, которое очень зрелое и имеет активное сообщество.Для Таро есть больше возможностей для многоконечной разработки.

Подводя итог, Таро наконец принялСинтаксис реакцииОн используется в качестве собственного стандарта грамматики в сочетании с идеей разработки интерфейса, чтобы создать более элегантный опыт разработки для разработки небольших программ.

Код организации проекта Таро

Чтобы разрабатывать проекты Taro, вы должны сначала установить taro-cli.Чтобы узнать о конкретном методе установки, см.Документация, я не буду вводить здесь слишком много, по умолчанию вы установили taro-cli и можете запускать команды.

Затем мы используем cli для создания нового проекта, и результирующий шаблон проекта выглядит следующим образом:

├── dist                   编译结果目录
├── config                 配置目录
|   ├── dev.js             开发时配置
|   ├── index.js           默认配置
|   └── prod.js            打包时配置
├── src                    源码目录
|   ├── pages              页面文件目录
|   |   ├── index          index页面目录
|   |   |   ├── index.js   index页面逻辑
|   |   |   └── index.css  index页面样式
|   ├── app.css            项目总通用样式
|   └── app.js             项目入口文件
└── package.json

Если это очень простой проект, такой шаблон можно использовать для удовлетворения требований, а необходимая для страницы логика прописывается в файле index.js.

Если в проекте используется избыточность, как в проекте, который мы разработали ранее, каталог выглядит следующим образом:

├── dist                   编译结果目录
├── config                 配置目录
|   ├── dev.js             开发时配置
|   ├── index.js           默认配置
|   └── prod.js            打包时配置
├── src                    源码目录
|   ├── actions            redux里的actions
|   ├── asset              图片等静态资源
|   ├── components         组件文件目录
|   ├── constants          存放常量的地方,例如api、一些配置项
|   ├── reducers           redux里的reducers
|   ├── store              redux里的store
|   ├── utils              存放工具类函数
|   ├── pages              页面文件目录
|   |   ├── index          index页面目录
|   |   |   ├── index.js   index页面逻辑
|   |   |   └── index.css  index页面样式
|   ├── app.css            项目总通用样式
|   └── app.js             项目入口文件
└── package.json

Для апплета электронной коммерции, который мы разработали ранее, весь проект содержит около 30 000 строк кода и десятки страниц, организованных в соответствии с приведенным выше каталогом. Наиболее важные папки в основномpages,componentsа такжеactions.

  • pagesВнутри находится входной файл каждой страницы.Простая страница может быть непосредственно входным файлом.Если страница более сложная, входной файл будет использоваться как совокупный файл компонента.reduxПривязка в основном тоже осуществляется на этой странице.

  • компоненты размещеныcomponentsв. Каталог внутри такой, если естьcouponстраница с купоном, наpagesКонечно, естьcoupon, как запись страницы, а затем его компоненты сохраняются вcomponents/couponвнутри, то естьcomponentsОн также будет разделен на модули в соответствии со страницей, и может быть построен общий компонент.components/publicпапка для повторного использования.

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

  • actionsЭта папка тоже важнее, здесь логика вытягивания данных и повторной обработки данных. Можно сказать, что если данные обрабатываются хорошо и поток понятен, весь проект наполовину успешен.Подробности см. в разделе ***Управление статусом данных*** ниже. как указано выше, еслиcouponстраницыactions, то он будет помещен вactions/couponВнутри вы снова можете увидеть, что все модули различаются по размеру страницы.

Помимо,assetСтатические ресурсы, для хранения которых используется файл, например некоторые изображения класса значков, но рекомендуется не хранить слишком много, в конце концов, существуют ограничения на пакет. а такжеconstantsЭто место для хранения констант, напримерapiДоменное имя, конфигурация и т. д.

После завершения настройки проекта в корневом каталоге командной строкиnpm run build:weappилиtaro build --type weapp --watchСкомпилируйте его в апплет, а затем вы можете открыть инструмент разработки апплета для предварительной разработки. Если вы компилируете на другом конце, вам нужно указать только тип (например, компиляция H5:taro build --type h5 --watch).

При разработке проекта с Taro, если код хорошо организован и следует спецификациям и условностям, половина успеха будет достигнута, по крайней мере, это сделает разработку более эффективной.

Управление статусом данных

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

Когда дело доходит до редукса, я считаю, что все уже знакомы с ним. В Taro его использование похоже на обычное использование в React, сначала создайтеstore,reducers, а затем напишитеactions; затем по@tarojs/redux,использоватьProviderа такжеconnect, который привязывает хранилище и действия к компоненту. Все знают основы использования, позвольте мне представить вам, как лучше использовать redux.

предварительная обработка данных

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

Например:

Например, в модуле корзины покупок, показанном выше, данные, возвращаемые интерфейсом,

{
	code: 0,
	data: {
        shopMap: {...}, // 存放购物车里商品的店铺信息的map
        goods: {...}, // 购物车里的商品信息
        ...
	}
	...
}

Да, магазин товаров и товар в корзине размещены в двух объектах, но представление требует, чтобы они отображались вместе. В настоящее время, если вы напрямую сохраняете возвращенные данные вstore, то внутри компонентаrenderКогда придет время собрать воедино, сопоставить две информации, а затем отобразить ее, окажется, что логика внутри компонента очень хаотична и недостаточно чиста.

Поэтому я лично рекомендую, чтобы после того, как интерфейс вернул данные, они были непосредственно обработаны в данные, соответствующие отображению страницы, а затемdispatchОбработанные данные эквивалентны слою перехвата, а именно:

const data = result.data // result为接口返回的数据
const cartData = handleCartData(data) // handleCartData为处理数据的函数
dispatch({type: 'RECEIVE_CART', payload: cartData}) // dispatch处理过后的函数

...
// handleCartData处理后的数据
{
    commoditys: [{
        shop: {...}, // 商品店铺的信息
        goods: {...}, // 对应商品信息
    }, ...]
}

Видно, что процесс обработки данных перехватывается и обрабатывается перед рендерингом, а соответствующий товарный магазин и товар помещаются в один объект.

Это имеет несколько преимуществ:

  • Один из них — рендеринг компонентов.чище, вам не нужно заботиться о том, как изменить данные, чтобы они соответствовали требованиям представления внутри компонента,Просто позаботьтесь о логике самого компонента, такие как события кликов, взаимодействия с пользователем и т. д.
  • Во-вторых, поток данных.более контролируемый,Фоновые данные——>Обработка перехвата——>ожидаемая структура данных——>компоненты, если данные, возвращаемые в фоновом режиме, изменяются, все, что нам нужно сделать, это изменитьhandleCartDataЛогика в функции не требует изменения логики внутри компонента.

На самом деле, не только при возврате фоновых данных, но и при необходимости изменения других структур данных может выполняться слой перехвата данных, а время перехвата также может быть скорректировано в соответствии с бизнес-логикой.Соответствуют ли данные представлению, ориентируйтесь только на логику внутреннего взаимодействия, что тоже подходитReactего первоначальное намерение,представление на основе данных.

Реализация вычисляемых свойств с помощью Connect

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

export default connect(({
  cart,
}) => ({
  couponData: cart.couponData,
  commoditys: cart.commoditys,
  editSkuData: cart.editSkuData
}), (dispatch) => ({
  // ...actions绑定
}))(Cart)

// 组件里
render () {
	const isShowCoupon = this.props.couponData.length !== 0
    return isShowCoupon && <Coupon />
}

Вышеупомянутое очень распространеноconnectпиши тогдаrenderфункционировать в соответствии сcouponDataБудут ли данные отображаться. В это время мы можемthis.props.couponData.length !== 0Это суждение потеряноconnect, чтобы достичьcomputedЭффект следующий:

export default connect(({
  cart,
}) => {
  const { couponData, commoditys, editSkuData  } = cart
  const isShowCoupon = couponData.length !== 0
  return {
    isShowCoupon,
    couponData,
    commoditys,
    editSkuData
}}, (dispatch) => ({
  // ...actions绑定
}))(Cart)

// 组件里
render () {
    return this.props.isShowCoupon && <Coupon />
}

можно увидеть вconnectопределено вisShowCouponпеременная, реализующаяcouponDataвыполнятьcomputedЭффект.

По сути, это тоже процесс перехвата данных. Кромеcomputed, и другие функции также могут быть реализованы, которые могут свободно выполняться чиновниками.

оптимизация производительности

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

Использование setState

На самом деле, при разработке небольших программ наиболее вероятные проблемы с производительностью, которые могут возникнуть, большинство из них появляются вsetData(Конкретно в Таро стоит призватьsetStateфункция). Это вызвано механизмом разработки апплета, каждый раз, когда он вызываетсяsetData, апплет будет хранить эту часть данных на логическом уровне (среда выполненияJSCore) для операций, подобных сериализации, преобразования данных в строку и передачи их на уровень представления (среда выполненияWebView), уровень представления получает данные посредством десериализации, а затем отображает страницу. Этот процесс имеет определенные накладные расходы на производительность.

Так оsetStateиспользование, есть следующиев общем:

  • Избегайте одновременного обновления огромных объемов данных. Это скорее проблема проектирования компонентов, максимально возможное разделение компонентов при балансировании эффективности разработки.
  • избегать частых звонковsetState. На самом деле setState в Taro является асинхронным, и это поможет вам выполнить этот уровень оптимизации в процессе компиляции.Например, если setState вызывается дважды в функции, Taro объединит их в следующем цикле обработки событий и устранит дубликаты. данные.
  • Избегайте страницы фонового состоянияsetState. Это более вероятно, потому что setState используется в асинхронных операциях, таких как таймеры, заставляя страницу фонового состояния выполнять операцию setState. Для решения проблемы можно выполнить операцию таймера уничтожения при уничтожении или скрытии страницы.

Оптимизация рендеринга списка

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

Следовательно, будет проблема, когда будет загружено все больше и больше данных о продукте, будет сообщено об ошибке.invokeWebviewMethod 数据传输长度为 1227297 已经超过最大长度 1048576. Причина в том, что мы сказали выше, апплет будет передавать эту часть данных между уровнем логики и уровнем представления, когда используется setData.Когда объем данных слишком велик, он превысит лимит.

Чтобы решить эту проблему, мы используем метод большой пейджинговой идеи. Это запись текущего пейджинга в раскрывающемся списке.Когда он достигает 10 страниц, 10 страниц используются в качестве точки разделения, а текущая страница делится наthis.stateвнутреннийlistВозьмите данные за точкой разделения и оцените прокрутку вперед, чтобы setState предыдущие данные Блок-схема выглядит следующим образом:

Как видите, мы сначала помещаем все необработанные данные продукта вthis.allList, а затем определите текущий номер страницы в событии прокрутки страницы в соответствии с высотой прокрутки страницы. Если номер страницы меньше 10, возьмите первые десять элементов this.allList.slice, если он больше или равен 10, возьмите последние десять элементов и, наконец, вызовитеthis.setStateСделайте рендеринг списка. Основная идея здесь заключается в том, чтоОтрисовываются только видимые данные, чтобы избежать ошибок, вызванных чрезмерным объемом данных.

В то же время, чтобы рендерить заранее, мы установим порог 500, чтобы сделать весь процесс переключения рендеринга более плавным.

Совместимость с несколькими терминалами

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

process.env.TARO_ENV

использоватьprocess.env.TARO_ENVЭто может помочь нам оценить текущую среду компиляции, чтобы выполнить некоторую специальную обработку.В настоящее время его значения равныweapp,swan,alipay,h5,rnпять. Вы можете использовать эту переменную для написания кода, соответствующего некоторым различным средам.При компиляции код, не принадлежащий текущему типу компиляции, будет удален, и будет сохранен только код текущего типа компиляции, чтобы достичь Цель совместимости. Например, если вы хотите сослаться на разные ресурсы в апплете WeChat и на стороне H5:

if (process.env.TARO_ENV === 'weapp') {
  require('path/to/weapp/name')
} else if (process.env.TARO_ENV === 'h5') {
  require('path/to/h5/name')
}

После того, как мы узнаем об использовании этой переменной, мы можем выполнить некоторую совместимость с несколькими терминалами.Следующие два примера подробно описаны.

Совместимость с событиями прокрутки

В апплете мониторинг прокрутки страницы должен быть на страницеonPageScrollВ событии и в H5 его нужно вызывать вручнуюwindow.addEventListenerЧтобы выполнить привязку событий, чтобы мы могли обрабатывать конкретную совместимость следующим образом:

class Demo extends Component {
  constructor() {
    super(...arguments)
    this.state = {
    }
    this.pageScrollFn = throttle(this.scrollFn, 200, this)
  }
  
  scrollFn = (scrollTop) => {
    // do something
  }
  
  // 在H5或者其它端中,这个函数会被忽略
  onPageScroll (e) {
    this.pageScrollFn(e.scrollTop)
  }

  componentDidMount () {
    // 只有编译为h5时下面代码才会被编译
    if (process.env.TARO_ENV === 'h5') {
      window.addEventListener('scroll', this.pageScrollFn)
    }
  }
}

Как видите, мы сначала определяем функции, которые нужно выполнять при прокрутке страницы, а заодно делаем слой обработки троттлинга снаружи (если вы не понимаете троттлинг функций, то можете посмотретьздесь). Затем вonPageScrollфункция, мы выполняем функцию. В то же время вcomponentDidMount, сделайте экологическую оценку, если даh5среда привязывает его кwindowв событии прокрутки.

Благодаря такой обработке в апплете она будет выполняться при прокрутке страницыonPageScrollфункция (эта функция игнорируется на других сторонах); на стороне h5 событие прокрутки напрямую связано сwindowначальство. Поэтому мы дошли до небольшой программы, и привязка события прокрутки на стороне h5 совместима (обработка на другой стороне аналогична).

холст совместимый

Если вы хотите использовать его в апплете и H5 одновременноcanvas, что также требует некоторой обработки совместимости.canvasAPI-интерфейсы в Mini Programs и H5 в основном одинаковы, но есть несколько отличий:

  • Контекст канвы получается по-разному, в h5 он получается напрямую из dom, в апплете его нужно создать вручную, вызвав Taro.createCanvasContext
  • При рисовании апплет также должен вручную вызывать CanvasContext.draw для рисования.

Поэтому при обработке совместимости мы должны сосредоточиться на этих двух моментах совместимости.

componentDidMount () {
    // 只有编译为h5下面代码才会被编译
    if (process.env.TARO_ENV === 'h5') {
        this.context = document.getElementById('canvas-id').getContext('2d')
    // 只有编译为小程序下面代码才会被编译
    } else if (process.env.TARO_ENV === 'weapp') {
        this.context = Taro.createCanvasContext('canvas-id', this.$scope)
	}
}

// 绘制的函数
draw () {
    // 进行一些绘制操作
  	// .....
    
    // 兼容小程序端的绘制
    typeof this.context.draw === 'function' && this.context.draw(true)
}

render () {
    // 同时标记上id和canvas-id
	return <Canvas id='canvas-id' canvas-id='canvas-id'/>
}

Видно, что сначала в жизненном цикле componentDidMount контекст CanvasContext получается для разных сторонних методов, а на стороне апплета напрямую черезTaro.createCanvasContextСоздайте и нужно передать второй параметрthis.$scope; со стороны H5 он проходит черезdocument.getElementById(id).getContext('2d')чтобы получить контекст CanvasContext.

После получения контекста процесс отрисовки одинаков, потому что API на обоих концах в основном одинаковые, и нужно только судить, есть ли у контекста функция отрисовки в конце отрисовки, и если да, то выполнить ее один раз, чтобы быть совместимым с апплетом и нарисовать его.

Мы написали кулон заграждения с Canvas внутри, и именно этот метод обеспечивает совместимость между обоими концами.

Подводя итог вышеприведенным двум конкретным примерам, он основан на встроенной системе Таро.process.env.TARO_ENVПеременные среды используются для оценки текущей среды, а затем некоторые терминалы индивидуально адаптируются. Поэтому методы совместимости конкретных уровней кода будут варьироваться в зависимости от ваших потребностей.Надеюсь приведенные выше примеры могут вас вдохновить.

Суммировать

В этой статье впервые рассказывается о ТароПочему стоит использовать синтаксис React, а затем изКод организации проекта Таро,Управление статусом данных,оптимизация производительноститак же какСовместимость с несколькими терминаламиЭти аспекты иллюстрируют глубокий опыт практики развития Таро. В целом это какое-то углубленное практическое содержание, если у вас есть какие-либо мнения или возражения, вы можете присоединиться к группе обмена разработками и вместе поучаствовать в обсуждении.