Skip to content
Branch: master
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
README.md

README.md

使用 Taro 编译小程序 + H5 + React Native 的最佳实践

本页内容多数整理自趣店 FED 开源的首个 Taro 多端统一实例 - 网易严选(小程序 + H5 + React Native)- https://github.com/js-newbee/taro-yanxuan

目录

样式管理

样式是实现多端编译的核心挑战,因为 RN 端的样式只是实现了 CSS 的一个子集,具体可参考 React-Native 样式指南(这个指南的版本有点旧了,内容不一定准确)。

因此,要做到适配 RN,首先得遵循 React Native 的约束:

  1. 只用 flex 布局
  2. 只用 class 选择器,不用标签、子代、伪类等选择器
  3. 样式单位只使用 px,部分属性支持 %(相关说明
  4. 图片统一用 Image 标签引入,不用 background 的方式
  5. 文本要用 Text 标签包裹,文本的样式要加在 Text 标签上

对于第 1 点只用 flex 布局,需要注意 RN 的 View 标签有如下默认样式:

view {
  display: flex;
  flex-direction: column;
  position: relative;
  box-sizing: border-box;
}

其中 flex 的主轴与 Web 默认值不一致,因此凡是用到 display: flex 的地方都需要声明主轴,且需要设置部分全局样式:

view, div {
  box-sizing: border-box;
}

button {
  /* 去掉微信小程序 button 的边框 */
  &:after {
    display: none;
  }
}

对于第 2 点只用 class 选择器,建议使用 BEM 管理样式:

.comp {}
.comp__list {}
.comp__list-item {}
.comp__list-item--last {}

对于第 3 点,是由于 RN 不支持 background-image,需要使用 Image 标签配合 postion 实现

// 建议自行封装个 ImageBackground 组件解决此类需求
<View>
  <Image style={{ position: 'absolute' }} />
</View>

最后,对于需要覆盖组件样式的情况,建议使用 style:

// 组件调用
<Comp textStyle={{ color: 'red' }} />

// Comp 组件
<View>
  <Text style={this.props.textStyle}>Hello</Text>
</View> 

之所以选用这样的方案,是基于微信小程序、RN 自身的限制及 Taro 目前支持的程度所作出的妥协,具体可参考 微信小程序的限制 中的详细说明,在此不展开。

fixed 定位的实现

由于 RN 不支持 fixed 定位,有 fixed 定位的场景就需要改用 ScrollView + absolute 实现:

<View style={{ position: 'relative' }}>
  <ScrollView style={{ height: 'xxx' }}>内容区域</ScrollView>
  <View style={{ position: 'absolute' }}>固定区域</View>
</View>

内容区域需要用到 ScrollView,而 ScrollView 又需要设置高度,就需要去计算页面可用高度,但该值在各端上不太一致,需要根据 systemInfo 进行二次计算

// 各端返回的 windowHeight 不一定是最终可用高度(例如可能没减去 statusBar 的高度),需二次计算
const NAVIGATOR_HEIGHT = 44
const TAB_BAR_HEIGHT = 50
function getWindowHeight(showTabBar = true) {
  const info = Taro.getSystemInfoSync()
  const { windowHeight, statusBarHeight, titleBarHeight } = info
  const tabBarHeight = showTabBar ? TAB_BAR_HEIGHT : 0

  if (process.env.TARO_ENV === 'rn') {
    return windowHeight - statusBarHeight - NAVIGATOR_HEIGHT - tabBarHeight
  }

  if (process.env.TARO_ENV === 'h5') {
    return `${windowHeight - tabBarHeight}px`
  }

  return `${windowHeight}px`
}

差异兼容

Taro 的差异处理一般是使用 process.env.TARO_ENV 进行判断,在编译时会自动去掉非当前编译环境的内容:

//  源代码
if (process.env.TARP_ENV === 'weapp') { // 微信小程序
  console.log(1)
} else if (process.env.TARP_ENV === 'alipay') { // 支付宝小程序
  console.log(2)
}

// 例如编译支付宝小程序时,编译之后的代码
{
  console.log(2)
}

另外一种情况,则是要根据不同环境引入不同组件,Taro 有引入了 Tree skaing,配合上述环境判断方式,很方便能够实现:

import Comp from './comp'
import CompAlipay from './comp.alipay'

render() {
  return (
    process.env.TARO_ENV === 'alipay' ? <CompAlipay /> : <Comp />
  )
}

当编译成支付宝小程序时,只会保留 CompAlipay 组件,不会将 Comp 引入,也就避免引入了其他端不支持的内容。

H5

路径别名

若要在 Sass 中使用别名,如 @styles 指向 src/styles,需要设置 h5.sassLoaderOption,具体配置见 设置路径别名 alias

跨域

跨域最好的解决方案是设置 CORS,如果没有条件,在开发时要解决跨域问题可以配置 devServer 的 proxy:

// config/dev.js

// 需要把 package.json 中 scripts 的 "dev:h5": "..." 改成:
// "dev:h5": "CLIENT_ENV=h5 npm run build:h5 -- --watch"
const isH5 = process.env.CLIENT_ENV === 'h5'
const HOST = '"http://xxx"'

module.exports = {
  defineConstants: {
    HOST: isH5 ? '"/api"' : HOST
  },
  h5: {
    devServer: {
      proxy: {
        '/api/': {
          target: JSON.parse(HOST),
          pathRewrite: {
            '^/api/': '/'
          },
          changeOrigin: true
        }
      }
    }
  }
}

打包静态资源带 hash 值

Taro 编译 H5 时静态资源是固定的文件名:

<!-- css -->
<link href="/css/app.css" rel="stylesheet"></head>

<!-- js -->
<script type="text/javascript" src="/js/app.js"></script>

<!-- 图片 -->
background:url(/static/images/bg.png)

但这样不利于缓存、版本控制,建议配置 webpack 给静态资源带上 hash:

// config/index.js
h5: {
  publicPath: '/',
  staticDirectory: 'static',
  output: {
    filename: 'js/[name].[hash].js',
    chunkFilename: 'js/[name].[chunkhash].js'
  },
  imageUrlLoaderOption: {
    limit: 5000,
    name: 'static/images/[name].[hash].[ext]'
  },
  miniCssExtractPluginOption: {
    filename: 'css/[name].[hash].css',
    chunkFilename: 'css/[name].[chunkhash].css'
  }
}

支付宝小程序

网络请求

(有待验证新版本是否已解决了该问题)支付宝小程序的网络请求默认的 content-type 是 application/x-www-form-urlencoded,若将 content-type 设置为 application/json,还需要手动将 data 转为字符串,否则会出现开发者工具中网络请求正常,但体验版网络请求异常的情况

Taro.request({
  url,
  data: method === 'POST' ? JSON.stringify(data) : data,
  method,
  header: { 'Content-Type': 'application/json' }
})
You can’t perform that action at this time.