bt365体育投注.主頁欢迎您!!

    <acronym id="zvmrr"></acronym>
    <td id="zvmrr"></td>
  • <tr id="zvmrr"><label id="zvmrr"></label></tr>
  • <acronym id="zvmrr"></acronym>
  • robin

    robin 查看完整档案

    杭州编辑兰州工业学院  |  计算机网络技术 编辑SegmentFault  |  前端 编辑 blog.rnode.me 编辑
    编辑

    前端开发一枚,做过RN、小程序。

    个人动态

    robin 收藏了文章 · 1月22日

    《带你入门前端工程》开源了

    这是一本关于前端工程化的小书(4W 字左右 )。项目地址:

    https://github.com/woai3c/int...

    前端工程化,其实是软件工程在前端方面的应用。什么是软件工程?来看一下百度百科的定义:

    软件工程是一门研究用工程化方法构建和维护有效的、实用的和高质量的软件的学科

    换句话说,工程化的目的就是为了提升团队的开发效率。例如大家所熟悉的构建打包、性能优化、自动化部署等知识,都属于工程化的内容。

    我写这本小书的原因,是想对过去两年的工程化实践经验和学习心得做一个总结。希望能全面地、系统地对前端工程化知识做一个总结。

    小书大部分的内容都是以理论知识 + 代码示例 + 图片的方式来讲解的,努力争取让读者更容易理解。另外还有小部分的章节在讲解完理论知识后,还有相应的实践教程。例如前端监控这一节,在讲解完前端监控原理后,将会教你如何利用现有的监控工具对项目实行监控。

    可能有人会问,什么时候开始做工程化?我认为在需求评审阶段就可以做工程化了,根据需求选用适当的技术栈(技术选型),然后制定相关规范...

    在线访问

    目录

    1. 技术选型:如何进行技术选型?
    2. 统一规范:如何制订规范并利用工具保证规范被严格执行?
    3. 前端组件化:什么是模块化、组件化?
    4. 测试:如何写单元测试和 E2E(端到端) 测试?
    5. 构建工具:构建工具有哪些?都有哪些功能和优势?
    6. 自动化部署:如何利用 Jenkins、Github Actions 自动化部署项目?
    7. 前端监控:讲解前端监控原理及如何利用 sentry 对项目实行监控。
    8. 性能优化(一):如何检测网站性能?有哪些实用的性能优化规则?
    9. 性能优化(二):如何检测网站性能?有哪些实用的性能优化规则?
    10. 重构:为什么做重构?重构有哪些手法?
    11. 微服务:微服务是什么?如何搭建微服务项目?
    12. Severless:Severless 是什么?如何使用 Severless?
    13. 参考资料

    微服务、Severless 按理说不属于工程化的内容,但从提升开发效率的角度来看,也可以归纳到这一范围。

    目录的顺序是以一个项目的生命周期来分配的:

    1. 接到新需求,进行需求评审后根据具体情况做技术选型。
    2. 开发前需要统一规范。
    3. 学会模块化、组件化,对于写代码很有好处。
    4. 开发完,需要对代码进行测试。
    5. 构建打包。
    6. 部署上线。
    7. 对项目进行监控,随时发现问题。
    8. 根据项目运行情况决定是否要做性能优化。
    9. 项目越来越复杂,需要重构以提高可维护性。
    10. 项目越来越大,可以考虑是否用微服务对其进行拆分(或者使用 git submodule 和 monorepo 的方式管理项目)。
    11. 不想自己管理服务器或数据库,可以考虑使用 Serverless。

    注意

    本书的定位是入门级教程,主要对前端能接触到的工程知识做一个较全面的介绍。适合对前端工程化不了解或了解得不多的“菜鸟”同学。如果你是个“老鸟”,那本书可能不太适合你。

    另外,建议读者在阅读本书时,能够配合书本的实践部分去做实践。如果读者能够严格按照指示去做实践,在阅读完本书后,不仅会收获前端工程化的理论知识,还会获得对应的实践经验。

    你会学到什么?

    • 对前端工程化有一个全面、清晰的了解
    • 为架构师之路打下扎实的基础

    适宜人群

    • 想学习工程化的前端
    • 具备基础的 HTML、CSS、JavaScript 知识

    License

    MIT

    查看原文

    robin 赞了文章 · 1月22日

    《带你入门前端工程》开源了

    这是一本关于前端工程化的小书(4W 字左右 )。项目地址:

    https://github.com/woai3c/int...

    前端工程化,其实是软件工程在前端方面的应用。什么是软件工程?来看一下百度百科的定义:

    软件工程是一门研究用工程化方法构建和维护有效的、实用的和高质量的软件的学科

    换句话说,工程化的目的就是为了提升团队的开发效率。例如大家所熟悉的构建打包、性能优化、自动化部署等知识,都属于工程化的内容。

    我写这本小书的原因,是想对过去两年的工程化实践经验和学习心得做一个总结。希望能全面地、系统地对前端工程化知识做一个总结。

    小书大部分的内容都是以理论知识 + 代码示例 + 图片的方式来讲解的,努力争取让读者更容易理解。另外还有小部分的章节在讲解完理论知识后,还有相应的实践教程。例如前端监控这一节,在讲解完前端监控原理后,将会教你如何利用现有的监控工具对项目实行监控。

    可能有人会问,什么时候开始做工程化?我认为在需求评审阶段就可以做工程化了,根据需求选用适当的技术栈(技术选型),然后制定相关规范...

    在线访问

    目录

    1. 技术选型:如何进行技术选型?
    2. 统一规范:如何制订规范并利用工具保证规范被严格执行?
    3. 前端组件化:什么是模块化、组件化?
    4. 测试:如何写单元测试和 E2E(端到端) 测试?
    5. 构建工具:构建工具有哪些?都有哪些功能和优势?
    6. 自动化部署:如何利用 Jenkins、Github Actions 自动化部署项目?
    7. 前端监控:讲解前端监控原理及如何利用 sentry 对项目实行监控。
    8. 性能优化(一):如何检测网站性能?有哪些实用的性能优化规则?
    9. 性能优化(二):如何检测网站性能?有哪些实用的性能优化规则?
    10. 重构:为什么做重构?重构有哪些手法?
    11. 微服务:微服务是什么?如何搭建微服务项目?
    12. Severless:Severless 是什么?如何使用 Severless?
    13. 参考资料

    微服务、Severless 按理说不属于工程化的内容,但从提升开发效率的角度来看,也可以归纳到这一范围。

    目录的顺序是以一个项目的生命周期来分配的:

    1. 接到新需求,进行需求评审后根据具体情况做技术选型。
    2. 开发前需要统一规范。
    3. 学会模块化、组件化,对于写代码很有好处。
    4. 开发完,需要对代码进行测试。
    5. 构建打包。
    6. 部署上线。
    7. 对项目进行监控,随时发现问题。
    8. 根据项目运行情况决定是否要做性能优化。
    9. 项目越来越复杂,需要重构以提高可维护性。
    10. 项目越来越大,可以考虑是否用微服务对其进行拆分(或者使用 git submodule 和 monorepo 的方式管理项目)。
    11. 不想自己管理服务器或数据库,可以考虑使用 Serverless。

    注意

    本书的定位是入门级教程,主要对前端能接触到的工程知识做一个较全面的介绍。适合对前端工程化不了解或了解得不多的“菜鸟”同学。如果你是个“老鸟”,那本书可能不太适合你。

    另外,建议读者在阅读本书时,能够配合书本的实践部分去做实践。如果读者能够严格按照指示去做实践,在阅读完本书后,不仅会收获前端工程化的理论知识,还会获得对应的实践经验。

    你会学到什么?

    • 对前端工程化有一个全面、清晰的了解
    • 为架构师之路打下扎实的基础

    适宜人群

    • 想学习工程化的前端
    • 具备基础的 HTML、CSS、JavaScript 知识

    License

    MIT

    查看原文

    赞 14 收藏 8 评论 2

    robin 赞了文章 · 1月22日

    AWS 开源:与社区一起逐步实现真正开源的 Elasticsearch

    近日,Elastic 在官网发文称将对 Elasticsearch 和 Kibana 在许可证方面进行了重大的更改,由开源 Apache 2.0 许可证改为采用 Elastic License 和 SSPL(服务器端公共许可证)。

    对于 Elastic 的这一决策,AWS 在 AWS 开源博客官方博客发表文章《Stepping up for a truly open source Elasticsearch》 — Elastic 正在破坏开放源代码本身的定义,而 AWS 将加紧创建和维护由开源 Elasticsearch 和 Kibana 获得 Apache 许可 2.0 版(ALv2)许可的分支。

    以下为 AWS 开源博客发表的文章全文翻译。


    上周,Elastic 宣布他们将改变软件许可策略,将不再以 Apache License 2.0 版本(ALv2)发布 Elasticsearch 和Kibana 的新版本。取而代之的是,新版本的软件将在 Elastic License(限制了软件的使用方式)或 Server Side Public License(有一些限制让很多开源社区无法接受)下提供。这意味着 Elasticsearch 和 Kibana 将不再是开源软件。为了确保这两个软件包的开源版本仍然可用并得到很好的支持,包括在我们自己的产品中,我们今天宣布 AWS 将出面创建并维护一个 ALv2 授权的开源 Elasticsearch 和 Kibana 的分叉。

    这对 Elasticsearch 社区的 Open Distro 意味着什么?

    我们在 2019 年推出了 Open Distro for Elasticsearch,为客户和开发人员提供功能齐全的 Elasticsearch 发行版,提供ALv2授权软件的所有自由。Open Distro for Elasticsearch 是一个 100% 开源的发行版,它提供了几乎每个 Elasticsearch 用户或开发者都需要的功能,包括支持网络加密和访问控制。在构建 Open Distro 的过程中,我们遵循了 "上游先行 "的推荐开源开发实践。所有对Elasticsearch 的改动都以上游 pull requests 的形式发送(#42066, #42658, #43284, #43839, #53643, #57271, #59563, #61400, #64513),然后我们将 Elastic 提供的 "oss "构建包含在我们的发行版中。这确保了我们与上游开发者和维护者合作,而不是创建一个软件的 "fork"。

    选择分叉一个项目并不是一个轻率的决定,但是当一个社区的需求出现分歧时,这可能是一条正确的前进道路--就像这里的情况一样。开源软件的一个重要好处是,当这样的事情发生时,如果开发者有足够的动力,他们已经拥有了所有需要的权利,可以自己接手工作。这里有很多成功的案例,比如 Grafana 就是从 Kibana 3 的分叉中产生的。

    当AWS决定提供一个基于开源项目的服务时,我们确保我们有能力并准备好在必要时自己维护它。AWS 带来了多年与这些代码库合作的经验,同时也为 Elasticsearch 和 Apache Lucene(Elasticsearch构建的核心搜索库)做出了上游代码贡献--仅 2020 年就有超过 230 个Lucene 贡献。

    我们对 Elasticsearch 和 Kibana 的分叉将基于最新的 ALv2 授权代码库,7.10 版本。我们将在未来几周内发布新的 GitHub 仓库。随着时间的推移,这两个版本将被包含在现有的 Open Distro 发行版中,取代 Elastic 提供的 ALv2 构建。我们将长期参与其中,并将以促进健康和可持续的开源实践的方式开展工作--包括实现与贡献者社区共享项目治理。

    这对亚马逊 Elasticsearch 服务客户意味着什么?

    您可以放心,无论是 Elastic 的许可证变更,还是我们分叉的决定,都不会对您目前享受的 Amazon Elasticsearch 服务(Amazon ES)产生任何负面影响。今天,我们在 Amazon ES 上提供了 18 个版本的Elasticsearch,这些版本都不会受到许可证变更的影响。

    未来,Amazon ES 将由 Elasticsearch 和 Kibana 的新分叉提供支持。我们将继续提供新功能、修复和增强功能。我们致力于提供兼容性,以消除您更新客户端或应用程序代码的需要。就像我们今天所做的那样,我们将为您提供一个无缝的升级路径到新版本的软件。

    这一变化不会减缓我们为客户提供的增强速度。如果有的话,一个社区拥有的 Elasticsearch 代码库为我们提供了新的机会,使我们在提高稳定性、可扩展性、弹性和性能方面的进展更快。

    这对开源社区意味着什么?

    开发者出于许多原因而接受开放源码软件,其中最重要的原因可能是可以自由地在他们希望的地方和方式使用该软件。

    自 1998 年 "开源 "一词被提出以来,它就有了特定的含义。Elastic 关于 SSPL 是 "自由开放 "的说法是误导和错误的。他们试图宣称开源的好处,同时又在削去开源本身的定义。他们对 SSPL 的选择掩盖了这一点。SSPL 是一个非开源许可证,它的设计看起来像一个开源许可证,模糊了两者之间的界限。正如 Fedora 社区所说的那样,"[将 SSPL 视为'自由'或'开源'会导致[一个]阴影笼罩在 FOSS 生态系统的所有其他许可证上。"

    2018 年 4 月,当 Elastic 将他们的专有授权软件与 ALv2 代码共同混合时,他们在 "We Opened X-Pack "中承诺。"我们没有改变Elasticsearch、Kibana、Beats 和 Logstash 的任何 Apache 2.0 代码的授权--我们永远不会改变。" 上周,在违背了这一承诺之后,Elastic 更新了同一页面,并在脚注中写道:"情况有变"。

    Elastic 知道他们做的事情很蹊跷。社区已经告诉他们这一点(例如,见BrasseurQuinnDeVaultJacob)。这也是为什么他们觉得有必要写一个额外的虚张声势的博客(在他们最初的许可证更改博客之上),试图将他们的行为解释为 "AWS 让我们这么做"。大多数人并没有被愚弄。我们没有让他们做任何事情。他们认为,限制他们的许可证将锁定其他人提供托管 Elasticsearch 服务,这将让 Elastic 建立更大的业务。当然 Elastic 有权改变他们的许可证,拥有自己的决定。

    同时,我们对我们与 Open Distro for Elasticsearch 一起踏上的长期旅程感到兴奋。我们期待着为 Elasticsearch 和 Kibana 提供一个使用 ALv2 许可证的真正的开源选择,并与社区一起建设和支持这个未来。

    image.png

    查看原文

    赞 4 收藏 0 评论 0

    robin 关注了用户 · 1月22日

    y_ck @user_ngjv5fmp

    Hi there,I'm yck ??

    目前任职于酷家乐业务架构组,有需要的内推的可以发邮件至 zx597813039@gmail.com。

    关注 578

    robin 赞了文章 · 1月22日

    前端搞工程化:从零打造性能检测库「源码 + 视频」

    工程化体系专栏永远首发自我的 Github,大家可以关注点赞,通常会早于发布各大平台一周时间以上。

    本文涉及到的源码及视频地址:

    • 源码
    • 视频,因为制作肯定比文字需要的时间多,所以本周才会更新完毕,大家可以先对视频插个眼

    前言

    经常有读者问我什么是前端工程化?该怎么开始做前端工程化?

    聊下来以后得出一些结论:这类读者普遍就职于中小型公司,前端人员个位数,平时疲于开发,团队内部几乎没有基础建设,工具很蛮荒。工程化对于这些读者来说很陌生,基本不知道这到底是什么,或者说认为 Webpack 就是前端工程化的全部了。

    笔者目前就职于某厂的基础架构组,为百来号前端提供基础服务建设,对于这个领域有些许皮毛经验。因此有了一些想法,前端搞工程化会是笔者今年开坑的一个系列作品,每块内容会以文章 + 源码 + 视频的方式呈现。

    这个系列的产出适用于以下群体:

    • 中小厂前端,基建蛮荒,平时疲于业务,不知道业务外怎么做东西能提高自己的竞争力、丰富简历
    • 公司暂时没有做基建计划,只能业余做一些低成本收益高的产品
    • 想了解前端工程化

    需要说明的是产出只会是一个低成本下的最小可用产品,你可以拿来按需增加功能、参考思路或者纯粹当学习一点知识。

    什么是前端工程化?

    因为是该系列第一篇文章,就先来大致说下什么是前端工程化。

    我的理解是前端工程化大体上可以理解为是做提效工程,从写代码开始的每一步都可以做工程化。比如说你用 IDE 对比记事本写代码的体验及效率肯定是不一样的;比如说 Webpack 等这类工具也是在帮助我们提升开发、构建的效率,其他的工具也就不一一列出了,大家知道意思就好。

    当然了,今天要聊到的性能检测也是工程化的一部分。毕竟我们需要有个工具去协助找到应用到底在哪块地方存在性能短板,能帮助开发者更快地定位问题,而不是在生产环境中让用户抱怨产品卡顿。

    为什么需要性能检测?

    性能优化是很多前端都绕不开的话题,先不说项目是否需要性能优化,面试的时候这类问题是很常见的。

    但是光会性能优化的手段还是不够的,我们最后还是需要做出前后数据对比才能体现出这次优化的价值到底有多少,毕竟数据的量化在职场中还是相当重要的。老板不知道你具体做的事情,很多东西都得从数据中来看,数据越好看就说明你完成工作的能力越高。

    想获取性能的前后数据变化,我们肯定得用一些工具来做性能检测。

    性能该怎么检测?

    性能检测的方式有很多:

    • Chrome 自带的开发者工具:Performance
    • Lighthouse 开源工具
    • 原生 Performance API
    • 各种官方库、插件

    这些方法各有各的好处,前两种方式简单快捷,能够可视化各类指标,但是很难拿到用户端的数据,毕竟你不大可能让用户去跑这些工具,当然除此之外还有一些很小的缺点,比如说拿不到重定向次数等等。

    官方库、插件相比前两者来说会逊色很多,并且只提供一部分核心指标。

    原生 Performance API 存在兼容问题,但是能覆盖到开发生产阶段,并且功能也能覆盖自带的开发者工具:Performance 工具。不仅在开发阶段能了解到项目的性能指标,还能获取用户端的数据,帮助我们更好地制定优化方案。另外能获取的指标也很齐全,因此是此次我们产品的选择。

    当然了这不是多选一的选择题,我们在开发阶段还是需要将 Performance 工具及 API 结合起来使用,毕竟他们还是有着相辅相成的作用。

    实战

    这是此处产品的源码:地址

    一些性能指标

    在开始实战前,我们还是得来了解一些性能指标,随着时代发展,其实一些老的性能优化文章已经有点过时了。谷歌一直在更新性能优化这块的指标,笔者之前写过一篇文章来讲述当下的最新性能指标有哪些,有兴趣的读者可以先详细的读一下。

    当然如果你嫌太长不看,可以先通过以下思维导图简单了解一下:

    当然除了这个指标以外,我们还需要获取网络、文件传输、DOM等信息丰富指标内容。

    Performance 使用

    Performance 接口可以获取到当前页面中与性能相关的信息,并且提供高精度的时间戳,秒杀 Date.now()。首先我们来看下这个 API 的兼容性:

    这个百分比其实已经算是兼容度很高了,主流浏览器的版本都能很好的支持。

    对于 Performance 上 API 具体的讲解文中就不赘述了,有兴趣的可以阅读 MDN 文档,笔者在这里只讲几个后续用到的重要 API。

    getEntriesByType

    这个 API 可以让我们通过传入 type 获取一些相应的信息:

    • frame:事件循环中帧的时间数据。
    • resource:加载应用程序资源的详细网络计时数据
    • mark:performance.mark 调用信息
    • measure:performance.measure 调用信息
    • longtask:长任务(执行时间大于 50ms)信息。这个类型已被废弃(文档未标注,但是在 Chrome 中使用会显示已废弃),我们可以通过别的方式来拿
    • navigation:浏览器文档事件的指标的方法和属性
    • paint:获取 FP 和 FCP 指标

    最后两个 type 是性能检测中获取指标的关键类型。当然你如果还想分析加载资源相关的信息的话,那可以多加上 resource 类型。

    PerformanceObserver

    PerformanceObserver 也是用来获取一些性能指标的 API,用法如下:

    const perfObserver = new PerformanceObserver((entryList) => {
        // 信息处理
    })
    // 传入需要的 type
    perfObserver.observe({ type: 'longtask', buffered: true })

    结合 getEntriesByType 以及 PerformanceObserver,我们就能获取到所有需要的指标了。

    上代码!

    因为已经贴了源码地址,笔者就不贴大段代码上来了,会把主要的从零到一过程梳理一遍。

    首先我们肯定要设计好用户如何调用 SDK(代指性能检测库)?需要传递哪些参数?如何获取及上报性能指标?

    一般来说调用 SDK 多是构建一个实例,所以这次我们选择 class 的方式来写。参数的话暂定传入一个 tracker 函数获取各类指标以及 log 变量决定是否打印指标信息,签名如下:

    export interface IPerProps {
      tracker?: (type: IPerDataType, data: any, allData: any) => void
      log?: boolean
    }
    
    export type IPerDataType =
      | 'navigationTime'
      | 'networkInfo'
      | 'paintTime'
      | 'lcp'
      | 'cls'
      | 'fid'
      | 'tbt'

    接下来我们写 class 内部的代码,首先在前文中我们知道了 Performance API 是存在兼容问题的,所以我们需要在调用 Performance 之前判断一下浏览器是否支持:

    export default class Per {
      constructor(args: IPerProps) {
        // 存储参数
        config.tracker = args.tracker
        if (typeof args.log === 'boolean') config.log = args.log
        // 判断是否兼容
        if (!isSupportPerformance) {
          log(`This browser doesn't support Performance API`)
          return
        }
    }
    
    export const isSupportPerformance = () => {
      const performance = window.performance
      return (
        performance &&
        !!performance.getEntriesByType &&
        !!performance.now &&
        !!performance.mark
      )
    }

    以上前置工作完毕以后,就可以开始写获取性能指标数据的代码了。

    我们首先通过 performance.getEntriesByType('navigation') 来获取关于文档事件的指标

    这个 API 还是能拿到挺多事件的时间戳的,如果你想了解这些事件具体含义,可以阅读文档,这里就不复制过来占用篇幅了。

    看到那么多字段,可能有的读者就晕了,那么多东西我可怎么算指标。其实不需要担心,看完下图结合刚才的文档就行了:

    我们不需要全部利用上获得的字段,重要的指标信息暴露出来即可,照着图和文档依样画葫芦就能得出代码:

    export const getNavigationTime = () => {
      const navigation = window.performance.getEntriesByType('navigation')
      if (navigation.length > 0) {
        const timing = navigation[0] as PerformanceNavigationTiming
        if (timing) {
        //   解构出来的字段,太长不贴
          const {...} = timing
    
          return {
            redirect: {
              count: redirectCount,
              time: redirectEnd - redirectStart,
            },
            appCache: domainLookupStart - fetchStart,
            // dns lookup time
            dnsTime: domainLookupEnd - domainLookupStart,
            // handshake end - handshake start time
            TCP: connectEnd - connectStart,
            // HTTP head size
            headSize: transferSize - encodedBodySize || 0,
            responseTime: responseEnd - responseStart,
            // Time to First Byte
            TTFB: responseStart - requestStart,
            // fetch resource time
            fetchTime: responseEnd - fetchStart,
            // Service work response time
            workerTime: workerStart > 0 ? responseEnd - workerStart : 0,
            domReady: domContentLoadedEventEnd - fetchStart,
            // DOMContentLoaded time
            DCL: domContentLoadedEventEnd - domContentLoadedEventStart,
          }
        }
      }
      return {}
    }

    大家可以发现以上获得的指标中有不少是和网络有关系的,因此我们还需要结合网络环境来分析,获取网络环境信息很方便,以下是代码:

    export const getNetworkInfo = () => {
      if ('connection' in window.navigator) {
        const connection = window.navigator['connection'] || {}
        const { effectiveType, downlink, rtt, saveData } = connection
        return {
          // 网络类型,4g 3g 这些
          effectiveType,
          // 网络下行速度
          downlink,
          // 发送数据到接受数据的往返时间
          rtt,
          // 打开/请求数据保护模式
          saveData,
        }
      }
      return {}
    }

    拿完以上的指标之后,我们需要用到 PerformanceObserver 来拿一些核心体验(性能)指标了。比如说 FP、FCP、FID 等等,内容就包括在我们上文中看过的思维导图中:

    在这之前我们需要先了解一个注意事项:页面是有可能在处于后台的情况下加载的,因此这种情况下获取的指标是不准确的。所以我们需要忽略掉这种情况,通过以下代码来存储一个变量,在获取指标的时候比较一下时间戳来判断是否处于后台中:

    document.addEventListener(
      'visibilitychange',
      (event) => {
        // @ts-ignore
        hiddenTime = Math.min(hiddenTime, event.timeStamp)
      },
      { once: true }
    )

    接下来是获取指标的代码,因为他们获取方式大同小异,所以先把获取方法封装一下:

    // 封装一下 PerformanceObserver,方便后续调用
    export const getObserver = (type: string, cb: IPerCallback) => {
      const perfObserver = new PerformanceObserver((entryList) => {
        cb(entryList.getEntries())
      })
      perfObserver.observe({ type, buffered: true })
    }

    我们先来获取 FP 及 FCP 指标:

    export const getPaintTime = () => {
      const data: { [key: string]: number } = ({} = {})
      getObserver('paint', entries => {
        entries.forEach(entry => {
          data[entry.name] = entry.startTime
          if (entry.name === 'first-contentful-paint') {
            getLongTask(entry.startTime)
          }
        })
      })
      return data
    }

    拿到的数据结构长这样:

    需要注意的是在拿到 FCP 指标以后需要同步开始获取 longtask 的时间,这是因为后续的 TBT 指标需要使用 longtask 来计算。

    export const getLongTask = (fcp: number) => {
      getObserver('longtask', entries => {
        entries.forEach(entry => {
          // get long task time in fcp -> tti
          if (entry.name !== 'self' || entry.startTime < fcp) {
            return
          }
          // long tasks mean time over 50ms
          const blockingTime = entry.duration - 50
          if (blockingTime > 0) tbt += blockingTime
        })
      })
    }

    接下来我们来拿 FID 指标,以下是代码:

    export const getFID = () => {
      getObserver('first-input', entries => {
        entries.forEach(entry => {
          if (entry.startTime < hiddenTime) {
            logIndicator('FID', entry.processingStart - entry.startTime)
            // TBT is in fcp -> tti
            // This data may be inaccurate, because fid >= tti
            logIndicator('TBT', tbt)
          }
        })
      })
    }

    FID 的指标数据长这样,需要用户交互才会触发:

    在获取 FID 指标以后,我们也去拿了 TBT 指标,但是拿到的数据不一定是准确的。因为 TBT 指标的含义是在 FCP 及 TTI 指标之间的长任务阻塞时间之和,但目前好像没有一个好的方式来获取 TTI 指标数据,所以就用 FID 暂代了。

    最后是 CLS 和 LCP 指标,大同小异就贴在一起了:

    export const getLCP = () => {
      getObserver('largest-contentful-paint', entries => {
        entries.forEach(entry => {
          if (entry.startTime < hiddenTime) {
            const { startTime, renderTime, size } = entry
            logIndicator('LCP Update', {
              time: renderTime | startTime,
              size,
            })
          }
        })
      })
    }
    
    export const getCLS = () => {
      getObserver('layout-shift', entries => {
        let cls = 0
        entries.forEach(entry => {
          if (!entry.hadRecentInput) {
            cls += entry.value
          }
        })
        logIndicator('CLS Update', cls)
      })
    }

    拿到的数据结构长这样:

    截屏2021-01-17下午7.37.33
    截屏2021-01-17下午7.37.14

    另外这两个指标还和别的不大一样,并不是一成不变的。一旦有新的数据符合指标要求,就会更新。

    以上就是我们需要获取的所有性能指标了,当然光获取到指标肯定是不够,还需要暴露每个数据给用户,对于这种统一操作,我们需要封装一个工具函数出来:

    // 打印数据
    export const logIndicator = (type: string, data: IPerData) => {
      tracker(type, data)
      if (config.log) return
      // 让 log 好看点
      console.log(
        `%cPer%c${type}`,
        'background: #606060; color: white; padding: 1px 10px; border-top-left-radius: 3px; border-bottom-left-radius: 3px;',
        'background: #1475b2; color: white; padding: 1px 10px; border-top-right-radius: 3px;border-bottom-right-radius: 3px;',
        data
      )
    }
    export default (type: string, data: IPerData) => {
      const currentType = typeMap[type]
      allData[currentType] = data
      // 如果用户传了回调函数,那么每次在新获取指标以后就把相关信息暴露出去
      config.tracker && config.tracker(currentType, data, allData)
    }

    封装好函数以后,我们可以这样调用:

    logIndicator('FID', entry.processingStart - entry.startTime)

    在这里为止我们 SDK 的大体内容已经完成了,我们可以按需添加一些小功能,比如说获取指标分数。

    指标分数是官方给的一些建议,你可以在官方 Blog 或者我的文章中看到定义的数据。

    代码不复杂,我们就以获取 FCP 指标的分数为例演示一下代码:

    export const scores: Record<string, number[]> = {
      fcp: [2000, 4000],
      lcp: [2500, 4500],
      fid: [100, 300],
      tbt: [300, 600],
      cls: [0.1, 0.25],
    }
    
    export const scoreLevel = ['good', 'needsImprovement', 'poor']
    
    export const getScore = (type: string, data: number) => {
      const score = scores[type]
      for (let i = 0; i < score.length; i++) {
        if (data <= score[i]) return scoreLevel[i]
      }
    
      return scoreLevel[2]
    }

    首先是获取分数相关的工具函数,这块反正就是看着官方建议照抄,然后我们只需要在刚才获取指标的地方多加一句代码即可:

    export const getPaintTime = () => {
      getObserver('paint', (entries) => {
        entries.forEach((entry) => {
          const time = entry.startTime
          const name = entry.name
          if (name === 'first-contentful-paint') {
            getLongTask(time)
            logIndicator('FCP', {
              time,
              score: getScore('fcp', time),
            })
          } else {
            logIndicator('FP', {
              time,
            })
          }
        })
      })
    }

    结束了,有兴趣的可以来这里读一下源码,反正也没几行。

    最后

    文章周末写的,略显仓促,如有出错请斧正,同时也欢迎大家一起探讨问题。

    想看更多文章可以关注我的 Github 或者进群一起聊聊前端工程化。

    查看原文

    赞 7 收藏 5 评论 0

    robin 赞了回答 · 1月22日

    关于受控非受控组件的一些疑问,客官进来看看

    你最后说的那中情况是非受控组件,受控的意思是子组件的渲染时机完全由父组件控制。换个通俗的讲法,受控组件就是个“听话的工具人”,父组件给啥用啥,让你干啥你就干啥,不给你,不指挥你,你就啥也不干,至于怎么用,就看开发者把它定义为实现何种功能。

    关注 3 回答 3

    robin 回答了问题 · 1月22日

    为什么同一个链接用chrome打开就是下载,用微信打开就是预览,一个pdf文件?

    微信内置文件预览功能而已

    关注 4 回答 3

    robin 回答了问题 · 1月22日

    关于受控非受控组件的一些疑问,客官进来看看

    个人理解,以 react 常规写法实现就好,最好不要有骚操作,就好比明明可以通过 setState 及onChage 就可改变值,可非要使用 ref 或者 原生 js 或者 jquery,一旦项目复杂之后,会出现奇奇怪怪的问题。

    至于你提到的自定义组件上受控组件和非受控组件的划分,看你组件的数据是否通过 state 或者 props 管理的,如果还是用的骚操作,那就要注意了,坑从此刻就挖下了。

    关注 3 回答 3

    robin 回答了问题 · 1月22日

    关于文件导出的一个疑问

    谢邀,试试这种方法

    Axios({
        url: 'http://localhost/downloadFile',
        method: 'GET',
        responseType: 'blob', // Important
    })
    .then(({ data }) => {
        const downloadUrl = window.URL.createObjectURL(new Blob([data]));
        const link = document.createElement('a');
        link.href = downloadUrl;
        link.setAttribute('download', 'file.zip'); //any other extension
        document.body.appendChild(link);
        link.click();
        link.remove();
    });

    也可以使用第三方库:https://www.npmjs.com/package...

    const FileDownload = require('js-file-download');
    
    Axios({
      url: 'http://localhost/downloadFile',
      method: 'GET',
      responseType: 'blob', // Important
    }).then((response) => {
        FileDownload(response.data, 'report.csv');
    });

    关注 5 回答 3

    robin 赞了回答 · 1月22日

    React hook 使用useEffect 如何判断多个值都改变了才执行内部方法?

    import React, { useState, useEffect, useRef } from 'react';
    
    const One = _ => {
      const [a, setA] = useState(0);
      const [b, setB] = useState(0);
      const ref = useRef({ a, b });
    
      useEffect(() => {
        let { a: prevA, b: prevB } = ref.current;
        console.log('更新前:', prevA, prevB);
        console.log('更新后:', a, b);
    
        if (prevA !== a && prevB !== b) {
          console.log('update!');
          ref.current = { a, b };
        }
      }, [a, b]);
    
      return (
        <>
          <h1>{a + b}</h1>
          <button onClick={_ => setA(d => d + 1)}>Chang A</button>
          <button onClick={_ => setB(d => d + 1)}>Chang B</button>
        </>
      );
    };
    
    export default One;

    关注 7 回答 4

    认证与成就

    • 获得 93 次点赞
    • 获得 13 枚徽章 获得 0 枚金徽章, 获得 2 枚银徽章, 获得 11 枚铜徽章

    擅长技能
    编辑

    开源项目 & 著作
    编辑

    (??? )
    暂时没有

    注册于 2017-07-09
    个人主页被 2.8k 人浏览

    bt365体育投注