运行demo、调试
来自:前端调试。
npm run build
客户端demo运行
iOS(/examples/ios-demo/)
直接可以根据文档运行Xcode
Android(/examples/android-demo/)
Android Studio安装SDK
选择安装制定版本的SDK Tools:
参考:Hippy/issues/39。
配置环境变量
export ANDROID_HOME=/Users/「用户名」/Library/Android/sdk
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools
Task 'wrapper' not found in project
、Task 'prepareKotlinBuildScriptModel' not found in project
:
// `build.gradle`文件最后添加:
task wrapper(type: Wrapper) {
gradleVersion = '8.0.0'
}
task prepareKotlinBuildScriptModel {
}
运行Hippy demo项目(/examples/hippy-react-demo/、/examples/hippy-vue-demo/)
npm run hippy:dev
、npm run hippy:debug
Android调试(按步骤):
adb reverse tcp:38989 tcp:38989
chrome://inspect/#devices
打开Hippy debug tools for V8
根据https://hippyjs.org/#/guide/debug?id=android的要求,关闭
Discover USB devices
可以减少断连手机。
iOS调试(按步骤):
打开Safari,勾选:开发 -> 模拟器 xxx -> ️自动显示JSContext的网页检查器
调试H5页面时要关闭「️自动显示JSContext的网页检查器」,无效且影响页面切换性能。
App点击:本地调试 -> 点击调试
若Safari没有调试信息,则重启App
前端使用:
构建工具
webpack
网络请求
fetch
WebSocket
cookie
(NetworkModule)定时器
setTimeout/clearTimeout
、setInterval/clearInterval
日志
console.log/warn/error
console
日志会输出到iOS和Android系统日志中(Hippy 2.10.0之前)。
注意:若客户端系统打印多次日志,则可能是前端调用多次,也可能是客户端打印bug导致打印多次。
自定义字体
fontFamily
二分法定位问题
因为依赖客户端渲染的本质,所以最终需要在iOS和Android都真机测试才可以。
iOS和Android的渲染结果可能不同,样式(截断、遮挡)、事件冒泡情况、等,容易产生区别。
组件
<Image>
、<ListView>
、<Modal>
、<Navigator>
、<RefreshWrapper>
、<ScrollView>
、<TextInput>
、<Text>
、<View>
、<ViewPager>
组件功能需要完全按照文档描述来使用,不像前端标签那么灵活。如:
<View>
和<ScrollView>
不能混用,<ScrollView>
文档只有contentContainerStyle
而没有,style
<View>
内部不能直接放字符串而需要放组件。
前端组件最终是传递给客户端,由客户端组件来实现呈现
因此用客户端工具查看客户端组件时,可以根据组件名字前缀有Hippy
的,判断其来自于前端。
客户端都是单屏视口(可以理解为外层包裹着一个宽高等于视口的flex父级容器),借助某些组件的内部滚动来实现多屏效果。
所有滚动都是:组件内部滚动。
<ScrollView>
支持:横向 或 竖向滚动(horizontal
只能选择其一,不能同时横向、竖向滚动)。没有复用节点优化。
<ScrollView>
内可嵌套所有组件(包括:<ScrollView>
、<ListView>
、<ViewPager>
)。<ListView>
支持:竖向滚动。复用节点优化:自动删除不在可视区的节点,对进入可视区的节点进行按类型复用。
<RefreshWrapper>
包裹一个<ListView>
后支持:下滑刷新。
<ListView>
支持下拉、上拉刷新功能(renderPullHeader
/onHeaderPulling
/onHeaderReleased
、renderPullFooter
/onFooterPulling
/onFooterReleased
),可以不需要<RefreshWrapper>
配合下拉刷新。<ViewPager>
支持:横向切换(类似Swiper)。
overflow
只有hidden/visible
2个属性值)。Tips(bug?)
<Image>
需要显式设置width
和height
,才能显示。
若是资源图且不知道高宽比例,可以用Image.getSize
获取图片的宽高。
部分机型onError
无法正常触发。
可能是客户端对不同类型的图片地址采取的加载错误处理方案不同,如:http、base64、普通字符串、等。
padding
无法像CSS那样扩展图片的外部,因此若想要图片的点击范围大于图片内容,则需要在外部嵌套父级,父级设置padding
并把点击事件放在父级。
<Text>
Android:
ellipsizeMode
仅支持tail
。低版本Android机可能不支持opacity
效果,可能导致整个文本渲染消失
用字体颜色color: rgba(x,y,z, 透明度)
来代替。
渲染变化的节点,如果没有加style
,会被渲染为空。
强制加上style
设置一些样式内容。
某些渲染情况下,不设置color
时会变成透明文字
需要显式设置color
才能展示文字。
<Text>
有不同截断效果(ellipsizeMode),其他组件需要自己实现(计算字符数,结尾自己添加...
或图片覆盖)。<Text>
内的文字内容,无法渲染新值,只能展示首次渲染值。文本若不设置lineHeight
(或height
),可能导致渲染出的行高影响整体渲染,甚至影响祖父元素的高度或间距,无法达到确定的”稳定状态”。
若出现文字上下被截断的情况,则也试着设置大一些的lineHeight
去解决。
<View>
用flex-start/flex-end
处理。fontStyle
只有'normal/italic'
值,若要制作下划线或删除线等,则需要用额外节点处理(包裹一层
<View>
<Text numberOfLines={1}>文案</Text>
<View style=/>
</View>
numberOfLines
设置会影响最大高度,文字排版几行才有几行的高度。lineHeight
最好大于fontSize
某个值(如:某些默认字体下lineHeight
大于等于1.2倍fontSize
),否则可能会导致行高不够裁切文字。
与CSS类似:
- 字体内容(content-area)高度 与
font-size
和font-family
相关,与无关。line-height
字体内容(content area) + 行间距(vertical spacing) = 行高(line-height)。其中行间距分上下部分,间距对半分。
若行高(line-height) 小于 字体内容(content area),则行间距是负数,此时文字被裁切,上下行间部分重合。
<ViewPager>
height
和flexBasis
类似。
height
,否则会导致内容高度为0。height
(会导致切换到一半卡住),可以设置延迟时间等待切换结束之后再改变height
(>300ms)。onPageSelected
iOS初始化时不会 触发;Android初始化时会触发onPageSelected
onPageSelected
。
大部分(但不是所有)组件都有onLayout
当元素挂载或者布局改变时被调用。返回节点实时的:宽高(width
、height
)、距离父级顶部(0,0)距离(x
、y
)。
onLayout
,则可以滚动到指定组件位置和判断组件是否”曝光”。onLayout
是异步的,并且可能触发时间比较慢,在组件渲染完毕之后上百毫秒(250ms?)才触发,也不一定按照排列顺序触发(如前面的组件比较复杂等原因)。<View>
并利用它的onLayout
。<ListView>
改变渲染内容后(除了onEndReached
触发之外)有时无法再触发onEndReached
(除了onEndReached
触发之外)改变渲染内容时,改变<ListView>
的key
属性。
getRowType
返回类型根据版本会有不同,旧版SDK(@hippy/react)需要字符串类型,新版SDK需要数字类型。
会在客户端层面报错(非前端层面,因此safari不报错),类似:
Error setting property 'type' of ListViewItem with tag #153: JSON value '0' of type NSNumber cannot be converted to NSString
。
renderRow
不支持横向排列、不足换行的方案(flexDirection: 'row'
flexWrap: wrap或wrap-reverse
renderRow
renderRow
。<ScrollView>
、<ListView>
的滚动事件,需要onMomentumScrollEnd
(非用户触发的滚动结束)和onScrollEndDrag
(用户触发的滚动结束)配合使用<ScrollView>
配合使用contentContainerStyle
(在内层的内容容器生效)和style
(在外层容器生效)
<ScrollView>
会渲染里外2个容器:
flex相关样式可能要同时设置到style
和contentContainerStyle
上(尤其是在降级为H5页面时)
style
是<ScrollView>
自身外层的的样式,设置flex: 1
占满其父级剩余空间。
Android的borderRadius
有问题。
contentContainerStyle
是<ScrollView>
内部包裹一层的样式,设置flexGrow: 1
可以使子项占满父级剩余空间并且正常滚动。
e.g.
实现:内容不够时子项拉伸,内容足够时滚动 ```jsx <View style={styles.container}> <View style= /> <ScrollView style={styles.scrollviewStyle} contentContainerStyle={styles.scrollviewContentContainerStyle}> <View style={styles.viewStyle}>1个或多个,满足效果</View> <View style= /> </View> const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#a2a2a2", alignItems: "center", }, scrollviewStyle: { flex: 1, backgroundColor: "yellow", width: 300, }, scrollviewContentContainerStyle: { alignItems: "center", background: "white", padding: 10, // 关键设置 flexGrow: 1, // 注意,不可以用:`flex: 1`。省去则仅正常滚动,不会内容不够时还占满父级 }, viewStyle: { height: 200, backgroundColor: "pink", width: 200, marginVertical: 50, // 关键设置 flex: 1, // 或:`flexGrow: 1` }, }); ```
horizontal={true}
横向滚动的左右间距问题,用 style
contentContainerStyle
<ScrollView>
转换为<View>
时注意是否有contentContainerStyle
,若有,则可能需要嵌套<View>
e.g.
```jsx <ScrollView style={样式1} contentContainerStyle={样式2}>内容</ScrollView> 转换 => <View style={样式1}> <View style={样式2}> 内容``` </details>
获取:滚动距离、内容总高度(视口高度+最大滚动距离 === 子级的总高度)、布局总高度(视口高度)
<ListView>
可能类似。
// 下面3个值能够实时获取
contentOffsetY = 0; // 滚动距离
contentSizeY = 0; // 内容总高度(视口高度+最大滚动距离 === 子级的总高度)
layoutMeasurementHeight = 0; // 布局总高度(视口高度)
render () {
return (
<ScrollView
onLayout={(data) => {
this.layoutMeasurementHeight = data.layout.height
}}
onMomentumScrollEnd={(data) => { // 滚动停止(非用户拖拽导致)
this.contentOffsetY = data.contentOffset.y;
this.contentSizeY = data.contentSize.height
this.layoutMeasurementHeight = data.layoutMeasurement.height
}}
onScrollEndDrag={(data) => { // 滚动停止(用户拖拽导致)
this.contentOffsetY = data.contentOffset.y;
this.contentSizeY = data.contentSize.height
this.layoutMeasurementHeight = data.layoutMeasurement.height
}}
>
<View
onLayout={(data)=>{
this.contentSizeY = data.layout.height
}}
>
真正布局内容。。。
</View>
</ScrollView>
)
}
<ScrollView>
默认设置flex: 1
效果,可在外部加一个<View>
节点限制来规避(或设置flex: 0
,但未成功)实现随着内容高度变化而自适应滚动或不滚动
// 内容不够则收缩至实际高度;内容超过外层flex: 1拥有的最高高度则内容滚动
<View style=>
<ScrollView>
...内容
</ScrollView>
</View>
// 内容不够则收缩至实际高度,内容超过外层最大高度则内容滚动
<View style=>
<ScrollView>
...内容
</ScrollView>
</View>
key
的diff有bug
key
的组件可能挂载2次(触发2次componentDidMount
)key="index"
规避padding
或margin
翻倍问题render
返回多个节点会有渲染问题(React已支持)
<TextInput>
keyboardType="phone-pad"
。multiline={false}
,否则多行。onEndEditing
事件、onBlur
事件、点击其他区域。onEndEditing
事件、onBlur
事件、onChangeText
事件Android:
editable
为false
。调用节点.blur()
(作用:触发节点onBlur
事件)无效。
需要设置editable
先为false
再为ture
才会触发节点onBlur
事件(可能是需要触发页面渲染)。
onBlur
事件触发还会隐藏软键盘。
iOS:
软键盘被唤起时,页面不会向上顶起,因此可能会挡住要输入的区域。
利用onKeyboardWillShow
事件获得软键盘弹起时高度,把输入框区域垫高;输入完毕之后再恢复高度。
书写方式:
<TextInput
multiline={false}
style=
onChangeText={text => {
// text -> data
}}
value={data}
/>
「变量名」 && 「组件或嵌套组件」
,当「变量名」
为false
时,会用一个新的空<Text>
替换「组件或嵌套组件」
false && 「组件或嵌套组件」
因为一直都是false
,所以不会有额外的空<Text>
出现,直接忽略本句渲染。
用三元运算符替换:「变量名」 ? 「组件或嵌套组件」 : null
(「变量名」
的true/false
切换时,仅新增/删除「组件或嵌套组件」
,不会有额外的空<Text>
出现替换)
new Hippy
的entryPage
)每个组件(包括<Text>
)都是块状的,不能像H5那样内联展示。但可以用如下方式制作类似inline-block
的文字,支持不同颜色、换行、事件。
import { Text } from "react-native";
<Text> {/* 必须是 Text 包裹 Text */}
<Text> {/* 可以加样式、点击事件等所有属性 */}
inline-block文字1
</Text>
<Text
style=
onClick={() => {
console.log('点到我了')
}}
>
inline-block文字2
</Text>
<Text> {/* 可以加样式、点击事件等所有属性 */}
inline-block文字3
</Text>
</Text>
列表项目曝光
<ListView>
,则用onAppear
或onWillAppear
判断某子节点是否曝光。<ScrollView>
(<ListView>
也同理),则用onLayout
按顺序记录每个子节点的宽高,然后 父级onScroll
的滚动距离 与 父级宽高、各子节点宽高 的关系。制作swiper
最基本的滑块
<ScrollView>
配置pagingEnabled={true}
<ViewPager>
有异化效果的滑块
(必须宽度满屏)<ScrollView>
监控滚动事件,滚动结束后补偿滚动从而使子项居中,还可以利用滚动距离设置每一项的异化。
e.g.
```tsx import { View, ScrollView, ScrollEvent } from "react-native"; import { useRef } from "react"; export default () => { const ScrollViewRef = useRef<ScrollView | null>(null); return ( <View style=> <ScrollView horizontal={true} scrollEventThrottle={16} showScrollIndicator={!!__DEV__} contentContainerStyle={样式} ref={ScrollViewRef} onScroll={(obj: ScrollEvent) => { // 异化子项:为每个子项计算与视图正中的距离 }} onScrollBeginDrag={() => { // 停止自动轮播 }} onScrollEndDrag={(obj: ScrollEvent) => { // 补偿滚动到合适位置(ScrollViewRef.current.scrollTo) // 开始自动轮播 }} onMomentumScrollEnd={(obj: ScrollEvent) => { // (可选)补偿滚动到合适位置(ScrollViewRef.current.scrollTo) }} > {子项} </ScrollView> </View> ); } ```
模块
动画组件
提供给前端React/Vue渲染使用的按时间变化的style中某样式属性值。
Animation
、AnimationSet
Animation
针对一个属性的一个配置后的变化;AnimationSet
针对一个属性的多个配置后的变化。若需要多个属性同时变化,则多个属性每个设置一个Animation
或AnimationSet
,同时触发。
e.g.
```tsx import { View, Text, AnimationOption, Animation } from "react-native"; import { useEffect, useState } from "react"; const DEFAULT = 1; const NEXT_VALUE = 0; // 从0->1动画 const animationConfig1: AnimationOption = { mode: "timing", timingFunction: "ease-in-out", startValue: NEXT_VALUE, toValue: DEFAULT, duration: 3000 }; // 从1->0动画 const animationConfig2: AnimationOption = { mode: "timing", timingFunction: "ease-in-out", startValue: DEFAULT, toValue: NEXT_VALUE, duration: 3000 }; export default function Demo2() { const [opacity, setOpacity] = useState<Animation | typeof DEFAULT>(DEFAULT); useEffect(() => { if (opacity !== DEFAULT) { opacity.start(); opacity.onAnimationEnd(() => { opacity.destroy(); }); } }, [opacity]); return ( <> <View style= /> <Text onClick={() => { setOpacity(new Animation(animationConfig1)); }} > opacity: 0=》1 </Text> <Text onClick={() => { setOpacity(new Animation(animationConfig2)); }} > opacity: 1 =》 0 </Text> </> ); } ```
Tips(bug?)
动画未完成不能中途updateAnimation
(或者参数startValue
必须是动画当前值)。
直接操作最终的客户端UI组件样式(跳过前端执行后再传递给客户端渲染),性能更佳。
e.g.
```tsx import { View, UIManagerModule } from "react-native"; import { useRef } from "react"; export default () => { const ViewRef = useRef<View | null>(null); return ( <View style={原样式} ref={ViewRef} onClick={() => { ViewRef.current && UIManagerModule.getElementFromFiberRef?.(ViewRef.current)?.setNativeProps?.({ style: {新样式(会合并到原样式中)}, }); }} /> ) } ```
AsyncStorage
异步、持久化的键-值存储系统
hippy
返回Promise实例。
h5
返回与window.localStorage
一致。
BackAndroid
监听Android实体键的back,在退出前做操作或拦截
BackAndroid.addListener(方法名)
BackAndroid.removeListener(方法名)
Clipboard
读取或写入剪贴板
ConsoleModule
提供了将前端日志输出到iOS终端日志和Android logcat的能力。(Hippy 2.10.0之后,console
不再能输出至终端日志)
Dimensions.get('window或screen')
获取设备的Hippy Root View或者屏幕尺寸的宽高
按照设计稿等比例宽高:
自由放大缩小
width: 设计稿此物体宽 / 设计稿总宽 * Dimensions.get("window").width
height: (设计稿此物体高 / 设计稿此物体宽) * 前面的width
间距固定、单个物体占满:
width: Dimensions.get("window").width - 固定宽度
height: (设计稿此物体高 / 设计稿此物体宽) * 前面的width
间距固定、多个物品平均占满:
width: (Dimensions.get("window").width - 固定宽度) / 几个物品
height: (设计稿此物体高 / 设计稿此物体宽) * 前面的width
若是
position: "absolute"
要占满全屏且父级已占满全屏(高满屏或宽满屏),则可以用top: 0; bottom: 0;
代替Dimensions.get("window").height
,left: 0; right: 0;
代替Dimensions.get("window").width
。
Tips(bug?)
(Android全面屏手机)Dimensions.get('window').height
或横屏的Dimensions.get('window').width
有可能会自动减少StatusBar的高度。
(React Native问题)市场上大多数的Android全面屏手机,一般都是以 刘海屏、水滴屏、挖孔屏 等异形屏的形式存在。屏幕在显示UI界面时,顶上的挖孔部分一般都是作为 状态栏 的形式存在。这其中的一些机型,在计算
Dimensions.get('window').height
时不将状态栏计算进去,但在实际渲染界面时又把状态栏作为可视区域。
(网上较多是根据Dimensions.get('window').height/Dimensions.get('window').width
比值判断出需要处理的Android全面屏,再一刀切加上StatusBar的高度。我感觉不妥,)需要更多地利用flex布局而不是确定尺寸的布局,或在最外层满屏的<View>
上用onLayout
异步获得渲染出的满屏高宽。
ImageLoaderModule
对远程图片进行相应操作:获取图片大小、预加载图片。
NetInfo
获取网络状态
NetworkModule
网络相关的模块,目前主要是操作Cookie。
PixelRatio
获取设备的像素密度(pixel density)
Platform
判断平台
Stylesheet
(.hairlineWidth
、.create()
)
CSS样式表
UIManagerModule
提供了操作UI相关的能力。
引入base64
默认情况下,webpack配置了针对小于某KB的图标进行转base64,所以一般情况不需要显式使用这个。
!!url-loader?modules!路径
,如:import defaultSource from '!!url-loader?modules!./defaultSource.jpg';
自定义组件、自定义模块
自定义组件
在需要渲染的地方通过nativeName
属性指定到终端组件名称。
e.g.
<div nativeName="LinearGradientView">
自定义模块
callNative
或callNativeWithPromise
与Native通信方式(桥协议
、JSI):import { callNative, callNativeWithPromise } from "@hippy/react"
通用的,H5与Native通信方式:
桥协议
或自定义URL Scheme
+ WebView提供给Native调用的全局回调函数(或匿名函数)。
手势系统(点击事件、触屏事件)
所有组件(或自定义组件)均支持监听手势系统(点击事件、触屏事件)。
点击事件
onClick
:点击onPressIn
:开始触屏onPressOut
:结束触屏onLongClick
:长按回调参数:
{
name: 「事件名」,
id: 「控件的id」,
target: 「控件的id」
}
触屏事件
onTouchDown
:开始触屏onTouchMove
:移动手指(持续触发回调)onTouchEnd
:结束触屏onTouchCancel
:被中断触屏
若触发onTouchCancel
则不触发onTouchEnd
。
回调参数:
{
name: 「事件名」,
id: 「控件的id」,
target: 「控件的id」,
page_x: 「触屏点相对于根元素的横坐标」,
page_y: 「触屏点相对于根元素的轴坐标」
}
事件
事件冒泡
点击事件、触屏事件均支持事件冒泡,由最上层组件往根元素冒泡触发事件回调。
false
,则冒泡。不返回值
、或返回任何除了 false
注意使用UI库时,某些组件是否进行事件冒泡拦截。e.g. 大部分
<Button>
的实现,点击事件不会继续向上冒泡。
事件捕获
在目标元素事件名添加Capture
后缀,e.g. onClickCapture
、onTouchDownCapture
。
事件拦截
父级组件拦截或中断子级组件的事件触发。所有触发在子级组件的事件都仅触发在父级组件。
onInterceptTouchEvent
:
true
:
onInterceptTouchEvent
为true
之前,子级组件已经在处理触屏事件,则子级组件将收到一次onTouchCancel
回调(如果子控件有注册该函数)。false
(默认):不拦截
onInterceptPullUpEvent
(貌似还未实现?)
事件穿透
客户端双端原生特性如此。
pointer-events: none;
)。对于结构复杂的情况,可能违背这个逻辑,导致有覆盖的节点就不穿透。
终端事件
import { HippyEventEmitter } from '@hippy/react';
const hippyEventEmitter = new HippyEventEmitter();
# 发送rotate事件并监听回调
this.call = hippyEventEmitter.addListener('rotate', evt => console.log(evt.result));
# 事件卸载
this.call.remove();
样式
- Hippy的还原设计稿方案,与客户端的方案基本一致:适配布局(与设计师协作思路)。
- React Native样式的子级。
- 注意属性值要求是
Number
还是String
,要严格遵守,不能互换。- 部分属性仅支持部分组件。如:
backgroundImage
仅支持<View>
、不支持。<Text>
长度单位
无单位、数值型(Number
)。含义是dp或pt。不支持:百分比、任何单位(。(部分机型)支持小数点、不取整。px/em/rem/vw/vh
)
1px或小数点长度:
(部分)客户端支持小数点数值(并非所有样式都支持,比如:有些机型不支持width
,但支持border
。以不同样式在具体机型的具体效果为准)。
import { View, PixelRatio, StyleSheet } from "react-native";
<View style=/>
<View style=/>
<View style=/>
{/* StyleSheet.hairlineWidth 注意要存在,返回0.33... */}
属性名的方向:
left === start
、right === end
。但貌似未实现或start
。end
盒模型
类型CSS的
box-sizing: border-box;
(布局所占宽度 = width = content + padding左右 + border左右。高度同理)。
width
height
max/min
+Width/Height
Tips(bug?)
minWidth/minHeight
可能是box-sizing: content-box 或 border-box
效果。maxWidth/maxHeight
可能完全不生效。minWidth
的父节点内部若有子级,则父级布局所占最小宽度 = minWidth = content(不包括:border
宽度(Number
)
borderWidth
仅能设置为相同数值。
borderTopWidth
borderRightWidth
borderBottomWidth
borderLeftWidth
颜色
属性值为颜色字符串。
borderColor
borderTopColor
borderRightColor
borderBottomColor
borderLeftColor
borderStyle
'solid'
(默认)'dotted'
'dashed'
padding
(Number
)
padding
仅能设置为相同数值。优先级最低。
paddingVertical
仅能设置为相同数值。
paddingHorizontal
仅能设置为相同数值。
paddingTop
paddingRight
paddingBottom
paddingLeft
margin
(Number
)
margin
仅能设置为相同数值。
marginVertical
仅能设置为相同数值。
marginHorizontal
仅能设置为相同数值。
marginTop
marginRight
marginBottom
marginLeft
布局(flex)
仅支持flex(,所以省略 ,所有组件全都只能是display: flex
flex
)。
与CSS的flex略有不同。
Flex容器
flexDirection
:决定主轴的方向。
'row'
:水平方向,起点在左端(CSS的flex-direction
默认:'row'
)。'column'
(默认):垂直方向,起点在上沿。flexWrap
:一条主轴排不下的情况,如何换行。
项的排布顺序。
与CSS的表现不同,也没有
配合。还是只能配合alignContent
justifyContent
、alignItems
使用。
justifyContent
:子项在主轴上的对齐方式(与轴的方向有关)。
与CSS的
justify-Content
表现一致。
alignItems
:子项在侧轴上的对齐方式(与轴的方向有关)。
默认:'stretch'
(CSS的align-content
默认:'normal'
)。
与CSS的
align-Items
表现一致。 无效属性:
:多根主轴(一条主轴排不下,有换行)的对齐方式(不换行则该属性不起作用)。alignContent
默认:
'flex-start'
(CSS的align-content
默认:'stretch'
)。
Flex子项
都在主轴方向上。
flex
:占用容器中剩余空间的大小。
0
(默认):元素没有弹性,不管父级容器空间,仅使用自身原本宽度/高度占据空间。
也可能因为内容太多溢出父级,建议用
-1
代替。
「正整数」
:元素有弹性,每个元素占用的剩余空间 = 自己的 flex 数值 / 所有同一级子容器的 flex 数字之和
。
此时
flex: 「正整数」
等价于flexGrow: 「正整数」, flexShrink: 1, flexBasis: 0
。
-1
:若空间不足则缩小到minWidth/minHeight
。若空间没有不足,则使用自身原本宽度/高度占据空间。
适合可变元素后面紧跟着内容的情况。
CSS的
flex-grow: 0
和flex-shrink: 1
的默认表现。 子项铺满父级的主轴剩余空间:flex: 1
;子项扩充满父级的侧轴:alignSelf: 'stretch'
。
多个项:若都设置
flex: 1
(无论各项内容不一),则所有项占用空间大小均一致。若都设置flexGrow: 1
,则各项先按照自己内容大小占据不同大小空间,再对剩余空间进行拉伸(各项所占空间不一致)。
flexGrow
:伸缩项目扩展的能力。
默认:0
。
与CSS的
flex-grow
表现一致。
flexShrink
:伸缩项目收缩的能力。
默认:0
(CSS的flex-shrink
默认:1
)。
与CSS的
flex-shrink
表现一致。
父级flexShrink: 1
+ 子级flex: 1
,可以实现:内容不够则收缩至实际高度/宽度;内容最大高度/宽度不超过外层flex: 1拥有的最大高度/宽度
<View>
<View style=>
<Text style=> // 也可以是<ScrollView>等 会变化的内容
自适应的内容
</Text>
</View>
<View>固定内容</View>
</View>
flexBasis
:伸缩基准值。
与CSS的
flex-basis
表现一致。
alignSelf
:单个子项覆盖父元素的alignItems
。
与CSS的
align-self
表现一致。 无效属性:order
颜色
包括所有颜色,如:border、字体、background、阴影。
rgb
e.g. '#f0f'
、'#ff00ff'
、'rgb(255, 0, 255)'
rgba
e.g. '#f0ff'
、'#ff00ff40'
、'rgba(255, 0, 255, 0.5)'
hsl
e.g. 'hsl(0, 33%, 69%)'
hsla
e.g. 'hsla(0, 33%, 69%, 0.5)'
'transparent'
没有
'none'
,只能用'transparent'
覆盖回无背景色。
颜色名字
字体
fontSize
lineHeight
color
fontWeight
transform: [{ 属性1: 值1 }, {属性2: 值2}]
属性值是
object[]
。
backfaceVisibility
元素背面朝向观察者时是否可见。
'visible'
(默认)'hidden'
定位
position
'relative'
(默认)'absolute'
起点:从父级border
内开始计算(父级的margin
、padding
不影响子级absolute
位置相对父级位置);从本身的margin
开始计算。
与CSS表现一致。
不支持 。若要制作fixed
fixed
效果,则:
有兄弟节点的情况
<View> // 此处父级必须是全屏才可以:占满上下左右全屏。因为"absolute"仅能针对父级
<ScrollView></ScrollView> // 兄弟节点
<View style=>类似fixed的效果</View>
<View style=>类似fixed的效果(不够优雅)</View>
<ScrollView></ScrollView> // 兄弟节点
</View>
没有兄弟节点的情况
<View style=> // 此处父级必须是全屏才可以:占满上下左右全屏。因为"absolute"仅能针对父级、才能让子级默认居中
<View>水平、垂直居中</View>
<View style=>类似fixed的效果</View>
<View style=>类似fixed的效果(不够优雅)</View>
</View>
若要制作全局定位(如:全屏居中、贴底部、贴顶部、等)的组件,因为'absolute'
仅针对父级 且 Android子级不能超出父级(强制'hidden'
)的限制,则其父级必须是占满上下左右全屏(视情况而定),但不要求是最外层元素。
top
right
bottom
left
zIndex
position: relative/absolute
的节点,均按代码书写顺序进行堆叠,zIndex
等效改变任何一个节点。
0
(默认)
(与CSS的层叠上下文略有不同)可以理解为:Hippy中任何一个组件,都形成层叠上下文且默认层叠顺序为
0
。因此层叠顺序仅在兄弟节点就确定了,不是兄弟节点无法改变堆叠顺序。
「整数」
overflow
overflow
只有2个属性值,没有CSS的,除了拥有滚动效果的组件(scroll
<ScrollView>
、<ListView>
、<RefreshWrapper>
、<ViewPager>
)之外,其他组件都不支持组件内滚动。
子元素超过父容器的宽度/高度后的显示情况。
'visible'
(默认)'hidden'
Android机型,大部分子级元素一定会被父级元素截断(就像:父级元素固定overflow: 'hidden'
不可改变),但是部分客户端组件还是需要父级设置overflow: 'hidden'
才截断。因此若需要做超过父级的节点,则必须往上不断提升该节点。但对于border
等,父级还是需要overflow: hidden
才能不被子级盖住。
背景
不能直接
,无效果。background
backgroundImage
- URL不能缩写。不能用
https://placeholder.com/?- 仅针对
<View>
等。
e.g. backgroundImage: import变量/require变量/"http资源"
backgroundPositionX
backgroundPositionY
backgroundColor
实现背景图某些效果
本地资源制作背景图方式:
<View>
<Image source= style= />
内部节点
</View>
或
<Image source= style=>
内部节点
</Image>
或
父级节点的style添加:backgroundImage
实现图片repeat效果
<Image>
的resizeMode
(cover
、contain
、stretch
、repeat
、center
)类似CSS的object-fit
+background-repeat
属性,但没有CSS那么多效果组合。
<Image
style=
source=
resizeMode="repeat"
/>
外边框圆角(Number
)
borderRadius
仅能设置为相同数值。
Tips(bug?)
<Text>
borderTopLeftRadius
、borderTopRightRadius
、borderBottomRightRadius
、borderBottomLeftRadius
Tips(bug?)
overflow: 'hidden'
有问题。<LinearGradientView>
(nativeName=”LinearGradientView”)对这种单独设置的属性支持不好(无效)。iOS若最终设置的4个角值不同,则导致backgroundColor
渲染问题。应避免使用这些单独设置的属性:
用一个子节点设置borderRadius
模拟
<View style=>...</View>
<View style=>
<View
style=
/>
<View style=>...</View>
</View>
e.g. 好像又能正常设置
```javascript tag: { position:'absolute', top: -pt(1.5), left: -pt(1.5), height: pt(16), paddingHorizontal: pt(6), backgroundColor:'#EA392C', borderTopLeftRadius: pt(8), borderBottomRightRadius: pt(8), } ```
opacity
阴影
boxShadow
+后缀
直接
boxShadow: '属性'
无效。
boxShadowOpacity: 0.6,
boxShadowRadius: 5,
boxShadowOffsetX: 10,
boxShadowOffsetY: 10,
// spread attr is only supported on iOS
// spread 属性仅适用于iOS
boxShadowSpread: 1,
boxShadowColor: '#4c9afa',
textShadow
+后缀
直接
textShadow: '属性'
无效。
textShadowColor
textShadowOffsetX // 大于0才有效
textShadowOffsetY // 大于0才有效
textShadowRadius
e.g.
```javascript textShadowColor: "#fff", textShadowOffsetX: 0.1, textShadowOffsetY: 0.1, textShadowRadius: 5 ```
样式写法
内联样式
e.g. <View style=/>
。
外部样式
无障碍
组件属性accessible
、accessibilityLabel
。
Tips(bug?)
Android:
组件均需要accessible
、accessibilityLabel
+ 事件(如:onClick={()=>false}
)才能支持无障碍选中。
注意加了事件之后,不要影响到原来的事件逻辑(如:阻止了原有冒泡逻辑)。
accessible={true}
,还会再选中子级的accessible
。iOS:
<Text>
不需要加accessible
、accessibilityLabel
(可以设置accessible={false}
关闭);其他组件需要加accessible
、accessibilityLabel
。accessible={true}
,就不会再选中子级的accessible
(但是可以阅读出所有子级的accessibilityLabel
内容)。