当百度小程序项目从 Okam 切换到 Tina.js 时,最先遇到的往往不是 API 缺失,而是两套框架对页面组织方式的理解并不相同。Okam 强调类 Vue 的单文件组件思维,模板、脚本和样式被封装在一起;而 Tina.js 更贴近原生小程序的拆分结构,同时提供了一层更轻量的逻辑封装。这种差异意味着迁移不只是改文件后缀,还需要重新梳理数据流、事件绑定和组件注册方式。

一、Okam 与 Tina.js 的核心差异
Okam 在百度小程序上构建了一套类 Vue 的开发体验,页面通常以 .okm 或类似单文件形式存在,模板中可以直接使用 v-if、v-for 等指令,逻辑部分通过 export default 导出组件选项。而 Tina.js 的页面由原生小程序支持的 .swan、.js、.json、.css 四个文件组成,逻辑层直接使用 Page() 或 Component() 构造器,数据通过 setData 更新。迁移的第一步就是要把 Okam 的声明式模板指令转换成小程序原生模板语法,例如 v-if 对应 s-if,v-for 需要写成 s-for 并配合 s-for-item 等属性。
生命周期方面,Okam 的 created、mounted 在 Tina.js 中并没有完全对应的钩子。Tina.js 复用了小程序的 onLoad、onReady、onShow 等原生生命周期,但它在 Page 构造器外层做了一层包装,允许通过 use 方法注入插件,这一点与 Okam 的插件机制思路类似,但实现更简洁。组件通信上,Okam 支持 props 和自定义事件,Tina.js 则要求使用 properties 定义属性,并借助 triggerEvent 派发事件,同时需要在 json 文件的 usingComponents 中注册组件。
下面这段对比可以直观看出页面定义方式的区别。Okam 中的单文件页面迁移后要拆成多个文件,逻辑结构完全不同:
// Okam 页面逻辑(单文件中的 script 部分)
export default {
data: {
list: []
},
created() {
this.fetchList();
},
methods: {
async fetchList() {
const res = await api.getList();
this.list = res.data;
}
}
};
// Tina.js 页面逻辑(page.js)
const app = getApp();
Page({
data: {
list: []
},
onLoad() {
this.fetchList();
},
async fetchList() {
const res = await app.request.getList();
this.setData({ list: res.data });
}
});
从代码中可以发现,Okam 直接赋值 this.list 即可触发视图更新,而 Tina.js 必须显式调用 setData。如果忽略这一点,迁移后页面很可能出现数据不渲染的情况。因此建议在迁移初期对每个页面的数据更新点做一次全局搜索,确保所有直接赋值都被替换为 setData 调用。
二、页面结构与自定义组件迁移实战
先处理静态结构,再迁移逻辑,是比较稳妥的顺序。Okam 模板里的 <view>、<text> 等基础标签可以直接保留,因为百度小程序原生语法本身就支持这些标签。真正需要调整的是指令、事件绑定和插槽写法。例如 Okam 中的 @click="handleClick" 要改成 bindtap="handleClick",v-model 在小程序中没有直接对应,需要拆分为 value 属性和 bindinput 事件,手动处理数据同步。
自定义组件的迁移尤其要注意属性类型声明和事件派发。Okam 组件中定义的 props 迁到 Tina.js 后要放入 properties 对象,并且每个属性需要声明类型,例如 value: { type: String, value: '' }。组件插槽方面,Okam 支持具名插槽,而百度小程序原生只支持默认插槽和多个 slot 的具名方式,但写法不同,需要在 options 中开启 multipleSlots: true,并使用 name 属性标记插槽。
以下是一个购物车商品卡片组件的迁移示例。原 Okam 组件通过 $emit 通知父组件数量变化,Tina.js 则改为 triggerEvent:
// Okam 组件逻辑
export default {
props: {
product: Object
},
methods: {
increase() {
this.$emit('count-change', { id: this.product.id, count: this.product.count + 1 });
}
}
};
// Tina.js 组件逻辑(component.js)
Component({
properties: {
product: {
type: Object,
value: {}
}
},
methods: {
increase() {
const { id, count } = this.data.product;
this.triggerEvent('count-change', { id, count: count + 1 });
}
}
});
父组件监听事件时,Okam 中写 @count-change="onCountChange",Tina.js 中需要改为 bind:count-change="onCountChange" 或 bindcountchange="onCountChange"。另外,组件样式隔离策略也不同:Okam 默认样式作用域是局部的,迁移到 Tina.js 后,如果组件样式出现覆盖冲突,需要检查 json 文件中的 styleIsolation 配置,按需设置为 isolated 或 apply-shared。
对于页面级组件较多的项目,建议先提取公共组件做迁移试点,验证事件链路和样式作用域后再批量推进。迁移过程中可以在原 Okam 项目和新 Tina.js 项目中同时运行同一个页面,用真机对比表现,避免因为模拟器与真机差异漏掉问题。
三、状态管理与接口请求的迁移
Okam 项目如果引入了类 Vuex 的状态管理库,迁移到 Tina.js 后需要根据业务复杂度重新选型。对于状态较少、页面间共享数据只有用户信息和购物车数量的场景,最简单的做法是使用 getApp() 上的全局数据配合事件通知,避免引入额外依赖。如果原项目大量依赖计算属性和模块化 store,推荐使用 mobx-miniprogram 配合 mobx-miniprogram-bindings,它可以在小程序里提供类似响应式的数据绑定,迁移成本相对低。
接口请求层的迁移重点在于保持统一的错误处理和身份认证逻辑。Okam 中通常在 api 目录下封装请求函数,并在组件内直接导入使用。Tina.js 没有强制约束请求层,但建议将请求封装挂载到 app 实例上,通过 getApp().request 调用,这样可以统一处理 token 失效跳转登录、请求重试和错误提示。原 Okam 项目中的拦截器逻辑可以平移到 Tina.js 的请求封装中,核心代码几乎可以原样保留。
下面是一个简单的请求封装对比。Okam 侧可能使用 Promise 链式调用,Tina.js 侧同样基于 Promise,但需要手动处理 token 过期后的页面跳转:
// Okam 请求封装示例
export function request(url, data) {
return new Promise((resolve, reject) => {
swan.request({
url,
data,
success(res) {
if (res.data.code === 401) {
// 跳转登录
} else {
resolve(res.data);
}
},
fail: reject
});
});
}
// Tina.js 请求封装示例(挂载到 app 上)
App({
onLaunch() {
this.initRequest();
},
initRequest() {
const app = this;
app.request = function (url, data) {
return new Promise((resolve, reject) => {
swan.request({
url,
data,
success(res) {
if (res.data.code === 401) {
swan.reLaunch({ url: '/pages/login/login' });
reject(res.data);
} else {
resolve(res.data);
}
},
fail: reject
});
});
};
}
});
状态管理迁移后,要特别关注页面生命周期中数据重置的时机。原 Okam 的 created 在每次进入页面时都会执行,而 Tina.js 的 onLoad 只在页面首次加载时触发一次。如果某个页面需要在从后台切回前台时刷新数据,Tina.js 中应当把刷新逻辑放到 onShow 中,而不是保留在 onLoad 里,否则返回页面时数据可能不更新。
四、工程配置与构建脚本调整
Okam 项目通常依赖 okam-build 或自定义 webpack 配置来实现单文件编译、热更新和代码压缩。Tina.js 没有提供官方的构建打包工具,它更推荐直接使用开发者工具打开项目目录,或者配合 gulp 做样式和静态资源处理。迁移时首先要调整 package.json 中的脚本命令,移除 Okam 相关的构建依赖,替换为适合原生小程序开发的轻量脚本,例如使用 gulp 编译 less 或 sass,但不要过度工程化,否则会增加维护成本。
配置文件方面,app.json 中的 pages、window、tabBar 等结构与 Okam 基本兼容,但需要留意 usingComponents 的全局注册。Okam 项目中自定义组件可能在入口统一注册,而 Tina.js 要求每个使用组件的页面或组件都要在自己的 json 文件中声明,或者通过 app.json 的全局 usingComponents 一次性注册。推荐将高频使用的公共组件放到全局配置里,页面私有组件则在页面级配置中声明。
构建配置的另一个常见坑是路径映射。Okam 使用别名 @ 指向 src 目录,迁移到 Tina.js 后别名不一定生效,所有相对路径导入都需要改成实际相对路径。可以用编辑器的全局搜索替换功能,把 @/components/ 批量替换为 ../../components/。此外,Tina.js 对 ES6 语法支持较好,但部分百度小程序基础库版本对 async/await 的兼容性仍有差异,上生产环境前需要在开发者工具中打开增强编译或转译选项。
调试阶段建议保留原 Okam 项目的可运行版本,使用双目录并行的方式做灰度切换。每次迁移一个页面后就在开发者工具中编译,关注控制台的组件注册警告和页面执行顺序日志。由于 Tina.js 的调试体验更接近原生小程序,原来依赖 Okam 框架日志定位问题的经验需要调整,重点检查 setData 的调用频率和数据大小,避免频繁全量更新导致卡顿。
整体来看,Okam 到 Tina.js 的迁移更像一次从封装思维回归原生思维的过程。虽然初期会觉得模板指令和单文件组件被拆散后不够顺手,但换来的是更直接的调试链路和更低的运行时依赖。迁移完成后,项目对百度小程序新特性的跟进速度也会更快,因为不再受框架层的版本限制。只要按照页面结构、组件通信、状态管理和工程配置四个维度逐项落地,就能把切换风险控制在一个可控范围内。