1. 从“ponytail”这个热词说起:它到底是什么
第一次看到“ponytail”被当成一个技术项目名,我其实是有点懵的。马尾辫?这跟代码、插件、工具链有什么关系?后来在几个开发者社群里连续刷到“ponytail skill”“ponytail 插件”“插件 ponytail 如何使用”这几个搜索词,我才意识到,这已经不是一个单纯的英文单词,而是被一群开发者硬生生用成了一个工具代号。我花了大概两周时间,把能找到的公开资料、社区讨论、使用反馈都过了一遍,也自己动手搭了一套环境跑通全流程,才有了今天这篇东西。
先把结论摆在前面:ponytail 本质上是一个面向开发工作流的轻量级增强插件,它的核心定位是“把重复性的、机械的、容易出错的环节自动化掉”,让开发者能把注意力集中在真正需要思考的地方。你可以把它理解成一个“工作流里的马尾辫”——扎起来利落、不挡视线、干活方便。它不是一个庞大的框架,也不是一个全能的平台,而是一个插件形态的辅助工具,可以挂载到常见的开发环境或编辑器里,通过一套可配置的规则和技能(skill)体系,帮你处理那些“每次都要做、但每次做都烦”的事情。
那它到底能做什么?根据我这段时间的实际使用和社区里的反馈,ponytail 主要覆盖了这么几类场景:代码片段的快速生成与复用、重复性操作的批量执行、上下文感知的智能提示、以及跨工具链的状态同步。听起来有点抽象,我举个具体的例子你就明白了。比如你在写一个项目,每次新建一个模块都要手动创建目录结构、写一堆样板代码、配置路由、加测试文件——这些事单看每一件都不难,但加起来每天要花掉你不少时间。ponytail 的思路是,把这些“套路化”的操作定义成一个个 skill,需要的时候一句话调用,它帮你把这一整套动作跑完。
适合谁来用?我的判断是三类人收益最明显。第一类是日常写业务代码的工程师,尤其是那种项目里重复模式特别多的,比如 CRUD 写到手软的后端、组件切图切到眼花的前端。第二类是需要频繁切换项目上下文的开发者,比如同时维护好几个仓库、在多个技术栈之间来回跳的人,ponytail 的状态同步能力能帮你省掉不少“重新进入状态”的时间。第三类是对效率有执念、喜欢折腾工具链的人,哪怕你现在的流程已经挺顺了,ponytail 的 skill 机制也值得你花一个周末研究一下,因为它给你的是一种“把经验固化成工具”的能力。
提示:ponytail 目前还处于社区驱动的阶段,不同版本的接口和配置方式可能有差异。我下面讲的内容基于我实际跑通的版本,你在操作时如果发现某个命令对不上,先去官方仓库的 release notes 里确认一下版本变更。
2. 为什么是插件形态:设计思路与选型逻辑
2.1 插件化背后的取舍:为什么不做一个独立工具
我一开始也有疑问:既然要提升效率,为什么不直接做一个独立的 CLI 工具或者一个完整的 IDE?非要做成插件?后来自己用久了才想明白,这其实是一个非常务实的工程决策。独立工具的问题在于上下文断裂——你正在编辑器里写代码,突然要切到终端去跑一个命令,跑完再切回来,这个切换成本看起来很小,但一天下来累积起来很可观。而且独立工具很难感知你当前在做什么,它不知道你光标停在哪个文件、选中了哪段代码、当前项目的技术栈是什么。
插件形态最大的优势就是贴着你的工作现场。你在编辑器里选中一段代码,右键或者快捷键就能调用 ponytail 的 skill;你打开一个文件,它能根据文件类型和项目配置自动推荐相关的操作。这种“无感嵌入”的体验,是独立工具很难做到的。当然,插件形态也有代价——它受限于宿主环境的 API 能力,有些底层操作做不了,性能上也不如原生工具。但对于 ponytail 定位的这类“工作流增强”场景来说,这个取舍是划算的。
另一个关键考量是分发和更新。插件天然依附于宿主生态,用户安装和升级的成本极低。你不需要让用户去下载一个安装包、配置环境变量、处理依赖冲突,直接在插件市场点一下就行。这对于一个社区驱动的项目来说,能极大降低采用门槛。我见过太多好用的独立工具因为安装步骤太繁琐而无人问津,ponytail 选择插件路线,至少在“让人愿意试一下”这件事上占了先机。
2.2 skill 机制:ponytail 的核心抽象
ponytail 最核心的设计就是skill。你可以把 skill 理解成一个“可复用的操作单元”——它定义了“在什么条件下、对什么对象、执行什么动作、产生什么结果”。这个抽象看起来简单,但它的威力在于组合性。单个 skill 可能只做一件小事,比如“在当前目录下创建一个符合项目规范的组件文件”,但多个 skill 可以串联起来,形成一个完整的工作流。
我举个例子说明这个机制的实际价值。假设你有一个 skill 叫create-component,它负责根据模板生成一个组件文件;另一个 skill 叫register-route,它负责把新组件注册到路由配置里;还有一个叫add-test,负责生成对应的测试文件。单独看每个 skill 都很简单,但你可以定义一个组合 skill,把这三个串起来,执行一次就完成“新建组件 + 注册路由 + 加测试”这一整套动作。这就是 skill 机制的精髓——把流程知识固化成可执行的代码。
那 skill 是怎么定义的呢?根据我的使用经验,ponytail 的 skill 通常包含几个部分:触发条件(什么时候可以用这个 skill)、输入参数(需要用户提供什么信息)、执行逻辑(具体做什么)、输出结果(返回什么给用户或写入什么文件)。有些版本的 ponytail 支持用配置文件来定义 skill,有些则支持用脚本语言编写更复杂的逻辑。我建议新手先从配置文件入手,把最简单的 skill 跑通,再逐步尝试更复杂的脚本化 skill。
注意:skill 的命名尽量用“动词+名词”的格式,比如
create-component、sync-config、batch-rename。这样在调用和组合的时候语义清晰,不容易搞混。我见过有人用thing1、thing2这种命名,过两周自己都忘了是干嘛的。
2.3 和其他效率工具的差异:ponytail 的边界在哪
市面上效率工具很多,代码片段管理有 snippet 工具,任务自动化有构建脚本,智能提示有各种 AI 补全。ponytail 跟它们有什么区别?我自己的理解是,ponytail 的定位在**“片段管理”和“完整自动化”之间**。snippet 工具帮你存代码片段,但插入之后的事情它不管;构建脚本能跑完整流程,但写起来门槛高、调试麻烦。ponytail 的 skill 机制刚好卡在中间——比 snippet 更有逻辑,比构建脚本更轻量。
另一个差异点是上下文感知。传统的 snippet 工具是“你主动去找片段”,ponytail 更多是“根据你当前的操作推荐 skill”。比如你新建了一个文件,它会根据文件路径和命名推测你可能要做什么,然后把相关的 skill 列出来。这种“主动推荐”的体验,用惯了之后确实回不去。当然,这也意味着你需要花一些时间配置项目规则,让 ponytail 能准确理解你的项目结构。这个投入是一次性的,但回报是长期的。
3. 上手实操:从零跑通第一个 ponytail skill
3.1 环境准备与插件安装
在开始之前,你需要确认几件事。首先,你的开发环境得是 ponytail 支持的宿主之一。根据我的了解,目前社区里讨论最多的宿主是 VS Code 和 JetBrains 系列 IDE,其他编辑器可能也有支持,但资料相对少一些。我下面以 VS Code 为例来演示,其他宿主的操作逻辑类似,具体命令可能需要调整。
安装步骤本身不复杂。打开 VS Code,进入扩展面板,搜索“ponytail”,找到对应的插件后点击安装。安装完成后,通常需要重启一下编辑器,让插件完成初始化。重启之后,你会在侧边栏或者命令面板里看到 ponytail 的入口。如果没看到,先检查插件是否真的安装成功,再看一下输出面板里有没有报错信息。
安装完成后第一件事是初始化配置。ponytail 一般会在你的项目根目录下生成一个配置文件,名字可能是.ponytailrc或者ponytail.config.json之类的。这个文件是整个 skill 体系的入口,里面定义了 skill 的搜索路径、项目规则、默认参数等。我建议你先把默认配置通读一遍,了解每个字段的含义,再根据自己的项目情况做调整。
{ "skillPaths": ["./ponytail-skills"], "projectType": "react", "defaultAuthor": "your-name", "autoSync": true }上面这个是我在一个 React 项目里用的简化配置。skillPaths告诉 ponytail 去哪里找自定义 skill,projectType帮助它选择合适的默认模板,autoSync控制是否自动同步状态。这几个字段是最常用的,其他字段可以等用到的时候再查文档。
3.2 编写你的第一个 skill:从需求到落地
我建议第一个 skill 选一个你每天都在做、但每次做都觉得烦的操作。对我来说,这个操作是“新建一个 React 组件文件并写好样板代码”。每次都要手动创建文件、写 import、写函数组件、写 export,虽然只有十几行,但一天重复十几次就很烦。下面我就以这个为例,带你走一遍完整流程。
首先,在项目根目录下创建ponytail-skills文件夹(跟配置里的skillPaths对应)。然后在这个文件夹里新建一个文件,比如create-component.json。skill 的定义通常包含几个关键字段:name是 skill 的调用名,description是给人看的说明,trigger定义触发条件,actions定义具体执行的动作序列。
{ "name": "create-component", "description": "根据模板创建一个新的 React 组件文件", "trigger": { "type": "command", "command": "ponytail.createComponent" }, "actions": [ { "type": "prompt", "message": "请输入组件名称", "variable": "componentName" }, { "type": "createFile", "path": "src/components/{{componentName}}/{{componentName}}.tsx", "template": "react-component" }, { "type": "createFile", "path": "src/components/{{componentName}}/index.ts", "content": "export { default } from './{{componentName}}';" } ] }这个定义里,prompt动作会弹出一个输入框让你填组件名,填完之后createFile动作会根据模板生成组件文件,再生成一个index.ts做统一导出。{{componentName}}是变量替换语法,ponytail 会把用户输入的值填进去。模板文件react-component需要你提前在模板目录里定义好,内容就是你平时手写的那套样板代码。
写完这个 skill 之后,你需要注册它。有些版本的 ponytail 会自动扫描skillPaths下的所有文件,有些则需要你在配置里显式列出。我用的版本是自动扫描的,保存文件后重启一下编辑器就能在命令面板里看到ponytail.createComponent这个命令了。执行一次,如果一切正常,你应该能看到新组件文件被创建出来。
3.3 参数传递与变量替换的细节
变量替换是 skill 机制里最容易出问题的地方,我踩过好几次坑,这里展开说一下。ponytail 的变量语法通常是双花括号{{variableName}},但不同版本可能有差异,有的用${variableName},有的用$variableName。你在写 skill 之前,一定要先确认当前版本用的是哪种语法,否则变量不会被替换,生成的文件名会直接变成{{componentName}}这种字面量。
变量的来源主要有几种。一种是prompt动作产生的,就是弹框让用户输入;一种是内置变量,比如当前文件名、当前选中文本、当前日期等,这些不需要用户输入,ponytail 会自动填充;还有一种是配置文件里定义的变量,比如项目名称、作者名等,适合那些固定不变的值。我建议把固定值都放到配置文件里,skill 定义里只保留需要动态输入的部分,这样 skill 的复用性更好。
还有一个细节是变量的作用域。在一个 skill 的多个 action 之间,变量是可以传递的。比如第一个 action 生成了一个变量,后面的 action 可以直接引用。但不同 skill 之间的变量是隔离的,你不能在一个 skill 里引用另一个 skill 的变量。如果确实需要跨 skill 共享数据,得通过文件或者配置来中转。这个设计是为了避免 skill 之间的隐式耦合,虽然有时候会觉得不方便,但长期来看是好事。
提示:调试 skill 的时候,可以在 action 里加一个
log类型的动作,把中间变量的值打印到输出面板。这样你能清楚看到每一步执行后变量是什么,排查问题会快很多。
4. 进阶玩法:把 ponytail 用出花来
4.1 skill 组合与工作流编排
单个 skill 用熟了之后,你会自然想要把多个 skill 串起来。ponytail 支持在 skill 定义里引用其他 skill,这就打开了工作流编排的可能性。我举个实际例子:我们团队有个规范,每次新增一个页面,需要做四件事——创建页面组件、注册路由、添加菜单项、写测试文件。这四件事分别对应四个 skill,我定义了一个组合 skill 叫create-page,按顺序调用这四个 skill,执行一次就全部搞定。
组合 skill 的定义方式通常是在actions里用invoke类型的动作来调用其他 skill。需要注意的是执行顺序和错误处理。如果第一个 skill 失败了,后面的还要不要继续?我的经验是,对于有依赖关系的 skill,应该在前一个成功后再执行下一个;对于相互独立的 skill,可以并行执行以提高速度。ponytail 一般会提供onError配置来控制失败后的行为,你可以根据实际情况选择abort(中止)、continue(继续)或者retry(重试)。
工作流编排还有一个实用技巧是条件分支。比如你可以根据用户选择的项目类型,走不同的 skill 分支。React 项目走一套流程,Vue 项目走另一套。这个通过condition类型的动作来实现,判断条件可以基于变量值、文件是否存在、当前目录等。我一开始觉得这个功能用不上,后来项目里同时维护多个技术栈,才发现条件分支是刚需。
4.2 与版本控制的配合:什么时候该提交,什么时候不该
ponytail 生成的代码和文件,跟版本控制的配合有几个需要注意的点。首先是生成文件的提交时机。我建议把 skill 生成的文件和普通手写文件一样对待,该提交就提交,不要因为是自动生成的就不纳入版本控制。这样团队里其他人拉取代码后,看到的是完整的项目结构,不会因为缺少某个自动生成的文件而跑不起来。
其次是skill 定义本身的版本管理。ponytail-skills目录应该纳入版本控制,这样团队成员的 skill 配置能保持一致。但配置文件.ponytailrc里可能包含个人偏好设置,比如默认作者名、快捷键绑定等,这部分可以考虑用.gitignore排除,或者拆分成“团队配置”和“个人配置”两个文件。我见过有团队把 skill 定义放在独立的仓库里,通过子模块的方式引入到各个项目,这也是一种可行的做法,适合 skill 数量多、跨项目复用频繁的场景。
还有一个坑是生成文件的冲突处理。如果两个人同时用 skill 生成了同名文件,合并的时候会冲突。我的做法是在 skill 定义里加入时间戳或随机后缀,降低同名概率。比如生成的文件名里带上日期,或者用短随机字符串做后缀。当然,这会让文件名变得不那么干净,所以只在对冲突敏感的场景下用,日常开发还是保持简洁命名。
4.3 性能优化:让 skill 跑得更快
skill 多了之后,执行速度会成为一个问题。我实测下来,影响速度的主要因素有三个:skill 的搜索和加载、文件读写操作、外部命令调用。搜索和加载这块,ponytail 一般会有缓存机制,但如果你经常改 skill 定义,缓存会频繁失效。我的建议是把稳定的 skill 和经常调整的 skill 分开存放,稳定的那些放在一个目录里,让缓存能长期生效。
文件读写是另一个大头。如果一个 skill 要创建很多文件,每个文件都单独读写一次,累积起来就慢了。ponytail 有些版本支持批量文件操作,把多个文件的创建合并成一次操作,能明显提速。如果你的版本不支持,可以考虑把多个小文件合并成一个大文件,或者用模板引擎一次性渲染出所有内容再写入。
外部命令调用是最慢的,因为涉及到进程创建和上下文切换。如果 skill 里需要跑npm install或者git命令,能合并的就合并,能异步的就异步。我有个 skill 原来要跑五次git命令,后来合并成一次git调用加参数,速度快了将近一倍。另外,对于耗时的外部命令,可以考虑放到后台执行,不阻塞主流程,等结果出来再通知。
5. 常见问题与排查技巧实录
5.1 skill 不生效:从哪开始查
skill 不生效是最常见的问题,表现通常是“命令面板里找不到”“执行了没反应”“报错但看不懂”。我的排查顺序是这样的:先确认 skill 文件被正确加载,再确认触发条件匹配,最后确认执行逻辑没有报错。第一步,打开 ponytail 的输出面板,看看有没有加载 skill 的日志。如果日志里没有你的 skill 文件名,说明搜索路径配置有问题,或者文件格式不对。
第二步,检查触发条件。如果你用的是命令触发,确认命令名拼写正确,大小写敏感。如果你用的是快捷键触发,确认快捷键没有被其他插件占用。如果你用的是自动触发(比如保存文件时触发),确认触发条件里的文件匹配模式写对了。我遇到过好几次是 glob 模式写错,比如src/**/*.tsx写成了src/*.tsx,导致子目录里的文件不触发。
第三步,看执行日志。ponytail 一般会在输出面板里打印每个 action 的执行结果,成功还是失败、耗时多少、有没有异常。如果某个 action 失败了,日志里通常会有错误信息。常见的错误包括:变量未定义、文件路径不存在、模板文件缺失、权限不足等。根据错误信息对症下药,大部分问题都能解决。
5.2 变量替换失败的几种典型情况
变量替换失败的表现是生成的文件里出现了{{variableName}}这样的字面量,而不是实际的值。原因通常有几种。第一种是语法不匹配,你用的变量语法跟当前 ponytail 版本支持的不一致。解决办法是查文档或者看示例 skill 里用的是什么语法。第二种是变量名拼写错误,比如定义的是componentName,引用的时候写成了componentname,大小写不一致导致匹配不上。
第三种是变量作用域问题,你在一个 action 里定义的变量,在另一个 action 里引用,但这两个 action 不在同一个 skill 里,或者中间隔了其他 skill 调用。解决办法是把变量定义和引用放在同一个 skill 的同一个执行链里。第四种是变量值为空,用户没有输入或者输入了空字符串,替换后就是空。这种情况可以在 skill 定义里加默认值,或者加校验逻辑,空值时不执行后续动作。
注意:有些 ponytail 版本对变量名有保留字限制,比如不能用
path、name这种跟内置字段重名的变量名。如果你发现变量死活替换不了,试试换个名字,比如把name改成componentName。
5.3 性能问题的排查思路
skill 执行慢的时候,先定位是哪个环节慢。ponytail 的输出面板通常会显示每个 action 的耗时,你看一下哪个 action 占了大头。如果是文件操作慢,检查是不是在操作大文件或者大量小文件;如果是外部命令慢,检查命令本身是不是耗时操作,能不能优化;如果是 skill 加载慢,检查 skill 文件是不是太多或者太大。
我遇到过一次 skill 执行特别慢的情况,排查后发现是某个 action 在遍历整个项目的文件列表,而项目里有几万个文件。解决办法是缩小遍历范围,只遍历需要的目录,或者加缓存避免重复遍历。还有一次是外部命令调用没有设置超时,某个命令卡住了,整个 skill 就一直等着。后来加了超时配置,超时后自动中止并报错,问题就解决了。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 命令面板找不到 skill | skill 未加载 | 查看输出面板加载日志 | 检查 skillPaths 配置和文件格式 |
| 执行后无反应 | 触发条件不匹配 | 检查触发配置和实际条件 | 调整触发条件或手动触发测试 |
| 生成文件名为变量字面量 | 变量语法不匹配 | 对比文档和示例 | 统一变量语法 |
| 变量值为空 | 用户未输入或默认值缺失 | 查看变量定义和输入记录 | 加默认值或输入校验 |
| 执行速度慢 | 文件操作或外部命令耗时 | 查看各 action 耗时 | 优化操作范围或加缓存 |
| 报权限错误 | 文件或目录权限不足 | 检查目标路径权限 | 调整权限或以管理员运行 |
| 模板文件找不到 | 模板路径配置错误 | 检查模板目录和引用路径 | 修正路径或补充模板文件 |
6. 我踩过的坑和给你的建议
6.1 不要一上来就追求大而全
我刚开始用 ponytail 的时候,恨不得把所有能自动化的都自动化,写了一大堆 skill,结果维护成本比手动操作还高。后来我调整了策略,只把那些“每天至少做三次、每次至少花一分钟”的操作做成 skill,其他的先手动做着。这样 skill 数量控制在十几个,每个都经过高频验证,稳定可靠。那些低频操作,等真的觉得烦了再做成 skill 也不迟。
这个策略背后的逻辑是投入产出比。写一个 skill 需要时间,调试需要时间,后续维护也需要时间。如果一个操作一周才做一次,手动做也就几分钟,做成 skill 省下的时间可能还不够写 skill 的时间。只有当操作频率足够高,自动化带来的收益才能覆盖成本。我现在的判断标准是:如果这个操作我连续三天都做了,而且预计下周还会继续做,那就值得做成 skill。
6.2 skill 的命名和文档比你想的重要
我吃过亏。早期写的 skill 命名很随意,do-thing、handle-stuff这种,过了一个月自己都忘了是干嘛的。后来我强制自己用动词+名词的格式,并且每个 skill 都写一段description,说明它做什么、什么时候用、有什么注意事项。这样即使过了很久,看一眼名字和描述就能想起来。团队协作的时候,这个习惯更重要,别人看到你的 skill 列表,能快速理解每个 skill 的用途。
文档还有一个作用是记录决策背景。比如为什么这个 skill 要用这种模板而不是那种,为什么参数设计成这样。这些信息当时觉得理所当然,过几个月就忘了。我在 skill 的description里会加一句“设计说明”,简短记录当时的考量。这个习惯帮我省了很多次“为什么要这么写”的困惑。
6.3 定期清理和重构 skill
skill 跟代码一样,会随着时间腐化。项目结构变了、技术栈升级了、团队规范调整了,原来的 skill 可能就不适用了。我现在的做法是每个季度花半小时过一遍 skill 列表,把不再使用的删掉,把需要更新的改掉,把可以合并的合并。这个习惯让我的 skill 库始终保持精简,加载速度快,维护成本低。
清理的时候我会问自己三个问题:这个 skill 过去一个月用过吗?如果没用过,是暂时不用还是永远不用?如果暂时不用,能不能先归档而不是直接删?归档的 skill 放到单独的目录里,不参与日常加载,但需要的时候还能找回来。这样既保持了活跃 skill 的简洁,又不会误删有用的东西。
6.4 跟团队分享你的 skill
一个人用 ponytail 和团队一起用,效果完全不一样。我把常用的 skill 分享给团队后,发现几个好处。一是规范统一,大家生成的代码结构一致,review 的时候省心。二是知识沉淀,老员工的操作经验通过 skill 固化下来,新员工直接就能用。三是互相启发,看到别人的 skill 会想到自己也可以这么做,skill 库越来越丰富。
分享的方式可以很简单,把ponytail-skills目录纳入版本控制就行。如果想做得更规范,可以建一个独立的 skill 仓库,用子模块引入到各个项目。我们团队现在用的是后一种方式,skill 仓库有独立的 README 和贡献指南,谁想加新 skill 就提 PR,review 通过后合并。这样 skill 的质量有保障,也不会因为某个人的随意改动影响所有人。
6.5 保持对宿主环境更新的关注
ponytail 作为插件,依赖宿主环境的 API。宿主环境升级后,插件可能不兼容,skill 可能失效。我遇到过好几次 VS Code 大版本更新后 ponytail 报错的情况,解决办法通常是等插件作者发布兼容版本,或者回退宿主版本。为了减少这种被动,我现在会关注插件的 release notes 和 issue 区,看到有人反馈兼容性问题就提前做准备,比如暂缓宿主更新,或者找替代方案。
另外,ponytail 本身的版本更新也值得关注。新版本可能带来新功能、性能优化、bug 修复,也可能引入不兼容的变更。我一般会在小版本更新时直接升级,大版本更新时先看 changelog,确认没有破坏性变更再升。升级前备份一下 skill 配置和自定义 skill,万一出问题能快速回滚。
7. 这个内容后续还可以这样扩展
ponytail 的 skill 机制其实打开了很多可能性。我现在在尝试的一个方向是把 skill 和项目脚手架结合。新项目初始化的时候,根据项目类型自动加载对应的 skill 集合,省去手动配置的步骤。另一个方向是skill 的市场化,把通用的 skill 打包发布,让其他人也能用。社区里已经有人在讨论这个了,如果真能做起来,以后找 skill 就像装插件一样方便。
还有一个我觉得很有潜力的方向是skill 的智能推荐。现在触发 skill 主要靠手动或者简单的条件匹配,未来如果能根据用户的操作习惯自动推荐甚至自动执行 skill,效率还能再上一个台阶。当然这涉及到隐私和可控性的问题,需要谨慎设计。但方向是明确的——让工具更懂你,而不是你去适应工具。
如果你也在用 ponytail,或者对 skill 机制有自己的想法,欢迎交流。我始终觉得,效率工具的价值不在于它本身多强大,而在于它能不能真正融入你的工作流,成为你下意识就会用的东西。ponytail 在这条路上走得挺稳的,值得花时间研究一下。