红糖云服-小程序

2. Web 小程序开发

TodoList Web 小程序开发流程

本文演示一条完整链路:用 @htyf-mp/cli 选择 web-template 创建项目(结构与 _web_temp_ 同类),把首页改成 TodoList,再通过 CLI 进行真机调试。

你将获得什么

  • 一个可直接运行的 Taro + React TodoList 页面。
  • 一条从 CLI 初始化到扫码真机调试的最短路径。
  • _web_temp_ 同类模板关键文件的定位能力。

适用模板

本篇面向 web-template(Taro + React)模板。初始化后的目录结构与仓库中的 _web_temp_ 基本一致,包含 src/app.config.tssrc/pages/index/index.tsxsrc/pages/index/index.less 等文件。

与 app-template 的区别

web-template 主要是 Taro 页面开发,不需要 npm run ios / npm run android
如果你要看 RN 小程序(含导航/Tab 注册)版本,请看 TodoList 小程序开发流程演示


流程总览

阶段操作
1. 初始化项目空目录执行 npx @htyf-mp/cli,选择 web-template
2. 安装依赖进入项目目录后 npm install
3. 写 TodoList替换 src/pages/index/index.tsx,并补充 index.less
4. 本地调试先用 npm run dev:h5 快速改页面,随后用 CLI 真机调试
5. 真机调试npm run htyf -> 选择「小程序 - 真机调试」-> 扫码

1) 用 CLI 生成 web-template 项目

在空目录执行:

npx @htyf-mp/cli

按提示选择并填写:

  • 选择:🆕 初始化新小程序项目
  • 应用程序目录名称:例如 todolist-web
  • 应用程序名称:例如 TodoList
  • 模板类型:web-template
  • 模板镜像:例如 GitHub (最新)

完成后进入目录并安装依赖:

cd todolist-web
npm install

2) 关键模板结构(与 _web_temp_ 同类)

初始化后常见结构:

todolist-web/
  app.json
  package.json
  src/
    app.ts
    app.config.ts
    pages/
      index/
        index.tsx
        index.less
        index.config.ts

你可以先确认 src/app.config.ts 中已注册首页:

export default defineAppConfig({
  pages: ['pages/index/index'],
});

2.1 与模板 README 对齐的配置要点

基于 _web_temp_/RADME.md(模板说明),web-templateapp.json 重点如下:

  • type:应为 web
  • webUrl:WebView 实际加载的网页地址(Web 模板核心字段)。
  • appUrlConfig:应用配置/元数据地址。
  • zipUrl:静态资源包地址,用于分发和更新。
  • versionengines:版本与引擎信息,需与发布节奏保持一致。

可这样记:webUrl 决定“打开哪个网页”,appUrlConfig 决定“读哪份配置”,zipUrl 决定“更新哪份资源包”。


3) 写 TodoList 页面代码

3.1 替换 src/pages/index/index.tsx

import { useMemo, useState } from 'react';
import { View, Text, Input, Button, ScrollView } from '@tarojs/components';
import './index.less';
 
type TodoItem = {
  id: string;
  title: string;
  done: boolean;
};
 
export default function Index() {
  const [text, setText] = useState('');
  const [items, setItems] = useState<TodoItem[]>([
    { id: '1', title: '完成 CLI 初始化', done: true },
    { id: '2', title: '替换首页为 TodoList', done: false },
  ]);
 
  const remain = useMemo(() => items.filter((i) => !i.done).length, [items]);
 
  const add = () => {
    const value = text.trim();
    if (!value) return;
    setItems((prev) => [
      ...prev,
      {
        id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
        title: value,
        done: false,
      },
    ]);
    setText('');
  };
 
  const toggle = (id: string) => {
    setItems((prev) =>
      prev.map((it) => (it.id === id ? { ...it, done: !it.done } : it)),
    );
  };
 
  const remove = (id: string) => {
    setItems((prev) => prev.filter((it) => it.id !== id));
  };
 
  return (
    <View className="todo-page">
      <View className="todo-header">
        <Text className="title">TodoList</Text>
        <Text className="desc">剩余 {remain} 项</Text>
      </View>
 
      <View className="todo-input-row">
        <Input
          className="todo-input"
          value={text}
          placeholder="输入待办事项"
          onInput={(e) => setText(e.detail.value)}
          onConfirm={add}
        />
        <Button className="add-btn" onClick={add}>
          添加
        </Button>
      </View>
 
      <ScrollView className="todo-list" scrollY>
        {items.length === 0 ? (
          <Text className="empty">暂无待办,先添加一条</Text>
        ) : (
          items.map((item) => (
            <View key={item.id} className="todo-item">
              <Text
                className={`check ${item.done ? 'check-done' : ''}`}
                onClick={() => toggle(item.id)}
              >
                {item.done ? '✓' : '○'}
              </Text>
              <Text className={`todo-title ${item.done ? 'todo-title-done' : ''}`}>
                {item.title}
              </Text>
              <Text className="del" onClick={() => remove(item.id)}>
                删除
              </Text>
            </View>
          ))
        )}
      </ScrollView>
    </View>
  );
}

3.2 替换 src/pages/index/index.less

.todo-page {
  min-height: 100vh;
  background: #f6f7fb;
  padding: 28px 24px;
  box-sizing: border-box;
}
 
.todo-header {
  margin-bottom: 20px;
}
 
.title {
  display: block;
  font-size: 40px;
  font-weight: 700;
  color: #121826;
}
 
.desc {
  display: block;
  margin-top: 8px;
  font-size: 24px;
  color: #6b7280;
}
 
.todo-input-row {
  display: flex;
  gap: 12px;
  margin-bottom: 18px;
}
 
.todo-input {
  flex: 1;
  background: #fff;
  border: 1px solid #d7dce5;
  border-radius: 12px;
  padding: 0 14px;
  height: 76px;
  line-height: 76px;
  font-size: 28px;
  box-sizing: border-box;
}
 
.add-btn {
  height: 76px;
  line-height: 76px;
  padding: 0 24px;
  border-radius: 12px;
  border: none;
  background: #2f7bff;
  color: #fff;
  font-size: 28px;
}
 
.todo-list {
  max-height: calc(100vh - 260px);
}
 
.empty {
  display: block;
  margin-top: 40px;
  text-align: center;
  font-size: 24px;
  color: #9ca3af;
}
 
.todo-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 12px;
  background: #fff;
  border-radius: 12px;
  margin-bottom: 10px;
}
 
.check {
  font-size: 34px;
  color: #2f7bff;
  width: 46px;
  text-align: center;
}
 
.check-done {
  color: #10b981;
}
 
.todo-title {
  flex: 1;
  font-size: 28px;
  color: #111827;
}
 
.todo-title-done {
  text-decoration: line-through;
  color: #9ca3af;
}
 
.del {
  font-size: 24px;
  color: #ef4444;
}

4) 开发与验证

本地调试(以模板脚本为准):

npm run dev:h5

阶段验收:浏览器或本地预览能看到 TodoList,且可添加 / 勾选 / 删除。

如果你需要验证小程序构建目标,也可使用:

npm run dev:weapp

5) 真机调试(扫码)

在项目根目录执行:

npm run htyf

然后按 CLI 菜单操作:

  1. 选择 📦 小程序 - 真机调试
  2. 输入版本号(例如 1.0.0
  3. 等待构建并出现二维码
  4. 用红糖云服 App 扫码,进入真机调试

建议调试顺序

先在 dev:h5 下快速确认交互和样式,再走 npm run htyf 真机调试,定位问题会更高效。

6) README 推荐工作流(对外分发)

  1. 先明确 webUrl 指向线上/测试网页地址。
  2. app.json 配好 appUrlConfigzipUrl
  3. 运行 npm run htyf 构建并产出资源。
  4. 将资源上传到 zipUrl 对应地址,供团队或用户更新使用。

常见问题

Q1:为什么强调在项目根执行 npm run htyf

因为 CLI 会读取当前目录下的 app.json(尤其是 htyf 配置)。如果目录不对,通常会提示找不到项目配置。

Q2:我的模板文件名和本文略有差异怎么办?

以本地生成模板为准,核心思路不变:修改首页页面组件(通常是 src/pages/index/index.tsx)并保持在 app.config.ts 中注册。

Q3:为什么建议先 dev:h5 再真机调试?

dev:h5 启动快、改样式反馈快,适合先确认页面交互。确认无误后再走 npm run htyf,能减少真机阶段排查范围。

On this page