diff --git a/.eslintrc.js b/.eslintrc.js index 2ede0909a..eac07bd93 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -53,6 +53,10 @@ module.exports = { 'jsx-a11y', ], overrides: [ + { + files: ['*.test.ts'], + extends: ['plugin:jest/recommended'], + }, { files: ['**/*.ts', '**/*.tsx'], extends: ['eslint-config-tencent/ts'], @@ -65,6 +69,9 @@ module.exports = { '@typescript-eslint/naming-convention': 'off', '@typescript-eslint/prefer-for-of': 'off', }, + parserOptions: { + project: ['./**/tsconfig.json'], + }, }, ], env: { @@ -95,6 +102,9 @@ module.exports = { // Allow event name not kebab-case 'vue/custom-event-name-casing': 'off', + // Allow component names not be multi-word + 'vue/multi-word-component-names': 'off', + 'import/no-unresolved': 'off', // Disable prop-types @@ -103,6 +113,8 @@ module.exports = { // Disable deprecated 'react/no-deprecated': 'off', + 'react/no-unknown-property': 'off', + 'import/namespace': [ 'error', { @@ -156,6 +168,7 @@ module.exports = { ['shared', resolveVue('shared')], ['sfc', resolveVue('sfc')], ['he', path.resolve(__dirname, './packages/hippy-vue/src/util/entity-decoder')], + ['@hippy-vue-next-style-parser', resolvePackage('hippy-vue-next-style-parser')], ], }, }, diff --git a/.github/workflows/frontend_build_tests.yml b/.github/workflows/frontend_build_tests.yml index 71f08b5fb..dbdc077d7 100644 --- a/.github/workflows/frontend_build_tests.yml +++ b/.github/workflows/frontend_build_tests.yml @@ -32,7 +32,7 @@ jobs: - name: lint & build run: npm run lint & npm run build - name: test - run: npm run coverage + run: npm run test:jest && npm run coverage - name: coverage if: matrix.node == '16.x' uses: codecov/codecov-action@v2 diff --git a/README.md b/README.md index 1dabf3a06..dd9f94554 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,10 @@ For iOS, we recommend to use iOS simulator when first try. However, you can chan > > `npm run build`: Build each front-end sdk package. -2. Choose a demo to build with `npm run buildexample [hippy-react-demo|hippy-vue-demo]` at root directory. +2. Choose a demo to build with `npm run buildexample [hippy-react-demo|hippy-vue-demo|hippy-vue-next-demo]` at root directory. 3. Start the Xcode and build the iOS app with `open examples/ios-demo/HippyDemo.xcodeproj`. -> If `Step 2` throw error, you can `cd` to `examples` hippy-react-demo or hippy-vue-demo, and run `npm install --legacy-peer-deps` to install demo dependencies first. +> If `Step 2` throw error, you can `cd` to `examples` hippy-react-demo or hippy-vue-demo or hippy-vue-next-demo, and run `npm install --legacy-peer-deps` to install demo dependencies first. > > More details for [iOS SDK integration](https://hippyjs.org/#/ios/integration?id=ios-%e9%9b%86%e6%88%90). @@ -81,12 +81,12 @@ Before build the android app, please make sure the SDK and NDK is installed, And > > `npm run build`: Build each front-end sdk package. -2. Choose a demo to build with `npm run buildexample [hippy-react-demo|hippy-vue-demo]` at root directory. +2. Choose a demo to build with `npm run buildexample [hippy-react-demo|hippy-vue-demo|hippy-vue-next-demo]` at root directory. 3. Open `Android Project` at root directory with Android Studio. 4. Connect Android phone with USB cable and make sure USB debugging mode is enabled(Run `adb devices` on the computer terminal to check cellphone connection status). 5. Open the project with Android Studio, run and install the apk. -> If `Step 2` throw error, you can `cd` to `examples` hippy-react-demo or hippy-vue-demo, and run `npm install --legacy-peer-deps` to install demo dependencies first. +> If `Step 2` throw error, you can `cd` to `examples` hippy-react-demo or hippy-vue-demo or hippy-vue-next-demo, and run `npm install --legacy-peer-deps` to install demo dependencies first. > > If you encounter the issue of `No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android`, here is the [solution](https://github.com/google/filament/issues/15#issuecomment-415423557). > @@ -95,19 +95,19 @@ Before build the android app, please make sure the SDK and NDK is installed, And ### Debug the js demo 1. Follow [Build the iOS simulator with js demo](https://github.com/Tencent/Hippy#build-the-ios-simulator-with-js-demo) or [Build the Android app with js demo](https://github.com/Tencent/Hippy#build-the-android-app-with-js-demo) first to build the App. -2. Run `npm run init:example [hippy-react-demo|hippy-vue-demo]` at root directory. -3. Run `npm run debugexample [hippy-react-demo|hippy-vue-demo] dev` at root directory. +2. Run `npm run init:example [hippy-react-demo|hippy-vue-demo|hippy-vue-next-demo]` at root directory. +3. Run `npm run debugexample [hippy-react-demo|hippy-vue-demo|hippy-vue-next-demo] dev` at root directory. -> Or you can `cd` to `examples/hippy-react-demo` or `examples/hippy-vue-demo` directory to run `npm run hippy:dev` instead. +> Or you can `cd` to `examples/hippy-react-demo` or `examples/hippy-vue-demo` or `examples/hippy-vue-next-demo` directory to run `npm run hippy:dev` instead. > -> On example debug mode, npm packages such as @hippy/react, @hippy/vue are linked to `packages` > `[different package]` > `dist`(not node_modules), so if you have changed js package source code and want to make it take effect in target example, please call `npm run build` at root directory again. +> On example debug mode, npm packages such as @hippy/react, @hippy/vue, @hippy/vue-next are linked to `packages` > `[different package]` > `dist`(not node_modules), so if you have changed js package source code and want to make it take effect in target example, please call `npm run build` at root directory again. > > More details for debugging can be read in [Hippy Debug Document](https://hippyjs.org/#/guide/debug). ### Build the js production demo 1. Follow [Build the iOS simulator with js demo](https://github.com/Tencent/Hippy#build-the-ios-simulator-with-js-demo) or [Build the Android app with js demo](https://github.com/Tencent/Hippy#build-the-android-app-with-js-demo) first to build the App. -2. `cd` to examples `hippy-react-demo` or `hippy-vue-demo`. +2. `cd` to examples `hippy-react-demo` or `hippy-vue-demo` or `hippy-vue-next-demo`. 3. Run `npm install` to install demo js dependencies. 4. Run `npm run hippy:vendor` and `npm run hippy:build` in sequence to build the production `vendor.[android|ios].js` and `index.[android|ios].js`. @@ -136,12 +136,15 @@ Hippy ├── examples # Demo code for frontend or native developer. │   ├── hippy-react-demo # hippy-react js demo code. │   ├── hippy-vue-demo # hippy-vue js demo code. +│   ├── hippy-vue-next-demo # hippy-vue-next js demo code. │   ├── ios-demo # iOS native demo code. │   └── android-demo # Android native demo code. ├── packages # npm packages. │   ├── hippy-react # React binding for Hippy. │   ├── hippy-react-web # Web adapter for hippy-react. +│   ├── hippy-vue-next-style-parser # style parser for Hippy vue-next. │   ├── hippy-vue # Vue binding for Hippy. +│   ├── hippy-vue-next # Vue-next binding for Hippy. │   ├── hippy-vue-css-loader # Webpack loader for convert CSS text to JS AST. │   ├── hippy-vue-loader # Forked from vue-loader to do some hippy customization. │   ├── hippy-vue-native-components # Native components extensions for hippy-vue. diff --git a/docs/assets/img/hippy-vue-next-arch-cn.png b/docs/assets/img/hippy-vue-next-arch-cn.png new file mode 100644 index 000000000..cae218709 Binary files /dev/null and b/docs/assets/img/hippy-vue-next-arch-cn.png differ diff --git a/docs/en-us/assets/img/hippy-vue-next-arch-en.png b/docs/en-us/assets/img/hippy-vue-next-arch-en.png new file mode 100644 index 000000000..5308867a6 Binary files /dev/null and b/docs/en-us/assets/img/hippy-vue-next-arch-en.png differ diff --git a/docs/en-us/guide/debug.md b/docs/en-us/guide/debug.md index 8b6043254..e701028bc 100644 --- a/docs/en-us/guide/debug.md +++ b/docs/en-us/guide/debug.md @@ -609,6 +609,6 @@ Set Host App debugMode to true and pass in the bundleUrl generated by front-end Both hippy-react and hippy-vue will output the information of communicating with native, including the js-native node operations, events sent/received. These logs are actually very helpful for business debugging, allowing developers to understand how the front-end framework translates code into a syntax that the native can understand. When you encounter problems, please first check the framework communication logs, as they can basically locate most of the problems. -If you need to turn off the logs, you can add `silent: true` to hippy-react's `new Hippy` startup parameters, or turn on `Vue.config.silent = true;` in hippy-vue project entry file. +If you need to turn off the logs, you can add `silent: true` to hippy-react's `new Hippy` startup parameters, or turn on `Vue.config.silent = true;` in hippy-vue project entry file, or add `silent: true` to hippy-vue-next's `createApp` init options. Communication Info diff --git a/docs/en-us/hippy-vue/vue3.md b/docs/en-us/hippy-vue/vue3.md index 92688cf55..07bdffc72 100644 --- a/docs/en-us/hippy-vue/vue3.md +++ b/docs/en-us/hippy-vue/vue3.md @@ -1,6 +1,278 @@ -# Vue 3.x +# Hippy-Vue-Next -Vue 3.x provides a better [createRenderer()](//github.com/vuejs/vue-next/blob/v3.0.0-alpha.0/packages/runtime-core/src/renderer.ts#L154) method to customize the render, eliminating the need for node-ops plug-ins in the future. Hippy is upgrading to support Vue 3.x, look forward to it! +
+ +# Introduction + +@hippy/vue-next is based on the existing logic of @hippy/vue, through the [createRenderer()](//github.com/vuejs/vue-next/blob/v3.0.0-alpha.0/packages/runtime-core/src/renderer.ts#L154) API provided by Vue 3.x, there is no need to invade Vue code, and Vue can be directly referenced through external libraries, which can follow the Vue ecosystem in time. +The implementation principle is basically the same as @hippy/vue. Convert the VNode Tree generated by the Vue component into a Hippy Node Tree, and complete the rendering through the Native rendering interface injected by the Hippy Native. +@hippy/vue-next All code is written in typescript, which can have better program robustness and type hints. And the overall architecture of @hippy/vue-next has also been optimized. +# Architecture +hippy-vue-next structure +
+
+ +# How to use + +The capabilities supported by @hippy/vue-next are basically the same as @hippy/vue. Therefore, there is no additional introduction about Hippy-Vue components, modules, styles, etc., you can refer to the relevant content in [Hippy-Vue](https://hippyjs.org/#/hippy-vue/introduction), this document only explains the differences. + +- initialization + +```javascript +// only for Vue +// app.ts +import { defineComponent, ref } from 'vue'; +import { type HippyApp, createApp } from '@hippy/vue-next'; + +// To create a Hippy App instance, it should be noted that Vue 3.x uses Typescript, and you need to use defineComponent to wrap the component object +const app: HippyApp = createApp(defineComponent({ + setup() { + const counter = ref(0); + return { + counter, + } + } +}), { + // Hippy App Name must be set, the sample project can use Demo + appName: 'Demo', +}); + +// start Hippy App +app.$start().then(({ superProps, rootViewId }) => { + // superProps is the initialization parameter passed in by Native. If you need to do routing preprocessing and other operations, you can let Native pass in the corresponding parameters + // rootViewId is the id of the native root node mounted by the current Hippy instance of Native + // mount app, render to screen + app.mount('#mount'); +}) +``` + +If you want to use Vue-Router, you need to use additional initialization logic + +```javascript + +// Vue + Vue Router + +// app.vue + + + +// index.vue + + + +// app.ts +import { defineComponent, ref } from 'vue'; +import { type HippyApp, createApp } from '@hippy/vue-next'; +import { createHippyRouter } from '@hippy/vue-router-next-history'; +import { type Router } from 'vue-router'; +import App from 'app.vue'; + +// To create a Hippy App instance, it should be noted that Vue 3.x uses Typescript, and you need to use defineComponent to wrap the component object +const app: HippyApp = createApp(App, { + // Hippy App Name must be set, the sample project can use Demo + appName: 'Demo', +}); + +// routes list +const routes = [ + { + path: '/', + component: Index, + }, +]; + +// create router +const router: Router = createHippyRouter({ + routes, +}); + +// use router +app.use(router); + +// start Hippy App +app.$start().then(({ superProps, rootViewId }) => { + // superProps is the initialization parameter passed in by Native. If you need to do routing preprocessing and other operations, you can let Native pass in the corresponding parameters + // rootViewId is the id of the native root node mounted by the current Hippy instance of Native + + // Because the memory history of vue-router is now used, the initial position needs to be pushed manually, otherwise the router will not be ready + // In the browser, vue-router matches according to location.href, and pushes the root path '/' by default. + // If you want to jump to the specified page by default like in the browser, you can let the native pass the initialized path from superProps, + // and then perform operations such as router.push({ path: 'other path' }) through the value of path + router.push('/'); + + // mount app, render to screen + app.mount('#mount'); +}) +``` + +> Tips: @hippy/vue-router-next-history modify vue-router's history mode. Added the logic of returning the history record first when the hardware back key is triggered for Android, +> If you don't need this, you can use original vue-router to implement routing: + +use original vue-router + +```javascript +import { createRouter, createMemoryHistory, type Router } from 'vue-router'; + +// 路由列表 +const routes = [ + { + path: '/', + component: Index, + }, +]; + +const router = createRouter({ + history: createMemoryHistory(), + routes, +}); +``` + +# Examples + +The functions implemented by the @hippy/vue example project are basically the same, but the writing method is the combined API of Vue 3.x, and some @hippy/vue-next and @hippy/vue different writing methods. +For details, please see the writing method of the [example project](https://github.com/Tencent/Hippy/tree/master/examples/hippy-vue-next-demo) directly. + +# Additional Notes + +@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: + +- Vue.Native + + In @hippy/vue, the capabilities provided by Native are provided by the Native attribute mounted on the global Vue. In Vue 3.x, this implementation is no longer feasible. You can access Native as follows: + + ```javascript + import { Native } from '@hippy/vue-next'; + + Native.xxx + ``` + +- v-model directive + + Because the built-in instructions in Vue 3.x are implemented by inserting code at compile time, the v-model instruction has not yet found a good way to deal with it. Here, a temporary solution can be used to implement the corresponding function: + + ```javascript + // For the specific usage, please refer to the example in demo-input.vue in demo + + + ``` + +- HMR for Keep-Alive + + In the sample code, our routing component is wrapped in the Keep-Alive component, but currently, the routing component wrapped with Keep-Alive cannot achieve hot update during development, and the entire instance needs to be refreshed to complete the refresh. + There is no such problem if it is not wrapped in Keep-Alive. At present, the [official problem](https://github.com/vuejs/core/pull/5165) has not been resolved. The problem can be solved by upgrading Vue after waiting for the official solution. +- Vue 3.x Proxy + + Because the reactivity of Vue 3.x is implemented through the "Proxy", so the object we get is actually an instance of Proxy instead of the original object, so we need to pay attention when calling the native interface, the native does not Know the Proxy object, + You need to use the "toRaw" method provided by Vue 3.x to get the original object and pass it to the native API. +- registerElement + + In @hippy/vue, method registerElement used by Vue.registerElement,But with the same reason with Vue.Native. registerElement method in @hippy/vue-next provide single. + + ```javascript + import { registerElement } from '@hippy/vue-next'; + ``` + + > You can use it like above +- Global Event + In @hippy/vue,global event used Vue.$on or Vue.$off,Now in @hippy/vue-next,we provide single EventBus API to do that. + + ```javascript + import { EventBus } from '@hippy/vue-next'; + + // Listen container size change event(Only Android) + EventBus.$on('onSizeChanged', ({ oldWidth, oldHeight, width, height }) => { + // oldWidth: old widht;oldHeight: old height;width: new width; height: new height + console.log('size', oldWidth, oldHeight, width, height); + }); + // trigger global event + EventBus.$emit('eventName', { + ...args, // event params + }); + ``` + +- Type hints of Nativeapis and Custom components + @hippy/vue-next provides type hints for the nativeapis. + If there is a business-defined nativeapi, it can also be extended in a similar way + + ```javascript + declare module '@hippy/vue-next' { + export interface NativeInterfaceMap { + // then you can have type hints in Native.callNative, Native.callNativeWithPromise + } + } + ``` + + @hippy/vue-next also provides event types with reference to the event declaration of lib.dom.d.ts. + For details, please refer to hippy-event.ts. + If you need to extend the built-in events, you can use a similar way + + ```javascript + declare module '@hippy/vue-next' { + export interface HippyEvent { + testProp: number; + } + } + ``` + + When using registerElement to register components, type narrowing is used to provide + accurate type hints in switch cases, If you also need similar type hints when registering custom + components, you can use the following methods + + ```javascript + export interface HippyGlobalEventHandlersEventMap { + // extend new event name and related event interface + onTest: CustomEvent; + // extend existing event interface + onAnotherTest: HippyEvent; + } + ``` + + For more information, please refer to extend.ts and app.ts in the demo. diff --git a/docs/guide/debug.md b/docs/guide/debug.md index c2cc9fa08..9abc23aaf 100644 --- a/docs/guide/debug.md +++ b/docs/guide/debug.md @@ -614,6 +614,6 @@ webpack(webpackConfig, (err, stats) => { 无论是 hippy-react 还是 hippy-vue 都将和终端通讯的信息进行输出,包含了前终端的节点操作、事件收发。这些日志对于业务调试其实很有帮助,可以让开发了解到前端框架是如何将代码转译成终端可以理解的语法。当遇到问题时应先检查框架通信日志,基本可以定位到大部分问题。 -如果需要关闭日志,可以在 hippy-react 的 `new Hippy` 启动参数中增加 `silent: true`,或者 hippy-vue 项目的入口文件中,开启 `Vue.config.silent = true;`。 +如果需要关闭日志,可以在 hippy-react 的 `new Hippy` 启动参数中增加 `silent: true`,或者 hippy-vue 项目的入口文件中,开启 `Vue.config.silent = true;`,或者在 hippy-vue-next 项目的 `createApp` 初始化参数中增加 `silent: true`。 Communication Info diff --git a/docs/hippy-vue/vue3.md b/docs/hippy-vue/vue3.md index f0d6f85eb..767a5ddcc 100644 --- a/docs/hippy-vue/vue3.md +++ b/docs/hippy-vue/vue3.md @@ -1,6 +1,265 @@ -# Vue 3.x +# Hippy-Vue-Next -Vue 3.x 提供了更好的 [createRenderer()](//github.com/vuejs/vue-next/blob/v3.0.0-alpha.0/packages/runtime-core/src/renderer.ts#L154) 方法可以自定义渲染器,未来就不需要 node-ops 那种外挂的方式了,当前 Vue 3.x 的支持正在开发中,敬请期待! +
+# 介绍 +@hippy/vue-next 基于 @hippy/vue 的已有逻辑。通过 Vue 3.x 提供的[createRenderer()](//github.com/vuejs/vue-next/blob/v3.0.0-alpha.0/packages/runtime-core/src/renderer.ts#L154),无需侵入 Vue 代码,直接通过外部库的方式引用 Vue。 +可以及时跟随 Vue 生态。在实现原理上与 @hippy/vue 基本一致。将 Vue 组件生成的 VNode Tree 转换为 Hippy Node Tree,并通过 Hippy 终端注入的 Native 渲染接口完成渲染。 +@hippy/vue-next 全部代码采用 typescript 进行编写,可以拥有更好的程序健壮性和类型提示。并且 @hippy/vue-next 的整体架构也进行了一定程度的优化 +# 架构图 + +hippy-vue-next 架构图 +
+
+ +# 如何使用 + +@hippy/vue-next 支持的能力与 @hippy/vue 基本一致。因此关于 Hippy-Vue 的组件、模块、样式等就不做额外声明了,可以直接参考 [Hippy-Vue](https://hippyjs.org/#/hippy-vue/introduction) +中的相关内容,本文档仅对差异部分进行说明 + +- 初始化 + +```javascript +// 仅 Vue +// app.ts +import { defineComponent, ref } from 'vue'; +import { type HippyApp, createApp } from '@hippy/vue-next'; + +// 创建 Hippy App 实例,需要注意 Vue 3.x 使用 Typescript,因此需要使用 defineComponent 将组件对象进行包裹 +const app: HippyApp = createApp(defineComponent({ + setup() { + const counter = ref(0); + return { + counter, + } + } +}), { + // Hippy App Name 必传,示例项目可以使用 Demo + appName: 'Demo', +}); + +// 启动 Hippy App +app.$start().then(({ superProps, rootViewId }) => { + // superProps 是 Native 传入的初始化参数,如果需要做路由预处理等操作,则可以让 Native 将对应参数传入 + // rootViewId 是 Native 当前 Hippy 实例所挂载的 Native 的跟节点的 id + // mount app,完成渲染上屏 + app.mount('#mount'); +}) +``` + +如果要使用 Vue-Router,则需要使用另外的初始化逻辑 + +```javascript +// Vue + Vue Router + +// app.vue + + + +// index.vue + + + +// app.ts +import { defineComponent, ref } from 'vue'; +import { type HippyApp, createApp } from '@hippy/vue-next'; +import { createHippyRouter } from '@hippy/vue-router-next-history'; +import { type Router } from 'vue-router'; +import App from 'app.vue'; + +// 创建 Hippy App 实例,需要注意 Vue 3.x 使用 Typescript,因此需要使用 defineComponent 将组件对象进行包裹 +const app: HippyApp = createApp(App, { + // Hippy App Name,必传,示例项目可以使用 Demo + appName: 'Demo', +}); + +// 路由列表 +const routes = [ + { + path: '/', + component: Index, + }, +]; + +// 创建路由对象 +const router: Router = createHippyRouter({ + routes, +}); + +// 使用路由 +app.use(router); + +// 启动 Hippy App +app.$start().then(({ superProps, rootViewId }) => { + // superProps 是 Native 传入的初始化参数,如果需要做路由预处理等操作,则可以让 Native 将对应参数传入 + // rootViewId 是 Native 当前 Hippy 实例所挂载的 Native 的跟节点的 id + + // 因为现在使用的是vue-router的memory history,因此需要手动推送初始位置,否则router将无法ready + // 浏览器上则是由vue-router根据location.href去匹配,默认推送根路径'/',如果想要实现类似浏览器中默认跳转到指定页面,可以让终端同学将初始化的 path + // 从 superProps 中传入,然后再通过 path 的值去进行 router.push({ path: 'other path' }) 等操作 + router.push('/'); + + // mount app,完成渲染上屏 + app.mount('#mount'); +}) +``` + +>Tips: @hippy/vue-router-next-history 对 vue-router 的 history 模式做了处理。为安卓加上了触发物理返回键时优先回退历史记录的逻辑, +> 如果不需要这个逻辑,可以直接使用原生 vue-router 来实现路由: + +使用原生 vue-router 实现路由 + +```javascript +import { createRouter, createMemoryHistory, type Router } from 'vue-router'; + +// 路由列表 +const routes = [ + { + path: '/', + component: Index, + }, +]; + +const router: Router = createRouter({ + history: createMemoryHistory(), + routes, +}); +``` + +# 示例 + +[示例项目](https://github.com/Tencent/Hippy/tree/master/examples/hippy-vue-next-demo)与 @hippy/vue 示例项目实现的功能基本一致,只是写法上采用的是 Vue 3.x 的组合式 API 的写法,以及部分 @hippy/vue-next 与 @hippy/vue 不同的写法。 +具体的请直接看示例项目的写法 + +# 额外说明 + +目前 @hippy/vue-next 与 @hippy/vue 功能上基本对齐,不过在 API 方面与 @hippy/vue 有稍许不同,以及还有一些问题没有解决,这里做些说明: + +- Vue.Native + + 在 @hippy/vue 中,Native 提供的能力是通过挂载在全局 Vue 对象的 Native 属性来提供的,在 Vue 3.x 中这种实现方式不再可行,因此现在 Native 属性需通过 @hippy/vue-next 导出来使用 + + ```javascript + import { Native } from '@hippy/vue-next'; + + console.log('do somethig', Native.xxx) + ``` + +- v-model 指令: + 因为 Vue 3.x 中内置指令的实现采用的是编译时插入代码的方式,目前 v-model 指令还没有找到很好的办法去处理,这里可以先使用临时解决办法实现对应功能 + + ```javascript + // 具体的可以参考 demo 中的 demo-input.vue 中的示例 + + + ``` + +- Keep-Alive HMR问题:在示例代码中,我们的路由组件是包裹在 Keep-Alive 组件内的,但是目前使用 Keep-Alive 包裹的路由组件无法实现开发时热更新,需要刷新整个实例才能完成刷新。 + 如果是不包裹在 Keep-Alive 中则没有这个问题。目前官方[该问题](https://github.com/vuejs/core/pull/5165)也尚未解决。等待官方解决后升级 Vue 即可解决该问题 +- Vue 3.x 变量 Proxy 问题:因为3.x的响应式是通过 Proxy 代理对象来实现的,所以我们得到的对象其实是 Proxy 的实例而非原始对象,因此调用终端接口时需要注意,终端并不认识 Proxy 对象, + 需要使用 Vue 提供的 toRaw 方法来拿到原始的对象并传递给终端接口 +- registerElement问题:@hippy/vue 中 registerElement方法是挂在全局 Vue 中,与 Native 类似,@hippy/vue-next 中 registerElement 方法也是单独提供了 + + ```javascript + import { registerElement } from '@hippy/vue-next'; + ``` + +- 全局事件: + 在 @hippy/vue 中,全局事件也是挂载在 Vue 上的,在 @hippy/vue-next 中,提供了单独的事件总线来处理该问题 + + ```javascript + import { EventBus } from '@hippy/vue-next'; + + // 监听容器大小改变事件(仅 Android) + EventBus.$on('onSizeChanged', ({ oldWidth, oldHeight, width, height }) => { + // oldWidth: 旧的宽度;oldHeight: 旧的高度;width: 新的宽度; height: 新的高度 + console.log('size', oldWidth, oldHeight, width, height); + }); + // 触发全局事件 + EventBus.$emit('eventName', { + ...args, // 事件参数 + }); + ``` + +- Native 接口和自定义组件的类型提示 + @hippy/vue-next 提供了 Native 接口的 Typescript 类型提示,如果有业务自定义的Native接口,也可以采用类似的方式进行扩展 + + ```javascript + declare module '@hippy/vue-next' { + export interface NativeInterfaceMap { + // 用户自定义的Native接口,接下来你可以在Native.callNative,Native.callNativeWithPromise拥有类型提示了 + } + } + ``` + + @hippy/vue-next 也参考 dom 的事件声明提供了事件类型,具体可以参考 hippy-event.ts. + 如果需要在内置的事件上进行扩展,可以采用类似方式 + + ```javascript + declare module '@hippy/vue-next' { + export interface HippyEvent { + testProp: number; + } + } + ``` + + 在使用 registerElement 去注册组件的时候,利用了 typescript 的 type narrow,在 switch case 中提供了准确的类型提示, + 如果在业务注册自定义组件的时候也需要类型提示,可以采用如下方式 + + ```javascript + export interface HippyGlobalEventHandlersEventMap { + // extend new event name and related event interface + onTest: CustomEvent; + // extend existing event interface + onAnotherTest: HippyEvent; + } + ``` + + 更多信息可以参考 demo 里的 extend.ts 和 app.ts diff --git a/examples/hippy-vue-demo/package.json b/examples/hippy-vue-demo/package.json index 9587dba45..217c31e5d 100644 --- a/examples/hippy-vue-demo/package.json +++ b/examples/hippy-vue-demo/package.json @@ -19,10 +19,10 @@ "@hippy/vue": "latest", "@hippy/vue-native-components": "latest", "@hippy/vue-router": "latest", + "@hippy/web-renderer": "^0.0.1", "core-js": "^3.11.0", "vue": "^2.6.10", - "vue-router": "^3.1.3", - "@hippy/web-renderer": "^0.0.1" + "vue-router": "^3.1.3" }, "devDependencies": { "@babel/core": "^7.12.0", diff --git a/examples/hippy-vue-next-demo/.gitignore b/examples/hippy-vue-next-demo/.gitignore new file mode 100644 index 000000000..a0dddc6fb --- /dev/null +++ b/examples/hippy-vue-next-demo/.gitignore @@ -0,0 +1,21 @@ +.DS_Store +node_modules +/dist + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/hippy-vue-next-demo/.npmrc b/examples/hippy-vue-next-demo/.npmrc new file mode 100644 index 000000000..43c97e719 --- /dev/null +++ b/examples/hippy-vue-next-demo/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/examples/hippy-vue-next-demo/README.md b/examples/hippy-vue-next-demo/README.md new file mode 100644 index 000000000..44a39edcb --- /dev/null +++ b/examples/hippy-vue-next-demo/README.md @@ -0,0 +1,10 @@ +# @hippy/vue-next demo + + +### Introduction +This package is the demo project for @hippy/vue-next. Project include most use case for +@hippy/vue-next. Just try it. + +### Usage +Read the hippy framework [doc](https://github.com/Tencent/Hippy/blob/master/README.md#-getting-started) and learn +how to use. diff --git a/examples/hippy-vue-next-demo/app.d.ts b/examples/hippy-vue-next-demo/app.d.ts new file mode 100644 index 000000000..d614236bf --- /dev/null +++ b/examples/hippy-vue-next-demo/app.d.ts @@ -0,0 +1,10 @@ +declare module '*.jpg'; +declare module '*.png'; +declare module '*.vue' { + import { defineComponent } from 'vue'; + const Component: ReturnType; + export default Component; +} + +type NeedToTyped = any; + diff --git a/examples/hippy-vue-next-demo/package.json b/examples/hippy-vue-next-demo/package.json new file mode 100644 index 000000000..cf7cdf5ad --- /dev/null +++ b/examples/hippy-vue-next-demo/package.json @@ -0,0 +1,58 @@ +{ + "name": "hippy-vue-next-demo", + "version": "2.0.0", + "description": "A Demo Example For Hippy-Vue-Next Library To Show.", + "private": true, + "repository": "https://github.com/Tencent/Hippy/tree/master/examples/hippy-vue-next-demo", + "license": "Apache-2.0", + "author": "birdguo@tencent.com", + "scripts": { + "hippy:debug": "hippy-debug", + "hippy:dev": "cross-env-os os=\"Windows_NT,Linux,Darwin\" minVersion=17 NODE_OPTIONS=--openssl-legacy-provider hippy-dev -c ./scripts/webpack.dev.js", + "hippy:vendor": "cross-env-os os=\"Windows_NT,Linux,Darwin\" minVersion=17 NODE_OPTIONS=--openssl-legacy-provider webpack --config ./scripts/webpack.ios-vendor.js --config ./scripts/webpack.android-vendor.js", + "hippy:build": "cross-env-os os=\"Windows_NT,Linux,Darwin\" minVersion=17 NODE_OPTIONS=--openssl-legacy-provider webpack --config ./scripts/webpack.ios.js --config ./scripts/webpack.android.js" + }, + "dependencies": { + "@hippy/vue-next": "latest", + "@hippy/vue-router-next-history": "latest", + "@vue/runtime-core": "^3.2.21", + "vue-router": "^4.0.12" + }, + "devDependencies": { + "@babel/core": "^7.12.0", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-decorators": "^7.10.5", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", + "@babel/plugin-proposal-object-rest-spread": "^7.5.5", + "@babel/plugin-proposal-optional-chaining": "^7.10.4", + "@babel/plugin-transform-async-to-generator": "^7.5.0", + "@babel/plugin-transform-runtime": "^7.11.0", + "@babel/polyfill": "^7.12.0", + "@babel/preset-env": "^7.12.0", + "@babel/runtime": "^7.16.0", + "@hippy/debug-server-next": "^0.3.7", + "@hippy/hippy-dynamic-import-plugin": "^2.0.0", + "@hippy/hippy-hmr-plugin": "^0.0.2", + "@hippy/rejection-tracking-polyfill": "^1.0.0", + "@hippy/vue-css-loader": "^2.0.1", + "@vitejs/plugin-vue": "^1.9.4", + "@vue/compiler-sfc": "^3.2.21", + "babel-loader": "^8.1.0", + "case-sensitive-paths-webpack-plugin": "^2.2.0", + "clean-webpack-plugin": "^4.0.0", + "cross-env-os": "^7.1.1", + "esbuild": "^0.13.14", + "esbuild-loader": "^2.18.0", + "file-loader": "^4.3.0", + "less": "^4.1.2", + "less-loader": "^7.1.0", + "terser": "^4.8.0", + "ts-loader": "^8.4.0", + "url-loader": "^4.0.0", + "vue": "^3.2.21", + "vue-loader": "^17.0.0", + "webpack": "^4.46.0", + "webpack-cli": "^4.7.2" + }, + "demo": "src/main-native.ts" +} diff --git a/examples/hippy-vue-next-demo/scripts/vendor.js b/examples/hippy-vue-next-demo/scripts/vendor.js new file mode 100644 index 000000000..41fecfb38 --- /dev/null +++ b/examples/hippy-vue-next-demo/scripts/vendor.js @@ -0,0 +1 @@ +require('@hippy/vue-next'); diff --git a/examples/hippy-vue-next-demo/scripts/webpack.android-vendor.js b/examples/hippy-vue-next-demo/scripts/webpack.android-vendor.js new file mode 100644 index 000000000..97133f478 --- /dev/null +++ b/examples/hippy-vue-next-demo/scripts/webpack.android-vendor.js @@ -0,0 +1,106 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const { VueLoaderPlugin } = require('vue-loader'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); + +const platform = 'android'; + +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: 'vue-loader', + options: { + compilerOptions: { + // disable vue3 dom patch flag,because hippy do not support innerHTML + hoistStatic: false, + }, + }, + }, + ], + }, + { + 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', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/examples/hippy-vue-next-demo/scripts/webpack.android.js b/examples/hippy-vue-next-demo/scripts/webpack.android.js new file mode 100644 index 000000000..54b3b9ee0 --- /dev/null +++ b/examples/hippy-vue-next-demo/scripts/webpack.android.js @@ -0,0 +1,162 @@ +const path = require('path'); +const fs = require('fs'); +const HippyDynamicImportPlugin = require('@hippy/hippy-dynamic-import-plugin'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const { VueLoaderPlugin } = require('vue-loader'); +const webpack = require('webpack'); + +const platform = 'android'; +const pkg = require('../package.json'); +const manifest = require('../dist/android/vendor-manifest.json'); +const mode = 'production'; +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'); +} + +module.exports = { + mode, + bail: true, + entry: { + index: [path.resolve(pkg.demo)], + }, + 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/hippyVueNextDemo/', + }, + plugins: [ + new webpack.NamedModulesPlugin(), + new CaseSensitivePathsPlugin(), + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(mode), + }, + __PLATFORM__: JSON.stringify(platform), + }), + new webpack.DllReferencePlugin({ + context: path.resolve(__dirname, '..'), + manifest, + }), + new HippyDynamicImportPlugin(), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: 'vue-loader', + options: { + compilerOptions: { + // disable vue3 dom patchflag,because hippy do not support innerHTML + hoistStatic: false, + }, + }, + }, + ], + }, + { + test: /\.(le|c)ss$/, + use: [cssLoader, 'less-loader'], + }, + { + test: /\.t|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/', + }, + }, + ], + }, + { + test: /\.(ts)$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: [/\.vue$/], + }, + }, + ], + exclude: /node_modules/, + }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/examples/hippy-vue-next-demo/scripts/webpack.dev.js b/examples/hippy-vue-next-demo/scripts/webpack.dev.js new file mode 100644 index 000000000..e38309068 --- /dev/null +++ b/examples/hippy-vue-next-demo/scripts/webpack.dev.js @@ -0,0 +1,164 @@ +const path = require('path'); +const fs = require('fs'); +const HippyDynamicImportPlugin = require('@hippy/hippy-dynamic-import-plugin'); +const { CleanWebpackPlugin } = require('clean-webpack-plugin'); +const { VueLoaderPlugin } = require('vue-loader'); +const webpack = require('webpack'); + +const pkg = require('../package.json'); +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'); +} + + +module.exports = { + mode: 'development', + // devtool: 'eval-source-map', + watch: true, + watchOptions: { + aggregateTimeout: 1500, + }, + devServer: { + remote: { + protocol: 'http', + host: '127.0.0.1', + port: 38989, + }, + vueDevtools: false, + multiple: false, + hot: true, + liveReload: true, + client: { + overlay: false, + }, + devMiddleware: { + writeToDisk: true, + }, + }, + entry: { + index: [path.resolve(pkg.demo)], + }, + output: { + filename: 'index.bundle', + strictModuleExceptionHandling: true, + path: path.resolve('./dist/dev/'), + globalObject: '(0, eval)("this")', + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify('development'), + HOST: JSON.stringify(process.env.DEV_HOST || '127.0.0.1'), + PORT: JSON.stringify(process.env.DEV_PORT || 38989), + }, + __VUE_OPTIONS_API__: true, + __VUE_PROD_DEVTOOLS__: false, + __PLATFORM__: null, + }), + new HippyDynamicImportPlugin(), + new CleanWebpackPlugin(), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: 'vue-loader', + options: { + compilerOptions: { + // disable vue3 dom patch flag,because hippy do not support innerHTML + hoistStatic: false, + }, + }, + }, + ], + }, + { + test: /\.(le|c)ss$/, + use: [cssLoader, 'less-loader'], + }, + { + test: /\.t|js$/, + use: [ + { + loader: 'esbuild-loader', + options: { + target: 'es2015', + }, + }, + ], + }, + { + 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/', + }, + }, + ], + }, + { + test: /\.(ts)$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: [/\.vue$/], + }, + }, + ], + exclude: /node_modules/, + }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/examples/hippy-vue-next-demo/scripts/webpack.ios-vendor.js b/examples/hippy-vue-next-demo/scripts/webpack.ios-vendor.js new file mode 100644 index 000000000..e02b5caf1 --- /dev/null +++ b/examples/hippy-vue-next-demo/scripts/webpack.ios-vendor.js @@ -0,0 +1,106 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const { VueLoaderPlugin } = require('vue-loader'); + +const platform = 'ios'; + +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: 'vue-loader', + options: { + compilerOptions: { + // disable vue3 dom patch flag,because hippy do not support innerHTML + hoistStatic: false, + }, + }, + }, + ], + }, + { + test: /\.(js)$/, + use: [ + { + loader: 'babel-loader', + options: { + presets: [ + [ + '@babel/preset-env', + { + targets: { + ios: 8, + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-class-properties'], + ], + }, + }, + ], + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/examples/hippy-vue-next-demo/scripts/webpack.ios.js b/examples/hippy-vue-next-demo/scripts/webpack.ios.js new file mode 100644 index 000000000..d17164f17 --- /dev/null +++ b/examples/hippy-vue-next-demo/scripts/webpack.ios.js @@ -0,0 +1,162 @@ +const path = require('path'); +const fs = require('fs'); +const HippyDynamicImportPlugin = require('@hippy/hippy-dynamic-import-plugin'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const { VueLoaderPlugin } = require('vue-loader'); +const webpack = require('webpack'); + +const platform = 'ios'; +const pkg = require('../package.json'); +const manifest = require('../dist/ios/vendor-manifest.json'); +const mode = 'production'; +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'); +} + +module.exports = { + mode, + bail: true, + entry: { + index: [path.resolve(pkg.demo)], + }, + 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/hippyVueNextDemo/', + }, + plugins: [ + new webpack.NamedModulesPlugin(), + new CaseSensitivePathsPlugin(), + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(mode), + }, + __PLATFORM__: JSON.stringify(platform), + }), + new webpack.DllReferencePlugin({ + context: path.resolve(__dirname, '..'), + manifest, + }), + new HippyDynamicImportPlugin(), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: 'vue-loader', + options: { + compilerOptions: { + // disable vue3 dom patchflag,because hippy do not support innerHTML + hoistStatic: false, + }, + }, + }, + ], + }, + { + test: /\.(le|c)ss$/, + use: [cssLoader, 'less-loader'], + }, + { + test: /\.t|js$/, + use: [ + { + loader: 'babel-loader', + options: { + sourceType: 'unambiguous', + presets: [ + [ + '@babel/preset-env', + { + targets: { + ios: 9, + }, + }, + ], + ], + 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/', + }, + }, + ], + }, + { + test: /\.(ts)$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: [/\.vue$/], + }, + }, + ], + exclude: /node_modules/, + }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/examples/hippy-vue-next-demo/src/app.vue b/examples/hippy-vue-next-demo/src/app.vue new file mode 100644 index 000000000..091d5d2a3 --- /dev/null +++ b/examples/hippy-vue-next-demo/src/app.vue @@ -0,0 +1,248 @@ + + + diff --git a/examples/hippy-vue-next-demo/src/assets/defaultSource.jpg b/examples/hippy-vue-next-demo/src/assets/defaultSource.jpg new file mode 100644 index 000000000..833417ea2 Binary files /dev/null and b/examples/hippy-vue-next-demo/src/assets/defaultSource.jpg differ diff --git a/examples/hippy-vue-next-demo/src/assets/hippyLogoWhite.png b/examples/hippy-vue-next-demo/src/assets/hippyLogoWhite.png new file mode 100644 index 000000000..20e428e2f Binary files /dev/null and b/examples/hippy-vue-next-demo/src/assets/hippyLogoWhite.png differ diff --git a/examples/hippy-vue-next-demo/src/assets/logo.png b/examples/hippy-vue-next-demo/src/assets/logo.png new file mode 100644 index 000000000..f3d2503fc Binary files /dev/null and b/examples/hippy-vue-next-demo/src/assets/logo.png differ diff --git a/examples/hippy-vue-next-demo/src/back-icon.png b/examples/hippy-vue-next-demo/src/back-icon.png new file mode 100644 index 000000000..fe1fbf1cd Binary files /dev/null and b/examples/hippy-vue-next-demo/src/back-icon.png differ diff --git a/examples/hippy-vue-next-demo/src/components/demo/demo-button.vue b/examples/hippy-vue-next-demo/src/components/demo/demo-button.vue new file mode 100644 index 000000000..00c06a74e --- /dev/null +++ b/examples/hippy-vue-next-demo/src/components/demo/demo-button.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/examples/hippy-vue-next-demo/src/components/demo/demo-div.vue b/examples/hippy-vue-next-demo/src/components/demo/demo-div.vue new file mode 100644 index 000000000..fe2cf2d61 --- /dev/null +++ b/examples/hippy-vue-next-demo/src/components/demo/demo-div.vue @@ -0,0 +1,343 @@ + + + + + diff --git a/examples/hippy-vue-next-demo/src/components/demo/demo-dynamicimport.vue b/examples/hippy-vue-next-demo/src/components/demo/demo-dynamicimport.vue new file mode 100644 index 000000000..8ae78a077 --- /dev/null +++ b/examples/hippy-vue-next-demo/src/components/demo/demo-dynamicimport.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/examples/hippy-vue-next-demo/src/components/demo/demo-iframe.vue b/examples/hippy-vue-next-demo/src/components/demo/demo-iframe.vue new file mode 100644 index 000000000..4d2589e2c --- /dev/null +++ b/examples/hippy-vue-next-demo/src/components/demo/demo-iframe.vue @@ -0,0 +1,122 @@ +