记录:Vue3+Pinia+Vite+TypeScript从0到1搭建

206 阅读4分钟

一、环境

node版本:

image.png

二、创建基本的文件目录结构

├── mock                       # 项目mock 模拟数据
├── src                        # 源代码
│   ├── api                    # 所有请求
│   ├── http                   # 封装请求
│   ├── assets                 # 主题 字体等静态资源
│   ├── components             # 全局公用组件
│   ├── layout                 # 全局 layout
│   ├── router                 # 路由
│   ├── store                  # 全局 store管理
│   ├── styles                 # 全局样式
│   ├── utils                  # 全局公用方法
│   ├── views                  # views 所有页面
│   ├── App.vue                # 入口页面
│   ├── main.ts                # 入口文件 加载组件 初始化等
├── .env.xxx                   # 环境变量配置
├── index.html                 # html模板
├── tsconfig.json              # ts配置
├── vite.config.ts             # vite 配置
└── package.json               # package.json

三、步骤

3.1、html文件

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Document</title>
</head>
<body>
   <div id="app"></div>
</body>
<script src="./src/main.ts" type="module"></script>
</html>

3.2、App.vue文件

<template>
    <div>
        <router-view></router-view>
    </div>
</template>
<script setup lang="ts">

</script>
<style lang="less" scoped>
    
</style>

3.3、tsconfig.json文件

{
    "compilerOptions": {
        // 编译出JS的目标ES版本
        "target": "esnext",
        // 使用的ES版本
        "module": "esnext",
        "allowJs": true,
        // 用于选择模块解析策略,有'node'和'classic'两种类型
        "moduleResolution": "node",
        // 开启严格模式
        "strict": true,
        // 强制代码中使用的模块文件名必须和文件系统中的文件名保持大小写一致
        "forceConsistentCasingInFileNames": true,
        // 允许使用 xxx 代替 * as xxx 导入
        "allowSyntheticDefaultImports": true,
        // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
        "jsx": "preserve",
        // 用来指定编译时是否生成.map文件
        "sourceMap": true,
        // 通过为导入内容创建命名空间,实现CommonJS和ES模块之间的互操作性
        "esModuleInterop": true,
        // 是否可以导入 json module
        "resolveJsonModule": true,
        // 是否检测定义了但是没使用的变量
        "noUnusedLocals": true,
        // 是否检查是否有在函数体中没有使用的参数
        "noUnusedParameters": true,
        // 是否启用实验性的装饰器特性
        "experimentalDecorators": true,
        // ts中可以使用的库 这里配置为 dom 与 es模块
        "lib": [
            "dom",
            "esnext"
        ],
        // 不允许变量或函数参数具有隐式any类型
        "noImplicitAny": false,
        // 启用阻止对下载库的类型检查
        "skipLibCheck": true,
        // types用来指定需要包含的模块
        "types": [
            "vite/client",
            "element-plus/global"
        ],
        // 编译的时候删除注释
        "removeComments": true,
        // 使用绝对路径时使用baseUrl去解析导入路径
        "baseUrl": ".",
        // 为导入路径配置别名
        "paths": {
            "@/*": [
                "src/*"
            ],
            "#/*": [
                "types/*"
            ]
        },
        // 配置虚拟目录
        "rootDirs": []
    },
    // 指定需要编译文件
    "include": [
        "src/**/*.ts",
        "src/**/*.d.ts",
        "src/**/*.tsx",
        "src/**/*.vue",
        "types/**/*.d.ts",
        "types/**/*.ts",
        "build/**/*.ts",
        "build/**/*.d.ts",
        "mock/**/*.ts",
        "vite.config.ts"
    ],
    // 指定不需要编译的文件
    "exclude": [
        "node_modules",
        "dist",
        "**/*.js"
    ]
}

3.4、vite.config.ts文件

import { defineConfig, loadEnv } from "vite";
import type { UserConfig, ConfigEnv } from "vite";
import { fileURLToPath } from "url";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";



export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
    // 获取当前工作目录
    const root = process.cwd();
    // 获取环境变量
    const env = loadEnv(mode, root);
    console.log(env);
    return {
        // 项目根目录
        root,
        // 项目部署的基础路径
        base: "./",
        publicDir: fileURLToPath(new URL("./public", import.meta.url)), // 无需处理的静态资源位置
        assetsInclude: fileURLToPath(new URL("./src/assets", import.meta.url)), // 需要处理的静态资源位置
        plugins: [
            // Vue模板文件编译插件
            vue(),
            // jsx文件编译插件
            vueJsx(),
        ],
        // 运行后本地预览的服务器
        server: {
            // 是否开启https
            https: false,
            // 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
            host: true,
            // 开发环境预览服务器端口
            port: 8080,
            // 启动后是否自动打开浏览器
            open: false,
            // 是否开启CORS跨域
            cors: true,
            // 代理服务器
            // 帮助我们开发时解决跨域问题
            proxy: {
                // 这里的意思是 以/agent 开头发送的请求都会被转发到 http://www.birne9.com
                '/agent': {
                    target: "http://www.birne9.com",
                    // 改变 Host Header
                    changeOrigin: true,
                    // 发起请求时将 '/agent' 替换为 ''
                    //rewrite: (path) => path.replace('/agent', "/"),
                },
              
            },
        },
        // 打包配置
        build: {
            // 关闭 sorcemap 报错不会映射到源码
            sourcemap: false,
            // 打包大小超出 400kb 提示警告
            chunkSizeWarningLimit: 10000,
            rollupOptions: {
                // 打包入口文件 根目录下的 index.html
                // 也就是项目从哪个文件开始打包
                input: {
                    index: fileURLToPath(
                        new URL("./index.html", import.meta.url),
                    ),
                },
                // 静态资源分类打包
                output: {
                    format: "esm",
                    chunkFileNames: "static/js/[name]-[hash].js",
                    entryFileNames: "static/js/[name]-[hash].js",
                    assetFileNames: "static/[ext]/[name]-[hash].[ext]",
                },
            },
        },
        // 配置别名
        resolve: {
            alias: {
                "@": fileURLToPath(new URL("./src", import.meta.url)),
                "#": fileURLToPath(new URL("./types", import.meta.url)),
            },
        },
    };
});

3.5、env.d.ts文件

/// <reference types="vite/client" />

interface ImportMetaEnv {
   readonly VITE_APP_API_BASEURL: string
   // 更多环境变量...
 }
 
 interface ImportMeta {
   readonly env: ImportMetaEnv
 }



3.6、配置router


import { createRouter, RouteRecordRaw, createWebHashHistory } from "vue-router";
import NProgress from 'nprogress'
//配置路由
const routes:Array<RouteRecordRaw>=[{
   path:"/",
   name:'home',
   component:()=>import('../views/home/index.vue')
},
{
   path:"/about",
   name:'about',
   component:()=>import('../views/about/index.vue')
}]

//创建路由
const router=createRouter({
   routes,
   history:createWebHashHistory()
})

router.beforeEach((to,from,next)=>{
NProgress.start();
next();
})

router.afterEach((_to) => {
   NProgress.done();
})
export default router;

3.7、配置仓库

import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export default pinia;

3.8、封装请求


import axios from "axios";
const service = axios.create({
   baseURL:'/',
   timeout:10000
});

//axios实例的拦截器
service.interceptors.request.use(config => {
   return config;
},err => {
   Promise.reject(err);
})
//axios的响应拦截
service.interceptors.response.use(res => {
   return res.data;
},err => {
   Promise.reject(err);    
})

export default service

3.9、引入重置样式reset.css

/*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */
html,
body,
p,
ol,
ul,
li,
dl,
dt,
dd,
blockquote,
figure,
fieldset,
legend,
textarea,
pre,
iframe,
hr,
h1,
h2,
h3,
h4,
h5,
h6 {
 margin: 0;
 padding: 0;
}

h1,
h2,
h3,
h4,
h5,
h6 {
 font-size: 100%;
 font-weight: normal;
}

ul {
 list-style: none;
}

button,
input,
select {
 margin: 0;
}

html {
 box-sizing: border-box;
}

*, *::before, *::after {
 box-sizing: inherit;
}

img,
video {
 height: auto;
 max-width: 100%;
}

iframe {
 border: 0;
}

table {
 border-collapse: collapse;
 border-spacing: 0;
}

td,
th {
 padding: 0;
}

3.10、一起引入到main.ts


import { createApp } from "vue";
import App from "./App.vue";
import  './styles/index.css'
import router from './router/index'
import pinia from "./store";
const app=createApp(App)

app.use(router)
app.use(pinia)
app.mount("#app")

四、package.json

4.1、包的下载

  "dependencies": {
   "axios": "^1.6.7",
   "element-plus": "^2.5.3",
   "nprogress": "^0.2.0",
   "pinia": "^2.1.7",
   "pinia-plugin-persistedstate": "^3.2.1",
   "vue": "^3.4.15",
   "vue-router": "^4.2.5"
 },
 "devDependencies": {
   "@types/node": "^20.11.10",
   "@types/nprogress": "^0.2.3",
   "@vitejs/plugin-vue": "^5.0.3",
   "@vitejs/plugin-vue-jsx": "^3.1.0",
   "less": "^4.2.0",
   "typescript": "^5.3.3",
   "vite": "^5.0.12",
   "vue-tsc": "^1.8.27"
 }

4.2、脚本的配置

    "dev": "vite --mode dev",
   "dev:test": "vite --mode test",
   "dev:stg": "vite --mode stg",
   "dev:prod": "vite --mode prod",
   "build": "vite build --mode dev",
   "build:test": "vite build --mode test",
   "build:stg": "vite build --mode stg",
   "build:prod": "vite build --mode prod"