create-vite-taro 0.7.13 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -9,7 +9,6 @@ import generatorPackage from './package.json' with { type: 'json' }
9
9
 
10
10
  const packageRoot = path.dirname(fileURLToPath(import.meta.url))
11
11
  const defaultProjectName = 'vite-taro-app'
12
- const defaultProjectTitle = 'Vite Taro App'
13
12
  const defaultWechatAppId = 'wx0000000000000000'
14
13
  const renamedFiles = {
15
14
  '_env.local': '.env.local',
@@ -128,14 +127,14 @@ function copyTemplate(sourceDirectory, targetDirectory, projectName, projectTitl
128
127
  continue
129
128
  }
130
129
 
131
- const source = readFileSync(sourcePath, 'utf8')
130
+ const source = readFileSync(sourcePath)
132
131
  writeFileSync(targetPath, transformTemplateFile(targetName, source, projectName, projectTitle, wechatAppId))
133
132
  }
134
133
  }
135
134
 
136
135
  function transformTemplateFile(fileName, source, projectName, projectTitle, wechatAppId) {
137
136
  if (fileName === 'package.json') {
138
- const template = JSON.parse(source)
137
+ const template = JSON.parse(source.toString('utf8'))
139
138
  // The fixed Changesets group releases the generator and plugin at the same version.
140
139
  const packageJson = {
141
140
  ...template,
@@ -149,13 +148,14 @@ function transformTemplateFile(fileName, source, projectName, projectTitle, wech
149
148
  }
150
149
 
151
150
  if (fileName === 'index.html') {
152
- return source.replace(/<title>.*?<\/title>/, `<title>${projectTitle}</title>`)
151
+ return source.toString('utf8').replace(/<title>.*?<\/title>/, `<title>${projectTitle}</title>`)
152
+ }
153
+
154
+ if (fileName === '.env.local') {
155
+ return source.toString('utf8').replaceAll(defaultWechatAppId, wechatAppId)
153
156
  }
154
157
 
155
158
  return source
156
- .replaceAll(defaultProjectName, projectName)
157
- .replaceAll(defaultProjectTitle, projectTitle)
158
- .replaceAll(defaultWechatAppId, wechatAppId)
159
159
  }
160
160
 
161
161
  function createWechatAppId() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-vite-taro",
3
- "version": "0.7.13",
3
+ "version": "0.8.0",
4
4
  "description": "Create Vite 8 + React 19 + Taro apps for WeChat, Alipay, TikTok, and H5 targets.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -83,7 +83,13 @@ npm run dev:h5
83
83
 
84
84
  ## 开发
85
85
 
86
- 默认页面是 `src/pages/home/index.tsx`。VPT 通过虚拟模块提供 Taro 组件和 API:
86
+ 模板包含 Home、Explore、About 三个真实 Tab 页面,默认页面是 `src/pages/home/index.tsx`。路由、标签和图标名称统一定义在 `src/tab-pages.ts`。
87
+
88
+ 每个页面负责自己的全屏布局和 `TabBar`,`src/app.tsx` 负责应用生命周期和全局样式,直接返回 `children`。通过 `Taro.switchTab()` 切换 Tab 时,已访问页面的 React 状态会保留。
89
+
90
+ `vite.config.ts` 显式声明页面并启用 `prerender: true`,原生 Tab 路由由 `tabPages` 生成。微信使用 `tabBar.custom: true`,由页面中的 React 标签栏负责渲染;`tabBar.list` 保留原生 `switchTab()` 所需的路由声明。支付宝使用 `tabBar.customize: true` 和 `tabBar.items`,VPT 自动生成隐藏的原生标签栏占位组件。
91
+
92
+ VPT 通过虚拟模块提供 Taro 组件和 API:
87
93
 
88
94
  ```tsx
89
95
  import Taro from 'virtual:taro/api'
@@ -92,7 +98,7 @@ import { Button, Text, View } from 'virtual:taro/components'
92
98
 
93
99
  不要直接安装或导入 `@tarojs/*`。在 `vite.config.ts` 中添加页面,并按当前目标修改应用与开发工具项目配置。
94
100
 
95
- 模板包含条件编译、微信原生组件、支付宝、抖音与 Web 共用的 Taro 组件、懒加载、自定义导航栏、Tailwind CSS、CSS Modules 和全局样式示例;不需要的示例可直接删除。
101
+ 模板包含条件编译、微信原生组件、支付宝、抖音与 Web 共用的 Taro 组件、懒加载、自定义导航栏、React 标签栏、Tailwind CSS、CSS Modules 和全局样式示例;不需要的示例可直接删除。
96
102
 
97
103
  ### 样式
98
104
 
@@ -49,13 +49,6 @@ button {
49
49
  line-height: inherit;
50
50
  }
51
51
 
52
- /* Page content fills the space above the shared Footer. */
53
- .app-shell > .taro_page,
54
- .app-shell > .taro_router {
55
- flex: 1;
56
- min-height: 0;
57
- }
58
-
59
52
  .brand-serif {
60
53
  font-family: Georgia, "Times New Roman", serif;
61
54
  }
@@ -1,20 +1,13 @@
1
1
  import { useLaunch } from 'virtual:taro/api'
2
- import { View } from 'virtual:taro/components'
3
2
  import type { PropsWithChildren } from 'react'
4
3
  import './app.css'
5
- import { Footer } from './components/footer/footer.tsx'
6
4
 
7
5
  function App({ children }: PropsWithChildren) {
8
6
  useLaunch(() => {
9
7
  console.log('App launched')
10
8
  })
11
9
 
12
- return (
13
- <View className="app-shell flex h-screen flex-col overflow-hidden bg-canvas bg-canvas-botanical text-foreground">
14
- {children}
15
- <Footer />
16
- </View>
17
- )
10
+ return children
18
11
  }
19
12
 
20
13
  export default App
@@ -0,0 +1,39 @@
1
+ import Taro from 'virtual:taro/api'
2
+ import { Button, Image, Text, View } from 'virtual:taro/components'
3
+ import { tabPages } from '../../tab-pages.ts'
4
+ import { tabIcons } from './tab-icons.ts'
5
+
6
+ interface TabBarProps {
7
+ activePath: (typeof tabPages)[number]['path']
8
+ }
9
+
10
+ export function TabBar({ activePath }: TabBarProps) {
11
+ return (
12
+ <View className="flex h-[calc(4rem+env(safe-area-inset-bottom))] w-full shrink-0 flex-row border-t border-divider bg-surface-subtle pb-[env(safe-area-inset-bottom)]">
13
+ {tabPages.map(({ path, label, icon }) => {
14
+ const selected = activePath === path
15
+ return (
16
+ <Button
17
+ key={path}
18
+ id={`tab-${icon}`}
19
+ aria-current={selected ? 'page' : 'false'}
20
+ className={`m-0 flex min-w-0 flex-1 flex-col items-center justify-center gap-1 rounded-none border-none bg-transparent p-0 text-xs after:content-none ${selected ? 'text-primary font-bold' : 'text-muted font-light'}`}
21
+ onClick={() => {
22
+ if (selected) {
23
+ return
24
+ }
25
+ void Taro.switchTab({ url: `/${path}` })
26
+ }}
27
+ >
28
+ <Image
29
+ src={tabIcons[icon]}
30
+ mode="aspectFit"
31
+ className={`h-6 w-6 ${selected ? 'opacity-100' : 'opacity-50'}`}
32
+ />
33
+ <Text>{label}</Text>
34
+ </Button>
35
+ )
36
+ })}
37
+ </View>
38
+ )
39
+ }
@@ -0,0 +1,9 @@
1
+ import aboutIcon from '../../assets/icons/about.png'
2
+ import exploreIcon from '../../assets/icons/explore.png'
3
+ import homeIcon from '../../assets/icons/home.png'
4
+
5
+ export const tabIcons = {
6
+ home: homeIcon,
7
+ explore: exploreIcon,
8
+ about: aboutIcon
9
+ } as const
@@ -0,0 +1,48 @@
1
+ import { ScrollView, Text, View } from 'virtual:taro/components'
2
+ import { NavigationBar } from '../../components/navigation-bar/navigation-bar.tsx'
3
+ import { TabBar } from '../../components/tab-bar/tab-bar.tsx'
4
+ import { tabPages } from '../../tab-pages.ts'
5
+
6
+ const platforms = ['WeChat', 'Alipay', 'TikTok', 'Web'] as const
7
+
8
+ export default function AboutPage() {
9
+ return (
10
+ <View className="flex h-screen flex-col overflow-hidden bg-canvas bg-canvas-botanical text-foreground">
11
+ <NavigationBar title="About" />
12
+ <ScrollView scrollY enableFlex className="flex min-h-0 flex-1 flex-col">
13
+ <View className="mx-auto flex w-full max-w-4xl flex-col px-5 pb-12 pt-10">
14
+ <Text className="text-xs font-bold tracking-widest text-primary-label">A LITTLE ABOUT VPT</Text>
15
+ <Text className="brand-serif mt-3 block text-5xl font-semibold text-heading">Made to travel.</Text>
16
+ <Text className="mt-4 block max-w-xl text-sm leading-7 text-muted">
17
+ VPT brings Vite, React and Taro together so your ideas can grow across mini programs and the
18
+ web.
19
+ </Text>
20
+
21
+ <View className="mt-9 rounded-3xl bg-primary p-7 text-white shadow-xl sm:p-9">
22
+ <Text className="brand-serif block text-3xl font-semibold text-white">
23
+ One project. Four places.
24
+ </Text>
25
+ <Text className="mt-3 block text-sm leading-6 text-on-primary-muted">
26
+ Share the interface and tailor the details to each platform as you go.
27
+ </Text>
28
+ <View className="mt-6 flex flex-row flex-wrap gap-2">
29
+ {platforms.map((platform) => (
30
+ <View key={platform} className="rounded-full border border-on-primary-accent px-4 py-2">
31
+ <Text className="text-xs font-bold text-white">{platform}</Text>
32
+ </View>
33
+ ))}
34
+ </View>
35
+ </View>
36
+
37
+ <View className="mt-5 flex flex-col rounded-3xl border border-outline bg-white/85 p-7 shadow-lg">
38
+ <Text className="text-lg font-bold text-heading">Ready to start?</Text>
39
+ <Text className="mt-2 text-sm leading-6 text-muted">
40
+ Tap the Home tab to try the counter, lazy-loaded card and your first Taro API call.
41
+ </Text>
42
+ </View>
43
+ </View>
44
+ </ScrollView>
45
+ <TabBar activePath={tabPages[2].path} />
46
+ </View>
47
+ )
48
+ }
@@ -0,0 +1,62 @@
1
+ import { ScrollView, Text, View } from 'virtual:taro/components'
2
+ import { NavigationBar } from '../../components/navigation-bar/navigation-bar.tsx'
3
+ import { TabBar } from '../../components/tab-bar/tab-bar.tsx'
4
+ import { tabPages } from '../../tab-pages.ts'
5
+
6
+ const steps = [
7
+ {
8
+ number: '01',
9
+ title: 'Make it yours',
10
+ description: 'Start with the home page. Change the copy, colors and components to match your idea.',
11
+ file: 'src/pages/home/index.tsx'
12
+ },
13
+ {
14
+ number: '02',
15
+ title: 'Grow another page',
16
+ description: 'Add a React page and register its path in the VPT pages array.',
17
+ file: 'vite.config.ts'
18
+ },
19
+ {
20
+ number: '03',
21
+ title: 'Ship to every target',
22
+ description: 'Build for WeChat, Alipay, TikTok or the web from the same source.',
23
+ file: 'npm run build:wx'
24
+ }
25
+ ] as const
26
+
27
+ export default function ExplorePage() {
28
+ return (
29
+ <View className="flex h-screen flex-col overflow-hidden bg-canvas bg-canvas-botanical text-foreground">
30
+ <NavigationBar title="Explore" />
31
+ <ScrollView scrollY enableFlex className="flex min-h-0 flex-1 flex-col">
32
+ <View className="mx-auto flex w-full max-w-4xl flex-col px-5 pb-12 pt-10">
33
+ <Text className="text-xs font-bold tracking-widest text-primary-label">YOUR NEXT STEPS</Text>
34
+ <Text className="brand-serif mt-3 block text-5xl font-semibold text-heading">Room to grow.</Text>
35
+ <Text className="mt-4 block max-w-xl text-sm leading-7 text-muted">
36
+ This starter is a beginning, not a blueprint. Pick a path and make something of your own.
37
+ </Text>
38
+ <View className="mt-9 flex flex-col gap-4">
39
+ {steps.map((step) => (
40
+ <View
41
+ key={step.number}
42
+ className="flex flex-col rounded-3xl border border-outline bg-white/85 p-6 shadow-lg sm:flex-row sm:gap-6"
43
+ >
44
+ <Text className="brand-serif block text-3xl font-semibold text-primary-control">
45
+ {step.number}
46
+ </Text>
47
+ <View className="mt-3 flex min-w-0 flex-1 flex-col sm:mt-0">
48
+ <Text className="text-lg font-bold text-heading">{step.title}</Text>
49
+ <Text className="mt-2 text-sm leading-6 text-muted">{step.description}</Text>
50
+ <Text className="mt-4 self-start rounded-full bg-primary-surface px-4 py-2 text-xs font-bold text-primary">
51
+ {step.file}
52
+ </Text>
53
+ </View>
54
+ </View>
55
+ ))}
56
+ </View>
57
+ </View>
58
+ </ScrollView>
59
+ <TabBar activePath={tabPages[1].path} />
60
+ </View>
61
+ )
62
+ }
@@ -1,8 +1,10 @@
1
1
  import { ScrollView, Text, View } from 'virtual:taro/components'
2
2
  import { lazy, Suspense, useState } from 'react'
3
- import { Counter } from '../../components/counter/counter.tsx'
4
3
  import { BotanicalSprig } from '../../components/botanical-sprig/botanical-sprig.tsx'
4
+ import { Counter } from '../../components/counter/counter.tsx'
5
5
  import { NavigationBar } from '../../components/navigation-bar/navigation-bar.tsx'
6
+ import { TabBar } from '../../components/tab-bar/tab-bar.tsx'
7
+ import { tabPages } from '../../tab-pages.ts'
6
8
 
7
9
  const featureCards = [
8
10
  {
@@ -32,9 +34,9 @@ function HomePage() {
32
34
  const [count, setCount] = useState(0)
33
35
 
34
36
  return (
35
- <View className="flex h-full min-h-0 flex-1 flex-col overflow-hidden">
37
+ <View className="flex h-screen flex-col overflow-hidden bg-canvas bg-canvas-botanical text-foreground">
36
38
  <NavigationBar title="VPT" />
37
- <ScrollView scrollY className="flex min-h-0 flex-1 flex-col">
39
+ <ScrollView scrollY enableFlex className="flex min-h-0 flex-1 flex-col">
38
40
  <View className="relative flex shrink-0 flex-col items-center overflow-hidden px-5 pb-8 pt-6">
39
41
  <BotanicalSprig placement="top" />
40
42
  <BotanicalSprig placement="side" />
@@ -43,10 +45,10 @@ function HomePage() {
43
45
  <Text className="brand-serif mt-7 block text-[7rem] font-semibold leading-none tracking-[-0.08em] text-primary sm:text-[9rem]">
44
46
  VPT
45
47
  </Text>
46
- <Text className="mt-5 block max-w-2xl text-2xl font-bold leading-tight text-heading sm:text-3xl">
48
+ <Text className="mt-5 block w-full max-w-2xl text-2xl font-bold leading-tight text-heading sm:text-3xl">
47
49
  Build naturally. Ship everywhere.
48
50
  </Text>
49
- <Text className="mt-4 block max-w-xl text-sm leading-7 text-muted sm:text-base">
51
+ <Text className="mt-4 block w-full max-w-xl text-sm leading-7 text-muted sm:text-base">
50
52
  A botanical little starter for building polished WeChat Mini Programs and Web apps with one
51
53
  modern React codebase.
52
54
  </Text>
@@ -96,13 +98,13 @@ function HomePage() {
96
98
  <View className="flex shrink-0 flex-col items-center px-5 pb-12 pt-14">
97
99
  <View className="flex w-full max-w-4xl flex-col">
98
100
  <View className="flex flex-col items-center text-center">
99
- <Text className="text-xs font-bold tracking-widest text-primary-label">
101
+ <Text className="w-full text-xs font-bold tracking-widest text-primary-label">
100
102
  A SMALL START, A MODERN FOUNDATION
101
103
  </Text>
102
- <Text className="brand-serif mt-3 block text-4xl font-semibold text-heading">
104
+ <Text className="brand-serif mt-3 block w-full text-4xl font-semibold text-heading">
103
105
  Everything is ready to grow.
104
106
  </Text>
105
- <Text className="mt-3 block max-w-xl text-sm leading-7 text-muted">
107
+ <Text className="mt-3 block w-full max-w-xl text-sm leading-7 text-muted">
106
108
  Keep the pieces you need, replace the rest, and let VPT handle the cross-platform build.
107
109
  </Text>
108
110
  </View>
@@ -136,6 +138,7 @@ function HomePage() {
136
138
  </View>
137
139
  </View>
138
140
  </ScrollView>
141
+ <TabBar activePath={tabPages[0].path} />
139
142
  </View>
140
143
  )
141
144
  }
@@ -0,0 +1,5 @@
1
+ export const tabPages = [
2
+ { path: 'pages/home/index', label: 'Home', icon: 'home' },
3
+ { path: 'pages/explore/explore-page', label: 'Explore', icon: 'explore' },
4
+ { path: 'pages/about/about-page', label: 'About', icon: 'about' }
5
+ ] as const
@@ -17,6 +17,7 @@
17
17
  "jsx": "react-jsx",
18
18
 
19
19
  /* Linting */
20
+ "strict": true,
20
21
  "noUnusedLocals": true,
21
22
  "noUnusedParameters": true,
22
23
  "erasableSyntaxOnly": true,
@@ -2,7 +2,7 @@
2
2
  "compilerOptions": {
3
3
  "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
4
  "target": "es2023",
5
- "lib": ["ES2023"],
5
+ "lib": ["ESNext"],
6
6
  "module": "esnext",
7
7
  "types": ["node"],
8
8
  "skipLibCheck": true,
@@ -15,6 +15,7 @@
15
15
  "noEmit": true,
16
16
 
17
17
  /* Linting */
18
+ "strict": true,
18
19
  "noUnusedLocals": true,
19
20
  "noUnusedParameters": true,
20
21
  "erasableSyntaxOnly": true,
@@ -2,6 +2,7 @@ import path from 'node:path'
2
2
  import { fileURLToPath } from 'node:url'
3
3
  import { defineConfig, loadEnv } from 'vite'
4
4
  import vpt, { type VptJsonObject, type VptTarget } from 'vite-plugin-taro'
5
+ import { tabPages } from './src/tab-pages.ts'
5
6
 
6
7
  export default defineConfig(({ mode }) => {
7
8
  const env = loadEnv(mode, process.cwd(), 'VITE_VPT_')
@@ -20,11 +21,22 @@ export default defineConfig(({ mode }) => {
20
21
  pages: [
21
22
  {
22
23
  path: 'pages/home/index',
24
+ prerender: true,
25
+ config: createPageJson(target)
26
+ },
27
+ {
28
+ path: 'pages/explore/explore-page',
29
+ prerender: true,
30
+ config: createPageJson(target)
31
+ },
32
+ {
33
+ path: 'pages/about/about-page',
34
+ prerender: true,
23
35
  config: createPageJson(target)
24
36
  }
25
37
  ],
26
38
  appJson: createAppJson(target),
27
- projectConfigJson: createProjectConfigJson({ target, appId }),
39
+ projectConfigJson: createProjectConfigJson(target, appId),
28
40
  projectPrivateConfigJson: createProjectPrivateConfigJson(target),
29
41
  hmr: {
30
42
  mode: target === 'wx' ? 'devtools' : 'interpreter'
@@ -38,7 +50,8 @@ function createPageJson(target: VptTarget): VptJsonObject {
38
50
  switch (target) {
39
51
  case 'wx': {
40
52
  return {
41
- glassEaselWebview: true,
53
+ // 在 app json 的 window 上设置,这里就不用设置了
54
+ // glassEaselWebview: true,
42
55
  navigationStyle: 'custom',
43
56
  navigationBarTextStyle: 'black'
44
57
  }
@@ -69,6 +82,13 @@ function createAppJson(target: VptTarget): VptJsonObject {
69
82
  renderer: 'skyline',
70
83
  componentFramework: 'glass-easel',
71
84
  glassEaselWebview: true,
85
+ tabBar: {
86
+ custom: true,
87
+ color: '#667085',
88
+ selectedColor: '#26734d',
89
+ backgroundColor: '#f8fbf4',
90
+ list: tabPages.map(({ path, label }) => ({ pagePath: path, text: label }))
91
+ },
72
92
  rendererOptions: {
73
93
  skyline: {
74
94
  defaultDisplayBlock: true,
@@ -77,6 +97,7 @@ function createAppJson(target: VptTarget): VptJsonObject {
77
97
  }
78
98
  },
79
99
  window: {
100
+ glassEaselWebview: true,
80
101
  navigationStyle: 'custom',
81
102
  navigationBarTextStyle: 'black'
82
103
  }
@@ -86,6 +107,13 @@ function createAppJson(target: VptTarget): VptJsonObject {
86
107
  return {
87
108
  lazyCodeLoading: 'renderedComponents',
88
109
  useDynamicPlugins: true,
110
+ tabBar: {
111
+ customize: true,
112
+ textColor: '#667085',
113
+ selectedColor: '#26734d',
114
+ backgroundColor: '#f8fbf4',
115
+ items: tabPages.map(({ path, label }) => ({ pagePath: path, name: label }))
116
+ },
89
117
  window: {
90
118
  transparentTitle: 'always',
91
119
  titlePenetrate: 'YES'
@@ -94,6 +122,13 @@ function createAppJson(target: VptTarget): VptJsonObject {
94
122
  }
95
123
  case 'tt': {
96
124
  return {
125
+ tabBar: {
126
+ custom: true,
127
+ color: '#667085',
128
+ selectedColor: '#26734d',
129
+ backgroundColor: '#f8fbf4',
130
+ list: tabPages.map(({ path, label }) => ({ pagePath: path, text: label }))
131
+ },
97
132
  window: {
98
133
  navigationStyle: 'custom',
99
134
  navigationBarTextStyle: 'black'
@@ -106,7 +141,7 @@ function createAppJson(target: VptTarget): VptJsonObject {
106
141
  }
107
142
  }
108
143
 
109
- function createProjectConfigJson({ target, appId }: { target: VptTarget; appId: string | undefined }): VptJsonObject {
144
+ function createProjectConfigJson(target: VptTarget, appId: string | undefined): VptJsonObject {
110
145
  switch (target) {
111
146
  case 'wx': {
112
147
  return {
@@ -115,6 +150,7 @@ function createProjectConfigJson({ target, appId }: { target: VptTarget; appId:
115
150
  description: '',
116
151
  compileType: 'miniprogram',
117
152
  setting: {
153
+ // 在这里改为 true 开启 Skyline,默认关闭
118
154
  skylineRenderEnable: false,
119
155
  urlCheck: false,
120
156
  es6: false,
@@ -1,9 +0,0 @@
1
- export function Footer() {
2
- return (
3
- <div className="mt-auto flex h-10 shrink-0 flex-row items-center justify-center border-t border-divider bg-surface-subtle/95 px-5">
4
- <span className="text-center text-xs font-bold tracking-widest text-quiet">
5
- VPT · OPEN SOURCE · MIT
6
- </span>
7
- </div>
8
- )
9
- }