前端架构思考,Vue or React?领域设计、文件结构、数据管理、主题替换

从 Vue 和 React 看问题

Vue 的优势
  1. 内置的 vite 构建工具,减少构建时间,提高开发效率,在大工程上特别明显

  2. 结构、样式、功能分开的设计,再通过 setup 做 crud 的分隔,整个页面维度的逻辑会特别清晰

  3. 在路由和数据管理上有官方的解决方案,可以完全没有选择的压力🍐

  4. 框架本身做了很多的性能优化,如下👇

    1. 静态提升,包括静态节点,静态属性

    2. 预字符串化,20 个静态节点以上

    3. 缓存事件处理函数

    4. block tree

    5. patch flag

React 的优势
  1. 整体采用函数式编程思想以及数据不可变的渲染方式,减少魔法,没有 vue 指令的花里胡哨,相对更加好理解

  2. 用 jsx 的语法,减少了 html 本身来带的一些困扰,更加灵活性,可操控性强,更容易出更个性化的项目

弱水三千,只取一瓢

其实写到这里,相信大家已经明白我的价值倾向了。在没有企业包袱的角度来看,大厂都是 react 为先😯, 我更加推荐使用 vue,原因如下👇

  1. 大神没那么多,就大部分场景 95%,vue 都可以很好的覆盖

  2. 天生的结构、样式、逻辑相分离,各自的职责界限已经很明显了

  3. 因为魔法,所以开发更简单。vue 将很多业务常见的场景(嵌套路由、受保护的页面、导航守卫、路由切换动画、滚动条复位)都在 vuex 和 router 中实现了,开箱即用

Why

主要是为了避免出现以下这些问题👇

  1. 一个文件千八百行,太长了,需要不断的上下滑动,还看不懂🤔

  2. 界限不明确,就会导致混乱,dom 里面写逻辑,逻辑里透出 dom

  3. 都是页面的维度,没有领域的概念,缺少一个整体的认知

最佳实践:每个页面不超过 7 个属性和方法,不强求

How

领域设计

用面向对象的思维来思考整个项目。可以从一个产品中找到它所涵盖的一些抽象概念,并对每个概念进行赋能。例子🌰:类似协议、商品、计划、达人等维度,然后再对计划进行细分为通用计划、招募计划(类型维度)等,达人模块有添加、履约详情和搜索(功能维度)等模块,落在目录结构上如下👇

└── page├── protocol ├── product├── plan ├── general └── recruit └── talent ├── add ├── fulfillment └── search

计划为栗子🌰

类型维度

└── plan├── general // 通用├── create // 创建├── modify // 修改├── detail // 详情└── info // 信息├── recruit // 招募└── create // 创建├── components // 业务组件└── views // 视图├── Main // 业务组件├── Video // 短视频├── Live // 直播├── index.module.scss // 页面样式└── index.tsx // 引入 main,提供上下文├── oriented // 定向├── free_visit // 自由└── detail // 详情├── components // 业务组件└── views // 视图├── Loading // 加载态├── Error // 错误态├── Retry // 重试├── Normal // 正常├── index.module.scss // 页面样式└── index.tsx // 引入 main,提供上下文└── coop_visit // 合作

在划分类型后,再对每个计划做能力层级的划分,可以是 create、modify、detail、info 等模块,适用于每个计划有较大的差异性,可复用的模块不太多的情况

在更复杂化的场景中,例如 recruit_plan 的 create 有 live 和 video 2种模式,差异化不大,可以在同一个页面中组装。可以用 main 承担 controller 层的功能,做模式的划分。同样例如页面的加载,错误,重试,正常等各个状态也同样可以在 main 做统一的处理

功能维度

└── plan├── create // 创建├── components // 业务组件├── models // 数据处理,逻辑层├── utils // 工具函数├── hooks // 自定义钩子🪝├── constants // 常量、enum├── typings // 类型 interface、type└── views // 对 components 做组装 ├── Main // controller,做模式的适配和分发├── CreateGeneral // 具体的业务页面├── CreateRecruit // 具体的业务页面├── CreateOriented // 具体的业务页面├── CreateFreeVisit // 具体的业务页面├── CreateCoopVisit // 具体的业务页面├── index.module.scss // 页面样式└── index.tsx // 引入 main,提供上下文├── modify // 修改├── detail // 详情├── info // 信息└── list // 列表

在功能划分后,再对每个计划进行赋能,可以是通用计划、招募计划、定向计划、自由探店、合作探店等模块,适用于功能大体类似的应用,可复用的组件、工具函数比较多的场景

当然,也可以每种计划类型都是单独的一个文件夹,只是全部聚合在 detail 这个域中而已

整体的一个原则是,跟着页面维度来走,页面文件夹📁映射路由,每个页面有自己的数据、权限等等其他的业务逻辑

/plan/general/create ---> 找到的就是 plan 域下, general 类型的 create 能力

顺便提一嘴,命名规范相关的

命名文件名变量名常量名css名组件名/文件夹
camelCase
PascaCase
snake_case
kebab-case

整体系统的框架体系

├── src ├── assets ├── biz_components ├── components ├── core ├── apis ├── constants ├── hooks ├── typings └── styles├── layout ├── pages├── app.module.scss ├── app.tsx ├── index.html ├── index.tsx └──router.ts
├── .gitignore 
├── build.sh 
├── jest.config.js 
├── pack.json 
├── README.md 
└── tsconfig.json

所有域的划分都是基于页面维度 pages 进行的,领域分层的方式也正如上面👆所谈到的那 2 种方式。在这种结构中,对几种 components 做下解读

和 pages 同级的 components,这 2 种类型,都是领域的原子能力,他们的数据来源绝对的纯粹,就是从 props 中取

  1. biz_components: 复用比较多的业务组件

  2. components: 纯粹的组件,不含一丝一毫的业务逻辑

每个页面下的 components,到了这个类别下,已经是圈定了指定的页面,所以除了 props,还可以有 model,甚至是页面级的 model 数据,至于数据的处理方案,请向下细读

数据管理

整体使用的是 context 的一个方案,包裹在最外层,在里层去消费数据

用到了一个三方库 unstated-next[1]

用法很简单,demo

// page.tsimport { createContainer } from 'unstated-next';const useContainer = () => {...return {state: {...}...}
}export const xxxModel = createContainer(useContainer);// 在页面的 index.tsx 中export default () => {<xxxModel.Provider><Main /></xxModel.Provider>
}// 在 views 或者 components 中消费export default () => {const {} = xxxModel.useContainer();}

这一套的思维逻辑和实现,是基于一定的业务场景,不一定全部适合哈。总的来说,通过这种代码的组织方式,让 ui 层和逻辑层出现了比较分明的界限,明确了各自的职责,让维护的成本更加低了。至于逻辑层的抽方法、抽 hook;ui 层的减少 dom 元素、语意化、seo 等其他的优化,需要在一定的场景下进行讨论,这里就不涉及了哈。

相比较 redux 来说,unstated-next 的 size 更小,使用起来更简单

相比较 context 来说,它本身就还是 hook,封装在自定义 hook,或者其他地方,都不是一种很好的实现 ui 和逻辑分离的方式

提供下 localStorage 的最佳用法,拒绝花里胡哨,只为解决问题

// 从 localStorage 中获取数据
export const getLocalStorage = (key: string) => JSON.parse(localStorage.getItem(key) || '{}')?.data;// 把值存在 localStorage,格式 { data: any }
export const setLocalStorage = (key: string, data: any) => {localStorage.setItem(key, JSON.stringify({ data }));
};想想 data 的妙用,哈哈哈😄

推荐 2 个处理数据 filter 的库,无样式侵入只接管状态

  1. rc-field-form[2] // 推荐

  2. formily

rc 还可以用来做反馈组件,很好用的,antd 的表单也是基于此封装的哈

单元测试覆盖

单测的写法,使用 jest + testing-library + mm 来进行 mock 以及断言

最好可以在 CI/CD 上配置增量的代码覆盖率是要求在多少,每个 mr 都不能拉低单测覆盖率(待学习)

需要注意的一些点

  1. describe 的描述可以统一下

  2. it 和 test 也可以统一下😒

  3. 通过 snapshot 来进行 ui 的校对

  4. 在每个 test 中,用户的行为操作是基于人的视角,而不是机器的视角

    await userEvent.click(btn as Elment) ✅ // @testing-library/user-event

    screen.querySelector('.btn').click ❌

强迫症患者最后的挣扎😄,毕竟代码是给人看的,机器顺便运行运行

test('render', () => {const { asFragment } = render(<XXX />);expect(asFragment()).toMatchSnapshot();
})

推荐一些学习单测的网站

jest: zh-hans.reactjs.org/docs/testin…[3]

testing-library:

  1. rualc.com/frontend/te…[4]

  2. rualc.com/frontend/te…[5]

  3. testing-library.com/docs/[6]

  4. juejin.cn/post/690705…[7]

  5. blog.mimacom.com/react-testi…[8]

  6. github.com/testing-lib…[9]

  7. testing-library.com/docs/ecosys…[10]

  8. www.w3cschool.cn/doc\_jest/je…[11]

  9. juejin.cn/post/709218…[12]

mm: www.npmjs.com/package/mm[13]

如果在组件维度去写单测需要去 mock 和页面一样多的数据时,我们应该考虑单测的覆盖维度就是页面级别的

个人喜好:test 跟着 components 或者 views,这种方式比放在最外层会好很多!

浅谈其他

主题替换

设计产品总是会有很多其他的 idea,特别在视觉上,所以视觉改版是 FE 很痛苦的一件事。纯粹的手动替换,傻傻的。所以我们在开发时,如果可以有一个主题包如果可以的组件库相结合是最好的,类似 antd 和 elmentui 一样,在需要更换主题的时候,升级包版本就欧了

其他方式

  1. 利用媒体查询,在 media_type 里去做xxx

  2. 利用 css next 的变量模式

总的来说,基于 css 变量,推荐一篇文章 关于前端主题切换的思考和现代前端样式的解决方案落地[14]

Icon管理

常见的几种方式

  1. 雪碧图 // 没条件的情况下

  2. iconfont

  3. png、svg // 最好只用一种,不强求

  4. 生成一个 icon 包,所有的小图标做统一的管理 // 有条件的话,成本比较大,有管理的成本💰

生成二维码

推荐使用库 qrcode.react[15]

在 svg 图片格式下,当成组件来用。通过 backgroud 的 z-index:0 和 info 的 z-index:1 来处理背景图的问题,简单好用

  • 参考资料

[1]

https://www.npmjs.com/package/unstated-next: https://link.juejin.cn/?target=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Funstated-next

[2]

https://www.npmjs.com/package/rc-field-form: https://link.juejin.cn/?target=https%3A%2F%2F

www.npmjs.com%2Fpackage%2Frc-field-form

[3]

https://zh-hans.reactjs.org/docs/testing-recipes.html: https://link.juejin.cn/?target=https%3A%2F%2Fzh-hans.reactjs.org%2Fdocs%2Ftesting-recipes.html

[4]

https://rualc.com/frontend/testing-library/#he-react-yi-qi: https://link.juejin.cn/?target=https%3A%2F%2Frualc.com%2Ffrontend%2Ftesting-library%2F%23he-react-yi-qi

[5]

https://rualc.com/frontend/testing-library/#package: https://link.juejin.cn/?target=https%3A%2F%2Frualc.com%2Ffrontend%2Ftesting-library%2F%23package

[6]

https://testing-library.com/docs/: https://link.juejin.cn/?target=https%3A%2F%2Ftesting-library.com%2Fdocs%2F

[7]

https://juejin.cn/post/6907052045262389255#heading-20: https://juejin.cn/post/6907052045262389255#heading-20

[8]

https://blog.mimacom.com/react-testing-library-fireevent-vs-userevent/: https://link.juejin.cn/?target=https%3A%2F%2Fblog.mimacom.com%2Freact-testing-library-fireevent-vs-userevent%2F

[9]

https://github.com/testing-library/user-event: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Ftesting-library%2Fuser-event

[10]

https://testing-library.com/docs/ecosystem-user-event/: https://link.juejin.cn/?target=https%3A%2F%2Ftesting-library.com%2Fdocs%2Fecosystem-user-event%2F

[11]

https://www.w3cschool.cn/doc_jest/jest-expect.html#tomatchregexporstring: https://link.juejin.cn/?target=https%3A%2F%2Fwww.w3cschool.cn%2Fdoc_jest%2Fjest-expect.html%23tomatchregexporstring

[12]

https://juejin.cn/post/7092188990471667749: https://juejin.cn/post/7092188990471667749

[13]

https://www.npmjs.com/package/mm: https://link.juejin.cn/?target=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fmm

[14]

https://mp.weixin.qq.com/s/0xTZcE3MPezRl3LILR8a_w: https://link.juejin.cn/?target=https%3A%2F%2Fmp.weixin.qq.com%2Fs%2F0xTZcE3MPezRl3LILR8a_w

[15]

https://www.npmjs.com/package/qrcode.react: https://link.juejin.cn/?target=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fqrcode.react

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/107721.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ppt怎么压缩到10m以内?分享ppt缩小方法

在日常工作中&#xff0c;我们常常需要制作和分享PowerPoint演示文稿&#xff0c;然而&#xff0c;有时候文稿中的图片、视频等元素会导致文件过大&#xff0c;无法在电子邮件或其他平台上顺利传输。为了将PPT文件压缩到10M以内&#xff0c;我们可以使用一些专门的文件压缩工具…

性能超越 Clickhouse | 物联网场景中的毫秒级查询案例

1 物联网应用场景简介 物联网&#xff08;Internet of Things&#xff0c;简称 IoT&#xff09;是指通过各种信息传感、通信和 IT 技术来实时连接、采集、监管海量的传感设备&#xff0c;从而实现对现实世界的精确感知和快速响应&#xff0c;继而实现自动化、智能化管理。在查…

Npm——yalc本地库调试工具

全局安装 npm i -g yalc本地库发布 yalc publish项目中安装 yalc add 库名本地库更新后推送 yalc push项目中删除库 yalc remove --all

Unity_热更方案

热更是指在游戏已经发布和运行后&#xff0c;仍然能够更新游戏内容、修复错误或添加新功能 具体的来说有几种方法可以实现&#xff1a; 1&#xff1a;UnityWebRequest 和 AssetBundle 这种方法比较基础&#xff0c;但对于一些小型项目或原型来说&#xff0c;是一种有效的热更…

2-k8s-控制器介绍

文章目录 一、控制器类型二、Deployment控制器三、SatefulSet控制器四、Daemonset控制器五、Job控制器六、CronJob 控制器 一、控制器类型 Deployment&#xff1a;适合无状态的服务部署StatefullSet&#xff1a;适合有状态的服务部署DaemonSet&#xff1a;一次部署&#xff0c…

时间复杂度为 O(n^2) 的排序算法

大家好&#xff0c;我是 方圆。对于小规模数据&#xff0c;我们可以选用时间复杂度为 O(n2) 的排序算法&#xff0c;因为时间复杂度并不代表实际代码的执行时间&#xff0c;而且它也省去了低阶、系数和常数&#xff0c;仅代表的增长趋势&#xff0c;所以在小规模数据情况下&…

python:使用卷积神经网络(CNN)进行回归预测

作者:CSDN @ _养乐多_ 本文详细记录了从Excel或者csv中读取用于训练卷积神经网络(CNN)模型的数据,包括多个自变量和1个因变量数据,以供卷积神经网络模型的训练。随后,我们将测试数据集应用于该CNN模型,进行回归预测和分析。 该代码进一步修改可用于遥感影像回归模型. …

14 幂等生产者和事务生产者

kafka消息交付可靠性保障和精确一次语义处理 消息交付可靠性保障&#xff0c;指的kafka对Producer和Consumer要处理的消息提供什么样的承诺。总共就三种&#xff1a;at most once 、at least once、axactly once kafka默认提供的是 at least once。原因是只有Broker提交消息并…

【广州华锐互动】人体血管器官3D动态展示为医学生提供哪些便利?

人体血管器官3D动态展示是一种采用先进的计算机图形技术和立体成像技术&#xff0c;对人体内部结构和功能进行三维可视化的教学方法。这种教学方式以其独特的优势&#xff0c;正在改变传统的解剖学教学模式&#xff0c;为医学教育带来了革新。 首先&#xff0c;3D动态演示能够提…

基于nodejs+vue网课学习平台

各功能简要描述如下: 1个人信息管理:包括对学生用户、老师和管理员的信息进行录入、修改&#xff0c;以及老师信息的审核等 2在库课程查询:用于学生用户查询相关课程的功能 3在库老师查询:用于学生用户查询相关老师教学的所有课程的功能。 4在库学校查询:用于学生用户查询相关学…

解密JavaScript的异步机制:打破单线程限制,提升性能与用户体验

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 一、JavaScript的异步编步机制 二、事件循环…

剑指offer(C++)-JZ50:第一个只出现一次的字符(算法-其他)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 题目描述&#xff1a; 在一个长为 字符串中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1&#xff08;需要区分…

SpringBoot面试题7:SpringBoot支持什么前端模板?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:SpringBoot支持什么前端模板? Spring Boot支持多种前端模板,其中包括以下几种常用的: Thymeleaf:Thymeleaf是一种服务器端Java模板引擎,能够…

SSM - Springboot - MyBatis-Plus 全栈体系(三十)

第七章 MyBatis-Plus MyBatis-Plus 高级用法&#xff1a;最优化持久层开发 一、MyBatis-Plus 快速入门 1. 简介 版本&#xff1a;3.5.3.1MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window) 的增强工具&#xff0c;在 MyBa…

上海亚商投顾:沪指震荡调整跌 减肥药、华为概念股持续活跃

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 沪指上个交易日低开后震荡调整&#xff0c;深成指、创业板指盘中跌超1%&#xff0c;宁德时代一度跌超3%&#xff…

高效工具类软件使用

高效工具类软件使用 目录概述需求&#xff1a; 设计思路实现思路分析1.Leanote2.Obsidian 的使用 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for…

面向红队的自动化引擎工具

gogo 介绍 面向红队的、高度可控的可拓展的自动化引擎。特征如下&#xff1a; 自由的端口配置 支持主动/主动指纹识别 关键信息提取&#xff0c;如标题、证书以及自定义提取信息的正则 支持nuclei poc&#xff0c;poc目录&#xff1a;https://chainreactors.github.io/wiki/…

阿里云云服务器实例使用教学

目录 云服务器免费试用 详细步骤 Xshell 远程连接 云服务器免费试用 阿里云云服务器网址&#xff1a;阿里云免费试用 - 阿里云 详细步骤 访问阿里云免费试用。单击页面右上方的登录/注册按钮&#xff0c;并根据页面提示完成账号登录&#xff08;已有阿里云账号&#xff09;…

torch.cat 使用小节

torch.cat 使用小节 torch.cat 要求在所指定拼接维度之外的所有维度都要匹配&#xff0c;例如 import torch v1 torch.tensor([[1, 2, 3], [4, 5, 6], [4, 5, 6]]) # 3*3 v2 torch.tensor([[3, 6, 8]]) # 1*3 torch.cat([v1, v2], dim1)运行之后会报错 Sizes of tensors…

【网络安全 --- win10系统安装】win10 系统详细安装过程(提供资源)

一&#xff0c;资源下载 百度网盘镜像下载地址链接&#xff1a; 百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固&#xff0c;支持教育网加速&#xff0c;支持手机端。注册使用百度网盘即可享受免费存储空间https://pan.ba…