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

简介: 开箱即用的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)


相关文章
|
16天前
|
存储 JavaScript
解锁Vuex高级玩法:模块化与插件共舞,让你的Vue项目状态管理如虎添翼!
【8月更文挑战第27天】Vuex是一款专为Vue.js应用程序设计的状态管理模式及库,它通过集中管理组件状态来确保状态变更的可预测性。在大型应用中,采用模块化管理可以让代码结构更加清晰,同时利用插件增强功能。模块化管理允许将store拆分为包含各自state、mutations、actions和getters的独立模块。插件则能监听状态变化,实现诸如日志记录或数据持久化等功能。本文通过具体示例介绍了如何在Vuex中实现模块化管理和插件的高级应用。
31 1
|
27天前
|
JavaScript
如何创建一个Vue项目(手把手教你)
这篇文章是一篇手把手教读者如何创建Vue项目的教程,包括使用管理员身份打开命令行窗口、找到存放项目的位置、通过vue-cli初始化项目、填写项目信息、进入项目目录、启动项目等步骤,并提供了一些常见第三方库的引入方法。
如何创建一个Vue项目(手把手教你)
|
28天前
|
JavaScript 前端开发
在Vue2或Vue3中项目中使用 Isotope(同位素) 过滤和排序神奇的布局神器,全网独家实现!
本文介绍了在Vue2或Vue3项目中如何使用Isotope(同位素)布局库来创建动态的网格布局,并提供了详细的代码实现和效果展示,包括过滤和排序功能。
49 0
在Vue2或Vue3中项目中使用 Isotope(同位素) 过滤和排序神奇的布局神器,全网独家实现!
|
3天前
|
前端开发
vue3+ts项目中使用mockjs
vue3+ts项目中使用mockjs
189 57
|
6天前
|
数据采集 JavaScript 搜索推荐
我们一起聊聊如何对Vue项目进行搜索引擎优化
【9月更文挑战第4天】Vue 项目的搜索引擎优化(SEO)较为复杂,因其内容默认由 JavaScript 渲染,部分搜索引擎难以索引。为提升 SEO 效果,可采用服务器端渲染(SSR)或预渲染,使用 Nuxt.js 或 Vue Server Renderer 实现 SSR,或利用 Prerender SPA Plugin 预渲染静态 HTML。此外,动态管理 Meta 标签、优化静态内容与 Sitemap、懒加载、图片优化、提升页面速度、设置正确的路由模式、使用结构化数据及构建良好外链均有益于 SEO。
40 11
|
23天前
|
JavaScript
Vue项目如何生成树形目录结构
这篇文章介绍了如何在Vue项目中生成树形目录结构,通过安装并使用`mddir`命令行工具来创建一个`directoryList.md`文件,从而快速获取项目的树形目录列表。
Vue项目如何生成树形目录结构
|
28天前
|
数据可视化 JavaScript
Vue3项目使用G6可视化组件实现一个树形机构图
在Vue 3项目中使用G6可视化组件库实现树形机构图的构建和展示。
246 1
Vue3项目使用G6可视化组件实现一个树形机构图
|
14天前
|
JavaScript
Vue3相关组件项目依赖依赖版本信息
这篇文章展示了一个Vue 3项目中使用的组件和库的依赖版本信息,包括`ant-design-vue`、`swiper`、`vue`、`vue-router`等,以及开发依赖如`vite`、`vue-tsc`、`eslint`等。
Vue3相关组件项目依赖依赖版本信息
|
28天前
|
JavaScript UED
如何在Vue3项目中使用防抖节流技巧
在Vue 3项目中使用防抖和节流技巧以优化组件性能,包括使用`lodash`库和自定义实现这两种方法。
162 0
如何在Vue3项目中使用防抖节流技巧
|
28天前
在 Vue3 + ElementPlus 项目中使用 el-autocomplete 控件
本文介绍了在Vue3 + ElementPlus项目中如何使用`el-autocomplete`控件实现自动补全输入功能,并探讨了不同版本ElementPlus中`clearable`属性的兼容性问题。
153 0
在 Vue3 + ElementPlus 项目中使用 el-autocomplete 控件