docs: update docs

This commit is contained in:
fonghehe 2026-04-28 10:16:46 +08:00
parent 235386120f
commit 5b32fedd9a
26 changed files with 9289 additions and 7100 deletions

View File

@ -1,31 +0,0 @@
{
"hash": "239dbc0d",
"configHash": "0cbf0876",
"lockfileHash": "05441bb6",
"browserHash": "dec8ef8e",
"optimized": {
"vue": {
"src": "../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "c8a520fe",
"needsInterop": false
},
"vitepress > @vue/devtools-api": {
"src": "../../../../node_modules/@vue/devtools-api/dist/index.js",
"file": "vitepress___@vue_devtools-api.js",
"fileHash": "b3eaab79",
"needsInterop": false
},
"vitepress > @vueuse/core": {
"src": "../../../../node_modules/@vueuse/core/index.mjs",
"file": "vitepress___@vueuse_core.js",
"fileHash": "e7d34837",
"needsInterop": false
}
},
"chunks": {
"chunk-RMNUDMGF": {
"file": "chunk-RMNUDMGF.js"
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// node_modules/@vue/devtools-shared/dist/index.js
// node_modules/.pnpm/@vue+devtools-shared@7.7.7/node_modules/@vue/devtools-shared/dist/index.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@ -258,7 +258,7 @@ function isUrlString(str) {
}
var deepClone = (0, import_rfdc.default)({ circles: true });
// node_modules/perfect-debounce/dist/index.mjs
// node_modules/.pnpm/perfect-debounce@1.0.0/node_modules/perfect-debounce/dist/index.mjs
var DEBOUNCE_DEFAULTS = {
trailing: true
};
@ -315,7 +315,7 @@ async function _applyPromised(fn, _this, args) {
return await fn.apply(_this, args);
}
// node_modules/hookable/dist/index.mjs
// node_modules/.pnpm/hookable@5.5.3/node_modules/hookable/dist/index.mjs
function flatHooks(configHooks, hooks2 = {}, parentName) {
for (const key in configHooks) {
const subHook = configHooks[key];
@ -520,11 +520,11 @@ function createHooks() {
return new Hookable();
}
// node_modules/birpc/dist/index.mjs
// node_modules/.pnpm/birpc@2.5.0/node_modules/birpc/dist/index.mjs
var { clearTimeout: clearTimeout2, setTimeout: setTimeout2 } = globalThis;
var random = Math.random.bind(Math);
// node_modules/@vue/devtools-kit/dist/index.js
// node_modules/.pnpm/@vue+devtools-kit@7.7.7/node_modules/@vue/devtools-kit/dist/index.js
var __create2 = Object.create;
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;

View File

@ -35,9 +35,9 @@ import {
unref,
watch,
watchEffect
} from "./chunk-RMNUDMGF.js";
} from "./chunk-DKDEFF44.js";
// node_modules/@vueuse/shared/index.mjs
// node_modules/.pnpm/@vueuse+shared@12.8.2/node_modules/@vueuse/shared/index.mjs
function computedEager(fn, options) {
var _a;
const result = shallowRef();
@ -1569,7 +1569,7 @@ function whenever(source, cb, options) {
return stop;
}
// node_modules/@vueuse/core/index.mjs
// node_modules/.pnpm/@vueuse+core@12.8.2/node_modules/@vueuse/core/index.mjs
function computedAsync(evaluationCallback, initialState, optionsOrRef) {
let options;
if (isRef(optionsOrRef)) {

View File

@ -2,7 +2,10 @@ import {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
@ -10,8 +13,10 @@ import {
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
@ -51,12 +56,17 @@ import {
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
@ -89,6 +99,7 @@ import {
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
popScopeId,
provide,
@ -129,9 +140,13 @@ import {
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
@ -153,12 +168,15 @@ import {
withMemo,
withModifiers,
withScopeId
} from "./chunk-RMNUDMGF.js";
} from "./chunk-DKDEFF44.js";
export {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
@ -166,8 +184,10 @@ export {
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
@ -207,12 +227,17 @@ export {
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
@ -245,6 +270,7 @@ export {
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
popScopeId,
provide,
@ -285,9 +311,13 @@ export {
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,

View File

@ -3,8 +3,8 @@ export default {
collapsible: true,
items: [
{ text: "启动项目", link: "/guide/vue3/start" },
{ text: "vite.config.ts 基础配置", link: "/guide/vue3/base" },
{ text: "vite插件集成", link: "/guide/vue3/vite" },
{ text: "vite.config.mts 基础配置", link: "/guide/vue3/base" },
{ text: "Vite 插件集成", link: "/guide/vue3/vite" },
{ text: "多环境变量", link: "/guide/vue3/env" },
{ text: "viewport 适配方案", link: "/guide/vue3/viewport" },
{ text: "UI组件库", link: "/guide/vue3/ui" },

View File

@ -3,7 +3,6 @@
欢迎有意愿参与到开源的朋友,加入到本文档的编写,书写文档不仅是教会别人知识,更是用自己的表达方式概括自己所学习知识的一种方式,这对个人来说是不可多得的成长机会。
```bash
# 拉取项目
git clone https://github.com/sunniejs/vue-h5-template
@ -11,9 +10,11 @@ git clone https://github.com/sunniejs/vue-h5-template
git checkout -b docs origin/docs
# 安装依赖
yarn install
pnpm install
# 启动项目
yarn start
pnpm start
# 打包
pnpm build
```

View File

@ -1,12 +1,12 @@
# 快速上手
## node 版本要求
## Node 版本要求
推荐 20.19.0+以上的版本,毕竟 2025 年了,别掐着 16+的版本了,你也可以使用[nvm](https://github.com/nvm-sh/nvm)或[nvm-windows](https://github.com/coreybutler/nvm-windows)在同一台电脑上管理多个 node 版本。
推荐 Node.js 20.10.0+ 以上的版本,你可以使用 [nvm](https://github.com/nvm-sh/nvm) [nvm-windows](https://github.com/coreybutler/nvm-windows) 在同一台电脑上管理多个 Node 版本。
## 包管理器
尽量使用 yarn 或者 pnpm本项目仅保证在 yarn 或 pnpm 下正确运行npm 涉及到网络环境等各种情况的限制不做过多考虑
推荐使用 pnpm>= 9.12.0),本项目仅保证在 pnpm 下正确运行
## 启动项目

View File

@ -1,67 +1,123 @@
# axios 封装及接口管理
`utils/request.js` 封装 axios , 开发者需要根据后台接口做修改。
`utils/request/index.ts` 封装 axios开发者需要根据后台接口做修改。
- `service.interceptors.request.use` 里可以设置请求头,比如设置 `token`
- `config.hideloading` 是在 api 文件夹下的接口参数里设置,下文会讲
- `service.interceptors.response.use` 里可以对接口返回数据处理,比如 401 删除本地信息,重新登录
```javascript
## Axios 封装
```typescript
import axios from "axios";
import store from "@/store";
import { Toast } from "vant";
// 根据环境不同引入不同api地址
import { baseApi } from "@/config";
// create an axios instance
const service = axios.create({
baseURL: baseApi, // url = base api url + request url
withCredentials: true, // send cookies when cross-domain requests
timeout: 5000, // request timeout
import type {
AxiosError,
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
InternalAxiosRequestConfig,
} from "axios";
import { showToast } from "vant";
const service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || "",
withCredentials: false,
timeout: 10000,
});
// request 拦截器 request interceptor
service.interceptors.request.use(
(config) => {
// 不传递默认开启loading
if (!config.hideloading) {
// loading
Toast.loading({
forbidClick: true,
});
}
if (store.getters.token) {
config.headers["X-Token"] = "";
}
(config: InternalAxiosRequestConfig) => {
return config;
},
(error) => {
// do something with request error
console.log(error); // for debug
(error: AxiosError) => {
return Promise.reject(error);
}
},
);
// respone拦截器
service.interceptors.response.use(
(response) => {
Toast.clear();
(response: AxiosResponse) => {
const res = response.data;
if (res.status && res.status !== 200) {
// 登录超时,重新登录
if (res.status === 401) {
store.dispatch("FedLogOut").then(() => {
location.reload();
});
}
return Promise.reject(res || "error");
if (res.code !== 200) {
showToast(res.msg);
return Promise.reject(res.msg || "Error");
} else {
return Promise.resolve(res);
return res.data;
}
},
(error) => {
Toast.clear();
console.log("err" + error); // for debug
return Promise.reject(error);
}
(error: AxiosError) => {
showToast(error.message);
return Promise.reject(error.message);
},
);
export const http = {
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
return service.get(url, config);
},
post<T = any>(
url: string,
data?: object,
config?: AxiosRequestConfig,
): Promise<T> {
return service.post(url, data, config);
},
put<T = any>(
url: string,
data?: object,
config?: AxiosRequestConfig,
): Promise<T> {
return service.put(url, data, config);
},
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
return service.delete(url, config);
},
};
export default service;
```
## useFetchApi 封装
项目同时提供了基于 `@vueuse/core``createFetch` 封装,支持响应式的请求:
```typescript
import { createFetch } from "@vueuse/core";
import { useCookies } from "@vueuse/integrations/useCookies";
import { showNotify } from "vant";
const useFetchApi = createFetch({
baseUrl: "",
options: {
async beforeFetch({ options }) {
const myToken =
useCookies().get(
(import.meta.env.VITE_TOKEN_KEY as string) || "Authorization",
) || "";
options.headers = {
...options.headers,
Authorization: `Bearer ${myToken}`,
};
return { options };
},
afterFetch(ctx) {
// 处理响应数据...
return ctx;
},
},
});
export default useFetchApi;
```
## 接口管理
`src/api/index.ts` 中统一管理接口:
```typescript
import { http } from "@/utils/request";
export function loginPassword() {
return http.post("/mock-api/login", {
data: { name: "123" },
});
}
```

View File

@ -1,4 +1,4 @@
# vite.config.ts 基础配置
# vite.config.mts 基础配置
如果你的 `Vue Router` 模式是 hash
@ -6,7 +6,7 @@
base: './',
```
如果你的 `Vue Router` 模式是 history 这里的 publicPath 和你的 `Vue Router` `base` **保持一致**
如果你的 `Vue Router` 模式是 history 这里的 base 和你的 `Vue Router` `base` **保持一致**
```javascript
base: '/app/',
@ -46,10 +46,9 @@ export default function ({ command, mode }: ConfigEnv): UserConfig {
css: {
preprocessorOptions: {
scss: {
quietDeps: true,
silenceDeprecations: ["legacy-js-api"],
// 配置 nutui 全局 scss 变量
additionalData: `@use "@nutui/nutui/dist/styles/variables.scss" as *; @use '@/styles/vant.scss' as *;`,
additionalData: `@use "@/styles/variable.scss" as *;@use "@nutui/nutui/dist/styles/variables.scss" as *;`,
quietDeps: true,
},
},
},

View File

@ -1,13 +1,48 @@
`package.json` 里的 `scripts` 配置 `dev` `dev:test` `dev:prod` ,通过 `--mode xxx` 来执行不同环境
# 多环境变量
- 通过 `yarn dev` 启动本地环境参数 , 执行 `development`
- 通过 `yarn dev:test` 启动测试环境参数 , 执行 `test`
- 通过 `yarn dev:prod` 启动正式环境参数 , 执行 `prod`
`package.json` 里的 `scripts` 配置 `dev` `build`,通过 `--mode xxx` 来执行不同环境
```javascript
- 通过 `pnpm dev` 启动本地环境参数,执行 `development`
- 通过 `pnpm build` 打包正式环境参数,执行 `production`
```json
"scripts": {
"dev": "vite",
"dev:test": "vite --mode test",
"dev:prod": "vite --mode production",
"build": "vite build"
}
```
## 环境变量配置
项目中有三个环境变量文件:
- `.env.development` - 开发环境
- `.env.test` - 测试环境
- `.env.production` - 生产环境
可配置的变量:
```bash
# 是否开启数据mock
VITE_USE_MOCK=true
# Token Key
VITE_TOKEN_KEY=Authorization
# 是否开启调试工具
VITE_USE_ERUDA=true
# 是否开启压缩
VITE_USE_COMPRESS=false
# 是否开启打包后生成报告
VITE_USE_REPORT=false
# 是否开启https
VITE_USE_HTTPS=false
# 是否开启PWA
VITE_USE_PWA=false
```
在代码中通过 `import.meta.env.VITE_XXX` 访问环境变量。

View File

@ -1,51 +1,96 @@
<!--
* @Author: Vinton
* @Date: 2022-08-22 11:08:11
* @Description: file content
-->
# i18n 文本多语言解决方案
```javascript
import { createI18n } from 'vue-i18n'; // 引入第三方最新的i18n注意版本
项目采用 `vue-i18n` 实现多语言,并支持按需懒加载语言包,提升首屏加载速度。
/**
* @description: 加载当前配置的语言配置目录,随意添加
*/
export function loadLang() {
const modules: Record<string, any> = import.meta.glob('./lang/*.ts', { eager: true });
const langs: Record<string, any> = {};
## 目录结构
for (const path in modules) {
const name = path.replace(/(\.\/lang\/|\.ts)/g, '');
langs[name] = modules[path].lang;
}
return langs;
}
```bash
├── locales
│ ├── index.ts # i18n 配置和初始化
│ ├── lang-base.ts # 基础语言配置
│ └── langs
│ ├── en-US
│ │ └── common.json
│ └── zh-CN
│ └── common.json
```
## 核心实现
```typescript
import { createI18n } from "vue-i18n";
import type { App } from "vue";
const LOCALE_KEY = "lang";
const DEFAULT_LOCALE = "zh-CN";
// 扫描所有语言文件(懒加载)
const modules = import.meta.glob("./langs/**/*.json");
// i18n 实例(初始不加载 messages按需加载
export const i18n = createI18n({
// globalInjection: true,
// legacy: false,
locale: 'zh-cn', // 默认语言当前这里的数据要跟配置的lang目录下面的文件名字前缀一致
fallbackLocale: 'zh-cn',
messages: loadLang(), // 记载当前引入的语言目录的处理过后的数据
legacy: false,
globalInjection: true,
locale: "",
fallbackLocale: DEFAULT_LOCALE,
messages: {},
});
/**
* @description: 切换当前多语言
*/
export function setLang(locale?: string) {
if (locale) {
localStorage.setItem('lang', locale);
// 设置语言
export async function setLang(locale?: string) {
const target = locale || localStorage.getItem(LOCALE_KEY) || DEFAULT_LOCALE;
if (!i18n.global.availableLocales.includes(target)) {
// 按需加载语言包
const loader = localeLoaders[target];
if (loader) {
const messages = await loader();
i18n.global.setLocaleMessage(target, messages);
}
}
i18n.global.locale = locale || localStorage.getItem('lang') || '';
i18n.global.locale.value = target;
localStorage.setItem(LOCALE_KEY, target);
document.documentElement.lang = target;
}
// 初始化main.ts 调用)
export async function setupI18n(app: App) {
app.use(i18n);
await setLang();
}
```
# css图片解决方案
## 在组件中切换语言
目前在业务开发中CSS 的样式多语言也会经常用到,可能一些图片的字体比较复杂,代码很难实现。或者我们为了减少多语言的配置,加快开发效率也会使用多语言的配置,目前这里提供 scss 的图片多语言的方案
```html
<script setup lang="ts">
import { setLang } from "@/locales";
```css
const changeLang = (type: string) => {
setLang(type);
};
</script>
<template>
<button @click="changeLang('zh-CN')">中文</button>
<button @click="changeLang('en-US')">English</button>
</template>
```
## 在模板中使用
```html
<template>
<div>{{ $t('common.title') }}</div>
</template>
```
# CSS 图片多语言方案
目前在业务开发中CSS 的样式多语言也会经常用到。这里提供 scss 的图片多语言方案:
```scss
@mixin main-lang-bg($width, $height, $preUrl, $posUrl) {
width: $width;
height: $height;
@ -53,37 +98,29 @@ export function setLang(locale?: string) {
background-size: 100% 100%;
@include loop-lang-bg($preUrl, $posUrl);
}
// 背景图多语言
@mixin loop-lang-bg($preUrl, $posUrl) {
$list: zh-cn, en-us; // 配置需要的多语言,根据项目来
$list: zh-CN, en-US;
@each $i in $list {
&.#{$i} {
background-image: url('#{$preUrl}/#{$i}/#{$posUrl}');
background-image: url("#{$preUrl}/#{$i}/#{$posUrl}");
}
}
}
```
## 定义图片的目录格式
```bash
├── button
│ ├── en-us
│ │ └── confirm.png
│ ├── zh-cn
│ │ └── confirm.png
```
## HTML的使用方式
## 使用方式
```html
<template>
<div :class="['btn-confirm', i18n.global.locale]"></div>
<div :class="['btn-confirm', i18n.global.locale.value]"></div>
</template>
<script lang="ts" setup name="HomePage">
import { i18n } from '/@/i18n';
<script lang="ts" setup>
import { i18n } from "@/locales";
</script>
<style lang="scss" scoped>
.btn-confirm {
@include main-lang-bg(302px, 82px, '/@/assets/button', 'confirm.png');
}
.btn-confirm {
@include main-lang-bg(302px, 82px, "@/assets/button", "confirm.png");
}
</style>
```

View File

@ -1,3 +1,18 @@
# Eslint + Pettier + Stylelint 统一开发规范
# ESLint + Prettier + Stylelint 统一开发规范
根目录下的 `.eslintrc.js``.stylelint.config.js``.prettier.config.js` 内置了 lint 规则,帮助你规范地开发代码,有助于提高团队的代码质量和协作性,可以根据团队的规则进行修改
根目录下的 `eslint.config.mjs``stylelint.config.js``prettier.config.js` 内置了 lint 规则,帮助你规范地开发代码,有助于提高团队的代码质量和协作性,可以根据团队的规则进行修改。
项目使用 ESLint flat config`eslint.config.mjs`),配合 `husky` + `lint-staged` 在 git commit 时自动执行代码检查。
## 常用命令
```bash
# ESLint 检查并修复
pnpm lint:eslint
# Prettier 格式化
pnpm lint:prettier
# Stylelint 检查并修复
pnpm lint:stylelint
```

View File

@ -1,34 +1,30 @@
<!--
* @Author: Vinton
* @Date: 2022-08-22 10:39:13
* @Description: file content
-->
# Pinia 状态管理
下一代 vuex使用极其方便ts 兼容好
下一代 vuex使用极其方便ts 兼容好。项目使用 `pinia-plugin-persistedstate` 实现状态持久化。
目录结构
```bash
├── store
│ ├── modules
│ │ └── user.js
│ ├── index.js
│ │ └── user.ts
│ ├── index.ts
```
目前pinia分为两种编程模式Options API 和 Composition API我们这边都会列举出来实现的业务逻辑效果是一样的提供大家思路
### Options API:
### Options API当前项目使用:
```typescript
import { loginPassword } from "@/api";
import { defineStore } from "pinia";
```javascript
interface StoreUser {
token: string;
info: Record<any, any>;
}
export const useUserStore = defineStore({
id: 'app-user',
export const useUserStore = defineStore("user", {
state: (): StoreUser => ({
token: token,
token: "",
info: {},
}),
getters: {
@ -38,51 +34,63 @@ export const useUserStore = defineStore({
},
actions: {
setInfo(info: any) {
this.info = info ? info : '';
this.info = info ?? "";
},
login() {
return new Promise((resolve) => {
const { data } = loginPassword();
watch(data, () => {
this.setInfo(data.value);
// useCookies().set(VITE_TOKEN_KEY as string, data.value.token);
resolve(data.value);
});
});
async login() {
try {
const res = await loginPassword();
this.setInfo(res);
this.token = res.token;
return res;
} catch (error) {
console.error("Login failed", error);
throw error;
}
},
},
persist: {
pick: ["token"],
storage: localStorage,
},
});
```
### Composition API:
```javascript
export const useUserStore = defineStore('app-user', () => {
const Token = ref(token);
const info = ref<Record<any, any>>({});
const setInfo = (info: any) => {
info.value = info ? info : '';
};
const getUserInfo = () => {
return info || {};
};
const login = () => {
return new Promise((resolve) => {
const { data } = loginPassword();
watch(data, () => {
setInfo(data.value);
// useCookies().set(VITE_TOKEN_KEY as string, data.value.token);
resolve(data.value);
});
});
};
return {
Token,
info,
setInfo,
login,
getUserInfo,
};
})
```typescript
export const useUserStore = defineStore(
"user",
() => {
const token = ref("");
const info = ref<Record<any, any>>({});
const getUserInfo = () => info.value || {};
const setInfo = (data: any) => {
info.value = data ?? "";
};
const login = async () => {
try {
const res = await loginPassword();
setInfo(res);
token.value = res.token;
return res;
} catch (error) {
console.error("Login failed", error);
throw error;
}
};
return { token, info, getUserInfo, setInfo, login };
},
{
persist: {
pick: ["token"],
storage: localStorage,
},
},
);
```
使用

View File

@ -2,27 +2,57 @@
本案例采用 `hash` 模式,开发者根据需求修改 `mode` `base`
**注意**:如果你使用了 `history` 模式, `vue.config.js` 中的 `publicPath` 要做对应的**修改**
**注意**:如果你使用了 `history` 模式, `vite.config.mts` 中的 `base` 要做对应的**修改**
前往: [vite.config.js 基础配置](#base)
前往: [vite.config.mts 基础配置](/guide/vue3/base)
```javascript
import Vue from "vue";
import { createRouter, createWebHistory, Router } from "vue-router";
```typescript
import type { RouteRecordRaw } from "vue-router";
Vue.use(Router);
export const router = [
export const routes: RouteRecordRaw[] = [
{
name: "root",
path: "/",
redirect: "/home",
component: () => import("@/layout/basic/index.vue"),
component: () => import("@/layout/index.vue"),
children: [
{
path: "home",
component: () => import("@/views/home/index.vue"),
meta: {
title: "common.tabbar.home",
keepAlive: true,
},
},
// ... 其他子路由
],
},
{
name: "login",
path: "/login",
component: () => import("@/views/login/index.vue"),
meta: {
title: "",
keepAlive: true,
},
},
// 匹配不到重定向到首页
{
path: "/:pathMatch(.*)",
redirect: "/home",
},
];
```
const router: Router = createRouter({
history: createWebHistory(),
routes: routes,
路由实例创建:
```typescript
import { createRouter, createWebHashHistory } from "vue-router";
import routes from "./routes";
const router = createRouter({
history: createWebHashHistory(),
routes,
scrollBehavior: () => ({ left: 0, top: 0 }),
});
export default router;

View File

@ -1,16 +1,26 @@
# 启动项目
```bash
## 环境要求
- Node.js >= 20.10.0
- pnpm >= 9.12.0
```bash
# 拉取项目
git clone https://github.com/sunniejs/vue-h5-template
# 进入项目目录
cd vue-h5-template
# 安装依赖
- pnpm install
- yarn install
pnpm install
# 启动项目
- pnpm dev
- yarn dev
pnpm dev
# 打包
pnpm build
# 预览打包结果
pnpm preview
```

View File

@ -1,37 +1,32 @@
# 多 UI 组件库供选择
Vite 构建工具,`使用 vite-plugin-style-import``unplugin-vue-components/vite` 实现按需引入。
## 安装插件
```bash
pnpm add unplugin-vue-components/vite -D
```
使用 `unplugin-vue-components``unplugin-auto-import` 实现按需引入,无需手动注册组件。
## 使用组件库
vant 、 varlet 和 nutUI 可以使用组件按需加载
Vant、Varlet 和 NutUI 三个组件库均已配置按需加载。
`config/vite/plugins/component.ts`
`build/vite/plugins/component.ts` 下配置:
```javascript
```typescript
import { VantResolver, VarletUIResolver } from 'unplugin-vue-components/resolvers';
import NutUIResolver from '@nutui/auto-import-resolver';
...
// ...
resolvers: [VantResolver(), VarletUIResolver(), NutUIResolver()],
...
// ...
```
## 不需要某个组件库
只需删除对应的 resolvers 即可
只需删除对应的 resolvers 即可
删除后需全局搜索删除不需要的组件,避免报错
删除后需全局搜索删除不需要的组件,避免报错
## 参考文档
- [nutUI](https://nutui.jd.com/#/zh-CN/component/button)
- [NutUI](https://nutui.jd.com/#/zh-CN/component/button)
- [vant](https://vant-contrib.gitee.io/vant/#/zh-CN)
- [Vant](https://vant-ui.github.io/vant/#/zh-CN)
- [varlet](https://varlet-varletjs.vercel.app/#/zh-CN/button)
- [Varlet](https://varlet.pages.dev/#/zh-CN/button)

View File

@ -1,17 +1,20 @@
# vite
# Vite 插件集成
基于原生 ES 模块提供了丰富的内建功能如速度快到惊人的模块热更新HMR使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。更多关于[vite](https://cn.vitejs.dev/guide/)
基于原生 ES 模块提供了丰富的内建功能如速度快到惊人的模块热更新HMR使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。更多关于 [Vite](https://cn.vitejs.dev/guide/)
模版集成了如下的 vite 插件
模版集成了如下的 vite 插件(配置目录:`build/vite/plugins/`
- unplugin-auto-import按需加载自动引入
- unplugin-vue-components按需加载自动引入组件
- vite-plugin-compression开启.gz 压缩)
- vite-plugin-eruda控制台方便移动端调试
- vite-plugin-compression开启 .gz 压缩)
- @zhaojjiang/vite-plugin-eruda控制台方便移动端调试
- vite-plugin-imagemin图片压缩
- vite-plugin-mock引入 mockjs本地模拟接口
- vite-plugin-pages动态生成路由
- vite-plugin-progress构建显示进度条
- vite-plugin-restart监听配置文件修改自动重启 Vite
- vite-plugin-style-import按需引入样式文件
- vite-plugin-svg-icons加载 SVG 文件,自动引入)
- vite-plugin-pwaPWA 支持)
- vite-plugin-qrcode开发时生成二维码方便移动端调试
- @vitejs/plugin-basic-ssl本地 HTTPS 开发支持)
- rollup-plugin-visualizer打包分析报告

92
types/auto-imports.d.ts vendored Normal file
View File

@ -0,0 +1,92 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue').EffectScope
const Snackbar: typeof import('@varlet/ui').Snackbar
const acceptHMRUpdate: typeof import('pinia').acceptHMRUpdate
const computed: typeof import('vue').computed
const createApp: typeof import('vue').createApp
const createPinia: typeof import('pinia').createPinia
const customRef: typeof import('vue').customRef
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
const defineComponent: typeof import('vue').defineComponent
const defineStore: typeof import('pinia').defineStore
const effectScope: typeof import('vue').effectScope
const getActivePinia: typeof import('pinia').getActivePinia
const getCurrentInstance: typeof import('vue').getCurrentInstance
const getCurrentScope: typeof import('vue').getCurrentScope
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
const h: typeof import('vue').h
const inject: typeof import('vue').inject
const isProxy: typeof import('vue').isProxy
const isReactive: typeof import('vue').isReactive
const isReadonly: typeof import('vue').isReadonly
const isRef: typeof import('vue').isRef
const isShallow: typeof import('vue').isShallow
const mapActions: typeof import('pinia').mapActions
const mapGetters: typeof import('pinia').mapGetters
const mapState: typeof import('pinia').mapState
const mapStores: typeof import('pinia').mapStores
const mapWritableState: typeof import('pinia').mapWritableState
const markRaw: typeof import('vue').markRaw
const nextTick: typeof import('vue').nextTick
const onActivated: typeof import('vue').onActivated
const onBeforeMount: typeof import('vue').onBeforeMount
const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave
const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate
const onBeforeUnmount: typeof import('vue').onBeforeUnmount
const onBeforeUpdate: typeof import('vue').onBeforeUpdate
const onDeactivated: typeof import('vue').onDeactivated
const onErrorCaptured: typeof import('vue').onErrorCaptured
const onMounted: typeof import('vue').onMounted
const onRenderTracked: typeof import('vue').onRenderTracked
const onRenderTriggered: typeof import('vue').onRenderTriggered
const onScopeDispose: typeof import('vue').onScopeDispose
const onServerPrefetch: typeof import('vue').onServerPrefetch
const onUnmounted: typeof import('vue').onUnmounted
const onUpdated: typeof import('vue').onUpdated
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
const provide: typeof import('vue').provide
const reactive: typeof import('vue').reactive
const readonly: typeof import('vue').readonly
const ref: typeof import('vue').ref
const resolveComponent: typeof import('vue').resolveComponent
const setActivePinia: typeof import('pinia').setActivePinia
const setMapStoreSuffix: typeof import('pinia').setMapStoreSuffix
const shallowReactive: typeof import('vue').shallowReactive
const shallowReadonly: typeof import('vue').shallowReadonly
const shallowRef: typeof import('vue').shallowRef
const showToast: typeof import('vant/es').showToast
const storeToRefs: typeof import('pinia').storeToRefs
const toRaw: typeof import('vue').toRaw
const toRef: typeof import('vue').toRef
const toRefs: typeof import('vue').toRefs
const toValue: typeof import('vue').toValue
const triggerRef: typeof import('vue').triggerRef
const unref: typeof import('vue').unref
const useAttrs: typeof import('vue').useAttrs
const useCssModule: typeof import('vue').useCssModule
const useCssVars: typeof import('vue').useCssVars
const useId: typeof import('vue').useId
const useLink: typeof import('vue-router').useLink
const useModel: typeof import('vue').useModel
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSlots: typeof import('vue').useSlots
const useTemplateRef: typeof import('vue').useTemplateRef
const watch: typeof import('vue').watch
const watchEffect: typeof import('vue').watchEffect
const watchPostEffect: typeof import('vue').watchPostEffect
const watchSyncEffect: typeof import('vue').watchSyncEffect
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}

60
types/components.d.ts vendored Normal file
View File

@ -0,0 +1,60 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import { GlobalComponents } from 'vue'
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
NutAvatar: typeof import('@nutui/nutui')['Avatar']
NutButton: typeof import('@nutui/nutui')['Button']
NutCard: typeof import('@nutui/nutui')['Card']
NutCell: typeof import('@nutui/nutui')['Cell']
NutCellGroup: typeof import('@nutui/nutui')['CellGroup']
NutForm: typeof import('@nutui/nutui')['Form']
NutFormItem: typeof import('@nutui/nutui')['FormItem']
NutGrid: typeof import('@nutui/nutui')['Grid']
NutGridItem: typeof import('@nutui/nutui')['GridItem']
NutInput: typeof import('@nutui/nutui')['Input']
NutSwiper: typeof import('@nutui/nutui')['Swiper']
NutSwiperItem: typeof import('@nutui/nutui')['SwiperItem']
NutTabbar: typeof import('@nutui/nutui')['Tabbar']
NutTabbarItem: typeof import('@nutui/nutui')['TabbarItem']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
VanButton: typeof import('vant/es')['Button']
VanNavBar: typeof import('vant/es')['NavBar']
VarButton: typeof import('@varlet/ui')['_ButtonComponent']
VarSpace: typeof import('@varlet/ui')['_SpaceComponent']
}
}
// For TSX support
declare global {
const NutAvatar: typeof import('@nutui/nutui')['Avatar']
const NutButton: typeof import('@nutui/nutui')['Button']
const NutCard: typeof import('@nutui/nutui')['Card']
const NutCell: typeof import('@nutui/nutui')['Cell']
const NutCellGroup: typeof import('@nutui/nutui')['CellGroup']
const NutForm: typeof import('@nutui/nutui')['Form']
const NutFormItem: typeof import('@nutui/nutui')['FormItem']
const NutGrid: typeof import('@nutui/nutui')['Grid']
const NutGridItem: typeof import('@nutui/nutui')['GridItem']
const NutInput: typeof import('@nutui/nutui')['Input']
const NutSwiper: typeof import('@nutui/nutui')['Swiper']
const NutSwiperItem: typeof import('@nutui/nutui')['SwiperItem']
const NutTabbar: typeof import('@nutui/nutui')['Tabbar']
const NutTabbarItem: typeof import('@nutui/nutui')['TabbarItem']
const RouterLink: typeof import('vue-router')['RouterLink']
const RouterView: typeof import('vue-router')['RouterView']
const VanButton: typeof import('vant/es')['Button']
const VanNavBar: typeof import('vant/es')['NavBar']
const VarButton: typeof import('@varlet/ui')['_ButtonComponent']
const VarSpace: typeof import('@varlet/ui')['_SpaceComponent']
}