<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>技术 &#8211; 萧邦的博客</title>
	<atom:link href="https://www.zlhlab.top/category/skill/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.zlhlab.top</link>
	<description>If not me who, If not now when</description>
	<lastBuildDate>Mon, 03 Aug 2026 06:29:01 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.6</generator>
	<item>
		<title>前端乾坤与模块联邦的用法及区别</title>
		<link>https://www.zlhlab.top/%e5%89%8d%e7%ab%af%e4%b9%be%e5%9d%a4%e4%b8%8e%e6%a8%a1%e5%9d%97%e8%81%94%e9%82%a6%e7%9a%84%e7%94%a8%e6%b3%95%e5%8f%8a%e5%8c%ba%e5%88%ab/</link>
		
		<dc:creator><![CDATA[萧邦]]></dc:creator>
		<pubDate>Mon, 03 Aug 2026 06:29:01 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">https://www.zlhlab.top/?p=847</guid>

					<description><![CDATA[概述 乾坤（Qiankun）和模块联邦（Module Federation）是前端微服务架构中常用的两种技术方<div class="more-link">
				 <a href="https://www.zlhlab.top/%e5%89%8d%e7%ab%af%e4%b9%be%e5%9d%a4%e4%b8%8e%e6%a8%a1%e5%9d%97%e8%81%94%e9%82%a6%e7%9a%84%e7%94%a8%e6%b3%95%e5%8f%8a%e5%8c%ba%e5%88%ab/" class="link-btn theme-btn"><span>Read More </span> <i class="fa fa-caret-right"></i></a>
			</div>]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">概述</h2>



<p>乾坤（Qiankun）和模块联邦（Module Federation）是前端微服务架构中常用的两种技术方案，用于实现模块化开发、动态加载和跨应用共享代码。两者在微前端场景下有各自的特点和适用场景。本文将详细介绍它们的用法、核心概念以及主要区别。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">一、乾坤（Qiankun）</h2>



<h3 class="wp-block-heading">1. 简介</h3>



<p>乾坤是基于 single-spa 的微前端框架，由蚂蚁金服开源，旨在提供开箱即用的微前端解决方案。它通过主应用和子应用的分离，动态加载子应用，支持多种前端框架（如 React、Vue、Angular 等）。</p>



<h3 class="wp-block-heading">2. 核心概念</h3>



<ul class="wp-block-list">
<li><strong>主应用（Main App）</strong>：负责加载和管理子应用，控制路由和生命周期。</li>



<li><strong>子应用（Sub App）</strong>：独立的微前端应用，可以使用不同技术栈，动态加载到主应用中。</li>



<li><strong>生命周期</strong>：乾坤定义了子应用的生命周期钩子，包括 <code>bootstrap</code>、<code>mount</code> 和 <code>unmount</code>。</li>



<li><strong>沙箱机制</strong>：通过 JS 沙箱（ProxySandbox 或 SnapshotSandbox）隔离子应用的全局变量和样式，防止冲突。</li>



<li><strong>通信机制</strong>：通过全局状态管理（如 <code>initGlobalState</code>）或事件机制实现主子应用通信。</li>
</ul>



<h3 class="wp-block-heading">3. 用法</h3>



<h4 class="wp-block-heading">主应用配置</h4>



<p>主应用需引入乾坤并注册子应用：</p>



<pre class="wp-block-code"><code>// main.js
import { registerMicroApps, start } from 'qiankun';

const apps = &#91;
  {
    name: 'reactApp',
    entry: '//localhost:3001', // 子应用入口
    container: '#subapp-container', // 渲染容器
    activeRule: '/react', // 路由匹配规则
  },
  {
    name: 'vueApp',
    entry: '//localhost:3002',
    container: '#subapp-container',
    activeRule: '/vue',
  },
];

registerMicroApps(apps);
start({
  prefetch: true, // 预加载子应用资源
  sandbox: { experimentalStyleIsolation: true }, // 开启样式隔离
});
</code></pre>



<h4 class="wp-block-heading">子应用配置</h4>



<p>子应用需暴露生命周期钩子：</p>



<pre class="wp-block-code"><code>// 子应用入口文件
export async function bootstrap() {
  console.log('子应用初始化');
}

export async function mount(props) {
  console.log('子应用挂载', props);
  // 渲染逻辑
}

export async function unmount() {
  console.log('子应用卸载');
  // 清理逻辑
}
</code></pre>



<h4 class="wp-block-heading">通信</h4>



<p>主应用通过 <code>initGlobalState</code> 初始化全局状态：</p>



<pre class="wp-block-code"><code>import { initGlobalState } from 'qiankun';

const state = { user: 'admin' };
const actions = initGlobalState(state);

actions.onGlobalStateChange((newState, prev) =&gt; {
  console.log('全局状态变更', newState, prev);
});
</code></pre>



<p>子应用通过 <code>props</code> 接收状态：</p>



<pre class="wp-block-code"><code>export async function mount(props) {
  props.onGlobalStateChange((state, prev) =&gt; {
    console.log('子应用接收状态', state);
  });
}
</code></pre>



<h3 class="wp-block-heading">4. 优点</h3>



<ul class="wp-block-list">
<li><strong>开箱即用</strong>：提供完整的微前端解决方案，集成路由、沙箱和通信机制。</li>



<li><strong>技术栈无关</strong>：支持多种框架，子应用无需大幅修改。</li>



<li><strong>沙箱隔离</strong>：内置 JS 和 CSS 隔离机制，减少冲突。</li>



<li><strong>生态丰富</strong>：与 single-spa 生态兼容，社区支持完善。</li>
</ul>



<h3 class="wp-block-heading">5. 缺点</h3>



<ul class="wp-block-list">
<li><strong>侵入性</strong>：子应用需适配乾坤的生命周期，略有改造成本。</li>



<li><strong>性能开销</strong>：沙箱机制可能增加性能消耗，尤其在多子应用场景。</li>



<li><strong>复杂场景适配</strong>：对复杂路由或深度集成场景需额外配置。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">二、模块联邦（Module Federation）</h2>



<h3 class="wp-block-heading">1. 简介</h3>



<p>模块联邦是 Webpack 5 引入的功能，允许在构建时将多个独立构建的应用模块化共享代码，实现动态加载和运行时共享。它更偏向于模块级别的复用，而不是完整的应用加载。</p>



<h3 class="wp-block-heading">2. 核心概念</h3>



<ul class="wp-block-list">
<li><strong>主机（Host）</strong>：加载远程模块的应用。</li>



<li><strong>远程（Remote）</strong>：暴露模块供其他应用使用的应用。</li>



<li><strong>模块暴露</strong>：通过 <code>exposes</code> 配置暴露模块，供主机消费。</li>



<li><strong>模块消费</strong>：通过 <code>remotes</code> 配置引用远程模块。</li>



<li><strong>共享模块</strong>：通过 <code>shared</code> 配置共享依赖（如 React、Vue），减少重复加载。</li>
</ul>



<h3 class="wp-block-heading">3. 用法</h3>



<h4 class="wp-block-heading">主机应用配置</h4>



<p>在 <code>webpack.config.js</code> 中配置：</p>



<pre class="wp-block-code"><code>const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  plugins: &#91;
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js',
      },
      shared: {
        react: { singleton: true },
        'react-dom': { singleton: true },
      },
    }),
  ],
};
</code></pre>



<h4 class="wp-block-heading">远程应用配置</h4>



<p>在远程应用的 <code>webpack.config.js</code> 中暴露模块：</p>



<pre class="wp-block-code"><code>module.exports = {
  plugins: &#91;
    new ModuleFederationPlugin({
      name: 'remoteApp',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/Button',
      },
      shared: {
        react: { singleton: true },
        'react-dom': { singleton: true },
      },
    }),
  ],
};
</code></pre>



<h4 class="wp-block-heading">使用远程模块</h4>



<p>在主机应用中动态加载远程模块：</p>



<pre class="wp-block-code"><code>import React, { lazy, Suspense } from 'react';

const RemoteButton = lazy(() =&gt; import('remoteApp/Button'));

function App() {
  return (
    &lt;Suspense fallback="Loading..."&gt;
      &lt;RemoteButton /&gt;
    &lt;/Suspense&gt;
  );
}
</code></pre>



<h3 class="wp-block-heading">4. 优点</h3>



<ul class="wp-block-list">
<li><strong>轻量灵活</strong>：仅处理模块级共享，无需生命周期管理，侵入性低。</li>



<li><strong>运行时加载</strong>：支持动态加载模块，适合按需加载场景。</li>



<li><strong>依赖共享</strong>：通过 <code>shared</code> 机制减少重复依赖加载，优化性能。</li>



<li><strong>生态支持</strong>：Webpack 生态强大，适配现代前端工具链。</li>
</ul>



<h3 class="wp-block-heading">5. 缺点</h3>



<ul class="wp-block-list">
<li><strong>配置复杂</strong>：需要手动配置 Webpack，学习成本较高。</li>



<li><strong>隔离性弱</strong>：无内置沙箱机制，需自行处理样式和全局变量冲突。</li>



<li><strong>功能局限</strong>：更适合模块共享，不适合完整应用的微前端架构。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">三、乾坤与模块联邦的区别</h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>特性</th><th>乾坤（Qiankun）</th><th>模块联邦（Module Federation）</th></tr></thead><tbody><tr><td><strong>定位</strong></td><td>完整的微前端框架</td><td>Webpack 的模块共享机制</td></tr><tr><td><strong>核心功能</strong></td><td>子应用加载、路由管理、沙箱隔离、通信</td><td>模块动态加载、依赖共享</td></tr><tr><td><strong>技术栈支持</strong></td><td>支持多种框架（React、Vue 等）</td><td>依赖 Webpack，框架无关</td></tr><tr><td><strong>隔离机制</strong></td><td>内置 JS 和 CSS 沙箱</td><td>无内置沙箱，需手动处理隔离</td></tr><tr><td><strong>通信机制</strong></td><td>全局状态管理、事件机制</td><td>通过模块 props 或自定义通信</td></tr><tr><td><strong>使用场景</strong></td><td>适合复杂微前端项目，需加载完整子应用</td><td>适合模块级共享，按需加载组件或功能</td></tr><tr><td><strong>侵入性</strong></td><td>子应用需适配生命周期钩子</td><td>侵入性低，仅需配置 Webpack</td></tr><tr><td><strong>性能</strong></td><td>沙箱和生命周期管理有一定开销</td><td>轻量，依赖共享优化性能</td></tr><tr><td><strong>学习成本</strong></td><td>简单，文档完善</td><td>配置复杂，需熟悉 Webpack</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">四、适用场景分析</h2>



<h3 class="wp-block-heading">1. 选择乾坤的场景</h3>



<ul class="wp-block-list">
<li>需要管理多个完整的前端应用（如不同团队开发的独立项目）。</li>



<li>对隔离性要求高（如需要严格的 JS 和 CSS 隔离）。</li>



<li>希望快速集成多种技术栈的应用，追求开箱即用。</li>



<li>需要统一的路由管理和全局状态管理。</li>
</ul>



<h3 class="wp-block-heading">2. 选择模块联邦的场景</h3>



<ul class="wp-block-list">
<li>需要在多个应用间共享特定模块或组件（如共享一个通用按钮组件）。</li>



<li>追求轻量级模块加载，减少重复依赖加载。</li>



<li>项目基于 Webpack 构建，团队熟悉 Webpack 配置。</li>



<li>不需要复杂的子应用生命周期管理。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">五、总结</h2>



<p>乾坤和模块联邦都是前端微服务化的重要工具，但定位和实现方式不同：</p>



<ul class="wp-block-list">
<li><strong>乾坤</strong> 更适合需要管理多个完整应用的微前端场景，提供全面的隔离和通信机制，适合复杂项目。</li>



<li><strong>模块联邦</strong> 更适合模块级别的共享和动态加载，配置灵活但隔离性较弱，适合轻量级模块复用场景。</li>
</ul>



<p>根据项目需求选择合适的方案：</p>



<ul class="wp-block-list">
<li>如果是大型微前端项目，推荐使用乾坤以获得完整的框架支持。</li>



<li>如果是模块共享或轻量级微前端场景，模块联邦是更好的选择。</li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>在 Vercel 上部署 Python Flask 或 FastAPI 项目</title>
		<link>https://www.zlhlab.top/%e5%9c%a8-vercel-%e4%b8%8a%e9%83%a8%e7%bd%b2-python-flask-%e6%88%96-fastapi-%e9%a1%b9%e7%9b%ae/</link>
		
		<dc:creator><![CDATA[萧邦]]></dc:creator>
		<pubDate>Tue, 09 Sep 2025 02:44:41 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[fastapi]]></category>
		<category><![CDATA[vercel]]></category>
		<guid isPermaLink="false">https://www.zlhlab.top/?p=844</guid>

					<description><![CDATA[Vercel 是一个广受欢迎的 Web 应用部署平台，以其简单性和无服务器架构而闻名。虽然它常用于部署像 Ne<div class="more-link">
				 <a href="https://www.zlhlab.top/%e5%9c%a8-vercel-%e4%b8%8a%e9%83%a8%e7%bd%b2-python-flask-%e6%88%96-fastapi-%e9%a1%b9%e7%9b%ae/" class="link-btn theme-btn"><span>Read More </span> <i class="fa fa-caret-right"></i></a>
			</div>]]></description>
										<content:encoded><![CDATA[
<p>Vercel 是一个广受欢迎的 Web 应用部署平台，以其简单性和无服务器架构而闻名。虽然它常用于部署像 Next.js 这样的前端框架，但 Vercel 也支持基于 Python 的后端框架，如 Flask 和 FastAPI。本文将指导您完成在 Vercel 上部署 Python Flask 或 FastAPI 项目的步骤，包括项目设置、配置和部署。</p>



<h2 class="wp-block-heading">前提条件</h2>



<p>在开始之前，请确保您具备以下条件：</p>



<ul class="wp-block-list">
<li>一个 Vercel 账户（在 <a href="https://vercel.com/">vercel.com</a> 注册）。</li>



<li>一个准备好部署的 Flask 或 FastAPI 项目。</li>



<li>对 Python、Flask/FastAPI 和 Git 的基本了解。</li>



<li>已安装 Vercel CLI（运行 <code>npm install -g vercel</code>）。</li>



<li>项目已托管在 Git 仓库中（例如 GitHub、GitLab 或 Bitbucket）。</li>
</ul>



<h2 class="wp-block-heading">步骤 1：准备您的 Flask/FastAPI 项目</h2>



<p>要在 Vercel 上部署 Python 项目，您的应用必须适配 Vercel 的无服务器环境。以下是如何准备 Flask 或 FastAPI 项目。</p>



<h3 class="wp-block-heading">Flask 项目示例</h3>



<p>对于 Flask 项目，确保您的项目结构如下：</p>



<pre class="wp-block-code"><code>my-flask-app/
├── app.py
├── requirements.txt
├── vercel.json
</code></pre>



<p><strong>app.py</strong>（示例 Flask 应用）：</p>



<pre class="wp-block-code"><code>from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return '来自 Vercel 上的 Flask 的问候！'

if __name__ == '__main__':
    app.run()
</code></pre>



<h3 class="wp-block-heading">FastAPI 项目示例</h3>



<p>对于 FastAPI 项目，结构类似：</p>



<pre class="wp-block-code"><code>my-fastapi-app/
├── main.py
├── requirements.txt
├── vercel.json
</code></pre>



<p><strong>main.py</strong>（示例 FastAPI 应用）：</p>



<pre class="wp-block-code"><code>from fastapi import FastAPI

app = FastAPI()

@app.get('/')
async def root():
    return {'message': '来自 Vercel 上的 FastAPI 的问候！'}
</code></pre>



<h3 class="wp-block-heading">依赖文件</h3>



<p>创建一个 <code>requirements.txt</code> 文件，列出项目的依赖项：</p>



<pre class="wp-block-code"><code># 对于 Flask
flask==2.3.2
gunicorn==20.1.0

# 对于 FastAPI
fastapi==0.95.1
uvicorn==0.21.1
</code></pre>



<p>Vercel 使用 <code>gunicorn</code> 作为 Flask 的 WSGI 服务器，使用 <code>uvicorn</code> 作为 FastAPI 的 ASGI 服务器，以在无服务器环境中处理请求。</p>



<h2 class="wp-block-heading">步骤 2：配置 Vercel</h2>



<p>Vercel 需要一个 <code>vercel.json</code> 配置文件来理解如何构建和部署您的 Python 项目。该文件指定了构建过程、路由和运行时设置。</p>



<h3 class="wp-block-heading">Flask 的 Vercel 配置</h3>



<p>在项目根目录创建 <code>vercel.json</code> 文件：</p>



<pre class="wp-block-code"><code>{
  "version": 2,
  "builds": &#91;
    {
      "src": "app.py",
      "use": "@vercel/python"
    }
  ],
  "routes": &#91;
    {
      "src": "/(.*)",
      "dest": "app.py"
    }
  ]
}
</code></pre>



<h3 class="wp-block-heading">FastAPI 的 Vercel 配置</h3>



<p>对于 FastAPI，<code>vercel.json</code> 文件因其 ASGI 特性略有不同：</p>



<pre class="wp-block-code"><code>{
  "version": 2,
  "builds": &#91;
    {
      "src": "main.py",
      "use": "@vercel/python"
    }
  ],
  "routes": &#91;
    {
      "src": "/(.*)",
      "dest": "main.py"
    }
  ]
}
</code></pre>



<h3 class="wp-block-heading"><code>vercel.json</code> 说明</h3>



<ul class="wp-block-list">
<li><code>"version": 2</code>：指定 Vercel 配置版本。</li>



<li><code>"builds"</code>：定义构建过程。这里我们使用 <code>@vercel/python</code> 运行时来处理 Python 应用。</li>



<li><code>"routes"</code>：将所有传入请求映射到您的应用文件（Flask 为 <code>app.py</code>，FastAPI 为 <code>main.py</code>）。</li>
</ul>



<h2 class="wp-block-heading">步骤 3：本地测试</h2>



<p>在部署之前，先在本地测试您的应用以确保其正常运行。</p>



<p>对于 Flask：</p>



<pre class="wp-block-code"><code>pip install -r requirements.txt
gunicorn --bind 0.0.0.0:8000 app:app
</code></pre>



<p>对于 FastAPI：</p>



<pre class="wp-block-code"><code>pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8000
</code></pre>



<p>在浏览器中访问 <code>http://localhost:8000</code> 以验证应用是否运行正常。</p>



<h2 class="wp-block-heading">步骤 4：部署到 Vercel</h2>



<p>项目准备好后，您可以将其部署到 Vercel。</p>



<ol class="wp-block-list">
<li><strong>初始化 Git 仓库</strong>（如果尚未完成）： <code>git init git add . git commit -m "初始提交"</code></li>



<li><strong>推送到 Git 托管服务</strong>（如 GitHub）： <code>git remote add origin &lt;您的仓库地址> git push origin main</code></li>



<li><strong>将 Vercel 链接到您的仓库</strong>：
<ul class="wp-block-list">
<li>使用 CLI 登录 Vercel：<code>vercel login</code></li>



<li>部署您的项目：<code>vercel</code></li>



<li>按照提示将项目链接到 Git 仓库或直接部署。Vercel 将检测 Python 运行时并使用 <code>vercel.json</code> 中的设置。</li>
</ul>
</li>



<li><strong>设置环境变量</strong>（如需要）：<br>如果您的应用需要环境变量（例如 API 密钥），在 Vercel 仪表板的 <strong>设置 > 环境变量</strong> 中添加它们。</li>



<li><strong>验证部署</strong>：<br>部署完成后，Vercel 会提供一个 URL（例如 <code>https://my-flask-app.vercel.app</code>）。访问该 URL 确认您的应用已上线。</li>
</ol>



<h2 class="wp-block-heading">步骤 5：持续部署</h2>



<p>要启用持续部署，请将您的 Git 仓库连接到 Vercel：</p>



<ul class="wp-block-list">
<li>在 Vercel 仪表板中，前往 <strong>新建项目</strong> 并导入您的仓库。</li>



<li>配置项目设置（Vercel 会自动检测 <code>vercel.json</code> 文件）。</li>



<li>推送到您的仓库，Vercel 将自动重新部署。</li>
</ul>



<h2 class="wp-block-heading">故障排除提示</h2>



<ul class="wp-block-list">
<li><strong>构建错误</strong>：确保 <code>requirements.txt</code> 包含所有依赖项，并且 <code>@vercel/python</code> 与您的 Python 版本兼容（Vercel 支持 Python 3.9+）。</li>



<li><strong>路由问题</strong>：检查 <code>vercel.json</code> 中的 <code>routes</code> 是否正确映射到您的应用入口点。</li>



<li><strong>超时错误</strong>：Vercel 的无服务器函数有最大执行时间（Hobby 计划为 10 秒，Pro 计划为 60 秒）。优化您的应用以快速响应。</li>



<li><strong>日志</strong>：在 Vercel 仪表板的 <strong>Functions > Logs</strong> 中查看部署日志以进行调试。</li>
</ul>



<h2 class="wp-block-heading">最佳实践</h2>



<ul class="wp-block-list">
<li><strong>保持依赖最小化</strong>：在 <code>requirements.txt</code> 中仅包含必要的包，以减少构建时间。</li>



<li><strong>使用环境变量</strong>：将敏感数据（例如数据库凭据）存储在 Vercel 的环境变量中。</li>



<li><strong>优化无服务器环境</strong>：确保您的应用是无状态的，因为 Vercel 的无服务器环境不会在请求之间保留状态。</li>



<li><strong>使用 Vercel CLI 本地测试</strong>：使用 <code>vercel dev</code> 模拟 Vercel 的本地环境。</li>
</ul>



<h2 class="wp-block-heading">结论</h2>



<p>通过正确的配置，在 Vercel 上部署 Flask 或 FastAPI 项目非常简单。通过设置项目结构、创建 <code>vercel.json</code> 文件并使用 Vercel 的 CLI 或仪表板，您可以在几分钟内让您的 Python 后端上线。无论您是构建简单的 API 还是完整的 Web 应用，Vercel 的无服务器平台都提供了一个可扩展且无忧的部署解决方案。</p>



<p>祝您部署顺利！</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>惠每数据中台项目</title>
		<link>https://www.zlhlab.top/%e6%83%a0%e6%af%8f%e6%95%b0%e6%8d%ae%e4%b8%ad%e5%8f%b0%e9%a1%b9%e7%9b%ae/</link>
		
		<dc:creator><![CDATA[萧邦]]></dc:creator>
		<pubDate>Wed, 20 Aug 2025 07:43:31 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[antd pro components]]></category>
		<category><![CDATA[react.js]]></category>
		<category><![CDATA[umi.js]]></category>
		<guid isPermaLink="false">https://www.zlhlab.top/?p=777</guid>

					<description><![CDATA[惠每数据中台项目包含项目管理、数据对象的创建，任务管理和任务自动化分派、系统多用户多角色的权限控制，以及医生和<div class="more-link">
				 <a href="https://www.zlhlab.top/%e6%83%a0%e6%af%8f%e6%95%b0%e6%8d%ae%e4%b8%ad%e5%8f%b0%e9%a1%b9%e7%9b%ae/" class="link-btn theme-btn"><span>Read More </span> <i class="fa fa-caret-right"></i></a>
			</div>]]></description>
										<content:encoded><![CDATA[
<p>惠每数据中台项目包含项目管理、数据对象的创建，任务管理和任务自动化分派、系统多用户多角色的权限控制，以及医生和文献知识库的查询。实现多项目管理、数据库构建。</p>



<h2 class="wp-block-heading">项目架构</h2>



<p>项目前端采用react + umi.js v3版本，角色权限采用人人企业管理组件。</p>



<h2 class="wp-block-heading">项目UI</h2>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="577" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.19.00-1024x577.png" alt="" class="wp-image-781" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.19.00-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.19.00-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.19.00-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.19.00-1536x865.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.19.00-2048x1153.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-gallery has-nested-images columns-1 is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="577" data-id="783" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.18.35-1024x577.png" alt="" class="wp-image-783" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.18.35-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.18.35-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.18.35-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.18.35-1536x865.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.18.35-2048x1153.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="576" data-id="786" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.05-1024x576.png" alt="" class="wp-image-786" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.05-1024x576.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.05-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.05-768x432.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.05-1536x864.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.05-2048x1152.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="577" data-id="787" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.23-1024x577.png" alt="" class="wp-image-787" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.23-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.23-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.23-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.23-1536x865.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.23-2048x1153.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="577" data-id="788" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.31-1024x577.png" alt="" class="wp-image-788" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.31-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.31-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.31-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.31-1536x865.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.20.31-2048x1153.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="577" data-id="789" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.19-1024x577.png" alt="" class="wp-image-789" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.19-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.19-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.19-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.19-1536x865.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.19-2048x1153.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="577" data-id="782" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.50-1024x577.png" alt="" class="wp-image-782" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.50-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.50-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.50-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.50-1536x866.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.50-2048x1155.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="577" data-id="790" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.38-1024x577.png" alt="" class="wp-image-790" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.38-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.38-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.38-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.38-1536x866.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.21.38-2048x1155.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="577" data-id="792" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.14-1024x577.png" alt="" class="wp-image-792" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.14-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.14-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.14-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.14-1536x866.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.14-2048x1155.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="577" data-id="793" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.26-1024x577.png" alt="" class="wp-image-793" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.26-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.26-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.26-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.26-1536x865.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.50.26-2048x1153.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="577" data-id="794" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.51.53-1024x577.png" alt="" class="wp-image-794" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.51.53-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.51.53-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.51.53-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.51.53-1536x866.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.51.53-2048x1155.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="577" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.16-1024x577.png" alt="" class="wp-image-779" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.16-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.16-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.16-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.16-1536x866.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.16-2048x1155.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="577" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.33-1024x577.png" alt="" class="wp-image-780" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.33-1024x577.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.33-300x169.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.33-768x433.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.33-1536x865.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-06-10.17.33-2048x1153.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p></p>



<h2 class="wp-block-heading">相关链接</h2>



<div class="wp-block-group is-vertical is-layout-flex wp-container-core-group-is-layout-8cf370e7 wp-block-group-is-layout-flex">
<p>umi.js v3: <a href="https://v3.umijs.org/">https://v3.umijs.org/</a></p>



<p>人人企业demo版本：<a href="https://demo.renren.io/security-enterprise/#/login">https://demo.renren.io/security-enterprise/#/login</a></p>
</div>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>AI 工具生态解析：Dify、Coze、LangChain、LangSmith、Google ADK 与 n8n 的关系与概念</title>
		<link>https://www.zlhlab.top/ai-%e5%b7%a5%e5%85%b7%e7%94%9f%e6%80%81%e8%a7%a3%e6%9e%90%ef%bc%9adify%e3%80%81coze%e3%80%81langchain%e3%80%81langsmith%e3%80%81google-adk-%e4%b8%8e-n8n-%e7%9a%84%e5%85%b3%e7%b3%bb%e4%b8%8e%e6%a6%82/</link>
		
		<dc:creator><![CDATA[萧邦]]></dc:creator>
		<pubDate>Wed, 20 Aug 2025 07:33:36 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">https://www.zlhlab.top/?p=830</guid>

					<description><![CDATA[随着大语言模型（LLM）和智能自动化技术的快速发展，开发者面临从低代码快速原型到高代码深度定制的多样化工具选择<div class="more-link">
				 <a href="https://www.zlhlab.top/ai-%e5%b7%a5%e5%85%b7%e7%94%9f%e6%80%81%e8%a7%a3%e6%9e%90%ef%bc%9adify%e3%80%81coze%e3%80%81langchain%e3%80%81langsmith%e3%80%81google-adk-%e4%b8%8e-n8n-%e7%9a%84%e5%85%b3%e7%b3%bb%e4%b8%8e%e6%a6%82/" class="link-btn theme-btn"><span>Read More </span> <i class="fa fa-caret-right"></i></a>
			</div>]]></description>
										<content:encoded><![CDATA[
<p>随着大语言模型（LLM）和智能自动化技术的快速发展，开发者面临从低代码快速原型到高代码深度定制的多样化工具选择。本文深入剖析六种主流 AI 及自动化工具——<strong>Dify</strong>、<strong>Coze</strong>、<strong>LangChain</strong>、<strong>LangSmith</strong>、<strong>Google Agent Development Kit (ADK)</strong> 和 <strong>n8n</strong>，从其功能定位、适用场景、生态关系及未来发展出发，为开发者和企业提供清晰的选型参考。</p>



<h2 class="wp-block-heading">1. 工具概述</h2>



<h3 class="wp-block-heading">1.1 Dify</h3>



<p><strong>Dify</strong> 是一款开源的大语言模型应用开发平台，融合了“后端即服务”（Backend as Service）和 LLMOps 理念，旨在降低 AI 应用开发的门槛。它通过可视化界面和拖拽式工作流设计，支持非技术人员快速构建 AI 应用，同时为开发者提供从原型到生产的全生命周期支持。核心功能包括：</p>



<ul class="wp-block-list">
<li><strong>可视化工作流</strong>：通过拖拽组件实现复杂任务编排，简化开发流程。</li>



<li><strong>RAG 引擎</strong>：内置检索增强生成（Retrieval-Augmented Generation）功能，支持企业知识库问答。</li>



<li><strong>多模型支持</strong>：兼容 OpenAI、阿里云通义千问、文心一言等数十种主流模型。</li>



<li><strong>企业级特性</strong>：提供多租户隔离、审计日志、性能监控，适合生产环境部署。</li>
</ul>



<p><strong>适用场景</strong>：快速构建企业级 AI 应用，如智能客服、知识库问答、自动化流程，特别适合需要本地部署或快速验证产品概念的团队。</p>



<h3 class="wp-block-heading">1.2 Coze</h3>



<p><strong>Coze</strong>（国内版称“扣子”）是由字节跳动推出的低代码 AI 智能体开发平台，专注于对话式 AI 应用和自动化任务。其设计理念是“人人都是 AI 开发者”，通过直观的图形化界面和丰富的插件生态降低技术门槛。核心功能包括：</p>



<ul class="wp-block-list">
<li><strong>插件生态</strong>：提供上千款工具插件，支持搜索、网页爬取、API 调用等。</li>



<li><strong>多平台发布</strong>：支持将智能体发布到抖音、飞书、公众号等平台。</li>



<li><strong>对话式交互</strong>：适合构建聊天机器人、自动化助手等场景。</li>



<li><strong>可视化工作流</strong>：通过拖拽方式配置复杂任务逻辑。</li>
</ul>



<p><strong>适用场景</strong>：C 端用户、小型团队或个人开发者，用于快速搭建对话机器人、社交媒体助手或轻量级自动化工具。</p>



<h3 class="wp-block-heading">1.3 LangChain</h3>



<p><strong>LangChain</strong> 是一个开源的 Python/TypeScript 框架，专注于构建基于语言模型的复杂应用程序。它通过模块化设计和链式调用，提供高度定制化的开发体验。核心组件包括：</p>



<ul class="wp-block-list">
<li><strong>模块化设计</strong>：包括 Models、Prompts、Memory、Chains、Agents 等，支持灵活组合。</li>



<li><strong>RAG 支持</strong>：便于集成外部数据源和知识库，增强模型上下文能力。</li>



<li><strong>工具集成</strong>：支持与数据库、API、文件系统等外部工具无缝交互。</li>



<li><strong>社区生态</strong>：拥有超过 10 万 GitHub Stars，社区活跃，扩展性强。</li>
</ul>



<p><strong>适用场景</strong>：需要深度定制的复杂 AI 应用，如多步骤任务链、数据分析助手、科研实验平台，适合有编程经验的开发者。</p>



<h3 class="wp-block-heading">1.4 LangSmith</h3>



<p><strong>LangSmith</strong> 是 LangChain 生态中的调试和监控工具，专注于优化 LLM 应用的开发和运维流程。它与 LangChain 紧密集成，提供了从开发到生产的可观测性支持。核心功能包括：</p>



<ul class="wp-block-list">
<li><strong>调试工具</strong>：支持可视化调试 Agent 和工作流，分析模型输入输出。</li>



<li><strong>性能监控</strong>：跟踪消息数量、Token 消耗、用户满意度等指标。</li>



<li><strong>日志分析</strong>：提供详细运行日志，帮助优化 Prompt 和工作流。</li>



<li><strong>协作支持</strong>：便于团队协作和持续改进。</li>
</ul>



<p><strong>适用场景</strong>：适用于需要精细化调试和性能优化的 LangChain 项目，特别是在生产环境中。</p>



<h3 class="wp-block-heading">1.5 Google Agent Development Kit (ADK)</h3>



<p><strong>Google Agent Development Kit (ADK)</strong> 是一个开源框架，于 2025 年 4 月在 Google Cloud NEXT 2025 发布，旨在简化多智能体系统的开发和部署。它强调模块化、灵活性和生产就绪，广泛应用于 Google 内部产品。核心功能包括：</p>



<ul class="wp-block-list">
<li><strong>多智能体架构</strong>：支持通过 LlmAgent、SequentialAgent、ParallelAgent 等构建复杂协作系统。</li>



<li><strong>模型无关性</strong>：支持 Google Gemini、Claude、Mistral 等多种模型，通过 LiteLLM 扩展兼容性。</li>



<li><strong>工具生态</strong>：提供预建工具、自定义函数和 OpenAPI 集成，支持与 Google Cloud 生态（如 Vertex AI）深度融合。</li>



<li><strong>A2A 协议</strong>：通过 Agent-to-Agent (A2A) 协议实现跨平台智能体协作。</li>



<li><strong>开发体验</strong>：内置 CLI（如 <code>adk web</code>、<code>adk run</code>）、Web UI 和评估框架，支持调试和优化。</li>



<li><strong>部署灵活性</strong>：支持本地调试和容器化部署（如 Cloud Run、Kubernetes、Vertex AI）。</li>
</ul>



<p><strong>适用场景</strong>：构建生产级多智能体系统，如自动化工作流、数据分析助手、客户服务机器人，适合 Google Cloud 生态用户或需要跨平台协作的开发者。</p>



<h3 class="wp-block-heading">1.6 n8n</h3>



<p><strong>n8n</strong> 是一个开源的工作流自动化工具，专注于通过节点式工作流实现系统集成和任务自动化。虽然其核心不是 AI 开发，但通过与 LLM 和 AI 工具的集成，n8n 在 AI 驱动的自动化场景中表现出色。核心功能包括：</p>



<ul class="wp-block-list">
<li><strong>节点式工作流</strong>：通过可视化节点连接数百种应用程序（如 Slack、Google Sheets、OpenAI），实现复杂任务自动化。</li>



<li><strong>AI 集成</strong>：支持与 OpenAI、Hugging Face 等模型集成，用于文本生成、数据处理等任务。</li>



<li><strong>灵活部署</strong>：支持云端、本地部署和 Docker，适合数据敏感场景。</li>



<li><strong>社区扩展</strong>：提供丰富的社区节点和自定义脚本支持，增强灵活性。</li>
</ul>



<p><strong>适用场景</strong>：跨系统集成和自动化任务，如数据管道、通知系统、AI 驱动的业务流程优化，适合需要连接多种工具的团队。</p>



<h2 class="wp-block-heading">2. 工具之间的关系</h2>



<h3 class="wp-block-heading">2.1 技术定位与层次</h3>



<ul class="wp-block-list">
<li><strong>Dify 和 Coze</strong>：定位于低代码/零代码平台，强调用户友好性和快速开发。Dify 更适合企业级场景，支持本地部署和 RAG 优化；Coze 聚焦对话式应用和字节生态整合，适合社交媒体场景。</li>



<li><strong>LangChain</strong>：高代码框架，面向开发者，提供模块化工具和深度定制能力，是许多低代码平台（如 Dify）的底层灵感来源。</li>



<li><strong>LangSmith</strong>：作为 LangChain 的配套工具，专注于调试和监控，增强 LangChain 应用的开发效率和生产稳定性。</li>



<li><strong>Google ADK</strong>：兼具高代码灵活性和生产级特性，与 LangChain 类似，但更强调多智能体协作和 Google 生态整合。</li>



<li><strong>n8n</strong>：定位于工作流自动化，侧重系统集成而非 AI 开发，但通过 AI 节点（如 OpenAI）与 Dify、Coze 等形成互补。</li>
</ul>



<h3 class="wp-block-heading">2.2 生态联系</h3>



<ul class="wp-block-list">
<li><strong>LangChain 生态</strong>：LangChain 是许多工具的底层框架。Dify 的工作流设计和 RAG 功能借鉴了 LangChain 的链式调用思想。LangSmith 直接服务于 LangChain 项目，提供调试和运维支持。</li>



<li><strong>Dify 与 Coze</strong>：两者均为低代码平台，Dify 强调企业级功能和开源部署，Coze 则依赖字节云服务，适合社交平台整合。</li>



<li><strong>Google ADK</strong>：与 LangChain 在功能上有重叠，但其 A2A 协议和 Google 生态整合（如 Gemini、Vertex AI）使其在多智能体协作中独具优势。</li>



<li><strong>n8n 的角色</strong>：n8n 通过 API 和节点与 Dify、Coze、LangChain、ADK 集成，充当自动化“胶水”，连接 AI 应用与外部系统（如 CRM、数据库）。例如，n8n 可调用 Dify 的 API 执行知识库查询，或触发 Coze 的智能体响应。</li>



<li><strong>跨工具协作</strong>：n8n 可作为工作流引擎，将 Dify/Coze 的 AI 能力与 LangChain/ADK 的复杂逻辑结合，LangSmith 则优化 LangChain/ADK 的性能。</li>
</ul>



<h3 class="wp-block-heading">2.3 竞争与互补</h3>



<ul class="wp-block-list">
<li><strong>竞争</strong>：
<ul class="wp-block-list">
<li><strong>Dify vs Coze</strong>：两者在低代码市场竞争，目标客户重叠（非技术用户和快速原型开发）。Dify 的开源和企业级特性适合数据敏感场景；Coze 的插件生态和字节整合适合 C 端和社交应用。</li>



<li><strong>LangChain vs ADK</strong>：两者均为高代码框架，竞争体现在开发方式选择。LangChain 社区生态更成熟，ADK 在 Google 生态和多智能体协作上有优势。</li>



<li><strong>Dify/Coze vs LangChain/ADK</strong>：低代码平台与高代码框架竞争在于易用性 vs 灵活性。</li>



<li><strong>n8n vs Dify/Coze</strong>：n8n 的自动化功能与 Dify/Coze 的工作流功能部分重叠，但 n8n 更专注于跨系统集成，而非 AI 应用开发。</li>
</ul>
</li>



<li><strong>互补</strong>：
<ul class="wp-block-list">
<li><strong>LangSmith 与 LangChain/ADK</strong>：LangSmith 增强 LangChain 和 ADK 的调试和运维能力，类似 Dify 的 LLMOps 功能。</li>



<li><strong>Dify/Coze 与 LangChain/ADK</strong>：低代码平台可基于 LangChain/ADK 构建，简化复杂功能的使用。</li>



<li><strong>n8n 与其他工具</strong>：n8n 作为自动化层，连接 Dify/Coze 的 AI 应用、LangChain/ADK 的智能体逻辑，以及外部系统，形成完整的业务流程。</li>



<li><strong>ADK 与 n8n</strong>：ADK 的多智能体系统可通过 n8n 的工作流触发或集成，增强自动化能力。</li>
</ul>
</li>
</ul>



<h2 class="wp-block-heading">3. 选择建议</h2>



<p>选择合适的工具需根据团队技术能力、项目复杂度和业务场景：</p>



<ul class="wp-block-list">
<li><strong>初学者/非技术团队</strong>：
<ul class="wp-block-list">
<li><strong>Dify</strong>：适合需要本地部署或企业级功能的用户，快速构建知识库问答或自动化流程。</li>



<li><strong>Coze</strong>：适合社交媒体或轻量级对话机器人开发，插件生态丰富，发布便捷。</li>



<li><strong>n8n</strong>：适合需要跨系统自动化的团队，快速连接 AI 工具与外部服务。</li>
</ul>
</li>



<li><strong>专业开发者</strong>：
<ul class="wp-block-list">
<li><strong>LangChain</strong>：首选框架，适合需要深度定制的复杂 AI 应用，社区支持强大。</li>



<li><strong>Google ADK</strong>：适合 Google Cloud 用户或多智能体系统开发者，A2A 协议和 Vertex AI 集成是亮点。</li>
</ul>
</li>



<li><strong>运维优化</strong>：
<ul class="wp-block-list">
<li><strong>LangSmith</strong>：为 LangChain/ADK 项目提供调试和监控支持，适合生产环境优化。</li>
</ul>
</li>



<li><strong>自动化与集成</strong>：
<ul class="wp-block-list">
<li><strong>n8n</strong>：适合需要连接多种工具和系统的工作流场景，与 Dify/Coze/LangChain/ADK 形成互补。</li>
</ul>
</li>



<li><strong>混合场景</strong>：
<ul class="wp-block-list">
<li>用 Dify/Coze 快速验证原型，LangChain/ADK 进行深度定制，n8n 实现跨系统自动化，LangSmith 优化性能。</li>
</ul>
</li>



<li><strong>Google 生态用户</strong>：
<ul class="wp-block-list">
<li><strong>ADK</strong>：与 Gemini 模型、Vertex AI 深度整合，适合多智能体和云部署场景。</li>
</ul>
</li>
</ul>



<h2 class="wp-block-heading">4. 未来展望</h2>



<p>随着 AI 和自动化技术的融合，低代码平台（如 Dify、Coze）、高代码框架（如 LangChain、ADK）和自动化工具（如 n8n）将进一步整合，兼顾易用性、灵活性和系统互联互通。LangSmith 将推动 LLM 应用的运维标准化，ADK 的 A2A 协议可能成为多智能体协作标准，n8n 则将继续扩展其节点生态，连接更多 AI 和非 AI 系统。开发者需关注社区活跃度、技术更新和行业适配性，以选择最契合的工具组合。</p>



<h2 class="wp-block-heading">5. 结论</h2>



<p>Dify、Coze、LangChain、LangSmith、Google ADK 和 n8n 共同构成了 AI 应用开发和自动化的多层次生态。Dify 和 Coze 降低 AI 开发门槛，适合快速原型；LangChain 和 ADK 提供深度定制，适合复杂应用；LangSmith 优化运维；n8n 连接 AI 与外部系统，增强自动化能力。理解它们的功能与关系，有助于开发者高效构建 AI 驱动的智能化解决方案。</p>



<p><strong>参考资料</strong>：</p>



<ul class="wp-block-list">
<li>Dify 官网：<a href="https://dify.ai/">https://dify.ai</a></li>



<li>Coze 官网：<a href="https://www.coze.cn/">https://www.coze.cn</a></li>



<li>LangChain 文档：<a href="https://python.langchain.com/">https://python.langchain.com</a></li>



<li>LangSmith 文档：<a href="https://docs.smith.langchain.com/">https://docs.smith.langchain.com</a></li>



<li>Google ADK 文档：<a href="https://google.github.io/adk">https://google.github.io/adk</a></li>



<li>n8n 官网：<a href="https://n8n.io/">https://n8n.io</a></li>



<li>Google Developers Blog：<a href="https://developers.googleblog.com/">https://developers.googleblog.com</a></li>



<li>GitHub ADK Python：<a href="https://github.com/google/adk-python">https://github.com/google/adk-python</a></li>



<li>Google Cloud Blog：<a href="https://cloud.google.com/">https://cloud.google.com</a></li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>使用 Next.js 建站：GitHub 代码管理、Vercel CI/CD 部署及腾讯云域名关联实现国内访问</title>
		<link>https://www.zlhlab.top/%e4%bd%bf%e7%94%a8-next-js-%e5%bb%ba%e7%ab%99%ef%bc%9agithub-%e4%bb%a3%e7%a0%81%e7%ae%a1%e7%90%86%e3%80%81vercel-ci-cd-%e9%83%a8%e7%bd%b2%e5%8f%8a%e8%85%be%e8%ae%af%e4%ba%91%e5%9f%9f%e5%90%8d%e5%85%b3/</link>
		
		<dc:creator><![CDATA[萧邦]]></dc:creator>
		<pubDate>Fri, 15 Aug 2025 03:43:56 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[next.js]]></category>
		<category><![CDATA[vercel]]></category>
		<guid isPermaLink="false">https://www.zlhlab.top/?p=818</guid>

					<description><![CDATA[引言 在为公司开发官网时，传统国内建站方式通常涉及购买云服务器、采用 WordPress 或自研系统，流程复杂<div class="more-link">
				 <a href="https://www.zlhlab.top/%e4%bd%bf%e7%94%a8-next-js-%e5%bb%ba%e7%ab%99%ef%bc%9agithub-%e4%bb%a3%e7%a0%81%e7%ae%a1%e7%90%86%e3%80%81vercel-ci-cd-%e9%83%a8%e7%bd%b2%e5%8f%8a%e8%85%be%e8%ae%af%e4%ba%91%e5%9f%9f%e5%90%8d%e5%85%b3/" class="link-btn theme-btn"><span>Read More </span> <i class="fa fa-caret-right"></i></a>
			</div>]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">引言</h2>



<p>在为公司开发官网时，传统国内建站方式通常涉及购买云服务器、采用 WordPress 或自研系统，流程复杂且耗时。相比之下，结合 Next.js、GitHub 和 Vercel 提供了一套高效、免费的解决方案：Next.js 提供现代化 Web 开发体验，GitHub 实现代码管理，Vercel 提供无缝 CI/CD 部署，而腾讯云域名配置则优化国内访问速度并满足合规需求。</p>



<p>本流程的优势在于：</p>



<ul class="wp-block-list">
<li><strong>Next.js</strong>：支持服务器端渲染（SSR）、静态生成（SSG）和 API 路由，兼顾性能与 SEO。</li>



<li><strong>GitHub</strong>：免费代码托管，版本控制简单。</li>



<li><strong>Vercel</strong>：Next.js 官方部署平台，支持自动部署和全球 CDN。</li>



<li><strong>腾讯云域名</strong>：支持 ICP 备案，加速国内访问。</li>
</ul>



<p>本文总结了从零搭建 Next.js 官网、通过 GitHub 管理代码、使用 Vercel 实现 CI/CD，并关联腾讯云域名以优化国内访问的完整流程。内容基于官方文档和实践经验，适合有 React 基础的开发者，预计耗时 1-2 小时。以下步骤包含详细命令和界面描述，确保清晰易懂。</p>



<h2 class="wp-block-heading">步骤 1: 创建 Next.js 项目</h2>



<p>确保本地安装 Node.js（建议版本 18 或以上）。使用 <code>create-next-app</code> 快速创建项目。</p>



<ol class="wp-block-list">
<li><strong>初始化项目</strong>：
<ul class="wp-block-list">
<li>在终端运行：<code>npx create-next-app@latest my-next-app</code></li>



<li>按提示选择：推荐使用 App Router，TypeScript（可选）、ESLint 和 Tailwind CSS。生成的项目包含仪表板模板，适合快速开发。</li>



<li>项目结构：<code>my-next-app/ ├── app/ ├── public/ ├── next.config.js ├── package.json └── README.md</code></li>
</ul>
</li>



<li><strong>启动开发服务器</strong>：
<ul class="wp-block-list">
<li>进入目录：<code>cd my-next-app</code></li>



<li>运行：<code>npm run dev</code></li>



<li>打开 <code>http://localhost:3000</code>，查看 Next.js 默认页面。</li>
</ul>
</li>
</ol>



<p><strong>图 1: Next.js 项目创建终端</strong></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="251" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.33.02-1024x251.png" alt="" class="wp-image-823" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.33.02-1024x251.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.33.02-300x74.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.33.02-768x189.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.33.02.png 1230w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>初学者可参考 Next.js 官方文档的 React 基础课程，深入了解 App Router。</p>



<h2 class="wp-block-heading">步骤 2: 代码托管到 GitHub</h2>



<p>GitHub 提供可靠的代码版本控制和协作平台。我们将本地项目推送到 GitHub 仓库。</p>



<ol class="wp-block-list">
<li><strong>初始化本地 Git 仓库</strong>：
<ul class="wp-block-list">
<li>在项目根目录运行：<code>git init git add . git commit -m "Initial commit"</code></li>
</ul>
</li>



<li><strong>创建 GitHub 仓库</strong>：
<ul class="wp-block-list">
<li>登录 GitHub，点击右上角 “+” &gt; “New repository”。</li>



<li>输入仓库名称（如 <code>my-next-app</code>），选择公有（便于 Vercel 集成）或私有，添加描述，可选添加 README 或 <code>.gitignore</code>。</li>



<li>点击 “Create repository”。</li>
</ul>
</li>



<li><strong>推送代码</strong>：
<ul class="wp-block-list">
<li>添加远程仓库：<code>git remote add origin https://github.com/your-username/my-next-app.git</code></li>



<li>推送：<code>git push -u origin main</code></li>
</ul>
</li>
</ol>



<p>此步骤确保代码安全备份，并为 CI/CD 部署做准备。</p>



<h2 class="wp-block-heading">步骤 3: 使用 Vercel 实现 CI/CD 部署</h2>



<p>Vercel 是 Next.js 的首选部署平台，支持从 GitHub 自动构建和部署，简化 CI/CD 流程。</p>



<ol class="wp-block-list">
<li><strong>连接 GitHub 与 Vercel</strong>：
<ul class="wp-block-list">
<li>注册/登录 Vercel（免费账户即可）。</li>



<li>在 Vercel 仪表板点击 “New Project”。</li>



<li>选择 GitHub，授权访问，选择 <code>my-next-app</code> 仓库，点击 “Import”。</li>
</ul>
</li>



<li><strong>配置并部署</strong>：
<ul class="wp-block-list">
<li>保留默认设置（框架自动识别为 Next.js）。</li>



<li>点击 “Deploy”，Vercel 将自动构建并部署项目。</li>



<li>部署完成后，获得类似 <code>my-next-app.vercel.app</code> 的默认域名。</li>
</ul>
</li>



<li><strong>启用 CI/CD</strong>：
<ul class="wp-block-list">
<li>推送至 <code>main</code> 分支触发生产部署，其他分支触发预览部署。</li>



<li>可在 Vercel 设置中调整生产分支（默认 <code>main</code>）。</li>



<li>私有仓库需确保提交者有 Vercel 项目权限。</li>
</ul>
</li>
</ol>



<p><strong>图 3: Vercel 部署仪表板</strong><br>（描述：Vercel 页面显示 “Deployments” 标签，列出构建日志和预览 URL，绿色状态表示部署成功。）</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="300" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.37.48-1024x300.png" alt="" class="wp-image-825" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.37.48-1024x300.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.37.48-300x88.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.37.48-768x225.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.37.48-1536x450.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.37.48-2048x600.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>测试 CI/CD：修改本地代码，推送至 GitHub，Vercel 将自动重新部署。</p>



<h2 class="wp-block-heading">步骤 4: 配置腾讯云域名以优化国内访问</h2>



<p>为提升品牌形象和国内访问速度，我们通过 Vercel 添加子域名（如 <code>blog.example.com</code>），并结合腾讯云 DNS 解析。注意：中国用户需完成域名 ICP 备案（约 20 天），否则可能无法访问。备案可通过腾讯云控制台申请。</p>



<ol class="wp-block-list">
<li><strong>在 Vercel 配置子域名</strong>：
<ul class="wp-block-list">
<li>在 Vercel 项目仪表板，进入 “Settings” &gt; “Domains”。</li>



<li>输入子域名（如 <code>blog.example.com</code>），点击 “Add”。</li>



<li>Vercel 提供 CNAME 记录（如 <code>cname.vercel-dns.com</code>），并自动配置 SSL。</li>
</ul>
</li>
</ol>



<p><strong>图 4: Vercel 域名配置界面</strong><br>（描述：Vercel 设置页面显示 “Add Domain” 输入框和 DNS 记录提示，包含 CNAME 值。）</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="253" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.34.41-1024x253.png" alt="" class="wp-image-824" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.34.41-1024x253.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.34.41-300x74.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.34.41-768x190.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.34.41-1536x379.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-11.34.41.png 1846w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ol start="2" class="wp-block-list">
<li><strong>在腾讯云设置 DNS</strong>：
<ul class="wp-block-list">
<li>登录腾讯云控制台，进入 “域名管理” &gt; “DNS 解析”。</li>



<li>选择已备案域名（如 <code>example.com</code>）。</li>



<li>添加记录：
<ul class="wp-block-list">
<li>类型：CNAME</li>



<li>主机记录：<code>blog</code></li>



<li>记录值：<code>cname.vercel-dns.com</code>（从 Vercel 复制）</li>



<li>TTL：600 秒</li>
</ul>
</li>



<li>保存后，等待 DNS 传播（几分钟至数小时）。</li>



<li>验证：运行 <code>dig blog.example.com</code>，确认指向 Vercel IP。</li>
</ul>
</li>
</ol>



<p><strong>图 5: 腾讯云 DNS 配置界面</strong><br>（描述：腾讯云控制台显示 “添加记录” 按钮，填写 CNAME 类型、主机记录和值，蓝色主题。）</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="85" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-12.02.14-1024x85.png" alt="" class="wp-image-828" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-12.02.14-1024x85.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-12.02.14-300x25.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-12.02.14-768x64.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-12.02.14-1536x128.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-15-12.02.14-2048x171.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ol start="3" class="wp-block-list">
<li><strong>优化国内访问</strong>：
<ul class="wp-block-list">
<li>备案完成后，国内用户可直接访问子域名。</li>



<li>Vercel 的全球 CDN 结合腾讯云域名解析可降低延迟。</li>



<li>若访问受限，检查备案状态或启用腾讯云 CDN 加速。</li>
</ul>
</li>
</ol>



<p>此配置兼顾合规性和性能：Vercel 负责部署，腾讯云 提供域名解析和备案支持。</p>



<h2 class="wp-block-heading">结论</h2>



<p>通过 Next.js、GitHub、Vercel 和腾讯云的组合，您可以快速构建一个高性能、易维护的企业官网。相比传统 WordPress 或自研建站方式，此流程简化了开发和部署，特别适合需要快速上线和国内优化的项目。遇到问题可参考 Next.js、Vercel 或腾讯云官方文档。</p>



<p>如需扩展功能（如环境变量、Edge Functions），请查阅 Vercel 文档。祝您的官网开发顺利！</p>



<h2 class="wp-block-heading">相关链接</h2>



<div class="wp-block-group is-vertical is-layout-flex wp-container-core-group-is-layout-8cf370e7 wp-block-group-is-layout-flex">
<p>vercel官网：<a href="https://vercel.com/home">https://vercel.com/home</a></p>



<p>github官网：<a href="https://github.com">https://github.com</a></p>



<p>聚信官网：<a href="https://juxin.www.zlhlab.top">https://juxin.www.zlhlab.top</a></p>
</div>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>D3.js、ECharts 和 Lightweight Charts 的数据可视化比较</title>
		<link>https://www.zlhlab.top/d3-js%e3%80%81echarts-%e5%92%8c-lightweight-charts-%e7%9a%84%e6%95%b0%e6%8d%ae%e5%8f%af%e8%a7%86%e5%8c%96%e6%af%94%e8%be%83/</link>
		
		<dc:creator><![CDATA[萧邦]]></dc:creator>
		<pubDate>Tue, 05 Aug 2025 04:05:57 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[d3.js]]></category>
		<category><![CDATA[echart.js]]></category>
		<category><![CDATA[Lightweight Charts]]></category>
		<guid isPermaLink="false">https://www.zlhlab.top/?p=768</guid>

					<description><![CDATA[数据可视化是现代 Web 应用的重要组成部分，开发者可以通过直观的方式展示复杂数据。在众多 JavaScrip<div class="more-link">
				 <a href="https://www.zlhlab.top/d3-js%e3%80%81echarts-%e5%92%8c-lightweight-charts-%e7%9a%84%e6%95%b0%e6%8d%ae%e5%8f%af%e8%a7%86%e5%8c%96%e6%af%94%e8%be%83/" class="link-btn theme-btn"><span>Read More </span> <i class="fa fa-caret-right"></i></a>
			</div>]]></description>
										<content:encoded><![CDATA[
<p>数据可视化是现代 Web 应用的重要组成部分，开发者可以通过直观的方式展示复杂数据。在众多 JavaScript 可视化库中，<strong>D3.js</strong>、<strong>ECharts</strong> 和 <strong>Lightweight Charts</strong> 以其独特优势和适用场景脱颖而出。本文从 <strong>功能</strong>、<strong>易用性</strong>、<strong>性能</strong>、<strong>定制化</strong> 和 <strong>适用场景</strong> 五个方面比较这三个库，帮助开发者选择适合项目的工具。每部分包含示例代码和可视化效果图以展示其能力。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">1. 库概览</h2>



<h3 class="wp-block-heading">D3.js</h3>



<ul class="wp-block-list">
<li><strong>全称</strong>：Data-Driven Documents（数据驱动文档）</li>



<li><strong>发布</strong>：2011 年由 Mike Bostock 开发</li>



<li><strong>许可证</strong>：BSD 许可证（开源，免费用于个人和商业用途）</li>



<li><strong>渲染</strong>：SVG、HTML 和 CSS</li>



<li><strong>描述</strong>：D3.js 是一个低级、灵活性极高的库，通过将数据绑定到 DOM，允许开发者创建自定义可视化。它不是严格意义上的图表库，而是用于基于数据操作 DOM 的工具集，适合复杂和定制化的可视化。</li>
</ul>



<h3 class="wp-block-heading">ECharts</h3>



<ul class="wp-block-list">
<li><strong>全称</strong>：Enterprise Charts（企业级图表）</li>



<li><strong>发布</strong>：由百度开发，基于 Apache 2.0 开源</li>



<li><strong>许可证</strong>：Apache 2.0 许可证（开源，免费用于个人和商业用途）</li>



<li><strong>渲染</strong>：Canvas，支持可选 SVG</li>



<li><strong>描述</strong>：ECharts 是一个高级图表库，注重易用性和快速开发，提供多种预构建图表类型和交互功能，适合需要快速构建精美可视化的开发者。</li>
</ul>



<h3 class="wp-block-heading">Lightweight Charts</h3>



<ul class="wp-block-list">
<li><strong>全称</strong>：TradingView Lightweight Charts</li>



<li><strong>发布</strong>：由 TradingView 开发</li>



<li><strong>许可证</strong>：Apache 2.0 许可证（开源，免费用于个人和商业用途）</li>



<li><strong>渲染</strong>：Canvas</li>



<li><strong>描述</strong>：Lightweight Charts 是一个专为金融和时间序列数据可视化设计的库，优化了股票或加密货币图表等场景。它注重性能和简单性，适合特定用例的轻量级解决方案。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">2. 功能比较</h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>功能</th><th>D3.js</th><th>ECharts</th><th>Lightweight Charts</th></tr></thead><tbody><tr><td><strong>图表类型</strong></td><td>自定义（支持任何类型）</td><td>50+ 预构建图表类型（柱状、折线、饼图等）</td><td>主要支持 K 线图、折线图、面积图和直方图</td></tr><tr><td><strong>交互性</strong></td><td>高度可定制（自定义事件）</td><td>内置（缩放、平移、提示框等）</td><td>内置（缩放、平移、提示框等）</td></tr><tr><td><strong>渲染</strong></td><td>SVG（可缩放，基于 DOM）</td><td>Canvas（可选 SVG）</td><td>Canvas</td></tr><tr><td><strong>性能</strong></td><td>依赖优化</td><td>优化用于大数据集</td><td>针对时间序列高度优化</td></tr><tr><td><strong>学习曲线</strong></td><td>陡峭（需掌握 JS/SVG 知识）</td><td>中等（基于 JSON 配置）</td><td>简单（API 简洁）</td></tr><tr><td><strong>社区/支持</strong></td><td>庞大，但多为英文资源</td><td>活跃，文档和示例详尽</td><td>较小，专注金融场景</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">3. 易用性</h2>



<h3 class="wp-block-heading">D3.js</h3>



<p>D3.js <strong>学习曲线陡峭</strong>，因为它是低级库。开发者需要深入了解 JavaScript、SVG 和数据绑定模式（如 enter-update-exit）。创建简单图表也需要手动定义比例尺、坐标轴和 DOM 操作，灵活性高但工作量大。</p>



<p><strong>示例：D3.js 柱状图</strong></p>



<pre class="wp-block-code"><code>// 全局变量
        let data = &#91;
            { genre: '周一', sold: 120, color: '#667eea' },
            { genre: '周二', sold: 200, color: '#764ba2' },
            { genre: '周三', sold: 150, color: '#f093fb' },
            { genre: '周四', sold: 80, color: '#f5576c' },
            { genre: '周五', sold: 70, color: '#4facfe' },
            { genre: '周六', sold: 110, color: '#00f2fe' },
            { genre: '周日', sold: 130, color: '#43e97b' },
        ];
        
        let animationEnabled = true;
        let currentTheme = 'default';
        
        // 图表配置
        const margin = { top: 40, right: 30, bottom: 60, left: 60 };
        const width = 800 - margin.left - margin.right;
        const height = 500 - margin.top - margin.bottom;
        
        // 创建SVG
        const svg = d3.select("#d3container")
            .append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", `translate(${margin.left},${margin.top})`);
        
        // 创建比例尺
        const xScale = d3.scaleBand()
            .range(&#91;0, width])
            .padding(0.1);
        
        const yScale = d3.scaleLinear()
            .range(&#91;height, 0]);
        
        // 创建坐标轴
        const xAxis = d3.axisBottom(xScale);
        const yAxis = d3.axisLeft(yScale).ticks(5);
        
        // 添加坐标轴
        svg.append("g")
            .attr("class", "x-axis")
            .attr("transform", `translate(0,${height})`)
            .call(xAxis);
        
        svg.append("g")
            .attr("class", "y-axis")
            .call(yAxis);
        
        // 添加标题
        svg.append("text")
            .attr("class", "chart-title")
            .attr("x", width / 2)
            .attr("y", -10)
            .attr("text-anchor", "middle")
            .style("font-size", "16px")
            .style("font-weight", "bold")
            .text("每周销售数据");
        
</code></pre>



<p><strong>输出</strong>：<br></p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="720" data-id="770" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-11.41.57-1024x720.png" alt="" class="wp-image-770" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-11.41.57-1024x720.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-11.41.57-300x211.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-11.41.57-768x540.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-11.41.57.png 1200w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
</figure>



<h3 class="wp-block-heading">ECharts</h3>



<p>ECharts <strong>易于使用</strong>，学习曲线较为平缓。它通过 JSON 配置定义图表，开发者只需掌握基础 JavaScript 知识即可快速上手。库内置了渲染和交互功能，适合快速开发复杂可视化。</p>



<p><strong>示例：ECharts 柱状图</strong></p>



<pre class="wp-block-code"><code>        // 数据
        let chartData = {
            categories: &#91;'周一', '周二', '周三', '周四', '周五', '周六', '周日'],
            values: &#91;120, 200, 150, 80, 70, 110, 130],
            pieData: &#91;
                { value: 120, name: '周一' },
                { value: 200, name: '周二' },
                { value: 150, name: '周三' },
                { value: 80, name: '周四' },
                { value: 70, name: '周五' },
                { value: 110, name: '周六' },
                { value: 130, name: '周日' }
            ]
        };
        
        // 主题配置
        const themes = {
            default: {
                color: &#91;'#667eea', '#764ba2', '#f093fb', '#f5576c', '#4facfe', '#00f2fe', '#43e97b'],
                backgroundColor: '#ffffff'
            },
            warm: {
                color: &#91;'#ff9a9e', '#fecfef', '#fecfef', '#ffc3a0', '#ffafbd', '#ffc3a0', '#ff9a9e'],
                backgroundColor: '#fff5f5'
            },
            cool: {
                color: &#91;'#a8edea', '#fed6e3', '#a8edea', '#fed6e3', '#a8edea', '#fed6e3', '#a8edea'],
                backgroundColor: '#f0f8ff'
            },
            dark: {
                color: &#91;'#636363', '#a2ab58', '#636363', '#a2ab58', '#636363', '#a2ab58', '#636363'],
                backgroundColor: '#2a2a2a'
            }
        };
</code></pre>



<p><strong>输出</strong>：</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="697" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-11.48.40-1024x697.png" alt="" class="wp-image-772" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-11.48.40-1024x697.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-11.48.40-300x204.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-11.48.40-768x523.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-11.48.40.png 1198w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading">Lightweight Charts</h3>



<p>Lightweight Charts 专为<strong>简单性</strong>设计，适合时间序列数据，是最易上手的库。其 API 简单直接，专注于金融图表（如 K 线图或折线图），适合快速配置和部署。</p>



<p><strong>示例：Lightweight Charts K 线图</strong></p>



<pre class="wp-block-code"><code>const chartOptions = { layout: { textColor: 'black', background: { type: 'solid', color: 'white' } } };
const chart = createChart(document.getElementById('container'), chartOptions);
const areaSeries = chart.addSeries(AreaSeries, {
    lineColor: '#2962FF', topColor: '#2962FF',
    bottomColor: 'rgba(41, 98, 255, 0.28)',
});
areaSeries.setData(&#91;
    { time: '2018-12-22', value: 32.51 },
    { time: '2018-12-23', value: 31.11 },
    { time: '2018-12-24', value: 27.02 },
    { time: '2018-12-25', value: 27.32 },
    { time: '2018-12-26', value: 25.17 },
    { time: '2018-12-27', value: 28.89 },
    { time: '2018-12-28', value: 25.46 },
    { time: '2018-12-29', value: 23.92 },
    { time: '2018-12-30', value: 22.68 },
    { time: '2018-12-31', value: 22.67 },
]);

const candlestickSeries = chart.addSeries(CandlestickSeries, {
    upColor: '#26a69a', downColor: '#ef5350', borderVisible: false,
    wickUpColor: '#26a69a', wickDownColor: '#ef5350',
});
candlestickSeries.setData(&#91;
    { time: '2018-12-22', open: 75.16, high: 82.84, low: 36.16, close: 45.72 },
    { time: '2018-12-23', open: 45.12, high: 53.90, low: 45.12, close: 48.09 },
    { time: '2018-12-24', open: 60.71, high: 60.71, low: 53.39, close: 59.29 },
    { time: '2018-12-25', open: 68.26, high: 68.26, low: 59.04, close: 60.50 },
    { time: '2018-12-26', open: 67.71, high: 105.85, low: 66.67, close: 91.04 },
    { time: '2018-12-27', open: 91.04, high: 121.40, low: 82.70, close: 111.40 },
    { time: '2018-12-28', open: 111.51, high: 142.83, low: 103.34, close: 131.25 },
    { time: '2018-12-29', open: 131.33, high: 151.17, low: 77.68, close: 96.43 },
    { time: '2018-12-30', open: 106.33, high: 110.20, low: 90.39, close: 98.10 },
    { time: '2018-12-31', open: 109.87, high: 114.69, low: 85.66, close: 111.26 },
]);

chart.timeScale().fitContent();</code></pre>



<p><strong>输出</strong>：</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="338" src="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-12.05.32-1024x338.png" alt="" class="wp-image-773" srcset="https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-12.05.32-1024x338.png 1024w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-12.05.32-300x99.png 300w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-12.05.32-768x254.png 768w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-12.05.32-1536x508.png 1536w, https://www.zlhlab.top/wp-content/uploads/2025/08/截屏2025-08-05-12.05.32.png 1628w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">4. 性能</h2>



<ul class="wp-block-list">
<li><strong>D3.js</strong>：性能取决于开发者的优化。基于 SVG 的渲染在大规模数据集（数千元素）时可能较慢，因 DOM 操作开销大。但通过优化（如使用 Canvas 或数据聚合），可实现高效复杂可视化。</li>



<li><strong>ECharts</strong>：针对大数据集优化，默认使用 Canvas 渲染，支持 WebGL 进一步提升性能。适合处理高数据量场景，交互和动画流畅。</li>



<li><strong>Lightweight Charts</strong>：专为性能设计，特别适合时间序列数据。其轻量级架构和 Canvas 渲染使其在实时金融图表中表现出色，但不适合非时间序列可视化。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">5. 定制化</h2>



<ul class="wp-block-list">
<li><strong>D3.js</strong>：提供无与伦比的定制化能力。开发者可控制可视化的每个细节，从形状到动画，适合独特设计。但这需要更多开发时间。</li>



<li><strong>ECharts</strong>：在易用性和定制化之间取得平衡。通过 JSON 配置提供丰富的选项（如主题、提示框、交互），但对完全自定义设计的灵活性不如 D3.js。</li>



<li><strong>Lightweight Charts</strong>：定制化能力有限，专注于金融图表美学，提供预定义样式。开发者可调整颜色、比例尺和基本交互，但灵活性较低。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">6. 适用场景</h2>



<ul class="wp-block-list">
<li><strong>D3.js</strong>：适合<strong>复杂、自定义可视化</strong>，如交互式网络图、力导向图或定制化仪表板。适合需要完全控制和独特设计的项目。</li>



<li><strong>ECharts</strong>：适合<strong>快速开发标准图表</strong>（柱状、折线、饼图等），内置交互功能。适用于商业应用、仪表板和需要跨浏览器兼容的项目。</li>



<li><strong>Lightweight Charts</strong>：优化用于<strong>金融和时间序列可视化</strong>，如股票或加密货币图表。适合需要高性能和简单配置的交易或分析平台。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">7. 结论</h2>



<p>选择 D3.js、ECharts 或 Lightweight Charts 取决于项目需求和团队技术水平：</p>



<ul class="wp-block-list">
<li><strong>选择 D3.js</strong>：如果需要最大灵活性且能接受陡峭的学习曲线，适合自定义、数据驱动的可视化，但开发工作量较大。</li>



<li><strong>选择 ECharts</strong>：如果需要在易用性、速度和定制化之间平衡，适合快速构建交互式图表，无需深入 JavaScript 知识。</li>



<li><strong>选择 Lightweight Charts</strong>：如果专注于高性能金融或时间序列可视化，适合需要最少配置的特定图表类型。</li>
</ul>



<p>每个库在特定领域表现出色，了解其优势可帮助您为数据可视化需求选择合适的工具。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">参考资料</h2>



<p><a href="https://blog.csdn.net/m0_57344393/article/details/135813534">CSDN：一文读懂：D3.js 的前世今生，以及与 ECharts 的对比</a></p>



<p><a href="https://npm-compare.com/zh-CN/chart.js,d3,echarts,highcharts">npm-compare.com：chart.js、d3、highcharts、echarts 的比较</a></p>



<p><a href="https://modstart.com/p/mlkbb2qa0p74jnvt">ModStart：ECharts 与 D3.js 的对比与选择</a></p>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>大前端完整学习路线（详解）</title>
		<link>https://www.zlhlab.top/%e5%a4%a7%e5%89%8d%e7%ab%af%e5%ae%8c%e6%95%b4%e5%ad%a6%e4%b9%a0%e8%b7%af%e7%ba%bf%ef%bc%88%e8%af%a6%e8%a7%a3%ef%bc%89/</link>
		
		<dc:creator><![CDATA[萧邦]]></dc:creator>
		<pubDate>Wed, 30 Jul 2025 07:19:49 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">https://www.zlhlab.top/?p=607</guid>

					<description><![CDATA[第一阶段：基础入门 HTML+CSS HTML进阶：语义化标签、表单验证、多媒体元素 CSS进阶：选择器、盒模<div class="more-link">
				 <a href="https://www.zlhlab.top/%e5%a4%a7%e5%89%8d%e7%ab%af%e5%ae%8c%e6%95%b4%e5%ad%a6%e4%b9%a0%e8%b7%af%e7%ba%bf%ef%bc%88%e8%af%a6%e8%a7%a3%ef%bc%89/" class="link-btn theme-btn"><span>Read More </span> <i class="fa fa-caret-right"></i></a>
			</div>]]></description>
										<content:encoded><![CDATA[
<h2>第一阶段：基础入门</h2>

<h3>HTML+CSS</h3>
<ul>
<li><strong>HTML进阶</strong>：语义化标签、表单验证、多媒体元素</li>
<li><strong>CSS进阶</strong>：选择器、盒模型、定位、浮动、弹性布局</li>
<li><strong>布局技术</strong>：div+css布局、响应式设计</li>
<li><strong>项目实践</strong>：HTML+css整站开发</li>
</ul>

<h3>JavaScript基础</h3>
<ul>
<li><strong>JS基础教程</strong>：语法、数据类型、运算符、控制结构</li>
<li><strong>内置对象</strong>：常用方法、数组、字符串、日期对象</li>
<li><strong>DOM操作</strong>：常见DOM树操作大全、节点操作</li>
<li><strong>核心概念</strong>：ECMAscript、DOM、BOM</li>
<li><strong>交互功能</strong>：定时器和焦点图</li>
</ul>

<h3>JS基本特效</h3>
<ul>
<li><strong>常见特效</strong>：tab切换、导航菜单、整页滚动</li>
<li><strong>动画效果</strong>：轮播图、JS制作幻灯片、弹出层</li>
<li><strong>交互组件</strong>：手风琴菜单、瀑布流布局</li>
<li><strong>事件处理</strong>：滚动事件、滚差视图</li>
</ul>

<h3>JS高级特征</h3>
<ul>
<li><strong>正则表达式</strong>：模式匹配、字符串处理</li>
<li><strong>算法基础</strong>：排序算法、递归算法</li>
<li><strong>函数进阶</strong>：闭包、函数节流、作用域链</li>
<li><strong>动画框架</strong>：基于距离运动框架</li>
<li><strong>面向对象</strong>：面向对象基础</li>
</ul>

<h3>jQuery基础</h3>
<ul>
<li><strong>选择器</strong>：各种选择器的使用</li>
<li><strong>DOM操作</strong>：元素增删改查</li>
<li><strong>特效动画</strong>：特效和动画、方法链</li>
<li><strong>交互功能</strong>：拖拽、变形</li>
<li><strong>UI组件</strong>：JQueryUI组件基本使用</li>
</ul>

<h2>第二阶段：HTML5和移动Web开发</h2>

<h3>HTML5</h3>
<ul>
<li><strong>新语义标签</strong>：header、nav、section、article等</li>
<li><strong>表单增强</strong>：HTML5表单、验证、新输入类型</li>
<li><strong>多媒体</strong>：音频和视频、Canvas绘图</li>
<li><strong>存储技术</strong>：离线和本地存储</li>
<li><strong>图形技术</strong>：SVG矢量图形</li>
<li><strong>通信技术</strong>：Web Socket实时通信</li>
</ul>

<h3>CSS3</h3>
<ul>
<li><strong>新选择器</strong>：CSS3新选择器、伪元素</li>
<li><strong>颜色表示</strong>：颜色表示法、透明度</li>
<li><strong>视觉效果</strong>：边框、阴影、background系列属性</li>
<li><strong>动画技术</strong>：Transition、动画、景深和透视</li>
<li><strong>3D效果</strong>：3D效果制作、Velocity.js框架</li>
<li><strong>交互策略</strong>：元素进场、出场策略</li>
<li><strong>项目实践</strong>：炫酷CSS3网页制作</li>
</ul>

<h3>Bootstrap</h3>
<ul>
<li><strong>响应式概念</strong>：响应式设计原理</li>
<li><strong>媒体查询</strong>：断点设置、适配策略</li>
<li><strong>栅格系统</strong>：栅格系统原理、Bootstrap常用模板</li>
<li><strong>预处理器</strong>：LESS和SASS</li>
</ul>

<h3>移动Web开发</h3>
<ul>
<li><strong>跨终端适配</strong>：跨终端WEB和主流设备简介</li>
<li><strong>视口技术</strong>：视口设置、移动端适配</li>
<li><strong>布局技术</strong>：流式布局、弹性盒子、rem单位</li>
<li><strong>事件处理</strong>：移动终端JavaScript事件</li>
<li><strong>特效制作</strong>：手机中常见JS效果制作</li>
<li><strong>框架应用</strong>：Zepto.js、手机聚划算页面、手机滚屏</li>
</ul>

<h2>第三阶段：HTTP服务和AJAX编程</h2>

<h3>WEB服务器基础</h3>
<ul>
<li><strong>服务器知识</strong>：服务器基础知识</li>
<li><strong>服务器类型</strong>：Apache服务器和其他WEB服务器介绍</li>
<li><strong>环境搭建</strong>：Apache服务器搭建</li>
<li><strong>协议基础</strong>：HTTP介绍</li>
</ul>

<h3>PHP基础</h3>
<ul>
<li><strong>语法基础</strong>：PHP基础语法</li>
<li><strong>请求处理</strong>：使用PHP处理简单的GET或者POST请求</li>
</ul>

<h3>AJAX上篇</h3>
<ul>
<li><strong>异步概念</strong>：Ajax简介和异步的概念</li>
<li><strong>框架封装</strong>：Ajax框架的封装</li>
<li><strong>核心对象</strong>：XMLHttpRequest对象详细介绍方法</li>
<li><strong>兼容处理</strong>：兼容性处理方法</li>
<li><strong>缓存问题</strong>：Ajax中缓存问题</li>
<li><strong>数据格式</strong>：XML介绍和使用</li>
</ul>

<h3>AJAX下篇</h3>
<ul>
<li><strong>JSON技术</strong>：JSON和JSON解析</li>
<li><strong>模板技术</strong>：数据绑定和模板技术</li>
<li><strong>跨域技术</strong>：JSONP、跨域技术</li>
<li><strong>性能优化</strong>：图片预读取和lazy-load技术</li>
<li><strong>框架应用</strong>：JQuery框架中的AjaxAPI</li>
<li><strong>项目实践</strong>：使用Ajax实现瀑布流案例</li>
</ul>

<h2>第四阶段：面向对象进阶</h2>

<h3>面向对象终极篇</h3>
<ul>
<li><strong>内存理解</strong>：从内存角度理解JS面向对象</li>
<li><strong>数据类型</strong>：基本类型、复杂类型</li>
<li><strong>原型链</strong>：原型链机制</li>
<li><strong>ES6特性</strong>：ES6中的面向对象</li>
<li><strong>属性控制</strong>：属性读写权限、设置器、访问器</li>
</ul>

<h3>面向对象三大特征</h3>
<ul>
<li><strong>继承性</strong>：原型继承、类继承</li>
<li><strong>多态性</strong>：方法重写、接口实现</li>
<li><strong>封装性</strong>：私有属性、模块化</li>
</ul>

<h3>设计模式</h3>
<ul>
<li><strong>编程思维</strong>：面向对象编程思维</li>
<li><strong>创建模式</strong>：单例模式、工厂模式</li>
<li><strong>结构模式</strong>：策略模式、适配器模式、装饰者模式</li>
<li><strong>行为模式</strong>：观察者模式、模板方法模式</li>
<li><strong>架构模式</strong>：代理模式、面向切面编程</li>
</ul>

<h2>第五阶段：封装一个属于自己的框架</h2>

<h3>框架封装基础</h3>
<ul>
<li><strong>事件机制</strong>：事件流、冒泡、捕获</li>
<li><strong>事件对象</strong>：事件对象、事件框架</li>
<li><strong>选择框架</strong>：选择框架设计</li>
</ul>

<h3>框架封装中级</h3>
<ul>
<li><strong>运动原理</strong>：运动原理、单物体运动框架</li>
<li><strong>多物体运动</strong>：多物体运动框架</li>
<li><strong>面向对象</strong>：运动框架面向对象封装</li>
</ul>

<h3>框架封装高级和补充</h3>
<ul>
<li><strong>框架雏形</strong>：JQuery框架雏形</li>
<li><strong>扩展性</strong>：可扩展性、模块化</li>
<li><strong>项目实践</strong>：封装属于自己的框架</li>
</ul>

<h2>第六阶段：模块化组件开发</h2>

<h3>面向组件编程</h3>
<ul>
<li><strong>组件概念</strong>：面向组件编程的方式</li>
<li><strong>实现原理</strong>：面向组件编程的实现原理</li>
<li><strong>实战应用</strong>：面向组件编程实战</li>
<li><strong>项目开发</strong>：基于组件化思想开发网站应用程序</li>
</ul>

<h3>面向模块编程</h3>
<ul>
<li><strong>AMD规范</strong>：AMD设计规范</li>
<li><strong>CMD规范</strong>：CMD设计规范</li>
<li><strong>模块加载</strong>：RequireJS、LoadJS</li>
<li><strong>淘宝方案</strong>：淘宝的SeaJS</li>
</ul>

<h2>第七阶段：主流的流行框架</h2>

<h3>Web开发工作流</h3>
<ul>
<li><strong>版本控制</strong>：GIT/SVN</li>
<li><strong>脚手架</strong>：Yeoman脚手架</li>
<li><strong>依赖管理</strong>：NPM/Bower依赖管理工具</li>
<li><strong>构建工具</strong>：Grunt/Gulp/Webpack</li>
</ul>

<h3>MVC/MVVM/MVW框架</h3>
<ul>
<li><strong>Angular.js</strong>：Google的MVVM框架</li>
<li><strong>Backbone.js</strong>：轻量级MVC框架</li>
<li><strong>Knockout/Ember</strong>：数据绑定框架</li>
</ul>

<h3>常用库</h3>
<ul>
<li><strong>React.js</strong>：Facebook的UI库</li>
<li><strong>Vue.js</strong>：渐进式JavaScript框架</li>
<li><strong>Zepto.js</strong>：移动端jQuery替代品</li>
</ul>

<h2>第八阶段：HTML5原生移动应用开发</h2>

<h3>Cordova</h3>
<ul>
<li><strong>应用类型</strong>：WebApp/NativeApp/HybirdApp简介</li>
<li><strong>框架介绍</strong>：Cordova简介、与PhoneGap之间的关系</li>
<li><strong>环境搭建</strong>：开发环境搭建</li>
<li><strong>实战开发</strong>：Cordova实战（创建项目，配置，编译，调试，部署发布）</li>
</ul>

<h3>Ionic</h3>
<ul>
<li><strong>框架对比</strong>：Ionic简介和同类对比</li>
<li><strong>项目解析</strong>：模板项目解析</li>
<li><strong>组件使用</strong>：常见组件及使用</li>
<li><strong>Angular集成</strong>：结合Angular构建APP</li>
<li><strong>交互效果</strong>：常见效果（下拉刷新，上拉加载，侧滑导航，选项卡）</li>
</ul>

<h3>React Native</h3>
<ul>
<li><strong>框架介绍</strong>：React Native简介</li>
<li><strong>环境配置</strong>：React Native环境配置</li>
<li><strong>项目开发</strong>：创建项目，配置，编译，调试，部署发布</li>
<li><strong>原生集成</strong>：原生模块和UI组件、原生常用API</li>
</ul>

<h3>HTML5+</h3>
<ul>
<li><strong>产业联盟</strong>：HTML5+中国产业联盟</li>
<li><strong>运行环境</strong>：HTML5 Plus Runtime环境</li>
<li><strong>开发工具</strong>：HBuilder开发工具</li>
<li><strong>框架应用</strong>：MUI框架、H5+开发和部署</li>
</ul>

<h2>第九阶段：Node.js全栈开发</h2>

<h3>快速入门</h3>
<ul>
<li><strong>发展历程</strong>：Node.js发展、生态圈</li>
<li><strong>环境配置</strong>：Io.js、Linux/Windows/OS X环境配置</li>
<li><strong>基础概念</strong>：REPL环境和控制台程序</li>
<li><strong>异步编程</strong>：异步编程，非阻塞I/O</li>
<li><strong>模块管理</strong>：模块概念，模块管理工具</li>
<li><strong>开发流程</strong>：开发流程，调试，测试</li>
</ul>

<h3>核心模块和对象</h3>
<ul>
<li><strong>全局对象</strong>：global，process，console，util</li>
<li><strong>事件机制</strong>：事件驱动，事件发射器</li>
<li><strong>数据处理</strong>：加密解密，路径操作，序列化和反序列化</li>
<li><strong>文件操作</strong>：文件流操作</li>
<li><strong>网络通信</strong>：HTTP服务端与客户端、Socket.IO</li>
</ul>

<h3>Web开发基础</h3>
<ul>
<li><strong>HTTP协议</strong>：HTTP协议，请求响应处理过程</li>
<li><strong>数据库操作</strong>：关系型数据库操作和数据访问、非关系型数据库操作和数据访问</li>
<li><strong>原生开发</strong>：原生的Node.js开发Web应用程序</li>
<li><strong>工作流</strong>：Web开发工作流</li>
<li><strong>项目实践</strong>：Node.js开发Blog案例</li>
</ul>

<h3>快速开发框架</h3>
<ul>
<li><strong>Express框架</strong>：Express简介+MVC简介</li>
<li><strong>API使用</strong>：Express常用API</li>
<li><strong>路由模块</strong>：Express路由模块</li>
<li><strong>模板引擎</strong>：Jade/Ejs模板引擎</li>
<li><strong>项目重构</strong>：使用Express重构Blog案例</li>
<li><strong>其他框架</strong>：Koa等其他常见MVC框架</li>
</ul>

<h3>Node.js开发电子商务实战</h3>
<ul>
<li><strong>需求设计</strong>：需求与设计</li>
<li><strong>用户系统</strong>：账户模块注册登录、会员中心模块</li>
<li><strong>前台展示</strong>：前台展示模块</li>
<li><strong>购物功能</strong>：购物车，订单结算</li>
<li><strong>即时通讯</strong>：在线客服即时通讯模块</li>
</ul>

<hr>

<p><strong>原文链接地址：</strong> <a href="https://blog.csdn.net/u011047006/article/details/52597178">https://blog.csdn.net/u011047006/article/details/52597178</a></p>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>使用masonry实现流式布局和滚动加载</title>
		<link>https://www.zlhlab.top/%e4%bd%bf%e7%94%a8masonry%e5%ae%9e%e7%8e%b0%e6%b5%81%e5%bc%8f%e5%b8%83%e5%b1%80%e5%92%8c%e6%bb%9a%e5%8a%a8%e5%8a%a0%e8%bd%bd/</link>
		
		<dc:creator><![CDATA[萧邦]]></dc:creator>
		<pubDate>Wed, 30 Jul 2025 02:23:35 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<guid isPermaLink="false">https://www.zlhlab.top/?p=532</guid>

					<description><![CDATA[最近在制作一个门户网站，其中案例部分采用的是流式布局设计。 实现效果如下图： 参考文章地址： https://<div class="more-link">
				 <a href="https://www.zlhlab.top/%e4%bd%bf%e7%94%a8masonry%e5%ae%9e%e7%8e%b0%e6%b5%81%e5%bc%8f%e5%b8%83%e5%b1%80%e5%92%8c%e6%bb%9a%e5%8a%a8%e5%8a%a0%e8%bd%bd/" class="link-btn theme-btn"><span>Read More </span> <i class="fa fa-caret-right"></i></a>
			</div>]]></description>
										<content:encoded><![CDATA[<p>最近在制作一个门户网站，其中案例部分采用的是流式布局设计。</p>
<p>实现效果如下图：</p>
<p><img decoding="async" src="https://www.zlhlab.top/wp-content/uploads/2018/02/3.jpg" alt=""></p>
<h4>参考文章地址：</h4>
<p>https://masonry.desandro.com<br />
https://infinite-scroll.com/options.html</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>React/React Native 的ES5 ES6写法对照表</title>
		<link>https://www.zlhlab.top/reactreact-native-%e7%9a%84es5-es6%e5%86%99%e6%b3%95%e5%af%b9%e7%85%a7%e8%a1%a8/</link>
		
		<dc:creator><![CDATA[萧邦]]></dc:creator>
		<pubDate>Wed, 21 Feb 2018 09:50:40 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[ES6]]></category>
		<guid isPermaLink="false">https://www.zlhlab.top/?p=524</guid>

					<description><![CDATA[很多React/React Native的初学者都被ES6的问题迷惑：各路大神都建议我们直接学习ES6的语法(class Foo extends React.Component)，然而网上搜到的很多教程和例子都是ES5版本的，所以很多人在学习的时候连照猫画虎都不知道怎么做。]]></description>
										<content:encoded><![CDATA[<p>很多React/React Native的初学者都被ES6的问题迷惑：各路大神都建议我们直接学习ES6的语法(class Foo extends React.Component)，然而网上搜到的很多教程和例子都是ES5版本的，所以很多人在学习的时候连照猫画虎都不知道怎么做。今天在此整理了一些ES5和ES6的写法对照表，希望大家以后读到ES5的代码，也能通过对照，在ES6下实现相同的功能。</p>
<hr>
<h4>模块</h4>
<p><strong>引用</strong></p>
<p>在ES5里，如果使用CommonJS标准，引入React包基本通过require进行，代码类似这样：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES5
var React = require("react");
var {
    Component,
    PropTypes
} = React;  //引用React抽象组件

var ReactNative = require("react-native");
var {
    Image,
    Text,
} = ReactNative;  //引用具体的React Native组件
</code></pre>
<p>在ES6里，import写法更为标准</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES6
import React, { 
    Component,
    PropTypes,
} from 'react';
import {
    Image,
    Text
} from 'react-native'
导出单个类
</code></pre>
<p>在ES5里，要导出一个类给别的模块用，一般通过module.exports来导出</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES5
var MyComponent = React.createClass({
    ...
});
module.exports = MyComponent;
</code></pre>
<p>在ES6里，通常用export default来实现相同的功能：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES6
export default class MyComponent extends Component{
    ...
}
</code></pre>
<p>引用的时候也类似：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES5
var MyComponent = require('./MyComponent');

//ES6
import MyComponent from './MyComponent';
</code></pre>
<p>注意导入和导出的写法必须配套，不能混用！</p>
<h4>定义组件</h4>
<p>在ES5里，通常通过React.createClass来定义一个组件类，像这样：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES5
var Photo = React.createClass({
    render: function() {
        return (
            &lt;Image source={this.props.source} /&gt;
        );
    },
});
</code></pre>
<p>在ES6里，我们通过定义一个继承自React.Component的class来定义一个组件类，像这样：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES6
class Photo extends React.Component {
    render() {
        return (
            &lt;Image source={this.props.source} /&gt;
        );
    }
}
</code></pre>
<h4>给组件定义方法</h4>
<p>从上面的例子里可以看到，给组件定义方法不再用 名字: function()的写法，而是直接用名字()，在方法的最后也不能有逗号了。</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES5
var Photo = React.createClass({
    componentWillMount: function(){

    },
    render: function() {
        return (
            &lt;Image source={this.props.source} /&gt;
        );
    },
});
</code></pre>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES6
class Photo extends React.Component {
    componentWillMount() {

    }
    render() {
        return (
            &lt;Image source={this.props.source} /&gt;
        );
    }
}
</code></pre>
<h4>定义组件的属性类型和默认属性</h4>
<p>在ES5里，属性类型和默认属性分别通过propTypes成员和getDefaultProps方法来实现</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES5
var Video = React.createClass({
    getDefaultProps: function() {
        return {
            autoPlay: false,
            maxLoops: 10,
        };
    },
    propTypes: {
        autoPlay: React.PropTypes.bool.isRequired,
        maxLoops: React.PropTypes.number.isRequired,
        posterFrameSrc: React.PropTypes.string.isRequired,
        videoSrc: React.PropTypes.string.isRequired,
    },
    render: function() {
        return (
            &lt;View /&gt;
        );
    },
});
</code></pre>
<p>在ES6里，可以统一使用static成员来实现</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES6
class Video extends React.Component {
    static defaultProps = {
        autoPlay: false,
        maxLoops: 10,
    };  // 注意这里有分号
    static propTypes = {
        autoPlay: React.PropTypes.bool.isRequired,
        maxLoops: React.PropTypes.number.isRequired,
        posterFrameSrc: React.PropTypes.string.isRequired,
        videoSrc: React.PropTypes.string.isRequired,
    };  // 注意这里有分号
    render() {
        return (
            &lt;View /&gt;
        );
    } // 注意这里既没有分号也没有逗号
}
</code></pre>
<p>也有人这么写，虽然不推荐，但读到代码的时候你应当能明白它的意思：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES6
class Video extends React.Component {
    render() {
        return (
            &lt;View /&gt;
        );
    }
}
Video.defaultProps = {
    autoPlay: false,
    maxLoops: 10,
};
Video.propTypes = {
    autoPlay: React.PropTypes.bool.isRequired,
    maxLoops: React.PropTypes.number.isRequired,
    posterFrameSrc: React.PropTypes.string.isRequired,
    videoSrc: React.PropTypes.string.isRequired,
};
</code></pre>
<p>注意: 对React开发者而言，static成员在IE10及之前版本不能被继承，而在IE11和其它浏览器上可以，这有时候会带来一些问题。React Native开发者可以不用担心这个问题。</p>
<h4>初始化STATE</h4>
<p>ES5下情况类似，</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES5
var Video = React.createClass({
    getInitialState: function() {
        return {
            loopsRemaining: this.props.maxLoops,
        };
    },
})
</code></pre>
<p>ES6下，有两种写法：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES6
class Video extends React.Component {
    state = {
        loopsRemaining: this.props.maxLoops,
    }
}
</code></pre>
<p>不过我们推荐更易理解的在构造函数中初始化（这样你还可以根据需要做一些计算）：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES6
class Video extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            loopsRemaining: this.props.maxLoops,
        };
    }
}
</code></pre>
<p>把方法作为回调提供</p>
<p>很多习惯于ES6的用户反而不理解在ES5下可以这么做：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES5
var PostInfo = React.createClass({
    handleOptionsButtonClick: function(e) {
        // Here, 'this' refers to the component instance.
        this.setState({showOptionsModal: true});
    },
    render: function(){
        return (
            &lt;TouchableHighlight onPress={this.handleOptionsButtonClick}&gt;
                &lt;Text&gt;{this.props.label}&lt;/Text&gt;
            &lt;/TouchableHighlight&gt;
        )
    },
});
</code></pre>
<p>在ES5下，React.createClass会把所有的方法都bind一遍，这样可以提交到任意的地方作为回调函数，而this不会变化。但官方现在逐步认为这反而是不标准、不易理解的。</p>
<p>在ES6下，你需要通过bind来绑定this引用，或者使用箭头函数（它会绑定当前scope的this引用）来调用</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//ES6
class PostInfo extends React.Component
{
    handleOptionsButtonClick(e){
        this.setState({showOptionsModal: true});
    }
    render(){
        return (
            &lt;TouchableHighlight 
                onPress={this.handleOptionsButtonClick.bind(this)}
                onPress={e=&gt;this.handleOptionsButtonClick(e)}
                &gt;
                &lt;Text&gt;{this.props.label}&lt;/Text&gt;
            &lt;/TouchableHighlight&gt;
        )
    },
}
</code></pre>
<p>箭头函数实际上是在这里定义了一个临时的函数，箭头函数的箭头=&gt;之前是一个空括号、单个的参数名、或用括号括起的多个参数名，而箭头之后可以是一个表达式（作为函数的返回值），或者是用花括号括起的函数体（需要自行通过return来返回值，否则返回的是undefined）。</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">// 箭头函数的例子
()=&gt;1
v=&gt;v+1
(a,b)=&gt;a+b
()=&gt;{
    alert("foo");
}
e=&gt;{
    if (e == 0){
        return 0;
    }
    return 1000/e;
}
</code></pre>
<p>需要注意的是，不论是bind还是箭头函数，每次被执行都返回的是一个新的函数引用，因此如果你还需要函数的引用去做一些别的事情（譬如卸载监听器），那么你必须自己保存这个引用</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">// 错误的做法
class PauseMenu extends React.Component{
    componentWillMount(){
        AppStateIOS.addEventListener('change', this.onAppPaused.bind(this));
    }
    componentDidUnmount(){
        AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this));
    }
    onAppPaused(event){
    }
}
</code></pre>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">// 正确的做法
class PauseMenu extends React.Component{
    constructor(props){
        super(props);
        this._onAppPaused = this.onAppPaused.bind(this);
    }
    componentWillMount(){
        AppStateIOS.addEventListener('change', this._onAppPaused);
    }
    componentDidUnmount(){
        AppStateIOS.removeEventListener('change', this._onAppPaused);
    }
    onAppPaused(event){
    }
}
</code></pre>
<p>从这个帖子中我们还学习到一种新的做法：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">// 正确的做法
class PauseMenu extends React.Component{
    componentWillMount(){
        AppStateIOS.addEventListener('change', this.onAppPaused);
    }
    componentDidUnmount(){
        AppStateIOS.removeEventListener('change', this.onAppPaused);
    }
    onAppPaused = (event) =&gt; {
        //把方法直接作为一个arrow function的属性来定义，初始化的时候就绑定好了this指针
    }
}
</code></pre>
<h4>Mixins</h4>
<p>在ES5下，我们经常使用mixin来为我们的类添加一些新的方法，譬如PureRenderMixin</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">var PureRenderMixin = require('react-addons-pure-render-mixin');
React.createClass({
  mixins: [PureRenderMixin],

  render: function() {
    return &lt;div className={this.props.className}&gt;foo&lt;/div&gt;;
  }
});
</code></pre>
<p>然而现在官方已经不再打算在ES6里继续推行Mixin，他们说：Mixins Are Dead. Long Live Composition。</p>
<p>尽管如果要继续使用mixin，还是有一些第三方的方案可以用，譬如这个方案</p>
<p>不过官方推荐，对于库编写者而言，应当尽快放弃Mixin的编写方式，上文中提到Sebastian Markbåge的一段代码推荐了一种新的编码方式：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//Enhance.js
import { Component } from "React";

export var Enhance = ComposedComponent =&gt; class extends Component {
    constructor() {
        this.state = { data: null };
    }
    componentDidMount() {
        this.setState({ data: 'Hello' });
    }
    render() {
        return &lt;ComposedComponent {...this.props} data={this.state.data} /&gt;;
    }
};
</code></pre>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">//HigherOrderComponent.js
import { Enhance } from "./Enhance";

class MyComponent {
    render() {
        if (!this.data) return &lt;div&gt;Waiting...&lt;/div&gt;;
        return &lt;div&gt;{this.data}&lt;/div&gt;;
    }
}

export default Enhance(MyComponent); // Enhanced component
</code></pre>
<p>用一个“增强函数”，来某个类增加一些方法，并且返回一个新类，这无疑能实现mixin所实现的大部分需求。</p>
<h4>ES6+带来的其它好处</h4>
<p><strong>解构&amp;属性延展</strong></p>
<p>结合使用ES6+的解构和属性延展，我们给孩子传递一批属性更为方便了。这个例子把className以外的所有属性传递给div标签：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">class AutoloadingPostsGrid extends React.Component {
    render() {
        var {
            className,
            ...others,  // contains all properties of this.props except for className
        } = this.props;
        return (
            &lt;div className={className}&gt;
                &lt;PostsGrid {...others} /&gt;
                &lt;button onClick={this.handleLoadMoreClick}&gt;Load more&lt;/button&gt;
            &lt;/div&gt;
        );
    }
}
</code></pre>
<p>下面这种写法，则是传递所有属性的同时，用覆盖新的className值：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">&lt;div {...this.props} className="override"&gt;
    …
&lt;/div&gt;
</code></pre>
<p>这个例子则相反，如果属性中没有包含className，则提供默认的值，而如果属性中已经包含了，则使用属性中的值</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">&lt;div className="base" {...this.props}&gt;
    …
&lt;/div&gt;
</code></pre>
<p>本文仅作技术分享，原文链接地址：<a href="http://bbs.reactnative.cn/topic/15/react-react-native-%E7%9A%84es5-es6%E5%86%99%E6%B3%95%E5%AF%B9%E7%85%A7%E8%A1%A8/5" title="阅读原文" target="_blank">阅读原文</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>react + redux 完整的项目，同时写一下个人感悟</title>
		<link>https://www.zlhlab.top/react-redux-%e5%ae%8c%e6%95%b4%e7%9a%84%e9%a1%b9%e7%9b%ae%ef%bc%8c%e5%90%8c%e6%97%b6%e5%86%99%e4%b8%80%e4%b8%8b%e4%b8%aa%e4%ba%ba%e6%84%9f%e6%82%9f/</link>
		
		<dc:creator><![CDATA[萧邦]]></dc:creator>
		<pubDate>Wed, 21 Feb 2018 07:54:09 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[react.js]]></category>
		<category><![CDATA[redux]]></category>
		<guid isPermaLink="false">https://www.zlhlab.top/?p=522</guid>

					<description><![CDATA[react的功能其实很单一，主要负责渲染的功能，现有的框架，比如angular是一个大而全的框架，用了angular几乎就不需要用其他工具辅助配合，但是react不一样，他只负责ui渲染，想要做好一个项目，往往需要其他库和工具的配合...]]></description>
										<content:encoded><![CDATA[<p>先附上项目源码地址和原文章地址：<a href="https://github.com/bailicangdu/pxq" title="https://github.com/bailicangdu/pxq" target="_blank">https://github.com/bailicangdu/pxq</a></p>
<h4>做React需要会什么？</h4>
<hr>
<p>react的功能其实很单一，主要负责渲染的功能，现有的框架，比如angular是一个大而全的框架，用了angular几乎就不需要用其他工具辅助配合，但是react不一样，他只负责ui渲染，想要做好一个项目，往往需要其他库和工具的配合，比如用redux来管理数据，react-router管理路由，react已经全面拥抱es6，所以es6也得掌握，webpack就算是不会配置也要会用，要想提高性能，需要按需加载，immutable.js也得用上，还有单元测试。。。。</p>
<h4>React 是什么</h4>
<hr>
<p>用脚本进行DOM操作的代价很昂贵。有个贴切的比喻，把DOM和JavaScript各自想象为一个岛屿，它们之间用收费桥梁连接，js每次访问DOM，都要途径这座桥，并交纳“过桥费”,访问DOM的次数越多，费用也就越高。 因此，推荐的做法是尽量减少过桥的次数，努力待在ECMAScript岛上。因为这个原因react的虚拟dom就显得难能可贵了，它创造了虚拟dom并且将它们储存起来，每当状态发生变化的时候就会创造新的虚拟节点和以前的进行对比，让变化的部分进行渲染。整个过程没有对dom进行获取和操作，只有一个渲染的过程，所以react说是一个ui框架。</p>
<h4>React的组件化</h4>
<hr>
<p>react的一个组件很明显的由dom视图和state数据组成，两个部分泾渭分明。state是数据中心，它的状态决定着视图的状态。这时候发现似乎和我们一直推崇的MVC开发模式有点区别，没了Controller控制器，那用户交互怎么处理，数据变化谁来管理？然而这并不是react所要关心的事情，它只负责ui的渲染。与其他框架监听数据动态改变dom不同，react采用setState来控制视图的更新。setState会自动调用render函数，触发视图的重新渲染，如果仅仅只是state数据的变化而没有调用setState，并不会触发更新。 组件就是拥有独立功能的视图模块，许多小的组件组成一个大的组件，整个页面就是由一个个组件组合而成。它的好处是利于重复利用和维护。</p>
<h4>React的 Diff算法</h4>
<hr>
<p>react的diff算法用在什么地方呢？当组件更新的时候，react会创建一个新的虚拟dom树并且会和之前储存的dom树进行比较，这个比较多过程就用到了diff算法，所以组件初始化的时候是用不到的。react提出了一种假设，相同的节点具有类似的结构，而不同的节点具有不同的结构。在这种假设之上进行逐层的比较，如果发现对应的节点是不同的，那就直接删除旧的节点以及它所包含的所有子节点然后替换成新的节点。如果是相同的节点，则只进行属性的更改。</p>
<p>对于列表的diff算法稍有不同，因为列表通常具有相同的结构，在对列表节点进行删除，插入，排序的时候，单个节点的整体操作远比一个个对比一个个替换要好得多，所以在创建列表的时候需要设置key值，这样react才能分清谁是谁。当然不写key值也可以，但这样通常会报出警告，通知我们加上key值以提高react的性能。</p>
<p><img decoding="async" src="https://www.zlhlab.top/wp-content/uploads/2018/02/diff.png" alt=""></p>
<h4>React组件是怎么来的</h4>
<hr>
<p>组件的创造方法为React.createClass() ——创造一个类，react系统内部设计了一套类系统，利用它来创造react组件。但这并不是必须的，我们还可以用es6的class类来创造组件,这也是Facebook官方推荐的写法。</p>
<p><img decoding="async" src="https://www.zlhlab.top/wp-content/uploads/2018/02/icon_class.png" alt=""></p>
<p>这两种写法实现的功能一样但是原理却是不同，es6的class类可以看作是构造函数的一个语法糖，可以把它当成构造函数来看，extends实现了类之间的继承 —— 定义一个类Main 继承React.Component所有的属性和方法，组件的生命周期函数就是从这来的。constructor是构造器，在实例化对象时调用，super调用了父类的constructor创造了父类的实例对象this，然后用子类的构造函数进行修改。这和es5的原型继承是不同的，原型继承是先创造一个实例化对象this，然后再继承父级的原型方法。了解了这些之后我们在看组件的时候就清楚很多。</p>
<p>当我们使用组件 &lt; Main /&gt; 时，其实是对Main类的实例化——new Main，只不过react对这个过程进行了封装，让它看起来更像是一个标签。</p>
<p>有三点值得注意：<br />
1、定义类名字的首字母必须大写;<br />
2、因为class变成了关键字，类选择器需要用className来代替;<br />
3、类和模块内部默认使用严格模式，所以不需要用use strict指定运行模式。</p>
<h4>组件的生命周期</h4>
<hr>
<p><img decoding="async" src="https://www.zlhlab.top/wp-content/uploads/2018/02/react-lifecycle.png" alt=""></p>
<p>组件在初始化时会触发5个钩子函数：</p>
<h5>1、getDefaultProps()</h5>
<blockquote><p>
  设置默认的props，也可以用dufaultProps设置组件的默认属性。
</p></blockquote>
<h5>2、getInitialState()</h5>
<blockquote><p>
  在使用es6的class语法时是没有这个钩子函数的，可以直接在constructor中定义this.state。此时可以访问this.props。
</p></blockquote>
<h5>3、componentWillMount()</h5>
<blockquote><p>
  组件初始化时只调用，以后组件更新不调用，整个生命周期只调用一次，此时可以修改state。
</p></blockquote>
<h5>4、 render()</h5>
<blockquote><p>
  react最重要的步骤，创建虚拟dom，进行diff算法，更新dom树都在此进行。此时就不能更改state了。
</p></blockquote>
<h5>5、componentDidMount()</h5>
<blockquote><p>
  组件渲染之后调用，可以通过this.getDOMNode()获取和操作dom节点，只调用一次。
</p></blockquote>
<p>在更新时也会触发5个钩子函数：</p>
<h5>6、componentWillReceivePorps(nextProps)</h5>
<blockquote><p>
  组件初始化时不调用，组件接受新的props时调用。
</p></blockquote>
<h5>7、shouldComponentUpdate(nextProps, nextState)</h5>
<blockquote><p>
  react性能优化非常重要的一环。组件接受新的state或者props时调用，我们可以设置在此对比前后两个props和state是否相同，如果相同则返回false阻止更新，因为相同的属性状态一定会生成相同的dom树，这样就不需要创造新的dom树和旧的dom树进行diff算法对比，节省大量性能，尤其是在dom结构复杂的时候。不过调用this.forceUpdate会跳过此步骤。
</p></blockquote>
<h5>8、componentWillUpdata(nextProps, nextState)</h5>
<blockquote><p>
  组件初始化时不调用，只有在组件将要更新时才调用，此时可以修改state
</p></blockquote>
<h5>9、render()</h5>
<blockquote><p>
  不多说
</p></blockquote>
<h5>10、componentDidUpdate()</h5>
<blockquote><p>
  组件初始化时不调用，组件更新完成后调用，此时可以获取dom节点。
</p></blockquote>
<p>还有一个卸载钩子函数</p>
<h5>11、componentWillUnmount()</h5>
<blockquote><p>
  组件将要卸载时调用，一些事件监听和定时器需要在此时清除。
</p></blockquote>
<p>以上可以看出来react总共有10个周期函数（render重复一次），这个10个函数可以满足我们所有对组件操作的需求，利用的好可以提高开发效率和组件性能。</p>
<h4>React-Router路由</h4>
<hr>
<p>Router就是React的一个组件，它并不会被渲染，只是一个创建内部路由规则的配置对象，根据匹配的路由地址展现相应的组件。Route则对路由地址和组件进行绑定，Route具有嵌套功能，表示路由地址的包涵关系，这和组件之间的嵌套并没有直接联系。Route可以向绑定的组件传递7个属性：children，history，location，params，route，routeParams，routes，每个属性都包涵路由的相关的信息。比较常用的有children（以路由的包涵关系为区分的组件），location（包括地址，参数，地址切换方式，key值，hash值）。react-router提供Link标签，这只是对a标签的封装，值得注意的是，点击链接进行的跳转并不是默认的方式，react-router阻止了a标签的默认行为并用pushState进行hash值的转变。切换页面的过程是在点击Link标签或者后退前进按钮时，会先发生url地址的转变，Router监听到地址的改变根据Route的path属性匹配到对应的组件，将state值改成对应的组件并调用setState触发render函数重新渲染dom。</p>
<p>当页面比较多时，项目就会变得越来越大，尤其对于单页面应用来说，初次渲染的速度就会很慢，这时候就需要按需加载，只有切换到页面的时候才去加载对应的js文件。react配合webpack进行按需加载的方法很简单，Route的component改为getComponent，组件用require.ensure的方式获取，并在webpack中配置chunkFilename。</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">const chooseProducts = (location, cb) =&gt; {
    require.ensure([], require =&gt; {
        cb(null, require('../Component/chooseProducts').default)
    },'chooseProducts')
}

const helpCenter = (location, cb) =&gt; {
    require.ensure([], require =&gt; {
        cb(null, require('../Component/helpCenter').default)
    },'helpCenter')
}

const saleRecord = (location, cb) =&gt; {
    require.ensure([], require =&gt; {
        cb(null, require('../Component/saleRecord').default)
    },'saleRecord')
}

const RouteConfig = (
    &lt;Router history={history}&gt;
        &lt;Route path="/" component={Roots}&gt;
            &lt;IndexRoute component={index} /&gt;//首页
            &lt;Route path="index" component={index} /&gt;
            &lt;Route path="helpCenter" getComponent={helpCenter} /&gt;//帮助中心
            &lt;Route path="saleRecord" getComponent={saleRecord} /&gt;//销售记录
            &lt;Redirect from='*' to='/'  /&gt;
        &lt;/Route&gt;
    &lt;/Router&gt;
);
</code></pre>
<h4>组件之间的通信</h4>
<hr>
<p>react推崇的是单向数据流，自上而下进行数据的传递，但是由下而上或者不在一条数据流上的组件之间的通信就会变的复杂。解决通信问题的方法很多，如果只是父子级关系，父级可以将一个回调函数当作属性传递给子级，子级可以直接调用函数从而和父级通信。</p>
<p>组件层级嵌套到比较深，可以使用上下文getChildContext来传递信息，这样在不需要将函数一层层往下传，任何一层的子级都可以通过this.context直接访问。</p>
<p>兄弟关系的组件之间无法直接通信，它们只能利用同一层的上级作为中转站。而如果兄弟组件都是最高层的组件，为了能够让它们进行通信，必须在它们外层再套一层组件，这个外层的组件起着保存数据，传递信息的作用，这其实就是redux所做的事情。</p>
<p>组件之间的信息还可以通过全局事件来传递。不同页面可以通过参数传递数据，下个页面可以用location.param来获取。其实react本身很简单，难的在于如何优雅高效的实现组件之间数据的交流。</p>
<h4>Redux</h4>
<hr>
<p>首先，redux并不是必须的，它的作用相当于在顶层组件之上又加了一个组件，作用是进行逻辑运算、储存数据和实现组件尤其是顶层组件的通信。如果组件之间的交流不多，逻辑不复杂，只是单纯的进行视图的渲染，这时候用回调，context就行，没必要用redux，用了反而影响开发速度。但是如果组件交流特别频繁，逻辑很复杂，那redux的优势就特别明显了。我第一次做react项目的时候并没有用redux，所有的逻辑都是在组件内部实现，当时为了实现一个逻辑比较复杂的购物车，洋洋洒洒居然写了800多行代码，回头一看我自己都不知道写的是啥，画面太感人。</p>
<p>先简单说一下redux和react是怎么配合的。react-redux提供了connect和Provider两个好基友，它们一个将组件与redux关联起来，一个将store传给组件。组件通过dispatch发出action，store根据action的type属性调用对应的reducer并传入state和这个action，reducer对state进行处理并返回一个新的state放入store，connect监听到store发生变化，调用setState更新组件，此时组件的props也就跟着变化。</p>
<h5>流程是这个样子的：</h5>
<p><img decoding="async" src="https://www.zlhlab.top/wp-content/uploads/2018/02/simple_redux.jpg" alt=""></p>
<p>值得注意的是connect，Provider，mapStateToProps,mapDispatchToProps是react-redux提供的，redux本身和react没有半毛钱关系，它只是数据处理中心，没有和react产生任何耦合，是react-redux让它们联系在一起。</p>
<h5>接下来具体分析一下，redux以及react-redux到底是怎么实现的。</h5>
<h5>先上一张图</h5>
<p><img decoding="async" src="https://www.zlhlab.top/wp-content/uploads/2018/02/all_redux.png" alt=""></p>
<p>明显比第一张要复杂，其实两张图说的是同一件事。从上而下慢慢分析：</p>
<h4>先说说redux：</h4>
<hr>
<h5>redux主要由三部分组成：store，reducer，action。</h5>
<p><strong>store</strong>是一个对象，它有四个主要的方法：</p>
<p><strong>1、dispatch:</strong></p>
<blockquote><p>
  用于action的分发——在createStore中可以用middleware中间件对dispatch进行改造，比如当action传入dispatch会立即触发reducer，有些时候我们不希望它立即触发，而是等待异步操作完成之后再触发，这时候用redux-thunk对dispatch进行改造，以前只能传入一个对象，改造完成后可以传入一个函数，在这个函数里我们手动dispatch一个action对象，这个过程是可控的，就实现了异步。
</p></blockquote>
<p><strong>2、subscribe：</strong></p>
<blockquote><p>
  监听state的变化——这个函数在store调用dispatch时会注册一个listener监听state变化，当我们需要知道state是否变化时可以调用，它返回一个函数，调用这个返回的函数可以注销监听。
</p></blockquote>
<pre class="prism-highlight line-numbers" data-start="1"><code class="language-null">let unsubscribe = store.subscribe(() =&gt;{console.log('state发生了变化')})
</code></pre>
<p><strong>3、getState：</strong></p>
<blockquote><p>
  获取store中的state——当我们用action触发reducer改变了state时，需要再拿到新的state里的数据，毕竟数据才是我们想要的。getState主要在两个地方需要用到，一是在dispatch拿到action后store需要用它来获取state里的数据，并把这个数据传给reducer，这个过程是自动执行的，二是在我们利用subscribe监听到state发生变化后调用它来获取新的state数据，如果做到这一步，说明我们已经成功了。
</p></blockquote>
<p><strong>4、replaceReducer:</strong></p>
<blockquote><p>
  替换reducer，改变state修改的逻辑。
</p></blockquote>
<p>store可以通过createStore()方法创建，接受三个参数，经过combineReducers合并的reducer和state的初始状态以及改变dispatch的中间件，后两个参数并不是必须的。store的主要作用是将action和reducer联系起来并改变state。</p>
<p><strong>action:</strong></p>
<blockquote><p>
  action是一个对象，其中type属性是必须的，同时可以传入一些数据。action可以用actionCreactor进行创造。dispatch就是把action对象发送出去。
</p></blockquote>
<p><strong>reducer:</strong></p>
<blockquote><p>
  reducer是一个函数，它接受一个state和一个action，根据action的type返回一个新的state。根据业务逻辑可以分为很多个reducer，然后通过combineReducers将它们合并，state树中有很多对象，每个state对象对应一个reducer，state对象的名字可以在合并时定义。
</p></blockquote>
<p>像这个样子：</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">const reducer = combineReducers({
     a: doSomethingWithA,
     b: processB,
     c: c
})
</code></pre>
<p><strong>combineReducers:</strong></p>
<blockquote><p>
  其实它也是一个reducer，它接受整个state和一个action，然后将整个state拆分发送给对应的reducer进行处理，所有的reducer会收到相同的action，不过它们会根据action的type进行判断，有这个type就进行处理然后返回新的state，没有就返回默认值，然后这些分散的state又会整合在一起返回一个新的state树。
</p></blockquote>
<p>接下来分析一下整体的流程，首先调用store.dispatch将action作为参数传入，同时用getState获取当前的状态树state并注册subscribe的listener监听state变化，再调用combineReducers并将获取的state和action传入。combineReducers会将传入的state和action传给所有reducer，reducer会根据state的key值获取与自己对应的state，并根据action的type返回新的state，触发state树的更新，我们调用subscribe监听到state发生变化后用getState获取新的state数据。</p>
<p>redux的state和react的state两者完全没有关系，除了名字一样。</p>
<p><strong>上面分析了redux的主要功能，那么react-redux到底做了什么？</strong></p>
<h4>React-Redux</h4>
<hr>
<p>如果只使用redux，那么流程是这样的：</p>
<blockquote><p>
  component &#8211;&gt; dispatch(action) &#8211;&gt; reducer &#8211;&gt; subscribe &#8211;&gt; getState &#8211;&gt; component
</p></blockquote>
<p>用了react-redux之后流程是这样的：</p>
<blockquote><p>
  component &#8211;&gt; actionCreator(data) &#8211;&gt; reducer &#8211;&gt; component
</p></blockquote>
<p>store的三大功能：dispatch，subscribe，getState都不需要手动来写了。react-redux帮我们做了这些，同时它提供了两个好基友Provider和connect。</p>
<p><strong>Provider</strong>是一个组件，它接受store作为props，然后通过context往下传，这样react中任何组件都可以通过contex获取store。也就意味着我们可以在任何一个组件里利用dispatch(action)来触发reducer改变state，并用subscribe监听state的变化，然后用getState获取变化后的值。但是并不推荐这样做，它会让数据流变的混乱，过度的耦合也会影响组件的复用，维护起来也更麻烦。</p>
<p><strong>connect &#8211;connect(mapStateToProps, mapDispatchToProps, mergeProps, options)</strong>是一个函数，它接受四个参数并且再返回一个函数&#8211;wrapWithConnect，wrapWithConnect接受一个组件作为参数wrapWithConnect(component)，它内部定义一个新组件Connect(容器组件)并将传入的组件(ui组件)作为Connect的子组件然后return出去。</p>
<p>所以它的完整写法是这样的：connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(component)</p>
<p><strong>mapStateToProps(state, [ownProps])：</strong></p>
<blockquote><p>
  mapStateToProps 接受两个参数，store的state和自定义的props，并返回一个新的对象，这个对象会作为props的一部分传入ui组件。我们可以根据组件所需要的数据自定义返回一个对象。ownProps的变化也会触发mapStateToProps
</p></blockquote>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">function mapStateToProps(state) {
   return { todos: state.todos };
}
</code></pre>
<p><strong>mapDispatchToProps(dispatch, [ownProps])：</strong></p>
<blockquote><p>
  mapDispatchToProps如果是对象，那么会和store绑定作为props的一部分传入ui组件。如果是个函数，它接受两个参数，bindActionCreators会将action和dispatch绑定并返回一个对象，这个对象会和ownProps一起作为props的一部分传入ui组件。所以不论mapDispatchToProps是对象还是函数，它最终都会返回一个对象，如果是函数，这个对象的key值是可以自定义的
</p></blockquote>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">function mapDispatchToProps(dispatch) {
   return {
      todoActions: bindActionCreators(todoActionCreators, dispatch),
      counterActions: bindActionCreators(counterActionCreators, dispatch)
   };
}
</code></pre>
<p>mapDispatchToProps返回的对象其属性其实就是一个个actionCreator，因为已经和dispatch绑定，所以当调用actionCreator时会立即发送action，而不用手动dispatch。ownProps的变化也会触发mapDispatchToProps。</p>
<p><strong>mergeProps(stateProps, dispatchProps, ownProps)：</strong></p>
<blockquote><p>
  将mapStateToProps() 与 mapDispatchToProps()返回的对象和组件自身的props合并成新的props并传入组件。默认返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。
</p></blockquote>
<p><strong>options：</strong></p>
<blockquote><p>
  pure = true 表示Connect容器组件将在shouldComponentUpdate中对store的state和ownProps进行浅对比，判断是否发生变化，优化性能。为false则不对比。
</p></blockquote>
<p>其实connect函数并没有做什么，大部分的逻辑都是在它返回的wrapWithConnect函数内实现的，确切的说是在wrapWithConnect内定义的Connect组件里实现的。</p>
<h4>下面是一个完整的 react &#8211;&gt; redux &#8211;&gt; react 流程：</h4>
<p>一、Provider组件接受redux的store作为props，然后通过context往下传。</p>
<p>二、connect函数在初始化的时候会将mapDispatchToProps对象绑定到store，如果mapDispatchToProps是函数则在Connect组件获得store后，根据传入的store.dispatch和action通过bindActionCreators进行绑定，再将返回的对象绑定到store，connect函数会返回一个wrapWithConnect函数，同时wrapWithConnect会被调用且传入一个ui组件，wrapWithConnect内部使用class Connect extends Component定义了一个Connect组件，传入的ui组件就是Connect的子组件，然后Connect组件会通过context获得store，并通过store.getState获得完整的state对象，将state传入mapStateToProps返回stateProps对象、mapDispatchToProps对象或mapDispatchToProps函数会返回一个dispatchProps对象，stateProps、dispatchProps以及Connect组件的props三者通过Object.assign()，或者mergeProps合并为props传入ui组件。然后在ComponentDidMount中调用store.subscribe，注册了一个回调函数handleChange监听state的变化。</p>
<p>三、此时ui组件就可以在props中找到actionCreator，当我们调用actionCreator时会自动调用dispatch，在dispatch中会调用getState获取整个state，同时注册一个listener监听state的变化，store将获得的state和action传给combineReducers，combineReducers会将state依据state的key值分别传给子reducer，并将action传给全部子reducer，reducer会被依次执行进行action.type的判断，如果有则返回一个新的state，如果没有则返回默认。combineReducers再次将子reducer返回的单个state进行合并成一个新的完整的state。此时state发生了变化。Connect组件中调用的subscribe会监听到state发生了变化，然后调用handleChange函数，handleChange函数内部首先调用getState获取新的state值并对新旧两个state进行浅对比，如果相同直接return，如果不同则调用mapStateToProps获取stateProps并将新旧两个stateProps进行浅对比，如果相同，直接return结束，不进行后续操作。如果不相同则调用this.setState()触发Connect组件的更新，传入ui组件，触发ui组件的更新，此时ui组件获得新的props，react &#8211;&gt; redux &#8211;&gt; react 的一次流程结束。</p>
<p><strong>上面的有点复杂，简化版的流程是：</strong></p>
<p>一、Provider组件接受redux的store作为props，然后通过context往下传。</p>
<p>二、connect函数收到Provider传出的store，然后接受三个参数mapStateToProps，mapDispatchToProps和组件，并将state和actionCreator以props传入组件，这时组件就可以调用actionCreator函数来触发reducer函数返回新的state，connect监听到state变化调用setState更新组件并将新的state传入组件。<br />
connect可以写的非常简洁，mapStateToProps，mapDispatchToProps只不过是传入的回调函数，connect函数在必要的时候会调用它们，名字不是固定的，甚至可以不写名字。</p>
<p>简化版本：<br />
    connect(state =&gt; state, action)(Component);</p>
<h4>项目搭建</h4>
<hr>
<p>上面说了react，react-router和redux的知识点。但是怎么样将它们整合起来，搭建一个完整的项目。</p>
<p>1、先引用 react.js，redux，react-router 等基本文件，建议用npm安装，直接在文件中引用。</p>
<p>2、从 react.js，redux，react-router 中引入所需要的对象和方法。</p>
<pre class="line-numbers prism-highlight" data-start="1"><code class="language-javascript">import React, {Component, PropTypes} from 'react';
import ReactDOM, {render} from 'react-dom';
import {Provider, connect} from 'react-redux';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import { Router, Route, Redirect, IndexRoute, browserHistory, hashHistory } from 'react-router';
</code></pre>
<p>3、根据需求创建顶层ui组件，每个顶层ui组件对应一个页面。</p>
<p>4、创建actionCreators和reducers，并用combineReducers将所有的reducer合并成一个大的reduer。利用createStore创建store并引入combineReducers和applyMiddleware。</p>
<p>5、利用connect将actionCreator，reuder和顶层的ui组件进行关联并返回一个新的组件。</p>
<p>6、利用connect返回的新的组件配合react-router进行路由的部署，返回一个路由组件Router。</p>
<p>7、将Router放入最顶层组件Provider，引入store作为Provider的属性。</p>
<p>8、调用render渲染Provider组件且放入页面的标签中。</p>
<p>可以看到顶层的ui组件其实被套了四层组件，Provider，Router，Route，Connect，这四个组件并不会在视图上改变react，它们只是功能性的。</p>
<p>通常我们在顶层的ui组件打印props时可以看到一堆属性：</p>
<p><img decoding="async" src="https://www.zlhlab.top/wp-content/uploads/2018/02/react_props.png" alt=""></p>
<p>上图的顶层ui组件属性总共有18个，如果刚刚接触react，可能对这些属性怎么来的感到困惑，其实这些属性来自五个地方：</p>
<p>组件自定义属性1个，actionCreator返回的对象6个，reducer返回的state4个，Connect组件属性0个，以及Router注入的属性7个。</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
