Skip to content

搭建Vite+Typescript+Vue3模板

目录

安装Vite模板

博主自己使用的是yarn来安装,如果没有安装的小伙伴可以看这篇文章:

工具篇:yarn

TIP

兼容性注意 Vite 需要 Node.js 版本 >= 12.0.0。

安装:

# npm 6.x
npm init vite@latest my-vue-app --template vue

# npm 7+, 需要额外的双横线:
npm init vite@latest my-vue-app -- --template vue

# yarn
yarn create vite my-vue-app --template vue

# pnpm
pnpm create vite my-vue-app -- --template vue

参考链接:Vite初始化项目

安装模板:

yarn create vite my-vue-app --template vue-ts

支持的模板类型:vanilla,vanilla-ts,vue,vue-ts,react,react-ts,preact,preact-ts,lit,lit-ts,svelte,svelte-ts

  • 该命令可以使我们生成一个基于Typescript的Vue项目
  • 目录结构如下:

安装VueRouter

安装Vue-Router4.0以支持Vue3语法。

yarn add vue-router@4.0.12

添加两个文件便于测试,src下面添加views文件夹,添加home和login文件夹,并在分别在两个文件夹里面新建index.vue文件。

vue
<template>
    <h2>login</h2>
</template>

src目录下创建 router目录,添加index.ts

ts
// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'

const routes: RouteRecordRaw[] = [
    {
        path: '/login',
        component: () => import("../views/login/index.vue")
    },
    {
        path: '/',
        component: () => import("../views/home/index.vue")
    },
]

const router = createRouter({
    history: createWebHistory(),
    routes
})
export default router

修改 src下的main.ts

ts
import { createApp } from 'vue'
import router from "./router"
import App from './App.vue'

createApp(App).use(router).mount('#app')

修改 src下的App.vue

vue
 <template>
    <router-view />
</template>

访问一下:

当前目录结构:

安装Vuex

安装Vuex以支持Vue3语法

yarn add vuex@4.0.2 或 yarn add vuex@next

next表示最新版本,我们安装Vuex4.0.2的版本以支持vue3的语法。

安装Vuex持久化插件:

yarn add vuex-persistedstate

安装完成以后要进入配置阶段,养成好的习惯,我们要将 Store模块化,模块化的好处就在于阅读起来十分清晰。我们在 src下面建立一个store目录,在store目录下面建立modules目录以及index.ts文件,然后再modules下面建立一个user.ts文件来保存一些用户信息

当前目录结构:

具体配置如下:

ts
// /src/store/modules/user.ts
import { Module } from "vuex"
import { RootState } from "../index"

export interface userState{
    counter: number
}

export const store:Module<userState,RootState> = {
    namespaced:true,
    state: ():userState => ({
        counter: 0
    }),
    mutations: {
        increment(state:userState,newVal:number):void{
            state.counter = newVal
        }
    }
}
ts
// /src/store/index.ts
import { InjectionKey } from 'vue'
import { createStore,Store,useStore as baseUseStore } from 'vuex'
// 持久化插件
import createPersistedState from "vuex-persistedstate"
import { store as user,userState } from "@/store/modules/user"

export interface RootState {
    user: userState
}

export const key: InjectionKey<Store<RootState>> = Symbol()

export const store:Store<RootState> = createStore({
    modules: {
        user
    },
    plugins: [
        createPersistedState({
            paths: ['user']
        })
    ]
})

export function useStore() {
    return baseUseStore(key)
}

修改 main.ts

ts
import { createApp } from 'vue'
import router from "./router"
import { store,key } from "./store"
import App from './App.vue'

createApp(App).use(store,key).use(router).mount('#app')

TIP

配置参考地址 Vuex官网

这样Vuex就配置好了,接下来我们要进行一些测试:

我们修改home/index.vue

vue
<template>
  <h2>home</h2>
  <div>vuex中的count:{{ count }}</div>
</template>

<script setup lang="ts">
import { computed } from "vue"
import { useStore } from "@/store/index";
const store = useStore();
const count = computed(()=>{
    return store.state.user.counter
}) 

</script>

再修改一下login/index.vue

vue
<template>
      <h2>login</h2>
      <div>
          vuex:{{count}}
      </div>
      <button @click="addCount">累加</button>
      <div>
          <router-link to="/">首页</router-link>
      </div>
</template>


<script setup lang="ts">
import { computed } from "vue"
import { useStore } from '@/store/index'
const store = useStore()
const count = computed(()=>{
    return store.state.user.counter
}) 
function addCount():void{
    store.commit('increment',count.value+1)
}
</script>

如果出现以上错误则需要修改tsconfig.js配置:

主要是加上 paths配置,顺便也加上bseUrl配置。

在home以及login页面测试一下:

可以看到很nice,🤪🤪。

安装css预处理器scss

yarn add dart-sass sass --dev

测试一下:

vue
<template>
  <div class="home">
    <h2 class="title">login</h2>
    <div>vuex:{{ count }}</div>
    <button @click="addCount">累加</button>
    <div>
      <router-link to="/">首页</router-link>
    </div>
  </div>
</template>


<script setup lang="ts">
import { computed } from "vue";
import { useStore } from "@/store/index";
const store = useStore();
const count = computed(() => {
  return store.state.user.counter;
});
function addCount(): void {
  store.commit("increment", count.value + 1);
}
</script>

<style lang="scss" scoped>
.home {
  .title {
      color: blue;
  }
}
</style>

引入全局scss样式

在src下assets里面建立style文件夹,然后添加main.scss,新建一个颜色变量用于测试。

scss
$test-color: red;

接下来修改vite.config.ts

ts
//vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  // 设置资源别名
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'), // src 路径
      'utils': path.resolve(__dirname, 'src/utils') // src 路径
    }
  },
  server: {
    proxy: {
      '/api': {
        // 当遇到 /api 路径时,将其转换成 target 的值
        target: '',
        changeOrigin: true,
        rewrite: path => path.replace(/^\/api/, '') // 将 /api 重写为空
      }
    }
  },
  css:{
    preprocessorOptions:{
      scss:{
        additionalData:'@import "@/assets/style/main.scss";'
      }
    }
  },
  base: '/',
})

主要是添加css配置

测试一下,修改login.vue

vue
<template>
  <div class="home">
    <h2 class="title">login</h2>
    <div>vuex:{{ count }}</div>
    <button @click="addCount">累加</button>
    <div>
      <router-link to="/">首页</router-link>
    </div>
  </div>
</template>


<script setup lang="ts">
import { computed } from "vue";
import { useStore } from "@/store/index";
const store = useStore();
const count = computed(() => {
  return store.state.user.counter;
});
function addCount(): void {
  store.commit("increment", count.value + 1);
}
</script>

<style lang="scss" scoped>
.home {
  .title {
      color: $test-color;
  }
}
</style>

到这里为止全局的scss文件就配置完成了。

封装request请求

接下来就是进行axios封装了,axios我相信大家应该都不陌生,我们先安装以下依赖:

# 安装 axios
yarn add axios
# 安装 nprogress 用于请求 loading
# 也可以根据项目需求自定义其它 loading
yarn add nprogress
# 类型声明,或者添加一个包含 `declare module 'nprogress'
yarn add @types/nprogress --dev

我们在 src下面建立一个service文件夹,并在里面建立一个http.ts文件封装axios请求,同时在service下面建立一个apis文件夹,apis文件夹里面建立一个login.ts和type.ts。

ts
// servicce/http.ts
import axios,{ AxiosRequestConfig,AxiosResponse} from "axios"
import NProgress from "nprogress"

// 设置请求的基本地址
axios.defaults.baseURL = '/'
// 请求超时时间
axios.defaults.timeout = 10000
// 设置头部contenttype为json格式
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'
// 请求拦截
axios.interceptors.request.use(
    (config):AxiosRequestConfig<any> => {
        const token = sessionStorage.getItem('token')
        if(token){
            // token业务处理
            // config.headers && config.headers.token && (config.headers.token = token)
        }
        return config
    },
    (error => {
        return error
    })
)

// 响应拦截
axios.interceptors.response.use(
    (res):AxiosResponse<any> =>{
        return res
    },
    (error =>{
        console.error(error);
        return error
    })
)

// 定义响应类型
interface ResType<T> {
    code: number | string
    data? : T
    msg: string
    err?: string
}

// 定义请求Http类型
interface Http{
    get<T>(url: string, params?: unknown): Promise<ResType<T>>
    post<T>(url: string, params?: unknown): Promise<ResType<T>>
    upload<T>(url: string, params: unknown): Promise<ResType<T>>
    download(url: string): void
}

const http: Http = {
    get(url, params) {
      return new Promise((resolve, reject) => {
        NProgress.start()
        axios
          .get(url, { params })
          .then((res) => {
            NProgress.done()
            resolve(res.data)
          })
          .catch((err) => {
            NProgress.done()
            reject(err.data)
          })
      })
    },
    post(url, params) {
      return new Promise((resolve, reject) => {
        NProgress.start()
        axios
          .post(url, JSON.stringify(params))
          .then((res) => {
            NProgress.done()
            resolve(res.data)
          })
          .catch((err) => {
            NProgress.done()
            reject(err.data)
          })
      })
    },
    upload(url, file) {
      return new Promise((resolve, reject) => {
        NProgress.start()
        axios
          .post(url, file, {
            headers: { 'Content-Type': 'multipart/form-data' },
          })
          .then((res) => {
            NProgress.done()
            resolve(res.data)
          })
          .catch((err) => {
            NProgress.done()
            reject(err.data)
          })
      })
    },
    download(url) {
      const iframe = document.createElement('iframe')
      iframe.style.display = 'none'
      iframe.src = url
      iframe.onload = function () {
        document.body.removeChild(iframe)
      }
      document.body.appendChild(iframe)
    },
  }
  export default http
ts
// service/api/login.ts
import http from '@/service/http'
import * as T from './type'

const loginApi: T.LoginApi = {
    login(params:T.LoginParams){
        return http.post('/login', params)
    }

}
export default loginApi
ts
// service/api/type.ts
export interface LoginParams {
    userName: string
    passWord: string | number
}
export interface LoginApi {
    login: (params: LoginParams)=> Promise<any>
}

当前目录结构:

环境变量配置

TIP

vite 提供了两种模式:具有开发服务器的开发模式(development)和生产模式(production)

在项目根目录下面添加 .env.development文件和.env.production文件。 .env.development文件

ts
NODE_ENV=development

VITE_APP_WEB_URL= 'YOUR WEB URL'

.env.production文件

ts
NODE_ENV=production

VITE_APP_WEB_URL= 'YOUR WEB URL'

vite中使用:

ts
console.log(import.meta.env.VITE_APP_WEB_URL)

vite一些其他配置

生产环境生成 .gz 文件

TIP

开启 gzip 可以极大的压缩静态资源,对页面加载的速度起到了显著的作用。

安装

yarn add --dev vite-plugin-compression
ts
import viteCompression from 'vite-plugin-compression'

然后在vite.config.ts的plugins里面添加如下代码:

ts
 viteCompression({
      verbose: true,
      disable: false,
      threshold: 10240,
      algorithm: 'gzip',
      ext: '.gz',
    }),

最终的vite.config.ts

ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import viteCompression from 'vite-plugin-compression'
// https://vitejs.dev/config/

export default defineConfig({
  plugins: [
    vue(),
    // gzip压缩 生产环境生成 .gz 文件
    viteCompression({
      verbose: true,
      disable: false,
      threshold: 10240,
      algorithm: 'gzip',
      ext: '.gz',
    }),
  ],
  // 设置资源别名
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'), // src 路径
      'utils': path.resolve(__dirname, 'src/utils') // src 路径
    }
  },
  server: {
    proxy: {
      '/api': {
        // 当遇到 /api 路径时,将其转换成 target 的值
        target: '',
        changeOrigin: true,
        rewrite: path => path.replace(/^\/api/, '') // 将 /api 重写为空
      }
    }
  },
  // build环境下面去除console.log打印信息
  build: {
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    },
  },
  css: {
    preprocessorOptions: {
      scss: {
        // 全局样式
        additionalData: '@import "@/assets/style/main.scss";'
      }
    }
  },
  // 打包根目录
  base: '/',
  publicDir: 'assets',
  sourcemap: true,
  publicPath:'./',
  outputDir:'dist',
  assetsDir:'static'
})

参考

Vite2+Vue3+TypeScript+Pinia搭建一套企业级的开发脚手架

Typescript开发常见报错

Vuex实现模块化

写在最后

博主在搭建的时候没有添加ui框架和一些eslint的配置,大家可以自行添加,这只是一个很轻量的模板。Typescript在dev阶段以及build阶段就可以帮助开发者发现许多隐藏的bug,个人觉得这真的很舒服,希望Typscript会发展的越来越好。

代码仓库奉上:vue3+ts+vite-template

编写良久,如对您有帮助,还望给个Star,如有错误的地方可在评论区指出🧐。

最后我想说一句,ts真香🤪!!!

上次更新于: