Tiktok小程序开发(使用uniapp框架)
Tiktok minis与传统小程序对比
TikTok Minis 不是传统小程序页面运行时,更接近 运行在 TikTok 客户端 WebView 里的 Web 应用方案。
- 页面本身仍然是标准 Web 工程
- 客户端能力通过TTMinis 暴露
开发Tiktok minis
安装命令行界面工具
注意node版本不能太低,通常要求大于18
npm install tiktok-minis-cli -g --registry=https://registry.npmjs.org/
指定 npm 仓库地址(官方源):–registry=https://registry.npmjs.org/
查看安装结果
minis -v
使用minis init生成配置文件 minis.config.json
minis init
执行minis init,提示用户确认项
√ Current app local dev server Node port: 3000
√ Business page local host (optional, default: localhost): localhost
√ Build output directory: dist
√ HTML entry relative path: index.html
Build output directory默认是dist,uniapp项目应改为打包H5所在的web目录,例如:unpackage/dist/build/web
完成后在项目根目录生成minis.config.json文件,如下
{
"dev": {
// Minis 应用项目本地启动时候的端口
"port": "3000",
// Minis 应用项目本地启动时候的 host,可以不填
"host": "localhost",
},
"build": {
// minis 应用构建后的文件目录
"outputDir": "unpackage/dist/build/web",
// minis 应用源代码 index.html 在项目中的相对路径
"htmlEntry": "public/index.html"
},
"domain": {
// 配置白名单
"trustedDomains": ["https://**.com/"],
// 兼容不同版本
"allowList": ["https://**.com/"]
}
}
初始化 TikTok Minis SDK
将 TikTok Minis SDK 添加到您的 index.html 文件中,并使用您项目的唯一客户端密钥初始化它。注意这段代码需要放入打包后的index.html内。
UniApp 项目是用 HBuilderX 或者 vue-cli 构建的 ,打包到 web 的 index.html 默认是由系统生成的,所以会去掉了之前加在根目录 index.html 里面的 SDK脚本,会造成 minis build 校验报错。
所以最方便的方式是设置脚本,让每次打包自动在打包后的unpackage/dist/build/web/index.html带上TikTok Minis SDK并初始化
1、在根目录创建scripts文件夹
2、在scripts文件夹内创建inject-minis-sdk.js文件,在每次执行构建后,自动把 TikTok Minis SDK 初始化代码注入到打包产物的 unpackage/dist/build/web/index.html里,并且避免重复注入。代码如下,其中const CLIENT_KEY = ‘your_client_key_here’;改为真实CLIENT_KEY
const fs = require('fs');
const path = require('path');
const CLIENT_KEY = 'your_client_key_here';
const SDK_SRC = 'https://connect.tiktok-minis.com/drama/sdk.js';
const targetFile = path.resolve(__dirname, '../unpackage/dist/build/web/index.html');
function fail(message) {
console.error(`[inject-minis-sdk] ${message}`);
process.exit(1);
}
if (!fs.existsSync(targetFile)) {
fail(`未找到构建产物: ${targetFile}。请先完成 H5 打包。`);
}
let html = fs.readFileSync(targetFile, 'utf8');
const sdkScriptRegex = /<script[^>]*src=["']https:\/\/connect\.tiktok-minis\.com\/drama\/sdk\.js["'][^>]*><\/script>/ig;
const initScriptRegex = /<script>\s*TTMinis\.init\(\{[\s\S]*?clientKey\s*:\s*["'][^"']+["'][\s\S]*?\}\);\s*<\/script>/ig;
html = html.replace(sdkScriptRegex, '');
html = html.replace(initScriptRegex, '');
const snippet = `<script src="${SDK_SRC}"></script><script>TTMinis.init({clientKey:'${CLIENT_KEY}'});</script>`;
const headMatch = html.match(/<head[^>]*>/i);
if (!headMatch) {
fail('未找到 <head> 标签,无法注入 Minis SDK。');
}
const headStart = headMatch.index + headMatch[0].length;
const tail = html.slice(headStart);
const firstScriptOffset = tail.search(/<script\b/i);
if (firstScriptOffset >= 0) {
const insertAt = headStart + firstScriptOffset;
html = `${html.slice(0, insertAt)}${snippet}${html.slice(insertAt)}`;
} else {
const closeHeadOffset = tail.search(/<\/head>/i);
if (closeHeadOffset < 0) {
fail('未找到 </head> 标签,无法注入 Minis SDK。');
}
const insertAt = headStart + closeHeadOffset;
html = `${html.slice(0, insertAt)}${snippet}${html.slice(insertAt)}`;
}
fs.writeFileSync(targetFile, html, 'utf8');
console.log(`[inject-minis-sdk] 注入完成: ${targetFile}`);
添加完成后,在minis build后会在unpackage/dist/build/web/index.html内自动注入如下代码,作用是完成TikTok Minis SDK的引入并初始化
<!-- index.html -->
<head>
<script src="https://connect.tiktok-minis.com/drama/sdk.js"></script>
<script>
TTMinis.init({
clientKey: "your_client_key_here",
});
</script>
</head>
TikTok Minis SDK (TTMinis):一个在 TikTok 应用内运行的客户端 JavaScript SDK。它提供了应用内能力,如登录和授权、支付、UI 触发器、奖励广告、生命周期钩子、网络和功能检测。
在项目根目录下创建package.json文件
代码如下
{
"scripts": {
"dev": "node scripts/serve-web-build.js",
"start": "node scripts/serve-web-build.js",
"minis:prepare": "node scripts/inject-minis-sdk.js",
"minis:build": "npm run minis:prepare && npx -y tiktok-minis-cli build",
"minis:dev:raw": "npm run minis:prepare && npx -y tiktok-minis-cli dev",
"minis:dev": "node scripts/minis-dev-safe.js"
}
}
TikTok小程序本地测试
如果没有使用到TikTok Minis SDK的功能,可直接基于浏览器进行开发调试。涉及到登录、授权、广告、支付、订阅等依赖客户端环境才需要如下操作进行本地调试
1、本地调试需要在项目根目录的index.html也要添加TikTok Minis SDK的引入并初始化
<!-- index.html -->
<head>
<script src="https://connect.tiktok-minis.com/drama/sdk.js"></script>
<script>
TTMinis.init({
clientKey: "your_client_key_here",
});
</script>
</head>
完整代码如下
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<!-- Tiktok Minis Init Start -->
<script src="https://connect.tiktok-minis.com/drama/sdk.js"></script>
<script>
TTMinis.init({
clientKey: 'your_client_key_here',
});
</script>
<!-- Tiktok Minis Init End -->
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<link rel="icon" type="image/svg+xml" href="static/favicon.svg" />
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>
2、在根目录创建的scripts文件夹内创建minis-dev-safe.js文件,作用是自动清端口 -> 自动同步 IP -> 自动起本地服务 -> 启动 minis”的一键流程。代码如下
const { execSync, spawn } = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
const BRIDGE_PORT = 3000;
const DEFAULT_MINIS_HOST = '127.0.0.1';
const DEFAULT_MINIS_PORT = 18080;
function getListeningPidsByPort(port) {
const output = execSync('netstat -ano -p tcp', {
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'utf8'
});
const pids = new Set();
const lines = output.split(/\r?\n/);
for (const line of lines) {
const normalized = line.trim().replace(/\s+/g, ' ');
if (!normalized.startsWith('TCP ')) {
continue;
}
const parts = normalized.split(' ');
if (parts.length < 5) {
continue;
}
const localAddress = parts[1] || '';
const state = (parts[3] || '').toUpperCase();
const pid = parts[4];
const portSuffix = `:${port}`;
if (state === 'LISTENING' && localAddress.endsWith(portSuffix) && /^\d+$/.test(pid)) {
pids.add(pid);
}
}
return Array.from(pids);
}
function killPid(pid) {
execSync(`taskkill /PID ${pid} /F`, {
stdio: ['ignore', 'ignore', 'ignore']
});
}
function clearPort(port) {
const pids = getListeningPidsByPort(port);
if (pids.length === 0) {
console.log(`[minis-dev-safe] ${port} 端口空闲`);
return;
}
for (const pid of pids) {
try {
killPid(pid);
console.log(`[minis-dev-safe] 已释放 ${port} 端口,占用 PID: ${pid}`);
} catch (error) {
console.warn(`[minis-dev-safe] 释放 PID ${pid} 失败: ${error.message}`);
}
}
}
function runMinisDevRaw() {
try {
execSync('npm run minis:dev:raw', {
stdio: 'inherit'
});
} catch (error) {
process.exit(typeof error.status === 'number' ? error.status : 1);
}
}
function readMinisConfig() {
const configPath = path.resolve(__dirname, '../minis.config.json');
try {
const text = fs.readFileSync(configPath, 'utf8');
const parsed = JSON.parse(text);
const host = (parsed.dev && parsed.dev.host) || DEFAULT_MINIS_HOST;
const port = Number((parsed.dev && parsed.dev.port) || DEFAULT_MINIS_PORT);
return { host, port };
} catch (error) {
return { host: DEFAULT_MINIS_HOST, port: DEFAULT_MINIS_PORT };
}
}
function getPreferredLanIPv4() {
const interfaces = os.networkInterfaces();
const candidates = [];
for (const entries of Object.values(interfaces)) {
for (const entry of entries || []) {
if (!entry || entry.family !== 'IPv4' || entry.internal) {
continue;
}
const ip = entry.address || '';
if (ip.startsWith('169.254.')) {
continue;
}
const score = ip.startsWith('192.168.') ? 3 : ip.startsWith('10.') ? 2 : ip.startsWith('172.') ? 1 : 0;
candidates.push({ ip, score });
}
}
if (candidates.length === 0) {
return null;
}
candidates.sort((a, b) => b.score - a.score);
return candidates[0].ip;
}
function syncMinisHostToLanIp() {
const configPath = path.resolve(__dirname, '../minis.config.json');
const current = readMinisConfig();
const lanIp = getPreferredLanIPv4();
if (!lanIp || current.host === lanIp) {
return current;
}
try {
const text = fs.readFileSync(configPath, 'utf8');
const parsed = JSON.parse(text);
parsed.dev = parsed.dev || {};
parsed.dev.host = lanIp;
if (!parsed.dev.port) {
parsed.dev.port = DEFAULT_MINIS_PORT;
}
fs.writeFileSync(configPath, `${JSON.stringify(parsed, null, '\t')}\n`, 'utf8');
console.log(`[minis-dev-safe] 已自动更新 minis.config.json dev.host: ${current.host} -> ${lanIp}`);
return { host: lanIp, port: Number(parsed.dev.port) || DEFAULT_MINIS_PORT };
} catch (error) {
console.warn(`[minis-dev-safe] 自动更新 dev.host 失败: ${error.message}`);
return current;
}
}
function startServeWebBuild(host, port) {
const scriptPath = path.resolve(__dirname, './serve-web-build.js');
const child = spawn(process.execPath, [scriptPath], {
detached: true,
stdio: 'ignore',
env: {
...process.env,
HOST: host,
PORT: String(port)
}
});
child.unref();
console.log(`[minis-dev-safe] 已启动本地应用服务: http://${host}:${port}`);
}
const minisConfig = syncMinisHostToLanIp();
clearPort(BRIDGE_PORT);
clearPort(minisConfig.port);
startServeWebBuild(minisConfig.host, minisConfig.port);
runMinisDevRaw();
3、在根目录创建的scripts文件夹内创建serve-web-build.js文件,这个文件是一个“本地静态资源服务器”,专门把 H5 打包产物目录对外提供给 TikTok Minis 调试环境访问,同时补上 CORS,保证手机端/调试页能正常访问你的 H5 构建产物。代码如下
const fs = require('fs');
const path = require('path');
const http = require('http');
const HOST = process.env.HOST || 'localhost';
const PORT = Number(process.env.PORT || 8080);
const ROOT_DIR = path.resolve(__dirname, '../unpackage/dist/build/web');
const MIME_TYPES = {
'.html': 'text/html; charset=utf-8',
'.js': 'application/javascript; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.webp': 'image/webp',
'.ico': 'image/x-icon',
'.woff': 'font/woff',
'.woff2': 'font/woff2'
};
function send(res, status, body, type) {
const requestOrigin = res.req && res.req.headers ? res.req.headers.origin : '';
const allowOrigin = requestOrigin || '*';
res.writeHead(status, {
'Content-Type': type || 'text/plain; charset=utf-8',
'Cache-Control': 'no-cache',
'Access-Control-Allow-Origin': allowOrigin,
'Access-Control-Allow-Methods': 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type,Authorization,X-Requested-With',
'Access-Control-Max-Age': '86400',
Vary: 'Origin'
});
res.end(body);
}
function safeResolve(urlPath) {
const cleanPath = decodeURIComponent(urlPath.split('?')[0]).replace(/\\/g, '/');
const normalized = path.posix.normalize(cleanPath);
const relative = normalized.startsWith('/') ? normalized.slice(1) : normalized;
const resolved = path.resolve(ROOT_DIR, relative);
if (!resolved.startsWith(ROOT_DIR)) {
return null;
}
return resolved;
}
if (!fs.existsSync(ROOT_DIR)) {
console.error('[serve-web-build] 未找到目录:', ROOT_DIR);
console.error('[serve-web-build] 请先进行 H5 打包,确保存在 unpackage/dist/build/web');
process.exit(1);
}
const server = http.createServer((req, res) => {
if ((req.method || '').toUpperCase() === 'OPTIONS') {
send(res, 204, '', 'text/plain; charset=utf-8');
return;
}
const filePath = safeResolve(req.url || '/');
if (!filePath) {
send(res, 403, 'Forbidden');
return;
}
let target = filePath;
if (fs.existsSync(target) && fs.statSync(target).isDirectory()) {
target = path.join(target, 'index.html');
}
if (!fs.existsSync(target)) {
const fallback = path.join(ROOT_DIR, 'index.html');
if (fs.existsSync(fallback)) {
const html = fs.readFileSync(fallback);
send(res, 200, html, 'text/html; charset=utf-8');
return;
}
send(res, 404, 'Not Found');
return;
}
const ext = path.extname(target).toLowerCase();
const mimeType = MIME_TYPES[ext] || 'application/octet-stream';
const content = fs.readFileSync(target);
send(res, 200, content, mimeType);
});
server.listen(PORT, HOST, () => {
console.log(`[serve-web-build] Serving ${ROOT_DIR}`);
console.log(`[serve-web-build] http://${HOST}:${PORT}`);
});
4、安装内测客户端,目前只支持安卓;iOS 需要上传到平台后扫码查看效果
M包(地区:US等)
T包(地区:JP等)
5、在项目根目录终端运行测试
npm run minis:dev
运行成功后,浏览器如图,然后使用第四步下载的Tiktok安装包登录后扫码

(1.)保证电脑和手机处在连接一个 WIFI 局域网
(2.)保证电脑和手机均可访问外网
(3.)需要在Tiktok开发者后台添加测试权限
(4.)在手机端需要前台运行tiktok、屏幕保持常亮。息屏或者切屏会断连
(5.)不支持热更新,代码更改后,需要先重新打包项目然后再运行npm run minis:dev

测试完成后,TikTok小程序打线上安装包
(1.)先执行打包项目
(2.)在根目录下执行npm run minis:build,完成打包
npm run minis:build

打包完成后安装包路径如上图:minis build completed successfully. Archive output: C:\Users\admin_TTMinis_\mnw636fnrz67y91r\minis.zip。也可自行把编译后的项目文件压缩为.zip
如果你直接使用 minis build(不是 npm run minis:build),npm 脚本不会触发,自动注入也不会执行。
需要集成这些功能
| 功能 | APIs | 描述 | 必需 |
|---|---|---|---|
| 静默登录 | TTMinis.login https://open.tiktokapis.com/v2/oauth/token/ | 在不进行明确授权的情况下获取用户的 OpenID 和访问令牌 | 是 |
| 明确授权 | TTMinis.authorize https://open.tiktokapis.com/v2/oauth/token/ https://open.tiktokapis.com/v2/user/info/ | 获取用户名和头像等额外用户数据,需要通过授权屏幕获取用户同意 | 否 |
| 应用内广告:激励视频广告 | TTMinis.createRewardedVideoAd | 将激励视频广告插入您的小程序;必须启用 IAA 才能进行配置 | 是 |
| 应用内广告:插屏广告 | TTMinis.createInterstitialAd | 将插屏广告插入您的小程序;必须启用 IAA 才能配置 | 是 |
| 应用内购买:金币(一次性付款) | TTMinis.pay https://open.tiktokapis.com/v2/minis/trade_order/create/ | 允许在您的小程序内使用金币进行一次性购买;必须启用 IAP 才能配置 | 是 |
| 应用内购买:订阅 | TTMinis.createSubscription https://open.tiktokapis.com/v2/minis/subscription/create/ | 允许在您的小程序内购买订阅;必须启用 IAP 才能配置 | 是 |
| 导航栏 | TTMinis.setNavigationBarColor TTMinis.getMenuButtonBoundingClientRect | 在您的小游戏中设置和自定义导航栏 | 是 |
示例代码
静默登录
TTMinis.login((res) => {
const code = res.authResponse.code;
if (code) {
// 业务逻辑接口
} else {
// 处理异常
}
});
切记,登录权限需要在TikTok开发者后台完善基本信息,尤其是《服务条款》和《隐私政策》,否则获取TTMinis.login报错

激励视频广告IAA(In-App Ads)
// 在<script>标签内声明变量
let rewardedAd = null
// 初始化激励视频广告方法
initTiktokAd() {
// 初始化
rewardedAd = TTMinis.createRewardedVideoAd({
adUnitId: 'rewarded_ad_unit',
});
// 监控广告视频的关闭
rewardedAd.onClose((res) => {
if (res.isEnded) {
console.log('用户完整观看激励视频');
// 业务逻辑接口
} else {
console.log('用户中途关闭激励视频');
// 中途退出或者播放完成后,广告实例会被释放,您需要重新创建广告实例
this.initTiktokAd()
}
});
},
// 在合适的地方调用广告展示方法
showAd() {
rewardedAd.show().catch((err) => {
console.error('rewardedAd show failed:', err);
});
},
切记,在广告播放后,需要调用initTiktokAd()重置广告
应用内购买IAP(In-App Purchases)
TTMinis.pay((res) => {
if (res.is_success) {
// 业务逻辑接口
} else {
}
}, {
trade_order_id: 'trade_order_id',
});
分享
TTMinis.on('minis.share', (share) => {
TTMinis.share((res) => {
if (res.error) {
console.error('share failed', res.error);
return;
}
console.log('share success');
}, {
title: ‘分享标题’,
desc: ‘分享描述’,
imageUrl: ‘分享图片地址’,
query: {
// 分享透传参数
}
})
})
TTMinis.share() 需要与 TTMinis.on(‘minis.share’, callback) 配合使用。TikTok 小程序链接由监听小程序的 share 消息(由客户端用户触发)并在消息处理回调中执行 TTMinis.share() 生成。
页面截屏与关闭页面截屏
TTMinis.enableCapture((res)=> {
console.log('enableCapture:', res.isSuccess, res.error);
});
TTMinis.disableCapture((res)=> {
console.log('disableCapture:', res.isSuccess, res.error);
});
卸载命令行界面工具
npm uninstall -g tiktok-minis-cli
参考文档
Tiktok开发者官网:
https://developers.tiktok.com/
Tiktok官方文档:
https://developers.tiktok.com/doc/overview
更多推荐
所有评论(0)