diff --git a/README.md b/README.md index bbd6ae55a27..02c62366e1b 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Hippy is now applied in [Tencent](http://www.tencent.com/) major apps such as Mo * Excellent performance with JS engine binding communication. * Build-in recyclable component with better performance. * Smoothly and gracefully migrate to Web browser. -* Fully supported Flex [Layout engine](./layout). +* Fully supported Flex [Layout engine](https://github.com/Tencent/taitank). ## 🔨 Getting started diff --git a/devtools/devtools-integration/native/src/v8/trace_control.cc b/devtools/devtools-integration/native/src/v8/trace_control.cc index 1b1b3972758..6e0d49104b1 100644 --- a/devtools/devtools-integration/native/src/v8/trace_control.cc +++ b/devtools/devtools-integration/native/src/v8/trace_control.cc @@ -67,6 +67,15 @@ void TraceControl::StartTracing() { auto trace_config = v8::platform::tracing::TraceConfig::CreateDefaultTraceConfig(); trace_config->SetTraceRecordMode(v8::platform::tracing::TraceRecordMode::RECORD_CONTINUOUSLY); trace_config->AddIncludedCategory(kTraceIncludedCategoryV8); + trace_config->EnableSystrace(); + trace_config->AddIncludedCategory("devtools.timeline"); + trace_config->AddIncludedCategory("v8.execute"); + trace_config->AddIncludedCategory("disabled-by-default-devtools.timeline"); + trace_config->AddIncludedCategory("disabled-by-default-devtools.timeline.frame"); + trace_config->AddIncludedCategory("disabled-by-default-devtools.timeline.stack"); + trace_config->AddIncludedCategory("disabled-by-default-v8.cpu_profiler"); + trace_config->AddIncludedCategory("disabled-by-default-v8.cpu_profiler.hires"); + trace_config->AddIncludedCategory("latencyInfo"); v8_trace_control_->StartTracing(trace_config); tracing_has_start_ = true; } diff --git a/docs/README.md b/docs/README.md index fd2dda610ec..23fa2f74554 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,8 +1,8 @@ # Hippy 简介 -版本:3.3.0 +版本:3.3.2 -更新时间:2024-6-26 +更新时间:2024-11-12 SDK介绍:Hippy 是 TDF 腾讯端框架(Tencent Device-oriented Framework)下的开源跨平台应用开发解决方案。Hippy 可以理解为一个精简版的浏览器,从底层做了大量工作,抹平了 iOS 和 Android 双端差异,提供了接近 Web 的开发体验,目前上层支持了 React 和 Vue 两套界面框架,前端开发人员可以通过它,将前端代码转换为终端的原生指令,进行原生终端 App 的开发。同时,Hippy 从底层进行了大量优化,在启动速度、渲染性能、动画速度、内存占用、包体积等方面都提供了业内顶尖的性能表现。 diff --git a/docs/api/_sidebar.md b/docs/api/_sidebar.md index e3f5b9ca36c..9d174cd59a0 100644 --- a/docs/api/_sidebar.md +++ b/docs/api/_sidebar.md @@ -8,6 +8,7 @@ * [手势系统](api/hippy-react/gesture.md) * [自定义组件和模块](api/hippy-react/customize.md) * [转 Web](api/hippy-react/web.md) + * [React 常见反馈](api/hippy-react/feedback.md) * hippy-vue * [介绍](api/hippy-vue/introduction.md) * [核心组件](api/hippy-vue/components.md) @@ -19,12 +20,15 @@ * [路由](api/hippy-vue/router.md) * [转 Web](api/hippy-vue/web.md) * [Vue 3.x](api/hippy-vue/vue3.md) + * [Vue 常见反馈](api/hippy-vue/feedback.md) * 样式 * [布局](api/style/layout.md) * [外观](api/style/appearance.md) * [颜色](api/style/color.md) * [变形](api/style/transform.md) * [更改终端属性](api/style/setNativeProps.md) + * [样式常见反馈](api/style/feedback.md) * [网络请求](api/network-request.md) * [性能监控](api/performance.md) * [定时器](api/timer.md) +* [常见反馈](api/feedback.md) diff --git a/docs/api/feedback.md b/docs/api/feedback.md new file mode 100644 index 00000000000..56591147dab --- /dev/null +++ b/docs/api/feedback.md @@ -0,0 +1,92 @@ +# 常见反馈 + +## 1. Hippy提供了@hippy/react-web和Web Renderer两套方案,哪一套方案转web的能力更完整一些呢?Hippy后续会重点支持哪一个方向? + +两套方案都有一些业务在用, 组件和属性支持可以参考这两个文档 + +https://iwiki.woa.com/p/1478044741 + +https://iwiki.woa.com/p/1410007954 + +Hippy 会继续维护 Web Renderer,基于 Web Render,更容易对接 kbone 等来扩展小程序 + +## 2. Hippy 如何支持小程序 + +可以Hippy 对接 Kbone、taro、uniapp 等框架,腾讯内部业务可参考其他[业务方案](https://doc.weixin.qq.com/doc/w3_ANsAsgZ1ACcxxR1G3KPR0K85XYnmP?scode=AJEAIQdfAAoD1a4bp0ANsAsgZ1ACc): + +## 3. Hippy 的组件库有推荐吗 + +[开源仓库](https://github.com/hippy-contrib) + +[腾讯内部仓库](https://raftx.woa.com/hippy) + +## 4. Hippy 如何做曝光上报 + +目前 Hippy 还没有对外的曝光上报方案,腾讯内部的业务,可以使用大同来做曝光上报 + +### 方案 + +hippy本质上使用的还是客户端原生组件 以及一部分自绘组件。客户端已集成的的大同sdk能够对原生组件、Activity等做检测上报,自然也可以对hippy的组件做检测上报。重点处理上报id绑定到组件的逻辑就可以。 + +### 接入指引 + +[Hippy Android 曝光上报指引](https://iwiki.woa.com/p/956352478) + +[Hippy iOS 曝光上报指引](https://iwiki.woa.com/p/589637144) + +有疑问可以咨询企业微信 TDF小助手 + + +## 5. 启动时, Hippy 如何从终端获取参数 + +React 通过根节点的 props 获取启动参数 + +Vue 通过 Vue.$start 回调获取启动参数 + +## 6. Hippy 页面支持 width: auto 吗 + +不支持的,可以用imageloader加载读图片尺寸,https://hippyjs.org/#/hippy-vue/vue-native?id=imageloader + +## 7. Hippy 背景透明是否支持毛玻璃效果呢 + +腾讯业务可参考[社区组件](https://raftx.woa.com/hippy/detail/578) + +## 8. Hippy 如何实现暗黑模式 + +有两种方案: + +方案一:设置2套css属性,然后切换时整体切换,性能较差 + +方案二:初始化节点时,把两套属性都带下去,然后终端渲染时切换 + +## 9. Hippy 有提供类似 IntersectionObserver 方法吗 + +react 还不支持,vue有封装了一个,可以参考封装下 [hippy-vue-intersection-observer](https://www.npmjs.com/package/hippy-vue-intersection-observer) 不过这个库是基于hippy的Measure API实现的,对bridge通信性能会有一定性能影响,使用时注意评估下 + +## 10. Hippy 有 Clipboard 的复制剪切功能吗 + +剪切板相关的能力应该是在3.2移出的,其他版本可以参考文档:https://github.com/Tencent/Hippy/blob/v2.15.x/docs/hippy-react/modules.md#clipboard + +## 11. 我们现在前端用的是React技术栈,我们想一部分业务用Vue,一部分业务用React,Hippy支持这种用法吗 + +支持的,但是react 和 vue得是不同的hippy引擎实例 + +## 12. Hippy 如何判断横屏 + +进入app后,横屏切换会触发 onSizeChanged 事件 + +进入app前已经横屏,这个可以读 Vue.Native.Dimensions ,获取当前窗口长宽来计算,可以参考这个帖子 https://mk.woa.com/q/293192?ADTAG=search + +## 13. Hippy 图片必须要设定宽高吗?希望宽度和父级view一样,高度自适应怎么写呢 + +可以用 ImageLoaderModule.getSize 这个接口先获取图片大小 https://hippyjs.org/#/hippy-react/modules?id=imageloadermodule + +## 14. 执行报错 startBatch is not a function, 可能是什么原因 + +startBatch是 Hippy2 才有的版本,如果前端使用 Hippy2 终端使用 Hippy3 会不兼容,检查下@hippy/hippy-vue 的版本,确保和终端版本一致 + +## 15. Hippy 是否支持动态加载 + +支持,参考 https://doc.openhippy.com/#/feature/feature2.0/dynamic-import + + diff --git a/docs/api/hippy-react/_sidebar.md b/docs/api/hippy-react/_sidebar.md index e3f5b9ca36c..9d174cd59a0 100644 --- a/docs/api/hippy-react/_sidebar.md +++ b/docs/api/hippy-react/_sidebar.md @@ -8,6 +8,7 @@ * [手势系统](api/hippy-react/gesture.md) * [自定义组件和模块](api/hippy-react/customize.md) * [转 Web](api/hippy-react/web.md) + * [React 常见反馈](api/hippy-react/feedback.md) * hippy-vue * [介绍](api/hippy-vue/introduction.md) * [核心组件](api/hippy-vue/components.md) @@ -19,12 +20,15 @@ * [路由](api/hippy-vue/router.md) * [转 Web](api/hippy-vue/web.md) * [Vue 3.x](api/hippy-vue/vue3.md) + * [Vue 常见反馈](api/hippy-vue/feedback.md) * 样式 * [布局](api/style/layout.md) * [外观](api/style/appearance.md) * [颜色](api/style/color.md) * [变形](api/style/transform.md) * [更改终端属性](api/style/setNativeProps.md) + * [样式常见反馈](api/style/feedback.md) * [网络请求](api/network-request.md) * [性能监控](api/performance.md) * [定时器](api/timer.md) +* [常见反馈](api/feedback.md) diff --git a/docs/api/hippy-react/components.md b/docs/api/hippy-react/components.md index b4ab90baf3e..d625e1e11ed 100644 --- a/docs/api/hippy-react/components.md +++ b/docs/api/hippy-react/components.md @@ -157,6 +157,12 @@ import icon from './qb_icon_new.png'; > >* time: number: 可指定延迟多久后收起 PullHeader,单位ms +### expandPullHeader + +> 最低支持版本 `2.14.0` + +`() => void` 展开顶部下拉刷新条 PullHeader。当设置了`renderPullHeader`后,可以通过该方法来主动触发下拉刷新的效果。 + ### collapsePullFooter > 最低支持版本 `2.14.0` @@ -503,6 +509,7 @@ import icon from './qb_icon_new.png'; | ------------------------ | ------------------------------------------------------------ | -------------------------------------------- | -------- | | bounces | 是否开启回弹效果,默认 `true` | `boolean` | `iOS、Voltron` | | initialPage | 指定一个数字,用于决定初始化后默认显示的页面 index,默认不指定的时候是0 | `number` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | +| offscreenPageLimit | 指定一个数字,用于设置最大挂载到view tree的page item数量,默认为1,如果在间隔较大的item之间切换遇到一些刷新闪动问题,可以尝试设置该属性为最大page item count来解决 | `number` | `Android` | | scrollEnabled | 指定 ViewPager 是否可以滑动,默认为 `true` | `boolean` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | | onPageSelected | 指定一个函数,当 page 被选中时进行回调。回调参数是一个 event 对象,回调参数: `position: number` - 表示即将滑到的目标 page 的索引 | `(obj: {position: number}) => void` | `Android、iOS、hippy-react-web、Web-Renderer、Voltron` | | onPageScroll | 指定一个函数,当 page 被滑动时进行回调。回调参数是一个 event 对象,回调参数 `position: number` - 表示即将滑到的目标 page 的索引,`offset: number` - 当前被选中的 page 的相对位移,取值范围 -1 到 1 | `(obj: {position: number, offset: number}) => void` | `Android、iOS、Web-Renderer、Voltron` | @@ -543,7 +550,8 @@ import icon from './qb_icon_new.png'; | interItemSpacing | item 间的垂直间距 | `number` | `Android、iOS、Voltron` | | contentInset | 内容缩进 ,默认值 `{ top:0, left:0, bottom:0, right:0 }` | `Object` | `Android、iOS、Voltron` | | renderItem | 这里的入参是当前 item 的 index,在这里可以凭借 index 获取到瀑布流一个具体单元格的数据,从而决定如何渲染这个单元格。 | `(index: number) => React.ReactElement` | `Android、iOS、Voltron` | -| renderBanner | 如何渲染 Banner。 | `() => React.ReactElement` | `Android、iOS、Voltron` +| renderBanner | 如何渲染 Banner (即Header,显示在内容顶部) | `() => React.ReactElement` | `Android、iOS、Voltron` | +| renderFooter | 如何渲染 Footer(与renderBanner对应,Footer显示在内容底部) | `() => React.ReactElement` | `iOS`(3.3.2版本起支持) | | getItemStyle | 设置`WaterfallItem`容器的样式。 | `(index: number) => styleObject` | `Android、iOS、Voltron` | | getItemType | 指定一个函数,在其中返回对应条目的类型(返回Number类型的自然数,默认是0),List 将对同类型条目进行复用,所以合理的类型拆分,可以很好地提升list 性能。 | `(index: number) => number` | `Android、iOS、Voltron` | | getItemKey | 指定一个函数,在其中返回对应条目的 Key 值,详见 [React 官文](//reactjs.org/docs/lists-and-keys.html) | `(index: number) => any` | `Android、iOS、Voltron` | diff --git a/docs/api/hippy-react/customize.md b/docs/api/hippy-react/customize.md index ed30972d492..971c9aa40aa 100644 --- a/docs/api/hippy-react/customize.md +++ b/docs/api/hippy-react/customize.md @@ -36,14 +36,20 @@ export class MyView extends React.Component { # 自定义模块 -> 该范例仅可以在 Android 下运行。 - 前端扩展模块分为三步: 1. 第一步导入 callNative 或者 callNativeWithPromise 接口 2. 封装调用接口 3. 导出模块 +注: callNative 代表前端调用客户端接口,无需返回 +callNativeWithPromise 代表前端调用客户端接口,需要返回,返回一个 Promise 对象 + +callNtive/callNativeWithPromise 参数含义: +moduleName: 代表和终端约定的模块名字,为 string 类型 +functionName: 代表和终端约定的模块函数名字,为 string 类型 +params: 实际携带参数, 为 Object 类型 + ```javascript // TestModule.js import { callNative, callNativeWithPromise } from "@hippy/react" diff --git a/docs/api/hippy-react/feedback.md b/docs/api/hippy-react/feedback.md new file mode 100644 index 00000000000..7e18764aa64 --- /dev/null +++ b/docs/api/hippy-react/feedback.md @@ -0,0 +1,18 @@ +# Hippy-React 常见反馈 + +## 1. 如何开始一个hippy react 项目 + +可以先参考我们的文档 和 demo + +https://hippyjs.org/#/hippy-react/introduction + +https://github.com/Tencent/Hippy/tree/master/examples/hippy-react-demo + +## 2. Hippy 是否支持 React 函数式写法 + +目前 Hippy 已适配 React 17,已支持函数式写法,未来会持续 React 18、9 + +## 3. Hippy-React支持class吗,就向下面这样传统react开发一样 + +Hippy-React 不支持 classname, 只能通过内联 Style + diff --git a/docs/api/hippy-react/modules.md b/docs/api/hippy-react/modules.md index 67acc1194d0..5e2ae4e5eb5 100644 --- a/docs/api/hippy-react/modules.md +++ b/docs/api/hippy-react/modules.md @@ -433,6 +433,10 @@ AsyncStorage 是一个简单的、异步的、持久化的 Key-Value 存储系 > - method:方法名称,如 ListView 的 `scrollToIndex` > - options: 需传递的数据,如 ListView 的 `[xIndex, yIndex, animated]`,空时显式写 `[]` +注: 也可以传入 callback 参数,这个是 Hippy 内部 API, 不推荐使用,源码可参考: + +[callUIFunction接口实现源码](https://github.com/Tencent/Hippy/blob/main/driver/js/packages/hippy-react/src/modules/ui-manager-module.ts) + ### UIManagerModule.getElementFromFiberRef 获取元素 Ref 对应的 Element(类似DOM)。`hippy-react-web` 不支持。 diff --git a/docs/api/hippy-vue/_sidebar.md b/docs/api/hippy-vue/_sidebar.md index e3f5b9ca36c..c25dfe608ae 100644 --- a/docs/api/hippy-vue/_sidebar.md +++ b/docs/api/hippy-vue/_sidebar.md @@ -8,6 +8,7 @@ * [手势系统](api/hippy-react/gesture.md) * [自定义组件和模块](api/hippy-react/customize.md) * [转 Web](api/hippy-react/web.md) + * [常见反馈](api/hippy-react/feedback.md) * hippy-vue * [介绍](api/hippy-vue/introduction.md) * [核心组件](api/hippy-vue/components.md) @@ -19,12 +20,14 @@ * [路由](api/hippy-vue/router.md) * [转 Web](api/hippy-vue/web.md) * [Vue 3.x](api/hippy-vue/vue3.md) + * [常见反馈](api/hippy-vue/feedback.md) * 样式 * [布局](api/style/layout.md) * [外观](api/style/appearance.md) * [颜色](api/style/color.md) * [变形](api/style/transform.md) * [更改终端属性](api/style/setNativeProps.md) + * [常见反馈](api/style/feedback.md) * [网络请求](api/network-request.md) * [性能监控](api/performance.md) * [定时器](api/timer.md) diff --git a/docs/api/hippy-vue/customize.md b/docs/api/hippy-vue/customize.md index a8e04624fcb..8ac9c16f575 100644 --- a/docs/api/hippy-vue/customize.md +++ b/docs/api/hippy-vue/customize.md @@ -124,10 +124,16 @@ component: { # 自定义模块 -> 该范例仅可以在 Android 下运行。 - hippy-vue 的模块其实只是一个 `Vue.Native.callNative` 调用,写个 `function` 即可。 +注: callNative 代表前端调用客户端接口,无需返回 +callNativeWithPromise 代表前端调用客户端接口,需要返回,返回一个 Promise 对象 + +callNtive/callNativeWithPromise 参数含义: +moduleName: 代表和终端约定的模块名字,为 string 类型 +functionName: 代表和终端约定的模块函数名字,为 string 类型 +params: 实际携带参数, 为 Object 类型 + ```js import Vue from 'vue'; diff --git a/docs/api/hippy-vue/external-components.md b/docs/api/hippy-vue/external-components.md index 6c6c112713a..1ccade411be 100644 --- a/docs/api/hippy-vue/external-components.md +++ b/docs/api/hippy-vue/external-components.md @@ -280,7 +280,7 @@ export default { | columnSpacing | 瀑布流每列之前的水平间距 | `number` | `Android、iOS、Voltron` | | interItemSpacing | item 间的垂直间距 | `number` | `Android、iOS、Voltron` | | contentInset | 内容缩进 ,默认值 `{ top:0, left:0, bottom:0, right:0 }` | `Object` | `Android、iOS、Voltron` | -| containBannerView | 是否包含`bannerView`,只能有一个bannerView,`Android` 暂不支持 | `boolean` | `iOS、Voltron` | +| containBannerView | 是否包含`bannerView`,只能有一个bannerView, (`Android` 暂不支持,`iOS` 3.3.2版本起已废弃该属性,请使用`waterfall-item`组件`isHeader/isFooter`属性代替) | `boolean` | `iOS、Voltron` | | containPullHeader | 是否包含`pull-header`;`Android` 暂不支持,可以用 `ul-refresh` 组件替代 | `boolean` | `iOS、Voltron` | | containPullFooter | 是否包含 `pull-footer` | `boolean` | `Android、iOS、Voltron` | | numberOfColumns | 瀑布流列数量,Default: 2 | `number` | `Android、iOS、Voltron` | @@ -323,3 +323,5 @@ export default { | --------------------- | ------------------------------------------------------------ | ----------------------------------------------------------- | -------- | | type | 指定一个函数,在其中返回对应条目的类型(返回Number类型的自然数,默认是0),List 将对同类型条目进行复用,所以合理的类型拆分,可以很好地提升 List 性能。 | `number` | `Android、iOS、Voltron` | | key | 指定一个函数,在其中返回对应条目的 Key 值,详见 [Vue 官网](//vuejs.org/v2/guide/list.html) | `string` | `Android、iOS、Voltron` | +| isHeader | 指定该Item是否为Header(即bannerView,显示在内容区顶部) | `boolean` | `iOS`(3.3.2版本起支持) | +| isFooter | 指定该Item是否为Footer(显示在内容区底部) | `boolean` | `iOS`(3.3.2版本起支持) | diff --git a/docs/api/hippy-vue/feedback.md b/docs/api/hippy-vue/feedback.md new file mode 100644 index 00000000000..3cf187d0600 --- /dev/null +++ b/docs/api/hippy-vue/feedback.md @@ -0,0 +1,28 @@ +# Hippy-Vue 常见反馈 + +## 1. 如何开始一个hippy vue 项目 + +可以先参考我们的文档 和 demo + +https://hippyjs.org/#/hippy-vue/introduction +https://github.com/Tencent/Hippy/tree/master/examples/hippy-vue-demo + +## 2. Hippy Vue中span的换行符会被 trim + +3.x hippy-vue的版本,Hippy默认开启了 Vue.config.trimWhitespace 这个参数。而 hippy-vue 2.x的版本是不开的, 这个也是为了和未来 vue3 版本的规划对齐 +https://github.com/vuejs/core/pull/1600 + +方案建议: +a. 在 hippy.js 文件加一句 Vue.config.trimWhitespace = false,这样配置就和安卓版本完全对齐了。这个参数会对产物有一些影响,也可以让你们前端同事再重新评估一下。 + +b. 因为现在 hippy 没有提供换行组件 br标签 或者 white-space 的 css,如果需要换行,则不适用span,而是重新创建一个 text文本组件 + +## 3. hippy-vue-next-style-parser,这个包的作用 + +这个包用于处理 vue-next 的 css parse 和 match 逻辑 + +## 4. Hippy 是否支持 Vite 构建 + +已支持,目前只有腾讯内部版,腾讯业务可联系 TDF小助手 + + diff --git a/docs/api/hippy-vue/vue-native.md b/docs/api/hippy-vue/vue-native.md index da43ca625b4..d5bca3599d3 100644 --- a/docs/api/hippy-vue/vue-native.md +++ b/docs/api/hippy-vue/vue-native.md @@ -194,6 +194,10 @@ Vue.Native.AsyncStorage.getItem('itemKey'); > * method:方法名称,如 ListView 的 `scrollToIndex` > * options: 需传递的数据,如 ListView 的 `[xIndex, yIndex, animated]` +注: 也可以传入 callback 参数,这个是 Hippy 内部 API, 不推荐使用,源码可参考: + +[callUIFunction接口实现源码](https://github.com/Tencent/Hippy/blob/main/driver/js/packages/hippy-vue/src/runtime/native.ts) + --- # Clipboard diff --git a/docs/api/hippy-vue/web.md b/docs/api/hippy-vue/web.md index 046700833aa..8f58ee928f6 100644 --- a/docs/api/hippy-vue/web.md +++ b/docs/api/hippy-vue/web.md @@ -2,4 +2,4 @@ ## WebRenderer 方案 -Hippy 全新 [`WebRenderer`](web/integration.md) 方案,增加基于公共通信协议的转换层,业务开发者可以使用同一套 Hippy 语法开发的业务代码,映射成 JS 实现的组件和模块,上层无论使用 React,Vue 或者其他第三方框架,都可以实现兼容,可参考。 +Hippy 全新 [`WebRenderer`](development/web-integration-guidelines.md) 方案,增加基于公共通信协议的转换层,业务开发者可以使用同一套 Hippy 语法开发的业务代码,映射成 JS 实现的组件和模块,上层无论使用 React,Vue 或者其他第三方框架,都可以实现兼容,可参考。 diff --git a/docs/api/performance.md b/docs/api/performance.md index 4a8c51a744a..60b72a92873 100755 --- a/docs/api/performance.md +++ b/docs/api/performance.md @@ -1,22 +1,759 @@ -# 性能监控 +# **Hippy 业务冷启动性能优化指引** -提供全局 `performance` 对象,用于获取性能数据。 +## **背景** ---- +经常有业务咨询 Hippy 有哪些优化手段,以及业务性能问题应该怎么分析,有哪些监控工具,本篇文档则系统的介绍了 Hippy 冷启动的性能优化的最佳实践。 -# memory -`performance.memory` 返回 js 引擎中内存的统计数据(仅 Android 支持,iOS 将返回 `undefined` )。 -> 最低支持版本 `2.15.0` -```javascript +## **冷启动阶段:** -global.performance.memory = undefined || { - jsHeapSizeLimit: 4096, // 堆内存大小限制 - totalJSHeapSize: 2048, // 可使用的堆内存 - usedJSHeapSize: 1024, // 已使用的堆内存 - jsNumberOfNativeContexts: 1, // 当前活动的顶层上下文的数量(随着时间的推移,此数字的增加表示内存泄漏) - jsNumberOfDetachedContexts: 0, // 已分离但尚未回收垃圾的上下文数(该数字不为零表示潜在的内存泄漏) +介绍一下Hippy应用冷启动有哪些阶段 + +​ 1. Hippy 运行环境启动 - 终端耗时 (启动) + +​ 2. 加载 Bundle的 JS 文件 - JS 引擎耗时 (自执行) + +​ 3. 应用实例的执行 - JS 引擎耗时(JS运行) + +​ a. 业务代码逻辑执行 + +​ b. 业务 IO(网络、JS API)等待 + +​ c. React/Vue 框架 DIFF 耗时 + +​ d. Hippy JS SDK计算耗时 + +​ 4. 上屏显示 - 终端耗时 (渲染) + +​ a. Hippy DOM 构建、排版耗时 + +​ b. Hippy 渲染上屏耗时 + +详细介绍参考:[Hippy冷启动核心逻辑和数据梳理](https://doc.weixin.qq.com/doc/w3_AJcAmQbdAFwmCbIe8OPScGUN6amkw?scode=AJEAIQdfAAo1QKXNGAAJcAmQbdAFw) + +我们把启动阶段分为 4个阶段:Hippy 启动、Bundle加载、**应用实例执行**、渲染上屏。 + +不同规模的 Hippy 业务,这些阶段耗时不尽相同,大家可以先测试各阶段耗时占比,再针对性的来分析。 + + + +Hippy 团队过去有一些经验可供参考: + +​ 1. 一般耗时大头是阶段 3 (一半耗时占比超过 50%) + +业务可重点排查 **业务 JS 执行耗时**、**IO(网络、JS API)等待耗时。**此外,**节点数过多、CSS 属性比较复杂**,也会影响 Hippy SDK的计算耗时 + +​ 2. JS Bundle包过大,会影响阶段 2 的耗时 + +​ 3. 节点数过多,节点层级比较深,渲染样式比较复杂,会影响阶段 3 的耗时 + + + +## **如何监控定位:** + +### **1.** **线上监控:** + +​在 Hippy 3.0,我们设计了用于性能分析的 Performance API: + +[Hippy 性能监控设计文档](https://doc.weixin.qq.com/doc/w3_ANsAsgZ1ACcBlbHY905RpW7Qj1vij?scode=AJEAIQdfAAosjxQAlNANsAsgZ1ACc) + + + +Performance API 冷启动打点指标: + +| **指标** | **对应 Key** | +| -------------------- | ------------------------ | +| Hippy 引擎加载开始 | hippyNativeInitStart | +| JS 引擎加载开始 | hippyJsEngineInitStart | +| JS 引擎加载结束 | hippyJsEngineInitEnd | +| Hippy 引擎加载结束 | hippyNativeInitEnd | +| JS Bundle 自执行耗时 | bundleInfo[] | +| 业务入口执行开始 | hippyRunApplicationStart | +| 业务入口执行结束 | hippyRunApplicationEnd | +| 首帧绘制开始 | hippyFirstFrameStart | +| 首帧绘制结束 | hippyFirstFrameEnd | +| 启动耗时 | duration | + + + +监控方式: + +​ 1. Hippy 3.x版本:可以接入最新 aegis-sdk (咨询 TDF小助手),已经基于 performance api,可以直接获取打点数据 + + + +​ 2. Hippy 2.x 版本可以基于业务代码打点,获取启动阶段的近似耗时数据。可参考:https://km.woa.com/articles/show/557810?kmref=search&from_page=1&no=1 + + + +### **2.** **Profile 工具:** + +​ 1. Perfdog + +​ a. 可查看 FPS, CPU, GPU, 内存等变化情况 + +​ 2. 安卓系统自带工具: + +​ a. GPU 渲染模式分析: 通过在 Android 设备的设置 APP 的开发者选项里打开 “ GPU 渲染模式分析” 选项,选择 ” 在屏幕上显示为条形图 “ 。 + +​ b. 过度绘制:Android 设备的设置 APP 的开发者选项里打开 “ 调试 GPU 过度绘制 ” + +​ c. 查看界面边界:系统设置 – 开发者选项 – 绘图 --显示布局边界 + +​ 3. iOS系统 simulator 模拟器 + +​ a. Debug 下自带分析工具 Color Blended Layers 图层绘制 Color Off-screen Rendered 离屏渲染 + +​ 4. Profile工具 + +​ a. Android Studio Profiler + +​ b. Xcode Instruments + +​ **c.** **Hippy Devtools (也在进一步完善中,欢迎大家提需求),如下示例:** + + + +**如何优化:** + +定位到耗时点,接下来就可以针对性分析。 + +先简单介绍常用的优化: + +​ 1. 预加载:大幅降低 1-2 阶段耗时 + +​ 2. 预渲染/SSR/Native-Vue:大幅降低 1-3阶段耗时 + +​ 3. 数据预拉取:降低 3阶段 IO 等待耗时 + +​ 4. JSI、动态加载、节点数优化:降低 3 阶段耗时 + +​ 5. 节点缓存/渲染优化/图片缓存:降低 4 阶段耗时 + +​ 6. 骨架屏优化/Loading:优化交互体验 + +... + + + +接下来详细介绍这些优化点: + + + +### **1.** **预加载** + +通过提前执行1+2阶段逻辑优化首屏耗时。 + +这两个阶段耗时主要被封装在HippyBridge中,因此可以通过提前初始化HippyBridge达成目的。 + + + +iOS 代码示例: + +``` javascript +HippyBridge *bridge = [[HippyBridge alloc] initWithDelegate:nil bundleURL:url moduleProvider:^NSArray> * { + return nil; // 或返回希望替换的module实例 +} launchOptions:option executorKey:nil]; + +// 将bridge保存起来 +``` + +安卓代码示例: + +``` javascript +mHippyEngine.initEngine(new HippyEngine.EngineListener() { + @Override + public void onInitialized(EngineInitStatus statusCode, String msg) { + if (statusCode == EngineInitStatus.STATUS_OK) { + HippyBundleLoader loader = ...; + // e.g.: + // new HippyAssetBundleLoader(context, assetName, true, "demo"); + // new HippyFileBundleLoader(filePath, true, "demo"); + mHippyEngine.preloadModule(loader); + } + } } +``` + + +优点: + +​ 1. 不依赖页面数据,在任何页面都可考虑使用预加载 + +​ 2. 前端也可以在预加载时提前fetch数据 + +缺点: + +​ 1. 优化效果相对预渲染不够明显 + + + +### **2.** **预渲染** + +如果还希望在预加载基础上继续优化体验,还可以进一步提前执行步骤 3 的逻辑。 + +优点: + +​ 1. 优化效果十分明显 + +缺点: + +​ 1. 内存开销大 + +​ 2. 无法对动态数据的页面使用 + +​ 3. 页面发版会导致命中率降低 + +​ 4. 预渲染时的Dimension可能与访问时不一致,导致布局异常 +​ 5. 在Android上预渲染只能使用AppContext,导致页面打开后也无法使用依赖Activity的能力,例如Modal(Dialog)组件 + + + +接入代码示例: + +iOS 代码示例 + + + +``` javascript +HippyRootView *hippyRootView = [[HippyRootView alloc] + initWithBridge:bridge + businessURL:businessURL + moduleName:appName + initialProperties:properties + delegate:delegate]; +// 将hippyRootView保存起来 ``` + + + + + +执行这一步后,HippyRootView内部将会执行实例的加载,但由于rootView未被真正挂在到VC的UI树上,所以暂时不可见。 + +当需要显示的时候可以在VC的view上将hippyRootView加为子view。 + + + +``` javascript +[parent addSubview:hippyRootView]; + +// 必要时可以更新一下frame +hippyRootView.frame = newFrame; + +// 如果在addSubview后还有其他的subview的添加,可以考虑通过bringSubviewToFront将hippyRootView改为最前方显示 +[parent bringSubviewToFront:hippyRootView]; +``` + + + + + + + +安卓代码示例: + +除了需要把 ModuleLoadParams.context 设置为 AppContext 外,其余步骤和常规加载一致。 + + + +``` javascript +HippyEngine.ModuleLoadParams loadParams = new HippyEngine.ModuleLoadParams(); +loadParams.context = getApplicationContext(); +... +mHippyView = mHippyEngine.loadModule(loadParams, listener, null); +``` + + + + + +### **3.** **SSR** + +SSR 代替预渲染方案的另一选择。 + +方案原理:把 1-3 阶段放在服务端执行,解析成最终的 Hippy指令 的字符串,启动时直接反序列化 Hippy指令,下发Hippy终端执行渲染。 + +优化效果:性能数据与预渲染接近 + +详见QQ游戏中心优化 https://km.woa.com/articles/show/564026?kmref=search&from_page=1&no=1 + + + +优点: + +​ 1. 可不依赖终端方案方案,前端执行 + +​ 2. 相比预渲染方案更节省内存 + +缺点: + +​ 1. 需要执行事件挂载逻辑,可交互时间会延迟 + +​ 2. 会带来服务器的运营成本 + + +### **4.** **数据预请求:** + +在不方便应用 预渲染/SSR/Native-Vue的场景。可以考虑提前下载数据,节省 3阶段 网络 IO 耗时: + +​ 1. 可以在终端预请求,在启动Hippy后,把数据传给前端 + +​ 2. 可以结合预加载,在前端 JS 自执行阶段,前端预请求数据。等待组件渲染时直接使用。 + + + +### **5.** **节点缓存** + +​ 1. Dom Node 缓存(安卓) + +接入方案详见:https://doc.openhippy.com/#/feature/feature2.0/dom-cache + +​ 2. RenderNode 缓存(3.0 安卓) + +接入方案详见:https://doc.openhippy.com/#/feature/feature3.0/render-node-snapshot + + + + + +### **6.** **JS 引擎优化** + +在不方便做 JS 预加载的场景,如果要优化 JS 自执行耗时,可以考虑对 JS 引擎本身做优化: + +#### **6.1.** **V8 编译开启 Code Cache** + +把编译和解析的结果缓存下来,等到下次遇到相同的文件,直接跳过这个过程,把直接缓存好的数据拿来使用; + +​Hippy 2.0: Hermes 接入指引文档:https://iwiki.woa.com/p/4007348225 + +​Hippy 3.0 Hermes会直接集成,文档待补充。 + + + + + +### **7.** **bundle 包动态加载** + +接入文档指引:https://iwiki.woa.com/p/491739348 + +Hippy 打包插件只支持一个jsbundle包的生成。随着业务逻辑越来越复杂,jsbundle越来越大、包的加载时间越来越长。为了解决这个问题,Hippy 在 2.2.0 版本增添了动态加载能力,Hippy 的开发人员可以按照业务需求来动态引入 JS。 + +Hippy 在 2.5.5 版本增加了远程网络加载模式的支持,业务可对每个bundle自定义不同的加载模式。 + + + +webpack打包脚本中引入插件:HippyDynamicImportPlugin + + + +``` javascript +const HippyDynamicImportPlugin = require('@hippy/hippy-dynamic-import-plugin'); + +module.exports = { + ... + plugins: [ + new HippyDynamicImportPlugin(), + ], +}; +``` + + + + + + + +​ (1)加载本地 JS 包: + +​ 与 Web 开发动态加载能力一样,直接使用 import() 语法即可 + + + +​ (2)加载远程 JS 包: + + i. webpack打包脚本配置全局 publicPath(可选) + + + +``` javascript + // webpack output 配置 + output: { + ... + publicPath: 'https://xxxx/hippy/hippyVueDemo/', +}, +``` + + + + + +ii. 在业务代码引用分包的入口配置 magic comment的 webpackChunkName(必须) 和 customChunkPath(可选),如果没有配置customChunkPath,会默认使用全局 publicPath; 以 Hippy-Vue 为例: + + + +``` javascript + // Hippy-Vue 配置 + AsyncComponentFromHttp: () => import(/* customChunkPath: "https://xxx/hippy/hippyVueDemo/", webpackChunkName: "asyncComponentFromHttp" */'./dynamicImport/async-component-http.vue') + .then(res => res) + .catch(err => console.error('import async remote component error', err)) +``` + + + + + + + + + +### **8.** **JS API 优化** + +#### **8.1.** **JS Bridge -> JSI** + +​接入方案详见:https://doc.openhippy.com/#/feature/feature2.0/jsi + +​优化效果: + +Android 代码示例: + + + +``` javascript +// 初始化引擎开启 enableTurbo +val initParams = HippyEngine.EngineInitParams() +initParams.enableTurbo = true + +// 定义 module +@HippyNativeModule(name = "demoTurbo") +class DemoTurboModule(context: HippyEngineContext?) : HippyNativeModuleBase(context) { + ... + @HippyMethod(isSync = true) + fun getNum(num: Double): Double = num + ... +} +``` + + + + + +​iOS 代码示例: + + + +``` javascript +// 方式一:bridge初始化时通过配置参数设置生效 +NSDictionary *launchOptions = @{@"EnableTurbo": @(DEMO_ENABLE_TURBO)}; +HippyBridge *bridge = [[HippyBridge alloc] initWithDelegate:self + bundleURL:[NSURL fileURLWithPath:commonBundlePath] + moduleProvider:nil + launchOptions:launchOptions + executorKey:@"demoTurbo"]; + +// 方式二:bridge初始化完成后,设置属性生效 +HippyRootView *rootView = [[HippyRootView alloc] initWithBridge:nil + businessURL:nil + moduleName:@"demoTurbo" + initialProperties:@{@"isSimulator": @(isSimulator)} + launchOptions:nil + shareOptions:nil + debugMode:YES + delegate:nil]; +rootView.bridge.enableTurbo = YES; +``` + + + + + + + +``` javascript +@implementation TurboConfig + +... + +// 注册模块 +HIPPY_EXPORT_TURBO_MODULE(TurboConfig) + +// 注册交互函数 +HIPPY_EXPORT_TURBO_METHOD(getInfo) { + return self.strInfo; +} +HIPPY_EXPORT_TURBO_METHOD(setInfo:(NSString *)string) { + self.strInfo = string; + return @(YES); +} + +... + +@end +``` + + + + + + + +Hippy-React 代码示例: + +https://github.com/Tencent/Hippy/blob/master/examples/hippy-react-demo/src/externals/Turbo/demoTurbo.js + +Hippy-Vue 代码示例: + +https://github.com/Tencent/Hippy/blob/master/examples/hippy-vue-demo/src/components/demos/demoTurbo.js + + + +#### **8.2.** **减少首屏 JS API 调用次数** + + + +### **9.** **Hippy-Vue 优化** + +​ 1. 减少 getElemCss 方法的调用 + + + +``` javascript +Vue.Native.getElemCss() +``` + + + + + +​ 2. 减少 scoped 的使用 + +vue scoped 能力是 2.15.0 版本引入,scoped会转换为 属性选择器,因此解析性能会比较差; + +在 Hippy 3.0 版本,我们使用 css-module的方案优化了 scoped的解析方式,性能得到提升。 + +​ 3. 减少 CSS 属性选择器、伪类等样式的使用 + +​ 4. 升级 Hippy-Vue-Next(Vue3) + +Vue3 升级指引:https://hippyjs.org/#/hippy-vue/vue3 + +Vue3 相比 Vue2 做了 Diff算法优化、静态提升、事件缓存等优化,从Vue框架层面会带来性能提升。实测了 Vue3 和 Vue2 在Hippy框架下的数据对比: + + + +### **10. Hippy-React 优化** + +​ 1. Hippy React节点更新优化: + +https://iwiki.woa.com/p/1335183393 + +@hippy/react 在 2.12.0 版本应用上了最新的渲染优化,针对 react 16 和 react 17,先删除原来的 react-reconciler 包依赖,再分别引入 + + + +``` javascript +"@hippy/react-reconciler": "react16" +"@hippy/react-reconciler": "react17" +``` + + + + + +​ + +### **11.** **渲染优化:** + +#### 1. 减少首屏节点数、节点层级 + +- 可以借助客户端 IDE 来直观地查看 "DOM" 树,使用前端的 DevTools 的 View 树来检查冗余节点。 + +#### 2. 减少节点重复渲染的次数 + +- 可以在高频渲染节点的 `render` 函数中打点统计。 +- 使用 `shouldComponentUpdate`、`PureComponent` 等优化重复渲染。 + +#### 3. 节点优化:节点合并,扁平化(触发 Hippy 引擎优化) + +- 减少冗余事件挂载。 + +#### 4. 圆角、裁剪、描边等渲染性能较差,尽量减少使用 + +#### 5. 减少过度绘制 + +- 移除重复的背景色。 +- 减少图层覆盖。 + +#### 6. ListView + +- 尽量使用 `ListView` 代替 `ScrollView`,`ListView` 通过 View 的缓存与重用大大提升渲染性能。 +- 使用 `getRowType`,将 item 按类型合理拆分,同一类型的 item 的 `RowType` 相同,可复用。 +- 严格保持 `ListView` item 的 DOM 结构一致: + - **hippy-vue**:item 中的节点可以用 `v-show`,不要用 `v-if`。 + - **hippy-react**:item 中的节点尽量不要用 `if` 条件来改变节点结构。 + +#### 7. ViewPager + +- `ViewPager` 的子节点尽量不要全加载,实现懒加载。 + +#### 8. Image + +- 图片压缩。 +- 图片按需加载。 +- 避免同时使用 `src` 和 `background`。 +- 大图放 CDN。 +- 减少 base64 图片使用。 +- 图片缓存(内存缓存、磁盘缓存)。 + +​ 代码示例:在自定义ImageLoader中实现自己的缓存逻辑,也可使用第三方图片加载库,如 slide 等。 + + + +``` javascript +HippyEngine.EngineInitParams initParams = new HippyEngine.EngineInitParams(); +// 必须:宿主(Hippy的使用者)的Context +// 若存在多个Activity加载多个业务jsbundle的情况,则这里初始化引擎时建议使用Application的Context +initParams.context = this; +... +// 必须:图片加载器 +initParams.imageLoader = new MyImageLoader(this.getApplicationContext()); +``` + + + + + + + +### **12.** **业务逻辑执行优化** + +此外,也要检查业务代码自身的耗时,并做针对性优化。 + +​ 1. 防抖节流:对滚动事件、数据上报等进行防抖节流,避免频繁触发计算 + +​ 2. 耗时逻辑检查是否可以放在首屏后执行 + + + +### **13.** **交互优化** + +除了上面的优化手段,交互优化在一些场景也是十分重要的,可以提升打开体验,让页面视觉上更流畅。 + +​ 1. 骨架屏优化 + +​ 2. Loading + +​ 3. 切换动画 + + + +## **总结:** + +优化总结、是否需要 终端/前端 同学支持。 + +| 优化方案 | 方案介绍 | 接入成本 | 优化效果 | 终端/前端工作 | +| -------------------- | ------------------------------ | -------- | ---------- | -------------- | +| 预加载 | 优化 JS Bundle包加载 | 中 | 中 | 终端工作 | +| 预渲染 | 基于预加载,进一步优化渲染耗时 | 中 | 好 | 终端工作 | +| SSR | 预渲染的替代方案 | 中 | 好 | 前端工作 | +| Native-Vue | 预渲染的另一种替代方案 | 高 | 好 | 前端工作 | +| 数据预请求 | 节省 IO 耗时 | 低 | 中 | 终端或前端工作 | +| 节点缓存 | 仅安卓支持,优化渲染耗时明显 | 低 | 中 | 终端工作 | +| JS引擎优化(Hermes) | 性能比JSC优化明显 | 高 | 好 | 终端工作 | +| Bundle动态加载 | 分包加载,节省 JS 执行耗时 | 中 | 中 | 前端工作 | +| JS API优化(JSI) | JSI 接口同步调用,等待耗时降低 | 低 | 中 | 前端工作 | +| Hippy-Vue 逻辑优化 | 优化 css 解析耗时 | 低 | 好 | 前端工作 | +| Hippy-React 逻辑优化 | 渲染队列优化 | 低 | 中 | 前端工作 | +| 渲染优化 | 组件使用的渲染耗时优化 | 低 | 中 | 前端工作 | +| 业务逻辑优化 | 业务自身逻辑优化 | 无 | 中 | 前端工作 | +| 交互优化 | 业务自身优化交互 | 无 | 体验上优化 | 前端工作 | + + + +## **参考资料:** + +​ 1. K歌 Hippy性能优化分享(一) + +https://km.woa.com/articles/show/511822?kmref=search&from_page=1&no=7 + +​ 2. k歌 Hippy性能优化分享(二) + +https://km.woa.com/articles/show/484172?kmref=search&from_page=1&no=5 + +​ 3. k歌 Hippy性能优化分享 (三) + +https://km.woa.com/articles/show/504866?kmref=search&from_page=1&no=3 + +​ 4. k歌渲染优化 + +https://km.woa.com/articles/show/465625?kmref=search&from_page=1&no=2 + +​ 5. 小说 Hippy性能优化 + +https://km.woa.com/articles/show/484172?kmref=search&from_page=1&no=5 + +​ 6. 浏览器小说阅读器内存优化 + +https://km.woa.com/articles/show/468088?kmref=search&from_page=1&no=6 + +​ 7. QQ 增值Hippy性能优化 + +https://km.woa.com/articles/show/516658?kmref=search&from_page=1&no=4 + +​ 8. QQ直播Hippy性能优化 + +https://km.woa.com/articles/show/557810?kmref=search&from_page=1&no=1 + +​ 9. QQ游戏中心Hippy SSR优化 + +https://km.woa.com/articles/show/564026?kmref=search&from_page=1&no=1 + +​ 10. QQ 音乐Hippy性能监控工具建设 + +https://km.woa.com/articles/show/494153?kmref=search&from_page=1&no=6 + +​ 11. 浏览器 Hermes 引擎优化性能 + +https://km.woa.com/articles/show/567715?kmref=search&from_page=1&no=4 + +​ 12. QB长视频业务优化-接入Hermes引擎 + +https://km.woa.com/articles/show/564575?kmref=search&from_page=1&no=7 + +​ 13. Hippy 内存、CPU性能优化 + +https://km.woa.com/articles/show/484158?kmref=search&from_page=1&no=8 + +​ 14. 闪现社区首屏优化 + +https://km.woa.com/articles/show/474636?kmref=search&from_page=1&no=9 + +​ 15. NativeVue:解决Hippy等类RN框架首屏白屏的极致方案 + +https://km.woa.com/articles/show/527262?kmref=search&from_page=1&no=10 + +​ 16. Hippy profile工具实现的思考 + +https://km.woa.com/articles/show/397231?kmref=search&from_page=1&no=6 + +https://km.woa.com/articles/show/445938 + +​ 17. 记一次Hippy-React优化 + +https://km.woa.com/articles/show/431695?kmref=search&from_page=1&no=9 + +​ 18. Flutter性能优化最佳实践 + +[Flutter 应用性能优化最佳实践 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter](https://flutter.cn/docs/perf/best-practices) + +​ 19. React Native 性能综述 + +[性能综述 · React Native 中文网](https://reactnative.cn/docs/performance) + +​ 20. 营地性能优化 + +https://km.woa.com/articles/show/599930?kmref=search&from_page=1&no=10 + + diff --git a/docs/api/style/_sidebar.md b/docs/api/style/_sidebar.md index 31e08a29e76..dda9aa7c24b 100644 --- a/docs/api/style/_sidebar.md +++ b/docs/api/style/_sidebar.md @@ -9,6 +9,7 @@ * [手势系统](api/hippy-react/gesture.md) * [自定义组件和模块](api/hippy-react/customize.md) * [转 Web](api/hippy-react/web.md) + * [常见反馈](api/hippy-react/feedback.md) * hippy-vue * [介绍](api/hippy-vue/introduction.md) * [核心组件](api/hippy-vue/components.md) @@ -20,12 +21,14 @@ * [路由](api/hippy-vue/router.md) * [转 Web](api/hippy-vue/web.md) * [Vue 3.x](api/hippy-vue/vue3.md) + * [常见反馈](api/hippy-vue/feedback.md) * 样式 * [布局](api/style/layout.md) * [外观](api/style/appearance.md) * [颜色](api/style/color.md) * [变形](api/style/transform.md) * [更改终端属性](api/style/setNativeProps.md) + * [常见反馈](api/style/feedback.md) * [网络请求](api/network-request.md) * [性能监控](api/performance.md) * [定时器](api/timer.md) diff --git a/docs/api/style/feedback.md b/docs/api/style/feedback.md new file mode 100644 index 00000000000..e8b37e03db5 --- /dev/null +++ b/docs/api/style/feedback.md @@ -0,0 +1,22 @@ +# CSS 样式常见反馈 + +## 1. Hippy 设置百分比失效 + +Hippy 自研了排版引擎 [Taitank](https://github.com/Tencent/taitank), 为了追求极致的性能, 阉割了百分比的实现。 +常见场景实现方案参考: + +> 需要设置高度 100% + +可设置 flex: 1 + +> 需要设置宽度 50% + +可通过 Dimensions.screen 接口获取屏幕尺寸大小,然后计算相应百分比值 + +> 需要百分比实现 + +可以终端修改编译设置,把排版引擎切换为 [Yoga](https://doc.openhippy.com/#/feature/feature3.0/layout) + +## 2. hippy支持 css var 的写法吗 + +可以先用 postcss 相关的插件来支持 diff --git a/docs/development/_sidebar.md b/docs/development/_sidebar.md index efbff78397d..5f1fef08ef3 100644 --- a/docs/development/_sidebar.md +++ b/docs/development/_sidebar.md @@ -15,8 +15,9 @@ - [事件](development/native-event.md) - [终端能力适配](development/native-adapter.md) - [数据类型映射](development/type-mapping.md) -- [V8 API](development/v8-api.md) +- [V8初始化参数与API](development/v8-api.md) - [调试](development/debug.md) +- [曝光上报](development/report.md) - [技术支持](development/support.md) - [隐私政策](development/privacy.md) - [开发者合规指南](development/privacy-developer-guide.md) diff --git a/docs/development/android-3.0-integration-guidelines.md b/docs/development/android-3.0-integration-guidelines.md index 1b39ea3445b..7a8f1140540 100644 --- a/docs/development/android-3.0-integration-guidelines.md +++ b/docs/development/android-3.0-integration-guidelines.md @@ -29,7 +29,7 @@ 下面引用Hippy最新版本号可在上述链接中查询 ```java - implementation 'com.tencent.hippy:release:3.3.0' + implementation 'com.tencent.hippy:release:3.3.2' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.viewpager:viewpager:1.0.0' diff --git a/docs/development/android-3.0-upgrade-guidelines.md b/docs/development/android-3.0-upgrade-guidelines.md index 31b7d8ce0d0..829fde9e8e0 100644 --- a/docs/development/android-3.0-upgrade-guidelines.md +++ b/docs/development/android-3.0-upgrade-guidelines.md @@ -54,6 +54,9 @@ } ``` + +7. module注解HippyNativeModule中取消了线程属性的自定义
+ 3.0中非JSI module调用将统一走hippy-module-Thread线程调用module对应接口,开发者可以根据自己的需要自行切换线程。
diff --git a/docs/development/native-adapter.md b/docs/development/native-adapter.md index 5e3a83d294f..494e032ea0c 100644 --- a/docs/development/native-adapter.md +++ b/docs/development/native-adapter.md @@ -21,6 +21,7 @@ Hippy SDK 现在所提供的 Adapter 包括: - `HippyStorageAdapter`:数据库(KEY-VALUE)Adapter。 - `HippyExecutorSupplierAdapter`:线程池 Adapter。 - `HippyEngineMonitorAdapter`:Hippy 引擎状态监控 Adapter。 +- `HippyFontScaleAdapter`:自定义字体 Adapter。 ## HippyHttpAdapter @@ -55,6 +56,25 @@ Hippy SDK 提供默认空实现 `DefaultEngineMonitorAdapter`。当你需要查 用于支持开发者有自定义格式图片的解码需求,需要开发者自行提供接口类实例。 +## HippyFontScaleAdapter + +Hippy SDK 提供默认的实现 `DefaultFontScaleAdapter`,默认实现中未对字体做任何定制处理,如果需要加载自定义字体或者改变文字显示大小需要实现自定义font adapter并实现以下接口。 + +``` java +// 返回字体的缩放系数,开发者可以返回自定义缩放系数,来动态改变字体的显示大小 +float getFontScale(); + +// 通过该方法可以替换text中自定义表情字符 +CharSequence getEmoticonText(CharSequence text, int fontSize) + +// 根据font family和style返回自定义字体的文件路径 +String getCustomFontFilePath(String fontFamily, int style); + +// 根据font family和style返回自定义字体的Typaeface对象 +Typeface getCustomTypeface(String fontFamily, int style); + +``` +
diff --git a/docs/development/native-component.md b/docs/development/native-component.md index 2bbf6c68c64..3d242874b6c 100644 --- a/docs/development/native-component.md +++ b/docs/development/native-component.md @@ -632,14 +632,14 @@ Widget build(BuildContext context) { 扩展组件主要包括: -1. 扩展 `HippyView` +1. 扩展 `HippyWebView` 2. 实现构造方法 3. 实现设置自定义组件的 `tagName` 4. 实现构造自定义组件的 `dom` 5. 实现自定义组件的 `API 能力` 6. 实现自定义组件的属性 -其中 `HippyView` 类,实现了一些 HippyBaseView 的接口和属性定义,在一个自定义组件中有几个比较重要的属性: +其中 `HippyWebView` 类,实现了一些 HippyBaseView 的接口和属性定义,在一个自定义组件中有几个比较重要的属性: - id: 每一个实例化 component 的唯一标识,默认会赋值给组件 dom 的 id 属性 - pId: 每一个实例化 component 的父组件标识 @@ -647,6 +647,16 @@ Widget build(BuildContext context) { - dom: 真正挂载到document上的节点 - props: 承载了从业务侧传递过来的属性和style的记录 +注: tagName 用来和 React/Vue 注册的自定义组件的 nativeName 关联,可参考: + +### 如果您使用的是`hippy-vue` + +可以参考 [hippy-vue/customize](api/hippy-vue/customize) + +### 如果您是用的是`hippy-react` + +可以参考 [hippy-react/customize](api/hippy-react/customize) + ### 例子 下面这个例子中,我们创建了 `CustomView` 的自定义组件,用来显示一个视频 @@ -655,10 +665,10 @@ Widget build(BuildContext context) { ```javascript -import { HippyView, HippyWebEngine, HippyWebModule } from '@hippy/web-renderer'; +import { HippyWebView, HippyWebEngine, HippyWebModule } from '@hippy/web-renderer'; -// 继承自 `HippyView` -class CustomView extends HippyView { +// 继承自 `HippyWebView` +class CustomView extends HippyWebView { // 实现构造方法 constructor(context, id, pId) { super(context, id, pId); @@ -682,9 +692,9 @@ class CustomView extends HippyView { ```javascript -import { HippyView, HippyWebEngine, HippyWebModule } from '@hippy/web-renderer'; +import { HippyWebView, HippyWebEngine, HippyWebModule } from '@hippy/web-renderer'; -class CustomView extends HippyView { +class CustomView extends HippyWebView { set src(value) { this.dom.src = value; @@ -735,7 +745,7 @@ Node.removeChild(child: T): T; - 如果组件不希望以这种默认的形式来实现,可以自行通过 `insertChild` 和 `removeChild` 方法管理节点的插入和移除逻辑。 ```javascript -class CustomView extends HippyView{ +class CustomView extends HippyWebView{ insertChild (child: HippyBaseView, childPosition: number) { // ... } @@ -750,7 +760,7 @@ class CustomView extends HippyView{ > 例子中,`data` 是组件本次更新的 `props` 数据信息,`defaultProcess()` 是 `HippyWebRenderer` 默认处理 `props` 更新的方法,开发者可以在这里拦截修改更新的数据后,依然使用默认的 `props` 进行更新,也可以不用默认的方法自行进行属性更新的遍历操作。 ```javascript -class CustomView extends HippyView{ +class CustomView extends HippyWebView{ updateProps (data: UIProps, defaultProcess: (component: HippyBaseView, data: UIProps) => void) { // ... diff --git a/docs/development/native-module.md b/docs/development/native-module.md index eddde6ffda1..aa12b911f1a 100644 --- a/docs/development/native-module.md +++ b/docs/development/native-module.md @@ -43,13 +43,16 @@ public class TestModule extends HippyNativeModuleBase HippyNativeModuleBase 要求增加注解 `@HippyNativeModule` 。 -HippyNativeModule有两个注解参数: +HippyNativeModule注解参数: -- name:能力名称,js调用时需要通过此访问该能力。 -- thread:能力运行的线程。包括 `HippyNativeModule.Thread.DOM`(Dom线程)、`HippyNativeModule.Thread.MAIN`(主线程)、`HippyNativeModule.Thread.BRIDGE`(Bridge线程、默认值)。 +- name:module名称,js调用时需要通过此属性找到对应的module实例对象。 +- names:module别名,支持同一个module设置不同的名称。 +- init:默认为false,即module在首次调用的时候才会进行实例初始化,如果设置为true,在引擎创建时候就会马上创建实例并初始化 + +> **注意:init参数在非必要的情况下不要设置为true,否则可能增加引擎启动的耗时。** ``` java -@HippyNativeModule(name = "TestModule", thread = HippyNativeModule.Thread.BRIDGE) +@HippyNativeModule(name = "TestModule") public class TestModule extends HippyNativeModuleBase { ... @@ -65,8 +68,17 @@ public class TestModule extends HippyNativeModuleBase - Java基本数据类型。 - HippyArray:类似于ArrayList,线程非安全。 - HippyMap:类似于HashMap,线程非安全。 +- 基于JSValue的新数据类型:注解参数useJSValueType设置为true时适用。 - Promise:回调JS的触发器,通过 `resolve` 方法返回成功信息给JS。通过 `reject` 方法返回失败实现给JS。 +HippyMethod注解参数: + +- name:接口名称,js调用时需要通过此参数找到对应的接口信息,并进行反射调用。 +- isSync:是否为JSI接口,JSI为同步调用接口,会卡住js线程,只适用于数据结构简单且size较小的数据传输,[JSI特性介绍](feature/feature2.0/jsi.md) +- useJSValueType:接口参数是否使用新数据类型,默认为false,即使用老的HippyMap与HippyArray类型接收参数,设置为true以后参数需要使用基于JSValue为基类的扩展数据类型,[新数据类型介绍](development/type-mapping.md) + +> **注意:新数据类型不能与HippyMap或HippyArray相互嵌套混用, 否则会导致数据编解码产生错误。** + ```java @HippyMethod(name="log") public void log(String msg) @@ -107,31 +119,17 @@ public void helloNativeWithPromise(HippyMap hippyMap, Promise promise) ## 4. 注册Module -然后需要注册这个Module。需要在 `HippyPackage` 的 `getNativeModules` 方法中添加这个 Module,这样它才能在JS中被访问到。 +需要自定义'APIProvider'类,并实现SDK HippyAPIProvider interface,然后在`getNativeModules` 方法中添加这个 Module,这样它才能在JS中被访问到。 ```java -import com.tencent.mtt.hippy.HippyEngineContext; -import com.tencent.mtt.hippy.HippyPackage; -import com.tencent.mtt.hippy.common.Provider; -import com.tencent.mtt.hippy.example.module.TestModule; +public class MyAPIProvider implements HippyAPIProvider { -import com.tencent.mtt.hippy.modules.javascriptmodules.HippyJavaScriptModule; -import com.tencent.mtt.hippy.modules.nativemodules.HippyNativeModuleBase; -import com.tencent.mtt.hippy.uimanager.HippyViewController; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ExamplePackages implements HippyPackage -{ @Override - public Map, Provider> getNativeModules(final HippyEngineContext context) + public Map, Provider> getNativeModules(final HippyEngineContext context) { Map, Provider> modules = new HashMap<>(); - - // regist the LogModule - modules.put(ToastModule.class, new Provider() + //regist the MyModule + modules.put(TestModule.class, new Provider() { @Override public HippyNativeModuleBase get() @@ -139,11 +137,27 @@ public class ExamplePackages implements HippyPackage return new TestModule(context); } }); - - return modules; } + + @Override + public List> getJavaScriptModules() {return null;} + + @Override + public List> getControllers() {return null;} +} ``` +## 5. 注册APIProvider + +在HippyEngine初始化的EngineInitParams参数属性中设置providers。 + +``` java +List providers = new ArrayList<>(); +providers.add(new MyAPIProvider()); +initParams.providers = providers; +``` + + ## 注意事项 扩展Module中不能同步执行耗时操作,这可能卡住整个引擎通信线程。存在耗时场景,请使用异步线程处理。 diff --git a/docs/development/report.md b/docs/development/report.md new file mode 100644 index 00000000000..2adda06e1ed --- /dev/null +++ b/docs/development/report.md @@ -0,0 +1,21 @@ +# 曝光上报 + +--- + +# 大同 + +目前 Hippy 还没有对外的曝光上报方案,腾讯内部的业务,可以使用大同来做曝光上报 + +## 方案 + +hippy本质上使用的还是客户端原生组件 以及一部分自绘组件。客户端已集成的的大同sdk能够对原生组件、Activity等做检测上报,自然也可以对hippy的组件做检测上报。重点处理上报id绑定到组件的逻辑就可以。 + +## 接入指引 + +[Hippy Android 曝光上报指引](https://iwiki.woa.com/p/956352478) + +[Hippy iOS 曝光上报指引](https://iwiki.woa.com/p/589637144) + +有疑问可以咨询企业微信 TDF小助手 + + diff --git a/docs/development/v8-api.md b/docs/development/v8-api.md index 01fdb5a7000..838435fc8e1 100755 --- a/docs/development/v8-api.md +++ b/docs/development/v8-api.md @@ -1,3 +1,27 @@ +# V8 相关初始化参数 + +在HippyEngine初始化的时候,EngineInitParams属性里有以下v8相关的属性参数。 + +## codeCacheTag + +code cache 是V8中的一个特性,简单说就是JavaScript代码在执行前,需要进行解析和编译,才能正确执行,解析编译过程是耗时的,V8 暴露了一个方法,可以将编译产物序列化存储下来,下次再执行相同一段代码时,就可以用之前缓存的内容,节省了解析编译的时间,codeCacheTag作为编译内容缓存的key,设置后便会开启v8 code cache能力,建议开发者对该初始化参数进行设置,可以有效降低非首次启动js bundle加载运行耗时。 + +## v8InitParams + +- initialHeapSize代表v8初始Heap size +- maximumHeapSize代表v8最大Heap size + +由于v8的内存是自己管理的,使用策略是尽可能使用更多的内存,只有在达到maximumHeapSize 80%左右的时候才会触发gc,未达到之前会一直增长,达到80%触发gc的同时会回调near_heap_limit_callback接口获取内存增量,这里内存增量通过sdk内部接口V8VMInitParam::HeapLimitSlowGrowthStrategy返回,默认内存增长策略是当前max值*2,如果前端申请大内存,扩容后还不满足内存分配就会产生OOM. + +在无限滚动列表场景,设置maximumHeapSize可以有效降低v8内存增加速率。 + +修改v8初始内存参数虽然能减少内存增量,但频繁的内存申请和gc,可能引入以下2个负面影响: + +- 首屏性能下降 +- OOM率升高 + +所以v8初始化内存参数的设置需要跟进具体的业务场景设置合适的值,并做完整的测试验证,如果不是内存占用有严格要的求场景不建议设置该初始化参数。 + # V8 API 获取 V8 JS 引擎对象,并操作相关方法。 diff --git a/dom/CMakeLists.txt b/dom/CMakeLists.txt index 27b6d41b686..9027fd00546 100644 --- a/dom/CMakeLists.txt +++ b/dom/CMakeLists.txt @@ -55,7 +55,7 @@ if ("${LAYOUT_ENGINE}" STREQUAL "Yoga") target_link_libraries(${PROJECT_NAME} PRIVATE yogacore) elseif ("${LAYOUT_ENGINE}" STREQUAL "Taitank") InfraPackage_Add(taitank - REMOTE "dom/third_party/taitank/1.0.5/git-repo.tgz" + REMOTE "dom/third_party/taitank/1.0.6/git-repo.tar.gz" LOCAL "third_party/taitank" ) target_link_libraries(${PROJECT_NAME} PRIVATE taitank) diff --git a/dom/include/dom/dom_manager.h b/dom/include/dom/dom_manager.h index da57955f68e..cc145033b20 100644 --- a/dom/include/dom/dom_manager.h +++ b/dom/include/dom/dom_manager.h @@ -133,10 +133,10 @@ class DomManager : public std::enable_shared_from_this { static byte_string GetSnapShot(const std::shared_ptr& root_node); bool SetSnapShot(const std::shared_ptr& root_node, const byte_string& buffer); - void RecordDomStartTimePoint(); - void RecordDomEndTimePoint(); - inline auto GetDomStartTimePoint() { return dom_start_time_point_; } - inline auto GetDomEndTimePoint() { return dom_end_time_point_; } + void RecordDomStartTimePoint(uint32_t root_id); + void RecordDomEndTimePoint(uint32_t root_id); + inline auto GetDomStartTimePoint(uint32_t root_id) { return dom_start_time_point_[root_id]; } + inline auto GetDomEndTimePoint(uint32_t root_id) { return dom_end_time_point_[root_id]; } private: friend class DomNode; @@ -152,8 +152,8 @@ class DomManager : public std::enable_shared_from_this { std::shared_ptr task_runner_; std::shared_ptr worker_; - footstone::TimePoint dom_start_time_point_; - footstone::TimePoint dom_end_time_point_; + std::unordered_map dom_start_time_point_; + std::unordered_map dom_end_time_point_; }; } // namespace dom diff --git a/dom/include/dom/dom_node.h b/dom/include/dom/dom_node.h index 858b1d4e14b..b485c468e30 100644 --- a/dom/include/dom/dom_node.h +++ b/dom/include/dom/dom_node.h @@ -157,7 +157,7 @@ class DomNode : public std::enable_shared_from_this { void UpdateLayoutStyleInfo( const std::unordered_map>& style_update, const std::vector& style_delete); - + void ResetLayoutCache(); /** * this method should run in dom taskrunner * */ diff --git a/dom/include/dom/layout_node.h b/dom/include/dom/layout_node.h index 01a0ec3f684..ddb1926a695 100644 --- a/dom/include/dom/layout_node.h +++ b/dom/include/dom/layout_node.h @@ -122,6 +122,11 @@ class LayoutNode { virtual void SetLayoutStyles( const std::unordered_map>& style_update, const std::vector& style_delete) = 0; + + /** + * @brief 清除节点layout缓存 + */ + virtual void ResetLayoutCache() = 0; }; void InitLayoutConsts(); diff --git a/dom/include/dom/taitank_layout_node.h b/dom/include/dom/taitank_layout_node.h index 25c906344a4..b1394e6f372 100644 --- a/dom/include/dom/taitank_layout_node.h +++ b/dom/include/dom/taitank_layout_node.h @@ -194,6 +194,11 @@ class TaitankLayoutNode : public LayoutNode, public std::enable_shared_from_this */ void SetHasNewLayout(bool has_new_layout) override; + /** + * @brief 清除节点layout缓存 + */ + void ResetLayoutCache() override; + /** * @brief 节点标脏 */ diff --git a/dom/include/dom/yoga_layout_node.h b/dom/include/dom/yoga_layout_node.h index b585e7238d2..d0153e5ee63 100644 --- a/dom/include/dom/yoga_layout_node.h +++ b/dom/include/dom/yoga_layout_node.h @@ -89,6 +89,8 @@ class YogaLayoutNode : public LayoutNode, public std::enable_shared_from_this> update_node_map; // xcode crash if we change for to loop - for (std::vector>::size_type i = 0; i < active_animations_.size(); ++i) { - UpdateAnimation(active_animations_[i], now, update_node_map); + std::vector> loop_animations = active_animations_; + for (size_t i = 0; i < loop_animations.size(); ++i) { + auto it = std::find(active_animations_.begin(), active_animations_.end(), loop_animations[i]); + if (it != active_animations_.end()) { + UpdateAnimation(loop_animations[i], now, update_node_map); + } } + loop_animations.clear(); std::vector> update_nodes; update_nodes.reserve(update_node_map.size()); for (const auto& [key, value]: update_node_map) { diff --git a/dom/src/dom/dom_manager.cc b/dom/src/dom/dom_manager.cc index 74d81826d92..2a6a9e1cdb9 100644 --- a/dom/src/dom/dom_manager.cc +++ b/dom/src/dom/dom_manager.cc @@ -272,16 +272,16 @@ bool DomManager::SetSnapShot(const std::shared_ptr& root_node, const b return true; } -void DomManager::RecordDomStartTimePoint() { - if (dom_start_time_point_.ToEpochDelta() == TimeDelta::Zero()) { - dom_start_time_point_ = footstone::TimePoint::SystemNow(); +void DomManager::RecordDomStartTimePoint(uint32_t root_id) { + if (dom_start_time_point_[root_id].ToEpochDelta() == TimeDelta::Zero()) { + dom_start_time_point_[root_id] = footstone::TimePoint::SystemNow(); } } -void DomManager::RecordDomEndTimePoint() { - if (dom_end_time_point_.ToEpochDelta() == TimeDelta::Zero() - && dom_start_time_point_.ToEpochDelta() != TimeDelta::Zero()) { - dom_end_time_point_ = footstone::TimePoint::SystemNow(); +void DomManager::RecordDomEndTimePoint(uint32_t root_id) { + if (dom_end_time_point_[root_id].ToEpochDelta() == TimeDelta::Zero() + && dom_start_time_point_[root_id].ToEpochDelta() != TimeDelta::Zero()) { + dom_end_time_point_[root_id] = footstone::TimePoint::SystemNow(); } } diff --git a/dom/src/dom/dom_node.cc b/dom/src/dom/dom_node.cc index 18a7dc56d9b..9a55af08db2 100644 --- a/dom/src/dom/dom_node.cc +++ b/dom/src/dom/dom_node.cc @@ -326,6 +326,10 @@ LayoutResult DomNode::GetLayoutInfoFromRoot() { return result; } +void DomNode::ResetLayoutCache() { + layout_node_->ResetLayoutCache(); +} + void DomNode::TransferLayoutOutputsRecursive(std::vector>& changed_nodes) { auto not_equal = std::not_equal_to<>(); bool changed = layout_node_->IsDirty() || layout_node_->HasNewLayout(); diff --git a/dom/src/dom/root_node.cc b/dom/src/dom/root_node.cc index eb94863ea70..0c660158a41 100644 --- a/dom/src/dom/root_node.cc +++ b/dom/src/dom/root_node.cc @@ -306,8 +306,9 @@ void RootNode::UpdateAnimation(std::vector>&& nodes) { auto event = std::make_shared(kDomUpdated, node, nullptr); node->HandleEvent(event); } - auto event = std::make_shared(kDomTreeUpdated, weak_from_this(), nullptr); - HandleEvent(event); + // TODO: update properties instead of dom tree in debug front end + // auto event = std::make_shared(kDomTreeUpdated, weak_from_this(), nullptr); + // HandleEvent(event); if (!nodes_to_update.empty()) { dom_operations_.push_back({DomOperation::Op::kOpUpdate, nodes_to_update}); } @@ -333,7 +334,7 @@ void RootNode::SyncWithRenderManager(const std::shared_ptr& rende TDF_PERF_LOG("RootNode::DoAndFlushLayout Done"); auto dom_manager = dom_manager_.lock(); if (dom_manager) { - dom_manager->RecordDomEndTimePoint(); + dom_manager->RecordDomEndTimePoint(this->GetId()); } render_manager->EndBatch(GetWeakSelf()); TDF_PERF_LOG("RootNode::SyncWithRenderManager End"); diff --git a/dom/src/dom/scene_builder.cc b/dom/src/dom/scene_builder.cc index a122f8d87d9..6f36894211c 100644 --- a/dom/src/dom/scene_builder.cc +++ b/dom/src/dom/scene_builder.cc @@ -19,9 +19,8 @@ */ #include "dom/scene_builder.h" - #include "dom/dom_listener.h" - +#include "dom/root_node.h" #include "footstone/logging.h" #include "footstone/string_view_utils.h" @@ -33,8 +32,9 @@ void SceneBuilder::Create(const std::weak_ptr& weak_dom_manager, std::vector>&& nodes, bool needSortByIndex) { auto dom_manager = weak_dom_manager.lock(); - if (dom_manager) { - dom_manager->RecordDomStartTimePoint(); + auto rootNode = root_node.lock(); + if (dom_manager && rootNode) { + dom_manager->RecordDomStartTimePoint(rootNode->GetId()); dom_manager->CreateDomNodes(root_node, std::move(nodes), needSortByIndex); } } diff --git a/dom/src/dom/taitank_layout_node.cc b/dom/src/dom/taitank_layout_node.cc index cd9f1766fcd..b22a47dbf7b 100644 --- a/dom/src/dom/taitank_layout_node.cc +++ b/dom/src/dom/taitank_layout_node.cc @@ -98,7 +98,7 @@ class TaitankLayoutConsts { {"inherit", DIRECTION_INHERIT}, {"ltr", DIRECTION_LTR}, {"rtl", DIRECTION_RTL}}; }; -static std::shared_ptr global_layout_consts = nullptr; +static TaitankLayoutConsts* global_layout_consts = nullptr; #define TAITANK_GET_STYLE_DECL(NAME, TYPE, DEFAULT) \ static TYPE GetStyle##NAME(const std::string& key) { \ @@ -238,6 +238,11 @@ void TaitankLayoutNode::SetLayoutStyles( Parser(style_update, style_delete); } +void TaitankLayoutNode::ResetLayoutCache() { + FOOTSTONE_DLOG(INFO) << "do reset layout cache"; + engine_node_->ResetLayoutRecursive(); +} + void TaitankLayoutNode::Parser( const std::unordered_map>& style_update, const std::vector& style_delete) { @@ -823,7 +828,7 @@ void TaitankLayoutNode::Deallocate() { void InitLayoutConsts() { if (global_layout_consts == nullptr) { - global_layout_consts = std::make_shared(); + global_layout_consts = new TaitankLayoutConsts(); } } diff --git a/dom/src/dom/yoga_layout_node.cc b/dom/src/dom/yoga_layout_node.cc index 7b681694fa5..2d4a4d37bc3 100644 --- a/dom/src/dom/yoga_layout_node.cc +++ b/dom/src/dom/yoga_layout_node.cc @@ -280,6 +280,10 @@ void YogaLayoutNode::SetLayoutStyles( Parser(style_update, style_delete); } +void YogaLayoutNode::ResetLayoutCache() { + FOOTSTONE_DLOG(INFO) << "ResetLayoutCache not impl in yoga"; +} + void YogaLayoutNode::SetWidth(float width) { YGNodeStyleSetWidth(yoga_node_, width); } void YogaLayoutNode::SetHeight(float height) { YGNodeStyleSetHeight(yoga_node_, height); } diff --git a/driver/js/CMakeLists.txt b/driver/js/CMakeLists.txt index 3701693902e..892b5502695 100644 --- a/driver/js/CMakeLists.txt +++ b/driver/js/CMakeLists.txt @@ -199,7 +199,8 @@ set(SOURCE_SET_STANDALONE src/modules/performance/performance_paint_timing_module.cc src/modules/performance/performance_resource_timing_module.cc src/modules/timer_module.cc - src/modules/ui_manager_module.cc) + src/modules/ui_manager_module.cc + src/modules/ui_layout_module.cc) target_sources(${PROJECT_NAME} PRIVATE ${SOURCE_SET}) # The linker treats `.a` and `.o` files are different: # * `.o` files from `.a` file only referenced `.o` object files are linked. diff --git a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.android-vendor.js b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.android-vendor.js index 3352ea03d3d..4dcfe8c83bf 100644 --- a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.android-vendor.js +++ b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.android-vendor.js @@ -18,6 +18,7 @@ module.exports = { library: 'hippyReactBase', }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.android.js b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.android.js index 0ec17eea641..20a4e1556a4 100644 --- a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.android.js +++ b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.android.js @@ -22,6 +22,7 @@ module.exports = { // publicPath: 'https://xxx/hippy/hippyReactDemo/', }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ios-vendor.js b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ios-vendor.js index d452d0bd22b..2d6134ae4d7 100644 --- a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ios-vendor.js +++ b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ios-vendor.js @@ -18,6 +18,7 @@ module.exports = { library: 'hippyReactBase', }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ios.js b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ios.js index 481cbc5ab0a..ec799757cb5 100644 --- a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ios.js +++ b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ios.js @@ -22,6 +22,7 @@ module.exports = { // publicPath: 'https://xxx/hippy/hippyReactDemo/', }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web-renderer.dev.js b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web-renderer.dev.js index eddabd07d96..52f7c6867fa 100644 --- a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web-renderer.dev.js +++ b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web-renderer.dev.js @@ -29,13 +29,18 @@ module.exports = { globalObject: '(0, eval)("this")', }, plugins: [ + new webpack.NamedModulesPlugin(), new HtmlWebpackPlugin({ inject: true, scriptLoading: 'blocking', template: path.resolve('./public/index.html'), }), new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify('development'), + 'process.env': { + NODE_ENV: JSON.stringify('development'), + HOST: JSON.stringify(process.env.DEV_HOST || '127.0.0.1'), + PORT: JSON.stringify(process.env.DEV_PORT || 3000), + }, __PLATFORM__: JSON.stringify(platform), }), new CaseSensitivePathsPlugin(), diff --git a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web-renderer.js b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web-renderer.js index 376365e717a..d15bec933fd 100644 --- a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web-renderer.js +++ b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web-renderer.js @@ -17,6 +17,7 @@ module.exports = { path: path.resolve(`./dist/${platform}/`), }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web.dev.js b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web.dev.js index 29938d273bd..b3a6c539f7c 100644 --- a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web.dev.js +++ b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web.dev.js @@ -25,6 +25,7 @@ module.exports = { path: path.resolve(`./dist/${platform}/`), }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('development'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web.js b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web.js index aa661369fc1..8a055cb4401 100644 --- a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web.js +++ b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.web.js @@ -18,6 +18,7 @@ module.exports = { path: path.resolve(`./dist/${platform}/`), }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-react-demo/src/components/ListView/index.jsx b/driver/js/examples/hippy-react-demo/src/components/ListView/index.jsx index eb57411f47b..6c50de00f2e 100644 --- a/driver/js/examples/hippy-react-demo/src/components/ListView/index.jsx +++ b/driver/js/examples/hippy-react-demo/src/components/ListView/index.jsx @@ -4,7 +4,6 @@ import { View, StyleSheet, Text, - Platform, } from '@hippy/react'; const STYLE_LOADING = 100; @@ -271,8 +270,7 @@ export default class ListExample extends React.Component { return true; }} bounces={true} - // horizontal ListView flag(only Android support) - horizontal={horizontal} + horizontal={horizontal} // horizontal ListView flag style={[{ backgroundColor: '#ffffff' }, horizontal ? { height: 50 } : { flex: 1 }]} numberOfRows={dataSource.length} renderRow={this.getRenderRow} @@ -297,33 +295,32 @@ export default class ListExample extends React.Component { onScroll={this.onScroll} scrollEventThrottle={1000} // 1s /> - {Platform.OS === 'android' - ? this.changeDirection()} - style={{ - position: 'absolute', - right: 20, - bottom: 20, - width: 67, - height: 67, - borderRadius: 30, - boxShadowOpacity: 0.6, - boxShadowRadius: 5, - boxShadowOffsetX: 3, - boxShadowOffsetY: 3, - boxShadowColor: '#4c9afa' }}> - + this.changeDirection()} + style={{ + position: 'absolute', + right: 20, + bottom: 20, + width: 67, + height: 67, + borderRadius: 30, + boxShadowOpacity: 0.6, + boxShadowRadius: 5, + boxShadowOffsetX: 3, + boxShadowOffsetY: 3, + boxShadowColor: '#4c9afa' }}> + 切换方向 - : null} + ); } diff --git a/driver/js/examples/hippy-react-demo/src/components/WaterfallView/index.jsx b/driver/js/examples/hippy-react-demo/src/components/WaterfallView/index.jsx index 70ab0cbb087..9b0824d2258 100644 --- a/driver/js/examples/hippy-react-demo/src/components/WaterfallView/index.jsx +++ b/driver/js/examples/hippy-react-demo/src/components/WaterfallView/index.jsx @@ -74,6 +74,7 @@ export default class ListExample extends React.Component { this.onHeaderPulling = this.onHeaderPulling.bind(this); this.onFooterPulling = this.onFooterPulling.bind(this); this.renderBanner = this.renderBanner.bind(this); + this.renderFooter = this.renderFooter.bind(this); this.getItemStyle = this.getItemStyle.bind(this); this.getHeaderStyle = this.getHeaderStyle.bind(this); this.onScroll = this.onScroll.bind(this); @@ -236,10 +237,10 @@ export default class ListExample extends React.Component { } onScroll(obj) { - + console.log('onScroll', obj); } - // render banner(it is not supported on Android yet) + // render banner renderBanner() { if (this.state.dataSource.length === 0) return null; return (); } + // render footer (currently only iOS support) + renderFooter() { + if (this.state.dataSource.length === 0) return null; + return ( + Footer View + ); + } + renderItem(index) { const { dataSource } = this.state; let styleUI = null; @@ -358,11 +377,13 @@ export default class ListExample extends React.Component { interItemSpacing={interItemSpacing} numberOfItems={dataSource.length} contentInset={contentInset} - preloadItemNumber={4} + preloadItemNumber={12} style={{ flex: 1 }} onScroll={this.onScroll} renderBanner={this.renderBanner} + renderFooter={this.renderFooter} renderPullHeader={this.renderPullHeader} + renderPullFooter={this.renderPullFooter} onEndReached={this.onEndReached} onFooterReleased={this.onEndReached} onHeaderReleased={this.onHeaderReleased} @@ -372,7 +393,6 @@ export default class ListExample extends React.Component { getItemKey={this.getItemKey} getItemStyle={this.getItemStyle} getHeaderStyle={this.getHeaderStyle} - contentInset={contentInset} /> ); } diff --git a/driver/js/examples/hippy-react-demo/src/modules/Animation/index.jsx b/driver/js/examples/hippy-react-demo/src/modules/Animation/index.jsx index 6f48554bea4..866eed69e7b 100644 --- a/driver/js/examples/hippy-react-demo/src/modules/Animation/index.jsx +++ b/driver/js/examples/hippy-react-demo/src/modules/Animation/index.jsx @@ -154,6 +154,7 @@ export default class AnimationExample extends React.Component { duration: 2000, delay: 0, mode: 'timing', + valueType: 'deg', timingFunction: 'linear', }), follow: false, // 配置子动画的执行是否跟随执行 @@ -165,6 +166,7 @@ export default class AnimationExample extends React.Component { duration: 2000, delay: 0, mode: 'timing', + valueType: 'deg', timingFunction: 'linear', }), follow: true, @@ -182,6 +184,7 @@ export default class AnimationExample extends React.Component { duration: 2000, delay: 0, mode: 'timing', + valueType: 'deg', timingFunction: 'linear', }), follow: false, // 配置子动画的执行是否跟随执行 @@ -193,6 +196,7 @@ export default class AnimationExample extends React.Component { duration: 2000, delay: 0, mode: 'timing', + valueType: 'deg', timingFunction: 'linear', }), follow: true, diff --git a/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.android-vendor.js b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.android-vendor.js index dd0a29d751d..7bd76b3a139 100644 --- a/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.android-vendor.js +++ b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.android-vendor.js @@ -31,6 +31,7 @@ module.exports = { library: 'hippyVueBase', }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.android.js b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.android.js index ae08ea81ea0..b7a42a1c073 100644 --- a/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.android.js +++ b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.android.js @@ -43,6 +43,7 @@ module.exports = { // publicPath: 'https://xxx/hippy/hippyVueDemo/', }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ios-vendor.js b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ios-vendor.js index 78b5ef6d2af..9d04455b698 100644 --- a/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ios-vendor.js +++ b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ios-vendor.js @@ -31,6 +31,7 @@ module.exports = { library: 'hippyVueBase', }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ios.js b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ios.js index 891d9564a66..f16be6650ca 100644 --- a/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ios.js +++ b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ios.js @@ -43,6 +43,7 @@ module.exports = { // publicPath: 'https://xxx/hippy/hippyVueDemo/', }, plugins: [ + new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production'), __PLATFORM__: JSON.stringify(platform), diff --git a/driver/js/examples/hippy-vue-demo/src/components/demos/demo-list.vue b/driver/js/examples/hippy-vue-demo/src/components/demos/demo-list.vue index 853bda36ee8..24d2a913cb2 100644 --- a/driver/js/examples/hippy-vue-demo/src/components/demos/demo-list.vue +++ b/driver/js/examples/hippy-vue-demo/src/components/demos/demo-list.vue @@ -78,7 +78,6 @@ + +
-import { type ListViewEvent, Native } from '@hippy/vue-next'; +import { type ListViewEvent } from '@hippy/vue-next'; import { defineComponent, ref, onMounted, type Ref } from '@vue/runtime-core'; const STYLE_LOADING = 100; @@ -269,7 +268,6 @@ export default defineComponent({ list, STYLE_LOADING, horizontal, - Platform: Native.Platform, onAppear, onDelete, onDisappear, diff --git a/driver/js/examples/hippy-vue-next-demo/src/components/native-demo/demo-waterfall.vue b/driver/js/examples/hippy-vue-next-demo/src/components/native-demo/demo-waterfall.vue index d67ab9635e8..fb00f5ee650 100644 --- a/driver/js/examples/hippy-vue-next-demo/src/components/native-demo/demo-waterfall.vue +++ b/driver/js/examples/hippy-vue-next-demo/src/components/native-demo/demo-waterfall.vue @@ -4,7 +4,6 @@ ref="gridView" :content-inset="contentInset" :column-spacing="columnSpacing" - :contain-banner-view="!isAndroid" :contain-pull-footer="true" :inter-item-spacing="interItemSpacing" :number-of-columns="numberOfColumns" @@ -25,7 +24,7 @@