项目设计
项目设计或者叫项目场景设计,给你一个场景、项目或者需求让你来设计一个方案。因为企业招人一般招两种人,一种是刚毕业的新人,给什么活写代码就好了,能自己独立的解决问题和bug就可以了。另外一种是来了之后代领大家一块干,交给你一个任务能独立的设计出来,并把控需求、设计、开发以及上线,所以这个是针对工作经验多一些的老人,就会考察项目设计的问题。如果只是问了几个面试题,不管是背诵过了还是理解了,深度广度也有了,仅仅这样就招进来带项目就有点不合适。所以就要问一问,找个场景问项目设计的经验和能力。
为何要考察呢?
- 是否具有独立承担项目的能力,培养为项目负责人。
- 设计能力是否匹配工作经验的时间
考察重点:
- 识别需求,转化为功能
- 功能模块的设计
- 数据模型的设计
注意事项:
- 要看整体设计,不要纠细节
例如:
- 要你开发一个前端统计SDK,你会如何设计?
- 一个H5编辑器(低代码),提交到服务端的数据格式怎样设计?
- 让你做团队的技术选型,你会考虑什么?
开发一个前端统计SDK,你会如何设计?
前端统计是非常大的一个东西,线上的前端页面都需要加前端统计,前端统计SDK是每一个产品或公司都要做的内容,它的应用范围很广。
前端统计SDK是将前端的原始数据统计发送给服务端,这个服务端是统计服务端也有可能是第三方服务端,统计服务端会去做离线计算(今天发送的所有数据在凌晨或者其他时间点进行计算),然后产生结果生成报表,根据结果优化页面,形成闭环。
那么SDK会把哪些数据发送给服务端呢?
- 访问量PV,每天有多少人访问这个页面
- 自定义事件,比如一个页面有两个按钮,是否升级为vip,一个确定一个取消,统计有多少人点击了确定和取消
- 性能和错误统计
class MyStatistic{
constructor(productId){
this.productId = productId;
this.initPerformance();//性能统计
this.initError();//错误监控
}
//发送统计数据
send(url,params={}){
}
//初始化性能数据
initPerformance(){
}
//初始化错误监控
initError(){
}
}
如果你是前端负责人,如何做技术选型?
技术选型,选什么呢?
- 前端框架(Vue、React、Nuxt.js、Next.js或者nodejs框架)
- 前端语言(javaScript或TypeScript)
- 其他(构建工具、CI/CD等 )
技术没有好快之分,看是否适合自己。尤其是刚毕业的小白很容易被网上带节奏,像React就是比Vue好,用Vue的都是小白,Vue3都发布了,赶紧用!Svelte是新框架,要拥抱未来!TS就是比JS高级,网上都是夸赞TS的文章等等。网上怎么说是网上怎么说,要看是否适合自己以及团队,认真平等的对待技术。
技术选型的依据是什么呢?
- 社区是否成熟,社区成熟指的是它经过了非常多的验证,已经足够成熟稳定了。如果出了问题,社区成熟做的人多,大家可以一块讨论解决。如果选了一个小众的,出了问题没人改。其次用的人多你身边会的人多,一旦自己请假了别人也能够快速的接手。
- 公司是否已有经验积累,如果公司已经对Vue或者React框架用了很长时间,积累了很多的组件,那么应该继承它去做,这样比较好。当然看之前积累的好不好,如果很烂那就重新开辟一下。
- 团队成员的学习成本,比如团队有五个人开发这个项目,你选了一个技术栈,大家都不会只有你自己会,这样就不合适了。
要全面考虑各种成本,例如:
- 学习成本
- 管理成本,如果用TS遍地any怎么办?这就需要人为控制了。
- 运维成本,框架不一样,语言不一样,运维成本也不一样。
所以初期选型是关键,所谓一将无能累死三军,技术选型很好的体验了一个人的技术修养。要站在团队的角度,而非个人的角度。
sourcemap有什么作用?如何配置?
sourcemap与JS报错关联性很强,那么sourcemap的作用是什么?
- JS上线时要压缩、混淆
- 线上的JS报错信息,将无法识别行、列。因为线上JS报错的哪一行哪一列都是压缩后的。
- sourcemap就可以解决这个问题,它可以将线上混淆压缩后的报错信息转换成混淆压缩前源码中哪个文件第几行第几列的信息,这就是sourcemap的作用。
sourcemap本质上是一个文件,这个文件中有很多东西表示对应关系。webpack通过devtool配置sourcemap。
- eval:JS在eval(...)中,不生成sourcemap
- source-map:生成单独的map文件,并在JS最后指定
- eval-source-map:JS在eval(...)中,sourcemap内嵌
- inline-source-map:sourcemap内嵌到JS中
- cheap-source-map:sourcemap只有行信息,没有列信息
- eval-cheap-source-map:同上,没有独立的map文件
开发环境下推荐使用eval、eval-source-map、eval-cheap-source-map,其实开发环境下不使用也可以,都直接使用源码,不压缩混淆。
线上环境使用source-map或者无
如果你是做开源项目,也要开源sourcemap。非开源项目的话,不要泄露sourcemap。了解sourcemap作用和配置即可,编码原理不用深究。
何时用SPA,何时用MPA?
项目用SPA还是MPA这是一个设计的问题。
- SPA:Single-page Application,单页面应用
- MPA:Multi-page Application,多页面应用
默认情况下,Vue、React都是SPA。
SPA的特点如下:
- 功能较多,一个页面展示不完
- 以操作为主,非展示为主
- 适合一个综合Web应用
SPA场景
- 大型后台管理系统,如阿里云的console
- 知识库,如语雀、石墨文档
- 比较复杂的WebApp,如外卖H5
MPA的特点如下:
- 功能较少,一个页面展示的完
- 以展示为主,操作较少
- 适合一个孤立的页面
MPA的场景
- 分享页,如腾讯文档分享出去
- 新闻详情页,如新闻APP的详情页,微信公众号发布的页面
作为项目前端技术负责人,主要的职责是什么?
目标
项目前端技术负责人,将负责和项目前端开发相关的所有事情,不仅仅是前端范围内的,也不仅仅是开发的事宜。
目标:保证项目按时、按质量的交付上线,以及上线之后的安全稳定运行。
把控需求
新项目开始、或者新功能模块开始时要参与需求评审,认真审阅需求的详细内容,给出评审意见,提出问题。自己已经同意的需求要能保证按时、按质量的完成。
评审需求需要你能深入理解项目的业务,不仅仅是自己负责的功能,还有上下游全局的串联。所以,一入职的新人无论技术能力多好,都无法立刻作为项目技术负责人,他还需要一段时间的业务积累和熟练。PS:除非他在其他公司已经是这个方面的业务专家。
需求评审之后,还可能有 UI 设计图的评审,也要参与,提出自己的意见和问题。保证评审通过的 UI 设计图都能保质保量的开发出来。
需求和 UI 设计图评审完之后,还要给出开发的排期。此时要全面考虑,不仅仅要考虑开发时间,还有内部测试、单元测试的时间,以及考虑一些延期的风险,多加几天的缓冲期。
最后,在项目进行过程中,老板或者 PM 有可能中途插入新需求。此时要积极沟通,重新评估,还要争取延长项目开发周期。需求增加了,肯定周期也要延长一些。
技术方案设计
需求指导设计,设计指导开发。
需求和 UI 设计图确定之后,要先进行技术方案设计,写设计文档,评审,通过之后再开发。技术方案设计应该包含核心数据结构的设计,核心流程的设计,核心功能模块的组织和实现。评审时看看这些有没有不合理、隐患、或者和别人开发重复了。
技术方案设计还要包括和其他对接方的,如和服务端、客户端的接口格式。也要叫他们一起参与评审,待他们同意之后再开发。
开发
作为技术负责人,不应该把自己的主要精力放在代码开发上,但也不能完全不写代码。
应该去写一些通用能力,核心功能,底层逻辑的代码。其他比较简单的业务代码,可以交给项目成员来完成。
监督代码质量
技术负责人,可能会带领好多人一起编写代码,但他要把控整个项目的代码质量。例如:
- 制定代码规范
- 定期组织代码审核
- CI 时使用自动化单元测试
跟踪进度
每天都组织 10 分钟站会,收集当前的进度、风险和问题。如有延期风险,要及时汇报。
不仅仅要关心前端开发的进度,还要关心上下游。例如上游的 UI 设计图延期,将会导致前端开发时间不够,进而导致测试时 间不够,甚至整个项目延期。
稳定安全的运行
上线之后,要能实时把控项目运行状态,是否稳定、安全的运行。万一遇到问题,要第一时间报警。
所以,项目中要增加各种统计和监控功能,例如流量统计、性能统计、错误监控,还有及时报警的机制。
总结
- 把控需求
- 技术方案设计
- 开发
- 监督代码质量
- 跟踪进度
- 稳定安全的运行
使用Vue + Vuex开发H5编辑器
有三个问题:
- 点击保存按钮,提交给服务端的数据格式怎样设计?
- 如何保证画布和属性面板的信息同步?
- 如果再扩展一个图层面板,Vuex如何设计数据?
状态设计
状态设计主要包括以下两点:
- state数据结构设计
- 组件设计(拆分、组合)和组件通讯
- state 数据结构设计
首先要用数据描述所有的内容,然后数据要结构化,易于程序操作(遍历、查找),其次数据要可扩展,以便增加新功能。
例如一个列表我们用 state 可以这样表示:
this.state = {
list: [
{
id: 1,
title: '标题 1',
compoleted: false
}
]
}
id 是一个标识,必须要有的。
- 组件设计
组件设计从功能上拆分层次,尽量让组件原子化。区分容器组件(只管理数据)和UI 组件(只显示视图)
<App>{/**只负责管理数据*/}
<Input/>{/**只负责输入,将数据结果给父组件*/}
<List>{/**只负责显示列表,从父组件获取数据*/}
<ListItem />{/**显示单条数据,删除、切换完成状态*/}
<ListItem />
<ListItem />
</List>
</App>
设计一个 input 的撤销、重做功能
思路是维护一个list 和 index,input change 时push 到 list且 index++,Undo时 index-1,redo 时 index+1。
const inputText = document.getElementById('input-text')
const btnUndo = document.getElementById('btn-undo')
const btnRedo = document.getElementById('btn-redo')
const list = [inputText.value] //初始化列表
let curIndex = list.length - 1 //初始化 index
indexText.addEventListener('change',e=>{
const text = e.target.value
console.log('change',text)
list.length = curIndex+ 1 //截取掉 index 后面的部分
list.push(text)
curIndex++ //重置 index
})
btnUndo.addEventListener('click',e=>{
if(curIndex<=0)return
curIndex-- //index 减少
inputText.value=list[curIndex]
})
btnRedo.addEventListener('click',e=>{
if(curIndex>=list.length-1)return
curIndex++ //index 增加
inputText.value=list[curIndex]
})