<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="https://oragekk.me/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://oragekk.me/rss.xml" rel="self" type="application/rss+xml"/>
    <title>Oragekk&amp;apos;s Blog</title>
    <link>https://oragekk.me/</link>
    <description>上冬十二的博客</description>
    <language>zh-CN</language>
    <pubDate>Mon, 23 Feb 2026 02:39:44 GMT</pubDate>
    <lastBuildDate>Mon, 23 Feb 2026 02:39:44 GMT</lastBuildDate>
    <generator>@vuepress/plugin-feed</generator>
    <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
    <category>Blog</category>
    <category>About</category>
    <category>使用指南</category>
    <category>收藏</category>
    <category>AI</category>
    <category>Linux</category>
    <category>python</category>
    <category>rust</category>
    <category>浏览器</category>
    <category>工具教程</category>
    <category>Vercel</category>
    <category>开源软件</category>
    <category>GitHub</category>
    <category>Git</category>
    <category>CSS</category>
    <category>JavaScript</category>
    <category>Vue</category>
    <category>Flutter</category>
    <category>Dart</category>
    <category>前端跨平台</category>
    <category>iOS</category>
    <category>Swift</category>
    <item>
      <title>关于本站</title>
      <link>https://oragekk.me/about.html</link>
      <guid>https://oragekk.me/about.html</guid>
      <source url="https://oragekk.me/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>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-ext="shell" data-title="shell"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">.</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> .github</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> ISSUE_TEMPLATE</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # issus 模版</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   └──</span><span style="color:#98C379;--shiki-dark:#98C379"> bug-report.yml</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   └──</span><span style="color:#98C379;--shiki-dark:#98C379"> workflows</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">       └──</span><span style="color:#98C379;--shiki-dark:#98C379"> deploy-docs.yml</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # 推送脚本</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> CNAME</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> LICENSE</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> README.md</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> api</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   └──</span><span style="color:#98C379;--shiki-dark:#98C379"> proxy.js</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # 跨域代理</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> env.d.ts</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> package.json</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> pnpm-lock.yaml</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> script</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> requirements.txt</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> </span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   └──</span><span style="color:#98C379;--shiki-dark:#98C379"> submit.py</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # Github Actions 推送URL使用脚本</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> src</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> .vuepress</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> client.ts</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # 客户端配置文件</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> components</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> MyCoverLink.vue</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # 友链组件</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   └──</span><span style="color:#98C379;--shiki-dark:#98C379"> Mylink.vue</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # 卡片组件</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> config.ts</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # vuepress配置文件</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> data</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # 数据</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> navbar</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> plugins</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> vuepress-plugin-canvas</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> vuepress-plugin-gradient-cover</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> vuepress-plugin-hitokoto</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> vuepress-plugin-live2DAssist</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   └──</span><span style="color:#98C379;--shiki-dark:#98C379"> vuepress-plugin-popper</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> public</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> assets</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # 资源</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> sidebar</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> styles</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> theme</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> api</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   └──</span><span style="color:#98C379;--shiki-dark:#98C379"> bing.ts</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # bing 每日壁纸</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> components</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> #自定义组件</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> index.ts</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> layouts</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # 自定义布局</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   └──</span><span style="color:#98C379;--shiki-dark:#98C379"> utils</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">       ├──</span><span style="color:#98C379;--shiki-dark:#98C379"> busuanzi.pure.js</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # 不蒜子统计</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">       └──</span><span style="color:#98C379;--shiki-dark:#98C379"> time.ts</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> #运行时间</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   │</span><span style="color:#98C379;--shiki-dark:#98C379">   └──</span><span style="color:#98C379;--shiki-dark:#98C379"> theme.ts</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # 主题配置文件</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">   └──</span><span style="color:#98C379;--shiki-dark:#98C379"> README.md</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├──</span><span style="color:#98C379;--shiki-dark:#98C379"> tsconfig.json</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">└──</span><span style="color:#98C379;--shiki-dark:#98C379"> vercel.json</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> # vercel 配置文件</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>News.vue(说说列表布局)</li>
</ul>
</li>
<li>
<p><strong>自定义组件</strong></p>
<ul>
<li>BlogHero.vue</li>
<li>PageFooter.vue</li>
<li>Sponsor.vue（打赏组件）</li>
<li>NewsList.vue （说说列表）</li>
<li>NewsItem.vue （说说item）</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>
</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>关于我</title>
      <link>https://oragekk.me/intro.html</link>
      <guid>https://oragekk.me/intro.html</guid>
      <source url="https://oragekk.me/rss.xml">关于我</source>
      <description>关于我 Profile Exploration &amp; geek enthusiast, full-stack front-end engineer, UX Designer | Currently focusing on cross-platform development with main directions in iOS, ReactNative...</description>
      <category>About</category>
      <pubDate>Sun, 12 Mar 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[

<h2>Profile</h2>
<p><em>Exploration &amp; geek enthusiast, full-stack front-end engineer, UX Designer | Currently focusing on cross-platform development with main directions in iOS, ReactNative, Flutter, and Vue | Discovering a bigger world together with you.</em></p>
<div class="hint-container tip">
<p class="hint-container-title">Profile</p>
<p>探索 &amp; 极客 爱好者，大前端工程师，用户体验设计师 |目前主要研究方向在跨平台开发上，iOS、ReactNative、Flutter、Vue是主要方向| 期待与你一起发现更大的世界</p>
</div>
<h2>Skills</h2>
<h3>Languages</h3>
<img src="https://skillicons.dev/icons?i=c,cs,swift,javascript,ts,dart,python,html,css,java,regex,md,sass,bash&amp;theme=dark&amp;&amp;perline=10" alt="Skills">
<h3>Frameworks</h3>
<img src="https://skillicons.dev/icons?i=flutter,vue,nodejs,react,reactivex,redux,dotnet&amp;theme=dark&amp;&amp;perline=10" alt="Skills">
<h3>Databases</h3>
<img src="https://skillicons.dev/icons?i=sqlite,mysql,redis&amp;theme=dark&amp;&amp;perline=10" alt="Skills">
<h3>Hardwares</h3>
<img src="https://skillicons.dev/icons?i=raspberrypi&amp;theme=dark&amp;&amp;perline=10" alt="Skills">
<h3>Others</h3>
<img src="https://skillicons.dev/icons?i=vim,visualstudio,vscode,androidstudio,aws,figma,gradle,gcp,git,github,gitlab,idea,jenkins,linux,postman,powershell,linkedin,stackoverflow,svg,tensorflow,twitter&amp;theme=dark&amp;&amp;perline=10" alt="Skills">
<h2>Github Summary</h2>
<img src="https://github-trophies.vercel.app/?username=oragekk&amp;theme=radical&amp;margin-w=25" alt="Github Summary">
<img src="https://github-readme-stats.vercel.app/api/top-langs/?username=oragekk&amp;theme=radical&amp;layout=compact&amp;bg_color=30,ef475d,904e95&amp;title_color=fff&amp;text_color=fff">
<img src="https://github-readme-stats.vercel.app/api?username=oragekk&amp;count_private=true&amp;show_icons=true&amp;theme=radical&amp;bg_color=30,ef475d,904e95&amp;title_color=fff&amp;text_color=fff">
<h2>Preview</h2>
<figure><img src="https://count.getloli.com/get/@:oragekk?theme=gelbooru-h" alt=":Augenestern-creator" tabindex="0" loading="lazy"><figcaption>:Augenestern-creator</figcaption></figure>
<h2>Activity Graph</h2>
<figure><a href="https://github.com/OrageKK/github-readme-activity-graph" target="_blank" rel="noopener noreferrer"><img src="https://github-readme-activity-graph-sandy.vercel.app/graph?username=oragekk&amp;theme=dracula" alt="Ashutosh's github activity graph" tabindex="0" loading="lazy"></a><figcaption>Ashutosh's github activity graph</figcaption></figure>
<h2>关于本站</h2>
<p><a href="/about">传送门</a></p>
]]></content:encoded>
      <enclosure url="https://count.getloli.com/get/@:oragekk?theme=gelbooru-h" type="image/"/>
    </item>
    <item>
      <title>如何利用GitHub Action提交URL到搜索引擎</title>
      <link>https://oragekk.me/blog/auto-push.html</link>
      <guid>https://oragekk.me/blog/auto-push.html</guid>
      <source url="https://oragekk.me/rss.xml">如何利用GitHub Action提交URL到搜索引擎</source>
      <description>相关信息 这是一个GitHub Actions的配置文件，整体思路是，获取本次更新的url （通过对比两次提交的sitemap.xml文件），所以触发条件是deploy分支有推送并修改了sitemap.xml文件，再利用python脚本进行url推送到百度、Bing、Google 脚本代码请看这里👉提交URL到搜索引擎（百度、Bing、Google）</description>
      <category>Blog</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<br>
（通过对比两次提交的sitemap.xml文件），所以触发条件是deploy分支有推送并修改了<code>sitemap.xml</code>文件，再利用python脚本进行url推送到百度、Bing、Google</p>
<p>脚本代码请看这里👉<a href="/posts/Python/submit-url">提交URL到搜索引擎（百度、Bing、Google）</a></p>
</div>
<!-- more -->
<p>前些天不是写了Bing的提交脚本吗，但是每次都要自己手动跑脚本也很麻烦，但我又没有服务器，所以用上了GitHub Actions了嘛<br>
总的来说做了以下事情：</p>
<ol>
<li>提交代码并修改sitemap.xml 后触发工作流</li>
<li>比对上一次提交和本次提交的新增URL</li>
<li>将URL分别提交给百度站长、Bing、Google 供搜索引擎快速收录</li>
<li></li>
</ol>
<h2>流程解读</h2>
]]></content:encoded>
      <enclosure url="https://w.wallhaven.cc/full/l3/wallhaven-l3xk6q.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>评论系统从多说迁移到disqus指南</title>
      <link>https://oragekk.me/blog/disqus.html</link>
      <guid>https://oragekk.me/blog/disqus.html</guid>
      <source url="https://oragekk.me/rss.xml">评论系统从多说迁移到disqus指南</source>
      <description>由于多说评论系统将于 6 月 1 日下线，所以准备迁移至disqus，相比较的话对于国内环境还是多说好用一点，毕竟加载快，支持各大媒体的分享，也不用小伙伴们翻墙；而 disqus 分享也只支持 Facebook 和 twitter。。PS:貌似现在又被墙了，以后有时间再换吧，目前就先这样，国内据说畅言还不错 迁移过程 1.首先为了不丢失原有评论，导出多...</description>
      <category>Blog</category>
      <pubDate>Fri, 14 Apr 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>由于多说评论系统将于 6 月 1 日下线，所以准备迁移至<a href="https://disqus.com/" target="_blank" rel="noopener noreferrer">disqus</a>，相比较的话对于国内环境还是多说好用一点，毕竟加载快，支持各大媒体的分享，也不用小伙伴们翻墙；而 disqus 分享也只支持 Facebook 和 twitter。。PS:貌似现在又被墙了，以后有时间再换吧，目前就先这样，国内据说<a href="http://changyan.kuaizhan.com/static/help/" target="_blank" rel="noopener noreferrer">畅言</a>还不错</p>
</blockquote>
<h2>迁移过程</h2>
<h3>1.首先为了不丢失原有评论，导出多说评论</h3>
<figure><img src="http://i2.muimg.com/567571/f0d7b62ff410decf.png" alt="" tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<h3>2.文件转换</h3>
<ul>
<li>由于 disqus 不支持多说导出的.json 文件，所以需要进行转换为 xml 文件，此处使用 github 上的轮子<a href="https://github.com/JamesPan/duoshuo-migrator" target="_blank" rel="noopener noreferrer">JamesPan/duoshuo-migrator</a></li>
<li>使用步骤
<ol>
<li>下载<a href="https://github.com/JamesPan/duoshuo-migrator/blob/master/duoshuo-migrator.py?raw=true" target="_blank" rel="noopener noreferrer">duoshuo-migrator.py</a>并安装依赖<br>
<img src="http://i2.muimg.com/567571/f98e1281fec1cdd7.png" alt="" loading="lazy"></li>
<li>执行 <code>python duoshuo-migrator.py -i ~/Desktop/export.json  -o disqus.xml</code>命令<br>
<img src="http://i2.muimg.com/567571/8e27bcddc31c29b2.png" alt="" loading="lazy"></li>
<li>将转换完成文件导入<br>
<img src="http://i2.muimg.com/567571/213761ad8cf62886.png" alt="" loading="lazy"></li>
</ol>
</li>
</ul>
]]></content:encoded>
      <enclosure url="http://i2.muimg.com/567571/f0d7b62ff410decf.png" type="image/png"/>
    </item>
    <item>
      <title>Jekyll旧站回忆</title>
      <link>https://oragekk.me/blog/jekyll.html</link>
      <guid>https://oragekk.me/blog/jekyll.html</guid>
      <source url="https://oragekk.me/rss.xml">Jekyll旧站回忆</source>
      <description>“Yeah It&amp;apos;s on. ” 前言 Oragekk 的 Blog 就这么开通了。 跳过废话，直接看技术实现 2016 年，11 月 总算有个地方可以好好写点东西了。 作为一个程序员， 看多了别人的 Blog 这种轮子都是酷炫的不要不要的，自己其实一种想搞一个，前两天发现了 GitHub Pages +Jekyll 的技术方案，一下子就上瘾了。 终于...</description>
      <category>Blog</category>
      <pubDate>Thu, 03 Nov 2016 12:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>“Yeah It's on. ”</p>
</blockquote>
<h2>前言</h2>
<p>Oragekk 的 Blog 就这么开通了。</p>
<p><a href="#build">跳过废话，直接看技术实现 </a></p>
<p>2016 年，11 月 总算有个地方可以好好写点东西了。</p>
<p>作为一个程序员， 看多了别人的 Blog 这种轮子都是酷炫的不要不要的，自己其实一种想搞一个，前两天发现了 GitHub Pages +Jekyll 的技术方案，一下子就上瘾了。</p>
<p>终于可以有自己的自留地了，之前一直在简书上写一些技术类的文章，这次可以有个自己的地盘，想怎么写就怎么写。😝 哈哈。不过这些前端的东西对我也是一种挑战，似懂非懂的看着模板，和一堆 js+css+html 的代码。。一顿头大。。对照着效果，一步步自己改。改好了之后也是蛮有成就感的嘛</p>
<h2><p id="build"></p></h2>
<h2>正文</h2>
<p>接下来说说搭建这个博客的技术细节。</p>
<p>正好之前就有关注过 <a href="https://pages.github.com/" target="_blank" rel="noopener noreferrer">GitHub Pages</a> + <a href="http://jekyllrb.com/" target="_blank" rel="noopener noreferrer">Jekyll</a> 快速 Building Blog 的技术方案，非常轻松时尚。</p>
<p>其优点非常明显：</p>
<ul>
<li><strong>Markdown</strong> 带来的优雅写作体验</li>
<li>非常熟悉的 Git workflow ，<strong>Git Commit 即 Blog Post</strong></li>
<li>利用 GitHub Pages 的域名和免费无限空间，不用自己折腾主机
<ul>
<li>如果需要自定义域名，也只需要简单改改 DNS 加个 CNAME 就好了</li>
</ul>
</li>
<li>Jekyll 的自定制非常容易，基本就是个模版引擎</li>
<li>Jekyll 的安装倒是不难，难的是安装它之前的一堆安装</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>评论插件 Waline 之邮件通知配置</title>
      <link>https://oragekk.me/blog/waline-mail.html</link>
      <guid>https://oragekk.me/blog/waline-mail.html</guid>
      <source url="https://oragekk.me/rss.xml">评论插件 Waline 之邮件通知配置</source>
      <description>评论插件 Waline 之邮件通知配置 ✨ 陆续优化中……这次到了评论的邮件通知，由于 waline 带后端，可以开启评论通知，我是部署在 Vercel 上的，配置一下就可以了，模版要感谢小波同学 前置注意点 1.邮件要注意每日发信限制，短时密集评论会导致直接被封，禁止发信 2.最好不要使用自己平常使用的邮箱，使用一个单独的邮箱来操作 3.有服务器的同...</description>
      <category>Blog</category>
      <pubDate>Wed, 29 Mar 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[
<div class="hint-container info">
<p class="hint-container-title">✨</p>
<p>陆续优化中……这次到了评论的邮件通知，由于 waline 带后端，可以开启评论通知，我是部署在 Vercel 上的，配置一下就可以了，模版要感谢<a href="https://blog.ganxb2.com/" target="_blank" rel="noopener noreferrer">小波同学</a></p>
<p>前置注意点</p>
<p>1.邮件要注意每日发信限制，短时密集评论会导致直接被封，禁止发信</p>
<p>2.最好不要使用自己平常使用的邮箱，使用一个单独的邮箱来操作</p>
<p>3.有服务器的同学，自己部署SMTP服务可以不受限制</p>
</div>
<p>老规矩先放效果图:</p>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/29/8958076dc393c8df.png" alt="show" tabindex="0" loading="lazy"><figcaption>show</figcaption></figure>
<h2><a class="header-anchor" href="#_1-waline-官方邮件通知环境变量说明"><span>1. waline 官方邮件通知环境变量说明<sup class="footnote-ref"></sup></span></a><a href="#footnote1">[1]</a><a class="footnote-anchor" id="footnote-ref1"></a></h2>
<h3>1.1 waline 邮件通知必填环境变量</h3>
<p><code>SMTP_SERVICE: SMTP</code> 邮件发送服务提供商。例如 163 在受支持的运营商列表可以直接填写 163</p>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>你可以在 <a href="https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json" target="_blank" rel="noopener noreferrer">这里</a> 查看所有支持的运营商。</p>
<p>如果你的运营商不受支持，你必须填写 <code>SMTP_HOST 和 SMTP_PORT</code>。</p>
<ul>
<li>
<p><code>SMTP_HOST</code>: SMTP 服务器地址，一般可以在邮箱的设置中找到。</p>
</li>
<li>
<p><code>SMTP_PORT</code>: SMTP 服务器端口，一般可以在邮箱的设置中找到。</p>
</li>
</ul>
</div>
<p><code>SMTP_USER</code>: SMTP 邮件发送服务的用户名，一般为登录邮箱【切记要完整填写xxx@xxx.xx】。</p>
<p><code>SMTP_PASS</code>: SMTP 邮件发送服务的密码，一般为邮箱登录密码，部分邮箱(例如 163)是单独的 SMTP 密码。 <sup class="footnote-ref"><a href="#footnote2">[2]</a><a class="footnote-anchor" id="footnote-ref2"></a></sup></p><a class="footnote-anchor" id="footnote-ref2">
<p><code>SMTP_SECURE</code>: 是否使用 SSL 连接 SMTP。</p>
<p><code>SITE_NAME</code>: 网站名称，用于在消息中显示。</p>
<p><code>SITE_URL</code>: 网站地址，用于在消息中显示。</p>
<p><code>AUTHOR_EMAIL</code>: 博主邮箱，用来接收新评论通知。如果是博主发布的评论则不进行提醒通知。</p>
<div class="hint-container tip">
<p class="hint-container-title">注意</p>
<p>其中<code>SITE_NAME</code>如果有类似 <code>xxxx's Blog</code> 这种的网站名称，在 Vercel 中配置的时候，可以使用<code>xxxx Blog</code> 格式<br>
因为<code>'</code>单引号会在模版中被转义为<code>&amp;#39;s</code></p>
</div>
</a><h3><a class="footnote-anchor" id="footnote-ref2"></a><a class="header-anchor" href="#_1-2-waline-邮件通知选填环境变量"><span>1.2 waline 邮件通知选填环境变量</span></a></h3>
<p><code>SENDER_NAME</code>: 自定义发送邮件的发件人</p>
<p><code>SENDER_EMAIL</code>: 自定义发送邮件的发件地址</p>
<div class="hint-container tip">
<p class="hint-container-title">注意</p>
<p>其中<code>SENDER_NAME</code>和<code>SENDER_EMAIL</code> 要成对出现</p>
</div>
<h3>1.3 waline 邮件通知模版环境变量</h3>
<p><code>MAIL_SUBJECT</code>: 自定义评论回复邮件标题</p>
<p><code>MAIL_TEMPLATE</code>: 自定义评论回复邮件内容</p>
<p><code>MAIL_SUBJECT_ADMIN</code>: 自定义新评论通知邮件标题</p>
<p><code>MAIL_TEMPLATE_ADMIN</code>: 自定义新评论通知邮件内容</p>
<h3>1.4 waline邮件通知模版index.js服务端配置参数</h3>
<ul>
<li>
<p><code>mailSubject</code> 类型: string</p>
<p>评论回复邮件标题自定义，等同于环境变量 <code>MAIL_SUBJECT</code>。</p>
</li>
<li>
<p><code>mailTemplate</code> 类型: string</p>
<p>评论回复邮件内容自定义，等同于环境变量 <code>MAIL_TEMPLATE</code>。</p>
</li>
<li>
<p><code>mailSubjectAdmin</code> 类型: string</p>
<p>新评论通知邮件标题自定义，等同于环境变量 <code>MAIL_SUBJECT_ADMIN</code>。</p>
</li>
<li>
<p><code>mailTemplateAdmin</code> 类型: string</p>
<p>新评论通知邮件内容自定义，等同于环境变量 <code>MAIL_TEMPLATE_ADMIN</code>。</p>
</li>
</ul>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>自此以下配置都是关于邮件模版的，1.3和1.4二选一配置即可，1.1是必填配置，自行填写即可。</p>
</div>
<h2>2.根据部署方式选择合适的邮件模版修改方法</h2>
<h3>2.1 vercel</h3>
<ul>
<li>环境变量<sup class="footnote-ref"><a href="#footnote3">[3]</a><a class="footnote-anchor" id="footnote-ref3"></a></sup></li><a class="footnote-anchor" id="footnote-ref3">
<li>私有walie仓库index.js文件【推荐】</li>
</a></ul><a class="footnote-anchor" id="footnote-ref3">
</a><h3><a class="footnote-anchor" id="footnote-ref3"></a><a class="header-anchor" href="#_2-2-独立部署"><span>2.2 独立部署</span></a></h3>
<ul>
<li>修改服务端入口文件index.js</li>
</ul>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>如果你使用模板，请额外注意你需要自行保存这些配置，因为它们会在拉取官方最新模板时被覆盖。</p>
<p>建议将官方最新模板上传到自己的仓库，并进行修改。</p>
</div>
<h2>3.开始配置</h2>
<h3>3.1 设置完毕1.1中Vercel配置必须的环境变量</h3>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/29/ab416544129053c3.png" alt="1.1" tabindex="0" loading="lazy"><figcaption>1.1</figcaption></figure>
<h3>3.2 继续使用环境变量设置模版</h3>
<p><code>MAIL_SUBJECT_ADMIN</code></p>
<div class="language-js" data-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">{{site.name | safe}}</span><span style="color:#E06C75;--shiki-dark:#E06C75"> 上有新评论了</span></span></code></pre>
</div><p><code>MAIL_TEMPLATE_ADMIN</code></p>
<div class="language-html line-numbers-mode" data-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"background: url(https://tva3.sinaimg.cn/large/c56b8822ly1h62npb7s1ej201y01y0lh.jpg);padding:40px 0px 20px;margin:0px;background-color:#FFCDCE;width:100%;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">style</span><span style="color:#D19A66;--shiki-dark:#D19A66"> type</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"text/css"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span><span style="color:#C678DD;--shiki-dark:#C678DD">@media</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> screen </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">and</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (max-width:</span><span style="color:#D19A66;--shiki-dark:#D19A66">600</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">){</span><span style="color:#D19A66;--shiki-dark:#D19A66">.afterimg</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#D19A66;--shiki-dark:#D19A66">.beforeimg</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{display:</span><span style="color:#D19A66;--shiki-dark:#D19A66">none</span><span style="color:#C678DD;--shiki-dark:#C678DD">!important</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"border-radius: 10px 10px 10px 10px;font-size:14px;color: #555555;width: 530px;font-family:'Century Gothic','Trebuchet MS','Hiragino Sans GB',微软雅黑,'Microsoft Yahei',Tahoma,Helvetica,Arial,'SimSun',sans-serif;margin:50px auto;max-width:100%;background: ##ffffff;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">img</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"beforeimg"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"width:530px;height:317px;pointer-events:none"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> src</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">img</span><span style="color:#D19A66;--shiki-dark:#D19A66"> src</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"https://npm.elemecdn.com/hexo-butterfly-envelope/lib/violet.jpg"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"width:100%;overflow:hidden;pointer-events:none;margin-top: -120px;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"width:100%;background:#f8d1ce;color:#9d2850;background-image: -moz-linear-gradient(0deg, rgb(67, 198, 184), rgb(255, 209, 244));height: 66px;background: url(https://tva2.sinaimg.cn/large/c56b8822ly1h61tb7tagcj20ii01u3yc.jpg) left top no-repeat;display: flex;justify-content: center;flex-direction: column;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"font-size:16px;font-weight: bold;text-align:center;word-break:break-all;margin:0;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		您在&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"text-decoration:none;color: #9d2850;"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> href</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"{{site.url}}"</span><span style="color:#D19A66;--shiki-dark:#D19A66">target</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"_blank"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;{{site.name}}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;上的文章有了新的评论&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"formmain"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"background:#fff;width:100%;max-width:800px;margin:auto auto;overflow:hidden;margin-bottom: -155px;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"margin:40px auto;width:90%;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">strong</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;{{self.nick}}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">strong</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt; 回复说：&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"background: #fafafa repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;{{self.comment | safe}}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"text-align:center;position: relative;z-index: 99;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;您可以点击&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"text-decoration:none;color:#cf5c83"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> href</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"{{site.postUrl}}"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> target</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"_blank"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;查看回复的完整內容&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">img</span><span style="color:#D19A66;--shiki-dark:#D19A66"> src</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"width:100%;margin:25px auto 5px auto;display:block;pointer-events:none"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"bottomhr"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"font-size:12px;text-align:center;color:#999"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;上冬十二(oragekk)博客竭诚为您服务！&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">img</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"afterimg"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"width:535px;height:317px;z-index:100;margin-left: -3px;"</span><span style="color:#D19A66;--shiki-dark:#D19A66">src</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>MAIL_SUBJECT</code></p>
<div class="language-js" data-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">{{parent.nick | safe}}</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">，『</span><span style="color:#E06C75;--shiki-dark:#E06C75">{{site.name | safe}}</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">』</span><span style="color:#E06C75;--shiki-dark:#E06C75">上的评论收到了回复</span></span></code></pre>
</div><p><code>MAIL_TEMPLATE</code></p>
<div class="language-html line-numbers-mode" data-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"background: url(https://tva3.sinaimg.cn/large/c56b8822ly1h62npb7s1ej201y01y0lh.jpg);padding:40px 0px 20px;margin:0px;background-color:#FFCDCE;width:100%;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">style</span><span style="color:#D19A66;--shiki-dark:#D19A66"> type</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"text/css"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span><span style="color:#C678DD;--shiki-dark:#C678DD">@media</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> screen </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">and</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (max-width:</span><span style="color:#D19A66;--shiki-dark:#D19A66">600</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">){</span><span style="color:#D19A66;--shiki-dark:#D19A66">.afterimg</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#D19A66;--shiki-dark:#D19A66">.beforeimg</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{display:</span><span style="color:#D19A66;--shiki-dark:#D19A66">none</span><span style="color:#C678DD;--shiki-dark:#C678DD">!important</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"border-radius: 10px 10px 10px 10px;font-size:14px;color: #555555;width: 530px;font-family:'Century Gothic','Trebuchet MS','Hiragino Sans GB',微软雅黑,'Microsoft Yahei',Tahoma,Helvetica,Arial,'SimSun',sans-serif;margin:50px auto;max-width:100%;background: ##ffffff;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">img</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"beforeimg"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"width:530px;height:317px;z-index:-100;pointer-events:none"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> src</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">img</span><span style="color:#D19A66;--shiki-dark:#D19A66"> src</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"https://npm.elemecdn.com/hexo-butterfly-envelope/lib/violet.jpg"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"width:100%;overflow:hidden;pointer-events:none;margin-top: -120px;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"width:100%;background:#f8d1ce;color:#9d2850;background-image: -moz-linear-gradient(0deg, rgb(67, 198, 184), rgb(255, 209, 244));height: 66px;background: url(https://tva2.sinaimg.cn/large/c56b8822ly1h61tb7tagcj20ii01u3yc.jpg) left top no-repeat;display: flex;justify-content: center;flex-direction: column;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"font-size:16px;font-weight: bold;text-align:center;word-break:break-all;margin:0;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		您在&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"text-decoration:none;color: #9d2850;"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> href</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"{{site.url}}"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;『{{site.name | safe}}』&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;上的留言有新回复啦！&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"formmain"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"background:#fff;width:100%;max-width:800px;margin:auto auto;overflow:hidden;margin-bottom: -155px;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"margin:40px auto;width:90%;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;😊Hi，{{parent.nick}}，您曾在文章上发表评论：&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"background: #fafafa repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;{{parent.comment | safe}}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">strong</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;{{self.nick}}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">strong</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt; 给您的回复如下：&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"background: #fafafa repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;{{self.comment | safe}}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;您可以点击&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"text-decoration:none; color:#cf5c83"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> href</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"{{site.postUrl}}"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> target</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"_blank"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt; 查看回复的完整內容 &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;，欢迎再次光临&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"text-decoration:none; color:#cf5c83"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> href</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"{{site.url}}"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> target</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"_blank"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt; {{site.name}} &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;。&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">hr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> /&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"font-size:14px;color:#b7adad;text-align:center;position: relative;z-index: 99;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;本邮件为系统自动发送，请勿直接回复邮件哦，可到博文内容回复。&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">br</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> /&gt;{{site.url}}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">img</span><span style="color:#D19A66;--shiki-dark:#D19A66"> src</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"width:100%;margin:25px auto 5px auto;display:block;pointer-events:none"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"bottomhr"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"font-size:12px;text-align:center;color:#999"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;上冬十二(oragekk)博客竭诚为您服务！&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">			&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">img</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"afterimg"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"width:535px;height:317px;z-index:100;margin-left: -3px;"</span><span style="color:#D19A66;--shiki-dark:#D19A66">src</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>3.3 使用服务端入口文件index.js变量设置模版</h3>
<p>文件初始内容为:</p>
<div class="language-js" data-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Application</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> require</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'@waline/vercel'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">module</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">exports</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> Application</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">({</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    async</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> postSave</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">comment</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // do what ever you want after save comment</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">});</span></span></code></pre>
</div><div class="hint-container caution">
<p class="hint-container-title">注意!!!</p>
<p>在我查阅资料的过程中，发现很多教程中填写index.js的方式都是错误的，如下:</p>
<div class="language-js" data-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Application</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> require</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'@waline/vercel'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">module</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">exports</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> Application</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">({</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    async</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> postSave</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">comment</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // do what ever you want after save comment</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        mailSubjectAdmin</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">'{{site.name | safe}} 上有新评论了'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        ……</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">});</span></span></code></pre>
</div><p>这就是导致很多人index.js配置不成功的原因，把模版配置写在了postSave这个钩子函数里边,在Vercel部署的过程中就会直接报错，语法不正确</p>
</div>
<p>正确使用姿势:</p>
<div class="language-js line-numbers-mode" data-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Application</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> require</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'@waline/vercel'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">module</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">exports</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> Application</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">({</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    async</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> postSave</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">comment</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // do what ever you want after save comment</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    mailSubjectAdmin</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">'{{site.name | safe}} 上有新评论了'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    mailTemplateAdmin</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">`&lt;div style="background: url(https://tva3.sinaimg.cn/large/c56b8822ly1h62npb7s1ej201y01y0lh.jpg);padding:40px 0px 20px;margin:0px;background-color:#FFCDCE;width:100%;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">	&lt;style type="text/css"&gt;@media screen and (max-width:600px){.afterimg,.beforeimg{display:none!important}}&lt;/style&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">	&lt;div style="border-radius: 10px 10px 10px 10px;font-size:14px;color: #555555;width: 530px;font-family:'Century Gothic','Trebuchet MS','Hiragino Sans GB',微软雅黑,'Microsoft Yahei',Tahoma,Helvetica,Arial,'SimSun',sans-serif;margin:50px auto;max-width:100%;background: ##ffffff;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;img class="beforeimg" style="width:530px;height:317px;pointer-events:none" src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;img src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/violet.jpg" style="width:100%;overflow:hidden;pointer-events:none;margin-top: -120px;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;div style="width:100%;background:#f8d1ce;color:#9d2850;background-image: -moz-linear-gradient(0deg, rgb(67, 198, 184), rgb(255, 209, 244));height: 66px;background: url(https://tva2.sinaimg.cn/large/c56b8822ly1h61tb7tagcj20ii01u3yc.jpg) left top no-repeat;display: flex;justify-content: center;flex-direction: column;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;p style="font-size:16px;font-weight: bold;text-align:center;word-break:break-all;margin:0;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		您在&lt;a style="text-decoration:none;color: #9d2850;" href="{{site.url}}"target="_blank"&gt;{{site.name}}&lt;/a&gt;上的文章有了新的评论&lt;/p&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;div class="formmain" style="background:#fff;width:100%;max-width:800px;margin:auto auto;overflow:hidden;margin-bottom: -155px;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;div style="margin:40px auto;width:90%;"&gt;&lt;p&gt;&lt;strong&gt;{{self.nick}}&lt;/strong&gt; 回复说：&lt;/p&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;div style="background: #fafafa repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;"&gt;{{self.comment | safe}}&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;p style="text-align:center;position: relative;z-index: 99;"&gt;您可以点击&lt;a style="text-decoration:none;color:#cf5c83" href="{{site.postUrl}}" target="_blank"&gt;查看回复的完整內容&lt;/a&gt;&lt;/p&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;img src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png" style="width:100%;margin:25px auto 5px auto;display:block;pointer-events:none"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;p class="bottomhr" style="font-size:12px;text-align:center;color:#999"&gt;上冬十二(oragekk)博客竭诚为您服务！&lt;/p&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;img class="afterimg" style="width:535px;height:317px;z-index:100;margin-left: -3px;"src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">	&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">&lt;/div&gt;`</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    mailSubject</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">'{{parent.nick}}，您在『{{site.name}}』上发表的评论收到了来自 {{self.nick}} 的回复'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    mailTemplate</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">`&lt;div style="background: url(https://tva3.sinaimg.cn/large/c56b8822ly1h62npb7s1ej201y01y0lh.jpg);padding:40px 0px 20px;margin:0px;background-color:#FFCDCE;width:100%;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">	&lt;style type="text/css"&gt;@media screen and (max-width:600px){.afterimg,.beforeimg{display:none!important}}&lt;/style&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">	&lt;div style="border-radius: 10px 10px 10px 10px;font-size:14px;color: #555555;width: 530px;font-family:'Century Gothic','Trebuchet MS','Hiragino Sans GB',微软雅黑,'Microsoft Yahei',Tahoma,Helvetica,Arial,'SimSun',sans-serif;margin:50px auto;max-width:100%;background: ##ffffff;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;img class="beforeimg" style="width:530px;height:317px;z-index:-100;pointer-events:none" src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;img src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/violet.jpg" style="width:100%;overflow:hidden;pointer-events:none;margin-top: -120px;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;div style="width:100%;background:#f8d1ce;color:#9d2850;background-image: -moz-linear-gradient(0deg, rgb(67, 198, 184), rgb(255, 209, 244));height: 66px;background: url(https://tva2.sinaimg.cn/large/c56b8822ly1h61tb7tagcj20ii01u3yc.jpg) left top no-repeat;display: flex;justify-content: center;flex-direction: column;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;p style="font-size:16px;font-weight: bold;text-align:center;word-break:break-all;margin:0;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		您在&lt;a style="text-decoration:none;color: #9d2850;" href="{{site.url}}"&gt;『{{site.name | safe}}』&lt;/a&gt;上的留言有新回复啦！&lt;/p&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;div class="formmain" style="background:#fff;width:100%;max-width:800px;margin:auto auto;overflow:hidden;margin-bottom: -155px;"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;div style="margin:40px auto;width:90%;"&gt;&lt;p&gt;😊Hi，{{parent.nick}}，您曾在文章上发表评论：&lt;/p&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;div style="background: #fafafa repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;"&gt;{{parent.comment | safe}}&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;p&gt;&lt;strong&gt;{{self.nick}}&lt;/strong&gt; 给您的回复如下：&lt;/p&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;div style="background: #fafafa repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;"&gt;{{self.comment | safe}}&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;p&gt;您可以点击&lt;a style="text-decoration:none; color:#cf5c83" href="{{site.postUrl}}" target="_blank"&gt; 查看回复的完整內容 &lt;/a&gt;，欢迎再次光临&lt;a style="text-decoration:none; color:#cf5c83" href="{{site.url}}" target="_blank"&gt; {{site.name}} &lt;/a&gt;。&lt;hr /&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;p style="font-size:14px;color:#b7adad;text-align:center;position: relative;z-index: 99;"&gt;本邮件为系统自动发送，请勿直接回复邮件哦，可到博文内容回复。&lt;br /&gt;{{site.url}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;/p&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;img src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png" style="width:100%;margin:25px auto 5px auto;display:block;pointer-events:none"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;p class="bottomhr" style="font-size:12px;text-align:center;color:#999"&gt;上冬十二(oragekk)博客竭诚为您服务！&lt;/p&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">			&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">		&lt;img class="afterimg" style="width:535px;height:317px;z-index:100;margin-left: -3px;"src="https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png"&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">	&lt;/div&gt;</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">&lt;/div&gt;`</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">});</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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. 结语</h3>
<p>至此，大功告成，所有的注意点我都写在上边了</p>
<p>此模版来自SaraKale<sup class="footnote-ref"><a href="#footnote4">[4]</a><a class="footnote-anchor" id="footnote-ref4"></a></sup><a class="footnote-anchor" id="footnote-ref4">，经</a><a href="https://blog.ganxb2.com/" target="_blank" rel="noopener noreferrer">小波同学</a>修改美化<br>
其他模版同理，可以去SaraKale这里看看</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="footnote1" class="footnote-item"><p><a href="https://waline.js.org/guide/features/notification.html" target="_blank" rel="noopener noreferrer">https://waline.js.org/guide/features/notification.html</a> <a href="#footnote-ref1" class="footnote-backref">↩︎</a></p>
</li>
<li id="footnote2" class="footnote-item"><p>如 163 的 SMTP 授权码只生成一次，要复制存下来，如果丢失，后续不会再显示，只能重新生成 <a href="#footnote-ref2" class="footnote-backref">↩︎</a></p>
</li>
<li id="footnote3" class="footnote-item"><p>Vercel 的环境变量大小限制为 4KB ，所以如果您的模板很长，请使用代码配置， <a href="#footnote-ref3" class="footnote-backref">↩︎</a></p>
</li>
<li id="footnote4" class="footnote-item"><p><a href="https://www.sarakale.top/blog/posts/537344b2.html" target="_blank" rel="noopener noreferrer">waline 邮件通知模板样式一览</a> <a href="#footnote-ref4" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
]]></content:encoded>
      <enclosure url="https://s3.bmp.ovh/imgs/2023/03/29/8958076dc393c8df.png" type="image/png"/>
    </item>
    <item>
      <title>Markdown 展示</title>
      <link>https://oragekk.me/demo/markdown.html</link>
      <guid>https://oragekk.me/demo/markdown.html</guid>
      <source url="https://oragekk.me/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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> a</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#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><a class="footnote-anchor" id="footnote-ref1">.</a></p><a class="footnote-anchor" id="footnote-ref1">
</a><ul><a class="footnote-anchor" id="footnote-ref1">
</a><li><a class="footnote-anchor" id="footnote-ref1"></a><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>
<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://oragekk.me/demo/slides.html</link>
      <guid>https://oragekk.me/demo/slides.html</guid>
      <source url="https://oragekk.me/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://oragekk.me/logo.svg" type="image/svg+xml"/>
    </item>
    <item>
      <title>一人一句宋词</title>
      <link>https://oragekk.me/private/%E4%B8%80%E4%BA%BA%E4%B8%80%E5%8F%A5%E5%AE%8B%E8%AF%8D.html</link>
      <guid>https://oragekk.me/private/%E4%B8%80%E4%BA%BA%E4%B8%80%E5%8F%A5%E5%AE%8B%E8%AF%8D.html</guid>
      <source url="https://oragekk.me/rss.xml">一人一句宋词</source>
      <description>Bing 每日壁纸Bing 每日壁纸 写来留待日后慢慢欣赏 😜 苏轼 十年生死两茫茫， 不思量，自难忘。 陆游 红酥手，黄藤酒， 满城春色宫墙柳。 李清照 此情无计可消除， 才下眉头，却上心头。 辛弃疾 众里寻他千百度。 蓦然回首，那人却在，灯火阑珊处。 柳永 多情自古伤离别， 更那堪冷落清秋节！ 范仲淹 羌管悠悠霜满地， 人不寐，将军白发征夫泪。 ...</description>
      <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>素材设计</title>
      <link>https://oragekk.me/site/design.html</link>
      <guid>https://oragekk.me/site/design.html</guid>
      <source url="https://oragekk.me/rss.xml">素材设计</source>
      <description>相关信息 素材、设计相关好用的网站 工具类</description>
      <category>收藏</category>
      <pubDate>Tue, 11 Apr 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p>素材、设计相关好用的网站</p>
</div>
<h2>工具类</h2>
]]></content:encoded>
    </item>
    <item>
      <title>公开API</title>
      <link>https://oragekk.me/site/public-api.html</link>
      <guid>https://oragekk.me/site/public-api.html</guid>
      <source url="https://oragekk.me/rss.xml">公开API</source>
      <description>相关信息 免费公开的API网站，提供多种公共接口服务</description>
      <category>收藏</category>
      <pubDate>Tue, 28 Mar 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p>免费公开的API网站，提供多种公共接口服务</p>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>谷歌发布多平台应用开发神器Project IDX！PaLM 2加持</title>
      <link>https://oragekk.me/tutorial/idx-dev.html</link>
      <guid>https://oragekk.me/tutorial/idx-dev.html</guid>
      <source url="https://oragekk.me/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 class="header-anchor" href="#codegeex-智能编程助手"><span>- </span></a><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 class="header-anchor" href="#aws-codewhisperer"><span>- </span></a><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 class="header-anchor" href="#poe"><span>- </span></a><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>终端究极美化iTerm2+Pure</title>
      <link>https://oragekk.me/posts/Linux/iterm2-pure.html</link>
      <guid>https://oragekk.me/posts/Linux/iterm2-pure.html</guid>
      <source url="https://oragekk.me/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-ext="bash" data-title="bash"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"># .zshrc</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">fpath</span><span style="color:#C678DD;--shiki-dark:#C678DD">+=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">$HOME</span><span style="color:#98C379;--shiki-dark:#98C379">/.zsh/pure</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span></code></pre>
</div><p>将以下内容复制到 <code>.zshrc</code>文件中</p>
<div class="language-bash" data-ext="bash" data-title="bash"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">autoload</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -U</span><span style="color:#98C379;--shiki-dark:#98C379"> promptinit</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">promptinit</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">prompt</span><span style="color:#98C379;--shiki-dark:#98C379"> pure</span></span></code></pre>
</div><p>在 .zshrc 中设置 ZSH_THEME="" 以禁用 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-ext="bash" data-title="bash"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">#首先在/Users目录下按照如下命令创建sh脚本</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">cd</span><span style="color:#98C379;--shiki-dark:#98C379"> /Users/</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">#创建iterm文件夹</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">mkdir</span><span style="color:#98C379;--shiki-dark:#98C379"> iterm</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> </span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">#进入iterm文件夹</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">cd</span><span style="color:#98C379;--shiki-dark:#98C379"> iterm</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">#创建myserver.sh文件</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">touch</span><span style="color:#98C379;--shiki-dark:#98C379"> myserver.sh</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">#编辑myserver.sh文件</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">vi</span><span style="color:#98C379;--shiki-dark:#98C379"> myserver.sh</span></span></code></pre>
</div><p>如果出现没有权限，就命令前面加上sudo</p>
<p>键盘输入i编辑文件，插入以下内容：</p>
<div class="language-bash" data-ext="bash" data-title="bash"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">#!/usr/bin/expect</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">set</span><span style="color:#98C379;--shiki-dark:#98C379"> timeout</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 30</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">spawn</span><span style="color:#98C379;--shiki-dark:#98C379"> ssh</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [lindex </span><span style="color:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="color:#98C379;--shiki-dark:#98C379"> 0]</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [lindex </span><span style="color:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="color:#98C379;--shiki-dark:#98C379"> 1]@[lindex</span><span style="color:#E06C75;--shiki-dark:#E06C75"> $argv</span><span style="color:#98C379;--shiki-dark:#98C379"> 2]</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">expect</span><span style="color:#98C379;--shiki-dark:#98C379"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">        "(yes/no)?"</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        {</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">send</span><span style="color:#98C379;--shiki-dark:#98C379"> "yes\n"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">exp_continue}</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">        "password:"</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        {</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">send</span><span style="color:#98C379;--shiki-dark:#98C379"> "[lindex </span><span style="color:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="color:#98C379;--shiki-dark:#98C379"> 3]\n"}</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">interact</span></span></code></pre>
</div><p>myserver.sh文件中变量解释：</p>
<div class="language-bash" data-ext="bash" data-title="bash"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[lindex </span><span style="color:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> 0]：端口号</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[lindex </span><span style="color:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> 1]：服务器用户名</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[lindex </span><span style="color:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> 2]：服务器IP地址</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[lindex </span><span style="color:#E06C75;--shiki-dark:#E06C75">$argv</span><span style="color:#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://oragekk.me/posts/Linux/unix-linux-note.html</link>
      <guid>https://oragekk.me/posts/Linux/unix-linux-note.html</guid>
      <source url="https://oragekk.me/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://oragekk.me/posts/Linux/zsh.html</link>
      <guid>https://oragekk.me/posts/Linux/zsh.html</guid>
      <source url="https://oragekk.me/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>使用Bing API提交网站URL</title>
      <link>https://oragekk.me/posts/Python/submit-bing.html</link>
      <guid>https://oragekk.me/posts/Python/submit-bing.html</guid>
      <source url="https://oragekk.me/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密钥。还需要将"<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>"替换为自己的站点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>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/24/6fb703876007f6b6.png" alt="API密钥生成" width="500" tabindex="0" loading="lazy"><figcaption>API密钥生成</figcaption></figure>
]]></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://oragekk.me/posts/Python/submit-url.html</link>
      <guid>https://oragekk.me/posts/Python/submit-url.html</guid>
      <source url="https://oragekk.me/rss.xml">提交URL到搜索引擎（百度、Bing、Google）</source>
      <description>相关信息 这是一个利用GitHub Actions自动触发的工作流进行解析更新的url并推送到搜索引擎的python脚本,除了之前介绍过的bing api之外，还增加了百度和Google的相关内容 关于GitHub Actions的介绍可以看这里👉GitHub Actions 关于GitHub Actions的配置可以看这里👉如何利用GitHub ...</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>关于<code>GitHub Actions</code>的配置可以看这里👉<a href="/blog/auto-push">如何利用GitHub Actions推送URL到搜索引擎</a></p>
</div>
<!-- more -->
<h2>流程图</h2>
]]></content:encoded>
      <enclosure url="https://files.codelife.cc/wallhaven/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>初识Rust</title>
      <link>https://oragekk.me/posts/Rust/first-time.html</link>
      <guid>https://oragekk.me/posts/Rust/first-time.html</guid>
      <source url="https://oragekk.me/rss.xml">初识Rust</source>
      <description>Rust 发展历程 Rust 最早是 Mozilla 雇员 Graydon Hoare 的个人项目。从 2009 年开始，得到了 Mozilla 研究院的资助，2010 年项目对外公布，2010 ～ 2011 年间实现自举。自此以后，Rust 在部分重构 -&amp;gt; 崩溃的边缘反复横跳（历程极其艰辛），终于，在 2015 年 5 月 15 日发布 1.0 版...</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 class="header-anchor" href="#为何又来一门新语言"><span></span></a><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 class="header-anchor" href="#缓解内卷"><span></span></a><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 class="header-anchor" href="#运行效率"><span></span></a><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 class="header-anchor" href="#开发效率"><span></span></a><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 class="header-anchor" href="#开源"><span></span></a><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 class="header-anchor" href="#相比其他语言-rust-的优势"><span></span></a><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 class="header-anchor" href="#go"><span></span></a><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 class="header-anchor" href="#c"><span></span></a><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 class="header-anchor" href="#java"><span></span></a><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 class="header-anchor" href="#python"><span></span></a><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 class="header-anchor" href="#使用现状"><span></span></a><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 class="header-anchor" href="#rust-语言版本更新"><span></span></a><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://oragekk.me/posts/Web/Q_A.html</link>
      <guid>https://oragekk.me/posts/Web/Q_A.html</guid>
      <source url="https://oragekk.me/rss.xml">前端-Q&amp;A</source>
      <description>浏览器是如何渲染页面的？ 当浏览器的网络线程收到 HTML 文档后，会产生一个渲染任务，并将其传递给渲染主线程的消息队列。 在事件循环机制的作用下，渲染主线程取出消息队列中的渲染任务，开启渲染流程。</description>
      <category>浏览器</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>Jenkins 远程触发构建踩坑记</title>
      <link>https://oragekk.me/tutorial/CI_CD/Jenkins.html</link>
      <guid>https://oragekk.me/tutorial/CI_CD/Jenkins.html</guid>
      <source url="https://oragekk.me/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>在顶部导航栏中，点击您的用户名，然后选择 "Configure" 选项。</p>
</li>
<li>
<p>在配置页面中，向下滚动，找到 "API Token" 部分。</p>
</li>
<li>
<p>如果您之前没有生成过 API Token，则点击 "Add new Token" 或 "Generate Token" 按钮。</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-ext="yaml" data-title="yaml"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">stages</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  - </span><span style="color:#98C379;--shiki-dark:#98C379">deploy</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">deploy_to_hyjk_open-x</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  stage</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">deploy</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  only</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    - </span><span style="color:#98C379;--shiki-dark:#98C379">master</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    - </span><span style="color:#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-ext="sh" data-title="sh"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">#!/bin/bash</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"># Jenkins 服务器信息</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">JENKINS_URL</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379">"http://192.168.155.57:8080/"</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">JENKINS_USERNAME</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379">"admin"</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">JENKINS_API_TOKEN</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379">"11abf441f4db6e341ae65b660f74de6e02"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"># 获取 CSRF crumb 值</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">CRUMB</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">$(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">curl</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -s</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -u</span><span style="color:#98C379;--shiki-dark:#98C379"> "</span><span style="color:#E06C75;--shiki-dark:#E06C75">$JENKINS_USERNAME</span><span style="color:#98C379;--shiki-dark:#98C379">:</span><span style="color:#E06C75;--shiki-dark:#E06C75">$JENKINS_API_TOKEN</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#98C379;--shiki-dark:#98C379"> "</span><span style="color:#E06C75;--shiki-dark:#E06C75">$JENKINS_URL</span><span style="color:#98C379;--shiki-dark:#98C379">/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">\"</span><span style="color:#98C379;--shiki-dark:#98C379">:</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">\"</span><span style="color:#98C379;--shiki-dark:#98C379">,//crumb)"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"># 提取 crumb 值</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">CRUMB_VALUE</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">$(</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">echo</span><span style="color:#98C379;--shiki-dark:#98C379"> "</span><span style="color:#E06C75;--shiki-dark:#E06C75">$CRUMB</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> | </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">awk</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -F</span><span style="color:#98C379;--shiki-dark:#98C379">':'</span><span style="color:#98C379;--shiki-dark:#98C379"> '{print $2}'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"># 执行 Jenkins 远程构建</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">curl</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -X</span><span style="color:#98C379;--shiki-dark:#98C379"> POST</span><span style="color:#98C379;--shiki-dark:#98C379"> "</span><span style="color:#E06C75;--shiki-dark:#E06C75">$JENKINS_URL</span><span style="color:#98C379;--shiki-dark:#98C379">/job/projectName/build?token=xZ7Y3n9A"</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> \</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">  --user</span><span style="color:#98C379;--shiki-dark:#98C379"> "</span><span style="color:#E06C75;--shiki-dark:#E06C75">$JENKINS_USERNAME</span><span style="color:#98C379;--shiki-dark:#98C379">:</span><span style="color:#E06C75;--shiki-dark:#E06C75">$JENKINS_API_TOKEN</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> \</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">  --header</span><span style="color:#98C379;--shiki-dark:#98C379"> "Jenkins-Crumb: </span><span style="color:#E06C75;--shiki-dark:#E06C75">$CRUMB_VALUE</span><span style="color:#98C379;--shiki-dark:#98C379">"</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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://oragekk.me/tutorial/CI_CD/vercel-deploy.html</link>
      <guid>https://oragekk.me/tutorial/CI_CD/vercel-deploy.html</guid>
      <source url="https://oragekk.me/rss.xml">Vercel deploy忽略指定分支</source>
      <description>Vercel Ignored Build Step使用</description>
      <category>Vercel</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>vuepress-plugin-meting2</title>
      <link>https://oragekk.me/tutorial/OSS/meting2.html</link>
      <guid>https://oragekk.me/tutorial/OSS/meting2.html</guid>
      <source url="https://oragekk.me/rss.xml">vuepress-plugin-meting2</source>
      <description>支持vuepress2.x的音乐播放器</description>
      <category>开源软件</category>
      <category>GitHub</category>
      <pubDate>Wed, 11 Oct 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<h2>前言</h2>
<p>嘿~<br>
🍰🍰🍰 播放器有了，撒花✿✿ヽ(°▽°)ノ✿🎉🎉🎉</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-ext="javascript" data-title="javascript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">plugins</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: [</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    metingPlugin</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">({</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      metingOptions</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        global</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#D19A66;--shiki-dark:#D19A66">true</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 开启关闭全局播放器</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        server</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"tencent"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        api</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"https://api.injahow.cn/meting/?server=:server&amp;type=:type&amp;id=:id&amp;auth=:auth&amp;r=:r"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        type</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"playlist"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        mid</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"851947617"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }),</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span></code></pre>
</div><h3>可作为组件引入</h3>
]]></content:encoded>
    </item>
    <item>
      <title>GitHub Actions 使用介绍</title>
      <link>https://oragekk.me/tutorial/github/github-action.html</link>
      <guid>https://oragekk.me/tutorial/github/github-action.html</guid>
      <source url="https://oragekk.me/rss.xml">GitHub Actions 使用介绍</source>
      <description>GitHub Actions 是什么？ Github Actions 是 Github 官方出的持续集成服务, 挺早之前就推出了。类似的还有如微软的DevOps、GitLab CI、Circle CI、Travis CI等等。大家知道，持续集成由很多操作组成，比如抓取代码、运行测试、登录远程服务器，发布到第三方服务等等。GitHub 把这些操作就称为 ...</description>
      <category>GitHub</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://files.codelife.cc/wallhaven/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://oragekk.me/tutorial/github/ssh-key.html</link>
      <guid>https://oragekk.me/tutorial/github/ssh-key.html</guid>
      <source url="https://oragekk.me/rss.xml">Git SSH 密钥配置</source>
      <description>在 GitLab 中配置 SSH 密钥 生成 SSH 密钥对 如果您还没有 SSH 密钥对，请首先生成一对密钥。在终端中执行以下命令： 这将生成一个 RSA 类型的密钥对，并将私钥保存在 ~/.ssh/id_rsa，公钥保存在 ~/.ssh/id_rsa.pub。 复制公钥内容 打开公钥文件 ~/.ssh/id_rsa.pub，将其中的内容复制到剪贴板...</description>
      <category>Git</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-ext="sh" data-title="sh"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">ssh-keygen</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -t</span><span style="color:#98C379;--shiki-dark:#98C379"> rsa</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -C</span><span style="color:#98C379;--shiki-dark:#98C379"> "your.email@example.com"</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -b</span><span style="color:#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-ext="sh" data-title="sh"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">pbcopy</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> &lt; </span><span style="color:#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-ext="sh" data-title="sh"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">xclip</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -sel</span><span style="color:#98C379;--shiki-dark:#98C379"> clip</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> &lt; </span><span style="color:#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-ext="sh" data-title="sh"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">type</span><span style="color:#98C379;--shiki-dark:#98C379"> %userprofile%</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">\.</span><span style="color:#98C379;--shiki-dark:#98C379">ssh</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">\i</span><span style="color:#98C379;--shiki-dark:#98C379">d_rsa.pub</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> | </span><span style="color:#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-ext="sh" data-title="sh"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">cat</span><span style="color:#98C379;--shiki-dark:#98C379"> ~/.ssh/id_rsa.pub</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> | </span><span style="color:#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>在 "SSH Keys" 页面，粘贴之前复制的公钥内容到 "Key" 字段中，并为该密钥添加一个可识别的标题（如 "My SSH Key"）。然后点击 "Add Key" 按钮。</p>
</li>
</ol>
<h3>验证 SSH 密钥</h3>
<p>GitLab 将验证并添加您提供的 SSH 密钥。如果一切顺利，您将在页面上看到成功的消息。</p>
<h3>测试 SSH 连接</h3>
<p>为了确保 SSH 密钥配置正确，您可以在终端中执行以下命令进行测试：</p>
<div class="language-bash" data-ext="bash" data-title="bash"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">ssh</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -T</span><span style="color:#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-ext="sh" data-title="sh"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">ssh-add</span><span style="color:#D19A66;--shiki-dark:#D19A66"> --apple-use-keychain</span><span style="color:#98C379;--shiki-dark:#98C379"> ~/.ssh/id_rsa</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> &amp;&gt; /dev/null</span></span></code></pre>
</div><p>该命令的作用是将指定的 SSH 私钥文件 id_rsa 添加到 Apple Keychain 中，并将输出静默地丢弃，以确保在终端上不会显示任何输出信息。这通常用于在登录时自动加载 SSH 密钥，并将其保存在钥匙串中，以便在需要时无需再次手动输入密码或密钥口令。</p>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>浏览器的事件循环</title>
      <link>https://oragekk.me/posts/Web/Browser/event-loop.html</link>
      <guid>https://oragekk.me/posts/Web/Browser/event-loop.html</guid>
      <source url="https://oragekk.me/rss.xml">浏览器的事件循环</source>
      <description>浏览器的进程模型 何为进程？ 程序运行需要有它自己专属的内存空间，可以把这块内存空间简单的理解为进程</description>
      <category>浏览器</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-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;Mr.Yuan is awesome!&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">button</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;change&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">button</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> h1</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> document</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'h1'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> btn</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> document</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'button'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // 死循环指定的时间</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> delay</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">duration</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> start</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Date</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">now</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    while</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">Date</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">now</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">-</span><span style="color:#E06C75;--shiki-dark:#E06C75"> start</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75"> duration</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {}</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  btn</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">onclick</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#C678DD;--shiki-dark:#C678DD"> function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> '袁老师很帅！'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    delay</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">3000</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  };</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 立即把一个函数添加到微队列</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">Promise</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">resolve</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">().</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">then</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">函数</span><span style="color:#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>CSS 属性计算过程</title>
      <link>https://oragekk.me/posts/Web/CSS/calculation.html</link>
      <guid>https://oragekk.me/posts/Web/CSS/calculation.html</guid>
      <source url="https://oragekk.me/rss.xml">CSS 属性计算过程</source>
      <description>CSS 属性计算过程 你是否了解 CSS 的属性计算过程呢？ 有的同学可能会讲，CSS属性我倒是知道，例如： 上面的 CSS 代码中，p 是元素选择器，color 就是其中的一个 CSS 属性。 但是要说 CSS 属性的计算过程，还真的不是很清楚。 没关系，通过此篇文章，能够让你彻底明白什么是 CSS 属性的计算流程。 image-2022081314...</description>
      <category>CSS</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-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  color : </span><span style="color:#D19A66;--shiki-dark:#D19A66">red</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#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-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">body</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;这是一个h1标题&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">body</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</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>答案是**该元素上面会有 CSS 所有的属性。**你可以打开浏览器的开发者面板，选择【元素】，切换到【计算样式】，之后勾选【全部显示】，此时你就能看到在此 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-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  color : </span><span style="color:#D19A66;--shiki-dark:#D19A66">red</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#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-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  color : </span><span style="color:#D19A66;--shiki-dark:#D19A66">red</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#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-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  color : </span><span style="color:#D19A66;--shiki-dark:#D19A66">red</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  display: </span><span style="color:#D19A66;--shiki-dark:#D19A66">inline-block</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#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-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"test"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;test&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
</div><div class="language-css" data-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">.test</span><span style="color:#E06C75;--shiki-dark:#E06C75"> h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  font-size: </span><span style="color:#D19A66;--shiki-dark:#D19A66">50</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  font-size: </span><span style="color:#D19A66;--shiki-dark:#D19A66">20</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#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-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  font-size: </span><span style="color:#D19A66;--shiki-dark:#D19A66">50</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">h1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  font-size: </span><span style="color:#D19A66;--shiki-dark:#D19A66">20</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#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-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;Lorem ipsum dolor sit amet.&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
</div><div class="language-css" data-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  color: </span><span style="color:#D19A66;--shiki-dark:#D19A66">red</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#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-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"test"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;Lorem ipsum dolor sit amet.&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
</div><div class="language-css" data-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  color: </span><span style="color:#D19A66;--shiki-dark:#D19A66">red</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">.test</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  color: </span><span style="color:#D19A66;--shiki-dark:#D19A66">blue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#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-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#D19A66;--shiki-dark:#D19A66"> href</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">""</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;test&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;test&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
</div><div class="language-css" data-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  color: </span><span style="color:#D19A66;--shiki-dark:#D19A66">red</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#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://oragekk.me/posts/Web/CSS/containing-block.html</link>
      <guid>https://oragekk.me/posts/Web/CSS/containing-block.html</guid>
      <source url="https://oragekk.me/rss.xml">你不知道的 CSS 之包含块</source>
      <description>你不知道的 CSS 之包含块 一说到 CSS 盒模型，这是很多小伙伴耳熟能详的知识，甚至有的小伙伴还能说出 border-box 和 content-box 这两种盒模型的区别。 但是一说到 CSS 包含块，有的小伙伴就懵圈了，什么是包含块？好像从来没有听说过这玩意儿。 image-20220814222004395 好吧，如果你对包含块的知识一无所知...</description>
      <category>CSS</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-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">body</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"container"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"item"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">body</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
</div><div class="language-css" data-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">.container</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="color:#D19A66;--shiki-dark:#D19A66">500</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="color:#D19A66;--shiki-dark:#D19A66">300</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: skyblue;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">.item</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="color:#D19A66;--shiki-dark:#D19A66">50</span><span style="color:#E06C75;--shiki-dark:#E06C75">%</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="color:#D19A66;--shiki-dark:#D19A66">50</span><span style="color:#E06C75;--shiki-dark:#E06C75">%</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: </span><span style="color:#D19A66;--shiki-dark:#D19A66">red</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#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-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">body</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"container"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"item"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"item2"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">body</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
</div><div class="language-css line-numbers-mode" data-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">.container</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="color:#D19A66;--shiki-dark:#D19A66">500</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="color:#D19A66;--shiki-dark:#D19A66">300</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: skyblue;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  position: </span><span style="color:#D19A66;--shiki-dark:#D19A66">relative</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">.item</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="color:#D19A66;--shiki-dark:#D19A66">300</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="color:#D19A66;--shiki-dark:#D19A66">150</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  border: </span><span style="color:#D19A66;--shiki-dark:#D19A66">5</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#D19A66;--shiki-dark:#D19A66"> solid</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  margin-left: </span><span style="color:#D19A66;--shiki-dark:#D19A66">100</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">.item2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="color:#D19A66;--shiki-dark:#D19A66">100</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="color:#D19A66;--shiki-dark:#D19A66">100</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: </span><span style="color:#D19A66;--shiki-dark:#D19A66">red</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  position: </span><span style="color:#D19A66;--shiki-dark:#D19A66">absolute</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  left: </span><span style="color:#D19A66;--shiki-dark:#D19A66">10</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  top: </span><span style="color:#D19A66;--shiki-dark:#D19A66">10</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">body</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"container"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"item"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"item2"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">body</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
</div><div class="language-css line-numbers-mode" data-ext="css" data-title="css"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">.container</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="color:#D19A66;--shiki-dark:#D19A66">500</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="color:#D19A66;--shiki-dark:#D19A66">300</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: skyblue;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  position: </span><span style="color:#D19A66;--shiki-dark:#D19A66">relative</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">.item</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="color:#D19A66;--shiki-dark:#D19A66">300</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="color:#D19A66;--shiki-dark:#D19A66">150</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  border: </span><span style="color:#D19A66;--shiki-dark:#D19A66">5</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#D19A66;--shiki-dark:#D19A66"> solid</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  margin-left: </span><span style="color:#D19A66;--shiki-dark:#D19A66">100</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  transform: </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">rotate</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#E06C75;--shiki-dark:#E06C75">deg</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">); </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/* 新增代码 */</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">.item2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  width: </span><span style="color:#D19A66;--shiki-dark:#D19A66">100</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  height: </span><span style="color:#D19A66;--shiki-dark:#D19A66">100</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  background-color: </span><span style="color:#D19A66;--shiki-dark:#D19A66">red</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  position: </span><span style="color:#D19A66;--shiki-dark:#D19A66">absolute</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  left: </span><span style="color:#D19A66;--shiki-dark:#D19A66">10</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  top: </span><span style="color:#D19A66;--shiki-dark:#D19A66">10</span><span style="color:#E06C75;--shiki-dark:#E06C75">px</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="html" data-title="html"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">html</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">head</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">title</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;Illustration of containing blocks&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">title</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">head</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">body</span><span style="color:#D19A66;--shiki-dark:#D19A66"> id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"body"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"div1"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#D19A66;--shiki-dark:#D19A66"> id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"p1"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;This is text in the first paragraph...&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#D19A66;--shiki-dark:#D19A66"> id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"p2"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        This is text</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">em</span><span style="color:#D19A66;--shiki-dark:#D19A66"> id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"em1"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">          in the</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">          &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">strong</span><span style="color:#D19A66;--shiki-dark:#D19A66"> id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"strong1"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;second&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">strong</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">          paragraph.</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">em</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">body</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">html</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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://oragekk.me/posts/Web/JavaScript/js-module-loader.html</link>
      <guid>https://oragekk.me/posts/Web/JavaScript/js-module-loader.html</guid>
      <source url="https://oragekk.me/rss.xml">CommonJS，RequireJS，SeaJS 归纳笔记</source>
      <description>Foreword Here comes Module! 随着网站逐渐变成「互联网应用程序」，嵌入网页的 JavaScript 代码越来越庞大，越来越复杂。网页越来越像桌面程序，需要一个团队分工协作、进度管理、单元测试……我们不得不使用软件工程的方法，来管理网页的业务逻辑。 于是，JavaScript 的模块化成为迫切需求。在 ES6 Module 来临...</description>
      <category>JavaScript</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://oragekk.me/posts/Web/JavaScript/js-version.html</link>
      <guid>https://oragekk.me/posts/Web/JavaScript/js-version.html</guid>
      <source url="https://oragekk.me/rss.xml">JavaScript ES6</source>
      <description>JavaScript 有着很奇怪的命名史。 1995 年，它作为网景浏览器（Netscape Navigator）的一部分首次发布，网景给这个新语言命名为 LiveScript。一年后，为了搭上当时媒体热炒 Java 的顺风车，临时改名为了 JavaScript （当然，Java 和 JavaScript 的关系，就和雷锋和雷锋塔一样 —— 并没有什么...</description>
      <category>JavaScript</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://oragekk.me/posts/Web/JavaScript/judgment.html</link>
      <guid>https://oragekk.me/posts/Web/JavaScript/judgment.html</guid>
      <source url="https://oragekk.me/rss.xml">通过UserAgent判断设备</source>
      <description>通过 js 判断 moblie 端和 pc 端进而加载不同的 css 或者 js 废话不多说，上代码 需要注意的是字符转义和引号嵌套的问题</description>
      <category>JavaScript</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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#D19A66;--shiki-dark:#D19A66;font-style:italic;--shiki-dark-font-style:italic"> type</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379">"text/javascript"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">function browserRedirect() </span><span style="color:#C678DD;--shiki-dark:#C678DD">{</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> sUserAgent</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> navigator</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">userAgent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">toLowerCase</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsIpad</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">/ipad/</span><span style="color:#C678DD;--shiki-dark:#C678DD">i</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#98C379;--shiki-dark:#98C379"> "ipad"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsIphoneOs</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">/iphone os/</span><span style="color:#C678DD;--shiki-dark:#C678DD">i</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#98C379;--shiki-dark:#98C379"> "iphone os"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsMidp</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">/midp/</span><span style="color:#C678DD;--shiki-dark:#C678DD">i</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#98C379;--shiki-dark:#98C379"> "midp"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsUc7</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">/rv:1.2.3.4/</span><span style="color:#C678DD;--shiki-dark:#C678DD">i</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#98C379;--shiki-dark:#98C379"> "rv:1.2.3.4"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsUc</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">/ucweb/</span><span style="color:#C678DD;--shiki-dark:#C678DD">i</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#98C379;--shiki-dark:#98C379"> "ucweb"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsAndroid</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">/android/</span><span style="color:#C678DD;--shiki-dark:#C678DD">i</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#98C379;--shiki-dark:#98C379"> "android"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsCE</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">/windows ce/</span><span style="color:#C678DD;--shiki-dark:#C678DD">i</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#98C379;--shiki-dark:#98C379"> "windows ce"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsWM</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> sUserAgent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">match</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">/windows mobile/</span><span style="color:#C678DD;--shiki-dark:#C678DD">i</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#98C379;--shiki-dark:#98C379"> "windows mobile"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// document.writeln("您的浏览设备为：");</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75">bIsIpad</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsIphoneOs</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsMidp</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsUc7</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsUc</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsAndroid</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsCE</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="color:#E06C75;--shiki-dark:#E06C75"> bIsWM</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// alert("手机浏览！");</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">} </span><span style="color:#E06C75;--shiki-dark:#E06C75">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// alert("PC浏览！");</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">document.write(</span><span style="color:#98C379;--shiki-dark:#98C379">"&lt;script type='text/javascript' size='150' alpha='0.8' zIndex='-10' src='../js/dist/ribbon.js'&gt;&lt;</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">\/</span><span style="color:#98C379;--shiki-dark:#98C379">script&gt;"</span><span style="color:#E06C75;--shiki-dark:#E06C75">);</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">document.write(</span><span style="color:#98C379;--shiki-dark:#98C379">"&lt;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'&gt;&lt;</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">\/</span><span style="color:#98C379;--shiki-dark:#98C379">script&gt;"</span><span style="color:#E06C75;--shiki-dark:#E06C75">);</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">	}</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">}</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">browserRedirect();</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">&lt;/script&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>Vue常见优化手段</title>
      <link>https://oragekk.me/posts/Web/Vue/optimization.html</link>
      <guid>https://oragekk.me/posts/Web/Vue/optimization.html</guid>
      <source url="https://oragekk.me/rss.xml">Vue常见优化手段</source>
      <description>相关信息 永远不要过早优化，优化也有相应的代价 开发时间变长 开发成本增加 代码难以阅读 增加维护成本 何时优化，因地制宜，是一门艺术，尽量把优化的思想带入写代码的过程中 本文章的优化手段基于vue2</description>
      <category>Vue</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 树并展示到页面中，最后将这些静态标记"激活"为客户端上完全可交互的应用程序。</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 应用程序也可以被认为是"同构"或"通用"，因为应用程序的大部分代码都可以在服务器和客户端上运行</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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> obj</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#E06C75;--shiki-dark:#E06C75">b</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 冻结对象</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">Object</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">freeze</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 尝试更改</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 打印</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#E06C75;--shiki-dark:#E06C75">b</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 验证</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">Object</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">isFrozen</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 结果</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">&lt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">Vue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">component</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'my-component'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  functional</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">true</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // Props 是可选的</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // ...</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // 为了弥补缺少的实例</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // 提供第二个参数作为上下文</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  render</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">createElement</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">context</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // ...</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#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-ext="vue" data-title="vue"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">template</span><span style="color:#D19A66;--shiki-dark:#D19A66"> functional</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">template</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> EmptyList</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/* ... */</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> TableList</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/* ... */</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> OrderedList</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/* ... */</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> UnorderedList</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/* ... */</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">Vue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">component</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'smart-list'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  functional</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">true</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    items</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      type</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E06C75;--shiki-dark:#E06C75">Array</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      required</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">true</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    isOrdered</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E06C75;--shiki-dark:#E06C75">Boolean</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  render</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">createElement</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">context</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> appropriateListComponent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> items</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> context</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">items</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">items</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)           </span><span style="color:#C678DD;--shiki-dark:#C678DD">return</span><span style="color:#E06C75;--shiki-dark:#E06C75"> EmptyList</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#C678DD;--shiki-dark:#C678DD">typeof</span><span style="color:#E06C75;--shiki-dark:#E06C75"> items</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">===</span><span style="color:#98C379;--shiki-dark:#98C379"> 'object'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#C678DD;--shiki-dark:#C678DD">return</span><span style="color:#E06C75;--shiki-dark:#E06C75"> TableList</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">context</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">isOrdered</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)      </span><span style="color:#C678DD;--shiki-dark:#C678DD">return</span><span style="color:#E06C75;--shiki-dark:#E06C75"> OrderedList</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      return</span><span style="color:#E06C75;--shiki-dark:#E06C75"> UnorderedList</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> createElement</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">      appropriateListComponent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(),</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      context</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      context</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">children</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    )</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">})</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> hasChanged</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">x</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">y</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75">x</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="color:#E06C75;--shiki-dark:#E06C75"> y</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#E06C75;--shiki-dark:#E06C75"> x</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &amp;&amp;</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> /</span><span style="color:#E06C75;--shiki-dark:#E06C75"> x</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> !==</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">/</span><span style="color:#E06C75;--shiki-dark:#E06C75">y</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#E06C75;--shiki-dark:#E06C75"> x</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="color:#E06C75;--shiki-dark:#E06C75"> x</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ||</span><span style="color:#E06C75;--shiki-dark:#E06C75"> y</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="color:#E06C75;--shiki-dark:#E06C75"> y</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// defer.js</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#C678DD;--shiki-dark:#C678DD"> function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">maxFrameCount</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(){</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 浏览器每重绘一次，计数</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        frameCount</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      };</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    mounted</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      const</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> refreshFrameCount</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> () </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">        requestAnimationFrame</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(() </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        	this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">frameCount</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">++</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">          if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">frameCount</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75"> maxFrameCount</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">            refreshFrameCount</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">          }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        });</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      };</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">      refreshFrameCount</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    methods</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">      defer</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">showInFrameCount</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 用于v-if 的判断条件，渲染次数大于showInFrameCount后继续下一次渲染</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        return</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">frameCount</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &gt;=</span><span style="color:#E06C75;--shiki-dark:#E06C75"> showInFrameCount</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  };</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="vue" data-title="vue"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">template</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"container"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    &lt;!--vue3.0 v-if 优先级高 vue2.x v-for优先级高--&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"block"</span><span style="color:#C678DD;--shiki-dark:#C678DD"> v-for</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#E06C75;--shiki-dark:#E06C75">n</span><span style="color:#C678DD;--shiki-dark:#C678DD"> in</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 20</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#C678DD;--shiki-dark:#C678DD"> v-if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">defer</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">n</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">heavy-comp</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">heavy-comp</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">template</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">import</span><span style="color:#E06C75;--shiki-dark:#E06C75"> defer</span><span style="color:#C678DD;--shiki-dark:#C678DD"> from</span><span style="color:#98C379;--shiki-dark:#98C379"> "./mixin/defer"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  mixins</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: [</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">defer</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">300</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)],</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  components</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: { </span><span style="color:#E06C75;--shiki-dark:#E06C75">HeavyComp</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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://oragekk.me/posts/Web/Vue/vue2-principle.html</link>
      <guid>https://oragekk.me/posts/Web/Vue/vue2-principle.html</guid>
      <source url="https://oragekk.me/rss.xml">Vue2响应式原理解析</source>
      <description>前言 首先要知道vue2 是2013年 基于 ES5开发出来的，我们常说的重渲染就是重新运行render函数 vue2 的响应式原理是利⽤ES5 的⼀个 API ，Object.defineProperty()对数据进⾏劫持结合发布订阅模式的⽅式来实现的。</description>
      <category>Vue</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><a class="footnote-anchor" id="footnote-ref1">的函数</a></strong><a class="footnote-anchor" id="footnote-ref1">进行重新渲染</a></p><a class="footnote-anchor" id="footnote-ref1">
<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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// euv.js</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/**</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> * 观察某个对象的所有属性</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> * </span><span style="color:#C678DD;--shiki-dark:#C678DD;font-style:italic;--shiki-dark-font-style:italic">@param</span><span style="color:#E5C07B;--shiki-dark:#E5C07B;font-style:italic;--shiki-dark-font-style:italic"> {Object}</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic"> obj</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> */</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> observe</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  for</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> key</span><span style="color:#C678DD;--shiki-dark:#C678DD"> in</span><span style="color:#E06C75;--shiki-dark:#E06C75"> obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    let</span><span style="color:#E06C75;--shiki-dark:#E06C75"> internalValue</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E06C75;--shiki-dark:#E06C75"> obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E06C75;--shiki-dark:#E06C75">key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    let</span><span style="color:#E06C75;--shiki-dark:#E06C75"> funcs</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    Object</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">defineProperty</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75">key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">      get</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        //  依赖收集，记录：是哪个函数在用我</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">__func</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &amp;&amp;</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> !</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">funcs</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">includes</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">__func</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">          funcs</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">push</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">__func</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        return</span><span style="color:#E06C75;--shiki-dark:#E06C75"> internalValue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">      set</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">val</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        internalValue</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E06C75;--shiki-dark:#E06C75"> val</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 派发更新，运行：执行用我的函数</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        for</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> i</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="color:#E06C75;--shiki-dark:#E06C75">i</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> funcs</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="color:#E06C75;--shiki-dark:#E06C75">i</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">++</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">          funcs</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E06C75;--shiki-dark:#E06C75">i</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]();</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> autorun</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">fn</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">__func</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E06C75;--shiki-dark:#E06C75"> fn</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  fn</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">__func</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> null</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// index.js</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> user</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">'有骨气'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  birth</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">'1998-4-7'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">observe</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">user</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">); </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 观察</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 显示姓氏</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> showFirstName</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  document</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'#firstName'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="color:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> '姓：'</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> +</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> user</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 显示名字</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> showLastName</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  document</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'#lastName'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="color:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> '名：'</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> +</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> user</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">slice</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 显示年龄</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> showAge</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> birthday</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#C678DD;--shiki-dark:#C678DD"> new</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> Date</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">user</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">birth</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> today</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#C678DD;--shiki-dark:#C678DD"> new</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> Date</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  today</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setHours</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">), </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">today</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setMinutes</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">), </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">today</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setMilliseconds</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  thisYearBirthday</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#C678DD;--shiki-dark:#C678DD"> new</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> Date</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    today</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">getFullYear</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(),</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    birthday</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">getMonth</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(),</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    birthday</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">getDate</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  );</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> age</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> today</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">getFullYear</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">-</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> birthday</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">getFullYear</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">today</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">getTime</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> thisYearBirthday</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">getTime</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()) {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    age</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">--</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  document</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'#age'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="color:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> '年龄：'</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> +</span><span style="color:#E06C75;--shiki-dark:#E06C75"> age</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">autorun</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">showFirstName</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">autorun</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">showLastName</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">autorun</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">showAge</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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></a><h2><a class="footnote-anchor" id="footnote-ref1"></a><a class="header-anchor" href="#_2-原理"><span>2. 原理</span></a></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-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> obj</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    b</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    c</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        d</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">3</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        e</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">4</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    f</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: [</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            b</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        },</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">        3</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">4</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">5</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">6</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    ]</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 利用Vue提供的静态方法 .observable, 将一个普通对象转化为响应式对象</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> reactiveObj</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Vue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">observable</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">reactiveObj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="vue" data-title="vue"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">template</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#D19A66;--shiki-dark:#D19A66"> class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"demo-wrapper"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;obj.a -&gt; {{ obj.a }}， obj.b -&gt; {{ obj.b }}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">p</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        &lt;!-- 非响应式式数据操作 --&gt;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        &lt;!-- </span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">            &lt;button @click="obj.b = 2"&gt;add obj.b&lt;/button&gt;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">            &lt;button @click="delete obj.a"&gt;delete obj.a&lt;/button&gt;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        --&gt;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        &lt;!-- 响应式数据操作 --&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">button</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> @</span><span style="color:#D19A66;--shiki-dark:#D19A66">click</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">$set</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#98C379;--shiki-dark:#98C379">'b'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;add obj.b&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">button</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        &amp;nbsp;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">button</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> @</span><span style="color:#D19A66;--shiki-dark:#D19A66">click</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">$delete</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#98C379;--shiki-dark:#98C379">'a'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;delete obj.a&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">button</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">div</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">template</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        };</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="vue" data-title="vue"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: [</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">3</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">4</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">],</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        };</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    created</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">lesson4</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    mounted</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 8</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 不是响应式的</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//不是响应式的</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="vue" data-title="vue"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: [</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">3</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">4</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">],</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        };</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    created</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">lesson4</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    mounted</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">				// 操作一：通过索引修改数组项</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">$set</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">8</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">); </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 响应式的</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 或</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        Vue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">set</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">8</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);  </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 响应式的</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 或</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">splice</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">8</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//响应式的</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      </span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 操作二：修改数组长度为2</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">splice</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 响应式的</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="vue" data-title="vue"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: [</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#D19A66;--shiki-dark:#D19A66">3</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#D19A66;--shiki-dark:#D19A66">4</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    mounted</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">_proto_</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Array</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">prototype</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)  </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// =&gt; false</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">arr</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">_proto_</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">_proto_</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ===</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Array</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">prototype</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)  </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//=&gt; true</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</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被多次触发，只会被推入到队列中一次，这样可以**「避免不必要的计算和DOM更操作」**。<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-ext="vue" data-title="vue"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            b</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            c</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">3</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            d</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">4</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        };</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    methods</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">        changeAllData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">            this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">$nextTick</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">                var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> pre</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> document</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"pre"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">pre</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">            this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">b</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">c</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">d</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 10</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">            this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">$nextTick</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">                var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> pre</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> document</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">querySelector</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"pre"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">pre</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            });</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    render</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">h</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'render function'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        return</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> h</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'div'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, [</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">            h</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'pre'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#98C379;--shiki-dark:#98C379">`</span><span style="color:#C678DD;--shiki-dark:#C678DD">${</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#C678DD;--shiki-dark:#C678DD">}</span><span style="color:#98C379;--shiki-dark:#98C379">, </span><span style="color:#C678DD;--shiki-dark:#C678DD">${</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">b</span><span style="color:#C678DD;--shiki-dark:#C678DD">}</span><span style="color:#98C379;--shiki-dark:#98C379">, </span><span style="color:#C678DD;--shiki-dark:#C678DD">${</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">c</span><span style="color:#C678DD;--shiki-dark:#C678DD">}</span><span style="color:#98C379;--shiki-dark:#98C379">, </span><span style="color:#C678DD;--shiki-dark:#C678DD">${</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">d</span><span style="color:#C678DD;--shiki-dark:#C678DD">}</span><span style="color:#98C379;--shiki-dark:#98C379">`</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">),</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">            h</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'button'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                on</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">                    click</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: () </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                        this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">changeAllData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            }, </span><span style="color:#98C379;--shiki-dark:#98C379">'change all data'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        ])</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="vue" data-title="vue"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">template</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">span</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;{{a}}&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">span</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">template</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">	export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">'hello'</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    mounted</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> 'world'</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">$el</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)  </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// -&gt; 'hello'</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">$nextTick</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">$el</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// -&gt; 'world'</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      })</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="vue" data-title="vue"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">	export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">        a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">'hello'</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // $nextTick 结合 async/await语法使用</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    async</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> mounted</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> 'world'</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">$el</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)  </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// -&gt; 'hello'</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      await</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">$nextTick</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">$el</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">textContent</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// -&gt; 'world'</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">script</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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://files.codelife.cc/wallhaven/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://oragekk.me/posts/Web/Vue/waline-bug.html</link>
      <guid>https://oragekk.me/posts/Web/Vue/waline-bug.html</guid>
      <source url="https://oragekk.me/rss.xml">一个 waline 评论系统bug引发的思考</source>
      <description>提示 前言： Waline 评论系统这个 bug 有几个月了，现象就是回复完其他人的评论后，评论内容会保留在顶部输入框中，而且不会自动清空。具体见 👉GitHub issuse #2173 许久未见修复，正好我有空，就看看这个问题，接下来跟我一起分析一下这个问题 1. 定位问题 问题现象上边已经描述过了，我们先来定位一下问题。 1.1. 关键 关键在...</description>
      <category>Vue</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-ext="ts" data-title="ts"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// walinejs/packages/client/src/composables/inputs.ts</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">import</span><span style="color:#C678DD;--shiki-dark:#C678DD"> type</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#E06C75;--shiki-dark:#E06C75">RemovableRef</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="color:#C678DD;--shiki-dark:#C678DD">from</span><span style="color:#98C379;--shiki-dark:#98C379"> '@vueuse/core'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">import</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#E06C75;--shiki-dark:#E06C75">useStorage</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="color:#C678DD;--shiki-dark:#C678DD">from</span><span style="color:#98C379;--shiki-dark:#98C379"> '@vueuse/core'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> interface</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> UserMeta</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    nick</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">string</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    mail</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">string</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    link</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">string</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> const</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> useUserMeta</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (): </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">RemovableRef</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">UserMeta</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt; </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">useStorage</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">UserMeta</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;(</span><span style="color:#98C379;--shiki-dark:#98C379">'WALINE_USER_META'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    nick</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">''</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    mail</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">''</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    link</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">''</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">});</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> const</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> useEditor</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (): </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">RemovableRef</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">string</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt; </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span></span>
<span class="line highlighted"><span style="color:#61AFEF;--shiki-dark:#61AFEF">useStorage</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">string</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;(</span><span style="color:#98C379;--shiki-dark:#98C379">'WALINE_COMMENT_BOX_EDITOR'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#98C379;--shiki-dark:#98C379">''</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="ts" data-title="ts"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> editor</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> useEditor</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span></code></pre>
</div><div class="language-vue" data-ext="vue" data-title="vue"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">textarea</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">  id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"wl-edit"</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">  ref</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"editorRef"</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">  v-model</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#E06C75;--shiki-dark:#E06C75">editor</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">  class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#98C379;--shiki-dark:#98C379">"wl-editor"</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  :</span><span style="color:#D19A66;--shiki-dark:#D19A66">placeholder</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#E06C75;--shiki-dark:#E06C75">replyUser</span><span style="color:#C678DD;--shiki-dark:#C678DD"> ?</span><span style="color:#98C379;--shiki-dark:#98C379"> `@</span><span style="color:#C678DD;--shiki-dark:#C678DD">${</span><span style="color:#E06C75;--shiki-dark:#E06C75">replyUser</span><span style="color:#C678DD;--shiki-dark:#C678DD">}</span><span style="color:#98C379;--shiki-dark:#98C379">`</span><span style="color:#C678DD;--shiki-dark:#C678DD"> :</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> locale</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">placeholder</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  @</span><span style="color:#D19A66;--shiki-dark:#D19A66">keydown</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#E06C75;--shiki-dark:#E06C75">onKeyDown</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  @</span><span style="color:#D19A66;--shiki-dark:#D19A66">drop</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#E06C75;--shiki-dark:#E06C75">onDrop</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  @</span><span style="color:#D19A66;--shiki-dark:#D19A66">paste</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span><span style="color:#E06C75;--shiki-dark:#E06C75">onPaste</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">"</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">/&gt;</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-ext="ts" data-title="ts"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD"> const</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> submitComment</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#C678DD;--shiki-dark:#C678DD"> async</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (): </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">Promise</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt; </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">     // 此处...省略若干</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">     try</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">         if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75">recaptchaV3Key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">         comment</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">recaptchaV3</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">             await</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> useReCaptcha</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">recaptchaV3Key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">execute</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'social'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">         if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75">turnstileKey</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">         comment</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">turnstile</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#C678DD;--shiki-dark:#C678DD"> await</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> useTurnstile</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">turnstileKey</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">execute</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'social'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">         const</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> options</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">         serverURL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">         lang</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">         token</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">userInfo</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">?.</span><span style="color:#E06C75;--shiki-dark:#E06C75">token</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">         comment</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">         };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">         const</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> response</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#C678DD;--shiki-dark:#C678DD"> await</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">edit</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">         ?</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> updateComment</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">({</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">             objectId</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">edit</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">objectId</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">             ...</span><span style="color:#E06C75;--shiki-dark:#E06C75">options</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">         })</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">         :</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> addComment</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">options</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">         isSubmitting</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> false</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">         if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">response</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">errmsg</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#C678DD;--shiki-dark:#C678DD">return</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> alert</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">response</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">errmsg</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">         emit</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'submit'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">response</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">data</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="color:#E5C07B;--shiki-dark:#E5C07B">         editor</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> ''</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">         previewText</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> ''</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">         if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">replyId</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">emit</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'cancelReply'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">         if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">edit</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">?.</span><span style="color:#E06C75;--shiki-dark:#E06C75">objectId</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">emit</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'cancelEdit'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">     } </span><span style="color:#C678DD;--shiki-dark:#C678DD">catch</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">err</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">unknown</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">         isSubmitting</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> false</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">         alert</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">((</span><span style="color:#E06C75;--shiki-dark:#E06C75">err</span><span style="color:#C678DD;--shiki-dark:#C678DD"> as</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> TypeError</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">).</span><span style="color:#E06C75;--shiki-dark:#E06C75">message</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">     }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> };</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="ts" data-title="ts"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">watch</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  () </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> editor</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  (</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    const</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">highlighter</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">texRenderer</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> config</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    content</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E06C75;--shiki-dark:#E06C75"> value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    previewText</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> parseMarkdown</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      emojiMap</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">emoji</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">map</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      highlighter</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      texRenderer</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    wordNumber</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> getWordNumber</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">autosize</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">editorRef</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    else</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> autosize</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">destroy</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">editorRef</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  {</span></span>
<span class="line highlighted"><span style="color:#E06C75;--shiki-dark:#E06C75">    immediate</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">true</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="ts" data-title="ts"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">watch</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  () </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> editor</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  (</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    const</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">highlighter</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">texRenderer</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> config</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    content</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E06C75;--shiki-dark:#E06C75"> value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    previewText</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> parseMarkdown</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      emojiMap</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">emoji</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">map</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      highlighter</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      texRenderer</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    wordNumber</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> getWordNumber</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">autosize</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">editorRef</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    else</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> autosize</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">destroy</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">editorRef</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    immediate</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">true</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    onTrack</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">e</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">      // 当 editor.value 被追踪为依赖时触发</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      debugger</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    onTrigger</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">e</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">      // 当 editor.value 被更改时触发</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      debugger</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>同步侦听器不会进行批处理，每当检测到响应式数据发生变化时就会触发。可以使用它来监视简单的布尔值，但应避免在可能多次同步修改的数据源 (如数组) 上使用。</p>
</div>
</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-ext="ts" data-title="ts"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">editor</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">value</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> ""</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span></code></pre>
</div><div class="language-ts" data-ext="ts" data-title="ts"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> editor</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> useEditor</span><span style="color:#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-ext="ts" data-title="ts"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 更换`editor.value`为</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">localStorage</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setItem</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'WALINE_COMMENT_BOX_EDITOR'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#98C379;--shiki-dark:#98C379">''</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 或</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">await</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> nextTick</span><span style="color:#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-ext="ts" data-title="ts"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> nextTick</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">callback</span><span style="color:#C678DD;--shiki-dark:#C678DD">?</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: () </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">): </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">Promise</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</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://files.codelife.cc/wallhaven/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://oragekk.me/posts/Web/node/node-version.html</link>
      <guid>https://oragekk.me/posts/Web/node/node-version.html</guid>
      <source url="https://oragekk.me/rss.xml">使用n命令管理node版本</source>
      <description>鉴于使用 ReactNative 时，会需要不同的 node 版本，提供一种版本切换方式 使用 n command 来进行 node 版本管理 概念 n Node version manager 提供了一个更简单的 CLI，用于在 Node 版本之间进行安装和切换。它仅在 Linux 或 Mac 操作系统上受到支持。 安装 如果你已经安装某个版本的 N...</description>
      <category>Linux</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-ext="shell" data-title="shell"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">curl</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -L</span><span style="color:#98C379;--shiki-dark:#98C379"> https://git.io/n-install</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> | </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">bash</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">===</span><span style="color:#98C379;--shiki-dark:#98C379"> n</span><span style="color:#98C379;--shiki-dark:#98C379"> successfully</span><span style="color:#98C379;--shiki-dark:#98C379"> installed.</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  The</span><span style="color:#98C379;--shiki-dark:#98C379"> active</span><span style="color:#98C379;--shiki-dark:#98C379"> Node.js</span><span style="color:#98C379;--shiki-dark:#98C379"> version</span><span style="color:#98C379;--shiki-dark:#98C379"> is:</span><span style="color:#98C379;--shiki-dark:#98C379"> v10.16.0</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  Run</span><span style="color:#98C379;--shiki-dark:#98C379"> `</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">n</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -h</span><span style="color:#98C379;--shiki-dark:#98C379">`</span><span style="color:#C678DD;--shiki-dark:#C678DD"> for</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> help.</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  To</span><span style="color:#98C379;--shiki-dark:#98C379"> update</span><span style="color:#98C379;--shiki-dark:#98C379"> n</span><span style="color:#98C379;--shiki-dark:#98C379"> later,</span><span style="color:#98C379;--shiki-dark:#98C379"> run</span><span style="color:#98C379;--shiki-dark:#98C379"> `</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">n-update</span><span style="color:#98C379;--shiki-dark:#98C379">`</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">.</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  To</span><span style="color:#98C379;--shiki-dark:#98C379"> uninstall,</span><span style="color:#98C379;--shiki-dark:#98C379"> run</span><span style="color:#98C379;--shiki-dark:#98C379"> `</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">n-uninstall</span><span style="color:#98C379;--shiki-dark:#98C379">`</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">.</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  IMPORTANT:</span><span style="color:#98C379;--shiki-dark:#98C379"> OPEN</span><span style="color:#98C379;--shiki-dark:#98C379"> A</span><span style="color:#98C379;--shiki-dark:#98C379"> NEW</span><span style="color:#98C379;--shiki-dark:#98C379"> TERMINAL</span><span style="color:#98C379;--shiki-dark:#98C379"> TAB/WINDOW</span><span style="color:#98C379;--shiki-dark:#98C379"> or</span><span style="color:#98C379;--shiki-dark:#98C379"> run</span><span style="color:#98C379;--shiki-dark:#98C379"> `</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">.</span><span style="color:#98C379;--shiki-dark:#98C379"> /home/brian/.bashrc`</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">             before</span><span style="color:#98C379;--shiki-dark:#98C379"> using</span><span style="color:#98C379;--shiki-dark:#98C379"> n</span><span style="color:#98C379;--shiki-dark:#98C379"> and</span><span style="color:#98C379;--shiki-dark:#98C379"> Node.js.</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">===</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">~$ </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">.</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> /home/brian/.bashrc</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">~$ n</span></span>
<span class="line"><span style="color:#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-ext="shell" data-title="shell"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">sudo</span><span style="color:#98C379;--shiki-dark:#98C379"> n</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 10.17.0</span></span></code></pre>
</div><h3>安装最新版本</h3>
<p>安装最新版本使用如下命令</p>
<div class="language-shell" data-ext="shell" data-title="shell"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">sudo</span><span style="color:#98C379;--shiki-dark:#98C379"> n</span><span style="color:#98C379;--shiki-dark:#98C379"> latest</span></span></code></pre>
</div><h3>切换版本</h3>
<div class="language-shell" data-ext="shell" data-title="shell"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">n</span></span></code></pre>
</div><p>显示如下</p>
<div class="language-shell" data-ext="shell" data-title="shell"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">Last</span><span style="color:#98C379;--shiki-dark:#98C379"> login:</span><span style="color:#98C379;--shiki-dark:#98C379"> Fri</span><span style="color:#98C379;--shiki-dark:#98C379"> Apr</span><span style="color:#D19A66;--shiki-dark:#D19A66">  3</span><span style="color:#98C379;--shiki-dark:#98C379"> 16:56:05</span><span style="color:#98C379;--shiki-dark:#98C379"> on</span><span style="color:#98C379;--shiki-dark:#98C379"> ttys004</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"># huangkun @ huagnkundeMacBook-Pro in ~ [16:58:24]</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">$</span><span style="color:#98C379;--shiki-dark:#98C379"> n</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  ο</span><span style="color:#98C379;--shiki-dark:#98C379"> node/10.17.0</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    node/13.12.0</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">Use</span><span style="color:#98C379;--shiki-dark:#98C379"> up/down</span><span style="color:#98C379;--shiki-dark:#98C379"> arrow</span><span style="color:#98C379;--shiki-dark:#98C379"> keys</span><span style="color:#98C379;--shiki-dark:#98C379"> to</span><span style="color:#98C379;--shiki-dark:#98C379"> select</span><span style="color:#98C379;--shiki-dark:#98C379"> a</span><span style="color:#98C379;--shiki-dark:#98C379"> version,</span><span style="color:#98C379;--shiki-dark:#98C379"> return</span><span style="color:#98C379;--shiki-dark:#98C379"> key</span><span style="color:#98C379;--shiki-dark:#98C379"> to</span><span style="color:#98C379;--shiki-dark:#98C379"> install,</span><span style="color:#98C379;--shiki-dark:#98C379"> d</span><span style="color:#98C379;--shiki-dark:#98C379"> to</span><span style="color:#98C379;--shiki-dark:#98C379"> delete,</span><span style="color:#98C379;--shiki-dark:#98C379"> q</span><span style="color:#98C379;--shiki-dark:#98C379"> to</span><span style="color:#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-ext="shell" data-title="shell"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"># huangkun @ huagnkundeMacBook-Pro in ~ [17:01:32]</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">$</span><span style="color:#98C379;--shiki-dark:#98C379"> node</span><span style="color:#D19A66;--shiki-dark:#D19A66"> -v</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">v10.17.0</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"># huangkun @ huagnkundeMacBook-Pro in ~ [17:01:39]</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">$</span><span style="color:#98C379;--shiki-dark:#98C379"> n</span><span style="color:#98C379;--shiki-dark:#98C379"> use</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 13.12.0</span><span style="color:#98C379;--shiki-dark:#98C379"> index.js</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">Node</span><span style="color:#98C379;--shiki-dark:#98C379"> version:</span><span style="color:#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://oragekk.me/posts/cross-platform/Flutter/concurrency.html</link>
      <guid>https://oragekk.me/posts/cross-platform/Flutter/concurrency.html</guid>
      <source url="https://oragekk.me/rss.xml">Dart 中的并发</source>
      <description>Dart 通过 async-await、isolate 以及一些异步类型概念（例如 Future 和 Stream）支持了并发代码编程。本篇文章会对 async-await、Future 和 Stream 进行简略的介绍，而侧重点放在 isolate 的讲解上。 在应用中，所有的 Dart 代码都在 isolate 中运行。每一个 Dart 的 iso...</description>
      <category>Flutter</category>
      <category>Dart</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-ext="dart" data-title="dart"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> main</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // Read some data.</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> fileData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> _readFileSync</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> jsonDecode</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(fileData);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // Use that data.</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'Number of JSON keys: </span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">${</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">jsonData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">length</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#98C379;--shiki-dark:#98C379">'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> _readFileSync</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> file </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> File</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(filename);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> contents </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> file.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">readAsStringSync</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> contents.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">trim</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>下面是类似的代码，但是变成了 <strong>异步调用</strong>：</p>
<div class="language-dart" data-ext="dart" data-title="dart"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> main</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="color:#C678DD;--shiki-dark:#C678DD">async</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // Read some data.</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> fileData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#C678DD;--shiki-dark:#C678DD"> await</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> _readFileAsync</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> jsonDecode</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(fileData);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // Use that data.</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'Number of JSON keys: </span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">${</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">jsonData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">length</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#98C379;--shiki-dark:#98C379">'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">Future</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt; </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">_readFileAsync</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="color:#C678DD;--shiki-dark:#C678DD">async</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> file </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> File</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(filename);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> contents </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#C678DD;--shiki-dark:#C678DD"> await</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> file.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">readAsString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> contents.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">trim</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#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-ext="dart" data-title="dart"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> main</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="color:#C678DD;--shiki-dark:#C678DD">async</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // Read some data.</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#C678DD;--shiki-dark:#C678DD"> await</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Isolate</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">run</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(_readAndParseJson);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // Use that data.</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'Number of JSON keys: </span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">${</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">jsonData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">length</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#98C379;--shiki-dark:#98C379">'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>生成的隔离器会执行作为第一个参数传递的函数, <code>_readAndParseJson</code>:</p>
<div class="language-dart" data-ext="dart" data-title="dart"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">Future</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">Map</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">dynamic</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;&gt; </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">_readAndParseJson</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="color:#C678DD;--shiki-dark:#C678DD">async</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> fileData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#C678DD;--shiki-dark:#C678DD"> await</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> File</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(filename).</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">readAsString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> jsonDecode</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(fileData) </span><span style="color:#C678DD;--shiki-dark:#C678DD">as</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Map</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">dynamic</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData;</span></span>
<span class="line"><span style="color:#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-ext="dart" data-title="dart"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> main</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="color:#C678DD;--shiki-dark:#C678DD">async</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // Read some data.</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#C678DD;--shiki-dark:#C678DD"> await</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Isolate</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">run</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(() </span><span style="color:#C678DD;--shiki-dark:#C678DD">async</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> fileData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#C678DD;--shiki-dark:#C678DD"> await</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> File</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(filename).</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">readAsString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    final</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> jsonDecode</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(fileData) </span><span style="color:#C678DD;--shiki-dark:#C678DD">as</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Map</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">dynamic</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> jsonData;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // Use that data.</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">'Number of JSON keys: </span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">${</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">jsonData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">length</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#98C379;--shiki-dark:#98C379">'</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#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>想象成一个 "并行运行 "的<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><img src="https://dart.cn/assets/img/shared/flutter/icon/64.png" alt="Flutter logo" width="20" loading="lazy">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://oragekk.me/posts/cross-platform/Flutter/newbie.html</link>
      <guid>https://oragekk.me/posts/cross-platform/Flutter/newbie.html</guid>
      <source url="https://oragekk.me/rss.xml">Flutter 基础大集合</source>
      <description>Flutter初学，demo</description>
      <category>Flutter</category>
      <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>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/20/b4d8d0a22fcdfd1a.png" alt="home" width="500" tabindex="0" loading="lazy"><figcaption>home</figcaption></figure>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/20/a4e70357270289d9.png" alt="side" width="500" tabindex="0" loading="lazy"><figcaption>side</figcaption></figure>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/20/88359eef4b2eee2f.png" alt="animation" width="500" tabindex="0" loading="lazy"><figcaption>animation</figcaption></figure>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/20/a1e88c9565dd0831.png" alt="search" width="500" tabindex="0" loading="lazy"><figcaption>search</figcaption></figure>
<figure><img src="https://s3.bmp.ovh/imgs/2023/03/20/5e6fe8f921ad4079.png" alt="alert" width="500" tabindex="0" loading="lazy"><figcaption>alert</figcaption></figure>
<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://oragekk.me/posts/cross-platform/Flutter/principle.html</link>
      <guid>https://oragekk.me/posts/cross-platform/Flutter/principle.html</guid>
      <source url="https://oragekk.me/rss.xml">Flutter 工作原理</source>
      <description>Flutter 工作原理 本文档解释了使 Flutter API 正常工作的 Flutter 工具包内部工作原理。由于 Flutter widget 是以积极组合的形式构建的，所以使用 Flutter 构建的用户界面含有大量 widget。为了支撑这些负载，Flutter 使用了次线性算法来布局和构建 widget，这些数据结构使树形结构优化更加高效，...</description>
      <category>Flutter</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://files.codelife.cc/wallhaven/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>ReactNative介绍</title>
      <link>https://oragekk.me/posts/cross-platform/ReactNative/react1.html</link>
      <guid>https://oragekk.me/posts/cross-platform/ReactNative/react1.html</guid>
      <source url="https://oragekk.me/rss.xml">ReactNative介绍</source>
      <description>前置知识点介绍</description>
      <category>前端跨平台</category>
      <pubDate>Sat, 24 Aug 2019 09:38:39 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>ReactNative 是 Facebook 开发的一套用于开发跨平台 App 的技术框架</p>
<p>相比传统开发方式解决了一些痛点：</p>
<p>1.难以复用</p>
<p>2.多平台多次开发</p>
<p>3.效率低下</p>
<p>效率带来的缺点也可想而知就是一些原生可以实现的复杂操作，RN 做不到</p>
</blockquote>
<h2>前置知识</h2>
<p>React Native 看起来很像 React，只不过其基础组件是原生组件而非 web 组件。要理解 React Native 应用的基本结构，首先需要了解一些基本的 React 的概念，比如 JSX 语法、组件、<code>state</code>状态以及<code>props</code>属性。如果你已经了解了 React，那么还需要掌握一些 React Native 特有的知识，比如原生组件的使用。</p>
<h2>语言选择</h2>
<p>typeScript 是<a href="https://baike.baidu.com/item/JavaScript" target="_blank" rel="noopener noreferrer">JavaScript</a>的一个超集，而且本质上向这个语言添加了可选的静态类型和基于类的<a href="https://baike.baidu.com/item/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B" target="_blank" rel="noopener noreferrer">面向对象编程</a>。</p>
<p>本次开发使用 typeScript 作为 ReactNative 的开发语言，对比 JavaScript 有一些优势</p>
<h4>一、ts 的静态检查</h4>
<p>参考<a href="https://www.zhihu.com/question/64563945" target="_blank" rel="noopener noreferrer">为什么要使用 TypeScript？有哪些情景请简单介绍一下，或者来个例子?</a><br>
TS 对 JS 的改进主要是静态类型检查，静态类型检查有何意义？标准答案是“静态类型更有利于构建大型应用”。为什么静态类型有利于构建大型应用？我总结，利在两点。</p>
<p>其一，静态类型检查可以做到 early fail，即你编写的代码即使没有被执行到，一旦你编写代码时发生类型不匹配，语言在编译阶段（解释执行也一样，可以在运行前）即可发现。针对大型应用，测试调试分支覆盖困难，很多代码并不一定能够在所有条件下执行到。而假如你的代码简单到任何改动都可以从 UI 体现出来，这确实跟大型应用搭不上关系，那么静态类型检查确实没什么作用。</p>
<p>配合 vscode 的<strong>TSLint</strong>插件可以很好的实现静态语法检查</p>
<h4>二、 类型就是最好的注释。</h4>
<p>静态类型对阅读代码是友好的，针对大型应用，方法众多，调用关系复杂，不可能每个函数都有人编写细致的文档，所以静态类型就是非常重要的提示和约束。而假如你的代码像 jQuery 这样所有函数基本全是 API，根本没什么内部函数，而且逻辑关系看起来显而易见，这确实跟大型应用搭不上关系，那么静态类型对阅读代码确实也没什么帮助。总的来说，现代编程语言设计，很多特性已经有非常成熟的理论支持了，如果我们重视计算机基础，那么一些语言的适用场景就像是拼积木，可以用几句话概括。像是 TS 对 JS 这样，只是单一特性变化。</p>
<h4>PS:typeScript 本质上还是一个解释执行的脚本语言，和 JavaScript 一样没有编译过程</h4>
<p>同时也不是强类型语言，**是「静态类型检查」的「弱类型」**语言</p>
<p>真正的强类型语言有：java，swift，C#</p>
<h4>三、 其他语法特性</h4>
<ol>
<li>
<p>TypeScript 工具使重构更变的容易、快捷。</p>
</li>
<li>
<p>TypeScript 引入了 JavaScript 中没有的“类”概念。</p>
</li>
<li>
<p>引入了 public，private，protected 访问控制符代替下划线私有</p>
</li>
<li>
<p>支持泛型和命名空间</p>
</li>
<li>
<p>TypeScript 中引入了模块的概念，可以把声明、数据、函数和类封装在模块中。</p>
</li>
<li>
<p>支持接口的定义</p>
<p>……</p>
</li>
</ol>
<h2>JSX</h2>
<p><a href="https://facebook.github.io/jsx/" target="_blank" rel="noopener noreferrer">JSX</a>是一种嵌入式的类似 XML 的语法。 它可以被转换成合法的 JavaScript，尽管转换的语义是依据不同的实现而定的。 JSX 因<a href="https://reactjs.org/" target="_blank" rel="noopener noreferrer">React</a>框架而流行，但也存在其它的实现。 TypeScript 支持内嵌，类型检查以及将 JSX 直接编译为 JavaScript。</p>
<p>想要使用 JSX 必须做两件事：</p>
<ol>
<li>给文件一个<code>.tsx</code>扩展名</li>
<li>启用<code>jsx</code>选项</li>
</ol>
<p>TypeScript 具有三种 JSX 模式：<code>preserve</code>，<code>react</code>和<code>react-native</code>。 这些模式只在代码生成阶段起作用 - 类型检查并不受影响。 在<code>preserve</code>模式下生成代码中会保留 JSX 以供后续的转换操作使用（比如：<a href="https://babeljs.io/" target="_blank" rel="noopener noreferrer">Babel</a>）。 另外，输出文件会带有<code>.jsx</code>扩展名。 <code>react</code>模式会生成<code>React.createElement</code>，在使用前不需要再进行转换操作了，输出文件的扩展名为<code>.js</code>。 <code>react-native</code>相当于<code>preserve</code>，它也保留了所有的 JSX，但是输出文件的扩展名是<code>.js</code>。</p>
<p>参考资料：</p>
<p><a href="https://reactnative.cn/docs/layout-props/#justifycontent" target="_blank" rel="noopener noreferrer">官方文档</a></p>
<p><a href="https://www.tslang.cn/docs/home.html" target="_blank" rel="noopener noreferrer">typeScriptg 中文文档</a></p>
<p><a href="https://www.imooc.com/video/14286" target="_blank" rel="noopener noreferrer">ReactNative 入门与进阶</a></p>
<p><a href="%5Bhttps://docs.nativebase.io%5D(https://docs.nativebase.io/)">https://docs.nativebase.io </a></p>
]]></content:encoded>
    </item>
    <item>
      <title>ReactNative开发环境配置，ES6语法介绍</title>
      <link>https://oragekk.me/posts/cross-platform/ReactNative/react2.html</link>
      <guid>https://oragekk.me/posts/cross-platform/ReactNative/react2.html</guid>
      <source url="https://oragekk.me/rss.xml">ReactNative开发环境配置，ES6语法介绍</source>
      <description>ReactNative开发环境配置，ES6语法介绍</description>
      <category>前端跨平台</category>
      <pubDate>Sun, 25 Aug 2019 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>接上篇 <a href="/posts/ReactNative/react1.html" target="_blank">ReactNative介绍</a></p>
</blockquote>
<h2>创建 ReactNative 项目</h2>
<h4>环境配置</h4>
<div class="language-shell" data-ext="shell" data-title="shell"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">yarn</span><span style="color:#98C379;--shiki-dark:#98C379"> add</span><span style="color:#D19A66;--shiki-dark:#D19A66"> --dev</span><span style="color:#98C379;--shiki-dark:#98C379"> typescript</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">yarn</span><span style="color:#98C379;--shiki-dark:#98C379"> add</span><span style="color:#D19A66;--shiki-dark:#D19A66"> --dev</span><span style="color:#98C379;--shiki-dark:#98C379"> react-native-typescript-transformer</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">yarn</span><span style="color:#98C379;--shiki-dark:#98C379"> tsc</span><span style="color:#D19A66;--shiki-dark:#D19A66"> --init</span><span style="color:#D19A66;--shiki-dark:#D19A66"> --pretty</span><span style="color:#D19A66;--shiki-dark:#D19A66"> --jsx</span><span style="color:#98C379;--shiki-dark:#98C379"> react-native</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">touch</span><span style="color:#98C379;--shiki-dark:#98C379"> rn-cli.config.js</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">yarn</span><span style="color:#98C379;--shiki-dark:#98C379"> add</span><span style="color:#D19A66;--shiki-dark:#D19A66"> --dev</span><span style="color:#98C379;--shiki-dark:#98C379"> @types/react</span><span style="color:#98C379;--shiki-dark:#98C379"> @types/react-native</span></span></code></pre>
</div><h4>配置 rn-cli.config.js</h4>
<div class="language-js" data-ext="js" data-title="js"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">module</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">exports</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  getTransformModulePath</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> require</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">resolve</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"react-native-typescript-transformer"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  getSourceExts</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#98C379;--shiki-dark:#98C379">"ts"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#98C379;--shiki-dark:#98C379">"tsx"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span></code></pre>
</div><h4>Create our new project</h4>
<p><code>react-native init AwesomeProject</code></p>
<p><code>cd AwesomeProject react-native run-ios</code></p>
<h4>项目结构图</h4>
<div class="language-shell" data-ext="shell" data-title="shell"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">my-app/</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├─</span><span style="color:#98C379;--shiki-dark:#98C379"> .gitignore</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├─</span><span style="color:#98C379;--shiki-dark:#98C379"> images.d.ts</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├─</span><span style="color:#98C379;--shiki-dark:#98C379"> node_modules/</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├─</span><span style="color:#98C379;--shiki-dark:#98C379"> public/</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├─</span><span style="color:#98C379;--shiki-dark:#98C379"> src/</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">│</span><span style="color:#98C379;--shiki-dark:#98C379">  └─</span><span style="color:#98C379;--shiki-dark:#98C379"> ...</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├─</span><span style="color:#98C379;--shiki-dark:#98C379"> package.json</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├─</span><span style="color:#98C379;--shiki-dark:#98C379"> tsconfig.json</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├─</span><span style="color:#98C379;--shiki-dark:#98C379"> tsconfig.prod.json</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">├─</span><span style="color:#98C379;--shiki-dark:#98C379"> tsconfig.test.json</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">└─</span><span style="color:#98C379;--shiki-dark:#98C379"> tslint.json</span></span></code></pre>
</div><ul>
<li><code>tsconfig.json</code> 包含我们项目的 TypeScript 特定选项。</li>
<li><code>package.json</code> 包含我们的依赖项，以及我们想要运行的命令的一些快捷方式，用于测试，预览和部署我们的应用程序。</li>
<li><code>public</code>包含静态资产，例如我们计划部署到的 HTML 页面或图像。您可以删除此文件夹中的任何文件<code>index.html</code>。</li>
<li><code>src</code>包含我们的 TypeScript 和 CSS 代码。<code>index.tsx</code>是我们文件的入口点，并且是强制性的。</li>
<li><code>images.d.ts</code>将告诉 TypeScript 可以使用某些类型的图像文件<code>import</code>，create-react-app 支持这些文件。</li>
</ul>
<h4>添加 TypeScript 配置文件</h4>
<p>我们想将 TypeScript 文件整合到一起 - 这包括我们写的源码和必要的声明文件。</p>
<p>我们需要创建一个<code>tsconfig.json</code>文件，它包含了输入文件列表以及编译选项。 在工程根目录下新建文件<code>tsconfig.json</code>文件，添加以下内容：</p>
<div class="language-json" data-ext="json" data-title="json"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  "compilerOptions"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    "outDir"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"./dist/"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    "sourceMap"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">true</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    "noImplicitAny"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">true</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    "module"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"commonjs"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    "target"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"es5"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">    "jsx"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"react"</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  },</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  "include"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: [</span><span style="color:#98C379;--shiki-dark:#98C379">"./src/**/*"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>你可以在<a href="https://www.tslang.cn/docs/handbook/tsconfig-json.html" target="_blank" rel="noopener noreferrer">这里</a>了解更多关于<code>tsconfig.json</code>文件的说明。</p>
<h4>运行</h4>
<p><code>npm run start</code></p>
<p><code>npm run test</code></p>
<h2>组件</h2>
<p>示例代码</p>
<div class="language-typescript" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">import</span><span style="color:#E06C75;--shiki-dark:#E06C75"> React</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, { </span><span style="color:#E06C75;--shiki-dark:#E06C75">Component</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="color:#C678DD;--shiki-dark:#C678DD">from</span><span style="color:#98C379;--shiki-dark:#98C379"> "react"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">import</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#E06C75;--shiki-dark:#E06C75">Text</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="color:#C678DD;--shiki-dark:#C678DD">from</span><span style="color:#98C379;--shiki-dark:#98C379"> "react-native"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#C678DD;--shiki-dark:#C678DD"> class</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> HelloWorldApp</span><span style="color:#C678DD;--shiki-dark:#C678DD"> extends</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Component</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  render</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">      &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#E06C75;--shiki-dark:#E06C75"> style</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E06C75;--shiki-dark:#E06C75">{{ flex: 1, justifyContent: "center", alignItems: "center" }}</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        &lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">Text</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">Hello</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75">world</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!&lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">Text</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">      &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    );</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>示例中的这一行<code>&lt;View&gt;&lt;Text&gt;Hello world!&lt;/Text&gt;&lt;/View&gt;</code>恐怕很多人看起来也觉得陌生。这叫做 JSX——是一种在 JavaScript 中嵌入 XML 结构的语法。很多传统的应用框架会设计自有的模板语法，让你在结构标记中嵌入代码。React 反其道而行之，设计的 JSX 语法却是让你在代码中嵌入结构标记。初看起来，这种写法很像 web 上的 HTML，只不过使用的并不是 web 上常见的标签如<code>&lt;div&gt;</code>或是<code>&lt;span&gt;</code>等，这里我们使用的是 React Native 的组件。上面的示例代码中，使用的是内置的<code>&lt;Text&gt;</code>组件，它专门用来显示文本，而<code>&lt;View&gt;</code>就类似 html 中的<code>div</code>或是<code>span</code>这样的容器。</p>
<p>上面的代码定义了一个名为<code>HelloWorldApp</code>的新的<code>组件（Component）</code>。你在编写 React Native 应用时，肯定会写出很多新的组件。而一个 App 的最终界面，其实也就是各式各样的组件的组合。组件本身结构可以非常简单——唯一必须的就是在<code>render</code>方法中返回一些用于渲染结构的 JSX 语句。</p>
<h2>Props（属性）</h2>
<p>大多数组件在创建时就可以使用各种参数来进行定制。用于定制的这些参数就称为<code>props</code>（属性）。</p>
<p>以常见的基础组件<code>Image</code>为例，在创建一个图片时，可以传入一个名为<code>source</code>的 prop 来指定要显示的图片的地址，以及使用名为<code>style</code>的 prop 来控制其尺寸。</p>
<div class="language-typescript" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">import</span><span style="color:#E06C75;--shiki-dark:#E06C75"> React</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, { </span><span style="color:#E06C75;--shiki-dark:#E06C75">Component</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="color:#C678DD;--shiki-dark:#C678DD">from</span><span style="color:#98C379;--shiki-dark:#98C379"> "react"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">import</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#E06C75;--shiki-dark:#E06C75">Image</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="color:#C678DD;--shiki-dark:#C678DD">from</span><span style="color:#98C379;--shiki-dark:#98C379"> "react-native"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#C678DD;--shiki-dark:#C678DD"> class</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Bananas</span><span style="color:#C678DD;--shiki-dark:#C678DD"> extends</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Component</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  render</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    let</span><span style="color:#E06C75;--shiki-dark:#E06C75"> pic</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      uri</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    };</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> &lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">Image</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> source</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">={</span><span style="color:#E06C75;--shiki-dark:#E06C75">pic</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">} </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">{{ width: 193, height: 110 }}</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> /&gt;;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>请注意<code>{pic}</code>外围有一层括号，我们需要用括号来把<code>pic</code>这个变量嵌入到 JSX 语句中。括号的意思是括号内部为一个 js 变量或表达式，需要执行后取值。因此我们可以把任意合法的 JavaScript 表达式通过括号嵌入到 JSX 语句中。</p>
<p>使用的时候像这样<code>this.props</code>，请注意一点，在自定义组件内部，由外部传入的 props 的属性均为**readonly（只读）**不可在组件内部修改</p>
<h2>ES6 props 使用解构赋值</h2>
<p>组件内部赋值是这样的</p>
<div class="language-typescript line-numbers-mode" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">render</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">            &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#E06C75;--shiki-dark:#E06C75"> style</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{style.</span><span style="color:#E06C75;--shiki-dark:#E06C75">ri_item</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#E06C75;--shiki-dark:#E06C75"> style</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{style.</span><span style="color:#E06C75;--shiki-dark:#E06C75">ri_line</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">View</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                {</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/* 中间部分 */</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#E06C75;--shiki-dark:#E06C75"> style</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{style.</span><span style="color:#E06C75;--shiki-dark:#E06C75">ri_item_center</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                    &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">BidderView</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                        bidderHeadImg</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{this.props.data.</span><span style="color:#E06C75;--shiki-dark:#E06C75">bidderHeadImg</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                        bidderName</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{this.props.data.</span><span style="color:#E06C75;--shiki-dark:#E06C75">bidderName</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                        targetName</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{this.props.data.</span><span style="color:#E06C75;--shiki-dark:#E06C75">targetName</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                    /&gt;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                {</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/* 尾部按钮 */</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#E06C75;--shiki-dark:#E06C75"> style</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E06C75;--shiki-dark:#E06C75">{{ flexDirection: "row", flex: 1, backgroundColor: "white", justifyContent: 'center', }}</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                    {</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">recordOpinionButton</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()}</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                    {</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">recordButton</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()}</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">            &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        );</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-typescript line-numbers-mode" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">render</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        const</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">bidderHeadImg</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">bidderName</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">targetName</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">data</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">            &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#E06C75;--shiki-dark:#E06C75"> style</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{style.</span><span style="color:#E06C75;--shiki-dark:#E06C75">ri_item</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#E06C75;--shiki-dark:#E06C75"> style</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{style.</span><span style="color:#E06C75;--shiki-dark:#E06C75">ri_line</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;/</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">View</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                {</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/* 中间部分 */</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#E06C75;--shiki-dark:#E06C75"> style</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{style.</span><span style="color:#E06C75;--shiki-dark:#E06C75">ri_item_center</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                    &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">BidderView</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                        bidderHeadImg</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span><span style="color:#E06C75;--shiki-dark:#E06C75">bidderHeadImg</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                        bidderName</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span><span style="color:#E06C75;--shiki-dark:#E06C75">bidderName</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                        targetName</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span><span style="color:#E06C75;--shiki-dark:#E06C75">targetName</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                    /&gt;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                {</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/* 尾部按钮 */</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#E06C75;--shiki-dark:#E06C75"> style</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E06C75;--shiki-dark:#E06C75">{{ flexDirection: "row", flex: 1, backgroundColor: "white", justifyContent: 'center', }}</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                    {</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">recordOpinionButton</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()}</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                    {</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">recordButton</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()}</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">                &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">            &lt;/</span><span style="color:#E06C75;--shiki-dark:#E06C75">View</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        );</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>ES6 延展操作符（...）</h2>
<h4>该运算符主要用于函数调用</h4>
<div class="language-javascript" data-ext="javascript" data-title="javascript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> push</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">array</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, ...</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">items</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  array</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">push</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(...</span><span style="color:#E06C75;--shiki-dark:#E06C75">items</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> add</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">x</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">y</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  return</span><span style="color:#E06C75;--shiki-dark:#E06C75"> x</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> +</span><span style="color:#E06C75;--shiki-dark:#E06C75"> y</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> numbers</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#D19A66;--shiki-dark:#D19A66">4</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">38</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">add</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(...</span><span style="color:#E06C75;--shiki-dark:#E06C75">numbers</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">); </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 42</span></span></code></pre>
</div><h4>对象的扩展运算</h4>
<ul>
<li><strong>拷贝对象</strong></li>
</ul>
<p>对象的扩展运算符（...）用于取出参数对象的所有可遍历属性，拷贝到当前对象之中。</p>
<div class="language-javascript" data-ext="javascript" data-title="javascript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">let</span><span style="color:#E06C75;--shiki-dark:#E06C75"> z</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">3</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75">b</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">4</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> };</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">let</span><span style="color:#E06C75;--shiki-dark:#E06C75"> n</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { ...</span><span style="color:#E06C75;--shiki-dark:#E06C75">z</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> };</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">n</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// { a: 3, b: 4 }</span></span></code></pre>
</div><ul>
<li><strong>合并对象</strong></li>
</ul>
<p>扩展运算符可以用于合并两个对象。</p>
<div class="language-javascript" data-ext="javascript" data-title="javascript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">let</span><span style="color:#E06C75;--shiki-dark:#E06C75"> ab</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { ...</span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, ...</span><span style="color:#E06C75;--shiki-dark:#E06C75">b</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> };</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 等同于</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">let</span><span style="color:#E06C75;--shiki-dark:#E06C75"> ab</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Object</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">assign</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">({}, </span><span style="color:#E06C75;--shiki-dark:#E06C75">a</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75">b</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span></code></pre>
</div><h4>用于 props，要求参数名一致，此处用法其实是对 copy 对象的一种应用</h4>
<div class="language-typescript" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">import</span><span style="color:#E06C75;--shiki-dark:#E06C75"> React</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, { </span><span style="color:#E06C75;--shiki-dark:#E06C75">Component</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="color:#C678DD;--shiki-dark:#C678DD">from</span><span style="color:#98C379;--shiki-dark:#98C379"> "react"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">import</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#E06C75;--shiki-dark:#E06C75">Image</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> } </span><span style="color:#C678DD;--shiki-dark:#C678DD">from</span><span style="color:#98C379;--shiki-dark:#98C379"> "react-native"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#C678DD;--shiki-dark:#C678DD"> class</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Bananas</span><span style="color:#C678DD;--shiki-dark:#C678DD"> extends</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Component</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  render</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    let</span><span style="color:#E06C75;--shiki-dark:#E06C75"> parameter</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      uri</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      title</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"HelloWorld"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">      message</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"Nice to meet you"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    };</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> &lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">ModalAlert</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {...</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">parameter</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">} /&gt;;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div>]]></content:encoded>
    </item>
    <item>
      <title>ReactNative State(状态)</title>
      <link>https://oragekk.me/posts/cross-platform/ReactNative/react3.html</link>
      <guid>https://oragekk.me/posts/cross-platform/ReactNative/react3.html</guid>
      <source url="https://oragekk.me/rss.xml">ReactNative State(状态)</source>
      <description>ReactNative State(状态)</description>
      <category>前端跨平台</category>
      <pubDate>Tue, 12 Nov 2019 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>接上篇 <a href="/posts/ReactNative/react2.html" target="_blank">ReactNative开发环境配置，ES6语法介绍</a></p>
</blockquote>
<h2>ReactNative State(状态)</h2>
<h3>概念</h3>
<p>通俗来讲，一个组件，或者一个视图，他们都是 Component，Component 用两个最重要的东西，一个<code>props</code></p>
<p>一个<code>state</code></p>
<p>我们使用两种数据来控制一个组件：<code>props</code>和<code>state</code>。<code>props</code>是在父组件中指定，而且一经指定，在被指定的组件的生命周期中则不再改变。(<code>props</code>就像一个类的初始化属性一样，只有在创建时可以赋值，并且组件内部不可修改，也就是<code>readonly</code>)</p>
<p>对于需要改变的数据，我们需要使用<code>state</code>。也可以把<code>state</code>理解为一个状态机，对于那些需要改变的数据可以使用<code>state</code>来更改，比如网络接口拿回来的数据，可以放在<code>state</code>里，当需要改变的时候只需要调用<code>setState</code>即可</p>
<h3>使用</h3>
<p>一般来说，你需要在 class 中声明一个<code>state</code>对象，然后在需要修改时调用<code>setState</code>方法。</p>
<p>假如我们有一个弹窗，想要控制弹窗是不是显示，需要一个<code>modalVisible</code>属性，当更改它的值时从而使界面 UI 产生相应的变化</p>
<div class="language-typescript" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 声明state对象</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">interface</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> IState</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  modalVisible</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">boolean</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">export</span><span style="color:#C678DD;--shiki-dark:#C678DD"> default</span><span style="color:#C678DD;--shiki-dark:#C678DD"> class</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> ModalMenu</span><span style="color:#C678DD;--shiki-dark:#C678DD"> extends</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> React</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">Component</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">IProps</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">IState</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt; {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  constructor</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">IProps</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B;font-style:italic;--shiki-dark-font-style:italic">    super</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">state</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#E06C75;--shiki-dark:#E06C75">modalVisible</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">false</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> };</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>另一种声明方式，声明在类内部，声明的同时，进行初始化</p>
<div class="language-typescript" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">state</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">  modalVisible</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">false</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span></code></pre>
</div><p>使用<code>this.state.modalVisible</code>来控制组件是否显示</p>
<div class="language-typescript" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">render</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">            &lt;</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">Modal</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                animationType</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379">"fade"</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                transparent</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span><span style="color:#E06C75;--shiki-dark:#E06C75">true</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                visible</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{this.state.</span><span style="color:#E06C75;--shiki-dark:#E06C75">modalVisible</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">                onRequestClose</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{() =&gt; {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                    this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setModalVisible</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">false</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                }}</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">            &gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        );</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>更新组件状态使用</p>
<div class="language-typescript" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setState</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">({ </span><span style="color:#E06C75;--shiki-dark:#E06C75">modalVisible</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">true</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> });</span></span></code></pre>
</div><h3>注意点</h3>
<p>实际开发中，我们一般不会在定时器函数（setInterval、setTimeout 等）中来操作 state。典型的场景是在接收到服务器返回的新数据，或者在用户输入数据之后。你也可以使用一些“状态容器”比如<a href="http://redux.js.org/index.html" target="_blank" rel="noopener noreferrer">Redux</a>来统一管理数据流。</p>
<p>每次调用<code>setState</code>时，BlinkApp 都会重新执行 render 方法重新渲染。</p>
<ul>
<li>render（）中 UI 的变化只有当绑定的 state 中的某个属性变化后，才会变化</li>
<li>一切界面变化都是<code>状态state变化</code></li>
<li><code>state</code>的修改必须通过<code>setState()</code>方法
<ul>
<li>this.state.likes = 100; // 这样的<code>直接赋值修改无效！</code></li>
<li>setState 是一个 merge 合并操作，只修改指定属性，不影响其他属性</li>
<li>setState 是<code>异步</code>操作，修改<code>不会马上生效</code></li>
</ul>
</li>
</ul>
<p>我们可以看到<code>setState</code>内部的声明</p>
<div class="language-typescript" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">setState</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">K</span><span style="color:#E06C75;--shiki-dark:#E06C75"> extends</span><span style="color:#E06C75;--shiki-dark:#E06C75"> keyof</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> S</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            state</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: ((</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">prevState</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">Readonly</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">S</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;, </span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">props</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">Readonly</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">P</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&gt;) </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75">Pick</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">S</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">K</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> |</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> S</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> |</span><span style="color:#D19A66;--shiki-dark:#D19A66"> null</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">|</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75">Pick</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&lt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">S</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">K</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> |</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> S</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> |</span><span style="color:#D19A66;--shiki-dark:#D19A66"> null</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">),</span></span>
<span class="line"><span style="color:#E06C75;--shiki-dark:#E06C75">            callback</span><span style="color:#C678DD;--shiki-dark:#C678DD">?:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> () </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#C678DD;--shiki-dark:#C678DD"> void</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        ): </span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span></code></pre>
</div><p>如果想要同步使用，我们可以使用第二个参数 callback?它是可选的</p>
<div class="language-typescript" data-ext="typescript" data-title="typescript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">this</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setState</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">({ </span><span style="color:#E06C75;--shiki-dark:#E06C75">modalVisible</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">true</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> }, () </span><span style="color:#C678DD;--shiki-dark:#C678DD">=&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // 这里是同步的</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">});</span></span></code></pre>
</div>]]></content:encoded>
    </item>
    <item>
      <title>WebViewJavascriptBridge</title>
      <link>https://oragekk.me/posts/iOS/other/JavaScriptBridge.html</link>
      <guid>https://oragekk.me/posts/iOS/other/JavaScriptBridge.html</guid>
      <source url="https://oragekk.me/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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">#import</span><span style="color:#98C379;--shiki-dark:#98C379"> "WebViewJavascriptBridge.h"</span></span></code></pre>
</div><ul>
<li>初始化，此处为了方便子类使用，所以在基类中注册 bridge，并 return bridge 对象，方便子类调用</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">#pragma mark</span><span style="color:#E06C75;--shiki-dark:#E06C75"> - 桥接</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)InitializeWebViewJavascriptBridge {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 注册桥接</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">bridge</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [SWHybridManager </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setJavaScriptBridgeWithWebView:</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.webView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">controller:</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[WKWebViewJavascriptBridge </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">enableLogging</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">WKWebViewJavascriptBridge *bridge </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [WKWebViewJavascriptBridge </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">bridgeForWebView:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">webView];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[bridge </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setWebViewDelegate:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">controller];</span></span></code></pre>
</div><ul>
<li>注册方法供 JavaScript 调用</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">__weak </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">typeof</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(controller)wController </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> controller;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/****************************公共方法注册-Start*********************/</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    //MARK:打开窗体</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    [bridge </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">registerHandler:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">HandlerFunctionNameOpenWindow </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">handler:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">^(</span><span style="color:#C678DD;--shiki-dark:#C678DD">id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> data, WVJBResponseCallback responseCallback) {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        __strong </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">typeof</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(wController)</span><span style="color:#E06C75;--shiki-dark:#E06C75">sController</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> wController;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">        NSLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">\n</span><span style="color:#98C379;--shiki-dark:#98C379">调用了openWindow: </span><span style="color:#D19A66;--shiki-dark:#D19A66">%@</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, data);</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        NSDictionary</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *dict </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSDictionary</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)data;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        SWOpenWindowModel *model </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [SWOpenWindowModel </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">yy_modelWithDictionary:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">dict];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> pushViewController:</span><span style="color:#E06C75;--shiki-dark:#E06C75">sController</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> data:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">model </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">responseCallback:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">responseCallback];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }];</span></span></code></pre>
</div><ul>
<li>调用 JavaScript 方法</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.bridge </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">callHandler:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">callFunctionNameGetSearchKeyWord </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">data:</span><span style="color:#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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/**打开窗体 */</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">FOUNDATION_EXPORT  </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *</span><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> HandlerFunctionNameOpenWindow;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/** 关闭窗口*/</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">FOUNDATION_EXPORT  </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *</span><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> HandlerFunctionNameCloseWindow;</span></span></code></pre>
</div><ul>
<li>桥接管理类的.m</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/**打开窗体 */</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *</span><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> HandlerFunctionNameOpenWindow </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"openWindow"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/** 关闭窗口*/</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *</span><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> HandlerFunctionNameCloseWindow </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"closeWindow"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span></code></pre>
</div><ul>
<li>使用时直接使用常量字符串即可</li>
<li>注意点：如类似我使用在基类传入控制器和 webview 到管理类中，在类中使用 controller 要注意循环引用，否则会导致控制器无法释放</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">+ (WKWebViewJavascriptBridge *)setJavaScriptBridgeWithWebView:(WKWebView *)webView controller:(__kindof SWBaseWebViewController *)controller;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">__weak </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">typeof</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(controller)wController </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> controller; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 弱引用传入控制器</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">__strong </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">typeof</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(wController)</span><span style="color:#E06C75;--shiki-dark:#E06C75">sController</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> wController; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--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-ext="javascript" data-title="javascript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> setupWebViewJavascriptBridge</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">callback</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">WebViewJavascriptBridge</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> callback</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">WebViewJavascriptBridge</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">WVJBCallbacks</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">WVJBCallbacks</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">push</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">callback</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  window</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">WVJBCallbacks</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#E06C75;--shiki-dark:#E06C75">callback</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  var</span><span style="color:#E06C75;--shiki-dark:#E06C75"> WVJBIframe</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> document</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">createElement</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"iframe"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  WVJBIframe</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">style</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">display</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> "none"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  WVJBIframe</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">src</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#98C379;--shiki-dark:#98C379"> "https://__bridge_loaded__"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  document</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">documentElement</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">appendChild</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">WVJBIframe</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  setTimeout</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> () {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    document</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">documentElement</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">removeChild</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">WVJBIframe</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }, </span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="javascript" data-title="javascript"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">setupWebViewJavascriptBridge</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">bridge</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  /* Initialize your app here */</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  bridge</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">registerHandler</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"JS Echo"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#C678DD;--shiki-dark:#C678DD">function</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">responseCallback</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"JS Echo called with:"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75">data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    responseCallback</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75">data</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  });</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  bridge</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">callHandler</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">    "ObjC Echo"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    { </span><span style="color:#E06C75;--shiki-dark:#E06C75">key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"value"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> },</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    function</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> responseCallback</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">responseData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">      console</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">log</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"JS received response:"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E06C75;--shiki-dark:#E06C75">responseData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  );</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">});</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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://oragekk.me/posts/iOS/other/coreML.html</link>
      <guid>https://oragekk.me/posts/iOS/other/coreML.html</guid>
      <source url="https://oragekk.me/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-ext="swift" data-title="swift"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">	func</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> process</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">_</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic"> image</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: UIImage) {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        imageView.image = image</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 需要CVPixelBuffer格式的输入源</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        guard</span><span style="color:#C678DD;--shiki-dark:#C678DD"> let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> pixelBuffer = image.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">pixelBuffer</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">width</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">299</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">height</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#D19A66;--shiki-dark:#D19A66">299</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            return</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        //I have `Use of unresolved identifier 'Inceptionv3'` error here when I use New Build System (File &gt; Project Settings)   ¯\_(ツ)_/¯</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> model = </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">Inceptionv3</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        do</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">            // 调用model的prediction方法进行分析</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> output = </span><span style="color:#C678DD;--shiki-dark:#C678DD">try</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> model.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">prediction</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">image</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: pixelBuffer)</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">            // 打印输出结果</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> probs = output.</span><span style="color:#E06C75;--shiki-dark:#E06C75">classLabelProbs</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">sorted</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">$0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.value &gt; </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">$1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.value }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            if</span><span style="color:#C678DD;--shiki-dark:#C678DD"> let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> prob = probs.</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">first</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                Label.text = </span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#C678DD;--shiki-dark:#C678DD">\(</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">prob.</span><span style="color:#E06C75;--shiki-dark:#E06C75">key</span><span style="color:#C678DD;--shiki-dark:#C678DD">)</span><span style="color:#C678DD;--shiki-dark:#C678DD"> \(</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">prob.value</span><span style="color:#C678DD;--shiki-dark:#C678DD">)</span><span style="color:#98C379;--shiki-dark:#98C379">"</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        catch</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">            self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">presentAlertController</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">withTitle</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: title,</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">                                        message</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: error.</span><span style="color:#E06C75;--shiki-dark:#E06C75">localizedDescription</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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://oragekk.me/posts/iOS/other/test-Three-ways-to-%20call.html</link>
      <guid>https://oragekk.me/posts/iOS/other/test-Three-ways-to-%20call.html</guid>
      <source url="https://oragekk.me/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://oragekk.me/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://oragekk.me/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://oragekk.me/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://oragekk.me/posts/iOS/source/AFNETworking-A-memory-leak.html</link>
      <guid>https://oragekk.me/posts/iOS/source/AFNETworking-A-memory-leak.html</guid>
      <source url="https://oragekk.me/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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    + (AFHTTPSessionManager *)sharedHTTPSession{</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    static</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> dispatch_once_t onceToken;</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    dispatch_once</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(&amp;onceToken, ^{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        manager </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [AFHTTPSessionManager </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">manager</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        manager</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">requestSerializer</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">timeoutInterval</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 30</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        [manager.requestSerializer  setValue:</span><span style="color:#98C379;--shiki-dark:#98C379">@"XMLHttpRequest"</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> forHTTPHeaderField:</span><span style="color:#98C379;--shiki-dark:#98C379">@"X-Requested-With"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> manager;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    + (AFURLSessionManager *)sharedURLSession{</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    static</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> dispatch_once_t onceToken2;</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    dispatch_once</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(&amp;onceToken2, ^{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        urlsession </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[AFURLSessionManager </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">alloc</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">initWithSessionConfiguration:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[NSURLSessionConfiguration </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">defaultSessionConfiguration</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> urlsession;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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://oragekk.me/posts/iOS/source/WCDB.html</link>
      <guid>https://oragekk.me/posts/iOS/source/WCDB.html</guid>
      <source url="https://oragekk.me/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://oragekk.me/posts/iOS/source/YYMemoryCache.html</link>
      <guid>https://oragekk.me/posts/iOS/source/YYMemoryCache.html</guid>
      <source url="https://oragekk.me/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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@interface</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> YYMemoryCache</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> : </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSObject</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">#pragma mark</span><span style="color:#E06C75;--shiki-dark:#E06C75"> - Attribute</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#C678DD;--shiki-dark:#C678DD">copy</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *name; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 缓存名称，默认为 nil</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#C678DD;--shiki-dark:#C678DD">readonly</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) NSUInteger totalCount; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 缓存对象总数</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#C678DD;--shiki-dark:#C678DD">readonly</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) NSUInteger totalCost; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 缓存对象总开销</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">#pragma mark</span><span style="color:#E06C75;--shiki-dark:#E06C75"> - Limit</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSUInteger countLimit; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 缓存对象数量限制，默认无限制，超过限制则会在后台逐出一些对象以满足限制</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSUInteger costLimit; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 缓存开销数量限制，默认无限制，超过限制则会在后台逐出一些对象以满足限制</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSTimeInterval ageLimit; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 缓存时间限制，默认无限制，超过限制则会在后台逐出一些对象以满足限制</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSTimeInterval autoTrimInterval; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 缓存自动清理时间间隔，默认 5s</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#C678DD;--shiki-dark:#C678DD"> BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> shouldRemoveAllObjectsOnMemoryWarning; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 是否应该在收到内存警告时删除所有缓存内对象</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#C678DD;--shiki-dark:#C678DD"> BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> shouldRemoveAllObjectsWhenEnteringBackground; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 是否应该在 App 进入后台时删除所有缓存内对象</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#C678DD;--shiki-dark:#C678DD">copy</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(^didReceiveMemoryWarningBlock)(YYMemoryCache *cache); </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 我认为这是一个 hook，便于我们在收到内存警告时自定义处理缓存</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#C678DD;--shiki-dark:#C678DD">copy</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(^didEnterBackgroundBlock)(YYMemoryCache *cache); </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 我认为这是一个 hook，便于我们在收到 App 进入后台时自定义处理缓存</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#C678DD;--shiki-dark:#C678DD"> BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> releaseOnMainThread; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 是否在主线程释放对象，默认 NO，有些对象（例如 UIView/CALayer）应该在主线程释放</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@property</span><span style="color:#C678DD;--shiki-dark:#C678DD"> BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> releaseAsynchronously; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 是否异步释放对象，默认 YES</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">containsObjectForKey:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="color:#C678DD;--shiki-dark:#C678DD"> id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">objectForKey:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setObject:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="color:#C678DD;--shiki-dark:#C678DD"> id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">object</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> forKey:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setObject:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">nullable</span><span style="color:#C678DD;--shiki-dark:#C678DD"> id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">object</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> forKey:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">key</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> withCost:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(NSUInteger)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">cost</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">removeObjectForKey:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">removeAllObjects</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">#pragma mark</span><span style="color:#E06C75;--shiki-dark:#E06C75"> - Trim</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">trimToCount:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(NSUInteger)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">count</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 用 LRU 算法删除对象，直到 totalCount &lt;= count</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">trimToCost:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(NSUInteger)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">cost</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 用 LRU 算法删除对象，直到 totalCost &lt;= cost</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">trimToAge:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(NSTimeInterval)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">age</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 用 LRU 算法删除对象，直到所有到期对象全部被删除</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@end</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@implementation</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> YYMemoryCache</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    pthread_mutex_t</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _lock; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 线程锁，旨在保证 YYMemoryCache 线程安全</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _YYLinkedMap *_lru; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// _YYLinkedMap，YYMemoryCache 通过它间接操作缓存对象</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    dispatch_queue_t _queue; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 串行队列，用于 YYMemoryCache 的 trim 操作</span></span>
<span class="line"><span style="color:#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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@interface</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> _YYLinkedMapNode</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> : </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSObject</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    @package</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 指向前一个节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    __unsafe_unretained _YYLinkedMapNode *_prev; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// retained by dic</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 指向后一个节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    __unsafe_unretained _YYLinkedMapNode *_next; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// retained by dic</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 缓存key</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _key;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 缓存对象</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _value;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 当前缓存内存开销</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    NSUInteger _cost;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 缓存时间</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    NSTimeInterval _time;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@end</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@interface</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> _YYLinkedMap</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> : </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSObject</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    @package</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 用字典保存所有节点_YYLinkedMapNode (为什么不用oc字典?因为用CFMutableDictionaryRef效率高，毕竟基于c)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CFMutableDictionaryRef _dic;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 总缓存开销</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    NSUInteger _totalCost;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 总缓存数量</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    NSUInteger _totalCount;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 链表头节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _YYLinkedMapNode *_head;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 链表尾节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _YYLinkedMapNode *_tail;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 是否在主线程上，异步释放 _YYLinkedMapNode对象</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _releaseOnMainThread;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 是否异步释放 _YYLinkedMapNode对象</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _releaseAsynchronously;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 添加节点到链表头节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">insertNodeAtHead:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(_YYLinkedMapNode *)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 移动当前节点到链表头节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">bringNodeToHead:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(_YYLinkedMapNode *)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 移除链表节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">removeNode:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(_YYLinkedMapNode *)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 移除链表尾节点(如果存在)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (_YYLinkedMapNode *)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">removeTailNode</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 移除所有缓存</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">removeAll</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@end</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 添加节点到链表头节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)insertNodeAtHead:(_YYLinkedMapNode *)node {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 哈希表保存链表节点node</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    CFDictionarySetValue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(_dic, (__bridge </span><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#C678DD;--shiki-dark:#C678DD"> void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">), (__bridge </span><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#C678DD;--shiki-dark:#C678DD"> void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)(node));</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 叠加该缓存开销到总内存开销</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCost </span><span style="color:#C678DD;--shiki-dark:#C678DD">+=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_cost</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 总缓存数+1</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCount</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">++</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (_head) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 存在链表头，取代当前表头</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_next</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _head;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        _head</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> node;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 重新赋值链表表头临时变量_head</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        _head </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> node;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    } </span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 不存在链表头</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        _head </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _tail </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> node;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 移动当前节点到链表头节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)bringNodeToHead:(_YYLinkedMapNode *)node {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 当前节点已是链表头节点</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (_head </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> node) </span><span style="color:#C678DD;--shiki-dark:#C678DD">return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (_tail </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> node) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        //**如果node是链表尾节点**</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 把node指向的上一个节点赋值给链表尾节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        _tail </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 把链表尾节点指向的下一个节点赋值nil</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        _tail</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_next</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    } </span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        //**如果node是非链表尾节点和链表头节点**</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 此处比较难以理解：总结如下</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 链接当前节点上节点(node-&gt;_prev)到当前节点下节点（node-&gt;_next）的上索引(-&gt;_prev)</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">_next</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 链接当前节点下节点(node-&gt;_next)到当前节点上节点（node-&gt;_prev）的下索引(-&gt;_next)</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">_prev</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_next</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_next</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 此处操作等于将本节点上下索引分别赋值给右左节点上下索引，将上下节点链接</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 把链表头节点赋值给node指向的下一个节点</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_next</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _head;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 把node指向的上一个节点赋值nil</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 把节点赋值给链表头节点的指向的上一个节点</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    _head</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> node;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _head </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> node;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 移除节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)removeNode:(_YYLinkedMapNode *)node {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 从字典中移除node</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    CFDictionaryRemoveValue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(_dic, (__bridge </span><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#C678DD;--shiki-dark:#C678DD"> void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">));</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 减掉总内存消耗</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCost </span><span style="color:#C678DD;--shiki-dark:#C678DD">-=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_cost</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // // 总缓存数-1</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCount</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">--</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 重新连接链表(看图分析吧)</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_next</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">_next</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">_prev</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_next</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_next</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (_head </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> node) _head </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_next</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (_tail </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> node) _tail </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> node</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div></li>
<li>
<p>移除尾节点</p>
<div class="language-objc line-numbers-mode" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 移除尾节点(如果存在)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (_YYLinkedMapNode *)removeTailNode {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">_tail) </span><span style="color:#C678DD;--shiki-dark:#C678DD">return</span><span style="color:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 拷贝一份要删除的尾节点指针</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _YYLinkedMapNode *tail </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _tail;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 移除链表尾节点</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    CFDictionaryRemoveValue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(_dic, (__bridge </span><span style="color:#C678DD;--shiki-dark:#C678DD">const</span><span style="color:#C678DD;--shiki-dark:#C678DD"> void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">_tail</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_key</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">));</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 减掉总内存消耗</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCost </span><span style="color:#C678DD;--shiki-dark:#C678DD">-=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> _tail</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_cost</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 总缓存数-1</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCount</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">--</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (_head </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _tail) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 清除节点，链表上已无节点了</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        _head </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _tail </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    } </span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 设倒数第二个节点为链表尾节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        _tail </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> _tail</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_prev</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        _tail</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-&gt;</span><span style="color:#E06C75;--shiki-dark:#E06C75">_next</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 返回完tail后_tail将会释放</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> tail;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 移除所有缓存</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)removeAll {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 清空内存开销与缓存数量</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCost </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _totalCount </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 清空头尾节点</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _head </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    _tail </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">CFDictionaryGetCount</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(_dic) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 拷贝一份字典</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        CFMutableDictionaryRef holder </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _dic;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 重新分配新的空间</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        _dic </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> CFDictionaryCreateMutable</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">CFAllocatorGetDefault</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(), </span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, &amp;</span><span style="color:#D19A66;--shiki-dark:#D19A66">kCFTypeDictionaryKeyCallBacks</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, &amp;</span><span style="color:#D19A66;--shiki-dark:#D19A66">kCFTypeDictionaryValueCallBacks</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (_releaseAsynchronously) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">            // 异步释放缓存</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            dispatch_queue_t queue </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _releaseOnMainThread </span><span style="color:#C678DD;--shiki-dark:#C678DD">?</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> dispatch_get_main_queue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() </span><span style="color:#C678DD;--shiki-dark:#C678DD">:</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> YYMemoryCacheGetReleaseQueue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">            dispatch_async</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(queue, ^{</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">                CFRelease</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(holder); </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// hold and release in specified queue</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            });</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        } </span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#C678DD;--shiki-dark:#C678DD"> if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (_releaseOnMainThread </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&amp;&amp;</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> !</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">pthread_main_np</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">            // 主线程上释放缓存</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">            dispatch_async</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">dispatch_get_main_queue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(), ^{</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">                CFRelease</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(holder); </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// hold and release in specified queue</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            });</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        } </span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">            // 同步释放缓存</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">            CFRelease</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(holder);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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://oragekk.me/posts/iOS/swift/swift%E6%8D%95%E8%8E%B7%E8%AF%AD%E4%B9%89.html</link>
      <guid>https://oragekk.me/posts/iOS/swift/swift%E6%8D%95%E8%8E%B7%E8%AF%AD%E4%B9%89.html</guid>
      <source url="https://oragekk.me/rss.xml">深入理解swift中闭包的捕捉语义</source>
      <description>参考链接：losures Capture Semantics, Part 1: Catch them all! 概述 原文中先定义一个 Pokemon 类 延时执行的闭包 默认捕捉语法 在 swift 中默认的捕捉语法是:被捕获的变量在闭包被执行的时候才被定值1 . 我们能说它捕获到了这个变量的引用(或者 指针)。 这个闭包会在 demo1() 方法函...</description>
      <category>Swift</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-ext="swift" data-title="swift"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">class</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> Pokemon</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">CustomDebugStringConvertible </span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> name: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">String</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  init</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF;font-style:italic;--shiki-dark-font-style:italic">name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> = name</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  var</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> debugDescription: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">String</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#C678DD;--shiki-dark:#C678DD">return</span><span style="color:#98C379;--shiki-dark:#98C379"> "</span><span style="color:#C678DD;--shiki-dark:#C678DD">\(</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">name</span><span style="color:#C678DD;--shiki-dark:#C678DD">)</span><span style="color:#98C379;--shiki-dark:#98C379">&gt;"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  deinit</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> { </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#C678DD;--shiki-dark:#C678DD">\(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#C678DD;--shiki-dark:#C678DD">)</span><span style="color:#98C379;--shiki-dark:#98C379"> escaped!"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>延时执行的闭包</p>
<div class="language-swift" data-ext="swift" data-title="swift"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">func</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> delay</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF;font-style:italic;--shiki-dark-font-style:italic">seconds</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">Int</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#61AFEF;--shiki-dark:#61AFEF;font-style:italic;--shiki-dark-font-style:italic">closure</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: ()-&gt;()) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> time = DispatchTime.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">now</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() + .</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">seconds</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(seconds)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  DispatchQueue.</span><span style="color:#E06C75;--shiki-dark:#E06C75">main</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">after</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">when</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: time) {</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">    print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"🕑"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    closure</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#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>(或者 &nbsp;<em>指针</em>)。</p>
<div class="language-swift" data-ext="swift" data-title="swift"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">func</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> demo1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> pokemon = </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">Pokemon</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">: </span><span style="color:#98C379;--shiki-dark:#98C379">"Mewtwo"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">  print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"before closure: </span><span style="color:#C678DD;--shiki-dark:#C678DD">\(</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">pokemon</span><span style="color:#C678DD;--shiki-dark:#C678DD">)</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">  delay</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">    print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"inside closure: </span><span style="color:#C678DD;--shiki-dark:#C678DD">\(</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">pokemon</span><span style="color:#C678DD;--shiki-dark:#C678DD">)</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">  print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"bye"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><p>这个闭包会在 demo1() 方法函数执行完成后 1 秒后被执行，并且我们已退出了方法函数的作用域… 当然 Pokemon 仍然是存在的，当这个代码块在下一个 1 秒后再次被执行的时候！</p>
<div class="language-swift" data-ext="swift" data-title="swift"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">before closure</span><span style="color:#C678DD;--shiki-dark:#C678DD">:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> &lt;Pokemon Mewtwo&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">bye</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">🕑</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">inside closure</span><span style="color:#C678DD;--shiki-dark:#C678DD">:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> &lt;Pokemon Mewtwo&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;Pokemon Mewtwo&gt; escaped!</span></span></code></pre>
</div><p>在这个例子中，当这个闭包被 GCD 执行后，这个闭包自行释放，就是 Pokemon 内部的 init 方法执行的时候。<br>
此处 Swift 自动捕获到这个 pokemon 变量，当 demo1 方法执行完成并且释放掉的时候，1s 之后闭包被执行的时候还是捕获到了 pokemon</p>
<h2>被捕获到的变量都被执行的时候定值</h2>
<div class="language-swift" data-ext="swift" data-title="swift"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> name = </span><span style="color:#98C379;--shiki-dark:#98C379">"John"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> test1 = {</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">    print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(name)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">name = </span><span style="color:#98C379;--shiki-dark:#98C379">"Aby"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">test1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span></code></pre>
</div><p>此处在创建了 test1 闭包之后，我们改变了 name 的值，打印结果是 Aby。说明此处 Swift 捕获到的是 name 这个变量的引用(指针)</p>
<p>原文中的例子使用了方法，并做了延时，大意在说明，函数被释放之后的延时闭包中，闭包依然捕获了变量的新值，而不是旧值，说明此处它捕获的是变量的引用，而非变量本身的值</p>
<p>这个在值类型中也可行例如 Int</p>
<h2>修改变量的值</h2>
<p>如果捕获的值是一个 &nbsp;var 并不是一个 &nbsp;let，你还是可以修改这个值 &nbsp;<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-ext="swift" data-title="swift"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> name = </span><span style="color:#98C379;--shiki-dark:#98C379">"John"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> test1 = {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    name = </span><span style="color:#98C379;--shiki-dark:#98C379">"Jack"</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">    print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(name)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">name = </span><span style="color:#98C379;--shiki-dark:#98C379">"Aby"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">test1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">()</span></span></code></pre>
</div><p>此处打印结果为 Jack，再一次印证，闭包内捕获的是变量 name 的引用，因为它可以被改变，而不是一个静态的拷贝</p>
<h2>捕获一个作为静态 copy 的变量</h2>
<div class="language-swift" data-ext="swift" data-title="swift"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">var</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> str = </span><span style="color:#98C379;--shiki-dark:#98C379">"Hello, playground"</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">let</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> show1 = { [strcopy = str] </span><span style="color:#C678DD;--shiki-dark:#C678DD">in</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 在闭包一开始创建的时候捕获变量的值，</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// []内的为捕获列表，一开始捕获值，而非引用。</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 捕获的为原始变量的副本-&gt;常量，并且只能在闭包内访问</span></span>
<span class="line"></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">    print</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">"这是str</span></span></code></pre>
</div>]]></content:encoded>
    </item>
    <item>
      <title>NSError</title>
      <link>https://oragekk.me/posts/iOS/system/NSError.html</link>
      <guid>https://oragekk.me/posts/iOS/system/NSError.html</guid>
      <source url="https://oragekk.me/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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // domain 不能为空 dict可以为空</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  </span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  - (</span><span style="color:#C678DD;--shiki-dark:#C678DD">instancetype</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)initWithDomain:(NSErrorDomain)domain code:(NSInteger)code userInfo:(nullable </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSDictionary</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)dict;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  + (</span><span style="color:#C678DD;--shiki-dark:#C678DD">instancetype</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)errorWithDomain:(NSErrorDomain)domain code:(NSInteger)code userInfo:(nullable </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSDictionary</span><span style="color:#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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSError</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> codes </span><span style="color:#C678DD;--shiki-dark:#C678DD">in</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> the Cocoa error domain.</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileNoSuchFileError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 4</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileLockingError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 255</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadUnknownError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 256</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadNoPermissionError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 257</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadInvalidFileNameError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 258</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadCorruptFileError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 259</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadNoSuchFileError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 260</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadInapplicableStringEncodingError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 261</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadUnsupportedSchemeError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 262</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadTooLargeError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 263</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileReadUnknownStringEncodingError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 264</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteUnknownError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 512</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteNoPermissionError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 513</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteInvalidFileNameError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 514</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteInapplicableStringEncodingError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 517</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteUnsupportedSchemeError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 518</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteOutOfSpaceError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 640</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileWriteVolumeReadOnlyError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#FFFFFF;--shiki-dark:#FFFFFF"> 642m</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSKeyValueValidationError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1024</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFormattingError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 2048</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSUserCancelledError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3072</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">NSFileErrorMinimum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFileErrorMaximum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1023</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSValidationErrorMinimum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1024</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSValidationErrorMaximum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 2047</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFormattingErrorMinimum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 2048</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSFormattingErrorMaximum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 2559</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">NSPropertyListReadCorruptError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3840</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSPropertyListReadUnknownVersionError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3841</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSPropertyListReadStreamError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3842</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSPropertyListWriteStreamError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3851</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSPropertyListErrorMinimum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3840</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSPropertyListErrorMaximum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 4095</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">NSExecutableErrorMinimum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3584</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableNotLoadableError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3584</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableArchitectureMismatchError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3585</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableRuntimeMismatchError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3586</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableLoadError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3587</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableLinkError </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3588</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSExecutableErrorMaximum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3839</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> URL Loading System Error Codes</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">These values are returned as the error code property of an </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSError</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> object with the domain “NSURLErrorDomain”.</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">typedef</span><span style="color:#C678DD;--shiki-dark:#C678DD"> enum</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorUnknown </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCancelled </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">999</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorBadURL </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1000</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorTimedOut </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1001</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorUnsupportedURL </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1002</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotFindHost </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1003</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotConnectToHost </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1004</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorDataLengthExceedsMaximum </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1103</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorNetworkConnectionLost </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1005</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorDNSLookupFailed </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1006</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorHTTPTooManyRedirects </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1007</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorResourceUnavailable </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1008</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorNotConnectedToInternet </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1009</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorRedirectToNonExistentLocation </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1010</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorBadServerResponse </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1011</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorUserCancelledAuthentication </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1012</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorUserAuthenticationRequired </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1013</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorZeroByteResource </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1014</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotDecodeRawData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1015</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotDecodeContentData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1016</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotParseResponse </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1017</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorFileDoesNotExist </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1100</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorFileIsDirectory </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1101</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorNoPermissionsToReadFile </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1102</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorSecureConnectionFailed </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1200</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorServerCertificateHasBadDate </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1201</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorServerCertificateUntrusted </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1202</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorServerCertificateHasUnknownRoot </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1203</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorServerCertificateNotYetValid </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1204</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorClientCertificateRejected </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1205</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorClientCertificateRequired </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1206</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotLoadFromNetwork </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">2000</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotCreateFile </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3000</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotOpenFile </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3001</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotCloseFile </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3002</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotWriteToFile </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3003</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotRemoveFile </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3004</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorCannotMoveFile </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3005</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorDownloadDecodingFailedMidStream </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3006</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> NSURLErrorDownloadDecodingFailedToComplete </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3007</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> }</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorUnknown</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无效的URL地址"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">999</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCancelled</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无效的URL地址"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1000</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorBadURL</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无效的URL地址"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1001</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorTimedOut</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"网络不给力，请稍后再试"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1002</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorUnsupportedURL</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"不支持的URL地址"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1003</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotFindHost</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"找不到服务器"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1004</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotConnectToHost</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"连接不上服务器"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1103</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorDataLengthExceedsMaximum</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"请求数据长度超出最大限度"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1005</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorNetworkConnectionLost</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"网络连接异常"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1006</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorDNSLookupFailed</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"DNS查询失败"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1007</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorHTTPTooManyRedirects</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"HTTP请求重定向"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1008</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorResourceUnavailable</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"资源不可用"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1009</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorNotConnectedToInternet</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无网络连接"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1010</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorRedirectToNonExistentLocation</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"重定向到不存在的位置"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1011</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorBadServerResponse</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"服务器响应异常"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1012</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorUserCancelledAuthentication</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"用户取消授权"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1013</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorUserAuthenticationRequired</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"需要用户授权"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1014</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorZeroByteResource</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"零字节资源"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1015</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotDecodeRawData</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无法解码原始数据"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1016</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotDecodeContentData</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无法解码内容数据"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1017</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotParseResponse</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无法解析响应"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1018</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorInternationalRoamingOff</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"国际漫游关闭"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1019</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCallIsActive</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"被叫激活"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1020</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorDataNotAllowed</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"数据不被允许"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1021</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorRequestBodyStreamExhausted</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"请求体"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1100</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorFileDoesNotExist</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"文件不存在"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1101</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorFileIsDirectory</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"文件是个目录"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1102</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorNoPermissionsToReadFile</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无读取文件权限"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1200</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorSecureConnectionFailed</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"安全连接失败"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1201</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorServerCertificateHasBadDate</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"服务器证书失效"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1202</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorServerCertificateUntrusted</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"不被信任的服务器证书"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1203</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorServerCertificateHasUnknownRoot</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"未知Root的服务器证书"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1204</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorServerCertificateNotYetValid</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"服务器证书未生效"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1205</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorClientCertificateRejected</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"客户端证书被拒"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">1206</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorClientCertificateRequired</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"需要客户端证书"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">2000</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotLoadFromNetwork</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无法从网络获取"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3000</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotCreateFile</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无法创建文件"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3001</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// NSURLErrorCannotOpenFile</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无法打开文件"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3002</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotCloseFile</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无法关闭文件"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3003</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotWriteToFile</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无法写入文件"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3004</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotRemoveFile</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无法删除文件"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3005</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorCannotMoveFile</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"无法移动文件"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3006</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorDownloadDecodingFailedMidStream</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"下载解码数据失败"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">case -</span><span style="color:#D19A66;--shiki-dark:#D19A66">3007</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">:</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//NSURLErrorDownloadDecodingFailedToComplete</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">errorMesg </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"下载解码数据失败"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">break</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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://oragekk.me/posts/iOS/system/NSOperation%E5%92%8CNSOPerationQueue.html</link>
      <guid>https://oragekk.me/posts/iOS/system/NSOperation%E5%92%8CNSOPerationQueue.html</guid>
      <source url="https://oragekk.me/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://oragekk.me/posts/iOS/system/Reduced-App-%20volume.html</link>
      <guid>https://oragekk.me/posts/iOS/system/Reduced-App-%20volume.html</guid>
      <source url="https://oragekk.me/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>用代码绘制简单的纯色图片 &nbsp;<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&nbsp;<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://oragekk.me/posts/iOS/system/WKWebView-URL.html</link>
      <guid>https://oragekk.me/posts/iOS/system/WKWebView-URL.html</guid>
      <source url="https://oragekk.me/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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (^)(WKNavigationActionPolicy))decisionHandler {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 获取完整url并进行UTF-8转码</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *strRequest </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [navigationAction.request.URL.absoluteString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">stringByRemovingPercentEncoding</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> ([strRequest </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">hasPrefix:</span><span style="color:#98C379;--shiki-dark:#98C379">@"app://"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 拦截点击链接</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> handleCustomAction:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">strRequest];</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 不允许跳转</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">      	decisionHandler</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(WKNavigationActionPolicyCancel);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    	// 允许跳转</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">        decisionHandler</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(WKNavigationActionPolicyAllow);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#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-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)handleCustomAction:(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)URL</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 判断跳转</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *link_id </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @""</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> ([URL </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">hasPrefix:</span><span style="color:#98C379;--shiki-dark:#98C379">@"app://video"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 视频</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">        MMLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"点击了视频</span><span style="color:#D19A66;--shiki-dark:#D19A66">%@</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,link_id);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#C678DD;--shiki-dark:#C678DD"> if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> ([URL </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">hasPrefix:</span><span style="color:#98C379;--shiki-dark:#98C379">@"app://item"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 单品</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">        MMLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"点击了单品</span><span style="color:#D19A66;--shiki-dark:#D19A66">%@</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,link_id);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#C678DD;--shiki-dark:#C678DD"> if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> ([URL </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">hasPrefix:</span><span style="color:#98C379;--shiki-dark:#98C379">@"app://brand"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 品牌</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        link_id </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [URL </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">substringFromIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> stringWithFormat:</span><span style="color:#98C379;--shiki-dark:#98C379">@"app://brand"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">].length];</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">        MMLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"点击了品牌</span><span style="color:#D19A66;--shiki-dark:#D19A66">%@</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,link_id);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>
    <item>
      <title>WKWebView使用及自适应高度</title>
      <link>https://oragekk.me/posts/iOS/system/WKWebView.html</link>
      <guid>https://oragekk.me/posts/iOS/system/WKWebView.html</guid>
      <source url="https://oragekk.me/rss.xml">WKWebView使用及自适应高度</source>
      <description>记录一下 iOS8 之后的新控件 WKWebView，用以替代之前的 UIWebView，因为需求是在 TableView 的 Cell 中放一个 WebView。就产生了滑动手势冲突，为了解决这个问题就需要让 webView 高度自适应 一、新特性 在性能、稳定性、功能方面有很大的提升，最明显的就是内存占用降低了很多。 允许 JavaScript 的...</description>
      <category>iOS</category>
      <pubDate>Fri, 26 May 2017 15:47:40 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>记录一下 iOS8 之后的新控件 WKWebView，用以替代之前的 UIWebView，因为需求是在 TableView 的 Cell 中放一个 WebView。就产生了滑动手势冲突，为了解决这个问题就需要让 webView 高度自适应</p>
</blockquote>
<h2>一、新特性</h2>
<ul>
<li>在性能、稳定性、功能方面有很大的提升，最明显的就是内存占用降低了很多。</li>
<li>允许 JavaScript 的 Nitro 库加载并使用（UIWebView 中限制）</li>
<li>支持了更多的 HTML5 特性；</li>
<li>高达 60fps 的滚动刷新率以及内置手势（支持右滑返回）；</li>
<li>将 UIWebViewDelegate 与 UIWebView 重构成了 14 类与 3 个协议（<a href="https://developer.apple.com/reference/webkit" target="_blank" rel="noopener noreferrer">查看苹果官方文档</a>）；</li>
</ul>
<h2>二、初始化</h2>
<ul>
<li>首先需要引入 WebKit 库</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">#import</span><span style="color:#98C379;--shiki-dark:#98C379"> &lt;WebKit/WebKit.h&gt;</span></span></code></pre>
</div><ul>
<li>采用 configuration 的方式初始化（可选）</li>
</ul>
<div class="language-objc line-numbers-mode" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (WKWebView *)webView {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">_webView) {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        WKWebViewConfiguration *config </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [WKWebViewConfiguration </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">new</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        //初始化偏好设置属性：preferences</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        config</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">preferences</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [WKPreferences </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">new</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        //The minimum font size in points default is 0;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        config</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">preferences</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">minimumFontSize</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 10</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        //是否支持JavaScript</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        config</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">preferences</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">javaScriptEnabled</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> YES</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        //不通过用户交互，是否可以打开窗口</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        config</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">preferences</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">javaScriptCanOpenWindowsAutomatically</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> NO</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        _webView </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[WKWebView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">alloc</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]initWithFrame:</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">CGRectMake</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.view.width, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.view.height) </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">configuration:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">config];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        _webView</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">UIDelegate</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        _webView</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">navigationDelegate</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 此处因为高度自适应所以不应该让webview内部可以滚动</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        _webView</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">scrollView</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">scrollEnabled</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> NO</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _webView;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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><ul>
<li>加载网页</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">WKWebView *webView </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[WKWebView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">alloc</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">initWithFrame:</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.view.bounds];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[webView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">loadRequest:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSURLRequest</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> requestWithURL:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSURL</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> URLWithString:</span><span style="color:#98C379;--shiki-dark:#98C379">@"http://m.baidu.com"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]]];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.view </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">addSubview:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">webView];</span></span></code></pre>
</div><h2>三、WKWebView 代理方法</h2>
<p>1.WKNavigationDelegate</p>
<p>该代理提供的方法，可以用来追踪加载过程（页面开始加载、加载完成、加载失败）、决定是否执行跳转。</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 页面开始加载时调用</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 当内容开始返回时调用</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 页面加载完成之后调用</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 页面加载失败时调用</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;</span></span></code></pre>
</div><p>页面跳转的代理方法有三种，分为（收到跳转与决定是否跳转两种）</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 接收到服务器跳转请求之后调用</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 在收到响应后，决定是否跳转</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (^)(WKNavigationResponsePolicy))decisionHandler;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 在发送请求之前，决定是否跳转</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (^)(WKNavigationActionPolicy))decisionHandler;</span></span></code></pre>
</div><p>2.WKUIDelegate</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 创建一个新的WebView</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;</span></span></code></pre>
</div><p>剩下三个代理方法全都是与界面弹出提示框相关的，针对于 web 界面的三种提示框（警告框、确认框、输入框）分别对应三种代理方法。下面只举了警告框的例子</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">/**</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> *  web界面中有弹出警告框时调用</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> *</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> *  @param webView           实现该代理的webview</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> *  @param message           警告框中的内容</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> *  @param frame             主窗口</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> *  @param completionHandler 警告框消失调用</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> */</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)message initiatedByFrame:(</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (^)())completionHandler;</span></span></code></pre>
</div><p>3.WKScriptMessageHandler</p>
<p>这个协议中包含一个必须实现的方法，这个方法是提高 App 与 web 端交互的关键，它可以直接将接收到的 JS 脚本转为 OC 或 Swift 对象</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 从web界面中接收到一个脚本时调用</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;</span></span></code></pre>
</div><h2>四、WKWebView 加载 JS</h2>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// js代码</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *js </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"	var count = document.images.length;for (var i = 0; i &lt; count; i++) {var image = document.images[i];image.style.width=320;};window.alert('找到' + count + '张图');"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 根据JS字符串初始化WKUserScript对象</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">WKUserScript *script </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[WKUserScript </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">alloc</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">initWithSource:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">js </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">injectionTime:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">WKUserScriptInjectionTimeAtDocumentEnd </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">forMainFrameOnly:</span><span style="color:#D19A66;--shiki-dark:#D19A66">YES</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 根据生成的WKUserScript对象，初始化WKWebViewConfiguration</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">WKWebViewConfiguration *config </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[WKWebViewConfiguration </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">alloc</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">init</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]; [config.userContentController </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">addUserScript:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">script]; _webView </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[WKWebView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">alloc</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">initWithFrame:</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.view.bounds </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">configuration:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">config];</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 加载html字符串</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[_webView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">loadHTMLString:</span><span style="color:#98C379;--shiki-dark:#98C379">@"&lt;head&gt;&lt;/head&gt;&lt;img src='http://www.nsu.edu.cn/v/2014v3/img/background/3.jpg' /&gt;"</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">baseURL:</span><span style="color:#D19A66;--shiki-dark:#D19A66">nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.view </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">addSubview:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">_webView];</span></span></code></pre>
</div><h2>五、作为 cell 自适应行高</h2>
<figure><img src="https://storage2.cuntuku.com/2017/05/27/2.gif" alt="2.gif" tabindex="0" loading="lazy"><figcaption>2.gif</figcaption></figure>
<ul>
<li>在 didFinishNavigation 方法中获取行高，然后刷新表格，网上资料所说的获取 webview.scrollview.contentsize.height 本人测试不可行，此处采用调用 js 通过 ajax 获取高度</li>
</ul>
<div class="language-objc line-numbers-mode" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 页面加载完成之后调用 此方法会调用多次</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    __block</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> CGFloat webViewHeight;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">height</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> webView</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">frame</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">size</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">height</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    //获取内容实际高度（像素）@"document.getElementById(\"content\").offsetHeight;"</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    [webView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">evaluateJavaScript:</span><span style="color:#98C379;--shiki-dark:#98C379">@"document.body.scrollHeight"</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> completionHandler:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">^(</span><span style="color:#C678DD;--shiki-dark:#C678DD">id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _Nullable result,</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSError</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> * _Nullable error) {</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 此处js字符串采用scrollHeight而不是offsetHeight是因为后者并获取不到高度，看参考资料说是对于加载html字符串的情况下使用后者可以，但如果是和我一样直接加载原站内容使用前者更合适</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        //获取页面高度，并重置webview的frame</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        webViewHeight </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [result </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">doubleValue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">        NSLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"</span><span style="color:#D19A66;--shiki-dark:#D19A66">%f</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,webViewHeight);</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">        dispatch_async</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">dispatch_get_main_queue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(), ^{</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (webViewHeight </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">height</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                webView</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">frame</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> CGRectMake</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">view</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">frame</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">size</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">width</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, webViewHeight);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">reloadData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        });</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">    NSLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"结束加载"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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><ul>
<li>此处完成之后需要在 heightforrow 方法中设置 cell 的高为 webview 的高，但是会出现一个问题就是内容显示不全。还需要调用下面这个方法进行重布局</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)scrollViewDidScroll:(UIScrollView *)scrollView {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> ([scrollView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">isKindOfClass:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]]) {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.webView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setNeedsLayout</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><h2>六、小结</h2>
<blockquote>
<p>自适应行高是本文的重点，是我自己试验了多种方法之后确定可行的方法</p>
</blockquote>
<blockquote>
<p>下篇文章将介绍拦截 url 进行原生跳转</p>
</blockquote>
]]></content:encoded>
      <enclosure url="https://storage2.cuntuku.com/2017/05/27/2.gif" type="image/gif"/>
    </item>
    <item>
      <title>Runloop</title>
      <link>https://oragekk.me/posts/iOS/system/iOS%E4%B8%AD%E7%9A%84Runloop.html</link>
      <guid>https://oragekk.me/posts/iOS/system/iOS%E4%B8%AD%E7%9A%84Runloop.html</guid>
      <source url="https://oragekk.me/rss.xml">Runloop</source>
      <description>Runloop Runloop 是什么 Runloop 是事件接收和分发机制的一个实现。 Runloop 提供了一种异步执行代码的机制，不能并行执行任务。 在主队列中，Main RunLoop 直接配合任务的执行，负责处理 UI 事件、定时器以及其他内核相关事件。 ###Runloop 的主要目的 保证执行程序的线程不会被终止 ###什么时候使用 Ru...</description>
      <category>iOS</category>
      <pubDate>Mon, 26 Dec 2016 13:39:59 GMT</pubDate>
      <content:encoded><![CDATA[
<h3>Runloop 是什么</h3>
<ul>
<li>Runloop 是事件接收和分发机制的一个实现。
<ul>
<li>Runloop 提供了一种异步执行代码的机制，不能并行执行任务。</li>
<li>在主队列中，Main RunLoop 直接配合任务的执行，负责处理 UI 事件、定时器以及其他内核相关事件。</li>
</ul>
</li>
</ul>
<p>###Runloop 的主要目的</p>
<ul>
<li>保证执行程序的线程不会被终止</li>
</ul>
<p>###什么时候使用 Runloop</p>
<ul>
<li>
<p>当需要和该线程进行交互的时候才会使用 Runloop</p>
</li>
<li>
<p>每一个线程都有其对应的 RunLoop，但是默认非主线程的 RunLoop 是没有运行的，需要为 RunLoop 添加至少一个事件源，然后去 run 它。</p>
</li>
<li>
<p>一般情况下我们是没有必要去启用线程的 RunLoop 的，除非你在一个单独的线程中需要长久的检测某个事件。</p>
</li>
<li>
<p>主线程 默认有 Runloop。当自己启动一个线程，如果只是用于处理单一的事件，则该线程在执行完之后就退出了。所以当我们需要让该线程监听某项事务时，就得让线程一直不退出，runloop 就是这么一个循环，没有事件的时候，一直卡着，有事件来临了，执行其对应的函数。</p>
</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>OC 中的枚举类型</title>
      <link>https://oragekk.me/posts/iOS/system/iOS%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8Benum%EF%BC%8CNS_ENUM%EF%BC%8CNS_OPTIONS.html</link>
      <guid>https://oragekk.me/posts/iOS/system/iOS%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8Benum%EF%BC%8CNS_ENUM%EF%BC%8CNS_OPTIONS.html</guid>
      <source url="https://oragekk.me/rss.xml">OC 中的枚举类型</source>
      <description>进入正题，今天介绍一下objective-c中的枚举 提要 首先要知道的是,枚举值 它是一个整形(int) 并且,它不参加内存的占用和释放 枚举定义变量即可直接使用,不用初始化 三种类型的枚举 enum 在iOS6之前一般我们采用C风格的enum关键字可以定义枚举类型 在iOS6之后引入两个宏来定义枚举实际上是将enum定义和typedef合二为一，并...</description>
      <category>iOS</category>
      <pubDate>Fri, 06 Jan 2017 11:19:16 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>进入正题，今天介绍一下objective-c中的枚举</p>
</blockquote>
<h3>提要</h3>
<ul>
<li>
<p>首先要知道的是,枚举值 它是一个整形(int) 并且,它不参加内存的占用和释放 枚举定义变量即可直接使用,不用初始化</p>
</li>
<li>
<p>三种类型的枚举</p>
<ul>
<li><strong>enum</strong> 在iOS6之前一般我们采用C风格的enum关键字可以定义枚举类型</li>
</ul>
<p>在iOS6之后引入两个宏来定义枚举实际上是将enum定义和typedef合二为一，并且采用不同的宏来从代码角度来区分。</p>
<ul>
<li>
<p><strong>NS_ENUM</strong>  普通枚举定义可参见UIKit.Framework中</p>
</li>
<li>
<p><strong>NS_OPTIONS</strong>  位运算及特殊枚举的定义。什么时候要用到这种方式呢? 那就是一个枚举变量可能要代表多个枚举值的时候. 其实给一个枚举变量赋予多个枚举值的时候,原理只是把各个枚举值加起来罢了. 当加起来以后,就获取了一个新的值,那么为了保证这个值的唯一性,这个时候就体现了位运算的重要作用. 位运算可以确保枚举值组合的唯一性. 因为位运算的计算方式是将二进制转换成十进制,也就是说,枚举值里面存取的是 计算后的十进制值. 打个比方: 通过上面的位运算方式设定好枚举以后,打印出来的枚举值分别是: 1 2 4 8 16 这5个数字,无论你如何组合在一起,也不会产生两个同样的数字.</p>
</li>
</ul>
<p>这两个宏的定义在Foundation.framework的NSObjCRuntime.h中：</p>
</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">  		#if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">__cplusplus</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&nbsp;&amp;&amp;</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">&nbsp;__cplusplus</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&nbsp;&gt;=</span><span style="color:#D19A66;--shiki-dark:#D19A66">&nbsp;201103</span><span style="color:#E06C75;--shiki-dark:#E06C75">L</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&nbsp;&amp;&amp;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">__has_extension</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">cxx_strong_enums</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)&nbsp;</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">||</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">&nbsp;__has_feature</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">objc_fixed_enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)))&nbsp;</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">||</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;(</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">__cplusplus</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&nbsp;&amp;&amp;</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">&nbsp;__has_feature</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">objc_fixed_enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">))</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">		#define</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">&nbsp;NS_ENUM</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">_type,&nbsp;_name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)&nbsp;</span><span style="color:#C678DD;--shiki-dark:#C678DD">enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;_name&nbsp;:&nbsp;_type&nbsp;_name;&nbsp;</span><span style="color:#C678DD;--shiki-dark:#C678DD">enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;_name&nbsp;:&nbsp;_type</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">		#if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">__cplusplu</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">		#define</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">&nbsp;NS_OPTIONS</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">_type,&nbsp;_name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)&nbsp;_type&nbsp;_name;&nbsp;</span><span style="color:#C678DD;--shiki-dark:#C678DD">enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;:&nbsp;_type&nbsp;&nbsp;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">		#else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;&nbsp;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">		#define</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">&nbsp;NS_OPTIONS</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">_type,&nbsp;_name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)&nbsp;</span><span style="color:#C678DD;--shiki-dark:#C678DD">enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;_name&nbsp;:&nbsp;_type&nbsp;_name;&nbsp;</span><span style="color:#C678DD;--shiki-dark:#C678DD">enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;_name&nbsp;:&nbsp;_type&nbsp;&nbsp;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">		#endif</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;&nbsp;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">		#else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;&nbsp;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">		#define</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">&nbsp;NS_ENUM</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">_type,&nbsp;_name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)&nbsp;_type&nbsp;_name;&nbsp;</span><span style="color:#C678DD;--shiki-dark:#C678DD">enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;&nbsp;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">				#define</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">&nbsp;NS_OPTIONS</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E06C75;--shiki-dark:#E06C75;font-style:italic;--shiki-dark-font-style:italic">_type,&nbsp;_name</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)&nbsp;_type&nbsp;_name;&nbsp;</span><span style="color:#C678DD;--shiki-dark:#C678DD">enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&nbsp;&nbsp;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">		#endif</span></span></code></pre>
</div><h3>举个🌰</h3>
<div class="language-objc line-numbers-mode" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    //推荐的定义枚举类型的方式</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    typedef</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> NS_ENUM</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(NSInteger, RWTLeftMenuTopItemType) {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">     RWTLeftMenuTopItemMain, </span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">     RWTLeftMenuTopItemShows,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">     RWTLeftMenuTopItemSchedule </span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }; </span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    typedef</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> NS_ENUM</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(NSInteger, RWTGlobalConstants) { </span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    RWTPinSizeMin </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    RWTPinSizeMax </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 5</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    RWTPinCountMin </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 100</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    RWTPinCountMax </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 500</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }; </span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    typedef</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> NS_OPTIONS</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(NSInteger, Test) {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    TestA </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//1 1 1等于号后面必须等于1</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    TestB </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &lt;&lt;</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//2 2 10 转换成 10进制 2</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    TestC </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &lt;&lt;</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//4 3 100 转换成 10进制 4</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    TestD </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &lt;&lt;</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 3</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//8 4 1000 转换成 10进制 8</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    TestE </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &lt;&lt;</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 4</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> //16 5 10000 转换成 10进制 16</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    };</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    //不推荐的方式</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    enum</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> GlobalConstants { </span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">    kMaxPinSize</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 5</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span></span>
<span class="line"><span style="color:#D19A66;--shiki-dark:#D19A66">    kMaxPinCount</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 500</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    };</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>共同学习共同进步，加油。fighting😆</p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>iOS Cookie的配置及使用</title>
      <link>https://oragekk.me/posts/iOS/system/iOS%E7%9A%84Cookie%E4%BD%BF%E7%94%A8.html</link>
      <guid>https://oragekk.me/posts/iOS/system/iOS%E7%9A%84Cookie%E4%BD%BF%E7%94%A8.html</guid>
      <source url="https://oragekk.me/rss.xml">iOS Cookie的配置及使用</source>
      <description>本文介绍 iOS 中 cookie 的使用包含 AFNetWorking 3.0 中的使用，常用于登录状态信息保存 什么是 Cookies？ Cookie 是由服务器保存在用户浏览器（客户端）上的一块数据，它可以包含有关用户的信息,比如果登陆的状态，用户标识等。 Cookie 有什么作用？ 主要用在以下三个方面: 会话状态管理（如用户登录状态、购物车）...</description>
      <category>iOS</category>
      <pubDate>Wed, 05 Jul 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>本文介绍 iOS 中 cookie 的使用包含 AFNetWorking 3.0 中的使用，常用于登录状态信息保存</p>
</blockquote>
<h2>什么是 Cookies？</h2>
<p>Cookie 是由服务器保存在用户浏览器（客户端）上的一块数据，它可以包含有关用户的信息,比如果登陆的状态，用户标识等。<br>
Cookie 有什么作用？</p>
<p>主要用在以下三个方面:</p>
<ul>
<li>
<p>会话状态管理（如用户登录状态、购物车）</p>
</li>
<li>
<p>个性化设置（如用户自定义设置）</p>
</li>
<li>
<p>浏览器行为跟踪（如跟踪分析用户行为）</p>
</li>
</ul>
<h2>cookie 的处理步骤</h2>
<ul>
<li>服务器向客户端发送 cookie</li>
<li>通常使用 HTTP 协议规定的 Set-Cookie 头操作。</li>
<li>规范规定 cookie 的格式为 name = value 格式，且必须包含这部分。</li>
<li>浏览器将 cookie 保存</li>
<li>每次请求浏览器都会将 cookie 发向服务器</li>
</ul>
<p>其他可选的 cookie 参数会影响将 cookie 发送给服务器端的过程，主要有以下几种：</p>
<p>| key      | 是否可选 |                                                                                                                     value |<br>
|</p>
]]></content:encoded>
      <enclosure url="https://storage1.cuntuku.com/2017/07/06/cookie12x.png" type="image/png"/>
    </item>
    <item>
      <title>iOS程序启动原理（上）</title>
      <link>https://oragekk.me/posts/iOS/system/iOS%E7%A8%8B%E5%BA%8F%E5%90%AF%E5%8A%A8%E5%8E%9F%E7%90%86%EF%BC%88%E4%B8%8A%EF%BC%89.html</link>
      <guid>https://oragekk.me/posts/iOS/system/iOS%E7%A8%8B%E5%BA%8F%E5%90%AF%E5%8A%A8%E5%8E%9F%E7%90%86%EF%BC%88%E4%B8%8A%EF%BC%89.html</guid>
      <source url="https://oragekk.me/rss.xml">iOS程序启动原理（上）</source>
      <description>本文介绍 iOS 程序中的 Info.plist,pch 文件,UIApplication,openURL 以及 UIWindow 的常用属性及方法; info.plist 常见设置 建立一个工程后,会在 Supporting files 文件夹下看到一个&amp;quot;工程名-Info.plist&amp;quot;的文件,该文件对工程做一些运行期的配置,非常重要,不能删除. 在旧...</description>
      <category>iOS</category>
      <pubDate>Fri, 30 Dec 2016 15:59:12 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>本文介绍 iOS 程序中的 Info.plist,pch 文件,UIApplication,openURL 以及 UIWindow 的常用属性及方法;</p>
</blockquote>
<h3>info.plist</h3>
<h4>常见设置</h4>
<p>建立一个工程后,会在 Supporting files 文件夹下看到一个"<em>工程名-Info.plist</em>"的文件,该文件对工程做一些运行期的配置,非常重要,不能删除.<br>
在旧版 Xcode 创建的工程中,这个配置文件的名字叫做"Info.plist".<br>
项目中的其他 plist 文件不能带有"Info"这个字眼,不然会被错认为是传说中非常重要的"Info.plist".<br>
项目中还有一个"InfoPlist.strings"的文件(Xcode6 之后需手动添加),跟 Info.plist 文件的本地化相关.</p>
<h4>Info.plist 常见属性:</h4>
<ol>
<li>Localiztion native development region (CFBundleDevelopmentRegion)-本地化相关;</li>
<li>Bundle display name(CFBundleDisplayName)-程序安装后显示的名称,限制在 10-12 个字符,如果超出,将被显示缩写名称;</li>
<li>Icon file(CFBundleIconFile)-app 图标名称,一般为 Icon.png;</li>
<li>Bundle version(CFBundleVersion)-应用程序的版本号,每次往 App Store 上发布一个新版本时,需要增加这个版本号;</li>
<li>Main storyboard file base name(NSMainStoryboardFile)-主 storyboard 文件名称;</li>
<li>Bundle identifier(CFBundleIdentifier)-项目的唯一标识,部署到真机时用到;</li>
<li>额外说一下从 iOS9 开始，所有的 http 请求都改成了 https，采用 TLS 1.2 协议，目的是增强数据安全。如果不更新的话，暂时可以在 Info.plist 中声明，使用不安全的网络请求。<br>
可以在 info.plist 中添加一下字段
即如：![](http://upload-images.jianshu.io/upload_images/2076247-22165721d20be6ab?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</li>
</ol>
<h3>pch 文件</h3>
<p>项目的 Supporting files 文件夹下面有个"工程名-Prefix.pch"文件,也是一个头文件;</p>
<pre><code>pch头文件的内容能被项目中的其他所有源文件共享和访问;

一般在pch头文件中定义一些全局的宏;

在pch头文件中添加下列预处理指令,然后在项目中使用Log(...)来输出日志信息,就可以在发布应用的时候,一次性将NSLog语句移除(在调试模式下,才有定义DEBUG)
    #ifdef&nbsp;DEBUG
    #define&nbsp;Log(...)&nbsp;NSLog(__VA_ARGS__)
    #else#define&nbsp;Log(...)&nbsp;/*&nbsp;*/
    #endif``
</code></pre>
<h3>UIApplication</h3>
<p>UIApplication 对象是应用程序的象征;<br>
&nbsp;&nbsp;&nbsp;&nbsp; 每一个应用都有自己的 UIApplication 对象,而且是单例的;<br>
&nbsp;&nbsp;&nbsp;&nbsp; 通过[UIApplication sharedApplication]可以获得这个单例对象;<br>
&nbsp;&nbsp;&nbsp;&nbsp; 一个 iOS 程序启动后创建的第一个对象就是 UIApplication 对象;<br>
&nbsp;&nbsp;&nbsp;&nbsp; 利用 UIApplication 对象,能进行一些应用级别的操作.</p>
<h4>UIApplication 的常用属性</h4>
<p>设置应用程序图标右上角的红色提醒数字:<br>
@property(nonatomic)&nbsp;NSInteger&nbsp;applicationIconBadgeNumber;<br>
设置联网指示器(菊花)的可见性<br>
@property(nonatomic,getter=isNetworkActivityIndicatorVisible)&nbsp;BOOL&nbsp;networkActivityIndicatorVisible;</p>
<h3>状态栏</h3>
<h5>从 iOS7 开始,系统提供了两种管理状态栏的方式:</h5>
<p>1.&gt;通过 UIViewController 管理(在 iOS 中,默认情况下,状态栏都是由 UIViewController 管理的):<br>
状态栏的样式:<br>
-&nbsp;(UIStatusBarStyle)preferredStatusBarStyle;<br>
&nbsp; 状态栏的可见性<br>
-&nbsp;(BOOL)prefersStatusBarHidden;<br>
2.&gt;通过 UIApplication 管理(一个应用程序的状态栏都由它统一管理)<br>
application.statusBarHidden&nbsp;=&nbsp;NO;</p>
<h4>openURL:</h4>
<p>UIApplication 有个功能十分强大的 openURL 方法:<br>
-&nbsp;(BOOL)openURL:(NSURL*)url;<br>
openURL:方法的部分功能有</p>
<ul>
<li>打电话<br>
UIApplication&nbsp;*app&nbsp;=&nbsp;[UIApplication&nbsp;sharedApplication];<br>
[app&nbsp;openURL:[NSURL&nbsp;URLWithString:@"tel://10086"]];</li>
<li>发短信<br>
[app&nbsp;openURL:[NSURL&nbsp;URLWithString:@"sms://10086"]];</li>
<li>发邮件<br>
[app&nbsp;openURL[NSURL&nbsp;URLWithString:@"mailto://605453790@qq.com"]];</li>
<li>打开一个网页资源<br>
[app&nbsp;openURL:[NSURL&nbsp;URLWithString:@"<a href="http://www.baidu.com" target="_blank" rel="noopener noreferrer">http://www.baidu.com</a>"]];</li>
<li>打开其他 app 程序<br>
NSString *urlString = [NSString stringWithFormat:@"AppJumpSecond://%@",textField.text];<br>
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];</li>
</ul>
<h3>UIApplication 和 delegate</h3>
<p>所有的移动操作系统都有个致命的缺点:app 很容易受到打扰.比如一个来电或者锁屏会导致 app 进入后台甚至被终止;</p>
<p>还有很多其他类似的情况会导致 app 受到干扰,在 app 受到干扰时,会产生一些系统事件,这时 UIApplication 会通知它的 delegate 对象,让 delegate 来处理这些系统事件.</p>
<p>delegate 可处理的事件包括:</p>
<pre><code>1&gt; 应用程序的生命周期事件(如程序的启动和关闭);

2&gt; 系统事件(如来电);

3&gt; 内存警告...
</code></pre>
<h4>UIApplicationDelegate 协议</h4>
<pre><code>//&nbsp;app接收到内存警告时调用
-&nbsp;(void)applicationDidReceiveMemoryWarning:(UIApplication&nbsp;*)application;
//&nbsp;app进入后台时调用（比如按了home键）
-&nbsp;(void)applicationDidEnterBackground:(UIApplication&nbsp;*)application;
//&nbsp;app启动完毕时调用
-&nbsp;(BOOL)application:(UIApplication&nbsp;*)application&nbsp;didFinishLaunchingWithOptions:(NSDictionary&nbsp;*)launchOptions;
</code></pre>
<p>每次新建完项目,都有个带有"AppDelegate"字眼的类,它就是 UIApplication 的代理,<em>AppDelegate</em>默认已经遵守了<code>UIApplicationDelegate</code>协议,已经是 UIApplication 的代理;</p>
<h3>UIWindow</h3>
<p>UIWindow 是一种特殊的 UIView,通常在一个 App 中只会有一个 UIWindow;<br>
&nbsp;&nbsp;&nbsp;&nbsp;iOS 程序启动完毕后,创建的第一个视图控件就是 UIWindow,接着创建控制器的 View,最后将控制器的 view 添加到 UIWindow 上,于是控制器的 view 就显示在屏幕上了.<br>
&nbsp;&nbsp;&nbsp;&nbsp; 一个程序之所以能显示在屏幕上,完全是因为它有 UIWindow,没有 UIWindow,就看不见任何 UI 界面;<br>
添加 UIView 到 UIWindow 中的两种常见方式:<br>
&nbsp;&nbsp;&nbsp;&nbsp;1&gt; 直接将 view 添加到 UIWindow 中,但并不会理会 view 对应的 UIViewController<br>
-&nbsp;(void)addSubview:(UIView&nbsp;*)view;</p>
<p>2&gt; 自动将 rootViewController 的 view 添加到 UIWindow 中,负责管理 rootViewController 的生命周期;</p>
<pre><code>@property(nonatomic,retain)&nbsp;UIViewController&nbsp;*rootViewController;
</code></pre>
<p>常用方法:<br>
&nbsp; -&nbsp;(void)makeKeyWindow;&nbsp;&nbsp;//让当前 UIWindow 变成 keyWindow(主窗口)&nbsp;<br>
-&nbsp;(void)makeKeyAndVisible;&nbsp;&nbsp;//让当前 UIWindow 变成 keyWindow，并显示出来</p>
<h4>UIWindow 的获得:</h4>
<pre><code>[UIApplication&nbsp;sharedApplication].windows
</code></pre>
<p>在本应用中打开的 UIWindow 列表,这样就可以接触应用中的任何一个 UIView 对象(平时输入文字弹出的键盘,就处在一个新的 UIWindow 中).</p>
<pre><code>[UIApplication&nbsp;sharedApplication].keyWindow
</code></pre>
<p>用来接收键盘以及非触摸类的消息事件的 UIWindow,而且程序中每时每刻只能有一个 UIWindow 是 keyWindow.如果某个 UIWindow 内部的文本框不能输入文字,可能是因为这个 UIWindow 不是 keyWindow.</p>
<pre><code>view.window
</code></pre>
<p>获得某个 UIView 所在的 UIWindow.</p>
]]></content:encoded>
      <enclosure url="http://upload-images.jianshu.io/upload_images/2076247-22165721d20be6ab?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" type="image/"/>
    </item>
    <item>
      <title>iOS程序启动原理（下）</title>
      <link>https://oragekk.me/posts/iOS/system/iOS%E7%A8%8B%E5%BA%8F%E5%90%AF%E5%8A%A8%E5%8E%9F%E7%90%86%EF%BC%88%E4%B8%8B%EF%BC%89.html</link>
      <guid>https://oragekk.me/posts/iOS/system/iOS%E7%A8%8B%E5%BA%8F%E5%90%AF%E5%8A%A8%E5%8E%9F%E7%90%86%EF%BC%88%E4%B8%8B%EF%BC%89.html</guid>
      <source url="https://oragekk.me/rss.xml">iOS程序启动原理（下）</source>
      <description>接上篇 下图是一个 iOS 程序启动的完整过程 图 main 函数中执行了一个 UIApplicationMain 这个函数. argc, argv:直接传递给 UIApplicationMain 进行相关处理即可; principalClassName:指定应用程序类名(app 象征),该类必须是 UIApplication(或子类).如果为 nil...</description>
      <category>iOS</category>
      <pubDate>Fri, 30 Dec 2016 16:02:25 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>接上篇</p>
</blockquote>
<p><a href="/posts/iOS/iOS%E7%A8%8B%E5%BA%8F%E5%90%AF%E5%8A%A8%E5%8E%9F%E7%90%86%EF%BC%88%E4%B8%8A%EF%BC%89.html" target="_blank">iOS 程序启动原理（上）</a></p>
<p>下图是一个 iOS 程序启动的完整过程</p>
<!-- ![图](https://upload-images.jianshu.io/upload_images/2076247-1f1b30ddcfcd7ef3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -->
<img src="https://upload-images.jianshu.io/upload_images/2076247-1f1b30ddcfcd7ef3.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" referrerpolicy="no-referrer" alt="图">
<p>main 函数中执行了一个 UIApplicationMain 这个函数.</p>
<pre><code>  int&nbsp;main(int&nbsp;argc,&nbsp;char&nbsp;*&nbsp;argv[])
  {&nbsp;&nbsp;&nbsp;&nbsp;
    @autoreleasepool&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      return&nbsp;UIApplicationMain(argc,&nbsp;argv,&nbsp;nil,&nbsp;NSStringFromClass([JNAppDelegate&nbsp;class]));&nbsp;&nbsp;&nbsp;&nbsp;
    }
   }
  int&nbsp;UIApplicationMain(int&nbsp;argc,&nbsp;char&nbsp;*argv[],&nbsp;NSString&nbsp;*principalClassName,&nbsp;NSString&nbsp;*delegateClassName);
</code></pre>
<p>argc, argv:直接传递给 UIApplicationMain 进行相关处理即可;</p>
<p>principalClassName:指定应用程序类名(app 象征),该类必须是 UIApplication(或子类).如果为 nil,则用 UIApplication 类作为默认值.</p>
<p>delegateClassName:指定应用程序的代理类,该类必须遵守 UIApplicationDelegate 协议.</p>
<p>UIApplicationMain 函数会根据 principalClassName 创建 UIApplication 对象,根据 delegateClassName 创建一个 delegate 对象,并将该 delegate 对象赋值给 UIApplication 对象中的 delegate 属性.</p>
<p>接着会建立应用程序的 Main Runloop(事件循环),进行事件的处理(首先会在程序启动完毕后调用 delegate 对象的 application:didFinishLaunchingWithOptions:方法)</p>
<p>程序正常退出时 UIApplicationMain 函数才返回.</p>
]]></content:encoded>
      <enclosure url="https://upload-images.jianshu.io/upload_images/2076247-1f1b30ddcfcd7ef3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" type="image/"/>
    </item>
    <item>
      <title>iOS 10.3 keychain 重大更新</title>
      <link>https://oragekk.me/posts/iOS/system/keychain%E6%9B%B4%E6%96%B0.html</link>
      <guid>https://oragekk.me/posts/iOS/system/keychain%E6%9B%B4%E6%96%B0.html</guid>
      <source url="https://oragekk.me/rss.xml">iOS 10.3 keychain 重大更新</source>
      <description>转载自微信公众号《Mrpeak 杂货铺》 Paste_Image.pngPaste_Image.png iOS 10.3 还未正式发布，beta 版中一个关于 keychain 特性的小修改，就已经引起了广泛的关注。 改动如下 如果 App 被删除，之前存储于 keychain 中的数据也会一同被清除。 如果使用了 keychain group，只要当...</description>
      <category>iOS</category>
      <pubDate>Tue, 14 Mar 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>转载自微信公众号《Mrpeak 杂货铺》</p>
</blockquote>
<figure><img src="http://upload-images.jianshu.io/upload_images/2076247-1b07e63d35825ce7.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="Paste_Image.png" tabindex="0" loading="lazy"><figcaption>Paste_Image.png</figcaption></figure>
<p>iOS 10.3 还未正式发布，beta 版中一个关于 keychain 特性的小修改，就已经引起了广泛的关注。</p>
<p>改动如下</p>
<ul>
<li>如果 App 被删除，之前存储于 keychain 中的数据也会一同被清除。</li>
<li>如果使用了 keychain group，只要当 group 所有相关的 App 被删除时，keychain 中的数据才会被删除。</li>
</ul>
<p>这一改动，虽未经官方公布。但已在论坛帖子里得到了 Apple 员工的确认，原文如下：</p>
<blockquote>
<p>This is an intentional change in iOS 10.3 to protect user privacy. Information that can identify a user should not be left on the device after the app that created it has been removed.</p>
</blockquote>
<blockquote>
<p>It has never been a part of the API contract that keychain items created by an app would survive when the app is removed. This has always been an implementation detail.</p>
</blockquote>
<blockquote>
<p>If a keychain item is shared with other apps, it won’t be deleted until those other apps have been deleted as well.</p>
</blockquote>
<p>心存侥幸的人可能会觉得这只是正常的 API 调整，会有新的存储机制做替补。在我看来，这种可能性极低。正如上面这段英文所述，Apple 这一改动是基于用户隐私的考虑，改动之后，开发者将没法再根据设备号来追踪设备的唯一性。结合最近 JSPatch 这类热更新机制被禁来看，Apple 近期似乎在用户隐私和安全方面有相当的规划和动作。</p>
<p>Apple 对于开发者的每次动作都有让人无法反驳的立场，用户隐私当然很重要。谈论其合理性其实并没有什么意义，因为这不会影响最终结果，我们只能拥抱变化，早作准备。</p>
<p>这次改动影响究竟有多大呢？没用到 keychain 特性还好，如有涉及，其影响可能比大部分人想象的要大，以下是我所能预知的一部分：</p>
<h3>重装需登录</h3>
<p>无法追踪设备，用户删掉一个 App，之后再重装就只能手动登录一次了。</p>
<p>很多 App 都有类似的功能，重装的时候不需要再次登录，比如 WhatsApp，知乎等，这对用户体验更好一些。这种用户场景下，App 的本意虽然不是追踪用户设备，但很不幸，开发者已经不能判断是否为同一设备的再次登录了。</p>
<h3>基于设备的免费试用功能无法实现了</h3>
<p>有不少 App 的试用功能都是基于账户或者基于设备的，基于设备的会更多一些，因为如果是基于账户，在同一个设备上更换账户还不算太麻烦，而用户更换设备的成本更高。但新版本 keychain 更改之后，用户如果删掉 App 重装，开发者无法判断试用功能是否已经试用过了。</p>
<p>这种基于设备的免费试用场景也不少，比如一些可以免费试用 7 天的 VPN App。</p>
<h3>短信费用上升</h3>
<p>这方面的影响就是直接和真金白银挂钩了。</p>
<p>现在不少基于熟人社交的 App 都是以用户的手机号为 ID 注册的，每次注册都要花费一条平台的短信费用。不少 App 为了节省费用，在用户删除重装之后登录的场景，都是直接从 keychain 读出以前的登录信息，绕过短信验证直接登进去的。iOS 10.3 之后，就无法再绕过了，必须在重装时再花一条短信的钱来验证用户。</p>
<p>不要小看了这一条短信的费用，对于财大气粗的大公司来说还好，但对于锱铢必较的创业团队来说，这些额外的开销就是一笔冤枉钱了。</p>
<h3>总结</h3>
<p>对于跟踪用户和设备，Apple 的管控越来越严，从最初的设备 ID，到 Mac 地址，到今天 keychain 的调整。每一次看似一个小的更改，影响面却很广。不知道大家是否有使用 keychain 来做持久化存储方案，是否会因为这次调整受到影响，如果不能跨越 App 的生命周期，真不知道 keychain 还有什么存在的意义，各位要早作准备了。</p>
]]></content:encoded>
      <enclosure url="http://upload-images.jianshu.io/upload_images/2076247-1b07e63d35825ce7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" type="image/"/>
    </item>
    <item>
      <title>利用Runtime进行快速归档</title>
      <link>https://oragekk.me/posts/iOS/system/runtime%E5%BF%AB%E9%80%9F%E5%BD%92%E6%A1%A3.html</link>
      <guid>https://oragekk.me/posts/iOS/system/runtime%E5%BF%AB%E9%80%9F%E5%BD%92%E6%A1%A3.html</guid>
      <source url="https://oragekk.me/rss.xml">利用Runtime进行快速归档</source>
      <description>使用 runtime 进行归档，对我们的最大遍历就是高效，快速。尤其适用于 model 中属性非常多的时候 Person.h person.m viewController.m printResult</description>
      <category>iOS</category>
      <pubDate>Thu, 13 Apr 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>使用 runtime 进行归档，对我们的最大遍历就是高效，快速。尤其适用于 model 中属性非常多的时候</p>
</blockquote>
<h2>Person.h</h2>
<figure><img src="http://i2.muimg.com/567571/e2afe593e10c0c78.png" alt="" tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<h2>person.m</h2>
<figure><img src="http://i4.buimg.com/567571/713e554a5fd04e13.png" alt="" tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<h2>viewController.m</h2>
<figure><img src="http://i1.piimg.com/567571/0333be9f5d16fd96.png" alt="" tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<h2>printResult</h2>
<figure><img src="http://i4.buimg.com/567571/5b12de7ec090769f.png" alt="" tabindex="0" loading="lazy"><figcaption></figcaption></figure>
]]></content:encoded>
      <enclosure url="http://i2.muimg.com/567571/e2afe593e10c0c78.png" type="image/png"/>
    </item>
    <item>
      <title>Update Cocoapods 1.1.1</title>
      <link>https://oragekk.me/posts/iOS/tool/Update-Cocoapods.html</link>
      <guid>https://oragekk.me/posts/iOS/tool/Update-Cocoapods.html</guid>
      <source url="https://oragekk.me/rss.xml">Update Cocoapods 1.1.1</source>
      <description>之前采用正常的 sudo gem install cocoapods更新 cocoapods 版本一直不成功，下面为和我遇到同样问题的兄弟们提供一个解决办法 先切换 gem 源 gem sources --remove https://rubygems.org/ gem source -a https://gems.ruby-china.org 查看是...</description>
      <category>iOS</category>
      <pubDate>Wed, 09 Nov 2016 11:34:47 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>之前采用正常的 <code>sudo gem install cocoapods</code>更新 cocoapods 版本一直不成功，下面为和我遇到同样问题的兄弟们提供一个解决办法</p>
</blockquote>
<h4>先切换 gem 源</h4>
<ul>
<li><code>gem sources --remove https://rubygems.org/</code></li>
<li><code>gem source -a https://gems.ruby-china.org</code></li>
<li>查看是否切换成功 <code>gem source -l</code><br>
如果出现下图这样的就说明切换成功了, 如果还是官方的源, 请手动重启电脑尝试<br>
<img src="http://upload-images.jianshu.io/upload_images/2076247-365912ab78be4906.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="" loading="lazy"></li>
</ul>
<h4>接下来就可以开始升级了 cocoapods 了</h4>
<ul>
<li><code>sudo gem install -n /usr/local/bin cocoapods --pre</code></li>
<li>是的, 你没看错是这个命令, 然后终端会出现一大推东西, 别管他, 最后停下来是这样的就差不多了<br>
<img src="http://upload-images.jianshu.io/upload_images/2076247-81b6046594fe772b.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="" loading="lazy"></li>
<li>然后查看版本<code>pod --version</code><br>
出现 1.1.1，恭喜你已经安装成功了</li>
<li>接下来设置 pod 仓库 <code>pod setup</code></li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/2076247-cafa12def948db48.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="Paste_Image.png" loading="lazy"><br>
此处需要耐心等待，根据网络情况完成时间长短不一。<br>
可以在终端中 CD 到<code>~/.cocoapods</code>目录中输入 <code>du -sh *</code>查看下载进度</p>
<h3>至此, 已经升级到 cocoapods1.1.1 了, 可以愉快的把玩 Swift3.0 的一些三方库了</h3>
]]></content:encoded>
      <enclosure url="http://upload-images.jianshu.io/upload_images/2076247-365912ab78be4906.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" type="image/"/>
    </item>
    <item>
      <title>ijkPlayer 集成</title>
      <link>https://oragekk.me/posts/iOS/tool/ijkplayer.html</link>
      <guid>https://oragekk.me/posts/iOS/tool/ijkplayer.html</guid>
      <source url="https://oragekk.me/rss.xml">ijkPlayer 集成</source>
      <description>参考地址 ijkplayer 是一款做视频直播的框架，基于 FFmpeg，支持 Android 和 iOS。这里介绍一下 iOS 中集成 ijkplayer 一、FFmpeg FFmpeg 是一套可以用来记录、转换数字音频、视频，并能将其转化为流的开源计算机程序。它包括了领先的音/视频编码库 libavcodec 等。 libavformat：用于各种...</description>
      <category>iOS</category>
      <pubDate>Wed, 30 Aug 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p><a href="http://www.jianshu.com/p/b7a646a6c80e" target="_blank" rel="noopener noreferrer">参考地址</a><br>
ijkplayer 是一款做视频直播的框架，基于 FFmpeg，支持 Android 和 iOS。这里介绍一下 iOS 中集成 ijkplayer</p>
</blockquote>
<h2>一、FFmpeg</h2>
<p>FFmpeg 是一套可以用来记录、转换数字音频、视频，并能将其转化为流的开源计算机程序。它包括了领先的音/视频编码库 libavcodec 等。</p>
<p><strong>libavformat</strong>：用于各种音视频封装格式的生成和解析，包括获取解码所需信息以生成解码上下文结构<br>
和读取音视频帧等功能；</p>
<p><strong>libavcodec</strong>：用于各种类型声音/图像编解码；</p>
<p><strong>libavutil</strong>：包含一些公共的工具函数；</p>
<p><strong>libswscale</strong>：用于视频场景比例缩放、色彩映射转换；</p>
<p><strong>libpostproc</strong>：用于后期效果处理；</p>
<p><strong>ffmpeg</strong>：该项目提供的一个工具，可用于格式转换、解码或电视卡即时编码等；</p>
<p><strong>ffsever</strong>：一个 HTTP 多媒体即时广播串流服务器；</p>
<p><strong>ffplay</strong>：是一个简单的播放器，使用 ffmpeg 库解析和解码，通过 SDL 显示；</p>
<h3>支持的编码</h3>
<p>源自 FFmpeg 项目组的两个视频编码：</p>
<p>Snow</p>
<p>FFV1</p>
<h3>支持的格式</h3>
<p>ASF</p>
<p>AVI</p>
<p>BFI[7]</p>
<p>IFF[8]</p>
<p>RL2[9]</p>
<p>FLV</p>
<p>MXF, Material eXchange Format, SMPTE 377M</p>
<p>Matroska</p>
<p>Maxis XA[10]</p>
<p>MSN Webcam stream[11]</p>
<p>MPEG transport stream</p>
<p>TXD[6]</p>
<p>OMA[12]</p>
<p>GXF, General eXchange Format, SMPTE 360M</p>
<p>mov,mp4,m4a,3gp,</p>
<h3>支持的协议</h3>
<p>HTTP</p>
<p>RTP</p>
<p>RTSP</p>
<p>RealMedia RTSP/RDT</p>
<p>TCP</p>
<p>UDP</p>
<p>Gopher</p>
<p>RTMP</p>
<p>RTMPT, RTMPE, RTMPTE, RTMPS (via librtmp)</p>
<p>SDP</p>
<p>MMS over TCP</p>
<h2>二、下载 ijkplayer</h2>
<p>ijkplayer 下载地址:<a href="https://github.com/Bilibili/ijkplayer" target="_blank" rel="noopener noreferrer">https://github.com/Bilibili/ijkplayer</a></p>
<p>下载完成后解压, 解压后文件夹内部目录如下图:<br>
<img src="https://storage2.cuntuku.com/2017/08/31/ijkplayer.png" alt="ijkplayer.png" loading="lazy"></p>
<h2>三、编译</h2>
<p>其实这里主要是编译 FFmpeg，因为他是一个 C 语言的跨平台库，需要 sh 脚本来进行编译</p>
<ol>
<li>打开终端, cd 到 jkplayer-master 文件夹中, 也就是下载完解压后的文件夹, 如下图:<img src="https://storage1.cuntuku.com/2017/08/31/1.png" alt="1.png" loading="lazy"></li>
<li>执行命令行**./init-ios.sh**, 这一步是去下载 ffmpeg 的, 时间会久一点, 耐心等一下.如下图:<img src="https://storage2.cuntuku.com/2017/08/31/2.png" alt="2.png" loading="lazy"></li>
<li>cd 到 ios 目录中</li>
<li>执行**./compile-ffmpeg.sh clean**<img src="https://storage1.cuntuku.com/2017/08/31/3.png" alt="3.png" loading="lazy"></li>
<li>执行**./compile-ffmpeg.sh all**进行 FFmpeg 的编译，时间较久<img src="https://storage2.cuntuku.com/2017/08/31/4.png" alt="4.png" loading="lazy"></li>
</ol>
<h2>四、打包 IJKMediaFramework.framework 框架</h2>
<p>其实集成 ijkplayer 有两种方法，一种是按照 Demo 中的导入 IJKMediaPlayer.xcodeproj，此方法不是很推荐</p>
<p>下面主要说另一种把 ijkplayer 打包成 framework 导入工程中使用.<br>
首先打开工程 IJKMediaPlayer.xcodeproj,<img src="http://upload-images.jianshu.io/upload_images/1803339-607cc84c212faf90.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="IJKMediaPlayer.xcodeproj" loading="lazy"></p>
<p>选择 IJKMediaFramework 点击 EditScheme<img src="http://upload-images.jianshu.io/upload_images/1803339-bbc0adc479cebb69.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="" loading="lazy"></p>
<p>选择 release<img src="http://upload-images.jianshu.io/upload_images/1803339-daa4498f7e0746d0.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="" loading="lazy"></p>
<p>设置好 scheme 后, 分别选择真机和模拟器进行编译, 编译完成后, 进入 Finder,<img src="http://upload-images.jianshu.io/upload_images/1803339-344cda905745ff39.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="" loading="lazy"></p>
<p>下面开始合并真机和模拟器版本的 framework, 注意不要合并错了, 合并的是这个文件, 如下图:<img src="http://upload-images.jianshu.io/upload_images/1803339-ec00ef4cb15c66d1.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="" loading="lazy"></p>
<p>打开终端, 进行合并, 命令行具体格式为:<br>
lipo -create 真机版本路径 模拟器版本路径 -output 合并后的文件路径</p>
<p>合并后如下图<img src="http://upload-images.jianshu.io/upload_images/1803339-d025e12bf804ee05.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="" loading="lazy"></p>
<p>用合并后的 IJKMediaFramework 把原来的 IJKMediaFramework 替换掉<img src="http://upload-images.jianshu.io/upload_images/1803339-8d228ab56eb77f43.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="" loading="lazy"></p>
<h2>五、在 iOS 项目中集成 ijkplayer</h2>
<p>新建工程, 导入合并后的 IJKMediaFramework.framework 以及相关依赖框架以及相关依赖框架,如下图:<br>
<img src="https://storage1.cuntuku.com/2017/08/31/5.png" alt="5.png" loading="lazy"></p>
<p>导入框架后在 ViewController.m 中进行 buid，如果成功，说明集成成功。然后可以在控制器中写一个 Demo 测试<img src="https://storage1.cuntuku.com/2017/08/31/Snip20170831_10.png" alt="Snip20170831_10.png" loading="lazy"><br>
可以是 mp4 格式，也可以是 m3u8,rtmp,hls 等流媒体</p>
<blockquote>
<p><a href="https://github.com/OrageKK/ijkPlayerDemo" target="_blank" rel="noopener noreferrer">demo 地址</a></p>
</blockquote>
]]></content:encoded>
      <enclosure url="https://storage2.cuntuku.com/2017/08/31/ijkplayer.png" type="image/png"/>
    </item>
    <item>
      <title>iOS - Image compression algorithm</title>
      <link>https://oragekk.me/posts/iOS/tool/%E5%9B%BE%E7%89%87%E4%B8%8A%E4%BC%A0%E5%8E%8B%E7%BC%A9%E7%AE%97%E6%B3%95.html</link>
      <guid>https://oragekk.me/posts/iOS/tool/%E5%9B%BE%E7%89%87%E4%B8%8A%E4%BC%A0%E5%8E%8B%E7%BC%A9%E7%AE%97%E6%B3%95.html</guid>
      <source url="https://oragekk.me/rss.xml">iOS - Image compression algorithm</source>
      <description>由于最近公司在做图片相册选择上传的功能，对于图片的压缩算法这里我借鉴了 ochina 的 ios 端 App。其中有涉及到图片压缩的算法，这里贴出来留待后用; GACompressionPicHandle.h GACompressionPicHandle.m</description>
      <category>iOS</category>
      <pubDate>Fri, 30 Dec 2016 16:09:45 GMT</pubDate>
      <content:encoded><![CDATA[<figure><img src="https://zero-space.s3.amazonaws.com/photos/bf30834c-a9de-41f4-9f39-4f44c2f2ff13x840.jpg" alt="" tabindex="0" loading="lazy"><figcaption></figcaption></figure>
<blockquote>
<p>由于最近公司在做图片相册选择上传的功能，对于图片的压缩算法这里我借鉴了 ochina 的 ios 端 App。其中有涉及到图片压缩的算法，这里贴出来留待后用;</p>
</blockquote>
<h3>GACompressionPicHandle.h</h3>
<div class="language-objc line-numbers-mode" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    	#import</span><span style="color:#98C379;--shiki-dark:#98C379"> &lt;Foundation/Foundation.h&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    	#import</span><span style="color:#98C379;--shiki-dark:#98C379"> &lt;UIKit/UIKit.h&gt;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    	#import</span><span style="color:#98C379;--shiki-dark:#98C379"> &lt;CoreGraphics/CoreGraphics.h&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    	@class</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> GACompressionPicHandle;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    	@protocol</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> GACompressionPicHandleDelegate</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> &lt;NSObject&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    	- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)CompressionPicHandle:(GACompressionPicHandle* )handle</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      			CompressionFailureInfo:(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">* )info;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      	@end</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      	#define</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> CompressionMax_W</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1224</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      	#define</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> CompressionMax_Size</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 300</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> * </span><span style="color:#D19A66;--shiki-dark:#D19A66">1024</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">      	/** OSChina 后台限制上传图片的大小*/</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      	#define</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> stand_size</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1024</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> * </span><span style="color:#D19A66;--shiki-dark:#D19A66">800</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      	#define</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> min_compressionRatio</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0.3</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">      	@interface</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> GACompressionPicHandle</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> : </span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSObject</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      	+ (</span><span style="color:#C678DD;--shiki-dark:#C678DD">instancetype</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)shareCompressionPicHandle;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    @property</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#C678DD;--shiki-dark:#C678DD">nonatomic</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#C678DD;--shiki-dark:#C678DD">weak</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#C678DD;--shiki-dark:#C678DD">id</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">&lt;GACompressionPicHandleDelegate&gt; delegate;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    	/** 分辨率越小反而越占用时间 建议分辨率高的图片使用*/</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    - (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">* )scaleToSize:(CGSize)size</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                   image:(UIImage* )picture;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">         /** 受到宽高比例问题 越接近正方形所用的时间越小 */</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    - (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)imageByScalingAndCroppingForSize:(	CGSize)targetSize image:(UIImage *)image;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    	@end</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>GACompressionPicHandle.m</h3>
<div class="language-objc line-numbers-mode" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">#import</span><span style="color:#98C379;--shiki-dark:#98C379"> "GACompressionPicHandle.h"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">@implementation</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> GACompressionPicHandle</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">static</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> GACompressioinPicHandle*_shareCompressionPicHandle;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">+ (</span><span style="color:#C678DD;--shiki-dark:#C678DD">instancetype</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">shareCompressionPicHandle</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    static</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> dispatch_once_t onceToken;</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    dispatch_once</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(&amp;onceToken, ^{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        _shareCompressionPicHandle </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> 					[[GACompressionPicHandle </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">alloc</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]init];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _shareCompressionPicHandle;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">* )</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">scaleToSize:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(CGSize)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">sizeimage</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(UIImage* )</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">picture</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat width </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> CGImageGetWidth</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">picture</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">CGImage</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat height </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> CGImageGetHeight</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">picture</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">CGImage</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    float</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> verticalRadio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> size</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">height</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> * </span><span style="color:#D19A66;--shiki-dark:#D19A66">1.0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> / height;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    float</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> horizontalRadio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> size</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">width</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> * </span><span style="color:#D19A66;--shiki-dark:#D19A66">1.0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> / width;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    float</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> radio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(verticalRadio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &amp;&amp;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> horizontalRadio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    radio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> verticalRadio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> horizontalRadio </span><span style="color:#C678DD;--shiki-dark:#C678DD">?</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> horizontalRadio </span><span style="color:#C678DD;--shiki-dark:#C678DD">:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> verticalRadio;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    radio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> verticalRadio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&lt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> horizontalRadio </span><span style="color:#C678DD;--shiki-dark:#C678DD">?</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> verticalRadio </span><span style="color:#C678DD;--shiki-dark:#C678DD">:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> horizontalRadio;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    width </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> width * radio;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    height </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> height * radio;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    int</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> xPos </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">size</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">width</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> - width) / </span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    int</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> yPos </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">size</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">height</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> - height) / </span><span style="color:#D19A66;--shiki-dark:#D19A66">2</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    UIGraphicsBeginImageContext</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(size);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    [picture </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">drawInRect:CGRectMake</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(xPos, yPos, width, height)];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    UIImage* scaledImage </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> UIGraphicsGetImageFromCurrentImageContext</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    UIGraphicsEndImageContext</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">* scaledImageData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> UIImageJPEGRepresentation</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(scaledImage, </span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat compressionRatio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0.9</span><span style="color:#E06C75;--shiki-dark:#E06C75">f</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">* tagerImageData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> scaledImageData;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">    NSLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"tagerImageData.length : </span><span style="color:#D19A66;--shiki-dark:#D19A66">%lu</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,(</span><span style="color:#C678DD;--shiki-dark:#C678DD">unsigned</span><span style="color:#C678DD;--shiki-dark:#C678DD"> long</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">tagerImageData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    while</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">tagerImageData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> stand_size </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&amp;&amp;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> compressionRatio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (compressionRatio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&lt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> min_compressionRatio) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> ([_delegate </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">respondsToSelector:</span><span style="color:#C678DD;--shiki-dark:#C678DD">@selector(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">CompressionPicHandle:CompressionFailureInfo:</span><span style="color:#C678DD;--shiki-dark:#C678DD">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]) {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                [_delegate </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">CompressionPicHandle:</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> CompressionFailureInfo:</span><span style="color:#98C379;--shiki-dark:#98C379">@"图片过大"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        @autoreleasepool {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">            NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">* newCompressionData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> UIImageJPEGRepresentation</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(scaledImage, compressionRatio);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            tagerImageData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> newCompressionData;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        compressionRatio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> compressionRatio - </span><span style="color:#D19A66;--shiki-dark:#D19A66">0.12</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">        NSLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"tagerImageData.length : </span><span style="color:#D19A66;--shiki-dark:#D19A66">%lu</span><span style="color:#98C379;--shiki-dark:#98C379"> , compressionRatio : </span><span style="color:#D19A66;--shiki-dark:#D19A66">%lf</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">tagerImageData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,compressionRatio);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">    NSLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"compressionRatio : </span><span style="color:#D19A66;--shiki-dark:#D19A66">%lf</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,compressionRatio);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> tagerImageData;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">imageByScalingAndCroppingForSize:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(CGSize)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">targetSize</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> image:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(UIImage *)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF;font-style:italic;--shiki-dark-font-style:italic">image</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    UIImage *sourceImage </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> image;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    UIImage *newImage </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGSize imageSize </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> sourceImage</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">size</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat width </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> imageSize</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">width</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat height </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> imageSize</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">height</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat targetWidth </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> targetSize</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">width</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat targetHeight </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> targetSize</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">height</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat scaleFactor </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0.0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat scaledWidth </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> targetWidth;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat scaledHeight </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> targetHeight;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGPoint thumbnailPoint </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> CGPointMake</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0.0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#D19A66;--shiki-dark:#D19A66">0.0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">CGSizeEqualToSize</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(imageSize, targetSize) </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#D19A66;--shiki-dark:#D19A66"> NO</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat widthFactor </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> targetWidth / width;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat heightFactor </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> targetHeight / height;</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (widthFactor </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> heightFactor){</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    scaleFactor </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> widthFactor; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// scale to fit height</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        scaleFactor </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> heightFactor; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// scale to fit width</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    scaledWidth</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> width * scaleFactor;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    scaledHeight </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> height * scaleFactor;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (widthFactor </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> heightFactor) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        thumbnailPoint</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">y</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (targetHeight - scaledHeight) * </span><span style="color:#D19A66;--shiki-dark:#D19A66">0.5</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    } </span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#C678DD;--shiki-dark:#C678DD"> if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (widthFactor </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&lt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> heightFactor){</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">        thumbnailPoint</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">x</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (targetWidth - scaledWidth) * </span><span style="color:#D19A66;--shiki-dark:#D19A66">0.5</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    UIGraphicsBeginImageContext</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(targetSize); </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// this will crop</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGRect thumbnailRect </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> CGRectZero;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    thumbnailRect</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">origin</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> thumbnailPoint;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    thumbnailRect</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">size</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">width</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> scaledWidth;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    thumbnailRect</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">size</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">height</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> scaledHeight;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    [sourceImage </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">drawInRect:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">thumbnailRect];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    newImage </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> UIGraphicsGetImageFromCurrentImageContext</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(newImage </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#D19A66;--shiki-dark:#D19A66"> nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">    UIGraphicsEndImageContext</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">* scaledImageData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> UIImageJPEGRepresentation</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(newImage, </span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    CGFloat compressionRatio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0.9</span><span style="color:#E06C75;--shiki-dark:#E06C75">f</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">* tagerImageData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> scaledImageData;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">    NSLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"tagerImageData.length : </span><span style="color:#D19A66;--shiki-dark:#D19A66">%lu</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,(</span><span style="color:#C678DD;--shiki-dark:#C678DD">unsigned</span><span style="color:#C678DD;--shiki-dark:#C678DD"> long</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">tagerImageData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    while</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">tagerImageData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> stand_size </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&amp;&amp;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> compressionRatio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (compressionRatio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&lt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> min_compressionRatio) {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> ([_delegate </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">respondsToSelector:</span><span style="color:#C678DD;--shiki-dark:#C678DD">@selector(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">CompressionPicHandle:CompressionFailureInfo:</span><span style="color:#C678DD;--shiki-dark:#C678DD">)</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]) {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                [_delegate </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">CompressionPicHandle:</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> CompressionFailureInfo:</span><span style="color:#98C379;--shiki-dark:#98C379">@"图片过大"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        @autoreleasepool {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">            NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">* newCompressionData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> UIImageJPEGRepresentation</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(newImage, compressionRatio);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            tagerImageData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> newCompressionData;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        compressionRatio </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> compressionRatio - </span><span style="color:#D19A66;--shiki-dark:#D19A66">0.12</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">        NSLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"tagerImageData.length : </span><span style="color:#D19A66;--shiki-dark:#D19A66">%lu</span><span style="color:#98C379;--shiki-dark:#98C379"> , compressionRatio : </span><span style="color:#D19A66;--shiki-dark:#D19A66">%lf</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">tagerImageData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,compressionRatio);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#56B6C2;--shiki-dark:#56B6C2">    NSLog</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#98C379;--shiki-dark:#98C379">@"compressionRatio : </span><span style="color:#D19A66;--shiki-dark:#D19A66">%lf</span><span style="color:#98C379;--shiki-dark:#98C379">"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,compressionRatio);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> tagerImageData;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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://zero-space.s3.amazonaws.com/photos/bf30834c-a9de-41f4-9f39-4f44c2f2ff13x840.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>iOS 配置https</title>
      <link>https://oragekk.me/posts/iOS/tool/%E9%85%8D%E7%BD%AEhttps.html</link>
      <guid>https://oragekk.me/posts/iOS/tool/%E9%85%8D%E7%BD%AEhttps.html</guid>
      <source url="https://oragekk.me/rss.xml">iOS 配置https</source>
      <description>昨天试验了 iOS 11 beta6 发现原有的 https 自建证书不能使用，可能是新版本要对 ATS 加强验证，之前一直说的要全面 https 估计在不久的将来就要来临，未接入的可能要像 Apple 说的不允许上架。所以把配置过程记录在此 要求 启用 ATS 必须符合以下标准，不满足条件的 HTTPS 证书，ATS 都会拒绝链接： 服务器所有的链接...</description>
      <category>iOS</category>
      <pubDate>Wed, 16 Aug 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>昨天试验了 iOS 11 beta6 发现原有的 https 自建证书不能使用，可能是新版本要对 ATS 加强验证，之前一直说的要全面 https 估计在不久的将来就要来临，未接入的可能要像 Apple 说的不允许上架。所以把配置过程记录在此</p>
</blockquote>
<h2>要求</h2>
<p>启用 ATS 必须符合以下标准，不满足条件的 HTTPS 证书，ATS 都会拒绝链接：</p>
<ul>
<li>服务器所有的链接使用 TLS1.2 以上版本</li>
<li>HTTPS 证书必须使用 SHA 256 以上哈希算法签名</li>
<li>HTTPS 证书必须使用 RAS 2048 位或 ECC 356 位以上公钥算法</li>
<li>使用前向加密技术</li>
</ul>
<h2>AFSecurityPolicy 相关的配置</h2>
<ul>
<li>
<p>SSLPinningMode<br>
SSLPinningMode 定义了 https 连接时，如何校验服务器端给予的证书</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">typedef</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> NS_ENUM</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(NSUInteger, AFSSLPinningMode){</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">AFSSLPinningModeNone,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">AFSSLPinningModePublicKey,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">AFSSLPinningModeCertificate,</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">};</span></span></code></pre>
</div><p>AFSSLPinningModeNone: 代表客户端无条件地信任服务器端返回的证书。</p>
<p>AFSSLPinningModePublicKey: 代表客户端会将服务器端返回的证书与本地保存的证书中，PublicKey 的部分进行校验；如果正确，才继续进行。</p>
<p>AFSSLPinningModeCertificate: 代表客户端会将服务器端返回的证书和本地保存的证书中的所有内容，包括 PublicKey 和证书部分，全部进行校验；如果正确，才继续进行。</p>
</li>
<li>
<p>allowInvalidCertificates 是否支持自建证书默认 NO 改为 YES</p>
</li>
<li>
<p>validatesDomainName 是否进行域名验证 默认 YES 改为 NO</p>
</li>
</ul>
<h2>客户端配置</h2>
<ul>
<li>
<p>首先导入证书到项目<br>
<a href="https://cuntuku.com/image/4xc6p" target="_blank" rel="noopener noreferrer"><img src="https://storage1.cuntuku.com/2017/08/16/daoru.md.png" alt="daoru.md.png" loading="lazy"></a><br>
<a href="https://cuntuku.com/image/4xyN0" target="_blank" rel="noopener noreferrer"><img src="https://storage2.cuntuku.com/2017/08/16/phases.md.png" alt="phases.md.png" loading="lazy"></a></p>
</li>
<li>
<p>配置 info.plist 文件<br>
<a href="https://cuntuku.com/image/4xA2z" target="_blank" rel="noopener noreferrer"><img src="https://storage1.cuntuku.com/2017/08/16/infoplist.md.png" alt="infoplist.md.png" loading="lazy"></a></p>
</li>
<li>
<p>网络请求配置(AFN)</p>
</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *cerPath </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSBundle</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> mainBundle</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">pathForResource:</span><span style="color:#98C379;--shiki-dark:#98C379">@"UpopRsaCert"</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> ofType:</span><span style="color:#98C379;--shiki-dark:#98C379">@"cer"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//证书的路径</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *certData </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSData</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> dataWithContentsOfFile:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">cerPath];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *cerPath2 </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSBundle</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> mainBundle</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">pathForResource:</span><span style="color:#98C379;--shiki-dark:#98C379">@"verify_sign_acp"</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> ofType:</span><span style="color:#98C379;--shiki-dark:#98C379">@"cer"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//证书的路径</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *certData2 </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSData</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> dataWithContentsOfFile:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">cerPath2];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *cerPath3 </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSBundle</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> mainBundle</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">pathForResource:</span><span style="color:#98C379;--shiki-dark:#98C379">@"myWebsite"</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> ofType:</span><span style="color:#98C379;--shiki-dark:#98C379">@"cer"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">//证书的路径</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *certData3 </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSData</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> dataWithContentsOfFile:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">cerPath3];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSSet</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *cerArray </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSSet</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> setWithObjects:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">certData,certData2,certData3, </span><span style="color:#D19A66;--shiki-dark:#D19A66">nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">securityPolicy.pinnedCertificates </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> cerArray;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[httpManager </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">setSecurityPolicy:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">securityPolicy];</span></span></code></pre>
</div>]]></content:encoded>
      <enclosure url="https://storage1.cuntuku.com/2017/08/16/daoru.md.png" type="image/png"/>
    </item>
    <item>
      <title>Cell的accessoryType属性标记单元格之后，出现的重用问题</title>
      <link>https://oragekk.me/posts/iOS/ui/cell%E5%A4%8D%E7%94%A8-accessoryType%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95.html</link>
      <guid>https://oragekk.me/posts/iOS/ui/cell%E5%A4%8D%E7%94%A8-accessoryType%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95.html</guid>
      <source url="https://oragekk.me/rss.xml">Cell的accessoryType属性标记单元格之后，出现的重用问题</source>
      <description>今天项目里出现一个问题，就是做一个列表选择，然后点击导航栏的确定按钮返回上级界面，并把选择的 cell 数据传递到上级界面。再使用 accessoryType 属性标记单元格之后会出现重用问题。 解决办法 把 tableView 的 allowsMultipleSelection 属性设为了 YES； 在 didSelectRowAtIndexPath...</description>
      <category>iOS</category>
      <pubDate>Mon, 13 Feb 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>今天项目里出现一个问题，就是做一个列表选择，然后点击导航栏的确定按钮返回上级界面，并把选择的 cell 数据传递到上级界面。再使用 accessoryType 属性标记单元格之后会出现重用问题。</p>
</blockquote>
<h2>解决办法</h2>
<ul>
<li>
<p>把 tableView 的 allowsMultipleSelection 属性设为了 YES；</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">_tableView.allowsMultipleSelection </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> YES</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span></code></pre>
</div></li>
<li>
<p>在 didSelectRowAtIndexPath 和 didDeselectRowAtIndexPath 方法里面使用了如下方法实现了点击单元格然后用 check mark 标记的方式。</p>
</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-(</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSIndexPath</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)indexPath {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">      UITableViewCell *cell </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">cellForRowAtIndexPath:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">indexPath];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">	  cell</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">accessoryType</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> UITableViewCellAccessoryCheckmark;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-(</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSIndexPath</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)indexPath {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	[tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">cellForRowAtIndexPath:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">indexPath].</span><span style="color:#E06C75;--shiki-dark:#E06C75">accessoryType</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> UITableViewCellAccessoryNone;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div><h3>重点来了 两种思路</h3>
<ul>
<li>记录选择的 indexpath</li>
</ul>
<div class="language-objc line-numbers-mode" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic"> 	// 1.设一个NSMutableArray属性，元素个数跟你的_dataArray一样，初始化里面存的都是0。</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">	 NSMutableArray</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">* selectionArray </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSMutableArray</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> array</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">	 for</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (NSInteger i </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">; i </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&lt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> _dataArray.count; i</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">++</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    	[selectionArray </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">addObject:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">@(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)]; </span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 0 表示未选中，1 表示选中了</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	 }</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">	 self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.selectionArray </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> selectionArray;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">   // 2.在 didSelectRowAtIndexPath:里</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	 [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.selectionArray </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">replaceObjectAtIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">indexPath.row </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">withObject:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">@(</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	 [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">reloadData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">   // 3.在 didDeselectRowAtIndexPath里：</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	 [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.selectionArray </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">replaceObjectAtIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">indexPath.row </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">withObject:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">@(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	 [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">reloadData</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">   // 4.在 cellForRow里：</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	UITableViewCell *cell </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">dequeueReusableCellWithIdentifier:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">identifier];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	cell.textLabel.text </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E06C75;--shiki-dark:#E06C75"> _dataArray</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">indexPath</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">row</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	NSInteger selected </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">selectionArray</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">indexPath</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">row</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">] </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">IntegerValue</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">	if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (selected </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">==</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    cell</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">accessoryType</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> UITableViewCellAccessoryNone;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	} </span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    	cell</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">accessoryType</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> UITableViewCellAccessoryCheckmark;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	}</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">	return</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> cell;</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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><ul>
<li>
<p>利用 cell 的 selected 属性</p>
<ul>
<li>继承 UITableViewCell，在 setSeleted:animated:方法里面，根据选择状态，修改 accessoryType</li>
</ul>
</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	 - (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)setSelected:(</span><span style="color:#C678DD;--shiki-dark:#C678DD">BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)selected animated:(</span><span style="color:#C678DD;--shiki-dark:#C678DD">BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)animated {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    	 [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">super</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> setSelected:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">selected </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">animated:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">animated];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">		 self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">accessoryType</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> selected</span><span style="color:#C678DD;--shiki-dark:#C678DD">?</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">UITableViewCellAccessoryCheckmark</span><span style="color:#C678DD;--shiki-dark:#C678DD">:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">UITableViewCellAccessoryNone;</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">		 // Configure the view for the selected state</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	 }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">	// 在 cellForRow里：</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	cell.accessoryType </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> cell.selected</span><span style="color:#C678DD;--shiki-dark:#C678DD">?</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">UITableViewCellAccessoryCheckmark</span><span style="color:#C678DD;--shiki-dark:#C678DD">:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">UITableViewCellAccessoryNone;</span></span></code></pre>
</div><h3>至此已完美解决因为复用所导致的问题</h3>
<div class="language-objc line-numbers-mode" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">	// 设置tableView可不可以选中</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView.allowsSelection </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> NO</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 允许tableview多选</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView.allowsMultipleSelection </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> YES</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 编辑模式下是否可以选中</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView.allowsSelectionDuringEditing </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> NO</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 编辑模式下是否可以多选</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView.allowsMultipleSelectionDuringEditing </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#D19A66;--shiki-dark:#D19A66"> YES</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 获取被选中的所有行</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">     [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">indexPathsForSelectedRows</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    // 获取当前可见的行</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">     [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">indexPathsForVisibleRows</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>TableView性能优化</title>
      <link>https://oragekk.me/posts/iOS/ui/tableview-radius.html</link>
      <guid>https://oragekk.me/posts/iOS/ui/tableview-radius.html</guid>
      <source url="https://oragekk.me/rss.xml">TableView性能优化</source>
      <description>此子必成大器 本文介绍内容主要是 tableView 的性能优化之不使用 cornerRadius 设置图片圆角 有人问我为什么 tableView 滑动不流畅，甚至闪退，其实和 cell 中的圆角头像使用了 cornerRadius 有关 优化点 行高一定要缓存 不要动态创建子视图 所有子视图都要预先创建 如果不需要显示可以设置 hidden 所有的...</description>
      <category>iOS</category>
      <pubDate>Mon, 26 Dec 2016 13:39:59 GMT</pubDate>
      <content:encoded><![CDATA[<h2><img src="http://upload-images.jianshu.io/upload_images/2076247-98a0c6450c69fa62.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="此子必成大器" loading="lazy"></h2>
<blockquote>
<p>本文介绍内容主要是 tableView 的性能优化之不使用 cornerRadius 设置图片圆角</p>
</blockquote>
<p>有人问我为什么 tableView 滑动不流畅，甚至闪退，其实和 cell 中的圆角头像使用了 cornerRadius 有关</p>
<h3>优化点</h3>
<ul>
<li>
<p>行高一定要缓存</p>
</li>
<li>
<p>不要动态创建子视图</p>
</li>
<li>
<p>所有子视图都要预先创建</p>
</li>
<li>
<p>如果不需要显示可以设置 hidden</p>
</li>
<li>
<p>所有的子视图都应该添加到 &nbsp;<code>contentView</code>上</p>
</li>
<li>
<p>所有的子视图都必须要指定颜色</p>
</li>
<li>
<p>不要动态的修改 cornerRadius 之类的图层渲染相关属性</p>
</li>
<li>
<p>使用颜色不要带透明度，此处我们可以使用模拟器中的混合模式去检测，如果如下图所示出现红色，除了 UILabel 之外，其他的我们都应该尽量去处理</p>
</li>
<li>
<p>cell 栅格化</p>
<pre><code>``cell.layer.shouldRasterize = YES;``

``cell.layer.rasterizationScale = [UIScreen mainScreen].scale;``
</code></pre>
</li>
<li>
<p>异步绘制<br>
<code>// 异步绘制   layer.drawsAsynchronously = YES;</code></p>
</li>
</ul>
<figure><img src="http://upload-images.jianshu.io/upload_images/2076247-b9d269a0daab047f.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="Color Blended Layers模式下" tabindex="0" loading="lazy"><figcaption>Color Blended Layers模式下</figcaption></figure>
<p>下图是使用了 CornerRadius 设置圆角之后 Color Misaligned Images 检测效果<br>
<img src="http://upload-images.jianshu.io/upload_images/2076247-a60b840cceed974b.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240" alt="Color Misaligned Images模式下" loading="lazy"></p>
<p>此处使用了一张 800*** 800 的图片设置在一个 200*200 的 ImageView 上，没有做任何特殊处理</p>
]]></content:encoded>
      <enclosure url="http://upload-images.jianshu.io/upload_images/2076247-98a0c6450c69fa62.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" type="image/"/>
    </item>
    <item>
      <title>textfield限制输入字符</title>
      <link>https://oragekk.me/posts/iOS/ui/textfiled%E9%99%90%E5%88%B6%E8%BE%93%E5%85%A5%E5%AD%97%E7%AC%A6.html</link>
      <guid>https://oragekk.me/posts/iOS/ui/textfiled%E9%99%90%E5%88%B6%E8%BE%93%E5%85%A5%E5%AD%97%E7%AC%A6.html</guid>
      <source url="https://oragekk.me/rss.xml">textfield限制输入字符</source>
      <description>记录一下限制输入字符的判断。不仅局限于中文或英文 首先在 ViewDidLoad 中注册通知 下面是判断逻辑 由于需求有中文键盘下的字母数字输入，所以其中那部分判断如不需要可以去除</description>
      <category>iOS</category>
      <pubDate>Sun, 21 May 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>记录一下限制输入字符的判断。不仅局限于中文或英文</p>
</blockquote>
<ul>
<li>首先在 ViewDidLoad 中注册通知</li>
</ul>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSNotificationCenter</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> defaultCenter</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]addObserver:</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> selector:</span><span style="color:#C678DD;--shiki-dark:#C678DD">@selector(</span><span style="color:#61AFEF;--shiki-dark:#61AFEF">textFieldEditChanged:</span><span style="color:#C678DD;--shiki-dark:#C678DD">)</span></span>
<span class="line"><span style="color:#61AFEF;--shiki-dark:#61AFEF">	name:</span><span style="color:#98C379;--shiki-dark:#98C379">@"UITextFieldTextDidChangeNotification"</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> object:</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.userTF];</span></span></code></pre>
</div><ul>
<li>下面是判断逻辑 由于需求有中文键盘下的字母数字输入，所以其中那部分判断如不需要可以去除</li>
</ul>
<div class="language-objc line-numbers-mode" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 输入字符判断</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">-(</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)textFieldEditChanged:(</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSNotification</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *)obj {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    UITextField *textField </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (UITextField *)</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">obj</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">object</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *toBeString </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#E5C07B;--shiki-dark:#E5C07B"> textField</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">text</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    NSString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *lang </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [textField.textInputMode </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">primaryLanguage</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> ([lang </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">isEqualToString:</span><span style="color:#98C379;--shiki-dark:#98C379">@"zh-Hans"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">])</span><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">// 简体中文输入</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    	{</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        //获取高亮部分</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        UITextRange *selectedRange </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [textField </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">markedTextRange</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        UITextPosition *position </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [textField </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">positionFromPosition:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">selectedRange.start </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">offset:</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">        // 没有高亮选择的字，则对已输入的文字进行字数统计和限制</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">!</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">position </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">||</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> !</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">selectedRange)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        {</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">toBeString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> MAX_CHINESE_LENGTH)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                NSRange rangeIndex </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">rangeOfComposedCharacterSequenceAtIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">MAX_CHINESE_LENGTH];</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">                if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">rangeIndex</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ==</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                    textField</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">text</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">substringToIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">MAX_CHINESE_LENGTH];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">                else</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                    NSRange rangeRange </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">rangeOfComposedCharacterSequencesForRange:</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">NSMakeRange</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, MAX_CHINESE_LENGTH)];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                    textField</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">text</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">substringWithRange:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">rangeRange];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">toBeString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> MAX_ENGLISH_LENGTH)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                NSRange rangeIndex </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">rangeOfComposedCharacterSequenceAtIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">MAX_ENGLISH_LENGTH];</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">                if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">rangeIndex</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ==</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                    textField</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">text</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">substringToIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">MAX_ENGLISH_LENGTH];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                }</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">                else</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                    NSRange rangeRange </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">rangeOfComposedCharacterSequencesForRange:</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">NSMakeRange</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, MAX_ENGLISH_LENGTH)];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                    textField</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">text</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">substringWithRange:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">rangeRange];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">    	// 中文输入法以外的直接对其统计限制即可，不考虑其他语种情况</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">    	else</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    	{</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">        	if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">toBeString</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> &gt;</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> MAX_ENGLISH_LENGTH)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        		{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            		NSRange rangeIndex </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">rangeOfComposedCharacterSequenceAtIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">MAX_ENGLISH_LENGTH];</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            		if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">rangeIndex</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">length</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> ==</span><span style="color:#D19A66;--shiki-dark:#D19A66"> 1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            		{</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                		textField</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">text</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">substringToIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">MAX_ENGLISH_LENGTH];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            		}</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">            		else</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            		{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">                NSRange rangeRange </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">rangeOfComposedCharacterSequencesForRange:</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">NSMakeRange</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, MAX_ENGLISH_LENGTH)];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">                textField</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">text</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [toBeString </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">substringWithRange:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">rangeRange];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">            		}</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">        		}</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">    		}</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">		}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>iOS timelineLogistics</title>
      <link>https://oragekk.me/posts/iOS/ui/timelineLogistics.html</link>
      <guid>https://oragekk.me/posts/iOS/ui/timelineLogistics.html</guid>
      <source url="https://oragekk.me/rss.xml">iOS timelineLogistics</source>
      <description>timelineLogistics 是模仿淘宝物流信息时间轴界面的自定义 View 效果 MarkdownMarkdown 使用正则表达式判断字符是否为电话号码，用 YYLable 进行富文本电话拨打 使用 引入 Masonry,YYkit 库 使用了 MAsonry 进行布局 使用了 YYkit 中的 YYLable 进行富文本电话号码点击拨打电话 ...</description>
      <category>iOS</category>
      <pubDate>Wed, 12 Jul 2017 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p><strong>timelineLogistics</strong> 是模仿淘宝物流信息时间轴界面的自定义 View</p>
</blockquote>
<h2>效果</h2>
<figure><img src="http://i4.piimg.com/1949/af2a87e889d29664.png" alt="Markdown" tabindex="0" loading="lazy"><figcaption>Markdown</figcaption></figure>
<blockquote>
<p>使用正则表达式判断字符是否为电话号码，用 YYLable 进行富文本电话拨打</p>
</blockquote>
<h2>使用</h2>
<ul>
<li>
<p>引入 Masonry,YYkit 库</p>
<ul>
<li>使用了 MAsonry 进行布局</li>
<li>使用了 YYkit 中的 YYLable 进行富文本电话号码点击拨打电话</li>
</ul>
</li>
<li>
<p>初始化数组</p>
<div class="language-objc line-numbers-mode" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  NSArray</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *titleArr </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSArray</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> arrayWithObjects:</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">               @"[北京通州区杨庄公司锦园服务部]快件已被27号楼e站代签收"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">               @"[北京通州区杨庄公司]到达目的地网店，快件将很快进行派送"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> ,</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">               @"[北京通州区杨庄公司]进行派件扫描；派送业务员：周志军；联系电话：13522464946"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">               @"[北京分拨中心]在分拨中心进行卸车扫描"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">               @"[浙江杭州分拨中心]在分拨中心进行称重扫描"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">               @"[浙江杭州下城区三里亭公司]进行揽件扫描"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#D19A66;--shiki-dark:#D19A66">nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  NSArray</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *timeArr </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSArray</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> arrayWithObjects:</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">              @"2017-07-04 12:59:00"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">              @"2017-07-03 10:59:00"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">              @"2017-07-03 08:22:00"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">              @"2017-07-03 03:34:22"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">              @"2017-07-02 12:59:00"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="color:#98C379;--shiki-dark:#98C379">              @"2017-07-02 08:10:00"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">,</span><span style="color:#D19A66;--shiki-dark:#D19A66">nil</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">```</span></span></code></pre>
<div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="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>
<ul>
<li>
<p>转为模型</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">for</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (NSInteger i </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> titleArr.count-</span><span style="color:#D19A66;--shiki-dark:#D19A66">1</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;i</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">&gt;=</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> ; i</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">--</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  OKLogisticModel * model </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[OKLogisticModel </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">alloc</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]init];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  model</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">dsc</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [titleArr </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">objectAtIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">i];</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">  model</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">date</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [timeArr </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">objectAtIndex:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">i];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.dataArry </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">addObject:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">model];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div></li>
<li>
<p>初始化控制器</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">OKLogisticsView * logis </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [[OKLogisticsView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">alloc</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">]initWithDatas:</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.dataArry];</span></span>
<span class="line"><span style="color:#7F848E;--shiki-dark:#7F848E;font-style:italic;--shiki-dark-font-style:italic">  // 给headView赋值</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  logis.wltype</span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379">@"已签收"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  logis.number </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"3908723967437"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  logis.company </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"韵达快运"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  logis.phone </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"400-821-6789"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  logis.imageUrl </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#98C379;--shiki-dark:#98C379"> @"http://pic40.nipic.com/20140420/12064170_201114370112_2.jpg"</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  logis.frame </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> CGRectMake</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">(</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, </span><span style="color:#D19A66;--shiki-dark:#D19A66">64</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">, OKScreenWidth, OKScreenHeight-</span><span style="color:#D19A66;--shiki-dark:#D19A66">64</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">  [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.view </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">addSubview:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">logis];</span></span></code></pre>
</div></li>
</ul>
<blockquote>
<p><a href="https://github.com/OrageKK/timelineLogistics" target="_blank" rel="noopener noreferrer">Demo下载</a>,如果对你有帮助麻烦点个Star</p>
</blockquote>
]]></content:encoded>
      <enclosure url="http://i4.piimg.com/1949/af2a87e889d29664.png" type="image/png"/>
    </item>
    <item>
      <title>优雅的实现TableViewCell单选</title>
      <link>https://oragekk.me/posts/iOS/ui/%E4%BC%98%E9%9B%85%E7%9A%84cell%E5%8D%95%E9%80%89.html</link>
      <guid>https://oragekk.me/posts/iOS/ui/%E4%BC%98%E9%9B%85%E7%9A%84cell%E5%8D%95%E9%80%89.html</guid>
      <source url="https://oragekk.me/rss.xml">优雅的实现TableViewCell单选</source>
      <description>最近有些忙，好久没有写博客了。 分享一个 cell 做单选的思路 可行的思路 在 tableview 的控制器中设立一个变量记录选择的 indexPath，点击 cell 之后刷新表格来和现有 indexPath 对比 和第一种大同小异，做一个和 dataArr 同样的数组，记录 indexPath，循环确定当前 cell 是否为选中 cell 利用 ...</description>
      <category>iOS</category>
      <pubDate>Fri, 12 Jan 2018 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>
<p>最近有些忙，好久没有写博客了。<br>
分享一个 cell 做单选的思路</p>
</blockquote>
<h2>可行的思路</h2>
<ol>
<li>在 tableview 的控制器中设立一个变量记录选择的 indexPath，点击 cell 之后刷新表格来和现有 indexPath 对比</li>
<li>和第一种大同小异，做一个和 dataArr 同样的数组，记录 indexPath，循环确定当前 cell 是否为选中 cell</li>
<li>利用 cell 的<code>- (void)setSelected:(BOOL)selected animated:(BOOL)animated</code>方法</li>
</ol>
<h2>利弊分析</h2>
<ol>
<li>前两种，都需要在<code>didSelectRowAtIndexPath</code>方法中来刷新表格，可能会造成不必要的滑动，而且需要单独的外在属性来记录这个选择</li>
<li>第三种方法是我要介绍的，不用任何外在属性，不用变量，不用数组。实现 cell、或 cell 中 Button 的单选。并且不会因为复用而造成位置错乱，如果要实现 cell 的多选可以参考我之前的文章<a href="http://oragekk.me/02-13-2017/cell%E5%A4%8D%E7%94%A8-accessoryType%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95.html" target="_blank" rel="noopener noreferrer">Cell 的 accessoryType 属性标记单元格之后，出现的重用问题</a></li>
</ol>
<h2>实现方式</h2>
<ol>
<li>
<p>如果要有默认选择在初始化 tableView 完成后写</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSIndexPath</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> *indexPath </span><span style="color:#56B6C2;--shiki-dark:#56B6C2">=</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> [</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">NSIndexPath</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> indexPathForRow:</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> inSection:</span><span style="color:#D19A66;--shiki-dark:#D19A66">0</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">selectRowAtIndexPath:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">indexPath </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">animated:</span><span style="color:#D19A66;--shiki-dark:#D19A66">YES</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> scrollPosition:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">UITableViewScrollPositionNone];</span></span></code></pre>
</div></li>
<li>
<p>然后在 cell 中实现<code>- (void)setSelected:(BOOL)selected animated:(BOOL)animated</code>方法</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">- (</span><span style="color:#C678DD;--shiki-dark:#C678DD">void</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)setSelected:(</span><span style="color:#C678DD;--shiki-dark:#C678DD">BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)selected animated:(</span><span style="color:#C678DD;--shiki-dark:#C678DD">BOOL</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">)animated {</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">super</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> setSelected:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">selected </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">animated:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">animated];</span></span>
<span class="line"><span style="color:#C678DD;--shiki-dark:#C678DD">	if</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> (selected) {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    	self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">selBtn</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">selected</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> YES</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	}</span><span style="color:#C678DD;--shiki-dark:#C678DD">else</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="color:#E5C07B;--shiki-dark:#E5C07B">    	self</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E5C07B;--shiki-dark:#E5C07B">selBtn</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">.</span><span style="color:#E06C75;--shiki-dark:#E06C75">selected</span><span style="color:#56B6C2;--shiki-dark:#56B6C2"> =</span><span style="color:#D19A66;--shiki-dark:#D19A66"> NO</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">	}</span></span>
<span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">}</span></span></code></pre>
</div></li>
<li>
<p>在<code>didSelectRowAtIndexPath</code>方法中给点击的 cell 手动选中，并不需要刷新表格</p>
<div class="language-objc" data-ext="objc" data-title="objc"><pre class="shiki shiki-themes one-dark-pro one-dark-pro vp-code" style="background-color:#282c34;--shiki-dark-bg:#282c34;color:#abb2bf;--shiki-dark:#abb2bf" tabindex="0"><code><span class="line"><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">[tableView </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">selectRowAtIndexPath:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">indexPath </span><span style="color:#61AFEF;--shiki-dark:#61AFEF">animated:</span><span style="color:#D19A66;--shiki-dark:#D19A66">YES</span><span style="color:#61AFEF;--shiki-dark:#61AFEF"> scrollPosition:</span><span style="color:#ABB2BF;--shiki-dark:#ABB2BF">UITableViewScrollPositionNone];</span></span></code></pre>
</div></li>
</ol>
<blockquote>
<p>至此结束，可以看一下效果<br>
<img src="https://storage4.cuntuku.com/2018/01/13/dvITJ.gif" alt="效果图.md.gif" loading="lazy"></p>
</blockquote>
]]></content:encoded>
      <enclosure url="https://storage4.cuntuku.com/2018/01/13/dvITJ.gif" type="image/gif"/>
    </item>
  </channel>
</rss>