Vue3+TS+Vite脚手架搭建全过程(一)

一. 文章结构

  • 技术栈
  • 搭建准备
  • 架构搭建
  • 代码规范
  • 提交规范

二. 技术栈

  • 编程语言:TypeScript 4.X + JavaScript
  • 构建工具:vite 3.X
  • 前端框架:Vue 3.X
  • 路由工具:Vue-router 4.X
  • 状态管理:pinia 2.X
  • UI框架:Element plus
  • CSS预处理:sass
  • HTTP工具:Axios
  • 代码规范:Prettier + Eslint + EditorConfig
  • 提交规范:cz-git

三. 搭建前准备

前置条件

  1. Vscode: 前端人必备写码神器
  2. Chrome:对开发者非常友好的浏览器
  3. Nodejs&npm:配置本地开发环境(建议安装node管理工具nvm方便切换node版本)
  4. Vue.js devtools:浏览器调试插件
  5. Vue Language Features (Volar):Vscode 开发 vue3 必备插件,提供语法高亮提示,非常好用
  6. Vue 3 Snippets:vue3 快捷输入

「Nvm常用命令」

  • 查看已安装的版本:nvm ls
  • 安装指定的版本:nvm install version
  • 删除指定的版本:nvm uninstall version
  • 使用选定的版本:nvm use version

why vite

Vite 是一个现代化的前端开发构建工具,它在某些方面与传统的构建工具(如Webpack)相比具有一些优点和缺点。「优点:」

  1. 「快速的开发启动」:Vite 以极快的速度启动开发服务器,因为它使用了 ES 模块的原生浏览器支持,无需预先构建或打包。这意味着你可以更快地开始编写和测试代码。
  2. 「热模块替换(HMR)」:Vite 提供了强大的热模块替换功能,可以在不刷新整个页面的情况下实时更新应用程序的部分。这可以显著提高开发效率。
  3. 「按需加载」:Vite 支持按需加载,只会在需要的时候加载模块,而不是将整个应用打包成一个巨大的文件。这有助于减小最终构建的文件大小,提高加载性能。
  4. 「简单的配置」:Vite 的配置相对简单,因为它采用了约定大于配置的理念。许多默认配置都可以满足大多数项目的需求,不需要复杂的配置。
  5. 「Vue.js 生态系统的首选工具」:Vite 最初是为 Vue.js 开发的,因此在 Vue.js 生态系统中得到广泛支持和推广。Vue 3 与 Vite 配合使用效果最佳。

「缺点:」

  1. 「生态系统相对不成熟」:虽然 Vite 在 Vue.js 生态系统中表现出色,但在其他框架和库中的支持相对不足。如果你的项目不使用 Vue.js,可能需要面临一些挑战。
  2. 「相对较新」:Vite 是相对较新的工具,可能会有一些不稳定性或不兼容性问题,尤其是在生产环境中使用时。这需要更多时间来成熟和稳定。
  3. 「较少的插件和扩展」:与一些成熟的构建工具相比,Vite 的插件生态系统相对较小。这意味着你可能需要编写自定义插件来满足特定需求。
  4. 「可能不适用于复杂项目」:对于非常复杂的前端项目,Vite 的简化可能会导致一些不足。一些大型项目可能更喜欢使用更强大的构建工具,如Webpack。

why Vue3

Vue3由于完全由TS进行重写,在应用中对类型判断的定义和使用有很强的表现。Vue2 的双向数据绑定是利用 ES5 的一个 API Object.definePropert()对数据进行劫持结合 发布订阅模式的方式来实现的。Vue3 中使用了 es6 的 ProxyAPI 对数据代理。Vue3支持碎片(Fragments)。Vue3引入传送门(Teleport)。Vue2 与 Vue3 最大的区别: Vue2 使用Options API而 Vue3 使用的Composition API

why pinia

「Pinia 优点:」

  1. 「类型安全」:Pinia 是基于 TypeScript 构建的,提供了强大的类型支持。
  2. 「Composition API 集成」:Pinia 是为 Composition API 设计的,与 Vue 3 更紧密集成,使代码更具可读性和可维护性。
  3. 「模块化和可组合性」:Pinia 使用了模块化的状态管理,可以轻松地将状态拆分为模块,并在多个组件之间共享和组合模块。
  4. 「更好的性能」:Pinia 在性能方面进行了优化,使用了 Vue 3 的响应式系统,提供了更好的性能表现,特别适用于大型应用。
  5. 「自动化的热重载」:Pinia 提供了自动的热重载支持,使开发过程更加顺畅。

「Pinia 缺点:」

  1. 「相对较新」:生态系统、文档和社区支持方面不如 Vuex 成熟。
  2. 「学习曲线」:不熟悉 TypeScript 的开发者学习困难。

「Vuex 优点:」

  1. 「成熟的生态系统」:Vuex 是 Vue.js 生态系统的一部分,因此具有大量的文档、教程和社区支持,适用于各种规模的项目。
  2. 「简单易用」:Vuex 的 API 相对简单,容易上手,特别是对于初学者来说。
  3. 「大型社区支持」:由于其流行度,Vuex 有一个庞大的社区,可以找到许多第三方插件和工具,用于扩展其功能。

「Vuex 缺点:」

  1. 「冗余的模板代码」:在 Vuex 中,一些常见操作需要编写大量的模板代码,可能导致代码冗余。
  2. 「缺乏类型支持」:虽然可以使用 TypeScript 与 Vuex 结合使用,但 Vuex 本身没有内置的类型支持,需要额外的工作来实现类型安全。
  3. 「较大的包大小」:Vuex 的核心库相对较大,这可能会增加应用程序的总体文件大小。

wyh CSS预处理

  1. 「变量和计算」:CSS 预处理器允许你定义变量,可以在整个样式表中重复使用。这使得更容易维护一致性的设计,因为你只需更新变量的值,而不必逐个修改每个使用该值的地方。此外,你还可以进行数学运算,使样式更具动态性。
  2. 「嵌套规则」:CSS 预处理器允许你使用嵌套规则,使样式更具可读性。这特别有用,因为你可以将相关的样式组织在一起,而不必编写冗长的选择器。
  3. 「混合(Mixins)」:混合是可重用的代码块,可以在不同的规则中重复使用。这有助于减少代码重复,提高样式表的可维护性。
  4. 「继承(Inheritance)」:继承允许一个选择器继承另一个选择器的样式。这使得创建和维护样式更加高效,因为你可以利用现有样式。
  5. 「模块化开发」:CSS 预处理器有助于将样式表分解为多个模块或组件,每个模块都可以独立开发和维护。这有助于提高团队协作和项目的可扩展性。
  6. 「自动前缀」:一些 CSS 预处理器和构建工具可以自动为你添加浏览器前缀,以确保样式在不同浏览器中正确显示,从而减少了跨浏览器兼容性问题。
  7. 「代码压缩和优化」:许多 CSS 预处理器具有优化选项,可以在构建过程中自动压缩和优化生成的 CSS,从而减小文件大小,提高加载性能。
  8. 「插件和生态系统」:CSS 预处理器通常有丰富的插件和工具生态系统,可以进一步扩展其功能,满足特定项目需求。

四. 架构搭建

Vite初始化项目

  • 使用npm
npm create vite@latest
  • 使用yarn
yarn create vite
  • 使用pnpm
pnpm create vite
  1. 项目创建

Vue3+TS+Vite脚手架搭建全过程(一)还可以通过附加的命令行选项直接指定项目名和模板,本项目要构建 Vite + Vue3 + TypeScript 项目

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

# npm 7+, extra double-dash is needed:
npm create 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
  1. 安装依赖
cd test
npm install
  1. 运行项目
npm run dev

Vue3+TS+Vite脚手架搭建全过程(一)一套简单Vue3项目骨架搭建完毕,接下来为项目功能添砖加瓦

修改Vite配置

Vite 配置文件 vite.config.ts 位于根目录下,项目启动时会自动读取。本项目先做一些简单配置,例如:设置@指向 src目录、服务器启动端口、打包路径、代理等。

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 如果编辑器提示 path 模块找不到,则可以安装一下 @types/node -> npm i @types/node -D
import { resolve } from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'// 设置 `@` 指向 `src` 目录
    }
  },
  base'./'// 设置打包路径
  server: {
    port4000// 设置服务启动端口号
    opentrue// 设置服务启动时是否自动打开浏览器
    corstrue // 允许跨域

    // 设置代理,根据我们项目实际情况配置
    // proxy: {
    //   '/api': {
    //     target: 'http://xxx.xxx.xxx.xxx:8000',
    //     changeOrigin: true,
    //     secure: false,
    //     rewrite: (path) => path.replace('/api/', '/')
    //   }
    // }
  }
})

修改 tsconfig.json

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

    /* Bundler mode */
    "moduleResolution""bundler",
    "allowImportingTsExtensions"true,
    "resolveJsonModule"true,
    "isolatedModules"true,
    "noEmit"true,
    "jsx""preserve",

    /* Linting */
    "strict"true,
    "noUnusedLocals"true,
    "noUnusedParameters"true,
    "noFallthroughCasesInSwitch"true,
    
    "baseUrl"".",
    "paths": {
      "@/*":["src/*"]  // 未设置 "baseUrl" 时,不允许使用非相对路径。是否忘记了前导 "./",所以添加一个baseUrl
    }
  },
  "include": ["src/**/*.ts""src/**/*.d.ts""src/**/*.tsx""src/**/*.vue"],
  "references": [{ "path""./tsconfig.node.json" }]
}

规范目录

├── publish/
└── src/
├── api/ // api接口目录
├── assets/ // 静态资源目录
├── common/ // 通用类库目录
├── components/ // 公共组件目录
├── router/ // 路由配置目录
├── store/ // 状态管理目录
├── style/ // 通用 CSS 目录
├── plugins/ // 插件目录
├── utils/ // 工具函数目录
├── server/ // 请求封装目录
├── views/ // 页面组件目录
├── App.vue
├── main.ts
├── shims-vue.d.ts
├── tests/ // 单元测试目录
├── index.html
├── tsconfig.json // TypeScript 配置文件
├── vite.config.ts // Vite 配置文件
└── package.json

集成路由 router

  1. 安装支持 Vue3 的路由工具 vue-router@4
npm i vue-router@4
  1. 创建 src/router/index.ts 文件

在 src 下创建 router 目录,然后在 router 目录里新建 index.ts 文件:

 └── src/
     ├── router/
         ├── index.ts  // 路由配置文件
import {
  createRouter,
  createWebHashHistory,
  RouteRecordRaw
from 'vue-router'
import Home from '@/views/home.vue'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('@/views/about.vue'// 懒加载组件
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

根据本项目路由配置的实际情况,你需要在 src 下创建 views 目录,用来存储页面组件。我们在 views 目录下创建 home.vue 、about.vue 。

  1. 在main.ts挂载路由配置
import { createApp } from 'vue'
import App from './App.vue'

import router from './router/index'

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

状态管理 pinia

  1. 安装:
yarn add pinia
  1. src下的main.ts添加:
# 引入
import { createPinia } from "pinia"
# 创建根存储库并将其传递给应用程序
app.use(createPinia())

在 src 文件夹下新增 store 文件夹,接在在 store 中新增 main.ts

  1. 在store下创建main.ts:
import { defineStore } from 'pinia'

export const useMainStore = defineStore({
  id: 'mian',
  state: () =>({
    name: '超级管理员'
  })
})

  1. 组件中获取:
<template>
  <div>{{mainStore.name}}</div>
</
template>

<script setup lang="ts">
import { useMainStore } from "@/store/mian"

const mainStore = useMainStore()

</script>

集成UI框架 Element plus

  1. 安装:
# NPM
$ npm install element-plus --save

# Yarn
$ yarn add element-plus

# pnpm
$ pnpm install element-plus
  1. main.ts挂载:
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from "pinia"

import router from './router/index'

import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css'

const app = createApp(App)


app.use(router) 
app.use(createPinia())
app.use(ElementPlus)
app.mount('#app')
  1. 组件中使用:

<template>
<div>
{{ mainStore.name }}
<el-button>点击</el-button>
</div>
</template>

<script setup lang="ts">
import { useMainStore } from './store/main';
const mainStore = useMainStore()
</script>

<style scoped>

</style>

集成HTTP工具Axios

  1. 安装:
# NPM
$ npm install axios

# Yarn
$ yarn add axios

# pnpm
$ pnpm install axios
  1. 配置:

为了使项目的目录结构合理且规范,我们在 src 下创建 server 目录来存储我们常用的工具函数。Axios 作为 HTTP 工具,我们在 server 目录下创建 server.ts 作为 Axios 配置文件:

└── src/
├── server/
├── server.ts // Axios 配置文件
├── tools.ts // Axios 工具
├── errorCode.ts // 错误处理文件

server.ts:

import axios from 'axios'
import { ElNotification, ElMessage } from 'element-plus'
import { tansParams } from './tools.ts'
import errorCode from './errorCode.ts'

const service = axios.create({
  baseURL: '',
  timeout: 10000,
})
/**
 * @description: 请求拦截
 */

service.interceptors.request.use(
  (config) => {
    // get请求映射params参数
    if (config.method === 'get' && config.params) {
      let url = config.url + '?' + tansParams(config.params)
      url = url.slice(0-1)
      config.params = {}
      config.url = url
    }
    return config
  },
  (error) => {
    Promise.reject(error)
  },
)
/**
 * @description: 响应拦截
 */

service.interceptors.response.use(
  (res) => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || 200
    // 获取错误信息
    const msg = errorCode[code] || res.data.msg || errorCode.default
    if (code === 401) {
      return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
    } else if (code === 500) {
      ElMessage({ message: msg, type'error' })
      return Promise.reject(new Error(msg))
    } else if (code === 601) {
      ElMessage({ message: msg, type'warning' })
      return Promise.reject(new Error(msg))
    } else if (code !== 200) {
      ElNotification.error({ title: '系统错误请联系管理员' })
      return Promise.reject('error')
    }
    return Promise.resolve(res.data)
  },
  (error) => {
    let { message } = error
    if (message === 'Network Error') {
      message = '后端接口连接异常'
    } else if (message.includes('timeout')) {
      message = '系统接口请求超时'
    } else if (message.includes('Request failed with status code')) {
      message = '系统接口' + message.substr(message.length - 3) + '异常'
    }
    ElMessage({ message: message, type'error', duration: 3 * 1000 })
    return Promise.reject(error)
  },
)

export default service

tools.ts:

/**
* 参数处理
* @param {*} params 参数
*/
export function tansParams(params: any) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName]
const part = encodeURIComponent(propName) + '='
if (value !== null && value !== '' && typeof value !== 'undefined') {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
// eslint-disable-next-line max-depth
if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
const params = propName + '[' + key + ']'
const subPart = encodeURIComponent(params) + '='
result += subPart + encodeURIComponent(value[key]) + '&'
}
}
} else {
result += part + encodeURIComponent(value) + '&'
}
}
}
return result
}

errorCode.ts:

export default {
'401': '认证失败,无法访问系统资源',
'403': '当前操作没有权限',
'404': '访问资源不存在',
default: '系统未知错误,请反馈给管理员',
} as any
  1. 使用:

在src添加api文件夹并在添加login文件夹,在login文件夹里添加login.ts以及types.ts:

└── src/
    ├── api/
        ├── login/
          ├── login.ts      // 登录接口文件
          ├── types.ts      // 类型判断

login.ts:

import service from '@/server/server.ts'
import * as T from './types.ts'

export function login(data: T.ILoginParams){
    return service({
        url: '/login',
        headers: {
            isToken: false
        },
        method: 'post',
        data: data
    })
}

types.ts:

export interface ILoginParams {
    userName: string
    passWord: string | number
}

集成CSS 预编译器 Stylus/Sass

虽然 vite 原生支持 less/sass/scss/stylus,但是你必须手动安装他们的预处理器依赖

  1. 安装:
yarn add dart-sass --dev
yarn add sass --dev
  1. 使用:

配置全局 scss 样式文件,在src/style 文件夹,新建 main.scss,  设置一个用于测试的颜色变量 :

$test-colorred;

如何将这个全局样式文件全局注入到项目中呢?配置 Vite 即可:

css:{
    preprocessorOptions:{
      scss:{
        additionalData:'@import "@/style/main.scss";'
      }
    }
  },
  1. 组件中使用:

<template>
 <div class="div">
  {{ mainStore.name }}
  <el-button>点击</el-button>
 </
div>
</template>

<script setup lang="ts">
import { useMainStore } from './
store/main';
const mainStore = useMainStore()
</script>

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

(未完 – 下一节集成代码规范+提交规范)

原文始发于微信公众号(前端大大大):Vue3+TS+Vite脚手架搭建全过程(一)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/174146.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!