Skip to content

Instantly share code, notes, and snippets.

@msyfls123
Last active October 11, 2019 07:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save msyfls123/72e71b86e4f53f9861caa8dd992dba91 to your computer and use it in GitHub Desktop.
Save msyfls123/72e71b86e4f53f9861caa8dd992dba91 to your computer and use it in GitHub Desktop.

Questions:

马老师,您好!非常高兴能够采访到您。请您先简单介绍下目前您在豆瓣前端团队所负责的工作,以及团队情况。

你好,我是豆瓣阅读前端负责人马申彦,现在负责豆瓣阅读 web 前端的开发与维护,包括一个完整的 web 版电子书店、App 内嵌页面、原创作品的写作发布及后台审稿相关功能,日常运营相关活动与比赛等,阅读工程团队内前端组共 3 人,整个技术团队不超过 15 人,继承了豆瓣优良的技术氛围同时具有自主创新的产品方法。

豆瓣是Web2.0时代比较有代表性的网站,在信息聚合与用户分享的部分都做得非常好。现在移动应用越来越注重用户交互,前端可以从哪几个方面入手,实现创新?可否和我们分享豆瓣的一些具体实践?

用户交互与前端关系密切,影响用户体验的要素很多,简单来说分为页面加载与后续交互两部分,页面加载方面又分为 App 内嵌页面、传统服务器渲染页面和 SPA 页面等,这里面加载的体验效果是从前往后递减,App 内嵌页面直接打包在 App 内无需加载页面,只需要载入数据,但这需要和 App 通讯获取数据,所以开发时需要与客户端配合定义相关数据;服务器渲染页面一般适合纯展示的需求,特点是加载快但基本没有后续交互了;SPA 页面是前端自主控制数据的获取与存储等,可以有更多的交互操作,但缺点是 js 文件加载会导致出首屏慢,这时就需要通过 webpack 等进行分包或进行异步加载以提升载入速度。页面载入后的交互上一般会考虑以下几种情况:

  1. SPA 页面的话有路由问题,如何在路由间共享数据等,比如 redux,来方便切换路由时有足够数据渲染。
  2. 页面内载入数据,一般就是考虑载入中和出错等情况,分别给用户提示,提示的话也会分全局的模态框和 toast 的形式,各有各的用武之地。
  3. feed 流式边滚动边载入数据,这个就会用到我之后在 JSConf 上分享的响应式编程相关知识了,简单说来也就是根据一定事件和特定规则来触发 UI 变动或网络请求
  4. 表单交互,早期网站用户交互就只有链接和表单两种,为了在传递给服务器数据前的各种自定义验证条件,我们分别使用了自行开发的表单验证脚本、react-form 和 formik 等来创建具有更丰富功能的 html 表单
  5. 在图标广泛应用的当下,svg 成为了绘制图形的首选,可通过 css 内嵌 svg 来提升展示速度或写入 DOM 来控制 svg 的色彩
  6. Safari 300ms 点击事件 / tap 代替 click,滚动用 scroll-snap-type
scrollSnap(itemSel)
  scroll-snap-type: x mandatory // Chrome 69
  -webkit-scroll-snap-type: mandatory // Safari
  -webkit-overflow-scrolling: touch // Safari only
  {itemSel}
    scroll-snap-coordinate: 0% 0% // Safari @stylint ignore
    scroll-snap-align: start // Chrome 69
  1. 输入电话用 input type='tel'
  2. 字体排版与高清晰度图片等

我之前看到过一篇豆瓣前端工程师写给前端新人的一封信,里面提到前端人员要有预见性地学习新技术,拥抱新标准,同时说到前端工程师是所有工程师中最需要“工匠精神”的。您可以分别谈谈豆瓣在新技术上有哪些尝试,以及主要在打磨何种技术实现吗?

不只是前端工程师,所有工程师都非常需要“工匠精神”,豆瓣向来拥抱新技术的尝试与落地,豆瓣前端组也会不定期组织组内分享各产品线或个人项目运用的新技术,2016 年底小程序刚出来时,豆瓣就组织了以小程序开发为主题的 Happy Day 活动,其中用小程序开发的“阿尔法城重生”项目触动了在场工程师们的集体怀旧记忆。豆瓣阅读这边早期使用 jQuery 和 Backbone 来构建 web 应用,在 React 出来以后迅速地被其声明式的写法所吸引,现今站内包括管理后台和编辑器在内的中大型 web 应用都优选使用 React 构建,应用了包括 TypeScript、GraphQL、Rx.js、Storybook 和 Jest 在内的多种技术完善保证项目的开发与维护。现在阅读站内主要针对原创作品写作者和读者进行了多项功能优化和调整,为了开发维护方便,我们果断抛弃了使用人数非常少的 IE10 以下的浏览器,这样我们代码里省去了很多为特定浏览器做的 css prefix 或是 hack。在产品上针对 Draft-js 进行二次开发的编辑器以及 web 阅读器,在保证正常阅读编辑体验的同时,不断优化代码结构,保证像素级体验。

您在本次JSConf的演讲主题是响应式编程,为什么会选择这一话题?将响应式编程的范式应用在前端开发的意义是什么?可以解决哪些问题?传统开发模式与响应式开发模式有何区别?

响应式编程其实已经拥有了很长的历史,甚至 Rx.js 也是早在 2012 年就由微软发布开源,但时至今日在前端圈内始终不温不火,Angular 在从 1.x 升级到 2.0 的时候果断选择了 Rx.js 作为其数据源,相比是经历了一番思索的,但 Angular 可惜在于其开发周期过长,期间 React + Vue 双雄崛起,一举拿下了国内大部分市场,只有少部分企业级应用继续使用 Angular。虽然豆瓣阅读站内基本都是采用 React 作为 UI 框架,但同时吸收了 Angular 采用的 TypeScript 和 Rx.js 来控制代码质量和数据流,取得了不错的成果,这才萌生了向广大前端 er 继续科普 Rx.js 及响应式编程的想法。Rx.js 本身就是一个基于 TypeScript 开发的框架,所以其代码健壮性很高,而且从 v5 到 v6 通过 pipe 方式载入运算符以后大大缩减了打包后的体积,为前端项目后续优化提供了丰富且宝贵的经验。

在三大框架大行其道的当下,大部分复杂的 DOM 操作都被框架接管了,现在的开发人员主要是基于框架做业务层的开发,但业务层往往数据密集且多变,而且又是与用户直接面对面,需要时刻处理用户的输入。一个比较常见的场景是用户的点击事件触发了一个网络请求继而发生了 DOM 上的变更,这种简单的变动一般一个函数就可以完成,但是遇到复杂的多输入问题,往往会形成函数套函数最后压根找不到事件源头的情况。在维护旧的 Backbone 代码过程中就很常见这种情况,后来把一系列异步流程用 Promise 来进行管理,确实达到了一定的效果,但我们不禁反思,这种复杂异步操作究竟如何才能做到在多重依赖和并发下的优雅,后来我们找到了它,就是响应式编程。具体来说,响应式编程容易让事件的发生和转化过程变得清晰有序,因为它的惰性求值可以做到按需计算,省去传统开发模式中很多无用的中间变量,又因为它几乎是纯函数的,所以也方便构成组件复用于不同的项目。这样我们就着眼于数据的流动而不是时时刻刻想着用户做了操作之后该执行哪一步,因为这些都是响应式编程的范式所解决的问题。

目前,主流前端框架或多或少受响应式编程范式的影响。您可以举例说明如何利用好这种范式,写出更优雅易读的代码吗?

因为我对 React 比较熟悉,就应用 React 做下说明,我们可以从源头说起,大家知道一个 React 组件有 props 和 state 两种储存变量的方式,如何判断究竟是 props 还是 state 呢?有三个问题:它是不是从父组件传递下来?它会随时间变化吗?它可以由其他 props 或者 state 计算而来吗?按照这种思路得到的 state 本身就是一个独立的随时间不断吐出值的数据流。我们会在设置 state 时有意识地让它们变得正交,也就是彼此独立。这样得到的 state 通过与 props 之类的进行组合就能在 render 时计算出不同的数值。但也绕不过随着 props 更新 state 的问题,所以有了 componentWillReceiveProps 这种 method,可是其调用方式非常诡谲,一不小心就容易形成死循环。

为了解决这些问题 React hooks 就出现了,它就是一个很棒的响应式编程的实践,比如说 useEffect 这个 hooks,我们观察一个 props 属性,当他发生改变时做一些操作。这样这个 property 就是 single source of truth,真理的唯一源头。而它是由父组件传下来的,所以就保证了整个组件是可被控制的。

响应式编程不是银弹,它有不擅长的领域。请问您觉得这个边界在哪里?如何在开发过程中进行判断?

具体说来响应式编程主要应用在那些随着时间推移会变化的数据上,如果只是一次性的同步计算,那响应式编程将毫无用武之处,当然还是可以用一些运算符来进行规约,是异步编程的一种特殊解。还有就是极其简单的异步操作,比如一次简单的 fetch 那就没有必要用上 Rx.js 这种重型武器,直接用 Promise 的方法就可以,但如果说需要一个带自动重试功能同时还要处理并发请求的 fetch,这时候就可能需要按需引入 Rx.js 来管理这些请求。需要注意的是,Rx.js 的学习曲线比较陡峭,但学了以后你肯定会爱上它的。事实上,最难的部分莫过于放弃这一想法:事件在某种层度上与我们每天正在用的集合不同。

在今年柏林的JSConf EU上,npm首席数据官Laurie披露了一组关于“逃离 JavaScript”的数据,其中提到了两个趋势。一个是,TypeScript的使用者已经从去年的46%增长到63%;另一个是,WebAssembly的出现。您如何看待JavaScript未来的发展趋势?

TypeScript 已是大型前端项目的标准配置,而且它是 JavaScript 的超集,这其实并不是逃离 JavaScript 的趋势。WebAssembly 主要是解决 JavaScript 计算性能不足的问题,重点用在依赖图形渲染或者高速计算的项目,WebAssembly 的另一个意义是可以有更多的语言参与到浏览器这块战场,这是好事,不应该将其他语言视为敌人,而应该拥抱这种变化,正如我们 iOS App 内的排版引擎是用 C++ 写的,但界面依然是用 Objective-C 和 Swift 构建,所谓术业有专攻。软件项目开发与技术人员对业务逻辑的熟悉度和实现难度关系更大,只要能够带来生产效率的提升,那么任何新技术都是值得探讨和学习的。回到 JavaScript 的发展趋势,应该还是渐进式增强,完善面向对象编程(主要是 class)的部分,标准类型借鉴现有流行类库的最佳实践,更友好的 DOM 操作方法,以及更优雅的语法糖等等,同样值得注意的还是与设备端的联合,比如传感器等 API,这也是移动优先的发展方向。

您目前还在关注哪些新的技术问题,可以和我们分享一下吗?

  1. 尝试建立从 GraphQL 的 schema 到 TypeScript 类型系统的直接映射,希望能优化开发流程。
  2. css-in-js 和 css-modules 相关。
  3. 前端微服务化等。

国内外 JS 发展方向

国外前端框架举例:Svelte,Elm, Cycle.js 国内前端框架举例:Vue, Avalon, dva 国外更偏向工具性,国内偏向平台应用

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment