目录文件结构
在src/pages和src/components目录下,您可以编写应用页面和组件了。每个页面/组件都占据着唯一的子目录,在子目录下可能有如下若干文件:
| 文件名 | 作用 |
|---|---|
| index.ts | 定义页面/组件的逻辑(必需) |
| index.config.ts | 页面/组件的结构化配置。页面使用CreatePageConfig,组件使用CreateComponentConfig |
| index.json | 历史兼容的小程序页面/组件JSON配置。新代码优先使用index.config.ts |
| locales/zh-CN.json | 页面/组件的i18n内容 |
| web.pc.tsx | 宽屏的html渲染 |
| web.tsx | web通用渲染,在没有更具体入口时回退 |
| web.mobile.tsx | 移动宽度下优先使用的html渲染 |
| web.pc.module.less | 宽屏样式 |
| web.module.less | 窄屏样式 |
| index.xml | 小程序渲染 |
| index.less | 小程序样式 |
| render.native.tsx | App渲染 |
| render.native.scss | App渲染样式 |
| render.ios.tsx | ios渲染 |
| render.android.tsx | android渲染 |
看上去有些复杂,但是实际上绝大多数项目都只会实现其中的部分文件。Oak框架在前端采取了一个较为保守的方案,以应对跨前端的一致性问题。其中心思想是:对页面的逻辑统一抽象(index.ts),而对页面的具体渲染则分别处理。
将前端各平台的渲染语法强行统一到一个框架之下(如taro, uniapp)的前景固然美好,但这会带来兼容性和扩展性的严重问题,同时也会造成与开源社区的割裂。这显然与Oak框架的设计目标背道而驰。因此Oak框架借鉴了微信小程序和React的设计思想,将页面的各种与渲染无关的逻辑部分抽象到index.ts当中,而将各个平台的渲染代码分割到各个文件之中,在对应平台下运行的时候进行加载,从而达到一致性和兼容性的较好平衡。
index.ts
在index.ts中,定义了本页面/组件的逻辑代码,此文件中应当避免引用任何平台相关的特性内容,而只关注于页面的逻辑能力。关于index.ts如何编写,请参见编写组件;而组件逻辑上的方法和数据项,可以参见定义组件对象。
如果确实需要引用平台相关的特性内容,可以通过process.env.OAK_PLATFORM环境变量加以区别。例如,在支付时如果需要唤起平台的支付接口,可以像下面这样编写(代码引用自oak-pay-business/src/components/pay/detail/index.ts):
if (process.env.OAK_PLATFORM === 'wechatMp') {
const { prepayMeta } = meta as { prepayMeta: WechatMiniprogram.RequestPaymentOption };
if (prepayMeta) {
const result = await wx.requestPayment(prepayMeta);
process.env.NODE_ENV === 'development' && console.log(result);
return result;
}
else {
features.message.setMessage({
type: 'error',
content: features.locales.t('startPayError.illegaPayData'),
});
}
}
else {
features.message.setMessage({
type: 'error',
content: features.locales.t('startPayError.falseEnv', { env: 'wechatMp' }),
});
}
在这里,当页面调用了支付事件时,如果判断当前环境是小程序,则调用wx.requestPayment方法唤起支付。
index.config.ts
index.config.ts用于声明页面或组件的结构化配置,它会被Oak CLI读取并参与web路由、小程序虚拟JSON、菜单和访问控制等生成流程。页面目录中通常这样写:
import { CreatePageConfig } from '@oak-frontend-base/config';
export default CreatePageConfig({
route: {
titleI18nKey: 'pageTitle',
access: { type: 'login' },
},
menu: {
name: 'Order',
icon: 'list',
},
mp: {
navigationBarTitleText: '订单',
enablePullDownRefresh: true,
usingComponents: {
price: '@project/components/price',
},
},
});
组件目录中通常这样写:
import { CreateComponentConfig } from '@oak-frontend-base/config';
export default CreateComponentConfig({
mp: {
component: true,
usingComponents: {
avatar: '@project/components/avatar',
},
},
});
其中mp字段对应原来小程序index.json中的配置项,例如usingComponents、componentGenerics、componentPlaceholder、styleIsolation、navigationBarTitleText等。保留index.json仍然可以兼容旧项目,但新页面和新组件建议优先写index.config.ts。
web端
如果您的应用基于 web,可以按需要提供 web.tsx、web.pc.tsx 和 web.mobile.tsx。当前选择顺序是:
- 移动宽度:
web.mobile.tsx -> web.tsx -> web.pc.tsx; - 宽屏:
web.pc.tsx -> web.tsx -> web.mobile.tsx。
只提供其中一个入口时,宽屏和移动端都会回退使用它。当前 Vite render-entry invalidation 会监听这些兄弟入口的新增和删除,开发中补充另一个入口后通常不再需要手工执行 clean:cache。在 TSX 中仍可使用普通 React 组件和 render-local hooks,但 Oak 数据树状态、projection、filter 和跨端业务方法应继续放在 index.ts。
微信小程序端
微信小程序端的模板写在index.xml中,其语法和wxml完全一致。页面/组件JSON配置优先写在index.config.ts的mp字段中,例如usingComponents、navigationBarTitleText、componentGenerics等;Oak CLI会在构建时生成对应的小程序JSON。旧项目中的index.json仍然兼容,但不建议在新代码里继续新增。
App端
Oak框架的App端基于react-native,您只需要编写render.native.tsx,则可生成在IOs和Android下通用的页面。当然,与react-native的编译规则类似,您也可以编写render.ios.tsx或者render.android.tsx,分别在两个操作系统之下进行渲染。同样的,您可以在tsx文件中引用任何您想使用的第三方组件。