create-vite-vue 1.3.1 → 1.5.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/bin/index.js +206 -117
- package/package.json +1 -1
- package/template/base-js/README.md +32 -19
- package/template/base-js/package.json.tpl +4 -4
- package/template/base-ts/README.md +32 -19
- package/template/base-ts/package.json.tpl +6 -6
- package/template/router-js/src/App.vue +3 -0
- package/template/router-js/src/router/index.js +1 -1
- package/template/router-ts/src/App.vue +3 -0
- package/template/router-ts/src/router/index.ts +1 -1
- /package/template/router-js/src/views/{Home.vue → home/index.vue} +0 -0
- /package/template/router-ts/src/views/{Home.vue → home/index.vue} +0 -0
package/bin/index.js
CHANGED
|
@@ -1,17 +1,43 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from 'child_process'
|
|
3
3
|
import fs from 'fs'
|
|
4
4
|
import path from 'path'
|
|
5
5
|
import prompts from 'prompts'
|
|
6
6
|
import { fileURLToPath } from 'url'
|
|
7
7
|
|
|
8
|
+
// 检测 Node 版本
|
|
9
|
+
const requiredVersion = '22.19.0'
|
|
10
|
+
|
|
11
|
+
function compareVersion (v1, v2) {
|
|
12
|
+
const a = v1.split('.').map(Number)
|
|
13
|
+
const b = v2.split('.').map(Number)
|
|
14
|
+
|
|
15
|
+
for(let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
16
|
+
const n1 = a[i] || 0
|
|
17
|
+
const n2 = b[i] || 0
|
|
18
|
+
if(n1 > n2) return 1
|
|
19
|
+
if(n1 < n2) return -1
|
|
20
|
+
}
|
|
21
|
+
return 0
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const currentVersion = process.version.replace('v', '')
|
|
25
|
+
|
|
26
|
+
if(compareVersion(currentVersion, requiredVersion) < 0) {
|
|
27
|
+
console.error(`❌ Node.js 版本过低`)
|
|
28
|
+
console.error(`当前版本: ${currentVersion}`)
|
|
29
|
+
console.error(`最低要求: ${requiredVersion}`)
|
|
30
|
+
console.error(`请升级 Node.js 后再运行`)
|
|
31
|
+
process.exit(1)
|
|
32
|
+
}
|
|
33
|
+
|
|
8
34
|
const __filename = fileURLToPath(import.meta.url)
|
|
9
35
|
const __dirname = path.dirname(__filename)
|
|
10
36
|
|
|
11
37
|
; (async () => {
|
|
12
38
|
// 1️⃣ 输入项目名
|
|
13
39
|
let projectName
|
|
14
|
-
while
|
|
40
|
+
while(true) {
|
|
15
41
|
const res = await prompts({
|
|
16
42
|
type: 'text',
|
|
17
43
|
name: 'projectName',
|
|
@@ -19,10 +45,10 @@ const __dirname = path.dirname(__filename)
|
|
|
19
45
|
validate: v => v ? true : '项目名不能为空'
|
|
20
46
|
})
|
|
21
47
|
projectName = res.projectName
|
|
22
|
-
if
|
|
48
|
+
if(!projectName) process.exit(1)
|
|
23
49
|
|
|
24
50
|
const targetDir = path.resolve(process.cwd(), projectName)
|
|
25
|
-
if
|
|
51
|
+
if(fs.existsSync(targetDir)) {
|
|
26
52
|
console.log('❌ 目录已存在,请重新输入')
|
|
27
53
|
continue
|
|
28
54
|
}
|
|
@@ -31,6 +57,7 @@ const __dirname = path.dirname(__filename)
|
|
|
31
57
|
|
|
32
58
|
const targetDir = path.resolve(process.cwd(), projectName)
|
|
33
59
|
|
|
60
|
+
// 选择语言
|
|
34
61
|
const { language } = await prompts({
|
|
35
62
|
type: 'select',
|
|
36
63
|
name: 'language',
|
|
@@ -41,71 +68,50 @@ const __dirname = path.dirname(__filename)
|
|
|
41
68
|
]
|
|
42
69
|
})
|
|
43
70
|
|
|
44
|
-
// 2️⃣
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
type: 'select',
|
|
60
|
-
name: 'axios',
|
|
61
|
-
message: '是否使用 Axios?',
|
|
62
|
-
choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
type: 'select',
|
|
66
|
-
name: 'ui',
|
|
67
|
-
message: '请选择 UI 框架',
|
|
68
|
-
choices: [
|
|
69
|
-
{ title: 'Element Plus(PC)', value: 'element' },
|
|
70
|
-
{ title: 'Vant(Mobile)', value: 'vant' },
|
|
71
|
-
{ title: '不使用 UI 框架', value: 'none' }
|
|
72
|
-
]
|
|
73
|
-
}
|
|
74
|
-
])
|
|
75
|
-
|
|
76
|
-
// ========== 新增:增强插件配置==========
|
|
77
|
-
let extraPlugins = []
|
|
78
|
-
|
|
79
|
-
const { wantExtra } = await prompts({
|
|
80
|
-
type: 'toggle',
|
|
81
|
-
name: 'wantExtra',
|
|
82
|
-
message: '是否需要添加额外工具?',
|
|
83
|
-
initial: false,
|
|
84
|
-
active: '是',
|
|
85
|
-
inactive: '否'
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
if (wantExtra) {
|
|
89
|
-
const pluginOptions = [
|
|
71
|
+
// 2️⃣ 功能选择(多选)
|
|
72
|
+
const { featureList } = await prompts({
|
|
73
|
+
type: 'multiselect',
|
|
74
|
+
name: 'featureList',
|
|
75
|
+
message: '请选择基础功能(↑↓选择,空格确认,回车完成)',
|
|
76
|
+
instructions: false,
|
|
77
|
+
choices: [
|
|
78
|
+
{ title: 'Vue Router', value: 'router' },
|
|
79
|
+
{ title: 'Pinia(含持久化)', value: 'pinia' },
|
|
80
|
+
{ title: 'Axios', value: 'axios' },
|
|
81
|
+
{ title: 'Element Plus(PC UI)', value: 'element' },
|
|
82
|
+
{ title: 'Vant(Mobile UI)', value: 'vant' },
|
|
90
83
|
{ title: 'VueUse(实用 Composition API)', value: 'vueuse' },
|
|
91
|
-
{ title: 'Lodash
|
|
92
|
-
{ title: 'Day.js
|
|
84
|
+
{ title: 'Lodash(工具库)', value: 'lodash' },
|
|
85
|
+
{ title: 'Day.js(日期处理)', value: 'dayjs' },
|
|
93
86
|
{ title: 'Tailwind CSS(原子化 CSS)', value: 'tailwind' }
|
|
94
87
|
]
|
|
88
|
+
})
|
|
95
89
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
90
|
+
// 转换成原来的结构(保证后面代码基本不用动)
|
|
91
|
+
const features = {
|
|
92
|
+
router: featureList?.includes('router') || false,
|
|
93
|
+
pinia: featureList?.includes('pinia') || false,
|
|
94
|
+
axios: featureList?.includes('axios') || false,
|
|
95
|
+
ui: featureList?.filter(v => ['element', 'vant'].includes(v)) || []
|
|
96
|
+
}
|
|
103
97
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
98
|
+
const extraPlugins = featureList?.filter(v =>
|
|
99
|
+
['vueuse', 'lodash', 'dayjs', 'tailwind'].includes(v)
|
|
100
|
+
) || []
|
|
101
|
+
|
|
102
|
+
// 询问是否开启自动路由
|
|
103
|
+
let autoRoute = false
|
|
104
|
+
if(features.router) {
|
|
105
|
+
const { enableAutoRoute } = await prompts({
|
|
106
|
+
type: 'toggle',
|
|
107
|
+
name: 'enableAutoRoute',
|
|
108
|
+
message: '是否开启自动配置路由(vite-plugin-pages)?',
|
|
109
|
+
initial: false,
|
|
110
|
+
active: '是',
|
|
111
|
+
inactive: '否'
|
|
112
|
+
})
|
|
113
|
+
autoRoute = enableAutoRoute
|
|
107
114
|
}
|
|
108
|
-
// ========== 增强插件配置结束 ==========
|
|
109
115
|
|
|
110
116
|
// 3️⃣ 是否立即运行 dev
|
|
111
117
|
const { runDev } = await prompts({
|
|
@@ -123,21 +129,21 @@ const __dirname = path.dirname(__filename)
|
|
|
123
129
|
{ recursive: true }
|
|
124
130
|
)
|
|
125
131
|
|
|
126
|
-
//
|
|
132
|
+
// 替换 index.html 的 title
|
|
127
133
|
const indexPath = path.join(targetDir, 'index.html')
|
|
128
|
-
if
|
|
134
|
+
if(fs.existsSync(indexPath)) {
|
|
129
135
|
const indexContent = fs.readFileSync(indexPath, 'utf-8')
|
|
130
136
|
fs.writeFileSync(
|
|
131
137
|
indexPath,
|
|
132
138
|
indexContent.replace(/<title>.*<\/title>/, `<title>${projectName}</title>`)
|
|
133
139
|
)
|
|
134
140
|
}
|
|
141
|
+
|
|
135
142
|
// 追加 Tailwind CSS 导入
|
|
136
|
-
if
|
|
143
|
+
if(extraPlugins.includes('tailwind')) {
|
|
137
144
|
const stylePath = path.join(targetDir, 'src/style.css')
|
|
138
145
|
const original = fs.readFileSync(stylePath, 'utf-8')
|
|
139
|
-
|
|
140
|
-
if (!original.startsWith('@import "tailwindcss";')) {
|
|
146
|
+
if(!original.startsWith('@import "tailwindcss";')) {
|
|
141
147
|
fs.writeFileSync(stylePath, `@import "tailwindcss";\n${original}`)
|
|
142
148
|
}
|
|
143
149
|
}
|
|
@@ -150,68 +156,56 @@ const __dirname = path.dirname(__filename)
|
|
|
150
156
|
features.pinia && copy(language === 'ts' ? 'pinia-ts' : 'pinia-js')
|
|
151
157
|
features.axios && copy(language === 'ts' ? 'axios-ts' : 'axios-js')
|
|
152
158
|
|
|
153
|
-
//
|
|
154
|
-
for
|
|
159
|
+
// 拷贝增强插件模板
|
|
160
|
+
for(const plugin of extraPlugins) {
|
|
155
161
|
const templateName = `${plugin}-${language === 'ts' ? 'ts' : 'js'}`
|
|
156
162
|
const templatePath = path.resolve(__dirname, `../template/${templateName}`)
|
|
157
|
-
if
|
|
163
|
+
if(fs.existsSync(templatePath)) {
|
|
158
164
|
fs.cpSync(templatePath, targetDir, { recursive: true })
|
|
159
165
|
}
|
|
160
166
|
}
|
|
161
|
-
// ========== 增强插件模板拷贝结束 ==========
|
|
162
167
|
|
|
163
|
-
// 6️⃣ 生成 main.js
|
|
168
|
+
// 6️⃣ 生成 main.js / main.ts
|
|
164
169
|
const mainFile = language === 'ts' ? 'main.ts' : 'main.js'
|
|
165
170
|
const mainTplPath = path.join(targetDir, `src/${mainFile}.tpl`)
|
|
166
171
|
let main = fs.readFileSync(mainTplPath, 'utf-8')
|
|
167
172
|
|
|
168
173
|
const replacements = {
|
|
169
|
-
// import
|
|
170
|
-
// vue-router
|
|
171
174
|
'/* __ROUTER_IMPORT__ */': features.router ? "import router from './router'" : '',
|
|
172
|
-
// pinia
|
|
173
175
|
'/* __PINIA_IMPORT__ */': features.pinia
|
|
174
176
|
? "import { createPinia } from 'pinia'\nimport persistedstate from 'pinia-plugin-persistedstate'"
|
|
175
177
|
: '',
|
|
176
|
-
|
|
177
|
-
'/* __ELEMENT_IMPORT__ */': features.ui === 'element'
|
|
178
|
+
'/* __ELEMENT_IMPORT__ */': features.ui.includes('element')
|
|
178
179
|
? `import ElementPlus from 'element-plus'
|
|
179
180
|
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
|
180
181
|
import 'element-plus/dist/index.css'
|
|
181
182
|
import * as ElementPlusIconsVue from '@element-plus/icons-vue'`
|
|
182
183
|
: '',
|
|
183
|
-
|
|
184
|
-
'/* __VANT_IMPORT__ */': features.ui === 'vant'
|
|
184
|
+
'/* __VANT_IMPORT__ */': features.ui.includes('vant')
|
|
185
185
|
? `import Vant from 'vant'
|
|
186
186
|
import 'vant/lib/index.css'`
|
|
187
187
|
: '',
|
|
188
|
-
// use
|
|
189
|
-
//router
|
|
190
188
|
'/* __ROUTER_USE__ */': features.router ? 'app.use(router)' : '',
|
|
191
|
-
// pinia
|
|
192
189
|
'/* __PINIA_USE__ */': features.pinia
|
|
193
190
|
? 'app.use(createPinia().use(persistedstate))'
|
|
194
191
|
: '',
|
|
195
|
-
|
|
196
|
-
'/* __ELEMENT_USE__ */': features.ui === 'element'
|
|
192
|
+
'/* __ELEMENT_USE__ */': features.ui.includes('element')
|
|
197
193
|
? `app.use(ElementPlus, { locale: zhCn })
|
|
198
194
|
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
|
199
195
|
app.component(key, component)
|
|
200
196
|
}`
|
|
201
197
|
: '',
|
|
202
|
-
|
|
203
|
-
'/* __VANT_USE__ */': features.ui === 'vant'
|
|
198
|
+
'/* __VANT_USE__ */': features.ui.includes('vant')
|
|
204
199
|
? 'app.use(Vant)'
|
|
205
200
|
: ''
|
|
206
201
|
}
|
|
207
|
-
// ========== 增强插件占位符结束 ==========
|
|
208
202
|
|
|
209
203
|
function escapeRegExp (str) {
|
|
210
204
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
211
205
|
}
|
|
212
206
|
|
|
213
|
-
for
|
|
214
|
-
if
|
|
207
|
+
for(const [placeholder, content] of Object.entries(replacements)) {
|
|
208
|
+
if(content) {
|
|
215
209
|
main = main.replace(placeholder, content)
|
|
216
210
|
} else {
|
|
217
211
|
const re = new RegExp(`^\\s*${escapeRegExp(placeholder)}\\s*$\\n?`, 'gm')
|
|
@@ -222,42 +216,37 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
|
|
222
216
|
main = main.replace(/(\s*)const app = createApp\(App\)/, '\n\n$1const app = createApp(App)')
|
|
223
217
|
main = main.replace(/\n{3,}/g, '\n\n')
|
|
224
218
|
|
|
225
|
-
fs.writeFileSync(
|
|
226
|
-
path.join(targetDir, `src/${mainFile}`),
|
|
227
|
-
main
|
|
228
|
-
)
|
|
219
|
+
fs.writeFileSync(path.join(targetDir, `src/${mainFile}`), main)
|
|
229
220
|
fs.unlinkSync(mainTplPath)
|
|
230
221
|
|
|
231
222
|
// 7️⃣ 生成 package.json
|
|
232
223
|
const pkgTpl = path.join(targetDir, 'package.json.tpl')
|
|
233
|
-
if
|
|
224
|
+
if(fs.existsSync(pkgTpl)) {
|
|
234
225
|
let pkg = fs.readFileSync(pkgTpl, 'utf-8')
|
|
235
226
|
|
|
236
227
|
const optionalDeps = {}
|
|
237
|
-
if
|
|
238
|
-
if
|
|
239
|
-
optionalDeps['pinia'] = '^
|
|
240
|
-
optionalDeps['pinia-plugin-persistedstate'] = '^
|
|
228
|
+
if(features.router) optionalDeps['vue-router'] = '^5.0.3'
|
|
229
|
+
if(features.pinia) {
|
|
230
|
+
optionalDeps['pinia'] = '^3.0.4'
|
|
231
|
+
optionalDeps['pinia-plugin-persistedstate'] = '^4.7.1'
|
|
241
232
|
}
|
|
242
|
-
if
|
|
243
|
-
if
|
|
244
|
-
optionalDeps['element-plus'] = '^2.
|
|
245
|
-
optionalDeps['@element-plus/icons-vue'] = '^2.3.
|
|
233
|
+
if(features.axios) optionalDeps['axios'] = '^1.13.6'
|
|
234
|
+
if(features.ui.includes('element')) {
|
|
235
|
+
optionalDeps['element-plus'] = '^2.13.3'
|
|
236
|
+
optionalDeps['@element-plus/icons-vue'] = '^2.3.2'
|
|
246
237
|
}
|
|
247
|
-
if
|
|
238
|
+
if(features.ui.includes('vant')) {
|
|
248
239
|
optionalDeps['vant'] = '^4.9.22'
|
|
249
240
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if
|
|
253
|
-
if
|
|
254
|
-
if
|
|
255
|
-
|
|
256
|
-
// ========== 增强插件依赖结束 ==========
|
|
241
|
+
// 增强插件依赖
|
|
242
|
+
if(extraPlugins.includes('vueuse')) optionalDeps['@vueuse/core'] = '^14.2.1'
|
|
243
|
+
if(extraPlugins.includes('dayjs')) optionalDeps['dayjs'] = '^1.11.19'
|
|
244
|
+
if(extraPlugins.includes('lodash')) optionalDeps['lodash'] = '^4.17.23'
|
|
245
|
+
if(autoRoute) optionalDeps['vite-plugin-pages'] = '^0.33.3'
|
|
257
246
|
|
|
258
247
|
let depsStr = ''
|
|
259
248
|
const keys = Object.keys(optionalDeps)
|
|
260
|
-
if
|
|
249
|
+
if(keys.length > 0) {
|
|
261
250
|
depsStr = ',\n' + keys.map(k => ` "${k}": "${optionalDeps[k]}"`).join(',\n')
|
|
262
251
|
}
|
|
263
252
|
|
|
@@ -269,18 +258,118 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
|
|
269
258
|
fs.unlinkSync(pkgTpl)
|
|
270
259
|
}
|
|
271
260
|
|
|
272
|
-
// 8️⃣
|
|
261
|
+
// 8️⃣ 配置自动路由
|
|
262
|
+
if(autoRoute) {
|
|
263
|
+
const viteConfigPath = path.join(targetDir, `vite.config.${language === 'ts' ? 'ts' : 'js'}`)
|
|
264
|
+
if(fs.existsSync(viteConfigPath)) {
|
|
265
|
+
let viteConfig = fs.readFileSync(viteConfigPath, 'utf-8')
|
|
266
|
+
if(!viteConfig.includes("import fs from 'fs'")) {
|
|
267
|
+
viteConfig = `import fs from 'fs'\n${viteConfig}`
|
|
268
|
+
}
|
|
269
|
+
// 确保顶部 import Pages
|
|
270
|
+
if(!viteConfig.includes("import Pages from 'vite-plugin-pages'")) {
|
|
271
|
+
viteConfig = viteConfig.replace(/(import .*?from .*?\n)/, `$1import Pages from 'vite-plugin-pages'\n`)
|
|
272
|
+
}
|
|
273
|
+
viteConfig = viteConfig.replace(/plugins:\s*\[/, `plugins: [
|
|
274
|
+
Pages({
|
|
275
|
+
dirs: 'src/views',
|
|
276
|
+
extensions: ['vue'],
|
|
277
|
+
exclude: ['**/_*.vue'],
|
|
278
|
+
async extendRoute(route) {
|
|
279
|
+
const componentPath = path.resolve(process.cwd(), route.component.slice(1))
|
|
280
|
+
const dirPath = path.dirname(componentPath)
|
|
281
|
+
const metaFile = path.resolve(dirPath, 'meta.json')
|
|
282
|
+
if(fs.existsSync(metaFile)) {
|
|
283
|
+
try {
|
|
284
|
+
const metaContent = fs.readFileSync(metaFile, 'utf-8')
|
|
285
|
+
const meta = JSON.parse(metaContent)
|
|
286
|
+
route.meta = { ...(route.meta || {}), ...meta }
|
|
287
|
+
} catch(err) {
|
|
288
|
+
console.warn(\`加载 \${metaFile} 失败:\`, err)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return { ...route }
|
|
292
|
+
}
|
|
293
|
+
}),`)
|
|
294
|
+
fs.writeFileSync(viteConfigPath, viteConfig)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 创建 Home/meta.json
|
|
298
|
+
const homeMetaPath = path.join(targetDir, 'src/views/home/meta.json')
|
|
299
|
+
if(!fs.existsSync(homeMetaPath)) {
|
|
300
|
+
fs.writeFileSync(homeMetaPath, JSON.stringify({ title: '首页' }, null, 2))
|
|
301
|
+
}
|
|
302
|
+
if(language === 'ts') {
|
|
303
|
+
// 生成 types 目录
|
|
304
|
+
const typesDir = path.join(targetDir, 'src/types')
|
|
305
|
+
if(!fs.existsSync(typesDir)) fs.mkdirSync(typesDir, { recursive: true })
|
|
306
|
+
|
|
307
|
+
// 创建 vite-plugin-pages.d.ts
|
|
308
|
+
const vitePagesDtsPath = path.join(typesDir, 'vite-plugin-pages.d.ts')
|
|
309
|
+
const vitePagesDtsContent = `declare module '~pages' {
|
|
310
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
311
|
+
const routes: RouteRecordRaw[]
|
|
312
|
+
export default routes
|
|
313
|
+
}
|
|
314
|
+
`
|
|
315
|
+
fs.writeFileSync(vitePagesDtsPath, vitePagesDtsContent)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// 9️⃣ 替换 router/index.js
|
|
321
|
+
if(features.router) {
|
|
322
|
+
const routerIndexPath = path.join(targetDir, `src/router/index.${language === 'ts' ? 'ts' : 'js'}`)
|
|
323
|
+
if(autoRoute) {
|
|
324
|
+
fs.writeFileSync(
|
|
325
|
+
routerIndexPath,
|
|
326
|
+
`import { createRouter, createWebHistory } from 'vue-router'
|
|
327
|
+
import routes from '~pages'
|
|
328
|
+
|
|
329
|
+
routes.unshift({
|
|
330
|
+
path: '/',
|
|
331
|
+
redirect: '/home'
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
export default createRouter({
|
|
335
|
+
history: createWebHistory(),
|
|
336
|
+
routes
|
|
337
|
+
})
|
|
338
|
+
`
|
|
339
|
+
)
|
|
340
|
+
} else {
|
|
341
|
+
fs.writeFileSync(
|
|
342
|
+
routerIndexPath,
|
|
343
|
+
`import { createRouter, createWebHistory } from 'vue-router'
|
|
344
|
+
|
|
345
|
+
const routes = [
|
|
346
|
+
{
|
|
347
|
+
path: '/',
|
|
348
|
+
component: () => import('@/views/home/index.vue')
|
|
349
|
+
}
|
|
350
|
+
]
|
|
351
|
+
|
|
352
|
+
export default createRouter({
|
|
353
|
+
history: createWebHistory(),
|
|
354
|
+
routes
|
|
355
|
+
})
|
|
356
|
+
`
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// 1️⃣0️⃣ 安装依赖
|
|
273
362
|
console.log('📦 安装依赖中...')
|
|
274
363
|
execSync('npm install', { cwd: targetDir, stdio: 'inherit' })
|
|
275
|
-
if
|
|
364
|
+
if(extraPlugins.includes('tailwind')) {
|
|
276
365
|
execSync('npm install tailwindcss @tailwindcss/postcss postcss', { cwd: targetDir, stdio: 'inherit' })
|
|
277
366
|
}
|
|
278
367
|
|
|
279
|
-
//
|
|
280
|
-
if
|
|
368
|
+
// 1️⃣1️⃣ 运行 dev
|
|
369
|
+
if(runDev) {
|
|
281
370
|
console.log('🚀 启动开发服务器...')
|
|
282
371
|
execSync('npm run dev', { cwd: targetDir, stdio: 'inherit' })
|
|
283
372
|
} else {
|
|
284
373
|
console.log(`\n✅ 项目创建完成\n👉 cd ${projectName}\n👉 npm run dev\n`)
|
|
285
374
|
}
|
|
286
|
-
})()
|
|
375
|
+
})()
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
- ⚡ 基于 Vite + Vue 3,启动快、构建快
|
|
11
11
|
- 📜 支持 JavaScript / TypeScript 自由选择
|
|
12
|
-
- 🧭 可选集成 Vue Router
|
|
12
|
+
- 🧭 可选集成 Vue Router(支持动态参数路由)
|
|
13
13
|
- 🗂️ 可选集成 Pinia(含持久化)
|
|
14
14
|
- 📡 内置 Axios 请求方案
|
|
15
15
|
- 🖥️ / 📱 支持 Element Plus / Vant
|
|
@@ -70,6 +70,11 @@ project-name
|
|
|
70
70
|
│ ├─ types/ —— 类型声明文件
|
|
71
71
|
│ ├─ utils/ —— 工具方法、请求封装
|
|
72
72
|
│ ├─ views/ —— 页面级组件(路由页面)
|
|
73
|
+
│ │ └─ home/ —— 示例页面文件夹
|
|
74
|
+
│ │ ├─ index.vue —— 默认首页 /home
|
|
75
|
+
│ │ ├─ meta.json —— 页面 meta 信息
|
|
76
|
+
│ │ └─ [id]/ —— 动态参数路由示例
|
|
77
|
+
│ │ └─ [name].vue —— 路由 /home/:id/:name
|
|
73
78
|
│ ├─ App.vue —— 根组件
|
|
74
79
|
│ ├─ main.js / main.ts —— 项目启动入口
|
|
75
80
|
│ └─ style.css —— 全局样式文件
|
|
@@ -125,18 +130,18 @@ npm run dev
|
|
|
125
130
|
## 🌐 技术栈官网链接
|
|
126
131
|
|
|
127
132
|
- [Vite](https://vitejs.dev/)
|
|
128
|
-
- [Vue 3](https://vuejs.org/)
|
|
133
|
+
- [Vue 3](https://vuejs.org/)
|
|
129
134
|
- [JavaScript](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)
|
|
130
135
|
- [TypeScript](https://www.typescriptlang.org/)
|
|
131
|
-
- [Vue Router](https://router.vuejs.org/)
|
|
132
|
-
- [Pinia](https://pinia.vuejs.org/)
|
|
133
|
-
- [Axios](https://axios-http.com/)
|
|
136
|
+
- [Vue Router](https://router.vuejs.org/)
|
|
137
|
+
- [Pinia](https://pinia.vuejs.org/)
|
|
138
|
+
- [Axios](https://axios-http.com/)
|
|
134
139
|
- [Element Plus](https://element-plus.org/)
|
|
135
140
|
- [Vant](https://vant-contrib.gitee.io/vant/)
|
|
136
141
|
- [VueUse](https://vueuse.org/)
|
|
137
142
|
- [Lodash](https://lodash.com/)
|
|
138
143
|
- [Day.js](https://day.js.org/)
|
|
139
|
-
-
|
|
144
|
+
- [Tailwind CSS](https://tailwindcss.com/)
|
|
140
145
|
|
|
141
146
|
---
|
|
142
147
|
|
|
@@ -189,33 +194,41 @@ export default defineConfig({
|
|
|
189
194
|
|
|
190
195
|
---
|
|
191
196
|
|
|
192
|
-
### 3️⃣
|
|
197
|
+
### 3️⃣ 路由结构(支持动态参数)
|
|
193
198
|
|
|
194
199
|
文件:`src/router/index.ts` `src/router/index.js`
|
|
195
200
|
```ts
|
|
196
|
-
import { createRouter, createWebHistory } from 'vue-router'
|
|
197
|
-
import
|
|
198
|
-
|
|
199
|
-
const routes = [
|
|
200
|
-
{ path: '/', name: 'Home', component: Home },
|
|
201
|
-
{ path: '/about', name: 'About', component: () => import('../views/About.vue') },
|
|
202
|
-
];
|
|
201
|
+
import { createRouter, createWebHistory } from 'vue-router'
|
|
202
|
+
import routes from '~pages' // 自动生成的路由
|
|
203
203
|
|
|
204
204
|
const router = createRouter({
|
|
205
205
|
history: createWebHistory(),
|
|
206
|
-
routes
|
|
207
|
-
})
|
|
206
|
+
routes
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
export default router
|
|
210
|
+
```
|
|
208
211
|
|
|
209
|
-
|
|
212
|
+
> 🔹 页面文件夹结构示例:
|
|
213
|
+
```text
|
|
214
|
+
src/views/home/
|
|
215
|
+
├─ index.vue -> /home
|
|
216
|
+
└─ [id]/[name].vue -> /home/:id/:name
|
|
210
217
|
```
|
|
211
218
|
|
|
212
|
-
> 🔹
|
|
219
|
+
> 🔹 获取路由参数:
|
|
220
|
+
```ts
|
|
221
|
+
import { useRoute } from 'vue-router'
|
|
222
|
+
const route = useRoute()
|
|
223
|
+
console.log(route.params.id)
|
|
224
|
+
console.log(route.params.name)
|
|
225
|
+
```
|
|
213
226
|
|
|
214
227
|
---
|
|
215
228
|
|
|
216
229
|
### 4️⃣ 页面内容与样式
|
|
217
230
|
|
|
218
|
-
文件示例:`src/views/
|
|
231
|
+
文件示例:`src/views/home/index.vue`
|
|
219
232
|
```vue
|
|
220
233
|
<template>
|
|
221
234
|
<div class="home-container">
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"less": "^4.5.1",
|
|
13
13
|
"tslib": "^2.8.1",
|
|
14
|
-
"vue": "^3.5.
|
|
14
|
+
"vue": "^3.5.25"__OPTIONAL_DEP__
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
18
|
-
"vite": "
|
|
17
|
+
"@vitejs/plugin-vue": "^6.0.2",
|
|
18
|
+
"vite": "^8.0.0-beta.13"
|
|
19
19
|
},
|
|
20
20
|
"overrides": {
|
|
21
|
-
"vite": "
|
|
21
|
+
"vite": "^8.0.0-beta.13"
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
- ⚡ 基于 Vite + Vue 3,启动快、构建快
|
|
11
11
|
- 📜 支持 JavaScript / TypeScript 自由选择
|
|
12
|
-
- 🧭 可选集成 Vue Router
|
|
12
|
+
- 🧭 可选集成 Vue Router(支持动态参数路由)
|
|
13
13
|
- 🗂️ 可选集成 Pinia(含持久化)
|
|
14
14
|
- 📡 内置 Axios 请求方案
|
|
15
15
|
- 🖥️ / 📱 支持 Element Plus / Vant
|
|
@@ -70,6 +70,11 @@ project-name
|
|
|
70
70
|
│ ├─ types/ —— 类型声明文件
|
|
71
71
|
│ ├─ utils/ —— 工具方法、请求封装
|
|
72
72
|
│ ├─ views/ —— 页面级组件(路由页面)
|
|
73
|
+
│ │ └─ home/ —— 示例页面文件夹
|
|
74
|
+
│ │ ├─ index.vue —— 默认首页 /home
|
|
75
|
+
│ │ ├─ meta.json —— 页面 meta 信息
|
|
76
|
+
│ │ └─ [id]/ —— 动态参数路由示例
|
|
77
|
+
│ │ └─ [name].vue —— 路由 /home/:id/:name
|
|
73
78
|
│ ├─ App.vue —— 根组件
|
|
74
79
|
│ ├─ main.js / main.ts —— 项目启动入口
|
|
75
80
|
│ └─ style.css —— 全局样式文件
|
|
@@ -125,18 +130,18 @@ npm run dev
|
|
|
125
130
|
## 🌐 技术栈官网链接
|
|
126
131
|
|
|
127
132
|
- [Vite](https://vitejs.dev/)
|
|
128
|
-
- [Vue 3](https://vuejs.org/)
|
|
133
|
+
- [Vue 3](https://vuejs.org/)
|
|
129
134
|
- [JavaScript](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)
|
|
130
135
|
- [TypeScript](https://www.typescriptlang.org/)
|
|
131
|
-
- [Vue Router](https://router.vuejs.org/)
|
|
132
|
-
- [Pinia](https://pinia.vuejs.org/)
|
|
133
|
-
- [Axios](https://axios-http.com/)
|
|
136
|
+
- [Vue Router](https://router.vuejs.org/)
|
|
137
|
+
- [Pinia](https://pinia.vuejs.org/)
|
|
138
|
+
- [Axios](https://axios-http.com/)
|
|
134
139
|
- [Element Plus](https://element-plus.org/)
|
|
135
140
|
- [Vant](https://vant-contrib.gitee.io/vant/)
|
|
136
141
|
- [VueUse](https://vueuse.org/)
|
|
137
142
|
- [Lodash](https://lodash.com/)
|
|
138
143
|
- [Day.js](https://day.js.org/)
|
|
139
|
-
-
|
|
144
|
+
- [Tailwind CSS](https://tailwindcss.com/)
|
|
140
145
|
|
|
141
146
|
---
|
|
142
147
|
|
|
@@ -189,33 +194,41 @@ export default defineConfig({
|
|
|
189
194
|
|
|
190
195
|
---
|
|
191
196
|
|
|
192
|
-
### 3️⃣
|
|
197
|
+
### 3️⃣ 路由结构(支持动态参数)
|
|
193
198
|
|
|
194
199
|
文件:`src/router/index.ts` `src/router/index.js`
|
|
195
200
|
```ts
|
|
196
|
-
import { createRouter, createWebHistory } from 'vue-router'
|
|
197
|
-
import
|
|
198
|
-
|
|
199
|
-
const routes = [
|
|
200
|
-
{ path: '/', name: 'Home', component: Home },
|
|
201
|
-
{ path: '/about', name: 'About', component: () => import('../views/About.vue') },
|
|
202
|
-
];
|
|
201
|
+
import { createRouter, createWebHistory } from 'vue-router'
|
|
202
|
+
import routes from '~pages' // 自动生成的路由
|
|
203
203
|
|
|
204
204
|
const router = createRouter({
|
|
205
205
|
history: createWebHistory(),
|
|
206
|
-
routes
|
|
207
|
-
})
|
|
206
|
+
routes
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
export default router
|
|
210
|
+
```
|
|
208
211
|
|
|
209
|
-
|
|
212
|
+
> 🔹 页面文件夹结构示例:
|
|
213
|
+
```text
|
|
214
|
+
src/views/home/
|
|
215
|
+
├─ index.vue -> /home
|
|
216
|
+
└─ [id]/[name].vue -> /home/:id/:name
|
|
210
217
|
```
|
|
211
218
|
|
|
212
|
-
> 🔹
|
|
219
|
+
> 🔹 获取路由参数:
|
|
220
|
+
```ts
|
|
221
|
+
import { useRoute } from 'vue-router'
|
|
222
|
+
const route = useRoute()
|
|
223
|
+
console.log(route.params.id)
|
|
224
|
+
console.log(route.params.name)
|
|
225
|
+
```
|
|
213
226
|
|
|
214
227
|
---
|
|
215
228
|
|
|
216
229
|
### 4️⃣ 页面内容与样式
|
|
217
230
|
|
|
218
|
-
文件示例:`src/views/
|
|
231
|
+
文件示例:`src/views/home/index.vue`
|
|
219
232
|
```vue
|
|
220
233
|
<template>
|
|
221
234
|
<div class="home-container">
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"less": "^4.5.1",
|
|
13
13
|
"tslib": "^2.8.1",
|
|
14
|
-
"vue": "^3.5.
|
|
14
|
+
"vue": "^3.5.25"__OPTIONAL_DEP__
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@types/node": "^24.
|
|
18
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
17
|
+
"@types/node": "^24.11.0",
|
|
18
|
+
"@vitejs/plugin-vue": "^6.0.2",
|
|
19
19
|
"@vue/tsconfig": "^0.8.1",
|
|
20
20
|
"typescript": "~5.9.3",
|
|
21
|
-
"vite": "
|
|
22
|
-
"vue-tsc": "^3.1.
|
|
21
|
+
"vite": "^8.0.0-beta.13",
|
|
22
|
+
"vue-tsc": "^3.1.5"
|
|
23
23
|
},
|
|
24
24
|
"overrides": {
|
|
25
|
-
"vite": "
|
|
25
|
+
"vite": "^8.0.0-beta.13"
|
|
26
26
|
}
|
|
27
27
|
}
|
|
File without changes
|
|
File without changes
|