Lile_Black

Lile_Black 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

有洁癖,无节操。

个人动态

Lile_Black 赞了文章 · 9月28日

解读新一代 Web 性能体验和质量指标

衡量一个 Web 页面的体验和质量一直有非常多的工具和指标 ... 每次我们去关注这些指标的时候都会非常痛苦,因为这些指标真的是又多又难理解,测量这些指标的工具也非常多。

当看到最近发布的 Chrome 83 中又增加了几个性能指标的时候我头都大了...

然而不要着急,这些指标就是为了聚焦关注度和降低理解成本的,下面我们就来具体看一下,新增加的 Core Web Vitals 到底是什么东西?

如何衡量用户体验质量?

优化用户体验的质量一直都是是每个 Web 站点长期成功的关键,衡量用户体验的质量有很多方面。虽然用户体验的某些方面是需要基于特定于站点和上下文的,但是所有站点仍然有一组共同的指标——Core Web Vitals,这些指标包括加载体验、交互性和页面内容的视觉稳定性,他们构成了 2020 年核心 Web 健康指标的基础。

多年来,Google 提供了很多工具:(Lighthouse, Chrome DevTools, PageSpeed Insights, Search Console's Speed Report) 来衡量和报告性能。一些开发人员是使用这些工具的专家,而大部分其他人则发现大量的工具和衡量标准都很难学习和使用。

网站开发者不应该为了理解他们交付给用户的体验的质量指标而成为性能专家。Web Vitals 计划的目的就是简化场景,降低学习成本,并帮助站点关注最重要的指标,即 Core Web Vitals

Core Web Vitals

Core Web Vitals 是应用于所有 Web 页面的 Web Vitals 的子集,所有的站点开发者都应该关注一下,他们将在所有谷歌提供的性能测试工具中进行显示。每个 Core Web Vitals 代表用户体验的一个不同方面,在该领域是可衡量的,并反映了以用户为中心的关键结果的真实体验。

网页核心的性能指标应该是随着时间的推移而不断演变的。当前 2020 年主要关注用户体验的三个方面——加载、交互性和视觉稳定性:

  • Largest Contentful Paint (LCP): 衡量加载体验:为了提供良好的用户体验, LCP 应该在页面首次开始加载后的 2.5 秒内发生。
  • First Input Delay (FID): 衡量可交互性,为了提供良好的用户体验,页面的 FID 应当小于 100毫秒。
  • Cumulative Layout Shift (CLS):衡量视觉稳定性,为了提供良好的用户体验,页面的CLS应保持小于 0.1。

下面我们来详细介绍这三种性能指标:

LCP

加载体验的衡量

衡量 Web 页主要内容的加载速度是众多开发者一直在关注的一个点,而且可衡量的指标非常多。

比如最早的 loadDOMContentLoaded 事件,用这两个事件来衡量页面加载速度是非常糟糕的,因为它们不一定与用户在屏幕上看到的内容相对应。

以用户为中心的更新性能指标(例如First Contentful Paint(FCP))它只能捕捉加载体验的最开始。如果页面最开始显示的是一个 loading 动画,那这个指标就很难关注了。

后来,业界开始建议使用比如 First Meaningful Paint (FMP)Speed Index (SI)(都可以在 Lighthouse 中获取)等性能指标来帮助捕获初次渲染后的更多加载体验,但是这些指标非常复杂,难以解释,而且误报率也比较高。

什么是 LCP

Largest Contentful Paint (LCP) 用于衡量标准报告视口内可见的最大内容元素的渲染时间。为了提供良好的用户体验,网站应努力在开始加载页面的前 2.5 秒内进行 最大内容渲染

相比 FCP ,这个指标就非常有价值了,因为这个值是根据页面加载渲染不断变化的,如果页面有一个 lodaing 动画,然后才渲染出具体内容,那么这个指标计算出来的就是具体内容的加载速度,而非 lodaing 动画的加载速度。

LCP 考虑哪些元素

LCP 目前并不会计算所有元素,因为这样会使这个指标变得非常复杂,它现在只关注下面的元素:

  • <img> 元素
  • <image>元素内的<svg>元素
  • <video> 元素
  • 通过 url() 函数加载背景图片的元素
  • 包含文本节点或其他内联文本元素子级的块级元素。
为了在开始时保持简单,将元素限制到这个有限的集合是有意的。随着研究的深入,将来可能会添加更多的元素。

如何计算 LCP ?

页面上最大的元素即绘制面积最大的元素,所谓绘制面积可以理解为每个元素在屏幕上的 “占地面积”,如果元素延伸到屏幕外,或者元素被裁切了一部分,被裁切的部分不算入在内,只有真正显示在屏幕里的才算数。

图片元素的面积计算方式稍微有点不同,因为可以通过 CSS 将图片扩大或缩小显示,也就是说,图片有两个面积:“渲染面积”与“真实面积”。在 LCP 的计算中,图片的绘制面积将获取较小的数值。例如:当“渲染面积”小于“真实面积”时,“绘制面积”为“渲染面积”,反之亦然。

页面在加载过程中,是线性的,元素是一个一个渲染到屏幕上的,而不是一瞬间全渲染到屏幕上,所以“渲染面积”最大的元素随时在发生变化。

如果元素被删除,LCP算法将不再考虑该元素,如果被删除的元素刚好是 “绘制面积” 最大的元素,则使用新的 “绘制面积” 最大的元素创建一个新的性能条目。

该过程将持续到用户第一次滚动页面或第一次用户输入(鼠标点击,键盘按键等),也就是说,一旦用户与页面开始产生交互,则停止报告新的性能指标。

在以上两个时间轴中,最大的元素随内容加载而变化。在第一个示例中,新内容被添加到 DOM 中,并且更改了最大的元素。在第二个示例中,布局发生更改,以前最大的内容从视口中删除。通常情况下,延迟加载的内容要大于页面上已存在的内容。

改善 LCP

LCP较差的最常见原因是:

  • 服务器响应时间慢
  • 阻断渲染的 JavascriptCSS
  • 资源加载时间慢
  • 客户端渲染

所以我们从上面的角度去考虑改善 LCP

优化服务器

这个很好理解,浏览器从服务器接收内容所需的时间越长,则在屏幕上呈现任何内容所花费的时间就越长。更快的服务器响应时间可以直接改善包括 LCP 在内的所有页面加载指标。

衡量服务器相应时间有一个专门的指标:首字节相应时间(TTFB)是最初的网络请求被发起到从服务器接收到第一个字节这段时间,它包含了 TCP 连接时间,发送 HTTP 请求时间和获得响应消息第一个字节的时间。你可以尝试在下面几个方便优化 TTFB

  • 缓存 HTML 离线页面,缓存页面资源,减少浏览器对资源的请求。
  • 尽量减小资源阻断渲染:CSSJavaScript 压缩、合并、级联、内联等等
  • 对图片进行优化。转化图片的格式为 JPG 或者 WEBP 等等的格式,降低图片的大小,以加快请求的速度。
  • HTML 重写、压缩空格、去除注释等。减少 HTML 大小,加快速度。
  • 使用 preconnect 尽快与服务器建立链接、使用 dns-prefetch 尽快进行 DNS 查找。
  • 使用 CDN 加快请求速度

优化阻断渲染的资源

JavaScriptCSS 都是会阻断页面渲染的资源,需要尽可能的对 CSSJavaScript 文件进行压缩、延迟加载首屏无需使用的 JavaScript、内联关键的 CSS 等来减小阻断时间。

优化资源加载时间

刚才我们上面提到的这些资源,如果在首屏进行渲染,则加载这些元素所花费的时间将直接影响 LCP

  • <img> 元素
  • <image>元素内的<svg>元素
  • <video> 元素
  • 通过 url() 函数加载背景图片的元素
  • 包含文本节点或其他内联文本元素子级的块级元素。

你可以使用下面的手段进行优化:

  • 对图片进行优化。转化图片的格式为 JPG 或者 WEBP 等等的格式,降低图片的大小。
  • 对重要的资源进行预加载,比如为 style 标签添加 rel="preload" 属性
  • 使用 GzipBrotli 压缩页面资源,降低传输时间
  • 使用 service worker 缓存资源

服务端渲染

使用服务端渲染可以确保首先在服务器上呈现页面内容,可以有效改善 LCP,但是相比客户端渲染的缺点是会加大页面从而影响 TTFB、服务端渲染需要等待所有 js 执行完毕后才能相应用户输入,这会使交互体验变差。

FID

第一印象

我们都知道留下一个好的第一印象是多么重要。在网络上,一个好的第一印象可以决定一个人是不是可以成为一个网站的忠实的用户,或者是离开以后再也不会回来。问题是,什么能给人留下好印象,你如何衡量你可能给用户留下什么样的印象?

在网络上,第一印象可以有很多种不同的形式——我们对网站的设计和视觉吸引力有第一印象,对其速度和响应能力也有第一印象。

开发者们使用 First Contentful Paint(FCP) 可以衡量对网站加载速度对第一印象 。但是,网站可以在屏幕上绘制像素的速度只是一部分,同样重要的是用户尝试与这些像素进行交互时你的网站的响应速度!

什么是 FID

FID( First Input Delay) 即记录用户和页面进行首次交互操作所花费的时间 。FID 指标影响用户对页面交互性和响应性的第一印象。 为了提供良好的用户体验,站点应努力使首次输入延迟小于 100 毫秒。

FID 发生在 FCPTTI 之间,因为这个阶段虽然页面已经显示出部分内容,但尚不具备完全的可交互性。这个阶段用户和页面交互,往往会有较大延迟。

如上图所示,浏览器接收到用户输入操作时,主线程正在忙于执行一个耗时比较长的任务,只有当这个任务执行完成后,浏览器才能响应用户的输入操作。它必须等待的时间就此页面上该用户的 FID 值。

例如,以下所有 HTML 元素都需要在响应用户交互之前等待主线程上正在进行的任务完成:

  • 文本输入框,复选框和单选按钮(<input>,<textarea>
  • 选择下拉菜单(<select>
  • 链接(<a>

如何提高 FID

以下几个方面是提高 FID 的重要指标:

减少 JavaScript 执行时间

同上面改善 LCP 的方法:

  • 缩小并压缩 JavaScript 文件
  • 延迟加载首屏不需要的 JavaScript
  • 尽量减少未使用的 polyfill

分解耗时任务

上面提到一个较长的耗时任务是影响 FID 的重要指标,任何阻塞主线程 50 毫秒或更长时间的代码段都可以称为“长任务”,我们可以将长的耗时任务拆分为较小的异步任务。

使用 Web Worker

主线程阻塞是输入延迟的主要原因之一。Web Workers 可以让你在与主执行线程分离的后台线程上运行 JavaScript,这样做的好处是可以在一个单独的线程中执行费时的处理任务,从而允许主(通常是UI)线程运行而不被阻塞。将非 UI 操作移至单独的工作线程可以减少主线程的阻塞时间,从而改善 FID

CLS

视觉稳定性

您是否曾经在访问一个 Web 页面时发生下面的情况?在阅读文章的同时文字突然移动了、你突然找不到你阅读的位置了、点按钮的时候按钮被移动到了其他地方,导致你点了其他东西?

页面内容的意外移动通常是由于异步加载资源或将 DOM 元素动态添加到现有内容上方的页面而发生的。罪魁祸首可能是尺寸未知的图像或视频,渲染后比其后备更大或更小的字体,或者是动态调整自身大小的第三方广告或小部件。

Cumulative Layout Shift (CLS) 可通过测量实际用户发生的频率来帮助您解决此问题。

什么是 CLS?

CLS 会测量在页面的整个生命周期中发生的每个意外的样式移动的所有单独布局更改得分的总和。布局的移动可能发生在可见元素从一帧到下一帧改变位置的任何时候。为了提供良好的用户体验,网站应努力使 CLS 分数小于 0.1

如何计算 CLS?

布局偏移分值

为了计算布局的偏移值,浏览器会查看两个渲染帧之间的视口大小和视口中不稳定元素的移动。布局偏移分是该移动的两个指标的乘积:影响分数和距离分数。

layout shift score = impact fraction * distance fraction

影响分数

前一帧和当前帧的所有不稳定元素的可见区域的并集(占视口总面积的一部分)是当前帧的影响分数。

在上图中,有一个元素在一帧中占据了视口的一半。然后,在下一帧中,元素下移视口高度的25%。红色的虚线矩形表示两个帧中元素的可见区域的并集,在这种情况下,其为总视口的75%,因此其影响分数为 0.75

距离分数

布局偏移值方程的另一部分测量不稳定元素相对于视口移动的距离。距离分数是任何不稳定元素在框架中移动的最大距离(水平或垂直)除以视口的最大尺寸(宽度或高度,以较大的为准)。

在上面的例子中,最大的视口尺寸是高度,并且不稳定元素移动了视口高度的25%,这使得距离分数为0.25

因此,在此示例中,影响分数为0.75,距离分数为0.25,因此版式位移分数为0.75 * 0.25 = 0.1875

如何改善 CLS?

不要使用无尺寸元素

图像和视频等元素上始终需要包括 widthheight 尺寸属性,现代浏览器会根据图像的 widthheight 属性设置图像的默认长宽比,知道纵横比后,浏览器就可以为元素计算和保留足够的空间。

或者,使用 aspect-ratio 也可以提前指定宽高比:

img {
    aspect-ratio: attr(width) / attr(height);
}

那响应式的图片呢?可以使用 srcset 定义图像,使浏览器可以在图像之间进行选择,以及每个图像的大小。

<img 
    width="1000" 
    height="1000"
    data-original="puppy-1000.jpg"
    srcset="puppy-1000.jpg 1000w,
            puppy-2000.jpg 2000w,
            puppy-3000.jpg 3000w"
    alt="ConardLi"
/>
  • 永远不要在现有内容之上插入内容,除非是响应用户交互。这确保了预期的布局变化。
  • 宁可转换动画,也不要转换触发布局变化的属性的动画。以一种提供从一个状态到另一个状态的上下文和连续性的方式动画转换。

提前给广告位预留空间

很多页面广告都是动态插入的,所以一定要提前为广告位预留一定空间。

警惕字体变化

字体通常是大文件,需要一段时间才能加载,一些浏览器直到下载完字体后才呈现文本

font-display: swap 告诉浏览器默认使用系统字体进行渲染,当自定义字体下载完成之后再进行替换。

@font-face {
  font-family: 'Pacifico';
  font-style: normal;
  font-weight: 400;
  src: local('Pacifico Regular'), local('Pacifico-Regular'), url(https://fonts.gstatic.com/xxx.woff2) format('woff2');
  font-display: swap;
}

另外,你可以使用 <link rel="preload"> 更早的加载字体文件。

获取 Core Web Vitals

Google 认为,Core Web Vitals 对于所有网络体验都至关重要。因此,它致力于在其工具中显示这些指标,下面是现有工具中指标的支持情况:

尚未支持这些指标的工具都将在最近得到支持。

web-vitals

现在你可以使用标准的 Web APIJavaScript 中测量每个指标。 Google 提供了一个 npm 包:web-vitals,这个库提供了非常简单的 API,测量每个指标就像调用一个普通函数一样简单:

npm install web-vitals

每个测量函数都接收一个 report 回调函数作为参数,回调函数将在测量完成后触发,另外,对于像 LCPCLS 这样的指标是不断变化的,所以它们的回调函数可能会多次触发,如果你想获取在这期间获取每次变化的数值,你可以指定第二个参数 reportAllChanges,否则回调函数只有在最终测量完成后触发一次。

import {getCLS, getFID, getLCP} from 'web-vitals';

getCLS(console.log, true);
getFID(console.log); // Does not take a `reportAllChanges` param.
getLCP(console.log, true);

这些变化的指标如果触发多次的话可能会多次发送到你的服务器,所以回调函数中提供了下面三个参数:

  • name:指标名称
  • id:本地分析的id
  • delta:当前值和上次获取值的差值

因此你只需要每次上报 delta (当前值和上次获取值的差值),而不需要报告新值。然后在服务器可以通过计算所有id对应值的和来获取最终结果。

import {getCLS, getFID, getLCP} from 'web-vitals';

function logDelta({name, id, delta}) {
  console.log(`${name} matching ID ${id} changed by ${delta}`);
}

getCLS(logDelta, true);
getFID(logDelta);
getLCP(logDelta, true);

你可以很好的结合 Google Analytics 来记录你的上报指标:

import {getCLS, getFID, getLCP} from 'web-vitals';

function sendToGoogleAnalytics({name, delta, id}) {
  ga('send', 'event', {
    eventCategory: 'Web Vitals',
    eventAction: name,
    eventValue: Math.round(name === 'CLS' ? delta * 1000 : delta),
    eventLabel: id,
    nonInteraction: true,
  });
}

getCLS(sendToGoogleAnalytics);
getFID(sendToGoogleAnalytics);
getLCP(sendToGoogleAnalytics);

使用 Chrome 插件

如果你不想在程序中计算,还可以使用 Chrome 插件这样更方便的方式,Google 也提供了一个新的插件 web-vitals-extension 来帮助我们获取这些指标:

这个插件非常简洁,只有 CLS、FID、LCP 这三个核心指标,这样可以大大聚焦我们的关注度,降低理解成本。

徽章的颜色可以告诉你页面有没有通过默认设定的阈值:

  • 灰色:插件不支持或者被禁用
  • 绿色:通过所有指标
  • 红色:一个或多个指标不达标

参考

小结

最后,想在浏览器中使用上面的工具和指标?快升级一下 Chrome 83 版本吧~,更多 Chrome 83 的更新可以点击 Chrome 83 发布,支持直接读写本地文件!新的跨域策略! 查看。

文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你,欢迎点赞和关注。

想阅读更多优质文章、可关注我的github博客,你的star✨、点赞和关注是我持续创作的动力!

推荐关注我的微信公众号【code秘密花园】,每天推送高质量文章,我们一起交流成长。

查看原文

赞 37 收藏 15 评论 0

Lile_Black 收藏了问题 · 9月28日

defer和async的区别

在javascript高级程序设计里,介绍了有关defer和async的区别,可是比较浅显,那位大牛能说明白些。

Lile_Black 收藏了文章 · 9月24日

React 328道最全面试题(持续更新)

今天的React题没有太多的故事……

半个月前出了248个Vue的知识点,受到很多朋友的关注,都强烈要求再出多些React相前的面试题,受到大家的邀请,我又找了20多个React的使用者,他们给出了328道React的面试题,由我整理好发给大家,同时发布在了前端面试每日3+1React专题,希望对大家有所帮助,同时大家可以到上面作答,看自己有多少能答得上来的,答案就先不公布了(答案都在你们的心中)...

前端面试每日3+1(今天第94天)

  • 《前端面试每日3+1》,学习不打烊,充电加油只为遇到更好的自己,365天无节假日,每天早上5点纯手工发布面试题(死磕自己,愉悦大家)。
  • 希望大家在这浮夸的前端圈里,保持冷静,坚持每天花20分钟来学习与思考。
  • 在这千变万化,类库层出不穷的前端,建议大家不要等到找工作时,才狂刷题,提倡每日学习!(不忘初心,html、css、javascript才是基石!)

超多题预警开始……

  • 题目更新时间

    • 第一次:2019.07.19 -> 328 道

React | ReactNative | React-Router | Redux/Mobox | Flux

React

  • 什么时候使用状态管理器?
  • render函数中return如果没有使用()会有什么问题?
  • componentWillUpdate可以直接修改state的值吗?
  • 说说你对React的渲染原理的理解
  • 什么渲染劫持?
  • React Intl是什么原理?
  • 你有使用过React Intl吗?
  • 怎么实现React组件的国际化呢?
  • 说说Context有哪些属性?
  • 怎么使用Context开发组件?
  • 为什么React并不推荐我们优先考虑使用Context?
  • 除了实例的属性可以获取Context外哪些地方还能直接获取Context呢?
  • childContextTypes是什么?它有什么用?
  • contextType是什么?它有什么用?
  • Consumer向上找不到Provider的时候怎么办?
  • 有使用过Consumer吗?
  • 在React怎么使用Context?
  • React15和16别支持IE几以上?
  • 说说你对windowing的了解
  • 举例说明React的插槽有哪些运用场景?
  • 你有用过React的插槽(Portals)吗?怎么用?
  • React的严格模式有什么用处?
  • React如何进行代码拆分?拆分的原则是什么?
  • React组件的构造函数有什么作用?
  • React组件的构造函数是必须的吗?
  • React中在哪捕获错误?
  • React怎样引入svg的文件?
  • 说说你对Relay的理解
  • 在React中你有经常使用常量吗?
  • 为什么说React中的props是只读的?
  • 你有使用过formik库吗?说说它的优缺点
  • 你有用过哪些React的表单库吗?说说它们的优缺点
  • 如果组件的属性没有传值,那么它的默认值是什么?
  • 可以使用TypeScript写React应用吗?怎么操作?
  • super()super(props)有什么区别?
  • 你有使用过loadable组件吗?它帮我们解决了什么问题?
  • 你有使用过suspense组件吗?它帮我们解决了什么问题?
  • 怎样动态导入组件?
  • 如何给非控组件设置默认的值?
  • 怎么在React中引入其它的UI库,例如Bootstrap
  • 怎样将事件传递给子组件?
  • 怎样使用Hooks获取服务端数据?
  • 使用Hooks要遵守哪些原则?
  • render方法的原理你有了解吗?它返回的数据类型是什么?
  • useEffect和useLayoutEffect有什么区别?
  • 在React项目中你用过哪些动画的包?
  • React必须使用JSX吗?
  • 自定义组件时render是可选的吗?为什么?
  • 需要把keys设置为全局唯一吗?
  • 怎么定时更新一个组件?
  • React根据不同的环境打包不同的域名?
  • 使用webpack打包React项目,怎么减小生成的js大小?
  • 在React中怎么使用async/await?
  • 你阅读了几遍React的源码?都有哪些收获?你是怎么阅读的?
  • 什么是React.forwardRef?它有什么作用?
  • 写个例子说明什么是JSX的内联条件渲染
  • 在React中怎么将参数传递给事件?
  • React的事件和普通的HTML事件有什么不同?
  • 在React中怎么阻止事件的默认行为?
  • 你最喜欢React的哪一个特性(说一个就好)?
  • 在React中什么时候使用箭头函数更方便呢?
  • 你最不喜欢React的哪一个特性(说一个就好)?
  • 说说你对React的reconciliation(一致化算法)的理解
  • 使用PropTypes和Flow有什么区别?
  • 怎样有条件地渲染组件?
  • 在JSX中如何写注释?
  • constructor和getInitialState有不同?
  • 写例子说明React如何在JSX中实现for循环
  • 为什么建议Fragment包裹元素?它的简写是什么?
  • 你有用过React.Fragment吗?说说它有什么用途?
  • 在React中你有遇到过安全问题吗?怎么解决?
  • React中如何监听state的变化?
  • React什么是有状态组件?
  • React v15中怎么处理错误边界?
  • React Fiber它的目的是解决什么问题?
  • React为什么不要直接修改state?如果想修改怎么做?
  • create-react-app有什么好处?
  • 装饰器(Decorator)在React中有什么应用?
  • 使用高阶组件(HOC)实现一个loading组件
  • 如何用React实现滚动动画?
  • 说出几点你认为的React最佳实践
  • 你是如何划分React组件的?
  • 举例说明如何在React创建一个事件
  • 如何更新组件的状态?
  • 怎样将多个组件嵌入到一个组件中?
  • React的render中可以写{if else}这样的判断吗?
  • React为什么要搞一个Hooks?
  • React Hooks帮我们解决了哪些问题?
  • 使用React的memo和forwardRef包装的组件为什么提示children类型不对?
  • 有在项目中使用过Antd吗?说说它的好处
  • 在React中如果去除生产环境上的sourcemap?
  • 在React中怎么引用sass或less?
  • 组件卸载前,加在DOM元素的监听事件和定时器要不要手动清除?为什么?
  • 为什么标签里的for要写成htmlFor呢?
  • 状态管理器解决了什么问题?什么时候用状态管理器?
  • 状态管理器它精髓是什么?
  • 函数式组件有没有生命周期?为什么?
  • 在React中怎么引用第三方插件?比如说jQuery等
  • React的触摸事件有哪几种?
  • 路由切换时同一组件无法重新渲染的有什么方法可以解决?
  • React16新特性有哪些?
  • 你有用过哪些React的UI库?它们的优缺点分别是什么?
  • <div onClick={handlerClick}>单击</div><div onClick={handlerClick(1)}>单击</div>有什么区别?
  • 在React中如何引入图片?哪种方式更好?
  • 在React中怎么使用字体图标?
  • React的应用如何打包发布?它的步骤是什么?
  • ES6的语法'...'在React中有哪些应用?
  • 如何封装一个React的全局公共组件?
  • 在React中组件的props改变时更新组件的有哪些方法?
  • immutable的原理是什么?
  • 你对immutable有了解吗?它有什么作用?
  • 如何提高组件的渲染效率呢?
  • 在React中如何避免不必要的render?
  • render在什么时候会被触发?
  • 写出React动态改变class切换组件样式
  • React中怎么操作虚拟DOM的Class属性?
  • 为什么属性使用className而不是class呢?
  • 请说下react组件更新的机制是什么?
  • 怎么在JSX里属性可以被覆盖吗?覆盖的原则是什么?
  • 怎么在JSX里使用自定义属性?
  • 怎么防止HTML被转义?
  • 经常用React,你知道React的核心思想是什么吗?
  • 在React中我们怎么做静态类型检测?都有哪些方法可以做到?
  • 在React中组件的state和setState有什么区别?
  • React怎样跳过重新渲染?
  • React怎么判断什么时候重新渲染组件呢?
  • 什么是React的实例?函数式组件有没有实例?
  • 在React中如何判断点击元素属于哪一个组件?
  • 在React中组件和元素有什么区别?
  • 在React中声明组件时组件名的第一个字母必须是大写吗?为什么?
  • 举例说明什么是高阶组件(HOC)的反向继承?
  • 有用过React Devtools吗?说说它的优缺点分别是什么?
  • 举例说明什么是高阶组件(HOC)的属性代理?
  • React的isMounted有什么作用?
  • React组件命名推荐的方式是哪个?为什么不推荐使用displayName?
  • React的displayName有什么作用?
  • 说说你对React的组件命名规范的理解
  • 说说你对React的项目结构的理解
  • React16废弃了哪些生命周期?为什么?
  • 怎样在React中开启生产模式?
  • React中getInitialState方法的作用是什么?
  • React中你知道creatClass的原理吗?
  • React中验证props的目的是什么?
  • React中你有使用过getDefaultProps吗?它有什么作用?
  • React中你有使用过propType吗?它有什么作用?
  • React中怎么检验props?
  • React.createClass和extends Component的区别有哪些?
  • 高阶组件(HOC)有哪些优点和缺点?
  • 给组件设置很多属性时不想一个个去设置有什么办法可以解决这问题呢?
  • React16跟之前的版本生命周期有哪些变化?
  • 怎样实现React组件的记忆?原理是什么?
  • 创建React动画有哪些方式?
  • 为什么建议不要过渡使用Refs?
  • 在React使用高阶组件(HOC)有遇到过哪些问题?如何解决?
  • 在使用React过程中什么时候用高阶组件(HOC)?
  • 说说React diff的原理是什么?
  • React怎么提高列表渲染的性能?
  • 使用ES6的class定义的组件不支持mixins了,那用什么可以替代呢?
  • 为何说虚拟DOM会提高性能?
  • React的性能优化在哪个生命周期?它优化的原理是什么?
  • 你知道的React性能优化有哪些方法?
  • 举例说明在React中怎么使用样式?
  • React有哪几种方法来处理表单输入?
  • 什么是浅层渲染?
  • 你有做过React的单元测试吗?如果有,用的是哪些工具?怎么做的?
  • 在React中什么是合成事件?有什么用?
  • 使用React写一个todo应用,说说你的思路
  • React16的reconciliation和commit分别是什么?
  • React的函数式组件有没有生命周期?
  • useState和this.state的区别是什么?
  • 请说说什么是useImperativeHandle?
  • 请说说什么是useReducer?
  • 请说说什么是useRef?
  • 请说说什么是useEffect?
  • 举例说明useState
  • 请说说什么是useState?为什么要使用useState?
  • 请描述下你对React的新特性Hooks的理解?它有哪些应用场景?
  • 说说你对Error Boundaries的理解
  • 说说你对Fiber架构的理解
  • 说说你是怎么理解React的业务组件和技术组件的?
  • 为什么建议setState的第一个参数是callback而不是一个对象呢?
  • 展示组件和容器组件有什么区别?
  • Mern和Yeoman脚手架有什么区别?
  • 你有在项目中使用过Yeoman脚手架吗?
  • 你有在项目中使用过Mern脚手架吗?
  • shouldComponentUpdate方法是做什么的?
  • 怎样在React中使用innerHTML?
  • 你有写过React的中间件插件吗?
  • React的中间件机制是怎么样的?这种机制有什么作用?
  • React中你用过哪些第三方的中间件?
  • 不用脚手架,你会手动搭建React项目吗?
  • 请说说React中Portal是什么?
  • React中修改prop引发的生命周期有哪几个?
  • React多个setState调用的原理是什么?
  • React中调用setState会更新的生命周期有哪几个?
  • React中setState的第二个参数作用是什么呢?
  • React中的setState是同步还是异步的呢?为什么state并不一定会同步更新?
  • React中的setState批量更新的过程是什么?
  • React中的setState执行机制是什么呢?
  • 在React中遍历的方法有哪些?它们有什么区别呢?
  • 请说说你对React的render方法的理解
  • props.children.map和js的map有什么区别?为什么优先选择React的?
  • 有用过React的严格模式吗?
  • React中的setState和replaceState的区别是什么?
  • React中的setState缺点是什么呢?
  • 有用过React的Fragment吗?它的运用场景是什么?
  • React组件间共享数据方法有哪些?
  • React的状态提升是什么?使用场景有哪些?
  • 简单描述下你有做过哪些React项目?
  • 在构造函数中调用super(props)的目的是什么?
  • 你是如何学习React的?
  • 从旧版本的React升级到新版本的React有做过吗?有遇到过什么坑?
  • 你用过React版本有哪些?
  • 有用过React的服务端渲染吗?怎么做的?
  • React的mixins有什么作用?适用于什么场景?
  • React怎么拿到组件对应的DOM元素?
  • 请描述下事件在React中的处理方式是什么?
  • JSX和HTML有什么区别?
  • React的书写规范有哪些?
  • create-react-app创建新运用怎么解决卡的问题?
  • 使用React的方式有哪几种?
  • 说说你对reader的context的理解
  • 同时引用这三个库React.js、React-dom.js和babel.js它们都有什么作用?
  • 你知道Virtual DOM的工作原理吗?
  • 你阅读过React的源码吗?简要说下它的执行流程
  • React中怎样阻止组件渲染?
  • React非兄弟组件如何通信?
  • React兄弟组件如何通信?
  • React非父子组件如何通信?
  • React父子组件如何通信?
  • React组件间的通信有哪些?
  • 类组件和函数式组件有什么区别?
  • React自定义组件你写过吗?说说看都写过哪些?
  • React组件的state和props两者有什么区别?
  • React有几种构建组件的方式?可以写出来吗?
  • React中遍历时为什么不用索引作为唯一的key值?
  • React中的key有什么作用?
  • React中除了在构造函数中绑定this,还有别的方式吗?
  • 在React中页面重新加载时怎样保留数据?
  • 请描述下React的事件机制
  • 怎样在React中创建一个事件?
  • 在React中无状态组件有什么运用场景?
  • 描述下在React中无状态组件和有状态组件的区别是什么?
  • 写一个React的高阶组件(HOC)并说明你对它的理解
  • React中可以在render访问refs吗?为什么?
  • React中refs的作用是什么?有哪些应用场景?
  • 请描述你对纯函数的理解?
  • 受控组件和非受控组件有什么区别?
  • React中什么是非控组件?
  • React中什么是受控组件?
  • React中发起网络请求应该在哪个生命周期中进行?为什么?
  • 说说React的生命周期有哪些?
  • 说说你对“在React中,一切都是组件”的理解
  • 写React你是用es6还是es5的语法?有什么区别?
  • 浏览器为什么无法直接JSX?怎么解决呢?
  • 在使用React过程中你都踩过哪些坑?你是怎么填坑的?
  • 说说你喜欢React的原因是什么?它有什么优缺点?
  • 如何解决引用类型在pureComponent下修改值的时候,页面不渲染的问题?
  • createElement与cloneElement两者有什么区别?
  • 解释下React中Element 和Component两者的区别是什么?
  • 解释下React中component和pureComponent两者的区别是什么?
  • React的虚拟DOM和vue的虚拟DOM有什么区别?
  • 你觉得React上手快不快?它有哪些限制?
  • 说说你对声明式编程的理解?
  • React与angular、vue有什么区别?
  • React是哪个公司开发的?
  • React是什么?它的主要特点是什么?
  • 简要描述下你知道的React工作原理是什么?
  • 在React中怎样改变组件状态,以及状态改变的过程是什么?
  • 在React中你是怎么进行状态管理的?
  • React声明组件有哪几种方法,各有什么不同?

ReactNative

  • 如何在React Native中设置环境变量?
  • 请描述下Code Push的原理是什么?
  • React Native怎样查看日记?
  • React Native怎样测试?
  • React Native怎样调试?
  • React Native和React有什么区别?
  • 有做过React Native项目吗?

React-Router

  • React-Router怎么获取历史对象?
  • React-Router怎么获取URL的参数?
  • 在history模式中push和replace有什么区别?
  • React-Router怎么设置重定向?
  • React-Router 4中<Router>组件有几种类型?
  • React-Router 3和React-Router 4有什么变化?添加了什么好的特性?
  • React-Router的实现原理是什么?
  • React-Router 4的switch有什么用?
  • React-Router的路由有几种模式?
  • React-Router 4怎样在路由变化时重新渲染同一个组件?
  • React-Router的<Link>标签和<a>标签有什么区别?
  • React的路由和普通路由有什么区别?
  • 请你说说React的路由的优缺点?
  • 请你说说React的路由是什么?

Redux/Mobox

  • 你有了解Rxjs是什么吗?它是做什么的?
  • 在Redux中怎么发起网络请求?
  • Redux怎样重置状态?
  • Redux怎样设置初始状态?
  • Context api可以取代Redux吗?为什么?
  • 推荐在reducer中触发Action吗?为什么?
  • Redux怎么添加新的中间件?
  • redux-saga和redux-thunk有什么本质的区别?
  • 在React中你是怎么对异步方案进行选型的?
  • 你知道redux-saga的原理吗?
  • 你有使用过redux-saga中间件吗?它是干什么的?
  • Redux中异步action和同步action最大的区别是什么?
  • Redux和vuex有什么区别?
  • Redux的中间件是什么?你有用过哪些Redux的中间件?
  • 说说Redux的实现流程
  • Mobx的设计思想是什么?
  • Redux由哪些组件构成?
  • Mobx和Redux有什么区别?
  • 在React项目中你是如何选择Redux和Mobx的?说说你的理解
  • 你有在React中使用过Mobx吗?它的运用场景有哪些?
  • Redux的thunk作用是什么?
  • Redux的数据存储和本地储存有什么区别?
  • 在Redux中,什么是reducer?它有什么作用?
  • 举例说明怎么在Redux中定义action?
  • 在Redux中,什么是action?
  • 在Redux中,什么是store?
  • 为什么Redux能做到局部渲染呢?
  • 说说Redux的优缺点分别是什么?
  • Redux和Flux的区别是什么?
  • Redux它的三个原则是什么?
  • 什么是单一数据源?
  • 什么是Redux?说说你对Redux的理解?有哪些运用场景?

Flux

  • 请说说点击按钮触发到状态更改,数据的流向?
  • 请描述下Flux的思想
  • 什么是Flux?说说你对Flux的理解?有哪些运用场景?
查看原文

Lile_Black 赞了文章 · 8月7日

一种新的组件协作模式 - Bit Components

作者:杨哲迪

平常大家在日常的开发中,会积累丰富的业务组件,无论是自己造轮子也好或者基于优秀的组件库二次封装也好。久而久之,组件越来越多,管理与共享这些业务组件就可能会成为负担。本文要介绍的Bit就为这种场景提供了一种新的解决思路。

Bit是什么?

Bit利用组件使它们不仅可以在应用程序内部重用,而且提供了一个生态系统,可在应用程序之间以及跨存储库共享组件。

Bit是一个工具,可以在不同项目 共享同步 组件。

Bit简化了UI组件上的协作过程,团队成员可以共享,维护和同步来自不同项目的隔离组件。

Bit是怎样工作的?

本文将会从三个模块:组件组成组件生命周期Bit工作流来介绍Bit和传统的组件库管理有哪些区别?

组件组成

每个组件,Bit都会存储三种元素

  • 源代码 (包括test和documentation)
  • 组件的内容不仅是源代码本身,还可以包括其他相关文件,例如样式文件,资产(图像,字体),测试代码,文档。
  • 依赖图谱
  • 将源添加到Bit组件时,Bit会分析它包含的所有依赖项(即,代码中的import和require语句)
  • 依赖图使组件成为独立的,并允许在项目中移动组件而不会丢失任何引用。
  • 配置项
  • Compiler,bit提供编译器 bit.envs/compilers/react@1.0.14  
  • Tester,bit提供的扩展,用于运行与组件关联的测试并返回状态。

源代码有点类似于原来组件库的源代码部分,依赖图谱类似于webpack在分析import依赖阶段生成的map,而配置项就类似于webpack.config.js,Bit会帮你把组件打包编译,所以这里可以看出Bit之所以能够如此方便的完成组件封装,有一部分原因是Bit帮我们把组件库打包编译的工作做掉了。

组件生命周期

生产组件

生产组件有三个动作,track、version、export

  1. Track: 在workspace里指定组件的构成文件。

  1. Version: 标记版本会锁定该版本下文件的内容和元数据。如果组件具有编译器,Bit会构建组件并锁定所构建后的组件(将其视为类似于git commit和npm发布同时进行)。

  1. Export: export命令将为该组件创建一个唯一ID。唯一的ID是远程作用域名称和本地组件名称,远程作用域名称可以包括任何名称空间(username.collection?.namespace)。https://bit.dev/yangzhedi/test/list

消费组件

只要上传到远程服务器上,该组件便可供其他工作空间使用。使用组件的方式是通过install或import。

  • Install: Bit会将组件作为常规NPM软件包添加到node_modules文件夹中。安装组件时(bit install / npm install / yarn add),该组件将添加到package.json中,指向已安装的版本:"@bit/yangzhedi.test.list": "0.0.1"。不会更改已安装组件的代码。

import List from '@bit/yangzhedi.test.list';

  • Import: Bit会将组件添加到工作区-组件文件夹并跟踪其修改。在导入时,您可以看到package.json指向本地文件:"@bit/yangzhedi.test.list": "file:./components/list",

  • 跟踪代码修改,并可以将其导出为新版本。 弹出(eject):如果导出了新版本,则可以还原为已安装的组件。在这种情况下,package.json将更新回"@bit/yangzhedi.test.list": "0.0.2"

Bit workflow

Ad-hoc Sharing(临时共享)

              |            

这种工作流适用于:

  • 有几种带有UI组件库的产品。 
  • 需要在项目之间保持一致的UI / UX。 
  • 没有时间/能力来组成专门的团队来维护组件。

带来的好处

  • 无需花费很长时间为UI组件构建和维护共享库,共享现有项目中已经开发的组件。 
  • 使用bit.dev作为收集门户中所有组件的发现门户,包括demo和文档。 
  • 在任何项目中都可以更改组件代码,可以保留使用项目中组件的本地修改,并且仍与传入的更新合并。 
  • 可以使用npm / yarn来安装组件,因此它们适合项目开发人员的正常工作流程。

风险

  • 规范组件的export流程

Centralized Library(中央图书馆)

这种工作流适用于:

  • 需要共享UI组件的集中式存储库 
  • 一个专门的团队来管理共享的UI组件 
  • 存在使用共享组件的多个项目

带来的好处

  • 对于生产者
  • Bit以更高的粒度自动打包每个组件,而不会增加维护单独包的开销,自动根据组件依赖关系的变化对组件进行版本控制
  • 在没有项目上下文的情况下在本地构建组件会缩短组件在其他项目上的调用方式上的反馈循环。 
  • 将组件发布到bit.dev,使其对所有团队可见,从而提高了组件的采用率 Bit有助于控制谁在更改组件。
  • 对于消费者
  • 获取每个项目所需的较小的分立组件对于: 减少应用程序的捆绑包大小。
  • 可以只引入他们需要的组件。 减少最终打包体积,缩短构建时间。
  • 更细颗粒度的调用组件库可以提高项目的稳定性。

风险

  • 如果有一个团队专门来维护统一的组件库,颗粒度太细会成为负担

和npm比有啥不一样?

  • Bit基于代码分析使代码打包自动化。
  • Bit可以在不离开项目上下文得情况下,访问到程序包 (bit import)。
  • 对于生产者来说,不需要把组件单独复制出来,就可以直接上传
  • 对于消费者来说,想要修改组件也不需要clone组件库,而是在项目里就可以直接修改
  • Bit可以在任何项目中使用组件代码,然后直接在其中进行更改。

以上,本文主要介绍了bit cli的使用方法,bit同时也提供了一个类似于npm.org的平台网站:bit.dev,而且还可以在bit.dev上直接看到组件的样式以及调用方式,连docs都不用自己写了。

参考:

https://docs.bit.dev/docs/quick-start

招聘

字节跳动商业化前端团队招人啦!可以每日与技术网红畅谈理想、参加技术大牛交流分享、享受一日四餐、顶级极客装备、免费健身房,与优秀的人做有挑战的事情,这种地方哪里找?快来加入我们吧!

简历投递邮箱:yangzhedi@bytedance.com

查看原文

赞 4 收藏 1 评论 1

Lile_Black 关注了专栏 · 7月29日

pixijs游戏开发

专注于pixi.js技术进行web游戏开发。 聚合优秀的pixi.js资源,让各个水平的pixier在这里都能有所收获。 招募专栏作者,欢迎自荐~~

关注 115

Lile_Black 赞了文章 · 7月20日

我在阿里招前端,我该怎么帮你?

我是谁?为什么写这篇文章?

我是淘宝技术部的一名普通的前端技术专家,花名磐冲。每年都想给团队内招几个同学,但是努力了几年,一个都没有招进来。是我看简历太少了吗?不是,只算内部简历系统,我看过的简历至少上千。是我要求太严格吗?也许是吧,不过,我电话面试拒绝的同学,只有1位在一段时间后,入职了另一个部门。

好吧,我承认,我自己在招聘上可能是有点没找到方法。但是,看了那么多简历,经历了那么多次面试,我最大的感受却是惋惜。因为有好多同学,在电话那头我听出了努力,听出了能力,听出了激情,但是却没有听到亮点、和让我觉得,能够继续闯过下一关的能力。

我面试过的同学,在结束的时候,我都会指出问题,并给出学习建议。大部分同学不是不够努力,不是不够聪明,而是没有找对方法,没有切中要害。我总结了一下之前所有的面试经历,以及常见的问题,写下这篇文章,希望能够给前端的同学,不论是否来面试阿里的职位,有一个参考。同时,也是写下我自己总结的方法,希望能帮助到其他技术相关的同学。

我们想要的同学

JD

业务背景

淘宝内部最大创新项目之一,大团队已有百人规模,大部分项目处于保密阶段,前景远大

职位描述

1.负责组件库与业务页面开发。
2.带领团队完成技术产品实现。
3.负责大型多应用架构设计。
4.利用前端技术与服务端协同完成团队业务目标。

职位要求

0.掌握图形学,webgl或熟练使用threejs框架,熟练canvas相关渲染及动画操作的优先。
1.熟练掌握JavaScript。
2.熟悉常用工程化工具,掌握模块化思想和技术实现方案。
3.熟练掌握React前端框架,了解技术底层。同时了解vue以及angular等其他框架者优先。
4.熟练掌握react生态常用工具,redux/react-router等。
5.熟悉各种Web前端技术,包括HTML/XML/CSS等,有基于Ajax的前端应用开发经验。
6.有良好的编码习惯,对前端技术有持续的热情,个性乐观开朗,逻辑性强,善于和各种背景的人合作。
7.具有TS/移动设备上前端开发/NodeJS/服务端开发等经验者优先。

翻译一下JD

为什么起这个标题呢?因为有很多人看到职位描述,可能就在和自己做的事情一一比对,把关键字都核对上。而很多前端同学看到职位要求第一条里的图形学,可能就开始打退堂鼓了。或者看到几个关键字自己都认识,就觉得没问题,还挺简单的。

就这样望而却步真的好吗?为什么职位描述看着简单,面试却这么难呢?你真的读懂这份职位描述了吗?

现在,不妨先停一下,就上面的问题,我们来细细品一下。什么叫读懂职位描述呢?从我个人的理解,读懂职位描述,应该是读懂这个职位需要哪些基础能力,以及可能遇到哪些挑战。我们写自己简历的时候,“精通react”和“熟练使用react”,相信大家不会随意去写。同样的,JD里面的:掌握、熟练掌握、了解、熟悉,也不是随意写的,这代表了团队对新同学的能力要求。

回想写自己简历的时候,我们会对这个前缀扪心自问一下。因为会担心一旦写了精通,面试官的问题会更难,甚至觉得只有源码倒背如流的人,才能称得上精通。当然也会有同学非常自信,用react做过几个项目,就写上了精通react。

这两种都可以称为精通,也都不可以。没有客观标准,又怎么去衡量呢?而标准在哪里呢?所以在这里,我从阿里面试官角度,给出我认为的标准,尽可能的做到客观可量化。那么,基于上面这份职位标准,我来翻译一下职位要求:

首先,总览全部的要求,会发现这个职位虽然提到了3d相关的技能,但是大部分却是应用开发相关的能力,所以这个职位并不是想找专业的3d领域同学,而是需要一个工程化能力强,对3d有了解的同学。

0.掌握图形学,webgl或熟练使用threejs框架,熟练canvas相关渲染及动画操作的优先。

初级:

  • 学习过图形学相关知识,知道矩阵等数学原理在动画中的作用,知道三维场景需要的最基础的构成,能用threejs搭3d场景,知道webgl和threejs的关系。
  • 知道canvas是干嘛的,聊到旋转能说出canvas的api。
  • 知道css动画,css动画属性知道关键字和用法(换句话说,电话面试会当场出题要求口喷css动画,至少能说对大概,而不是回答百度一下就会用)。
  • 知道js动画,能说出1~2个社区js动画库,知道js动画和css动画优缺点以及适用场景。
  • 知道raf和其他达到60fps的方法。

中级:

  • 如果没有threejs,你也能基于webgl自己封装一个简单的threejs出来。
  • 聊到原理能说出四元数,聊到鼠标操作能提到节流,聊到性能能提到restore,聊到帧说出raf和timeout的区别,以及各自在优化时候的作用。
  • 知道怎样在移动端处理加载问题,渲染性能问题。
  • 知道如何结合native能力优化性能。
  • 知道如何排查性能问题。对chrome动画、3d、传感器调试十分了解。

高级:

  • 搭建过整套资源加载优化方案,能说明白整体方案的各个细节,包括前端、客户端、服务端分别需要实现哪些功能点、依赖哪些基础能力,以及如何配合。
  • 设计并实现过前端动画引擎,能说明白一个复杂互动项目的技术架构,知道需要哪些核心模块,以及这些模块间如何配合。
  • 有自己实现的动画相关技术方案产出,这套技术方案必须是解决明确的业务或技术难点问题的。为了业务快速落地而封装一个库,不算这里的技术方案。如果有类似社区方案,必须能从原理上说明白和竞品的差异,各自优劣,以及技术选型的原因。
1.熟练掌握JavaScript。

初级:

  • JavaScript各种概念都得了解,《JavaScript语言精粹》这本书的目录都得有概念,并且这些核心点都能脱口而出是什么。这里列举一些做参考:
  • 知道组合寄生继承,知道class继承。
  • 知道怎么创建类function + class。
  • 知道闭包在实际场景中怎么用,常见的坑。
  • 知道模块是什么,怎么用。
  • 知道event loop是什么,能举例说明event loop怎么影响平时的编码。
  • 掌握基础数据结构,比如堆、栈、树,并了解这些数据结构计算机基础中的作用。
  • 知道ES6数组相关方法,比如forEach,map,reduce。

中级:

  • 知道class继承与组合寄生继承的差别,并能举例说明。
  • 知道event loop原理,知道宏微任务,并且能从个人理解层面说出为什么要区分。知道node和浏览器在实现loop时候的差别。
  • 能将继承、作用域、闭包、模块这些概念融汇贯通,并且结合实际例子说明这几个概念怎样结合在一起。
  • 能脱口而出2种以上设计模式的核心思想,并结合js语言特性举例或口喷基础实现。
  • 掌握一些基础算法核心思想或简单算法问题,比如排序,大数相加。
2.熟悉常用工程化工具,掌握模块化思想和技术实现方案。

初级:

  • 知道webpack,rollup以及他们适用的场景。
  • 知道webpack v4和v3的区别。
  • 脱口而出webpack基础配置。
  • 知道webpack打包结果的代码结构和执行流程,知道index.js,runtime.js是干嘛的。
  • 知道amd,cmd,commonjs,es module分别是什么。
  • 知道所有模块化标准定义一个模块怎么写。给出2个文件,能口喷一段代码完成模块打包和执行的核心逻辑。

中级:

  • 知道webpack打包链路,知道plugin生命周期,知道怎么写一个plugin和loader。
  • 知道常见loader做了什么事情,能几句话说明白,比如babel-loader,vue-loader。
  • 能结合性能优化聊webpack配置怎么做,能清楚说明白核心要点有哪些,并说明解决什么问题,需要哪些外部依赖,比如cdn,接入层等。
  • 了解异步模块加载的实现原理,能口喷代码实现核心逻辑。

高级:

  • 能设计出或具体说明白团队研发基础设施。具体包括但不限于:
  • 项目脚手架搭建,及如何以工具形态共享。
  • 团队eslint规范如何设计,及如何统一更新。
  • 工具化打包发布流程,包括本地调试、云构建、线上发布体系、一键部署能力。同时,方案不仅限于前端工程部分,包含相关服务端基础设施,比如cdn服务搭建,接入层缓存方案设计,域名管控等。
  • 客户端缓存及预加载方案。
3.熟练掌握React前端框架,了解技术底层。同时了解vue以及angular等其他框架者优先。

初级:

  • 知道react常见优化方案,脱口而出常用生命周期,知道他们是干什么的。
  • 知道react大致实现思路,能对比react和js控制原生dom的差异,能口喷一个简化版的react。
  • 知道diff算法大致实现思路。
  • 对state和props有自己的使用心得,结合受控组件、hoc等特性描述,需要说明各种方案的适用场景。
  • 以上几点react替换为vue或angular同样适用。

中级:

  • 能说明白为什么要实现fiber,以及可能带来的坑。
  • 能说明白为什么要实现hook。
  • 能说明白为什么要用immutable,以及用或者不用的考虑。
  • 知道react不常用的特性,比如context,portal。
  • 能用自己的理解说明白react like框架的本质,能说明白如何让这些框架共存。

高级:

  • 能设计出框架无关的技术架构。包括但不限于:
  • 说明如何解决可能存在的冲突问题,需要结合实际案例。
  • 能说明架构分层逻辑、各层的核心模块,以及核心模块要解决的问题。能结合实际场景例举一些坑或者优雅的处理方案则更佳。
4.熟练掌握react生态常用工具,redux/react-router等。

初级:

  • 知道react-router,redux,redux-thunk,react-redux,immutable,antd或同级别社区组件库。
  • 知道vue和angular对应全家桶分别有哪些。
  • 知道浏览器react相关插件有什么,怎么用。
  • 知道react-router v3/v4的差异。
  • 知道antd组件化设计思路。
  • 知道thunk干嘛用的,怎么实现的。

中级:

  • 看过全家桶源码,不要求每行都看,但是知道核心实现原理和底层依赖。能口喷几行关键代码把对应类库实现即达标。
  • 能从数据驱动角度透彻的说明白redux,能够口喷原生js和redux结合要怎么做。
  • 能结合redux,vuex,mobx等数据流谈谈自己对vue和react的异同。

高级:

  • 有基于全家桶构建复杂应用的经验,比如最近很火的微前端和这些类库结合的时候要注意什么,会有什么坑,怎么解决
5.熟悉各种Web前端技术,包括HTML/XML/CSS等,有基于Ajax的前端应用开发经验。

初级:

  • HTML方面包括但不限于:语义化标签,history api,storage,ajax2.0等。
  • CSS方面包括但不限于:文档流,重绘重排,flex,BFC,IFC,before/after,动画,keyframe,画三角,优先级矩阵等。
  • 知道axios或同级别网络请求库,知道axios的核心功能。
  • 能口喷xhr用法,知道网络请求相关技术和技术底层,包括但不限于:content-type,不同type的作用;restful设计理念;cors处理方案,以及浏览器和服务端执行流程;口喷文件上传实现;
  • 知道如何完成登陆模块,包括但不限于:登陆表单如何实现;cookie登录态维护方案;token base登录态方案;session概念;

中级:

  • HTML方面能够结合各个浏览器api描述常用类库的实现。
  • css方面能够结合各个概念,说明白网上那些hack方案或优化方案的原理。
  • 能说明白接口请求的前后端整体架构和流程,包括:业务代码,浏览器原理,http协议,服务端接入层,rpc服务调用,负载均衡。
  • 知道websocket用法,包括但不限于:鉴权,房间分配,心跳机制,重连方案等。
  • 知道pc端与移动端登录态维护方案,知道token base登录态实现细节,知道服务端session控制实现,关键字:refresh token。
  • 知道oauth2.0轻量与完整实现原理。
  • 知道移动端api请求与socket如何通过native发送,知道如何与native进行数据交互,知道ios与安卓jsbridge实现原理。

高级:

  • 知道移动端webview和基础能力,包括但不限于:iOS端uiwebview与wkwebview差异;webview资源加载优化方案;webview池管理方案;native路由等。
  • 登陆抽象层,能够给出完整的前后端对用户体系的整体技术架构设计,满足多业务形态用户体系统一。考虑跨域名、多组织架构、跨端、用户态开放等场景。
  • mock方案,能够设计出满足各种场景需要的mock数据方案,同时能说出对前后端分离的理解。考虑mock方案的通用性、场景覆盖度,以及代码或工程侵入程度。
  • 埋点方案,能够说明白前端埋点方案技术底层实现,以及技术选型原理。能够设计出基于埋点的数据采集和分析方案,关键字包括:分桶策略,采样率,时序性,数据仓库,数据清洗等。
6.有良好的编码习惯,对前端技术有持续的热情,个性乐观开朗,逻辑性强,善于和各种背景的人合作。

初级:

  • 知道eslint,以及如何与工程配合使用。
  • 了解近3年前端较重要的更新事件。
  • 面试过程中遇到答不出来的问题,能从逻辑分析上给出大致的思考路径。
  • 知道几个热门的国内外前端技术网站,同时能例举几个面试过程中的核心点是从哪里看到的。

高级:

  • 在团队内推行eslint,并给出工程化解决方案。
  • 面试过程思路清晰,面试官给出关键字,能够快速反应出相关的技术要点,但是也要避免滔滔不绝,说一堆无关紧要的东西。举例来说,当时勾股老师面试我的时候,问了我一个左图右文的布局做法,我的回答是:我自己总结过7种方案,其中比较好用的是基于BFC的,float的以及flex的三种。之后把关键css口喷了一下,然后css就面完了。
7.具有TS/移动设备上前端开发/NodeJS/服务端开发等经验者优先。
  • 根据了解的深度分初/中/高级。
  • 知道TS是什么,为什么要用TS,有TS工程化实践经验。
  • 知道移动端前端常见问题,包括但不限于:rem + 1px方案;预加载;jsbridge原理等。
  • 能说出大概的服务端技术,包括但不限于:docker;k8s;rpc原理;中后台架构分层;缓存处理;分布式;响应式编程等。

JD的要求很难吗?

首先,感谢你能看到这里,如果你是仔细看的,那么我更加感动了。而且你已经用实际行动,证明了你的学习能力和耐心。上面那么大篇幅的JD翻译,有一个问题,大家应该都有答案了:为什么职位描述看着简单,面试却这么难呢?然而,有些同学可能会嘲讽起来:写了那么多,我认识的有些阿里P6,P7也不是都会啊,大厂都是螺丝钉,也就面试时候问问,实际工作不还是if else,何况我又遇不到这些场景,我怎么可能知道。

在这里,我想严肃的说明的是:

  1. 我所认识的淘宝前端,以及我所在团队的P6同学,上面初级都能做到,中级至少覆盖60%,高级覆盖20%;P6+同学,中级覆盖80%以上,高级覆盖50%以上;P7同学高级覆盖80%以上。
  2. 我们团队的前端,每一个人都负责多个复杂业务项目(客观数据上:至少对接20+服务端接口,5个以上router配置,涉及多个用户角色的综合业务系统),以及一些通用能力,比如组件库等。不存在一个人只接一条业务线,只负责维护某几个组件这种螺丝钉式的工作。我不知道大厂都是螺丝钉的言论为什么会被复用到互联网企业,我个人感受是,如果我在阿里的工作是螺丝钉,那么我以前几份工作可能勉强算是螺纹。另外,如果你想要晋升,那么维护好这几个业务系统只是你的本职工作,晋升时请提供一些更高层面的思考和技术产出。
  3. if else也分鲜花和牛粪。有的人写的是[].reduce,而有的人写的是var temp = ''; for() { temp += 'xxx' }。另外,如果不知道原理,那么类似webpack这种明星级的技术产品,将永远与你无缘。冷静下来想想,webpack难道也只是if else吗?是,又不全是。

聪明的你应该看出来了,上面JD翻译里的初级、中级和高级,对应的就是我认为的,阿里p6/p6+/p7的能力标准,同时也是一张知识图谱。初级的要求更偏实际应用和基础原理,中级的要求是基于原理的拓展和复杂技术架构的应用,高级的要求是对跨栈、跨端,多领域结合产出综合方案的能力。而且,我们对技术的要求,都是能够与实际业务场景结合,或者能对提升工作效率有帮助的。空谈和尬想,或者只是百度来的文章,没有经过内化,那么面试过程中将被瞬间拆穿。

有时我会在boss直聘上直接打字面试,有时我也会听到面试过程中,电话那头传来键盘敲击的声音,甚至有时候我会主动让面试的同学去百度一下,或者挂电话思考一下,过15分钟再聊。我敢这么面试,因为我知道,我要的答案你查不出来,我看的是你真正理解的东西。能搜索到的,我不在乎,我也希望你去查,来为你更好的表现综合能力。

破局的方法

好了,如果看到这里,并没有把你劝退的话,那么让我们来点希望的曙光。这里用一句阿里土话来给大家一些安慰:不难,要你干嘛?

开篇我提到面试过那么多同学之后,我最大的感受是惋惜,因为有很多同学在我看来就差一点点,他有足够的个人能力,可能只是没有找到感觉。这里我例举两个比较典型的问题。

什么是亮点?

我相信这是很多同学心中的疑惑,而且事实上,我看到很多简历下面的面试记录都会写:缺乏亮点,暂不考虑。如果仔细看了上文,到这里还有这个疑惑,那么我觉得你需要再静下心来感受一下。

这里我不对亮点做明确的表述,我举一个例子来让大家更有体感一些:

A: 负责公司前端工作,使用webpack打包代码并发布线上。使用webpack配置对整体性能做优化,用happypack加快了打包速度。

B: 建设内部云构建体系,产出通用命令行指令工具;将发布、环境切换、快速回滚能力平台化,保证了线上环境稳定性;同时将研发流程量化管控,每周产出研发效能报告。

如果你是面试官,在简历的大海里看一个项目描述,什么最吸引你的眼球呢?是webpack,happypack的关键字吗?还是一句话就让你想到这件事的复杂性,和这个系统带来的巨大价值?

没有场景怎么办?

这也是很多同学经常遇到的问题。上面例举了那么多技术点,而我在的环境,前端就我一个,甚至服务端我都要写一点,哪有精力去搞这种大规模团队用到的东西?

首先,时间靠自己合理规划。我和老婆两个人自己带孩子,有两个娃,每天平均9点下班,我每天回家收拾玩具,孩子睡得晚可能需要再陪玩一下,周末我带孩子为主,但是我去年仍然白金了2个ps4的游戏。

在时间问题排除之后,我建议分三个阶段:

  1. 毕业3年以内的阶段:不用着急,你的选择很多,你可以核对上面初级的点,看自己是否都做到了,没做到就去好好学习吧,初级的技术要点对团队规模没有依赖,一个人也能做到极致。如果你所处的环境已经有2个人,可以同时关注中级和高级的点,不要觉得人少就不去尝试,放手去做,过程中会有实打实的收获。
  2. 毕业5年以内的阶段:不论你处的环境团队规模如何,请开始着眼于中级和高级相关能力,人少就不需要研发提效了吗?我在segmentFault上发的第一篇文章,是如何用travis和github做一键部署,那时候我还没有去淘宝,我所在的团队也没有用到这个能力,这篇文章是我自己的个人项目用到的。而整个过程同样涉及到了研发效能的方方面面。
  3. 毕业8年以内的阶段:请开始着眼于高级相关的技术方案产出。我以组件动态化为例,我早年维护手机淘宝的整个交易链路H5页面,所有页面的ui部分都是细粒度组件化抽离,通过配置下发页面结构的。即使一个人维护一个页面,也要竭尽所能去思考好的技术方案。这种高度动态的设计,带来的好处是,每年双十一,80%的需求交给pd自己处理就行了,剩下流转到我手上需要开发的需求,都是新增交互,或者之前抽象不足的组件。所以当时我在的团队,3个人在维护了包括手淘首页、商品详情和正逆向交易链路所有H5页面,同时还有额外精力去支持大促会场页。更好的技术思考和设计,一定能给你带来更多的可能性,而系统的优雅程度,一定不是靠业务代码的堆砌,而是作为技术核心的你,如何去思考。

我想怎么帮你

我相信每个人都是能快速成长的,只是每个人缺少的东西不同。有的人少了些脚踏实地,有的人少了些登高望远的机会,更多的人或许只是没有找到那条正确的路。

我希望这篇文章能够帮助到正在前端领域努力的人,也希望这一篇文章就能成为指路明灯之一。但同时我也深知,每个人都是不一样的,所以,我这里留下联系方式,
需要的同学可以加微信:vianvio
编组.png
加备注:前端同路人。我可以给你做模拟面试,同时给出我认为的,适合你的发展思路和建议,当然也可以帮你内推。

另外,目前我们成立了一个模拟面试群,有定期活动,可以参考 https://github.com/vianvio/FE...
欢迎有兴趣的同学来参加。

介绍一下我所在的团队

我在阿里巴巴淘宝技术部-ihome业务。目前,ihome正在深耕家居家装行业,纵向深入行业内部,希望能给行业带来一些创新。目前可对外公开的产品和业务形态有:躺平App、位于青岛和宁波的桔至生活门店。我们还有更多有趣、充满挑战和超出你想象的业务。我们期待有志之士的加入!

如果你愿意来和我们一起相信,那请发送简历过来,我们一定会一起看见!

前端简历请发送到:yefei.niuyf@alibaba-inc.com 或 lijie.slj@alibaba-inc.com
主攻3d方向的同学,简历请发送到:jiangcheng.wxd@alibaba-inc.com
java简历请发送到:xiaoxian.zzy@taobao.com 或 wuxin.sn@taobao.com
客户端简历请发送到:fangying.fy@alibaba-inc.com

或许有人会觉得奇怪,联系方式写在最后,还有多少人能看到,这里我引用马爸爸和逍遥子大佬对阿里价值观的解读,来解释一下:我们的价值观是为了帮助我们寻找同路的人。

感谢你陪我一起走到这篇文章的最后,如果你觉得这篇文章已经对你有很大帮助了,那就请我喝杯咖啡吧~

查看原文

赞 219 收藏 134 评论 20

Lile_Black 赞了文章 · 7月20日

2020最新:100道有答案的前端面试题(上)

网上的面试题一大堆,鱼龙混杂,一方面多数题目质量不高,另一方面有答案的很少,即使拿到面试题对自己的帮助也不大。

最近我花了一些时间,为大家整理了2020年各一、二线互联网公司的前端面试题,内容包括JavaScript、算法、网络&安全、Vue、React等大量的前端知识点和相关面试题。答案和解析也整理在文中了,整理不易,麻烦各位走过路过的壮士给颗star,如果可以star fork watch三连更好,感谢😄 🙏

因篇幅太长,本文收录前50道,后50道将在下篇更新,欢迎关注。

173382ede7319973.gif

1.写一个 mySetInterVal(fn, a, b),每次间隔 a,a+b,a+2b 的时间,然后写一个 myClear,停止上面的 mySetInterVal

公司:头条

分类:JavaScript

答案&解析

2.合并二维有序数组成一维有序数组,归并排序的思路

公司:头条

分类:算法

答案&解析

3.斐波那契数列

公司:腾讯、CVTE、微软

分类:算法

答案&解析

4.字符串出现的不重复最长长度

公司:腾讯

分类:算法

答案&解析

5.介绍chrome 浏览器的几个版本

公司:滴滴

分类:网络&安全

答案&解析

6.React 项目中有哪些细节可以优化?实际开发中都做过哪些性能优化

公司:滴滴、掌门一对一、网易、有赞、沪江、喜马拉雅、酷家乐、快手

分类:React

答案&解析

7.react 最新版本解决了什么问题 加了哪些东西

公司:滴滴

分类:React

答案&解析

8.说一下 Http 缓存策略,有什么区别,分别解决了什么问题

公司:滴滴、头条、网易、易车、脉脉、掌门一对一、虎扑、挖财、爱范儿

分类:网络&安全

答案&解析

9.介绍防抖节流原理、区别以及应用,并用JavaScript进行实现

公司:滴滴、虎扑、挖财、58、头条

分类:JavaScript、编程题

答案&解析

10.前端安全、中间人攻击

公司:滴滴

分类:网络&安全

答案&解析


前端刷题神器

扫码进入前端面试星球🌍,解锁刷题神器,还可以获取800+道前端面试题一线常见面试高频考点

173382ede7319973.gif


11.对闭包的看法,为什么要用闭包?说一下闭包原理以及应用场景

公司:滴滴、携程、喜马拉雅、微医、蘑菇街、酷家乐、腾讯应用宝、安居客

分类:JavaScript

答案&解析

12.css 伪类与伪元素区别

公司:滴滴

分类:Css

答案&解析

13.有一堆整数,请把他们分成三份,确保每一份和尽量相等(11,42,23,4,5,6 4 5 6 11 23 42 56 78 90)

公司:滴滴

分类:算法

答案&解析

14.实现 lodash 的_.get

公司:滴滴

分类:JavaScript

答案&解析

15.实现 add(1)(2)(3)

公司:滴滴

分类:JavaScript

答案&解析

16.实现链式调用

公司:滴滴

分类:JavaScript

答案&解析

17.React 事件绑定原理

公司:滴滴、沪江

分类:React

答案&解析

18.类数组和数组的区别,dom 的类数组如何转换成数组

公司:海康威视

分类:JavaScript

答案&解析

19.webpack 做过哪些优化,开发效率方面、打包策略方面等等

公司:滴滴、快手、掌门一对一、高思教育

分类:工程化

答案&解析

20.说一下事件循环机制(node、浏览器)

公司:滴滴、伴鱼、高德、自如、虎扑、58

分类:Node、JavaScript

答案&解析

21.如何封装 node 中间件

公司:滴滴、酷狗

分类:Node

答案&解析

22.node 中间层怎样做的请求合并转发

公司:易车

分类:Node

答案&解析

23.介绍下 promise 的特性、优缺点,内部是如何实现的,动手实现 Promise

公司:滴滴、头条、喜马拉雅、兑吧、寺库、百分点、58、安居客

分类:JavaScript、编程题

答案&解析

24.实现 Promise.all

Promise.all = function (arr) {
  // 实现代码
};

公司:滴滴、头条、有赞、微医

分类:JavaScript、编程题

答案&解析

25.React 组件通信方式

公司:滴滴、掌门一对一、喜马拉雅、蘑菇街

分类:React

答案&解析

26.redux-saga 和 mobx 的比较

公司:掌门一对一

分类:React

答案&解析

27.说一下 react-fiber

公司:头条、滴滴、菜鸟网络、挖财、喜马拉雅

分类:React

答案&解析

28.手写发布订阅

公司:滴滴、头条

分类:JavaScript

答案&解析

29.手写数组转树

公司:滴滴

分类:JavaScript

答案&解析

30.手写用 ES6proxy 如何实现 arr[-1] 的访问

公司:滴滴

分类:JavaScript

答案&解析

31.请写出下面代码执行的的结果

console.log(1);
setTimeout(() => {
  console.log(2);
  process.nextTick(() => {
    console.log(3);
  });
  new Promise((resolve) => {
    console.log(4);
    resolve();
  }).then(() => {
    console.log(5);
  });
});
new Promise((resolve) => {
  console.log(7);
  resolve();
}).then(() => {
  console.log(8);
});
process.nextTick(() => {
  console.log(6);
});
setTimeout(() => {
  console.log(9);
  process.nextTick(() => {
    console.log(10);
  });
  new Promise((resolve) => {
    console.log(11);
    resolve();
  }).then(() => {
    console.log(12);
  });
});

分类:JavaScript

答案&解析


173382ede7319973.gif

31.写出执行结果

function side(arr) {
  arr[0] = arr[2];
}
function a(a, b, c = 3) {
  c = 10;
  side(arguments);
  return a + b + c;
}
a(1, 1, 1);

分类:JavaScript

答案&解析

32.写出执行结果

var min = Math.min();
max = Math.max();
console.log(min < max);

分类:JavaScript

答案&解析

33.写出执行结果,并解释原因

var a = 1;
(function a () {
    a = 2;
    console.log(a);
})();

分类:JavaScript

答案&解析

34.写出执行结果,并解释原因

var a = [0];
if (a) {
  console.log(a == true);
} else {
  console.log(a);
}

分类:JavaScript

答案&解析

35.写出执行结果,并解释原因

(function () {
  var a = (b = 5);
})();

console.log(b);
console.log(a);

分类:JavaScript

答案&解析

36.写出执行结果,并解释原因

var fullname = 'a';
var obj = {
   fullname: 'b',
   prop: {
      fullname: 'c',
      getFullname: function() {
         return this.fullname;
      }
   }
};
 
console.log(obj.prop.getFullname()); // c
var test = obj.prop.getFullname;
console.log(test());  // a

分类:JavaScript

答案&解析

37.写出执行结果,并解释原因

var company = {
    address: 'beijing'
}
var yideng = Object.create(company);
delete yideng.address
console.log(yideng.address);

分类:JavaScript

答案&解析

38.写出执行结果,并解释原因

var foo = function bar(){ return 12; };
console.log(typeof bar());  

分类:JavaScript

答案&解析

39.写出执行结果,并解释原因

var x=1;
if(function f(){}){
    x += typeof f;
}
console.log(x)

分类:JavaScript

答案&解析

40.写出执行结果,并解释原因

function f(){
      return f;
 }
console.log(new f() instanceof f);

分类:JavaScript

答案&解析

41.写出执行结果,并解释原因

var foo = {
        bar: function(){
            return this.baz;
        },
         baz:1
    }
console.log(typeof (f=foo.bar)());

分类:JavaScript

答案&解析

42.说一下React Hooks在平时开发中需要注意的问题和原因?

答案&解析


43.Vue组件中写name选项有除了搭配keep-alive还有其他作用么?你能谈谈你对keep-alive了解么?(平时使用和源码实现方面)

分类:Vue

答案&解析

44.Vue 为什么要用 vm.$set() 解决对象新增属性不能响应的问题 ?你能说说如下代码的实现原理么?

Vue.set (object, propertyName, value) 
vm.$set (object, propertyName, value)

分类:Vue

答案&解析

45.既然 Vue 通过数据劫持可以精准探测数据在具体dom上的变化,为什么还需要虚拟 DOM diff 呢?

分类:Vue

答案&解析

分类:Vue

答案&解析

46.下面代码输出什么?

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}

分类:JavaScript

答案&解析

47.写出执行结果,并解释原因

const num = {
  a: 10,
  add() {
    return this.a + 2;
  },
  reduce: () => this.a -2;
};
console.log(num.add());
console.log(num.reduce());

分类:JavaScript

答案&解析

48.写出执行结果,并解释原因

const person = { name: "yideng" };

function sayHi(age) {
  return `${this.name} is ${age}`;
}
console.log(sayHi.call(person, 5));
console.log(sayHi.bind(person, 5));

分类:JavaScript

答案&解析

49.写出执行结果,并解释原因

["1", "2", "3"].map(parseInt);

分类:JavaScript

答案&解析

50.写出执行结果,并解释原因

[typeof null, null instanceof Object]

分类:JavaScript

答案&解析

前端刷题神器

扫码进入前端面试星球🌍,解锁刷题神器,还可以获取800+道前端面试题一线常见面试高频考点

173382ede7319973.gif


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎继续阅读本专栏其它高赞文章:


查看原文

赞 71 收藏 57 评论 0

Lile_Black 关注了专栏 · 7月3日

疯狂的技术宅

本专栏文章首发于公众号:前端先锋 。

关注 24321

Lile_Black 关注了用户 · 6月30日

array_huang @array_huang

关注 4975

Lile_Black 关注了用户 · 6月30日

德来 @delai

有赞前端工程师,关注前端性能、工程化、交互体验、团队管理
欢迎投简历:delai@youzan.com

关注 987

认证与成就

  • 获得 8 次点赞
  • 获得 21 枚徽章 获得 1 枚金徽章, 获得 5 枚银徽章, 获得 15 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-04-11
个人主页被 629 人浏览