<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="https://www.linkjm.com/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <atom:link href="https://www.linkjm.com/rss.xml" rel="self" type="application/rss+xml"/>
    <title>LinkJM 交易日志</title>
    <link>https://www.linkjm.com/</link>
    <description>系统学习 Web3、加密资产交易与风险管理</description>
    <language>zh-CN</language>
    <pubDate>Fri, 25 Sep 2026 11:11:15 GMT</pubDate>
    <lastBuildDate>Fri, 25 Sep 2026 11:11:15 GMT</lastBuildDate>
    <generator>@vuepress/plugin-feed</generator>
    <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
    <copyright>Copyright © 2020-2026 LinkJM（恋上加密）</copyright>
    <category>Blog</category>
    <category>使用指南</category>
    <category>指南</category>
    <category>生活</category>
    <category>新手导航</category>
    <category>VIP</category>
    <category>教程</category>
    <category>AI</category>
    <category>Python</category>
    <category>Linux</category>
    <category>Rust</category>
    <category>Web</category>
    <category>跨平台</category>
    <category>iOS</category>
    <item>
      <title>关于本站</title>
      <link>https://www.linkjm.com/about-site/</link>
      <guid>https://www.linkjm.com/about-site/</guid>
      <source url="https://www.linkjm.com/rss.xml">关于本站</source>
      <description>关于本站 ✨📒 详细记录一下此次建站过程 开始 之前的博客是基于jekyll打造的，要添加和定制化的东西都只能基于html+js+css完成，有些麻烦，所以一直有想更换引擎的想法 直到偶然间发现vuepress，首先是被vue3+typescript+vite吸引，然后看到默认主题属实有点不合符我的期待，自己动手成本又太高，也没有太急着去折腾，直到无...</description>
      <category>Blog</category>
      <pubDate>Tue, 14 Mar 2023 21:45:45 GMT</pubDate>
      <content:encoded><![CDATA[
<div class="hint-container info">
<p class="hint-container-title">✨📒</p>
<p>详细记录一下此次建站过程</p>
</div>
<h2>开始</h2>
<p>之前的博客是基于jekyll打造的，要添加和定制化的东西都只能基于html+js+css完成，有些麻烦，所以一直有想更换引擎的想法<br>
直到偶然间发现vuepress，首先是被<code>vue3</code>+<code>typescript</code>+<code>vite</code>吸引，然后看到默认主题属实有点不合符我的期待，自己动手成本又太高，也没有太急着去折腾，直到无意中发现了<br>
<a href="https://theme-hope.vuejs.press/zh/" target="_blank" rel="noopener noreferrer">vuepress-theme-hope</a>，漂亮的外观一下子就吸引到我了，然后去官网深入研究了一番，发现二次开发的成本并不高，对于我来说比较友好，基本都是基于选项的配置型，和一小部分的定制开发，也可以基于vue来写，这让我觉得很合适。所以，一步步折腾了起来……</p>
<h2>近期更新</h2>
<h3>Changelog</h3>
<h4>2026-07-01 <code>e1a2760</code> - 新增音乐页</h4>
<ul>
<li>新增独立 <code>/music/</code> 音乐页面，基于自定义 <code>Music.vue</code> 布局，配置导航栏入口与主题路由。</li>
<li>重构布局 CSS，将 <code>_base.scss</code> 拆分为 <code>_buttons.scss</code>、<code>_layout.scss</code> 等独立模块，提升样式可维护性。</li>
<li>修复 <code>banner-button</code> 按钮样式，精简 About 和 Essay 页面冗余代码。</li>
<li>优化 About、Essay、Link 布局样式细节与动画配置。</li>
</ul>
<h4>2026-06-30 <code>61885d4</code> - 移除 pages.css，SCSS 模块化迁移</h4>
<ul>
<li>移除 5000+ 行的第三方 <code>pages.css</code>，将样式迁移至 SCSS 模块（<code>_animations.scss</code>、<code>_base.scss</code>、<code>_variables.scss</code>、<code>_skills-tags.scss</code>）。</li>
<li>About、Essay、Link 布局组件内联对应样式，减少全局 CSS 依赖。</li>
<li>删除重复样式内容，修复样式错乱问题。</li>
<li>更新 <code>.gitignore</code> 配置。</li>
</ul>
<h4>2026-06-29 <code>f636cd3</code> - 双栏布局 tag 样式与时间轴修复</h4>
<ul>
<li>优化双栏文章列表 tag 样式展示。</li>
<li>新增 <code>TimelineItems.vue</code> 自定义组件，修复时间轴页面渲染问题。</li>
</ul>
<h4>2026-06-29 <code>f38ac1f</code> - 新增友链页布局</h4>
<ul>
<li>新增独立 <code>/link/</code> 友链页面，迁移原 <code>friend.md</code> 内容到新的 Link layout。</li>
<li>抽离友链数据到 <code>src/.vuepress/data/link.ts</code>，统一维护站点信息、分组、申请说明与免责声明。</li>
<li>引入 AnZhiYu 风格的友链横幅、头像滚动、站点卡片和申请入口，并接入主题路由与导航。</li>
</ul>
<h4>2026-06-29 <code>692ab4d</code> - 重构关于页与即刻页</h4>
<ul>
<li>关于我迁移为独立 <code>/about/</code> 页面，基于 AnZhiYu 风格重做卡片布局、动效与个人信息数据源。</li>
<li>新增 <code>/essay/</code> 即刻布局与数据配置，替换原 news 说说列表实现。</li>
<li>补充相关静态资源、AnZhiYu 样式、评论背景、地图与站点 Logo 资源，同时保留关于本站为 <code>/about-site/</code>。</li>
</ul>
<h4>2026-06-26 <code>64a093</code> - 文章列表支持双栏</h4>
<ul>
<li>首页文章列表新增 <code>single</code> / <code>double</code> 两种展示模式，可在主题配置中切换阅读密度。</li>
<li>自定义 <code>ArticleList</code> 与 <code>ArticleItem</code>，重排封面、标题、分类、标签和日期信息。</li>
<li>为双栏卡片补充响应式样式，移动端自动回落为单栏展示。</li>
</ul>
<h2>Markdown增强</h2>
<p>hope主题的markdown效果是出乎意料的好，而且支持了很多普通markdown不支持的东西，如自定义容器、带tab的代码块，最方便的是可以直接写流程图了，可选高亮主题（本站代码高亮基于shikiPlugin,虽不如默认的prismjs轻量高效，但能提供更准确的语法高亮）具体效果看这里☞<a href="/demo/markdown.html" target="_blank">Markdown展示</a></p>
<h2>目录结构</h2>
<div class="language-shell line-numbers-mode" data-highlighter="shiki" data-ext="shell" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-shell"><span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">.</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> .github</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> ISSUE_TEMPLATE</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # issus 模版</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> bug-report.yml</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> workflows</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">       └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> deploy-docs.yml</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # 推送脚本</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> CNAME</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> LICENSE</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> README.md</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> api</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> proxy.js</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # 跨域代理</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> env.d.ts</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> package.json</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> pnpm-lock.yaml</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> script</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> requirements.txt</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> </span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> submit.py</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # Github Actions 推送URL使用脚本</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> src</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> .vuepress</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> client.ts</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # 客户端配置文件</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> components</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> MyCoverLink.vue</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # 友链组件</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> Mylink.vue</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # 卡片组件</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> config.ts</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # vuepress配置文件</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> data</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # 数据</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> navbar</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> plugins</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> vuepress-plugin-canvas</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> vuepress-plugin-gradient-cover</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> vuepress-plugin-hitokoto</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> vuepress-plugin-live2DAssist</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> vuepress-plugin-popper</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> public</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> assets</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # 资源</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> sidebar</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> styles</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> theme</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> api</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> bing.ts</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # bing 每日壁纸</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> components</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> #自定义组件</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> index.ts</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> layouts</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # 自定义布局</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> utils</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">       ├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> busuanzi.pure.js</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # 不蒜子统计</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">       └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> time.ts</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> #运行时间</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   │</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> theme.ts</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # 主题配置文件</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">   └──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> README.md</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> tsconfig.json</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">└──</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> vercel.json</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> # vercel 配置文件</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>框架支持</h2>
<p><a href="https://v2.vuepress.vuejs.org/zh/" target="_blank" rel="noopener noreferrer">vuepress2.x</a></p>
<h2>主题支持</h2>
<p><a href="https://theme-hope.vuejs.press/zh/" target="_blank" rel="noopener noreferrer">vuepress-theme-hope</a></p>
<h2>自定义内容</h2>
<p>基于原主题进行了继承，个性化内容如下，主要自定义内容分为</p>
<ol>
<li>
<p><strong>自定义布局</strong></p>
<ul>
<li>NotFound.vue</li>
<li>Layout.vue(增加打赏组件)</li>
<li>About.vue（关于我页面）</li>
<li>Essay.vue（即刻列表布局）</li>
<li>Link.vue（友链页面）</li>
</ul>
</li>
<li>
<p><strong>自定义组件</strong></p>
<ul>
<li>BlogHero.vue</li>
<li>ArticleList.vue（文章列表）</li>
<li>ArticleItem.vue（文章卡片）</li>
<li>PageFooter.vue</li>
<li>Sponsor.vue（打赏组件）</li>
</ul>
</li>
<li>
<p><strong>本地插件开发</strong></p>
<ul>
<li>vuepress-plugin-canvas（支持彩虹背景和动态几何图形两种）</li>
<li>vuepress-plugin-gradient-cover （遮罩背景）</li>
<li>vuepress-plugin-hitokoto （一言插件）</li>
<li>vuepress-plugin-live2DAssist （看板娘辅助，由于子页有sidebar，看板娘会挡住，所以写了一个子页隐藏的小东西）</li>
<li>vuepress-plugin-popper （鼠标特效，基于<a href="https://github.com/moefyit/moefy-canvas" target="_blank" rel="noopener noreferrer">@moefy-canvas/theme-popper</a>）</li>
</ul>
</li>
<li>
<p><strong>引用外部内容</strong></p>
<ul>
<li>
<p><a href="https://github.com/oh-my-live2d/vuepress-plugin-oh-my-live2d" target="_blank" rel="noopener noreferrer">vuepress-plugin-oh-my-live2d</a> 看板娘插件</p>
</li>
<li>
<p>不蒜子统计</p>
</li>
<li>
<p><a href="https://github.com/moefyit/moefy-canvas" target="_blank" rel="noopener noreferrer">@moefy-canvas/theme-popper</a>原有插件只支持vuepress1.x，自己基于moefy-canvas进行了支持vuepress2.x的本地化插件开发</p>
</li>
<li>
<p><a href="https://v2.vuepress.vuejs.org/zh/reference/plugin/google-analytics.html" target="_blank" rel="noopener noreferrer">@vuepress/plugin-google-analytics</a> 支持Google Analytics 4 正好看到通知原来的UA也要被强制转换了，所以更换了G4</p>
</li>
</ul>
</li>
<li>
<p><strong>配置内容</strong></p>
<ul>
<li>navbar</li>
<li>sidebar</li>
<li>评论基于 <a href="https://waline.js.org/" target="_blank" rel="noopener noreferrer">Waline</a></li>
<li>搜索基于<a href="https://www.algolia.com/developers/?utm_content=powered_by&amp;utm_source=localhost&amp;utm_medium=referral&amp;utm_campaign=docsearch" target="_blank" rel="noopener noreferrer">algolia</a></li>
<li>启用 copyright 版权信息插件</li>
<li>feed rss插件</li>
<li>增加文章类型-即刻，为即刻 markdown 图片添加预览选择器</li>
<li>友链页数据配置，统一维护站点分组、申请模板与免责声明</li>
<li>文章列表布局配置，支持 single / double 两种展示模式</li>
</ul>
</li>
<li>
<p><strong>零碎</strong></p>
<ul>
<li>运行时间统计</li>
<li>CSS 样式美化</li>
<li>引入字体，品如手写体，夏行楷体</li>
<li>wanlie 增加自定义emoji，并修改展示样式</li>
<li>个性log</li>
<li>自动推送新文章url到搜索引擎（百度、Bing、Google）👉<a href="/platform/github/github-action">详细配置</a></li>
</ul>
</li>
</ol>
<h2>总结</h2>
<blockquote>
<p>未完待续，持续优化中</p>
</blockquote>
<p>本地插件，喜欢自取，源码公开，点击右上角，github图标即可，当然不要忘记点个✨哦</p>
]]></content:encoded>
    </item>
    <item>
      <title>web3赚钱路径</title>
      <link>https://www.linkjm.com/basic/001.html</link>
      <guid>https://www.linkjm.com/basic/001.html</guid>
      <source url="https://www.linkjm.com/rss.xml">web3赚钱路径</source>
      <description>more 注释之前的内容被视为文章摘要。</description>
      <category>使用指南</category>
      <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><code>more</code> 注释之前的内容被视为文章摘要。</p>
<!-- more -->
<h2>页面标题</h2>
<p>The first H1 title in Markdown will be regarded as page title.</p>
<p>Markdown 中的第一个 H1 标题会被视为页面标题。</p>
<p>你可以在 Markdown 的 Frontmatter 中设置页面标题。</p>
<div class="language-md" data-highlighter="shiki" data-ext="md" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-md"><span class="line"></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>IP教程</title>
      <link>https://www.linkjm.com/basic/002.html</link>
      <guid>https://www.linkjm.com/basic/002.html</guid>
      <source url="https://www.linkjm.com/rss.xml">IP教程</source>
      <description>more 注释之前的内容被视为文章摘要。</description>
      <category>使用指南</category>
      <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><code>more</code> 注释之前的内容被视为文章摘要。</p>
<!-- more -->
<h2>页面标题</h2>
<p>The first H1 title in Markdown will be regarded as page title.</p>
<p>Markdown 中的第一个 H1 标题会被视为页面标题。</p>
<p>你可以在 Markdown 的 Frontmatter 中设置页面标题。</p>
<div class="language-md" data-highlighter="shiki" data-ext="md" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-md"><span class="line"></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Gmail+Twitter+Discord+Telegram账号购买</title>
      <link>https://www.linkjm.com/basic/003.html</link>
      <guid>https://www.linkjm.com/basic/003.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Gmail+Twitter+Discord+Telegram账号购买</source>
      <description>more 注释之前的内容被视为文章摘要。</description>
      <category>使用指南</category>
      <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><code>more</code> 注释之前的内容被视为文章摘要。</p>
<!-- more -->
<h2>页面标题</h2>
<p>The first H1 title in Markdown will be regarded as page title.</p>
<p>Markdown 中的第一个 H1 标题会被视为页面标题。</p>
<p>你可以在 Markdown 的 Frontmatter 中设置页面标题。</p>
<div class="language-md" data-highlighter="shiki" data-ext="md" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-md"><span class="line"></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>指纹浏览器安全设置</title>
      <link>https://www.linkjm.com/basic/004.html</link>
      <guid>https://www.linkjm.com/basic/004.html</guid>
      <source url="https://www.linkjm.com/rss.xml">指纹浏览器安全设置</source>
      <description>more 注释之前的内容被视为文章摘要。</description>
      <category>使用指南</category>
      <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><code>more</code> 注释之前的内容被视为文章摘要。</p>
<!-- more -->
<h2>页面标题</h2>
<p>The first H1 title in Markdown will be regarded as page title.</p>
<p>Markdown 中的第一个 H1 标题会被视为页面标题。</p>
<p>你可以在 Markdown 的 Frontmatter 中设置页面标题。</p>
<div class="language-md" data-highlighter="shiki" data-ext="md" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-md"><span class="line"></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>女巫教程</title>
      <link>https://www.linkjm.com/basic/005.html</link>
      <guid>https://www.linkjm.com/basic/005.html</guid>
      <source url="https://www.linkjm.com/rss.xml">女巫教程</source>
      <description>more 注释之前的内容被视为文章摘要。</description>
      <category>使用指南</category>
      <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><code>more</code> 注释之前的内容被视为文章摘要。</p>
<!-- more -->
<h2>页面标题</h2>
<p>The first H1 title in Markdown will be regarded as page title.</p>
<p>Markdown 中的第一个 H1 标题会被视为页面标题。</p>
<p>你可以在 Markdown 的 Frontmatter 中设置页面标题。</p>
<div class="language-md" data-highlighter="shiki" data-ext="md" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-md"><span class="line"></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>撸空投</title>
      <link>https://www.linkjm.com/basic/006.html</link>
      <guid>https://www.linkjm.com/basic/006.html</guid>
      <source url="https://www.linkjm.com/rss.xml">撸空投</source>
      <description>more 注释之前的内容被视为文章摘要。</description>
      <category>使用指南</category>
      <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><code>more</code> 注释之前的内容被视为文章摘要。</p>
<!-- more -->
<h2>页面标题</h2>
<p>The first H1 title in Markdown will be regarded as page title.</p>
<p>Markdown 中的第一个 H1 标题会被视为页面标题。</p>
<p>你可以在 Markdown 的 Frontmatter 中设置页面标题。</p>
<div class="language-md" data-highlighter="shiki" data-ext="md" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-md"><span class="line"></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>web3基础知识｜从入门到精通</title>
      <link>https://www.linkjm.com/basic/</link>
      <guid>https://www.linkjm.com/basic/</guid>
      <source url="https://www.linkjm.com/rss.xml">web3基础知识｜从入门到精通</source>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
    </item>
    <item>
      <title>一、</title>
      <link>https://www.linkjm.com/crypto/001.html</link>
      <guid>https://www.linkjm.com/crypto/001.html</guid>
      <source url="https://www.linkjm.com/rss.xml">一、</source>
      <description>一、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>]]></content:encoded>
    </item>
    <item>
      <title>二、</title>
      <link>https://www.linkjm.com/crypto/002.html</link>
      <guid>https://www.linkjm.com/crypto/002.html</guid>
      <source url="https://www.linkjm.com/rss.xml">二、</source>
      <description>二、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>
]]></content:encoded>
    </item>
    <item>
      <title>三、</title>
      <link>https://www.linkjm.com/crypto/003.html</link>
      <guid>https://www.linkjm.com/crypto/003.html</guid>
      <source url="https://www.linkjm.com/rss.xml">三、</source>
      <description>三、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>]]></content:encoded>
    </item>
    <item>
      <title>四、</title>
      <link>https://www.linkjm.com/crypto/004.html</link>
      <guid>https://www.linkjm.com/crypto/004.html</guid>
      <source url="https://www.linkjm.com/rss.xml">四、</source>
      <description>四、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>]]></content:encoded>
    </item>
    <item>
      <title>五、</title>
      <link>https://www.linkjm.com/crypto/005.html</link>
      <guid>https://www.linkjm.com/crypto/005.html</guid>
      <source url="https://www.linkjm.com/rss.xml">五、</source>
      <description>五、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>]]></content:encoded>
    </item>
    <item>
      <title>六、</title>
      <link>https://www.linkjm.com/crypto/006.html</link>
      <guid>https://www.linkjm.com/crypto/006.html</guid>
      <source url="https://www.linkjm.com/rss.xml">六、</source>
      <description>六、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>
]]></content:encoded>
    </item>
    <item>
      <title>七、</title>
      <link>https://www.linkjm.com/crypto/007.html</link>
      <guid>https://www.linkjm.com/crypto/007.html</guid>
      <source url="https://www.linkjm.com/rss.xml">七、</source>
      <description>七、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>
]]></content:encoded>
    </item>
    <item>
      <title>八、</title>
      <link>https://www.linkjm.com/crypto/008.html</link>
      <guid>https://www.linkjm.com/crypto/008.html</guid>
      <source url="https://www.linkjm.com/rss.xml">八、</source>
      <description>八、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>
]]></content:encoded>
    </item>
    <item>
      <title>九、</title>
      <link>https://www.linkjm.com/crypto/009.html</link>
      <guid>https://www.linkjm.com/crypto/009.html</guid>
      <source url="https://www.linkjm.com/rss.xml">九、</source>
      <description>九、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>]]></content:encoded>
    </item>
    <item>
      <title>十、</title>
      <link>https://www.linkjm.com/crypto/010.html</link>
      <guid>https://www.linkjm.com/crypto/010.html</guid>
      <source url="https://www.linkjm.com/rss.xml">十、</source>
      <description>十、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>]]></content:encoded>
    </item>
    <item>
      <title>十一、</title>
      <link>https://www.linkjm.com/crypto/011.html</link>
      <guid>https://www.linkjm.com/crypto/011.html</guid>
      <source url="https://www.linkjm.com/rss.xml">十一、</source>
      <description>十一、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>]]></content:encoded>
    </item>
    <item>
      <title>十二、</title>
      <link>https://www.linkjm.com/crypto/012.html</link>
      <guid>https://www.linkjm.com/crypto/012.html</guid>
      <source url="https://www.linkjm.com/rss.xml">十二、</source>
      <description>十二、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>]]></content:encoded>
    </item>
    <item>
      <title>十三、</title>
      <link>https://www.linkjm.com/crypto/013.html</link>
      <guid>https://www.linkjm.com/crypto/013.html</guid>
      <source url="https://www.linkjm.com/rss.xml">十三、</source>
      <description>十三、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>]]></content:encoded>
    </item>
    <item>
      <title>十四、</title>
      <link>https://www.linkjm.com/crypto/014.html</link>
      <guid>https://www.linkjm.com/crypto/014.html</guid>
      <source url="https://www.linkjm.com/rss.xml">十四、</source>
      <description>十四、</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>]]></content:encoded>
    </item>
    <item>
      <title>Web3 入门</title>
      <link>https://www.linkjm.com/crypto/</link>
      <guid>https://www.linkjm.com/crypto/</guid>
      <source url="https://www.linkjm.com/rss.xml">Web3 入门</source>
      <description>系统了解 Web3、区块链、钱包、DeFi、NFT、链上数据与资产安全。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>本栏目从 Web3 基础概念开始，逐步进入钱包和链上操作、项目研究、数据分析以及生态实践。建议第一次学习时按照章节顺序阅读。</p>
<h2>学习目录</h2>
<h3><a href="/crypto/chapter001/introduction.html" target="_blank">一、Web3 基础与行业全景</a></h3>
<p>理解 Web3、区块链、公链、钱包、DeFi、稳定币、NFT、GameFi、DAO、监管、行业机会与资产安全。</p>
<h3><a href="/crypto/chapter002/001.html" target="_blank">二、账户、钱包与链上操作</a></h3>
<p>完成交易所、钱包、区块浏览器、跨链桥与 DEX 等基础工具的准备，并建立安全操作习惯。</p>
<h3><a href="/crypto/chapter003/001.html" target="_blank">三、项目研究与估值框架</a></h3>
<p>学习赛道分类、公链估值、项目调研、长期标的分析，以及一级和二级市场的基本研究方法。</p>
<h3><a href="/crypto/chapter004/001.html" target="_blank">四、链上数据与情报分析</a></h3>
<p>掌握链上数据、信息流、Gas、地址分析、巨鲸监控和数据看板等常用研究方法。</p>
<h3><a href="/crypto/chapter005/001.html" target="_blank">五、加密货币交易实践</a></h3>
<p>通过 Binance、Bitget 等中心化交易所，学习现货、永续合约、订单类型、仓位管理与交易复盘。</p>
<h3><a href="/crypto/chapter006/001.html" target="_blank">六、链上美股与全球资产交易</a></h3>
<p>区分真实股票、代币化股票和美股合约，理解链上交易机制、产品权益以及平台与监管风险。</p>
<div class="hint-container warning">
<p class="hint-container-title">学习提示</p>
<p>部分文章形成时间较早，涉及的平台、政策、项目状态和操作步骤可能已经变化。涉及资金操作前，请核对最新官方资料，并先理解资产安全和风险管理。</p>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Markdown 展示</title>
      <link>https://www.linkjm.com/demo/markdown.html</link>
      <guid>https://www.linkjm.com/demo/markdown.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Markdown 展示</source>
      <description>VuePress 主要从 Markdown 文件生成页面。因此，你可以使用它轻松生成文档或博客站点。 你应该创建和编写 Markdown 文件，以便 VuePress 可以根据文件结构将它们转换为不同的页面。</description>
      <category>使用指南</category>
      <pubDate>Sat, 11 Feb 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>VuePress 主要从 Markdown 文件生成页面。因此，你可以使用它轻松生成文档或博客站点。</p>
<p>你应该创建和编写 Markdown 文件，以便 VuePress 可以根据文件结构将它们转换为不同的页面。</p>
<!-- more -->
<h2>Markdown 介绍</h2>
<p>如果你是一个新手，还不会编写 Markdown，请先阅读 <a href="https://theme-hope.vuejs.press/zh/cookbook/markdown/" target="_blank" rel="noopener noreferrer">Markdown 介绍</a> 和 <a href="https://theme-hope.vuejs.press/zh/cookbook/markdown/demo.html" target="_blank" rel="noopener noreferrer">Markdown 演示</a>。</p>
<h2>Markdown 配置</h2>
<p>VuePress 通过 Frontmatter 为每个 Markdown 页面引入配置。</p>
<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p>Frontmatter 是 VuePress 中很重要的一个概念，如果你不了解它，你需要阅读 <a href="https://theme-hope.vuejs.press/zh/cookbook/vuepress/page.html#front-matter" target="_blank" rel="noopener noreferrer">Frontmatter 介绍</a>。</p>
</div>
<h2>Markdown 扩展</h2>
<p>VuePress 会使用 <a href="https://github.com/markdown-it/markdown-it" target="_blank" rel="noopener noreferrer">markdown-it</a> 来解析 Markdown 内容，因此可以借助于 markdown-it 插件来实现 <a href="https://github.com/markdown-it/markdown-it#syntax-extensions" target="_blank" rel="noopener noreferrer">语法扩展</a> 。</p>
<h3>VuePress 扩展</h3>
<p>为了丰富文档写作，VuePress 对 Markdown 语法进行了扩展。</p>
<p>关于这些扩展，请阅读 <a href="https://theme-hope.vuejs.press/zh/cookbook/vuepress/markdown.html" target="_blank" rel="noopener noreferrer">VuePress 中的 Markdown 扩展</a>。</p>
<h3>主题扩展</h3>
<p>通过 [<code>vuepress-plugin-md-enhance</code>][md-enhance]，主题扩展了更多 Markdown 语法，提供更加丰富的写作功能。</p>
<h4>自定义容器</h4>
<div v-pre>
<p>安全的在 Markdown 中使用 {{ variable }}。</p>
</div>
<div class="hint-container info">
<p class="hint-container-title">自定义标题</p>
<p>信息容器，包含 <code>代码</code> 与 <a href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8">链接</a>。</p>
<div class="language-js" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> a</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span></code></pre>
</div></div>
<div class="hint-container tip">
<p class="hint-container-title">自定义标题</p>
<p>提示容器</p>
</div>
<div class="hint-container warning">
<p class="hint-container-title">自定义标题</p>
<p>警告容器</p>
</div>
<div class="hint-container caution">
<p class="hint-container-title">自定义标题</p>
<p>危险容器</p>
</div>
<details class="hint-container details"><summary>自定义标题</summary>
<p>详情容器</p>
</details>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/container.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>代码块</h4>

<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/code-tabs.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>上下角标</h4>
<p>19<sup>th</sup> H<sub>2</sub>O</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/sup-sub.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>自定义对齐</h4>
<div style="text-align:center">
<p>我是居中的</p>
</div>
<div style="text-align:right">
<p>我在右对齐</p>
</div>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/align.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>Attrs</h4>
<p>一个拥有 ID 的 <strong id="word">单词</strong>。</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/attrs.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>脚注</h4>
<p>此文字有脚注<sup class="footnote-ref"><a href="#footnote1">[1]</a><a class="footnote-anchor" id="footnote-ref1"></a></sup>.</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/footnote.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>标记</h4>
<p>你可以标记 <mark>重要的内容</mark> 。</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/mark.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>任务列表</h4>
<ul class="task-list-container">
<li class="task-list-item">
<p><input type="checkbox" class="task-list-item-checkbox" id="task-item-0" checked="checked" disabled="disabled"><label class="task-list-item-label" for="task-item-0"> 计划 1</label></p>
</li>
<li class="task-list-item">
<p><input type="checkbox" class="task-list-item-checkbox" id="task-item-1" disabled="disabled"><label class="task-list-item-label" for="task-item-1"> 计划 2</label></p>
</li>
<li>
<p><a href="https://theme-hope.vuejs.press/zh/guide/markdown/tasklist.html" target="_blank" rel="noopener noreferrer">查看详情</a></p>
</li>
</ul>
<h3>图片增强</h3>
<p>支持为图片设置颜色模式和大小</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/image.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>图表</h4>
<p>::: chart 一个散点图案例</p>
<div class="language-json line-numbers-mode" data-highlighter="shiki" data-ext="json" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-json"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  "type"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"scatter"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  "data"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    "datasets"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: [</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        "label"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"散点数据集"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        "data"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: [</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">          { </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">"x"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">-10</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">"y"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">          { </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">"x"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">"y"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">10</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">          { </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">"x"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">10</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">"y"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">5</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">          { </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">"x"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0.5</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">"y"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">5.5</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        ],</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        "backgroundColor"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"rgb(255, 99, 132)"</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    ]</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  "options"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    "scales"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">      "x"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        "type"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"linear"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        "position"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"bottom"</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>:::</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/chart.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>Echarts</h4>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/echarts.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>流程图</h4>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/flowchart.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>Mermaid</h4>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="footnote1" class="footnote-item"><p>这是脚注内容 <a href="#footnote-ref1" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
]]></content:encoded>
    </item>
    <item>
      <title>幻灯片页</title>
      <link>https://www.linkjm.com/demo/slides.html</link>
      <guid>https://www.linkjm.com/demo/slides.html</guid>
      <source url="https://www.linkjm.com/rss.xml">幻灯片页</source>
      <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<!-- markdownlint-disable MD024 MD033 MD051 -->
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/logo.svg" type="image/svg+xml"/>
    </item>
    <item>
      <title>电脑技术</title>
      <link>https://www.linkjm.com/computer/</link>
      <guid>https://www.linkjm.com/computer/</guid>
      <source url="https://www.linkjm.com/rss.xml">电脑技术</source>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
    </item>
    <item>
      <title>VIP会员内容介绍</title>
      <link>https://www.linkjm.com/docs/001.html</link>
      <guid>https://www.linkjm.com/docs/001.html</guid>
      <source url="https://www.linkjm.com/rss.xml">VIP会员内容介绍</source>
      <description>more 注释之前的内容被视为文章摘要。</description>
      <category>使用指南</category>
      <pubDate>Fri, 18 Nov 2022 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><code>more</code> 注释之前的内容被视为文章摘要。</p>
<!-- more -->
<h2>页面标题</h2>
<p>The first H1 title in Markdown will be regarded as page title.</p>
<p>Markdown 中的第一个 H1 标题会被视为页面标题。</p>
<p>你可以在 Markdown 的 Frontmatter 中设置页面标题。</p>
<div class="language-md" data-highlighter="shiki" data-ext="md" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-md"><span class="line"></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>声明和公告</title>
      <link>https://www.linkjm.com/docs/002.html</link>
      <guid>https://www.linkjm.com/docs/002.html</guid>
      <source url="https://www.linkjm.com/rss.xml">声明和公告</source>
      <description>more 注释之前的内容被视为文章摘要。</description>
      <category>使用指南</category>
      <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><code>more</code> 注释之前的内容被视为文章摘要。</p>
<!-- more -->
<h2>页面标题</h2>
<p>The first H1 title in Markdown will be regarded as page title.</p>
<p>Markdown 中的第一个 H1 标题会被视为页面标题。</p>
<p>你可以在 Markdown 的 Frontmatter 中设置页面标题。</p>
<div class="language-md" data-highlighter="shiki" data-ext="md" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-md"><span class="line"></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>声明和公告</title>
      <link>https://www.linkjm.com/docs/003.html</link>
      <guid>https://www.linkjm.com/docs/003.html</guid>
      <source url="https://www.linkjm.com/rss.xml">声明和公告</source>
      <description>more 注释之前的内容被视为文章摘要。</description>
      <category>使用指南</category>
      <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><code>more</code> 注释之前的内容被视为文章摘要。</p>
<!-- more -->
<h2>页面标题</h2>
<p>The first H1 title in Markdown will be regarded as page title.</p>
<p>Markdown 中的第一个 H1 标题会被视为页面标题。</p>
<p>你可以在 Markdown 的 Frontmatter 中设置页面标题。</p>
<div class="language-md" data-highlighter="shiki" data-ext="md" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-md"><span class="line"></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>其他</title>
      <link>https://www.linkjm.com/docs/</link>
      <guid>https://www.linkjm.com/docs/</guid>
      <source url="https://www.linkjm.com/rss.xml">其他</source>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
    </item>
    <item>
      <title>布局与功能禁用</title>
      <link>https://www.linkjm.com/docs/disable.html</link>
      <guid>https://www.linkjm.com/docs/disable.html</guid>
      <source url="https://www.linkjm.com/rss.xml">布局与功能禁用</source>
      <description>你可以通过设置页面的 Frontmatter，在页面禁用功能与布局。</description>
      <category>使用指南</category>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[<p>你可以通过设置页面的 Frontmatter，在页面禁用功能与布局。</p>
<!-- more -->
<p>本页面就是一个示例，禁用了如下功能:</p>
<ul>
<li>导航栏</li>
<li>侧边栏</li>
<li>路径导航</li>
<li>页面信息</li>
<li>贡献者</li>
<li>编辑此页链接</li>
<li>更新时间</li>
<li>上一篇/下一篇 链接</li>
<li>评论</li>
<li>页脚</li>
<li>返回顶部按钮</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>密码加密的文章</title>
      <link>https://www.linkjm.com/docs/encrypt.html</link>
      <guid>https://www.linkjm.com/docs/encrypt.html</guid>
      <source url="https://www.linkjm.com/rss.xml">密码加密的文章</source>
      <description>密码加密的文章 实际的文章内容。 段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字。 段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2...</description>
      <category>使用指南</category>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>实际的文章内容。</p>
<p>段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字段落 1 文字。</p>
<p>段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字段落 2 文字。</p>
]]></content:encoded>
    </item>
    <item>
      <title>布局</title>
      <link>https://www.linkjm.com/docs/layout.html</link>
      <guid>https://www.linkjm.com/docs/layout.html</guid>
      <source url="https://www.linkjm.com/rss.xml">布局</source>
      <description>布局包括: 导航栏 侧边栏 页脚 同时每个页面包含: 路径导航 标题和页面信息 TOC (文章标题列表) 贡献者、更新时间等页面元信息 评论 主题也带有以下元素: 夜间模式按钮 返回顶部按钮 打印按钮 你可以在主题选项和页面的 frontmatter 中自定义它们。</description>
      <category>指南</category>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[<p>布局包括:</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/layout/navbar.html" target="_blank" rel="noopener noreferrer">导航栏</a></li>
<li><a href="https://theme-hope.vuejs.press/zh/guide/layout/sidebar.html" target="_blank" rel="noopener noreferrer">侧边栏</a></li>
<li><a href="https://theme-hope.vuejs.press/zh/guide/layout/footer.html" target="_blank" rel="noopener noreferrer">页脚</a></li>
</ul>
<p>同时每个页面包含:</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/layout/breadcrumb.html" target="_blank" rel="noopener noreferrer">路径导航</a></li>
<li><a href="https://theme-hope.vuejs.press/zh/guide/feature/page-info.html" target="_blank" rel="noopener noreferrer">标题和页面信息</a></li>
<li><a href="https://theme-hope.vuejs.press/zh/guide/layout/page.html#%E6%A0%87%E9%A2%98%E5%88%97%E8%A1%A8" target="_blank" rel="noopener noreferrer">TOC (文章标题列表)</a></li>
<li><a href="https://theme-hope.vuejs.press/guide/feature/meta.html" target="_blank" rel="noopener noreferrer">贡献者、更新时间等页面元信息</a></li>
<li><a href="https://theme-hope.vuejs.press/zh/guide/feature/comment.html" target="_blank" rel="noopener noreferrer">评论</a></li>
</ul>
<p>主题也带有以下元素:</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/interface/darkmode.html" target="_blank" rel="noopener noreferrer">夜间模式按钮</a></li>
<li><a href="https://theme-hope.vuejs.press/guide/interface/others.html#%E8%BF%94%E5%9B%9E%E9%A1%B6%E9%83%A8%E6%8C%89%E9%92%AE" target="_blank" rel="noopener noreferrer">返回顶部按钮</a></li>
<li><a href="https://theme-hope.vuejs.press/guide/interface/others.html#%E6%89%93%E5%8D%B0%E6%8C%89%E9%92%AE" target="_blank" rel="noopener noreferrer">打印按钮</a></li>
</ul>
<p>你可以在主题选项和页面的 frontmatter 中自定义它们。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Markdown 展示</title>
      <link>https://www.linkjm.com/docs/markdown.html</link>
      <guid>https://www.linkjm.com/docs/markdown.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Markdown 展示</source>
      <description>VuePress 主要从 Markdown 文件生成页面。因此，你可以使用它轻松生成文档或博客站点。 你应该创建和编写 Markdown 文件，以便 VuePress 可以根据文件结构将它们转换为不同的页面。</description>
      <category>使用指南</category>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[<p>VuePress 主要从 Markdown 文件生成页面。因此，你可以使用它轻松生成文档或博客站点。</p>
<p>你应该创建和编写 Markdown 文件，以便 VuePress 可以根据文件结构将它们转换为不同的页面。</p>
<!-- more -->
<h2>Markdown 介绍</h2>
<p>如果你是一个新手，还不会编写 Markdown，请先阅读 <a href="https://theme-hope.vuejs.press/zh/cookbook/markdown/" target="_blank" rel="noopener noreferrer">Markdown 介绍</a> 和 <a href="https://theme-hope.vuejs.press/zh/cookbook/markdown/demo.html" target="_blank" rel="noopener noreferrer">Markdown 演示</a>。</p>
<h2>Markdown 配置</h2>
<p>VuePress 通过 Frontmatter 为每个 Markdown 页面引入配置。</p>
<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p>Frontmatter 是 VuePress 中很重要的一个概念，如果你不了解它，你需要阅读 <a href="https://theme-hope.vuejs.press/zh/cookbook/vuepress/page.html#front-matter" target="_blank" rel="noopener noreferrer">Frontmatter 介绍</a>。</p>
</div>
<h2>Markdown 扩展</h2>
<p>VuePress 会使用 <a href="https://github.com/markdown-it/markdown-it" target="_blank" rel="noopener noreferrer">markdown-it</a> 来解析 Markdown 内容，因此可以借助于 markdown-it 插件来实现 <a href="https://github.com/markdown-it/markdown-it#syntax-extensions" target="_blank" rel="noopener noreferrer">语法扩展</a> 。</p>
<h3>VuePress 扩展</h3>
<p>为了丰富文档写作，VuePress 对 Markdown 语法进行了扩展。</p>
<p>关于这些扩展，请阅读 <a href="https://theme-hope.vuejs.press/zh/cookbook/vuepress/markdown.html" target="_blank" rel="noopener noreferrer">VuePress 中的 Markdown 扩展</a>。</p>
<h3>主题扩展</h3>
<p>通过 <a href="https://plugin-md-enhance.vuejs.press/zh/" target="_blank" rel="noopener noreferrer"><code>vuepress-plugin-md-enhance</code></a>，主题扩展了更多 Markdown 语法，提供更加丰富的写作功能。</p>
<h4>提示容器</h4>
<div v-pre>
<p>安全的在 Markdown 中使用 {{ variable }}。</p>
</div>
<div class="hint-container info">
<p class="hint-container-title">自定义标题</p>
<p>信息容器，包含 <code>代码</code> 与 <a href="#%E6%8F%90%E7%A4%BA%E5%AE%B9%E5%99%A8">链接</a>。</p>
<div class="language-js" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> a</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span></code></pre>
</div></div>
<div class="hint-container tip">
<p class="hint-container-title">自定义标题</p>
<p>提示容器</p>
</div>
<div class="hint-container warning">
<p class="hint-container-title">自定义标题</p>
<p>警告容器</p>
</div>
<div class="hint-container caution">
<p class="hint-container-title">自定义标题</p>
<p>危险容器</p>
</div>
<details class="hint-container details"><summary>自定义标题</summary>
<p>详情容器</p>
</details>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/hint.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>代码块</h4>

<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/code-tabs.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>上下角标</h4>
<p>19<sup>th</sup> H<sub>2</sub>O</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/sup-sub.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>自定义对齐</h4>
<div style="text-align:center">
<p>我是居中的</p>
</div>
<div style="text-align:right">
<p>我在右对齐</p>
</div>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/align.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>Attrs</h4>
<p>一个拥有 ID 的 <strong id="word">单词</strong>。</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/attrs.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>脚注</h4>
<p>此文字有脚注<sup class="footnote-ref"><a href="#footnote1">[1]</a><a class="footnote-anchor" id="footnote-ref1"></a></sup>.</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/footnote.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>标记</h4>
<p>你可以标记 <mark>重要的内容</mark> 。</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/mark.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>任务列表</h4>
<ul class="task-list-container">
<li class="task-list-item">
<p><input type="checkbox" class="task-list-item-checkbox" id="task-item-0" checked="checked" disabled="disabled"><label class="task-list-item-label" for="task-item-0"> 计划 1</label></p>
</li>
<li class="task-list-item">
<p><input type="checkbox" class="task-list-item-checkbox" id="task-item-1" disabled="disabled"><label class="task-list-item-label" for="task-item-1"> 计划 2</label></p>
</li>
<li>
<p><a href="https://theme-hope.vuejs.press/zh/guide/markdown/tasklist.html" target="_blank" rel="noopener noreferrer">查看详情</a></p>
</li>
</ul>
<h3>图片增强</h3>
<p>支持为图片设置颜色模式和大小</p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/image.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>组件</h4>
<div class="language-component" data-highlighter="shiki" data-ext="component" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-component"><span class="line"><span>title: Mr.Hope</span></span>
<span class="line"><span>desc: Where there is light, there is hope</span></span>
<span class="line"><span>logo: https://mister-hope.com/logo.svg</span></span>
<span class="line"><span>link: https://mister-hope.com</span></span>
<span class="line"><span>background: rgba(253, 230, 138, 0.15)</span></span></code></pre>
</div><ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/component.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>导入文件</h4>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/include.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>样式化</h4>
<p>向 Mr.Hope 捐赠一杯咖啡。 </p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/stylize.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>Tex 语法</h4>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><msup><mi mathvariant="normal">∂</mi><mi>r</mi></msup><mrow><mi mathvariant="normal">∂</mi><msup><mi>ω</mi><mi>r</mi></msup></mrow></mfrac><mrow><mo fence="true">(</mo><mfrac><msup><mi>y</mi><mi>ω</mi></msup><mi>ω</mi></mfrac><mo fence="true">)</mo></mrow><mo>=</mo><mrow><mo fence="true">(</mo><mfrac><msup><mi>y</mi><mi>ω</mi></msup><mi>ω</mi></mfrac><mo fence="true">)</mo></mrow><mrow><mo fence="true">{</mo><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><mi>y</mi><msup><mo stretchy="false">)</mo><mi>r</mi></msup><mo>+</mo><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>r</mi></munderover><mfrac><mrow><mo stretchy="false">(</mo><mo>−</mo><mn>1</mn><msup><mo stretchy="false">)</mo><mi>i</mi></msup><mi>r</mi><mo>⋯</mo><mo stretchy="false">(</mo><mi>r</mi><mo>−</mo><mi>i</mi><mo>+</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><mi>y</mi><msup><mo stretchy="false">)</mo><mrow><mi>r</mi><mo>−</mo><mi>i</mi></mrow></msup></mrow><msup><mi>ω</mi><mi>i</mi></msup></mfrac><mo fence="true">}</mo></mrow></mrow><annotation encoding="application/x-tex">\frac {\partial^r} {\partial \omega^r} \left(\frac {y^{\omega}} {\omega}\right)
= \left(\frac {y^{\omega}} {\omega}\right) \left\{(\log y)^r + \sum_{i=1}^r \frac {(-1)^i r \cdots (r-i+1) (\log y)^{r-i}} {\omega^i} \right\}
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.4em;vertical-align:-0.95em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord" style="margin-right:0.0556em;">∂</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.5904em;"><span style="top:-2.989em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em;">r</span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord" style="margin-right:0.0556em;">∂</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em;">r</span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3414em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em;">ω</span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">)</span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:3.0277em;vertical-align:-1.2777em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3414em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em;">ω</span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">)</span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size4">{</span></span><span class="mopen">(</span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7144em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em;">r</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6514em;"><span style="top:-1.8723em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.3em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em;">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2777em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5017em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7507em;"><span style="top:-2.989em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mopen">(</span><span class="mord">−</span><span class="mord">1</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8247em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.0278em;">r</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">⋯</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0278em;">r</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">1</span><span class="mclose">)</span><span class="mopen">(</span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8247em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em;">r</span><span class="mbin mtight">−</span><span class="mord mathnormal mtight">i</span></span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size4">}</span></span></span></span></span></span></span></p>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/tex.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>图表</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/chartjs.html" width="100%" height="450"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/chartjs.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>Echarts</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/echarts.html" width="100%" height="800"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/echarts.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>流程图</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/flowchart.html" width="100%" height="450"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/flowchart.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>MarkMap</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/markmap.html" width="100%" height="380"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/markmap.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>Mermaid</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/mermaid.html" width="100%" height="620"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/mermaid.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>代码演示</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/code-demo.html" width="100%" height="450"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/demo.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>交互演示</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/playground.html" width="100%" height="480"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/playground.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>Kotlin 交互演示</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/kotlin-playground.html" width="100%" height="220"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/kotlin-playground.html" target="_blank" rel="noopener noreferrer">View Detail</a></li>
</ul>
<h4>Vue 交互演示</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/vue-playground.html" width="100%" height="380"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/vue-playground.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>Sandpack 交互演示</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/sandpack.html" width="100%" height="380"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/sandpack.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<h4>幻灯片</h4>
<iframe src="https://plugin-md-enhance-demo.vuejs.press/snippet/revealjs.html" width="100%" height="400"></iframe>
<ul>
<li><a href="https://theme-hope.vuejs.press/zh/guide/markdown/revealjs.html" target="_blank" rel="noopener noreferrer">查看详情</a></li>
</ul>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="footnote1" class="footnote-item"><p>这是脚注内容 <a href="#footnote-ref1" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
]]></content:encoded>
    </item>
    <item>
      <title>页面配置</title>
      <link>https://www.linkjm.com/docs/page.html</link>
      <guid>https://www.linkjm.com/docs/page.html</guid>
      <source url="https://www.linkjm.com/rss.xml">页面配置</source>
      <description>more 注释之前的内容被视为文章摘要。</description>
      <category>使用指南</category>
      <pubDate>Wed, 01 Jan 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><code>more</code> 注释之前的内容被视为文章摘要。</p>
<!-- more -->
<h2>页面标题</h2>
<p>The first H1 title in Markdown will be regarded as page title.</p>
<p>Markdown 中的第一个 H1 标题会被视为页面标题。</p>
<p>你可以在 Markdown 的 Frontmatter 中设置页面标题。</p>
<div class="language-md" data-highlighter="shiki" data-ext="md" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-md"><span class="line"></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>新手导航</title>
      <link>https://www.linkjm.com/guide/</link>
      <guid>https://www.linkjm.com/guide/</guid>
      <source url="https://www.linkjm.com/rss.xml">新手导航</source>
      <description>从这里了解站点的主要板块，并选择适合自己的学习入口。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>欢迎来到交易学习站。你可以按照学习阶段依次阅读，也可以直接进入感兴趣的板块。</p>
<h2>站点板块</h2>
<h3><a href="/" target="_blank">首页</a></h3>
<p>返回网站首页，查看站点概览和近期内容。</p>
<h3><a href="/start/" target="_blank">快速开始</a></h3>
<p>面向新手的准备指南，包括常用应用、交易所、Web3 钱包、网络工具、基础术语和安全注意事项。</p>
<h3><a href="/crypto/" target="_blank">Web3 入门</a></h3>
<p>了解 Web3 的基础概念、常用工具、账户环境以及参与链上生态需要掌握的基本知识。</p>
<h3><a href="/finance/" target="_blank">交易知识手册</a></h3>
<p>系统学习市场机制、风险管理、价格行为、技术分析、策略验证、交易记录和复盘方法。</p>
<h3><a href="/vip/" target="_blank">VIP 文档</a></h3>
<p>汇总进阶教程、实践资料和会员专属内容。</p>
<h3><a href="/nav/" target="_blank">加密导航</a></h3>
<p>集中整理交易所、行情数据、链上工具、研究平台、钱包和常用加密行业网站。</p>
<h3><a href="/link/" target="_blank">友链</a></h3>
<p>发现其他值得阅读的个人网站、技术博客和内容社区。</p>
<h3><a href="/books/" target="_blank">书单</a></h3>
<p>查看交易、投资、金融、技术与个人成长相关的推荐书籍。</p>
<h3><a href="/essay/" target="_blank">即刻</a></h3>
<p>记录简短观点、学习片段、市场观察和日常思考。</p>
<div class="hint-container tip">
<p class="hint-container-title">建议的学习顺序</p>
<p>第一次访问可以从“快速开始”进入，再依次阅读“Web3 入门”和“交易知识手册”。开始实践前，请优先掌握风险管理与资产安全。</p>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>节点搭建｜挖矿教程</title>
      <link>https://www.linkjm.com/node/</link>
      <guid>https://www.linkjm.com/node/</guid>
      <source url="https://www.linkjm.com/rss.xml">节点搭建｜挖矿教程</source>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
    </item>
    <item>
      <title>一人一句宋词</title>
      <link>https://www.linkjm.com/private/%E4%B8%80%E4%BA%BA%E4%B8%80%E5%8F%A5%E5%AE%8B%E8%AF%8D.html</link>
      <guid>https://www.linkjm.com/private/%E4%B8%80%E4%BA%BA%E4%B8%80%E5%8F%A5%E5%AE%8B%E8%AF%8D.html</guid>
      <source url="https://www.linkjm.com/rss.xml">一人一句宋词</source>
      <description>Bing 每日壁纸Bing 每日壁纸 写来留待日后慢慢欣赏 😜 苏轼 十年生死两茫茫， 不思量，自难忘。 陆游 红酥手，黄藤酒， 满城春色宫墙柳。 李清照 此情无计可消除， 才下眉头，却上心头。 辛弃疾 众里寻他千百度。 蓦然回首，那人却在，灯火阑珊处。 柳永 多情自古伤离别， 更那堪冷落清秋节！ 范仲淹 羌管悠悠霜满地， 人不寐，将军白发征夫泪。 ...</description>
      <category>生活</category>
      <pubDate>Mon, 16 Jan 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<figure><img src="https://file.mo7.cc/api/public/bz?idx=1" alt="Bing 每日壁纸" tabindex="0" loading="lazy"><figcaption>Bing 每日壁纸</figcaption></figure>
<blockquote>
<p>写来留待日后慢慢欣赏 😜</p>
</blockquote>
<h3>苏轼</h3>
<p>十年生死两茫茫，<br>
不思量，自难忘。</p>
<h3>陆游</h3>
<p>红酥手，黄藤酒，<br>
满城春色宫墙柳。</p>
<h3>李清照</h3>
<p>此情无计可消除，<br>
才下眉头，却上心头。</p>
<h3>辛弃疾</h3>
<p>众里寻他千百度。<br>
蓦然回首，那人却在，灯火阑珊处。</p>
<h3>柳永</h3>
<p>多情自古伤离别，<br>
更那堪冷落清秋节！</p>
<h3>范仲淹</h3>
<p>羌管悠悠霜满地，<br>
人不寐，将军白发征夫泪。</p>
<h3>晏殊</h3>
<p>无可奈何花落去，<br>
似曾相识燕归来。</p>
<h3>岳飞</h3>
<p>三十功名尘与土，八千里路云和月。<br>
莫等闲、白了少年头，空悲切！</p>
<h3>秦观</h3>
<p>两情若是久长时，<br>
又岂在朝朝暮暮。</p>
<h3>李之仪</h3>
<p>只愿君心似我心，<br>
定不负相思意。</p>
<h3>欧阳修</h3>
<p>人生自是有情痴，<br>
此恨不关风与月。</p>
<h3>唐婉</h3>
<p>世情薄，人情恶，<br>
雨送黄昏花易落。</p>
<h3>姜夔</h3>
<p>二十四桥仍在，<br>
波心荡、冷月无声。</p>
<h3>晏几道</h3>
<p>琵琶弦上说相思。<br>
当时明月在，曾照彩云归。</p>
<h3>李冠</h3>
<p>一寸相思千万绪。<br>
人间没个安排处。</p>
<h3>宋祁</h3>
<p>绿杨烟外晓寒轻，<br>
红杏枝头春意闹。</p>
<h3>周彦邦</h3>
<p>执手霜风吹鬓影，<br>
去意徊徨，别语愁难听。</p>
<h3>万俟咏</h3>
<p>短长亭，古今情。<br>
楼外凉蟾一晕生，雨余秋更清。</p>
<h3>蒋捷</h3>
<p>流光容易把人抛，<br>
红了樱桃，绿了芭蕉。</p>
<h3>吴潜</h3>
<p>老去惜花心，相对花无语。</p>
<h3>刘著</h3>
<p>江南几度梅花发，<br>
人在天涯鬓已斑。</p>
<h3>贺铸</h3>
<p>试问闲情都几许。<br>
一川烟草，满城风絮。梅子黄时雨。</p>
<h3>严蕊</h3>
<p>不是爱风尘，似被前缘误。<br>
花落花开自有时，总赖东君主。</p>
<h3>黄庭坚</h3>
<p>若有人知春去处。<br>
唤取归来同住。</p>
<h3>张先</h3>
<p>天不老，情难绝。<br>
心似双丝网，中有千千结。</p>
<h3>晁补之</h3>
<p>人生无奈别离何。<br>
夜长嫌梦短，泪少怕愁多。</p>
<h3>乐婉</h3>
<p>若是前生未有缘，<br>
待重结、来生愿。</p>
<h3>朱淑真</h3>
<p>把酒送春春不语。黄昏却下潇潇雨。</p>
<blockquote>
<p>看完了，跪安吧。╮(╯_╰)╭</p>
</blockquote>
]]></content:encoded>
      <enclosure url="https://file.mo7.cc/api/public/bz?idx=1" type="image/"/>
    </item>
    <item>
      <title>常用APP下载｜美区Apple ID注册</title>
      <link>https://www.linkjm.com/start/001.html</link>
      <guid>https://www.linkjm.com/start/001.html</guid>
      <source url="https://www.linkjm.com/rss.xml">常用APP下载｜美区Apple ID注册</source>
      <description>Apple ID注册

基本都可以自己注册成功，如果总是注册失败或者不想花时间注册可以看下方的购买方法

一、打开电脑浏览器，收入苹果官网地址：https://appleid.apple.com，点击右上角创建你的Apple ID，进入注册页面
image-20240331201456172image-20240331201456172
填写姓名，国家...</description>
      <category>新手导航</category>
      <pubDate>Sat, 16 Jan 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<!-- more -->
<h2>Apple ID注册</h2>
<blockquote>
<p>基本都可以自己注册成功，如果总是注册失败或者不想花时间注册可以看下方的购买方法</p>
</blockquote>
<p>一、打开电脑浏览器，收入苹果官网地址：<a href="https://appleid.apple.com" target="_blank" rel="noopener noreferrer">https://appleid.apple.com</a>，点击右上角创建你的Apple ID，进入注册页面</p>
<figure><figcaption>image-20240331201456172</figcaption></figure>
<p>填写姓名，国家或地区选择香港（注册地区），出生日期、邮箱、密码等信息，填写待验证的手机号码，如无香港手机号码，可以选择并填写中国大陆手机号码。</p>
<p>输入图片验证码</p>
<figure><figcaption>image-20240331201824963</figcaption></figure>
<p>查看邮箱收到的验证码，输入到弹出的对话框</p>
<figure><figcaption>image-20240331202304972</figcaption></figure>
<p>查看手机收到的验证码，填入到弹出的对话框</p>
<figure><figcaption>image-20240331202931588</figcaption></figure>
<p>注册成功：</p>
<figure><figcaption>image-20240331203044955</figcaption></figure>
<p>打开iPhone的App Store，输入刚刚注册的邮箱和密码，点击下方的登陆，不是右上角完成。</p>
<figure><figcaption>image-20240331204404828</figcaption></figure>
<p>输入手机收到的验证码。</p>
<figure><figcaption>image-20240331204453406</figcaption></figure>
<p>点击继续</p>
<figure><figcaption>image-20240331204523579</figcaption></figure>
<p>登陆成功</p>
<figure><figcaption>image-20240331204633508</figcaption></figure>
<h2>Apple ID购买</h2>
<blockquote>
<p>因为是购买的ID，购买后注意要修改密码，换绑成自己的邮箱和手机号</p>
<p>如需购买Apple ID，请联系小助理</p>
</blockquote>
<div class="hint-container tip">
<p class="hint-container-title">常用地区的Apple ID</p>
<p><strong>中国香港 - Apple ID</strong></p>
<p><strong>新加坡 - Apple ID</strong></p>
<p><strong>美国 - Apple ID</strong></p>
<p><strong>韩国 - Apple ID</strong></p>
</div>
<br>
<p>关于绑定手机号说明<br>
如需要绑定手机号（推荐绑定），点击继续按钮，按页面提示输入手机号绑定即可，注意输入手机号页面选择中国+86（Tip：绑定任意国家手机号都不会改变苹果账号所属国家/地区），当然如果你有其他国家手机号，选择相应区号即可</p>
<ol>
<li>苹果App用哪个账号下载，就必须哪个账号才能更新</li>
</ol>
<p>苹果更改资料教程（避免被盗扯皮）</p>
<p>重要提示<br>
修改全部资料包括修改邮箱（AppleID）/密码/密保资料（或绑定手机号），建议先改邮箱，成功修改邮箱后再更改其他资料；如不按教程修改全部资料，此后遇到任何问题或损失自行承担！</p>
<p>登陆苹果官网<br>
打开苹果官方账号管理页面（这是苹果官网，打不开的自行检查你的网络）<br>
<a href="https://appleid.apple.com/" target="_blank" rel="noopener noreferrer">https://appleid.apple.com</a><br>
登陆购买的账号（强调一遍，是登陆你购买的账号改资料）</p>
<ol start="2">
<li>关于密保问题</li>
</ol>
<p>各个账号密保问题答案不相同，以购买收到的商品内容为准，答案不需要输入中文。</p>
<p>关于Apple ID安全提示<br>
登陆出现Apple ID安全提示，也就是提示绑定手机升级双重验证，如不想绑定手机就点【其他选项】，再点【不升级】跳过，如下图所示；当然，如果你要绑定手机（推荐，可以用中国手机号码），就点【继续】按钮</p>
<ol start="3">
<li>更改邮箱（Apple ID）必须！</li>
</ol>
<p>点击【Apple ID】即可更改邮箱，更改为你自己的邮箱，之后登录就是用此邮箱登录；注意以下几点</p>
<p>更改Apple ID为你自己的邮箱，是你自己的邮箱，不是让你乱编一个邮箱<br>
新邮箱不能是其他苹果账号已经占用的，否则会提示“无法使用此电子邮件地址”<br>
新邮箱不分什么国内国外，任意国家且只要满足以上条件均可<br>
邮箱更改成功后，此苹果账号就是用新邮箱登录，跟原来的旧邮箱没有任何关系了</p>
<ol start="4">
<li>更改密码/密保资料（必须）</li>
</ol>
<p>点击【密码】即可更改登录密码<br>
点击【账户安全】即可更改密保资料或绑定手机号码<br>
注意：如果绑定手机号，那密保资料就自动删除了<br>
温馨提示：更改资料过程中会让你再次输入密码确认操作，此时输入的是苹果账号密码，苹果官网不会要求你输入邮箱密码，如果你改了密码，那输入的就是你更改后的苹果密码</p>
<ol start="5">
<li>关于账单地址（非必须）</li>
</ol>
<p>此项非必须甚至不建议小白操作<br>
美国购买商品有消费税，所以如果你要自行购买付费商品，请百度找一个美国免税州地址更改账单地址，可以省一点点钱；当然，不更改并不影响账号使用，且不建议小白自行更改，不自行购买付费app的话也不建议修改，防止出现各种奇奇怪怪的问题；同时新设备异地使用余额消费可能导致风控限购，所以本站不建议自行购买付费商品！</p>
<ol start="6">
<li>关于更改密码出现错误提示</li>
</ol>
<p>如果更改密码时出现红色“Unexpected HTTP response: undefined undefined”错误提示，可以按以下步骤解决</p>
<p>当前密码、新密码、确认新密码，三个框都输入后<br>
删除新密码输入框内的内容<br>
注意注意：是删除中间输入框里的内容，不要动确认新密码里的内容<br>
从新在中间新密码输入框输入你需要设置的新密码<br>
输入完毕后不要点击任何地方，此时下方的【更改密码】按钮将变成可以点击状态，点击【更改密码】即可</p>
<h2>Google Play安装</h2>
<h2>组件</h2>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>注册交易所｜币安/欧意OKX/芝麻等</title>
      <link>https://www.linkjm.com/start/002.html</link>
      <guid>https://www.linkjm.com/start/002.html</guid>
      <source url="https://www.linkjm.com/rss.xml">注册交易所｜币安/欧意OKX/芝麻等</source>
      <description>几大交易所官网链接

交易大所，用下方的邀请链接注册最高省【55%手续费】
●币安：第一大所，大资金优先（邀请码：LINKJM）
●欧意OKX：前三大所，出入金好用，web3钱包撸空投必备（ 邀请码：70998013）
●芝麻Gate.io：老牌大所，跟单很好用（邀请码：AwIRUwxZ）
●Bitget：新币和交易产品较丰富（邀请码：MLHY8C0N...</description>
      <category>新手导航</category>
      <pubDate>Tue, 08 Nov 2022 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<!-- more -->
<h2>几大交易所官网链接</h2>
<div class="hint-container important">
<p class="hint-container-title">交易大所，用下方的邀请链接注册最高省【55%手续费】</p>
<p>●<a href="https://www.binance.com/register?ref=LINKJM" target="_blank" rel="noopener noreferrer">币安</a>：第一大所，大资金优先（邀请码：LINKJM）</p>
<p>●<a href="https://okx.com/join/70998013" target="_blank" rel="noopener noreferrer">欧意OKX</a>：前三大所，出入金好用，web3钱包撸空投必备（ 邀请码：70998013）</p>
<p>●<a href="https://www.gate.io/signup/AwIRUwxZ?ref_type=103" target="_blank" rel="noopener noreferrer">芝麻Gate.io</a>：老牌大所，跟单很好用（邀请码：AwIRUwxZ）</p>
<p>●<a href="https://share.glassgs.com/u/50NAXL0D" target="_blank" rel="noopener noreferrer">Bitget</a>：新币和交易产品较丰富（邀请码：MLHY8C0N）</p>
<p>●<a href="https://www.bybit.com/invite?ref=66L99N" target="_blank" rel="noopener noreferrer">Bybit</a>：新所，新币多机会多（邀请码：66L99N）</p>
<p>●<a href="https://www.xtcore.plus/zh-CN/accounts/register?ref=7WAN4Y" target="_blank" rel="noopener noreferrer">XT</a>：新所，新币多机会多（邀请码：7WAN4Y）</p>
<p>●<a href="https://www.hashkey.com/zh-CN/register/invite?invite_code=zzOYRR" target="_blank" rel="noopener noreferrer">HashKey</a>：香港持牌交易所，出入金安全，需要大陆以外KYC（邀请码：zzOYRR）</p>
</div>
<p><a href="https://www.binance.info/zh-CN/activity/referral-entry/CPA?ref=CPA_00EITA059B" target="_blank" rel="noopener noreferrer">https://www.binance.info/zh-CN/activity/referral-entry/CPA?ref=CPA_00EITA059B</a></p>
<p><strong>注册要点</strong></p>
<p>●注册时建议使用梯子定位到不受限制的地方（中国、美国、俄罗斯等受监管的可能都不行，要么被限制，我习惯定位到台湾、韩国、新加坡等加密友好的地方。</p>
<p>●均可以继续使用身份证进行KYC，护照和其他国家的居住证也可以认证。</p>
<h2>币安注册教程</h2>
<p>注册建议使用邮箱，推荐注册“<a href="https://mail.google.com/" target="_blank" rel="noopener noreferrer">google邮箱</a>”（需梯子）</p>
<ol>
<li>点击访问<a href="https://www.binance.com/register?ref=LINKJM" target="_blank" rel="noopener noreferrer">币安注册链接</a>，填写自己常用的邮箱或手机号（邀请码：<code>LINKJM</code>）</li>
</ol>
<figure><figcaption>image-20240331212417930</figcaption></figure>
<ol start="2">
<li>填入验证码，点击下一步</li>
</ol>
<figure><figcaption>image-20240331212559456</figcaption></figure>
<ol start="3">
<li>创建自己的秘密，点击下一步</li>
</ol>
<figure><figcaption>image-20240331212739833</figcaption></figure>
<ol start="4">
<li>核对验证码，点击下一步</li>
</ol>
<figure><figcaption>image-20240331213035397</figcaption></figure>
<ol start="5">
<li>进行实名认证</li>
</ol>
<figure><figcaption>image-20240331213133779</figcaption></figure>
<ol start="6">
<li>填入自己身份证一样的真实信息</li>
</ol>
<figure><figcaption>image-20240331213230966</figcaption></figure>
<ol start="7">
<li>选择身份证或护照，推荐身份证</li>
</ol>
<figure><figcaption>image-20240331213300004</figcaption></figure>
<ol start="8">
<li>点击继续</li>
</ol>
<figure><figcaption>image-20240331213343114</figcaption></figure>
<ol start="9">
<li>选择自己方便的验证方式，推荐手机安装币安App</li>
</ol>
<figure><figcaption>image-20240331213432847</figcaption></figure>
<ol start="10">
<li>后续上传自己的身份证正反面，扫脸实名认证后等待审核即可。</li>
</ol>
<p>注册后建议绑定谷歌安全令牌（Google Authenticator），关于Authenticator使用查看下方文章：</p>
<p><a href>Google Authenticator安装使用教程</a></p>
<h2>欧意OKX注册教程</h2>
<p>OKX交易所也已经出海，同样推荐使用Gmail邮箱注册。</p>
<p>1.点击<a href="https://okx.com/join/70998013" target="_blank" rel="noopener noreferrer">OKX优惠注册链接</a>（ 邀请码：70998013）</p>
<figure><figcaption>image-20240331214345096</figcaption></figure>
<ol start="2">
<li>填入验证码，点击下一步</li>
</ol>
<figure><figcaption>image-20240331214406324</figcaption></figure>
<ol start="3">
<li>输入手机号码，点击立即验证</li>
</ol>
<figure><figcaption>image-20240331214633020</figcaption></figure>
<ol start="4">
<li>输入收到的手机验证码</li>
</ol>
<figure><figcaption>image-20240331214818079</figcaption></figure>
<ol start="5">
<li>选择国家，点击下一步</li>
</ol>
<figure><figcaption>image-20240331214907775</figcaption></figure>
<ol start="6">
<li>创建密码，点击下一步，注册成功</li>
</ol>
<figure><figcaption>image-20240331215023749</figcaption></figure>
<ol start="7">
<li>KYC认证，点击右上角头像图标，选择身份认证</li>
</ol>
<figure><figcaption>image-20240331215257023</figcaption></figure>
<ol start="8">
<li>点击个人认证，去认证</li>
</ol>
<figure><figcaption>image-20240331215353171</figcaption></figure>
<ol start="9">
<li>点击立即认证</li>
</ol>
<figure><figcaption>image-20240331215420854</figcaption></figure>
<ol start="10">
<li>证件类型，推荐选择身份证</li>
</ol>
<figure><figcaption>image-20240331215458860</figcaption></figure>
<ol start="11">
<li>下载欧意手机App，完成认证即可</li>
</ol>
<figure><figcaption>image-20240331215621430</figcaption></figure>
<h2>其他交易所</h2>
<p>注册过程和币安、欧意OKX类似，用我上面的邀请链接注册即可，没有邀请链接是没有手续费减免的！</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/cover1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>快速开始</title>
      <link>https://www.linkjm.com/start/</link>
      <guid>https://www.linkjm.com/start/</guid>
      <source url="https://www.linkjm.com/rss.xml">快速开始</source>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
    </item>
    <item>
      <title>VIP内容介绍及加入方式</title>
      <link>https://www.linkjm.com/vip/</link>
      <guid>https://www.linkjm.com/vip/</guid>
      <source url="https://www.linkjm.com/rss.xml">VIP内容介绍及加入方式</source>
      <description>了解 LinkJM VIP 内容、加入方式与双平台邀请码绑定规则。</description>
      <category>VIP</category>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>LinkJM VIP 面向希望继续深入学习 Web3、链上操作和交易实践的读者。这里提供的不只是零散信息，而是经过整理、可以反复查阅和执行的学习资料。</p>
<div class="hint-container warning">
<p class="hint-container-title">风险提示</p>
<p>VIP 内容仅用于学习、研究与经验交流，不构成投资建议，也不承诺收益。加密资产价格波动较大，任何交易决策都应由你独立完成，并优先控制风险。</p>
</div>
<h2>VIP 内容包括哪些？</h2>
<p>加入 VIP 后，可以阅读和使用以下内容：</p>
<ul>
<li><strong>完整进阶文章</strong>：Web3 赚钱路径、链上实操、交易方法与常见问题解析。</li>
<li><strong>交易实践教程</strong>：中心化交易所、现货与合约交易、仓位管理、止损和复盘等实用内容。</li>
<li><strong>空投与链上机会</strong>：项目筛选、参与流程、成本控制、安全检查和风险识别方法。</li>
<li><strong>账号与工具教程</strong>：网络环境、常用账号、Telegram、钱包及辅助工具的配置与使用。</li>
<li><strong>配套学习资料</strong>：文章、视频、思维导图、PDF 文档、操作清单与工具汇总。</li>
<li><strong>持续更新内容</strong>：根据行业变化补充新的平台教程、案例和风险提示。</li>
</ul>
<div class="hint-container tip">
<p class="hint-container-title">适合哪些人？</p>
<p>如果你已经完成本站的<a href="/start/" target="_blank">快速开始</a>和<a href="/crypto/" target="_blank">Web3 入门</a>，希望进一步学习真实操作与交易实践，可以考虑加入 VIP。刚接触加密资产的新手，建议先完成免费基础内容，再决定是否加入。</p>
</div>
<h2>如何加入 VIP？</h2>
<p>目前提供两种加入方式，选择其中一种即可。</p>
<h3>方式一：通过赞赏加入</h3>
<p>你可以通过本站赞赏页面支持 LinkJM，并申请加入 VIP。</p>
<p><a href="/reward/" target="_blank">前往赞赏页面 →</a></p>
<p>完成赞赏后，请联系 LinkJM，并提供能够核对的赞赏记录。具体赞赏标准、VIP 有效期及开通方式，以联系确认时的说明为准。</p>
<h3>方式二：绑定双平台邀请码</h3>
<p>通过 LinkJM 的邀请链接注册并绑定以下两个交易平台，也可以申请加入 VIP：</p>
<p>| 平台 | 邀请码 | 注册入口 |<br>
|</p>
]]></content:encoded>
    </item>
    <item>
      <title>VIP 内容</title>
      <link>https://www.linkjm.com/vip/j.html</link>
      <guid>https://www.linkjm.com/vip/j.html</guid>
      <source url="https://www.linkjm.com/rss.xml">VIP 内容</source>
      <description>LinkJM VIP 进阶学习内容、配套资料与加入方式。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[<p>想了解 VIP 权益与开通条件，请先阅读：</p>
<h2><a href="/vip/join.html" target="_blank">加入 LinkJM VIP</a></h2>
<p>查看 VIP 包含的内容、赞赏加入方式，以及 Binance 和 Bitget 双平台邀请码绑定规则。</p>
<!-- <Catalog /> -->
]]></content:encoded>
    </item>
    <item>
      <title>ChatGPT&amp;Codex订阅教程</title>
      <link>https://www.linkjm.com/tutorial/chatgpt-codex-subscribe.html</link>
      <guid>https://www.linkjm.com/tutorial/chatgpt-codex-subscribe.html</guid>
      <source url="https://www.linkjm.com/rss.xml">ChatGPT&amp;Codex订阅教程</source>
      <description>准备土耳其 Apple ID，通过 SEAGM 充值礼品卡，并在 iPhone 上完成 ChatGPT 与 Codex 相关订阅</description>
      <category>教程</category>
      <pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[
<p>这篇文章记录一种通过 iPhone 端完成 ChatGPT 与 Codex 相关订阅的思路：先准备土耳其区 Apple ID，再通过 SEAGM 购买 500 土耳其里拉的 Apple Gift Card，把余额兑换到账号里，最后在 iPhone 的 ChatGPT App 内选择需要的订阅方案。</p>
<p>需要先说明两点。第一，Apple、OpenAI、SEAGM 的价格、库存、支付方式和订阅入口都可能调整，实际操作以你当前看到的页面为准。第二，账号信息、地区设置、付款行为都应符合平台规则，不建议使用虚假身份信息或来源不明的礼品卡；如果你的主力 Apple ID 已经绑定了家庭共享、云盘、长期订阅和大量购买记录，建议不要随意切区。</p>
<figure><img src="/assets/images/chatgpt-codex-subscribe/checklist.svg" alt="ChatGPT 与 Codex 订阅准备清单" tabindex="0" loading="lazy"><figcaption>ChatGPT 与 Codex 订阅准备清单</figcaption></figure>
<h2>一、准备土耳其 Apple ID</h2>
<p>最稳妥的做法是单独准备一个土耳其区 Apple ID，只用来安装 App、兑换礼品卡和管理订阅。这样即使后面需要切换地区、注销登录或处理余额，也不会影响主账号的 iCloud、相册、通讯录和已有订阅。</p>
<p>注册时可以准备一个单独邮箱，不一定使用主邮箱，但要确保可以正常接收 Apple 的验证码和安全邮件。手机号可以填写中国大陆手机号，国家区号选择 <code>+86</code> 即可，后续登录、验证和找回账号都会用到它。</p>
<p>账单地址可以借助 <a href="https://1ktools.com/zh-cn/tools/developer/turkey-address-generator#google_vignette" target="_blank" rel="noopener noreferrer">1K Tools 土耳其地址生成器</a> 获取。打开页面后点击「生成数据」，复制其中的街道地址、城市、省份和邮编，填入 Apple ID 的账单地址页面即可。这个工具生成的是符合土耳其本地格式的虚拟地址，适合用来解决地址格式校验问题；填写时尽量保持城市、省份、邮编在同一条生成结果里，不要把不同结果混在一起。</p>
<p>地区选择土耳其，姓名、生日等信息按注册页要求填写。Apple 对账号安全比较敏感，新号频繁切换设备、频繁改资料、短时间内大量兑换，都可能触发风控，所以注册完成后不要立刻做太多异常操作。</p>
<p>注册完成后，在 iPhone 上进入「设置」顶部头像，先确认 iCloud 不需要切换。通常只需要切换 App Store 账号：打开 App Store，点击右上角头像，拉到底部退出当前媒体与购买项目账号，再登录土耳其 Apple ID。这样不会影响主 iCloud，只会影响 App Store 下载、更新和订阅支付。</p>
<p>如果 ChatGPT App 在当前地区不可用，可以登录土耳其区 App Store 后搜索下载。已经安装过的用户也可以检查更新，确保使用的是官方 ChatGPT App，不要通过第三方安装包或描述文件安装仿冒应用。</p>
<h2>二、在 SEAGM 购买 500 礼品卡</h2>
<p>SEAGM 是常见的数字点卡平台。进入网站后搜索 Apple Gift Card，注意选择 Turkey / Turkish 区域，不要买成美国、香港、日本或其他地区的礼品卡。Apple 礼品卡通常只支持同区兑换，买错区域基本无法跨区使用。</p>
<p>面额选择 500 TRY。下单前重点核对三件事：商品区域是否为土耳其，面额是否为 500，商品是否有库存并支持你能使用的支付方式。不同时间平台手续费、汇率和到账速度会变化，实际成本以付款页显示为准。支付完成后，订单页一般会展示兑换码，有时需要等待几分钟人工或系统发码。</p>
<figure><img src="/assets/images/chatgpt-codex-subscribe/seagm-card.svg" alt="SEAGM 礼品卡购买与兑换流程" tabindex="0" loading="lazy"><figcaption>SEAGM 礼品卡购买与兑换流程</figcaption></figure>
<p>拿到兑换码后，不要在公开聊天、截图或网盘里保存。建议复制后立刻到 iPhone 的 App Store 兑换：打开 App Store，点击右上角头像，选择「Redeem Gift Card or Code」或「兑换礼品卡或代码」，粘贴兑换码并确认。成功后，账号余额会增加对应土耳其里拉金额。</p>
<p>如果提示无法兑换，先检查当前 App Store 登录的是否为土耳其 Apple ID，再检查兑换码是否有空格、换行或字符识别错误。区域错误、兑换码已使用、账号风控、余额上限等问题都可能导致失败。遇到这类情况不要反复乱试，先回到 SEAGM 订单页确认商品区域和兑换码状态。</p>
<h2>三、在 iPhone 上订阅 ChatGPT / Codex</h2>
<p>余额到账后，打开 ChatGPT App，登录你的 OpenAI 账号。进入账号或设置页面，找到升级入口，选择 ChatGPT Plus、Pro 或页面中展示的其他可用套餐。Codex 的使用权益会随 OpenAI 当时的产品和套餐设计变化，因此不要只看教程里的文字判断，最终以 App 内套餐说明和 OpenAI 账号页面展示为准。</p>
<figure><img src="/assets/images/chatgpt-codex-subscribe/iphone-subscribe.svg" alt="iPhone 上订阅 ChatGPT 与 Codex" tabindex="0" loading="lazy"><figcaption>iPhone 上订阅 ChatGPT 与 Codex</figcaption></figure>
<p>点击订阅后，iOS 会弹出 Apple 的系统购买确认框。这里要认真看价格、周期和付款账号。如果付款方式显示为 Apple Account Balance，说明会优先使用刚刚兑换的余额。确认无误后用 Face ID、Touch ID 或密码完成购买。订阅成功后，回到 ChatGPT App 刷新账号状态，通常几秒到几分钟内就会生效。</p>
<p>如果 App 内没有看到升级入口，可以尝试更新 ChatGPT App、重新登录 OpenAI 账号、切换网络，或在 App Store 的订阅管理里检查当前 Apple ID 是否正常。不要频繁创建新号反复试错，这样更容易触发 Apple 或 OpenAI 的安全检查。</p>
<h2>四、订阅后的管理与续费</h2>
<p>订阅完成后，建议立刻确认续费时间。路径是 App Store 右上角头像，进入「Subscriptions」或「订阅」，找到 ChatGPT。这里可以查看套餐、续费日期、取消订阅入口。即使取消自动续费，通常也可以使用到当前订阅周期结束；如果想持续订阅，则需要保证下次扣款前账号余额足够。</p>
<p>500 TRY 是否够用，取决于当时的 App Store 定价和税费。不要按历史价格做精确预算，购买前先在 ChatGPT App 的付款弹窗里看清楚金额。如果余额不足，可以继续购买同区礼品卡补足。为了降低风控概率，不建议短时间内连续兑换大量礼品卡。</p>
<p>另外，Apple ID 余额一般不能提现，也不一定能转移给其他地区账号。决定充值前最好先确认自己确实需要这个订阅，并且接受余额留在土耳其区账号里的情况。</p>
<h2>五、常见问题</h2>
<p><strong>1. 可以直接用主 Apple ID 切到土耳其区吗？</strong><br>
不太建议。主账号通常绑定了订阅、家庭共享、付款方式、购买记录和 iCloud 生态，切区可能遇到余额未清零、订阅未取消、媒体资料变化等问题。单独账号更容易管理风险。</p>
<p><strong>2. 为什么已经充值成功，ChatGPT 还是订阅失败？</strong><br>
可能是 App Store 账号和 iCloud 账号混淆、余额不足、账号风控、网络异常，或 OpenAI 当前不支持你看到的购买入口。先确认 App Store 登录的是土耳其 Apple ID，再检查订阅弹窗里的付款账号和金额。</p>
<p><strong>3. Codex 一定包含在这个订阅里吗？</strong><br>
不一定。OpenAI 的产品权益会调整，Codex 相关能力可能跟 Plus、Pro、Team、API 或其他方案绑定。订阅前务必看清 App 内说明和 OpenAI 账号页面，不要只按旧教程判断。</p>
<p><strong>4. 礼品卡在哪里买更安全？</strong><br>
优先选择有订单记录、发码记录、售后渠道的平台。不要购买明显低于市场价、来源不明或个人转卖的兑换码。礼品卡一旦被兑换，售后争议通常比较麻烦。</p>
<p>整个流程的核心其实很简单：账号区域要一致，礼品卡区域要一致，订阅时付款账号要一致。只要这三点不出错，大多数问题都能避免。真正需要谨慎的是账号安全和余额管理：不要把主账号拿来频繁切区，不要一次性充值太多，也不要忽略订阅自动续费时间。</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/chatgpt-codex-subscribe/checklist.svg" type="image/svg+xml"/>
    </item>
    <item>
      <title>谷歌发布多平台应用开发神器Project IDX！PaLM 2加持</title>
      <link>https://www.linkjm.com/tutorial/idx-dev.html</link>
      <guid>https://www.linkjm.com/tutorial/idx-dev.html</guid>
      <source url="https://www.linkjm.com/rss.xml">谷歌发布多平台应用开发神器Project IDX！PaLM 2加持</source>
      <description>Project IDX，PaLM 2加持，代码效率翻倍</description>
      <category>AI</category>
      <pubDate>Tue, 15 Aug 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[
<p>8 月 8 日，谷歌宣布推出 AI 代码编辑器 Project IDX，这是一个基于浏览器的开发环境：集成 AI、支持全栈编程语言、跨平台真机预览、一键部署，用于构建全栈网络和多平台应用程序。<br>
<img src="https://s3.bmp.ovh/imgs/2023/08/15/c778fbcb229fc714.png" alt loading="lazy"></p>
<p>一直以来，从 0 开始构建应用，都是一项复杂的工作。尤其是跨越手机、Web 和桌面平台的程序。</p>
<p>这是一片无尽的复杂海洋，需要把技术堆栈融合在一起，来引导、编译、测试、部署、监控应用程序。</p>
<p>多年来，谷歌一直致力于让多平台程序开发流程更快、更顺畅。</p>
<p>经过几个月的成果，团队成功做出了 Project IDX。</p>
<p>谷歌在创建 Project IDX 时并没有构建新的 IDE（集成开发环境），而是使用 VS Code 作为其项目的基础。这让团队能够专注于与 Codey 的集成，Codey 是谷歌基于 PaLM 2 的编程任务基础模型。Project IDX 支持智能代码补全，可以帮助开发者解答一般编码问题，提供与你正在处理的代码有关的特定问题（包括解释能力）的类似 ChatGPT/Bard 的聊天机器人，以及添加如 “添加注释” 等上下文代码操作的能力。<br>
目前，Project IDX 支持 Angular、Flutter、Next.js、React、Svelte 和 Vue 等框架以及 JavaScript 和 Dart 等语言，后续还将支持 Python、Go 和其他语言。</p>
<h2>Project IDX 特性</h2>
<ul>
<li><strong>随时随地快速开始</strong>：你能够在任何地方、任何设备上进行开发，具有本地开发的全部保真度。每个 Project IDX 工作空间都具有基于 Linux 的虚拟机的全部功能，配合云中托管的通用访问权限。</li>
</ul>
<figure><img src="https://s3.bmp.ovh/imgs/2023/08/15/6ce0e43362735ec2.gif" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<ul>
<li><strong>一键导入或从模板创建</strong>：从 GitHub 导入现有项目，让你继续之前的进度。还可以创建新项目，预先包含流行框架的模板，包括 Angular、Flutter、Next.js、React、Svelte、Vue 和如 JavaScript、Dart 以及（即将推出的）Python、Go 等语言。IDX 还在积极努力为更多项目类型和框架添加一流支持。</li>
</ul>
<figure><img src="https://s3.bmp.ovh/imgs/2023/08/15/5fb316d7a0d02033.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<ul>
<li><strong>跨平台预览</strong>：应用成功发布后，需要优化设计和不同平台上的行为（像用户那样预览自己的应用），而 IDX 内置的网络预览（目前只支持 web 预览），和即将推出的完全配置的 Android 模拟器和嵌入式 iOS 模拟器，所有这些都可以直接在浏览器中使用。</li>
</ul>
<figure><img src="https://s3.bmp.ovh/imgs/2023/08/15/f0ee99149149ef4b.gif" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<ul>
<li><strong>AI 助手</strong>：我们花费大量时间编写代码，而人工智能的最新进展创造了巨大的机会，可以让我们的时间变得更加高效。IDX 正在探索 Google 在 AI 领域的创新 —— 包括 为 Android Studio 中的 Studio Bot 提供支持的 Codey 和 PaLM 2 模型、Google Cloud 中的 Duet [4] 等 —— 可以帮助你不仅更快地编写代码，还可以编写更高质量的代码。</li>
</ul>
<figure><img src="https://s3.bmp.ovh/imgs/2023/08/15/c40f15f7d756b746.gif" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<ul>
<li>
<p><strong>一键部署</strong>：在最后，将应用推向生产方面的一个常见痛点是部署它。通过集成 Firebase Hosting，通过几次点击就可以部署你的 Web 应用的可共享预览，或通过快速、安全和全球托管平台部署到生产环境。由于 Firebase Hosting 支持由 Cloud Functions 提供动力的动态后端，所以它非常适合像 Next.js 这样的全栈框架。</p>
</li>
<li>
<p><strong>IDX 申请链接</strong>：<a href="https://idx.dev/" target="_blank" rel="noopener noreferrer">https://idx.dev/</a></p>
</li>
</ul>
<h2>IDX 背后的 AI 模型 Codey</h2>
<p>根据介绍，IDX 由 Codey 提供支持。</p>
<p>在 Google I/O 2023 大会上，谷歌正式发布 Codey。这是一款新型 AI 驱动工具，能够编写并理解代码内容。这款新工具被外界视为谷歌对于 GitHub Copilot 的回应，属于同 Replit 结盟打造的成果。</p>
<p>Codey 基于谷歌的下一代大语言模型 PaLM 2，并采用谷歌自家产品代码及大量合法许可的源代码作为训练素材。更重要的是，Codey 仍在不断学习和发展，从谷歌服务生态系统的各个项目中持续汲取新的力量。</p>
<p>Codey 支持 20 多种编程语言，包括 Go、谷歌标准 SQL、Java、JavaScript、Python 以及 TypeScript。开发者可以通过 Visual Studio Code、JetBrains IDE、Google Shell 编辑器以及 Google Cloud 托管工作站服务的扩展来访问 Codey。开发者能够直接在 IDE 的聊天框中与该模型交流（例如 Android Studio Bot），或者在文本文件中编写注释以指示其生成相关代码。它支持各种编码任务，通过以下方式帮助开发人员更快地工作并缩小技能差距：</p>
<p>代码完成：Codey 根据提示中输入的代码上下文建议接下来的几行。</p>
<p>代码生成：小程根据开发人员的自然语言提示生成代码。</p>
<p>代码聊天：Codey 允许开发人员与机器人对话，以获得调试、文档、学习新概念和其他与代码相关问题的帮助。</p>
<p>Codey 在处理与编码相关的提示词方面接受了专门训练，谷歌还通过其他训练让该模型学会了处理关于 Google Cloud 的一般查询。</p>
<h2>目前在用的辅助AI开发工具</h2>
<h3>- <a href="https://codegeex.cn/zh-CN" target="_blank" rel="noopener noreferrer">CodeGeeX 智能编程助手</a></h3>
<ul>
<li><strong>代码自动生成和补全</strong> CodeGeeX可以根据自然语言注释描述的功能自动生成代码，也可以根据已有的代码自动生成后续代码，补全当前行或生成后续若干行，帮助你提高编程效率。</li>
<li><strong>提供代码翻译能力</strong> 基于AI大模型对代码进行语义级翻译，支持多种编程语言互译</li>
<li><strong>自动添加注释</strong> CodeGeeX可以给代码自动添加行级注释，节省大量开发时间。没有注释的历史代码，也不再是问题。</li>
</ul>
<p>基于<a href="https://github.com/THUDM/CodeGeeX2/" target="_blank" rel="noopener noreferrer">CodeGeeX2</a>: 更强大的多语言代码生成模型<br>
是多语言代码生成模型 CodeGeeX (KDD’23) 的第二代模型。不同于一代 CodeGeeX（完全在国产华为昇腾芯片平台训练） ，CodeGeeX2 是基于 ChatGLM2 架构加入代码预训练实现，得益于 ChatGLM2 的更优性能，CodeGeeX2 在多项指标上取得性能提升（+107% &gt; CodeGeeX；仅 60 亿参数即超过 150 亿参数的 StarCoder-15B 近 10%），更多特性包括：</p>
<ul>
<li><strong>更强大的代码能力</strong>：基于 ChatGLM2-6B 基座语言模型，CodeGeeX2-6B 进一步经过了 600B 代码数据预训练，相比一代模型，在代码能力上全面提升，HumanEval-X 评测集的六种编程语言均大幅提升 (Python +57%, C++ +71%, Java +54%, JavaScript +83%, Go +56%, Rust +321%)，在 Python 上达到 35.9% 的 Pass@1 一次通过率，超越规模更大的 StarCoder-15B。</li>
<li><strong>更优秀的模型特性</strong>：继承 ChatGLM2-6B 模型特性，CodeGeeX2-6B 更好支持中英文输入，支持最大 8192 序列长度，推理速度较一代 CodeGeeX-13B 大幅提升，量化后仅需 6GB 显存即可运行，支持轻量级本地化部署。<br>
更全面的 AI 编程助手：CodeGeeX 插件（VS Code, Jetbrains）后端升级，支持超过 100 种编程语言，新增上下文补全、跨文件补全等实用功能。结合 Ask CodeGeeX 交互式 AI 编程助手，支持中英文对话解决各种编程问题，包括且不限于代码解释、代码翻译、代码纠错、文档生成等，帮助程序员更高效开发。</li>
<li><strong>更开放的协议</strong>：CodeGeeX2-6B 权重对学术研究完全开放，填写登记表申请商业使用。</li>
</ul>
<h3>- <a href="https://aws.amazon.com/cn/codewhisperer/" target="_blank" rel="noopener noreferrer">AWS CodeWhisperer</a></h3>
<p>4月18日，亚马逊云科技宣布，实时AI编程助手Amazon CodeWhisperer正式可用，同时推出的还有供所有开发人员免费使用的个人版（CodeWhisperer Individual）。CodeWhisperer帮助开发者基于注释生成代码，追踪开源参考，扫描查找漏洞。此外，还可以帮助开发者创建代码胜任如下场景，比如常规、耗时的无差别任务，或是在使用不熟悉的API或SDK时构建示例代码，亦或要正确高效地使用亚马逊云科技API操作，还有其他场景比如编写读写文件、处理图像、编写单元测试等代码。</p>
<p>Amazon CodeWhisperer 为多种编程语言提供基于人工智能（AI）的代码建议，包括 Python、Java、JavaScript、TypeScript、C#、Go、Rust、PHP、Ruby、Kotlin、C、C++、Shell 脚本、SQL 和 Scala。您可以使用来自多个 IDE 的服务，包括 JetBrains IDE（IntelliJ IDEA、PyCharm、WebStorm 和 Rider）、Visual Studio（VS）Code、AWS Cloud9、AWS Lambda 控制台、JupyterLab 和 Amazon SageMaker Studio。</p>
<h3>- <a href="https://poe.com/" target="_blank" rel="noopener noreferrer">Poe</a></h3>
<p>国内可注册，使用方便，免费，有针对代码和程序专门优化的gpt-3.5-turbo模型 <strong>Assistant</strong><br>
当然还有其他的模型，包含一些限额和付费的，你也可以创建自己的bot</p>
<ul>
<li>Claude-2-100k</li>
<li>Claude-instant-100k</li>
<li>gpt-4-32k（Powered by gpt-4-32k. Since this is a beta model, the usage limit is subject to change.）</li>
<li>Google-PaLM（Powered by Google's PaLM 2 chat-bison-001 model）</li>
</ul>
<p><em>CodeGeeX2</em>和<em>AWS CodeWhisperer</em>都提供了VSCode的插件可以使用，体验下来，<em>CodeGeeX2</em>支持的文件类型更多一点，而且反应也还算迅速，<em>AWS CodeWhisperer</em>只支持了主流开发语言，对于如dart、Vue、markdown、CSS等非主流编程语言支持效果很差，目前两者都免费，体验下来<strong>CodeGeeX2</strong>更不错一点，根据注释生成代码，给现有代码生成注释，代码翻译，纠错等功能也比较实用</p>
<h2>Some thoughts🤔</h2>
<p>看来云上开发将来一定是主流趋势，程序员再也不用因为换设备，换环境而苦恼，集成式一体开发环境，开箱即用，开发随时可中断，可恢复，不受限于设备，环境，地点影响<br>
而且集成AI代码助手，各大厂家都在发力了，微软的<strong>GitHub Copilot</strong>,亚马逊的<strong>CodeWhisperer</strong>，还有即将到来的Google的<strong>Project IDX</strong> 方便快捷的开发，将大量繁琐重复的工作交给AI来执行</p>
<h2>That's It</h2>
<blockquote>
<p>参考资料：</p>
<ul>
<li>OSC 开源社区（ID：oschina2013)</li>
<li><a href="https://developers.googleblog.com/2023/08/introducing-project-idx-experiment-to-improve-full-stack-multiplatform-app-development.html" target="_blank" rel="noopener noreferrer">https://developers.googleblog.com/2023/08/introducing-project-idx-experiment-to-improve-full-stack-multiplatform-app-development.html</a></li>
<li><a href="https://codeandhack.com/google-codey/" target="_blank" rel="noopener noreferrer">https://codeandhack.com/google-codey/</a></li>
</ul>
</blockquote>
]]></content:encoded>
      <enclosure url="https://s3.bmp.ovh/imgs/2023/08/15/c778fbcb229fc714.png" type="image/png"/>
    </item>
    <item>
      <title>OpenSpec 技术分享：用规格驱动 AI 编程</title>
      <link>https://www.linkjm.com/tutorial/openspec.html</link>
      <guid>https://www.linkjm.com/tutorial/openspec.html</guid>
      <source url="https://www.linkjm.com/rss.xml">OpenSpec 技术分享：用规格驱动 AI 编程</source>
      <description>正在使用 Cursor、Claude Code、Codex、GitHub Copilot、Gemini CLI、Windsurf 等 AI 编程工具的研发团队</description>
      <category>教程</category>
      <category>AI</category>
      <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[
<blockquote>
<p>面向：正在使用 Cursor、Claude Code、Codex、GitHub Copilot、Gemini CLI、Windsurf 等 AI 编程工具的研发团队。</p>
<p>目标：理解 OpenSpec 解决什么问题、如何在 IDE 和 CLI 中落地、典型工作流、注意事项和示例。</p>
</blockquote>
]]></content:encoded>
      <enclosure url="https://s41.ax1x.com/2026/06/29/pma61AK.png" type="image/png"/>
    </item>
    <item>
      <title>Web3 常用名词与术语</title>
      <link>https://www.linkjm.com/crypto/chapter001/000.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/000.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Web3 常用名词与术语</source>
      <description>按基础设施、账户资产、应用与交易场景整理 Web3 入门常用术语。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>Web3 的名词很多，但大多数都可以放进一张简单的地图：区块链提供基础设施，钱包连接用户与网络，智能合约构成应用，代币则用于表达资产或权益。</p>
<p>这份术语表用于建立初步认识。后续章节会进一步解释其中的重要概念。</p>
<h2>网络与基础设施</h2>
<h3>区块链（Blockchain）</h3>
<p>一种由多个参与者共同维护的数据记录系统。交易会被整理进区块，并按照规则连接起来。区块链并非绝对“不可修改”，而是通过密码学、共识机制和经济成本，让已经确认的记录难以被单方面更改。</p>
<h3>节点（Node）</h3>
<p>运行区块链软件并参与网络通信的计算机。不同节点可能负责保存数据、验证交易、传播信息或生成区块。</p>
<h3>共识机制（Consensus Mechanism）</h3>
<p>网络参与者对交易顺序和账本状态达成一致的方法。常见类型包括工作量证明和权益证明，不同机制在安全、性能与资源消耗之间有不同取舍。</p>
<h3>公链（Public Blockchain）</h3>
<p>任何人通常都可以读取数据、发送交易或参与验证的开放区块链网络。比特币和以太坊是典型公链。</p>
<h3>Layer 1（L1）</h3>
<p>区块链的基础网络，负责共识、结算和数据记录，例如比特币、以太坊和 Solana。</p>
<h3>Layer 2（L2）</h3>
<p>建立在基础链之上的扩容系统。它在链外或独立环境中处理部分交易，再把结果提交给基础链，以降低成本或提高处理能力。</p>
<h3>智能合约（Smart Contract）</h3>
<p>部署在区块链上的程序。满足预先设定的条件时，网络会按照代码执行操作。智能合约可以公开验证，但代码公开并不代表它没有漏洞。</p>
<h3>Gas</h3>
<p>执行链上操作所消耗计算和存储资源的计量方式。用户支付的网络费用通常由 Gas 用量和当时的费率共同决定。</p>
<h3>预言机（Oracle）</h3>
<p>向智能合约提供链外数据的机制，例如价格、天气或比赛结果。预言机提供的数据是否可靠，会直接影响依赖它的应用。</p>
<h3>跨链桥（Bridge）</h3>
<p>用于在不同区块链之间传递资产或信息的协议。跨链桥需要解决多条网络之间的验证问题，也是历史上安全事件较集中的基础设施之一。</p>
<h2>账户、钱包与身份</h2>
<h3>地址（Address）</h3>
<p>区块链账户的公开标识，可用于接收资产或查询公开记录。公开地址通常可以分享，但它不等于私钥。</p>
<h3>私钥（Private Key）</h3>
<p>控制链上账户并签署交易的秘密数据。拥有私钥通常意味着拥有对应账户的控制权；私钥一旦泄露，资产可能无法追回。</p>
<h3>助记词（Seed Phrase）</h3>
<p>用于恢复钱包密钥的一组单词。它本质上是账户控制权的备份，不应截图、上传网盘或发送给任何人。</p>
<h3>钱包（Wallet）</h3>
<p>管理密钥、生成地址并与区块链交互的工具。钱包通常不直接“存放”资产；资产记录在链上，钱包保存的是访问和操作这些资产所需的凭证。</p>
<h3>签名（Signature）</h3>
<p>用户使用私钥对操作进行授权的密码学证明。签名交易会改变链上状态；某些链下签名也可能授予重要权限，不能因为“不花 Gas”就放松警惕。</p>
<h3>DID（Decentralized Identifier）</h3>
<p>去中心化标识符。它尝试让个人或组织在不依赖单一身份提供商的情况下创建和控制数字身份标识，但具体实现和采用程度因项目而异。</p>
<h2>资产与代币</h2>
<h3>比特币（Bitcoin / BTC）</h3>
<p>比特币既指一套点对点价值转移网络，也指该网络中的原生资产 BTC。其发行上限和发行节奏由协议规则设定。</p>
<h3>以太坊（Ethereum）与 ETH</h3>
<p>以太坊是支持智能合约的区块链网络，ETH 是其原生资产，用于支付网络费用、参与网络安全以及在生态应用中作为资产使用。</p>
<h3>代币（Token）</h3>
<p>由区块链或智能合约记录的数字单位，可以表达支付资产、使用权、治理权或其他权益。代币名称相似，不代表权利、价值或风险相同。</p>
<h3>同质化代币（Fungible Token）</h3>
<p>同一合约中的每个单位可以相互替代，例如一个单位通常与另一个单位具有相同属性。以太坊上的 ERC-20 是常见标准之一。</p>
<h3>NFT（Non-Fungible Token）</h3>
<p>非同质化代币。每个代币可以拥有不同的编号或元数据，常用于表示收藏品、凭证、会员资格或游戏资产。NFT 证明的是链上记录关系，不自动等同于作品版权。</p>
<h3>稳定币（Stablecoin）</h3>
<p>目标是相对某种参考资产保持价格稳定的代币，常见参考对象是法定货币。不同稳定币可能依赖现金储备、加密资产抵押或算法机制，因此风险结构并不相同。</p>
<h3>BRC-20</h3>
<p>一种利用比特币 Ordinals 相关机制记录同质化代币信息的实验性标准。它与以太坊 ERC-20 的实现方式不同，也不具备完全相同的智能合约能力。</p>
<h2>应用与组织</h2>
<h3>dApp（Decentralized Application）</h3>
<p>与区块链或智能合约交互的应用。前端网站仍可能由中心化服务器提供，因此“使用区块链”不代表整个产品完全去中心化。</p>
<h3>DeFi（Decentralized Finance）</h3>
<p>建立在公开区块链和智能合约上的金融应用集合，包括交易、借贷、衍生品和资产管理等。DeFi 减少了部分人工中介，但增加了合约、预言机、流动性和治理风险。</p>
<h3>DEX（Decentralized Exchange）</h3>
<p>通过智能合约完成资产交换的去中心化交易协议。用户通常直接使用钱包交互，但仍需承担滑点、恶意代币和合约安全风险。</p>
<h3>DAO（Decentralized Autonomous Organization）</h3>
<p>通过公开规则、智能合约和社区协作进行治理的组织形式。DAO 的去中心化程度差异很大，需要观察投票权、资金控制权和实际决策过程。</p>
<h3>GameFi</h3>
<p>将游戏机制与可交易数字资产、代币激励或链上所有权结合的应用类型。游戏体验和资产经济模型需要分别评估。</p>
<h2>交易与市场</h2>
<h3>中心化交易所（CEX）</h3>
<p>由公司运营并代用户保管资产、撮合交易的平台。使用方便，但用户需要承担平台运营、托管、合规和账户限制等风险。</p>
<h3>流动性（Liquidity）</h3>
<p>在不显著影响价格的情况下完成买卖的能力。流动性不足时，即使页面显示某个价格，也可能无法按该价格成交。</p>
<h3>滑点（Slippage）</h3>
<p>预期成交价格与实际成交价格之间的差异。交易规模、市场波动、流动性和交易确认速度都会影响滑点。</p>
<h3>TVL（Total Value Locked）</h3>
<p>锁定总价值，用于描述某个协议或网络中存入资产的估算价值。TVL 可以辅助观察规模，但不能单独证明协议安全或项目具有投资价值。</p>
<h3>ICO（Initial Coin Offering）</h3>
<p>首次代币发行，一种通过出售代币筹集资金的方式。代币发行不等同于股票上市，购买者获得的权利也可能非常有限。相关活动受到不同司法辖区的监管约束，并伴随较高风险。</p>
<h2>常见中文俗称</h2>
<p>| 俗称 | 通常指代 |<br>
|</p>
]]></content:encoded>
    </item>
    <item>
      <title>一、Web3 是什么：从互联网演进到用户主权</title>
      <link>https://www.linkjm.com/crypto/chapter001/001.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/001.html</guid>
      <source url="https://www.linkjm.com/rss.xml">一、Web3 是什么：从互联网演进到用户主权</source>
      <description>从互联网的演进、数据与资产控制权出发，理解 Web3 的核心组成、价值主张与现实边界。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>Web3 不是一个边界清晰的产品名称，而是对下一代互联网形态的一种概括。它试图利用区块链、密码学和开放协议，让用户能够直接持有数字资产、管理账户，并在不同应用之间迁移部分身份和数据。</p>
<p>理解 Web3，不应从“某个代币会不会上涨”开始，而应先看互联网中的权利如何分配：谁保存数据、谁制定规则、谁控制账户，以及用户能否带走自己的资产和关系。</p>
<h2>从 Web1、Web2 到 Web3</h2>
<p>人们常用 Web1、Web2 和 Web3 描述互联网不同阶段的主要特征。这种划分是一种理解框架，并不是严格的技术版本号。</p>
<p>| 阶段 | 用户的主要行为 | 内容和账户的控制方式 | 典型特征 |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/web-evolution.png" type="image/png"/>
    </item>
    <item>
      <title>三、Web3 技术框架：区块链、公链与扩容</title>
      <link>https://www.linkjm.com/crypto/chapter001/002.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/002.html</guid>
      <source url="https://www.linkjm.com/rss.xml">三、Web3 技术框架：区块链、公链与扩容</source>
      <description>从交易确认、智能合约、公链架构和 Layer 2 扩容出发，理解 Web3 系统各层如何协同工作。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>前两节介绍了 Web3 的价值主张，以及比特币和以太坊的定位。这一节把视角移到系统内部：一笔交易如何被网络确认，智能合约在哪里运行，不同公链为什么采用不同设计，以及 Layer 2 如何缓解基础链的性能压力。</p>
<p>学习这些内容的目的不是背诵技术名词，而是建立一套分析框架。当你遇到一条新链或新协议时，能够判断它负责哪一层、依赖谁提供安全性，以及为了提升性能作出了哪些取舍。</p>
<h2>Web3 系统的基本分层</h2>
<p>Web3 应用通常不是由单一程序完成，而是由多个层次共同组成：</p>
<figure><img src="/assets/images/web3/chapter001/web3-stack.png" alt="Web3 技术栈的五个层次" tabindex="0" loading="lazy"><figcaption>Web3 技术栈的五个层次</figcaption></figure>
<p>| 层次 | 主要职责 | 常见风险 |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/web3-stack.png" type="image/png"/>
    </item>
    <item>
      <title>四、数字身份与钱包：地址、私钥和账户安全</title>
      <link>https://www.linkjm.com/crypto/chapter001/003.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/003.html</guid>
      <source url="https://www.linkjm.com/rss.xml">四、数字身份与钱包：地址、私钥和账户安全</source>
      <description>从地址、私钥和助记词开始理解 Web3 钱包，分清托管与自托管、登录与签名，并建立一套适合新手的账户安全方法。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>如果说区块链是一套公开运行的账本，那么钱包就是普通用户进入这套系统的主要入口。转账、兑换代币、铸造 NFT、参与治理，几乎都要通过钱包完成。</p>
<p>不过，“钱包”这个名字很容易造成误解：<strong>链上资产并不存放在钱包软件里，而是记录在区块链上。</strong> 钱包真正保管的是能够控制账户的密钥，并帮助用户创建地址、查询资产和完成签名。</p>
<p>理解这一点，是建立 Web3 安全意识的第一步。</p>
<h2>1. 地址、密钥与资产是什么关系</h2>
<p>一个典型的区块链账户，可以先用下面这组关系来理解：</p>
<ul>
<li><strong>私钥</strong>：账户控制权的根源，用来生成数字签名。</li>
<li><strong>公钥</strong>：由私钥推导而来，可以公开，用于验证签名。</li>
<li><strong>地址</strong>：通常由公钥进一步计算得到，是对外收款和识别账户的标识。</li>
<li><strong>链上资产</strong>：记录在区块链状态中，由网络根据规则确认归属。</li>
</ul>
<figure><img src="/assets/images/web3/chapter001/wallet-keys-assets.png" alt="钱包、地址与链上资产的关系" tabindex="0" loading="lazy"><figcaption>钱包、地址与链上资产的关系</figcaption></figure>
<p>地址可以类比为公开的收款编号，但私钥不能简单等同于银行卡密码。银行卡密码泄露后，银行可能协助冻结账户或重置凭证；私钥一旦泄露，攻击者通常可以直接签署交易，而区块链上并不存在一个必然能够撤销交易的中心机构。</p>
<p>因此需要记住一条最重要的规则：</p>
<blockquote>
<p>地址可以公开，私钥和助记词永远不能公开。</p>
</blockquote>
<h2>2. 钱包到底做了什么</h2>
<p>钱包通常承担以下几项工作：</p>
<ol>
<li><strong>创建或导入账户</strong>：生成密钥，或者通过助记词恢复已有账户。</li>
<li><strong>管理多个地址</strong>：同一组助记词可以按照确定的规则派生出多个地址。</li>
<li><strong>读取链上数据</strong>：展示余额、代币、NFT 和交易记录。</li>
<li><strong>构造交易</strong>：把收款地址、金额、网络费用等信息整理成网络可识别的数据。</li>
<li><strong>请求并完成签名</strong>：用户确认后，用私钥对交易或消息进行签名。</li>
<li><strong>广播交易</strong>：把签名后的交易发送给区块链网络。</li>
</ol>
<p>钱包界面里显示的资产，本质上是它读取链上数据后形成的视图。即使卸载钱包应用，只要私钥或助记词仍然安全，通常仍可在兼容钱包中恢复账户；反过来，如果密钥丢失，仅保留钱包应用也无法重新取得控制权。</p>
<h2>3. 私钥、助记词和钱包密码</h2>
<p>这三个概念经常被混在一起，但它们的权限完全不同。</p>
<h3>私钥</h3>
<p>私钥是一段具有极大随机性的数字。谁能使用私钥签名，谁就能控制对应账户。不要截图、不要通过聊天工具传输，也不要粘贴到来历不明的网站。</p>
<h3>助记词</h3>
<p>助记词通常由 12 个或 24 个单词组成，它可以按照既定标准派生出一组私钥和地址。它不是普通的“找回密码”，而更接近整个钱包的主钥匙。</p>
<p>一组助记词可能控制多个网络、多个地址和多类资产。泄露助记词的后果，往往比泄露单个账户的私钥更严重。</p>
<h3>钱包密码</h3>
<p>钱包密码一般用于解锁当前设备中的钱包应用，或加密本地密钥文件。它不一定能够在另一台设备上恢复链上账户，也不能替代助记词备份。</p>
<p>| 凭证 | 主要用途 | 丢失后的影响 | 是否可以告诉他人 |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/wallet-keys-assets.png" type="image/png"/>
    </item>
    <item>
      <title>七、DeFi 与稳定币：开放金融的机制与风险</title>
      <link>https://www.linkjm.com/crypto/chapter001/004.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/004.html</guid>
      <source url="https://www.linkjm.com/rss.xml">七、DeFi 与稳定币：开放金融的机制与风险</source>
      <description>从稳定币、去中心化交易所、流动性池和借贷协议入门 DeFi，理解开放金融如何运作，以及智能合约、脱锚、清算等主要风险。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>在传统金融中，转账、兑换、借贷和资产管理通常由银行、券商或支付机构提供。DeFi 尝试把其中一部分规则写入智能合约，让用户通过钱包直接使用链上的金融服务。</p>
<p>DeFi 是 <strong>Decentralized Finance</strong> 的缩写，中文通常译为“去中心化金融”。它并不意味着所有环节都完全去中心化，也不代表产品天然安全。更准确的理解是：<strong>资产由区块链记录，业务规则由智能合约执行，用户可以在满足协议条件时直接参与。</strong></p>
<p>本篇只讨论运行机制和风险，不构成任何投资建议。</p>
<h2>1. DeFi 是一组可以组合的金融积木</h2>
<p>DeFi 不是某个单独的应用，而是由钱包、稳定币、交易协议、借贷协议、预言机和区块链等组件共同构成的生态。</p>
<figure><img src="/assets/images/web3/chapter001/defi-building-blocks.png" alt="DeFi 的主要组成模块" tabindex="0" loading="lazy"><figcaption>DeFi 的主要组成模块</figcaption></figure>
<p>一次看似简单的链上兑换，背后可能经过以下环节：</p>
<ol>
<li>用户连接钱包并发起交易。</li>
<li>钱包展示合约调用内容并请求签名。</li>
<li>交易协议根据流动性和价格规则计算结果。</li>
<li>智能合约转移两种资产并更新状态。</li>
<li>区块链验证、排序并最终确认交易。</li>
</ol>
<p>不同协议之间通常可以互相调用，这种特性被称为<strong>可组合性</strong>。它能加快产品创新，也会传导风险：底层稳定币、预言机或协议出现问题，上层应用可能同时受到影响。</p>
<h2>2. 稳定币为什么是 DeFi 的基础工具</h2>
<p>比特币、以太币等加密资产的市场价格会持续变化。如果每一次支付、借贷和结算都使用高波动资产，用户很难判断自己实际承担了多少债务或购买力变化。</p>
<p>稳定币试图锚定某种参考资产，最常见的是美元。它为链上世界提供了较稳定的计价和结算单位，常见用途包括：</p>
<ul>
<li>在不同加密资产之间提供交易媒介。</li>
<li>作为借贷协议中的存款、借款或抵押资产。</li>
<li>用于跨境转账和链上支付。</li>
<li>在市场波动期间暂时承担结算工具的角色。</li>
</ul>
<p>需要特别注意：<strong>“稳定”是设计目标，不是兑付保证。</strong> 稳定币仍可能偏离目标价格，也就是常说的“脱锚”。</p>
<h2>3. 三类常见的稳定机制</h2>
<p>稳定币的关键问题不是名称，而是：谁发行、用什么支撑、如何赎回，以及系统失效时损失由谁承担。</p>
<figure><img src="/assets/images/web3/chapter001/stablecoin-mechanisms.png" alt="三类稳定币机制与主要风险" tabindex="0" loading="lazy"><figcaption>三类稳定币机制与主要风险</figcaption></figure>
<h3>法币储备型</h3>
<p>发行方接收法币或其他低波动资产，并发行对应数量的链上代币。用户理论上可以按照规则把稳定币交还给发行方，换回其锚定的法币。</p>
<p>这类稳定币的核心依赖包括：</p>
<ul>
<li>储备资产是否真实、充足并且流动性良好。</li>
<li>托管银行和发行机构是否稳健。</li>
<li>审计或鉴证信息是否及时、可信。</li>
<li>用户是否具备直接赎回资格。</li>
<li>发行方是否有权冻结特定地址。</li>
</ul>
<p>它在价格稳定性上往往较直观，但信任并没有消失，而是集中到了发行、托管和监管环节。</p>
<h4>USDT 与 USDC</h4>
<p>USDT 和 USDC 都属于中心化发行的美元稳定币。两者都试图让一枚代币维持接近一美元的价值，但它们不是银行账户中的美元，也不是由区块链自动保证价格永远等于一美元。</p>
<p>| 项目 | USDT | USDC |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/defi-building-blocks.png" type="image/png"/>
    </item>
    <item>
      <title>八、NFT：数字所有权、应用场景与常见风险</title>
      <link>https://www.linkjm.com/crypto/chapter001/005.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/005.html</guid>
      <source url="https://www.linkjm.com/rss.xml">八、NFT：数字所有权、应用场景与常见风险</source>
      <description>从代币标准、元数据和媒体存储理解 NFT，分清持有权、版权与使用许可，并掌握铸造、交易和安全核验的基本方法。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>提到 NFT，很多人首先想到头像、数字艺术品和高价交易。但这些只是 NFT 曾经最受关注的表现形式，并不是它的完整定义。</p>
<p>NFT 的核心能力，是在区块链上创建一个可以识别、持有和转移的独特记录。它可以关联图片、游戏道具、门票或会员权益，也可以只是一个不具备额外用途的链上收藏品。</p>
<p>理解 NFT，首先要把“链上的代币”“代币指向的内容”和“持有人获得的权利”分开。</p>
<h2>1. NFT 到底是什么</h2>
<p>NFT 是 <strong>Non-Fungible Token</strong> 的缩写，通常译为“非同质化代币”。</p>
<p>“非同质化”表示每个代币可以具有独立编号和属性，不能默认与另一个代币一比一互换。例如，一张特定座位的门票和另一张门票虽然属于同一场活动，但座位、权益和价格可能不同。</p>
<p>与之相对，同一种普通代币的各个单位通常可以相互替代。你持有的一枚代币，与他人持有的同种一枚代币，在协议层面没有编号和属性差异。</p>
<p>NFT 通常包含以下链上信息：</p>
<ul>
<li>所属智能合约的地址。</li>
<li>在合约中的 Token ID。</li>
<li>当前持有地址。</li>
<li>指向元数据的链接或编码内容。</li>
<li>转移、授权和销毁等操作记录。</li>
</ul>
<p>因此，一个 NFT 的准确身份通常不是它的名称或图片，而是：</p>
<blockquote>
<p>区块链网络 + 合约地址 + Token ID</p>
</blockquote>
<p>不同合约完全可以创建名称和图片相同的 NFT。只看图片，无法判断它是不是某个项目的官方资产。</p>
<h2>2. NFT、元数据和图片是三层结构</h2>
<p>人们常说“把一张图片铸造成 NFT”，但多数情况下，图片本身并没有完整写入区块链。更常见的结构分为三层：</p>
<figure><img src="/assets/images/web3/chapter001/nft-technical-layers.png" alt="NFT 的链上记录、元数据与媒体文件" tabindex="0" loading="lazy"><figcaption>NFT 的链上记录、元数据与媒体文件</figcaption></figure>
<h3>第一层：链上代币</h3>
<p>智能合约记录 Token ID、持有地址、授权关系等信息。这是 NFT 能够在钱包间转移并被应用识别的基础。</p>
<h3>第二层：元数据</h3>
<p>元数据描述 NFT 的名称、简介、属性和媒体文件位置，常见格式是 JSON。合约可能直接保存元数据，也可能只保存一个指向元数据的 URI。</p>
<h3>第三层：媒体文件</h3>
<p>图片、视频、音频或三维模型通常体积较大，可能存放在去中心化存储网络、项目服务器或其他内容分发系统中。</p>
<p>三层结构带来一个重要结论：<strong>链上代币仍然存在，不代表它指向的内容永远可访问。</strong> 如果元数据依赖的服务器停止运行，或者合约允许管理员修改链接，钱包里看到的名称和图片可能发生变化。</p>
<p>核验 NFT 时，不仅要看代币在不在链上，还要查看元数据存在哪里、能否修改、由谁维护。</p>
<h2>3. 常见的 NFT 代币标准</h2>
<p>不同区块链有不同的 NFT 标准。以 EVM 生态为例，常见标准包括 ERC-721 和 ERC-1155。</p>
<h3>ERC-721</h3>
<p>ERC-721 为每个 NFT 提供独立的 Token ID，适合一件件区分的收藏品、证书、域名和独特游戏道具。</p>
<h3>ERC-1155</h3>
<p>ERC-1155 允许一个合约同时管理多类资产，同一个 Token ID 也可以发行多个数量。它适合游戏道具、票券和同时包含同质化与非同质化资产的场景。</p>
<p>| 对比项 | ERC-721 | ERC-1155 |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/nft-technical-layers.png" type="image/png"/>
    </item>
    <item>
      <title>九、GameFi：链上游戏、资产经济与可持续性</title>
      <link>https://www.linkjm.com/crypto/chapter001/006.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/006.html</guid>
      <source url="https://www.linkjm.com/rss.xml">九、GameFi：链上游戏、资产经济与可持续性</source>
      <description>从游戏架构、NFT 资产和代币经济理解 GameFi，并通过 Axie Infinity、STEPN、Sorare 与全链上游戏案例分析链游的价值和风险。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>GameFi 通常由 Game 和 Finance 两个词组合而来，用于描述引入区块链资产、代币经济或开放市场的游戏与游戏化应用。</p>
<p>早期 GameFi 常用“边玩边赚”吸引用户，但如果只把它理解成一种赚钱工具，就会忽略最重要的问题：奖励从哪里来，玩家为什么愿意长期留下，以及资产离开游戏后还有没有用途。</p>
<p>一个健康的链上游戏，首先应当是一款有人愿意玩的产品，其次才是资产和经济系统。</p>
<h2>1. 什么是区块链游戏</h2>
<p>区块链游戏会把一部分游戏数据或规则放到区块链上，例如：</p>
<ul>
<li>用 NFT 表示角色、卡牌、土地或装备。</li>
<li>用同质化代币作为游戏资源或治理工具。</li>
<li>通过智能合约完成铸造、交易、合成和奖励分配。</li>
<li>允许玩家使用钱包登录并自行保管部分资产。</li>
<li>把关键规则和游戏状态公开在链上。</li>
</ul>
<p>但这不代表所有操作都必须上链。角色移动、画面渲染、实时战斗和反作弊等高频行为，通常仍由客户端和服务器处理。</p>
<figure><img src="/assets/images/web3/chapter001/gamefi-architecture.png" alt="区块链游戏的分层架构" tabindex="0" loading="lazy"><figcaption>区块链游戏的分层架构</figcaption></figure>
<p>把所有动作写入区块链会带来等待确认、手续费和吞吐量问题。常见做法是让游戏体验留在链下，只把资产转移、关键结果或需要公开验证的规则放到链上。</p>
<h2>2. 链上资产真的完全属于玩家吗</h2>
<p>如果游戏装备以 NFT 形式存放在玩家钱包中，开发者通常不能像修改中心化数据库那样直接删除钱包里的代币。玩家还可能把它转移到兼容市场或另一个地址。</p>
<p>不过，“代币仍在钱包中”和“资产仍然有用”是两回事。</p>
<p>假设你拥有一把链上宝剑：</p>
<ol>
<li>NFT 合约仍然存在，因此钱包还能证明你持有这枚代币。</li>
<li>宝剑图片可能存放在项目服务器；服务器关闭后，图片可能无法加载。</li>
<li>攻击力和战斗效果由游戏服务器解释；游戏停止运行后，这些能力可能失去意义。</li>
<li>另一款游戏不会自动认识这把宝剑，除非新开发者主动读取并适配它。</li>
</ol>
<p>因此，链上所有权主要保证的是<strong>代币的持有与转移记录</strong>，不能保证游戏持续运营、资产价格稳定或跨游戏通用。</p>
<h2>3. GameFi 的三类资产</h2>
<h3>游戏 NFT</h3>
<p>角色、装备、卡牌和土地经常被设计成 NFT。它们可以具有等级、稀有度和技能等属性，也可能被用于合成或繁殖。</p>
<p>检查游戏 NFT 时，需要判断哪些属性写在链上、哪些由服务器保存，以及开发者能否修改元数据和游戏效果。</p>
<h3>功能代币</h3>
<p>功能代币通常用于升级、修理、合成、繁殖、门票或其他日常消耗。玩家可能通过游戏行为获得它，再在游戏内消耗。</p>
<p>如果代币不断产出，却缺少有意义的消耗场景，供给会持续增加，卖出压力也会逐渐积累。</p>
<h3>治理代币</h3>
<p>治理代币可能用于投票、质押或分享部分生态权益。它与功能代币可以是同一种资产，也可以采用双代币结构。</p>
<p>“可以投票”不等于玩家能够决定所有规则。需要查看团队、投资者和社区的代币分布，以及投票结果是否真正约束开发者。</p>
<h2>4. 游戏经济如何形成循环</h2>
<p>GameFi 经济系统通常同时存在发行端和消耗端。</p>
<p><strong>发行端</strong>可能包括战斗奖励、任务奖励、排行榜、质押和活动空投；<strong>消耗端</strong>可能包括升级、修理、合成、繁殖、报名和装饰品。</p>
<figure><img src="/assets/images/web3/chapter001/gamefi-economy-loops.png" alt="可持续与失衡的游戏经济循环" tabindex="0" loading="lazy"><figcaption>可持续与失衡的游戏经济循环</figcaption></figure>
<h3>一个简单的铁匠铺案例</h3>
<p>假设游戏每天向玩家奖励 100 万枚矿石代币：</p>
<ul>
<li>玩家打造武器每天只消耗 20 万枚。</li>
<li>剩余代币没有其他用途，大量玩家选择卖出。</li>
<li>代币价格下跌后，“挖矿奖励”的吸引力下降。</li>
<li>新玩家减少，武器需求继续下降。</li>
<li>为吸引用户而提高奖励，又会加快代币增发。</li>
</ul>
<p>问题并不是奖励存在，而是产出长期高于真实消耗。</p>
<p>如果开发者增加装备维修、赛季报名和高价值合成，确实可以提高消耗，但这些设计只有在游戏本身好玩、玩家自愿参与时才有效。为了维持代币价格而制造无意义消耗，只会把问题推迟。</p>
<h2>5. 案例一：Axie Infinity 的繁殖经济</h2>
<p>Axie Infinity 是早期具有代表性的收藏与战斗类链游。玩家使用数字宠物参与战斗，并可以消耗游戏资源繁殖新的宠物。</p>
<p>它的经济结构可以简化为：</p>
<ol>
<li>新玩家需要获得宠物才能参与部分玩法。</li>
<li>玩家通过游戏获得功能型资源。</li>
<li>繁殖宠物需要消耗相应资源。</li>
<li>新宠物可以进入市场，供其他玩家购买和使用。</li>
</ol>
<h3>为什么早期循环能够运转</h3>
<p>当新玩家持续进入时，宠物需求提高，繁殖就有吸引力；繁殖消耗游戏代币，代币也获得了使用需求。玩家、繁殖者和市场形成正向循环。</p>
<h3>循环为什么容易失衡</h3>
<p>宠物能够不断繁殖，意味着供应会增长。如果新增玩家和宠物消耗速度低于繁殖速度，市场供给就可能超过需求。与此同时，玩家持续获得并卖出奖励，会形成额外卖压。</p>
<p>Axie 的官方经济说明也曾明确讨论人口增长、奖励发行、资源消耗和对新增参与者的依赖。这一案例说明：<strong>玩家拥有资产并不能代替供需管理。</strong></p>
<p>参考：<a href="https://whitepaper.axieinfinity.com/gameplay/axie-population-and-long-term-sustainability" target="_blank" rel="noopener noreferrer">Axie Infinity 官方经济说明</a></p>
<h3>从案例中学到什么</h3>
<ul>
<li>可繁殖资产必须有供应上限或足够强的消耗机制。</li>
<li>新玩家需求不应成为系统唯一的价值来源。</li>
<li>经济参数需要持续调整，但频繁改动也会损害玩家预期。</li>
<li>奖励应更多来自游戏服务收入，而不是单纯增发代币。</li>
</ul>
<h2>6. 案例二：STEPN 的运动激励和双代币模型</h2>
<p>STEPN 把户外运动、NFT 运动鞋和代币奖励结合起来。用户装备符合条件的运动鞋，在特定规则下步行或跑步，消耗能量并获得游戏代币。</p>
<p>它提供了一个清晰的游戏化设计案例：现实行为通过手机传感器和反作弊系统验证，再转换成游戏奖励。</p>
<h3>双代币分别解决什么问题</h3>
<p>其设计中包含功能代币与治理代币：</p>
<ul>
<li>功能代币用于游戏中的日常获得与消耗。</li>
<li>治理代币承担更长期的治理或高级功能。</li>
<li>NFT 运动鞋决定部分属性、能量和奖励能力。</li>
</ul>
<p>用户可以把功能代币用于修理、升级、合成等操作。这样的“代币水槽”用于减少流通中的供给。</p>
<h3>一个玩家选择案例</h3>
<p>假设玩家今天获得 30 枚功能代币，他可以：</p>
<ul>
<li>全部卖出，形成市场供给。</li>
<li>用于修理运动鞋，维持后续使用。</li>
<li>用于升级属性，换取未来可能的效率提升。</li>
<li>与其他资源组合，尝试生成新资产。</li>
</ul>
<p>如果大多数玩家认为升级没有意义，就会倾向于卖出；如果所有人都只为获得收益而运动，代币买方可能不足。因此，经济平衡不仅取决于消耗按钮有多少，还取决于玩家是否真正需要这些功能。</p>
<p>参考：<a href="https://whitepaper.stepn.com/" target="_blank" rel="noopener noreferrer">STEPN 官方白皮书</a></p>
<h3>从案例中学到什么</h3>
<ul>
<li>把现实行为游戏化，可以扩大 GameFi 的使用场景。</li>
<li>反作弊是经济安全的一部分；虚假运动等同于无成本增发奖励。</li>
<li>双代币可以分离日常使用与治理，但不会自动解决供需失衡。</li>
<li>当进入成本和回本预期成为主要讨论时，游戏体验往往退居其次。</li>
</ul>
<h2>7. 案例三：Sorare 将现实比赛数据引入卡牌游戏</h2>
<p>Sorare 是梦幻体育类游戏。玩家收集数字球员卡，组成阵容，游戏得分与现实比赛中的球员表现相关。</p>
<p>这个案例与纯粹的“做任务获得代币”不同，它把 NFT 的效用与外部数据结合起来：</p>
<ol>
<li>卡牌代表特定球员和赛季版本。</li>
<li>玩家用卡牌组成参赛阵容。</li>
<li>现实比赛数据通过数据服务进入游戏。</li>
<li>游戏根据表现计算分数和排名。</li>
</ol>
<h3>为什么它具有持续使用场景</h3>
<p>球迷本来就有关注球队、球员和比赛的需求。卡牌既可以收藏，也可以参与阵容策略。其使用价值不完全依赖“今天能获得多少代币”，而与体育内容和游戏竞争有关。</p>
<h3>它增加了哪些新依赖</h3>
<p>这种模式仍然依赖：</p>
<ul>
<li>体育联盟与球员形象的授权。</li>
<li>现实比赛和数据供应商。</li>
<li>游戏评分规则的公平性。</li>
<li>卡牌发行量与赛季更新机制。</li>
<li>项目在不同地区适用的监管规则。</li>
</ul>
<p>参考：<a href="https://sorare.com/help/a/4402780460817/what-is-sorare-football" target="_blank" rel="noopener noreferrer">Sorare 官方玩法介绍</a></p>
<h3>从案例中学到什么</h3>
<ul>
<li>链上资产可以与现实内容结合，形成持续效用。</li>
<li>现实数据为玩法提供变化，也引入预言机和授权风险。</li>
<li>“资产属于玩家”不意味着项目方失去对比赛规则的控制。</li>
<li>游戏的长期吸引力可以来自兴趣和竞争，而不仅是代币奖励。</li>
</ul>
<h2>8. 案例四：全链上策略游戏</h2>
<p>另一类实验性产品会把地图状态、资源规则、行动结果和胜负条件尽可能写进智能合约。这类产品常被称为全链上游戏。</p>
<h3>一个链上星际探索案例</h3>
<p>假设每个星球的位置和归属都记录在链上：</p>
<ol>
<li>
<p>玩家提交探索或移动指令。</p>
</li>
<li>
<p>合约验证距离、资源和冷却时间。</p>
</li>
<li>
<p>合法操作改变星球归属和资源状态。</p>
</li>
<li>
<p>任何人都可以读取规则，并开发新的地图界面或数据工具。</p>
</li>
</ol>
<p>如果官方网页停止服务，社区仍有可能为同一套链上状态开发新的客户端。这是全链上设计相较传统服务器游戏最独特的地方。</p>
<h3>为什么不是所有游戏都适合全上链</h3>
<ul>
<li>每次操作都可能产生手续费和确认等待。</li>
<li>公开状态可能让隐藏信息和反作弊更困难。</li>
<li>合约升级比服务器更新更复杂。</li>
<li>实时动作和高频交互对性能要求极高。</li>
<li>错误规则部署后，修复可能需要治理或迁移。</li>
</ul>
<p>因此，全链上更适合回合制、策略、模拟经营等允许较慢交互的类型，而不一定适合所有实时竞技游戏。</p>
<h2>9. 四种设计模式放在一起看</h2>
<figure><img src="/assets/images/web3/chapter001/gamefi-design-patterns.png" alt="区块链游戏的四种典型设计模式" tabindex="0" loading="lazy"><figcaption>区块链游戏的四种典型设计模式</figcaption></figure>
<p>| 案例模式 | 区块链承担的角色 | 主要需求来源 | 核心风险 |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/gamefi-architecture.png" type="image/png"/>
    </item>
    <item>
      <title>十、DAO：链上协作、治理机制与现实边界</title>
      <link>https://www.linkjm.com/crypto/chapter001/007.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/007.html</guid>
      <source url="https://www.linkjm.com/rss.xml">十、DAO：链上协作、治理机制与现实边界</source>
      <description>从提案、投票、委托、多签、时间锁和金库管理理解 DAO，并通过协议治理、ENS 与 Nouns 等案例认识去中心化协作的优势和局限。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>DAO 是 <strong>Decentralized Autonomous Organization</strong> 的缩写，通常译为“去中心化自治组织”。它用公开规则、数字钱包、智能合约和在线社区协调一群人共同管理协议、资金或公共资源。</p>
<p>DAO 并不是“没有管理者的公司”，也不是把每件事都交给所有成员投票。现实中的 DAO 往往同时包含论坛讨论、核心团队、工作组、多签委员会和链上治理，是一套技术规则与社会协作混合而成的组织方式。</p>
<p>理解 DAO 的关键，是回答四个问题：谁能提出建议、谁有决定权、谁负责执行、谁为错误承担责任。</p>
<h2>1. DAO 解决什么问题</h2>
<p>假设一群来自不同国家的开发者和用户共同维护一个开源协议。协议积累了一笔收入，需要决定如何升级产品、资助开发和处理安全事故。</p>
<p>传统做法是成立公司，由董事会和管理层决策。DAO 则尝试把部分组织规则公开化：</p>
<ul>
<li>成员通过钱包证明身份或治理权。</li>
<li>提案与讨论可以被所有人查看。</li>
<li>投票权按照预先约定的规则计算。</li>
<li>共同资金由多签钱包或智能合约管理。</li>
<li>通过的链上提案可以自动执行。</li>
<li>决策和资金流留下可审计记录。</li>
</ul>
<p>这让跨地域协作更容易，但不会消除人的判断、谈判、利益冲突和法律责任。</p>
<h2>2. 一个 DAO 通常由哪些部分组成</h2>
<h3>共同目标</h3>
<p>DAO 需要明确自己为何存在，例如维护借贷协议、管理域名系统、资助公共产品或收藏数字艺术。没有共同目标的代币持有人，并不会自动形成有效组织。</p>
<h3>成员与治理权</h3>
<p>成员资格可能来自治理代币、NFT、贡献记录、邀请或现实身份。不同资格可以对应不同权利，例如讨论、提案、投票、领取预算或代表工作组。</p>
<h3>治理规则</h3>
<p>治理规则包括提案门槛、投票方式、法定人数、通过比例、执行延迟和紧急权限。规则可以写入智能合约，也可以由章程和社区共识约束。</p>
<h3>金库</h3>
<p>DAO 金库用于保存代币、稳定币或其他资产，并为开发、运营、研究和社区项目提供预算。公开地址让任何人能够查看余额和转账，但无法自动证明每一笔支出都合理。</p>
<h3>执行者</h3>
<p>投票通过后仍然需要执行。执行者可能是智能合约、多签成员、基金会、公司或工作组。判断 DAO 是否真正去中心化，不能只看投票界面，还要看最终控制密钥在谁手中。</p>
<h2>3. 一项提案如何变成实际行动</h2>
<p>成熟的治理不会从一个想法直接跳到资金转账，通常会经历多个阶段。</p>
<figure><img src="/assets/images/web3/chapter001/dao-governance-process.png" alt="DAO 从讨论到链上执行的治理流程" tabindex="0" loading="lazy"><figcaption>DAO 从讨论到链上执行的治理流程</figcaption></figure>
<h3>讨论</h3>
<p>成员在论坛或社区中说明问题，收集反对意见，并判断它是否值得进入正式流程。</p>
<h3>正式提案</h3>
<p>提案需要写清目标、预算、时间表、负责人、验收标准和链上操作。涉及合约调用时，还要提供可以被审查的执行数据。</p>
<h3>意见投票</h3>
<p>社区可以先进行不直接执行的“温度检查”，观察支持度。此类投票成本较低，但结果可能只是社会信号。</p>
<h3>正式投票</h3>
<p>符合门槛的提案进入正式表决。系统会检查投票时间、投票权、法定人数和通过比例。</p>
<h3>时间锁</h3>
<p>通过的提案可以先进入一段等待期。时间锁让用户有机会发现恶意操作、撤出资产或采取防御措施。</p>
<h3>执行</h3>
<p>链上治理可以在等待期结束后调用合约或转移资金；链下治理则需要多签委员会或工作组按照结果执行。</p>
<h3>一个社区工具资助案例</h3>
<p>假设开发者申请 50,000 枚稳定币制作数据看板。一个完整提案至少应包含：</p>
<ol>
<li>看板解决什么问题，目标用户是谁。</li>
<li>负责人过去完成过哪些项目。</li>
<li>资金分几期支付，每期交付什么成果。</li>
<li>源代码和数据是否向社区开放。</li>
<li>项目延期或停止时，剩余预算如何处理。</li>
<li>谁负责验收，争议如何解决。</li>
</ol>
<p>投票只能决定“是否授权”，不能代替项目管理。使用分期拨款、里程碑和公开报告，通常比一次性支付全部资金更稳妥。</p>
<h2>4. DAO 常见的四种决策机制</h2>
<figure><img src="/assets/images/web3/chapter001/dao-voting-models.png" alt="DAO 的四种决策机制" tabindex="0" loading="lazy"><figcaption>DAO 的四种决策机制</figcaption></figure>
<h3>一人一票</h3>
<p>每名合格成员拥有相同票数，形式上较平等。但系统需要解决一个人创建多个身份的问题，这被称为女巫攻击。</p>
<p>适用场景包括有明确成员审核的小型社区、合作组织或使用可信身份凭证的投票。</p>
<h3>代币加权投票</h3>
<p>持有的治理代币越多，投票权越大。它容易在开放网络中计算，也让承担较大经济风险的人拥有更高权重。</p>
<p>缺点是财富可能转化为治理控制权。早期团队、投资机构和大户可能主导结果，小额成员即使人数很多也难以影响决策。</p>
<h3>委托投票</h3>
<p>成员把投票权委托给更愿意研究提案的代表，代币通常仍保留在原钱包中。委托可以提高专业程度和参与率，也会形成具有持续影响力的代表群体。</p>
<p>评估委托制度时，要看委托能否随时更换、代表是否公开披露利益冲突，以及投票记录是否透明。</p>
<h3>多签委员会</h3>
<p>多签钱包要求多名成员中的一定数量共同签名。例如五名成员中至少三人签名，资金才能转出。</p>
<p>多签适合日常付款和紧急响应，执行速度通常快于全体投票。但签名人数量过少、身份关联或缺乏监督，会形成新的中心化风险。</p>
<h3>机制组合比单一机制更常见</h3>
<p>一个 DAO 可以让代币持有人决定重大方向，让工作组处理日常运营，再由多签钱包执行预算。不同决策使用不同机制，往往比“所有事情全民投票”更有效。</p>
<h2>5. 法定人数、通过比例和投票门槛</h2>
<p>治理参数决定少数人能否代表整个社区。</p>
<ul>
<li><strong>提案门槛</strong>：发起正式提案需要达到的投票权或支持量，用于减少垃圾提案。</li>
<li><strong>法定人数</strong>：一项投票至少需要多少参与权重才有效。</li>
<li><strong>通过比例</strong>：有效投票中需要多少赞成票才能通过。</li>
<li><strong>投票周期</strong>：成员可以参与表决的时间长度。</li>
<li><strong>执行延迟</strong>：通过后等待多久才能执行。</li>
</ul>
<h3>一个法定人数案例</h3>
<p>某 DAO 一共有 100 万票治理权：</p>
<ul>
<li>只有 2 万票参与投票。</li>
<li>其中 1.5 万票赞成，5 千票反对。</li>
<li>赞成比例是 75%，看似获得明显支持。</li>
</ul>
<p>如果该 DAO 规定法定人数为 10 万票，这项提案仍然无效。法定人数防止极少数活跃成员在其他人不知情时控制组织。</p>
<p>但法定人数设置过高，也可能让任何提案都无法通过。治理设计是在安全、代表性和执行效率之间寻找平衡。</p>
<h2>6. 多签和时间锁为什么重要</h2>
<h3>多签降低单点密钥风险</h3>
<p>如果金库只由一个私钥控制，密钥泄露或持有人失联都可能造成严重后果。多签把执行权分散到多个签名人。</p>
<p>良好的多签安排还应考虑：</p>
<ul>
<li>签名人分布在不同地区和设备。</li>
<li>成员之间不存在同一控制关系。</li>
<li>定期检查并轮换离职或失效的签名人。</li>
<li>保存可验证的执行记录。</li>
<li>为紧急情况准备恢复和替补流程。</li>
</ul>
<h3>时间锁提供反应窗口</h3>
<p>假设一项提案通过后要转走金库中的大量资产。如果操作立即执行，社区即使发现提案代码与描述不一致，也没有时间阻止。</p>
<p>时间锁把“决定已经通过”和“操作真正发生”分开，为审查和应急响应留出窗口。不过，面对正在发生的攻击，过长的时间锁又可能妨碍快速修复。</p>
<h2>7. 案例一：协议参数治理</h2>
<p>借贷和稳定币协议需要不断调整抵押率、利率、支持资产和风险上限。一种常见模式是让治理代币持有人或代表对参数变化投票。</p>
<h3>抵押品调整案例</h3>
<p>假设社区准备把一种新代币加入借贷协议：</p>
<ol>
<li>风险团队分析流动性、价格波动和预言机质量。</li>
<li>提案设置抵押率、借款上限和清算参数。</li>
<li>社区讨论该资产是否容易被操纵。</li>
<li>正式投票通过后，时间锁等待审查。</li>
<li>合约执行提案，把新资产加入市场。</li>
</ol>
<p>如果参数过于宽松，攻击者可能利用低流动性资产借走优质资产；如果过于严格，新市场又无法使用。DAO 投票并不能取代风险专业知识，因此许多协议会设置专门的风险团队或委员会。</p>
<p>这类模式常见于 Maker、Aave、Compound 等协议治理。项目名称只是案例，不代表其当前具体参数或风险水平。</p>
<h2>8. 案例二：ENS 的委托和工作组治理</h2>
<p>ENS DAO 管理 ENS 协议和社区金库。其治理将代币投票、委托代表、章程与工作组结合起来。</p>
<p>一个普通代币持有人可能没有时间阅读每项提案，于是可以把投票权委托给长期参与治理的代表。代表研究提案并投票，持有人仍可以调整委托关系。</p>
<p>对于持续性的日常事务，工作组和负责人可以在授权预算内开展工作，例如生态建设、公共产品和治理运营。重大事项再进入更广泛的提案流程。</p>
<p>ENS 还使用章程为治理权设定边界：即使某个提案获得多数支持，也不意味着它一定符合组织的基本原则。</p>
<p>参考：<a href="https://docs.ens.domains/dao/governance/process/" target="_blank" rel="noopener noreferrer">ENS DAO 治理流程</a>与 <a href="https://docs.ens.domains/dao/constitution/" target="_blank" rel="noopener noreferrer">ENS DAO 章程</a></p>
<h3>从案例中学到什么</h3>
<ul>
<li>委托解决“每个人都没有时间研究所有提案”的问题。</li>
<li>工作组适合处理需要连续投入的专业事务。</li>
<li>章程可以约束多数投票，防止治理无限扩张。</li>
<li>社会规则、法律主体与链上合约需要共同运作。</li>
</ul>
<h2>9. 案例三：Nouns DAO 的持续拍卖与公共金库</h2>
<p>Nouns 采用一种容易观察的资金循环：系统持续生成并拍卖 NFT，拍卖收入进入 DAO 金库，NFT 持有人使用治理权决定如何使用资金。</p>
<p>假设社区成员提出制作一部开放版权短片：</p>
<ol>
<li>创作者公开脚本、预算和制作时间。</li>
<li>NFT 持有人讨论它是否有助于社区目标。</li>
<li>符合条件的提案进入链上投票。</li>
<li>通过后，金库按照提案执行资金转移。</li>
<li>社区继续跟踪作品是否按计划交付。</li>
</ol>
<p>它展示了 NFT 不仅可以作为收藏品，也能同时代表治理成员资格。但一枚 NFT 一票的设计，也意味着持有成本和资产集中会影响治理参与。</p>
<p>参考：<a href="https://nouns.center/intro" target="_blank" rel="noopener noreferrer">Nouns DAO 机制介绍</a></p>
<h3>从案例中学到什么</h3>
<ul>
<li>收入来源、成员资格与治理权可以通过协议连接起来。</li>
<li>链上执行提高了资金流的可验证性。</li>
<li>公开支出不等于支出有效，仍需要评审和验收。</li>
<li>NFT 持有人与更广泛的社区参与者不一定拥有相同权力。</li>
</ul>
<h2>10. 案例四：紧急漏洞应该全民投票吗</h2>
<p>假设安全团队发现一个尚未公开的漏洞，攻击者可能在两小时内转走资金。常规治理需要七天讨论和投票，显然来不及。</p>
<p>DAO 通常会考虑设置安全委员会，并授予有限权限，例如暂停特定合约。但这也产生新的问题：</p>
<ul>
<li>委员会能否借“紧急”之名冻结正常用户？</li>
<li>权限是否有期限和适用范围？</li>
<li>暂停后由谁决定恢复？</li>
<li>委员身份和行动记录是否公开？</li>
</ul>
<p>一种折中方案是让安全委员会只能暂停、不能转移资金，并要求暂停后在限定时间内获得完整治理确认，否则自动失效。</p>
<p>这个案例说明：治理越去中心化，紧急响应可能越慢；响应越集中，权力滥用风险越高。好的制度会限制紧急权力，而不是假设紧急权力永远不会被滥用。</p>
<h2>11. DAO 常见的治理风险</h2>
<figure><img src="/assets/images/web3/chapter001/dao-governance-risks.png" alt="DAO 的主要治理风险与防护措施" tabindex="0" loading="lazy"><figcaption>DAO 的主要治理风险与防护措施</figcaption></figure>
<h3>低参与率</h3>
<p>大量持有人不投票，少数活跃地址就可能决定结果。委托、提醒机制和合理的法定人数可以改善参与，但无法彻底解决冷漠问题。</p>
<h3>巨鲸控制</h3>
<p>代币加权投票可能让大户、团队和机构拥有决定性影响。公开委托、投票权上限或其他权重设计可以缓解，但也会增加复杂性。</p>
<h3>治理攻击</h3>
<p>攻击者可能购买或借入大量投票权，通过恶意提案转移资产或修改规则。提案门槛、历史快照、时间锁和安全否决机制可以提供防护。</p>
<h3>多签失陷</h3>
<p>签名人私钥泄露、合谋或长期不更新，会危及金库。硬件钱包、成员分散、密钥轮换和权限限制是必要措施。</p>
<h3>治理疲劳</h3>
<p>提案太多、文本太长或议题过于专业，会使普通成员退出治理。工作组和委托能够减少负担，但也可能让权力集中到少数专业参与者。</p>
<h3>利益冲突</h3>
<p>代表可能同时受雇于项目方、投资多个竞争协议，或从提案中直接获益。利益披露和回避机制对于建立信任十分重要。</p>
<h2>12. DAO 不等于法律主体</h2>
<p>智能合约能够持有资产和执行规则，但它本身不一定具有签订劳动合同、缴税、承担债务和出庭应诉的法律能力。</p>
<p>很多 DAO 会同时设立基金会、协会或公司，负责雇佣、知识产权、银行账户和合规事务。此时要区分：</p>
<ul>
<li>链上治理决定了什么。</li>
<li>法律实体能够执行什么。</li>
<li>多签成员承担什么责任。</li>
<li>社区参与者是否构成合伙或其他法律关系。</li>
</ul>
<p>不同地区对 DAO、代币和成员责任的规定差异很大。涉及募集资金、投资、雇佣或现实资产时，应获得适用地区的专业法律意见。</p>
<h2>13. 如何判断一个 DAO 是否真正有效</h2>
<p>不要只看它有没有治理代币或 Discord 社区，可以从以下维度评估：</p>
<ol>
<li><strong>目标是否清楚</strong>：成员是否知道组织要解决什么问题。</li>
<li><strong>权力是否透明</strong>：合约管理员、多签成员和法律实体是否公开。</li>
<li><strong>参与是否真实</strong>：投票人数、委托分布和讨论质量如何。</li>
<li><strong>金库是否可持续</strong>：收入来源是什么，预算是否有监督。</li>
<li><strong>执行是否可靠</strong>：通过的提案能否按时交付和验收。</li>
<li><strong>安全是否分层</strong>：是否具有时间锁、权限限制和应急流程。</li>
<li><strong>退出是否明确</strong>：成员如何退出，资产和责任如何处理。</li>
</ol>
<p>DAO 的价值不在于把“公司”换成“社区”，而在于能否用更透明、更开放的方式完成长期协作。</p>
<h2>本文小结</h2>
<ul>
<li>DAO 是技术规则与社会协作共同构成的在线组织形式。</li>
<li>一套完整治理包含讨论、提案、投票、时间锁、执行和监督。</li>
<li>一人一票、代币加权、委托和多签各有适用场景与缺点。</li>
<li>法定人数和提案门槛用于防止极少数成员控制决策。</li>
<li>ENS 展示了委托、章程和工作组的组合治理。</li>
<li>Nouns 展示了持续收入、NFT 成员资格和链上金库治理。</li>
<li>紧急权限能够提高响应速度，但必须限制范围和期限。</li>
<li>公开投票不能自动解决低参与率、巨鲸控制和利益冲突。</li>
<li>DAO 不一定是法律主体，现实活动仍需要明确责任和合规安排。</li>
</ul>
<p>真正的自治，不是取消所有负责人，而是让权力来源、决策过程、执行权限和问责方式都更加清楚。</p>
<p>下一篇：<a href="/crypto/chapter001/008.html" target="_blank">十一、Web3 监管：合规边界、常见风险与用户责任</a></p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/dao-governance-process.png" type="image/png"/>
    </item>
    <item>
      <title>十一、Web3 监管：合规边界、常见风险与用户责任</title>
      <link>https://www.linkjm.com/crypto/chapter001/008.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/008.html</guid>
      <source url="https://www.linkjm.com/rss.xml">十一、Web3 监管：合规边界、常见风险与用户责任</source>
      <description>从代币定性、平台许可、反洗钱、税务和消费者保护理解 Web3 监管，并认识中国内地、香港、欧盟等地区规则的差异。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>Web3 可以跨越国境运行，但参与者仍然生活在具体的司法管辖区。代码能够自动执行交易，却不能让发行、经营、纳税和消费者保护等法律问题自动消失。</p>
<p>监管内容变化很快。本篇提供的是理解框架和学习案例，不构成法律、税务或投资意见。进行交易、发行代币或经营相关业务前，应查询所在地监管机构的最新文件，并在必要时咨询专业人士。</p>
<h2>1. 为什么 Web3 需要监管</h2>
<p>区块链同时具有技术与金融属性。它可以改善跨境结算、资产登记和开放协作，也可能被用于诈骗、非法集资、洗钱、操纵市场和逃避制裁。</p>
<p>监管通常关注五个层面：</p>
<figure><img src="/assets/images/web3/chapter001/regulation-layers.png" alt="Web3 合规监管的五个层面" tabindex="0" loading="lazy"><figcaption>Web3 合规监管的五个层面</figcaption></figure>
<h3>市场规则</h3>
<p>防止虚假披露、内幕交易、操纵价格和未经许可募集资金，要求参与者在相对公平的信息环境中交易。</p>
<h3>消费者保护</h3>
<p>要求服务商说明风险、妥善保管客户资产、处理投诉，并避免用误导方式向不适合的用户推销高风险产品。</p>
<h3>反洗钱与制裁</h3>
<p>要求特定服务商识别客户、监测异常交易、保存记录，并在法律要求下报告可疑活动。</p>
<h3>税务申报</h3>
<p>买卖、兑换、支付、质押奖励、挖矿、空投和 NFT 交易，都可能产生记录或申报义务。是否纳税以及如何计算，取决于所在地规定和具体事实。</p>
<h3>网络与托管安全</h3>
<p>监管可能对客户资产隔离、私钥管理、审计、业务连续性和事件报告提出要求，降低平台倒闭或被攻击造成的损失。</p>
<h2>2. 同一种代币可能面对不同规则</h2>
<p>“加密货币”“治理代币”或“NFT”只是产品名称，并不能直接决定法律属性。监管者通常会查看发行方式、经济功能、持有人权利和实际营销方式。</p>
<figure><img src="/assets/images/web3/chapter001/token-classification.png" alt="代币法律定性取决于功能和事实" tabindex="0" loading="lazy"><figcaption>代币法律定性取决于功能和事实</figcaption></figure>
<h3>支付型功能</h3>
<p>如果资产主要用于转账或支付，可能涉及支付服务、反洗钱、外汇和资金传输规则。</p>
<h3>投资属性</h3>
<p>如果发行方公开募集资金，并让购买者期待依靠团队经营获得回报，可能触及证券、集体投资或金融产品规则。</p>
<h3>使用权益</h3>
<p>只能访问某个产品或服务的代币，也不一定自动排除金融监管。监管者会判断它是否真正投入使用，还是以“功能代币”名义进行投资募集。</p>
<h3>稳定价值</h3>
<p>锚定法币或其他资产的稳定币，可能受到储备、赎回、披露和发行人许可等额外要求。</p>
<h3>NFT 与收藏品</h3>
<p>真正独特、不可替代的收藏品可能适用不同规则。但把大量同质资产包装成 NFT，或以收益分配为卖点，仍可能进入金融监管范围。</p>
<h3>一个“游戏金币”案例</h3>
<p>项目把代币称为游戏金币，但在游戏上线前向公众出售，并宣传团队经营会推动价格上涨。即使名称强调“游戏”，监管判断仍可能关注它的融资方式和购买者预期。</p>
<p>反过来，一个已经运行的游戏积分，如果只能在封闭系统中兑换服务、不能自由交易，面对的规则可能完全不同。</p>
<h2>3. KYC、AML 和转账信息规则</h2>
<h3>KYC 是什么</h3>
<p>KYC 是“了解你的客户”。受监管平台通常会收集姓名、证件、地址或资金来源等信息，以确认账户由谁控制。</p>
<p>KYC 能提高违法活动追踪能力，但也让平台持有大量敏感数据。用户应同时关注服务商的数据保护、保存期限和泄露应对措施。</p>
<h3>AML 是什么</h3>
<p>AML 是反洗钱。服务商不仅要在开户时识别客户，还可能持续监测交易，识别资金分层、诈骗流入、受制裁地址和异常跨境活动。</p>
<h3>Travel Rule 是什么</h3>
<p>FATF 针对虚拟资产服务商推动转账信息规则。受监管服务商之间进行符合条件的转账时，可能需要传递发送方和接收方的必要信息。</p>
<figure><img src="/assets/images/web3/chapter001/travel-rule-flow.png" alt="受监管服务商之间的转账与信息流" tabindex="0" loading="lazy"><figcaption>受监管服务商之间的转账与信息流</figcaption></figure>
<p>身份信息通常由服务商之间通过合规渠道传递，并不意味着个人姓名直接公开写入区块链。区块链上主要显示地址、金额和时间等交易数据。</p>
<p>不同地区对适用门槛、信息字段和自托管钱包的处理方式可能不同，应以当地规则为准。</p>
<p>参考：<a href="https://www.fatf-gafi.org/content/dam/fatf/documents/recommendations/Updated-Guidance-VA-VASP.pdf" target="_blank" rel="noopener noreferrer">FATF 虚拟资产与服务商风险方法指南</a></p>
<h2>4. 去中心化不等于不受监管</h2>
<p>一个协议可能没有传统账户系统，但仍然存在开发团队、网站运营者、前端、治理成员、手续费接收者和法律实体。</p>
<p>监管判断可能关注：</p>
<ul>
<li>谁开发并持续控制协议。</li>
<li>谁能够升级合约或暂停交易。</li>
<li>谁收取费用并进行营销。</li>
<li>用户是否依赖某个前端或托管服务。</li>
<li>治理是否真正分散，还是由少数地址控制。</li>
</ul>
<h3>一个 DEX 案例</h3>
<p>智能合约部署后可以自行撮合兑换，但官方网站仍由公司运营，公司还控制代币列表和管理员密钥。此时“合约去中心化”并不能直接回答前端经营者是否承担监管责任。</p>
<h3>一个 DAO 案例</h3>
<p>DAO 通过投票销售代币、雇佣推广人员并向公众承诺收益。即使没有注册公司，提案人、多签成员和实际运营者仍可能在特定地区承担责任。</p>
<h2>5. 为什么不同地区会得出不同结论</h2>
<p>Web3 交易可能同时连接用户所在地、平台所在地、发行方所在地和区块链节点所在地区。</p>
<figure><img src="/assets/images/web3/chapter001/jurisdiction-factors.png" alt="Web3 规则因司法管辖区而异" tabindex="0" loading="lazy"><figcaption>Web3 规则因司法管辖区而异</figcaption></figure>
<p>分析时至少要确认：</p>
<ol>
<li>用户通常居住或纳税的地区。</li>
<li>服务商在哪里注册、经营和主动营销。</li>
<li>代币或产品具有什么实际功能。</li>
<li>用户进行的是持有、兑换、发行、推广还是资产管理。</li>
<li>法币与加密资产从哪里流入、流向哪里。</li>
</ol>
<p>使用境外网站，不会自动绕开用户所在地的法律；平台能够访问，也不代表当地允许使用。</p>
<h2>6. 中国内地：区块链技术与虚拟货币业务需要区分</h2>
<p>中国内地政策支持区块链技术在实体经济和数字化中的合规应用，但对虚拟货币相关金融活动采取严格限制。</p>
<p>中国人民银行等部门 2021 年发布的通知明确指出：比特币、以太币、泰达币等虚拟货币不具有与法定货币等同的法律地位，相关兑换、交易中介、代币发行融资和衍生品等业务属于非法金融活动；境外交易所通过互联网向境内居民提供服务，也被纳入通知范围。</p>
<p>因此，“支持区块链创新”不能被理解为允许面向境内居民经营虚拟货币交易或发行融资业务。</p>
<p>个人还应注意：即使能够技术上访问某个协议或境外平台，也不代表交易受到法律保护。涉及代币交易、推广、支付结算、技术支持或为他人提供通道时，应先了解具体行为的法律风险。</p>
<p>参考：<a href="https://www.pbc.gov.cn/tiaofasi/144941/3581332/4348658/index.html" target="_blank" rel="noopener noreferrer">中国人民银行等部门《关于进一步防范和处置虚拟货币交易炒作风险的通知》</a></p>
<h2>7. 中国香港：通过许可制度管理虚拟资产平台</h2>
<p>香港对虚拟资产采取许可和持续监管框架。根据香港证监会公开资料，在香港经营或积极向香港投资者推广服务的中心化虚拟资产交易平台，需要符合相应许可要求。</p>
<p>普通用户需要区分三种状态：</p>
<ul>
<li>已正式获得许可的平台。</li>
<li>正在申请但尚未获批的平台。</li>
<li>未受当地监管或没有相应许可的平台。</li>
</ul>
<p>“已经申请”“正在审批”和“已经持牌”并不是同一回事。证监会也提醒，获得许可不代表监管机构保证平台的表现或信用。</p>
<p>核验时应直接使用 <a href="https://www.sfc.hk/en/Welcome-to-the-Fintech-Contact-Point/Virtual-assets/Virtual-asset-trading-platforms-operators/Lists-of-virtual-asset-trading-platforms" target="_blank" rel="noopener noreferrer">香港证监会虚拟资产交易平台名单</a>，不要只相信平台自己的宣传截图。</p>
<h2>8. 欧盟：MiCA 建立统一的加密资产框架</h2>
<p>欧盟的《加密资产市场监管法案》通常简称 MiCA。它为不属于欧盟其他金融法规覆盖范围的加密资产发行和相关服务建立统一框架。</p>
<p>其关注对象包括部分加密资产发行人、稳定币发行人和加密资产服务商，并涉及授权、信息披露、组织管理、审慎要求和市场滥用等方面。</p>
<p>MiCA 并不代表所有加密资产都采用同一规则：已经属于传统金融工具的产品可能适用其他法律，真正独特且不可替代的资产也需要根据具体情况判断。</p>
<p>参考：<a href="https://finance.ec.europa.eu/digital-finance/crypto-assets_en" target="_blank" rel="noopener noreferrer">欧盟委员会 MiCA 官方介绍</a></p>
<h2>9. 美国：多机构和多层级规则并存</h2>
<p>美国数字资产监管可能同时涉及联邦证券、商品、反洗钱、税务和州级许可等规则。某项资产或业务由哪个机构监管，往往取决于产品结构和实际活动。</p>
<p>对于普通用户，最容易忽视的是税务记录。美国国税局明确将数字资产范围涵盖加密货币、稳定币和 NFT，并要求符合条件的收入与处置交易进行申报。</p>
<p>这并不意味着其他国家采用相同税制，而是说明一个普遍原则：<strong>钱包之间可以自由转账，不代表税务义务自动消失。</strong></p>
<p>参考：<a href="https://www.irs.gov/filing/digital-assets" target="_blank" rel="noopener noreferrer">美国国税局数字资产指引</a></p>
<h2>10. 托管平台与自托管的责任不同</h2>
<h3>使用托管平台</h3>
<p>平台掌握私钥并维护账户系统，用户通常需要接受身份验证、提款审核和地区限制。相应地，平台可能承担客户资产保管、交易监测和监管报告义务。</p>
<p>持牌并不等于零风险。用户仍需查看资产是否隔离、平台如何处理破产、是否提供储备或审计信息，以及所在地区是否有赔偿机制。</p>
<h3>使用自托管钱包</h3>
<p>用户自己控制私钥，不依赖平台批准每笔链上交易。但用户也要自己承担助记词备份、合约授权、地址核验和税务记录责任。</p>
<p>自托管不等于匿名。公开区块链会长期保留地址之间的交易关系，一旦地址与现实身份关联，历史活动可能被分析。</p>
<h2>11. 税务：没有换成法币也可能需要记录</h2>
<p>不同地区的税务规则差异很大，但以下操作通常都值得完整记录：</p>
<ul>
<li>用法币买入数字资产。</li>
<li>卖出或兑换为法币。</li>
<li>一种代币兑换另一种代币。</li>
<li>使用代币购买商品或服务。</li>
<li>获得质押、挖矿、空投或工作报酬。</li>
<li>铸造、购买和出售 NFT。</li>
<li>在不同钱包和平台之间转账。</li>
<li>支付网络手续费和平台费用。</li>
</ul>
<h3>一个兑换案例</h3>
<p>用户用一种价值上涨的代币兑换稳定币，没有把钱提到银行账户。他可能认为自己“还没有兑现”，但某些地区会把代币交换视为资产处置，需要计算收益或损失。</p>
<h3>一个钱包转账案例</h3>
<p>把资产从自己的交易平台账户转到自己的钱包，通常与出售给他人不同。但如果缺少记录，未来可能难以证明两个地址都由自己控制，也难以还原取得成本。</p>
<p>最稳妥的做法是从第一笔交易开始保存时间、资产、数量、法币价值、手续费、交易哈希和交易目的。</p>
<h2>12. 常见的合规误区</h2>
<h3>“平台能打开，所以一定合法”</h3>
<p>网站没有屏蔽某个地区，不代表它获得当地许可，也不代表用户可以合法使用全部功能。</p>
<h3>“只使用稳定币，所以没有税务问题”</h3>
<p>稳定币仍然是数字资产。兑换、收益和支付可能产生报告或记录义务。</p>
<h3>“链上没有实名，所以查不到”</h3>
<p>地址虽然不是姓名，但交易关系公开可见。交易平台数据、银行记录和链上分析可能把地址与现实身份关联。</p>
<h3>“DAO 投票通过，所以行为合法”</h3>
<p>社区多数同意不能替代监管许可，也不能免除欺诈、制裁、税务或消费者保护责任。</p>
<h3>“持牌平台一定不会倒闭”</h3>
<p>许可代表平台受到一定规则约束，不是收益、流动性或信用保证。</p>
<h3>“不知情收到风险资金就一定没事”</h3>
<p>法律责任取决于事实和所在地规则，但资金来源异常可能导致平台审查、账户限制或调查。遇到陌生大额转入时，不应急于代为兑换或转出。</p>
<h2>13. 普通用户的合规与记录清单</h2>
<figure><img src="/assets/images/web3/chapter001/user-compliance-checklist.png" alt="普通用户的六项合规检查" tabindex="0" loading="lazy"><figcaption>普通用户的六项合规检查</figcaption></figure>
<ol>
<li><strong>核对当地规则</strong>：确认所在地是否允许使用该类资产和服务。</li>
<li><strong>核验平台资质</strong>：从监管机构官网查询，不依赖平台宣传。</li>
<li><strong>保存交易记录</strong>：记录买入、卖出、兑换、收益和转账。</li>
<li><strong>维护成本记录</strong>：保留取得时间、价格、手续费和资金来源。</li>
<li><strong>了解税务义务</strong>：确定哪些行为属于收入或处置，何时申报。</li>
<li><strong>保护身份和钱包</strong>：只向可信服务商提交资料，防止钓鱼和数据泄露。</li>
</ol>
<p>如果准备发行代币、管理他人资产、提供兑换、代客操作或公开募集资金，应在开展业务前获得专业意见，而不是等产品上线后再补合规。</p>
<h2>本文小结</h2>
<ul>
<li>Web3 监管通常涉及市场规则、消费者保护、反洗钱、税务和托管安全。</li>
<li>代币的法律属性取决于实际功能、发行方式和所在地规则，而不是项目名称。</li>
<li>KYC 用于识别客户，AML 用于持续监测风险，转账信息规则适用于部分受监管服务商。</li>
<li>去中心化协议仍可能存在可识别的开发、运营、治理和收费主体。</li>
<li>中国内地、香港、欧盟和美国采用不同监管结构，不能相互套用。</li>
<li>使用境外平台或自托管钱包，不会自动免除所在地的法律和税务责任。</li>
<li>持牌代表受到监管，不代表平台或资产绝对安全。</li>
<li>普通用户应长期保存交易、成本、手续费和钱包转移记录。</li>
</ul>
<p>合规不是 Web3 的附加功能，而是使用数字资产时需要与钱包安全同时建立的基本习惯。</p>
<p>下一篇：<a href="/crypto/chapter001/010.html" target="_blank">十二、资产安全与风险管理：从防骗到交易纪律</a></p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/regulation-layers.png" type="image/png"/>
    </item>
    <item>
      <title>十四、Web3 的未来：真实需求、行业趋势与参与机会</title>
      <link>https://www.linkjm.com/crypto/chapter001/009.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/009.html</guid>
      <source url="https://www.linkjm.com/rss.xml">十四、Web3 的未来：真实需求、行业趋势与参与机会</source>
      <description>从稳定币支付、现实资产上链、智能钱包、扩容互操作、可验证身份和开放协作观察 Web3 的长期趋势，并建立个人参与路线。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>预测一个行业的短期热点很难，预测代币价格更加困难。相比追逐叙事，更可靠的方法是观察：哪些问题真实存在，哪些产品已经有人使用，以及区块链是否比传统方案更适合解决它们。</p>
<p>本篇不提供价格预测或项目推荐，而是从用户需求、技术进展和商业模式出发，整理 Web3 值得长期观察的方向，以及个人可以如何参与。</p>
<h2>1. 判断趋势的三个问题</h2>
<p>看到一个新概念时，先不要问它会不会上涨，而要问：</p>
<ol>
<li><strong>解决什么问题</strong>：没有区块链时，用户遇到了什么实际困难？</li>
<li><strong>为什么需要区块链</strong>：公开结算、自托管、可组合或抗单点故障是否真的重要？</li>
<li><strong>谁愿意付费</strong>：产品收入来自真实服务，还是主要依赖代币增发和新增参与者？</li>
</ol>
<p>区块链是一种工具，而不是商业模式。技术成立，不代表市场需求成立；用户增长，也不代表收入能够覆盖成本。</p>
<h2>2. 六个值得长期观察的方向</h2>
<p>这些方向并不保证产生投资回报。它们值得关注，是因为背后存在可以持续验证的使用需求。</p>
<h2>3. 趋势一：稳定币成为互联网结算工具</h2>
<p>稳定币把法币计价单位带入区块链网络，使用户能够全天候转账，并与链上协议组合。</p>
<p>它可能适用于：</p>
<ul>
<li>跨境企业之间的结算。</li>
<li>全球平台向创作者或自由职业者付款。</li>
<li>商户收款和供应商付款。</li>
<li>加密资产市场中的交易计价。</li>
<li>无法方便获得美元银行服务地区的数字结算。</li>
</ul>
<h3>案例：跨境自由职业者收款</h3>
<p>假设设计师为海外客户完成项目：</p>
<ol>
<li>客户使用当地支付渠道购买合规发行的稳定币。</li>
<li>稳定币通过支持的区块链发送给设计师。</li>
<li>设计师可以保留稳定币、用于链上支付，或通过当地合规渠道兑换。</li>
</ol>
<p>这个流程可能缩短结算路径，但没有消除风险。双方仍要考虑发行方信用、网络选择、兑换渠道、反洗钱、税务和当地法律。</p>
<p>Visa 等支付机构已经公开讨论并试验稳定币结算与支付能力，这说明稳定币正在从加密交易工具向支付基础设施延伸。参考：<a href="https://corporate.visa.com/content/dam/VCOM/corporate/services/documents/vca-stablecoin-strategy.pdf" target="_blank" rel="noopener noreferrer">Visa 稳定币策略资料</a></p>
<h3>长期观察指标</h3>
<ul>
<li>有多少交易来自真实支付，而不是交易平台内部调拨。</li>
<li>商户是否愿意接受并长期持有稳定币。</li>
<li>发行方储备和赎回机制是否透明。</li>
<li>用户是否能在不同网络和法币通道之间顺畅转换。</li>
<li>监管是否为发行和支付服务提供清晰规则。</li>
</ul>
<h2>4. 趋势二：现实世界资产上链</h2>
<p>现实世界资产通常简称 RWA。它指的是把债券、基金份额、应收账款、商品或其他现实权益，通过数字代币进行登记、转移或结算。</p>
<h3>资产上链不是把图片做成 NFT</h3>
<p>一个有效的 RWA 系统至少需要连接五个环节：</p>
<p>链上代币只是其中一层。如果代币持有人没有清晰的法律权利，或者发行方无法交付底层资产，公开账本也无法自动解决问题。</p>
<h3>案例：代币化短期债券基金</h3>
<p>投资者完成身份验证后购买基金份额，份额以代币形式记录；基金管理人持有现实中的债券，托管人和审计机构核验资产；投资者按规则转让或赎回代币。</p>
<p>潜在优势包括缩短结算时间、提高可编程性和支持更细粒度的份额管理。主要风险则包括发行人、托管、法律执行、预言机、智能合约和流动性风险。</p>
<p>BIS 对代币化的讨论也强调，真正价值来自货币、资产和平台之间更顺畅的组合，而不仅是把已有资产换成链上形式。参考：<a href="https://www.bis.org/speeches/sp240209.pdf" target="_blank" rel="noopener noreferrer">BIS 关于现实资产代币化的资料</a></p>
<h2>5. 趋势三：智能钱包降低使用门槛</h2>
<p>传统钱包要求用户保管助记词、准备网络手续费，并逐笔理解授权和交易。这是大规模采用的重要障碍。</p>
<p>账户抽象和智能合约钱包尝试提供：</p>
<ul>
<li>通过可信联系人或备用设备恢复账户。</li>
<li>为大额转账设置多重确认和等待时间。</li>
<li>一次完成授权与兑换等多步操作。</li>
<li>由应用赞助手续费，或使用其他代币支付费用。</li>
<li>为不同应用设置额度和有效期。</li>
</ul>
<h3>案例：带限额的家庭钱包</h3>
<p>主账户可以设置三条规则：日常转账每天不超过一定额度；大额交易需要两台设备共同批准；主密钥丢失后，由三个恢复联系人中的两人帮助更换密钥。</p>
<p>这比“一个助记词控制全部资产”更接近日常金融账户体验。但智能钱包代码更复杂，也带来合约漏洞、恢复人合谋和服务依赖等新风险。</p>
<p>以太坊官方路线图将账户抽象视为改善钱包安全和体验的重要方向。参考：<a href="https://ethereum.org/roadmap/account-abstraction" target="_blank" rel="noopener noreferrer">Ethereum.org 账户抽象介绍</a></p>
<h2>6. 趋势四：扩容之后，重点转向互操作体验</h2>
<p>Layer 2 和 Rollup 把大量交易集中处理，再把数据或结果提交到底层网络，从而降低成本并提高吞吐量。</p>
<p>扩容解决了一部分费用问题，也带来了新的碎片化：同一种资产分布在不同网络，用户需要跨链，应用需要处理不同确认规则和流动性。</p>
<p>未来体验是否改善，取决于钱包和应用能否把这些复杂性隐藏起来，同时保持安全边界清晰。</p>
<h3>案例：一次跨网络购买</h3>
<p>用户的稳定币在网络 A，想购买的 NFT 位于网络 B。理想体验是钱包自动选择安全路径、展示总费用并完成交易；现实中可能需要跨链桥、兑换手续费代币，再调用目标合约。</p>
<p>便利性越高，越要明确谁在执行跨链、资金经过哪些合约，以及失败后如何退款。</p>
<p>参考：<a href="https://ethereum.org/roadmap/scaling" target="_blank" rel="noopener noreferrer">Ethereum.org 扩容路线</a></p>
<h2>7. 趋势五：可验证身份与隐私证明</h2>
<p>今天的互联网身份通常被平台控制。用户更换平台时，很难带走账号、信誉和凭证。</p>
<p>DID 与可验证凭证提供另一种思路：机构签发可被密码学验证的凭证，用户自行保存，并在需要时向验证方出示。</p>
<h3>案例：只证明“年满要求”</h3>
<p>传统做法可能要求上传完整身份证。可验证凭证系统可以尝试只证明“年龄满足条件”，而不披露证件号码和详细生日。</p>
<h3>案例：跨平台学历凭证</h3>
<p>学校签发数字学历凭证，毕业生把凭证保存在自己的数字钱包中。申请工作时，雇主可以验证签发机构和凭证状态，而不必每次联系学校人工核实。</p>
<p>W3C 已发布 DID 标准，并持续推进可验证凭证相关规范。标准解决互操作问题，但现实采用仍依赖可信签发者、隐私设计和法律认可。参考：<a href="https://www.w3.org/press-releases/2022/did-rec/" target="_blank" rel="noopener noreferrer">W3C DID 介绍</a></p>
<h2>8. 趋势六：开放协议与链上组织</h2>
<p>智能合约可以把支付、所有权和组织规则变成开放接口，让不同开发者在同一套协议上构建产品。</p>
<p>这可能形成类似互联网开放协议的创新模式：</p>
<ul>
<li>开发者无需从零建立支付和资产系统。</li>
<li>用户可以在兼容应用间携带账户和资产。</li>
<li>社区通过公开提案资助工具和内容。</li>
<li>应用可以组合交易、借贷、身份和治理能力。</li>
</ul>
<p>但“开放”不代表没有控制者。前端入口、管理员密钥、数据服务和品牌仍可能集中。判断开放程度，应看其他团队是否真的能够独立读取状态、运行客户端并构建替代产品。</p>
<h2>9. 哪些方向仍需要保持谨慎</h2>
<h3>只依赖代币激励的产品</h3>
<p>补贴可以帮助产品冷启动，却不能替代长期需求。如果用户只在奖励存在时使用，激励减少后活跃度可能快速下降。</p>
<h3>没有明确法律权利的资产上链</h3>
<p>链上记录透明，不等于现实资产归属能够执行。RWA 项目必须解释法律主体、托管、违约处理和赎回方式。</p>
<h3>过度承诺的跨链互操作</h3>
<p>跨链桥扩大了资产使用范围，也增加了合约、验证者和消息传递风险。所谓“一键跨链”只是隐藏操作步骤，不会消除底层风险。</p>
<h3>把用户数量等同于真实采用</h3>
<p>一个人可以创建多个钱包，空投和机器人也会制造大量地址。衡量产品时，应结合留存、手续费收入、真实支付和重复使用情况。</p>
<h3>用价格证明技术价值</h3>
<p>代币上涨可能来自流动性和市场情绪，不能证明协议具有长期需求；价格下跌也不一定意味着技术完全无用。产品价值与资产价格需要分别分析。</p>
<h2>10. 普通人有哪些参与路径</h2>
<p>Web3 的机会不只等于购买代币。投入时间学习、参与开源项目或提供专业服务，也是一种参与。</p>
<h3>技术与产品</h3>
<ul>
<li>智能合约和协议开发。</li>
<li>钱包、前端和移动端体验。</li>
<li>数据索引、节点与开发者工具。</li>
<li>安全审计、测试和事件响应。</li>
<li>产品研究和用户体验设计。</li>
</ul>
<h3>运营与内容</h3>
<ul>
<li>技术文档与开发者教育。</li>
<li>社区运营和治理协调。</li>
<li>多语言本地化。</li>
<li>用户研究和客户支持。</li>
<li>品牌、设计与内容传播。</li>
</ul>
<h3>金融、法律与数据</h3>
<ul>
<li>链上数据分析和风险监控。</li>
<li>稳定币支付与结算运营。</li>
<li>会计、税务和合规研究。</li>
<li>协议风险、经济模型与治理分析。</li>
</ul>
<h3>一个 Web2 产品经理转型案例</h3>
<p>产品经理不必先学会编写智能合约。可以选择一个钱包完成转账、兑换和授权，记录每一步困惑，再研究交易生命周期和安全边界，最后为开源项目提交产品改进建议。</p>
<p>行业能力建立在原有职业能力之上，而不是把过去经验全部清零。</p>
<h2>11. 建立自己的 Web3 学习项目</h2>
<p>相比只看资讯，完成一个可验证作品更有价值。</p>
<p>可以选择以下项目：</p>
<ul>
<li>建立一个测试网钱包并记录完整安全流程。</li>
<li>分析一个协议的资金路径、权限与风险。</li>
<li>制作一份稳定币转账网络对比表。</li>
<li>阅读并解释一项 DAO 提案。</li>
<li>为开源项目修正文档或翻译内容。</li>
<li>使用公开链上数据制作简单看板。</li>
<li>写一篇某个安全事件的复盘报告。</li>
</ul>
<p>作品能够证明你会查资料、理解机制并清楚表达，比简历里写“熟悉 Web3”更有说服力。</p>
<h2>12. 如何判断项目是否值得长期关注</h2>
<p>可以使用下面的研究框架：</p>
<p>| 维度 | 核心问题 |<br>
|</p>
]]></content:encoded>
    </item>
    <item>
      <title>十二、资产安全与风险管理：从防骗到交易纪律</title>
      <link>https://www.linkjm.com/crypto/chapter001/010.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/010.html</guid>
      <source url="https://www.linkjm.com/rss.xml">十二、资产安全与风险管理：从防骗到交易纪律</source>
      <description>建立覆盖设备、账户、钱包、签名和资产配置的安全体系，理解钓鱼、恶意授权、平台风险与交易风险，并掌握事故后的应急步骤。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>Web3 把更多控制权交给用户，也把更多责任交给用户。银行账户出现异常时，可以联系客服冻结；链上交易一旦确认，通常没有一个中心机构能够直接撤销。</p>
<p>资产安全不是记住“不要泄露助记词”这一句话，而是建立多层防护，让一次点击错误、一个设备故障或一个平台问题不会影响全部资产。</p>
<p>本篇讨论安全与风险管理，不构成任何投资建议。</p>
<h2>1. 安全是一套系统，不是某个工具</h2>
<p>硬件钱包不能阻止用户确认恶意交易，双重验证也不能保护已经泄露的助记词。每一种工具只解决一部分问题。</p>
<figure><img src="/assets/images/web3/chapter001/security-layers.png" alt="个人数字资产的多层安全体系" tabindex="0" loading="lazy"><figcaption>个人数字资产的多层安全体系</figcaption></figure>
<p>完整的个人防线通常包括：</p>
<ul>
<li><strong>设备安全</strong>：操作系统、应用来源、浏览器扩展和网络环境。</li>
<li><strong>账户安全</strong>：邮箱、交易平台、云盘和社交账号的密码与多重验证。</li>
<li><strong>钱包安全</strong>：私钥、助记词、备份和恢复方式。</li>
<li><strong>交易核验</strong>：域名、网络、合约、金额、授权和签名内容。</li>
<li><strong>资产分层</strong>：不同钱包、平台与用途之间的风险隔离。</li>
</ul>
<p>安全的目标不是保证永远不出错，而是降低出错概率，并限制单次错误的最大影响。</p>
<h2>2. 先建立自己的威胁模型</h2>
<p>威胁模型就是提前思考：我的资产可能通过什么方式损失？</p>
<p>常见风险可以分为五类：</p>
<p>| 风险类型 | 典型场景 | 主要防护 |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/security-layers.png" type="image/png"/>
    </item>
    <item>
      <title>二、比特币与以太坊：数字货币与可编程区块链</title>
      <link>https://www.linkjm.com/crypto/chapter001/011.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/011.html</guid>
      <source url="https://www.linkjm.com/rss.xml">二、比特币与以太坊：数字货币与可编程区块链</source>
      <description>从比特币的点对点现金与数字稀缺性，到以太坊的智能合约和可编程状态，理解两大区块链的目标、运行方式与核心差异。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>比特币和以太坊经常同时出现在行情页面上，但它们并不是两个功能相同的“币”。比特币首先要解决无需可信中介的价值转移与数字稀缺问题；以太坊则进一步把区块链扩展为能够运行程序的公共计算平台。</p>
<p>理解二者，是理解整个加密行业最重要的一步。</p>
<figure><img src="/assets/images/web3/chapter001/bitcoin-vs-ethereum.png" alt="比特币与以太坊的定位差异" tabindex="0" loading="lazy"><figcaption>比特币与以太坊的定位差异</figcaption></figure>
<h2>1. 比特币为什么会出现</h2>
<p>传统电子支付依赖银行、支付机构和清算网络维护账本。系统运转高效，但用户必须相信中介正确记账、不会随意冻结交易，并能持续提供服务。</p>
<p>比特币提出另一条路线：让分布在全球的节点共同维护一份公开账本，用密码学签名证明资产所有权，用工作量证明决定交易历史，从而在没有中央记账人的情况下防止同一笔钱被重复花费。</p>
<p>比特币白皮书将其描述为点对点电子现金系统。它最重要的创新并不是创造一串数字，而是组合已有技术，建立一套公开、可验证且难以篡改的价值转移规则。参考：<a href="https://bitcoin.org/en/bitcoin-paper" target="_blank" rel="noopener noreferrer">Bitcoin 白皮书</a></p>
<h2>2. UTXO：比特币怎样记录余额</h2>
<p>比特币没有直接在账本里写“某地址拥有 1 BTC”，而是记录尚未花费的交易输出，即 UTXO。</p>
<p>可以把 UTXO 理解成不同面额的数字钞票：</p>
<ul>
<li>收到一笔比特币，会产生一个可以被你花费的输出。</li>
<li>发起付款时，钱包选择一个或多个输出作为输入。</li>
<li>输入总额大于付款金额时，剩余部分会作为找零形成新的输出。</li>
<li>已经作为输入使用的 UTXO 不能再次使用。</li>
</ul>
<p>例如，小林拥有 0.3 BTC 和 0.8 BTC 两个 UTXO。支付 0.9 BTC 时，钱包可能同时花费二者，并创建“收款人 0.9 BTC”和“找零减去手续费”两个新输出。</p>
<h2>3. 工作量证明如何形成共识</h2>
<p>比特币矿工把待确认交易组合成候选区块，并投入计算寻找符合网络难度要求的哈希结果。最先找到有效结果的矿工广播区块，其他节点独立验证其中每笔交易和区块规则。</p>
<p>工作量证明承担三个作用：</p>
<ol>
<li>让创建新区块需要付出现实成本。</li>
<li>让节点能够依据累计工作量选择交易历史。</li>
<li>通过区块奖励和手续费激励参与者保护网络。</li>
</ol>
<p>矿工不能随意修改供应上限或创造不符合规则的交易。完整节点会验证区块，拒绝违反共识规则的数据。Bitcoin Core 的说明也强调，独立验证能够阻止矿工让节点接受违反供应限制的区块。参考：<a href="https://bitcoin.org/en/bitcoin-core/features/validation" target="_blank" rel="noopener noreferrer">Bitcoin Core 验证机制</a></p>
<h2>4. 供应上限、减半与安全预算</h2>
<p>比特币协议将最终供应量限制在约 2,100 万枚。新区块奖励会按预定规则周期性减半，因此新增供应速度逐渐下降。</p>
<p>这不等于价格必然上涨。价格仍然取决于需求、流动性、监管环境和市场预期。减半直接改变的是新增供应节奏，不是市场必须接受的价格。</p>
<p>随着区块补贴下降，交易手续费在网络安全激励中的相对重要性可能提升。这也是研究比特币长期运行时必须关注的问题。</p>
<h2>5. 以太坊为什么不只是另一种货币</h2>
<p>比特币的脚本能力有意保持相对克制。以太坊则希望让开发者在区块链上部署通用程序，也就是智能合约。</p>
<p>以太坊可以被理解为一台由全球节点共同验证的状态机：</p>
<ul>
<li>账户保存余额、代码和状态。</li>
<li>用户发送带签名的交易。</li>
<li>EVM 按统一规则执行合约代码。</li>
<li>每个节点验证执行结果。</li>
<li>合法结果更新为新的全网状态。</li>
</ul>
<p><a href="http://Ethereum.org" target="_blank" rel="noopener noreferrer">Ethereum.org</a> 将 EVM 描述为所有以太坊节点一致执行代码的去中心化虚拟环境。参考：<a href="https://ethereum.org/developers/docs/evm/" target="_blank" rel="noopener noreferrer">Ethereum Virtual Machine</a></p>
<h2>6. 智能合约能做什么</h2>
<p>智能合约是部署在链上的程序。满足调用条件后，它按照代码执行，不需要传统服务器管理员逐笔批准。</p>
<p>常见应用包括：</p>
<ul>
<li>去中心化交易和借贷。</li>
<li>稳定币与代币发行。</li>
<li>NFT、游戏资产和会员凭证。</li>
<li>DAO 投票与链上金库。</li>
<li>保险、预测市场和现实资产登记。</li>
</ul>
<p>“自动执行”不代表“不会出错”。漏洞、权限设计、预言机数据和治理决策都可能影响结果。代码公开也不意味着普通用户已经完成审计。</p>
<h2>7. ETH 在系统中的作用</h2>
<p>ETH 不只是交易标的，它在以太坊网络中承担多项功能：</p>
<ul>
<li>支付交易和智能合约执行所需的 Gas。</li>
<li>作为 DeFi 中的抵押品和基础资产。</li>
<li>由验证者质押以参与共识并承担惩罚风险。</li>
<li>为链上应用提供统一的原生结算资产。</li>
</ul>
<p>以太坊目前采用权益证明。验证者质押 ETH、提出或证明区块，诚实参与获得奖励，严重违反规则可能遭受罚没。</p>
<h2>8. BTC 与 ETH 的关键差异</h2>
<p>| 维度 | 比特币 | 以太坊 |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/bitcoin-vs-ethereum.png" type="image/png"/>
    </item>
    <item>
      <title>五、链上交易全过程：签名、Gas 与区块确认</title>
      <link>https://www.linkjm.com/crypto/chapter001/012.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/012.html</guid>
      <source url="https://www.linkjm.com/rss.xml">五、链上交易全过程：签名、Gas 与区块确认</source>
      <description>以一次以太坊交易为主线，理解钱包构造、私钥签名、RPC 广播、交易池、验证者打包、Gas 计费和区块确认。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>钱包里的“确认”按钮看似简单，背后却涉及签名、节点传播、交易排序、合约执行和全网状态更新。理解这个过程，能帮助我们判断 Pending、失败扣费、Nonce 冲突和授权交易等常见问题。</p>
<figure><img src="/assets/images/web3/chapter001/transaction-lifecycle.png" alt="链上交易从签名到确认的完整流程" tabindex="0" loading="lazy"><figcaption>链上交易从签名到确认的完整流程</figcaption></figure>
<h2>1. 交易究竟是什么</h2>
<p>交易是账户发给区块链的一条带签名指令。它可以是转账，也可以是调用合约、授权代币或部署程序。</p>
<p>以太坊交易通常包含：</p>
<p>| 字段 | 作用 |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/transaction-lifecycle.png" type="image/png"/>
    </item>
    <item>
      <title>六、代币经济学：供应、解锁与价值捕获</title>
      <link>https://www.linkjm.com/crypto/chapter001/013.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/013.html</guid>
      <source url="https://www.linkjm.com/rss.xml">六、代币经济学：供应、解锁与价值捕获</source>
      <description>从供应量、市值、FDV、分配、解锁、通胀、销毁和价值捕获出发，建立研究代币的基础框架并识别常见估值陷阱。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>一个项目有真实用户，不代表它的代币一定有价值；一个代币供应稀缺，也不代表市场一定产生需求。代币经济学研究的是供应如何产生、分配给谁、怎样进入流通，以及持有代币能获得什么权利或用途。</p>
<p>本文用于建立研究框架，不构成任何投资建议。</p>
<figure><img src="/assets/images/web3/chapter001/tokenomics-system.png" alt="代币供应、解锁、需求与销毁系统" tabindex="0" loading="lazy"><figcaption>代币供应、解锁、需求与销毁系统</figcaption></figure>
<h2>1. Coin 与 Token 的区别</h2>
<p>Coin 通常是某条区块链的原生资产，例如 BTC 和 ETH，用于支付网络费用、奖励维护者或保障共识。</p>
<p>Token 通常由智能合约发行，依赖已有公链运行，例如以太坊上的 ERC-20 代币。它可以表示治理权、协议权益、稳定价值、游戏资源或现实资产凭证。</p>
<p>这种划分不是价值高低判断。关键问题是：代币在系统中是否真正必要，以及持有者具体拥有什么权利。</p>
<h2>2. 先分清四种供应量</h2>
<p>| 指标 | 含义 |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/tokenomics-system.png" type="image/png"/>
    </item>
    <item>
      <title>十三、Web3 赚钱路径：从资产收益到技能变现</title>
      <link>https://www.linkjm.com/crypto/chapter001/014.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/014.html</guid>
      <source url="https://www.linkjm.com/rss.xml">十三、Web3 赚钱路径：从资产收益到技能变现</source>
      <description>系统梳理买币囤币、空投、现货与合约交易、DeFi、质押、节点、内容创作、社群运营和专业服务等 Web3 收益路径，并比较其门槛、风险与可持续性。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>Web3 中确实存在许多获得收入的方式，但“赚钱路径”不等于“稳赚方法”。不同路径的收益来源完全不同：有些依靠资产上涨，有些来自承担协议风险，有些是项目补贴，还有一些本质上是劳动、专业服务或创业收入。</p>
<p>在选择路径前，先回答三个问题：收益由谁支付、为什么愿意支付、最坏情况下会损失什么。</p>
<p>本文用于知识学习和风险教育，不构成投资、交易或法律建议。</p>
<figure><img src="/assets/images/web3/chapter001/web3-income-paths.png" alt="Web3 中常见的收入与赚钱路径" tabindex="0" loading="lazy"><figcaption>Web3 中常见的收入与赚钱路径</figcaption></figure>
<h2>1. 先分清四种收益来源</h2>
<h3>资产收益</h3>
<p>通过低价买入、高价卖出获得价差，例如长期持有 BTC、ETH 或其他代币。收益来自未来购买者愿意支付更高价格，并不产生确定现金流。</p>
<h3>协议收益</h3>
<p>通过质押、借贷、提供流动性或运行节点获得奖励。收益可能来自用户手续费，也可能来自代币增发和短期补贴。</p>
<h3>劳动收益</h3>
<p>通过研究、开发、设计、翻译、内容创作、社群管理和商务拓展获得报酬。这类收入主要依赖个人能力和交付结果。</p>
<h3>经营收益</h3>
<p>通过运营媒体、工具、社群、咨询业务或产品获得订阅费、服务费、广告费和销售收入，需要面对获客、成本、合规与持续经营风险。</p>
<p>理解收益来源，可以避免把“领取项目补贴”误认为稳定利润，也能看清高收益背后究竟承担了什么风险。</p>
<h2>2. 买币囤币：用时间换取行业增长</h2>
<p>买币囤币是最容易理解的路径：研究资产后分批买入，并在较长周期内持有。</p>
<h3>收益从哪里来</h3>
<ul>
<li>网络使用和市场需求增长。</li>
<li>资产稀缺性得到更多人认可。</li>
<li>行业周期、流动性和风险偏好推动估值提高。</li>
</ul>
<h3>适合什么人</h3>
<ul>
<li>有稳定现金流，不依赖短期变现。</li>
<li>能承受较大浮亏和多年波动。</li>
<li>愿意研究资产、托管方式与退出条件。</li>
</ul>
<h3>常见方法</h3>
<p><strong>一次性买入</strong>适合已经完成研究、能够接受入场时点风险的人。</p>
<p><strong>定期定额</strong>可以降低择时压力，但不会消除资产长期下跌或归零风险。</p>
<p><strong>核心与观察仓</strong>是把多数资金放在研究较充分、流动性较好的资产中，只用少量资金验证高风险判断。</p>
<h3>主要风险</h3>
<ul>
<li>买入没有真实需求或价值捕获的代币。</li>
<li>高位追涨后无法承受回撤。</li>
<li>过度集中于单一资产、平台或叙事。</li>
<li>私钥泄露、平台破产和监管限制。</li>
<li>没有退出计划，把长期持有变成拒绝止损。</li>
</ul>
<p>CFTC 提醒，数字资产价格由市场供需驱动，可能出现剧烈波动，平台还存在操纵、网络安全和客户保护不足等风险。参考：<a href="https://www.cftc.gov/LearnAndProtect/AdvisoriesAndArticles/understand_risks_of_virtual_currency.html" target="_blank" rel="noopener noreferrer">CFTC 虚拟货币交易风险提示</a></p>
<h2>3. 现货波段：用判断和纪律获取价差</h2>
<p>现货交易不使用借贷杠杆，交易者通过趋势、区间或事件变化买卖资产。</p>
<p>一次完整交易至少需要写清：</p>
<div class="language-text" data-highlighter="shiki" data-ext="text" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-text"><span class="line"><span>交易理由 + 入场条件 + 仓位大小 + 失效条件 + 退出计划</span></span></code></pre>
</div><p>例如，不能只写“突破就买”，而应说明观察周期、有效突破标准、允许损失、分批方式，以及判断错误时如何退出。</p>
<p>现货不会因为短期波动被平台强制平仓，但资产仍可能大幅下跌。频繁交易还会产生手续费、滑点、税务记录和心理消耗。</p>
<h2>4. 合约交易：收益与损失都会被放大</h2>
<p>永续合约、期货和期权允许交易者做多、做空或使用杠杆。它们原本可以用于套期保值，但也经常被当作高风险投机工具。</p>
<h3>杠杆是怎样放大结果的</h3>
<p>假设使用 1,000 USDT 保证金建立 10,000 USDT 的仓位：</p>
<ul>
<li>标的上涨 3%，仓位浮盈约 300 USDT，未计费用。</li>
<li>标的下跌 3%，仓位浮亏约 300 USDT。</li>
<li>当亏损接近平台维持保证金要求时，仓位可能被强制平仓。</li>
</ul>
<p>杠杆并没有提高判断准确率，只是让错误更快、更昂贵。</p>
<h3>还需要承担哪些成本</h3>
<ul>
<li>开仓与平仓手续费。</li>
<li>买卖价差和滑点。</li>
<li>永续合约资金费率。</li>
<li>剧烈波动时的强平与穿仓风险。</li>
<li>平台宕机、标记价格和对手方风险。</li>
</ul>
<p>CFTC 明确提醒，保证金和杠杆会放大现货价格波动的影响，投机性期货交易属于高风险行为。参考：<a href="https://www.cftc.gov/LearnAndProtect/AdvisoriesAndArticles/understand_risks_of_virtual_currency.html" target="_blank" rel="noopener noreferrer">CFTC 杠杆风险说明</a></p>
<h3>新手应遵守的底线</h3>
<ul>
<li>在稳定盈利能力得到长期验证前，不把高杠杆当作主要路径。</li>
<li>单笔最大损失必须事先确定，不能通过不断补保证金回避错误。</li>
<li>不借生活费、应急资金或负债资金交易。</li>
<li>不相信带单、喊单、保本套利和自动盈利机器人。</li>
</ul>
<h2>5. 撸空投：用时间、资金与链上行为换取不确定激励</h2>
<p>空投通常用于奖励早期用户、分配治理权或推广网络。参与者通过测试产品、跨链、交易、提供流动性、投票或完成社区任务，争取未来可能发放的代币。</p>
<h3>收益从哪里来</h3>
<p>主要来自项目预留的社区代币，而不是链上操作本身自动创造利润。</p>
<h3>需要投入什么</h3>
<ul>
<li>研究项目和规则的时间。</li>
<li>Gas、跨链和交易成本。</li>
<li>承担合约、桥、资产和钱包安全风险。</li>
<li>保存记录并持续使用产品。</li>
</ul>
<h3>一个正确的成本表</h3>
<p>| 项目 | 应记录内容 |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://www.linkjm.com/assets/images/web3/chapter001/web3-income-paths.png" type="image/png"/>
    </item>
    <item>
      <title>Web3 前言介绍</title>
      <link>https://www.linkjm.com/crypto/chapter001/introduction.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/introduction.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Web3 前言介绍</source>
      <description>开始学习 Web3 前，先了解这套手册的目标、内容结构、学习方法与风险边界。</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<p>Web3 经常和区块链、加密资产、去中心化应用等概念一起出现，但它并不是某一种产品或单一技术。更准确地说，Web3 是围绕开放网络、数字资产和用户自主权形成的一组技术与实践。</p>
<p>这个领域既有真实运行的基础设施，也有仍在探索的产品；既包含技术创新，也伴随着投机、欺诈和安全风险。因此，学习 Web3 的第一步不是寻找“下一个机会”，而是建立一套能够分辨概念、事实和宣传的知识框架。</p>
<h2>为什么需要系统学习</h2>
<p>刚接触 Web3 时，你可能会遇到大量陌生词汇：公链、钱包、Gas、智能合约、DeFi、NFT、DAO、Layer 2……如果只是零散地记住这些词，很容易知道得不少，却无法解释它们之间的关系。</p>
<p>系统学习主要解决四个问题：</p>
<ol>
<li><strong>理解系统如何运作</strong>：一笔交易如何发出、验证和记录，钱包与区块链分别承担什么角色。</li>
<li><strong>看懂不同应用</strong>：DeFi、稳定币、NFT 和 DAO 解决什么问题，又引入了哪些新风险。</li>
<li><strong>建立判断能力</strong>：面对新项目时，能够检查它的技术、需求、经济模型、团队和安全边界。</li>
<li><strong>保护个人资产</strong>：理解私钥、授权、钓鱼、合约漏洞和平台风险，避免因基础错误造成损失。</li>
</ol>
<h2>这一章会学到什么</h2>
<p>本章从基础概念开始，逐步建立 Web3 的整体地图：</p>
<ul>
<li>Web3 与传统互联网有什么不同；</li>
<li>区块链、公链和智能合约如何协作；</li>
<li>身份、钱包和数字资产之间是什么关系；</li>
<li>DeFi、稳定币、NFT、GameFi 与 DAO 的基本逻辑；</li>
<li>不同地区为什么采用不同的监管方式；</li>
<li>普通人可以通过哪些方向参与这个行业；</li>
<li>如何识别资产安全、平台和市场风险。</li>
</ul>
<p>这些内容的目标不是让你记住所有名词，而是帮助你回答三个问题：它是什么、为什么存在、使用它需要承担什么风险。</p>
<h2>推荐的学习方式</h2>
<h3>第一次阅读：建立地图</h3>
<p>按侧边栏顺序阅读，先理解各主题之间的联系。遇到不熟悉的词，可以暂时记录下来，不必在第一次阅读时追求所有细节。</p>
<h3>第二次阅读：动手验证</h3>
<p>结合区块浏览器、官方文档和公开数据验证文中的概念。例如观察一笔链上交易包含哪些信息，或比较不同网络的费用与确认方式。</p>
<h3>第三次阅读：形成自己的笔记</h3>
<p>每学完一个主题，尝试用自己的话回答：</p>
<ul>
<li>它解决了什么问题？</li>
<li>它依赖哪些技术或参与者？</li>
<li>它和传统方案相比有什么变化？</li>
<li>哪些情况下可能失效？</li>
<li>对普通用户最大的风险是什么？</li>
</ul>
<p>如果无法清楚回答，就说明这个概念仍需要继续查证。</p>
<h2>区分技术、产品和资产</h2>
<p>学习过程中要特别注意：认可一项技术，不等于认可所有基于它发行的资产；理解一个产品，也不代表它适合投资。</p>
<p>可以把判断拆成三个层次：</p>
<p>| 层次 | 关注的问题 |<br>
|</p>
]]></content:encoded>
    </item>
    <item>
      <title>目录</title>
      <link>https://www.linkjm.com/crypto/chapter001/list.html</link>
      <guid>https://www.linkjm.com/crypto/chapter001/list.html</guid>
      <source url="https://www.linkjm.com/rss.xml">目录</source>
      <description>目录 第一章：Web3 基础与行业全景，by LinkJM 应用生态 参与边界 总结与展望</description>
      <pubDate>Fri, 25 Sep 2026 11:10:07 GMT</pubDate>
      <content:encoded><![CDATA[
<br>
<blockquote>
<p>第一章：Web3 基础与行业全景，by LinkJM</p>
</blockquote>
<p><a href="/crypto/chapter001/introduction.html" target="_blank">写在前面</a></p>
<p><a href="/crypto/chapter001/000.html" target="_blank">Web3常用名词&amp;术语</a></p>
<p><a href="/crypto/chapter001/001.html" target="_blank">一、Web3 是什么：从互联网演进到用户主权</a></p>
<p><a href="/crypto/chapter001/011.html" target="_blank">二、比特币与以太坊：数字货币与可编程区块链</a></p>
<p><a href="/crypto/chapter001/002.html" target="_blank">三、Web3 技术框架：区块链、公链与扩容</a></p>
<p><a href="/crypto/chapter001/003.html" target="_blank">四、数字身份与钱包：地址、私钥和账户安全</a></p>
<p><a href="/crypto/chapter001/012.html" target="_blank">五、链上交易全过程：签名、Gas 与区块确认</a></p>
<p><a href="/crypto/chapter001/013.html" target="_blank">六、代币经济学：供应、解锁与价值捕获</a></p>
<h2>应用生态</h2>
<p><a href="/crypto/chapter001/004.html" target="_blank">七、DeFi 与稳定币：开放金融的机制与风险</a></p>
<p><a href="/crypto/chapter001/005.html" target="_blank">八、NFT：数字所有权、应用场景与常见风险</a></p>
<p><a href="/crypto/chapter001/006.html" target="_blank">九、GameFi：链上游戏、资产经济与可持续性</a></p>
<p><a href="/crypto/chapter001/007.html" target="_blank">十、DAO：链上协作、治理机制与现实边界</a></p>
<h2>参与边界</h2>
<p><a href="/crypto/chapter001/008.html" target="_blank">十一、Web3 监管：合规边界、常见风险与用户责任</a></p>
<p><a href="/crypto/chapter001/010.html" target="_blank">十二、资产安全与风险管理：从防骗到交易纪律</a></p>
<p><a href="/crypto/chapter001/014.html" target="_blank">十三、Web3 赚钱路径：从资产收益到技能变现</a></p>
<h2>总结与展望</h2>
<p><a href="/crypto/chapter001/009.html" target="_blank">十四、Web3 的未来：真实需求、行业趋势与参与机会</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>使用Bing API提交网站URL</title>
      <link>https://www.linkjm.com/posts/Python/submit-bing.html</link>
      <guid>https://www.linkjm.com/posts/Python/submit-bing.html</guid>
      <source url="https://www.linkjm.com/rss.xml">使用Bing API提交网站URL</source>
      <description>提示 最近在做SEO，因为链接没有做同步，需要清除之前旧站的链接，重新提交，让搜索引擎尽快索引，google search console的已经基本做差不多了 bing最近因为 New Bing 的原因也用的比较多，所以做了一些工作，很方便的是，它可以直接同步GSC的站点数据，不过就只是域数据，URL还是要自己提交 为了尽快索引，只提交sitemap是...</description>
      <category>Python</category>
      <pubDate>Fri, 24 Mar 2023 10:52:27 GMT</pubDate>
      <content:encoded><![CDATA[<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>最近在做SEO，因为链接没有做同步，需要清除之前旧站的链接，重新提交，让搜索引擎尽快索引，google search console的已经基本做差不多了</p>
<p>bing最近因为 <em>New Bing</em> 的原因也用的比较多，所以做了一些工作，很方便的是，它可以直接同步GSC的站点数据，不过就只是域数据，URL还是要自己提交</p>
<p>为了尽快索引，只提交sitemap是不够的，还需要调用API手动提交URL</p>
</div>
<h3>官方示例</h3>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/25889c6c306381f8.png" alt="bing example" tabindex="0" loading="lazy"><figcaption>bing example</figcaption></figure>
<h3>python代码</h3>

<h3>解读</h3>
<p>在上面的脚本中，我们首先指定了Sitemap的URL。然后，我们使用Python中的requests库获取Sitemap的内容，并使用Python中的xml.etree.ElementTree库解析Sitemap中的URL。</p>
<p>接下来，我们使用Bing API提交URL。我们首先指定Bing API的密钥和API URL，并设置请求头。然后，我们将Sitemap中提取的URL列表作为数据，将其作为JSON格式发送到Bing API。最后，我们检查响应的状态码，以确保URL已成功提交。</p>
<p>注意：在使用Bing API提交URL之前，需要先注册Bing Webmaster工具，并获取Bing API密钥。还需要将&quot;<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>&quot;替换为自己的站点URL。</p>
<p>API密钥生成↘️</p>
<ol>
<li>访问<a href="https://www.bing.com/webmasters/" target="_blank" rel="noopener noreferrer">Bing Webmaster Tools</a></li>
<li>右上角设置</li>
</ol>
<p>![API密钥生成](<a href="https://s3.bmp.ovh/imgs/2023/03/24/6fb703876007f6b6.png" target="_blank" rel="noopener noreferrer">https://s3.bmp.ovh/imgs/2023/03/24/6fb703876007f6b6.png</a> =500x)</p>
]]></content:encoded>
      <enclosure url="https://s3.bmp.ovh/imgs/2023/03/24/25889c6c306381f8.png" type="image/png"/>
    </item>
    <item>
      <title>提交URL到搜索引擎（百度、Bing、Google）</title>
      <link>https://www.linkjm.com/posts/Python/submit-url.html</link>
      <guid>https://www.linkjm.com/posts/Python/submit-url.html</guid>
      <source url="https://www.linkjm.com/rss.xml">提交URL到搜索引擎（百度、Bing、Google）</source>
      <description>相关信息 这是一个利用GitHub Actions自动触发的工作流进行解析更新的url并推送到搜索引擎的python脚本,除了之前介绍过的bing api之外，还增加了百度和Google的相关内容 关于GitHub Actions的介绍可以看这里👉GitHub Actions 这个脚本也可以集成到 GitHub Actions，在网站更新后自动执行 ...</description>
      <category>Python</category>
      <pubDate>Mon, 17 Apr 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p>这是一个利用<code>GitHub Actions</code>自动触发的工作流进行解析更新的url并推送到搜索引擎的python脚本,除了之前介绍过的bing api之外，还增加了百度和Google的相关内容</p>
<p>关于<code>GitHub Actions</code>的介绍可以看这里👉<a href="/tutorial/github/github-action">GitHub Actions</a></p>
<p>这个脚本也可以集成到 GitHub Actions，在网站更新后自动执行 URL 提交任务。</p>
</div>
<!-- more -->
<h2>流程图</h2>
]]></content:encoded>
      <enclosure url="https://w.wallhaven.cc/full/ex/wallhaven-ex8j2k.jpg?x-oss-process=image/resize,limit_0,m_fill,w_1366,h_768/quality,Q_92/format,webp" type="image/"/>
    </item>
    <item>
      <title>终端究极美化iTerm2+Pure</title>
      <link>https://www.linkjm.com/posts/Linux/iterm2-pure.html</link>
      <guid>https://www.linkjm.com/posts/Linux/iterm2-pure.html</guid>
      <source url="https://www.linkjm.com/rss.xml">终端究极美化iTerm2+Pure</source>
      <description>提示 之前介绍了ZSH的安装和使用，这次把最终成果介绍一下，就是ZSH + iTerm2 + Pure 前置工作：安装ZSH，请参照之前的文章👉zsh安装</description>
      <category>Linux</category>
      <pubDate>Fri, 24 Mar 2023 16:40:37 GMT</pubDate>
      <content:encoded><![CDATA[<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>之前介绍了ZSH的安装和使用，这次把最终成果介绍一下，就是ZSH + iTerm2 + Pure</p>
<p>前置工作：安装ZSH，请参照之前的文章👉<a href="/posts/Linux/zsh.html" target="_blank">zsh安装</a></p>
</div>
<!-- more -->
<h2>目录</h2>

<h2>先看效果图</h2>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/140a5562a8b9e334.png" alt="界面" tabindex="0" loading="lazy"><figcaption>界面</figcaption></figure>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/810d0c1d2a4d7069.png" alt="命令提示" tabindex="0" loading="lazy"><figcaption>命令提示</figcaption></figure>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/052c26b51ecde528.png" alt="流输出" tabindex="0" loading="lazy"><figcaption>流输出</figcaption></figure>
<h2>安装pure</h2>
<p>👉<a href="https://github.com/sindresorhus/pure" target="_blank" rel="noopener noreferrer">官方文档</a></p>
<p>可以使用 npm 或手动安装。需要 Git 2.15.2+ 和 ZSH 5.2+。已知旧版本的 ZSH 可以工作，但不推荐使用。</p>

<p>如果是手动安装的，将克隆的 repo 的路径添加到 $HOME/.zshrc 中的 $fpath 。</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># .zshrc</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">fpath</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">+=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$HOME</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">/.zsh/pure</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span></code></pre>
</div><p>将以下内容复制到 <code>.zshrc</code>文件中</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">autoload</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -U</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> promptinit</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">promptinit</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">prompt</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> pure</span></span></code></pre>
</div><p>在 .zshrc 中设置 ZSH_THEME=&quot;&quot; 以禁用 oh-my-zsh 主题。</p>
<p>到这里就基本完成了，如果要基于pure做详细定制，请参考文档</p>
<h2>iTerm2</h2>
<p>👉戳这里下载 <a href="https://iterm2.com/downloads.html" target="_blank" rel="noopener noreferrer">官方下载地址</a></p>
<p>效果图我自用的配置文件在这里 👉 <a href="https://pan.baidu.com/s/1cG-gSwoPouYXeZ-P2pjmAg" target="_blank" rel="noopener noreferrer">链接</a>  密码:74aq</p>
<h3>1.主题配置</h3>
<p>访问iTerm2主题网站👉<a href="https://github.com/mbadolato/iTerm2-Color-Schemes" target="_blank" rel="noopener noreferrer">iTerm2-Color-Schemes</a></p>
<p>可以下载zip包并解压到本地，进入解压缩的文件目录，找到schemes文件夹，里边全是主题配置，可以对照github上的效果图来选择</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/2eee92812c17fc48.png" alt="导入" tabindex="0" loading="lazy"><figcaption>导入</figcaption></figure>
<h3>2. 标签页配色</h3>
<p>标签配色默认为黑色，不能与操作页面保持统一</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/9995b0382a76bba8.png" alt="默认" tabindex="0" loading="lazy"><figcaption>默认</figcaption></figure>
<p>打开iTerm2，打开Preferences配置界面，Appearence -&gt; General，将 Theme 改为 Minimal</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/411e917c7fb3d2b9.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<h3>3. 设置 Status bar</h3>
<p>iTerm2 提供了不少的 Status bar，开启后我们可以在终端的最上方非常方便的实时查看本机的一些信息。</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/d93a133dcd5fe00c.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>打开iTerm2，打开Preferences配置界面，Profiles -&gt; session-&gt; 勾选 Status bar enable-&gt; configure Status bar，选择自己想要的展示内容即可。向下托动放入Active Components 中即可,我这里只选了CPU、内存、网络</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/918522902136668f.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<h3>4. 光标选择</h3>
<p>iterm提供了三种光标可供选择：_、|、[]。</p>
<p>打开iTerm2，打开Preferences配置界面，Profiles -&gt; text-&gt; cursor，选择自己想要的光标即可。</p>
<h3>5.配置SSH快速连接</h3>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">#首先在/Users目录下按照如下命令创建sh脚本</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">cd</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> /Users/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">#创建iterm文件夹</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">mkdir</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> iterm</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> </span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">#进入iterm文件夹</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">cd</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> iterm</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">#创建myserver.sh文件</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">touch</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> myserver.sh</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">#编辑myserver.sh文件</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">vi</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> myserver.sh</span></span></code></pre>
</div><p>如果出现没有权限，就命令前面加上sudo</p>
<p>键盘输入i编辑文件，插入以下内容：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">#!/usr/bin/expect</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">set</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> timeout</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 30</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">spawn</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> ssh</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [lindex </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> 0]</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [lindex </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> 1]@[lindex</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> $argv</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> 2]</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">expect</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">        "(yes/no)?"</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        {</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">send</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "yes\n"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">exp_continue}</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">        "password:"</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        {</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">send</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "[lindex </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> 3]\n"}</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">interact</span></span></code></pre>
</div><p>myserver.sh文件中变量解释：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[lindex </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> 0]：端口号</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[lindex </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> 1]：服务器用户名</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[lindex </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> 2]：服务器IP地址</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[lindex </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> 3]：服务器密码</span></span></code></pre>
</div><p>插入完成后键盘esc 然后输入:wq退出，接下来给文件赋权</p>
<p>chmod 777 <a href="http://myserver.sh" target="_blank" rel="noopener noreferrer">myserver.sh</a><br>
打开iTerm2，打开Preferences配置界面，Profiles -&gt; general，左下角点击+号，新建profile，参考下面图片在对应位置输入内容即可。</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/349fdd06e7bc428d.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>Name:根据需求输入，通常选择标识性较强的内容便于区分，例如服务器的IP地址</p>
<p>Command：这里选择login Shell</p>
<p>Send text at start ：填写格式形如A B C D E这样，每一个部分之间用空格隔开，根据自己实际情况填写,下面是对每一部分内容的解释</p>
<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p>A 代表咱们上面写的本机保存sh脚本的路径：/Users/iterm/myserver.sh</p>
<p>B 代表服务器端口号一般远程连接端口为：22</p>
<p>C 代表服务器用户名一般为：root</p>
<p>D 代表服务器IP：公网IP填写</p>
<p>E 代表服务器密码：根据自己实际的服务器密码填写<br>
设置好之后打开iTerm2，点击profiles，点击前面自己新增的连接远程服务器的profile的名字</p>
<p>首次连接需要输入一次服务器密码，之后再连接就免密码登陆了</p>
</div>
<h2>结语</h2>
<p>当然这里只介绍一部分，还有很多高级玩法，需要用到的时候自己去研究一下了</p>
]]></content:encoded>
      <enclosure url="https://s3.bmp.ovh/imgs/2023/03/24/140a5562a8b9e334.png" type="image/png"/>
    </item>
    <item>
      <title>Unix/Linux 扫盲笔记</title>
      <link>https://www.linkjm.com/posts/Linux/unix-linux-note.html</link>
      <guid>https://www.linkjm.com/posts/Linux/unix-linux-note.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Unix/Linux 扫盲笔记</source>
      <description>This document is not completed and will be updated anytime. Catagory Catagory Unix Unix-like Single UNIX Specification Apple iOS XNU Kernel Linux Linux Kernel GNU Project Androi...</description>
      <category>Linux</category>
      <pubDate>Tue, 14 Apr 2015 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>This document is not completed and will be updated anytime.</p>
</blockquote>
<h2>Catagory</h2>
<ul>
<li><a href="#catagory">Catagory</a></li>
<li><a href="#unix">Unix</a></li>
<li><a href="#unix-like">Unix-like</a>
<ul>
<li><a href="#single-unix-specification">Single UNIX Specification</a></li>
<li><a href="#apple-ios">Apple iOS</a></li>
<li><a href="#xnu-kernel">XNU Kernel</a></li>
</ul>
</li>
<li><a href="#linux">Linux</a>
<ul>
<li><a href="#linux-kernel">Linux Kernel</a></li>
<li><a href="#gnu-project">GNU Project</a></li>
<li><a href="#android">Android</a></li>
<li><a href="#android-kernel">Android Kernel</a></li>
<li><a href="#android-rom">Android ROM</a></li>
<li><a href="#chrome-os">Chrome OS</a></li>
<li><a href="#chromium-os">Chromium OS</a></li>
</ul>
</li>
</ul>
]]></content:encoded>
      <enclosure url="http://upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Unix_timeline.en.svg/800px-Unix_timeline.en.svg.png" type="image/png"/>
    </item>
    <item>
      <title>更优雅强大的终端ZSH</title>
      <link>https://www.linkjm.com/posts/Linux/zsh.html</link>
      <guid>https://www.linkjm.com/posts/Linux/zsh.html</guid>
      <source url="https://www.linkjm.com/rss.xml">更优雅强大的终端ZSH</source>
      <description>MacOS 自带的 bash 作为几乎所有 Linux 发行版的默认终端，正常使用时没什么问题的 这里介绍一个更强大的终端神器 目录 背景介绍 在 unix 内核的操作系统中,当然现在衍生出好多分支,linux ,OS X 都算. shell 就算和上面这些系统内核指令打交道的一座桥梁,我们通过键盘输入一种自己容易记忆识别的符号标识(shell 命令)...</description>
      <category>Linux</category>
      <pubDate>Wed, 24 Jul 2019 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>MacOS 自带的 bash 作为几乎所有 Linux 发行版的默认终端，正常使用时没什么问题的</p>
<p>这里介绍一个更强大的终端神器</p>
</blockquote>
<h2>目录</h2>

<h2>背景介绍</h2>
<p>在 unix 内核的操作系统中,当然现在衍生出好多分支,linux ,OS X 都算.</p>
<p>shell 就算和上面这些系统内核指令打交道的一座桥梁,我们通过键盘输入一种自己容易记忆识别的符号标识(shell 命令)</p>
<p>然后 shell 解析这种命令再反馈给内核去执行一系列操作.</p>
<p>zsh 和 shell 有什么关系呢?</p>
<p>其实 zsh 也是一种 shell ,但是并不是我们系统默认的 shell ,unix 衍生系统的默认 shell 都是 bash。</p>
<p>查看已安装 shell<br>
查看 Mac 上已有的 shell,一共有 6 种</p>
]]></content:encoded>
      <enclosure url="https://s2.ax1x.com/2019/11/15/MarNhq.md.png" type="image/png"/>
    </item>
    <item>
      <title>初识Rust</title>
      <link>https://www.linkjm.com/posts/Rust/first-time.html</link>
      <guid>https://www.linkjm.com/posts/Rust/first-time.html</guid>
      <source url="https://www.linkjm.com/rss.xml">初识Rust</source>
      <description>Rust 发展历程 Rust 最早是 Mozilla 雇员 Graydon Hoare 的个人项目。从 2009 年开始，得到了 Mozilla 研究院的资助，2010 年项目对外公布，2010 ～ 2011 年间实现自举。自此以后，Rust 在部分重构 -&amp;gt; 崩溃的边缘反复横跳（历程极其艰辛），终于，在 2015 年 5 月 15 日发布 1....</description>
      <category>Rust</category>
      <pubDate>Thu, 07 Dec 2023 17:14:55 GMT</pubDate>
      <content:encoded><![CDATA[<h2>Rust 发展历程</h2>
<p>Rust 最早是 Mozilla 雇员 Graydon Hoare 的个人项目。从 2009 年开始，得到了 Mozilla 研究院的资助，2010 年项目对外公布，2010 ～ 2011 年间实现自举。自此以后，Rust 在部分重构 -&gt; 崩溃的边缘反复横跳（历程极其艰辛），终于，在 2015 年 5 月 15 日发布 1.0 版。</p>
<p>在紧锣密鼓的开发过程中，Rust 建立了一个强大且活跃的社区，形成一整套完善稳定的项目贡献机制（Rust 能够飞速发展，与这一点密不可分）。Rust 现在由 <a href="https://github.com/rust-lang/rust" target="_blank" rel="noopener noreferrer">Rust 项目开发者社区</a> 维护， <a href="https://foundation.rust-lang.org/" target="_blank" rel="noopener noreferrer">Rust 基金会</a>赞助支持。</p>
<p>大家可能疑惑 Rust 为啥用了这么久才到 1.0 版本？与之相比，Go 语言 2009 年发布，却在 2012 年仅用 3 年就发布了 1.0 版本。</p>
<ul>
<li>首先，Rust 语言特性较为复杂，所以需要全盘考虑的问题非常多；</li>
<li>其次，Rust 当时的参与者太多，七嘴八舌的声音很多，众口难调，而 Rust 开发团队又非常重视社区的意见；</li>
<li>最后，一旦 1.0 快速发布，那绝大部分语言特性就无法再被修改，对于有完美强迫症的 Rust 开发者团队来说，某种程度上的不完美是不可接受的。</li>
</ul>
<p>因此，Rust 语言用了足足 6 年时间，才发布了尽善尽美的 1.0 版本。</p>
<h2><a href="https://course.rs/into-rust.html#%E4%B8%BA%E4%BD%95%E5%8F%88%E6%9D%A5%E4%B8%80%E9%97%A8%E6%96%B0%E8%AF%AD%E8%A8%80" target="_blank" rel="noopener noreferrer">为何又来一门新语言？</a></h2>
<p>简而言之，<strong>因为还缺一门无 GC 且无需手动内存管理、性能高、工程性强、语言级安全性以及能同时得到工程派和学院派认可的语言</strong>，而 Rust 就是这样的语言。你也可以回忆下熟悉的语言，看是不是有另外一门可以同时满足这些需求：)</p>
<p>至于 Rust 最为人诟病的点，那也就一个：学习曲线陡峭。不过当语言生态起来后，这都不算问题。</p>
<h3><a href="https://course.rs/into-rust.html#%E7%BC%93%E8%A7%A3%E5%86%85%E5%8D%B7" target="_blank" rel="noopener noreferrer">缓解内卷</a></h3>
<p>有人说 Rust 作为新语言会增加内卷，其实恰恰相反，Rust 可以缓解内卷。为何不说 C++ 内卷，而说 Java、Python、JS 内卷？不就是后几个相对简单、上手容易嘛？而 Rust 怎么看也是 C++ 级别的上手难度。</p>
<p>其实从我内心不可告人的角度出发，并不希望 Rust 大众化，因为这样可以保饭碗、保薪资，还能拥有行业内的地位。但是从对 Rust 的喜爱角度出发，我还是希望能卷一些。不过，目前来看真的卷不动，现在全世界范围内 Rust 的需求都大于供给，特别是优秀的 Rust 程序员更是难寻。</p>
<p>与 Go 语言相比，成为一名优秀的 Rust 程序员所需的门槛高得多，例如融汇贯通 Rust 语言各种中高级特性、闭着眼睛趟过各种坑、不用回忆无需查找就能立刻写出最合适的包/模块/方法、性能/安全/工程性的权衡选择信手拈来、深层性能优化易如反掌、异步编程小菜一碟，更别说 Rust 之外的操作系统、网络、算法等等相关知识。</p>
<p>所以，Rust 可以缓解内卷，而不是增加内卷。可以说是程序员的福音，不再是被随意替换的螺丝钉。</p>
<h4><a href="https://course.rs/into-rust.html#%E8%BF%90%E8%A1%8C%E6%95%88%E7%8E%87" target="_blank" rel="noopener noreferrer">运行效率</a></h4>
<p>得益于各种零开销抽象、深入到底层的优化潜力、优质的标准库和第三方库实现，Rust 具备非常优秀的性能，和 C、C++ 是 <a href="https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/rust.html" target="_blank" rel="noopener noreferrer">一个级别</a>。</p>
<p>同时 Rust 有一个极大的优点：只要按照正确的方式使用 Rust，无需性能优化，就能有非常优秀的表现，不可谓不惊艳。</p>
<p>现在有不少用 Rust 重写的工具、平台都超过了原来用 C、C++ 实现的版本，将老前辈拍死在沙滩上，俨然成为一种潮流～～</p>
<h4><a href="https://course.rs/into-rust.html#%E5%BC%80%E5%8F%91%E6%95%88%E7%8E%87" target="_blank" rel="noopener noreferrer">开发效率</a></h4>
<p>Rust 的开发效率可以用先抑后扬来形容。在最初上手写项目时，你的开发速度将显著慢于 Go、Java 等语言，不过，一旦开始熟悉标准库、熟悉生命周期和所有权的常用解决方法，开发效率将大幅提升，甚至当形成肌肉记忆后，开发效率将不会慢于这些语言，而且原生就能写出高质量、安全、高效的代码，可以说中高级 Rust 程序员就是高效程序员的代名词。</p>
<h3><a href="https://course.rs/into-rust.html#%E5%BC%80%E6%BA%90" target="_blank" rel="noopener noreferrer">开源</a></h3>
<p>目前 Rust 的主战场是在开源上，Go 的成功也证明了农村包围城市( 开源包围商业 )的可行性。</p>
<ul>
<li>UI 层开发，Rust 的 WASM 发展的如火如荼，隐隐有王者风范，在 JS 的基础设施领域，Rust 也是如鱼得水，例如 <code>swc</code>、 <code>deno</code> 等。同时 <code>nextjs</code> 也是押宝 Rust，可以说 Rust 在前端的成功完全是无心插柳柳成荫。</li>
<li>基础设施层、数据库、搜索引擎、网络设施、云原生等都在出现 Rust 的身影，而且还不少。</li>
<li>系统开发，目前 Linux 已经将 Rust 语言纳入内核，是继 C 语言后第二门支持内核开发的语言，不过刚开始将主要支持驱动开发。</li>
<li>系统工具，现在最流行的就是用 Rust 重写之前 C、C++ 写的一票系统工具，还都获得了挺高的关注和很好的效果，例如 sd, exa, ripgrep, fd, bat 等。</li>
<li>操作系统，正在使用 Rust 开发的操作系统有好几个，其中最有名的可能就是谷歌的 Fuchsia，Rust 在其中扮演非常重要的角色。</li>
<li>区块链，如果 Rust 的份额说第二，应该没人敢稳说自己是第一吧？</li>
</ul>
<p>类似的还有很多，我们就不一一列举。总之，现在有大量的项目正在被 Rust 重写，同时还有海量的项目在等待被重写，这些都是赚取github 星星和认可的好机会。在其它语言杀成一片红海时，Rust 还留了一大片蓝海等待大家的探索！</p>
<h3><a href="https://course.rs/into-rust.html#%E7%9B%B8%E6%AF%94%E5%85%B6%E4%BB%96%E8%AF%AD%E8%A8%80-rust-%E7%9A%84%E4%BC%98%E5%8A%BF" target="_blank" rel="noopener noreferrer">相比其他语言 Rust 的优势</a></h3>
<p>由于篇幅有限，我们这里不会讲述详细的对比，就是简单介绍下 Rust 的优势，并不是说 Rust 优于这些语言，大家轻喷：)</p>
<h4><a href="https://course.rs/into-rust.html#go" target="_blank" rel="noopener noreferrer">Go</a></h4>
<p>Rust 语言表达能力更强，性能更高。同时线程安全方面 Rust 也更强，不容易写出错误的代码。包管理 Rust 也更好，Go 虽然在 1.10 版本后提供了包管理，但是目前还比不上 Rust 。</p>
<h4><a href="https://course.rs/into-rust.html#c" target="_blank" rel="noopener noreferrer">C++</a></h4>
<p>Rust 与 C++ 的性能旗鼓相当，但是在安全性方面 Rust 会更优，特别是使用第三方库时，Rust 的严格要求会让三方库的质量明显高很多。</p>
<p>语言本身的学习，Rust 的前中期学习曲线会更陡峭，但是在实际的项目开发过程中，C++ 会更难，代码也更难以维护。</p>
<h4><a href="https://course.rs/into-rust.html#java" target="_blank" rel="noopener noreferrer">Java</a></h4>
<p>除了极少数纯粹的数字计算性能，Rust 的性能全面领先于 Java 。同时 Rust 占用内存小的多，因此实现同等规模的服务，Rust 所需的硬件成本会显著降低。</p>
<h4><a href="https://course.rs/into-rust.html#python" target="_blank" rel="noopener noreferrer">Python</a></h4>
<p>性能自然是 Rust 完胜，同时 Rust 对运行环境要求较低，这两点差不多就足够抉择了。不过 Python 和 Rust 的彼此适用面其实也不太冲突。</p>
<h3><a href="https://course.rs/into-rust.html#%E4%BD%BF%E7%94%A8%E7%8E%B0%E7%8A%B6" target="_blank" rel="noopener noreferrer">使用现状</a></h3>
<ul>
<li>AWS 从 2017 年开始就用 Rust 实现了无服务器计算平台： AWS Lambda 和 AWS Fargate，并且用 Rust 重写了 Bottlerocket OS 和 AWS Nitro 系统，这两个是弹性计算云 (EC2) 的重要服务</li>
<li>Cloudflare 是 Rust 的重度用户，DNS、无服务计算、网络包监控等基础设施都与 Rust 密不可分</li>
<li>Dropbox 的底层存储服务完全由 Rust 重写，达到了数万 PB 的规模</li>
<li>Google 除了在安卓系统的部分模块中使用 Rust 外，还在它最新的操作系统 Fuchsia 中重度使用 Rust</li>
<li>Facebook 使用 Rust 来增强自己的网页端、移动端和 API 服务的性能，同时还写了 Hack 编程语言的虚拟机</li>
<li>Microsoft 使用 Rust 为 Azure 平台提供一些组件，其中包括 IoT 的核心服务</li>
<li>GitHub 和 <a href="http://npmjs.com" target="_blank" rel="noopener noreferrer">npmjs.com</a>，使用 Rust 提供高达每天 13 亿次的 npm 包下载</li>
<li>Rust 目前已经成为全世界区块链平台的首选开发语言</li>
<li>TiDB，国内最有名的开源分布式数据库</li>
</ul>
<p>尤其值得一提的是，AWS 实际上在押宝 Rust，内部对 Rust 的使用已经上升到头等公民 <strong>first-class</strong> 的地位。</p>
<h2><a href="https://course.rs/into-rust.html#rust-%E8%AF%AD%E8%A8%80%E7%89%88%E6%9C%AC%E6%9B%B4%E6%96%B0" target="_blank" rel="noopener noreferrer">Rust 语言版本更新</a></h2>
<p>与其它语言相比，Rust 的更新迭代较为频繁（得益于精心设计过的发布流程以及 Rust 语言开发者团队的严格管理）：</p>
<ul>
<li>每 6 周发布一个迭代版本</li>
<li>2-3 年发布一个新的大版本，例如 Rust 2018 edition，Rust 2021 edition</li>
</ul>
<p>好处在于，可以满足不同的用户群体的需求：</p>
<ul>
<li>对于活跃的 Rust 用户，他们总是能很快获取到新的语言内容，毕竟，尝鲜是技术爱好者的共同特点：)</li>
<li>对于一般的用户，edition 大版本的发布会告诉他们：Rust 语言相比上次大版本发布，有了重大的改进，值得一看</li>
<li>对于 Rust 语言开发者，可以让他们的工作成果更快的被世人所知，不必锦衣夜行</li>
</ul>
<h2>总结</h2>
<p>连续 6 年最受欢迎的语言当然不是浪得虚名。 无 GC、效率高、工程性强、强安全性以及能同时得到工程派和学院派认可，这些令 Rust 拥有了自己的特色和生存空间。社区的友善，生态的快速发展，大公司的重仓跟进，一切的一切都在说明 Rust 的璀璨未来。</p>
<p>当然，语言毕竟只是工具，我们不能神话它，但是可以给它一个机会，也许，你最终能收获自己的真爱 ：)</p>
<p>相信大家听了这么多 Rust 的优点，已经迫不及待想要开始学习旅程，那么容我引用一句 CS（Counter-Strike：反恐精英） 的经典台词：Ok, Let's Rust.</p>
<blockquote>
<p>引自：<a href="https://course.rs/into-rust.html" target="_blank" rel="noopener noreferrer">Rust语言圣经(Rust Course)</a></p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>前端-Q&amp;A</title>
      <link>https://www.linkjm.com/posts/Web/Q_A.html</link>
      <guid>https://www.linkjm.com/posts/Web/Q_A.html</guid>
      <source url="https://www.linkjm.com/rss.xml">前端-Q&amp;A</source>
      <description>浏览器是如何渲染页面的？ 当浏览器的网络线程收到 HTML 文档后，会产生一个渲染任务，并将其传递给渲染主线程的消息队列。 在事件循环机制的作用下，渲染主线程取出消息队列中的渲染任务，开启渲染流程。</description>
      <category>Web</category>
      <pubDate>Thu, 06 Apr 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[
<h2>浏览器是如何渲染页面的？</h2>
<p>当浏览器的网络线程收到 HTML 文档后，会产生一个渲染任务，并将其传递给渲染主线程的消息队列。</p>
<p>在事件循环机制的作用下，渲染主线程取出消息队列中的渲染任务，开启渲染流程。</p>
<!-- more -->
]]></content:encoded>
    </item>
    <item>
      <title>vuepress-plugin-meting2</title>
      <link>https://www.linkjm.com/tutorial/OSS/meting2.html</link>
      <guid>https://www.linkjm.com/tutorial/OSS/meting2.html</guid>
      <source url="https://www.linkjm.com/rss.xml">vuepress-plugin-meting2</source>
      <description>支持vuepress2.x的音乐播放器</description>
      <category>教程</category>
      <pubDate>Wed, 11 Oct 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<h2>前言</h2>
<p>嘿~<br>
:cakecake: 播放器有了，撒花✿✿ヽ(°▽°)ノ✿:tadatada:</p>
<p align="center">
   <a href="https://www.npmjs.com/package/vuepress-plugin-meting2" target="_blank"><img alt="npm" src="https://img.shields.io/npm/v/vuepress-plugin-meting2.svg"></a>&nbsp
   <a href="https://github.com/moefyit/vuepress-plugin-meting2/stargazers" target="_blank"><img alt="GitHub stars" src="https://img.shields.io/github/stars/oragekk/vuepress-plugin-meting2"></a>&nbsp
   <a href="https://www.npmjs.com/package/vuepress-plugin-meting2" target="_blank"><img alt="downloads" src="https://img.shields.io/npm/dt/vuepress-plugin-meting2.svg"></a>&nbsp
   <a href="https://www.npmjs.com/package/vuepress-plugin-meting2" target="_blank"><img alt="downloads" src="https://img.shields.io/npm/dm/vuepress-plugin-meting2.svg"></a>&nbsp
   <a href="https://github.com/oragekk/vuepress-plugin-meting2/blob/main/LICENSE" target="_blank"><img alt="GitHub license" src="https://img.shields.io/github/license/oragekk/vuepress-plugin-meting2"></a>
</p>
<p>文档👉🏻戳这里<a href="https://github.com/OrageKK/vuepress-plugin-meting2" target="_blank" rel="noopener noreferrer">文档</a></p>
<h2>介绍</h2>
<p>借鉴了<a href="https://github.com/u2sb/vuepress-plugin-sbaudio" target="_blank" rel="noopener noreferrer">vuepress-plugin-sbaudio</a>和<a href="https://github.com/moefyit/vuepress-plugin-meting" target="_blank" rel="noopener noreferrer">vuepress-plugin-meting</a> 在此表示感谢 </p>
<p>借鉴MetingJS解析和使用APlayer作为播放组件</p>
<h2>安装很方便</h2>

<h2>使用也很方便</h2>
<div class="language-javascript" data-highlighter="shiki" data-ext="javascript" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-javascript"><span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">plugins</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: [</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    metingPlugin</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">({</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">      metingOptions</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        global</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">true</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 开启关闭全局播放器</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        server</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"tencent"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        api</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"https://api.injahow.cn/meting/?server=:server&#x26;type=:type&#x26;id=:id&#x26;auth=:auth&#x26;r=:r"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        type</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"playlist"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        mid</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"851947617"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }),</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span></code></pre>
</div><h3>可作为组件引入</h3>
<h3>也可作为全局播放器引入</h3>
<p>全局引入目前和看板娘有些重叠，还没有处理，先把开关关了</p>
]]></content:encoded>
    </item>
    <item>
      <title>GitHub Actions 使用介绍</title>
      <link>https://www.linkjm.com/tutorial/github/github-action.html</link>
      <guid>https://www.linkjm.com/tutorial/github/github-action.html</guid>
      <source url="https://www.linkjm.com/rss.xml">GitHub Actions 使用介绍</source>
      <description>GitHub Actions 是什么？ Github Actions 是 Github 官方出的持续集成服务, 挺早之前就推出了。类似的还有如微软的DevOps、GitLab CI、Circle CI、Travis CI等等。大家知道，持续集成由很多操作组成，比如抓取代码、运行测试、登录远程服务器，发布到第三方服务等等。GitHub 把这些操作就称为 ...</description>
      <category>教程</category>
      <pubDate>Fri, 14 Apr 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<!-- more -->

<h2>GitHub Actions 是什么？</h2>
<p>Github Actions 是 Github 官方出的持续集成服务, 挺早之前就推出了。类似的还有如微软的DevOps、GitLab CI、Circle CI、Travis CI等等。大家知道，持续集成由很多操作组成，比如抓取代码、运行测试、登录远程服务器，发布到第三方服务等等。GitHub 把这些操作就称为 actions。</p>
<p>很多操作在不同项目里面是类似的，完全可以共享。GitHub 注意到了这一点，想出了一个很妙的点子，允许开发者把每个操作写成独立的脚本文件，存放到代码仓库，使得其他开发者可以引用。</p>
<p>如果你需要某个 action，不必自己写复杂的脚本，直接引用他人写好的 action 即可，整个持续集成过程，就变成了一个 actions 的组合。这就是 GitHub Actions 最特别的地方。</p>
<h2>什么是CI/CD？</h2>
<p><a href="https://link.zhihu.com/?target=https%3A//www.redhat.com/en/topics/devops/what-is-ci-cd" target="_blank" rel="noopener noreferrer">What is CI/CD? (redhat.com)</a></p>
<p>字面意思就是持续集成Continuous Intergation/持续分发Continuous Delivery持续部署Continuous Deployment，网上有太多同质的解释都太过于晦涩，</p>
<p>提到CI/CD就离不开一个词叫流水线。流水线上每个人的工作是分工明确的，而且工程是有先后顺序，就像造一台车，先造零件，然后组装、喷涂、内饰最后还要测试。软件开发同样如此，有前期的产品设计，UI设计、单元的开发，产品的测试，优化迭代等都由不同的人负责。</p>
]]></content:encoded>
      <enclosure url="https://w.wallhaven.cc/full/j3/wallhaven-j3zvvp.jpg?x-oss-process=image/resize,limit_0,m_fill,w_1366,h_768/quality,Q_92/format,webp" type="image/"/>
    </item>
    <item>
      <title>Git SSH 密钥配置</title>
      <link>https://www.linkjm.com/tutorial/github/ssh-key.html</link>
      <guid>https://www.linkjm.com/tutorial/github/ssh-key.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Git SSH 密钥配置</source>
      <description>在 GitLab 中配置 SSH 密钥 生成 SSH 密钥对 如果您还没有 SSH 密钥对，请首先生成一对密钥。在终端中执行以下命令： 这将生成一个 RSA 类型的密钥对，并将私钥保存在 ~/.ssh/id_rsa，公钥保存在 ~/.ssh/id_rsa.pub。 复制公钥内容 打开公钥文件 ~/.ssh/id_rsa.pub，将其中的内容复制到剪贴板...</description>
      <category>教程</category>
      <pubDate>Thu, 11 Jan 2024 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<h2>在 GitLab 中配置 SSH 密钥</h2>
<h3>生成 SSH 密钥对</h3>
<p>如果您还没有 SSH 密钥对，请首先生成一对密钥。在终端中执行以下命令：</p>
<div class="language-sh" data-highlighter="shiki" data-ext="sh" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-sh"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">ssh-keygen</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -t</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> rsa</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -C</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "your.email@example.com"</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -b</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 4096</span></span></code></pre>
</div><p>这将生成一个 RSA 类型的密钥对，并将私钥保存在 <code>~/.ssh/id_rsa</code>，公钥保存在 <code>~/.ssh/id_rsa.pub</code>。</p>
<h3>复制公钥内容</h3>
<p>打开公钥文件 <strong>~/.ssh/id_rsa.pub</strong>，将其中的内容复制到剪贴板。您可以使用以下命令来完成：</p>
<p><strong>macOS:</strong></p>
<div class="language-sh" data-highlighter="shiki" data-ext="sh" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-sh"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">pbcopy</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> &#x3C; </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">~/.ssh/id_rsa.pub</span></span></code></pre>
</div><p><strong>GNU/Linux (requires the xclip package):</strong></p>
<div class="language-sh" data-highlighter="shiki" data-ext="sh" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-sh"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">xclip</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -sel</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> clip</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> &#x3C; </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">~/.ssh/id_rsa.pub</span></span></code></pre>
</div><p><strong>Windows Command Line:</strong></p>
<div class="language-sh" data-highlighter="shiki" data-ext="sh" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-sh"><span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">type</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> %userprofile%</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">\.</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">ssh</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">\i</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">d_rsa.pub</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">clip</span></span></code></pre>
</div><p><strong>Git Bash on Windows / Windows PowerShell:</strong></p>
<div class="language-sh" data-highlighter="shiki" data-ext="sh" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-sh"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">cat</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> ~/.ssh/id_rsa.pub</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">clip</span></span></code></pre>
</div><h3>将您的SSH密钥添加到GitLab。</h3>
<ol>
<li>
<p>登录到 GitLab</p>
</li>
<li>
<p>打开您的 GitLab 帐户，在右上角点击个人头像，选择设置</p>
</li>
<li>
<p>在左侧导航到 SSH 密钥设置</p>
</li>
<li>
<p>在 &quot;SSH Keys&quot; 页面，粘贴之前复制的公钥内容到 &quot;Key&quot; 字段中，并为该密钥添加一个可识别的标题（如 &quot;My SSH Key&quot;）。然后点击 &quot;Add Key&quot; 按钮。</p>
</li>
</ol>
<h3>验证 SSH 密钥</h3>
<p>GitLab 将验证并添加您提供的 SSH 密钥。如果一切顺利，您将在页面上看到成功的消息。</p>
<h3>测试 SSH 连接</h3>
<p>为了确保 SSH 密钥配置正确，您可以在终端中执行以下命令进行测试：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">ssh</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -T</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> git@gitlab.com</span></span></code></pre>
</div><p>如果配置正确，您将看到与 GitLab 相关的欢迎消息。<br>
现在，您已经成功在 GitLab 中配置了 SSH 密钥。这将允许您通过 SSH 协议进行与 GitLab 之间的安全通信，例如进行代码的克隆、推送和拉取操作。</p>
<div class="hint-container important">
<p class="hint-container-title">重要</p>
<p>我们建议您使用ssh密钥而非用户名密码登录</p>
<p>因为有些情况比如更换git客户端、或者某些自动化脚本，是无法使用用户名密码登录的</p>
<p>对于macOS用户，我强烈建议您添加以下命令到zsh的配置文件 <code>~/.zprofile</code>中</p>
<div class="language-sh" data-highlighter="shiki" data-ext="sh" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-sh"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">ssh-add</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> --apple-use-keychain</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> ~/.ssh/id_rsa</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> &#x26;> /dev/null</span></span></code></pre>
</div><p>该命令的作用是将指定的 SSH 私钥文件 id_rsa 添加到 Apple Keychain 中，并将输出静默地丢弃，以确保在终端上不会显示任何输出信息。这通常用于在登录时自动加载 SSH 密钥，并将其保存在钥匙串中，以便在需要时无需再次手动输入密码或密钥口令。</p>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Jenkins 远程触发构建踩坑记</title>
      <link>https://www.linkjm.com/tutorial/CI_CD/Jenkins.html</link>
      <guid>https://www.linkjm.com/tutorial/CI_CD/Jenkins.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Jenkins 远程触发构建踩坑记</source>
      <description>CSRF crumbk,Jenkins,</description>
      <category>教程</category>
      <pubDate>Fri, 12 Jan 2024 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>如果想在代码 Push 后，或者 Merge request 后，自动部署，可以采用多种方案，以下介绍两种</p>
<p>不知道如何配置的同学，可以参考一下</p>
</div>
<h2>1. 通过 Jenkins 提供的【触发远程构建】</h2>
<h3>1.1. 勾选【触发远程构建】并填入 token</h3>
<figure><img src="https://s3.bmp.ovh/imgs/2024/01/12/45009f0747ed807a.png" alt="勾选远程构建开关" tabindex="0" loading="lazy"><figcaption>勾选远程构建开关</figcaption></figure>
<h3>1.2. 配置 API token</h3>
<ol>
<li>
<p>打开 Jenkins 控制台。</p>
</li>
<li>
<p>在顶部导航栏中，点击您的用户名，然后选择 &quot;Configure&quot; 选项。</p>
</li>
<li>
<p>在配置页面中，向下滚动，找到 &quot;API Token&quot; 部分。</p>
</li>
<li>
<p>如果您之前没有生成过 API Token，则点击 &quot;Add new Token&quot; 或 &quot;Generate Token&quot; 按钮。</p>
</li>
<li>
<p>在生成或更改 Token 的过程中，您可能需要提供您的 Jenkins 用户密码进行身份验证。</p>
</li>
<li>
<p>生成或更改成功后，您将看到新生成的 Token 值。请将其复制并妥善保存，因为在以后的访问中，您将无法再查看该 Token 的值。</p>
</li>
</ol>
<h3>1.3. 如何调用 Url</h3>
<p>Jenkins 提供了便捷的远程触发功能，但是需要配置一个 token，然后在 push 后，通过 post 请求，调用 Jenkins 的 url 即可触发构建</p>
<p>调用 url 可以使用 python 脚本，或者 curl 命令，一般配合 git 提交，使用 curl 命令居多</p>
<p>下面以 GitLab CI/CD 为例进行举例</p>
<p>GitLab CI/CD 是一个简洁好用的的持续集成/持续交付的框架。通过为你的项目配置一个或者多个 GitLab Runner，然后撰写一个 .gitlab-ci.yml，你就可以很方便地利用 GitLab CI/CD 来为你的项目引入持续集成/交付的功能。比较类似之前介绍过的<a href="/tutorial/github/github-action.html" target="_blank">GitHubAction</a></p>
<p>GitLab CI/CD 是通过 GitLab Runner 来执行的</p>
<p>GitLab CI/CD 将按照 Stage 定义的顺序来执行，任何一个 Stage 失败，整个 CI/CD 将失败</p>
<p>每一个 Stage 可以被若干个 Job 关联。Stage 在执行的时候，关联到这个 Stage 的所有 Job 都将被执行，不过不同的 Job 可能是并行执行的。</p>
<p>每个 Job 在执行的时候，会先按照缓存策略加载缓存数据，然后按照顺序依次运行 before_script、script 和 after_script 中配置的脚本，运行完毕以后，会将生成的数据保存到缓存中。</p>
<h3>1.4. 编写.gitlab-ci.yml</h3>
<div class="language-yaml" data-highlighter="shiki" data-ext="yaml" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-yaml"><span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">stages</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  - </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">deploy</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">deploy_to_hyjk_open-x</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  stage</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">deploy</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  only</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    - </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">master</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    - </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">./deploy.sh</span></span></code></pre>
</div><p>大意是当 master 分支 deploy 时执行 ./deploy.sh 脚本<br>
这里 script 调用了一个 shell 脚本，因为在 yml 文件中写脚本比较麻烦，个人比较习惯在 sh 文件中写脚本，然后在 yml 文件中调用这个脚本即可</p>
<h3>1.5.  <code>deploy.sh</code></h3>
<p>由于新版本的 Jenkins 安全机制，每次调用 API 前都需要先调用获取获取 CSRF crumb 值的接口</p>
<p>下面脚本演示了，如何先获取 crumb 的值，并在之后触发远程构建 API 的时候，携带这个值</p>
<div class="language-sh line-numbers-mode" data-highlighter="shiki" data-ext="sh" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-sh"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">#!/bin/bash</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># Jenkins 服务器信息</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">JENKINS_URL</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"http://192.168.155.57:8080/"</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">JENKINS_USERNAME</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"admin"</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">JENKINS_API_TOKEN</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"11abf441f4db6e341ae65b660f74de6e02"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 获取 CSRF crumb 值</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">CRUMB</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">$(</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">curl</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -s</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -u</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$JENKINS_USERNAME</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">:</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$JENKINS_API_TOKEN</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$JENKINS_URL</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">\"</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">:</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">\"</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">,//crumb)"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 提取 crumb 值</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">CRUMB_VALUE</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">$(</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">echo</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$CRUMB</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">awk</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -F</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">':'</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> '{print $2}'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 执行 Jenkins 远程构建</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">curl</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -X</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> POST</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$JENKINS_URL</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">/job/projectName/build?token=xZ7Y3n9A"</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> \</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">  --user</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$JENKINS_USERNAME</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">:</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$JENKINS_API_TOKEN</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> \</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">  --header</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "Jenkins-Crumb: </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">$CRUMB_VALUE</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>2. 通过 GitLab 的 <strong>webhook</strong> 触发远程构建</h2>
<p>使用 gitlab 的 <strong>webhook</strong> 配合 Jenkins 实现自动化部署。<br>
Jenkins 需要安装 GitLab Plugin 插件，然后配置 <strong>webhook</strong> 即可。</p>
<h3>2.1. 勾选插件选项</h3>
<figure><img src="https://s3.bmp.ovh/imgs/2024/01/12/fe222283253f58a1.png" alt="勾选插件选项" tabindex="0" loading="lazy"><figcaption>勾选插件选项</figcaption></figure>
<h3>2.2. 选择配置</h3>
<p>插件有很多配置项，这里我们常用的就是过滤分支，可以选择，有以下选项</p>
<ul>
<li><strong>Include</strong></li>
<li><strong>Exclude</strong></li>
<li><strong>正则表达式</strong></li>
<li><strong>tag</strong></li>
</ul>
<figure><img src="https://s3.bmp.ovh/imgs/2024/01/12/893d0e7b5ca4e1b9.png" alt="设置token" tabindex="0" loading="lazy"><figcaption>设置token</figcaption></figure>
<h3>2.3. 将生成的 url 和 token 填入 GitLab 中</h3>
<p>可以选择触发的 events<br>
<img src="https://s3.bmp.ovh/imgs/2024/01/12/525dcea85ba7ae20.png" alt="填入GitLab" loading="lazy"></p>
<h3>2.4. 查看运行结果</h3>
<figure><img src="https://s3.bmp.ovh/imgs/2024/01/12/b5afe5d3321e17da.png" alt="查看运行结果" tabindex="0" loading="lazy"><figcaption>查看运行结果</figcaption></figure>
]]></content:encoded>
      <enclosure url="https://s3.bmp.ovh/imgs/2024/01/12/45009f0747ed807a.png" type="image/png"/>
    </item>
    <item>
      <title>Vercel deploy忽略指定分支</title>
      <link>https://www.linkjm.com/tutorial/CI_CD/vercel-deploy.html</link>
      <guid>https://www.linkjm.com/tutorial/CI_CD/vercel-deploy.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Vercel deploy忽略指定分支</source>
      <description>Vercel Ignored Build Step使用</description>
      <category>教程</category>
      <pubDate>Fri, 31 Mar 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[
<p>一招教你解决Vercel deploy时总是任意分支都可以触发，如果你的项目有多个分支，可以指定某一个或几个有提交时触发deploy</p>
<!-- more -->
<h2>1. 前言</h2>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>这不是刚把博客部署从GitHub Pages转移到了Vercel上，稍微加快了一点国内的访问速度，然后发现了一个新问题~</p>
<p>原先是推送到<code>main</code>分之，通过Github Action 工作流进行部署到<code>gh-page</code> 分支上</p>
<p>转移到Vercel之后，工作流我并没有删掉，所以流程还是一样，就导致<code>main</code>提交了</p>
</div>
]]></content:encoded>
      <enclosure url="https://s3.bmp.ovh/imgs/2023/03/31/5ae4ba35bd181dcf.png" type="image/png"/>
    </item>
    <item>
      <title>CSS 属性计算过程</title>
      <link>https://www.linkjm.com/posts/Web/CSS/calculation.html</link>
      <guid>https://www.linkjm.com/posts/Web/CSS/calculation.html</guid>
      <source url="https://www.linkjm.com/rss.xml">CSS 属性计算过程</source>
      <description>CSS 属性计算过程 你是否了解 CSS 的属性计算过程呢？ 有的同学可能会讲，CSS属性我倒是知道，例如： 上面的 CSS 代码中，p 是元素选择器，color 就是其中的一个 CSS 属性。 但是要说 CSS 属性的计算过程，还真的不是很清楚。 没关系，通过此篇文章，能够让你彻底明白什么是 CSS 属性的计算流程。 image-2022081314...</description>
      <category>Web</category>
      <pubDate>Thu, 06 Apr 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[
<p>你是否了解 CSS 的属性计算过程呢？</p>
<p>有的同学可能会讲，CSS属性我倒是知道，例如：</p>
<div class="language-css" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  color : </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">red</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>上面的 CSS 代码中，p 是元素选择器，color 就是其中的一个 CSS 属性。</p>
<p>但是要说 CSS 属性的计算过程，还真的不是很清楚。</p>
<p>没关系，通过此篇文章，能够让你彻底明白什么是 CSS 属性的计算流程。</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-060434.png" alt="image-20220813140434032" style="zoom:50%;">
<p>首先，不知道你有没有考虑过这样的一个问题，假设在 HTML 中有这么一段代码：</p>
<div class="language-html" data-highlighter="shiki" data-ext="html" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-html"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">body</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>这是一个h1标题&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">body</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><p>上面的代码也非常简单，就是在 body 中有一个 h1 标题而已，该 h1 标题呈现出来的外观是如下：</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-060724.png" alt="image-20220813140724136" style="zoom:50%;">
<p>目前我们没有设置该 h1 的任何样式，但是却能看到该 h1 有一定的默认样式，例如有默认的字体大小、默认的颜色。</p>
<p>那么问题来了，我们这个 h1 元素上面除了有默认字体大小、默认颜色等属性以外，究竟还有哪些属性呢？</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-15-014216.png" alt="image-20220815094215982" style="zoom:30%;">
<p>答案是<strong>该元素上面会有 CSS 所有的属性。</strong>你可以打开浏览器的开发者面板，选择【元素】，切换到【计算样式】，之后勾选【全部显示】，此时你就能看到在此 h1 上面所有 CSS 属性对应的值。</p>
<figure><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-061516.png" alt="image-20220813141516153" tabindex="0" loading="lazy"><figcaption>image-20220813141516153</figcaption></figure>
<p>换句话说，<strong>我们所书写的任何一个 HTML 元素，实际上都有完整的一整套 CSS 样式</strong>。这一点往往是让初学者比较意外的，因为我们平时在书写 CSS 样式时，往往只会书写必要的部分，例如前面的：</p>
<div class="language-css" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  color : </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">red</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>这往往会给我们造成一种错觉，认为该 p 元素上面就只有 color 属性。而真实的情况确是，任何一个 HTML 元素，都有一套完整的 CSS 样式，只不过你没有书写的样式，<strong>大概率可能</strong>会使用其默认值。例如上图中 h1 一个样式都没有设置，全部都用的默认值。</p>
<p>但是注意，我这里强调的是“大概率可能”，难道还有我们“没有设置值，但是不使用默认值”的情况么？</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-15-014459.png" alt="image-20220815094458940" style="zoom:25%;">
<p>嗯，确实有的，所以我才强调你要了解“CSS 属性的计算过程”。</p>
<p>总的来讲，属性值的计算过程，分为如下这么 <em>4</em> 个步骤：</p>
<ul>
<li>确定声明值</li>
<li>层叠冲突</li>
<li>使用继承</li>
<li>使用默认值</li>
</ul>
<h2>确定声明值</h2>
<p>首先第一步，是确定声明值。所谓声明值就是作者自己所书写的 CSS 样式，例如前面的：</p>
<div class="language-css" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  color : </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">red</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>这里我们声明了 p 元素为红色，那么就会应用此属性设置。</p>
<p>当然，除了作者样式表，一般浏览器还会存在“用户代理样式表”，简单来讲就是浏览器内置了一套样式表。</p>
<figure><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-063500.png" alt="image-20220813143500066" tabindex="0" loading="lazy"><figcaption>image-20220813143500066</figcaption></figure>
<p>在上面的示例中，作者样式表中设置了 color 属性，而用户代理样式表（浏览器提供的样式表）中设置了诸如 display、margin-block-start、margin-block-end、margin-inline-start、margin-inline-end 等属性对应的值。</p>
<p>这些值目前来讲也没有什么冲突，因此最终就会应用这些属性值。</p>
<h2>层叠冲突</h2>
<p>在确定声明值时，可能出现一种情况，那就是声明的样式规则发生了冲突。</p>
<p>此时会进入解决层叠冲突的流程。而这一步又可以细分为下面这三个步骤：</p>
<ul>
<li>比较源的重要性</li>
<li>比较优先级</li>
<li>比较次序</li>
</ul>
<p>来来来，我们一步一步来看。</p>
<h3>比较源的重要性</h3>
<p>当不同的 CSS 样式来源拥有相同的声明时，此时就会根据样式表来源的重要性来确定应用哪一条样式规则。</p>
<p>那么问题来了，咱们的样式表的源究竟有几种呢？</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-23-100047.png" alt="image-20220823180047075" style="zoom:40%;">
<p>整体来讲有三种来源：</p>
<ul>
<li>浏览器会有一个基本的样式表来给任何网页设置默认样式。这些样式统称<strong>用户代理样式</strong>。</li>
<li>网页的作者可以定义文档的样式，这是最常见的样式表，称之为<strong>页面作者样式</strong>。</li>
<li>浏览器的用户，可以使用自定义样式表定制使用体验，称之为<strong>用户样式</strong>。</li>
</ul>
<p>对应的重要性顺序依次为：页面作者样式 &gt; 用户样式 &gt; 用户代理样式</p>
<p>更详细的来源重要性比较，可以参阅 <em>MDN</em>：<em><a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/Cascade" target="_blank" rel="noopener noreferrer">https://developer.mozilla.org/zh-CN/docs/Web/CSS/Cascade</a></em></p>
<p>我们来看一个示例。</p>
<p>例如现在有<strong>页面作者样式表</strong>和<strong>用户代理样式表</strong>中存在属性的冲突，那么会以作者样式表优先。</p>
<div class="language-css" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  color : </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">red</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  display: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">inline-block</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><figure><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-064222.png" alt="image-20220813144222152" tabindex="0" loading="lazy"><figcaption>image-20220813144222152</figcaption></figure>
<p>可以明显的看到，作者样式表和用户代理样式表中同时存在的 display 属性的设置，最终作者样式表干掉了用户代理样式表中冲突的属性。这就是第一步，根据不同源的重要性来决定应用哪一个源的样式。</p>
<h3>比较优先级</h3>
<p>那么接下来，如果是在在同一个源中有样式声明冲突怎么办呢？此时就会进行样式声明的优先级比较。</p>
<p>例如：</p>
<div class="language-html" data-highlighter="shiki" data-ext="html" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-html"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"test"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>test&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><div class="language-css" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">.test</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  font-size: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">50</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  font-size: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">20</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>在上面的代码中，同属于<strong>页面作者样式</strong>，源的重要性是相同的，此时会以选择器的权重来比较重要性。</p>
<p>很明显，上面的选择器的权重要大于下面的选择器，因此最终标题呈现为 <em>50px</em>。</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2021-09-16-071546.png" alt="image-20210916151546500" style="zoom: 40%;">
<p>可以看到，落败的作者样式在 <em>Elements&gt;Styles</em> 中会被划掉。</p>
<p>有关选择器权重的计算方式，不清楚的同学，可以进入此传送门：<em><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity" target="_blank" rel="noopener noreferrer">https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity</a></em></p>
<h3>比较次序</h3>
<p>经历了上面两个步骤，大多数的样式声明能够被确定下来。但是还剩下最后一种情况，那就是样式声明既是同源，权重也相同。</p>
<p>此时就会进入第三个步骤，比较样式声明的次序。</p>
<p>举个例子：</p>
<div class="language-css" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  font-size: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">50</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  font-size: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">20</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>在上面的代码中，同样都是<strong>页面作者样式</strong>，<strong>选择器的权重也相同</strong>，此时位于下面的样式声明会层叠掉上面的那一条样式声明，最终会应用 <em>20px</em> 这一条属性值。</p>
<figure><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-23-103928.png" alt="image-20220823183928330" tabindex="0" loading="lazy"><figcaption>image-20220823183928330</figcaption></figure>
<p>至此，样式声明中存在冲突的所有情况，就全部被解决了。</p>
<h2>使用继承</h2>
<p>层叠冲突这一步完成后，解决了相同元素被声明了多条样式规则究竟应用哪一条样式规则的问题。</p>
<p>那么如果没有声明的属性呢？此时就使用默认值么？</p>
<p><em>No、No、No</em>，别急，此时还有第三个步骤，那就是使用继承而来的值。</p>
<p>例如：</p>
<div class="language-html" data-highlighter="shiki" data-ext="html" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-html"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>Lorem ipsum dolor sit amet.&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><div class="language-css" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  color: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">red</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>在上面的代码中，我们针对 div 设置了 color 属性值为红色，而针对 p 元素我们没有声明任何的属性，但是由于 color 是可以继承的，因此 p 元素从最近的 div 身上继承到了 color 属性的值。</p>
<figure><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-065102.png" alt="image-20220813145102293" tabindex="0" loading="lazy"><figcaption>image-20220813145102293</figcaption></figure>
<p>这里有两个点需要同学们注意一下。</p>
<p>首先第一个是我强调了是<strong>最近的</strong> div 元素，看下面的例子：</p>
<div class="language-html" data-highlighter="shiki" data-ext="html" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-html"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"test"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>Lorem ipsum dolor sit amet.&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><div class="language-css" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  color: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">red</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">.test</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  color: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">blue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><figure><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-065653.png" alt="image-20220813145652726" tabindex="0" loading="lazy"><figcaption>image-20220813145652726</figcaption></figure>
<p>因为这里并不涉及到选中 p 元素声明 color 值，而是从父元素上面继承到 color 对应的值，因此这里是<strong>谁近就听谁</strong>的，初学者往往会产生混淆，又去比较权重，但是这里根本不会涉及到权重比较，因为压根儿就没有选中到 p 元素。</p>
<p>第二个就是哪些属性能够继承？</p>
<p>关于这一点的话，大家可以在 MDN 上面很轻松的查阅到。例如我们以 text-align 为例，如下图所示：</p>
<figure><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-070148.png" alt="image-20220813150147885" tabindex="0" loading="lazy"><figcaption>image-20220813150147885</figcaption></figure>
<h2>使用默认值</h2>
<p>好了，目前走到这一步，如果属性值都还不能确定下来，那么就只能是使用默认值了。</p>
<p>如下图所示：</p>
<figure><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-070825.png" alt="image-20220813150824752" tabindex="0" loading="lazy"><figcaption>image-20220813150824752</figcaption></figure>
<p>前面我们也说过，一个 HTML 元素要在浏览器中渲染出来，必须具备所有的 CSS 属性值，但是绝大部分我们是不会去设置的，用户代理样式表里面也不会去设置，也无法从继承拿到，因此最终都是用默认值。</p>
<p>好了，这就是关于 CSS 属性计算过程的所有知识了。</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-154655.png" alt="image-20220814234654914" style="zoom:33%;">
<h2>一道面试题</h2>
<p>好了，学习了今天的内容，让我来用一道面试题测试测试大家的理解程度。</p>
<p>下面的代码，最终渲染出来的效果，a 元素是什么颜色？p 元素又是什么颜色？</p>
<div class="language-html" data-highlighter="shiki" data-ext="html" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-html"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> href</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">""</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>test&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>test&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><div class="language-css" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  color: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">red</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>大家能说出为什么会呈现这样的结果么？</p>
<p>解答如下：</p>
<figure><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-071941.png" alt="image-20220813151941113" tabindex="0" loading="lazy"><figcaption>image-20220813151941113</figcaption></figure>
<p>实际上原因很简单，因为 a 元素在用户代理样式表中已经设置了 color 属性对应的值，因此会应用此声明值。而在 p 元素中无论是作者样式表还是用户代理样式表，都没有对此属性进行声明，然而由于 color 属性是可以继承的，因此最终 p 元素的 color 属性值通过继承来自于父元素。</p>
<p>你答对了么？-）</p>
]]></content:encoded>
      <enclosure url="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-061516.png" type="image/png"/>
    </item>
    <item>
      <title>你不知道的 CSS 之包含块</title>
      <link>https://www.linkjm.com/posts/Web/CSS/containing-block.html</link>
      <guid>https://www.linkjm.com/posts/Web/CSS/containing-block.html</guid>
      <source url="https://www.linkjm.com/rss.xml">你不知道的 CSS 之包含块</source>
      <description>你不知道的 CSS 之包含块 一说到 CSS 盒模型，这是很多小伙伴耳熟能详的知识，甚至有的小伙伴还能说出 border-box 和 content-box 这两种盒模型的区别。 但是一说到 CSS 包含块，有的小伙伴就懵圈了，什么是包含块？好像从来没有听说过这玩意儿。 image-20220814222004395 好吧，如果你对包含块的知识一无所知...</description>
      <category>Web</category>
      <pubDate>Thu, 06 Apr 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[
<p>一说到 CSS 盒模型，这是很多小伙伴耳熟能详的知识，甚至有的小伙伴还能说出 border-box 和 content-box 这两种盒模型的区别。</p>
<p>但是一说到 CSS 包含块，有的小伙伴就懵圈了，什么是包含块？好像从来没有听说过这玩意儿。</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-142005.png" alt="image-20220814222004395" style="zoom: 20%;">
<p>好吧，如果你对包含块的知识一无所知，那么系好安全带，咱们准备出发了。</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-060434.png" alt="image-20220813140434032" style="zoom:50%;">
<p>包含块英语全称为<strong>containing block</strong>，实际上平时你在书写 CSS 时，大多数情况下你是感受不到它的存在，因此你不知道这个知识点也是一件很正常的事情。但是这玩意儿是确确实实存在的，在 CSS 规范中也是明确书写了的：</p>
<p><em><a href="https://drafts.csswg.org/css2/#containing-block-details" target="_blank" rel="noopener noreferrer">https://drafts.csswg.org/css2/#containing-block-details</a></em></p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-142459.png" alt="image-20220814222458695" style="zoom:50%;">
<p>并且，如果你不了解它的运作机制，有时就会出现一些你认为的莫名其妙的现象。</p>
<p>那么，这个包含块究竟说了什么内容呢？</p>
<p>说起来也简单，<strong>就是元素的尺寸和位置，会受它的包含块所影响。对于一些属性，例如 width, height, padding, margin，绝对定位元素的偏移值（比如 position 被设置为 absolute 或 fixed），当我们对其赋予百分比值时，这些值的计算值，就是通过元素的包含块计算得来。</strong></p>
<p>来吧，少年，让我们从最简单的 case 开始看。</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-143153.png" alt="image-20220814223152726" style="zoom: 50%;">
<div class="language-html" data-highlighter="shiki" data-ext="html" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-html"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">body</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"container"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"item"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">body</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><div class="language-css" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">.container</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">500</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">300</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: skyblue;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">.item</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">50</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">%</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">50</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">%</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">red</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>请仔细阅读上面的代码，然后你认为 div.item 这个盒子的宽高是多少？</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-143451.png" alt="image-20220814223451349" style="zoom: 33%;">
<p>相信你能够很自信的回答这个简单的问题，div.item 盒子的 width 为 250px，height 为 150px。</p>
<p>这个答案确实是没有问题的，但是如果我追问你是怎么得到这个答案的，我猜不了解包含块的你大概率会说，因为它的父元素 div.container 的 width 为 500px，50% 就是 250px，height 为 300px，因此 50% 就是 150px。</p>
<p>这个答案实际上是不准确的。正确的答案应该是，<strong>div.item 的宽高是根据它的包含块来计算的</strong>，而这里包含块的大小，正是这个元素最近的祖先块元素的内容区。</p>
<p>因此正如我前面所说，<strong>很多时候你都感受不到包含块的存在。</strong></p>
<p>包含块分为两种，一种是根元素（HTML 元素）所在的包含块，被称之为初始包含块（<strong>initial containing block</strong>）。对于浏览器而言，初始包含块的的大小等于视口 viewport 的大小，基点在画布的原点（视口左上角）。它是作为元素绝对定位和固定定位的参照物。</p>
<p>另外一种是对于非根元素，对于非根元素的包含块判定就有几种不同的情况了。大致可以分为如下几种：</p>
<ul>
<li>如果元素的 positiion 是 relative 或 static ，那么包含块由离它最近的块容器（block container）的内容区域（content area）的边缘建立。</li>
<li>如果 position 属性是 fixed，那么包含块由视口建立。</li>
<li>如果元素使用了 absolute 定位，则包含块由它的最近的 position 的值不是 static （也就是值为fixed、absolute、relative 或 sticky）的祖先元素的内边距区的边缘组成。</li>
</ul>
<p>前面两条实际上都还比较好理解，第三条往往是初学者容易比较忽视的，我们来看一个示例：</p>
<div class="language-html" data-highlighter="shiki" data-ext="html" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-html"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">body</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"container"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"item"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"item2"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">body</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><div class="language-css line-numbers-mode" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">.container</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">500</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">300</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: skyblue;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  position: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">relative</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">.item</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">300</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">150</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  border: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">5</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> solid</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  margin-left: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">100</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">.item2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">100</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">100</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">red</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  position: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">absolute</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  left: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">10</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  top: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">10</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>首先阅读上面的代码，然后你能在脑海里面想出其大致的样子么？或者用笔和纸画一下也行。</p>
<p>公布正确答案：</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-153548.png" alt="image-20220814233548188" style="zoom: 33%;">
<p>怎么样？有没有和你所想象的对上？</p>
<p>其实原因也非常简单，根据上面的第三条规则，对于 div.item2 来讲，它的包含块应该是 div.container，而非 div.item。</p>
<p>如果你能把上面非根元素的包含块判定规则掌握，那么关于包含块的知识你就已经掌握 80% 了。</p>
<p>实际上对于非根元素来讲，包含块还有一种可能，那就是如果 position 属性是 absolute 或 fixed，包含块也可能是由满足以下条件的最近父级元素的内边距区的边缘组成的：</p>
<ul>
<li>transform 或 perspective 的值不是 none</li>
<li>will-change 的值是 transform 或 perspective</li>
<li>filter 的值不是 none 或 will-change 的值是 filter(只在 Firefox 下生效).</li>
<li>contain 的值是 paint (例如: contain: paint;)</li>
</ul>
<p>我们还是来看一个示例：</p>
<div class="language-html" data-highlighter="shiki" data-ext="html" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-html"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">body</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"container"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"item"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"item2"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">body</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><div class="language-css line-numbers-mode" data-highlighter="shiki" data-ext="css" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-css"><span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">.container</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">500</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">300</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: skyblue;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  position: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">relative</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">.item</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">300</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">150</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  border: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">5</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> solid</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  margin-left: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">100</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  transform: </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">rotate</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">deg</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">); </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/* 新增代码 */</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">.item2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">100</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">100</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">red</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  position: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">absolute</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  left: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">10</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  top: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">10</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">px</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>我们对于上面的代码只新增了一条声明，那就是 transform: rotate(0deg)，此时的渲染效果却发生了改变，如下图所示：</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-154347.png" alt="image-20220814234347149" style="zoom:33%;">
<p>可以看到，此时对于 div.item2 来讲，包含块就变成了 div.item。</p>
<p>好了，到这里，关于包含块的知识就基本讲完了。</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-154655.png" alt="image-20220814234654914" style="zoom:33%;">
<p>我们再把 CSS 规范中所举的例子来看一下。</p>
<div class="language-html line-numbers-mode" data-highlighter="shiki" data-ext="html" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-html"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">html</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">head</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">title</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>Illustration of containing blocks&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">title</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">head</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">body</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"body"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"div1"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"p1"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>This is text in the first paragraph...&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"p2"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        This is text</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">em</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"em1"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">          in the</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">          &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">strong</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"strong1"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>second&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">strong</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">          paragraph.</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">em</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">body</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">html</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上面是一段简单的 HTML 代码，在没有添加任何 CSS 代码的情况下，你能说出各自的包含块么？</p>
<p>对应的结果如下：</p>
<p>| 元素    | 包含块                      |<br>
|</p>
]]></content:encoded>
    </item>
    <item>
      <title>CommonJS，RequireJS，SeaJS 归纳笔记</title>
      <link>https://www.linkjm.com/posts/Web/JavaScript/js-module-loader.html</link>
      <guid>https://www.linkjm.com/posts/Web/JavaScript/js-module-loader.html</guid>
      <source url="https://www.linkjm.com/rss.xml">CommonJS，RequireJS，SeaJS 归纳笔记</source>
      <description>Foreword Here comes Module! 随着网站逐渐变成「互联网应用程序」，嵌入网页的 JavaScript 代码越来越庞大，越来越复杂。网页越来越像桌面程序，需要一个团队分工协作、进度管理、单元测试……我们不得不使用软件工程的方法，来管理网页的业务逻辑。 于是，JavaScript 的模块化成为迫切需求。在 ES6 Module 来临...</description>
      <category>Web</category>
      <pubDate>Mon, 25 May 2015 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<h2>Foreword</h2>
<blockquote>
<p>Here comes Module!</p>
</blockquote>
<p>随着网站逐渐变成「互联网应用程序」，嵌入网页的 JavaScript 代码越来越庞大，越来越复杂。网页越来越像桌面程序，需要一个团队分工协作、进度管理、单元测试……我们不得不使用软件工程的方法，来管理网页的业务逻辑。</p>
<p>于是，JavaScript 的模块化成为迫切需求。在 ES6 Module 来临之前，JavaScript 社区提供了强大支持，尝试在现有的运行环境下，实现模块的效果。</p>
]]></content:encoded>
    </item>
    <item>
      <title>JavaScript ES6</title>
      <link>https://www.linkjm.com/posts/Web/JavaScript/js-version.html</link>
      <guid>https://www.linkjm.com/posts/Web/JavaScript/js-version.html</guid>
      <source url="https://www.linkjm.com/rss.xml">JavaScript ES6</source>
      <description>JavaScript 有着很奇怪的命名史。 1995 年，它作为网景浏览器（Netscape Navigator）的一部分首次发布，网景给这个新语言命名为 LiveScript。一年后，为了搭上当时媒体热炒 Java 的顺风车，临时改名为了 JavaScript （当然，Java 和 JavaScript 的关系，就和雷锋和雷锋塔一样 —— 并没有什么...</description>
      <category>Web</category>
      <pubDate>Tue, 22 Sep 2015 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>JavaScript 有着很奇怪的命名史。</p>
<p>1995 年，它作为网景浏览器（Netscape Navigator）的一部分首次发布，网景给这个新语言命名为 LiveScript。一年后，为了搭上当时媒体热炒 Java 的顺风车，临时改名为了 JavaScript <em>（当然，Java 和 JavaScript 的关系，就和雷锋和雷锋塔一样 —— 并没有什么关系）</em></p>
<p><small class="img-hint">歪果仁的笑话怎么一点都不好笑</small></p>
<blockquote>
<p>译者注：<a href="https://en.wikipedia.org/wiki/JavaScript#History" target="_blank" rel="noopener noreferrer">wikipedia 的 JavaScript 词条</a> 更详细的叙述了这段历史</p>
</blockquote>
<p>1996 年，网景将 JavaScript 提交给 <a href="http://www.ecma-international.org/" target="_blank" rel="noopener noreferrer">ECMA International（欧洲计算机制造商协会）</a> 进行标准化，并最终确定出新的语言标准，它就是 ECMAScript。自此，ECMAScript 成为所有 JavaScript 实现的基础，不过，由于 JavaScript 名字的历史原因和市场原因（很显然 ECMAScript 这个名字并不令人喜欢……），现实中我们只用 ECMAScript 称呼标准，平时都还是使用 JavaScript 来称呼这个语言。</p>
<blockquote>
<p>术语（译者注）：</p>
<ul>
<li><em>标准（Standard）</em>： 用于定义与其他事物区别的一套规则</li>
<li><em>实现（Implementation）</em>： 某个标准的具体实施/真实实践</li>
</ul>
</blockquote>
<p>不过，JavaScript 开发者们并不怎么在乎这些，因为在诞生之后的 15 年里，ECMAScript 并没有多少变化，而且现实中的很多实现都已经和标准大相径庭。其实在第一版的 ECMAScript 发布后，很快又跟进发布了两个版本，但是自从 1999 年 ECMAScript 3 发布后，十年内都没有任何改动被成功添加到官方规范里。取而代之的，是各大浏览器厂商们争先进行自己的语言拓展，web 开发者们别无选择只能去尝试并且支持这些 API。即使是在 2009 年 ECMAScript 5 发布之后，仍然用了数年这些新规范才得到了浏览器的广泛支持，可是大部分开发者还是写着 ECMAScript 3 风格的代码，并不觉得有必要去了解这些规范。</p>
<blockquote>
<p>译者注：<a href="https://en.wikipedia.org/wiki/ECMAScript#4th_Edition_.28abandoned.29" target="_blank" rel="noopener noreferrer">ECMAScript 第四版草案</a>由于太过激进而被抛弃，Adobe 的 <a href="https://en.wikipedia.org/wiki/ActionScript" target="_blank" rel="noopener noreferrer">ActionScript 3.0</a> 是 ECMAScript edition 4 的唯一实现（ Flash 差点就统一 Web 了）</p>
</blockquote>
<p>到了 2012 年，事情突然开始有了转变。大家开始推动停止对旧版本 IE 浏览器的支持，用 ECMAScript 5 (ES5) 风格来编写代码也变得更加可行。与此同时，一个新的 ECMAScript 规范也开始启动。到了这时，大家开始逐渐习惯以对 ECMAScript 规范的版本支持程度来形容各种 JavaScript 实现。在正式被指名为 ECMAScript 第 6 版 (ES6) 之前，这个新的标准原本被称为 ES.Harmony（和谐）。2015 年，负责制定 ECMAScript 规范草案的委员会 TC39 决定将定义新标准的制度改为一年一次，这意味着每个新特性一旦被批准就可以添加，而不像以往一样，规范只有在整个草案完成，所有特性都没问题后才能被定稿。因此，ECMAScript 第 6 版在六月份公布之前，又被重命名为了 ECMAScript 2015（ES2015）</p>
<p>目前，仍然有很多新的 JavaScript 特性或语法正在提议中，包括 <a href="https://github.com/wycats/javascript-decorators" target="_blank" rel="noopener noreferrer">decorators（装饰者）</a>，<a href="https://github.com/lukehoban/ecmascript-asyncawait" target="_blank" rel="noopener noreferrer">async-await（async-await 异步编程模型）</a> 和 <a href="https://github.com/jeffmo/es-class-properties" target="_blank" rel="noopener noreferrer">static class properties（静态类属性）</a>。它们通常被称为 ES7，ES2016 或者 ES.Next 的特性，不过实际上它们只能被称作提案或者说可能性，毕竟 ES2016 的规范还没有完成，有可能全部都会引入，也有可能一个都没有。TC39 把一个提案分为 4 个阶段，你可以在 <a href="https://babeljs.io/docs/usage/experimental/" target="_blank" rel="noopener noreferrer">Babel 的官网</a> 上查看各个提案目前都在哪个阶段了。</p>
<p>所以，我们该如何使用这一大堆术语呢？下面的列表或许能帮助到你：</p>
<ul>
<li><strong>ECMAScript</strong>：一个由 ECMA International 进行标准化，TC39 委员会进行监督的语言。通常用于指代标准本身。</li>
<li><strong>JavaScript</strong>：ECMAScript 标准的各种实现的最常用称呼。这个术语并不局限于某个特定版本的 ECMAScript 规范，并且可能被用于任何不同程度的任意版本的 ECMAScript 的实现。</li>
<li><strong>ECMAScript 5 (ES5)</strong>：ECMAScript 的第五版修订，于 2009 年完成标准化。这个规范在所有现代浏览器中都相当完全的实现了。</li>
<li><strong>ECMAScript 6 (ES6) / ECMAScript 2015 (ES2015)</strong>：ECMAScript 的第六版修订，于 2015 年完成标准化。这个标准被部分实现于大部分现代浏览器。可以查阅<a href="http://kangax.github.io/compat-table/es6/" target="_blank" rel="noopener noreferrer">这张兼容性表</a>来查看不同浏览器和工具的实现情况。</li>
<li><strong>ECMAScript 2016</strong>：预计的第七版 ECMAScript 修订，计划于明年夏季发布。这份规范具体将包含哪些特性还没有最终确定</li>
<li><strong>ECMAScript Proposals</strong>：被考虑加入未来版本 ECMAScript 标准的特性与语法提案，他们需要经历五个阶段：Strawman（稻草人），Proposal（提议），Draft（草案），Candidate（候选）以及 Finished （完成）。</li>
</ul>
<p>在这整个 Blog 中，我将把目前的 ECMAScript 版本称作 ES6（因为这是大部分开发者最习以为常的），把明年的规范称作 ES2016（因为，与 ES6/ES2015 不同，这个名字将在整个标准化过程中沿用）并且将那些还没有成为 ECMAScript 定稿或草案的未来语言概念称为 ECMAScript 提案或者 JavaScript 提案。我将尽我所能在任何可能引起困惑的场合沿用这篇文章。</p>
<h4>一些资源</h4>
<ul>
<li>TC39 的 <a href="https://github.com/tc39/ecma262" target="_blank" rel="noopener noreferrer">Github 仓库</a>上可以看到所有目前公开的提案</li>
<li>如果你还不熟悉 ES6，Babel 有一个<a href="https://babeljs.io/docs/learn-es2015/" target="_blank" rel="noopener noreferrer">很不错的特性概览</a></li>
<li>如果你希望深入 ES6，这里有两本很不错的书： Axel Rauschmayer 的 <a href="http://exploringjs.com/" target="_blank" rel="noopener noreferrer">Exploring ES6</a>和 Nicholas Zakas 的 <a href="https://leanpub.com/understandinges6" target="_blank" rel="noopener noreferrer">Understanding ECMAScript 6</a>。Axel 的博客 <a href="http://www.2ality.com/" target="_blank" rel="noopener noreferrer">2ality</a> 也是很不错的 ES6 资源</li>
</ul>
<p><small class="img-hint">来学 JavaScript 吧！</small></p>
<h4>著作权声明</h4>
<p>本文译自 <a href="http://benmccormick.org/2015/09/14/es5-es6-es2016-es-next-whats-going-on-with-javascript-versioning/" target="_blank" rel="noopener noreferrer">ES5, ES6, ES2016, ES.Next: What's going on with JavaScript versioning?</a><br>
译者 <a href="http://weibo.com/huxpro" target="_blank" rel="noopener noreferrer">黄玄</a>，首次发布于 <a href="http://huangxuan.me" target="_blank" rel="noopener noreferrer">Hux Blog</a>，转载请保留以上链接</p>
]]></content:encoded>
    </item>
    <item>
      <title>通过UserAgent判断设备</title>
      <link>https://www.linkjm.com/posts/Web/JavaScript/judgment.html</link>
      <guid>https://www.linkjm.com/posts/Web/JavaScript/judgment.html</guid>
      <source url="https://www.linkjm.com/rss.xml">通过UserAgent判断设备</source>
      <description>通过 js 判断 moblie 端和 pc 端进而加载不同的 css 或者 js 废话不多说，上代码 需要注意的是字符转义和引号嵌套的问题</description>
      <category>Web</category>
      <pubDate>Tue, 07 Feb 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>通过 js 判断 moblie 端和 pc 端进而加载不同的 css 或者 js</p>
</blockquote>
<h2>废话不多说，上代码</h2>
<div class="language-js line-numbers-mode" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#D19A66;--shiki-light-font-style:italic;--shiki-dark:#D19A66;--shiki-dark-font-style:italic"> type</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"text/javascript"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">function browserRedirect() </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">{</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> sUserAgent</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> navigator</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">userAgent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">toLowerCase</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsIpad</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">/ipad/</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">i</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "ipad"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsIphoneOs</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">/iphone os/</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">i</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "iphone os"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsMidp</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">/midp/</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">i</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "midp"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsUc7</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">/rv:1.2.3.4/</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">i</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "rv:1.2.3.4"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsUc</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">/ucweb/</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">i</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "ucweb"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsAndroid</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">/android/</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">i</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "android"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsCE</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">/windows ce/</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">i</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "windows ce"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsWM</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">/windows mobile/</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">i</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "windows mobile"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// document.writeln("您的浏览设备为：");</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">bIsIpad</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsIphoneOs</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsMidp</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsUc7</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsUc</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsAndroid</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsCE</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> bIsWM</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// alert("手机浏览！");</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">} </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">else</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// alert("PC浏览！");</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">document.write(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"&#x3C;script type='text/javascript' size='150' alpha='0.8' zIndex='-10' src='../js/dist/ribbon.js'>&#x3C;</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">\/</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">script>"</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">);</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">document.write(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"&#x3C;script type='text/javascript' color='0,188,212' opacity='0.7' zIndex='-2' count='99' src='http://cdn.bootcss.com/canvas-nest.js/1.0.1/canvas-nest.min.js'>&#x3C;</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">\/</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">script>"</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">);</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">	}</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">}</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">browserRedirect();</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">&#x3C;/script></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>需要注意的是字符转义和引号嵌套的问题</h3>
]]></content:encoded>
    </item>
    <item>
      <title>浏览器的事件循环</title>
      <link>https://www.linkjm.com/posts/Web/Browser/event-loop.html</link>
      <guid>https://www.linkjm.com/posts/Web/Browser/event-loop.html</guid>
      <source url="https://www.linkjm.com/rss.xml">浏览器的事件循环</source>
      <description>浏览器的进程模型 何为进程？ 程序运行需要有它自己专属的内存空间，可以把这块内存空间简单的理解为进程</description>
      <category>Web</category>
      <pubDate>Thu, 06 Apr 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<h2>浏览器的进程模型</h2>
<h3>何为进程？</h3>
<p>程序运行需要有它自己专属的内存空间，可以把这块内存空间简单的理解为进程</p>
<!-- more -->
<figure><img src="https://s3.bmp.ovh/imgs/2023/04/11/9e81b88e42fe104f.png" alt="何为进程" tabindex="0" loading="lazy"><figcaption>何为进程</figcaption></figure>
<p>每个应用至少有一个进程，进程之间相互独立，即使要通信，也需要双方同意。</p>
<h3>何为线程？</h3>
<p>有了进程后，就可以运行程序的代码了。</p>
<p>运行代码的「人」称之为「线程」。</p>
<p>一个进程至少有一个线程，所以在进程开启后会自动创建一个线程来运行代码，该线程称之为主线程。</p>
<p>如果程序需要同时执行多块代码，主线程就会启动更多的线程来执行代码，所以一个进程中可以包含多个线程。</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/04/11/5badc062636572ac.png" alt="何为线程" tabindex="0" loading="lazy"><figcaption>何为线程</figcaption></figure>
<h3>浏览器有哪些进程和线程？</h3>
<p><strong>浏览器是一个多进程多线程的应用程序</strong></p>
<p>浏览器内部工作极其复杂。</p>
<p>为了避免相互影响，为了减少连环崩溃的几率，当启动浏览器后，它会自动启动多个进程。</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/04/11/8a913a1d57ab9ad1.png" alt="浏览器有哪些进程和线程" tabindex="0" loading="lazy"><figcaption>浏览器有哪些进程和线程</figcaption></figure>
<blockquote>
<p>可以在浏览器的任务管理器中查看当前的所有进程</p>
</blockquote>
<p>其中，最主要的进程有：</p>
<ol>
<li>
<p>浏览器进程</p>
<p>主要负责界面显示、用户交互、子进程管理等。浏览器进程内部会启动多个线程处理不同的任务。</p>
</li>
<li>
<p>网络进程</p>
<p>负责加载网络资源。网络进程内部会启动多个线程来处理不同的网络任务。</p>
</li>
<li>
<p><strong>渲染进程</strong>（本节课重点讲解的进程）</p>
<p>渲染进程启动后，会开启一个<strong>渲染主线程</strong>，主线程负责执行 HTML、CSS、JS 代码。</p>
<p>默认情况下，浏览器会为每个标签页开启一个新的渲染进程，以保证不同的标签页之间不相互影响。</p>
<blockquote>
<p>将来该默认模式可能会有所改变，有兴趣的同学可参见<a href="https://chromium.googlesource.com/chromium/src/+/main/docs/process_model_and_site_isolation.md#Modes-and-Availability" target="_blank" rel="noopener noreferrer">chrome官方说明文档</a></p>
</blockquote>
</li>
</ol>
<h2>渲染主线程是如何工作的？</h2>
<p>渲染主线程是浏览器中最繁忙的线程，需要它处理的任务包括但不限于：</p>
<ul>
<li>解析 HTML</li>
<li>解析 CSS</li>
<li>计算样式</li>
<li>布局</li>
<li>处理图层</li>
<li>每秒把页面画 60 次</li>
<li>执行全局 JS 代码</li>
<li>执行事件处理函数</li>
<li>执行计时器的回调函数</li>
<li>......</li>
</ul>
<blockquote>
<p>思考题：为什么渲染进程不适用多个线程来处理这些事情？</p>
</blockquote>
<p>要处理这么多的任务，主线程遇到了一个前所未有的难题：如何调度任务？</p>
<p>比如：</p>
<ul>
<li>我正在执行一个 JS 函数，执行到一半的时候用户点击了按钮，我该立即去执行点击事件的处理函数吗？</li>
<li>我正在执行一个 JS 函数，执行到一半的时候某个计时器到达了时间，我该立即去执行它的回调吗？</li>
<li>浏览器进程通知我“用户点击了按钮”，与此同时，某个计时器也到达了时间，我应该处理哪一个呢？</li>
<li>......</li>
</ul>
<p>渲染主线程想出了一个绝妙的主意来处理这个问题：排队</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/04/11/3be6637939475013.png" alt="排队" tabindex="0" loading="lazy"><figcaption>排队</figcaption></figure>
<ol>
<li>在最开始的时候，渲染主线程会进入一个无限循环</li>
<li>每一次循环会检查消息队列中是否有任务存在。如果有，就取出第一个任务执行，执行完一个后进入下一次循环；如果没有，则进入休眠状态。</li>
<li>其他所有线程（包括其他进程的线程）可以随时向消息队列添加任务。新任务会加到消息队列的末尾。在添加新任务时，如果主线程是休眠状态，则会将其唤醒以继续循环拿取任务</li>
</ol>
<p>这样一来，就可以让每个任务有条不紊的、持续的进行下去了。</p>
<p><strong>整个过程，被称之为事件循环（消息循环）</strong></p>
<h2>若干解释</h2>
<h3>何为异步？</h3>
<p>代码在执行过程中，会遇到一些无法立即处理的任务，比如：</p>
<ul>
<li>计时完成后需要执行的任务 —— <code>setTimeout</code>、<code>setInterval</code></li>
<li>网络通信完成后需要执行的任务 -- <code>XHR</code>、<code>Fetch</code></li>
<li>用户操作后需要执行的任务 -- <code>addEventListener</code></li>
</ul>
<p>如果让渲染主线程等待这些任务的时机达到，就会导致主线程长期处于「阻塞」的状态，从而导致浏览器「卡死」</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/04/11/aa0ac442c15daaf3.png" alt="image-20220810104344296" tabindex="0" loading="lazy"><figcaption>image-20220810104344296</figcaption></figure>
<p><strong>渲染主线程承担着极其重要的工作，无论如何都不能阻塞！</strong></p>
<p>因此，浏览器选择<strong>异步</strong>来解决这个问题</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/04/11/4f53367a24118e95.png" alt="异步" tabindex="0" loading="lazy"><figcaption>异步</figcaption></figure>
<p>使用异步的方式，<strong>渲染主线程永不阻塞</strong></p>
<blockquote>
<p>面试题：如何理解 JS 的异步？</p>
<p>参考答案：</p>
<p>JS是一门单线程的语言，这是因为它运行在浏览器的渲染主线程中，而渲染主线程只有一个。</p>
<p>而渲染主线程承担着诸多的工作，渲染页面、执行 JS 都在其中运行。</p>
<p>如果使用同步的方式，就极有可能导致主线程产生阻塞，从而导致消息队列中的很多其他任务无法得到执行。这样一来，一方面会导致繁忙的主线程白白的消耗时间，另一方面导致页面无法及时更新，给用户造成卡死现象。</p>
<p>所以浏览器采用异步的方式来避免。具体做法是当某些任务发生时，比如计时器、网络、事件监听，主线程将任务交给其他线程去处理，自身立即结束任务的执行，转而执行后续代码。当其他线程完成时，将事先传递的回调函数包装成任务，加入到消息队列的末尾排队，等待主线程调度执行。</p>
<p>在这种异步模式下，浏览器永不阻塞，从而最大限度的保证了单线程的流畅运行。</p>
</blockquote>
<h3>JS为何会阻碍渲染？</h3>
<p>先看代码</p>
<div class="language-html line-numbers-mode" data-highlighter="shiki" data-ext="html" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-html"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>Mr.Yuan is awesome!&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">button</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>change&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">button</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> h1</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> document</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'h1'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> btn</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> document</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'button'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // 死循环指定的时间</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> delay</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">duration</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> start</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> Date</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">now</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    while</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Date</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">now</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">-</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> start</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> duration</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {}</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  btn</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">onclick</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    h1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> '袁老师很帅！'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    delay</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3000</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  };</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>点击按钮后，会发生什么呢？</p>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>三秒之后h1文字变更，主线程被阻塞</p>
</div>
<h3>任务有优先级吗？</h3>
<p>任务没有优先级，在消息队列中先进先出</p>
<p>但<strong>消息队列是有优先级的</strong></p>
<p>根据 W3C 的最新解释:</p>
<ul>
<li>每个任务都有一个任务类型，同一个类型的任务必须在一个队列，不同类型的任务可以分属于不同的队列。<br>
在一次事件循环中，浏览器可以根据实际情况从不同的队列中取出任务执行。</li>
<li>浏览器必须准备好一个微队列，微队列中的任务优先所有其他任务执行<br>
<a href="https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint" target="_blank" rel="noopener noreferrer">https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint</a></li>
</ul>
<blockquote>
<p>随着浏览器的复杂度急剧提升，W3C 不再使用宏队列的说法</p>
</blockquote>
<p>在目前 chrome 的实现中，至少包含了下面的队列：</p>
<ul>
<li>延时队列：用于存放计时器到达后的回调任务，优先级「中」</li>
<li>交互队列：用于存放用户操作后产生的事件处理任务，优先级「高」</li>
<li>微队列：用户存放需要最快执行的任务，优先级「最高」</li>
</ul>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>添加任务到微队列的主要方式主要是使用 Promise、MutationObserver</p>
<p>例如：</p>
<div class="language-js" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 立即把一个函数添加到微队列</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Promise</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">resolve</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">().</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">then</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">函数</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span></code></pre>
</div></div>
<blockquote>
<p>浏览器还有很多其他的队列，由于和我们开发关系不大，不作考虑</p>
</blockquote>
<blockquote>
<p>面试题：阐述一下 JS 的事件循环</p>
<p>参考答案：</p>
<p>事件循环又叫做消息循环，是浏览器渲染主线程的工作方式。</p>
<p>在 Chrome 的源码中，它开启一个不会结束的 for 循环，每次循环从消息队列中取出第一个任务执行，而其他线程只需要在合适的时候将任务加入到队列末尾即可。</p>
<p>过去把消息队列简单分为宏队列和微队列，这种说法目前已无法满足复杂的浏览器环境，取而代之的是一种更加灵活多变的处理方式。</p>
<p>根据 W3C 官方的解释，每个任务有不同的类型，同类型的任务必须在同一个队列，不同的任务可以属于不同的队列。不同任务队列有不同的优先级，在一次事件循环中，由浏览器自行决定取哪一个队列的任务。但浏览器必须有一个微队列，<mark>微队列的任务一定具有最高的优先级，必须优先调度执行</mark>。</p>
</blockquote>
<blockquote>
<p>面试题：JS 中的计时器能做到精确计时吗？为什么？</p>
<p>参考答案：</p>
<p>不行，因为：</p>
<ol>
<li>计算机硬件没有原子钟，无法做到精确计时</li>
<li>操作系统的计时函数本身就有少量偏差，由于 JS 的计时器最终调用的是操作系统的函数，也就携带了这些偏差</li>
<li>按照 W3C 的标准，浏览器实现计时器时，如果嵌套层级超过 5 层，则会带有 4 毫秒的最少时间，这样在计时时间少于 4 毫秒时又带来了偏差</li>
<li>受事件循环的影响，计时器的回调函数只能在主线程空闲时运行，因此又带来了偏差</li>
</ol>
</blockquote>
]]></content:encoded>
      <enclosure url="https://s3.bmp.ovh/imgs/2023/04/11/9e81b88e42fe104f.png" type="image/png"/>
    </item>
    <item>
      <title>Vue常见优化手段</title>
      <link>https://www.linkjm.com/posts/Web/Vue/optimization.html</link>
      <guid>https://www.linkjm.com/posts/Web/Vue/optimization.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Vue常见优化手段</source>
      <description>相关信息 永远不要过早优化，优化也有相应的代价 开发时间变长 开发成本增加 代码难以阅读 增加维护成本 何时优化，因地制宜，是一门艺术，尽量把优化的思想带入写代码的过程中 本文章的优化手段基于vue2</description>
      <category>Web</category>
      <pubDate>Wed, 17 May 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p>永远不要过早优化，优化也有相应的代价</p>
<ul>
<li>开发时间变长</li>
<li>开发成本增加</li>
<li>代码难以阅读</li>
<li>增加维护成本</li>
</ul>
<p>何时优化，因地制宜，是一门艺术，尽量把优化的思想带入写代码的过程中</p>
</div>
<p>本文章的优化手段基于<code>vue2</code></p>
<!-- more -->
<h2>服务端渲染 SSR or 预渲染</h2>
<p>客户端渲染：使用 JavaScript 框架进行页面渲染<br>
服务端渲染：服务端将HTML文本组装好，并返回给浏览器，这个HTML文本被浏览器解析之后，不需要经过 JavaScript 脚本的执行，即可直接构建出希望的 DOM 树并展示到页面中，最后将这些静态标记&quot;激活&quot;为客户端上完全可交互的应用程序。</p>
<p><strong>优点：</strong></p>
<p>更好的 SEO，由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。对客户端渲染的页面来说，简直无能为力，因为返回的HTML是一个空壳，它需要执行 JavaScript 脚本之后才会渲染真正的页面。<br>
用户将会更快速地看到完整渲染的页面</p>
<p><strong>缺点:</strong></p>
<p>为了实现服务端渲染，应用代码中需要兼容服务端和客户端两种运行情况<br>
由于服务器增加了渲染HTML的需求，使得原本只需要输出静态资源文件的nodejs服务，新增了数据获取的IO和渲染HTML的CPU占用，<br>
服务器渲染应用程序，需要处于 Node.js server 运行环境。</p>
<h3>如何实现？</h3>
<p>想要在服务器端渲染，我们需要做什么呢？那就是同构我们的项目，Vue.js 是构建客户端应用程序的框架，服务器渲染的 Vue.js 应用程序也可以被认为是&quot;同构&quot;或&quot;通用&quot;，因为应用程序的大部分代码都可以在服务器和客户端上运行</p>
<p>当运行在不同环境中时，我们的代码将不会完全相同，同构就是让一份代码，既可以在服务端中执行，也可以在客户端中执行，并且执行的效果都是一样的，都是完成这个html的组装，正确的显示页面。<br>
对于同构应用来说，我们必须实现客户端与服务端的路由、模型组件、数据模型的共享。</p>
<h3>服务器端渲染注意事项</h3>
<p>为避免造成交叉请求状态污染，每个请求应该都是全新的、独立的应用程序实例。<br>
由于没有动态更新，所有的生命周期钩子函数中，只有 beforeCreate 和 created 会在服务器端渲染(SSR)过程中被调用。<br>
通用代码不可接受像 window 或 document，这种仅浏览器可用的全局变量<br>
浏览器可能会更改的一些特殊的 HTML 结构，例如，浏览器会在</p>
<p>内部自动注入 ，然而，由于 Vue 生成的虚拟 DOM(virtual DOM) 不包含 ，所以会导致无法匹配。</p>
<h2>使用key</h2>
<p>对于通过循环生成的列表，应给每个列表项一个稳定且唯一的key，这有利于在列表变动时，尽量少的删除、新增、改动元素</p>
<h2>使用冻结的对象</h2>
<p>冻结的对象不会被响应化，如果对象很多，嵌套结构很深，遍历过程需要花费很多时间，如果对象不需要动态更改，可以使用冻结对象，如：商品列表等纯展示页面，并不会通过用户交互来更改</p>
<div class="language-js" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> obj</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">b</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 冻结对象</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Object</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">freeze</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">obj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 尝试更改</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">obj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">obj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 打印</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">b</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 验证</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Object</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">isFrozen</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">obj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 结果</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">&#x3C;</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">true</span></span></code></pre>
</div><p>vue在处理过程中，如果发现对象是冻结对象，就不会去遍历对象，不会变成响应式</p>
<h4>下面是1000000个对象的加载过程</h4>
<p><strong>vue</strong></p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/05/17/225d8b0bb0b07ab1.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p><strong>冻结对象</strong></p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/05/17/3c863e0c03a3a382.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>可见vue把对象深度遍历成为响应式，对于大量结构复杂的数据来说，是很耗时间的</p>
<h2>使用函数式组件</h2>
<p>函数式组件，设置<code>functional:true</code>，函数式组件没有<code>data</code>，这以为它无状态（没有<mark>响应式数据</mark>）</p>
<p>，也没用实例（没有<code>this</code>上下文），所以组件树中不存在函数式组件，一个函数式组件就像这样</p>
<div class="language-js" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Vue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">component</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'my-component'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  functional</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">true</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Props 是可选的</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  props</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // ...</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // 为了弥补缺少的实例</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // 提供第二个参数作为上下文</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  render</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">createElement</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">context</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // ...</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">})</span></span></code></pre>
</div><blockquote>
<p>注意：在 2.3.0 之前的版本中，如果一个函数式组件想要接收 prop，则 <code>props</code> 选项是必须的。在 2.3.0 或以上的版本中，你可以省略 <code>props</code> 选项，所有组件上的 attribute 都会被自动隐式解析为 prop。</p>
<p>当使用函数式组件时，该引用将会是 HTMLElement，因为他们是无状态的也是无实例的。</p>
</blockquote>
<p>在 2.5.0 及以上版本中，如果你使用了<a href="https://v2.cn.vuejs.org/v2/guide/single-file-components.html" target="_blank" rel="noopener noreferrer">单文件组件</a>，那么基于模板的函数式组件可以这样声明：</p>
<div class="language-vue" data-highlighter="shiki" data-ext="vue" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-vue"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">template</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> functional</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">template</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><p>组件需要的一切都是通过 <code>context</code> 参数传递，它是一个包括如下字段的对象：</p>
<ul>
<li><code>props</code>：提供所有 prop 的对象</li>
<li><code>children</code>：VNode 子节点的数组</li>
<li><code>slots</code>：一个函数，返回了包含所有插槽的对象</li>
<li><code>scopedSlots</code>：(2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。</li>
<li><code>data</code>：传递给组件的整个<a href="https://v2.cn.vuejs.org/v2/guide/render-function.html#%E6%B7%B1%E5%85%A5%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1" target="_blank" rel="noopener noreferrer">数据对象</a>，作为 <code>createElement</code> 的第二个参数传入组件</li>
<li><code>parent</code>：对父组件的引用</li>
<li><code>listeners</code>：(2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 <code>data.on</code> 的一个别名。</li>
<li><code>injections</code>：(2.3.0+) 如果使用了 <a href="https://v2.cn.vuejs.org/v2/api/#provide-inject" target="_blank" rel="noopener noreferrer"><code>inject</code></a> 选项，则该对象包含了应当被注入的 property。</li>
</ul>
<p>在添加 <code>functional: true</code> 之后，需要更新我们的锚点标题组件的渲染函数，为其增加 <code>context</code> 参数，并将 <code>this.$slots.default</code> 更新为 <code>context.children</code>，然后将 <code>this.level</code> 更新为 <code>context.props.level</code>。</p>
<p>因为函数式组件只是函数，所以渲染开销(<strong>时间</strong>和<strong>内存</strong>)也低很多。</p>
<p>在作为包装组件时它们也同样非常有用。比如，当你需要做这些时：</p>
<ul>
<li>程序化地在多个组件中选择一个来代为渲染；</li>
<li>在将 <code>children</code>、<code>props</code>、<code>data</code> 传递给子组件之前操作它们。</li>
</ul>
<p>下面是一个 <code>smart-list</code> 组件的例子，它能根据传入 prop 的值来代为渲染更具体的组件：</p>
<div class="language-js line-numbers-mode" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> EmptyList</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/* ... */</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> TableList</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/* ... */</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> OrderedList</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/* ... */</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> UnorderedList</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/* ... */</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Vue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">component</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'smart-list'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  functional</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">true</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  props</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    items</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">      type</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">Array</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">      required</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">true</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    isOrdered</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">Boolean</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  render</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">createElement</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">context</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> appropriateListComponent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> items</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> context</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">items</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">items</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">length</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)           </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">return</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> EmptyList</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">typeof</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> items</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">===</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> 'object'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">return</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> TableList</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">context</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">isOrdered</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)      </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">return</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> OrderedList</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      return</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> UnorderedList</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> createElement</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">      appropriateListComponent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(),</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      context</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      context</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">children</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    )</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">})</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>使用计算属性</h2>
<p>如果模版中某个数据会使用多次，并且该数据是通过计算得到的，使用计算属性以缓存它们</p>
<h2>非实时绑定的表单项</h2>
<p>当使用<code>v-model</code>绑定一个表单项时，当用户改变表单项的状态时，也会随之改变数据，从而导致vue发生重新渲染（<code>rerender</code>），这会带来一些性能的开销。</p>
<p>我们可以通过使用<code>lazy</code>或不使用<code>v-model</code>的方式解决该问题，但要注意，这样可能会导致在某一个时间段内数据和表单项的值是不一致的。</p>
<p>vue设计思想是关注的是数据而不是界面，代码的可维护性和可阅读性也很重要，js执行线程和浏览器渲染线程是互斥的，所以运行动画时执行jS线程动画会卡顿</p>
<p>如双向绑定的文本框输入的内容改变，输入abcd，会进行4次重新渲染，可以使用<code>v-model.lazy</code>,监听<code>@change</code>，不使用监听的是<code>@input</code></p>
<h2>保持对象引用稳定</h2>
<p>在绝大部分情况下，<code>vue</code>出发<code>rerender</code>的时机是其依赖的数据发生<strong>变化</strong></p>
<p>若数据没有发生变化，哪怕给数据重新赋值了，<code>vue</code>也不会做出任何处理的</p>
<p>下面是vue判断数据<strong>没有变化</strong>的源码</p>
<div class="language-js" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> hasChanged</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">x</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">y</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">x</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> y</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> x</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> &#x26;&#x26;</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> /</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> x</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> !==</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">y</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> x</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> x</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> y</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> y</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>因此，如果需要，只要能保证组件的依赖数据不发生变化，组件就不会重新渲染</p>
<p>对于原始数据类型，保持其值不变即可</p>
<p>对于对象类型，保持其引用不变即可</p>
<h2>使用v-show替代v-if</h2>
<p>对于频繁切换显示状态的元素，使用v-show可以保证虚拟的dom树的稳定，避免频繁的新增和删除元素，特别是对于那些内部包含大量dom元素的节点，这一点及其重要</p>
<h2>使用延迟装载defer</h2>
<p>首页白屏时间主要受到两个因素的影响：</p>
<ul>
<li>
<p>打包体积过大</p>
<p>巨型包需要消耗大量的传输时间，导致JS传输完成前页面只有一个<code>&lt;div&gt;</code>，没有可显示的内容</p>
</li>
<li>
<p>需要立即渲染的内容太多</p>
</li>
</ul>
<p>JS传输完成后，浏览器开始执行JS构造页面</p>
<p>但可能一开始要渲染的组件太多，不仅JS的事件很长，而且执行完后浏览器要渲染的元素过多，从而导致页面白屏loading过久</p>
<p>一个可行的办法就是延迟装载组件，让组件按照指定的先后顺序依次一个一个渲染出来</p>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>延迟装载是一个思路，本质上就是利用<code>requestAnimationFrame</code>事件分批渲染内容，它的具体实现多种多样</p>
</div>
<p>告诉浏览器——你希望执行一个动画，并且要求浏览器在下次重绘之前调用指定的<a href="https://so.csdn.net/so/search?q=%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0&amp;spm=1001.2101.3001.7020" target="_blank" rel="noopener noreferrer">回调函数</a>更新动画。该方法需要传入一个回调函数作为参数，该回调函数会在浏览器下一次重绘之前执行</p>
<p><code>callback</code>： 下一次重绘之前更新动画帧所调用的函数(即上面所说的回调函数)。该回调函数会被传入<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/DOMHighResTimeStamp" target="_blank" rel="noopener noreferrer">DOMHighResTimeStamp</a>参数，该参数与<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Performance/now" target="_blank" rel="noopener noreferrer">performance.now()</a>的返回值相同，它表示<code>requestAnimationFrame()</code> 开始去执行回调函数的时刻。</p>
<p>思路：浏览器渲染1s渲染60次，第一次渲染一部分，第二次一部分，隔开渲染,分批绘制</p>
<div class="language-js line-numbers-mode" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// defer.js</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> default</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">maxFrameCount</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(){</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 浏览器每重绘一次，计数</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        frameCount</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      };</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    mounted</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      const</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> refreshFrameCount</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> () </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">        requestAnimationFrame</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(() </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        	this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">frameCount</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">++</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">          if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">frameCount</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> maxFrameCount</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">            refreshFrameCount</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">          }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        });</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      };</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">      refreshFrameCount</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    methods</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">      defer</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">showInFrameCount</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 用于v-if 的判断条件，渲染次数大于showInFrameCount后继续下一次渲染</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">frameCount</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> >=</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> showInFrameCount</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  };</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-vue line-numbers-mode" data-highlighter="shiki" data-ext="vue" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-vue"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">template</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"container"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    &#x3C;!--vue3.0 v-if 优先级高 vue2.x v-for优先级高--></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"block"</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> v-for</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"n in 20"</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> v-if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"defer(n)"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">heavy-comp</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">heavy-comp</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">template</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">import</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> defer</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> from</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "./mixin/defer"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> default</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  mixins</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: [</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">defer</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">300</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)],</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  components</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: { </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">HeavyComp</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>使用keep-alive</h2>
<p>用于缓存内部组件实例，里面有include和exclude属性，max设置最大缓存数，超过后，自动删除最久没用的。</p>
<p>受到keep-alive影响，其内部的组件都具有两个生命周期，<code>activated</code>和<code>deactivated</code> ,分别再组件激活和失活时触发，第一次<code>activated</code>是在<code>mounted</code>之后。</p>
<p>一般用在需要多个页面频繁操作的场景（导航条）</p>
<h2>长列表优化</h2>
<p>一般用在app端下拉的时候，或者列表很长的时候，通过一个固定大小的渲染池来解决。通过滚动条等一些操作，减少页面渲染市场，有现成的库，vue-virtual-scroller</p>
<p><a href="https://github.com/Akryum/vue-virtual-scroller" target="_blank" rel="noopener noreferrer">https://github.com/Akryum/vue-virtual-scroller</a></p>
<p>通过v-once创建低开销的静态组件，渲染一次后就缓存起来了，除非你非常留意渲染速度，不然最好不要用，因为有的开发者不知道这个属性或者看漏了，然后花费好几个小时来找为什么模板无法正确更新。</p>
<h2>打包体积优化</h2>
<ul>
<li>Webpack 对图片进行压缩</li>
<li>静态资源的优化使用对象存储加CDN</li>
<li>减少 ES6 转为 ES5 的冗余代码</li>
<li>提取公共代码</li>
<li>模板预编译</li>
<li>提取组件的 CSS</li>
<li>优化 SourceMap</li>
<li>构建结果输出分析</li>
<li>Vue 项目的编译优化</li>
</ul>
<h2>基础优化</h2>
<ul>
<li>开启 gzip 压缩</li>
<li>浏览器缓存</li>
<li>CDN 的使用</li>
<li>使用 Chrome Performance 查找性能瓶颈</li>
</ul>
]]></content:encoded>
      <enclosure url="https://w.wallhaven.cc/full/57/wallhaven-577og5.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Vue2响应式原理解析</title>
      <link>https://www.linkjm.com/posts/Web/Vue/vue2-principle.html</link>
      <guid>https://www.linkjm.com/posts/Web/Vue/vue2-principle.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Vue2响应式原理解析</source>
      <description>前言 首先要知道vue2 是2013年 基于 ES5开发出来的，我们常说的重渲染就是重新运行render函数 vue2 的响应式原理是利⽤ES5 的⼀个 API ，Object.defineProperty()对数据进⾏劫持结合发布订阅模式的⽅式来实现的。</description>
      <category>Web</category>
      <pubDate>Tue, 16 May 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<div class="hint-container info">
<p class="hint-container-title">前言</p>
<p>首先要知道vue2 是2013年 基于 ES5开发出来的，我们常说的重渲染就是重新运行<code>render</code>函数</p>
<p>vue2 的响应式原理是利⽤ES5 的⼀个 API ，<code>Object.defineProperty()</code>对数据进⾏劫持结合发布订阅模式的⽅式来实现的。</p>
</div>
<!-- more -->

<h2>1. 思路</h2>
<p>带入作者的角度思考一下，想要达成响应式的特点应该是:<strong>属性更新，自动调用依赖<sup class="footnote-ref"><a href="#footnote1">[1]</a><a class="footnote-anchor" id="footnote-ref1"></a></sup>的函数</strong>进行重新渲染</p>
<ol>
<li>使用属性描述符<code>Object.defineProperty</code>监听属性的赋值</li>
<li>赋值完成后调用依赖该属性的函数，那如何获取依赖的函数呢？看第三点</li>
<li>由于依赖会调用属性的get方法，所以可以在get方法中<strong>收集依赖</strong></li>
<li>然后在set方法中执行这些依赖的函数，称为<strong>派发更新</strong></li>
</ol>
<p>基于以上思路的简单实现代码：</p>
<div class="language-js line-numbers-mode" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// euv.js</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/**</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> * 观察某个对象的所有属性</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> * </span><span style="--shiki-light:#C678DD;--shiki-light-font-style:italic;--shiki-dark:#C678DD;--shiki-dark-font-style:italic">@param</span><span style="--shiki-light:#E5C07B;--shiki-light-font-style:italic;--shiki-dark:#E5C07B;--shiki-dark-font-style:italic"> {Object}</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic"> obj</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"> */</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> observe</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">obj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  for</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> key</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> in</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> obj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    let</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> internalValue</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> obj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    let</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> funcs</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [];</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    Object</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">defineProperty</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">obj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">      get</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        //  依赖收集，记录：是哪个函数在用我</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">__func</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> &#x26;&#x26;</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> !</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">funcs</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">includes</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">__func</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)) {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">          funcs</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">push</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">__func</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> internalValue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">      set</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">val</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        internalValue</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> val</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 派发更新，运行：执行用我的函数</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        for</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> i</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> &#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> funcs</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">length</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">++</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">          funcs</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">]();</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> autorun</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">fn</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">__func</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> fn</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  fn</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">__func</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-js line-numbers-mode" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// index.js</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> user</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  name</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'有骨气'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  birth</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'1998-4-7'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">observe</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">user</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">); </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 观察</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 显示姓氏</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> showFirstName</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  document</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'#firstName'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> '姓：'</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> +</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> user</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">name</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 显示名字</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> showLastName</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  document</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'#lastName'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> '名：'</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> +</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> user</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">name</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">slice</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 显示年龄</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> showAge</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> birthday</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> new</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> Date</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">user</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">birth</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> today</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> new</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> Date</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  today</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">setHours</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">), </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">today</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">setMinutes</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">), </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">today</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">setMilliseconds</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">  thisYearBirthday</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> new</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> Date</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    today</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">getFullYear</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(),</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    birthday</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">getMonth</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(),</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    birthday</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">getDate</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  );</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> age</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> today</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">getFullYear</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">-</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> birthday</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">getFullYear</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">today</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">getTime</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> thisYearBirthday</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">getTime</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">()) {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    age</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">--</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  document</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'#age'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> '年龄：'</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> +</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> age</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">autorun</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">showFirstName</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">autorun</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">showLastName</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">autorun</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">showAge</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>2. 原理</h2>
<p>vue2响应式原理简单来说就是vue官网上的这图片</p>
<figure><img src="https://v2.cn.vuejs.org/images/data.png" alt="原理图" tabindex="0" loading="lazy"><figcaption>原理图</figcaption></figure>
<p>通过 <code>Object.defineProperty</code> 遍历对象的每一个属性，把每一个属性变成一个 <code>getter</code> 和 <code>setter</code> 函数，读取属性的时候调用 <code>getter</code>，给属性赋值的时候就会调用 <code>setter</code>.</p>
<p>当运行 <code>render</code> 函数的时候,发现用到了响应式数据，这时候就会运行 <code>getter</code> 函数，然后 watcher（发布订阅）就会记录下来。当响应式数据发生变化的时候，就会调用 <code>setter</code> 函数，watcher 就会再记录下来这次的变化，然后通知 <code>render</code> 函数，数据发生了变化，然后就会重新运行 <code>render</code> 函数，重新生成虚拟 dom 树。</p>
<h2>3. 深入了解</h2>
<p>我们要明白，响应式的最终目标：<strong>是当对象本身或对象属性发生变化时，会运行一些函数，最常见的就是 render 函数。不是只有 render，只要数据发生了变化后运行了一些函数，就是响应式,比如 watch。</strong></p>
<p>在具体实现上，vue 采用了几个核心部件:</p>
<ol>
<li>
<p><strong>Observer</strong></p>
</li>
<li>
<p><strong>Dep</strong></p>
</li>
<li>
<p><strong>Watcher</strong></p>
</li>
<li>
<p><strong>Scheduler</strong></p>
</li>
</ol>
<h2>4. Observer</h2>
<p>observer 要实现的目标非常简单，就是把一个普通的对象转换成响应式的对象</p>
<p>为了实现这一点，observer 把对象的每个属性通过 <code>object.defineProperty</code> 转换为带有 <code>getter</code> 和 <code>setter</code> 的属性，这样一来，当访问或者设置属性时，vue 就会有机会做一些别的事情。</p>
<p><mark>在组件的生命周期中，这件事发生在 <code>beforeCreate</code> 之后，create 之前。</mark></p>
<p>具体实现上，他会递归遍历对象的所有属性，以完成深度的属性转换。</p>
<p>但是由于遍历只能遍历到对象的当前属性，无法监测到将来动态添加或者删除的属性，因此 vue 提供了<code>$set</code>和<code>$delete</code> 两个实例方法，但是 vue 并不提倡这样使用，我讲到 dep 的时候我再说为什么。</p>
<p>对于数组的话，vue 会更改它的隐式原型，之所以这样做是因为 vue 需要监听那些可能改变数组内容的方法。</p>
<p>数组 --&gt; vue 自定义的对象 --&gt; Array.prototype</p>
<p>总之，observer 的目标，就是要让一个对象，它属性的读取，赋值，内部数组的变化都要能够被 vue 感知到。</p>
<h3>4.1. 手动转换响应式对象</h3>
<p>Vue提供了静态方法：<code>Vue.observable()</code> 手动将普通对象转为响应式对象。</p>
<div class="language-js line-numbers-mode" data-highlighter="shiki" data-ext="js" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> obj</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    a</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    b</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    c</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        d</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        e</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">4</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    f</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: [</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">            a</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">            b</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        },</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">        3</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">4</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">5</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">6</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    ]</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 利用Vue提供的静态方法 .observable, 将一个普通对象转化为响应式对象</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> reactiveObj</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> Vue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">observable</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">obj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">reactiveObj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>4.2. data</h3>
<p>Vue不允许动态添加根级响应式属性，所以需要在组件实例化之前通过配置中的 <code>data</code> 字段，声明所有根级响应式属性，哪怕属性值为 <code>null</code>。由此带来的好处有：</p>
<ol>
<li>更易于维护： data对象就像组件的状态结构（schema）, 提前声明所有响应式属性，后期有助于开发者理解和修改组件逻辑。</li>
<li>消除了在依赖项跟踪系统中的一类边界情况。</li>
<li>使Vue实例能够更好的配合类型检查系统工作。</li>
</ol>
<h3>4.3. 「动态添加或删除属性」</h3>
<p>由于Vue会在初始化实例时，对所有属性（配置里 <code>data</code> 中存在的属性）执行 getter/setter 的转化。</p>
<p>那么对于 <strong>「动态添加或删除」</strong> 的属性，Vue是无法自动检查其变化。</p>
<p>因此，Vue提供了以下方式来手动完成响应式数据。</p>
<ol>
<li>添加：<strong>「Vue.set(target, key, val)」</strong> 或 <strong>「this.$set(target, key, val)」</strong></li>
<li>删除：<strong>「Vue.delete(target, key)」</strong> 或 <strong>「this.$delete(target, key)」</strong></li>
<li>批量操作：<code>this.reactiveObj = Object.assign({}, this.reactiveObj, obj)</code></li>
</ol>
<p>举个例子：</p>
<div class="language-vue line-numbers-mode" data-highlighter="shiki" data-ext="vue" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-vue"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">template</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"demo-wrapper"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>obj.a -> {{ obj.a }}， obj.b -> {{ obj.b }}&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">p</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        &#x3C;!-- 非响应式式数据操作 --></span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        &#x3C;!-- </span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">            &#x3C;button @click="obj.b = 2">add obj.b&#x3C;/button></span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">            &#x3C;button @click="delete obj.a">delete obj.a&#x3C;/button></span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        --></span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        &#x3C;!-- 响应式数据操作 --></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">button</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> @click</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"$set(obj, 'b', 2)"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>add obj.b&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">button</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        &#x26;nbsp;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">button</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> @click</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"$delete(obj, 'a')"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>delete obj.a&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">button</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    &#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">div</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">template</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> default</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">            obj</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">                a</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">            },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        };</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>4.4. <strong>「关于数组」</strong></h3>
<p>由于js的限制， Vue不能检测到以下数组变动：</p>
<ol>
<li>当利用索引直接改变数组项时， 例如：<code>vm.arr[idx] = newValue</code></li>
<li>当修改数组长度时 ，例如： <code>vm.arr.length = newLength</code></li>
</ol>
<p>举个例子：</p>
<div class="language-vue line-numbers-mode" data-highlighter="shiki" data-ext="vue" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-vue"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> default</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">            arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: [</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">4</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">],</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        };</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    created</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">lesson4</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    mounted</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 8</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 不是响应式的</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">length</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//不是响应式的</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>为了让上述数组操作具有响应式，采用以下方法处理：</p>
<div class="language-vue line-numbers-mode" data-highlighter="shiki" data-ext="vue" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-vue"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> default</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">            arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: [</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">4</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">],</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        };</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    created</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">lesson4</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    mounted</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">				// 操作一：通过索引修改数组项</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">$set</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">8</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">); </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 响应式的</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 或</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        Vue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">set</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">8</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);  </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 响应式的</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 或</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">splice</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">8</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//响应式的</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      </span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 操作二：修改数组长度为2</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">splice</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 响应式的</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>除了可以通过静态方法 <code>Vue.set()</code> 和 实例方法 <code>this.$set()</code> 响应式的修改数组项的值。还可以使用数组方法 - <code>splice()</code> 。</p>
<p>因为，Vue对一些可以改变数组自身内容的操作API，如：splice()、sort()、push()、pop()、reverse()、shift()、unshift() 等进行了拦截和重写。从而在开发者使用这些API时，可以触发响应式数据，进而更新视图。</p>
<div class="language-vue" data-highlighter="shiki" data-ext="vue" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-vue"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> default</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: [</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">4</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">]</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    mounted</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_proto_</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> Array</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">prototype</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)  </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// => false</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">arr</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">_proto_</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_proto_</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> Array</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">prototype</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)  </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//=> true</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><h2>5. Dep</h2>
<p>这里有两个问题没解决，就是读取属性时要做什么事，属性变化时又要做什么事，这个问题就得需要 dep 来解决</p>
<p>dep 的含义是 dependency 表示依赖的意思。</p>
<p>vue 会为响应式对象中的每一个属性，对象本身，数组本身创建一个 dep 实例，每个 dep 实例都可以做两件事情：</p>
<p>1，记录依赖：是谁在用我</p>
<p>2，派发更新：我变了，我要通知那些用我的人</p>
<p>当读取响应式对象的某一个属性时，他会进行依赖收集，有人用到了我</p>
<p>当改变某个属性时，他会派发更新，那些用我的人听好了，我变了</p>
<p>为什么尽量不要使用<code>$set</code> <code>$delete</code> ?</p>
<p>因为如果模板上没有用到值的话，你凭空加了一个数据,理论上来说应该不会重新运行<code>render</code>函数，但是上一级的dep发现自身发生改变了，所以也会导致重新运行render函数。</p>
<p>所以vue不建议使用<code>$set</code> 和<code>$delete</code>,最好提前先写上数据,哪怕先给数据赋值为 null;</p>
<h2>6. watcher</h2>
<p>这里又出现了一个问题，就是 dep 如何知道是谁在用我呢</p>
<p>watcher 就解决了这个问题</p>
<p>当函数执行的过程中，用到了响应式数据，响应式数据是无法知道是谁在用自己的</p>
<p>所以，我们不要直接执行函数，而是把函数交给一个 watcher 的东西去执行，watch 是一个对象，每个函数执行时都应该创建一个 watcher，通过 wacher 去执行</p>
<p>watcher 会创建一个全局变量，让全局变量记录当前负责执行的 watcher 等于自己，然后再去执行函数，在函数执行的过程中，如果发生了依赖记录，那么 dep 就会把这个全局变量记录下来，表示有一个 wathcer 用到了我这个属性。</p>
<p>当 dep 进行派发更行时，他会通知之前记录的所有 watcher，我变了。</p>
<h2>7. Scheduler</h2>
<p>现在还剩下最后一个问题啊，就是 dep 通知 watcher 之后，如果 wathcer 执行重新运行对应的函数，就有可能导致频繁运行，从而导致效率低下，试想，如果一个交给 watcher 的函数，它里面用到了属性 a,b,c,d,那么 a,b,c,d 都会记录依赖，然后这四个值都以此重新赋值，那么就会触发四次更新，这样显然不行啊，所以当 watcher 收到派发更新的通知后，实际上并不是立即执行，而是通过一个叫做 nextTick 的工具方法，把这些需要执行的 watcher 放到事件循环的微队列，nextTick 是通过 Promise then 来完成的。</p>
<p>也就是说，在响应式数据发生变化时，render 函数执行是异步的，并且在微队列中。</p>
<h2>8. <strong>异步更新队列</strong></h2>
<blockquote>
<p>Vue侦听到数据变化，就会开启一个队列。但是组件不会立即重新渲染，而是先会缓冲在同一个事件循环中的发生的所有数据变化。此时如果同一个watcher被多次触发，只会被推入到队列中一次，这样可以<strong>「避免不必要的计算和DOM更操作」</strong>。<br>
在下一个事件循环”tick“中， Vue刷新队列并执行实际（已去重的）工作（更新渲染）。<br>
为此，Vue提供了异步更新的监听接口 —— <code>Vue.nextTick(callback)</code> 或 <code>this.$nextTick(callback)</code> 。当数据发生改变，异步DOM更新完成后，callback回调将被调用。开发者可以在回调中，操作更新后的DOM。</p>
</blockquote>
<p><strong>「举例1」</strong></p>
<div class="language-vue line-numbers-mode" data-highlighter="shiki" data-ext="vue" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-vue"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> default</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">            a</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">            b</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">            c</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">            d</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">4</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        };</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    methods</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">        changeAllData</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">            this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">$nextTick</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">                var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> pre</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> document</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"pre"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">                console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">pre</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">            });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">            this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">b</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">c</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">d</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 10</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">            this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">$nextTick</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">                var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> pre</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> document</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"pre"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">                console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">pre</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">            });</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    render</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">h</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'render function'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> h</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'div'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, [</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">            h</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'pre'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">`</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">${</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">}</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">, </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">${</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">b</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">}</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">, </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">${</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">c</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">}</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">, </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">${</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">d</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">}</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">`</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">),</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">            h</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'button'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">                on</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">                    click</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: () </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">                        this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">changeAllData</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">                    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">                }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">            }, </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'change all data'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        ])</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上例，通过一个NextTick组件的渲染，了解下 的用法。为了方便查看组件渲染时，<code>render</code>函数被调用的过程，在组件定义时，直接给出<code>render</code>函数。当点击按钮后，会在数据修改前后，使用<code>nextTick</code>工具方法。分别写入两个读取界面Dom的函数。结果会发现，第一个 <code>$nextTick</code> 回调函数获取的数据为旧数据，第二个 <code>$nextTick</code>回调函数获取的数据为新数据。</p>
<p>分析一下：</p>
<p>按钮点击后，异步队列的添加步骤是：</p>
<ol>
<li>第一个 <code>$nextTick</code> ，会将自己的回调函数（fn1）加入到当前的异步队列中。</li>
<li>修改数据后， 经过派发更新，Scheduler会将包含了watcher队列执行逻辑的函数（fn2）加入到当前的异步队列中。</li>
<li>第二个 <code>$nextTick</code>， 已将自己的回调函数（fn3）加入到当前的异步队列中。</li>
</ol>
<p>当异步队列执行时，会依次执行 fn1 ， fn2，fn3。而当fn2执行后，界面才会更新最新数据，所以fn1，fn3获取的界面数据前者为旧数据，后者为新数据。</p>
<p><strong>「举例2」</strong></p>
<div class="language-vue line-numbers-mode" data-highlighter="shiki" data-ext="vue" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-vue"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">template</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  &#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">span</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>{{a}}&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">span</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">template</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">	export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> default</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        a</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'hello'</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    mounted</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> 'world'</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">$el</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)  </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// -> 'hello'</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">$nextTick</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">$el</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// -> 'world'</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      })</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上面代码，当设置 <code>this.a = 'world'</code> 后，访问DOM元素内容，但完成未更新。此时，立即使用 <code>this.$nextTick()</code> 监听DOM更新，并在监听回调调用时，获取更新后的DOM内容。</p>
<p>另外， <code>this.$nextTick()</code> 其内部尝试使用原生的 <code>Promise.then</code>、<code>MutationObserve</code>、<code>setImmediate</code>，如果执行环境不支持，则会采用 <code>setTimeout</code> 替代。并且最终返回一个<code>Promise</code>对象，所以可以使用 <code>async/await</code> 语法替代 <code>callback</code>的写法。</p>
<div class="language-vue line-numbers-mode" data-highlighter="shiki" data-ext="vue" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-vue"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">	export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> default</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">        a</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'hello'</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">      }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // $nextTick 结合 async/await语法使用</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    async</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> mounted</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> 'world'</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">$el</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)  </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// -> 'hello'</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      await</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">$nextTick</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">$el</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// -> 'world'</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;/</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">script</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>9. 总流程图</h2>
<figure><img src="https://s3.bmp.ovh/imgs/2023/05/16/6ebf6f55fe1cd59e.webp" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="footnote1" class="footnote-item"><p>依赖：某个函数在运行期间用到这个属性的get方法 <a href="#footnote-ref1" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
]]></content:encoded>
      <enclosure url="https://w.wallhaven.cc/full/5w/wallhaven-5wmyo8.jpg?x-oss-process=image/resize,limit_0,m_fill,w_1366,h_768/quality,Q_92/format,webp" type="image/"/>
    </item>
    <item>
      <title>一个 waline 评论系统bug引发的思考</title>
      <link>https://www.linkjm.com/posts/Web/Vue/waline-bug.html</link>
      <guid>https://www.linkjm.com/posts/Web/Vue/waline-bug.html</guid>
      <source url="https://www.linkjm.com/rss.xml">一个 waline 评论系统bug引发的思考</source>
      <description>提示 前言： Waline 评论系统这个 bug 有几个月了，现象就是回复完其他人的评论后，评论内容会保留在顶部输入框中，而且不会自动清空。具体见 👉GitHub issuse #2173 许久未见修复，正好我有空，就看看这个问题，接下来跟我一起分析一下这个问题 1. 定位问题 问题现象上边已经描述过了，我们先来定位一下问题。 1.1. 关键 关键在...</description>
      <category>Web</category>
      <pubDate>Thu, 23 May 2024 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>前言： Waline 评论系统这个 bug 有几个月了，现象就是回复完其他人的评论后，评论内容会保留在顶部输入框中，而且不会自动清空。具体见 👉<a href="https://github.com/walinejs/waline/issues/2371" target="_blank" rel="noopener noreferrer">GitHub issuse #2173</a></p>
<p>许久未见修复，正好我有空，就看看这个问题，接下来跟我一起分析一下这个问题</p>
</div>
<h2>1. 定位问题</h2>
<p>问题现象上边已经描述过了，我们先来定位一下问题。</p>
<h3>1.1. 关键</h3>
<p>关键在于【不会清空】，即使刷新浏览器，也不会清空，由此可知必然保存在 localStorage 中，打开开发者工具，看看保存在 localStorage 中的数据结构。发现如下图所示</p>
<figure><img src="https://s3.bmp.ovh/imgs/2024/05/23/54fa93eb4563daea.png" alt="localStorage" tabindex="0" loading="lazy"><figcaption>localStorage</figcaption></figure>
<p>然后有了<strong>key</strong>就好办了</p>
<h3>1.2. 源码</h3>
<p>在 Waline 评论系统源码中，我们全局搜索 <code>WALINE_COMMENT_BOX_EDITOR</code> 结果如下</p>
<div class="language-ts line-numbers-mode" data-highlighter="shiki" data-ext="ts" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-ts"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// walinejs/packages/client/src/composables/inputs.ts</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">import</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> type</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">RemovableRef</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">from</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> '@vueuse/core'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">import</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">useStorage</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">from</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> '@vueuse/core'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> interface</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> UserMeta</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    nick</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">string</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    mail</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">string</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    link</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">string</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> const</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> useUserMeta</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (): </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">RemovableRef</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">UserMeta</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">useStorage</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">UserMeta</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'WALINE_USER_META'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    nick</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">''</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    mail</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">''</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    link</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">''</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">});</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">export</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> const</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> useEditor</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (): </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">RemovableRef</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">string</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span></span>
<span class="line highlighted"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">useStorage</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">string</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'WALINE_COMMENT_BOX_EDITOR'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">''</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>继续查找调用，找到<code>CommentBox.vue</code>关键组件，该组件将<em>textarea</em>评论框<strong>v-model</strong>与<code>useEditor</code>函数绑定，该函数返回一个<code>RemovableRef</code>，该类型为<code>&lt;string&gt;</code>。</p>
<div class="language-ts" data-highlighter="shiki" data-ext="ts" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-ts"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> editor</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> useEditor</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span></code></pre>
</div><div class="language-vue" data-highlighter="shiki" data-ext="vue" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-vue"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">textarea</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">  id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"wl-edit"</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">  ref</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"editorRef"</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">  v-model</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">editor</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">"</span></span>
<span class="line"><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">  class</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"wl-editor"</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  :</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">placeholder</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">replyUser</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> ?</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> `@</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">${</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">replyUser</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">}</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">`</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> :</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> locale</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">placeholder</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">"</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  @</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">keydown</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">onKeyDown</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">"</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  @</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">drop</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">onDrop</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">"</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  @</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">paste</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">onPaste</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">"</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">/></span></span></code></pre>
</div><h2>2. 调试</h2>
<h3>2.1. 准备工作</h3>
<ol>
<li>
<p>已锁定问题文件为<code>CommentBox.vue</code>,接下来开始 debug，因为准备提 PR，所以先 fork 一份</p>
</li>
<li>
<p>按照<a href="https://waline.js.org/advanced/contribution.html" target="_blank" rel="noopener noreferrer">waline-贡献指南</a>进行准备</p>
</li>
<li>
<p>先执行<code>pnpm i &amp; pnpm build</code>,本地调试依赖<code>@waline/api</code> 需要前置 build</p>
</li>
<li>
<p>使用 <code>pnpm client:dev</code> 启动 <code>@waline/client</code> 本地开发,由于 waline 是 Client/Server 架构，在调试 client 时，你需要设置 SERVERURL 为调试服务器（可以直接使用 vercel 的服务器），或同时启动下面的 server 开发服务器并使用默认的 <code>localhost:9090</code>。</p>
</li>
<li>
<p>使用 <code>pnpm server:dev</code> 启动 <code>@waline/server</code> 本地开发,配置必要的本地环境变量至 <code>example/.env</code>。（这里我配置了 <code>leancloud</code> 的环境变量，一直在报错）</p>
</li>
</ol>
<h3>2.2. 关键函数 <code>CommentBox.vue</code>的<code>submitComment</code>和<code>watch</code></h3>
<div class="language-ts line-numbers-mode" data-highlighter="shiki" data-ext="ts" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-ts"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> const</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> submitComment</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> async</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (): </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Promise</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">     // 此处...省略若干</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">     try</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">         if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">recaptchaV3Key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">         comment</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">recaptchaV3</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">             await</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> useReCaptcha</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">recaptchaV3Key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">execute</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'social'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">         if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">turnstileKey</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">         comment</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">turnstile</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> await</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> useTurnstile</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">turnstileKey</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">execute</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'social'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">         const</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> options</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">         serverURL</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">         lang</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">         token</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">userInfo</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">?.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">token</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">         comment</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">         };</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">         const</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> response</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> await</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">edit</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">         ?</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> updateComment</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">({</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">             objectId</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">edit</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">objectId</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">             ...</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">options</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">         })</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">         :</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> addComment</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">options</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">         isSubmitting</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> false</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">         if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">response</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">errmsg</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">return</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> alert</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">response</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">errmsg</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">         emit</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'submit'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">response</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">data</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">         editor</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> ''</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">         previewText</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> ''</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">         if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">replyId</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">emit</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'cancelReply'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">         if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">edit</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">?.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">objectId</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">emit</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'cancelEdit'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">     } </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">catch</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">err</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">unknown</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">         isSubmitting</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> false</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">         alert</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">((</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">err</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> as</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> TypeError</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">message</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">     }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> };</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>接下来看 watch</p>
<div class="language-ts line-numbers-mode" data-highlighter="shiki" data-ext="ts" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-ts"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">watch</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  () </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> editor</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  (</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    const</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">highlighter</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">texRenderer</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> config</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    content</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    previewText</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> parseMarkdown</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">      emojiMap</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">emoji</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">map</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">      highlighter</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">      texRenderer</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    wordNumber</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> getWordNumber</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">autosize</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">editorRef</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    else</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> autosize</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">destroy</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">editorRef</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  {</span></span>
<span class="line highlighted"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    immediate</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">true</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>2.3. 断点测试，整体流程如下</h3>
<ol>
<li>
<p>监听<code>editor.value</code>，并将<strong>editor</strong>和<strong>textarea</strong>绑定,用户输入的值自动保存在<strong>localStorage</strong>中</p>
</li>
<li>
<p>点击提交按钮，将<strong>textarea</strong>中的值发送给后端，收到回调后将<strong>editor</strong>清空,如果抛出异常则不清空</p>
</li>
<li>
<p>正常提交评论无异常，在<code>submitComment</code>函数中的<code>editor.value = '';</code>将<strong>localStorage</strong>存储的内容清空</p>
</li>
<li>
<p>回复他人评论时，前置流程提交和步骤 3 一致，然后触发了<code>watch</code>,此时<code>watch</code>监听到的<strong>value</strong>为未被清空的值，既之前用户输入的内容</p>
</li>
<li>
<p><code>submitComment</code>中的<code>editor.value = '';</code>和<code>watch</code>都打上了断点，先赋值为空，后触发<code>watch</code>，而此时<code>watch</code>的 value 为被清空之前的值，即用户输入的内容</p>
</li>
</ol>
<h3>2.4. 分析</h3>
<p>问题已经定位到代码级别，接下来只需要找到 watch 被触发的原因即可，初步猜测可能原因：</p>
<p><code>submitComment</code>是异步函数，其内部赋空值后，<code>editor.value=''</code>没有及时更新，导致<code>watch</code>触发取到旧值</p>
<p>遂添加 watch 的 bebug 函数加以验证</p>
<ol>
<li><code>onTrack</code> 将在响应属性或引用作为依赖项被跟踪时被调用。相当于 get</li>
<li><code>onTrigger</code> 将在侦听器回调被依赖项的变更触发时被调用。相当于 set</li>
</ol>
<div class="language-ts line-numbers-mode" data-highlighter="shiki" data-ext="ts" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-ts"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">watch</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  () </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> editor</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  (</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    const</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">highlighter</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">texRenderer</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> config</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    content</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    previewText</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> parseMarkdown</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">      emojiMap</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">emoji</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">map</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">      highlighter</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">      texRenderer</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    wordNumber</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> getWordNumber</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">autosize</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">editorRef</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    else</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> autosize</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">destroy</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">editorRef</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  {</span></span>
<span class="line"><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">    immediate</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">true</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    onTrack</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">e</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">      // 当 editor.value 被追踪为依赖时触发</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      debugger</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    onTrigger</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">e</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">      // 当 editor.value 被更改时触发</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">      debugger</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>结果为：</p>
<ol>
<li><code>editor.value=''</code>时，<code>onTrigger</code>触发，newValue 为<code>''</code></li>
<li>紧接着触发<code>onTrack</code>value 为旧值</li>
</ol>
<p>调整回调的触发时机试试看：</p>
<ul>
<li>
<p><a href="https://cn.vuejs.org/guide/essentials/watchers.html#post-watchers" target="_blank" rel="noopener noreferrer">Post Watchers</a></p>
<p>如果想在侦听器回调中能访问被 Vue 更新<strong>之后</strong>的所属组件的 DOM，你需要指明 <code>flush: 'post'</code> 选项：</p>
</li>
<li>
<p><a href="https://cn.vuejs.org/guide/essentials/watchers.html#sync-watchers" target="_blank" rel="noopener noreferrer">同步侦听器</a></p>
<p>它会在 Vue 进行任何更新之前触发：</p>
<blockquote>
<p>[!warning]<br>
同步侦听器不会进行批处理，每当检测到响应式数据发生变化时就会触发。可以使用它来监视简单的布尔值，但应避免在可能多次同步修改的数据源 (如数组) 上使用。</p>
</blockquote>
</li>
</ul>
<p>结果是没有作用，但在更改为 sync 后，发现了一件有趣的事情，因为 sync 不会进行批处理的特性，所以触发了两次<code>onTrack</code>,我们来看一下两次的堆栈信息</p>
<figure><img src="https://s3.bmp.ovh/imgs/2024/05/23/f4ffa6bb556812cc.png" alt="第一次" tabindex="0" loading="lazy"><figcaption>第一次</figcaption></figure>
<figure><img src="https://s3.bmp.ovh/imgs/2024/05/23/33bc634ac459ec19.png" alt="第二次" tabindex="0" loading="lazy"><figcaption>第二次</figcaption></figure>
<p>很明显第一次触发是<code>editor.value=''</code>时触发的，第二次触发异步的，在<code>submitComment</code>还未执行完成时就触发了，点击堆栈信息定位到了<strong>291</strong>行，很明显早于<code>editor.value=''</code>的<strong>297</strong>行。所以取到的是旧的值，虽然断点是<code>editor.value=''</code>先执行，但<code>submitComment</code>是异步的，watch 取到的是旧值，那接下来就需要看 <mark><code>watch</code>是被什么触发了</mark></p>
<p>由于源码中的<code>watch</code>是写在<code>onMounted</code>中的，断点确定二次走到<code>watch</code>中时，是触发了<code>onMounted</code>，那触发<code>onMounted</code>是组件重新加载了，查找后得知，整体逻辑是，评论区域，代码结构如下：</p>
<ul>
<li>
<p>组件为<code>WalineComment.vue</code>,其中包含了评论列表组件 item<code>CommentCard.vue</code>和顶部默认输入框<code>CommentBox.vue</code></p>
</li>
<li>
<p>针对文章发布评论使用的是顶部的<code>CommentBox.vue</code>组件</p>
</li>
<li>
<p>针对评论回复时，使用的是<code>CommentCard.vue</code>-<code>CommentBox.vue</code></p>
</li>
<li>
<p>回复完成后<code>CommentCard.vue</code>-<code>CommentBox.vue</code>销毁，顶部的<code>CommentBox.vue</code>组件重新渲染</p>
</li>
</ul>
<p><mark>顶部的<code>CommentBox.vue</code>组件重新渲染会触发 watch，此时取值是旧值，</mark> 经查看，<code>submitComment</code>中的<code>editor.value=''</code>执行完后， <code>localStorage</code> 中的值并未立即修改，所以重新渲染的顶部<code>CommentBox.vue</code>组件在初始化时取到的值仍为<code>localStorage</code>中的旧值。</p>
<h2>3. 验证</h2>
<p>上边已经基本确定问题出在这句上</p>
<div class="language-ts" data-highlighter="shiki" data-ext="ts" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-ts"><span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">editor</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> ""</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span></code></pre>
</div><div class="language-ts" data-highlighter="shiki" data-ext="ts" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-ts"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> editor</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> useEditor</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span></code></pre>
</div><p>而在 <a href="#_1-2-%E6%BA%90%E7%A0%81">1.2</a> 中可以看到<code>useEditor()</code>是<code>@vueuse/core</code>的导出函数,怀疑其内部实现有一些异步操作，导致的没有立即更新<code>localStorage</code></p>
<div class="language-ts" data-highlighter="shiki" data-ext="ts" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-ts"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 更换`editor.value`为</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">localStorage</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">setItem</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'WALINE_COMMENT_BOX_EDITOR'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">''</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 或</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">await</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> nextTick</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span></code></pre>
</div><h3>3.1. 至此问题解决</h3>
<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p><strong>nextTick()</strong></p>
<p>等待下一次 DOM 更新刷新的工具方法。</p>
<ul>
<li>类型</li>
</ul>
<div class="language-ts" data-highlighter="shiki" data-ext="ts" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-ts"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> nextTick</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">callback</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">?</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: () </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">): </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Promise</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">></span></span></code></pre>
</div><ul>
<li>详细信息</li>
</ul>
<p>当你在 Vue 中更改响应式状态时，最终的 DOM 更新并不是同步生效的，而是由 Vue 将它们缓存在一个队列中，直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变，都仅执行一次更新。</p>
<p><strong>nextTick()</strong> 可以在状态改变后立即使用，以等待 DOM 更新完成。你可以传递一个回调函数作为参数，或者 await 返回的 Promise。</p>
</div>
<h2>4. 思考</h2>
<p>在<a href="https://www.vueusejs.com/core/useStorage/#usage" target="_blank" rel="noopener noreferrer">vueusejs</a>的文档中，其实<code>useStorage</code>应该是一个同步操作，它还有一个<code>useStorageAsync</code>的API，支持异步的响应式Storage，按理说里边不应该包含太多异步的或者延时性的代码，我也找到源码浅浅看了一下，目前还没找到问题在哪里，按照替换<code>localStorage.setItem('WALINE_COMMENT_BOX_EDITOR', '');</code>可行来看，问题就在<code>useStorage</code>身上无疑，后续有时间，会继续探索一下，给官方的PR<a href="https://github.com/walinejs/waline/pull/2524" target="_blank" rel="noopener noreferrer">#2524</a>也已经提了</p>
]]></content:encoded>
      <enclosure url="https://w.wallhaven.cc/full/6d/wallhaven-6d7xmx.jpg?x-oss-process=image/resize,limit_0,m_fill,w_1366,h_768/quality,Q_92/format,webp" type="image/"/>
    </item>
    <item>
      <title>使用n命令管理node版本</title>
      <link>https://www.linkjm.com/posts/Web/node/node-version.html</link>
      <guid>https://www.linkjm.com/posts/Web/node/node-version.html</guid>
      <source url="https://www.linkjm.com/rss.xml">使用n命令管理node版本</source>
      <description>鉴于使用 ReactNative 时，会需要不同的 node 版本，提供一种版本切换方式 使用 n command 来进行 node 版本管理 概念 n Node version manager 提供了一个更简单的 CLI，用于在 Node 版本之间进行安装和切换。它仅在 Linux 或 Mac 操作系统上受到支持。 安装 如果你已经安装某个版本的 N...</description>
      <category>Web</category>
      <pubDate>Fri, 03 Apr 2020 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>鉴于使用 ReactNative 时，会需要不同的 node 版本，提供一种版本切换方式</p>
</blockquote>
<h2>使用 n command 来进行 node 版本管理</h2>
<h3>概念</h3>
<p>n Node version manager 提供了一个更简单的 CLI，用于在 Node 版本之间进行安装和切换。它仅在 Linux 或 Mac 操作系统上受到支持。</p>
<h3>安装</h3>
<p>如果你已经安装某个版本的 Node 和 npm ，则可以用 npm install -g n 来安装 n，就像安装其他 NPM 包一样。</p>
<p>如果你还没有安装 Node 或 npm，可以用 GitHub 中的 bash 脚本安装 n。这是它的样子：</p>
<div class="language-shell" data-highlighter="shiki" data-ext="shell" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-shell"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">curl</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -L</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> https://git.io/n-install</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">bash</span></span>
<span class="line"><span style="--shiki-light:#98C379;--shiki-dark:#98C379">===</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> n</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> successfully</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> installed.</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  The</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> active</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> Node.js</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> version</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> is:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> v10.16.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  Run</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> `</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">n</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -h</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">`</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> for</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> help.</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  To</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> update</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> n</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> later,</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> run</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> `</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">n-update</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">`</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">.</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  To</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> uninstall,</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> run</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> `</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">n-uninstall</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">`</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">.</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  IMPORTANT:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> OPEN</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> A</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> NEW</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> TERMINAL</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> TAB/WINDOW</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> or</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> run</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> `</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">.</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> /home/brian/.bashrc`</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">             before</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> using</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> n</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> and</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> Node.js.</span></span>
<span class="line"><span style="--shiki-light:#98C379;--shiki-dark:#98C379">===</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">~$ </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">.</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> /home/brian/.bashrc</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">~$ n</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">node/10.16.0</span></span></code></pre>
</div><p>通过从 GitHub 下载并运行 n-install 脚本来安装 n。n 默认安装了一个版本的 Node。</p>
<h3>安装版本 10.17.0</h3>
<p>如果需要指定版本号，可以这样安装</p>
<div class="language-shell" data-highlighter="shiki" data-ext="shell" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-shell"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> n</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 10.17.0</span></span></code></pre>
</div><h3>安装最新版本</h3>
<p>安装最新版本使用如下命令</p>
<div class="language-shell" data-highlighter="shiki" data-ext="shell" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-shell"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> n</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> latest</span></span></code></pre>
</div><h3>切换版本</h3>
<div class="language-shell" data-highlighter="shiki" data-ext="shell" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-shell"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">n</span></span></code></pre>
</div><p>显示如下</p>
<div class="language-shell" data-highlighter="shiki" data-ext="shell" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-shell"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">Last</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> login:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> Fri</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> Apr</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">  3</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> 16:56:05</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> on</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> ttys004</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># huangkun @ huagnkundeMacBook-Pro in ~ [16:58:24]</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">$</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> n</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  ο</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> node/10.17.0</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    node/13.12.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">Use</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> up/down</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> arrow</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> keys</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> to</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> select</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> a</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> version,</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> return</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> key</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> to</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> install,</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> d</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> to</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> delete,</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> q</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> to</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> quit</span></span></code></pre>
</div><p>可以使用上下方向键来选择版本，并回车。如果不想选择可以按<code>q</code>退出</p>
<h2>直接使用 Node 二进制文件</h2>
<p>n 提供了直接调用特定 Node 二进制文件的功能，而无需显式切换到该版本的 Node。 NVM 则没有类似的功能。</p>
<div class="language-shell" data-highlighter="shiki" data-ext="shell" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-shell"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># huangkun @ huagnkundeMacBook-Pro in ~ [17:01:32]</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">$</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> node</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> -v</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">v10.17.0</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># huangkun @ huagnkundeMacBook-Pro in ~ [17:01:39]</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">$</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> n</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> use</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 13.12.0</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> index.js</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">Node</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> version:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> v13.12.0</span></span></code></pre>
</div><p>请注意，n use 命令所请求的 Node 版本需要由 n 安装。</p>
<p>在某些情况下，这个功能非常有用。例如，有一个构建服务器，用于构建需要不同 Node 版本的程序。可以用 n use 命令触发每个构建，并能够指定该程序所需的 Node 版本。</p>
<h2>差异化</h2>
<p>NVM for Windows 和 n 有许多常见功能，也有一些独特的功能，这些功能会影响你使用每个工具的方式和位置。以下是一些主要差异的摘要：</p>
<p>|              <strong>能力</strong>              |          <strong>NVM for Windows</strong>           |       <strong>n</strong>        |<br>
| :</p>
]]></content:encoded>
    </item>
    <item>
      <title>Dart 中的并发</title>
      <link>https://www.linkjm.com/posts/cross-platform/Flutter/concurrency.html</link>
      <guid>https://www.linkjm.com/posts/cross-platform/Flutter/concurrency.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Dart 中的并发</source>
      <description>Dart 通过 async-await、isolate 以及一些异步类型概念（例如 Future 和 Stream）支持了并发代码编程。本篇文章会对 async-await、Future 和 Stream 进行简略的介绍，而侧重点放在 isolate 的讲解上。 在应用中，所有的 Dart 代码都在 isolate 中运行。每一个 Dart 的 iso...</description>
      <category>跨平台</category>
      <pubDate>Wed, 26 Apr 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Dart 通过 async-await、isolate 以及一些异步类型概念（例如 <code>Future</code> 和 <code>Stream</code>）支持了并发代码编程。本篇文章会对 async-await、<code>Future</code> 和 <code>Stream</code> 进行简略的介绍，而侧重点放在 isolate 的讲解上。</p>
<p>在应用中，所有的 Dart 代码都在 <strong>isolate</strong> 中运行。每一个 Dart 的 isolate 都有独立的运行线程，它们无法与其他 isolate 共享可变对象。在需要进行通信的场景里，isolate 会使用消息机制。很多 Dart 应用都只使用一个 isolate，也就是 main isolate。你可以创建额外的 isolate 以便在多个处理器核心上执行并行代码。</p>
<p>尽管 Dart 的 isolate 模型设计是基于操作系统提供的进程和线程等更为底层的原语进行设计的， Dart 虚拟机对其的使用是一个具体的实现，在本篇文章中，我们不对其具体实现展开讨论。</p>
<h2>异步的类型和语法</h2>
<p>如果你已经对 <code>Future</code>、<code>Stream</code> 和 async-await 比较熟悉了，可以直接跳到 <a href="https://dart.cn/guides/language/concurrency#how-isolates-work" target="_blank" rel="noopener noreferrer">isolate 部分</a> 进行阅读。</p>
<h3>Future 和 Stream 类型</h3>
<p>Dart 语言和库通过 <code>Future</code> 和 <code>Stream</code> 对象，来提供会在当前调用的未来返回某些值的功能。以 JavaScript 中的 Promise 为例，在 Dart 中一个最终会返回 <code>int</code> 类型值的 promise，应当声明为 <code>Future&lt;int&gt;</code>；一个会持续返回一系列 <code>int</code> 类型值的 promise，应当声明为 <code>Stream&lt;int&gt;</code>。</p>
<p>让我们用 dart:io 来举另外一个例子。<code>File</code> 的同步方法 <a href="https://api.dart.cn/stable/dart-io/File/readAsStringSync.html" target="_blank" rel="noopener noreferrer"><code>readAsStringSync()</code></a> 会以同步调用的方式读取文件，在读取完成或者抛出错误前保持阻塞。这个会返回 <code>String</code> 类型的对象，或者抛出异常。而与它等效的异步方法 <a href="https://api.dart.cn/stable/dart-io/File/readAsString.html" target="_blank" rel="noopener noreferrer"><code>readAsString()</code></a>，会在调用时立刻返回 <code>Future&lt;String&gt;</code> 类型的对象。在未来的某一刻，<code>Future&lt;String&gt;</code> 会结束，并返回一个字符串或错误。</p>
<h4>为什么异步的代码如此重要？</h4>
<p>It matters whether a method is synchronous or asynchronous because most apps need to do more than one thing at a time.</p>
<p>大部分应用需要在同一时刻做很多件事。例如，应用可能会发起一个 HTTP 请求，同时在请求返回前对用户的操作做出不同的界面更新。异步的代码会有助于应用保持更高的可交互状态。</p>
<p>异步场景包括调用系统 API，例如非阻塞的 I/O 操作、HTTP 请求或与浏览器交互。还有一些场景是利用 Dart 的 isolate 进行计算，或等待一个计时器的触发。这些场景要么是在不同的线程运行，要么是被系统或 Dart 运行时处理，让 Dart 代码可以在计算时同步运行。</p>
<h3>async-await 语法</h3>
<p><code>async</code> 和 <code>await</code> 关键字是用声明来定义异步函数和获取它们的结果的方式。</p>
<p>下面是一段同步代码调用文件 I/O 时阻塞的例子：</p>
<div class="language-dart" data-highlighter="shiki" data-ext="dart" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-dart"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Read some data.</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> fileData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> _readFileSync</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> jsonDecode</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(fileData);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Use that data.</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'Number of JSON keys: </span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">${</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">jsonData</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">length</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> _readFileSync</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> file </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> File</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(filename);</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> contents </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> file.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">readAsStringSync</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> contents.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">trim</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>下面是类似的代码，但是变成了 <strong>异步调用</strong>：</p>
<div class="language-dart" data-highlighter="shiki" data-ext="dart" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-dart"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">async</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Read some data.</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> fileData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> await</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> _readFileAsync</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> jsonDecode</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(fileData);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Use that data.</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'Number of JSON keys: </span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">${</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">jsonData</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">length</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Future</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">_readFileAsync</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">async</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> file </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> File</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(filename);</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> contents </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> await</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> file.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">readAsString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> contents.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">trim</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p><code>main()</code> 函数在调用 <code>_readFileAsync()</code> 前使用了 <code>await</code> 关键字，让原生代码（文件 I/O）执行的同时，其他的 Dart 代码（例如事件处理器）能继续执行。使用 <code>await</code> 后，<code>_readFileAsync()</code> 调用返回的 <code>Future&lt;String&gt;</code> 类型也转换为了 <code>String</code>。从而在将结果 <code>content</code> 赋予变量时，隐式转换为 <code>String</code> 类型。</p>
<p><strong>备注:</strong></p>
<p><code>await</code> 关键字仅在函数体前定义了 <code>async</code> 的函数中有效。</p>
<p>如下图所示，无论是在 Dart VM 还是在系统中， Dart 代码都会在 <code>readAsString()</code> 执行非 Dart 代码时暂停。在 <code>readAsString()</code> 返回值后，Dart 代码将继续执行。</p>
<figure><img src="https://dart.cn/guides/language/concurrency/images/basics-await.png" alt="类似于流程图的图形显示了应用程序代码从开始到退出的执行过程，在这之间等待本地I/O" tabindex="0" loading="lazy"><figcaption>类似于流程图的图形显示了应用程序代码从开始到退出的执行过程，在这之间等待本地I/O</figcaption></figure>
<p>如果你想了解更多关于 <code>async</code>、<code>await</code> 和 <code>Future</code> 的内容，可以访问 <a href="https://dart.cn/codelabs/async-await" target="_blank" rel="noopener noreferrer">异步编程 codelab</a> 进行学习。</p>
<h2>Isolate 的工作原理</h2>
<p>现代的设备通常会使用多核 CPU。开发者为了让程序在设备上有更好的表现，有时会使用共享内容的线程来并发运行代码。然而，状态的共享可能会 <a href="https://baike.baidu.com/l/kex6qKvt" target="_blank" rel="noopener noreferrer">产生竞态条件，从而造成错误</a>，也可能会增加代码的复杂度。</p>
<p>Dart 代码并不在多个线程上运行，取而代之的是它们会在 isolate 内运行。每一个 isolate 会有自己的堆内存，从而确保 isolate 之间互相隔离，无法互相访问状态。由于这样的实现并不会共享内存，所以你也不需要担心 <a href="https://baike.baidu.com/l/My2bXiba" target="_blank" rel="noopener noreferrer">互斥锁和其他锁</a>。</p>
<p>在使用 isolate 时，你的 Dart 代码可以在同一时刻进行多个独立的任务，并且使用可用的处理器核心。 Isolate 与线程和进程近似，但是<mark>每个 isolate 都拥有独立的内存，以及运行事件循环的独立线程。</mark></p>
<p><strong>Platform note:</strong> Only the <a href="https://dart.cn/overview#platform" target="_blank" rel="noopener noreferrer">Dart Native platform</a> implements isolates. To learn more about the Dart Web platform, see the <a href="https://dart.cn/guides/language/concurrency#concurrency-on-the-web" target="_blank" rel="noopener noreferrer">Concurrency on the web</a> section.</p>
<div class="hint-container info">
<p class="hint-container-title">平台说明</p>
<p>只有<a href="https://dart.cn/overview#platform" target="_blank" rel="noopener noreferrer">Dart Native</a>平台实现了隔离器。要了解更多关于Dart网络平台的信息，请参见<a href="#%E5%9C%A8web%E7%9A%84%E5%B9%B6%E5%8F%91">Web的并发性部分</a>。</p>
</div>
<h3>主 isolate</h3>
<p>在一般场景下，你完全无需关心 isolate。通常一个 Dart 应用会在主 isolate 下执行所有代码，如下图所示：</p>
<figure><img src="https://dart.cn/guides/language/concurrency/images/basics-main-isolate.png" alt="图中显示了一个主隔离区，它运行，对事件作出反应，然后退出" tabindex="0" loading="lazy"><figcaption>图中显示了一个主隔离区，它运行<code>main()</code>，对事件作出反应，然后退出</figcaption></figure>
<p>就算是只有一个 isolate 的应用，只要通过使用 async-await 来处理异步操作，也完全可以流畅运行。一个拥有良好性能的应用，会在快速启动后尽快进入事件循环。这使得应用可以通过异步操作快速响应对应的事件。</p>
<h3>Isolate 的生命周期</h3>
<p>如下图所示，每个 isolate 都是从运行 Dart 代码开始的，比如 <code>main()</code> 函数。执行的 Dart 代码可能会注册一些事件监听，例如处理用户操作或文件读写。当 isolate 执行的 Dart 代码结束后，如果它还需要处理已监听的事件，那么它依旧会继续被保持。处理完所有事件后，isolate 会退出。</p>
<figure><img src="https://dart.cn/guides/language/concurrency/images/basics-isolate.png" alt="一个更一般的图显示，任何隔离体都会运行一些代码，选择性地对事件做出反应，然后退出" tabindex="0" loading="lazy"><figcaption>一个更一般的图显示，任何隔离体都会运行一些代码，选择性地对事件做出反应，然后退出</figcaption></figure>
<h3>事件处理</h3>
<p>在客户端应用中，主 isolate 的事件队列内，可能会包含重绘的请求、点击的通知或者其他界面事件。例如，下图展示了包含四个事件的事件队列，队列会按照先进先出的模式处理事件。</p>
<figure><img src="https://dart.cn/guides/language/concurrency/images/event-loop.png" alt="一个显示事件被逐一送入事件循环的图。" tabindex="0" loading="lazy"><figcaption>一个显示事件被逐一送入事件循环的图。</figcaption></figure>
<p>如下图所示，在 <code>main()</code> 方法执行完毕后，事件队列中的处理才开始，此时处理的是第一个重绘的事件。而后主 isolate 会处理点击事件，接着再处理另一个重绘事件。</p>
<figure><img src="https://dart.cn/guides/language/concurrency/images/event-handling.png" alt="显示主隔离区逐一执行事件处理程序的图" tabindex="0" loading="lazy"><figcaption>显示主隔离区逐一执行事件处理程序的图</figcaption></figure>
<p>如果某个同步执行的操作花费了很长的处理时间，应用看起来就像是失去了响应。在下图中，处理点击事件的代码比较耗时，导致紧随其后的事件并没有及时处理。这时应用可能会产生卡顿，所有的动画都无法流畅播放。</p>
<figure><img src="https://dart.cn/guides/language/concurrency/images/event-jank.png" alt="图中显示了一个执行时间过长的分接处理程序" tabindex="0" loading="lazy"><figcaption>图中显示了一个执行时间过长的分接处理程序</figcaption></figure>
<p>在一个客户端应用中，耗时过长的同步操作，通常会导致 <a href="https://flutter.cn/docs/perf/rendering-performance" target="_blank" rel="noopener noreferrer">卡顿的动画</a>。而最糟糕的是，应用界面可能完全失去响应。</p>
<h3>后台运行对象</h3>
<p>如果你的应用受到耗时计算的影响而出现卡顿，例如 <a href="https://flutter.cn/docs/cookbook/networking/background-parsing" target="_blank" rel="noopener noreferrer">解析较大的 JSON 文件</a>，你可以考虑将耗时计算转移到单独工作的 isolate，通常我们称这样的 isolate 为 <strong>后台运行对象</strong>。下图展示了一种常用场景，你可以生成一个 isolate，它将执行耗时计算的任务，并在结束后退出。这个 isolate 工作对象退出时会把结果返回。</p>
<figure><img src="https://dart.cn/guides/language/concurrency/images/isolate-bg-worker.png" alt="A figure showing a main isolate and a simple worker isolate" tabindex="0" loading="lazy"><figcaption>A figure showing a main isolate and a simple worker isolate</figcaption></figure>
<p>每个 isolate 都可以通过消息通信传递一个对象，这个对象的所有内容都需要满足可传递的条件。并非所有的对象都满足传递条件，在无法满足条件时，消息发送会失败。举个例子，如果你想发送一个 <code>List&lt;Object&gt;</code>，你需要确保这个列表中所有元素都是可被传递的。假设这个列表中有一个 <code>Socket</code>，由于它无法被传递，所以你无法发送整个列表。</p>
<p>你可以查阅 <a href="https://api.dart.cn/stable/dart-isolate/SendPort/send.html" target="_blank" rel="noopener noreferrer"><code>send()</code> 方法</a> 的文档来确定哪些类型可以进行传递。</p>
<p>Isolate 工作对象可以进行 I/O 操作、设置定时器，以及其他各种行为。它会持有自己内存空间，与主 isolate 互相隔离。这个 isolate 在阻塞时也不会对其他 isolate 造成影响。</p>
<h2>代码示例</h2>
<p>本节将重点讨论使用 <code>Isolate</code> API 实现 isolate 的一些示例。</p>
<h3>实现一个简单的 isolate 工作对象</h3>
<p>这些例子实现了一个主隔离器，它生成了一个简单的工作隔离器。 <a href="https://api.dart.cn/dev/dart-isolate/Isolate/run.html" target="_blank" rel="noopener noreferrer"><code>Isolate.run()</code></a> 简化了设置和管理工作者隔离区的步骤:</p>
<ol>
<li>生成（启动并创建）一个隔离器</li>
<li>在生成的隔离体上运行一个函数</li>
<li>捕获结果</li>
<li>将结果返回给主隔离区</li>
<li>工作完成后，终止隔离区的运行</li>
<li>检查、捕获并将异常和错误抛回给主隔离区</li>
</ol>
<div class="hint-container tip">
<p class="hint-container-title">备注</p>
<p>如果你使用Flutter，考虑使用<a href="https://api.flutter-io.cn/flutter/foundation/compute-constant.html" target="_blank" rel="noopener noreferrer">Flutter的<code>compute()</code>函数</a>而不是<code>Isolate.run()</code>。<code>compute</code>函数允许你的代码在<a href="https://dart.cn/overview#platform" target="_blank" rel="noopener noreferrer">本地和非本地平台</a>上工作。当只针对原生平台时，使用<code>Isolate.run()</code>以获得更符合人类工程学的的API。</p>
</div>
<h4>在一个新的隔离区中运行一个现有的方法</h4>
<p>主 isolate 的代码如下：</p>
<div class="language-dart" data-highlighter="shiki" data-ext="dart" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-dart"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">async</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Read some data.</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> await</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> Isolate</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">run</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(_readAndParseJson);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Use that data.</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'Number of JSON keys: </span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">${</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">jsonData</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">length</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>生成的隔离器会执行作为第一个参数传递的函数, <code>_readAndParseJson</code>:</p>
<div class="language-dart" data-highlighter="shiki" data-ext="dart" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-dart"><span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Future</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Map</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">dynamic</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>> </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">_readAndParseJson</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">async</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> fileData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> await</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> File</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(filename).</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">readAsString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> jsonDecode</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(fileData) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">as</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> Map</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">dynamic</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><ol>
<li><code>Isolate.run()</code> 产生了一个隔离器，即后台工作者， 而 <code>main()</code> 则在等待结果</li>
<li>生成的隔离器会执行传递给 <code>run()</code>的参数: the function <code>_readAndParseJson()</code>.</li>
<li><code>Isolate.run()</code> 从返回中获取结果，并将该值送回主隔离区，从而关闭工作隔离区。</li>
<li>工作者隔离区会将保存结果的内存<em>转移</em>到主隔离区。它并<em>不复制</em>数据。工作者隔离区会执行一个验证通道，以确保对象被允许转移。</li>
</ol>
<p><code>_readAndParseJson()</code> 是一个现有的异步函数，可以很容易地直接在主隔离区中运行。使用 <code>Isolate.run()</code> 来运行它，从而实现了并发性。工作者隔离区完全抽象了<code>_readAndParseJson()</code>的计算过程。它可以在不阻塞主隔离区的情况下完成。</p>
<p><code>Isolate.run()</code> 的结果总是一个Future，因为主隔离区的代码仍在继续运行。工作者隔离区执行的计算是同步的还是异步的，并不影响主隔离区，因为无论如何，它都是在并发地运行。</p>
<h4>Sending closures with isolates</h4>
<p>您也可以在主隔离区中直接使用函数字面或闭包，用<code>run()</code> 创建一个简单的工作隔离区。</p>
<div class="language-dart" data-highlighter="shiki" data-ext="dart" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-dart"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">async</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Read some data.</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> await</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> Isolate</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">run</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(() </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">async</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> fileData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> await</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> File</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(filename).</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">readAsString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    final</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> jsonDecode</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(fileData) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">as</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> Map</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">dynamic</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">>;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Use that data.</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'Number of JSON keys: </span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">${</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">jsonData</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">length</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">'</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>这个例子的完成情况与前一个例子相同。一个新的隔离器产生了，计算了一些东西，并把结果送了回来。</p>
<p>不过，现在这个隔离体发送的是一个<a href="https://dart.cn/guides/language/language-tour#anonymous-functions" target="_blank" rel="noopener noreferrer">闭包</a>。与典型的命名函数相比，闭包的限制较少，无论是在功能上还是在代码中的编写方式上。在这个例子中，<code>Isolate.run()</code>执行的是看起来像本地代码的东西，同时进行。在这个意义上，你可以把<code>run()</code>想象成一个 &quot;并行运行 &quot;的<a href="https://dart.cn/guides/language/language-tour#control-flow-statements" target="_blank" rel="noopener noreferrer">控制流操作符</a>。</p>
<h3>实现一个简单的 isolate 工作对象</h3>
<p><code>Isolate.run()</code> a抽取了一些较低级别的、与隔离物相关的API，以简化隔离物管理：</p>
<ul>
<li><a href="https://api.dart.cn/stable/dart-isolate/Isolate/spawn.html" target="_blank" rel="noopener noreferrer"><code>Isolate.spawn()</code></a> and <a href="https://api.dart.cn/stable/dart-isolate/Isolate/exit.html" target="_blank" rel="noopener noreferrer"><code>Isolate.exit()</code></a></li>
<li><a href="https://api.dart.cn/stable/dart-isolate/ReceivePort-class.html" target="_blank" rel="noopener noreferrer"><code>ReceivePort</code></a> and <a href="https://api.dart.cn/stable/dart-isolate/SendPort-class.html" target="_blank" rel="noopener noreferrer"><code>SendPort</code></a></li>
</ul>
<p>您可以直接使用这些基元来对隔离区的功能进行更精细的控制。例如，<code>run()</code>在返回一条消息后就会关闭其隔离区。如果您想允许多个消息在隔离区之间传递，该怎么办呢？您可以用与<code>run()</code>的实现方式大致相同的方式来设置自己的隔离区，只是以稍微不同的方式利用<code>SendPort</code>的<a href="https://api.dart.cn/stable/dart-isolate/SendPort/send.html" target="_blank" rel="noopener noreferrer"><code>send()</code>方法</a>。</p>
<p>如果你想在 isolate 之间建立更多的通信，那么你需要使用 <code>SendPort</code> 的 <a href="https://api.dart.cn/stable/dart-isolate/SendPort/send.html" target="_blank" rel="noopener noreferrer"><code>send()</code> 方法</a>。下图展示了一种常见的场景，主 isolate 会发送请求消息至 isolate 工作对象，然后它们之间会继续进行多次通信，进行请求和回复。</p>
<figure><img src="https://dart.cn/guides/language/concurrency/images/isolate-custom-bg-worker.png" alt="图中显示了主隔离器催生隔离器，然后发送请求消息，工作隔离器用回复消息进行响应；显示了两个请求-回复循环。" tabindex="0" loading="lazy"><figcaption>图中显示了主隔离器催生隔离器，然后发送请求消息，工作隔离器用回复消息进行响应；显示了两个请求-回复循环。</figcaption></figure>
<p>下方列举的 <a href="https://github.com/dart-lang/samples/tree/master/isolates" target="_blank" rel="noopener noreferrer">isolate 示例</a> 包含了发送多次消息的使用方法：</p>
<ul>
<li><a href="https://github.com/dart-lang/samples/tree/master/isolates/bin/send_and_receive.dart" target="_blank" rel="noopener noreferrer">send_and_receive.dart</a> 展示了如何从主 isolate 发送消息至生成的 isolate，与前面的示例较为接近，不过没有使用 <code>run()</code> 方法；</li>
<li><a href="https://github.com/dart-lang/samples/tree/master/isolates/bin/long_running_isolate.dart" target="_blank" rel="noopener noreferrer">long_running_isolate.dart</a> 展示了如何生成一个长期运行、且多次发送和接收消息的 isolate。</li>
</ul>
<h2>性能和 isolate 组</h2>
<p>当一个 isolate 调用了 <a href="https://api.dart.cn/stable/dart-isolate/Isolate/spawn.html" target="_blank" rel="noopener noreferrer"><code>Isolate.spawn()</code></a>，两个 isolate 将拥有同样的执行代码，并归入同一个 <strong>isolate 组</strong> 中。 Isolate 组会带来性能优化，例如新的 isolate 会运行由 isolate 组持有的代码，即共享代码调用。同时，<code>Isolate.exit()</code> 仅在对应的 isolate 属于同一组时有效。</p>
<p>某些场景下，你可能需要使用 <a href="https://api.dart.cn/stable/dart-isolate/Isolate/spawnUri.html" target="_blank" rel="noopener noreferrer"><code>Isolate.spawnUri()</code></a>，使用执行的 URI 生成新的 isolate，并且包含代码的副本。然而，<code>spawnUri()</code> 会比 <code>spawn()</code> 慢很多，并且新生成的 isolate 会位于新的 isolate 组。另外，当 isolate 在不同的组中，它们之间的消息传递会变得更慢。</p>
<div class="hint-container tip">
<p class="hint-container-title">备注</p>
<p>![Flutter logo](<a href="https://dart.cn/assets/img/shared/flutter/icon/64.png" target="_blank" rel="noopener noreferrer">https://dart.cn/assets/img/shared/flutter/icon/64.png</a> =20x)Flutter 不支持 <code>Isolate.spawnUri()</code>。</p>
</div>
<h2>在Web的并发</h2>
<p>所有的Dart应用程序都可以使用<code>async-await</code>、<code>Future</code>和<code>Stream</code>进行非阻塞、交错的计算。然而，<a href="(https://dart.cn/overview#platform)">Dart web 平台</a>并不支持隔离器。Dart网络应用程序可以使用网络工作者在后台线程中运行脚本，这与隔离程序类似。不过，<a href="https://developer.mozilla.org/docs/Web/API/Web_Workers_API/Using_web_workers" target="_blank" rel="noopener noreferrer">web workers</a>的功能和能力与隔离器有些不同。</p>
<p>例如，当Web工作者在线程之间发送数据时，他们会来回复制数据。不过，数据复制的速度可能非常慢，尤其是对于大的消息。隔离器也做同样的事情，但也提供了API，可以更有效地传输保存消息的内存。</p>
<p>创建Web Worker和Isolates也有不同。你只能通过声明一个单独的程序入口并单独编译来创建网络工作者。启动Web Worker类似于使用<code>Isolate.spoonUri</code>来启动一个隔离器。您也可以使用<code>Isolate.spown</code>来启动一个隔离器，这需要的资源较少，因为它<a href="https://dart.cn/guides/language/concurrency#performance-and-isolate-groups" target="_blank" rel="noopener noreferrer">重用了一些与催生隔离器相同的代码和数据</a>。Web Worker没有一个同等的API。</p>
]]></content:encoded>
      <enclosure url="https://w.wallhaven.cc/full/72/wallhaven-7286w9.png" type="image/png"/>
    </item>
    <item>
      <title>Flutter 基础大集合</title>
      <link>https://www.linkjm.com/posts/cross-platform/Flutter/newbie.html</link>
      <guid>https://www.linkjm.com/posts/cross-platform/Flutter/newbie.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Flutter 基础大集合</source>
      <description>Flutter初学，demo</description>
      <category>跨平台</category>
      <pubDate>Mon, 20 Mar 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>把之前学习时候写的demo拿出来记录一下<br>
地址：<br>
<a href="https://github.com/OrageKK/flutter_my_app" target="_blank" rel="noopener noreferrer">flutter_my_app</a></p>
</div>
<p>demo是照着B站的视频敲的，现在可能用不上了，但是还是觉得当时写的demo对于想学习flutter的新手来说作用还是有的，因为视频课程很零散，所以我边看边写了一个app，里边基本介绍了大部分基础的用法，当然进阶的东西，这部分demo是没有的，最近也在忙，有空了再补一部分进阶的东西吧</p>
<p>现在在做的内容是native+flutter混编，其中坑也不少，有时间会慢慢记录一下</p>
<h2>预览</h2>
<p>![home](<a href="https://s3.bmp.ovh/imgs/2023/03/20/b4d8d0a22fcdfd1a.png" target="_blank" rel="noopener noreferrer">https://s3.bmp.ovh/imgs/2023/03/20/b4d8d0a22fcdfd1a.png</a> =500x)</p>
<p>![side](<a href="https://s3.bmp.ovh/imgs/2023/03/20/a4e70357270289d9.png" target="_blank" rel="noopener noreferrer">https://s3.bmp.ovh/imgs/2023/03/20/a4e70357270289d9.png</a> =500x)</p>
<p>![animation](<a href="https://s3.bmp.ovh/imgs/2023/03/20/88359eef4b2eee2f.png" target="_blank" rel="noopener noreferrer">https://s3.bmp.ovh/imgs/2023/03/20/88359eef4b2eee2f.png</a> =500x)</p>
<p>![search](<a href="https://s3.bmp.ovh/imgs/2023/03/20/a1e88c9565dd0831.png" target="_blank" rel="noopener noreferrer">https://s3.bmp.ovh/imgs/2023/03/20/a1e88c9565dd0831.png</a> =500x)</p>
<p>![alert](<a href="https://s3.bmp.ovh/imgs/2023/03/20/5e6fe8f921ad4079.png" target="_blank" rel="noopener noreferrer">https://s3.bmp.ovh/imgs/2023/03/20/5e6fe8f921ad4079.png</a> =500x)</p>
<h2>Getting Started</h2>
<p>This project is a starting point for a Flutter application.</p>
<p>A few resources to get you started if this is your first Flutter project:</p>
<ul>
<li><a href="https://docs.flutter.dev/get-started/codelab" target="_blank" rel="noopener noreferrer">Lab: Write your first Flutter app</a></li>
<li><a href="https://docs.flutter.dev/cookbook" target="_blank" rel="noopener noreferrer">Cookbook: Useful Flutter samples</a></li>
</ul>
<p>For help getting started with Flutter development, view the<br>
<a href="https://docs.flutter.dev/" target="_blank" rel="noopener noreferrer">online documentation</a>, which offers tutorials,<br>
samples, guidance on mobile development, and a full API reference.</p>
<h2>包含</h2>
<ul>
<li>路由管理</li>
<li>基础组件（Text、Image、Icon）</li>
<li>布局组件
<ul>
<li>容器组件Container</li>
<li>线性布局Row、Column</li>
<li>弹性布局Flex</li>
<li>流式布局Wrap</li>
<li>层叠布局Stack、Positioned</li>
<li>对齐与相对定位Align</li>
</ul>
</li>
<li>容器类组件
<ul>
<li>填充Padding</li>
<li>装饰容器DecoratedBox</li>
<li>变换Transform</li>
<li>剪裁Clip</li>
<li>页面骨架Scaffold、AppBar</li>
<li>抽屉菜单Drawer</li>
<li>底部Tab导航</li>
<li>页面body</li>
</ul>
</li>
<li>可滚动组件
<ul>
<li>GridView</li>
<li>ListView</li>
<li>TabBarView</li>
<li>CustomScrollView 和 Slivers</li>
</ul>
</li>
<li>动画
<ul>
<li>隐式动画</li>
<li>显式动画</li>
<li>交错动画</li>
<li>动画切换组件（AnimatedSwitcher）</li>
</ul>
</li>
</ul>
<h2>使用库</h2>
<ul>
<li>fluttertoast: ^8.0.9</li>
<li>cached_network_image: ^3.2.2</li>
</ul>
]]></content:encoded>
      <enclosure url="https://s3.bmp.ovh/imgs/2023/03/20/b4d8d0a22fcdfd1a.png =500x" type="image/"/>
    </item>
    <item>
      <title>Flutter 工作原理</title>
      <link>https://www.linkjm.com/posts/cross-platform/Flutter/principle.html</link>
      <guid>https://www.linkjm.com/posts/cross-platform/Flutter/principle.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Flutter 工作原理</source>
      <description>Flutter 工作原理 本文档解释了使 Flutter API 正常工作的 Flutter 工具包内部工作原理。由于 Flutter widget 是以积极组合的形式构建的，所以使用 Flutter 构建的用户界面含有大量 widget。为了支撑这些负载，Flutter 使用了次线性算法来布局和构建 widget，这些数据结构使树形结构优化更加高效，...</description>
      <category>跨平台</category>
      <pubDate>Wed, 26 Apr 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[
<p>本文档解释了使 Flutter API 正常工作的 Flutter 工具包内部工作原理。由于 Flutter widget 是以积极组合的形式构建的，所以使用 Flutter 构建的用户界面含有大量 widget。为了支撑这些负载，Flutter 使用了次线性算法来布局和构建 widget，这些数据结构使树形结构优化更加高效，并且具有很多常量因子优化。通过一些额外的机制，该设计也允许开发者利用回调（用于构建用户可见的 widget）来轻松创建无限滚动列表。</p>
<h2>积极可组合性</h2>
<p>组合性是 Flutter 最为出众的一个特性。widget 通过组合其他 widget 的方式进行构建，并且这些 widget 自身由更基础的 widget 构建。比如，<code>Padding</code> 是一个 widget 而非其他 widget 的属性。因此，使用 Flutter 创建的用户界面是由多个 widget 组成的。</p>
<p>widget 递归构建的底层是 RenderObjectwidget，它将在渲染树的底部创建子节点。渲染树是一种存储用户界面几何信息的数据结构，该几何信息在 <strong>布局</strong> 期间计算并在 <strong>绘制</strong> 及 <strong>命中测试</strong> 期间使用。大多数 Flutter 开发者无需直接创建这些对象，而是使用 widget 来操纵渲染树。</p>
<p>为了支持 widget 层的积极可组合性， Flutter 在 widget 和树渲染层使用了大量的高效算法和优化措施，这些将在下面小节中进行介绍。</p>
<h3>次线性布局</h3>
<p>使用大量 widget 及渲染对象并保持高性能的关键是使用高效的算法。其中最重要的是确定渲染对象几何空间（比如大小和位置）的<strong>布局</strong>算法的性能。其他一些工具包使用 O(N²) 或更糟糕的布局算法（例如，约束域中的不动点迭代）。 Flutter 的目标在于布局初始化的线性性能，及一般情况下更新现有布局的<code>次线性布局性能</code>。通常情况下，布局所花费的时间应该比对象渲染要多得多。</p>
<p>Flutter 对每一帧执行一次布局操作，且布局算法仅在一次传递中完成。 <strong>约束</strong>信息通过父节点调用每个子节点的布局方法向下传递。子节点递归执行自身的布局操作，并在它们的布局方法中返回<strong>几何</strong>信息以便将其添加到渲染树中。需要注意的是，一旦渲染对象从布局中返回，该对象将不会被再次访问 [^1]，直到下一帧布局的执行。该策略将可能存在的单独测量和布局传递合并为单次传递，因此，每个渲染对象在布局过程中<strong>最多</strong>被访问<strong>两次</strong> [^2]：一次在树的向下传递过程中，一次在树的向上传递过程中。</p>
<p>针对这个通用协议，Flutter 拥有多种实现。最常用的是 <code>RenderBox</code>，它以二维的笛卡尔坐标进行运算。在盒子布局中，约束是最小及最大宽高。在布局过程中，子节点通过选择这些边界内的大小来确定其几何信息。子节点在布局中返回后，由父节点确定该子节点在父坐标系中的位置 [^3]。注意，子节点的布局并不取决于它的位置，这是因为它的位置直到它从布局中返回后才确定。因此父节点可以在无需重新计算子节点布局的情况下重新定位子节点的位置信息。</p>
<p>更广泛地讲，在布局期间，从父节点流向子节点的<strong>唯一</strong>信息是约束信息，从子节点流向父节点的<strong>唯一</strong>信息是几何信息。通过这些不变量可减少布局期间所需的工作量：</p>
<ul>
<li>如果父节点对子节点使用与上一次布局中相同的约束，且子节点没有将自己的布局标记为脏，那么该节点可立即从布局中返回，以切断布局的向下传递。</li>
<li>当父节点调用子节点的布局方法时，父节点会表明它是否使用从子节点返回的大小信息。如果父节点经常不使用此信息，即使子节点重新选择了大小，父节点依旧无需重新计算其布局，这是因为父节点需要保证新的大小符合现有约束。</li>
<li><strong>严格</strong>约束是指恰好由一个有效几何满足的约束。比如，如果最小最大宽度彼此相等，且最小最大高度彼此相等，那么满足这些约束的唯一大小便是具有该宽度及高度的大小。如果父节点提供了严格约束，即便父节点在布局中使用了子节点的大小，在子节点重新计算布局时，父节点的布局也无需重新计算，这是因为子节点在没有父节点新约束的情况下无法更改其大小。</li>
<li>渲染对象可以声明仅使用父节点提供的约束来确定其几何信息。此类声明通知框架： <strong>即便约束为非严格约束，以及父节点的布局取决于子节点的大小，</strong> 该渲染对象父节点的布局在子节点的布局重新计算时仍无需重新计算，这是因为子节点在没有父节点新约束的情况下无法更改其大小。</li>
</ul>
<p>这些优化措施的效果是，当渲染对象包含脏节点时，在布局过程中，只有这些节点以及它们周围子树的有限节点才允许被访问。</p>
<h3>次线性 widget 构建</h3>
<p>Flutter 使用类似于布局的次线性算法来构建 widget。widget 构建完成后，它们将被保留了用户页面逻辑结构的 <strong>element 树</strong> 保存。 Element 树是非常有必要的，这是因为 widget 自身是<strong>不可变的</strong>，这意味着（其他情况除外），它们无法记住父（或子）节点与其他 widget 的关系。 Element 还保存了与 Stateful widget 相关联的 <strong>state</strong> 对象。</p>
<p>由于用户输入（或来自其他地方的响应），比如开发者在关联的 state 对象上调用了 <code>setState()</code> 方法，element 可能会变脏。框架维护了一个脏 element 列表，使得 <strong>构建</strong> 过程可跳过干净的 element，直接跳转到脏的 element。构建过程中，信息在 element 树中向下 <strong>单向</strong> 传递，这意味着该阶段中每个 element 最多会被访问一次。一个 element 一旦被清洗，它将不会再次变脏，这是因为通过归纳，它所有的祖先 element 也都是干净的 [^4]。</p>
<p>由于 widget 是<strong>不可变的</strong>，因此父节点使用相同的 widget 来重新构建 element，如果 element 没有将自己标记为脏，那么该 element 可立即从构建中返回，以切断构建的向下传递。另外，element 只需比较两个 widget 所引用的对象标识来确定新 widget 与旧 widget 是否相同。开发者可利用该优化实现<strong>投影</strong>模式，即 widget 包含了被存储为成员变量、在构建过程中预先构建的子 widget</p>
<p>构建过程中，Flutter 同时使用 <code>Inheritedwidgets</code> 来避免父链的遍历。如果 widget 经常遍历它们的父链，比如确定当前的主题颜色，那么构建阶段树的深底将变为 O(N²)，由于 Flutter 的积极可组合性，其数量可能非常巨大。为了避免这些父链的遍历，框架通过在每个 element 上维护一个 <code>Inheritedwidget</code> 哈希表来向下传递 element 树中的信息。通常情况下，多个 element 引用相同的哈希表，并且该表仅在 element 引入新的 <code>Inheritedwidget</code> 时改变。</p>
<h3>线性协调</h3>
<p>不同于传统做法，Flutter 没有使用树差异比较算法。相反，框架通过使用 O(N) 算法独立地检查每个 element 的子节点来决定是否重用该 element。子列表协调算法针对以下情况进行了优化：</p>
<ul>
<li>旧的子列表为空。</li>
<li>两个列表完全相同。</li>
<li>在列表的某个位置插入或删除一个或多个 widget。</li>
<li>如果新旧列表都包含相同 key [^5] 的 widget，那么这两个 widget 就会被认为是相同的。</li>
</ul>
<p>通常的做法是从新旧子列表的头部和尾部开始对每一个 widget 的运行时类型和 key 进行匹配，这样就可能找到在两个列表中间所有不匹配子节点的（非空）范围。然后框架将旧子列表中该范围内的子项根据它的 key 放入一个哈希表中。接下来，框架将会遍历新的子列表以寻找该范围内能够匹配哈希表中的 key 的子项。无法匹配的子项将会被丢弃并从头开始重建，匹配到的子项则使用它们新的 widget 进行重建。</p>
<h3>树结构优化</h3>
<p>重用 element 对性能非常重要，这是因为 element 拥有两份关键数据：Stateful widget 的状态对象及底层的渲染对象。当框架能够重用 element 时，用户界面的逻辑状态信息是不变的，并且可以重用之前计算的布局信息，这通常可以避免遍历整棵子树。事实上，重用 element 是非常有价值的，因为 Flutter 支持 <strong>全局</strong> 树更新，以此保留状态和布局信息。</p>
<p>开发者可通过将 <code>GlobalKey</code> 与其中一个 widget 相关联来实施全局树更新。每个全局 key 在整个应用中都是唯一的，并使用特定于线程的哈希表进行注册。在构建过程中，开发者可以使用全局 key 将 widget 移动到 element 树的任意位置。框架将不会在该位置上重新构建 element，而是检查哈希表并将现有的 element 从之前的位置移动到新的位置，从而保留整棵子树。</p>
<p>重新构建的子树中的渲染对象能够保留它们的布局信息，这是因为布局约束是渲染树从父节点传递到子节点的唯一信息。子列表发生变化后，父节点将会被标记为脏，但如果新的父节点传递给子节点的布局约束与该子节点从旧的父节点接收到的相同，那么子节点可立即从布局中返回，从而切断布局的向下传递。</p>
<p>开发者广泛使用全局 key 和全局树更新来实现 hero transition 及导航等效果。</p>
<h3>恒定因子优化</h3>
<p>除了上述算法优化，实现积极可组合还需依赖几个重要的恒定因子优化。这些优化对于上面所讨论的主要算法是非常重要的。</p>
<ul>
<li><strong>子模型无关</strong>。与大多数使用子列表的工具包不同， Flutter 渲染树不会记住一个特定的子模型。比如，类 <code>RenderBox</code> 存在一个抽象的 <code>visitChildren()</code> 方法，而非具体的 <strong>firstChild</strong> 和 <strong>nextSibling</strong> 接口。许多子类仅支持直接作为其成员变量的单个子项，而非子项列表。比如，由于 <code>RenderPadding</code> 仅支持单个子节点，因此它拥有一个更为简单、高效的布局方法。</li>
<li><strong>视觉渲染树、widget 逻辑树</strong>。在 Flutter 中，渲染树在与设备无关的视觉坐标系中运行，这意味着即使 x 轴的读取方向是从右到左，其左侧的值依旧小于右侧。Widget 树通常在逻辑坐标中运行，这意味着拥有 <strong>开始</strong> 和 <strong>结束</strong> 值的视觉解释取决于读取方向。逻辑坐标到视觉坐标的转换是在 widget 树和渲染树之间的切换中完成的。这种方法更为高效的原因是，渲染树中的布局和绘制计算比 widget 到渲染树的切换更加频繁，并且可以避免重复的坐标转换。</li>
<li><strong>通过专门的渲染对象处理文本</strong>。大多数渲染对象都不清楚文本的复杂性。相反，文本是由专门的渲染对象 <code>RenderParagraph</code> 进行处理，它是渲染树中的一个叶子节点。开发者使用组合形式将文本并入到用户界面中，而非使用文本感知渲染对象进行子类化。该模式意味着 <code>RenderParagraph</code> 可避免文本布局在父节点提供相同布局约束下的重复计算，这是非常常见的，即使在树优化期间也是如此。</li>
<li><strong>可观察对象</strong>。 Flutter 使用模型观察及响应设计模式。显而易见，响应模式占主导地位，但 Flutter 在某些叶子节点的数据结构上使用了可观察对象。比如 <code>Animation</code> 会在值发生变化时通知观察者列表。 Flutter 将这些可观察对象从 widget 树转移到渲染树中，渲染树直接监听这些对象，并在它们改变时仅重绘管道的相关阶段。比如，更改 <code>Animation&lt;Color&gt;</code> 可能只触发绘制阶段，而非整个构建和绘制阶段。</li>
</ul>
<p>总的来说，这些优化对通过积极组合方式产生的大型树结构的性能产生了重大影响。</p>
<h3>元素和 RenderObject 树的分离</h3>
<p>Flutter 中的<code>RenderObject</code>和<code>Element</code>（Widget）树是同构的（严格来说，<code>RenderObject</code>树是<code>Element</code>树的一个子集）。一个明显的简化是将这些树合并成一棵树。然而，在实践中，将这些树分开是有很多好处的：</p>
<ul>
<li>
<p><strong>性能</strong> 当布局发生变化时，只有布局树的相关部分需要被行走。由于组成的原因，元素树经常有许多额外的节点需要被跳过。</p>
</li>
<li>
<p><strong>明确性</strong> 更清晰的关注点分离允许小部件协议和渲染对象协议各自针对其特定需求进行专业化，简化了 API 表面，从而降低了错误的风险和测试负担。</p>
</li>
<li>
<p><strong>类型安全</strong> 呈现对象树可以更具有类型安全性，因为它可以在运行时保证子代将具有适当的类型（每个坐标系，例如，有自己的呈现对象类型）。组成部件可以不考虑布局时使用的坐标系（例如，同一个部件暴露了应用程序模型的一部分，可以在盒子布局和狭长布局中使用），因此在元素树中，验证呈现对象的类型需要在树上行走。</p>
</li>
</ul>
<h2>无限滚动</h2>
<p>对于工具包来说，实现无限滚动列表是非常困难的。Flutter 支持基于 <strong>构造器</strong> 模式实现的简单无限滚动列表界面，其中 <code>ListView</code> 使用回调按需构建 widget，即它们只在滚动过程中才对用户可见。该功能需要 <strong>视窗感知布局</strong> 及 <strong>按需构建 widget</strong> 的支持。</p>
<h3>视窗感知布局</h3>
<p>同 Flutter 中的大多数东西一样，可滚动的 widget 是基于组合模式构建的。可滚动 widget 的外部是一个 <code>Viewport</code>，这是一个拥有更大内部空间的盒子，这意味着它的子节点可以超出视窗口的边界并滚动到可视区域中。但是，视窗口没有 <code>RenderBox</code> 子节点，而是拥有被称为 <strong>sliver</strong>，实现了视窗感知协议的<code>RenderSliver</code> 子节点。</p>
<p>sliver 布局协议中父节点向下传递给子节点的约束信息及接收到的几何信息的结构与盒子布局相同。但约束和几何数据在两个协议之间不同。在 sliver 协议中，子节点接收到的是关于视窗口的信息，这其中包含剩余的可见空间量。它们返回的几何数据支持各种滚动链接效果，包括可折叠标题及视差。</p>
<p>不同的 sliver 以不同的方式填充视窗口中的可用空间。比如，生成线性子列表的 sliver 按顺序排列每个子节点，直到 sliver 中无任何子节点或可用空间。同理，生成二维子节点网格的 sliver 仅填充网格中的可见区域。由于它们知道还有多大的可见空间，sliver 可以生成有限的子节点，即使它们可能生成无限的子节点。</p>
<p>可组合 sliver 来创建特定的滚动布局和效果。比如，单个视窗口可以有一个折叠标题、一个线性列表和一个网格。所有这些 sliver 将按照 sliver 布局协议进行协作，只生成那些在视窗口实际可见的子节点，而不管这些子节点是否属于标题、列表或网格[^6]。</p>
<h3>按需构建 widget</h3>
<p>如果 Flutter 拥有一个严格的<strong>从构建到布局，再到绘制</strong>的管道，那么前面的内容将不足以实现无限滚动列表，这是因为只有在布局阶段才能通过视窗口获取可用的空间信息。如果没有额外的机制，在布局阶段构建用于填充空间的 widget 已经太迟了。 Flutter 使用将管道的构建与布局交叉在一起的方式来解决这个问题。在布局阶段的任意时刻，<strong>只要这些 widget 是当前布局的渲染对象的子节点</strong>，框架就可以按需构建新的 widget。</p>
<p>只有严格控制构建及布局中消息传播的算法，才能实现构建和布局的交叉执行。也就是说，在构建过程中，消息只能沿构建树向下传递。当渲染对象进行布局时，布局遍历过程中并没有访问该渲染对象的子树，这意味通过子树构建的写入无法使到目前为止已进入布局计算过程的任何信息失效。无独有偶，一旦布局从渲染对象中返回，在当前布局过程中，该渲染对象将永远不会被再次访问，这意味后续布局计算生成的任何写入都不会使用于构建渲染对象的子树的信息失效。</p>
<p>此外，线性协调及树结构优化对于在滚动过程中有效更新 element，以及当 element 在视窗口边缘滚动进出视图期间修改渲染树至关重要。</p>
<h2>人机工程 API</h2>
<p>速度只有在框架能够被有效使用时才有意义。为了引导设计更高可用性的 Flutter API， Flutter 已经在与开发者进行的广泛用户体验研究中进行了反复测试。这些研究有时证实了已有的设计决策，有时有助于引导功能的优先级，有时会改变 API 的设计方向。比如，Flutter 的 API 文档很多，用户体验的研究不仅证实了这些文档的价值，也同时强调了示例代码及说明性图表的重要性。</p>
<p>本节将要讨论 Flutter API 设计中为提高可用性所做的一些决策。</p>
<h3>与开发者思维模式相匹配的专项 API</h3>
<p>Flutter 中 <code>widget</code>、<code>Element</code> 和 <code>RenderObject</code> 的基类节点不定义子类模型。该机制允许每个节点对适用于该节点的子模型进行定制化。</p>
<p>大多数 <code>widget</code> 对象都有一个子 <code>widget</code> 对象，因此它只暴露了一个 <code>child</code> 参数。一些 widget 支持任意数量的子节点，并暴露了一个获取子节点列表的 <code>children</code> 参数。有些 widget 无任何子节点、不保留内存且无任何参数。同样的，<code>RenderObjects</code> 暴露特定于子模型的 API。 <code>RenderImage</code> 是一个没有子节点的叶子节点。 <code>RenderPadding</code> 只持有一个子节点，因此它有一个指向单个子节点的指针存储空间。 <code>RenderFlex</code> 接受任意数量的子节点，并通过链表对其进行管理。</p>
<p>在一些罕见情况下，将使用更复杂的子类模型。渲染对象 <code>RenderTable</code> 的构造函数需要使用二维数组来存储子节点，所以该类暴露了用于控制行和列数量的 getter 及 setter 方法，还有一些可以用 x、y 轴坐标来替换单个子节点的特殊方法，可通过提供一个新的子节点数组来添加新行，并用单个数组及列的个数来替换整个子节点列表。该对象并不像大多数渲染对象那样使用链表，而是使用可索引数组来实现。</p>
<p><code>Chip</code> widget 和 <code>InputDecoration</code> 对象具有与其控制中的插槽相匹配的字段。如果一个通用子模型将强制语义定义在子列表之上，比如将第一个子节点定义为前缀，第二个子节点定义为后缀，那么专用子模型允许使用特有的命名属性。</p>
<p>这种灵活性允许树中的每个子节点以其最常用的方式操作它的角色。很少有人想要在表格中插入一个单元格，从而导致其他所有单元格被环绕；同样的，很少有人想要通过索引而不是通过引用从 flex 行中删除子项。</p>
<p><code>RenderParagraph</code> 对象是最极端的情况：它有一个完全不同类型的子节点，<code>TextSpan</code>。在 <code>RenderParagraph</code> 的边界，<code>RenderObject</code> 树会被转换为 <code>TextSpan</code> 树。</p>
<p>专门用于满足开发者期望的 API 的一切方法不仅适用于子模型。</p>
<p>专门存在一些琐碎的 widget，以便开发者在寻找问题解决方案时能够发现并使用它们。一旦知道如何使用 <code>Expanded</code> 和大小为零的 <code>SizedBox</code> 子部件，就可以轻松地为行或列添加空格，但你会发现这种模式是没有必要的，因为搜索 <code>space</code> 所找到的 <code>Spacer</code>，它是直接使用 <code>Expanded</code> 和 <code>SizedBox</code> 来达到同样的效果的。</p>
<p>同理，可以通过在构建过程中不包含 widget 子树来轻松隐藏 widget 子树。但开发者通常希望有一个 widget 来执行该操作，因此 <code>Visibility</code> 的存在便是将此模式封装在一个简单的可重用 widget 中。</p>
<h3>明确的参数</h3>
<p>UI 框架往往拥有大量的属性，因此很少有开发者能够记住每个类的每个构造函数参数的作用。由于 Flutter 使用响应式编程范式，因此在 Flutter 中，构建方法通常会对构造函数进行多次调用。通过利用 Dart 的命名参数，Flutter 中的 API 能够使这些构建方法保持清晰易懂。</p>
<p>该模式已被扩展到任何具有多个参数（尤其是具有 boolean 类型参数）的方法，因此独立的 <code>true</code> 或 <code>false</code> 值在方法调用中总是自我描述的。此外，为避免 API 中通常由双重否定所造成的困惑， boolean 类型的参数和属性始终以肯定的形式命名（比如，使用 <code>enabled: true</code> 而非 <code>disabled: false</code>）。</p>
<h3>参数陷阱</h3>
<p>在 Flutter 框架中被大量使用的一项技术是定义不存在错误条件的 API。这样可以避免考虑整个错误类别。</p>
<p>比如插值函数允许插值的一端或两端为空，而不是将其定义为错误：两个空值之间的插值永远为空，并且从空值或空值插值等效于对指定类型进行零模拟插值。这意味着不小心将 null 传递给插值函数的开发者不会遇到错误，而是会得到一个合理结果。</p>
<p>一个更加微妙的例子是 <code>Flex</code> 布局算法。该布局给予 flex 渲染对象的空间被它的子节点所划分。因此 flex 的大小应该是整个可用空间。在最初的设计中提供无限空间将导致失败：这意味着 flex 应该是无限大且无用的布局设置。然而，通过对 API 的改造，在为 flex 对象提供无限空间时，渲染对象会调整自身大小来满足所需子节点的大小，从而减少可能出现的错误次数。</p>
<p>该方法也可用于避免使用允许创建不符合逻辑的数据的构造函数。例如，<code>PointerDownEvent</code> 的构造函数不允许将 <code>PointerEvent</code> 的 <code>down</code> 属性设置为 <code>false</code>（这种情况是自相矛盾的）；相反，构造函数没有关于字段 <code>down</code> 的参数，且将值始终设置为 <code>true</code>。</p>
<p>一般情况下，该方法用于为输入域中的所有值定义有效的解释。最简单的例子是 <code>Color</code> 的构造函数。相对于接受四个整型参数（分别用于表示红色、绿色、蓝色和 alpha），其中任何一个都可能超出范围，它的默认构造函数仅接受一个整数值，并定义每位的含义（例如，低八位代表红色），以便任何输入都是有效的颜色值。</p>
<p>一个更复杂的例子是 <code>paintImage()</code> 函数。该函数需要 11 个参数，其中一些具有相当宽泛的输入域，但它们都经过精心设计且大部分都能够彼此相交，因此很少出现无效组合。</p>
<h3>积极报告错误</h3>
<p>并非所有的错误都能被设计出来。对于那些遗漏的错误，在 debug 版本中，Flutter 通常会尝试尽早捕获并立即报告。它使用了大量的断言，对构造函数参数进行了详细的完整性检查，并监视其生命周期，一旦检测到不一致，它们会立即引发异常。</p>
<p>这在某些情况下是极端情况：比如，在执行单元测试时，无论测试用例正在做什么，每个 <code>RenderBox</code> 子类都会主动地检查其内部大小调整方法是否满足内部大小调整契约。这有助于捕获可能无法执行的 API 错误。</p>
<p>当异常抛出时，它们会包含尽可能多的信息。 Flutter 中的一些错误会主动探测相关的堆栈跟踪信息，以确定实际错误最可能发生的位置。其他错误则通过相关树来确定坏数据的来源。最常见的错误包含详细说明（在某些情况下会包含避免错误的示例代码），或指向其他文档的链接。</p>
<h3>响应式</h3>
<p>可变的基于树结构的 API 受二元访问模式的影响：创建树的原始状态通常使用与后续更新完全不同的操作集。Flutter 的渲染层使用了这种范式，因为它是维护持久树的有效方法，是高效布局和绘制的关键所在。但这也意味着，与渲染层的直接交互是十分笨拙的，甚至极其容易出错。</p>
<p>Flutter 在 widget 层引入了一个使用响应式来操作底层渲染树的组合机制[^7]。该 API 通过将树的创建和更新步骤整合到一个单一的树结构描述（构建）中，从而将树操作抽象出来，这包括：每次系统状态更新之后，开发者用于描述用户界面的新配置；框架对于新配置所需要进行的一系列树更新计算。</p>
<h3>插值</h3>
<p>由于 Flutter 鼓励开发者描述与当前应用状态相匹配的界面配置，因此存在一种在这些配置之间执行隐式的动画机制。</p>
<p>例如，假设界面在状态 S1 由一个圆形组成，在状态 S2 时由一个正方形组成。如果没有动画机制，状态更改将导致不和谐的界面更改。隐式动画则允许界面在几个帧的时间里由圆形平滑地过渡到正方形。</p>
<p>每个可执行隐式动画的特性都包含一个 Stateful widget，它用于记录输入的当前值，并在输入值改变时开始执行动画序列，并在指定的持续时间内从当前值转换为新值。</p>
<p>这是使用不可变对象的 <code>lerp</code>（线性插值）函数来实现的。每个状态（这里为圆形和正方形）代表一个配置中包含恰当设置（比如颜色、笔划宽度等）且知道如何绘制自己的不可变对象。在动画绘制中间步骤时，开始和结束值连同表示动画中点的 <strong>t</strong> 值一并传递给 <code>lerp</code>函数。其中 0.0 代表开始 <code>start</code>，1.0 代表结束 <code>end</code>[^8]，并且该方法返回表示中间阶段的第三个不可变对象。</p>
<p>对于从圆形到正方形的转换，<code>lerp</code> 函数将返回一个圆角正方形对象，其半径被描述为从 <strong>t</strong> 值导出的分数，使用 <code>lerp</code> 函数进行插值计算的颜色，以及使用 <code>lerp</code> 函数进行双倍插值计算的笔划宽度。该对象与圆形、正方形一样具有相同的接口实现，并且可以在请求时进行自我绘制。</p>
<p>该技术允许状态机、状态到配置的映射、动画和插值机制以及与如何绘制每一桢完全分离的特定逻辑。</p>
<p>在 Flutter 中，该机制得到了广泛应用，无论是像 <code>Color</code> 和 <code>Shape</code> 这样的基本类型，还是像 <code>Decoration</code>，<code>TextStyle</code> 或 <code>Theme</code> 这样更为复杂的类型，都是可以进行插值处理的。它们通常是由可插入组件构成的，并且插入更复杂的对象通常就像递归插入描述复杂对象的所有值一样简单。</p>
<p>一些插值对象由类层次结构定义。比如，形状由 <code>ShapeBorder</code> 接口表示，并且存在多种形状类型，包括： <code>BeveledRectangleBorder</code>、<code>BoxBorder</code>、<code>CircleBorder</code>、<code>RoundedRectangleBorder</code> 和 <code>StadiumBorder</code>。单一的 <code>lerp</code> 函数并不能了解所有可能的类型信息，因此接口定义了 <code>lerpFrom</code> 和 <code>lerpTo</code> 方法以替代静态的 <code>lerp</code> 方法。当被告知从形状 A 切换到 B 时，将首选询问 B 是否 <code>lerpFrom</code> A，如其答案为否，则询问 A 是否可以 <code>lerpTo</code> B （如两者的答案均为否，如果 <code>t</code> 的值小于 0.5 则返回 A，否则返回 B）。</p>
<p>这允许类层次结构的任意扩展，后续新增的能够在先前已知值与它们之间进行插值处理。</p>
<p>在某些情况下，插值本身不能被任何可用的类描述，并且定义一个私有类来描述中间状态。比如在 <code>CircleBorder</code> 和 <code>RoundedRectangleBorder</code> 之间进行插值时就是如此。</p>
<p>该机制的另外一个优点是：它可以处理从中间态到新值的插值。比如，在圆形到正方形过渡的中途，形状可能再次改变，导致动画需要插值到一个三角形。只要该三角形类是 <code>lerpFrom</code> 圆形到正方形的中间类，就可以无缝进行转换。</p>
<h2>结论</h2>
<p>Flutter 一切都是 widget 的口号是围绕着通过组合 widget 来构建用户界面， widget 又由更为基础的 widget 构成。这种积极组合的结果是需要精心设计的算法和数据结构才能有效处理大量的 widget。通过一些额外的机制，这些数据结构还能使开发者轻松构建无限滚动列表，以便在 widget 可见时进行按需构建。</p>
]]></content:encoded>
      <enclosure url="https://w.wallhaven.cc/full/4v/wallhaven-4vp2x3.png?x-oss-process=image/resize,limit_0,m_fill,w_1366,h_768/quality,Q_92/format,webp" type="image/"/>
    </item>
    <item>
      <title>WebViewJavascriptBridge</title>
      <link>https://www.linkjm.com/posts/iOS/other/JavaScriptBridge.html</link>
      <guid>https://www.linkjm.com/posts/iOS/other/JavaScriptBridge.html</guid>
      <source url="https://www.linkjm.com/rss.xml">WebViewJavascriptBridge</source>
      <description>最近一直在忙，今天抽空写一下 H5 和 Native 的交互 一、选择 项目本身 webview 使用的是 WKWebview，其实 WKWebview 自带的 messageHandle 也可以满足此需求 JSContext,源自于 JavaScriptCore 框架中的东西，最后不使用此方案源于一下几点 但是其中繁杂的字符串使用，让我觉的可能会由于...</description>
      <category>iOS</category>
      <pubDate>Sun, 05 Aug 2018 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>最近一直在忙，今天抽空写一下 H5 和 Native 的交互</p>
</blockquote>
<h2>一、选择</h2>
<ul>
<li>项目本身 webview 使用的是 WKWebview，其实 WKWebview 自带的 messageHandle 也可以满足此需求</li>
<li>JSContext,源自于 JavaScriptCore 框架中的东西，最后不使用此方案源于一下几点
<ul>
<li>但是其中繁杂的字符串使用，让我觉的可能会由于粗心出现不可预知的错误</li>
<li>加载时机的问题，当你重新 loadrequest 的时候，会导致 js 注入失败</li>
<li>回调方法略复杂</li>
</ul>
</li>
<li>JavaScriptBridge，最后选择此库源于以下几点
<ul>
<li>使用简单，注册完毕之后设置完代理，只需要负责注册方法和调用方法</li>
<li>回调简单，两端回调 responsecallback 包含在注册的方法中。使用 block</li>
<li>三端通用，JavaScript 和 iOS、Android 都可以（<a href="https://github.com/wendux/WebViewJavascriptBridge" target="_blank" rel="noopener noreferrer">Android 版本库</a>）</li>
<li>Ps :关于 Android 版本库，其中很多是按照 iOS 版的 JavaScriptBridge 改写的。但是其中有很多问题，尤其是各种调用时机问题，上面的链接是经过我旁边的 Android 小哥试了四五个版本之后发现的，修复了各种改写版的问题</li>
</ul>
</li>
</ul>
<h2>二、使用</h2>
<ul>
<li>首先需要引入 WebViewJavascriptBridge 库</li>
</ul>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">#import</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "WebViewJavascriptBridge.h"</span></span></code></pre>
</div><ul>
<li>初始化，此处为了方便子类使用，所以在基类中注册 bridge，并 return bridge 对象，方便子类调用</li>
</ul>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">#pragma mark</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> - 桥接</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)InitializeWebViewJavascriptBridge {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 注册桥接</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    self</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">bridge</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [SWHybridManager </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">setJavaScriptBridgeWithWebView:</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.webView </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">controller:</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[WKWebViewJavascriptBridge </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">enableLogging</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">WKWebViewJavascriptBridge *bridge </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [WKWebViewJavascriptBridge </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">bridgeForWebView:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">webView];</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[bridge </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">setWebViewDelegate:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">controller];</span></span></code></pre>
</div><ul>
<li>注册方法供 JavaScript 调用</li>
</ul>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">__weak </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">typeof</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(controller)wController </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> controller;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/****************************公共方法注册-Start*********************/</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    //MARK:打开窗体</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    [bridge </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">registerHandler:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">HandlerFunctionNameOpenWindow </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">handler:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">^(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> data, WVJBResponseCallback responseCallback) {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        __strong </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">typeof</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(wController)</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">sController</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> wController;</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">        NSLog</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">\n</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">调用了openWindow: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">%@</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, data);</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        NSDictionary</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *dict </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSDictionary</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *)data;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        SWOpenWindowModel *model </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [SWOpenWindowModel </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">yy_modelWithDictionary:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">dict];</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        [</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> pushViewController:</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">sController</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> data:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">model </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">responseCallback:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">responseCallback];</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }];</span></span></code></pre>
</div><ul>
<li>调用 JavaScript 方法</li>
</ul>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.bridge </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">callHandler:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">callFunctionNameGetSearchKeyWord </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">data:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">json];</span></span></code></pre>
</div><h2>三、方法名定义</h2>
<ul>
<li>因为方法名的定义是字符串，所以建议采用常量字符串，防止拼写错误</li>
<li>其次不建议采用宏定义</li>
<li>我采用以下方法</li>
<li>桥接管理类的.h</li>
</ul>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/**打开窗体 */</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">FOUNDATION_EXPORT  </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> HandlerFunctionNameOpenWindow;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/** 关闭窗口*/</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">FOUNDATION_EXPORT  </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> HandlerFunctionNameCloseWindow;</span></span></code></pre>
</div><ul>
<li>桥接管理类的.m</li>
</ul>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/**打开窗体 */</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> HandlerFunctionNameOpenWindow </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"openWindow"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/** 关闭窗口*/</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> HandlerFunctionNameCloseWindow </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"closeWindow"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span></code></pre>
</div><ul>
<li>使用时直接使用常量字符串即可</li>
<li>注意点：如类似我使用在基类传入控制器和 webview 到管理类中，在类中使用 controller 要注意循环引用，否则会导致控制器无法释放</li>
</ul>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">+ (WKWebViewJavascriptBridge *)setJavaScriptBridgeWithWebView:(WKWebView *)webView controller:(__kindof SWBaseWebViewController *)controller;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">__weak </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">typeof</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(controller)wController </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> controller; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 弱引用传入控制器</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">__strong </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">typeof</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(wController)</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">sController</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> wController; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 在block内部强引用</span></span></code></pre>
</div><h2>四、数据传输</h2>
<ul>
<li>iOS 端直接返回字典即可</li>
<li>我代码中是返回 json 字符串，为了与 Android 统一，方便 H5 解析数据</li>
</ul>
<h2>五、JavaScript 代码</h2>
<div class="language-javascript line-numbers-mode" data-highlighter="shiki" data-ext="javascript" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-javascript"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> setupWebViewJavascriptBridge</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">callback</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">WebViewJavascriptBridge</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> callback</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">WebViewJavascriptBridge</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">WVJBCallbacks</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">WVJBCallbacks</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">push</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">callback</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  window</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">WVJBCallbacks</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">callback</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  var</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> WVJBIframe</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> document</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">createElement</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"iframe"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  WVJBIframe</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">style</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">display</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "none"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  WVJBIframe</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">src</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "https://__bridge_loaded__"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  document</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">documentElement</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">appendChild</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">WVJBIframe</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  setTimeout</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    document</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">documentElement</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">removeChild</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">WVJBIframe</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }, </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-javascript line-numbers-mode" data-highlighter="shiki" data-ext="javascript" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-javascript"><span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">setupWebViewJavascriptBridge</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">bridge</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  /* Initialize your app here */</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  bridge</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">registerHandler</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"JS Echo"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">function</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">responseCallback</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"JS Echo called with:"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    responseCallback</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">data</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  });</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">  bridge</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">callHandler</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="--shiki-light:#98C379;--shiki-dark:#98C379">    "ObjC Echo"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    { </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"value"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> },</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    function</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> responseCallback</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E06C75;--shiki-light-font-style:italic;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">responseData</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"JS received response:"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">responseData</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  );</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">});</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>六、注意事项</h2>
<ul>
<li>如果产生调用不通的问题，多为 JavaScript 调用时机问题</li>
<li>注意桥接的代理</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>初探机器学习框架CoreML</title>
      <link>https://www.linkjm.com/posts/iOS/other/coreML.html</link>
      <guid>https://www.linkjm.com/posts/iOS/other/coreML.html</guid>
      <source url="https://www.linkjm.com/rss.xml">初探机器学习框架CoreML</source>
      <description>CoreML 是 iOS 11 新推出的机器学习框架，是人工智能的核心内容，他可以在训练好的机器学习模型应用到 APP 中 所谓已训练模型 (trained model)指的是对一组训练数据应用了某个机器学习算法后，所生成的一组结果 Core ML 是领域特定 (domain-specific) 框架和功能的基础所在。Core ML 为 Vision ...</description>
      <category>iOS</category>
      <pubDate>Tue, 21 Nov 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>CoreML 是 iOS 11 新推出的机器学习框架，是人工智能的核心内容，他可以在训练好的机器学习模型应用到 APP 中</p>
</blockquote>
<figure><img src="http://upload-images.jianshu.io/upload_images/74454-4726f1eccb39b18c.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>所谓已训练模型 (trained model)指的是对一组训练数据应用了某个机器学习算法后，所生成的一组结果 Core ML 是领域特定 (domain-specific) 框架和功能的基础所在。Core ML 为 Vision 提供了图像处理的支持，为 Foundation 提供了自然语言处理的支持（例如 NSLinguisticTagger 类），为 <a href="https://developer.apple.com/documentation/gameplaykit" target="_blank" rel="noopener noreferrer">GameplayKit</a> 提供了对学习决策树 (learned decision tree) 进行分析的支持。Core ML 本身是基于底层基本类型而建立的，包括 Accelerate、BNNS 以及 Metal Performance Shaders 等。</p>
<h2>获取模型</h2>
<p>Core ML 支持多种机器学习模型，其中包括了神经网络 (Neural Network)、组合树 (Tree Ensemble)、支持向量机 (Support Vector Machine) 以及广义线性模型 (Generalized Linear Model)。Core ML 的运行需要使用 Core ML 模型格式（也就是以 .mlmodel 扩展名结尾的模型）。</p>
<p>Apple 提供了一些常见的<a href="https://developer.apple.com/machine-learning/" target="_blank" rel="noopener noreferrer">开源模型</a>供大家使用，这些模型已经使用了 Core ML 模型格式。您可以自行下载这些模型，然后就可以开始在应用中使用它们了。</p>
<h2>工程实例</h2>
<p>首先因为 CoreML 和 Vision 都是 iOS 11 才有的功能，你要确保 Xcode9 和 iOS 11 的设备，当然模拟器也可以。开发语言使用 Swift4</p>
<ol>
<li>
<p>将模型添加到 Xcode 中<br>
创建工程并引入模型文件<br>
<img src="https://storage1.cuntuku.com/2017/11/21/Snip20171121_6.png" alt="Snip20171121_6.png" loading="lazy"><br>
单击这个文件就可以看到这个模型的详细信息<br>
<a href="https://cuntuku.com/image/KNKHr" target="_blank" rel="noopener noreferrer"><img src="https://storage2.cuntuku.com/2017/11/21/Snip20171121_7.md.png" alt="Snip20171121_7.md.png" loading="lazy"></a><br>
下面是这个模型的官方介绍</p>
<blockquote>
<p>Detects the dominant objects present in an image from a set of 1000 categories such as trees, animals, food, vehicles, people, and more.大意为可以从 1000 个类别中筛选传树木、动物、食品、汽车、人等等。</p>
</blockquote>
</li>
<li>
<p>模型解读<br>
inputs 中写了需要一个 image 大小 299*299；outputs 里会有两个参数 classLabelProbs 和 classLabel<br>
，classLabelProbs 是一个[string:Double]的字典数组，数组里每一个字典就是这个输入图片分析得出可能的一个结果 string 就是对图片类型的描述，而 double 就是可能性百分比。另一个 classLabel 就是最有可能的一个一个结果描述</p>
<pre><code>*Model Class*下面有这个类文件点进去可以看到如下三个类

**input输入源，可以看到它需要一个CVPixelBuffer格式的图片作为输入**
[![Snip20171121_8.md.png](https://storage2.cuntuku.com/2017/11/21/Snip20171121_8.md.png)](https://cuntuku.com/image/KNlCE)
**output可以看到输出的两个参数classLabel和classLabelProbs正式我们上面有介绍过的所有可能的结果数组与最有可能的结果描述**
[![Snip20171121_9.md.png](https://storage1.cuntuku.com/2017/11/21/Snip20171121_9.md.png)](https://cuntuku.com/image/KNgTJ)
**inceptionv3调用这个类的Prediction方法来开始进行分析**
[![Snip20171121_10.md.png](https://storage1.cuntuku.com/2017/11/21/Snip20171121_10.md.png)](https://cuntuku.com/image/KN1Im)
</code></pre>
</li>
<li>
<p>编写代码<br>
定义一个 imageView，一个 Label，一个 button<br>
点击按钮打开相册选取图片，选取完成执行下面的方法，然后再 label 显示分析结果</p>
</li>
</ol>
<div class="language-swift line-numbers-mode" data-highlighter="shiki" data-ext="swift" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-swift"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">	func</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> process</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">_</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic"> image</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: UIImage) {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        imageView.image = image</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 需要CVPixelBuffer格式的输入源</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        guard</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> pixelBuffer = image.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">pixelBuffer</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">width</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">299</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">height</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">299</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">            return</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        //I have `Use of unresolved identifier 'Inceptionv3'` error here when I use New Build System (File > Project Settings)   ¯\_(ツ)_/¯</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> model = </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">Inceptionv3</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        do</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">            // 调用model的prediction方法进行分析</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">            let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> output = </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">try</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> model.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">prediction</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">image</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: pixelBuffer)</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">            // 打印输出结果</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">            let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> probs = output.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">classLabelProbs</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">sorted</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">$0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.value > </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">$1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.value }</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">            if</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> prob = probs.</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">first</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">                Label.text = </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">\(</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">prob.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">key</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">)</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> \(</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">prob.value</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">)</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">            }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        catch</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">            self</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">presentAlertController</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">withTitle</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: title,</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">                                        message</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: error.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">localizedDescription</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>运行效果</h2>
<p><a href="https://cuntuku.com/image/KNToV" target="_blank" rel="noopener noreferrer"><img src="https://storage2.cuntuku.com/2017/11/21/Snip20171121_3.md.png" alt="Snip20171121_3.md.png" loading="lazy"></a><br>
<a href="https://cuntuku.com/image/KNvFW" target="_blank" rel="noopener noreferrer"><img src="https://storage1.cuntuku.com/2017/11/21/Snip20171121_4.md.png" alt="Snip20171121_4.md.png" loading="lazy"></a><br>
<a href="https://cuntuku.com/image/KNNdd" target="_blank" rel="noopener noreferrer"><img src="https://storage2.cuntuku.com/2017/11/21/Snip20171121_5.md.png" alt="Snip20171121_5.md.png" loading="lazy"></a></p>
<h2>Demo</h2>
<p><a href="https://github.com/OrageKK/coreML-Examples" target="_blank" rel="noopener noreferrer">👉Demo 下载</a><br>
<strong>如果有帮助烦请点 star</strong></p>
]]></content:encoded>
      <enclosure url="http://upload-images.jianshu.io/upload_images/74454-4726f1eccb39b18c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" type="image/"/>
    </item>
    <item>
      <title>Test Three ways to call</title>
      <link>https://www.linkjm.com/posts/iOS/other/test-Three-ways-to-%20call.html</link>
      <guid>https://www.linkjm.com/posts/iOS/other/test-Three-ways-to-%20call.html</guid>
      <source url="https://www.linkjm.com/rss.xml">Test Three ways to call</source>
      <description>LabelPhoneNum 使用 YYtext 实现 label 中的某些文字点击拨打电话---Github 真机测试结果 设备型号：iphone6s 系统:10.1.1 Xcode 版本：8.1 三种打电话的方法 方法一:网上说使用此方法，电话结束后进入联系人列表，测试结果为：正常，电话结束后返回程序 方法二：测试结果为先弹窗后拨打，呼叫结束后返回程...</description>
      <category>iOS</category>
      <pubDate>Sat, 12 Nov 2016 12:31:17 GMT</pubDate>
      <content:encoded><![CDATA[
<p>使用 YYtext 实现 label 中的某些文字点击拨打电话</p>
]]></content:encoded>
    </item>
    <item>
      <title>神经网络模型训练</title>
      <link>https://www.linkjm.com/posts/iOS/other/%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%9E%8B%E8%AE%AD%E7%BB%83.html</link>
      <guid>https://www.linkjm.com/posts/iOS/other/%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%9E%8B%E8%AE%AD%E7%BB%83.html</guid>
      <source url="https://www.linkjm.com/rss.xml">神经网络模型训练</source>
      <description>神经网络模型训练 经过之前 coreML 的介绍，自己训练一个模型 使用 xcodeplayground 训练简单神经网络模型 详细： 去年，苹果(Apple)推出了 Core ML:这是一种快速的方法，可以让你用尽可能少的代码将预先培训好的机器学习模型导入应用程序中!今年，有了 Create ML，苹果给了我们开发人员创建我们自己的机器学习模型直接进...</description>
      <category>iOS</category>
      <pubDate>Tue, 23 Jul 2019 16:08:25 GMT</pubDate>
      <content:encoded><![CDATA[
<blockquote>
<p>经过之前 coreML 的介绍，自己训练一个模型</p>
<p><em>使用 xcodeplayground 训练简单神经网络模型</em></p>
</blockquote>
<h2>详细：</h2>
<p>去年，苹果(Apple)推出了 Core ML:这是一种快速的方法，可以让你用尽可能少的代码将预先培训好的机器学习模型导入应用程序中!今年，有了 Create ML，苹果给了我们开发人员创建我们自己的机器学习模型直接进入 Xcode 的平台的能力!我们只需要一些数据就行了!目前，Create ML 允许文本、图像和表作为数据。然而，由于这是大多数 ML 应用程序的组成部分，这应该很好地服务于您的目的!我将向您展示如何使用这三种类型的数据创建一个 ML 模型.</p>
<figure><img src="https://upload-images.jianshu.io/upload_images/3343369-38993f3fc4b87558.jpeg?imageMogr2/auto-orient/strip|imageView2/2/w/1000/format/webp" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p><strong>注意：本教程构建在 Xcode 10 和 macOS Mojave 之上。请确保您升级了 Xcode 和 macOS，以便遵循本教程.</strong></p>
<p>之前的文章介绍过如何使用 ML，今天主要讲一下使用 xocde 的 playground 来训练一个简单神经网络模型，顺便可以熟悉 swift</p>
<h2>准备工作</h2>
<p>1.本文想做一个图像分类器的模型，区分简单图像</p>
<p>2.打开文件夹时，您将注意到另外两个文件夹:训练数据和测试数据。每个文件夹都有苹果和香蕉的混合图片。有大约 20 张苹果图片和 20 张香蕉图片，分别被称为测试数据和 80 张苹果图片和 80 张香蕉图片。我们将在训练数据中使用图像来训练分类器，然后使用测试数据来确定其准确性。</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/sw1Tn6SBESjcjvMyVvu.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>3.如果您想构建自己的图像分类器，那么将数据集进行 2-8 分割是很重要的。大约 80%的图像用于训练数据，其余部分用于测试数据。这样，您的分类器就有更多的数据需要训练。在每个文件夹中，将图像放在各自的文件夹中。根据图像的类别标签命名这些文件夹。</p>
<p>4.新建 playground，注意这里一定要选 macos 而不是 iOS，因为训练模型需要引入的头文件<strong>CreateMLUI</strong>是 macos 的</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/RaJzQO6Nm9Wjuvw3nCh.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>5.接下来，给你的 playground 的起个名字，把他保存起来。</p>
<p>开始 coding</p>
<h2>开始</h2>
<p>确保在 Xcode playground 中启用 Live View 特性，您将能够看到可视化界面!<br>
需要引入 CreateMLUI，CreateMLUI 是一个与 CreateML 类似的框架，但它有一个 UI。到目前为止，CreateMLUI 只能用于图像分类.<br>
然后在 playground 中输入以下代码</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/FqBcXuSwRDOxpw8UBTF.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>运行之后是这个样子的</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/LxHV4JoUeBdaUGfodqb.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>下面向其中拖入训练数据，它会自动开始训练并显示当前进度</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/gWYNXlrru03F8sMYlIR.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>控制台也会显示一些信息</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/TUgR2XH0hovyEuJv7hP.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>训练完成之后我们可以看到上面有三个标签:培训、验证和评估。培训是指 Xcode 成功培训的培训数据的百分比。这应该是 100%</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/9JrqyfzXxNCjuDxSJMO.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>评估是空的因为我们还没有给 xcode 任何测试数据，xcode 会在剩余的 20%上验证分类器，接着拖入测试数据</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/PjupzGNXjHBhQixucpb.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>当一切都完成后，我们的评估分数应该也是在 100%</p>
<p>如果你对你的结果满意，剩下的就是保存文件了!单击图像分类器标题旁边的箭头。应该出现一个下拉菜单，显示所有的元数据。将元数据更改为您想要的方式，并将其保存到您想要的位置!</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/9XYJjpWXhZSdRjZWLze.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>他长这个样子</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/8cNWX8eidTARekAmU3J.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>接下来可以打开 CoreML 模型并查看元数据。它有你填的所有东西!恭喜你!您是您自己的图像分类器模型的作者，它非常强大，并且只需要 17 KB!</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/gAS7BWestsJDUJChOch.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<h2>文本分类器模型</h2>
<p>接下来，我们将使用 Create ML 构建一个垃圾邮件检测器模型。<br>
废话不多说，上代码</p>
<figure><img src="http://www.demodashi.com/contentImages/image/20190724/Zb3xW53OWSeEt2pw1k7.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<ul>
<li>首先，我们创建一个名为 data 的常量，它是垃圾邮件的一种 MLDataTable。json 文件。MLDataTable 是一个全新的对象，用于创建一个决定训练或评估 ML 模型的表。我们将数据分为 trainingData 和 testingData。和以前一样，比率是 80-20，种子是 5。种子是指分类器的起点。然后我们用我们的训练数据定义一个叫做 spamClassifier 的 MLTextClassifier，定义数据的值是文本，什么值是标签。</li>
<li>创建了两个变量，名为 trainingAccuracy 和 validationAccuracy，用于确定分类器的准确程度。在侧窗格中，您可以看到百分比。</li>
<li>我们还检查评估的执行情况。(请记住，评价是分类器以前没有看到的文本上使用的结果，以及它们的准确性。</li>
<li>最后，我们为 ML 模型创建一些元数据，如作者、描述和版本。我们使用 write()函数将模型保存到我们选择的位置!在下面的图片中，你会看到我选择了桌面!</li>
</ul>
<figure><img src="https://upload-images.jianshu.io/upload_images/3343369-ce98597754508f93.png?imageMogr2/auto-orient/strip|imageView2/2/w/970/format/webp" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<p>运行。您可以在控制台中看到迭代，在右边栏中看到精度! 完成所有操作后，将保存核心 ML 模型!您可以查看模型并查看元数据!</p>
<figure><img src="https://upload-images.jianshu.io/upload_images/3343369-d8deb59d132c958c.png?imageMogr2/auto-orient/strip|imageView2/2/w/1000/format/webp" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
]]></content:encoded>
      <enclosure url="https://upload-images.jianshu.io/upload_images/3343369-38993f3fc4b87558.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1000/format/webp" type="image/"/>
    </item>
    <item>
      <title>AFNetworking A memory leak</title>
      <link>https://www.linkjm.com/posts/iOS/source/AFNETworking-A-memory-leak.html</link>
      <guid>https://www.linkjm.com/posts/iOS/source/AFNETworking-A-memory-leak.html</guid>
      <source url="https://www.linkjm.com/rss.xml">AFNetworking A memory leak</source>
      <description>细心的你是否也发现了 AFN 的内存泄漏的问题了呢. 解决方法 将有问题的语句全部替换成单例后，再用 instruments 检查，再也没有出现泄漏的红叉了。O(∩_∩)O 哈哈~</description>
      <category>iOS</category>
      <pubDate>Thu, 19 Jan 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>细心的你是否也发现了 AFN 的内存泄漏的问题了呢.</p>
</blockquote>
<h3>解决方法</h3>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    + (AFHTTPSessionManager *)sharedHTTPSession{</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    static</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> dispatch_once_t onceToken;</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    dispatch_once</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(&#x26;onceToken, ^{</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        manager </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [AFHTTPSessionManager </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">manager</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        manager</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">requestSerializer</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">timeoutInterval</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 30</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        [manager.requestSerializer  setValue:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"XMLHttpRequest"</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> forHTTPHeaderField:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"X-Requested-With"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> manager;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    + (AFURLSessionManager *)sharedURLSession{</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    static</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> dispatch_once_t onceToken2;</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    dispatch_once</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(&#x26;onceToken2, ^{</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        urlsession </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [[AFURLSessionManager </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">alloc</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">initWithSessionConfiguration:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[NSURLSessionConfiguration </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">defaultSessionConfiguration</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">]];</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> urlsession;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>将有问题的语句全部替换成单例后，再用 instruments 检查，再也没有出现泄漏的红叉了。O(∩_∩)O 哈哈~</p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>WCDB漫谈</title>
      <link>https://www.linkjm.com/posts/iOS/source/WCDB.html</link>
      <guid>https://www.linkjm.com/posts/iOS/source/WCDB.html</guid>
      <source url="https://www.linkjm.com/rss.xml">WCDB漫谈</source>
      <description>前言 移动端的数据库选型一直是一个难题，直到前段时间看到了 WeMobileDev(微信前端团队)放出了第三个开源组件-WCDB WCDB(WeChat DataBase)是微信官方的移动端数据库组件，致力于提供一个高效、易用、完整的移动端存储方案 微信团队怎么说 基于 SQLCipher WCDB-iOS/Mac WCDB-Android 数据库损坏...</description>
      <category>iOS</category>
      <pubDate>Thu, 18 Jan 2018 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>前言</p>
</blockquote>
<blockquote>
<p>移动端的数据库选型一直是一个难题，直到前段时间看到了 WeMobileDev(微信前端团队)放出了第三个开源组件-WCDB</p>
</blockquote>
<blockquote>
<p>WCDB(WeChat DataBase)是微信官方的移动端数据库组件，致力于提供一个高效、易用、完整的移动端存储方案</p>
</blockquote>
<h2>微信团队怎么说</h2>
<ul>
<li>
<p>基于 SQLCipher</p>
</li>
<li>
<p>WCDB-iOS/Mac</p>
</li>
<li>
<p>WCDB-Android</p>
</li>
<li>
<p>数据库损坏修复工具 WDBRepair</p>
</li>
</ul>
<h3>背景</h3>
<p>WCDB 的出现可以说解决了目前移动端数据库的几个难点</p>
<ul>
<li>
<p>首先在选型上，<strong>FMDB</strong>的 SQL 拼接、难以防止的 SQL 注入；<strong>CoreData</strong>虽然可以方便 ORM，但学习成本高，稳定性堪忧，而且多线程鸡肋；另外基于 C 语言的<strong>sqlite</strong>我想用的人也应该不多；除了上述关系型数据库之外然后还有一些其他的 Key-Value 型数据库，如我用过的 Realm，对于 ObjC 开发者来说，上手倒是没什么难度，但缺点显而易见，需要继承，入侵性强，对于单继承的 OC 来说这并不理想，而且对于集合类型不完全支持，复杂查询也比较无力。</p>
</li>
<li>
<p><strong>高效</strong></p>
<ul>
<li>
<p>多线程高并发：WCDB 支持多线程读与读、读与写并发执行，写与写串行执行。</p>
</li>
<li>
<p>批量写操作性能测试：</p>
<p>| 批量写 | ops/sec |<br>
|</p>
</li>
</ul>
</li>
</ul>
]]></content:encoded>
      <enclosure url="https://s2.ax1x.com/2019/11/15/Ma6EBF.png" type="image/png"/>
    </item>
    <item>
      <title>YYCache优秀的缓存设计</title>
      <link>https://www.linkjm.com/posts/iOS/source/YYMemoryCache.html</link>
      <guid>https://www.linkjm.com/posts/iOS/source/YYMemoryCache.html</guid>
      <source url="https://www.linkjm.com/rss.xml">YYCache优秀的缓存设计</source>
      <description>1.1. 前言 开发中总会用到各种缓存，但是各位有没有考虑过什么样的缓存才能被叫做优秀的缓存，或者说优秀的缓存应该具备哪些特质？ 本文将结合 YYCache 的源码逐步带大家找到答案。 YYCache 是一个线程安全的高性能键值缓存（该项目是 YYKit 组件之一） YYCache 的代码逻辑清晰，注释详尽，加上自身不算太大的代码量使得其阅读非常简单，...</description>
      <category>iOS</category>
      <pubDate>Fri, 26 Apr 2019 16:08:25 GMT</pubDate>
      <content:encoded><![CDATA[<h2>1.1. 前言</h2>
<p>开发中总会用到各种缓存，但是各位有没有考虑过什么样的缓存才能被叫做优秀的缓存，或者说优秀的缓存应该具备哪些特质？<br>
本文将结合 YYCache 的源码逐步带大家找到答案。<br>
YYCache 是一个线程安全的高性能键值缓存（该项目是 YYKit 组件之一）<br>
YYCache 的代码逻辑清晰，注释详尽，加上自身不算太大的代码量使得其阅读非常简单，更加难能可贵的是它的性能还非常高。</p>
<!-- more -->
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>YYCache 是由 YYMemoryCache 与 YYDiskCache 两部分组成的，其中 YYMemoryCache 作为高速内存缓存，而 YYDiskCache 则作为低速磁盘缓存。<br>
YYMemoryCache 是内存缓存，所以存取速度非常快，主要用到两种数据结构的 LRU 淘汰算法</p>
</div>
<p>NSCache 是苹果提供的一个简单的内存缓存，它有着和 NSDictionary 类似的 API，不同点是它是线程安全的，并且不会 retain key。我在测试时发现了它的几个特点：NSCache 底层并没有用 NSDictionary 等已有的类，而是直接调用了 libcache.dylib，其中线程安全是由 pthread_mutex 完成的。另外，它的性能和 key 的相似度有关，如果有大量相似的 key (比如 “1”, “2”, “3”, …)，NSCache 的存取性能会下降得非常厉害，大量的时间被消耗在 CFStringEqual() 上，不知这是不是 NSCache 本身设计的缺陷。</p>
<h2>1.2. 介绍</h2>
<p>YYMemoryCache 是一个高速的内存缓存，用于存储键值对。它与 NSDictionary 相反，Key 被保留并且不复制。API 和性能类似于 NSCache，所有方法都是线程安全的。</p>
<p>YYMemoryCache 对象与 NSCache 的不同之处在于：</p>
<p>YYMemoryCache 使用 LRU(least-recently-used) 算法来驱逐对象；NSCache 的驱逐方式是非确定性的。<br>
YYMemoryCache 提供 age、cost、count 三种方式控制缓存；NSCache 的控制方式是不精确的。<br>
YYMemoryCache 可以配置为在收到内存警告或者 App 进入后台时自动逐出对象。</p>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@interface</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> YYMemoryCache</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSObject</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">#pragma mark</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> - Attribute</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">copy</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *name; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 缓存名称，默认为 nil</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">readonly</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) NSUInteger totalCount; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 缓存对象总数</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">readonly</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) NSUInteger totalCost; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 缓存对象总开销</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">#pragma mark</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> - Limit</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSUInteger countLimit; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 缓存对象数量限制，默认无限制，超过限制则会在后台逐出一些对象以满足限制</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSUInteger costLimit; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 缓存开销数量限制，默认无限制，超过限制则会在后台逐出一些对象以满足限制</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSTimeInterval ageLimit; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 缓存时间限制，默认无限制，超过限制则会在后台逐出一些对象以满足限制</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSTimeInterval autoTrimInterval; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 缓存自动清理时间间隔，默认 5s</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> BOOL</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> shouldRemoveAllObjectsOnMemoryWarning; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 是否应该在收到内存警告时删除所有缓存内对象</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> BOOL</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> shouldRemoveAllObjectsWhenEnteringBackground; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 是否应该在 App 进入后台时删除所有缓存内对象</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">copy</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(^didReceiveMemoryWarningBlock)(YYMemoryCache *cache); </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 我认为这是一个 hook，便于我们在收到内存警告时自定义处理缓存</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">copy</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(^didEnterBackgroundBlock)(YYMemoryCache *cache); </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 我认为这是一个 hook，便于我们在收到 App 进入后台时自定义处理缓存</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> BOOL</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> releaseOnMainThread; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 是否在主线程释放对象，默认 NO，有些对象（例如 UIView/CALayer）应该在主线程释放</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@property</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> BOOL</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> releaseAsynchronously; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 是否异步释放对象，默认 YES</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">BOOL</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">containsObjectForKey:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">objectForKey:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">setObject:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">object</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> forKey:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">setObject:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">object</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> forKey:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">key</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> withCost:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(NSUInteger)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">cost</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">removeObjectForKey:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">removeAllObjects</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">#pragma mark</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75"> - Trim</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">trimToCount:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(NSUInteger)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">count</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 用 LRU 算法删除对象，直到 totalCount &#x3C;= count</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">trimToCost:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(NSUInteger)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">cost</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 用 LRU 算法删除对象，直到 totalCost &#x3C;= cost</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">trimToAge:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(NSTimeInterval)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">age</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 用 LRU 算法删除对象，直到所有到期对象全部被删除</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@end</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>1.3. YYMemoryCache 是如何做到线程安全的</h2>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@implementation</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> YYMemoryCache</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    pthread_mutex_t</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _lock; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 线程锁，旨在保证 YYMemoryCache 线程安全</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _YYLinkedMap *_lru; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// _YYLinkedMap，YYMemoryCache 通过它间接操作缓存对象</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    dispatch_queue_t _queue; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 串行队列，用于 YYMemoryCache 的 trim 操作</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>在最初 YYMemoryCache 这里使用的锁是 OSSpinLock 自旋锁（详见 <a href="https://blog.ibireme.com/2015/10/26/yycache/" target="_blank" rel="noopener noreferrer">YYCache 设计思路</a> 备注-关于锁），后面有人在 Github 向作者提 issue 反馈 OSSpinLock 不安全，经过作者的确认（详见 不再安全的 OSSpinLock）最后选择用 pthread_mutex 替代 OSSpinLock。</p>
</div>
<h2>1.4. LRU 淘汰算法</h2>
<p>LRU（Least recently used，最近最少使用）算法根据数据的历史访问记录来进行淘汰数据，其核心思想是“如果数据最近被访问过，那么将来被访问的几率也更高”。</p>
<p>最常见的实现是使用一个链表保存缓存数据</p>
<p>【命中率】</p>
<p>当存在热点数据时，LRU 的效率很好，但偶发性的、周期性的批量操作会导致 LRU 命中率急剧下降，缓存污染情况比较严重。</p>
<p>Cache 的容量是有限的，当 Cache 的空间都被占满后，如果再次发生缓存失效，就必须选择一个缓存块来替换掉。LRU 法是依据各块使用的情况， 总是选择那个最长时间未被使用的块替换。这种方法比较好地反映了程序局部性规律</p>
<h2>1.5. 数据结构</h2>
<ul>
<li>双向链表 (Doubly Linked List) <code>_YYLinkedMap</code></li>
<li>哈希表 (Dictionary) <code>CFMutableDictionaryRef _dic</code></li>
</ul>
<h2>1.6. 缓存操作</h2>
<ul>
<li>新数据插入到链表头部；</li>
<li>每当缓存命中（即缓存数据被访问），则将数据移到链表头部；</li>
<li>当链表满的时候，将链表尾部的数据丢弃。</li>
</ul>
<h2>1.7. 分析图</h2>
<figure><img src="https://s3.bmp.ovh/imgs/2024/01/20/c6e7391286dce045.webp" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<h2>1.8. YYMemoryCache.m 里的两个分类</h2>
<ol>
<li>
<p>链表节点 <code>_YYLinkedMapNode</code></p>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@interface</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> _YYLinkedMapNode</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSObject</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    @package</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 指向前一个节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    __unsafe_unretained _YYLinkedMapNode *_prev; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// retained by dic</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 指向后一个节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    __unsafe_unretained _YYLinkedMapNode *_next; </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// retained by dic</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 缓存key</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _key;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 缓存对象</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    id</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _value;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 当前缓存内存开销</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    NSUInteger _cost;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 缓存时间</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    NSTimeInterval _time;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@end</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></li>
<li>
<p>链表 <code>_YYLinkedMap</code></p>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@interface</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> _YYLinkedMap</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSObject</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    @package</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 用字典保存所有节点_YYLinkedMapNode (为什么不用oc字典?因为用CFMutableDictionaryRef效率高，毕竟基于c)</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    CFMutableDictionaryRef _dic;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 总缓存开销</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    NSUInteger _totalCost;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 总缓存数量</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    NSUInteger _totalCount;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 链表头节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _YYLinkedMapNode *_head;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 链表尾节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _YYLinkedMapNode *_tail;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 是否在主线程上，异步释放 _YYLinkedMapNode对象</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    BOOL</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _releaseOnMainThread;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 是否异步释放 _YYLinkedMapNode对象</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    BOOL</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _releaseAsynchronously;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 添加节点到链表头节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">insertNodeAtHead:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(_YYLinkedMapNode *)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 移动当前节点到链表头节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">bringNodeToHead:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(_YYLinkedMapNode *)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 移除链表节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">removeNode:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(_YYLinkedMapNode *)</span><span style="--shiki-light:#ABB2BF;--shiki-light-font-style:italic;--shiki-dark:#ABB2BF;--shiki-dark-font-style:italic">node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 移除链表尾节点(如果存在)</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (_YYLinkedMapNode *)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">removeTailNode</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 移除所有缓存</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">removeAll</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">@end</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></li>
</ol>
<h2>1.9. 链表插入、查找、替换操作实现</h2>
<ul>
<li>
<p>添加节点到链表头节点</p>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 添加节点到链表头节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)insertNodeAtHead:(_YYLinkedMapNode *)node {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 哈希表保存链表节点node</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    CFDictionarySetValue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(_dic, (__bridge </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *)(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">), (__bridge </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *)(node));</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 叠加该缓存开销到总内存开销</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCost </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">+=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_cost</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 总缓存数+1</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCount</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">++</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (_head) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 存在链表头，取代当前表头</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_next</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _head;</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        _head</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> node;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 重新赋值链表表头临时变量_head</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        _head </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> node;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    } </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 不存在链表头</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        _head </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _tail </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> node;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><figure><img src="https://upload-images.jianshu.io/upload_images/295346-1cb03d629ecbf2fa.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
</li>
<li>
<p>移动当前节点到链表头节点</p>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 移动当前节点到链表头节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)bringNodeToHead:(_YYLinkedMapNode *)node {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 当前节点已是链表头节点</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (_head </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> node) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (_tail </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> node) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        //**如果node是链表尾节点**</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 把node指向的上一个节点赋值给链表尾节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        _tail </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 把链表尾节点指向的下一个节点赋值nil</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        _tail</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_next</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    } </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        //**如果node是非链表尾节点和链表头节点**</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 此处比较难以理解：总结如下</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 链接当前节点上节点(node->_prev)到当前节点下节点（node->_next）的上索引(->_prev)</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">_next</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 链接当前节点下节点(node->_next)到当前节点上节点（node->_prev）的下索引(->_next)</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">_prev</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_next</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_next</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 此处操作等于将本节点上下索引分别赋值给右左节点上下索引，将上下节点链接</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 把链表头节点赋值给node指向的下一个节点</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_next</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _head;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 把node指向的上一个节点赋值nil</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 把节点赋值给链表头节点的指向的上一个节点</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    _head</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> node;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _head </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> node;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><figure><img src="https://upload-images.jianshu.io/upload_images/295346-682e8396c2d9e092.png" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
</li>
<li>
<p>移除节点</p>
<ul>
<li>
<p>移除指定节点</p>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 移除节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)removeNode:(_YYLinkedMapNode *)node {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 从字典中移除node</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    CFDictionaryRemoveValue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(_dic, (__bridge </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *)(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">));</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 减掉总内存消耗</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCost </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">-=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_cost</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // // 总缓存数-1</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCount</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">--</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 重新连接链表(看图分析吧)</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_next</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">_next</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">_prev</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_next</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_next</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (_head </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> node) _head </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_next</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (_tail </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> node) _tail </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div></li>
<li>
<p>移除尾节点</p>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 移除尾节点(如果存在)</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (_YYLinkedMapNode *)removeTailNode {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">_tail) </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">return</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 拷贝一份要删除的尾节点指针</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _YYLinkedMapNode *tail </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _tail;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 移除链表尾节点</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    CFDictionaryRemoveValue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(_dic, (__bridge </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *)(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">_tail</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_key</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">));</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 减掉总内存消耗</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCost </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">-=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> _tail</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_cost</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 总缓存数-1</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCount</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">--</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (_head </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _tail) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 清除节点，链表上已无节点了</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        _head </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _tail </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    } </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 设倒数第二个节点为链表尾节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        _tail </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> _tail</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">        _tail</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">-></span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">_next</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 返回完tail后_tail将会释放</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> tail;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></li>
<li>
<p>移除所有缓存</p>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 移除所有缓存</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)removeAll {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 清空内存开销与缓存数量</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCost </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCount </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 清空头尾节点</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _head </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    _tail </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">CFDictionaryGetCount</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(_dic) </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">></span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 拷贝一份字典</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        CFMutableDictionaryRef holder </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _dic;</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 重新分配新的空间</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        _dic </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> CFDictionaryCreateMutable</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">CFAllocatorGetDefault</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(), </span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, &#x26;</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">kCFTypeDictionaryKeyCallBacks</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, &#x26;</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">kCFTypeDictionaryValueCallBacks</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">        if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (_releaseAsynchronously) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">            // 异步释放缓存</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">            dispatch_queue_t queue </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> _releaseOnMainThread </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">?</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> dispatch_get_main_queue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">:</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> YYMemoryCacheGetReleaseQueue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">            dispatch_async</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(queue, ^{</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">                CFRelease</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(holder); </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// hold and release in specified queue</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">            });</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        } </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (_releaseOnMainThread </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">&#x26;&#x26;</span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2"> !</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">pthread_main_np</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">()) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">            // 主线程上释放缓存</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">            dispatch_async</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">dispatch_get_main_queue</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(), ^{</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">                CFRelease</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(holder); </span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// hold and release in specified queue</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">            });</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        } </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">            // 同步释放缓存</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">            CFRelease</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(holder);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></li>
</ul>
</li>
</ul>
]]></content:encoded>
      <enclosure url="https://s3.bmp.ovh/imgs/2024/01/20/c6e7391286dce045.webp" type="image/webp"/>
    </item>
    <item>
      <title>深入理解swift中闭包的捕捉语义</title>
      <link>https://www.linkjm.com/posts/iOS/swift/swift%E6%8D%95%E8%8E%B7%E8%AF%AD%E4%B9%89.html</link>
      <guid>https://www.linkjm.com/posts/iOS/swift/swift%E6%8D%95%E8%8E%B7%E8%AF%AD%E4%B9%89.html</guid>
      <source url="https://www.linkjm.com/rss.xml">深入理解swift中闭包的捕捉语义</source>
      <description>参考链接：losures Capture Semantics, Part 1: Catch them all! 概述 原文中先定义一个 Pokemon 类 延时执行的闭包 默认捕捉语法 在 swift 中默认的捕捉语法是:被捕获的变量在闭包被执行的时候才被定值1 . 我们能说它捕获到了这个变量的引用(或者 指针)。 这个闭包会在 demo1() 方法函...</description>
      <category>iOS</category>
      <pubDate>Thu, 19 Oct 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>参考链接：<a href="https://link.juejin.im/?target=https%3A%2F%2Fgold.xitu.io%2Fentry%2F5796308479bc440066443c8e" target="_blank" rel="noopener noreferrer">losures Capture Semantics, Part 1: Catch them all!</a></p>
</blockquote>
<h2>概述</h2>
<p>原文中先定义一个 Pokemon 类</p>
<div class="language-swift" data-highlighter="shiki" data-ext="swift" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-swift"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">class</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B"> Pokemon</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">CustomDebugStringConvertible </span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> name: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">String</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  init</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-light-font-style:italic;--shiki-dark:#61AFEF;--shiki-dark-font-style:italic">name</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    self</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">name</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> = name</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  var</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> debugDescription: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">return</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> "</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">\(</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">name</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">)</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">>"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  deinit</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">\(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">)</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> escaped!"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>延时执行的闭包</p>
<div class="language-swift" data-highlighter="shiki" data-ext="swift" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-swift"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">func</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> delay</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-light-font-style:italic;--shiki-dark:#61AFEF;--shiki-dark-font-style:italic">seconds</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">Int</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#61AFEF;--shiki-light-font-style:italic;--shiki-dark:#61AFEF;--shiki-dark-font-style:italic">closure</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: ()->()) {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> time = DispatchTime.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">now</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() + .</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">seconds</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(seconds)</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  DispatchQueue.</span><span style="--shiki-light:#E06C75;--shiki-dark:#E06C75">main</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">after</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">when</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: time) {</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">    print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"🕑"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">    closure</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><h2>默认捕捉语法</h2>
<p>在 swift 中默认的捕捉语法是:<strong>被捕获的变量在闭包被执行的时候才被定值</strong><a href="https://link.juejin.im/?target=http%3A%2F%2Falisoftware.github.io%2Fswift%2Fclosures%2F2016%2F07%2F25%2Fclosure-capture-1%2F%23fn%3Ablock-modifier" target="_blank" rel="noopener noreferrer">1</a><br>
. 我们能说它捕获到了这个变量的<em>引用</em>(或者  <em>指针</em>)。</p>
<div class="language-swift" data-highlighter="shiki" data-ext="swift" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-swift"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">func</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> demo1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">  let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> pokemon = </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">Pokemon</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">name</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"Mewtwo"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">  print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"before closure: </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">\(</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">pokemon</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">)</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">  delay</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">    print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"inside closure: </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">\(</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">pokemon</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">)</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">  print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"bye"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>这个闭包会在 demo1() 方法函数执行完成后 1 秒后被执行，并且我们已退出了方法函数的作用域… 当然 Pokemon 仍然是存在的，当这个代码块在下一个 1 秒后再次被执行的时候！</p>
<div class="language-swift" data-highlighter="shiki" data-ext="swift" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-swift"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">before closure</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> &#x3C;Pokemon Mewtwo></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">bye</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">🕑</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">inside closure</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> &#x3C;Pokemon Mewtwo></span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">&#x3C;Pokemon Mewtwo> escaped!</span></span></code></pre>
</div><p>在这个例子中，当这个闭包被 GCD 执行后，这个闭包自行释放，就是 Pokemon 内部的 init 方法执行的时候。<br>
此处 Swift 自动捕获到这个 pokemon 变量，当 demo1 方法执行完成并且释放掉的时候，1s 之后闭包被执行的时候还是捕获到了 pokemon</p>
<h2>被捕获到的变量都被执行的时候定值</h2>
<div class="language-swift" data-highlighter="shiki" data-ext="swift" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-swift"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> name = </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"John"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> test1 = {</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">    print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(name)</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">name = </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"Aby"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">test1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span></code></pre>
</div><p>此处在创建了 test1 闭包之后，我们改变了 name 的值，打印结果是 Aby。说明此处 Swift 捕获到的是 name 这个变量的引用(指针)</p>
<p>原文中的例子使用了方法，并做了延时，大意在说明，函数被释放之后的延时闭包中，闭包依然捕获了变量的新值，而不是旧值，说明此处它捕获的是变量的引用，而非变量本身的值</p>
<p>这个在值类型中也可行例如 Int</p>
<h2>修改变量的值</h2>
<p>如果捕获的值是一个  var 并不是一个  let，你还是可以修改这个值  <strong>在闭包内部</strong><a href="https://link.juejin.im/?target=http%3A%2F%2Falisoftware.github.io%2Fswift%2Fclosures%2F2016%2F07%2F25%2Fclosure-capture-1%2F%23fn%3Aobjc_block_modify" target="_blank" rel="noopener noreferrer">2</a></p>
<div class="language-swift" data-highlighter="shiki" data-ext="swift" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-swift"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> name = </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"John"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> test1 = {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    name = </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"Jack"</span></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">    print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(name)</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">name = </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"Aby"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">test1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span></code></pre>
</div><p>此处打印结果为 Jack，再一次印证，闭包内捕获的是变量 name 的引用，因为它可以被改变，而不是一个静态的拷贝</p>
<h2>捕获一个作为静态 copy 的变量</h2>
<div class="language-swift" data-highlighter="shiki" data-ext="swift" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-swift"><span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> str = </span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"Hello, playground"</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">let</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> show1 = { [strcopy = str] </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">in</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 在闭包一开始创建的时候捕获变量的值，</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// []内的为捕获列表，一开始捕获值，而非引用。</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// 捕获的为原始变量的副本->常量，并且只能在闭包内访问</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">    print</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"这是str</span></span></code></pre>
</div>]]></content:encoded>
    </item>
    <item>
      <title>NSError</title>
      <link>https://www.linkjm.com/posts/iOS/system/NSError.html</link>
      <guid>https://www.linkjm.com/posts/iOS/system/NSError.html</guid>
      <source url="https://www.linkjm.com/rss.xml">NSError</source>
      <description>前言 整理一下在iOS开发中NSError的错误代码 NSError NSError是系统错误信息类 初始化方法两个 属性 @property (readonly) NSInteger code;//错误代码 @property (readonly, copy) NSDictionary *userInfo;//错误信息 @property (read...</description>
      <category>iOS</category>
      <pubDate>Thu, 31 May 2018 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>前言</p>
<p>整理一下在iOS开发中NSError的错误代码</p>
</blockquote>
<h2>NSError</h2>
<p>NSError是系统错误信息类</p>
<p>初始化方法两个</p>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // domain 不能为空 dict可以为空</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  </span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  - (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">instancetype</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)initWithDomain:(NSErrorDomain)domain code:(NSInteger)code userInfo:(nullable </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSDictionary</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *)dict;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">  + (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">instancetype</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)errorWithDomain:(NSErrorDomain)domain code:(NSInteger)code userInfo:(nullable </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSDictionary</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *)dict;</span></span></code></pre>
</div><h3>属性</h3>
<ul>
<li>
<p>@property (readonly) NSInteger code;//错误代码</p>
</li>
<li>
<p>@property (readonly, copy) NSDictionary *userInfo;//错误信息</p>
</li>
<li>
<p>@property (readonly, copy) NSString *localizedDescription;//获取本地化描述</p>
</li>
<li>
<p>@property (nullable, readonly, copy) NSString *localizedFailureReason;//获取失败原因</p>
</li>
<li>
<p>@property (nullable, readonly, copy) NSString *localizedRecoverySuggestion;//获取恢复建议</p>
</li>
<li>
<p>@property (nullable, readonly, copy) NSArray&lt;NSString *&gt; *localizedRecoveryOptions;本地恢复建议</p>
</li>
<li>
<p>@property (nullable, readonly, strong) id recoveryAttempter;</p>
</li>
<li>
<p>@property (nullable, readonly, copy) NSString *helpAnchor;</p>
</li>
</ul>
<h3>NSError错误code对照表</h3>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSError</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> codes </span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">in</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> the Cocoa error domain.</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">enum</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileNoSuchFileError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 4</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileLockingError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 255</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadUnknownError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 256</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadNoPermissionError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 257</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadInvalidFileNameError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 258</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadCorruptFileError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 259</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadNoSuchFileError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 260</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadInapplicableStringEncodingError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 261</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadUnsupportedSchemeError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 262</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadTooLargeError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 263</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadUnknownStringEncodingError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 264</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteUnknownError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 512</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteNoPermissionError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 513</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteInvalidFileNameError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 514</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteInapplicableStringEncodingError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 517</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteUnsupportedSchemeError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 518</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteOutOfSpaceError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 640</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteVolumeReadOnlyError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#FFFFFF;--shiki-dark:#FFFFFF"> 642m</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSKeyValueValidationError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 1024</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFormattingError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 2048</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSUserCancelledError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3072</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">NSFileErrorMinimum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileErrorMaximum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 1023</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSValidationErrorMinimum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 1024</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSValidationErrorMaximum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 2047</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFormattingErrorMinimum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 2048</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSFormattingErrorMaximum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 2559</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">NSPropertyListReadCorruptError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3840</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSPropertyListReadUnknownVersionError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3841</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSPropertyListReadStreamError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3842</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSPropertyListWriteStreamError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3851</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSPropertyListErrorMinimum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3840</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSPropertyListErrorMaximum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 4095</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">NSExecutableErrorMinimum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3584</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableNotLoadableError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3584</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableArchitectureMismatchError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3585</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableRuntimeMismatchError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3586</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableLoadError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3587</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableLinkError </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3588</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableErrorMaximum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66"> 3839</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> URL Loading System Error Codes</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">These values are returned as the error code property of an </span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSError</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> object with the domain “NSURLErrorDomain”.</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">typedef</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> enum</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorUnknown </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCancelled </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">999</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorBadURL </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1000</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorTimedOut </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1001</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorUnsupportedURL </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1002</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotFindHost </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1003</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotConnectToHost </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1004</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorDataLengthExceedsMaximum </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1103</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorNetworkConnectionLost </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1005</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorDNSLookupFailed </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1006</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorHTTPTooManyRedirects </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1007</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorResourceUnavailable </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1008</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorNotConnectedToInternet </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1009</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorRedirectToNonExistentLocation </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1010</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorBadServerResponse </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1011</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorUserCancelledAuthentication </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1012</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorUserAuthenticationRequired </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1013</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorZeroByteResource </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1014</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotDecodeRawData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1015</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotDecodeContentData </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1016</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotParseResponse </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1017</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorFileDoesNotExist </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1100</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorFileIsDirectory </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1101</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorNoPermissionsToReadFile </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1102</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorSecureConnectionFailed </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1200</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorServerCertificateHasBadDate </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1201</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorServerCertificateUntrusted </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1202</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorServerCertificateHasUnknownRoot </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1203</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorServerCertificateNotYetValid </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1204</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorClientCertificateRejected </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1205</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorClientCertificateRequired </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1206</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotLoadFromNetwork </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2000</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotCreateFile </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3000</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotOpenFile </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3001</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotCloseFile </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3002</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotWriteToFile </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3003</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotRemoveFile </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3004</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotMoveFile </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3005</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorDownloadDecodingFailedMidStream </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3006</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorDownloadDecodingFailedToComplete </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3007</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>有关网络请求失败的解释</h3>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorUnknown</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无效的URL地址"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">999</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCancelled</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无效的URL地址"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1000</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorBadURL</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无效的URL地址"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1001</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorTimedOut</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"网络不给力，请稍后再试"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1002</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorUnsupportedURL</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"不支持的URL地址"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1003</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotFindHost</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"找不到服务器"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1004</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotConnectToHost</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"连接不上服务器"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1103</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorDataLengthExceedsMaximum</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"请求数据长度超出最大限度"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1005</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorNetworkConnectionLost</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"网络连接异常"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1006</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorDNSLookupFailed</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"DNS查询失败"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1007</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorHTTPTooManyRedirects</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"HTTP请求重定向"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1008</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorResourceUnavailable</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"资源不可用"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1009</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorNotConnectedToInternet</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无网络连接"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1010</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorRedirectToNonExistentLocation</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"重定向到不存在的位置"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1011</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorBadServerResponse</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"服务器响应异常"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1012</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorUserCancelledAuthentication</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"用户取消授权"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1013</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorUserAuthenticationRequired</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"需要用户授权"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1014</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorZeroByteResource</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"零字节资源"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1015</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotDecodeRawData</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无法解码原始数据"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1016</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotDecodeContentData</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无法解码内容数据"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1017</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotParseResponse</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无法解析响应"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1018</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorInternationalRoamingOff</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"国际漫游关闭"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1019</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCallIsActive</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"被叫激活"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1020</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorDataNotAllowed</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"数据不被允许"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1021</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorRequestBodyStreamExhausted</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"请求体"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1100</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorFileDoesNotExist</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"文件不存在"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1101</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorFileIsDirectory</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"文件是个目录"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1102</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorNoPermissionsToReadFile</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无读取文件权限"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1200</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorSecureConnectionFailed</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"安全连接失败"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1201</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorServerCertificateHasBadDate</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"服务器证书失效"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1202</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorServerCertificateUntrusted</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"不被信任的服务器证书"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1203</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorServerCertificateHasUnknownRoot</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"未知Root的服务器证书"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1204</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorServerCertificateNotYetValid</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"服务器证书未生效"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1205</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorClientCertificateRejected</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"客户端证书被拒"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">1206</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorClientCertificateRequired</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"需要客户端证书"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">2000</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotLoadFromNetwork</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无法从网络获取"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3000</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotCreateFile</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无法创建文件"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3001</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// NSURLErrorCannotOpenFile</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无法打开文件"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3002</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotCloseFile</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无法关闭文件"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3003</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotWriteToFile</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无法写入文件"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3004</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotRemoveFile</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无法删除文件"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3005</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorCannotMoveFile</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"无法移动文件"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3006</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorDownloadDecodingFailedMidStream</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"下载解码数据失败"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">3007</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">//NSURLErrorDownloadDecodingFailedToComplete</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @"下载解码数据失败"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">break</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div>]]></content:encoded>
    </item>
    <item>
      <title>NSOperatioin</title>
      <link>https://www.linkjm.com/posts/iOS/system/NSOperation%E5%92%8CNSOPerationQueue.html</link>
      <guid>https://www.linkjm.com/posts/iOS/system/NSOperation%E5%92%8CNSOPerationQueue.html</guid>
      <source url="https://www.linkjm.com/rss.xml">NSOperatioin</source>
      <description>一. NSOperatioin 1.目的 开启线程 提供一些 GCD 不具备的功能 OC 框架,内部封装的是 GCD 2.区别 GCD 执行效率高于 NSOperation NSOperation 提供了一些 GCD 中不具备的功能(暂停/恢复/取消)---管理操作-NSOperation 的高级用法 3.NSOperation 本身是一个抽象类不可以...</description>
      <category>iOS</category>
      <pubDate>Mon, 26 Dec 2016 13:39:59 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<h1>一. NSOperatioin</h1>
</blockquote>
<h4>1.目的</h4>
<ul>
<li>开启线程</li>
<li>提供一些 GCD 不具备的功能</li>
<li>OC 框架,内部封装的是 GCD</li>
</ul>
<h4>2.区别</h4>
<ul>
<li>GCD 执行效率高于 NSOperation</li>
<li>NSOperation 提供了一些 GCD 中不具备的功能(暂停/恢复/取消)</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>减小iOS-App或者静态库体积</title>
      <link>https://www.linkjm.com/posts/iOS/system/Reduced-App-%20volume.html</link>
      <guid>https://www.linkjm.com/posts/iOS/system/Reduced-App-%20volume.html</guid>
      <source url="https://www.linkjm.com/rss.xml">减小iOS-App或者静态库体积</source>
      <description>把打包好的.ipa 文件的后缀改为.zip 并解压。右键.appbundle 选择显示包内容。有些情况下，大一点的文件压缩后反而比小一点的文件压缩后的体积小，而我们真正关心的时候解压后的真实体积，所以一定要解压里面的资源文件，看解压后的 size。从 APP Store 下载的.ipa 文件要比自己本地打包的要大，因为 APP Store 对 ipa ...</description>
      <category>iOS</category>
      <pubDate>Fri, 04 Nov 2016 18:11:56 GMT</pubDate>
      <content:encoded><![CDATA[<figure><img src="https://zero-space.s3.amazonaws.com/photos/2e6cc210-e9a6-4a0f-9c05-2c4056a982acx840.jpg" alt tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<blockquote>
<p>把打包好的.ipa 文件的后缀改为.zip 并解压。右键.appbundle 选择显示包内容。有些情况下，大一点的文件压缩后反而比小一点的文件压缩后的体积小，而我们真正关心的时候解压后的真实体积，所以一定要解压里面的资源文件，看解压后的 size。从 APP Store 下载的.ipa 文件要比自己本地打包的要大，因为 APP Store 对 ipa 包又做了加密处理。Xcode 的 Organizer window 的 Estimate Size 功能能估计本地打包文件从 APP Store 下载时的大小。根据优化的 28 定律和常识，首先当然是多媒体资源的体积啦。</p>
</blockquote>
<h2>图片</h2>
<p>压缩图片 不重要的图片可适当采用 8bit PNG 图片 1.什么是矢量图 矢量图是由计算机的算法产生的，可以无限放大或缩小，不会有任何损失，通常由矢量软件制作。 2.什么是位图 位图是由一个一个的小色块组成，放大后会看到那些小色块，同一面积内小色块越多，分辨率就越高。 3.矢量图的优缺点 可以无限放大或缩小，不会影响图像素质，文件体积较小，编辑灵活。缺点是表达的色彩层次不清，整体观感效果不如位图 4.位图的优缺点 不能放太大，减少文件分辨率后会影响图片质量，图片战胜空间较大，优点是能很细腻地表达图片的效果，图片表达效果非常好 5.什么情况下用位图，什么情况下用矢量图 一些对图片要求高的用位图，例如照片。其他的尽量用矢量图。例如文字、表格、卡通图片等</p>
<ul>
<li>
<p>去掉无用的图片</p>
</li>
<li>
<p>用代码绘制简单的纯色图片  <a href="http://www.jianshu.com/p/d01110c80495" target="_blank" rel="noopener noreferrer">用 Sketch 和 PaintCode 快速得到绘制代码</a></p>
</li>
<li>
<p>如果不需要使用透明，可以用 jpeg 代替 PNG。jpeg 减少了些效率但更加小。需权衡性能，大小。</p>
</li>
<li>
<p>对 32 位的图片，尽肯能的使用高压缩率，使用 PS 的“Save For Web”功能，可以有效的减小 JPEG 和 PNG 图片的尺寸。 默认情况下，在 build 时，PNG 图像就被<a href="https://developer.apple.com/library/ios/qa/qa1681/_index.html" target="_blank" rel="noopener noreferrer">pngcrush</a>压缩。</p>
</li>
</ul>
<h2>音频</h2>
<ul>
<li>压缩音频，<a href="https://developer.apple.com/videos/wwdc/2011/?id=404" target="_blank" rel="noopener noreferrer">尽可能使用 AAC 或者 MP3 格式，并且使用一个较低的码率。通常 44.1khz 的码率有点浪费，降低一定的码率也不会丢失多少音质</a></li>
</ul>
<h2>视频</h2>
<ul>
<li>视频也可以使用类似于音频的处理方法，音视频的压缩可以很大程度的压缩，但是要注意压缩的格式，是不是会增加编解码的负担，这要权衡考虑。</li>
</ul>
<h2>Assets</h2>
<ul>
<li>检查 bundle 中的无用文件，不要打包到 app 或者静态库中。可以点击文件，在右侧的 file inspector 里面的 target membership 中取消勾选；或者在 build phase 里面的 Copy Bundle Resources 中去掉。</li>
<li>确定 dead code（代码被定义但从未被调用）被剥离，build setting 里 DEAD_CODE_STRIPPING = YES。 去掉冗余的代码，即使一点冗余代码，编译后体积也是很可观的。</li>
</ul>
<h2>编译设置</h2>
<ul>
<li>
<p><code>Optimization Level</code>设置为<code>Fastest, Smallest [-Os]</code>，<code>Strip Debug</code><br>
<code>Symbols During Copy</code>设置为<code>YES (COPY_PHASE_STRIP = YES)</code><br>
这样会减小接近一半的体积，但是在 release 版本，这些貌似是默认的配置，但是不妨也检查一下。 此外在 debug 版本最好在完成开发测试后，设置成这种模式，重新测试一遍，因为不同的编译设置可能会掩盖一些 bug。</p>
</li>
<li>
<p>设置<code>IOS_DEPLOYMENT_TARGET</code> 为想要运行系统的最低版本</p>
</li>
<li>
<p>设置需要的 arm <a href="http://lib.csdn.net/base/16" target="_blank" rel="noopener noreferrer">架构</a>，设置 <code>ARCHS = arm64</code>可以消除 armv6 架构，潜在的减少近一半的容量。<br>
<a href="https://www.innerfence.com/howto/apple-ios-devices-dates-versions-instruction-sets" target="_blank" rel="noopener noreferrer">iOS Devices: ARM，尺寸，像素一览表</a><br>
1，如果想自己的 app 在各个机器都能够最高效率的运行，则需要将 Build Active Architecture Only 改为 NO,Valid architectures 选择对应的指令集：armv7 armv7s arm64。这个会为各个指令集编译对应的代码，因此最后的 ipa 体积基本翻了 3 倍,Release 版本必须 NO。<br>
2，如果想让 app 体积保持最小，则现阶段应该选择 Valid architectures 为 armv7,这样 Build Active Architecture Only 选 YES 或 NO 就无所谓了</p>
</li>
</ul>
<h2>其他</h2>
<ul>
<li>将应用的中一些数据，如长字符串、表格等移到外部文件中，不要放在代码里面，这样能减小一些体积，因为外部文件的压缩率要比应用中的数据压缩率高。</li>
</ul>
<h2>编译选项</h2>
<ol>
<li>编译器优化级别<br>
Build Settings-&gt;Optimization Level 有几个编译优化选项，release 版应该选择 Fastest, Smalllest，这个选项会开启那些不增加代码大小的全部优化，并让可执行文件尽可能小。</li>
<li>去除符号信息<br>
Strip Linked Product / Deployment Postprocessing / Symbols Hidden by Default 在 release 版本应该设为 yes，可以去除不必要的调试符号。Symbols Hidden by Default 会把所有符号都定义成”private extern”，详细信息见<a href="https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/MachOTopics/1-Articles/executing_files.html#//apple_ref/doc/uid/TP40001829-97021-TPXREF121" target="_blank" rel="noopener noreferrer">官方文档</a>。<br>
这些选项目前都是 XCode 里 release 的默认选项，但旧版 XCode 生成的项目可能不是，可以检查一下。其他优化还可以参考官方文档—<a href="https://developer.apple.com/legacy/library/documentation/Performance/Conceptual/CodeFootprint/CodeFootprint.pdf" target="_blank" rel="noopener noreferrer">CodeFootprint.pdf</a></li>
</ol>
<h3>第三方库统计</h3>
<p>项目里会引入很多第三方静态库，如果能知道这些第三方库在可执行文件里占用的大小，就可以评估是否值得去找替代方案去掉这个第三方库。我们可以从 linkmap 中统计出这个信息，对此写了个 node.js 脚本，可以通过 linkmap 统计每个.o 目标文件占用的体积和每个.a 静态库占用的体积，<a href="https://gist.github.com/bang590/8f3e9704f1c2661836cd" target="_blank" rel="noopener noreferrer">详见这里</a>(需翻墙)。</p>
<h3>ARC-&gt;MRC</h3>
<p>有人提出用 ARC 写的代码编译出来的可执行文件是会比用 MRC 大的，原因大致是 ARC 代码会在某些情况多出一些 retain 和 release 的指令，例如调用一个方法，它返回的对象会被 retain，退出作用域后会被 release，MRC 就不需要，汇编指令变多，机器码变多，可执行文件就变大了。还有其他细节实现的区别，先不纠结了。<br>
那用 ARC 究竟会增大多少体积？我觉得从汇编指令的增多减少去算是很难算准确的，这东西涉及细节太多，还是得从统计的角度计算。做了几个对比试验，统计了几个同时支持 ARC/MRC 的开源项目在开启/关闭 ARC 的情况下<strong>TEXT 代码段的大小对比。只对比</strong>TEXT 代码段是因为：<br>
ARC 对可执行文件大小的影响几乎都是在代码段<br>
可执行文件会进行某种对齐，例如有些段在不足 32K 的时候填充 0 直到对齐 32K，若用可执行文件大小对比结果可能是对齐后的，不准确。</p>
<p>实验数据：</p>
<p>|</p>
]]></content:encoded>
      <enclosure url="https://zero-space.s3.amazonaws.com/photos/2e6cc210-e9a6-4a0f-9c05-2c4056a982acx840.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>WKWebView拦截URL</title>
      <link>https://www.linkjm.com/posts/iOS/system/WKWebView-URL.html</link>
      <guid>https://www.linkjm.com/posts/iOS/system/WKWebView-URL.html</guid>
      <source url="https://www.linkjm.com/rss.xml">WKWebView拦截URL</source>
      <description>本文介绍使用 WKWebView 拦截 url 进行原生界面跳转 3.gif3.gif 使用代理方法 decidePolicyForNavigationAction 自定义方法传过来 url 进行判断，需要 html 元素本身就有跳转链接，才可以拦截，如没有，拦截不到。下文 app://xxx 链接为自定义链接</description>
      <category>iOS</category>
      <pubDate>Sat, 27 May 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>本文介绍使用 WKWebView 拦截 url 进行原生界面跳转</p>
</blockquote>
<figure><img src="https://storage1.cuntuku.com/2017/05/27/3.gif" alt="3.gif" tabindex="0" loading="lazy"><figcaption>3.gif</figcaption></figure>
<ul>
<li>使用代理方法 decidePolicyForNavigationAction</li>
</ul>
<div class="language-objc" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> (^)(WKNavigationActionPolicy))decisionHandler {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 获取完整url并进行UTF-8转码</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    NSString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *strRequest </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [navigationAction.request.URL.absoluteString </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">stringByRemovingPercentEncoding</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> ([strRequest </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">hasPrefix:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"app://"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">]) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 拦截点击链接</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        [</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> handleCustomAction:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">strRequest];</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 不允许跳转</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">      	decisionHandler</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(WKNavigationActionPolicyCancel);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    	// 允许跳转</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">        decisionHandler</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(WKNavigationActionPolicyAllow);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><ul>
<li>自定义方法传过来 url 进行判断，需要 html 元素本身就有跳转链接，才可以拦截，如没有，拦截不到。下文 app://xxx 链接为自定义链接</li>
</ul>
<div class="language-objc line-numbers-mode" data-highlighter="shiki" data-ext="objc" style="--shiki-light:#abb2bf;--shiki-dark:#abb2bf;--shiki-light-bg:#282c34;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code"><code class="language-objc"><span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">)handleCustomAction:(</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *)URL</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 判断跳转</span></span>
<span class="line"><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">    NSString</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> *link_id </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379"> @""</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> ([URL </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">hasPrefix:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"app://video"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">]) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 视频</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">        MMLog</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"点击了视频</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">%@</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,link_id);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> ([URL </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">hasPrefix:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"app://item"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">]) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 单品</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">        MMLog</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"点击了单品</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">%@</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,link_id);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#C678DD;--shiki-dark:#C678DD"> if</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> ([URL </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">hasPrefix:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"app://brand"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">]) {</span></span>
<span class="line"><span style="--shiki-light:#7F848E;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // 品牌</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">        link_id </span><span style="--shiki-light:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF"> [URL </span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">substringFromIndex:</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF"> stringWithFormat:</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"app://brand"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">].length];</span></span>
<span class="line"><span style="--shiki-light:#61AFEF;--shiki-dark:#61AFEF">        MMLog</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">@"点击了品牌</span><span style="--shiki-light:#D19A66;--shiki-dark:#D19A66">%@</span><span style="--shiki-light:#98C379;--shiki-dark:#98C379">"</span><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">,link_id);</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div>]]></content:encoded>
      <enclosure url="https://storage1.cuntuku.com/2017/05/27/3.gif" type="image/gif"/>
    </item>
  </channel>
</rss>