<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[陈柏林的Blog RSS Feed]]></title><description><![CDATA[陈柏林的Blog RSS Feed]]></description><link>http://github.com/dylang/node-rss</link><generator>GatsbyJS</generator><lastBuildDate>Wed, 08 Apr 2026 19:44:30 GMT</lastBuildDate><item><title><![CDATA[维修美嘉欣 H12P Ford Bronco 遥控车避震漏油]]></title><description><![CDATA[美嘉欣 H12P 原厂的避震器密封胶圈尺寸不合适，导致漏油。我自己用胶皮裁剪了一个换上，再也不漏油。]]></description><link>https://www.berlinchan.com/2026/01/fix-mjx-h12p-ford-bronco-rc-oil-leakage</link><guid isPermaLink="false">https://www.berlinchan.com/2026/01/fix-mjx-h12p-ford-bronco-rc-oil-leakage</guid><pubDate>Sat, 10 Jan 2026 15:24:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;买美嘉欣 H12P 作为圣诞节礼物给儿子。Ford Bronco 福特烈马授权的模型还原度很高，超帅！在公园玩时，总是一道靓丽的风景。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dd540278bef5d3eca4cc78b1859c73ef/ca9b4/MJX-H12P-Ford-Bronco-1.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.78947368421052%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMCBAX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAgP/2gAMAwEAAhADEAAAAZpWZ3oFIP/EABwQAAICAgMAAAAAAAAAAAAAAAEDAAIREiEjMf/aAAgBAQABBQJrSTV+kq3gnsr5if/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AYj/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwGq/8QAGhAAAgIDAAAAAAAAAAAAAAAAAREAECFhof/aAAgBAQAGPwJCYHXS1f8A/8QAGxABAAMBAAMAAAAAAAAAAAAAAQARIVFBgZH/2gAIAQEAAT8hAA0mxHpR2ApW7eRKeG/cBipR79n/2gAMAwEAAgADAAAAEP8Az//EABYRAQEBAAAAAAAAAAAAAAAAAAARIf/aAAgBAwEBPxDaH//EABYRAQEBAAAAAAAAAAAAAAAAAAARIf/aAAgBAgEBPxDKn//EAB0QAQEAAQQDAAAAAAAAAAAAAAERACExUWFBcaH/2gAIAQEAAT8QcveLb7vkYo0lBeesDkBUC5q5rqTwN8mSlXu+8UVaeEZ//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/dd540278bef5d3eca4cc78b1859c73ef/5ed05/MJX-H12P-Ford-Bronco-1.webp 190w,
/static/dd540278bef5d3eca4cc78b1859c73ef/9d76b/MJX-H12P-Ford-Bronco-1.webp 380w,
/static/dd540278bef5d3eca4cc78b1859c73ef/33466/MJX-H12P-Ford-Bronco-1.webp 760w,
/static/dd540278bef5d3eca4cc78b1859c73ef/ca244/MJX-H12P-Ford-Bronco-1.webp 1140w,
/static/dd540278bef5d3eca4cc78b1859c73ef/e9f1f/MJX-H12P-Ford-Bronco-1.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/dd540278bef5d3eca4cc78b1859c73ef/89129/MJX-H12P-Ford-Bronco-1.jpg 190w,
/static/dd540278bef5d3eca4cc78b1859c73ef/0036d/MJX-H12P-Ford-Bronco-1.jpg 380w,
/static/dd540278bef5d3eca4cc78b1859c73ef/e484a/MJX-H12P-Ford-Bronco-1.jpg 760w,
/static/dd540278bef5d3eca4cc78b1859c73ef/32d87/MJX-H12P-Ford-Bronco-1.jpg 1140w,
/static/dd540278bef5d3eca4cc78b1859c73ef/ca9b4/MJX-H12P-Ford-Bronco-1.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/dd540278bef5d3eca4cc78b1859c73ef/e484a/MJX-H12P-Ford-Bronco-1.jpg&quot;
            alt=&quot;MJX H12P Ford Bronco 1&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;原厂的避震器密封胶圈尺寸不合适，安装时经常从原位置脱落导致漏油。玩的第二天初次发现漏油后，我拆开多个避震器看到胶圈是这种状态，而且有一个胶圈有破损。问卖家说他没有单独的胶圈配件，只能换整只避震器。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/03585445cb2985573e466f3c16087532/ca9b4/MJX-H12P-Ford-Bronco-seal.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.26315789473685%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMBBP/EABUBAQEAAAAAAAAAAAAAAAAAAAIA/9oADAMBAAIQAxAAAAHirXSqCH//xAAbEAADAAIDAAAAAAAAAAAAAAAAAQMCBBESIf/aAAgBAQABBQKmu8Ckuo54lacGt6LJt//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/AUn/xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPwGn/8QAHBAAAgICAwAAAAAAAAAAAAAAAAECIRExMlKB/9oACAEBAAY/AsqyMuRo9G+pLVM//8QAGhABAQEBAQEBAAAAAAAAAAAAAREAMSFBUf/aAAgBAQABPyEQUn66NFFBE8b+YLDwZAktjdrqhrX0nN//2gAMAwEAAgADAAAAEAD/AP/EABcRAAMBAAAAAAAAAAAAAAAAAAABETH/2gAIAQMBAT8QXYjD/8QAFhEBAQEAAAAAAAAAAAAAAAAAAREQ/9oACAECAQE/EBSuf//EABwQAQADAAIDAAAAAAAAAAAAAAEAESExYUFRgf/aAAgBAQABPxAQBZXqj1UML1Zi1gldbgVkpbHIeI8C7Oq0JeC0fIDrC34533P/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/03585445cb2985573e466f3c16087532/5ed05/MJX-H12P-Ford-Bronco-seal.webp 190w,
/static/03585445cb2985573e466f3c16087532/9d76b/MJX-H12P-Ford-Bronco-seal.webp 380w,
/static/03585445cb2985573e466f3c16087532/33466/MJX-H12P-Ford-Bronco-seal.webp 760w,
/static/03585445cb2985573e466f3c16087532/ca244/MJX-H12P-Ford-Bronco-seal.webp 1140w,
/static/03585445cb2985573e466f3c16087532/e9f1f/MJX-H12P-Ford-Bronco-seal.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/03585445cb2985573e466f3c16087532/89129/MJX-H12P-Ford-Bronco-seal.jpg 190w,
/static/03585445cb2985573e466f3c16087532/0036d/MJX-H12P-Ford-Bronco-seal.jpg 380w,
/static/03585445cb2985573e466f3c16087532/e484a/MJX-H12P-Ford-Bronco-seal.jpg 760w,
/static/03585445cb2985573e466f3c16087532/32d87/MJX-H12P-Ford-Bronco-seal.jpg 1140w,
/static/03585445cb2985573e466f3c16087532/ca9b4/MJX-H12P-Ford-Bronco-seal.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/03585445cb2985573e466f3c16087532/e484a/MJX-H12P-Ford-Bronco-seal.jpg&quot;
            alt=&quot;MJX H12P Ford Bronco seal&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/38ded264d0444345bb0d6b5acfdaead8/ca9b4/MJX-H12P-Ford-Bronco-seal-broken.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAUCAwT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB1UsZQtGYf//EABkQAAIDAQAAAAAAAAAAAAAAAAIDAAESEP/aAAgBAQABBQLNxq7yhZiHf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAAICAwAAAAAAAAAAAAAAAAABESAiMVH/2gAIAQEABj8CfBRLM90//8QAGRAAAwEBAQAAAAAAAAAAAAAAAREhADEQ/9oACAEBAAE/IWlkC5iAwGo3IJdZX3//2gAMAwEAAgADAAAAEOs//8QAFhEAAwAAAAAAAAAAAAAAAAAAEBEh/9oACAEDAQE/EFB//8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Qp//EABoQAQADAQEBAAAAAAAAAAAAAAEAESExUUH/2gAIAQEAAT8Qoi6EdJt3uYpTWvjVwPcafaMgPkiBwIBXCf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/38ded264d0444345bb0d6b5acfdaead8/5ed05/MJX-H12P-Ford-Bronco-seal-broken.webp 190w,
/static/38ded264d0444345bb0d6b5acfdaead8/9d76b/MJX-H12P-Ford-Bronco-seal-broken.webp 380w,
/static/38ded264d0444345bb0d6b5acfdaead8/33466/MJX-H12P-Ford-Bronco-seal-broken.webp 760w,
/static/38ded264d0444345bb0d6b5acfdaead8/ca244/MJX-H12P-Ford-Bronco-seal-broken.webp 1140w,
/static/38ded264d0444345bb0d6b5acfdaead8/e9f1f/MJX-H12P-Ford-Bronco-seal-broken.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/38ded264d0444345bb0d6b5acfdaead8/89129/MJX-H12P-Ford-Bronco-seal-broken.jpg 190w,
/static/38ded264d0444345bb0d6b5acfdaead8/0036d/MJX-H12P-Ford-Bronco-seal-broken.jpg 380w,
/static/38ded264d0444345bb0d6b5acfdaead8/e484a/MJX-H12P-Ford-Bronco-seal-broken.jpg 760w,
/static/38ded264d0444345bb0d6b5acfdaead8/32d87/MJX-H12P-Ford-Bronco-seal-broken.jpg 1140w,
/static/38ded264d0444345bb0d6b5acfdaead8/ca9b4/MJX-H12P-Ford-Bronco-seal-broken.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/38ded264d0444345bb0d6b5acfdaead8/e484a/MJX-H12P-Ford-Bronco-seal-broken.jpg&quot;
            alt=&quot;MJX H12P Ford Bronco seal broken&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;儿子的小鞭炮瓶子上的橡胶挂绳尺寸正好，我裁剪了几个换上，再也不漏油。自己裁剪的胶圈没有了气肺结构，所以灌满避震油后，要把弹簧压缩到最紧后再上紧盖子，否则避震桶是没法压缩的。我用的是 300号避震油~~，使用起来感觉没什么差别（对我这个业余者来说）~~。这样带来的影响是，避震有点不回弹导致行程变短。关于避震的安装，这个&lt;a href=&quot;https://www.youtube.com/watch?v=NoRan7WgA9I&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;视频&lt;/a&gt;讲得很清楚。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1603e0a81325c5cdf5bc0fb24beda061/ca9b4/MJX-H12P-Ford-Bronco-seal-bottle.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.42105263157895%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAACA//EABYBAQEBAAAAAAAAAAAAAAAAAAEAA//aAAwDAQACEAMQAAABUd/LJzMm/8QAGxABAAICAwAAAAAAAAAAAAAAAgEDABESEyH/2gAIAQEAAQUC4GsyDa4rr0hvOrDHn//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABkRAQACAwAAAAAAAAAAAAAAAAEAAhExQf/aAAgBAgEBPwFq8YDjc//EABoQAAICAwAAAAAAAAAAAAAAAAERAAIgMTP/2gAIAQEABj8CKEFrNLU5g4f/xAAZEAEBAQADAAAAAAAAAAAAAAABABEhMYH/2gAIAQEAAT8hxnB3lmqMiAeSAwMEUsb/AP/aAAwDAQACAAMAAAAQB8//xAAXEQEBAQEAAAAAAAAAAAAAAAABEQAh/9oACAEDAQE/ECTplLv/xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQIBAT8QYrhHAz//xAAbEAEBAQEAAwEAAAAAAAAAAAABEQAhMUFRof/aAAgBAQABPxDvOO23usUCNit8vwmCFBK1+uQ1a86/dWN99uIDf//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/1603e0a81325c5cdf5bc0fb24beda061/5ed05/MJX-H12P-Ford-Bronco-seal-bottle.webp 190w,
/static/1603e0a81325c5cdf5bc0fb24beda061/9d76b/MJX-H12P-Ford-Bronco-seal-bottle.webp 380w,
/static/1603e0a81325c5cdf5bc0fb24beda061/33466/MJX-H12P-Ford-Bronco-seal-bottle.webp 760w,
/static/1603e0a81325c5cdf5bc0fb24beda061/ca244/MJX-H12P-Ford-Bronco-seal-bottle.webp 1140w,
/static/1603e0a81325c5cdf5bc0fb24beda061/e9f1f/MJX-H12P-Ford-Bronco-seal-bottle.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/1603e0a81325c5cdf5bc0fb24beda061/89129/MJX-H12P-Ford-Bronco-seal-bottle.jpg 190w,
/static/1603e0a81325c5cdf5bc0fb24beda061/0036d/MJX-H12P-Ford-Bronco-seal-bottle.jpg 380w,
/static/1603e0a81325c5cdf5bc0fb24beda061/e484a/MJX-H12P-Ford-Bronco-seal-bottle.jpg 760w,
/static/1603e0a81325c5cdf5bc0fb24beda061/32d87/MJX-H12P-Ford-Bronco-seal-bottle.jpg 1140w,
/static/1603e0a81325c5cdf5bc0fb24beda061/ca9b4/MJX-H12P-Ford-Bronco-seal-bottle.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/1603e0a81325c5cdf5bc0fb24beda061/e484a/MJX-H12P-Ford-Bronco-seal-bottle.jpg&quot;
            alt=&quot;MJX H12P Ford Bronco seal bottle&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;汽车是每个男孩的梦想。给儿子拍一个超酷的短片。短片是模仿 &lt;a href=&quot;https://www.youtube.com/watch?v=dXWLrGkYh8Q&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The All-New 2021 Ford Bronco Return of a legend&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/caV4EUFh0is?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[记 Nikon55-200VR 镜头防抖维修（失败）]]></title><description><![CDATA[大概 2009年购入的 Nikon 55-200 VR 镜头防抖坏了，斗胆自己拆机维修试试看。Taobao 买到了损坏的排线配件，自己焊接飞线，花了一整天，但还是失败告终。]]></description><link>https://www.berlinchan.com/2025/03/failing-to-fix-nikon-55-200-vr-lens</link><guid isPermaLink="false">https://www.berlinchan.com/2025/03/failing-to-fix-nikon-55-200-vr-lens</guid><pubDate>Mon, 31 Mar 2025 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/pU-NngSMhJM?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;p&gt;大概 2009年购入的 Nikon 55-200 VR 镜头防抖坏了，使用率很低，但偶尔出去玩的时候给儿子拍些运动的照片还是有用处的。找维修师傅竟然被拒“没有维修价值”。据说维修镜头这事，一看就会，一拆就废。之前已经&lt;a href=&quot;https://youtu.be/8Y48-AidUsI&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;拆费过一个 18-55 狗头&lt;/a&gt;，这次还是斗胆自己拆机维修试试看。&lt;/p&gt;
&lt;p&gt;拆机前我在网上搜了一下，看到别人也有同样的问题，有的自己动手成功修复，有的没有下文。从镜头后部能看到断开的排线，能确定故障的原因，但不知道这个排线的型号。拆开后照样子在 Taobao 买到排线配件，花一整天自己焊接飞线，但还是失败告终，估计是飞线未焊好，最后作罢原模原样装回去。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7d7cb73b7cef3377948e297f33954804/1f1e7/fix-nikon-55-200-vr-lens.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.21052631578948%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAUBAwT/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAL/2gAMAwEAAhADEAAAAWlMqsTswCf/xAAZEAADAQEBAAAAAAAAAAAAAAABAgMAEQT/2gAIAQEAAQUCPczMGzPwUq1KeZy0v//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABsQAAICAwEAAAAAAAAAAAAAAAABAhEQISJR/9oACAEBAAY/ArRHxvKiuVRs/8QAGhABAQEBAAMAAAAAAAAAAAAAAREAIRBBUf/aAAgBAQABPyE1TvzGMkD78NYM5EK5hbajLd//2gAMAwEAAgADAAAAEHj/AP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/EIf/xAAcEAEAAwADAQEAAAAAAAAAAAABABEhMUFhUfD/2gAIAQEAAT8Q3FTs4nycwgIcKc83uX7FJbHuJ5Q2tWPmts0/rn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/7d7cb73b7cef3377948e297f33954804/5ed05/fix-nikon-55-200-vr-lens.webp 190w,
/static/7d7cb73b7cef3377948e297f33954804/9d76b/fix-nikon-55-200-vr-lens.webp 380w,
/static/7d7cb73b7cef3377948e297f33954804/33466/fix-nikon-55-200-vr-lens.webp 760w,
/static/7d7cb73b7cef3377948e297f33954804/ca244/fix-nikon-55-200-vr-lens.webp 1140w,
/static/7d7cb73b7cef3377948e297f33954804/b0196/fix-nikon-55-200-vr-lens.webp 1520w,
/static/7d7cb73b7cef3377948e297f33954804/65980/fix-nikon-55-200-vr-lens.webp 1800w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/7d7cb73b7cef3377948e297f33954804/89129/fix-nikon-55-200-vr-lens.jpg 190w,
/static/7d7cb73b7cef3377948e297f33954804/0036d/fix-nikon-55-200-vr-lens.jpg 380w,
/static/7d7cb73b7cef3377948e297f33954804/e484a/fix-nikon-55-200-vr-lens.jpg 760w,
/static/7d7cb73b7cef3377948e297f33954804/32d87/fix-nikon-55-200-vr-lens.jpg 1140w,
/static/7d7cb73b7cef3377948e297f33954804/cf227/fix-nikon-55-200-vr-lens.jpg 1520w,
/static/7d7cb73b7cef3377948e297f33954804/1f1e7/fix-nikon-55-200-vr-lens.jpg 1800w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/7d7cb73b7cef3377948e297f33954804/e484a/fix-nikon-55-200-vr-lens.jpg&quot;
            alt=&quot;fix-nikon-55-200-vr-lens&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[I'm on Turing Developer Review]]></title><description><![CDATA[I changed my job to Turing as a front-end developer and work for a US-based company since April, and was invited to record a review video.]]></description><link>https://www.berlinchan.com/2022/09/turing-developer-review</link><guid isPermaLink="false">https://www.berlinchan.com/2022/09/turing-developer-review</guid><pubDate>Thu, 01 Sep 2022 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;I changed my job to Turing as a front-end developer and work for a US-based company since April, and was invited to record a review video.&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/kXNOwVDwzu8?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/vuf-u4EIMNg?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/a3JeapNlurg?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;p&gt;For making this video, it takes my whole family 2-day to tidy the messy house✨. It was a wonderful journey to make this video with Turing’s team and my family👍!&lt;/p&gt;
&lt;p&gt;I like the movie ”&lt;a href=&quot;https://www.imdb.com/title/tt2084970/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Imitation Game&lt;/a&gt;” that I mentioned in the video.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b0e1b0d7ac7c0537b4c0325f72a64eeb/9d361/the-imitation-game-poster.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 150%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAeABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAMBBAUC/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAcWwnXXIGipuUu0kCv/EAB0QAAIBBAMAAAAAAAAAAAAAAAABEQIDEiEEECP/2gAIAQEAAQUCgdipUYiMk+Po2T5ORGT6/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAEREP/aAAgBAwEBPwFIiyn/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPwFjP//EABsQAAEEAwAAAAAAAAAAAAAAAAABAhASESDh/9oACAEBAAY/Ai05eqnIrr//xAAdEAEAAwACAwEAAAAAAAAAAAABABEhQXEQUYGR/9oACAEBAAE/IWnECszuX9RFkMiKpPsXEBSwah374iGN/sVOTCvH/9oADAMBAAIAAwAAABDn0TD/xAAXEQEBAQEAAAAAAAAAAAAAAAABABEQ/9oACAEDAQE/EAeQ5bv/xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQIBAT8QSWsFsv/EAB0QAQADAQEAAwEAAAAAAAAAAAEAESExQVFxgfD/2gAIAQEAAT8QoGrebARj7j8mArL5seCpzQ2XeAqJS8NHvIopeG0fmMsXpl23FOHsVKKGQt/RBLHN8P7yfc//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/b0e1b0d7ac7c0537b4c0325f72a64eeb/5ed05/the-imitation-game-poster.webp 190w,
/static/b0e1b0d7ac7c0537b4c0325f72a64eeb/9d76b/the-imitation-game-poster.webp 380w,
/static/b0e1b0d7ac7c0537b4c0325f72a64eeb/33466/the-imitation-game-poster.webp 760w,
/static/b0e1b0d7ac7c0537b4c0325f72a64eeb/e8af0/the-imitation-game-poster.webp 800w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/b0e1b0d7ac7c0537b4c0325f72a64eeb/89129/the-imitation-game-poster.jpg 190w,
/static/b0e1b0d7ac7c0537b4c0325f72a64eeb/0036d/the-imitation-game-poster.jpg 380w,
/static/b0e1b0d7ac7c0537b4c0325f72a64eeb/e484a/the-imitation-game-poster.jpg 760w,
/static/b0e1b0d7ac7c0537b4c0325f72a64eeb/9d361/the-imitation-game-poster.jpg 800w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/b0e1b0d7ac7c0537b4c0325f72a64eeb/e484a/the-imitation-game-poster.jpg&quot;
            alt=&quot;The Immitation Game&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Interesting Blogs/Articals(To Be Continued)]]></title><description><![CDATA[Some interesting blogs or articles that I like, jot them here in case I want to read them again.]]></description><link>https://www.berlinchan.com/2022/02/interesting-blogs-sites</link><guid isPermaLink="false">https://www.berlinchan.com/2022/02/interesting-blogs-sites</guid><pubDate>Sun, 13 Feb 2022 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;When I surf on the Internet, I found some interesting, useful, impression, inspired blogs/sites occassionally, sometimes I want to find it out and read it again, but I had nothing clues to find them, that’s really made me feel frustrate.&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;p&gt;Next time when I find something like this, I’ll jot it down here.&lt;/p&gt;
&lt;h2 id=&quot;letters-to-a-new-developer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#letters-to-a-new-developer&quot; aria-label=&quot;letters to a new developer permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://letterstoanewdeveloper.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Letters To A New Developer&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I heard a principle of programming from my colleague,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Always leave the code better than you found it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I found this site when I was search the explaination, and found more interesting letters on the site, like this one: &lt;a href=&quot;https://letterstoanewdeveloper.com/2019/08/23/the-surprising-number-of-programmers-who-cant-program/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Surprising Number Of Programmers Who Can’t Program&lt;/a&gt;. I’m curies what’s the &lt;strong&gt;FizzBuzz&lt;/strong&gt; about, so I searched it and found an other interesting post, &lt;a href=&quot;https://joelgrus.com/2016/05/23/fizz-buzz-in-tensorflow/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Fizz Buzz in Tensorflow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Although it’s a fiction story, I believe so, but the idea behind it is enlightening, and the author even wrote a &lt;a href=&quot;https://joelgrus.com/2020/06/06/ten-essays-on-fizz-buzz/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;book&lt;/a&gt; about it.&lt;/p&gt;
&lt;h2 id=&quot;fengsi&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fengsi&quot; aria-label=&quot;fengsi permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://feng.si/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;feng.si&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/67fb494847d875a1f6f37fee65426adc/e066f/snapshot_feng.si.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABeElEQVR42nWRvW7bMBDH8wBOFLtemsjiNyXe8UMUJTlWYqRAO3bJ0jHwkA4tUBQdC3TIM+QV+qJlkKRICxv44cDhfvzfkUeEwxNUoHGxtrHBrsZIJRIJFW8yK17L2msMGsLf/szRKxlC30M3+n4yYVCYqFcLejIvi6U4PX5zvGDFkiz3y0SAtgO4Tei2NQxS9wRV8XZ2Wp7MSTGbz4rz2fxskdv2yRxkB2oAvQFzbZvJ0RqIMlQbqp4h0hxOTq0MniNKh7lSDewFDk6FVrp9O1ccBDfeOF63jLuzlXl6whVtStqcVzXTjjc+1wPJuZtBxXIO3nx8H92lbLxqWgfR+SFfVDFDmDko58lzFNfx98P9j93t7vO33d2XXzeffn79jnFTVpoIPCw/YkoCIfVgUeggtBfSCuWYtPRf879/RuuH2F+m8cq3Fz5O/Xob05TG7bh591gvrl1Yr6jZL3fD1bT9kH2DyYaxTZOxCf2QMba3YZ0P1au1/wALxGifZpjhHwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/67fb494847d875a1f6f37fee65426adc/5ed05/snapshot_feng.si.webp 190w,
/static/67fb494847d875a1f6f37fee65426adc/9d76b/snapshot_feng.si.webp 380w,
/static/67fb494847d875a1f6f37fee65426adc/33466/snapshot_feng.si.webp 760w,
/static/67fb494847d875a1f6f37fee65426adc/ca244/snapshot_feng.si.webp 1140w,
/static/67fb494847d875a1f6f37fee65426adc/b0196/snapshot_feng.si.webp 1520w,
/static/67fb494847d875a1f6f37fee65426adc/3a324/snapshot_feng.si.webp 2710w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/67fb494847d875a1f6f37fee65426adc/253c6/snapshot_feng.si.png 190w,
/static/67fb494847d875a1f6f37fee65426adc/810ee/snapshot_feng.si.png 380w,
/static/67fb494847d875a1f6f37fee65426adc/b4918/snapshot_feng.si.png 760w,
/static/67fb494847d875a1f6f37fee65426adc/c0204/snapshot_feng.si.png 1140w,
/static/67fb494847d875a1f6f37fee65426adc/54dc9/snapshot_feng.si.png 1520w,
/static/67fb494847d875a1f6f37fee65426adc/e066f/snapshot_feng.si.png 2710w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/67fb494847d875a1f6f37fee65426adc/b4918/snapshot_feng.si.png&quot;
            alt=&quot;https://feng.si/&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I found it by one of the author’s project named &lt;a href=&quot;https://cfeditor.feng.si/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;CFEditor: Make Routers Great Again&lt;/a&gt;, it’s a ASUS router CFE modify tool. In the site, there is a terminal window on every top of the post, and the command-line described the gist of each post, it’s very interesting! There is a post explained &lt;a href=&quot;https://feng.si/posts/2019/07/centos-the-last-linux-distro-you-should-ever-consider/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;why you should keep away from CentOS&lt;/a&gt;, the author’s point of view is unique.&lt;/p&gt;
&lt;h2 id=&quot;the-stories-behind-3-great-business-mantras&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-stories-behind-3-great-business-mantras&quot; aria-label=&quot;the stories behind 3 great business mantras permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The stories behind 3 great business mantras&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://theweek.com/articles/462863/stories-behind-3-great-business-mantras&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The stories behind 3 great business mantras&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Google: &lt;code class=&quot;language-text&quot;&gt;Don&apos;t be evil&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Facebook:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Code wins arguments&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Move fast and breaking things&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Done is better than perfect&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apple: &lt;code class=&quot;language-text&quot;&gt;Think different&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;to-be-continued&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#to-be-continued&quot; aria-label=&quot;to be continued permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;To Be Continued&lt;/h2&gt;</content:encoded></item><item><title><![CDATA[See Movie The Wave(Die Welle)]]></title><description><![CDATA[What are the requirements for an autocratic system? An ideology, control, surveilliance, social injustice, extreme nationalism.]]></description><link>https://www.berlinchan.com/2022/01/see-movie-the-wave</link><guid isPermaLink="false">https://www.berlinchan.com/2022/01/see-movie-the-wave</guid><pubDate>Sat, 29 Jan 2022 10:46:37 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;What are the requirements for an autocratic system? An ideology, control, surveilliance, social injustice, extreme nationalism.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!-- endExcerpt --&gt;
&lt;blockquote&gt;
&lt;p&gt;What else is important for a dictatorship? Discipline, discipline is power.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Some lines quoted from the movie &lt;a href=&quot;https://www.imdb.com/title/tt1063669/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Wave&lt;/a&gt; answered my question for why I felt nervous and depressed since I was in school, and how we got brainwashed little by little.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e5d916a91d5f3bd5c7617f8d363e8271/36e56/die-welle.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAACbUlEQVR42gFiAp39ANXXobu5rbCunWt4U0VZQjRCLzdAKEVMKk9WMEBPJU5aM1JdMkFNI0tPMFNZOnF4XVZkRU1mPVluUHB+bADX2au/vqy6sqiRj3OIfmqFb1injHutk4WihnaokYRoYVmWgXaYgXWMdWiPe252ZVdzZ1ZHUTMmKhwsNyIA0dChpJ50pJONwKqewamcp4t5tZ+Tp5OJr5mMqJaMl3lmkoZ7cGFXinhrgXBkfWpXg3Vpbl9OW09FNjspAJeMa6WYe5mEfItzZJB6cJJ8cZB4aoJrX9PGxkg9M19DLXxnV4aBfG1bTYl9b6GQg3xwZYd8cmtdU1xTSgBgSD2mlY+PdmibioJYPS6MeHC2qaO9qp7Nvr1MR0UuIhVQPzNJR0FhUkhzbWKglYqYjYGWl5FlX1VsaWIAbmdihYN/tKmmeHNqNygfREE6mI6F0ZeLz2hYZkU5jJGLm5STQkRAenJpb2VbgHtwiYeBXmBYbnBmV1VKAHV+e2NhWnJ0b1teV21ybIZxa4M6LrhnXLZdSaCCeqOlo8nCwPn09bi1skI7NGJnYmZjWlheV2tybjw/OAA8RENTVFEbHh1cX15NVVJ7YFyohn+uf3W3gnTBq6W/trK5trX07/K9vLtHRUIiJyUoKiY8PTslKCcrLS0ACQ8QICYnERQTHx8gFRoYQEdJk5eWb3t1kpuYzMzLxcfGr7GwzMzMp6SmTU5PEhQWEhYXEQ8UEBAUERAUABUbHBUbHBkbGhweHQ8TERAVFR0hHi02LC82LGBmYJGUkaaopqSjozY0NioqKxoaHBobHBkZGhseHRQQE4BM+J4B+fzHAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/e5d916a91d5f3bd5c7617f8d363e8271/5ed05/die-welle.webp 190w,
/static/e5d916a91d5f3bd5c7617f8d363e8271/9d76b/die-welle.webp 380w,
/static/e5d916a91d5f3bd5c7617f8d363e8271/33466/die-welle.webp 760w,
/static/e5d916a91d5f3bd5c7617f8d363e8271/e8af0/die-welle.webp 800w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/e5d916a91d5f3bd5c7617f8d363e8271/253c6/die-welle.png 190w,
/static/e5d916a91d5f3bd5c7617f8d363e8271/810ee/die-welle.png 380w,
/static/e5d916a91d5f3bd5c7617f8d363e8271/b4918/die-welle.png 760w,
/static/e5d916a91d5f3bd5c7617f8d363e8271/36e56/die-welle.png 800w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/e5d916a91d5f3bd5c7617f8d363e8271/b4918/die-welle.png&quot;
            alt=&quot;die-welle&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This movie is based on a true story that once happened in spring 1967, in Palo Alto, California, high school history teacher Ron Jones conducted a social experiment in fascism with his class. There is a &lt;a href=&quot;https://www.thewavehome.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;website&lt;/a&gt; to document and share the original experiment and variations on the Wave story over the years, and to support the discussion of this dark and never-ending side of human nature.&lt;/p&gt;
&lt;h2 id=&quot;my-favorite-lines&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#my-favorite-lines&quot; aria-label=&quot;my favorite lines permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;My Favorite Lines&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;What are we supposed to rebel against nowadays?&lt;/p&gt;
&lt;p&gt;Nothing means anything anymore.&lt;/p&gt;
&lt;p&gt;We all just want to have fun.&lt;/p&gt;
&lt;p&gt;What our generation lacks is a common goal to unite us.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Autocracy comes from the Greek and means self-rule. From auto, “self”, and kratia, “power”, “rule”… In an autocracy, the ruler or rulers have unlimited power to change the law as they like. Can you think of any examples?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;We’re not talking about guilt. It’s a historical responsibility.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My thoughts: This recalls me of another movie that explored national historical trauma: &lt;a href=&quot;https://www.imdb.com/title/tt6878038/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;A Taxi Driver&lt;/a&gt;. And also some historical tragedies happened in my country that we still can’t discuss in public today. An ethnic just like a person, if he/she can’t face the past mistake, he/she can’t go further too.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I could see myself getting a regular job, marrying the right girl, having two or three kids and living my life in some townhouse. Does that sound stupid?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;If you’ve learnt one thing this week, it’s the value of sticking together.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[The Banality of Evil - Experimenter]]></title><description><![CDATA[The movie: Experimenter reminds us how easy a person will obedience to authority, how important to keep independent thinking. And be aware the pitfall of The Banality of Evil.]]></description><link>https://www.berlinchan.com/2022/01/the-banality-of-evil-experimenter</link><guid isPermaLink="false">https://www.berlinchan.com/2022/01/the-banality-of-evil-experimenter</guid><pubDate>Tue, 25 Jan 2022 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;The movie: &lt;a href=&quot;https://www.imdb.com/title/tt3726704/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Experimenter&lt;/a&gt; reminds us how easy a person will obedience to authority, how important to keep independent thinking. And be aware the pitfall of &lt;a href=&quot;https://aeon.co/ideas/what-did-hannah-arendt-really-mean-by-the-banality-of-evil&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;strong&gt;The Banality of Evil&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/42b7d7e967e72b4c3cfa0d9598415fbc/36e56/experimenter1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAACbUlEQVR42gFiAp39ABMbIBEZHREXGwkRFy0nJXhlW4OZoYapunmZp2+QnmyNnWaHl2CBk198jl16i1t3iFdzg1Jse0tjckNYZgAUHiMTGh8TGh4DDhRiTEO1hm6Ai42hw9CYuMSEpbZTa3pQY21fbnZadYRffItkgI5jgI5gfIpcd4hadYcAFyMqFB0hEhoeBxEXVEZBj3NnorjAr8vVqsbPka69N0hPJSMgUE5MaoqYfJuoh6axi6q3jau5iqq5iam6ACEuNhQgJSAlKjY2PGJjaJCRjl5kaoWWnb3Y4aO/zj9OVR8dHWp+h5G2xpu8yqPCz6PAzaO+zJ+8ypq5xwAsO0UcKjIpLjM2NDpaWV7Myrw/PkEaGhyHl5+nw9ErMzkAAABBTlZ+maZ8lJ+CmaShvMiuyderxtWiv80ANEdSKThBKSwwIB4hODk+gHtyLi4uPENHFBQWLTE2PkJGcWhiVUtGJisvEhcaDxIUbICJkqu4gpmll7TEADpRXzBBSykqLiEgIykpLCopKDAzNKG3vmBvcnaEh4aTlqOqqZ+ioomTl4CMkIGOkm17gSkzOhkgJmR0fgBAV2M1SFIpKS0vLTE0NDgnJipFSk6ju8Ois7bJ19rJ1tm6x8uwv8SZo6amsrefrbGTnqMRFhoWGh88QEMARFxpOU5XJSgtKyovMjE1JSQmPkNHnLfBjqCjorCzprS3prO3oa6ymqernqqvorC1ipecIygsAgMFZXF3AEdgbzhPWU1LTjw2NissMCYlKDs+QZOpr5iqrbfHy7TFybbHy7bFyrTCxqy6v6i3vJmnrkxWXjlKV4ucpeVg/9mlKSngAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/42b7d7e967e72b4c3cfa0d9598415fbc/5ed05/experimenter1.webp 190w,
/static/42b7d7e967e72b4c3cfa0d9598415fbc/9d76b/experimenter1.webp 380w,
/static/42b7d7e967e72b4c3cfa0d9598415fbc/33466/experimenter1.webp 760w,
/static/42b7d7e967e72b4c3cfa0d9598415fbc/e8af0/experimenter1.webp 800w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/42b7d7e967e72b4c3cfa0d9598415fbc/253c6/experimenter1.png 190w,
/static/42b7d7e967e72b4c3cfa0d9598415fbc/810ee/experimenter1.png 380w,
/static/42b7d7e967e72b4c3cfa0d9598415fbc/b4918/experimenter1.png 760w,
/static/42b7d7e967e72b4c3cfa0d9598415fbc/36e56/experimenter1.png 800w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/42b7d7e967e72b4c3cfa0d9598415fbc/b4918/experimenter1.png&quot;
            alt=&quot;experimenter&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;my-favorite-lines&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#my-favorite-lines&quot; aria-label=&quot;my favorite lines permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;My favorite lines&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Why don’t I have a choice? I came here on my own free will.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;In nearly every case, the essential results are the same. They heisitate, sigh, tremble and groan, but they advance to the last switch, 450 volts, “Danger Severe Shock XXX”, because they’re politely told to. The results are terrifying and depressing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The agentic state, in which the demands of the democratically installed authority conflict with conscience.
&lt;strong&gt;The Banality of Evil.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;In the agentic state, the individual difines himself as an instrument carrying out the wished of others.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;A person has a choice. He or she chooses to become agentic. But once you assume the role, it’s almost impossible to go back.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Every day I choose you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;♪ Fools give you reasons. Wise men never try ♪&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;You could say we’re puppets, with perception, with awareness. Sometimes we can see the strings, and perhaps, our awareness is the first step in our liberation.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[See The Movie Don't Look Up]]></title><description><![CDATA[I just saw the comedy movie ”Don’t Look Up”, it’s seriously funny, like what the director said. It means serious and funny.]]></description><link>https://www.berlinchan.com/2022/01/see-movie-dont-look-up</link><guid isPermaLink="false">https://www.berlinchan.com/2022/01/see-movie-dont-look-up</guid><pubDate>Sun, 09 Jan 2022 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;I just saw the comedy movie ”&lt;a href=&quot;https://www.imdb.com/title/tt11286314/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Don’t Look Up&lt;/a&gt;”, it’s seriously funny, like &lt;a href=&quot;https://youtu.be/YEj3juMYCn4?t=42&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;what the director said&lt;/a&gt;. It means serious and funny.&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;p&gt;It’s a perfect sample to help us to understand why would people say “The core of comedy is the tragedy”.&lt;/p&gt;
&lt;p&gt;I like the scene when DiCaprio’s character yelling towards the camera, shouted “the president of the United State is f**king lying.” His acting was so crazy and emotional.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f76d24528bf9972be7e7c43e5364fad3/b41e7/dont-look-up.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.578947368421055%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8UlEQVR42mMwVVMxVFHWVZBTlZKUExGREuSXFOCTERMMtDU/09N6d+702VWFSnLS8hpa+vbO+nbOBo6uxq6e5p6+QMRgqKSgr6igLSenJSujLi2lJCYixstjp6la7O0xOTVxXmHu4tI8DSkRRgYGcSkpdWNTLXNrLTMrLQsbXRsHBgMFeT15WV15eT0FBT1FRR15OUlBgWRbq82FBRPDwjeVlp7t6bJXVwZq5mRm5OfjkVRQlFPTUNUzUDcyYzBRUjBUkNNXkDdUVjJUVgYylESFHTTUVifHppubNPl6H2mskeblZmVg4GFjYWdiMDQyC04rUDO1lNPSZtAQFVYXFdYUE9GSEDWQl7XS1nAyN/UI8M0K8atwdeqLjqxLjjWwMJeTlBBgZ2NgYFi0aPaa9okZAemmRrYM3XVV7dUV5fmFifGJ6fnZld1tKWXF4fHROq4uqqpKLpZm8RnprhlpVmHBdoH+6jo6CxbNmqyt18TAVyyrwRDqYhvkZONmbhIS6O+flmIRHmrj7bWjrqKnpMA5Miq+qck+IdEsLETX19skItQ2Lnbzrs1Tdc3bGVimMgsy6IoIaosKqfPzOJgZO4QEi+rpRcZGXehsPjWpO7a4qHvNmujaOv1Af8NAf7PQYG0vz1Vb1szQs2phYJ3LoQwAHoV+jKzMR9EAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/f76d24528bf9972be7e7c43e5364fad3/5ed05/dont-look-up.webp 190w,
/static/f76d24528bf9972be7e7c43e5364fad3/9d76b/dont-look-up.webp 380w,
/static/f76d24528bf9972be7e7c43e5364fad3/33466/dont-look-up.webp 760w,
/static/f76d24528bf9972be7e7c43e5364fad3/ca244/dont-look-up.webp 1140w,
/static/f76d24528bf9972be7e7c43e5364fad3/b0196/dont-look-up.webp 1520w,
/static/f76d24528bf9972be7e7c43e5364fad3/ea13c/dont-look-up.webp 1920w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/f76d24528bf9972be7e7c43e5364fad3/253c6/dont-look-up.png 190w,
/static/f76d24528bf9972be7e7c43e5364fad3/810ee/dont-look-up.png 380w,
/static/f76d24528bf9972be7e7c43e5364fad3/b4918/dont-look-up.png 760w,
/static/f76d24528bf9972be7e7c43e5364fad3/c0204/dont-look-up.png 1140w,
/static/f76d24528bf9972be7e7c43e5364fad3/54dc9/dont-look-up.png 1520w,
/static/f76d24528bf9972be7e7c43e5364fad3/b41e7/dont-look-up.png 1920w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/f76d24528bf9972be7e7c43e5364fad3/b4918/dont-look-up.png&quot;
            alt=&quot;dont look up&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;my-favorite-lines&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#my-favorite-lines&quot; aria-label=&quot;my favorite lines permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;My Favorite Lines&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;OK. At this very moment, I say we sit tight and assess.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The divisions in this country are bad enough. We don’t want more of that in our house.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Sometimes we need to just be able to say things to one another. We need to hear things.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Just look up, turn off the shitbox news.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I’m gratefull we tried.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;We really did have everything, didn’t we?&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[Fix DSM 7.01 can't hibernate on Xpenology]]></title><description><![CDATA[After upgrading to DSM 7.0.1, I found the NAS couldn't auto poweroff caused by disks waking up frequently. I'm gonna try to figure it out.]]></description><link>https://www.berlinchan.com/2021/12/fix-DSM-7-cant-hibernation-on-xpenology</link><guid isPermaLink="false">https://www.berlinchan.com/2021/12/fix-DSM-7-cant-hibernation-on-xpenology</guid><pubDate>Wed, 29 Dec 2021 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;I have a J3455-itx motherboard based NAS installed with Xpenology. Recently, I tried to upgrade the DSM from 6.2.3 to 7.0.1-42218 using the RedPill loader. After upgrading, I found the NAS couldn’t auto poweroff which I enabled the auto poweroff time to 4 hours. I have checked the log, found the disks have been waking up almost immediately just after hibernation. So, I’m gonna try to figure it out.&lt;/p&gt;
&lt;h2 id=&quot;upgrade-xpenology-to-dsm-701-42218&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#upgrade-xpenology-to-dsm-701-42218&quot; aria-label=&quot;upgrade xpenology to dsm 701 42218 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Upgrade Xpenology to DSM 7.0.1-42218&lt;/h2&gt;
&lt;p&gt;Operated following this tutorial, &lt;a href=&quot;https://wp.gxnas.com/11305.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;黑群晖升级DSM7的教程（黑群晖DS918-6.23升级到DS918-7.01保姆级教程）&lt;/a&gt;. I choose Retain system configurations(Keep the files and settings).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3ae8a3c4e0169036505018a6f4c2a031/b3681/retain-system-configurations.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABk0lEQVR42n1RS24UMRDti3EALsAB4CycACEhsWPFCsSeBTuUrCEEZghMMpn2p90zbn/KdtvllNMZglDE0+vq59eucnW5k1L2jF/3bHO5ZVz0Pbve9XNKeV7QRMmZ4kJy6EHEWmsXUjY+xpggJAPJNkbtApmTI4bJRzJJkKktEUhAwlxqN3pkpn7l+WJfBleELdJhi5Y0Mlu5rcygcFX5e3KLymGnoXyX89P308sTx+V4/rv/uZWrDVtdih87sxZwIfyKw2YAZYLQIHWQUxh9HgG7PeCVLq9O4eM6DdpfSbMdzE7ZXy2N9sU7mkRR3FJOSblCLXc6tFbngiYV5XGESiWPEWkHcXALy0D6dilcocSuVqTMD9/imZgxRw+BpgcAIQQfi0vo2mzabP8BDbwlH6A8en14/skVmJhQ+4OWgxrUKPZ2mID+ZbKw3A2B6iykih1iKQXP5dybTIVIF7xD+9BW9M5Hj0rg/clHq+IDqH+xgU5bq/mMz1/4zEzufMI/nfwHS9cm4rN305O3+vGbw4vP/gaNEXPX6XIcaAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/3ae8a3c4e0169036505018a6f4c2a031/5ed05/retain-system-configurations.webp 190w,
/static/3ae8a3c4e0169036505018a6f4c2a031/9d76b/retain-system-configurations.webp 380w,
/static/3ae8a3c4e0169036505018a6f4c2a031/33466/retain-system-configurations.webp 760w,
/static/3ae8a3c4e0169036505018a6f4c2a031/cd0a5/retain-system-configurations.webp 974w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/3ae8a3c4e0169036505018a6f4c2a031/253c6/retain-system-configurations.png 190w,
/static/3ae8a3c4e0169036505018a6f4c2a031/810ee/retain-system-configurations.png 380w,
/static/3ae8a3c4e0169036505018a6f4c2a031/b4918/retain-system-configurations.png 760w,
/static/3ae8a3c4e0169036505018a6f4c2a031/b3681/retain-system-configurations.png 974w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/3ae8a3c4e0169036505018a6f4c2a031/b4918/retain-system-configurations.png&quot;
            alt=&quot;retain-system-configurations&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;fix-disks-wake-up-frequently&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fix-disks-wake-up-frequently&quot; aria-label=&quot;fix disks wake up frequently permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Fix disks wake up frequently&lt;/h2&gt;
&lt;p&gt;Searched on the Internet, in post &lt;a href=&quot;http://www.gebi1.com/thread-300625-1-1.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PVE直通SATA控制器硬盘无法休眠！&lt;/a&gt; comments, I found something useful. Quoted some parts of it here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;解决办法2：&lt;/p&gt;
&lt;p&gt;修改 /etc.defaults/syslog-ng/patterndb.d/message.conf 和 /etc.defaults/syslog-ngpatterndb.d/kernel.conf 文件，把 /var/log/messages 和 /var/log/kern.log 日志文件分都设置到 /tmp/messages 和 /tmp/kern.log ，然后重启系统即可。&lt;/p&gt;
&lt;p&gt;最好也把 /etc.defaults/logrotate.d/syslog-ng 里的 messages 和 kern.log 路径也做相应修改，否则这两个日志文件的大小可能会无休止的增长。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I did these operates followed the comment and inspired by this comment, I continue to analyze the &lt;code class=&quot;language-text&quot;&gt;/var/log/hibernationFull.log&lt;/code&gt;, to find what interrupted the hibernation:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20078.480642&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;smart_shim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;684&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Expected to copy HDIO_DRIVE_CMD header of &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; bytes from           &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; it failed
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20078.492686&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;smart_shim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;684&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Expected to copy HDIO_DRIVE_CMD header of &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; bytes from           &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; it failed
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20079.725404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;override_symbol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;239&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Obtaining lock for &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;GetHwCapability&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x110&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;apollolake_synobios&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token file-path string&quot;&gt;/ffffffffa0256490&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20079.737718&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;override_symbol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;246&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Writing original code to &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ffffffffa0256490&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20079.746002&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;override_symbol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;239&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Released lock for &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ffffffffa0256490&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20079.753621&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;override_symbol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;207&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Obtaining lock for &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;GetHwCapability&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x110&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;apollolake_synobios&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token file-path string&quot;&gt;/ffffffffa0256490&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20079.765761&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;override_symbol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;217&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Writing trampoline code to &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ffffffffa0256490&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20079.774277&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;override_symbol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;207&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Released lock for &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ffffffffa0256490&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20079.782018&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bios_hwcap_shim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; proxying GetHwCapability&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;support &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; real&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;org_fout&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ovs_fout&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20079.944380&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;pmu_shim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;310&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Got &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; bytes from PMU&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; reason&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; hex&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2d&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt; ascii&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20080.094384&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;pmu_shim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;310&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Got &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; bytes from PMU&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; reason&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; hex&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;37&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;}&lt;/span&gt; ascii&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;7&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20080.102730&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;pmu_shim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;239&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Executing cmd OUT_STATUS_LED_OFF handler cmd_shim_noop&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x31&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;redpill&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20080.113113&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;pmu_shim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; vPMU received OUT_STATUS_LED_OFF using &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; bytes &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; NOOP
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20140.212198&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;redpill&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bios_shims_collection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; mfgBIOS&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; nullify zero&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;int for VTK_SET_HDD_ACT_LED
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20616.112182&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14337&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syslog&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ng&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;43169&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;ovs&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vswitchd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20616.121477&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14337&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syslog&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ng&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;43169&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;ovs&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vswitchd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20616.130693&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14337&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syslog&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ng&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;43169&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;ovs&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vswitchd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Same as before, in file &lt;code class=&quot;language-text&quot;&gt;/etc.defaults/syslog-ng/patterndb.d/openvswitch.conf&lt;/code&gt;, edit &lt;code class=&quot;language-text&quot;&gt;/var/log/openvswitch/ovs-vswitchd.log&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;/tmp/log/openvswitch/ovs-vswitchd.log&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then, hibernation time last longer for more than 2 hours,&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/95595564a34af677ca481387f39e865b/6bdb5/synology-log.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 7.894736842105264%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAW0lEQVR42hWMSRKAMBAC/f87XWZPMtEcHbGKAwUNW7TOqm9VH8lq6m4RMDnv/aCRs6rutUhUzPVXWDR4MJtHu0jqHw9iFUXQzSNznhfjAtXzLDAsRixgcIExiX62qm/V0LAyXgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/95595564a34af677ca481387f39e865b/5ed05/synology-log.webp 190w,
/static/95595564a34af677ca481387f39e865b/9d76b/synology-log.webp 380w,
/static/95595564a34af677ca481387f39e865b/33466/synology-log.webp 760w,
/static/95595564a34af677ca481387f39e865b/ca244/synology-log.webp 1140w,
/static/95595564a34af677ca481387f39e865b/8c1a9/synology-log.webp 1496w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/95595564a34af677ca481387f39e865b/253c6/synology-log.png 190w,
/static/95595564a34af677ca481387f39e865b/810ee/synology-log.png 380w,
/static/95595564a34af677ca481387f39e865b/b4918/synology-log.png 760w,
/static/95595564a34af677ca481387f39e865b/c0204/synology-log.png 1140w,
/static/95595564a34af677ca481387f39e865b/6bdb5/synology-log.png 1496w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/95595564a34af677ca481387f39e865b/b4918/synology-log.png&quot;
            alt=&quot;synology log&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18650.562808&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1416&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syslog&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ng&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;30459&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;scemd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18650.571354&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1416&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syslog&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ng&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;30459&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;scemd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18650.579863&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1416&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syslog&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ng&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;30459&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;scemd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18656.121265&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;kthreadd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5185&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;jbd2&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;md0&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; WRITE block &lt;span class=&quot;token number&quot;&gt;2136272&lt;/span&gt; on md0 &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; sectors&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18656.130018&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;kthreadd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2746&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;kworker&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; READ block &lt;span class=&quot;token number&quot;&gt;4980352&lt;/span&gt; on sda1 &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; sectors&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18656.138904&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;kthreadd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2746&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;kworker&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; READ block &lt;span class=&quot;token number&quot;&gt;4980352&lt;/span&gt; on sdb1 &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; sectors&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18656.138955&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;ata1:&lt;/span&gt; wake up from deepsleep&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reset link now
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18656.154211&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;kthreadd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4804&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;md0_raid1&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; WRITE block &lt;span class=&quot;token number&quot;&gt;4980352&lt;/span&gt; on sda1 &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; sectors&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18656.162927&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;kthreadd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4804&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;md0_raid1&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; WRITE block &lt;span class=&quot;token number&quot;&gt;4980352&lt;/span&gt; on sdb1 &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; sectors&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18657.923813&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7042&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syno_hibernatio&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;33099&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;hibernation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18657.933470&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7042&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syno_hibernatio&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;33099&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;hibernation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18657.943076&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7042&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syno_hibernatio&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;33099&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;hibernation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18657.953053&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7042&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syno_hibernatio&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;32851&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;hibernationFull&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18657.963047&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7042&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syno_hibernatio&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;32851&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;hibernationFull&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18657.972997&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; ppid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;systemd&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7042&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;syno_hibernatio&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dirtied inode &lt;span class=&quot;token number&quot;&gt;32851&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;hibernationFull&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt; on md0
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18659.139845&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;ata1:&lt;/span&gt; wake up successful&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; the reset fail can be ignored
uptime &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18656.162927&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token separator comment&quot;&gt;======&lt;/span&gt;Idle &lt;span class=&quot;token number&quot;&gt;7636&lt;/span&gt; seconds&lt;span class=&quot;token separator comment&quot;&gt;======&lt;/span&gt;
&lt;span class=&quot;token separator comment&quot;&gt;***&lt;/span&gt; &lt;span class=&quot;token separator comment&quot;&gt;***&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token time number&quot;&gt;15:27:37&lt;/span&gt; CST &lt;span class=&quot;token number&quot;&gt;2021&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In file &lt;code class=&quot;language-text&quot;&gt;/etc.defaults/syslog-ng/patterndb.d/scemd.conf&lt;/code&gt;, edit &lt;code class=&quot;language-text&quot;&gt;/var/log/scemd.log&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;/tmp/log/scemd.log&lt;/code&gt;. And in file &lt;code class=&quot;language-text&quot;&gt;/etc.defaults/syslog-ng/patterndb.d/synocrond.conf&lt;/code&gt;, edit &lt;code class=&quot;language-text&quot;&gt;/var/log/synocrond.log&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;/tmp/log/synocrond.log&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When you change the log destination, don’t forget to change the logrotate config correspondently under the path &lt;code class=&quot;language-text&quot;&gt;/etc.defaults/logrotate.d&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Now, the disks can hibernate without being woken up frequently, and the NAS can auto poweroff.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/706de2e9e52bcab895b1534c01e58231/7480a/log-auto-poweroff.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.947368421052634%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAmElEQVR42iWO7RICIQhFe//HrLZVED/Q9teWHbcZZIDDxXubc7bmZuU8T0kWNSXL7t1yoYX2MRgSCpWVQVG0j/cNXJunS8xo5yWr7mqZaon74IpcRyn+WdTYX+JSKidpQtQQ5RUkXUsxKhQXz1fY9rCHuBZE748tlwpaYu+j1Pr5fPFDYKR5L61RQ4/jwKjlDCH4CRdI5pw/S9XhQaVxwfUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/706de2e9e52bcab895b1534c01e58231/5ed05/log-auto-poweroff.webp 190w,
/static/706de2e9e52bcab895b1534c01e58231/9d76b/log-auto-poweroff.webp 380w,
/static/706de2e9e52bcab895b1534c01e58231/33466/log-auto-poweroff.webp 760w,
/static/706de2e9e52bcab895b1534c01e58231/ca244/log-auto-poweroff.webp 1140w,
/static/706de2e9e52bcab895b1534c01e58231/b0196/log-auto-poweroff.webp 1520w,
/static/706de2e9e52bcab895b1534c01e58231/c8a47/log-auto-poweroff.webp 1550w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/706de2e9e52bcab895b1534c01e58231/253c6/log-auto-poweroff.png 190w,
/static/706de2e9e52bcab895b1534c01e58231/810ee/log-auto-poweroff.png 380w,
/static/706de2e9e52bcab895b1534c01e58231/b4918/log-auto-poweroff.png 760w,
/static/706de2e9e52bcab895b1534c01e58231/c0204/log-auto-poweroff.png 1140w,
/static/706de2e9e52bcab895b1534c01e58231/54dc9/log-auto-poweroff.png 1520w,
/static/706de2e9e52bcab895b1534c01e58231/7480a/log-auto-poweroff.png 1550w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/706de2e9e52bcab895b1534c01e58231/b4918/log-auto-poweroff.png&quot;
            alt=&quot;log auto poweroff&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;problems&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problems&quot; aria-label=&quot;problems permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problems&lt;/h2&gt;
&lt;p&gt;The RedPill loader is not yet stable. In my case, I found some problems.&lt;/p&gt;
&lt;h3 id=&quot;synology-drive-is-indexing-over-and-over-constantly&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#synology-drive-is-indexing-over-and-over-constantly&quot; aria-label=&quot;synology drive is indexing over and over constantly permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Synology Drive is indexing over and over constantly&lt;/h3&gt;
&lt;p&gt;There is a Reddit post that discussed this problem, &lt;a href=&quot;https://www.reddit.com/r/synology/comments/pcu4x5/synology_drive_is_indexing_over_and_over/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Synology Drive is indexing over and over constantly&lt;/a&gt;. So I uninstall the Synology Drive Package.&lt;/p&gt;
&lt;h3 id=&quot;process-synoscgi-cpu-usage-100&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#process-synoscgi-cpu-usage-100&quot; aria-label=&quot;process synoscgi cpu usage 100 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Process &lt;code class=&quot;language-text&quot;&gt;synoscgi&lt;/code&gt; cpu usage 100%&lt;/h3&gt;
&lt;p&gt;Sometimes the process &lt;code class=&quot;language-text&quot;&gt;synoscgi&lt;/code&gt; cpu usage 100% and froze the system. I heard some discussion that this problem was caused by docker, so I uninstalled the Docker package.&lt;/p&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://wp.gxnas.com/11305.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;黑群晖升级DSM7的教程（黑群晖DS918-6.23升级到DS918-7.01保姆级教程）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bugxia.com/1498.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;群晖硬盘不断唤醒（无法休眠）的一些折腾&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.gebi1.com/thread-300625-1-1.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PVE直通SATA控制器硬盘无法休眠！&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hexianjie.cn/archives/610&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;黑群晖7.0修复photos中视频不生成缩略图的问题&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://xpenology.com/forum/topic/7004-tutorial-how-to-access-dsms-data-system-partitions/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tutorial: How to access DSM’s Data &amp;#x26; System partitions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kb.synology.com/en-global/DSM/tutorial/How_can_I_recover_data_from_my_DiskStation_using_a_PC&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;How can I use a PC to recover data when my Synology NAS malfunctions?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Two WesternDigital HDDs retire after 10-year occupation]]></title><description><![CDATA[I have two Western Digital HDDs, one is Caviar Black and another is Caviar Green serial, both capacities are 1TB. Because uncorrectable sectors occurred, it’s time for them to retire.]]></description><link>https://www.berlinchan.com/2021/09/two-western-digital-hdd-retire</link><guid isPermaLink="false">https://www.berlinchan.com/2021/09/two-western-digital-hdd-retire</guid><pubDate>Thu, 02 Sep 2021 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;I have two Western Digital HDDs, one is Caviar Black and another is Caviar Green serial, both capacities are 1TB. Because &lt;strong&gt;uncorrectable sectors&lt;/strong&gt; occurred, it’s time for them to retire.&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 580px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/466943ae014a55e9612ab1b1be12d83f/7b21a/western-digital-hdd-black-1t-sector-scan.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 81.57894736842105%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAAsTAAALEwEAmpwYAAADMklEQVR42j2T2VMiVxjF+c/zMlEcgmtERDApTWZJJVqOiIqsMgITDAM0dNs0fZemm15vr/TC5CVNJpmqU7fuy++e+s53TwJDTI8n1JBiaAbykBkzowE1GU2owZimaGowGg8pngW+4y/tZXx+U+AGCchB6jM16o8EOF8IEjFIGIS+59uGJSDBNKwoCFVJncO5a3krfxUto29KBK7/9VUJi5IgYiyYpiWLik0cwAE0Q/wUQA5gHmMgCECwdCvGYtvQCxO+HQTOKnDCwA5DJzIWurXQPNX0NMtRiKuatmyoWFKQqEqKZ3lRbO6GMbmGIT99ods0+5mZDkfMpyFL8cTkTSsWMAmwXVrVmtSoMR7rGvFMNx5BJoTEd3uZ+EudZriLHK5nUeMQ3e/j1mvYOoKVQ1jdh49Zrng8K+2h2ikoL0zTMxxFUsU4GI0sbT+G2QNQ3UC9JN/Zgt0k/rTNV3Kz2yNYPoT1PHt5wl1t83d5rqjY1t/+F13RRV3UZG2ddkfDr0Bna97NP138Nn6fFSuF1vlB78Mevj+dXZ0N32ZBKQWfUqin2E7o+IZqGMbC1M31zB2NT3E3J7PrFGhug4cCuD3lSyd86QC1kqibBJ3vhd4uauzBuuR4obVURMXQ5jax13BP5Xan1/nJ5R6o7oDyEShnUP0QV3dQOwlaeaGaG96cMaXfwTUx9Xg18nzh6v87j/VpnvlwWj1/x7wp8Fdb8DHNt1JsKw0/pkHjZ1QuDK7Pxje/ciViG5Eb6bLmqMj56kxLgx9mt5v4eRP14tjSqH0+fJN9+qPA3+3j5jvu5uz54pf+VWZSkYm68lamRtzF1CVO6EWJvsy+4pvHsPETKGZAKQerP8J6Bt7l+NIxdZmDlSNY20B/plEPyHLkBvFXMRHtmv/CXQ19h1pp1NiG96/hwy6s7IBKCt6nwEOSLW+B2iZobqBmGtVkx10SRxUVAtF/zoaGqly7DIZV0K+AQYN9ak87FditiS8NXWyouC7j4rRffHnWiGXqZI4EhebWcFwMQzYWM3E4639kH9vsI+BmCpQVKFkLI9AdX3eWqiWwADCcLutREMWdi6IvcTdCL/wHj5wjUhJta5oAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/466943ae014a55e9612ab1b1be12d83f/5ed05/western-digital-hdd-black-1t-sector-scan.webp 190w,
/static/466943ae014a55e9612ab1b1be12d83f/9d76b/western-digital-hdd-black-1t-sector-scan.webp 380w,
/static/466943ae014a55e9612ab1b1be12d83f/929d0/western-digital-hdd-black-1t-sector-scan.webp 580w&quot;
              sizes=&quot;(max-width: 580px) 100vw, 580px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/466943ae014a55e9612ab1b1be12d83f/253c6/western-digital-hdd-black-1t-sector-scan.png 190w,
/static/466943ae014a55e9612ab1b1be12d83f/810ee/western-digital-hdd-black-1t-sector-scan.png 380w,
/static/466943ae014a55e9612ab1b1be12d83f/7b21a/western-digital-hdd-black-1t-sector-scan.png 580w&quot;
            sizes=&quot;(max-width: 580px) 100vw, 580px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/466943ae014a55e9612ab1b1be12d83f/7b21a/western-digital-hdd-black-1t-sector-scan.png&quot;
            alt=&quot;western digital hdd black 1t sector scan&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;These two hard disks &lt;a href=&quot;/2011/01/harddisk/&quot;&gt;have been bought in 2010&lt;/a&gt;, which was for my third computer, also the first self-assemble PC. In this ten-year occupation, they’re mostly idle, because their &lt;strong&gt;Power On Hours count is just about 7000+ hours&lt;/strong&gt;. I didn’t find the MTBF(mean time between failures) specification about the product serial, but I don’t think it should break down in so much short time using, or maybe just BAD luck.&lt;/p&gt;
&lt;p&gt;It almost ruined a very important video of mine, which scared me badly.&lt;/p&gt;
&lt;p&gt;I have a strong impression of a BBS post before I’ve decided to buy the Caviar Black 1TB disk, it said “the data stored on the Black disk could never lose.” Haha, it’s hard to say NEVER.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/19bb4d111be4436804f2cfdfaa1dd3da/ca9b4/two-western-digital-hdd.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.26315789473684%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAwAE/8QAFgEBAQEAAAAAAAAAAAAAAAAAAgAB/9oADAMBAAIQAxAAAAHQIEbVHYv/xAAZEAEBAQEBAQAAAAAAAAAAAAABAgAhETH/2gAIAQEAAQUC66O74rQlMs+1v//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAEDAQE/AYf/xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPwGn/8QAGhAAAwADAQAAAAAAAAAAAAAAAAERITEygf/aAAgBAQAGPwLqeGxow4OFP//EABoQAQACAwEAAAAAAAAAAAAAAAEAESExQVH/2gAIAQEAAT8hw4A8jYWitpdMdlBb1FborMqL6z//2gAMAwEAAgADAAAAEM8P/8QAFREBAQAAAAAAAAAAAAAAAAAAAQD/2gAIAQMBAT8QVA3/xAAWEQEBAQAAAAAAAAAAAAAAAAARAAH/2gAIAQIBAT8QBOX/xAAdEAEAAgICAwAAAAAAAAAAAAABESEAMUGBYXGR/9oACAEBAAE/EBIapQszd4RlCQQuq+8ZJLTl5rJk0wg+nvDqlCS35xlpLtdZ/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/19bb4d111be4436804f2cfdfaa1dd3da/5ed05/two-western-digital-hdd.webp 190w,
/static/19bb4d111be4436804f2cfdfaa1dd3da/9d76b/two-western-digital-hdd.webp 380w,
/static/19bb4d111be4436804f2cfdfaa1dd3da/33466/two-western-digital-hdd.webp 760w,
/static/19bb4d111be4436804f2cfdfaa1dd3da/ca244/two-western-digital-hdd.webp 1140w,
/static/19bb4d111be4436804f2cfdfaa1dd3da/e9f1f/two-western-digital-hdd.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/19bb4d111be4436804f2cfdfaa1dd3da/89129/two-western-digital-hdd.jpg 190w,
/static/19bb4d111be4436804f2cfdfaa1dd3da/0036d/two-western-digital-hdd.jpg 380w,
/static/19bb4d111be4436804f2cfdfaa1dd3da/e484a/two-western-digital-hdd.jpg 760w,
/static/19bb4d111be4436804f2cfdfaa1dd3da/32d87/two-western-digital-hdd.jpg 1140w,
/static/19bb4d111be4436804f2cfdfaa1dd3da/ca9b4/two-western-digital-hdd.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/19bb4d111be4436804f2cfdfaa1dd3da/e484a/two-western-digital-hdd.jpg&quot;
            alt=&quot;two western digital hdd&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;With these two HDDs in use, I mostly use them to do some photography work, video editing, and 3D animation. I’ve moved important data to a new NAS with 2 Seagate 8T disks. How do they doing? We’ll see.&lt;/p&gt;
&lt;p&gt;And also it’s time to say goodbye to the old PC, thanks for working alongside me for so many years, from Yichang to Wuhan!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 750px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6e9ddf5b2e76149acbda8433a0819e8b/c2ed8/pc-case.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 133.1578947368421%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAAFA//EABUBAQEAAAAAAAAAAAAAAAAAAAEA/9oADAMBAAIQAxAAAAF3IpY0rjCrD3gi+pv/xAAcEAACAgIDAAAAAAAAAAAAAAABAgMSACIRMTL/2gAIAQEAAQUCFTipoa8mOrF3yOM0mFgfajU9MBYZ/8QAFhEBAQEAAAAAAAAAAAAAAAAAEQAQ/9oACAEDAQE/ASN//8QAFhEAAwAAAAAAAAAAAAAAAAAAAREg/9oACAECAQE/AWI//8QAHhAAAgEDBQAAAAAAAAAAAAAAAAIBEBExEiAhUZH/2gAIAQEABj8CZZyKcuvp0abkYpYjZ//EABkQAQEBAQEBAAAAAAAAAAAAAAERACExUf/aAAgBAQABPyGdHlJdNbTmAUD8nNwITP68/Mh4qXppM9PM1UK6AfDdd6Az3eN//9oADAMBAAIAAwAAABA/8v3/xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8Q2SGz/8QAGBEAAwEBAAAAAAAAAAAAAAAAAAEREDH/2gAIAQIBAT8Qp0o3M//EABwQAQADAAMBAQAAAAAAAAAAAAEAESExQVFhof/aAAgBAQABPxC1QPUHMT2EELLjjh7AS1NrCNCrUB4ZpIM9LjERYbnfsJjrmR/dweP2UX0n5B6MmYbpb7s45//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/6e9ddf5b2e76149acbda8433a0819e8b/5ed05/pc-case.webp 190w,
/static/6e9ddf5b2e76149acbda8433a0819e8b/9d76b/pc-case.webp 380w,
/static/6e9ddf5b2e76149acbda8433a0819e8b/f5bde/pc-case.webp 750w&quot;
              sizes=&quot;(max-width: 750px) 100vw, 750px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/6e9ddf5b2e76149acbda8433a0819e8b/89129/pc-case.jpg 190w,
/static/6e9ddf5b2e76149acbda8433a0819e8b/0036d/pc-case.jpg 380w,
/static/6e9ddf5b2e76149acbda8433a0819e8b/c2ed8/pc-case.jpg 750w&quot;
            sizes=&quot;(max-width: 750px) 100vw, 750px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/6e9ddf5b2e76149acbda8433a0819e8b/c2ed8/pc-case.jpg&quot;
            alt=&quot;pc case&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;pc-specifications&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pc-specifications&quot; aria-label=&quot;pc specifications permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PC Specifications&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Mother Board : Gigabyte P55-UD3R&lt;/li&gt;
&lt;li&gt;CPU : Intel Core i5 750&lt;/li&gt;
&lt;li&gt;RAM : 12GB DDR3 1600&lt;/li&gt;
&lt;li&gt;Graphics Card : Nvidia Quadro 2000&lt;/li&gt;
&lt;li&gt;Optical Drive : ASUS DVD RW&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;hdd-specifications&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hdd-specifications&quot; aria-label=&quot;hdd specifications permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;HDD Specifications&lt;/h2&gt;
&lt;h3 id=&quot;wdc-wd1002faex-00z3a0&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#wdc-wd1002faex-00z3a0&quot; aria-label=&quot;wdc wd1002faex 00z3a0 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WDC WD1002FAEX-00Z3A0&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Model : WDC WD1002FAEX-00Z3A0&lt;/li&gt;
&lt;li&gt;Firmware : 05.01D05&lt;/li&gt;
&lt;li&gt;Serial Number : WD-WCATR4700830&lt;/li&gt;
&lt;li&gt;Disk Size : 1000.2 GB (8.4/137.4/1000.2/1000.2)&lt;/li&gt;
&lt;li&gt;Queue Depth : 32&lt;/li&gt;
&lt;li&gt;Number of Sectors : 1953525168&lt;/li&gt;
&lt;li&gt;Interface : Serial ATA&lt;/li&gt;
&lt;li&gt;Major Version : ATA8-ACS&lt;/li&gt;
&lt;li&gt;Transfer Mode : SATA/600&lt;/li&gt;
&lt;li&gt;Power On Hours : 7728 hours&lt;/li&gt;
&lt;li&gt;Power On Count : 2528 count&lt;/li&gt;
&lt;li&gt;Health Status : Caution&lt;/li&gt;
&lt;li&gt;Features : S.M.A.R.T., AAM, NCQ&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;smart&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#smart&quot; aria-label=&quot;smart permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;S.M.A.R.T.&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;Cur&lt;/th&gt;
&lt;th&gt;Wor&lt;/th&gt;
&lt;th&gt;Thr&lt;/th&gt;
&lt;th&gt;RawValues&lt;/th&gt;
&lt;th&gt;Attribute Name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;198&lt;/td&gt;
&lt;td&gt;198&lt;/td&gt;
&lt;td&gt;_51&lt;/td&gt;
&lt;td&gt;0000000102CB&lt;/td&gt;
&lt;td&gt;Read Error Rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03&lt;/td&gt;
&lt;td&gt;209&lt;/td&gt;
&lt;td&gt;164&lt;/td&gt;
&lt;td&gt;_21&lt;/td&gt;
&lt;td&gt;0000000009E5&lt;/td&gt;
&lt;td&gt;Spin-Up Time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;04&lt;/td&gt;
&lt;td&gt;_98&lt;/td&gt;
&lt;td&gt;_98&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000A19&lt;/td&gt;
&lt;td&gt;Start/Stop Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;05&lt;/td&gt;
&lt;td&gt;191&lt;/td&gt;
&lt;td&gt;191&lt;/td&gt;
&lt;td&gt;140&lt;/td&gt;
&lt;td&gt;000000000041&lt;/td&gt;
&lt;td&gt;Reallocated Sectors Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;07&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;Seek Error Rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09&lt;/td&gt;
&lt;td&gt;_90&lt;/td&gt;
&lt;td&gt;_90&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000001E30&lt;/td&gt;
&lt;td&gt;Power-On Hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0A&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;Spin Retry Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0B&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;Recalibration Retries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0C&lt;/td&gt;
&lt;td&gt;_98&lt;/td&gt;
&lt;td&gt;_98&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;0000000009E0&lt;/td&gt;
&lt;td&gt;Power Cycle Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C0&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000172&lt;/td&gt;
&lt;td&gt;Power-off Retract Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C1&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;0000000008A6&lt;/td&gt;
&lt;td&gt;Load/Unload Cycle Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C2&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;_95&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;00000000002B&lt;/td&gt;
&lt;td&gt;Temperature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C4&lt;/td&gt;
&lt;td&gt;141&lt;/td&gt;
&lt;td&gt;141&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;00000000003B&lt;/td&gt;
&lt;td&gt;Reallocation Event Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C5&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000001&lt;/td&gt;
&lt;td&gt;Current Pending Sector Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C6&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;00000000005A&lt;/td&gt;
&lt;td&gt;Uncorrectable Sector Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C7&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;UltraDMA CRC Error Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C8&lt;/td&gt;
&lt;td&gt;_61&lt;/td&gt;
&lt;td&gt;__1&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000006D39&lt;/td&gt;
&lt;td&gt;Write Error Rate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;wdc-wd10ears-00y5b1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#wdc-wd10ears-00y5b1&quot; aria-label=&quot;wdc wd10ears 00y5b1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WDC WD10EARS-00Y5B1&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Model : WDC WD10EARS-00Y5B1&lt;/li&gt;
&lt;li&gt;Firmware : 80.00A80&lt;/li&gt;
&lt;li&gt;Serial Number : WD-WCAV59391908&lt;/li&gt;
&lt;li&gt;Disk Size : 1000.2 GB (8.4/137.4/1000.2/----)&lt;/li&gt;
&lt;li&gt;Queue Depth : 32&lt;/li&gt;
&lt;li&gt;Number of Sectors : 1953525168&lt;/li&gt;
&lt;li&gt;Interface : Serial ATA&lt;/li&gt;
&lt;li&gt;Major Version : ATA8-ACS&lt;/li&gt;
&lt;li&gt;Transfer Mode : SATA/300&lt;/li&gt;
&lt;li&gt;Power On Hours : 7516 hours&lt;/li&gt;
&lt;li&gt;Power On Count : 2414 count&lt;/li&gt;
&lt;li&gt;Health Status : Caution&lt;/li&gt;
&lt;li&gt;Features : S.M.A.R.T., AAM, NCQ&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;smart-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#smart-1&quot; aria-label=&quot;smart 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;S.M.A.R.T.&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;Cur&lt;/th&gt;
&lt;th&gt;Wor&lt;/th&gt;
&lt;th&gt;Thr&lt;/th&gt;
&lt;th&gt;RawValues&lt;/th&gt;
&lt;th&gt;Attribute Name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;197&lt;/td&gt;
&lt;td&gt;_51&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;Read Error Rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03&lt;/td&gt;
&lt;td&gt;180&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;_21&lt;/td&gt;
&lt;td&gt;000000000F7E&lt;/td&gt;
&lt;td&gt;Spin-Up Time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;04&lt;/td&gt;
&lt;td&gt;_96&lt;/td&gt;
&lt;td&gt;_96&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;0000000012F2&lt;/td&gt;
&lt;td&gt;Start/Stop Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;05&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;140&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;Reallocated Sectors Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;07&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;Seek Error Rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09&lt;/td&gt;
&lt;td&gt;_90&lt;/td&gt;
&lt;td&gt;_90&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000001D5C&lt;/td&gt;
&lt;td&gt;Power-On Hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0A&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;Spin Retry Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0B&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;Recalibration Retries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0C&lt;/td&gt;
&lt;td&gt;_98&lt;/td&gt;
&lt;td&gt;_98&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;00000000096E&lt;/td&gt;
&lt;td&gt;Power Cycle Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C0&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;00000000013D&lt;/td&gt;
&lt;td&gt;Power-off Retract Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C1&lt;/td&gt;
&lt;td&gt;172&lt;/td&gt;
&lt;td&gt;172&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000014D52&lt;/td&gt;
&lt;td&gt;Load/Unload Cycle Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C2&lt;/td&gt;
&lt;td&gt;108&lt;/td&gt;
&lt;td&gt;_94&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000027&lt;/td&gt;
&lt;td&gt;Temperature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C4&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;Reallocation Event Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C5&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;197&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000003&lt;/td&gt;
&lt;td&gt;Current Pending Sector Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C6&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;198&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;Uncorrectable Sector Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C7&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000000&lt;/td&gt;
&lt;td&gt;UltraDMA CRC Error Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C8&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;196&lt;/td&gt;
&lt;td&gt;__0&lt;/td&gt;
&lt;td&gt;000000000001&lt;/td&gt;
&lt;td&gt;Write Error Rate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</content:encoded></item><item><title><![CDATA[Netgear R6300v2 安装外置天线]]></title><description><![CDATA[问题 Wi-Fi 放在金属弱电箱内，隔一堵墙 6m 远就连不上，房间面积小没必要用 AC+AP，也不想接中继或者 Mesh 而到处扯线，于是把 R6300v2 的内置天线延长外接出来。]]></description><link>https://www.berlinchan.com/2021/08/extend-netgear-r6300v2-antenna</link><guid isPermaLink="false">https://www.berlinchan.com/2021/08/extend-netgear-r6300v2-antenna</guid><pubDate>Mon, 23 Aug 2021 10:46:37 GMT</pubDate><content:encoded>&lt;h2 id=&quot;问题&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E9%97%AE%E9%A2%98&quot; aria-label=&quot;问题 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;问题&lt;/h2&gt;
&lt;p&gt;Wi-Fi 放在金属弱电箱内，隔一堵墙 6m 远就连不上，房间面积小没必要用 AC+AP，也不想接中继或者 Mesh 而到处扯线，于是把 R6300v2 的内置天线延长外接出来。&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;施工&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%96%BD%E5%B7%A5&quot; aria-label=&quot;施工 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;施工&lt;/h2&gt;
&lt;p&gt;房间除去公摊实际面积 68m2，弱电箱状况如下图，内含光猫、软路由、路由器、门禁设备，图中路由器是 Netgear WNDR4500，临时拿来用的。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6d6baae0719d6fd6ae180de7ca409170/ca9b4/PXL_20210821_030154306.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.26315789473684%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQFAgP/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAL/2gAMAwEAAhADEAAAAWp7HHLRNkf/xAAbEAADAAIDAAAAAAAAAAAAAAABAgMAEgQRIf/aAAgBAQABBQLc9vyaZAkxRvXgGVBqn//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/AUn/xAAWEQEBAQAAAAAAAAAAAAAAAAABECH/2gAIAQIBAT8BHJ//xAAcEAACAgIDAAAAAAAAAAAAAAAAAQIRIjFBUYH/2gAIAQEABj8C00Sy5IuTts8MXViXR//EABsQAQEBAQADAQAAAAAAAAAAAAERADEhUWGh/9oACAEBAAE/IRkcHbvSJBDNkBVcF3lP5gy9b55d8DTf/9oADAMBAAIAAwAAABD0/wD/xAAWEQADAAAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8QkV//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPxCw/8QAHRABAAMAAgMBAAAAAAAAAAAAAQARITFhUZGh4f/aAAgBAQABPxAFtFq2lZ9gSuVT8bz6ljooa3KVgoo8fiOgCJpL6Tr9x0T/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/6d6baae0719d6fd6ae180de7ca409170/5ed05/PXL_20210821_030154306.webp 190w,
/static/6d6baae0719d6fd6ae180de7ca409170/9d76b/PXL_20210821_030154306.webp 380w,
/static/6d6baae0719d6fd6ae180de7ca409170/33466/PXL_20210821_030154306.webp 760w,
/static/6d6baae0719d6fd6ae180de7ca409170/ca244/PXL_20210821_030154306.webp 1140w,
/static/6d6baae0719d6fd6ae180de7ca409170/e9f1f/PXL_20210821_030154306.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/6d6baae0719d6fd6ae180de7ca409170/89129/PXL_20210821_030154306.jpg 190w,
/static/6d6baae0719d6fd6ae180de7ca409170/0036d/PXL_20210821_030154306.jpg 380w,
/static/6d6baae0719d6fd6ae180de7ca409170/e484a/PXL_20210821_030154306.jpg 760w,
/static/6d6baae0719d6fd6ae180de7ca409170/32d87/PXL_20210821_030154306.jpg 1140w,
/static/6d6baae0719d6fd6ae180de7ca409170/ca9b4/PXL_20210821_030154306.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/6d6baae0719d6fd6ae180de7ca409170/e484a/PXL_20210821_030154306.jpg&quot;
            alt=&quot;弱电箱&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;R6300v2 拆开来看到 6条天线，天线接口是 IPEX一代，我找了下资料未区分开哪些是 2.4G，哪些是 5G天线，后面接延长天线，只按左右两边分开来交替摆放天线。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d423e4fe9bf719c48ef23fe94d95acc4/ca9b4/PXL_20210821_030555980.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.26315789473684%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDBAX/xAAXAQADAQAAAAAAAAAAAAAAAAAAAQID/9oADAMBAAIQAxAAAAFWjS8uqZAf/8QAGxAAAgIDAQAAAAAAAAAAAAAAAQIAAxESIxP/2gAIAQEAAQUC02ic3W8Y6gViwv4WGf/EABcRAQADAAAAAAAAAAAAAAAAAAACESL/2gAIAQMBAT8Bqbb/xAAXEQEAAwAAAAAAAAAAAAAAAAAAAhEi/9oACAECAQE/Abiy/8QAGxAAAgIDAQAAAAAAAAAAAAAAABEBQSEiMZH/2gAIAQEABj8CcseTkiRtFFen/8QAGxAAAgMBAQEAAAAAAAAAAAAAAREAITFRQZH/2gAIAQEAAT8hCAdyHRiABhbDc1/hKrKPRk8uMsjxNOR//9oADAMBAAIAAwAAABAn7//EABYRAQEBAAAAAAAAAAAAAAAAAAERAP/aAAgBAwEBPxAJtuBFXf/EABcRAQEBAQAAAAAAAAAAAAAAAAEAESH/2gAIAQIBAT8QVMyUvC//xAAbEAEAAwADAQAAAAAAAAAAAAABABEhMUFx0f/aAAgBAQABPxAqAy3LGoVdC2ZGiqmUQIVmofqnQVQsa+Qaw19Cjcn/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/d423e4fe9bf719c48ef23fe94d95acc4/5ed05/PXL_20210821_030555980.webp 190w,
/static/d423e4fe9bf719c48ef23fe94d95acc4/9d76b/PXL_20210821_030555980.webp 380w,
/static/d423e4fe9bf719c48ef23fe94d95acc4/33466/PXL_20210821_030555980.webp 760w,
/static/d423e4fe9bf719c48ef23fe94d95acc4/ca244/PXL_20210821_030555980.webp 1140w,
/static/d423e4fe9bf719c48ef23fe94d95acc4/e9f1f/PXL_20210821_030555980.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/d423e4fe9bf719c48ef23fe94d95acc4/89129/PXL_20210821_030555980.jpg 190w,
/static/d423e4fe9bf719c48ef23fe94d95acc4/0036d/PXL_20210821_030555980.jpg 380w,
/static/d423e4fe9bf719c48ef23fe94d95acc4/e484a/PXL_20210821_030555980.jpg 760w,
/static/d423e4fe9bf719c48ef23fe94d95acc4/32d87/PXL_20210821_030555980.jpg 1140w,
/static/d423e4fe9bf719c48ef23fe94d95acc4/ca9b4/PXL_20210821_030555980.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/d423e4fe9bf719c48ef23fe94d95acc4/e484a/PXL_20210821_030555980.jpg&quot;
            alt=&quot;R6300v2拆机&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;在某宝上买了 6条馈线长 1.5m的内置天线，特性如下：&lt;/p&gt;
&lt;p&gt;电气特性/Electrical Characteristics&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;工作频率/Frequency： 2400&lt;del&gt;2500MHz 4900&lt;/del&gt;5900MHz&lt;/li&gt;
&lt;li&gt;驻波比/S.W.R.： &amp;#x3C;= 1.8&lt;/li&gt;
&lt;li&gt;天线增益/Antenna Gain： 8 dBi&lt;/li&gt;
&lt;li&gt;辐射模式/Radiation： 全方位/Omni-directional&lt;/li&gt;
&lt;li&gt;极化方向/Polarization： 水平极化/Linear&lt;/li&gt;
&lt;li&gt;输入阻抗/Impedance： 50 欧姆&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;物理特性/Material &amp;#x26; Mechanical Characteristics&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;振子材料： PCB板覆铜&lt;/li&gt;
&lt;li&gt;PCB板规格： 95mm&lt;em&gt;13.5mm&lt;/em&gt;0.55mm&lt;/li&gt;
&lt;li&gt;线材材料： RG178镀银高温线&lt;/li&gt;
&lt;li&gt;线材长： 1.5m&lt;/li&gt;
&lt;li&gt;接口方式： IPEX 1代&lt;/li&gt;
&lt;li&gt;工作温度/Operation Temperature： -40℃ ~ +65℃&lt;/li&gt;
&lt;li&gt;储存温度/Storage Temperature： -40℃ ~ +80℃&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不像外置的那种一根棒棒，内置天线尺寸更小。正好弱电箱上有个门禁对讲平板，将天线从暗线线管穿到平板后，天线贴背胶粘在墙上，平板装上去遮挡住不影响整洁美观。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/461c74556d98da0f55212dabfbb68b33/ca9b4/PXL_20210823_084142586.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.26315789473684%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBf/EABcBAAMBAAAAAAAAAAAAAAAAAAABAgP/2gAMAwEAAhADEAAAAXiXFmjEaf/EABoQAAIDAQEAAAAAAAAAAAAAAAECABESBCH/2gAIAQEAAQUC6GNnxVmgxZkxU//EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPwGxj//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/ARn/xAAbEAACAgMBAAAAAAAAAAAAAAAAARFBAhAhUf/aAAgBAQAGPwJY1JELSVKx+nEf/8QAHRABAAICAgMAAAAAAAAAAAAAAQARITFBUWFxgf/aAAgBAQABPyFNosPcdn31mJpy7hyBXh5m630pgsqA6n//2gAMAwEAAgADAAAAEGgv/8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAR/9oACAEDAQE/EE0yFf/EABcRAAMBAAAAAAAAAAAAAAAAAAABESH/2gAIAQIBAT8QRNHD/8QAHBABAAICAwEAAAAAAAAAAAAAAQARITFBUbGB/9oACAEBAAE/EGE1UGznriC9RSW9J7oMsvYXgrRxGtXiofNRywVgTVHbP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/461c74556d98da0f55212dabfbb68b33/5ed05/PXL_20210823_084142586.webp 190w,
/static/461c74556d98da0f55212dabfbb68b33/9d76b/PXL_20210823_084142586.webp 380w,
/static/461c74556d98da0f55212dabfbb68b33/33466/PXL_20210823_084142586.webp 760w,
/static/461c74556d98da0f55212dabfbb68b33/ca244/PXL_20210823_084142586.webp 1140w,
/static/461c74556d98da0f55212dabfbb68b33/e9f1f/PXL_20210823_084142586.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/461c74556d98da0f55212dabfbb68b33/89129/PXL_20210823_084142586.jpg 190w,
/static/461c74556d98da0f55212dabfbb68b33/0036d/PXL_20210823_084142586.jpg 380w,
/static/461c74556d98da0f55212dabfbb68b33/e484a/PXL_20210823_084142586.jpg 760w,
/static/461c74556d98da0f55212dabfbb68b33/32d87/PXL_20210823_084142586.jpg 1140w,
/static/461c74556d98da0f55212dabfbb68b33/ca9b4/PXL_20210823_084142586.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/461c74556d98da0f55212dabfbb68b33/e484a/PXL_20210823_084142586.jpg&quot;
            alt=&quot;接延长天线&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;完工&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%AE%8C%E5%B7%A5&quot; aria-label=&quot;完工 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;完工&lt;/h2&gt;
&lt;p&gt;完工后的样子。未进行系统测试前后对比信号数据，效果就是在屋内之前连不上 Wi-Fi 的地方都可以连上了，而且测速还不错！隔一堵墙 6米远下载测速约 200Mbps。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/33cebe445005b700d5155847c7f90ee0/e50c7/PXL_20210823_093151089.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 133.1578947368421%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIEBQP/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB1uavDChFRl0y2mcH/8QAHRAAAgICAwEAAAAAAAAAAAAAAQMAAhIiBBATIf/aAAgBAQABBQJpxqtmYlrEH71d3lZbAwGwErsUDGcg7//EABcRAQADAAAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8BqWP/xAAXEQEAAwAAAAAAAAAAAAAAAAAAARAR/9oACAECAQE/Aahr/8QAHhAAAgEDBQAAAAAAAAAAAAAAAAERAhAhEjFRgbH/2gAIAQEABj8C1cDxFkbWhqV4YtXOSpI6P//EAB4QAQACAgEFAAAAAAAAAAAAAAEAESExQRBRYYGh/9oACAEBAAE/IdCu1xHOBroeAt+ZdOXufJVAZpzC1KV3iGWYjivMLSFxAUx//9oADAMBAAIAAwAAABA//wDN/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAQEf/aAAgBAwEBPxDBxif/xAAXEQEBAQEAAAAAAAAAAAAAAAABABEQ/9oACAECAQE/EA3i0mf/xAAeEAEBAAICAgMAAAAAAAAAAAABEQAhMVFBYXGRsf/aAAgBAQABPxA9iyAKevHzhJ9cXnVuLvDv2i1rL8YvLLg6KV1bMgbhR1PB06v1gx9bAeepkxJS4A6B0C7EmPB0WX1g2gR+uf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/33cebe445005b700d5155847c7f90ee0/5ed05/PXL_20210823_093151089.webp 190w,
/static/33cebe445005b700d5155847c7f90ee0/9d76b/PXL_20210823_093151089.webp 380w,
/static/33cebe445005b700d5155847c7f90ee0/33466/PXL_20210823_093151089.webp 760w,
/static/33cebe445005b700d5155847c7f90ee0/a91d7/PXL_20210823_093151089.webp 900w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/33cebe445005b700d5155847c7f90ee0/89129/PXL_20210823_093151089.jpg 190w,
/static/33cebe445005b700d5155847c7f90ee0/0036d/PXL_20210823_093151089.jpg 380w,
/static/33cebe445005b700d5155847c7f90ee0/e484a/PXL_20210823_093151089.jpg 760w,
/static/33cebe445005b700d5155847c7f90ee0/e50c7/PXL_20210823_093151089.jpg 900w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/33cebe445005b700d5155847c7f90ee0/e484a/PXL_20210823_093151089.jpg&quot;
            alt=&quot;完工&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;其他&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%85%B6%E4%BB%96&quot; aria-label=&quot;其他 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;其他&lt;/h2&gt;
&lt;p&gt;Netgear R6300v2 真是神机，特别是在 Koolshare 论坛的 coolshine 发布的固件，可以让其用上改版梅林 386.2_4固件（截至发稿时），原帖地址 &lt;a href=&quot;https://koolshare.cn/thread-182286-1-2.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;[教程] 【重获新生】让R6300V2支持384.17！变身RT-AC6300v2&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;弱电箱里这么多设备闷在一起，散热是个问题。夏季 R6300v2 不开风扇 CPU温度 80℃ 以上。一直开着风扇在冬天又很蠢，手动开关很麻烦，在某宝上买了个温控器，调到超过 30℃ 自动开启风扇，低温时自动停止很好用。现在放在软路由上，软路由温度手摸不烫，但 R6300v2吹不到还是很烫，应该需要在弱电箱门上开几个进风口，有合理的风道散热效果会更好些吧。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/30914de4265d26a509b7549e48d761c5/ca9b4/IMG_20200821_204632.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.26315789473684%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEDBP/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAFO0ZdQCf/EABkQAAIDAQAAAAAAAAAAAAAAAAEDAgQTEf/aAAgBAQABBQKVlhOzMq05MXOtwCsc668l/wD/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAbEAACAgMBAAAAAAAAAAAAAAAAEQECEiExQf/aAAgBAQAGPwJNE2z4O1vRRMFqzaNmLez/xAAbEAEAAwEAAwAAAAAAAAAAAAABABEhMUFRgf/aAAgBAQABPyFKBi7CVarTxLXFx6iJ9Ka4+MZZIdMn/9oADAMBAAIAAwAAABDI3//EABYRAQEBAAAAAAAAAAAAAAAAAAEQUf/aAAgBAwEBPxBMn//EABYRAQEBAAAAAAAAAAAAAAAAAAEhEP/aAAgBAgEBPxAZc//EABsQAQEBAQEAAwAAAAAAAAAAAAERACFRMXHh/9oACAEBAAE/ECN1kh09ubRclmA/uSFa4CJOTUEJBD8p3ESIqpEysYqwh2e/W//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/30914de4265d26a509b7549e48d761c5/5ed05/IMG_20200821_204632.webp 190w,
/static/30914de4265d26a509b7549e48d761c5/9d76b/IMG_20200821_204632.webp 380w,
/static/30914de4265d26a509b7549e48d761c5/33466/IMG_20200821_204632.webp 760w,
/static/30914de4265d26a509b7549e48d761c5/ca244/IMG_20200821_204632.webp 1140w,
/static/30914de4265d26a509b7549e48d761c5/e9f1f/IMG_20200821_204632.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/30914de4265d26a509b7549e48d761c5/89129/IMG_20200821_204632.jpg 190w,
/static/30914de4265d26a509b7549e48d761c5/0036d/IMG_20200821_204632.jpg 380w,
/static/30914de4265d26a509b7549e48d761c5/e484a/IMG_20200821_204632.jpg 760w,
/static/30914de4265d26a509b7549e48d761c5/32d87/IMG_20200821_204632.jpg 1140w,
/static/30914de4265d26a509b7549e48d761c5/ca9b4/IMG_20200821_204632.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/30914de4265d26a509b7549e48d761c5/e484a/IMG_20200821_204632.jpg&quot;
            alt=&quot;温控器&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Where the Askent comes from]]></title><description><![CDATA[I have a personal project named Askent, which is actually a clone of Sli.do.]]></description><link>https://www.berlinchan.com/2021/07/where-the-askent-comes-from</link><guid isPermaLink="false">https://www.berlinchan.com/2021/07/where-the-askent-comes-from</guid><pubDate>Fri, 02 Jul 2021 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;I have a personal project named &lt;a href=&quot;https://github.com/BerlinChan/askent&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Askent&lt;/a&gt;, which is actually a clone of &lt;a href=&quot;https://www.sli.do/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sli.do&lt;/a&gt;.&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;p&gt;The idea was comes from a webinar hosted by &lt;a href=&quot;https://www.gatsbyjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GatsbyJs&lt;/a&gt;. At that meeting, the host use &lt;a href=&quot;https://www.sli.do/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sli.do&lt;/a&gt; as a tool to interact with audiences, let them ask questions, and make a survey at the end of the meeting.&lt;/p&gt;
&lt;p&gt;Sli.do’s easy-to-use and elegant design impressed me a lot when I first saw it. And at that time, I was learning &lt;a href=&quot;https://graphql.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL&lt;/a&gt; by myself, so I decided to make a clone of it as an exercise project.&lt;/p&gt;
&lt;p&gt;In project Askent, I’ve tried many new Front-End techs like &lt;a href=&quot;https://www.prisma.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PrismaJs&lt;/a&gt;, &lt;a href=&quot;https://reactjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ReactJs&lt;/a&gt;, &lt;a href=&quot;https://www.apollographql.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apollo&lt;/a&gt;, &lt;a href=&quot;https://deepstream.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;DeepStream&lt;/a&gt;, &lt;a href=&quot;https://hasura.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Hasura&lt;/a&gt;, etc …&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9576b0ca846a5c7cfd45d2ee38abcd43/ce099/github-contributions-2020.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.210526315789476%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA3klEQVR42iWPUW6EMAxEuUBCbAxJgDgJNuyqK9huP3r/ozWo0pPlnzea6bbHKccp+1XkxeVpne8h2H+cty5YvOmH2K6jeWaNSZd8UODOjtEssxPuU+q3BJKHR8UlUc64ZZRMnoGWwTOG1HDTAmHtaTbgu1HFXQLf6t4KH0Ut0OSP0tcGUhqYGCIPL8FaoFEKScUxtSIdaDFXNWc1v9r/CD0r0gozQ80Y+H5SBs7QsprpV4yMC7f+t0xr9ptOu/rHHvUYubQ+dohmipaCpXiPx2CGYH0cQ7Z5nS5pWwz4P0gTHlGX+uGfAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/9576b0ca846a5c7cfd45d2ee38abcd43/5ed05/github-contributions-2020.webp 190w,
/static/9576b0ca846a5c7cfd45d2ee38abcd43/9d76b/github-contributions-2020.webp 380w,
/static/9576b0ca846a5c7cfd45d2ee38abcd43/33466/github-contributions-2020.webp 760w,
/static/9576b0ca846a5c7cfd45d2ee38abcd43/ca244/github-contributions-2020.webp 1140w,
/static/9576b0ca846a5c7cfd45d2ee38abcd43/b0196/github-contributions-2020.webp 1520w,
/static/9576b0ca846a5c7cfd45d2ee38abcd43/26110/github-contributions-2020.webp 1528w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/9576b0ca846a5c7cfd45d2ee38abcd43/253c6/github-contributions-2020.png 190w,
/static/9576b0ca846a5c7cfd45d2ee38abcd43/810ee/github-contributions-2020.png 380w,
/static/9576b0ca846a5c7cfd45d2ee38abcd43/b4918/github-contributions-2020.png 760w,
/static/9576b0ca846a5c7cfd45d2ee38abcd43/c0204/github-contributions-2020.png 1140w,
/static/9576b0ca846a5c7cfd45d2ee38abcd43/54dc9/github-contributions-2020.png 1520w,
/static/9576b0ca846a5c7cfd45d2ee38abcd43/ce099/github-contributions-2020.png 1528w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/9576b0ca846a5c7cfd45d2ee38abcd43/b4918/github-contributions-2020.png&quot;
            alt=&quot;github-contributions-2020&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The project was funded in December 2019, when before the outbreak of COVID-19. and when I was locked down at my wife’s hometown in Spring festival vacation and nowhere to go, I’ve been spent a lot of time on it, try a variety of tools to store data persistence(like MySQL, MongoDB, and PostgreSQL), figure out how to send/receive messages in realtime on all clients(GraphQL subscriptions based on Pub/Sub model, DeepStream, Hasura), and how to create a user authenticate service(JWT).&lt;/p&gt;
&lt;p&gt;Through all of these trials, I’ve been learned a lot, and maybe I can name myself not only a front-end developer but also a full stack web developer too(maybe too much early ^_^).&lt;/p&gt;
&lt;p&gt;Nowadays, the Askent was &lt;a href=&quot;https://askent.berlinchan.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;deployed at Github Pages&lt;/a&gt;, and server-side runs on my home’s server so it will not costs me an arm and a leg, &lt;del&gt;and regards of security of the home network, the server has not published to the Internet which I’m still working on it.&lt;/del&gt; I’ve deployed the server to the Internet in my spare time, which with a lot of bugs, still WIP(update on July, 7th).&lt;/p&gt;
&lt;h2 id=&quot;about-the-name&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#about-the-name&quot; aria-label=&quot;about the name permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;About the name&lt;/h2&gt;
&lt;p&gt;Askent = Ask + Event&lt;/p&gt;
&lt;h2 id=&quot;related-topics-on-project-askent&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#related-topics-on-project-askent&quot; aria-label=&quot;related topics on project askent permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Related topics on project Askent&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2021/03/askent-realtime-search-implement-and-hasura&quot;&gt;Askent 实时消息搜索的问题及 Hasura 替代&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2021/03/Scaling-to-1-million-active-GraphQL-subscriptions&quot;&gt;借助 GraphQL 承载 100万并发活动订阅（实时查询）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2020/03/askent-give-up-prisma2&quot;&gt;Askent 项目进展及准备弃用 Prisma2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2020/03/real-time-multi-device-collaboration-devtools&quot;&gt;几款多端实时协作、同步的开发工具&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2020/02/project-askent-admin-audience-client&quot;&gt;互动演示工具 Askent 已开发出管理与观众端雏形&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2019/12/create-presentation-tool-from-scratch&quot;&gt;从零开始，创建一个多端互动演示工具&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Askent 实时消息搜索的问题及 Hasura 替代]]></title><description><![CDATA[当前参照 DeepStream 的 Realtime Search 思路实现的提问列表实时查询，存在严重耗费性能的问题，偶然发现 Hasura 正是我需要的工具。]]></description><link>https://www.berlinchan.com/2021/03/askent-realtime-search-implement-and-hasura</link><guid isPermaLink="false">https://www.berlinchan.com/2021/03/askent-realtime-search-implement-and-hasura</guid><pubDate>Sat, 13 Mar 2021 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;问题&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E9%97%AE%E9%A2%98&quot; aria-label=&quot;问题 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;问题&lt;/h2&gt;
&lt;p&gt;在项目 &lt;a href=&quot;/2020/03/real-time-multi-device-collaboration-devtools&quot;&gt;Askent&lt;/a&gt; 开发中，需要同步管理后台、客户端、演示大屏，三端上的消息显示，继之前&lt;a href=&quot;/2020/03/real-time-multi-device-collaboration-devtools&quot;&gt;使用 Apollo Subscription 基于 Pub/Sub 开发遇到问题&lt;/a&gt;，然后参照 &lt;a href=&quot;https://deepstream.io/blog/20191104-realtime-search/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;DeepStream 的 Realtime Search 思路&lt;/a&gt;实现一版后，仍有一个严重的性能问题无法解决——每个客户端会话权限、查询变量各不同，因此一个提问的 Pub 事件需要为每个客户端执行一次（实际是两次，见下文流程图）查询，再计算 diff 后向对应客户端推送增量数据。虽未进行压力测试，但可以预见这是个严重耗费性能的不好的设计。&lt;/p&gt;
&lt;h2 id=&quot;目前实现&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%9B%AE%E5%89%8D%E5%AE%9E%E7%8E%B0&quot; aria-label=&quot;目前实现 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;目前实现&lt;/h2&gt;
&lt;p&gt;该实时搜索实现的流程如下图：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3242a49d271769e1ce9dc87ac5c1a1ba/19c75/realtime-search-workflow.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEDBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAC/9oADAMBAAIQAxAAAAHWVFSgyX//xAAaEAEAAgMBAAAAAAAAAAAAAAABAAIREjEy/9oACAEBAAEFAmzndlPKGdSV5//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEv/aAAgBAwEBPwGiv//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAAICAwAAAAAAAAAAAAAAAAABEWEhQXH/2gAIAQEAAT8hSeOFqGlDSFAkYn//2gAMAwEAAgADAAAAEAAv/8QAGBEAAgMAAAAAAAAAAAAAAAAAABEBIWH/2gAIAQMBAT8Qwgbs/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAEhMf/aAAgBAgEBPxCk6//EABoQAQEAAwEBAAAAAAAAAAAAAAERACFBUTH/2gAIAQEAAT8QaopdDlwqyC+Y1XNYpPW/rlVXr1wiBDP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/3242a49d271769e1ce9dc87ac5c1a1ba/5ed05/realtime-search-workflow.webp 190w,
/static/3242a49d271769e1ce9dc87ac5c1a1ba/9d76b/realtime-search-workflow.webp 380w,
/static/3242a49d271769e1ce9dc87ac5c1a1ba/33466/realtime-search-workflow.webp 760w,
/static/3242a49d271769e1ce9dc87ac5c1a1ba/ca244/realtime-search-workflow.webp 1140w,
/static/3242a49d271769e1ce9dc87ac5c1a1ba/a0b4f/realtime-search-workflow.webp 1366w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/3242a49d271769e1ce9dc87ac5c1a1ba/89129/realtime-search-workflow.jpg 190w,
/static/3242a49d271769e1ce9dc87ac5c1a1ba/0036d/realtime-search-workflow.jpg 380w,
/static/3242a49d271769e1ce9dc87ac5c1a1ba/e484a/realtime-search-workflow.jpg 760w,
/static/3242a49d271769e1ce9dc87ac5c1a1ba/32d87/realtime-search-workflow.jpg 1140w,
/static/3242a49d271769e1ce9dc87ac5c1a1ba/19c75/realtime-search-workflow.jpg 1366w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/3242a49d271769e1ce9dc87ac5c1a1ba/e484a/realtime-search-workflow.jpg&quot;
            alt=&quot;realtime-search-workflow&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;它总体分为两步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先请求提问列表，服务端根据请求参数生成一个 hash，并与提问列表结果一同返回&lt;/li&gt;
&lt;li&gt;客户端用 hash 订阅提问列表&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;hasura&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hasura&quot; aria-label=&quot;hasura permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hasura&lt;/h2&gt;
&lt;p&gt;偶然看到文章 &lt;a href=&quot;https://blog.graphqleditor.com/graphql-tools-partone/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL tools &amp;#x26; libraries&lt;/a&gt; 中介绍 Hasura - instant realtime GraphQL APIs on any Postgres database。&lt;/p&gt;
&lt;p&gt;看了文档 &lt;a href=&quot;https://github.com/hasura/graphql-engine/blob/master/architecture/live-queries.md#scaling-to-1-million-active-graphql-subscriptions-live-queries&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Scaling to 1 million active GraphQL subscriptions (live queries)&lt;/a&gt;(&lt;a href=&quot;/2021/03/Scaling-to-1-million-active-GraphQL-subscriptions&quot;&gt;中文&lt;/a&gt;)，发现它正是我需要的。&lt;/p&gt;
&lt;p&gt;目前替换提问列表的实时查询，包含用户权限的请求待验证。&lt;/p&gt;
&lt;p&gt;其他可用的实时查询、实时数据库工具：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/n1ru4l/graphql-live-query&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL Live Query&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.timescale.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TimescaleDB&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[借助 GraphQL 承载 100万并发活动订阅（实时查询）]]></title><description><![CDATA[Hasura 是基于 Postgres数据库的 GraphQL 引擎，提供可控制权限的开箱即用的 GraphQL API。到 hasura.io 和 github.com/hasura/graphql-engine 了解更多。]]></description><link>https://www.berlinchan.com/2021/03/Scaling-to-1-million-active-GraphQL-subscriptions</link><guid isPermaLink="false">https://www.berlinchan.com/2021/03/Scaling-to-1-million-active-GraphQL-subscriptions</guid><pubDate>Tue, 09 Mar 2021 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;Hasura 是基于 Postgres数据库的 GraphQL 引擎，提供可控制权限的开箱即用的 GraphQL API。到 &lt;a href=&quot;https://hasura.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;hasura.io&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/hasura/graphql-engine&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/hasura/graphql-engine&lt;/a&gt; 了解更多。&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;p&gt;Hasura 可为客户端提供实时查询（基于 GraphQL 订阅）。例如，一个外卖应用使用实时查询显示某特定用户的订单实时状态。&lt;/p&gt;
&lt;p&gt;本文档描述 Hasura 的架构，阐述它是如何支撑百万个并发实时查询的。&lt;/p&gt;
&lt;h2 id=&quot;原文&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%8E%9F%E6%96%87&quot; aria-label=&quot;原文 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;原文&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hasura/graphql-engine/blob/master/architecture/live-queries.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Scaling to 1 million active GraphQL subscriptions (live queries)&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;目录&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%9B%AE%E5%BD%95&quot; aria-label=&quot;目录 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E5%8E%9F%E6%96%87&quot;&gt;原文&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E7%9B%AE%E5%BD%95&quot;&gt;目录&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E7%BB%93%E8%AE%BA&quot;&gt;结论&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#graphql-%E4%B8%8E%E8%AE%A2%E9%98%85&quot;&gt;GraphQL 与订阅&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E5%AE%9E%E7%8E%B0-graphql-%E5%AE%9E%E6%97%B6%E6%9F%A5%E8%AF%A2&quot;&gt;实现 GraphQL 实时查询&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E5%86%8D%E8%8E%B7%E5%8F%96-graphql-%E6%9F%A5%E8%AF%A2%E7%BB%93%E6%9E%9C&quot;&gt;再获取 GraphQL 查询结果&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#hasura-%E7%9A%84%E6%96%B9%E6%B3%95&quot;&gt;Hasura 的方法&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%B8%BB%E6%84%8F-1%E7%BC%96%E8%AF%91-%E4%B8%80%E4%B8%AA-graphql-%E6%9F%A5%E8%AF%A2%E4%B8%BA%E5%8D%95%E4%B8%AA-sql-%E6%9F%A5%E8%AF%A2&quot;&gt;主意 #1：编译 一个 GraphQL 查询为单个 SQL 查询&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%B8%BB%E6%84%8F-2%E4%BD%BF%E6%8E%88%E6%9D%83%E5%85%B7%E6%9C%89%E5%A3%B0%E6%98%8E%E6%80%A7&quot;&gt;主意 #2：使授权具有声明性&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%B8%BB%E6%84%8F-3%E5%9C%A8%E5%8D%95%E4%B8%AA-sql-%E6%9F%A5%E8%AF%A2%E4%B8%AD%E6%89%B9%E5%A4%84%E7%90%86%E5%A4%9A%E4%B8%AA%E5%AE%9E%E6%97%B6%E6%9F%A5%E8%AF%A2&quot;&gt;主意 #3：在单个 SQL 查询中批处理多个实时查询&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%BD%95%E6%97%B6%E6%89%A7%E8%A1%8C%E5%86%8D%E8%8E%B7%E5%8F%96refetch&quot;&gt;何时执行再获取（refetch）？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%B5%8B%E8%AF%95&quot;&gt;测试&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%9C%AC%E6%96%B9%E6%B3%95%E7%9A%84%E4%BC%98%E5%8A%BF&quot;&gt;本方法的优势&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%9C%AA%E6%9D%A5%E5%B1%95%E6%9C%9B&quot;&gt;未来展望&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;结论&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%BB%93%E8%AE%BA&quot; aria-label=&quot;结论 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;结论&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;设置：&lt;/strong&gt; 每个客户端（Web 或 移动应用）用认证令牌登录并订阅一个实时查询结果。数据存放于 Postgres 数据库。每秒钟更新 Postgres 数据库中的一百万行数据，并确保推送一个新查询结果到客户端。Hasura 是 GraphQL API 的提供者（包含授权）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;测试：&lt;/strong&gt; Hasura 可以并发处理多少个客户端的实施订阅？Hasura 是否可以纵向或横向性能伸缩扩展？&lt;/p&gt;
&lt;img alt=&quot;&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/main-image-fs8.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;img alt=&quot;single-instance-results&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/images2/single-instance-fs8.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;单例配置&lt;/th&gt;
&lt;th&gt;活动实时查询数量&lt;/th&gt;
&lt;th&gt;CPU 平均负载&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1xCPU, 2GB RAM&lt;/td&gt;
&lt;td&gt;5000&lt;/td&gt;
&lt;td&gt;60%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2xCPU, 4GB RAM&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td&gt;73%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4xCPU, 8GB RAM&lt;/td&gt;
&lt;td&gt;20000&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;img alt=&quot;results-horizontally-scaled&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/horizontal-scalability.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;p&gt;当一百万个实时查询的时候，Postgres 的负载不超过 28%，连接数峰值在 850 左右。&lt;/p&gt;
&lt;p&gt;关于配置的说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS RDS postgres, Fargate, 使用 ELB 的默认配置，无任何微调&lt;/li&gt;
&lt;li&gt;RDS Postgres: 16xCPU, 64GB RAM, Postgres 11&lt;/li&gt;
&lt;li&gt;Hasura 运行在 Fargate (4xCPU, 8GB RAM per instance) ，采用默认配置&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;graphql-与订阅&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#graphql-%E4%B8%8E%E8%AE%A2%E9%98%85&quot; aria-label=&quot;graphql 与订阅 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GraphQL 与订阅&lt;/h2&gt;
&lt;p&gt;GraphQL 让应用开发者轻松地从 API 中精确获取他们想要的数据。&lt;/p&gt;
&lt;p&gt;比如，我们要创建一个外卖应用。Postgres 的架构看起来像这样：&lt;/p&gt;
&lt;img alt=&quot;postgres schema for food delivery app&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/pg-schema.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;p&gt;应用界面会显示当前用户订单的状态，GraphQL 查询会获取订单最新状态和配送员的定位。&lt;/p&gt;
&lt;img alt=&quot;order-graphql-query&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/images2/basic-query-fs8.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;p&gt;在底层，查询被当作字符串发送给服务器，经解析、授权后，从数据库中获取应用所需的数据。返回的 JSON 数据结构与请求时的相同。&lt;/p&gt;
&lt;p&gt;进入实时查询：实时查询的主意是订阅特定查询的最新结果。一旦底层的数据改变，服务器应该推送最新结果到客户端。&lt;/p&gt;
&lt;p&gt;乍一看，这完美符合 GraphQL 的使用场景，因为 GraphQL 客户端支持处理大量 WebSockets 连接。用 subscription 替换 query 就能转换查询到实时查询。就是这么简单，如果 GraphQL 服务器可实现的话。&lt;/p&gt;
&lt;img alt=&quot;order-subscription-query&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/images2/subscription-query-fs8.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;h2 id=&quot;实现-graphql-实时查询&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%AE%9E%E7%8E%B0-graphql-%E5%AE%9E%E6%97%B6%E6%9F%A5%E8%AF%A2&quot; aria-label=&quot;实现 graphql 实时查询 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;实现 GraphQL 实时查询&lt;/h2&gt;
&lt;p&gt;实现实时查询是个痛苦的过程。当你的数据库查询包含授权规则，那么当变更事件发生时，会增加查询结果的计算量。这在 web服务上是一个挑战。像 Postgres 这样的数据库，这相当于在底层表更改时，要保持物化视图最新一样困难困难。另一种方法是为特定查询重新获取全部数据（针对特定授权规则的客户端）。这是我们目前采取的方法。&lt;/p&gt;
&lt;p&gt;其次，构建一个 web服务以一种可伸缩的方式来处理 websockets 有时也有点麻烦，但是某些框架和语言确实使并发编程更容易处理。&lt;/p&gt;
&lt;h3 id=&quot;再获取-graphql-查询结果&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%86%8D%E8%8E%B7%E5%8F%96-graphql-%E6%9F%A5%E8%AF%A2%E7%BB%93%E6%9E%9C&quot; aria-label=&quot;再获取 graphql 查询结果 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;再获取 GraphQL 查询结果&lt;/h3&gt;
&lt;p&gt;为了理解为什么这里使用再获取(refetching) GraphQL 查询不好，我们来看一个典型的 GraphQL 查询过程：&lt;/p&gt;
&lt;img alt=&quot;graphql-resolvers&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/resolvers-fs8.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;p&gt;在 GraphQL 查询中，授权 + 数据获取逻辑必须为每个节点运行一次。这很可怕，因为即使稍大一点的查询都会轻易拖垮数据库。运用 ORM 不当时，N+1 问题也是个常见问题，这对数据库性能不好，且难以优化 Postgres 查询。Data loader 类型模式可以缓解该问题，但在底层仍然会多次查询 Postgres 数据库（减少为 GraphQL 查询中有多少个节点）。&lt;/p&gt;
&lt;p&gt;实时查询中，该问题更为严重，因为每个客户端的查询都有一个独一无二的再获取。尽管查询是相同的，考虑到授权规则创建不同的会话变量，每个客户端需要单独的再获取。&lt;/p&gt;
&lt;h3 id=&quot;hasura-的方法&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hasura-%E7%9A%84%E6%96%B9%E6%B3%95&quot; aria-label=&quot;hasura 的方法 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hasura 的方法&lt;/h3&gt;
&lt;p&gt;如何做得更好？如果申明式映射数据模型到 GraphQL 模式，并使用它创建单个 SQL 数据库查询？这样就能避免多次捣鼓数据库，无论返回中是否有大量项目或 GraphQL 查询中有很多节点。&lt;/p&gt;
&lt;h4 id=&quot;主意-1编译-一个-graphql-查询为单个-sql-查询&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%B8%BB%E6%84%8F-1%E7%BC%96%E8%AF%91-%E4%B8%80%E4%B8%AA-graphql-%E6%9F%A5%E8%AF%A2%E4%B8%BA%E5%8D%95%E4%B8%AA-sql-%E6%9F%A5%E8%AF%A2&quot; aria-label=&quot;主意 1编译 一个 graphql 查询为单个 sql 查询 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;主意 #1：编译 一个 GraphQL 查询为单个 SQL 查询&lt;/h4&gt;
&lt;p&gt;Hasura 部分功能是作为转译器，它使用数据模型到 GraphQL 模式的映射信息的元数据，编译 GraphQL 查询为 SQL 查询，来从数据库获取数据。&lt;/p&gt;
&lt;p&gt;GraphQL 查询  GraphQL 抽象语法树（AST）  SQL 抽象语法树  SQL&lt;/p&gt;
&lt;img alt=&quot;graphql-to-sql&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/graphql-2-sql-fs8.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;p&gt;这消除了 N+1 查询问题，并允许数据库优化数据获取。&lt;/p&gt;
&lt;p&gt;但这不够，因为解析器（resolver）还应用了授权来强制它只获取权限范围内的数据。因此我们需要将授权规则嵌入到生成的 SQL 中。&lt;/p&gt;
&lt;h4 id=&quot;主意-2使授权具有声明性&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%B8%BB%E6%84%8F-2%E4%BD%BF%E6%8E%88%E6%9D%83%E5%85%B7%E6%9C%89%E5%A3%B0%E6%98%8E%E6%80%A7&quot; aria-label=&quot;主意 2使授权具有声明性 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;主意 #2：使授权具有声明性&lt;/h4&gt;
&lt;p&gt;访问数据时的授权本质上是一种约束，它取决于所获取的数据(或行)的值，以及动态提供的应用程序特定用户的会话变量。例如最普通的，一行数据包含表示数据所有权的字段 &lt;code class=&quot;language-text&quot;&gt;user_id&lt;/code&gt;。或有个关联表 &lt;code class=&quot;language-text&quot;&gt;document_viewers&lt;/code&gt; 来表示用户可查看哪些文档。其他场景中，会话变量本身可能包含与行相关的数据所有权信息，例如，账户管理员可以访问任何帐户 [1,2,3]，其中该信息不存在于当前数据库中，而是存在于会话变量（可能由其他数据系统提供）。&lt;/p&gt;
&lt;p&gt;为了对此建模，我们在 API 层实现了类似于 Postgres RLS 的授权层，以提供用于配置访问控制的声明性框架。如果您熟悉 RLS，可以类比 SQL查询中的当前会话变量为来自 cookie、JWTs 或 HTTP头的 HTTP会话变量。&lt;/p&gt;
&lt;p&gt;顺便说一句，因为我们在许多年前就开始了 Hasura 工程，所以我们在 Postgres RLS特性加入 Postgres 之前就在应用层实现了该特性。我们甚至在的 insert 返回子句中也有与已修复的 Postgres RLS 相同的 bug  &lt;a href=&quot;https://www.postgresql.org/about/news/1614/%E3%80%82&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.postgresql.org/about/news/1614/。&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;因为在 Hasura 应用层面实现了授权（而不是使用 RLS，传递用户详情给 Postgres 连接的当前会话），这带来一个显著优势，我们等下会看到。&lt;/p&gt;
&lt;p&gt;总而言之，既然授权是声明性的，并且可以在表、视图甚至函数（如果函数返回 SETOF）中使用，那么就可以创建具有授权规则的单个 SQL查询。&lt;/p&gt;
&lt;p&gt;GraphQL 查询  GraphQL 抽象语法树  包含授权规则的内部抽象语法树  SQL 抽象语法树  SQL&lt;/p&gt;
&lt;img alt=&quot;graphql-to-sql-with-authorization&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/graphql-2-sql-auth-fs8.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;h4 id=&quot;主意-3在单个-sql-查询中批处理多个实时查询&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%B8%BB%E6%84%8F-3%E5%9C%A8%E5%8D%95%E4%B8%AA-sql-%E6%9F%A5%E8%AF%A2%E4%B8%AD%E6%89%B9%E5%A4%84%E7%90%86%E5%A4%9A%E4%B8%AA%E5%AE%9E%E6%97%B6%E6%9F%A5%E8%AF%A2&quot; aria-label=&quot;主意 3在单个 sql 查询中批处理多个实时查询 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;主意 #3：在单个 SQL 查询中批处理多个实时查询&lt;/h4&gt;
&lt;p&gt;仅凭主意 &lt;a href=&quot;#%E4%B8%BB%E6%84%8F-1%E7%BC%96%E8%AF%91-%E4%B8%80%E4%B8%AA-graphql-%E6%9F%A5%E8%AF%A2%E4%B8%BA%E5%8D%95%E4%B8%AA-sql-%E6%9F%A5%E8%AF%A2&quot;&gt;#1&lt;/a&gt;、&lt;a href=&quot;#%E4%B8%BB%E6%84%8F-2%E4%BD%BF%E6%8E%88%E6%9D%83%E5%85%B7%E6%9C%89%E5%A3%B0%E6%98%8E%E6%80%A7&quot;&gt;#2&lt;/a&gt; 的实现，我们仍会导致这样的情况：连接了 100k 个客户端可能会导致相称的 100k 个 Postgres查询来获取最新数据（假如 100k 个更新，每个更新对应一个客户端）。&lt;/p&gt;
&lt;p&gt;然而，考虑到我们在 API层有所有应用程序用户级会话变量可用，我们实际上可以创建单个 SQL查询来一次为许多客户端再次获取数据！&lt;/p&gt;
&lt;p&gt;假设有客户端运行一个订阅，以获取最新的订单状态和配送员位置。我们可以在查询中创建一个关系，其中包含作为不同行的所有查询变量（订单id）和会话变量（用户id）。然后join查询以获取具有此关系的实际数据，以确保在单个响应中获取多个客户端的最新数据。这将允许同时为多个用户获取最新的结果，即使它们提供的参数和会话变量是完全动态的，并且仅在查询时可用。&lt;/p&gt;
&lt;img alt=&quot;graphql-to-sql-multiplexed&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/multiplexed-fs8.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;h4 id=&quot;何时执行再获取refetch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BD%95%E6%97%B6%E6%89%A7%E8%A1%8C%E5%86%8D%E8%8E%B7%E5%8F%96refetch&quot; aria-label=&quot;何时执行再获取refetch permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;何时执行再获取（refetch）？&lt;/h4&gt;
&lt;p&gt;我们尝试了多种方法，获取底层数据库更新事件时来再获取查询。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;监听、通知：需要用触发器检测所有表，在消费端（web服务器）重启或网络中断的情况下，被消费事件可能会被丢弃。&lt;/li&gt;
&lt;li&gt;WAL（译注：Write-ahead logging，预写式日志）：Reliable stream，但是 LR 插槽使得横向性能扩展非常困难，并且在托管数据库供应商上通常不可用。繁重的写入负载会污染 WAL，并且需要在应用层进行节流。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在这些尝试后，我们当前回退到基于时间间隔的轮询来再获取查询。因此，我们不是在有适当事件时再获取，而是根据一个时间间隔再获取查询。这样做有两个主要原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当实时查询中使用的声明性权限和条件很简单时，在某种程度上可以将数据库事件映射到特定客户端的动态查询上（比如 &lt;code class=&quot;language-text&quot;&gt;order_id&lt;/code&gt; = 1 and &lt;code class=&quot;language-text&quot;&gt;user_id&lt;/code&gt; = cookie.&lt;code class=&quot;language-text&quot;&gt;session_id&lt;/code&gt;），但是对于复杂的查询会变得棘手（比如查询 &lt;code class=&quot;language-text&quot;&gt;&apos;status&apos; ILIKE &apos;failed_%&apos;&lt;/code&gt;）。声明性权限有时也可以跨表使用。我们在研究这种方法以及基本的增量更新方面投入了大量的精力，并且在生产中有&lt;a href=&quot;https://www.postgresql.eu/events/pgconfeu2018/sessions/session/2195/slides/144/Implementing%20Incremental%20View%20Maintenance%20on%20PostgreSQL%20.pdf&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;几个小项目&lt;/a&gt;采用了类似的方法。&lt;/li&gt;
&lt;li&gt;对于任何应用程序，除非写吞吐量很小，否则无论如何，您最终都会在一个时间间隔内对事件进行节流/防抖（throttling/debouncing）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这种方法的缺点是写负载很小时存在延迟。再获取可以立即完成，而不是在几毫秒后。通过适当地调整再获取间隔和批量大小，可以容易地缓解这一问题。到目前为止，我们首先关注的是消除开销最大的瓶颈，即再获取查询。也就是说，我们将在接下来的几个月里继续关注改进，特别是使用事件依赖（在适用的情况下），来潜在地减少实时查询中每隔一段时间再获取的的数量。&lt;/p&gt;
&lt;p&gt;请注意，我们在内部有其他基于事件的方法的驱动程序，如果你有一个用例，目前的方法不能满足你的要求，我们愿意与你合作与提供协助！&lt;/p&gt;
&lt;h2 id=&quot;测试&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%B5%8B%E8%AF%95&quot; aria-label=&quot;测试 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;测试&lt;/h2&gt;
&lt;p&gt;测试基于 Websockets 的实时查询的性能扩展性与可靠性是一项挑战。我们花了几周来构建测试套件和基础自动化工具。设置如下所示：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一个 nodejs脚本，它运行大量的 GraphQL实时查询客户端，并在内存中记录事件，这些事件随后被存储到数据库中。&lt;a href=&quot;https://github.com/hasura/subscription-benchmark&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;[github]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;一个在数据库上创建写负载的脚本，从而导致所有运行实时查询的客户端之间发生更改（每秒更新一百万行）。&lt;/li&gt;
&lt;li&gt;一旦测试套件完成运行，验证脚本就会在数据库中运行，在该数据库中提取日志/事件以验证没有错误并且所有事件已被接收到。&lt;/li&gt;
&lt;li&gt;仅在以下情况下才认为测试有效：
&lt;ul&gt;
&lt;li&gt;收到的有效载荷错误数为 0&lt;/li&gt;
&lt;li&gt;从创建事件到客户端接收的平均延迟小于 1000毫秒&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;testing-architecture&quot; src=&quot;https://storage.googleapis.com/graphql-engine-cdn.hasura.io/img/subscriptions-images/test-architecture.png&quot; style=&quot;width:100%;&quot;&gt;
&lt;h2 id=&quot;本方法的优势&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9C%AC%E6%96%B9%E6%B3%95%E7%9A%84%E4%BC%98%E5%8A%BF&quot; aria-label=&quot;本方法的优势 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;本方法的优势&lt;/h2&gt;
&lt;p&gt;Hasura 使实时查询变得触手可及。查询的概念很容易扩展到实时查询，而不需要使用 GraphQL 的开发人员做任何额外工作。这对我们来说是最重要的。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;功能特性丰富的实时查询，全面支持 Postgres operators/aggregations/views/functions 等&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;h2 id=&quot;未来展望&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9C%AA%E6%9D%A5%E5%B1%95%E6%9C%9B&quot; aria-label=&quot;未来展望 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;未来展望&lt;/h2&gt;
&lt;p&gt;减少 Postgres 负载，通过：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;映射事件到实时查询&lt;/li&gt;
&lt;li&gt;结果集的增量计算&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[前端动态插件的实现 with Apollo & React]]></title><description><![CDATA[如何在前端动态安装、运行插件？之前一直好奇这个问题，正巧最近看到一篇文章《Creating a Frontend Architecture With Dynamic Plugins》，于是我参照大体思路，用 Apollo & React 做一个实现 Demo。]]></description><link>https://www.berlinchan.com/2020/11/frontend-dynamic-plugin-implement</link><guid isPermaLink="false">https://www.berlinchan.com/2020/11/frontend-dynamic-plugin-implement</guid><pubDate>Tue, 24 Nov 2020 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;如何在前端动态安装、运行插件？之前一直好奇这个问题，正巧最近看到一篇文章《&lt;a href=&quot;https://dzone.com/articles/dynamically-pluggable-frontend-architecture&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Creating a Frontend Architecture With Dynamic Plugins&lt;/a&gt;》，于是我参照大体思路，用 Apollo &amp;#x26; React 做一个&lt;a href=&quot;https://github.com/BerlinChan/frontend-dynamic-plugin-demo&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;实现 Demo&lt;/a&gt;。&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;demo-repository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#demo-repository&quot; aria-label=&quot;demo repository permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Demo Repository&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/BerlinChan/frontend-dynamic-plugin-demo&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;frontend-dynamic-plugin-demo&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;思路&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%80%9D%E8%B7%AF&quot; aria-label=&quot;思路 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;思路&lt;/h2&gt;
&lt;p&gt;前端动态加载插件，最小化情况的关键两点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;获取已安装插件的列表，按照插件的 metadata 信息，&lt;strong&gt;显示插件模块入口&lt;/strong&gt;。入口可以是导航菜单、Tab页、按钮等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加载并运行&lt;/strong&gt;插件入口 js 文件。入口文件运行后，可以是将插件内容渲染进核心程序界面预留的标签中，或弹出 Modal窗口等。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;更多考虑&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9B%B4%E5%A4%9A%E8%80%83%E8%99%91&quot; aria-label=&quot;更多考虑 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;更多考虑&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;真实情况中，已安装插件的信息肯定是在数据库中维护的。本例为简单从文件中读取。&lt;/li&gt;
&lt;li&gt;在大型项目中，需要为插件开发提供便于调试的环境，和打包插件的构建程序。&lt;/li&gt;
&lt;li&gt;核心程序与插件、插件与插件之间的通讯，可以用抽象出的 store 实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有更多复杂场景，在参照的文章作者写的第二篇 &lt;a href=&quot;https://dzone.com/articles/scale-your-frontend-application-dynamically&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Scale Your Frontend Application&lt;/a&gt; 中讨论了很多。&lt;/p&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dzone.com/articles/dynamically-pluggable-frontend-architecture&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Creating a Frontend Architecture With Dynamic Plugins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dzone.com/articles/scale-your-frontend-application-dynamically&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Scale Your Frontend Application&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[《聆听音乐》读书笔记（二、三）]]></title><description><![CDATA[《聆听音乐》读书笔记（二） 两种演唱方式：音节式，歌词的每个音节只有一个或两个音符；花唱式，一个音节有很多音符。]]></description><link>https://www.berlinchan.com/2020/11/read-listen-to-music-2</link><guid isPermaLink="false">https://www.berlinchan.com/2020/11/read-listen-to-music-2</guid><pubDate>Sun, 08 Nov 2020 10:46:37 GMT</pubDate><content:encoded>&lt;h2 id=&quot;聆听音乐读书笔记二&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%81%86%E5%90%AC%E9%9F%B3%E4%B9%90%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%BA%8C&quot; aria-label=&quot;聆听音乐读书笔记二 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;《聆听音乐》读书笔记（二）&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;两种演唱方式：&lt;strong&gt;音节式&lt;/strong&gt;，歌词的每个音节只有一个或两个音符；&lt;strong&gt;花唱式&lt;/strong&gt;，一个音节有很多音符。&lt;/li&gt;
&lt;/ol&gt;
&lt;!-- endExcerpt --&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;文艺复兴 Renaissance&lt;/strong&gt; 字面意思是“再生”，广义指 1350 - 1600 年首次出现在意大利，然后在法国，最后在英国的一个理性和艺术的兴盛时期，音乐上更狭义指 1450 - 1600 年。关注古代经典文学艺术的的再生是一种对人类自身的兴趣的再生。我们把这种热情的对人自身的兴趣叫做&lt;strong&gt;人文主义&lt;/strong&gt;。简单地说，人文主义是一种信念，他相信人不仅是上天的礼物，他们的确也有能力创造很多美好的事物，也有能力来欣赏它们。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;艺术的判断、欣赏和批评在人文主义的文艺复兴时期第一次进入了西方人的思想。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;帕勒斯特里那被人们夸张的称为“宗教音乐的救星”，他的复调弥撒曲避免了一种强烈的节拍和“迷人”的节奏，用简单的对位代替了复杂的模仿复调，所有的特质都使歌词能够非常清晰地表达，符合特伦特宗教会议提出的所有要求。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1460 年约翰·古登堡发明活字印刷术。1501 年乐谱被快速、便宜的印制，第一批有学识的业余音乐爱好者出现了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;聆听音乐读书笔记三&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%81%86%E5%90%AC%E9%9F%B3%E4%B9%90%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%B8%89&quot; aria-label=&quot;聆听音乐读书笔记三 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;《聆听音乐》读书笔记（三）&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;巴洛克 Baroque，源于葡萄牙语，意思是形状不规则的珍珠，艺术史指 1600～1750年。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;罗马圣彼得大教堂、巴黎郊外凡尔赛宫都是典型的&lt;strong&gt;巴洛克建筑&lt;/strong&gt;，他们的广场、建筑、柱廊、花园和喷泉都是建立在&lt;strong&gt;宏大&lt;/strong&gt;的规模上。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;巴洛克音乐的特征：&lt;strong&gt;宏大、激情的表现和戏剧性&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;巴洛克时期，很多调式逐渐减少，剩下现代所用的&lt;strong&gt;大调、小调&lt;/strong&gt;。这两种调式尖锐地、戏剧性地形成了不同的音响，他们可以被用于表现的目的。作曲家可以在阴暗的小调与光明的大调之间形成对比，就像画家为了戏剧性的效果而做明暗对比那样。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;歌剧 opera&lt;/strong&gt;，意大利语意思是作品。&lt;strong&gt;咏叹调 aria&lt;/strong&gt;，意大利语意思是歌曲、曲调。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;奏鸣曲 Sonata&lt;/strong&gt;，是一种器乐的室内乐，每个声部只有一个演奏者。&lt;strong&gt;协奏曲 Concerto&lt;/strong&gt;，是一种以独奏者和管弦乐队之间友好竞赛为标志的音乐作品。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[我为什么喜欢古典音乐]]></title><description><![CDATA[为什么想学古典音乐，回顾我的经历找到一些动机：音乐是舞蹈的灵魂、声音的飘渺神秘、离别时刻的感触、寻回想象力等原因，都在引领我走近它。]]></description><link>https://www.berlinchan.com/2020/10/why-i-love-classical-music</link><guid isPermaLink="false">https://www.berlinchan.com/2020/10/why-i-love-classical-music</guid><pubDate>Mon, 26 Oct 2020 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;一音乐是舞蹈的灵魂&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%B8%80%E9%9F%B3%E4%B9%90%E6%98%AF%E8%88%9E%E8%B9%88%E7%9A%84%E7%81%B5%E9%AD%82&quot; aria-label=&quot;一音乐是舞蹈的灵魂 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;一、音乐是舞蹈的灵魂&lt;/h2&gt;
&lt;p&gt;2006年，在武汉体育学院本科学体育舞蹈时，维也纳华尔兹课上总是伴随一些耳熟能详的圆舞曲练习方步，这些甚至我都叫不出名字的优美旋律令我陶醉，每次聆听都不禁想迈出舞步。专业课虽然包含视唱练耳，但只止步于基础乐理的认识五线谱，我记得最后几节课开始提到大调与小调，并且通过听老师的弹奏来区分它们，没有进一步深入赏析我们舞蹈所使用舞曲。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;音乐是舞蹈的灵魂，舞蹈是音乐的回声。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;我理解，作为灵魂的音乐正是舞蹈所要表现的情绪与涵义的最核心的根源&lt;/strong&gt;，在这个层面上，它同编舞者一样重要，指导着肢体动作。当我随音乐跳起华尔兹，却不知道音乐（或歌词）在表达什么的时候，我确实可以被形容为头脑简单，四肢发达。不巧的是，作为舶来文化的体育舞蹈，所用的舞曲大部分也来自于西方，特别是歌曲里唱的都是英文歌词的时候，我常常听不懂在唱什么。&lt;/p&gt;
&lt;p&gt;欣赏像 Slavik &amp;#x26; Karina 这类高水平运动员表演的时候，常看到他们的嘴形跟着舞曲一起唱，我想那一定是基于理解音乐内容的基础上完全投入的自然舞蹈流露，才会令这些小动作看起来那么自然且动情。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=F9FMjkflII4&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;![Slavik Kryklyvyy &amp;#x26; Karina Smirnoff - Samba, Miami 2017](./Slavik Kryklyvyy &amp;#x26; Karina Smirnoff - Samba, Miami 2017.jpg)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=F9FMjkflII4&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Slavik Kryklyvyy &amp;#x26; Karina Smirnoff - Samba, Miami 2017&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;细心的同学很快就把小动作“学”到了，听一位同学半开玩笑说“不知道嘴巴怎么动，就一边跳一边念拼音 a b c d”。可最后呈现的效果做作又可笑，后来老师实在看不下去了，课上专门强调叫我们跳舞时候嘴巴不要乱念叨。&lt;strong&gt;盲目模仿流于表面，仿若东施效颦&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;自那以后我慢慢意识到，&lt;strong&gt;要跳好体育舞蹈，需要更多的理解音乐，除学好英语之外，就是要学会聆听音乐了&lt;/strong&gt;。同时，也为我舞学的不好找了个理由🤣。&lt;/p&gt;
&lt;h2 id=&quot;二飘渺神秘&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BA%8C%E9%A3%98%E6%B8%BA%E7%A5%9E%E7%A7%98&quot; aria-label=&quot;二飘渺神秘 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;二、飘渺神秘&lt;/h2&gt;
&lt;p&gt;在报社做摄影工作时经常用 Photoshop，当一张照片呈现在屏幕上，你可以很直观的在平面空间中观察和反复修改它，无论整体还是放大局部，都能在修改后一眼看出变化，具体又直观。&lt;/p&gt;
&lt;p&gt;在我对照片处理方式习以为常后，偶尔处理声音文件，立刻就被它**飘渺神秘的特性&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;**难住。面对声音处理软件中呈现出来的一串波形图，除了音量大小之外我看不出来任何东西（废话，声音当然是要用听的🙄）。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d640975ba24ce13931da0757d2e880db/7b58d/waveform-in-premiere.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.736842105263165%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACK0lEQVR42o2R61PaQBTFk4B58ApEwwQQhKgUUQjT8pKHgsSxrRUGH7SCtp+d6ah9/fk7pzcb1GmnH/rhzNl779mdnfsT1k/7sOZHsGYDpOYjWJ+GsC4PkFqMlnKRuj32deP6uj7i2fTnYz737njZrYYDIXnWh3H/AZFhFXIxA3k7jVC3DK1ZhLqXg+oUoNZsLqWaJy9AqWxA2c3xTKhXhrKTheLkka2VIcTSKUSOa1C20hAEgSsQj0AMBp7rf0oUX7TsZXJZCIlKEfr1AbRKnoYCxIAEOWc+PyqqMkRlBaIcXPqK7yQprCJgRCnj15miDcG6GmH1YQxjMUTstIGoW4M+7SB+2YU+bvnnq77vsz7iF12e8zz+8QCJmyFle4iN2yi0ahDS8xOsPo5hfj2D+W0K82GCtbtTJH+cc5nfp0j+vHiR13uc+P1fVJOb92PeL/YbBOVdB8bde0QIhLyRhJw1EaZla1Va/mYKSmkdainLpRA0lWqFwHkzjSCF6ttQbAsy9bNOCYJOUMKu8xeU8H9AeQIj/AnFoFfji0P+I1GSfCj002DChyJpCiQPgCcCJD0BIRCBiIrgms4zoiYj82qToMxc2uEExu0RYpM2oievoS8Xrp93oBOIxHxAYHpIXB9yMDrlPE8sBjC+jHg2Nt33odjtDrPdt8weDFm+WWX5RoXl3+yR07lZYXbLYaV+kxWajj9baqNeYcVune0etpnj9tjucJ9t7ZTYb8/aNYOv7T9sAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/d640975ba24ce13931da0757d2e880db/5ed05/waveform-in-premiere.webp 190w,
/static/d640975ba24ce13931da0757d2e880db/9d76b/waveform-in-premiere.webp 380w,
/static/d640975ba24ce13931da0757d2e880db/33466/waveform-in-premiere.webp 760w,
/static/d640975ba24ce13931da0757d2e880db/ca244/waveform-in-premiere.webp 1140w,
/static/d640975ba24ce13931da0757d2e880db/e9f1f/waveform-in-premiere.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/d640975ba24ce13931da0757d2e880db/253c6/waveform-in-premiere.png 190w,
/static/d640975ba24ce13931da0757d2e880db/810ee/waveform-in-premiere.png 380w,
/static/d640975ba24ce13931da0757d2e880db/b4918/waveform-in-premiere.png 760w,
/static/d640975ba24ce13931da0757d2e880db/c0204/waveform-in-premiere.png 1140w,
/static/d640975ba24ce13931da0757d2e880db/7b58d/waveform-in-premiere.png 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/d640975ba24ce13931da0757d2e880db/b4918/waveform-in-premiere.png&quot;
            alt=&quot;waveform-in-premiere&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;处理声音只能先一遍遍地回放它找到位置，修改后再一遍遍回放来比较效果。不像照片将画面凝固在某一时间，声音虽然也可以被录制“凝固”下来，但与照片不同——时间完全凝固时就听不到什么了。&lt;strong&gt;声音只存在时间流逝中，稍纵即逝的神秘令我难受又好奇&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&quot;三离别时刻&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%B8%89%E7%A6%BB%E5%88%AB%E6%97%B6%E5%88%BB&quot; aria-label=&quot;三离别时刻 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;三、离别时刻&lt;/h2&gt;
&lt;p&gt;坐在离别的火车上听 Time to say goodbye，感触颇深。这个已经在&lt;a href=&quot;/2019/09/piano-solo-time-to-say-goodbye&quot;&gt;另外一篇&lt;/a&gt;中写过了。&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/q0chGWzSBrU?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;h2 id=&quot;四音乐无需懂&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%9B%9B%E9%9F%B3%E4%B9%90%E6%97%A0%E9%9C%80%E6%87%82&quot; aria-label=&quot;四音乐无需懂 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;四、音乐无需懂&lt;/h2&gt;
&lt;p&gt;在 &lt;a href=&quot;https://mp.weixin.qq.com/s?src=11&amp;#x26;timestamp=1603938565&amp;#x26;ver=2673&amp;#x26;signature=7G2p26EnAb-iVzZVvB3pleQm8a0NTbndQT83sPZRQCzPy0VqCC*xSWHPgkhy12eQUtqEkaytcQN18LqNXSbN8CfVPL**edE4JW9f29lqwlOJpYif2f9KVGagdUqTFm0g&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;WeThinker 《聆听音乐》读书会&lt;/a&gt;上，主读人张娜老师在预热分享上讲的话，让我产生的一点联想，下面是群消息摘录：&lt;/p&gt;
&lt;h3 id=&quot;我&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%88%91&quot; aria-label=&quot;我 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;我：&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;昨天张娜老师说她的老师讲一句话“古典音乐不需要懂”，让我想起来电影&lt;a href=&quot;https://www.imdb.com/title/tt6723592/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;天能（Tenet）&lt;/a&gt;一句台词：Don’t try to understand it, feel it.
想解读古典音乐，就像想搞懂克里斯托弗·诺兰烧脑的电影剧情一样困难。我这个门外汉也能听出古典音乐中所蕴含的情绪，并陶冶其中，也确实不用学究其中的原理。但就像烧脑电影中的惊人谜题一样，越深入研究越有趣。
分享个我为什么想学习它的原因之一。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;张娜&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%BC%A0%E5%A8%9C&quot; aria-label=&quot;张娜 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;张娜：&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;“音乐无需懂”，首要是不必以追求“懂”艺术音乐而在渐近之前就设定一个无形的障碍和门槛。
如果能够在反复聆听作品的过程中，与个人的感性直觉体验、知识储备——无论是否音乐领域的、经历阅历，运用充分积极的想象加以链接，艺术音乐一定可以“懂”的。
谢谢大家贡献了那么多非常优质的资料资源，我们不已经都在艺术音乐的大门里了吗？再不是门外汉。
搬走这个无形的障碍，甚至它根本不存在，“Don’t try to understand it, feel it.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;五寻回想象力&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BA%94%E5%AF%BB%E5%9B%9E%E6%83%B3%E8%B1%A1%E5%8A%9B&quot; aria-label=&quot;五寻回想象力 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;五、寻回想象力&lt;/h2&gt;
&lt;p&gt;童年末期，我开始对一些动画片感到索然无味（猫和老鼠至今都看不厌），但仍想通过看动画片抓住想象力的魔力。可时间留不住，年龄和理性增长同时想象力不免减退，对我而言，&lt;strong&gt;想象力是创造力和快来的来源&lt;/strong&gt;，所以当我聆听古典音乐，思绪飞翔进入想象力的天空时，我仿佛找回了那个天真的年代&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;见《聆听音乐》中文第5版，前言，插图小节。中文第7版没有这一小节了。还有第5版第1章末尾赏析《查拉图斯特拉如是说》，直接写“在用做斯坦利·库布里克的电影《2001年太空漫游》一片中的音乐时”，而第7版则含糊写成“被用来作为一部电影的配乐”，这些改变不知是原版还是翻译造成的，后面还未看完，至此觉得中文第7版很多主题在目标周围绕，就是没命中上去。&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;《聆听音乐》中文第5版第1章的“为什么听古典音乐？”小节中，公众的典型回答其中有 - 3.“在欣赏交响曲时，我的思绪飞扬到美妙的儿童时代。”。中文第7版中把这个例证去掉了。&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Moment.js 使命完成，替代品推荐]]></title><description><![CDATA[流行 JS 时间日期库 Moment.js 宣布进入维护模式，他已完成属于他时代的使命。本文翻译官方声明的开头部分，推荐替代品，并向他表示我的感谢。]]></description><link>https://www.berlinchan.com/2020/10/replace-moment-with-date-fns</link><guid isPermaLink="false">https://www.berlinchan.com/2020/10/replace-moment-with-date-fns</guid><pubDate>Fri, 02 Oct 2020 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;看到新闻 &lt;a href=&quot;https://society.solidot.org/story?sid=65553&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Moment.js 宣布进入遗留状态&lt;/a&gt; 的当天觉得挺惊奇，没想到这一天这么快就到来，不禁感叹前端开发、Javascript 世界变化之快以及高效。到 &lt;a href=&quot;https://momentjs.com/docs/#/-project-status/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Moment.js 官网&lt;/a&gt;看了声明，很清楚的说明项目的情况和考虑，前面一部分翻译如下：&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;项目状态&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E9%A1%B9%E7%9B%AE%E7%8A%B6%E6%80%81&quot; aria-label=&quot;项目状态 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;项目状态&lt;/h2&gt;
&lt;p&gt;Moment.js 已被数百万个项目运用，我们为在 Web 上更好使用时间日期作出贡献而感到高兴。截至 2020年9月，Moment 每周的下载量超过 1200万！然而，Moment 是为 JavaScript 生态系统的上一个时代构建的。如今的现代 Web 看起来已大不相同。这些年来，Moment 有所发展，但其设计基本上与 2011年创建时相同。鉴于有众多项目依赖它，我们选择优先考虑稳定性而不是新功能。&lt;/p&gt;
&lt;p&gt;例如，Moment 对象是可变的。这是有关 Moment 的常见抱怨来源。我们在&lt;a href=&quot;https://momentjs.com/guides/#/lib-concepts/mutability/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;使用指南&lt;/a&gt;中提到了该问题，但仍给大多数新用户带来意外困扰。对于每个使用 Moment 的项目，更改为不可变对象将是一项重大的断层式更新。创建不可变的 “Moment v3” 将是一项艰巨的任务，并将使 Moment 完全成为一个不同的库。由于这已在其他库中完成，因此我们认为保留可变的 API 更为重要。&lt;/p&gt;
&lt;p&gt;在现代应用程序中针对 Moment 的另外一个问题是他的大小。Moment 在流行的 “tree shaking” 算法中工作的不太好，导致 Web 应用程序包的曾大。若需要国际化或时区支持，Moment 会非常大（译注：当前的 v2.29.0 官网上显示 moment.min.js gz压缩后为 18.2k，moment-with-locales.js gz压缩则增至 73.2k）。现代 Web浏览器（和 Node.js）通过规范编号为 &lt;a href=&quot;https://ecma-international.org/ecma-402/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ECMA-402&lt;/a&gt; 的&lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Intl&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt;&lt;/a&gt; 对象暴露对国际化和时区支持。&lt;a href=&quot;https://moment.github.io/luxon/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Luxon&lt;/a&gt; 等其他库利用这一优势，减少或取消自己提供数据文件的需要。&lt;/p&gt;
&lt;p&gt;最近，Chrome 开发工具&lt;a href=&quot;https://twitter.com/addyosmani/status/1304676118822174721&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;开始因其大小而建议替换掉 Moment&lt;/a&gt;。我们普遍支持这一举措。&lt;/p&gt;
&lt;p&gt;你可能想阅读：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dockyard.com/blog/2020/02/14/you-probably-don-t-need-moment-js-anymore&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;你可能不再需要 Moment.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/you-dont-need/You-Dont-Need-Momentjs/blob/master/README.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;你（可能）不需要 Moment.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inventi.studio/en/blog/why-you-shouldnt-use-moment-js&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;为什么你不应该用 Moment.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/4-alternatives-to-moment-js-for-internationalizing-dates/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;4 个日期国际化的 moment.js 替代品&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Moment 团队已详细讨论了这些问题。我们认识到许多现有项目可能会继续使用 Moment，但是我们不鼓励在以后的新项目中使用。作为替换，我们推荐当今在现代应用中使用的&lt;a href=&quot;https://momentjs.com/docs/#/-project-status/recommendations/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;绝佳选择&lt;/a&gt;。我们还想推广 JavaScript 的 &lt;a href=&quot;https://momentjs.com/docs/#/-project-status/future/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Temporal&lt;/code&gt;&lt;/a&gt; 新增提案，该功能正在寻求反馈和贡献者。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;我们认定 Moment 现在是处于维护模式下的遗留项目。他并没有死，但他确实已完成使命。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这意味着：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不再添加新功能特性。&lt;/li&gt;
&lt;li&gt;不再修改 Moment 的 API 为不可变对象。&lt;/li&gt;
&lt;li&gt;不再解决 tree shaking 和包大小问题。&lt;/li&gt;
&lt;li&gt;不再有大版本更新（没有 version 3）。&lt;/li&gt;
&lt;li&gt;可能不再修正 bug，特别是较久远的问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于 Moment 国际化语言文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我们可能不接受对区域设置字符串或本地化日期格式的修正，特别是在它们的当前形式已经被成功地论证过的情况下。&lt;/li&gt;
&lt;li&gt;您必须使用重要的，非轶事证据来为地区变更提出新的令人信服的论据，以支持您的立场。&lt;/li&gt;
&lt;li&gt;如果您要更改的字符串或格式反映在 &lt;a href=&quot;http://cldr.unicode.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;CLDR&lt;/a&gt; 中，那么您必须首先在此处提交更改并接受它。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但由于 Moment 已在数百万个现有项目中使用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当出现严重安全问题时，我们将予以解决。&lt;/li&gt;
&lt;li&gt;在 &lt;a href=&quot;https://www.iana.org/time-zones&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;IANA时区数据库&lt;/a&gt; 发布后更新 Moment-Timezone 的数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;替代品&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9B%BF%E4%BB%A3%E5%93%81&quot; aria-label=&quot;替代品 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;替代品&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://momentjs.com/docs/#/-project-status/recommendations/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;官方推荐&lt;/a&gt;如下几个库：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://moment.github.io/luxon/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Luxon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://day.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Day.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://date-fns.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;date-fns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://js-joda.github.io/js-joda/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;js-Joda&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我建议你去每个库的官网看看他们各自的特色和优点，然后再决定选择哪个作为替代。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;其中 &lt;a href=&quot;https://moment.github.io/luxon/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Luxon&lt;/a&gt; 的作者是 Moment 长期贡献者 &lt;a href=&quot;https://github.com/icambron&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Isaac Cambron&lt;/a&gt;，算是 Moment 的灵魂续作。&lt;a href=&quot;https://day.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Day.js&lt;/a&gt; 有着与 Moment 类似的 API，快速替换的好选择。&lt;a href=&quot;https://date-fns.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;date-fns&lt;/a&gt; 遵循函数式编程规范，直接操作 JS Date 对象，这是我的首选。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;我与-moment-的故事&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%88%91%E4%B8%8E-moment-%E7%9A%84%E6%95%85%E4%BA%8B&quot; aria-label=&quot;我与 moment 的故事 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;我与 Moment 的故事&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/2016/12/why-quit-media-field/&quot;&gt;2015下半年转行 Web 开发&lt;/a&gt;的第一份工作中，首次看到同事使用 Moment 后，我就对他&lt;a href=&quot;https://momentjs.com/docs/#/displaying/format/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;格式化日期操作&lt;/a&gt;的便利性所吸引（不用再 getFullYear, getMonth …），然后被我运用到各种不同项目中。&lt;/p&gt;
&lt;p&gt;上面提到的可变对象问题，今年在公司一个可视化大屏项目中，确实令我困扰了一下，直到我反复试验后才注意到 &lt;a href=&quot;https://momentjs.com/docs/#/manipulating/add/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;add()&lt;/code&gt;&lt;/a&gt; 操作后可变对象的问题。不过这只是一个小插曲而已，仍然要感谢 Moment 在这段时间中给我开发所带来的便利和愉悦。&lt;/p&gt;
&lt;p&gt;Moment 的发展现状正是 Javascript 生态&lt;em&gt;增长且发展&lt;/em&gt;的一个正面例子，是当今热门词汇&lt;a href=&quot;https://youtu.be/nURtMLCanp0&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;em&gt;内卷化&lt;/em&gt;&lt;/a&gt;的一个反面例证。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[升级 07 年的 HP Compaq v3000 笔记本电脑]]></title><description><![CDATA[给 07年买的 HP v3000 笔记本电脑升级硬件包括 CPU、内存、硬盘和无线网卡]]></description><link>https://www.berlinchan.com/2020/07/upgrade-old-hp-compaq-v3000-notebook</link><guid isPermaLink="false">https://www.berlinchan.com/2020/07/upgrade-old-hp-compaq-v3000-notebook</guid><pubDate>Thu, 02 Jul 2020 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/-p64Iaf7dio?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;p&gt;一台 2007 年买的 HP Compaq v3000 笔记本电脑，陪伴我整个大学年代，学网页制作、3D动画、校报编辑、摄影、玩 PSP，毕业前夕出去找网页制作的工作，都把它装在我的斜挎运动包里带着给别人演示作品，对它有很多回忆。&lt;/p&gt;
&lt;p&gt;13 年过去，现在运行 Win10 速度很慢，花 202元买了些部件给它升级下。包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU: Intel Core Duo T2050 1.6GHz -&gt; T7200 2GHz&lt;/li&gt;
&lt;li&gt;RAM: 2.5G -&gt; 4G&lt;/li&gt;
&lt;li&gt;Storage: 80G HDD -&gt; 120G SSD&lt;/li&gt;
&lt;li&gt;Wireless: 3945ABG -&gt; 4965 AGN&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;升级无线网卡-3945&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%8D%87%E7%BA%A7%E6%97%A0%E7%BA%BF%E7%BD%91%E5%8D%A1-3945&quot; aria-label=&quot;升级无线网卡 3945 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;升级无线网卡 3945&lt;/h2&gt;
&lt;p&gt;升级换掉 3945 无线网卡遇到问题，v3000 主板的 BIOS 中有硬件白名单 whitelist，在 &lt;a href=&quot;https://www.bios-mods.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;bios-mods&lt;/a&gt; 论坛中有找到个 &lt;a href=&quot;https://www.bios-mods.com/forum/Thread-REQUEST-HP-DV2000-Wifi-whitelist-removal-sp36869?pid=65656#pid65656&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;HP DV2000 白名单修改的 BIOS&lt;/a&gt;，被修改的原 DV2000 BIOS 更新文件与 v3000 的更新文件，文件名相同为 sp36869.exe，且 MD5 hash 相同，所以判断修改后的 BIOS 应该也可以通用。&lt;/p&gt;
&lt;p&gt;赌一把刷了试试，反而 3945、4965 两个都不能用了。修改的 BIOS 应该不是移除白名单，而是将白名单中的 3945 修改为其他型号。再进一步，只能自己修改 BIOS，考虑再三我还是放弃并刷回原 BIOS。&lt;/p&gt;
&lt;h2 id=&quot;可用内存为-3g&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%8F%AF%E7%94%A8%E5%86%85%E5%AD%98%E4%B8%BA-3g&quot; aria-label=&quot;可用内存为 3g permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;可用内存为 3G&lt;/h2&gt;
&lt;p&gt;T7200 CPU 是 64bit 的，也装了 64位的 Win10，已安装内存显示 4G，但可用内存为 3G，有 1G为硬件保留的内存，上网查说这好像是正常的，就没再研究。&lt;/p&gt;
&lt;h2 id=&quot;升级效果&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%8D%87%E7%BA%A7%E6%95%88%E6%9E%9C&quot; aria-label=&quot;升级效果 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;升级效果&lt;/h2&gt;
&lt;p&gt;升级后开机和操作响应速度快了很多，特别是硬盘的速度，不再动不动就 100%。足够下载、上网、文本处理等日常用途了。老机器再战 5年不成问题。&lt;/p&gt;
&lt;h2 id=&quot;补充-by-网友-老丹&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%A1%A5%E5%85%85-by-%E7%BD%91%E5%8F%8B-%E8%80%81%E4%B8%B9&quot; aria-label=&quot;补充 by 网友 老丹 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;补充 by 网友-老丹&lt;/h2&gt;
&lt;p&gt;朋友也是这款笔记本，升级2G内存，但外侧插槽不工作不知缘故，算了。Wifi 模块坏了，所以必须更换。&lt;a href=&quot;https://item.taobao.com/item.htm?spm=a1z09.2.0.0.28392e8dSnWf6N&amp;#x26;id=520474293817&amp;#x26;_u=t22tep0ba70&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;BCM94322MC&lt;/a&gt; 是 Dell 的，必须更换这个名，同时更改硬件ID：PCIVEN 14E48 DEV 432B SUBSYS 1380 103C，商家给的1379是错的。参见文章&lt;a href=&quot;http://www.biosrepair.com/bios/ibmbmd.htm&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;BiosRepair-修改BIOS添加网卡白名单问题&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;由于给他升级，勾起想换 CPU 的心，再说吧。目标可能是 T7400，三十多块钱。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[🦠 使用 GitHub Actions 自动填报健康打卡]]></title><description><![CDATA[First of first, in my opinion that was a bad idea, it almost take any effect to control pandemic spreading, but a violation personal privacy. 中国多地欲将健康码常态化，引发隐私担忧]]></description><link>https://www.berlinchan.com/2020/06/automatically-submit-COVID-19-report</link><guid isPermaLink="false">https://www.berlinchan.com/2020/06/automatically-submit-COVID-19-report</guid><pubDate>Sun, 14 Jun 2020 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;First of first, in my opinion that was a bad idea, it almost take any effect to control pandemic spreading, but a violation personal privacy.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://cn.nytimes.com/technology/20200527/china-coronavirus-surveillance/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;中国多地欲将健康码常态化，引发隐私担忧&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!-- endExcerpt --&gt;
&lt;p&gt;Many bad things (GFW…) like these full of my daily life, makes me feel tired, let me can not taking time to thinking things really matters.&lt;/p&gt;
&lt;p&gt;So, here we come a Github Action to automatically submit COVID-19 report.&lt;/p&gt;
&lt;p&gt;Find more detail at &lt;a href=&quot;https://github.com/BerlinChan/yg-covid-report-action&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;yg-covid-report-action&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[推荐三部关于吹哨人的电影]]></title><description><![CDATA[Recently I saw the movie ”Official Secret”, it’s based on a true story about Katharine Teresa Gun, who the British whistleblower, leaked information to the press about an illegal NSA spy operation designed to push the UN Security Council into sanctioning the 2003 invasion of Iraq. This reminds me other two movies I have seen:]]></description><link>https://www.berlinchan.com/2020/05/movies-about-whistleblower</link><guid isPermaLink="false">https://www.berlinchan.com/2020/05/movies-about-whistleblower</guid><pubDate>Fri, 29 May 2020 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;Recently I saw the movie ”&lt;a href=&quot;https://www.imdb.com/title/tt5431890/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Official Secret&lt;/a&gt;”, it’s based on a true story about Katharine Teresa Gun, who the British whistleblower, leaked information to the press about an illegal NSA spy operation designed to push the UN Security Council into sanctioning the 2003 invasion of Iraq.&lt;/p&gt;
&lt;p&gt;This reminds me other two movies I have seen:&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.imdb.com/title/tt4044364/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Citizenfour&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.imdb.com/title/tt1824254/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;We Steal Secrets: The Story of WikiLeaks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/33aca6fa4c2b199ef8aac10327d37e06/9d361/whistleblower-movie.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAMBAgQF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAeTZi0kzDH//xAAZEAEAAgMAAAAAAAAAAAAAAAABAgMQITP/2gAIAQEAAQUCiCCRk2br5WY//8QAFhEBAQEAAAAAAAAAAAAAAAAAABFB/9oACAEDAQE/AcR//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGRAAAgMBAAAAAAAAAAAAAAAAAAECEHED/9oACAEBAAY/AmxV0wjlf//EABwQAQACAQUAAAAAAAAAAAAAAAEAESEQMUFhkf/aAAgBAQABPyFSGQt6ioXxLsLNPb5x3n//2gAMAwEAAgADAAAAECPf/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAERMf/aAAgBAwEBPxDZKP/EABcRAAMBAAAAAAAAAAAAAAAAAAABETH/2gAIAQIBAT8Qeop//8QAGxABAAMAAwEAAAAAAAAAAAAAAQARITFBYYH/2gAIAQEAAT8QbNdoOkavI30TYqXAyc3um/SM0vNz6xqrZ//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/33aca6fa4c2b199ef8aac10327d37e06/5ed05/whistleblower-movie.webp 190w,
/static/33aca6fa4c2b199ef8aac10327d37e06/9d76b/whistleblower-movie.webp 380w,
/static/33aca6fa4c2b199ef8aac10327d37e06/33466/whistleblower-movie.webp 760w,
/static/33aca6fa4c2b199ef8aac10327d37e06/e8af0/whistleblower-movie.webp 800w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/33aca6fa4c2b199ef8aac10327d37e06/89129/whistleblower-movie.jpg 190w,
/static/33aca6fa4c2b199ef8aac10327d37e06/0036d/whistleblower-movie.jpg 380w,
/static/33aca6fa4c2b199ef8aac10327d37e06/e484a/whistleblower-movie.jpg 760w,
/static/33aca6fa4c2b199ef8aac10327d37e06/9d361/whistleblower-movie.jpg 800w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/33aca6fa4c2b199ef8aac10327d37e06/e484a/whistleblower-movie.jpg&quot;
            alt=&quot;whistleblower movie&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;由来&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%94%B1%E6%9D%A5&quot; aria-label=&quot;由来 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;由来&lt;/h2&gt;
&lt;p&gt;“吹哨人”这个说法的来源，引用 &lt;a href=&quot;https://zh.wikipedia.org/zh-hans/%E5%90%B9%E5%93%A8%E4%BA%BA&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;wiki&lt;/a&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“吹哨人”（Whistleblower）这个词起源自19世纪警察发现有罪案发生时会吹哨子的动作，以引起同僚以及民众的注意。而从此延伸出来，目前我们所指的“吹哨人”是为使公众注意到政府或企业的弊端，以采取某种纠正行动的人。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由此看，whistleblower 翻译为的“吹哨人”是个舶来词汇。我的印象中，该词在李文亮医生的相关报道中，被首次大量使用，从 &lt;a href=&quot;https://trends.google.com/trends/explore?date=2016-05-02%202020-05-29&amp;#x26;geo=CN&amp;#x26;q=%2Fm%2F0857g&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Google Trends&lt;/a&gt; 上看，&lt;strong&gt;该词语的趋势在&lt;a href=&quot;https://www.bbc.com/zhongwen/trad/chinese-news-51403740&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;李文亮医生于2月7日去世&lt;/a&gt;后2天到达峰值&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/76708ae4a05e4c8f86931705ca30d488/890a8/google-trends.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.73684210526315%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAABfUlEQVR42p1TS0vDQBDOb/MX9Sz07MFbz4KIoIeKB8FLQURoER8o9WSs0cRGY7SYpEmT7GNmnd0kWA+V2mFIhmG+nW9eVpqmjuN4nuf7vuu6QRCoxSJAJXEaR5MkSYQQVs7kLM/LsuScM8boi4sFEAUXFEhhAGCFuZpESVHk6v9iVT96dRVwxUetJBospcTlBIDS4EuK4xRr2lT6kmBpwOcBnL1CDV6eNpjAEx+PPfyhTak5aFZkQcMQTCpSYWzycKkzdx9h124ySwCmvQqJhDL9Qw2YViNXla/m7Ge4cQP7owY847Bjw7YNgwCuPyBmGmBHeOrDe45Ogg8xfhbqq1Reiq2BXDuS6xcQ5khgTAreGYr2FXQdPHDwdoJvM9Ub49Y9Hj7j5hBafdFzWd9neyPZuRPtS1L5NEVdM+2mkII4gW6e7gpUtumQ9khRlCUpNcdUp3B+SerFhcYC+DW/agvNXGAu0KK0URTFcZxlGV0YGXQx5AnDkO7k7w37BmBkZnTpM9UVAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/76708ae4a05e4c8f86931705ca30d488/5ed05/google-trends.webp 190w,
/static/76708ae4a05e4c8f86931705ca30d488/9d76b/google-trends.webp 380w,
/static/76708ae4a05e4c8f86931705ca30d488/33466/google-trends.webp 760w,
/static/76708ae4a05e4c8f86931705ca30d488/f4d24/google-trends.webp 1096w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/76708ae4a05e4c8f86931705ca30d488/253c6/google-trends.png 190w,
/static/76708ae4a05e4c8f86931705ca30d488/810ee/google-trends.png 380w,
/static/76708ae4a05e4c8f86931705ca30d488/b4918/google-trends.png 760w,
/static/76708ae4a05e4c8f86931705ca30d488/890a8/google-trends.png 1096w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/76708ae4a05e4c8f86931705ca30d488/b4918/google-trends.png&quot;
            alt=&quot;https://trends.google.com/trends/explore?date=2016-05-02%202020-05-29&amp;amp;geo=CN&amp;amp;q=%2Fm%2F0857g&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I recommend these three movies to you, the people with sense of justice, hope you 能！明白！&lt;/p&gt;</content:encoded></item><item><title><![CDATA[烂读者还是烂媒体？]]></title><description><![CDATA[劣质资讯内容就是抓住了人趋向选择简单容易的心理，引诱精神脆弱、空虚的读者，令他们人性中的堕落与贪婪暴露无遗。]]></description><link>https://www.berlinchan.com/2020/05/bad-audience-or-bad-media</link><guid isPermaLink="false">https://www.berlinchan.com/2020/05/bad-audience-or-bad-media</guid><pubDate>Sun, 17 May 2020 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8be792212faf80a5883fa91e0dac0b27/ca9b4/IMG_20160108_124213.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.26315789473684%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFAf/EABUBAQEAAAAAAAAAAAAAAAAAAAAC/9oADAMBAAIQAxAAAAF5DZhVAmv/xAAbEAADAAIDAAAAAAAAAAAAAAABAgMAEQQTIf/aAAgBAQABBQK9VXJXOu9s5OiAfUIVf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/AVf/xAAeEAACAQMFAAAAAAAAAAAAAAAAEQECECESQVJxgf/aAAgBAQAGPwKeWkVazuTh9EV+WR//xAAaEAACAwEBAAAAAAAAAAAAAAABEQAhMUFR/9oACAEBAAE/IXN0LkOYQgeJZQDo4l5JlhLBAQ8gno7P/9oADAMBAAIAAwAAABDEH//EABYRAQEBAAAAAAAAAAAAAAAAAAARUf/aAAgBAwEBPxBMf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QH//EAB4QAQEAAgEFAQAAAAAAAAAAAAERAEEhMVFhobHw/9oACAEBAAE/EDwiQwxel9+MfnbABZAD9cBIAewNTvnKfia12YYcjBNLvF00KX4Z/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/8be792212faf80a5883fa91e0dac0b27/5ed05/IMG_20160108_124213.webp 190w,
/static/8be792212faf80a5883fa91e0dac0b27/9d76b/IMG_20160108_124213.webp 380w,
/static/8be792212faf80a5883fa91e0dac0b27/33466/IMG_20160108_124213.webp 760w,
/static/8be792212faf80a5883fa91e0dac0b27/ca244/IMG_20160108_124213.webp 1140w,
/static/8be792212faf80a5883fa91e0dac0b27/e9f1f/IMG_20160108_124213.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/8be792212faf80a5883fa91e0dac0b27/89129/IMG_20160108_124213.jpg 190w,
/static/8be792212faf80a5883fa91e0dac0b27/0036d/IMG_20160108_124213.jpg 380w,
/static/8be792212faf80a5883fa91e0dac0b27/e484a/IMG_20160108_124213.jpg 760w,
/static/8be792212faf80a5883fa91e0dac0b27/32d87/IMG_20160108_124213.jpg 1140w,
/static/8be792212faf80a5883fa91e0dac0b27/ca9b4/IMG_20160108_124213.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/8be792212faf80a5883fa91e0dac0b27/e484a/IMG_20160108_124213.jpg&quot;
            alt=&quot;IMG 20160108 124213&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;“猎奇猎色、匪夷所思、荒诞无稽、耸人听闻”，5年前，我在大楚网地方频道做编辑的一次内部产品说明会上，这样形容我每天编辑处理的资讯，但未深入地讨论，无奈这些内容是经过长期市场 PV/UV 反馈检验，而受到同事们所默认的。&lt;/p&gt;
&lt;p&gt;是受众的偏好造就媒体产品的选题方向，还是媒体的水平培养读者的品味？当时我自己反思的简单结论认为，这是一个互相反馈促进的系统。&lt;/p&gt;
&lt;p&gt;最近看到一些文章/视频(如下，都墙🧱)，让我再次思考起这个话题。&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/rAh5TftvRlk?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://readandanalyse.blogspot.com/2013/04/blog-post_17.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;人民該替爛媒體的形成負多少責任？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这些文章的见解比我专业深刻得多，令我更好理解了这个话题，下面是我的一些浅显再思考（不想浪费时间，只看上面的链接就够了）。&lt;/p&gt;
&lt;hr&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;h2 id=&quot;2020-10-30-更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2020-10-30-%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;2020 10 30 更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2020-10-30 更新&lt;/h2&gt;
&lt;p&gt;推荐看 Netflix 的纪录片&lt;a href=&quot;https://www.imdb.com/title/tt11464826/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;《监视资本主义：智能陷阱》（The social dilemma）&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8f0b404eefeb7d7ab79102d5fc26f3b2/7bdc5/checkmate-humanity-the-social-dilemma.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.63157894736842%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEDBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHDpAhiK//EABcQAAMBAAAAAAAAAAAAAAAAAAECESD/2gAIAQEAAQUCApdZn//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABgQAAIDAAAAAAAAAAAAAAAAAAARICGR/9oACAEBAAY/Ailsf//EABoQAAIDAQEAAAAAAAAAAAAAAAERABAhYYH/2gAIAQEAAT8hWjPTBiDhxqcdf//aAAwDAQACAAMAAAAQTA//xAAWEQADAAAAAAAAAAAAAAAAAAAQESH/2gAIAQMBAT8QcH//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPxCH/8QAGxAAAgIDAQAAAAAAAAAAAAAAAREAIRBRkaH/2gAIAQEAAT8QdqM2gDsPAVmgvJWsjn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/8f0b404eefeb7d7ab79102d5fc26f3b2/5ed05/checkmate-humanity-the-social-dilemma.webp 190w,
/static/8f0b404eefeb7d7ab79102d5fc26f3b2/9d76b/checkmate-humanity-the-social-dilemma.webp 380w,
/static/8f0b404eefeb7d7ab79102d5fc26f3b2/33466/checkmate-humanity-the-social-dilemma.webp 760w,
/static/8f0b404eefeb7d7ab79102d5fc26f3b2/ca244/checkmate-humanity-the-social-dilemma.webp 1140w,
/static/8f0b404eefeb7d7ab79102d5fc26f3b2/4a436/checkmate-humanity-the-social-dilemma.webp 1280w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/8f0b404eefeb7d7ab79102d5fc26f3b2/89129/checkmate-humanity-the-social-dilemma.jpg 190w,
/static/8f0b404eefeb7d7ab79102d5fc26f3b2/0036d/checkmate-humanity-the-social-dilemma.jpg 380w,
/static/8f0b404eefeb7d7ab79102d5fc26f3b2/e484a/checkmate-humanity-the-social-dilemma.jpg 760w,
/static/8f0b404eefeb7d7ab79102d5fc26f3b2/32d87/checkmate-humanity-the-social-dilemma.jpg 1140w,
/static/8f0b404eefeb7d7ab79102d5fc26f3b2/7bdc5/checkmate-humanity-the-social-dilemma.jpg 1280w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/8f0b404eefeb7d7ab79102d5fc26f3b2/e484a/checkmate-humanity-the-social-dilemma.jpg&quot;
            alt=&quot;Checkmate humanity - The social dilemma&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;纪录片《监视资本主义：智能陷阱》的截图，PPT 中写着 “挫败人性”。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[学习面向对象编程设计原则]]></title><description><![CDATA[在用 TypeGraphQL 和工具 TypeDI开发中，新接触到依赖注入(dependency inject)、抽象类(abstract class) 概念，这些概念的提出都源自于面向对象编程的设计原则(Object Oriented Program design principles)。找到几篇通俗易懂的文章，让我这个自学出生的程序员都能理解，记下这几篇优秀的文章，反复阅读理解之。]]></description><link>https://www.berlinchan.com/2020/03/object-oriented-program-design-principles-study</link><guid isPermaLink="false">https://www.berlinchan.com/2020/03/object-oriented-program-design-principles-study</guid><pubDate>Tue, 31 Mar 2020 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;在用 &lt;a href=&quot;https://typegraphql.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TypeGraphQL&lt;/a&gt; 和工具 &lt;a href=&quot;https://github.com/typestack/typedi&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TypeDI&lt;/a&gt;开发中，新接触到&lt;strong&gt;依赖注入(dependency inject)&lt;/strong&gt;、&lt;strong&gt;抽象类(abstract class)&lt;/strong&gt; 概念，这些概念的提出都源自于面向对象编程的设计原则(Object Oriented Program design principles)。找到几篇通俗易懂的文章，让我这个自学出生的程序员都能理解，记下这几篇优秀的文章，反复阅读理解之。&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/liuhaorain/p/3747470.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;深入理解 DIP、IoC、DI 以及 IoC 容器&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ibm.com/developerworks/cn/java/l-javainterface-abstract/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;深入理解 abstract class 和 interface&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当然 OOP 设计原则不止这两个，还有诸如 Open-close principle, Do Not Repeat Yourself(DRY) 等。Keep searching!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[几款多端实时协作、同步的开发工具]]></title><description><![CDATA[在做一个消息列表同步时，使用 PubSub 模式针对各种场景补漏，代码越来越多且复杂难以维护。展开搜索，发现一些有用的工具]]></description><link>https://www.berlinchan.com/2020/03/real-time-multi-device-collaboration-devtools</link><guid isPermaLink="false">https://www.berlinchan.com/2020/03/real-time-multi-device-collaboration-devtools</guid><pubDate>Sun, 22 Mar 2020 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;在开发 &lt;a href=&quot;/2020/03/askent-give-up-prisma2&quot;&gt;Askent&lt;/a&gt; 的 Question 列表中，需要在 &lt;em&gt;管理、用户、演示&lt;/em&gt; 三端之间同步用户提问 Question 的状态。该列表需支持筛选、关键词搜索、排序、分页，使用 &lt;a href=&quot;https://www.apollographql.com/docs/apollo-server/data/subscriptions/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apollo Subscription&lt;/a&gt;(实质是 PubSub 模式) 做了很久，同步总是存在问题，如场景：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;管理端置顶一条用户端当前分页中不存在的 &lt;em&gt;Question&lt;/em&gt;，用户端相当于新增一条，而用户端当前分页中有该条的，则需更新&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;针对各种场景进行补漏，代码越来越多且复杂难以维护（回顾迭代过程类似 &lt;a href=&quot;https://deepstream.io/blog/20191104-realtime-search/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;deepstream 介绍 realtime search&lt;/a&gt; 的这篇文章），我意识到在这里使用 &lt;a href=&quot;https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PubSub 模式&lt;/a&gt;可能是个错误。&lt;/p&gt;
&lt;p&gt;于是展开一番搜索，发现如下开发工具：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://deepstream.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;deepstream&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://feathersjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Feathers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://emitter.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Emitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kuzzle.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Kuzzle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pubnub.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PubNub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;先记下来再逐一细看，也想起来之前要做一个多人协作文档编辑工具，找了些 OT 算法的实现库，但对于状态同步，那时没找到好用的工具，配合上面几个工具应该可以实现。&lt;/p&gt;
&lt;p&gt;找软件工具时，&lt;a href=&quot;https://alternativeto.net/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AlternativeTo&lt;/a&gt; 是个很好的参考，非常适合寻找类似工具，进行横向对比选择。&lt;strong&gt;有个搜索关键词的技巧&lt;/strong&gt;，在要找的软件后面加 &lt;code class=&quot;language-text&quot;&gt;like&lt;/code&gt; 并 Google 之，如搜：&lt;code class=&quot;language-text&quot;&gt;pubnub like&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;另外找到一篇有趣的文章 &lt;a href=&quot;https://glebbahmutov.com/blog/redux-and-rethinkdb/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;A single data structure holding program’s state has a name - database&lt;/a&gt;，探讨了将 &lt;a href=&quot;https://redux.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Redux&lt;/a&gt; and &lt;a href=&quot;https://rethinkdb.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;RethinkDB&lt;/a&gt; 结合使用，持久化、实时同步客户端状态，作者网站还有很多 Javascript 开发、测试主题文章，并且作者十分关注全球气候问题。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Askent 项目进展及准备弃用 Prisma2]]></title><description><![CDATA[完成大屏演示端，随着功能开发深入，仍在 Preview 重度开发的 ORM 工具 Prisma2 问题凸显。我对数据库、ORM 没经验，这些问题在之前的选型中确实未考虑到，准备用成熟的 Sequelize 替换之。]]></description><link>https://www.berlinchan.com/2020/03/askent-give-up-prisma2</link><guid isPermaLink="false">https://www.berlinchan.com/2020/03/askent-give-up-prisma2</guid><pubDate>Sun, 01 Mar 2020 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;进展&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%BF%9B%E5%B1%95&quot; aria-label=&quot;进展 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;进展&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/2020/02/project-askent-admin-audience-client&quot;&gt;继上篇&lt;/a&gt;后新完成大屏演示端、消息订阅、部分 API 分页。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/58005bf63e7a584d2a2a7ee830f22c29/ded7f/askent-wall-and-iphone-mockup.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.36842105263158%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC00lEQVR42i2R7WtbZRjGz78gONesbWqb19MkpycvTXqWJufkPelJUmNtFrsmdemW1gmiTHSKiMy1Q7euMDpQO3Fq2dyYH/wzNhjiy1DW4aeVgaLDYr4JP59ztgd+PBcX933dN9xSZHqe9z7c5O33z4v/IitvnMWlmihaneDULG61zKhSekKo+JQCI8E8I+MZnH6DQU+KYV+aA88nkeY7b7HzzVd8/tmn3Lp5na+vfUdEP4JRaqKXW0wXmyTz86RLLXRBWDPxh3PIkRw+NYt3wsAdTOFV0hzyGEhzInDr8hZr6+dYWzvLlas3iBiLBOImcrxGQGwZ1F6wsfR4ooY/ZuKLVvCoJXzhEl61IMLzDPpzSK3uaa5f22Fj4zwXPjnHzrffo5WOYYjtjJlF0pWj5KodstUlMmabdHnBRq8siJqXcSsZBlwJhrwaQ3IBSa/2WN/YZv3iFba2b3DmwlXUTJdI7hjR4gni5VUS5ms28cqq7YVzXRS9LVjk3TOX+Hhzmw/WLuGbbCA55DIDsokjUGdoYo6xyRYh4zhq4SSRyutM1k6hNd6xsXRUeGrhVeRUl1hxha0vfuCjzZ+4/OUv6GYPaShg4oq+iFbpEcsvI0+3CepdJnI9IqWTdkC8+qaNpS1Pza+IuiUSpR637+zy2+5jfrz3CL16AmnAV0RJLTDXOc1M6xT+w0eRkx07VMkeF8ErYqNVG0tbXtDo4tUWCWe77O3tAf/R7/dJzSwjHXTn8UQbHC4vM1VcxhM/gjvewieC/ck246klAqlXbCxtedbQsVgTJd3m/u7v7O/v8/fjf5gWx5QOuDIcdOd4zlPAIVdwKnVGww3R8BKuyaY9wJNoPUFoy3PF5hlWZpG1Jn/8+RfW6//bJ1nsID3jTPLsqC5Cszh8BQ6JIw2OzzAcrOIM1RiZqAtmn1K3PWeoisNfZkytcefuzzx8+Ih7vz4gNNXgfw1qtPa+Sa2xAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/58005bf63e7a584d2a2a7ee830f22c29/5ed05/askent-wall-and-iphone-mockup.webp 190w,
/static/58005bf63e7a584d2a2a7ee830f22c29/9d76b/askent-wall-and-iphone-mockup.webp 380w,
/static/58005bf63e7a584d2a2a7ee830f22c29/33466/askent-wall-and-iphone-mockup.webp 760w,
/static/58005bf63e7a584d2a2a7ee830f22c29/ca244/askent-wall-and-iphone-mockup.webp 1140w,
/static/58005bf63e7a584d2a2a7ee830f22c29/b0196/askent-wall-and-iphone-mockup.webp 1520w,
/static/58005bf63e7a584d2a2a7ee830f22c29/35696/askent-wall-and-iphone-mockup.webp 2400w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/58005bf63e7a584d2a2a7ee830f22c29/253c6/askent-wall-and-iphone-mockup.png 190w,
/static/58005bf63e7a584d2a2a7ee830f22c29/810ee/askent-wall-and-iphone-mockup.png 380w,
/static/58005bf63e7a584d2a2a7ee830f22c29/b4918/askent-wall-and-iphone-mockup.png 760w,
/static/58005bf63e7a584d2a2a7ee830f22c29/c0204/askent-wall-and-iphone-mockup.png 1140w,
/static/58005bf63e7a584d2a2a7ee830f22c29/54dc9/askent-wall-and-iphone-mockup.png 1520w,
/static/58005bf63e7a584d2a2a7ee830f22c29/ded7f/askent-wall-and-iphone-mockup.png 2400w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/58005bf63e7a584d2a2a7ee830f22c29/b4918/askent-wall-and-iphone-mockup.png&quot;
            alt=&quot;大屏演示端界面及观众移动端界面&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;center&gt;大屏演示端界面及观众移动端界面&lt;/center&gt;
&lt;h2 id=&quot;问题&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E9%97%AE%E9%A2%98&quot; aria-label=&quot;问题 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;问题&lt;/h2&gt;
&lt;p&gt;随着功能开发深入细节，仍在 Preview 开发状态，功能未完善的 ORM 工具 &lt;a href=&quot;https://github.com/prisma/prisma2&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Prisma2&lt;/a&gt; 问题凸显。我对数据库、ORM 没经验，这些问题在之前的选型中确实未考虑到，用到的 &lt;a href=&quot;https://github.com/prisma-labs/nexus&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nexus&lt;/a&gt; 框架也还在重度开发中，&lt;a href=&quot;https://github.com/prisma-labs/nexus-prisma/releases/tag/0.10.0&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;经常有 breaking changes&lt;/a&gt;，导致项目框架变动频繁，不过 Askent 项目一开始是作为我技术实践学习用的，折腾期间已学到很多。&lt;/p&gt;
&lt;p&gt;开发中的 Prisma2 关键功能严重缺失，导致产品功能无法实现，如今到了要“弃坑”的时候。缺失关键功能如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;关联表不支持 column 名自定义&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我的 &lt;code class=&quot;language-text&quot;&gt;prismal.schema&lt;/code&gt; 中有如下 model 定义：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token property&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;token object&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;id&lt;/span&gt;             &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;     &lt;span class=&quot;token directive function&quot;&gt;@default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property-query&quot;&gt;cuid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token directive function&quot;&gt;@id&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;votedQuestions&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Question&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token directive function&quot;&gt;@relation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;VotedUsersUserTable&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token property&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;token object&quot;&gt;Question&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;id&lt;/span&gt;           &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;  &lt;span class=&quot;token directive function&quot;&gt;@default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property-query&quot;&gt;cuid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token directive function&quot;&gt;@id&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;votedUsers&lt;/span&gt;   &lt;span class=&quot;token property&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;token directive function&quot;&gt;@relation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;VotedUsersUserTable&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;Question&lt;/code&gt; 多对多时会创建关联表，用 &lt;code class=&quot;language-text&quot;&gt;@relation(_name: String?, references: Identifier[]?)&lt;/code&gt; &lt;a href=&quot;https://github.com/prisma/prisma2/blob/master/docs/relations.md/#the-relation-attribute&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;定义关联表名&lt;/a&gt;，但无法自定义 field 名，只是无意义的 &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;B&lt;/code&gt;，如下&lt;/p&gt;
&lt;p&gt;关系表 &lt;code class=&quot;language-text&quot;&gt;VotedUsersUserTable&lt;/code&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;aId&lt;/td&gt;
&lt;td&gt;bId&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;不知道 AB 哪个是 userId，哪个是 questionId，虽然比较一下能确定，但这样也太不明确了，也就不好统计 &lt;code class=&quot;language-text&quot;&gt;Question.votedUsers&lt;/code&gt; 的 &lt;code class=&quot;language-text&quot;&gt;Count&lt;/code&gt;。&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;查询排序不支持多条件。详情见 &lt;a href=&quot;https://github.com/prisma/prisma/issues/62&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;issue: Order by multiple fields&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;不支持 Aggreation 查询。详情见 &lt;a href=&quot;https://github.com/prisma/prisma-client-js/issues/5&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;issue: Aggregations&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;Count 查询不支持条件。详情见 &lt;a href=&quot;https://github.com/prisma/prisma-client-js/issues/252&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;issue: Parameterized &lt;code class=&quot;language-text&quot;&gt;.count()&lt;/code&gt; queries&lt;/a&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些功能将会在以后逐渐完善，但并没有具体的实践计划，所以为了让 Askent 功能开发继续，暂只能弃坑，准备用成熟的 &lt;a href=&quot;https://sequelize.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sequelize&lt;/a&gt; 替换之，参照 &lt;a href=&quot;https://github.com/dooboolab/ts-apollo-sequelize&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ts-apollo-sequelize&lt;/a&gt; 项目。&lt;/p&gt;
&lt;p&gt;同时数据库准备用 MySQL 替换 SQLite，之前用 SQLite 是起步之初为简化开发环境。&lt;/p&gt;
&lt;p&gt;我很喜欢 &lt;code class=&quot;language-text&quot;&gt;prisma.schema&lt;/code&gt; 定义 model 的方式，屏蔽麻烦的数据库的建表工作，还有自动生成 &lt;a href=&quot;https://github.com/prisma/prisma-client-js&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PrismaClient&lt;/a&gt;，极大简化访问数据库操作。看到正在更新的 &lt;a href=&quot;https://www.nexusjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nexus 新官网&lt;/a&gt;，将要集成数据库访问，或许就是 &lt;code class=&quot;language-text&quot;&gt;nexus-prisma&lt;/code&gt; 将默认集成到其中，成为从数据库到 GraphQL 的一站式开发框架。很看好 Prisma2 的发展，期待看到它逐渐完善后的样子。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[看不见的恐怖——游戏《返校》通关感受]]></title><description><![CDATA[最令人毛骨悚然的在于游戏背景所影射真实历史中，正在我们身边很多人却无法察觉的恐怖。当可以思想多样迥异、观点自由碰撞，老师不再怕被讲授不同见解丢饭碗时，这种恐怖才算消散]]></description><link>https://www.berlinchan.com/2020/02/game-detention-experience</link><guid isPermaLink="false">https://www.berlinchan.com/2020/02/game-detention-experience</guid><pubDate>Tue, 25 Feb 2020 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;我是个胆小怕玩恐怖游戏的人，小时候狂风大雨的夜晚，吓得翻出不敢玩的生化危机光盘 💿，果断掰断后才敢睡觉。但《返校》这种程度我还很能接受，唯二两次被吓到是，第一次在礼堂外被鬼追，第二次是在厕所镜子前，可以说恐怖程度很轻，易于让大众接受的。掌握鬼和灯笼鬼的规律就很容易对付了，游戏后两章甚至都没有鬼。&lt;/p&gt;
&lt;p&gt;配合美术风格、音效，游戏营造出的恐怖氛围令人压抑，场景中的建筑、生活物品、日记行文，很有年代和文化氛围的特点，我非常喜欢。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.facebook.com/redcandlegames/posts/2266742710294344&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;『返校 - Detention』及『還願 - Devotion』兩款作品，同時成為哈佛大學燕京圖書館的館藏內容之一&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;游戏的恐怖在视听效果上只是轻量，&lt;strong&gt;最令人毛骨悚然的在于游戏背景所影射真实历史中，也是正在我们身边，很多人却无法察觉的恐怖&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;当可以思想多样迥异、观点自由碰撞，老师不再怕被讲授不同见解丢饭碗时，恐怖才算消散。当旁边小朋友问我在玩什么游戏，我告诉他后，他能自己在 Store 中找到，才能让我不再愤怒。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/34cdc32bb7c9c5eaddeaff05463c0ea6/7bdc5/game-detetion-good-ending.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAEDBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABrvOUIjK//8QAGxAAAQQDAAAAAAAAAAAAAAAAAwABEjECEST/2gAIAQEAAQUCAwHFzwJqWNJ7/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGRAAAgMBAAAAAAAAAAAAAAAAACEgIjEy/9oACAEBAAY/Ar9GMUP/xAAaEAACAgMAAAAAAAAAAAAAAAAAARARMWFx/9oACAEBAAE/IVyjDWRtzm1G4v/aAAwDAQACAAMAAAAQ8A//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPxCn/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Qh//EABsQAAMAAwEBAAAAAAAAAAAAAAABESExQWGx/9oACAEBAAE/ELRnTsb3wThNbUeLwxZnL3IzkZ3Z8j//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/34cdc32bb7c9c5eaddeaff05463c0ea6/5ed05/game-detetion-good-ending.webp 190w,
/static/34cdc32bb7c9c5eaddeaff05463c0ea6/9d76b/game-detetion-good-ending.webp 380w,
/static/34cdc32bb7c9c5eaddeaff05463c0ea6/33466/game-detetion-good-ending.webp 760w,
/static/34cdc32bb7c9c5eaddeaff05463c0ea6/ca244/game-detetion-good-ending.webp 1140w,
/static/34cdc32bb7c9c5eaddeaff05463c0ea6/4a436/game-detetion-good-ending.webp 1280w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/34cdc32bb7c9c5eaddeaff05463c0ea6/89129/game-detetion-good-ending.jpg 190w,
/static/34cdc32bb7c9c5eaddeaff05463c0ea6/0036d/game-detetion-good-ending.jpg 380w,
/static/34cdc32bb7c9c5eaddeaff05463c0ea6/e484a/game-detetion-good-ending.jpg 760w,
/static/34cdc32bb7c9c5eaddeaff05463c0ea6/32d87/game-detetion-good-ending.jpg 1140w,
/static/34cdc32bb7c9c5eaddeaff05463c0ea6/7bdc5/game-detetion-good-ending.jpg 1280w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/34cdc32bb7c9c5eaddeaff05463c0ea6/e484a/game-detetion-good-ending.jpg&quot;
            alt=&quot;game-detetion-good-ending&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;我玩出的是 Good Ending，也就是上图那一幕作为结尾。然后我在网上搜，看到简体中文版被修改或阉割对比，我觉得在此游戏上的审核事情，显得格外讽刺。&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/YfV8AX8PAL8?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[销户微博账号]]></title><description><![CDATA[看得透彻，所以如此。 我先用批量删除微博脚本，清空了所有短文，然后邮件客服处理注销账号。 注销之前想清除头像随便换个别的，但网页客户端上传图片一直失败，感觉太糟了。]]></description><link>https://www.berlinchan.com/2020/02/logout-weibo</link><guid isPermaLink="false">https://www.berlinchan.com/2020/02/logout-weibo</guid><pubDate>Fri, 07 Feb 2020 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;看得透彻，所以如此。&lt;/p&gt;
&lt;p&gt;我先用&lt;a href=&quot;https://greasyfork.org/en/scripts/14709-weibored-js/code&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;批量删除微博脚本&lt;/a&gt;，清空了所有短文，然后邮件客服处理注销账号。&lt;/p&gt;
&lt;p&gt;注销之前想清除头像随便换个别的，但网页客户端上传图片一直失败，感觉太糟了。&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bbb71cf667848f555d2c7e1361b205c4/ab86a/logout-weibo.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.21052631578947%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABkElEQVR42oVQ207CQBTcLzEopQJC7xew9xaw9AYWVAhooojwAKlgFNQH44O++c8eWEUfDCaTk+nuTmfmINdrH8f9Izd2Ko3MPr+bpsmsAIQguVxBFiSLE4zR+PZh8TK/ewYAGVxPK9UwCNuoUms2414yWy4fX29GCS+a6QyL9TCzeWk/J7K8IcqOINkAIBSjlZWK7fiIYhTA6dllrz8c3iSa4W3MsT+OA0jhuQeT2SMYIAiCtdoXg+vJcJTopr+TKq7dBJAByR2snLF/7kCGz3yhBGR9JSNN9ybT+9n86e39Y5oswka3073ygpO41Q+jDiyirNRMO1B113JCVa/DjuDQsALd9BB00Ix6vliSSo6m1xXNhReiZFtOxHAaZAZPaLEpgjkGgvTQAUKuCb2Z0BA2hwUrkPwP/wYCGTxageTI7I/JxmoLEMvrEFiSbY5T0wRFEHSaYL5+99v5T3EQdUbj5Cg6t6Ihb55SaosSaxSt0KxapJV/xOXDqltvS+WqajXMaiyrnmr4hunj3W4P/wny25B7bOB3hAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/bbb71cf667848f555d2c7e1361b205c4/5ed05/logout-weibo.webp 190w,
/static/bbb71cf667848f555d2c7e1361b205c4/9d76b/logout-weibo.webp 380w,
/static/bbb71cf667848f555d2c7e1361b205c4/33466/logout-weibo.webp 760w,
/static/bbb71cf667848f555d2c7e1361b205c4/ca244/logout-weibo.webp 1140w,
/static/bbb71cf667848f555d2c7e1361b205c4/475b0/logout-weibo.webp 1186w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/bbb71cf667848f555d2c7e1361b205c4/253c6/logout-weibo.png 190w,
/static/bbb71cf667848f555d2c7e1361b205c4/810ee/logout-weibo.png 380w,
/static/bbb71cf667848f555d2c7e1361b205c4/b4918/logout-weibo.png 760w,
/static/bbb71cf667848f555d2c7e1361b205c4/c0204/logout-weibo.png 1140w,
/static/bbb71cf667848f555d2c7e1361b205c4/ab86a/logout-weibo.png 1186w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/bbb71cf667848f555d2c7e1361b205c4/b4918/logout-weibo.png&quot;
            alt=&quot;logout-weibo&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[互动演示工具 Askent 已开发出管理与观众端雏形]]></title><description><![CDATA[武汉新冠状病毒肆掠，春节在家待着，休息期间继续给之前立的项目 Askent 添砖加瓦，目前实现增删活动、观众端提问、审核编辑提问、提问增删改实时同步。还差展示大屏端功能……]]></description><link>https://www.berlinchan.com/2020/02/project-askent-admin-audience-client</link><guid isPermaLink="false">https://www.berlinchan.com/2020/02/project-askent-admin-audience-client</guid><pubDate>Wed, 05 Feb 2020 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;武汉新冠状病毒肆掠，春节在家待着，休息期间继续给&lt;a href=&quot;/2019/12/create-presentation-tool-from-scratch&quot;&gt;之前立的项目 Askent&lt;/a&gt; 添砖加瓦，目前实现增删活动、观众端提问、审核编辑提问、提问增删改实时同步。还差展示大屏端功能，现有功能也只是实现了雏形，working in progress…&lt;/p&gt;
&lt;p&gt;项目是模仿 &lt;a href=&quot;https://sli.do/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sli.do&lt;/a&gt; 做的，随着功能开发深入研究后，
越发觉得这是一款&lt;a href=&quot;https://blog.sli.do/slido-brand-refresh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;精心设计和开发&lt;/a&gt;的产品👍。&lt;/p&gt;
&lt;p&gt;Repository: &lt;a href=&quot;https://github.com/BerlinChan/askent&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/BerlinChan/askent&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;screenshots&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#screenshots&quot; aria-label=&quot;screenshots permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Screenshots&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/abe63fa74866555862ff3bafb544e0b2/7b58d/askent-admin.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.36842105263158%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABP0lEQVR42p2S207CQBCG+14KqBQNxgufwQOtt2IE99SCWPERuVQgTUiAi8YLesjvztY2GKVGN/ky2+ns15l0rZrdQaPloNbsgPZ7R1cmEnWdJxott6Ruuzg4dmCfumi2XRyeuLDbDs7Ob7CvHdbAV5BCQGh8vfeUAOVMXjKdf/iCFMzU+b40cThUuO8xXFzeQkkJizEOznPyvTCR6efH0QjBU4Dx+KUkCJ7NuwIuOLp3fVw7XfT6DJb47G4bknueh8lkgvV6jTRNsdlskGUZFouF7lyamqJeKYnBgCYSu4VKKcznc0RRZGTFWq1W34T5mTxWCmezGeI4RpIkRkxrufxZWFApnE6nZlxaNG5Vh78K6VAYhqY7Gpk6jKJ3vL6F5lb8Sbj9YwroA0IQaqesUkgwxkqKq8U5Q9WZSuF/+AA3/jq9O1KGIQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/abe63fa74866555862ff3bafb544e0b2/5ed05/askent-admin.webp 190w,
/static/abe63fa74866555862ff3bafb544e0b2/9d76b/askent-admin.webp 380w,
/static/abe63fa74866555862ff3bafb544e0b2/33466/askent-admin.webp 760w,
/static/abe63fa74866555862ff3bafb544e0b2/ca244/askent-admin.webp 1140w,
/static/abe63fa74866555862ff3bafb544e0b2/e9f1f/askent-admin.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/abe63fa74866555862ff3bafb544e0b2/253c6/askent-admin.png 190w,
/static/abe63fa74866555862ff3bafb544e0b2/810ee/askent-admin.png 380w,
/static/abe63fa74866555862ff3bafb544e0b2/b4918/askent-admin.png 760w,
/static/abe63fa74866555862ff3bafb544e0b2/c0204/askent-admin.png 1140w,
/static/abe63fa74866555862ff3bafb544e0b2/7b58d/askent-admin.png 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/abe63fa74866555862ff3bafb544e0b2/b4918/askent-admin.png&quot;
            alt=&quot;Askent-admin screen&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;center&gt;管理后台新增活动&lt;/center&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3c5eff599f0bc039b46fe4c34309ce4f/7b58d/askent-admin-event.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.36842105263158%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABUUlEQVR42rWQ20sCQRTG978u7Snx0kshPfafFIEEYW1uumKl2YOQW6F7Yd2be5mZrzO7CkWESnTgY2bOd85vzoxSbmgo1TVUTnponOoo1VSU6x3sV1XKd3BwdI9Ks49qU8c+nWV90SM9DYfHPfK6aJ51sVfToAAccZyQUkBwCEEr5QRPwUmMxUAQACwhj+VeIRlitRcrj0EJoyUM4w3zuYmF58P3Q8SJBAkkaUa5AEvTRXeo4bx/gctRC63xFa4nbbRfb3FjqIWmKhaBByVNU4L48AgW0CRyn2UZvgbdj0fjCXcTDfpUx+B9gMHHA4b2CCPnOdfYeUEURwVwm7BMC7ZpIwxCBDR1mnzvE0JAsrYGOo4D27bpJR7CMATjLIesxTnfDTibzeivDViWhSiKfvg7TSiL5WSu6+bTyfOfgPI5Upvi/4Drht/EGMu1qU4CPwFt3EVNH1abtgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/3c5eff599f0bc039b46fe4c34309ce4f/5ed05/askent-admin-event.webp 190w,
/static/3c5eff599f0bc039b46fe4c34309ce4f/9d76b/askent-admin-event.webp 380w,
/static/3c5eff599f0bc039b46fe4c34309ce4f/33466/askent-admin-event.webp 760w,
/static/3c5eff599f0bc039b46fe4c34309ce4f/ca244/askent-admin-event.webp 1140w,
/static/3c5eff599f0bc039b46fe4c34309ce4f/e9f1f/askent-admin-event.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/3c5eff599f0bc039b46fe4c34309ce4f/253c6/askent-admin-event.png 190w,
/static/3c5eff599f0bc039b46fe4c34309ce4f/810ee/askent-admin-event.png 380w,
/static/3c5eff599f0bc039b46fe4c34309ce4f/b4918/askent-admin-event.png 760w,
/static/3c5eff599f0bc039b46fe4c34309ce4f/c0204/askent-admin-event.png 1140w,
/static/3c5eff599f0bc039b46fe4c34309ce4f/7b58d/askent-admin-event.png 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/3c5eff599f0bc039b46fe4c34309ce4f/b4918/askent-admin-event.png&quot;
            alt=&quot;Askent-admin-event screen&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;center&gt;管理后台，预览审核编辑问题&lt;/center&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f7e218d42828f6d15e8e93788475e8f6/7b58d/askent-audience-event.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.36842105263158%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABW0lEQVR42qWSzU/CMBiH9/8fIH6QOBU9mHjw4sGb3ggHIVFRxoeJMcQDH9sYG2y0W9v9bDtG+FJJbPKk3fZ7n75tZhycWTi+aKF02cJh2ULxpImCKSk9wTx9wbH5hqOy/HbegsqquSAzRXOd8nUHVzcWDCAFIFbInimZIZxNQGkIIteUhHqt3qeCL3KbCBiExliDZDAuwEUKLme2QpwIeD7BfE6XWQVd1BtJkmCTOI4x8XxwxrOm1f5pNnsexe3dpxYyxrZqt4QsYSAxQfOjgUbvGc2hhY7TRdd917TtDhpfr4hItKdQhkJ5X5VuBfftB1R7VdRHddQGdTz2a5qaRGX2EuZH7g8GGA5HCPwAYRjKI86RD0qpzuyq/VE4dsfyvjxEUaQFqpt87OrsT6HjOHBdVwsVnPP/dWjbtpYGQYDpdCp/DQIhhBbnmb2FCtWFujclUp36vr+U/cY3UGxDfgD5FIgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/f7e218d42828f6d15e8e93788475e8f6/5ed05/askent-audience-event.webp 190w,
/static/f7e218d42828f6d15e8e93788475e8f6/9d76b/askent-audience-event.webp 380w,
/static/f7e218d42828f6d15e8e93788475e8f6/33466/askent-audience-event.webp 760w,
/static/f7e218d42828f6d15e8e93788475e8f6/ca244/askent-audience-event.webp 1140w,
/static/f7e218d42828f6d15e8e93788475e8f6/e9f1f/askent-audience-event.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/f7e218d42828f6d15e8e93788475e8f6/253c6/askent-audience-event.png 190w,
/static/f7e218d42828f6d15e8e93788475e8f6/810ee/askent-audience-event.png 380w,
/static/f7e218d42828f6d15e8e93788475e8f6/b4918/askent-audience-event.png 760w,
/static/f7e218d42828f6d15e8e93788475e8f6/c0204/askent-audience-event.png 1140w,
/static/f7e218d42828f6d15e8e93788475e8f6/7b58d/askent-audience-event.png 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/f7e218d42828f6d15e8e93788475e8f6/b4918/askent-audience-event.png&quot;
            alt=&quot;Askent-audience-event screen&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;center&gt;观众端提问与投票&lt;/center&gt;
&lt;h2 id=&quot;tech-stack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tech-stack&quot; aria-label=&quot;tech stack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tech-stack&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;GraphQL&lt;/li&gt;
&lt;li&gt;Apollo&lt;/li&gt;
&lt;li&gt;Prisma2&lt;/li&gt;
&lt;li&gt;Nexus&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;MaterialUI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Askent 也是我的一个技术探索实践项目，所以技术选型偏激进，
特别是还在 preview 版本的 &lt;a href=&quot;https://github.com/prisma/prisma2/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Prisma2&lt;/a&gt;，
很多 &lt;a href=&quot;https://github.com/prisma/prisma-client-js/issues/5&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;aggregation 查询&lt;/a&gt;都还未实现，
且存在严重 bug，但活跃的社区应该能逐渐完善它。
我也借此以一位深入使用者的身份，旁观一个开源项目的发展。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.apollographql.com/docs/react/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Apollo-Client&lt;/code&gt;&lt;/a&gt; + &lt;a href=&quot;https://www.apollographql.com/docs/apollo-server/data/subscriptions/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Subscription&lt;/code&gt;&lt;/a&gt; 是消息实时同步的绝佳使用场景，来开发提问的多端实时同步非常方便。
它实质是 WebSockets 的 API 高级封装，在&lt;a href=&quot;https://www.apollographql.com/docs/react/data/subscriptions/#authentication-over-websocket&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;授权&lt;/a&gt;、&lt;a href=&quot;https://www.apollographql.com/docs/apollo-server/data/subscriptions/#subscription-filters&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;订阅过滤&lt;/a&gt;方面使用更加方便。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[从零开始，创建一个多端互动演示工具]]></title><description><![CDATA[互动演示工具让演示者与其观众双向沟通。结合我常用的开发工具，从零开始创建一个以“简洁、易用、安全”为标准的多端互动演示工具。]]></description><link>https://www.berlinchan.com/2019/12/create-presentation-tool-from-scratch</link><guid isPermaLink="false">https://www.berlinchan.com/2019/12/create-presentation-tool-from-scratch</guid><pubDate>Mon, 30 Dec 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;初识互动演示工具&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%88%9D%E8%AF%86%E4%BA%92%E5%8A%A8%E6%BC%94%E7%A4%BA%E5%B7%A5%E5%85%B7&quot; aria-label=&quot;初识互动演示工具 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;初识互动演示工具&lt;/h2&gt;
&lt;p&gt;参加过一次 &lt;a href=&quot;https://www.gatsbyjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gatsby&lt;/a&gt; 线上讨论会，会后提问环节用 &lt;a href=&quot;https://www.sli.do/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Slido&lt;/a&gt; 互动收集问题，主持人解答了投票数最高的几个问题，当时这款 Slido 工具清爽得界面和简单实用给我留下很深印象。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a6893d04d9c88e158f1c01c3b1e3b73d/26178/event-pesent-mode.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAACW0lEQVR42i2QXU/acBTG+xW2KDqRAi0tlEJ5rxVKKW21QmlFQd5UprLCmE6X6TI39+KIAbbM6eXiMvdVln2aRblYdrNx5Q5mye/iPOf/f855chA8sKDoDTXfDCVLdLodX+lG8ifuuSNyxKtbjkZSeUnKh4T0HBcPMOGpK7U/QRnIcm3n2+XXLxef9551jBc/1i5uFj/91T4OjbNh/ny4eDbUT4dKbygD/f9I3T/Z8xuP1kdqWwf9Xne73drdf8Ntf8/0f2u9K603yPUHxoeB/n6Q7Q3S765HHF+Lx1cj3v4UO79I9QRRl1ud7ulJ/2y9echpuyH1MZk08XiD4E0XbxLJJim03IJJJhtEYhPj6k52DY1U8JlVG2MgNp+GhZfdXNU1U42IJX6uHEsXOaXMSuW4uhqfr+Eh1eFX7LQ0TQlWkp/EZy2OmMURniLTiM2fQwOGE/yzNTxaIGIFKl7xCetMeiOoNAA6Vaf4mpsruWIFZ3jRHtBstDrlka2UiqCMnsiYot72C3VPYt2X2mLkZmj+UTSzG8s9AaLZvbC6HZBbPnGL4uskV8WjRUcwP01riJdb2dzpVM3XSb3tYstevu5LbfrTDwJyM6g8BBjJBEkLGzD61rniDC1BXiu1gEyQCivXxVzLGV7CIkWCrUB++Efx973JEVCAhKaLrWDRIjhRv271LkySCmLBxTFMmiDn4HIoY8AbFilAMBdbItgyAAVsg6YzuATXmfZpsBOc47iI3EUT41hqkpBA3/OoVjoLUyCVnTHsgVsYAyQ0rXRmilIhqQWXxpzCHTTxDx9xxC4bsk1bAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/a6893d04d9c88e158f1c01c3b1e3b73d/5ed05/event-pesent-mode.webp 190w,
/static/a6893d04d9c88e158f1c01c3b1e3b73d/9d76b/event-pesent-mode.webp 380w,
/static/a6893d04d9c88e158f1c01c3b1e3b73d/33466/event-pesent-mode.webp 760w,
/static/a6893d04d9c88e158f1c01c3b1e3b73d/02ec0/event-pesent-mode.webp 960w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/a6893d04d9c88e158f1c01c3b1e3b73d/253c6/event-pesent-mode.png 190w,
/static/a6893d04d9c88e158f1c01c3b1e3b73d/810ee/event-pesent-mode.png 380w,
/static/a6893d04d9c88e158f1c01c3b1e3b73d/b4918/event-pesent-mode.png 760w,
/static/a6893d04d9c88e158f1c01c3b1e3b73d/26178/event-pesent-mode.png 960w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/a6893d04d9c88e158f1c01c3b1e3b73d/b4918/event-pesent-mode.png&quot;
            alt=&quot;Slido 的提问互动演示界面&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;center&gt;Slido 的提问互动演示界面&lt;/center&gt;  
&lt;p&gt;这类软件在国内多被称为“现场/大屏互动”工具，&lt;a href=&quot;https://www.hixianchang.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Hi现场&lt;/a&gt;是这类产品的代表。这类产品功能丰富且贴近国情（微信、弹幕、摇一摇），同样界面也很接地气——眼花缭乱、纷繁复杂😵。我很不喜欢这种玩具化的界面，令人想起 2000年国内站长热时候的“&lt;a href=&quot;https://museum.berlinchan.com/2003/08/16/star-land-v2/#%E5%8F%8D%E6%80%9D&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;网页特效&lt;/a&gt;”，&lt;strong&gt;炫丽动感的界面吸引人们的注意力，反而降低有效信息传递的能力&lt;/strong&gt;，甚至从心理上引导人们的思考方式，将严肃交流讨论的思维活动变成一场儿戏——&lt;a href=&quot;https://www.zhihu.com/question/39259317&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;媒介即隐喻&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;当然，就其本身定位用于娱乐领域，这并不是问题。后来找到一个面向国内的产品&lt;a href=&quot;https://picpiclive.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;皮豆互动&lt;/a&gt;还不错。&lt;/p&gt;
&lt;h2 id=&quot;什么是互动演示工具&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BB%80%E4%B9%88%E6%98%AF%E4%BA%92%E5%8A%A8%E6%BC%94%E7%A4%BA%E5%B7%A5%E5%85%B7&quot; aria-label=&quot;什么是互动演示工具 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;什么是互动演示工具&lt;/h2&gt;
&lt;p&gt;简言之，&lt;strong&gt;互动演示工具让演示者与其观众双向沟通&lt;/strong&gt;。详细的介绍请参见《&lt;a href=&quot;https://ahaslides.com/blog/what-is-interactive-presentation-software-and-how-should-you-use-it/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;What Is Interactive Presentation Software And How Should You Use It?&lt;/a&gt;》。&lt;/p&gt;
&lt;p&gt;还有不少 &lt;a href=&quot;https://alternativeto.net/software/sli-do/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Slido 类似的工具&lt;/a&gt;，功能大同小异，且国内都可正常访问，推荐大家使用。&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;结合我常用的开发工具，从零开始创建一个以“简洁、易用、安全”为标准的多端互动演示工具，且叫它为 Askent，一个代号好记而已。好吧，既然又开始“造轮子”了，也不介意再造一个新词了😆，开始吧！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;repository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#repository&quot; aria-label=&quot;repository permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Repository&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/BerlinChan/askent&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/BerlinChan/askent&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Working in progress, continually updated…&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/2020/02/project-askent-admin-audience-client&quot;&gt;2月5日开发进度更新&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[使用 GnuPG 的常见问题]]></title><description><![CDATA[GnuPG 是赛门铁克 PGP 加密套件的自由软件替代品。记载一些使用 GPG 的教程与常见问题。]]></description><link>https://www.berlinchan.com/2019/12/common-problems-in-using-gnuPg</link><guid isPermaLink="false">https://www.berlinchan.com/2019/12/common-problems-in-using-gnuPg</guid><pubDate>Mon, 16 Dec 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/GNU_Privacy_Guard&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GNU Privacy Guard (GnuPG 或 GPG)&lt;/a&gt; 是赛门铁克 &lt;a href=&quot;https://zh.wikipedia.org/wiki/PGP&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PGP&lt;/a&gt; 加密套件的自由软件替代品。&lt;/p&gt;
&lt;p&gt;关于 GPG(PGP) 有两则轶事。&lt;/p&gt;
&lt;p&gt;作者齐默尔曼差点因 PGP 的强加密功能而被指控为“没有授权的军需品出口”。军需品——枪支、炸药、飞机和软件的出口是被限制的，齐默尔曼就使用了一种富有想象力的方法来对抗这一规定。他将 PGP 的全套源代码出版成一本书，于是作为书本出口，受到&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%AC%AC%E4%B8%80%E4%BF%AE%E6%AD%A3%E6%A1%88&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;第一修正案&lt;/a&gt;的保护。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 470px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ecc1ff6eb4ffcbbe03fa23bddde95acf/65eb2/PGP_Source_Code_and_Internals.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 106.3157894736842%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAVABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAIEAQP/xAAVAQEBAAAAAAAAAAAAAAAAAAABAP/aAAwDAQACEAMQAAABvSqbeStlAcA//8QAGxAAAgMAAwAAAAAAAAAAAAAAAQIAAxESEzL/2gAIAQEAAQUC1hATkRLePXZpWzV8zZ//xAAVEQEBAAAAAAAAAAAAAAAAAAARIP/aAAgBAwEBPwEj/8QAFREBAQAAAAAAAAAAAAAAAAAAESD/2gAIAQIBAT8BWP/EABkQAAMBAQEAAAAAAAAAAAAAAAABMRAhcf/aAAgBAQAGPwKEyoqH0Xm//8QAHBAAAwEAAgMAAAAAAAAAAAAAAAERIUFRMXGR/9oACAEBAAE/IUgpanY8l+GJDiRKnRibhzBCSy5R5hwNS8s9D//aAAwDAQACAAMAAAAQ18DC/8QAFhEBAQEAAAAAAAAAAAAAAAAAIREg/9oACAEDAQE/EA3H/8QAFxEBAQEBAAAAAAAAAAAAAAAAEQEAEP/aAAgBAgEBPxCTAYyc/8QAHBABAAMBAAMBAAAAAAAAAAAAAQARIUExUWGR/9oACAEBAAE/EGlK1pRj2d9LamOwDSHxrxCye+AFn5KQrsRaH3IJ7b0rhCxtndiGvQ9z/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/ecc1ff6eb4ffcbbe03fa23bddde95acf/5ed05/PGP_Source_Code_and_Internals.webp 190w,
/static/ecc1ff6eb4ffcbbe03fa23bddde95acf/9d76b/PGP_Source_Code_and_Internals.webp 380w,
/static/ecc1ff6eb4ffcbbe03fa23bddde95acf/d5ad8/PGP_Source_Code_and_Internals.webp 470w&quot;
              sizes=&quot;(max-width: 470px) 100vw, 470px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/ecc1ff6eb4ffcbbe03fa23bddde95acf/89129/PGP_Source_Code_and_Internals.jpg 190w,
/static/ecc1ff6eb4ffcbbe03fa23bddde95acf/0036d/PGP_Source_Code_and_Internals.jpg 380w,
/static/ecc1ff6eb4ffcbbe03fa23bddde95acf/65eb2/PGP_Source_Code_and_Internals.jpg 470w&quot;
            sizes=&quot;(max-width: 470px) 100vw, 470px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/ecc1ff6eb4ffcbbe03fa23bddde95acf/65eb2/PGP_Source_Code_and_Internals.jpg&quot;
            alt=&quot;Book: PGP Source Code and Internals&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;另一则，在&lt;a href=&quot;https://citizenfourfilm.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;纪录片《第四公民(Citizen Four)》&lt;/a&gt;（&lt;a href=&quot;https://www.bilibili.com/video/av24623314/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;在 BiliBili上观看&lt;/a&gt;）画面中配有使用 GPG 解密消息的画面，Edward Snowden 就是通过 GPG 与记者进行加密通讯的。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 663px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8de1abea099742f32f576f8d81fa9b18/14bc7/citizen_four_gpg.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.263157894736835%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABSUlEQVR42qVS226CQBClBuQWTHxRayC12Da1piZqgF32xrKlmsb//54eoAn1waRJTxaYnZkzZ2cHy7qBu9u4yptOp/PZbDKZuK4bBkEYBqPRyPojPM9bLu9Xq4fFYp6mj2maohCcfofewDuKoqADNAZyHMfGGMZ4nueEFJwzik+BXQZbCk4pYaw0pqa0uFy+drv3vq8fstYaGZwhhyollJJZloGMPchSCvi5YHWtT6dmvU4HcpIkQogSYcZQBQQI7/d7yKIKmFpXQnBdqaYxWNvt23BskJuPRshWEPrH4wG0VrYEKGpVFQ4C9wEJWJvN62/lGIUBdIUY5yVjFHxjdFFkaB5R+IXkhBZS8pfnpytl0TYm+4tBHgQJIVqrsr0CVdcVFnJwnPP580rZtu1+HuOxAwOTwON1gO0HPsbTTst1+x/BcWzr//gGEYZKCKNmDD8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/8de1abea099742f32f576f8d81fa9b18/5ed05/citizen_four_gpg.webp 190w,
/static/8de1abea099742f32f576f8d81fa9b18/9d76b/citizen_four_gpg.webp 380w,
/static/8de1abea099742f32f576f8d81fa9b18/9946f/citizen_four_gpg.webp 663w&quot;
              sizes=&quot;(max-width: 663px) 100vw, 663px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/8de1abea099742f32f576f8d81fa9b18/253c6/citizen_four_gpg.png 190w,
/static/8de1abea099742f32f576f8d81fa9b18/810ee/citizen_four_gpg.png 380w,
/static/8de1abea099742f32f576f8d81fa9b18/14bc7/citizen_four_gpg.png 663w&quot;
            sizes=&quot;(max-width: 663px) 100vw, 663px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/8de1abea099742f32f576f8d81fa9b18/14bc7/citizen_four_gpg.png&quot;
            alt=&quot;Using GPG decrypt message in documentary Citizen Four&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;下面记载一些使用 GPG 的教程与常见问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;通俗易懂的-gpg-教程&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E9%80%9A%E4%BF%97%E6%98%93%E6%87%82%E7%9A%84-gpg-%E6%95%99%E7%A8%8B&quot; aria-label=&quot;通俗易懂的 gpg 教程 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;通俗易懂的 GPG 教程&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ruanyifeng.com/blog/2013/07/gpg.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GPG入门教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vimacs.wehack.space/openpgp-about.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;从 keyserver 获取其他人的 GPG 公钥&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;最佳实践&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5&quot; aria-label=&quot;最佳实践 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;最佳实践&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://oguya.ch/posts/2016-04-01-gpg-subkeys/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GPG Subkeys&lt;/a&gt; 的建议。使用子密钥，仅当需要如下情景时才用主密钥对：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建新子密钥&lt;/li&gt;
&lt;li&gt;改变一个 UID 的首选项&lt;/li&gt;
&lt;li&gt;吊销 UID 或 子密钥&lt;/li&gt;
&lt;li&gt;为密钥签名或撤销现有签名&lt;/li&gt;
&lt;li&gt;创建新的 UID 或将现有的 UID 标记为主要&lt;/li&gt;
&lt;li&gt;更改主密钥或其任何子密钥的过期日期&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;请参考如下最佳实践文章：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://2xiangzi.blogspot.com/2016/09/perfect-gpg-keypair.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;如何创建完美的GPG密钥对&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mdrights/Digital-rights/blob/master/T%E6%95%99%E7%A8%8B%E5%92%8C%E7%AC%94%E8%AE%B0/2019-04-17-OpenPGP-%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5-Mailfence.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OpenPGP 数字签名最佳实践&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jackqqcn.wordpress.com/2008/06/22/gnupg-%E7%94%A8%E5%A4%9A%E4%B8%AAsub-keys%E4%BF%9D%E6%8A%A4primary-key/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GnuPG: 用多个sub keys保护primary key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://oguya.ch/posts/2016-04-01-gpg-subkeys/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GPG Subkeys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.tinned-software.net/create-gnupg-key-with-sub-keys-to-sign-encrypt-authenticate/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Create GnuPG key with sub-keys to sign, encrypt, authenticate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;只能上传一个-subkey-到-keyserver&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%8F%AA%E8%83%BD%E4%B8%8A%E4%BC%A0%E4%B8%80%E4%B8%AA-subkey-%E5%88%B0-keyserver&quot; aria-label=&quot;只能上传一个 subkey 到 keyserver permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;只能上传一个 subkey 到 keyserver&lt;/h2&gt;
&lt;p&gt;有的 keyserver 只支持一个 subkey，在文章&lt;a href=&quot;https://jackqqcn.wordpress.com/2008/06/22/gnupg-%E7%94%A8%E5%A4%9A%E4%B8%AAsub-keys%E4%BF%9D%E6%8A%A4primary-key/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GnuPG: 用多个sub keys保护primary key&lt;/a&gt;中有提及。&lt;/p&gt;
&lt;h2 id=&quot;简便易用的-gui-for-gnupg&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%AE%80%E4%BE%BF%E6%98%93%E7%94%A8%E7%9A%84-gui-for-gnupg&quot; aria-label=&quot;简便易用的 gui for gnupg permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;简便易用的 GUI for GnuPG&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;参见 &lt;a href=&quot;https://www.gnupg.org/software/frontends.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GnuPG Software Frontends&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;警告签名的子密钥--未经交叉验证&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%AD%A6%E5%91%8A%E7%AD%BE%E5%90%8D%E7%9A%84%E5%AD%90%E5%AF%86%E9%92%A5--%E6%9C%AA%E7%BB%8F%E4%BA%A4%E5%8F%89%E9%AA%8C%E8%AF%81&quot; aria-label=&quot;警告签名的子密钥  未经交叉验证 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;警告：签名的子密钥 … 未经交叉验证&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;WARNING: signing subkey &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;. is not cross-certified&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;验证签名的警告，下面给出的链接有很清楚的&lt;a href=&quot;https://gnupg.org/faq/subkey-cross-certify.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;解决方案与说明&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[学习 GraphQL 的疑问和解決方案]]></title><description><![CDATA[在试用 GraphQL 读写数据库和包装现有 REST 后，觉得确实是新鲜实用的 API 方案。对于这种 Schema First Development 的开发实践方法也很赞同。但留下了一些疑问(和解决方法)，于是记载一下。]]></description><link>https://www.berlinchan.com/2019/11/question-about-using-graphql</link><guid isPermaLink="false">https://www.berlinchan.com/2019/11/question-about-using-graphql</guid><pubDate>Fri, 29 Nov 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;先扯点别的&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%85%88%E6%89%AF%E7%82%B9%E5%88%AB%E7%9A%84&quot; aria-label=&quot;先扯点别的 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;先扯点别的&lt;/h2&gt;
&lt;p&gt;上一篇准备翻译 &lt;a href=&quot;https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;10++ TypeScript Pro tips/patterns with (or without) React&lt;/a&gt; 的，但翻译到一半后决定弃坑，强烈的挫败感，原因如下：&lt;/p&gt;
&lt;p&gt;原文最初写于 2018-10-29，自那以后 Typescript 和 React 都有很多更新(JS 的世界日新月异)，文中有些内容已不再适用。而且我觉得本文中有些建议，包括 Typescript 本身，过于追求了严格和限制，反而丧失 Javascript 的灵活优势(双刃剑，莫抬杠)。没有深入使用过 TS，所以还抱着怀疑的态度在学习中。&lt;/p&gt;
&lt;p&gt;技术文章有很多术语，翻译后反而不容易理解，原有单词放在英语语境中反而容易理解，有时候一整句保留所有术语后，翻译出来的中文就是几个”的”、“在”、“上”、“使用”，都怀疑还有没有必要翻译。&lt;/p&gt;
&lt;p&gt;下面正文开始。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;学习-graphql&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%AD%A6%E4%B9%A0-graphql&quot; aria-label=&quot;学习 graphql permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;学习 GraphQL&lt;/h2&gt;
&lt;p&gt;老有人拿 RESTful 和 GraphQL 比较，一直很好奇，最近我就看了看。跟官方教程走了一遍后，我更喜欢 GraphQL 的另一种实现 Apollo，相比 GraphQL 更容易理解，写起来更简洁。而且与之配合的客户端 Apollo Client 要比 Relay 也更简单(虽然客户端是非必选的)，于是就选择 Apollo 全家桶了。&lt;/p&gt;
&lt;p&gt;在用 GraphQL 读写数据库，和用 Apollo Server 包装了两个现有 REST API 初步试用后，觉得这确实是个很新鲜实用的 API 方案。对于这种 &lt;a href=&quot;https://www.apollographql.com/docs/tutorial/schema/#write-your-graphs-schema&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Schema First Development&lt;/a&gt; 的开发实践方法也很赞同。但留下了一些疑问(逐渐更新，找到解决方法也会更新，求教各位)，于是记载一下。&lt;/p&gt;
&lt;h2 id=&quot;1-如何模块化的管理本地状态&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%E5%A6%82%E4%BD%95%E6%A8%A1%E5%9D%97%E5%8C%96%E7%9A%84%E7%AE%A1%E7%90%86%E6%9C%AC%E5%9C%B0%E7%8A%B6%E6%80%81&quot; aria-label=&quot;1 如何模块化的管理本地状态 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 如何模块化的管理本地状态?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.apollographql.com/docs/react/caching/cache-configuration/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apollo cache&lt;/a&gt; 缓存从远程拉取的数据，同时也具有了管理本地状态数据的能力。Apollo 官方也&lt;a href=&quot;https://www.apollographql.com/docs/react/why-apollo/#combine-local--remote-data&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;推荐这么做&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Managing all your data with Apollo Client allows you to take advantage of GraphQL as a unified interface to all of your data.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这样做的好处是，可以不再关心状态数据从远端还是本地来，一概都用 GraphQL 的 Query 来查询，用 Mutation 来更改。但随着应用的功能增加和业务复杂化，所有的状态在一个 Apollo cache 中，且所有 &lt;code class=&quot;language-text&quot;&gt;typeDefs&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;resolvers&lt;/code&gt; 都写在一起，&lt;strong&gt;很快会变得无法维护&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在专门的状态管理方案中都有解决模块化问题的方案:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redux 中的 &lt;a href=&quot;https://redux.js.org/recipes/structuring-reducers/using-combinereducers#using-combinereducers&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;combineReducers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vuex 的&lt;a href=&quot;https://vuex.vuejs.org/zh/guide/modules.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;将 store 分割成模块（module）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;解决方案部分&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E9%83%A8%E5%88%86&quot; aria-label=&quot;解决方案部分 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;解决方案(部分)&lt;/h3&gt;
&lt;p&gt;在官方文档 &lt;a href=&quot;https://www.apollographql.com/docs/react/data/local-state/#code-splitting&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Code splitting&lt;/a&gt; 中提到了一点这个问题，用 &lt;code class=&quot;language-text&quot;&gt;client&lt;/code&gt; 实例的方法 &lt;code class=&quot;language-text&quot;&gt;addResolvers&lt;/code&gt;，添加模块中的 resolvers。但还是有问题：怎么分离 &lt;code class=&quot;language-text&quot;&gt;typeDefs&lt;/code&gt; 呢？&lt;/p&gt;
&lt;p&gt;现在看 Client 端 typeDefs 还是要写到一起去。虽然直接在 resolvers 中定义一个 typeDefs 中不存在的 &lt;a href=&quot;https://graphql.org/learn/queries/#fields&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;field&lt;/a&gt; 不会报错并可以用，但显然是个&lt;strong&gt;不优雅&lt;/strong&gt;的做法。&lt;/p&gt;
&lt;h3 id=&quot;结论&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%BB%93%E8%AE%BA&quot; aria-label=&quot;结论 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;结论&lt;/h3&gt;
&lt;p&gt;那目前我选择&lt;strong&gt;不用 Apollo Client 管理本地状态&lt;/strong&gt;，若需要，还是交给 Redux 或者 MobX 这类专门工具，这样客户端几乎不用写 &lt;code class=&quot;language-text&quot;&gt;typeDefs&lt;/code&gt; 和 &lt;code class=&quot;language-text&quot;&gt;resolvers&lt;/code&gt; 从而避开模块化问题了。&lt;/p&gt;
&lt;p&gt;值得反思的是，在事情复杂到需要模块化之前，你可能不需要什么状态管理，如 Redux 作者之一 Dan Abramov 说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://redux.js.org/faq/general#when-should-i-use-redux&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;I would like to amend this: don’t use Redux until you have problems with vanilla React.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;2-模板代码名称多&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%E6%A8%A1%E6%9D%BF%E4%BB%A3%E7%A0%81%E5%90%8D%E7%A7%B0%E5%A4%9A&quot; aria-label=&quot;2 模板代码名称多 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 模板代码名称多&lt;/h2&gt;
&lt;p&gt;比如我要取值 &lt;code class=&quot;language-text&quot;&gt;someList&lt;/code&gt;，用 Apollo Client 操作前先定义这个 schema:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;GET_SOME_LIST&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; gql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
  query GetSomeList {
    someList
  }
&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;GET_SOME_LIST&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;GetSomeList&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;someList&lt;/code&gt; 这三个名称分别是:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;给 &lt;code class=&quot;language-text&quot;&gt;useQuery&lt;/code&gt;(或 &lt;code class=&quot;language-text&quot;&gt;useMutation&lt;/code&gt;)传参数的常量，可类比 &lt;a href=&quot;https://redux.js.org/basics/actions#note-on-boilerplate&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Redux 中的 Action type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://graphql.org/learn/queries/#operation-name&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL 操作名&lt;/a&gt;，非必须&lt;/li&gt;
&lt;li&gt;GraphQL &lt;a href=&quot;https://graphql.org/learn/queries/#fields&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;field&lt;/a&gt; 名&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这三个名称看起来很类似，但是为了程序可读性它们都按这样的模板写好。现在名称很短还不成问题，但在上一个模块问题没解决前，可能需要用加前缀当作命名空间，避免命名冲突，比如：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SET_DASHBOARD_LIST_EXPANDED&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; gql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    mutation SetDashBoardListExpanded($ids:[ID]!){
        setDashBoardListExpanded(ids:$ids):[ID]!
    }
&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;啊，看不清了 😵&lt;/p&gt;
&lt;h3 id=&quot;解决方案&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88&quot; aria-label=&quot;解决方案 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;解决方案&lt;/h3&gt;
&lt;p&gt;老老实实写。用 &lt;a href=&quot;https://graphql-code-generator.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL Code Generator&lt;/a&gt; 辅助生成，能缓解部分手工写模板代码的工作。&lt;/p&gt;
&lt;h4 id=&quot;2019-12-24-更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2019-12-24-%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;2019 12 24 更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2019-12-24 更新&lt;/h4&gt;
&lt;p&gt;GraphQL Code Generator 的使用案例参考：&lt;a href=&quot;https://levelup.gitconnected.com/build-a-graphql-react-app-with-typescript-9661f908b26&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Build a GraphQL + React App with TypeScript&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;3-如何身份验证2019-12-04-更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%E5%A6%82%E4%BD%95%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%812019-12-04-%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;3 如何身份验证2019 12 04 更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 如何身份验证？(2019-12-04 更新)&lt;/h2&gt;
&lt;p&gt;这是我接触 GraphQL 的第一个问题。GraphQL 给我最初的感觉是将数据库读写直接暴露给应用程序端了，当然这是个&lt;strong&gt;错误(或浅显)的认识&lt;/strong&gt;。身份验证怎么办？这样岂不是可以瞎搞？🤪&lt;/p&gt;
&lt;h3 id=&quot;解决方案-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88-1&quot; aria-label=&quot;解决方案 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;解决方案&lt;/h3&gt;
&lt;p&gt;在 Apollo Server、Client 加上身份验证的逻辑就行，官方教程与文档已经写得很清楚了。请参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.apollographql.com/docs/tutorial/resolvers/#authenticate-users&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;教程：Apollo 身份验证&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.apollographql.com/docs/apollo-server/security/authentication/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;文档：Apollo 服务端身份验证&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.apollographql.com/docs/react/networking/authentication/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;文档：Apollo 客户端身份验证&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;4-如何更方便将数据库接入-apollo-server2019-12-05-更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%E5%A6%82%E4%BD%95%E6%9B%B4%E6%96%B9%E4%BE%BF%E5%B0%86%E6%95%B0%E6%8D%AE%E5%BA%93%E6%8E%A5%E5%85%A5-apollo-server2019-12-05-%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;4 如何更方便将数据库接入 apollo server2019 12 05 更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 如何更方便将数据库接入 Apollo Server？(2019-12-05 更新)&lt;/h2&gt;
&lt;p&gt;Apollo Server 结合 npm 包 &lt;code class=&quot;language-text&quot;&gt;pg-promise&lt;/code&gt;、&lt;code class=&quot;language-text&quot;&gt;monk&lt;/code&gt; 这类工具很方便将 Postgre、MongoDB 数据库作为数据源接入，代码如下：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ApolloServer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;apollo-server&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; gql &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;apollo-server&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pgp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pg-promise&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; connectionString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;postgres://username:password@host:port/database&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pgp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;connectionString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; typeDefs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; gql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
  type Query {
    user(id: ID!): User
  }

  type User {
    id: ID!
    name: String
    email: String
  }
&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; resolvers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function-variable function&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; query &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;SELECT id,name,email FROM table_user WHERE id = &apos;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ApolloServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  typeDefs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  resolvers
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; url &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;🚀 Server ready at &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;url&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;但显然有个不够自动化的问题——&lt;strong&gt;需要根据数据库表结构定义手工写 schema&lt;/strong&gt;，如上例中的 &lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt;，对于不同的查询参数也需要手动写 resolver。我想，数据库中定义好的表结构应该能根据一定的规则转换为 schema，于是朋友推荐看了 &lt;a href=&quot;https://www.prisma.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Prisma&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&quot;解决方案-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88-2&quot; aria-label=&quot;解决方案 2 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;解决方案&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.prisma.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Prisma&lt;/a&gt; 可以解决上面的问题，自动根据现有数据表派生 datamodel（即 GraphQL 术语中的 schema，或者反过来，根据 datamodel 建立数据库表），并自动生成客户端。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Prisma 替代传统 ORMs 并简化数据库工作流程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ORM(Object Relational Mapping)意为对象关系映射，将面向对象语言程序中的对象自动持久化到关系数据库中。关于它的解释请见&lt;a href=&quot;http://www.ruanyifeng.com/blog/2019/02/orm-tutorial.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ORM 实例教程&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;之前看 Java 同事用 &lt;a href=&quot;https://hibernate.org/orm/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Hibernate&lt;/a&gt; 操作数据库好爽的，原来 JS 里面也可以了。&lt;/p&gt;
&lt;p&gt;这东西给我（作为前端开发）感觉是，&lt;strong&gt;轻松拉近应用程序端与数据库的距离&lt;/strong&gt;，在开发中有了更多想象与尝试空间。&lt;/p&gt;
&lt;h3 id=&quot;在-api-中添加业务逻辑而不是直接暴露-crud2020-01-17-更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%9C%A8-api-%E4%B8%AD%E6%B7%BB%E5%8A%A0%E4%B8%9A%E5%8A%A1%E9%80%BB%E8%BE%91%E8%80%8C%E4%B8%8D%E6%98%AF%E7%9B%B4%E6%8E%A5%E6%9A%B4%E9%9C%B2-crud2020-01-17-%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;在 api 中添加业务逻辑而不是直接暴露 crud2020 01 17 更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;在 API 中添加业务逻辑，而不是直接暴露 CRUD(2020-01-17 更新)&lt;/h3&gt;
&lt;p&gt;可以直接借助 Prisma Photon 暴露操作数据库的 &lt;a href=&quot;https://github.com/prisma/prisma2/blob/master/docs/photon/api.md#crud&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;CRUD&lt;/a&gt; 方法，但更多的情况是 API 中有特定业务逻辑，而不是直接将数据写到数据库，这才是 API 这一环节的真正作用所在。&lt;/p&gt;
&lt;p&gt;不同的环节数据模型也有所差异，这正是 &lt;strong&gt;PO、DTO、VO&lt;/strong&gt; 所定义的。&lt;/p&gt;
&lt;h2 id=&quot;5-field-为-list-时的两个感叹号2019-12-27-更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-field-%E4%B8%BA-list-%E6%97%B6%E7%9A%84%E4%B8%A4%E4%B8%AA%E6%84%9F%E5%8F%B9%E5%8F%B72019-12-27-%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;5 field 为 list 时的两个感叹号2019 12 27 更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Field 为 List 时的两个感叹号？(2019-12-27 更新)&lt;/h2&gt;
&lt;p&gt;例如定义 Field &lt;code class=&quot;language-text&quot;&gt;tags&lt;/code&gt; 为如下：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TwoExclamation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;[String!]!&lt;/code&gt; 有两个感叹号。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;第一个&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;!&lt;/code&gt;(String 后面) 表示 &lt;code class=&quot;language-text&quot;&gt;tags&lt;/code&gt; 中没有元素可为 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;，比如这个是无效的：[“Software”, null, “Prisma”]。可以期望请求结果中 &lt;code class=&quot;language-text&quot;&gt;tags&lt;/code&gt; 的每个元素都为 &lt;code class=&quot;language-text&quot;&gt;String&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第二个&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;!&lt;/code&gt;(] 后面) 表示该 List 不可为 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;，尽管可为空列表。所以对于 &lt;code class=&quot;language-text&quot;&gt;tags&lt;/code&gt;，&lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 是无效值而 &lt;code class=&quot;language-text&quot;&gt;[]&lt;/code&gt; 有效。可以期望请求结果中 &lt;code class=&quot;language-text&quot;&gt;tags&lt;/code&gt; 一定是个数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样明确定义，让我们在处理数据时省掉类型判断与容错，而不必担心&lt;code class=&quot;language-text&quot;&gt;类型错误&lt;/code&gt;的出现。&lt;/p&gt;
&lt;h2 id=&quot;6-graphql-的性能如何2020-01-17-更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-graphql-%E7%9A%84%E6%80%A7%E8%83%BD%E5%A6%82%E4%BD%952020-01-17-%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;6 graphql 的性能如何2020 01 17 更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. GraphQL 的性能如何？(2020-01-17 更新)&lt;/h2&gt;
&lt;p&gt;参考项目 &lt;a href=&quot;https://github.com/benawad/node-graphql-benchmarks&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;node-graphql-benchmarks&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;7-a-graphqlapolloprismareactmaterialui-tech-stack-project&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7-a-graphqlapolloprismareactmaterialui-tech-stack-project&quot; aria-label=&quot;7 a graphqlapolloprismareactmaterialui tech stack project permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. A GraphQL+Apollo+Prisma+React+MaterialUI tech-stack project&lt;/h2&gt;
&lt;p&gt;我正在用这一系列技术栈开发 Askent 项目——一个现场交互演示工具，它的介绍请见：&lt;a href=&quot;/2019/12/create-presentation-tool-from-scratch/&quot;&gt;从零开始，创建一个多端互动演示工具&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;Github Repository: &lt;a href=&quot;https://github.com/BerlinChan/askent&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/BerlinChan/askent&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[潜江返湾湖湿地马拉松健康跑]]></title><description><![CDATA[潜江返湾湖湿地马拉松健康跑，8km，第一次体验这种活动，热闹有趣]]></description><link>https://www.berlinchan.com/2019/11/qianjiang-marathon</link><guid isPermaLink="false">https://www.berlinchan.com/2019/11/qianjiang-marathon</guid><pubDate>Mon, 18 Nov 2019 00:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/vl4sYZHo43A?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;br&gt;
&lt;a href=&quot;https://v.qq.com/x/page/l3022eawqtq.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;在腾讯视频&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;潜江返湾湖湿地马拉松健康跑，8km，第一次体验这种活动，热闹有趣。全程背着一台笔记本电脑，三件衣服跑，特别有干劲！&lt;/p&gt;
&lt;p&gt;这天天气选的好，上午举办活动，下午就下雨晚上大风降温了。主办方不断消息通知注意事项，应对天气变化问题，给我的感觉是很尽心的。&lt;/p&gt;
&lt;p&gt;0:57 处，大数据中心，哈哈，想起来&lt;a href=&quot;https://cn.nytimes.com/business/20191111/china-reporter-police/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;这篇新闻&lt;/a&gt;的 ——&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个巨大的运动场馆被改造成了“大数据”中心。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;😅&lt;/p&gt;</content:encoded></item><item><title><![CDATA[在 Javascript 中实现高速公钥加密(一)]]></title><description><![CDATA[本文介绍了如何在 JavaScript 中使用 TweetNaCL.js 库来实现公钥加密。NaCl 是一个新的易于使用的高速软件库，用于网络通信、加密解密、签名等。]]></description><link>https://www.berlinchan.com/2019/11/High-speed-public-key-cryptography-in-JavaScript-1</link><guid isPermaLink="false">https://www.berlinchan.com/2019/11/High-speed-public-key-cryptography-in-JavaScript-1</guid><pubDate>Fri, 15 Nov 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;这是一篇翻译。翻译已获得原作者授权。&lt;br&gt;
原文：&lt;a href=&quot;https://medium.com/sharenowtech/high-speed-public-key-cryptography-in-javascript-part-1-3eefb6f91f77&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://medium.com/sharenowtech/high-speed-public-key-cryptography-in-javascript-part-1-3eefb6f91f77&lt;/a&gt;&lt;br&gt;
作者：&lt;a href=&quot;https://medium.com/@kyberneees&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Rolando Santamaria Maso&lt;/a&gt;&lt;br&gt;
翻译：陈柏林&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;本文将讨论如何在 Javascript 中实现端到端的安全通信。
使用最先进的加密库，我们展示了一个健壮的加密系统的基础。&lt;/p&gt;
&lt;h1 id=&quot;基础&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%9F%BA%E7%A1%80&quot; aria-label=&quot;基础 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;基础&lt;/h1&gt;
&lt;p&gt;在开始前，我们要讲两个入门概念。&lt;/p&gt;
&lt;h2 id=&quot;公钥加密&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%85%AC%E9%92%A5%E5%8A%A0%E5%AF%86&quot; aria-label=&quot;公钥加密 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;公钥加密&lt;/h2&gt;
&lt;p&gt;“公钥加密”(&lt;a href=&quot;https://en.wikipedia.org/wiki/Public-key_cryptography&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Public Key Cryptography&lt;/a&gt;)是一种使 Internet 安全的加密机制。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;公钥加密&lt;/strong&gt; 或称 &lt;strong&gt;非对称加密&lt;/strong&gt;，是一种使用密钥对的加密系统，其 &lt;em&gt;公钥&lt;/em&gt; 可以广泛散播，&lt;em&gt;私钥&lt;/em&gt; 只有所有者才知道。
它可运用于两方面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;身份验证（用公钥验证配对的私钥的持有者发送的消息）&lt;/li&gt;
&lt;li&gt;加密（私钥持有者解密用公钥加密的消息）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用公钥加密系统的示例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSL/TLS 握手&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.whatsapp.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Whatsapp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://threema.ch/en&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Threema&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PGP &amp;#x26; &lt;a href=&quot;https://www.openpgp.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OpenPGP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;其他成千上万的应用……&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;nacl--tweetnacljs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nacl--tweetnacljs&quot; aria-label=&quot;nacl  tweetnacljs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NaCL &amp;#x26; TweetNaCL.js&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://nacl.cr.yp.to/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NaCl&lt;/a&gt;(发音”salt”、“氯化钠”或”盐”) 是一个新的易于使用的高速软件库，用于网络通信、加密、解密、签名等。NaCl 的目标是提供构建高级加密工具所需的所有核心操作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;NaCL 项目由我们时代最杰出的计算机科学家之一 &lt;a href=&quot;http://cr.yp.to/djb.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;em&gt;Daniel J.Bernstein&lt;/em&gt;&lt;/a&gt; 领导。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考阅读 &lt;a href=&quot;http://www.aaronsw.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Aaron Swartz&lt;/a&gt; 对 Daniel J.Bernstein 的评论：&lt;a href=&quot;http://www.aaronsw.com/weblog/djb&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;http://www.aaronsw.com/weblog/djb&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://cryptojedi.org/papers/coolnacl-20111201.pdf&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;具体来说，NaCl 使用椭圆曲线密码术，而不是 RSA；它使用椭圆曲线 Curve25519，具有几个高级的安全特性；它使用 Salsa20，而不是 AES(尽管它在边上包含了一个 AES实现)；它使用的是 Poly1305，而不是 HMAC；它使用的是 EdDSA，而不是 ECDSA。&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;NaCL 被证明是安全的，因为从理论上讲，暴力破解 Salsa20 比相应的 AES 更昂贵。
另一方面，Salsa20 在设计时考虑了速度，即使在低速 CPU 下也能提供出色的性能。Salsa20 速度的更详细的解释在这里：&lt;a href=&quot;https://cr.yp.to/snuffle/speed.pdf&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://cr.yp.to/snuffle/speed.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TweetNaCL&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://tweetnacl.cr.yp.to/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TweetNaCl&lt;/a&gt; 是世界上第一个可审计的高安全性密码库。TweetNaCl 仅有 100条推文那么大，包含了 C &lt;a href=&quot;http://nacl.cr.yp.to/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NaCl&lt;/a&gt; 的全部 25个函数。TweetNaCl 是一个自包含的公共 C 库，因此可以轻松地将其集成到应用程序中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://tweetnacl.js.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;strong&gt;&lt;em&gt;TweetNaCL.js&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt; 将 TweetNaCL 引入了 JavaScript，因此也引入了 Node.js 和 Web。它使用 &lt;a href=&quot;https://cr.yp.to/snuffle/xsalsa-20110204.pdf&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;strong&gt;&lt;em&gt;XSalsa20&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;(longer nonce) 流密码(stream cipher)代替 Salsa20。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Javascript Benchmarks: &lt;a href=&quot;https://github.com/dchest/tweetnacl-js/blob/master/README.md#benchmarks&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/dchest/tweetnacl-js/blob/master/README.md#benchmarks&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;开启-javascript-加密之旅&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%BC%80%E5%90%AF-javascript-%E5%8A%A0%E5%AF%86%E4%B9%8B%E6%97%85&quot; aria-label=&quot;开启 javascript 加密之旅 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;开启 JavaScript 加密之旅&lt;/h1&gt;
&lt;p&gt;译注：可直接运行的 &lt;a href=&quot;/fb1940da477e3e5feeb2a20efc74450b/TweetNaClJsDemo.zip&quot;&gt;Demo 下载&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;bob-加密消息给-alice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bob-%E5%8A%A0%E5%AF%86%E6%B6%88%E6%81%AF%E7%BB%99-alice&quot; aria-label=&quot;bob 加密消息给 alice permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bob 加密消息给 Alice&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nacl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;tweetnacl&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;tweetnacl-util&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// reading Bob key pair from secret key&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bob &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyPair&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromSecretKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Uint8Array with 32-byte secret key */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// reading Alice public key&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; alice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;publicKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Uint8Array with 32-byte secret key */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// generating one time nonce for encryption&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nonce &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// message for Alice&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; utf8 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Hello Alice&apos;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Bob encrypts message for Alice&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decodeUTF8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;utf8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  nonce&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publicKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  bob&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secretKey
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 
&lt;span class=&quot;token comment&quot;&gt;// somehow send this message to Alice&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nonce&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;TweetNaCL.js 公钥加密的例子&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;alice-解密来自-bob-的消息&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#alice-%E8%A7%A3%E5%AF%86%E6%9D%A5%E8%87%AA-bob-%E7%9A%84%E6%B6%88%E6%81%AF&quot; aria-label=&quot;alice 解密来自 bob 的消息 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Alice 解密来自 Bob 的消息&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nacl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;tweetnacl&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;tweetnacl-util&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// reading Alice key pair from secret key&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; alice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyPair&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromSecretKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Uint8Array with 32-byte secret key */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// reading Bob public key&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bob &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;publicKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Uint8Array with 32-byte secret key */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
             
&lt;span class=&quot;token comment&quot;&gt;// const message = ... the message object from Bob&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Alice decrypts message from Bob&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; payload &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nonce&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bob&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publicKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secretKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; utf8 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;encodeUTF8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- &apos;Hello Alice&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;TweetNaCL.js 私钥解密的例子&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;使用预先计算的共享密钥&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BD%BF%E7%94%A8%E9%A2%84%E5%85%88%E8%AE%A1%E7%AE%97%E7%9A%84%E5%85%B1%E4%BA%AB%E5%AF%86%E9%92%A5&quot; aria-label=&quot;使用预先计算的共享密钥 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;使用预先计算的共享密钥&lt;/h2&gt;
&lt;p&gt;对等两点之间的加密/解密会话，建议使用预先计算的共享密钥：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Bob pre-computes shared key with Alice (one time)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sharedKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publicKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bob&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secretKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// generating one time nonce for encryption&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nonce &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Bob encrypt message for Alice&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decodeUTF8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hello Alice&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  nonce&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  sharedKey &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- using shared key&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nonce&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;TweetNaCL.js 使用共享密钥加密的例子&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Alice pre-computes shared key with Bob (one time)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sharedKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bob&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publicKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; alice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secretKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- message object from Bob side&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; payload &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nacl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;open&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;box&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
  message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nonce&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
  sharedKey &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- using shared key&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// assert.equal(&apos;Hello Alice&apos;, nacl.util.encodeUTF8(payload))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;TweetNaCL.js 使用共享密钥解密的例子&lt;/em&gt;&lt;/p&gt;
&lt;h1 id=&quot;维护密钥&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%BB%B4%E6%8A%A4%E5%AF%86%E9%92%A5&quot; aria-label=&quot;维护密钥 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;维护密钥&lt;/h1&gt;
&lt;h2 id=&quot;公钥&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%85%AC%E9%92%A5&quot; aria-label=&quot;公钥 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;公钥&lt;/h2&gt;
&lt;p&gt;”&lt;em&gt;公钥加密架构&lt;/em&gt;“需要适当的方案来维护和认证某公钥确实属于某用户。尽管此过程不在本文讨论范围之内，但常见的解决方案包括将用户的公钥作为其个人资料页面的一部分进行维护，使其联系人可以通过查找目录来访问它。(译注：这类服务叫 &lt;a href=&quot;https://en.wikipedia.org/wiki/Key_server_(cryptographic)&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Key Server&lt;/a&gt;，例如 &lt;a href=&quot;http://keys.gnupg.net/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OpenPGPkeyserver&lt;/a&gt;)&lt;/p&gt;
&lt;h2 id=&quot;私钥&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%A7%81%E9%92%A5&quot; aria-label=&quot;私钥 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;私钥&lt;/h2&gt;
&lt;p&gt;维护密钥要复杂得多，因为这是用户希望保密的非常敏感的信息。
通常，这个过程是由用户负责的，并且在某种程度上，实现公钥加密的应用程序需要允许用户输入其私钥(绝不要将其传到服务器端，译注：参考&lt;a href=&quot;https://github.com/mdrights/Digital-rights/blob/master/E%E5%8A%A0%E5%AF%86%E6%8A%80%E8%A1%93/2019-09-21-%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E7%AB%AF%E5%88%B0%E7%AB%AF%E5%8A%A0%E5%AF%86%E4%B9%88.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;该文章&lt;/a&gt;)。&lt;/p&gt;
&lt;p&gt;密钥通常使用离线安全存储机制或在线存储，使用强对称加密方法(如 &lt;em&gt;AES&lt;/em&gt;)来保护密钥。
将私钥在线存储，简化了将私钥添加进应用程序的过程(&lt;em&gt;仅用于客户端解密&lt;/em&gt;)，例如：在登录阶段。&lt;/p&gt;
&lt;h1 id=&quot;结论&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%BB%93%E8%AE%BA&quot; aria-label=&quot;结论 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;本文介绍了 &lt;em&gt;公钥加密&lt;/em&gt;，以及如何在 JavaScript中使用 &lt;em&gt;TweetNaCL.js&lt;/em&gt; 库来实现它。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用 &lt;em&gt;XSalsa20&lt;/em&gt; 而不是 &lt;em&gt;RSA&lt;/em&gt;，&lt;em&gt;CPU&lt;/em&gt; 使用和 &lt;em&gt;电池&lt;/em&gt; 消耗降低到历史最低，同时保持强大的 &lt;em&gt;256位安全&lt;/em&gt; 级别。
—— 给你 App 的提示 ;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用 &lt;em&gt;TweetNaCL.js&lt;/em&gt; 进行 &lt;strong&gt;&lt;em&gt;数字签名&lt;/em&gt;&lt;/strong&gt; 将在该系列的第二篇文章中进行讨论：&lt;a href=&quot;https://medium.com/@kyberneees/high-speed-public-key-cryptography-in-javascript-part-2-digital-signatures-3e58876d1dff&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;在此处阅读&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;一如既往地感谢您的留言与反馈!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Windows 10(7) 内置 MD5 校验和计算工具]]></title><description><![CDATA[如果只是确定一个下载文件的校验和，实际上不必安装另外的实用工具，Win 10 自带的命令 CertUtil 就能搞定。]]></description><link>https://www.berlinchan.com/2019/11/Windows-10-Built-In-MD5-Checksum-Calculator</link><guid isPermaLink="false">https://www.berlinchan.com/2019/11/Windows-10-Built-In-MD5-Checksum-Calculator</guid><pubDate>Thu, 14 Nov 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;这是一篇翻译。&lt;br&gt;
原文：&lt;a href=&quot;https://onthefencedevelopment.com/2017/08/15/windows-10-builtin-md5-checksum-calculator/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://onthefencedevelopment.com/2017/08/15/windows-10-builtin-md5-checksum-calculator/&lt;/a&gt;&lt;br&gt;
作者：&lt;a href=&quot;https://onthefencedevelopment.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;On The Fence Development&lt;/a&gt;&lt;br&gt;
翻译：陈柏林&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;最近我的主开发电脑工作不正常(启动缓慢，一些应用程序打不开等)，在重装软件中我格外小心。&lt;/p&gt;
&lt;p&gt;很久前，我安装过各种应用程序，游戏(包括Steam)和实用程序，现在已很难寻找导致问题的原因。可能有多个罪魁祸首。&lt;/p&gt;
&lt;p&gt;现在，我要装 MySQL Workbench，因此我去下载，并注意到该链接下面的 MD5 校验和(&lt;a href=&quot;https://zh.wikipedia.org/zh-cn/%E6%A0%A1%E9%AA%8C%E5%92%8C&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;checksum&lt;/a&gt;)。我总是不检查这些，也许这就是为什么我电脑被搞混乱的原因。但为了使系统尽可能保持整洁，我决定现在起尽量对比检查这些校验和。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a17d887066a3711ad58bfc6a568253ea/4f58c/f6b10-mysql-workbench-download.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.73684210526316%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACCklEQVR42mVSzW7TQBD2w/AQPcOFNgERjjwJSDRpERUSBy68AEFJI4QEOJGQIlSRC20SJFDVRk5okSrVjhP/xInt/fP+mLFdCm1Hn8ezs/vN7M6Mluai0jRkXCiVKpUkCb8qQkoFWzdEA++SMESJu5j7MYJTlLEMlNFEUC4py4MJyYXIIf/aUoMAA9P5eXaaml1FPIgvRSKFUBJ4S4F9xbGSUvAEAH8Fe7ktRKLpB+POYNLuTzr98efhGHS7b4BH/3akf9nTu129N+gMfmX+A9jKoOfGp31Du/P4TanaKNea5Wr90Yu3le36xmaz8qxVqu5ubL2r7Hwo1VrlavPBduterbm+2bi/tbv+tAE2EDXbjywvtLxVpv1omiEsPDYY7hKWpruaeiGcBA3LArDM3pxAaSihBMNzijJywWe2ZZnmMlh4nhsEwcyeuo4zt6eLhX+l2nEch1G0ClcxQtCqQjiExBCOc8aKCoEHY0IozRrBGJzJMmOEgB9FEca4CAmNNcz56NyZWK5hOmPLOTyzT2yP/SdZ5oyMMYov+EADT4DI2vPW2s77u6/at19+BH3rSf3ha/1yoi6uXZCjOKaUwGzJnAzX2v9h9Iajr8Pj3vcRGHuDo8Pf1vUJg48QDDmBeelVMGHmeeLM0uUih58Gfppf9ToZIRSGIWT7R1YKQY2UgmsUgDaIG+P9B7bIux7eBxzaAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/a17d887066a3711ad58bfc6a568253ea/5ed05/f6b10-mysql-workbench-download.webp 190w,
/static/a17d887066a3711ad58bfc6a568253ea/9d76b/f6b10-mysql-workbench-download.webp 380w,
/static/a17d887066a3711ad58bfc6a568253ea/33466/f6b10-mysql-workbench-download.webp 760w,
/static/a17d887066a3711ad58bfc6a568253ea/d0919/f6b10-mysql-workbench-download.webp 870w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/a17d887066a3711ad58bfc6a568253ea/253c6/f6b10-mysql-workbench-download.png 190w,
/static/a17d887066a3711ad58bfc6a568253ea/810ee/f6b10-mysql-workbench-download.png 380w,
/static/a17d887066a3711ad58bfc6a568253ea/b4918/f6b10-mysql-workbench-download.png 760w,
/static/a17d887066a3711ad58bfc6a568253ea/4f58c/f6b10-mysql-workbench-download.png 870w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/a17d887066a3711ad58bfc6a568253ea/b4918/f6b10-mysql-workbench-download.png&quot;
            alt=&quot;f6b10 mysql workbench download&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;那么问题来了，用什么工具来计算下载来的文件的校验和咧？&lt;/p&gt;
&lt;p&gt;如果你 Google 搜 “MD5 checker” 会看到许多实用工具，虽然我没怀疑它们的可用性，但我一个也没安装。&lt;/p&gt;
&lt;p&gt;每个下载都附带 MD5 校验和便于验证，但过去胡乱安装一堆实用工具把电脑搞乱后，这次我有点担心了。&lt;/p&gt;
&lt;p&gt;现在，MD5 并不是什么新鲜事物，您会认为 Windows 10 内置了某实用工具，可以计算哈希值 —— 确实如此。显然，它在 Windows 7 中也可用，但我没有 Win7 系统，因此无法验证(译注：在 Windows 7 SP1 中验证可用)。&lt;/p&gt;
&lt;p&gt;打开命令行(译注：或 PowerShell)，输入：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;CertUtil &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;hashfile &amp;lt;path to file&gt; MD5&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;根据文件大小计算需要几秒钟，如果成功，MD5 hash值将显示如下：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9b07dedcbd811b30a3d0c85dd8af8f81/4e11f/08acc-certutil-mysql.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.10526315789474%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA4klEQVR42mNIyczJzs3LyS8oLCnNLyouLi3PzS/Iys7JyMpOTc9IA6KMzLQMIJmRnpkJQhmZQKmE+Lim3mkMYsIiIgL8qoKCavz8WkJC8lxccjw8QCTDxSnBxibJzibOBkUCTExCzMzCzMxszMwMDAxOPiEMRkoqurIyejIyutLSBjIy8mLiQkJCIsLCfLy8PNzcvDw8XJyc3FxcQJKVmYWNhZWNlZWdnQOo2S0wkoGFnZOVFSjOwkA0YGYGKXb2DQUySdBGXc1MQ06zi18oAyM7DyMrOyMrB/GIhYMbFM/+UQBWCin1FegEQQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/9b07dedcbd811b30a3d0c85dd8af8f81/5ed05/08acc-certutil-mysql.webp 190w,
/static/9b07dedcbd811b30a3d0c85dd8af8f81/9d76b/08acc-certutil-mysql.webp 380w,
/static/9b07dedcbd811b30a3d0c85dd8af8f81/33466/08acc-certutil-mysql.webp 760w,
/static/9b07dedcbd811b30a3d0c85dd8af8f81/d56c8/08acc-certutil-mysql.webp 979w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/9b07dedcbd811b30a3d0c85dd8af8f81/253c6/08acc-certutil-mysql.png 190w,
/static/9b07dedcbd811b30a3d0c85dd8af8f81/810ee/08acc-certutil-mysql.png 380w,
/static/9b07dedcbd811b30a3d0c85dd8af8f81/b4918/08acc-certutil-mysql.png 760w,
/static/9b07dedcbd811b30a3d0c85dd8af8f81/4e11f/08acc-certutil-mysql.png 979w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/9b07dedcbd811b30a3d0c85dd8af8f81/b4918/08acc-certutil-mysql.png&quot;
            alt=&quot;08acc certutil mysql&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;也可以为其他 hash 算法生成校验和，用以下任意一个参数替换上面使用的 MD5 (注意，如果没有指定值，则默认使用 SHA1)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MD2&lt;/li&gt;
&lt;li&gt;MD4&lt;/li&gt;
&lt;li&gt;MD5&lt;/li&gt;
&lt;li&gt;SHA1&lt;/li&gt;
&lt;li&gt;SHA256&lt;/li&gt;
&lt;li&gt;SHA384&lt;/li&gt;
&lt;li&gt;SHA512&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，如果您只是确定一个下载文件的校验和，实际上不必安装另外的实用工具。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;相关文章&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%9B%B8%E5%85%B3%E6%96%87%E7%AB%A0&quot; aria-label=&quot;相关文章 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;相关文章&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.computerhope.com/unix/sha256sum.htm&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Linux sha256sum command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://program-think.blogspot.com/2013/02/file-integrity-check.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;扫盲文件完整性校验——关于散列值和数字签名&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[荒野大镖客2——我们的时代已过去是悲凉的事实]]></title><description><![CDATA[站在酒吧门口，亚瑟背影满是磨毛的线头，戴着牛仔帽、背着子弹夹，一副老土野蛮的形象，与城里人绅士帽、精致西装显得格格不入。再想亚瑟说的"我们的时代过去了"，那是悲凉的事实。]]></description><link>https://www.berlinchan.com/2019/11/red-dead-redemption2-our-era-has-gone</link><guid isPermaLink="false">https://www.berlinchan.com/2019/11/red-dead-redemption2-our-era-has-gone</guid><pubDate>Wed, 13 Nov 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;blockquote&gt;
&lt;p&gt;断断续续，&lt;strong&gt;荒野大镖客：救赎&lt;/strong&gt; 玩到第四章了，最近有两个场景令我惊艳、感慨。学会了用 Xbox 的截屏和录屏功能，将这两个场景记录下来，与大家分享。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;大战在即&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%A4%A7%E6%88%98%E5%9C%A8%E5%8D%B3&quot; aria-label=&quot;大战在即 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;大战在即&lt;/h2&gt;
&lt;p&gt;第三章 - 克莱蒙斯据点 - 古恨今仇。&lt;/p&gt;
&lt;p&gt;在得知杰克被布雷斯韦特家抓走后，达奇带着几乎全体帮派成员一路开到庄园门口，下马步行来到屋前要人。&lt;/p&gt;
&lt;p&gt;逆着明亮的月光，树和人都成了阴暗的剪影，画面令人印象深刻。全员出动和对峙谈话间的紧张气氛，很好烘托出为救孩子杰克的心切，这种&lt;strong&gt;倾尽全力保障帮派成员安全，团结一心的情感连结&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/481106c929732a2ee0ea7020ef1c4640/c2834/2019-11-13-red-dead-redemption2-1.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABwxSQohX/xAAbEAACAQUAAAAAAAAAAAAAAAAAAgEDEBESMf/aAAgBAQABBQJNFapKsYUjtv/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABcQAAMBAAAAAAAAAAAAAAAAAAABEDH/2gAIAQEABj8CRlc//8QAGhAAAwADAQAAAAAAAAAAAAAAAAERITFRgf/aAAgBAQABPyF3EqumjWRw72jOR59DP//aAAwDAQACAAMAAAAQz8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAADAQEAAwAAAAAAAAAAAAABESEAMUFhkf/aAAgBAQABPxDiXBR9yAL2GVdOy9CstOPdh8EBgD3/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/481106c929732a2ee0ea7020ef1c4640/5ed05/2019-11-13-red-dead-redemption2-1.webp 190w,
/static/481106c929732a2ee0ea7020ef1c4640/9d76b/2019-11-13-red-dead-redemption2-1.webp 380w,
/static/481106c929732a2ee0ea7020ef1c4640/33466/2019-11-13-red-dead-redemption2-1.webp 760w,
/static/481106c929732a2ee0ea7020ef1c4640/ca244/2019-11-13-red-dead-redemption2-1.webp 1140w,
/static/481106c929732a2ee0ea7020ef1c4640/b0196/2019-11-13-red-dead-redemption2-1.webp 1520w,
/static/481106c929732a2ee0ea7020ef1c4640/b8d31/2019-11-13-red-dead-redemption2-1.webp 3840w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/481106c929732a2ee0ea7020ef1c4640/89129/2019-11-13-red-dead-redemption2-1.jpg 190w,
/static/481106c929732a2ee0ea7020ef1c4640/0036d/2019-11-13-red-dead-redemption2-1.jpg 380w,
/static/481106c929732a2ee0ea7020ef1c4640/e484a/2019-11-13-red-dead-redemption2-1.jpg 760w,
/static/481106c929732a2ee0ea7020ef1c4640/32d87/2019-11-13-red-dead-redemption2-1.jpg 1140w,
/static/481106c929732a2ee0ea7020ef1c4640/cf227/2019-11-13-red-dead-redemption2-1.jpg 1520w,
/static/481106c929732a2ee0ea7020ef1c4640/c2834/2019-11-13-red-dead-redemption2-1.jpg 3840w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/481106c929732a2ee0ea7020ef1c4640/e484a/2019-11-13-red-dead-redemption2-1.jpg&quot;
            alt=&quot;大战在即&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;br&gt;
&lt;em&gt;点击图片查看完整 4K 分辨率&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/6GHwkAUljG4?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;
&lt;a href=&quot;https://v.qq.com/x/page/y30219kp9yp.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;em&gt;在腾讯视频上&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;我们的时代过去了&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%88%91%E4%BB%AC%E7%9A%84%E6%97%B6%E4%BB%A3%E8%BF%87%E5%8E%BB%E4%BA%86&quot; aria-label=&quot;我们的时代过去了 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&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;strong&gt;格格不入&lt;/strong&gt;。&lt;strong&gt;现在再想亚瑟说的”我们的时代过去了”，那是悲凉的事实&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0df855e5cb2ad7ec3f150f70dea6b598/c2834/2019-11-13-red-dead-redemption2-2.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAwAEBf/EABcBAAMBAAAAAAAAAAAAAAAAAAABAgP/2gAMAwEAAhADEAAAAeaxhnS2eZ//xAAYEAADAQEAAAAAAAAAAAAAAAAAAQISQf/aAAgBAQABBQLMiyh2VTc8P//EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPwFtf//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/AUf/xAAYEAACAwAAAAAAAAAAAAAAAAAAEAExcf/aAAgBAQAGPwIpRj//xAAcEAADAAIDAQAAAAAAAAAAAAAAAREhMUFRYaH/2gAIAQEAAT8ho4rexcb5bpk39LC9Sht4eCP/2gAMAwEAAgADAAAAEM8P/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxAP/8QAFhEAAwAAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/EI2Sf//EABoQAQADAQEBAAAAAAAAAAAAAAEAESGBMVH/2gAIAQEAAT8QSySqj48gxeVFsLWX8jXBIYV4mnVGAUSos1rs2dn/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/0df855e5cb2ad7ec3f150f70dea6b598/5ed05/2019-11-13-red-dead-redemption2-2.webp 190w,
/static/0df855e5cb2ad7ec3f150f70dea6b598/9d76b/2019-11-13-red-dead-redemption2-2.webp 380w,
/static/0df855e5cb2ad7ec3f150f70dea6b598/33466/2019-11-13-red-dead-redemption2-2.webp 760w,
/static/0df855e5cb2ad7ec3f150f70dea6b598/ca244/2019-11-13-red-dead-redemption2-2.webp 1140w,
/static/0df855e5cb2ad7ec3f150f70dea6b598/b0196/2019-11-13-red-dead-redemption2-2.webp 1520w,
/static/0df855e5cb2ad7ec3f150f70dea6b598/b8d31/2019-11-13-red-dead-redemption2-2.webp 3840w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/0df855e5cb2ad7ec3f150f70dea6b598/89129/2019-11-13-red-dead-redemption2-2.jpg 190w,
/static/0df855e5cb2ad7ec3f150f70dea6b598/0036d/2019-11-13-red-dead-redemption2-2.jpg 380w,
/static/0df855e5cb2ad7ec3f150f70dea6b598/e484a/2019-11-13-red-dead-redemption2-2.jpg 760w,
/static/0df855e5cb2ad7ec3f150f70dea6b598/32d87/2019-11-13-red-dead-redemption2-2.jpg 1140w,
/static/0df855e5cb2ad7ec3f150f70dea6b598/cf227/2019-11-13-red-dead-redemption2-2.jpg 1520w,
/static/0df855e5cb2ad7ec3f150f70dea6b598/c2834/2019-11-13-red-dead-redemption2-2.jpg 3840w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/0df855e5cb2ad7ec3f150f70dea6b598/e484a/2019-11-13-red-dead-redemption2-2.jpg&quot;
            alt=&quot;格格不入&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;br&gt;
&lt;em&gt;点击图片查看完整 4K 分辨率&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;主线通关更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%B8%BB%E7%BA%BF%E9%80%9A%E5%85%B3%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;主线通关更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;主线通关更新：&lt;/h2&gt;
&lt;p&gt;以 Good Ending 完成主线剧情，即亚瑟在晨曦的山崖上逝去，约翰·马斯顿在比彻之愿农场安顿好妻儿后，与沙迪·阿德勒、查尔斯·史密斯在雪山上找到麦卡，为亚瑟报仇。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6c24e75f72d594e00f71a1728e9d007d/9ef28/rdr2-morning.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEA/9oADAMBAAIQAxAAAAF82cpLzKG//8QAGxAAAgIDAQAAAAAAAAAAAAAAAQMAAhASEzH/2gAIAQEAAQUCq1Ue6uvYGDwnH//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AUf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwFX/8QAFxAAAwEAAAAAAAAAAAAAAAAAACAxIv/aAAgBAQAGPwIxSJ//xAAbEAEAAgMBAQAAAAAAAAAAAAABABEhMVFBkf/aAAgBAQABPyH0LVcgduXIGu/2EyjO2Wz/2gAMAwEAAgADAAAAEBfP/8QAFhEBAQEAAAAAAAAAAAAAAAAAABEx/9oACAEDAQE/ENQ//8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARQf/aAAgBAgEBPxAU7bv/xAAaEAEAAwEBAQAAAAAAAAAAAAABABEhQTFR/9oACAEBAAE/EL1K6fU1RUaiq7C9BfSmMBlr9leYDh5Klp7P/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/6c24e75f72d594e00f71a1728e9d007d/5ed05/rdr2-morning.webp 190w,
/static/6c24e75f72d594e00f71a1728e9d007d/9d76b/rdr2-morning.webp 380w,
/static/6c24e75f72d594e00f71a1728e9d007d/33466/rdr2-morning.webp 760w,
/static/6c24e75f72d594e00f71a1728e9d007d/ca244/rdr2-morning.webp 1140w,
/static/6c24e75f72d594e00f71a1728e9d007d/b0196/rdr2-morning.webp 1520w,
/static/6c24e75f72d594e00f71a1728e9d007d/c7dec/rdr2-morning.webp 3073w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/6c24e75f72d594e00f71a1728e9d007d/89129/rdr2-morning.jpg 190w,
/static/6c24e75f72d594e00f71a1728e9d007d/0036d/rdr2-morning.jpg 380w,
/static/6c24e75f72d594e00f71a1728e9d007d/e484a/rdr2-morning.jpg 760w,
/static/6c24e75f72d594e00f71a1728e9d007d/32d87/rdr2-morning.jpg 1140w,
/static/6c24e75f72d594e00f71a1728e9d007d/cf227/rdr2-morning.jpg 1520w,
/static/6c24e75f72d594e00f71a1728e9d007d/9ef28/rdr2-morning.jpg 3073w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/6c24e75f72d594e00f71a1728e9d007d/e484a/rdr2-morning.jpg&quot;
            alt=&quot;约翰在比彻之愿开始了平静的农场生活&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;center&gt;约翰在比彻之愿开始了平静的农场生活&lt;/center&gt;
&lt;p&gt;达奇在雪山小屋前枪指麦卡和约翰时仍然不知所措，在开枪杀死麦卡后再次甩下一言不发的背影离开。&lt;strong&gt;达奇在逃亡和社会变革的环境压力下迷失了&lt;/strong&gt;。他挂在嘴边的 “I have a plan” 再三被证明“计划跟不上变化”，特别是任务&lt;code class=&quot;language-text&quot;&gt;第六章-河狸岩洞 最真实的自己&lt;/code&gt;，在圣丹尼斯计划抢的火车未按计划停站却呼啸驶过后，被亚瑟质问是否有计划时，他虽然斩钉截铁地回答“Of course we are”，但内心一定认识到一些问题。&lt;/p&gt;
&lt;p&gt;达奇寄期望于搞到一大笔钱然后远走高飞，再过上平静的生活，可剧烈的社会变革已在平淡日常下暗流涌动，文明的“枷锁”步步紧逼着整个帮派，达奇的理想已变成不切实际的幻想。即使（我操作）亚瑟不断外出打猎补充河狸岩洞营地的食物短缺，帮派仍在不断滑向瓦解边缘。不顺应大势所趋地维持现状，最终还是会被现实冲溃。&lt;/p&gt;
&lt;p&gt;“我们的时代已经过去了”，达奇始终不愿向这一事实低头，亚瑟在反思转变的可能，但命运没有给他足够的时间。看起来约翰·马斯顿代表着最圆满的任务结局，但铲粪、喂鸡、挤奶、砍柴、家庭争吵等日常琐事是代价。&lt;/p&gt;
&lt;p&gt;写到这，让我想起《&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E9%BA%A5%E7%94%B0%E6%8D%95%E6%89%8B&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;麦田里的守望者&lt;/a&gt;》中 Antolini 老师说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个不成熟的男子的标志是他愿意为某种事业英勇地死去，一个成熟的男子的标志使他愿意为某种事业卑贱地活着&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;坚持——冒险人生、远大理想，妥协——平淡无奇、安稳现实，无法分出是非对错&lt;/strong&gt;。关于这个主题，印第长老落雨和儿子飞鹰，有着比主角们故事更冲突的对比。无论这些人物选择有多么不同，他们都展现出了人性光辉最闪耀的不同侧面，他们是各自故事中的英雄。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fb5ef5aea3343fd3c18fd96016b63c35/c2834/rdr2-merry.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEBf/EABUBAQEAAAAAAAAAAAAAAAAAAAIB/9oADAMBAAIQAxAAAAFbMkDvISn/xAAaEAACAgMAAAAAAAAAAAAAAAAAAQMRBBAT/9oACAEBAAEFAoJmPIs7MTLrX//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/Aar/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwGI/8QAGhAAAgIDAAAAAAAAAAAAAAAAAAECESAiMv/aAAgBAQAGPwJ3Gx6HGH//xAAbEAEBAQACAwAAAAAAAAAAAAABEQAhMUFR0f/aAAgBAQABPyFCOx4ZN0tx45zb6ye8oQZhZv/aAAwDAQACAAMAAAAQAD//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/EC//xAAVEQEBAAAAAAAAAAAAAAAAAAAQMf/aAAgBAgEBPxCh/8QAHBABAAICAwEAAAAAAAAAAAAAAQARITFBUXFh/9oACAEBAAE/EHRmyVD4zAyx1BgrnvnUcqZ7BcCzuBlDoiAq37P/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/fb5ef5aea3343fd3c18fd96016b63c35/5ed05/rdr2-merry.webp 190w,
/static/fb5ef5aea3343fd3c18fd96016b63c35/9d76b/rdr2-merry.webp 380w,
/static/fb5ef5aea3343fd3c18fd96016b63c35/33466/rdr2-merry.webp 760w,
/static/fb5ef5aea3343fd3c18fd96016b63c35/ca244/rdr2-merry.webp 1140w,
/static/fb5ef5aea3343fd3c18fd96016b63c35/b0196/rdr2-merry.webp 1520w,
/static/fb5ef5aea3343fd3c18fd96016b63c35/b8d31/rdr2-merry.webp 3840w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/fb5ef5aea3343fd3c18fd96016b63c35/89129/rdr2-merry.jpg 190w,
/static/fb5ef5aea3343fd3c18fd96016b63c35/0036d/rdr2-merry.jpg 380w,
/static/fb5ef5aea3343fd3c18fd96016b63c35/e484a/rdr2-merry.jpg 760w,
/static/fb5ef5aea3343fd3c18fd96016b63c35/32d87/rdr2-merry.jpg 1140w,
/static/fb5ef5aea3343fd3c18fd96016b63c35/cf227/rdr2-merry.jpg 1520w,
/static/fb5ef5aea3343fd3c18fd96016b63c35/c2834/rdr2-merry.jpg 3840w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/fb5ef5aea3343fd3c18fd96016b63c35/e484a/rdr2-merry.jpg&quot;
            alt=&quot;约翰向阿比盖尔求婚&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;center&gt;约翰向阿比盖尔求婚&lt;/center&gt;
&lt;p&gt;荒野大镖客-救赎 2 无疑是 2019 年最令我印象深刻的游戏。后来再玩 战地5 的时候感觉体验要单薄不少，最近都好像没有什么特别想玩的游戏了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[在 Javascript 中使用公钥加密的介绍]]></title><description><![CDATA[译文。通过用 Node、Javascript 实现一个端到端加密聊天程序，解释非对称加密 RSA 如何工作以及如何使用。]]></description><link>https://www.berlinchan.com/2019/11/building-an-encrypted-messenger-with-javascript</link><guid isPermaLink="false">https://www.berlinchan.com/2019/11/building-an-encrypted-messenger-with-javascript</guid><pubDate>Fri, 08 Nov 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;style type=&quot;text/css&quot;&gt;
    pre {max-height: 30em; overflow: auto;}
&lt;/style&gt;
&lt;p&gt;这是一篇翻译。翻译已获得原作者授权。&lt;br&gt;
原文：&lt;a href=&quot;https://blog.patricktriest.com/building-an-encrypted-messenger-with-javascript/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://blog.patricktriest.com/building-an-encrypted-messenger-with-javascript/&lt;/a&gt;&lt;br&gt;
作者：&lt;a href=&quot;https://blog.patricktriest.com/author/patrick/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Patrick Triest&lt;/a&gt;&lt;br&gt;
翻译：陈柏林&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;端到端加密聊天程序---教程&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%AB%AF%E5%88%B0%E7%AB%AF%E5%8A%A0%E5%AF%86%E8%81%8A%E5%A4%A9%E7%A8%8B%E5%BA%8F---%E6%95%99%E7%A8%8B&quot; aria-label=&quot;端到端加密聊天程序   教程 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;端到端加密聊天程序 - 教程&lt;/h2&gt;
&lt;p&gt;密码学很重要。没有密码学，就没有 Internet —— 在线发送的数据就像在拥挤的房间里大声喊叫一样容易被截获。
密码学也是当前时事中的主要话题，在&lt;a href=&quot;https://en.wikipedia.org/wiki/FBI%E2%80%93Apple_encryption_dispute&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;执法调查&lt;/a&gt;和&lt;a href=&quot;https://www.politico.com/tipsheets/morning-cybersecurity/2017/11/10/texas-shooting-could-revive-encryption-legislation-223290&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;政府立法&lt;/a&gt;中日益发挥中心作用。&lt;/p&gt;
&lt;p&gt;对于记者、活动人士、国家、企业和需要保护数据不受黑客、间谍和广告机构威胁的普通人来说，加密是一种无价的工具。&lt;/p&gt;
&lt;p&gt;了解如何利用强加密对于现代软件开发至关重要。
在本教程中，我们不会深入研究底层的数学和密码学理论；相反，重点将放在如何在您自己的应用程序利用这些技术。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3ddcd4f7188d7a9818daab8d83e1442b/67c4b/screenshot_5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.78947368421052%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA9UlEQVR42o2R226DMAyG8wCD+BDnRIeADphoK+52vetqe//XmaFb14tu5ZMVxXHs35ZN420TuWka7wPeg4jGcRyGoW3blFLO2TkHABoyo4NeaN8tpBgvr7cw82GatPo8z33fay2k75DBgORJS4agN7qrrKVzVe1X6rr+VUY9AWHlr7aVa/Ti/ig/Qr+qLIsQs85AN7YhWWfuupcYa8SMKIh+Ndmo/G7tuXj6sOVnudibtRVAtTH5JP7IMiD3yHq2xJGYYFuydInaUDxjscMiYRkQtGmHhh6he6ZXspOFA8AR4ATYLdslIaPr1b3xv7joWK7OaitfZ/M0Zc6o4mYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/3ddcd4f7188d7a9818daab8d83e1442b/5ed05/screenshot_5.webp 190w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/9d76b/screenshot_5.webp 380w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/33466/screenshot_5.webp 760w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/ca244/screenshot_5.webp 1140w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/34e40/screenshot_5.webp 1440w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/3ddcd4f7188d7a9818daab8d83e1442b/253c6/screenshot_5.png 190w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/810ee/screenshot_5.png 380w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/b4918/screenshot_5.png 760w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/c0204/screenshot_5.png 1140w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/67c4b/screenshot_5.png 1440w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/3ddcd4f7188d7a9818daab8d83e1442b/b4918/screenshot_5.png&quot;
            alt=&quot;Screenshot 5&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;本教程中，我们将介绍端到端 2048位 &lt;a href=&quot;https://en.wikipedia.org/wiki/RSA_(cryptosystem)&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;RSA加密&lt;/a&gt;消息收发工具的基本概念和实现。
我们将利用 &lt;a href=&quot;https://vuejs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Vue.js&lt;/a&gt; 协调前端功能，在 &lt;a href=&quot;https://nodejs.org/en/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Node.js&lt;/a&gt; 后端环境中使用 &lt;a href=&quot;https://socket.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Socket.io&lt;/a&gt;，用于在用户间收发消息。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;演示 - &lt;a href=&quot;https://chat.patricktriest.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://chat.patricktriest.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Github 仓库 - &lt;a href=&quot;https://github.com/triestpa/Open-Cryptochat&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/triestpa/Open-Cryptochat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本教程涉及的概念用 Javascript 实现，该语言具有平台无关特性。
我们将构建一个传统的基于浏览器的 Web 应用，但是如果您担心基于浏览器应用程序的安全性，可以修改此代码以使其在预构建的桌面(使用 &lt;a href=&quot;https://electronjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Electron&lt;/a&gt;)或移动应用程序(&lt;a href=&quot;https://facebook.github.io/react-native/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;React Native&lt;/a&gt;，&lt;a href=&quot;https://ionicframework.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ionic&lt;/a&gt;，&lt;a href=&quot;https://cordova.apache.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cordova&lt;/a&gt;)二进制文件中工作。&lt;sup&gt;&lt;a href=&quot;#fn1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;a id=&quot;fnref1&quot;&gt;&lt;/a&gt;
用另一种编程语言实现类似的功能应该也简单，因为大多数语言都有著名的开源加密库可用；
虽然语法不同，但核心概念是相同的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;免责声明 - 本文旨在入门端到端加密的实现，不是构建像诺克斯堡(译注：&lt;a href=&quot;https://zh.wikipedia.org/zh-cn/%E8%AF%BA%E5%85%8B%E6%96%AF%E5%A0%A1&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Fort Knox&lt;/a&gt;)般固若金汤般浏览器聊天应用程序的权威指南。&lt;/p&gt;
&lt;p&gt;我致力于为您的 Javascript 应用程序提供有关加密的有用信息，但不保证应用的 100% 安全。&lt;/p&gt;
&lt;p&gt;在构建应用程序过程的各个阶段，有很多可能出现问题的地方，特别是在本教程未涵盖的阶段，例如设置 Web主机和保护服务器安全。&lt;/p&gt;
&lt;p&gt;如果您是安全专家，且在教程代码中找到漏洞，请随时通过邮件(patrick.triest#gmail.com)或下面评论部分与我联系。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;1---项目设置&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1---%E9%A1%B9%E7%9B%AE%E8%AE%BE%E7%BD%AE&quot; aria-label=&quot;1   项目设置 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1 - 项目设置&lt;/h2&gt;
&lt;h3 id=&quot;11---安装依赖&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#11---%E5%AE%89%E8%A3%85%E4%BE%9D%E8%B5%96&quot; aria-label=&quot;11   安装依赖 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.1 - 安装依赖&lt;/h3&gt;
&lt;p&gt;你需要安装 &lt;a href=&quot;https://nodejs.org/en/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Node.js&lt;/a&gt;(版本 6 或更高) 来运行本应用的后端。&lt;/p&gt;
&lt;p&gt;为该项目创建一个空目录，并添加包含如下内容的文件 &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;open-cryptochat&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;8.1.4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;license&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MIT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;patrick.triest#gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;End-to-end RSA-2048 encrypted chat application.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;app.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;engines&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&gt;=7.6&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node app.js&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;express&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4.15.3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;socket.io&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2.0.3&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在命令行中运行 &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;，安装两个 Node.js 依赖。&lt;/p&gt;
&lt;h3 id=&quot;12---创建-nodejs-应用&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#12---%E5%88%9B%E5%BB%BA-nodejs-%E5%BA%94%E7%94%A8&quot; aria-label=&quot;12   创建 nodejs 应用 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.2 - 创建 Node.js 应用&lt;/h3&gt;
&lt;p&gt;创建文件 &lt;code class=&quot;language-text&quot;&gt;app.js&lt;/code&gt;，添加如下内容。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; express &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;express&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 设置 Express 服务器&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; http &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 将 Socket.io 附加到服务器&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; io &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;socket.io&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 伺服 Web 应用目录&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;express&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;public&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 这里插入 SOCKET.IO 代码&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 启动服务&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;
http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Chat server listening on port &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;port&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这就是服务的核心逻辑。现在，它要做的就是启动服务，并使本地 &lt;code class=&quot;language-text&quot;&gt;/public&lt;/code&gt; 目录中的所有文件可供 Web客户端访问。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;生产环境中，我强烈建议你将前端代码与 Node.js 后端应用分开伺服，使用 &lt;a href=&quot;https://httpd.apache.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apache&lt;/a&gt; 和 &lt;a href=&quot;https://www.nginx.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nginx&lt;/a&gt; 等久经沙场的服务器软件，或将网站托管在 &lt;a href=&quot;https://aws.amazon.com/s3/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AWS S3&lt;/a&gt; 等文件存储服务上。本教程为简单起见，使用 Express 静态文件服务器来伺服前端程序。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;13---添加前端&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#13---%E6%B7%BB%E5%8A%A0%E5%89%8D%E7%AB%AF&quot; aria-label=&quot;13   添加前端 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.3 - 添加前端&lt;/h3&gt;
&lt;p&gt;创建新目录 &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt;。这里放所有前端应用代码。&lt;/p&gt;
&lt;h5 id=&quot;131---添加-html-模板&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#131---%E6%B7%BB%E5%8A%A0-html-%E6%A8%A1%E6%9D%BF&quot; aria-label=&quot;131   添加 html 模板 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.3.1 - 添加 HTML 模板&lt;/h5&gt;
&lt;p&gt;新建文件 &lt;code class=&quot;language-text&quot;&gt;/public/index.html&lt;/code&gt;，添加如下内容。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;utf-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Open Cryptochat&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;A minimalist, open-source, end-to-end RSA-2048 encrypted chat application.&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.googleapis.com/css?family=Montserrat:300,400&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.googleapis.com/css?family=Roboto+Mono&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/styles.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;vue-instance&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Add Chat Container Here --&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;info-container full-width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Add Room UI Here --&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;notification-list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;notificationContainer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;NOTIFICATION LOG&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;notification full-width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;notification in notifications&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;notification-timestamp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ notification.timestamp }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;notification-message&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ notification.message }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flex-fill&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Add Encryption Key UI Here --&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Add Bottom Bar Here --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.min.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.slim.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/page.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;该模板设置基础 HTML 结构并下载客户端 JS 依赖项。等添加客户端 JS 代码后，它还会显示一个简单的通知列表。&lt;/p&gt;
&lt;h5 id=&quot;132---创建-vuejs-应用程序&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#132---%E5%88%9B%E5%BB%BA-vuejs-%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F&quot; aria-label=&quot;132   创建 vuejs 应用程序 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.3.2 - 创建 Vue.js 应用程序&lt;/h5&gt;
&lt;p&gt;新建 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 并添加如下内容。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** 控制 UI 的 Vue 实例 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; vm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;#vue-instance&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;cryptWorker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;originPublicKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;destinationPublicKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;notifications&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;currentRoom&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;pendingRoom&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;draft&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;created&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hello World&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/** 在 UI 中追加一条通知消息 */&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLocaleTimeString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notifications&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timestamp &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;该脚本将初始化 Vue.js 应用，并在 UI 中显示一个 “Hello World” 通知。&lt;/p&gt;
&lt;h5 id=&quot;133---添加样式&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#133---%E6%B7%BB%E5%8A%A0%E6%A0%B7%E5%BC%8F&quot; aria-label=&quot;133   添加样式 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.3.3 - 添加样式&lt;/h5&gt;
&lt;p&gt;新建文件 &lt;code class=&quot;language-text&quot;&gt;/public/styles.css&lt;/code&gt;，粘贴进如下样式。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Global */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;:root &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--black&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #111111&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--light-grey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #d6d6d6&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--highlight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yellow&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;body &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--black&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-grey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Roboto Mono&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; monospace&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100vh&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;div &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; border-box&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;input, textarea, select &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inherit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; small&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;textarea:focus, input:focus &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.full-width &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.green &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; green&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.red &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.yellow &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yellow&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.center-x &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.center-text &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;h1, h2, h3 &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Montserrat&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;h1 &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; medium&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;h2 &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; small&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 300&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;h3 &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; x-small&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 300&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;p &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; x-small&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.clearfix:after &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token property&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token property&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; both&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;#vue-instance &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; row&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1 0 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow-x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** Chat Window **/&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.chat-container &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 60%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;word-wrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; break-word&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow-x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow-y&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scroll&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 6px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.message &gt; p &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; small&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.title-header &gt; p &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Montserrat&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 300&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* Info Panel */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.info-container &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 40%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; solid 1px &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-grey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 12px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow-x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow-y&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scroll&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;justify-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; space-around&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; column&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.divider &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;max-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-grey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 12px -12px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.notification-list &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; column&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scroll&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 24px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1 0 40%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.notification &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Montserrat&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 300&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; small&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4px 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.notification-timestamp &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 20%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 12px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.notification-message &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 80%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.notification:last-child &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 24px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.keys &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; xx-small&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow-x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow-y&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scroll&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.keys &gt; .divider &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 75%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 16px auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.key &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scroll&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.room-select &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;min-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 24px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Montserrat&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 300&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;#room-input &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 60%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-grey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-grey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-grey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-grey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.yellow-button &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 30%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--highlight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--highlight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.yellow-button:hover &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--highlight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--black&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.yellow &gt; a &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--highlight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.loader &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4px solid black&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4px solid &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--highlight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 48px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 48px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; spin 2s linear infinite&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; spin&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;0% &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;100% &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;360deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* Message Input Bar */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.message-input &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-grey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 90%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.bottom-bar &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; solid 1px &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-grey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--black&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 12px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 48px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.message-list &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里不深入讨论 CSS，但我向您保证它相当简单。&lt;/p&gt;
&lt;p&gt;简单起见，我们不需要在前端中添加构建系统。在我看来，对于一个如此简单的应用程序来说，构建系统并不是必须的(完成应用程序的 gzip 压缩总负载小于 100kb)。
非常欢迎(并鼓励，因为它将允许应用程序向后兼容过时的浏览器)你添加一个构建系统，如 &lt;a href=&quot;https://webpack.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Webpack&lt;/a&gt;、 &lt;a href=&quot;https://gulpjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gulp&lt;/a&gt; 或 &lt;a href=&quot;https://rollupjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Rollup&lt;/a&gt;，如果您决定将此代码应用到您自己的项目中。&lt;/p&gt;
&lt;h3 id=&quot;14---试试看&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#14---%E8%AF%95%E8%AF%95%E7%9C%8B&quot; aria-label=&quot;14   试试看 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.4 - 试试看&lt;/h3&gt;
&lt;p&gt;在命令行中运行 &lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt;。应该能看到命令行输出 &lt;code class=&quot;language-text&quot;&gt;Chat server listening on port 3000.&lt;/code&gt;。
在浏览器中访问 &lt;a href=&quot;http://localhost:3000&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;http://localhost:3000&lt;/code&gt;&lt;/a&gt;，应该能看到一个空的黑色界面，在页面上显示 “Hello World”。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e10103d34f0e1784c46f63d84108f13c/67c4b/screenshot_1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.78947368421052%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAMklEQVR42mNQV1dXVVWVlZUVEBAQJBEwiIiIALUJCQkJkg5AmgXJBaOah45m8pIHRDMAiFAsyIvMBpUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/e10103d34f0e1784c46f63d84108f13c/5ed05/screenshot_1.webp 190w,
/static/e10103d34f0e1784c46f63d84108f13c/9d76b/screenshot_1.webp 380w,
/static/e10103d34f0e1784c46f63d84108f13c/33466/screenshot_1.webp 760w,
/static/e10103d34f0e1784c46f63d84108f13c/ca244/screenshot_1.webp 1140w,
/static/e10103d34f0e1784c46f63d84108f13c/34e40/screenshot_1.webp 1440w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/e10103d34f0e1784c46f63d84108f13c/253c6/screenshot_1.png 190w,
/static/e10103d34f0e1784c46f63d84108f13c/810ee/screenshot_1.png 380w,
/static/e10103d34f0e1784c46f63d84108f13c/b4918/screenshot_1.png 760w,
/static/e10103d34f0e1784c46f63d84108f13c/c0204/screenshot_1.png 1140w,
/static/e10103d34f0e1784c46f63d84108f13c/67c4b/screenshot_1.png 1440w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/e10103d34f0e1784c46f63d84108f13c/b4918/screenshot_1.png&quot;
            alt=&quot;Screenshot 1&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;2---基础消息收发&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2---%E5%9F%BA%E7%A1%80%E6%B6%88%E6%81%AF%E6%94%B6%E5%8F%91&quot; aria-label=&quot;2   基础消息收发 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2 - 基础消息收发&lt;/h2&gt;
&lt;p&gt;现在项目脚手架已就绪，我们将开始添加基础的(非加密)实时消息收发功能。&lt;/p&gt;
&lt;h3 id=&quot;21---设置服务器端-socket-监听器&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#21---%E8%AE%BE%E7%BD%AE%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF-socket-%E7%9B%91%E5%90%AC%E5%99%A8&quot; aria-label=&quot;21   设置服务器端 socket 监听器 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.1 - 设置服务器端 Socket 监听器&lt;/h3&gt;
&lt;p&gt;在文件 &lt;code class=&quot;language-text&quot;&gt;/app.js&lt;/code&gt; 的注释 &lt;code class=&quot;language-text&quot;&gt;// 这里插入 SOCKET.IO 代码&lt;/code&gt; 处，添加如下代码。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** 管理每个客户端 socket 连接的行为 */&lt;/span&gt;
io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;connection&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;User Connected - Socket ID &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 储存 socket 连接到的聊天室&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; currentRoom &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;DEFAULT&apos;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/** 处理加入聊天室的请求. */&lt;/span&gt;
  socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;JOIN&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;roomName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 通知用户，加入聊天室成功&lt;/span&gt;
    io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ROOM_JOINED&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 通知聊天室，用户加入成功&lt;/span&gt;
    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;NEW_CONNECTION&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/** 在聊天室广播一条收到的消息 */&lt;/span&gt;
  socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;MESSAGE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;New Message - &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;MESSAGE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;此代码块将创建一个连接监听器，管理从前端应用程序连接到服务器的所有客户端。
目前，它只将用户添加到 &lt;code class=&quot;language-text&quot;&gt;DEFAULT&lt;/code&gt; 聊天室中，然后将接收到的所有消息重新发送给聊天室中的其他用户。&lt;/p&gt;
&lt;h3 id=&quot;22---设置客户端-socket-监听器&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#22---%E8%AE%BE%E7%BD%AE%E5%AE%A2%E6%88%B7%E7%AB%AF-socket-%E7%9B%91%E5%90%AC%E5%99%A8&quot; aria-label=&quot;22   设置客户端 socket 监听器 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.2 - 设置客户端 Socket 监听器&lt;/h3&gt;
&lt;p&gt;在前端中，我们将添加一些连接到服务器的代码，用如下代码替换文件 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 中的 &lt;code class=&quot;language-text&quot;&gt;created&lt;/code&gt; 函数。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;created&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 初始化 socket.io&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setupSocketListeners&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;接着，我们要添加一些自定义函数来管理客户端 Socket 连接并收发消息。
在文件 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 中 Vue 应用的 &lt;code class=&quot;language-text&quot;&gt;methods&lt;/code&gt; 代码块中添加如下。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** 设置 Socket.io 事件监听器 */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;setupSocketListeners&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 连接时自动加入默认聊天室&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;connect&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Connected To Server.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;joinRoom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 通知用户丢失 socket 连接&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;disconnect&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Lost Connection&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 接收消息并显示&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;MESSAGE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** 发送消息 */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 消息为空时不发送&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;draft &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;draft &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;draft

  &lt;span class=&quot;token comment&quot;&gt;// 重置输入文本&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;draft &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 将消息立即显示到本地 UI&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 发送消息&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;MESSAGE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** 加入聊天室 */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;joinRoom&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;JOIN&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** 将消息添加到 UI */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;addMessage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;messages&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;23---将消息显示到-ui&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#23---%E5%B0%86%E6%B6%88%E6%81%AF%E6%98%BE%E7%A4%BA%E5%88%B0-ui&quot; aria-label=&quot;23   将消息显示到 ui permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.3 - 将消息显示到 UI&lt;/h3&gt;
&lt;p&gt;最后，我们为发送与显示消息提供一个 UI。&lt;/p&gt;
&lt;p&gt;为在当前聊天中显示所有消息，在文件 &lt;code class=&quot;language-text&quot;&gt;/public/index.html&lt;/code&gt; 的 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;!-- Add Chat Container Here --&gt;&lt;/code&gt; 注释处添加如下。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;chat-container full-width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;chatContainer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;message-list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;message full-width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;message in messages&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &gt; {{ message }}
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;添加一个文本输入框让用户输入消息，在 &lt;code class=&quot;language-text&quot;&gt;/public/index.html&lt;/code&gt; 的 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;!-- Add Bottom Bar Here --&gt;&lt;/code&gt; 注释处添加如下。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bottom-bar full-width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;message-input&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Message&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-model&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;draft&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@keyup.enter&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;sendMessage()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;现在重启服务，在浏览器的两个不同标签页或窗口中打开 &lt;a href=&quot;http://localhost:3000&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;http://localhost:3000&lt;/code&gt;&lt;/a&gt;。
尝试在标签页之间来回发送消息。在命令行中，您应该能够看到正在发送的消息的服务器日志。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/812ad39380a23adc53cbae0d1762f4b8/67c4b/screenshot_2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.78947368421052%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAZElEQVR42u2SOw7AIAxDcwNCAkJFDAyQ+x8Rd6k68Fk69k1R7LeZWmu995SSXyAiZoZOrTXGyMxPRNBKKVfOGzmEAEdVcb8jwtcBdhsZ2jQif+KXP5TlBExsYxrdI8EGdMuqMADN1i5ybuV6rAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/812ad39380a23adc53cbae0d1762f4b8/5ed05/screenshot_2.webp 190w,
/static/812ad39380a23adc53cbae0d1762f4b8/9d76b/screenshot_2.webp 380w,
/static/812ad39380a23adc53cbae0d1762f4b8/33466/screenshot_2.webp 760w,
/static/812ad39380a23adc53cbae0d1762f4b8/ca244/screenshot_2.webp 1140w,
/static/812ad39380a23adc53cbae0d1762f4b8/34e40/screenshot_2.webp 1440w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/812ad39380a23adc53cbae0d1762f4b8/253c6/screenshot_2.png 190w,
/static/812ad39380a23adc53cbae0d1762f4b8/810ee/screenshot_2.png 380w,
/static/812ad39380a23adc53cbae0d1762f4b8/b4918/screenshot_2.png 760w,
/static/812ad39380a23adc53cbae0d1762f4b8/c0204/screenshot_2.png 1140w,
/static/812ad39380a23adc53cbae0d1762f4b8/67c4b/screenshot_2.png 1440w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/812ad39380a23adc53cbae0d1762f4b8/b4918/screenshot_2.png&quot;
            alt=&quot;Screenshot 2&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d2ac120214b07292db389b0fe54991e8/2658a/screenshot_3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16.842105263157894%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAX0lEQVR42mOQkpJWUlaWEJcQEREVFBSSkZERFBTk5uZmY2NjIAgMDAwtLK20tXWMjU0MDA11dPU0NLXU1NWFhISAsoyMjPg06wJVa2hISUlpammpqKoqKiry8fExEAcA+HUJiQxGLCEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/d2ac120214b07292db389b0fe54991e8/5ed05/screenshot_3.webp 190w,
/static/d2ac120214b07292db389b0fe54991e8/9d76b/screenshot_3.webp 380w,
/static/d2ac120214b07292db389b0fe54991e8/33466/screenshot_3.webp 760w,
/static/d2ac120214b07292db389b0fe54991e8/79961/screenshot_3.webp 792w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/d2ac120214b07292db389b0fe54991e8/253c6/screenshot_3.png 190w,
/static/d2ac120214b07292db389b0fe54991e8/810ee/screenshot_3.png 380w,
/static/d2ac120214b07292db389b0fe54991e8/b4918/screenshot_3.png 760w,
/static/d2ac120214b07292db389b0fe54991e8/2658a/screenshot_3.png 792w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/d2ac120214b07292db389b0fe54991e8/b4918/screenshot_3.png&quot;
            alt=&quot;Screenshot 3&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;数据加密-101&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%95%B0%E6%8D%AE%E5%8A%A0%E5%AF%86-101&quot; aria-label=&quot;数据加密 101 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;数据加密 101&lt;/h3&gt;
&lt;p&gt;Cool, 现在我们有了一个实时消息收发应用。在添加端到端加密之前，必须对非对称加密的工作原理有基本的了解。&lt;/p&gt;
&lt;h4 id=&quot;对称加密-与-单向函数&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86-%E4%B8%8E-%E5%8D%95%E5%90%91%E5%87%BD%E6%95%B0&quot; aria-label=&quot;对称加密 与 单向函数 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;对称加密 与 单向函数&lt;/h4&gt;
&lt;p&gt;假设我们在交换一个秘密数字。我们通过第三方发送这个数字，但不想让第三方知道数字是什么。&lt;/p&gt;
&lt;p&gt;为实现这一点，我们先共享一个密钥 —— 就用 &lt;code class=&quot;language-text&quot;&gt;7&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;为了加密消息，先用一个随机数字 &lt;code class=&quot;language-text&quot;&gt;n&lt;/code&gt; 与密钥(&lt;code class=&quot;language-text&quot;&gt;7&lt;/code&gt;)相乘，再与 &lt;code class=&quot;language-text&quot;&gt;x&lt;/code&gt; 相加。
该等式中，&lt;code class=&quot;language-text&quot;&gt;x&lt;/code&gt; 表示我们要发送的数字，&lt;code class=&quot;language-text&quot;&gt;y&lt;/code&gt; 表示加密后的结果。&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;(7 * n) + x = y&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;然后，我们可以使用模运算将加密的输入转换为解密的输出。&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;y mod 7 = x&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这里，&lt;code class=&quot;language-text&quot;&gt;y&lt;/code&gt; 是公开的(加密的)消息，&lt;code class=&quot;language-text&quot;&gt;x&lt;/code&gt; 是原始的未加密消息。&lt;/p&gt;
&lt;p&gt;如果一方想交换数字 &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;，我们可以计算 &lt;code class=&quot;language-text&quot;&gt;(7*4) + 2&lt;/code&gt; 并发送 &lt;code class=&quot;language-text&quot;&gt;30&lt;/code&gt; 作为消息。
我们都知道密钥 (&lt;code class=&quot;language-text&quot;&gt;7&lt;/code&gt;)，所以都能计算 &lt;code class=&quot;language-text&quot;&gt;30 mod 7&lt;/code&gt;，并确定 &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; 是原始的数字。&lt;/p&gt;
&lt;p&gt;原始数字(&lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;)实际上是隐藏起来的，因为我们传递的唯一消息是 &lt;code class=&quot;language-text&quot;&gt;30&lt;/code&gt;。
就算第三方能同时得到加密的结果(&lt;code class=&quot;language-text&quot;&gt;30&lt;/code&gt;)和加密的未数字(&lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;)，他仍然不知道密钥的值。
本例中，&lt;code class=&quot;language-text&quot;&gt;30 mod 14&lt;/code&gt; 与 &lt;code class=&quot;language-text&quot;&gt;30 mod 28&lt;/code&gt; 都等于 &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;，所以中间人并不能确定密钥是 &lt;code class=&quot;language-text&quot;&gt;7&lt;/code&gt;，&lt;code class=&quot;language-text&quot;&gt;14&lt;/code&gt;还是 &lt;code class=&quot;language-text&quot;&gt;28&lt;/code&gt;，因此无法可靠地破译下一条消息。&lt;/p&gt;
&lt;p&gt;因此，取模(Modulo)运算被认为是一个“单向”函数，因为它不能被简单地逆运算。&lt;/p&gt;
&lt;p&gt;现代加密算法就是对该通用原理的复杂化应用。
通过使用大质数(large prime numbers)，模幂(modular exponentiation)，长私钥和多轮密码转换，破解这些算法通常要花费非常长的时间(1+百万年)。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;理论上，量子计算机可以很快破解。详情见 &lt;a href=&quot;https://www.infoworld.com/article/3040991/security/mits-new-5-atom-quantum-computer-could-make-todays-encryption-obsolete.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;这里&lt;/a&gt;。
这项技术还处于起步阶段，所以我们可能还无需担心加密数据会以这种方式泄露。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;上面的示例假设双方都可以提前交换密钥(本例中为7)。这被称为 &lt;em&gt;对称加密&lt;/em&gt;，因为加密和解密消息都使用相同的密钥。
然而，在 Internet 上，这通常不是可行 —— 我们需要一种不必离线协调获取共享密钥，而加密消息的方法。
这就是非对称加密发挥作用的地方。&lt;/p&gt;
&lt;h4 id=&quot;公钥加密&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%85%AC%E9%92%A5%E5%8A%A0%E5%AF%86&quot; aria-label=&quot;公钥加密 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;公钥加密&lt;/h4&gt;
&lt;p&gt;与对称加密使用一个共享密钥不同，公钥加密(非对称加密)使用一对密钥(公钥、私钥)，&lt;em&gt;公钥&lt;/em&gt; 用于加密数据，&lt;em&gt;私钥&lt;/em&gt; 用于解密。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;公钥&lt;/em&gt; 就像一个有牢不可破锁的公开投信箱。如果有人想给你发消息，就将消息投入这个公开信箱，然后盖上盖子把它锁上。
这时信箱就能让不被信任的第三方传递发送，而不必担心内容被曝光。
一旦我收到信箱，我会用我的 &lt;em&gt;私钥&lt;/em&gt; ——只有我有，来打开信箱。&lt;/p&gt;
&lt;p&gt;交换 &lt;em&gt;公钥&lt;/em&gt; 就像交换这些公开信箱，但 &lt;em&gt;私钥&lt;/em&gt; 仅由信箱所有者保管，所以在信箱传递过程中能保证内容的安全。&lt;/p&gt;
&lt;p&gt;当然，这是对公钥加密原理的一个简化解释。如果你好奇(特别是关于这些技术的历史和数学基础)，我强烈推荐你从这两个视频开始。&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/YEBfamv-_do?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/wXB-V_Keiu8?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;h2 id=&quot;3---用-web-worker-处理加密&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3---%E7%94%A8-web-worker-%E5%A4%84%E7%90%86%E5%8A%A0%E5%AF%86&quot; aria-label=&quot;3   用 web worker 处理加密 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3 - 用 Web Worker 处理加密&lt;/h2&gt;
&lt;p&gt;加密操作往往是计算密集型的。由于 Javascript 是单线程，在 UI 主线程上处理加密会导致浏览器卡顿几秒钟。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;将加密操作包装在 Promise 中也没用，因为 Promise 是在单线程中管理异步操作，而不是改善计算密集型任务的性能。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为保证应用程序的性能，我们采用 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Web Worker&lt;/a&gt; 在浏览器单独的线程中执行加密运算。
我们将使用 &lt;a href=&quot;https://github.com/travist/jsencrypt&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;JSEncrypt&lt;/a&gt;，这是一个源自斯坦福的著名 Javascript RSA 实现。
使用 JSEncrypt 来创建一些帮助函数，用于加密、解密与生成密钥对。&lt;/p&gt;
&lt;h3 id=&quot;31---创建-web-worker-来包装-jsencrypt-方法&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#31---%E5%88%9B%E5%BB%BA-web-worker-%E6%9D%A5%E5%8C%85%E8%A3%85-jsencrypt-%E6%96%B9%E6%B3%95&quot; aria-label=&quot;31   创建 web worker 来包装 jsencrypt 方法 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.1 - 创建 Web Worker 来包装 JSEncrypt 方法&lt;/h3&gt;
&lt;p&gt;在目录 &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt; 中新建文件 &lt;code class=&quot;language-text&quot;&gt;crypto-worker.js&lt;/code&gt;。
这个文件存放 Web Worker 的代码，以便在一个单独的浏览器线程上执行加密操作。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;window &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self &lt;span class=&quot;token comment&quot;&gt;// 在 Web Worker 中使用 JSEncrypt 库，这行是必须的&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 导入 JSEncrypt 库&lt;/span&gt;
self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;importScripts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/2.3.1/jsencrypt.min.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; crypt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; privateKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** Web Worker onmessage 监听器 */&lt;/span&gt;
&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; messageType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; messageId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result
  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;messageType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;generate-keys&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateKeypair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;encrypt&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;encrypt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;decrypt&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;decrypt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 返回结果给 UI 线程&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; messageId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; result &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** 生成与储存密钥对 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateKeypair&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  crypt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSEncrypt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;default_key_size&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2056&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  privateKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPrivateKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 仅返回公钥，隐藏私钥&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; crypt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPublicKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** 使用目标公钥加密字符串 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;encrypt&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;content&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; publicKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  crypt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;publicKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; crypt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;encrypt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** 使用本地私钥解密字符串 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;decrypt&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  crypt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;privateKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; crypt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decrypt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;该 Web Worker 在 &lt;code class=&quot;language-text&quot;&gt;onmessage&lt;/code&gt; 监听器中接收来自 UI 线程的消息，执行请求的操作，并返回结果到 UI 线程。
私钥永远不会直接暴露给 UI 线程，这有助于减少跨站脚本攻击(&lt;a href=&quot;https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;XSS&lt;/a&gt;)中私钥被盗的可能性。&lt;/p&gt;
&lt;h3 id=&quot;32---配置-vue-应用程序与-web-worker-通讯&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#32---%E9%85%8D%E7%BD%AE-vue-%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E4%B8%8E-web-worker-%E9%80%9A%E8%AE%AF&quot; aria-label=&quot;32   配置 vue 应用程序与 web worker 通讯 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.2 - 配置 Vue 应用程序与 Web Worker 通讯&lt;/h3&gt;
&lt;p&gt;下面，我们配置 UI controller 与 Web Worker 通讯。
使用事件侦听器按顺序的调用/响应通信很难同步。
为简化，我们创建一个工具函数，将整个通讯周期包装到 Promise 中。
在 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 中的 &lt;code class=&quot;language-text&quot;&gt;methods&lt;/code&gt; 代码块中添加如下。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** 向 Web Worker 发消息，返回一个包含结果的 Promise。 */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;getWebWorkerResponse&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;messageType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; messagePayload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 生成一个随机消息 id 来标识相应的事件回调&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; messageId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 发送消息给 Web Worker&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cryptWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;messageType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; messageId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;messagePayload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 为 Web Worker 消息事件创建句柄&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// 仅处理消息 id 匹配的消息&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; messageId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 监听器被调用后移除之&lt;/span&gt;
        e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentTarget&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Resolve the promise with the message payload.&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 将句柄分配给 Web Worker &apos;message&apos; 事件。&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cryptWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;message&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;该代码允许我们在 Web Worker 线程中触发一个操作，并在 Promise 中接收返回结果。
在将调用/响应处理外包给 Web Worker 的任何项目中，这都是非常有用的帮助函数。&lt;/p&gt;
&lt;h2 id=&quot;4---密钥交换&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4---%E5%AF%86%E9%92%A5%E4%BA%A4%E6%8D%A2&quot; aria-label=&quot;4   密钥交换 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4 - 密钥交换&lt;/h2&gt;
&lt;p&gt;在我们的应用中，第一步是为每个用户生成一对密钥。
然后一旦用户进入同一个对话，应用程序会交换用户的 &lt;em&gt;公钥&lt;/em&gt;，这样每个用户就能解密属于自己的消息，而其他人只能加密。&lt;/p&gt;
&lt;p&gt;因此，应用程序始终使用接收者的 &lt;em&gt;公钥&lt;/em&gt; 来加密消息，使用用户自己的 &lt;em&gt;私钥&lt;/em&gt; 解密消息。&lt;/p&gt;
&lt;h3 id=&quot;41---添加服务器端-socket-监听器来传递公钥&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#41---%E6%B7%BB%E5%8A%A0%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF-socket-%E7%9B%91%E5%90%AC%E5%99%A8%E6%9D%A5%E4%BC%A0%E9%80%92%E5%85%AC%E9%92%A5&quot; aria-label=&quot;41   添加服务器端 socket 监听器来传递公钥 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4.1 - 添加服务器端 Socket 监听器来传递公钥&lt;/h3&gt;
&lt;p&gt;在服务器端，需要一个 Socket 监听器接收从客户端发来的公钥，并将其广播给聊天室中其他人。
还需要一个监听器通知客户端，何人何时从当前聊天室中退出。&lt;/p&gt;
&lt;p&gt;在 &lt;code class=&quot;language-text&quot;&gt;/app.js&lt;/code&gt; 的 &lt;code class=&quot;language-text&quot;&gt;io.on(&apos;connection&apos;, (socket) =&gt; { ... }&lt;/code&gt; 回调中添加监听器。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** 广播公钥到聊天室 */&lt;/span&gt;
socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;PUBLIC_KEY&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;PUBLIC_KEY&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** 广播断开连接到聊天室 */&lt;/span&gt;
socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;disconnect&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;USER_DISCONNECTED&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;42---生成密钥对&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#42---%E7%94%9F%E6%88%90%E5%AF%86%E9%92%A5%E5%AF%B9&quot; aria-label=&quot;42   生成密钥对 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4.2 - 生成密钥对&lt;/h3&gt;
&lt;p&gt;下一步，替换 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 中的 &lt;code class=&quot;language-text&quot;&gt;created&lt;/code&gt; 函数，用于初始化 Web Worker 和生成一对密钥。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;created&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Welcome! Generating a new keypair now.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Initialize crypto webworker thread&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cryptWorker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;crypto-worker.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Generate keypair and join default room&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;originPublicKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebWorkerResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;generate-keys&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Keypair Generated&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Initialize socketio&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setupSocketListeners&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里使用 &lt;a href=&quot;https://blog.patricktriest.com/what-is-async-await-why-should-you-care/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;async/await 语法&lt;/a&gt; 在一行代码中接收 Web Worker 返回的 Promise。&lt;/p&gt;
&lt;h3 id=&quot;43---添加公钥帮助函数&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#43---%E6%B7%BB%E5%8A%A0%E5%85%AC%E9%92%A5%E5%B8%AE%E5%8A%A9%E5%87%BD%E6%95%B0&quot; aria-label=&quot;43   添加公钥帮助函数 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4.3 - 添加公钥帮助函数&lt;/h3&gt;
&lt;p&gt;还要在 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 添加一些函数用于发送公钥，并将密钥简化为人类可读的标识符。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** 发送公钥给聊天室中所有用户 */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sendPublicKey&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;originPublicKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;PUBLIC_KEY&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;originPublicKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** 获取用于显示的密钥片段 */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;getKeySnippet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;416&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;44---收发公钥&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#44---%E6%94%B6%E5%8F%91%E5%85%AC%E9%92%A5&quot; aria-label=&quot;44   收发公钥 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4.4 - 收发公钥&lt;/h3&gt;
&lt;p&gt;下面在客户端 Socket 代码中添加一些监听器，用于当有新用户加入聊天室时发送本地公钥，并保存接收到的其他用户的公钥。&lt;/p&gt;
&lt;p&gt;在 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 的 &lt;code class=&quot;language-text&quot;&gt;setupSocketListeners&lt;/code&gt; 函数处添加如下。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 有新用户加入当前聊天室时，向他发送你的公钥&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;NEW_CONNECTION&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Another user joined the room.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendPublicKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 加入一个聊天室时广播公钥&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ROOM_JOINED&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;newRoom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentRoom &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newRoom
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Joined Room - &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentRoom&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendPublicKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 收到公钥时保存之&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;PUBLIC_KEY&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Public Key Received - &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getKeySnippet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destinationPublicKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; key
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 有用户离开聊天室时删除用户对应公钥&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;user disconnected&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;User Disconnected - &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getKeySnippet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destinationKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destinationPublicKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;45---在-ui-中显示公钥&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#45---%E5%9C%A8-ui-%E4%B8%AD%E6%98%BE%E7%A4%BA%E5%85%AC%E9%92%A5&quot; aria-label=&quot;45   在 ui 中显示公钥 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4.5 - 在 UI 中显示公钥&lt;/h3&gt;
&lt;p&gt;最后，添加一些 HTML 用于显示两个公钥。&lt;/p&gt;
&lt;p&gt;在 &lt;code class=&quot;language-text&quot;&gt;/public/index.html&lt;/code&gt; 的 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;!-- Add Encryption Key UI Here --&gt;&lt;/code&gt; 注释后添加如下。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;divider&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;keys full-width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;KEYS&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;THEIR PUBLIC KEY&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;key red&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-if&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;destinationPublicKey&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;TRUNCATED IDENTIFIER - {{ getKeySnippet(destinationPublicKey) }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ destinationPublicKey }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Waiting for second user to join room...&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;divider&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;YOUR PUBLIC KEY&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;key green&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-if&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;originPublicKey&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;TRUNCATED IDENTIFIER - {{ getKeySnippet(originPublicKey) }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ originPublicKey }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;keypair-loader full-width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;center-x loader&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;center-text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Generating Keypair...&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;重启应用程序并刷新 &lt;code class=&quot;language-text&quot;&gt;http://localhost:3000&lt;/code&gt;。
打开两个浏览器标签页，应该就能成功模拟一次密钥交换。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/67c4b/screenshot_4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.78947368421052%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2ElEQVR42pWSTW7DIBCFfYAa5geMAcsVpCaWrTS77rtspKj3v02nthp1VdOnt0Ezj28G0eCRiOh8nkspKSXvfQjBGAMAUmqOssDM67pK8vp6LdNEm/ZaU0M+5RyH4bRpHJ+ttZVk3FF79+P4D3KIka1FZjH9WFQRlp1zfun7EdEjGsRus60hC/pd61v79KHVXalPpd60jgChcuyL7Ra2BXlCnpETcS+TV4Zt9pRcO2AbsfWoHIL5dkNHkoehmdSiYQFYAS6AGamTK6lxzsmP4T9lesOPll+9X3fyMyV7m2ZtAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/5ed05/screenshot_4.webp 190w,
/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/9d76b/screenshot_4.webp 380w,
/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/33466/screenshot_4.webp 760w,
/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/ca244/screenshot_4.webp 1140w,
/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/34e40/screenshot_4.webp 1440w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/253c6/screenshot_4.png 190w,
/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/810ee/screenshot_4.png 380w,
/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/b4918/screenshot_4.png 760w,
/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/c0204/screenshot_4.png 1140w,
/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/67c4b/screenshot_4.png 1440w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/b8bb4c638be1fd2cfeb13a2f87ab8cf8/b4918/screenshot_4.png&quot;
            alt=&quot;Screenshot 4&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在多于两个标签页中打开应用，交换密钥应该会失败。我们将进一步解决此问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;5---消息加密&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5---%E6%B6%88%E6%81%AF%E5%8A%A0%E5%AF%86&quot; aria-label=&quot;5   消息加密 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5 - 消息加密&lt;/h2&gt;
&lt;p&gt;现在密钥交换已完成，加密、解密消息就很简单了。&lt;/p&gt;
&lt;h3 id=&quot;51---发送前加密消息&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#51---%E5%8F%91%E9%80%81%E5%89%8D%E5%8A%A0%E5%AF%86%E6%B6%88%E6%81%AF&quot; aria-label=&quot;51   发送前加密消息 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5.1 - 发送前加密消息&lt;/h3&gt;
&lt;p&gt;替换 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 中的 &lt;code class=&quot;language-text&quot;&gt;sendMessage&lt;/code&gt; 函数为如下。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** 加密并发送当前消息 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 消息为空时不发送&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;draft &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;draft &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 使用 Immutable.js来避免意外的副作用。&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Immutable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;draft&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;recipient&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destinationPublicKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;originPublicKey
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 重置输入文本&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;draft &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 立即将(未加密的)消息显示到本地 UI&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destinationPublicKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 使用其他用户的公钥加密消息&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; encryptedText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebWorkerResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&apos;encrypt&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;text&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destinationPublicKey &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; encryptedMsg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;text&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; encryptedText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 发送加密的消息&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;MESSAGE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; encryptedMsg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;52---接收与解密消息&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#52---%E6%8E%A5%E6%94%B6%E4%B8%8E%E8%A7%A3%E5%AF%86%E6%B6%88%E6%81%AF&quot; aria-label=&quot;52   接收与解密消息 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5.2 - 接收与解密消息&lt;/h3&gt;
&lt;p&gt;修改 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 中的 &lt;code class=&quot;language-text&quot;&gt;message&lt;/code&gt; 监听器，用于解密接收到的消息。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 解密与显示接收到的消息&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;MESSAGE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 仅解密用用户公钥加密的消息&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;recipient &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;originPublicKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 在 Web Worker 线程中解密消息&lt;/span&gt;
    message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWebWorkerResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;decrypt&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 立即将(未加密的)消息显示到本地 UI&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;53---显示消息列表&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#53---%E6%98%BE%E7%A4%BA%E6%B6%88%E6%81%AF%E5%88%97%E8%A1%A8&quot; aria-label=&quot;53   显示消息列表 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5.3 - 显示消息列表&lt;/h3&gt;
&lt;p&gt;修改 &lt;code class=&quot;language-text&quot;&gt;/public/index.html&lt;/code&gt; 中的消息列表 UI(在 &lt;code class=&quot;language-text&quot;&gt;chat-container&lt;/code&gt; 中)，来显示解密的消息和发送者公钥的缩写。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;message full-width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;message in messages&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;v-bind:&lt;/span&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(message.sender == originPublicKey) ? &apos;green&apos; : &apos;red&apos;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ getKeySnippet(message.sender) }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &gt; {{ message.text }}
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;54---试试看&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#54---%E8%AF%95%E8%AF%95%E7%9C%8B&quot; aria-label=&quot;54   试试看 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5.4 - 试试看&lt;/h3&gt;
&lt;p&gt;重启服务并刷新 &lt;a href=&quot;http://localhost:3000&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;http://localhost:3000&lt;/code&gt;&lt;/a&gt;。
UI 界面应该看起来跟之前没有变化，除了会显示每个消息发送者的公钥片段。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3ddcd4f7188d7a9818daab8d83e1442b/67c4b/screenshot_5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.78947368421052%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA9UlEQVR42o2R226DMAyG8wCD+BDnRIeADphoK+52vetqe//XmaFb14tu5ZMVxXHs35ZN420TuWka7wPeg4jGcRyGoW3blFLO2TkHABoyo4NeaN8tpBgvr7cw82GatPo8z33fay2k75DBgORJS4agN7qrrKVzVe1X6rr+VUY9AWHlr7aVa/Ti/ig/Qr+qLIsQs85AN7YhWWfuupcYa8SMKIh+Ndmo/G7tuXj6sOVnudibtRVAtTH5JP7IMiD3yHq2xJGYYFuydInaUDxjscMiYRkQtGmHhh6he6ZXspOFA8AR4ATYLdslIaPr1b3xv7joWK7OaitfZ/M0Zc6o4mYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/3ddcd4f7188d7a9818daab8d83e1442b/5ed05/screenshot_5.webp 190w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/9d76b/screenshot_5.webp 380w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/33466/screenshot_5.webp 760w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/ca244/screenshot_5.webp 1140w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/34e40/screenshot_5.webp 1440w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/3ddcd4f7188d7a9818daab8d83e1442b/253c6/screenshot_5.png 190w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/810ee/screenshot_5.png 380w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/b4918/screenshot_5.png 760w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/c0204/screenshot_5.png 1140w,
/static/3ddcd4f7188d7a9818daab8d83e1442b/67c4b/screenshot_5.png 1440w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/3ddcd4f7188d7a9818daab8d83e1442b/b4918/screenshot_5.png&quot;
            alt=&quot;Screenshot 5&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;br&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/157fcb94638099c9cea089014baaa17d/67c4b/screenshot_6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 15.789473684210527%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAhElEQVR42lWNQQ/CIAyFueC4M0VCQlYTnMmMMK8cpGid/v8/ZEe87Mtr+vL6kooQzt57ABjHS9/vpZTdFqXU1qtu9w9FzrkUXD5fooXojbUiT30hPsujOSJE5A4rhABwGgb+Bc45MU1X5rYSU0oxJl73RkxzXLOZT80ka605GGOOLK31D0LFGkEYvcrnAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/157fcb94638099c9cea089014baaa17d/5ed05/screenshot_6.webp 190w,
/static/157fcb94638099c9cea089014baaa17d/9d76b/screenshot_6.webp 380w,
/static/157fcb94638099c9cea089014baaa17d/33466/screenshot_6.webp 760w,
/static/157fcb94638099c9cea089014baaa17d/ca244/screenshot_6.webp 1140w,
/static/157fcb94638099c9cea089014baaa17d/34e40/screenshot_6.webp 1440w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/157fcb94638099c9cea089014baaa17d/253c6/screenshot_6.png 190w,
/static/157fcb94638099c9cea089014baaa17d/810ee/screenshot_6.png 380w,
/static/157fcb94638099c9cea089014baaa17d/b4918/screenshot_6.png 760w,
/static/157fcb94638099c9cea089014baaa17d/c0204/screenshot_6.png 1140w,
/static/157fcb94638099c9cea089014baaa17d/67c4b/screenshot_6.png 1440w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/157fcb94638099c9cea089014baaa17d/b4918/screenshot_6.png&quot;
            alt=&quot;Screenshot 6&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;在命令行输出中，消息已不再可读 —— 它们现在显示为乱码的加密文本。&lt;/p&gt;
&lt;h2 id=&quot;6---聊天室&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6---%E8%81%8A%E5%A4%A9%E5%AE%A4&quot; aria-label=&quot;6   聊天室 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6 - 聊天室&lt;/h2&gt;
&lt;p&gt;你大概已经注意到当前应用程序的一个重大缺陷 —— 如果在第三个标签页中打开该应用会使加密系统挂掉。
非对称加密被设计用于一对一的场景；无法加密消息 &lt;em&gt;一次&lt;/em&gt; 然后分别被 &lt;em&gt;两个&lt;/em&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;h3 id=&quot;61---进入聊天室的服务器端逻辑&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#61---%E8%BF%9B%E5%85%A5%E8%81%8A%E5%A4%A9%E5%AE%A4%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E9%80%BB%E8%BE%91&quot; aria-label=&quot;61   进入聊天室的服务器端逻辑 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6.1 - 进入聊天室的服务器端逻辑&lt;/h3&gt;
&lt;p&gt;为执行两个用户限制，修改 &lt;code class=&quot;language-text&quot;&gt;/app.js&lt;/code&gt; 中的服务器端 &lt;code class=&quot;language-text&quot;&gt;JOIN&lt;/code&gt; Socket 监听器，在 Socket 连接监听器代码块的上面加入如下。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 保存 Socket 连接到的聊天室&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 如果需要横向扩展应用程序，需将此变量存储在诸如 Redis 之类的持久存储中。&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 详情见： https://github.com/socketio/socket.io-redis&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; currentRoom &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** 处理进入聊天室的请求 */&lt;/span&gt;
socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;JOIN&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;roomName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 获取聊天室信息&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; room &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sockets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;adapter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rooms&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;roomName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 若聊天室已经有多于 1 的连接，则拒绝进入&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;room &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; room&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 通知用户进入聊天室请求被拒绝&lt;/span&gt;
    io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ROOM_FULL&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 通知聊天室有用户进&lt;/span&gt;
    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;roomName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;INTRUSION_ATTEMPT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 离开当前聊天室&lt;/span&gt;
    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;leave&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 通知聊天室用户已离开&lt;/span&gt;
    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;USER_DISCONNECTED&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 进入新聊天室&lt;/span&gt;
    currentRoom &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; roomName
    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 通知用户进入聊天室成功&lt;/span&gt;
    io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ROOM_JOINED&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 通知聊天室用户进入成功&lt;/span&gt;
    socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;NEW_CONNECTION&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;修改这段 Socket 逻辑，防止用户进入一个已有两位用户的聊天室。&lt;/p&gt;
&lt;h3 id=&quot;62---通过客户端进入聊天室&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#62---%E9%80%9A%E8%BF%87%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%BF%9B%E5%85%A5%E8%81%8A%E5%A4%A9%E5%AE%A4&quot; aria-label=&quot;62   通过客户端进入聊天室 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6.2 - 通过客户端进入聊天室&lt;/h3&gt;
&lt;p&gt;修改客户端 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 中的 &lt;code class=&quot;language-text&quot;&gt;joinRoom&lt;/code&gt; 函数，使切换聊天室时重置聊天状态。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** 进入指定的聊天室 */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;joinRoom&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pendingRoom &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentRoom &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;originPublicKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Connecting to Room - &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pendingRoom&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 重置聊天室状态变量&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;messages &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destinationPublicKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 发送进入聊天室请求&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;JOIN&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pendingRoom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;63---添加通知&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#63---%E6%B7%BB%E5%8A%A0%E9%80%9A%E7%9F%A5&quot; aria-label=&quot;63   添加通知 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6.3 - 添加通知&lt;/h3&gt;
&lt;p&gt;创建两个客户端 Socket 监听器(在 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 的 &lt;code class=&quot;language-text&quot;&gt;setupSocketListeners&lt;/code&gt; 函数中)，当进入聊天室请求被拒绝时发出通知。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 通知用户试图加入的聊天室已满&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ROOM_FULL&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Cannot join &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pendingRoom&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, room is full&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 加入一个随机聊天室作为后备&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pendingRoom &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;joinRoom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 通知聊天室有人试图加入&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;INTRUSION_ATTEMPT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;A third user attempted to join the room.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;64---添加进入聊天室-ui&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#64---%E6%B7%BB%E5%8A%A0%E8%BF%9B%E5%85%A5%E8%81%8A%E5%A4%A9%E5%AE%A4-ui&quot; aria-label=&quot;64   添加进入聊天室 ui permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6.4 - 添加进入聊天室 UI&lt;/h3&gt;
&lt;p&gt;最后，添加一些 HTML，为用户提供一个界面来加入他们选择的房间。&lt;/p&gt;
&lt;p&gt;在 &lt;code class=&quot;language-text&quot;&gt;/public/index.html&lt;/code&gt; 的 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;!-- Add Room UI Here --&gt;&lt;/code&gt; 注释处添加如下。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;CHATROOM&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;room-select&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;full-width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Room Name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;room-input&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-model&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;pendingRoom&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@keyup.enter&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;joinRoom()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;yellow-button full-width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;v-on:&lt;/span&gt;click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;joinRoom()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;JOIN&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;divider&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;65---自动滚动&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#65---%E8%87%AA%E5%8A%A8%E6%BB%9A%E5%8A%A8&quot; aria-label=&quot;65   自动滚动 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6.5 - 自动滚动&lt;/h3&gt;
&lt;p&gt;应用程序中还有个烦人的 bug，即通知和聊天列表尚未自动滚动以显示新消息。&lt;/p&gt;
&lt;p&gt;在 &lt;code class=&quot;language-text&quot;&gt;/public/page.js&lt;/code&gt; 的 &lt;code class=&quot;language-text&quot;&gt;methods&lt;/code&gt; 代码块中添加如下函数。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** 自动滚动 DOM 元素到底部 */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;autoscroll&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;element&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scrollTop &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scrollHeight &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;要自动滚动通知和消息列表，我们将在 &lt;code class=&quot;language-text&quot;&gt;add&lt;/code&gt; 方法的末尾调用 &lt;code class=&quot;language-text&quot;&gt;autoscroll&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** 将消息添加到 UI 并滚动视图以显示新消息。 */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;addMessage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;messages&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;autoscroll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$refs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatContainer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** 在 UI 中添加通知消息 */&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;addNotification&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLocaleTimeString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notifications&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timestamp &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;autoscroll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$refs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notificationContainer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;66---试试看&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#66---%E8%AF%95%E8%AF%95%E7%9C%8B&quot; aria-label=&quot;66   试试看 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6.6 - 试试看&lt;/h3&gt;
&lt;p&gt;最后一步！重启 Node 应用程序并刷新 &lt;a href=&quot;http://localhost:3000&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;localhost:3000&lt;/code&gt;&lt;/a&gt;。
现在应该可以自由地在不同聊天室间切换，从第三个浏览器标签页加入同一聊天室的任何尝试都将被拒绝。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8aabb20a82040a8b5e0a929d10f599fe/67c4b/screenshot_7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.78947368421052%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABHUlEQVR42o2SzW6DMBCE/QKxvbM2/glB4BQCSRV66rnXqmrf/3G6gHpKlPTT3LzjnV1bFWdK4LZtQwh0D4CHYajrGoDzTkgZeQ9mqJ7ti0e7EkOw1t6YcVpZCmLMOcfkQmTnWL2xnitM4zSOo6+qWzMzXy6XUsr1ei1d8d5Llq1K9c70gQ+HgwSTg/vm81nazvM8TVOKUbJsR2rHpJmMtcaYW+cWu2kaCdy1XdeVYynhbzoll4AeIeaQEpwzgBWtYbBK0TOk6LWUPsYGlAGZuAJVRF4603M3Poz50rtPo7+1/tH63ZjaWtE/OgPyDBP7gdATToQWHCTCMvMzZNvhuHcl6hp6D51gIyiAPJSsTj4NP0ZeJxIn5rwqMvtFv3dINtirv53jAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/8aabb20a82040a8b5e0a929d10f599fe/5ed05/screenshot_7.webp 190w,
/static/8aabb20a82040a8b5e0a929d10f599fe/9d76b/screenshot_7.webp 380w,
/static/8aabb20a82040a8b5e0a929d10f599fe/33466/screenshot_7.webp 760w,
/static/8aabb20a82040a8b5e0a929d10f599fe/ca244/screenshot_7.webp 1140w,
/static/8aabb20a82040a8b5e0a929d10f599fe/34e40/screenshot_7.webp 1440w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/8aabb20a82040a8b5e0a929d10f599fe/253c6/screenshot_7.png 190w,
/static/8aabb20a82040a8b5e0a929d10f599fe/810ee/screenshot_7.png 380w,
/static/8aabb20a82040a8b5e0a929d10f599fe/b4918/screenshot_7.png 760w,
/static/8aabb20a82040a8b5e0a929d10f599fe/c0204/screenshot_7.png 1140w,
/static/8aabb20a82040a8b5e0a929d10f599fe/67c4b/screenshot_7.png 1440w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/8aabb20a82040a8b5e0a929d10f599fe/b4918/screenshot_7.png&quot;
            alt=&quot;Screenshot 7&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;7---接下来&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7---%E6%8E%A5%E4%B8%8B%E6%9D%A5&quot; aria-label=&quot;7   接下来 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7 - 接下来？&lt;/h2&gt;
&lt;p&gt;恭喜！您已经构建了一个功能完整的端到端加密消息收发应用程序。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;演示 - &lt;a href=&quot;https://chat.patricktriest.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://chat.patricktriest.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Github 仓库 - &lt;a href=&quot;https://github.com/triestpa/Open-Cryptochat&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/triestpa/Open-Cryptochat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以该代码为基础，您可以在自己的服务器上部署一个私有消息收发应用程序。
为协调在哪个聊天室见面，一个巧妙的选择是使用基于时间的伪随机数生成器(如 &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&amp;#x26;hl=en&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Google Authenticator&lt;/a&gt;)，
你和对方共享一个 seed(Javascript “Google Authenticator” 教程我正在写，保持关注)。&lt;/p&gt;
&lt;h3 id=&quot;更进一步&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9B%B4%E8%BF%9B%E4%B8%80%E6%AD%A5&quot; aria-label=&quot;更进一步 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;更进一步&lt;/h3&gt;
&lt;p&gt;以此为起点可以做很多应用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;群聊，通过存储多个公钥，并分别为每个用户加密消息。&lt;/li&gt;
&lt;li&gt;多媒体消息，通过加密一个包含媒体文件的字节数组。&lt;/li&gt;
&lt;li&gt;将密钥对导入和导出为本地文件。&lt;/li&gt;
&lt;li&gt;使用发送方身份验证的私钥对消息进行签名。这是一种折衷，因为它增加了伪造消息的难度，但也破坏了 OTR消息传递标准(&lt;a href=&quot;https://en.wikipedia.org/wiki/Off-the-Record_Messaging&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OTR messaging standard&lt;/a&gt;)中概述的“可拒绝身份验证”的目标。&lt;/li&gt;
&lt;li&gt;尝试不同的加密系统，如：
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Advanced_Encryption_Standard&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;strong&gt;AES&lt;/strong&gt;&lt;/a&gt; - 对称加密，用户之间共享秘密。这是 NSA 和美国军方使用的唯一公开可用的算法。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/ElGamal_encryption&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;strong&gt;ElGamal&lt;/strong&gt;&lt;/a&gt; - 类似于 RSA，但具有更小的密码文本、更快的解密和更慢的加密。这是在PGP中使用的核心算法。&lt;/li&gt;
&lt;li&gt;实现一个  &lt;a href=&quot;https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;strong&gt;Diffie-Helman&lt;/strong&gt;&lt;/a&gt; 密钥交换。这是一种使用非对称加密(例如 ElGamal)交换共享密钥的技术。在现有项目基础上构建，并在发送每条消息之前交换新的共享密钥是提高应用程序安全性的一种好方法(请参阅 &lt;a href=&quot;https://en.wikipedia.org/wiki/Forward_secrecy&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Perfect Forward Security&lt;/a&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;建立一个应用程序，在任何情况下，中间服务器绝不应有未加密的访问，如密码管理器和 P2P(点对点) 网络。&lt;/li&gt;
&lt;li&gt;为 &lt;a href=&quot;https://facebook.github.io/react-native/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;React Native&lt;/a&gt;，&lt;a href=&quot;https://ionicframework.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ionic&lt;/a&gt;，&lt;a href=&quot;https://cordova.apache.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cordova&lt;/a&gt; 或 &lt;a href=&quot;https://electronjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Electron&lt;/a&gt; 重构应用程序，以便为移动和/或桌面环境提供安全的预构建应用程序包。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;请在下面随意评论有关本教程的问题，反馈和/或反馈。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a id=&quot;fn1&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;基于浏览器的加密安全隐患&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;请谨记。在基于浏览器的 Javascript 应用程序中使用这些协议是试验和了解它们在实际中如何工作的好方法，但此应用程序不适合替代已建立的，经过同行评审的加密协议实现，例如 &lt;a href=&quot;https://en.wikipedia.org/wiki/OpenSSL&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OpenSSL&lt;/a&gt; 和 &lt;a href=&quot;https://en.wikipedia.org/wiki/GNU_Privacy_Guard&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GnuPG&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;客户端浏览器 Javascript 加密在安全专家中是个有争议的话题，因为 Web 应用程序交付相比在浏览器外运行的预打包软件存在漏洞。
可通过使用 HTTPS 防止中间人资源注入攻击，并通过避免在浏览器中持久存储未加密的敏感数据来缓解这些问题，但务必注意 Web 平台中潜在的漏洞。&lt;a href=&quot;#fnref1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;相关参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;RSA算法原理（一） - 阮一峰&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://travistidwell.com/jsencrypt/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;JSEncrypt - OpenSSL RSA 加密的 Javascript 实现&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://prism-break.org/zh-CN/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;prism-break.org 推荐的加密通信软件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mdrights/Digital-rights/blob/master/E%E5%8A%A0%E5%AF%86%E6%8A%80%E8%A1%93/2019-09-21-%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E7%AB%AF%E5%88%B0%E7%AB%AF%E5%8A%A0%E5%AF%86%E4%B9%88.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;你真的了解端到端加密么？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://openpgpjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;OpenPGP.js - OpenPGP 的 Javascript 实现&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[临时解决 Gatsby 中文链接 activeClass 无效问题]]></title><description><![CDATA[Gatsby Link 组件在检查当前激活时，没有对当前 href 转义，导致链接中包含中文字符时判断不准确，activeClass 无效问题。]]></description><link>https://www.berlinchan.com/2019/10/fix-gatsby-link-active-class</link><guid isPermaLink="false">https://www.berlinchan.com/2019/10/fix-gatsby-link-active-class</guid><pubDate>Fri, 18 Oct 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;2020-03-10-更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2020-03-10-%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;2020 03 10 更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2020-03-10 更新：&lt;/h2&gt;
&lt;p&gt;该问题在 Gatsby v2.19.32 已修正，&lt;a href=&quot;https://github.com/gatsbyjs/gatsby/pull/21171&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;pull request #21171&lt;/a&gt; 已对 reach-router 更新，解决该问题。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Gatsby &lt;a href=&quot;https://www.gatsbyjs.org/docs/gatsby-link/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Link&lt;/code&gt;&lt;/a&gt; 组件在检查当前激活时，没有对当前 href 转义（encodeURI），导致链接中包含中文字符时判断不准确，activeClass 无效问题。其他非拉丁字符应该都有这个问题。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d086ef354137ae597e613b7f47b26cbb/36e56/featured_media.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAzElEQVR42p3KzQvBcBjA8ef/PzrShqKwKLRcftuMGIaD2fI2HIwRo7zkQHnKWj+/7aQ+h+flC+/X+29wvz7DHjdWZAZH9xJ22J73W8/doJPreHvHi8xgPd8xVrPdUJv2GpamGHrTGrQmKJwhGA+WYUbfRqPeYqTb3zUyg07d/KH4Q1c127KBOir7CgCp6AGp2q8JmpCU8xwpcBIOAi8VU0qBx4skCm0M6B4q2SatnGmU0mopXaf4K76YGHJx8iNB8pQcNbBlnEAqJv7tA8v24pw2qW8sAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/d086ef354137ae597e613b7f47b26cbb/5ed05/featured_media.webp 190w,
/static/d086ef354137ae597e613b7f47b26cbb/9d76b/featured_media.webp 380w,
/static/d086ef354137ae597e613b7f47b26cbb/33466/featured_media.webp 760w,
/static/d086ef354137ae597e613b7f47b26cbb/e8af0/featured_media.webp 800w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/d086ef354137ae597e613b7f47b26cbb/253c6/featured_media.png 190w,
/static/d086ef354137ae597e613b7f47b26cbb/810ee/featured_media.png 380w,
/static/d086ef354137ae597e613b7f47b26cbb/b4918/featured_media.png 760w,
/static/d086ef354137ae597e613b7f47b26cbb/36e56/featured_media.png 800w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/d086ef354137ae597e613b7f47b26cbb/b4918/featured_media.png&quot;
            alt=&quot;featured media&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;该问题实际是包 &lt;a href=&quot;https://github.com/reach/router&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;@reach/router&lt;/a&gt; 导致的，并已在&lt;a href=&quot;https://github.com/reach/router/commit/137a1ae931d62afe1e1bb0f6180ad9347baacb4c&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;这次 commit&lt;/a&gt; 中解决，并更新 npm 为 &lt;code class=&quot;language-text&quot;&gt;1.3.0-beta.0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;不过当前 Gatsby 依赖的 &lt;code class=&quot;language-text&quot;&gt;@reach/router&lt;/code&gt; 版本是 1.2.1，该版本&lt;strong&gt;未包含&lt;/strong&gt;此 bug 修正。&lt;/p&gt;
&lt;h2 id=&quot;临时解决&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%B8%B4%E6%97%B6%E8%A7%A3%E5%86%B3&quot; aria-label=&quot;临时解决 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;临时解决&lt;/h2&gt;
&lt;p&gt;当初我提了 &lt;a href=&quot;https://github.com/gatsbyjs/gatsby/issues/18597&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;issues&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/gatsbyjs/gatsby/pull/18600&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;PR&lt;/a&gt;，结果&lt;a href=&quot;https://github.com/gatsbyjs/gatsby/pull/18600#event-2709702550&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;找错了&lt;/a&gt;问题根本所在 😂。随后用户 &lt;code class=&quot;language-text&quot;&gt;pieh&lt;/code&gt; 给出了&lt;a href=&quot;https://github.com/gatsbyjs/gatsby/pull/18600#issuecomment-541575112&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;临时解决方案&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;使用 &lt;a href=&quot;https://yarnpkg.com/lang/en/docs/selective-version-resolutions/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Yarn resolutions&lt;/a&gt; 配置，在 &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; 中添加如下：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;resolutions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;@reach/router&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.3.0-beta.0&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;版本&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%89%88%E6%9C%AC&quot; aria-label=&quot;版本 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;版本&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;gatsby: 2.15.36&lt;/li&gt;
&lt;li&gt;gatsby-link: 2.2.20&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;等 Gatsby 的依赖更新了，要记得去掉这个临时配置。感谢热心 ❤ 的&lt;strong&gt;了不起的盖茨比&lt;/strong&gt;社区成员。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[武汉物价飞涨早点吃不起了]]></title><description><![CDATA[武汉物价飞涨，感觉早点在外面吃不起了，我这个韭菜有点被狂虐的感觉。因为猪瘟疫情，今年过年应该还有各种涨一波。]]></description><link>https://www.berlinchan.com/2019/10/food-price-increase</link><guid isPermaLink="false">https://www.berlinchan.com/2019/10/food-price-increase</guid><pubDate>Thu, 10 Oct 2019 12:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/-FEaQN0C3w0?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;p&gt;武汉物价飞涨。今早上班到金融港买一笼蒸包，8个小包子 6元，艹！十一节前 5元的，软件园附近类似的也才 4元。我这个韭菜有点被狂虐的感觉。&lt;/p&gt;
&lt;p&gt;去年过年热干面部分商家就涨价 1元，十一假期后小笼蒸包涨1元，因为猪瘟疫情，今年过年应该还有各种涨一波。&lt;/p&gt;
&lt;p&gt;才去过沈阳、嘉兴饮食消费上都没有这么低性价比的，武汉这条件太恶劣了。&lt;/p&gt;
&lt;h2 id=&quot;新闻&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%96%B0%E9%97%BB&quot; aria-label=&quot;新闻 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;新闻&lt;/h2&gt;
&lt;p&gt;下面是新闻 &lt;a href=&quot;https://cn.wsj.com/articles/%E4%B8%AD%E5%9B%BD9%E6%9C%88cpi%E5%90%8C%E6%AF%94%E4%B8%8A%E6%B6%A83%E9%AB%98%E4%BA%8E%E9%A2%84%E6%9C%9F%EF%BC%8C%E5%B9%B6%E5%88%9B%E5%87%BA%E8%BF%916%E5%B9%B4%E9%AB%98%E7%82%B9-11571105703&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;中国9月CPI同比升3%，创近六年高点&lt;/a&gt; 中的一句：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;中国国家统计局周二公布的数据显示，9月份消费者价格指数(CPI)同比上涨3.0%&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;国家统计局，也许有想象空间，来自 Wiki 的 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%B6%88%E8%B2%BB%E8%80%85%E7%89%A9%E5%83%B9%E6%8C%87%E6%95%B8&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;CPI 定义&lt;/a&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一般定义超过3％为通货膨胀，超过5％就是比较严重的通货膨胀&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也就是说现在是通货膨胀了。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[使用 Lodash 避免 TypeError Cannot read property 错误]]></title><description><![CDATA[JS 中访问某个对象的属性时，经常会碰到 Uncaught TypeError: Cannot read property 错误，短路 && 写法简单安全但不优雅，工具库 Lodash 的 get 方法是个替代方法。]]></description><link>https://www.berlinchan.com/2019/10/avoid-js-typeerror-cannot-read-property-with-lodash</link><guid isPermaLink="false">https://www.berlinchan.com/2019/10/avoid-js-typeerror-cannot-read-property-with-lodash</guid><pubDate>Tue, 08 Oct 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;问题&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E9%97%AE%E9%A2%98&quot; aria-label=&quot;问题 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;问题&lt;/h2&gt;
&lt;p&gt;在 Javascript 中访问某个对象的属性时，经常会碰到 &lt;code class=&quot;language-text&quot;&gt;Uncaught TypeError: Cannot read property &apos;bar&apos; of undefined&lt;/code&gt; 错误，例如这样：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; foo
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;该问题之常见，以至于在&lt;a href=&quot;https://juejin.im/post/5a73e3ad5188257a6d63521a&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;6个经典的JavaScript报错分析&lt;/a&gt;的统计中位列第一。&lt;/p&gt;
&lt;p&gt;我最常碰到的情况是 API 返回数据结构变化或值为 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 时，我不得不小心翼翼地处理它们。&lt;/p&gt;
&lt;h2 id=&quot;使用--短路&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BD%BF%E7%94%A8--%E7%9F%AD%E8%B7%AF&quot; aria-label=&quot;使用  短路 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;使用 &amp;#x26;&amp;#x26; 短路&lt;/h2&gt;
&lt;p&gt;在有潜在该问题的地方，一直以来我使用 &amp;#x26;&amp;#x26; 短路写法解决。例如访问如下对象：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; modal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;formData&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;foo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;4px&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;formData &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;formData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;因为 &lt;code class=&quot;language-text&quot;&gt;modal&lt;/code&gt; 我能确保它是定义过的对象，但 &lt;code class=&quot;language-text&quot;&gt;formData&lt;/code&gt; 的值可能来自于 API 的返回，所以访问它的下属属性前先判断之，若存在该对象则可访问它的 &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; 属性，否则就&lt;strong&gt;短路&lt;/strong&gt;在 &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 之前，返回 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; 而不是报错。&lt;/p&gt;
&lt;p&gt;但当对象嵌套多层后这样写就很冗长，比如访问上面对象中的 &lt;code class=&quot;language-text&quot;&gt;margin&lt;/code&gt; 属性，可能会写成这样：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;margin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;事实上我也这样写了很久，虽然不优雅，但是最安全最保险的方法。&lt;/p&gt;
&lt;h2 id=&quot;使用-lodash&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BD%BF%E7%94%A8-lodash&quot; aria-label=&quot;使用 lodash permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;使用 Lodash&lt;/h2&gt;
&lt;p&gt;对于大型项目可以使用工具库 Lodash 的 get 方法，安全访问深度嵌套的对象，&lt;a href=&quot;https://lodash.com/docs/4.17.15#get&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;官方示例&lt;/a&gt;如下：&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; object &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&apos;c&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 
_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;a[0].b.c&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// =&gt; 3&lt;/span&gt;
 
_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;0&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;c&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// =&gt; 3&lt;/span&gt;
 
_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;a.b.c&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;default&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// =&gt; &apos;default&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样就不会有短路写法一长串的 &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; 了，最近将本博客中很多短路写法都换成该方法。&lt;a href=&quot;https://ramdajs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ramda.js&lt;/a&gt; 中的 path 方法也能实现同样功能。阮一峰更推荐这个库，原因看他的文章《&lt;a href=&quot;http://www.ruanyifeng.com/blog/2017/03/ramda.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ramda 函数库参考教程&lt;/a&gt;》&lt;/p&gt;
&lt;h2 id=&quot;参考&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%8F%82%E8%80%83&quot; aria-label=&quot;参考 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://juejin.im/post/5c810170e51d450a453fb48e&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;[译] 避免那些可恶的 “cannot read property of undefined” 错误&lt;/a&gt;(&lt;a href=&quot;https://css-tricks.com/%E2%80%8B%E2%80%8Bavoiding-those-dang-cannot-read-property-of-undefined-errors/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;英文原文&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://juejin.im/post/5a73e3ad5188257a6d63521a&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;6个经典的JavaScript报错分析&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.ruanyifeng.com/blog/2017/03/ramda.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ramda 函数库参考教程&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[钢琴独奏 - Time To Say Goodbye]]></title><description><![CDATA[六年前，当我离开三峡晚报社，坐上去武汉的火车时，戴上耳机放了这首 Time To Say Goodbye，我知道我会记得这离别时刻，记得这些坚持。]]></description><link>https://www.berlinchan.com/2019/09/piano-solo-time-to-say-goodbye</link><guid isPermaLink="false">https://www.berlinchan.com/2019/09/piano-solo-time-to-say-goodbye</guid><pubDate>Mon, 30 Sep 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/q0chGWzSBrU?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://v.qq.com/x/page/l3005rsphgm.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;在腾讯视频上&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;背景&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%83%8C%E6%99%AF&quot; aria-label=&quot;背景 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;背景&lt;/h2&gt;
&lt;p&gt;六年前，当我离开三峡晚报社，坐上去武汉的火车时，新闻理想早已崩溃。&lt;/p&gt;
&lt;p&gt;虽然放弃了一些坚持，开始对一些事情变得无所谓，但是当火车缓慢启动，远处三峡物流园的字缓慢移近然后消失在身后时，想着她在我镜头记录下，从一片荒芜到车水马龙，想着宜昌的大街小巷和人情冷暖，不免有一些不舍之情。&lt;/p&gt;
&lt;p&gt;戴上耳机放了这首 Time To Say Goodbye，我知道我会记得这一刻，记得那些坚持。&lt;/p&gt;
&lt;p&gt;弹这首曲子送给自己，并&lt;strong&gt;敬有坚持的人&lt;/strong&gt;！&lt;/p&gt;
&lt;h2 id=&quot;piano-sheet&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#piano-sheet&quot; aria-label=&quot;piano sheet permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Piano Sheet&lt;/h2&gt;
&lt;p&gt;作曲：Francesco Sartori&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://musescore.com/user/125146/scores/906416&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Download&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;练习&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%BB%83%E4%B9%A0&quot; aria-label=&quot;练习 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;练习&lt;/h2&gt;
&lt;p&gt;这是去年学的一首曲子，时隔一年疏于练习反而不如以前弹得好。反复录制都无法完整无错弹奏，最后只好剪切处理。我的基本功还远不能掌握这样的曲子，还是要多练基础。&lt;/p&gt;
&lt;h2 id=&quot;指法键盘顶视图&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%8C%87%E6%B3%95%E9%94%AE%E7%9B%98%E9%A1%B6%E8%A7%86%E5%9B%BE&quot; aria-label=&quot;指法键盘顶视图 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;指法键盘顶视图&lt;/h2&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/_Fr9E7YXhTg?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://v.qq.com/x/page/f3005i51n2a.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;在腾讯视频上&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[旧站博物馆——我的个人项目回顾]]></title><description><![CDATA[2000年开始接触计算机到大学毕业，做过的网站习作、文曲星主题站、学校、公司网站。整理与回顾这些简陋青涩的页面，看到了我的成长，也记载了那个年代样貌。]]></description><link>https://www.berlinchan.com/2019/09/museum-for-archived-projects</link><guid isPermaLink="false">https://www.berlinchan.com/2019/09/museum-for-archived-projects</guid><pubDate>Sat, 21 Sep 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8529e4fe3b89e1237f734fe33d46f746/9d361/museum-tile.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMFBP/EABUBAQEAAAAAAAAAAAAAAAAAAAIB/9oADAMBAAIQAxAAAAGmT3laxop//8QAGhAAAgIDAAAAAAAAAAAAAAAAAAECERMhIv/aAAgBAQABBQKRzWQvcUikf//EABcRAAMBAAAAAAAAAAAAAAAAAAECEBH/2gAIAQMBAT8BUbP/xAAVEQEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAgEBPwGn/8QAHBAAAgEFAQAAAAAAAAAAAAAAAAESAhARMYEh/9oACAEBAAY/AtJ9MyS6eRtSaP/EABsQAQADAAMBAAAAAAAAAAAAAAEAESFRYXGR/9oACAEBAAE/IcmCuAdngl5kQSrVyM2E63yf/9oADAMBAAIAAwAAABBMD//EABcRAAMBAAAAAAAAAAAAAAAAAAABEWH/2gAIAQMBAT8QW0Lh/8QAFhEBAQEAAAAAAAAAAAAAAAAAAREQ/9oACAECAQE/EEtLn//EAB0QAQEAAgIDAQAAAAAAAAAAAAERADEhQVFhcYH/2gAIAQEAAT8QRbgoUa8894ZiRkifmriEsjdmbNRtveEUq8094FQM+M//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/8529e4fe3b89e1237f734fe33d46f746/5ed05/museum-tile.webp 190w,
/static/8529e4fe3b89e1237f734fe33d46f746/9d76b/museum-tile.webp 380w,
/static/8529e4fe3b89e1237f734fe33d46f746/33466/museum-tile.webp 760w,
/static/8529e4fe3b89e1237f734fe33d46f746/e8af0/museum-tile.webp 800w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/8529e4fe3b89e1237f734fe33d46f746/89129/museum-tile.jpg 190w,
/static/8529e4fe3b89e1237f734fe33d46f746/0036d/museum-tile.jpg 380w,
/static/8529e4fe3b89e1237f734fe33d46f746/e484a/museum-tile.jpg 760w,
/static/8529e4fe3b89e1237f734fe33d46f746/9d361/museum-tile.jpg 800w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/8529e4fe3b89e1237f734fe33d46f746/e484a/museum-tile.jpg&quot;
            alt=&quot;museum tile&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;logo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#logo&quot; aria-label=&quot;logo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LOGO&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/3100012fbe8644e3011b0cc16d1219a5/museum-logo.svg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;网址&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%BD%91%E5%9D%80&quot; aria-label=&quot;网址 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;网址&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://museum.berlinchan.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://museum.berlinchan.com/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;背景&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%83%8C%E6%99%AF&quot; aria-label=&quot;背景 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;背景&lt;/h2&gt;
&lt;p&gt;2000年开始接触计算机到大学毕业，做过的网站习作、文曲星主题站、学校、公司网站。用 Table布局、动感的 Flash、滚动的走马灯文字……如今看起来青涩的页面，是互联网发展伊始，曾经个人站长热时期的流行。虽然这些页面简陋可笑，其中还不乏错误，但却记载了我的成长，也记载了那个年代样貌。&lt;/p&gt;
&lt;p&gt;特别是文曲星主题站“星大陆”的几个版本，是我学以致用的集大成作品，包含精心收集整理PC1000a的程序与游戏，当年在星迷圈算是小受欢迎。正是因为文曲星，我爱上计算机编程，后来进入软件开发行业。近20年过去了，仍然很怀念当年那种学习、探索、成就的感觉，搜索发现网上还有不少人跟我一样在怀念，于是特意将这些沉睡已久的字节，包括文曲星相关及其他个人项目整理存档，建立这个“博物馆”项目。&lt;/p&gt;
&lt;h2 id=&quot;整理说明&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%95%B4%E7%90%86%E8%AF%B4%E6%98%8E&quot; aria-label=&quot;整理说明 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;整理说明&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;修正了站内链接，但有些外部链接，包括网站链接、js资源、图片资源的死链仍然保留&lt;/li&gt;
&lt;li&gt;有些可爱的错别字也原味保留&lt;/li&gt;
&lt;li&gt;旧站中有很多 Flash 元素，需要启用浏览器的 Flash 运行&lt;/li&gt;
&lt;li&gt;论坛、留言本动态服务器文件未上传&lt;/li&gt;
&lt;li&gt;以前网站的域名和邮箱现在都已失效&lt;/li&gt;
&lt;li&gt;删除隐私、公司信息&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;旧站回顾目录&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%97%A7%E7%AB%99%E5%9B%9E%E9%A1%BE%E7%9B%AE%E5%BD%95&quot; aria-label=&quot;旧站回顾目录 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;旧站回顾目录&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;年代&lt;/th&gt;
&lt;th&gt;回顾&lt;/th&gt;
&lt;th&gt;旧站存档&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2000-12&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2000/12/30/first-try/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;个人主页 - 习作&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/site/01-study/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站存档&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2002-05&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2002/05/12/chaplin-page/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;卓别林之页 - 习作&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/site/02-study-Chaplin/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站存档&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2003-08&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2003/08/16/star-land-v2/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;星大陆 v2.0 - 文曲星主题站&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/site/03-starland-v2/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站存档&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2003-11&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2003/11/14/offbeat/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;另类网 - 高中参赛作品&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/site/05-offbeat/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站存档&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2003-12&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2003/12/07/star-land-v3/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;星大陆 v3.0 - 文曲星主题站&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/site/04-starland-v3/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站存档&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2006-09&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2006/09/20/gallery-v1/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;相册 v1 - Javascript习作&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/site/06-gallery-v1/website/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站存档&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2007-01&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2007/01/09/wipe-library-guestbook/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;武汉体育学院藏龙岛校区图书馆留言板&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2007-03&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2007/03/15/wipe-art-portal/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;武汉体育学院艺术系门户&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/site/10-wipe-art-portal/index.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站存档&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2008-11&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2008/11/18/comeplay/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;某体育文化公司官网&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/site/09-comeplay/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站存档&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2010-03&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2010/03/29/hospital-job/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;武汉某妇科医院门户改版设计&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2010-11&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2010/11/23/gallery-v2/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;相册 v2 - Flash习作&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/site/07-gallery-v2/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站存档&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2011-06&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/2011/06/20/gallery-v3/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;相册 v3 - Flash习作&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://museum.berlinchan.com/site/08-gallery-v3/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站存档&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</content:encoded></item><item><title><![CDATA[入手《荒野大镖客 救赎 2》]]></title><description><![CDATA[在某宝上 190元入手《荒野大镖客 救赎 2》兑换码，比在 Store上6折扣买还便宜。冲着 IGN 评分 9.8 入手的。]]></description><link>https://www.berlinchan.com/2019/09/buy-red-dead-redemption-2</link><guid isPermaLink="false">https://www.berlinchan.com/2019/09/buy-red-dead-redemption-2</guid><pubDate>Tue, 17 Sep 2019 13:30:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;购买与兑换&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%B4%AD%E4%B9%B0%E4%B8%8E%E5%85%91%E6%8D%A2&quot; aria-label=&quot;购买与兑换 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;购买与兑换&lt;/h2&gt;
&lt;p&gt;在某宝上 190元入手《荒野大镖客 救赎 2》 xbox 兑换码，虽然 Store上此游戏使用金会员订阅正在 6折促销，但算下来还是某宝划算。&lt;/p&gt;
&lt;p&gt;卖家应该是买的美区的礼物兑换码，我的主机国行用 U盘方法切换港区，挂美国 VPN 轻松兑换。&lt;/p&gt;
&lt;p&gt;RockStar 的游戏都未玩过，这次冲着 &lt;a href=&quot;https://www.ign.com/games/red-dead-redemption/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;IGN 评分 9.8&lt;/a&gt;等各界好评，决定入坑。&lt;/p&gt;
&lt;h2 id=&quot;玩前功课&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%8E%A9%E5%89%8D%E5%8A%9F%E8%AF%BE&quot; aria-label=&quot;玩前功课 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;玩前功课&lt;/h2&gt;
&lt;p&gt;看就知道玩游戏的《提前遇见下世代的游戏》&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/_wCgLGDjggk?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;h2 id=&quot;更新主线通关感受&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9B%B4%E6%96%B0%E4%B8%BB%E7%BA%BF%E9%80%9A%E5%85%B3%E6%84%9F%E5%8F%97&quot; aria-label=&quot;更新主线通关感受 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;更新：主线通关感受&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/2019/11/red-dead-redemption2-our-era-has-gone&quot;&gt;荒野大镖客2——我们的时代已过去是悲凉的事实&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[游戏《古墓丽影-崛起》通关感受]]></title><description><![CDATA[有中文语音，对我了解剧情和世界观帮助很大，很喜欢游戏中动画交互的细节。体验剧情时，让我想起一句话——通往地狱的路都是善意铺就的]]></description><link>https://www.berlinchan.com/2019/09/rise-of-the-tomb-raider</link><guid isPermaLink="false">https://www.berlinchan.com/2019/09/rise-of-the-tomb-raider</guid><pubDate>Sun, 08 Sep 2019 13:04:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;通关状况&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E9%80%9A%E5%85%B3%E7%8A%B6%E5%86%B5&quot; aria-label=&quot;通关状况 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;通关状况&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;通关用时 36小时。时间应该算长的，因为途中的任务、古墓挑战、收集要素都不想错过&lt;/li&gt;
&lt;li&gt;“古墓丽影”难度通关，关闭辅助瞄准。刚开始没有辅助瞄准很不习惯，熟悉一下就好些&lt;/li&gt;
&lt;li&gt;斗士、猎人、生存者还有 4个未升级&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0f411a6535b171af260219a3ba1eab24/aeb1f/rise-of-the-tomb-raider-skill-upgrade.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAgX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAAB5ja0VOyAT//EABoQAAICAwAAAAAAAAAAAAAAAAABAhIQESL/2gAIAQEAAQUCeiFWclYkklj/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPwEn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhAAAgIDAAAAAAAAAAAAAAAAABExUQIQgf/aAAgBAQAGPwKH0dEkGSrX/8QAGxAAAwEAAwEAAAAAAAAAAAAAAAERITFBUXH/2gAIAQEAAT8hes+gkbKdq6SVl4PymQJBcH//2gAMAwEAAgADAAAAEPvv/8QAFhEBAQEAAAAAAAAAAAAAAAAAABEx/9oACAEDAQE/ENI//8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/EK1//8QAHBABAAIDAAMAAAAAAAAAAAAAAQARITFBcZGh/9oACAEBAAE/ELnGLwRqIJAIENVv7LOaWscJQLoK7BSIKgPEbTL7n//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/0f411a6535b171af260219a3ba1eab24/5ed05/rise-of-the-tomb-raider-skill-upgrade.webp 190w,
/static/0f411a6535b171af260219a3ba1eab24/9d76b/rise-of-the-tomb-raider-skill-upgrade.webp 380w,
/static/0f411a6535b171af260219a3ba1eab24/33466/rise-of-the-tomb-raider-skill-upgrade.webp 760w,
/static/0f411a6535b171af260219a3ba1eab24/ca244/rise-of-the-tomb-raider-skill-upgrade.webp 1140w,
/static/0f411a6535b171af260219a3ba1eab24/674e9/rise-of-the-tomb-raider-skill-upgrade.webp 1350w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/0f411a6535b171af260219a3ba1eab24/89129/rise-of-the-tomb-raider-skill-upgrade.jpg 190w,
/static/0f411a6535b171af260219a3ba1eab24/0036d/rise-of-the-tomb-raider-skill-upgrade.jpg 380w,
/static/0f411a6535b171af260219a3ba1eab24/e484a/rise-of-the-tomb-raider-skill-upgrade.jpg 760w,
/static/0f411a6535b171af260219a3ba1eab24/32d87/rise-of-the-tomb-raider-skill-upgrade.jpg 1140w,
/static/0f411a6535b171af260219a3ba1eab24/aeb1f/rise-of-the-tomb-raider-skill-upgrade.jpg 1350w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/0f411a6535b171af260219a3ba1eab24/e484a/rise-of-the-tomb-raider-skill-upgrade.jpg&quot;
            alt=&quot;rise-of-the-tomb-raider-skill-upgrade&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;装备制作全部完成、武器升级大半&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/64a8f9a170f06c235c06589844ec45a9/aeb1f/rise-of-the-tomb-raider-all-equipment.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIEAQP/xAAVAQEBAAAAAAAAAAAAAAAAAAAAA//aAAwDAQACEAMQAAABio5rSWDh/8QAGhAAAgMBAQAAAAAAAAAAAAAAAQIDERIiMv/aAAgBAQABBQI501KrCRjQzKeF8//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AUf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAXEAEBAQEAAAAAAAAAAAAAAAAAAREC/9oACAEBAAY/AsS8VrUR/8QAGhABAAMBAQEAAAAAAAAAAAAAAQARITFBYf/aAAgBAQABPyGhqs+uRLwX0ciMuPNmihd9jV+RPRn/2gAMAwEAAgADAAAAEH8f/8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAR/9oACAEDAQE/EMFsX//EABcRAAMBAAAAAAAAAAAAAAAAAAABESH/2gAIAQIBAT8Q1EZ//8QAGxABAQADAAMAAAAAAAAAAAAAAREAITFBYbH/2gAIAQEAAT8QcUGh7ndPcfvNUqLu0vT7iQ7dRrLLYrjF1kLoA8YaG4vc/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/64a8f9a170f06c235c06589844ec45a9/5ed05/rise-of-the-tomb-raider-all-equipment.webp 190w,
/static/64a8f9a170f06c235c06589844ec45a9/9d76b/rise-of-the-tomb-raider-all-equipment.webp 380w,
/static/64a8f9a170f06c235c06589844ec45a9/33466/rise-of-the-tomb-raider-all-equipment.webp 760w,
/static/64a8f9a170f06c235c06589844ec45a9/ca244/rise-of-the-tomb-raider-all-equipment.webp 1140w,
/static/64a8f9a170f06c235c06589844ec45a9/674e9/rise-of-the-tomb-raider-all-equipment.webp 1350w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/64a8f9a170f06c235c06589844ec45a9/89129/rise-of-the-tomb-raider-all-equipment.jpg 190w,
/static/64a8f9a170f06c235c06589844ec45a9/0036d/rise-of-the-tomb-raider-all-equipment.jpg 380w,
/static/64a8f9a170f06c235c06589844ec45a9/e484a/rise-of-the-tomb-raider-all-equipment.jpg 760w,
/static/64a8f9a170f06c235c06589844ec45a9/32d87/rise-of-the-tomb-raider-all-equipment.jpg 1140w,
/static/64a8f9a170f06c235c06589844ec45a9/aeb1f/rise-of-the-tomb-raider-all-equipment.jpg 1350w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/64a8f9a170f06c235c06589844ec45a9/e484a/rise-of-the-tomb-raider-all-equipment.jpg&quot;
            alt=&quot;rise-of-the-tomb-raider-all-equipment&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;古墓挑战 9个全部完成。&lt;/li&gt;
&lt;li&gt;我很依赖求生本能视觉，几乎在奔跑中不断用求生本能过关，不错过任何互动物品。有说法 UI提示 会损害游戏沉浸感，我的游玩习惯偏 light user，所以不介意这一点。因为中文语音的原因，相比于以前我算是深度体验剧情了，但仍没有耐心仔细地探索场景。不过对于《荒野大镖客：救赎2》，这种连快速旅行都没有的劝退特性，又好奇又期待。&lt;/li&gt;
&lt;li&gt;遇到 Bug 包括：敌人 T-pose 不动、音效无限循环、劳拉头发渲染黑块、画面卡死。可接受范围内，重启大法好，不像玩 &lt;a href=&quot;/2019/09/hellblade-senuas-sacrifice&quot;&gt;Hellblade 遇到 bug 剧情卡死&lt;/a&gt;玩不下去&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;扫除语言障碍&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%89%AB%E9%99%A4%E8%AF%AD%E8%A8%80%E9%9A%9C%E7%A2%8D&quot; aria-label=&quot;扫除语言障碍 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;扫除语言障碍&lt;/h2&gt;
&lt;p&gt;中文语音对我了解背景剧情、世界观非常有帮助，相比之前的刺客信条、地铁2033、战地系列来说，游戏代入感更加强烈。&lt;strong&gt;感叹学好英语太重要了，没文化连游戏都玩不好&lt;/strong&gt;。反过来则是我在游戏中学习了英语与不同历史文化背景的知识，这是我玩游戏的目的之一。&lt;/p&gt;
&lt;p&gt;古墓丽影系列之前只玩过 GBA平台的《古墓丽影-预言》，我记得是在模拟器上一路突突突，典型的 light user，对话剧情一概不看。&lt;/p&gt;
&lt;p&gt;这次在 Xbox 强大性能支持下，剧情以风格鲜明的场景和富含感情的角色互动来演绎，加上中文语音，我不愿错过任何一个从文献、文物中了解故事背景的机会，包括剧情中多条故事线：劳拉的回忆、圣三一、先知的追随者、蒙古人、苏联红军、雅阁与原住民、狩猎老师。&lt;/p&gt;
&lt;h2 id=&quot;游玩感受&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%B8%B8%E7%8E%A9%E6%84%9F%E5%8F%97&quot; aria-label=&quot;游玩感受 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;游玩感受&lt;/h2&gt;
&lt;p&gt;攀爬、跑动、潜水操作流畅、手感好，我基本是一路快跑、快速游泳、攀爬时跳跃来加速的，加快游戏流程。&lt;/p&gt;
&lt;p&gt;在与人类敌人战斗中很有紧逼压迫感，敌人会主动推进或冲过来肉搏，会丢手榴弹将人从掩体中赶出来。远程攻击时我喜欢用手枪瞄准爆头，近身群攻霰弹枪扫一片，要是扛得住一个个肉搏再终结技，省弹药也蛮好，不过游戏期间很少有缺少弹药的情况。&lt;/p&gt;
&lt;p&gt;很喜欢游戏中的动画、交互细节，这些不起眼之处也是用心之处。例如：&lt;/p&gt;
&lt;ul&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;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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但从悬崖高处掉落摔死的动画做的很僵硬，摔落后直接就像一块板往下落。&lt;/p&gt;
&lt;p&gt;过场动画与游戏之间过度自然，必要时利用预渲染的动画衔接游戏与过场，避免加载中断。剧情尾声时，甚至有两次过场动画中，我都不清楚是否开始游戏，这样又太过了吧。&lt;/p&gt;
&lt;p&gt;用钱币秘宝在补给小屋购买道具，觉得最有用的就是索道攀爬辅助，绳索攀爬时按 X 可快速向上爬升，要比自己爬快多了。后来又买了手枪消声器、榴弹发射器和武器升级工具。&lt;/p&gt;
&lt;h2 id=&quot;剧情感悟&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%89%A7%E6%83%85%E6%84%9F%E6%82%9F&quot; aria-label=&quot;剧情感悟 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;剧情感悟&lt;/h2&gt;
&lt;p&gt;在对神圣之源的抢夺中角色各有动机。安娜重病渴望被治愈，康斯坦丁表面是为了妹妹安娜，深处则是为满足对力量、权力的追求，安娜与康斯坦丁的分裂从找到的文献自白录音中听得出。劳拉的动机是为圆满父亲的遗志，还父亲一个清白，并将先知的失落文明与力量公诸于世、造福大众。&lt;/p&gt;
&lt;p&gt;这些角色动机要么符合人性欲望，要么正义。虽然有牵强之处，例如圣三一总是能突然空降夺取成果，但对于游戏剧情，我觉得可以接受。讲故事讲到模糊了绝对的正与邪、超越善与恶的对立的地步，我觉得就比较成功了。每个角色的行为都有足够合理的动机支撑，才能从角色自身的矛盾、角色与角色之间的矛盾中，感受到故事戏剧化的冲突，进而推广发散地感受到人生矛盾、混乱的本质。&lt;/p&gt;
&lt;p&gt;先知的化身雅各为保护神圣之源的秘密，创造不死军团，却导致了无法控制的杀戮。劳拉为洗清父亲的污名和救赎自己的悔恨想将神圣之源公诸于众，却总是帮助圣三一离完成邪恶目的更进一步。又让我想起这句&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;通往地狱的路都是善意铺就的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在重伤的雅各说出“也许神圣之源一开始就不应该存在”，然后劳拉毅然决然砸毁神圣之源时，终于结束了这一拥有强大力量却总是带来悲剧的神物，终结了一条“地狱之路”。&lt;/p&gt;
&lt;p&gt;片尾剧情交代，病重的安娜命丧圣三一的子弹而非疾病，真是人祸可怕于天灾。劳拉在基特城阻止安娜带走神圣之源时说“死亡是生命的一部分”，借劳拉之口道出了生死问题的真理。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b999a8fe2a35eb7ab333dd854302e5a3/aeb1f/rise-of-the-tomb-raider-constantine.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEAv/EABUBAQEAAAAAAAAAAAAAAAAAAAIE/9oADAMBAAIQAxAAAAHLZWS0UCQL/8QAGhABAAIDAQAAAAAAAAAAAAAAAQIDABEhQf/aAAgBAQABBQJ1CviImXgHsuP/xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQMBAT8BjX//xAAWEQADAAAAAAAAAAAAAAAAAAABAhD/2gAIAQIBAT8BLT//xAAZEAEAAgMAAAAAAAAAAAAAAAARABABEiH/2gAIAQEABj8CNusErF//xAAaEAADAQADAAAAAAAAAAAAAAAAAREhMUFR/9oACAEBAAE/IdMnkZY64IaREF2LJSUGcU4j/9oADAMBAAIAAwAAABBbH//EABcRAAMBAAAAAAAAAAAAAAAAAAABESH/2gAIAQMBAT8QSOGD/8QAFhEBAQEAAAAAAAAAAAAAAAAAAREA/9oACAECAQE/EEl1Xf/EABkQAQEBAAMAAAAAAAAAAAAAAAERACExYf/aAAgBAQABPxCugBWVfcBhfJZLqQXi0LdGMsPukiICQz5wdF63/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/b999a8fe2a35eb7ab333dd854302e5a3/5ed05/rise-of-the-tomb-raider-constantine.webp 190w,
/static/b999a8fe2a35eb7ab333dd854302e5a3/9d76b/rise-of-the-tomb-raider-constantine.webp 380w,
/static/b999a8fe2a35eb7ab333dd854302e5a3/33466/rise-of-the-tomb-raider-constantine.webp 760w,
/static/b999a8fe2a35eb7ab333dd854302e5a3/ca244/rise-of-the-tomb-raider-constantine.webp 1140w,
/static/b999a8fe2a35eb7ab333dd854302e5a3/674e9/rise-of-the-tomb-raider-constantine.webp 1350w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/b999a8fe2a35eb7ab333dd854302e5a3/89129/rise-of-the-tomb-raider-constantine.jpg 190w,
/static/b999a8fe2a35eb7ab333dd854302e5a3/0036d/rise-of-the-tomb-raider-constantine.jpg 380w,
/static/b999a8fe2a35eb7ab333dd854302e5a3/e484a/rise-of-the-tomb-raider-constantine.jpg 760w,
/static/b999a8fe2a35eb7ab333dd854302e5a3/32d87/rise-of-the-tomb-raider-constantine.jpg 1140w,
/static/b999a8fe2a35eb7ab333dd854302e5a3/aeb1f/rise-of-the-tomb-raider-constantine.jpg 1350w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/b999a8fe2a35eb7ab333dd854302e5a3/e484a/rise-of-the-tomb-raider-constantine.jpg&quot;
            alt=&quot;rise-of-the-tomb-raider-constantine&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;最后，与康斯坦丁的一战中我没有亲手了结他，他被仇恨与野心吞噬，扭曲了思想走入歧途，这样的人会自取灭亡。&lt;/p&gt;
&lt;h2 id=&quot;血脉相连&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%A1%80%E8%84%89%E7%9B%B8%E8%BF%9E&quot; aria-label=&quot;血脉相连 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&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;/p&gt;</content:encoded></item><item><title><![CDATA[游玩《地狱之刃：塞纳的献祭》感受]]></title><description><![CDATA[在 Unreal Engine 的运动捕捉实时渲染演示上首次见过，留下很深印象。体验该游戏中了解凯尔特、北欧历史神话背景觉得很有意思，但被一个罕见bug卡住剧情无法继续]]></description><link>https://www.berlinchan.com/2019/09/hellblade-senuas-sacrifice</link><guid isPermaLink="false">https://www.berlinchan.com/2019/09/hellblade-senuas-sacrifice</guid><pubDate>Sat, 07 Sep 2019 10:54:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;初次印象&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%88%9D%E6%AC%A1%E5%8D%B0%E8%B1%A1&quot; aria-label=&quot;初次印象 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;初次印象&lt;/h2&gt;
&lt;p&gt;初次在 &lt;a href=&quot;https://youtu.be/JbQSpfWUs4I?t=273&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Unreal Engine 的运动捕捉实时渲染演示&lt;/a&gt;上见过，但没注意到是这个游戏。
因为之前对&lt;a href=&quot;/2015/04/memo-of-qq-april-fool-fun/&quot;&gt;渲染和运动捕捉研究&lt;/a&gt;过一点，所以对整个系统的实时性和画面效果留下很深印象。&lt;/p&gt;
&lt;p&gt;后来&lt;a href=&quot;/2019/08/talk-about-xbox-1x-after-1-year&quot;&gt;买 Xbox&lt;/a&gt; 了试用 Game Pass 时候又看到，于是开始深入体验这款游戏。
游戏环境氛围音效动画非常棒，带耳机听&lt;a href=&quot;https://www.hellblade.com/development-diary-24-hearing-voices/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;萦绕在主角脑海的耳语&lt;/a&gt;都会有高潮。
喜欢凯尔特、北欧神话背景世界观的独特风格。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/054055ac3c1aa76b6eaca0941589ea0b/aeb1f/hellblade-astonishing-scene.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMFBP/EABYBAQEBAAAAAAAAAAAAAAAAAAQAAv/aAAwDAQACEAMQAAABw1JbCrWLN3//xAAZEAEBAQADAAAAAAAAAAAAAAACAREQIjL/2gAIAQEAAQUCFw16Xex8W8f/xAAVEQEBAAAAAAAAAAAAAAAAAAAQMf/aAAgBAwEBPwGn/8QAFREBAQAAAAAAAAAAAAAAAAAAAhD/2gAIAQIBAT8BU//EABgQAAIDAAAAAAAAAAAAAAAAAAAQAREh/9oACAEBAAY/ArWkP//EABsQAAMAAgMAAAAAAAAAAAAAAAABESExUXGh/9oACAEBAAE/IVR3g7RGVszwUdFkbdZ//9oADAMBAAIAAwAAABCAz//EABYRAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAwEBPxBDC2F//8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAh/9oACAECAQE/EArkGX//xAAdEAEBAAIBBQAAAAAAAAAAAAABEQAxIUFxgdHw/9oACAEBAAE/ELMEYA5+3gmoaW7O2IoFOttzgJqPKvOR+j6xVnTDP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/054055ac3c1aa76b6eaca0941589ea0b/5ed05/hellblade-astonishing-scene.webp 190w,
/static/054055ac3c1aa76b6eaca0941589ea0b/9d76b/hellblade-astonishing-scene.webp 380w,
/static/054055ac3c1aa76b6eaca0941589ea0b/33466/hellblade-astonishing-scene.webp 760w,
/static/054055ac3c1aa76b6eaca0941589ea0b/ca244/hellblade-astonishing-scene.webp 1140w,
/static/054055ac3c1aa76b6eaca0941589ea0b/674e9/hellblade-astonishing-scene.webp 1350w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/054055ac3c1aa76b6eaca0941589ea0b/89129/hellblade-astonishing-scene.jpg 190w,
/static/054055ac3c1aa76b6eaca0941589ea0b/0036d/hellblade-astonishing-scene.jpg 380w,
/static/054055ac3c1aa76b6eaca0941589ea0b/e484a/hellblade-astonishing-scene.jpg 760w,
/static/054055ac3c1aa76b6eaca0941589ea0b/32d87/hellblade-astonishing-scene.jpg 1140w,
/static/054055ac3c1aa76b6eaca0941589ea0b/aeb1f/hellblade-astonishing-scene.jpg 1350w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/054055ac3c1aa76b6eaca0941589ea0b/e484a/hellblade-astonishing-scene.jpg&quot;
            alt=&quot;hellblade-astonishing-scene&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;画面氛围与主角 Senua 分裂人格而萦绕的多个耳语声音，让我深深体会到她的迷惑、绝望和痛苦，而跟随剧情的推进却又无不透露出她的坚强、执着和勇敢。
进入上图那个场景时被震撼到了。&lt;/p&gt;
&lt;h2 id=&quot;背景历史与文化&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%83%8C%E6%99%AF%E5%8E%86%E5%8F%B2%E4%B8%8E%E6%96%87%E5%8C%96&quot; aria-label=&quot;背景历史与文化 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;背景历史与文化&lt;/h2&gt;
&lt;p&gt;游戏故事背景来自于凯尔特、北欧历史与神话，这个看&lt;a href=&quot;https://www.youtube.com/watch?v=KQUwYYS4QyE&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;“就知道玩游戏”的解读视频&lt;/a&gt;说的很好，
特别是对游戏结局 Senua 与 海拉，到底是谁杀死谁的解读独到且深刻，我很赞同这个解读。对剧情的截然不同解读，和游戏标题 Hellblade 细想耐人寻味。&lt;/p&gt;
&lt;p&gt;像我这种没法通关的人，只好看这个解读来“云通关”了。&lt;/p&gt;
&lt;h2 id=&quot;bug-卡住剧情&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bug-%E5%8D%A1%E4%BD%8F%E5%89%A7%E6%83%85&quot; aria-label=&quot;bug 卡住剧情 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bug 卡住剧情&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a9fae32e57f2434f0f670ee303eaf858/aeb1f/hellblade-torch-bug.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.315789473684205%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBP/EABYBAQEBAAAAAAAAAAAAAAAAAAECA//aAAwDAQACEAMQAAABTDfIMxCs/wD/xAAZEAACAwEAAAAAAAAAAAAAAAABAgMQEjL/2gAIAQEAAQUCyrFgBUPMlf/EABcRAAMBAAAAAAAAAAAAAAAAAAECECH/2gAIAQMBAT8BYZP/xAAXEQEAAwAAAAAAAAAAAAAAAAAAAQIR/9oACAECAQE/Aay1/8QAFxAAAwEAAAAAAAAAAAAAAAAAAhARMf/aAAgBAQAGPwIsjJ//xAAaEAACAwEBAAAAAAAAAAAAAAAAARFBgSEQ/9oACAEBAAE/IeegiWFRUGobQdy+35//2gAMAwEAAgADAAAAEEwv/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAERIUH/2gAIAQMBAT8QRZaQf//EABYRAQEBAAAAAAAAAAAAAAAAAAEAcf/aAAgBAgEBPxBhRs3/xAAcEAACAgIDAAAAAAAAAAAAAAABEQAhEDFhcZH/2gAIAQEAAT8QJAOylbhQRRhMcROkdwyHRAHybWw//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/a9fae32e57f2434f0f670ee303eaf858/5ed05/hellblade-torch-bug.webp 190w,
/static/a9fae32e57f2434f0f670ee303eaf858/9d76b/hellblade-torch-bug.webp 380w,
/static/a9fae32e57f2434f0f670ee303eaf858/33466/hellblade-torch-bug.webp 760w,
/static/a9fae32e57f2434f0f670ee303eaf858/ca244/hellblade-torch-bug.webp 1140w,
/static/a9fae32e57f2434f0f670ee303eaf858/674e9/hellblade-torch-bug.webp 1350w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/a9fae32e57f2434f0f670ee303eaf858/89129/hellblade-torch-bug.jpg 190w,
/static/a9fae32e57f2434f0f670ee303eaf858/0036d/hellblade-torch-bug.jpg 380w,
/static/a9fae32e57f2434f0f670ee303eaf858/e484a/hellblade-torch-bug.jpg 760w,
/static/a9fae32e57f2434f0f670ee303eaf858/32d87/hellblade-torch-bug.jpg 1140w,
/static/a9fae32e57f2434f0f670ee303eaf858/aeb1f/hellblade-torch-bug.jpg 1350w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/a9fae32e57f2434f0f670ee303eaf858/e484a/hellblade-torch-bug.jpg&quot;
            alt=&quot;hellblade-torch-bug&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;在黑暗恶魔的侵袭下，我一路乱冲，从一处木板桥上掉下去，掉落的过程中（我猜掉落有2秒钟）我想估计是死了，结果掉到这里。
没有火把，无法解开这个门的谜题，无限循环卡死在这里了。&lt;/p&gt;
&lt;p&gt;看别人视频，这里应该是在洞口点燃火把，然后靠火光的庇护再往前的。我是被黑暗恶魔侵袭下，只想着快点冲没注意到这个点火处，结果慌乱之中就冲掉下去了。&lt;/p&gt;
&lt;p&gt;这有一段 &lt;a href=&quot;https://www.youtube.com/watch?v=PZsMbxvDjLQ&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;torch bug 卡住剧情&lt;/a&gt;的演示视频。&lt;/p&gt;
&lt;p&gt;忍者理论不应该遗留这种问题啊，倒霉还让我给碰上了!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Pixel 升级 Android 10 并破解电信 4G]]></title><description><![CDATA[Google 今天开始正式向自己的 Pixel 手机推送 Android 10 更新，我第一时间更新并按网友老办法破解电信4G]]></description><link>https://www.berlinchan.com/2019/09/update-android-10-and-flash-modem-for-china-telecom-4g</link><guid isPermaLink="false">https://www.berlinchan.com/2019/09/update-android-10-and-flash-modem-for-china-telecom-4g</guid><pubDate>Wed, 04 Sep 2019 15:09:00 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;不以甜点命名的版本&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%B8%8D%E4%BB%A5%E7%94%9C%E7%82%B9%E5%91%BD%E5%90%8D%E7%9A%84%E7%89%88%E6%9C%AC&quot; aria-label=&quot;不以甜点命名的版本 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;不以甜点命名的版本&lt;/h2&gt;
&lt;p&gt;我的 Pixel 欧版今天中午收到了 &lt;a href=&quot;https://cn.engadget.com/2019/09/03/android-10-official-release-google-pixels/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Android 10 的 OTA 推送&lt;/a&gt;，这是首个不以甜点命名的安卓版本，以免”棉花糖”、“棒棒糖”之类的命名方式让人无法区分版本先后，&lt;a href=&quot;https://zhuanlan.zhihu.com/p/79546951&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Android 10结束了Google甜点命名的传统&lt;/a&gt;。今年也是 Android 10周年哦。&lt;/p&gt;
&lt;h2 id=&quot;更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;更新&lt;/h2&gt;
&lt;p&gt;晚上回家后直接点开始更新，下载安装一切顺利。由于这是个完整的系统更新，会将我之前刷的 TWRP、modem 覆盖，为安全起见我先用 TWRP 备份数据。备份虽然后面没用到，但也很必要。&lt;/p&gt;
&lt;p&gt;更新完后果然 电信4G 无连接了，但是打电话可以。于是我尝试按照之前网友分享的 Android 9 破解方法破解之。
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 563px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4c815b0b866f92d5966efdb1af7bf8ef/449f5/android-10-version-info.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 177.36842105263156%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAjABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe7ZQ0MaCgkCg//EABgQAQADAQAAAAAAAAAAAAAAAAEAEBEw/9oACAEBAAEFAmsjCnj/AP/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BX//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BX//EABQQAQAAAAAAAAAAAAAAAAAAADD/2gAIAQEABj8Cf//EAB4QAAIBAwUAAAAAAAAAAAAAAAABERAhgSAxYZHw/9oACAEBAAE/IU9ImtkzIhKVujCjXVxTxqVP/9oADAMBAAIAAwAAABBTCwBwD//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8QX//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8QX//EACAQAAICAQMFAAAAAAAAAAAAAAERACFREEGBIDFhcZH/2gAIAQEAAT8QsFrmJVBPLMTP1DaWOWgLuSOQFogVXsqMyU8dW73p/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/4c815b0b866f92d5966efdb1af7bf8ef/5ed05/android-10-version-info.webp 190w,
/static/4c815b0b866f92d5966efdb1af7bf8ef/9d76b/android-10-version-info.webp 380w,
/static/4c815b0b866f92d5966efdb1af7bf8ef/8779c/android-10-version-info.webp 563w&quot;
              sizes=&quot;(max-width: 563px) 100vw, 563px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/4c815b0b866f92d5966efdb1af7bf8ef/89129/android-10-version-info.jpg 190w,
/static/4c815b0b866f92d5966efdb1af7bf8ef/0036d/android-10-version-info.jpg 380w,
/static/4c815b0b866f92d5966efdb1af7bf8ef/449f5/android-10-version-info.jpg 563w&quot;
            sizes=&quot;(max-width: 563px) 100vw, 563px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/4c815b0b866f92d5966efdb1af7bf8ef/449f5/android-10-version-info.jpg&quot;
            alt=&quot;android-10-version-info&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;破解电信4g&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%A0%B4%E8%A7%A3%E7%94%B5%E4%BF%A14g&quot; aria-label=&quot;破解电信4g permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;破解电信4G&lt;/h2&gt;
&lt;p&gt;破解的方法见 Gfan 帖子：&lt;a href=&quot;http://bbs.gfan.com/android-9531535-1-1.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;傻瓜都会超简单的破解方法&lt;/a&gt;，屡试不爽直接成功了，正常打电话与 4G LTE 上网。&lt;/p&gt;
&lt;p&gt;以前的破解还参考过这篇：&lt;a href=&quot;https://tuzhao.org/article/49&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Google Pixel android 9 破解电信4G&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;体验&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BD%93%E9%AA%8C&quot; aria-label=&quot;体验 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;体验&lt;/h2&gt;
&lt;p&gt;新版加入的黑暗主题
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 563px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/43fa856ceb24b9d1a7266151fa3fb5d0/449f5/android-10-dark-theme.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 177.36842105263156%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAjABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAQACBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHhCpmZWRCY00FR/8QAHRAAAgEEAwAAAAAAAAAAAAAAAREAAhASISAiQf/aAAgBAQABBQLxWZxJMLa6Ebq1UpjFw//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BX//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BX//EABwQAAICAgMAAAAAAAAAAAAAAAABETEQISBBYf/aAAgBAQAGPwLFHghzZM90LY9zz//EABwQAAIDAAMBAAAAAAAAAAAAAAERACExEFFhcf/aAAgBAQABPyEjRFY5XXeVsIRRQiFEPWOGlzYAVwyMTazCCMfRBaywHWQAAEPe4/kfyP5wTnzj/9oADAMBAAIAAwAAABBvBP8ADD//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EF//xAAVEQEBAAAAAAAAAAAAAAAAAAAgIf/aAAgBAgEBPxCL/8QAHhABAAICAgMBAAAAAAAAAAAAAQARMUEhcVFhkeH/2gAIAQEAAT8QRDC2BTjVxSChFhXSozYIRsbjU8LTQ7aineM+dGupezE2Xb3KkZocAeblFCoTl2e6jmgVMrtUdYz3Ff1KgE+5bG8DhgqWz//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/43fa856ceb24b9d1a7266151fa3fb5d0/5ed05/android-10-dark-theme.webp 190w,
/static/43fa856ceb24b9d1a7266151fa3fb5d0/9d76b/android-10-dark-theme.webp 380w,
/static/43fa856ceb24b9d1a7266151fa3fb5d0/8779c/android-10-dark-theme.webp 563w&quot;
              sizes=&quot;(max-width: 563px) 100vw, 563px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/43fa856ceb24b9d1a7266151fa3fb5d0/89129/android-10-dark-theme.jpg 190w,
/static/43fa856ceb24b9d1a7266151fa3fb5d0/0036d/android-10-dark-theme.jpg 380w,
/static/43fa856ceb24b9d1a7266151fa3fb5d0/449f5/android-10-dark-theme.jpg 563w&quot;
            sizes=&quot;(max-width: 563px) 100vw, 563px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/43fa856ceb24b9d1a7266151fa3fb5d0/449f5/android-10-dark-theme.jpg&quot;
            alt=&quot;android-10-dark-theme&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Google Photos APP 中已经自动适配的黑色主题，但是 Gmail 却还没有，微信同样也没有，各厂商应该会逐渐跟进软件的更新&lt;/p&gt;
&lt;p&gt;其他改变包括更细致的权限设置、手势导航等暂时未体验到。还有低功耗蓝牙传输等估计也不会用上了，毕竟 Pixel 老矣，省电流畅才是我看中的。&lt;/p&gt;
&lt;h2 id=&quot;问题&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E9%97%AE%E9%A2%98&quot; aria-label=&quot;问题 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;问题&lt;/h2&gt;
&lt;p&gt;之后按照 &lt;a href=&quot;https://twrp.me/google/googlepixel.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TWRP官网&lt;/a&gt;安装了 3.3.0-0，
刷完后进入执行备份的时候会报错 &lt;code class=&quot;language-text&quot;&gt;Failed to mount /system&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;Failed to mount /vender&lt;/code&gt;，无法备份这两个目录，也暂时没有研究到底怎么回事。&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 578px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/78bcfb2a6fe3189e13d92730e7ca397b/03b3e/android-10-twrp-failed-mount-system-vendor.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 173.15789473684208%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAjABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAIDBAEF/8QAFwEBAQEBAAAAAAAAAAAAAAAAAQADBP/aAAwDAQACEAMQAAABQy1PPqeaTdRJnvmdLV0WAr//xAAdEAACAgIDAQAAAAAAAAAAAAABAgADBBASE0Ex/9oACAEBAAEFAu9YclIchJ9DJrmOJOlx7GUY9jSxTW3ns//EABgRAAIDAAAAAAAAAAAAAAAAAAACExQg/9oACAEDAQE/AaykC5//xAAXEQADAQAAAAAAAAAAAAAAAAAAARMg/9oACAECAQE/Aaso8//EAB8QAAEDAwUAAAAAAAAAAAAAAAEAEBEhMTICEkJhkf/aAAgBAQAGPwLArEqxQoqtC4+MCIg9oxFDF1t1XYN//8QAHhAAAgEEAwEAAAAAAAAAAAAAAAERECFBUTFhccH/2gAIAQEAAT8hcK0bQi7oHH8kOYTcYZI5kvaW8sHYDE5QJQ7wg2YeHJkLi3T/2gAMAwEAAgADAAAAEL8ywHPP/8QAHBEAAgAHAAAAAAAAAAAAAAAAAAEQETFBYZHx/9oACAEDAQE/EG3osuytiUf/xAAWEQEBAQAAAAAAAAAAAAAAAAAREAD/2gAIAQIBAT8QCZi//8QAHxABAQACAQQDAAAAAAAAAAAAAREAITFBYXHBUZGh/9oACAEBAAE/EKhkhKnvJFA6QfeH22sE3JG8Uh8+0V/d4m8K0DJwy46VZ7NX7MTdmQDRQjPGFKwlAbOcQ6AF1N98FtWiHPnB2UUvOV+c/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/78bcfb2a6fe3189e13d92730e7ca397b/5ed05/android-10-twrp-failed-mount-system-vendor.webp 190w,
/static/78bcfb2a6fe3189e13d92730e7ca397b/9d76b/android-10-twrp-failed-mount-system-vendor.webp 380w,
/static/78bcfb2a6fe3189e13d92730e7ca397b/681b4/android-10-twrp-failed-mount-system-vendor.webp 578w&quot;
              sizes=&quot;(max-width: 578px) 100vw, 578px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/78bcfb2a6fe3189e13d92730e7ca397b/89129/android-10-twrp-failed-mount-system-vendor.jpg 190w,
/static/78bcfb2a6fe3189e13d92730e7ca397b/0036d/android-10-twrp-failed-mount-system-vendor.jpg 380w,
/static/78bcfb2a6fe3189e13d92730e7ca397b/03b3e/android-10-twrp-failed-mount-system-vendor.jpg 578w&quot;
            sizes=&quot;(max-width: 578px) 100vw, 578px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/78bcfb2a6fe3189e13d92730e7ca397b/03b3e/android-10-twrp-failed-mount-system-vendor.jpg&quot;
            alt=&quot;android-10-twrp-failed-mount-system-vendor&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;更新：后来从&lt;a href=&quot;https://developers.google.com/android/images&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;官网下载 ROM&lt;/a&gt; 后线刷重置再刷入 TWRP 也会报这两个错，推测是 TWRP 的兼容性问题。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;更新使用一天感受&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9B%B4%E6%96%B0%E4%BD%BF%E7%94%A8%E4%B8%80%E5%A4%A9%E6%84%9F%E5%8F%97&quot; aria-label=&quot;更新使用一天感受 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;更新：使用一天感受&lt;/h2&gt;
&lt;p&gt;流畅度比之前的9版好了不是一点点！体现在底部左右滑动切换不同 APP 上尤为明显，在五六个 APP 中切换几乎毫无卡顿，
程序流畅度上来，网速似乎也有提升。感觉 Pixel 重获新生了，希望能一直保持。&lt;/p&gt;
&lt;p&gt;底部左右滑动手势导航，交互跟之前有点差别，关键是流畅度上来了，操作体验非常好。
之前的总是滑超前一个，要么卡顿突然到后一个，中间要切换的经常三四次切换不到，最后直接向上滑打开所有最近应用然后点击选择。&lt;/p&gt;
&lt;h2 id=&quot;更新app后台存取位置提示&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9B%B4%E6%96%B0app%E5%90%8E%E5%8F%B0%E5%AD%98%E5%8F%96%E4%BD%8D%E7%BD%AE%E6%8F%90%E7%A4%BA&quot; aria-label=&quot;更新app后台存取位置提示 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;更新：APP后台存取位置提示&lt;/h2&gt;
&lt;p&gt;会提示哪些 APP 在后台访问定位，如下图：
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1a80da49d626123506fc8c13891b7cb7/94a4d/android-10-access-location-backstage-tips.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.89473684210526%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAASABQDASIAAhEBAxEB/8QAGQABAAIDAAAAAAAAAAAAAAAAAAIEAwUG/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAc87Uk1iwXlgAf/EABwQAAICAgMAAAAAAAAAAAAAAAADAQITFAQQEv/aAAgBAQABBQLUSaSS/DVE5KGRY2y/Xf8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwEf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAHBAAAgICAwAAAAAAAAAAAAAAAAIykQGSICEz/9oACAEBAAY/AvNyLWdYzZJNiSbElvh//8QAGxAAAgIDAQAAAAAAAAAAAAAAAAEhQRARMeH/2gAIAQEAAT8hvob6J62TV0YHrIKwzUudZOen/9oADAMBAAIAAwAAABAswDz/xAAVEQEBAAAAAAAAAAAAAAAAAAABIP/aAAgBAwEBPxAGP//EABYRAQEBAAAAAAAAAAAAAAAAAAEgQf/aAAgBAgEBPxBTCP/EAB4QAQADAAIDAQEAAAAAAAAAAAEAESExYUFRcYGR/9oACAEBAAE/EFC17zPh4+sGULBpVl/viFQnst12DLjvwf2WHH0IPU8Cly3uO87WSj1Eq1b3P//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/1a80da49d626123506fc8c13891b7cb7/5ed05/android-10-access-location-backstage-tips.webp 190w,
/static/1a80da49d626123506fc8c13891b7cb7/9d76b/android-10-access-location-backstage-tips.webp 380w,
/static/1a80da49d626123506fc8c13891b7cb7/1546b/android-10-access-location-backstage-tips.webp 720w&quot;
              sizes=&quot;(max-width: 720px) 100vw, 720px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/1a80da49d626123506fc8c13891b7cb7/89129/android-10-access-location-backstage-tips.jpg 190w,
/static/1a80da49d626123506fc8c13891b7cb7/0036d/android-10-access-location-backstage-tips.jpg 380w,
/static/1a80da49d626123506fc8c13891b7cb7/94a4d/android-10-access-location-backstage-tips.jpg 720w&quot;
            sizes=&quot;(max-width: 720px) 100vw, 720px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/1a80da49d626123506fc8c13891b7cb7/94a4d/android-10-access-location-backstage-tips.jpg&quot;
            alt=&quot;android-10-access-location-backstage-tips&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;用了这4天，提示的有DiDi、微信、GoPro、豌豆荚。针对后台访问定位操作可选允许、仅在运行时允许和禁用。&lt;/p&gt;
&lt;h2 id=&quot;2019-12-12-更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2019-12-12-%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;2019 12 12 更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2019-12-12 更新&lt;/h2&gt;
&lt;p&gt;应该是 Pixel 的最后一个 OTA —— &lt;a href=&quot;https://developers.google.com/android/ota#sailfish&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Android 10.0.0 (QP1A.191005.007.A3, Dec 2019)&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;2019-12-18-更新&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2019-12-18-%E6%9B%B4%E6%96%B0&quot; aria-label=&quot;2019 12 18 更新 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2019-12-18 更新&lt;/h2&gt;
&lt;p&gt;微信 7.0.10 开始支持 Android 10 黑暗主题&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Xbox One X 开箱及一年后感受]]></title><description><![CDATA[主机新用户开箱Xbox One X，谈为什么选它，和使用一年后的感受]]></description><link>https://www.berlinchan.com/2019/08/talk-about-xbox-1x-after-1-year</link><guid isPermaLink="false">https://www.berlinchan.com/2019/08/talk-about-xbox-1x-after-1-year</guid><pubDate>Tue, 27 Aug 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;h2 id=&quot;xbox-one-x-开箱&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#xbox-one-x-%E5%BC%80%E7%AE%B1&quot; aria-label=&quot;xbox one x 开箱 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Xbox One X 开箱&lt;/h2&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/MUpySG1rE8g?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;h2 id=&quot;一年使用情况&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%B8%80%E5%B9%B4%E4%BD%BF%E7%94%A8%E6%83%85%E5%86%B5&quot; aria-label=&quot;一年使用情况 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;一年使用情况&lt;/h2&gt;
&lt;p&gt;其实我也不完全算主机新手，之前有台 Nintendo Wii，现在偶尔朋友们来还会拿出来玩马里奥赛车，而掌机则是陪伴我整个学生时代，
不过那都是以前了，所以”新手”针对的是”次世代主机”。&lt;/p&gt;
&lt;p&gt;购买后一年时间里，平均每周约玩3小时，算比较少的。有时候很久不玩，但抓住机会了，哼哼，可一日茶饭不思。&lt;/p&gt;
&lt;p&gt;我对于游戏的视听效果要求较高，所以目前最强性能的 Xbox One X 是我的首选，而下一代 Scarlett 预计仍将领跑主机性能。&lt;/p&gt;
&lt;p&gt;我购买的游戏都是数字版，比光盘便宜容易购买，购买的游戏从 Store 安装也很容易，用支付宝在 Store 购买游戏很方便。&lt;/p&gt;
&lt;h2 id=&quot;满意的方面&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%BB%A1%E6%84%8F%E7%9A%84%E6%96%B9%E9%9D%A2&quot; aria-label=&quot;满意的方面 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;满意的方面&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;手柄按键、握感非常好，再拿 PS4 手柄感觉像塑料玩具&lt;/li&gt;
&lt;li&gt;大量 4K 游戏支持，不断更新的重制游戏，次世代画质。像刺客信条、古墓丽影、战地这类游戏，看画面都是享受&lt;/li&gt;
&lt;li&gt;国行转区为港版，通玩各种游戏&lt;/li&gt;
&lt;li&gt;Store 中可用支付宝购买，且常有游戏大作折扣，我的游戏都是折扣时候买的&lt;/li&gt;
&lt;li&gt;向下兼容 Xbox 360，怀念老游戏的有福了，不过我没用到过&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;目前为止购买的游戏&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E7%9B%AE%E5%89%8D%E4%B8%BA%E6%AD%A2%E8%B4%AD%E4%B9%B0%E7%9A%84%E6%B8%B8%E6%88%8F&quot; aria-label=&quot;目前为止购买的游戏 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;目前为止购买的游戏&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;游戏&lt;/th&gt;
&lt;th&gt;评价&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Power Star Golf&lt;/td&gt;
&lt;td&gt;主机商家赠送，兑换后再未玩过&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;决战喵星&lt;/td&gt;
&lt;td&gt;主机商家赠送，兑换后再未玩过&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forza Horizon 4&lt;/td&gt;
&lt;td&gt;买的第一个游戏，体验 Xbox 必玩，较少玩&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assassin Creed - Unity&lt;/td&gt;
&lt;td&gt;冲这个系列买的主机，主线通关，期间看了不少法国大革命历史&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assassin Creed - Origins&lt;/td&gt;
&lt;td&gt;主线通关，期间看了些托勒密王朝末期的历史&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Halo - The Master Chief Collection&lt;/td&gt;
&lt;td&gt;冲着名气买的，玩了一点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Halo 5: Guardians&lt;/td&gt;
&lt;td&gt;冲着名气买的，玩了一点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rise of the Tomb Raider - 20 year celebration&lt;/td&gt;
&lt;td&gt;之前电脑上拖不动，中文语音业界良心，正玩主线过半&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ori and the Blind Forest: Definitive Edition&lt;/td&gt;
&lt;td&gt;网友推荐，较少玩&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Battle Field 1&lt;/td&gt;
&lt;td&gt;主线剧情通关，了解一战的好材料&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Battle Field 5&lt;/td&gt;
&lt;td&gt;主线过半，了解二战的好材料&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metro 2033 Redux&lt;/td&gt;
&lt;td&gt;独特的 RPG FPS，喜欢小说的末日废土故事背景，主线通关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metro Last Light Redux&lt;/td&gt;
&lt;td&gt;主线通关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hellblade: Senua’s sacrifice&lt;/td&gt;
&lt;td&gt;Game Pass 试用时玩的，被未修复的 &lt;a href=&quot;https://www.youtube.com/watch?v=PZsMbxvDjLQ&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;torch bug 卡住剧情&lt;/a&gt;玩不下去。很多感受&lt;a href=&quot;/2019/09/hellblade-senuas-sacrifice&quot;&gt;单独写了篇文章&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overcooked&lt;/td&gt;
&lt;td&gt;在朋友 Switch 上玩了的，准备再买个手柄来和女朋友一起玩&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;值得关注的&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%80%BC%E5%BE%97%E5%85%B3%E6%B3%A8%E7%9A%84&quot; aria-label=&quot;值得关注的 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;值得关注的&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/channel/UCLgGLSFMZQB8c0WGcwE49Gw&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;攻壳Gamker唯一官方频道&lt;/a&gt;，有很多对游戏的深刻解读&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/channel/UCnq0zNFkSa8YFc3f1-7Q3mg&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;游研社YYSTV&lt;/a&gt;，有很多对游戏的深刻解读&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.yxzzd.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;游戏早知道&lt;/a&gt;，最新折扣第一时间知晓，一边玩游戏一边捂好钱包，经常看到小编在凌晨发微信文章&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;xbox-近况照片&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#xbox-%E8%BF%91%E5%86%B5%E7%85%A7%E7%89%87&quot; aria-label=&quot;xbox 近况照片 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Xbox 近况照片&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1c42935b3c3859915d7e3be09c74fcca/ca9b4/xbox-and-wii.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.31578947368422%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAwAC/8QAFQEBAQAAAAAAAAAAAAAAAAAAAgP/2gAMAwEAAhADEAAAAVyxyotuR//EABsQAQACAgMAAAAAAAAAAAAAAAIBAwAEESEj/9oACAEBAAEFAlaxZO5MSPQoF4qzwej/AP/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AYr/xAAXEQEAAwAAAAAAAAAAAAAAAAAAARES/9oACAECAQE/AdQp/8QAGxAAAgIDAQAAAAAAAAAAAAAAAAERMRIhIjL/2gAIAQEABj8CdaPJlUnSkoSP/8QAGxABAQEAAgMAAAAAAAAAAAAAAREAITFRYbH/2gAIAQEAAT8hNOYp3GaSW5tp6MtanzuPQ/coHQb/2gAMAwEAAgADAAAAECAP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARMf/aAAgBAwEBPxDbyUOF/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARQf/aAAgBAgEBPxA7QE1v/8QAGhABAAMBAQEAAAAAAAAAAAAAAQARITFBUf/aAAgBAQABPxC4Cqj9AdamQsFokFbzF2MAALVrFU74pgrl9qAdoAbP/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/1c42935b3c3859915d7e3be09c74fcca/5ed05/xbox-and-wii.webp 190w,
/static/1c42935b3c3859915d7e3be09c74fcca/9d76b/xbox-and-wii.webp 380w,
/static/1c42935b3c3859915d7e3be09c74fcca/33466/xbox-and-wii.webp 760w,
/static/1c42935b3c3859915d7e3be09c74fcca/ca244/xbox-and-wii.webp 1140w,
/static/1c42935b3c3859915d7e3be09c74fcca/e9f1f/xbox-and-wii.webp 1200w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/1c42935b3c3859915d7e3be09c74fcca/89129/xbox-and-wii.jpg 190w,
/static/1c42935b3c3859915d7e3be09c74fcca/0036d/xbox-and-wii.jpg 380w,
/static/1c42935b3c3859915d7e3be09c74fcca/e484a/xbox-and-wii.jpg 760w,
/static/1c42935b3c3859915d7e3be09c74fcca/32d87/xbox-and-wii.jpg 1140w,
/static/1c42935b3c3859915d7e3be09c74fcca/ca9b4/xbox-and-wii.jpg 1200w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/1c42935b3c3859915d7e3be09c74fcca/e484a/xbox-and-wii.jpg&quot;
            alt=&quot;xbox-and-wii&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;我把 Xbox 塞到电视柜里，平时电视柜移门都关着在，我不喜欢灰尘多。
但是我电视柜不够深，Xbox 正面朝里放，下半部分凹进去的正好贴在柜子后板，才能关上门。&lt;/p&gt;
&lt;p&gt;中间红色是买家给的换区U盘，128MB的U盘，现在这么小的很少见呢！下面白色是 Wii，偶尔还玩一玩。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[博客从 Wordpress 迁移到 Gatsby]]></title><description><![CDATA[利用 Wordpress 插件 WP2Static 将内容静态化成 HTML，然后利用插件 gatsby-source-wordpress 提供的数据，为旧 blog 内容生成目录，并连接到静态化后的 HTML]]></description><link>https://www.berlinchan.com/2019/08/migrate-from-wordpress-to-gatsby</link><guid isPermaLink="false">https://www.berlinchan.com/2019/08/migrate-from-wordpress-to-gatsby</guid><pubDate>Mon, 12 Aug 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;之前&lt;a href=&quot;/2019/08/why-leave-wordpress-and-aliyun/&quot;&gt;为什么弃用WordPress并搬离阿里云&lt;/a&gt;中记的三点问题，
导致我决定迁移网站，弃用 Wordpress 的替代方案比较过 Jekyll/Hexo/Gatsby 三种框架。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jekyll，虽然是 GitHub Pages 官方支持的静态内容生成框架，但是我对 Ruby 语言不熟悉，所以首先排除。&lt;/li&gt;
&lt;li&gt;Hexo，上手容易有很多中文教程，但是文档写的不好，很多 API 说明不容易找到，不过 StackOverflow 上有很多热心人的补充。我试着用 Hexo 搭建&lt;a href=&quot;https://museum.berlinchan.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站博物馆&lt;/a&gt;，期间不喜欢主题中的模板引擎语法。&lt;/li&gt;
&lt;li&gt;Gatsby，是我迁移的选择。基于 React 优雅的组件构建，既能轻松搭上 SPA PWA 的现代化便车，也顺便在迁移中感受一下 GraphQL。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;迁移工作&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E8%BF%81%E7%A7%BB%E5%B7%A5%E4%BD%9C&quot; aria-label=&quot;迁移工作 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;迁移工作&lt;/h2&gt;
&lt;p&gt;迁移的前提是&lt;strong&gt;保证旧博客内容访问正常&lt;/strong&gt;，最后方案可分为两部分工作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;脱离 Wordpress 程序，将旧文章生成为静态文件&lt;/li&gt;
&lt;li&gt;搭建新站，将旧文章保持原来路径的导入到新站&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第一步脱离 Wordpress 程序，我用插件 WP2Static 将整站生成为静态 HTML 文件，注意生成前先将评论和不必要的插件关掉，
我的阿里云虚主机全天处于半死的状态，所以我将文件和数据库放到本地的 WAMP 环境中运行，
调大内存后，剩下工作这个插件做的已经很好了，有时候生成错误多试几次就好。&lt;/p&gt;
&lt;p&gt;生成的静态 HTML 页面中有很多冗余的 SVG 图标数据，还有其他冗余的标签，可以批处理精简掉，
另外批量替换 URL 为存档地址，最后部署到 GitHub Pages。&lt;/p&gt;
&lt;h2 id=&quot;基于-gatsby-搭建新站&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%9F%BA%E4%BA%8E-gatsby-%E6%90%AD%E5%BB%BA%E6%96%B0%E7%AB%99&quot; aria-label=&quot;基于 gatsby 搭建新站 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;基于 Gatsby 搭建新站&lt;/h2&gt;
&lt;p&gt;Gatsby 有很多 &lt;a href=&quot;https://www.gatsbyjs.org/starters/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;starter 项目&lt;/a&gt;，最初我用&lt;a href=&quot;https://github.com/alxshelepenok/gatsby-starter-lumen&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;gatsby-starter-lumen&lt;/a&gt;作为 starter 开始的，
它集成了我不准备使用的 Netlify CMS 功能，因为之前建&lt;a href=&quot;https://museum.berlinchan.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站博物馆&lt;/a&gt;时候，
发现 Netlify 的 CDN 加速，在大陆访问反而还没有 GitHub Pages 本身快，所以决定不使用 Netlify 服务。&lt;/p&gt;
&lt;p&gt;还有 Flow 和 Jest 也被我移除。起初想多用用测试框架和静态检查的，但发现频繁变动中写这些测试和检查太过繁琐。&lt;/p&gt;
&lt;p&gt;UI 方案引入 &lt;a href=&quot;https://material-ui.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Material-UI&lt;/a&gt;进行改造，并形成最终的样子。&lt;/p&gt;
&lt;h2 id=&quot;将旧文章导入到-gatsby-新站&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%B0%86%E6%97%A7%E6%96%87%E7%AB%A0%E5%AF%BC%E5%85%A5%E5%88%B0-gatsby-%E6%96%B0%E7%AB%99&quot; aria-label=&quot;将旧文章导入到 gatsby 新站 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;将旧文章导入到 Gatsby 新站&lt;/h2&gt;
&lt;p&gt;得益于 Gatsby 的&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pull data from anywhere&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wordpress 的旧文章很容易接入 GraphQL 并为生成器提供数据，使用 &lt;code class=&quot;language-text&quot;&gt;gatsby-source-wordpress&lt;/code&gt; 就行了，
安装和配置见官方教程 &lt;a href=&quot;https://www.gatsbyjs.org/tutorial/wordpress-source-plugin-tutorial/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;WordPress Source Plugin Tutorial&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;然后我用接入的 Wordpress 旧站数据生成了目录及文章，并在文章中添加指向原存档旧页面的链接，像下图这样：
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/32e6b5c7f989615b9ca9b183cf5facd4/bc1df/archived-blog-tips.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.42105263157895%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACFUlEQVR42n1Qa2+bMBT1v1nXbdqmSdN+aJ/psk37MfnSQjXxCJAEkmBj3pDU4BicsAtppGrSejhcHV9zjc9BlAaapuma9qiqzmymqqplWSAs29F03TQNz/N0Xfd9PDVNEH80zXXdxWIexwmqWJXneRSFlNKqqhLoJVCiNM3yLKNBUBZFEidpmj4xhjEBcdjvD4eDlBJRGi4WC8d2AkKWnjeZTOqqgu12gITnBCGElH1/NwCWqNxs4J9hGEVJElDqzOc1500rRds2w0zTNEclh0UvpDy+qCwLGOBF2hF3n4aHLJQRlnR9wN4uxOZ0SgI4OnTmM8gCvC2Xnm3Z69UanKM0SaYAw3AdOwxwHIWWYTjWdD41HcsihPh4ZRia7UCIM3vmmIYOWWZ53l8bQoIv4HAIpSgKyAME3D/NsjiOGWMpHD+grmuwyjkXA+D+CAbmzmy1WvqYQKoBCcC+57pxEmOMy7Jc+2vf95erFQz3Tk9BABBEkGR5zXeb7XbLqlbuRR8QBDZw0Mf8dqL5h2gvmyLGTb2pt6moN13Xdl3TdeJF/S9RuBEKbdVAPNJGpY1CdgoRRz70hOXu4Zl95+UuulX4xzH7NGbffrPPP9jXX+xs9PTlZ995M2Jv79j7O3Y+Yu++9+LsFnbZ+R37MO4rUom4uOc3Cr9R+bXSi8t7fqXwq4dnQvP6VI+di9MHSMrmdWOveP4LziXuxAlIynAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/32e6b5c7f989615b9ca9b183cf5facd4/5ed05/archived-blog-tips.webp 190w,
/static/32e6b5c7f989615b9ca9b183cf5facd4/9d76b/archived-blog-tips.webp 380w,
/static/32e6b5c7f989615b9ca9b183cf5facd4/1ebe6/archived-blog-tips.webp 590w&quot;
              sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/32e6b5c7f989615b9ca9b183cf5facd4/253c6/archived-blog-tips.png 190w,
/static/32e6b5c7f989615b9ca9b183cf5facd4/810ee/archived-blog-tips.png 380w,
/static/32e6b5c7f989615b9ca9b183cf5facd4/bc1df/archived-blog-tips.png 590w&quot;
            sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/32e6b5c7f989615b9ca9b183cf5facd4/bc1df/archived-blog-tips.png&quot;
            alt=&quot;archived-blog-tips&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;但这样接入 Wordpress 数据有个问题，每次更新生成网站都需要运行 Wordpress REST API。我的解决办法是将 GraphQL 查询结果存为 JSON 文件，
而后用 &lt;code class=&quot;language-text&quot;&gt;gatsby-transformer-json&lt;/code&gt; 接入到 GraphQL，就可以完全脱离开 Wordpress 运行了。我存为的 JSON 文件如下图：
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 291px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4232a97a7b3ec00c8f5d312989683211/43cf9/archived-blog-json.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.47368421052632%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACG0lEQVR42n2T2W4TMRSG550QbTPj2Tfv23gmk43SkpaqwA0tSNwXtUXwGk15S84kIa0gIH22LPv89tnsUaH64+Xi9Oz49amxLitJWdOyJjXZzjVhWVmjOEFx+gdemGSEK+XGpusJK4sqSvIMRpwVcZZH6WZR/K1ci+Ospky5XrVtnL+o8EGUhCjOdhZBlAD7xXCAKbf9zPZzjIVxEwjkCIWjMB6hyA/jvbKteHM3ZgI8b/pZhQmTuiIMdggTWVn969mtGIDwaia6xQkoAaEN5VJbJ5QBMUQe7E3YLjAfRYc+aqYLKg3ouVTadUpb61rdtNZ1XBllGqkM5DiInok3wK6b/BYLqZpWSANe1FSAH+AFV5oIBSVE61i8dQGfaCYzSBgVknOubAPvS60ho5QLxiVhnA5ijGJIZOINdzyRuukxUxaAp7QbC2WVdaYdw01MmqyojnwUQAnW9h69fHjOqy+P3dVqfPXQX61mnx8m1zA/9teP008/xx9X4v2KvgO2xl45uxmYD1Tzr9MPP+z5nT3/Zpd33eV3d37fvr3XyztzdmuWt/X8Zmu/xguCQ2A0OvD9g9HRS9u1mEJoRApqrFGSS8mMUZAExvDGeIeHkiKI85pKqtz09AJSrW1XYeZHqR9lcBQmBUryMAWzDA3rJzYdlhY1xlyOFyclplDMfPhGQ+WA54Xc357w74jQizcXQ3Nal+bFf7pyJ/4Fs1gXzEBulVYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/4232a97a7b3ec00c8f5d312989683211/5ed05/archived-blog-json.webp 190w,
/static/4232a97a7b3ec00c8f5d312989683211/e8ba4/archived-blog-json.webp 291w&quot;
              sizes=&quot;(max-width: 291px) 100vw, 291px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/4232a97a7b3ec00c8f5d312989683211/253c6/archived-blog-json.png 190w,
/static/4232a97a7b3ec00c8f5d312989683211/43cf9/archived-blog-json.png 291w&quot;
            sizes=&quot;(max-width: 291px) 100vw, 291px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/4232a97a7b3ec00c8f5d312989683211/43cf9/archived-blog-json.png&quot;
            alt=&quot;archived-blog-json&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;添加评论功能&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%B7%BB%E5%8A%A0%E8%AF%84%E8%AE%BA%E5%8A%9F%E8%83%BD&quot; aria-label=&quot;添加评论功能 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;添加评论功能&lt;/h2&gt;
&lt;p&gt;考虑过 Gitalk/Disqus/多说/Firebase/野狗，Firebase/Disqus 大陆用不了，Gitalk 只能用 GitHub 账号登陆，
多说已经无法说🤐，野狗已停止运营。&lt;/p&gt;
&lt;p&gt;权衡后还是选择业界领先的 Disqus，反正这博客站访问的人少🤣&lt;/p&gt;
&lt;h2 id=&quot;其它的尝试&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%85%B6%E5%AE%83%E7%9A%84%E5%B0%9D%E8%AF%95&quot; aria-label=&quot;其它的尝试 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;其它的尝试&lt;/h2&gt;
&lt;p&gt;最开始尝试直接将 WP2Static 生成的静态 HTML 页放到 Gatsby 项目的 &lt;code class=&quot;language-text&quot;&gt;/static&lt;/code&gt; 目录下，因为该目录的内容会不经处理的直接拷贝至生成目录。
然后用新站 Gatsby 生成目录指向这些旧文章，但这些链接就不在 SPA 的治下，前端路由会 404 找不到页面。&lt;/p&gt;
&lt;p&gt;或者使用 &lt;code class=&quot;language-text&quot;&gt;gatsby-plugin-meta-redirect&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;gatsby-plugin-client-side-redirect&lt;/code&gt; 生成一个 redirect 的空页面用于重定向。
但这样既然生成“空页面”了，还不如生成一篇完整的文章页，还能利用上 PWA 的 pre-fetch 特性。&lt;/p&gt;
&lt;p&gt;我在这些尝试上费了很多时间。&lt;/p&gt;
&lt;h2 id=&quot;github-上好用的工具&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#github-%E4%B8%8A%E5%A5%BD%E7%94%A8%E7%9A%84%E5%B7%A5%E5%85%B7&quot; aria-label=&quot;github 上好用的工具 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GitHub 上好用的工具&lt;/h2&gt;
&lt;p&gt;Gatsby 是一个快速发展中的新框架，将代码托管在 GitHub 上时，用市场中的 &lt;a href=&quot;https://github.com/marketplace/dependabot-preview&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Dependabot Preview&lt;/a&gt;
或者 &lt;a href=&quot;https://github.com/marketplace/renovate&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Renovate&lt;/a&gt;，能很好帮我们更新 npm 依赖。
&lt;a href=&quot;/2019/08/imgBot-optimize-image-asset-for-github-pages-project&quot;&gt;使用 ImgBot 自动为 Github Pages 项目优化图片&lt;/a&gt;，
还有持续集成工具 &lt;a href=&quot;https://github.com/marketplace/travis-ci&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Travis CI&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;这些市场中的工具设计的简单易用，自动化的工作为我节省了大量时间。&lt;/p&gt;
&lt;h2 id=&quot;总结&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%80%BB%E7%BB%93&quot; aria-label=&quot;总结 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;总结&lt;/h2&gt;
&lt;p&gt;Gatsby 是个非常不错的框架，框架自身和插件轻松实现 SPA PWA 特性，静态页先天的 SEO 优化。
我的迁移工作大概用了一个月业余时间，新站部署在 GitHub Pages 上，用阿里云 CDN 加速，访问速度体验较以前快了不少，Lighthouse 评分如下，并还可进一步改善：
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 760px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a446a0561c100f0b37614a7cc2f91421/7fbec/lighthouse-audit.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAABYlAAAWJQFJUiTwAAABaklEQVR42kWQ207jQAyG097CzXKoaNKMY88hmaRJSzJpElqhIhZVLUJCSIhdccHVstf7Fstb47Yc/rEsa6zP84+91Wrl+/7xj6PB8clgMDg9PTk4OPS8Xp/FeV/0+73eNnbFx822D1EUCRCawioBBCFgHIvUiDAEMdaiMGIkEGFqhUTBXSIVRQg7eYR8CNYlPl/4y+myCB+W+GsZqlTBYwtPHblkMwv/rOFnHZokfX39d319AyCklB6yIhxd5aPfzVmTzTP/5WZ41w7JkH9fBQ8OcnPbBH/Xw8tShEBvb/83m00QBES0hSVSzeraSV6k4/xy3rRtw97Kqixnjm0bo+ddzZ6LvOi6rqoqJhncvUxotOZQSkmlSMVKaf6Lc/WsriVJBCSpBUCWZYvFwjnHi/qE2finJJFVUsdxrNHG0tqUjJKJSa3VWu/3tCe/4Y8RiEy2SWzHxdRGbqKKfIqZ0WVWTs554Be2h98BaKNG9/aY/oEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/a446a0561c100f0b37614a7cc2f91421/5ed05/lighthouse-audit.webp 190w,
/static/a446a0561c100f0b37614a7cc2f91421/9d76b/lighthouse-audit.webp 380w,
/static/a446a0561c100f0b37614a7cc2f91421/33466/lighthouse-audit.webp 760w,
/static/a446a0561c100f0b37614a7cc2f91421/ca244/lighthouse-audit.webp 1140w,
/static/a446a0561c100f0b37614a7cc2f91421/0d9bb/lighthouse-audit.webp 1166w&quot;
              sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/a446a0561c100f0b37614a7cc2f91421/253c6/lighthouse-audit.png 190w,
/static/a446a0561c100f0b37614a7cc2f91421/810ee/lighthouse-audit.png 380w,
/static/a446a0561c100f0b37614a7cc2f91421/b4918/lighthouse-audit.png 760w,
/static/a446a0561c100f0b37614a7cc2f91421/c0204/lighthouse-audit.png 1140w,
/static/a446a0561c100f0b37614a7cc2f91421/7fbec/lighthouse-audit.png 1166w&quot;
            sizes=&quot;(max-width: 760px) 100vw, 760px&quot;
            type=&quot;image/png&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/a446a0561c100f0b37614a7cc2f91421/b4918/lighthouse-audit.png&quot;
            alt=&quot;lighthouse-audit&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[使用 ImgBot 自动为 Github 项目优化图片]]></title><description><![CDATA[用 Github Pages 做 blog 时，经常会插入很多图片，利用 ImgBot 可以自动为我们优化图片大小，加快图片加载]]></description><link>https://www.berlinchan.com/2019/08/imgBot-optimize-image-asset-for-github-project</link><guid isPermaLink="false">https://www.berlinchan.com/2019/08/imgBot-optimize-image-asset-for-github-project</guid><pubDate>Sun, 11 Aug 2019 10:46:37 GMT</pubDate><content:encoded>&lt;p&gt;我在建&lt;a href=&quot;https://github.com/BerlinChan/museum&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;旧站博物馆&lt;/a&gt;时，旧站内容中有不少图片，数量巨大不便于逐个处理，部分人工筛选和压缩后，闲逛 &lt;a href=&quot;https://github.com/marketplace&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github Marketplace&lt;/a&gt; 时看到了这个 &lt;a href=&quot;https://github.com/marketplace/imgbot&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ImgBot&lt;/a&gt;。&lt;/p&gt;
&lt;!-- endExcerpt --&gt;
&lt;p&gt;⚠️ 开始之前值得一提的是，使用在线工具存在安全风险。处理包含敏感内容的图片，应使用本地工具。&lt;/p&gt;
&lt;h2 id=&quot;介绍-imgbot&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BB%8B%E7%BB%8D-imgbot&quot; aria-label=&quot;介绍 imgbot permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;介绍 ImgBot&lt;/h2&gt;
&lt;p&gt;官方介绍翻译：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ImgBot 是一个为你节省时间优化图片的机器人。优化图片意味着不牺牲图片质量和更小的文件大小。
安装后不久，你会收到一个优化图片的 pull request。合并这个 pull request 就行了！Imgbot 会伴随你的工作，保持图片的优化。
ImgBot 默认使用无损压缩。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;使用-imgbot&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E4%BD%BF%E7%94%A8-imgbot&quot; aria-label=&quot;使用 imgbot permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;使用 ImgBot&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/marketplace/imgbot&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ImgBot&lt;/a&gt; 对开源项目是免费的，点最下面的安装按钮然后选择要安装的项目就完成，太简单就不放图了，过几分钟我就收到优化图片后的 pull request，如下：&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 755px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/191b1e9bd4cebce5bc819fe2e22688aa/92579/pull-request-from-ImgBot.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.36842105263158%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe5WCgf/xAAWEAEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQEAAQUCDK3/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAXEAADAQAAAAAAAAAAAAAAAAABEBEx/9oACAEBAAE/IYJA9B//2gAMAwEAAgADAAAAECDP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAAMAAwAAAAAAAAAAAAAAAQARMRAhkf/aAAgBAQABPxAzputHUCsPJbKyAvOP/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
          &lt;source
              srcset=&quot;/static/191b1e9bd4cebce5bc819fe2e22688aa/5ed05/pull-request-from-ImgBot.webp 190w,
/static/191b1e9bd4cebce5bc819fe2e22688aa/9d76b/pull-request-from-ImgBot.webp 380w,
/static/191b1e9bd4cebce5bc819fe2e22688aa/0f085/pull-request-from-ImgBot.webp 755w&quot;
              sizes=&quot;(max-width: 755px) 100vw, 755px&quot;
              type=&quot;image/webp&quot;
            /&gt;
          &lt;source
            srcset=&quot;/static/191b1e9bd4cebce5bc819fe2e22688aa/89129/pull-request-from-ImgBot.jpg 190w,
/static/191b1e9bd4cebce5bc819fe2e22688aa/0036d/pull-request-from-ImgBot.jpg 380w,
/static/191b1e9bd4cebce5bc819fe2e22688aa/92579/pull-request-from-ImgBot.jpg 755w&quot;
            sizes=&quot;(max-width: 755px) 100vw, 755px&quot;
            type=&quot;image/jpeg&quot;
          /&gt;
          &lt;img
            class=&quot;gatsby-resp-image-image&quot;
            src=&quot;/static/191b1e9bd4cebce5bc819fe2e22688aa/92579/pull-request-from-ImgBot.jpg&quot;
            alt=&quot;pull-request-from-ImgBot&quot;
            title=&quot;&quot;
            loading=&quot;lazy&quot;
            decoding=&quot;async&quot;
            style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
          /&gt;
        &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;被优化图片的报告可见，png 图片文件大小最高被优化掉88.43%，很可观的数字！总体被优化掉4.48%。&lt;/p&gt;
&lt;p&gt;ImgBot 默认是无损压缩的，可以按需进行&lt;a href=&quot;https://imgbot.net/docs/#configuration&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;配置&lt;/a&gt;为有损压缩，对文件大小进一步压榨。&lt;/p&gt;
&lt;p&gt;配置方法是在根目录放置文件 &lt;code class=&quot;language-text&quot;&gt;.imgbotconfig&lt;/code&gt;,内容如下&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;aggressiveCompression&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 使用有损压缩 true|false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;还有更多配置项，完整说明请见&lt;a href=&quot;https://imgbot.net/docs/#configuration&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;官方文档&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;schedule&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;daily&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 执行周期 daily|weekly|monthly&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;ignoredFiles&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;*.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;                   &lt;span class=&quot;token comment&quot;&gt;// ignore by extension&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;image1.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;              &lt;span class=&quot;token comment&quot;&gt;// ignore by filename&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;public/special_images/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ignore by folderpath&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;aggressiveCompression&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 使用有损压缩 true|false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;另外推荐-tinypng&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E5%8F%A6%E5%A4%96%E6%8E%A8%E8%8D%90-tinypng&quot; aria-label=&quot;另外推荐 tinypng permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;另外推荐 TinyPNG&lt;/h2&gt;
&lt;p&gt;后来我将经 ImgBot 无损压缩优化后的图片放到 &lt;a href=&quot;https://tinypng.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TinyPNG&lt;/a&gt; 中，竟然还能压榨掉很多字节，因为 TinyPNG 采用的是&lt;strong&gt;有损压缩&lt;/strong&gt;，官方说明翻译：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TinyPNG 采用智能有损压缩技术来优化 PNG 文件大小。有选择地减少图片中的颜色，用更少的字节数存储。图片损失几乎不可见，但文件大小有很大改善。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;TingPNG 是个不错的另外选择，他还提供压缩、智能裁剪的 API 用于批量处理。不过 ImgBot 也可以配置为有损压缩的，对于图片多的还是配置 ImgBot 简单又省事。&lt;/p&gt;
&lt;h2 id=&quot;更新-2025-10-30&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%E6%9B%B4%E6%96%B0-2025-10-30&quot; aria-label=&quot;更新 2025 10 30 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;更新 2025-10-30&lt;/h2&gt;
&lt;p&gt;TinyPNG 的免费服务最大支持 5MB 文件的处理，对于更大文件的支持需要订阅 Web Pro。网友 Ana 推荐另一款在线图片压缩工具 &lt;a href=&quot;https://www.websiteplanet.com/webtools/imagecompressor/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;imagecompressor&lt;/a&gt;，其支持最大 50MB 文件的处理。经实测，压缩一张 49.5MB 的 PNG 是没有问题的。&lt;/p&gt;</content:encoded></item><item><title><![CDATA[母亲节回宜昌]]></title><description><![CDATA[母亲节和夫人回去宜昌，陪妈妈看电影逛街吃小吃，妈妈好开心！还骑车闲逛了西坝。]]></description><link>https://www.berlinchan.com/2019/05/mothers-day-yichang</link><guid isPermaLink="false">https://www.berlinchan.com/2019/05/mothers-day-yichang</guid><pubDate>Sun, 12 May 2019 10:46:37 GMT</pubDate><content:encoded>&lt;!-- endExcerpt --&gt;
&lt;p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.49999999999999%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;div class=&quot;embedVideo-container&quot;&gt; &lt;iframe title=&quot;&quot; src=&quot;https://www.youtube.com/embed/kcrbp9ZhXvQ?rel=0&quot; class=&quot;embedVideo-iframe&quot; style=&quot;border:0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; loading=&quot;eager&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;
&lt;p&gt;母亲节和夫人回去宜昌，陪妈妈看电影逛街吃小吃，妈妈好开心！妈妈出镜视频太少，都拍照片了。&lt;/p&gt;
&lt;p&gt;骑车在西坝逛，很多以前的房子都要拆了，药厂、学校、餐馆，下次去估计已不再有记忆中的模样，有点伤感。&lt;/p&gt;</content:encoded></item></channel></rss>