vianvio / FE-Companions

山虽高,我心已决要攀登, 路再难,绊不住我的脚跟; 因为我看到生命之路就在这里。 -- 《天路历程》
447 stars 34 forks source link

20200226 - bobobo #23

Open vianvio opened 4 years ago

vianvio commented 4 years ago

问题列表:

  1. 一个复杂的前端工程,从技术模块拆分来看,需要有哪些必要的模块,举例:网络库。
  2. (可选)业务上的H5App如果是在公司自主app内部,如何做到离线首次加载可用。
  3. 画一张业务架构图(脱敏),描述一下你的技术能力对业务带来了哪些提升?为什么做这些功能会有提升?
  4. 从行业的角度,分析一下整个业务的发展方向,提供数据支撑(看情况不需要公开),并从中提取1~2个技术可行的要点,大概给出一个技术落地方案。
lfb commented 4 years ago

问题列表:

  1. 一个复杂的前端工程,从技术模块拆分来看,需要有哪些必要的模块,举例:网络库。
  2. (可选)业务上的H5App如果是在公司自主app内部,如何做到离线首次加载可用。
  3. 画一张业务架构图(脱敏),描述一下你的技术能力对业务带来了哪些提升?为什么做这些功能会有提升?
  4. 从行业的角度,分析一下整个业务的发展方向,提供数据支撑(看情况不需要公开),并从中提取1~2个技术可行的要点,大概给出一个技术落地方案。

一、一个复杂的前端工程,从技术模块拆分来看,需要有哪些必要的模块

脚手架

为什么需要脚手架? 脚手架解决的问题:

以 Vue-cli 为例子,Vue-cli 提供了模板选择,编译以及本地开发服务器等功能模块,使用 Vue-cli 创建项目,开发人员无需再操作复杂的 webpack 配置,只需要关心业务逻辑开发本身,降低了开发的时间成本。

环境

本地开发环境是开发阶段使用。

前端一般也需要做单元测试,记得一次做一个海鲜项目,老板急着要上线,没有做单元测试,五一活动就要上,突发情况太多了,不做单元测试后果负责真的很严重。

生产环境,我装了一个了 fundebug 的插件,随时随地的报错警报,且配上解决方案。

环境的重要性,在于它一套代码运行下,可以有不同的表现,在开发阶段,可以开发需求,且考虑的事情较多,单元测试环境下,可以减少线上环境的BUG,生产环境下,也需要部署一些错误监控,发现BUG时候,快速定位解决。

构建编译

构建工具一般有 grunt,gulp,webpack等,构建工具主要做的事情就是将源代码转化为浏览器可以执行的代码,对资源进行管理,包含JS,CSS,HTML,图片等。

以 webpack 为例,构建的大概功能:

举个例子,需要构建编译什么?:Less 和 sass 预编译的语言,JavaScript ES6 编译成 ES5,HTML的模板渲染(Vue 模板解析)

还有一个因素:性能优化。比如如果是模块化开发的,那每个模块化都有独立的JS,CSS,图片等文件,如果不做处理优化,那么HTTP请求数量增加,影响性能。webpack 构建工具也可以进行压缩、按需加载,代码切割等功能。

网络(websocket,http)

如果是前后端分类,那么数据从哪里来?那么前后端需要通信,通信也要有规范,比如接口规范,RESTful API 规范。

网络库选择:ajax,axios

异步获取数据方案:callback,generater,promise,async/await

常见问题:跨域

网络这块性能优化有很多点,比如使减少请求次数,合并接口。用强缓存和协商缓存,也提高性能优化。

团队协作、Mock

一个项目需要急上线,或者开发周期短,这时候团队的合作能力,协作能力要沟通好,比如前端这块,设计图还没出?要得先去找产品和后端,了解清楚开发大概流程,拿到产品需求,要思考好。后端还没有那么快出数据,怎么办?前端懂 mock 数据很数据,简单说就是先模拟假数据,那么前端需要懂一点点的数据结构。

Mock 的特点有点像前端的“全栈工程师”,自己先把接口数据弄出来,自己再搞渲。不过我一直秉持着“术业有专攻”的思想,前端是前端,后端是后端,学海无涯,继续加油!

单元测试

单元测试我用过的 jest,Mocha。无论前端或后端单元测试很重要。

代码审查 Code review

代码一般使用 git 工管理。这个一般有主管来审核merge,不过我一般提交代码的时候,我都会检查 2~3 次再push。

部署

部署这块也很重要,可能会遇到很多坑,比如路径,资源找不到等问题。一般使用 Nginx 来进行配置转发,同时也要做错误监控等。

lfb commented 4 years ago

二、业务上的H5App如果是在公司自主app内部,如何做到离线首次加载可用。

非常抱歉,在工作中我没有做过离线加载的功能,但我知道 Service Worker Cache 可以实现缓存。

Service Worker 的生命周期包括 install、active、working 三个阶段。一旦 Service Worker 被 install,它将始终存在,只会在 active 与 working 之间切换,除非我们主动终止它。这是它可以用来实现离线存储的重要先决条件。

比如,需要缓存三个文件:index.html, index.js, index.css

// Service Worker会监听 install事件,我们在其对应的回调里可以实现初始化的逻辑
self.addEventListener('install', event => {
  event.waitUntil(
    // 考虑到缓存也需要更新,open内传入的参数为缓存的版本号
    caches.open('index-v1').then(cache => {
      return cache.addAll([
        // 需缓存的文件名
        '/index.html',
        '/index.js',
        '/index.css'
      ])
    })
  )
})

// Service Worker会监听所有的网络请求,网络请求的产生触发的是fetch事件
// 我们可以在其对应的监听函数中实现对请求的拦截,进而判断是否有对应到该请求的缓存,实现从Service Worker中取到缓存的目的
self.addEventListener('fetch', event => {
  event.respondWith(
    // 尝试匹配该请求对应的缓存值
    caches.match(event.request).then(res => {
      // 如果匹配到了,调用Server Worker缓存
      if (res) {
        return res;
      }
      // 如果没匹配到,向服务端发起这个资源请求
      return fetch(event.request).then(response => {
        if (!response || response.status !== 200) {
          return response;
        }
        // 请求成功的话,将请求缓存起来。
        caches.open('test-v1').then(function(cache) {
          cache.put(event.request, response);
        });
        return response.clone();
      });
    })
  );

参考代码:前端性能优化原理与实践, by 修言。

不过我特意去搜索学习一下,是通过 manifest 实现缓存的。

离线存储的manifest一般由三个部分组成:

知识来源

这点是我的薄弱点,我会好好认真经过实践学习的,如果不对,请磐冲大大指点,谢谢!

lfb commented 4 years ago

第三题:画一张业务架构图(脱敏),描述一下你的技术能力对业务带来了哪些提升?为什么做这些功能会有提升?

这个问题,我思考了很久,我一下子下不了手,脑子里疯狂转动自己的知识,自己有什么值得骄傲的知识可以说一说。思考了很久,当复工的第一天在做一个下单时选择的优惠券的小小功能的时候,我思考到,其实每一个小小的基础知识点,都可以写出不同的代码,往往很高大上的功能,也是基础堆积起来的,这道题,我想说一下我这2天做这个功能的时候,思考的过程,可能会偏题,请磐冲大大和朋友们多多指教,谢谢。

思考:如何做一个下单可选择优惠的功能

img

开始

需求:用户下单的时候可以选择可使用的优惠券

如何做:

这个功能很简单,进入下单确定页面的时候,就获取用户可用的优惠券接口,如果给个 Popup 弹出窗给用户选中即可。当选择了优惠券的时候,自动更新一下总价(totalPrice)

this.chooseCoupon = coupon
this.totalPrice -= coupon.price

提升用户体验?

如何做?JS的基础知识 - 数组方法

用户下单的时候可以选择可使用的优惠券,自动帮用户选中优惠额度最大的那张优惠券,提高用户体验。

现在考虑的场景,一个用户的优惠券最多十位数张数,可以用JS简单算法就可以找出最大的优惠券的索引

this.userCoupon.findIndex(item => item.price === Math.max.apply(null, userCoupon.map(item=>item.price)))

减少开发成本:

使用 computed 计算总价,因为 computed 可以动态监听依赖值,动态返回结果。

computed: {
    // 商品总价格
    totalPrice() {
        return (商品单价 * 数量)  - 选择的优惠券价格
    }
}

思考:使用 comptued 会比选择优惠券的时候操作数据更新会好吗?那用 watch 监听优惠券再做回调可以吗?

总结:可以完成的方法可能还有很多,先说下为什么选 comptued 不选 watch 的原因:因为场景不同,watch是监听一个依赖值而触发回调做一些事情,而comptued 是自动监听某个依赖值自动返回计算值,这计算总价格比较符合场景。而另一个手动更新一个数据是很容易的事情,当数据变得更多的时候,那么久变得繁琐了。

新需求又来了:

涉及技术栈:组件/组件设计/组件通信等,如何设计一个通用,且功能可拓展的组件?

<coupon-item @on-chang="onChange" :list="list"></coupon-item>

或者有更好的方案?

当设计完这个组件的时候,可能又有新的思考:

lfb commented 4 years ago

四、从行业的角度,分析一下整个业务的发展方向,提供数据支撑(看情况不需要公开),并从中提取1~2个技术可行的要点,大概给出一个技术落地方案。

writing..
lfb commented 4 years ago

五、移动端适配的方案

备注:这题是磐冲大大在微信里面问过我的问题,所以也总结了一下:地址:移动端适配的方案

vianvio commented 4 years ago

第一题总结的非常到位,如果要拓展的话,可以把这里面每一个单点都以平台化的思路去想一下做法,这就是小团队往大团队拓展的思路。

第二题是对边界的考察,能学一下pwa确实挺好了,但是最理想的答案是结合native能力完成pwa的效果,甚至更进一步达到首次即可离线渲染的能力,可以再搜一下,或者单独聊。

第三题的思路非常好,出题时候的预期也是这题会花费很长的时间。但是答案和题目之间还是有点差别,可以用类似的方法,总结整个业务的大图。站在整个部门或者公司的视角,去了解每一个大的核心项目,了解他们对公司业务的价值,或者对某一个核心业务的价值,在这个层面画一张业务大图出来,帮助你梳理整个商业逻辑的思路,同时反过来思考自己负责的事情对这张大图是否有帮助,有没有可能做得更好。

第四题则是对三的递进,站在更远的视角去看,业务是可能倒闭的,但是技术沉淀是可以带走的,可以多一些沉淀。

第五题基本将整个业界常见的适配方案都整理到位了,其他同学可以参考一下,一篇全部包含了。

lfb commented 4 years ago

第一题总结的非常到位,如果要拓展的话,可以把这里面每一个单点都以平台化的思路去想一下做法,这就是小团队往大团队拓展的思路。

第二题是对边界的考察,能学一下pwa确实挺好了,但是最理想的答案是结合native能力完成pwa的效果,甚至更进一步达到首次即可离线渲染的能力,可以再搜一下,或者单独聊。

第三题的思路非常好,出题时候的预期也是这题会花费很长的时间。但是答案和题目之间还是有点差别,可以用类似的方法,总结整个业务的大图。站在整个部门或者公司的视角,去了解每一个大的核心项目,了解他们对公司业务的价值,或者对某一个核心业务的价值,在这个层面画一张业务大图出来,帮助你梳理整个商业逻辑的思路,同时反过来思考自己负责的事情对这张大图是否有帮助,有没有可能做得更好。

第四题则是对三的递进,站在更远的视角去看,业务是可能倒闭的,但是技术沉淀是可以带走的,可以多一些沉淀。

第五题基本将整个业界常见的适配方案都整理到位了,其他同学可以参考一下,一篇全部包含了。

真的非常感谢磐冲大大的指导,周末我会继续进行思考总结的:)