欢迎光临
我们一直在努力

项目安装:vue3 + TypeScript

由于每次在一个新环境电脑中都要去重新搭建项目,到处要去找资料,找官方文档去安装。所以我打算把工作中需要安装的环境都一一记录下来,等我下次在新的环境中能够快速去搭建项目。

一、环境版本:

nvm:1.2.2 (用于node版本切换,主页中有对应的安装教程)

node: 24.12.0

二、快速创建 Vue3+Vite+TS 基础项目

2.1. 在终端中执行

cnpm create vite@latest

2.2 安装router

注意:终端指令要进入到对应项目的目录,不然容易把依赖下载在外部文件中

2.2.1 安装router

cnpm add vue-router@4

2.2.2 配置 Vue Router 4(TS 版,添加路由类型约束)

// src/router/index.ts
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
// 导入页面组件(TS支持@路径别名,官方模板已配置)
import Home from '@/views/Home.vue'
import NotFound from '@/views/NotFound.vue'

// 路由规则:用RouteRecordRaw约束类型,避免路由配置错误
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// 路由懒加载(TS自动识别类型,无需额外约束)
component: () => import('@/views/About.vue')
},
// 404兜底路由(TS版写法和JS一致,类型由RouteRecordRaw约束)
{
path: '/:pathMatch(.*)*',
component: NotFound
}
]

// 创建路由实例
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})

export default router

2.2.3 创建 TS 版页面组件

Vue3 中组件使用的核心规范(适配 TS):

  • 组件文件放在src/components目录下(按业务 / 功能分类,框架已建好该目录);
  • 组件名遵循多单词大驼峰(如HelloWorld、CountButton),符合 Vue3+ESLint 规范;
  • 组件内用<script setup lang="ts">编写,天然支持 TS 类型约束;
  • 推荐局部引入(按需加载,减小打包体积),高频组件才用全局注册。
  • 在src下新建views文件夹,创建 3 个基础页面(Vue3+TS 的<script setup lang="ts">语法糖):

    src/views/Home.vue(首页)

    // src/views/Home.vue(首页)
    <template>
    <div class="home-page">
    <h1>Vue3+TS基础框架 – 首页</h1>
    <p>已集成:Vite + Vue3 + TS + Vue Router + Pinia</p>
    </div>
    </template>

    <script setup lang="ts">
    // lang="ts"标记为TS语法,Vue3+TS的核心写法
    defineOptions({ name: 'Home' }) // 给组件命名,方便调试和路由识别
    </script>

    <style scoped lang="scss">
    .home-page {
    padding: 20px;
    h1 {
    color: #42b983;
    }
    }
    </style>

    src/views/About.vue(关于页,演示 TS 版路由跳转)

    <template>
    <div class="about-page">
    <h1>关于页面</h1>
    <button @click="goHome">返回首页</button>
    </div>
    </template>

    <script setup lang="ts">
    defineOptions({ name: 'About' })
    import { useRouter } from 'vue-router'
    const router = useRouter()

    // TS版:给方法添加返回值类型(void表示无返回值,可选但推荐)
    const goHome = (): void => {
    router.push('/')
    }
    </script>

    <style scoped lang="scss">
    .about-page {
    padding: 20px;
    button {
    margin-top: 10px;
    padding: 6px 12px;
    cursor: pointer;
    }
    }
    </style>

    src/views/NotFound.vue(404 页)

    <template>
    <div class="not-found">
    <h1>404 – 页面不存在</h1>
    <router-link to="/">返回首页</router-link>
    </div>
    </template>

    <script setup lang="ts">
    defineOptions({ name: 'NotFound' })
    </script>

    <style scoped lang="scss">
    .not-found {
    padding: 20px;
    h1 {
    color: #f56c6c;
    }
    a {
    color: #42b983;
    text-decoration: none;
    }
    }
    </style>

    2.3 安装 pinia

    2.3.1 下载

    cnpm install pinia

    2.3.2 配置 Pinia(TS 版,强类型状态管理,无需手动写类型声明)

    在src下新建store文件夹,创建index.ts(创建并导出 Pinia 实例):

    // src/store/index.ts
    import { createPinia } from 'pinia'

    // 创建Pinia实例,TS自动识别类型
    const pinia = createPinia()

    export default pinia

    创建 TS 版 Pinia 模块(示例计数器):

    在store下新建modules文件夹,创建counter.ts(按业务拆分模块,TS 自动推导状态、方法类型):

    // src/store/modules/counter.ts
    import { defineStore } from 'pinia'

    // 定义并导出store,命名规则:useXxxStore(Pinia规范)
    export const useCounterStore = defineStore('counter', {
    // 状态:TS自动推导state的类型,无需手动接口约束
    state: () => ({
    count: 0, // 自动推导为number类型
    title: 'Pinia TS 计数器' // 自动推导为string类型
    }),
    // 计算属性:getters的返回值类型会自动推导,也可手动指定
    getters: {
    // 手动指定返回值类型(可选,推荐复杂逻辑时添加)
    doubleCount: (state): number => state.count * 2,
    // 基于其他getters推导类型
    doubleCountAddOne(): number {
    return this.doubleCount + 1
    }
    },
    // 方法:actions支持同步/异步,参数和返回值类型可手动指定
    actions: {
    // 同步方法:手动指定参数类型(可选)
    increment(num: number = 1): void {
    this.count += num
    },
    // 同步方法
    decrement(num: number = 1): void {
    this.count -= num
    },
    // 异步方法(示例:模拟接口请求),返回Promise类型
    async asyncIncrement(): Promise<number> {
    return new Promise((resolve) => {
    setTimeout(() => {
    this.count++
    resolve(this.count)
    }, 1000)
    })
    }
    }
    })

    // 非必要:如果需要在外部解构store且保持响应式,可导出辅助函数(Pinia推荐)
    export type CounterStore = ReturnType<typeof useCounterStore>

    2.4 安装 Sass

    Vite 无缝支持 Scss,无需额外配置

    cnpm add sass -D

    2.5 Ts语法校验

    2.5.1 无需额外装 TS 依赖:官方模板已内置typescript、@vitejs/plugin-vue、vue-tsc(TS 语法检查)

    cnpm add eslint eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser -D

    2.5.2 配置 ESLint(适配 Vue3+TS,统一代码规范)

    npx eslint –init

    解释:npx 不是包管理器,而是Node 自带的包执行工具—— 它的作用是临时下载并执行eslint包的–init命令,执行完后会删掉临时文件,不用手动全局装 eslint;而cnpm只是npm 的国内镜像包管理器,只有安装/卸载依赖的功能,没有临时执行包命令的能力,直接敲cnpm eslint –init会报错(cnpm 识别不了eslint这个命令)。

    执行之后安装流程:

    ESLint 默认不识别 Vite 的@路径别名(指向src),需要修改 ESLint 配置文件.eslintrc.cjs,添加settings和parserOptions配置,避免import @/xxx报红:

    // .eslintrc.cjs
    module.exports = {
    env: {
    browser: true,
    es2021: true
    },
    extends: [
    'plugin:vue/vue3-essential',
    'airbnb-base',
    'plugin:@typescript-eslint/recommended' // TS必备规则
    ],
    parser: 'vue-eslint-parser', // 解析Vue文件
    parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    parser: '@typescript-eslint/parser', // 嵌套解析TS文件
    project: './tsconfig.json', // 关联TS配置文件,让ESLint识别TS配置
    tsconfigRootDir: __dirname // TS配置根目录
    },
    plugins: [
    'vue',
    '@typescript-eslint'
    ],
    rules: {
    // 自定义规则:关闭不必要的严格规则(新手友好)
    'import/no-unresolved': 'off',
    'import/extensions': 'off',
    'no-unused-vars': 'warn',
    '@typescript-eslint/no-unused-vars': 'warn',
    'vue/multi-word-component-names': 'warn' // 关闭组件名必须多单词的严格错误
    },
    // 关键:让ESLint识别@路径别名
    settings: {
    'import/resolver': {
    alias: {
    map: [['@', './src']],
    extensions: ['.js', '.ts', '.vue']
    }
    }
    }
    };

    2.6 全局注册插件(路由 + Pinia)+ 优化入口文件

    // src/main.ts
    import { createApp } from 'vue'
    import App from './App.vue'
    // 导入路由实例(TS版)
    import router from './router'
    // 导入Pinia实例(TS版)
    import pinia from './store'
    // 全局样式(后续可写全局样式、引入重置样式)
    import './styles/global.scss'

    // 创建Vue应用实例,TS自动推导App类型
    const app = createApp(App)

    // 注册插件(顺序无要求,TS无类型报错)
    app.use(pinia)
    app.use(router)

    // 挂载到DOM节点(index.html中的<div id="app"></div>)
    app.mount('#app')

    2.7 添加全局样式(Scss 版)

    // src/styles/global.scss
    // 全局样式重置(简单版,可替换为normalize.css)
    * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    }

    body {
    font-size: 14px;
    line-height: 1.5;
    font-family: Avenir, Helvetica, Arial, sans-serif;
    }

    // 全局Scss变量(后续组件可通过@import引入)
    $vue-primary: #42b983; // Vue官方主色
    $vue-danger: #f56c6c; // 错误色
    $vue-gray: #eee; // 灰色
    $font-size-base: 14px; // 基础字体大小

    2.8 优化根组件 App.vue(Vue3+TS 版,演示路由 + Pinia 使用)

    <!– src/App.vue –>
    <template>
    <div id="app">
    <!– 全局导航栏:路由链接 –>
    <nav class="nav-bar">
    <router-link to="/">首页</router-link>
    <router-link to="/about">关于</router-link>
    </nav>
    <!– 路由出口:所有路由组件渲染到这里 –>
    <router-view />
    <!– Pinia TS版使用示例:计数器 –>
    <div class="counter-demo">
    <h3>{{ counterStore.title }}</h3>
    <p>当前计数:{{ counterStore.count }}</p>
    <p>双倍计数:{{ counterStore.doubleCount }}</p>
    <p>双倍计数+1:{{ counterStore.doubleCountAddOne }}</p>
    <button @click="handleIncrement">+1</button>
    <button @click="handleDecrement">-1</button>
    <button @click="handleAsyncIncrement">异步+1(1s后)</button>
    </div>
    </div>
    </template>

    <script setup lang="ts">
    // TS版:defineOptions给组件命名(Vue3.3+支持,TS无报错)
    defineOptions({ name: 'App' })

    // 导入Pinia的计数器store(TS自动推导类型)
    import { useCounterStore } from '@/store/modules/counter'
    // 实例化store(Pinia自动注入,TS无类型报错)
    const counterStore = useCounterStore()

    // TS版:给方法添加返回值类型(void表示无返回值,推荐)
    // 同步+1
    const handleIncrement = (): void => {
    counterStore.increment(1)
    }
    // 同步-1
    const handleDecrement = (): void => {
    counterStore.decrement(1)
    }
    // 异步+1(处理Promise)
    const handleAsyncIncrement = async (): Promise<void> => {
    const newCount = await counterStore.asyncIncrement()
    console.log('异步+1后计数:', newCount) // TS自动推导newCount为number类型
    }
    </script>

    <style scoped lang="scss">
    #app {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    }

    .nav-bar {
    margin-bottom: 30px;
    a {
    margin-right: 20px;
    color: $vue-primary;
    text-decoration: none;
    &.router-link-active {
    font-weight: bold;
    border-bottom: 2px solid $vue-primary;
    }
    }
    }

    .counter-demo {
    margin-top: 30px;
    padding-top: 20px;
    border-top: 1px solid $vue-gray;
    button {
    margin: 0 5px;
    padding: 6px 12px;
    cursor: pointer;
    &:hover {
    background: $vue-primary;
    color: #fff;
    border: none;
    border-radius: 4px;
    }
    }
    }
    </style>

    2.9 配置@别名

    Vite 配置(核心,让 Vite 编译识别 @)

    // vite.config.ts
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    // 导入node的path模块,用于解析路径(TS需装@types/node,下文会说)
    import { resolve } from 'path'

    // https://vitejs.dev/config/
    export default defineConfig({
    plugins: [vue()],
    resolve: {
    // 配置路径别名
    alias: {
    // @ → 指向src目录(__dirname是当前文件所在目录,即项目根目录)
    '@': resolve(__dirname, 'src')
    }
    }
    })

    2.10 测试 TS 版完整框架

    npm run dev

    2.11 生产环境打包 + 预览

    2.11.1 执行打包

    npm run build

    !!!如果报错了

    替换tsconfig.json

    //tsconfig.json
    {
    "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,

    /* 👇 新增这2项:配置@路径别名,让TS识别 */
    "baseUrl": ".", // 基础解析路径:项目根目录(必须加,否则paths失效)
    "paths": {
    "@/*": ["src/*"] // 匹配规则:@/xxx 等价于 src/xxx
    },

    /* 以下是原有配置,无需修改 */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    },
    "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
    "references": [{ "path": "./tsconfig.node.json" }]
    }

    替换tsconfig.node.json

    //tsconfig.node.json
    {
    "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
    "target": "ES2023",
    "lib": ["ES2023"],
    "module": "ESNext",
    "types": ["node", "vite/client"],
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "noEmit": false,
    "composite": true,

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "erasableSyntaxOnly": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true
    },
    "include": ["vite.config.js", "vite.config.d.ts"]
    }

    2.11.2 本地预览生产环境构建的应用

    npm run preview

    具体来说:

    1. 它会启动一个本地静态文件服务器,提供对 dist 目录(生产构建输出目录)的访问
    2. 这允许开发者在部署前检查生产构建的应用是否正常工作
    3. 它与开发服务器( npm run dev )不同,因为它使用的是实际的生产构建文件,而不是开发模式下的热重载版本

    三、安装组件库

    3.1 Element Plus 组件库

    安装组件库

    npm install element-plus –save

    步骤 1:在入口文件src/main.ts中全局注册

    // src/main.ts
    import { createApp } from 'vue'
    import App from './App.vue'
    // 导入路由实例(TS版)
    import router from './router'
    // 导入Pinia实例(TS版)
    import pinia from './store'
    // 全局样式(后续可写全局样式、引入重置样式)
    import './styles/global.scss'

    // 1. 导入Element Plus核心库
    import ElementPlus from 'element-plus'
    // 2. 导入Element Plus全局样式(必须导入,否则无样式)
    import 'element-plus/dist/index.css'
    // 若需要使用Element Plus的内置图标,额外导入图标库(可选,下文会讲)
    import * as ElementPlusIconsVue from '@element-plus/icons-vue'

    // 创建Vue应用实例,TS自动推导App类型
    const app = createApp(App)

    // 注册插件(顺序无要求,TS无类型报错)
    app.use(pinia)
    app.use(router)

    // 3. 全局注册Element Plus
    app.use(ElementPlus)
    // // 可选:全局注册Element Plus所有图标(注册后直接用<el-icon><User /></el-icon>)
    // for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    // app.component(key, component)
    // }

    // 挂载到DOM节点(index.html中的<div id="app"></div>)
    app.mount('#app')

    步骤 2:直接使用 Element Plus 组件(无需额外导入)

    <!– src/App.vue –>
    <template>
    <div id="app">
    <!– 全局导航栏:路由链接 –>
    <nav class="nav-bar">
    <router-link to="/">首页</router-link>
    <router-link to="/about">关于</router-link>
    </nav>
    <!– 路由出口:所有路由组件渲染到这里 –>
    <router-view />
    <!– Pinia TS版使用示例:计数器 –>
    <div class="counter-demo">
    <h3>{{ counterStore.title }}</h3>
    <p>当前计数:{{ counterStore.count }}</p>
    <p>双倍计数:{{ counterStore.doubleCount }}</p>
    <p>双倍计数+1:{{ counterStore.doubleCountAddOne }}</p>
    <button @click="handleIncrement">+1</button>
    <button @click="handleDecrement">-1</button>
    <button @click="handleAsyncIncrement">异步+1(1s后)</button>
    </div>

    <div class="test-elementPlus">
    <!– 直接使用Element Plus按钮组件 –>
    <el-button type="primary">点击计数:</el-button>
    <!– 直接使用Element Plus卡片组件 –>
    <el-card shadow="hover" style="width: 300px; margin-top: 20px">
    <el-tag type="success">Element Plus全局引入成功</el-tag>
    </el-card>
    </div>
    </div>
    </template>

    <script setup lang="ts">
    // TS版:defineOptions给组件命名(Vue3.3+支持,TS无报错)
    defineOptions({ name: "App" });

    // 导入Pinia的计数器store(TS自动推导类型)
    import { useCounterStore } from "@/store/modules/counter";
    // 实例化store(Pinia自动注入,TS无类型报错)
    const counterStore = useCounterStore();

    // TS版:给方法添加返回值类型(void表示无返回值,推荐)
    // 同步+1
    const handleIncrement = (): void => {
    counterStore.increment(1);
    };
    // 同步-1
    const handleDecrement = (): void => {
    counterStore.decrement(1);
    };
    // 异步+1(处理Promise)
    const handleAsyncIncrement = async (): Promise<void> => {
    const newCount = await counterStore.asyncIncrement();
    console.log("异步+1后计数:", newCount); // TS自动推导newCount为number类型
    };
    </script>

    <style scoped lang="scss">
    // 使用Scss嵌套+全局变量(需@import引入)
    @import "@/styles/global.scss";

    #app {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    }

    .nav-bar {
    margin-bottom: 30px;
    a {
    margin-right: 20px;
    color: $vue-primary;
    text-decoration: none;
    &.router-link-active {
    font-weight: bold;
    border-bottom: 2px solid $vue-primary;
    }
    }
    }

    .counter-demo {
    margin-top: 30px;
    padding-top: 20px;
    border-top: 1px solid $vue-gray;
    button {
    margin: 0 5px;
    padding: 6px 12px;
    cursor: pointer;
    &:hover {
    background: $vue-primary;
    color: #fff;
    border: none;
    border-radius: 4px;
    }
    }
    }
    </style>

    步骤 3:运行验证

    npm run dev

    3.2 Vant 组件库

    步骤 1:安装 Vant 4

    # npm
    npm install vant -S
    # cnpm
    cnpm install vant -S
    # pnpm
    pnpm install vant -S

    TS 类型声明已内置,无需额外安装。

    步骤 2:安装 Vite 按需引入插件(Vant 官方推荐)

    # npm
    npm install unplugin-vue-components unplugin-auto-import -D
    # cnpm
    cnpm install unplugin-vue-components unplugin-auto-import -D
    # pnpm
    pnpm install unplugin-vue-components unplugin-auto-import -D

    步骤 3:Vite 配置(自动按需引入 + REM 适配,核心)

    // vite.config.ts
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import { resolve } from 'path'
    import AutoImport from 'unplugin-auto-import/vite'
    import Components from 'unplugin-vue-components/vite'
    // 👇 导入Element Plus和Vant的解析器
    import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
    import { VantResolver } from 'unplugin-vue-components/resolvers'

    export default defineConfig({
    plugins: [
    vue(),
    AutoImport({
    // 自动导入Element Plus(PC)+ Vant(移动端)的API
    resolvers: [ElementPlusResolver(), VantResolver()],
    }),
    Components({
    // 自动导入Element Plus(PC)+ Vant(移动端)的组件
    resolvers: [ElementPlusResolver(), VantResolver()],
    }),
    ],
    resolve: { alias: { '@': resolve(__dirname, 'src') } },
    // 👇 手机调试配置:允许IP访问
    server: {
    host: '0.0.0.0',
    port: 5173
    }
    })

    步骤 4:配置 Vant 自动 REM 适配(一键适配所有手机屏幕)

    步骤 4.1:安装适配插件

    # npm
    npm install postcss-pxtorem amfe-flexible -S
    # cnpm
    cnpm install postcss-pxtorem amfe-flexible -S
    # pnpm
    pnpm install postcss-pxtorem amfe-flexible -S

    步骤 4.2:入口文件引入适配脚本

    // src/main.ts
    import { createApp } from 'vue'
    import App from './App.vue'
    // 导入路由实例(TS版)
    import router from './router'
    // 导入Pinia实例(TS版)
    import pinia from './store'
    // 全局样式(后续可写全局样式、引入重置样式)
    import './styles/global.scss'
    // 👇 新增:Vant移动端REM适配核心脚本
    import 'amfe-flexible'

    // 1. 导入Element Plus核心库
    import ElementPlus from 'element-plus'
    // 2. 导入Element Plus全局样式(必须导入,否则无样式)
    import 'element-plus/dist/index.css'
    // 若需要使用Element Plus的内置图标,额外导入图标库(可选,下文会讲)
    import * as ElementPlusIconsVue from '@element-plus/icons-vue'

    // 创建Vue应用实例,TS自动推导App类型
    const app = createApp(App)

    // 注册插件(顺序无要求,TS无类型报错)
    app.use(pinia)
    app.use(router)

    // 3. 全局注册Element Plus
    app.use(ElementPlus)
    // // 可选:全局注册Element Plus所有图标(注册后直接用<el-icon><User /></el-icon>)
    // for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    // app.component(key, component)
    // }

    // 挂载到DOM节点(index.html中的<div id="app"></div>)
    app.mount('#app')

    步骤 4.3:配置 postcss.config.cjs(项目根目录新建)

    在项目根目录新建postcss.config.cjs文件,写入 REM 适配配置:

    // postcss.config.cjs
    module.exports = {
    plugins: {
    'postcss-pxtorem': {
    rootValue: 37.5, // Vant官方推荐(设计稿以375px为准)
    propList: ['*'], // 所有CSS属性都转换为REM
    selectorBlackList: ['el-'] // 排除Element Plus的组件(避免PC端样式被转换)
    }
    }
    }

    配置后,Vant 的组件会自动根据手机屏幕宽度缩放,Element Plus 的 PC 组件不受影响,PC 和移动端样式完美隔离。

    步骤 5:直接使用 Vant 组件(和 Element Plus 同时使用,无冲突)

    <!– 任意组件中 –>
    <template>
    <div>
    <!– PC端:屏幕宽度≥768px,显示Element Plus –>
    <el-button v-if="isPc" type="primary">PC端按钮</el-button>

    <!– 移动端:屏幕宽度<768px,显示Vant/Element Plus Mobile –>
    <van-button v-else type="primary">移动端按钮</van-button>
    </div>
    </template>

    <script setup lang="ts">
    import { ref, onMounted, onUnmounted } from 'vue'

    // 判断是否为PC端
    const isPc = ref(window.innerWidth >= 768)

    // 监听窗口大小变化,动态更新
    const handleResize = (): void => {
    isPc.value = window.innerWidth >= 768
    }

    // 初始化执行
    onMounted(() => {
    window.addEventListener('resize', handleResize)
    })
    // 组件销毁时移除监听
    onUnmounted(() => {
    window.removeEventListener('resize', handleResize)
    })
    </script>

    赞(0)
    未经允许不得转载:171主机测评 » 项目安装:vue3 + TypeScript
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址