跳到主要内容

学习资料

· 阅读需 5 分钟
柏拉文
越努力,越幸运

一、JavaScript


1.1 【视频】破解JavaScript高级玩法,成为精通 JS 的原生专家

二、Vue


2.1 【书籍】深入浅出 Vue.js

2.2 【书籍】Vue.js 设计与实现

2.3 【视频】Vue.js 源码深入解析 深入理解Vue实现原理

2.4 【视频】Vue3源码解析,打造自己的Vue3框架

2.5 【视频】Vue3.0+TS打造企业级组件库 前端中高级开发者必修课

2.6 【视频】基于Vue3+Vite+TS,二次封装element-plus业务组件

2.7 【视频】Vue3+TS打造SSR网站应用,0到1实现服务端渲染

2.8 【掘金】李永宁-Vue 源码解读

2.9 【公众号】精通 Vue 技术栈源码原理

2.10 【文章】Vue.js 技术揭秘

三、React


3.1 【书籍】React 设计原理

3.2 【视频】React源码深度解析 高级前端工程师必备技能

3.3 【视频】【新升级】TS+ React18高仿AntD从零到一打造组件库

3.4 【视频】高级前端进阶必修:自主打造高扩展的业务组件库

3.5 【视频】React服务器渲染原理解析与实践 已学完

3.6 【视频】Next.js+React+Node系统实战,搞定SSR服务器渲染

3.7 【掘金】React 进阶实践指南

3.8 【掘金】React SSR 服务端渲染原理解析与同构实践

3.9 【文章】React技术揭秘

3.10 【文章】React 源码解析

3.11 【视频】React18内核探秘:手写React高质量源码迈向高阶开发

3.12 【视频】2023 React 18 系统入门 进阶实战《欢乐购》

3.13 【视频】React18+TS 通用后台管理系统解决方案落地实战

3.14 【视频】React18+Next.js13+TS,B端+C端完整业务+技术双闭环

四、Webpack


4.1 【视频】Webpack5 入门与实战,前端开发必备技能

五、Vite


5.1 【视频】Vite 从入门到精通,玩转新时代前端构建法则

六、Node


6.1 【视频】构建千万级高可用企业级Node.js应用

七、微前端


7.1 【视频】从零打造微前端框架:实战“汽车资讯平台”项目

八、面试


8.1 【视频】前端面试全家桶:从求职准备到面试演练

8.2 【视频】前端框架及项目面试-聚焦Vue3/React/Webpack

8.3 【视频】2周刷完100道前端优质面试真题 双越最新力作

8.4 【视频】一天时间高效准备前端技术一面 匹配大厂面试要求

8.5 【视频】前端性能优化,掌握行业实用、专业、前沿的解决方案

8.6 【博客】大厂面试题每日一题

8.7 【博客】前端面试题整理

8.8 【爪哇教育】[2023 前端面试八股文]

8.9 【爪哇教育】[P8 大佬的算法解题笔记]

8.10 【博客】小智 - 大迁世界 一线大厂面试完整考点,有梦想,有干货

九、项目


9.1 【视频】Vue3+Pinia+Vite+TS 还原高性能外卖APP项目

9.2 【视频】Web前端架构师2022版-脚手架项目

9.3 【视频】Web前端架构师2022版-可拖拽编辑器

9.4 【视频】Web前端架构师2022版-前端监控平台

9.5 【视频】自主搭建5个精品脚手架,玩转前端提效

6.6 【视频】前端共学会:学习|成长|工作|职业,综合服务社区

如何管理好10万行代码的前端单页面应用

· 阅读需 12 分钟
柏拉文
越努力,越幸运

蚂蚁金服数据平台前端团队主要负责多个数据相关的PC Web单页面应用程序,业务复杂度类比Excel等桌面应用,业务前端代码量在几万行~几十万行,随着产品不断完善,破百万指日可待。管理好10万行级甚至百万行级代码的前端应用,是我们团队的核心挑战之一。 接下来的系列文章,我会尝试从以下几个角度介绍我们团队应对挑战的方法:

  • 前端架构

  • 质量保障

  • 性能优化

  • 团队前端开发流程

  • 人员素养

前端架构


团队的架构方案是多个产品经历一年的持续迭代,不断摸索出来的一套适合本团队数据产品业务场景的架构方案,架构方案中还存在尚未解决的痛点和有争议的部分需要持续优化,不保证这套架构适合您的产品。

一、产品特点


先介绍下我们团队的产品特点:

  • ToB产品,业务复杂度高、业务理解门槛高;

  • 前端代码量巨大(数据分析产品从零开始经历8个月迭代业务代码8万行,仅实现了产品长期规划需求的20%)

二、架构方案


架构的目的是管理复杂度,将复杂问题分而治之、有效管理,我们的具体方法如下:

2.1 首先通过路由切割“页面级”粒度的功能模块

这里的“页面级”粒度指一个路由映射的组件

2.2 同一“页面”内的模块再划分

划分原则:

  • 纵向:通过业务功能(可根据视图模块判断)划分

  • 横向:通过Model-View-Controller三种不同职能划分

2.3 合并同类项

继续细分粒度,然后将可复用模块或组件抽离到公共区域

2.3.1 数据模型

数据模型根据职责分成两类:

  1. Domain Model 领域模型
  2. App State Modal 应用状态模型
2.3.1.1 数据模型

领域模型是业务数据,往往要持久化到数据库或localStorage中,属于可跨模块复用的公共数据,如:

  • Users 用户信息
  • Datasets 数据集信息
  • Reports 报表信息

领域模型作为公共数据,建议统一存放在一个叫做Domain Model Layer的架构独立分层中(前端业界一般对这层的命名为ORM层)。

下沉到Domain Model Layer(领域模型层)有诸多利处:

  • 跨模块数据同步问题不复存在,例如:之前Users对象在A和B两个业务模块中单独存储,A模块变更Users对象后,需将Users变更同步到B模块中,如不同步,A、B模块在界面上呈现的User信息不一致,下沉到领域模型层统一管理后,问题不复存在;
  • 除领域模型复用外,还可复用领域模型相关的CRUD Reducer,例如:之前Users对象对应的Create Read Update Delete方法可能在A和B两个业务模块各维护一套,下沉到领域模型层统一管理后,减少了代码重复问题;
  • 自然承担了部分跨模块通信职责,之前数据同步相关的跨模块通信代码没有了存在的必要性;
2.3.1.2 应用状态模型

应用状态模型是与视图相关的状态数据,如:

  • 当前页面选中了列表的第n行 currentSelectedRow: someId

  • 窗口是否处于打开状态 isModalShow: false

  • 某种视图元素是否在拖拽中 isDragging: true

这些数据与具体的视图模块或业务功能强相关,建议存放在业务模块的Model中。

2.3.2 视图层组件

组件根据职责划分为两类:

  1. Container Component 容器型组件
  2. Presentational Component 展示型组件
2.3.2.1 容器型组件

容器型组件是与store直连的组件,为展示型组件或其它容器组件提供数据和行为,尽量避免在其中做一些界面渲染相关的事情。

2.3.2.2 展示型组件

展示型组件独立于应用的其它部分内容,不关心数据的加载和变更,保持职责单一,仅做视图呈现和最基本交互行为,通过props接收数据和回调函数输出结果,保证接收的数据为组件数据依赖的最小集。 一个有成百上千展示型组件的复杂系统,如果展示型组件粒度切分能很好的遵循高内聚低耦合和职责单一原则的话,可以沉淀出很多可复用的通用业务组件。

2.3.3 公共服务

  • 所有的HTTP请求放在一起统一管理;

  • 日志服务、本地存储服务、错误监控、Mock服务等统一存放在公共服务层;

2.4 跨模块通信

模块粒度逐渐细化,会带来更多的跨模块通信诉求,为避免模块间相互耦合、确保架构长期干净可维护,我们规定:

  • 不允许在一个模块内部直接调用其他模块的Dispatch方法(写操作、变更其他模块的state)

  • 不允许在一个模块内部直接读取其他模块的state方法(读操作)

我们建议将跨模块通信的逻辑代码放在父模块中,或者在一个叫做Mediator层中单独维护。

三、数据流管理


刚刚从空间维度讲了架构管理的方案,现在从时间维度说说应用的数据流转 --- Redux单向数据流。 Redux架构的设计核心是单向数据流,应用中所有的数据都应该遵循相同的生命周期,确保应用状态的可预测性。

3.1 Action

  • 用户操作行为:click drag input ...

  • 服务端返回数据后续的行为

3.2 Reducer

每个Action都会对应一个数据处理函数,即Reducer。特别强调,Reducer必须是纯函数(pure function),这个规定带来一个非常大的好处,数据处理层代码变的非常容易写单元测试。

纯函数的特征是入参相同的情况下,返回值恒等,举个栗子:

function add(a, b) {
return a + b;
}

非纯函数:

function now() {
let now = new Date();
return now;
}

函数中如果包含 Math.random,new Date(), 异步请求等内容,且影响到最终结果的返回,即为非纯函数。

3.3 Store

Store 数据存放的地方,store保存从进入页面开始所有Action操作生成的数据状态(state),每次Action引发的数据变更都必须生成一个新的state对象,且确保旧的state对象不被修改。这样做可以保证 应用的状态的可预测、可追溯,也方便设计Redo/Undo功能。 我们团队使用轻量级的immutable方案immutability-helper,相比完全拷贝一份(deep clone)性能更优、存储空间利用率更高。

immutability-helper的API不够友好,我们写了一个库immutability-helper-x增强它的易用性。

import update from 'immutability-helper';
const newData = update(myData, {
x: {
y: {
z: { $set: 7 }
}
},
});

immutability-helper-x API风格:

import update from 'immutability-helper-x';
const newData = update.$set(myData, 'x.y.z', 7);

3.4 统一渲染视图

React/Redux是一种典型的数据驱动的开发框架(Data-Driven-Development),在开发中,我们可以将更多的精力集中在数据(领域模型+状态模型)的操作和流转上,再也不用被各种繁琐的DOM操作代码困扰,当Store变更时,React/Redux框架会帮助我们自动的统一渲染视图。

监听Store变更刷新视图的功能是由react-redux完成的:

  • <Provider> 组件通过context属性向后代<connect>组件提供(provide)store对象;

  • <connect> 是一个高阶组件,作用是将store与view层组件连接起来(这里重复提一句,redux官方将<connect>直接连接的组件定义为container component),<connect>向开发者开放了几个回调函数钩子(mapStateToProps, mapDispatchToProps...)用于自定义注入container component的props的姿势;

  • react-redux监听redux store的变更,store改变后通知每一个connect组件刷新自己和后代组件,为了减少不必要的刷新提升性能,connect实现了shouldComponentUpdate方法,如果props不变的话,不刷新connect包裹的container component;

四、总结


严格遵循架构规范和单向数据流规范,可以保证我们的前端应用在比较粗的粒度上的可维护性和扩展性,对于更细的粒度的代码,我们组织童鞋学习和分享《设计模式》 和 《重构 - 改善既有代码的设计》,持续打磨和优化自己的代码,未来团队会持续输出这方面的系列文章。

参考资料


如何管理好10万行代码的前端单页面应用