diff --git a/docs/en-us/hippy-vue/vue3.md b/docs/en-us/hippy-vue/vue3.md
index c6f2591b5aa..33421a1d93f 100644
--- a/docs/en-us/hippy-vue/vue3.md
+++ b/docs/en-us/hippy-vue/vue3.md
@@ -155,6 +155,253 @@ const router = createRouter({
});
```
+
+# Custom Components & Modules
+
+In @hippy/vue-next, the `registerElement` method is also available for registering custom components and mapping tags in the template to native components.
+It is worth noting that, similar to Native, in @hippy/vue, the `registerElement` method is attached to the global Vue object.
+Similarly, in @hippy/vue-next, the `registerElement` method is also exported separately.
+
+```javascript
+import { registerElement } from '@hippy/vue-next';
+```
+
+## Register Custom Component
+
+```javascript
+// custom-tag.ts
+import { registerElement } from '@hippy/vue-next'
+
+/**
+ * register custom tag
+ */
+export function registerCustomTag(): void {
+ // native component name
+ const nativeComponentName = 'CustomTagView'
+ // custom tag name
+ const htmlTagName = 'h-custom-tag'
+ // register native custom component named "CustomTagView", native component name must same with native real name.
+ // this method establish mapping between our "h-custom-tag" to native "CustomTagView"
+ registerElement(htmlTagName, {
+ component: {
+ name: nativeComponentName
+ }
+ })
+}
+
+// app.ts
+import { defineComponent, ref } from 'vue';
+import { type HippyApp, createApp } from '@hippy/vue-next';
+import { registerCustomTag } from './custom-tag'
+
+// register
+registerCustomTag()
+
+// create hippy app instance
+const app: HippyApp = createApp(defineComponent({
+ setup() {
+ const counter = ref(0);
+ return {
+ counter,
+ }
+ }
+}), {
+ // Hippy App Name, required, use demo for test
+ appName: 'Demo',
+});
+
+// ...other code
+
+```
+
+## Binding Native Event Return Values
+
+Because @hippy/vue-next adopts a consistent event model with the browser and aims to unify events on both ends (sometimes the return values of events may differ),
+a solution was implemented to manually modify the event return values. This requires explicitly declaring the return values for each event.
+This step is handled during the registration of custom components using the `processEventData` method, which takes two parameters.
+
+- evtData Include event instance `handler` and event name `__evt`
+- nativeEventParams native event real return values
+
+Eg: @hippy/vue-next's [swiper](https://github.com/Tencent/Hippy/blob/master/packages/hippy-vue-next/src/native-component/swiper.ts) native component,
+it was the real rendered node by `swiper` that handle the event return values
+
+```javascript
+ // register swiper tag
+ registerElement('hi-swiper', {
+ component: {
+ name: 'ViewPager', // native component name
+ processEventData(
+ evtData: EventsUnionType,
+ nativeEventParams: { [key: string]: NeedToTyped },
+ ) {
+ // handler: event instance,__evt: native event name
+ const { handler: event, __evt: nativeEventName } = evtData;
+ switch (nativeEventName) {
+ case 'onPageSelected':
+ // Explicitly assigning the value of nativeEventParams from the native event to the event bound to the event in @hippy/vue-next
+ // This way, the event parameters received in the pageSelected event of the swiper component will include currentSlide.
+ event.currentSlide = nativeEventParams.position;
+ break;
+ case 'onPageScroll':
+ event.nextSlide = nativeEventParams.position;
+ event.offset = nativeEventParams.offset;
+ break;
+ case 'onPageScrollStateChanged':
+ event.state = nativeEventParams.pageScrollState;
+ break;
+ default:
+ }
+ return event;
+ },
+ },
+ });
+```
+
+## Use `Vue` Component Implement Custom Component
+
+When your custom component involves more complex interactions, events, and lifecycle methods, simply using `registerElement` may not be sufficient.
+It can only achieve basic mapping of element names to components and basic parameter mapping. In such cases, you can use Vue to register separate
+components to implement this complex custom component. For information on registering components in Vue, you can refer to the [Component Registration](https://cn.vuejs.org/guide/components/registration.html) guide.
+Please note that there are some differences in component registration between Vue 3 and Vue 2.
+You can also refer to the implementation of [swiper](https://github.com/Tencent/Hippy/blob/master/packages/hippy-vue-next/src/native-component/swiper.ts) components in the @hippy/vue library
+
+### Event Handle
+
+When using components registered with Vue, if you want to pass terminal events to the outer component, you need to handle it differently.
+There are two ways to achieve this.
+
+- Use `render` Function(Recommend)
+
+```javascript
+import { createApp } from 'vue'
+
+const vueApp = createApp({})
+
+// notice Vue3 register component isn't global now
+vueApp.component('Swiper', {
+ // ... other code
+ render() {
+ /*
+ * Use "render" function
+ * "pageScroll" is the event name passed to native(automaticlly transform to "onPageScroll")
+ * "dragging" is the event name user used
+ */
+ const on = getEventRedirects.call(this, [
+ ['dropped', 'pageSelected'],
+ ['dragging', 'pageScroll'],
+ ['stateChanged', 'pageScrollStateChanged'],
+ ]);
+
+ return h(
+ 'hi-swiper',
+ {
+ ...on,
+ ref: 'swiper',
+ initialPage: this.$initialSlide,
+ },
+ this.$slots.default ? this.$slots.default() : null,
+ );
+ },
+});
+
+// register native custom component "ViewPager"
+registerElement('hi-swiper', {
+ component: {
+ name: 'ViewPager',
+ },
+});
+```
+
+
+- Use Vue `SFC`
+
+```javascript
+// swiper.vue
+
+
+
+
+
+
+
+// app.ts
+import { registerElement } from '@hippy/vue-next'
+import { createApp } from 'vue'
+import Swiper from './swiper.vue'
+
+// register custom native component
+registerElement('hi-swiper', {
+ component: {
+ name: 'ViewPager',
+ },
+});
+
+// create vue instance
+const vueApp = createApp({})
+// register vue component
+vueApp.component('Swiper', Swiper)
+```
+
+> When registering a custom tag using the Single File Component (SFC) approach, Vue treats it as a component. However, if the component is not explicitly registered,
+> it will result in an error. Therefore, we need to use isCustomElement to inform Vue that this is our [custom component](https://cn.vuejs.org/api/application.html#app-config-compileroptions-iscustomelement),
+> just render directly.
+> Attention, hippy-webpack.dev.js, hippy-webpack.android.js, hippy-webpack.ios.js both need to be handled, first by development builds and other for production builds.
+
+```javascript
+// src/scripts/hippy-webpack.dev.js & src/scripts/hippy-webpack.android.js & src/scripts/hippy-webpack.ios.js both need to be handled
+
+/**
+ * determine tag is custom tag or not, should handle by your project
+ */
+function isCustomTag(tag) {
+ return tag === 'hi-swiper'
+}
+
+// vue loader part
+{
+ test: /\.vue$/,
+ use: [
+ {
+ loader: 'vue-loader',
+ options: {
+ compilerOptions: {
+ // disable vue3 dom patch flag,because hippy do not support innerHTML
+ hoistStatic: false,
+ // whitespace handler, default is 'condense', it can be set 'preserve'
+ whitespace: 'condense',
+ // register custom element that won't transform as Vue component
+ isCustomElement: tag => isCustomTag(tag)
+ },
+ },
+ },
+ ],
+},
+```
+
# Additional Differences
@hippy/vue-next is basically functionally aligned with @hippy/vue now, but the APIs are slightly different from @hippy/vue, and there are still some problems that have not been solved, here is the description:
@@ -169,14 +416,6 @@ const router = createRouter({
console.log('do somethig', Native.xxx)
```
-- registerElement
-
- In @hippy/vue, method `registerElement` used by Vue.registerElement,But with the same reason with Vue.Native, `registerElement` method in @hippy/vue-next needs exported from @hippy/vue-next .
-
- ```javascript
- import { registerElement } from '@hippy/vue-next';
- ```
-
- Global Event
In @hippy/vue,global event used by `Vue.$on` or `Vue.$off`,now in @hippy/vue-next,we provide `EventBus` to do that.
diff --git a/docs/hippy-vue/vue3.md b/docs/hippy-vue/vue3.md
index c04d14abff6..3661a3b91d0 100644
--- a/docs/hippy-vue/vue3.md
+++ b/docs/hippy-vue/vue3.md
@@ -154,6 +154,246 @@ const router: Router = createRouter({
});
```
+# 自定义组件和模块
+
+@hippy/vue-next 中同样提供了 `registerElement` 来注册自定义组件,将 template 中的 tag 和原生组件映射起来,所需要注意的是,@hippy/vue
+中 `registerElement` 方法是挂在全局 Vue 中,与 Native 类似,@hippy/vue-next 中 `registerElement` 方法也是单独提供了导出
+
+```javascript
+import { registerElement } from '@hippy/vue-next';
+```
+
+## 注册自定义组件
+
+```javascript
+// custom-tag.ts
+import { registerElement } from '@hippy/vue-next'
+
+/**
+ * 注册自定义标签
+ */
+export function registerCustomTag(): void {
+ // 终端组件名称
+ const nativeComponentName = 'CustomTagView'
+ // 标签名称
+ const htmlTagName = 'h-custom-tag'
+ // 注册终端名为 CustomTagView 的自定义组件,这里终端组件名必须与终端组件一致,这里是将我们写的 h-custom-tag 和终端的 CustomTagView 建立映射
+ registerElement(htmlTagName, {
+ component: {
+ name: nativeComponentName
+ }
+ })
+}
+
+// app.ts
+import { defineComponent, ref } from 'vue';
+import { type HippyApp, createApp } from '@hippy/vue-next';
+import { registerCustomTag } from './custom-tag'
+
+// 注册自定义组件
+registerCustomTag()
+
+// 创建 Hippy App 实例,需要注意 Vue3.x 使用 Typescript,因此需要使用 defineComponent 将组件对象进行包裹
+const app: HippyApp = createApp(defineComponent({
+ setup() {
+ const counter = ref(0);
+ return {
+ counter,
+ }
+ }
+}), {
+ // Hippy App Name 必传,示例项目可以使用 Demo
+ appName: 'Demo',
+});
+
+// 此后代码省去...
+
+```
+
+## 绑定终端事件返回值
+
+因为 @hippy/vue-next 采用了和浏览器一致的事件模型,又希望能统一双端的事件(有的时候双端事件返回值不一样),所以采取了手动修改事件返回值的方案,需要显式声明每个事件的返回值。
+这一步是在注册自定义组件时通过 `processEventData` 方法进行处理的,它有两个参数
+
+- evtData 包含事件处理 handler 和 事件名 __evt
+- nativeEventParams 终端的原生事件返回体
+
+例如 @hippy/vue-next 中原生的 [swiper](https://github.com/Tencent/Hippy/blob/master/packages/hippy-vue-next/src/native-component/swiper.ts) 组件,它是 swiper 实际渲染的对应节点,这里就对事件返回值进行了处理
+
+```javascript
+ // register swiper tag
+ registerElement('hi-swiper', {
+ component: {
+ name: 'ViewPager', // 终端的组件名
+ processEventData(
+ evtData: EventsUnionType,
+ nativeEventParams: { [key: string]: NeedToTyped },
+ ) {
+ // handler 即我们收到的 event 对象,__evt 即使我们收到的终端事件名
+ const { handler: event, __evt: nativeEventName } = evtData;
+ switch (nativeEventName) {
+ case 'onPageSelected':
+ // 显式将 native 的事件参数 nativeEventParams 的值赋予 @hippy/vue-next 真正绑定的事件 event
+ // 这样我们在 swiper 组件的 pageSelected 中收到的事件参数就包含 currentSlide 了
+ event.currentSlide = nativeEventParams.position;
+ break;
+ case 'onPageScroll':
+ event.nextSlide = nativeEventParams.position;
+ event.offset = nativeEventParams.offset;
+ break;
+ case 'onPageScrollStateChanged':
+ event.state = nativeEventParams.pageScrollState;
+ break;
+ default:
+ }
+ return event;
+ },
+ },
+ });
+```
+
+## 使用 Vue 组件实现自定义组件
+
+当你的自定义组件包含了更复杂的交互、事件、声明周期的时候,单纯的 `registerElement` 就不够用了,它只能做到很基本的元素名称到组件的映射,和基本的参数映射。
+这时我们可以通过 Vue 注册单独的组件来实现这个复杂的自定义组件,关于 Vue 组件注册可以参考[组件注册](https://cn.vuejs.org/guide/components/registration.html),注意 Vue3 和
+Vue2的组件注册有一些差异
+也可以参考 @hippy/vue [swiper](https://github.com/Tencent/Hippy/blob/master/packages/hippy-vue-next/src/native-component/swiper.ts)组件的实现
+
+### 事件处理
+
+通过 Vue 注册的组件,如果要将终端事件传给组件外层,需要做额外处理,有两种方式
+
+- 使用 `render` 函数(推荐)
+
+```javascript
+import { createApp } from 'vue'
+
+const vueApp = createApp({})
+// Vue3 中组件注册不再是通过全局 Vue 来实现了,这里注册 swiper 组件
+vueApp.component('Swiper', {
+ // ... 省去其他代码
+ render() {
+ /*
+ * 可以用 render 函数的方式
+ * 'pageScroll'是传输给终端的事件名(传输终端时会被自动转成转成onPageScroll)
+ * 'dragging' 是真正暴露给用户使用的事件名
+ */
+ const on = getEventRedirects.call(this, [
+ ['dropped', 'pageSelected'],
+ ['dragging', 'pageScroll'],
+ ['stateChanged', 'pageScrollStateChanged'],
+ ]);
+
+ return h(
+ 'hi-swiper',
+ {
+ ...on,
+ ref: 'swiper',
+ initialPage: this.$initialSlide,
+ },
+ this.$slots.default ? this.$slots.default() : null,
+ );
+ },
+});
+
+// 这里注册终端自定义组件
+registerElement('hi-swiper', {
+ component: {
+ name: 'ViewPager',
+ },
+});
+```
+
+
+- 使用 Vue `SFC` 方式
+
+```javascript
+// swiper.vue
+
+
+
+
+
+
+
+// app.ts
+import { registerElement } from '@hippy/vue-next'
+import { createApp } from 'vue'
+import Swiper from './swiper.vue'
+
+// 注册自定义终端组件
+registerElement('hi-swiper', {
+ component: {
+ name: 'ViewPager',
+ },
+});
+
+// 创建 vue 实例
+const vueApp = createApp({})
+// 注册 vue 组件
+vueApp.component('Swiper', Swiper)
+```
+
+> 使用 `SFC` 方式注册的自定义 tag 会被 Vue 作为组件来处理,但是有没有注册该组件,因此会报错,所以我们需要通过 `isCustomElement` 告诉 Vue 这是我们的
+> [自定义组件](https://cn.vuejs.org/api/application.html#app-config-compileroptions-iscustomelement),直接渲染即可。
+> 注意 hippy-webpack.dev.js,hippy-webpack.android.js,hippy-webpack.ios.js 都需要处理,这里第一个是开发环境构建使用,后两个是生产环境构建使用
+
+```javascript
+// src/scripts/hippy-webpack.dev.js & src/scripts/hippy-webpack.android.js & src/scripts/hippy-webpack.ios.js 都需要处理
+
+/**
+ * 判断给定 tag 是否是自定义组件的 tag,这里根据实际情况处理
+ */
+function isCustomTag(tag) {
+ return tag === 'hi-swiper'
+}
+
+// vue loader 部分
+{
+ test: /\.vue$/,
+ use: [
+ {
+ loader: 'vue-loader',
+ options: {
+ compilerOptions: {
+ // disable vue3 dom patch flag,because hippy do not support innerHTML
+ hoistStatic: false,
+ // whitespace handler, default is 'condense', it can be set 'preserve'
+ whitespace: 'condense',
+ // 注册自定义组件
+ isCustomElement: tag => isCustomTag(tag)
+ },
+ },
+ },
+ ],
+}
+
+```
+
# 其他差异说明
目前 `@hippy/vue-next` 与 `@hippy/vue` 功能上基本对齐,不过在 API 方面与 @hippy/vue 有一些区别,以及还有一些问题还没有解决,这里做些说明:
@@ -168,14 +408,6 @@ const router: Router = createRouter({
console.log('do somethig', Native.xxx)
```
-- registerElement
-
- @hippy/vue 中 `registerElement` 方法是挂在全局 Vue 中,与 Native 类似,@hippy/vue-next 中 `registerElement` 方法也是单独提供了导出
-
- ```javascript
- import { registerElement } from '@hippy/vue-next';
- ```
-
- 全局事件
在 @hippy/vue 中,全局事件是挂载在 Vue 上的,在 @hippy/vue-next 中,提供了单独的 `EventBus` 事件总线来处理该问题