본문으로 건너뛰기

Taro

무료2018-12-08#Solution#Taro教程#Taro入门#Taro原理#Taro源码分析#Taro与mpvue

일종의 멀티엔드 코드 변환 방안. 위챗 미니프로그램, Web, ReactNative, 바이두 미니프로그램, 알리페이 미니프로그램, 진터우 미니프로그램, 퀵앱 등을 지원

일.목표 위치

React 구문 규범을 따르는 멀티엔드 통일 개발 프레임워크

일종의 멀티엔드 코드 변환 방안. 여기에서의 "엔드"란 위챗 미니프로그램, Web, ReactNative, 바이두 미니프로그램, 알리페이 미니프로그램, 진터우 미니프로그램, 퀵앱 등을 가리킵니다

구체적으로, 1 부의 React 풍 소스 코드를, "컴파일"을 통해 타겟 엔드와 호환되는 형식으로 변환합니다. 즉:

             변환
nerv 업무 코드 ------> xx 미니프로그램 업무 코드 +
                    Web 업무 코드 +
                    ReactNative 업무 코드

목적은 개발 비용을 낮추고 효율을 향상시키는 것입니다:

원래 1 엔드에서만 실행할 수 있었던 프로젝트에 멀티엔드 실행 능력을 갖게 하여, 개발자의 리팩토링 비용을 낮춘다.

이.사고 탐색

초심

React 로 위챗 미니프로그램을 쓴다.

위챗 미니프로그램의 네이티브 방식 개발은 매우 힘들었습니다. 그래서 React 로 위챗 미니프로그램을 개발하고 싶었습니다

연장

React 업무 코드를 위챗 미니프로그램 코드로 변환한다는 최초의 니즈를 실현한 후, 동일한 변환 사고로 멀티엔드에 적응할 수 있음을 발견했습니다. 즉1 대 1 에서 1 대 n 으로 연장했습니다:

P.S. 그 중에서 Nerv 는 일종의 React 풍 프레임워크로, API 는 React 와 유사합니다

P.S. Taro 컴포넌트 라이브러리가 위챗 미니프로그램을 기준으로 한 것도 초심 때문입니다 (이미 완성한 것을 낭비할 수 없죠)

사고

1 부의 코드로 n 엔드를 제패하려면, 2 종류의 사고밖에 없습니다:

  • 직접 1 엔드에서 n - 1 엔드로 변환

  • 1 층의 추상을 추가하고, 이 추상층에서 n 엔드로 변환

Bash 와 Batch(Windows 배치 처리 스크립트) 를 예로, 1 부의 스크립트만으로*nix 에서도 Windows 에서도 실행하고 싶다면, 제 1 의 사고에서는 1 개의 것만 구현하면 됩니다 (bash 에서 n - 1 엔드로 변환):

function bash2batch(bash) {
  // ...
  return equivalentBatch;
}

또는 (batch 에서 n - 1 엔드로 변환):

function batch2bash(batch) {
  // ...
  return equivalentBash;
}

AtoB 를 실현할 수 있으면, 1 부의 AAB 에 적응할 수 있지만, "硬" 변환은 일반적으로 비교적 어렵습니다. 따라서 Bash 와 Batch 의 장면에서는, 제 2 의 사고의 실현이 탄생했습니다:

Batsh: A language that compiles to Bash and Windows Batch.

즉 1 층의 추상 C 를 추가하고, 각각 CtoACtoB 를 구현하며, Batsh 라는 추상층에서 n 엔드로 변환합니다:

// 1.추상층 Batsh 정의
const batsh = 'Neither bash nor batch';
// 2.추상층에서 2 엔드로의 변환 구현
function batsh2batch(batsh) {
  // ...
  return equivalentBatch;
}
function batsh2bash(batsh) {
  // ...
  return equivalentBash;
}

마찬가지로, Taro 도 제 2 의 사고를 채택했습니다. 이 추상층이 Taro 업무 코드입니다:

P.S. Taro 업무 코드는 도중의 Nerv 코드이지만, Taro 코드라고 부르는 것이 더 정확합니다. Taro 고유의 API 서포트 (Taro.getEnv() 등) 가 추가되어 있으며, Nerv 의 슈퍼셋이기 때문입니다

삼.핵심 구현

위챗 미니프로그램을 예로, 그것은 4 부분으로 구성됩니다:

  • 설정 (JSON)

  • 템플릿 (WXML)

  • 스타일 (WXSS)

  • 로직 (JS)

설정과 스타일에 대해서는 말할 것이 별로 없습니다. 어려운 점은 템플릿의 변환과 로직의 변환에 있습니다

P.S. ReactNative 스타일 변환은 별개로, 이것도 난제입니다. RN 은 셀렉터, 속성명/값 및 기본값, 심지어 CSS 특성 서포트 정도에서 큰 차이가 있기 때문입니다

컴파일 변환

1 부의 코드 A 를 다른 코드 B 로 변환하려면, 3 가지를 수행해야 합니다:

  1. 코드 A 를 해석하여 추상 기술 (AST) 을 생성

  2. 몇 가지 매핑 규칙에 따라 AST 를 조작하여 새로운 AST 를 생성

  3. 새로운 AST 에 기반하여 코드 B 를 생성

taro-compile

P.S. 컴파일 변환에 대한 상세 정보는, 再看编译原理 와 [Babel 快速指南](/articles/babel 快速指南/) 참조

템플릿의 변환

JSX 구문을 미니프로그램에서 실행 가능한 문자열 템플릿으로 변환한다.

JSX 를 입력:

render() {
  const { percent } = this.state;

  return (
    <View className='index'>
      <Button className='add_btn' onClick={this.props.add}>+</Button>
      { percent && <MyProgress percent={percent} strokeWidth={6} color='#FF4949' /> }
    </View>
  );
}

@tarojs/transformer-wx 로 변환 후, 위챗 미니프로그램 템플릿을 출력:

<block>
  <view class="index">
    <button class="add_btn" bindtap="funPrivatesBrJC">+</button>
    <block wx:if="{{percent}}">
      <my-progress percent="{{percent}}" strokeWidth="{{6}}" color="#FF4949"></my-progress>
    </block>
  </view>
</block>

View, Button 등은Taro 내장 컴포넌트입니다:

Taro 는 위챗 미니프로그램 컴포넌트 라이브러리를 기준으로, jsx 구문 규범과 결합하여, 자체 컴포넌트 라이브러리 규범을 커스터마이즈했다

관련 package 는 다음과 같습니다:

  • @tarojs/components: Web 환경 Nerv 컴포넌트 라이브러리를 서포트. 컴파일을 통해 타겟 플랫폼의 네이티브 태그/컴포넌트로 치환

  • @tarojs/taro-components-rn: ReactNative 환경을 서포트하는 React 컴포넌트 라이브러리 (ReactNative 컴포넌트 라이브러리가 독립된 것은, 차이가 크고, 컴파일 수단으로 변환을 실현하기 어렵기 때문일 수 있습니다)

모두 타겟 엔드의 네이티브 컴포넌트로 변환됩니다:

미니프로그램 엔드에서는, 모든 미니프로그램 네이티브 컴포넌트를 사용할 수 있고, 다른 엔드에서는, 대응하는 컴포넌트 라이브러리 실현을 제공한다

그러나 커스텀 컴포넌트 my-progress 는 위챗 미니프로그램에는 존재하지 않으므로, 예상대로 실행할 수 없습니다

반드시크로스엔드 컴포넌트 정의가 필요합니다. 이를 위해 Taro 는 2 가지를 제공했습니다:

전자는 유무의 문제를 해결하고, 일반 응용 장면に対応합니다. 후자는 커스터마이즈가 필요한 장면に対応하기 위해 커스터마이즈 능력을 개방합니다

로직의 변환

컴포넌트 라이브러리가 멀티엔드 적응을 필요로 하는 것과 마찬가지로, 각 엔드의 능력 차이도 마찬가지로 적응이 필요합니다:

컴포넌트 라이브러리 및 엔드 능력은, 다른 엔드에서 다른 실현을 수행함으로써 차이를 평준화한다

런타임 프레임워크가 각 엔드 능력을 적응하여, 그 위에서 실행되는 Taro 업무 코드를 서포트합니다. 주로 3 가지 작용이 있습니다:

  • 컴포넌트화 방안, 설정 옵션 등의 기초 API 를 적응

  • 플랫폼 능력 관련 API 를 적응 (네트워크 요청, 결제, 촬영 등)

  • 애플리케이션 급 특성을 제공. 이벤트 버스 (Taro.Events, Taro.eventCenter), 실행 환경 관련 API (Taro.getEnv(), Taro.ENV_TYPE), UI 적응 방안 (Taro.initPxTransform()) 등

구현상, @tarojs/taro 는 API 적응의 통일 입구로, 컴파일 시 플랫폼별로 치환합니다:

  • @tarojs/taro: 단순한 빈 껍데기로, API 시그니처만 제공

플랫폼 적응 관련 package 는 6 개 있습니다:

P.S. 컴포넌트 라이브러리 적응 방안과 달리, API 는 컴파일 변환이라는 길을 포기하고, 직접 전체를 치환합니다

실제, 1 부의 업무 코드만 유지하고 싶다면, Taro 가 제공하는 API 는 반드시n 엔드 API 의 합집합이 됩니다. 예를 들어:

// 각 미니프로그램이 서포트하는 API
Taro.setStorage()
// 바이두 미니프로그램 전용 API
Taro.textToAudio()
// 알리페이 미니프로그램과 위챗 미니프로그램에서 파라미터 처리에 차이가 있는 API
Taro.getStorageSync()
// ...

이러한 API 는 직접 사용할 수 있고, 현재 플랫폼이 서포트하는지気に할 필요가 없습니다. 런타임 프레임워크의 적응 작업의 일부가 플랫폼 능력 API 차이를 평준화하기 때문입니다. 예를 들어:

H5 엔드에서는 스캔코드, 블루투스 등의 엔드 능력을 호출할 수 없다

위챗 미니프로그램 표준을 채택했으므로, 이러한 API 는 H5 엔드에서 실행할 때 아무것도 하지 않는다.

동시에업무층에서 타겟 환경을 구분하여, 이러한 플랫폼 관련 코드가 예상되는 타겟 환경에서만 실행되는 것을 보증합니다:

  • 컴파일 시: process.env.TARO_ENV

  • 런타임 시: Taro.getEnv()

예를 들어:

// 플랫폼별로 API 호출
if (process.env.TARO_ENV === 'swan') {
  Taro.textToAudio()
}
// 플랫폼별로 다른 컴포넌트 사용
<View>
  {process.env.TARO_ENV === 'weapp' && <ScrollViewWeapp />}
  {process.env.TARO_ENV === 'h5' && <ScrollViewH5 />}
</View>

P.S. 컴파일 시의 정적 환경 구분으로 대다수의 장면に対応할 수 있습니다. 런타임 시의 환경 구분은 불시의 수요 를 위해 준비되어 있습니다

사.구조

설계상, Taro 방안은 3 층으로 나뉩니다:

업무층 (React 풍 코드)
---------------------
변환층 (JSX 를 위챗 미니프로그램으로 변환)
---------------------
적응층 컴포넌트 라이브러리 (n 엔드 네이티브 컴포넌트를 적응)
      런타임 프레임워크 (n 엔드 API 능력을 적응)
---------------------

게다가,

  • 에코시스템: UI 라이브러리, 라우팅, 데이터 플로우 관리, CSS 전처리 등

  • 빌드: Web 은 Webpack, ReactNative 는 Expo 의 xdl, 나머지는 각각 자신의 IDE 사용

  • Lint: 변환층이 서포트하지 않는 쓰기에 대해, 정적 검사를 통해 일부 경고를 제공

오.소스 코드 간단 분석

구체적 구현에 대응하여, 각 부분에 대응하는 package 는 다음과 같습니다 (taro/packages/):

// 변환
babel-plugin-transform-jsx-to-stylesheet
taro-plugin-babel
taro-plugin-csso
taro-plugin-uglifyjs
taro-transformer-wx

// 적응 - 컴포넌트 라이브러리
taro-components-rn
taro-components

// 적응 - 런타임 프레임워크
taro-alipay
taro-h5
taro-qapp
taro-rn
taro-swan
taro-tt
taro-weapp
taro

// 에코시스템
postcss-plugin-constparse
postcss-pxtransform
postcss-unit-transform
taro-async-await
taro-mobx-common
taro-mobx-h5
taro-mobx-prop-types
taro-mobx-rn
taro-mobx
taro-plugin-less
taro-plugin-sass
taro-plugin-stylus
taro-plugin-typescript
taro-redux-h5
taro-redux-rn
taro-redux
taro-router-rn
taro-router

// 빌드
taro-cli
taro-rn-runner
taro-webpack-runner

// Lint
eslint-config-taro
eslint-plugin-taro

// 기타 (공공 방법)
taro-utils

게다가, 재미있는 것이 있습니다:

// 위챗 미니프로그램에서 Taro 로 변환
taroize
// taroize 후의 런타임
taro-with-weapp

역변환은 또 다른 문입니다. 변환而言, 1 대 1 에서 1 대 n 으로 연장한 후, 다음 단계는 n 에서 1 입니다. 즉:

// 타겟 엔드
A = weapp
B = ReactNative
C = ReactNative
// 추상층
T = Taro

// 제 1 단계: 1 대 1
T2A()
// 제 2 단계: 1 대 n
T2A(), T2B(), T2C()...
// 제 3 단계: n 에서 1
A2T(), B2T(), C2T()...

제 3 단계가 완성되면, 천하대동이 됩니다 (아무东西든 n 엔드로 변환할 수 있습니다)

P.S. 현재 (2018/12/9), A2T()(미니프로그램 코드에서 Taro 로 변환) 는 릴리스 대기 중입니다. 상세는 버전 계획 참조

육.제한

제한 방면에서 가장 깊게 느끼는 것은 JSX 입니다. JSX 의 유연성은 놀라울 정도이고 (동적 컴포넌트, 고계 컴포넌트), 동시에 위챗 미니프로그램의 템플릿 구문은 제한이 매우 많습니다 (WXS 라는 패치로 일부 능력을 강화했지만). 따라서 조화할 수 없는모순이 발생합니다. 그래서:

JSX 의 쓰기는 극도로 유연하고 변화가 많다. 우리는 열거 방식으로, 상용적인, React 공식이 권장하는 쓰기를 변환 규칙으로 하여 서포트하고, 일부 비교적 드문, 또는それほど 권장되지 않는 쓰기는 서포트하지 않고, 대신 eslint 플러그인 방식으로, 사용자에게 수정을 제시한다

구체적으로, JSX 제한은 다음과 같습니다:

  • 동적 컴포넌트 를 서포트하지 않음
  • JSX 요소를 포함한 map 루프 내에서 if 식을 사용할 수 없음
  • Array#map 이외의 방법으로 JSX 배열을 조작할 수 없음
  • JSX 파라미터 내에서 무명 함수를 사용할 수 없음
  • JSX 파라미터 (props) 에 JSX 요소를 전달하는 것을 허가하지 않음
  • class 컴포넌트만 서포트
  • 당분간 render() 이외의 방법으로 JSX 를 정의하는 것을 서포트하지 않음
  • JSX 파라미터 내에서 오브젝트 전개 연산자를 사용할 수 없음
  • 상태 없는 컴포넌트 (함수형 컴포넌트) 를 서포트하지 않음
  • props.children 은 전달하는 것만 가능하고 조작할 수 없음
  • ...

이러한 변환 제한에 대해, 보완성 방안은 Lint 검사 에러이며, 대체 방안을 제공합니다

JSX 외에, 2 가지 큰 제한이 있습니다:

  • CSS: ReactNative 의 CSS 서포트 정도에 제한됨 (flex 레이아웃만 사용 가능)

  • 태그: HTML 태그를 사용하지 않기를 약정 (모두 멀티엔드 적응済みの 내장 컴포넌트를 사용. View, Button 등)

P.S. 정적 변환 자신의 제한으로 인해, 많은 변환은 실현할 수 없습니다

칠.응용 장면

업무가 동시에 다른 엔드에서 표현을 요구할 때, 다른 엔드를 향해 여러 세트의 코드를 쓰는 비용은 분명히 매우 높다

즉, 동일 업무가 멀티엔드에서 겹치는 니즈가 있을 때, Taro 등의 멀티엔드 코드 변환 방안에 의미가 있습니다

또 하나의 장면류는 Taro 가 최초로 해결하고 싶은 위챗 미니프로그램 개발 체험 문제입니다. Taro 로 위챗 미니프로그램을 개발하면, 실수로 멀티엔드에도 적응할 수 있으므로, 이것도不错的选择입니다

참고 자료

댓글

아직 댓글이 없습니다

댓글 작성