preface
As early as last October, qudian FED, where I work, fully used the Taro framework to develop mini programs (version 1.0-Beta.4 at that time), and has launched two wechat mini programs and two Alipay mini programs so far.
Taro is chosen to solve the pain points of the native development of wechat small programs on the one hand. On the other hand, the team also has the pursuit of multi-stage unified development. Taro is undoubtedly the best support at that time. React also fits into the overall technology stack of individuals and teams, significantly reducing team learning costs.
Taro has good support on the App side and H5 side, and there are many online instances to check. However, in React Native’s support, Github’s public projects are not compatible with RN:
This situation is understandable. After all, it is difficult to achieve the unity of multiple applications, so it is necessary to accurately grasp the differences between different applications and make a reasonable choice. Taro’s design goal is multiple applications, but his focus is on the small application side, without certain development constraints on multiple applications, it is normal that he has no way to start. The author once shared “Multi-terminal Unified Development Practice” at the 2018 iWeb Summit – Xiamen station, mentioned the pit and general idea of using Taro to develop RN terminal, and put it into practice.
Based on the practical experience of qudian FED in the past six months, we have developed the first Taro three-terminal unified application: taro-yanxuan (a small program modeled after netease yanxuan wechat) to discuss the focus of this paper: the correct posture for Taro to develop multi-terminal applications.
The related code is open source: github.com/js-newbee/t… .
The online preview
The mini program side already supports wechat mini program and Alipay mini program, but cannot provide online version, please clone code to run locally.
H5 and RN terminals can preview online (yexuan interface of netease is directly invoked. To experience login and shopping cart functions, please use netease email account to log in) :
Small program | H5 – Access the link | React Native |
---|---|---|
Clone code to run locally | Expo Snacks |
React Native works like this:
Home page, Categories | For details, add shopping cart | Shopping cart, individual |
---|---|---|
Style of management
Style management is the primary challenge of multi-terminal development, because React Native has a large difference in support of common Web styles. Most of the above multi-terminal projects that are not adapted to RN are already stuck in styles, and a large number of styles that are not supported by RN are used. In this case, to comply with RN is like rewriting pages. Must be a heart powerless. This is also emphasized in this article, need to grasp the correct multi – stage development posture.
H5 is the most flexible style, followed by applets, and RN is the weakest. Unified multi-terminal style is to align the short board, that is, to manage the style with the constraints of RN, while taking into account the limitations of applets. The core can be summarized in three points:
- Using Flex layout
- Write based on BEM style
- Override the component style with the style property
Using Flex layout
Before further elaboration, it is necessary to understand the main differences of several influencing style schemes at the RN end:
display
onlyflex / none
.position
onlyrelative / absolute
;- Tag selectors, child selectors, pseudo-elements are not supported
background: url()
And so on; - The text takes
Text
Tag wrap, text style cannot be added inView
On the label, you can only addText
On the label.
Flex layout is used, not just because RN’s View tag has a default style display: Flex; Flex -direction: column, flex-direction: column
// The View tag will not be 100px high, and there will be a few pixels of white space below the image, called ghost white space
<View>
<Image src={... } style={{ height: '100px' }}
</View>
Copy the code
The conventional solution would be to set font size/line-height: 0 or Image display: inline-block on the View tag, but none of these are supported in RN. Flex is the only reliable solution.
And Flex has great layout power, so why not use it? Just note that the View tag in RN defaults to column, and if you don’t change the rest of RN to align with it, you need to explicitly declare the main axis everywhere you use display: flex.
Write based on BEM style
RN actually supports only one style declaration, which is to declare the style attribute:
<View style={{ height: '100%' }}
Copy the code
As a result, Taro only supports class selectors (eventually compiled into object literals) on RN. The Block Element Modifier (BEM) applies appropriately here:
- Avoid style conflicts (RN, applets style independent, but H5 is not)
- Self-interpretation, semantic
For example, a list of two elements per row, with the last element of each row having a specific style, is easy to implement with the pseudo-element selector :nth-child(even), which computs itself in RN:
{list.map((item, index) = > (
<View className={classNames('block__element',
index % 2= = =1 && 'block__element--even'
)} />
)}
Copy the code
Writing a class style based on BEM, without relying on other selectors, makes the code a little more cumbersome, but it also ensures that everything works without support issues.
Override the component style with the style property
Applets and RN have problems passing styles between pages and components:
// There is currently no implementation of Taro RN to pass a className style to a component
<CompA compClass='my-style' />
// CompA, the style does not work
<View className={this.props.compClass} />
Copy the code
The above scenario applet can be implemented using externalClasses, but the documentation on the official website states that “when using a normal style class and an external style class on the same node, the priority of the two classes is undefined, so it is best to avoid this situation”. Global styles are fine, but they are hard to maintain.
Then, overriding component styles via style is the only option. Note that style files are compiled to be compatible with multiple applications, but the style method requires runtime compatibility:
<Comp style={postcss({ background: '#fff'})} / >RN does not support background-color
function postcss(style) {
const{ background, ... restStyle } = styleconst newStyle = {}
if (background) {
newStyle.backgroundColor = background
}
return{... newStyle, ... restStyle } }Copy the code
From this point of view, styled components may be the best style scheme for multi-development, yet Taro does not yet support them. In addition, it is mentioned in the official document of wechat mini program that “try to avoid writing static styles into style, so as not to affect the rendering speed”. All styles are written into the style attribute, but only covering a small number of styles will not have a great impact.
The style is compatible with
For example, white-space: Nowrap will cause errors in RN. Taro provides a solution to this problem:
.text {
/*postcss-pxtransform rn eject enable*/
white-space: nowrap;
/*postcss-pxtransform rn eject disable*/
}
Copy the code
However, there is more than one place in the project that has this problem. It is not very beautiful to be realistic, which can be slightly encapsulated with Sass Mixins:
@mixin eject($attr, $value) {
/*postcss-pxtransform rn eject enable*/
#{$attr}: $value;
/*postcss-pxtransform rn eject disable*/
}
.text {
@includes eject(white-space, nowrap);
}
Copy the code
Sass Mixins cannot solve the differences, but it is a reasonable way to deal with some incompatible styles through Sass Mixins. The code is relatively beautiful and easy to maintain.
End capacity difference
Compared to the style, the difference between RN’s capabilities is good, and there are objective differences between RN’s capabilities, not to mention that there are already a lot of differences between iOS and Android.
Taro.(Get /set)StorageSync, then use async/await + Taro.(get/set)Storage, or have to use the environment judgment mode.
Taro provides process.env.taro_env for environmental judgment. Most small differences can be resolved in this way:
function foo() {
if (process.env.TARO_ENV === 'weapp') {
// wechat applet logic
}
if (process.env.TARO_ENV === 'h5') {
/ / H5 logic
}
if (process.env.TARO_ENV === 'rn') {
/ / RN logic}}Copy the code
At this time, it also tests the developer’s ability of encapsulation. It is generally recommended to unify the judgment of these different logic, such as encapsulation in SRC /utils, providing consistent interface externally, and trying not to mix too many judgments in business pages.
For problems that cannot be handled by simple environmental judgment, native development can only be used. For example, Taro does not support WebView components on the RN side, so you should implement native RN components yourself (note: Taro v1.2.16 supports:
import { WebView } from '@tarojs/components'
// Taro has Tree Shaking enabled, so you can import various components
// Unused content is automatically removed during compilation
import WebViewRN from './rn'
export default class extends Component Render () {render() {, {/* Call */ based on the environment}
return process.env.TARO_ENV === 'rn'? <WebViewRN src={this.url} /> : <WebView SRC ={this.url} />}} // native RN page, WebView import Taro from React-native { Component } from '@tarojs/taro' import { WebView } from 'react-native' export default class WebViewRN extends Component { render() { return <WebView source={{ uri: this.props.src }} /> } }Copy the code
Process.env.taro_env is handled compile time, not run time, and Taro has introduced Tree shaking, which means that WebViewRN written natively above is not packaged unless RN is compiled. This ensures that unsupported content will not be introduced when compiled to other ends (otherwise, an error will be reported when referring to react-native on a non-Rn end).
Native pages can be introduced, multi – terminal problems also have a basic implementation guarantee.
TARO_ENV.TARO_ENV. The Taro V1.2.17, released in mid-March, provides a more convenient cross-platform development approach that is more suitable for multi-terminal compatibility:
// For example, there was a component called test.js
// To implement h5 and RN components, name the components as follows:
/ / test. The h5. Js test. Rn. Js
// All you need to do is introduce test, and Taro will automatically introduce the appropriate components based on the environment
// There is no need to write process.env.taro_env
import Test from '.. /.. /components/test'
render() {
For example, when compiling H5, the actual imported component is test.h5.js
// The component only needs to conform to the unified external interface
return <Test data={data} onClick={onClick} />
}
Copy the code
Taro a pit at the RN end
Taro RN terminal is still a lot of small problems, this project development process also incidentally solved several bugs:
In addition, there are a number of problems, pr has not been mentioned to solve the time, for the moment to bypass, but the height of the adaptive pit is worth mentioning.
Small programs and H5 can realize self-adaptation with RPX/EM, while the self-adaptation scheme of RN is more troublesome. Generally, width and height should be obtained through Dimensions before conversion. Taro provides pxTransform() to solve this problem, but it is not considered when compiling rN-side style files, that is, width: 100px will compile to width: 50 instead of width: 50. Taro. PxTransform (100), also cannot be adapted to different screen sizes.
For this reason, the Taro RN side is currently not adaptive, either using style + taro.pxTransform () for non-percentage width and height, or having to write a script to process the compiled style file.
Issue 2204 has been submitted for this issue. If necessary, please pay attention to the progress of solution
Taro H5 end pit
Taro’s support for H5 is acceptable. If you only want to achieve compatibility between applets and H5, you are still recommended to use BEM writing style + style attribute to cover the component style. It can effectively avoid many limitations of applets custom components, but CSS features are not as constrained as RN. Transition, pseudo-elements, and so on are stress-free to use.
This can be solved by devServer.proxy. Besides, static resources packed by compilation are fixed file names. I won’t repeat it.
After all, Taro’s design is based on wechat small program to supplement the differences of other ends. Compiling small program is the built-in component of small program directly used. However, H5 requires a set of built-in components with equivalent functions. The onerous details needed to achieve consistency are also imaginable.
Taro. SwitchTab () does not support Taro. SwitchTab ().
if (process.env.TARO_ENV === 'h5') {
Taro.navigateBack({ delta: Taro.getCurrentPages().length - 1 })
setTimeout((a)= > { Taro.redirectTo({ url }) }, 100)}Copy the code
Fortunately, H5 TabBar is scheduled to be refactored in the upcoming 1.3 release, which should fix this problem.
other
To achieve multi-terminal unity, can say the details of the point is too much, although the above implementation ideas are simple, but behind are also implied on the difference between the struggle and trade-off, this article is only listed the most basic points, for the elaboration of Taro multi-terminal development of the core ideas.
This project code doesn’t do too much packaging, easy to read, also achieved enough style details on pit, specific points on pit, matters needing attention in the code to comment / / TODO (Taro is not support), / / NOTE (development skills, NOTE) indicate that more content is open to the practice and experience.
ps: Taro’s version update speed is relatively powerful. This project was originally developed based on V1.2.11. It was released with V1.2.13 on February 19, and has been updated to v1.2.17 on March 11. The author will try to improve the content of this paper and github code as the version changes.
conclusion
As mentioned in the preface, although Taro is designed to be multi-terminal, its focus is on the small program end, and the current support situation of RN end is not particularly ideal. However, after fully understanding the differences and mastering the correct multi-terminal development posture (especially in style management, to avoid the need for a big knife after the project is formed to compatible), it is completely possible to show their skills on simple projects.
It is common to develop a small program in 2 weeks. However, it only took 2 weeks to complete the small program (wechat, Alipay, Baidu, etc.), H5, React Native, and the subsequent update only needed to change one place. The output and maintenance efficiency was really amazing. This is the appeal of “Write Once, Run Anywhere “(although in the front end it could easily be “Write Once, Debug everywhere” 😂)
I believe that with the rising popularity of small programs, there will be more excellent open source frameworks and solutions. We don’t tend to build wheels, we focus on how best to develop multi-application based on existing solutions. If you are interested in the front end, you may as well join us to do things together.
This article is produced by Qudian FED and first published in Qudian Institute of Technology. Project open source address github.com/js-newbee/t… .