前面开篇里提到过,使用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支持两种单位:px
和rem
,或者不带单位后缀。
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不支持float
、z-index
样式,那么stylesheet-loader会提示:
用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'
]
}