开箱即用的Vite-Vue3工程化模板

前言

由于临近毕业肝毕设和论文,停更有一段时间了,不过好在终于肝完了大部分内容,只剩下校对工作

毕设采用技术栈Vue3,Vite,TypeScript,Node,开发过程中产出了一些其它的东西,预计会出一系列的文章进行介绍

废话不多了步入正题...

体验模板

模板仓库地址


线上预览

两步到位

本地引入

# 方法一
npx degit atqq/vite-vue3-template#main my-project cd my-project
# 方法二
git clone https://github.com/ATQQ/vite-vue3-template.git cd vite-vue3-template

启动

# 安装依赖
yarn install
# 运行
yarn dev

模板介绍

已包含特性

内置了常见的工程化项目所用的内容,后文只对其中的一些特性做简单介绍

目录介绍

.
├── __tests__
├── dist # 构建结果
├── public # 公共静态资源
├── src # 源码目录
│ ├── apis
│ ├── assets
│ ├── components
│ ├── pages
│ ├── router
│ ├── store
│ ├── @types
│ ├── utils
│ ├── shims-vue.d.ts
│ ├── env.d.ts
│ ├── main.ts
│ └── App.vue
├── README.md
├── index.html # 应用入口
├── jest.config.ts
├── LICENSE
├── package.json
├── tsconfig.json
├── cloudbaserc.json # 腾讯云CloudBase相关配置文件
├── vite.config.ts # vite配置文件
└── yarn.lock

Vite

Vite有多牛牪犇,我就不赘述了

简单的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(),
],
build: {
target: 'modules', // 默认值
// sourcemap: true,
},
server: {
port: 8080,
proxy: {
'/api/': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''),
},
'/api-prod/': {
target: 'http://localhost:3001',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api-prod/, ''),
},
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
},
},
})

@vue/compiler-sfc

这个就是前段时间比较争议的一个提案,不过真香,进一步了解

Vuex

采用分业务模块的方案

目录结构

src/store/
├── index.ts
└── modules
└── module1.ts
module1.ts
import { Module } from 'vuex'

interface State {
count: number
} const store: Module<State, unknown> = {
namespaced: true,
state() {
return {
count: 0,
}
},
getters: {
isEven(state) {
return state.count % 2 === 0
},
},
// 只能同步
mutations: {
increase(state, num = 1) {
state.count += num
},
decrease(state) {
state.count -= 1
},
},
// 支持异步,可以考虑引入API
actions: {
increase(context, payload) {
context.commit('increase', payload)
setTimeout(() => {
context.commit('decrease')
}, 1000)
},
},
} export default store
index.ts
import { createStore } from 'vuex'
import module1 from './modules/module1' // Create a new store instance.
const store = createStore({
modules: {
m1: module1,
},
}) export default store

main.ts中引入

import store from './store'
app.use(store)

视图中调用

import { computed } from 'vue'
import { useStore } from 'vuex' const store = useStore() // state
const count = computed(() => store.state.m1.count)
// getters
const isEven = computed(() => store.getters['m1/isEven'])
// mutations
const add = () => store.commit('m1/increase')
// actions
const asyncAdd = () => store.dispatch('m1/increase')

Vue-Router

目录结构

src/router/
├── index.ts
├── Interceptor
│ └── index.ts
└── routes
└── index.ts

拦截器与页面路由相分离

Interceptor/index.ts
import { Router } from 'vue-router'

declare module 'vue-router' {
interface RouteMeta {
// 是可选的
isAdmin?: boolean
// 是否需要登录
requireLogin?: boolean
}
} function registerRouteGuard(router: Router) {
/**
* 全局前置守卫
*/
router.beforeEach((to, from) => {
if (to.meta.requireLogin) {
if (from.path === '/') {
return from
}
return false
}
return true
}) /**
* 全局解析守卫
*/
router.beforeResolve(async (to) => {
if (to.meta.isAdmin) {
try {
console.log(to)
} catch (error) {
// if (error instanceof NotAllowedError) {
// // ... 处理错误,然后取消导航
// return false
// } else {
// // 意料之外的错误,取消导航并把错误传给全局处理器
// throw error
// }
console.error(error)
}
}
}) /**
* 全局后置守卫
*/
router.afterEach((to, from, failure) => {
// 改标题,监控上报一些基础信息
// sendToAnalytics(to.fullPath)
if (failure) {
console.error(failure)
}
})
} export default registerRouteGuard
routes/index.ts
import { RouteRecordRaw } from 'vue-router'
import Home from '../../pages/home/index.vue'
import About from '../../pages/about/index.vue'
import Dynamic from '../../pages/dynamic/index.vue' const NotFind = () => import('../../pages/404/index.vue')
const Index = () => import('../../pages/index/index.vue')
const Axios = () => import('../../pages/axios/index.vue')
const Element = () => import('../../pages/element/index.vue')
const routes: RouteRecordRaw[] = [
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFind },
{
path: '/',
name: 'index',
component: Index,
children: [
{ path: 'home', component: Home, name: 'home' },
{ path: 'about', component: About, name: 'about' },
{ path: 'axios', component: Axios, name: 'axios' },
{ path: 'element', component: Element, name: 'element' },
{
path: 'dynamic/:id',
component: Dynamic,
meta: {
requireLogin: false,
isAdmin: true,
},
name: 'dynamic',
},
],
},
] export default routes
router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import registerRouteGuard from './Interceptor'
import routes from './routes' const router = createRouter({
history: createWebHistory(import.meta.env.VITE_ROUTER_BASE as string),
routes,
}) // 注册路由守卫
registerRouteGuard(router) export default router

main.ts中引入

import router from './router'
app.use(router)

Axios

对axios的简单包装

ajax.ts
import axios from 'axios'

const instance = axios.create({
baseURL: import.meta.env.VITE_APP_AXIOS_BASE_URL,
}) /**
* 请求拦截
*/
instance.interceptors.request.use((config) => {
const { method, params } = config
// 附带鉴权的token
const headers: any = {
token: localStorage.getItem('token'),
}
// 不缓存get请求
if (method === 'get') {
headers['Cache-Control'] = 'no-cache'
}
// delete请求参数放入body中
if (method === 'delete') {
headers['Content-type'] = 'application/json;'
Object.assign(config, {
data: params,
params: {},
})
} return ({
...config,
headers,
})
}) /**
* 响应拦截
*/
instance.interceptors.response.use((v) => {
if (v.data?.code === 401) {
localStorage.removeItem('token')
// alert('即将跳转登录页。。。', '登录过期')
// setTimeout(redirectHome, 1500)
return v.data
}
if (v.status === 200) {
return v.data
}
// alert(v.statusText, '网络错误')
return Promise.reject(v)
})
export default instance

api目录结构

src/apis/
├── ajax.ts
├── index.ts
└── modules
└── public.ts

分业务模块编写接口调用方法,通过apis/index.ts对外统一导出

export { default as publicApi } from './modules/public'

注入全局的Axios实例,Vue2中通常是往原型(prototype)上挂载相关方法,在Vue3中由于使用CreateApp创建实例,所以推荐使用provide/inject 来传递一些全局的实例或者方法

main.ts

import Axios from './apis/ajax'

const app = createApp(App)

app.provide('$http', Axios)

视图中使用

import { inject } from 'vue'

const $http = inject<AxiosInstance>('$http')

polyfill.io

部分浏览器可能对ES的新语法支持程度不一致,存在一定的兼容问题,此时就需要使用polyfill(垫片)

polyfill.io是一个垫片服务,直接通过cdn按需引入垫片,不影响包体积

工作原理是通过解析客户端的UA信息,然后根据查询参数,判断是否需要垫片,不需要则不下发

简单使用

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<script
src="https://polyfill.alicdn.com/polyfill.min.js?features=es2019%2Ces2018%2Ces2017%2Ces5%2Ces6%2Ces7%2Cdefault"></script>
</head> <body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body> </html>

查询参数在线生成->url-builder

由于官方服务是部署在非大陆,所以延迟较高,由于polyfill-service是开源的,所以可以自己进行搭建

国内大厂也有一些镜像:

element UI Plus

Vue3版本的Element UI 组件库,虽然有些坑,但勉强能用 O(∩_∩)O哈哈~

按需引入在使用过程中发现Dev和Prod环境下的样式表现有差异,固采用全量引入的方式

utils/elementUI.ts

import { App } from '@vue/runtime-core'

// 全量引入
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import 'dayjs/locale/zh-cn'
import locale from 'element-plus/lib/locale/lang/zh-cn' export default function mountElementUI(app: App<Element>) {
app.use(ElementPlus, { locale })
}

main.ts

import mountElementUI from './utils/elementUI'

mountElementUI(app)

开箱即用的Vite-Vue3工程化模板的更多相关文章

  1. 手把手带你实现基于 Vite+Vue3 的在线Excel表格系统

    今天,葡萄带你了解如何基于Vite+Vue3实现一套纯前端在线表格系统. 在正式开始项目介绍之前,首先咱们首先来介绍一下Vite和Vue3. Vue3 2020年09月18日Vue.js 3.0发布, ...

  2. 如何开发一款基于 vite+vue3 的在线表格系统(下)

    在上篇内容中我们为大家分享了详细介绍Vue3和Vite的相关内容.在本篇中我们将从项目实战出发带大家了解Vite+Vue3 的在线表格系统的构建. 使用Vite初始化Vue3项目 在这里需要注意:根据 ...

  3. 基于 vite 创建 vue3 全家桶项目(vite + vue3 + tsx + pinia)

    vite 最近非常火,它是 vue 作者尤大神发布前端构建工具,底层基于 Rollup,无论是启动速度还是热加载速度都非常快.vite 随 vue3 正式版一起发布,刚开始的时候与 vue 绑定在一起 ...

  4. vite vue3 规范化与Git Hooks

    在 <JS 模块化>系列开篇中,曾提到前端技术的发展不断融入很多后端思想,形成前端的"四个现代化":工程化.模块化.规范化.流程化.在该系列文章中已详细介绍了模块化的发 ...

  5. 如何开发一款基于 Vite+Vue3 的在线表格系统(上)

    今天,葡萄带你了解如何基于Vite+Vue3实现一套纯前端在线表格系统. 在正式开始项目介绍之前,首先咱们首先来介绍一下Vite和Vue3. Vue3 Vue是什么?大多前端开发者对这个词已毫不陌生了 ...

  6. 一个实用的 vite + vue3 组件库脚手架工具,提升开发效率

    无论是 vue2 全家桶还是 vue3 + vite + TypeScript,组件库的使用几乎大家都会,但自己开发一个独立组件库就不是每个人都掌握的,因为搭建组件库的基础开发环境,就会让很多同学望而 ...

  7. Vite+TS带你搭建一个属于自己的Vue3组件库

    theme: nico 前言 随着前端技术的发展,业界涌现出了许多的UI组件库.例如我们熟知的ElementUI,Vant,AntDesign等等.但是作为一个前端开发者,你知道一个UI组件库是如何被 ...

  8. 开箱即用 yyg-cli(脚手架工具):快速创建 vue3 组件库和vue3 全家桶项目

    1 yyg-cli 是什么 yyg-cli 是优雅哥开发的快速创建 vue3 项目的脚手架.在 npm 上发布了两个月,11月1日进行了大升级,发布 1.1.0 版本:支持创建 vue3 全家桶项目和 ...

  9. Vite ❤ Electron——基于Vite搭建Electron+Vue3的开发环境【一】

    背景 目前社区两大Vue+Electron的脚手架:electron-vue和vue-cli-plugin-electron-builder, 都有这样那样的问题,且都还不支持Vue3,然而Vue3已 ...

随机推荐

  1. JS逆向-抠代码的第四天【手把手学会抠代码】

    今天是md5巩固项目,该项目比昨天的复杂一些,但方法思路是一样的. 今天的目标:https://www.webportal.top/ 打开网站,填入账号密码(密码项目以123456做测试).点击登录抓 ...

  2. unbutu系统扩展磁盘大小

    建议下载一个可视化工具,这样的话就更不容易出错 sudo apt install gparted 在终端中运行这条命令就可安装可视化工具 fdisk -l 在终端中输入这条命令来查看自己系统所挂载的磁 ...

  3. JAVA 写一个方法,判断一个整数是否为素数

    1 import java.util.Scanner; 2 3 public class Question3 { 4 public static void main(String[] args) { ...

  4. 网关Ocelot功能演示安排的明明白白~~~

    前言 网关(Gateway)在微服务架构中至关重要,可以将其理解为是外部客户端(前端.MVC后台等调用方)与后台服务的连接点,通过这层可以做统一的处理,比如路由.身份认证和授权.服务治理等: 网关的好 ...

  5. 【洛谷】P1294 高手去散步

    题目背景 高手最近谈恋爱了.不过是单相思."即使是单相思,也是完整的爱情",高手从未放弃对它的追求.今天,这个阳光明媚的早晨,太阳从西边缓缓升起.于是它找到高手,希望在晨读开始之前 ...

  6. java面试-Java内存模型(JMM)

    p.p1 { margin: 0; font: 15px Helvetica } 一.并发编程两个关键问题 线程之间如何通信.同步.java并发采用的是共享内存模型 二.JMM内存模型的抽象结构 描述 ...

  7. 6.4map用法

    map是映射也是常用的STL容器,可以将任何基本类型映射到任何的基本类型,如 map<char,int>mp 一.基本定义 map<typename,typename>mp; ...

  8. JetBrains系列IDE无法输入中文

    1 问题描述 环境Linux+fcitx,JetBrains的IDE无法输入中文,包括IDEA,PyCharm,WebStorm,CLion等等. 2 解决方案 Linux下一般使用fcitx进入中文 ...

  9. Ambassador-08-跨域

    官方文档:https://www.getambassador.io/docs/latest/topics/using/cors/ Cross-Origin Resource Sharing-CORS ...

  10. Floyd算法C++实现与模板题应用

    简介 Floyd算法算是最简单的算法,没有之一. 其状态转移方程如下map[i , j] =min{ map[i , k] + map[k , j] , map[i , j] }: map[i , j ...