经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » JavaScript » 查看文章
开箱即用的Vite-Vue3工程化模板
来源:cnblogs  作者:粥里有勺糖  时间:2021/5/17 9:21:15  对本文有异议

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

前言

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

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

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

体验模板

模板仓库地址

线上预览

两步到位

本地引入

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

启动

  1. # 安装依赖
  2. yarn install
  1. # 运行
  2. yarn dev

模板介绍

已包含特性

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

目录介绍

  1. .
  2. ├── __tests__
  3. ├── dist # 构建结果
  4. ├── public # 公共静态资源
  5. ├── src # 源码目录
  6. ├── apis
  7. ├── assets
  8. ├── components
  9. ├── pages
  10. ├── router
  11. ├── store
  12. ├── @types
  13. ├── utils
  14. ├── shims-vue.d.ts
  15. ├── env.d.ts
  16. ├── main.ts
  17. └── App.vue
  18. ├── README.md
  19. ├── index.html # 应用入口
  20. ├── jest.config.ts
  21. ├── LICENSE
  22. ├── package.json
  23. ├── tsconfig.json
  24. ├── cloudbaserc.json # 腾讯云CloudBase相关配置文件
  25. ├── vite.config.ts # vite配置文件
  26. └── yarn.lock

Vite

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

简单的vite.config.ts配置文件
  1. import { defineConfig } from 'vite'
  2. import vue from '@vitejs/plugin-vue'
  3. import path from 'path'
  4. // https://vitejs.dev/config/
  5. export default defineConfig({
  6. plugins: [
  7. vue(),
  8. ],
  9. build: {
  10. target: 'modules', // 默认值
  11. // sourcemap: true,
  12. },
  13. server: {
  14. port: 8080,
  15. proxy: {
  16. '/api/': {
  17. target: 'http://localhost:3000',
  18. changeOrigin: true,
  19. rewrite: (p) => p.replace(/^\/api/, ''),
  20. },
  21. '/api-prod/': {
  22. target: 'http://localhost:3001',
  23. changeOrigin: true,
  24. rewrite: (p) => p.replace(/^\/api-prod/, ''),
  25. },
  26. },
  27. },
  28. resolve: {
  29. alias: {
  30. '@': path.resolve(__dirname, './src'),
  31. '@components': path.resolve(__dirname, './src/components'),
  32. },
  33. },
  34. })

@vue/compiler-sfc

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

Vuex

采用分业务模块的方案

目录结构

  1. src/store/
  2. ├── index.ts
  3. └── modules
  4. └── module1.ts
module1.ts
  1. import { Module } from 'vuex'
  2. interface State {
  3. count: number
  4. }
  5. const store: Module<State, unknown> = {
  6. namespaced: true,
  7. state() {
  8. return {
  9. count: 0,
  10. }
  11. },
  12. getters: {
  13. isEven(state) {
  14. return state.count % 2 === 0
  15. },
  16. },
  17. // 只能同步
  18. mutations: {
  19. increase(state, num = 1) {
  20. state.count += num
  21. },
  22. decrease(state) {
  23. state.count -= 1
  24. },
  25. },
  26. // 支持异步,可以考虑引入API
  27. actions: {
  28. increase(context, payload) {
  29. context.commit('increase', payload)
  30. setTimeout(() => {
  31. context.commit('decrease')
  32. }, 1000)
  33. },
  34. },
  35. }
  36. export default store
index.ts
  1. import { createStore } from 'vuex'
  2. import module1 from './modules/module1'
  3. // Create a new store instance.
  4. const store = createStore({
  5. modules: {
  6. m1: module1,
  7. },
  8. })
  9. export default store

main.ts中引入

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

视图中调用

  1. import { computed } from 'vue'
  2. import { useStore } from 'vuex'
  3. const store = useStore()
  4. // state
  5. const count = computed(() => store.state.m1.count)
  6. // getters
  7. const isEven = computed(() => store.getters['m1/isEven'])
  8. // mutations
  9. const add = () => store.commit('m1/increase')
  10. // actions
  11. const asyncAdd = () => store.dispatch('m1/increase')

Vue-Router

目录结构

  1. src/router/
  2. ├── index.ts
  3. ├── Interceptor
  4. └── index.ts
  5. └── routes
  6. └── index.ts

拦截器与页面路由相分离

Interceptor/index.ts
  1. import { Router } from 'vue-router'
  2. declare module 'vue-router' {
  3. interface RouteMeta {
  4. // 是可选的
  5. isAdmin?: boolean
  6. // 是否需要登录
  7. requireLogin?: boolean
  8. }
  9. }
  10. function registerRouteGuard(router: Router) {
  11. /**
  12. * 全局前置守卫
  13. */
  14. router.beforeEach((to, from) => {
  15. if (to.meta.requireLogin) {
  16. if (from.path === '/') {
  17. return from
  18. }
  19. return false
  20. }
  21. return true
  22. })
  23. /**
  24. * 全局解析守卫
  25. */
  26. router.beforeResolve(async (to) => {
  27. if (to.meta.isAdmin) {
  28. try {
  29. console.log(to)
  30. } catch (error) {
  31. // if (error instanceof NotAllowedError) {
  32. // // ... 处理错误,然后取消导航
  33. // return false
  34. // } else {
  35. // // 意料之外的错误,取消导航并把错误传给全局处理器
  36. // throw error
  37. // }
  38. console.error(error)
  39. }
  40. }
  41. })
  42. /**
  43. * 全局后置守卫
  44. */
  45. router.afterEach((to, from, failure) => {
  46. // 改标题,监控上报一些基础信息
  47. // sendToAnalytics(to.fullPath)
  48. if (failure) {
  49. console.error(failure)
  50. }
  51. })
  52. }
  53. export default registerRouteGuard
routes/index.ts
  1. import { RouteRecordRaw } from 'vue-router'
  2. import Home from '../../pages/home/index.vue'
  3. import About from '../../pages/about/index.vue'
  4. import Dynamic from '../../pages/dynamic/index.vue'
  5. const NotFind = () => import('../../pages/404/index.vue')
  6. const Index = () => import('../../pages/index/index.vue')
  7. const Axios = () => import('../../pages/axios/index.vue')
  8. const Element = () => import('../../pages/element/index.vue')
  9. const routes: RouteRecordRaw[] = [
  10. { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFind },
  11. {
  12. path: '/',
  13. name: 'index',
  14. component: Index,
  15. children: [
  16. { path: 'home', component: Home, name: 'home' },
  17. { path: 'about', component: About, name: 'about' },
  18. { path: 'axios', component: Axios, name: 'axios' },
  19. { path: 'element', component: Element, name: 'element' },
  20. {
  21. path: 'dynamic/:id',
  22. component: Dynamic,
  23. meta: {
  24. requireLogin: false,
  25. isAdmin: true,
  26. },
  27. name: 'dynamic',
  28. },
  29. ],
  30. },
  31. ]
  32. export default routes
router/index.ts
  1. import { createRouter, createWebHistory } from 'vue-router'
  2. import registerRouteGuard from './Interceptor'
  3. import routes from './routes'
  4. const router = createRouter({
  5. history: createWebHistory(import.meta.env.VITE_ROUTER_BASE as string),
  6. routes,
  7. })
  8. // 注册路由守卫
  9. registerRouteGuard(router)
  10. export default router

main.ts中引入

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

Axios

对axios的简单包装

ajax.ts
  1. import axios from 'axios'
  2. const instance = axios.create({
  3. baseURL: import.meta.env.VITE_APP_AXIOS_BASE_URL,
  4. })
  5. /**
  6. * 请求拦截
  7. */
  8. instance.interceptors.request.use((config) => {
  9. const { method, params } = config
  10. // 附带鉴权的token
  11. const headers: any = {
  12. token: localStorage.getItem('token'),
  13. }
  14. // 不缓存get请求
  15. if (method === 'get') {
  16. headers['Cache-Control'] = 'no-cache'
  17. }
  18. // delete请求参数放入body中
  19. if (method === 'delete') {
  20. headers['Content-type'] = 'application/json;'
  21. Object.assign(config, {
  22. data: params,
  23. params: {},
  24. })
  25. }
  26. return ({
  27. ...config,
  28. headers,
  29. })
  30. })
  31. /**
  32. * 响应拦截
  33. */
  34. instance.interceptors.response.use((v) => {
  35. if (v.data?.code === 401) {
  36. localStorage.removeItem('token')
  37. // alert('即将跳转登录页。。。', '登录过期')
  38. // setTimeout(redirectHome, 1500)
  39. return v.data
  40. }
  41. if (v.status === 200) {
  42. return v.data
  43. }
  44. // alert(v.statusText, '网络错误')
  45. return Promise.reject(v)
  46. })
  47. export default instance

api目录结构

  1. src/apis/
  2. ├── ajax.ts
  3. ├── index.ts
  4. └── modules
  5. └── public.ts

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

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

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

main.ts

  1. import Axios from './apis/ajax'
  2. const app = createApp(App)
  3. app.provide('$http', Axios)

视图中使用

  1. import { inject } from 'vue'
  2. const $http = inject<AxiosInstance>('$http')

polyfill.io

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

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

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

简单使用

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

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

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

国内大厂也有一些镜像:

element UI Plus

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

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

utils/elementUI.ts

  1. import { App } from '@vue/runtime-core'
  2. // 全量引入
  3. import ElementPlus from 'element-plus'
  4. import 'element-plus/lib/theme-chalk/index.css'
  5. import 'dayjs/locale/zh-cn'
  6. import locale from 'element-plus/lib/locale/lang/zh-cn'
  7. export default function mountElementUI(app: App<Element>) {
  8. app.use(ElementPlus, { locale })
  9. }

main.ts

  1. import mountElementUI from './utils/elementUI'
  2. mountElementUI(app)

原文链接:http://www.cnblogs.com/roseAT/p/14773701.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号