关于前端:从-16-个方向逐步搭建基于-vue3-的前端架构

48次阅读

共计 17996 个字符,预计需要花费 45 分钟才能阅读完成。

前言

因为 vue3.2 版本的公布,<script setup> 的实验性标记曾经去掉,这阐明这个语法提案曾经正式开始应用,并且我集体对这个计划示意十分喜爱,其余的更新请自行理解。到目前为止,我认为 vue3 曾经齐全能够用于生产环境。在此将我的开发体验,总结至此,分享给大家。

我认为前端架构外围工作是定制一套适宜以后业务需要的解决方案,从而升高需要的减少而带来的技术实现的复杂度。上面我将从 16 个方向,逐步率领大家搭建一套属于你本人的脚手架,制订一套正当的解决方案,为我的项目打下良好的根底,与伙伴造成适合的开发习惯。

因为篇幅问题,以解说实现思路为主,心愿大家友善发言,共同进步!

目录

  • 1. 搭建脚手架

    • 1.1 前端脚手架应具备哪些性能?
    • 1.2 如何开发一款本人的脚手架?
    • 1.3 如何依据选项生成文件?
  • 2. 基于 vite 的搭建根底模板

    • 2.1 创立根本模板我的项目
    • 2.2 罕用插件举荐
  • 3. 应用 Typescript
  • 4. 配置环境变量

    • 4.1 配置模式
    • 4.2 罕用的环境变量
    • 4.3 封装动态资源文件
    • 4.4 封装 SVG 的图标组件
  • 5. 按需主动引入组件

    • 5.1 装置与配置
    • 5.2 扭转全局组件注册形式
    • 5.3 主动引入组件库
  • 6. 款式

    • 6.1 预设根底款式
    • 6.2 CSS 预处理器
    • 6.3 开启 scoped
    • 6.4 深度选择器
  • 7. 布局

    • 7.1 惯例的布局
    • 7.2 非凡的布局
  • 8. 集成 Tailwind.css

    • 8.1 效率晋升
    • 8.2 JIT 模式
    • 8.3 对于打包体积
  • 9.vuex 代替计划 pinia

    • 9.1 为什么采纳 Pinia ?
    • 9.2 创立 Store
    • 9.3 State
    • 9.4 Getters
    • 9.5 Actions
    • 9.6 Devtools
  • 10. 基于 mitt 解决组件间事件联动

    • 10.1 为什么抉择 mitt?
    • 10.2 重大正告
    • 10.3 如何应用 mitt?
  • 11. 异步申请

    • 11.1 基于 axios 的封装
    • 11.2 为 axios 减少泛型的反对
    • 11.3 封装更不便的 useRequest
    • 11.4 对立的 API 接口治理
    • 11.5 mock
  • 12. 路由

    • 12.1 创立路由三部曲
    • 12.2 应用 meta 丰盛你的路由
  • 13. 我的项目性能与细节优化

    • 13.1 开启 gzip
    • 13.2 页面载入进度条
    • 13.3 Title
    • 13.4 解决挪动端应用 vh 的问题
    • 13.5 能够常驻的 JavaScript 库
  • 14. 代码格调与流程标准

    • 14.1 ESLint
    • 14.2 StyleLint
    • 14.3 代码提交标准
  • 15. 编写应用文档

    • 15.1 应用 vitepress 搭建文档
    • 15.2 文档部署
  • 16. 插件

    • 16.1 VSCode 插件
    • 16.2 Chrome 插件
  • 源码
  • 参考

1. 搭建脚手架

应用 vue-clivite,通过一系列的配置,初始化一个开发模板,无需从零开始搭建开发环境,能够无效的晋升开发效率,置信也是大多数开发者接手一个新我的项目所应用的一种形式。只管官网提供的脚手架曾经足够优良,但未必是真正合乎咱们本人团队的应用习惯,所以从官网的根底上,开发一款属于咱们本人的脚手架,能更多的晋升开发效率。

1.1 前端脚手架应具备哪些性能?

  • 缩小反复的初始化工作,不须要再复制其余相似的我的项目删除无关代码,或从零搭建一个我的项目。
  • 能够依据团队需要,应用简略的交互操作生成相应的目录构造和文件。
  • 对立团队的开发习惯、代码格调,保障构建后果的一致性。
  • 残缺的应用文档,升高新人上手、开发和前期保护老本。

1.2 如何开发一款本人的脚手架?

提到构建前端工程化中脚手架,置信大家曾经看过不少文章,几年前我也已经写过一篇对于脚手架构建的文章,轻易搜一下关键词能够看到很多相干的文章,在这里不做太多的介绍,次要讲一些这些文章中很少提到的如何依据选项生成文件。

1.3 如何依据选项生成文件?

说实话我也不晓得大佬们是怎么依据各种配置编译成相应的文件,这块心愿大家踊跃发言,寻求一种更佳高效简洁的形式。在这里跟大家分享一下我的计划:

交互方面,搭建过脚手架的同学肯定晓得 inquirer,这个库能够很不便的通过交互式操作获取到咱们抉择的一些自定义配置参数。那么问题来了,如何通过这些配置相应的创立对应的文件呢?

这里我举荐应用 EJS + Prettier 生成代码,通过 fs-extra 写入最终的文件。

  • EJS

EJS 是一款 JavaScript 模板引擎,咱们能够通过传入参数,生成对应的代码串,例如创立一个 package.ejs 用来生成 package.json 中,如果咱们抉择应用了 scss 作为 CSS 预处理器,而后将 sassstylelint-scss 作为我的项目的装置依赖:

<% if (precss === 'scss') { -%>
  "sass": "1.26.5",
  "stylelint-scss": "^3.20.1",
<% } -%>

模板引擎能够帮你通过参数生成代码,它并不会限度你生成任何类型的代码文件,因为咱们生成的是纯代码,最初通过读取 .ejs 文件对应生成相应的类型文件即可。

  • Prettier

Prettier 是一款代码格式化工具,置信大家对它并不生疏。应用 EJS 生成的目标还是给开发人员浏览和编辑,所以生成的代码应该合乎最终的格局要求,因为后续咱们会为脚手架增加 ESLint 和 StyleLint 等工具,刚刚创立的我的项目外面一堆红线报错可是非常不敌对的。

import prettier = require("prettier");
prettier.format(code, { parser: 'json'}))

parser 是 prettier 的解析器,常见的 typescript、css、less、json 等文件都能够进行格式化。

2. 基于 vite 的搭建根底模板

最早搭建 vue3 脚手架的时候,我抉择的用 vue/cli 搭建,因为生态不健全,有些基于 webpack 的性能无奈应用,但当初 vite 生态曾经比较完善了,所以重构脚手架,由 webpack 转向 vite,这一步极大的晋升了开发体验。

2.1 创立根本模板我的项目

npm init vite@latest
yarn create vite
pnpm create vite

而后依照提醒操作即可,vite 提供的选项很少,只有 vue 或 vue + ts,不像 vue/cli 提供那么多的配置形式,所以剩下的货色须要咱们手动配置。

当然 vite 也提供了很多模板,然而我认为做加法比做减法更加容易,在泛滥的模板中很难找到适宜咱们本人的。

2.2 罕用插件举荐

这里先简略理解几个好用的 vite 插件:

  • unplugin-vue-components:组件的按需主动导入。
  • vite-plugin-svg-icons:用于生成 svg 雪碧图。
  • vite-plugin-compression:应用 gzip 或者 brotli 来压缩资源。

为什么只举荐这么几个插件?因为 vite 对许多 webpack 须要装置的 loaderplugin 都有着天生的反对,比方 less、sass、typescript,后续会在相应的章节阐明用法。

3. 应用 Typescript

vue2.x 版本对 TypeScript 的反对是硬伤,而 TypeScript 对大型项目的保障能力是被广泛认可的。这一点在 vue3.x 版本中失去了十分敌对的反对。

Vite 人造反对引入 .ts 文件。

这里对 tsconfig.json 做了一些批改:

{
  "compilerOptions": {"types": ["vite/client"],
    "baseUrl": "src",
    "paths": {"@/*": ["./*"]
    }
  },
  "exclude": ["node_modules"]
}

在初期应用 typeScript 的时候,很多人都很喜爱应用 any 类型,把 typeScript 写成了 anyScript,尽管应用起来很不便,然而这就失去了 typeScript 的类型查看意义了,当然写类型的习惯是须要缓缓去养成的,不必急于一时。

4. 配置环境变量

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

这里咱们能够建设 4 个 .env 文件,一个通用配置和三种环境:开发、测试、生产。

4.1 配置模式

NODE_ENV=development # 开发模式
NODE_ENV=production # 生产模式
  • .env 通用配置,我集体喜爱把他当作我的项目的配置文件,例如我的项目的 title,此文件不对应任何模式。
  • .env.development 开发环境,应用 development 模式。
  • .env.staging 测试环境,因为要部署到测试服务器,或本地应用 serve 命令预览,所以应用 production 模式。
  • .env.production 生产环境,因为要部署到测试服务器,或本地应用 serve 命令预览,所以应用 production 模式。

package.json 内 script 须要减少 staging 命令

"script": {
  "build": "vue-tsc --noEmit && vite build",
  "staging": "vue-tsc --noEmit && vite build --mode staging",
  "serve": "vite preview --host"
}

4.2 罕用的环境变量

举荐应用以下常见的三个变量:

  • VITE_APP_BASE_URL

接口申请地址。

通常后端会辨别三种环境,部署在不同的地址下。

  • VITE_APP_STATIC_URL

动态资源地址。

动态资源我是不倡议你间接放在我的项目中,这会导致我的项目仓库变得微小。

本地开发和测试环境我会选在应用本地搭建的动态资源服务器,你能够找后端运维的同学帮你搭建,或者你应用 http-server 在本地启动一个服务器也能够。生产环境倡议上传至 OSS。

  • VITE_PUBLIC_PATH

构建资源公共门路。

这个与 vue/cli 中的 publicPath 同理,有的时候你构建的我的项目并不是寄存在跟门路下,例如 http://ip:port/{我的项目名}

4.3 封装动态资源文件

如果你配置了 VITE_APP_STATIC_URL 动态资源环境变量,那么你须要封装以下两个货色:

  • 依据环境返回理论的资源地址函数。
  • 方便使用的动态资源组件。

baseStaticUrl.ts

// 解决动态资源链接
export default function baseStaticUrl(src = '') {const { VITE_APP_STATIC_URL} = import.meta.env;
  if (src) {return `${VITE_APP_STATIC_URL}${src}`;
  }
  return VITE_APP_STATIC_URL as string;
}

动态资源组件

动态资源次要有图片、音频和视频三种常见的模式。

  • 通过 src 写入绝对的门路,应用上述的函数来补全残缺的门路,即可在不同的环境下应用不同地址的动态资源。
  • 通过 type 传入图片、音频和视频的类型。
  • autoplay 是解决以视频为背景的状况下,视频无奈自动播放的问题。
<script lang="ts" setup>
import {computed, ref, Ref, withDefaults, onMounted, watch} from 'vue';
import {baseStaticUrl} from '@/libs/utils';
import useDevice from '@/hooks/useDevice';

interface Props {
  src?: string;
  type?: string;
  autoplay?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
  src: '',
  type: 'img',
  autoplay: true,
});

const envSrc = computed(() => baseStaticUrl(props.src));
// 解决视频自动播放(解决 chrome 无奈自动播放的问题)const {deviceType} = useDevice();
const poster = computed(() =>
  deviceType.value === 'desktop' ? '' : baseStaticUrl(props.src),
);
const videoRef: Ref<HTMLVideoElement | null> = ref(null);

// 解决挪动端视频无奈自动播放的问题
function videoAutoPlay() {if (props.type === 'video' && videoRef.value !== null) {videoRef.value.src = baseStaticUrl(props.src);
  }
  if (props.autoplay && videoRef.value) {videoRef.value.oncanplay = () => {if (videoRef.value) videoRef.value.play();};
  }
}

// 自动播放视频
onMounted(() => { videoAutoPlay();});
// 监听视频 src,如果存在则自动播放

watch(envSrc, () => {if (videoRef.value) videoRef.value.play();});
</script>

<script lang="ts">
export default {name: 'StaticFile'};
</script>

<template>
  <img v-if="type ==='img'":src="envSrc" />
  <video ref="videoRef" v-else-if="type ==='video'"muted :poster="poster" />
  <audio v-else :src="envSrc" />
</template>

4.4 封装 SVG 的图标组件

svg 图标比拟小,而且都是可读的 xml 文本,所以咱们把它间接放在我的项目中即可,通过 vite-plugin-svg-icons 插件,实现主动引入 svg 图标。

配置 vite.config.ts:

plugins: [
  viteSvgIcons({iconDirs: [resolve(process.cwd(), 'src/assets/icons')],
    symbolId: 'icon-[dir]-[name]',
  }),
]

封装一个 vue 组件:

<script setup lang="ts">
import {computed, withDefaults} from 'vue';

interface Props {
  prefix?: string;
  name?: string;
  color?: string;
}

const props = withDefaults(defineProps<Props>(), {
  prefix: 'icon',
  name: '',
  color: '#000',
});

const symbolId = computed(() => `#${props.prefix}-${props.name}`);
</script>

<template>
  <svg aria-hidden="true">
    <use :xlink:href="symbolId" :fill="color" />
  </svg>
</template>

首先将下载的 .svg 图标放入 @/assets/icons 文件夹下

<svg-icon name=""color="" />
  • name 搁置在 @/assets/icons 文件夹下的文件名。
  • color 色彩填充,应用此项会默认笼罩图标色彩。

5. 按需主动引入组件

unplugin-vue-components 是一款十分弱小的插件(极力推荐),外围性能就是帮忙你主动按需引入组件,Tree-shakable,只注册你应用的组件。这里说一下他的两个外围应用形式和配置形式。

此插件不仅反对 vue3,同时也反对 vue2,并且反对 Vite、Webpack、Vue CLI、Rollup。

5.1 装置与配置

装置:

npm i unplugin-vue-components -D

配置:

// vite.config.ts
import Components from 'unplugin-vue-components/vite'

export default defineConfig({
  plugins: [Components({ /* options */}),
  ],
})

这里的 options 能够配置一些选项,前面提到的组件库注册会应用到。

5.2 扭转全局组件注册形式

咱们通常将全局的组件封装在 @/src/components 中,而后通过 app.component() 注册全局组件。应用此插件后,无需手写注册,间接在模板中应用组件即可:

这里引入官网的示例:

<template>
  <div>
    <HelloWorld msg="Hello Vue 3.0 + Vite" />
  </div>
</template>

<script>
export default {name: 'App'}
</script>

主动编译为:

<template>
  <div>
    <HelloWorld msg="Hello Vue 3.0 + Vite" />
  </div>
</template>

<script>
import HelloWorld from './src/components/HelloWorld.vue'

export default {
  name: 'App',
  components: {HelloWorld}
}
</script>

5.3 主动引入组件库

在应用组件库时,惯例组件咱们也会注册到全局,如果应用部分注册因为页面中会应用到多个组件,会十分麻烦,所以这个性能绝佳,例如咱们应用 ant-design-vue 组件库。

间接在模板中应用即可,无需手动注册或部分援用:

<template>
  <a-button> 按钮 </a-button>
</template>

当然,你还须要在 vite 中引入它的解析器:

import Components from 'unplugin-vue-components/vite'
import {AntDesignVueResolver} from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    Components({
      resolvers: [AntDesignVueResolver(),
      ]
    })
  ],
})

目前反对的解析器,依据你的爱好去抉择:

  • Ant Design Vue
  • Element Plus
  • Element UI
  • Headless UI
  • IDux
  • Naive UI
  • Prime Vue
  • Vant
  • VEUI
  • Varlet UI
  • View UI
  • Vuetify
  • VueUse Components
  • Quasar

6. 款式

我的项目中最好应用通用款式,能够创立 src/styles 目录寄存,这里举荐一些分类:

styles
  ├── antd # 组件库款式笼罩,命名自取,这里以 ant design 为例
  ├── color.less # 色彩
  ├── index.less # 入口
  ├── global.less # 公共类
  ├── transition.less # 动画相干
  └── variable.less # 变量

6.1 预设根底款式

置信用过 normalize 的同学不在少数,它能够重置 css 款式,使各浏览器成果保持一致。前面的章节会提到 tailwind.css,它内置了预设款式重置的性能,与 normalize 还是有肯定的区别,有趣味的同学能够理解一下。

6.2 CSS 预处理器

尽管 vite 原生反对 less/sass/scss/stylus,然而你必须手动装置他们的预处理器依赖,例如:

npm install -D less

如何抉择预处理器?

举荐应用你是所应用的组件库的款式语言,因为 css 预处理器学会一种后,动手其余简直没有学习老本。

6.3 开启 scoped

没有加 scoped 属性,会编译成全局款式,造成全局净化。

<style scoped></style>

6.4 深度选择器

有时咱们可能想明确地制订一个针对子组件的规定。

如果你心愿 scoped 款式中的一个选择器可能作用得“更深”,例如影响子组件,你能够应用 >>> 操作符。有些像 Sass 之类的预处理器无奈正确解析 >>>。这种状况下你能够应用 /deep/ 或 ::v-deep 操作符取而代之——两者都是 >>> 的别名,同样能够失常工作。

7. 布局

页面整体布局是一个产品最外层的框架结构,往往会蕴含导航、页脚、侧边栏等。在页面之中,也有很多区块的布局构造。在实在我的项目中,页面布局通常统领整个利用的界面,有十分重要的作用,所以独自拆分进去也是十分有必要的。

在脚手架中,所有的通用布局组件都应该放在 src/layouts 中,这种封装比较简单,这里就不贴代码了,大家依照本人理论状况自行施展,在此仅提供一下封装思路。

7.1 惯例的布局

BasicLayout

根底页面布局,蕴含了头部导航,侧边栏等。

BlankLayout

空白的布局。

7.2 非凡的布局

RouteLayout

如果你的我的项目在路由切换中须要对某些二级页面进行缓存,那么举荐你创立一个 RouteLayout,通过路由 meta 中的配置,返回 router-view 或者应用 keep-alive 包裹的 router-view

UserLayout

用于用户登录注册等页面抽离进去。

PageLayout

根底布局,蕴含了面包屑等信息,内含 slot。

8. 集成 Tailwind.css

Tailwind.css 在我第一次看到它的时候,心田是比拟恶感的,但实际上手之后又感觉真香。从 vue2 我的项目中,我曾经引入了 tailwind,整体的开发后果就是,根本很少再应用 <style> 标签去转本定义一些 class 和款式,毕竟起名字这种事,一个是波及到标准,一个是波及到英语。如果你抉择 tailwind,CSS 预处理器的作用就会显得微不足道,因为你无需再自定定义各种变量和 mixins。

总体来说,学习老本并不高,花上两个小时足够上手,记住不必死记硬背那些类名。

8.1 效率晋升

很多人总是说款式要与 HTML 拆散,当初为什么又要提倡 tailwind 这种与 HTML 紧密结合的工具?这是因为当初应用 vue 这类框架曾经高度组件化,款式拆散是为了不便复用和保护,但在组件化背后款式拆散只能是升高开发效率。

上面介绍一下 tailwind 提供了哪些晋升效率的性能:

  • 提供了大量的性能类,极大的进步了可维护性。
  • 响应式设计,各种设施一把梭。
  • 悬停、焦点和其它状态。
  • 深色模式。
  • 反对配置,例如色彩方面很难做到跟你的设计师对立。
  • 不必为起名字而纠结???

8.2 JIT 模式

如果你的环境反对 postcss8(vue/cli 构建的 vue2 我的项目是 postcss7),那么 JIT 模式间接带你腾飞。

  • 超快的构建速度。
  • 反对变体,你甚至能够这么写 sm:hover:active:disabled:opacity-75
  • 反对任意款式,例如 md:top-[-113px]
  • 开发和生产环境后果是统一的,(我在 vue2 我的项目中就遇到过组件库构建后果不统一的问题)。

如果你应用 vscode 那你肯定要装置 Tailwind CSS IntelliSense 插件,它能够主动补全类名,显著升高学习老本。

8.3 对于打包体积

应用默认配置,未压缩是 3739.4kB,Gzip 压缩 是 293.9kB,Brotli 压缩 是 73.2kB。这仿佛看起来很大,这是因为 tailwind 提供了成千上万的性能类,其中绝大部分你不会应用到。

当构建生产时,你应该应用 purge 选项来 tree-shake 优化未应用的款式,并优化您的最终构建大小当应用 Tailwind 删除未应用的款式时,很难最终失去超过 10kb 的压缩 CSS。

还有一点,Atom CSS 极大的晋升了款式的复用水平,从而间接升高了构建体积。

9.vuex 代替计划 pinia

因为 vuex 4 对 typescript 的反对让人感到惆怅,所以状态治理弃用了 vuex 而采取了 pinia。

遗记在哪看到,尤大如同说 pinia 可能会代替 vuex,所以请放心使用。

9.1 为什么采纳 Pinia ?

  • Pinia 的 API 设计十分靠近 Vuex 5 的提案。(作者是 Vue 外围团队成员)
  • 无需像 Vuex 4 自定义简单的类型来反对 typescript,天生具备完满的类型推断。
  • 模块化设计,你引入的每一个 store 在打包时都能够主动拆分他们。
  • 无嵌套构造,但你能够在任意的 store 之间穿插组合应用。
  • PiniaVue devtools 挂钩,不会影响 Vue 3 开发体验。

上面简略的介绍一下如何应用 Pinia,并比照 vuex 有哪些区别与注意事项,具体请参考官网文档。

9.2 创立 Store

Pinia 曾经内置在脚手架中,并且与 vue 曾经做好了关联,你能够在任何地位创立一个 store:

import {defineStore} from 'pinia'

export const useUserStore = defineStore({
  id: 'user',
  state: () =>({}),
  getters: {},
  actions: {}})

这与 Vuex 有很大不同,它是规范的 Javascript 模块导出,这种形式也让开发人员和你的 IDE 更加分明 store 来自哪里。

Pinia 与 Vuex 的区别:

  • id 是必要的,它将所应用 store 连贯到 devtools。
  • 创立形式:new Vuex.Store(...)(vuex3),createStore(...)(vuex4)。
  • 比照于 vuex3,state 当初是一个 函数返回对象
  • 没有 mutations,不必放心,state 的变动仍然记录在 devtools 中。

9.3 State

创立好 store 之后,能够在 state 中创立一些属性了:

state: () => ({ name: 'codexu', age: 18})

将 store 中的 state 属性设置为一个函数,该函数返回一个蕴含不同状态值的对象,这与咱们在组件中定义数据的形式十分类似。

在模板中应用 store:

当初咱们想从 store 中获取到 name 的状态,咱们只须要应用以下的形式即可:

<h1>{{userStore.name}}</h1>

const userStore = useUserStore()
return {userStore}

留神这里并不需要 userStore.state.name

尽管下面的写法很舒服,然而你肯定不要用解构的形式去提取它外部的值,这样做的话,会失去它的响应式:

const {name, email} = useUserStore()

9.4 Getters

Pinia 中的 getter 与 Vuex 中的 getter、组件中的计算属性具备雷同的性能,传统的函数申明应用 this 代替了 state 的传参办法,但箭头函数还是要应用函数的第一个参数来获取 state,因为箭头函数解决 this 的作用范畴:

getters: {nameLength() {return this.name.length},
  nameLength: state => state.name.length,
  nameLength: ()=> this.name.length ❌}

9.5 Actions

这里与 Vuex 有极大的不同,Pinia 仅提供了一种办法来定义如何更改状态的规定,放弃 mutations 只依附 Actions,这是一项重大的扭转。

Pinia 让 Actions 更加的灵便:

  • 能够通过 组件 或其余 action 调用
  • 能够从 其余 store 的 action 中调用
  • 间接在商店实例上调用
  • 反对 同步 异步
  • 有任意数量的参数
  • 能够蕴含无关如何更改状态的逻辑(也就是 vuex 的 mutations 的作用)
  • 能够 $patch 办法间接更改状态属性
actions: {async insertPost(data){await doAjaxRequest(data);
    this.name = '...';
  }
}

9.6 Devtools

脚手架已内置上面的代码,这将增加 devtools 反对:

import {createPinia, PiniaPlugin} from 'pinia'

Vue.use(PiniaPlugin)
const pinia = createPinia()

工夫旅行性能貌似曾经能够应用了,这块后续会关注。

10. 基于 mitt 解决组件间事件联动

如果你已经是 Vue2.x 的开发者,那么请浏览上面援用官网文档的一段话:

咱们从实例中齐全移除了 $on$off$once 办法。$emit 依然蕴含于现有的 API 中,因为它用于触发由父组件申明式增加的事件处理函数。

在 Vue 3 中,曾经不可能应用这些 API 从组件外部监听组件本人收回的事件了,该用例暂没有迁徙的办法。然而该 eventHub 模式能够被替换为实现了事件触发器接口的内部库,例如 mitttiny-emitter

10.1 为什么抉择 mitt?

  • 足够小,仅有 200bytes。
  • 反对全副事件的监听和批量移除。
  • 无依赖,不论是什么框架都能够间接应用。

10.2 重大正告

咱们曾经无奈在我的项目中应用 eventBus,仅举荐你在 非凡场合 下应用 mitt,它并不是开发的常态,你肯定要确保晓得本人在做什么?否则你的我的项目将难以保护!!!

10.3 如何应用 mitt?

在应用 mitt 前倡议请浏览官网文档:

脚手架默认提供一个能够间接应用的对象:

import emitter from '@/libs/emitter';

当然你也能够引入曾经装置好的 mitt:

import mitt from 'mitt'

const emitter = mitt()

mitt 提供了非常简单的 API,上面代码是官网演示:

// listen to an event
emitter.on('foo', e => console.log('foo', e) )

// listen to all events
emitter.on('*', (type, e) => console.log(type, e) )

// fire an event
emitter.emit('foo', { a: 'b'})

// clearing all events
emitter.all.clear()

// working with handler references:
function onFoo() {}
emitter.on('foo', onFoo)   // listen
emitter.off('foo', onFoo)  // unlisten

11. 异步申请

绝大多数我的项目想必逃脱不了接口的对接,如果你的我的项目存在大量的接口,我倡议做到以下几点:

  • 封装申请。
  • 对立的 API 接口治理。
  • Mock 数据性能(依据需要斟酌应用)。

上述的次要目标就是在帮忙咱们简化代码和利于前期的更新保护。

11.1 基于 axios 的封装

置信开发过 vue2 我的项目的同学曾经对 axios 十分相熟的,在这里提供一些封装的思路:

  • 通过 import.meta.env.VITE_APP_BASE_URL 获取环境变量,配置 baseURL,如果接口存在多个不同域名,能够通过 js 变量管制。
  • 设置 timeout 申请超时、断网状况解决。
  • 设置 申请头,携带 token
  • 异样拦挡解决,后端通过你携带的 token 判断你是否过期,如果返回 401 你可能须要跳转到登录页面,并提醒须要从新登录。
  • 响应拦挡,通常后端返回 code、data、msg,如果是申请失常,咱们能够间接返回 data 数据,如果是异样的 code,咱们也能够在这里间接弹出报错提醒。
  • 无感刷新 token,如果你的 token 过期,能够通过后端返回的 refreshToken 调用刷新接口,获取新的 token。当然这里波及到很多细节,例如终端申请、从新发送申请、从新申请列队。
  • 中断请求,例如页面切换时,咱们要中断正在产生的申请。

相干代码(仅供参考)

11.2 为 axios 减少泛型的反对

到目前为止,axios 申请返回的类型是 any,这时咱们对申请后的数据进行操作时,没有享受到 ts 带来的类型提醒,这显然不合乎咱们的预期。

这时咱们要做的就是从新申明 axios 模块:新建一个 shims.d.ts,而后在调用时加上泛型。

import {AxiosRequestConfig} from 'axios';

declare module 'axios' {
  export interface AxiosInstance {<T = any>(config: AxiosRequestConfig): Promise<T>;
    request<T = any>(config: AxiosRequestConfig): Promise<T>;
    get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
    delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
    head<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
    post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
    put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
    patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
  }
}

做好这一步后,你就必须在创立接口时,申明申请相应数据的类型。

11.3 封装更不便的 useRequest

构想一下,编写申请代码时,咱们通常会定义这么几个变量:

  • data: 贮存申请数据
  • loading: 申请加载状态

尤其是 loading,咱们须要在申请前设置为 true,申请完结后设置为 false。

下面的封装形式,是对根底的性能封装,因为咱们在应用 vue3,所以能够进行再一次的封装成为 hook,咱们应用起来会更加不便。

例如上面这个样子:

应用 useRequest 定义一个接口:

export default getUserInfo(id) {
  return useRequest({
    method: 'get',
    url: '/api/user',
    params: {id}
  })
}

应用此接口:

const {data, loading} = getUserInfo();

留神这里的 data 是响应式的。

这是我想到的一种思路,目前还没有做很好的封装,相干代码仅供参考,你也能够借鉴一些成熟计划,比方 vueuse 中的 useFetch,然而他是基于 Fetch API 设计的,并不合乎我的预期要求,有更好的计划请大家在上面留言。

11.4 对立的 API 接口治理

自从前端和后端分家之后,前后端接口对接就成为了常态,而对接接口的过程就离不开接口文档,比拟支流就是 Swagger,然而如何在前端我的项目中更好的去治理跟后端对接的接口呢?

在 src 目录中 创立 api 目录,外部目录应依照后端制订的模块创立。

每个模块中创立多个 ts 文件,一个接口应对应一个 ts 文件,其中蕴含了以下内容:

  • 申请 参数 的类型申明。
  • 响应 数据 的类型申明。
  • 返回定义好的申请函数(url、method、params、data 等)。

对立去定义和治理 API 接口,只有后端标准的命名和你认真的写好类型申明,对前端来说 typescript 就是最好的接口文档。

11.5 mock

vite 应用 mock 数据非常简单,你能够应用 vite-plugin-mock 插件,如果你理解 mockjs,你能够疾速上手。

12. 路由

路由和菜单是组织起一个利用的要害骨架。

12.1 创立路由三部曲

通常一个我的项目须要做到这几步:

  • 应用 createRouter 创立路由,这时候依据需要抉择 Hash 路由或者 History 路由。
  • 依据业务需要 配置路由 ,留神这里很可能就用到前文提到过的 布局组件
  • 如果有权限相干的业务,你须要创立 permission.ts 在路由钩子触发时做一些事件。

如果你的页面比拟多,倡议你创立 routes 目录,分模块申明路由。

参考代码

12.2 应用 meta 丰盛你的路由

vue-router4.x 反对 typescript,配置路由的类型是 RouteRecordRaw,这里 meta 能够让咱们有更多的施展空间,这里提供一些参考:

  • title: string; 页面题目,通常必选。
  • icon?: string; 图标,个别配合菜单应用。
  • auth?: boolean; 是否须要登录权限。
  • ignoreAuth?: boolean; 是否疏忽权限。
  • roles?: RoleEnum[]; 能够拜访的角色
  • keepAlive?: boolean; 是否开启页面缓存
  • hideMenu?: boolean; 有些路由咱们并不想在菜单中显示,比方某些编辑页面。
  • order?: number; 菜单排序。
  • frameUrl?: string; 嵌套外链。

这里只提供一些思路,每个我的项目多多少少会波及到这些问题,具体如何实现请查阅材料自行解决。

13. 我的项目性能与细节优化

13.1 开启 gzip

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

应用 vite-plugin-compression 能够 gzipbrotli 的形式来压缩资源,这一步须要服务器端的配合,vite 只能帮你打包出 .gz 文件。此插件应用简略,你甚至无需配置参数,引入即可。

13.2 页面载入进度条

页面路由切换时,附带一个加载进度条会显得十分敌对,不至于白屏工夫过长,让用户认为页面假死。

这时候咱们能够用到 nprogress,在路由切换时开启和敞开:

import NProgress from 'nprogress';

router.beforeEach(async (to, from, next) => {NProgress.start();
});

router.afterEach((to) => {NProgress.done();
});

13.3 Title

在不同的路由下显示不同的题目是惯例的操作,咱们能够通过路由钩子获取 meta 中的 title 属性扭转标签页上的 title。

你能够应用 vueuse 提供的 useTitle,或者 window.document.title 自行封装。

你也能够通过环境变量将你的主题目拼接在路由题目的前面:

const {VITE_APP_TITLE} = import.meta.env;

13.4 解决挪动端应用 vh 的问题

有趣味的同学能够尝试一下 chrome 挪动端浏览器上的 100vh,是真正的视口高度的 100% 嘛。

为了解决这一问题,咱们能够通过 postCss 插件解决。

装置 postcss-viewport-height-correction 插件:

npm install -D postcss-viewport-height-correction

在 postcss.config.js 中减少 plugin:

module.exports = {
  plugins: {'postcss-viewport-height-correction': {},
  },
}

增加这一段 js 代码在全局,你能够间接增加在 index.html 上即可:

const customViewportCorrectionVariable = 'vh';
function setViewportProperty(doc) {
  let prevClientHeight;
  const customVar = `--${customViewportCorrectionVariable || 'vh'}`;
  function handleResize() {const { clientHeight} = doc;
    if (clientHeight === prevClientHeight) return;
    requestAnimationFrame(function updateViewportHeight() {doc.style.setProperty(customVar, `${clientHeight * 0.01}px`);
      prevClientHeight = clientHeight;
    });
  }
  handleResize();
  return handleResize;
}
window.addEventListener('resize', setViewportProperty(document.documentElement));

13.5 能够常驻的 JavaScript 库

  • 前文提到过的 vueuse,十分弱小,强烈建议尝试。
  • lodash,用了都说好,早用早上班。

14. 代码格调与流程标准

14.1 ESLint

不论是多人单干还是集体我的项目,代码标准都是很重要的。这样做不仅能够很大水平地防止根本语法错误,也保障了代码的可读性。

这里举荐应用 airbnb 标准。

配置参考

14.2 StyleLint

只管前文提到过 tailwind,能够让你简直不写 css,然而波及到团队合作,这一点也要谨严。

StyleLint 是一个弱小的、现代化的 CSS 检测工具, 与 ESLint 相似, 是通过定义一系列的编码格调规定帮忙咱们防止在样式表中呈现谬误,配合编辑器的主动修复,能够很好的对立团队我的项目 css 格调。

配置参考

14.3 代码提交标准

在多人合作的背景下,git 仓库和 workflow 的作用很重要。而对于 commit 提交的信息阐明存在肯定标准,现应用 commitlint + husky 标准 git commit -m “” 中的形容信息。咱们都晓得,在应用 git commit 时,git 会提醒咱们填入此次提交的信息。可不要小看了这些 commit,团队中标准了 commit 能够更清晰的查看每一次代码提交记录,还能够依据自定义的规定,主动生成 changeLog 文件。

提交格局(留神冒号前面有空格):

<type>[optional scope]: <description>
  • type:用于表明咱们这次提交的改变类型。
  • optional scope:可选,用于标识此次提交次要波及到代码中哪个模块。
  • description:一句话形容此次提交的次要内容,做到长篇累牍。

Type 类型

  • build:编译相干的批改,例如公布版本、对我的项目构建或者依赖的改变
  • chore:其余批改, 比方扭转构建流程、或者减少依赖库、工具等
  • ci:继续集成批改
  • docs:文档批改
  • feat:新个性、新性能
  • fix:批改 bug
  • perf:优化相干,比方晋升性能、体验
  • refactor:代码重构
  • revert:回滚到上一个版本
  • style:代码格局批改, 留神不是 css 批改
  • test:测试用例批改

对于 commitlint + husky 的配置文章有很多,大同小异,请依据本人的理论状况配置。

15. 编写应用文档

做到这一步,你的整个脚手架开发曾经靠近于序幕,然而你做了这么多,你的共事并不知道如何应用,甚至你过一段时间也会遗记,所以你必须养成良好的编写文档习惯。

15.1 应用 vitepress 搭建文档

这里我举荐应用 vuepress 或者 vitepress,说实话你只写文档 vitepress 会让你更难受,因为它很快。

vitepress 很适宜构建博客网站、技术文档,就是因为它能够间接用 markdown 进行书写,所有写过博客的人,都应该对它不生疏。一个 .md 文件,即可生成一张页面,非常不便。

创立一个 vitepress 文档切实是太过于简略,你能够参考官网文档,或者参考我的文档。

15.2 文档部署

如果你的团队能够帮忙你搭建 CI/CD 主动部署是再好不过了,如果没有这个条件,你也能够通过 github 提供的 actions 性能,实现主动部署。

代码参考

16. 插件

如果你想更畅快的用上述性能,倡议你装置上面的插件。

16.1 VSCode 插件

  • Vue Language Features (Volar),你当初查 Volar 可能找不到,你须要的是这个。
  • Vue 3 Snippets,vue3 快捷输出。
  • Tailwind CSS IntelliSense,tailwind 代码提醒。
  • Stylelint
  • Prettier – Code formatter
  • ESLint

16.2 Chrome 插件

  • Vue.js devtools,你当然要装置反对 vue3 的版本,而且此版本对 pinia 反对的也十分敌对。

源码

上述内容,均可在我的开源我的项目 X-BUILD 中找到相干源码,如果能够帮到你,请给一颗 star 或点赞激励我奉献出更多的开源我的项目或文章。

参考

  • 《基于 Vue 的前端架构,我做了这 15 点》
  • 《搭建本人的脚手架—“优雅”生成前端工程》
  • 《Vuex4 对 TypeScript 并不敌对,所以我抉择 Pinia》
  • 《前端脚手架 webpack 迁徙 Vite2 踩坑实际》

正文完
 0