• 中文
  • ENGLISH
Rax深入浅出 – 样式编写
2017/02/09

前面开篇里提到过,使用Rax/Weex构建的是:一个移动端·跨平台应用

那么,从样式编写的角度,该如何看待移动端跨平台这两个Rax/Weex特性?

  • 移动端:宽高/边距,甚至是字体大小等样式,需要能够适配不同屏幕尺寸的手机
  • 跨平台:同一份样式代码需要能够同时在Web/IOS/Android平台上运行

面对上述的问题,其实在以往的Web App实践中,已经有了一些类似的经验沉淀:

  • 通过「rem」「flexbox」可以做到手机屏幕适配
  • 通过「css in js」的方式,让JS拥有描述样式的能力,从而做到将同一份代码运行在不同的JS Engine里(抛弃了原来Web固有的CSS样式表)

Rax/Weex也正是基于类似上述的思路实现了对应样式API,接下来主要以Rax为例介绍如何编写样式。

基础

Rax提供的大多数元件都支持style属性,用来初始化或修改元素样式,就像原生html标签的style属性一样(<img style="width:40px" />),对比下Rax的样式编写方式:

// 定义样式集合
const styles = {
  container: {
    padding: '20rem',
    height: '300rem',
    fontSize: '28rem',
    backgroundColor: 'red'
  },
  container_title: {
    color: '#fff',
    fontSize: '32rem' // font-size不会继承父或祖先元素,需重新定义
  }
};

// 应用样式
const App = (props) => {
  return (
    <View style={styles.container}>
      <Text style={styles.container_title}>Hello World</Text>
    </View>
  );
};

与原生的html标签style属性不同,Rax元件接收的style属性类型是一个对象

受限于Native元素渲染方式,元件之间的嵌套关系并不会带来样式继承的效果,因此上述例子中定义了一个样式集合,各自描述对应元素的样式(比如:fontSize)。

关于适配

在传统的Web中,根据手机屏幕宽度的值,动态改变html font-size,再结合rem单位对不同尺寸的手机屏幕进行适配(对于样式数值的转换,往往会通过sass/less来进行预编译)。

而在Weex/Rax中,同样是根据手机屏幕宽度,由于样式已经通过「css in js」的方式引入,所以动态转换用户传递参数(样式数值)就可以达到适配的目的(这里的数值转换,与之前的不同,因为发生在javascript运行时)。

如何方便的还原视觉稿?

一般设计师给到前端同学的,都是以iPhone6为样板的双倍高清设计稿,所以适配方案的对应的基准宽度就是375 * 2 = 750,对应的样式数值转换公式类似如下(以Web为例):

outpuValue = inputValue * (document.documentElement.clientWidth / 750);

对于一个设计稿上,宽高400×200的div,它对应的样式代码以Rax书写方式为例:

const style = {
    width: '400rem',
    height: '200rem'
};

通过Rax内部执行,最终呈现在不同的手机屏幕上的真实宽高,如下:

机型 屏幕宽度 div真实宽高
iPhone5 320px 170.7×85.3
iPhone6(设计稿) 375px 200×100
iPhone6+ 414px 220.8×110.4

也就是说:我们在 750 的设计稿上量到的数值是多少,代码编写就写多少(方便了很多,省去了心算)。

关于布局

在Weex/Rax中,不再支持float布局,取而代之的是flexbox布局(对于移动端而言,理应如此)。

更多关于「Weex Flexbox」,详见这里。

关于单位

Weex只支持px长度单位,不支持相对单位(em、rem),并且它将在JavaScript运行时和本机渲染器中解析为数字类型,所以省略px 单位后缀,直接写数字也是可以的。

/* 以下两种写法,效果是一样的,更推荐带单位的写法 */
.classA { font-size: 48; line-height: 64; }
.classB { font-size: 48px; line-height: 64px; }

而Rax支持两种单位:pxrem,或者不带单位后缀。

const styles = {
    borderWidth: '1px',
    height: '100rem',
    width: 750
};

如何理解?

Rax在Web端,重写了H5-RenderEngine;在Native端,底层调用的仍是Weex-RenderEngine(IOS/Android)。

在将具体的元素样式,传递给Weex之前,Rax针对单位做了一层处理,转换规则(这里假设手机屏幕宽度/deviceWidth为375px),如下:

类型 Rax-Web Weex-Native
无单位 750 ‘375px’ ‘750px’
rem为单位 ‘100rem’ ’50px’ ‘100px’
px为单位 ‘1px’ ‘1px’ ‘1px’

Rax中依然推荐使用带单位的方式,如:{ width: '750rem', borderWidth: '1px' },比较直观。

结论:

  • Rax依靠Weex的Native渲染能力,所以这里的Weex-Native指的是Rax调用Weex SDK API渲染UI时,传的样式值,关于Native的具体的适配处理由Weex-renderEngine完成。
  • 在Rax中,如果数值单位是px,那么数值是不会根据手机屏幕做数值转换的,如:{ borderWidth: '1px' } 在deviceWidth为375px的手机上,不会变成{ borderWidth: '0.5px' },这其实很好的解决了很多Android手机在Web端因为不支持0.5px而导致线条无法呈现的问题,但同时也会使得一些原本支持0.5px的IOS手机得不到细线条的美感,倘若你真的追求极致的web端优雅降级,可以在html页面中引入「lib-flexible.js」通过页面scale的方式,支持IOS border 0.5px。

关于颜色

Weex/Rax支持多种格式的颜色值书写方式,基本对准了W3C标准,以Weex书写方式为例:

.my-class { color: red; }                  /* 色值关键字 */
.my-class { color: #f00; }                 /* 精简十六进制*/
.my-class { color: #ff0000; }              /* 十六进制 */
.my-class { color: rgb(255, 0, 0); }       /* RGB */
.my-class { color: rgba(255, 0, 0, 0.5); } /* RGBA */

一般情况下,不推荐使用「色值关键字」

高阶

事实上,在项目开发过程中,如果将所有的样式代码都写在JS文件里,会发现代码看起来很臃肿,或许我们更习惯于:分离样式代码

Webpack的诞生,推动了前端自动化工具的发展的同时,也加速了我们对前端的认知,不用再去一直等待标准规范的完全实现,有很多这样的例子:

  • babel-loader:jsx/es6/es7转换成es5
  • sass/less-loader:sass/less转换成css
  • vue-loader:实现了SFC(Single File Components)
  • weex-loader:实现了SFC的同时,还注入了__weex_require__(可以引用Native Module)

所以,代码的写法已经不受太多约束,因为可以有很多插件去支撑一个靠谱的想法。

同样的,Rax也提供了这么一个Webpack插件 – 「stylesheet-loader」,让我们能够在Rax应用中通过CSS写样式。

就像下面这样:

/* hello-world.css */

.container {
  padding: 20rem;
  height: 300rem;
  background-color: red;
}

.container_title {
  color: #fff;
  font-size: 32rem;
  border-width: 1px;
}

如何引入?

/* hello-world.js */
import styles from './hello-world.css';

webpack配置

{
    test: /\.css$/,
    loader: 'stylesheet'
}

除了样式代码的分离,少写几个引号之外,stylesheet-loader的好处还在于:

  • 对于Weex不支持的样式,能快速给予友好的warning提示
  • 可以跟sass/less-loader结合,使用变量、mixin、嵌套等预编译功能

warning提示

比如:Weex不支持floatz-index样式,那么stylesheet-loader会提示:

warning.png

用sass写样式

/* hello-world.scss */

/* 变量 */
$fontSize: 32rem;

/* 嵌套 */
.container {
  padding: 20rem;
  height: 300rem;
  background-color: red;
  .title {
    color: #fff;
    font-size: $fontSize;
    border-width: 1px;
  }
}

NOTE:默认情况下,stylesheet-loader不支持嵌套,即不支持.container .title { ... }的转换,需要开启一个配置项:transformDescendantCombinator,嵌套变量名将以_连接,如下:

.container .title { ... }

/* 转换成 -> */

.container_title { ... }

所以,一个完整的配置如下:

{
  test: /\.scss$/,
  loaders: [
    'stylesheet?transformDescendantCombinator',
    'sass'
  ]
}

参考资料

订阅我们