普通视图

发现新文章,点击刷新页面。
昨天以前清竹茶馆博客

Codex Skills 不是 Prompt 的升级版,而是写给 AI 的岗位 SOP

作者 vadxq
2026年3月15日 08:21

Prompt 更像临时交代,Skill 更像写给 AI 的岗位 SOP。

我花时间研究了 Codex Skills,发现它不是 Prompt 的升级版

最近我重新梳理了一遍 Codex 的 Skills 机制,最大的感受是:它被很多人低估了。

我一开始也没太当回事。第一眼看上去,它很像 prompt 模板:给 AI 一段说明,告诉它做什么、注意什么、最后产出什么。看久了甚至会产生一种错觉:这不就是把 prompt 打包了一下吗?

但真正把它放到工程场景里看,我的想法变了。

因为在真实开发里,问题从来不只是“这次能不能把代码写出来”。更麻烦的是另外几件事:同一类任务每次都要重新解释背景;不同人写出来的提示词风格完全不一样;任务一长,约束开始丢;协作一多,规范就散掉。很多团队现在用 AI 不够稳,不是因为模型不够强,而是因为工作流还停留在临场发挥。

Skills 真正解决的,恰恰是这个问题:它不是让 AI 多会一点,而是把原本散落在个人经验、团队规范和临时指令里的工作方法,整理成可复用、可迁移、可版本化的执行单元。

如果要我用一句话概括:

Prompt 更像临时交代,Skill 更像写给 AI 的岗位 SOP。


为什么我会重新看待 Skills

我现在越来越觉得,Skills 不是 prompt 的小升级,而是 AI 编程开始进入“工作流工程”的一个信号。

在 prompt 时代,我们优化的是一次对话:怎么把上下文塞进去,怎么把要求说清楚,怎么让模型这一次别跑偏。这当然有用,但它天然偏一次性。它适合解决“眼前这件事”,不太适合沉淀“以后同类事情都怎么做”。

而软件工程最贵的,往往不是一次性的输出,而是重复执行时的稳定性。一个团队每天都在反复做类似的事情:评审 PR、查文档、补测试、做重构前的架构判断、接手陌生代码库、把需求拆成工程任务。这些事情如果每次都靠人重新写 prompt,本质上还是手工作坊。

所以我这次重新看 Skills,最大的转变就是:它让我第一次比较清楚地看到,AI 工具正在从“会补全的助手”,往“能执行团队方法论的工程角色”演进。

Skills 到底是什么

我现在对 Skill 的理解很简单:它不是一句咒语,而是一个可复用的工作流目录。

一个标准 Skill 的核心通常是 SKILL.md。这个文件不只是说明文档,它其实定义了一个技能的边界:它在什么场景下应该被触发,要做哪些步骤,产出什么结果,做到什么程度才算完成。

如果任务需要确定性执行,可以挂 scripts/;如果要补充领域知识,可以放 references/;如果要复用模板和脚手架,可以放 assets/。有些技能还会带 agents/openai.yaml 这种平台侧元数据,用来描述它在特定界面里的展示和依赖关系。

一个典型的技能目录,大概会长这样:

1
2
3
4
5
6
7
8
my-skill/
├── SKILL.md
├── scripts/
│ └── verify.sh
├── references/
│ └── api-contract.md
└── assets/
└── template.ts

SKILL.md 里最关键的通常不是长篇背景,而是几个东西:namedescription、执行步骤、边界条件和 Done criteria。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---
name: pr-review-fixer
description: Handle GitHub review comments and patch the matching code safely.
---

1. Read review comments.
2. Locate the related code.
3. Apply the patch.
4. Run the relevant checks.
5. Summarize what changed.

Done criteria:
- All requested comments are addressed.
- Relevant checks pass.
- Risky assumptions are called out.

我很认同一个说法:SKILL.md 更像目录,不像百科全书。长文档、规范、架构约束、接口契约,应该拆到 references/ 里,别把所有东西都堆进主指令。这样更清楚,也更稳。

scripts/ 还有一个经常被低估的价值:它不只是为了“把逻辑写死一点”,也是为了把确定性执行和敏感鉴权隔离开。比如你要连 Slack、Jira 或内部 API,真正的密钥最好通过运行时环境变量注入给脚本读取,而不是直接写在 SKILL.md 里。这样 Skill 作为静态资产在团队内流转时会安全得多,方法能复用,凭据却不会跟着扩散。

从这个角度看,Skill 跟普通 prompt 最大的区别是:prompt 更像一段临时话术,Skill 更像一个有结构的工作单元。前者重点是“怎么说”,后者重点是“怎么稳定地做”。

一个Skill的结构

它为什么比普通 Prompt 更进一步

Skills 最值得说的一点,是它的按需加载思路。

系统平时并不会把所有技能内容都塞进上下文,而是只记录每个 Skillnamedescription。只有两种情况,完整技能才会被调起:要么你显式点名,要么当前任务和它的 description 高度匹配。

这看起来只是个技术细节,但实际价值很大。

第一,上下文更干净。没有被调用的技能,不会抢占注意力。第二,复杂任务更稳。模型不需要在一大堆无关规则里找重点。第三,技能库可以做得更细。你可以维护很多高专业度的小技能,而不是逼着一个巨型 prompt 同时解决所有问题。

这套机制背后,本质上就是“渐进式披露”和“延迟绑定”。启动时先注册轻量元数据,真正需要时再把完整指令、脚本路径和参考资料带入当前会话。从工程实现上看,可以把这一步理解为一种 Discovery 和按需展开的过程:系统先发现技能,再在需要时加载技能,而不是一开始就把所有能力灌进模型。

token 只是副产品,真正重要的是稳定性。尤其在企业场景里,稳定比“偶尔惊艳一次”重要得多。更有意思的是,最近模型侧对内置工具的调用,也在朝类似方向演进:像 OpenAI 的 web searchfile search 这类能力,本质上也是需要时才触发,而不是先把所有工具结果预塞进上下文。换句话说,Skills 里的这套延迟绑定思路,并不是一个孤立技巧,而是越来越像现代 agent 系统里的通用设计。

这也是我为什么觉得,Skills 比普通 prompt 更适合团队使用。因为它天然支持经验沉淀、角色分工和规范复用。你可以把架构审查、测试约束、发布检查、PR 处理,甚至某个业务域里的特殊规则,变成团队共享的资产,而不是继续依赖“谁比较会写提示词”。

Skills 和 MCP 不是一回事

很多人会把 MCPSkills 放在一起聊,但它们解决的问题并不一样。

我的理解是:MCP 更像能力接口,负责把 AI 连到 GitHub、文档服务、数据库、外部系统;Skills 更像执行层,决定什么时候调这些能力、按什么步骤调、失败了怎么收口。前者提供基础设施,后者提供工作方法。

你可以把它理解成:MCP 决定“能不能连上工具”,Skill 决定“连上之后该怎么做”。

gh-address-comments 这类技能举例:真正去读取 PR 评论、定位代码、拉取仓库信息,靠的是相关工具能力;但“先拉评论、再定位代码块、再生成补丁、再跑验证、最后给出解释”这套顺序和规则,才是 Skill 在定义的东西。

这也是为什么我觉得,真正好用的不是“工具越多越好”,而是“有没有一套稳定的方法,把这些工具组织起来”。

Skill、Agent 和 Sub-agent 到底是什么关系

我觉得这里还有一个特别容易混的点:很多人会把 SkillAgentSub-agent 当成一层东西来理解,但它们其实分别属于三层。这里的 Sub-agent 更多是工程上的描述;在不同产品界面里,也可能表现为 parallel agent、worker agent 或 background task。

最简单的说法是:Skill 是方法,Agent 是执行者,Sub-agent 是被拆分出去的执行者。

如果展开一点看:

  • Skill 是静态资产。它通常躺在磁盘里,以 SKILL.mdscripts/references/ 这些文件形式存在,本质上是一套可复用的工作流定义。
  • Agent 是运行时主体。它接到任务后,会读取上下文、调用工具、决定是否使用某个 Skill,并对当前任务的结果负责。
  • Sub-agent 是被主 Agent 派生出去的子执行单元。它不是另一种 Skill,而是一个范围更小、目标更明确、生命周期更短的 Agent

如果非要做一个技术上的对应关系,我会这样记:

  • Skill 更像类库或 playbook
  • Agent 更像正在运行的进程或 worker
  • Sub-agent 更像被主进程派生出来的子 worker

这三个对象最核心的区别,不在名字,而在“有没有运行态”。Skill 本身不会执行任何事,它只是定义规则;Agent 才是真正会读代码、调工具、改文件、总结结果的那个运行实体;Sub-agent 则是在需要并行、隔离或分治时,被主 Agent 分发出去的运行实体。

所以它们的关系,不是并列替代,而是组合关系:

  • 一个 Agent 可以在一个任务里调用多个 Skills
  • 同一个 Skill 也可以被多个 Agents 反复复用
  • 一个 Agent 在任务复杂时,可以继续派生多个 Sub-agents
  • 这些 Sub-agents 既可以共享同一个 Skill,也可以各自使用不同的 Skill

我觉得一个例子会更清楚。假设现在有个任务:处理一轮复杂 PR review。

  • Skill 定义的是方法:先读取 review comments,再定位代码,再生成补丁,再跑检查,最后汇总风险。
  • Agent 是接任务的人:它拿到“处理这轮 review”这个目标后,决定调用 gh-address-comments,并协调相关工具。
  • Sub-agent 是被拆出去的执行者:如果评论很多,主 Agent 可能把前端、后端、测试三个子问题分别丢给三个 Sub-agents 去并行处理,最后再统一汇总。

从工程角度看,这个区分特别重要。因为一旦你搞混了,就很容易把本该写进 Skill 的方法论,误塞进单次任务提示词里;也容易把本该由 Agent 做的调度和判断,错误期待 Skill 自己“自动完成”。

所以我现在会用一句更工程化的话来区分它们:

  • Skill 解决“应该怎么做”
  • Agent 解决“现在由谁来做、怎么推进”
  • Sub-agent 解决“任务拆开之后,谁并行去做”

如果团队开始做多智能体协作,这个分层就更重要了。因为 Skill 是可版本化、可审查、可复用的方法资产;AgentSub-agent 则是围绕具体任务临时生成、执行完就结束的运行实例。一个是长期资产,一个是运行时编排。

如果要给团队新人快速解释,我会直接给这张对照表:

核心对象与职责概览

如果再压缩成一句话,我会这么说:Skill 像方法手册,Agent 像正在干活的人,Sub-agent 像被分出去并行处理子任务的人,而 MCP 像他们手里的工具接口。

企业落地时,我最看重的两个边界

如果把 Skills 放进真实团队,我最在意的其实不是“它能不能多做点事”,而是“边界有没有守住”。至少有两个边界很关键:文件系统边界,和执行审批边界。

在 Codex CLI 这类本地代理工作流里,常见会有类似 sandbox_mode 的文件系统控制项。像 workspace-write 这种思路,核心是允许模型在工作区内读写,同时尽量把 .git/.codex/ 这类敏感目录保护起来。这一点很重要,因为它决定了 AI 可以动哪里,不能动哪里。

另一个常见控制项是 approval_policy。在交互式开发里,像 on-requestuntrusted 这种模式,本质上是在高风险操作前加一道人工闸门。你可以让低风险动作继续流畅执行,把真正危险的 shell 操作、外部系统访问或者敏感数据外发拦下来。

如果我要给团队做一个很保守的起点,大概会是类似下面这样的思路(示意,不同版本或界面的命名可能不同):

1
2
3
4
5
sandbox_mode = "workspace-write"
approval_policy = "on-request"

# 更严格时,可以把可写目录继续收窄
# writable_roots = ["./tmp", "./generated"]

这类配置的意义,不只是安全,更是治理。因为一旦你开始让 Skill 调脚本、调用外部能力、修改仓库,你就必须同时定义“允许它做什么”和“出现风险时谁来兜底”。

怎么安装、放置和启用

安装其实不复杂。

如果你用的是带本地技能管理能力的 Codex / CLI 工作流,常见做法是通过快捷命令面板进入 Skill Installer。以我当前这套环境为例,可以在终端里输入 $ 打开面板,再选择 Skill Installer。如果你装的是官方精选技能,可以直接用别名,例如:

1
$skill-installer gh-address-comments

如果你更喜欢社区生态,也可以直接从 GitHub 的目录 URL 拉取第三方技能。这一点我觉得很重要,因为它意味着 Skills 不是封闭能力,而是一个可以持续生长的生态。

这里有个很容易忽略的小坑:装完之后要重启当前会话。 因为重启时会触发 Discovery,重新扫描新的 SKILL.md 元数据,把技能注册进当前可用列表。很多“明明装了却没生效”的情况,问题都出在这里。

另外,Skills 放在哪,也很关键。以本地技能体系为例,常见会有这样几层:

  • 系统级:~/.codex/skills/.system/
  • 用户级:$HOME/.codex/skills/
  • 仓库级:<项目根目录>/.agents/skills/

我的理解是:个人偏好放用户级,团队规范放仓库级。前者适合你的工作习惯,比如审查风格、写作规范、个人常用流程;后者适合项目规则,比如部署检查、业务约束、测试标准、本地编排脚本。这些东西如果能和代码一起版本化,团队协作会顺很多,CI 也更容易继承同一套约束。

怎么用,怎么写得稳

怎么用,其实也不复杂。

最直接的方法就是显式调用。比如:用 openai-docs 查最新接口文档;用 gh-address-comments 处理 reviewer 评论;在改核心模块前,先调用某个架构审查类 Skill;接手陌生项目时,先用 onboarding 或 orientation 类 Skill 建立基本认知。

另一种是隐式触发:系统判断当前任务和某个 Skill 的边界高度匹配,就会自动加载它。但前提是这个 Skill 本身写得足够清楚。所以真正影响技能效果的,往往不是“名字起得响不响”,而是边界定义准不准。

如果你准备自己写 Skill,我觉得有几条原则特别重要。

  • One Skill, One Job:一个技能只解决一个具体问题,别把生成代码、补测试、写文档硬塞进一个技能里
  • 不要假设模型记得上下文:技能必须尽量自包含,把前置条件和完成标准写清楚
  • 把输入、输出和 Done criteria 写清楚:让结束条件可检查,而不是靠感觉
  • 能用清楚的文字规则,就别急着上脚本:只有真的需要确定性执行、校验或外部系统操作时,再把逻辑放进 scripts/
  • 让技能像微服务,而不是大杂烩:边界越小,行为越稳,可维护性也越高

我自己现在看一个 Skill 写得好不好,核心就看三件事:边界清不清楚,触发稳不稳定,失败后能不能收口。说到底,Skill 越像一个明确职责的工程角色,越不容易失控。

写好 AI Skill 的核心原则

多智能体协作,为什么会放大 Skills 的价值

我觉得这部分其实特别值得讲,因为 Skills 一旦和多智能体协作结合,价值会被明显放大。

原因很简单:单个 Agent 处理复杂任务时,最大的问题是上下文越滚越大,最后什么都混在一起。而多智能体的思路,是把任务拆开,让不同 Agent 在隔离环境里各自处理不同子问题。

从公开资料和工程实践看,用 Git Worktrees 做物理隔离是很自然的一步。比如一个 Agent 去改数据库查询,另一个 Agent 去重构前端交互,它们不应该在同一个工作树里同时写文件。给它们各自独立的 worktree 和分支,最后再做 diff、集成和合并,这才像工程系统,而不是在同一份工作区里赌运气。

这时候 Skills 的作用就很明显了:它不只是告诉某个 Agent“去干活”,而是把每个子任务的执行规则也一起分发下去。这样多个 Agent 虽然并行,但行为边界是一致的。

对于大规模、同质化的任务,在一些代理编排工作流里还会出现类似 spawn_agents_on_csv 这样的 CSV 驱动分发接口:你准备一个 CSV,把目标文件或参数一行一行列出来,系统按行派生 worker;再用 max_concurrency 控制并发,用 max_runtime_seconds 控制超时,让每个 worker 完成后回传结构化结果。更关键的是,这类批处理通常还会带一个明确的回调契约:每个 worker 完工后,都要通过类似 report_agent_job_result 这样的接口回传结构化 JSON;谁没按约定上报,谁就会被标成失败项。这个思路本质上已经很接近批处理编排系统了。

换句话说,Skills 的真正价值,不只是在“一个 Agent 怎么更稳地做事”,还在于“很多 Agent 一起做事时,怎么保持方法一致、输出可追踪、风险可收敛”。

Skills +多智能体:实现工程化协作

我会优先推荐哪些 Skills

如果你问我,团队刚开始配 Skills 时该优先上哪一批,我不会先挑“看起来最强”的,而会先挑“最能减少返工”的。对大多数团队来说,真正值得优先投入的,不是某一个单点很炫的 Skill,而是一组能顺着“先理解、再实现、再校验、再协作”这条链路接起来的能力。链路接得上,Skills 才是生产力基建;接不上,就还是零散工具。

按这个标准,我通常会优先看下面五类,而且大致有先后顺序。

第一类,我会先推“架构推演与代码库认知”。

代表 Skills / 工具:plan@architecture@brainstormingcodebase-orientation

这是最应该放在前面的,因为它决定了 Agent 是不是先把系统看明白,再开始动手。无论是接手陌生代码库,还是往复杂系统里塞一个新需求,最怕的都不是“写慢了”,而是一开始理解就错了。这类 Skill 的价值,就是先帮你建立模块地图、摸清耦合点、识别边界和风险,把后面的返工压下来。

第二类,我会优先配“代码质量与工程协作”。

代表 Skills / 工具:security-auditorpytest-skillagentic-evalgh-address-commentsNightly CI Report

原因也很现实:如果 AI 生成的结果进不了现有的 review、test 和 CI 流程,那它的价值就放不大。团队真正需要的,不是“它能写出东西”,而是“它写出来的东西能不能被检查、被追踪、被回归、被合并”。这一类 Skill 的作用,就是把 AI 输出重新接回工程现实。

第三类和第四类,我会按团队重心来排先后。

如果你们是后端更重、稳定性更敏感的团队,我会把“后端防线与系统稳定性”往前提。

代表 Skills / 工具:circuit-breakerdead-letter-queuerow-level-security

这类 Skill 的核心不在于多写几个模式,而在于把防线前移:在生成阶段就默认考虑 RPC 熔断、异步重试、死信队列、ORM 越权这些最容易被忽略、但一出事就很贵的问题。

如果你们是产品和设计驱动更强的团队,我会把“前端工程与 UI/UX 落地”提到更前面。

代表 Skills / 工具:frontend-designtailwind(建议配合 Figma MCP 使用)

这类 Skill 解决的是另一种很常见的问题:代码也许能跑,但一眼看上去就是“AI 味很重”。它真正有价值的地方,是把设计稿、节点信息、无障碍检查和设计令牌更顺地带进实现过程,让前端产出更像成熟产品,而不是演示稿。

最后的判断

所以我这次看完之后的结论很明确:Skills 真正厉害的地方,不是让 AI 多会写几行代码,而是让团队第一次有机会把自己的方法论、规范和经验,变成一套能复制、能协作、能持续演进的资产。

这件事一旦成立,团队和团队之间比拼的就不只是模型、上下文和个人 prompt 水平,而是谁更早把自己的工作流产品化、资产化、版本化。

未来团队之间拉开差距的,也许不是谁更会写 prompt,而是谁更早把自己的工作流产品化成 Skills

【译】React 服务器组件中的关键安全漏洞

作者 vadxq
2025年12月4日 08:21

重要提醒:React 服务器组件曝光了一枚未认证的远程代码执行(RCE)漏洞,只要项目启用了 RSC 支持,就可能被远程掌控。建议所有依赖 React 19 的项目立即排查并升级。

本文根据 React 团队于 2025 年 12 月 3 日发布的「Critical Security Vulnerability in React Server Components」翻译整理。


🚨 漏洞概述

  • 12 月 3 日,React 官方团队发布最高级别安全通告:React 服务器组件(RSC)存在未认证的远程代码执行漏洞。
  • 漏洞已被登记为 CVE-2025-55182,CVSS 得分 10.0(危害最高级别)。
  • 攻击者无需通过身份验证,只要能向 Server Function 端点发送恶意 HTTP 请求,就可能在服务器上执行任意代码,直接接管你的后端环境。
  • 即便你尚未实现任何 React Server Function,只要你的框架或构建工具启用了 RSC 能力,就可能处于风险之中。

⚠️ 你是否受到影响?

受影响的 React 版本

  • 19.0
  • 19.1.0
  • 19.1.1
  • 19.2.0

受影响的框架与工具

  • Next.js
  • React Router
  • Waku
  • @parcel/rsc
  • @vitejs/plugin-rsc
  • rwsdk

以下情况暂不受影响

✅ 完全运行在客户端、没有任何服务端代码的 React 应用
✅ 没有使用支持 React 服务器组件的框架或打包工具

特别注意:只要支持 React 服务器组件,即便没有配置任何 Server Function 端点,也可能遭到攻击!

🛡️ 紧急修复方案

  • React 团队已在 19.0.119.1.219.2.1 中修复,请立即升级到对应分支的安全版本。
  • 若你使用的是 canary 版本(如 Next.js 14.3.0-canary.77+),请降级到最新稳定版,再等待后续补丁。
  • 一些托管服务商已在 React 团队指导下部署临时缓解,但请勿依赖临时方案,必须升级依赖。

基础升级命令示例

1
2
3
npm install react@19.2.1 react-dom@19.2.1
# 或
yarn add react@19.2.1 react-dom@19.2.1

📅 漏洞时间线

  • 11 月 29 日:安全研究员 Lachlan Davidson 通过 Meta Bug Bounty 报告漏洞。
  • 11 月 30 日:Meta 安全团队确认漏洞,并与 React 团队展开修复。
  • 12 月 1 日:修复方案完成,同时与托管服务商和生态项目联动部署缓解措施。
  • 12 月 3 日:补丁发布到 npm,漏洞以 CVE-2025-55182 正式披露。

💡 技术背景

React Server Functions 允许客户端通过 HTTP 请求调用运行在服务器上的函数,React 会负责序列化与反序列化过程。漏洞恰恰出在服务端解码载荷的环节:

  1. 客户端发起的函数调用被转换为 HTTP 请求。
  2. 服务端解析载荷并执行对应的函数。
  3. 攻击者可以伪造恶意载荷,诱使 React 在反序列化过程中执行任意代码。

在官方确认补丁完全部署之前,更多技术细节将保持保密,以免漏洞被大规模利用。

🎯 行动建议

  1. 立即确认项目所用的 React 与框架版本。
  2. 马上升级 React 核心依赖以及框架/打包器提供的 RSC 支持包。
  3. 同步通知团队、合作伙伴及客户,确保所有部署都得到修复。
  4. 监控服务器日志与入侵检测,关注是否存在可疑请求。
  5. 持续关注官方公告(React、Next.js、Expo、Redwood 等),获取最新补丁状态。

升级指南(框架 & 构建工具)

Next.js

1
2
3
4
5
6
7
npm install next@15.0.5   # 15.0.x
npm install next@15.1.9 # 15.1.x
npm install next@15.2.6 # 15.2.x
npm install next@15.3.6 # 15.3.x
npm install next@15.4.8 # 15.4.x
npm install next@15.5.7 # 15.5.x
npm install next@16.0.7 # 16.0.x

若使用 14.3.0-canary.77 或更高的 canary,请降级至最新 14.x 稳定版:

1
npm install next@14

详见 Next.js 安全公告

React Router(不稳定 RSC API)

1
2
3
4
5
npm install react@latest
npm install react-dom@latest
npm install react-server-dom-parcel@latest
npm install react-server-dom-webpack@latest
npm install @vitejs/plugin-rsc@latest

Expo

1
npm install react@latest react-dom@latest react-server-dom-webpack@latest

Redwood SDK

1
2
npm install rwsdk@latest
npm install react@latest react-dom@latest react-server-dom-webpack@latest

更多迁移说明见 Redwood 文档

Waku

1
npm install react@latest react-dom@latest react-server-dom-webpack@latest waku@latest

详情参考 Waku 官方讨论

@vitejs/plugin-rsc

1
npm install react@latest react-dom@latest @vitejs/plugin-rsc@latest

react-server-dom-parcel

1
npm install react@latest react-dom@latest react-server-dom-parcel@latest

react-server-dom-turbopack

1
npm install react@latest react-dom@latest react-server-dom-turbopack@latest

react-server-dom-webpack

1
npm install react@latest react-dom@latest react-server-dom-webpack@latest

🙏 致谢

感谢安全研究员 Lachlan Davidson 发现并报告漏洞,也向在补丁发布期间提供临时缓解与验证的托管服务商、框架团队和社区贡献者致以谢意。

📢 重要提醒

安全无小事! 该漏洞的严重性不容忽视,请务必在今天完成升级,保障用户数据与业务连续性。

转发给你的技术团队,让更多开发者看到这条重要信息!

Vercel收购NuxtLabs,我和尤雨溪一样心情复杂

作者 vadxq
2025年7月9日 00:00

2025年7月8日,前端领域迎来一次重磅整合:以托管和优化Next.js闻名的Vercel公司,宣布收购Nuxt.js背后的团队实体NuxtLabs。这一举动不仅标志着Nuxt发展进入新阶段,也引发了整个社区对前端生态未来走向的深度思考。

Nuxt 的新篇章:独立精神与商业支持的融合

vercel-nuextlabs-x

根据 Nuxt 核心维护者 Daniel Roe 在 GitHub 上的公告,核心团队加入 Vercel: Nuxt 的创始人 Sébastien Chopin (@atinux)、核心开发者 Pooya Parsa (@pi0)、Anthony Fu (@antfu) 以及 Daniel Roe (@danielroe) 等人将加入 Vercel,继续全职投入运行 Nuxt 的开源开发。

  • 保持独立性: 公告特别强调,Nuxt 作为一个开源项目,将保留其独立性。团队将继续拥有其公开的技术路线图、开放的治理模式,并维持其核心价值观。Daniel Roe 将继续领导 Nuxt 的技术和战略方向。
  • 拥抱选择的多样性: Nuxt 始终坚持“选择权”的核心理念。这意味着 Nuxt 将继续支持开发者自由选择模块、数据库、打包工具、路由方案,当然,也包括部署平台。它不会成为 Vercel 独占的框架。
  • 更可持续的未来: Vercel 的投资为 Nuxt 团队提供了更稳定的财务支持。这使得核心开发者能更专注于打磨框架、赋能社区,并将社区赞助的资金用于支持更广泛的贡献者,形成一个更健康、可持续的开源生态。

nuxtlabs-github-tip.png

Vue之父:尤雨溪大大的反应

我想,无数Nuxt老用户的心情应该都和我类似。而Vue的作者尤雨溪(Evan You),则用几条动态,无比精准地把我们这种复杂的心绪摆到了台面上。

youda-congratulate-nuxtlabs.png

一个值得细品的“心情复杂”:在收购官宣之后,尤雨溪第一时间向Nuxt团队送上了祝福。作为NuxtLabs的天使投资人,他比任何人都清楚这个团队一路走来的不易——既要维护一个庞大的开源项目,又要想办法让公司活下去。现在团队被Vercel收购,核心成员可以直接全职为爱发电,这无疑是算是比较好的结局之一。

但同时,他也坦率地写下了那句话,说自己“算不上是Vercel最大的粉丝”(may not be the biggest Vercel fan)。

这句话简练地概括了我们这些旁观者的矛盾心态。我们由衷佩服Vercel的平台能力和技术远见,但又对它日益强大的“虹吸效应”抱有一丝天然的警惕。在官宣前几天,他就曾发帖说自己“第一笔一级市场投资退出,心情有点复杂”,现在回头看,一切都顺理成章了。

youda-last-week-x.png

这份“复杂”到底从何而来?

  • 作为“大家长”的守护心态:Vue是他一手打造的生态。Nuxt作为Vue生态中最成功的全栈框架,就像一个极其优秀的孩子。现在,这个孩子被隔壁家特别有钱、还主推另一个孩子(React/Next.js)的家庭“收养”了,心情自然微妙。
  • 作为投资人的务实:他深知开源项目的商业化有多难。Nuxt团队在“免费奉献”和“商业求生”之间走了太久钢丝。现在Vercel的收购,让团队成员的价值得到了认可,让项目有了持续的生命力。从务实的角度看,这是一次成功的退出。
  • 作为战略家的隐忧:当一个平台通过收购,将多个主流框架的“大脑”都收归麾下时,生态的中心化风险就在增加。一个健康的市场,需要有多个实力相当的玩家来回“掰手腕”。尤雨溪的表态,正是对这种潜在失衡的温和提醒。

所以,他的“心情复杂”,并非个人好恶,而是站在整个前端生态高度的一种清醒与期许。

Nuxt的未来:是如虎添翼,还是会被“温水煮青蛙”?

冷静下来分析,这次收购对Nuxt本身,短期内是利大于弊。

Nuxt团队的公告给我们吃了定心丸:项目会继续保持开源和独立,技术路线图不变,开发者仍然可以自由选择部署到任何平台。这打消了我们最大的顾虑。

最直接的好处是,像Sébastien Chopin (atinux)、Pooya Parsa (pi0)这些大神,终于可以全职、专注地投入到开发中了。这意味着我们可以期待更快的Bug修复、更雄心勃勃的新功能、更完善的文档和社区支持。这对于我们这些一线开发者来说,是实实在在的红利。

但隐忧也并非空穴来风。这种风险是温和而长期的。比如,未来的新功能,会不会在设计上不自觉地优先适配Vercel的架构?官方文档和教程的示例,会不会越来越多地以Vercel为“最佳实践”?这就是开发者们最担心的“软锁定”——它不会强迫你,但会通过潜移默化的方式,让你觉得“用Vercel才是最顺滑的”。

再看Vercel,它的野心早已不加掩饰。从亲手打造Next.js这个“样板间”,到把Svelte的作者Rich Harris请入麾下,再到如今将Nuxt的核心团队收入囊中,它的目标绝不只是当一个“前端托管服务商”。

Vercel想做的,是成为“前端开发与部署的操作系统”。它提供基础设施(OS),同时又掌控着OS上最流行的应用软件(各类框架)。这步棋下得很大,也很有远见。它所创造的从代码提交到全球部署的丝滑体验,确实是革命性的。

对于我们开发者来说,这当然是好事。工具越来越强大,部署越来越简单,我们可以把更多精力放在业务逻辑和用户体验的创造上。但同时,我们也需要保持一份清醒:当选择越来越少,当“最优解”变得越来越唯一时,我们是否会失去一部分选择的自由和议价的权利?

nuxtlabs-is-joining-vercel-cover.png

一个老用户的缘分

作为一名从2016年就开始接触Vue的开发者,我与Nuxt的渊源,是无数Vue开发者成长路径的缩影。2018年,我在为学校开发官方网站时,首次面临了服务端渲染和SEO的硬性需求。在短暂调研过百度推出的亲儿子“Lavas”框架后,我惊喜地发现了Nuxt.js,那种“拨云见日”的狂喜。它“约定优于配置”的理念、清晰的工程化结构以及对SSR场景的专注,几乎完美解决了当时Vue生态一个的痛点。我毫不犹豫地选择了它。一个优秀的“元框架”对于提升开发效率、拓展应用场景是何其重要。从那以后,Nuxt就成了我工具箱里的“SSR利器”。它陪我走过了很多需要考虑首屏加载和SEO的项目,也让我对Vue的全栈能力有了全新的认识。它就像那个你从一个小众音乐节上发现的宝藏乐队,你希望它火,但又怕它火了之后就“变味”了。这也是我看到该消息想写点什么的缘故。希望它被收编后,不会失去那份独特的、纯粹的“为Vue生态而生”的气质。

first-got-nuxtjs-time.png

免费平替Claude-Code:Google Gemini CLI深度解析与实践指南

作者 vadxq
2025年6月24日 16:10

最近,Claude-Code刷屏朋友圈!紧接着昨晚Google推出的开源免费的Gemini CLI让AI强大能力直接走进开发者的终端界面,这款免费开源工具正彻底改变开发者与AI的交互方式。无需切换应用,直接在命令行中完成内容生成、代码辅助与复杂问题解决,开创了终端交互新时代。

为何选择Gemini CLI?四大核心优势

  1. 开源免费,透明可定制

作为开源项目,Gemini CLI不仅免费使用,更重要的是提供了完全的代码透明性和可定制性。这种开放模式激发了全球开发者社区的贡献,确保工具能快速迭代、持续进化。

  1. 强大的AI代理能力

基于”Reason and Act (ReAct)”循环机制,Gemini CLI远超普通聊天机器人,可自主分解任务、调用工具、执行动作并自我修正。这种代理能力可自动化复杂工作流,从代码错误修复到功能模块创建,大幅提升开发效率。

  1. 无缝终端集成体验

将AI能力直接融入开发者最熟悉的终端环境,最大限度减少上下文切换开销,让开发流程更专注、更高效。

  1. 突破性的多模态与大上下文处理

能处理超越1M token上下文窗口限制的大型代码库,支持PDF文档处理、视频分析和媒体生成,拓展了AI在创意与开发领域的应用边界。

gemini-cli-skills-list

三步上手,即刻体验

  1. 环境准备
  • Node.js: v18+(推荐LTS版本)
1
2
# 检查版本
node -v
  • 网络连接: 确保可访问Google服务
1
2
# 会返回一个html
curl www.google.com
  1. 简单安装,两种方式
  • 通过 npx 快速运行 (推荐初次尝试体验使用这种方法):
1
npx https://github.com/google-gemini/gemini-cli
  • 全局安装 (长期使用推荐)
1
npm install -g @google/gemini-cli

安装好后输入/help可以看到命令列表

gemini-cli-help

  1. 认证配置
  • 使用个人 Google 账户 (快速上手): 可使用 Gemini 2.5 Pro 模型的权限,并享有每分钟最多 60 个模型请求和每天 1,000 个模型请求的额度
  • 使用 API Key (高级用途与更高额度):如果有更高需求可以采用这种,但是注意费用⚠️
1
2
3
- 生成 API Key: 首先,你需要前往 Google AI Studio 生成你的专属 API Key 。   
- 设置环境变量: 将生成的 API Key 设置为终端的环境变量。在终端中执行以下命令(请务必将 YOUR_API_KEY 替换为你的实际密钥):export GEMINI_API_KEY="YOUR_API_KEY"
- 持久化配置:放进配置文件里:~/.zshrc 或 ~/.bashrc
  • 其他方式:谷歌workspace账户或者是授权code assist用户,Vertex AI用户

gemini-cli-login-account

实战应用场景

  • 代码开发助手
1
2
3
4
5
6
7
8
# 快速解析代码库架构
gemini "分析这个代码库的核心模块和交互方式"

# 智能错误排查
gemini "解释为什么这段代码会产生内存泄漏并提供修复方案"

# 代码生成与优化
gemini "创建一个带缓存的用户认证API"
  • 创意内容生成

利用多模态能力,从简单文本创作到复杂媒体内容分析与生成,一条命令轻松完成。创建文本肯定没问题,但是创建图片和视频是需要额外工具的。理解图片是没有什么问题的。

gemini-cli-skill-desc

  • 高效工作流自动化
1
2
3
4
5
# Git历史分析
gemini "总结过去一周的代码提交,按功能分类"

# 项目文档生成
gemini "为这个API模块生成完整的使用文档"

这样终于可以看懂同事写什么了

  • 高级技巧与故障排除
1
2
3
参数调优: 使用--model、--temperature等参数精确控制AI行为
工具链集成: 通过管道连接其他命令行工具,如git log | gemini "总结这些提交"
调试技巧: 遇到问题时使用DEBUG=true gemini "你的提示"获取详细日志

gemini-cli-command-list

实战让Gemini Cli帮你做产品

创建一个简单的网页:高考能力小测试

  1. 告诉gemini-cli我的需求是什么

gemini-cli-project-demo-gkquiz

  1. 然后告诉它你的更细致需求,一步步调试,最后的到下面的结果,也可以扫码浏览:https://gkquiz.vadxq.com/

gkquiz.vadxq.com

创建一个工程化的nextjs项目

  1. 首先告诉gemini-cli,我们需要创建一个nextjs项目,是关于什么内容,有什么要求

gemini-cli-nextjs-start

  1. 它还会自己安装依赖,写完后会自己debug,并且运行起来

gemini-cli-nextjs-npm

gemini-cli-nextjs-debug.png

gemini-cli-nextjs-run

  1. 当运行后出现问题,需要取消运行后告诉它debug,这里有点不智能了,需要改进的地方

gemini-cli-nextjs-run-error

gemini-cli-bugfix

  1. 最后的目录结构和粒子特效如下

gemini-cli-nextjs-files

gemini-cli-nextjs-result

结语:AI终端新时代
Gemini CLI将强大的AI能力直接融入开发工作流,极大提升开发效率。随着持续进化,它将带来更强大的多模态能力和更深度的系统集成,成为每位开发者不可或缺的智能助手。立即尝试Gemini CLI,开启你的终端AI革命!

最后!建了一个AI交流群,欢迎诸君前来交流!有任何关于文章的疑问也可以在群里交流哟

回复公众号【aiqun】或者【ai群】访问 https://aiqun.vadxq.com

参考来源
[1] https://github.com/google-gemini/gemini-cli
[2] https://blog.google/technology/developers/introducing-gemini-cli-open-source-ai-agent

Newsletter逆天福利:三种方式获取全年免费 AI/SaaS 工具包(含团体购买兑换防坑指南)

作者 vadxq
2025年4月16日 10:30

最近,Lenny Rachitsky 的 Lenny’s Newsletter推出了一个真正让人 “难以置信的优惠”。简单来说:$200就可以体验10个知名AI科技产品工具。只要订阅他的 Newsletter 一年,你就能免费获得一整套价值上千美元的顶级 SaaS 和 AI 工具,使用期限同样是一整年!这简直就是给产品人和创业者的及时雨。

捆绑包内含工具详解 (截至 2025年4月)

这套工具组合简直是为现代知识工作者量身打造的,从编程到会议记录,从产品设计到项目管理,应有尽有:
捆绑包包含内容

  • Cursor: AI 加持的代码编辑器 - 专业版一年 (年价值 $240)
  • Bolt: 智能会议助手 - 专业版一年 (年价值 $240)
  • Lovable: AI 驱动的用户研究平台 - 入门版一年 (年价值 $240)
  • Replit: 强大的在线 IDE - 核心版一年 (年价值 $240)
  • v0 (by Vercel): AI 生成 UI 界面 - 高级版一年 (年价值 $240)
  • Granola: 会议摘要分析工具 - 商业版一年 (支持100席位,团队福利)
  • Notion: 全能工作空间 - Plus 版一年及额度 (价值 $2000+,团队适用)
  • Linear: 高效项目管理工具 - 商业版一年 (价值 $336+,团队适用)
  • Superhuman: 效率邮件客户端 - 入门版一年 (年价值 $300)
  • Perplexity: 对话式 AI 搜索 - 专业版一年 (年价值 $240)

粗略一算,这套工具包的总价值轻松超过$4000美元,而你只需支付Lenny 的订阅费(个人$200,团购$170,学生约$100)就能全部拿下。老实说,这种性价比,不趁手抓住简直对不起自己的钱包。

入口地址,可以复制打开:https://www.lennysnewsletter.com/p/an-unbelievable-offer-now-get-one

如何获得这个超值捆绑包?三种主要途径详解

方式一:直接个人订阅

  • 适合人群: 普通个人用户
  • 价格: $200/年
  • 操作步骤: 访问 Lenny’s Newsletter 主订阅页面 购买,支付后会收到兑换指引邮件。

方式二:学生优惠订阅

  • 适合人群: 学生党们,是时候发挥你学生身份的价值了!
  • 价格: 半价约 $100 美元/年,简直是白菜价
  • 操作步骤: 需要通过学生专属优惠链接订阅,需要验证学生邮箱。专属链接如下:https://www.lennysnewsletter.com/subscribe?coupon=a29ec9b5,coupon这个代码目前不清楚能够切换别的。
  • 相关信息:目前测试的国内学生邮箱都失败了,美国教育邮箱部分可以

方式三:团体订阅(最划算)

  • 适合人群: 公司团队、创业伙伴、同学或朋友小组
  • 最大优势: 买 N 个席位,得 N 份完整的工具包!每个成员都能获得全套福利
  • 价格: 阶梯定价,人均可能比个人订阅更便宜
  • 管理员操作:
  • 团队负责人在官网选择 "Group Subscription",选择席位数并支付
  • 在团体管理页面 https://www.lennysnewsletter.com/account/group 添加成员邮箱
  • 大家要组团购买的话,可以私聊公众号【lenny】或者访问https://lenny-ai.vadxq.com 即可得到入群二维码

获取和激活你的捆绑包兑换码

无论选择哪种方式,激活流程都大同小异:

邮箱里的链接

  1. 查收第一封邮件: 支付成功后收到确认邮件,点击捆绑包链接
  • 小提示: 别忘了检查推广和垃圾邮件文件夹,有时好东西就藏在那里
  1. 登录 Lenny’s Bundle 网站: 输入邮箱进行登录/绑定

  2. 查收第二封邮件: 获取专属登录链接

  3. 访问兑换码页面: 点击登录链接后,即可看到所有工具及兑换码

Group需要管理员添加
团队成员注意: 需使用被管理员添加的那个邮箱完成上述步骤,获取属于你自己的那份独立激活码。

详细兑换示例:以 Cursor Pro 为例(附支付验证技巧)

以下是激活 Cursor Pro 的详细步骤:

cursor激活码入口

  1. 找到并点击兑换: 在 Lenny’s Bundle 页面找到 Cursor Pro 卡片,点击”Redeem”
  2. 前往 Cursor 官网: 系统会自动跳转到带有你激活码的专属页面
  3. 注册/登录账号:
  • 关键提示! 必须使用全新账号,否则可能无法享受福利
  • 建议使用 Gmail 等国际邮箱注册,避免不必要的麻烦
  1. 绑定支付方式:
  • 虽然实际不扣费,但需要验证支付方式
  • 推荐使用支付宝:填写姓名和地址,扫描二维码签约”订阅代扣”
  • 也可绑定国际信用卡
  1. 设置取消订阅提醒:
  • 这绝对是最重要的一步!立即在手机日历设置提醒,免费期结束前取消自动续费
  • 例如:2025年4月激活,就设置2026年4月初的提醒”取消 Cursor Pro 订阅”
  • 请注意:目前cursor激活后都是提示一个月,所以目前还不清楚cursor是什么一个情况,目前有三种猜测:一种是需要手动激活续费,一种是可能是一个号只能试用一个月,然后下个月就新注册账号,还有一种就是会自动激活续费直到一年期满

重要前提条件

每个工具的福利都仅限于该工具的”新付费客户”。如果你之前已为某工具付过费,那么这次活动无法获得该工具的免费使用权。

但是可以新注册一个账号即可解决。

潜在问题与实用提示

验证问题

  • 支付验证问题:
  • Lenny’s Newsletter 订阅支持国内信用卡
  • 兑换各工具时,遇到验证错误可尝试支付宝或联系客服
  • 邮箱接收问题:
  • 务必检查所有邮箱文件夹
  • 注册工具网站时,建议使用 Gmail 等国际邮箱

留心二手市场

这种捆绑包的最容易流入到二手市场,如果想捡漏或者回本,可以留心一下二手市场,比如闲鱼等平台,如果你只需个别工具,可以关注,但请注意交易风险

这个 Lenny’s Newsletter 捆绑包简直是一场及时雨,为我们这些对效率和创新工具有强烈需求的人带来了巨大福利。只要注意”新客户”条款,并记得设置取消续费提醒,就能安全享受这波惊人的优惠。个人认为,这种模式也为国内内容创作者与工具服务商的合作提供了新思路,期待看到更多类似的创新合作出现。

建了一个小群,大家可以组团或者交换不需要的工具激活码:关注公众号【显林叔】,回复【lenny】或者访问:https://lenny-ai.vadxq.com 即可得到入群二维码

Google发布Agent2Agent(A2A)协议:开启协作式 AI 代理的新时代

作者 vadxq
2025年4月10日 05:30

引言

我们正处在一个 AI 智能体(Agent)蓬勃发展的时代,它们在各种应用中大显身手。但你有没有想过,如果这些聪明的“助手”们能够跨越框架和供应商的限制,更好地沟通协作,是不是能发挥出更大的潜力呢?

现实是,各自为战的 AI 智能体难以实现无缝的信息共享和行动协调,这就像一个团队里大家各说各话,难以完成复杂任务。为了打破这种壁垒,Google 在 Cloud Next 大会上正式发布并开源了 Agent2Agent (A2A) 协议

这项举措旨在为不同来源、不同架构的 AI 智能体提供一个通用的“交流平台”,促进它们之间的无缝互操作。更令人兴奋的是,A2A 一经推出就获得了 50 多家行业领先企业的积极响应和支持,这预示着 AI 代理生态系统即将迎来一个全新的协作时代,有点像当年安卓系统重塑移动生态!今天,我们就来深入了解 A2A 协议,看看它如何让 AI 伙伴们协同工作。

认识 A2A:AI 伙伴间的通用语言

A2A 协议的核心目标就是实现智能体之间的互操作性,它建立在一系列清晰的设计原则之上:

  • 🤝 视代理为伙伴 (Agent-First): 超越简单的“工具调用”思维,让智能体即使没有共享内存或工具也能自主协作。
  • 🔧 基于成熟标准 (Standards-Based): 构建在大家熟悉的 HTTP、SSE(服务器发送事件)、JSON-RPC 之上,极大简化集成,降低开发者门槛。
  • 🛡️ 安全第一 (Secure by Default): 内置企业级身份验证和授权机制,符合 OpenAPI 标准,确保通信安全可靠。
  • 支持长时任务 (Long-Running Tasks): 轻松处理从快速查询到数小时甚至数天的复杂研究,并能提供实时状态更新和反馈。
  • 🗣️ 多种沟通方式 (Modality-Agnostic): 不仅限于文本,原生支持音频、视频流、文件、表单等多种交互模态。

Agent2Agent

为了实现这些,A2A 定义了一些关键的技术组件:

  • 代理卡片 (Agent Card): 每个智能体都有一个标准化的 JSON 文件(可以参考github上的https://github.com/google/A2A),像它的“数字名片”或“黄页”,公开自己的功能、技能、地址和认证需求,让其他智能体能动态发现它。
  • 任务 (Task): A2A 中的核心工作单元,拥有从启动到完成(或失败/取消)的完整生命周期,用于管理和跟踪协作流程。
  • 消息 (Message) & 部件 (Part): 智能体间通过“消息”沟通,每条消息可包含一个或多个“部件”。部件可以是文本 (TextPart)、文件 (FilePart) 或结构化数据 (DataPart),灵活支持多模态交互。
  • 工件 (Artifact): 代表智能体完成任务后生成的输出成果,如报告、文件或数据,同样可包含多个部件。

MCP与A2A协作

A2A 与 MCP:强强联手,各有侧重

提到智能体协议,大家可能也会想到 Anthropic 的模型上下文协议 (MCP)。A2A 和 MCP 并非竞争关系,而是互补的!

  • MCP (模型上下文协议): 主要关注 单个 AI 智能体如何更方便地调用外部工具和获取上下文信息。你可以把它想象成给智能体配上各种趁手的 “工具”或“扳手” 🔧,让它能连接 API、访问数据库等。
  • A2A (Agent2Agent 协议): 则专注于 多个 自主 AI 智能体之间的 直接通信和协作。它更像是智能体之间的 “对话机制” 💬,定义了它们如何找到彼此、协商任务、交换信息、协同行动。

MCP与A2A组合的应用

它们如何协作? 想象一个招聘流程 :

  1. 招聘经理的智能体 (Agent A) 可能使用 MCP 连接内部简历数据库,筛选候选人。
  2. Agent A 筛选出合格者后,通过 A2A 将信息传递给负责安排面试的智能体 (Agent B)。
  3. Agent B (可能也通过 A2A 与日历智能体交互) 完成面试安排。 整个过程流畅高效,每个智能体发挥专长,通过 A2A 无缝衔接。Google 的 Agent Development Kit (ADK) 甚至同时支持这两种协议 。

【A2A 的价值:不止于连接】

A2A 协议的开放性和互操作性带来了巨大的潜力:

  • 🚀 提升效率与生产力: 自动化跨智能体、跨系统的复杂工作流。
  • 🔗 打破数据与应用孤岛: 实现不同平台和服务(如 CRM、ERP、HR 系统)间智能体的协作与信息共享。
  • 💡 促进创新: 允许企业自由组合来自不同供应商、基于不同框架(如 ADK, LangGraph, CrewAI )构建的专业智能体,打造定制化、更强大的 AI 解决方案,避免供应商锁定。
  • 🌐 应用场景广泛:
    • 企业自动化: 订单处理、客户服务升级、供应链管理优化。
    • 客户服务: 构建多层级、专业化的智能客服体系。
    • 科学研究: 连接数据源、模拟工具,加速新药研发、材料发现等。
    • 构建更强 AI 应用: 将具备不同能力的智能体连接起来,处理更复杂、更高级的任务。

拥抱 AI 协作新时代

总而言之,Google 的 Agent2Agent (A2A) 协议是 AI 发展中的一个重要里程碑。它直面日益复杂的 AI 环境中对互操作性和协作的关键需求。

通过为 AI 智能体提供标准化的“通用语言”,A2A 有望打破现有壁垒,开启一个功能更强大、应用更广泛、协作更顺畅的 AI 新时代。对于开发者和企业而言,这意味着能够构建更复杂、更集成、更智能的自动化解决方案,真正释放 AI 的潜力。

让我们一起期待 A2A 协议为 AI 生态带来的变革!

参考新闻:
[1] Google blog: https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability
[2] A2A Docs:  https://google.github.io/A2A
[3] A2A Github: https://github.com/google/A2A

有趣的灵魂在等你

长按二维码识别

颠覆传统搜索!全球AI搜索工具正在攻城略地

作者 vadxq
2025年3月17日 01:14

还在被传统搜索的广告和无效信息折磨?是时候告别低效时代了!AI正以前所未有的力量颠覆搜索体验,一场信息革命悄然来临。从古老的关键词匹配到如今能“读懂你心”的智能引擎,搜索引擎的进化速度超乎想象。大型语言模型等AI技术的崛起,正以前所未有的效率和精准度,重塑我们获取信息的方式。面对信息爆炸、广告围剿和复杂查询的困境,AI凭借自然语言处理和语义搜索,精准捕捉你的意图,提供直达答案的个性化结果。在全球数字化浪潮中,英语和中文无疑是两大引擎。那么,针对这两大语种优化的AI搜索引擎,谁能更胜一筹?它们又将如何塑造未来的信息世界?本报告将深入对比分析中英文AI搜索引擎,揭秘其功能、优劣势,并洞察跨语言动态与特定语言环境下的独特考量,究竟谁能引领未来?

一、AI搜索革命:从「关键词猜谜」到「读心术」

插入对比图:传统搜索 vs AI搜索流程图

插入对比图:传统搜索 vs AI搜索流程图

对比以上两个流程图可以看出:传统搜索流程像一个迷宫,让用户在信息海洋中迷失。而AI搜索像是把答案一条直线连接到用户需求,可以突出其智能分析和直接给出答案的特点

▶ 传统搜索三大酷刑

  • 关键词猜谜:”周杰伦 歌词”搜出演唱会票务广告,“想找附近的咖啡馆,搜‘咖啡’结果全是连锁品牌广告,根本找不到特色小店。”
  • 信息过载:找论文先翻10页垃圾网站,一项调查显示,用户平均需要花费很长时间才能在传统搜索引擎上找到所需信息。
  • 复杂需求抓瞎:”对比iPhone15和小米15Ultra夜景拍摄效果”,想了解‘2024年全球新能源汽车销量前十名及各自的技术特点’,传统搜索需要打开无数个网页进行对比分析。

▶ AI搜索杀手锏

  • 语义理解:听懂潜台词(”找许嵩 拆了东墙补西墙是哪首歌”),不再需要精确的关键词,像和朋友聊天一样提问,AI也能秒懂。
  • 智能摘要:3秒提取20页PDF核心结论,AI不仅能提取核心结论,还能标注信息来源,避免虚假信息。
  • 跨模态搜索:用图片搜论文,语音问财报,AI搜索不再局限于文字,而是可以处理和理解多种模态的信息。

英语区顶流工具优缺点

下面是我列出的市面上主要的优缺点表格

英语区AI搜索工具优缺点

对比维度说明

  • 核心功能:突出各工具最具特色的技术实现和主要应用场景
  • 主要优势:强调差异化竞争优势和独特价值主张
  • 主要劣势:揭示当前存在的使用限制或潜在风险

市场定位分析

  • 学术研究:Perplexity AI > Waldo > Google Gemini
  • 开发者工具:Claude > Phind > ChatGPT > Komo
  • 企业服务:Yep > Microsoft Copilot > You.com
  • 隐私保护:Brave > Andi > Arc Search
  • 多模态处理:Google Gemini > ChatGPT > Bing/Copilot

个人挑选出最优的三个工具

Google Gemini

功能:

  • 自然语言搜索: 用户可以使用自然语言提问,Gemini 能够理解复杂的查询意图。
  • 实时网络浏览: 可以实时访问互联网信息,提供最新的搜索结果。
  • 多模态能力: Gemini 是一个多模态大语言模型,能够处理文本、图像、音频、视频等- 多种数据类型。这意味着它可以理解图片、分析视频内容,并生成包含多种媒体的回复。
  • 与 Google 服务集成: 深度集成到 Google 搜索、Gmail、Docs、Slides 等 Google 常用服务中,提供更便捷的 AI 助手功能。
  • 推理和问题解决: 具备一定的逻辑推理和问题解决能力,可以处理更复杂的查询。
  • “Double-check response”功能: 在 Google 搜索中,Gemini 生成的 AI 概览会提供引用链接,用户可以点击验证信息的来源。
  • 代码生成和解释: 可以生成代码片段,并解释代码的功能。
  • 多语言支持: 支持多种语言。

价格:

免费使用: Gemini 的核心功能通常通过 Google 搜索等产品免费提供。

Google One AI Premium 订阅: 某些高级功能,例如在 Gmail、Docs 等应用中集成 Gemini Advanced (基于更强大的模型),可能需要订阅 Google One AI Premium 计划。该计划包含额外的存储空间和其他权益,价格约为每月 19.99 美元(价格可能因地区而异)。

Perplexity AI

功能:

  • 对话式搜索
  • 多来源分析/实时溯源引用
  • “Keep Exploring”功能:在给出答案后,会提供一些用户可能感兴趣的相关问题,引导用户进行更深入的探索。
  • Collections(收藏夹): 用户可以创建和组织自己的查询和答案,方便日后查阅和分享。
  • 多模型集成: 集成了多种大型语言模型,包括 OpenAI 的 GPT、Anthropic 的 Claude、Mistral AI 的模型以及 Perplexity 自研的模型,可以根据不同的查询选择最合适的模型。
  • 网页总结和思维导图: 可以快速总结网页内容,甚至生成思维导图帮助用户理解。
  • PDF 文献知识库: 用户可以上传 PDF 文献,Perplexity AI 可以帮助建立知识库,并回答与文献内容相关的问题。
  • 图片和视频搜索: 除了文本搜索,还提供相关的图片和视频搜索结果,并支持通过图片进行搜索。

价格:

Free Plan(免费版): 提供基本的搜索和对话功能,每天有使用次数限制。

Pro Plan(专业版):

价格: 通常为每月 20 美元或每年 200 美元(价格可能因地区或促销活动而有所不同)。

权益: 无使用次数限制,更快的响应速度,访问更强大的模型,上传文件进行分析,优先支持等。

Enterprise Plan(企业版): 为企业用户提供定制化的解决方案和功能,价格根据具体需求而定。

Bing Search / Microsoft Copilot

功能:

  • 传统网络搜索增强: Bing Search 在传统搜索的基础上,集成了 AI 模型来提高搜索结果的相关性。
  • Bing Chat (现已整合到 Copilot 中): 提供一个对话式的 AI 聊天界面,用户可以进行自然语言提问,并获得更具互动性的回复。
  • 个性化购物体验: 根据用户的搜索历史、位置等信息,提供更个性化的购物建议。
  • 结构化数据呈现: 能够以文本摘要、列表、电子表格等多种格式呈现结构化数据。
  • 图像生成: 集成了 DALL-E 3 驱动的 Designer,用户可以通过文本描述生成图像。
  • 生成式搜索: 在搜索结果页面提供 AI 生成的见解和摘要。
  • 安全搜索: 提供安全过滤器,帮助用户过滤不适宜的内容。
  • 图像创建器: 用户可以通过文本提示生成图像。
  • 与 Microsoft 365 集成 (Microsoft Copilot): 在 Word、Excel、PowerPoint、Outlook、Teams 等 Microsoft 365 应用中提供 AI 助手功能,帮助用户撰写文档、分析数据、制作演示文稿、管理邮件等。
  • 会议纪要和总结: 可以自动生成会议录音的纪要和关键点总结。

价格:

Bing Search: 免费使用。

Microsoft Copilot (个人版): 通常作为 Windows 的一部分免费提供,提供基本的 AI 助手功能。

Microsoft Copilot for Microsoft 365 (企业版/个人订阅): 需要订阅 Microsoft 365 商业版或个人版才能使用在 Word、Excel、PowerPoint 等应用中的完整 Copilot 功能。价格根据订阅计划而有所不同,例如 Microsoft 365 商业标准版约为每月 12.50 美元/用户(按年付费),Microsoft 365 个人版约为每月 69.99 美元/年。

总结对比

Perplexity AI: 专注于提供透明、可验证的答案,特别适合研究和学习场景,免费版功能强大,专业版提供更多高级功能。

Google Gemini: 拥有强大的多模态能力和与 Google 生态系统的深度集成,适合处理复杂和模糊的查询,核心搜索功能免费,高级功能需要订阅 Google One AI Premium。

Bing Search/Microsoft Copilot: 在传统搜索基础上融入 AI,尤其在提高办公效率方面表现出色,与 Microsoft 365 集成紧密,个人版免费,完整版 Copilot 功能需要 Microsoft 365 订阅。

中文区黑马突围战

列出市面上主要的优缺点表格

中文区AI搜索工具优缺点

挑选出最优的三个工具

夸克AI搜索 / 通义

功能:

  • “AI超级框”: 这是夸克AI搜索的核心特色,旨在将传统的搜索框升级为一个集成了AI对话、深度思考、深度搜索、深度研究和深度执行的一站式平台。用户可以在这个框内完成从提问到获取最终答案的整个过程,无需跳转多个页面或进行复杂的关键词调整。
  • 智能交互: 强调与用户的智能对话,能够理解用户的自然语言提问,并进行多轮交互以完成用户需求。
  • 深度搜索: 不仅仅是关键词匹配,而是基于对用户意图的理解进行更深层次的信息检索。
  • 深度研究: 具备一定的分析和整合能力,能够从多个来源提取信息并进行总结。
  • 深度执行: 意味着可能具备一定的行动能力,例如直接提供可执行的建议或引导用户完成特定任务。
  • 通义大模型支持: 背靠阿里巴巴的通义大模型,拥有强大的自然语言处理和生成能力。

优势:

  • 颠覆传统搜索逻辑: “AI超级框”的概念试图改变用户使用搜索引擎的习惯,从“关键词匹配+网页链接”模式升级为“智能交互+一站式答案”,有望提高信息获取的效率和精准度。
  • 强大的AI能力: 依托通义大模型,具备优秀的语义理解、知识问答和内容生成能力。
  • 一站式解决需求: 理论上可以在一个界面内完成多种任务,减少用户的操作步骤。
  • 潜在的生态整合能力: 作为阿里巴巴旗下的产品,未来可能与淘宝、支付宝等阿里生态内的其他服务进行更深入的整合。

潜在劣势/市场定位分析:

  • 用户习惯培养成本: 颠覆性的设计可能需要用户适应新的搜索方式。
  • “超级框”的复杂性: 功能集成过多可能会导致界面复杂,反而降低易用性。
  • 与阿里生态的绑定: 如果过度依赖阿里生态,可能会限制其在非阿里用户群体中的普及。
  • 实际体验待观察: “深度执行”的具体能力和效果还需要在实际使用中检验。

市场定位:

夸克浏览器本身在用户群体中具有一定的基础,AI搜索的加入可能会吸引更多追求效率和智能化体验的用户。

微信AI搜索 / 腾讯元宝

功能:

  • 微信生态内搜索: 微信拥有庞大的用户基数和丰富的内容生态(包括公众号文章、朋友圈、视频号、小程序等),微信AI搜索的核心优势在于能够便捷地搜索这些内容。
  • 腾讯元宝赋能: 腾讯元宝是腾讯推出的AI助手产品,微信AI搜索的升级很可能得到了腾讯元宝的技术支持,意味着其在自然语言理解、知识问答等方面具备更强的能力。
  • 潜在的跨平台搜索能力: 除了微信生态内的内容,未来也可能拓展到更广阔的互联网信息。
  • 语音搜索和图像搜索: 依托微信强大的语音和图像识别技术,可能提供更便捷的搜索方式。
  • 社交属性的结合: 或许会结合微信的社交属性,例如基于朋友推荐或群组讨论进行搜索结果的优化。

优势:

  • 庞大的用户基础: 微信的月活跃用户数非常庞大,这为微信AI搜索的普及提供了天然的优势。
  • 丰富的内容生态: 微信内拥有海量的优质内容,AI搜索能够帮助用户更高效地发现和获取这些信息。
  • 便捷的使用场景: 用户无需下载额外的App,在日常使用的微信中即可完成搜索。
  • 潜在的社交互动: 结合社交属性的搜索可能带来更个性化和可信赖的结果。
  • 腾讯强大的AI技术支持: 腾讯在AI领域的技术积累为微信AI搜索提供了坚实的基础。

潜在劣势/市场定位分析:

  • 搜索范围的局限性: 目前来看,微信AI搜索可能更侧重于微信生态内的内容,对于需要搜索更广泛互联网信息的用户来说可能不够全面。
  • 隐私担忧: 微信作为社交平台,用户对其数据的使用可能存在一定的隐私担忧。
  • 商业化考量: 微信搜索可能受到商业化因素的影响,例如广告的展示方式等。

市场定位:

微信AI搜索主要服务于微信用户,旨在提升在微信生态内的信息获取效率和用户体验。

豆包AI

功能:

  • 字节跳动旗下产品: 豆包AI 由字节跳动推出,字节跳动在内容推荐和算法方面拥有强大的技术积累。
  • 潜在的短视频和内容推荐优势: 考虑到字节跳动在抖音/TikTok 等短视频平台的成功,豆包AI 很可能在短视频内容的搜索和推荐方面具有独特的优势。
  • 更广泛的AI助手能力: 除了搜索功能,豆包AI 可能还具备更广泛的AI助手能力,例如文本生成、对话聊天、知识问答等。
  • 个性化推荐: 可能会根据用户在字节跳动旗下平台上的行为和偏好,提供更个性化的搜索结果和内容推荐。

优势:

  • 强大的内容推荐算法: 字节跳动在内容推荐方面的技术领先地位是豆包AI 的重要优势。
  • 短视频搜索的潜力: 在短视频内容日益重要的今天,豆包AI 有望成为用户搜索和发现短视频内容的重要工具。
  • 潜在的跨平台整合: 未来可能与抖音、今日头条等字节跳动旗下的其他产品进行整合。
  • 新兴AI力量: 作为字节跳动在AI领域的布局,豆包AI 有望带来一些新的创新功能。

潜在劣势/市场定位分析:

  • 可能侧重于字节跳动生态: 类似于微信,豆包AI 的搜索结果可能更倾向于字节跳动平台上的内容。
  • 通用搜索能力待观察: 在更广泛的互联网信息搜索方面,豆包AI 的能力可能还需要进一步提升。
  • 品牌认知度: 相对于夸克和微信,豆包AI 的品牌认知度可能还需要一定的提升时间。

市场定位:

豆包AI 的目标用户可能更偏向于年轻群体和对短视频内容有较高需求的用户。

总结

这三款中文AI搜索工具都背靠着中国互联网巨头,由于国内免费的用户习惯以及各大APP的信息茧房,其实各自的优缺点更多是针对于各家的搜索独特数据源来的。各自具有独特的优势和侧重点:

  • 夸克AI搜索/通义:背靠阿里巴巴,拥有强大的内容推荐和算法能力,有望在搜索领域带来新的突破。
  • 微信AI搜索/腾讯元宝:依托微信庞大的用户基础和丰富的内容生态,有望在社交搜索领域取得显著优势。
  • 豆包AI:作为字节跳动在AI领域的布局,有望带来一些新的创新功能,尤其是在娱乐领域和字节系如抖音、今日头条等短视频搜索方面。

未来搜索预言

短期内的趋势猜测

  • 场景化搜索: 例如对着汽车说“附近哪里有充电桩”,AR导航直接显示路线。或者是拍冰箱照片→推荐本周菜谱+盒马一键下单
  • 人格化助手:比如用鲁迅文风写邮件催尾款,比如用李白的风格写一首催人泪下的情诗并且邮寄给你精彩问怎么追的女生微信上
  • 防AI幻觉协议:自动标注内容可信度等级,减少AI幻觉,是deepseek推理模式的再度升级化

担忧

  • 隐私问题:AI搜索可能会收集用户的搜索习惯和偏好,这可能会导致隐私泄露。
  • 信息茧房:AI搜索可能会导致用户被锁定在特定的信息流中,这可能会导致信息茧房效应。
  • 安全问题:AI搜索可能会被用于传播错误信息,这可能会导致安全问题。

【2023年终总结】与逝去的年华道个别

作者 vadxq
2023年12月31日 23:21

这一年,过的可真快呀!最后缅怀的时间很少,那就与与逝去的年华道个别吧

前言

这一年,过的可真快呀!最后缅怀的时间很少,那就与与逝去的年华道个别吧

与All in Web3的自己道别

围绕着Web3进行的build,在上半年还是很好很有精力,是一个个人认为很Crypto,很积极建设社区的氛围。上半年做了很多的事情。在BuidlerDAO里,做了DAO工具,做了一个完善框架的Web3教育产品,用Flutter做了一个圈子知识付费产品,做了些自己想做的邮箱体系,Docs体系等。在DeBox的开源生态里做了DeBoxLove生态,做了开源的DeBox SDK功能。也参与了一些其他Web3的项目,但是都是涉入不深。这一年,自己似乎做了很多,但是什么都没做好,再也没有以前做Vitae3的成就感和奋斗感。不知从何其,越来越多的活让自己再无心关注业内前沿,再无心去关注开源生态,也无心去与人交流和分享,陷入无限的自我损耗,有心而无力的感觉越来越深。遂于年末,和All in Web3道了别,但是不会远离,只是以兴趣与业余去研究。

luori

与追逐乌托邦技术的自己道别

今年总计提交了1744commit,数量相对前两年是有下降的,但是相对质量和技术积累有所进步的,今年主要是围绕着Nextjs工程化和推进React Server Components而进行工程化适配和改造。同时今年也对Flutter得工程化进行了改造,对最贴近Flutter官方RoadMap的工程化体系进行了深究与项目实践。具体的总结可以见我的GitHub代码,希望能对开源有所贡献。这一年对于Web3原生态技术的追逐越来越弱了,但是对于Rust还是很向往,曾利用Rust写了钱包生成的Demo。曾经的自己是一个有代码洁癖,向往开源与技术改造世界的乌托邦世界的技术人。向往做一个浪漫而简单的技术人,沉迷技术,做好自己。而今创业的经历,以及经济下行行情下办公室的环境氛围,埋头苦干的人并不能得不到好的结局,你无法想象当行情不好的情况下,小利益也会竞争的不可开交。也许是时候和过去纯乌托邦的幻想而道别,在原则性的前提下,做一些改变反而可以让自己可以更好的做技术。

github commit

与沉迷独自念旧的自己道别

这一年去了好几个地方,处于上海和江西自不必说。去了苏州,游过拙政园,踏过虎丘,随船穿过十里山塘。去了成都,住于琴台,逛了春熙路太古里万象,穿过了宽窄巷子,吃遍了乐山,排过了玉林的队,批发了菜市场的特产。也去了大理,住于苍山脚下,绕了圈洱海,登顶了玉龙雪山。最后从远程工作又兜兜转转的回到了上海。这一年的自己,和很多人都道别了。苏州,上海,南昌,上饶,大理。每一个地方,也许都是和一些人的最后碰面。站在年终的世界,也许只是自己一个人的念旧与回首。自己一直是一个很仪式感,很念旧的人。其实仪式感也是念旧的一种,可惜,时代变了,世界变了,身边变了,我也变了。这一年,也见证了浪浪的求婚,围观了他们的婚纱照,也见证了子建学长步入婚姻的殿堂。世界也是有美好在传递。但也是记忆中的存在了,和过去的自己道个别吧。

lang

新的一年:变

一切都在变化,个人是无法应对种种因果下的变化的伟力。也许拥抱变化,做最适合当下的舒服且有竞争力的自己。改变衣食住行,做一个积极向上的人。放弃幻想,摒弃固守己见。放弃固守技术,多接触不同职业渠道。也许变,是新的一年的主题。

by vadxq

2023.12.31 上海

【Swift】Swift学习计划与资料

作者 vadxq
2021年12月9日 11:21

2021年了,flutter玩过了,就想看看原生是怎么一回事,看看iOS怎么玩玩。仅仅是个人兴趣爱好,就想玩一玩。学习学习,技多不压身。也想横向扩展一下自己的知识面,对比不同语言的异同。
目前每天更新

学习与实战计划

  • day1-day7: 基础学习
  • day7-day14: 模拟出个视频App首页(主要训练首页一个页面,列表,绘制ui,连接接口数据)
  • day14-day21: 详情播放页,我的页面(登录注册)
  • day21-day30: webview打开h5,模仿更多细节和交互,重训练逻辑

上面的模仿不一定是一样功能,一模一样,就是模仿到那个味道就好了。主要是训练自己

学习资料推荐

  • 零基础了解Swift和Xcode:视频,0基础推荐
    • 地址:BBCo - iOS开发零基础教程 Swift
    • Up讲解很详细,适合真0基础了解Swift开发是怎么一回事
    • 讲解的知识点包括:基础知识,数组,字典,函数,类,协议这些。还有xcode怎么用的细节
    • 缺点:后面视频收费了,看看就好,如果你觉得好可以购买,不想花钱就看下面列的资料
  • Stanford CS193p 2021: 斯坦福2021年SwiftUI课程
  • SwiftUI Example练习文字版教程

学习进度

  • 2021.12.09: 看完BBCo - iOS开发零基础教程 Swift教程
  • 2021.12.10

【图解算法】前端思维学习图解算法数据结构笔记(一)之算法复杂度

作者 vadxq
2021年9月22日 11:21

书名是《图解算法数据结构》,在力扣看到的,准备开始系统学习一下算法了。会用前端的语言和思维来学习学习~希望能获得一些见解。第一篇是关于算法复杂度的学习笔记记录。

算法复杂度

复杂度概念

书中介绍,算法复杂度旨在计算在输入数据量 N 的情况下,算法的时间使用空间使用情况;体现算法运行使用的时间和空间随「数据大小N」而增大的速度

  • 时间:假设各操作的运行时间为固定常数,统计算法运行的计算操作的数量,以代表算法运行所需时间
  • 空间:统计在最差情况下,算法运行所需使用的”最大空间”
  • 数据大小N:算法处理的输入数据量;根据不同算法,具有不同定义
    • 排序算法:N 代表需要排序的元素数量
    • 搜索算法:N 代表搜索范围的元素总数

理解:算法复杂度是在一定的输入数据量下,计算所需要的时间操作数量大小(计算操作数量)和空间的大小来体现复杂度量。

时间复杂度

概念:时间复杂度指输入数据大小为 N 时,算法运行所需花费的时间

注意的是,这里的时间是计算操作数量,和运行绝对时间呈正相关关系,并不相等。

时间复杂度具有最差平均最佳三种情况,分别使用O, Θ, Ω三种符号表示

1
2
3
4
5
6
7
8
9
10
11
12
13
// demo
const find_num = (nums) => {
for (const num in nums) {
if (num === 7) return false
}
return false
}

// 最佳情况Ω(1): nums = [7, a, b, c, ...] ,即当数组首个数字为 77 时,无论 nums 有多少元素,线性查找的循环次数都为 11 次;

// 最差情况O(N): nums = [a, b, c, ...] 且 nums 中所有数字都不为 77 ,此时线性查找会遍历整个数组,循环 N 次

// 平均情况Θ: 需要考虑输入数据的分布情况,计算所有数据情况下的平均时间复杂度;例如本题目,需要考虑数组长度、数组元素的取值范围等

常见的算法时间复杂度大小排序:O(1) < O(logN) < O(N) < O(NlogN) < O(N^2) < O(2^N) < O(N!)

  • 常数O(1):不随输入数据大小 N 的变化而变化
  • 线性O(N):循环运行次数与 N 大小呈线性关系
  • 平方O(N^2):两层循环相互独立,都与 N 呈线性关系,因此总体与 N 呈平方关系
  • 指数O(2^N):指数阶常出现于递归
  • 阶乘O(N!):阶乘阶对应数学上常见的 “全排列” 。即给定 NN 个互不重复的元素,求其所有可能的排列方案。阶乘常使用递归实现,算法原理:第一层分裂出 N 个,第二层分裂出 N−1 个,…… ,直至到第 N 层时终止并回溯
  • 对数O(logN):对数阶与指数阶相反,指数阶为 “每轮分裂出两倍的情况” ,而对数阶是 “每轮排除一半的情况”
  • 线性对数O(NlogN):两层循环相互独立,第一层和第二层时间复杂度分别为 O(logN) 和 O(N)

个人理解

  • 常数O(1):就是传入变量不影响计算,相当于一个未使用的变量。eslint下应该是遇不到的
  • 线性O(N):与输入N线性关系,其中只有一次循环是和输入的N有关
  • 平方O(N^2):两层互相独立的线性关系嵌套,两层嵌套循环,比如冒泡排序
  • 指数O(2^N): 树形图,每个节点都有两个操作,节节往下,第一层祖父节点,2^0 ,第二层就是2^1 ,以此类推,最后就是 2^n-1 次方,加起来就是 2^n
  • 阶乘O(N!):全排列,和上面反过来,第一次=层全部循环一遍,然后第二层操作剔除一个特征操作,以此类推
  • 对数O(logN):和指数反过来,就是二分法,分治这样的算法基础,一分为二或者一分为多。
  • 线性对数O(NlogN):两层循环相互独立,第一层和第二层时间复杂度分别为O(logN)和O(N)

空间复杂度

概念:空间复杂度指在输入数据大小为 N 时,算法运行所使用的暂存空间+输出空间的总体大小

  • 输入空间: 存储输入数据所需的空间大小;输入数据
  • 暂存空间: 算法运行过程中,存储所有中间变量和对象等数据所需的空间大小;包括数据空间``栈帧空间``指令空间
    • 数据空间:各项变量使用的空间,包括:声明的常量、变量、动态数组、动态对象等使用的内存空间。
    • 栈帧空间:程序调用函数是基于栈实现的,函数在调用期间,占用常量大小的栈帧空间,直至返回后释放。
    • 指令空间:编译后,程序指令所使用的内存空间。
  • 输出空间: 算法运行返回时,存储输出数据所需的空间大小;输出数据

最差情况:空间复杂度统计算法在 “最差情况” 下使用的空间大小,以体现算法运行所需预留的空间量,使用符号O表示,包含两种情况

  • 最差输入数据:当 N≤10 时,数组 nums 的长度恒定为 10 ,空间复杂度为 O(10) = O(1);当 N > 10时,数组 nums 长度为 N ,空间复杂度为 O(N) ;因此,空间复杂度应为最差输入数据情况下的 O(N)
  • 最差运行点:在执行nums = Array(10).fill(0)时,算法仅使用 O(1) 大小的空间;而当执行 nums = Array(n).fill(0)时,算法使用 O(N) 的空间;因此,空间复杂度应为最差运行点的 O(N)

常见的算法空间复杂度有:O(1) < O(logN) < O(N) < O(N^2) < O(2^N)

  • 常数O(1):普通常量、变量、对象、元素数量与输入数据大小 N 无关的集合,皆使用常数大小的空间
  • 线性O(logN):元素数量与 N 呈线性关系的任意类型集合(常见于一维数组、链表、哈希表等)
  • 平方O(N):元素数量与 N 呈平方关系的任意类型集合(常见于矩阵),皆使用平方大小的空间
  • 指数O(N^2):指数阶常见于二叉树、多叉树
  • 对数O(2^N):对数阶常出现于分治算法的栈帧空间累计、数据类型转换等。如快速排序/数字转化字符串

时空权衡

对于算法的性能,需要从时间和空间的使用情况来综合评价。优良的算法应具备两个特性,即时间和空间复杂度皆较低。而实际上,对于某个算法问题,同时优化时间复杂度和空间复杂度是非常困难的。降低时间复杂度,往往是以提升空间复杂度为代价的,反之亦然。

由于当代计算机的内存充足,通常情况下,算法设计中一般会采取空间换时间的做法,即牺牲部分计算机存储空间,来提升算法的运行速度。

一些DEMO

时间复杂度相关的demo

1
2
3
4
5
6
7
8
9
10
11
const algorithm = (n) => {
let count = 0;
const a = 100;
for (const i in n) {
for (const j in a) {
count += 1;
}
}
return count;
}
// 时间复杂度是o(N),一直一层循环是与输入有关
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 冒泡排序
const bubbleSort = (nums) => {
const n = nums.length;
for (const i in n - 1) {
for (const j in n - i - i) {
if (nums[j] > nums[j + 1]) {
cache = nums[j]
nums[j] = nums[j + 1]
nums[j + 1] = cache
}
}
}
return nums;
}
// 时间复杂度是O(N^2),第一层复杂的o(N), 第二层平均循环次数为N/2,复杂度也为o(N)
1
2
3
4
5
6
7
8
// 递归
const algorithm = (n) => {
if (n <= 0) return 1
const a = algorithm(n - 1)
const b = algorithm(n - 1)
return a + b;
}
// 时间复杂度是O(2^N)
1
2
3
4
5
6
7
8
9
10
// 递归
const algorithm = (n) => {
if (n <= 0) return 1
let count = 0
for (const i in n) {
count = algorithm(n - 1)
}
return count;
}
// 时间复杂度是O(N!)
1
2
3
4
5
6
7
8
9
10
11
// 一分为二,二分查找
const algorithm = (n) => {
let count = 0
let i = n
while (i > 1) {
i = i / 2
count += 1
}
return count
}
// 时间复杂度是O(logN)
1
2
3
4
5
6
7
8
9
10
// 线性对数常出现在排序算法中,快速排序、归并排序、堆排序
const algorithm = (n) => {
let count = 0
let i = n
while (i > 1) {
i = i / 2
for (const j in n) count += 1
}
}
// 时间复杂度是O(NlogN)

空间复杂度的相关demo

1
2
3
4
5
6
const child = () => 0

const parent = (n) => {
for (const i in n) child()
}
// 空间复杂度o(1),因为栈帧空间被释放掉了,每次掉用child()后
1
2
3
4
5
6
// 递归调用
const parent = (n) => {
if (n <= 1) return 1
return parent(n - 1) + 1
}
// 空间复杂度o(N),因为递归调用,会存在N个未返回的函数,累计使用O(N)大小的栈帧空间
1
2
3
4
5
6
// 最差运行点
const algorithm = (n) => {
const num = 5 // O(1)
let nums = Array(10).fill(0) // O(1)
if (n > 10) nums = Array(n).fill(0) // O(N)
}

时空权衡的demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 两数之和
// 方法一:暴力枚举
var twoSum = function(nums, target) {
const len = nums.length;
for (let i = 0; i < len - 1; i++) {
for (let j = i + 1; j < len; j++) {
if (nums[i] + nums[j] === target) {
return [i, j]
}
}
}
};
// 时间复杂度 O(N^2),空间复杂度 O(1);属于时间换空间,虽然仅使用常数大小的额外空间,但运行速度过慢。


// 方法二:辅助哈希表
const twoSum = function(nums, target) {
const len = nums.length;

// 使用哈希表存储当前值
const map = new Map();
for (let i = 0; i < len; i++) {
map.set(nums[i], i);
}

for (let i = 0; i < len; i++) {
const needNum = target - nums[i];

if (map.has(needNum) && i !== map.get(needNum)) {
return [i, map.get(needNum)]
}
}
}
// 时间复杂度 O(N),空间复杂度 O(N);属于「空间换时间」,借助辅助哈希表 dic,通过保存数组元素值与索引的映射来提升算法运行效率,是本题的最佳解法。

// 方法三:哈希优化
const twoSum = function(nums, target) {
const len = nums.length;
const map = new Map();

for (let i = 0; i < len; i++) {
const needNum = target - nums[i];

if (map.has(needNum) && i !== map.get(needNum)) {
return [i, map.get(needNum)];
}

// 边读边存
map.set(nums[i], i);
}
}

【三亚攻略】2021年三亚团建攻略

作者 vadxq
2021年3月20日 21:21

2021年!三月,团建,三亚。由于自由活动时间受团体活动限制,网上攻略只能取其精华去其糟粕。故形成此特此且定向攻略。欢迎诸君企业微信向我提出想要添加的添加分类和知识。我会逐一更新,此文只是将各种参考攻略合集,然后分析距离和时间的可行性供大家参考,然后诸君可以借此形成适合自己的攻略.

本攻略食用指南,左边目录可以跳转到相关内容。地图上标记了相关景点和住所,缩放可以查看景点距离住所的大概距离。

一、时间点

1.1-共同时间

  • 3.27(周六) 中下午晚上跟团!13.30 团建 18.00 集体吃饭,亚龙湾1号小镇奥特莱斯
  • 3.28(周日) 全天跟团!8.30 集合 9.30 码头 上岛,后自由活动 最后一班轮渡 17.30 15.00 码头集合 18:00酒店集合去亚龙湾吃饭
  • 3.29(周一) 自由活动

1.2-个人时间(文章最后将根据各个航班时间给出推荐游玩路线)

!!!注意!如果行李多行走慢的建议再提前点时间!

A 航班号

  • 3.26(周五) 10.00 前到机场 11:30-13:55
  • 3.30(周二) 17:30 发车前往机场 20:25-23:00

B 航班号

  • 3.26(周五) 19.40 前到机场 21:20-01:05
  • 3.30(周二) 13:40 发车前往机场 8.05 航班 16:20-19:40

C 航班号

  • 3.26(周五) 11.00 前到机场 12:30-16:05
  • 3.30(周二) 5.20 发车去机场 08:05-11:20

D 航班号

  • 3.26(周五) 16.45 前到机场 18:25-21:50
  • 3.30(周二) 09:50 发车去机场 12:30-15:35

二、住宿地点与活动范围

2.1-住宿金茂三亚希尔顿酒店(地图亚龙湾)

2.2-上岛在蜈支洲岛,另一个海湾,海棠区

三、景点篇

3.1-亚龙湾景点(距离最近)

包含:亚龙湾热带天堂森林公园 亚龙湾爱立方滨海乐园 亚龙湾国际玫瑰谷 擎天石 红霞石 八仙庙 飞跃雨林 滑索 雨林魔幻迷宫 泰裕仙 亚龙湾假日度假酒店-小星星儿童乐园 雅尚摄影 春光生态椰岛服 秀都魔幻城 小刀专业海钓潜水俱乐部 龙仔湾 三亚亚泽游艇俱乐部 桃花酿 喜麦蒂精酿鲜啤酒吧 三亚海生活 PADI 游艇潜水俱乐部

3.1.1-亚龙湾看日出

距离,最近,住的地方便可看到

3.1.2-亚龙湾热带天堂森林公园

  • 主要景点:兰花谷/过龙江索桥(《非诚勿扰 2》的拍摄地)/山顶
  • 费用:门票+往返游览车票 158 元,买票一定要提前一个小时
  • 时间:预计半天
  • 距离:5.2km,打车 12min/18 元

3.1.3-商业街

推荐 亚龙湾1号小镇奥特莱斯

亚龙湾的新商圈,位于亚龙湾核心地段,各种大品牌折扣店,超市,儿童乐园,电影院,各类特色餐厅等,吃喝玩乐都在一起,重点是这里有个亚龙湾唯一的一个海鲜广场,在亚龙湾能吃到新鲜且性价比高的海鲜,也就只有这里了,大名鼎鼎的林姐香味海鲜在这里也设有分店。

亚龙湾1号小镇奥特莱斯>亚泰商业中心>百花谷

3.1.3-其他景点(如果有遗漏或者想要加上详细描述可以私我)

亚龙湾爱立方滨海乐园 亚龙湾国际玫瑰谷 擎天石 红霞石 八仙庙 飞跃雨林 滑索 雨林魔幻迷宫 泰裕仙 亚龙湾假日度假酒店-小星星儿童乐园 雅尚摄影 春光生态椰岛服 秀都魔幻城 小刀专业海钓潜水俱乐部 龙仔湾 三亚亚泽游艇俱乐部 桃花酿 喜麦蒂精酿鲜啤酒吧 三亚海生活 PADI 游艇潜水俱乐部

3.2-海棠湾景点(距离 32km,驾车 38 分钟,公交一个半小时左右)

3.2.1-蜈支洲岛景点(公司包车)

  • 蜈支洲岛旅游风景区(6-8 小时)
  • 情人桥(1 小时)
  • 蜈支洲岛海滨浴场(1 小时)
  • 观日岩(1 小时) (适合看日出,根据我们的上岛时间不推荐)
  • 《私人订制》淡水泳池(2 小时)

观光车档位

  • 120 元,环岛一周约 60 分钟,在观日岩、妈祖庙、夏季电瓶车候车区、海洋之星四个景点停靠。
  • 150 元,环岛一周约 60 分钟,在观日岩、情人岛、妈祖庙、夏季电瓶车候车区、海洋之星五个景点停靠。

3.2.3-海南三亚海棠湾免税店—全球最大的免税店

详情见购物免税栏目,推荐时间是我们离开蜈支洲岛的时候去购买比较方便

3.2.4-林旺镇和藤桥镇

比较远啊…性价比不高

3.3-三亚湾景点(距离住的地方 40km 左右,驾车 56 分钟,公交 2 个半小时,打车约 140 元)

以下景点最好选择部分,因为距离较远

3.3.1-第一市场(夜市/林姐香味海鲜/四川小胡子海鲜店)

距离 19km,打车半小时,公交一个半小时。适合吃好多好多好吃的!!!!适合下午晚上来!

3.3.2-三亚湾沙滩(看日落!!!)

  • 三亚湾椰梦长廊(门票 0/用时 1 小时)
  • 三亚湾中段沙滩

3.3.3-鹿回头公园

登高望海、观看日出日落、俯瞰三亚市全景的佳处,是三亚夜景的最佳观赏点。

3.3.4-临春岭公园(根据咱们的行程,不推荐这)

三亚市内爬山的好去处,重点是没有缆车,全程只能徒步,免费景点。

3.3.5-凤凰岭

都是公园,地理位置较远,看个人选择

3.3.6-天涯海角

就是两块刻了天涯海角的石头…

3.3.7-西岛

距离三亚市区较近的一座小海岛,需要坐船,是三亚海上游玩项目最多,且性价比最高的海岛。

但是吧,时间不允许,蜈支洲岛就够了

3.3.8-南山寺

拥有 108 米世界上最高的三面观音像,景区很大,可游玩性很高,是礼佛的好去处。

3.3.9-千古情

晚上的演出很震撼,对海南许多传说轶事做了非常生动的艺术诠释,除了演出景区还有冰雪世界,动物世界,浪浪水公园

3.4-大东海(距离住的地方 20km 左右,驾车 530 分钟,公交 1 个半小时,打车约 65 元)

3.5-其他

  • 呀诺达雨林文化旅游区

距离:35km,驾车 52 分钟,公交两个小时,不推荐…性价比低

附近包含景点: 梦幻谷 雨林谷 三道谷 HOLD 住爱电影主景地 桐亭 峡谷幽湖 千年夫妻榕 碧玉亭 八榕观景台 一柱亭 千年灵芝洞 鹦鹉表演广场 幸福天道 雨林花架观赏区 雨林登峰区 兰花溪景观区 船型屋景观区 鸟语花香景观区 吉祥平台 神龟石 荷花池

四、美食篇

4.1-海鲜

三亚的海鲜相当有名,来到三亚不试试这里的生猛海鲜那你的旅途将会是不完整的。在三亚若想吃上经济实惠的海鲜,自然是去声名远播的海鲜加工广场,比如第一市场、春园海鲜加工广场等。在这里你可以自己选购海鲜,然后找店加工就可以了。海鲜通常烹调方式有火锅、蒸、炒为主,只是收取相应的加工费。加工前都先询价,一般加工费是 100 左右,不会很贵

推荐店:林姐香味海鲜/梅姐川味海鲜连锁加工店(第一市场总店)

4.2-抱罗粉

抱罗粉是米粉的一种,颜色呈半透明状,软而韧,非常有嚼劲的。好吃的抱罗粉关键在于汤靓,把精心熬煮的猪骨或牛骨汤放入早已烫熟凉却的米粉,然后入炒花生米、炒芝麻仁、炒酸菜等配料。

推荐店:正宗抱罗粉店/祥姐抱罗粉(二十年老字号)

4.3-椰子鸡

因当地盛产椰子,椰子鸡也结合了当地人的智慧结晶。将子鸡切成小块放入精选的老椰子内,再加入适量的料酒、酱油等配料蒸煮,出品汤汁鲜香,鸡肉滑嫩。

推荐店:萌哒哒椰子鸡(情人桥店)/嗲嗲的椰子鸡(明珠广场店)

4.4-文昌鸡

为海南“四大名菜”之一,去当地必吃的菜肴。文昌鸡身材娇小、肉质鲜嫩,隔水清蒸后,待菜品凉后切成小块,配以当地酱料便可食用。

推荐店:萌哒哒椰子鸡(亚龙湾森林公园店)/琼菜王婚宴

4.5-椰子饭

将精选的海南优质糯米放入装着椰肉和椰汁的椰子内蒸煮,蒸熟后的椰肉与米饭相互融合,硬软相间,入口香甜可口,椰香浓郁。

推荐店:阿浪海鲜连锁加工(第一市场店)/7岁家庭餐厅(海棠湾林旺北风情小镇店)

4.6-冬瓜海螺汤

冬瓜海螺汤是当地必尝的特色菜之一。主要原料是冬瓜和海螺,汤清且甜。冬瓜性凉而味甘,加上海螺的鲜香,其营养丰富,能解渴消暑、去除水肿。

推荐店:王梅妹多客来海鲜加工店(友谊海鲜广场B栋11号店),这条推荐存疑,请自行查找

4.7-清补凉

清补凉多以糖水或老火汤的形式出现,具有健脾润肺的作用。清补凉食材丰富,如:花生、红豆、绿豆、通心粉、新鲜椰肉、芋头等,其口感层次丰富,美味至极。

推荐店:张猫猫的店(大东海店)/不仔客海鲜(第一市场总店)

4.8-和乐蟹

和乐蟹是海南四大名菜之一。其膏满肉肥,为其它青蟹罕见,特别脂膏金黄油亮,犹如咸鸭蛋黄,香味扑鼻。常见的制作方法是清蒸,蘸以姜醋配成的调料,原汁原味。

推荐店:阿浪海鲜连锁加工(第一市场店)/梅姐川味海鲜连锁加工店(第一市场总店)

4.9-东山羊

东山羊由于主产于海南万宁的东山岭,所以被当地人简称为“东山羊”。肉质鲜美有弹性,脂肪呈淡黄色,经传统方法烹饪后,肉嫩汤白,无膻味。

推荐店:琼菜王婚宴

4.10-各种热带水果

推荐去第一市场吃!

4.11-其他食物

四角豆/水果炒冰/加积鸭/南山素斋/陵水酸粉

五、特产(可邮寄亲朋好友类)

尽量在市区买,不要在景点

  • 热带水果
  • 珍珠、水晶、贝壳、手工纪念品
  • 椰子食品–椰子糖/椰奶等
  • 海南咖啡
  • 果肉果干
  • 海产干货

六、购物免税方面

6.1-免税政策

  • 每人每年免税额度10万元
  • 每人每次限购化妆品30件手机电话4件,酒类不超过1500ml,其他不限件数,但必须一次性随身带离岛
  • 取消单件8k元免税限额

据个人对比,apple产品和教育版产品差不多价格。

6.2-推荐去最大的免税店-海棠湾免税店

6.2.1-A区(进门右手边:主营彩妆、护肤和大牌奢侈品

  • 1-2楼

品牌:香奈儿、兰蔻、后、雪花秀、AHC、馥蕾诗、雅诗兰黛、迪奥、SK2、HR、资生堂、肌肤之钥、MAC、科颜氏等

  • 3楼

名品折扣区、儿童乐园、烟酒、运动品牌、特产

6.2.2-B区(进门左手边,主营珠宝首饰、服饰、儿童玩具、食品

  • 1-2楼

国际化大腕品牌为主:万国、伯爵、宝格丽、蒂芙尼、TWC、普拉达、欧米伽、浪琴、天梭、阿玛尼等

  • 3楼

餐饮区,快餐等

6.3-扫货建议

6.3.1-开门时间

免税店9点30分开门,建议您在9点前就到,在门口有序排队。上午中午下午人都很多,晚上8店之后人会逐渐变少(一点),22点30分准时关闭

6.3.2-提货地点

购物后提货单保管好,离岛时在机场或火车站凭身份证和提货单提货机场提货(安检后)

海口美兰:15号登机口旁

三亚凤凰:109号登机口对面

6.3.3-优惠

  • 申请海南健康码时,可以领取两张50元代金券
  • 进店先到服务台办好会员卡(免费),食品、日用10元积1分,手表、饰品50元积1分,化妆、衣服30元积1分,1积分=1元,购买商品积分可以直接抵现,结账前告诉店员用积分就好
  • 名品折扣商品:橘标7折、绿标6折、紫标5折、蓝标4折
  • 套盒优先、大额优先,因为积分第二笔有时候就已经够抵扣啦

6.3.4-购物时间/运输时间

购买时间:免税店不全在机场,需保证产品到机场的运输时间

  • 现场线下
    • 三亚凤凰机场登机: 离岛前6小时
    • 其他机场登机: 离岛前24小时
  • app-线上
    • 三亚凤凰机场登机: 离岛前24小时
    • 其他机场登机: 离岛前40小时

七、必备物品

只是罗列出来,根据个人需要选择,除了团建用品和证件,不都是必须的

  • 证件类:身份证、手机、充电器
  • 团建用品:公司衣服/活动队马甲/电脑,以防改bug
  • 防晒类型:防晒霜、太阳镜、帽子、晒后修复(芦荟胶、面膜、晒后修复霜等)
  • 抗蚊虫:驱蚊水、抗过敏药、风油精
  • 药物:防中暑感冒药、止泻药(以防海鲜吃坏了)、晕车药
  • 沙滩潜水物品:沙滩鞋(花园洞洞鞋)、衣服、防水袋、耳塞、泳装、游泳眼镜
  • 个人用品:纸巾、衣服

八、推荐自由行路线

列出了候选项,可以自己和朋友组合去玩。有时间错误的地方还请联系我更正!

注意事项:3.27中午下午晚上/3.28全天跟团哦!!!迟到发200红包呢!!!

8.1-A航班号

此航班时间比较充裕

  • 3.26 下午/晚上

    • 亚龙湾1号小镇奥特莱
    • 第一市场 吃好吃的
    • 免税店
  • 3.27 上午

    • 亚龙湾热带天堂森林公园
    • 免税店
  • 3.27 下午/晚上

    • 团建和一起吃晚饭
    • 亚龙湾1号小镇奥特莱
  • 3.28 上午

    • 蜈支洲岛相关景点
  • 3.28 下午/晚上

    • 15.00集合离岛 18.00乘车去亚龙湾吃集体晚饭
  • 3.29 上午

    • 亚龙湾热带天堂森林公园
    • 免税店
    • 三亚湾相关景点:椰梦长廊/鹿回头公园等
  • 3.29 下午/晚上

    • 三亚湾相关景点:椰梦长廊/沙滩看日落/鹿回头公园/第一市场
  • 3.30 上午

    • 亚龙湾热带天堂森林公园
    • 亚龙湾1号小镇奥特莱
    • 购买纪念品之类的
  • 3.30 下午

    • 亚龙湾1号小镇奥特莱
    • 等待班车回家

8.2-B航班号

  • 3.27 上午

    • 亚龙湾热带天堂森林公园
    • 免税店
  • 3.27 下午/晚上

    • 团建和一起吃晚饭
    • 亚龙湾1号小镇奥特莱
  • 3.28 上午

    • 蜈支洲岛相关景点
  • 3.28 下午/晚上

    • 15.00集合离岛 18.00乘车去亚龙湾吃集体晚饭
  • 3.29 上午

    • 亚龙湾热带天堂森林公园
    • 免税店
    • 三亚湾相关景点:椰梦长廊/鹿回头公园等
  • 3.29 下午/晚上

    • 三亚湾相关景点:椰梦长廊/沙滩看日落/鹿回头公园/第一市场
  • 3.30 上午

    • 亚龙湾1号小镇奥特莱
    • 亚龙湾热带天堂森林公园
  • 3.30 下午

    • 等班车回家

8.3-C航班号

  • 3.26 晚上

    • 亚龙湾1号小镇奥特莱
    • 第一市场 吃好吃的
    • 免税店
  • 3.27 上午

    • 亚龙湾热带天堂森林公园
    • 免税店
  • 3.27 下午/晚上

    • 团建和一起吃晚饭
  • 3.28 上午

    • 蜈支洲岛相关景点
  • 3.28 下午/晚上

    • 15.00集合离岛 18.00乘车去亚龙湾吃集体晚饭
  • 3.29 上午

    • 亚龙湾热带天堂森林公园
    • 免税店
    • 三亚湾相关景点:椰梦长廊/鹿回头公园等
  • 3.29 下午/晚上

    • 三亚湾相关景点:椰梦长廊/沙滩看日落/鹿回头公园/第一市场
  • 3.30 05:20

    • 等班车回家

8.4-D航班号

  • 3.27 上午

    • 亚龙湾热带天堂森林公园
    • 免税店
  • 3.27 下午/晚上

    • 团建和一起吃晚饭
    • 亚龙湾1号小镇奥特莱
  • 3.28 上午

    • 蜈支洲岛相关景点
  • 3.28 下午/晚上

    • 15.00集合离岛 18.00乘车去亚龙湾吃集体晚饭
  • 3.29 上午

    • 亚龙湾热带天堂森林公园
    • 免税店
    • 三亚湾相关景点:椰梦长廊/鹿回头公园等
  • 3.29 下午/晚上

    • 三亚湾相关景点:椰梦长廊/沙滩看日落/鹿回头公园/第一市场
  • 3.30 上午

    • 亚龙湾1号小镇奥特莱
    • 等班车回家

示例

我是C航班

根据上面的选项,我选择
3.26晚去亚龙湾1号小镇奥特莱吃晚饭,3.27上午去亚龙湾热带天堂森林公园,中午回来团建,吃完晚饭可以去亚龙湾1号小镇奥特莱逛逛,因为晚饭就在那里。3.28,跟团蜈支洲岛相关景点。3.29上午免税店,下午去三亚湾那边溜达一圈,傍晚看完日出去第一市场。差不多就结束了。其中有些可以因为时间太紧去掉。

注意事项:3.27中午下午晚上/3.28全天跟团哦!!!迟到发200红包呢!!!

后记

感谢黄TT大佬/陈Y大佬/大前端朋友们提供的资讯和建议~

有时间错误的地方还请联系我更正!

【2020年终总结】如果这一年你很健康,那便是最好的一年!

作者 vadxq
2020年12月31日 21:21

这一年里 无论你赚了多少钱,经历了什么事,受过多少波折……
但请记得:如果这一年你很健康!那便是最好的一年!

前言

2020年,一个短暂而又漫长的一年,一个艰难而又有所成就的一年,一个令人难忘的一年,伤心与感动常在。年初席卷全国的疫情,充满阴霾的开头。庆幸中华民族努力抗疫,充满希望。2020年,疫情还未结束,这场疫情也将载入史册。人类必然战胜这场看不见硝烟的战争。

学习与开源

2020年了,一年多社会的磨练,从稚嫩与冲劲,变得认清现实与洒脱,始沉淀自己。上半年是一个沉淀基础的半年,通过接触国外高校学习的内容与练习,锻炼诸多的基础,结合MDN文档库与实践,半年后的成果个人觉得还是很丰硕!疫情反而让自己飘飘然的心更快沉静下来。下半年,对工程化与原理有更多的认知,2020年,更多的是在沉淀,不仅仅是纵向的沉淀,也有横向的沉淀。以前更多是个人的方向,而今对于团队,对于大型项目的全流程全环节有了更多自己的认知。这一年,对于新技术的关注度没有大学时候那么强烈,许是因为工作忙,许是和以前关注一下就没有以后的态度不一样了,而今会先琢磨一下实用性。比如WebAssembly,目前自己的技术流和瓶颈还很难需要处理高计算的事。今年遗憾之处是对于开源的贡献几乎可以忽略。曾想过很多,都半路夭折了。希望接下来能重新优化,精简一下,大道至简。今年一共贡献了920+个commit,第一年实现github每天都有提交记录!全绿色状态!希望未来继续保持!

github记录

工作与社交

2020年的工作历程也是一个很艰难而又很幸运的历程。无论去何处,都遇到了不错的团队。目前经历社会毒打也才一年半,锤炼技术,和身边的人一起进步,许是个很不错的选择。循循渐进,慢慢跳动。原公司的前端团队是很不错,团队氛围很好,都是很不错的人,基建也不错。基建的建设对于团队的效率提升带来的收益远比开发基建的人力消耗高。不管去哪,基建是一个很重要的东西。6月离职后,便边休养边去学校帮忙做了几个项目,顺带赚点伙食费。10月开始投递简历,基本都是投的不加班的公司。不加班是因为自己想多做些开源的事情,多学习提升一下自己,上一年的业务代码已经写的挺累了,想做一些不一样的事儿。来现在公司是个缘分,当时面试官,现在的leader给人的感觉和氛围很好,技术如何都无关系,技术靠自己,工作的环境更最重要的。现在呆了两个月了,也确实是还行。自己也有学习的时间,工作也很自由,把自己的事做好,对得起工作就行。来这里接触面还是很多的,JS,Flutter等都接了些小需求,熟悉了代码和流程。体验很好,重点是不加班!

个人生活

2020年确实比较的难以忘怀却又难以启齿,受到2019年的打击而郁郁到年后慢慢的走出来,而今已经觉得没什么好说的。这年家人和睦,事业波折但总体上还是上升,身体健健康康的,一切都很好。认识了很多新朋友,也走散了很多旧友,熙熙攘攘,江湖路远,能走到最后的又有几人?2月因为疫情在家远程办公,A了快一年的剑三又回去玩了。不想面对旧回忆,玩了个新服新号,遇见了个很好的师父,某红烧肉。三月底方面对过去,许是真的放下了。后来啊,感谢婉言和月总他们的陪伴,带我刷家具玩家园,也非常感谢江上酒馆的小伙伴们,遇见你们真的很好!很庆幸当年选择了这个帮会,也很幸运结识了你们,可惜我后来咸鱼了,现在又不玩了,号都没满级呢。在最后一个赛季前,做出了田螺大橙武,当年不小心承诺过的话,都成了现实。至此,剑三的所有玩法,几乎都体验过了,仿佛就像江湖行走,最后到了该田园生活的时光一样。后来A了很久,11月底才回去玩了。认识了黑鬼系列亲友,也认识了小秃珏和他的亲友们,曾师祖他们考完研也回来了。谢谢你们在剑三江湖的陪伴~

和小秃珏的合影

过年同为老乡的可芹也在赣州过年,本来是打算赣州聚,可惜疫情,同一个市快递都到达不了对方那儿。离职和散伙进度小伙伴们的离别。而今也半年未见,甚是想念!暑假回校园回温了校园生活,也见到了十七子亦清明他们那伙人哈哈哈。也见到了李聪文浩他们未毕业的家园学习学妹们。一切都是那么好!8月,和谢某人日久生情,九月九日本以为能长长久久,而现实却总是令人遗憾,梦一个月的时间便被现实击碎,也许本身便是路人,只是这一段时间因为引入发生偏移,导致短暂的交集吧。所有的生死不离最终都江湖不见了。十月与基佬浪浪于上海再次相见了,整整一年未见!在上海和很多朋友都相见了!有朋友在的城市,待着总是不那么难受。12月冬至,家园传统冬至活动于北京相聚,见到了许多家园小伙伴!待会要去和基佬浪浪他们一起吃跨年饭!2020年,过去的都过去了,未来总不会太差。

冬至合影

未来展望

希望未来自己的能力有更大的提升,希望未来能做一些有意思的开源项目!

希望未来江湖路远,能有人同去同归!

愿岁月无波澜,愿余生无遗憾

by vadxq

2020.12.31 上海

【前端重温系列】this的不可描述与变化原理(献给2020-1024的礼物)

作者 vadxq
2020年10月24日 00:14

时间已经是2020年了,马上也就2021年了,现如今的发展,ES6的实现几乎现代化浏览器都实现了。红宝书也出第四版了,删除了过旧的知识,引入了ES6,涵盖至ECMAScript 2019新标准。新的基础工具书的推出,自己也该好好重温一遍基础,重新梳理自己脑海的知识点,过时的删除,腾出空间给新知识。当然,内容主要还是从核心知识开始,扩展性涵盖其他知识点。

今天是本次前端重温系列的第二篇,有关于this的指向原则和call/apply/bind等原理。欢迎各位看官收看。

js的内存管理和堆栈

谈到this,就不得不说一说javascript的内存管理机制。

有许多语言会暴露内存管理的api给开发者,也有的语言会自己默默的独自完成内存的管理操作。

内存生命周期

不管什么程序语言,内存生命周期基本是一致的:

  • 分配你所需要的内存
    • 值的初始化:js在定义变量时就完成了内存分配
    • 通过函数调用分配内存
  • 使用分配到的内存(读、写)
  • 不需要时将其释放\归还

栈内存与堆内存

js有两大数据类型:基本类型和引用类型

基本类型往往在栈内存里存储,而引用类型往往在堆内存里存储。

堆和栈分别是不同的数据结构。栈是线性表的一种,而堆则是树形结构。

垃圾回收

垃圾回收算法主要依赖于引用的概念。在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如,一个Javascript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)。“对象”的概念不仅特指 JavaScript 对象,还包括函数作用域(或者全局词法作用域)。

  • 引用计数垃圾收集

    • 这是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。
  • 标记-清除算法

    • 这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象。从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。

内存泄漏

内存泄漏的概念

该释放的变量(内存垃圾)没有被释放,仍然霸占着原有的内存不松手,导致内存占用不断攀高,带来性能恶化、系统崩溃等一系列问题,这种现象就叫内存泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
var unused = function () {
if (originalThing) // 'originalThing'的引用
console.log("嘿嘿嘿");
};
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log("哈哈哈");
}
};
};
setInterval(replaceThing, 1000);

这段代码有什么问题吗?

在 V8 中,一旦不同的作用域位于同一个父级作用域下,那么它们会共享这个父级作用域。unused 是一个不会被使用的闭包,但和它共享同一个父级作用域的 someMethod,则是一个 “可抵达”(也就意味着可以被使用)的闭包。unused 引用了 originalThing,这导致和它共享作用域的 someMethod 也间接地引用了 originalThing。结果就是 someMethod “被迫” 产生了对 originalThing 的持续引用,originalThing 虽然没有任何意义和作用,却永远不会被回收。不仅如此,originalThing 每次 setInterval 都会改变一次指向(指向最近一次的 theThing 赋值结果),这导致无法被回收的无用 originalThing 越堆积越多,最终导致严重的内存泄漏。

可能导致内存泄漏的写法

  • 无意义的全局变量
1
2
3
function a() {
b = 0
}
  • 未清除的setInterval和链式调用的setTimeout
1
2
setInterval(function() {
}, 1000);
1
2
3
setTimeout(function() {
setTimeout(arguments.callee, 1000);
}, 1000);
  • 清除不当的变量
1
2
3
4
5
6
7
8
9
10
11
var myDiv = document.getElementById('myDiv')

function handleMyDiv() {
// 一些与myDiv相关的逻辑
}

// 使用myDiv
handleMyDiv()

// 尝试删除,但是由于前面函数引用了,在内存上的表现还是可访问的地址,也就是没删除掉。
document.body.removeChild(document.getElementById('myDiv'));

this的指向原则

this指向:指向执行时所在的上下文,即被调用函数所在的对象

  • this的指向由函数执行时确定,而不是定义时决定的。这点和闭包恰恰相反。当调用方法没有明确对象时,则是指向window

  • 如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

1
2
3
4
5
6
7
8
9
10
var o = {
a:10,
b:{
a:12,
fn: function(){
console.log(this.a); // 12
}
}
}
o.b.fn();
  • this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的

  • 如果 new 关键词出现在被调用函数的前面,那么JavaScript引擎会创建一个新的对象,被调用函数中的this指向的就是这个新创建的函数。

  • 如果通过apply、call或者bind的方式触发函数,那么函数中的this指向传入函数的第一个参数

  • 如果一个函数是某个对象的方法,并且对象使用句点符号触发函数,那么this指向的就是该函数作为那个对象的属性的对象,也就是,this指向句点左边的对象。

this特殊情形

  • this必然指向window的情况

    • 立即执行函数(IIFE)
    • setTimeout 中传入的函数
    • setInterval 中传入的函数
  • 严格模式的情形

    • 严格模式下,this 将保持它被指定的那个对象的值,所以,如果没有指定对象,this 就是 undefined
  • 箭头函数

    • 箭头函数中的 this,和你如何调用它无关,由你书写它的位置决定
  • 如果返回值是一个Object,那么this指向的就是那个返回的对象,否则指向函数的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// null
function fn()
{
this.user = 'vadxq';
return null;
}
var a = new fn;
console.log(a.user); // vadxq

// return fn
function fn()
{
this.user = 'vadxq';
return function() {};
}
var a = new fn;
console.log(a.user); // undefined


// return Object
function fn()
{
this.user = 'vadxq';
return {};
}
var a = new fn;
console.log(a.user); // undefined

// return other
function fn()
{
this.user = 'vadxq';
return undefined;
}
var a = new fn;
console.log(a.user); // vadxq

改变this

改变this的方法途径

  • 书写定义时改变,比如箭头函数
  • 调用时改变,显式地调用一些方法,比如call/apply/bind

箭头函数

箭头函数是在定义的时候就决定了指向

1
2
3
4
5
6
7
8
9
10
11
12
var a = 1

var obj = {
a: 2,
// 声明位置
showA: () => {
console.log(this.a)
}
}

// 调用位置
obj.showA() // 1

构造函数:构造函数里面的 this 会绑定到我们 new 出来的这个对象上

显式调用

call/apply/bind的特点

  • call
    • 改变后直接调用
    • fn.call(ctx, arg1, arg2)
  • apply
    • 改变后直接调用
    • fn.apply(ctx, [arg1, arg2])
  • bind
    • 改变后不进行调用操作
    • fn.bind(ctx, arg1, arg2)

实现call/apply/bind方法

可以看此文章,写的很详细:

手写call、apply、bind实现及详解

后记

这一篇文章由于内容涵盖了的知识比较的偏底层和js语法的特性,在准备花费的时间较长,由于这个阶段自己正好在寻找工作,断断续续的在填坑,最后的内容是在去入职的火车上完成的哈哈哈。算是比较有意义的一个纪念!特写了个后记记录一下。

同时今天又是1024!我们的狂欢🎉!献给2020-1024的礼物!

【前端重温系列】闭包及其涉及知识点的理解

作者 vadxq
2020年10月6日 19:14

时间已经是2020年了,马上也就2021年了,现如今的发展,ES6的实现几乎现代化浏览器都实现了。红宝书也出第四版了,删除了过旧的知识,引入了ES6,涵盖至ECMAScript 2019新标准。新的基础工具书的推出,自己也该好好重温一遍基础,重新梳理自己脑海的知识点,过时的删除,腾出空间给新知识。当然,内容主要还是从核心知识开始,扩展性涵盖其他知识点。

今天开始从闭包及其涉及知识点开始说起。

闭包的定义:闭包是什么

不同资料对闭包的解释

MDN

函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在 JavaScript 中,每当函数被创建,就会在函数生成时生成闭包。

阮一峰

其理解:闭包就是能够读取其他函数内部变量的函数。

由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成”定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

维基百科

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是在支持头等函数的编程语言中实现词法绑定的一种技术。闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)。环境里是若干对符号和值的对应关系,它既要包括约束变量(该函数内部绑定的符号),也要包括自由变量(在函数外部定义但在函数内被引用),有些函数也可能没有自由变量。闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。捕捉时对于值的处理可以是值拷贝,也可以是名称引用,这通常由语言设计者决定,也可能由用户自行指定(如C++)。

在支持头等函数的语言中,如果函数f内定义了函数g,那么如果g存在自由变量,且这些自由变量没有在编译过程中被优化掉,那么将产生闭包。

闭包和匿名函数经常被用作同义词。但严格来说,匿名函数就是字面意义上没有被赋予名称的函数,而闭包则实际上是一个函数的实例,也就是说它是存在于内存里的某个结构体。如果从实现上来看的话,匿名函数如果没有捕捉自由变量,那么它其实可以被实现为一个函数指针,或者直接内联到调用点,如果它捕捉了自由变量那么它将是一个闭包;而闭包则意味着同时包括函数指针和环境两个关键元素。在编译优化当中,没有捕捉自由变量的闭包可以被优化成普通函数,这样就无需分配闭包结构体,这种编译技巧被称为函数跃升。

自我理解总结

包含了既不是函数参数、也不是函数的局部变量,而是一个不属于当前作用域的变量,相对于当前作用域来说,是一个自由变量的函数,就叫闭包。

闭包包含了自由变量和函数环境

为什么需要闭包,闭包优缺点

闭包作用

  • 可以引用外部函数的变量或者参数
  • 使该变量或者参数常驻内存,避免被垃圾回收机制所回收

在 js 中变量的作用域属于函数作用域, 在函数执行完后,作用域就会被清理,内存也会随之被回收,但是由于闭包可访问上级作用域,即使上级函数执行完, 作用域也不会随之销毁

总结:某个函数在定义时的词法作用域之外的地方被调用,闭包可以使该函数访问定义时的词法作用域

注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

闭包涉及知识:作用域及作用域链

作用域分为词法作用域和动态作用域,Javascript的作用域遵循的就是词法作用域模型

关于词法作用域和动态作用域区别

词法作用域

  • 也称为静态作用域。这是最普遍的一种作用域模型
  • 在代码书写的时候完成划分,作用域链沿着它定义的位置往外延伸

动态作用域

  • 相对“冷门”,但确实有一些语言采纳的是动态作用域,如:Bash 脚本、Perl 等
  • 在代码运行时完成划分,作用域链沿着它的调用栈往外延伸

词法作用域及其作用域链

词法(lexical)一词指的是,词法作用域根据源代码中声明变量的位置来确定该变量在何处可用。嵌套函数可访问声明于它们外部作用域的变量。

任何变量(不管包含的是原始值还是引用值)都存在于某个执行上下文中(也称为作用域)。这个上下文(作用域)决定了变量的生命周期,以及它们可以访问代码的哪些部分。执行上下文可以总结如下。

  • 执行上下文(作用域)分全局上下文(全局作用域)、函数上下文(局部作用域)和块级上下文(块级作用域)。

  • 代码执行流每进入一个新上下文,都会创建一个作用域链,用于搜索变量和函数。

  • 函数或块的局部上下文不仅可以访问自己作用域内的变量,而且也可以访问任何包含上下文乃至全局上下文中的变量。

  • 全局上下文只能访问全局上下文中的变量和函数,不能直接访问局部上下文中的任何数据。

  • 变量的执行上下文用于确定什么时候释放内存。

  • 整个代码结构中只有函数可以限定作用域(待考证)

  • 作用域规则优先使用变量提升规则分析

  • 如果当前作用规则中有名字了, 就不考虑外面的名字

闭包涉及知识点:js内存管理

JavaScript是使用垃圾回收的编程语言,开发者不需要操心内存分配和回收。JavaScript的垃圾回收程序可以总结如下。

  • 离开作用域的值会被自动标记为可回收,然后在垃圾回收期间被删除。
  • 主流的垃圾回收算法是标记清理,即先给当前不使用的值加上标记,再回来回收它们的内存。
  • 引用计数是另一种垃圾回收策略,需要记录值被引用了多少次。JavaScript引擎不再使用这种算法,但某些旧版本的IE仍然会受这种算法的影响,原因是JavaScript会访问非原生JavaScript对象(如DOM元素)。
  • 引用计数在代码中存在循环引用时会出现问题。
  • 解除变量的引用不仅可以消除循环引用,而且对垃圾回收也有帮助。为促进内存回收,全局对象、全局对象的属性和循环引用都应该在不需要时解除引用。

闭包的实现

若函数作为参数被传递

1
2
3
4
5
6
7
8
9
10
11
// 函数作为参数被传递
function print(fn) {
const a = 200
fn()
}

const a = 100
function fn() {
console.log(a)
}
print(fn) // 100

函数作为返回值被返回

1
2
3
4
5
6
7
8
9
10
11
// 函数作为返回值
function create() {
const a = 100
return function () {
console.log(a)
}
}

const fn = create()
const a = 200
fn() // 100

闭包的应用

实现数据(变量和方法)私有化

函数柯里化(函数式编程

闭包相关例题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
for (var i = 1; i < 5; i++) {
setTimeout(function () {
console.log(i)
}, 1000 * i)
}
console.log(i)

// 5 5 5 5 5 5
// 创建的5个setTimeout闭包共享一个词法作用域,优先打印外层5
// 闭包只能取得包含函数中任何变量赋值最后一个值 // 5


for (var i = 0; i < 5; i++) {
((j) => {
setTimeout(() => {
console.log(j)
}, 1000 * j)
})(i)
}
// 0 1 2 3 4
// 5个setTimeout闭包有自己独立的词法环境
// 闭包读取到不同的i值

var output = function (i) {
setTimeout(function() {
console.log(i);
}, 1000);
};

for (var i = 0; i < 5; i++) {
output(i);
}

// 0 1 2 3 4
// 这里的 i 被赋值给了 output 作用域内的变量 i

// 变种
function test (){
var num = []
var i

for (i = 0; i < 10; i++) {
num[i] = function () {
console.log(i)
}
}

return num[9]
}

test()()
// 10

var test = (function() {
var num = 0
return () => {
return num++
}
}())

for (var i = 0; i < 10; i++) {
test()
}

console.log(test())
// 10


var a = 1;
function test(){
a = 2;
return function(){
console.log(a);
}
var a = 3;
}
test()();
// 2
// 变量提升,test a提升了。后来又赋值2
// 我们作用域的划分,是在书写的过程中,根据你把它写在哪个位置来决定的。像这样划分出来的作用域,遵循的就是词法作用域模型。这里我们匿名函数被定义的时候 a = 3 的赋值动作还没有发生(只有声明会被提前!),因此它拿到的 a 就是 2!


function foo(a,b){
console.log(b);
return {
foo:function(c){
return foo(c,a);
}
}
}

var func1=foo(0);
func1.foo(1);
func1.foo(2);
func1.foo(3);
var func2=foo(0).foo(1).foo(2).foo(3);
var func3=foo(0).foo(1);
func3.foo(2);
func3.foo(3);
// undefined
// 0
// 0
// 0
// undefined
// 0
// 1
// 2
// undefined
// 0
// 1
// 1
// {foo: ƒ}

【教程】Macbook使用初始化指南

作者 vadxq
2020年2月20日 09:14

去年双十一拿着自己的工资买了台Macbook Pro,由于携带性选择了13寸,反正写代码办公等日常够用了哈哈哈,并不需要15寸的高级显卡等性能。

下面就开始我自己的MBP的环境配置和常用软件清单

brew

1
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装git

1
brew install git

替换brew.git

1
2
cd "$(brew --repo)"
git remote set-url origin https://mirrors.ustc.edu.cn/brew.git

替换homebrew-core.git

1
2
cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"
git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git

替换Homebrew Bottles源: 参考:替换Homebrew Bottles源

在中科大源失效或宕机时可以: 1. 使用清华源设置参考。 2. 切换回官方源

重置brew.git
1
2
cd "$(brew --repo)"
git remote set-url origin https://github.com/Homebrew/brew.git
重置homebrew-core.git
1
2
cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"
git remote set-url origin https://github.com/Homebrew/homebrew-core.git

zsh

1
brew install zsh

ohmyzsh

1
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
1
2
3
4
5
6
7
8
9
10
11
12
13
# 自动补全
git clone https://github.com/zsh-users/zsh-completions ~/.oh-my-zsh/custom/plugins/zsh-completions

git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ~/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting

echo "source ${(q-)PWD}/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" >> ${ZDOTDIR:-$HOME}/.zshrc

plugins=(git nvm node npm extract zsh-completions zsh-syntax-highlighting)

source /Users/vadxq/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh

export CHROME_EXECUTABLE=/Applications/Microsoft\ Edge.app/Contents/MacOS/Microsoft\ Edge

nvm/node/npm/pnpm

1
2
3
4
5
6
7
8
9
10
brew install nvm
nvm install node

npm i -g nrm

corepack enable

corepack prepare pnpm@6.22.2 --activate

pnpm add -g pnpm

mongoldb

1
2
3
4
# https://github.com/mongodb/homebrew-brew
brew tap mongodb/brew
brew install mongodb-community
brew services start mongodb-community

mysql

1
brew install mysql

配置启动

1
2
brew tap home-brew/services
brew services start mysql

修改密码

1
mystery sqladmin -u root password

密码,没密码,直接回车

1
ALTER user 'root'@'localhost' IDENTIFIED BY “123456”

Office

Office2019(不推荐了)

October 15, 2019: Version 16.30 (19101301). For more information, see update history for Office for Mac.
Office 2019 for Mac download link:

1
2
3
4
5
6
7
8
9
https://go.microsoft.com/fwlink/?linkid=525133 # Office
https://go.microsoft.com/fwlink/?linkid=525134 # Word
https://go.microsoft.com/fwlink/?linkid=525135 # Excel
https://go.microsoft.com/fwlink/?linkid=525136 # PowerPoint
https://go.microsoft.com/fwlink/?linkid=525137 # Outlook
https://officecdn.microsoft.com/pr/C1297A47-86C4-4C1F-97FA-950631F94777/MacAutoupdate/Microsoft_Word_16.39.20071300_Updater.pkg
https://officecdn.microsoft.com/pr/C1297A47-86C4-4C1F-97FA-950631F94777/MacAutoupdate/Microsoft_Excel_16.39.20071300_Updater.pkg
https://officecdn.microsoft.com/pr/C1297A47-86C4-4C1F-97FA-950631F94777/MacAutoupdate/Microsoft_PowerPoint_16.39.20071300_Updater.pkg
https://officecdn.microsoft.com/pr/C1297A47-86C4-4C1F-97FA-950631F94777/MacAutoupdate/Microsoft_Outlook_16.39.20071300_Updater.pkg

Office 2021(推荐)

https://macwk.com/soft/office

Docker

1
2
3
4
5
6
brew install --cask docker

brew install docker
brew install docker-compose

brew install docker-machine

其他日常工具

  • ClashX
  • ps/pr/激活工具
  • weiyun
  • maczip
  • paste:剪贴历史
  • clipy –paste免费替代
  • lulu
  • lemon
  • navicat
  • teamviewer
  • kodo
  • transmit
  • charles
  • licecap
  • maczip
  • popcli:软件扩展copy and paste

【Linux】阿里云服务器初始化操作

作者 vadxq
2019年12月20日 09:14

终究还是购买了一台2c8g5m的阿里云服务器,三年近1399元,真的太香了!

下面是从老的腾讯云学生机器挪移过来顺带升级以下软件操作等命令。想想我的那台腾讯云机器还是2016年买的了,没记错的话。还是五月份哈哈。

选择系统ubuntu

开启ssh登陆

1
2
3
sudo vim /etc/ssh/sshd_config
Port 22
sudo service sshd restart

关于port,我一般会选择换一个,安全点哈哈哈。

切换阿里源

1
vim /etc/apt/sources.list
1
2
3
4
5
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse

更新软件神马的快一点

更新系统

1
2
sudo apt-get update
sudo apt-get upgrade

新建用户

1
2
3
4
5
sudo useradd -r -m vadxq
sudo passwd vadxq
sudo vim /etc/sudoers
root ALL=(ALL:ALL) ALL
vadxq ALL=(ALL:ALL) ALL

防火墙ufw

1
2
3
4
5
6
7
8
9
sudo ufw enable
sudo ufw allow 80/tcp

# ssh
sudo ufw allow 22
#mongodb
sudo ufw allow 27017
#mysql
sudo ufw allow 3306

按照zsh

1
2
3
sudo apt-get install zsh
sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
git clone https://github.com/zsh-users/zsh-completions ~/.oh-my-zsh/custom/plugins/zsh-completions

历史语法

1
2
git clone git://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions
source $ZSH_CUSTOM/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh

法高亮插件

1
2
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git $ZSH_CUSTOM/plugins/zsh-syntax-highlighting
source $ZSH_CUSTOM/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh

nvm

1
2
3
4
5
6
7
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
nvm install 13.10.1
npm i -g pm2
npm i nrm -g

docker

1
2
3
4
5
6
7
curl -k -sSl https://get.docker.com | sudo sh
# docker image prune --force --all或者docker image prune -f -a` : 删除所有不使用的镜像
# docker container prune -f: 删除所有停止的容器

# docker-compose
sudo curl -L https://github.com/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

配置ssh

1
ssh-keygen -t rsa -C "dongxianlin@vadxq.com"

nginx

1
2
3
4
5
6
7
8
https://nginx.org/en/linux_packages.html#Ubuntu
sudo apt install curl gnupg2 ca-certificates lsb-release
echo "deb http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
sudo apt-key fingerprint ABF5BD827BD9BF62
sudo apt update
sudo apt install nginx

nginx编译

安装依赖环境
1
2
sudo apt install  perl make build-essential procps libreadline-dev libncurses5-dev libpcre3-dev libssl-dev zlib1g-dev
2>&1 nginx -V | tr ' ' '\n'|grep module
查看完整的编译参数:nginx -V
1
2
./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.18.0/debian/debuild-base/nginx-1.18.0=. -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie' --add-module=../ngx-fancyindex
--add-module=../lua-nginx-module
1
2
3
git clone https://github.com/aperezdc/ngx-fancyindex.git
wget http://nginx.org/download/nginx-1.18.0.tar.gz
tar -xvf nginx-1.18.0.tar.gz
1
2
3
4
5
cd nginx-1.18.0
./configure 参数
make
cp obj/nginx /usr/sbin
sudo service nginx start

配置泛域名(可以见我另一个文章)

1
2
3
4
5
6
7
8
9
10
export Ali_Key=xxx

export Ali_Secret=xxx

acme.sh --issue --dns dns_ali -d vadxq.com -d *.vadxq.com

sudo ~/.acme.sh/acme.sh --installcert -d vadxq.com --force \
--key-file /etc/nginx/ssl/vadxq.com/vadxq.key \
--fullchain-file /etc/nginx/ssl/vadxq.com/fullchain.cer \
--reloadcmd "service nginx force-reload”

mysql

1
2
3
4
5
6
7
8
9
10
11
12
wget –c https://dev.mysql.com/get/mysql-apt-config_0.8.15-1_all.deb

sudo dpkg –i mysql-apt-config_0.8.15-1_all.deb
sudo apt-get update
sudo apt-get install mysql-server
sudo cat /etc/myspl/debian.cnf

grant all privileges on *.* to root@'localhost' identified by "123456" with grant option;
grant all privileges on *.* to vadxq@‘%' identified by “密码" with grant option;
flush privileges;
修改端口
https://www.cnblogs.com/richardzhu/p/3318595.html

【泛域名】使用Let's Encrypt免费签发个人或企业网站泛域名证书

作者 vadxq
2019年5月20日 09:14

Let’s Encrypt 是一个免费,自动化和开放的证书颁发机构。官方已经支持免费签发通配符域名证书,即泛域名证书,俗称野卡。签发步骤简单快捷,几行命令即可实现免费自动签发。

前提,先启动nginx

安装acme.sh

1
curl https://get.acme.sh | sh

可选择在.bashrc或者是如果zsh使用者可以使用.zshrc添加对用映射:alias acme.sh=~/.acme.sh/acme.sh

获取DNS api

acme.sh支持cloudflare,dnspod,aliyun,GoDaddy,CloudXNS等大约80余种DNS解析商。详情可看dnsapi。这里一腾讯云为介绍,阿里云也一样。首先去腾讯云dnspod官网获取dns的DP_Id和DP_Key,腾讯云因为是使用的dnspod,所以需要去这个的官网申请。请注意,key只能查看一次,需记住!不过可以再次申请,忘记了也没多大问题。获取到了这两者后,回到服务器进行如下操作,添加至环境变量中:

1
2
export DP_Id="89XXXX"
export DP_Key="71943XXXXXXXXXXXXXXXXXXXXXXXX"

DNS api主要四用于后期修改dns解析验证此域名和服务器都是你自己的,相当于一个验证授权信息的玩意儿。

签发证书

接下来,当将dns api添加到环境变量中后,便要开始签发证书了,这一步也很简单的,别慌。
此处依旧使用腾讯云作示例:
acme.sh –issue –dns dns_dp -d vadxq.com -d .vadxq.com
其中,–dns表示通过dns方式签发,其实还可以http验证,不过这个不利于后期的自动更新,由于Let’s Encrypt签发的证书只有三个月有效期,所以此方法此处略过。
-d后面跟着域名,dns_dp是指dnspod,不同的域名解析商,此段不一样,具体看上一步的dnsapi。泛域名最好使用vadxq.com和
.vadxq.com两者结合,当然也可以只*.vadxq.com。目前两者都玩过,还没细究有何区别。
在这一步,acme.sh会自动在dns添加dns记录验证域名所有权。

copy/安装证书

证书生成后, 需要把证书 copy 到真正需要用它的地方。注意, 默认生成的证书都放在安装目录下: ~/.acme.sh/, 请不要直接使用此目录下的文件。
正确的使用方法是使用 –installcert 命令,并指定目标位置, 然后证书文件会被copy到相应的位置,。
你可以在nginx目录建一个专门存放证书的目录:

1
2
3
4
sudo ~/.acme.sh/acme.sh --installcert -d vadxq.com \
--key-file /etc/nginx/ssl/vadxq.com/vadxq.key \
--fullchain-file /etc/nginx/ssl/vadxq.com/fullchain.cer \
--reloadcmd "service nginx force-reload"

–reloadcmd:配置nginx重启命令。在这一步,acme.sh会记住操作命令,在证书快过期的时候,自动续期。需要将nginx指向复制后的目录。

至此,你的免费自动签发泛域名证书的步骤就完成了,打开你的页面查看是不是生效啦。

nginx配置文件参考

示例ssl配置文件vadxq.com.ssl.conf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ssl on;
server_tokens off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 60m;
ssl_session_tickets on;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;
ssl_prefer_server_ciphers on;
# 证书路径 绝对地址
ssl_certificate /etc/nginx/ssl/vadxq.com/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/vadxq.com/vadxq.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA3 84:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
#add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload";
#add_header X-Frame-Options deny;
add_header X-Content-Type-Options nosniff;
add_header x-xss-protection "1; mode=block";
#add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: https:; connect-src 'self' https:; img-src 'self' data: https: blob:; style-src 'unsafe-inline' https:; font-src https:";

在需要引入SSL证书的nginx的server引入此文件即可

示例:blog.vadxq.com.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
server {
server_name blog.vadxq.com;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
proxy_pass http://127.0.0.1:7191;
proxy_set_header Host $host;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
}
location /api {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://127.0.0.1:7190;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
}
location /sitemap.xml {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://127.0.0.1:7190;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
}
listen 443 ssl;
include /etc/nginx/ssl/vadxq.com.ssl.conf;
}
server {
listen 80;
server_name blog.vadxq.com;
return 301 https://$host$request_uri;
#return 404;
}

如上配置中的include,引入绝对地址下的ssl配置文件即可。

好啦,教程到此结束,谢谢大家捧场,有什么不对的或者有其他疑问,可以github上blog项目里提issue(https://github.com/vadxq/koa-nuxt-blog/issues)或者是邮件我哟~

【Flutter】flutter初学习笔记

作者 vadxq
2019年2月8日 09:14

Flutter 是Google推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。 开发者可以通过Dart语言开发App,一套代码同时运行在iOS 和Android平台。 Flutter提供了丰富的组件、接口,开发者可以很快地为Flutter添加native扩展。

以下是个人学习笔记

Text Widget

TextAlign属性:left(左对齐)、center(居中对齐)、right(右对齐)

maxLines属性:设置最多显示的行数

overflow属性:clip:直剩下的文字不显示;ellipsis:显示省略号;

fade:溢出的部分会进行一个上线的渐变消失的效果

style属性:见https://docs.flutter.io/flutter/painting/TextStyle-class.html

Container(容器控件)

Alignment属性

1
2
3
4
5
6
7
8
9
bottomCenter:下部居中对齐。
botomLeft: 下部左对齐。
bottomRight:下部右对齐。
center:纵横双向居中对齐。
centerLeft:纵向居中横向居左对齐。
centerRight:纵向居中横向居右对齐。
topLeft:顶部左侧对齐。
topCenter:顶部居中对齐。
topRight: 顶部居左对齐。

设置宽、高和颜色属性

1
2
3
width:500.0,
height:400.0,
color: Colors.lightBlue,

padding属性

margin属性

decoration属性: container 的修饰器,设置背景和边框

1
2
3
4
5
6
decoration:new BoxDecoration( // 渐变
gradient:const LinearGradient(
colors:[Colors.lightBlue,Colors.greenAccent,Colors.purple] // 颜色
),
border:Border.all(width:2.0,color:Colors.red) // 边框
),

Image图片组件

路径属性

1
2
3
4
Image.asset:加载资源图片,相对路径。
Image.network:网络资源图片。
Image.file:加载本地图片,绝对路径。
Image.memory: 加载Uint8List资源图片。

fit属性的设置:控制图片的拉伸和挤压

1
2
3
4
5
6
BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
BoxFit.contain:全图显示,显示原比例,可能会有空隙。
BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大

colorBlendMode(图片混合模式)

1
2
color:是要混合的颜色,如果你只设置color是没有意义的。
colorBlendMode:是混合模式,相当于我们如何混合。

repeat图片重复

1
2
3
ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
ImageRepeat.repeatX: 横向重复,纵向不重复。
ImageRepeat.repeatY:纵向重复,横向不重复。

ListView 列表组件

demo

1
2
3
4
5
6
7
8
body: new ListView(
children:<Widget>[
new ListTile(
leading: new Icon(Icons.access_time),
title: new Text('access_time')
)
]
),

图片列表的使用:将ListTile替换成Image Widget

横向列表的使用

ListView组件里加一个ScrollDirection属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Axis.horizontal:横向滚动或者叫水平方向滚动。
Axis.vertical:纵向滚动或者叫垂直方向滚动。
child:new ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
new Container(
width:180.0,
color: Colors.lightBlue,
),
new Container(
width:180.0,
color: Colors.amber,
),
new Container(
width:180.0,
color: Colors.deepOrange,
),
],
)

动态列表的使用

List类型的使用:

1
2
3
4
5
var myList = List(): 非固定长度的声明。
var myList = List(2): 固定长度的声明。
var myList= List<String>():固定类型的声明方式。
var myList = [1,2,3]: 对List直接赋值。
动态列表 ListView.builder()

GridView网格列表组件

padding:表示内边距,这个小伙伴们应该很熟悉。

crossAxisSpacing:网格间的空当,相当于每个网格之间的间距。

crossAxisCount:网格的列数,相当于一行放置的网格数量。

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
body:GridView.count(
padding:const EdgeInsets.all(20.0),
crossAxisSpacing: 10.0,
crossAxisCount: 3,
children: <Widget>[
const Text('I am Jspang'),
const Text('I love Web'),
const Text('jspang.com'),
const Text('我喜欢玩游戏'),
const Text('我喜欢看书'),
const Text('我喜欢吃火锅')
],
)

水平布局Row的使用

自适应:Expanded

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
body:new Row(
children: <Widget>[
Expanded(
child:new RaisedButton(
onPressed: (){
},
color:Colors.redAccent,
child:new Text('红色按钮')
)
),
Expanded(child:new RaisedButton(
onPressed: (){
},
color:Colors.orangeAccent,
child: new Text('黄色按钮'),
)
),
Expanded(child:new RaisedButton(
onPressed: (){
},
color:Colors.pinkAccent,
child:new Text('粉色按钮')
)
)
],
)

非自适应

1
2
3
4
5
6
7
8
body:new Row(
children: <Widget>[
new RaisedButton(
onPressed: (){
},
color:Colors.redAccent,
child:new Text('红色按钮')
),

垂直布局Column组件

1
2
3
4
5
6
7
body:Column(
children: <Widget>[
Text('I am JSPang'),
Text('my website is jspang.com'),
Text('I love coding')
],
)
1
2
3
4
5
6
CrossAxisAlignment.star:居左对齐。
CrossAxisAlignment.end:居右对齐。
CrossAxisAlignment.center:居中对齐。

main轴:如果你用column组件,那垂直就是主轴,如果你用Row组件,那水平就是主轴。
cross轴:cross轴我们称为幅轴,是和主轴垂直的方向。比如Row组件,那垂直就是幅轴,Column组件的幅轴就是水平方向的。
1
2
3
4
5
6
7
8
9
body:Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('I am JSPang'),
Text('my website is jspang.com'),
Text('I love coding')
],
)

水平方向相对屏幕居中

1
2
3
4
5
6
7
8
body:Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(child:Text('I am JSPang')),
Center(child:Text('my website is jspang.com')),
Center(child:Text('I love coding'))
],
)

Expanded属性的使用

1
2
3
4
5
6
7
8
body:Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(child:Text('I am JSPang')),
Expanded(child:Center(child:Text('my website is jspang.com'))),
Center(child:Text('I love coding'))
],
)

Stack层叠布局

层叠布局的 alignment 属性:alignment属性是控制层叠的位置的,建议在两个内容进行层叠时使用。它有两个值X轴距离和Y轴距离,值是从0到1的,都是从上层容器的左上角开始算起的

CircleAvatar组件的使用,常用于头像之类的

1
2
3
4
new CircleAvatar(
backgroundImage: new NetworkImage(''),
radius: 100.0,
),

Positioned组件

1
2
3
4
5
6
bottom: 距离层叠组件下边的距离
left:距离层叠组件左边的距离
top:距离层叠组件上边的距离
right:距离层叠组件右边的距离
width: 层叠定位组件的宽度
height: 层叠定位组件的高度

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var stack = new Stack(
children: <Widget>[
new CircleAvatar(
backgroundImage: new NetworkImage('http://jspang.com/static//myimg/blogtouxiang.jpg'),
radius: 100.0,
),
new Positioned(
top:10.0,
left:10.0,
child: new Text('JSPang.com'),
),
new Positioned(
bottom:10.0,
right:10.0,
child: new Text('技术胖'),
)
],
);

卡片组件布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var card = new Card(
child: Column(
children: <Widget>[
ListTile(
title:new Text('吉林省吉林市昌邑区',style: TextStyle(fontWeight: FontWeight.w500),),
subtitle: new Text('技术胖:1513938888'),
leading: new Icon(Icons.account_box,color: Colors.lightBlue,),
),
new Divider(),
ListTile(
title:new Text('北京市海淀区中国科技大学',style: TextStyle(fontWeight: FontWeight.w500),),
subtitle: new Text('胜宏宇:1513938888'),
leading: new Icon(Icons.account_box,color: Colors.lightBlue,),
),
new Dvider(),

],
),
)

route路由层级

1
2
3
4
5
6
7
RaisedButton按钮组件
child:可以放入容器,图标,文字。让你构建多彩的按钮。
onPressed:点击事件的相应,一般会调用Navigator组件。

Navigator.push 和 Navigator.pop
Navigator.push:是跳转到下一个页面,它要接受两个参数一个是上下文context,另一个是要跳转的函数。
Navigator.pop:是返回到上一个页面,使用时传递一个context(上下文)参数,使用时要注意的是,你必须是有上级页面的,也就是说上级页面使用了Navigator.push。

导航参数的传递和接收

声明数据结构类:

Dart中可以使用类来抽象一个数据,比如我们模仿一个商品信息,有商品标题和商品描述。我们定义了一个Product类,里边有两个字符型变量,title和description。

title:是商品标题。

description: 商品详情描述

代码如下:

1
2
3
4
5
class Product{
final String title; //商品标题
final String description; //商品描述
Product(this.title,this.description);
}

构建一个商品列表

1
2
3
4
5
6
7
8
9
10
11
void main(){
runApp(MaterialApp(
title:'数据传递案例',
home:ProductList(
products:List.generate(
20,
(i)=>Product('商品 $i','这是一个商品详情,编号为:$i')
),
)
));
}

导航参数的传递:使用Navigator组件,然后使用路由MaterialPageRoute传递参数

1
2
3
4
5
6
Navigator.push(
context,
MaterialPageRoute(
builder:(context)=>new ProductDetail(product:products[index])
)
);

子页面接受参数并显示,需要声明ProductDetail这个类(组件),先要作的就是接受参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import 'package:flutter/material.dart';
//传递的数据结构,也可以理解为对商品数据的抽象
class Product{
final String title; //商品标题
final String description; //商品描述
Product(this.title,this.description);
}
void main(){
runApp(MaterialApp(
title:'数据传递案例',
home:ProductList(
products:List.generate(
20,
(i)=>Product('商品 $i','这是一个商品详情,编号为:$i')
),
)
));
}
class ProductList extends StatelessWidget{
final List<Product> products;
ProductList({Key key,@required this.products}):super(key:key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title:Text('商品列表')),
body:ListView.builder(
itemCount:products.length,
itemBuilder: (context,index){
return ListTile(
title:Text(products[index].title),
onTap:(){
Navigator.push(
context,
MaterialPageRoute(
builder:(context)=>new ProductDetail(product:products[index])
)
);
}
);
},
)
);
}
}
class ProductDetail extends StatelessWidget {
final Product product;
ProductDetail({Key key ,@required this.product}):super(key:key);
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title:Text('${product.title}'),
),
body:Center(child: Text('${product.description}'),)
);
}
}

页面跳转并返回数据

异步请求和等待:async…await

1
2
3
SnackBar的使用
SnackBar是用户操作后,显示提示信息的一个控件,类似Tost,会自动隐藏。SnackBar是以Scaffold的showSnackBar方法来进行显示的。
Scaffold.of(context).showSnackBar(SnackBar(content:Text('$result')));

返回数据的方式

返回数据其实是特别容易的,只要在返回时带第二个参数就可以了。

Navigator.pop(context,’xxxx’); //xxx就是返回的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(
title:'页面跳转返回数据',
home:FirstPage()
));
}
class FirstPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(title:Text("找小姐姐要电话")),
body:Center(
child: RouteButton(),
)
);
}
}
//跳转的Button
class RouteButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed:(){
_navigateToXiaoJieJie(context);
},
child: Text('去找小姐姐'),
);
}
_navigateToXiaoJieJie(BuildContext context) async{ //async是启用异步方法
final result = await Navigator.push(//等待
context,
MaterialPageRoute(builder: (context)=> XiaoJieJie())
);
Scaffold.of(context).showSnackBar(SnackBar(content:Text('$result')));
}
}
class XiaoJieJie extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(
title:Text('我是小姐姐')
),
body:Center(
child:Column(
children: <Widget>[
RaisedButton(
child: Text('大长腿小姐姐'),
onPressed: (){
Navigator.pop(context,'大长腿:1511008888');
},
) ,
RaisedButton(
child: Text('小蛮腰小姐姐'),
onPressed: (){
Navigator.pop(context,'大长腿:1511009999');
},
) ,
],
)
) ,
);
}
}

静态资源和项目图片的处理

pubspec.yaml

如果想配置项目资源文件,就需要使用pubspec.yaml文件,需要把资源文件在这里声明。

比如在项目根目录下新建了一个images文件夹,文件夹下面放了一个图片,图片的名称叫做blogtouxiang.jpg,那我们在pubspec.yaml文件里就要写如下代码进行声明。

1
2
assets:
- images/blogtouxiang.jpg

使用项目图片资源

有了声明后,我们就可以直接在项目中引用这个文件了。这里使用最简单的代码结构,只用了一张图片。代码如下:

1
2
3
4
5
6
7
8
9
10
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Image.asset('images/blogtouxiang.jpg'),
);
}
}

Flutter客户端打包

配置

项目根目录/android/app/src/main/res/AndroidManifest.xml文件
这个文件主要用来配置APP的名称、图标和系统权限,所在的目录在:

1
2
3
项目根目录/android/app/src/main/AndroidManifest.xml
android:label="flutter_app" //配置APP的名称,支持中文
android:icon="@mipmap/ic_launcher" //APP图标的文件名称

生成 keystore

D:\Program\Android\'Android Studio'\jre\bin\keytool -genkey -v -keystore D:\key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key

有了这个key.jks文件后,可以到项目目录下的android文件夹下,创建一个名为key.properties的文件,并打开粘贴下面的代码。

1
2
3
4
storePassword=<password from previous step>    //输入上一步创建KEY时输入的 密钥库 密码
keyPassword=<password from previous step> //输入上一步创建KEY时输入的 密钥 密码
keyAlias=key
storeFile=<E:/key.jks> //key.jks的存放路径

配置key注册

key生成好后,需要在build.gradle文件中进行配置。这个过程其实很简单,就是粘贴复制一些东西,你是不需要知道这些文件的具体用处的。

第一项:

进入项目目录的/android/app/build.gradle文件,在android{这一行前面,加入如下代码:

1
2
3
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

把如下代码进行替换

1
2
3
4
5
buildTypes {
release {
signingConfig signingConfigs.debug
}
}

替换成的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}

生成apk

flutter build apk

❌
❌