3、项目结构设计:抖音小程序目录规范、全局配置(app.json)、页面配置(page.json)、组件注册机制

好,咱们进入第三讲。这一章我打算聊聊项目结构设计。说白了,就是你的代码该怎么摆,文件该怎么放。

很多新手开发者上来就写代码,写到后面发现目录乱成一锅粥。我自己刚做小程序那会儿也踩过这个坑——找文件要找半天,组件引用全靠猜路径。后来被坑了几次,才老老实实按规范来。

3.1 抖音小程序目录规范

抖音小程序的目录结构,其实和微信小程序很像。但有几个细节不一样,我重点说。

先看一个标准的项目目录长什么样:

my-miniapp/
├── app.json              # 全局配置
├── app.js                # 全局逻辑
├── app.ttss              # 全局样式
├── project.config.json   # 项目配置
├── pages/                # 页面目录
│   ├── index/
│   │   ├── index.js
│   │   ├── index.ttml
│   │   ├── index.ttss
│   │   └── index.json
│   └── detail/
│       ├── detail.js
│       ├── detail.ttml
│       ├── detail.ttss
│       └── detail.json
├── components/           # 公共组件
│   ├── header/
│   │   ├── header.js
│   │   ├── header.ttml
│   │   ├── header.ttss
│   │   └── header.json
│   └── footer/
│       ├── footer.js
│       ├── footer.ttml
│       ├── footer.ttss
│       └── footer.json
├── utils/                # 工具函数
│   ├── api.js
│   └── util.js
└── images/               # 静态资源
    └── logo.png

核心要点:

  • 每个页面一个文件夹,文件夹名就是页面名
  • 页面文件必须包含 .js、.ttml、.ttss、.json 四个文件(.ttss 可以没有)
  • 组件目录和页面目录结构一致,但放在 components/ 下

嗯,这里要注意:抖音小程序的模板文件后缀是 .ttml,样式文件后缀是 .ttss。这和微信的 .wxml.wxss 不一样。我第一次迁移项目时,就因为后缀名没改,编译报错了半天。

3.2 全局配置 app.json

app.json 是整个小程序的配置文件。它决定了你的小程序长什么样、有哪些页面、用什么窗口样式。

我直接给一个生产环境的配置示例:

{
  "pages": [
    "pages/index/index",
    "pages/detail/detail",
    "pages/user/user"
  ],
  "window": {
    "navigationBarTitleText": "我的小程序",
    "navigationBarBackgroundColor": "#ffffff",
    "navigationBarTextStyle": "black",
    "backgroundColor": "#f5f5f5",
    "backgroundTextStyle": "dark"
  },
  "tabBar": {
    "color": "#999999",
    "selectedColor": "#ff5500",
    "backgroundColor": "#ffffff",
    "borderStyle": "black",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "images/home.png",
        "selectedIconPath": "images/home_active.png"
      },
      {
        "pagePath": "pages/user/user",
        "text": "我的",
        "iconPath": "images/user.png",
        "selectedIconPath": "images/user_active.png"
      }
    ]
  },
  "networkTimeout": {
    "request": 10000,
    "connectSocket": 5000,
    "uploadFile": 30000
  },
  "debug": false
}

避坑指南:

我曾经在配置 tabBar 时,把 pagePath 写成了绝对路径(比如 /pages/index/index),结果 tab 死活不显示。后来才发现,这里必须用相对路径,不能加斜杠开头。

几个关键配置项,我挑重点说:

配置项 说明 我的建议
pages 页面路径列表,第一项是首页 按业务模块分组排列,方便维护
window 全局窗口样式 导航栏颜色统一用品牌色
tabBar 底部导航栏 最多5个tab,图标用纯色SVG
networkTimeout 网络超时时间 请求超时建议10秒以上
debug 调试模式开关 上线前务必设为 false

注意:

pages 数组的顺序很重要。第一项就是小程序的首页。如果你改了顺序,首页也会变。我有个同事就因为这个,把用户引导页和首页搞反了,上线后用户直接看到了引导页...嗯,那场面挺尴尬的。

3.3 页面配置 page.json

每个页面都可以有自己的 .json 配置文件。它会覆盖 app.json 中的全局配置。

举个例子,你的首页可能不需要导航栏,但详情页需要:

// pages/index/index.json
{
  "navigationBarTitleText": "首页",
  "navigationStyle": "custom",  // 自定义导航栏
  "enablePullDownRefresh": true,
  "backgroundColor": "#f8f8f8"
}

// pages/detail/detail.json
{
  "navigationBarTitleText": "商品详情",
  "navigationBarBackgroundColor": "#ff5500",
  "navigationBarTextStyle": "white",
  "enablePullDownRefresh": false
}

我个人习惯把页面级别的配置都写在 page.json 里,而不是全局配置。为什么?因为每个页面的需求不一样。比如首页需要下拉刷新,但详情页不需要。你想想看,如果都写在全局配置里,那所有页面都得跟着改,多麻烦。

3.4 组件注册机制

抖音小程序的组件注册,分两种:全局注册和局部注册。

3.4.1 全局注册

app.json 中注册,所有页面都能用:

{
  "usingComponents": {
    "my-header": "/components/header/header",
    "my-footer": "/components/footer/footer"
  }
}

3.4.2 局部注册

在页面的 .json 中注册,只有当前页面能用:

// pages/index/index.json
{
  "usingComponents": {
    "custom-card": "/components/card/card"
  }
}

我的经验:

全局注册虽然方便,但会增加包体积。我建议只把那些真正通用的组件(比如导航栏、底部栏)注册到全局。业务组件尽量局部注册,按需加载。

组件本身的结构和页面一样,但多了一个 Component 构造器:

// components/header/header.js
Component({
  properties: {
    title: {
      type: String,
      value: '默认标题'
    },
    showBack: {
      type: Boolean,
      value: false
    }
  },
  data: {
    // 内部数据
  },
  methods: {
    onBack() {
      this.triggerEvent('back')
    }
  }
})

组件模板里用 properties 接收外部传值:

<!-- components/header/header.ttml -->
<view class="header">
  <view class="back" tt:if="{{showBack}}" bindtap="onBack">
    <text>返回</text>
  </view>
  <text class="title">{{title}}</text>
</view>

在页面中使用组件:

<!-- pages/index/index.ttml -->
<my-header title="首页" showBack="{{false}}" bind:back="onHeaderBack" />

避坑指南:

我曾经在组件里用 this.data 直接修改数据,结果视图没更新。后来才想起来,组件里也要用 this.setData() 才行。嗯,这个坑我印象特别深,因为当时排查了整整一个下午。

3.5 总结一下

这一章的内容其实不难,但很重要。目录规范决定了你的项目好不好维护,配置项决定了你的小程序长什么样,组件注册机制决定了代码能不能复用。

我个人建议,在项目一开始就把目录结构搭好,配置项写清楚。别等到代码写了一半再来重构,那滋味可不好受。

下一章我会讲页面生命周期和数据通信,到时候咱们再聊。