diff --git a/.gitignore b/.gitignore index 33cc2630ba5..f3eb78a9e39 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,11 @@ framework/voltron/VoltronCore/VoltronCore.xcodeproj/project.xcworkspace framework/voltron/VoltronCore/VoltronCore.xcodeproj/xcuserdata/ renderer/voltron/core/lib/ + +.hvigor/ +oh_modules/* +oh-package-lock.json5 +.clang-format +.clang-tidy +.clangd +BuildProfile.ets diff --git a/AppScope/app.json5 b/AppScope/app.json5 new file mode 100644 index 00000000000..e0605ec6682 --- /dev/null +++ b/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.tencent.hippydemo", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json new file mode 100644 index 00000000000..5d806813dd8 --- /dev/null +++ b/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "HippyDemo" + } + ] +} diff --git a/AppScope/resources/base/media/app_icon.png b/AppScope/resources/base/media/app_icon.png new file mode 100644 index 00000000000..cd45accb1df Binary files /dev/null and b/AppScope/resources/base/media/app_icon.png differ diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100644 index 00000000000..660eb033a34 --- /dev/null +++ b/build-profile.json5 @@ -0,0 +1,57 @@ +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug", + "buildOption": { + "externalNativeOptions": { + "abiFilters": [ + "arm64-v8a" + ] + } + } + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry_har", + "srcPath": "./framework/examples/ohos-har-demo", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "entry", + "srcPath": "./framework/examples/ohos-demo", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "hippy", + "srcPath": "./framework/ohos" + }, + ] +} diff --git a/buildconfig/cmake/GlobalPackagesModule.cmake b/buildconfig/cmake/GlobalPackagesModule.cmake index 061c633845f..166d9573910 100644 --- a/buildconfig/cmake/GlobalPackagesModule.cmake +++ b/buildconfig/cmake/GlobalPackagesModule.cmake @@ -45,6 +45,13 @@ function(GlobalPackages_Add_jni) endif() endfunction() +function(GlobalPackages_Add_oh_napi) + if (NOT TARGET oh_napi) + InfraPackage_Add(OH_NAPI + LOCAL "${PROJECT_ROOT_DIR}/modules/ohos/oh_napi") + endif() +endfunction() + function(GlobalPackages_Add_vfs) if (NOT TARGET vfs) InfraPackage_Add(VFS diff --git a/devtools/devtools-backend/CMakeLists.txt b/devtools/devtools-backend/CMakeLists.txt index e51e0c82a84..f319ec12b47 100644 --- a/devtools/devtools-backend/CMakeLists.txt +++ b/devtools/devtools-backend/CMakeLists.txt @@ -50,7 +50,11 @@ endif () # endregion # region footstone -GlobalPackages_Add(footstone) +if (OHOS) + GlobalPackages_Add_footstone() +else () + GlobalPackages_Add(footstone) +endif () target_link_libraries(${PROJECT_NAME} PRIVATE footstone) # endregion diff --git a/devtools/devtools-backend/include/api/adapter/data/domain_metas.h b/devtools/devtools-backend/include/api/adapter/data/domain_metas.h index 9a1e41b54bd..cdad49eedaf 100644 --- a/devtools/devtools-backend/include/api/adapter/data/domain_metas.h +++ b/devtools/devtools-backend/include/api/adapter/data/domain_metas.h @@ -22,7 +22,11 @@ #include #include +// nlohmann/json.hpp:3177:30: error: out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated [-Werror,-Wdeprecated] +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" #include "nlohmann/json.hpp" +#pragma clang diagnostic pop namespace hippy::devtools { diff --git a/devtools/devtools-backend/include/api/adapter/devtools_common_protocol_adapter.h b/devtools/devtools-backend/include/api/adapter/devtools_common_protocol_adapter.h index 4a188a21c72..7c02eccaa5b 100644 --- a/devtools/devtools-backend/include/api/adapter/devtools_common_protocol_adapter.h +++ b/devtools/devtools-backend/include/api/adapter/devtools_common_protocol_adapter.h @@ -19,6 +19,7 @@ */ #pragma once +#pragma clang diagnostic ignored "-Wdeprecated" #include #include "nlohmann/json.hpp" diff --git a/devtools/devtools-backend/include/tunnel/tcp/frame_codec.h b/devtools/devtools-backend/include/tunnel/tcp/frame_codec.h index 32a1bef8f29..176fe67d4d0 100644 --- a/devtools/devtools-backend/include/tunnel/tcp/frame_codec.h +++ b/devtools/devtools-backend/include/tunnel/tcp/frame_codec.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include #include diff --git a/devtools/devtools-backend/src/tunnel/tcp/tcp_channel.cc b/devtools/devtools-backend/src/tunnel/tcp/tcp_channel.cc index 77fc77651a7..9a717d6eb93 100644 --- a/devtools/devtools-backend/src/tunnel/tcp/tcp_channel.cc +++ b/devtools/devtools-backend/src/tunnel/tcp/tcp_channel.cc @@ -20,6 +20,7 @@ #include "tunnel/tcp/tcp_channel.h" #include +#include #include #include #include diff --git a/devtools/devtools-integration/CMakeLists.txt b/devtools/devtools-integration/CMakeLists.txt index 6577bf9fc02..32c7b6dfdc0 100644 --- a/devtools/devtools-integration/CMakeLists.txt +++ b/devtools/devtools-integration/CMakeLists.txt @@ -30,4 +30,7 @@ elseif (ANDROID) add_subdirectory(${PROJECT_ROOT_DIR}/devtools/devtools-integration/android/src/main/cpp) elseif (IOS) add_subdirectory(${PROJECT_ROOT_DIR}/devtools/devtools-integration/ios) +elseif (OHOS) + add_subdirectory(${PROJECT_ROOT_DIR}/devtools/devtools-integration/ohos) endif() + diff --git a/devtools/devtools-integration/native/CMakeLists.txt b/devtools/devtools-integration/native/CMakeLists.txt index 8abcb8a90f5..55cffdd2799 100644 --- a/devtools/devtools-integration/native/CMakeLists.txt +++ b/devtools/devtools-integration/native/CMakeLists.txt @@ -51,22 +51,38 @@ endif () # endregion # region footstone -GlobalPackages_Add(footstone) +if (OHOS) + GlobalPackages_Add_footstone() +else () + GlobalPackages_Add(footstone) +endif () target_link_libraries(${PROJECT_NAME} PRIVATE footstone) # endregion # region vfs -GlobalPackages_Add(vfs) +if (OHOS) + GlobalPackages_Add_vfs() +else () + GlobalPackages_Add(vfs) +endif () target_link_libraries(${PROJECT_NAME} PRIVATE vfs) # endregion # region dom -GlobalPackages_Add(dom) +if (OHOS) + GlobalPackages_Add_dom() +else () + GlobalPackages_Add(dom) +endif () target_link_libraries(${PROJECT_NAME} PRIVATE dom) # endregion # region devtools -GlobalPackages_Add(devtools_backend) +if (OHOS) + GlobalPackages_Add_devtools_backend() +else () + GlobalPackages_Add(devtools_backend) +endif () target_link_libraries(${PROJECT_NAME} PRIVATE devtools_backend) # endregion diff --git a/devtools/devtools-integration/ohos/CMakeLists.txt b/devtools/devtools-integration/ohos/CMakeLists.txt new file mode 100644 index 00000000000..a6af6fdd0de --- /dev/null +++ b/devtools/devtools-integration/ohos/CMakeLists.txt @@ -0,0 +1,73 @@ +# +# Tencent is pleased to support the open source community by making +# Hippy available. +# +# Copyright (C) 2022 THL A29 Limited, a Tencent company. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +cmake_minimum_required(VERSION 3.14) + +project("devtools_integration") + +get_filename_component(PROJECT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../" REALPATH) + +include("${PROJECT_ROOT_DIR}/buildconfig/cmake/InfraPackagesModule.cmake") +include("${PROJECT_ROOT_DIR}/buildconfig/cmake/compiler_toolchain.cmake") + +set(CMAKE_CXX_STANDARD 17) + +# region library +add_library(${PROJECT_NAME} STATIC) +target_include_directories(${PROJECT_NAME} PUBLIC include) +target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS}) +# endregion + +# region jni +GlobalPackages_Add_oh_napi() +target_link_libraries(${PROJECT_NAME} PUBLIC oh_napi) +# endregion + +# region footstone +GlobalPackages_Add_footstone() +target_link_libraries(${PROJECT_NAME} PRIVATE footstone) +# endregion + +# region dom +GlobalPackages_Add_dom() +target_link_libraries(${PROJECT_NAME} PRIVATE dom) +# endregion + +# region vfs +GlobalPackages_Add_vfs() +target_link_libraries(${PROJECT_NAME} PRIVATE vfs) +# endregion + +# region devtools +GlobalPackages_Add_devtools_backend() +target_link_libraries(${PROJECT_NAME} PRIVATE devtools_backend) +# endregion + +# region devtools_integration +add_subdirectory(${PROJECT_ROOT_DIR}/devtools/devtools-integration/native ${CMAKE_CURRENT_BINARY_DIR}/devtools-integration/native) +target_link_libraries(${PROJECT_NAME} PUBLIC devtools_integration_native) +# endregion + +# region source set +set(SOURCE_SET + src/devtools_napi.cc) + +target_sources(${PROJECT_NAME} PUBLIC ${SOURCE_SET}) +# endregion diff --git a/devtools/devtools-integration/ohos/include/devtools_napi.h b/devtools/devtools-integration/ohos/include/devtools_napi.h new file mode 100644 index 00000000000..cca4ffa10b7 --- /dev/null +++ b/devtools/devtools-integration/ohos/include/devtools_napi.h @@ -0,0 +1,32 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "footstone/string_view.h" +#include "footstone/task_runner.h" +#include +#include + +namespace hippy::devtools { + + +} + diff --git a/devtools/devtools-integration/ohos/src/devtools_napi.cc b/devtools/devtools-integration/ohos/src/devtools_napi.cc new file mode 100644 index 00000000000..2d40910b69f --- /dev/null +++ b/devtools/devtools-integration/ohos/src/devtools_napi.cc @@ -0,0 +1,190 @@ +// #include "driver/scope.h" +#include "footstone/string_view_utils.h" +#include "napi/native_api.h" + +// #include "dom_manager_napi.h" +#include +#include +#include "oh_napi/data_holder.h" +#include "oh_napi/oh_napi_register.h" +#include "oh_napi/ark_ts.h" +#include "dom/dom_manager.h" +#include "dom/root_node.h" +#include "footstone/check.h" +#include "footstone/logging.h" +#include "footstone/persistent_object_map.h" +#include "footstone/task_runner.h" +#include "footstone/worker_impl.h" +#include "devtools_napi.h" + +#include "devtools/devtools_data_source.h" +// #include "devtools/vfs/devtools_handler.h" + +namespace hippy::devtools { + +using WorkerManager = footstone::runner::WorkerManager; +using string_view = footstone::stringview::string_view; +using StringViewUtils = footstone::stringview::StringViewUtils; +constexpr uint32_t kPoolSize = 1; +constexpr char kDevToolsNapiTag[] = "DevTools-Napi:"; +std::shared_ptr worker_manager; +using DevtoolsDataSource = hippy::devtools::DevtoolsDataSource; + +static napi_value OnCreateDevtools(napi_env env, napi_callback_info info) { + // size_t requireArgc = 2; + ArkTS arkTs(env); + size_t argc = 2; + napi_value args[2] = {nullptr}; + + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + std::vector argVector = arkTs.GetCallbackArgs(info); + FOOTSTONE_DLOG(INFO) << "devtools napi::OnCreateDevtools::" << argVector.size(); + + if (argc != 2) { + napi_throw_error(env, nullptr, " Requires 2 arguments: data directory and WebSocket URL."); + return nullptr; + } + + napi_valuetype dirType = arkTs.GetType(args[0]); + napi_valuetype wsType = arkTs.GetType(args[1]); + if (!(dirType == napi_string && wsType == napi_string)) { + napi_throw_error(env, nullptr, "Both arguments must be strings."); + return nullptr; + } + + std::string data_dir = arkTs.GetString(args[0]); + std::string ws_url = arkTs.GetString(args[1]); + + worker_manager = std::make_shared(kPoolSize); + + DevtoolsDataSource::SetFileCacheDir(data_dir); + + auto devtools_data_source = + std::make_shared(); + devtools_data_source->CreateDevtoolsService(ws_url, worker_manager); + uint32_t id = devtools::DevtoolsDataSource::Insert(devtools_data_source); + + napi_value result = arkTs.CreateInt(static_cast(id)); + + return result; +} + +static napi_value OnDestroyDevtools(napi_env env, napi_callback_info info) { + ArkTS arkTs(env); + size_t argc = 2; + napi_value args[2] = {nullptr}; + + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + std::vector argVector = arkTs.GetCallbackArgs(info); + FOOTSTONE_DLOG(INFO) << "devtools napi::OnDestroyDevtools::" << argVector.size(); + + if (argc != 2) { + napi_throw_error(env, nullptr, " Requires 2 arguments: data directory and WebSocket URL."); + return nullptr; + } + + napi_valuetype devtoolsIdType = arkTs.GetType(args[0]); + napi_valuetype isReloadType = arkTs.GetType(args[1]); + if (!(devtoolsIdType == napi_number && isReloadType == napi_boolean)) { + napi_throw_error(env, nullptr, "Both arguments must be strings."); + return nullptr; + } + + int devtools_id = arkTs.GetInteger(args[0]); + bool is_reload = arkTs.GetBoolean(args[1]); + std::shared_ptr devtools_data_source = DevtoolsDataSource::Find(footstone::checked_numeric_cast(devtools_id)); + devtools_data_source->Destroy(is_reload); + bool flag = DevtoolsDataSource::Erase(footstone::checked_numeric_cast(devtools_id)); + FOOTSTONE_DLOG(INFO) << kDevToolsNapiTag << "OnDestroyDevtools devtools_id=" << devtools_id << ",flag=" << flag; + FOOTSTONE_DCHECK(flag); + worker_manager->Terminate(); + napi_value result = arkTs.GetUndefined(); + return result; +} + + +static napi_value OnBindDevtools(napi_env env, napi_callback_info info) { + ArkTS arkTs(env); + size_t argc = 4; + napi_value args[4] = {nullptr}; + + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + std::vector argVector = arkTs.GetCallbackArgs(info); + FOOTSTONE_DLOG(INFO) << "devtools napi::OnBindDevtools::" << argVector.size(); + + if (argc != 4) { + napi_throw_error(env, nullptr, " Requires 4 arguments: data directory and WebSocket URL."); + return nullptr; + } + + napi_valuetype devtoolsIdType = arkTs.GetType(args[0]); + // napi_valuetype driverIdType = arkTs.GetType(args[1]); + napi_valuetype domIdType = arkTs.GetType(args[2]); + // napi_valuetype renderIdType = arkTs.GetType(args[3]); + if (!(devtoolsIdType == napi_number && domIdType == napi_number)) { + napi_throw_error(env, nullptr, "Both arguments must be strings."); + return nullptr; + } + + int devtools_id = arkTs.GetInteger(args[0]); + // int driver_id = arkTs.GetInteger(args[1]); + int dom_id = arkTs.GetInteger(args[2]); + // int render_id = arkTs.GetInteger(args[3]); + std::shared_ptr devtools_data_source = + DevtoolsDataSource::Find(footstone::checked_numeric_cast(devtools_id)); + std::any dom_manager; + auto flag = hippy::global_data_holder.Find(footstone::checked_numeric_cast(dom_id), dom_manager); + FOOTSTONE_CHECK(flag); + auto dom_manager_object = std::any_cast>(dom_manager); + devtools_data_source->Bind(dom_manager_object); + + napi_value result = arkTs.GetUndefined(); + return result; +} + +static napi_value OnAttachToRoot(napi_env env, napi_callback_info info) { + ArkTS arkTs(env); + size_t argc = 2; + napi_value args[2] = {nullptr}; + + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + std::vector argVector = arkTs.GetCallbackArgs(info); + FOOTSTONE_DLOG(INFO) << "devtools napi::OnAttachToRoot::" << argVector.size(); + + if (argc != 2) { + napi_throw_error(env, nullptr, " Requires 2 arguments: data directory and WebSocket URL."); + return nullptr; + } + + napi_valuetype devtoolsIdType = arkTs.GetType(args[0]); + napi_valuetype rootIdType = arkTs.GetType(args[1]); + if (!(devtoolsIdType == napi_number && rootIdType == napi_number)) { + napi_throw_error(env, nullptr, "Both arguments must be number."); + return nullptr; + } + + int devtools_id = arkTs.GetInteger(args[0]); + int root_id = arkTs.GetInteger(args[1]); + + auto &root_map = RootNode::PersistentMap(); + std::shared_ptr root_node; + auto ret = root_map.Find(footstone::checked_numeric_cast(root_id), root_node); + if (!ret) { + FOOTSTONE_DLOG(WARNING) << kDevToolsTag << "OnAttachToRoot root_node is nullptr"; + return nullptr; + } + + FOOTSTONE_DLOG(INFO) << kDevToolsTag << "OnAttachToRoot root_id=" << root_id; + std::shared_ptr devtools_data_source = + DevtoolsDataSource::Find(footstone::checked_numeric_cast(devtools_id)); + devtools_data_source->SetRootNode(root_node); + napi_value result = arkTs.GetUndefined(); + return result; +} + +REGISTER_OH_NAPI("Devtools", "Devtools_OnCreateDevtools", OnCreateDevtools) +REGISTER_OH_NAPI("Devtools", "Devtools_OnDestroyDevtools", OnDestroyDevtools) +REGISTER_OH_NAPI("Devtools", "Devtools_OnBindDevtools", OnBindDevtools) +REGISTER_OH_NAPI("Devtools", "Devtools_OnAttachToRoot", OnAttachToRoot) + +} // namespace devtools diff --git a/docs/api/hippy-vue/components.md b/docs/api/hippy-vue/components.md index c2fa9c550e1..21bb0090935 100644 --- a/docs/api/hippy-vue/components.md +++ b/docs/api/hippy-vue/components.md @@ -65,7 +65,7 @@ | scrollEnabled | 当值为 `false` 的时候,内容不能滚动。`default: true` `(仅在 overflow-y/x: scroll 时适用)` | `boolean` | `Android、iOS、Web-Renderer、Voltron` | | showScrollIndicator | 是否显示滚动条。 `default: false`(仅在 overflow-y/x: scroll 时适用) | `boolean` | `Android、Voltron` | | showsHorizontalScrollIndicator | 当此值设为 `false` 的时候,`ScrollView` 会隐藏水平的滚动条。`default: true` `(仅在 overflow-y/x: scroll 时适用)`| `boolean` | `iOS、Voltron` | -| showsVerticalScrollIndicator | 当此值设为 `false` 的时候,`ScrollView` 会隐藏垂直的滚动条。 `default: true` `(仅在 overflow-y/x: scroll 时适用)`| `boolean` | `iOS、Voltron` | +| showsVerticalScrollIndicator | 当此值设为 `false` 的时候,`ScrollView` 会隐藏垂直的滚动条。 `default: true` `(仅在 overflow-y/x: scroll 时适用)`| `boolean` | `iOS、Voltron` | | nativeBackgroundAndroid | 配置水波纹效果,`最低支持版本 2.13.1`;配置项为 `{ borderless: boolean, color: Color, rippleRadius: number }`; `borderless` 表示波纹是否有边界,默认false;`color` 波纹颜色;`rippleRadius` 波纹半径,若不设置,默认容器边框为边界; `注意:设置水波纹后默认不显示,需要在对应触摸事件中调用 setPressed 和 setHotspot 方法进行水波纹展示,详情参考相关`[demo](//github.com/Tencent/Hippy/tree/master/examples/hippy-vue-demo/src/components/demos/demo-div.vue) | `Object`| `Android` | | pointerEvents | 用于控制视图是否可以成为触摸事件的目标。 | `enum('box-none', 'none', 'box-only', 'auto')` | `iOS` | | nestedScrollPriority* | 嵌套滚动事件处理优先级,`default:self`。相当于同时设置 `nestedScrollLeftPriority`、 `nestedScrollTopPriority`、 `nestedScrollRightPriority`、 `nestedScrollBottomPriority`。 `Android最低支持版本 2.16.0,iOS最低支持版本3.3.3` | `enum(self,parent,none)` | `Android、iOS` | @@ -184,17 +184,17 @@ | 参数 | 描述 | 类型 | 支持平台 | | ------------------ | ------------------------------------------------------------ | ------------------------------------ | --------- | -| src | 内嵌用的网址 | `string` | `Android、iOS、Web-Renderer、Voltron` | -| method | 请求方式,`get`、`post` | `string` | `Android、iOS` | -| userAgent | Webview userAgent | `string` | `Android、iOS、Voltron`| +| src | 内嵌用的网址 | `string` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| method | 请求方式,`get`、`post` | `string` | `Android、iOS、Ohos` | +| userAgent | Webview userAgent | `string` | `Android、iOS、Voltron、Ohos` | ## 事件 | 事件名称 | 描述 | 类型 | 支持平台 | | ------------- | ------------------------------------------------------------ | ----------------------------------------- | -------- | -| load | 网页加载成功后会触发 | `(object: { url: string }) => void` | `Android、iOS、Web-Renderer、Voltron` | -| loadStart | 网页开始加载时触发 | `(object: { url: string }) => void` | `Android、iOS、Web-Renderer、Voltron` | -| loadEnd | 网页加载结束时触发 (`success`与`error`参数仅`Android`、`iOS`上可用,最低支持版本`2.15.3`) | `(object: { url: string, success: boolean, error: string }) => void` | `Android、iOS、Web-Renderer、Voltron` | +| load | 网页加载成功后会触发 | `(object: { url: string }) => void` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| loadStart | 网页开始加载时触发 | `(object: { url: string }) => void` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| loadEnd | 网页加载结束时触发 (`success`与`error`参数仅`Android`、`iOS`上可用,最低支持版本`2.15.3`) | `(object: { url: string, success: boolean, error: string }) => void` | `Android、iOS、Web-Renderer、Voltron、Ohos` | --- @@ -211,9 +211,9 @@ | 参数 | 描述 | 类型 | 支持平台 | | ------------- | ------------------------------------------------------------ | ----------------------------------------- | -------- | -| src | 图片地址。现在支持的图片格式有 png , jpg , jpeg , bmp , gif 。 | string | `Android、iOS、Web-Renderer、Voltron` | -| capInsets | 当调整 img 大小的时候,由 capInsets 指定的边角尺寸会被固定而不进行缩放,而中间和边上其他的部分则会被拉伸。这在制作一些可变大小的圆角按钮、阴影、以及其它资源的时候非常有用。 | `{ top: number, left: number, bottom: number, right: number }` | `Android、iOS、Voltron` | -| placeholder | 指定当 `img` 组件还没加载出 `src` 属性指定的图片或者图片加载出错时的占位符图片 | `string`: 图片 base64 字符串 | `Android、iOS、Web-Renderer、Voltron` | +| src | 图片地址。现在支持的图片格式有 png , jpg , jpeg , bmp , gif 。 | string | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| capInsets | 当调整 img 大小的时候,由 capInsets 指定的边角尺寸会被固定而不进行缩放,而中间和边上其他的部分则会被拉伸。这在制作一些可变大小的圆角按钮、阴影、以及其它资源的时候非常有用。 | `{ top: number, left: number, bottom: number, right: number }` | `Android、iOS、Voltron` | +| placeholder | 指定当 `img` 组件还没加载出 `src` 属性指定的图片或者图片加载出错时的占位符图片 | `string`: 图片 base64 字符串 | `Android、iOS、Web-Renderer、Voltron、Ohos` | > `2.8.1` 版本后支持终端本地图片能力,可通过 webpack `file-loader` 加载。 @@ -221,17 +221,17 @@ | 参数 | 描述 | 类型 | 支持平台 | | ------------------ | ------------------------------------------------------------ | ------------------------------------ | --------- | -| resize-mode | 决定当组件尺寸和图片尺寸不成比例的时候如何调整图片的大小。(`Web-Renderer 不支持 repeat`) | `enum (cover, contain, stretch, repeat, center)` | `Android、iOS、Web-Renderer、Voltron` | +| resize-mode | 决定当组件尺寸和图片尺寸不成比例的时候如何调整图片的大小。(`Web-Renderer 不支持 repeat`) | `enum (cover, contain, stretch, repeat, center)` | `Android、iOS、Web-Renderer、Voltron、Ohos` | ## 事件 | 事件名称 | 描述 | 类型 | 支持平台 | | ------------- | ------------------------------------------------------------ | ----------------------------------------- | -------- | | layout | 当元素挂载或者布局改变的时候调用,参数为: `nativeEvent: { layout: { x, y, width, height } }`,其中 `x` 和 `y` 为相对父元素的坐标位置 | `Function` | `Android、iOS、Web-Renderer、Voltron` | -| load | 加载成功完成时调用此回调函数。 | `Function` | `Android、iOS、Web-Renderer、Voltron` | +| load | 加载成功完成时调用此回调函数。 | `Function` | `Android、iOS、Web-Renderer、Voltron、Ohos` | | loadStart | 加载开始时调用。 | `Function` | `Android、iOS、Web-Renderer、Voltron` | -| loadEnd | 加载结束后,不论成功还是失败,调用此回调函数。参数为:`nativeEvent: { success: number, width: number, height: number}` | `Function` | `Android、iOS、Web-Renderer、Voltron` | -| error | 当加载错误的时候调用此回调函数。| `Function` | `Android、iOS、Web-Renderer、Voltron` | +| loadEnd | 加载结束后,不论成功还是失败,调用此回调函数。参数为:`nativeEvent: { success: number, width: number, height: number}` | `Function` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| error | 当加载错误的时候调用此回调函数。| `Function` | `Android、iOS、Web-Renderer、Voltron、Ohos` | | progress | 在加载过程中不断调用,参数为 `nativeEvent: { loaded: number, total: number }`, `loaded` 表示加载中的图片大小, `total` 表示图片总大小 | `Function` | `iOS、Voltron` | | touchstart | 触屏开始事件,最低支持版本 2.6.2,参数为 `evt: { touches: [{ clientX: number, clientY: number }] }`,`clientX` 和 `clientY` 分别表示点击在屏幕内的绝对位置 | `Function` | `Android、iOS、Web-Renderer、Voltron` | | touchmove | 触屏移动事件,最低支持版本 2.6.2,参数为 `evt: { touches: [{ clientX: number, clientY: number }] }`,`clientX` 和 `clientY` 分别表示点击在屏幕内的绝对位置 | `Function` | `Android、iOS、Web-Renderer、Voltron` | @@ -293,15 +293,15 @@ | 参数 | 描述 | 类型 | 支持平台 | | --------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | --------- | -| caret-color | 输入光标颜色。(也可设置为 Style 属性) `最低支持版本2.11.5` | [`color`](api/style/color.md) | `Android、Voltron` | -| defaultValue | 提供一个文本框中的初始值。当用户开始输入的时候,值就可以改变。 在一些简单的使用情形下,如果你不想用监听消息然后更新 value 属性的方法来保持属性和状态同步的时候,就可以用 defaultValue 来代替。 | `string` | `Android、iOS、Voltron` | +| caret-color | 输入光标颜色。(也可设置为 Style 属性) `最低支持版本2.11.5` | [`color`](api/style/color.md) | `Android、Voltron、Ohos` | +| defaultValue | 提供一个文本框中的初始值。当用户开始输入的时候,值就可以改变。 在一些简单的使用情形下,如果你不想用监听消息然后更新 value 属性的方法来保持属性和状态同步的时候,就可以用 defaultValue 来代替。 | `string` | `Android、iOS、Voltron、Ohos` | | editable | 如果为 false,文本框是不可编辑的。`default: true` | `boolean` | `Android、iOS、Web-Renderer、Voltron` | -| type | 决定弹出的何种软键盘的。 注意,`password`仅在属性 `multiline=false` 单行文本框时生效。 | `enum(default, numeric, password, email, phone-pad)` | `Android、iOS、Web-Renderer、Voltron` | -| maxlength | 限制文本框中最多的字符数。使用这个属性而不用JS 逻辑去实现,可以避免闪烁的现象。 | `numbers` | `Android、iOS、Web-Renderer、Voltron` | +| type | 决定弹出的何种软键盘的。 注意,`password`仅在属性 `multiline=false` 单行文本框时生效。 | `enum(default, numeric, password, email, phone-pad)` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| maxlength | 限制文本框中最多的字符数。使用这个属性而不用JS 逻辑去实现,可以避免闪烁的现象。 | `numbers` | `Android、iOS、Web-Renderer、Voltron、Ohos` | | numberOfLines | 设置 `input` 最大显示行数,如果 `input` 没有显式设置高度,会根据 `numberOfLines` 来计算高度撑开。在使用的时候必需同时设置 `multiline` 参数为 `true`。 | `number` | `Android、Voltron、Web-Renderer` | -| placeholder | 如果没有任何文字输入,会显示此字符串。 | `string` | `Android、iOS、Web-Renderer、Voltron` | -| placeholder-text-color | 占位字符串显示的文字颜色。(也可设置为 Style 属性) `最低支持版本2.13.4` | [`color`](api/style/color.md) | `Android、iOS、Web-Renderer、Voltron` | -| returnKeyType | 指定软键盘的回车键显示的样式。(其中部分样式仅对单行文本组件有效) | `enum(done, go, next, search, send)` | `Android、iOS、Web-Renderer` | +| placeholder | 如果没有任何文字输入,会显示此字符串。 | `string` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| placeholder-text-color | 占位字符串显示的文字颜色。(也可设置为 Style 属性) `最低支持版本2.13.4` | [`color`](api/style/color.md) | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| returnKeyType | 指定软键盘的回车键显示的样式。(其中部分样式仅对单行文本组件有效) | `enum(done, go, next, search, send)` | `Android、iOS、Web-Renderer`、Ohos | | value | 指定 `input` 组件的值。 | `string` | `Android、iOS、Web-Renderer、Voltron` | | break-strategy* | 设置Android API 23及以上系统的文本换行策略。`default: simple` | `enum(simple, high_quality, balanced)` | `Android(版本 2.14.2以上)` | @@ -316,13 +316,13 @@ | ------------- | ------------------------------------------------------------ | ----------------------------------------- | -------- | | blur | 当文本框失去焦点的时候调用此回调函数。 | `Function` | `Android、iOS、Web-Renderer、Voltron` | | focus | 当文本框获得焦点的时候调用此回调函数。 | `Function` | `Android、iOS、Voltron` | -| change | 当文本框内容变化时调用此回调函数。改变后的文字内容会作为参数传递。 | `Function` | `Android、iOS、Voltron` | +| change | 当文本框内容变化时调用此回调函数。改变后的文字内容会作为参数传递。 | `Function` | `Android、iOS、Voltron、Ohos` | | keyboardWillShow | 在弹出输入法键盘时候会触发此回调函数,返回值包含键盘高度 `keyboardHeight`,样式如 `{ keyboardHeight: 260 }`。 | `Function` | `Android、iOS、Voltron` | | keyboardWillHide | 在隐藏输入法键盘时候会触发此回调函数 | `Function` | `Android、iOS、Voltron` | -| keyboardHeightChanged | 在输入法键盘高度改变时触发此回调函数,返回值包含键盘高度 `keyboardHeight`,样式如 `{ keyboardHeight: 260 }`, `最低支持版本2.14.0`。 | `Function` | `iOS、Voltron` | -| endEditing | 当文本输入结束后调用此回调函数。 | `Function` | `Android、iOS、Web-Renderer、Voltron` | +| keyboardHeightChanged | 在输入法键盘高度改变时触发此回调函数,返回值包含键盘高度 `keyboardHeight`,样式如 `{ keyboardHeight: 260 }`, `最低支持版本2.14.0`。 | `Function` | `iOS、Voltron` | +| endEditing | 当文本输入结束后调用此回调函数。 | `Function` | `Android、iOS、Web-Renderer、Voltron、Ohos` | | layout | 当元素挂载或者布局改变的时候调用,参数为: `nativeEvent: { layout: { x, y, width, height } }`,其中 `x` 和 `y` 为相对父元素的坐标位置。 | `Function` | `Android、iOS、Web-Renderer、Voltron` | -| selectionChange | 当输入框选择文字的范围被改变时调用。返回参数的样式如 `{ nativeEvent: { selection: { start, end } } }`。 | `Function` | `Android、iOS、Web-Renderer、Voltron` | +| selectionChange | 当输入框选择文字的范围被改变时调用。返回参数的样式如 `{ nativeEvent: { selection: { start, end } } }`。 | `Function` | `Android、iOS、Web-Renderer、Voltron、Ohos` | ## 方法 @@ -385,15 +385,15 @@ Hippy 的重点功能,高性能的可复用列表组件,在终端侧会被 | 参数 | 描述 | 类型 | 支持平台 | | --------------------- | ------------------------------------------------------------ | ----------------------------------------------------------- | -------- | -| horizontal | 指定 `ul` 是否采用横向布局。`default: undefined` 纵向布局,Android `2.14.1` 版本后可设置 `false` 显式固定纵向布局;iOS 暂不支持横向 `ul` | `boolean \| undefined` | `Android、Voltron` | -| initialContentOffset | 初始位移值。在列表初始化时即可指定滚动距离,避免初始化后再通过 scrollTo 系列方法产生的闪动。Android 在 `2.8.0` 版本后支持 | `number` | `Android、iOS、Web-Renderer、Voltron` | +| horizontal | 指定 `ul` 是否采用横向布局。`default: undefined` 纵向布局,Android `2.14.1` 版本后可设置 `false` 显式固定纵向布局;iOS 暂不支持横向 `ul` | `boolean \| undefined` | `Android、Voltron、Ohos` | +| initialContentOffset | 初始位移值。在列表初始化时即可指定滚动距离,避免初始化后再通过 scrollTo 系列方法产生的闪动。Android 在 `2.8.0` 版本后支持 | `number` | `Android、iOS、Web-Renderer、Voltron、Ohos` | | bounces | 是否开启回弹效果,默认 `true`, Android `2.14.1` 版本后支持该属性,老版本使用 `overScrollEnabled` | `boolean` | `Android、iOS、Voltron` | | rowShouldSticky | 设置 `ul` 是否需要开启悬停效果能力,与 `li` 的 `sticky` 配合使用。 `default: false` | `boolean` | `Android、iOS、Web-Renderer、Voltron`| -| scrollEnabled | 滑动是否开启。`default: true` | `boolean` | `Android、iOS、Web-Renderer、Voltron` | -| scrollEventThrottle | 指定滑动事件的回调频率,传入数值指定了多少毫秒(ms)组件会调用一次 `onScroll` 回调事件,默认 200ms | `number` | `Android、iOS、Web-Renderer、Voltron` | +| scrollEnabled | 滑动是否开启。`default: true` | `boolean` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| scrollEventThrottle | 指定滑动事件的回调频率,传入数值指定了多少毫秒(ms)组件会调用一次 `onScroll` 回调事件,默认 200ms | `number` | `Android、iOS、Web-Renderer、Voltron、Ohos` | | showScrollIndicator | 是否显示滚动条。`default: true` | `boolean` | `iOS、Voltron` | -| preloadItemNumber | 指定当列表滚动至倒数第几行时触发 `endReached` 回调。 | `number` | `Android、iOS、Web-Renderer、Voltron` | -| exposureEventEnabled | Android 曝光能力启用开关,如果要使用 `appear`、`disappear` 相关事件,Android 需要设置该开关(iOS无需设置), `default: true` | `boolean` | `Android、Voltron`| +| preloadItemNumber | 指定当列表滚动至倒数第几行时触发 `endReached` 回调。 | `number` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| exposureEventEnabled | Android 曝光能力启用开关,如果要使用 `appear`、`disappear` 相关事件,Android 需要设置该开关(iOS无需设置), `default: true` | `boolean` | `Android、Voltron、Ohos` | | endReached | 当所有的数据都已经渲染过,并且列表被滚动到最后一条时,将触发 `endReached` 回调。 | `Function` | `Android、iOS、Web-Renderer、Voltron` | | editable | 是否可编辑,开启侧滑删除时需要设置为 `true`。`最低支持版本2.9.0` | `boolean` | `iOS` | | delText | 侧滑删除文本。`最低支持版本2.9.0` | `string` | `iOS` | @@ -420,12 +420,12 @@ Hippy 的重点功能,高性能的可复用列表组件,在终端侧会被 | 事件名称 | 描述 | 类型 | 支持平台 | | ------------- | ------------------------------------------------------------ | ----------------------------------------- | -------- | -| endReached | 当所有的数据都已经渲染过,并且列表被滚动到最后一条时,将触发 `onEndReached` 回调。 | `Function` | `Android、iOS、Web-Renderer、Voltron` | -| momentumScrollBegin | 在 `ListView` 开始滑动的时候调起,`2.14.6` 版本后支持 `offset` 相关参数 | `(event: { offsetX: number, offsetY: number }) => any` | `Android、iOS、Web-Renderer、Voltron` | -| momentumScrollEnd | 在 `ListView` 结束滑动的时候调起,`2.14.6` 版本后支持 `offset` 相关参数 | `(event: { offsetX: number, offsetY: number }) => any` | `Android、iOS、Web-Renderer、Voltron` | -| scroll | 当触发 `ListView` 的滑动事件时回调。由于在 `ListView` 滑动时回调,调用会非常频繁,请使用 `scrollEventThrottle` 进行频率控制。 注意:ListView 在滚动时会进行组件回收,不要在滚动时对 renderRow() 生成的 ListItemView 做任何 ref 节点级的操作(例如:所有 callUIFunction 和 measureInAppWindow 方法),回收后的节点将无法再进行操作而报错。横向ListView时,Android在 `2.8.0` 版本后支持 | `(event: { offsetX: number, offsetY: number }) => any` | `Android、iOS、Web-Renderer、Voltron` | -| scrollBeginDrag | 当用户开始拖拽 `ListView` 时调用,`2.14.6` 版本后支持 `offset` 相关参数 | `(event: { offsetX: number, offsetY: number }) => any` | `Android、iOS、Web-Renderer、Voltron` | -| scrollEndDrag | 当用户停止拖拽 `ListView` 或者放手让 `ListView` 开始滑动的时候调用,`2.14.6` 版本后支持 `offset` 相关参数 | `(event: { offsetX: number, offsetY: number }) => any` | `Android、iOS、Web-Renderer、Voltron` | +| endReached | 当所有的数据都已经渲染过,并且列表被滚动到最后一条时,将触发 `onEndReached` 回调。 | `Function` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| momentumScrollBegin | 在 `ListView` 开始滑动的时候调起,`2.14.6` 版本后支持 `offset` 相关参数 | `(event: { offsetX: number, offsetY: number }) => any` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| momentumScrollEnd | 在 `ListView` 结束滑动的时候调起,`2.14.6` 版本后支持 `offset` 相关参数 | `(event: { offsetX: number, offsetY: number }) => any` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| scroll | 当触发 `ListView` 的滑动事件时回调。由于在 `ListView` 滑动时回调,调用会非常频繁,请使用 `scrollEventThrottle` 进行频率控制。 注意:ListView 在滚动时会进行组件回收,不要在滚动时对 renderRow() 生成的 ListItemView 做任何 ref 节点级的操作(例如:所有 callUIFunction 和 measureInAppWindow 方法),回收后的节点将无法再进行操作而报错。横向ListView时,Android在 `2.8.0` 版本后支持 | `(event: { offsetX: number, offsetY: number }) => any` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| scrollBeginDrag | 当用户开始拖拽 `ListView` 时调用,`2.14.6` 版本后支持 `offset` 相关参数 | `(event: { offsetX: number, offsetY: number }) => any` | `Android、iOS、Web-Renderer、Voltron、Ohos` | +| scrollEndDrag | 当用户停止拖拽 `ListView` 或者放手让 `ListView` 开始滑动的时候调用,`2.14.6` 版本后支持 `offset` 相关参数 | `(event: { offsetX: number, offsetY: number }) => any` | `Android、iOS、Web-Renderer、Voltron、Ohos` | | layout | 当元素挂载或者布局改变的时候调用,参数为: `nativeEvent: { layout: { x, y, width, height } }`,其中 `x` 和 `y` 为相对父元素的坐标位置。 | `Function` | `Android、iOS、Web-Renderer、Voltron` | | delete | 在列表项侧滑删除时调起。`最低支持版本2.9.0` | `(nativeEvent: { index: number}) => void` | `iOS` | diff --git a/docs/development/native-event.md b/docs/development/native-event.md index 02772a9a1ee..f7e5a4ef415 100644 --- a/docs/development/native-event.md +++ b/docs/development/native-event.md @@ -142,6 +142,18 @@ hippyEngine.sendEvent("rotate", hippyMap); - (void)sendEvent:(NSString *)eventName params:(NSDictionary *_Nullable)params; ``` +# Ohos + +鸿蒙平台接口和安卓相似。 + +终端在需要发送事件的地方调用代码: + +```typescript +const map = new Map(); +map.pushString("result", "hello i am from native"); +hippyEngine.sendEvent("rotate", map); +``` + # Voltron ```dart diff --git a/docs/development/native-integration.md b/docs/development/native-integration.md index c7d43e6ff83..1f343f07c26 100644 --- a/docs/development/native-integration.md +++ b/docs/development/native-integration.md @@ -1,6 +1,6 @@ # 环境搭建 -这篇教程,讲述了如何将 Hippy 集成到 Android、iOS 、Flutter、Web(同构) 等平台。 +这篇教程,讲述了如何将 Hippy 集成到 Android、iOS、Ohos、Flutter、Web(同构) 等平台。


@@ -272,6 +272,126 @@ ENV['layout_engine'] = 'Yoga' 之后,重新执行`pod install`命令更新项目依赖即可。 +# Ohos + +> 注:以下文档都是假设您已经具备一定的 Ohos 开发经验。 + +--- + +## 前期准备 + +- 已经安装 DevEco Studio 最新版本 + +## Demo 体验 + +Ohos Har Demo:Har 包方式依赖 Hippy。 体验方法:DevEco 打开 hippy 项目根目录运行 entry_har。 + +Ohos Demo:源码依赖 Hippy。体验方法:DevEco 打开 hippy 项目根目录直接运行 entry。 + +## 接入方式一:Har包快速接入 + +### 1. 创建一个 Ohos 工程 + +### 2. Har 包集成 + +- 配置 oh-package.json5 + + ```json + "dependencies": { + "hippy": "1.3.0" + } + ``` + +### 3. 初始化代码 + +- 获取 libhippy.so 接口对象和 UIAbility context + + ```TypeScript + import libHippy from 'libhippy.so' + AppStorage.setOrCreate("libHippy", libHippy) + AppStorage.setOrCreate("abilityContext", this.context) + ``` + +> 注:App 直接集成 Hippy,context 使用 UIAbility context;如果 App 在一个模块里集成 Hippy,js 等资源也集成在模块里,context 使用 getContext().createModuleContext("moduleName"),否则会找不到 js 等资源。 + +- 创建 HippyEngine、初始化 HippyEngine、加载业务 bundle + + ```TypeScript + this.hippyEngine = createHippyEngine(params) + this.hippyEngine.initEngine() + this.hippyEngine?.loadModule() + ``` + +- 组装 HippyRoot 组件 + + ```TypeScript + HippyRoot({ + hippyEngine: this.hippyEngine, + rootViewWrapper: this.rootViewWrapper, + onRenderException: (exception: HippyException) => { + this.exception = `${exception.message}\n${exception.stack}` + }, + }) + ``` + +具体可以参考 [Har Demo](https://github.com/sohotz/Hippy/tree/main/framework/examples/ohos-har-demo) 工程中 `EntryAbility.ets` `Index.ets` 实现 + +## 接入方式二:源码接入 + +> 源码接入主要为了方便在 App 项目里直接调试 Hippy 代码(c++ 和 ets 代码)。 + +### 1. 创建一个 Ohos 工程 + +### 2. Hippy 代码集成 + +- 拉取 hippy 代码到项目里(比如:根目录下) + +> https://github.com/sohotz/Hippy.git,分支:main + +- 配置 oh-package.json5 + + ```json + "dependencies": { + "hippy": "file:../Hippy/framework/ohos/" + } + ``` + +### 3. 初始化代码 + +- 获取 libhippy.so 接口对象和 UIAbility context + + ```TypeScript + import libHippy from 'libhippy.so' + AppStorage.setOrCreate("libHippy", libHippy) + AppStorage.setOrCreate("abilityContext", this.context) + ``` + +- 创建 HippyEngine、初始化 HippyEngine、加载业务 bundle + + ```TypeScript + this.hippyEngine = createHippyEngine(params) + this.hippyEngine.initEngine() + this.hippyEngine?.loadModule() + ``` + +- 组装 HippyRoot 组件 + + ```TypeScript + HippyRoot({ + hippyEngine: this.hippyEngine, + rootViewWrapper: this.rootViewWrapper, + onRenderException: (exception: HippyException) => { + this.exception = `${exception.message}\n${exception.stack}` + }, + }) + ``` + +具体可以参考 [Demo](https://github.com/sohotz/Hippy/tree/main/framework/examples/ohos-demo) 工程中 `EntryAbility.ets` `ExampleHippyPage.ets` 实现 + +## 接入方式三:定制场景接入 + +- 对于需要直接依赖 hippy c++ 代码编译使用的定制场景,可参考 [Demo](https://github.com/sohotz/Hippy/tree/main/framework/examples/ohos-demo) 工程中 `CMakeLists.txt` 说明 + # Voltron/Flutter > 注:以下文档都是假设您已经具备一定的 Flutter 开发经验。 diff --git a/docs/development/native-module.md b/docs/development/native-module.md index aa12b911f1a..1fa924fb13e 100644 --- a/docs/development/native-module.md +++ b/docs/development/native-module.md @@ -274,6 +274,141 @@ HIPPY_EXPORT_METHOD(click) { @end ``` +# 鸿蒙 + +很多时候 `JS` 需要访问对应终端的一些能力模块,比如数据库、下载、网络请求等,这时候就需要使用 `Module` 来暴露接口给JS使用。Voltron SDK 中默认实现了部分 `Module`,但这极有可能无法满足你的需求,这就需要你对 `Module` 进行扩展封装。 + +--- + +## Module扩展 + +我们将以 `TestModule` 为例,从头扩展一个 `Module`,这个 `Module` 将展示前端如何调用终端能力,并且把结果返回给前端 + +终端扩展 `Module` 包括四步: + +1. 创建 `TestModule` +2. 实现导出给 `JS` 的方法。 +3. 注册 `Module`。 + +## 1. 创建 `TestModule`,并且继承 HippyNativeModuleBase + +```typescript +export class ExampleNativeModule extends HippyNativeModuleBase { + public static readonly NAME = 'ExampleNativeModule' + + constructor(ctx: HippyEngineContext) { + super(ctx) + } + + public call(method: string, params: Array, promise: HippyModulePromise): HippyAny { + switch (method) { + case 'test': { + this.test(); + break; + } + case 'testPromise': { + this.testPromise(params, promise); + break; + } + case 'testSendEvent': { + this.testSendEvent(params, promise); + } + default: + super.call(method, params, promise); + } + return null; + } + + public test() { + LogUtils.i(ExampleNativeModule.NAME, 'module test'); + } + + public testPromise(params: Array, promise: HippyModulePromise) { + promise.resolve('test'); + } + + public testSendEvent(params: Array, promise: HippyModulePromise) { + LogUtils.i(ExampleNativeModule.NAME, 'testSendEvent'); + if (this.ctx != null && this.ctx.getModuleManager() != null) { + const eventModule = this.ctx.getModuleManager().getJavaScriptModule(EventDispatcher.MODULE_NAME); + if (eventModule != null) { + const event = 'testEvent'; + const params = new Map(); + params.set('testString', 'testStringValue'); + + const valueMap = new Map(); + valueMap.set('testString2', 'testStringValue2'); + params.set('testMap', valueMap); + + const array: HippyArray = []; + array.push(valueMap); + params.set('testArray', array); + + (eventModule as EventDispatcher).receiveNativeEvent(event, params); + } + } + } + +} + +``` + +需要注意的是,这里与Android、iOS有几处不同。 + +1. 需要指定 NAME,设置为前端调用的 module name + +2. 需要实现 call 方法 + +## Turbo Module扩展 + +和 Module 的扩展一致,不过还需要配置 isTurbo 方法,且不需要实现 call 方法,参考如下: + +```typescript +export class ExampleNativeTurboModule extends HippyNativeModuleBase { + public static readonly NAME = 'demoTurbo' + + constructor(ctx: HippyEngineContext) { + super(ctx) + } + + isTurbo(): boolean { + return true + } + + public getString(info: string): string { + return 'demoTurbo' + info; + } + + public getNum(num: number): number { + return num; + } + + public getBoolean(b: boolean): boolean { + return b; + } + + public getMap(map: HippyMap): HippyMap { + return map + } + + public getArray(array: HippyArray): HippyArray { + return array + } + + public getObject(obj: HippyAny): HippyAny { + return obj + } + + public getTurboConfig(): TurboConfig { + return new TurboConfig(); + } + + public printTurboConfig(turboConfig: TurboConfig): string { + return turboConfig.info; + } +} + +``` # Voltron diff --git a/docs/feature/feature2.0/jsi.md b/docs/feature/feature2.0/jsi.md index 7fd33998677..49298610347 100644 --- a/docs/feature/feature2.0/jsi.md +++ b/docs/feature/feature2.0/jsi.md @@ -148,6 +148,107 @@ HIPPY_EXPORT_TURBO_METHOD(setInfo:(NSString *)string) { 更多示例可参考类[DemoIOSTurboModule](https://github.com/Tencent/Hippy/blob/master/examples/ios-demo/HippyDemo/turbomodule/TurboBaseModule.mm) +## 鸿蒙 + +* 通过设置引擎初始化参数开启JSI能力 + +```typescript +initParams.enableTurbo = true +``` + +* 定义Module + +> 跟普通NativeModule类似,区别在于需要设置 isTurbo 为 true + +```typescript +export class ExampleNativeTurboModule extends HippyNativeModuleBase { + public static readonly NAME = 'demoTurbo' + + constructor(ctx: HippyEngineContext) { + super(ctx) + } + + isTurbo(): boolean { + return true + } + + public getString(info: string): string { + return 'demoTurbo' + info; + } + + public getNum(num: number): number { + return num; + } + + public getBoolean(b: boolean): boolean { + return b; + } + + public getMap(map: HippyMap): HippyMap { + return map + } + + public getArray(array: HippyArray): HippyArray { + return array + } + + public getObject(obj: HippyAny): HippyAny { + return obj + } + + public getTurboConfig(): TurboConfig { + return new TurboConfig(); + } + + public printTurboConfig(turboConfig: TurboConfig): string { + return turboConfig.info; + } +} +``` + +> 嵌套 turbo 对象需要设置注解@HippyTurboObject, 否则会当作普通对象解析 + +``` typescript +@HippyTurboObject() +export class TurboConfig { + public static readonly NAME = 'turboConfig' + public info = "info from turboConfig" + private static instance: TurboConfig; + + public static getInstance() { + if (!TurboConfig.instance) { + TurboConfig.instance = new TurboConfig(); + } + return TurboConfig.instance; + } + + public toString() { + return this.info; + } + + public getTestString() { + return 'test' + this.info; + } +} +``` + +> 支持的数据类型说明: + +| Objec类型 | Js类型 | +|:----------|:----------| +| napi_boolean | Bool | +| napi_number | Number | +| napi_bigint | Number | +| napi_string | String | +| napi_object | Array | +| napi_object | Object | +| NULL | null | + +更多示例可参考类[DemoTurboModule](https://github.com/sohotz/Hippy/blob/main/framework/examples/ohos-demo/src/main/ets/hippy_extend/ExampleNativeTurboModule.ets) + +* 注册TurboModule模块,跟NativeModule注册方法完全一致 + + # 使用例子 diff --git a/dom/CMakeLists.txt b/dom/CMakeLists.txt index 9027fd00546..7cba289b8ae 100644 --- a/dom/CMakeLists.txt +++ b/dom/CMakeLists.txt @@ -37,7 +37,12 @@ target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS}) # endregion # region footstone -GlobalPackages_Add(footstone) +if (OHOS) + GlobalPackages_Add_footstone() +else () + GlobalPackages_Add(footstone) +endif () + target_link_libraries(${PROJECT_NAME} PRIVATE footstone) # endregion @@ -47,10 +52,17 @@ if (NOT DEFINED LAYOUT_ENGINE) endif () if ("${LAYOUT_ENGINE}" STREQUAL "Yoga") - InfraPackage_Add(yoga - REMOTE "dom/third_party/yoga/1.19.0/git-repo.tgz" - LOCAL "third_party/yoga" - ) + if (ANDROID) + InfraPackage_Add(yoga + REMOTE "dom/third_party/yoga/1.19.0/git-repo.tgz" + LOCAL "third_party/yoga" + ) + else () + InfraPackage_Add(yoga + REMOTE "dom/third_party/yoga_ohos/1.19.0/git-repo.tgz" + LOCAL "third_party/yoga" + ) + endif () target_compile_definitions(${PROJECT_NAME} PRIVATE "USE_YOGA") target_link_libraries(${PROJECT_NAME} PRIVATE yogacore) elseif ("${LAYOUT_ENGINE}" STREQUAL "Taitank") diff --git a/dom/include/dom/dom_node.h b/dom/include/dom/dom_node.h index b485c468e30..ea9900ed1d2 100644 --- a/dom/include/dom/dom_node.h +++ b/dom/include/dom/dom_node.h @@ -243,6 +243,10 @@ class DomNode : public std::enable_shared_from_this { bool is_virtual_{}; bool layout_only_ = false; + // support root wrap_content, invoke SetRootSize with param NAN + bool is_layout_width_nan_ = false; + bool is_layout_height_nan_ = false; + // Node can only be eliminated for the first time, // and if they cannot be eliminated for the first time, they cannot be eliminated at all times. bool enable_eliminated_ = true; diff --git a/dom/include/dom/layout_node.h b/dom/include/dom/layout_node.h index ddb1926a695..c0234b31c78 100644 --- a/dom/include/dom/layout_node.h +++ b/dom/include/dom/layout_node.h @@ -81,6 +81,8 @@ class LayoutNode { virtual void SetWidth(float width) = 0; virtual void SetHeight(float height) = 0; + virtual void SetMaxWidth(float width) = 0; + virtual void SetMaxHeight(float height) = 0; virtual void SetPosition(Edge edge, float position) = 0; virtual void SetScaleFactor(float scale_factor) = 0; virtual bool HasNewLayout() = 0; diff --git a/dom/include/dom/root_node.h b/dom/include/dom/root_node.h index 2b8f505565b..e2a7a79059e 100644 --- a/dom/include/dom/root_node.h +++ b/dom/include/dom/root_node.h @@ -53,6 +53,18 @@ class DomNodeStyleDiffer { std::unordered_map>> node_ext_style_map_; }; +struct ListenerOp { + bool add; + std::weak_ptr dom_node; + std::string name; + + ListenerOp(bool add, std::weak_ptr dom_node, const std::string& name) { + this->add = add; + this->dom_node = dom_node; + this->name = name; + } +}; + class RootNode : public DomNode { public: using TaskRunner = footstone::runner::TaskRunner; @@ -72,6 +84,8 @@ class RootNode : public DomNode { virtual void AddEventListener(const std::string& name, uint64_t listener_id, bool use_capture, const EventCallback& cb) override; virtual void RemoveEventListener(const std::string& name, uint64_t listener_id) override; + + std::map> &EventListenerOps() { return event_listener_ops_; } void ReleaseResources(); void CreateDomNodes(std::vector>&& nodes, bool needSortByIndex); @@ -94,11 +108,16 @@ class RootNode : public DomNode { void SetRootOrigin(float x, float y); void Traverse(const std::function&)>& on_traverse); void AddInterceptor(const std::shared_ptr& interceptor); + void SetDisableSetRootSize(bool disable) { + disable_set_root_size_ = disable; + } static footstone::utils::PersistentObjectMap>& PersistentMap() { return persistent_map_; } + std::vector> GetAllTextNodes(); + private: static void MarkLayoutNodeDirty(const std::vector>& nodes); @@ -115,6 +134,7 @@ class RootNode : public DomNode { std::vector dom_operations_; std::vector event_operations_; + std::map> event_listener_ops_; void FlushDomOperations(const std::shared_ptr& render_manager); void FlushEventOperations(const std::shared_ptr& render_manager); @@ -128,6 +148,8 @@ class RootNode : public DomNode { std::shared_ptr animation_manager_; std::unique_ptr style_differ_; + bool disable_set_root_size_ { false }; + static footstone::utils::PersistentObjectMap> persistent_map_; }; diff --git a/dom/include/dom/taitank_layout_node.h b/dom/include/dom/taitank_layout_node.h index b1394e6f372..14854b71389 100644 --- a/dom/include/dom/taitank_layout_node.h +++ b/dom/include/dom/taitank_layout_node.h @@ -239,13 +239,13 @@ class TaitankLayoutNode : public LayoutNode, public std::enable_shared_from_this * @brief 设置 max width 属性 * @param max_width 最大宽度 */ - void SetMaxWidth(float max_width); + void SetMaxWidth(float max_width) override; /** * @brief 设置 max height 属性 * @param max_height 最大高度 */ - void SetMaxHeight(float max_height); + void SetMaxHeight(float max_height) override; /** * @brief 设置 min width 属性 diff --git a/dom/include/dom/yoga_layout_node.h b/dom/include/dom/yoga_layout_node.h index d0153e5ee63..48906156e9c 100644 --- a/dom/include/dom/yoga_layout_node.h +++ b/dom/include/dom/yoga_layout_node.h @@ -46,6 +46,10 @@ class YogaLayoutNode : public LayoutNode, public std::enable_shared_from_this hippy_value); - void SetFlexBasis(float flex_basis); + void SetYGFlexBasis(std::shared_ptr dom_value); void SetFlex(float flex); diff --git a/dom/src/dom/animation/animation_manager.cc b/dom/src/dom/animation/animation_manager.cc index 04aa8fdd332..8e39a617acb 100644 --- a/dom/src/dom/animation/animation_manager.cc +++ b/dom/src/dom/animation/animation_manager.cc @@ -345,11 +345,12 @@ void AnimationManager::UpdateCubicBezierAnimation(double current, HippyValue prop_value(current); for (auto prop: prop_it->second) { dom_node->EmplaceStyleMapAndGetDiff(prop, prop_value, diff_value); - FOOTSTONE_DLOG(INFO) << "animation related_animation_id = " << related_animation_id - << "node id = " << dom_node->GetId() << ", key = " << prop << ", value = " << prop_value; + if (footstone::gEnableUpdateAnimLog) { + FOOTSTONE_DLOG(INFO) << "animation related_animation_id = " << related_animation_id + << "node id = " << dom_node->GetId() << ", key = " << prop << ", value = " << prop_value; + } } - dom_node->SetDiffStyle(std::make_shared< std::unordered_map>>(std::move(diff_value))); update_node_map[dom_node_id] = dom_node; @@ -409,6 +410,8 @@ void AnimationManager::UpdateAnimations() { return; } + footstone::AutoInUpdateAnimScope autoInUpdateAnimScope; + auto now = footstone::time::MonotonicallyIncreasingTime(); std::unordered_map> update_node_map; // xcode crash if we change for to loop diff --git a/dom/src/dom/animation/cubic_bezier_animation.cc b/dom/src/dom/animation/cubic_bezier_animation.cc index df33f510f2b..89b1c61f511 100644 --- a/dom/src/dom/animation/cubic_bezier_animation.cc +++ b/dom/src/dom/animation/cubic_bezier_animation.cc @@ -51,22 +51,32 @@ CubicBezier CubicBezierAnimation::ParseCubicBezierStr(const std::string& str) { double CubicBezierAnimation::CalculateColor(double start_color, double to_color, double scale) { auto start_value = static_cast(start_color); - auto start_red = static_cast(((start_value >> 24) & 0xff)); - auto start_green = static_cast(((start_value >> 16) & 0xff)); - auto start_blue = static_cast(((start_value >> 8) & 0xff)); - auto start_alpha = static_cast((start_value & 0xff)); - + if (start_color < 0) { + auto start_int_value = static_cast(start_color); + start_value = static_cast(start_int_value); + } + + auto start_alpha = ((start_value >> 24) & 0xff); + auto start_red = ((start_value >> 16) & 0xff); + auto start_green = ((start_value >> 8) & 0xff); + auto start_blue = (start_value & 0xff); + auto to_value = static_cast(to_color); - auto to_red = static_cast(((to_value >> 24) & 0xff)); - auto to_green = static_cast(((to_value >> 16) & 0xff)); - auto to_blue = static_cast(((to_value >> 8) & 0xff)); - auto to_alpha = static_cast((to_value & 0xff)); - - auto red = static_cast(start_red + (to_red - start_red) * scale); - auto green = static_cast(start_green + (to_green - start_green) * scale); - auto blue = static_cast(start_blue + (to_blue - start_blue) * scale); - auto alpha = static_cast(start_alpha + (to_alpha - start_alpha) * scale); - auto ret = (static_cast(red) << 24) + (static_cast(green) << 16) + (static_cast(blue) << 8) + alpha; + if (to_color < 0) { + auto to_int_value = static_cast(to_color); + to_value = static_cast(to_int_value); + } + + auto to_alpha = ((to_value >> 24) & 0xff); + auto to_red = ((to_value >> 16) & 0xff); + auto to_green = ((to_value >> 8) & 0xff); + auto to_blue = (to_value & 0xff); + + auto red = static_cast(start_red + static_cast(to_red - start_red) * scale); + auto green = static_cast(start_green + static_cast(to_green - start_green) * scale); + auto blue = static_cast(start_blue + static_cast(to_blue - start_blue) * scale); + auto alpha = static_cast(start_alpha + static_cast(to_alpha - start_alpha) * scale); + auto ret = (static_cast(alpha) << 24) + (static_cast(red) << 16) + (static_cast(green) << 8) + static_cast(blue); return static_cast(ret); } diff --git a/dom/src/dom/dom_manager.cc b/dom/src/dom/dom_manager.cc index 2a6a9e1cdb9..b319cea536f 100644 --- a/dom/src/dom/dom_manager.cc +++ b/dom/src/dom/dom_manager.cc @@ -132,7 +132,9 @@ void DomManager::EndBatch(const std::weak_ptr& weak_root_node) { if (!root_node) { return; } - FOOTSTONE_DLOG(INFO) << "[Hippy Statistic] total node size = " << root_node->GetChildCount(); + if (!footstone::gInUpdateAnimScope || footstone::gEnableUpdateAnimLog) { + FOOTSTONE_DLOG(INFO) << "[Hippy Statistic] total node size = " << root_node->GetChildCount(); + } root_node->SyncWithRenderManager(render_manager); } diff --git a/dom/src/dom/dom_node.cc b/dom/src/dom/dom_node.cc index 9a55af08db2..6c7d68adc90 100644 --- a/dom/src/dom/dom_node.cc +++ b/dom/src/dom/dom_node.cc @@ -205,7 +205,7 @@ void DomNode::DoLayout() { } void DomNode::DoLayout(std::vector>& changed_nodes) { - layout_node_->CalculateLayout(0, 0); + layout_node_->CalculateLayout(is_layout_width_nan_ ? NAN : 0, is_layout_height_nan_ ? NAN : 0); TransferLayoutOutputsRecursive(changed_nodes); } @@ -221,6 +221,9 @@ std::tuple DomNode::GetLayoutSize() { } void DomNode::SetLayoutSize(float width, float height) { + is_layout_width_nan_ = std::isnan(width) ? true : false; + is_layout_height_nan_ = std::isnan(height) ? true : false; + layout_node_->SetWidth(width); layout_node_->SetHeight(height); } diff --git a/dom/src/dom/layer_optimized_render_manager.cc b/dom/src/dom/layer_optimized_render_manager.cc index 3c744dc0010..1ce6cadc28d 100644 --- a/dom/src/dom/layer_optimized_render_manager.cc +++ b/dom/src/dom/layer_optimized_render_manager.cc @@ -88,8 +88,10 @@ void LayerOptimizedRenderManager::UpdateRenderNode(std::weak_ptr root_ } } } - FOOTSTONE_DLOG(INFO) << "[Hippy Statistic] update node size before optimize = " << nodes.size() - << ", update node size after optimize = " << nodes_to_update.size(); + if (!footstone::gInUpdateAnimScope || footstone::gEnableUpdateAnimLog) { + FOOTSTONE_DLOG(INFO) << "[Hippy Statistic] update node size before optimize = " << nodes.size() + << ", update node size after optimize = " << nodes_to_update.size(); + } if (!nodes_to_update.empty()) { render_manager_->UpdateRenderNode(root_node, std::move(nodes_to_update)); } diff --git a/dom/src/dom/root_node.cc b/dom/src/dom/root_node.cc index 0c660158a41..d3b950748ad 100644 --- a/dom/src/dom/root_node.cc +++ b/dom/src/dom/root_node.cc @@ -460,7 +460,11 @@ std::shared_ptr RootNode::GetNode(uint32_t id) { std::tuple RootNode::GetRootSize() { return GetLayoutSize(); } -void RootNode::SetRootSize(float width, float height) { SetLayoutSize(width, height); } +void RootNode::SetRootSize(float width, float height) { + if (!disable_set_root_size_) { + SetLayoutSize(width, height); + } +} void RootNode::SetRootOrigin(float x, float y) { SetLayoutOrigin(x, y); } @@ -575,5 +579,16 @@ void RootNode::Traverse(const std::function& } } +std::vector> RootNode::GetAllTextNodes() { + std::vector> textNodes; + for (auto it = nodes_.begin(); it != nodes_.end(); it++) { + auto node = it->second.lock(); + if (node && node->GetViewName() == "Text") { + textNodes.emplace_back(node); + } + } + return textNodes; +} + } // namespace dom } // namespace hippy diff --git a/dom/src/dom/yoga_layout_node.cc b/dom/src/dom/yoga_layout_node.cc index 2d4a4d37bc3..87929a8847c 100644 --- a/dom/src/dom/yoga_layout_node.cc +++ b/dom/src/dom/yoga_layout_node.cc @@ -288,6 +288,10 @@ void YogaLayoutNode::SetWidth(float width) { YGNodeStyleSetWidth(yoga_node_, wid void YogaLayoutNode::SetHeight(float height) { YGNodeStyleSetHeight(yoga_node_, height); } +void YogaLayoutNode::SetMaxWidth(float width) { YGNodeStyleSetMaxWidth(yoga_node_, width); } + +void YogaLayoutNode::SetMaxHeight(float height) { YGNodeStyleSetMaxHeight(yoga_node_, height); } + void YogaLayoutNode::SetScaleFactor(float scale_factor) { YGConfigSetPointScaleFactor(yoga_config_, scale_factor); } static LayoutMeasureMode ToLayoutMeasureMode(YGMeasureMode measure_mode) { @@ -307,6 +311,7 @@ static YGSize YGMeasureFunction(YGNodeRef node, float width, YGMeasureMode width YGMeasureMode height_mode) { auto yoga_node = reinterpret_cast(YGNodeGetContext(node)); int64_t key = yoga_node->GetKey(); + std::lock_guard lock(mutex); auto iter = measure_function_map.find(key); if (iter != measure_function_map.end()) { auto size = iter->second(width, ToLayoutMeasureMode(width_mode), height, ToLayoutMeasureMode(height_mode), nullptr); @@ -319,12 +324,17 @@ static YGSize YGMeasureFunction(YGNodeRef node, float width, YGMeasureMode width } void YogaLayoutNode::SetMeasureFunction(MeasureFunction measure_function) { + std::lock_guard lock(mutex); measure_function_map[key_] = measure_function; YGNodeSetContext(yoga_node_, reinterpret_cast(this)); return YGNodeSetMeasureFunc(yoga_node_, YGMeasureFunction); } -bool YogaLayoutNode::HasMeasureFunction() { return measure_function_map.find(key_) != measure_function_map.end(); } +bool YogaLayoutNode::HasMeasureFunction() { + std::lock_guard lock(mutex); + return measure_function_map.find(key_) != measure_function_map.end(); +} + float YogaLayoutNode::GetLeft() { return YGNodeLayoutGetLeft(yoga_node_); } float YogaLayoutNode::GetTop() { return YGNodeLayoutGetTop(yoga_node_); } @@ -467,7 +477,8 @@ void YogaLayoutNode::Parser( if (it != style_delete.end()) YGNodeStyleSetFlexShrink(yoga_node_, 0); } if (style_update.find(kFlexBasis) != style_update.end()) { - SetFlexBasis(static_cast(style_update.find(kFlexBasis)->second->ToDoubleChecked())); + auto dom_value = style_update.find(kFlexBasis)->second; + SetYGFlexBasis(dom_value); } else { auto it = std::find(style_delete.begin(), style_delete.end(), kFlexBasis); if (it != style_delete.end()) YGNodeStyleSetFlexBasis(yoga_node_, NAN); @@ -694,11 +705,13 @@ void YogaLayoutNode::Parser( if (it != style_delete.end()) YGNodeStyleSetAspectRatio(yoga_node_, 0); } - // if (style_update.find(kAlignContent) != style_update.end()) { - // SetAlignContent(GetFlexAlign(style_update.find(kAlignContent)->second->ToString())); - // } + if (style_update.find(kAlignContent) != style_update.end()) { + SetAlignContent(GetFlexAlign(style_update.find(kAlignContent)->second->ToStringChecked())); + } } +YG_SET_NUMBER_PERCENT_AUTO_DECL(FlexBasis) + YG_SET_NUMBER_PERCENT_AUTO_DECL(Width) YG_SET_NUMBER_PERCENT_AUTO_DECL(Height) @@ -713,8 +726,6 @@ YG_SET_NUMBER_PERCENT_DECL(MinHeight) void YogaLayoutNode::SetDirection(YGDirection direction) { YGNodeStyleSetDirection(yoga_node_, direction); } -void YogaLayoutNode::SetFlexBasis(float flex_basis) { YGNodeStyleSetFlexBasis(yoga_node_, flex_basis); } - void YogaLayoutNode::SetFlex(float flex) { YGNodeStyleSetFlex(yoga_node_, flex); } void YogaLayoutNode::SetFlexGrow(float flex_grow) { YGNodeStyleSetFlexGrow(yoga_node_, flex_grow); } @@ -741,8 +752,8 @@ void YogaLayoutNode::SetFlexWrap(YGWrap wrap_mode) { YGNodeStyleSetFlexWrap(yoga void YogaLayoutNode::SetJustifyContent(YGJustify justify) { YGNodeStyleSetJustifyContent(yoga_node_, justify); } -// void YogaLayoutNode::SetAlignContent(YGAlign align_content) { YGNodeStyleSetAlignContent(yoga_node_, align_content); -// } +void YogaLayoutNode::SetAlignContent(YGAlign align_content) { YGNodeStyleSetAlignContent(yoga_node_, align_content); +} void YogaLayoutNode::SetAlignItems(YGAlign align_items) { YGNodeStyleSetAlignItems(yoga_node_, align_items); } diff --git a/driver/js/CMakeLists.txt b/driver/js/CMakeLists.txt index 892b5502695..35b3befaacd 100644 --- a/driver/js/CMakeLists.txt +++ b/driver/js/CMakeLists.txt @@ -30,6 +30,16 @@ include("${PROJECT_ROOT_DIR}/buildconfig/cmake/compiler_toolchain.cmake") set(CMAKE_VERBOSE_MAKEFILE on) set(CMAKE_CXX_STANDARD 17) +# Ohos系统上,配置本来应该放到生成so对应的build-profile.json5里。 +#(externalNativeOptions里arguments项) +# 现在源码集成配置需在app的build-profile.json5里,不方便,所以先放在这里。 +# 后续hippy产物的独立后,放到hippy模块的build-profile.json5里。 +if (OHOS) +# set(JS_ENGINE "V8") + set(V8_COMPONENT "10.6.194") + set(JS_ENGINE "JSH") +endif () + # region library add_library(${PROJECT_NAME} STATIC) target_include_directories(${PROJECT_NAME} PUBLIC include) @@ -60,6 +70,8 @@ if ("${JS_ENGINE}" STREQUAL "V8") set(V8_REMOTE_FILENAME "windows-x64.zip") endif() endif() + elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "OHOS") + set(V8_REMOTE_FILENAME "ohos-arm64.tgz") elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "iOS") if ("${CMAKE_OSX_ARCHITECTURES}" MATCHES "arm") set(V8_REMOTE_FILENAME "ios-arm64.tgz") @@ -115,30 +127,52 @@ if ("${JS_ENGINE}" STREQUAL "V8") endif () elseif ("${JS_ENGINE}" STREQUAL "JSC") target_compile_definitions(${PROJECT_NAME} PUBLIC "JS_JSC") +elseif ("${JS_ENGINE}" STREQUAL "JSH") + target_compile_definitions(${PROJECT_NAME} PUBLIC "JS_JSH") endif () # endregion # region footstone -GlobalPackages_Add(footstone) +if (OHOS) + GlobalPackages_Add_footstone() +else () + GlobalPackages_Add(footstone) +endif () target_link_libraries(${PROJECT_NAME} PRIVATE footstone) # endregion # region vfs -GlobalPackages_Add(vfs) +if (OHOS) + GlobalPackages_Add_vfs() +else () + GlobalPackages_Add(vfs) +endif () # Just reference the `dom` header files, no library needed target_include_directories(${PROJECT_NAME} PRIVATE $) # endregion # region dom -GlobalPackages_Add(dom) +if (OHOS) + GlobalPackages_Add_dom() +else () + GlobalPackages_Add(dom) +endif () target_link_libraries(${PROJECT_NAME} PRIVATE dom) # endregion # region devtools if (ENABLE_INSPECTOR) +if (OHOS) + GlobalPackages_Add_devtools_backend() +else () GlobalPackages_Add(devtools_backend) +endif () target_link_libraries(${PROJECT_NAME} PUBLIC devtools_backend) +if (OHOS) + GlobalPackages_Add_devtools_integration() +else () GlobalPackages_Add(devtools_integration) +endif () target_link_libraries(${PROJECT_NAME} PUBLIC devtools_integration) endif () # endregion @@ -174,6 +208,9 @@ if ("${JS_ENGINE}" STREQUAL "V8") src/vm/v8/native_source_code_android.cc src/vm/v8/serializer.cc src/vm/v8/v8_vm.cc) + + set_source_files_properties(src/vm/v8/serializer.cc PROPERTIES COMPILE_FLAGS -fno-rtti) + if (NOT V8_WITHOUT_INSPECTOR) list(APPEND SOURCE_SET src/vm/v8/inspector/v8_channel_impl.cc @@ -186,6 +223,13 @@ elseif ("${JS_ENGINE}" STREQUAL "JSC") src/napi/jsc/jsc_try_catch.cc src/vm/jsc/jsc_vm.cc src/vm/jsc/native_source_code_ios.cc) +elseif ("${JS_ENGINE}" STREQUAL "JSH") + list(APPEND SOURCE_SET + src/napi/jsh/jsh_class_definition.cc + src/napi/jsh/jsh_ctx.cc + src/napi/jsh/jsh_try_catch.cc + src/vm/jsh/jsh_vm.cc + src/vm/jsh/native_source_code_ohos.cc) endif () set(SOURCE_SET_STANDALONE src/modules/console_module.cc diff --git a/driver/js/examples/hippy-react-demo/package.json b/driver/js/examples/hippy-react-demo/package.json index e3225b0130c..50d2737107d 100644 --- a/driver/js/examples/hippy-react-demo/package.json +++ b/driver/js/examples/hippy-react-demo/package.json @@ -12,10 +12,12 @@ "serve": "node ./scripts/env-polyfill.js webpack serve --config ./scripts/hippy-webpack.web.dev.js", "build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.web.js", "hippy:dev": "node ./scripts/env-polyfill.js hippy-dev -c ./scripts/hippy-webpack.dev.js", - "hippy:vendor": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ios-vendor.js --config ./scripts/hippy-webpack.android-vendor.js", - "hippy:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ios.js --config ./scripts/hippy-webpack.android.js", + "hippy:vendor": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ios-vendor.js --config ./scripts/hippy-webpack.android-vendor.js --config ./scripts/hippy-webpack.ohos-vendor.js", + "hippy:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ios.js --config ./scripts/hippy-webpack.android.js --config ./scripts/hippy-webpack.ohos.js", "web:dev": "npm run hippy:dev & node ./scripts/env-polyfill.js webpack serve --config ./scripts/hippy-webpack.web-renderer.dev.js", - "web:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.web-renderer.js" + "web:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.web-renderer.js", + "ohos:vendor": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ohos-vendor.js", + "ohos:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ohos.js" }, "keywords": [ "Hippy", diff --git a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ohos-vendor.js b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ohos-vendor.js new file mode 100644 index 00000000000..be251153f42 --- /dev/null +++ b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ohos-vendor.js @@ -0,0 +1,79 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); + +const platform = 'ohos'; + +module.exports = { + mode: 'production', + bail: true, + entry: { + vendor: [path.resolve(__dirname, './vendor.js')], + }, + output: { + filename: `[name].${platform}.js`, + path: path.resolve(`./dist/${platform}/`), + globalObject: '(0, eval)("this")', + library: 'hippyReactBase', + }, + plugins: [ + new webpack.NamedModulesPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('production'), + __PLATFORM__: JSON.stringify(platform), + }), + new CaseSensitivePathsPlugin(), + new webpack.DllPlugin({ + context: path.resolve(__dirname, '..'), + path: path.resolve(__dirname, `../dist/${platform}/[name]-manifest.json`), + name: 'hippyReactBase', + }), + ], + module: { + rules: [ + { + test: /\.(js)$/, + use: [ + { + loader: 'babel-loader', + options: { + presets: [ + [ + '@babel/preset-env', + { + targets: { + chrome: 57, + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-class-properties'], + ], + }, + }, + ], + }, + ], + }, + resolve: { + extensions: ['.js', '.jsx', '.json'], + // if node_modules path listed below is not your repo directory, change it. + modules: [path.resolve(__dirname, '../node_modules')], + alias: (() => { + const aliases = {}; + // If hippy-react was built exist then make a alias + // Remove the section if you don't use it + const hippyReactPath = path.resolve(__dirname, '../../../packages/hippy-react'); + if (fs.existsSync(path.resolve(hippyReactPath, 'dist/index.js'))) { + console.warn(`* Using the @hippy/react in ${hippyReactPath}`); + aliases['@hippy/react'] = hippyReactPath; + } else { + console.warn('* Using the @hippy/react defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ohos.js b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ohos.js new file mode 100644 index 00000000000..105c494fdb2 --- /dev/null +++ b/driver/js/examples/hippy-react-demo/scripts/hippy-webpack.ohos.js @@ -0,0 +1,112 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const HippyDynamicImportPlugin = require('@hippy/hippy-dynamic-import-plugin'); +const pkg = require('../package.json'); +const manifest = require('../dist/ohos/vendor-manifest.json'); + +const platform = 'ohos'; + +module.exports = { + mode: 'production', + bail: true, + entry: { + index: ['regenerator-runtime', path.resolve(pkg.main)], + }, + output: { + filename: `[name].${platform}.js`, + path: path.resolve(`./dist/${platform}/`), + globalObject: '(0, eval)("this")', + // CDN path can be configured to load children bundles from remote server + // publicPath: 'https://xxx/hippy/hippyReactDemo/', + }, + plugins: [ + new webpack.NamedModulesPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('production'), + __PLATFORM__: JSON.stringify(platform), + }), + new CaseSensitivePathsPlugin(), + new webpack.DllReferencePlugin({ + context: path.resolve(__dirname, '..'), + manifest, + }), + new HippyDynamicImportPlugin(), + // LimitChunkCountPlugin can control dynamic import ability + // Using 1 will prevent any additional chunks from being added + // new webpack.optimize.LimitChunkCountPlugin({ + // maxChunks: 1, + // }), + // use SourceMapDevToolPlugin can generate sourcemap file + // new webpack.SourceMapDevToolPlugin({ + // test: /\.(js|jsbundle|css|bundle)($|\?)/i, + // filename: '[file].map', + // }), + ], + module: { + rules: [ + { + test: /\.(jsx?)$/, + use: [ + { + loader: 'babel-loader', + options: { + sourceType: 'unambiguous', + presets: [ + '@babel/preset-react', + [ + '@babel/preset-env', + { + targets: { + chrome: 57, + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-class-properties'], + ['@babel/plugin-proposal-decorators', { legacy: true }], + ['@babel/plugin-transform-runtime', { regenerator: true }], + ], + }, + }, + ], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [{ + loader: 'url-loader', + options: { + // if you would like to use base64 for picture, uncomment limit: true + // limit: true, + limit: 8192, + fallback: 'file-loader', + name: '[name].[ext]', + outputPath: 'assets/', + }, + }], + }, + ], + }, + resolve: { + extensions: ['.js', '.jsx', '.json'], + // if node_modules path listed below is not your repo directory, change it. + modules: [path.resolve(__dirname, '../node_modules')], + alias: (() => { + const aliases = {}; + + // If hippy-react was built exist then make a alias + // Remove the section if you don't use it + const hippyReactPath = path.resolve(__dirname, '../../../packages/hippy-react'); + if (fs.existsSync(path.resolve(hippyReactPath, 'dist/index.js'))) { + console.warn(`* Using the @hippy/react in ${hippyReactPath}`); + aliases['@hippy/react'] = hippyReactPath; + } else { + console.warn('* Using the @hippy/react defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/driver/js/examples/hippy-vue-demo/package.json b/driver/js/examples/hippy-vue-demo/package.json index caef249c2b0..55beadb9e50 100644 --- a/driver/js/examples/hippy-vue-demo/package.json +++ b/driver/js/examples/hippy-vue-demo/package.json @@ -10,10 +10,12 @@ "repository": "https://github.com/Tencent/Hippy/tree/master/examples/hippy-vue-demo", "scripts": { "hippy:dev": "node ./scripts/env-polyfill.js hippy-dev -c ./scripts/hippy-webpack.dev.js", - "hippy:vendor": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ios-vendor.js --config ./scripts/hippy-webpack.android-vendor.js", - "hippy:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ios.js --config ./scripts/hippy-webpack.android.js", + "hippy:vendor": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ohos-vendor.js", + "hippy:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ohos.js", "web:dev": "npm run hippy:dev & node ./scripts/env-polyfill.js webpack serve --config ./scripts/hippy-webpack.web-renderer.dev.js", - "web:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.web-renderer.js" + "web:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.web-renderer.js", + "ohos:vendor": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ohos-vendor.js", + "ohos:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ohos.js" }, "dependencies": { "@hippy/vue": "3.3.1-rc.1", diff --git a/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ohos-vendor.js b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ohos-vendor.js new file mode 100644 index 00000000000..d1158ae17a3 --- /dev/null +++ b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ohos-vendor.js @@ -0,0 +1,131 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); + +const platform = 'ohos'; + +let vueLoader = '@hippy/vue-loader'; +let VueLoaderPlugin; +const hippyVueLoaderPath = path.resolve(__dirname, '../../../packages/hippy-vue-loader/lib'); +const hippyVueLoaderNodeModulesPath = path.resolve(__dirname, '../../../packages/hippy-vue-loader/node_modules'); +if (fs.existsSync(hippyVueLoaderNodeModulesPath) && fs.existsSync(hippyVueLoaderPath)) { + console.warn(`* Using the @hippy/vue-loader in ${hippyVueLoaderPath}`); + vueLoader = hippyVueLoaderPath; + VueLoaderPlugin = require(path.resolve(__dirname, '../../../packages/hippy-vue-loader/lib/plugin')); +} else { + console.warn('* Using the @hippy/vue-loader defined in package.json'); + VueLoaderPlugin = require('@hippy/vue-loader/lib/plugin'); +} + +module.exports = { + mode: 'production', + bail: true, + entry: { + vendor: [path.resolve(__dirname, './vendor.js')], + }, + output: { + filename: `[name].${platform}.js`, + path: path.resolve(`./dist/${platform}/`), + globalObject: '(0, eval)("this")', + library: 'hippyVueBase', + }, + plugins: [ + new webpack.NamedModulesPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('production'), + __PLATFORM__: JSON.stringify(platform), + }), + new CaseSensitivePathsPlugin(), + new VueLoaderPlugin(), + new webpack.DllPlugin({ + context: path.resolve(__dirname, '..'), + path: path.resolve(__dirname, `../dist/${platform}/[name]-manifest.json`), + name: 'hippyVueBase', + }), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: vueLoader, + options: { + compilerOptions: { + // whitespace handler, default is 'preserve' + whitespace: 'condense', + }, + }, + }, + ], + }, + { + test: /\.(js)$/, + use: [ + { + loader: 'babel-loader', + options: { + presets: [ + [ + '@babel/preset-env', + { + targets: { + chrome: 57, + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-class-properties'], + ], + }, + }, + ], + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + // if node_modules path listed below is not your repo directory, change it. + modules: [path.resolve(__dirname, '../node_modules')], + alias: (() => { + const aliases = { + vue: '@hippy/vue', + '@': path.resolve('./src'), + 'vue-router': '@hippy/vue-router', + }; + // If hippy-vue was built exist then make a alias + // Remove the section if you don't use it + const hippyVuePath = path.resolve(__dirname, '../../../packages/hippy-vue'); + if (fs.existsSync(path.resolve(hippyVuePath, 'dist/index.js'))) { + console.warn(`* Using the @hippy/vue in ${hippyVuePath} as vue alias`); + aliases.vue = hippyVuePath; + aliases['@hippy/vue'] = hippyVuePath; + } else { + console.warn('* Using the @hippy/vue defined in package.json'); + } + // If hippy-vue-router was built exist then make a alias + // Remove the section if you don't use it + const hippyVueRouterPath = path.resolve(__dirname, '../../../packages/hippy-vue-router'); + if (fs.existsSync(path.resolve(hippyVueRouterPath, 'dist/index.js'))) { + console.warn(`* Using the @hippy/vue-router in ${hippyVueRouterPath} as vue-router alias`); + aliases['vue-router'] = hippyVueRouterPath; + } else { + console.warn('* Using the @hippy/vue-router defined in package.json'); + } + + // If hippy-vue-native-components was built exist then make a alias + // Remove the section if you don't use it + const hippyVueNativeComponentsPath = path.resolve(__dirname, '../../../packages/hippy-vue-native-components'); + if (fs.existsSync(path.resolve(hippyVueNativeComponentsPath, 'dist/index.js'))) { + console.warn(`* Using the @hippy/vue-native-components in ${hippyVueNativeComponentsPath}`); + aliases['@hippy/vue-native-components'] = hippyVueNativeComponentsPath; + } else { + console.warn('* Using the @hippy/vue-native-components defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ohos.js b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ohos.js new file mode 100644 index 00000000000..3775a177b9d --- /dev/null +++ b/driver/js/examples/hippy-vue-demo/scripts/hippy-webpack.ohos.js @@ -0,0 +1,178 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const HippyDynamicImportPlugin = require('@hippy/hippy-dynamic-import-plugin'); +const pkg = require('../package.json'); +const manifest = require('../dist/ohos/vendor-manifest.json'); + +const platform = 'ohos'; +let cssLoader = '@hippy/vue-css-loader'; +const hippyVueCssLoaderPath = path.resolve(__dirname, '../../../packages/hippy-vue-css-loader/dist/css-loader.js'); +if (fs.existsSync(hippyVueCssLoaderPath)) { + console.warn(`* Using the @hippy/vue-css-loader in ${hippyVueCssLoaderPath}`); + cssLoader = hippyVueCssLoaderPath; +} else { + console.warn('* Using the @hippy/vue-css-loader defined in package.json'); +} + +let vueLoader = '@hippy/vue-loader'; +let VueLoaderPlugin; +const hippyVueLoaderPath = path.resolve(__dirname, '../../../packages/hippy-vue-loader/lib'); +const hippyVueLoaderNodeModulesPath = path.resolve(__dirname, '../../../packages/hippy-vue-loader/node_modules'); +if (fs.existsSync(hippyVueLoaderNodeModulesPath) && fs.existsSync(hippyVueLoaderPath)) { + console.warn(`* Using the @hippy/vue-loader in ${hippyVueLoaderPath}`); + vueLoader = hippyVueLoaderPath; + VueLoaderPlugin = require(path.resolve(__dirname, '../../../packages/hippy-vue-loader/lib/plugin')); +} else { + console.warn('* Using the @hippy/vue-loader defined in package.json'); + VueLoaderPlugin = require('@hippy/vue-loader/lib/plugin'); +} + +module.exports = { + mode: 'production', + bail: true, + entry: { + index: [path.resolve(pkg.nativeMain)], + }, + output: { + filename: `[name].${platform}.js`, + path: path.resolve(`./dist/${platform}/`), + globalObject: '(0, eval)("this")', + // CDN path can be configured to load children bundles from remote server + // publicPath: 'https://xxx/hippy/hippyVueDemo/', + }, + plugins: [ + new webpack.NamedModulesPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('production'), + __PLATFORM__: JSON.stringify(platform), + }), + new CaseSensitivePathsPlugin(), + new VueLoaderPlugin(), + new webpack.DllReferencePlugin({ + context: path.resolve(__dirname, '..'), + manifest, + }), + new HippyDynamicImportPlugin(), + // LimitChunkCountPlugin can control dynamic import ability + // Using 1 will prevent any additional chunks from being added + // new webpack.optimize.LimitChunkCountPlugin({ + // maxChunks: 1, + // }), + // use SourceMapDevToolPlugin can generate sourcemap file + // new webpack.SourceMapDevToolPlugin({ + // test: /\.(js|jsbundle|css|bundle)($|\?)/i, + // filename: '[file].map', + // }), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: vueLoader, + options: { + compilerOptions: { + // whitespace handler, default is 'preserve' + whitespace: 'condense', + }, + }, + }, + ], + }, + { + test: /\.css$/, + use: [ + cssLoader, + ], + }, + { + test: /\.(js)$/, + use: [ + { + loader: 'babel-loader', + options: { + sourceType: 'unambiguous', + presets: [ + [ + '@babel/preset-env', + { + targets: { + chrome: 57, + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-class-properties'], + ['@babel/plugin-proposal-decorators', { legacy: true }], + ['@babel/plugin-transform-runtime', { regenerator: true }], + ], + }, + }, + ], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [{ + loader: 'url-loader', + options: { + // if you would like to use base64 for picture, uncomment limit: true + // limit: true, + limit: 8192, + fallback: 'file-loader', + name: '[name].[ext]', + outputPath: 'assets/', + }, + }], + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + // if node_modules path listed below is not your repo directory, change it. + modules: [path.resolve(__dirname, '../node_modules')], + alias: (() => { + const aliases = { + vue: '@hippy/vue', + '@': path.resolve('./src'), + 'vue-router': '@hippy/vue-router', + }; + + // If hippy-vue was built exist in packages directory then make a alias + // Remove the section if you don't use it + const hippyVuePath = path.resolve(__dirname, '../../../packages/hippy-vue'); + if (fs.existsSync(path.resolve(hippyVuePath, 'dist/index.js'))) { + console.warn(`* Using the @hippy/vue in ${hippyVuePath} as vue alias`); + aliases.vue = hippyVuePath; + aliases['@hippy/vue'] = hippyVuePath; + } else { + console.warn('* Using the @hippy/vue defined in package.json'); + } + + // If hippy-vue-router was built exist in packages directory then make a alias + // Remove the section if you don't use it + const hippyVueRouterPath = path.resolve(__dirname, '../../../packages/hippy-vue-router'); + if (fs.existsSync(path.resolve(hippyVueRouterPath, 'dist/index.js'))) { + console.warn(`* Using the @hippy/vue-router in ${hippyVueRouterPath} as vue-router alias`); + aliases['vue-router'] = hippyVueRouterPath; + } else { + console.warn('* Using the @hippy/vue-router defined in package.json'); + } + + // If hippy-vue-native-components was built in packages directory exist then make a alias + // Remove the section if you don't use it + const hippyVueNativeComponentsPath = path.resolve(__dirname, '../../../packages/hippy-vue-native-components'); + if (fs.existsSync(path.resolve(hippyVueNativeComponentsPath, 'dist/index.js'))) { + console.warn(`* Using the @hippy/vue-native-components in ${hippyVueNativeComponentsPath}`); + aliases['@hippy/vue-native-components'] = hippyVueNativeComponentsPath; + } else { + console.warn('* Using the @hippy/vue-native-components defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/driver/js/examples/hippy-vue-demo/src/components/demos/demo-dynamicimport.vue b/driver/js/examples/hippy-vue-demo/src/components/demos/demo-dynamicimport.vue index 0afaf20f342..dc21bf736c7 100644 --- a/driver/js/examples/hippy-vue-demo/src/components/demos/demo-dynamicimport.vue +++ b/driver/js/examples/hippy-vue-demo/src/components/demos/demo-dynamicimport.vue @@ -40,7 +40,7 @@ export default { * customChunkPath 会在运行时替换全局配置的publicPath * import 出错时需在catch里做对应的降级方案 */ - AsyncComponentFromHttp: process.env.NODE_ENV === 'development' ? () => import(/* webpackMode: "lazy", webpackChunkName: "asyncComponentFromHttp" */'./dynamicImport/async-component-http.vue').then(res => res).catch(err => console.error('import async remote component error', err)) : () => import(/* webpackMode: "lazy",customChunkPath: "https://raw.githubusercontent.com/Tencent/Hippy/master/static/hippy-vue/", webpackChunkName: "asyncComponentFromHttp" */'./dynamicImport/async-component-http.vue').then(res => res).catch(err => console.error('import async remote component error', err)), + AsyncComponentFromHttp: process.env.NODE_ENV === 'development' ? () => import(/* webpackMode: "lazy", webpackChunkName: "asyncComponentFromHttp" */'./dynamicImport/async-component-http.vue').then(res => res).catch(err => console.error('import async remote component error', err)) : () => import(/* webpackMode: "lazy",customChunkPath: "https://raw.githubusercontent.com/sohotz/Hippy/main/driver/js/static/hippy-vue/", webpackChunkName: "asyncComponentFromHttp" */'./dynamicImport/async-component-http.vue').then(res => res).catch(err => console.error('import async remote component error', err)), }, data() { return { 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 24d2a913cb2..58a2501ee2a 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 @@ -108,6 +108,40 @@

+ +
+
+

+ 切换方向 +

+
+
+ diff --git a/driver/js/examples/hippy-vue-demo/src/components/demos/demo-shadow.vue b/driver/js/examples/hippy-vue-demo/src/components/demos/demo-shadow.vue index 3dc24eb8dde..c474f11dba4 100644 --- a/driver/js/examples/hippy-vue-demo/src/components/demos/demo-shadow.vue +++ b/driver/js/examples/hippy-vue-demo/src/components/demos/demo-shadow.vue @@ -1,5 +1,6 @@