<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>the blog for yss</title>
    <description>focus on technology by a front-end development</description>
    <link>https://yss.github.iohttps://yss.github.io/</link>
    <atom:link href="https://yss.github.iohttps://yss.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 15 Mar 2023 11:21:08 +0000</pubDate>
    <lastBuildDate>Wed, 15 Mar 2023 11:21:08 +0000</lastBuildDate>
    <generator>Jekyll v3.9.3</generator>
    
      <item>
        <title>SQL 中 inner join, left join, right join, full join 的差异</title>
        <description>&lt;h1 id=&quot;为什么要用-join&quot;&gt;为什么要用 join&lt;/h1&gt;

&lt;p&gt;主要涉及到连表查询，join 方式可以很快速的把多张表合并起来。&lt;/p&gt;

&lt;p&gt;合并的方式就分：inner join, left join, right join, full join。&lt;/p&gt;

&lt;h1 id=&quot;各个含义&quot;&gt;各个含义&lt;/h1&gt;
&lt;h2 id=&quot;inner-join&quot;&gt;inner join&lt;/h2&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SELECT table1.column1, table2.column2...
  FROM table1
 INNER JOIN table2
    ON table1.common_field = table2.common_field;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;OR 二次查询&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SELECT column1, column2
    FROM (
        SELECT column1, uid
        FROM table1
        WHERE x1=y1
    ) a
    INNER JOIN (
        SELECT column2, uid
        FROM table1
        WHERE x2=y2
    ) b
    ON a.uid = b.uid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个含义就是取两组查询数据的相交部分，并最终返回。&lt;/p&gt;

&lt;p&gt;比如：查询出来的数据是&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
uid column1     uid column2
1   x1          1   y1
2   x2          3   y2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;最终得到的结果是：&lt;/p&gt;
&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;uid column1 column2
1   x1      y1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;left-join&quot;&gt;left join&lt;/h2&gt;

&lt;p&gt;完全返回左边的条件查出来的数据，如果同时满足右边的条件，那么就合并右边的数据。&lt;/p&gt;

&lt;p&gt;同上面那个数据，可以得到这样的结果：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;uid column1 column2
1   x1      y1
2   x2      NULL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;right-join&quot;&gt;right join&lt;/h2&gt;

&lt;p&gt;刚好跟 left join 想法，本质上是一个意思。&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;uid column1 column2
1   x1      y1
3   NULL    y2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;full-join&quot;&gt;FULL join&lt;/h2&gt;

&lt;p&gt;就是把能合并的合并，不能合并单独放一条，没有的数据就置空。&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;uid column1 column2
1   x1      y1
2   x2      NULL
3   NULL    y2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 16 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://yss.github.iohttps://yss.github.io/2023/01/16/what-is-the-difference-between-inner-join,-left-join,-right-join-and-full-join-in-sql.html</link>
        <guid isPermaLink="true">https://yss.github.iohttps://yss.github.io/2023/01/16/what-is-the-difference-between-inner-join,-left-join,-right-join-and-full-join-in-sql.html</guid>
        
        <category>sql</category>
        
        
        <category>sql</category>
        
      </item>
    
      <item>
        <title>RN bundle 下的 lottie 多语言文件优化</title>
        <description>&lt;h1 id=&quot;背景&quot;&gt;背景&lt;/h1&gt;
&lt;p&gt;我们现在的 lottie 文件，针对多语言环境是打到一个包的。&lt;/p&gt;

&lt;p&gt;相当于一个动画，两个 json 文件。&lt;/p&gt;

&lt;p&gt;我们拿一个最典型的例子开始：&lt;/p&gt;

&lt;h2 id=&quot;一个典型例子&quot;&gt;一个典型例子&lt;/h2&gt;
&lt;p&gt;比如下面这个图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/lottie-file-optimization.png&quot; alt=&quot;lottie-file-optimization&quot; /&gt;&lt;/p&gt;

&lt;p&gt;同时有两个：rewardBoxClaimEn.json 和 rewardBoxClaimId.json 。&lt;/p&gt;

&lt;h1 id=&quot;lottie-文件对-bundle-size-的影响&quot;&gt;Lottie 文件对 bundle size 的影响&lt;/h1&gt;
&lt;p&gt;在我们理解里，Lottie 文件应该主要是文字上的差异。如果在一起压缩，应该能压缩很大部分。&lt;/p&gt;

&lt;p&gt;我们拿这两个文件，分别用两种方式压缩：&lt;/p&gt;

&lt;p&gt;三个文件夹：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;id：只包含 rewardBoxClaimId.json&lt;/li&gt;
  &lt;li&gt;en: 只包含 rewardBoxClaimEn.json&lt;/li&gt;
  &lt;li&gt;all：包含 rewardBoxClaimEn.json 和 rewardBoxClaimId.json&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对应的压缩文件形式：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一种是直接用 7zip 压缩文件夹&lt;/li&gt;
  &lt;li&gt;二种是先用 zip 压缩，再用 7zip 压缩。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;压缩命令及参数和 bundle 构建保持一致：7z a -mtm- -mx=3 xx.7z xx 。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;原始目录及压缩后的文件&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;大小(KB)&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;说明&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;all/&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;3720&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;带 .zip 的都是先 zip 压缩后再 7zip 压缩。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;all.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;2912&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;all.zip.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;2744&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;id/&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1864&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;id.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1456&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;id.zip.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1372&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;en/&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1864&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;en.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1456&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;en.zip.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1372&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;从上面的数据可以看出&quot;&gt;从上面的数据可以看出：&lt;/h3&gt;
&lt;p&gt;先用 zip 压缩再用 7zip 压缩确实能够减少整个包的大小。&lt;/p&gt;

&lt;p&gt;另外，用这种方式最终得到的压缩包体积和拆分之后在压缩的体积，近乎完全相等：1372 + 1372 = 2744&lt;/p&gt;

&lt;p&gt;也就是说：这两个文件在一起，并没有多少相同的部分。&lt;/p&gt;

&lt;p&gt;带着这个疑问，我重新看了具体的文件内容。&lt;/p&gt;

&lt;h1 id=&quot;文件内容分析&quot;&gt;文件内容分析&lt;/h1&gt;
&lt;p&gt;先是简单的对比了文件内容，可以发现前面基本一样。等找到图片部分，发现绝大部分都不一样。&lt;/p&gt;

&lt;p&gt;通过手段，剔除掉图片后，我们对一下大小：&lt;/p&gt;

&lt;p&gt;使用正则：,”p”:”[^”]+” 搜索出，总共有 9 个图片，删除后得到如下数据（au -sk xx.json）：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;大小（KB）&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;正常大小&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;2176&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;剔除图片后的大小&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;36&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;从这个数据可以明显看出，图片在这个 json 文件的占比是：98.3%。&lt;/p&gt;

&lt;p&gt;然后图片内容来说：&lt;/p&gt;

&lt;p&gt;相同的图片内容数是：8个&lt;/p&gt;

&lt;p&gt;然后，再剔除一些细微的差异（En 文件多了一个 ,”l”:2）和一个不相同的图片，我们发现：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;大小（KB）&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;正常大小&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1897060&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;剔除不相同后的大小&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1885786&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;也就是说大概接近 100% 相同。&lt;/p&gt;

&lt;p&gt;那为什么压缩后几乎没有变化呢？&lt;/p&gt;

&lt;p&gt;带着这个疑问开始了各种各样的搜索和尝试。&lt;/p&gt;

&lt;h1 id=&quot;为什么压缩后几乎没有变化呢&quot;&gt;为什么压缩后几乎没有变化呢&lt;/h1&gt;
&lt;h2 id=&quot;第一个尝试相同文件压缩&quot;&gt;第一个尝试：相同文件压缩&lt;/h2&gt;
&lt;p&gt;就是在目录下放两个一模一样的文件，只是名字不同。&lt;/p&gt;

&lt;p&gt;然后对这个目录进行压缩：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;原始目录及压缩后的文件&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;大小（KB）&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;same/&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;3712&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;same.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;2744&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;same.zip.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;2744&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;single.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1372&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;这不符合我们的预期！&lt;/p&gt;

&lt;p&gt;我们预期大小应该是 1372 ，但是得到的大小是 2744。&lt;/p&gt;

&lt;h2 id=&quot;第一个疑问算法问题&quot;&gt;第一个疑问：算法问题？&lt;/h2&gt;
&lt;p&gt;这怎么也不可能呀！&lt;/p&gt;

&lt;p&gt;单独一个文件是有压缩的。&lt;/p&gt;

&lt;h2 id=&quot;第二个疑问压缩命令的问题&quot;&gt;第二个疑问：压缩命令的问题？&lt;/h2&gt;
&lt;p&gt;我用的 7z 命令是通过 brew install p7zip 安装的，难道是压缩命令的差异？&lt;/p&gt;

&lt;p&gt;为了验证，我重新把 Independent Builder 库关于压缩命令那一环节看了又看。&lt;/p&gt;

&lt;p&gt;最后，缩减成下面这个命令：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;glob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sevenBin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;7zip-bin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path7za&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;execa&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@xx-rn/deployment-fns&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;execa&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DIR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;same&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;glob&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/Users/xx/tmp/7z/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DIR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;relative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/Users/xx/tmp/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DIR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
 
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Contents&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
     
    &lt;span class=&quot;nx&quot;&gt;compressFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/Users/xx/tmp/7z/build/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DIR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.7z&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;cwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/Users/xx/tmp/7z&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;compressFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;zipOpts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// see: https://sourceforge.net/p/sevenzip/discussion/45797/thread/61905a4c/#5437&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// -mtm- will remove the timestamp effect when zipping&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;defaultZipOpts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;-mtm-&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;-mx=3&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
 
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;execa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;sevenBin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;...(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;zipOpts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;defaultZipOpts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;...(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过上面这个 js 脚本压缩得到的压缩包大小和直接通过命令方式的压缩得到的压缩包大小是一致的。&lt;/p&gt;

&lt;p&gt;所以，为什么呢？&lt;/p&gt;

&lt;h2 id=&quot;从源头分析线上-plugin-包&quot;&gt;从源头分析线上 plugin 包&lt;/h2&gt;
&lt;p&gt;回到我们通过抓包获取到的线上 plugin 包的解压缩来看。&lt;/p&gt;

&lt;p&gt;我们解压 xxx.plugin.7z（10MB） 后得到了一个 xxx.plugin（19.2MB） 的文件。&lt;/p&gt;

&lt;p&gt;我们之前做过一个尝试，就是在代码里面（lucky-video.plugin 文件）找到了对应的引入 rewardBoxClaimEn.json（1.8MB） 的那一行，并删掉。然后再压缩。&lt;/p&gt;

&lt;p&gt;得到的结论是删掉那一行代码对整个最终的 plugin 大小几乎没有影响。&lt;/p&gt;

&lt;h3 id=&quot;一个小的尝试&quot;&gt;一个小的尝试&lt;/h3&gt;
&lt;p&gt;尝试直接删除 assets/fallback 下最大的 json 文件，也就是  rewardBoxClaimEn.json（1.8MB），然后再压缩。&lt;/p&gt;

&lt;p&gt;发现整个 bundle 大小从 10MB 减小到了 8.3MB。&lt;/p&gt;

&lt;p&gt;然后继续删除  rewardBoxClaimId.json，整个 bundle 大小从 8.3MB 减小到了 6.6MB。&lt;/p&gt;

&lt;h3 id=&quot;第二个尝试&quot;&gt;第二个尝试&lt;/h3&gt;
&lt;p&gt;删除 lucky-video.plugin 文件中对应的引入  rewardBoxClaimEn.json（1.8MB）和 rewardBoxClaimId.json（1.8MB） 的那两行。然后再压缩。&lt;/p&gt;

&lt;p&gt;整个 bundle 大小从 10MB 减小到 7MB。&lt;/p&gt;

&lt;p&gt;然后尝试只删除一个  rewardBoxClaimEn.json（1.8MB）。&lt;/p&gt;

&lt;p&gt;整个 bundle 大小从 10MB 减小到 8.5MB&lt;/p&gt;

&lt;p&gt;尝试了很多次都是这个结论。&lt;/p&gt;

&lt;p&gt;最后再验证，不做任何改动，然后再压缩。&lt;/p&gt;

&lt;p&gt;得到的是整个 bundle 大小都是 10MB。&lt;/p&gt;

&lt;p&gt;所以，初步认定应该是最开始的实验（006 - RN lucky-video bundle 大小分析）删除的那张图片不对，应该是删除了一张小图片，所以看着对整个 bundle 大小没有什么影响。&lt;/p&gt;

&lt;h1 id=&quot;一个无心之举&quot;&gt;一个无心之举&lt;/h1&gt;
&lt;p&gt;尝试了不知道多少次，整个屏幕都写着满满的失望。直到。。。&lt;/p&gt;

&lt;p&gt;在试的过程中，有一次无意中没有加参数，然后直接运行 7z a all.7z all/ ，发现出现了奇迹般的效果：&lt;/p&gt;

&lt;p&gt;原始目录及压缩后的文件 | 大小（KB）
:—|:—
all/  |  3712
all.7z  |  1384
en.7z  |  1372
成功的达到预期了！！！&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;所以出问题的是 7z 的参数！！！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;那是哪个参数的问题呢？&lt;/p&gt;

&lt;p&gt;分别试了删除 -mtm- 和 -mx=3 ，最终发现是 -mx=3 这个参数导致的。&lt;/p&gt;

&lt;p&gt;-mx=3 是设置压缩等价为 3，默认是 5。&lt;/p&gt;

&lt;h2 id=&quot;现在-7z-是否还有压缩空间&quot;&gt;现在 7z 是否还有压缩空间？&lt;/h2&gt;
&lt;p&gt;试了一下压缩线上的 7z 文件。&lt;/p&gt;

&lt;p&gt;得到的结果出乎意料：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;文件&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;大小（KB）&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;说明&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;55a68962e6a9c28b78caaaeed1621935.plugin.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10MB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;线上的 plugin 大小&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;55a68962e6a9c28b78caaaeed1621935.plugin&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;19.2MB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;解压后的大小&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;55a68962e6a9c28b78caaaeed1621935.plugin.new.7z&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;3.5MB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;把 -mx=5 后压缩 55a68962e6a9c28b78caaaeed1621935.plugin&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;也就是说，使用新的参数后，能够减少 6.5 MB，也就是能够减少 65% 的大小！！！&lt;/strong&gt;&lt;/p&gt;

&lt;h1 id=&quot;深究-7z-压缩命令的含义&quot;&gt;深究 7z 压缩命令的含义&lt;/h1&gt;
&lt;p&gt;我们现在用的压缩命令是： &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;7z a -mtm- -mx=3&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;a 的含义是启动压缩。&lt;/p&gt;

&lt;p&gt;-m&lt;method_parameter&gt; 是设置压缩方法。&lt;/method_parameter&gt;&lt;/p&gt;

&lt;p&gt;tm 的意思是设置多线程模式（sets multithreading  mode），- 的意思是 off ，也就是关闭多线程模式。&lt;/p&gt;

&lt;p&gt;默认只能在 LZMA、LZMA2 和 BZip2 压缩模式下才能使用。默认的值是 2，即开启 2 个线程。&lt;/p&gt;

&lt;p&gt;然后，这里之所以要关闭，是因为开启多线程模式会清除时间戳（https://sourceforge.net/p/sevenzip/discussion/45797/thread/61905a4c/#5437）。&lt;/p&gt;

&lt;p&gt;x 的意思是设置压缩等级，取值有：0，1，3，5，7，9。默认是 5。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Level&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Method&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Dictionary&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;FastBytes&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;MatchFinder&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Filter&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Copy&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;No compression.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;LZMA2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;64 KB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;32&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;HC4&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;BCJ&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Fastest compressing&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;LZMA2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1 MB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;32&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;HC4&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;BCJ&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Fast compressing&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;5&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;LZMA2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;16 MB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;32&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;BT4&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;BCJ&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Normal compressing&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;7&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;LZMA2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;32 MB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;64&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;BT4&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;BCJ&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Maximum compressing&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;9&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;LZMA2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;64 MB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;64&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;BT4&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;BCJ2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Ultra compressing&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;这里面 3 和 5 的差异主要是两个点：Dictionary 和 MatchFinder。&lt;/p&gt;

&lt;p&gt;Dictionary 一般说的是压缩算法使用的内存区域，用来查找和压缩重复数据模式。&lt;/p&gt;

&lt;p&gt;HC4：具有 4 字节哈希的哈希链，加上具有 3 字节哈希的表（hash chains with 4 bytes hashing, plus a table with 3 bytes hashing）&lt;/p&gt;

&lt;p&gt;BT4：具有 4 字节散列的二叉树，加上具有 3 字节散列的表（binary trees with 4 bytes hashing, plus a table with 3 bytes hashing）&lt;/p&gt;

&lt;p&gt;那到底是哪个差异导致的呢？&lt;/p&gt;

&lt;p&gt;带着这个疑问，我分别试了一个实验，使用两份一样的 862KB 大小的文件，然后分别使用 -mx=1、-mx=3、-mx=5 和 -mx=7 压缩。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;两个 862KB 原始文件&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;-mx=1&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;-mx=3&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;-mx=5&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;-mx=7&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;大小&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;862KB x 2 = 1724 KB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1.3MB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;664KB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;629KB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;629KB&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;在找了一个文件相似率在 90% 以上的两个文件：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;两个相似率 90% 原始文件&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;-mx=1&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;-mx=3&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;-mx=5&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;-mx=7&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;大小&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;862KB x 2 = 1724 KB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1.3MB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;670KB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;634KB&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;634KB&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;从这里可以看出：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;文件大小如果是在 Dictionary 范围内的话，是能够做到相同文件被压缩的。&lt;/li&gt;
  &lt;li&gt;在相似率比较大的情况下 BT4 的压缩率是比 HC4 好的。能减少大概 5.3%。 (664 - 629)/664 = 5.27%  (670-634)/670 = 5.37%&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;结论&quot;&gt;结论&lt;/h1&gt;
&lt;p&gt;不需要做任何的优化。&lt;/p&gt;

&lt;p&gt;主要的两点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;多语言文件内容相似比例接近 100%。压缩算法是可以达到接近 100% 的压缩率。可以不用处理。&lt;/li&gt;
  &lt;li&gt;结合我们最新 lottie 文件大小优化的结果，我们现在所有的 Lottie 文件都已经小于 1MB 了。对应的压缩效果会有一个明显的提升。&lt;/li&gt;
  &lt;li&gt;测试环境的数据是从 8.8MB 降到了 4.1MB，相当于整个包的大小降低了 53.4%。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;然后，是否可以使用 BT4 模式的 MatchFinder，这个需要问一下 SG 那边对应的人，是否可以做相关的参数调整。&lt;/p&gt;
</description>
        <pubDate>Mon, 05 Dec 2022 00:00:00 +0000</pubDate>
        <link>https://yss.github.iohttps://yss.github.io/2022/12/05/the-optimization-of-lottie-files-with-multiple-country-languages.html</link>
        <guid isPermaLink="true">https://yss.github.iohttps://yss.github.io/2022/12/05/the-optimization-of-lottie-files-with-multiple-country-languages.html</guid>
        
        <category>rn</category>
        
        <category>optimize</category>
        
        
        <category>rn</category>
        
      </item>
    
      <item>
        <title>从 master squash 到 release 是否可行</title>
        <description>&lt;h1 id=&quot;背景&quot;&gt;背景&lt;/h1&gt;
&lt;p&gt;现在所有合 master 的分支都要求用 squash 的方式，这样一方面可以减少很多的冲突，另外一个方面也可以极大的缩小 commit 记录。&lt;/p&gt;

&lt;p&gt;减少冲突是因为，合并的时候关注已改动的文件和目标 master 文件的差异，而不关心中间曾经做过的一些无用或者调试类的修改跟其他人的提交产生版本冲突。&lt;/p&gt;

&lt;p&gt;缩小 commit 记录，也是因为每个需求可能会有很多的无用提交。&lt;/p&gt;

&lt;p&gt;对于小需求来说是非常好的，但是大需求的话，可能不是特别友好，毕竟中间过程完全没了，确实存在一些问题。这个要具体问题具体看。&lt;/p&gt;

&lt;p&gt;但整体来说，这个模式是大大提高了我们的工作效率。&lt;/p&gt;

&lt;p&gt;那这个方式能不能应用到 master 合并到 release 呢？&lt;/p&gt;

&lt;h1 id=&quot;现状&quot;&gt;现状&lt;/h1&gt;
&lt;p&gt;然而，现在我们还有一个 release 分支，release 分支只接收两个类型的分支合并：hotfix 和 master。&lt;/p&gt;

&lt;p&gt;hotfix 大部分时候都是一个小提交，通过 squash 或者直接合都问题不大，当然，最好是直接 squash。&lt;/p&gt;

&lt;p&gt;但 master 的话，目前是直接合并，而不是 squash 的方式。&lt;/p&gt;

&lt;p&gt;正常情况下一个 hotfix 流程是：&lt;/p&gt;

&lt;p&gt;创建 hotfix 分支 → 开发 → 测试 → 合并到 release → 发布 → 合并到 master&lt;/p&gt;

&lt;p&gt;master 和 release 是一个相互合并的过程。&lt;/p&gt;

&lt;h1 id=&quot;对比&quot;&gt;对比&lt;/h1&gt;
&lt;p&gt;| |优势|缺点
:—-|:—–
merge directly 方式	| 能保留各个需求的提交记录 |
merge with squash 方式 | | 发 hotfix 后 merge 回来可能会产生非常大的冲突。&lt;/p&gt;

&lt;p&gt;主要是 release 是一个长期维护的分支通过 squash 来合代码，会导致它和其他正常开发分支产生严格的割裂。&lt;/p&gt;

&lt;h1 id=&quot;结论&quot;&gt;结论&lt;/h1&gt;
&lt;p&gt;维持现状。&lt;/p&gt;

&lt;p&gt;依旧采用 merge directly。&lt;/p&gt;
</description>
        <pubDate>Tue, 21 Jun 2022 00:00:00 +0000</pubDate>
        <link>https://yss.github.iohttps://yss.github.io/2022/06/21/is-it-better-that-merge-master-branch-to-release-branch-with-squash.html</link>
        <guid isPermaLink="true">https://yss.github.iohttps://yss.github.io/2022/06/21/is-it-better-that-merge-master-branch-to-release-branch-with-squash.html</guid>
        
        <category>git</category>
        
        
        <category>git</category>
        
      </item>
    
      <item>
        <title>RN 计算 JS 线程下的各个组件执行耗时和次数方案</title>
        <description>&lt;h1 id=&quot;前言&quot;&gt;前言&lt;/h1&gt;

&lt;p&gt;我们现在是有冷启动的各个性能埋点的，但各个点的粒度都太粗了，它可以反应出整个性能的走势，是性能参考的核心指标。&lt;/p&gt;

&lt;p&gt;在切切实实去做性能优化的时候，只能提供一个方向上的指导。就是其中哪一部分数组偏大应该去优化。&lt;/p&gt;

&lt;p&gt;但完全给不了具体的优化建议。&lt;/p&gt;

&lt;p&gt;而我们在做性能优化分析的时候，就需要找到更细粒度，更具体的数据，要能够深入到各个组件的执行耗时和次数。&lt;/p&gt;

&lt;p&gt;这样，我们就能通过分析这些数据去发现我们代码逻辑、流程、乃至整个架构上的问题，之后再针对性的去做实际的优化。&lt;/p&gt;

&lt;h1 id=&quot;目的&quot;&gt;目的&lt;/h1&gt;
&lt;p&gt;计算 JS 线程下的各个组件执行耗时和次数。&lt;/p&gt;

&lt;h1 id=&quot;调研&quot;&gt;调研&lt;/h1&gt;
&lt;h2 id=&quot;现有工具&quot;&gt;现有工具&lt;/h2&gt;
&lt;p&gt;第一步想看看市面上是否有这样功能的工具，可以拿到所有函数渲染时间，然后我们做一层过滤，拿到我们自己业务所有组件的渲染时间。&lt;/p&gt;

&lt;p&gt;最开始各种都看，但渐渐的发现通过 Native 相关的工具是拿不到的。因为所有的 RN 组件都是在额外的 js 线程执行的。&lt;/p&gt;

&lt;p&gt;所以还是要回归到去找 js 及 react 相关的工具。&lt;/p&gt;

&lt;h3 id=&quot;react-devtools&quot;&gt;react-devtools&lt;/h3&gt;
&lt;p&gt;react-devtools 是一款专门为非浏览器环境下的 React 应用做调试和分析的工具。&lt;/p&gt;

&lt;p&gt;通过在测试环境运行，确实是能通过可视化的树状图看到各个 commit 中都有哪些是渲染的：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/rn-profiler.png&quot; alt=&quot;rn-profiler&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是，我们的项目非常的庞大，像上面这张图，我们只能看到是谁渲染时间比较长，但因为组件太多，而且一个流程下来会触发非常多的 commit 。&lt;/p&gt;

&lt;p&gt;这样一来没法直观的去看到数据。&lt;/p&gt;

&lt;p&gt;那有没有一种方式能拿到这些原始数据呢？&lt;/p&gt;

&lt;p&gt;但在网上搜了很久，也看过它本身的 githu吧，但都没有找到过。&lt;/p&gt;

&lt;h3 id=&quot;react-profiler&quot;&gt;React Profiler&lt;/h3&gt;
&lt;p&gt;回归到 React，React 本身是提供了一个 Profiler 的组件，它可以测量一个 React 组件多久渲染一次以及渲染一次的耗时。&lt;/p&gt;

&lt;p&gt;我们可以通过在我们需要测量的组件外层包裹一个 Profiler 就可以做到。&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Profiler&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Navigation&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;onRender&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Navigation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/Profiler&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Profiler 总共有两个参数： id 和 onRender&lt;/p&gt;

&lt;p&gt;参数 | 详细说明
:—-|:—–
id  |  唯一标识，值为 string  &lt;br /&gt;
onRender  |&lt;br /&gt;
    onRenderCallback(&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;id, // 对应上面的 “id”

phase, // &quot;mount&quot; （组件第一次加载） 或者 &quot;update&quot; （重新渲染）

actualDuration, // 本次更新 committed 花费的渲染时间

baseDuration, // 估计不使用 memoization 的情况下渲染整棵子树需要的时间

startTime, // 本次更新中 React 开始渲染的时间

commitTime, // 本次更新中 React committed 的时间

interactions // 属于本次更新的 interactions 的集合

)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里面就涉及到一个很重要的概念，那就是 React 的两个主要阶段：render 阶段和 commit 阶段。&lt;/p&gt;

&lt;p&gt;React16 引入 Fiber 架构后，将整个调度分为了两个阶段 render &amp;amp; commit，在 render 阶段，React 会计算 DOM 的更新，并将所有需要更新的 fiber 整理成一个 effect list，在 commit  阶段中， React 会遍历  effect list  执行所有的副作用，期间会执行更新相关的生命周期、挂载 DOM 等等。&lt;/p&gt;

&lt;p&gt;直观上来说，render 阶段就是组件调用 render 方法。commit 阶段就是把更新的内容渲染到界面。&lt;/p&gt;

&lt;p&gt;通过 Profiler 是可以拿到 commit 的数据。&lt;/p&gt;

&lt;p&gt;那我们是不是可以有一种方式，可以在需要的时候做到自动的把 Profiler 包裹到我们需要包裹的组件上呢？&lt;/p&gt;

&lt;h1 id=&quot;实现&quot;&gt;实现&lt;/h1&gt;

&lt;p&gt;这里也是看了很久，终于是在 why did you render 这个库上找到一个有用的信息。&lt;/p&gt;

&lt;p&gt;就是拦截 React.createElement 和 React.cloneElement。&lt;/p&gt;

&lt;p&gt;因为我们所有的组件 render 最终都是调用的这个最顶级方法做到的。比如：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Main&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;React&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;propObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过拦截 React.createElement ，然后一个包裹 Profiler 的组件。即&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Main&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;React&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Profiler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{...},&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;React&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;propObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;拦截-reactcreateelement&quot;&gt;拦截 React.createElement&lt;/h2&gt;
&lt;h3 id=&quot;编译环节&quot;&gt;编译环节&lt;/h3&gt;
&lt;p&gt;最开始想的是在编译环节引入，可以在 preset-react 的时候通过 importSource 来改变引入的 React。&lt;/p&gt;

&lt;p&gt;这个还有待验证。。。&lt;/p&gt;

&lt;h3 id=&quot;入口环节&quot;&gt;入口环节&lt;/h3&gt;
&lt;p&gt;在第一个入口文件直接替换 React.createElement 。&lt;/p&gt;

&lt;p&gt;目前通过实践，发现这个方法是可行的。主要原因是因为 React 的打包模式用的是 commonjs 规范。&lt;/p&gt;

&lt;p&gt;这样只要有改动就全局生效。&lt;/p&gt;

&lt;h2 id=&quot;黑名单机制&quot;&gt;黑名单机制&lt;/h2&gt;
&lt;p&gt;除了拦截，我们还需要过滤掉非业务组件。目前主要包括两大类：React Native 自带的组件 和 react-native-sdk 里的组件。&lt;/p&gt;

&lt;p&gt;通过黑名单的机制，我们列出上述所有的组件名。&lt;/p&gt;

&lt;p&gt;然后，在拦截的时候判断是在黑名单里忽略，直接返回。反之，就包裹一层 Profiler。&lt;/p&gt;

&lt;p&gt;获取组件名的实现：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getDisplayName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;displayName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getDisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getDisplayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;component&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Unknown&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;计算值&quot;&gt;计算值&lt;/h2&gt;
&lt;p&gt;主要是围绕 Profiler 的 onRender 函数的参数做处理。&lt;/p&gt;

&lt;p&gt;目前需要拿到的三个值：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;id：组件名&lt;/li&gt;
  &lt;li&gt;render 时间：commitTime - startTime - actualDuration  &lt;strong&gt;这个是有问题的&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;commit 时间：actualDuration&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;问题&quot;&gt;问题&lt;/h1&gt;
&lt;p&gt;Q: 某个组件被多次引入如何区分&lt;/p&gt;

&lt;p&gt;目前还没有一个好的自动化方式做区分，只能手动设置 displayName 属性来区分。&lt;/p&gt;
</description>
        <pubDate>Thu, 09 Jun 2022 00:00:00 +0000</pubDate>
        <link>https://yss.github.iohttps://yss.github.io/2022/06/09/the-solutions-of-compute-the-cost-time-and-rendering-time-in-each-rn-components.html</link>
        <guid isPermaLink="true">https://yss.github.iohttps://yss.github.io/2022/06/09/the-solutions-of-compute-the-cost-time-and-rendering-time-in-each-rn-components.html</guid>
        
        <category>rn</category>
        
        
        <category>rn</category>
        
      </item>
    
      <item>
        <title>微前端在 Video 后台项目中的应用</title>
        <description>&lt;h1 id=&quot;背景&quot;&gt;背景&lt;/h1&gt;
&lt;p&gt;Shopee Video 是一个新成立不久的部门，也是一个快速增长的部门。最初我们前端只有一个组，十来号人，但随着公司对 Shopee Video 的定位调整和业务扩张，Shopee Video 迎来了一个极快速的增长，团队的整体规模翻了不知道多少倍。&lt;/p&gt;

&lt;p&gt;然后，在业务上划分成各个子业务线，每个子业务线都有自己独立的前端团队。&lt;/p&gt;

&lt;p&gt;而我们的 B 端系统最初都在一块，不同团队共同开发和维护一份代码，无法避免的就会出现各种各样的团队协作问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/1.png&quot; alt=&quot;micro-fe-1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上面这类的问题，随着团队逐步变大，问题越来越严重。&lt;/p&gt;

&lt;p&gt;为了要解决这些问题，我们已经有意识的在拆分我们的项目。&lt;/p&gt;

&lt;p&gt;初期一个团队，一个新域名，一个新工程。&lt;/p&gt;

&lt;p&gt;但这种完全隔离式的拆分，对于用户来说是非常不友好的。&lt;/p&gt;

&lt;p&gt;基于用户的使用场景以及业界现有的一些优秀方案，我们对现有工程进行了微前端的实践与落地。&lt;/p&gt;

&lt;h1 id=&quot;微前端的定义&quot;&gt;微前端的定义&lt;/h1&gt;
&lt;p&gt;微前端并不是一项新的技术，而是一种浏览器端的架构理念。&lt;/p&gt;

&lt;p&gt;它是一种多个团队通过独立发布功能的方式来共同构建现代化 Web 应用的技术手段及方法策略。&lt;/p&gt;

&lt;p&gt;这是网上的定义。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/2.png&quot; alt=&quot;micro-fe-2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通俗来讲，就是微前端提供了一种技术实现，可以将多个独立的 Web 应用聚合到一起，提供统一的访问入口。&lt;/p&gt;

&lt;p&gt;在用户视角上看一个微前端应用就是一个完整的应用，但是在技术视角上看微前端应用就是由一个个独立的前端应用组合而成的。&lt;/p&gt;

&lt;h1 id=&quot;微前端的技术实现&quot;&gt;微前端的技术实现&lt;/h1&gt;
&lt;p&gt;技术实现上，分主应用和子应用。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/3.png&quot; alt=&quot;micro-fe-3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;主应用我们一般叫它基座应用，基座应用主要是负责调度各个子应用。&lt;/p&gt;

&lt;p&gt;对应的，子应用是一个个负责具体的业务逻辑的应用。&lt;/p&gt;

&lt;p&gt;正常情况下，为了保证整个框架的可用性，基座应用不应该包含任何的业务逻辑。&lt;/p&gt;

&lt;h1 id=&quot;微前端实施的五种方式&quot;&gt;微前端实施的五种方式&lt;/h1&gt;
&lt;h2 id=&quot;路由分发微前端&quot;&gt;路由分发微前端&lt;/h2&gt;
&lt;p&gt;路由分发式微前端，即通过路由将不同的业务分发到不同的、独立前端应用上。&lt;/p&gt;

&lt;p&gt;其通常可以通过 HTTP 服务器的反向代理来实现，又或者是应用框架自带的路由来解决。&lt;/p&gt;

&lt;p&gt;如图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/4.png&quot; alt=&quot;micro-fe-4&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个一般是前期团队拆分最常见的一个种方式，简单明了。&lt;/p&gt;

&lt;p&gt;我们之前的拆分做法跟这个方式有点类似，不过是彻底的拆分。&lt;/p&gt;

&lt;h3 id=&quot;优点&quot;&gt;优点&lt;/h3&gt;
&lt;p&gt;快速实施和部署，没有历史包袱。&lt;/p&gt;
&lt;h3 id=&quot;缺点&quot;&gt;缺点&lt;/h3&gt;
&lt;p&gt;对于使用者来说，需要在各个系统跳来跳去，体验不是很好。
后期多套系统各自分散，不好协调。&lt;/p&gt;
&lt;h3 id=&quot;适合的场景&quot;&gt;适合的场景&lt;/h3&gt;
&lt;p&gt;团队的初期拆分。
系统需要做大的迁移，特别是需要在技术栈上做大的切换。
不想花费大量的时间在这个系统的改造上。&lt;/p&gt;
&lt;h2 id=&quot;iframe-式微前端&quot;&gt;iframe 式微前端&lt;/h2&gt;
&lt;p&gt;iframe 作为一个非常古老的，人人都觉得普通的技术，却一直很管用。&lt;/p&gt;

&lt;p&gt;iframe 可以创建一个全新的、独立的宿主环境，这意味着我们的前端应用之间可以相互独立运行。&lt;/p&gt;

&lt;p&gt;采用 iframe 有几个重要的前提：&lt;/p&gt;

&lt;p&gt;网站不需要 SEO 支持
拥有相应的应用管理和通信机制。
如果我们做的是一个应用平台，会在我们的系统中集成第三方系统，显然这是一个不错的方案。&lt;/p&gt;

&lt;h3 id=&quot;优点-1&quot;&gt;优点&lt;/h3&gt;
&lt;p&gt;提供了浏览器原生的硬隔离方案，不论是样式隔离、js 隔离这类问题统统都能被完美解决。&lt;/p&gt;
&lt;h3 id=&quot;缺点-1&quot;&gt;缺点&lt;/h3&gt;
&lt;p&gt;应用间上下文无法被共享，随之带来各种开发体验、产品体验一系列问题。&lt;/p&gt;
&lt;h3 id=&quot;适合场景&quot;&gt;适合场景&lt;/h3&gt;
&lt;p&gt;集成第三方系统。
在新系统中快速集成部分老旧系统功能。&lt;/p&gt;
&lt;h2 id=&quot;微应用化&quot;&gt;微应用化&lt;/h2&gt;
&lt;p&gt;微应用化是指在开发时应用都是以单一、微小应用的形式存在的，而在运行时，则是通过构建系统合并这些应用，并组合成一个新的应用。&lt;/p&gt;

&lt;p&gt;微应用化早期大都是以软件工程的方式来完成前端应用的聚合，而现在可以使用 Webpack 5 提供了 Module Federation 的方式来解决这类聚合的问题。&lt;/p&gt;

&lt;p&gt;微应用化只能使用唯一的一种前端框架。&lt;/p&gt;

&lt;p&gt;如图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/5.png&quot; alt=&quot;micro-fe-5&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;优点-2&quot;&gt;优点&lt;/h3&gt;
&lt;p&gt;适合大一统的团队，可以保持整个团队的技术栈和开发风格的统一，各个成员可以无缝切换到任意的系统。
技术层面可以得到持续的积累，持续的改进。&lt;/p&gt;
&lt;h3 id=&quot;缺点-2&quot;&gt;缺点&lt;/h3&gt;
&lt;p&gt;依赖升级比较麻烦，需要统一升级。
内部协调较为频繁而复杂。特别是一些不兼容的升级。&lt;/p&gt;
&lt;h3 id=&quot;适合场景-1&quot;&gt;适合场景&lt;/h3&gt;
&lt;p&gt;要求快速及统一的大团队。&lt;/p&gt;
&lt;h2 id=&quot;前端微服务化&quot;&gt;前端微服务化&lt;/h2&gt;
&lt;p&gt;前端微服务化，是微服务架构在前端的实施，每个前端应用都是完全独立（技术栈、开发、部署、构建独立）、自主运行的，最后通过模块化的方式组合出完整的应用。采用这种方式意味着，一个页面上可以同时存在两个以上的前端应用在运行。&lt;/p&gt;

&lt;p&gt;如图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/6.png&quot; alt=&quot;micro-fe-6&quot; /&gt;&lt;/p&gt;

&lt;p&gt;目前主流的框架有 qiankun，qiankun 是基于 single-spa 的封装。&lt;/p&gt;

&lt;h3 id=&quot;优点-3&quot;&gt;优点&lt;/h3&gt;
&lt;p&gt;技术栈无关，任意技术栈的应用均可 使用/接入，不论是 React/Vue/Angular/JQuery 还是其他等框架
样式隔离，确保微应用之间样式互相不干扰
JS 沙箱，确保微应用之间 全局变量/事件 不冲突&lt;/p&gt;
&lt;h3 id=&quot;缺点-3&quot;&gt;缺点&lt;/h3&gt;
&lt;p&gt;主要是功能上还有一些欠缺，不能完美达到 iframe 基本的隔离，然后在 Angular 上的问题会比较多些。&lt;/p&gt;
&lt;h3 id=&quot;适合场景-2&quot;&gt;适合场景&lt;/h3&gt;
&lt;p&gt;多个团队共同构建一个大型项目。&lt;/p&gt;
&lt;h2 id=&quot;web-components&quot;&gt;Web Components&lt;/h2&gt;
&lt;p&gt;Web Components 是一套不同的技术，允许开发者创建可重用的定制元素（它们的功能封装在代码之外）并且在 Web 应用中使用它们。&lt;/p&gt;

&lt;p&gt;在真正的项目上使用 Web Components技术，离现在还有一些距离，结合 Web Components 来构建前端应用，是一种面向未来演进的架构。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/7.png&quot; alt=&quot;micro-fe-7&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;优点-4&quot;&gt;优点&lt;/h3&gt;
&lt;p&gt;通过 web component 形式可以做到比较完美的隔离。&lt;/p&gt;
&lt;h3 id=&quot;缺点-4&quot;&gt;缺点&lt;/h3&gt;
&lt;p&gt;需要重写现有的前端应用。
上下游生态系统不完善。
系统架构复杂。当应用被拆分为一个又一个的组件时，组件间的通讯就成了一个特别大的麻烦。&lt;/p&gt;

&lt;h1 id=&quot;微前端方案的对比&quot;&gt;微前端方案的对比&lt;/h1&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;开发成本&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;维护成本&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;可行性&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;同一框架要求&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;实现难度&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;潜在风险及存在的问题&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt; &lt;/th&gt;
      &lt;th&gt; &lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;路由式分发微前端&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;低&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;低&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;高&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;否&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;★&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;跟我们需要达成的目标不符合&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;iframe&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;式微前端&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;低&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;中&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;中&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;否&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;★★★&lt;/td&gt;
      &lt;td&gt;需要搭建统一的应用管理和通信机制&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;微应用化&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;低&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;高&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;中&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;是&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;★★★★&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;不同团队协作成本高&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;前端微服务化&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;中&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;中&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;高&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;否&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;★★&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;统一不同应用的构建规范&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Web Components&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;高&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;低&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;低&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;否&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;★★★★★&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;现有项目下不太可行&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;基于我们现在的组织架构和分工协作方式。采用前端微服务化，会是一个更好的选择。&lt;/p&gt;

&lt;p&gt;而前端微服务化，&lt;strong&gt;首推 qiankun 的解决方案。&lt;/strong&gt;&lt;/p&gt;

&lt;h1 id=&quot;single-spa-简介&quot;&gt;Single-SPA 简介&lt;/h1&gt;
&lt;p&gt;Single-SPA 是一个用于前端微服务化的 JavaScript 前端解决方案。&lt;/p&gt;

&lt;p&gt;它本身就相当于大号的路由分发和调度程序。&lt;/p&gt;

&lt;p&gt;而每个应用本身相当于一个大模块。对 Single-SPA 暴露三个方法：bootstrap, mount, unmount。&lt;/p&gt;

&lt;p&gt;比如：Single-SPA 收到一个路由切换从 /app1  =&amp;gt; /app2，那么它就会调用 App 1 的 unmount，然后再调用 App 2 的 bootstrap，等到执行完后，再调用 App 2 的 mount。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/8.png&quot; alt=&quot;micro-fe-8&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但是，本身 Single-SPA 不提供 js 隔离和 css 隔离，需要引入其他包来做到。&lt;/p&gt;

&lt;p&gt;做不到那种开箱即用。&lt;/p&gt;

&lt;h1 id=&quot;qiankun-简介&quot;&gt;qiankun 简介&lt;/h1&gt;
&lt;h2 id=&quot;特性&quot;&gt;特性&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;基于 single-spa 封装，提供了更加开箱即用的 API。&lt;/li&gt;
  &lt;li&gt;技术栈无关，任意技术栈的应用均可 使用/接入，不论是 React/Vue/Angular/JQuery 还是其他等框架。&lt;/li&gt;
  &lt;li&gt;HTML Entry 接入方式，让你接入微应用像使用 iframe 一样简单。&lt;/li&gt;
  &lt;li&gt;样式隔离，确保微应用之间样式互相不干扰。&lt;/li&gt;
  &lt;li&gt;JS 沙箱，确保微应用之间 全局变量/事件 不冲突。&lt;/li&gt;
  &lt;li&gt;资源预加载，在浏览器空闲时间预加载未打开的微应用资源，加速微应用打开速度。&lt;/li&gt;
  &lt;li&gt;umi 插件，提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;样式隔离&quot;&gt;样式隔离&lt;/h2&gt;
&lt;h3 id=&quot;strictstyleisolation-shadow-dom&quot;&gt;strictStyleIsolation: Shadow DOM&lt;/h3&gt;
&lt;p&gt;严格样式隔离，核心是 Shadow DOM。&lt;/p&gt;

&lt;p&gt;实现形式为将整个子应用放到 Shadow DOM 内进行嵌入，完全隔离了主子应用。&lt;/p&gt;

&lt;p&gt;目前从社区的反馈看，使用这种方式会存在一些问题，比如：子应用的 Dialog 之类的因找不到主应用的 body 会丢失，或跑到整个屏幕外。&lt;/p&gt;

&lt;h3 id=&quot;experimentalstyleisolationcss-scope&quot;&gt;experimentalStyleIsolation：CSS Scope&lt;/h3&gt;
&lt;p&gt;qiankun 会自动为子应用所有的样式增加后缀标签，如：div[data-qiankun-microName]。&lt;/p&gt;

&lt;p&gt;这样改后就会存在一些挂载在 body 的弹窗样式失效。&lt;/p&gt;

&lt;p&gt;所以，最终我们在实践过程中并没有使用样式隔离。&lt;/p&gt;

&lt;p&gt;而是通过 qiankun 本身的 css 解析做到的隔离。也就是 qiankun 解析 css link，然后通过 style 的方式插入到子应用挂载的节点下。&lt;/p&gt;

&lt;p&gt;跟子应用一起创建，一起销毁。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/9.png&quot; alt=&quot;micro-fe-9&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;js-沙箱实现&quot;&gt;JS 沙箱实现&lt;/h2&gt;
&lt;p&gt;qiankun 中沙箱的实现有三种：SnapshotSandbox、LegacySandbox、ProxySandbox。&lt;/p&gt;

&lt;p&gt;对于我们来说，需要简单理解 ProxySandbox。&lt;/p&gt;

&lt;h3 id=&quot;snapshotsandbox&quot;&gt;SnapshotSandbox&lt;/h3&gt;
&lt;p&gt;在页面初始化后，通过变量 window 对象，备份了一份当前的 window 对象快照。等子应用每次切换的时候再把 window 对象恢复到备份时的状态。&lt;/p&gt;

&lt;p&gt;这个主要是在不支持 Proxy 的浏览器上使用。&lt;/p&gt;

&lt;h3 id=&quot;legacysanbox&quot;&gt;LegacySanbox&lt;/h3&gt;
&lt;p&gt;它的主要原理就是使用了 ES6 中的 Proxy，把原来的 window 代理到 fakeWindow 上，这样就不用遍历整个 window 去应用和恢复环境了。&lt;/p&gt;

&lt;p&gt;然后，在沙箱内部设置了三个变量池：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;addedPropsMapinSandbox 用于存放子应用运行期间新增的全局变量，用于在卸载子应用的时候删除；&lt;/li&gt;
  &lt;li&gt;modifiedPropsOriginalMapInSandbox 用于存放子应用运行期间修改的全局变量，用于卸载时进行恢复；&lt;/li&gt;
  &lt;li&gt;currentUpdatedPropsValueMap 用于存放子应用运行期间所有变化的变量，这样可以在加载子应用时恢复其上一次的环境。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;但这个实现有个问题就是不支持同时存在多个子应用。&lt;/p&gt;

&lt;h3 id=&quot;proxysanbox&quot;&gt;ProxySanbox&lt;/h3&gt;
&lt;p&gt;这是 LegacySanbox 改进版本，而且实现更为简单。也是目前 qiankun 默认采用的方式。&lt;/p&gt;

&lt;p&gt;每次对 window 取值的时候，先从自己沙箱环境的 fakeWindow 里面找，如果不存在，就从 rawWindow (原始的 window )里去找；当对沙箱内部的 window 对象赋值的时候，会直接操作 fakeWindow ，而不会影响到 rawWindow。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/10.png&quot; alt=&quot;micro-fe-10&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;工程实践概览&quot;&gt;工程实践概览&lt;/h1&gt;
&lt;h2 id=&quot;主应用注册&quot;&gt;主应用注册&lt;/h2&gt;
&lt;p&gt;基于 qiankun 的微前端方式是比较简单的，对于基座应用来说就是简单的注册各个子应用的路由：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;registerMicroApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Video Core Web&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/video-core-web/index.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#micro-app&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;activeRule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/video-core-web/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;主要就两个值：entry 和 activeRule。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;entry 是入口：用于 qiankun 子应用的入口文件。
`. activeRule 就是需要匹配的 url path 前缀。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;子应用接入&quot;&gt;子应用接入&lt;/h2&gt;
&lt;p&gt;子应用是一个完全独立的应用。在实现可以按照自己的团队需要做任意的处理。&lt;/p&gt;

&lt;p&gt;但，因为要放到基座应用里去运行，所以还是需要加一些额外的处理。&lt;/p&gt;

&lt;h3 id=&quot;一是打包配置修改&quot;&gt;一是打包配置修改&lt;/h3&gt;
&lt;p&gt;对应的是要修改 webpack.config.js 的 output 属性：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;webpack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;js&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/package&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 
&lt;span class=&quot;c1&quot;&gt;//...&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rsv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./dist&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;library&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-[name]`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 项目名-[name]，防止 umd 层面模块名冲突&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;libraryTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;umd&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;二是新增一个-public-pathts-文件&quot;&gt;二是新增一个 public-path.ts 文件&lt;/h3&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ts&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;__webpack_public_path__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;__POWERED_BY_QIANKUN__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;__webpack_public_path__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;__INJECTED_PUBLIC_PATH_BY_QIANKUN__&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;三是修改入口-apptsx&quot;&gt;三是修改入口 app.tsx&lt;/h3&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tsx&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./public-path&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 需要在第一行&lt;/span&gt;
  
&lt;span class=&quot;c1&quot;&gt;// 包裹之前的 render 处理&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ReactDOM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#app&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;__POWERED_BY_QIANKUN__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bootstrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app bootstraped&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;unmount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ReactDOM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unmountComponentAtNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#app&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;就这样三步就完成了一个子应用的改造。&lt;/p&gt;

&lt;p&gt;qiankun 官网也给出了实践，具体可以看：https://qiankun.umijs.org/zh/guide/tutorial#react-%E5%BE%AE%E5%BA%94%E7%94%A8&lt;/p&gt;

&lt;h2 id=&quot;运行流程&quot;&gt;运行流程&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/micro-fe/11.png&quot; alt=&quot;micro-fe-11&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;最后&quot;&gt;最后&lt;/h1&gt;

&lt;p&gt;真实在实际应用上，微前端只是一个基础的技术方案。我们做得更多的是，技术上怎么让其他团队能够快速的接入，业务上如何让各个团队做更少的改动。&lt;/p&gt;

&lt;p&gt;最后，所有微前端相关的文档都在这里：Micro-FE Docs 大家有兴趣可以去看。&lt;/p&gt;

</description>
        <pubDate>Mon, 06 Jun 2022 00:00:00 +0000</pubDate>
        <link>https://yss.github.iohttps://yss.github.io/2022/06/06/the-micro-fe-application-in-the-video-project.html</link>
        <guid isPermaLink="true">https://yss.github.iohttps://yss.github.io/2022/06/06/the-micro-fe-application-in-the-video-project.html</guid>
        
        <category>project</category>
        
        
        <category>project</category>
        
      </item>
    
      <item>
        <title>RN 性能优化总体指导</title>
        <description>&lt;h1 id=&quot;前言&quot;&gt;前言&lt;/h1&gt;
&lt;p&gt;为什么要写这篇文章呢？&lt;/p&gt;

&lt;p&gt;一个是记录后面我们怎么去优化。&lt;/p&gt;

&lt;p&gt;另外一个点就是总结一些经验，指导后面的优化方向。&lt;/p&gt;

&lt;h1 id=&quot;存在的情况&quot;&gt;存在的情况&lt;/h1&gt;
&lt;p&gt;我们在面对性能优化的时候，有的时候可能不知道如何着手，有各种各样的原因，但最重要的一个问题是没有一个方向，想到什么有问题，觉得什么有问题，就一股脑的去做。&lt;/p&gt;

&lt;p&gt;这样下来，看似做了很多事情，一通优化下来，也确实是有提高了，但是，别人再问你后面应该如何优化，应该怎么去做。&lt;/p&gt;

&lt;p&gt;你会发现你还是一片茫然。&lt;/p&gt;

&lt;h1 id=&quot;总体指导思想&quot;&gt;总体指导思想&lt;/h1&gt;
&lt;p&gt;我们在面对着这一系列问题的时候，其实最缺乏的是一个总体指导思想。&lt;/p&gt;

&lt;p&gt;那什么是一个总体指导思想呢？&lt;/p&gt;

&lt;p&gt;总体指导思想是一个整体解决问题的方式方法的总结。&lt;/p&gt;

&lt;p&gt;更多的是通过过往的经验和教训得到的。&lt;/p&gt;

&lt;p&gt;而在这里所要阐述的，更多的是我个人的经验和教训下，在遇到性能优化，应该如何去做的。&lt;/p&gt;

&lt;h1 id=&quot;解决&quot;&gt;解决&lt;/h1&gt;
&lt;h2 id=&quot;第一步梳理现状&quot;&gt;第一步，梳理现状&lt;/h2&gt;
&lt;p&gt;在做性能优化的第一步，不是一股脑的直接按照现有的优化规则去做，也不是去直接找其他人或其他团队去问是否有遇到同样的问题。&lt;/p&gt;

&lt;p&gt;更多的是要去理清我们项目的实际情况。从我们自身项目实际情况出发，找到我们自身的问题所在，特别是问题的大头在哪里。&lt;/p&gt;

&lt;p&gt;比如网络上很多的前端优化的 xx 条军规。他们确实总结得非常的好，也非常实用。但是不同的项目，不同的团队下都有非常大的业务差异。直接拿过来用，不是不行，只是很可能解决不了实际问题。&lt;/p&gt;

&lt;h3 id=&quot;反面例子&quot;&gt;反面例子&lt;/h3&gt;

&lt;p&gt;我在面试的时候，经常会让候选人说一个自己解决的性能优化问题。有一次就说到 SSR 的优化，比如一些弹窗，登录组件等等不涉及 SEO 的组件设置为不需要 SEO，也不需要直接呈现的都设置为非服务端渲染。&lt;/p&gt;

&lt;p&gt;我觉得这个做法非常好，也非常的认同，但是当我问及这个优化的实际效果呢？&lt;/p&gt;

&lt;p&gt;他回答不出来，一个是他没有实际的数据，另外一个就是他的优化方向本身就是错的。&lt;/p&gt;

&lt;p&gt;你在 Node.js 服务端本身已经有缓存了，上面的这些优化只是优化了第一次的执行速度。但对整个服务来说，后面 N 多个请求都是拿的缓存，那这个优化又有什么意义呢？&lt;/p&gt;

&lt;h3 id=&quot;如何做&quot;&gt;如何做&lt;/h3&gt;

&lt;p&gt;那我们具体应该如何去梳理现状呢？&lt;/p&gt;

&lt;p&gt;总共三个点：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;核心指标&lt;/li&gt;
  &lt;li&gt;执行链路&lt;/li&gt;
  &lt;li&gt;性能埋点&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/rn-opt-1.png&quot; alt=&quot;rn-opt-1&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第一个点核心指标&quot;&gt;第一个点：核心指标&lt;/h3&gt;

&lt;p&gt;核心指标就是要明确做这个优化，我们最最核心要关注的点，也是我们所有优化效果的最终衡量指标。&lt;/p&gt;

&lt;p&gt;这个核心指标需要根据我们自己实际的业务，实际的项目和团队去共同制定并确认。&lt;/p&gt;

&lt;p&gt;确定了这个核心指标，就好比有了前行的目标，后面要做的事情就是围绕这个目标一步步前行。&lt;/p&gt;

&lt;h3 id=&quot;第二个点执行链路&quot;&gt;第二个点：执行链路&lt;/h3&gt;

&lt;p&gt;执行链路指的是我们自己开发的程序是怎么一步步被执行的，更重要的是从用户角度出发，它是在哪个环节被用户使用到的。&lt;/p&gt;

&lt;p&gt;一个是程序执行：这个就是我们代码是怎么运行。&lt;/p&gt;

&lt;p&gt;第二个是用户使用我们产品的行为都有哪些，对应的是否会直接影响到我们的程序执行。&lt;/p&gt;

&lt;p&gt;有了这些才能更好的指导后面的性能埋点应该如何去做。&lt;/p&gt;

&lt;h3 id=&quot;第三个点性能埋点&quot;&gt;第三个点：性能埋点&lt;/h3&gt;

&lt;p&gt;性能埋点就是最终践行我们核心指标的一个个点。&lt;/p&gt;

&lt;p&gt;一个核心指标是一个点，但同时一个核心指标可以是很多个小埋点组成。&lt;/p&gt;

&lt;p&gt;我们通过执行链路，把一个核心指标划分成很多个阶段，每个阶段都做一个埋点。&lt;/p&gt;

&lt;p&gt;以此能分析出各个阶段的指标数据，既而知道后续的优化方向。&lt;/p&gt;

&lt;p&gt;比如，我们 RN 项目的冷启动时间，可以拆分成：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;引擎加载完 Bundle 的时间&lt;/li&gt;
  &lt;li&gt;Bundle 初始化的时间&lt;/li&gt;
  &lt;li&gt;第一个页面网络数据请求时间&lt;/li&gt;
  &lt;li&gt;页面渲染时间&lt;/li&gt;
  &lt;li&gt;播放器播放加载时间&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;通过这些数据，我们就能非常直观明了的知道我们的性能问题所在了。&lt;/p&gt;

&lt;p&gt;接下来要做的就是如何去优化了。&lt;/p&gt;

&lt;h2 id=&quot;第二步逐个逐步优化&quot;&gt;第二步，逐个逐步优化&lt;/h2&gt;

&lt;p&gt;逐个逐步优化的核心就是我们每次都聚焦在一个点上，然后去通过优化这个点去达到我们的一个小目标。&lt;/p&gt;

&lt;p&gt;之后再重新走一遍这个流程，直到优化到我们认可的一个效果，或者没法再进一步优化为止。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/rn-opt-2.png&quot; alt=&quot;rn-opt-2&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;性能点分析&quot;&gt;性能点分析&lt;/h3&gt;

&lt;p&gt;这一阶段就是拿到我们的埋点数据，一个点一个点的数据去看。&lt;/p&gt;

&lt;p&gt;结合各个维度去核实这些数据是不是有优化空间。&lt;/p&gt;

&lt;p&gt;同时，还需要分析各个点的耗时情况，针对耗时特别大的，可以再看看这个点是不是可以再细化到更小的点。&lt;/p&gt;

&lt;p&gt;特别注意的是，&lt;/p&gt;

&lt;p&gt;这里面可能还有一些技术性的指标，不能在这些埋点数据体现的话，就需要回归第一步去定义这些技术性指标，并且是一个可量化的技术性指标。&lt;/p&gt;

&lt;h3 id=&quot;优化点明确&quot;&gt;优化点明确&lt;/h3&gt;

&lt;p&gt;这一阶段是明确我们接下来要优化的目标。&lt;/p&gt;

&lt;p&gt;期间，我们可能会有很多需要优化的地方，但我们需要更聚焦于一个更具体的点，更能体现效果的点。&lt;/p&gt;

&lt;p&gt;首先，明确的标准应该是我们当前遇到的最急迫，以及最需要去优化的点。&lt;/p&gt;

&lt;p&gt;再则，是数值比较大的点。&lt;/p&gt;

&lt;p&gt;最后，还有优化空间的点。&lt;/p&gt;

&lt;p&gt;然后详细了解现在这个需要优化的点具体都做了哪些事情。&lt;/p&gt;

&lt;h3 id=&quot;问题分析及调研&quot;&gt;问题分析及调研&lt;/h3&gt;

&lt;p&gt;这一阶段主要是梳理现状，去分析现状，既而去发现问题。&lt;/p&gt;

&lt;p&gt;然后，从这个问题着手去看看我们内外部有没有其他人针对类似问题的解决方案。&lt;/p&gt;

&lt;p&gt;看看其他人是怎么做的，是不是可以给到我们一些参考。&lt;/p&gt;

&lt;h3 id=&quot;方案设计及实施&quot;&gt;方案设计及实施&lt;/h3&gt;

&lt;p&gt;这个阶段就是跟进内外部方案，以及我们的实际情况，给出我们认可的一个详细方案设计并实施上线。&lt;/p&gt;

&lt;h3 id=&quot;解决并上线核实&quot;&gt;解决并上线核实&lt;/h3&gt;

&lt;p&gt;这个阶段就是把我们的方案实现并上线。最终还需要通过新旧对比来看看我们是不是达到了预期的效果。这个很重要。&lt;/p&gt;

&lt;p&gt;** 最后，如果一个优化方案的结果是没有可量化的、可以对照的指标，那么这个优化是没法让人信服的，是无效的。**&lt;/p&gt;

&lt;h2 id=&quot;第三步持续监控&quot;&gt;第三步，持续监控&lt;/h2&gt;

&lt;p&gt;上面的第二步是针对具体问题的具体解决。&lt;/p&gt;

&lt;p&gt;我们不能仅仅停留在只是去解决一个问题，我们还需要一种自动化的手段，帮助我们去持续监控我们优化后的效果。&lt;/p&gt;

&lt;p&gt;我们希望这个自动化的手段做到的效果是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;至少能够做到每次上线能跑一遍&lt;/li&gt;
  &lt;li&gt;更高一点就是每个需求单独部署的时候跑一遍&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;通过对比来确定当前上线或者当前的需求是否影响到了我们的核心指标。最后，通过报告的形式输出出来。&lt;/p&gt;

&lt;h2 id=&quot;第四步竞对分析&quot;&gt;第四步，竞对分析&lt;/h2&gt;

&lt;p&gt;只有和你的竞对去做比较，看看我们之间的优势在哪里，差距在哪里。&lt;/p&gt;

&lt;p&gt;才能更明确自己下一步的目标。&lt;/p&gt;

&lt;p&gt;这里面最重要的一个点就是指标的选择。&lt;/p&gt;

&lt;p&gt;视频类的，可以是：首帧耗时、起播耗时、滑动耗时、黑边率、CPU&amp;amp;MEM 、电量。&lt;/p&gt;

&lt;h1 id=&quot;全局的指标制定&quot;&gt;全局的指标制定&lt;/h1&gt;

&lt;p&gt;越是看得多，越是觉得欠缺了一些重要的东西。&lt;/p&gt;

&lt;p&gt;这里从一个特别的角度来看待性能优化。&lt;/p&gt;

&lt;p&gt;也是之前做过一段时间的 node.js 服务端的性能优化，最终优化来优化去，所有的优化指标最终都可以归结到四个点：CPU、内存、网络、IO。&lt;/p&gt;

&lt;p&gt;每次遇到性能瓶颈的时候最直观的表现，都能够在这个四个点体现出来。&lt;/p&gt;

&lt;p&gt;做 Web 性能优化的时候，其实还是很少涉及运行环境层面的监控。&lt;/p&gt;

&lt;p&gt;主要还是本身 Web 都是运行在浏览器上的，正常情况下还是很难拿到 CPU、内存、网络及 IO 数据的。&lt;/p&gt;

&lt;p&gt;但在 RN 这层本身有一半算是 Native，还有就是这些数据是可以通过 Native 去获取到的。&lt;/p&gt;

&lt;p&gt;如果我们把 RN 当着 Native 去看待的话，这些数据就显得很重要了。&lt;/p&gt;

&lt;h1 id=&quot;最后&quot;&gt;最后&lt;/h1&gt;
&lt;p&gt;有很多时候，我们看到的问题，不一定是因为我们自己问题，也可能是流程上的问题，也可能是整个技术栈的问题。&lt;/p&gt;

&lt;p&gt;但更多的时候我们需要跳出一些固定思维。&lt;/p&gt;

&lt;p&gt;当然，更多的时候，我们需要多看看其他人是怎么解决一个事情，这个比直接告诉你怎么去做更重要。&lt;/p&gt;
</description>
        <pubDate>Wed, 23 Mar 2022 00:00:00 +0000</pubDate>
        <link>https://yss.github.iohttps://yss.github.io/2022/03/23/the-direction-summary-of-RN-optimization.html</link>
        <guid isPermaLink="true">https://yss.github.iohttps://yss.github.io/2022/03/23/the-direction-summary-of-RN-optimization.html</guid>
        
        <category>rn</category>
        
        
        <category>rn</category>
        
      </item>
    
      <item>
        <title>埋点现状梳理及解决</title>
        <description>&lt;h1 id=&quot;埋点历史&quot;&gt;埋点历史&lt;/h1&gt;
&lt;p&gt;要了解现状就必须知道之前埋点的历史是怎样的。&lt;/p&gt;

&lt;p&gt;我们现在主要使用的埋点版本是 V2，追溯到最早的版本是 V0 。&lt;/p&gt;

&lt;h2 id=&quot;bi-tracking-v0&quot;&gt;BI Tracking V0&lt;/h2&gt;
&lt;p&gt;V0 版本是 2015 年开发的，现在在 Confluence 上已经找不到相关文档了。&lt;/p&gt;

&lt;p&gt;它是一个没有固定格式约定（schema-less）的，可以按照自己的需要发送任意的数据。&lt;/p&gt;

&lt;p&gt;对于使用者来说非常的灵活，但是对于最终的数据处理用户，也就是我们的 BI 团队来说，这个是巨大的痛苦。每来一个需求就意味需要重新去写程序处理，特别是需要花费大量的精力去预处理这些数据。&lt;/p&gt;

&lt;p&gt;这在业务没有大规模扩大的情况下，总还是能跑的通的，但是随着 Shopee 业务的极速发展，这样的方式不管是对于 BI 团队来说，还业务团队来说，都会有巨大的成本。&lt;/p&gt;

&lt;p&gt;所以，有了 V1 版本。&lt;/p&gt;

&lt;h2 id=&quot;bi-tracking-v1&quot;&gt;BI Tracking V1&lt;/h2&gt;
&lt;p&gt;首先，V1 版本相比 V0 的第一个差异就是使用了 protobuffer，这个可以极大压缩传输数据量大小。&lt;/p&gt;

&lt;p&gt;而使用了 protobuffer，就需要定义一个固定格式的 Schema，现有的定义可以参考：//&lt;/p&gt;

&lt;p&gt;然后，在实施过程中，所有的 TrackingEvent 都需要 BI 团队定义，然后传递给开发人员。&lt;/p&gt;

&lt;p&gt;在这么一个工作流程下，数据预处理就转移到了前端。这个时候前端的复杂性就迅速提升了。&lt;/p&gt;

&lt;p&gt;实现 V1 的过程中，最大的挑战就是在触发跟踪事件时，需要记录上下文。这里的上下文是有相关的页面参数、相关的用户交互等定义的。&lt;/p&gt;

&lt;p&gt;比如这么一个场景：我们正在收集用户在搜索结果页面中查看我们产品的曝光数据，然后，前端需要传递原始的用户在搜索栏中输入文本。&lt;/p&gt;

&lt;p&gt;这个场景下，原始输入文本是跟这个曝光数据相关的，但是它并不直接存在产品卡片曝光事件中。&lt;/p&gt;

&lt;p&gt;所以，为了支持这个场景下的数据，FE 开发人员需要付出很大的努力才能做到。&lt;/p&gt;

&lt;p&gt;像这种存在于应用程序上下文中固有的东西，为了数据收集我们不得不明确地记录它们。&lt;/p&gt;

&lt;p&gt;也就是因为这类的问题，所以才有了 V2 的诞生。&lt;/p&gt;

&lt;h2 id=&quot;bi-tracking-v2&quot;&gt;BI Tracking V2&lt;/h2&gt;
&lt;p&gt;那为什么 V2 能解决呢？&lt;/p&gt;

&lt;p&gt;V2 基于两个公理：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;任何单个 V1 埋点事件都可以基于用户的一个或多个交互时间线被重新创建。比如说，我要收集用户视频播放时长，我只需要两个点，一个开始播放的点，一个结束播放的点。&lt;/li&gt;
  &lt;li&gt;埋点数据都是一堆 UI/UX 相关数据的子集。任何的埋点数据都是用户交互下产生的，开发人员不需要去想哪些应该上报，只需要尽可能的把涉及到是用户交互数据上报即可。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;然后，会有加一个中间层（MiddleLayer），作为连接前端 V2 Log 和 BI Tracking V1 的桥梁。 以此来达到既能节省开发人员时间，而且也不需要 BI Team 做大的修改。&lt;/p&gt;

&lt;p&gt;理想情况，在这种模式下我们只需要上报一个个的点，然后在中间层去做各种数据的收集、整合和计算。&lt;/p&gt;

&lt;h1 id=&quot;现有-rn-项目-v2-版本的实现&quot;&gt;现有 RN 项目 V2 版本的实现&lt;/h1&gt;
&lt;p&gt;现有的 RN 项目同时存在 V1 和 V2 版本，但主体上是使用的 V2 版本，V1 版本的埋点会逐步废弃。&lt;/p&gt;

&lt;p&gt;然后，RN 底层埋点逻辑是直接调用 Native Modules 提供的 GAShopeeBITrackerV2 模块里的方法来发送的。&lt;/p&gt;

&lt;p&gt;主要有三个方法：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;trackActions：多条数据上报&lt;/li&gt;
  &lt;li&gt;trackPerformanceEvent：性能数据上报&lt;/li&gt;
  &lt;li&gt;trackActionsRealTime：实时数据上报&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;详细的 API 文档在这里：GAShopeeBITrackerV2&lt;/p&gt;

&lt;h2 id=&quot;实际情况&quot;&gt;实际情况&lt;/h2&gt;
&lt;p&gt;这里，虽然，我们使用了 V2 版本，但是我们实际只是用到了里面的一部分东西：&lt;/p&gt;

&lt;p&gt;新的埋点结构。
尽可能少的定义埋点。比如：不同页面可以有相同的埋点值，不需要每次都去定义。然后通过 pageName 做区分。&lt;/p&gt;
&lt;h2 id=&quot;rn-层做的事情&quot;&gt;RN 层做的事情&lt;/h2&gt;
&lt;p&gt;在 RN 层要做的事情：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在业务代码中收集数据&lt;/li&gt;
  &lt;li&gt;通过 trackering schema 校验数据&lt;/li&gt;
  &lt;li&gt;通过 processorResolver 组装数据&lt;/li&gt;
  &lt;li&gt;最后调用 Native Modules 提供的方法发送。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;整个流程中，最复杂的就是数据收集层面，也就是第一步做的事情。&lt;/p&gt;

&lt;p&gt;我们现在几乎所有的数据收集处理代码都是跟着业务一起写的。最初业务不大，逻辑不复杂的时候，其实也是没有什么问题的。&lt;/p&gt;

&lt;p&gt;但随着业务需求和埋点需求一步步变多，对应的我们也遇到了很大的问题和挑战。&lt;/p&gt;

&lt;h1 id=&quot;问题&quot;&gt;问题&lt;/h1&gt;
&lt;h2 id=&quot;代码层面&quot;&gt;代码层面&lt;/h2&gt;
&lt;p&gt;最近也是做了几个埋点相关的需求，从代码层面能直观看到的一些问题：&lt;/p&gt;

&lt;h3 id=&quot;一埋点代码和业务代码藕合紧密&quot;&gt;一、埋点代码和业务代码藕合紧密&lt;/h3&gt;
&lt;p&gt;我们的业务代码已经非常庞大了，对应的业务需求也没有减弱的迹象，而对应的埋点代码和业务代码藕合得也是十分的紧密，越是核心的地方埋点就越多。&lt;/p&gt;

&lt;p&gt;很多代码可以说都是在为埋点而做，既而导致整体的代码看着就非常庞大繁杂，非常难以维护。&lt;/p&gt;

&lt;p&gt;举一个非常典型的例子：videoPlayer 的 HOCs，纯粹为埋点服务的就是 5 个 HOC，这还没有包括还有 2 个 HOC 中含有埋点代码。&lt;/p&gt;

&lt;p&gt;可以说 videoPlayer 的绝大部分代码都是埋点。&lt;/p&gt;

&lt;p&gt;当然，这个时候也是最需要我们能停下来好好想想如何去做到一个更好维护的代码。&lt;/p&gt;

&lt;h3 id=&quot;二埋点中存在很多的数据计算和-context-数据&quot;&gt;二、埋点中存在很多的数据计算和 Context 数据&lt;/h3&gt;
&lt;p&gt;我们代码中有很多的埋点，不仅仅只是打一个点那么简单，还涉及到很多数据计算和逻辑处理也放到了前端代码里。&lt;/p&gt;

&lt;p&gt;而且，还需要很多非直接但相关联的一些数据需要合并上报。&lt;/p&gt;

&lt;p&gt;这也是一个从侧面能看出其中埋点的复杂性所在。&lt;/p&gt;

&lt;h3 id=&quot;三相似埋点&quot;&gt;三、相似埋点&lt;/h3&gt;
&lt;p&gt;同一个地方因为诉求上的一些差异，上报了相似的埋点数据。比如：video_like 和 video_like_cancel（这个后面看主要是方便产品查数据）&lt;/p&gt;

&lt;h3 id=&quot;四老埋点&quot;&gt;四、老埋点&lt;/h3&gt;
&lt;p&gt;有些埋点，可能只是试验性的，某一个阶段在用，后面就不再用了。&lt;/p&gt;

&lt;p&gt;但这些埋点依旧会一直存在代码里，并不会有清退一说。&lt;/p&gt;

&lt;h2 id=&quot;线上事故&quot;&gt;线上事故&lt;/h2&gt;
&lt;p&gt;然后，最近一段时间，我们也是发生了几个比较大的线上埋点问题。&lt;/p&gt;

&lt;p&gt;目前看到的并有记录的有：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;211216 版本导致的埋点事故分析&lt;/li&gt;
  &lt;li&gt;220308版本videoPlayTime埋点时长突变的问题&lt;/li&gt;
  &lt;li&gt;220312 - 分辨率突变问题分析&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当然，我们可以说有导致的原因很多，有代码规范，有 CR，有本身业务代码不熟悉。&lt;/p&gt;

&lt;p&gt;但更重要的还是业务逻辑本身已经够复杂了，但依旧还要在业务逻辑里追加很多的埋点代码，既而导致整个系统复杂性进一步提高。&lt;/p&gt;

&lt;p&gt;对于开发人员来说，每一次的开发和改动都是步步惊心。&lt;/p&gt;

&lt;p&gt;对于测试人员来说，每一次的测试都可能不够完善。&lt;/p&gt;

&lt;p&gt;因为你不知道你的改动会不会影响到其他地方。&lt;/p&gt;

&lt;h1 id=&quot;改造&quot;&gt;改造&lt;/h1&gt;
&lt;h2 id=&quot;目标&quot;&gt;目标&lt;/h2&gt;
&lt;p&gt;把业务逻辑和埋点逻辑分离。&lt;/p&gt;

&lt;h2 id=&quot;定理&quot;&gt;定理&lt;/h2&gt;
&lt;p&gt;在谈下面方案之前，必须定义一个定理，也是前提，就是：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;所有相互关联的事件的触发都是有固定时序的。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;如果相关联的事件本身时序都不能保证，那么你本身代码逻辑就是有问题。&lt;/p&gt;

&lt;p&gt;比如：一个页面的 pageView 事情是优先页面上的其他事件的，一个组件的 mounted 事件一定是早于 unmouted 事件的。&lt;/p&gt;

&lt;h2 id=&quot;埋点类型划分&quot;&gt;埋点类型划分&lt;/h2&gt;
&lt;p&gt;从广义上来说，埋点分两种类型，一种是瞬时埋点，一种是持续埋点。&lt;/p&gt;

&lt;h3 id=&quot;瞬时埋点&quot;&gt;瞬时埋点&lt;/h3&gt;
&lt;p&gt;就仅仅记录一个动作/交互，比如：page_view、xx_click。&lt;/p&gt;

&lt;p&gt;它本身并没有特别的逻辑，主要是本身需要携带很多的 Context Data（埋点额外附带的数据）。&lt;/p&gt;

&lt;p&gt;除非有一些特别的 Context Data 不能够实时获取，否则一般都是即时发送。&lt;/p&gt;

&lt;h3 id=&quot;持续埋点&quot;&gt;持续埋点&lt;/h3&gt;
&lt;p&gt;持续埋点一般是一段时间周期内的统计，是由一个或多个动作/交互组合而成。比如：video_play_time。&lt;/p&gt;

&lt;p&gt;它的结果会依赖于很多的动作、事件、交互，并通过计算得来。&lt;/p&gt;

&lt;p&gt;而且很多时候本身依赖的 Context Data 也是不能够实时获取到的。&lt;/p&gt;

&lt;p&gt;既而导致需要在很多地方处理。&lt;/p&gt;

&lt;p&gt;正常情况下，瞬时埋点几乎不会出问题。出问题的，也是复杂度最高的就是持续埋点了。&lt;/p&gt;

&lt;p&gt;而我们方案的重点就是要解决在持续埋点中遇到的种种问题。&lt;/p&gt;

&lt;h2 id=&quot;详细方案设计&quot;&gt;详细方案设计&lt;/h2&gt;
&lt;p&gt;我们要知道，要彻底把业务逻辑和埋点逻辑分开，那必须需要用到事件模型，我们这里采用了订阅发布模式。&lt;/p&gt;

&lt;h3 id=&quot;架构图&quot;&gt;架构图&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/event-tracking.png&quot; alt=&quot;event-tracking&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;整体设计思路&quot;&gt;整体设计思路&lt;/h3&gt;
&lt;p&gt;整个的设计思路总体是和 BI Tracking V2 的思想非常契合的，就是做一个中间层来做处理和计算。&lt;/p&gt;

&lt;p&gt;我们把所有可能的计算逻辑抽离出来，放到一个个埋点 plugin 中去做计算。&lt;/p&gt;

&lt;p&gt;这也是为什么我们要定义一个定理的原因，因为 plugin 是强依赖相关联事件的时序的。&lt;/p&gt;

&lt;h3 id=&quot;events-模块&quot;&gt;Events 模块&lt;/h3&gt;
&lt;p&gt;这样一来就有了 Events 模块。&lt;/p&gt;

&lt;p&gt;我们把页面的切换，RN 的回调，各种动作和事件都做一个统一的收集、管理、分发。&lt;/p&gt;

&lt;p&gt;同时，我们还需要保证一个事件的订阅者出现的异常不能影响到其他订阅着，所以我们需要一个异常处理模块来保证。&lt;/p&gt;

&lt;h3 id=&quot;plugins-模块&quot;&gt;Plugins 模块&lt;/h3&gt;
&lt;p&gt;有一些埋点本身有很多逻辑是相通的，但是如果都放到一块的话，早期可能还好，但后期一旦逻辑发生变化就意味着各种逻辑判断和处理。&lt;/p&gt;

&lt;p&gt;所以，为了后期的维护，以及清晰读懂一个埋点，我们把所有的埋点都当着一个个 Plugin 。&lt;/p&gt;

&lt;p&gt;核心：一个埋点对应一个 plugin。&lt;/p&gt;

&lt;p&gt;它本身包含了自身埋点所需要的各种逻辑处理。而这些逻辑处理所需要那些变动、回调、事件都可以通过监听 Events 来做对应的逻辑处理。&lt;/p&gt;

&lt;p&gt;确实有通用数据处理逻辑，可以放到 Shared Data 模块或者单独抽离一个通用的 Util 去做。&lt;/p&gt;

&lt;h3 id=&quot;shared-data&quot;&gt;Shared Data&lt;/h3&gt;
&lt;p&gt;Shared Data 的存在本身是因为我们很多个埋点本身依赖了很多相同的 Context Data。&lt;/p&gt;

&lt;p&gt;如果每个埋点都自己做的话，就意味着大量重复的处理。比如常规的，我们需要保留上一页下一页数据，以及我们之后要做的 fromSource 参数要记录长达 5 级的结构，还有很多类似的参数。&lt;/p&gt;

&lt;p&gt;等等这些都是需要有一个统一的地方去管理和维护。&lt;/p&gt;

&lt;p&gt;所以，才有了这么一个公共的共享数据源，来管理和维护这些需要共享的单个数据点。&lt;/p&gt;

&lt;h3 id=&quot;processors&quot;&gt;Processors&lt;/h3&gt;
&lt;p&gt;这里面 Processors 是现有的 Tracker 的实现，磊哥和林锐在这里已经完善得非常不错了，整体暂时保持不变。&lt;/p&gt;

&lt;h2 id=&quot;额外收获&quot;&gt;额外收获&lt;/h2&gt;
&lt;h3 id=&quot;测试&quot;&gt;测试&lt;/h3&gt;
&lt;p&gt;业务代码和埋点代码分离后，我们可以看到整个埋点的处理都统一到了一个大的模块里。&lt;/p&gt;

&lt;p&gt;外层只需要通过事件驱动的方式就可以让埋点代码跑起来。&lt;/p&gt;

&lt;p&gt;而这个时候，整个的埋点测试其实是完全可以做到完全独立的、并且是全自动化的。&lt;/p&gt;

&lt;h3 id=&quot;即插即用&quot;&gt;即插即用&lt;/h3&gt;
&lt;p&gt;因为采用了插件模式来编写，所以可以非常方便的对某个埋点进行修改、删除，而不影响到其他埋点。&lt;/p&gt;

&lt;p&gt;当然，除此之外，还可以做整体埋点技术层面的性能分析，数据优化和统计。&lt;/p&gt;

&lt;h1 id=&quot;最后&quot;&gt;最后&lt;/h1&gt;
&lt;p&gt;上面所说的埋点问题的解决，都仅仅只是聚焦在了现有的代码层面。&lt;/p&gt;

&lt;p&gt;在我看来，真实能解决的东西其实不多，也就是降低了一些复杂性、更适合测试。&lt;/p&gt;

&lt;p&gt;要想真正去解决，还需要更全方面的去考虑。比如把前端的计算逻辑移除放到 DA 中做，比如无埋点模式。&lt;/p&gt;
</description>
        <pubDate>Tue, 08 Mar 2022 00:00:00 +0000</pubDate>
        <link>https://yss.github.iohttps://yss.github.io/2022/03/08/summarized-the-current-logics-of-the-tracker-and-solving-the-tracker-problems-in-our-project.html</link>
        <guid isPermaLink="true">https://yss.github.iohttps://yss.github.io/2022/03/08/summarized-the-current-logics-of-the-tracker-and-solving-the-tracker-problems-in-our-project.html</guid>
        
        <category>project</category>
        
        
        <category>project</category>
        
      </item>
    
      <item>
        <title>RFC 流程在团队技术文档中的应用</title>
        <description>&lt;h1 id=&quot;rfc-是什么&quot;&gt;RFC 是什么&lt;/h1&gt;
&lt;p&gt;作为一名技术人员，看到 RFC 的第一印象就是它是一个庞大的文档库，里面包含了计算机领域方方面面的规范。&lt;/p&gt;

&lt;p&gt;RFC 的全称是 Request For Comments，即请求意见稿。初衷是为了方便大家一起讨论和交流，后来慢慢变成了互联网协议草案及标准。&lt;/p&gt;

&lt;h1 id=&quot;rfc-的发布过程&quot;&gt;RFC 的发布过程&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/rfc-1.png&quot; alt=&quot;rfc-1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通常 RFC 的发布过程是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;当某家机构或团体开发出了一套标准或提出对某种标准的设想，想要征询外界的意见时，就会在Internet上发放一份 RFC；&lt;/li&gt;
  &lt;li&gt;然后，对这一问题感兴趣的人可以阅读该 RFC 并提出自己的意见；&lt;/li&gt;
  &lt;li&gt;之后，经过大量的论证和修改过程；&lt;/li&gt;
  &lt;li&gt;最后，由主要的标准化组织进行发布。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;IETF互联网工程任务小组，英文全称 Internet Engineering Task Force，就是这样的一个标准化组织，它负责互联网标准的开发和推动。&lt;/p&gt;

&lt;p&gt;但是，我们今天说的 RFC 流程并不是 RFC 本身发布流程，而是借鉴了它的文档发布流程思想。&lt;/p&gt;

&lt;h1 id=&quot;开源项目中-rfc-流程应用&quot;&gt;开源项目中 RFC 流程应用&lt;/h1&gt;

&lt;p&gt;现在已经有非常多的开源项目在采用了 RFC 流程来帮助和指导项目的设计，比如：React RFC 流程（https://github.com/reactjs/rfcs/blob/master/README.md）、Vue RFC 流程（https://github.com/vuejs/rfcs/blob/master/README.md）、Rust RFC 流程（https://github.com/rust-lang/rfcs/blob/master/README.md）等。&lt;/p&gt;

&lt;p&gt;这里我们拿 Vue RFC 流程来着重说明。&lt;/p&gt;

&lt;h1 id=&quot;vue-rfc-流程&quot;&gt;Vue RFC 流程&lt;/h1&gt;

&lt;p&gt;最初接触到 RFC 流程是通过尤大发布的 Vue 3.0 计划里提到的通过 RFC 征集公众反馈。&lt;/p&gt;

&lt;p&gt;里面说到对 RFC 流程的说明：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;what is an RFC

The &quot;RFC&quot; (request for comments) process is intended to provide a consistent and controlled path for new features to enter the framework.

Many changes, including bug fixes and documentation improvements can be implemented and reviewed via the normal GitHub pull request workflow.

Some changes though are &quot;substantial&quot;, and we ask that these be put through a bit of a design process and produce a consensus among the Vue core team and the community.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;翻译过来就是：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;RFC 是什么

&quot;RFC&quot;(request for comments) 流程的目的是为了提供一种让新功能进入框架的可持续且可控的路径。

很多提交，包括 bug 修复和文档改进都可以通过在日常的 GitHub Pull Request 工作流中得以实施和评审。

但是对于一些重大的提交，我们期望这些提交能够经历一个大的设计流程，并且在 Vue 核心团队及社区中达成共识。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于像我们这样 Vue 的主要使用者来说，之前很多的新功能，新特性，我们只有在官方文档发布之后，才能得知。&lt;/p&gt;

&lt;p&gt;现在有了 RFC 流程后，我们不仅可以事先就能知道这个事情，还可以了解作者的设计理念、设计思路，既而更好的理解作者引入新特性、废弃旧特性的原因。&lt;/p&gt;

&lt;p&gt;甚至我们还可以在上面提出自己的问题，帮助 Vue 向更好的方向发展。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;每个人都不再是旁观者，而是决策的参与者。&lt;/strong&gt;&lt;/p&gt;

&lt;h1 id=&quot;vue-rfc-流程生命周期&quot;&gt;Vue RFC 流程生命周期&lt;/h1&gt;
&lt;p&gt;当然，Vue RFC 本身的灵感是来源于 React RFC Process 、Rust RFC Process 和 Ember RFC Process。&lt;/p&gt;

&lt;p&gt;并且它明确了一个生命周期的概念：Pending、Active、Landed、Rejected。&lt;/p&gt;

&lt;p&gt;一个 Vue 的 RFC 会经历以下几个阶段：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Pending：这个 RFC 被作为一个 PR(pull request) 被提交。&lt;/li&gt;
  &lt;li&gt;Active：这个 RFC 被合并并且正在实施。&lt;/li&gt;
  &lt;li&gt;Landed：这个 RFC 提出的更改在实际版本中被发布。&lt;/li&gt;
  &lt;li&gt;Rejected：这个 RFC 的 PR 被否决。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;整个流程图就可以表示成这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/rfc-2.png&quot; alt=&quot;rfc-2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这样一来，整个 RFC 流程就清晰明了。&lt;/p&gt;

&lt;h1 id=&quot;团队技术文档下的-rfc-流程&quot;&gt;团队技术文档下的 RFC 流程&lt;/h1&gt;

&lt;p&gt;同样的，换到我们正常的文档里，我们拿到一个文档是不是也会有各种疑问：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;这个文档当前是怎么一个状态，是大家已经一致认同通过了，还是已经废弃了。&lt;/li&gt;
  &lt;li&gt;这个文档之前都有谁 Review 过。&lt;/li&gt;
  &lt;li&gt;这个文档的结论到底是什么。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在不确定的情况下，我们就会去找之前写这份文档的人，问问他，当前文档是什么一个状况。更深一点会问当时为什么要这么去做。&lt;/p&gt;

&lt;p&gt;那我们有没有一种机制，能够更好的解决这类问题呢？&lt;/p&gt;

&lt;p&gt;那就是在我们的文档里引入 RFC 流程。&lt;/p&gt;

&lt;h2 id=&quot;概述&quot;&gt;概述&lt;/h2&gt;
&lt;p&gt;对于需要讨论的重要内容，我们应当创建一份 RFC 文档，描述问题的背景和解决方案并征求大家的意见。对于 RFC 文档的可以通过 comment 或者会议的方式来充分讨论后再确定最终方案。&lt;/p&gt;

&lt;h2 id=&quot;rfc-的状态&quot;&gt;RFC 的状态&lt;/h2&gt;
&lt;p&gt;RFC 的流转状态是生命周期的基础，按照历史经验，我们可以状态来定义 RFC 的生命周期。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;WIP: 当一份 RFC 处于构思阶段，内容尚不完整时，可以标记状态为 WIP。标记为 WIP 状态的 RFC，不会邀请大家进行 Review。&lt;/li&gt;
  &lt;li&gt;PENDING: PENDING 状态的 RFC 表示这个文档需要多个人的评论，内容需要经过评审后才可以进入执行阶段。&lt;/li&gt;
  &lt;li&gt;COMMITTED: 已提交的 RFC 意味着进入了执行阶段。如果是这个技术方案设计的 RFC，它会指导后续的技术开发工作。如果是团队流程的 RFC，那么需要团队里的同学遵守相关的流程。&lt;/li&gt;
  &lt;li&gt;DEPRECATED：这个 RFC 不再有效。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/rfc-3.png&quot; alt=&quot;rfc-3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这其中 WIP 状态已经有很多团队在用了，本意是 work in process。&lt;/p&gt;

&lt;h2 id=&quot;rfc-的顺序&quot;&gt;RFC 的顺序&lt;/h2&gt;
&lt;p&gt;除了状态还需要标识各个 RFC 文档的创建顺序，我们可以在标题上加上，比如：RFC [WIP-0001] xxxx&lt;/p&gt;

&lt;h2 id=&quot;rfc-的-reviewer&quot;&gt;RFC 的 Reviewer&lt;/h2&gt;
&lt;p&gt;我们的 RFC 更重要的事是指明需要哪些人来 review 我们写的。&lt;/p&gt;

&lt;p&gt;所以，需要一栏表述这个 RFC 都需要哪些人来 Review，并且还需要加上一些说明，比如：打勾就代表已经认同当前方案并充分表达。名单人员可能不齐，可自行添加。&lt;/p&gt;

&lt;p&gt;因为你的 RFC 可以给任意感兴趣的人 Review 的。&lt;/p&gt;

&lt;h2 id=&quot;其他需求&quot;&gt;其他需求&lt;/h2&gt;
&lt;p&gt;同时，一个技术团队，除了正常的需求和技术文档，也需要更多的其他想法和建议。&lt;/p&gt;

&lt;p&gt;特别像一些流程、团队协作之类的。都可以放到 RFC 中去讨论。&lt;/p&gt;

&lt;p&gt;让每个人都有一个渠道去表达自己的一些看法和想法，或者是针对某个具体问题的疑问及解决。&lt;/p&gt;

&lt;h2 id=&quot;最后一点&quot;&gt;最后一点&lt;/h2&gt;
&lt;p&gt;讨论即文档。&lt;/p&gt;

&lt;p&gt;一个 RFC 文档，不仅仅只是文档，更多的还有讨论。讨论的过程，就是大家相关沟通经验和想法的过程，所谓千人千面，不同的人对不同事情有不一样的看法和想法，本身也是一个相互学习的过程。&lt;/p&gt;

&lt;p&gt;而且对于后来的人来说，在熟悉这个项目的时候可以清楚的知道之前都发生了什么。这个项目都经过了哪些讨论。&lt;/p&gt;

&lt;p&gt;至此，这些 RFC 也是非常有价值的学习资料。&lt;/p&gt;
</description>
        <pubDate>Wed, 19 Jan 2022 00:00:00 +0000</pubDate>
        <link>https://yss.github.iohttps://yss.github.io/2022/01/19/use-RFC-process-in-our-team-technical-document.html</link>
        <guid isPermaLink="true">https://yss.github.iohttps://yss.github.io/2022/01/19/use-RFC-process-in-our-team-technical-document.html</guid>
        
        <category>rfc</category>
        
        
        <category>application</category>
        
      </item>
    
      <item>
        <title>Label 项目下的 Popup 改造</title>
        <description>&lt;h1 id=&quot;前言&quot;&gt;前言&lt;/h1&gt;
&lt;p&gt;我们的 B端项目都是放在 admin-fe 仓库里。&lt;/p&gt;

&lt;p&gt;Label 变更的需求是我接手到的第一个需求，所以在正式做需求之前，很有必要先完整的去了解整个项目的运作，以及理解 Label 这块业务的由来。&lt;/p&gt;

&lt;h1 id=&quot;admin-fe-项目&quot;&gt;Admin-FE 项目&lt;/h1&gt;
&lt;p&gt;整体先过了一遍项目，看看它是怎么本地启动的，入口是怎么去做的，路由层面做了什么样的处理。&lt;/p&gt;

&lt;p&gt;通通过来一遍之后就画了一个简单的 admin-fe 的系统架构图。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/label-1.png&quot; alt=&quot;admin-fe-constructor&quot; /&gt;&lt;/p&gt;

&lt;p&gt;整体的理解就是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Layout 是最外层的入口&lt;/li&gt;
  &lt;li&gt;页面上做了大的路由曾经划分，每个大的路由都是异步加载的，通过第一级的 path 做了区分&lt;/li&gt;
  &lt;li&gt;每个路由是由一个或多个页面聚合而成的，同一个大路由下的页面基本共用相同的 Model 、Component 和 对应的 API 文件&lt;/li&gt;
  &lt;li&gt;之后就是一些公共的模块和库，包括 Hooks、Store、Utils&lt;/li&gt;
  &lt;li&gt;最底层就是 React + AntD ，我们所有的内容都是基于 AntD 组件去构建的&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;熟悉了项目才能更好的去做好开发，写好代码。&lt;/p&gt;

&lt;p&gt;但仅仅写代码，好说，但不理解业务，就白忙活了。&lt;/p&gt;

&lt;p&gt;所以，我们需要看一下我们做的这个 Label 业务具体是怎么回事。&lt;/p&gt;

&lt;h1 id=&quot;label-的理解&quot;&gt;Label 的理解&lt;/h1&gt;
&lt;p&gt;Label 我们知道它就是标签的意思，在英文里还有一个单词叫 Tag。&lt;/p&gt;

&lt;p&gt;通常来说 Tag 会更细一些。&lt;/p&gt;

&lt;p&gt;然后，在我们当前 Video 里的场景来说，Label 具体分为这么几类：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Object Type&lt;/li&gt;
  &lt;li&gt;Label Type&lt;/li&gt;
  &lt;li&gt;L1&lt;/li&gt;
  &lt;li&gt;L2&lt;/li&gt;
  &lt;li&gt;Attribute&lt;/li&gt;
  &lt;li&gt;Value&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这个版本我们叫它 Label 2.0&lt;/p&gt;

&lt;p&gt;那肯定还有之前的一个版本，对，那就是 Label 1.0&lt;/p&gt;

&lt;h2 id=&quot;label-10&quot;&gt;Label 1.0&lt;/h2&gt;
&lt;p&gt;label 1.0 是一个极简单的分类，就是只有两级：L1 、L2&lt;/p&gt;

&lt;p&gt;L1 &amp;gt; L2&lt;/p&gt;

&lt;p&gt;这里没有什么特别可以说的。那我们看看 Label 2.0&lt;/p&gt;

&lt;h2 id=&quot;label-20&quot;&gt;Label 2.0&lt;/h2&gt;
&lt;p&gt;做 Label 2.0 的目标是要建立一个可扩展的 Label 标签数据体系。能更好的服务于后续的业务发展需要。&lt;/p&gt;

&lt;p&gt;它的做法，这张图给出了诠释：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/label-2.png&quot; alt=&quot;label-2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那怎么理解这些名词呢？&lt;/p&gt;

&lt;p&gt;首先，还是保留了分级分类，Object Type 、Label Type 、L1 、L2 就是各个层级的分类&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Object Type：可以理解为大的业务线分类&lt;/li&gt;
  &lt;li&gt;Label Type：基于这个业务下的产品分类，比如：Content Label&lt;/li&gt;
  &lt;li&gt;L1：能够明确指代某一类事物的分类，比如：Food&lt;/li&gt;
  &lt;li&gt;L2：具体描述某一种类型的事物分类，比如：Cooking Class&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当然，我们也没有必要刻意去抠字眼。&lt;/p&gt;

&lt;p&gt;而 Attribute: Value，更多指的是具体标签，比如：手机 Attribute brand，对应的取值有：huawei、apple、xiaomi  etc.&lt;/p&gt;

&lt;h2 id=&quot;总的理解&quot;&gt;总的理解&lt;/h2&gt;
&lt;p&gt;Label 2.0 是一个集分类和标签为一体的标签数据架构体系。&lt;/p&gt;

&lt;p&gt;当我们理解到这一层的时候，其实就已经对整个的 Video Label 体系有了一个整体认识。&lt;/p&gt;

&lt;p&gt;那接下来要做的事情就是按照 PRD 的要求去做对应的开发。&lt;/p&gt;

&lt;h1 id=&quot;开发&quot;&gt;开发&lt;/h1&gt;
&lt;h2 id=&quot;之前的做法&quot;&gt;之前的做法&lt;/h2&gt;
&lt;p&gt;之前的做法每个页面都有自己完全独立的 popForm 去承接。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/label-3.png&quot; alt=&quot;label-3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这就导致了每一个类型都需要一个单独的 popForm，但每个 popForm 里面的内容大致雷同，里面做的事情都一样的。&lt;/p&gt;

&lt;p&gt;这样一来，代码就产生了很多的冗余。很多逻辑都没有复用。&lt;/p&gt;

&lt;p&gt;对应的，如果产品要改一块逻辑，意味着所有的地方都要去改。&lt;/p&gt;

&lt;p&gt;而我这次做的需求就最明显不过了。&lt;/p&gt;

&lt;h2 id=&quot;这次的需求&quot;&gt;这次的需求&lt;/h2&gt;
&lt;p&gt;这次的需求就是要在 label 所有分类和标签数据层面增加，地区属性，对应的交互上，就是需要把 popForm 这部分从最初的只需要一步就能完成，变成了必须两步才能完成。&lt;/p&gt;

&lt;p&gt;继而，不管是交互还是复杂程度都较之前增加了不少。&lt;/p&gt;

&lt;h2 id=&quot;现实的问题&quot;&gt;现实的问题&lt;/h2&gt;
&lt;p&gt;如果继续按照之前的写法，就意味着我需要一个一个的去修改 popForm。&lt;/p&gt;

&lt;p&gt;比较好的做法，应该是先完成一个 popForm ，然后再去把对应的修改逻辑合并到新的 popForm 上。&lt;/p&gt;

&lt;p&gt;这种做法看似也能完成这次任务，但是&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;可维护性差，一个逻辑有问题，所有的 popForm 都需要去改&lt;/li&gt;
  &lt;li&gt;代码冗余，相同的处理代理，存在多个文件里&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;我的改造&quot;&gt;我的改造&lt;/h2&gt;
&lt;p&gt;最简单的做法，可能是说依旧沿用现在的方案，然后抽离出一些公共的逻辑及模块。&lt;/p&gt;

&lt;p&gt;这样一来，整体看着，是有了改进，有了进步。&lt;/p&gt;

&lt;p&gt;但我们是不是还可以有更好的方式呢？&lt;/p&gt;

&lt;p&gt;下面就是我给出的一个方案，做一次彻底的拆分和解耦：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/label-3.png&quot; alt=&quot;label-3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;整体思路就两个：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;整体抽象，拆分成各个独立的个体，个体尽可能的完全独立。&lt;/li&gt;
  &lt;li&gt;抽离各个模块里，可以通用逻辑，通过通用模块或者 hooks 进行聚合。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;具体到上面的架构：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;FormItem 是最小粒度的组件，它是一个完全独立的组件，只关注于自身的一个实现逻辑，以及对应的校验规则。&lt;/li&gt;
  &lt;li&gt;Popup 是把各个 FormItem 进行组合使用的，每个 Popup 可能会用到其中的一个或多个 FormItem ，它不关心里面的逻辑，只是每次点击完成的时候触发 Form 逻辑的检查，最后收集到的 Form 数据做一些必要的转换后传递给 PopupFrom 去使用。&lt;/li&gt;
  &lt;li&gt;PopupForm 是由两个 Popup 组成，它负责调度不同的 Popup 展示，并且保留各个 Popup 传递给它的数据，最终把所有 Popup 数据混合起来传递给业务。&lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Fri, 07 Jan 2022 00:00:00 +0000</pubDate>
        <link>https://yss.github.iohttps://yss.github.io/2022/01/07/the-Popup-refactor-in-label-problem.html</link>
        <guid isPermaLink="true">https://yss.github.iohttps://yss.github.io/2022/01/07/the-Popup-refactor-in-label-problem.html</guid>
        
        <category>refactor</category>
        
        
        <category>project</category>
        
      </item>
    
      <item>
        <title>入职之 Entry Task 有感</title>
        <description>&lt;p&gt;一不小心来到了新公司，既是兴奋，也是紧张。&lt;/p&gt;

&lt;p&gt;兴奋的是，终于可以好好的静下来重新出发。&lt;/p&gt;

&lt;p&gt;紧张的是，新的环境新的同事新的氛围都需要重新适应。&lt;/p&gt;

&lt;h1 id=&quot;新人的第一课-entry-task&quot;&gt;新人的第一课 Entry Task&lt;/h1&gt;
&lt;p&gt;入职后接触到的第一件事情，就是要做 Entry Task。&lt;/p&gt;

&lt;p&gt;第一次接触 Entry Task 的概念，很是新奇：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一个是竟然可以让新人完全脱产式的去做这么一个东西。&lt;/li&gt;
  &lt;li&gt;第二个是给了新人完全独立自主的环境，让他自己去想象去发挥。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这个做法还是挺新怡的。&lt;/p&gt;

&lt;p&gt;怎么让新人入门，怎么让新人快速融入，我在之前的工作也思考过。&lt;/p&gt;

&lt;p&gt;之前的主体想的是从熟悉开发模式、熟悉业务开始。&lt;/p&gt;

&lt;p&gt;做法就是先找一些简单的需求入手，然后逐个逐个加深到新人能完成融入，能独立的开发或者配合其他成员一起工作。&lt;/p&gt;

&lt;h1 id=&quot;老问题&quot;&gt;老问题&lt;/h1&gt;
&lt;p&gt;但这个过程，有的长，有的慢。&lt;/p&gt;

&lt;p&gt;而且更迫切的一个问题是，很多时候是根本找不到这么一条顺滑的线，让新人可以循序渐进的融入。&lt;/p&gt;

&lt;p&gt;更多的时候，是直接上手大的需求，或者跟着其他人一起做一个需求。&lt;/p&gt;

&lt;h1 id=&quot;新问题&quot;&gt;新问题&lt;/h1&gt;
&lt;p&gt;做完 Entry Task 是不是就能达到我们预期的效果呢？&lt;/p&gt;

&lt;p&gt;整体看 Entry Task 的侧重点更多的在于技术，而具体业务的落实点偏少。&lt;/p&gt;

&lt;p&gt;对于这边需要的某些技术点，但之前没有掌握的，需要在这里重新学习的，那么 Entry Task 会是一个绝佳的缓冲。&lt;/p&gt;

&lt;p&gt;但对于需要的技术点都掌握了的话，那还关注的是哪些点呢？&lt;/p&gt;

&lt;p&gt;我看到的是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;规范：包括代码、编码、开发、Git提交&lt;/li&gt;
  &lt;li&gt;真实的编码水平&lt;/li&gt;
  &lt;li&gt;还有么？&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;关于业务&quot;&gt;关于业务&lt;/h1&gt;
&lt;p&gt;最初的想法是如果 Entry Task 能涉及到业务层面的东西的话，那将会是非常棒的一个事情。&lt;/p&gt;

&lt;p&gt;但现实是，你需要提供一个和当前开发环境相同的环境，单就这一点来说，就很是一个巨大的工程。而且本身我们的开发环境一直都是在不断变化的，其维护成本可想而知。&lt;/p&gt;

&lt;h1 id=&quot;如何设计一个好的-entry-task&quot;&gt;如何设计一个好的 Entry Task&lt;/h1&gt;
&lt;p&gt;首先要肯定的是 Entry Task 是一个非常好的做法。&lt;/p&gt;

&lt;p&gt;然后还是以满足岗位技能为第一出发点。&lt;/p&gt;

&lt;p&gt;但要设计出一个好的 Entry Task 是非常难的，因为每个新人他所掌握的技术能力，他的背景知识，他本身的其他各方面能力都是有很大的差异的。&lt;/p&gt;

&lt;p&gt;但是不管怎样，最重要的一个点就是在要求的技能点里补不足。&lt;/p&gt;

&lt;p&gt;可以多制定几种类型的 Entry Task ，然后让新人根据自己的情况，对比当前岗位需要的技能，有针对性的去做 Entry Task。&lt;/p&gt;

&lt;p&gt;所以，需要做的就是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;梳理我们当前岗位需要的技能点及需要达到的水准。&lt;/li&gt;
  &lt;li&gt;收集大家的意见，整理几套可行的 Entry Task，最后再从新人的身上不断的去收集他们对 Entry Task 的反馈。&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;最后&quot;&gt;最后&lt;/h1&gt;
&lt;p&gt;这个是我个人作为新人对 Entry Task 的理解。&lt;/p&gt;
</description>
        <pubDate>Mon, 20 Dec 2021 00:00:00 +0000</pubDate>
        <link>https://yss.github.iohttps://yss.github.io/2021/12/20/what-I-think-about-Entry-Task.html</link>
        <guid isPermaLink="true">https://yss.github.iohttps://yss.github.io/2021/12/20/what-I-think-about-Entry-Task.html</guid>
        
        <category>职场</category>
        
        
        <category>other</category>
        
      </item>
    
  </channel>
</rss>
