<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-cn"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://devqin.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://devqin.github.io/" rel="alternate" type="text/html" hreflang="zh-cn" /><updated>2025-08-13T07:15:48+00:00</updated><id>https://devqin.github.io/feed.xml</id><title type="html">步履不停</title><subtitle></subtitle><entry><title type="html">React状态管理:优雅地同步你的业务模型与UI</title><link href="https://devqin.github.io/2025/react-model-view-summary.html" rel="alternate" type="text/html" title="React状态管理:优雅地同步你的业务模型与UI" /><published>2025-08-13T00:00:00+00:00</published><updated>2025-08-13T00:00:00+00:00</updated><id>https://devqin.github.io/2025/react-model-view-summary</id><content type="html" xml:base="https://devqin.github.io/2025/react-model-view-summary.html"><![CDATA[<p>在现代前端开发中，尤其是在使用React时，我们常常遵循“先设计模型（Model），再开发业务逻辑和UI”的流程。这是一个优秀的设计模式，但它也带来了React开发中的一个核心挑战：<strong>如何优雅地将独立的业务模型与React的组件状态（State）和生命周期（Lifecycle）结合起来？</strong></p>

<p>状态变更如何触发UI更新？模型的状态如何与组件的状态同步？本文将深入探讨这些问题，并提供从简单到复杂的多种优雅解决方案。</p>

<h2 id="核心理念模型是唯一的真相来源-single-source-of-truth">核心理念：模型是唯一的真相来源 (Single Source of Truth)</h2>

<p>在深入代码之前，我们必须建立一个核心设计理念：<strong>业务模型是应用状态的唯一、权威的来源。</strong> React组件的 <code class="language-plaintext highlighter-rouge">state</code> 只是这个权威数据在UI上的一种 <strong>反映 (Reflection)</strong>。</p>

<p>遵循这个理念，我们的数据流应该是清晰的单向流：</p>

<ol>
  <li><strong>用户交互</strong> (<code class="language-plaintext highlighter-rouge">onClick</code>, <code class="language-plaintext highlighter-rouge">onChange</code> 等) 调用模型提供的方法。</li>
  <li><strong>模型</strong> 内部更新其自身的状态。</li>
  <li><strong>模型</strong> 通知所有“订阅”了其变化的监听者。</li>
  <li><strong>React组件</strong> (作为一个监听者) 收到通知后，更新自己的 <code class="language-plaintext highlighter-rouge">state</code> 以反映模型的最新状态。</li>
  <li><strong>React</strong> 根据新的 <code class="language-plaintext highlighter-rouge">state</code> 重新渲染UI。</li>
</ol>

<p>在这个流程中，<code class="language-plaintext highlighter-rouge">useEffect</code> 扮演了至关重要的 <strong>“桥梁”</strong> 角色，它负责在组件挂载时 <strong>订阅</strong> 模型的变更，并在组件卸载时 <strong>取消订阅</strong>，从而实现模型与UI的同步。</p>

<h2 id="场景一组件级模型当模型与ui生命周期同步">场景一：组件级模型——当模型与UI生命周期同步</h2>

<p>当一个较为复杂的业务逻辑和状态只被单个组件（及其子组件）使用时，我们可以采用一种轻量级的模式。</p>

<h3 id="1-设计一个可订阅的模型">1. 设计一个可订阅的模型</h3>

<p>首先，我们创建一个标准的JavaScript/TypeScript类作为模型。关键在于，它需要包含一个简单的订阅/发布机制（观察者模式）。</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// models/UserProfileModel.js</span>

<span class="cm">/**
 * 用户配置文件的模型类。
 * 包含数据、更新方法和一个简单的发布/订阅系统。
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">UserProfileModel</span> <span class="p">{</span>
  <span class="kd">constructor</span><span class="p">(</span><span class="nx">initialData</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">data</span> <span class="o">=</span> <span class="nx">initialData</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">listeners</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Set</span><span class="p">();</span> <span class="c1">// 使用Set来存储回调函数，避免重复</span>
  <span class="p">}</span>

  <span class="cm">/**
   * 订阅模型变更。
   * @param {function} callback - 当数据变化时要执行的回调函数。
   * @returns {function} - 一个用于取消订阅的函数。
   */</span>
  <span class="nx">subscribe</span><span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">listeners</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">callback</span><span class="p">);</span>
    <span class="c1">// 返回一个清理函数，这对于在useEffect中安全使用至关重要</span>
    <span class="k">return</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="k">this</span><span class="p">.</span><span class="nx">listeners</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">callback</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="c1">// 私有方法，用于通知所有监听者</span>
  <span class="nx">_notify</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">listeners</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">callback</span> <span class="o">=&gt;</span> <span class="nx">callback</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">data</span><span class="p">));</span>
  <span class="p">}</span>

  <span class="c1">// 模型方法：更新名称</span>
  <span class="nx">updateName</span><span class="p">(</span><span class="nx">newName</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">name</span> <span class="o">!==</span> <span class="nx">newName</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">data</span> <span class="o">=</span> <span class="p">{</span> <span class="p">...</span><span class="k">this</span><span class="p">.</span><span class="nx">data</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="nx">newName</span> <span class="p">};</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">_notify</span><span class="p">();</span> <span class="c1">// 状态变更后，通知所有订阅者</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// 模型方法：异步获取数据</span>
  <span class="k">async</span> <span class="nx">fetchData</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// 模拟API请求</span>
    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">John Doe</span><span class="dl">'</span><span class="p">,</span> <span class="na">email</span><span class="p">:</span> <span class="dl">'</span><span class="s1">john.doe@example.com</span><span class="dl">'</span> <span class="p">};</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">data</span> <span class="o">=</span> <span class="nx">response</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">_notify</span><span class="p">();</span> <span class="c1">// 数据获取后，通知所有订阅者</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="2-在组件中消费模型">2. 在组件中消费模型</h3>

<p>在React组件中，我们使用 <code class="language-plaintext highlighter-rouge">useState</code> 来持有模型的 <strong>反映</strong>，并用 <code class="language-plaintext highlighter-rouge">useEffect</code> 来建立 <strong>同步</strong>。</p>

<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// components/UserProfile.jsx</span>
<span class="k">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span> <span class="nx">useState</span><span class="p">,</span> <span class="nx">useEffect</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">UserProfileModel</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../models/UserProfileModel</span><span class="dl">'</span><span class="p">;</span>

<span class="c1">// 使用useState的函数式初始值，确保模型实例只在组件首次渲染时创建一次。</span>
<span class="c1">// 这是一个性能优化的好习惯。</span>
<span class="kd">const</span> <span class="nx">userModel</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">UserProfileModel</span><span class="p">({</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Guest</span><span class="dl">'</span><span class="p">,</span> <span class="na">email</span><span class="p">:</span> <span class="dl">''</span> <span class="p">});</span>

<span class="kd">function</span> <span class="nx">UserProfile</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// 组件的state，它将永远是模型 `userModel.data` 的一个副本</span>
  <span class="kd">const</span> <span class="p">[</span><span class="nx">user</span><span class="p">,</span> <span class="nx">setUser</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="nx">userModel</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>

  <span class="nx">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="c1">// --- 这是连接模型和React的关键 ---</span>

    <span class="c1">// 1. 组件挂载时，立即订阅模型的变化</span>
    <span class="kd">const</span> <span class="nx">unsubscribe</span> <span class="o">=</span> <span class="nx">userModel</span><span class="p">.</span><span class="nx">subscribe</span><span class="p">(</span><span class="nx">newUserData</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">模型通知组件更新...</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">setUser</span><span class="p">(</span><span class="nx">newUserData</span><span class="p">);</span> <span class="c1">// 当模型数据变化时，更新组件的state</span>
    <span class="p">});</span>

    <span class="c1">// 2. 触发模型的初始数据加载</span>
    <span class="nx">userModel</span><span class="p">.</span><span class="nx">fetchData</span><span class="p">();</span>

    <span class="c1">// 3. 组件卸载时，执行返回的清理函数，取消订阅以防止内存泄漏</span>
    <span class="k">return</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">组件卸载，取消订阅。</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">unsubscribe</span><span class="p">();</span>
    <span class="p">};</span>
  <span class="p">},</span> <span class="p">[]);</span> <span class="c1">// 空依赖数组 `[]` 确保此effect仅在挂载和卸载时运行</span>

  <span class="kd">const</span> <span class="nx">handleNameChange</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">newName</span> <span class="o">=</span> <span class="s2">`User </span><span class="p">${</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()}</span><span class="s2">`</span><span class="p">;</span>
    <span class="c1">// 关键：我们调用模型的方法来改变状态，而不是直接调用setUser！</span>
    <span class="nx">userModel</span><span class="p">.</span><span class="nx">updateName</span><span class="p">(</span><span class="nx">newName</span><span class="p">);</span>
  <span class="p">};</span>

  <span class="k">return</span> <span class="p">(</span>
    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">button</span> <span class="na">onClick</span><span class="p">=</span><span class="si">{</span><span class="nx">handleNameChange</span><span class="si">}</span><span class="p">&gt;</span>Change Name<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
  <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="场景二跨组件共享模型状态管理的真正挑战">场景二：跨组件共享模型——状态管理的真正挑战</h2>

<p>当多个不相关的组件需要访问和操作同一个模型时（如全局的用户信息、购物车），状态管理就变得复杂了。</p>

<h3 id="方案areact-context内置的官方解法">方案A：React Context——内置的官方解法</h3>

<p>React Context API 专为解决跨组件数据共享而设计，可以避免“属性层层传递”（Prop Drilling）的麻烦。</p>

<p><strong>最佳实践是：将模型实例、状态和同步逻辑封装到一个 <code class="language-plaintext highlighter-rouge">Provider</code> 和一个自定义 <code class="language-plaintext highlighter-rouge">Hook</code> 中。</strong></p>

<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// context/UserContext.js</span>
<span class="k">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span> <span class="nx">createContext</span><span class="p">,</span> <span class="nx">useContext</span><span class="p">,</span> <span class="nx">useState</span><span class="p">,</span> <span class="nx">useEffect</span><span class="p">,</span> <span class="nx">useMemo</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">UserProfileModel</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../models/UserProfileModel</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">UserContext</span> <span class="o">=</span> <span class="nx">createContext</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>

<span class="c1">// 1. 创建一个Provider组件，它将成为共享状态的“源头”</span>
<span class="k">export</span> <span class="kd">function</span> <span class="nx">UserProvider</span><span class="p">({</span> <span class="nx">children</span> <span class="p">})</span> <span class="p">{</span>
  <span class="c1">// 使用useMemo确保模型实例在组件的生命周期内是单例的</span>
  <span class="kd">const</span> <span class="nx">userModel</span> <span class="o">=</span> <span class="nx">useMemo</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="k">new</span> <span class="nx">UserProfileModel</span><span class="p">({</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Guest</span><span class="dl">'</span> <span class="p">}),</span> <span class="p">[]);</span>
  
  <span class="kd">const</span> <span class="p">[</span><span class="nx">user</span><span class="p">,</span> <span class="nx">setUser</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="nx">userModel</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>

  <span class="nx">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="c1">// 同样的订阅/取消订阅逻辑</span>
    <span class="kd">const</span> <span class="nx">unsubscribe</span> <span class="o">=</span> <span class="nx">userModel</span><span class="p">.</span><span class="nx">subscribe</span><span class="p">(</span><span class="nx">setUser</span><span class="p">);</span>
    <span class="nx">userModel</span><span class="p">.</span><span class="nx">fetchData</span><span class="p">();</span>
    <span class="k">return</span> <span class="nx">unsubscribe</span><span class="p">;</span>
  <span class="p">},</span> <span class="p">[</span><span class="nx">userModel</span><span class="p">]);</span> <span class="c1">// 依赖于模型实例</span>

  <span class="c1">// 将数据和模型实例本身都传递下去</span>
  <span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">user</span><span class="p">,</span> <span class="nx">userModel</span> <span class="p">};</span>

  <span class="k">return</span> <span class="p">&lt;</span><span class="nc">UserContext</span><span class="p">.</span><span class="nc">Provider</span> <span class="na">value</span><span class="p">=</span><span class="si">{</span><span class="nx">value</span><span class="si">}</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">children</span><span class="si">}</span><span class="p">&lt;/</span><span class="nc">UserContext</span><span class="p">.</span><span class="nc">Provider</span><span class="p">&gt;;</span>
<span class="p">}</span>

<span class="c1">// 2. 创建一个自定义Hook，让消费Context变得极其简单和类型安全</span>
<span class="k">export</span> <span class="kd">function</span> <span class="nx">useUser</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">context</span> <span class="o">=</span> <span class="nx">useContext</span><span class="p">(</span><span class="nx">UserContext</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">useUser 必须在 UserProvider 内部使用</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">context</span><span class="p">;</span>
<span class="p">}</span>

<span class="cm">/*
// 用法：
// 在你的应用顶层（如 App.js）包裹组件
&lt;UserProvider&gt;
  &lt;YourApp /&gt;
&lt;/UserProvider&gt;

// 在任何子组件中
function Header() {
  const { user } = useUser();
  return &lt;h1&gt;Welcome, {user.name}&lt;/h1&gt;;
}

function ProfileEditor() {
  const { user, userModel } = useUser();
  return &lt;input value={user.name} onChange={e =&gt; userModel.updateName(e.target.value)} /&gt;;
}
*/</span>
</code></pre></div></div>

<h3 id="方案b外部状态库拥抱更强大的生态-以zustand为例">方案B：外部状态库——拥抱更强大的生态 (以Zustand为例)</h3>

<p>对于大型或非常复杂的应用，外部状态管理库（如 Zustand, Redux, Jotai）提供了更强大的功能，如更精细的性能优化、中间件支持和出色的开发者工具。</p>

<p><strong>Zustand</strong> 以其极简的API和对Hooks的天然亲和力而备受青睐，它能让模型和状态的集成变得异常优雅。</p>

<ol>
  <li>
    <p><strong>创建Store（即模型）</strong></p>

    <p>使用Zustand，你的模型和状态管理逻辑被合并到一个 <code class="language-plaintext highlighter-rouge">store</code> 中。</p>

    <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// stores/userStore.js</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">create</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">zustand</span><span class="dl">'</span><span class="p">;</span>

<span class="c1">// `create` 函数返回一个可以直接在组件中使用的Hook</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">useUserStore</span> <span class="o">=</span> <span class="nx">create</span><span class="p">((</span><span class="kd">set</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">({</span>
  <span class="c1">// State (模型的数据)</span>
  <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Guest</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">email</span><span class="p">:</span> <span class="dl">''</span><span class="p">,</span>
      
  <span class="c1">// Actions (模型的方法)</span>
  <span class="na">updateName</span><span class="p">:</span> <span class="p">(</span><span class="nx">newName</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="kd">set</span><span class="p">({</span> <span class="na">name</span><span class="p">:</span> <span class="nx">newName</span> <span class="p">}),</span>
      
  <span class="na">fetchData</span><span class="p">:</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">/api/user</span><span class="dl">'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">res</span> <span class="o">=&gt;</span> <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">());</span>
    <span class="kd">set</span><span class="p">({</span> <span class="na">name</span><span class="p">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="na">email</span><span class="p">:</span> <span class="nx">response</span><span class="p">.</span><span class="nx">email</span> <span class="p">});</span>
  <span class="p">},</span>
<span class="p">}));</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>在组件中使用</strong></p>

    <p>Zustand的魔法在于，你不再需要手动管理 <code class="language-plaintext highlighter-rouge">Context</code> 或 <code class="language-plaintext highlighter-rouge">useEffect</code> 来同步状态。</p>

    <div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// components/UserProfile.jsx</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">useUserStore</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../stores/userStore</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span> <span class="nx">useEffect</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">function</span> <span class="nx">UserProfile</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// 从store中“选择”你需要的数据和方法</span>
  <span class="c1">// Zustand会自动处理订阅，只有当被选择的状态变化时，组件才会重新渲染</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">email</span><span class="p">,</span> <span class="nx">updateName</span><span class="p">,</span> <span class="nx">fetchData</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">useUserStore</span><span class="p">();</span>

  <span class="c1">// 在组件挂载时调用action</span>
  <span class="nx">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">fetchData</span><span class="p">();</span>
  <span class="p">},</span> <span class="p">[</span><span class="nx">fetchData</span><span class="p">]);</span>

  <span class="k">return</span> <span class="p">(</span>
    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">name</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">email</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">button</span> <span class="na">onClick</span><span class="p">=</span><span class="si">{</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">updateName</span><span class="p">(</span><span class="s2">`User </span><span class="p">${</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()}</span><span class="s2">`</span><span class="p">)</span><span class="si">}</span><span class="p">&gt;</span>
        Change Name
      <span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
  <span class="p">);</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="总结你的优雅之道">总结：你的优雅之道</h2>

<p>无论你选择哪条路，通往优雅架构的原则是相通的：</p>

<ol>
  <li><strong>坚持单一数据源</strong>：让你的Class模型或Store成为状态的唯一权威。</li>
  <li><strong>保持单向数据流</strong>：UI事件调用模型方法，模型变更通知UI更新。</li>
  <li><strong>封装复杂性</strong>：将状态订阅、数据获取等副作用逻辑封装到自定义Hook (<code class="language-plaintext highlighter-rouge">useUser</code>) 或Provider (<code class="language-plaintext highlighter-rouge">UserProvider</code>)中，让UI组件保持简洁和声明式。</li>
  <li><strong><code class="language-plaintext highlighter-rouge">useEffect</code> 是同步工具</strong>：它的核心职责是同步外部系统（你的模型）和React状态，并处理好订阅和清理。</li>
  <li><strong>按需选择工具</strong>：
    <ul>
      <li><strong>组件级复杂状态</strong>：Class模型 + <code class="language-plaintext highlighter-rouge">useEffect</code> 模式清晰且无依赖。</li>
      <li><strong>中等应用全局状态</strong>：React Context + 自定义Hook是官方推荐的可靠方案。</li>
      <li><strong>大型应用全局状态</strong>：Zustand或Redux等库提供了更专业的性能和工程化支持。</li>
    </ul>
  </li>
</ol>

<p>通过遵循这些模式，你就可以在React应用中构建出逻辑清晰、易于维护、扩展性强的状态管理架构。</p>]]></content><author><name>devqin</name></author><category term="React状态管理" /><category term="React模型设计" /><category term="React模型设计" /><category term="React状态管理" /><category term="代码设计" /><summary type="html"><![CDATA[在现代前端开发中，尤其是在使用React时，我们常常遵循“先设计模型（Model），再开发业务逻辑和UI”的流程。这是一个优秀的设计模式，但它也带来了React开发中的一个核心挑战：如何优雅地将独立的业务模型与React的组件状态（State）和生命周期（Lifecycle）结合起来？]]></summary></entry><entry><title type="html">健身计划</title><link href="https://devqin.github.io/2025/fitness-by-ai.html" rel="alternate" type="text/html" title="健身计划" /><published>2025-06-19T00:00:00+00:00</published><updated>2025-06-19T00:00:00+00:00</updated><id>https://devqin.github.io/2025/fitness-by-ai</id><content type="html" xml:base="https://devqin.github.io/2025/fitness-by-ai.html"><![CDATA[<p>好的，完全没问题。</p>

<p>我将以标准文本格式为您呈现我们最终确定的完整训练计划文档，内容与我们最终完善的版本完全一致，只是不再使用特殊的Canvas卡片。</p>

<hr />

<h3 id="专属高效健身训练计划-终版"><strong>专属高效健身训练计划 (终版)</strong></h3>

<hr />

<h4 id="一-个人档案与核心目标"><strong>一、 个人档案与核心目标</strong></h4>

<ul>
  <li><strong>姓名</strong>: [您的姓名]</li>
  <li><strong>年龄</strong>: 35岁</li>
  <li><strong>基本数据</strong>: 身高178cm, 体重179斤 (约89.5kg)</li>
  <li><strong>核心目标</strong>:
    <ol>
      <li><strong>提升最大有氧适能 (VO₂ Max)</strong>: 通过系统化的骑行训练。</li>
      <li><strong>提升绝对力量与肌肉量</strong>: 通过结构化的分化力量训练。</li>
      <li><strong>优化身材与身体成分</strong>: 降低体脂，收紧线条，尤其是腰腹部。</li>
    </ol>
  </li>
  <li><strong>训练时间</strong>: 周一至周五训练，周六、周日恢复。</li>
</ul>

<hr />

<h4 id="二-训练核心原则"><strong>二、 训练核心原则</strong></h4>

<ol>
  <li><strong>强度感知 (RPE/RIR)</strong>: 大部分力量训练组，应达到“感觉还能再做1-3次就力竭”的程度 (RIR 1-3)，以此为标准来选择最适合您的重量。</li>
  <li><strong>动作质量优先</strong>: 永远将动作的规范性、完整性放在第一位，再追求重量的提升。</li>
  <li><strong>渐进超负荷</strong>: 持续进步的基石。当您能轻松完成目标组数与次数时，就需要通过增加重量、次数、组数或缩短休息来给身体新的刺激。</li>
</ol>

<hr />

<h4 id="三-公路车极化有氧训练-周一至周五"><strong>三、 公路车极化有氧训练 (周一至周五)</strong></h4>

<ul>
  <li><strong>基本定义</strong>:
    <ul>
      <li><strong>Zone 1 (恢复/耐力区)</strong>: 轻松的骑行，可以正常交谈。</li>
      <li><strong>Zone 3 (高强度区)</strong>: 非常费力的骑行，呼吸急促，无法完整说出一句话。</li>
    </ul>
  </li>
  <li><strong>每日骑行计划</strong>:
    <ul>
      <li><strong>周一 (基础恢复日)</strong>: 早/晚两次骑行，全部采用 <strong>Zone 1</strong> 强度。</li>
      <li><strong>周二 (最大摄氧量刺激日)</strong>: <strong>早上</strong>进行高强度课 [3组 x (4分钟 Zone 3 + 4分钟 Zone 1)]；<strong>晚上</strong>进行纯 <strong>Zone 1</strong> 恢复性骑行。</li>
      <li><strong>周三 (技术与耐力日)</strong>: 早/晚两次骑行，全部采用 <strong>Zone 1</strong> 强度，可加入高踏频训练。</li>
      <li><strong>周四 (神经肌肉刺激日)</strong>: <strong>早上</strong>进行高强度课 [5组 x (30秒全力冲刺 + 3分30秒 Zone 1)]；<strong>晚上</strong>进行纯 <strong>Zone 1</strong> 恢复性骑行。</li>
      <li><strong>周五 (有氧巩固日)</strong>: 早/晚两次骑行，全部采用 <strong>Zone 1</strong> 强度。</li>
    </ul>
  </li>
</ul>

<hr />

<h4 id="四-午间力量训练详解-周一至周五"><strong>四、 午间力量训练详解 (周一至周五)</strong></h4>

<ul>
  <li><strong>通用流程</strong>:
    <ol>
      <li><strong>动态热身 (5-7分钟)</strong>: 开合跳、关节活动、动态拉伸。</li>
      <li><strong>正式训练 (45-50分钟)</strong>: 严格执行下表。</li>
      <li><strong>整理放松 (5-8分钟)</strong>: 完成针对性的静态拉伸，每个动作保持 <strong>20-30秒</strong>。</li>
    </ol>
  </li>
</ul>

<p><strong>周一：胸部全面训练</strong>
| 动作 (Exercise) | 组数 (Sets) | 次数 (Reps) | 休息时间 (Rest) | 备注与指导 (Notes &amp; Guidance) |
| :— | :— | :— | :— | :— |
| <strong>杠铃卧推 (平板)</strong>&lt;br&gt;<strong>(黄金动作)</strong> | 4 | 8-10 | 90秒 | <strong>部位</strong>: 整体胸大肌、三角肌前束、肱三头肌。&lt;br&gt;<strong>目的</strong>: 上半身推力动作的王牌，建立胸部整体厚度、力量和维度的基石。 |
| <strong>上斜哑铃卧推</strong> | 3 | 10-12 | 60-75秒 | <strong>部位</strong>: 胸大肌上部、三角肌前束。&lt;br&gt;<strong>目的</strong>: 专门强化最难发展的上胸区域，让胸型更饱满、挺拔。 |
| <strong>下斜哑铃卧推</strong> | 3 | 10-12 | 60-75秒 | <strong>部位</strong>: 胸大肌下部。&lt;br&gt;<strong>目的</strong>: 专门刻画胸肌下沿的线条，改善胸部轻微下垂。 |
| <strong>哑铃飞鸟 (平板)</strong> | 3 | 12-15 | 60秒 | <strong>部位</strong>: 胸大肌（侧重中缝）。&lt;br&gt;<strong>目的</strong>: 孤立刺激胸肌，提升胸中缝的深度和肌肉泵感。 |</p>

<ul>
  <li><strong>训练后拉伸</strong>: 靠墙单臂胸肌拉伸、双手背后交握拉伸。</li>
</ul>

<p><strong>周二：背部宽度与厚度</strong>
| 动作 (Exercise) | 组数 (Sets) | 次数 (Reps) | 休息时间 (Rest) | 备注与指导 (Notes &amp; Guidance) |
| :— | :— | :— | :— | :— |
| <strong>杠铃俯身划船</strong>&lt;br&gt;<strong>(黄金动作)</strong> | 4 | 8-10 | 90秒 | <strong>部位</strong>: 整体背部肌群。&lt;br&gt;<strong>目的</strong>: 增加背部厚度的最佳动作，打造“虎背”般坚实的体态。 |
| <strong>单臂哑铃划船</strong> | 3 | 每侧10-12 | 60-75秒 | <strong>部位</strong>: 背阔肌、菱形肌。&lt;br&gt;<strong>目的</strong>: 允许更大的动作幅度，更好地孤立刺激背阔肌，改善两侧力量不平衡。 |
| <strong>反向划船</strong> | 3 | 力竭 | 60秒 | <strong>部位</strong>: 背阔肌、斜方肌、三角肌后束。&lt;br&gt;<strong>目的</strong>: 通往标准引体向上的黄金阶梯，帮助学习如何用背部发力。 |
| <strong>哑铃直臂上拉</strong> | 3 | 12-15 | 60秒 | <strong>部位</strong>: 背阔肌、前锯肌、胸肌。&lt;br&gt;<strong>目的</strong>: 经典的塑形动作，拉伸胸腔，提升背阔肌的激活度。 |</p>

<ul>
  <li><strong>训练后拉伸</strong>: 猫驼式伸展、婴儿式伸展、站姿体侧拉伸。</li>
</ul>

<p><strong>周三：腿部力量基石</strong>
| 动作 (Exercise) | 组数 (Sets) | 次数 (Reps) | 休息时间 (Rest) | 备注与指导 (Notes &amp; Guidance) |
| :— | :— | :— | :— | :— |
| <strong>杠铃深蹲</strong>&lt;br&gt;<strong>(黄金动作)</strong> | 4 | 8-10 | 90-120秒 | <strong>部位</strong>: 股四头肌、臀大肌、腘绳肌、核心。&lt;br&gt;<strong>目的</strong>: “力量训练之王”，最有效地提升全身力量、促进合成代谢激素的分泌。 |
| <strong>哑铃箭步蹲</strong> | 3 | 每侧10-12 | 60-75秒 | <strong>部位</strong>: 股四头肌、臀大肌。&lt;br&gt;<strong>目的</strong>: 优秀的单腿训练，极大考验核心稳定性与平衡能力。 |
| <strong>罗马尼亚硬拉 (RDL)</strong> | 3 | 10-12 | 75-90秒 | <strong>部位</strong>: 腘绳肌、臀大肌、下背部。&lt;br&gt;<strong>目的</strong>: 发展身体“后侧链”的关键动作，能有效提升臀线。 |
| <strong>哑铃提踵</strong> | 3 | 15-20 | 45-60秒 | <strong>部位</strong>: 腓肠肌、比目鱼肌（小腿）。&lt;br&gt;<strong>目的</strong>: 专门针对小腿的孤立训练，让下肢发展更完整。 |</p>

<ul>
  <li><strong>训练后拉伸</strong>: 站姿股四头肌拉伸、坐姿腘绳肌拉伸、“4”字臀部拉伸。</li>
</ul>

<p><strong>周四：肩部3D塑形</strong>
| 动作 (Exercise) | 组数 (Sets) | 次数 (Reps) | 休息时间 (Rest) | 备注与指导 (Notes &amp; Guidance) |
| :— | :— | :— | :— | :— |
| <strong>杠铃站姿推举</strong>&lt;br&gt;<strong>(黄金动作)</strong> | 4 | 8-10 | 90秒 | <strong>部位</strong>: 整体三角肌、肱三头肌、核心。&lt;br&gt;<strong>目的</strong>: 发展肩部力量和维度的最佳复合动作，打造“铠甲肩”。 |
| <strong>哑铃侧平举</strong> | 3 | 12-15 | 60秒 | <strong>部位</strong>: 三角肌中束。&lt;br&gt;<strong>目的</strong>: 塑造肩宽的王牌动作，“倒三角”身材的点睛之笔。 |
| <strong>俯身哑铃反向飞鸟</strong> | 3 | 12-15 | 60秒 | <strong>部位</strong>: 三角肌后束、斜方肌中下部。&lt;br&gt;<strong>目的</strong>: 改善圆肩、驼背等不良体态，维持肩关节健康。 |
| <strong>阿诺德推举</strong> | 3 | 10-12 | 75秒 | <strong>部位</strong>: 整体三角肌。&lt;br&gt;<strong>目的</strong>: 在一个动作中刺激到更多的肌纤维，提升肩部的饱满度和分离度。 |</p>

<ul>
  <li><strong>训练后拉伸</strong>: 手臂交叉胸前拉伸、肱三头肌伸展、门框拉伸。</li>
</ul>

<p><strong>周五：手臂与腹部超级组</strong>
| 动作 (Exercise) | 组数 (Sets) | 次数/时间 | 休息时间 (Rest) | 备注与指导 (Notes &amp; Guidance) |
| :— | :— | :— | :— | :— |
| <strong>超级组A</strong>&lt;br&gt;1. 哑铃弯举&lt;br&gt;2. 哑铃颈后臂屈伸 | 3 | 10-12次&lt;br&gt;10-12次 | <strong>组间休息75秒</strong>&lt;br&gt;(完成两个动作后) | <strong>部位</strong>: 肱二头肌、肱三头肌（长头）。&lt;br&gt;<strong>目的</strong>: 高效轰炸手臂屈肌和伸肌。 |
| <strong>超级组B</strong>&lt;br&gt;1. 锤式弯举&lt;br&gt;2. 俯身哑铃臂屈伸 | 3 | 10-12次&lt;br&gt;12-15次 | <strong>组间休息75秒</strong>&lt;br&gt;(完成两个动作后) | <strong>部位</strong>: 肱肌、肱桡肌、肱三头肌。&lt;br&gt;<strong>目的</strong>: 全面发展手臂厚度和分离度。 |
| <strong>核心循环</strong>&lt;br&gt;1. 死虫式&lt;br&gt;2. 仰卧抬腿&lt;br&gt;3. 俄罗斯转体&lt;br&gt;4. 平板支撑 | 3 | 每侧10-12次&lt;br&gt;15-20次&lt;br&gt;每侧15-20次&lt;br&gt;45-60秒 | <strong>轮间休息60秒</strong>&lt;br&gt;(完成四个动作后) | <strong>部位</strong>: 整体核心肌群。&lt;br&gt;<strong>目的</strong>: 打造从内到外都强大的核心，注意动作顺序。 |</p>

<ul>
  <li><strong>训练后拉伸</strong>: 肱二头肌与前臂拉伸、肱三头肌拉伸、眼镜蛇式拉伸、抱膝放松。</li>
</ul>

<hr />

<h4 id="五-进阶恢复与效果评估"><strong>五、 进阶、恢复与效果评估</strong></h4>

<ul>
  <li><strong>引体向上专项突破</strong>: 当反向划船能轻松完成3组x15次后，在背部训练日加入：
    <ol>
      <li><strong>静态悬挂</strong>: 3组，每组30-60秒。</li>
      <li><strong>离心引体向上</strong>: 3组，每组3-5次 (每次下放过程尽量超过5秒)。</li>
    </ol>
  </li>
  <li><strong>恢复日策略 (周六、周日)</strong>: 主动恢复、保证营养与睡眠。</li>
  <li><strong>训练效果评估与周期回顾 (每4周一次)</strong>:
    <ul>
      <li><strong>评估指标</strong>: 力量（核心动作重量）、耐力（力竭次数）、身体维度（体重、胸围、腰围）、主观感受。</li>
      <li><strong>计划调整</strong>: 根据评估结果进行渐进超负荷或调整。</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="附录核心训练概念解析"><strong>附录：核心训练概念解析</strong></h3>

<h4 id="1-如何找到最佳重量rpe与rir量表"><strong>1. 如何找到最佳重量：RPE与RIR量表</strong></h4>

<p>在不借助专业设备的情况下，主观感受是衡量训练强度的最佳工具。其中，<strong>RPE（Perceived Exertion，自我感知强度）</strong> 和 <strong>RIR（Reps in Reserve，储备次数）</strong> 是最实用、最科学的两种方法。</p>

<ul>
  <li><strong>RPE (自我感知强度)</strong>: 一个从1到10的评分，1代表毫不费力，10代表你拼尽全力只能完成1次的重量。</li>
  <li><strong>RIR (储备次数)</strong>: 指完成一组后，你感觉自己“还能标准地再做几次”。RIR 2就代表“我还能再做2次”。</li>
</ul>

<p><strong>RPE 与 RIR 对应关系及训练效果</strong></p>

<table>
  <thead>
    <tr>
      <th style="text-align: left">RPE 等级</th>
      <th style="text-align: left">RIR 储备次数</th>
      <th style="text-align: left">描述</th>
      <th style="text-align: left">主要训练效果</th>
      <th style="text-align: left">本计划应用场景</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left"><strong>10</strong></td>
      <td style="text-align: left">0</td>
      <td style="text-align: left">真正的力竭，无法再多做一次</td>
      <td style="text-align: left">极限力量、神经适应</td>
      <td style="text-align: left">仅在极少数情况或测试时使用</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>9</strong></td>
      <td style="text-align: left">1</td>
      <td style="text-align: left">非常困难，但确信还能再做1次</td>
      <td style="text-align: left"><strong>力量与肌肉增长 (最佳)</strong></td>
      <td style="text-align: left">大部分复合动作的主要训练组</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>8</strong></td>
      <td style="text-align: left">2</td>
      <td style="text-align: left">困难，感觉还能再做2次</td>
      <td style="text-align: left"><strong>肌肉增长与力量 (最佳)</strong></td>
      <td style="text-align: left">几乎所有动作的主要训练组</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>7</strong></td>
      <td style="text-align: left">3</td>
      <td style="text-align: left">中等偏上，感觉还能再做3次</td>
      <td style="text-align: left">肌肉增长、技术练习</td>
      <td style="text-align: left">孤立动作、技术练习组</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>5-6</strong></td>
      <td style="text-align: left">4-6</td>
      <td style="text-align: left">中等强度，比较轻松</td>
      <td style="text-align: left">技术练习、速度训练</td>
      <td style="text-align: left">热身组</td>
    </tr>
    <tr>
      <td style="text-align: left"><strong>1-4</strong></td>
      <td style="text-align: left">&gt;6</td>
      <td style="text-align: left">非常轻松</td>
      <td style="text-align: left">动态恢复、热身</td>
      <td style="text-align: left">热身、放松</td>
    </tr>
  </tbody>
</table>

<p><strong>【实战应用指南】</strong></p>

<p>对于本计划中大部分 <strong>8-12次</strong> 的目标区间，您的目标是让每一组的结束点都落在 <strong>RPE 7-9</strong> 或 <strong>RIR 1-3</strong>。</p>

<ol>
  <li><strong>选择初始重量</strong>: 凭经验选择一个你认为能做12次左右的重量。</li>
  <li><strong>执行第一组</strong>:
    <ul>
      <li>如果你轻松完成了12次，且感觉还能做4次以上 (RIR ≥ 4)，说明<strong>重量太轻</strong>，下一组请果断加重。</li>
      <li>如果你完成了10次，感觉拼尽全力也只能再做1-2次 (RIR 1-2)，那么这<strong>就是你的理想重量</strong>！</li>
      <li>如果你做了不到8次就无法保持标准动作，说明<strong>重量太重</strong>，下一组请减重。</li>
    </ul>
  </li>
  <li><strong>动态调整</strong>: 状态每天都不同，请根据每一组的真实感受来微调下一组的重量，而不是僵化地使用同一个重量。</li>
</ol>

<h4 id="2-渐进超负荷持续进步的秘诀"><strong>2. 渐进超负荷：持续进步的秘诀</strong></h4>

<p>肌肉和力量的增长，源于身体对“所承受的、超出过往习惯的压力”所做出的适应性反应。如果一直用同样的重量、做同样的次数，身体习惯后就不会再有进步。渐进超负荷就是系统性地增加训练压力，逼迫身体持续变强。</p>

<p><strong>【五种主要的渐进方法】</strong></p>

<ol>
  <li><strong>增加重量 (最核心)</strong>: 这是最直接的方式。当你能在目标次数区间内轻松完成所有组数时（例如，用20kg哑铃做卧推，3组12次都能以RIR 2-3完成），下一次训练就应该尝试22.5kg。</li>
  <li><strong>增加次数</strong>: 在重量不变的情况下，提升完成的次数。例如，上周用20kg做了3组x10次，这周的目标就是用同样的重量完成3组x11次或12次。</li>
  <li><strong>增加组数</strong>: 在重量和次数不变的情况下，增加训练组数。例如，从3组x12次增加到4组x12次。这能有效提升总训练量。</li>
  <li><strong>缩短休息时间</strong>: 在其他变量不变的情况下，缩短组间休息时间。例如，将休息时间从90秒缩短到75秒，这会增加训练密度和心血管压力。</li>
  <li><strong>提升动作质量</strong>: 这是一种“无形”的进步。例如，用同样的重量和次数，但动作过程更稳定、控制得更好、念动一致更强，这同样是一种有效的超负荷。</li>
</ol>

<p>在您的训练中，请优先专注于<strong>前两种方法</strong>，当它们遇到瓶颈时，再考虑后三种。</p>]]></content><author><name>devqin</name></author><category term="健身计划" /><category term="fitness,力量训练" /><summary type="html"><![CDATA[好的，完全没问题。]]></summary></entry><entry><title type="html">前端学习AI路线图</title><link href="https://devqin.github.io/2025/learn-roadmap.html" rel="alternate" type="text/html" title="前端学习AI路线图" /><published>2025-06-12T00:00:00+00:00</published><updated>2025-06-12T00:00:00+00:00</updated><id>https://devqin.github.io/2025/learn-roadmap</id><content type="html" xml:base="https://devqin.github.io/2025/learn-roadmap.html"><![CDATA[<p>这个计划分为两个核心阶段，共计10周。每周都有明确的目标、行动任务和可验证的成果。</p>

<hr />

<h3 id="第一阶段ai核心认知与基础工具掌握-4周"><strong>第一阶段：AI核心认知与基础工具掌握 (4周)</strong></h3>

<p><strong>目标：</strong> 不再是AI领域的“门外汉”，能够流利地使用AI领域的“黑话”，并掌握AI开发最基础的“水电煤”——Python和数据处理。</p>

<h4 id="第1周破冰与宏观认知"><strong>第1周：破冰与宏观认知</strong></h4>
<ul>
  <li><strong>本周目标：</strong> 彻底揭开AI的神秘面纱，用你最熟悉的方式（写代码）建立与AI的第一次亲密接触。</li>
  <li><strong>行动任务：</strong>
    <ol>
      <li><strong>学习：</strong> 观看吴恩达的 <strong>《AI For Everyone》</strong> 课程（非常快，几小时就能看完）。重点是理解AI能做什么、AI项目的生命周期是怎样的。</li>
      <li><strong>实践：</strong>
        <ul>
          <li>注册一个OpenAI账号，获取API Key。</li>
          <li><strong>不动用Python！</strong> 直接在你熟悉的前端环境里（比如一个简单的HTML页面或React/Vue组件），使用 <code class="language-plaintext highlighter-rouge">fetch</code> API 调用OpenAI的Chat Completions接口。</li>
          <li><strong>目标代码示例 (JavaScript):</strong>
            <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">// 这是一个可以直接在浏览器开发者工具中运行的简单示例</span>
  <span class="c1">// 记得将 'YOUR_OPENAI_API_KEY' 替换成你自己的Key</span>
  <span class="kd">const</span> <span class="nx">apiKey</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">YOUR_OPENAI_API_KEY</span><span class="dl">'</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">userMessage</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">你好，请用一句话介绍一下什么是大语言模型。</span><span class="dl">'</span><span class="p">;</span>

  <span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">https://api.openai.com/v1/chat/completions</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
    <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
      <span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span><span class="p">,</span>
      <span class="dl">'</span><span class="s1">Authorization</span><span class="dl">'</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="nx">apiKey</span><span class="p">}</span><span class="s2">`</span> <span class="c1">// 设置认证头</span>
    <span class="p">},</span>
    <span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span>
      <span class="na">model</span><span class="p">:</span> <span class="dl">'</span><span class="s1">gpt-3.5-turbo</span><span class="dl">'</span><span class="p">,</span> <span class="c1">// 使用性价比高的模型</span>
      <span class="na">messages</span><span class="p">:</span> <span class="p">[{</span> <span class="na">role</span><span class="p">:</span> <span class="dl">'</span><span class="s1">user</span><span class="dl">'</span><span class="p">,</span> <span class="na">content</span><span class="p">:</span> <span class="nx">userMessage</span> <span class="p">}]</span> <span class="c1">// 发送用户消息</span>
    <span class="p">})</span>
  <span class="p">})</span>
  <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=&gt;</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">())</span>
  <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">data</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="c1">// 打印出AI的回复内容</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">AI的回复:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">choices</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">message</span><span class="p">.</span><span class="nx">content</span><span class="p">);</span>
  <span class="p">})</span>
  <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">error</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">出错了:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">));</span>
</code></pre></div>            </div>
          </li>
        </ul>
      </li>
    </ol>
  </li>
  <li><strong>可验证成果：</strong> <strong>成功运行一个能与ChatGPT对话的简单网页。</strong> 这将给你巨大的信心：AI应用开发，你已经入门了。</li>
</ul>

<h4 id="第2周掌握ai世界的普通话python"><strong>第2周：掌握AI世界的“普通话”——Python</strong></h4>
<ul>
  <li><strong>本周目标：</strong> 快速上手Python，并掌握AI领域最常用的两个数据处理库。</li>
  <li><strong>行动任务：</strong>
    <ol>
      <li><strong>学习：</strong> 如果你对Python完全不熟，花半天时间过一遍Python的基础语法（例如廖雪峰的Python教程）。你是有经验的程序员，重点关注语法差异即可。</li>
      <li><strong>实践：</strong> 动手学习 <strong>NumPy</strong> 和 <strong>Pandas</strong>。这是数据处理的基石。
        <ul>
          <li>在Jupyter Notebook环境中，学习用Pandas加载一个CSV文件（可以找一些公开数据集，比如泰坦尼克号乘客数据）。</li>
          <li>练习使用Pandas进行数据查看（<code class="language-plaintext highlighter-rouge">.head()</code>, <code class="language-plaintext highlighter-rouge">.describe()</code>）、数据筛选（按条件选择行或列）、处理缺失值（<code class="language-plaintext highlighter-rouge">.fillna()</code>）。</li>
        </ul>
      </li>
    </ol>
  </li>
  <li><strong>可验证成果：</strong> <strong>写出一个Python脚本</strong>，该脚本能读取一个CSV文件，并打印出数据的基本统计信息和按特定条件筛选后的结果。</li>
</ul>

<h4 id="第3周机器学习核心概念速览"><strong>第3周：机器学习核心概念速览</strong></h4>
<ul>
  <li><strong>本周目标：</strong> 理解机器学习的基本工作原理，能向别人解释清楚模型的“训练”和“推理”是怎么回事。</li>
  <li><strong>行动任务：</strong>
    <ol>
      <li><strong>学习：</strong> 开始学习吴恩达的 <strong>《Machine Learning Specialization》</strong> (新版) 的第一门课程。</li>
      <li><strong>聚焦概念：</strong> 不需要深究数学公式，但必须理解以下概念：
        <ul>
          <li>监督学习 vs 无监督学习</li>
          <li>分类 vs 回归</li>
          <li>训练集、验证集、测试集的作用</li>
          <li>什么是“过拟合”和“欠拟合”</li>
        </ul>
      </li>
    </ol>
  </li>
  <li><strong>可验证成果：</strong> 能够用自己的话，清晰地向一个非技术人员解释“我们是如何训练一个能识别猫和狗的模型的”。</li>
</ul>

<h4 id="第4周聚焦llm现代ai应用的核心"><strong>第4周：聚焦LLM——现代AI应用的核心</strong></h4>
<ul>
  <li><strong>本周目标：</strong> 深入了解大语言模型（LLM）的技术栈，为开发高级AI应用做准备。</li>
  <li><strong>行动任务：</strong>
    <ol>
      <li><strong>学习：</strong>
        <ul>
          <li><strong>Prompt Engineering</strong>: 学习如何写出高效的提示词。阅读OpenAI官方的Prompt Engineering指南。</li>
          <li><strong>Embeddings</strong>: 理解“嵌入”是什么。它就是把文字变成向量（一串数字），是实现“语义搜索”和“知识库问答”的基石。可以调用OpenAI的Embeddings API，看看不同句子的向量有何异同。</li>
          <li><strong>RAG (检索增强生成)</strong>: 这是目前最主流的LLM应用模式。理解其原理：当用户提问时，程序先从一个专门的知识库（Vector DB）中检索最相关的信息，然后把这些信息连同用户的问题一起发给LLM，让LLM基于这些信息来回答。</li>
        </ul>
      </li>
    </ol>
  </li>
  <li><strong>可验证成果：</strong> 能够设计一套用于特定任务（例如“帮我总结这段文字”）的、包含多个示例（Few-shot）的复杂Prompt。</li>
</ul>

<hr />

<h3 id="第二阶段核心工具与全栈ai应用实战-6周"><strong>第二阶段：核心工具与全栈AI应用实战 (6周)</strong></h3>

<p><strong>目标：</strong> 亲手打造一个完整的、具备实用价值的全栈AI应用，形成你的核心作品集项目。我们将做一个<strong>“个人知识库问答机器人”</strong>。</p>

<h4 id="第5-6周构建ai应用的大脑后端服务"><strong>第5-6周：构建AI应用的“大脑”——后端服务</strong></h4>
<ul>
  <li><strong>本周目标：</strong> 使用现代Python框架，将AI能力封装成一个稳定、可调用的后端API。</li>
  <li><strong>行动任务：</strong>
    <ol>
      <li><strong>学习工具：</strong>
        <ul>
          <li><strong>FastAPI:</strong> 一个高性能的Python Web框架，非常适合用来写API。学习它的基本用法。</li>
          <li><strong>LangChain:</strong> AI应用的“瑞士军刀”。学习它的核心概念：<code class="language-plaintext highlighter-rouge">LLMChain</code>, <code class="language-plaintext highlighter-rouge">PromptTemplate</code>。</li>
        </ul>
      </li>
      <li><strong>实践：</strong>
        <ul>
          <li>用FastAPI创建一个简单的Web服务器。</li>
          <li>集成LangChain，创建一个API端点 <code class="language-plaintext highlighter-rouge">/chat</code>，它接收用户的问题，然后通过LangChain调用LLM（如GPT-3.5）并返回答案。</li>
          <li><strong>目标代码示例 (Python/FastAPI/LangChain):</strong>
            <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1"># main.py - 一个简单的AI后端服务
</span>  <span class="kn">from</span> <span class="nn">fastapi</span> <span class="kn">import</span> <span class="n">FastAPI</span>
  <span class="kn">from</span> <span class="nn">langchain_openai</span> <span class="kn">import</span> <span class="n">ChatOpenAI</span>
  <span class="kn">from</span> <span class="nn">langchain.prompts</span> <span class="kn">import</span> <span class="n">ChatPromptTemplate</span>
  <span class="kn">from</span> <span class="nn">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span>
  <span class="kn">import</span> <span class="nn">os</span>

  <span class="c1"># 设置你的API Key为环境变量
</span>  <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="s">'OPENAI_API_KEY'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'YOUR_OPENAI_API_KEY'</span>

  <span class="c1"># 1. 初始化FastAPI应用
</span>  <span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">()</span>

  <span class="c1"># 2. 定义请求体模型
</span>  <span class="k">class</span> <span class="nc">UserRequest</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
      <span class="n">question</span><span class="p">:</span> <span class="nb">str</span>

  <span class="c1"># 3. 初始化LLM模型
</span>  <span class="n">llm</span> <span class="o">=</span> <span class="n">ChatOpenAI</span><span class="p">(</span><span class="n">model</span><span class="o">=</span><span class="s">"gpt-3.5-turbo"</span><span class="p">)</span>

  <span class="c1"># 4. 创建Prompt模板
</span>  <span class="n">prompt</span> <span class="o">=</span> <span class="n">ChatPromptTemplate</span><span class="p">.</span><span class="n">from_messages</span><span class="p">([</span>
      <span class="p">(</span><span class="s">"system"</span><span class="p">,</span> <span class="s">"你是一个乐于助人的AI助手。"</span><span class="p">),</span>
      <span class="p">(</span><span class="s">"user"</span><span class="p">,</span> <span class="s">"{input}"</span><span class="p">)</span>
  <span class="p">])</span>

  <span class="c1"># 5. 创建一个简单的调用链
</span>  <span class="n">chain</span> <span class="o">=</span> <span class="n">prompt</span> <span class="o">|</span> <span class="n">llm</span>

  <span class="c1"># 6. 创建API端点
</span>  <span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"/chat"</span><span class="p">)</span>
  <span class="k">def</span> <span class="nf">chat_endpoint</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">UserRequest</span><span class="p">):</span>
      <span class="c1"># 调用chain并传入用户的输入
</span>      <span class="n">response</span> <span class="o">=</span> <span class="n">chain</span><span class="p">.</span><span class="n">invoke</span><span class="p">({</span><span class="s">"input"</span><span class="p">:</span> <span class="n">request</span><span class="p">.</span><span class="n">question</span><span class="p">})</span>
      <span class="k">return</span> <span class="p">{</span><span class="s">"answer"</span><span class="p">:</span> <span class="n">response</span><span class="p">.</span><span class="n">content</span><span class="p">}</span>
</code></pre></div>            </div>
          </li>
        </ul>
      </li>
    </ol>
  </li>
  <li><strong>可验证成果：</strong> 一个可以通过Postman或curl工具访问的、功能完备的AI聊天后端服务。</li>
</ul>

<h4 id="第7-8周实现rag让ai学习你的私有数据"><strong>第7-8周：实现RAG——让AI学习你的私有数据</strong></h4>
<ul>
  <li><strong>本周目标：</strong> 实现RAG流程，让你的应用能根据你上传的文档（如PDF, TXT）来回答问题。</li>
  <li><strong>行动任务：</strong>
    <ol>
      <li><strong>学习工具：</strong>
        <ul>
          <li><strong>Vector Store (向量数据库):</strong> 学习一个简单的内存向量数据库，如 <strong>ChromaDB</strong> 或 <strong>FAISS</strong>。</li>
          <li><strong>Document Loaders &amp; Text Splitters:</strong> 学习LangChain如何加载文档并将其切割成小块。</li>
        </ul>
      </li>
      <li><strong>实践：</strong>
        <ul>
          <li>在你的FastAPI应用中增加一个 <code class="language-plaintext highlighter-rouge">/upload</code> 端点，用于接收用户上传的文档。</li>
          <li>后端接收到文档后，使用LangChain将其加载、切块、计算Embeddings，并存入ChromaDB。</li>
          <li>改造 <code class="language-plaintext highlighter-rouge">/chat</code> 端点，使其遵循RAG流程：接收问题 -&gt; 从ChromaDB检索相关文档块 -&gt; 将文档块和问题组合成新的Prompt -&gt; 发给LLM -&gt; 返回答案。</li>
        </ul>
      </li>
    </ol>
  </li>
  <li><strong>可验证成果：</strong> 你的后端服务现在是一个真正的“知识库问答”引擎了。你可以上传一篇公司介绍PDF，然后问它“我们公司的核心价值观是什么？”并得到准确回答。</li>
</ul>

<h4 id="第9周发挥你的优势打造精美前端"><strong>第9周：发挥你的优势——打造精美前端</strong></h4>
<ul>
  <li><strong>本周目标：</strong> 利用你最擅长的技术，为你的AI后端制作一个用户友好的前端界面。</li>
  <li><strong>行动任务：</strong>
    <ol>
      <li><strong>实践：</strong>
        <ul>
          <li>使用你最熟悉的框架（React, Vue, Svelte等）创建一个新的前端项目。</li>
          <li>设计一个聊天界面，包含输入框、发送按钮和聊天记录展示区。</li>
          <li>调用你之前用FastAPI创建的后端API。</li>
          <li><strong>加分项：</strong> 实现“流式响应”（Streaming Response），让AI的回答像打字机一样一个字一个字地显示出来，极大提升用户体验。这需要后端FastAPI和前端fetch做相应改造。</li>
        </ul>
      </li>
    </ol>
  </li>
  <li><strong>可验证成果：</strong> 一个完整、流畅、体验优秀的全栈AI问答应用。</li>
</ul>

<h4 id="第10周部署与作品集收尾"><strong>第10周：部署与作品集收尾</strong></h4>
<ul>
  <li><strong>本周目标：</strong> 学会如何将你的AI应用打包和部署，并将其整理成一个高质量的作品集项目。</li>
  <li><strong>行动任务：</strong>
    <ol>
      <li><strong>学习工具：</strong>
        <ul>
          <li><strong>Docker:</strong> 学习为你的FastAPI后端和前端应用编写 <code class="language-plaintext highlighter-rouge">Dockerfile</code>。</li>
          <li><strong>Docker Compose:</strong> 学习如何使用 <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> 文件一键启动你的整个应用（前端服务 + 后端服务）。</li>
        </ul>
      </li>
      <li><strong>实践：</strong>
        <ul>
          <li>将你的全栈应用Docker化。</li>
          <li>在你的GitHub上创建一个新Repo，上传所有代码，并写一个清晰的 <code class="language-plaintext highlighter-rouge">README.md</code> 文件，包含项目介绍、技术栈、如何运行、以及应用截图或GIF动图。</li>
        </ul>
      </li>
    </ol>
  </li>
  <li><strong>可验证成果：</strong> <strong>一个可以通过 <code class="language-plaintext highlighter-rouge">docker-compose up</code> 命令在任何机器上运行的AI应用，以及一个能充分展示你能力的GitHub项目。</strong></li>
</ul>

<hr />

<h3 id="完成这两个阶段后你将具备的能力"><strong>完成这两个阶段后，你将具备的能力：</strong></h3>

<ul>
  <li><strong>技术硬实力：</strong> 你将拥有一个可以<strong>在面试中从头到尾讲清楚</strong>的全栈AI项目。这不仅仅是一个调用API的玩具，它涵盖了数据处理、RAG、后端服务、前端交互和容器化部署的全流程。</li>
  <li><strong>职业转换资本：</strong> 你的简历上将不再只有前端经验，而是会增加一个亮眼的<strong>“AI应用开发工程师”</strong>的项目经历。你可以自信地说：“我独立设计并开发了一个基于LLM的知识库问答系统，技术栈包括Python, FastAPI, LangChain, Docker以及React/Vue。”</li>
  <li><strong>面试自信心：</strong> 你能流利地讨论Prompt Engineering, RAG, Embeddings等核心概念，并能解释你在项目中是如何应用它们的。这足以证明你不是浅尝辄止，而是具备了深度参与AI项目开发的能力。</li>
</ul>]]></content><author><name>devqin</name></author><category term="AI" /><category term="AI" /><summary type="html"><![CDATA[这个计划分为两个核心阶段，共计10周。每周都有明确的目标、行动任务和可验证的成果。]]></summary></entry><entry><title type="html">如何进行沟通</title><link href="https://devqin.github.io/2021/how-to-communication.html" rel="alternate" type="text/html" title="如何进行沟通" /><published>2021-07-20T00:00:00+00:00</published><updated>2021-07-20T00:00:00+00:00</updated><id>https://devqin.github.io/2021/how-to-communication</id><content type="html" xml:base="https://devqin.github.io/2021/how-to-communication.html"><![CDATA[<p>沟通能力是职场中的一种关键能力。因为你需要不断的推销自己的想法，不停的和老板沟通汇报，和同事沟通协作，而沟通质量的好坏也直接决定了自己的想法能不能被执行，老板能不能 Get 到你汇报的重点，
同事能不能理解你的意思并产生协作。而绩效和信任也在每一次沟通中慢慢成型，往往会沟通的人总会工作做的好，也能够在汇报的时候被看到。</p>

<p>既然沟通这么重要，怎么样开始这项能力的提高呢？是否是掌握了一些方法就能够极大的提高自己的沟通能力呢？</p>

<p>我觉得，沟通的方法固然重要，我们看一些沟通表达的书可能就能够掌握一些有用的方法，但沟通并没有这么简单，我们还应该找到一些底层的逻辑，来处理各种情况下的沟通。
恰好，最近在一期播客中听到黄执中对沟通能力底层的三个重点的讲解：</p>

<p> 
第一，没有人喜欢被改变。对于所有人而言，被说服了，就等于被打败了。每一个人的脑子里，既有的看法、观念或立场，都是过去生活经验当中无数偏好与选择的结果。改变的意思就是要告诉这个人，过去的选择、偏好统统要被修正。
 </p>

<p>第二，没有人喜欢不知情。我们在说服、改变、想要影响别人的时候，我们总是觉得让对方知道的越少越好，但实际上，没有人喜欢不知情。
 </p>

<p>第三，所有人都希望有退路，除了让他有选择权、让他知道完整的事实，最棒的一种说法是对方可以随时反悔，这是世界上最有说服力的一句话。</p>

<p>接下来半年，不断学习沟通方法的同时，也把方法不断的带入到这三个底层逻辑中，来不断提高自己的沟通能力。</p>]]></content><author><name>devqin</name></author><category term="沟通" /><category term="如何进行沟通" /><summary type="html"><![CDATA[沟通能力是职场中的一种关键能力。因为你需要不断的推销自己的想法，不停的和老板沟通汇报，和同事沟通协作，而沟通质量的好坏也直接决定了自己的想法能不能被执行，老板能不能 Get 到你汇报的重点， 同事能不能理解你的意思并产生协作。而绩效和信任也在每一次沟通中慢慢成型，往往会沟通的人总会工作做的好，也能够在汇报的时候被看到。]]></summary></entry><entry><title type="html">从图景说起</title><link href="https://devqin.github.io/2020/mental-model.html" rel="alternate" type="text/html" title="从图景说起" /><published>2020-12-24T00:00:00+00:00</published><updated>2020-12-24T00:00:00+00:00</updated><id>https://devqin.github.io/2020/mental-model</id><content type="html" xml:base="https://devqin.github.io/2020/mental-model.html"><![CDATA[<blockquote>
  <p>大家一定要对自己生活的小世界发生兴趣，有意识地用自己的语言把自己的生活讲出来，做一个独立的叙述——也不用分析，就是叙述。-项飙,吴琦. 《把自己作为方法》</p>
</blockquote>

<blockquote>
  <p>比如说做年糕，他会把从浸水、攒米到火候掌握很系统地讲出来，能够把其中的道理勾勒出来，事情和事情的联系也就清晰起来，形成一个图景。“图景”这个概念很重要，“理论”在拉丁文里就是“图景”的意思，给出一个理论，就是给出一个世界的图景。-项飙,吴琦. 《把自己作为方法》</p>
</blockquote>

<blockquote>
  <p>理论其实不是给出判断，而是给世界一个精确的图景，同时在背后透出未来可能的图景。-项飙,吴琦. 《把自己作为方法》</p>
</blockquote>

<p>在读这章节的时候佩服于项飙的敏锐，能够感受到周围的小世界，在更高层次对小世界进行抽象。</p>

<p>同时也发现自己一直以来对于建立技术知识体系的理解是过于片面的。在技术学习中过于强调点和点的联系，而又把点当作点，忽视这些点在系统中的联系，也忽视了在更大的背景下的，比如说历史对技术发展的影响。</p>

<p>举例来说，<code class="language-plaintext highlighter-rouge">let</code> 和 <code class="language-plaintext highlighter-rouge">const</code> 在学习中过于强调它们做了什么，产生了什么影响，和以前的 <code class="language-plaintext highlighter-rouge">var</code> 有什么区别，但这还不够，因为这并不能勾勒出一个清晰的图景：虽然建立了联系也进行了对比，但背景原因是什么？为什么会是这样的实现。</p>

<p>如果在勾勒这一块知识点的局部图景时再加上这些知识：Javascript起初作为脚本语言，不能严格保证加载的代码的执行顺序，而后来随着项目代码越来越多，项目变得越来越不容易维护，需要使用模块化的思想来使项目的复杂度降低，而模块化最重要的就是变量的作用范围，于是就有了 <code class="language-plaintext highlighter-rouge">let</code> 和 <code class="language-plaintext highlighter-rouge">const</code>这样的设计。</p>

<p>这样才算是勾勒了一个完整的图景，也就是要建立的技术知识体系：“概括现在”和“透出未来”。</p>]]></content><author><name>devqin</name></author><category term="知识体系" /><category term="项飙,吴琦-把自己作为方法" /><summary type="html"><![CDATA[大家一定要对自己生活的小世界发生兴趣，有意识地用自己的语言把自己的生活讲出来，做一个独立的叙述——也不用分析，就是叙述。-项飙,吴琦. 《把自己作为方法》]]></summary></entry></feed>