<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://luoruihuan.top/</id>
  <title type="text">技术派罗恩</title>
  <subtitle type="text">AI 实战 · 全栈工程 · 工程师搞钱</subtitle>
  <updated>2026-10-30T13:00:00.000Z</updated>
  <author><name>技术派罗恩</name></author>
  <link rel="alternate" href="https://luoruihuan.top/"/>
  <link rel="self" href="https://luoruihuan.top/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.9</generator>
    <entry>
      <id>https://luoruihuan.top/posts/2026-10-22-geo-practice/</id>
      <title type="text">个人网站 GEO 实践：让 ChatGPT、Perplexity 引用你的内容</title>
      <published>2026-10-22T08:00:00.000Z</published>
      <updated>2026-10-22T08:00:00.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/2026-10-22-geo-practice/"/>
      <summary type="text">GEO（生成式引擎优化）不是玄学。以这个网站的真实配置为例，拆解 AI 爬虫矩阵、llms.txt、结构化数据和内容策略的完整落地。</summary>
      <content type="html"><![CDATA[<p>越来越多的人通过 ChatGPT、Perplexity、豆包获取信息，而不是 Google。让你的内容被这些 AI 引擎<strong>引用</strong>，就是 GEO（Generative Engine Optimization，生成式引擎优化）要解决的问题。</p>
<p>我自己在做 GEO 方向的产品（一个生成式推荐优化工作台），这个网站本身就是我的方法论的试验田。这篇文章拆解真实配置，所有代码你都可以在这个站的<a href="https://github.com/luoruihuan/luoruihuan.top" target="_blank">仓库</a>里找到。</p>
<section><h2>先搞清楚：AI 引擎的爬虫矩阵<a href="#先搞清楚ai-引擎的爬虫矩阵"><span>#</span></a></h2><p>这是 GEO 最容易踩坑的地方。每家 AI 公司都有<strong>多个爬虫</strong>，职责不同，而且 <strong>robots.txt 的规则组不继承通配符</strong>——你给 <code>User-agent: *</code> 放行了，不等于放行了它们。</p>

<table><thead><tr><th>类别</th><th>User-Agent</th><th>作用</th><th>策略</th></tr></thead><tbody><tr><td>搜索/引用</td><td><code>OAI-SearchBot</code></td><td>ChatGPT 搜索引用</td><td>放行</td></tr><tr><td>搜索/引用</td><td><code>Claude-SearchBot</code></td><td>Claude 搜索引用</td><td>放行</td></tr><tr><td>搜索/引用</td><td><code>PerplexityBot</code></td><td>Perplexity 引用</td><td>放行</td></tr><tr><td>搜索/引用</td><td><code>Googlebot</code></td><td>Google + AI Overviews</td><td>放行</td></tr><tr><td>训练</td><td><code>GPTBot</code></td><td>OpenAI 模型训练</td><td>个人站建议放行</td></tr><tr><td>训练</td><td><code>ClaudeBot</code></td><td>Anthropic 模型训练</td><td>个人站建议放行</td></tr><tr><td>训练</td><td><code>Google-Extended</code></td><td>Gemini 训练</td><td>个人站建议放行</td></tr></tbody></table><p>关键区分：<strong>搜索爬虫</strong>决定你能否被 AI 答案引用；<strong>训练爬虫</strong>决定你的内容进入模型语料。个人站我全部放行——被引用和被学习都是曝光。</p><p>落地成 <code>robots.txt</code>（这个站的真实配置，由 <a href="https://github.com/luoruihuan/luoruihuan.top/blob/main/src/pages/robots.txt.ts" target="_blank">robots.txt.ts</a> 动态生成）：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span># AI 搜索与引用爬虫</span></div></div><div><div><div>2</div></div><div><span>User-agent: OAI-SearchBot</span></div></div><div><div><div>3</div></div><div><span>Allow: /</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>User-agent: Claude-SearchBot</span></div></div><div><div><div>6</div></div><div><span>Allow: /</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>User-agent: PerplexityBot</span></div></div><div><div><div>9</div></div><div><span>Allow: /</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span># AI 训练爬虫</span></div></div><div><div><div>12</div></div><div><span>User-agent: GPTBot</span></div></div><div><div><div>13</div></div><div><span>Allow: /</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span># ...其余逐个声明，不要依赖通配</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>
<section><h2>llms.txt：做不做？<a href="#llmstxt做不做"><span>#</span></a></h2><p><code>llms.txt</code> 是 Answer.AI 在 2024 年提出的约定：站点根目录放一个 Markdown 文件，告诉 AI 你的网站是什么、有哪些重要内容。</p><p>诚实的现状：<strong>Google 官方明确表示忽略它</strong>，OpenAI 和 Anthropic 也没有官方文档承认读取它。但 Perplexity 等引擎的态度更开放，而且它的成本极低——一个静态文件而已。</p><p>我的选择：做。这是这个站的 <a href="/llms.txt">llms.txt</a>，本质是「给 AI 看的站点地图 + 简介」。即使暂时没引擎读它，也零副作用。</p></section>
<section><h2>技术层：让 AI 读得到你的内容<a href="#技术层让-ai-读得到你的内容"><span>#</span></a></h2><p>比 robots.txt 更基础的问题：<strong>AI 爬虫多数不执行 JavaScript</strong>。如果你的正文在 React 客户端渲染里，很多 AI 爬虫看到的是空页面。</p><p>这也是我选 Astro 做这个站的原因：构建产物是纯静态 HTML，正文直接在 HTML 源码里。验证方法很简单：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>curl</span><span> </span><span>-s</span><span> </span><span>https://luoruihuan.top</span><span> | </span><span>grep</span><span> </span><span>-c</span><span> </span><span>"技术派罗恩"</span></div></div><div><div><div>2</div></div><div><span># 输出 &gt; 0 说明正文对爬虫可见</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其余技术项一并列出：</p><ul>
<li><strong>sitemap.xml</strong>：带准确的 <code>lastmod</code>，不要全站统一时间戳</li>
<li><strong>RSS</strong>：AI 引擎和聚合器都爱它</li>
<li><strong>JSON-LD 结构化数据</strong>：<code>Person</code>、<code>Article</code>、<code>BreadcrumbList</code>，描述页面里真实存在的可见内容</li>
<li><strong>OG / Twitter 卡片</strong>：被分享时的门面</li>
<li><strong>真实 404</strong>、<strong>canonical URL</strong>、<strong>可见的发布/修改日期</strong></li>
</ul></section>
<section><h2>内容层：Google 官方口径说了什么<a href="#内容层google-官方口径说了什么"><span>#</span></a></h2><p>Google 的 <a href="https://developers.google.com/search/docs/fundamentals/ai-optimization-guide" target="_blank">AI 功能优化指南</a>值得细读，核心观点可以浓缩成一句：</p><blockquote><p><strong>独特的一手经验 &gt; 二手总结。</strong></p></blockquote><ul>
<li>第一人称的实操复盘（「我这样做，结果是 X」）比「7 个技巧」式水文更容易被 AI 引用——因为 AI 训练数据里后者已经泛滥</li>
<li>结构清晰：标题、段落、列表，让内容「可切片」</li>
<li>别为了关键词堆砌变体文章，Google 的 scaled content abuse 政策会收拾你</li>
</ul><p>这篇文章本身就在践行这一点：爬虫矩阵的坑（通配符不继承）是我在真实配置里踩的，不是抄来的。</p></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>GEO 的技术部分（爬虫矩阵、静态化、结构化数据）是一小时的活；真正决定被引用的是<strong>持续输出值得引用的一手内容</strong>。</p><p>技术是杠杆，内容是本金。这个站两者都有，接下来交给时间。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/2026-10-22-hello-world/</id>
      <title type="text">开篇：技术派罗恩上线，聊 AI 实战、全栈工程和工程师搞钱</title>
      <published>2026-10-22T07:00:00.000Z</published>
      <updated>2026-10-22T07:00:00.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/2026-10-22-hello-world/"/>
      <summary type="text">这个网站的由来：我是谁、为什么写、以及这个站本身是怎么搭起来的——Astro + Cloudflare Tunnel + GitHub Actions 的完整链路。</summary>
      <content type="html"><![CDATA[<p>你好，我是<strong>技术派罗恩</strong>。</p>
<p>94 年，全栈工程师，坐标苏州。专科起步、专升本、最后从苏州大学计算机毕业——这条路走得不算快，但 10 多年互联网研发干下来，每一步都算数。</p>
<section><h2>这个站聊什么<a href="#这个站聊什么"><span>#</span></a></h2><p>三件事：</p><ol>
<li><strong>AI 实战</strong>——我当前的主业是 AI Agent 开发，Node / Python 双栈。这里会有真实项目的一手复盘：什么方案跑通了、什么坑踩过了，而不是二手新闻的转述。</li>
<li><strong>全栈工程</strong>——从 React 前端、NestJS 后端到 Electron 桌面端、Docker 部署的完整链路。</li>
<li><strong>工程师搞钱</strong>——技术人怎么把技能变成产品、把产品变成收入。工程师的时间很贵，别只用来打工。</li>
</ol></section>
<section><h2>这个站本身就是一篇技术文章<a href="#这个站本身就是一篇技术文章"><span>#</span></a></h2><p>与其写一篇《如何搭建个人网站》，我直接把过程做成了这个站，几个关键决策：</p><ul>
<li><strong>框架选 Astro</strong>：纯静态 HTML 输出、默认零 JS、Lighthouse 接近满分。对内容站来说，这比任何 SPA 框架都合适——搜索引擎和 AI 爬虫拿到原始 HTML 就能读全文。</li>
<li><strong>部署零新增成本</strong>：站点跑在一台 1GB 内存的甲骨文小机上，复用已有的 Cloudflare Tunnel 和 Apache，只加了一个 vhost，内存零增长。国内国外访问都走 Cloudflare 边缘。</li>
<li><strong>发布走 GitHub Actions</strong>：<code>git push</code> 之后自动构建、自动 rsync 到服务器。写文章的姿势就是提交一个 Markdown 文件。</li>
</ul><p>这套链路的细节（包括为 AI 引擎做的 GEO 优化），我会在下一篇文章里完整拆解。</p></section>
<section><h2>写在最后<a href="#写在最后"><span>#</span></a></h2><p>我信奉一个原则：<strong>一手经验大于二手总结</strong>。这个网站上的一切内容，都来自我真实做过的东西——包括你现在看到的这个页面。</p><p>欢迎订阅 <a href="/rss.xml">RSS</a>，也欢迎来 <a href="https://github.com/luoruihuan" target="_blank">GitHub</a> 找我聊聊。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/2026-10-20-audit-geo-report/</id>
      <title type="text">我审计了一份第三方 GEO 检测报告，教你怎么看数据水分</title>
      <published>2026-10-20T10:00:00.000Z</published>
      <updated>2026-10-20T10:00:00.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/2026-10-20-audit-geo-report/"/>
      <summary type="text">从页面数据包与渲染逻辑入手，拆解一份 GEO 检测报告的五处水分：凑数的明细、复用的证据、虚拟的引用、直给的基准分与对称的优化预测，附七条验收清单。</summary>
      <content type="html"><![CDATA[<p>十月初，一个朋友给我发来一个链接，附带一句：</p>
<p>「帮我看看这份GEO报告，现在GEO很火，各个企业都在做，我想搞个代理做做，你看看这家代理商出的GEO报告怎么样，我感觉数据漂亮得有点不真实，但又说不出哪里不对。」</p>
<p>这是一份 GEO 检测报告——检测一个学校品牌（成都某外籍人员子女学校）在豆包、元宝、DeepSeek 这些 AI 平台里被推荐的情况。8 个平台、15 道题、情感分析、竞品对比、六维雷达图、行业百分位，一应俱全。</p>
<p>综合 84 分，超过 78% 的同行，购买优化服务后可以做到 95 分。</p>
<p>我没有急着下结论。做数据的人都知道，越是漂亮的报表越要重数一遍。我打开浏览器开发者工具，看这个页面到底加载了什么。</p>
<p>结果比我预想的有意思。</p>
<section><h2>我只做了一件事：看页面自己交代的数据<a href="#我只做了一件事看页面自己交代的数据"><span>#</span></a></h2><p>先说清楚审计方法，因为这一步比结论更重要。</p><p>一份在线报告页面，本质是前端拿一份数据包渲染出来的。这意味着：页面上每个数字，要么来自数据包里的原始值，要么是前端脚本现场算出来的。两者都摊在你面前。打开开发者工具的网络面板，找到那份 JSON；再看渲染脚本怎么消费它。不需要任何特殊权限，不碰对方后台，一切基于页面正常加载时主动交给浏览器的东西。</p><p>我用这个方法把报告页面拆了一遍：数据包是页面上一个叫 <code>REPORT_DATA</code> 的全局变量，由一个 <code>normalizeData()</code> 函数从原始 JSON 加工而来；渲染逻辑在一段 65304 字符的主脚本里，<code>renderKPI</code> 之类的函数逐个可读。</p><p>下面五个发现，全部来自这两处。我把它分成「页面实锤」和「合理怀疑」两类——前者是我重算过、代码里写死的，后者是数据形态可疑但后台无法核实的。这个区分后面还会用到。</p></section>
<section><h2>水分一：总数凑平了，明细对不上<a href="#水分一总数凑平了明细对不上"><span>#</span></a></h2><p>报告里最基础的表：每个平台、15 道题里提及了学校几次。我把数据包里的明细逐条重数了一遍。</p>

<table><thead><tr><th>平台</th><th>报告声称</th><th>我数出来</th><th>题数</th></tr></thead><tbody><tr><td>DeepSeek</td><td>11</td><td><strong>10</strong></td><td>15</td></tr><tr><td>豆包</td><td>14</td><td>14</td><td>15</td></tr><tr><td>元宝</td><td>14</td><td>14</td><td>15</td></tr><tr><td>通义千问</td><td>10</td><td><strong>11</strong></td><td>15</td></tr><tr><td>文心一言</td><td>14</td><td>14</td><td>15</td></tr><tr><td>纳米搜索</td><td>5</td><td>5</td><td>15</td></tr><tr><td>Kimi</td><td>9</td><td>9</td><td>15</td></tr><tr><td>智谱清言</td><td>4</td><td>4</td><td>15</td></tr></tbody></table><p></p><figure><img src="https://fastly.jsdelivr.net/gh/bucketio/img4@main/2026/10/20/1792467410258-7038de7d-7a70-4958-afd3-d81af4879e28.png" alt="平台计数声称值与页面实数对比（AI 生成示意图）" /><figcaption>平台计数声称值与页面实数对比（AI 生成示意图）</figcaption></figure><p></p><p>总数 81 对 81，看起来严丝合缝。但 DeepSeek 多算了一条，千问少算了一条。</p><p>总数凑平、明细错位，这个形态很有意思。如果是一次真实采集自动计数，明细应该天然一致；手工改过两个平台的数字又忘了总数，才会长这样。当然，也可能有别的解释。这是我说的「合理怀疑」，但它至少说明：这份报告的数字没经过最基本的自检。</p><p>顺带一提，DeepSeek 的提及率写着 0.73。可 10/15 是 0.67，11/15 才是 0.73。</p></section>
<section><h2>水分二：15 道题的「正面证据」，其实是同一句话<a href="#水分二15-道题的正面证据其实是同一句话"><span>#</span></a></h2><p>每道题下面都挂着几条「正面评价片段」，两三条到八条不等，看起来证据充分。</p><p>但数据包里每道题都有一个字段：独立证据数（uniqueSnippets）。15 道题，全部等于 1。</p><p>意思是：不管这道题下面列了多少条正面表述，都出自同一段素材。同一句「IB 均分 38.2，有牛剑 offer」，同时支撑了「学校怎么样」「学费值不值」「升学怎么样」「师资怎么样」四五个问题的结论。</p><p></p><figure><img src="https://fastly.jsdelivr.net/gh/bucketio/img9@main/2026/10/20/1792467480760-17532039-d406-47a8-8787-a88eea86a680.png" alt="图 2：唯一素材辐射支撑 15 道题（AI 生成示意图）" /><figcaption>图 2：唯一素材辐射支撑 15 道题（AI 生成示意图）</figcaption></figure><p></p><p>这不算撒谎。只是 15 道题、每题平均 5 条正面片段，看上去 70 多条证据的量，独立来源始终是那一个。</p></section>
<section><h2>水分三：4 条引用，来源写着 virtual<a href="#水分三4-条引用来源写着-virtual"><span>#</span></a></h2><p>报告的「自媒体引用」部分，统计了知乎、公众号、百家号、小红书这些平台上关于学校的引用内容，共 12 条。</p><p>数据包里这 12 条引用带来源标记：8 条标 search，4 条标 <strong>virtual</strong>。</p><p>virtual 是什么？翻译过来：这 4 条不是从任何平台搜出来的，是生成的。它们出现在「自媒体引用」列表里，和真实搜到的 8 条混在一起，页面上看不出任何区别。</p><p></p><figure><img src="https://fastly.jsdelivr.net/gh/bucketio/img13@main/2026/10/20/1792467547460-23bec9e6-d28d-48d2-b88d-6bfbf752e680.png" alt="图 3：search 与 virtual 引用并排展示（AI 生成示意图）" /><figcaption>图 3：search 与 virtual 引用并排展示（AI 生成示意图）</figcaption></figure><p></p><p>引用的平台分布，声称和实数也对不上：微信公众号 3 条实为 2 条，百家号 2 条实为 3 条；权威媒体那 11 条里，声称的 7 个来源实际只出现 6 个。</p></section>
<section><h2>水分四：行业基准和百分位，是后台发的三个现成数字<a href="#水分四行业基准和百分位是后台发的三个现成数字"><span>#</span></a></h2><p>报告顶部最抓眼球的三个数：综合 84 分、行业基准 65、超过 78% 的同行。</p><p>在数据包里它们是同一个 score 对象下的三个字段：total、benchmark、percentile。值都是现成的。「行业基准 65」不需要页面算什么，后台发什么就显示什么；渲染函数的工作，是画一个仪表盘。</p><p>哪个行业？多少家同行？分位数怎么算的？无从考证。</p><p></p><figure><img src="https://fastly.jsdelivr.net/gh/bucketio/img13@main/2026/10/20/1792467573540-0c2c27ab-368b-412c-8c51-3ba810e8efef.png" alt="图 4：总分填入六个维度，score 三字段现成下发（AI 生成示意图）" /><figcaption>图 4：总分填入六个维度，score 三字段现成下发（AI 生成示意图）</figcaption></figure><p></p><p>同一套渲染逻辑里还有个更妙的处理：官网六维雷达图，在分项数据缺失时，把官网总分填进六个维度。六边形看起来饱满均衡，实际数据点只有一个。这份样本正好命中了这个分支——那张漂亮的六边形雷达图，是总分的一次六次方展示。</p><p>还有「竞品优势 1.8 倍」这种指标，实现方式是本品牌得分除以竞品平均得分，两个分都是后台给的。</p></section>
<section><h2>水分五：优化预测，84 到 95 的对称魔术<a href="#水分五优化预测84-到-95-的对称魔术"><span>#</span></a></h2><p>报告最后一节是「优化预测」：当前 84 分，购买优化服务后 95 分。</p><p>四个维度各自的提升：可见度 85→95、内容 82→92、权威 88→95、互动 80→90。清一色 +10 左右。</p><p></p><figure><img src="https://fastly.jsdelivr.net/gh/bucketio/img8@main/2026/10/20/1792467597946-037e495e-1c89-4dd8-8d13-94326fed0796.png" alt="图 5：84→95 的对称提升（AI 生成示意图）" /><figcaption>图 5：84→95 的对称提升（AI 生成示意图）</figcaption></figure><p></p><p>做过预测模型的人都知道，真实的预测不会这么对称——不同维度的提升空间取决于基线、竞争强度和可操作性，必然有多有少。四个维度整齐划一的 +10，不是模型输出，是拍出来的定价依据：84 到 95，刚好够证明服务值得买，又不至于夸张到离谱。</p><p>情感分析那部分的 20 道题，我也数了一下，全部是品牌词问题——「X 学校怎么样」「X 学校学费贵不贵」这种，没有一道是无品牌提示的开放题。这正好踩中了 GEO 测量里最经典的口径陷阱：把「用户问了学校名字后 AI 复述了它」当成「获得了新曝光」。这道题库里 20 道题，全是前者。</p></section>
<section><h2>先说句客观的公道话：它不算假报告<a href="#先说句客观的公道话它不算假报告"><span>#</span></a></h2><p>审计完这五处，我的结论反而是：这份报告不是全盘造假的空壳。</p><p>8 个平台、15 道题、81 条提及，采集是真做了的；大部分计数也对得上。如果它凭空编数据，反而好识别——总数对不上、平台行为模式不合理，一眼就能看穿。</p><p>真正难防的是这种：采集真的做，数据大体真，水分藏在证据复用、虚拟来源、直给的基准分、对称的预测里。每一处单独看都可以有解释，合在一起就是一份没经过验收、却拿来收钱和谈合作的商业报告。</p><p>还有一个边界要交代：我只审计了页面交付的数据和渲染逻辑，后台究竟怎么采集、有没有人工修饰，我看不到，也不下结论。这篇文章里所有「实锤」，都限于页面自己交代的部分。</p></section>
<section><h2>针对GEO相关报告，我们怎么看<a href="#针对geo相关报告我们怎么看"><span>#</span></a></h2><p>这事给我的最大启发，跟某一家做得行不行没关系：GEO 检测报告目前没有任何行业标准，你拿到手的每一份都应该先验收。方法不难，以下七条按优先级排：</p><ol>
<li><strong>要原始回答</strong>。每道题的 AI 回答原文 + 采集时间戳，给不出的直接出局。这是所有指标的地基，地基不给，上层全是对不上的账。</li>
<li><strong>指标必须带分母</strong>。「提及率 93%」这种数没法验收；要说清「15 道题里提及 14 道，采集于某周三晚上 8 点，每题新会话」，才谈得上核对。</li>
<li><strong>数一遍明细</strong>。声称的总数和明细自己加总对不对得上，是最便宜的自检。连这都错的，说明交付前没人看过第二遍。</li>
<li><strong>查引用的来源标记</strong>。让供应商说明每条引用的来源字段含义，警惕 search 之外的任何标记：virtual、generated、synthetic，都是同一个意思。</li>
<li><strong>问独立证据数</strong>。同一句素材被剪成多份用，直接问：这 N 条正面评价，独立来源几条？</li>
<li><strong>基准分要求给算法</strong>。行业基准和百分位，必须能回答「样本是谁、多少家、什么时间窗、怎么算的」。答不上的，当成装饰看。</li>
<li><strong>警惕无品牌题占比为零的提及率</strong>。全是「X 学校怎么样」这种题测出来的提及率，和真实获客能力是两回事。</li>
</ol><p>这七条不针对任何一家。行业早期，真采集和真水分并存，区分的成本暂时落在买家自己头上。</p></section>
<section><h2>总结<a href="#总结"><span>#</span></a></h2><p>后来我把这次审计的证据存成了一份 JSON 留档，也成为我们后来自己做 GEO 监测平台时定数据口径的起点：每个指标必须能从原始回答重算，独立证据数直接进数据库字段，未知值不许当零处理。</p><p>那套平台怎么搭、跟这次审计的关联有多深，是下一篇文章的事。</p><p>至于朋友那单合作，后来确实没谈成，和他聊了之后他又去实地考察了一趟，回来跟我说，这家公司的业务重心其实放在代理加盟上，检测是那门生意里的一环。话里话外，收加盟费的味道比做检测重。他说幸好提前把数据拆了一遍，心里有了防范，看什么都有点祛魅了。现在GEO的风口大家都想抢占，这个可以理解，但是布局的同时，千万别被别人割了韭菜。</p><p>如果这篇文章对你有帮助，欢迎关注「技术派罗恩」，会不定时的分享一下对你真正有用的实战经验。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/2026-10-30-blog-rebuild-with-agent/</id>
      <title type="text">博客挂了三天，我和 AI Agent 把它整个重造了一遍</title>
      <published>2026-10-30T13:00:00.000Z</published>
      <updated>2026-10-30T13:00:00.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/2026-10-30-blog-rebuild-with-agent/"/>
      <summary type="text">一次域名失联的完整排障：DNS 记录凭空消失、HTTPS 证书被撤、真正的站藏在遗忘的服务器里。修完索性换掉整个主题，迁移 25 篇十年文章，顺手记录 AI Agent 的四次真实翻车。</summary>
      <content type="html"><![CDATA[<p>十月底的一个晚上，我想给博客加个备案号，打开 <code>luoruihuan.top</code>——转圈，超时，打不开。</p>
<p>我的第一反应跟大多数人一样：域名过期了？被墙了？服务器欠费了？</p>
<p>都不是。这三天发生的事比我预想的多，不写下来有点亏。</p>
<p>网站挂了三天，真正花在“修”上的只有半天。剩下两天半，我和 AI Agent 把整个博客推倒重造了一遍。</p>
<section><h2>第一幕：网站”凭空消失”了<a href="#第一幕网站凭空消失了"><span>#</span></a></h2><p>排障最忌瞎猜。我的习惯是分层查：域名注册 → DNS 解析 → 证书 → 服务器，一层层排除。</p><p><strong>第一层，域名还活着吗？</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>whois</span><span> </span><span>luoruihuan.top</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>阿里云注册，到期日 2027-09-29，状态 <code>ok</code>。域名没问题。但有个细节引起了我的注意：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Updated Date: 2026-10-28T03:25:56Z</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>域名信息昨天刚被更新过，今天就挂了——时间线高度吻合。这成了后面定位问题的关键线索。</p><p><strong>第二层，DNS 在撒谎吗？</strong></p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>dig</span><span> </span><span>luoruihuan.top</span><span> </span><span>NS</span><span> </span><span>+short</span></div></div><div><div><div>2</div></div><div><span># andy.ns.cloudflare.com.</span></div></div><div><div><div>3</div></div><div><span># laila.ns.cloudflare.com.     ← NS 正常，在 Cloudflare</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>dig</span><span> </span><span>luoruihuan.top</span><span> </span><span>A</span><span> </span><span>+short</span></div></div><div><div><div>6</div></div><div><span># （空）                        ← A 记录呢？？？</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>NS 还在，但 A / AAAA / CNAME 全部查空。换了 Google 的 8.8.8.8 和 Cloudflare 的 1.1.1.1 交叉验证，结果一致——不是本地缓存问题。</p><p>还不放心，直接问权威服务器：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>dig</span><span> </span><span>@andy.ns.cloudflare.com</span><span> </span><span>luoruihuan.top</span><span> </span><span>A</span><span> </span><span>+noall</span><span> </span><span>+answer</span><span> </span><span>+authority</span></div></div><div><div><div>2</div></div><div><span># -&gt;&gt;HEADER&lt;&lt;- status: NOERROR</span></div></div><div><div><div>3</div></div><div><span># ANSWER: 0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意这个结果：<code>NOERROR</code> + 0 条回答（NODATA），和 <code>NXDOMAIN</code> 是两回事。<code>NXDOMAIN</code> 是“这个域名不存在”；<code>NOERROR</code> 加空 answer 是“域名存在，但它没有你要的这条记录”。</p><p>权威服务器亲口告诉我：zone 活着，里面的解析记录没了。</p><p>登录 Cloudflare 一看，DNS 记录列表干干净净——被谁删的、什么时候删的，已经无从考证（whois 那条更新时间暗示了时间点）。重新加上记录，域名恢复了解析。</p></section>
<section><h2>第二幕：修好了，但浏览器说”连接不是私密连接”<a href="#第二幕修好了但浏览器说连接不是私密连接"><span>#</span></a></h2><p>DNS 通了，浏览器却给我一个大大的红色警告：<code>net::ERR_CERT_COMMON_NAME_INVALID</code>。</p><p>用 openssl 看一眼服务器到底给了我什么证书：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>curl</span><span> </span><span>-sv</span><span> </span><span>--resolve</span><span> </span><span>luoruihuan.top:443:185.199.108.153</span><span> </span><span>https://luoruihuan.top/</span><span> 2&gt;&amp;1 | </span><span>grep</span><span> </span><span>subject</span></div></div><div><div><div>2</div></div><div><span># *  subject: CN=*.github.io</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>破案了。这个站之前托管在 GitHub Pages,GitHub 会给自定义域名签发专属的 Let’s Encrypt 证书。但证书续期的前提是 DNS 一直指向 GitHub——我的解析记录断了几天，证书过期被撤了。DNS 恢复后,GitHub 边缘节点上只剩一张默认的 <code>*.github.io</code> 证书,浏览器一看&lt;我要的是&gt; <code>luoruihuan.top</code>,你给我 <code>*.github.io</code>?不匹配,拒绝。</p><p>解法不难：到仓库设置里把 custom domain 解绑再重新绑定，触发 GitHub 重新走一遍域名验证和证书签发，几分钟搞定。</p></section>
<section><h2>第三幕：我修好了，但修的是”错的站”<a href="#第三幕我修好了但修的是错的站"><span>#</span></a></h2><p>证书签好、网站恢复、备案号加上的当天，我后知后觉地想起一件事——</p><p><strong>我好像还有个新博客项目，不在 GitHub Pages 上。</strong></p><p>翻出那个 <code>luoruihuan.top</code> 仓库，<code>.github/workflows/deploy.yml</code> 里写得明明白白：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>- </span><span>name</span><span>: </span><span>Deploy via rsync</span></div></div><div><div><div>2</div></div><div><span>  </span><span>run</span><span>: </span><span>|</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>rsync -az --delete dist/ ubuntu@129.146.16.16:/var/www/ron-site/</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>真正要上线的站，部署在一台 Oracle 云的 ARM 小鸡上，rsync 推静态文件，早就忘了是 vm1 还是 vm2——workflow 文件本身就是最好的部署文档。ssh 上去确认：文件在，服务在，但 80 端口在系统防火墙里被 REJECT，443 被 Caddy 占着却没配主站站点。放行端口、补一段 Caddyfile、证书自动签发，链路才算真正修通。</p><p>这一幕给我的教训比前面所有技术问题都深：“部署在哪”这件事，不能只存在于记忆里。</p></section>
<section><h2>第四幕：既然都动土了，干脆重造<a href="#第四幕既然都动土了干脆重造"><span>#</span></a></h2><p>站修好了，但看着那个 2023 年审美的主题，我决定不将就了——换。</p><p>我把需求丢给 AI Agent，要求很直接：现代、酷炫、有 demo 可看、中文社区活跃。它给了一份对比报告，我快速过了一眼：</p>

<table><thead><tr><th>主题</th><th>Stars</th><th>我的评价</th></tr></thead><tbody><tr><td>Fuwari</td><td>5100+</td><td>现代圆润，但太”大众脸”</td></tr><tr><td>Mizuki</td><td>1900+</td><td>极简有设计感，但偏笔记风</td></tr><tr><td>astro-gyoza</td><td>300+</td><td>简洁，但维护评级 C 级，落后三个大版本</td></tr><tr><td><strong>Firefly（流萤）</strong></td><td>2300+</td><td>Material Design 3、四种壁纸模式、文档全、更新勤</td></tr></tbody></table><p>我选了 Firefly。接下来的事，基本是 Agent 主导、我做决策：</p><ul>
<li><strong>内容迁移</strong>：现有 3 篇 + 我那个 2016 年的 Hexo 老博客 22 篇，一共 25 篇。Agent 写了个脚本批量转换 frontmatter：Hexo 的 <code>date/categories/urlname</code> 映射成 Astro 的 <code>published/category/slug</code>，<code>&lt;!--more--&gt;</code> 摘要分割符自动转成 description，十年的时间线一篇不乱。老博客的图片整体搬到 <code>public/uploads/</code>，文章里的引用路径一行不用改。</li>
<li><strong>站点个性化</strong>：标题、头像、favicon、导航链接（GitHub、我的服务监控页）、页脚备案号、背景图、音乐播放器换成周传雄的《青花》。</li>
<li><strong>部署链路</strong>：CI 构建 → rsync 到小鸡，全链路保留，push 即上线。</li>
</ul><p>一个下午，25 篇文章迁完，主题换成新的，部署链路照旧跑通。</p></section>
<section><h2>第五幕：Agent 翻车集锦<a href="#第五幕agent-翻车集锦"><span>#</span></a></h2><p>网上写“AI Agent 有多强”的文章已经泛滥了，这篇写点不一样的：这几天 Agent 真实翻过的四次车。四次都是真事，每一次都真实造成过线上故障。</p><p><strong>翻车一：一条分号，误删整个工作目录。</strong></p><p>clone 仓库时网络抖了一下失败，<code>cd</code> 进目录自然也失败——但那条命令链里我（其实是 Agent）用了分号分隔而不是 <code>&amp;&amp;</code>，于是”清空目录”的命令在 <code>/tmp</code> 本尊上执行了，把所有工作文件一锅端。好在所有源头资产都在 GitHub 上，用脚本重做了一遍。</p><blockquote><p>教训：shell 命令链永远用 <code>&amp;&amp;</code>，清理类命令前先确认当前目录。</p></blockquote><p><strong>翻车二：BSD sed 的一��范围删除，删到了文件尾。</strong></p><p>修复 CI 配置时用了 <code>sed '/with:/,/version: 10/d'</code>。macOS 的 sed 在”结束模式”永远匹配不到时,会<strong>从起点一路删到文件末尾</strong>。workflow 被删得只剩三个初始化步骤—没有构建、没有部署,job 却显示 success。CI“空转成功”是最迷惑人的故障形态：绿灯亮着，什么都没发生。</p><blockquote><p>教训：范围删除用 Python/lint 校验结果，“成功”必须验证产物而不是只看退出码。</p></blockquote><p><strong>翻车三：rsync 忘了 <code>--delete</code>，线上闹鬼。</strong></p><p>背景图明明换掉了，线上却一直显示旧的示例图。排查到怀疑人生，最后发现是同步目录时 rsync 没带 <code>--delete</code>——<strong>本地删掉的文件在目标端永远残留</strong>，CI 又基于旧文件构建，旧图就这么”复活”了。</p><blockquote><p>教训：“同步”和“覆盖”是两回事，镜像同步记得 <code>--delete</code>。</p></blockquote><p><strong>翻车四：正则替换，替换到了注释里。</strong></p><p>把背景图配置从数组改成单张图片，Agent 用正则 <code>desktop: \[...\]</code> 匹配替换——结果文件顶部的<strong>注释示例</strong>里恰好先出现了一模一样的写法。替换成功了（日志全是 ✅），但改的是注释，真正的配置原封未动。</p><blockquote><p>教训：正则替换前先数一数会命中几处；带校验的替换脚本应该在“恰好命中一次”时才放行。</p></blockquote><p>这四次翻车，没有一次是因为 AI 笨。命令链容错、平台差异、同步语义、正则命中，全是工程里的经典陷阱，人也会踩，只是 Agent 踩得更快、更集中。</p></section>
<section><h2>总结：三天下来，我对”人机协作”的三点新认识<a href="#总结三天下来我对人机协作的三点新认识"><span>#</span></a></h2><p>一、人做判断，Agent 做执行，但验收必须回到人手里。 主题选型、审美取舍、备案信息这些决策我从不假手;命令、脚本、全链路验证这种脏活累活 Agent 干得又快又好。但每次上线前的最终验证—build 产物里 grep 关键内容、线上 curl 实测—一步都不能省。四次翻车全都是被”验证”拦下来的,没有一次是靠运气。</p><p>二、Agent 的“成功”输出不可信，产物才可信。绿灯、✅、exit code 0，都只是必要条件。真正可靠的判断标准是：产物里有没有它，线上有没有它，行为对不对。</p><p>三、排障还是要分层。域名注册（whois）→ DNS（dig 递归再问权威）→ 证书（openssl 直接看）→ 网络（curl 分端口）→ 服务器（ssh 查防火墙），每层只回答一个问题。三天里的每一次快速定位，靠的都是这个笨办法。</p><blockquote><p>时光不语，静待花开。网站也一样，挂掉的那三天它一言不发，但每一层都留下了答案，就看你问不问。</p></blockquote><hr /><p><em>这篇文章从写作到发布，跑在重造后的链路上：<code>git push</code> → GitHub Actions 构建 → rsync 到那台 Oracle 小鸡 → Caddy 服务。全程无人工干预。</em></p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/puppeteer/</id>
      <title type="text">Puppeteer 入门</title>
      <published>2023-06-22T15:03:05.000Z</published>
      <updated>2023-06-22T15:03:05.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/puppeteer/"/>
      <summary type="text">一、Puppeteer 是什么 Puppeteer，翻译成中文可以理解成傀儡的意思，他是一个Node库，主要是提供接口来控制 headless chrome ，也就是无头浏览器，无头浏览器是一种不使用</summary>
      <content type="html"><![CDATA[<section><h2>一、Puppeteer 是什么<a href="#一puppeteer-是什么"><span>#</span></a></h2><p>Puppeteer，翻译成中文可以理解成傀儡的意思，他是一个Node库，主要是提供接口来控制 headless chrome ，也就是无头浏览器，无头浏览器是一种不使用chrome来运行chrome浏览器的方式，简单来讲，其实就是编写javascript 代码自动化控制 Chrome。</p><p>我们能用Puppeteer做些什么</p><blockquote><ul>
<li>1、利用网页生成PDF，图片</li>
<li>2、主要的从网站抓取内容。</li>
<li>3、自动化表单提交、UI测试、键盘输入等。</li>
<li>4、捕获站点的时间线，一遍追踪网站，帮助分析网站性能问题。</li>
</ul></blockquote><hr /></section>
<section><h2>二、运行环境<a href="#二运行环境"><span>#</span></a></h2><p>Node 版本大于 V7.6.0，因为Puppeteer中所有的操作几乎都是一步的，如果使用大量的then会使代码的可读性特别低，所以选择ES7中的 async，await 语法，这也是 Puppeteer 官方推荐的写法。</p><hr /></section>
<section><h2>三、基本用法<a href="#三基本用法"><span>#</span></a></h2><section><h3>1、入门 Demo 屏幕截图<a href="#1入门-demo-屏幕截图"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>puppeteer</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'puppeteer'</span><span>);</span></div></div><div><div><div>2</div></div><div><span>async</span><span> </span><span>function</span><span> </span><span>getPic</span><span>() {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>// 启动 puppeteer</span></div></div><div><div><div>4</div></div><div><span>    </span><span>const</span><span> </span><span>browser</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>puppeteer</span><span>.</span></span><span>launch</span><span>({ </span><span>headless</span><span>:</span><span> </span><span>false</span><span> });</span></div></div><div><div><div>5</div></div><div><span>    </span><span>// 在浏览器中创建一个新的页面</span></div></div><div><div><div>6</div></div><div><span>    </span><span>const</span><span> </span><span>page</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>browser</span><span>.</span></span><span>newPage</span><span>();</span></div></div><div><div><div>7</div></div><div><span>    </span><span>// 使用 goto 打开指定网页</span></div></div><div><div><div>8</div></div><div><span>    </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>goto</span><span>(</span><span>"https://www.baidu.com"</span><span>);</span></div></div><div><div><div>9</div></div><div><span>    </span><span>// 调整页面配置大小，截图更好看</span></div></div><div><div><div>10</div></div><div><span>    </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>setViewport</span><span>({ </span><span>width</span><span>:</span><span> </span><span>1400</span><span>, </span><span>height</span><span>:</span><span> </span><span>900</span><span> });</span></div></div><div><div><div>11</div></div><div><span>    </span><span>// 使用 screenshot 函数对网页进行截屏</span></div></div><div><div><div>12</div></div><div><span>    </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>screenshot</span><span>({ </span><span>path</span><span>:</span><span> </span><span>'baidu.png'</span><span> })</span></div></div><div><div><div>13</div></div><div><span>    </span><span>// 关闭浏览器</span></div></div><div><div><div>14</div></div><div><span>    </span><span>await</span><span><span> </span><span>browser</span><span>.</span></span><span>close</span><span>();</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div><span>getPic</span><span>();</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h3>2、基础使用属性<a href="#2基础使用属性"><span>#</span></a></h3><section><h4>① puppeteer.launch(options) options 参数详解<a href="#-puppeteerlaunchoptions-options-参数详解"><span>#</span></a></h4>

<table><thead><tr><th>参数名称</th><th>参数类型</th><th>参数说明</th></tr></thead><tbody><tr><td>ignoreHTTPSErrors</td><td>boolean</td><td>在请求的过程中是否忽略 Https 报错信息，默认为 false</td></tr><tr><td>headless</td><td>boolean</td><td>是否以”无头”的模式运行 chrome, 也就是不显示 UI， 默认为 true</td></tr><tr><td>executablePath</td><td>string</td><td>可执行文件的路劲，Puppeteer 默认是使用它自带的 chrome webdriver, 如果你想指定一个自己的 webdriver 路径，可以通过这个参数设置</td></tr><tr><td>slowMo</td><td>number</td><td>使 Puppeteer 操作减速，单位是毫秒。如果你想看看 Puppeteer 的整个工作过程，这个参数将非常有用。</td></tr><tr><td>args</td><td>Array(String)</td><td>传递给 chrome 实例的其他参数，比如你可以使用”–ash-host-window-bounds=1024x768” 来设置浏览器窗口大小。</td></tr><tr><td>handleSIGINT</td><td>boolean</td><td>是否允许通过进程信号控制 chrome 进程，也就是说是否可以使用 CTRL+C 关闭并退出浏览器.</td></tr><tr><td>timeout</td><td>number</td><td>等待 Chrome 实例启动的最长时间。默认为30000（30秒）。如果传入 0 的话则不限制时间</td></tr><tr><td>userDataDir</td><td>string</td><td>设置用户数据目录，默认linux 是在 ~/.config 目录，window 默认在 C:\Users{USER}\AppData\Local\Google\Chrome\User Data, 其中 {USER} 代表当前登录的用户名.</td></tr><tr><td>env</td><td>Object</td><td>指定对Chromium可见的环境变量。默认为process.env.</td></tr><tr><td>devtools</td><td>boolean</td><td>是否为每个选项卡自动打开DevTools面板， 这个选项只有当 headless 设置为 false 的时候有效</td></tr></tbody></table></section><section><h4>② Browser 对象<a href="#-browser-对象"><span>#</span></a></h4><p>当 Puppeteer 连接到一个 Chrome 实例的时候就会创建一个 Browser 对象，有以下两种方式：
Puppeteer.launch 和 Puppeteer.connect. Browser 对象 API :</p>

<table><thead><tr><th>方法名称</th><th>返回值</th><th>说明</th></tr></thead><tbody><tr><td>browser.close()</td><td>Promise</td><td>关闭浏览器</td></tr><tr><td>browser.disconnect()</td><td>void</td><td>断开浏览器连接</td></tr><tr><td>browser.newPage()</td><td>Promise(Page)</td><td>创建一个 Page 实例</td></tr><tr><td>browser.pages()</td><td>Promise(Array(Page))</td><td>获取所有打开的 Page 实例</td></tr><tr><td>browser.targets()</td><td>Array(Target)</td><td>获取所有活动的 targets</td></tr><tr><td>browser.version()</td><td>Promise(Page)</td><td>获取浏览器的版本</td></tr><tr><td>browser.wsEndpoint()</td><td>String</td><td>返回浏览器实例的 socket 连接 URL, 可以通过这个 URL 重连接 chrome 实例</td></tr></tbody></table><p>Puppeteer 详细Api地址  <a href="https://github.com/puppeteer/puppeteer/blob/main/docs/api.md" target="_blank">https://github.com/puppeteer/puppeteer/blob/main/docs/api.md</a></p><hr /></section></section></section>
<section><h2>四、实战获取京东商城手机商品<a href="#四实战获取京东商城手机商品"><span>#</span></a></h2><p>首先梳理操作流程：</p><blockquote><ul>
<li>①、打开京东首页</li>
<li>②、输入手机关键字并搜索</li>
<li>③、获取前10个商品的A标签，并获取商品详情链接。</li>
<li>④、分别打开10个详情图，截取网页图片。</li>
</ul></blockquote><p>要实现上面的功能，需要用到查找元素，获取属性，键盘回车等，接下来一个一个讲解一下。</p><section><h3>1、获取元素<a href="#1获取元素"><span>#</span></a></h3><p>Page 对象提供了2个API来获取页面元素：
(1). <code>Page.$(selector)</code> 获取单个元素，底层是调用的是 <code>document.querySelector()</code> , 所以选择器的 selector 格式遵循 css 选择器规范;</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>let</span><span><span> </span><span>inputElement</span><span> </span></span><span>=</span><span> </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>$</span><span>(</span><span>'#search'</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>(2). <code>Page.$$(selector)</code> 获取一组元素，底层调用的是 <code>document.querySelectorAll()</code>. 返回 Promise(Array(ElemetHandle)) 元素数组.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>links</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>$$</span><span>(</span><span>"a"</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>2、获取元素属性<a href="#2获取元素属性"><span>#</span></a></h3><p>(1). <code>Page.$eval(selector, pageFunction[, …args])</code>, 获取单个元素的属性</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>const</span><span> </span><span>value</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>$eval</span><span>(</span><span>'input[name=search]'</span><span><span>, </span><span>input</span><span> </span></span><span>=&gt;</span><span><span> </span><span>input</span><span>.</span></span><span>value</span><span>);</span></div></div><div><div><div>2</div></div><div><span>    </span><span>const</span><span> </span><span>href</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>$eval</span><span>(</span><span>'#a", ele =&gt; ele.href)</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>const</span><span> </span><span>content</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>$eval</span><span>(</span><span>'.content'</span><span><span>, </span><span>ele</span><span> </span></span><span>=&gt;</span><span><span> </span><span>ele</span><span>.</span></span><span>outerHTML</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>(2). <code>Page.$$eval(selector, pageFunction[, …args])</code>, 获取元素数组的属性</p></section><section><h3>3、键盘和鼠标事件<a href="#3键盘和鼠标事件"><span>#</span></a></h3><p>键盘和鼠标的API比较简单，键盘的几个API如下：</p><blockquote><ul>
<li>keyboard.down(key[, options]) 触发 keydown 事件</li>
<li>keyboard.press(key[, options]) 按下某个键，key 表示键的名称</li>
<li>keyboard.sendCharacter(char) 输入一个字符</li>
<li>keyboard.type(text, options) 输入一个字符串</li>
<li>keyboard.up(key) 触发 keyup 事件</li>
<li>page.keyboard.press(“Shift”); //按下 Shift 键</li>
<li>page.keyboard.sendCharacter(‘嗨’);</li>
<li>page.keyboard.type(‘Hello’); // 一次输入完成</li>
<li>page.keyboard.type(‘World’, {delay: 100}); // 像用户一样慢慢输入
鼠标Api如下:</li>
<li>mouse.click(x, y, [options]) 移动鼠标指针到指定的位置，然后按下鼠标，这个其实 mouse.move 和 mouse.down 或 mouse.up 的快捷操作
&gt; * mouse.down([options]) 触发 mousedown 事件，options 可配置:
&gt; * options.button 按下了哪个键，可选值为[left, right, middle], 默认是 left, 表示鼠标左键
&gt; * options.clickCount 按下的次数，单击，双击或者其他次数
&gt; * delay 按键延时时间
&gt; * mouse.move(x, y, [options]) 移动鼠标到指定位置， options.steps 表示移动的步长
&gt; * mouse.up([options]) 触发 mouseup 事件</li>
</ul></blockquote></section><section><h3>4、其他实用API<a href="#4其他实用api"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>page.waitForSelector(selector[, options]) 等待某个选择器的元素加载之后，这个元素可以是异步加载的，这个 API 非常有用，比如我想获取某个通过 js 异步加载的元素，那么直接获取肯定是获取不到的。这个时候就可以使用 page.waitForSelector 来解决。</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>5、实现代码<a href="#5实现代码"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>puppeteer</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'puppeteer'</span><span>);</span></div></div><div><div><div>2</div></div><div><span>const</span><span> </span><span>fs</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'fs'</span><span>);</span></div></div><div><div><div>3</div></div><div><span>const</span><span> </span><span>path</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>'path'</span><span>);</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>async</span><span> </span><span>function</span><span> </span><span>getShopDetail</span><span>() {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>// 启动 puppeteer</span></div></div><div><div><div>7</div></div><div><span>    </span><span>const</span><span> </span><span>browser</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>puppeteer</span><span>.</span></span><span>launch</span><span>({ </span><span>headless</span><span>:</span><span> </span><span>false</span><span>, </span><span>ignoreHTTPSErrors</span><span>:</span><span> </span><span>true</span><span> });</span></div></div><div><div><div>8</div></div><div><span>    </span><span>let</span><span><span> </span><span>page</span><span> </span></span><span>=</span><span> </span><span>await</span><span><span> </span><span>browser</span><span>.</span></span><span>newPage</span><span>();</span></div></div><div><div><div>9</div></div><div><span>    </span><span>// 启用Javascript,允许执行js脚本</span></div></div><div><div><div>10</div></div><div><span>    </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>setJavaScriptEnabled</span><span>(</span><span>true</span><span>);</span></div></div><div><div><div>11</div></div><div><span>    </span><span>// 打开京东网页</span></div></div><div><div><div>12</div></div><div><span>    </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>goto</span><span>(</span><span>"https://www.jd.com/"</span><span>);</span></div></div><div><div><div>13</div></div><div><span>    </span><span>// 获取搜索框元素</span></div></div><div><div><div>14</div></div><div><span>    </span><span>const</span><span> </span><span>searchInput</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>$</span><span>(</span><span>"#key"</span><span>);</span></div></div><div><div><div>15</div></div><div><span>    </span><span>// 定位到搜索框</span></div></div><div><div><div>16</div></div><div><span>    </span><span>await</span><span><span> </span><span>searchInput</span><span>.</span></span><span>focus</span><span>();</span></div></div><div><div><div>17</div></div><div><span>    </span><span>// 输入搜索内容</span></div></div><div><div><div>18</div></div><div><span>    </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>keyboard</span><span>.</span><span>type</span><span>(</span><span>"手机"</span><span>);</span></div></div><div><div><div>19</div></div><div><span>    </span><span>// 获取搜索按钮</span></div></div><div><div><div>20</div></div><div><span>    </span><span>const</span><span> </span><span>searchBtn</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>$</span><span>(</span><span>".button"</span><span>);</span></div></div><div><div><div>21</div></div><div><span>    </span><span>// 执行点击事件</span></div></div><div><div><div>22</div></div><div><span>    </span><span>await</span><span><span> </span><span>searchBtn</span><span>.</span></span><span>click</span><span>();</span></div></div><div><div><div>23</div></div><div><span>    </span><span>// 等待元素加载之后，否则获取不异步加载的元素</span></div></div><div><div><div>24</div></div><div><span>    </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>waitForSelector</span><span>(</span><span>'.gl-item'</span><span>);</span></div></div><div><div><div>25</div></div><div><span>    </span><span>// 获取搜索结果</span></div></div><div><div><div>26</div></div><div><span>    </span><span>const</span><span> </span><span>links</span><span> </span><span>=</span><span> </span><span>await</span><span><span> </span><span>page</span><span>.</span></span><span>$$eval</span><span>(</span><span>'.gl-item &gt; .gl-i-wrap &gt; .p-img &gt; a'</span><span><span>, </span><span>links</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>27</div></div><div><span>        </span><span>return</span><span><span> </span><span>links</span><span>.</span></span><span>map</span><span><span>(</span><span>a</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>28</div></div><div><span>            </span><span>return</span><span> {</span></div></div><div><div><div>29</div></div><div><span>                </span><span>href</span><span>:</span><span><span> </span><span>a</span><span>.</span></span><span>href</span><span>.</span><span>trim</span><span>(),</span></div></div><div><div><div>30</div></div><div><span>                </span><span>title</span><span>:</span><span><span> </span><span>a</span><span>.</span></span><span>title</span></div></div><div><div><div>31</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>32</div></div><div><span><span>        </span></span><span>});</span></div></div><div><div><div>33</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>34</div></div><div><span>    </span><span>const</span><span> </span><span>content</span><span> </span><span>=</span><span> </span><span>JSON</span><span>.</span><span>stringify</span><span><span>(</span><span>links</span><span>);</span></span></div></div><div><div><div>35</div></div><div><span>    </span><span>const</span><span> </span><span>file</span><span> </span><span>=</span><span><span> </span><span>path</span><span>.</span></span><span>join</span><span><span>(</span><span>__dirname</span><span>, </span></span><span>'resource/index.json'</span><span>);</span></div></div><div><div><div>36</div></div><div><span><span>    </span></span><span>fs</span><span>.</span><span>writeFile</span><span><span>(</span><span>file</span><span>, </span><span>content</span><span>, ()</span></span><span>=&gt;</span><span> {});</span></div></div><div><div><div>37</div></div><div><span>    </span><span>await</span><span><span> </span><span>browser</span><span>.</span></span><span>close</span><span>();</span></div></div><div><div><div>38</div></div><div><span>}</span></div></div><div><div><div>39</div></div><div>
</div></div><div><div><div>40</div></div><div><span>getShopDetail</span><span>();</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><hr /></section></section>
<section><h2>五、定制属于自己的个人资讯网站<a href="#五定制属于自己的个人资讯网站"><span>#</span></a></h2><ul>
<li>抓取资讯 puppeteer</li>
<li>定时任务 node-schedule</li>
<li>部署 docker + github pages</li>
</ul><p>主要思路: puppeteer 定时抓取各个网站板块信息，每次获取内容后，保存至数据库或者简单的用Json保存，保存完成，只需要请求这个文件就可以渲染出需要的内容，定时任务可以用过 node-schedule 库来实现，部署可以采用 docker + gittpage 部署。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/webOptimize/</id>
      <title type="text">了解输入url到页面加载，为了更好地性能优化</title>
      <published>2023-01-28T01:51:35.000Z</published>
      <updated>2023-01-28T01:51:35.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/webOptimize/"/>
      <summary type="text">一、 DNS解析过程 DNS 全称 DomainName System 域名系统，俗称电话本。DNS解析就是通过域名最终得到对应ip地址，知道哪台服务器上有你需要的资源的过程。当然如何进行DNS 解析</summary>
      <content type="html"><![CDATA[<section><h5>一、 DNS解析过程<a href="#一-dns解析过程"><span>#</span></a></h5><p>DNS  全称 DomainName System 域名系统，俗称电话本。DNS解析就是通过域名最终得到对应ip地址，知道哪台服务器上有你需要的资源的过程。当然如何进行DNS 解析又是另外的话题。</p><section><h6>1、首先搜索浏览器自身的DNS缓存，有缓存直接返回;<a href="#1首先搜索浏览器自身的dns缓存有缓存直接返回"><span>#</span></a></h6></section><section><h6>2、浏览器自身DNS不存在，浏览器就会调用一个类似 gethostbyname 的库函数,此函数会先去检测本地hosts文件，查看是否有对应ip。<a href="#2浏览器自身dns不存在浏览器就会调用一个类似-gethostbyname-的库函数此函数会先去检测本地hosts文件查看是否有对应ip"><span>#</span></a></h6></section><section><h6>3、如果本地hosts文件不存在映射关系，就会查询路由缓存，路由缓存不存在就去查找本地DNS服务器（一般TCP/IP参数里会设置首选DNS服务器，通常是8.8.8.8);<a href="#3如果本地hosts文件不存在映射关系就会查询路由缓存路由缓存不存在就去查找本地dns服务器一般tcpip参数里会设置首选dns服务器通常是8888"><span>#</span></a></h6></section><section><h6>4、如果本地DNS服务器还没找到就会向根服务器发出请求。具体过程：<a href="#4如果本地dns服务器还没找到就会向根服务器发出请求具体过程"><span>#</span></a></h6><blockquote><ul>
<li>本地DNS服务器代我们的浏览器发起迭代DNS解析请求，首先它会找根域的DNS的IP地址（全球13台哟，可惜中国没有！）。找到根域的DNS地址,就会向其发起请求；</li>
<li>根域发现这是一个顶级域top域的一个域名，于是告诉本地DNS服务器我不知道这个域名的IP地址,但是我知道top域的IP地址，你去找它去吧；</li>
<li>于是本地DNS服务器就得到了top域的IP地址，又向top域的IP地址发起了请求（请问<code>www.luoruihuan.top</code>这个域名的IP地址是多少呀?）,于是top域服务器告诉本地DNS服务器我不知道<code>www.luoruihuan.top</code>这个域名的IP地址，但是我知道<code>luoruihuan.top</code>这个域的DNS地址，你去找它去;</li>
<li>于是本地DNS服务器又向<code>luoruihuan.top</code>这个域名的DNS地址（这个一般就是由域名注册商提供的，像万网，新网等）发起请求（请问<code>www.luoruihuan.top</code>这个域名的IP地址是多少？），这个时候<code>luoruihuan.top</code>域的DNS服务器一查，呀！果真在我这耶，于是就把找到的结果发送给本地DNS服务器;</li>
<li>这个时候本地DNS服务器就拿到了<code>www.luoruihuan.top</code>这个域名对应的IP地址。</li>
</ul></blockquote><p>从上述过程中，可以看出网址的解析是一个从右向左的过程: <code>top</code> -&gt;<code>luoruihuan.top</code> -&gt; <code>www.luoruihuan.top</code>。但是你是否发现少了点什么，根域名服务器的解析过程呢？事实上，真正的网址是<code>www.luoruihuan.top.</code>，并不是我多打了一个<code>.</code>，这个<code>.</code>对应的就是根域名服务器，默认情况下所有的网址的最后一位都是<code>.</code>，既然是默认情况下，为了方便用户，通常都会省略，浏览器在请求DNS的时候会自动加上，所有网址真正的解析过程为: <code>.</code> -&gt; <code>.top</code> -&gt; <code>luoruihuan.top.</code> -&gt; <code>www.luoruihuan.top.</code>。</p><p>DNS 缓存，DNS存在着多级缓存，从离浏览器的距离排序的话，有以下几种: 浏览器缓存，系统缓存，路由器缓存，IPS服务器缓存，根域名服务器缓存，顶级域名服务器缓存，主域名服务器缓存。</p><br /></section></section>
<section><h5>二、TCP 链接(三次握手)<a href="#二tcp-链接三次握手"><span>#</span></a></h5><p>所谓三次握手(Three-way Handshake)，是指建立一个 TCP 连接时，需要客户端和服务器总共发送3个包。</p><p>三次握手的目的是连接服务器指定端口，建立 TCP 连接，并同步连接双方的序列号和确认号，交换 TCP 窗口大小信息。</p><section><h6>1、客户端发送带有SYN标识（SYN=1，seq=x）的请求报文段，然后进入SYN_SEND状态，等待服务端确认;<a href="#1客户端发送带有syn标识syn1seqx的请求报文段然后进入syn_send状态等待服务端确认"><span>#</span></a></h6></section><section><h6>2、服务端接收到客户端SYN报文段后，需要发送ACK信息对这个SYN进行确认，同时还要发送自己的SYN信息(SYN=1，ACK=1,seq=y，ack=x+1)服务端把这些信息放在一个报文段中((SYN+ACK报文段），一并发给客户端，此时客户端进入SYN_RECV状态;<a href="#2服务端接收到客户端syn报文段后需要发送ack信息对这个syn进行确认同时还要发送自己的syn信息syn1ack1seqyackx1服务端把这些信息放在一个报文段中synack报文段一并发给客户端此时客户端进入syn_recv状态"><span>#</span></a></h6></section><section><h6>3、客户端接收到服务端的SYN+ACK报文段后会向服务端发送ACK（ACK=1，seq=x+，ack=y+1）确认报文段，这个报文段发送后， 客户端和服务端都进入ESTABLISHED状态，完成三次握手。<a href="#3客户端接收到服务端的synack报文段后会向服务端发送ackack1seqxacky1确认报文段这个报文段发送后-客户端和服务端都进入established状态完成三次握手"><span>#</span></a></h6><p></p><figure><img src="/uploads/tcpHand.png" alt="vim" /><figcaption>vim</figcaption></figure><p></p><p>为什么会三次握手？这块咋一看会感觉比较复杂，其实这个大家稍微思考一下就能明白，如果两个人需要互相确定可以通讯，步骤其实就是三次，类似于:</p><p><code>浏览器：在吗？</code>
<code>服务器：在的</code>
<code>浏览器: 好的，我找你是为了诚挚的邀请你，晚上请我吃饭。</code></p><p>这个过程多了或者少了都不能保证确保互相通信，实际信号就是:</p><p><code>浏览器：syn</code>
<code>服务器：syn + ack</code>
<code>浏览器: ack </code></p><hr /></section></section>
<section><h5>三、客户端HTTP请求<a href="#三客户端http请求"><span>#</span></a></h5><p>发送HTTP请求的过程就是: 构建HTTP请求报文并通过TCP协议发送到服务器指定端口(HTTP协议80/8080, HTTPS协议443)。 HTTP请求报文是由三部分组成: 请求行, 请求报头和请求正文。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>POST /login HTTP/1.1</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>Host: www.example.com</span></div></div><div><div><div>4</div></div><div><span>Content-Type: application/x-www-form-urlencoded</span></div></div><div><div><div>5</div></div><div><span>Content-Length: 30</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>username=hello&amp;password=123456</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h5>四、服务端响应HTTP请求<a href="#四服务端响应http请求"><span>#</span></a></h5><p>HTTP响应也是由 <code>Header</code> 和 <code>Body</code> 两部分组成，响应的第一行总是 HTTP版本 响应代码 响应说明，例如，<code>HTTP/1.1 200 OK</code> 表示版本是 <code>HTTP/1.1</code> ，响应代码是 <code>200</code>，响应说明是 <code>OK</code>。客户端只依赖响应代码判断HTTP响应是否成功。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>HTTP/1.1 200 OK</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>Content-Type: text/html</span></div></div><div><div><div>4</div></div><div><span>Content-Length: 133251</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>&lt;!DOCTYPE html&gt;</span></div></div><div><div><div>7</div></div><div><span>&lt;html&gt;&lt;body&gt;</span></div></div><div><div><div>8</div></div><div><span>&lt;h1&gt;Hello&lt;/h1&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>响应状态码是由3位数组成，第一个数字定义了响应的类别，且有五种可能取值:</p><blockquote><ul>
<li>1xx：指示信息–表示请求已接收，继续处理。</li>
<li>2xx：成功–表示请求已被成功接收、理解、接受。</li>
<li>3xx：重定向–要完成请求必须进行更进一步的操作。</li>
<li>4xx：客户端错误–请求有语法错误或请求无法实现。例如400表示因为Content-Type等各种原因导致的无效请求，404表示指定的路径不存在；</li>
<li>5xx：服务器端错误–服务器未能实现合法的请求。例如500表示服务器内部故障，503表示服务器暂时无法响应。</li>
</ul></blockquote><p>当浏览器收到第一个http响应后，解析html后又会发起一系列http请求，例如请求图片，文件等。请求图片时。服务器响应图片请求后，会直接把二进制内容的图片发送给浏览器，因此，服务器总是被动的接收客户端的一个HTTP请求，然后响应他，客户端则根据需要发送若干个HTTP请求。对于最早期的HTTP/1.0协议，每次发送一个HTTP请求，客户端都需要先创建一个新的TCP连接，然后，收到服务器响应后，关闭这个TCP连接。由于建立TCP连接就比较耗时，因此，为了提高效率，HTTP/1.1协议允许在一个TCP连接中反复发送-响应，这样就能大大提高效率，因为HTTP协议是一个请求-响应协议，客户端在发送了一个HTTP请求后，必须等待服务器响应后，才能发送下一个请求，这样一来，如果某个响应太慢，它就会堵住后面的请求。可见，HTTP/2.0进一步提高了效率。</p><hr /></section>
<section><h5>五、TCP链接断开<a href="#五tcp链接断开"><span>#</span></a></h5><p>TCP 的连接的拆除需要发送四个包，因此称为四次挥手(Four-way handshake)，。客户端或服务器均可主动发起挥手动作。</p><section><h6>1、第一次挥手(FIN=1，seq=x)<a href="#1第一次挥手fin1seqx"><span>#</span></a></h6><p>假设客户端想要关闭连接，客户端发送一个 FIN 标志位置为1的包，表示自己已经没有数据可以发送了，但是仍然可以接受数据。发送完毕后，客户端进入 FIN_WAIT_1 状态。</p></section><section><h6>2、第二次挥手(ACK=1，ACKnum=x+1)<a href="#2第二次挥手ack1acknumx1"><span>#</span></a></h6><p>服务器端确认客户端的 FIN 包，发送一个确认包，表明自己接受到了客户端关闭连接的请求，但还没有准备好关闭连接。
发送完毕后，服务器端进入 CLOSE_WAIT 状态，客户端接收到这个确认包之后，进入 FIN_WAIT_2 状态，等待服务器端关闭连接。</p></section><section><h6>3、第三次挥手(FIN=1，seq=y)<a href="#3第三次挥手fin1seqy"><span>#</span></a></h6><p>服务器端准备好关闭连接时，向客户端发送结束连接请求，FIN 置为1。
发送完毕后，服务器端进入 LAST_ACK 状态，等待来自客户端的最后一个ACK。</p></section><section><h6>4、第四次挥手(ACK=1，ACKnum=y+1)<a href="#4第四次挥手ack1acknumy1"><span>#</span></a></h6><p>客户端接收到来自服务器端的关闭请求，发送一个确认包，并进入 TIME_WAIT状态，等待可能出现的要求重传的 ACK 包。</p><p>服务器端接收到这个确认包之后，关闭连接，进入 CLOSED 状态。</p><p>客户端等待了某个固定时间（两个最大段生命周期，2MSL，2 Maximum Segment Lifetime）之后，没有收到服务器端的 ACK ，认为服务器端已经正常关闭连接，于是自己也关闭连接，进入 CLOSED 状态。</p><p><b>当然4次挥手主要就是为了保证数据互相完成发送，例如：</b></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>浏览器</span><span>：</span><span>吃饱了</span><span>，</span><span>我要走了</span><span>。</span></span></div></div><div><div><div>2</div></div><div><span><span>服务器</span><span>：</span><span>干饭人等一下</span><span>，</span><span>我看一下晚上吃饭钱你转给我没有</span><span>。</span></span></div></div><div><div><div>3</div></div><div><span><span>服务器</span><span>：</span><span>钱收到了</span><span>，</span><span>那我也走了</span><span>。</span></span></div></div><div><div><div>4</div></div><div><span><span>浏览器</span><span>：</span><span>好的</span><span>。</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当然大家可能想问，其实三次也是可以关闭，为什么非要分四次，这是因为服务端在LISTEN状态下，收到关闭连接的报文时，仅仅表示对方不在发送数据，但是还是可以接受数据的，服务端是否现在关闭数据通道可以理解为上层应用是否还有其他事情要做，因此服务端的ACK和FIN一般都是分开发送。</p><p></p><figure><img src="/uploads/tcpend.png" alt="vim" /><figcaption>vim</figcaption></figure><p></p><hr /></section></section>
<section><h5>六、浏览器渲染资源回流<a href="#六浏览器渲染资源回流"><span>#</span></a></h5><p><b>导致 reflow 发生的一些原因：</b></p><blockquote><ul>
<li>改变窗口大小</li>
<li>改变文字大小</li>
<li>添加删除样式变</li>
<li>内容的改变</li>
<li>激活伪类</li>
<li>操作class属性</li>
<li>脚本操作DOM</li>
<li>计算offsetWidth和offsetHeight</li>
<li>设置style属性</li>
</ul></blockquote><p><b>减少 reflow 的一些方法</b></p><blockquote><ul>
<li>避免设置大量的style属性，因为设置style每一次都会触发refolw 最好使用class</li>
<li>尽量不使用table布局，因为因为table中某个元素旦触发了reflow，那么整个table的元素都会触发reflow。那么在不得已使用table的场合，可以设置table-layout;或者是table-layout，这种做法也是为了限制reflow的影响范围。</li>
<li>如果css里面有计算表达式，每次都会重新计算一遍，触发一次reflow</li>
</ul></blockquote></section>
<section><h5>七、总结<a href="#七总结"><span>#</span></a></h5><p>    上面主要介绍了一次完整请求对应的过程，了解这个过程的主要目的其实也就是为了web优化，前端的本质一句话可以很好地概括，将用户所需要的信息，快速并友好的展示给用户，并能够与用户进行交互，快速肯定就是尽可能短的时间内完成页面的加载，当然，如何快速的完成页面加载，可以参考雅虎34条军规，这个34条军规实际上就是围绕请求过程进行的一些优化方式。如何尽快的加载资源，大概的一个总体思路，能不从网络中加载的资源就不从网络中加载，这就要我们合理利用缓存，将资源放在浏览器端，这也是最快的方式。如果资源必须从网络中加载，则考虑缩短连接时间，也就是上面提到的DNS优化，类似于DNS 缓存，DNS 负载均衡等，Http方面可以优化响应内容大小，即对内容进行压缩。另一方面，当资源到达浏览器以后，浏览器开始进行解析渲染，浏览器中最耗时的部分就是reflow 回流，围绕这一部分考虑的就是如何减少reflow的次数，reflow 是导致DOM脚本执行效率低的关键因素之一，页面上任何一个节点触发了 reflow，都会导致他的子节点及祖先节点重新渲染，</p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/TypeScript2/</id>
      <title type="text">TypeScript 基础入门知识分享(二)</title>
      <published>2022-10-13T14:19:00.000Z</published>
      <updated>2022-10-13T14:19:00.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/TypeScript2/"/>
      <summary type="text">一、简单介绍 第一篇的时候简单介绍了TS的基本和常用用法，这篇就用webpack搭建一个ts+react的简易demo，react也有官方的脚手架去使用，但是还是感觉自己搭建的脚手架更加可控。 二、搭</summary>
      <content type="html"><![CDATA[<section><h3>一、简单介绍<a href="#一简单介绍"><span>#</span></a></h3><p>     第一篇的时候简单介绍了TS的基本和常用用法，这篇就用webpack搭建一个ts+react的简易demo，react也有官方的脚手架去使用，但是还是感觉自己搭建的脚手架更加可控。</p></section>
<section><h3>二、搭建开发环境<a href="#二搭建开发环境"><span>#</span></a></h3><section><h4>从头搭建一个基于react + ts + webpack + Hooks 的开发环境<a href="#从头搭建一个基于react--ts--webpack--hooks-的开发环境"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>//</span><span> </span><span>初始化文件夹文件和</span><span> </span><span>package.json</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>  </span><span>$</span><span>  </span><span>mkdir</span><span> </span><span>React-Typescript-Hooks</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>$</span><span>  </span><span>touch</span><span> </span><span>webpack.config.js</span><span> </span><span>index.html</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>$</span><span>  </span><span>npm</span><span> </span><span>init</span><span> </span><span>-y</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>打开 webpack.config.js文件，初始化简单的 webpack 配置<a href="#打开-webpackconfigjs文件初始化简单的-webpack-配置"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>const</span><span> </span><span>path</span><span> </span><span>=</span><span> </span><span>require</span><span>(</span><span>"path"</span><span>);</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>module</span><span>.</span><span>exports</span><span> </span><span>=</span><span> {</span></div></div><div><div><div>4</div></div><div><span>entry</span><span>:</span><span> </span><span>"./src/index.tsx"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>output</span><span>:</span><span> {</span></div></div><div><div><div>6</div></div><div><span>        </span><span>path</span><span>:</span><span><span> </span><span>path</span><span>.</span></span><span>resolve</span><span><span>(</span><span>__dirname</span><span>, </span></span><span>"build"</span><span>),</span></div></div><div><div><div>7</div></div><div><span>        </span><span>filename</span><span>:</span><span> </span><span>"bundle.js"</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>9</div></div><div><span>    </span><span>module</span><span>:</span><span> {},</span></div></div><div><div><div>10</div></div><div><span>    </span><span>plugins</span><span>:</span><span> []</span></div></div><div><div><div>11</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>然后新增一个入口文件index.tsx<a href="#然后新增一个入口文件indextsx"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>React</span><span> </span><span>from</span><span> </span><span>"react"</span><span>;</span></div></div><div><div><div>2</div></div><div><span>import</span><span> </span><span>*</span><span><span> </span><span>as</span><span> </span></span><span>ReactDOM</span><span> </span><span>from</span><span> </span><span>"react-dom"</span><span>;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>const</span><span> </span><span>ROOT</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>querySelector</span><span>(</span><span>".container"</span><span>);</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span><span>ReactDOM</span><span>.</span></span><span>render</span><span>(&lt;</span><span>h1</span><span>&gt;Hello&lt;/</span><span>h1</span><span>&gt;, </span><span>ROOT</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><section><h5>注：for Typescript you have to import all (*) as React.<a href="#注for-typescript-you-have-to-import-all--as-react"><span>#</span></a></h5></section></section><section><h4>接着需要安装react，webpack依赖模块<a href="#接着需要安装reactwebpack依赖模块"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>//</span><span> </span><span>注：webpack4.0后，webpack</span><span> </span><span>命令行相关的内容都迁移到</span><span> </span><span>webpack-cli，除了</span><span> </span><span>webpack</span><span> </span><span>外，我们还需要安装</span><span> </span><span>webpack-cli</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>  </span><span>$</span><span>  </span><span>npm</span><span> </span><span>i</span><span> </span><span>react</span><span> </span><span>react-dom</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>  </span><span>$</span><span>  </span><span>npm</span><span> </span><span>i</span><span> </span><span>webpack</span><span> </span><span>webpack-dev-server</span><span> </span><span>webpack-cli</span><span> </span><span>-D</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>我们现在已经有了ts的文件，但是webpack并不能解析，所以需要安装ts解析器 awesome-typescript-loader，为什么不用ts-loader，而选择用 awesome-typescript-loader，你可以<a href="https://github.com/s-panferov/awesome-typescript-loader#differences-between-ts-loader" target="_blank">点击这里查看。</a><a href="#我们现在已经有了ts的文件但是webpack并不能解析所以需要安装ts解析器-awesome-typescript-loader为什么不用ts-loader而选择用-awesome-typescript-loader你可以点击这里查看"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>$</span><span>  </span><span>npm</span><span> </span><span>i</span><span> </span><span>typescript</span><span> </span><span>awesome-typescript-loader</span><span> </span><span>-D</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>更新webpack.config，新建对tsx的规则处理<a href="#更新webpackconfig新建对tsx的规则处理"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>module</span><span>: {</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span>rules</span><span>: [</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>{</span></div></div><div><div><div>4</div></div><div><span>      </span><span>test</span><span>:</span><span><span> /</span><span>\.</span><span>tsx</span><span>?</span></span><span>$</span><span>/</span><span>,</span></div></div><div><div><div>5</div></div><div><span>      </span><span>loader</span><span>:</span><span> </span><span>"awesome-typescript-loader"</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>  </span></span><span>]</span></div></div><div><div><div>8</div></div><div><span>},</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>我们还需要在根路径创建一个 tsconfig.json 文件，来告诉 webpack 如何去处理TS的文件<a href="#我们还需要在根路径创建一个-tsconfigjson-文件来告诉-webpack-如何去处理ts的文件"><span>#</span></a></h4><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>  </span></span><span>{</span></div></div><div><div><div>2</div></div><div><span>  </span><span>"compilerOptions"</span><span>: {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>"allowSyntheticDefaultImports"</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>4</div></div><div><span>    </span><span>"jsx"</span><span>:</span><span> </span><span>"react"</span><span>,</span></div></div><div><div><div>5</div></div><div><span>    </span><span>"module"</span><span>:</span><span> </span><span>"commonjs"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>    </span><span>"outDir"</span><span>:</span><span> </span><span>"./build/"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>    </span><span>"preserveConstEnums"</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>"removeComments"</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>"sourceMap"</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>10</div></div><div><span>    </span><span>"noImplicitAny"</span><span>:</span><span> </span><span>true</span><span>,</span></div></div><div><div><div>11</div></div><div><span>    </span><span>"target"</span><span>:</span><span> </span><span>"es5"</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>},</span></div></div><div><div><div>13</div></div><div><span>  </span><span>"include"</span><span>: [</span></div></div><div><div><div>14</div></div><div><span>    </span><span>"./src/**/*"</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>]</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>接下来我们需要安装react的声明文件，由于 React 和 React-dom 并不是使用 TS 进行开发的，所以 TS 不知道 React、 React-dom 的类型，以及该模块导出了什么，此时需要引入 .d.ts 的声明文件。<a href="#接下来我们需要安装react的声明文件由于-react-和-react-dom-并不是使用-ts-进行开发的所以-ts-不知道-react-react-dom-的类型以及该模块导出了什么此时需要引入-dts-的声明文件"><span>#</span></a></h4><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>$</span><span>  </span><span>npm</span><span> </span><span>i</span><span> </span><span>@types/react</span><span> </span><span>@types/react-dom</span><span> </span><span>-D</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>接下来就只要将webpack.config中的test命令更改成下面，就可以直接启动项目了,然后在浏览器中输入localhost&lt;8080&gt; 就可以直接查看。<a href="#接下来就只要将webpackconfig中的test命令更改成下面就可以直接启动项目了然后在浏览器中输入localhost-就可以直接查看"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>"start"</span><span>: </span><span>"webpack-dev-server"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/TypeScript/</id>
      <title type="text">TypeScript 基础入门知识分享(一)</title>
      <published>2022-06-30T15:09:41.000Z</published>
      <updated>2022-06-30T15:09:41.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/TypeScript/"/>
      <summary type="text">一、简单介绍 从第一次接触TS到现在其实已经有一年多的时间，当初在上家公司是开始技术栈使用Angular 2，大家都知道Angular2框架是使用TypeScript开发的，Angular和TypeS</summary>
      <content type="html"><![CDATA[<section><h3>一、简单介绍<a href="#一简单介绍"><span>#</span></a></h3><p>    从第一次接触TS到现在其实已经有一年多的时间，当初在上家公司是开始技术栈使用Angular 2，大家都知道Angular2框架是使用TypeScript开发的，Angular和TypeScript一起使用非常简单方便，那时候ES6语法都还比较新，TS可以很好地去补充一些新的属性API，加上那时候后台用的ASP.NET Core，借那个机会也学习了一波，不过最后项目转型使用了vue全家桶，也就放弃了TS的使用，虽然现在vue对ts的支持也已经很好了，现在的工作技术栈集中在React上，所以可以简单的分享下一些TypeScript的用法，和利用webpack+ TypeScript + React Hooks 搭建一个简单的TodoList.这次会分两篇文章，第一篇大致介绍下ts用法，第二篇用webpack搭建一个ts+react的简易demo。</p></section>
<section><h3>二、TypeScript 概述和基本用法<a href="#二typescript-概述和基本用法"><span>#</span></a></h3><p>    TypeScript本质上是向javaScript语言添加了可选的静态类型和基于类的面向对象编程，同时也支持诸如接口，命名空间，装饰器等特性，相当于js的超集。因为ES6大家都已经很熟悉，所有一些ES6已经包含的语法这边就不在赘述，类似于声明解构赋值，let，const使用，可选参数，默认参数，箭头函数之类的。</p><section><h4>1、安装<a href="#1安装"><span>#</span></a></h4><p>TypeScript相关的工具一般都是通过npm进行安装，安装命令如下：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>typescript@3.6.0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>安装完成后可以来编写第一个TypeScript程序，类似于新建一个hello.ts，加入代码 <code>console.log('Hello TypeScript!')</code>，然后通过tsc编译器来编译ts文件，生成相对应的javascript文件，编译代码如下：编译过后的文件可以直接在浏览器中运行</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>tsc</span><span> </span><span>hello.ts</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>2、基本类型<a href="#2基本类型"><span>#</span></a></h4><p>在Ts中，提供了一下基本类型：</p><blockquote><ul>
<li>布尔类型（boolean） <code> eg : let isDone: boolean = false;</code></li>
<li>数字类型 (number)   <code> eg: let decLiteral: number = 6; 支持二、八、十、十六进制</code></li>
<li>字符串类型 (string) <code> eg: let name: string = "bob"; 支持模板字符串</code></li>
<li>数组类型 (array)    <code> eg: let list: number[] = [1, 2, 3]; 或者泛型数组，Array&lt;元素类型&gt;</code></li>
<li>元祖类型 (trup)     <code> eg: let x: [string, number]; 表示一个已知元素数量和类型的数组，各元素的类型不必相同</code></li>
<li>枚举类型（enum） <code> eg : enum Color {Red, Green, Blue} let c: Color = Color.Green;</code></li>
<li>任意值类型（any） <code> eg : let notSure: any = 4; 一般用于忽略类型检查</code></li>
<li>空值（void）    <code>eg: (): void 一般用于函数没有返回值的情况，像是和any类型相反</code></li>
<li>Null 和 Undefined <code>是所有类型的子类型，只有当指定了--strictNullChecks标记，才会严格检查</code></li>
<li>Never 类型  <code>表示永不存在的值，一般用于抛出错误时使用</code></li>
</ul></blockquote></section><section><h4>3、类<a href="#3类"><span>#</span></a></h4><p>    ES6中已经支持了基于类的面向对象编程，在Ts中，同样也是使用extends关键字实现继承，封装，继承，多态，是面向对象的三大特性。类必须拥有一个构造函数，如果类扩展了其他类，则创建的任何构造函数都必须要包含对super的调用。这里主要是着重说一下Ts中的类的修饰符。在类中的修饰符可以分为公共(public)、私有(private)、受保护(protected)三种。</p><p>     <code>I、Public 修饰符</code>
       在TypeScript里，成员都默认为public。可以自由的访问程序里定义的成员。
    <code>II、Private 修饰符</code>
       当成员被标记成private时，它就不能在声明它的类的外部访问。
    <code>III、Protected 修饰符</code>
       protected修饰符与private修饰符的行为很相似，但有一点不同，protected成员在派生类中仍然可以访问。
    <code>IV、Readonly 修饰符</code>
       可以使用readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
    <code>V、静态属性 static</code>
    到目前为止，我们只讨论了类的实例成员，那些仅当类被实例化的时候才会被初始化的属性。 我们也可以创建类的静态成员，这些属性存在于类本身上面而不是类的实例上。</p></section><section><h4>4、接口<a href="#4接口"><span>#</span></a></h4><p>    Ts的接口使用方式类似于Java，同时还增加了更加灵活的接口类型，包括属性、函数、可索引和类等类型。在TypeScript里，接口的作用就是为类型命名和自己的代码或第三方代码定义契约。在Ts中使用<code>Interface</code>关键字来定义接口。</p><p>    <code>I、属性接口</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>interface</span><span> </span><span>FullName</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>firstName</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>secondName</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    属性接口的定义很简单，ts还提供了可选属性，可选属性对可能存在的属性进行预定义，并兼容不传值的情况，用法就是在变量名后加一个？写法示例：<code>secondName？: string</code>。
    <code>II、函数类型接口</code>
    接口除了描述带有属性的普通对象外，也能描述函数类型，定义函数类型接口时，需要明确定义函数的参数列表，和返回值类型，且参数列表的每个参数都要有参数名和类型，示例：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>interface</span><span> </span><span>encrypt</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>（</span><span>val</span><span>:</span><span> </span><span>string</span><span><span>, </span><span>salt</span></span><span>:</span><span> </span><span>string</span><span>）</span><span>:</span><span> </span><span>string</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    <code>II、可索引类型接口</code>
    可索引类型接口用来描述那些可以通过索引得到的类型，类似于array[1],object[‘name’]等，其实就是通过特定的索引来得到指定类型的返回值。示例：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>interface</span><span> </span><span>indexArray</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>[</span><span>index</span><span>:</span><span> </span><span>number</span><span>]</span><span>:</span><span> </span><span>string</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div><span>    </span><span>let</span><span><span> </span><span>userArray</span></span><span>:</span><span> </span><span>indexArray</span><span>  </span><span>=</span><span> [</span><span>'A'</span><span>, </span><span>'B'</span><span>];</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>userArray</span><span>[</span><span>0</span><span>]   </span><span>// 结果为A</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    <code>III、类类型接口</code>
    类类型接口主要用来规范一个类的内容，示例：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>interface</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span>        </span><span>setName</span><span><span>(</span><span>n</span></span><span>:</span><span> </span><span>string</span><span>)</span><span>:</span><span> </span><span>void</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>    </span><span>class</span><span> </span><span>Dog</span><span> </span><span>implements</span><span> </span><span>Animal</span><span> {</span></div></div><div><div><div>7</div></div><div><span>        </span><span>constructor</span><span><span>(</span><span>n</span></span><span>:</span><span> </span><span>string</span><span>) {}</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>name</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>9</div></div><div><span>        </span><span>setName</span><span><span>(</span><span>n</span></span><span>:</span><span> </span><span>string</span><span>) {</span></div></div><div><div><div>10</div></div><div><span>            </span><span>this</span><span>.</span><span>name</span><span> </span><span>=</span><span><span> </span><span>n</span><span>;</span></span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    <code>IV、接口拓展</code>
    和类一样，接口也可以实现相互拓展，即能将成员从一个接口复制到另一个里面，这样可以更灵活的将接口拆分到可复用的模块里，一个接口可以继承多个接口，创建出多个接口的合成接口，示例：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>interface</span><span> </span><span>Shape</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>color</span><span>:</span><span> </span><span>string</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>    </span><span>interface</span><span> </span><span>Square</span><span> </span><span>extends</span><span> </span><span>Shape</span><span> {</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>sideLength</span><span>:</span><span> </span><span>number</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>    </span><span>let</span><span><span> </span><span>square</span><span> </span></span><span>=</span><span> &lt;</span><span>Square</span><span>&gt;</span><span>{}</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>square.color = "blue";</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>square.sideLength = 10;</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>5、泛型(generic)<a href="#5泛型generic"><span>#</span></a></h4><p>    在实际开发中，我们定义API不仅仅需要考虑功能是否健全，更多的还要考虑到API的复用性，这个时候急需要组件支持不特定的数据类型，而泛型就是用来实现这样的效果。示例：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>function</span><span> </span><span>identity</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>arg</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>T</span><span> {</span></div></div><div><div><div>2</div></div><div><span>        </span><span>return</span><span><span> </span><span>arg</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    我们给identity方法添加了类型变量<code>T</code>，<code>T</code>帮助我们捕获用户传入的类型，之后我们就可以使用这个类型，例子中就是使用了<code>T</code>作为返回值，这样我们就可以知道参数类型与返回值类型是相同的了。还有就是比较常用的场景就是使用泛型变量，我们改变下之前的例子：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>function</span><span> </span><span>identity</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>arg</span></span><span>:</span><span> </span><span>T</span><span>)</span><span>:</span><span> </span><span>T</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>arg</span><span>.</span></span><span>length</span><span>); </span><span>// Error: T doesn't have .length</span></div></div><div><div><div>3</div></div><div><span>        </span><span>return</span><span><span> </span><span>arg</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    如果直接这样使用的话编译器会报错，这是因为并没有地方指明arg具有这个属性，记住，这些类型变量代表的是任意类型，所以使用这个函数的人有可能传入的是个数字，就会报错。所以我们可以假设我们想操作的是<code>T</code>类型的数组，而不直接是<code>T</code>,所以我们可以这样：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>function</span><span> </span><span>identity</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>arg</span></span><span>:</span><span> </span><span>T</span><span>[])</span><span>:</span><span> </span><span>T</span><span>[] {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>arg</span><span>.</span></span><span>length</span><span>); </span><span>// // Array has a .length, so no more error</span></div></div><div><div><div>3</div></div><div><span>        </span><span>return</span><span><span> </span><span>arg</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>或者这种写法</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>function</span><span> </span><span>loggingIdentity</span><span>&lt;</span><span>T</span><span><span>&gt;(</span><span>arg</span></span><span>:</span><span> </span><span>Array</span><span>&lt;</span><span>T</span><span>&gt;)</span><span>:</span><span> </span><span>Array</span><span>&lt;</span><span>T</span><span>&gt; {</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>arg</span><span>.</span></span><span>length</span><span>);  </span><span>// Array has a .length, so no more error</span></div></div><div><div><div>3</div></div><div><span>        </span><span>return</span><span><span> </span><span>arg</span><span>;</span></span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h3>三、总结<a href="#三总结"><span>#</span></a></h3><p>上面总结了TypeScript的基本用法，这些用法算是TS中最基础的，但是是实际开发中用到最多的，二八原则，先花20%的时间学会80%的东西，剩下的高阶用法等到该用了，自然就会用了。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/nodeDeploy/</id>
      <title type="text">node前端线上部署与基础运维</title>
      <published>2022-04-16T13:10:35.000Z</published>
      <updated>2022-04-16T13:10:35.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/nodeDeploy/"/>
      <summary type="text">一、Linux基础相关 1、在Linux系统中安装node程序，Linux系统常用的有Ubuntu，centOS等，如果不能确定Linux具体的系统版本，可以通过命令查看. shell $ cat /</summary>
      <content type="html"><![CDATA[<section><h5>一、Linux基础相关<a href="#一linux基础相关"><span>#</span></a></h5><section><h6>1、在Linux系统中安装node程序，Linux系统常用的有Ubuntu，centOS等，如果不能确定Linux具体的系统版本，可以通过命令查看.<a href="#1在linux系统中安装node程序linux系统常用的有ubuntucentos等如果不能确定linux具体的系统版本可以通过命令查看"><span>#</span></a></h6><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>cat</span><span> </span><span>/etc/redhat-release</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h6>2、常用的安装方法是使用已编译好的版本，也可以通过编译安装，好处就是可以获取最新版，但是坑比较多。<a href="#2常用的安装方法是使用已编译好的版本也可以通过编译安装好处就是可以获取最新版但是坑比较多"><span>#</span></a></h6><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>$</span><span> </span><span>wget</span><span> </span><span>https</span><span>:</span></span><span>//nodejs.org/dist/v10.9.0/node-v10.9.0-linux-x64.tar.xz    // 下载</span></div></div><div><div><div>2</div></div><div><span><span>$</span><span> </span><span>tar</span><span> </span><span>xf</span><span>  </span><span>node</span></span><span>-</span><span><span>v10</span><span>.</span></span><span>9.0</span><span>-</span><span>linux</span><span>-</span><span><span>x64</span><span>.</span></span><span>tar</span><span>.</span><span>xz</span><span>       </span><span>// 解压</span></div></div><div><div><div>3</div></div><div><span><span>$</span><span> </span><span>cd</span><span> </span><span>node</span></span><span>-</span><span><span>v10</span><span>.</span></span><span>9.0</span><span>-</span><span>linux</span><span>-</span><span>x64</span><span>/</span><span>                  </span><span>// 进入解压目录</span></div></div><div><div><div>4</div></div><div><span><span>$</span><span> .</span></span><span>/</span><span>bin</span><span>/</span><span><span>node</span><span> </span></span><span>-</span><span><span>v</span><span>                               </span></span><span>// 查看版本</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 使用ln命令来设置软链接</span></div></div><div><div><div>2</div></div><div><span><span>$</span><span> </span><span>ln</span><span> </span></span><span>-</span><span><span>s</span><span> </span></span><span>/</span><span>usr</span><span>/</span><span>software</span><span>/</span><span>nodejs</span><span>/</span><span>bin</span><span>/</span><span><span>npm</span><span>   </span></span><span>/</span><span>usr</span><span>/</span><span>local</span><span>/</span><span>bin</span><span>/</span></div></div><div><div><div>3</div></div><div><span><span>$</span><span> </span><span>ln</span><span> </span></span><span>-</span><span><span>s</span><span> </span></span><span>/</span><span>usr</span><span>/</span><span>software</span><span>/</span><span>nodejs</span><span>/</span><span>bin</span><span>/</span><span><span>node</span><span>   </span></span><span>/</span><span>usr</span><span>/</span><span>local</span><span>/</span><span>bin</span><span>/</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h6>比较简单的可以直接通过EPEL安装，EPEL库安装的版本相对较低,可以通过安装node 版本工具 n 来进行管理。<a href="#比较简单的可以直接通过epel安装epel库安装的版本相对较低可以通过安装node-版本工具-n-来进行管理"><span>#</span></a></h6><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>$</span><span> </span><span>Sudo</span><span> </span><span>yum</span><span> </span><span>install</span><span> </span><span>epel</span></span><span>-</span><span>release</span></div></div><div><div><div>2</div></div><div><span><span>$</span><span> </span><span>sudo</span><span> </span><span>yum</span><span> </span><span>install</span><span> </span><span>nodejs</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h6>3、安装pm2 node 的管理工具<a href="#3安装pm2-node-的管理工具"><span>#</span></a></h6><p>全局安装</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>$</span><span> </span><span>npm</span><span> </span><span>install</span><span> </span><span>pm2</span><span> </span></span><span>-</span><span>g</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>pm2 管理node程序的好处</p><blockquote><p>内建负载均衡
支持性能监控
0 秒停机重载，我理解大概意思是维护升级的时候不需要停机.
具有 Ubuntu 和 CentOS 的启动脚本
停止不稳定的进程（避免无限循环）
服务器重新启动时自动重新启动
自动化部署(没研究)</p></blockquote><p>pm2常用命令</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 启动node程序</span></div></div><div><div><div>2</div></div><div><span><span>$</span><span> </span><span>pm2</span><span> </span><span>start</span><span> </span><span>app</span><span>.</span></span><span>js</span></div></div><div><div><div>3</div></div><div><span>// 列出所有进程</span></div></div><div><div><div>4</div></div><div><span><span>$</span><span> </span><span>pm2</span><span> </span><span>list</span></span></div></div><div><div><div>5</div></div><div><span>// 删除进程</span></div></div><div><div><div>6</div></div><div><span><span>$</span><span> </span><span>pm2</span><span> </span></span><span>delete</span><span> </span><span>0</span></div></div><div><div><div>7</div></div><div><span>// 重启进程</span></div></div><div><div><div>8</div></div><div><span><span>$</span><span> </span><span>pm2</span><span> </span><span>restart</span><span> </span><span>app</span></span></div></div><div><div><div>9</div></div><div><span>// 查看日志</span></div></div><div><div><div>10</div></div><div><span><span>$</span><span> </span><span>pm2</span><span> </span><span>logs</span><span> </span><span>app</span></span></div></div><div><div><div>11</div></div><div><span>// 设置pm2 开机自启(centos 下)</span></div></div><div><div><div>12</div></div><div><span><span>$</span><span> </span><span>pm2</span><span> </span><span>startup</span><span> </span><span>centos</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h6>4、vim 文本编辑器 相关命令<a href="#4vim-文本编辑器-相关命令"><span>#</span></a></h6><p>Vim 文本编辑在类unix系统中使用广泛，主用用来编辑文件内容，针对前端来说，平时简单修改配置，个人觉得
只需要熟练记住几个常用命令既可，如图：</p><p></p><figure><img src="/uploads/vimPhoto.png" alt="vim" /><figcaption>vim</figcaption></figure><p></p><p>截止目前我常用的就是编辑nginx配置，拿nginx举例基本操作如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 切换 root 账户</span></div></div><div><div><div>2</div></div><div><span><span>$</span><span> </span><span>sudo</span><span> </span><span>su</span><span> </span><span>root</span></span></div></div><div><div><div>3</div></div><div><span>// 查看nginx配置</span></div></div><div><div><div>4</div></div><div><span><span>$</span><span> </span><span>vim</span><span> </span></span><span>/</span><span>etc</span><span>/</span><span>nginx</span><span>/</span><span><span>nginx</span><span>.</span></span><span>conf</span></div></div><div><div><div>5</div></div><div><span>// 基本命令</span></div></div><div><div><div>6</div></div><div><span><span>$</span><span> </span><span>a</span><span> </span><span>进入编辑</span><span> :</span><span>wq</span><span> </span><span>保存退出</span><span> :</span><span>q</span></span><span>!</span><span><span> </span><span>不保存退出</span><span> </span></span><span>ESC</span><span><span> </span><span>退出编辑状态</span></span></div></div><div><div><div>7</div></div><div><span>// 重启nginx 服务</span></div></div><div><div><div>8</div></div><div><span><span>$</span><span> </span><span>nginx</span><span> </span></span><span>-</span><span><span>s</span><span> </span><span>reload</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h5>二、项目部署<a href="#二项目部署"><span>#</span></a></h5><section><h6>1、文件上传(通过Jenkins自动化构建)<a href="#1文件上传通过jenkins自动化构建"><span>#</span></a></h6><p>如果没有配置Jenkins 也可以手动通过命令上传</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>$</span><span> </span><span>scp</span><span> </span></span><span>-</span><span><span>r</span><span> </span></span><span>-</span><span>P</span><span> </span><span>10086</span><span> </span><span>/</span><span>Users</span><span>/</span><span>ronluo</span><span>/</span><span>Desktop</span><span>/</span><span>h5</span><span>/</span><span>gaia</span><span>-</span><span>flexi</span><span>-</span><span>cms</span><span>-</span><span>team</span><span>/</span><span>maya</span><span>-</span><span>server</span><span>/</span><span><span>dist</span><span>  </span><span>devops</span><span>@</span></span><span>47.102</span><span>.</span><span>207.109</span><span>:</span><span>/</span><span>home</span><span>/</span><span>devops</span><span>/</span><span>flexi</span><span>-</span><span>fuwu</span><span>/</span><span>Maya</span><span>-</span><span>server</span><span>/</span><span>dist</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h6>2、node服务启动<a href="#2node服务启动"><span>#</span></a></h6><p>服务器上传node dist包，使用pm2 启动，因为后台服务和前台放置在同一服务器，所以可以把后台地址配置为内网地址127.0.0.1&lt;8809&gt;，然后node中的public指向前端静态文件。</p><p>配置ngnix反向代理到node的端口，可以不使用ngnix去反向代理，也可以联系运维直接将域名指向node端口，但是找别人太麻烦，所以直接通过ngnix反向代理到node端口。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/waterMark/</id>
      <title type="text">input 调用相机和HTML5 getUserMedia API 实现图片的拍照加水印功能</title>
      <published>2022-01-11T08:09:41.000Z</published>
      <updated>2022-01-11T08:09:41.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/waterMark/"/>
      <summary type="text">一、业务回顾 之前接到过一个需求，需要用户在企业微信内使用我们的程序时，进行拍照功能，并添加水印后将水印图片存储至自己的阿里云图片服务器，本身需求并不难，因为以当时的了解，加水印可以直接canvas操</summary>
      <content type="html"><![CDATA[<section><h3>一、业务回顾<a href="#一业务回顾"><span>#</span></a></h3><p>    之前接到过一个需求，需要用户在企业微信内使用我们的程序时，进行拍照功能，并添加水印后将水印图片存储至自己的阿里云图片服务器，本身需求并不难，因为以当时的了解，加水印可以直接canvas操作，然后只需要调用企业微信暴露的chooseImage 方法得到图片file信息就可以，但是too native，企业微信对img的localIds进行了一层封装，无法触发img的onload事件，canvas自然就没办法去捕获绘图，尝试过二进制数据流，FileReader去进行文件转换，最后都没办法绕过，坑了很久，最后想着大不了不用微信的chooseImage，我自己去掉原生的API，getUserMedia获取用户拍照信息，很遗憾，这个方法在企业微信禁用了，所以最后的实现方案成了在后台服务器端加水印，然后通过腾讯服务器中转来获取图片，虽然最后的方案不尽人意，但是过程中还是学到了很多，就点，可以来简单的总结下getUserMedia的用法。</p></section>
<section><h3>二、getUserMedia API简介<a href="#二getusermedia-api简介"><span>#</span></a></h3><p>    HTML5的getUserMedia API为用户提供访问硬件设备媒体（摄像头、视频、音频、地理位置等）的接口，基于该接口，我们可以在不依赖任何插件的条件下访问硬件媒体设备。
getUserMedia API最初是navigator.getUserMedia，目前已被最新Web标准（2017版的）废除，变更为navigator.mediaDevices.getUserMedia(),但浏览器支持情况不如旧版API普及。所以实际业务场景中使用时，一般都会做一些兼容处理。MediaDevices.getUserMedia（）方法提示用户允许使用一个视频和/或一个音频输入设备，例如相机或屏幕共享和/或麦克风。如果用户给予许可，就返回一个Promise对象，MediaStream对象作为此Promise对象的Resolved状态的回调函数参数，如果用户拒绝了许可，或者没有媒体可用的情况下PermissionDeniedError或者NotFoundError作为此Promise的Rejected状态的回调函数参数。注意，由于用户不会被要求必须作出允许或者拒绝的选择，所以返回的Promise对象可能既不会触发resolve也不会触发 reject。</p></section>
<section><h3>三、实际使用<a href="#三实际使用"><span>#</span></a></h3><p>    具体的使用方法其实也很简单,html如下，一个简单的video标签获取当前的媒体流，一个拍照按钮，一个canvas容器对图片进行水印处理。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;</span><span>video</span><span> </span><span>id</span><span>=</span><span>"video"</span><span> </span><span>width</span><span>=</span><span>"320"</span><span> </span><span>height</span><span>=</span><span>"240"</span><span> </span><span>autoplay</span><span>&gt;&lt;/</span><span>video</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>&lt;</span><span>button</span><span> </span><span>id</span><span>=</span><span>"btn"</span><span>&gt;拍照&lt;/</span><span>button</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>canvas</span><span> </span><span>id</span><span>=</span><span>"canvas"</span><span> </span><span>width</span><span>=</span><span>"320"</span><span> </span><span>height</span><span>=</span><span>"240"</span><span> &gt;&lt;/</span><span>canvas</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>简单的JS代码如下：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>camera</span><span> </span></span><span>=</span><span> {</span></div></div><div><div><div>2</div></div><div><span>    </span><span>video</span><span>:</span><span><span> </span><span>document</span><span>.</span></span><span>getElementById</span><span>(</span><span>'video'</span><span>),</span></div></div><div><div><div>3</div></div><div><span>    </span><span>canvas</span><span>:</span><span><span>  </span><span>document</span><span>.</span></span><span>getElementById</span><span>(</span><span>'canvas'</span><span>),</span></div></div><div><div><div>4</div></div><div><span>    </span><span>btn</span><span>:</span><span><span> </span><span>document</span><span>.</span></span><span>getElementById</span><span>(</span><span>"btn"</span><span>),</span></div></div><div><div><div>5</div></div><div><span>    </span><span>font</span><span>:</span><span> </span><span>"14px microsoft yahei"</span><span>,</span></div></div><div><div><div>6</div></div><div><span>    </span><span>style</span><span>:</span><span> </span><span>"rgba(255,255,255,0.9)"</span><span>,</span></div></div><div><div><div>7</div></div><div><span>    </span><span>text</span><span>:</span><span> </span><span>"这是我要加的水印"</span><span>,</span></div></div><div><div><div>8</div></div><div><span>    </span><span>height</span><span>:</span><span> </span><span>240</span><span>, </span><span>width</span><span>:</span><span> </span><span>320</span><span>,</span></div></div><div><div><div>9</div></div><div><span>    </span><span>draw_pic</span><span>:</span><span> </span><span>function</span><span>(){</span></div></div><div><div><div>10</div></div><div><span>        </span><span>var</span><span><span> </span><span>self</span><span> </span></span><span>=</span><span> </span><span>this</span><span>;</span></div></div><div><div><div>11</div></div><div><span>        </span><span>var</span><span><span> </span><span>context</span><span> </span></span><span>=</span><span><span> </span><span>self</span><span>.</span></span><span>canvas</span><span>.</span><span>getContext</span><span>(</span><span>'2d'</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>context</span><span>.</span><span>drawImage</span><span><span>(</span><span>self</span><span>.</span></span><span>video</span><span>, </span><span>0</span><span>, </span><span>0</span><span><span>, </span><span>self</span><span>.</span></span><span>width</span><span><span>,</span><span>self</span><span>.</span></span><span>height</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>        </span></span><span>context</span><span>.</span><span>font</span><span> </span><span>=</span><span><span> </span><span>self</span><span>.</span></span><span>font</span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>        </span></span><span>context</span><span>.</span><span>fillStyle</span><span> </span><span>=</span><span><span> </span><span>self</span><span>.</span></span><span>style</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>context</span><span>.</span><span>fillText</span><span><span>(</span><span>self</span><span>.</span></span><span>text</span><span><span>, </span><span>self</span><span>.</span></span><span>width</span><span> </span><span>-</span><span> </span><span>140</span><span><span> , </span><span>self</span><span>.</span></span><span>height</span><span> </span><span>-</span><span> </span><span>10</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>17</div></div><div><span>    </span><span>bind</span><span>:</span><span> </span><span>function</span><span>(){</span></div></div><div><div><div>18</div></div><div><span>        </span><span>var</span><span><span> </span><span>self</span><span> </span></span><span>=</span><span> </span><span>this</span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>        </span></span><span>self</span><span>.</span><span>btn</span><span>.</span><span>addEventListener</span><span>(</span><span>"click"</span><span>, </span><span>function</span><span>() {</span></div></div><div><div><div>20</div></div><div><span><span>            </span></span><span>self</span><span>.</span><span>draw_pic</span><span>();</span></div></div><div><div><div>21</div></div><div><span><span>        </span></span><span>});</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>},</span></div></div><div><div><div>23</div></div><div><span>    </span><span>init</span><span>:</span><span> </span><span>function</span><span>(){</span></div></div><div><div><div>24</div></div><div><span>        </span><span>var</span><span><span> </span><span>video</span><span> </span></span><span>=</span><span> </span><span>this</span><span>.</span><span>video</span><span>;</span></div></div><div><div><div>25</div></div><div><span>        </span><span>// 原来的navigator.mediaDevices 方法已废弃， window.URL.createObjectURL 已废弃</span></div></div><div><div><div>26</div></div><div><span>        </span><span>// 想要获取一个最接近 1280x720 的相机分辨率</span></div></div><div><div><div>27</div></div><div><span>        </span><span>var</span><span><span> </span><span>constraints</span><span> </span></span><span>=</span><span> { </span><span>audio</span><span>:</span><span> </span><span>true</span><span>, </span><span>video</span><span>:</span><span> { </span><span>width</span><span>:</span><span> </span><span>1280</span><span>, </span><span>height</span><span>:</span><span> </span><span>720</span><span> } };</span></div></div><div><div><div>28</div></div><div>
</div></div><div><div><div>29</div></div><div><span><span>        </span></span><span>navigator</span><span>.</span><span>mediaDevices</span><span>.</span><span>getUserMedia</span><span><span>(</span><span>constraints</span><span>)</span></span></div></div><div><div><div>30</div></div><div><span><span>        </span></span><span>.</span><span>then</span><span>(</span><span>function</span><span><span>(</span><span>mediaStream</span><span>) {</span></span></div></div><div><div><div>31</div></div><div><span><span>          </span></span><span>video</span><span>.</span><span>srcObject</span><span> </span><span>=</span><span><span> </span><span>mediaStream</span><span>;</span></span></div></div><div><div><div>32</div></div><div><span>          </span><span>// 指定视频/音频（audio/video）的元数据加载后触发</span></div></div><div><div><div>33</div></div><div><span><span>          </span></span><span>video</span><span>.</span><span>onloadedmetadata</span><span> </span><span>=</span><span> </span><span>function</span><span><span>(</span><span>e</span><span>) {</span></span></div></div><div><div><div>34</div></div><div><span><span>            </span></span><span>video</span><span>.</span><span>play</span><span>();</span></div></div><div><div><div>35</div></div><div><span><span>          </span></span><span>};</span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>}).</span><span>catch</span><span>(</span><span>function</span><span><span>(</span><span>err</span><span>) {</span></span></div></div><div><div><div>37</div></div><div><span><span>            </span></span><span>console</span><span>.</span><span>log</span><span><span>(</span><span>err</span><span>.</span></span><span>name</span><span> </span><span>+</span><span> </span><span>": "</span><span> </span><span>+</span><span><span> </span><span>err</span><span>.</span></span><span>message</span><span>);</span></div></div><div><div><div>38</div></div><div><span><span>        </span></span><span>});</span></div></div><div><div><div>39</div></div><div><span>        </span><span>this</span><span>.</span><span>bind</span><span>();</span></div></div><div><div><div>40</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>41</div></div><div><span>};</span></div></div><div><div><div>42</div></div><div><span><span>camera</span><span>.</span></span><span>init</span><span>();</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>    实现效果如图，当用户调用相机时，获取视频流，然后将视频流设置为video元素的源，然后播放视频，点击拍照时绘制当前照片页面。
</p><figure><img src="/uploads/waterMack.png" alt="js-three-bg" /><figcaption>js-three-bg</figcaption></figure>
当然，功能很简单，注意事项还是不少的，例如我刚才开始写的时候是用的window.URL.createObjectURL方法去设置视频流，但是一直有问题，Google发现属性已经废弃，可以直接video.src = stream,来使用视频流。但因为老版浏览器对旧属性支持更好所以用try catch做了兼容处理。针对上面用到的onloadedmetadata方法，这边有一些关于video视频元数据加载的执行顺序钩子：onloadstart、ondurationchange、onloadedmetadata、onloadeddata、onprogress、oncanplay、oncanplaythrough.<p></p></section>
<section><h3>四、再次接触图片加水印<a href="#四再次接触图片加水印"><span>#</span></a></h3><p>    上面提到需求拍照加水印最后虽然实现了，但是是服务器端实现，占用服务器带宽，再加上现在手机拍照动辄十几M的图片大小，我们这个功能使用的客户数在两万左右，而且大概率是集中使用，这样就会导致服务器压力很大，当然，企业微信那块我们是真没办法处理，所以产品就把功能加在了标准版没有了企业微信的一系列限制，我们就可以大展手脚了，综合调研下，我们决定用input标签的 <strong>accept=“image/*” capture=“camera”</strong> 属性来实现仅调用相机，不从相册上传的限制，但是这个属性是有兼容问题，目前使用下来，除ios10.<em>.</em> 版本系统还是会提示从相机选择外，其他机型都是完美兼容的，既然客户同意，那咱开发自然肯定是没啥意见，功能点的实现大致是有以下几个坑。</p><blockquote><ul>
<li>1、input兼容：就是刚刚提到的input 仅调用相机的兼容问题</li>
<li>2、img图片渲染：对img元素加水印时，onLoad 事件的执行顺序问题。</li>
<li>3、前端图片压缩：现在的手机拍摄照片过大，上传服务器有较大压力，如果功能对图片质量要求不是很高的话，对图片进行压缩也是必须的。</li>
<li>4、IOS 拍照图片旋转角问题。</li>
<li>5、canvas绘图后图片失真问题。</li>
<li>6、图片转为二进制流数据传输时，toBlob 方法的兼容问题。</li>
</ul></blockquote><p>既然理论和坑都已经大致说在前面了，剩下的就是开始撸代码了，功能其实还是比较简单的，主要是踩坑过程比较艰辛，话不多说，上代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;</span><span>input</span><span> </span><span>type</span><span>=</span><span>"file"</span><span> </span><span>onChange</span><span>=</span><span>'onFileSelected(event)'</span><span> </span><span>accept</span><span>=</span><span>"image/*"</span><span> </span><span>capture</span><span>=</span><span>"camera"</span><span> /&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>为了demo简单，就将获取图片和加水印写在了一个方法里：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 参数为:file文件,水印内容，图片大小</span></div></div><div><div><div>2</div></div><div><span>Watermark</span><span> </span><span>=</span><span><span> (</span><span>file</span><span>, </span><span>text</span><span>, </span><span>maxWidth</span><span> </span></span><span>=</span><span> </span><span>400</span><span>) </span><span>=&gt;</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>return</span><span> </span><span>new</span><span> </span><span>Promise</span><span><span>((</span><span>resove</span><span>, </span><span>reject</span><span>)</span></span><span>=&gt;</span><span>{</span></div></div><div><div><div>4</div></div><div><span>        </span><span>// 上传之前通过FileReader方法去预览图片</span></div></div><div><div><div>5</div></div><div><span>        </span><span>let</span><span><span> </span><span>reader</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>FileReader</span><span>();</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>reader</span><span>.</span><span>onload</span><span> </span><span>=</span><span><span> (</span><span>e</span><span>)</span></span><span>=&gt;</span><span>{</span></div></div><div><div><div>7</div></div><div><span>            </span><span>let</span><span><span> </span><span>image</span><span> </span></span><span>=</span><span> </span><span>new</span><span> </span><span>Image</span><span>();</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>image</span><span>.</span><span>src</span><span> </span><span>=</span><span><span> </span><span>e</span><span>.</span></span><span>target</span><span>.</span><span>result</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>image</span><span>.</span><span>onload</span><span> </span><span>=</span><span> () </span><span>=&gt;</span><span> {</span></div></div><div><div><div>10</div></div><div><span>                </span><span>let</span><span><span> </span><span>canvas</span><span> </span></span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>createElement</span><span>(</span><span>'canvas'</span><span>);</span></div></div><div><div><div>11</div></div><div><span>                </span><span>let</span><span><span> </span><span>ctx</span><span> </span></span><span>=</span><span><span> </span><span>canvas</span><span>.</span></span><span>getContext</span><span>(</span><span>'2d'</span><span>);</span></div></div><div><div><div>12</div></div><div><span>                </span><span>let</span><span><span> { </span><span>width</span><span>, </span><span>height</span><span> } </span></span><span>=</span><span> </span><span>this</span><span>;</span></div></div><div><div><div>13</div></div><div><span>                </span><span>if</span><span><span>(</span><span>maxWidth</span><span> </span></span><span>!==</span><span> </span><span>0</span><span>){</span></div></div><div><div><div>14</div></div><div><span><span>                    </span></span><span>width</span><span> </span><span>=</span><span><span> </span><span>Math</span><span>.</span></span><span>min</span><span><span>(</span><span>maxWidth</span><span>, </span></span><span>this</span><span>.</span><span>width</span><span>);</span></div></div><div><div><div>15</div></div><div><span><span>                    </span></span><span>height</span><span> </span><span>=</span><span><span> </span><span>height</span><span> </span></span><span>*</span><span><span> (</span><span>width</span><span> </span></span><span>/</span><span> </span><span>this</span><span>.</span><span>width</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>                </span></span><span>}</span></div></div><div><div><div>17</div></div><div><span><span>                </span></span><span>canvas</span><span>.</span><span>width</span><span> </span><span>=</span><span><span> </span><span>width</span><span>;</span></span></div></div><div><div><div>18</div></div><div><span><span>                </span></span><span>canvas</span><span>.</span><span>height</span><span> </span><span>=</span><span><span> </span><span>height</span><span>;</span></span></div></div><div><div><div>19</div></div><div><span>                </span><span>// 水印内容</span></div></div><div><div><div>20</div></div><div><span><span>                </span></span><span>ctx</span><span>.</span><span>font</span><span>=</span><span>'26px Roboto-Medium'</span><span>; </span><span>//字体</span></div></div><div><div><div>21</div></div><div><span><span>                </span></span><span>ctx</span><span>.</span><span>fillStyle</span><span> </span><span>=</span><span> </span><span>'#FFFFFF'</span><span>;  </span><span>//字体颜色</span></div></div><div><div><div>22</div></div><div><span><span>                </span></span><span>ctx</span><span>.</span><span>fillText</span><span><span>(</span><span>text</span><span>, </span></span><span>20</span><span>, </span><span>35</span><span>);</span></div></div><div><div><div>23</div></div><div>
</div></div><div><div><div>24</div></div><div><span>                </span><span>// 如果需要base64的格式传输</span></div></div><div><div><div>25</div></div><div><span>                </span><span>let</span><span><span> </span><span>base64</span><span> </span></span><span>=</span><span><span> </span><span>canvas</span><span>.</span></span><span>toDataURL</span><span>(</span><span>'image/png'</span><span>);</span></div></div><div><div><div>26</div></div><div><span>                </span><span>resolve</span><span><span>(</span><span>base64</span><span>);</span></span></div></div><div><div><div>27</div></div><div>
</div></div><div><div><div>28</div></div><div><span>                </span><span>// 如果需要二进制流传输</span></div></div><div><div><div>29</div></div><div><span><span>                </span></span><span>canvas</span><span>.</span><span>toBlob</span><span><span>( </span><span>blob</span><span> </span></span><span>=&gt;</span><span> {</span></div></div><div><div><div>30</div></div><div><span>                    </span><span>resolve</span><span><span>(</span><span>blob</span><span>);</span></span></div></div><div><div><div>31</div></div><div><span><span>                </span></span><span>};</span></div></div><div><div><div>32</div></div><div>
</div></div><div><div><div>33</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>34</div></div><div><span><span>            </span></span><span>image</span><span>.</span><span>src</span><span> </span><span>=</span><span><span> </span><span>e</span><span>.</span></span><span>target</span><span>.</span><span>result</span><span>;</span></div></div><div><div><div>35</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>36</div></div><div><span><span>        </span></span><span>reader</span><span>.</span><span>onerror</span><span> </span><span>=</span><span><span> </span><span>reject</span><span>;</span></span></div></div><div><div><div>37</div></div><div><span><span>        </span></span><span>reader</span><span>.</span><span>readAsDataURL</span><span><span>(</span><span>file</span><span>);</span></span></div></div><div><div><div>38</div></div><div><span><span>    </span></span><span>})</span></div></div><div><div><div>39</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><section><h4>IOS 拍照旋转问题<a href="#ios-拍照旋转问题"><span>#</span></a></h4><p>主要的代码结构其实已经完成，针对上面提到问题，可以增加适当的兼容代码，例如：IOS 拍照旋转问题，需要引入exif-js插件，做一下处理：</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/** 获取照片方向角属性 */</span></div></div><div><div><div>2</div></div><div><span>EXIF</span><span>.</span><span>getData</span><span><span>(</span><span>image</span><span>, </span></span><span>function</span><span>(){</span></div></div><div><div><div>3</div></div><div><span>   </span><span>let</span><span><span> </span><span>orientation</span><span> </span></span><span>=</span><span> </span><span>EXIF</span><span>.</span><span>getTag</span><span>(</span><span>this</span><span>, </span><span>'Orientation'</span><span>);</span></div></div><div><div><div>4</div></div><div><span>    </span><span>switch</span><span><span> (</span><span>orientation</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>        </span><span>/** 需要顺时针90度旋转 */</span></div></div><div><div><div>6</div></div><div><span>        </span><span>case</span><span> </span><span>6</span><span>:</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>canvas</span><span>.</span><span>width</span><span> </span><span>=</span><span><span> </span><span>height</span><span>;</span></span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>canvas</span><span>.</span><span>height</span><span> </span><span>=</span><span><span> </span><span>width</span><span>;</span></span></div></div><div><div><div>9</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>save</span><span>();</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>translate</span><span><span>(</span><span>canvas</span><span>.</span></span><span>width</span><span> </span><span>/</span><span> </span><span>2</span><span><span>, </span><span>canvas</span><span>.</span></span><span>height</span><span> </span><span>/</span><span> </span><span>2</span><span>);</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>rotate</span><span>(</span><span>90</span><span> </span><span>*</span><span><span> </span><span>Math</span><span>.</span></span><span>PI</span><span> </span><span>/</span><span> </span><span>180</span><span>);</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>drawImage</span><span><span>(</span><span>image</span><span>, </span></span><span>0</span><span> </span><span>-</span><span><span> </span><span>canvas</span><span>.</span></span><span>height</span><span> </span><span>/</span><span> </span><span>2</span><span>, </span><span>0</span><span> </span><span>-</span><span><span> </span><span>canvas</span><span>.</span></span><span>width</span><span> </span><span>/</span><span> </span><span>2</span><span><span>, </span><span>canvas</span><span>.</span></span><span>height</span><span><span>, </span><span>canvas</span><span>.</span></span><span>width</span><span>);</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>restore</span><span>();</span></div></div><div><div><div>14</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>15</div></div><div><span>        </span><span>/** 需要逆时针90度旋转 */</span></div></div><div><div><div>16</div></div><div><span>        </span><span>case</span><span> </span><span>8</span><span>:</span></div></div><div><div><div>17</div></div><div><span><span>            </span></span><span>canvas</span><span>.</span><span>width</span><span> </span><span>=</span><span><span> </span><span>height</span><span>;</span></span></div></div><div><div><div>18</div></div><div><span><span>            </span></span><span>canvas</span><span>.</span><span>height</span><span> </span><span>=</span><span><span> </span><span>width</span><span>;</span></span></div></div><div><div><div>19</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>save</span><span>();</span></div></div><div><div><div>20</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>translate</span><span><span>(</span><span>canvas</span><span>.</span></span><span>width</span><span> </span><span>/</span><span> </span><span>2</span><span><span>, </span><span>canvas</span><span>.</span></span><span>height</span><span> </span><span>/</span><span> </span><span>2</span><span>);</span></div></div><div><div><div>21</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>rotate</span><span>(</span><span>-</span><span>90</span><span> </span><span>*</span><span><span> </span><span>Math</span><span>.</span></span><span>PI</span><span> </span><span>/</span><span> </span><span>180</span><span>);</span></div></div><div><div><div>22</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>drawImage</span><span><span>(</span><span>image</span><span>, </span></span><span>0</span><span> </span><span>-</span><span><span> </span><span>canvas</span><span>.</span></span><span>height</span><span> </span><span>/</span><span> </span><span>2</span><span>, </span><span>0</span><span> </span><span>-</span><span><span> </span><span>canvas</span><span>.</span></span><span>width</span><span> </span><span>/</span><span> </span><span>2</span><span><span>, </span><span>canvas</span><span>.</span></span><span>height</span><span><span>, </span><span>canvas</span><span>.</span></span><span>width</span><span>);</span></div></div><div><div><div>23</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>restore</span><span>();</span></div></div><div><div><div>24</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>25</div></div><div><span>        </span><span>/** 需要180度旋转 */</span></div></div><div><div><div>26</div></div><div><span>        </span><span>case</span><span> </span><span>3</span><span>:</span></div></div><div><div><div>27</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>save</span><span>();</span></div></div><div><div><div>28</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>rotate</span><span>(</span><span>180</span><span> </span><span>*</span><span><span> </span><span>Math</span><span>.</span></span><span>PI</span><span> </span><span>/</span><span> </span><span>180</span><span>);</span></div></div><div><div><div>29</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>drawImage</span><span>(</span><span>this</span><span>, </span><span>-</span><span><span>width</span><span>, </span></span><span>-</span><span><span>height</span><span>, </span><span>canvas</span><span>.</span></span><span>width</span><span><span>, </span><span>canvas</span><span>.</span></span><span>height</span><span>);</span></div></div><div><div><div>30</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>restore</span><span>();</span></div></div><div><div><div>31</div></div><div><span>            </span><span>break</span><span>;</span></div></div><div><div><div>32</div></div><div><span>        </span><span>default</span><span>:</span></div></div><div><div><div>33</div></div><div><span><span>            </span></span><span>ctx</span><span>.</span><span>drawImage</span><span>(</span><span>this</span><span>, </span><span>0</span><span>, </span><span>0</span><span><span>, </span><span>canvas</span><span>.</span></span><span>width</span><span><span>, </span><span>canvas</span><span>.</span></span><span>height</span><span>);</span></div></div><div><div><div>34</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>35</div></div><div><span>});</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>IOS10.0版本以下，toBlob方法不存在的兼容方法<a href="#ios100版本以下toblob方法不存在的兼容方法"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 当canvas对象的原型中没有toBlob方法的时候，手动添加该方法</span></div></div><div><div><div>2</div></div><div><span>if</span><span> (</span><span>!</span><span>HTMLCanvasElement</span><span>.</span><span>prototype</span><span>.</span><span>toBlob</span><span>) {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>Object</span><span>.</span><span>defineProperty</span><span>(</span><span>HTMLCanvasElement</span><span>.</span><span>prototype</span><span>, </span><span>'toBlob'</span><span>, {</span></div></div><div><div><div>4</div></div><div><span>        </span><span>value</span><span>:</span><span> </span><span>function</span><span><span> (</span><span>callback</span><span>, </span><span>type</span><span>, </span><span>quality</span><span>) {</span></span></div></div><div><div><div>5</div></div><div><span>            </span><span>var</span><span><span> </span><span>binStr</span><span> </span></span><span>=</span><span> </span><span>atob</span><span>(</span><span>this</span><span>.</span><span>toDataURL</span><span><span>(</span><span>type</span><span>, </span><span>quality</span><span>).</span></span><span>split</span><span>(</span><span>','</span><span>)[</span><span>1</span><span>]),</span></div></div><div><div><div>6</div></div><div><span><span>                </span></span><span>len</span><span> </span><span>=</span><span><span> </span><span>binStr</span><span>.</span></span><span>length</span><span>,</span></div></div><div><div><div>7</div></div><div><span><span>                </span></span><span>arr</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>Uint8Array</span><span><span>(</span><span>len</span><span>);</span></span></div></div><div><div><div>8</div></div><div><span>            </span><span>for</span><span> (</span><span>var</span><span><span> </span><span>i</span><span> </span></span><span>=</span><span> </span><span>0</span><span><span>; </span><span>i</span><span> </span></span><span>&lt;</span><span><span> </span><span>len</span><span>; </span><span>i</span></span><span>++</span><span>) {</span></div></div><div><div><div>9</div></div><div><span><span>                </span></span><span>arr</span><span>[</span><span>i</span><span>] </span><span>=</span><span><span> </span><span>binStr</span><span>.</span></span><span>charCodeAt</span><span><span>(</span><span>i</span><span>);</span></span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>11</div></div><div><span>            </span><span>callback</span><span>(</span><span>new</span><span> </span><span>Blob</span><span><span>([</span><span>arr</span><span>], { </span></span><span>type</span><span>:</span><span><span> </span><span>type</span><span> </span></span><span>||</span><span> </span><span>'image/png'</span><span> }));</span></div></div><div><div><div>12</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>});</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>Canvas图片压缩<a href="#canvas图片压缩"><span>#</span></a></h4><p>还有就是Canvas图片压缩，图片压缩原理其实很简单，核心API就是使用<code>Canvas</code>的<code>drawImage()</code>方法，API大致如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>context</span><span>.</span></span><span>drawImage</span><span><span>(</span><span>img</span><span>, </span><span>dx</span><span>, </span><span>dy</span><span>);</span></span></div></div><div><div><div>2</div></div><div><span><span>context</span><span>.</span></span><span>drawImage</span><span><span>(</span><span>img</span><span>, </span><span>dx</span><span>, </span><span>dy</span><span>, </span><span>dWidth</span><span>, </span><span>dHeight</span><span>);</span></span></div></div><div><div><div>3</div></div><div><span><span>context</span><span>.</span></span><span>drawImage</span><span><span>(</span><span>img</span><span>, </span><span>sx</span><span>, </span><span>sy</span><span>, </span><span>sWidth</span><span>, </span><span>sHeight</span><span>, </span><span>dx</span><span>, </span><span>dy</span><span>, </span><span>dWidth</span><span>, </span><span>dHeight</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>虽然参数很多，简单总结下其实可以归纳为如下三个参数：
    ** img : **   图片对象,也可以是虚拟DOM中的图片对象
    ** dx, dy, dWidth, dHeight : <strong>  表示在<code>canvas</code>画布上规划处一片区域用来放置图片，<code>dx</code>, <code>dy</code>为<code>canvas</code>元素的左上角坐标，<code>dWidth</code>, <code>dHeight</code>指<code>canvas</code>元素上用在显示图片的区域大小。如果没有指定<code>sx</code>,<code>sy</code>,<code>sWidth</code>,<code>sHeight</code>这4个参数，则图片会被拉伸或缩放在这片区域内。
    </strong> sx, sy, swidth, sheight : **   这4个坐标是针对图片元素的，表示图片在canvas画布上显示的大小和位置。<code>sx</code>,<code>sy</code>表示图片上<code>sx</code>,<code>sy</code>这个坐标作为左上角，然后往右下角的<code>swidth</code>,<code>sheight</code>尺寸范围图片作为最终在<code>canvas</code>上显示的图片内容。
当然，为了方便我个人一般都是用五个参数的语法，我们举个例子，假如一张图片的尺寸是1024 * 800，我们需要限制为102 * 80，核心代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>...</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>ctx</span><span>.</span><span>drawImage</span><span><span>(</span><span>img</span><span>,</span></span><span>0</span><span>,</span><span>0</span><span>,</span><span>102</span><span>,</span><span>80</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其实就这么一句代码就实现了图片压缩，有木有很简单。</p></section><section><h4><code>Canvas</code>绘图容易失真的问题<a href="#canvas绘图容易失真的问题"><span>#</span></a></h4><p>    最后还有一个关于<code>Canvas</code>绘图容易失真的问题，这块内容主要涉及到<code>devicePixelRatio</code> 设备像素比，这块的话，大家可以去看张鑫旭的这篇文章：<a href="https://www.zhangxinxu.com/wordpress/2012/08/window-devicepixelratio/" target="_blank">文章地址</a> 这块对<code>devicePixelRatio</code>属性进行了很详细的介绍，实际我们解决<code>canvas</code>画图模糊就是画一个两倍于实际大小的<code>canvas</code>，简单解释就是<code>devicePixelRatio</code>属性决定了用几个像素点渲染一个像素，假如某个屏幕的<code>devicePixelRatio</code>值为2，一张100 * 100像素的图片会用两个像素点渲染，实际图片其实会占据200 * 200像素空间，所以我们需要直接创建一个两倍实际大小<code>canvas</code>，然后用css样式把canvas限定在实际的大小，具体核心代码如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>...</span></div></div><div><div><div>2</div></div><div><span>var</span><span><span> </span><span>devicePixelRatio</span><span> </span></span><span>=</span><span><span> </span><span>window</span><span>.</span></span><span>devicePixelRatio</span><span> </span><span>||</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>3</div></div><div><span>var</span><span><span> </span><span>backingStoreRatio</span><span> </span></span><span>=</span><span><span> </span><span>context</span><span>.</span></span><span>webkitBackingStorePixelRatio</span><span> </span><span>||</span></div></div><div><div><div>4</div></div><div><span><span>                    </span></span><span>context</span><span>.</span><span>mozBackingStorePixelRatio</span><span> </span><span>||</span></div></div><div><div><div>5</div></div><div><span><span>                    </span></span><span>context</span><span>.</span><span>msBackingStorePixelRatio</span><span> </span><span>||</span></div></div><div><div><div>6</div></div><div><span><span>                    </span></span><span>context</span><span>.</span><span>oBackingStorePixelRatio</span><span> </span><span>||</span></div></div><div><div><div>7</div></div><div><span><span>                    </span></span><span>context</span><span>.</span><span>backingStorePixelRatio</span><span> </span><span>||</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>var</span><span><span> </span><span>ratio</span><span> </span></span><span>=</span><span><span> </span><span>devicePixelRatio</span><span> </span></span><span>/</span><span><span> </span><span>backingStoreRatio</span><span>;</span></span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>canvas</span><span>.</span><span>width</span><span> </span><span>=</span><span><span> </span><span>canvas</span><span>.</span></span><span>width</span><span> </span><span>*</span><span><span> </span><span>ratio</span><span>;</span></span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>canvas</span><span>.</span><span>height</span><span> </span><span>=</span><span><span> </span><span>canvas</span><span>.</span></span><span>height</span><span>*</span><span><span> </span><span>ratio</span><span>;</span></span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>context</span><span>.</span><span>scale</span><span><span>(</span><span>ratio</span><span>, </span><span>ratio</span><span>);</span></span></div></div><div><div><div>12</div></div><div><span>...</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h3>总结<a href="#总结"><span>#</span></a></h3><p>    嗯，到这里这篇文章差不多就要结束了，说的有点乱，大致就是前端实现图片上传，加水印，和后台数据传输（base64，二进制流）实现过程中的一些坑和注意事项，如有错误，不吝指教。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/summaryJs2/</id>
      <title type="text">前端知识点总结(六) JavaScript篇(二)</title>
      <published>2019-12-29T14:10:00.000Z</published>
      <updated>2019-12-29T14:10:00.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/summaryJs2/"/>
      <summary type="text">一、写在前面的叨叨叨 前两周整理一些比较核心的Js的基础，这两周又收集了一些问题和知识点，拿到这篇文章来整理回顾下，大部分其实都是一些初级面试经常问的， 可以作为一个题库，等需要面试的时候刷刷也是极好</summary>
      <content type="html"><![CDATA[<section><h4>一、写在前面的叨叨叨<a href="#一写在前面的叨叨叨"><span>#</span></a></h4><p>    前两周整理一些比较核心的Js的基础，这两周又收集了一些问题和知识点，拿到这篇文章来整理回顾下，大部分其实都是一些初级面试经常问的， 可以作为一个题库，等需要面试的时候刷刷也是极好的。这段时间晚上都在追庆余年，之前拿出来学习的时间也少了又少，不过剧终于追完了，也是该更新下博客拉！</p></section>
<section><h4>二、Js基础梳理<a href="#二js基础梳理"><span>#</span></a></h4><section><h5>1、创建对象的多种方式<a href="#1创建对象的多种方式"><span>#</span></a></h5><blockquote><ul>
<li>通过”字面量“方式创建。将成员信息写到{}中，并赋值给一个变量，此时这个变量就是一个对象。</li>
<li>通过”构造函数“方式创建。 var obj = new 函数名();</li>
<li>通过object方式创建。通过object构造器new一个对象，再往里丰富成员信息。</li>
</ul></blockquote></section><section><h5>2、变量提升(hoisting)<a href="#2变量提升hoisting"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>JavaScript 中，函数及变量的声明都将被提升到函数的最顶部。变量可以在使用后声明，也就是变量可以先使用再声明。</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>3、举例说明一个匿名函数的典型用例<a href="#3举例说明一个匿名函数的典型用例"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>自执行函数，用闭包模拟私有变量、特权函数等。</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>4、DOM事件的绑定的几种方式<a href="#4dom事件的绑定的几种方式"><span>#</span></a></h5><blockquote><ul>
<li>在DOM元素中直接绑定。</li>
<li>在JavaScript代码中绑定;</li>
<li>绑定事件监听函数。</li>
</ul></blockquote></section><section><h5>5、DOM事件中target和currentTarget的区别<a href="#5dom事件中target和currenttarget的区别"><span>#</span></a></h5><blockquote><ul>
<li>target&lt;触发事件的某个具体对象&gt;，只会出现在事件流的目标阶段；</li>
<li>currentTarget&lt;绑定事件的对象&gt;，恒等于this，可能出现在事件流的任意一个阶段中。</li>
</ul></blockquote></section><section><h5>6、手写Ajax，XMLHttpRequest<a href="#6手写ajaxxmlhttprequest"><span>#</span></a></h5><p><code>XMLHttpRequest对象的本质</code>：可以发送Http请求，处理Http响应，与服务器之间进行异步交换数据的对象，其核心是Http.
<code>具体实现步骤</code>：1、创建XMLHttpRequest对象。2、指定响应函数。3、打开连接（指定请求）4、发送请求。5、创建响应函数。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 声明一个变量，用来实例化XMLHttpRequest对象</span></div></div><div><div><div>2</div></div><div><span>var</span><span><span> </span><span>xmlhttp</span></span><span>=</span><span>null</span><span>;</span></div></div><div><div><div>3</div></div><div><span>if</span><span><span> (</span><span>window</span><span>.</span></span><span>XMLHttpRequest</span><span>){</span></div></div><div><div><div>4</div></div><div><span>    </span><span>// 新浏览器直接创建XMLHttpRequest对象</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>xmlhttp</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>XMLHttpRequest</span><span>( );</span></div></div><div><div><div>6</div></div><div><span>}</span><span>else</span><span> </span><span>if</span><span><span>(</span><span>window</span><span>.</span></span><span>ActiveXObject</span><span>){</span></div></div><div><div><div>7</div></div><div><span>    </span><span>// IE5或IE6没有XMLHttpRequest对象，而是用的ActiveXObject对象</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>Xmlhttp</span><span> </span><span>=</span><span> </span><span>new</span><span> </span><span>ActiveXObject</span><span>(</span><span>"Microsoft.XMLHTTP"</span><span>);</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>if</span><span><span> (</span><span>xmlhttp</span><span> </span></span><span>!==</span><span> </span><span>null</span><span>) {</span></div></div><div><div><div>12</div></div><div><span>    </span><span>// 指定响应函数为state_Change</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>xmlhttp</span><span>.</span><span>onreadystatechange</span><span>=</span><span><span>state_Change</span><span>;</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>// 指定请求，这里要访问note.xml文件，true代表的使用的是异步请求</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>xmlhttp</span><span>.</span><span>open</span><span>(</span><span>"GET"</span><span>,</span><span>"note.xml"</span><span>,</span><span>true</span><span>);</span></div></div><div><div><div>16</div></div><div><span><span>     </span></span><span>xmlhttp</span><span>.</span><span>send</span><span>(</span><span>null</span><span>);</span><span>//发送请求</span></div></div><div><div><div>17</div></div><div><span>} </span><span>else</span><span>{ </span><span>alert</span><span>(</span><span>"你得浏览器不支持XMLHTTP."</span><span>); }</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// 创建具体的响应函数state_Change</span></div></div><div><div><div>20</div></div><div><span>function</span><span> </span><span>state_Change</span><span>(){</span></div></div><div><div><div>21</div></div><div><span>    </span><span>if</span><span><span> (</span><span>xmlhttp</span><span>.</span></span><span>readyState</span><span> </span><span>===</span><span> </span><span>4</span><span>){</span></div></div><div><div><div>22</div></div><div><span>        </span><span>if</span><span><span> (</span><span>xmlhttp</span><span>.</span></span><span>status</span><span> </span><span>===</span><span> </span><span>200</span><span>){</span></div></div><div><div><div>23</div></div><div><span>          </span><span>// 这里应该是函数具体的逻辑</span></div></div><div><div><div>24</div></div><div><span><span>         </span></span><span>}</span><span>else</span><span>{ </span><span>alert</span><span>(</span><span>"Problem retrieving XML data"</span><span>) ;}</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>26</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h5>7、JS原生对象、内置对象、宿主对象。<a href="#7js原生对象内置对象宿主对象"><span>#</span></a></h5><p>     <strong>原生（Naitve）对象：</strong> 独立于宿主环境的 ECMAScript实现提供的对象，包含：<code>Object</code>、<code>Function</code>、<code>Array</code>、<code>String</code>、<code>Boolean</code>、<code>Number</code>、<code>Date</code>、<code>RegExp</code>、<code>Error</code>、<code>EvalError</code>、<code>RangeError</code>、<code>ReferenceError</code>、<code>SyntaxError</code>、<code>TypeError</code>、<code>URIError</code>，简单来说，本地对象就是 ECMA-262 定义的类（引用类型）。
     <strong>内置（Build-in）对象：</strong> 由 ECMAScript实现提供的、独立于宿主环境的所有对象，在 ECMAScript程序开始执行时出现，而ECMA-262 只定义了两个内置对象，即 Global 和 Math，内置对象是原生对象的一种。
     <strong>宿主（host）对象：</strong> 所有非本地对象都是宿主对象，所有的BOM和DOM都是宿主对象，ECMAScript官方未定义的对象都属于宿主对象。</p></section><section><h5>8、<code>document load</code> 和 <code>document DOMContentLoaded</code> 两个事件的区别<a href="#8document-load-和-document-domcontentloaded-两个事件的区别"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>load是页面所有资源全部加载完成后（DOM文档树，css文件，js文件，图片资源等）;</span></div></div><div><div><div>2</div></div><div><span>DOMContentLoaded：当页面文档加载并解析完毕之后会马上出发 DOMContentLoaded 事件，而不会等待样式文件、图片文件和子框架页面的加载。</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>9、什么是“use strict”,好处和坏处<a href="#9什么是use-strict好处和坏处"><span>#</span></a></h5><p><code>use strict:</code> 意味着代码按照严格模式解析，这种模式使得Javascript在更严格的条件下运行。
<code>好处:</code> 消除Javascript语法的一些不合理、不严谨之处，减少一些怪异行为，消除代码运行的一些不安全之处，保证代码运行的安全；提高编译器效率，增加运行速度；
<code>坏处:</code> 同样代码，在”严格模式”中，可能会有不一样的运行结果；一些在”正常模式”下可以运行的语句，在”严格模式”下将不能运行。</p></section><section><h5>10、JS如何实现重载和多态<a href="#10js如何实现重载和多态"><span>#</span></a></h5><blockquote><ul>
<li>根据arguments个数实现重载, arguments检测传参个数，执行不同的方式。</li>
<li>JavaScript中大多是通过子类重写父类方法的方式实现多态。</li>
</ul></blockquote></section><section><h5>11、浅拷贝(Shallow Copy)和深拷贝(Deep Copy)<a href="#11浅拷贝shallow-copy和深拷贝deep-copy"><span>#</span></a></h5><p><code>浅拷贝：</code> 复制指向某个对象的指针，而不复制对象本身，新旧对象还是共享同一块内存。
<code>浅拷贝实现方式：</code> 1、简单地复制语句。 2、Object.assign() <span>(ES6)</span>
<code>深拷贝：</code> 创造一个一模一样的对象，新对象跟原对象不共享内存，修改新对象不会改到原对象。实现方式：</p><blockquote><ul>
<li>1、用 <code>JSON.stringify</code> 把对象转成字符串，再用 <code>JSON.parse</code> 把字符串转成新的对象。</li>
<li>2、<code>Object.create()</code>。</li>
<li>3、递归拷贝（注意避免互相引用导致死循环）</li>
</ul></blockquote></section><section><h5>12、CSSOM视图模式(CSSOM View Module)<a href="#12cssom视图模式cssom-view-module"><span>#</span></a></h5><blockquote><ul>
<li>Window视图属性</li>
</ul></blockquote><ul>
<li><code>innerWidth</code> 和 <code>innerHeight</code>：window窗体的内部宽度(高度)不包括窗框。</li>
<li><code>outerWidth</code> 和 <code>outerHeight</code>: 表示整个浏览器窗体的大小，包括任务栏。</li>
<li><code>pageXOffset</code> 和 <code>pageYOffset</code>: 整个页面滚动的像素值(水平方向和垂直方向的)。</li>
<li><code>screenX</code> 和 <code>screenY</code>: 浏览器窗口在显示器中的位置(screenX水平screenY垂直)。</li>
</ul><blockquote><ul>
<li>Screen视图属性</li>
</ul></blockquote><ul>
<li><code>availWidth</code> 和 <code>availHeight</code>: 显示器可用宽高，不包括任务栏等。</li>
<li><code>colorDepth</code>: 表示显示器的颜色深度。</li>
<li><code>width和height</code>: 表示显示器屏幕的宽高。</li>
</ul><blockquote><ul>
<li>文档(DocumentView)视图属性</li>
</ul></blockquote><ul>
<li><code>elementFromPoint:</code> 返回给定坐标处所在的元素。</li>
<li><code>getBoundingClientRect:</code> 得到矩形元素的界线，返回的是一个对象，包含 top, left, right, 和 bottom四个属性值，大小都是相对于文档视图左上角计算而来。</li>
<li><code>scrollIntoView:</code> 让元素滚动到可视区域。</li>
</ul><blockquote><ul>
<li>元素(DocumentView)视图属性</li>
</ul></blockquote><ul>
<li><code>clientLeft</code> 和 <code>clientTop</code>: 表示内容区域的左上角相对于整个元素左上角的位置（包括边框）。</li>
<li><code>clientWidth</code> 和 <code>clientHeight</code>: 表示内容区域的高度和宽度，包括padding大小，但是不包括边框和滚动条。</li>
<li><code>offsetLeft</code> 和 <code>offsetTop</code>: 表示相对于最近的祖先定位元素（CSS position 属性被设置为 relative、absolute 或 fixed 的元素）的左右偏移值。</li>
<li><code>offsetWidth</code> 和 <code>offsetHeight</code>: 整个元素的尺寸（包括边框）。</li>
<li><code>scrollLeft</code> 和 <code>scrollTop</code>: 表示元素滚动的像素大小。</li>
<li><code>scrollWidth</code> 和 <code>scrollHeight</code>: 表示整个内容区域的宽高，包括隐藏的部分。如果元素没有隐藏的部分，则相关的值应该等用于clientWidth和clientHeight。当你向下滚动滚动条的时候，scrollHeight应该等用于scrollTop + clientHeight。</li>
</ul><blockquote><ul>
<li>鼠标位置(Mouse position)属性</li>
</ul></blockquote><ul>
<li><code>clientX</code> 和 <code>clientY:</code> 鼠标相对于window的偏移。</li>
<li><code>pageX</code>, <code>pageY:</code> 鼠标相对于document的坐标。</li>
<li><code>screenX</code>, <code>screenY:</code> 鼠标相对于显示器屏幕的偏移坐标。</li>
</ul></section><section><h5>13、常用的数组api，字符串api，Math对象，Date日期对象<a href="#13常用的数组api字符串apimath对象date日期对象"><span>#</span></a></h5><blockquote><ul>
<li>数组API</li>
</ul></blockquote><ul>
<li><code>unshift():</code> 数组开头增加一个或多个元素，返回数组长度，原数组改变。</li>
<li><code>shift():</code> 删除数组第一个元素，返回被删除内的容，原数组改变。</li>
<li><code>push():</code> 数组末尾增加一个或多个元素，返回数组长度，原数组改变。</li>
<li><code>pop():</code> 删除数组末尾一项，返回被删除的内容，原数组改变。</li>
<li><code>concat():</code> 数组拼接，不改变原数组，返回被连接数组副本。不传参数可实现数组克隆。</li>
<li><code>splice(index, howmany, item1, ...itemx):</code> 根据参数实现数组的删除，增加，替换。
例: splice（index,0，item1，item2…)从索引index开始增加，增加的内容插入到索引index前面。</li>
<li><code>splice(index, n)</code> 从索引index开始删除n个，如果只有一个参数splice(index)，就是从索引index开始后面的内容全部删除。</li>
<li><code>Splice(index,n，item1，item2..）</code>从索引index开始替换n个,替换的内容为item1, item2…。</li>
<li><code>slice(n，m):</code> 从索引 n 截取到索引 m 但不包括 m ,原数组不发生改变。</li>
<li><code>reverse():</code> 数组翻转,返回反转后新数组，原数组发生改变。</li>
<li><code>sort():</code> 数组排序,使用方法一般sort（function（a，b）{return a-b }）从小到大排。</li>
<li><code>toString():</code> 把数组转成以逗号分隔的字符串。</li>
<li><code>join（拼接形式）:</code> 把数组拼接成以其他形式分割的字符串。</li>
</ul><blockquote><ul>
<li>字符串API</li>
</ul></blockquote><ul>
<li><code>charAt(index):</code> 通过索引找到该索引对应字符，包含空格。</li>
<li><code>charCodeAt(index):</code> 通过索引找到该索引字符对应的Unicode编码(0-65535的整数）。</li>
<li><code>indexOf( ):</code> 从前往后找，找到返回内容的索引，找不到返回-1。</li>
<li><code>lastIndexOf( ):</code> 从后往前找，找到返回内容的索引，找不到返回-1。</li>
<li><code>slice(n，m):</code> 从索引n 查找到索引m ,但不包括m，slice可以取负值。</li>
<li><code>substring(n，m):</code> 从索引n 查找到索引m ，但不包括m， 不可以取负值。</li>
<li><code>substr（n，m):</code> 从索引n开始截取m 个</li>
<li><code>split（切割形式）:</code>  把一个字符串分割成字符串数组。</li>
<li><code>toUpperCase（）</code> &lt;转大写字母&gt;</li>
<li><code>toLowerCase（）</code> &lt;转小写字母&gt;</li>
</ul><blockquote><ul>
<li>Math 对象API</li>
</ul></blockquote><ul>
<li><code>Math.floor( ):</code> 向下取整</li>
<li><code>Math.ceil( ):</code> 向上取整</li>
<li><code>Math.round( ):</code> 四舍五入</li>
<li><code>Math.random( ):</code> 取0-1之间的随机小数</li>
<li><code>Math.pow(x,y):</code> x的y次幂</li>
<li><code>Math.abs( ):</code> 取绝对值  Math.sqrt( )开平方</li>
<li><code>Math.max/min( ):</code> 取最大/小值</li>
</ul><blockquote><ul>
<li>Date日期对象API</li>
</ul></blockquote><ul>
<li><code>new Date( ):</code> 创建一个日期对象</li>
<li><code>getFullYear()</code> 返回年份</li>
<li><code>getMonth( ):</code> 返回月份数（0-11）, 想要得到几月，需要加一</li>
<li><code>getDay( ):</code> 返回一周的第几天（0-6），想要得到星期几，需要加一。</li>
<li><code>getDate( ):</code> 返回日(号)</li>
<li><code>getMinutes():</code> 返回分</li>
<li><code>getSeconds():</code> 返回秒</li>
<li><code>getTime( ):</code> 返回从1970年1月1日00：00到现在的毫秒数，也就是时间戳。</li>
<li><code>setFullYear(yearInt):</code> 设置年份4位数</li>
<li><code>setMonth(monthInt):</code> 设置月份(0-11)</li>
<li><code>setDate(dateInt):</code> 设置日(1-31)</li>
<li><code>setHours(hourInt):</code> 设置小时数(0-23)</li>
<li><code>setMinutes(minInt):</code> 设置分钟数(0-59)</li>
<li><code>setSeconds(secInt):</code> 设置秒数(0-59)</li>
</ul></section><section><h5>14、正则表达式<a href="#14正则表达式"><span>#</span></a></h5><blockquote><ul>
<li>正则表达式的创建</li>
</ul></blockquote><ul>
<li>调用RegExp对象的构造函数<code>var reg = new RegExp('^[a-z]+[0-9]$', 'gi')</code>其中第一个参数是匹配模式，第二个参数是可选参数(g, i, m),分别用于指定全局匹配、区分大小写的匹配和多行匹配。</li>
<li>使用正则表达式字面值<code>(var reg = /^[a-z]+[0-9]$/gi)</code></li>
</ul><blockquote><ul>
<li>正则表达式的组成</li>
</ul></blockquote><ul>
<li><code>元字符: </code> .匹配除了换行符(n)以外的所有字符,w匹配字母，数字，或者汉字,W匹配除了字母，数字，汉字以外的其他字符,d匹配数字,D匹配除了数字以外的其他字符, s匹配任意的空白符(f, n, r, t, v), S匹配空白符以外的任意字符, b匹配单词的开始或者结束, B匹配单词的非开始或者结束, <code>^</code>匹配行首, <code>$</code>匹配行尾。</li>
<li><code>转义字符(\):</code> * + ? | { [ ( ) ] }^ $ . # 和空白需要转义。</li>
<li><code>限定符: </code> <code>*</code>匹配零次至多次，<code>+</code>匹配一次至多次，<code>？</code>匹配零次或一次{2,}至少匹配两次，{10}匹配10次，{2, 8}至少匹配两次至多匹配八次。</li>
</ul><blockquote><ul>
<li>正则表达式的方法</li>
</ul></blockquote><ul>
<li><code>test：</code> 检索字符串中的指定子串，返回布尔值。</li>
<li><code>exec: </code> 返回一个数组，数组中的第一个条目是第一个匹配。</li>
<li><code>search: </code> 返回子串的开始位置</li>
<li><code>match: </code> 返回匹配到的子串</li>
<li><code>replace: </code> 替换匹配到的子串</li>
<li><code>split:</code> 将字符串分割成数组  <code> // 'a12b2334c34'.split(/\d{4}/) ["a12b", "c34"]</code></li>
</ul></section><section><h5>15、js 处理异常，调试方法<a href="#15js-处理异常调试方法"><span>#</span></a></h5><p><code>处理异常：</code> try catch
<code>调试方法：</code> 1、打印到控制台。2、记录到当前页面。3、抛出错误</p></section></section>
<section><h4>三、写在后面的叨叨叨<a href="#三写在后面的叨叨叨"><span>#</span></a></h4><p>     不知不觉又到了深夜，渐渐的喜欢上了夜深人静的时候整理代码的感觉，这次比自己想象的整理的要多一些，除了正则花费了些功夫重新整理梳理了外，其他内容大多都是一些API文档，自己从头又过了下，虽然很多东西都过时了，例如<code>indeOf</code>早已可以用Es6的<code>includes</code> 方法去替代，语义和用法都好用很多，但是过一遍基础api也没啥坏处，明天周一，不过下周还是比较爽的元旦周三，上两天又休息了，美滋滋，加油!</p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/summaryJs1/</id>
      <title type="text">前端知识点总结(五) JavaScript篇(一)</title>
      <published>2019-12-14T12:10:35.000Z</published>
      <updated>2019-12-14T12:10:35.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/summaryJs1/"/>
      <summary type="text">一、写在前面的叨叨叨 之前用了四篇对Html,Css进行了总结，从这篇开始就是对Js进行一些温习回顾，在实际业务中框架用的多了，对一些原生属性是越来越不熟悉了，一些基础的操作dom的方法慢慢的也越来越</summary>
      <content type="html"><![CDATA[<section><h4>一、写在前面的叨叨叨<a href="#一写在前面的叨叨叨"><span>#</span></a></h4><p>    之前用了四篇对Html,Css进行了总结，从这篇开始就是对Js进行一些温习回顾，在实际业务中框架用的多了，对一些原生属性是越来越不熟悉了，一些基础的操作dom的方法慢慢的也越来越手生了，正好这个机会整理一波，JS和css，html一样也只是针对性的挑了一些比较经典和基础的内容来进行总结。</p></section>
<section><h4>二、Js基础梳理<a href="#二js基础梳理"><span>#</span></a></h4><section><h5>1、js的基本类型有哪些？引用类型有哪些？引用类型和基本类型有什么区别？<code>null</code> 和 <code>undefined</code> 的区别。<a href="#1js的基本类型有哪些引用类型有哪些引用类型和基本类型有什么区别null-和-undefined-的区别"><span>#</span></a></h5><p>    <code>值类型(基本类型)</code>：<strong>数值(number)</strong>、<strong>布尔值(boolean)</strong>、<strong>null</strong>、<strong>undefined</strong>、<strong>string(字符串)</strong>、<strong>Symbol</strong>。
   基本类型的值是不可变的，比较是它们的值的比较，数据类型不同也可以进行值比较，因为会进行数据类型的隐式转换。基本类型的变量是存放在栈内存（Stack）里的，栈内存中包括了变量的标识符和变量的值。
   <code>引用类型</code>：<strong>对象(Object)</strong>、<strong>数组（Array）</strong>、<strong>函数(Function)</strong>、<strong>RegExp</strong>、<strong>Date 类型</strong>。
   引用类型的值是可变的，比较是它们引用的比较，引用类型的值是保存在堆内存（Heap）中的对象（Object）。栈内存中保存了变量标识符和指向堆内存中该对象的指针，堆内存中保存了对象的内容。
    <strong>null</strong> 表示”没有对象”，即该处不应该有值。典型用法是：
    （1） 作为函数的参数，表示该函数的参数不是对象。
    （2） 作为对象原型链的终点。
    <strong>undefined</strong> 表示”缺少值”，就是此处应该有一个值，但是还没有定义。典型用法是：
    （1） 变量被声明了，但没有赋值时，就等于undefined。
    （2） 调用函数时，应该提供的参数没有提供，该参数等于undefined。
    （3） 对象没有赋值的属性，该属性的值为undefined。
    （4） 函数没有返回值时，默认返回undefined。</p></section><section><h5>2、如何判断对象类型<a href="#2如何判断对象类型"><span>#</span></a></h5><blockquote><ul>
<li><code>typeof 操作符</code>
适合判断基本类型，不适合判断引用类型（都返回object），比如自定义的对象类型。如果我们想要判断一个变量是否存在，可以使用typeof a != “undefined”</li>
<li><code>instanceOf操作符</code>
判断一个实例是否存在于某对象的原型链上</li>
<li><code>用对象的 constructor 来判断对象类型</code>
当检测null或者undefined类型的constructor 属性时，js会报错，不推荐使用</li>
<li><code>Object.prototype.toString.call(object)</code> <span>（主流建议方法）</span>
形式 Object.prototype.toString.call(object).slice(8,-1).toLowerCase();</li>
</ul></blockquote></section><section><h5>3、JS常见的DOM操作API<a href="#3js常见的dom操作api"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>基本概念：浏览器为JavaScript提供的一系列接口。Node有一个属性nodeType表示Node的类型，它是一个整数，其数值分别表示相应的Node类型。</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><ul>
<li><code>Element类型</code>：提供了对元素标签名，子节点和特性的访问，nodeType为1。</li>
<li><code>Attr类型</code>：表示元素的特性，nodeType值为2。</li>
<li><code>Text类型</code>：表示文本节点，它包含的是纯文本内容，nodeType为3。</li>
<li><code>Comment类型</code>：表示HTML文档中的注释，nodeType为8。</li>
<li><code>Document</code>：表示文档，document对象是HTMLDocument的一个实例，nodeType为9。DocumentFragment类型：表示一种轻量级的文档，nodeType为11。</li>
</ul></blockquote><section><h6>     a、节点创建API（用来创建节点）<a href="#-a节点创建api用来创建节点"><span>#</span></a></h6><blockquote><ul>
<li><code>createElement</code> (创建元素)
   Document.createElement()通过传入指定的一个标签名来创建一个元素.</li>
<li><code>createTextNode</code> (创建文本)
   Document.createdTextNode()传入文本节点中的文本</li>
<li><code>cloneNode</code> (复制节点)
   返回调用方法的节点的一个副本，它接收一个bool参数，用来表示是否复制子元素，为真表示复制子元素，为假表示只复制parent节点。parent.cloneNode(true);。如复制的元素有id，则其副本同样包含该id，所以复制节点后必须要修改其id。</li>
<li><code>createDocumentFragment</code> （创建一个DocumentFragment）
   主要是用于添加大量节点到文档中时会使用到，可以先将节点添加到fragment，最后一次性添加到文档，因为DocumentFragment不是文档树的一部分，它是保存在内存中的，所以不会造成回流问题。</li>
</ul></blockquote></section><section><h6>     b、页面修改API(用来修改页面DOM)<a href="#-b页面修改api用来修改页面dom"><span>#</span></a></h6><blockquote><ul>
<li><code>appendChild</code> (插入到末尾)
   将指定的节点添加到调用该方法的节点的子元素的末尾，如果被添加的节点是一个页面中存在的节点，则执行后这个节点将会添加到指定位置，其原本所在的位置将移除该节点</li>
<li><code>insertBefore</code> （添加一个节点到一个参照节点之前）
   例：parentNode.insertBefore(newNode,refNode)，parentNode表示新节点被添加后的父节点，newNode要添加的节点，refNode参照节点，新节点添加到这个节点之前。refNode是必传的，如果不传该参数会报错。</li>
<li><code>removeChild</code> （删除指定子节点）</li>
<li><code>replaceChild</code> （用于使用一个节点替换另一个节点）
   例：parent.replaceChild(newChild,oldChild);newChild是替换的节点，可以是新的节点，也可以是页面上的节点，如果是页面上的节点，则其将被转移到新的位置。oldChild是被替换的节点。</li>
</ul></blockquote></section><section><h6>     c、节点查询API<a href="#-c节点查询api"><span>#</span></a></h6><blockquote><ul>
<li><code>document.getElementById()</code>
   根据元素id返回元素，返回值是Element类型，如果不存在该元素，则返回null。</li>
<li><code>document.getElementsByTagName()</code>
   根据元素标签名获取元素，返回一个即时的HTMLCollection类型</li>
<li><code>document.getElementsByName()</code>
   通过指定的name属性来获取元素，它返回一个即时的NodeList对象。</li>
<li><code>document.getElementsByClassName()</code> <span>（注：IE9+）</span>
   根据元素的class返回一个即时的HTMLCollection，如果要获取2个以上classname，可传入多个classname，每个用空格相隔。</li>
<li><code>document.querySelector（）</code>	<span>（注：IE9+）</span>
   通过css选择器来查找元素，返回第一个匹配的元素，如果没有，则返回null。</li>
<li><code>document.querySelectorAll（）</code><span>（注：IE9+）</span>
   返回的是所有匹配的元素，而且可以匹配多个选择符。</li>
</ul></blockquote></section><section><h6>     d、节点关系API<a href="#-d节点关系api"><span>#</span></a></h6><blockquote><ul>
<li><code>parentNode和parentElement</code>（父关系型）
   parentNode：表示元素的父节点，parentElement：返回元素的父元素节点，与parentNode的区别在于，其父节点必须是一个Element，如果不是，则返回null。</li>
<li><code>previousSibling</code>、<code>previousElementSibling</code>、<code>nextSibling</code>、<code>nextElementSibling</code>
   previousSibling：节点的前一个节点，previousElementSibling：返回前一个元素节点，前一个节点必须是Element<span>（注：IE9+）</span>。nextSibling：节点的后一个节点，nextElementSibling：返回后一个元素节点，后一个节点必须是Element。<span>（注：IE9+）</span></li>
<li><code>childNodes</code>、<code>children</code>、<code>firstNode</code>、<code>lastNode</code>、<code>hasChildNodes</code>（子关系型）
   childNodes：表示元素的子节点列表，children：返回是Element的子节点，firstNode：第一个子节点，lastNode：最后一个子节点。hasChildNodes：判断是否包含子节点。</li>
</ul></blockquote></section><section><h6>     e、元素属性API<a href="#-e元素属性api"><span>#</span></a></h6><blockquote><ul>
<li><code>setAttribute</code> (根据名称和值修改元素的特性)
   element.setAttribute(name, value); 其中name是特性名，value是特性值。如果元素不包含该特性，则会创建该特性并赋值。</li>
<li><code>getAttribute</code> (返回指定的特性名相应的特性值)
   element.getAttribute(“id”)，如果不存在，则返回null或空字符串。</li>
</ul></blockquote></section><section><h6>     f、元素样式API<a href="#-f元素样式api"><span>#</span></a></h6><blockquote><ul>
<li><code>window.getComputedStyle()</code> 获取应用到元素后的样式
   window.getComputedStyle(element[, pseudoElt]) element是要获取的元素，pseudoElt指定一个伪元素进行匹配。返回的style是一个CSSStyleDeclaration对象。通过style可以访问到元素计算后的样式.</li>
<li><code>getBoundingClientRect()</code> 返回元素的大小以及相对于浏览器可视窗口的位置
   clientRect是一个DOMRect对象，包含left，top，right，bottom，它是相对于可视窗口的距离，滚动位置发生改变时，它们的值是会发生变化的。除了IE9以下浏览器，还包含元素的height和width等。</li>
</ul></blockquote></section></section><section><h5>4、解释一下事件捕获和事件冒泡，事件委托，如何阻止冒泡,阻止默认事件。<a href="#4解释一下事件捕获和事件冒泡事件委托如何阻止冒泡阻止默认事件"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>DOM2级事件规定的事件流的三个阶段：事件捕获阶段，处于目标阶段，事件冒泡阶段。</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>事件捕获</code>：从document到触发事件的那个节点，即自上而下的去触发事件，触发顺序为自外向内。
<code>事件冒泡</code>：事件冒泡是自下而上的去触发事件，直到document/window。个人理解，事件冒泡存在的意义，就是在事件运行过程中，避免使用循环遍历的方式去给每个同级元素触发相同的事件，而优化性能。
<code>事件委托</code>：事件委托就是利用事件冒泡，只指定一个事件处理程序，就可以管理某一类型的所有事件。（经典取快递例子理解）
    addEventListener(event,fn,false)方法用于向指定元素添加事件，事件方法的第三个参数，就是控制事件触发顺序是否为事件捕获。true,事件捕获；false,事件冒泡。默认false,即事件冒泡。
<code>阻止冒泡</code>： event.stopPropagation()<span>（注：IE9+）</span>
<code>阻止默认行为</code>：event.preventDefault()</p></section><section><h5>5、 js 的作用域有几种？函数的作用域是什么？<a href="#5-js-的作用域有几种函数的作用域是什么"><span>#</span></a></h5><p>    一般说三种：<code>全局</code>、<code>函数</code> 和 <code>eval作用域</code>。作用域又称执行环境，所有变量都存在于一个执行环境（作用域）中，这个执行环境决定了变量的生命周期，以及哪一部分代码可以访问其中的变量，每进入一个执行环境都会创建一个用于搜索变量和函数的作用域链。作用域链本质是一个指向变量对象的指针列表，它只引用但不实际包含变量对象。作用域在函数定义时就已经确定，而不是在函数调用时确定。作用域中变量的值是在执行过程中产生的确定的，而作用域却是在函数创建时就确定了。</p></section><section><h5>6、对闭包的理解？什么时候构成闭包？闭包的优缺点？<a href="#6对闭包的理解什么时候构成闭包闭包的优缺点"><span>#</span></a></h5><p>     当在函数内部定义了其他函数时（引用了父作用域变量）就创建了闭包，闭包有权访问包含函数内部的所有变量。在执行环境中，闭包的作用域链包含着它自己的作用域，包含函数的作用域，全局作用域，通常函数的作用域及其变量都会在函数执行结束后被销毁，但当函数返回一个闭包时，这个函数的作用域将会一直在内存中保存到闭包不存在为止。
<code>优点</code>：可以读取函数内部的变量。实现私有作用域（封装插件）
<code>缺点</code>：创建闭包必须维护额外的作用域，过度使用会占用大量内存。</p></section><section><h5>7、显示原型和隐式原型，手绘原型链，原型链是什么<a href="#7显示原型和隐式原型手绘原型链原型链是什么"><span>#</span></a></h5><p>      创建的每个函数都有<code>prototype（原型）</code>属性，这个属性是个指针，指向一个对象（通过调用构造函数而创建的那个对象实例的原型对象）。 每个对象都有一个<code>__proto__</code>属性，可成为隐式原型.指向创建该对象的函数的<code>prototype</code>。
      每个构造函数都有一个原型对象，原型对象包含一个指向构造函数的指针<code>(constructor)</code>，而实例包含一个指向原型对象内部的指针<code>（__prototype__）</code>。
      <code>原型链</code>：访问一个对象的属性时，先在基本属性中查找，如果没有，再沿着__proto__这条链向上找。原型链是实现继承的主要方法。</p></section><section><h5>8、实现继承的多种方式和优缺点<a href="#8实现继承的多种方式和优缺点"><span>#</span></a></h5><blockquote><ul>
<li>原型链(缺点：包含引用类型值的原型属性会被所有实例共享，创建子类型实例时，不能向超类型的构造函数中传参)</li>
<li>借用构造函数（方法都在构造函数中定义，函数无法复用）</li>
<li>组合继承（原型链+构造函数）<span>*推荐用法</span></li>
<li>使用原型链实现对原型属性和方法的继承，而通过借用构造函数来实现实例属性的继承。</li>
<li>原型式继承 Object.creat()  <span>IE9+</span></li>
</ul></blockquote></section><section><h5>9、this有哪些使用场景？如何改变this的值（call，apply，bind）？<a href="#9this有哪些使用场景如何改变this的值callapplybind"><span>#</span></a></h5><p>      <code>this</code>对象是在运行时基于函数的执行环境绑定的：在全局函数中，<code>this</code>等于<code>window</code>，当函数被作为某个对象的方法掉用时，<code>this</code>等于那个对象，不过匿名函数的执行环境具有全局性，因此其<code>this</code>通常指向<code>window</code>。
      唯一的一个不同就是采用了<code>function.call</code>或者<code>function.apply</code>的情况，<code>function</code>的<code>this</code>指向了<code>call</code>的传参，但这恰恰是针对上述<code>this</code>指向调用者的情况的一种补充，专用于调整<code>this</code>指向的。</p></section><section><h5>10、new 一个对象（构造函数）具体做了什么<a href="#10new-一个对象构造函数具体做了什么"><span>#</span></a></h5><blockquote><ul>
<li>1.创建一个新对象</li>
<li>2.将构造函数的作用域赋给新对象（因此this就指向了这个新对象）</li>
<li>3.执行构造函数中的代码（为这个新对象添加属性）</li>
<li>4.返回新对象</li>
</ul></blockquote></section></section>
<section><h4>三、写在后面的叨叨叨<a href="#三写在后面的叨叨叨"><span>#</span></a></h4><p>      本来自己是单独整理了20个左右的题目，放在这篇文章中自己去总结回答，但是没想到会花费那么多的时间，这十个题目差不多也包含了<code>js</code>最难得那几块，原型链，闭包，继承之类的，虽然我写的很简单，但是资料查了很多，总结的话也是自己通过查资料后自己理解后进行总结的并没有照搬其他的文章，除了一些标准的API之类的东西，总之时间是花了比较久的，所以暂时只整理了这些，等下次有时间接着撸其他问题。感谢阅读。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/summaryCss3/</id>
      <title type="text">前端知识点总结(四) Css进阶Css3、Sass篇</title>
      <published>2019-11-30T12:20:20.000Z</published>
      <updated>2019-11-30T12:20:20.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/summaryCss3/"/>
      <summary type="text">一、简单介绍 前段时间已经整理过两篇关于Css文章，一个是基础，一个是布局相关，这篇就整理了一下关于Sass的相关内容，Css3 在前面也提到过，像Flex布局之类的，这篇会介绍下Css3的简单新特性</summary>
      <content type="html"><![CDATA[<section><h5>一、简单介绍<a href="#一简单介绍"><span>#</span></a></h5><p>    前段时间已经整理过两篇关于Css文章，一个是基础，一个是布局相关，这篇就整理了一下关于Sass的相关内容，Css3 在前面也提到过，像Flex布局之类的，这篇会介绍下Css3的简单新特性和动画相关，然后一些Sass用法，很多以前用的东西不去查文档很多都想不起来了，所以发现经常的去整理温习下确实也挺有必要，温故而知新嘛，废话少说，下面是文章的核心内容~</p></section>
<section><h5>二、Css3整理<a href="#二css3整理"><span>#</span></a></h5><section><h6>1、CSS3新特性，伪类，伪元素，锚伪类<a href="#1css3新特性伪类伪元素锚伪类"><span>#</span></a></h6><blockquote><ul>
<li>动态伪类：（并不存在于HTML中,当用户和网站交互的时候才能体现出来）</li>
</ul></blockquote><ul>
<li><code>. property a:link</code> { color; } 链接没有被访问时前景色为灰色</li>
<li><code>. property a: visited</code> { color: yellow; } 链接被访问过后前景色为黄色</li>
<li><code>. property a: hover</code> { color: green; } 鼠标悬浮在链接上时前景色为绿色</li>
<li><code>. property a: active</code> { color: blue; } 鼠标点钟激活链接那一下前景色为蓝色
<em>注：先后顺序，遵守爱恨原则LoVe/HAte,也就是Link—visited—hover—active；
对于，，。，这个经常用在表单元素上。</em></li>
</ul><blockquote><ul>
<li>UI元素状态伪类：</li>
</ul></blockquote><ul>
<li><code>:enabled</code>,<code>:disabled</code>,<code>:checked</code>
<em>IE6-8不支持"","",""这三种选择器。</em></li>
</ul><blockquote><ul>
<li>CSS3的：</li>
</ul></blockquote><ul>
<li><code>:first-child</code> 选择某个元素的第一个子元素；</li>
<li><code>:last-child</code> 选择某个元素的最后一个子元素；</li>
<li><code>:nth-child()</code> 选择某个元素的一个或多个特定的子元素；</li>
<li><code>:nth-last-child()</code> 选择某个元素的一个或多个特定的子元素，从这个元素的最后一个子元素开始算；</li>
<li><code>:nth-of-type()</code> 选择指定的元素；</li>
<li><code>:nth-last-of-type()</code> 选择指定的元素，从元素的最后一个开始计算；</li>
<li><code>:first-of-type </code> 选择一个上级元素下的第一个同类子元素；</li>
<li><code>:last-of-type</code> 选择一个上级元素的最后一个同类子元素；</li>
<li><code>:only-child</code> 选择的元素是它的父元素的唯一一个了元素；</li>
<li><code>:only-of-type</code>选择一个元素是它的上级元素的唯一一个相同类型的子元素；</li>
<li><code>:empty</code> 选择的元素里面没有任何内容</li>
</ul><blockquote><ul>
<li>否定选择器（：not）</li>
</ul></blockquote><ul>
<li>否定选择器 <code>:not()</code>，可以让你定位不匹配该选择器的元素。IE6-8浏览器不支持()选择器</li>
</ul><blockquote><ul>
<li>伪元素（两个“：：”和一个“：”css3中主要用来区分伪类和伪元素）</li>
</ul></blockquote><ul>
<li><code>::first-line</code>：选择元素的第一行</li>
<li><code>::first-letter</code>：选择元素的第一个字母</li>
<li><code>::before</code>和<code>::after</code>：这两个主要用来给元素的前面或后面插入内容，这两个常用”content”配合使用，见过最多的就是清除浮动</li>
<li><code>::selection</code>：用来改变浏览网页时选中文字的默认效果</li>
</ul></section><section><h6>2、css3动画transform，transition和animation的区别<a href="#2css3动画transformtransition和animation的区别"><span>#</span></a></h6><blockquote><ul>
<li><strong>transform(2D变换)</strong></li>
</ul></blockquote><ul>
<li><code>translate(x,y)</code>：沿着 X 和 Y 轴移动元素。</li>
<li><code>rotate(angle)</code>：旋转，在参数中规定角度。</li>
<li><code>scale(x,y)</code>：缩放转换，改变元素的宽度和高度。</li>
<li><code>skew(x-angle,y-angle)</code>：倾斜转换，沿着 X 和 Y 轴。</li>
<li><code>matrix(n,n,n,n,n,n)</code>：定义 2D 转换，使用六个值的矩阵。</li>
</ul><p></p><figure><img src="/uploads/transform.png" alt="transform" /><figcaption>transform</figcaption></figure><p></p><blockquote><ul>
<li><strong>transition（过渡）</strong>
<code>Tip:</code> transition允许css的属性值在一定的时间区间内平滑地过渡。这种效果可以在鼠标单击、获得焦点、被点击或对元素任何改变中触发，并圆滑地以动画效果改变CSS的属性值。
兼容性：IE10+，Firefox, Opera 和 Chrome
transition属性的值包括以下四个：</li>
</ul></blockquote><ul>
<li><code>transition-property</code>： 指定对HTML元素的哪个css属性进行过渡渐变处理，这个属性可以是color、width、height等各种标准的css属性。</li>
<li><code>transition-duration</code>：指定属性过渡的持续时间</li>
<li><code>transition-timing-function</code>：指定渐变的速度：
<ul>
<li><code>ease</code>：（逐渐变慢）默认值</li>
<li><code>linear</code>：（匀速）</li>
<li><code>ease-in</code>：(加速)</li>
<li><code>ease-out</code>：（减速）</li>
<li><code>ease-in-out</code>：（加速然后减速）</li>
<li><code>cubic-bezier</code>：（该值允许你去自定义一个时间曲线）， 特定的cubic-bezier曲线。 (x1, y1, x2, y2)四个值特定于曲线上点P1和点P2。所有值需在[0, 1]区域内，否则无效。</li>
</ul>
</li>
<li><code>transition-delay</code>：指定延迟时间，也就是经过多长时间才开始执行过渡过程。</li>
</ul><blockquote><ul>
<li><strong>animation（动画）</strong>
需要配合@keyframes使用，语法规则&lt;命名是由&gt;”@keyframes”开头，后面接着 “动画的名称”加上一对花括号“{}”，括号中就是一些不同时间段样式规则。不同关键帧是通过from（相当于0%）、to（相当于100%）或百分比来表示。@keyframes定义好后，须通过animation把它绑定到选择器。animation的属性如下：</li>
</ul></blockquote><ul>
<li><code>Animation</code>：所有动画属性的简写属性（除了animation-play-state 属性）。</li>
<li><code>animation-name</code>：规定 @keyframes 动画的名称。</li>
<li><code>animation-duration</code>：规定动画完成一个周期所花费的秒或毫秒,默认是0。</li>
<li><code>animation-timing-function</code>：规定动画的速度曲线，默认是”ease”。</li>
<li><code>animation-delay</code>：规定动画何时开始，默认是 0</li>
<li><code>animation-iteration-count</code>：规定动画被播放的次数，默认是 1（infinite：无限次）</li>
<li><code>animation-direction</code>：规定动画是否在下一周期逆向地播放。默认“normal”（reverse：反向播放；alternate：奇数次正向播放，偶数次反向播放；alternate-reverse：奇数次反向播放，偶数次正向播放。）</li>
<li><code>animation-play-state</code>：规定动画是否正在运行或暂停，默认是“running”（paused：暂停动画）。</li>
</ul></section></section>
<section><h5>三、Sass整理<a href="#三sass整理"><span>#</span></a></h5><p><code>Sass</code>和<code>Less</code>两者都是CSS预处理器，具有相同的功能，快速编译代码，更好的维护样式代码。
不同点在于语法规则不同，相比<code>less</code>，<code>sass</code>更像一门正规的编程语言，有变量和作用域，变量有全局和局部之分，并且有优先级。有函数的概念，进程控制，数据结构。相同点在于：</p><blockquote><ul>
<li><code>混入(Mixins)</code>——class中的class；</li>
<li><code>参数混入</code>——可以传递参数的class，就像函数一样；</li>
<li><code>嵌套规则</code>——Class中嵌套class，从而减少重复的代码；</li>
<li><code>运算</code>——CSS中用上数学；</li>
<li><code>颜色功能</code>——可以编辑颜色；</li>
<li><code>名字空间(namespace)</code>——分组样式，从而可以被调用；</li>
<li><code>作用域</code>——局部修改样式；</li>
<li><code>JavaScript 赋值</code>——在CSS中使用JavaScript表达式赋值。</li>
</ul></blockquote><section><h6>a、Sass基本用法：<a href="#asass基本用法"><span>#</span></a></h6><p>    1、<code>变量</code>（SASS允许使用变量，所有变量以$开头，如果变量需要镶嵌在字符串之中，就必须需要写在#{}之中)。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>$blue: #1875e7;</span></div></div><div><div><div>2</div></div><div><span>div</span><span> {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>color: $</span><span>blue</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    2、<code>计算功能</code>（SASS允许在代码中使用算式：)。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>body</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>margin: (</span><span><span>14</span><span>px</span></span><span>/</span><span>2</span><span>);</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>top: </span><span><span>50</span><span>px</span></span><span> + </span><span><span>100</span><span>px</span></span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    3、<code>嵌套</code>（SASS允许选择器嵌套，属性也可以嵌套，在嵌套的代码块内，可以使用&amp;引用父元素（伪类）;)。</p></section><section><h6>b、代码重用：<a href="#b代码重用"><span>#</span></a></h6><p>     1、<code>继承</code>: SASS允许一个选择器，继承另一个选择器.</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.class1</span><span> {</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>border: </span><span><span>1</span><span>px</span></span><span><span> </span><span>solid</span><span> </span></span><span>#</span><span>ddd</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>/* class2要继承class1，需要使用 @extend 命令*/</span></div></div><div><div><div>5</div></div><div><span>.class2</span><span> {</span></div></div><div><div><div>6</div></div><div><span>    </span><span>@extend</span><span> .class1;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>font-size: </span><span><span>120</span><span>%</span></span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>     2、<code>Mixin</code>: (定义可重用代码块)，mixin的强大之处，在于可以指定参数和缺省值。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* 使用 @mixin 命令，定义一个代码块 */</span></div></div><div><div><div>2</div></div><div><span>@minxin</span><span> left {</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>float: </span><span>left</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>margin-left: </span><span><span>10</span><span>px</span></span><span>;</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>/* 使用 @include 命令，调用 minxin */</span></div></div><div><div><div>7</div></div><div><span>div</span><span> {</span></div></div><div><div><div>8</div></div><div><span>    </span><span>@include</span><span> left;</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>     3、<code>颜色函数</code>（SASS提供了一些内置的颜色函数，以便生成系列颜色。）
     4、<code>插入文件</code>（@import命令，用来插入外部文件。如果插入的是.css文件，则等同于css的import命令。）</p></section><section><h6>c、高级用法：<a href="#c高级用法"><span>#</span></a></h6><p>     1、<code>条件语句</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* @if 可以用来判断 */</span></div></div><div><div><div>2</div></div><div><span>p</span><span> {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>@if</span><span> 1 + 1 == 2 { border: </span><span><span>1</span><span>px</span></span><span><span> </span><span>solid</span><span>; }</span></span></div></div><div><div><div>4</div></div><div><span>    </span><span>@if</span><span> 5 &lt; 3 { border: </span><span><span>2</span><span>px</span></span><span> detted; }</span></div></div><div><div><div>5</div></div><div><span>}</span></div></div><div><div><div>6</div></div><div><span>/* 一起的还有 @else 命令 */</span></div></div><div><div><div>7</div></div><div><span>@if</span><span> lightness($color) &gt; 30% {</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>background-color: </span><span>#</span><span>000</span><span>;</span></div></div><div><div><div>9</div></div><div><span>} </span><span>@else</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>background-color: </span><span>#</span><span>fff</span><span>;</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>     2、<code>循环语句</code></p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* sass 支持 for 循环 */</span></div></div><div><div><div>2</div></div><div><span>@for</span><span> $i from 1 to 10 {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>.border-</span><span>#</span><span>{$1} {</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>border: #{$i}px </span><span>solid</span><span> </span><span>blue</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div><span>/* while 循环 */</span></div></div><div><div><div>8</div></div><div><span>$i: 6;</span></div></div><div><div><div>9</div></div><div><span>@while</span><span> $i &gt; 0 {</span></div></div><div><div><div>10</div></div><div><span>    </span><span>.item-</span><span>#</span><span>{#i} {</span></div></div><div><div><div>11</div></div><div><span><span>        </span></span><span>width: </span><span><span>2</span><span>em</span></span><span> * $i</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>$i: $i - </span><span>2</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div><div><div><div>15</div></div><div><span>/* each 命令与for类似 */</span></div></div><div><div><div>16</div></div><div><span>@each</span><span> $member in a,b,c,d {</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>.#{$member} {</span></div></div><div><div><div>18</div></div><div><span><span>        </span></span><span>background=image: </span><span>url</span><span>(</span><span>"#{$member}.jpg"</span><span>);</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>     2、<code>自定义函数</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* Sass 允许用户编写自己的函数 */</span></div></div><div><div><div>2</div></div><div><span>@function</span><span> double($n) {</span></div></div><div><div><div>3</div></div><div><span>    </span><span>@return</span><span> $n * 2</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>#sidebar {</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>width: </span><span>double</span><span>(</span><span><span>5</span><span>px</span></span><span>);</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section>
<section><h5>四、最后<a href="#四最后"><span>#</span></a></h5><p>    到现在算是<code>Css</code>的内容告一段落了，虽然文章中看起来提到的东西并不多，但是每一点的整理和总结，资料的查找也颇费功夫，如果有不对的地方欢迎指正。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/summaryCss2/</id>
      <title type="text">前端知识点总结(三) Css布局篇</title>
      <published>2019-11-16T07:20:35.000Z</published>
      <updated>2019-11-16T07:20:35.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/summaryCss2/"/>
      <summary type="text">一、简单介绍 关于 Css 第一章的时候总结了一些常用的基础知识，但是没有涉及到布局相关的东西，大家都知道 Css 布局算是一个核心功能，兼容和问题包括布局方法也都很多，这次单独拿出来一个章节对布局进</summary>
      <content type="html"><![CDATA[<section><h3>一、简单介绍<a href="#一简单介绍"><span>#</span></a></h3><p>    关于<code>Css</code>第一章的时候总结了一些常用的基础知识，但是没有涉及到布局相关的东西，大家都知道<code>Css</code>布局算是一个核心功能，兼容和问题包括布局方法也都很多，这次单独拿出来一个章节对布局进行一下总结，几种常见的布局写法，双飞翼布局，圣杯布局，还有<code>Css3</code>的<code>Flex</code>布局。正好今天是周末，阳光明媚的，午后晒晒太阳写点东西还是比较惬意的。</p></section>
<section><h3>二、Css布局相关<a href="#二css布局相关"><span>#</span></a></h3><section><h4>1、 grid布局(Grid layout)<a href="#1-grid布局grid-layout"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>版本支持情况：IE10+（过时语法），Edge，chrome(57)，firefox(52)，opera(44)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>术语：</strong>
<code>Grid container</code> (display: gird 的元素。 这是所有grid item的直接父项)；
<code>Grid item：</code> Grid 容器的孩子（直接子元素）。
<code>Grid line：</code> 这个分界线组成的网格结构，即可是垂直(column),也可是水平(lines);
<code>Grid track:</code> 两个相邻网格线之间的空间。
<code>Grid cell：</code> 两个相邻的行和两个相邻的列网格线之间的空间。它是网格的一个“单元”。
<code>Grid Area: </code> 四个网格线包围的总空间。 网格区域可以由任意数量的网格单元组成。
<strong>API：</strong></p><blockquote><ul>
<li>父容器(Grid Container)的属性</li>
</ul></blockquote><ul>
<li><code>Display：</code> 元素定义为<strong>grid contaienr</strong>，并为其内容建立新的网格格式化上下文.
Grid：生成一个块级网格；<strong>inline-grid</strong>：生成一个行级网格；<strong>subgrid</strong>：从父节点获取行列大小。
注：<strong>column</strong>, <strong>float</strong>, <strong>clear</strong>, 以及 <strong>vertical-align</strong> 对一个 <strong>grid container</strong> 没有影响。</li>
<li><code>grid-template-columns/grid-template-rows:</code>  使用以空格分隔的多个值来定义网格的列和行。值表示轨道大小(track size)，之间的空格代表表格线(grid line)。
例：<strong>grid-template-columns: 40px 50px auto 50px 40px;<strong>或</strong>grid-template-columns: [first] 40px [line2] 50px [line3] auto [col4-start] 50px [five] 40px</strong> [end]；另外<strong>grid-template-columns: 1fr 1fr 1fr;</strong>“fr”单位允许您将轨道大小设置为网格容器自由空间的一部分。自由空间是在排除所有不可伸缩的item 之后计算得到的。</li>
<li><code>grid-template-areas: </code> 通过引用 grid-area属性指定的网格区域的名称来定义网格模板.grid-area-name&lt;使用&gt; grid-area 属性设置的网格区域的名称。.：点号代表一个空网格单元. None&lt;没有定义网格区域&gt;。</li>
<li><code>grid-template：</code> 在单个声明中定义 <strong>grid-template-rows</strong>、<strong>grid-template-columns</strong>、<strong>grid-template-areas</strong> 的简写。</li>
<li><code>grid-column-gap / grid-row-gap：</code> 指定网格线的大小，可以理解为设置列/行之间的间距的宽度。注：只能在列/行之间创建缝隙，而不是在外部边缘创建。<strong>grid-row-gap</strong> 和 <strong>grid-column-gap</strong>可以简写为**grid-gap：<em>px <em>px;</em></em></li>
<li><code>justify-items: </code> 沿着Y轴对齐网格内的内容；<strong>align-items</strong>,沿着X轴对齐；
start: 内容与网格区域的左端对齐；end: 内容与网格区域的右端对齐； center: 内容位于网格区域的中间位置；<strong>stretch:</strong> 内容宽度占据整个网格区域空间(这是默认值)；</li>
<li><code>justify-content/align-content：</code> 有时，网格的总大小可能小于其网格容器的大小。此时，你可以设置网格容器内的网格的对齐方式。（justify Y轴，align X轴）。
Start：网格与网格容器的左边对齐；End：网格与网格容器的右边对齐；Center： 网格与网格容器的中间对齐；Stretch：调整grid item 的大小，让宽度填充整个网格容器；Space-around：在 grid item 之间设置均等宽度的空白间隙，其外边缘间隙大小为中间空白间隙宽度的一半；Space-between：在grid item之间设置均等宽度空白间隙，其外边缘无间隙；Space-evenly：在每个 grid item 之间设置均等宽度的空白间隙，包括外边缘。</li>
<li><code>grid-auto-columns/grid-auto-rows:</code>  指定自动生成的网格轨道（又名隐式网格轨道）的大小,简单理解就是当你指定的定位超过了网格范围的行或列时被创建。</li>
<li>子元素(Grid Items)的属性
<ul>
<li><code>grid-column-start/grid-column-end/grid-row-start/grid-row-end：</code>使用特定的网格线确定grid item在网格内的位置。grid-column-start/grid-row-start 属性表示grid item的网格线的起始位置，grid-column-end/grid-row-end属性表示网格项的网格线的终止位置。
Line: 可以是一个数字来指代相应编号的网格线，也可使用名称指代相应命名的网格线;
Span: 网格项将跨越指定数量的网格轨道;Span: 网格项将跨越一些轨道，直到碰到指定命名的网格线; auto: 自动布局， 或者自动跨越， 或者跨越一个默认的轨道。</li>
<li><code>justify-self/align-self：</code>沿着轴grid item 里的内容。 此属性对单个网格项内的内容生效。（justify沿着Y轴，align沿着X轴）；</li>
</ul>
</li>
</ul></section><section><h4>2、Flexbox 布局相关<a href="#2flexbox-布局相关"><span>#</span></a></h4><blockquote><p><strong>1. 什么是Flexbox：</strong>
CSS3 引入的新的布局模式。它决定了元素如何在页面上排列，使它们能在不同的屏幕尺寸和设备下可预测地展现出来。
<strong>2.	兼容情况，什么情况下不建议使用：</strong>
IE10、11部分支持，低版本不支持，现代浏览器支持。整体页面布局或者完全支持就浏览器网站不建议使用。
<strong>3. 基本API：</strong></p><ul>
<li>容器属性</li>
</ul></blockquote><ul>
<li><code>Flex-direction</code>：决定主轴的方向（即项目排列方向）
<ul>
<li><code>row</code>：主轴水平，起点在左；</li>
<li><code>column</code>：主轴垂直，起点在上；</li>
<li><code>row-reverse</code>：主轴水平，起点在右</li>
<li><code>column-reverse</code>：主轴垂直，起点在下；</li>
</ul>
</li>
<li><code>Flex-wrap</code>：默认情况下，项目排成一行，flex-wrap定义如何换行。
<ul>
<li><code>Wrap</code>：换行，第一行在上；</li>
<li><code>wrap-reverse</code>：换行，第一行在下；</li>
</ul>
</li>
<li><code>Flex-flow</code>: <code>flex-direction</code>和<code>flex-warp</code>的简写，默认row nowrap；</li>
<li><code>justify-content</code>：定义了项目在主轴（X轴）的对齐方式
<ul>
<li><code>flex-start</code>：默认左对齐；</li>
<li><code>flex-end</code>：右对齐；</li>
<li><code>center</code>：居中</li>
<li><code>space-between</code>：两端对齐，间隔相等;</li>
<li><code>space-around</code>：每个项目两侧间隔相等。</li>
</ul>
</li>
<li><code>align-items</code>：定义了项目在交叉轴（Y轴）上的对齐方式
<ul>
<li><code>Flex-start</code>：交叉轴起点对齐；</li>
<li><code>flex-end</code>：终点对齐；</li>
<li><code>center</code>&lt;居中&gt;</li>
<li><code>Baseline</code>：项目第一行文字对齐；</li>
<li><code>stretch</code>：默认，未设置高度。占满容器高度。</li>
</ul>
</li>
</ul><p></p><figure><img src="/uploads/justifyContent.png" alt="justifyContent" /><figcaption>justifyContent</figcaption></figure><p></p><blockquote><ul>
<li>项目属性</li>
</ul></blockquote><ul>
<li><code>order：</code> 定义项目的排列顺序，数值越小，越往前，默认0</li>
<li><code>flex-grow：</code> 定义项目的放大比列，默认0，即如果存在剩余也不放大，如果所有项目的flex-grow属性为2，其他为1，则前者占据比其对一倍。</li>
<li><code>flex-shrink：</code> 定义了项目的缩小比列，默认1，如果空间不足，则缩小，。如果所有项目的flex-shrink属性为1，当空间不足时，都将等比列缩放，如果一个项目的flex-shrink属性为0，其他为1，若空间不足，为0则不缩小。</li>
<li><code>flex-basis：</code> 定义了在分配多余空间之前，项目占据的主轴空间，浏览器根据这个属性，计算主轴是否又多余空间，默认为auto，即本来大小。</li>
<li><code>flex：</code> flex是flex-grow，flex-shrink，flex-basis的简写，默认0，1，auto；</li>
<li><code>align-self：</code> 允许单个项目有与其他项目不一样的对齐方式，可以覆盖align-items属性，默认为auto，表示继承父元素的align-items属性。</li>
</ul></section><section><h4>3、实现两栏布局，三列布局中间自适应，三栏布局（圣杯布局，双飞翼布局）的方法。<a href="#3实现两栏布局三列布局中间自适应三栏布局圣杯布局双飞翼布局的方法"><span>#</span></a></h4><p>    <strong>a、两栏布局（左侧固定，右侧自适应）</strong>
    第一种: float + BFC方法（这个方案同样是利用了左侧浮动，但是右侧盒子通过overflow: auto;形成了BFC，因此右侧盒子不会与浮动的元素重叠。）</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.wrapper</span><span>{</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>overflow: </span><span>auto</span><span>;</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div><div><div><div>4</div></div><div><span>.wrapper</span><span> </span><span>.left</span><span> {</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>float: </span><span>left</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>margin-right: </span><span><span>20</span><span>px</span></span><span>;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>.wrapper</span><span> </span><span>.right</span><span> {</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>margin-left: </span><span>0</span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>overflow: </span><span>auto</span><span>;</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    第二种: Flex方案</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/*  flex容器的一个默认属性值:align-items: stretch;这个属性导致了列等高的效果。 */</span></div></div><div><div><div>2</div></div><div><span>/*  为了让两个盒子高度自动，需要设置: align-items: flex-start;  */</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>wrapper</span><span>{</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>display: </span><span>flex</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>align-items: </span><span>flex-start</span><span>;</span></div></div><div><div><div>7</div></div><div><span>}</span></div></div><div><div><div>8</div></div><div><span>.wrapper</span><span> </span><span>.left</span><span> {</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>flex: </span><span>0</span><span> </span><span>0</span><span><span> </span><span>auto</span><span>;</span></span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div><span>.wrapper</span><span> </span><span>.right</span><span> {</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>flex: </span><span>1</span><span> </span><span>1</span><span><span> </span><span>auto</span><span>;</span></span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    <strong>b、三列布局（左右宽度固定（要想不固定将宽度值改为百分值即可），中间自适应）</strong>
    <strong>第一种：绝对定位法</strong>（左右两栏采用绝对定位，分别固定于页面的左右两侧，中间的主体栏用左右margin值撑开距离。于是实现了三栏自适应布局。）注：html元素高度为100%；
    <strong>第二种：自身浮动法</strong>（左栏左浮动，右栏右浮动，主体直接放后面，就实现了自适应）;
    <strong>第三种：圣杯布局</strong>（中间栏要在浏览器中优先展示渲染）
    实现思路：首先设置父元素位置，padding左右空出left和right的宽度，将主体部分的三个子元素都设置左浮动，设置main宽度width：100%；让其独占一行，然后设置left和right的负的外边距（负的margin-left会让元素沿文档流向左移动，如果负的数值比较大就会一直移动到上一行，设置left部分的margin-left为-100%,就会使left向左移动一整个行的宽度，由于left左边是父元素的边框，所以left继续跳到上一行左移，一直移动到上一行的开头，并覆盖了main部分，left上移过后，right就会处于上一行的开头位置，这时再设置right部分margin-left为负的宽度，right就会左移到上一行的末尾。）接下来分别把left和right用相对定位移动到两个留白就可以。代码如下</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!--html--&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>'container'</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>'main'</span><span>&gt;main&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>'left'</span><span>&gt;left&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>'right'</span><span>&gt;right&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span>&lt;/</span><span>div</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* css */</span></div></div><div><div><div>2</div></div><div><span>.container</span><span>{</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>padding: </span><span>0</span><span> </span><span><span>300</span><span>px</span></span><span> </span><span>0</span><span> </span><span><span>200</span><span>px</span></span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>.left</span><span>,</span><span>.main</span><span>,</span><span>.right</span><span>{</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>position: </span><span>relative</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>min-height: </span><span><span>130</span><span>px</span></span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>float: </span><span>left</span><span>;</span></div></div><div><div><div>9</div></div><div><span>}</span></div></div><div><div><div>10</div></div><div><span>.left</span><span>{</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>left: </span><span><span>-200</span><span>px</span></span><span>;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>margin-left: </span><span><span>-100</span><span>%</span></span><span>;</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>background: </span><span>green</span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>width: </span><span><span>200</span><span>px</span></span><span>;</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div><span>.right</span><span>{</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>right: </span><span><span>-300</span><span>px</span></span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>margin-left: </span><span><span>-300</span><span>px</span></span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>background-color: </span><span>red</span><span>;</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>width: </span><span><span>200</span><span>px</span></span><span>;</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div><span>.mian</span><span>{</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>background-color: </span><span>blue</span><span>;</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>width: </span><span><span>100</span><span>%</span></span><span>;</span></div></div><div><div><div>25</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>    <strong>第四种：双飞翼布局</strong>（与圣杯布局的不同在于解决中间栏div内容不被遮挡问题）</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>&lt;!--html--&gt;</span></div></div><div><div><div>2</div></div><div><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>'container'</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>'main'</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>'content'</span><span>&gt;main&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>'left'</span><span>&gt;left&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>class</span><span>=</span><span>'right'</span><span>&gt;right&lt;/</span><span>div</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span>&lt;/</span><span>div</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>/* css 双飞翼布局比圣杯布局多创建了一个div，但不用相对布局了。*/</span></div></div><div><div><div>2</div></div><div><span>.left</span><span>,</span><span>.main</span><span>,</span><span>.right</span><span>{</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>float: </span><span>left</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>min-height: </span><span><span>130</span><span>px</span></span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>text-align: </span><span>center</span><span>;</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div><div><div><div>7</div></div><div><span>.left</span><span>{</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>background: </span><span>green</span><span>;</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>width: </span><span><span>200</span><span>px</span></span><span>;</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>margin-left: </span><span><span>-100</span><span>%</span></span><span>;</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div><div><div><div>12</div></div><div><span>.mian</span><span>{</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>background-color: </span><span>blue</span><span>;</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>width: </span><span><span>100</span><span>%</span></span><span>;</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div><div><div><div>16</div></div><div><span>.right</span><span>{</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>background-color: </span><span>red</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>width: </span><span><span>200</span><span>px</span></span><span>;</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>margin-left: </span><span><span>-300</span><span>px</span></span><span>;</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div><div><div><div>21</div></div><div><span>.content</span><span>{</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>margin:</span><span>0</span><span> </span><span><span>300</span><span>px</span></span><span> </span><span>0</span><span> </span><span><span>200</span><span>px</span></span><span>;</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/summaryCss1/</id>
      <title type="text">前端知识点总结(二) Css基础篇</title>
      <published>2019-10-31T15:20:35.000Z</published>
      <updated>2019-10-31T15:20:35.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/summaryCss1/"/>
      <summary type="text">一、简单介绍 Css 基础的东西比较多，涉及到基础问题也比较多，例如盒模型，布局， Css3 ， Sass 的一些基础内容， Css 部分打算分三块去写，一章是 Css 相关的特性，然后把布局部分单独</summary>
      <content type="html"><![CDATA[<section><h3>一、简单介绍<a href="#一简单介绍"><span>#</span></a></h3><p>    <code>Css</code>基础的东西比较多，涉及到基础问题也比较多，例如盒模型，布局，<code>Css3</code>，<code>Sass</code>的一些基础内容，<code>Css</code>部分打算分三块去写，一章是<code>Css</code>相关的特性，然后把布局部分单独拿出来一章来写，<code>Sass</code>部分和<code>Css3</code>部分拿出来一章来写，暂时计划是这样，如果后面东西比较多的话在适当增加些篇幅。</p></section>
<section><h3>二、Css基础相关<a href="#二css基础相关"><span>#</span></a></h3><section><h4>1、 盒模型，box-sizing<a href="#1-盒模型box-sizing"><span>#</span></a></h4><p>    <code>box-sizing</code> 属性用于改变元素相对于其“盒模型”的大小的方式
     <strong>API：</strong> <code>content-box</code>（默认），<code>border-box</code>，<code>inherit</code>，<code>padding-box</code>（已从规范移除）
    简单理解就是当<code>box-sizing</code>为默认时，为元素设置<code>width</code>，<code>Height</code>是不包括<code>border</code>和<code>padding</code>宽度，设置为<code>border-box</code>，<code>width</code>实质等于<code>border + padding + content</code>，当创建流体<code>（flow）</code>和网格<code>（grid）</code>布局时，例如，你需要计算列的宽度时，这是特别有用的。而不必担心额外的 <code>padding</code> 和 <code>border</code> 宽度会破坏布局，使创建布局更容易。</p></section><section><h4>2、CSS实现隐藏页面的方式<a href="#2css实现隐藏页面的方式"><span>#</span></a></h4><blockquote><p>1、<code>Display：none</code> （真正隐藏元素，不占据任何空间，不生成盒模型）
2、<code>Opacity设置为0</code> （视觉上隐藏，元素本身仍然占有位置，仍然响应用户操作）
3、<code>Visibility：hidden</code> （视觉上隐藏，仍然对布局起作用，不会响应用户操作）
4、<code>Position{absolute，top：-9999，left：-9999}</code> （既想要交互，又不想影响布局时）</p></blockquote></section><section><h4>3、如何实现水平居中和垂直居中。<a href="#3如何实现水平居中和垂直居中"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// </span><span>简单水平居中：</span></div></div><div><div><div>2</div></div><div><span>margin:0 auto;</span></div></div><div><div><div>3</div></div><div><span>text-align:center;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// </span><span>简单垂直居中</span></div></div><div><div><div>2</div></div><div><span>height:line-height;</span></div></div><div><div><div>3</div></div><div><span>vertical-align:middle;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// </span><span><span>复杂div居中</span><span>,</span><span>第一种 （IE9(-ms-</span></span><span>), </span><span>IE10</span><span>+</span><span>以及其他现代浏览器）</span></div></div><div><div><div>2</div></div><div><span>parentElement</span><span>{</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>position:</span><span>relative</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>childElement</span><span>{</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>position: </span><span>absolute</span><span>; top: </span><span><span>50</span><span>%</span></span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>transform: </span><span>translateY</span><span>(</span><span><span>-50</span><span>%</span></span><span>);</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div><span>// </span><span><span>复杂div居中</span><span>,</span><span>第二种 (IE8</span></span><span>+</span><span>以及其他浏览器</span><span>)</span></div></div><div><div><div>10</div></div><div><span>parentElement</span><span> {</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>position: </span><span>relative</span><span>;</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div><div><div><div>13</div></div><div><span>childElement</span><span> {</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>position: </span><span>absolute</span><span>;</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>left: </span><span>0</span><span>; top: </span><span>0</span><span>; right: </span><span>0</span><span>; bottom: </span><span>0</span><span>;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>margin: </span><span>auto</span><span>;</span></div></div><div><div><div>17</div></div><div><span>}</span></div></div><div><div><div>18</div></div><div><span>// </span><span><span>复杂div居中</span><span>,</span><span>第三种 Flex 布局：(不考虑兼容老式浏览器</span></span><span>)</span></div></div><div><div><div>19</div></div><div><span>parentElement</span><span>{</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>display:</span><span>flex</span><span>; </span><span>/*Flex布局*/</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>display: </span><span>-webkit-flex</span><span>; </span><span>/* Safari */</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>align-items:</span><span>center</span><span>; </span><span>/*指定垂直居中*/</span></div></div><div><div><div>23</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section><section><h4>4.元素浮动后的display值<a href="#4元素浮动后的display值"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>无论当前元素为块级元素`block`或者行内元素`inline`或者`inline-block`，被设置为浮动后，元素的`display`属性都变为`block`。</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>5、浮动元素引起的问题和解决办法<a href="#5浮动元素引起的问题和解决办法"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>问题：</span></div></div><div><div><div>2</div></div><div><span>1.父元素的高度无法被撑开，影响与父元素同级的元素</span></div></div><div><div><div>3</div></div><div><span>2.与浮动元素同级的非浮动元素会跟随其后</span></div></div><div><div><div>4</div></div><div><span>3.若非第一个元素浮动，则该元素之前的元素也需要浮动，否则会影响页面显示的结构</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>解决：清除浮动。</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// </span><span>清除浮动常用方法</span></div></div><div><div><div>2</div></div><div><span>// 1、clear: both</span></div></div><div><div><div>3</div></div><div><span>// 2、封装通用类名</span></div></div><div><div><div>4</div></div><div><span>clearfix</span><span>:after</span><span>{</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>content:</span><span>"."</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>display:</span><span>block</span><span>; height:</span><span>0</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>visibility:</span><span>hidden</span><span>; clear:</span><span>both</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div><div><div><div>9</div></div><div><span>.clearfix</span><span> {</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>*zoom:</span><span>1</span><span>;</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>6、link和@import引入css的区别<a href="#6link和import引入css的区别"><span>#</span></a></h4><blockquote><p>区别1：link是XHTML标签，除了加载CSS外，还可以定义RSS等其他事务; @import属于CSS范畴，只能加载CSS。
区别2：link引用CSS时，在页面载入时同时加载；@import需要页面网页完全载入以后加载。
区别3：link是XHTML标签，无兼容问题；@import是在CSS2.1提出的，低版本的浏览器不支持。
区别4：ink支持使用Javascript控制DOM去改变样式；而@import不支持。</p></blockquote></section><section><h4>7、有哪些常用块级元素，行内元素，block、inline和inline-block的区别<a href="#7有哪些常用块级元素行内元素blockinline和inline-block的区别"><span>#</span></a></h4><p>    常见的块级元素有 <strong>div</strong>, <strong>form</strong>, <strong>table</strong>, <strong>p</strong>, <strong>H1~H6</strong>, <strong>dl</strong>, <strong>ol</strong>, <strong>ul</strong>等。
    常见的内联元素有 <strong>span</strong>, <strong>a</strong>, <strong>em</strong>, <strong>label</strong>, <strong>input</strong>, <strong>select</strong>, <strong>textarea</strong>, <strong>img</strong>等。
    <strong><code>Block:</code></strong> 会独占一行,可以设置width,height属性。即使设置了宽度,仍然是独占一行. 可以设置margin和padding属性。
    <strong><code>Inline:</code></strong> 不会独占一行，多个相邻的行内元素会排列在同一行里，直到一行排列不下，才会新换一行，其宽度随元素的内容而变化, 设置width,height属性无效, 水平方向的padding-left, padding-right, margin-left, margin-right都产生边距效果；但竖直方向的padding-top, padding-bottom, margin-top, margin-bottom不会产生边距效果。
    <strong><code>Inline-block</code></strong>：将对象呈现为inline对象，但是对象的内容作为block对象呈现。之后的内联对象会被排列在同一行内, 使其既具有block的宽度高度特性又具有inline的同行特性。</p></section><section><h4>8、css dpi(每英寸包含点的数量（dots per inch）)<a href="#8css-dpi每英寸包含点的数量dots-per-inch"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span> </span></span><span>普通屏幕通常包含96dpi，一般将2倍于此的屏幕称之为高分屏，即大于等于192dpi的屏幕，比如Mac视网膜屏就达到了192dpi（即2dppx），打印时一般会需要更大的dpi；</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>9、Attribute与property的区别<a href="#9attribute与property的区别"><span>#</span></a></h4><p>    <code>property</code>是DOM中的属性，是JavaScript里的对象；<code>attribute</code>是HTML标签上的特性，它的值只能够是字符串；<code>property</code>能够从<code>attribute</code>中得到同步；<code>attribute</code>不会同步<code>property</code>上的值；<code>attribute</code>和<code>property</code>之间的数据绑定是单向的，<code>attribute-&gt;property；</code>更改<code>property</code>和<code>attribute</code>上的任意值，都会将更新反映到HTML页面中；</p></section><section><h4>10、流式布局，自适应布局，响应式布局<a href="#10流式布局自适应布局响应式布局"><span>#</span></a></h4><p>    <code>流式布局（Liquid Layout）</code>：页面元素的宽度按照屏幕分辨率进行适配调整，但整体布局不变。代表作栅栏系统（网格系统）。
    **布局特点和设计方法：**屏幕分辨率变化时，页面里元素的大小会变化而但布局不变。使用%百分比定义宽度，高度大都是用px来固定住，可以根据可视区域 (viewport) 和父元素的实时尺寸进行调整，尽可能的适应各种分辨率。往往配合 max-width/min-width 等属性控制尺寸流动范围以免过大或者过小影响阅读。
    <code>自适应布局（Adaptive Layout）</code>：自适应布局的特点是分别为不同的屏幕分辨率定义布局，即创建多个静态布局，每个静态布局对应一个屏幕分辨率范围。
    **布局特点和设计方法：**屏幕分辨率变化时，页面里面元素的位置会变化而大小不会变化。　使用 @media 媒体查询给不同尺寸和介质的设备切换不同的样式。在优秀的响应范围设计下可以给适配范围内的设备最好的体验，在同一个设备下实际还是固定的布局。
    <code>响应式布局（Responsive Layout）</code>：分别为不同的屏幕分辨率定义布局，同时，在每个布局中，应用流式布局的理念，即页面元素宽度随着窗口调整而自动适配。
    **布局特点和设计方法：**每个屏幕分辨率下面会有一个布局样式，即元素位置和大小都会变。媒体查询+流式布局。通常使用 @media 媒体查询 和网格系统 (Grid System) 配合相对布局单位进行布局，实际上就是综合响应式、流动等上述技术通过 CSS 给单一网页不同设备返回不同样式的技术统称。</p></section><section><h4>11、移动端布局方案<a href="#11移动端布局方案"><span>#</span></a></h4><blockquote><ul>
<li>JS + rem</li>
<li>淘宝Flexible</li>
</ul></blockquote></section><section><h4>12、 BFC（block formatting context）块格式化上下文相关<a href="#12-bfcblock-formatting-context块格式化上下文相关"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>BFC可以理解为没有定义，但有一些特性/功能。例如BFC包住浮动元素，或float + div做左右自适应布局。</span></div></div><div><div><div>2</div></div><div><span>CSS3中display：flow-root；就是创建一个BFC；</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote></blockquote><p><strong>触发BFC：</strong></p><ol>
<li>body 根元素；</li>
<li>浮动元素：float 除 none 以外的值</li>
<li>绝对定位元素：position (absolute、fixed)；</li>
<li>display 为 inline-block、table-cells、flex、flow-root；</li>
<li>overflow 除了 visible 以外的值 (hidden、auto、scroll)
<strong>BFC特性及应用</strong></li>
<li>同一个 BFC 下外边距会发生折叠，如果想要避免外边距的重叠，可以将其放在不同的 BFC 容器中。</li>
<li>BFC 可以包含浮动的元素（清除浮动）</li>
<li>BFC 可以阻止元素被浮动元素覆盖，可以用来实现两列自适应布局。</li>
</ol><blockquote></blockquote></section><section><h4>13、清除浮动的原理<a href="#13清除浮动的原理"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>在支持BFC的浏览器（IE8+，firefox，chrome，safari）通过创建新的BFC闭合浮动；</span></div></div><div><div><div>2</div></div><div><span>在不支持BFC的浏览器 （IE6-7），通过触发 hasLayout 闭合浮动。</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>14、overflow？<a href="#14overflow"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>内容增多时候容易造成不会自动换行导致内容被隐藏掉，无法显示需要溢出的元素；</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>15、padding百分比是相对于父级宽度还是自身的宽度<a href="#15padding百分比是相对于父级宽度还是自身的宽度"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>相对于父级宽度；</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>16、移动端开发用JavaScript 正则表达式判断平台。<a href="#16移动端开发用javascript-正则表达式判断平台"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>var</span><span><span> </span><span>u</span><span> </span></span><span>=</span><span><span> </span><span>navigator</span><span>.</span></span><span>userAgent</span><span><span>, </span><span>app</span><span> </span></span><span>=</span><span><span> </span><span>navigator</span><span>.</span></span><span>appVersion</span><span>;</span></div></div><div><div><div>2</div></div><div><span>//移动终端浏览器版本信息</span></div></div><div><div><div>3</div></div><div><span><span>trident</span><span>: </span><span>u</span><span>.</span></span><span>indexOf</span><span>(</span><span>"Trident"</span><span>) </span><span>&gt;</span><span> </span><span>-</span><span>1</span><span>, </span><span>//IE内核</span></div></div><div><div><div>4</div></div><div><span><span>presto</span><span>: </span><span>u</span><span>.</span></span><span>indexOf</span><span>(</span><span>"Presto"</span><span>) </span><span>&gt;</span><span> </span><span>-</span><span>1</span><span>, </span><span>//opera内核</span></div></div><div><div><div>5</div></div><div><span><span>webKit</span><span>: </span><span>u</span><span>.</span></span><span>indexOf</span><span>(</span><span>"AppleWebKit"</span><span>) </span><span>&gt;</span><span> </span><span>-</span><span>1</span><span>, </span><span>//苹果、谷歌内核</span></div></div><div><div><div>6</div></div><div><span><span>gecko</span><span>: </span><span>u</span><span>.</span></span><span>indexOf</span><span>(</span><span>"Gecko"</span><span>) </span><span>&gt;</span><span> </span><span>-</span><span>1</span><span> </span><span>&amp;&amp;</span><span><span> </span><span>u</span><span>.</span></span><span>indexOf</span><span>(</span><span>"KHTML"</span><span>) </span><span>==</span><span> </span><span>-</span><span>1</span><span>, </span><span>//火狐内核</span></div></div><div><div><div>7</div></div><div><span><span>mobile</span><span>: </span></span><span>!!</span><span><span>u</span><span>.</span></span><span>match</span><span>(</span><span>/AppleWebKit</span><span>.</span><span><span>*</span><span>Mobile</span></span><span>.</span><span><span>*</span><span>/</span></span><span>), </span><span>//是否为移动终端</span></div></div><div><div><div>8</div></div><div><span><span>ios</span><span>: </span></span><span>!!</span><span><span>u</span><span>.</span></span><span>match</span><span>(</span><span><span>/</span><span>\(</span><span>i</span></span><span>[</span><span>^</span><span>;]</span><span><span>+</span><span>;( U;)</span><span>?</span><span> CPU</span></span><span>.</span><span><span>+</span><span>Mac OS X/</span></span><span>), </span><span>//ios终端</span></div></div><div><div><div>9</div></div><div><span><span>android</span><span>: </span><span>u</span><span>.</span></span><span>indexOf</span><span>(</span><span>"Android"</span><span>) </span><span>&gt;</span><span> </span><span>-</span><span>1</span><span> </span><span>||</span><span><span> </span><span>u</span><span>.</span></span><span>indexOf</span><span>(</span><span>"Linux"</span><span>) </span><span>&gt;</span><span> </span><span>-</span><span>1</span><span>, </span><span>//android终端或者uc浏览器</span></div></div><div><div><div>10</div></div><div><span><span>iPhone</span><span>: </span><span>u</span><span>.</span></span><span>indexOf</span><span>(</span><span>"iPhone"</span><span>) </span><span>&gt;</span><span> </span><span>-</span><span>1</span><span> , </span><span>//是否为iPhone或者QQHD浏览器</span></div></div><div><div><div>11</div></div><div><span><span>iPad</span><span>: </span><span>u</span><span>.</span></span><span>indexOf</span><span>(</span><span>"iPad"</span><span>) </span><span>&gt;</span><span> </span><span>-</span><span>1</span><span>, </span><span>//是否iPad</span></div></div><div><div><div>12</div></div><div><span><span>webApp</span><span>: </span><span>u</span><span>.</span></span><span>indexOf</span><span>(</span><span>"Safari"</span><span>) </span><span>==</span><span> </span><span>-</span><span>1</span><span> </span><span>//是否web应该程序，没有头部与底部</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>17、CSS 选择器的优先级<a href="#17css-选择器的优先级"><span>#</span></a></h4><blockquote><ul>
<li>优先级顺序</li>
</ul></blockquote><ul>
<li><code>内联样式</code> &gt; <code>ID选择器</code> &gt; <code>伪类</code> &gt; <code>属性选择器</code> &gt; <code>类选择器</code> &gt; <code>元素（类型）选择器</code> &gt; <code>通用选择器</code></li>
<li>选择器优先权，规则
<ul>
<li>内联样式表的权值最高 1000。ID 选择器的权值为 100。Class 类选择器的权值为 10。</li>
<li>HTML 标签（类型）选择器的权值为 1。 选择器都有一个权值，权值越大越优先。当权值相等时，后出现的样式表设置要优于先出现的样式表设置。创作者的规则高于浏览者：即网页编写者设置的CSS 样式的优先权高于浏览器所设置的样式。继承的CSS 样式不如后来指定的CSS 样式，在同一组属性设置中标有“!important”规则的优先级最大。</li>
</ul>
</li>
</ul><blockquote></blockquote></section><section><h4>18.CSS中的长度单位（em, rem, pt, ex,vw,vh,vmin,vmax）<a href="#18css中的长度单位em-rem-pt-exvwvhvminvmax"><span>#</span></a></h4><p>    <code>em：</code> 相对单位，基准点为父节点字体的大小，如果自身定义了font-size按自身来计算
    <code>rem：</code> 相对单位，可理解为**”root em”**,相对根节点html的字体来计算。chrome最小字体为12号，即使设置10px也会显示成12px，把html的font-size设置成10px,rem还12px为基准，页面基准320px(20px),html font-size值的计算：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 参考手淘</span></div></div><div><div><div>2</div></div><div><span>var</span><span><span> </span><span>ele</span><span> </span></span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>getElementsByTagName</span><span>(</span><span>"html"</span><span>)[</span><span>0</span><span>];</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>size</span><span> </span><span>=</span><span><span> </span><span>document</span><span>.</span></span><span>body</span><span>.</span><span>clientWidth</span><span>/</span><span>320</span><span>*</span><span>20</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>ele</span><span>.</span><span>style</span><span>.</span><span>fontSize</span><span> </span><span>=</span><span><span> </span><span>size</span><span> </span></span><span>+</span><span> </span><span>"px"</span><span>;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    <code>pt:</code> point，大约1/72寸
    <code>ex:</code> 取当前作用效果的字体的x的高度，在无法确定x高度的情况下以0.5em计算(IE11及以下均不支持，firefox/chrome/safari/opera/ios safari/android browser4.4+等均需属性加么有前缀)
    <code>vw：</code> viewpoint width，视窗宽度，1vw等于视窗宽度的1%。
    <code>vh：</code> viewpoint height，视窗高度，1vh等于视窗高度的1%。
    <code>vmin：</code> vw和vh中较小的那个。
    <code>vmax：</code> vw和vh中较大的那个。</p></section><section><h4>19、雪碧图<a href="#19雪碧图"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>将小图标合并到一张图片上，然后利用css的背景定位（background-position）来显示需要显示的图片部分，从而减少你的网站的HTTP请求数量。</span></div></div><div><div><div>2</div></div><div><span>优点：减少加载图片对服务器请求次数，提高页面的加载速度，减少鼠标滑过的一些bug。</span></div></div><div><div><div>3</div></div><div><span>缺点：内存使用，影响浏览器的缩放功能，拼图维护比较麻烦，使CSS的编写变得困难。</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>20.常遇到的浏览器兼容性问题有哪些？常用的hack的技巧<a href="#20常遇到的浏览器兼容性问题有哪些常用的hack的技巧"><span>#</span></a></h4><blockquote><p>1：表单元素行高不一致
<strong>解决方案：</strong> 给表单元素添加float：left（左浮动）；或vertical-align：middle；
2：当在a标签中嵌套img标签时，在某些浏览器中img会有蓝色边框；
<strong>解决方案：</strong> 给img添加border：0；或者是border：none；</p></blockquote></section></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/summaryHtml/</id>
      <title type="text">前端知识点总结(一) Html篇</title>
      <published>2019-10-16T14:00:02.000Z</published>
      <updated>2019-10-16T14:00:02.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/summaryHtml/"/>
      <summary type="text">一、初衷 时光飞逝，转眼还有两个多月19年就过去了，最近打算更新一系列自己整理的关于前端知识点的一些汇总，算是对自己掌握知识的一个汇总，查漏补缺，系列打算分为四个大块来梳理，大概分为 HTML部分 、</summary>
      <content type="html"><![CDATA[<section><h2>一、初衷<a href="#一初衷"><span>#</span></a></h2><p>    时光飞逝，转眼还有两个多月19年就过去了，最近打算更新一系列自己整理的关于前端知识点的一些汇总，算是对自己掌握知识的一个汇总，查漏补缺，系列打算分为四个大块来梳理，大概分为<code>HTML部分</code>、<code>Css部分(包括Sass用法)</code>、<code>Js部分(包括ES6相关基础)</code>、最后<code>拓展部分</code>总结一些关于常用的方法或者概念梳理。此系列文章纯粹属于温故知新，大部分都是基础的知识，如有整理的不到位之处，望各位轻拍。</p></section>
<section><h2>二、HTML<a href="#二html"><span>#</span></a></h2><section><h3>1、 HTML5 新特性，语义化<a href="#1-html5-新特性语义化"><span>#</span></a></h3><blockquote><ul>
<li><code>Section：</code> 定义文档中的主体部分的节、段</li>
<li><code>Article：</code> 定义来自外部的一个独立的、完整的内容块，例如什么论坛的文章</li>
<li><code>Header：</code> 定义文档、页面的页眉。通常是一些引导和导航信息</li>
<li><code>Footer：</code> 定义了文档、页面的页脚</li>
<li><code>Nav：</code> 定义了一个链接组成的导航部分</li>
<li><code>Progress：</code> 进度条（value，max）</li>
<li><code>Summary，details </code> 定义元素，用户可以点击查看或者隐藏:</li>
<li><code>Datalist: </code>  定义可选数据的列表，与 input 配合使用</li>
</ul></blockquote></section><section><h3>2、浏览器的标准模式和怪异模式（quirks mode）<a href="#2浏览器的标准模式和怪异模式quirks-mode"><span>#</span></a></h3><p>    由来：由于历史的原因，在W3C标准出台前不同浏览器对页面的渲染是不同的。
    区别：</p><blockquote><ul>
<li>a、严格模式，width 是内容宽度，元素真正宽度 = <code>margin + border + padding + width</code>;怪异模式，width 是元素实际宽度，内容宽度 = <code>width - padding - border</code>。</li>
<li>b、标准模式下给<code>span</code>等行内元素设置<code>width</code>和<code>height</code>不会生效，而在怪异模式下会生效。</li>
<li>c、在标准模式下，一个元素的高度是由其包含的内容决定的，如果父元素没有设置高度，子元素设置一个百分比的高度是无效的。</li>
<li>d、怪异模式下<code>margin：0 auto</code>，水平居中失效，需用<code>text-align：center</code>。</li>
</ul></blockquote></section><section><h3>3、xhtml和html的区别<a href="#3xhtml和html的区别"><span>#</span></a></h3><blockquote><ul>
<li>XHTML 元素必须被正确地嵌套。</li>
<li>XHTML 元素必须被关闭。</li>
<li>XHTML 标签名必须用小写字母。</li>
<li>XHTML 文档必须拥有根元素。</li>
</ul></blockquote></section><section><h3>4、使用data-的好处<a href="#4使用data-的好处"><span>#</span></a></h3><p>    赋予我们在所有 <code>HTML</code> 元素上嵌入自定义 <code>data</code> 属性的能力，用于存储页面或应用程序的私有自定义数据，以创建更好的用户体验（不进行 <code>Ajax</code> 调用或服务器端数据库查询）。</p></section><section><h3>5、meta标签<a href="#5meta标签"><span>#</span></a></h3><p>    提供有关页面的元信息<code>(meta-information)</code>，比如针对搜索引擎和更新频度的描述和关键词。</p></section><section><h3>6、Canvas<a href="#6canvas"><span>#</span></a></h3><p>     <code>&lt;canvas&gt;</code> 标签定义图形，比如图表和其他图像，但必须使用脚本（js）来绘制图形。首先需要获取<code>canvas</code>对象，创建<code>context</code>对象，<code>getContext("2d")</code> 对象是内建的 <code>HTML5</code> 对象，拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法.下面是<code>canvas的API</code>简单介绍：
     a、Canvas坐标: (canvas是一个二维网格，左上角坐标为0，0)：
        <code>FillRect（x,y,width,height）：</code> 在画布上绘制width*height的矩形，从左上角（x，y）开始。
        <code>FillStyle：</code> 可以是CSS颜色，渐变，图案默认是#000（黑色）。
     b、Canvas路径: (绘制线条必须使用ink方法例如stroke()，fill() )
        <code>moveTo(x,y)：</code> 定义线条开始坐标
        <code>lineTo(x,y)：</code> 定义线条结束坐标
        <code>lineTo(x,y)：</code> 起点坐标，半径，开始位置，结束位置
     c、Canvas文本：
        <code>Font：</code>定义字体
        <code>fillText：</code>（text，x，y）在canvas上绘制实心的文本（文字，文字起点位置）
        <code>strokeText：</code>（text，x，y）在canvas上绘制空心的文本
     d、Canvas渐变：
        <code>createLinearGradient(x，y，x1，y1)</code> 创建线条渐变
        <code>createRadialGradient (x，y，r，x1，y1，r1)</code> 创建一个径向/圆渐变
        <code>addColorStop：</code>方法指定颜色停止，参数使用坐标来描述，可以是0至1.
     e、Canvas图像：
        <code>drawImage(image,x,y)</code>：将图像放在画布上</p></section><section><h3>7、HTML废弃的标签<a href="#7html废弃的标签"><span>#</span></a></h3><p>    font: 可以通过size属性改字体大小,还可以用color属性改变颜色； b : 可以加粗文字  u : 可以加下划线  i : 倾斜； del : 删除线 hr : 水平线；strong : 强调  em : 强调</p></section><section><h3>8、IE6/7/8 bug，及兼容写法<a href="#8ie678-bug及兼容写法"><span>#</span></a></h3><blockquote><ul>
<li>1、IE6 css选择器不支持子选择器</li>
<li>2、在IE浏览器中，定位元素的z-index层级是相对于各自的父级容器，所以会导致z-index出现错误的表现。解决方法：其父级元素定义z-index，有些情况下还需要定义position；</li>
</ul></blockquote></section><section><h3>9、css js放置位置和原因<a href="#9css-js放置位置和原因"><span>#</span></a></h3><p>    <code>js</code>是阻塞加载，会影响页面加载的速度，如果<code>js</code>文件比较大，算法也比较复杂的话，影响更大。<code>CSS</code>放在前端是页面渲染时首先是根据<code>DOM</code>结构生成一个<code>DOM</code>树然后加上<code>CSS</code>样式生成一个渲染树，如果<code>CSS</code>放在后面可能页面会出现闪跳的感觉，或者是白屏或者布局混乱样式很丑直到<code>CSS</code>加载完成。</p></section><section><h3>10、什么是渐进式渲染<a href="#10什么是渐进式渲染"><span>#</span></a></h3><p>    以往基于字符串拼接的模板引擎技术, 性能可以,前端不方便,而现在基于 <code>Virtual DOM</code> 实现的后端渲染, 前端舒服, 后端却变慢,原因是 <code>Virtual DOM</code> 难以做静态分析进行预编译, 最终难以提高性能.另外组件级别 Caching(缓存) 方案也不够成熟, 提高性能短期做不到, 那么考虑对服务端渲染的工作进行缩减.比如说, 首屏渲染多少内容? 整页渲染性能低, 能不能只渲染部分,比如说只渲染第一屏主体内容, 而下方或者更详细数据在客户端抓取.就是说, 服务端渲染一部分, 客户端加载一部分, 从而做出效果。</p></section><section><h3>11、html模板语言<a href="#11html模板语言"><span>#</span></a></h3><p>    <code>html</code>有打开效率高的先天优势，但也有一个先天缺陷“不支持动态语言”，这也是<code>html</code>模板语言出现的原因，让网站即享受<code>html</code>高效，又享受内容的动态化；使用比较多的模板引擎也就是<code>Handlebars.js</code>，<code>Etpl</code>，基本原理也就是将<code>json</code>数据通过一些方法，例如用对应的字符串匹配替换扩展方法（正则匹配），转换成<code>html</code>模板片段，然后一个实际的<code>javascript</code>循环。</p></section><section><h3>12、meta viewport简介<a href="#12meta-viewport简介"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>  </span><span>&lt;meta</span><span> </span><span>name="viewport"</span><span> </span><span>content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure></div><blockquote><ul>
<li><code>width：</code> 控制 viewport 的大小，可以指定值，还可以特殊的值，如：device-width。</li>
<li><code>height：</code> 与 width 相对应，指定viewport 高度。</li>
<li><code>initial-scale：</code> 初始缩放比例，也即是当页面第一次 load 的时候缩放比例。</li>
<li><code>maximum-scale：</code> 允许用户缩放到的最大比例。minimum-scale：允许用户缩放到的最小比例。</li>
<li><code>user-scalable：</code> 是否允许用户手动缩放。</li>
</ul></blockquote></section></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/gitWorkflow/</id>
      <title type="text">Git的基本操作和 git workflow</title>
      <published>2019-09-27T15:03:05.000Z</published>
      <updated>2019-09-27T15:03:05.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/gitWorkflow/"/>
      <summary type="text">一、扯扯Git的诞生 在没有Git之前，大家大部分都是靠 SVN 和 CVS 等集中式的版本控制系统，集中式版本控制系统不但速度慢，而且必须联网才能使用，所有的版本信息都放在服务器上，万一服务器宕机一</summary>
      <content type="html"><![CDATA[<section><h3>一、扯扯Git的诞生<a href="#一扯扯git的诞生"><span>#</span></a></h3><p>    在没有Git之前，大家大部分都是靠<code>SVN</code>和<code>CVS</code>等集中式的版本控制系统，集中式版本控制系统不但速度慢，而且必须联网才能使用，所有的版本信息都放在服务器上，万一服务器宕机一个小时，基本上这一个小时大家就不用工作了。这个时候分布式代码版本控制就横空出世了，优点很明显，分布式管理，没有网络的情况下仍然可以查看日志、提交代码、创建分支，而且支持<code>local branch</code>, 缺点可能算是有一定的学习曲线，但是作为程序员算是一个基本技能。</p></section>
<section><h3>二、Git的基本操作<a href="#二git的基本操作"><span>#</span></a></h3><section><h4>1、创建版本库<a href="#1创建版本库"><span>#</span></a></h4><p>    版本库其实就是一个仓库，仓库里面的所有文件都可以被<code>Git</code>管理起来，对文件的每次修改，删除，新增都可以进行追踪。新建一个仓库非常简单只需要新增一个文件夹，然后<code>init</code>一下就可以。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>//</span><span> </span><span>新建一个文件夹</span></div></div><div><div><div>2</div></div><div><span>$</span><span>  </span><span>mkdir</span><span> </span><span>gitDemo</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>//</span><span> </span><span>初始化Git仓库</span></div></div><div><div><div>5</div></div><div><span>$</span><span>  </span><span>git</span><span> </span><span>init</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    然后你就会发现在文件夹下多了一个<code>.git</code>的文件夹，这个文件夹就是来跟踪管理版本库的，一般不需要改动。如果没有看到<code>.git</code>文件夹那应该是隐藏了可以通过 <code>ls -ah</code> 命令查看。</p></section><section><h4>2、将文件添加到版本库<a href="#2将文件添加到版本库"><span>#</span></a></h4><p>    当我们对文件进行改动需要提交到仓库时，只需要做二步就可以,假如我需要把readme.text加入到仓库</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>//</span><span> </span><span>用</span><span> </span><span>add</span><span> </span><span>命令告诉</span><span> </span><span>git</span><span> </span><span>我准备把文件添加到仓库,</span><span> </span><span>暂时先放在你这</span><span>(</span><span>暂存区</span><span>)</span></div></div><div><div><div>2</div></div><div><span>$</span><span>   </span><span>git</span><span> </span><span>add</span><span> </span><span>readme.text</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>//</span><span> </span><span>用</span><span> </span><span>commit</span><span> </span><span>命令提交到本地仓库，Git是分布式，commit后本地就会有提交记录</span></div></div><div><div><div>5</div></div><div><span>//</span><span> </span><span>commit命令是有一个</span><span> </span><span>-m</span><span> </span><span>参数后面跟随的是此次提交的说明，这个说明很重要，不能乱写，不然后面找改动记录就很麻烦了</span></div></div><div><div><div>6</div></div><div><span>$</span><span>   </span><span>git</span><span> </span><span>commit</span><span> </span><span>-m</span><span> </span><span>'init readme.text file'</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>    我们现在已经成功提交了一个文件，如果我们想随时知道当前版本库的状态可以使用 <code>git status</code> 命令进行查看，这个命令会告诉我们当前的状态几个文件<code>add</code>, 几个<code>commit</code>都可以清楚的看到，但是如果我们想知道这次对文件做了哪些修改，就可以使用 <code>git diff</code>命令进行查看，这个命令在我们实际开发中每次提交前通过<code>diff</code>去查看本次的改动还是很有必要的。</p></section><section><h4>3、远程仓库<a href="#3远程仓库"><span>#</span></a></h4><p>    我们知道一个项目不可能只有我们自己在开发，那么协作开发就需要用到一个中间服务器，角色有点类似<code>SVN</code>的中央存储但是又有本质的区别，在<code>git</code>中可以理解成交换中心，我们自己写完代码推送到远程交换中心，别人想要看到我们的最新代码就需要就远程就拉取。远程库的创建有两种方式，一种是本地已有仓库，和远程关联，一种是先创建远程，直接通过命令克隆到本地然后进行开发。如果一个项目从头开始，个人还是比较建议先创建远程仓库，然后从远程克隆库。
      <b>I、</b>添加远程库
          假设我们现在已经创建了一个远程仓库，现在流行的git仓库管理地址也就差不多是<code>Github Gitee GitLab</code>等平台，创建仓库的方式都大同小异也很简单。创建仓库后会提示我们克隆新的，还是关联已有的，如果关联已有的话我们需要在本地仓库运行命令：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>git</span><span> </span><span>remote</span><span> </span><span>add</span><span> </span><span>origin</span><span> </span><span>git@github.com:userName/gitDemo.git</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>          以 <code>GitHub</code> 为例，上面的userName需要替换成你自己的账户名称，这个命令其实就是将本地仓库和远程关联了起来，下一步就需要把本地的内容全部推送上去。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>git</span><span> </span><span>push</span><span> </span><span>-u</span><span> </span><span>origin</span><span> </span><span>master</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>          <code>git push</code>命令就是把本地库的内容推送到远程，由于远程库是空的，我们第一次推送<code>master</code>分支时，加上了<code>-u</code>参数，<code>Git</code>不但会把本地的<code>master</code>分支内容推送的远程新的<code>master</code>分支，还会把本地的<code>master</code>分支和远程的<code>master</code>分支关联起来，在以后的推送或者拉取时就可以简化命令。
      <b>II、</b>从远程克隆
          从远程克隆的话其实就很简单了，远端创建好仓库后，只需要执行命令：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>$</span><span> </span><span>git</span><span> </span><span>clone</span><span> </span><span>git@github.com:userName/gitDemo.git</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>          如果有多人开发只需要每个人各自克隆一下就好了。</p></section><section><h4>4、分支管理<a href="#4分支管理"><span>#</span></a></h4><p>    几乎每一种版本控制系统都以某种形式支持分支，分支的功能大致就是将不同功能的代码剥离出来，同时不影响主线的开发。实际应用中，一般采用一个<code>master</code>主分支，为线上稳定版本分支，此分支不可直接提交，只能通过<code>develop</code>或者<code>hotfix</code>分支进行合并。一个为<code>develop</code>开发分支，分支为最新完成开发通过开发自测的开发分支，在我们项目中，给到测试的代码就是直接从<code>develop</code>中打包，而没有采用<code>release</code>分支。第三个为<code>feature</code>分支，也就是功能分支，当我们接到一个新功能需要开发时，需要从<code>develop</code>分支切一个属于自己的功能开发分支，当功能开发完成需要交付测试时，将代码合并至<code>develop</code>分支。最后还需要一个<code>hotfix</code>分支，此分支用来解决线上爆出的bug需要紧急修复，此分支需要从线上版本<code>master</code>分支切出，通过测试上线后合并至<code>master</code>和<code>develop</code>分支。这是现在大多数包括我们自己开发团队在使用的<code>Git</code>分支管理规范和流程，也是前人经过探索总结的，来保证开发过程更好的进行沟通协作。这种<code>git flow</code>模式下的项目会一直保留两个长期分支，<code>master</code>存放对外发布的稳定版，<code>develop</code>存放日常开发的最新版。另外<code>master</code>每次发布后需要对当前节点打上<code>标签(Tag)</code>进行记录，一般可以用于版本号记录，方便以后版本追溯。
      <b>I、</b>基本管理
          创建分支很简单,速度也很快，主要因为创建分支时，<code>Git</code>只是新建了一个指针，指向切出分支相同的那次提交，再把<code>HEAD</code>指向新分支，就表示在新建分支上，工作区文件实质上没有变化。分支操作的基础命令如下:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 创建分支</span></div></div><div><div><div>2</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span></span><span>branch</span><span><span> (</span><span>branchname</span><span>)</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 切换分支（-b 参数表示创建并切换）</span></div></div><div><div><div>5</div></div><div><span>// 或者 git switch （-c 参数表示创建并切换）</span></div></div><div><div><div>6</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span></span><span>checkout</span><span><span> (</span><span>branchname</span><span>)</span></span></div></div><div><div><div>7</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>checkout</span><span> </span></span><span>-</span><span>b</span><span><span> (</span><span>branchname</span><span>)</span></span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span></span><span>switch</span><span><span> (</span><span>branchname</span><span>)</span></span></div></div><div><div><div>10</div></div><div><span>$ git switch -c (branchname)</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>// 合并分支</span></div></div><div><div><div>13</div></div><div><span>$ git merge</span></div></div><div><div><div>14</div></div><div>
</div></div><div><div><div>15</div></div><div><span>// 列出所有分支</span></div></div><div><div><div>16</div></div><div><span>$ git branch</span></div></div><div><div><div>17</div></div><div>
</div></div><div><div><div>18</div></div><div><span>// 删除分支</span></div></div><div><div><div>19</div></div><div><span>$ git branch -d (branchname)</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>      <b>II、</b>标签管理
          上面我们其实也有提到针对<code>master</code>的每次发布进行打标签，其实标签就是来定义一个重要阶段，并希望永远记住那个时刻，比如我们版本发布了<code>1.0版本</code>,就可以在<code>master</code>的最新一次提交加上<code>V1.0</code>得标签。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 创建一个带注解标签</span></div></div><div><div><div>2</div></div><div><span>$ git tag -a v1.0</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 查看所有标签</span></div></div><div><div><div>5</div></div><div><span>$ git tag</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 删除标签</span></div></div><div><div><div>8</div></div><div><span>$ git tag -d v1.0</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 对历史提交打标签</span></div></div><div><div><div>11</div></div><div><span>$ git tag v1.0 commitID</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>          需要注意的一点是标签总是与某个<code>commit</code>挂钩的，如果这个<code>commit</code>既出现在<code>master</code>分支又出现在<code>dev</code>分支那么这两个分支都是可以看到标签的。</p></section><section><h4>5、Git Stash 和 Git WorkTree 场景和用法<a href="#5git-stash-和-git-worktree-场景和用法"><span>#</span></a></h4><p>      <b>I、Git Stash </b>
          我们在开发时经常会遇到这样的场景，正在 <code>feature</code> 分支开发功能，线上突然出现了高优先级bug要马上解决，但是新功能才写了一半不想提交，这个时候就可以用<code>stash</code>命令将当前进度保存起来，然后切换至另一个分支修改bug，修改完成后切回 <code>feature</code> 恢复之前进度继续开发。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 保存当前工作进度，可以添加save增加注释，可以多次使用</span></div></div><div><div><div>2</div></div><div><span>$ git stash save &lt;message&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 显示保存进度列表</span></div></div><div><div><div>5</div></div><div><span>$ git stash list</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>// 恢复最新的进度</span></div></div><div><div><div>8</div></div><div><span>$ git stash pop</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 删除一个存储进度，不指定stash_id，默认删除最新</span></div></div><div><div><div>11</div></div><div><span>$ git stash drop [stash_id]</span></div></div><div><div><div>12</div></div><div>
</div></div><div><div><div>13</div></div><div><span>// 删除所有存储进度</span></div></div><div><div><div>14</div></div><div><span>$ git stash clear</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>      <b>II、Git WorkTree </b>
          <code>git stash</code> 已经可以基本上帮助我们解决多个分支切换工作的问题，但是工作中更多的时候会有需要并行开发的情况，我需要同时多开编辑器，而且更多时候因为分支不同，我有可能依赖的<code>node_modules</code>版本也是有差异的，但是因为我们配置<code>ignore</code>的原因，切换分支并不会重新安装依赖会导致打包依赖时的各种问题，<code>git 2.6 </code>以上开始提供了<code> worktree</code> 功能，可以解决这样的问题。<code>git worktree</code> 从一个仓库中可以创建多个工作目录，方便多开编辑器并行开发。<code>git worktree </code>的命令只有几行非常容易记住。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 增加新的worktree</span></div></div><div><div><div>2</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>worktree</span><span> </span><span>add</span><span> [</span><span>路径</span><span>] [</span><span>分支</span><span>]</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 新分支创建</span></div></div><div><div><div>5</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>worktree</span><span> </span><span>add</span><span> </span></span><span>-</span><span><span>b</span><span> [</span><span>新分支名</span><span>] [</span><span>新路径</span><span>] [</span><span>从此分支创建</span><span>]</span></span></div></div><div><div><div>6</div></div><div><span>// eg. git worktree add -b myNewBranch ../testDir master</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>// worktree 详细情况, 可以增加 --porcelain 选项，用来改变显示风格</span></div></div><div><div><div>9</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>worktree</span><span> </span><span>list</span><span> </span></span><span>--</span><span>porcelain</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// 删除worktree(删除关联目录后，清除worktree信息</span></div></div><div><div><div>12</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>worktree</span><span> </span><span>prune</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>6、撤销和回滚<a href="#6撤销和回滚"><span>#</span></a></h4><p>          在我们开发过程中肯定会遇到这样的场景，一、我刚把不想要的代码，点错了给<code>commit</code>，但是还没有<code>push</code>，二、线上炸了，需要还原这次代码。类似于这几种情况我们就需要用到<code>git</code>的撤销与回滚。
          场景一、未<code>push</code>之前的所有提交操作，可以统称为撤销.</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 文件被修改，但是还没 add</span></div></div><div><div><div>2</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>checkout</span><span> </span><span>fileName</span></span></div></div><div><div><div>3</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>checkout</span><span> .</span></span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>// 对多个文件执行了add，但是只想提交一部分</span></div></div><div><div><div>6</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>add</span><span> </span></span><span>*</span></div></div><div><div><div>7</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>status</span></span></div></div><div><div><div>8</div></div><div><span><span># </span><span>取消暂存</span></span></div></div><div><div><div>9</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>reset</span><span> </span></span><span>HEAD</span><span> </span><span>&lt;</span><span>filename</span><span>&gt;</span></div></div><div><div><div>10</div></div><div>
</div></div><div><div><div>11</div></div><div><span>// 文件执行了add 但是不想要这些改动</span></div></div><div><div><div>12</div></div><div><span><span># </span><span>取消暂存</span></span></div></div><div><div><div>13</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>reset</span><span> </span></span><span>HEAD</span><span><span> </span><span>fileName</span></span></div></div><div><div><div>14</div></div><div><span><span># </span><span>撤销修改</span></span></div></div><div><div><div>15</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>checkout</span><span> </span><span>fileName</span></span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div><p>          场景二、已进行<code>push</code>，需要回滚。</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>// 回滚到tag标记处</span></div></div><div><div><div>2</div></div><div><span><span>$</span><span> </span><span>git</span><span> </span><span>checkout</span><span> </span></span><span>&lt;</span><span>tag</span><span>&gt;</span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>// 撤销指定文件到指定版本</span></div></div><div><div><div>5</div></div><div><span><span># </span><span>查看指定文件的历史版本</span></span></div></div><div><div><div>6</div></div><div><span><span>git</span><span> </span><span>log</span><span> </span></span><span>&lt;</span><span>filename</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span># </span><span>回滚到指定commitID</span></span></div></div><div><div><div>8</div></div><div><span><span>git</span><span> </span><span>checkout</span><span> </span></span><span>&lt;</span><span>commitID</span><span>&gt;</span><span> &lt;</span><span>filename</span><span>&gt;</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>// 删除最后一次远程提交</span></div></div><div><div><div>11</div></div><div><span>#使用revert</span></div></div><div><div><div>12</div></div><div><span>$ git revert HEAD</span></div></div><div><div><div>13</div></div><div><span>$ git push origin master</span></div></div><div><div><div>14</div></div><div><span># 使用reset</span></div></div><div><div><div>15</div></div><div><span>$ git reset --hard commitID^</span></div></div><div><div><div>16</div></div><div><span>$ git push origin master -f</span></div></div><div><div><div>17</div></div><div><span>#区别: revert 放弃提交但是会生成一次新的提交，reset直接重置到指定提交</span></div></div><div><div><div>18</div></div><div>
</div></div><div><div><div>19</div></div><div><span>// 回滚某次提交</span></div></div><div><div><div>20</div></div><div><span># 找到要回滚的commitID</span></div></div><div><div><div>21</div></div><div><span>git log</span></div></div><div><div><div>22</div></div><div><span>git revert commitID</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/next1/</id>
      <title type="text">Hexo Next 主题添加百度统计</title>
      <published>2019-07-14T13:10:05.000Z</published>
      <updated>2019-07-14T13:10:05.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/next1/"/>
      <summary type="text">网站搭建也有了很长一段时间，以前也没怎么维护过，想起来了就写点东西，域名也买了，博客也搭了，总不能就在这长草了，还是要装修装修，今天想着虽然博客没啥浏览量但是说不准以后流量会越来越大，这个时候就要去统</summary>
      <content type="html"><![CDATA[<p>    网站搭建也有了很长一段时间，以前也没怎么维护过，想起来了就写点东西，域名也买了，博客也搭了，总不能就在这长草了，还是要装修装修，今天想着虽然博客没啥浏览量但是说不准以后流量会越来越大，这个时候就要去统计博客的访问情况，比如博客的历史访问量、访问来源、访问地域等统计数据。之前用过百度统计的工具，百度账号啥的也都是现成的，这篇就简单介绍下 <code>Hexo + Next</code> 博客配置百度统计功能。</p>
<section><h5>1、注册百度统计账号<a href="#1注册百度统计账号"><span>#</span></a></h5><p>    首先需要注册一个百度统计的账号，这块都很简单，注册成功后登录会进入到网站列表添加目标网站。网站添加成功后可以在管理模块选择代码获取，如图所示：
</p><figure><img src="/uploads/tongjiKey.jpg" alt="tongjiKey" /><figcaption>tongjiKey</figcaption></figure>
     按照以往的经验，想着直接拿这段代码添加到 <code>footer.swig</code>文件中，或者<code>header</code>文件中，只需要添加到网站全部页面的<code>head</code>标签前就行，但是网上查了资料发现，远没有那么复杂，Next 主题对百度统计已经进行了封装优化，我们只需要将上面图片中的key值写入就可以。<p></p></section>
<section><h5>2、配置next主题文件<a href="#2配置next主题文件"><span>#</span></a></h5><p>     在配置文件 <code>themes/next/_config.yml</code> 中，有个 <code>baidu_analytics</code> 字段，将百度统计中的key值配置下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span># </span><span>Baidu</span><span> </span><span>Analytics</span><span> </span></span><span>ID</span></div></div><div><div><div>2</div></div><div><span><span>baidu_analytics</span><span>: </span><span>上图中key值</span><span>。</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>     就这么简单~ 是不是so easy，然后编译发布下就OK。</p></section>
<section><h5>3、验证时可能遇到问题<a href="#3验证时可能遇到问题"><span>#</span></a></h5><p>     配置成功后，我们就可以到百度统计的管理模块，点击右侧的代码安装检查
</p><figure><img src="/uploads/tongjikey2.jpg" alt="tongjikey2" /><figcaption>tongjikey2</figcaption></figure>
     如果显示的代码安装正确那么就成功没啥问题了，大概20分钟左右就可以看到统计数据，但是我在第一次测试时，页面安装状态提示：代码未生效。我手工检测了一下，发现代码安装是正确的，但是一直提示未生效，我又在其他网页中输入<code>http:luoruihuan.top</code> 没有添加www进行了测试，发现提示安装正确，这就比较费解了，虽然不知道具体导致原因是啥，但是我编辑了一下网站列表中的网站首页改成了去除www的形式，就ok了，虽然不知道为啥，感觉有可能是百度统计的bug。然后就可以去主页查看报告了~，虽然现在还没啥浏览量。。。<p></p></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/RecentlyReadHistory/</id>
      <title type="text">扯扯最近读的历史</title>
      <published>2016-05-21T15:08:00.000Z</published>
      <updated>2016-05-21T15:08:00.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/RecentlyReadHistory/"/>
      <summary type="text">这段时间酷爱读史，今读胡三省在《资治通鉴》注中“姜维之心,始终为汉,千载之下,炳炳如丹,陈寿、孙盛、干宝之讥皆非也。”想起当年刘禅献降后，姜维密奏，“愿陛下忍数日之辱,臣欲使社稷危而复安,日月幽而复明</summary>
      <content type="html"><![CDATA[<p>     这段时间酷爱读史，今读胡三省在《资治通鉴》注中“姜维之心,始终为汉,千载之下,炳炳如丹,陈寿、孙盛、干宝之讥皆非也。”想起当年刘禅献降后，姜维密奏，“愿陛下忍数日之辱,臣欲使社稷危而复安,日月幽而复明”，这个时候姜维已经是62岁，大大小小北伐了11次。这种执着，真的是可贵。姜维的一生，是充满矛盾的，在众生相中有些格格不入的。无论身前事，还是身后名。当年写下“若有远志，不在当归”，能在异国一片主和声坚持攻伐自己的母国，执著甚于诸葛亮。</p>
<p>     《银河英雄传说》中，杨威利说过一句话：“我不能选择最好的，我只是选择较好的制度。” 被诸多银英迷奉为圭臬。但可惜，这句话并不适用所有情况：“三国归晋”并不是一个相对较好的选择，而是剩下的唯一选择：短期内不那么差，长期更差。当然，封建集团出于政治目的在后世对他的褒贬，现在看来没有什么直接的参考意义。但他最后这份卫国者的决绝，值得任何人尊重。</p>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/ReadingForComment/</id>
      <title type="text">如何看待读书杂</title>
      <published>2016-04-18T13:10:00.000Z</published>
      <updated>2016-04-18T13:10:00.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/ReadingForComment/"/>
      <summary type="text">前段时间和同学聊天，聊到读书的问题，大学时候经常泡在图书馆看各种书，于是大家一起讨论，读书肯定是有好处的，这是毋庸置疑的，但是读了那么多的杂书，意义在哪呢，后来想了很久，</summary>
      <content type="html"><![CDATA[<p>    前段时间和同学聊天，聊到读书的问题，大学时候经常泡在图书馆看各种书，于是大家一起讨论，读书肯定是有好处的，这是毋庸置疑的，但是读了那么多的杂书，意义在哪呢，后来想了很久，印象中梁启超曾说过，“书宜杂读，业宜精钻”，今悟: 读书杂，头绪多，可以探微寻幽，别开蹊径。业务精，专一艺，可以打牢基础，扬其所长；业务精，明利弊，可以看事通透，做事到底。</p>
<p>    挤出时间学些常识性东西，特别是文学，或人文科学，稍多用点工夫就能有大收获,以前不屑的一些东西，要试着去接触。既然我不能判断是坏的，那为什么要拒之门外呢？既然是存在的，那我就应该了解这些常识。停止学习，停止读书，我仿佛看到了未来的自己，一无所知的自己。</p>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/TernaryConditionalOperator/</id>
      <title type="text">Javascript三元条件运算符</title>
      <published>2016-03-16T14:03:05.000Z</published>
      <updated>2016-03-16T14:03:05.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/TernaryConditionalOperator/"/>
      <summary type="text">今天谈一个小知识点，三元运算符。三元运算，顾名思义会有三个要素，表达式的大致组成为condition ? expr1 : expr2；一个语句加两个表达式。问号之前为判断语句。如果为真，则执行第一个表</summary>
      <content type="html"><![CDATA[<p>今天谈一个小知识点，三元运算符。三元运算，顾名思义会有三个要素，表达式的大致组成为condition ? expr1 : expr2；一个语句加两个表达式。问号之前为判断语句。如果为真，则执行第一个表达式，如果为假，则执行第二个表达式。我的理解其实就是简化的if else语句，举个栗子，经常看到的一个点击展开，再点击隐藏按钮:</p>
<p></p><figure><img src="/uploads/three.png" alt="js-three-bg" /><figcaption>js-three-bg</figcaption></figure><p></p>
<p>我想实现点击输入法，实现下面ul的展开与隐藏，如果我们用if else的话可能会这么写：</p>

<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>     </span></span><span>oBth</span><span>.</span><span>onclick</span><span>=</span><span>function</span><span>(){</span></div></div><div><div><div>2</div></div><div><span>        </span><span>if</span><span><span> (</span><span>oUl</span><span>.</span></span><span>style</span><span>.</span><span>display</span><span>==</span><span>'block'</span><span>) {</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>oUl</span><span>.</span><span>style</span><span>.</span><span>display</span><span>==</span><span>'none'</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>}</span><span>else</span><span>{</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>oUl</span><span>.</span><span>style</span><span>.</span><span>display</span><span>==</span><span>'block'</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>但是如果我们用三元运算符的话可能就只需要下面这一行代码：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>oBth</span><span>.</span><span>onclick</span><span>=</span><span>function</span><span>(){</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>oUl</span><span>.</span><span>style</span><span>.</span><span>display</span><span> </span><span>==</span><span> </span><span>"block"</span><span> </span><span>?</span><span><span> </span><span>oUl</span><span>.</span></span><span>style</span><span>.</span><span>display</span><span>=</span><span>"none"</span><span> </span><span>:</span><span><span> </span><span>oUl</span><span>.</span></span><span>style</span><span>.</span><span>display</span><span>=</span><span>"block"</span></div></div><div><div><div>3</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>有木有很神奇。当然我只是用了一个比较容易理解的写法，还有很大的优化空间，我们当然也可以这样：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>oBth</span><span>.</span><span>onclick</span><span>=</span><span>function</span><span>(){</span></div></div><div><div><div>2</div></div><div><span>        </span><span>var</span><span><span> </span><span>style</span><span> </span></span><span>=</span><span><span> </span><span>oUl</span><span>.</span></span><span>style</span><span>.</span><span>display</span><span>;</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>oUl</span><span>.</span><span>style</span><span>.</span><span>display</span><span>=</span><span><span> </span><span>style</span><span> </span></span><span>==</span><span> </span><span>"block"</span><span> </span><span>?</span><span> </span><span>"none"</span><span>:</span><span>"block"</span><span>  </span><span>//这样写的话就一定不能忘了把运算结果重新赋值给元素 - -</span></div></div><div><div><div>4</div></div><div><span>　　    </span><span>//oUl.style.display= (style == "block" ? "none":"block") 可读性更高</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>上面代码的意思是，如果style == “block” 成立的话就把none返回，不成立就返回block，然后把返回结果赋值给作用元素，不过要特别注意 = 和 == 用法和区别。因为赋值运算符（=）的优先级比较低，所以会最后执行赋值运算。如果把后面的三元运算符加上（）可读性会更高一点，但效果是一样的。</p>
<section><h2>比较容易出错的地方：<a href="#比较容易出错的地方"><span>#</span></a></h2><p>三元运算比较容易出错的应该就是运算的优先级问题：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>var</span><span><span> </span><span>isMember</span><span> </span></span><span>=</span><span> </span><span>false</span><span>;</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>"当前费用"</span><span> </span><span>+</span><span><span> </span><span>isMember</span><span> </span></span><span>?</span><span> </span><span>"$2.00"</span><span> </span><span>:</span><span> </span><span>"$10.00"</span><span>); </span><span>//返回$2.00</span></div></div></code></pre><div><div></div><div></div></div></figure></div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>"当前费用false"</span><span> </span><span>?</span><span> </span><span>"$2.00"</span><span> </span><span>:</span><span> </span><span>"$10.00"</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在js中字符串是为真的，所以会输出$2.00.</p><p>不过为了避免以上错误的话，可以记住</p><section><h3>不是false, 0, undefined, NaN, "" or null，js都认为是true;<a href="#不是false-0-undefined-nan--or-nulljs都认为是true"><span>#</span></a></h3><p>感谢阅读。</p></section></section>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/JSSwiper/</id>
      <title type="text">原生js实现幻灯片轮播效果</title>
      <published>2016-02-18T12:00:00.000Z</published>
      <updated>2016-02-18T12:00:00.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/JSSwiper/"/>
      <summary type="text">在以往的认知中，一直以为用原生JS写轮播是件很难得事情，今天上班仿照网上的写了一个小demo。小试牛刀。 大致效果： swiper-bg /uploads/swiper-bg.png html结构很简</summary>
      <content type="html"><![CDATA[<p>在以往的认知中，一直以为用原生JS写轮播是件很难得事情，今天上班仿照网上的写了一个小demo。小试牛刀。</p>
<p>大致效果：</p>
<p></p><figure><img src="/uploads/swiper-bg.png" alt="swiper-bg" /><figcaption>swiper-bg</figcaption></figure><p></p>
<p>html结构很简单，两个列表，一个代表图片列表，一个是右下角序号列表。</p>

<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span><span>    </span></span><span>&lt;</span><span>div</span><span> </span><span>id</span><span>=</span><span>"box"</span><span>&gt;</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>&lt;</span><span>ul</span><span> </span><span>class</span><span>=</span><span>"list"</span><span>&gt;</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span>&gt;&lt;</span><span>img</span><span> </span><span>src</span><span>=</span><span>"01.jpg"</span><span> </span><span>width</span><span>=</span><span>"490"</span><span> </span><span>height</span><span>=</span><span>"170"</span><span> /&gt;&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span>&gt;&lt;</span><span>img</span><span> </span><span>src</span><span>=</span><span>"02.jpg"</span><span> </span><span>width</span><span>=</span><span>"490"</span><span> </span><span>height</span><span>=</span><span>"170"</span><span> /&gt;&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>5</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span>&gt;&lt;</span><span>img</span><span> </span><span>src</span><span>=</span><span>"03.jpg"</span><span> </span><span>width</span><span>=</span><span>"490"</span><span> </span><span>height</span><span>=</span><span>"170"</span><span> /&gt;&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span>&gt;&lt;</span><span>img</span><span> </span><span>src</span><span>=</span><span>"04.jpg"</span><span> </span><span>width</span><span>=</span><span>"490"</span><span> </span><span>height</span><span>=</span><span>"170"</span><span> /&gt;&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span>&gt;&lt;</span><span>img</span><span> </span><span>src</span><span>=</span><span>"05.jpg"</span><span> </span><span>width</span><span>=</span><span>"490"</span><span> </span><span>height</span><span>=</span><span>"170"</span><span> /&gt;&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>&lt;</span><span>ul</span><span> </span><span>class</span><span>=</span><span>"count"</span><span>&gt;</span></div></div><div><div><div>10</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span> </span><span>class</span><span>=</span><span>"current"</span><span>&gt;1&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>11</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span>&gt;2&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>12</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span>&gt;3&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>13</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span>&gt;4&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>14</div></div><div><span><span>            </span></span><span>&lt;</span><span>li</span><span>&gt;5&lt;/</span><span>li</span><span>&gt;</span></div></div><div><div><div>15</div></div><div><span><span>        </span></span><span>&lt;/</span><span>ul</span><span>&gt;</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>&lt;/</span><span>div</span><span>&gt;</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div>
<p>布局的大致想法是图片全部绝对定位重合，透明度为1，右下角序号给选中的添加样式。样式都很简单。</p>
<p>关于JS方面，自己还是捉摸了很久，刚开始实现时，当鼠标移入右下角对应序列号时图片能够准时切换，但是鼠标移出时便不能自动播放，关于setInterval和clearInterval自己的处理还是不是很好。借鉴了网上的案列之后，具体的实现逻辑简单为，先写一个图片的展现方法，和鼠标移出图片时的定时函数。这样可以在事件中直接调用，值得注意的一点就是在鼠标移入时，记得清除定时器。下面上代码：</p>
<p>这是图片展示函数，参数为当前的展示的index。先让所有图片透明度为0；所有下标没有样式，然后给当前的加上样式，透明度为1；</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>function</span><span> </span><span>show</span><span><span>(</span><span>a</span><span>){</span></span></div></div><div><div><div>2</div></div><div><span>        </span><span>for</span><span>(</span><span>var</span><span><span> </span><span>i</span></span><span>=</span><span>0</span><span><span>;</span><span>i</span></span><span>&lt;</span><span><span>oLi</span><span>.</span></span><span>length</span><span><span>;</span><span>i</span></span><span>++</span><span>){</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>oLi</span><span>[</span><span>i</span><span>].</span><span>style</span><span>.</span><span>opacity</span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>oImg</span><span>[</span><span>i</span><span>].</span><span>className</span><span> </span><span>=</span><span> </span><span>''</span><span>;</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>oLi</span><span>[</span><span>a</span><span>].</span><span>style</span><span>.</span><span>opacity</span><span> </span><span>=</span><span> </span><span>200</span><span>;</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>oImg</span><span>[</span><span>a</span><span>].</span><span>className</span><span> </span><span>=</span><span> </span><span>'current'</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>然后写一个鼠标移除时的定时器，图片自动轮播函数：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>function</span><span> </span><span>autoplay</span><span>(){</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>time</span><span> </span><span>=</span><span> </span><span>setInterval</span><span>(</span><span>function</span><span>(){</span></div></div><div><div><div>3</div></div><div><span><span>            </span></span><span>b</span><span>++</span><span>;</span></div></div><div><div><div>4</div></div><div><span>            </span><span>if</span><span><span>(</span><span>b</span></span><span>&gt;=</span><span><span>oImg</span><span>.</span></span><span>length</span><span>){</span></div></div><div><div><div>5</div></div><div><span><span>                </span></span><span>b</span><span>=</span><span>0</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>            </span><span>show</span><span><span>(</span><span>b</span><span>);</span></span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>},</span><span>1000</span><span>);</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span>    </span><span>autoplay</span><span>();</span><span>//自动运行</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>最后是鼠标移入移出事件:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>    </span><span>for</span><span>(</span><span>var</span><span><span> </span><span>i</span></span><span>=</span><span>0</span><span><span>;</span><span>i</span></span><span>&lt;</span><span><span>oImg</span><span>.</span></span><span>length</span><span><span>;</span><span>i</span></span><span>++</span><span>){</span></div></div><div><div><div>2</div></div><div><span><span>        </span></span><span>oImg</span><span>[</span><span>i</span><span>].</span><span>index</span><span> </span><span>=</span><span><span> </span><span>i</span><span>;</span></span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>oImg</span><span>[</span><span>i</span><span>].</span><span>onmouseover</span><span>=</span><span>function</span><span>(){</span></div></div><div><div><div>4</div></div><div><span>            </span><span>clearInterval</span><span><span>(</span><span>time</span><span>);</span></span></div></div><div><div><div>5</div></div><div><span>            </span><span>show</span><span>(</span><span>this</span><span>.</span><span>index</span><span>)</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>oImg</span><span>[</span><span>i</span><span>].</span><span>onmouseout</span><span>=</span><span>function</span><span>(){</span></div></div><div><div><div>8</div></div><div><span>            </span><span>autoplay</span><span>();</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>}</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>代码只是写了一个结构骨架，实现大致效果，具体可以优化的地方还有很多，对于动效方面可以对透明度的改变设置一个函数，让透明度缓慢变成1，或者对图片的定位和移动方向进行改变，实现真正意义上的左右轮播。大致原理都是差不多的。</p>
<p>原生JS实现轮播，于我而言还是有点小难度的，因为JS写的比较少，所以对于整体的逻辑把握不好，虽有曲折，但是靠着自己也是写了出来。</p>
<p>总结一些轮播遇到的大致困难，第一个是对于鼠标移出时的定时自动轮播，刚开始自己是写在事件onmouseout里面的，但是测试过程中发现事件只会触发一次，所以导致轮播不能执行，所以选择在重新写了一个方法。第二个就是对于函数的传参问题，我写函数一直没有传参的习惯，都是匿名函数，虽然知道适用场景不同，对函数的运用应该灵活，可能是因为做的项目太少，经验还是太不足，第三个就是细节方面，例如移入时清除定时器，轮播要先执行一次等。总之收获还是很大的。</p>
<p>感谢阅读。</p>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/ChineseInternet/</id>
      <title type="text">曾经的中国互联网</title>
      <published>2016-01-23T03:40:07.000Z</published>
      <updated>2016-01-23T03:40:07.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/ChineseInternet/"/>
      <summary type="text">1、最早在美国上市的中国互联网公司是中华网，域名牛逼，中国概念，一度股价爆高到100多美元，后来一路跌到地板，然后退市，现在，谁还记得呢。 2、曾经联想搞了一个fm365.com，联想的互联网战略，非</summary>
      <content type="html"><![CDATA[<p>1、最早在美国上市的中国互联网公司是中华网，域名牛逼，中国概念，一度股价爆高到100多美元，后来一路跌到地板，然后退市，现在，谁还记得呢。</p>
<p>2、曾经联想搞了一个fm365.com，联想的互联网战略，非常非常早的事情，随着联想电脑预装铺开，一度号称是国内门户前几名，然后呢？后来一度域名都丢了，然后也没有然后了。</p>

<p>3、我们知道现在最大的文学站是起点文学，最大的下载一个是华军软件，一个是百度收购天空下载。但曾经最大的文学站黄金书屋，最大的下载站是海阔天空，然而这些网站都被一个叫多来米的公司用很低的价格收购了，那时候个人站不值钱啊，然后呢，多来米收了一堆当时最好的个人站，结果全做死了。</p>
<p>4、最早的电子商务网站，8848.com，曾经是中国电子商务网站头牌，在上市关键时刻的临门一脚，没有成功，然后，每况愈下。</p>
<p>5、世界互联网巨头收购中国互联网领先者，听上去好像是一个不错的强强联手，两个案例，ebay收购了当时的领先者易趣，彼时易趣在中国电商市场份额超过80%，然后被新成立的淘宝打的丢盔卸甲。 雅虎收购了3721，当时3721的收入规模和终端覆盖率均力压百度，然后就眼看着百度神话诞生了。</p>
<p>6、第一只A股上的互联网概念股叫做海虹控股，当时他们控股了中国最大的，完全独孤求败的棋牌游戏平台，联众游戏。 然后慢慢的联众就废掉了，一步步落后于时代。 今天我们不要说腾讯了，说波克城市这样的棋牌平台，收入我估计都比联众高不少。</p>
<p>7、曾经最大的下载工具叫做网络蚂蚁。</p>
<p>8、互联网泡沫破裂的时候，网易是三大门户里最脆弱的，一度股价掉到1块钱以内，丁磊炒掉了他聘用的CEO，财务出现丑闻，媒体天天给网易做退市的倒计时，大家都在幸灾乐祸，然而今天我们看，三大门户里网易转身最快，新业务拓展最成功，现在游戏市场，腾讯拿他也没办法。</p>
<p>9、南非电讯投资腾讯的时候，所有媒体人都觉得南非人SB，被马化腾骗了，说马化腾的估值方式完全是蒙人的，oicq用户（当时不叫QQ）都是低龄低层次用户，没有消费能力。 现在作为腾讯股东的价值和回报是南非电讯集团当前市值最大的支撑，超过其本身自营业务。。</p>
<p>10、曾经大家都认为中国互联网大局已定，新浪搜狐的霸主地位无可动摇，今天回头看，呵呵。</p>
<p>11、百度和新浪曾经撕逼过，百度那时候还是技术服务商，新浪的搜索是百度提供技术支持的，然后，新浪拖欠了百度的技术服务费，李彦宏一怒之下停了新浪的搜索接口，媒体都认为百度疯了，互联网霸主新浪分分钟捏死百度，这也是李彦宏摔电话决心自己做用户的主要原因之一。</p>
<p>12、曾经有一种期盼，说中国互联网啥时候能出市值超过十亿美元的巨头啊，呵呵。</p>
<p>13、百度上市的时候市值破百亿美元，市盈率过千，当时基本上国内所有分析师都说百度被严重高估了。</p>
<p>14、中国做游戏是不可能赚钱的，中国人没有人会为游戏付费，这是在2002年之前行业的共识，当时说的游戏主要是pc游戏，台湾和日本的游戏在大陆都很流行，直到盛大突然崛起，一切都变了。</p>
<p>15、群聊功能最早是朗玛uc推出的，市场反馈极好，当时用户发展极快，新浪见状立刻收购。那时候腾讯想做用户变现，取消了电脑注册QQ的功能，只允许手机短信验证码注册，一个注册收一块钱，结果看到朗玛uc有取代之势，立即恢复免费注册，同时推出QQ群，然后新浪这次投资基本就失败了。</p>
<p>16、百度推出过一个百度hi的聊天工具，还有一个百度有啊的电商平台，不过今天大部分年轻人都没听说过。 我的头像来源于是百度hi当时的气泡熊表情包，我觉得百度hi最有价值的成果就是气泡熊表情做的确实不错。</p>
<p>17、网景浏览器曾经被认为是一种革命，市场占有率一度在80%以上，当时网景的野心很大，想用浏览器做网络终端操作系统，市场也极为认可，股价和市值扶摇直上，很多投资者都认为网景可能是互联网时代的微软，然后微软内置了浏览器，网景告微软垄断，官司一直打，微软浏览器市场份额一直上升，然后，官司没打完，网景完了。 但有意思的是，现在，国内有360浏览器，国外的火狐，google的chrome浏览器，市场占有率都不低；为什么当时网景模式没成功，而现在第三方浏览器又都冒出来了，所以，归咎于微软垄断和捆绑，不如多思考自己的问题。 我给一个答案，网景的思路是互联网上的软件公司，而如今第三方浏览器完全是走互联网模式，做软件公司，被微软掐死是分分钟的事情。</p>
<p>18、2000年左右微软推搜索引擎，当时还不是bing，是MSN搜索，国内分析师近乎一致认为google要完，大家都觉得微软的技术能力很强，有钱，有资源，又能铺终端，但当时俞军老师坚持认为微软根本不懂搜索，不会冲击google，我特意翻过旧帖，从此转为俞军脑残粉。</p>
<p>19、吴京川老师十几年前给我讲搜索套利模式的时候，我完全没听懂，几年后我看一些新闻案例突然明白了，然后感慨市场时机过去了，直到最近我才知道，其实市场时机一直都在，只是进入门槛越来越高而已，意识到自己简直蠢的不可救药。</p>
<p>20、地方电信的门户网站和其附属的一些资源，社区曾经一度占据互联网流量的半壁江山，比较有名的包括上海热线，贵州信息港，碧海银沙等等。</p>
<p>21、网易最早就是个个人站，丁磊是中国最大的个人站长，当时丁磊要搞免费邮局，去广州电信谈合作，说不要电信出一分钱共同搭建一个邮局平台，只要电信出带宽资源。（当时流量带宽老贵老贵的，但对于电信来说成本极低），合作，然后未来广告收入大家分啥的巴拉巴拉。 当时的局长张静君一听，没听懂，但是禁不住她琢磨，不要出钱还能分钱，有这么好的事情？这里肯定藏着很大的利益，这好事不能让丁磊占了，这样，我直接买你们系统。 于是就有了163.net免费邮局，国内第一个免费邮局，用户量果然激增，因为没有其他家啊，然而做了很久，也没赚到钱，后来这个邮局被卖掉了还涉及国有资产流失啥的，张静君后来出去创业，一度被认为是互联网大佬，继续呵呵吧。 当然，丁磊也够坏的，卖了163.net给电信，自己掖着163.com。</p>
<p>22、腾讯最早域名是tencent.com，百度 cdn 长期有个跳转域名叫做shifen.com  大家都是1毛， 这事哪说理去。 美国的巨头叫做微软，中国的巨头叫做1毛。</p>
<p>23、李彦宏，周鸿祎，马化腾，雷军，丁磊，史玉柱，最早都是程序员，而且都还是水平相当不错的程序员，当然更不用说张志东这样的了。其实这个名单可以拉很长，很多名气没那么大的上市公司老板其实也做过程序员，比如携程老板梁建章等等。</p>
<p>24、个人网站</p>
<p>南杨北高曾经是个人网站最早的传奇，高春辉的个人主页曾经是国内网站综合排名前十，汽车销售员出身的第一代站长杨震霆目前已经移民加拿大，当地华人没有几个知道他曾经辉煌的历史；而高春辉依然在连续创业，中国互联网草根创业者的活化石。 可能很少人知道，卓越网的第一任老板是高春辉，而那时候卓越网，是一个下载站。躲在梅州几乎从不出远门，从不接受采访以及从不参加各种峰会（实际上，只参加过一次）的中专生李兴平  创造了草根网站最大的奇迹，他依靠本地的几个人的小团队，先后创建过三个曾经位居国内访问量前20的网站，hao123, qq163 和 4399。而其他诸如3533，ip138，oicq88等一堆网站也都各个流量不菲，最疯狂的时候，一个音乐个人站长，和李兴平私交不错，就能轻松从他那里获得每天十几万到几十万的访问来路。</p>
<p>当然，今天，个人站长的时代过去了。</p>
<p>25、关于拥抱变化</p>
<p>百度最早是给门户做搜索技术服务商，没打算自己做流量入口；</p>
<p>腾讯最早想给电信做网络寻呼系统，没打算自己做用户，当时想发展点用户做样本测试系统；结果电信不鸟他，幸亏电信不鸟他。</p>
<p>阿里最开始做企业黄页；</p>
<p>盛大最开始做社区，失败了后用最后剩下的钱代理了传奇，当时大家都觉得中国游戏没市场，所以代理金极低；</p>
<p>京东最开始中关村摆摊的不解释；</p>
<p>唯品会开始搞奢侈品赔的一塌糊涂，转入二线品牌开始翻身；</p>
<p>聚美优品陈欧第一次创业是做游戏广告平台，然后转型卖了化妆品；</p>
<p>奇虎最开始做社区搜索，恶心阿里搞了360去弄死3721，没想到无心栽柳；</p>
<p>新浪最开始是四通利方论坛，收了海外一个华人门户后转型做门户，然后靠微博翻身；</p>
<p>网易从虚拟社区到邮局到门户到移动SP到游戏；</p>
<p>搜狐从门户到游戏；</p>
<p>现在你告诉我你看准了一个大方向保证未来很有前途？？</p>
<p>你可以当八卦看，也可以多想想，沧海桑田的背后，为什么。然后想想，未来会怎样？我们是不是还会看走眼，选错路？</p>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/PipelineCrisis/</id>
      <title type="text">什么是运营商的‘管道化危机’？ -读移动帝国整理</title>
      <published>2015-12-18T12:00:00.000Z</published>
      <updated>2015-12-18T12:00:00.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/PipelineCrisis/"/>
      <summary type="text">运营商相当于高速公路，建好物理网络后对上面运营的服务进行收费。 随着手机上网速度的加快，微信、Q Q、微博等涉及语音、文字信息的服务也快速发展，这些服务占用了高速公路的车道资源，却没给收费站带来应有的</summary>
      <content type="html"><![CDATA[<p>运营商相当于高速公路，建好物理网络后对上面运营的服务进行收费。</p>
<p>随着手机上网速度的加快，微信、Q Q、微博等涉及语音、文字信息的服务也快速发展，这些服务占用了高速公路的车道资源，却没给收费站带来应有的收益，同时还冲击到了运营商的语音、短信、彩信等业务。</p>
<p>移动互联网时代的到来，已经成为运营商们心中最大的煎熬。一方面，它们希望借此继续转型，在传统话音业务增长见顶的情况下，从数据业务获得更多的收入。</p>
<p>另一方面，在和新兴移动互联网企业同台竞争时，“基因”的差异又使得运营商们稍显力不从心。</p>
<p>所以各国电信业务运营商迫切的需要去‘管道化’！</p>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/xiaomi/</id>
      <title type="text">读《雷军-从金山到小米手机》有感</title>
      <published>2015-11-28T10:15:53.000Z</published>
      <updated>2015-11-28T10:15:53.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/xiaomi/"/>
      <summary type="text">花了两天时间，读完了褚亚玲的这本《雷军-从金山到小米》。刚读完时，内心的第一反应竟是，不容易，雷军不容易。 印象中第一次接触小米是在12年刚上大一时，那时有同学用小米手机，说是为发烧而生，遂让我有了对</summary>
      <content type="html"><![CDATA[<p>花了两天时间，读完了褚亚玲的这本《雷军-从金山到小米》。刚读完时，内心的第一反应竟是，不容易，雷军不容易。</p>
<p>印象中第一次接触小米是在12年刚上大一时，那时有同学用小米手机，说是为发烧而生，遂让我有了对小米的好奇和关注。而了解雷军，是在13年的赢在中国节目上，看他言谈举止，觉得这个人很有内涵，很有想法。</p>

<p>以上的了解都很局限，可以说是表象。前几天小米note 3发布，雷军在发布会现场落泪，引发了我的思考，我一直在想那么成功的一个企业家，面对自己打造的产品，在发布会落泪，这应该是一种怎样的情感，带着很多疑问和不解，我买了一本关于雷军的书，希望能深入的了解一下。</p>
<p>读完整本书，对雷军个人的崇拜和景仰有上升了一个新的高度，从大学时期的佼佼者，到金山的第六号员工，再到CEO，董事长，金山十六年，不是一般人可以比拟的。雷军是个很有毅力的人，说是毅力，倒不如说是梦想，书中有一句话，我深表同意，那就是‘没有梦想的人，永远不知道梦想的力量’。我能体会到他那时的感受和想法。不过，我还是感觉雷军的成功来的有点迟，但幸好还是来了。</p>
<p>1999年，在中国互联网史上，真的是值得大书特书的一年。在这一年，马云从北京返回杭州，和他的十八罗汉，创办了一家叫阿里巴巴的电子商务网站；2月，年仅28岁的马化腾开始推广一款名为OICQ的即时通信网络工具，这只叫QQ的小企鹅，在不久后便出现在中国千家万户互联网用户的电脑桌面上；6月，沈南鹏、梁建章、季琦、范敏四个好朋友聚在一起，创建了一家提供在线旅游服务的网站——携程网；7月，中华网上市，IPO当日大涨，11月，个体书商李国庆和妻子俞渝，创办了从事网络图书销售的当当网；也是在11月，董事长秘书陈天桥拒绝了即将到手的分房，下海创办了一家叫盛大的互联网游戏公司，也是这年，李彦宏认定环境成熟，启程回国，在北大资源宾馆租了两间房，开始了创建百度公司。雷军曾说，‘站在风口，一头猪都能飞起来，一个智慧的人要懂得顺势而为，不要做逆天的事情。99年是互联网最黄金的一年，错过了99，几乎没有哪个企业能够大成。但他却把盐碱地里的金山种出了草，不得不说，雷军经受的考验不一般。</p>
<p>从刚开始对创业的反思：在年轻人扬帆出海之际，遭遇狂风巨浪，会打击自信心，筹备成熟再出手，经过岁月沉淀之后的从容淡定，会让创业者更加游刃有余。到后来对自己的反思：聪明者不在于不走弯路与错路，而是少走弯路与错路，特别是走了弯路与错路后懂得及时回头。可以看出，雷军的每一个反思，都让自己的内心和意志更上了一层楼。雷军善于总结，善于思考，这也是最应该学习的地方。方向如果错了，走得越快，离终点越远。</p>
<p>有些人生下来就是要成功的，有些人，命里就有不服输的基因。从大学的积累，到在金山的历练，最后用小米实现梦想，雷军给我诠释了什么是厚积薄发，什么是信念和理想。小米会成功的，不因为别的，就因为他的boss是雷军。</p>
<p>最后，Google十戒，与各位共勉。</p>
<blockquote><ul>
<li>1.一切以用户为中心，其他一切纷至沓来。（Focus on the user and all else will follow）.</li>
<li>2.把一件事情做到极致。（It`s best to do one thing really,really well）.</li>
<li>3.快比慢好（Fast is better than slow）.</li>
<li>4.网络社会需要民主。（Democracy on the web works）</li>
<li>5.你不一定要在桌子上找答案。（You don`t need to be at your desk to need an answer）</li>
<li>6.不做坏事也能挣钱。（You can make money without doing evil）</li>
<li>7.未知的事情总是存在（There`s always more information out there）</li>
<li>8.对信息的需求无所不在。（The need for information crosses all borders）</li>
<li>9.不穿西装也可以严肃认真（You can be serious without a suit）</li>
<li>10.仅有优秀是远远不够的。（Great just isn`t good enough?）</li>
</ul></blockquote>]]></content>
    </entry>
    <entry>
      <id>https://luoruihuan.top/posts/RuralCultural/</id>
      <title type="text">浅谈农村文化变迁</title>
      <published>2015-11-26T02:52:31.000Z</published>
      <updated>2015-11-26T02:52:31.000Z</updated>
      <author><name>技术派罗恩</name></author>
      <link rel="alternate" href="https://luoruihuan.top/posts/RuralCultural/"/>
      <summary type="text">依稀记得小时候观看农村“唱大戏”的场景，在农村，无论喜事还是丧事都有请戏班唱戏的风俗。本就是图个热闹吉利，或许人们观赏的仅仅局限于形式，也就渐渐助长了农村文化低俗，奢靡之风。 总是习惯于晚饭后，逢邻里</summary>
      <content type="html"><![CDATA[<p>依稀记得小时候观看农村“唱大戏”的场景，在农村，无论喜事还是丧事都有请戏班唱戏的风俗。本就是图个热闹吉利，或许人们观赏的仅仅局限于形式，也就渐渐助长了农村文化低俗，奢靡之风。</p>
<p>总是习惯于晚饭后，逢邻里乡亲有红、白事时，端一小板凳，携家带口，安坐于戏台旁。静候一场‘晚宴’。</p>
 
<p>印象中的人，总是不太多的。想想还不懂事时，总是喜欢穿梭与幕后台下，无所顾忌。那是的一切所说不上多么有档次，但极尽欢乐。 近两年，由于在外地上学，唯有适逢寒暑假期能回家探望。记得从外地刚回来时，陪父亲去听戏，脑海中展现了儿时听戏的些许程序。</p>
<p>若是喜事，总是先来半个小时的歌曲串烧，继而一段较为经典的戏曲，然后一段小品，差不多已有九点多，最后几首老歌压轴，记忆中好像喜事总是唱一首难忘今宵，然后散场。赶上运气好的时候，有可能看上那么一两场杂技表演。</p>
<p>而丧事大多为了表达对死者以尊重，通常以一段唢呐开场。继而白头戏、小品、经典压轴歌曲父老乡亲。但，这次看戏，却让我大跌眼镜，又可以说是大开眼界，不知从何时起，一股低俗、毫无趣味之风气刮遍乡村。每逢‘当大事’时。再无以往的戏台，取而代之的是一辆农用五征车，将车厢打开，平铺，快捷方便。而再也不见三三两两等待化妆的演员。</p>
<p>唯有浓妆艳抹的所谓舞女。舞台的表现形式更从“多元化”趋向于“单一”。以前的所有项目，演技一毫无踪迹，而香艳的脱衣舞在舞台确实摄人心魄。时间安排更是极为巧尽。刚开始，用播放器先播放半小时的音乐，待酒足饭饱的人们珊珊而坐的时候，所谓‘少女’就要登台露肉了。一场持续两个小时的舞蹈占据了演出的所有，而大多数的结尾，也一般由此女穿内衣下台。</p>
<p>一个传播乡村文明的舞台到处充斥着糜烂的气息，演出未完，几经想走，但又有着强烈的好奇心，并不是对那厮的身体多么有兴趣，只是想知道，为了生活而牺牲一切，在台上极尽风光而台下满脸厌恶和疲倦的女人，到底会不会躲在自己心灵的角落，默默地哭泣。</p>
<p>所以我也懂了，有一万个人就会有一万个生活方式，各取所需。我们没权利去指责任何人，更没权利去劝导和改变她。作为茫茫众生的一菩提，沧海一粟，我想能做的仅仅是在心里暗暗叫几声可惜。真可惜啊。然后，悻悻的滚被窝，重拾旧梦。</p>
<p>谈到这两声可惜，我想说的是，虽然这个可惜有着对此女沦落的悲哀，为她选择这样一种生活方式而惋惜。但大多数还是对农村一种文化遗产渐渐遗失的痛楚。</p>
<p>读过余秋雨先生“文化苦旅”中有一篇是写废墟的。我想，农村文化正在由昌盛，繁荣趋向没落，几近毁灭。渐渐沦为农村文化的‘废墟’。但其却又有存在的意义与价值。对于文化的理解我始终太过于浅薄和粗鄙。或许这迎合了大多数人们的需求与需要，‘民心所向’。但当‘无知’的农民欣赏，玩乐过后，深深留在人们心坎中的沉痛思考，对于追随民族文化而言，恐怕又是一次巨大的心灵创伤吧。</p>]]></content>
    </entry>
</feed>
