最新从 16 个方向逐步搭建基于 Vue3 的前端架构,一线互联网移动架构师设计思想解读开源框架

结尾

学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

html5

静态资源组件

静态资源主要有图片、音频和视频三种常见的形式。

  • 通过 src 写入相对的路径,使用上述的函数来补全完整的路径,即可在不同的环境下使用不同地址的静态资源。

  • 通过 type 传入图片、音频和视频的类型。

  • autoplay 是解决以视频为背景的情况下,视频无法自动播放的问题。

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 组件:

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

  • name 放置在 @/assets/icons 文件夹下的文件名。

  • color 颜色填充,使用此项会默认覆盖图标颜色。

5.按需自动引入组件


unplugin-vue-components[10] 是一款非常强大的插件(极力推荐),核心功能就是帮助你自动按需引入组件,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() 注册全局组件。使用此插件后,无需手写注册,直接在模板中使用组件即可:

这里引入官方的示例:

自动编译为:

5.3 自动引入组件库

在使用组件库时,常规组件我们也会注册到全局,如果使用局部注册由于页面中会使用到多个组件,会非常麻烦,所以这个功能绝佳,例如我们使用 ant-design-vue 组件库。

直接在模板中使用即可,无需手动注册或局部引用:

按钮

当然,你还需要在 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[11]

  • Element Plus[12]

  • Element UI[13]

  • Headless UI[14]

  • IDux[15]

  • Naive UI[16]

  • Prime Vue[17]

  • Vant[18]

  • VEUI[19]

  • Varlet UI[20]

  • View UI[21]

  • Vuetify[22]

  • VueUse Components[23]

  • Quasar[24]

6.样式


项目中最好使用通用样式,可以创建 src/styles 目录存放,这里推荐一些分类:

styles

├── antd # 组件库样式覆盖,命名自取,这里以 ant design 为例

├── color.less # 颜色

├── index.less # 入口

├── global.less # 公共类

├── transition.less # 动画相关

└── variable.less # 变量

6.1 预设基础样式

相信用过 normalize[25] 的同学不在少数,它可以重置 css 样式,使各浏览器效果保持一致。后面的章节会提到 tailwind.css,它内置了预设样式重置的功能,与 normalize 还是有一定的区别,有兴趣的同学可以了解一下[26]。

6.2 CSS 预处理器

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

npm install -D less

如何选择预处理器?

推荐使用你是所使用的组件库的样式语言,因为 css 预处理器学会一种后,入手其他几乎没有学习成本。

6.3 开启 scoped

没有加 scoped 属性,会编译成全局样式,造成全局污染。

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[27] 在我第一次看到它的时候,内心是比较反感的,但实际上手之后又觉得真香。从 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[28] 插件,它可以自动补全类名,显著降低学习成本。

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[29]。

忘记在哪看到,尤大好像说 pinia[30] 可能会代替 vuex,所以请放心使用。

9.1 为什么采用 Pinia ?

  • Pinia 的 API 设计非常接近 Vuex 5 的提案[31]。(作者是 Vue 核心团队成员)

  • 无需像 Vuex 4 自定义复杂的类型来支持 typescript,天生具备完美的类型推断。

  • 模块化设计,你引入的每一个 store 在打包时都可以自动拆分他们。

  • 无嵌套结构,但你可以在任意的 store 之间交叉组合使用。

  • PiniaVue devtools 挂钩,不会影响 Vue 3 开发体验。

下面简单的介绍一下如何使用 Pinia,并对比 vuex 有哪些区别与注意事项,具体请参考官方文档[32]。

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 的状态,我们只需要使用以下的方式即可:

{{userStore.name}}

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 的开发者,那么请阅读下面引用官方文档[33]的一段话:

我们从实例中完全移除了 $on$off$once 方法。$emit 仍然包含于现有的 API 中,因为它用于触发由父组件声明式添加的事件处理函数。

在 Vue 3 中,已经不可能使用这些 API 从组件内部监听组件自己发出的事件了,该用例暂没有迁移的方法。但是该 eventHub 模式可以被替换为实现了事件触发器接口的外部库,例如 mitttiny-emitter

10.1 为什么选择 mitt ?

  • 足够小,仅有 200bytes。

  • 支持全部事件的监听和批量移除。

  • 无依赖,不论是什么框架都可以直接使用。

10.2 严重警告

我们已经无法在项目中使用 eventBus,仅推荐你在特殊场合下使用 mitt,它并不是开发的常态,你一定要确保知道自己在做什么?否则你的项目将难以维护!!!

10.3 如何使用 mitt ?

在使用 mitt 前建议请阅读官方文档[34]:

脚手架默认提供一个可以直接使用的对象:

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。当然这里涉及到很多细节,例如终端请求、重新发送请求、重新请求列队。

  • 中断请求,例如页面切换时,我们要中断正在发生的请求。

相关代码(仅供参考)[35]

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;

request<T = any>(config: AxiosRequestConfig): Promise;

get<T = any>(url: string, config?: AxiosRequestConfig): Promise;

delete<T = any>(url: string, config?: AxiosRequestConfig): Promise;

head<T = any>(url: string, config?: AxiosRequestConfig): Promise;

post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise;

put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise;

patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise;

}

}

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

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 是响应式的。

这是我想到的一种思路,目前还没有做很好的封装,相关代码仅供参考[36],你也可以借鉴一些成熟方案,比如 vueuse 中的 useFetch[37],但是他是基于 Fetch API 设计的,并不符合我的预期要求,有更好的方案请大家在下面留言。

11.4 统一的 API 接口管理

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

在 src 目录中 创建 api 目录,内部目录应按照后端制定的模块创建。

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

  • 请求参数的类型声明。

  • 响应数据的类型声明。

  • 返回定义好的请求函数(url、method、params、data 等)。

统一去定义和管理 API 接口,只要后端规范的命名和你认真的写好类型声明,对前端来说 typescript 就是最好的接口文档。

11.5 mock

vite 使用 mock 数据非常简单,你可以使用 vite-plugin-mock[38] 插件,如果你了解 mockjs,你可以快速上手。

12.路由


路由和菜单是组织起一个应用的关键骨架。

12.1 创建路由三部曲

通常一个项目需要做到这几步:

  • 使用 createRouter 创建路由,这时候根据需求选择 Hash 路由或者 History 路由。

  • 根据业务需求配置路由,注意这里很可能就用到前文提到过的布局组件

最后更多分享:前端字节跳动真题解析

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

尤其是 loading,我们需要在请求前设置为 true,请求结束后设置为 false。

上面的封装方式,是对基础的功能封装,因为我们在使用 vue3,所以可以进行再一次的封装成为 hook,我们使用起来会更加方便。

例如下面这个样子:

使用 useRequest 定义一个接口:

export default getUserInfo(id) {

return useRequest({

method: ‘get’,

url: ‘/api/user’,

params: { id }

})

}

使用此接口:

const { data, loading } = getUserInfo();

注意这里的 data 是响应式的。

这是我想到的一种思路,目前还没有做很好的封装,相关代码仅供参考[36],你也可以借鉴一些成熟方案,比如 vueuse 中的 useFetch[37],但是他是基于 Fetch API 设计的,并不符合我的预期要求,有更好的方案请大家在下面留言。

11.4 统一的 API 接口管理

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

在 src 目录中 创建 api 目录,内部目录应按照后端制定的模块创建。

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

  • 请求参数的类型声明。

  • 响应数据的类型声明。

  • 返回定义好的请求函数(url、method、params、data 等)。

统一去定义和管理 API 接口,只要后端规范的命名和你认真的写好类型声明,对前端来说 typescript 就是最好的接口文档。

11.5 mock

vite 使用 mock 数据非常简单,你可以使用 vite-plugin-mock[38] 插件,如果你了解 mockjs,你可以快速上手。

12.路由


路由和菜单是组织起一个应用的关键骨架。

12.1 创建路由三部曲

通常一个项目需要做到这几步:

  • 使用 createRouter 创建路由,这时候根据需求选择 Hash 路由或者 History 路由。

  • 根据业务需求配置路由,注意这里很可能就用到前文提到过的布局组件

最后更多分享:前端字节跳动真题解析

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • [外链图片转存中…(img-rOzMU2Xv-1715259325339)]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值