<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://blog.17lai.site</id>
    <title>夜法之书 • Posts by &#34;web&#34; category</title>
    <link href="https://blog.17lai.site" />
    <updated>2026-06-04T09:04:45.000Z</updated>
    <category term="github" />
    <category term="jekyll" />
    <category term="blog" />
    <category term="gitlab" />
    <category term="git" />
    <category term="crack" />
    <category term="qnap" />
    <category term="硬盘" />
    <category term="nas" />
    <category term="linux" />
    <category term="hardlink" />
    <category term="bt" />
    <category term="pt" />
    <category term="markdown" />
    <category term="picgo" />
    <category term="wiz" />
    <category term="joplin" />
    <category term="typora" />
    <category term="embeded" />
    <category term="hisilicon" />
    <category term="driver" />
    <category term="3798m" />
    <category term="mpp" />
    <category term="unf" />
    <category term="kernel" />
    <category term="3G" />
    <category term="4G" />
    <category term="3531a" />
    <category term="移植" />
    <category term="嵌入式" />
    <category term="宝塔" />
    <category term="vps" />
    <category term="建站" />
    <category term="nginx" />
    <category term="apache" />
    <category term="mysql" />
    <category term="docker" />
    <category term="lamp" />
    <category term="php" />
    <category term="tomcat" />
    <category term="ssd" />
    <category term="hdd" />
    <category term="qbittorrent" />
    <category term="pdca" />
    <category term="5w2h" />
    <category term="smart" />
    <category term="swot" />
    <category term="grow" />
    <category term="okr" />
    <category term="wbs" />
    <category term="启动" />
    <category term="transmission" />
    <category term="ssl" />
    <category term="tls" />
    <category term="隐私" />
    <category term="安全" />
    <category term="优化" />
    <category term="emby" />
    <category term="sonarr" />
    <category term="jeckett" />
    <category term="portainer" />
    <category term="registry" />
    <category term="教程" />
    <category term="email" />
    <category term="https" />
    <category term="gitbook" />
    <category term="debian" />
    <category term="laptop" />
    <category term="ed2k" />
    <category term="ati" />
    <category term="note" />
    <category term="webdav" />
    <category term="mariadb" />
    <category term="web" />
    <category term="http" />
    <category term="css" />
    <category term="js" />
    <category term="mermaid" />
    <category term="vim" />
    <category term="ide" />
    <category term="3a" />
    <category term="ae" />
    <category term="image" />
    <category term="ccs" />
    <category term="music" />
    <category term="刮削" />
    <category term="musicbrainz" />
    <category term="mp3tag" />
    <category term="thinkpad" />
    <category term="sound" />
    <category term="speaker" />
    <category term="ai" />
    <category term="face" />
    <category term="cmake" />
    <category term="develop" />
    <category term="韩红" />
    <category term="ipv6" />
    <category term="hexo" />
    <category term="matery" />
    <category term="npm" />
    <category term="vscode" />
    <category term="插件" />
    <category term="编码" />
    <category term="traefik" />
    <category term="proxy" />
    <category term="swarm" />
    <category term="ubuntu" />
    <category term="tmm" />
    <category term="字幕" />
    <category term="plex" />
    <category term="mstream" />
    <category term="selfhost" />
    <category term="earthly" />
    <category term="中岛美嘉" />
    <category term="ci/cd" />
    <category term="node" />
    <category term="jenkins" />
    <category term="tools" />
    <category term="winrar" />
    <category term="emoji" />
    <category term="shell" />
    <category term="cdn" />
    <category term="seo" />
    <category term="plantuml" />
    <category term="mathjax" />
    <category term="isp" />
    <category term="awb" />
    <category term="mywork" />
    <category term="devops" />
    <category term="fitness" />
    <category term="健身" />
    <category term="运动" />
    <category term="drowio" />
    <category term="man" />
    <category term="框架" />
    <category term="图解" />
    <category term="中医" />
    <category term="西医" />
    <category term="文化历史" />
    <category term="doxygen" />
    <category term="管理" />
    <category term="竞品分析" />
    <category term="rss" />
    <category term="rsshub" />
    <category term="k8s" />
    <category term="harbor" />
    <category term="驱动" />
    <category term="sensor" />
    <category term="社会观察" />
    <category term="知识" />
    <category term="智慧" />
    <category term="os" />
    <category term="自制" />
    <category term="bash" />
    <category term="nodeppt" />
    <category term="编译器" />
    <category term="C" />
    <category term="compiler" />
    <category term="写作" />
    <category term="potplayer" />
    <category term="概率" />
    <category term="普朗克" />
    <category term="故事" />
    <category term="vercel" />
    <category term="yandex" />
    <category term="source" />
    <category term="hack" />
    <category term="download" />
    <category term="action" />
    <category term="workflow" />
    <category term="pmbok" />
    <category term="ipd" />
    <category term="绩效" />
    <category term="不可能三角" />
    <category term="chatgpt" />
    <category term="openai" />
    <category term="calibre" />
    <category term="douban" />
    <category term="book" />
    <category term="投资" />
    <category term="理财" />
    <category term="金融" />
    <category term="waline" />
    <category term="信用卡" />
    <category term="search" />
    <category term="IPD" />
    <category term="MM方法论" />
    <category term="端到端" />
    <category term="开源" />
    <category term="echarts" />
    <category term="读书评鉴" />
    <category term="易学" />
    <category term="穿越必备" />
    <category term="google" />
    <category term="profile" />
    <category term="高考" />
    <category term="专业" />
    <category term="人性" />
    <category term="人格分析" />
    <category term="定制服务器" />
    <category term="webhook" />
    <category term="密码" />
    <category term="bitwarden" />
    <category term="tampermonkey" />
    <category term="zhihu" />
    <category term="csdn" />
    <category term="juejin" />
    <category term="健康" />
    <category term="恢复" />
    <category term="养生" />
    <category term="umami" />
    <category term="office" />
    <category term="kms" />
    <category term="破解" />
    <category term="刀郎" />
    <category term="windows" />
    <category term="垃圾清理" />
    <category term="速度优化" />
    <category term="阳历" />
    <category term="阴历" />
    <category term="阴阳和历" />
    <category term="热水器" />
    <category term="维护" />
    <category term="汽车" />
    <category term="测速" />
    <category term="nat" />
    <category term="光猫" />
    <category term="天干" />
    <category term="地支" />
    <category term="sql" />
    <category term="zlibrary" />
    <category term="postgresql" />
    <category term="ipfs" />
    <category term="sqlite" />
    <category term="dns" />
    <category term="smartdns" />
    <category term="dnsmasq" />
    <category term="物理" />
    <category term="广义相对论" />
    <category term="科学" />
    <category term="传媒" />
    <category term="侠义相对论" />
    <category term="llm" />
    <category term="api" />
    <category term="免费" />
    <category term="ollama" />
    <category term="高薪" />
    <category term="codex" />
    <category term="electron" />
    <category term="功能测试" />
    <category term="tag" />
    <category term="layout" />
    <category term="interaction" />
    <category term="自动化测试" />
    <category term="study" />
    <category term="english" />
    <category term="雅思" />
    <entry>
        <id>https://blog.17lai.site/posts/2bcf4ad9/</id>
        <title>如何优雅的给npm包打补丁--以hexo-minify包为例</title>
        <link rel="alternate" href="https://blog.17lai.site/posts/2bcf4ad9/"/>
        <content type="html">&lt;p&gt;本文将以修复 &lt;code&gt;hexo-minify&lt;/code&gt; 的这个报错为例，详细介绍两种在前端开发中&lt;strong&gt;最优雅、最常用且能一劳永逸&lt;/strong&gt;的 npm 包打补丁方法。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;hr&gt;
&lt;p&gt;在维护 Hexo 博客或进行前端开发时，我们经常会遇到这样的尴尬局面：&lt;strong&gt;某个长期不更新的第三方插件，因为它的深层依赖项升级，导致整个项目直接崩溃。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;最近升级 hexo 内部包版本，就遇到了几个兼容性问题。其中一个报错&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;FATAL Something&amp;#39;s wrong. Maybe you can find the solution here: https:&amp;#x2F;&amp;#x2F;hexo.io&amp;#x2F;docs&amp;#x2F;troubleshooting.html
TypeError: minimatch is not a function
    at &amp;#x2F;app&amp;#x2F;node_modules&amp;#x2F;hexo-minify&amp;#x2F;lib&amp;#x2F;filter.js:20:33
    at Array.some (&amp;lt;anonymous&amp;gt;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;🔍-问题起因&#34;&gt;🔍 问题起因&lt;/h2&gt;
&lt;p&gt;报错信息指向了 &lt;code&gt;hexo-minify/lib/filter.js&lt;/code&gt;。导致这个报错的根本原因是：&lt;br&gt;
&lt;code&gt;hexo-minify&lt;/code&gt; 依赖的 &lt;code&gt;minimatch&lt;/code&gt; 库最近发布了破坏性更新（v9.x+），将默认导出（Default Export）改为了命名导出（Named Export）。&lt;/p&gt;
&lt;p&gt;而旧版的 &lt;code&gt;hexo-minify&lt;/code&gt; 仍在像以前一样，试图将 &lt;code&gt;minimatch&lt;/code&gt; 当作一个函数直接调用：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-javascript&#34; data-language=&#34;javascript&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const minimatch &amp;#x3D; require(&amp;#39;minimatch&amp;#39;); &amp;#x2F;&amp;#x2F; 此时导入的是一个对象，而不是函数
minimatch(file, pattern); &amp;#x2F;&amp;#x2F; 报错：TypeError: minimatch is not a function&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;面对这种情况，等待原作者更新可能遥遥无期。我们需要自己动手解决，并且要保证&lt;strong&gt;团队其他成员拉取代码、或者下次重新 &lt;code&gt;npm install&lt;/code&gt; 时，这个修复依然有效&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;🛠️-方法一：使用依赖覆盖（overrides）——-适合版本不兼容问题&#34;&gt;🛠️ 方法一：使用依赖覆盖（Overrides）—— 适合版本不兼容问题&lt;/h2&gt;
&lt;p&gt;如果你的 Node.js 版本在 16+ 且使用 npm v8.3.0 以上，最省心的方法是&lt;strong&gt;强行锁死深层依赖的版本&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;由于 &lt;code&gt;minimatch&lt;/code&gt; 在 &lt;code&gt;v5&lt;/code&gt; 及以下版本是兼容函数调用的，我们可以通过配置 &lt;code&gt;package.json&lt;/code&gt;，强制让所有子依赖都使用旧版的 &lt;code&gt;minimatch&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;操作步骤&#34;&gt;操作步骤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;打开项目根目录下的 &lt;code&gt;package.json&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;添加 &lt;code&gt;overrides&lt;/code&gt; 字段（如果你使用的是 Yarn，请使用 &lt;code&gt;resolutions&lt;/code&gt;）：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;=== “npm”&lt;br&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-json&#34; data-language=&#34;json&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-json&#34;&gt;&amp;#123;
  &amp;quot;dependencies&amp;quot;: &amp;#123;
    &amp;quot;hexo-minify&amp;quot;: &amp;quot;^x.x.x&amp;quot;
  &amp;#125;,
  &amp;quot;overrides&amp;quot;: &amp;#123;
    &amp;quot;minimatch&amp;quot;: &amp;quot;^5.1.6&amp;quot;
  &amp;#125;
&amp;#125;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;=== “Yarn”&lt;br&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-json&#34; data-language=&#34;json&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-json&#34;&gt;&amp;#123;
  &amp;quot;dependencies&amp;quot;: &amp;#123;
    &amp;quot;hexo-minify&amp;quot;: &amp;quot;^x.x.x&amp;quot;
  &amp;#125;,
  &amp;quot;resolutions&amp;quot;: &amp;#123;
    &amp;quot;minimatch&amp;quot;: &amp;quot;^5.1.6&amp;quot;
  &amp;#125;
&amp;#125;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;=== “pnpm”&lt;br&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-json&#34; data-language=&#34;json&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-json&#34;&gt;&amp;#123;
  &amp;quot;pnpm&amp;quot;: &amp;#123;
    &amp;quot;overrides&amp;quot;: &amp;#123;
      &amp;quot;minimatch&amp;quot;: &amp;quot;^5.1.6&amp;quot;
    &amp;#125;
  &amp;#125;
&amp;#125;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;清理缓存并重新安装：&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;rm -rf node_modules package-lock.json
npm install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;💡-优缺点分析&#34;&gt;💡 优缺点分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：无需修改任何源码，纯配置解决，极其优雅。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：只适用于通过“降低/锁定依赖版本”能解决的问题。如果必须修改源码，此方法无效。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;🛠️-方法二：使用-patch-package-自动打补丁-——-适合需要修改源码的场景&#34;&gt;🛠️ 方法二：使用 &lt;code&gt;patch-package&lt;/code&gt; 自动打补丁 —— 适合需要修改源码的场景&lt;/h2&gt;
&lt;p&gt;如果你必须使用新版的依赖，或者报错的根本不是版本问题，而是代码本身有 Bug，那么就需要直接修改 &lt;code&gt;node_modules&lt;/code&gt; 里的源码。&lt;/p&gt;
&lt;p&gt;但是，直接修改 &lt;code&gt;node_modules&lt;/code&gt; 是临时的，一旦重新 &lt;code&gt;npm install&lt;/code&gt; 就会被覆盖。&lt;code&gt;patch-package&lt;/code&gt; 就是为了解决这个痛点而生的工具。&lt;/p&gt;
&lt;h3 id=&#34;操作步骤-2&#34;&gt;操作步骤&lt;/h3&gt;
&lt;h4 id=&#34;1-安装-patch-package&#34;&gt;1. 安装 &lt;code&gt;patch-package&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;在项目根目录下安装它：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;npm install patch-package --save-dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h4 id=&#34;2-手动修改-node-modules-中的错误代码&#34;&gt;2. 手动修改 &lt;code&gt;node_modules&lt;/code&gt; 中的错误代码&lt;/h4&gt;
&lt;p&gt;打开报错的文件：&lt;code&gt;node_modules/hexo-minify/lib/filter.js&lt;/code&gt;。找到顶部引入 &lt;code&gt;minimatch&lt;/code&gt; 的地方：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-javascript&#34; data-language=&#34;javascript&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const minimatch &amp;#x3D; require(&amp;#39;minimatch&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;将其修改为兼容新旧版本的写法：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-javascript&#34; data-language=&#34;javascript&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;const minimatchModule &amp;#x3D; require(&amp;#39;minimatch&amp;#39;);
&amp;#x2F;&amp;#x2F; 兼容处理：如果导入的是函数则直接用，否则使用其导出的 minimatch 属性
const minimatch &amp;#x3D; typeof minimatchModule &amp;#x3D;&amp;#x3D;&amp;#x3D; &amp;#39;function&amp;#39; ? minimatchModule : minimatchModule.minimatch;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h4 id=&#34;3-创建补丁文件（patch）&#34;&gt;3. 创建补丁文件（Patch）&lt;/h4&gt;
&lt;p&gt;在终端运行 &lt;code&gt;patch-package&lt;/code&gt; 命令，传入你修改过的包名：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;npx patch-package hexo-minify&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;运行成功后，你会发现项目根目录下多出了一个 &lt;code&gt;patches&lt;/code&gt; 文件夹，里面有一个类似 &lt;code&gt;hexo-minify+0.5.0.patch&lt;/code&gt; 的文件。这个文件记录了你对该包的修改。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;注意&lt;/strong&gt;：请把这个 &lt;code&gt;patches&lt;/code&gt; 文件夹提交到你的 Git 仓库中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;4-配置自动修补脚本&#34;&gt;4. 配置自动修补脚本&lt;/h4&gt;
&lt;p&gt;为了让其他人拉取代码后、或者每次执行 &lt;code&gt;npm install&lt;/code&gt; 时都自动应用这个补丁，我们需要在 &lt;code&gt;package.json&lt;/code&gt; 的 &lt;code&gt;scripts&lt;/code&gt; 中配置 &lt;code&gt;postinstall&lt;/code&gt; 钩子：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-json&#34; data-language=&#34;json&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-json&#34;&gt;&amp;#123;
  &amp;quot;scripts&amp;quot;: &amp;#123;
    &amp;quot;postinstall&amp;quot;: &amp;quot;patch-package&amp;quot;,
    &amp;quot;build&amp;quot;: &amp;quot;hexo generate&amp;quot;
  &amp;#125;
&amp;#125;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h3 id=&#34;🧪-测试效果&#34;&gt;🧪 测试效果&lt;/h3&gt;
&lt;p&gt;现在你可以放心删掉整个 &lt;code&gt;node_modules&lt;/code&gt; 文件夹，然后重新执行：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;npm install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;你会看到控制台在安装完成后自动输出：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-text&#34; data-language=&#34;text&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-text&#34;&gt;&amp;gt; patch-package
&amp;gt; Applying patches...
&amp;gt; hexo-minify@0.5.0 ✔&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;这说明补丁已经被自动应用了！再次运行 &lt;code&gt;hexo g&lt;/code&gt;，报错完美解决。&lt;/p&gt;
&lt;h3 id=&#34;💡-优缺点分析-2&#34;&gt;💡 优缺点分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：非常强大，可以直接修改任何第三方库的源码；补丁文件跟随 Git 仓库，团队成员及 CI/CD 流程能自动同步。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：如果第三方库本身进行了大版本升级，补丁可能会因为源码行数对不上而失效（此时需要重新生成补丁）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;📝-总结与建议&#34;&gt;📝 总结与建议&lt;/h2&gt;
&lt;p&gt;在面对 npm 依赖问题时，我们的排查和解决思路应该是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;查&lt;/strong&gt;：先看社区有没有提 Issue，原作者有没有修复。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;套&lt;/strong&gt;：如果只是深层依赖的版本冲突，优先使用 &lt;strong&gt;方法一（Overrides/Resolutions）&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改&lt;/strong&gt;：如果需要改动源码才能解决，果断使用 &lt;strong&gt;方法二（patch-package）&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过这两种方法，我们不仅能快速解决眼前的 Bug，还能保证项目的构建流程在任何环境下都具备极高的稳定性。希望这篇文章能帮到正在为 Node.js 依赖报错头疼的你！&lt;/p&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css&#34;&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/d3@7&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-view@0.18.10&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10&#34;&gt;&lt;/script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;/css/markmap.css&#34;&gt;

&lt;script src=&#34;/js/markmap.js&#34;&gt;&lt;/script&gt;
&lt;script data-pjax src=&#34;/lib/hbe.54b2b4c40b.js&#34;&gt;&lt;/script&gt;&lt;link href=&#34;/css/hbe-style.css&#34; rel=&#34;stylesheet&#34; type=&#34;text/css&#34;&gt;</content>
        <category term="hexo" />
        <category term="npm" />
        <category term="node" />
        <updated>2026-06-04T09:04:45.000Z</updated>
    </entry>
    <entry>
        <id>https://blog.17lai.site/posts/1d87ed2/</id>
        <title>如何防止密码泄露，保护密码安全？</title>
        <link rel="alternate" href="https://blog.17lai.site/posts/1d87ed2/"/>
        <content type="html">&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;密码安全：现代数字生活的必需品&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在这个互联网无处不在的时代，我们的生活已经与网络紧密相连。无论是社交媒体、在线银行、电子邮件，还是各种在线服务，每一个账户的安全都依赖于密码。然而，密码的管理和保护却成为了一个巨大的挑战。&lt;/p&gt;
&lt;h3 id=&#34;密码安全的重要性&#34;&gt;密码安全的重要性&lt;/h3&gt;
&lt;p&gt;密码是我们保护个人信息、财务数据和隐私的第一道防线。一个强而复杂的密码能够有效地阻止未经授权的访问，防止我们的账户被黑客入侵。然而，随着我们在线账户的数量不断增加，记住所有复杂的密码变得几乎不可能。这时，许多人选择使用简单的、易记的密码，甚至在多个网站上使用相同的密码，这种做法却大大增加了密码被盗的风险。&lt;/p&gt;
&lt;h3 id=&#34;密码泄露的普遍性&#34;&gt;密码泄露的普遍性&lt;/h3&gt;
&lt;p&gt;近年来，密码泄露事件频繁发生，成千上万的用户信息被曝光在互联网上。数据泄露不仅让用户的个人信息面临风险，还可能导致财产损失和隐私侵犯。例如，大型公司如Yahoo、Equifax、LinkedIn等都曾发生过严重的数据泄露事件，影响了数亿用户。数据显示，黑客每天都在进行成千上万次的攻击，试图通过各种方式获取用户的密码信息。&lt;/p&gt;
&lt;h3 id=&#34;密码泄露的危害&#34;&gt;密码泄露的危害&lt;/h3&gt;
&lt;p&gt;密码泄露可能带来多方面的危害，具体如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;身份盗窃&lt;/strong&gt;：黑客可以利用泄露的密码登录您的个人账户，获取您的个人信息，甚至冒用您的身份进行违法活动。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;财产损失&lt;/strong&gt;：如果您的银行账户或支付平台的密码被泄露，黑客可能会窃取您的资金，导致直接的经济损失。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;隐私侵犯&lt;/strong&gt;：密码泄露可能导致您的私人邮件、照片、社交媒体信息等被曝光，严重侵犯您的隐私。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;业务损失&lt;/strong&gt;：对于企业用户来说，密码泄露可能导致商业秘密和客户信息被盗，带来巨大的经济损失和声誉损害。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;恶意操作&lt;/strong&gt;：黑客可能利用您的账户发送垃圾邮件、散布恶意软件，甚至进行更大范围的网络攻击。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;黑客攻击的泛滥&#34;&gt;黑客攻击的泛滥&lt;/h3&gt;
&lt;p&gt;黑客攻击变得越来越普遍和复杂，他们使用钓鱼邮件、恶意软件、暴力破解等多种手段来窃取密码。一旦一个网站的密码被攻破，黑客们通常会尝试使用相同的密码登录其他网站的账户。如果用户在多个网站上使用相同的密码，黑客就能轻易地侵入更多的账户，造成更大的损失。&lt;/p&gt;
&lt;h3 id=&#34;急切需要密码管理工具&#34;&gt;急切需要密码管理工具&lt;/h3&gt;
&lt;p&gt;鉴于上述情况，强烈推荐使用密码管理工具来保护我们的数字生活。密码管理工具不仅能帮助我们生成强而随机的密码，还能安全地存储和管理这些密码。以下是密码管理工具的主要优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;生成随机密码&lt;/strong&gt;：密码管理工具可以生成复杂且随机的密码，大大增强了密码的安全性，防止黑客轻易破解。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;独特密码&lt;/strong&gt;：每个账户都使用独特的密码，避免一个密码被泄露后，其他账户也随之陷入危险。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动填充&lt;/strong&gt;：简化登录过程，避免因频繁输入密码而增加泄露风险。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多设备同步&lt;/strong&gt;：在所有设备上同步密码，确保随时随地都能安全地访问账户。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用密码管理工具是保护在线账户安全的有效方法，避免密码泄露和黑客攻击带来的风险。接下来，我们将详细介绍几款流行的密码管理工具，帮助您选择最适合自己的安全解决方案。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;各类密码登录方式的差异与演进：从传统密码到生物识别&#34;&gt;各类密码登录方式的差异与演进：从传统密码到生物识别&lt;/h2&gt;
&lt;p&gt;在数字身份认证领域，登录方式正经历从“记忆”到“生物特征”、从“主动输入”到“无感验证”的革新。本文将解析主流登录技术的核心差异与适用场景。&lt;/p&gt;
&lt;h3 id=&#34;1-传统密码登录&#34;&gt;1. &lt;strong&gt;传统密码登录&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原理&lt;/strong&gt;：用户自定义字符串（字母/数字/符号组合）作为身份凭证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：开发成本低、无需硬件支持&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺陷&lt;/strong&gt;：易被撞库攻击、弱密码风险高、需记忆负担&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：备用登录方式、低安全需求系统（如论坛）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-短信验证码登录&#34;&gt;2. &lt;strong&gt;短信验证码登录&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原理&lt;/strong&gt;：通过运营商网络发送动态数字验证码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：无需记忆、可获取用户真实手机号&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺陷&lt;/strong&gt;：存在SIM卡劫持、短信延迟、隐私泄露风险&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全增强&lt;/strong&gt;：5分钟时效限制、每小时发送次数限制&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-生物识别登录&#34;&gt;3. &lt;strong&gt;生物识别登录&lt;/strong&gt;&lt;/h3&gt;
&lt;h4 id=&#34;3-1-人脸识别&#34;&gt;3.1 &lt;strong&gt;人脸识别&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;技术&lt;/strong&gt;：通过摄像头捕捉面部特征点比对&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：无接触、用户体验流畅&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;挑战&lt;/strong&gt;：光线敏感、整容/化妆影响精度&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;3-2-虹膜识别&#34;&gt;3.2 &lt;strong&gt;虹膜识别&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;技术&lt;/strong&gt;：分析虹膜纹理（含266+特征点）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：终身唯一性、误识率低至百万分之一&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;局限&lt;/strong&gt;：设备成本高、需近距离配合&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用&lt;/strong&gt;：军事基地、金融系统等高安全场景&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;4-通行密钥（passkeys）&#34;&gt;4. &lt;strong&gt;通行密钥（Passkeys）&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原理&lt;/strong&gt;：基于公钥加密的端到端验证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;突破性&lt;/strong&gt;：防钓鱼攻击、无密码记忆、跨设备同步&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;现状&lt;/strong&gt;：苹果/谷歌/微软已支持，但普及率不足30%&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;5-第三方登录（oauth）&#34;&gt;5. &lt;strong&gt;第三方登录（OAuth）&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;代表&lt;/strong&gt;：微信/QQ/微博授权&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：一键登录、降低用户流失&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;隐患&lt;/strong&gt;：账号体系依赖外部平台、信息获取受限&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;技术对比矩阵&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方式&lt;/th&gt;
&lt;th&gt;安全性&lt;/th&gt;
&lt;th&gt;便捷性&lt;/th&gt;
&lt;th&gt;开发成本&lt;/th&gt;
&lt;th&gt;隐私风险&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;传统密码&lt;/td&gt;
&lt;td&gt;★★&lt;/td&gt;
&lt;td&gt;★★&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;短信验证码&lt;/td&gt;
&lt;td&gt;★★★&lt;/td&gt;
&lt;td&gt;★★★★&lt;/td&gt;
&lt;td&gt;中&lt;/td&gt;
&lt;td&gt;中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;人脸识别&lt;/td&gt;
&lt;td&gt;★★★★&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;td&gt;中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;虹膜识别&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★&lt;/td&gt;
&lt;td&gt;极高&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;通行密钥&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★&lt;/td&gt;
&lt;td&gt;中高&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;未来趋势&lt;/strong&gt;：多模态认证（如人脸+虹膜）、无密码化（Passkeys）将成为主流。企业在选择方案时需平衡安全与体验——金融场景倾向虹膜/Passkeys，社交娱乐则优先短信/第三方登录。技术的终点，始终是让安全“隐形”于便捷之中。&lt;/p&gt;
&lt;h3 id=&#34;6-二维码认证的崛起&#34;&gt;6. &lt;strong&gt;二维码认证的崛起&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;随着谷歌宣布在Gmail中逐步淘汰短信验证码并转向动态二维码认证，这一技术正成为安全验证的新方向。其核心优势在于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;抗钓鱼攻击&lt;/strong&gt;：无需传输可被截获的数字代码，通过加密的图形验证切断中间人攻击路径&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨平台兼容性&lt;/strong&gt;：仅需设备摄像头扫描即可完成，适配iOS/Android/PC等多终端&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;增强隐私保护&lt;/strong&gt;：验证过程不依赖手机号等个人信息，降低运营商数据泄露风险&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;7-生物识别技术的深层挑战&#34;&gt;7. &lt;strong&gt;生物识别技术的深层挑战&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;尽管指纹、人脸等生物识别技术已广泛应用，其局限性仍不容忽视：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;硬件依赖性&lt;/strong&gt;：3D结构光/ToF模组（苹果、华为、荣耀等厂商采用）虽提升安全性，但也推高设备成本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生物特征不可重置性&lt;/strong&gt;：一旦生物数据泄露，无法像密码一样修改&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;环境敏感性&lt;/strong&gt;：虹膜识别需在30cm内配合，人脸识别受光线/遮挡影响显著&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;8-多模态认证的实践探索&#34;&gt;8. &lt;strong&gt;多模态认证的实践探索&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;为解决单一技术的缺陷，混合认证模式逐渐普及：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;生物特征+设备认证&lt;/strong&gt;：如苹果Face ID配合Apple Watch近场解锁&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动态生物密钥&lt;/strong&gt;：Passkeys将生物验证与硬件加密结合，生成临时密钥替代静态密码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;行为特征补充&lt;/strong&gt;：敲击节奏、滑动轨迹等行为数据增强身份判定&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;技术对比深度解析&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;传统密码&lt;/th&gt;
&lt;th&gt;生物识别&lt;/th&gt;
&lt;th&gt;Passkeys&lt;/th&gt;
&lt;th&gt;二维码认证&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;安全基线&lt;/td&gt;
&lt;td&gt;弱（依赖复杂度）&lt;/td&gt;
&lt;td&gt;中高（硬件依赖）&lt;/td&gt;
&lt;td&gt;极高（非对称加密）&lt;/td&gt;
&lt;td&gt;高（动态加密）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;隐私泄露风险&lt;/td&gt;
&lt;td&gt;极高（撞库攻击）&lt;/td&gt;
&lt;td&gt;中（数据不可逆）&lt;/td&gt;
&lt;td&gt;极低（本地存储）&lt;/td&gt;
&lt;td&gt;低（无身份绑定）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;用户教育成本&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;td&gt;中（需信任机制）&lt;/td&gt;
&lt;td&gt;高（新概念普及）&lt;/td&gt;
&lt;td&gt;低（直观操作）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;部署复杂度&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;td&gt;高（专用硬件）&lt;/td&gt;
&lt;td&gt;中（需FIDO支持）&lt;/td&gt;
&lt;td&gt;中（服务端改造）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&#34;行业应用场景指南&#34;&gt;行业应用场景指南&lt;/h3&gt;
&lt;h4 id=&#34;金融级安全&#34;&gt;金融级安全&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;首选方案&lt;/strong&gt;：虹膜识别（误识率1/1,000,000）+ Passkeys双因素认证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;典型案例&lt;/strong&gt;：银行VIP室采用虹膜门禁，移动支付绑定Passkeys硬件密钥&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;消费电子领域&#34;&gt;消费电子领域&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;平衡方案&lt;/strong&gt;：3D人脸识别（苹果/华为结构光技术）+ 备用密码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;创新实践&lt;/strong&gt;：荣耀Magic系列通过AI学习用户面部微表情提升识别精度&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;企业级服务&#34;&gt;企业级服务&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;合规选择&lt;/strong&gt;：动态二维码+RSA令牌（满足GDPR/CCPA数据隔离要求）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;效率优化&lt;/strong&gt;：微软Azure AD支持Passkeys跨设备同步，降低IT运维成本&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;未来技术演进方向&#34;&gt;未来技术演进方向&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;量子加密融合&lt;/strong&gt;：虹膜特征数据通过量子密钥分发（QKD）传输，防御量子计算攻击&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;边缘计算增强&lt;/strong&gt;：生物特征比对在设备端TEE（可信执行环境）完成，避免云端数据暴露&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;抗伪造技术突破&lt;/strong&gt;：基于GAN生成对抗网络的人脸活体检测，识别精度达99.99%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无感身份验证&lt;/strong&gt;：通过脑电波识别/心跳特征等生理信号实现持续身份认证&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;小结&lt;/strong&gt;&lt;br&gt;
从短信验证码到动态二维码，从指纹识别到虹膜认证，安全与便捷的天平始终在动态调整。当Passkeys开始解构传统密码体系，当3D结构光重新定义人脸识别边界，我们正在见证一个&amp;quot;无感安全&amp;quot;时代的黎明。技术的终极形态，或许正如科幻作家威廉·吉布森所言：“未来已来，只是分布不均。”&lt;/p&gt;
&lt;h2 id=&#34;密码管理工具&#34;&gt;密码管理工具&lt;/h2&gt;
&lt;p&gt;在当今数字化时代，密码安全变得尤为重要。随着我们在网上创建的账户越来越多，记住所有复杂的密码变得几乎不可能。这时候，密码管理工具就显得尤为重要了。本文将介绍几款流行的密码管理工具，帮助您保护数字生活。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h3 id=&#34;lastpass&#34;&gt;LastPass&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;LastPass&lt;/strong&gt; 是一款广受欢迎的密码管理工具，其特点在于简单易用且功能强大。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;主要功能&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;密码生成&lt;/strong&gt;：生成强密码，避免使用简单或重复的密码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动填充&lt;/strong&gt;：自动填充网页和应用程序中的登录信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多设备同步&lt;/strong&gt;：在所有设备之间同步密码数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全共享&lt;/strong&gt;：安全地与他人共享密码。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用体验&lt;/strong&gt;： LastPass 提供了友好的用户界面，易于设置和使用。其浏览器扩展和移动应用使得管理密码变得方便快捷。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;官网链接&lt;/strong&gt;：&lt;a href=&#34;https://www.lastpass.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;LastPass 官网&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;工具下载&lt;/strong&gt;：&lt;a href=&#34;https://www.lastpass.com/download&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;LastPass 下载&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;bitwarden&#34;&gt;Bitwarden&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Bitwarden&lt;/strong&gt; 是另一款备受好评的密码管理工具，以其开源和安全性著称。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;主要功能&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;开源&lt;/strong&gt;：代码公开，透明度高，安全性有保障。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多平台支持&lt;/strong&gt;：支持 Windows、macOS、Linux、iOS 和 Android 等多种操作系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;端到端加密&lt;/strong&gt;：确保数据在传输和存储过程中始终加密。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自托管&lt;/strong&gt;：提供自托管选项，增强数据控制力。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用体验&lt;/strong&gt;： Bitwarden 提供了丰富的功能和高度的可定制性，适合那些对安全性和隐私有较高要求的用户。其免费版本已经足够强大，付费版本提供了更多高级功能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;官网链接&lt;/strong&gt;：&lt;a href=&#34;https://bitwarden.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Bitwarden 官网&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;工具下载&lt;/strong&gt;：&lt;a href=&#34;https://bitwarden.com/download/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Bitwarden 下载&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;1password&#34;&gt;1Password&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1Password&lt;/strong&gt; 是一款以其优雅设计和强大功能而著称的密码管理工具。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;主要功能&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;密码生成和存储&lt;/strong&gt;：生成和存储强密码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;旅行模式&lt;/strong&gt;：在旅行时隐藏敏感信息，防止被检查。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多重身份&lt;/strong&gt;：支持管理多个身份和信用卡信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;密码监控&lt;/strong&gt;：监控数据泄露，提醒用户更改密码。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用体验&lt;/strong&gt;： 1Password 提供了极佳的用户体验，界面美观且功能直观。其家人和团队计划非常适合家庭和企业用户。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;官网链接&lt;/strong&gt;：&lt;a href=&#34;https://1password.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;1Password 官网&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;工具下载&lt;/strong&gt;：&lt;a href=&#34;https://1password.com/downloads/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;1Password 下载&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;dashlane&#34;&gt;Dashlane&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Dashlane&lt;/strong&gt; 是一款功能全面的密码管理工具，提供了许多实用的附加功能。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;主要功能&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;密码管理&lt;/strong&gt;：生成、存储和自动填充密码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VPN 服务&lt;/strong&gt;：内置 VPN，增强上网安全性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;密码健康报告&lt;/strong&gt;：分析密码强度，提供改进建议。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;暗网监控&lt;/strong&gt;：监控暗网，及时提醒用户更改被泄露的密码。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用体验&lt;/strong&gt;： Dashlane 提供了全面的安全功能，其内置的 VPN 和密码健康报告使其成为一个一站式安全解决方案。用户界面简洁直观，适合不同水平的用户。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;官网链接&lt;/strong&gt;：&lt;a href=&#34;https://www.dashlane.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Dashlane 官网&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;工具下载&lt;/strong&gt;：&lt;a href=&#34;https://www.dashlane.com/download&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Dashlane 下载&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;keeper&#34;&gt;Keeper&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Keeper&lt;/strong&gt; 是一款注重安全性和隐私的密码管理工具。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;主要功能&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;零知识安全架构&lt;/strong&gt;：确保用户数据的绝对隐私。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文件存储&lt;/strong&gt;：安全存储和共享文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;紧急访问&lt;/strong&gt;：在紧急情况下允许指定联系人访问账户。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生物识别登录&lt;/strong&gt;：支持指纹和面部识别登录。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用体验&lt;/strong&gt;： Keeper 提供了高度安全的环境，适合那些对隐私和数据安全有极高要求的用户。其丰富的功能和灵活的定制选项，使其成为企业和个人用户的理想选择。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;官网链接&lt;/strong&gt;：&lt;a href=&#34;https://www.keepersecurity.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Keeper 官网&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;工具下载&lt;/strong&gt;：&lt;a href=&#34;https://www.keepersecurity.com/download.html&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Keeper 下载&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过这些密码管理工具，您可以轻松管理和保护您的密码，提升数字生活的安全性和便利性。选择适合您的工具，开始安全的在线体验吧！&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;总结&#34;&gt;总结&lt;/h3&gt;
&lt;p&gt;选择合适的密码管理工具取决于您的具体需求和偏好。无论是 LastPass 的易用性，Bitwarden 的开源特性，1Password 的优雅设计，Dashlane 的全面功能，还是 Keeper 的高度安全性，都能为您提供安全可靠的密码管理服务。通过使用这些工具，您可以轻松管理和保护您的密码，提升数字生活的安全性和便利性。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;后面将着重介绍 bitwarden ，因为其免费版本功能限制很少，并且支持 指纹识别 ，客户端全平台支持，并且有服务端的开源实现版本！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;bitwarden-密码安全原理&#34;&gt;Bitwarden 密码安全原理&lt;/h2&gt;
&lt;p&gt;在当前数字化的世界里，保护在线账户的安全变得尤为重要。复杂、独特的密码是防止黑客入侵的关键，但记住所有这些密码却是一个巨大的挑战。Bitwarden 是一款流行且广受好评的密码管理工具，它以安全性和开源特性著称。下面，我们将详细介绍 Bitwarden 如何保管密码以及它为什么如此安全。&lt;/p&gt;
&lt;h3 id=&#34;bitwarden-如何保管密码？&#34;&gt;Bitwarden 如何保管密码？&lt;/h3&gt;
&lt;p&gt;Bitwarden 使用了多层次的安全措施来确保您的密码和数据始终受到保护。以下是 Bitwarden 保管密码的基本原理：&lt;/p&gt;
&lt;h4 id=&#34;数据加密&#34;&gt;数据加密&lt;/h4&gt;
&lt;p&gt;Bitwarden 采用端到端加密来保护您的数据。所有数据在您的设备上加密，并且只有在传输和存储过程中保持加密状态。具体来说，Bitwarden 使用以下加密技术：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AES-256 位加密&lt;/strong&gt;：高级加密标准（AES）是目前公认的最强加密算法之一。256 位加密意味着有 2^256 种可能的密钥组合，几乎无法被破解。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PBKDF2-SHA-256&lt;/strong&gt;：使用密码生成密钥并通过密码基础的密钥派生函数 2（PBKDF2）和 SHA-256 哈希算法反复迭代，以增加破解难度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HMAC-SHA-256&lt;/strong&gt;：用于数据完整性校验，确保数据在传输过程中没有被篡改。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;零知识安全架构&#34;&gt;零知识安全架构&lt;/h4&gt;
&lt;p&gt;Bitwarden 采用零知识安全架构，这意味着即使是 Bitwarden 的开发者和服务器也无法访问您的主密码或解密您的数据。您的主密码永远不会在 Bitwarden 的服务器上存储或传输，只有您自己知道并掌握。&lt;/p&gt;
&lt;h4 id=&#34;端到端加密&#34;&gt;端到端加密&lt;/h4&gt;
&lt;p&gt;所有数据（包括密码、笔记、文件等）在离开您的设备之前就已经加密，只有在您的设备上解密。这种端到端加密保证了即使数据在传输过程中被拦截，拦截者也无法读取内容。&lt;/p&gt;
&lt;h3 id=&#34;bitwarden-为什么安全？&#34;&gt;Bitwarden 为什么安全？&lt;/h3&gt;
&lt;p&gt;Bitwarden 的安全性不仅仅依赖于强大的加密算法，还包括一系列其他的安全措施和最佳实践。&lt;/p&gt;
&lt;h4 id=&#34;开源透明性&#34;&gt;开源透明性&lt;/h4&gt;
&lt;p&gt;Bitwarden 是完全开源的，其源代码公开可见。这意味着任何人都可以审查代码，确保没有隐藏的漏洞或后门。这种透明性增加了用户的信任度，并且允许全球安全社区对其进行持续的安全评估和改进。&lt;/p&gt;
&lt;h4 id=&#34;定期安全审计&#34;&gt;定期安全审计&lt;/h4&gt;
&lt;p&gt;Bitwarden 定期接受独立的第三方安全审计。这些审计包括对应用程序和基础设施的全面安全评估，以确保不存在安全漏洞。审计报告公开发布，进一步增强了用户对 Bitwarden 安全性的信心。&lt;/p&gt;
&lt;h4 id=&#34;强密码要求&#34;&gt;强密码要求&lt;/h4&gt;
&lt;p&gt;Bitwarden 强制要求用户设置强密码，并提供内置的密码生成器，帮助用户创建复杂、独特的密码。这有效减少了由于弱密码导致的账户被破解的风险。&lt;/p&gt;
&lt;h4 id=&#34;多因素认证-mfa&#34;&gt;多因素认证 (MFA)&lt;/h4&gt;
&lt;p&gt;Bitwarden 支持多因素认证，提供额外的安全层。即使攻击者获得了您的主密码，他们也无法在没有第二个认证因素的情况下访问您的账户。Bitwarden 支持多种 MFA 方式，包括 TOTP（基于时间的一次性密码）、U2F（通用第二因素）和 Duo 安全。&lt;/p&gt;
&lt;h4 id=&#34;安全的分享和存储&#34;&gt;安全的分享和存储&lt;/h4&gt;
&lt;p&gt;Bitwarden 允许用户安全地与他人分享密码和其他敏感信息。所有共享的数据同样经过加密，并且只有授权的接收者才能解密和访问。&lt;/p&gt;
&lt;h3 id=&#34;总结-2&#34;&gt;总结&lt;/h3&gt;
&lt;p&gt;Bitwarden 通过强大的加密技术、零知识安全架构、开源透明性、定期安全审计、强密码要求和多因素认证，提供了高度安全的密码管理解决方案。选择 Bitwarden，不仅能帮助您方便地管理所有密码，还能确保您的数字生活免受各种网络威胁的侵扰。无论是个人用户还是企业，Bitwarden 都是一个值得信赖的密码管理工具。&lt;/p&gt;
&lt;h2 id=&#34;密码泄露查询&#34;&gt;密码泄露查询&lt;/h2&gt;
&lt;h3 id=&#34;bitwarden-2&#34;&gt;bitwarden&lt;/h3&gt;
&lt;p&gt;需要付费用户才有查询密码暴露功能&lt;/p&gt;
&lt;h3 id=&#34;vaultwarden&#34;&gt;Vaultwarden&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Vaultwarden  是 bitwarden 的开源服务端实现版本&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859.webp&#34; alt=&#34;Vaultwarden  密码暴露&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-1.webp&#34; alt=&#34;Vaultwarden  检查暴露的密码&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;chrome-密码泄露提示&#34;&gt;chrome 密码泄露提示&lt;/h3&gt;
&lt;p&gt;可能你会碰到谷歌浏览器Chrome提示 某个网站或应用发生了数据泄露导致您的密码外泄.建议修改密码 看到这个提示还是挺吓人. 遇到这个提示不建议关闭弹窗了事. 博主强烈建议尽快修改密码. 原因为你在某个网站上账号和密码很大可能已经被泄露了.为什么挣了说,这可从Chrome怎么检测你密码是否被泄露原理说起.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-2.webp&#34; alt=&#34;Chrome 提示泄露的密码&#34;&gt;&lt;/p&gt;
&lt;p&gt;Chrome检测你密码是否被泄露的实现原理.&lt;/p&gt;
&lt;p&gt;其实这技术很简单,就是将你登录A网站的账号和密码加密后和网上公开已知的泄露数据进行匹配对比,如果发现你的在这份数据中,那就说明你在A网站上的密码已经被泄露了 (更具体技术流程见官方文档&lt;a href=&#34;https://security.googleblog.com/2019/12/better-password-protections-in-chrome.html&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;https://security.googleblog.com/2019/12/better-password-protections-in-chrome.html&lt;/a&gt;)&lt;br&gt;
所以, 综上, 一般我们是不会收到这种告警的.但是有这个告警那就说明你的账号密码已经在网上公开,泄露, 是很危险, 需要马上改密码.&lt;/p&gt;
&lt;h4 id=&#34;关闭方法&#34;&gt;关闭方法&lt;/h4&gt;
&lt;p&gt;设置-&amp;gt;隐私设置和安全性-&amp;gt;安全-&amp;gt;标准保护-&amp;gt;在密码遭遇数据泄露时，向您发出警告关闭此选项即可&lt;/p&gt;
&lt;h2 id=&#34;自建bitwarden&#34;&gt;自建bitwarden&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;免费用户功能虽然不少，但是有开源的服务端实现，可以提供付费bitwarden 的功能，不香吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://hub.docker.com/r/vaultwarden/server&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;https://hub.docker.com/r/vaultwarden/server&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/dani-garcia/vaultwarden&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;https://github.com/dani-garcia/vaultwarden&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-yaml&#34; data-language=&#34;yaml&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;version: &amp;#39;3&amp;#39;
services:
  bitwarden:
    container_name: bitwarden
    hostname: bitwarden
    image: vaultwarden&amp;#x2F;server:latest
    volumes:
      - $&amp;#123;USERDIR&amp;#125;&amp;#x2F;Bitwarden&amp;#x2F;data:&amp;#x2F;data
    env_file:
      - $&amp;#123;USERDIR&amp;#125;&amp;#x2F;Bitwarden&amp;#x2F;config.env
      - .env-server
    environment:
      - PUID&amp;#x3D;$&amp;#123;PUID&amp;#125;
      - PGID&amp;#x3D;$&amp;#123;PGID&amp;#125;
      - TZ&amp;#x3D;$&amp;#123;TZ&amp;#125;
      - WEBSOCKET_ENABLED&amp;#x3D;&amp;quot;true&amp;quot;
      - SIGNUPS_ALLOWED&amp;#x3D;&amp;quot;true&amp;quot;
      - WEB_VAULT_ENABLED&amp;#x3D;&amp;quot;true&amp;quot;
    ports:
      - &amp;quot;9000:80&amp;quot;
    restart: always
    #network_mode: bridge
    networks:
      public_access:
        aliases:
          - bitwarden&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-3.webp&#34; alt=&#34;bitwarden 自建服务器&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;目前 &lt;a href=&#34;https://pass.17lai.site&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;https://pass.17lai.site&lt;/a&gt; 没有关闭注册！&lt;/p&gt;
&lt;p&gt;博文阅读用户可以测试使用，特别是密码暴露查询最为实用！但是博主不保证服务稳定性，可能会清除数据！&lt;/p&gt;
&lt;p&gt;使用它的用户请在本文下面留言，这样博主清理服务器数据的时候可以给你留言，就有邮件通知你！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;bitwarden-使用&#34;&gt;bitwarden 使用&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;带有指纹识别的笔记本，登录pc 客户端之后，浏览器插件直接指纹登录。是最佳使用体验！&lt;/p&gt;
&lt;p&gt;带指纹识别的手机，直接指纹登录app，也是十分的方便。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;bitwarden-pc-客户端&#34;&gt;bitwarden PC 客户端&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-4.webp&#34; alt=&#34;bitwarden客户端&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;bitwarden登录自定义服务器&#34;&gt;bitwarden登录自定义服务器&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-5.webp&#34; alt=&#34;bitwarden登录自定义服务器1&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-6.webp&#34; alt=&#34;bitwarden登录自定义服务器2&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;bitwarden-pc-客户端两步验证&#34;&gt;bitwarden PC 客户端两步验证&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-7.webp&#34; alt=&#34;bitwarden客户端两步验证&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;bitwarden-pc-客户端指纹验证登录&#34;&gt;bitwarden PC 客户端指纹验证登录&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-8.webp&#34; alt=&#34;bitwarden客户端指纹验证&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;bitwarden-浏览器插件&#34;&gt;bitwarden 浏览器插件&lt;/h3&gt;
&lt;h4 id=&#34;浏览器插件指纹验证登录&#34;&gt;浏览器插件指纹验证登录&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-9.webp&#34; alt=&#34;浏览器插件指纹验证登录&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;随机密码生成&#34;&gt;随机密码生成&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;这就是密码管理器最主要的使用功能&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-10.webp&#34; alt=&#34;随机密码生成&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;随机用户名生成&#34;&gt;随机用户名生成&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-11.webp&#34; alt=&#34;随机用户名生成&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;同域名多账号支持&#34;&gt;同域名多账号支持&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-12.webp&#34; alt=&#34;同域名多账号支持&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;相同网站多个域名同一个账号支持&#34;&gt;相同网站多个域名同一个账号支持&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-13.webp&#34; alt=&#34;相同网站多个域名同一个账号支持&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;android-app&#34;&gt;android app&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;由于android有 &lt;mark&gt;防止隐私截屏&lt;/mark&gt;，下面很多功能就没有截图展示了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;支持&lt;mark&gt;指纹登录&lt;/mark&gt;&lt;/li&gt;
&lt;li&gt;支持 android app 密码自动填充&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-14.webp&#34; alt=&#34;bitwarden android app&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-15.webp&#34; alt=&#34;bitwarden android app 密码移动填充&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;密码导入导出&#34;&gt;密码导入导出&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;作为一个密码管理工具，能够完全的导入导出密码是必备的基本功能&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;首先，找到密码库管理入口，见下图&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2025/04/13/20250413152213.webp&#34; alt=&#34;密码库&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;导入密码&#34;&gt;导入密码&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2025/04/13/20250413152213-1.webp&#34; alt=&#34;导入项目&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2025/04/13/20250413152213-2.webp&#34; alt=&#34;导入数据&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;导出密码&#34;&gt;导出密码&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2025/04/13/20250413152213-3.webp&#34; alt=&#34;导出密码库&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2025/04/13/20250413152213-4.webp&#34; alt=&#34;导出密码库2&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;指纹识别&#34;&gt;指纹识别&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;指纹识别是如此的方便，电脑没有自带指纹怎么办？&lt;/p&gt;
&lt;p&gt;有办法！使用外置指纹识别模块！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;windows-hello-usb-模块&#34;&gt;windows hello usb 模块&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;不打广告，就不放购买链接了。下面模块比较适合笔记本插在侧面 usb 接口&lt;/p&gt;
&lt;p&gt;下面模块一百块大洋左右！ 可惜客服说不支持 Linux&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-16.webp&#34; alt=&#34;usb指纹识别模块&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-17.webp&#34; alt=&#34;usb指纹识别模块&#34;&gt;&lt;/p&gt;
&lt;p&gt;台式机 usb 接口在主机上面，日常使用指纹识别如果手摸比较麻烦，可以使用 usb 扩展器！把指纹识别模块插在下面红框中的接口上面！&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-18.webp&#34; alt=&#34;usb 扩展器&#34;&gt;&lt;/p&gt;
&lt;p&gt;不缺钱的用户可以选择下面这种，要大几百块&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/05/31/20240531165859-19.webp&#34; alt=&#34;指纹识别模块&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;密码替代技术&#34;&gt;密码替代技术&lt;/h2&gt;
&lt;p&gt;密码是如此的重要，但人类记忆复杂密码又是如此的困难！值得一提的是，随着技术的发展，一些替代密码的工具和方法正在兴起，以提供更安全和便捷的身份验证方式。以下是一些主要的替代密码的方法：&lt;/p&gt;
&lt;h3 id=&#34;生物识别技术&#34;&gt;生物识别技术&lt;/h3&gt;
&lt;p&gt;生物识别技术利用人体的独特特征来验证身份，提供了一种不需要记忆的安全解决方案。常见的生物识别方法包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;指纹识别&lt;/strong&gt;：通过扫描指纹来确认身份，已广泛应用于智能手机和安全系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;面部识别&lt;/strong&gt;：利用面部特征进行身份验证，广泛应用于智能手机、笔记本电脑和门禁系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;虹膜识别&lt;/strong&gt;：通过扫描虹膜图案进行身份验证，通常用于高安全性的场合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;声纹识别&lt;/strong&gt;：通过分析声音的独特特征来确认身份，应用于电话银行和语音助手。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;多因素认证-mfa-2&#34;&gt;多因素认证 (MFA)&lt;/h3&gt;
&lt;p&gt;多因素认证通过结合多种验证方法来增强安全性。常见的 MFA 方式包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;短信验证码&lt;/strong&gt;：在登录时向用户手机发送一次性验证码，需要同时输入密码和验证码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TOTP 应用&lt;/strong&gt;：使用基于时间的一次性密码（如 Google Authenticator 和 Authy）生成动态验证码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;硬件令牌&lt;/strong&gt;：如 YubiKey，通过物理设备生成动态验证码或直接进行认证。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;推送通知&lt;/strong&gt;：通过认证应用向用户设备发送推送通知，用户确认后完成登录。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;无密码登录&#34;&gt;无密码登录&lt;/h3&gt;
&lt;p&gt;无密码登录是一种不需要传统密码的身份验证方式，常见的方法包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;魔法链接&lt;/strong&gt;：用户在登录时收到包含一次性登录链接的电子邮件，点击链接即可登录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一次性密码 (OTP)&lt;/strong&gt;：通过短信或电子邮件发送一次性密码，用户输入该密码完成登录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;基于公共密钥的认证 (FIDO2)&lt;/strong&gt;：利用设备上的安全密钥对进行认证，如 Windows Hello 和 Apple Face ID。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;行为识别&#34;&gt;行为识别&lt;/h3&gt;
&lt;p&gt;行为识别通过分析用户的行为模式进行身份验证，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;打字节律&lt;/strong&gt;：通过检测用户打字的速度和节奏来确认身份。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;鼠标行为&lt;/strong&gt;：分析用户使用鼠标的方式，如点击和移动模式。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;手机传感器数据&lt;/strong&gt;：利用加速度计和陀螺仪数据分析用户的手机使用习惯。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;智能卡和身份认证芯片&#34;&gt;智能卡和身份认证芯片&lt;/h3&gt;
&lt;p&gt;智能卡和身份认证芯片通过物理卡片或内置芯片进行身份验证，常用于企业和政府机构。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;智能卡&lt;/strong&gt;：包含加密芯片的卡片，用于访问控制和身份验证。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内置芯片&lt;/strong&gt;：如 NFC 芯片，常用于移动支付和门禁系统。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;总结-3&#34;&gt;总结&lt;/h3&gt;
&lt;p&gt;随着技术的进步，越来越多的替代密码的方法应运而生，这些工具和方法不仅提升了安全性，还大大简化了用户的身份验证过程。选择适合自己的替代密码方案，可以有效减少密码管理的负担，提升数字生活的安全性和便利性。&lt;/p&gt;
&lt;p&gt;为了密码安全，一定要做到一下几点！&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;一定要一个网站一个随机密码，密码不要多网站共用！&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一定要使用合适的密码管理工具，人类的大脑没办法记忆多个随机密码！&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一定不要把密码管理工具的主密码写在任何地方，也一定不要忘记它&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;尽量使用多步验证，这样即使密码泄露黑客也无法登录攻击&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;参考-致谢&#34;&gt;参考&amp;amp;致谢&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;bitwarden&lt;/li&gt;
&lt;li&gt;chatGPT&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;bitwarden 功能十分强大，上面介绍了不少有趣又实用的用法，大家有什么实用补充没有？&lt;/p&gt;
&lt;p&gt;如果谁想自建服务器，可以联系博主收费技术支持！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;参考-致谢-2&#34;&gt;参考&amp;amp;致谢&lt;/h2&gt;
&lt;h2 id=&#34;系列教程&#34;&gt;&lt;strong&gt;系列教程&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;全部文章RSS订阅&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;web系列&#34;&gt;&lt;strong&gt;web系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/web/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;&lt;strong&gt;Web 分类 RSS 订阅&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/69a052c9/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;浏览器的渲染过程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/656a0abb/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;在浏览器输入 URL 回车之后发生了什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/448f849b/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;1.5 万字 CSS 基础拾遗（核心知识、常见需求）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/80906b88/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;http 错误代码表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/8f873c0f/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;写了些浏览器增强脚本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1d87ed2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;如何防止密码泄露，保护密码安全？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/ec2cad2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ESNI和加密DNS - 保护信息隐私的最后一块拼图&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/68d3867d/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;node 项目从构建到使用 jenkins + docker + nginx + mysql + redis 自动化部署&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1991789c/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Github Pages + jekyll 全面介绍极简搭建个人网站和博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css&#34;&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/d3@7&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-view@0.18.10&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10&#34;&gt;&lt;/script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;/css/markmap.css&#34;&gt;

&lt;script src=&#34;/js/markmap.js&#34;&gt;&lt;/script&gt;
&lt;script data-pjax src=&#34;/lib/hbe.54b2b4c40b.js&#34;&gt;&lt;/script&gt;&lt;link href=&#34;/css/hbe-style.css&#34; rel=&#34;stylesheet&#34; type=&#34;text/css&#34;&gt;</content>
        <category term="安全" />
        <category term="web" />
        <category term="密码" />
        <category term="bitwarden" />
        <updated>2024-05-31T06:42:11.000Z</updated>
    </entry>
    <entry>
        <id>https://blog.17lai.site/posts/eb731135/</id>
        <title>webhook部署Hexo静态博客指南</title>
        <link rel="alternate" href="https://blog.17lai.site/posts/eb731135/"/>
        <content type="html">&lt;blockquote&gt;
&lt;p&gt;webhook是一种可以让人远程调用的RPC指令集，可以实现更新github仓库后自动更新部署你的Hexo博客，当然几乎所有的你能想到的东西都能做，本文将详细讲解webhook自动化部署静态博客方法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!-- more --&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/adnanh/webhook&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;adnanh/webhook&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;上面这个仓库支持在你的VPS 快速建立一个webhook 接口。然后利用 github webhook 通知你的 VPS 来拉取最新的代码部署&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对于 webhook 来说，可以自己写一个脚本来接受信息，思路很简单，运行一个 &lt;code&gt;HTTP Server&lt;/code&gt;，监听服务器的某个端口，如果有消息传递过来，那么就运行事先写好的脚本，来完成 webhook 的功能即可。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/04/24/20240424000916.webp&#34; alt=&#34;webhook流程&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;安装-webhook&#34;&gt;安装 webhook&lt;/h2&gt;
&lt;p&gt;这里推荐三种安装方法,使用其中一种方法即可，&lt;strong&gt;github 下载速度可能很慢&lt;/strong&gt;，推荐使用系统源安装的方式&lt;/p&gt;
&lt;p&gt;法一：使用系统源的安装方式&lt;/p&gt;
&lt;p&gt;如果是 Ubuntu 系统，可以直接使用下面的命令进行安装：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ sudo apt-get update
$ sudo apt-get install webhook&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;法二：使用 github 下载&lt;/p&gt;
&lt;p&gt;webhook 的 &lt;a href=&#34;https://github.com/adnanh/webhook/releases&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;release 链接&lt;/a&gt;，找到对应的选项 &lt;code&gt;webhook-linux-amd64.tar.gz&lt;/code&gt; 右键复制链接 ，当前最新版本链接为  &lt;a href=&#34;https://github.com/adnanh/webhook/releases/tag/2.8.1&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;# 下载对应的软件
$ wget https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;adnanh&amp;#x2F;webhook&amp;#x2F;releases&amp;#x2F;download&amp;#x2F;2.8.1&amp;#x2F;webhook-linux-amd64.tar.gz
# 解压进入
$ tar -zxf tar -zxf webhook-linux-amd64.tar.gz &amp;amp;&amp;amp; cd webhook-linux-amd64&amp;#x2F;
$ .&amp;#x2F;webhook&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;法三：Golang 环境下安装&lt;/p&gt;
&lt;p&gt;首先安装 Golang 环境（这里略过），然后安装 webhook，&lt;span class=&#34;label label-info&#34;&gt;这里注意需要已经设置了 GOPATH&lt;/span&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;# 安装对应的工具
$ go get github.com&amp;#x2F;adnanh&amp;#x2F;webhook
# 安装完成之后可以在 $GOPATH&amp;#x2F;bin 下找到对应的执行文件
$ echo $GOPATH
&amp;#x2F;root&amp;#x2F;go
$ ls &amp;#x2F;root&amp;#x2F;go&amp;#x2F;bin
webhook
# 将路径写入到 shell 可以直接执行
$ vi ~&amp;#x2F;.bash_profile
# 在文件最后一行加入对应内容
export PATH&amp;#x3D;&amp;quot;&amp;#x2F;root&amp;#x2F;go&amp;#x2F;bin:$PATH&amp;quot;
# 更新文件使其生效
$ source ~&amp;#x2F;.bash_profile
# 测试命令是否已经成功安装，得到输出说明安装完成
$ webhook&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;配置-webhook&#34;&gt;配置 webhook&lt;/h2&gt;
&lt;p&gt;我们的 hooks.json 文件现在看起来是这样的：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-json&#34; data-language=&#34;json&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-json&#34;&gt;[
  &amp;#123;
    &amp;quot;id&amp;quot;: &amp;quot;redeploy-webhook&amp;quot;,
    &amp;quot;execute-command&amp;quot;: &amp;quot;&amp;#x2F;var&amp;#x2F;scripts&amp;#x2F;redeploy.sh&amp;quot;,
    &amp;quot;command-working-directory&amp;quot;: &amp;quot;&amp;#x2F;var&amp;#x2F;webhook&amp;quot;
  &amp;#125;
]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;注意：如果您更喜欢 YAML 格式，等价的 hooks.yaml 文件如下：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-yaml&#34; data-language=&#34;yaml&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;- id: redeploy-webhook
  execute-command: &amp;quot;&amp;#x2F;var&amp;#x2F;scripts&amp;#x2F;redeploy.sh&amp;quot;
  command-working-directory: &amp;quot;&amp;#x2F;var&amp;#x2F;webhook&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;您现在可以使用以下命令运行 webhook：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;$ &amp;#x2F;path&amp;#x2F;to&amp;#x2F;webhook -hooks hooks.json -verbose&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;它将在默认端口 9000 上启动，并为您提供一个 HTTP 终端：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;http:&amp;#x2F;&amp;#x2F;yourserver:9000&amp;#x2F;hooks&amp;#x2F;redeploy-webhook&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h3 id=&#34;更多配置参数&#34;&gt;更多配置参数&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/adnanh/webhook/blob/master/docs/Webhook-Parameters.md&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;webhook/docs/Webhook-Parameters.md at master&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;Usage of webhook:
  -cert string
        path to the HTTPS certificate pem file (default &amp;quot;cert.pem&amp;quot;)
  -cipher-suites string
        comma-separated list of supported TLS cipher suites
  -debug
        show debug output
  -header value
        response header to return, specified in format name&amp;#x3D;value, use multiple times to set multiple headers
  -hooks value
        path to the json file containing defined hooks the webhook should serve, use multiple times to load from different files
  -hotreload
        watch hooks file for changes and reload them automatically
  -http-methods string
        globally restrict allowed HTTP methods; separate methods with comma
  -ip string
        ip the webhook should serve hooks on (default &amp;quot;0.0.0.0&amp;quot;)
  -key string
        path to the HTTPS certificate private key pem file (default &amp;quot;key.pem&amp;quot;)
  -list-cipher-suites
        list available TLS cipher suites
  -logfile string
        send log output to a file; implicitly enables verbose logging
  -nopanic
        do not panic if hooks cannot be loaded when webhook is not running in verbose mode
  -pidfile string
        create PID file at the given path
  -port int
        port the webhook should serve hooks on (default 9000)
  -secure
        use HTTPS instead of HTTP
  -setgid int
        set group ID after opening listening port; must be used with setuid
  -setuid int
        set user ID after opening listening port; must be used with setgid
  -template
        parse hooks file as a Go template
  -tls-min-version string
        minimum TLS version (1.0, 1.1, 1.2, 1.3) (default &amp;quot;1.2&amp;quot;)
  -urlprefix string
        url prefix to use for served hooks (protocol:&amp;#x2F;&amp;#x2F;yourserver:port&amp;#x2F;PREFIX&amp;#x2F;:hook-id) (default &amp;quot;hooks&amp;quot;)
  -verbose
        show verbose output
  -version
        display webhook version and quit
  -x-request-id
        use X-Request-Id header, if present, as request ID
  -x-request-id-limit int
        truncate X-Request-Id header to limit; default no limit&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;实战&#34;&gt;实战&lt;/h2&gt;
&lt;h3 id=&#34;创建各种配置文件&#34;&gt;创建各种配置文件&lt;/h3&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;# pwd
&amp;#x2F;www&amp;#x2F;docker&amp;#x2F;webhook

# tree .
.
├── bin
│   └── webhook
├── xxx
└── webhook-linux-amd64.tar.gz

2 directories, 2 files&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;利用 uuidgen 生成随机 ID&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;# uuidgen
ecbfc10e-6b2d-4d32-98da-bdd2c3c29fc7&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;hooks.yaml&lt;/code&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-yaml&#34; data-language=&#34;yaml&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;- id: redeploy-hblog-xxxxx-7e86-4f24-8667-23xxxxxxx1
  execute-command: &amp;quot;&amp;#x2F;www&amp;#x2F;docker&amp;#x2F;webhook&amp;#x2F;github-webhooks&amp;#x2F;depHblog.sh&amp;quot;
  command-working-directory: &amp;quot;&amp;#x2F;www&amp;#x2F;wwwroot&amp;#x2F;blog.17lai.site&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;depHblog.sh&lt;/code&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;#!&amp;#x2F;bin&amp;#x2F;sh

# 创建临时目录
temp_dir&amp;#x3D;&amp;#39;&amp;#x2F;www&amp;#x2F;docker&amp;#x2F;webhook&amp;#x2F;tmp&amp;#x2F;hexoblog&amp;#39;
dst_dir&amp;#x3D;&amp;#39;&amp;#x2F;www&amp;#x2F;wwwroot&amp;#x2F;blog.17lai.site&amp;#39;
repo_url&amp;#x3D;&amp;#39;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;appotry&amp;#x2F;hexo.git&amp;#39;
data_dir&amp;#x3D;&amp;#39;&amp;#39;

# 在这里进行你的操作，例如复制文件、下载内容等


# 检查目标目录是否存在 .git 目录
if [ -d &amp;quot;$temp_dir&amp;#x2F;.git&amp;quot; ]; then
    # 如果存在 .git 目录，则执行 git pull 命令进行更新
    echo &amp;quot;Repository already cloned. Performing git pull...&amp;quot;
    cd &amp;quot;$temp_dir&amp;quot;
    git pull origin main  # 如果使用了默认的主分支名字为 main
    git reset --hard origin&amp;#x2F;main
else
    # 如果不存在 .git 目录，则执行 git clone 命令进行克隆
    rm $temp_dir -rf
    echo &amp;quot;Cloning repository into $temp_dir...&amp;quot;
    git clone --progress -v --depth 1 &amp;quot;$repo_url&amp;quot; &amp;quot;$temp_dir&amp;quot;
fi


echo &amp;quot;rsync 目录同步&amp;quot;

# -a：表示以归档模式同步，保留文件的所有属性，包括权限、时间戳等。
# -v：表示详细模式，显示同步过程中的详细信息。
# --delete：表示删除目标目录中源目录没有的文件。
rsync --delete -r --exclude&amp;#x3D;&amp;#39;.git&amp;#39; --link-dest&amp;#x3D;&amp;quot;$dst_dir&amp;quot; $&amp;#123;temp_dir&amp;#125;&amp;#x2F;$&amp;#123;data_dir&amp;#125; $&amp;#123;dst_dir&amp;#125;

echo &amp;quot;设置文件权限&amp;quot;
chown www:www $&amp;#123;dst_dir&amp;#125;&amp;#x2F;$&amp;#123;data_dir&amp;#125;  -R

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;vim depHblog.sh
chmod +x depHblog.sh
curl -X POST https:&amp;#x2F;&amp;#x2F;xxxx.17lai.site&amp;#x2F;hooks&amp;#x2F;redeploy-hblog-xxxx8fa-7e86-4f24-8667-23axxxx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;# 运行测试
su root

&amp;#x2F;www&amp;#x2F;docker&amp;#x2F;webhook&amp;#x2F;bin&amp;#x2F;webhook -hooks &amp;#x2F;www&amp;#x2F;docker&amp;#x2F;webhook&amp;#x2F;github-webhooks&amp;#x2F;hooks.json -verbose -port &amp;lt;通信端口&amp;gt;

&amp;#x2F;www&amp;#x2F;docker&amp;#x2F;webhook&amp;#x2F;bin&amp;#x2F;webhook -hooks &amp;#x2F;www&amp;#x2F;docker&amp;#x2F;webhook&amp;#x2F;github-webhooks&amp;#x2F;hooks.yaml -verbose -port 18xxx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h3 id=&#34;后台运行&#34;&gt;后台运行&lt;/h3&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;nohup webhook -hooks &amp;#x2F;usr&amp;#x2F;local&amp;#x2F;bin&amp;#x2F;github-webhooks&amp;#x2F;hooks.json -verbose -port &amp;lt;通信端口&amp;gt; &amp;amp;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h3 id=&#34;反向代理&#34;&gt;反向代理&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;可以直接端口运行，当然上SSL 安全性更高！&lt;/p&gt;
&lt;p&gt;这里使用 反向代理 复用443&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;宝塔面板 go 项目&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/04/14/20240414210809.webp&#34; alt=&#34;go project webhook&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;调用测试&#34;&gt;调用测试&lt;/h3&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-bash&#34; data-language=&#34;bash&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;curl -X POST https:&amp;#x2F;&amp;#x2F;xxx.17lai.site&amp;#x2F;hooks&amp;#x2F;redeploy-hblog-xxxa-7e86-4f24-8667-2xxxx3xx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h3 id=&#34;关联github仓库&#34;&gt;关联github仓库&lt;/h3&gt;
&lt;p&gt;上面命令测试成功之后就可以正式和 github 仓库关联起来了&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/04/14/20240414210809-1.webp&#34; alt=&#34;github webhooks&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;后话&#34;&gt;后话&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;webhook 实际应用非常强大，只要你有想象力，几乎无所不能！&lt;/p&gt;
&lt;p&gt;本文也在 &lt;a href=&#34;https://blog.17lai.site/posts/5311b619/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;hexo博客增强部署篇&lt;/a&gt; 一文中有提到。&lt;a href=&#34;https://blog.17lai.site/categories/hexo/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;hexo 博客&lt;/a&gt; 系列文章越写越长，有兴趣可以再去看看，更新了很多东西了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;参考-致谢&#34;&gt;参考&amp;amp;致谢&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/adnanh/webhook&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;adnanh/webhook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;系列教程&#34;&gt;&lt;strong&gt;系列教程&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;全部文章RSS订阅&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;hexo系列&#34;&gt;&lt;strong&gt;Hexo系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/hexo/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;&lt;strong&gt;HexoRSS分类订阅&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[十万字图文教程]基于Hexo的matery主题搭建博客并深度优化完全一站式教程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/40300608/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Hexo Docker环境与Hexo基础配置篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/4d8a0b22/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;hexo博客自定义修改篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/9b056c86/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;hexo博客网络优化篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/5311b619/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;hexo博客增强部署篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/4a2050e2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;hexo博客个性定制篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/84b4059a/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;hexo博客常见问题篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/253706ff/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;hexo博客博文撰写篇之完美笔记大攻略终极完全版&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/cf0f47fd/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Hexo Markdown以及各种插件功能测试&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;markdown 各种其它语法插件，latex公式支持，mermaid图表，plant uml图表，URL卡片，bilibili卡片，github卡片，豆瓣卡片，插入音乐和视频，插入脑图，插入PDF，嵌入iframe&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/217ccdc1/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;在 Hexo 博客中插入 ECharts 动态图表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/546887ac/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;使用nodeppt给hexo博客嵌入PPT演示&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/a3c81cc3/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;GithubProfile美化与自动获取RSS文章教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/e922fac8/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Vercel部署高级用法教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/eb731135/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;webhook部署Hexo静态博客指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/8f9792ab/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;在宝塔VPS上面采用docker部署waline全流程图解教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/843eb2k9/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;自建Umami访问统计服务并统计静态博客UV/PV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;笔记系列&#34;&gt;&lt;strong&gt;笔记系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/note/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;&lt;strong&gt;Note分类RSS订阅&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/a8535f26/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;完美笔记进化论&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/253706ff/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;hexo博客博文撰写篇之完美笔记大攻略终极完全版&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/e6086437/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Joplin入门指南&amp;amp;实践方案&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/45f878cd/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;替代Evernote免费开源笔记Joplin-网盘同步笔记历史版本Markdown可视化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/92d347d6/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Joplin 插件以及其Markdown语法。All in One!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/e3ee7f8b/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Joplin 插件使用推荐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1802a8a7/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;为知笔记私有化Docker部署&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;gitbook使用系列&#34;&gt;&lt;strong&gt;Gitbook使用系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/gitbook/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;Gitbook分类RSS订阅&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/7fe86002/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;GitBook+GitLab撰写发布技术文档-Part1:GitBook篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/7790e989/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;GitBook+GitLab撰写发布技术文档-Part2:GitLab篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/d6bad1e5/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;自己动手制作电子书的最佳方式（支持PDF、ePub、mobi等格式）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;gitlab-使用系列&#34;&gt;&lt;strong&gt;Gitlab 使用系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/gitlab/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;&lt;strong&gt;Gitlab RSS 分类订阅&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/acc13b70/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;strong&gt;Gitlab的安装及使用教程完全版&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/29a820b3/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;破解Gitlab EE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/d08eb7b/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Gitlab的安装及使用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1879721e/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;CI/CD与Git Flow与GitLab&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css&#34;&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/d3@7&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-view@0.18.10&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10&#34;&gt;&lt;/script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;/css/markmap.css&#34;&gt;

&lt;script src=&#34;/js/markmap.js&#34;&gt;&lt;/script&gt;
</content>
        <category term="web" />
        <category term="hexo" />
        <category term="webhook" />
        <updated>2024-04-23T16:02:06.000Z</updated>
    </entry>
    <entry>
        <id>https://blog.17lai.site/posts/8f873c0f/</id>
        <title>写了些浏览器增强脚本</title>
        <link rel="alternate" href="https://blog.17lai.site/posts/8f873c0f/"/>
        <content type="html">&lt;blockquote&gt;
&lt;p&gt;平时创造写代码的时候，写了一些辅助工具，可以极大的提高工作效率！浏览器配合 &lt;a href=&#34;https://www.tampermonkey.net/index.php&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;tampermonkey&lt;/a&gt; 使用，真神器也！现在有繁简转换外挂，CSDN zhihu创作助手！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;jstools&#34;&gt;JsTools&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;一些有用的 浏览器脚本&lt;/p&gt;
&lt;p&gt;自用，以及给所有创作者们！&lt;/p&gt;
&lt;p&gt;有什么建议可以留言或者提交 &lt;a href=&#34;https://github.com/appotry/JsTools/issues&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;issue&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;仓库地址 &lt;a href=&#34;https://github.com/appotry/JsTools&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;作者 &lt;a href=&#34;https://blog.17lai.site/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;夜法之书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;脚本列表&#34;&gt;脚本列表&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/appotry/JsTools/blob/main/%E7%B9%81%E7%AE%80%E8%BD%AC%E6%8D%A2/README.MD&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;网页繁简转换&lt;/a&gt; 给所有网页添加繁简转换功能！&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/appotry/JsTools/blob/main/forWriter/README.MD&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;zhihu 创作助手&lt;/a&gt; 1，去掉知乎链接跳转，还原原始链接。 2，去掉知乎关键字搜索&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/appotry/JsTools/blob/main/forWriter/README.MD&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;CSDN 创作助手&lt;/a&gt; 只需轻轻一点，就去掉 CSDN 烦人的关键字搜索&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/appotry/JsTools/blob/main/forWriter/README.MD&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;juejin 创作助手&lt;/a&gt;  1，去掉知乎链接跳转，还原原始链接。2，去掉 复制代码 文字&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;tampermonkey-安装和使用&#34;&gt;Tampermonkey 安装和使用&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://www.tampermonkey.net/index.php&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tampermonkey&lt;/a&gt; 是一款免费的浏览器扩展和最出名的用户脚本管理器,它可以让你在浏览器中自定义网页的行为和外观。通过安装 &lt;a href=&#34;https://www.tampermonkey.net/index.php&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tampermonkey&lt;/a&gt;,你可以使用来自世界各地的开发者提供的上千个脚本,也可以创建和运行自己的脚本。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;🌐在-chrome-上安装-tampermonkey&#34;&gt;🌐在 Chrome 上安装 Tampermonkey&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;打开 Chrome 浏览器。&lt;/li&gt;
&lt;li&gt;访问 &lt;a href=&#34;https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tampermonkey Chrome Web 商店页面&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;点击“添加至 Chrome”按钮，然后再次点击“添加扩展程序”确认安装。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;🦊在-firefox-上安装-tampermonkey&#34;&gt;🦊在 Firefox 上安装 Tampermonkey&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;打开 Firefox 浏览器。&lt;/li&gt;
&lt;li&gt;访问 &lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tampermonkey Firefox Add-ons 页面&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;点击“Add to Firefox”按钮，然后再次点击“Add”确认安装。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;📱-移动设备&#34;&gt;📱 移动设备&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.tampermonkey.net/index.php&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tampermonkey&lt;/a&gt; 也可以在移动浏览器上使用:&lt;/p&gt;
&lt;h4 id=&#34;android&#34;&gt;Android&lt;/h4&gt;
&lt;p&gt;安装 &lt;a href=&#34;https://www.tampermonkey.net/index.php&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tampermonkey&lt;/a&gt; 辅助应用在你的 Android 浏览器中启用 &lt;a href=&#34;https://www.tampermonkey.net/index.php&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tampermonkey&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&#34;ios&#34;&gt;iOS&lt;/h4&gt;
&lt;p&gt;安装支持 &lt;a href=&#34;https://www.tampermonkey.net/index.php&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tampermonkey&lt;/a&gt; 的第三方浏览器,如 Dolphin&lt;/p&gt;
&lt;h3 id=&#34;在其他浏览器上安装-tampermonkey&#34;&gt;在其他浏览器上安装 Tampermonkey&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;您可以在其他支持 &lt;a href=&#34;https://www.tampermonkey.net/index.php&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tampermonkey&lt;/a&gt; 的浏览器上，如 Edge 或 Safari 上查找适用于 Tampermonkey 的扩展程序，并按照它们的指示进行安装。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;安装用户脚本插件&#34;&gt;安装用户脚本插件&lt;/h3&gt;
&lt;p&gt;安装完 &lt;a href=&#34;https://www.tampermonkey.net/index.php&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tampermonkey&lt;/a&gt; 后,你可以安装各种用户脚本来扩展浏览器功能了。在任何提供用户脚本的网站上,点击&amp;quot;安装&amp;quot;按钮即可自动将脚本添加到 Tampermonkey 中。如果网页上没有&amp;quot;安装&amp;quot;按钮,你也可以通过复制脚本代码并在 Tampermonkey 的&amp;quot;添加新脚本&amp;quot;中粘贴代码来手动安装。&lt;/p&gt;
&lt;p&gt;就是这样!现在你已经可以体验由全球开发者提供的各种有用的用户脚本,或者自己动手创建脚本来定制网页体验了。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开您想要安装的用户脚本插件的下载链接。&lt;/li&gt;
&lt;li&gt;Tampermonkey 会检测到页面上的用户脚本，并显示一个安装提示框。&lt;/li&gt;
&lt;li&gt;点击安装按钮，Tampermonkey 将会自动安装用户脚本。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;网页繁简转换外挂&#34;&gt;网页繁简转换外挂&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;给所有网页添加繁简转换功能&lt;/strong&gt;！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;经常访问到一些没有自带中文繁简转换的网站，虽然也认识，但是阅读感要累很多！&lt;/p&gt;
&lt;p&gt;由于最近一直在更新修改 &lt;a href=&#34;https://blog.17lai.site/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Hexo主题&lt;/a&gt;，对 JS 熟悉了不少，如果就写了这个&lt;/p&gt;
&lt;p&gt;中文简体用户 请安装 &lt;a href=&#34;https://raw.githubusercontent.com/appotry/JsTools/main/%E7%B9%81%E7%AE%80%E8%BD%AC%E6%8D%A2/translateCN.js&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Github&lt;/a&gt; &lt;a href=&#34;https://greasyfork.org/zh-CN/scripts/492934-%E7%B9%81%E7%AE%80%E8%BD%AC%E6%8D%A2&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;greasyfork&lt;/a&gt;&lt;br&gt;
中文繁体用户 请安装 &lt;a href=&#34;https://raw.githubusercontent.com/appotry/JsTools/main/%E7%B9%81%E7%AE%80%E8%BD%AC%E6%8D%A2/translateTW.js&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Github&lt;/a&gt; &lt;a href=&#34;https://greasyfork.org/zh-CN/scripts/492935-%E7%B9%81%E7%AE%80%E8%BD%AC%E6%8D%A2&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;greasyfork&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/04/19/20240419225358.webp&#34; alt=&#34;baidu translate&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;csdn&#34;&gt;CSDN&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;只需轻轻一点，就去掉 CSDN 烦人的关键字搜索&lt;/p&gt;
&lt;p&gt;一次去不干净就多点击几次&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/04/19/20240419225358-1.webp&#34; alt=&#34;csdn&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/04/19/20240419225358-2.webp&#34; alt=&#34;csdn&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;zhihu&#34;&gt;zhihu&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1，去掉知乎链接跳转，还原原始链接,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2，去掉知乎关键字搜索&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/04/20/20240420111248.webp&#34; alt=&#34;zhihu&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/04/19/20240419225358-3.webp&#34; alt=&#34;zhihu&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;juejin&#34;&gt;juejin&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1，去掉知乎链接跳转，还原原始链接,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2，去掉 复制代码 文字&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/04/21/20240421130124.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2024/04/21/20240421130124-1.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;关于作者&#34;&gt;关于作者&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;作者的博客 &lt;a href=&#34;https://blog.17lai.site/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;夜法之书&lt;/a&gt; 欢迎光临&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;license&#34;&gt;License&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;GPL V3&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;参考-致谢&#34;&gt;参考&amp;amp;致谢&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;chatGPT 真神器，编码过程中帮助非常大。&lt;/p&gt;
&lt;p&gt;无论是日常生活，还是编码，或者创作，效率提升10倍不止。不过 chatGPT 一本正经的胡说八道不能给准确的建议，真正准确的判断还是需要行业领域专家才行！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;参考-致谢-2&#34;&gt;参考&amp;amp;致谢&lt;/h2&gt;
&lt;h2 id=&#34;系列教程&#34;&gt;&lt;strong&gt;系列教程&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;全部文章RSS订阅&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;web系列&#34;&gt;&lt;strong&gt;web系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/web/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;&lt;strong&gt;Web 分类 RSS 订阅&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/69a052c9/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;浏览器的渲染过程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/656a0abb/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;在浏览器输入 URL 回车之后发生了什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/448f849b/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;1.5 万字 CSS 基础拾遗（核心知识、常见需求）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/80906b88/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;http 错误代码表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/8f873c0f/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;写了些浏览器增强脚本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1d87ed2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;如何防止密码泄露，保护密码安全？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/ec2cad2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ESNI和加密DNS - 保护信息隐私的最后一块拼图&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/68d3867d/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;node 项目从构建到使用 jenkins + docker + nginx + mysql + redis 自动化部署&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1991789c/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Github Pages + jekyll 全面介绍极简搭建个人网站和博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css&#34;&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/d3@7&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-view@0.18.10&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10&#34;&gt;&lt;/script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;/css/markmap.css&#34;&gt;

&lt;script src=&#34;/js/markmap.js&#34;&gt;&lt;/script&gt;
&lt;script data-pjax src=&#34;/lib/hbe.54b2b4c40b.js&#34;&gt;&lt;/script&gt;&lt;link href=&#34;/css/hbe-style.css&#34; rel=&#34;stylesheet&#34; type=&#34;text/css&#34;&gt;</content>
        <category term="tampermonkey" />
        <category term="zhihu" />
        <category term="csdn" />
        <category term="juejin" />
        <updated>2024-04-19T14:45:16.000Z</updated>
    </entry>
    <entry>
        <id>https://blog.17lai.site/posts/80938cfe/</id>
        <title>emoji表情符号</title>
        <link rel="alternate" href="https://blog.17lai.site/posts/80938cfe/"/>
        <content type="html">&lt;blockquote&gt;
&lt;p&gt;emoji表情符号是非常流行的表情符号，几乎没有地方都会支持。这里列举出常用的emoji表情不好，方便大家使用与查阅。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;常用符号&#34;&gt;常用符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;❤❥웃유♋☮✌☏☢☠✔☑♚▲♪✈✞÷↑↓◆◇⊙■□△▽¿─│♥❣♂♀☿Ⓐ✍✉☣☤✘☒♛▼♫⌘☪≈←→◈◎☉★☆⊿※¡━┃♡ღ ツ

☼☁❅♒✎©®™Σ✪✯☭➳ 卐 √↖↗●◐Θ◤◥︻〖〗┄┆℃℉°✿ϟ☃☂✄¢€£∞✫★½✡×↙↘○◑⊕◣◢︼【】┅┇☽☾✚〓

▂▃▄▅▆▇█▉▊▋▌▍▎▏↔↕☽☾ の•▸◂▴▾┈┊①②③④⑤⑥⑦⑧⑨⑩ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ ㍿ ▓♨♛❖♓☪✙

┉┋☹☺☻ت ヅツッシ Üϡﭢ™℠℗©®♥❤❥❣❦❧♡۵웃유ღ♋♂♀☿☼☀☁☂☄☾☽❄☃☈⊙☉℃℉❅✺ϟ☇♤♧♡♢♠♣

♥♦☜☞☝✍☚☛☟✌✽✾✿❁❃❋❀⚘☑✓✔√☐☒✗✘ ㄨ ✕✖✖⋆✢✣✤✥❋✦✧✩✰✪✫✬✭✮✯❂✡★✱✲✳✴✵✶✷✸✹✺✻✼❄❅❆❇❈❉❊†☨✞✝☥☦☓☩☯☧☬

☸✡♁✙♆。，、＇：∶；?‘’“”〝〞ˆˇ﹕︰﹔﹖﹑•¨….¸;！´？！～—ˉ ｜‖＂〃｀@﹫¡¿﹏﹋﹌︴々﹟#

﹩$﹠&amp;amp;﹪%*﹡﹢﹦﹤‐￣ ¯―﹨ˆ˜﹍﹎+&amp;#x3D;&amp;lt;＿*-\ˇ~﹉﹊（）〈〉‹›﹛﹜『』〖〗［］《》〔〕&amp;#123;&amp;#125;「」【】︵︷︿︹︽*﹁﹃︻︶︸﹀︺︾ˉ﹂﹄︼☩☨☦✞✛✜✝✙✠✚†‡◉○◌◍◎●◐◑◒◓◔◕◖◗❂☢⊗⊙◘◙◍⅟½⅓⅕⅙⅛⅔⅖⅚⅜¾⅗⅝⅞⅘≂≃≄≅≆≇≈≉≊≋≌

≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩⊰⊱⋛⋚∫∬∭∮∯∰∱∲∳%℅‰‱㊣㊎㊍㊌㊋㊏㊐㊊㊚㊛㊤㊥

㊦㊧㊨㊒㊞㊑㊒㊓㊔㊕㊖㊗㊘㊜㊝㊟㊠㊡㊢㊩㊪㊫㊬㊭㊮㊯㊰㊙ ㉿ 囍 ♔♕♖♗♘♙♚♛♜♝♞♟ℂℍℕℙℚℝℤℬℰℯℱℊℋℎℐℒℓℳℴ℘ℛℭ℮ℌℑℜℨ♪♫♩♬♭♮♯°øⒶ☮✌☪✡☭✯ 卐 ✐✎✏

✑✒✍✉✁✂✃✄✆✉☎☏➟➡➢➣➤➥➦➧➨➚➘➙➛➜➝➞➸♐➲➳⏎➴➵➶➷➸➹➺➻➼➽←↑→↓↔↕↖↗↘↙↚↛↜↝

↞↟↠↡↢↣↤↥↦↧↨➫➬➩➪➭➮➯➱↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑⇒⇓⇔⇕

⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡ

ⓢⓣⓤⓥⓦⓧⓨⓩ⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⅠⅡⅢⅣⅤ

ⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿ┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫
┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽
┾┿╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏
═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬◤◥◄►▶◀◣◢▲

▼◥▸◂▴▾△▽▷◁⊿▻◅▵▿▹◃❏❐❑❒▀▁▂▃▄▅▆▇▉▊▋█▌▍▎▏▐░▒▓▔▕■□▢▣▤▥▦▧

▨▩▪▫▬▭▮▯ ㋀㋁㋂㋃㋄㋅㋆㋇㋈㋉㋊㋋㏠㏡㏢㏣㏤㏥㏦㏧㏨㏩㏪㏫㏬㏭㏮㏯㏰㏱㏲

㏳㏴㏵㏶㏷㏸㏹㏺㏻㏼㏽㏾㍙㍚㍛㍜㍝㍞㍟㍠㍡㍢㍣㍤㍥㍦㍧㍨㍩㍪㍫㍬㍭㍮㍯㍰㍘

☰☲☱☴☵☶☳☷☯&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;特殊符号&#34;&gt;特殊符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;♠♣♧♡♥❤❥❣♂♀✲☀☼☾☽◐◑☺☻☎☏✿❀№↑↓←→√×÷★℃℉°◆◇⊙■□△▽¿½☯✡ ㍿卍卐 ♂♀✚〓㎡♪♫♩♬

㊚㊛囍㊒㊖ Φ♀♂‖KaTeX parse error: Expected ‘EOF’, got ‘&amp;amp;’ at position 3: @*&amp;amp;̲#※卍卐 Ψ♫♬♭♩♪♯♮⌒¶∮…&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;编号序号&#34;&gt;编号序号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⓪

❶❷❸❹❺❻❼❽❾❿⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴

㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇

⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛

ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹ

ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ

⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;数学符号&#34;&gt;数学符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;﹢﹣×÷±&amp;#x2F;&amp;#x3D;≌∽≦≧≒ ﹤﹥ ≈≡≠&amp;#x3D;≤≥&amp;lt;&amp;gt;≮≯∷∶∫∮∝∞∧∨∑∏∪∩∈∵∴⊥∥∠⌒⊙√∟⊿㏒㏑%

‰⅟½⅓⅕⅙⅛⅔⅖⅚⅜¾⅗⅝⅞⅘≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟

≠≡≢≣≤≥≦≧≨≩⊰⊱⋛⋚∫∬∭∮∯∰∱∲∳%℅‰‱øØπ&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;爱心符号&#34;&gt;爱心符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;♥❣ღ♠♡♤❤❥&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;标点符号&#34;&gt;标点符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;。，、＇：∶；?‘’“”〝〞ˆˇ﹕︰﹔﹖﹑•¨….¸;！´？！～—ˉ ｜‖＂〃｀@﹫¡¿﹏﹋﹌︴

々﹟#﹩$﹠&amp;amp;﹪%*﹡﹢﹦﹤‐￣ ¯―﹨ˆ˜﹍﹎+&amp;#x3D;&amp;lt;＿*-\ˇ~﹉﹊

（）〈〉‹›﹛﹜『』〖〗［］《》〔〕&amp;#123;&amp;#125;「」【】

︵︷︿︹︽*﹁﹃︻︶︸﹀︺︾ˉ﹂﹄︼❝❞&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;单位符号&#34;&gt;单位符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;°′″＄￥〒￠￡％＠℃℉ ﹩﹪‰﹫㎡㏕㎜㎝㎞㏎m³㎎㎏㏄º○¤%$º¹²³&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;货币符号&#34;&gt;货币符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;€£Ұ₴$₰¢₤¥₳₲₪₵ 元 ₣₱฿¤₡₮₭₩ރ 円 ₢₥₫₦zł﷼₠₧₯₨Kčर₹ƒ₸ ￠&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;箭头符号（含推导-转换符号）&#34;&gt;箭头符号（含推导&amp;amp;转换符号）&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;↑↓←→↖↗↘↙↔↕➻➼➽➸➳➺➻➴➵➶➷➹▶►▷◁◀◄«

»➩➪➫➬➭➮➯➱⏎➲➾➔➘➙➚➛➜➝➞➟➠➡➢➣➤➥➦➧➨

↚↛↜↝↞↟↠↠↡↢↣↤↤↥↦↧↨⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑⇒⇓⇔⇖⇗⇘⇙

⇜↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹☇☈↼↽↾↿⇀⇁⇂⇃⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪↺↻⇚⇛♐&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;符号图案&#34;&gt;符号图案&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;✐✎✏✑✒✍✉✁✂✃✄✆✉☎☏☑✓✔√☐☒✗✘ ㄨ ✕✖✖☢☠☣✈★☆✡ 囍㍿ ☯☰☲☱☴☵☶☳☷

☜☞☝✍☚☛☟✌♤♧♡♢♠♣♥♦☀☁☂❄☃♨웃유❖☽☾☪✿♂♀✪✯☭➳ 卍卐 √×■◆●○◐◑✙☺☻

❀⚘♔♕♖♗♘♙♚♛♜♝♞♟♧♡♂♀♠♣♥❤☜☞☎☏⊙◎☺☻☼▧▨♨◐◑↔↕▪▒◊◦▣▤▥▦▩◘◈◇

♬♪♩♭♪ の ★☆→ あぃ￡ Ю〓§♤♥▶¤✲❈✿✲❈➹☀☂☁【】┱┲❣✚✪✣✤✥✦❉❥❦❧❃❂❁❀✄☪☣☢☠☭ღ▶▷◀◁

☀☁☂☃☄★☆☇☈⊙☊☋☌☍ⓛⓞⓥⓔ╬『』∴☀♫♬♩♭♪☆∷﹌の ★◎▶☺☻►◄▧▨♨◐◑↔↕↘▀▄█▌

◦☼♪ の ☆→♧ ぃ￡ ❤▒▬♦◊◦♠♣▣۰•❤•۰►◄▧▨♨◐◑↔↕▪▫☼♦⊙●○①⊕◎Θ⊙¤ ㊣ ★☆♀◆◇◣◢◥▲▼△▽⊿◤◥

✐✌✍✡✓✔✕✖♂♀♥♡☜☞☎☏⊙◎☺☻►◄▧▨♨◐◑↔↕♥♡▪▫☼♦▀▄█▌▐░▒▬♦◊◘◙◦☼♠♣▣▤▥▦▩◘◙◈

♫♬♪♩♭♪✄☪☣☢☠♯♩♪♫♬♭♮☎☏☪♈ºº₪¤큐«»™♂✿♥ 　 ◕‿-｡　｡◕‿◕｡&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;希腊字母&#34;&gt;希腊字母&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ

αβγδεζνξοπρσηθικλμτυφχψω&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;俄语字母&#34;&gt;俄语字母&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвг

деёжзийклмнопрстуфхцчшщъыьэюя&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;汉语拼音&#34;&gt;汉语拼音&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;āáǎàōóǒòēéěèīíǐìūúǔùǖǘǚǜüêɑńňɡ ㄅㄆㄇㄈㄉ

ㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞ

ㄟㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩ&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;中文字符&#34;&gt;中文字符&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;零壹贰叁肆伍陆柒捌玖拾佰仟万亿吉太拍艾分厘毫微

卍卐卄巜弍弎弐朤氺曱甴囍兀々〆のぁ〡〢〣〤〥〦〧〨〩

㊎㊍㊌㊋㊏㊚㊛㊐㊊㊣㊤㊥㊦㊧㊨㊒㊫㊑㊓㊔㊕㊖㊗㊘㊜㊝㊞㊟㊠㊡㊢㊩㊪㊬㊭㊮㊯㊰

㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;日文符号&#34;&gt;日文符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてで

とどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎ

わゐゑをんゔゕゖァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタ

ダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユ

ョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿ゠ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;制表符号&#34;&gt;制表符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;─ ━│┃╌╍╎╏┄ ┅┆┇┈ ┉┊┋┌┍┎┏┐┑┒┓└ ┕┖┗ ┘┙┚┛├┝┞┟┠┡┢┣ ┤┥┦┧┨┩┪┫┬ ┭ ┮ ┯ ┰ ┱ ┲ ┳

┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻┼ ┽ ┾ ┿ ╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╪ ╫ ╬═║╒╓╔ ╕╖╗╘╙╚ ╛╜╝╞╟╠ ╡╢╣

╤ ╥ ╦ ╧ ╨ ╩ ╳╔ ╗╝╚ ╬ ═ ╓ ╩ ┠ ┨┯ ┷┏ ┓┗ ┛┳ ⊥ ﹃ ﹄┌ ╮ ╭ ╯╰&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;皇冠符号&#34;&gt;皇冠符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;♚ 　 ♛ 　 ♝ 　 ♞ 　 ♜ 　 ♟ 　 ♔ 　 ♕ 　 ♗ 　 ♘ 　 ♖ 　 ♟&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;彩色表情符号&#34;&gt;彩色表情符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;🌹🍀🍎💰📱🌙🍁🍂🍃🌷💎🔪🔫🏀⚽⚡👄👍🔥&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;常用-emoji-符号&#34;&gt;常用 emoji 符号&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;😀😁😂😃😄😅😆😉😊😋😎😍😘😗😙😚☺😇😐😑😶😏😣😥😮😯😪😫😴😌😛😜😝😒😓😔😕😲😷😖😞😟😤😢😭😦😧😨😬😰😱😳😵😡😠😈👿👹👺💀👻👽👦👧👨👩👴👵👶👱👮👲👳👷👸💂🎅👰👼💆💇🙍🙎🙅🙆💁🙋🙇🙌🙏👤👥🚶🏃👯💃👫👬👭💏💑👪💪👈👉☝👆👇✌✋👌👍👎✊👊👋👏👐✍👣👀👂👃👅👄💋👓👔👕👖👗👘👙👚👛👜👝🎒💼👞👟👠👡👢👑👒🎩🎓💄💅💍🌂🙈🙉🙊🐵🐒🐶🐕🐩🐺🐱😺😸😹😻😼😽🙀😿😾🐈🐯🐅🐆🐴🐎🐮🐂🐃🐄🐷🐖🐗🐽🐏🐑🐐🐪🐫🐘🐭🐁🐀🐹🐰🐇🐻🐨🐼🐾🐔🐓🐣🐤🐥🐦🐧🐸🐊🐢🐍🐲🐉🐳🐋🐬🐟🐠🐡🐙🐚🐌🐛🐜🐝🐞🦋💐🌸💮🌹🌺🌻🌼🌷🌱🌲🌳🌴🌵🌾🌿🍀🍁🍂🍃🌍🌎🌏🌐🌑🌒🌓🌔🌕🌖🌗🌘🌙🌚🌛🌜☀🌝🌞⭐🌟🌠☁⛅☔⚡❄🔥💧🌊💩🍇🍈🍉🍊🍋🍌🍍🍎🍏🍐🍑🍒🍓🍅🍆🌽🍄🌰🍞🍖🍗🍔🍟🍕🍳🍲🍱🍘🍙🍚🍛🍜🍝🍠🍢🍣🍤🍥🍡🍦🍧🍨🍩🍪🎂🍰🍫🍬🍭🍮🍯🍼☕🍵🍶🍷🍸🍹🍺🍻🍴🎪🎭🎨🎰🚣🛀🎫🏆⚽⚾🏀🏈🏉🎾🎱🎳⛳🎣🎽🎿🏂🏄🏇🏊🚴🚵🎯🎮🎲🎷🎸🎺🎻🎬👾🌋🗻🏠🏡🏢🏣🏤🏥🏦🏨🏩🏪🏫🏬🏭🏯🏰💒🗼🗽⛪⛲🌁🌃🌆🌇🌉🌌🎠🎡🎢🚂🚃🚄🚅🚆🚇🚈🚉🚊🚝🚞🚋🚌🚍🚎🚏🚐🚑🚒🚓🚔🚕🚖🚗🚘🚚🚛🚜🚲⛽🚨🚥🚦🚧⚓⛵🚤🚢✈💺🚁🚟🚠🚡🚀🎑🗿🛂🛃🛄🛅💌💎🔪💈🚪🚽🚿🛁⌛⏳⌚⏰🎈🎉🎊🎎🎏🎐🎀🎁📯📻📱📲☎📞📟📠🔋🔌💻💽💾💿📀🎥📺📷📹📼🔍🔎🔬🔭📡💡🔦🏮📔📕📖📗📘📙📚📓📃📜📄📰📑🔖💰💴💵💶💷💸💳✉📧📨📩📤📥📦📫📪📬📭📮✏✒📝📁📂📅📆📇📈📉📊📋📌📍📎📏📐✂🔒🔓🔏🔐🔑🔨🔫🔧🔩🔗💉💊🚬🔮🚩🎌💦💨💣☠♠♥♦♣🀄🎴🔇🔈🔉🔊📢📣💤💢💬💭♨🌀🔔🔕✡✝🔯📛🔰🔱⭕✅☑✔✖❌❎➕➖➗➰➿〽✳✴❇‼⁉❓❔❕❗©®™🎦🔅🔆💯🔠🔡🔢🔣🔤🅰🆎🅱🆑🆒🆓ℹ🆔Ⓜ🆕🆖🅾🆗🅿🆘🆙🆚🈁🈂🈷🈶🈯 🉐 🈹🈚🈲 🉑 🈸🈴🈳 ㊗㊙ 🈺🈵▪▫◻◼◽◾⬛⬜🔶🔷🔸🔹🔺🔻💠🔲🔳⚪⚫🔴🔵♈♉♊♋♌♍♎♏♐♑♒♓⛎💘❤💓💔💕💖💗💙💚💛💜💝💞💟❣🌿🚧💒☎📟💽⬆↗➡↘⬇↙⬅↖↕↔↩↪⤴⤵🔃🔄🔙🔚🔛🔜🔝🔀🔁🔂▶⏩◀⏪🔼⏫🔽⏬📱📶📳📴♻🏧🚮🚰♿🚹🚺🚻🚼🚾⚠🚸⛔🚫🚳🚭🚯🚱🚷🔞&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css&#34;&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/d3@7&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-view@0.18.10&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10&#34;&gt;&lt;/script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;/css/markmap.css&#34;&gt;

&lt;script src=&#34;/js/markmap.js&#34;&gt;&lt;/script&gt;
&lt;script data-pjax src=&#34;/lib/hbe.54b2b4c40b.js&#34;&gt;&lt;/script&gt;&lt;link href=&#34;/css/hbe-style.css&#34; rel=&#34;stylesheet&#34; type=&#34;text/css&#34;&gt;</content>
        <category term="web" />
        <category term="emoji" />
        <updated>2022-03-23T10:18:00.000Z</updated>
    </entry>
    <entry>
        <id>https://blog.17lai.site/posts/68d3867d/</id>
        <title>node 项目从构建到使用 jenkins + docker + nginx + mysql + redis 自动化部署</title>
        <link rel="alternate" href="https://blog.17lai.site/posts/68d3867d/"/>
        <content type="html">&lt;h1 id=&#34;概述&#34;&gt;概述&lt;/h1&gt;
&lt;p&gt;这都2019年末了，你还不会 docker吗？ 你 low 爆了，我们作为一枚前端，不能说是要精通使用 docker 完成各种事情，但我觉得必须要学会使用 docker 干下面几件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;部署前端应用&lt;/li&gt;
&lt;li&gt;部署nginx&lt;/li&gt;
&lt;li&gt;使用docker-compose部署&lt;/li&gt;
&lt;li&gt;在容器之间建立联系&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Docker 可理解为跑在宿主机上的非常精简、小巧、高度浓缩的虚拟机。 它可以将容器里的进程安稳的在宿主机上运行，之前我也有写过一些关于docker的文章，在这我就不做过多的介绍了，如有需要请自行查看我之前的文章即可，接下来我们通过项目来了解并使用 docker&lt;/p&gt;
&lt;h1 id=&#34;egg.js-？&#34;&gt;Egg.js ？&lt;/h1&gt;
&lt;p&gt;在这里我使用 &lt;code&gt;egg.js&lt;/code&gt; 来为大家实操一下项目的部署流程。有人会问 &lt;code&gt;egg.js&lt;/code&gt; 是什么？ 我只能回答这是一款专业级的 &lt;code&gt;node&lt;/code&gt; 框架。作为一个有梦想的前端，我们有必要去学习一种后端语言，而作为前端 &lt;code&gt;node&lt;/code&gt; 的学习成本相对来说比较低的。 &lt;code&gt;egg.js&lt;/code&gt; 这个框架在 node 现有框架中也是比较优秀的，如有需要，大家可以自行学习，我们今天的学习主要还是项目的部署流程，在这我就不给大家做过多的介绍。如有需要，请查阅 &lt;a href=&#34;https://eggjs.org/zh-cn/intro/index.html&#34; title=&#34;https://eggjs.org/zh-cn/intro/index.html&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;官方文档&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;开始前的准备&#34;&gt;开始前的准备&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;docker 与 docker-compose 的安装我就不给大家介绍了。在之前的文章中是有的，也比较详细，作为一位开发人员，我认为这点事情难不倒大家&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;初始化项目&#34;&gt;初始化项目&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;$ mkdir egg-example &amp;amp;&amp;amp; cd egg-example
$ npm init egg --type&amp;#x3D;simple
$ npm i
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;创建需要的文件&#34;&gt;创建需要的文件&lt;/h2&gt;
&lt;p&gt;我们需要在项目根目录创建我们所需要的文件&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;$ touch Dockerfile
$ touch docker-compose.yml
$ setup.sh
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;目录结构&#34;&gt;目录结构&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;egg-project
├── package.json
├── setup.sh (新建)
├── Dockerfile (新建)
├── docker-compose.yml (新建)
├── app
|   ├── router.js
│   ├── controller
│   |   └── home.js
│   ├── service (可选)
│   |   └── user.js
│   ├── middleware (可选)
│   |   └── response_time.js
│   ├── schedule (可选)
│   |   └── my_task.js

...

复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;常用指令&#34;&gt;常用指令&lt;/h2&gt;
&lt;p&gt;在开始之前我们要学习下常用的一些指令，看下方：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110127.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;了解流程&#34;&gt;了解流程&lt;/h1&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110133.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;安装-jenkins&#34;&gt;安装 jenkins&lt;/h1&gt;
&lt;p&gt;在安装jenkins我选择了使用docker-compose&lt;br&gt;
docker-compose 是一个用来把 docker 自动化的东西&lt;br&gt;
有了 docker-compose 你可以把所有繁复的 docker 操作全都一条命令，自动化的完成。&lt;/p&gt;
&lt;h2 id=&#34;首先我们需要在服务器上创建一个目录机构-具体结构个人自行创建&#34;&gt;首先我们需要在服务器上创建一个目录机构 (具体结构个人自行创建)&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;&amp;#x2F;home&amp;#x2F;jenkins
     - docker-compose.yml
     - jenkins-home
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;接下来我们来编写-docker-compose-yml-安装jenkins&#34;&gt;接下来我们来编写 docker-compose.yml 安装jenkins&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;version: &amp;#39;3&amp;#39;                                    # 指定 docker-compose.yml 文件的写法格式
services:                                       # 多个容器集合
  docker_jenkins: 
    user: root                                  # 为了避免一些权限问题 在这我使用了root
    restart: always                             # 重启方式
    image: jenkins&amp;#x2F;jenkins:lts                  # 指定服务所使用的镜像 在这里我选择了 LTS (长期支持)
    container_name: jenkins                     # 容器名称
    ports:                                      # 对外暴露的端口定义
      - &amp;#39;8080:8080&amp;#39;
      - &amp;#39;50000:50000&amp;#39;
    volumes:                                    # 卷挂载路径
      - &amp;#x2F;home&amp;#x2F;jenkins&amp;#x2F;jenkins_home&amp;#x2F;:&amp;#x2F;var&amp;#x2F;jenkins_home   # 这是我们一开始创建的目录挂载到容器内的jenkins_home目录
      - &amp;#x2F;var&amp;#x2F;run&amp;#x2F;docker.sock:&amp;#x2F;var&amp;#x2F;run&amp;#x2F;docker.sock
      - &amp;#x2F;usr&amp;#x2F;bin&amp;#x2F;docker:&amp;#x2F;usr&amp;#x2F;bin&amp;#x2F;docker                 # 这是为了我们可以在容器内使用docker命令
      - &amp;#x2F;usr&amp;#x2F;local&amp;#x2F;bin&amp;#x2F;docker-compose:&amp;#x2F;usr&amp;#x2F;local&amp;#x2F;bin&amp;#x2F;docker-compose     # 同样的这是为了使用docker-compose命令
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;我们需要进入到 jenkins 目录下执行：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;$ docker-compose up -d
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;配置&#34;&gt;配置&lt;/h2&gt;
&lt;p&gt;不出意外你现在可以打开你的服务器地址 &lt;a href=&#34;http://xxxxxxx&#34; title=&#34;http://xxxxxxx&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;http://xxxxxxx&lt;/a&gt;:端口号 就能看到这个界面：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110139.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;打开你所创建的jenkins目录进入到jenkins-home&lt;br&gt;
/home/jenkins/jenkins-home&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110148.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;进入 secrets 目录&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;$ cat initialAdminPassword
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110209.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;然后把里面的文本复制出来填到管理员密码中&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110327.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110215.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;接下来需要安装两个插件&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;NodeJS Plugin
Publish Over SSH
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110334.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110221.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;然后我们滑到最下方&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110228.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110341.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110347.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110235.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;开始我们的操作&#34;&gt;开始我们的操作&lt;/h1&gt;
&lt;h2 id=&#34;dockerfile&#34;&gt;Dockerfile&lt;/h2&gt;
&lt;p&gt;我们在开始阶段的时候学过一些常用指令，大家应该一眼就可以看得懂这些命令。 加油！！&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;FROM node:10.0-alpine             # 镜像版本

# 设置时区
RUN apk --update add tzdata \
    &amp;amp;&amp;amp; cp &amp;#x2F;usr&amp;#x2F;share&amp;#x2F;zoneinfo&amp;#x2F;Asia&amp;#x2F;Shanghai &amp;#x2F;etc&amp;#x2F;localtime \
    &amp;amp;&amp;amp; echo &amp;quot;Asia&amp;#x2F;Shanghai&amp;quot; &amp;gt; &amp;#x2F;etc&amp;#x2F;timezone \
    &amp;amp;&amp;amp; apk del tzdata

# 创建app目录
RUN mkdir -p &amp;#x2F;usr&amp;#x2F;src&amp;#x2F;node-app&amp;#x2F;egg-santak

# 设置工作目录
WORKDIR &amp;#x2F;usr&amp;#x2F;src&amp;#x2F;node-app&amp;#x2F;egg-santak

# 拷贝package.json文件到工作目录
# !!重要：package.json需要单独添加。
# Docker在构建镜像的时候，是一层一层构建的，仅当这一层有变化时，重新构建对应的层。
# 如果package.json和源代码一起添加到镜像，则每次修改源码都需要重新安装npm模块，这样木有必要。
# 所以，正确的顺序是: 添加package.json；安装npm模块；添加源代码。
COPY package.json &amp;#x2F;usr&amp;#x2F;src&amp;#x2F;node-app&amp;#x2F;egg-santak&amp;#x2F;package.json

# 安装npm依赖(使用淘宝的镜像源)
# 如果使用的境外服务器，无需使用淘宝的镜像源，即改为&amp;#96;RUN npm i&amp;#96;。
RUN npm i --registry&amp;#x3D;https:&amp;#x2F;&amp;#x2F;registry.npm.taobao.org

# 拷贝所有源代码到工作目录
COPY . &amp;#x2F;usr&amp;#x2F;src&amp;#x2F;node-app&amp;#x2F;egg-santak

# 暴露容器端口
EXPOSE 7001

# 启动node应用
CMD npm start
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;在服务器中创建我们所需要挂载的数据卷&#34;&gt;在服务器中创建我们所需要挂载的数据卷&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110241.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;# nginx
$ mkdir -p nginx&amp;#x2F;conf.d nginx&amp;#x2F;logs

# mysql
$ mkdir mysql

# redis
$ mkdir redis
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;然后进入 &lt;code&gt;nginx/conf.d&lt;/code&gt; 文件夹中 创建一个后缀为 &lt;code&gt;conf&lt;/code&gt; 的文件&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;$ cd nginx&amp;#x2F;conf.d
$ touch default.conf
$ vim default.conf
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;写入以下内容：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;server &amp;#123;
  listen 80;
  listen [::]:80;
  server_tokens off;

  root &amp;#x2F;var&amp;#x2F;www&amp;#x2F;html;
  index index.html index.htm;

  # 修改为自己的域名
  server_name api.lovelp.xin;

  # 访问 &amp;#x2F; 路径时执行反向代理
  location &amp;#x2F; &amp;#123;
    # 这里 nodejs 是 node 容器名
    proxy_pass http:&amp;#x2F;&amp;#x2F;nodejs:7001;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    # 后端的Web服务器可以通过 X-Forwarded-For 获取用户真实 IP
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # 允许客户端请求的最大单文件字节数
    client_max_body_size 15M;
    # 缓冲区代理缓冲用户端请求的最大字节数
    client_body_buffer_size 128k;
  &amp;#125;
&amp;#125;
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;docker-compose-yml&#34;&gt;docker-compose.yml&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;version: &amp;#39;3&amp;#39;

services:
  santak_redis:
    image: redis:3                  # 指定服务镜像
    container_name: santak_redis    # 容器名称
    restart: always                 # 重启方式
    hostname: redis
    command: redis-server &amp;#x2F;usr&amp;#x2F;local&amp;#x2F;etc&amp;#x2F;redis&amp;#x2F;redis.conf --requirepass 123456  --appendonly yes
    volumes:                        # 挂载数据卷
      - &amp;#x2F;root&amp;#x2F;redis&amp;#x2F;redis.conf:&amp;#x2F;usr&amp;#x2F;local&amp;#x2F;etc&amp;#x2F;redis&amp;#x2F;redis.conf
    ports:                          # 映射端口
      - &amp;quot;6379:6379&amp;quot;     
    networks:                       # 加入指定网络
      - app-network

  santak_nginx:
    image: nginx:stable-alpine      # 指定服务镜像
    container_name: santak_nginx    # 容器名称
    restart: always                 # 重启方式
    ports:                          # 映射端口
      - &amp;quot;80:80&amp;quot;
    volumes:                        # 挂载数据卷
      - &amp;#x2F;etc&amp;#x2F;localtime:&amp;#x2F;etc&amp;#x2F;localtime
      - &amp;#x2F;root&amp;#x2F;nginx&amp;#x2F;conf.d:&amp;#x2F;etc&amp;#x2F;nginx&amp;#x2F;conf.d
      - &amp;#x2F;root&amp;#x2F;nginx&amp;#x2F;logs:&amp;#x2F;var&amp;#x2F;log&amp;#x2F;nginx
    depends_on:                     # 启动顺序
      - nodejs
    networks:                       # 加入指定网络
      - app-network

  santak_mysql:
    image: mysql:5.7
    container_name: santak_mysql
    restart: always
    ports:                          # 映射端口
      - &amp;quot;3306:3306&amp;quot;
    environment:
      - MYSQL_ROOT_PASSWORD&amp;#x3D;root
      - MYSQL_USER&amp;#x3D;lovelp           # 创建lovelp用户
      - MYSQL_PASSWORD&amp;#x3D;mm123321     # 设置lovelp用户的密码
      - MYSQL_DATABASE&amp;#x3D;santak       # 创建初始数据库
      - TZ&amp;#x3D;Asia&amp;#x2F;Shanghai            # 设置时区
    volumes:                        # 挂载数据卷
      - &amp;#x2F;root&amp;#x2F;mysql:&amp;#x2F;var&amp;#x2F;lib&amp;#x2F;mysql  # 为了数据持久化
    command: --character-set-server&amp;#x3D;utf8mb4 --collation-server&amp;#x3D;utf8mb4_unicode_ci
    networks:                       # 加入指定网络
      - app-network 

  nodejs:
    build:                          # 这里指的是我们刚刚撸的 Dockerfile 文件
      context: .                    
      dockerfile: Dockerfile
    image: nodejs                   # 镜像名称
    container_name: nodejs          # 容器名称
    restart: always                 # 重启方式
    depends_on:                     # 启动顺序
      - santak_redis
      - santak_mysql
    links:                          # 容器连接
      - santak_redis:santak_redis
      - santak_mysql:santak_mysql
    networks:                       # 加入指定网络
      - app-network

volumes:
  certbot-etc:
  certbot-var:

networks:  # 实现通信
  app-network:
    driver: bridge
复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;在项目中的使用&#34;&gt;在项目中的使用&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110359.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;setup-sh&#34;&gt;&lt;a href=&#34;http://setup.sh&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;setup.sh&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;#!&amp;#x2F;usr&amp;#x2F;bin&amp;#x2F;env bash
#image_version&amp;#x3D;&amp;#96;date +%Y%m%d%H%M&amp;#96;;

# 关闭容器
docker-compose stop || true;
# 删除容器
docker-compose down || true;
# 构建镜像
docker-compose build;
# 启动并后台运行
docker-compose up -d;
# 查看日志
docker logs nodejs;
# 对空间进行自动清理
docker system prune -a -f

复制代码&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h1 id=&#34;jenkins-创建项目&#34;&gt;jenkins 创建项目&lt;/h1&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110247.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110248.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110254.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110409.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;最后我们就可以愉快的 &lt;code&gt;Build Now&lt;/code&gt; 了&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2022/03/1120220311110415.webp&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;在这里我选择的是手动构建。其实jenkins有很多可配置项，比如自动化构建啥的，兴趣使然，大家自行摸索，谢谢大家&lt;/p&gt;
&lt;h2 id=&#34;整理转载&#34;&gt;整理转载:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://juejin.cn/post/6844904006184091662&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;掘金&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;参考-致谢&#34;&gt;参考&amp;amp;致谢&lt;/h2&gt;
&lt;h2 id=&#34;系列教程&#34;&gt;&lt;strong&gt;系列教程&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;全部文章RSS订阅&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;web系列&#34;&gt;&lt;strong&gt;web系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/web/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;&lt;strong&gt;Web 分类 RSS 订阅&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/69a052c9/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;浏览器的渲染过程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/656a0abb/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;在浏览器输入 URL 回车之后发生了什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/448f849b/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;1.5 万字 CSS 基础拾遗（核心知识、常见需求）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/80906b88/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;http 错误代码表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/8f873c0f/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;写了些浏览器增强脚本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1d87ed2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;如何防止密码泄露，保护密码安全？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/ec2cad2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ESNI和加密DNS - 保护信息隐私的最后一块拼图&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/68d3867d/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;node 项目从构建到使用 jenkins + docker + nginx + mysql + redis 自动化部署&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1991789c/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Github Pages + jekyll 全面介绍极简搭建个人网站和博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css&#34;&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/d3@7&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-view@0.18.10&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10&#34;&gt;&lt;/script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;/css/markmap.css&#34;&gt;

&lt;script src=&#34;/js/markmap.js&#34;&gt;&lt;/script&gt;
&lt;script data-pjax src=&#34;/lib/hbe.54b2b4c40b.js&#34;&gt;&lt;/script&gt;&lt;link href=&#34;/css/hbe-style.css&#34; rel=&#34;stylesheet&#34; type=&#34;text/css&#34;&gt;</content>
        <category term="docker" />
        <category term="web" />
        <category term="ci/cd" />
        <category term="node" />
        <category term="jenkins" />
        <updated>2022-03-16T10:04:02.000Z</updated>
    </entry>
    <entry>
        <id>https://blog.17lai.site/posts/80906b88/</id>
        <title>http 错误代码表</title>
        <link rel="alternate" href="https://blog.17lai.site/posts/80906b88/"/>
        <content type="html">&lt;p&gt;所有 HTTP 状态代码及其定义。&lt;br&gt;
　代码  指示  &lt;br&gt;
2xx  成功  &lt;br&gt;
200  正常；请求已完成。  &lt;br&gt;
201  正常；紧接 POST 命令。  &lt;br&gt;
202  正常；已接受用于处理，但处理尚未完成。  &lt;br&gt;
203  正常；部分信息 — 返回的信息只是一部分。  &lt;br&gt;
204  正常；无响应 — 已接收请求，但不存在要回送的信息。  &lt;br&gt;
3xx  重定向  &lt;br&gt;
301  已移动 — 请求的数据具有新的位置且更改是永久的。  &lt;br&gt;
302  已找到 — 请求的数据临时具有不同 URI。  &lt;br&gt;
303  请参阅其它 — 可在另一 URI 下找到对请求的响应，且应使用 GET 方法检索此响应。  &lt;br&gt;
304  未修改 — 未按预期修改文档。  &lt;br&gt;
305  使用代理 — 必须通过位置字段中提供的代理来访问请求的资源。  &lt;br&gt;
306  未使用 — 不再使用；保留此代码以便将来使用。  &lt;br&gt;
4xx  客户机中出现的错误  &lt;br&gt;
400  错误请求 — 请求中有语法问题，或不能满足请求。  &lt;br&gt;
401  未授权 — 未授权客户机访问数据。  &lt;br&gt;
402  需要付款 — 表示计费系统已有效。  &lt;br&gt;
403  禁止 — 即使有授权也不需要访问。  &lt;br&gt;
404  找不到 — 服务器找不到给定的资源；文档不存在。  &lt;br&gt;
407  代理认证请求 — 客户机首先必须使用代理认证自身。  &lt;br&gt;
415  介质类型不受支持 — 服务器拒绝服务请求，因为不支持请求实体的格式。  &lt;br&gt;
5xx  服务器中出现的错误  &lt;br&gt;
500  内部错误 — 因为意外情况，服务器不能完成请求。  &lt;br&gt;
501  未执行 — 服务器不支持请求的工具。  &lt;br&gt;
502  错误网关 — 服务器接收到来自上游服务器的无效响应。  &lt;br&gt;
503  无法获得服务 — 由于临时过载或维护，服务器无法处理请求。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;HTTP 400 - 请求无效 &lt;br&gt;
HTTP 401.1 - 未授权：登录失败 &lt;br&gt;
HTTP 401.2 - 未授权：服务器配置问题导致登录失败 &lt;br&gt;
HTTP 401.3 - ACL 禁止访问资源 &lt;br&gt;
HTTP 401.4 - 未授权：授权被筛选器拒绝 &lt;br&gt;
HTTP 401.5 - 未授权：ISAPI 或 CGI 授权失败  &lt;br&gt;
HTTP 403 - 禁止访问 &lt;br&gt;
HTTP 403 - 对 Internet 服务管理器 (HTML) 的访问仅限于 Localhost &lt;br&gt;
HTTP 403.1 禁止访问：禁止可执行访问 &lt;br&gt;
HTTP 403.2 - 禁止访问：禁止读访问 &lt;br&gt;
HTTP 403.3 - 禁止访问：禁止写访问 &lt;br&gt;
HTTP 403.4 - 禁止访问：要求 SSL &lt;br&gt;
HTTP 403.5 - 禁止访问：要求 SSL 128 &lt;br&gt;
HTTP 403.6 - 禁止访问：IP 地址被拒绝 &lt;br&gt;
HTTP 403.7 - 禁止访问：要求客户证书 &lt;br&gt;
HTTP 403.8 - 禁止访问：禁止站点访问 &lt;br&gt;
HTTP 403.9 - 禁止访问：连接的用户过多 &lt;br&gt;
HTTP 403.10 - 禁止访问：配置无效 &lt;br&gt;
HTTP 403.11 - 禁止访问：密码更改 &lt;br&gt;
HTTP 403.12 - 禁止访问：映射器拒绝访问 &lt;br&gt;
HTTP 403.13 - 禁止访问：客户证书已被吊销 &lt;br&gt;
HTTP 403.15 - 禁止访问：客户访问许可过多 &lt;br&gt;
HTTP 403.16 - 禁止访问：客户证书不可信或者无效 &lt;br&gt;
HTTP 403.17 - 禁止访问：客户证书已经到期或者尚未生效 &lt;br&gt;
HTTP 404.1 - 无法找到 Web 站点 &lt;br&gt;
HTTP 404 - 无法找到文件 &lt;br&gt;
HTTP 405 - 资源被禁止 &lt;br&gt;
HTTP 406 - 无法接受 &lt;br&gt;
HTTP 407 - 要求代理身份验证 &lt;br&gt;
HTTP 410 - 永远不可用 &lt;br&gt;
HTTP 412 - 先决条件失败 &lt;br&gt;
HTTP 414 - 请求 - URI 太长 &lt;br&gt;
HTTP 500 - 内部服务器错误 &lt;br&gt;
HTTP 500.100 - 内部服务器错误 - ASP 错误 &lt;br&gt;
HTTP 500-11 服务器关闭 &lt;br&gt;
HTTP 500-12 应用程序重新启动 &lt;br&gt;
HTTP 500-13 - 服务器太忙 &lt;br&gt;
HTTP 500-14 - 应用程序无效 &lt;br&gt;
HTTP 500-15 - 不允许请求 global.asa &lt;br&gt;
Error 501 - 未实现 &lt;br&gt;
HTTP 502 - 网关错误&lt;/p&gt;
&lt;h2 id=&#34;参考-致谢&#34;&gt;参考&amp;amp;致谢&lt;/h2&gt;
&lt;h2 id=&#34;系列教程&#34;&gt;&lt;strong&gt;系列教程&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;全部文章RSS订阅&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;web系列&#34;&gt;&lt;strong&gt;web系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/web/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;&lt;strong&gt;Web 分类 RSS 订阅&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/69a052c9/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;浏览器的渲染过程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/656a0abb/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;在浏览器输入 URL 回车之后发生了什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/448f849b/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;1.5 万字 CSS 基础拾遗（核心知识、常见需求）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/80906b88/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;http 错误代码表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/8f873c0f/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;写了些浏览器增强脚本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1d87ed2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;如何防止密码泄露，保护密码安全？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/ec2cad2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ESNI和加密DNS - 保护信息隐私的最后一块拼图&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/68d3867d/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;node 项目从构建到使用 jenkins + docker + nginx + mysql + redis 自动化部署&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1991789c/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Github Pages + jekyll 全面介绍极简搭建个人网站和博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css&#34;&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/d3@7&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-view@0.18.10&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10&#34;&gt;&lt;/script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;/css/markmap.css&#34;&gt;

&lt;script src=&#34;/js/markmap.js&#34;&gt;&lt;/script&gt;
&lt;script data-pjax src=&#34;/lib/hbe.54b2b4c40b.js&#34;&gt;&lt;/script&gt;&lt;link href=&#34;/css/hbe-style.css&#34; rel=&#34;stylesheet&#34; type=&#34;text/css&#34;&gt;</content>
        <category term="web" />
        <category term="http" />
        <updated>2021-10-06T11:25:00.000Z</updated>
    </entry>
    <entry>
        <id>https://blog.17lai.site/posts/448f849b/</id>
        <title>1.5 万字 CSS 基础拾遗（核心知识、常见需求）</title>
        <link rel="alternate" href="https://blog.17lai.site/posts/448f849b/"/>
        <content type="html">&lt;h2 id=&#34;1-5-万字-css-基础拾遗（核心知识、常见需求）&#34;&gt;1.5 万字 CSS 基础拾遗（核心知识、常见需求）&lt;/h2&gt;
&lt;p&gt;本篇文章围绕了 CSS 的核心知识点和项目中常见的需求来展开。虽然行文偏长，但偏基础，适合初级中级前端阅读，阅读的时候请适当跳过已经掌握的部分。&lt;/p&gt;
&lt;p&gt;这篇文章断断续续写了比较久，也参考了许多优秀的文章，但或许文章里还是存在不好或不对的地方，请多多指教，可以评论里直接提出来哈。&lt;/p&gt;
&lt;p&gt;小tip：后续内容更精彩哦。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192327.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;核心概念和知识点&#34;&gt;核心概念和知识点&lt;/h2&gt;
&lt;h3 id=&#34;语法&#34;&gt;语法&lt;/h3&gt;
&lt;p&gt;CSS 的核心功能是将 CSS 属性设定为特定的值。一个属性与值的键值对被称为&lt;strong&gt;声明&lt;/strong&gt;（declaration）。&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;color: red;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;而如果将一个或者多个声明用 &lt;code&gt;&amp;#123;&amp;#125;&lt;/code&gt; 包裹起来后，那就组成了一个&lt;strong&gt;声明块&lt;/strong&gt;（declaration block）。&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;&amp;#123;  
    color: red;  
    text-align: center;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;声明块如果需要作用到对应的 HTML 元素，那还需要加上&lt;strong&gt;选择器&lt;/strong&gt;。选择器和声明块组成了&lt;strong&gt;CSS 规则集&lt;/strong&gt;（CSS ruleset），常简称为 CSS 规则。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005346.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;span &amp;#123;  
    color: red;  
    text-align: center;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;“&lt;/p&gt;
&lt;p&gt;规则集中最后一条声明可以省略分号，但是并不建议这么做，因为容易出错。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;CSS 中的&lt;strong&gt;注释&lt;/strong&gt;：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;&amp;#x2F;* 单行注释 *&amp;#x2F;

&amp;#x2F;*  
    多行  
    注释  
*&amp;#x2F;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;在 CSS 文件中，除了注释、CSS 规则集以及 @规则 外，定义的一些别的东西都将被浏览器忽略。&lt;/p&gt;
&lt;h3 id=&#34;规则&#34;&gt;@规则&lt;/h3&gt;
&lt;p&gt;CSS 规则是样式表的主体，通常样式表会包括大量的规则列表。但有时候也需要在样式表中包括其他的一些信息，比如字符集，导入其它的外部样式表，字体等，这些需要专门的语句表示。&lt;/p&gt;
&lt;p&gt;而 @规则 就是这样的语句。CSS 里包含了以下 @规则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;@namespace 告诉 CSS 引擎必须考虑XML命名空间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@media, 如果满足媒体查询的条件则条件规则组里的规则生效。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@page, 描述打印文档时布局的变化.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@font-face, 描述将下载的外部的字体。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@keyframes, 描述 CSS 动画的关键帧。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@document, 如果文档样式表满足给定条件则条件规则组里的规则生效。 (推延至 CSS Level 4 规范)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;除了以上这几个之外，下面还将对几个比较生涩的 @规则 进行介绍。&lt;/p&gt;
&lt;h4 id=&#34;charset&#34;&gt;@charset&lt;/h4&gt;
&lt;p&gt;@charset[1] 用于定义样式表使用的字符集。它必须是样式表中的第一个元素。如果有多个 &lt;code&gt;@charset&lt;/code&gt; 被声明，只有第一个会被使用，而且不能在HTML元素或HTML页面的 &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; 元素内使用。&lt;/p&gt;
&lt;p&gt;注意：值必须是双引号包裹，且和&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;@charset &amp;quot;UTF-8&amp;quot;;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;平时写样式文件都没写 @charset 规则，那这个 CSS 文件到底是用的什么字符编码的呢？&lt;/p&gt;
&lt;p&gt;某个样式表文件到底用的是什么字符编码，浏览器有一套识别顺序（优先级由高到低）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;文件开头的 Byte order mark[2] 字符值，不过一般编辑器并不能看到文件头里的 BOM 值；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;HTTP 响应头里的 &lt;code&gt;content-type&lt;/code&gt; 字段包含的 &lt;code&gt;charset&lt;/code&gt; 所指定的值，比如：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Content-Type: text/css; charset=utf-8  &lt;/code&gt;&lt;br&gt;
`&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CSS 文件头里定义的 @charset 规则里指定的字符编码；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; 标签里的 charset 属性，该条已在 HTML5 中废除；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;默认是 &lt;code&gt;UTF-8&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;import&#34;&gt;@import&lt;/h4&gt;
&lt;p&gt;@import[3] 用于告诉 CSS 引擎引入一个外部样式表。&lt;/p&gt;
&lt;p&gt;link 和 @import 都能导入一个样式文件，它们有什么区别嘛？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;link 是 HTML 标签，除了能导入 CSS 外，还能导入别的资源，比如图片、脚本和字体等；而 @import 是 CSS 的语法，只能用来导入 CSS；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;link 导入的样式会在页面加载时同时加载，@import 导入的样式需等页面加载完成后再加载；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;link 没有兼容性问题，@import 不兼容 ie5 以下；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;link 可以通过 JS 操作 DOM 动态引入样式表改变样式，而@import不可以。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;supports&#34;&gt;@supports&lt;/h4&gt;
&lt;p&gt;@supports[4] 用于查询特定的 CSS 是否生效，可以结合 not、and 和 or 操作符进行后续的操作。&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;&amp;#x2F;* 如果支持自定义属性，则把 body 颜色设置为变量 varName 指定的颜色 *&amp;#x2F;  
@supports (--foo: green) &amp;#123;  
    body &amp;#123;  
        color: var(--varName);  
    &amp;#125;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h3 id=&#34;层叠性&#34;&gt;层叠性&lt;/h3&gt;
&lt;p&gt;层叠样式表，这里的层叠怎么理解呢？其实它是 CSS 中的核心特性之一，用于合并来自多个源的属性值的算法。比如说针对某个 HTML 标签，有许多的 CSS 声明都能作用到的时候，那最后谁应该起作用呢？层叠性说的大概就是这个。&lt;/p&gt;
&lt;p&gt;针对不同源的样式，将按照如下的顺序进行层叠，越往下优先级越高：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;用户代理样式表中的声明(例如，浏览器的默认样式，在没有设置其他样式时使用)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用户样式表中的常规声明(由用户设置的自定义样式。由于 Chrome 在很早的时候就放弃了用户样式表的功能，所以这里将不再考虑它的排序。)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;作者样式表中的常规声明(这些是我们 Web 开发人员设置的样式)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;作者样式表中的 !important 声明。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用户样式表中的 !important 声明S。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;理解层叠性的时候需要结合 CSS 选择器的优先级以及继承性来理解。比如针对同一个选择器，定义在后面的声明会覆盖前面的；作者定义的样式会比默认继承的样式优先级更高。&lt;/p&gt;
&lt;h3 id=&#34;选择器&#34;&gt;选择器&lt;/h3&gt;
&lt;p&gt;CSS 选择器无疑是其核心之一，对于基础选择器以及一些常用伪类必须掌握。下面列出了常用的选择器。 想要获取更多选择器的用法可以看 MDN CSS Selectors[5]。&lt;/p&gt;
&lt;h4 id=&#34;基础选择器&#34;&gt;基础选择器&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;标签选择器：&lt;code&gt;h1&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;类选择器：&lt;code&gt;.checked&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ID 选择器：&lt;code&gt;#picker&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通配选择器：&lt;code&gt;*&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;属性选择器&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[attr]&lt;/code&gt;：指定属性的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[attr=val]&lt;/code&gt;：属性等于指定值的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[attr*=val]&lt;/code&gt;：属性包含指定值的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[attr^=val]&lt;/code&gt; ：属性以指定值开头的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[attr$=val]&lt;/code&gt;：属性以指定值结尾的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[attr~=val]&lt;/code&gt;：属性包含指定值(完整单词)的元素(不推荐使用)；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[attr|=val]&lt;/code&gt;：属性以指定值(完整单词)开头的元素(不推荐使用)；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;组合选择器&#34;&gt;组合选择器&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;相邻兄弟选择器：&lt;code&gt;A + B&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;普通兄弟选择器：&lt;code&gt;A ~ B&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;子选择器：&lt;code&gt;A &amp;gt; B&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;后代选择器：&lt;code&gt;A B&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;伪类&#34;&gt;伪类&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;条件伪类&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:lang()&lt;/code&gt;：基于元素语言来匹配页面元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:dir()&lt;/code&gt;：匹配特定文字书写方向的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:has()&lt;/code&gt;：匹配包含指定元素的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:is()&lt;/code&gt;：匹配指定选择器列表里的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:not()&lt;/code&gt;：用来匹配不符合一组选择器的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;行为伪类&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:active&lt;/code&gt;：鼠标激活的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:hover&lt;/code&gt;： 鼠标悬浮的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;::selection&lt;/code&gt;：鼠标选中的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;状态伪类&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:target&lt;/code&gt;：当前锚点的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:link&lt;/code&gt;：未访问的链接元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:visited&lt;/code&gt;：已访问的链接元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:focus&lt;/code&gt;：输入聚焦的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:required&lt;/code&gt;：输入必填的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:valid&lt;/code&gt;：输入合法的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:invalid&lt;/code&gt;：输入非法的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:in-range&lt;/code&gt;：输入范围以内的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:out-of-range&lt;/code&gt;：输入范围以外的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:checked&lt;/code&gt;：选项选中的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:optional&lt;/code&gt;：选项可选的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:enabled&lt;/code&gt;：事件启用的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:disabled&lt;/code&gt;：事件禁用的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:read-only&lt;/code&gt;：只读的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:read-write&lt;/code&gt;：可读可写的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:blank&lt;/code&gt;：输入为空的表单元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:current()&lt;/code&gt;：浏览中的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:past()&lt;/code&gt;：已浏览的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:future()&lt;/code&gt;：未浏览的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;结构伪类&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:root&lt;/code&gt;：文档的根元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:empty&lt;/code&gt;：无子元素的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:first-letter&lt;/code&gt;：元素的首字母；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:first-line&lt;/code&gt;：元素的首行；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:nth-child(n)&lt;/code&gt;：元素中指定顺序索引的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:nth-last-child(n)&lt;/code&gt;：元素中指定逆序索引的元素；；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:first-child&lt;/code&gt;：元素中为首的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:last-child&lt;/code&gt; ：元素中为尾的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:only-child&lt;/code&gt;：父元素仅有该元素的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:nth-of-type(n)&lt;/code&gt;：标签中指定顺序索引的标签；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:nth-last-of-type(n)&lt;/code&gt;：标签中指定逆序索引的标签；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:first-of-type&lt;/code&gt; ：标签中为首的标签；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:last-of-type&lt;/code&gt;：标签中为尾标签；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;:only-of-type&lt;/code&gt;：父元素仅有该标签的标签；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;伪元素&#34;&gt;伪元素&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;::before&lt;/code&gt;：在元素前插入内容；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;::after&lt;/code&gt;：在元素后插入内容；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;优先级&#34;&gt;优先级&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192225.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;优先级就是分配给指定的 CSS 声明的一个权重，它由匹配的选择器中的每一种选择器类型的数值决定。为了记忆，可以把权重分成如下几个等级，数值越大的权重越高：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;10000：!important；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;01000：内联样式；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;00100：ID 选择器；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;00010：类选择器、伪类选择器、属性选择器；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;00001：元素选择器、伪元素选择器；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;00000：通配选择器、后代选择器、兄弟选择器；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以看到内联样式（通过元素中 style 属性定义的样式）的优先级大于任何选择器；而给属性值加上 &lt;code&gt;!important&lt;/code&gt; 又可以把优先级提至最高，就是因为它的优先级最高，所以需要谨慎使用它，以下有些使用注意事项：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;一定要优先考虑使用样式规则的优先级来解决问题而不是 !important；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;只有在需要覆盖全站或外部 CSS 的特定页面中使用 !important；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;永远不要在你的插件中使用 !important；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;永远不要在全站范围的 CSS 代码中使用 !important；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;继承性&#34;&gt;继承性&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192218.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;在 CSS 中有一个很重要的特性就是子元素会继承父元素对应属性计算后的值。比如页面根元素 html 的文本颜色默认是黑色的，页面中的所有其他元素都将继承这个颜色，当申明了如下样式后，H1 文本将变成橙色。&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;body &amp;#123;  
    color: orange;  
&amp;#125;  
h1 &amp;#123;  
    color: inherit;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;设想一下，如果 CSS 中不存在继承性，那么我们就需要为不同文本的标签都设置一下 color，这样一来的后果就是 CSS 的文件大小就会无限增大。&lt;/p&gt;
&lt;p&gt;CSS 属性很多，但并不是所有的属性默认都是能继承父元素对应属性的，那哪些属性存在默认继承的行为呢？一定是那些不会影响到页面布局的属性，可以分为如下几类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;字体相关：&lt;code&gt;font-family&lt;/code&gt;、&lt;code&gt;font-style&lt;/code&gt;、&lt;code&gt;font-size&lt;/code&gt;、&lt;code&gt;font-weight&lt;/code&gt; 等；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文本相关：&lt;code&gt;text-align&lt;/code&gt;、&lt;code&gt;text-indent&lt;/code&gt;、&lt;code&gt;text-decoration&lt;/code&gt;、&lt;code&gt;text-shadow&lt;/code&gt;、&lt;code&gt;letter-spacing&lt;/code&gt;、&lt;code&gt;word-spacing&lt;/code&gt;、&lt;code&gt;white-space&lt;/code&gt;、&lt;code&gt;line-height&lt;/code&gt;、&lt;code&gt;color&lt;/code&gt; 等；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;列表相关：&lt;code&gt;list-style&lt;/code&gt;、&lt;code&gt;list-style-image&lt;/code&gt;、&lt;code&gt;list-style-type&lt;/code&gt;、&lt;code&gt;list-style-position&lt;/code&gt; 等；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;其他属性：&lt;code&gt;visibility&lt;/code&gt;、&lt;code&gt;cursor&lt;/code&gt; 等；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于其他默认不继承的属性也可以通过以下几个属性值来控制继承行为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;inherit&lt;/code&gt;：继承父元素对应属性的计算值；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;initial&lt;/code&gt;：应用该属性的默认值，比如 color 的默认值是 &lt;code&gt;#000&lt;/code&gt;；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;unset&lt;/code&gt;：如果属性是默认可以继承的，则取 &lt;code&gt;inherit&lt;/code&gt; 的效果，否则同 &lt;code&gt;initial&lt;/code&gt;；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;revert&lt;/code&gt;：效果等同于 &lt;code&gt;unset&lt;/code&gt;，兼容性差。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;文档流&#34;&gt;文档流&lt;/h3&gt;
&lt;p&gt;在 CSS 的世界中，会把内容按照从左到右、从上到下的顺序进行排列显示。正常情况下会把页面分割成一行一行的显示，而每行又可能由多列组成，所以从视觉上看起来就是从上到下从左到右，而这就是 CSS 中的流式布局，又叫文档流。文档流就像水一样，能够自适应所在的容器，一般它有如下几个特性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;块级元素默认会占满整行，所以多个块级盒子之间是从上到下排列的；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内联元素默认会在一行里一列一列的排布，当一行放不下的时候，会自动切换到下一行继续按照列排布；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;如何脱离文档流呢？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;脱流文档流指节点脱流正常文档流后，在正常文档流中的其他节点将忽略该节点并填补其原先空间。文档一旦脱流，计算其父节点高度时不会将其高度纳入，脱流节点不占据空间。有两种方式可以让元素脱离文档流：浮动和定位。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用浮动（float）会将元素脱离文档流，移动到容器左/右侧边界或者是另一个浮动元素旁边，该浮动元素之前占用的空间将被别的元素填补，另外浮动之后所占用的区域不会和别的元素之间发生重叠；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用绝对定位（&lt;code&gt;position: absolute;&lt;/code&gt;）或者固定定位（&lt;code&gt;position: fixed;&lt;/code&gt;）也会使得元素脱离文档流，且空出来的位置将自动被后续节点填补。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;盒模型&#34;&gt;盒模型&lt;/h3&gt;
&lt;p&gt;在 CSS 中任何元素都可以看成是一个盒子，而一个盒子是由 4 部分组成的：内容（content）、内边距（padding）、边框（border）和外边距（margin）。&lt;/p&gt;
&lt;p&gt;盒模型有 2 种：标准盒模型和 IE 盒模型，本别是由 W3C 和 IExplore 制定的标准。&lt;/p&gt;
&lt;p&gt;如果给某个元素设置如下样式：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;.box &amp;#123;  
    width: 200px;  
    height: 200px;  
    padding: 10px;  
    border: 1px solid #eee;  
    margin: 10px;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;标准盒模型认为：盒子的实际尺寸 = 内容（设置的宽/高） + 内边距 + 边框&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192143.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;所以 &lt;code&gt;.box&lt;/code&gt; 元素内容的宽度就为 &lt;code&gt;200px&lt;/code&gt;，而实际的宽度则是 &lt;code&gt;width&lt;/code&gt; + &lt;code&gt;padding-left&lt;/code&gt; + &lt;code&gt;padding-right&lt;/code&gt; + &lt;code&gt;border-left-width&lt;/code&gt; + &lt;code&gt;border-right-width&lt;/code&gt; = 200 + 10 + 10 + 1 + 1 = 222。&lt;/p&gt;
&lt;p&gt;IE 盒模型认为：盒子的实际尺寸 = 设置的宽/高 = 内容 + 内边距 + 边框&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192137.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.box&lt;/code&gt; 元素所占用的实际宽度为 &lt;code&gt;200px&lt;/code&gt;，而内容的真实宽度则是 &lt;code&gt;width&lt;/code&gt; - &lt;code&gt;padding-left&lt;/code&gt; - &lt;code&gt;padding-right&lt;/code&gt; - &lt;code&gt;border-left-width&lt;/code&gt; - &lt;code&gt;border-right-width&lt;/code&gt; = 200 - 10 - 10 - 1 - 1 = 178。&lt;/p&gt;
&lt;p&gt;现在高版本的浏览器基本上默认都是使用标准盒模型，而像 IE6 这种老古董才是默认使用 IE 盒模型的。&lt;/p&gt;
&lt;p&gt;在  CSS3 中新增了一个属性 &lt;code&gt;box-sizing&lt;/code&gt;，允许开发者来指定盒子使用什么标准，它有 2 个值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;content-box&lt;/code&gt;：标准盒模型；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;border-box&lt;/code&gt;：IE 盒模型；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;视觉格式化模型&#34;&gt;视觉格式化模型&lt;/h3&gt;
&lt;p&gt;视觉格式化模型（Visual formatting model）是用来处理和在视觉媒体上显示文档时使用的计算规则。CSS 中一切皆盒子，而视觉格式化模型简单来理解就是规定这些盒子应该怎么样放置到页面中去，这个模型在计算的时候会依赖到很多的因素，比如：盒子尺寸、盒子类型、定位方案（是浮动还是定位）、兄弟元素或者子元素以及一些别的因素。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192137.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Visual formatting model&lt;/p&gt;
&lt;p&gt;从上图中可以看到视觉格式化模型涉及到的内容很多，有兴趣深入研究的可以结合上图看这个 W3C 的文档 Visual formatting model[6]。所以这里就简单介绍下盒子类型。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192129.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;盒子类型由 display 决定，同时给一个元素设置 display 后，将会决定这个盒子的 2 个显示类型（display type）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;outer display type（对外显示）：决定了该元素本身是如何布局的，即参与何种格式化上下文；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;inner display type（对内显示）：其实就相当于把该元素当成了容器，规定了其内部子元素是如何布局的，参与何种格式化上下文；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;outer-display-type&#34;&gt;outer display type&lt;/h4&gt;
&lt;p&gt;对外显示方面，盒子类型可以分成 2 类：block-level box（块级盒子） 和 inline-level box（行内级盒子）。&lt;/p&gt;
&lt;p&gt;依据上图可以列出都有哪些块级和行内级盒子：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;块级盒子：display 为 block、list-item、table、flex、grid、flow-root 等；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;行内级盒子：display 为 inline、inline-block、inline-table 等；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所有块级盒子都会参与 BFC，呈现垂直排列；而所有行内级盒子都参会 IFC，呈现水平排列。&lt;/p&gt;
&lt;p&gt;除此之外，block、inline 和 inline-block 还有什么更具体的区别呢？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;block&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;占满一行，默认继承父元素的宽度；多个块元素将从上到下进行排列；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置 width/height 将会生效；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置 padding 和 margin 将会生效；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;inline&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;不会占满一行，宽度随着内容而变化；多个 inline 元素将按照从左到右的顺序在一行里排列显示，如果一行显示不下，则自动换行；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置 width/height 将不会生效；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置竖直方向上的 padding 和 margin 将不会生效；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;inline-block&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;是行内块元素，不单独占满一行，可以看成是能够在一行里进行左右排列的块元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置 width/height 将会生效；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置 padding 和 margin 将会生效；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;inner-display-type&#34;&gt;inner display type&lt;/h4&gt;
&lt;p&gt;对内方面，其实就是把元素当成了容器，里面包裹着文本或者其他子元素。container box 的类型依据 display 的值不同，分为 4 种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;block container：建立 BFC 或者 IFC；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;flex container：建立 FFC；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;grid container：建立 GFC;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ruby container：接触不多，不做介绍。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;值得一提的是如果把 img 这种替换元素（replaced element）申明为 block 是不会产生 container box 的，因为替换元素比如 img 设计的初衷就仅仅是通过 src 把内容替换成图片，完全没考虑过会把它当成容器。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CSS 原理 - 你所不知道的 display[7]&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;格式化上下文[8]&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;格式化上下文&#34;&gt;格式化上下文&lt;/h3&gt;
&lt;p&gt;格式化上下文（Formatting Context）是 CSS2.1 规范中的一个概念，大概说的是页面中的一块渲染区域，规定了渲染区域内部的子元素是如何排版以及相互作用的。&lt;/p&gt;
&lt;p&gt;不同类型的盒子有不同格式化上下文，大概有这 4 类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;BFC (Block Formatting Context) 块级格式化上下文；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IFC (Inline Formatting Context) 行内格式化上下文；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;FFC (Flex Formatting Context) 弹性格式化上下文；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;GFC (Grid Formatting Context) 格栅格式化上下文；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中 BFC 和 IFC 在 CSS 中扮演着非常重要的角色，因为它们直接影响了网页布局，所以需要深入理解其原理。&lt;/p&gt;
&lt;h4 id=&#34;bfc&#34;&gt;BFC&lt;/h4&gt;
&lt;p&gt;块格式化上下文，它是一个独立的渲染区域，只有块级盒子参与，它规定了内部的块级盒子如何布局，并且与这个区域外部毫不相干。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192122.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;图来源于 yachen168&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BFC 渲染规则&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;内部的盒子会在垂直方向，一个接一个地放置；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;盒子垂直方向的距离由 margin 决定，属于同一个 BFC 的两个相邻盒子的 margin 会发生重叠；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每个元素的 margin 的左边，与包含块 border 的左边相接触(对于从左往右的格式化，否则相反)，即使存在浮动也是如此；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;BFC 的区域不会与 float 盒子重叠；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;BFC 就是页面上的一个隔离的独立容器，容器里面的子元素不会影响到外面的元素。反之也如此。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;计算 BFC 的高度时，浮动元素也参与计算。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;如何创建 BFC？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;根元素：html&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;非溢出的可见元素：overflow 不为 visible&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置浮动：float 属性不为 none&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置定位：position 为 absolute 或 fixed&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;定义成块级的非块级元素：display: inline-block/table-cell/table-caption/flex/inline-flex/grid/inline-grid&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;BFC 应用场景&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;1、 自适应两栏布局&lt;/p&gt;
&lt;p&gt;应用原理：BFC 的区域不会和浮动区域重叠，所以就可以把侧边栏固定宽度且左浮动，而对右侧内容触发 BFC，使得它的宽度自适应该行剩余宽度。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192107.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;&amp;lt;div class&amp;#x3D;&amp;quot;layout&amp;quot;&amp;gt;  
    &amp;lt;div class&amp;#x3D;&amp;quot;aside&amp;quot;&amp;gt;aside&amp;lt;&amp;#x2F;div&amp;gt;  
    &amp;lt;div class&amp;#x3D;&amp;quot;main&amp;quot;&amp;gt;main&amp;lt;&amp;#x2F;div&amp;gt;  
&amp;lt;&amp;#x2F;div&amp;gt;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;.aside &amp;#123;  
    float: left;  
    width: 100px;  
&amp;#125;  
.main &amp;#123;  
    &amp;lt;!-- 触发 BFC --&amp;gt;  
    overflow: auto;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;2、清除内部浮动&lt;/p&gt;
&lt;p&gt;浮动造成的问题就是父元素高度坍塌，所以清除浮动需要解决的问题就是让父元素的高度恢复正常。而用     BFC 清除浮动的原理就是：计算 BFC 的高度时，浮动元素也参与计算。只要触发父元素的 BFC 即可。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192102.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;.parent &amp;#123;
    overflow: hidden;
&amp;#125;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;3、 防止垂直 margin 合并&lt;/p&gt;
&lt;p&gt;BFC 渲染原理之一：同一个 BFC 下的垂直 margin 会发生合并。所以如果让 2 个元素不在同一个 BFC 中即可阻止垂直 margin 合并。那如何让 2 个相邻的兄弟元素不在同一个 BFC 中呢？可以给其中一个元素外面包裹一层，然后触发其包裹层的 BFC，这样一来 2 个元素就不会在同一个 BFC 中了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912192012.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;&amp;lt;div class&amp;#x3D;&amp;quot;layout&amp;quot;&amp;gt;  
	&amp;lt;div class&amp;#x3D;&amp;quot;a&amp;quot;&amp;gt;a&amp;lt;&amp;#x2F;div&amp;gt;  
    &amp;lt;div class&amp;#x3D;&amp;quot;contain-b&amp;quot;&amp;gt;  
        &amp;lt;div class&amp;#x3D;&amp;quot;b&amp;quot;&amp;gt;b&amp;lt;&amp;#x2F;div&amp;gt;  
    &amp;lt;&amp;#x2F;div&amp;gt;  
&amp;lt;&amp;#x2F;div&amp;gt;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;.demo3 .a,  
.demo3 .b &amp;#123;  
    border: 1px solid #999;  
    margin: 10px;  
&amp;#125;  
.contain-b &amp;#123;  
    overflow: hidden;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;针对以上 3 个 示例 ，可以结合这个 BFC 应用示例 配合观看更佳。&lt;/p&gt;
&lt;p&gt;参考：CSS 原理 - Formatting Context[9]&lt;/p&gt;
&lt;h4 id=&#34;ifc&#34;&gt;IFC&lt;/h4&gt;
&lt;p&gt;IFC 的形成条件非常简单，块级元素中仅包含内联级别元素，需要注意的是当IFC中有块级元素插入时，会产生两个匿名块将父元素分割开来，产生两个 IFC。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160820.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IFC 渲染规则&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;子元素在水平方向上一个接一个排列，在垂直方向上将以容器顶部开始向下排列；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;节点无法声明宽高，其中 margin 和 padding 在水平方向有效在垂直方向无效；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;节点在垂直方向上以不同形式对齐；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;能把在一行上的框都完全包含进去的一个矩形区域，被称为该行的线盒（line box）。线盒的宽度是由包含块（containing box）和与其中的浮动来决定；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IFC 中的 line box 一般左右边贴紧其包含块，但 float 元素会优先排列。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IFC 中的 line box 高度由 line-height 计算规则来确定，同个 IFC 下的多个 line box 高度可能会不同；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当内联级盒子的总宽度少于包含它们的 line box 时，其水平渲染规则由 text-align 属性值来决定；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当一个内联盒子超过父元素的宽度时，它会被分割成多盒子，这些盒子分布在多个 line box 中。如果子元素未设置强制换行的情况下，inline box 将不可被分割，将会溢出父元素。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;针对如上的 IFC 渲染规则，你是不是可以分析下下面这段代码的 IFC 环境是怎么样的呢？&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;p&amp;gt;It can get &amp;lt;strong&amp;gt;very complicated&amp;lt;/storng&amp;gt; once you start looking into it.&amp;lt;/p&amp;gt;   &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160815.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;对应上面这样一串 HTML 分析如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;p 标签是一个 block container，对内将产生一个 IFC；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;由于一行没办法显示完全，所以产生了 2 个线盒（line box）；线盒的宽度就继承了 p 的宽度；高度是由里面的内联盒子的 line-height 决定；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It can get：匿名的内联盒子；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;very complicated：strong 标签产生的内联盒子；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;once you start：匿名的内联盒子；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;looking into it.：匿名的内联盒子。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;参考：Inline formatting contexts[10]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IFC 应用场景&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;水平居中：当一个块要在环境中水平居中时，设置其为 inline-block 则会在外层产生 IFC，通过 text-align 则可以使其水平居中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;垂直居中：创建一个 IFC，用其中一个元素撑开父元素的高度，然后设置其 vertical-align: middle，其他行内元素则可以在此父元素下垂直居中。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;偷个懒，demo 和图我就不做了。&lt;/p&gt;
&lt;h3 id=&#34;层叠上下文&#34;&gt;层叠上下文&lt;/h3&gt;
&lt;p&gt;在电脑显示屏幕上的显示的页面其实是一个三维的空间，水平方向是 X 轴，竖直方向是 Y 轴，而屏幕到眼睛的方向可以看成是 Z 轴。众 HTML 元素依据自己定义的属性的优先级在 Z 轴上按照一定的顺序排开，而这其实就是层叠上下文所要描述的东西。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160813.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;-w566&lt;/p&gt;
&lt;p&gt;我们对层叠上下文的第一印象可能要来源于 z-index，认为它的值越大，距离屏幕观察者就越近，那么层叠等级就越高，事实确实是这样的，但层叠上下文的内容远非仅仅如此：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;z-index 能够在层叠上下文中对元素的堆叠顺序其作用是必须配合定位才可以；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;除了 z-index 之外，一个元素在 Z 轴上的显示顺序还受层叠等级和层叠顺序影响；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在看层叠等级和层叠顺序之前，我们先来看下如何产生一个层叠上下文，特定的 HTML 元素或者 CSS 属性产生层叠上下文，MDN 中给出了这么一个列表，符合以下任一条件的元素都会产生层叠上下文：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;html 文档根元素&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;声明 position: absolute/relative 且 z-index 值不为 auto 的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;声明 position: fixed/sticky 的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;flex 容器的子元素，且 z-index 值不为 auto；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;grid 容器的子元素，且 z-index 值不为 auto；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;opacity 属性值小于 1 的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;mix-blend-mode 属性值不为 normal 的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;以下任意属性值不为 none 的元素：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;transform&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;filter&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;perspective&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;clip-path&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;mask / mask-image / mask-border&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;isolation 属性值为 isolate 的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;-webkit-overflow-scrolling 属性值为 touch 的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;contain 属性值为 layout、paint 或包含它们其中之一的合成值（比如 contain: strict、contain: content）的元素。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;层叠等级&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;层叠等级指节点在三维空间 Z 轴上的上下顺序。它分两种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在同一个层叠上下文中，它描述定义的是该层叠上下文中的层叠上下文元素在 Z 轴上的上下顺序；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在其他普通元素中，它描述定义的是这些普通元素在 Z 轴上的上下顺序；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;普通节点的层叠等级优先由其所在的层叠上下文决定，层叠等级的比较只有在当前层叠上下文中才有意义，脱离当前层叠上下文的比较就变得无意义了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;层叠顺序&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在同一个层叠上下文中如果有多个元素，那么他们之间的层叠顺序是怎么样的呢？&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160807.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;以下这个列表越往下层叠优先级越高，视觉上的效果就是越容易被用户看到（不会被其他元素覆盖）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;层叠上下文的 border 和 background&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;z-index &amp;lt; 0 的子节点&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;标准流内块级非定位的子节点&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;浮动非定位的子节点&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;标准流内行内非定位的子节点&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;z-index: auto/0 的子节点&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;z-index &amp;gt; 0的子节点&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;如何比较两个元素的层叠等级？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在同一个层叠上下文中，比较两个元素就是按照上图的介绍的层叠顺序进行比较。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果不在同一个层叠上下文中的时候，那就需要比较两个元素分别所处的层叠上下文的等级。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果两个元素都在同一个层叠上下文，且层叠顺序相同，则在 HTML 中定义越后面的层叠等级越高。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;参考：彻底搞懂CSS层叠上下文、层叠等级、层叠顺序、z-index[11]&lt;/p&gt;
&lt;h3 id=&#34;值和单位&#34;&gt;值和单位&lt;/h3&gt;
&lt;p&gt;CSS 的声明是由属性和值组成的，而值的类型有许多种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;数值：长度值 ，用于指定例如元素 width、border-width、font-size 等属性的值；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;百分比：可以用于指定尺寸或长度，例如取决于父容器的 width、height 或默认的 font-size；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;颜色：用于指定 background-color、color 等；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;坐标位置：以屏幕的左上角为坐标原点定位元素的位置，比如常见的 background-position、top、right、bottom 和 left 等属性；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;函数：用于指定资源路径或背景图片的渐变，比如 url()、linear-gradient() 等；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而还有些值是需要带单位的，比如 width: 100px，这里的 px 就是表示长度的单位，长度单位除了 px 外，比较常用的还有 em、rem、vw/vh 等。那他们有什么区别呢？又应该在什么时候使用它们呢？&lt;/p&gt;
&lt;h4 id=&#34;px&#34;&gt;px&lt;/h4&gt;
&lt;p&gt;屏幕分辨率是指在屏幕的横纵方向上的像素点数量，比如分辨率 1920×1080 意味着水平方向含有 1920 个像素数，垂直方向含有 1080 个像素数。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160800.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;而 px 表示的是 CSS 中的像素，在 CSS 中它是绝对的长度单位，也是最基础的单位，其他长度单位会自动被浏览器换算成 px。但是对于设备而言，它其实又是相对的长度单位，比如宽高都为 2px，在正常的屏幕下，其实就是 4 个像素点，而在设备像素比(devicePixelRatio) 为 2 的 Retina 屏幕下，它就有 16 个像素点。所以屏幕尺寸一致的情况下，屏幕分辨率越高，显示效果就越细腻。&lt;/p&gt;
&lt;p&gt;讲到这里，还有一些相关的概念需要理清下：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;设备像素（Device pixels）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;设备屏幕的物理像素，表示的是屏幕的横纵有多少像素点；和屏幕分辨率是差不多的意思。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;设备像素比（DPR）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;设备像素比表示 1 个 CSS 像素等于几个物理像素。&lt;/p&gt;
&lt;p&gt;计算公式：DPR = 物理像素数 / 逻辑像素数；&lt;/p&gt;
&lt;p&gt;在浏览器中可以通过 window.devicePixelRatio 来获取当前屏幕的 DPR。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;像素密度（DPI/PPI）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;像素密度也叫显示密度或者屏幕密度，缩写为 DPI(Dots Per Inch) 或者 PPI(Pixel Per Inch)。从技术角度说，PPI 只存在于计算机显示领域，而 DPI 只出现于打印或印刷领域。&lt;/p&gt;
&lt;p&gt;计算公式：像素密度 = 屏幕对角线的像素尺寸 / 物理尺寸&lt;/p&gt;
&lt;p&gt;比如，对于分辨率为 750 * 1334 的 iPhone 6 来说，它的像素密度为：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Math.sqrt(750 * 750 + 1334 * 1334) / 4.7 = 326ppi   &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;设备独立像素（DIP）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;DIP 是特别针对 Android设备而衍生出来的，原因是安卓屏幕的尺寸繁多，因此为了显示能尽量和设备无关，而提出的这个概念。它是基于屏幕密度而计算的，认为当屏幕密度是 160 的时候，px = DIP。&lt;/p&gt;
&lt;p&gt;计算公式：dip = px * 160 / dpi&lt;/p&gt;
&lt;h4 id=&#34;em&#34;&gt;em&lt;/h4&gt;
&lt;p&gt;em 是 CSS 中的相对长度单位中的一个。居然是相对的，那它到底是相对的谁呢？它有 2 层意思：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在 font-size 中使用是相对于&lt;strong&gt;父元素&lt;/strong&gt;的 font-size 大小，比如父元素 font-size: 16px，当给子元素指定 font-size: 2em 的时候，经过计算后它的字体大小会是 32px；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在其他属性中使用是相对于自身的字体大小，如 width/height/padding/margin 等；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们都知道每个浏览器都会给 HTML 根元素 html 设置一个默认的 font-size，而这个值通常是 16px。这也就是为什么 1em = 16px 的原因所在了。&lt;/p&gt;
&lt;p&gt;em 在计算的时候是会层层计算的，比如：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;&amp;lt;div&amp;gt;  
    &amp;lt;p&amp;gt;&amp;lt;&amp;#x2F;p&amp;gt;  
&amp;lt;&amp;#x2F;div&amp;gt;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;div &amp;#123; font-size: 2em; &amp;#125;  
p &amp;#123; font-size: 2em; &amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;对于如上一个结构的 HTML，由于根元素 html 的字体大小是 16px，所以 p 标签最终计算出来后的字体大小会是 16 * 2 * 2 = 64px&lt;/p&gt;
&lt;h4 id=&#34;rem&#34;&gt;rem&lt;/h4&gt;
&lt;p&gt;rem(root em) 和 em 一样，也是一个相对长度单位，不过 rem 相对的是 HTML 的根元素 html。&lt;/p&gt;
&lt;p&gt;rem 由于是基于 html 的 font-size 来计算，所以通常用于自适应网站或者 H5 中。&lt;/p&gt;
&lt;p&gt;比如在做 H5 的时候，前端通常会让 UI 给 750px 宽的设计图，而在开发的时候可以基于 iPhone X 的尺寸 375px * 812px 来写页面，这样一来的话，就可以用下面的 JS 依据当前页面的视口宽度自动计算出根元素 html 的基准 font-size 是多少。&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;(function (doc, win) &amp;#123;  
    var docEl &amp;#x3D; doc.documentElement,  
        resizeEvt &amp;#x3D; &amp;#39;orientationchange&amp;#39; in window ? &amp;#39;orientationchange&amp;#39; : &amp;#39;resize&amp;#39;,  
        psdWidth &amp;#x3D; 750,  &amp;#x2F;&amp;#x2F; 设计图宽度  
        recalc &amp;#x3D; function () &amp;#123;  
            var clientWidth &amp;#x3D; docEl.clientWidth;  
            if ( !clientWidth ) return;  
            if ( clientWidth &amp;gt;&amp;#x3D; 640 ) &amp;#123;  
                docEl.style.fontSize &amp;#x3D; 200 * ( 640 &amp;#x2F; psdWidth ) + &amp;#39;px&amp;#39;;  
            &amp;#125; else &amp;#123;  
                docEl.style.fontSize &amp;#x3D; 200 * ( clientWidth &amp;#x2F; psdWidth ) + &amp;#39;px&amp;#39;;  
            &amp;#125;  
        &amp;#125;;

    if ( !doc.addEventListener ) return;  
    &amp;#x2F;&amp;#x2F; 绑定事件的时候最好配合防抖函数  
    win.addEventListener( resizeEvt, debounce(recalc, 1000), false );  
    doc.addEventListener( &amp;#39;DOMContentLoaded&amp;#39;, recalc, false );

        function debounce(func, wait) &amp;#123;  
        var timeout;  
        return function () &amp;#123;  
            var context &amp;#x3D; this;  
            var args &amp;#x3D; arguments;  
            clearTimeout(timeout)  
            timeout &amp;#x3D; setTimeout(function()&amp;#123;  
                func.apply(context, args)  
            &amp;#125;, wait);  
        &amp;#125;  
    &amp;#125;  
&amp;#125;)(document, window);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;比如当视口是 375px 的时候，经过计算 html 的 font-size 会是 100px，这样有什么好处呢？好处就是方便写样式，比如从设计图量出来的 header 高度是 50px 的，那我们写样式的时候就可以直接写：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;header &amp;#123;  
    height: 0.5rem;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160740.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;每个从设计图量出来的尺寸只要除于 100 即可得到当前元素的 rem 值，都不用经过计算，非常方便。偷偷告诉你，如果你把上面那串计算 html 标签 font-size 的 JS 代码中的 200 替换成 2，那在计算 rem 的时候就不需要除于 100 了，从设计图量出多大 px，就直接写多少个 rem。&lt;/p&gt;
&lt;h4 id=&#34;vw-vh&#34;&gt;vw/vh&lt;/h4&gt;
&lt;p&gt;vw 和 vh 分别是相对于屏幕视口宽度和高度而言的长度单位：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1vw = 视口宽度均分成 100 份中 1 份的长度；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1vh = 视口高度均分成 100 份中 1 份的长度；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 JS 中 100vw = window.innerWidth，100vh = window.innerHeight。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160734.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;vw/vh 的出现使得多了一种写自适应布局的方案，开发者不再局限于 rem 了。&lt;/p&gt;
&lt;p&gt;相对视口的单位，除了 vw/vh 外，还有 vmin 和 vmax：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;vmin：取 vw 和 vh 中值较小的；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;vmax：取 vw 和 vh 中值较大的；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;颜色体系&#34;&gt;颜色体系&lt;/h3&gt;
&lt;p&gt;CSS 中用于表示颜色的值种类繁多，足够构成一个体系，所以这里就专门拿出一个小节来讲解它。&lt;/p&gt;
&lt;p&gt;根据 CSS 颜色草案[12] 中提到的颜色值类型，大概可以把它们分为这几类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;颜色关键字&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;transparent 关键字&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;currentColor 关键字&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RGB 颜色&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;HSL 颜色&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;颜色关键字&#34;&gt;颜色关键字&lt;/h4&gt;
&lt;p&gt;颜色关键字（color keywords）是不区分大小写的标识符，它表示一个具体的颜色，比如 white（白），黑（black）等；&lt;/p&gt;
&lt;p&gt;可接受的关键字列表在CSS的演变过程中发生了改变：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CSS 标准 1 只接受 16 个基本颜色，称为 VGA 颜色，因为它们来源于 VGA 显卡所显示的颜色集合而被称为 VGA colors （视频图形阵列色彩）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CSS 标准 2 增加了 orange 关键字。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从一开始，浏览器接受其它的颜色，由于一些早期浏览器是 X11 应用程序，这些颜色大多数是 X11 命名的颜色列表，虽然有一点不同。SVG 1.0 是首个正式定义这些关键字的标准；CSS 色彩标准 3 也正式定义了这些关键字。它们经常被称作扩展的颜色关键字， X11 颜色或 SVG 颜色 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CSS 颜色标准 4 添加可 rebeccapurple 关键字来纪念 web 先锋 Eric Meyer。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如下这张图是 16 个基础色，又叫 VGA 颜色。截止到目前为止 CSS 颜色关键字总共有 146 个，这里可以查看 完整的色彩关键字列表[13]。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160733.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;VGA 颜色&lt;/p&gt;
&lt;p&gt;需要注意的是如果声明的时候的颜色关键字是错误的，浏览器会忽略它。&lt;/p&gt;
&lt;h4 id=&#34;transparent-关键字&#34;&gt;transparent 关键字&lt;/h4&gt;
&lt;p&gt;transparent 关键字表示一个完全透明的颜色，即该颜色看上去将是背景色。从技术上说，它是带有 alpha 通道为最小值的黑色，是 rgba(0,0,0,0) 的简写。&lt;/p&gt;
&lt;p&gt;透明关键字有什么应用场景呢？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实现三角形&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;下面这个图是用 4 条边框填充的正方形，看懂了它你大概就知道该如何用 CSS 写三角形了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160726.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;div &amp;#123;  
    border-top-color: #ffc107;  
    border-right-color: #00bcd4;  
    border-bottom-color: #e26b6b;  
    border-left-color: #cc7cda;  
    border-width: 50px;  
    border-style: solid;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;用 transparent 实现三角形的原理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;首先宽高必须是 0px，通过边框的粗细来填充内容；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;那条边需要就要加上颜色，而不需要的边则用 transparent；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;想要什么样姿势的三角形，完全由上下左右 4 条边的中有颜色的边和透明的边的位置决定；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;等腰三角形：设置一条边有颜色，然后紧挨着的 2 边是透明，且宽度是有颜色边的一半；直角三角形：设置一条边有颜色，然后紧挨着的任何一边透明即可。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;看下示例：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160719.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;增大点击区域&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;常常在移动端的时候点击的按钮的区域特别小，但是由于现实效果又不太好把它做大，所以常用的一个手段就是通过透明的边框来增大按钮的点击区域：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;.btn &amp;#123;  
    border: 5px solid transparent;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h4 id=&#34;currentcolor-关键字&#34;&gt;currentColor 关键字&lt;/h4&gt;
&lt;p&gt;currentColor 会取当前元素继承父级元素的文本颜色值或声明的文本颜色值，即 computed 后的 color 值。&lt;/p&gt;
&lt;p&gt;比如，对于如下 CSS，该元素的边框颜色会是 red：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;.btn &amp;#123;  
    color: red;  
    border: 1px solid currentColor;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h4 id=&#34;rgb-a-颜色&#34;&gt;RGB[A] 颜色&lt;/h4&gt;
&lt;p&gt;RGB[A] 颜色是由 R(red)-G(green)-B(blue)-A(alpha) 组成的色彩空间。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160707.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;在 CSS 中，它有两种表示形式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;十六进制符号；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;函数符；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;十六进制符号&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;RGB 中的每种颜色的值范围是 00~ff，值越大表示颜色越深。所以一个颜色正常是 6 个十六进制字符加上 # 组成，比如红色就是 #ff0000。&lt;/p&gt;
&lt;p&gt;如果 RGB 颜色需要加上不透明度，那就需要加上 alpha 通道的值，它的范围也是 00~ff，比如一个带不透明度为 67% 的红色可以这样写 #ff0000aa。&lt;/p&gt;
&lt;p&gt;使用十六进制符号表示颜色的时候，都是用 2 个十六进制表示一个颜色，如果这 2 个字符相同，还可以缩减成只写 1 个，比如，红色 #f00；带 67% 不透明度的红色 #f00a。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;函数符&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当 RGB 用函数表示的时候，每个值的范围是 0~255 或者 0%~100%，所以红色是 rgb(255, 0, 0)， 或者 rgb(100%, 0, 0)。&lt;/p&gt;
&lt;p&gt;如果需要使用函数来表示带不透明度的颜色值，值的范围是 0~1 及其之间的小数或者 0%~100%，比如带 67% 不透明度的红色是 rgba(255, 0, 0, 0.67) 或者 rgba(100%, 0%, 0%, 67%)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“&lt;/p&gt;
&lt;p&gt;需要注意的是 RGB 这 3 个颜色值需要保持一致的写法，要嘛用数字要嘛用百分比，而不透明度的值的可以不用和 RGB 保持一致写法。比如 rgb(100%, 0, 0) 这个写法是无效的；而 rgb(100%, 0%, 0%, 0.67) 是有效的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在第 4 代 CSS 颜色标准中，新增了一种新的函数写法，即可以把 RGB 中值的分隔逗号改成空格，而把 RGB 和 alpha 中的逗号改成 /，比如带 67% 不透明度的红色可以这样写 rgba(255 0 0 / 0.67)。另外还把 rgba 的写法合并到 rgb 函数中了，即 rgb 可以直接写带不透明度的颜色。&lt;/p&gt;
&lt;h4 id=&#34;hsl-a-颜色&#34;&gt;HSL[A] 颜色&lt;/h4&gt;
&lt;p&gt;HSL[A] 颜色是由色相(hue)-饱和度(saturation)-亮度(lightness)-不透明度组成的颜色体系。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160701.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;色相（H）是色彩的基本属性，值范围是 0~360 或者 0deg~360deg， 0 (或 360) 为红色, 120 为绿色, 240 为蓝色；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;饱和度（S）是指色彩的纯度，越高色彩越纯，低则逐渐变灰，取 0~100% 的数值；0% 为灰色， 100% 全色；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;亮度（L），取 0~100%，0% 为暗，100% 为白；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不透明度（A），取 0&lt;sub&gt;100%，或者0&lt;/sub&gt;1及之间的小数；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;写法上可以参考 RGB 的写法，只是参数的值不一样。&lt;/p&gt;
&lt;p&gt;给一个按钮设置不透明度为 67% 的红色的 color 的写法，以下全部写法效果一致：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;button &amp;#123;  
    color: #ff0000aa;  
    color: #f00a;  
    color: rgba(255, 0, 0, 0.67);  
    color: rgb(100% 0% 0% &amp;#x2F; 67%);  
    color: hsla(0, 100%, 50%, 67%);  
    color: hsl(0deg 100% 50% &amp;#x2F; 67%);  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;小提示：在 Chrome DevTools 中可以按住 shift + 鼠标左键可以切换颜色的表示方式。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912160642.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;媒体查询&#34;&gt;媒体查询&lt;/h3&gt;
&lt;p&gt;媒体查询是指针对不同的设备、特定的设备特征或者参数进行定制化的修改网站的样式。&lt;/p&gt;
&lt;p&gt;你可以通过给 &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; 加上 media 属性来指定该样式文件只能对什么设备生效，不指定的话默认是 all，即对所有设备都生效：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;&amp;lt;link rel&amp;#x3D;&amp;quot;stylesheet&amp;quot; src&amp;#x3D;&amp;quot;styles.css&amp;quot; media&amp;#x3D;&amp;quot;screen&amp;quot; &amp;#x2F;&amp;gt;  
&amp;lt;link rel&amp;#x3D;&amp;quot;stylesheet&amp;quot; src&amp;#x3D;&amp;quot;styles.css&amp;quot; media&amp;#x3D;&amp;quot;print&amp;quot; &amp;#x2F;&amp;gt;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;都支持哪些设备类型？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;all：适用于所有设备；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;print：适用于在打印预览模式下在屏幕上查看的分页材料和文档；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;screen：主要用于屏幕；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;speech：主要用于语音合成器。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;需要注意的是：通过 media 指定的  资源尽管不匹配它的设备类型，但是浏览器依然会加载它。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155838.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;除了通过 &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; 让指定设备生效外，还可以通过 &lt;code&gt;@media&lt;/code&gt; 让 CSS 规则在特定的条件下才能生效。响应式页面就是使用了 @media 才让一个页面能够同时适配 PC、Pad 和手机端。&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;@media (min-width: 1000px) &amp;#123;&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;媒体查询支持逻辑操作符：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;and：查询条件都满足的时候才生效；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;not：查询条件取反；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;only：整个查询匹配的时候才生效，常用语兼容旧浏览器，使用时候必须指定媒体类型；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;逗号或者 or：查询条件满足一项即可匹配；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;媒体查询还支持众多的媒体特性[14]，使得它可以写出很复杂的查询条件：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;&amp;#x2F;* 用户设备的最小高度为680px或为纵向模式的屏幕设备 *&amp;#x2F;  
@media (min-height: 680px), screen and (orientation: portrait) &amp;#123;&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h2 id=&#34;常见需求&#34;&gt;常见需求&lt;/h2&gt;
&lt;h3 id=&#34;自定义属性&#34;&gt;自定义属性&lt;/h3&gt;
&lt;p&gt;之前我们通常是在预处理器里才可以使用变量，而现在 CSS 里也支持了变量的用法。通过自定义属性就可以在想要使用的地方引用它。&lt;/p&gt;
&lt;p&gt;自定义属性也和普通属性一样具有级联性，申明在 :root 下的时候，在全文档范围内可用，而如果是在某个元素下申明自定义属性，则只能在它及它的子元素下才可以使用。&lt;/p&gt;
&lt;p&gt;自定义属性必须通过 &lt;code&gt;--x&lt;/code&gt; 的格式申明，比如：–theme-color: red; 使用自定义属性的时候，需要用 var 函数。比如：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;&amp;lt;!-- 定义自定义属性 --&amp;gt;  
:root &amp;#123;  
    --theme-color: red;  
&amp;#125;

&amp;lt;!-- 使用变量 --&amp;gt;  
h1 &amp;#123;  
    color: var(--theme-color);  
&amp;#125;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155818.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;上图这个是使用 CSS 自定义属性配合 JS 实现的动态调整元素的 box-shadow，具体可以看这个 codepen demo。&lt;/p&gt;
&lt;h3 id=&#34;1px-边框解决方案&#34;&gt;1px 边框解决方案&lt;/h3&gt;
&lt;p&gt;Retina 显示屏比普通的屏幕有着更高的分辨率，所以在移动端的 1px 边框就会看起来比较粗，为了美观通常需要把这个线条细化处理。这里有篇文章列举了 7 中方案可以参考一下：7种方法解决移动端Retina屏幕1px边框问题[15]&lt;/p&gt;
&lt;p&gt;而这里附上最后一种通过伪类和 transform 实现的相对完美的解决方案：&lt;/p&gt;
&lt;p&gt;只设置单条底部边框：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;.scale-1px-bottom &amp;#123;  
    position: relative;  
    border:none;  
&amp;#125;  
.scale-1px-bottom::after &amp;#123;  
    content: &amp;#39;&amp;#39;;  
    position: absolute;  
    left: 0;  
    bottom: 0;  
    background: #000;  
    width: 100%;  
    height: 1px;  
    -webkit-transform: scaleY(0.5);  
    transform: scaleY(0.5);  
    -webkit-transform-origin: 0 0;  
    transform-origin: 0 0;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;同时设置 4 条边框：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;.scale-1px &amp;#123;  
    position: relative;  
    margin-bottom: 20px;  
    border:none;  
&amp;#125;  
.scale-1px::after &amp;#123;  
    content: &amp;#39;&amp;#39;;  
    position: absolute;  
    top: 0;  
    left: 0;  
    border: 1px solid #000;  
    -webkit-box-sizing: border-box;  
    box-sizing: border-box;  
    width: 200%;  
    height: 200%;  
    -webkit-transform: scale(0.5);  
    transform: scale(0.5);  
    -webkit-transform-origin: left top;  
    transform-origin: left top;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h3 id=&#34;清除浮动&#34;&gt;清除浮动&lt;/h3&gt;
&lt;p&gt;什么是浮动：浮动元素会脱离文档流并向左/向右浮动，直到碰到父元素或者另一个浮动元素。&lt;/p&gt;
&lt;p&gt;为什么要清楚浮动，它造成了什么问题？&lt;/p&gt;
&lt;p&gt;因为浮动元素会脱离正常的文档流，并不会占据文档流的位置，所以如果一个父元素下面都是浮动元素，那么这个父元素就无法被浮动元素所撑开，这样一来父元素就丢失了高度，这就是所谓的浮动造成的父元素高度坍塌问题。&lt;/p&gt;
&lt;p&gt;父元素高度一旦坍塌将对后面的元素布局造成影响，为了解决这个问题，所以需要清除浮动，让父元素恢复高度，那该如何做呢？&lt;/p&gt;
&lt;p&gt;这里介绍两种方法：通过 BFC 来清除、通过 clear 来清除。&lt;/p&gt;
&lt;h4 id=&#34;bfc-清除浮动&#34;&gt;BFC 清除浮动&lt;/h4&gt;
&lt;p&gt;前面介绍 BFC 的时候提到过，计算 BFC 高度的时候浮动子元素的高度也将计算在内，利用这条规则就可以清楚浮动。&lt;/p&gt;
&lt;p&gt;假设一个父元素 parent 内部只有 2 个子元素 child，且它们都是左浮动的，这个时候 parent 如果没有设置高度的话，因为浮动造成了高度坍塌，所以 parent 的高度会是 0，此时只要给 parent 创造一个 BFC，那它的高度就能恢复了。&lt;/p&gt;
&lt;p&gt;而产生 BFC 的方式很多，我们可以给父元素设置overflow: auto 来简单的实现 BFC 清除浮动，但是为了兼容 IE 最好用 overflow: hidden。&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;.parent &amp;#123;  
    overflow: hidden;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;通过 overflow: hidden 来清除浮动并不完美，当元素有阴影或存在下拉菜单的时候会被截断，所以该方法使用比较局限。&lt;/p&gt;
&lt;h4 id=&#34;通过-clear-清除浮动&#34;&gt;通过 clear 清除浮动&lt;/h4&gt;
&lt;p&gt;我先把结论贴出来：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;.clearfix &amp;#123;  
    zoom: 1;  
&amp;#125;  
.clearfix::after &amp;#123;  
    content: &amp;quot;&amp;quot;;  
    display: block;  
    clear: both;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;这种写法的核心原理就是通过 ::after 伪元素为在父元素的最后一个子元素后面生成一个内容为空的块级元素，然后通过 clear 将这个伪元素移动到所有它之前的浮动元素的后面，画个图来理解一下。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155806.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;可以结合这个 codepen demo 一起理解上图的 clear 清楚浮动原理。&lt;/p&gt;
&lt;p&gt;上面这个 demo 或者图里为了展示需要所以给伪元素的内容设置为了 ::after，实际使用的时候需要设置为空字符串，让它的高度为 0，从而父元素的高度都是由实际的子元素撑开。&lt;/p&gt;
&lt;p&gt;该方式基本上是现在人人都在用的清除浮动的方案，非常通用。&lt;/p&gt;
&lt;p&gt;参考：CSS中的浮动和清除浮动，梳理一下[16]&lt;/p&gt;
&lt;h3 id=&#34;消除浏览器默认样式&#34;&gt;消除浏览器默认样式&lt;/h3&gt;
&lt;p&gt;针对同一个类型的 HTML 标签，不同的浏览器往往有不同的表现，所以在网站制作的时候，开发者通常都是需要将这些浏览器的默认样式清除，让网页在不同的浏览器上能够保持一致。&lt;/p&gt;
&lt;p&gt;针对清除浏览器默认样式这件事，在很早之前 CSS 大师 Eric A. Meyer 就干过。它就是写一堆通用的样式用来重置浏览器默认样式，这些样式通常会放到一个命名为 reset.css 文件中。比如大师的 reset.css[17] 是这么写的：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;html, body, div, span, applet, object, iframe,  
h1, h2, h3, h4, h5, h6, p, blockquote, pre,  
a, abbr, acronym, address, big, cite, code,  
del, dfn, em, img, ins, kbd, q, s, samp,  
small, strike, strong, sub, sup, tt, var,  
b, u, i, center,  
dl, dt, dd, ol, ul, li,  
fieldset, form, label, legend,  
table, caption, tbody, tfoot, thead, tr, th, td,  
article, aside, canvas, details, embed,   
figure, figcaption, footer, header, hgroup,   
menu, nav, output, ruby, section, summary,  
time, mark, audio, video &amp;#123;  
    margin: 0;  
    padding: 0;  
    border: 0;  
    font-size: 100%;  
    font: inherit;  
    vertical-align: baseline;  
&amp;#125;  
&amp;#x2F;* HTML5 display-role reset for older browsers *&amp;#x2F;  
article, aside, details, figcaption, figure,   
footer, header, hgroup, menu, nav, section &amp;#123;  
    display: block;  
&amp;#125;  
body &amp;#123;  
    line-height: 1;  
&amp;#125;  
ol, ul &amp;#123;  
    list-style: none;  
&amp;#125;  
blockquote, q &amp;#123;  
    quotes: none;  
&amp;#125;  
blockquote:before, blockquote:after,  
q:before, q:after &amp;#123;  
    content: &amp;#39;&amp;#39;;  
    content: none;  
&amp;#125;  
table &amp;#123;  
    border-collapse: collapse;  
    border-spacing: 0;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;他的这份 reset.css 据说是被使用最广泛的重设样式的方案了。&lt;/p&gt;
&lt;p&gt;除了 reset.css 外，后来又出现了 Normalize.css[18] 。关于 Normalize.css, 其作者 necolas 专门写了一篇文章介绍了它，并谈到了它和 reset.css 的区别。这个是他写那篇文章的翻译版：让我们谈一谈 Normalize.css[19]。&lt;/p&gt;
&lt;p&gt;文章介绍到：Normalize.css 只是一个很小的CSS文件，但它在默认的 HTML 元素样式上提供了跨浏览器的高度一致性。相比于传统的 CSS reset，Normalize.css 是一种现代的、为 HTML5 准备的优质替代方案，现在已经有很多知名的框架和网站在使用它了。&lt;/p&gt;
&lt;p&gt;Normalize.css 的具体样式可以看这里 Normalize.css&lt;/p&gt;
&lt;p&gt;区别于 reset.css，Normalize.css 有如下特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;reset.css 几乎为所有标签都设置了默认样式，而 Normalize.css 则是有选择性的保护了部分有价值的默认值；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;修复了很多浏览器的 bug，而这是 reset.css 没做到的；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不会让你的调试工具变的杂乱，相反 reset.css 由于设置了很多默认值，所以在浏览器调试工具中往往会看到一大堆的继承样式，显得很杂乱；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Normalize.css 是模块化的，所以可以选择性的去掉永远不会用到的部分，比如表单的一般化；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Normalize.css 有详细的说明文档；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;长文本处理&#34;&gt;长文本处理&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;默认：字符太长溢出了容器&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155744.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;字符超出部分换行&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005529.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;字符超出位置使用连字符&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155738.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;单行文本超出省略&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005537.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;多行文本超出省略&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005552.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;查看以上这些方案的示例： codepen demo&lt;/p&gt;
&lt;p&gt;有意思的是刚好前两天看到 chokcoco 针对文本溢出也写了一篇文章，主要突出的是对整块的文本溢出处理。啥叫整块文本？比如，下面这种技术标签就是属于整块文本：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155731.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;另外他还对 iOS/Safari 做了兼容处理，感兴趣的可以去阅读下：CSS 整块文本溢出省略特性探究[20]。&lt;/p&gt;
&lt;h3 id=&#34;水平垂直居中&#34;&gt;水平垂直居中&lt;/h3&gt;
&lt;p&gt;让元素在父元素中呈现出水平垂直居中的形态，无非就 2 种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;单行的文本、inline 或者 inline-block 元素；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;固定宽高的块级盒子；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不固定宽高的块级盒子；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下列到的所有水平垂直居中方案这里写了个 codepen demo，配合示例阅读效果更佳。&lt;/p&gt;
&lt;h4 id=&#34;单行的文本、inline-或-inline-block-元素&#34;&gt;单行的文本、inline 或 inline-block 元素&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;水平居中&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;此类元素需要水平居中，则父级元素必须是块级元素(&lt;code&gt;block level&lt;/code&gt;)，且父级元素上需要这样设置样式：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;.parent &amp;#123;  
    text-align: center;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;垂直居中&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;方法一：通过设置上下内间距一致达到垂直居中的效果：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;.single-line &amp;#123;  
    padding-top: 10px;  
    padding-bottom: 10px;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;方法二：通过设置 &lt;code&gt;height&lt;/code&gt; 和 &lt;code&gt;line-height&lt;/code&gt; 一致达到垂直居中：&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-none&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;.single-line &amp;#123;  
    height: 100px;  
    line-height: 100px;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;h4 id=&#34;固定宽高的块级盒子&#34;&gt;固定宽高的块级盒子&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;方法一：absolute + 负 margin&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005614.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法二：absolute + margin auto&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155724.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法三：absolute + calc&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005620.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;不固定宽高的块级盒子&#34;&gt;不固定宽高的块级盒子&lt;/h4&gt;
&lt;p&gt;这里列了 6 种方法，参考了颜海镜 写的文章 ，其中的两种 line-height 和 writing-mode 方案看后让我惊呼：还有这种操作？学到了学到了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法一：absolute + transform&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155717.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法二：line-height + vertical-align&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005627.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法三：writing-mode&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005633.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法四：table-cell&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005634.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法五：flex&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155710.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法六：grid&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155704.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;常用布局&#34;&gt;常用布局&lt;/h3&gt;
&lt;h4 id=&#34;两栏布局（边栏定宽主栏自适应）&#34;&gt;两栏布局（边栏定宽主栏自适应）&lt;/h4&gt;
&lt;p&gt;针对以下这些方案写了几个示例： codepen demo&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法一：float + overflow（BFC 原理）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005646.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法二：float + margin&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155657.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法三：flex&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005701.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法四：grid&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155650.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;三栏布局（两侧栏定宽主栏自适应）&#34;&gt;三栏布局（两侧栏定宽主栏自适应）&lt;/h4&gt;
&lt;p&gt;针对以下这些方案写了几个示例： codepen demo&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法一：圣杯布局&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155616.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法二：双飞翼布局&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155643.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法三：float + overflow（BFC 原理）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155556.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法四：flex&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155633.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法五：grid&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155549.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;多列等高布局&#34;&gt;多列等高布局&lt;/h4&gt;
&lt;p&gt;结合示例阅读更佳：codepen demo&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法一：padding + 负margin&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1420210914005719.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法二：设置父级背景图片&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155542.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;三行布局（头尾定高主栏自适应）&#34;&gt;三行布局（头尾定高主栏自适应）&lt;/h4&gt;
&lt;p&gt;列了 4 种方法，都是基于如下的 HTML 和 CSS 的，结合示例阅读效果更佳：codepen demo&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;&amp;lt;div class&amp;#x3D;&amp;quot;layout&amp;quot;&amp;gt;  
    &amp;lt;header&amp;gt;&amp;lt;&amp;#x2F;header&amp;gt;  
    &amp;lt;main&amp;gt;  
        &amp;lt;div class&amp;#x3D;&amp;quot;inner&amp;quot;&amp;gt;&amp;lt;&amp;#x2F;div&amp;gt;  
    &amp;lt;&amp;#x2F;main&amp;gt;  
    &amp;lt;footer&amp;gt;&amp;lt;&amp;#x2F;footer&amp;gt;  
&amp;lt;&amp;#x2F;div&amp;gt;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;html,  
body,  
.layout &amp;#123;  
    height: 100%;  
&amp;#125;  
body &amp;#123;  
    margin: 0;  
&amp;#125;  
header,   
footer &amp;#123;  
    height: 50px;  
&amp;#125;  
main &amp;#123;  
    overflow-y: auto;  
&amp;#125;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;方法一：calc&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155519.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法二：absolute&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155509.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法三：flex&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155502.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法四：grid&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1220210912155500.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;结了个尾&#34;&gt;结了个尾&lt;/h2&gt;
&lt;p&gt;这是我断断续续写了 2 周完成的文章，算是自己对 CSS 的一个总结，虽然写得很长，但不足以覆盖所有 CSS 的知识，比如动画和一些 CSS3 的新特性就完全没涉及，因为这要写下来估计得有大几万字（其实就是懒 😝 ）。&lt;/p&gt;
&lt;p&gt;码字作图不易，如果喜欢或者对你有丝毫帮助的话，帮忙点个👍 哈，点赞就是我的动力。同时也希望自己能坚持认真的写下去，因为在总结提升自己的同时如果也能帮助更多的前端er，那将会让我感觉很开心。&lt;/p&gt;
&lt;h3 id=&#34;参考资料&#34;&gt;参考资料&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;[1] &lt;a href=&#34;https://developer.mozilla.org/zh-CN/docs/Web/CSS/@charset&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;@charset: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[2] &lt;a href=&#34;https://en.wikipedia.org/wiki/Byte_order_mark&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Byte order mark: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[3] &lt;a href=&#34;https://developer.mozilla.org/zh-CN/docs/Web/CSS/@import&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;@import: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[4] &lt;a href=&#34;https://developer.mozilla.org/zh-CN/docs/Web/CSS/@supports&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;@supports: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[5] &lt;a href=&#34;https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Selectors&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;MDN CSS Selectors: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[6] &lt;a href=&#34;https://www.w3.org/TR/CSS2/visuren.html&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Visual formatting model: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[7] &lt;a href=&#34;https://yachen168.github.io/article/display.html&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;CSS 原理 - 你所不知道的 display: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[8] &lt;a href=&#34;https://ithelp.ithome.com.tw/articles/10223896?sc=pt&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;格式化上下文: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[9] &lt;a href=&#34;https://yachen168.github.io/article/Formatting-context.html&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;CSS 原理 - Formatting Context: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[10] &lt;a href=&#34;https://www.w3.org/TR/CSS2/visuren.html#inline-formatting&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Inline formatting contexts: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[11] &lt;a href=&#34;https://juejin.cn/post/6844903667175260174&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;彻底搞懂CSS层叠上下文、层叠等级、层叠顺序、z-index: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[12] &lt;a href=&#34;https://drafts.csswg.org/css-color-3/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;CSS 颜色草案: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[13] &lt;a href=&#34;https://codepen.io/bulandent/pen/gOLovwL&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;完整的色彩关键字列表: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[14] &lt;a href=&#34;https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Media_queries#%E5%AA%92%E4%BD%93%E7%89%B9%E6%80%A7&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;众多的媒体特性: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[15] &lt;a href=&#34;https://www.jianshu.com/p/7e63f5a32636&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;7种方法解决移动端Retina屏幕1px边框问题: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[16] &lt;a href=&#34;https://www.jianshu.com/p/09bd5873bed4&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;CSS中的浮动和清除浮动，梳理一下: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[17] &lt;a href=&#34;https://meyerweb.com/eric/tools/css/reset/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;reset.css: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[18] &lt;a href=&#34;https://github.com/necolas/normalize.css&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Normalize.css: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[19] &lt;a href=&#34;https://jerryzou.com/posts/aboutNormalizeCss/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;让我们谈一谈 Normalize.css: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[20] &lt;a href=&#34;https://juejin.cn/post/6938583040469762055&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;CSS 整块文本溢出省略特性探究: &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;编辑整理From &lt;a href=&#34;https://mp.weixin.qq.com/s/UtYENocSsl0R10h8fww7Iw&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;https://mp.weixin.qq.com/s/UtYENocSsl0R10h8fww7Iw&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;参考-致谢&#34;&gt;参考&amp;amp;致谢&lt;/h2&gt;
&lt;h2 id=&#34;系列教程&#34;&gt;&lt;strong&gt;系列教程&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;全部文章RSS订阅&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;web系列&#34;&gt;&lt;strong&gt;web系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/web/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;&lt;strong&gt;Web 分类 RSS 订阅&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/69a052c9/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;浏览器的渲染过程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/656a0abb/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;在浏览器输入 URL 回车之后发生了什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/448f849b/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;1.5 万字 CSS 基础拾遗（核心知识、常见需求）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/80906b88/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;http 错误代码表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/8f873c0f/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;写了些浏览器增强脚本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1d87ed2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;如何防止密码泄露，保护密码安全？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/ec2cad2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ESNI和加密DNS - 保护信息隐私的最后一块拼图&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/68d3867d/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;node 项目从构建到使用 jenkins + docker + nginx + mysql + redis 自动化部署&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1991789c/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Github Pages + jekyll 全面介绍极简搭建个人网站和博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css&#34;&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/d3@7&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-view@0.18.10&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10&#34;&gt;&lt;/script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;/css/markmap.css&#34;&gt;

&lt;script src=&#34;/js/markmap.js&#34;&gt;&lt;/script&gt;
&lt;script data-pjax src=&#34;/lib/hbe.54b2b4c40b.js&#34;&gt;&lt;/script&gt;&lt;link href=&#34;/css/hbe-style.css&#34; rel=&#34;stylesheet&#34; type=&#34;text/css&#34;&gt;</content>
        <category term="web" />
        <category term="http" />
        <category term="ccs" />
        <updated>2021-09-15T11:25:00.000Z</updated>
    </entry>
    <entry>
        <id>https://blog.17lai.site/posts/69a052c9/</id>
        <title>浏览器的渲染过程</title>
        <link rel="alternate" href="https://blog.17lai.site/posts/69a052c9/"/>
        <content type="html">&lt;blockquote&gt;
&lt;p&gt;通常，我们只需要编写HTML，CSS，JavaScript，浏览器上就能呈现出漂亮的网页了，但是浏览器是如何使用我们的代码在屏幕上渲染像素的呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;首先，请先看一张大图&#34;&gt;&lt;strong&gt;首先，请先看一张大图&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;浏览器将HTML，CSS，JavaScript代码转换成屏幕上所能呈现的实际像素，这期间所经历的一系列步骤，叫做关键渲染路径（Critical Rendering Path）。其中包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;构建对象模型（DOM，CSSOM）&lt;/li&gt;
&lt;li&gt;构建渲染树（RenderTree）&lt;/li&gt;
&lt;li&gt;布局&lt;/li&gt;
&lt;li&gt;渲染&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在构建对象模型到构建渲染树的这一过程，还穿插着JS脚本的加载和执行。如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220512.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;1-domtree的构建&#34;&gt;&lt;strong&gt;1.DOMTree的构建&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;浏览器的渲染从解析HTML文档开始，宏观上，可以分为下面几个步骤：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220515.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一步（解析）：从网络或者磁盘下读取的HTML原始字节码，通过设置的charset编码，转换成相字符&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220653.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第二步（token化）：通过词法分析器，将字符串解析成Token，Token中会标注出当前的Token是&lt;code&gt;开始标签&lt;/code&gt;，还是&lt;code&gt;结束标签&lt;/code&gt;，或者&lt;code&gt;文本标签&lt;/code&gt;等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220522.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第三步（生成Nodes并构建DOM树）：浏览器会根据Tokens里记录的&lt;code&gt;开始标签&lt;/code&gt;，&lt;code&gt;结束标签&lt;/code&gt;，将Tokens之间相互串联起来_（带有结束标签的Token不会生成Node）_。&lt;br&gt;
Node包含了这个节点的所有属性。例如&lt;code&gt;&amp;lt;img src=&amp;quot;xxx.png&amp;quot; &amp;gt;&lt;/code&gt;标签最终生成出的节点对象中会保存图片地址等信息。&lt;br&gt;
事实上，在构建DOM树时，不是要等所有的Tokens都转换成Nodes后才开始，而是一边生成Token一边采取&lt;code&gt;深度遍历算法&lt;/code&gt;消耗Token来生成Node，如下图所示：&lt;br&gt;
图中有颜色的小数字代表构建的具体步骤，可以看出，首先生成出&lt;code&gt;html Token&lt;/code&gt;,并消耗Token创建出&lt;code&gt;html 节点对象&lt;/code&gt;，接着生成&lt;code&gt;head Token&lt;/code&gt;并消耗Token创建出&lt;code&gt;head节点对象&lt;/code&gt;…，当所有的Tokens都消耗完了，紧接着DOM树也就构建完了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220658.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;这里抛出个小问题，为什么有时在js中访问DOM时浏览器会报错呢？&lt;br&gt;
因为在上述的解析的过程中，如果碰到了&lt;code&gt;script&lt;/code&gt;或者&lt;code&gt;link&lt;/code&gt;标签，就会根据&lt;code&gt;src&lt;/code&gt;对应的地址去加载资源，在&lt;code&gt;script&lt;/code&gt;标签没有设置&lt;code&gt;async/defer&lt;/code&gt;属性时，这个加载过程是&lt;code&gt;下载并执行完全部的代码&lt;/code&gt;，此时，DOM树还没有完全创建完毕，这个时候如果js企图访问script标签后面的DOM元素，浏览器就会抛出&lt;strong&gt;找不到该DOM元素&lt;/strong&gt;的错误。&lt;br&gt;
值得注意的是：从bytes到Tokens的这个过程，浏览器都可以交给其他单独的线程去处理，不会堵塞浏览器的渲染线程。但是后面的部分就都在渲染线程下进行了，也就是我们常说的js单线程环境。&lt;/p&gt;
&lt;h3 id=&#34;2-cssomtree的构建&#34;&gt;&lt;strong&gt;2.CSSOMTree的构建&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;DOM会记录页面的内容，但是浏览器还需要知道这些内容该用什么样式去展示，所以还需要构建CSSOMTree。CSSOM的生成过程和DOM的生成过程十分相似，也是：1.解析，2.Token化，3.生成Nodes并构建CSSOMTree：&lt;/p&gt;
&lt;p&gt;假设浏览器收到了下面这样一段css:&lt;/p&gt;
&lt;figure&gt;&lt;div class=&#34;code-area&#34;&gt;&lt;pre class=&#34;line-numbers language-css&#34; data-language=&#34;css&#34; data-start=&#34;1&#34;&gt;&lt;code class=&#34;language-css&#34;&gt;body &amp;#123;font-size: 16px;&amp;#125;
p &amp;#123;font-weight: bold;&amp;#125;
p span &amp;#123;display:none;&amp;#125;
span &amp;#123;color: red;&amp;#125;
img &amp;#123;float: right;&amp;#125;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;最终会生成如下的CSSOMTree:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220529.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;从图中可以看出，最开始&lt;code&gt;body&lt;/code&gt;有一个样式规则是&lt;code&gt;font-size:16px&lt;/code&gt;，之后，在body这个样式基础上每个子节点还会添加自己单独的样式规则，比如&lt;code&gt;span&lt;/code&gt;又添加了一个样式规则&lt;code&gt;color:red&lt;/code&gt;。正是因为样式这种类似于继承的特性，浏览器设定了一条规则：&lt;strong&gt;CSSOMTree需要等到完全构建后才可以被使用，因为后面的属性可能会覆盖掉前面的设置&lt;/strong&gt;。比如在上面的css代码基础上再添加一行代码&lt;code&gt;p &amp;#123;font-size:12px&amp;#125;&lt;/code&gt;，那么之前设置的&lt;code&gt;16px&lt;/code&gt;将会被覆盖成&lt;code&gt;12px&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;下面是官方给的一种解释：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;未构建完的CSSOMTree是不准确的，浏览器必须等到CSSOMTree构建完毕后才能进入下一阶段。&lt;br&gt;
所以，CSS的加载速度与构建CSSOMTree的速度将直接影响首屏渲染速度，因此在默认情况下CSS被视为阻塞渲染的资源，需要将它尽早、尽快地下载到客户端，以便缩短首次渲染的时间。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么回到上面生成DOM时提到的JS问题：&lt;strong&gt;在标签没有设置&lt;code&gt;async/defer&lt;/code&gt;属性时，js会阻塞DOM的生成&lt;/strong&gt;。原因是js会改变DOMTree的内容，如果不阻塞，会出现一边生成DOM内容，一边修改DOM内容的情况，无法确保最终生成的DOMTree是确定唯一的。&lt;/p&gt;
&lt;p&gt;同理，JS也会可以修改CSS样式，影响CSSOMTree最终的结果。而我们前面提到，不完整的CSSOMTree是不可以被使用的，如果JS试图在&lt;strong&gt;浏览器还未完成CSSOMTree的下载和构建&lt;/strong&gt;时去操作CSS样式，浏览器会&lt;strong&gt;暂停脚本的运行和DOM的构建&lt;/strong&gt;，直至浏览器完成了CSSOM的下载和构建。也就是说，&lt;strong&gt;JS脚本的出现会让CSSOM的构建阻塞DOM的构建&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;平时谈及页面性能优化，经常会强调css文件应该放在html文档中的前面引入，js文件应该放在后面引入，这么做的原因是什么呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;举个例子：本来，DOM构建和CSSOM构建是两个过程，井水不犯河水。假设DOM构建完成需要1s，CSSOM构建也需要1s，在DOM构建了0.2s时发现了一个&lt;code&gt;link&lt;/code&gt;标签，此时完成这个操作需要的时间大概是1.2s，如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220540.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;而此时我们在HTML文档的中间插中入了一段JS代码，在DOM构建中间的过程中发现了这个&lt;code&gt;script&lt;/code&gt;标签，假设这段JS代码只需要执行0.0001s，那么完成这个操作需要的时间就会变成：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220546.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;那如果我们把css放到前面，js放到最后引入时，构建时间会变成：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220705.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;由此可见，虽然只是插入了小小的一段只运行0.0001s的js代码，不同的引入时机也会严重影响DOMTree的构建速度。&lt;/p&gt;
&lt;p&gt;简而言之，如果在DOM，CSSOM和JavaScript执行之间引入大量的依赖关系，可能会导致浏览器在处理渲染资源时出现大幅度延迟：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当浏览器遇到一个script标签时，DOMTree的构建将被暂停，直至脚本执行完毕&lt;/li&gt;
&lt;li&gt;JavaScript可以查询和修改DOMTree与CSSOMTree&lt;/li&gt;
&lt;li&gt;直至CSSOM构建完毕，JavaScript才会执行&lt;/li&gt;
&lt;li&gt;脚本在文档中的位置很重要&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-渲染树的构建&#34;&gt;&lt;strong&gt;3.渲染树的构建&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;现在，我们已经拥有了完整的DOM树和CSSOM树。DOM 树上每一个节点对应着网页里每一个元素，CSSOM树上每个节点对应着网页里每个元素的样式，并且此时浏览器也可以通过 JavaScript 操作DOM/CSSOM树，动态改变它的结构。但是DOM/CSSOM树本身并不能直接用于排版和渲染，浏览器还会生成另外一棵树：Render树&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220711.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;接下来我们来谈几条概念&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Render 树上的每一个节点被称为：&lt;code&gt;RenderObject&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RenderObject跟 DOM 节点几乎是一一对应的，当一个&lt;code&gt;可见的 DOM 节点&lt;/code&gt;被添加到 DOM 树上时，内核就会为它生成对应的 RenderOject 添加到 Render 树上。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;其中，可见的DOM节点不包括：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一些不会体现在渲染输出中的节点（&lt;code&gt;&amp;lt;html&amp;gt;&amp;lt;script&amp;gt;&amp;lt;link&amp;gt;….&lt;/code&gt;），会直接被忽略掉。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过CSS隐藏的节点。例如上图中的&lt;code&gt;span&lt;/code&gt;节点，因为有一个CSS显式规则在该节点上设置了&lt;code&gt;display:none&lt;/code&gt;属性，那么它在生成RenderObject时会被直接忽略掉。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Render 树是衔接浏览器排版引擎和渲染引擎之间的&lt;strong&gt;桥梁&lt;/strong&gt;，它是&lt;strong&gt;排版引擎的输出，渲染引擎的输入&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此时的Render树上，已经包含了网页上所有可见元素的内容和位置信息 排版引擎会根据Render树的内容和结构，准确的计算出元素该在网页上的什么位置。到此，我们已经具备进入布局的一切准备条件，但是通过上面我们知道，布局后面还有一个渲染过程，那么_Render 树是衔接浏览器排版引擎和渲染引擎之间的桥梁，它是排版引擎的输出，渲染引擎的输入。_这句话是什么意思呢？&lt;/p&gt;
&lt;h3 id=&#34;renderobject-and-renderlayer&#34;&gt;&lt;strong&gt;RenderObject and RenderLayer&lt;/strong&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;浏览器渲染引擎并不是直接使用Render树进行绘制，为了方便处理&lt;strong&gt;Positioning,Clipping,Overflow-scroll,CSS Transfrom/Opacrity/Animation/Filter,Mask or Reflection,Z-indexing&lt;/strong&gt;等属性，浏览器需要生成另外一棵树：&lt;strong&gt;Layer树&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220730.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;浏览器会为一些&lt;strong&gt;特定&lt;/strong&gt;的&lt;code&gt;RenderObject&lt;/code&gt;生成对应的&lt;code&gt;RenderLayer&lt;/code&gt;，其中的规则是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;是否是页面的根节点 &lt;em&gt;It’s the root object for the page&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;是否有css的一些布局属性（relative absolute or a transform) &lt;em&gt;It has explicit CSS position properties (relative, absolute or a transform)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;是否透明 &lt;em&gt;It is transparent&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;是否有溢出 &lt;em&gt;Has overflow, an alpha mask or reflection&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;是否有css滤镜 &lt;em&gt;Has a CSS filter&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;是否包含一个canvas元素使得节点拥有视图上下文 &lt;em&gt;Corresponds to canvas element that has a 3D (WebGL) context or an accelerated 2D context&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;是否包含一个video元素 &lt;em&gt;Corresponds to a video element&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当满足上面其中一个条件时，这个&lt;code&gt;RrenderObject&lt;/code&gt;就会被浏览器选中生成对应的&lt;code&gt;RenderLayer&lt;/code&gt;。至于那些没有被命运选中的RrenderObject，会从属与父节点的RenderLayer。最终，每个RrenderObject都会直接或者间接的属于一个RenderLayer。&lt;/p&gt;
&lt;p&gt;浏览器渲染引擎在布局和渲染时会遍历整个Layer树，访问每一个&lt;code&gt;RenderLayer&lt;/code&gt;，再遍历从属于这个RenderLayer的 &lt;code&gt;RrenderObject&lt;/code&gt;，将每一个 RenderObject 绘制出来。可以理解为：Layer 树决定了网页绘制的层次顺序，而从属于RenderLayer 的 RrenderObject决定了这个 Layer 的内容，所有的 &lt;code&gt;RenderLayer&lt;/code&gt; 和 &lt;code&gt;RrenderObject&lt;/code&gt; 一起就决定了网页在屏幕上最终呈现出来的内容。&lt;/p&gt;
&lt;h3 id=&#34;4-布局&#34;&gt;&lt;strong&gt;4.布局&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;到目前为止，浏览器计算出了哪些节点是可见的以及它的信息和样式，接下来就需要计算这些节点在设备视口内的确切位置和大小，这个过程我们称之为“布局”。&lt;/p&gt;
&lt;p&gt;布局最后的输出是一个“盒模型”：将所有相对测量值都转换成屏幕上的绝对像素。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220553.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;5-渲染&#34;&gt;&lt;strong&gt;5.渲染&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;最后，既然我们知道了哪些节点可见、它们的计算样式以及几何信息，我们终于可以将这些信息传递给最后一个阶段：将渲染树中的每个节点转换成屏幕上的实际像素：浏览器通过发出“Paint Setup”和“Paint”事件，将渲染树转换成屏幕上的像素。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913220718.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;至此，我们就能够在浏览器上看到漂亮的网页了&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;谈及页面性能优化，我们也常说要尽量减少浏览器的重排和重绘，浏览器重排和重绘时究竟做了哪些工作呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们平时常说的重排，其实就是浏览器计算render树，布局到渲染的这个过程，而重绘就是计算layer树到渲染的这个过程，每当触发一次重绘和重排时，浏览器都需要重新经过一遍上述的计算。很显然，重排会产生比重绘更大的开销，但无论是重排还是重绘，都会给浏览器渲染线程造成很大的负担，所以，我们在实际生产中要严格注意减少重排和重绘的触发。至于如何减少重排和重绘的次数，这里就不多做展开了，详细请听下回分解~&lt;/p&gt;
&lt;h3 id=&#34;总结：&#34;&gt;&lt;strong&gt;总结：&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;经过：1.构建对象模型（DOM，CSSOM），2.构建渲染树（RenderTree），3.布局，4.渲染 这几个步骤后，我们就能在浏览器上看到漂亮的网页啦。&lt;/li&gt;
&lt;li&gt;CSS被视为阻塞渲染的资源，应放到代码的头部尽快加载。&lt;/li&gt;
&lt;li&gt;同步的JavaScript会暂停DOMTree的构建，应放到代码的尾部最后加载，或者使用&lt;code&gt;async/defer属性&lt;/code&gt;异步加载JavaScript。&lt;/li&gt;
&lt;li&gt;重排和重绘会给浏览器渲染线程造成很大的负担，尽量减少重排和重绘的触发次数&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;参考文献：&#34;&gt;&lt;strong&gt;参考文献：&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-model?hl=zh-cn&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-model?hl=zh-cn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction?hl=zh-cn&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction?hl=zh-cn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-blocking-css?hl=zh-cn&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-blocking-css?hl=zh-cn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s?__biz=MzA5NzkwNDk3MQ==&amp;amp;mid=2650588806&amp;amp;idx=1&amp;amp;sn=408a54e7c8102fd6944c9a40b119015a&amp;amp;scene=21#wechat_redirect&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;https://mp.weixin.qq.com/s?__biz=MzA5NzkwNDk3MQ==&amp;amp;mid=2650588806&amp;amp;idx=1&amp;amp;sn=408a54e7c8102fd6944c9a40b119015a&amp;amp;scene=21#wechat_redirect&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;参考-致谢&#34;&gt;参考&amp;amp;致谢&lt;/h2&gt;
&lt;h2 id=&#34;系列教程&#34;&gt;&lt;strong&gt;系列教程&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;全部文章RSS订阅&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;web系列&#34;&gt;&lt;strong&gt;web系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/web/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;&lt;strong&gt;Web 分类 RSS 订阅&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/69a052c9/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;浏览器的渲染过程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/656a0abb/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;在浏览器输入 URL 回车之后发生了什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/448f849b/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;1.5 万字 CSS 基础拾遗（核心知识、常见需求）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/80906b88/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;http 错误代码表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/8f873c0f/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;写了些浏览器增强脚本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1d87ed2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;如何防止密码泄露，保护密码安全？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/ec2cad2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ESNI和加密DNS - 保护信息隐私的最后一块拼图&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/68d3867d/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;node 项目从构建到使用 jenkins + docker + nginx + mysql + redis 自动化部署&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1991789c/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Github Pages + jekyll 全面介绍极简搭建个人网站和博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css&#34;&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/d3@7&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-view@0.18.10&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10&#34;&gt;&lt;/script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;/css/markmap.css&#34;&gt;

&lt;script src=&#34;/js/markmap.js&#34;&gt;&lt;/script&gt;
&lt;script data-pjax src=&#34;/lib/hbe.54b2b4c40b.js&#34;&gt;&lt;/script&gt;&lt;link href=&#34;/css/hbe-style.css&#34; rel=&#34;stylesheet&#34; type=&#34;text/css&#34;&gt;</content>
        <category term="web" />
        <category term="http" />
        <category term="css" />
        <category term="js" />
        <updated>2021-09-13T14:25:00.000Z</updated>
    </entry>
    <entry>
        <id>https://blog.17lai.site/posts/656a0abb/</id>
        <title>在浏览器输入 URL 回车之后发生了什么</title>
        <link rel="alternate" href="https://blog.17lai.site/posts/656a0abb/"/>
        <content type="html">&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;这个问题已经是老生常谈了，更是经常被作为面试的压轴题出现，网上也有很多文章，但最近闲的无聊，然后就自己做了一篇笔记，感觉比之前理解更透彻了。&lt;/p&gt;
&lt;p&gt;这篇笔记是我这两天看了数十篇文章总结出来的，所以相对全面一点，但由于我是做前端的，所以会比较重点分析浏览器渲染页面那一部分，至于其他部分我会罗列出关键词，感兴趣的可以自行查阅，&lt;/p&gt;
&lt;p&gt;**注意：**本文的步骤是建立在，请求的是一个简单的 HTTP 请求，没有 HTTPS、HTTP2、最简单的 DNS、没有代理、并且服务器没有任何问题的基础上，尽管这是不切实际的。&lt;/p&gt;
&lt;h2 id=&#34;大致流程&#34;&gt;大致流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;URL 解析&lt;/li&gt;
&lt;li&gt;DNS 查询&lt;/li&gt;
&lt;li&gt;TCP 连接&lt;/li&gt;
&lt;li&gt;处理请求&lt;/li&gt;
&lt;li&gt;接受响应&lt;/li&gt;
&lt;li&gt;渲染页面&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;一、url-解析&#34;&gt;一、URL 解析&lt;/h2&gt;
&lt;h3 id=&#34;地址解析：&#34;&gt;&lt;strong&gt;地址解析：&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;首先判断你输入的是一个合法的 URL 还是一个待搜索的关键词，并且根据你输入的内容进行自动完成、字符编码等操作。&lt;/p&gt;
&lt;h3 id=&#34;hsts&#34;&gt;&lt;strong&gt;HSTS&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;由于安全隐患，会使用 HSTS 强制客户端使用 HTTPS 访问页面。详见：&lt;a href=&#34;https://www.barretlee.com/blog/2015/10/22/hsts-intro/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;你所不知道的 HSTS (opens new window)&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&#34;其他操作&#34;&gt;&lt;strong&gt;其他操作&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;浏览器还会进行一些额外的操作，比如安全检查、访问限制（之前国产浏览器限制 996.icu）。&lt;/p&gt;
&lt;h3 id=&#34;检查缓存&#34;&gt;&lt;strong&gt;检查缓存&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094301.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;二、dns-查询&#34;&gt;二、DNS 查询&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;基本步骤&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094305.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;1-浏览器缓存&#34;&gt;&lt;strong&gt;1. 浏览器缓存&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;浏览器会先检查是否在缓存中，没有则调用系统库函数进行查询。&lt;/p&gt;
&lt;h3 id=&#34;2-操作系统缓存&#34;&gt;&lt;strong&gt;2. 操作系统缓存&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;操作系统也有自己的 DNS 缓存，但在这之前，会向检查域名是否存在本地的 Hosts 文件里，没有则向 DNS 服务器发送查询请求。&lt;/p&gt;
&lt;h3 id=&#34;3-路由器缓存&#34;&gt;&lt;strong&gt;3. 路由器缓存&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;路由器也有自己的缓存。&lt;/p&gt;
&lt;h3 id=&#34;4-isp-dns-缓存&#34;&gt;&lt;strong&gt;4. ISP DNS 缓存&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;ISP DNS 就是在客户端电脑上设置的首选 DNS 服务器，它们在大多数情况下都会有缓存。&lt;/p&gt;
&lt;h3 id=&#34;根域名服务器查询&#34;&gt;&lt;strong&gt;根域名服务器查询&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;在前面所有步骤没有缓存的情况下，本地 DNS 服务器会将请求转发到互联网上的根域，下面这个图很好的诠释了整个流程：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094311.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;根域名服务器：&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%A0%B9%E7%B6%B2%E5%9F%9F%E5%90%8D%E7%A8%B1%E4%BC%BA%E6%9C%8D%E5%99%A8&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;维基百科 (opens new window)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;需要注意的点&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;递归方式：一路查下去中间不返回，得到最终结果才返回信息（浏览器到本地 DNS 服务器的过程）&lt;/li&gt;
&lt;li&gt;迭代方式，就是本地 DNS 服务器到根域名服务器查询的方式。&lt;/li&gt;
&lt;li&gt;什么是 DNS 劫持&lt;/li&gt;
&lt;li&gt;前端 dns-prefetch 优化&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;三、tcp-连接&#34;&gt;三、TCP 连接&lt;/h2&gt;
&lt;p&gt;TCP/IP 分为四层，在发送数据时，每层都要对数据进行封装：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094325.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;1-应用层：发送-http-请求&#34;&gt;&lt;strong&gt;1. 应用层：发送 HTTP 请求&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;在前面的步骤我们已经得到服务器的 IP 地址，浏览器会开始构造一个 HTTP 报文，其中包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;请求报头（Request Header）：请求方法、目标地址、遵循的协议等等&lt;/li&gt;
&lt;li&gt;请求主体（其他参数）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中需要注意的点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;浏览器只能发送 GET、POST 方法，而打开网页使用的是 GET 方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-传输层：tcp-传输报文&#34;&gt;&lt;strong&gt;2. 传输层：TCP 传输报文&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;传输层会发起一条到达服务器的 TCP 连接，为了方便传输，会对数据进行分割（以报文段为单位），并标记编号，方便服务器接受时能够准确地还原报文信息。&lt;/p&gt;
&lt;p&gt;在建立连接前，会先进行 TCP 三次握手。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;关于 TCP/IP 三次握手，网上已经有很多段子和图片生动地描述了。&lt;/p&gt;
&lt;p&gt;相关知识点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;SYN 泛洪攻击&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;3-网络层：ip-协议查询-mac-地址&#34;&gt;&lt;strong&gt;3. 网络层：IP 协议查询 Mac 地址&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;将数据段打包，并加入源及目标的 IP 地址，并且负责寻找传输路线。&lt;/p&gt;
&lt;p&gt;判断目标地址是否与当前地址处于同一网络中，是的话直接根据 Mac 地址发送，否则使用路由表查找下一跳地址，以及使用 ARP 协议查询它的 Mac 地址。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：在 OSI 参考模型中 ARP 协议位于链路层，但在 TCP/IP 中，它位于网络层。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;4-链路层：以太网协议&#34;&gt;&lt;strong&gt;4. 链路层：以太网协议&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;以太网协议&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;根据以太网协议将数据分为以“帧”为单位的数据包，每一帧分为两个部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;标头：数据包的发送者、接受者、数据类型&lt;/li&gt;
&lt;li&gt;数据：数据包具体内容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Mac 地址&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以太网规定了连入网络的所有设备都必须具备“网卡”接口，数据包都是从一块网卡传递到另一块网卡，网卡的地址就是 Mac 地址。每一个 Mac 地址都是独一无二的，具备了一对一的能力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;广播&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;发送数据的方法很原始，直接把数据通过 ARP 协议，向本网络的所有机器发送，接收方根据标头信息与自身 Mac 地址比较，一致就接受，否则丢弃。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：接收方回应是单播。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;相关知识点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ARP 攻击&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;服务器接受请求&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;接受过程就是把以上步骤逆转过来，参见上图。&lt;/p&gt;
&lt;h2 id=&#34;四、服务器处理请求&#34;&gt;四、服务器处理请求&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;大致流程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094339.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;httpd&#34;&gt;&lt;strong&gt;HTTPD&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;最常见的 HTTPD 有 Linux 上常用的 Apache 和 Nginx，以及 Windows 上的 IIS。&lt;/p&gt;
&lt;p&gt;它会监听得到的请求，然后开启一个子进程去处理这个请求。&lt;/p&gt;
&lt;h3 id=&#34;处理请求&#34;&gt;&lt;strong&gt;处理请求&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;接受 TCP 报文后，会对连接进行处理，对 HTTP 协议进行解析（请求方法、域名、路径等），并且进行一些验证：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;验证是否配置虚拟主机&lt;/li&gt;
&lt;li&gt;验证虚拟主机是否接受此方法&lt;/li&gt;
&lt;li&gt;验证该用户可以使用该方法（根据 IP 地址、身份信息等）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;重定向&#34;&gt;&lt;strong&gt;重定向&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;假如服务器配置了 HTTP 重定向，就会返回一个 &lt;code&gt;301&lt;/code&gt;永久重定向响应，浏览器就会根据响应，重新发送 HTTP 请求（重新执行上面的过程）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;关于更多：&lt;a href=&#34;https://www.cnblogs.com/workest/p/3891321.html&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;详见这篇文章 (opens new window)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;url-重写&#34;&gt;&lt;strong&gt;URL 重写&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;然后会查看 URL 重写规则，如果请求的文件是真实存在的，比如图片、html、css、js 文件等，则会直接把这个文件返回。&lt;/p&gt;
&lt;p&gt;否则服务器会按照规则把请求重写到 一个 REST 风格的 URL 上。&lt;/p&gt;
&lt;p&gt;然后根据动态语言的脚本，来决定调用什么类型的动态文件解释器来处理这个请求。&lt;/p&gt;
&lt;p&gt;以 PHP 语言的 MVC 框架举例，它首先会初始化一些环境的参数，根据 URL 由上到下地去匹配路由，然后让路由所定义的方法去处理请求。&lt;/p&gt;
&lt;h2 id=&#34;五、浏览器接受响应&#34;&gt;五、浏览器接受响应&lt;/h2&gt;
&lt;p&gt;浏览器接收到来自服务器的响应资源后，会对资源进行分析。&lt;/p&gt;
&lt;p&gt;首先查看 Response header，根据不同状态码做不同的事（比如上面提到的重定向）。&lt;/p&gt;
&lt;p&gt;如果响应资源进行了压缩（比如 gzip），还需要进行解压。&lt;/p&gt;
&lt;p&gt;然后，对响应资源做缓存。&lt;/p&gt;
&lt;p&gt;接下来，根据响应资源里的 &lt;a href=&#34;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;MIME (opens new window)&lt;/a&gt; 类型去解析响应内容（比如 HTML、Image 各有不同的解析方式）。&lt;/p&gt;
&lt;h2 id=&#34;六、渲染页面&#34;&gt;六、渲染页面&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;浏览器内核&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094432.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;不同的浏览器内核，渲染过程也不完全相同，但大致流程都差不多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;基本流程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094347.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;1-html-解析&#34;&gt;&lt;strong&gt;1. HTML 解析&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;首先要知道浏览器解析是从上往下一行一行地解析的。&lt;/p&gt;
&lt;p&gt;解析的过程可以分为四个步骤：&lt;/p&gt;
&lt;h4 id=&#34;1-解码（encoding）&#34;&gt;&lt;strong&gt;1. 解码（encoding）&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;传输回来的其实都是一些二进制字节数据，浏览器需要根据文件指定编码（例如 UTF-8）转换成字符串，也就是 HTML 代码。&lt;/p&gt;
&lt;h4 id=&#34;2-预解析（pre-parsing）&#34;&gt;&lt;strong&gt;2. 预解析（pre-parsing）&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;预解析做的事情是提前加载资源，减少处理时间，它会识别一些会请求资源的属性，比如&lt;code&gt;img&lt;/code&gt;标签的&lt;code&gt;src&lt;/code&gt;属性，并将这个请求加到请求队列中。&lt;/p&gt;
&lt;h4 id=&#34;3-符号化（tokenization）&#34;&gt;&lt;strong&gt;3. 符号化（Tokenization）&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;符号化是词法分析的过程，将输入解析成符号，HTML 符号包括，开始标签、结束标签、属性名和属性值。&lt;/p&gt;
&lt;p&gt;它通过一个状态机去识别符号的状态，比如遇到&lt;code&gt;&amp;lt;&lt;/code&gt;，&lt;code&gt;&amp;gt;&lt;/code&gt;状态都会产生变化。&lt;/p&gt;
&lt;h4 id=&#34;4-构建树（tree-construction）&#34;&gt;&lt;strong&gt;4. 构建树（tree construction）&lt;/strong&gt;&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：符号化和构建树是并行操作的，也就是说只要解析到一个开始标签，就会创建一个 DOM 节点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在上一步符号化中，解析器获得这些标记，然后以合适的方法创建&lt;code&gt;DOM&lt;/code&gt;对象并把这些符号插入到&lt;code&gt;DOM&lt;/code&gt;对象中。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094354.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;浏览器容错进制&#34;&gt;&lt;strong&gt;浏览器容错进制&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;你从来没有在浏览器看过类似&amp;quot;语法无效&amp;quot;的错误，这是因为浏览器去纠正错误的语法，然后继续工作。&lt;/p&gt;
&lt;h4 id=&#34;事件&#34;&gt;&lt;strong&gt;事件&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;当整个解析的过程完成以后，浏览器会通过&lt;code&gt;DOMContentLoaded&lt;/code&gt;事件来通知&lt;code&gt;DOM&lt;/code&gt;解析完成。&lt;/p&gt;
&lt;h3 id=&#34;2-css-解析&#34;&gt;2. CSS 解析&lt;/h3&gt;
&lt;p&gt;一旦浏览器下载了 CSS，CSS 解析器就会处理它遇到的任何 CSS，根据&lt;a href=&#34;https://drafts.csswg.org/css-syntax-3/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;语法规范 (opens new window)&lt;/a&gt;解析出所有的 CSS 并进行标记化，然后我们得到一个规则表。&lt;/p&gt;
&lt;h4 id=&#34;css-匹配规则&#34;&gt;&lt;strong&gt;CSS 匹配规则&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;在匹配一个节点对应的 CSS 规则时，是按照从右到左的顺序的，例如：&lt;code&gt;div p &amp;#123; font-size :14px &amp;#125;&lt;/code&gt;会先寻找所有的&lt;code&gt;p&lt;/code&gt;标签然后判断它的父元素是否为&lt;code&gt;div&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;所以我们写 CSS 时，尽量用 id 和 class，千万不要过度层叠。&lt;/p&gt;
&lt;h3 id=&#34;3-渲染树&#34;&gt;3. 渲染树&lt;/h3&gt;
&lt;p&gt;其实这就是一个 DOM 树和 CSS 规则树合并的过程。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：渲染树会忽略那些不需要渲染的节点，比如设置了&lt;code&gt;display:none&lt;/code&gt;的节点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;计算&#34;&gt;&lt;strong&gt;计算&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;通过计算让任何尺寸值都减少到三个可能之一：&lt;code&gt;auto&lt;/code&gt;、百分比、px，比如把&lt;code&gt;rem&lt;/code&gt;转化为&lt;code&gt;px&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id=&#34;级联&#34;&gt;&lt;strong&gt;级联&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;浏览器需要一种方法来确定哪些样式才真正需要应用到对应元素，所以它使用一个叫做&lt;code&gt;specificity&lt;/code&gt;的公式，这个公式会通过：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;标签名、class、id&lt;/li&gt;
&lt;li&gt;是否内联样式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;!important&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;然后得出一个权重值，取最高的那个。&lt;/p&gt;
&lt;h4 id=&#34;渲染阻塞&#34;&gt;&lt;strong&gt;渲染阻塞&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;当遇到一个&lt;code&gt;script&lt;/code&gt;标签时，DOM 构建会被暂停，直至脚本完成执行，然后继续构建 DOM 树。&lt;/p&gt;
&lt;p&gt;但如果 JS 依赖 CSS 样式，而它还没有被下载和构建时，浏览器就会延迟脚本执行，直至 CSS Rules 被构建。&lt;/p&gt;
&lt;p&gt;所有我们知道：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CSS 会阻塞 JS 执行&lt;/li&gt;
&lt;li&gt;JS 会阻塞后面的 DOM 解析&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了避免这种情况，应该以下原则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CSS 资源排在 JavaScript 资源前面&lt;/li&gt;
&lt;li&gt;JS 放在 HTML 最底部，也就是 &lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt;前&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外，如果要改变阻塞模式，可以使用 defer 与 async，详见：&lt;a href=&#34;https://github.com/xiaoyu2er/blog/issues/8&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;这篇文章 (opens new window)&lt;/a&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;布局与绘制&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;确定渲染树种所有节点的几何属性，比如：位置、大小等等，最后输入一个盒子模型，它能精准地捕获到每个元素在屏幕内的准确位置与大小。&lt;/p&gt;
&lt;p&gt;然后遍历渲染树，调用渲染器的 paint() 方法在屏幕上显示其内容。&lt;/p&gt;
&lt;h3 id=&#34;5-合并渲染层&#34;&gt;&lt;strong&gt;5. 合并渲染层&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;把以上绘制的所有图片合并，最终输出一张图片。&lt;/p&gt;
&lt;h3 id=&#34;6-回流与重绘&#34;&gt;&lt;strong&gt;6. 回流与重绘&lt;/strong&gt;&lt;/h3&gt;
&lt;h4 id=&#34;回流-reflow&#34;&gt;&lt;strong&gt;回流(reflow)&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;当浏览器发现某个部分发现变化影响了布局时，需要倒回去重新渲染，会从&lt;code&gt;html&lt;/code&gt;标签开始递归往下，重新计算位置和大小。&lt;/p&gt;
&lt;p&gt;reflow 基本是无法避免的，因为当你滑动一下鼠标、resize 窗口，页面就会产生变化。&lt;/p&gt;
&lt;h4 id=&#34;重绘-repaint&#34;&gt;&lt;strong&gt;重绘(repaint)&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;改变了某个元素的背景色、文字颜色等等不会影响周围元素的位置变化时，就会发生重绘。&lt;/p&gt;
&lt;p&gt;每次重绘后，浏览器还需要合并渲染层并输出到屏幕上。&lt;/p&gt;
&lt;p&gt;回流的成本要比重绘高很多，所以我们应该尽量避免产生回流。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;display:none&lt;/code&gt; 会触发回流，而 &lt;code&gt;visibility:hidden&lt;/code&gt; 只会触发重绘。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&#34;7&#34;&gt;
&lt;li&gt;JavaScript 编译执行&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;大致流程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094359.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;可以分为三个阶段：&lt;/p&gt;
&lt;h5 id=&#34;1-词法分析&#34;&gt;&lt;strong&gt;1. 词法分析&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;JS 脚本加载完毕后，会首先进入语法分析阶段，它首先会分析代码块的语法是否正确，不正确则抛出“语法错误”，停止执行。&lt;/p&gt;
&lt;p&gt;几个步骤：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分词，例如将&lt;code&gt;var a = 2&lt;/code&gt;，，分成&lt;code&gt;var&lt;/code&gt;、&lt;code&gt;a&lt;/code&gt;、&lt;code&gt;=&lt;/code&gt;、&lt;code&gt;2&lt;/code&gt;这样的词法单元。&lt;/li&gt;
&lt;li&gt;解析，将词法单元转换成抽象语法树（AST）。&lt;/li&gt;
&lt;li&gt;代码生成，将抽象语法树转换成机器指令。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;2-预编译&#34;&gt;&lt;strong&gt;2. 预编译&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;JS 有三种运行环境：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全局环境&lt;/li&gt;
&lt;li&gt;函数环境&lt;/li&gt;
&lt;li&gt;eval&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每进入一个不同的运行环境都会创建一个对应的执行上下文，根据不同的上下文环境，形成一个函数调用栈，栈底永远是全局执行上下文，栈顶则永远是当前执行上下文。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;创建执行上下文&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;创建执行上下文的过程中，主要做了以下三件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建变量对象
&lt;ul&gt;
&lt;li&gt;参数、函数、变量&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;建立作用域链
&lt;ul&gt;
&lt;li&gt;确认当前执行环境是否能访问变量&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;确定 This 指向&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;3-执行&#34;&gt;&lt;strong&gt;3. 执行&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;JS 线程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094406.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;虽然 JS 是单线程的，但实际上参与工作的线程一共有四个：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;其中三个只是协助，只有 JS 引擎线程是真正执行的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;JS 引擎线程：也叫 JS 内核，负责解析执行 JS 脚本程序的主线程，例如 V8 引擎&lt;/li&gt;
&lt;li&gt;事件触发线程：属于浏览器内核线程，主要用于控制事件，例如鼠标、键盘等，当事件被触发时，就会把事件的处理函数推进事件队列，等待 JS 引擎线程执行&lt;/li&gt;
&lt;li&gt;定时器触发线程：主要控制&lt;code&gt;setInterval&lt;/code&gt;和&lt;code&gt;setTimeout&lt;/code&gt;，用来计时，计时完毕后，则把定时器的处理函数推进事件队列中，等待 JS 引擎线程。&lt;/li&gt;
&lt;li&gt;HTTP 异步请求线程：通过 XMLHttpRequest 连接后，通过浏览器新开的一个线程，监控 readyState 状态变更时，如果设置了该状态的回调函数，则将该状态的处理函数推进事件队列中，等待 JS 引擎线程执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;注：浏览器对同一域名的并发连接数是有限的，通常为 6 个。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;宏任务&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;分为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同步任务：按照顺序执行，只有前一个任务完成后，才能执行后一个任务&lt;/li&gt;
&lt;li&gt;异步任务：不直接执行，只有满足触发条件时，相关的线程将该异步任务推进任务队列中，等待 JS 引擎主线程上的任务执行完毕时才开始执行，例如异步 Ajax、DOM 事件，setTimeout 等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;微任务&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;微任务是 ES6 和 Node 环境下的，主要 API 有：&lt;code&gt;Promise&lt;/code&gt;，&lt;code&gt;process.nextTick&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;微任务的执行在宏任务的同步任务之后，在异步任务之前。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://cimg1.17lai.site/data/2021/09/1320210913094407.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;代码例子&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以上代码输出顺序为：1,3,5,4,2&lt;/p&gt;
&lt;h2 id=&#34;参考文档&#34;&gt;参考文档&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/skyline75489/what-happens-when-zh_CN&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;what-happens-when-zh_CN (opens new window)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://alistapart.com/article/tags-to-dom/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Tags to DOM (opens new window)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://heyingye.github.io/2018/04/16/%E5%BD%BB%E5%BA%95%E7%90%86%E8%A7%A3%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%84%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;彻底理解浏览器的缓存机制 (opens new window)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/#The_rendering_engine&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;浏览器的工作原理：新式网络浏览器幕后揭秘 (opens new window)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.fundebug.com/2019/01/03/understand-browser-rendering/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;深入浅出浏览器渲染原理 (opens new window)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://heyingye.github.io/2018/03/19/js%E5%BC%95%E6%93%8E%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B%EF%BC%88%E4%B8%80%EF%BC%89/#%E9%A2%84%E7%BC%96%E8%AF%91%E9%98%B6%E6%AE%B5&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;js 引擎的执行过程（一） (opens new window)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;还有一些找不到了。。。。。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From: &lt;a href=&#34;https://4ark.me/post/b6c7c0a2.html&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;4ark.me&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;参考-致谢&#34;&gt;参考&amp;amp;致谢&lt;/h2&gt;
&lt;h2 id=&#34;系列教程&#34;&gt;&lt;strong&gt;系列教程&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;全部文章RSS订阅&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&#34;web系列&#34;&gt;&lt;strong&gt;web系列&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/categories/web/atom.xml&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;i class=&#34;fas fa-rss&#34;&gt;&lt;/i&gt;&lt;strong&gt;Web 分类 RSS 订阅&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;/posts/69a052c9/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;浏览器的渲染过程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/656a0abb/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;在浏览器输入 URL 回车之后发生了什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/448f849b/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;1.5 万字 CSS 基础拾遗（核心知识、常见需求）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/80906b88/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;http 错误代码表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/8f873c0f/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;写了些浏览器增强脚本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1d87ed2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;如何防止密码泄露，保护密码安全？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/ec2cad2/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ESNI和加密DNS - 保护信息隐私的最后一块拼图&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/68d3867d/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;node 项目从构建到使用 jenkins + docker + nginx + mysql + redis 自动化部署&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;/posts/1991789c/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Github Pages + jekyll 全面介绍极简搭建个人网站和博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css&#34;&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/d3@7&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-view@0.18.10&#34;&gt;&lt;/script&gt;&lt;script src=&#34;https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.10&#34;&gt;&lt;/script&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;/css/markmap.css&#34;&gt;

&lt;script src=&#34;/js/markmap.js&#34;&gt;&lt;/script&gt;
&lt;script data-pjax src=&#34;/lib/hbe.54b2b4c40b.js&#34;&gt;&lt;/script&gt;&lt;link href=&#34;/css/hbe-style.css&#34; rel=&#34;stylesheet&#34; type=&#34;text/css&#34;&gt;</content>
        <category term="web" />
        <category term="http" />
        <updated>2021-09-13T01:25:00.000Z</updated>
    </entry>
</feed>
