SegmentFault 全栈增长工程师最新的文章
2022-05-23T20:23:03+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
为“架构”再建个模:如何用代码描述软件架构?
https://segmentfault.com/a/1190000041886019
2022-05-23T20:23:03+08:00
2022-05-23T20:23:03+08:00
phodal
https://segmentfault.com/u/phodal
2
<p>在架构治理平台 ArchGuard 中,为了实现对架构的治理,我们需要<strong>代码</strong> + <strong>模型</strong>描述所要处理的内容和数据。所以,在 ArchGuard 中,我们有了<a href="https://link.segmentfault.com/?enc=2fq7qXImd%2B1XJ1cgffnoHA%3D%3D.sUFui2k2cPnfggnJtVRDpblnWObgU0H%2FXn9voziC989q%2BMcr1v2LzRou2wGjLp7K" rel="nofollow">代码的模型</a>、依赖的模型、变更的模型等,剩下的两个核心的部分就是<strong>架构的模型</strong>、<strong>架构的治理模型</strong>,其它的还有诸如<a href="https://link.segmentfault.com/?enc=gs6SDiVL1Es9eEHidZm8uw%3D%3D.n8NGrLGVxYKJLJqFWUTJD13BK7Jm7RJSU59CCAb4jp3IKU3B7UgjZxr%2FlpCfmlFP" rel="nofollow">构建的模型</a>等,会在后续的过程中持续引入到系统中。</p><p>PS:本文里的架构展开是基于自动化分析需求的,模型也是基于这个动机出发的。</p><h2>架构是什么??</h2><p>对单个语言的代码建模并不难,对于一个语言有特别的概念,如 package、class、field、function 等等。在有了明确概念的基础之下,结合我们的业务上的需求,就能构建一个太差不差的模型。在采用 DDD 这一类建模方式的时候,产生共识,提炼知识,形成概念等,便能构建出模型的雏形。</p><h3>起点:架构是重要的元素</h3><p>然而,对于架构来说,业内没有统一的定义。于是乎,诸如 Martin Fowler 喜欢引用 GoF(《设计模式》作者们) 之一的 Ralph Johnson 对于架构的描述:</p><blockquote>架构是那些重要的东西……,无论它具体是什么。</blockquote><p>同样的 Grady Booch (UML 的发明者之一)也是惟类似的方式来概括架构的:</p><blockquote>软件架构是系统设计过程中的重要设计决定的集合,可以通过变更成本来衡量每个设计决定的重要维度。</blockquote><p>所以呢,这让我们感觉说了等于没说,我们得去定义什么是<strong>重要的东西</strong>。而重要的东西,在不同人、不同场景之下,它是存在差异的。哪怕是同一个类型的软件,在不同的公司、不同的利益相关者的背景之下,重要的东西也尽相同。</p><h3>原则:可是到底哪些是重要的?</h3><p>于是乎,我再尝试去引用最新的架构相关的书籍,诸如于我编写这篇文章时,参考的书《软件架构:架构模式、特征及实践指南》一书中 Neal Ford 对于架构的定义:</p><blockquote>软件架构中包含系统的结构、系统必须支持的架构特征、架构决策以及设计原则。<strong>系统的结构</strong>是指实现该系统的一种或多种架构风格(如微服务、分层和微内核等)。<strong>架构特征</strong>定义了系统的成功标准。<strong>架构决策</strong>定义了一组关于如何构建系统的规则。<strong>设计原则</strong>是关于如何构建系统的非必须遵循的指导原则。</blockquote><p>从模型构筑的层面来说,书中的定义也提供了一个灵活性。诸如于在架构特征的定义里,关注的是各类能力(ability),如互操作性、可适用性、可测试性等等。</p><p>在现有的 ArchGuard 这个业务场景之下,我们难以自动化地识别出各类的特征。因为从实践的层面上来说,这些能力并不一定实现了,它是目标架构,可能还只存在于架构蓝图之上的。在这个层面上来说,这里定义的架构,偏向于是<strong>设计层面</strong>的定义。</p><p>从另外一方面来说,架构决策则是在架构治理的过程中,我们所关注的核心。可以在后续针对于这一系列的原则的规则,构建出一个描述架构特征的 DSL。</p><h3>重要的元素:组件、边界与通信</h3><p>接着,让我们再回到 Bob 大叔(Robert C. Martin)的《架构整洁之道》书中的定义:</p><blockquote>软件系统的质量是由它的构建者所决定的,软件架构这项工作的实质就是<strong>规划如何将系统切分成组件,并安排好组件之间的关系,以及组件之间互相通信的方式</strong>。</blockquote><p>再从 Clean Architecture 模式来说,Bob 大叔一直在强调的是:顶层抽象策略与底层实现要实现解耦。诸如于如何划定合理的边界?如何组合相关的策略与层次?从这个模式上来说,我们得到了一个越来越清晰的定义。</p><p>然而,我们还遇到一个更难的问题是,<strong>如何定义一个组件是什么</strong>?**还有关系是什么?**在书里的序言, Kevlin Henney(《面向模式的软件架构》卷4、卷 5的作者之一)给了一个更精确的描述词:<strong>组织结构</strong>(structure),从宏观到微观的构筑过程,其中的构件包含了组件、类、函数、模块、层级、服务等。</p><p>对于大型软件来说,其组织结构方式异常复杂,它像极了一个国家的层级关系,一级部门、二级部门等等。而部门之间又有复杂的关系,正是层级关系 + 层级的构件构建成了这个复杂的系统。(PS:而了让系统能良好的运行,即其中的组件(螺丝钉)按规则执行,则需要一个督察组织。)</p><h3>层次结构:组件和关系</h3><p>软件架构已经有了几十年的历史,我们已经用 ”模式“ 这一词对过去的架构进行了一系列的总结。二十年前,人们初步总结了《面向模式的软件架构》(POSA)。在这里,就引述 POSA 1 的第 6 章里,有一个完整的层级关系介绍:</p><blockquote><p><strong>软件架构</strong>描述了软件系统的子系统和组件以及它们之间的关系。通常使用不同的视图来说明子系统和组件,以展示软件系统的功能特征和非功能特征。</p><p><strong>组件</strong>是被封装起来的软件系统的一部分,包含一个接口。组件是用于打造系统的构件。在编程语言层面,组件可能由模块、类、对象或一组相关的函数表示。</p><p><strong>关系</strong>描述了组件之间的联系,可能是静态的,也可能是动态的。静态关系会在源代码中直接显示出来,它们指出了架构中组件的布局;动态关系指出了组件之间 的临时关系和动态交互,可能不容易通过源代码的静态结构看出来。</p><p><strong>视图</strong>呈现软件架构的某个方面,展示软件系统的某些具体特征。</p><p>……</p></blockquote><p>在今天来看,从模式上来说,软件架构本身并没有发生太大的变化。只是呢,一些定义发生了变化,诸如于组件和接口。在微服务架构风格流行的今天,一个微服务也可以视为一个组件,它包含了一系列的接口,对外提供了复用的能力。而用来描述它们的关系的元素,则不再是过去的函数调用,变为了远程调用、事件触发。</p><p>现在,我们有了一详尽的定义,在建模上,可能还欠缺一些元素,诸如于,如何分析出<strong>组件间的关系</strong>。</p><h3>第 3 种架构视图:展示工程关注点</h3><p>在 ArchGuard 中,我们使用了 C4 架构可视化模型作为一种参考视图。这种实现的方式主要是从分析和可视化的层面来考虑的。除了 C4 之外,另外一种主流的方式是 4 + 1 视图。顺带一提,在 4 + 1 的论文《Architectural Blueprints—The “4+1” View Model of Software Architecture》,同样也有一个描述架构的表示公式:<code>Software architecture = {Elements, Forms, Rationale/Constraints}</code>。</p><p>从通识的角度来看,采用 4 + 1 视图是一个比较理想的方式。只是,由于存在大量的 PaaS、IaaS 等 xx 即服务设计的不合理性,使得这些记录基础设计相关信息的代码,并没有与代码库一起存放,使得在辩识上存在一定的难度。</p><p>因此,从实现的层面来说,在这里,我们要引用的是《面向模式的软件架构》中,提到的《Software Architecture in Industrial Applications》(也可以参考《实用软件体系结构》一书)架构视图:</p><ul><li>概念视图:描述了整个系统需求向整个体系结构的转化。</li><li>模块视图:描述了如何将系统划分成模块并将模块组织成层。</li><li>执行视图:描述了系统的动态元素以及它们之间的交互。</li><li>代码视图:描述了源代码的组织结构。</li></ul><p>在这个视图的定义里,它更能清晰的划分开几个不同层面的考虑因素。采用作者们在最早的论文里提到的示例:</p><table><thead><tr><th>软件架构</th><th>使用示例</th><th>影响因素的例子</th></tr></thead><tbody><tr><td>代码架构</td><td>配置管理, 系统构建、OEM 定价</td><td>编程语言,开发工具和环境,扩展子系统</td></tr><tr><td>模块架构</td><td>模块接口管控、变更影响分析、接口约束一致性检查、配置管理</td><td>使能软件技术、组织结构、设计原则</td></tr><tr><td>执行架构</td><td>性能和可调度性分析,系统的静态和动态配置,将系统移植到不同的执行环境</td><td>硬件架构、运行时环境性能标准、通信机制</td></tr><tr><td>概念架构</td><td>使用特定领域的组件和连接器进行设计、性能评估、安全性和可靠性分析、了解系统的静态和动态可配置性</td><td>应用领域、抽象软件范式、设计方法</td></tr></tbody></table><p>从表格的右边里,我们就可以直接对应到系统所需要的每个层面的设计因素,诸如于编程语言等元素放在代码架构上。换句话来说,在微服务、单体架构下,都能找到自己合适的位置。</p><h3>概念的最后:描述模型的类型系统</h3><p>最后,为了保证本文在概念的完整性,我们还需要一种方式来描述这个系统种的模型和一系列的概念,从形式上来说,它是一个类型系统。诸如于,我们在 UML中所表示的 (PlantUML 表示方式):</p><pre><code class="javascript">class Architecture {
Component[] components
System[] subSystems
Relation[] relations
ArchStyle archStyle
Rule[] archRules
...
}</code></pre><p>一个用来描述类型的系统,就是一个类型系统,和编程语言里的类型是等同的。它可以用来解释一系列的概念,以及概念之间如何连接。</p><p>顺带一题,如果我们把编程语言看作是一个系统,那么我们就会发现其在设计的有趣之处。类型系统与结构体(或者类)可以用于构建系统中的概念,一个个的表达式则是用于构建概念之间的关系。</p><h2>建模</h2><p>对于概念来说,哪怕是有了如此多丰富的展开,要做一个小结也并非一件容易的事情。更何况于模型而言,它是数值上的标准化,一种接近于通用的模型形态。</p><h3>设计:第一个版本的架构模型</h3><p>所以,我的第一个尝试是从 《面向模式的软件架构》的定义下手:</p><pre><code class="javascript">class SystemArchitecture(
val archStyle: ArchitectureStyle,
val subSystem: List<SubSystem>,
var components: List<ArchComponent>,
val connections: List<ArchConnection>
)</code></pre><p>示例:对于一个系统来说,它存在一个主的架构模式,如微服务架构。而在每个微服务相当于是一个子系统或者说组件。当然了,一个子系统可以包含多个微服务。而对于个组件来说,它包含了输入和输出,以及一系列的计算逻辑。所以,它的一个模型可能是这样的:</p><pre><code class="javascript">class ArchComponent(
val name: String,
val type: ArchComponentType,
val inbounds: List<String>,
val outbound: List<String>,
val components: List<ArchComponent>
)</code></pre><p>而麻烦的一点在于,组件是动态的,组件之间是存在关系的,组件内部也存在关系。所以,我们还需要考虑如何表示组件间的关系?</p><pre><code class="javascript">class Connection(
val connectors: String,
val source: String,
val target: String,
var connectionType: ConnectionType,
val connectorStyles: ConnectorStyle,
)</code></pre><p>所以,如果以宽泛的定义来看,组件其实是树形结构的。同样的,对于架构来说,这种 Connection 也是存在的。根据上面的一系列形态展开,我们就能得到一个初始版本的架构的架构模型。</p><p>更多模型相关内容,详见 ArchGuard Scanner 中的初始版本 <a href="https://link.segmentfault.com/?enc=I1U26KlC6HWmPRfVsF6Wow%3D%3D.cWPC6%2BvbkO7D8XaRt09IIGKv9QOwVObEwhmC%2BTBIlZaMOc5%2FD4EY%2BNXcB7luxoG1EwAFKXObTdeVV8SWtxgnOKxqh3aF%2F5y1bPtGp6ciPElNr1KSPsXOd7YJDjUhs84TKGwqeQ85VoTCOFL4YNBBxa3Pba%2Bf%2F57EzcS%2F0UWGw5BuPgTg1RIt7z138wlA2J0V" rel="nofollow">Architecture.kt</a> 。</p><h3>实现思路:生成架构模型</h3><p>ArchGuard 中的模型是通过代码来生成的。在这种业务场景之下,在构建模型的时候,需要平衡设计与实现。由此,基于代码分析的视图方式更适用我们。从代码和依赖中,我们可以:</p><ul><li>基于目录结构分析出分层关系,进而得到代码的结构视图。</li><li>分析依赖管理工具,进而得到一个模块的视图。</li><li>分析依赖的软件,如 Spring、Dubbo 等,进而得到一个执行的视图。</li><li>分析软件中的模型,进而得到概念视图。</li></ul><p>结合之下,我们就能构建出一个更完整的架构模型。</p><h3>实现:放弃第一个版本的模型</h3><p>设计挺美好的,但是当我开始写第一行代码的时候,发现不对劲了:我们有了一个分析的模型,但是输入呢?</p><p>仅从项目的分析来说,我们拿到的只是一个代码仓库的代码。一个代码仓库可能是一个模块,一个系统,又或者是一个服务。所以,我们的输入是什么?基于已有的分析情况,如项目依赖(依赖管理工具)、源码结构、语言等?我们缺少构建一个项目所需要的上下文。</p><p>我们从源码所能分析到的内容如下所示:</p><pre><code class="kotlin">data class PotentialExecArch(
var protocols: List<String> = listOf(),
var appTypes: List<String> = listOf(),
var connectorTypes: List<ConnectorType> = listOf(),
var coreStacks: List<String> = listOf(),
var concepts: List<CodeDataStruct> = listOf()
)</code></pre><p>对应的一些分析细节:</p><ol><li>协议信息。解析源码和 Gradle、Maven、NPM 中的依赖信息,如含 Dubbo 就视为带 RPC;如带 <code>java.io.File</code> 就认为带 FileIO</li><li>应用类型。解析依赖信息,如包含 <code>clikt</code> 就视为 Kotlin 的 CLI 应用。</li><li>连接类型。同上</li><li>核心技术栈。根据依赖类型分析。</li><li>概念模型。需要先分析潜在的分层架构模型,根据分层架构模型,就能得到概念集。</li></ol><p>当然了, 这部分代码还没写完,如果有兴趣,也欢迎来加入我们。</p><h3>多种架构模型</h3><p>于是,在 ArchGuard 的背景下,“架构” 的架构模型有多种形态的:</p><ul><li>扫描形态下的架构模型(对应到 ArchGuard Scanner)。即,从各个项目的源码中得到的架构模型,从源码、依赖、数据库等中计算出来。</li><li>分析形态下的架构模型(对应到 ArchGuard Backend)。基于分析形态下的架构模型,构建出包含架构相关知识的架构模型。</li><li>展示形态下的架构模型(对应到 ArchGuard Frontend)。结合可视化的知识,展示出对应的架构模型。</li></ul><p>每一个模型都是在上一步的基础上添加了领域知识,那么什么是领域知识呢?如何做好架构?</p><h2>小结:挑战</h2><p>模型,一个开始,它需要不断地演进才能适用需要。而有了一个凑合能用的模型,只是这一系列工作的开始,我们还会遇到一系列的挑战。</p><h3>描绘目标架构</h3><p>系统中的架构反应的只是现状,如何去描述未来的架构,并将两者进行匹配,又是一个非常有意思的话题。</p><h3>衡量变化性</h3><p>我们还将面临的另外一个问题是,软件架构并非是不变的。</p><p>软件只要一直在开发,就会以细微地方式变化着。从宏观的层面来说,尽可能架构师都在努力地不去大范围地变动结构。而我们需要面对于这些挑战,诸如于基础设施变化,而这种变化带来的是临时性的中间状态。</p><p>如何去表示这种临时性的中间状态,就变得更加有意思。</p><p>在这里只开始了迈出了第一步,如果大家有兴趣,欢迎来 ArchGuard 为技术建模:<a href="https://link.segmentfault.com/?enc=3iOZSWZUJnH6al2FOR1%2Fzg%3D%3D.5BUgPE226qQL7szCMkr3AE6ZOa9cSmuzfyoY150eNz0RL6x54oXWslzsFWr%2BnHEHBdgcNPPDq10P%2BppX3DfUhg%3D%3D" rel="nofollow">https://github.com/archguard/archguard/discussions/67</a></p><h2>参考资料</h2><ul><li>《整洁架构之道 》</li><li>《架构之美》</li><li>《软件架构:架构模式、特征及实践指南》</li><li>《面向模式的软件架构 卷 1:模式系统》</li><li>《实用软件体系结构》</li><li>《Software Architecture in Industrial Applications》</li><li>《Architectural Blueprints—The “4+1” View Model of Software Architecture》</li></ul>
开源架构治理平台 ArchGuard,专治分布式场景下各种不服
https://segmentfault.com/a/1190000041660342
2022-04-06T10:42:18+08:00
2022-04-06T10:42:18+08:00
phodal
https://segmentfault.com/u/phodal
1
<p>过去的 10 年间,软件的架构发生了巨大的变化,从早先流行的单体 MVC 架构,变成了所谓的 5:5 开,即分布式 vs 单体。只是呢,有大量的软件开发人员,无法看到系统的全貌,又或者是从单体的思维转变过来。于是,哪怕是在使用了微服务的情况下,但是实现的却又是一个一个的单体,只是它们变成了“分布式的单体”。</p><p>架构治理变成一个急待解决的问题。</p><h2>我们所面临的挑战</h2><p>作为一个架构师或者是软件开发人员,在架构治理上,我们面对的诸多挑战有:</p><ul><li>设计与实现不匹配。设计的软件架构与真正实施后的架构,存在着巨大的差异。而这个差异,往往需要编码上线、乃至一段时间之后才能发现。</li><li>没有规范/不遵守规范。作为一个资深的开发人员,我们制定了一系列的规范,但是没有多少团队人员愿意遵守。</li><li>代码量巨大,难以识别问题。一个由十几个或者几十个微服务创建的系统,往往难以快速发现它们之间错综复杂的关系。</li><li>架构模型的每个层级都可能出错。如服务间 API 耦合、代码间耦合、数据库耦合等等。</li><li>架构师、开发人员自身缺乏丰富的经验。知道有问题,但是说不出来哪有问题,也不知道如何改进。</li></ul><p>应对这些挑战,我们需要一个平台/工具,来帮助我们解决这些问题。所以,结合我们过去的一系列软件开发和重构经验,我们(Thoughtworks 的咨询师们)从 2020 年(疫情开始的时候)开始了架构治理平台 ArchGuard 的开发。</p><p>如今呢,它开源了,GitHub:<a href="https://link.segmentfault.com/?enc=WNIoylRjpQs3geC0RnsXXg%3D%3D.sJ%2Bw2rtgj06dLFes7f8p6HhOa3%2Fp1A9bGXk2EmerAl2Z7o10ieRsn4iQzNBEIYD%2F" rel="nofollow">https://github.com/archguard/archguard</a></p><h2>它能做点什么?</h2><p>ArchGuard 按照流行的 C4 架构模型进行分层化的分析。即在 System Context(上下文), Container(容器), Component(部件), Code(代码)四个不同的架构视图上,它们是不同的抽象级别,对应于不同的受众,如团队内开发人员关心代码内的依赖,架构师关心组件、窗口间的依赖。</p><p><img src="/img/remote/1460000041660344" alt="Home" title="Home"></p><p>而在最后的实现形式上,它们是以代码库和文档的形式存在的。ArchGuard 是基于代码的静态分析工具,未来也将基于设计提供这方面的功能。</p><p>在 ArchGuard 中,我们需要先创建一系列的系统组件,即要配置好对应的语言和 GitHub 地址,就可以对代码进行扫描。</p><h3>组件/模块</h3><p>在组件视图内,我们可以看到单个项目的总体情况,根据对应的代码提交历史,不稳定代码模块:</p><p><img src="/img/remote/1460000041660345" alt="Summary" title="Summary"></p><p>API 声明和使用情况等:</p><p><img src="/img/remote/1460000041660346" alt="API Usage" title="API Usage"></p><p>并通过体量维度、耦合维度、内聚维度、冗余维度、测试维度五大维度对架构进行评估,以及一系列的指标来分析系统的情况:</p><p><img src="/img/remote/1460000041660347" alt="Evolution" title="Evolution"></p><h3>系统依赖分析:服务地图</h3><p><strong>注意</strong>:这种依赖分析方式,依赖于团队开发人员拥有统一的编码规范。</p><p>而针对于微服务来说,ArchGuard 可以自动化地分析不同服务之间的依赖关系,并将这种依赖关系可视化出来:</p><p><img src="/img/remote/1460000041660348" alt="API Analysis" title="API Analysis"></p><p>PS:由于 ArchGuard 过去是微服务架构,合并成单体之后,存在自己调用自己的情况。</p><p>同时,系统能帮你自动分析哪些 API 是使用的,哪些 API 是未被使用的(有些 API 暂时分析不到):</p><p><img src="/img/remote/1460000041660349" alt="未匹配 API" title="未匹配 API"></p><p>当前,ArchGuard 可以支持 Spring、RestTemplate、Axios、UMI-Request 等几种有限的 API 调用识别。</p><h3>数据库依赖分析:数据库地图</h3><p><strong>注意</strong>:这种依赖分析方式,依赖于团队开发人员拥有统一的编码规范。</p><p>针对于数据库间的依赖问题,ArchGuard 可以解析代码中的 SQL 调用,并<strong>尝试性</strong>将这种依赖关系与不同的微服务相匹配,进而分析哪些服务在数据库层是耦合的。由于存在不统一的编码规范,所以有些情况下,我们并没有识别出代码中的数据库表:</p><p><img src="/img/remote/1460000041660350" alt="Database" title="Database"></p><p>通过这种依赖关系,我们可以查看代码中最经常使用的表。再结合我们在代码分析中的功能,就可以查看数据库的调用地图(前端实现中)。</p><h3>代码分析</h3><p>对于开发团队来说,它们可以在 ArchGuard 上查看项目的模块、包、类、方法之间的依赖关系:</p><p><img src="/img/remote/1460000041660351" alt="Code Analysis" title="Code Analysis"></p><p>通过上面的 LoginModuleRepository 就能匹配到数据库对应的结果。</p><h3>变更影响分析(开发中)</h3><p>我们正在实现的一个功能是,通过分析和配置系统潜在的代码修改点,进而通过依赖关系,分析出变更的影响范围。它即能帮助架构师分析需求的影响,又能帮助测试人员更精准地测试系统中的内容。</p><h2>ArchGuard 是如何达成上述功能的?</h2><p>ArchGuard 内置两个代码分析引擎:Bytecode 分析 + 源码分析。</p><ul><li>Bytecode 分析。顾名思义,就是通过分析 JVM 中的字节码,从而分析出代码中的依赖关系。</li><li>源码分析。即通过分析生成编译语言的语法树,产出特定的数据结构。</li></ul><p>源码分析主要是静态分析,结合先前在重构自动化开源组织 <a href="https://link.segmentfault.com/?enc=n74GNYBfevzYqkQ%2FbTAw6w%3D%3D.Lrcq6VDzE7gEcwWvtUUp%2FTGjeA%2Fg3AsQYrVsLR9O%2BEM%3D" rel="nofollow">Modernizing</a> 下开源的 Chapi 代码分析引擎(<a href="https://link.segmentfault.com/?enc=Yem5W8z3j0KO7i14ZxuOKA%3D%3D.B7k7Of4GEIgYWmDRqF52AJ86aRwCbul5Ac0l26JU7kwPbVM1hh%2FhzGgeUrbEyr4M" rel="nofollow">https://github.com/modernizing/chapi</a>)。Chapi 基于 Antlr 实现的语法分析,支持主流的编程语言:TypeScript/JavaScript、Kotlin、Java、C# 等等。如下表所示:</p><table><thead><tr><th>Features/Languages</th><th>Java</th><th>Python</th><th>Go</th><th>Kotlin</th><th>TypeScript</th><th>C</th><th>C#</th><th>Scala</th><th>C++</th></tr></thead><tbody><tr><td>http api decl</td><td>✅</td><td>🆕</td><td>🆕</td><td>✅</td><td>✅</td><td>🆕</td><td>🆕</td><td>🆕</td><td>🆕</td></tr><tr><td>syntax parse</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>🆕</td><td>🆕</td><td>✅</td><td>🆕</td></tr><tr><td>function call</td><td>✅</td><td>🆕</td><td> </td><td>✅</td><td>✅</td><td> </td><td> </td><td> </td><td> </td></tr><tr><td>arch/package</td><td>✅</td><td> </td><td> </td><td>✅</td><td>✅</td><td> </td><td> </td><td>✅</td><td> </td></tr><tr><td>real world validate</td><td>✅</td><td> </td><td> </td><td>✅</td><td>✅</td><td> </td><td> </td><td> </td><td> </td></tr></tbody></table><p>由于是静态代码分析,所以有些内容并不是非常准确。</p><p>再结合 ArchGuard Scanner (<a href="https://link.segmentfault.com/?enc=obPjmpXlQxkUOjgLcouaCg%3D%3D.pCyNOajiGpFoHwCBzvEDaB0jINiqk8zDxpr1i0ymse1J5Rb6nBd8nj1U8vAZRika" rel="nofollow">https://github.com/archguard/scanner</a>)中的几个扫描工具将数据流入数据库中:</p><ul><li>scan_git,分析 Git 提交历史、行数、语言等基础信息</li><li>scan_jacoco,分析代码测试覆盖率</li><li>scan_bytecode,字节码分析</li><li>scan_sourcecode,源码分析(包含 HTTP API 分析、数据库分析)</li><li>scan_test_badsmell,测试代码坏味道</li><li>collector_ci,收集 CI/CD 中的历史记录</li></ul><h2>其它</h2><p>欢迎加入 ArchGuard 的开发中来,GitHub:<a href="https://link.segmentfault.com/?enc=DLiUr2nsObk23lBrHnM%2BbA%3D%3D.OFO2QD36MRrEHjckcrKPEGljTJwCkhQ9LDFO7nZTuGQF644%2F48OfPY0QT9Lgf%2Bue" rel="nofollow">https://github.com/archguard/archguard</a></p>
云研发 IDE Uncode:演示版发布(欢迎加入开发)
https://segmentfault.com/a/1190000040133543
2021-06-07T12:06:45+08:00
2021-06-07T12:06:45+08:00
phodal
https://segmentfault.com/u/phodal
2
<p>花仲马,五一在加班,我便只得找点事情做。</p><p>还记得 Uncode 吗?用于落地我构思的整个云研发体系的 IDE,如果不记得的话,请访问『<a href="https://link.segmentfault.com/?enc=CzFzqKcY7QcZ9C%2F94XLcfw%3D%3D.1N2IeLaaYU%2F%2BGNnoSgjRuDt81DoPGfhTknhiExpb%2FZ%2BLPpVAuFgZAwAoSos2X76Prm%2FeVHss7xxWXZkL%2Bnhg4j6d7EvK9xUQfp%2F7OSIm5o0%3D" rel="nofollow">流程即代码:低代码 & 云研发 IDE —— Uncode</a>』。它来了,现在来了。</p><h2>Uncode 架构</h2><p>我,也许擅长 IDE 的插件开发,还有阅读了大量 Intellij IDEA Community 的源码,但也说不上擅长 IDE 的设计 —— 有太多的坑值得去探。特别是在五一之前,我尝试用 Druid 去构建一个编辑器,便发现这并不是容易的事情。所以,我走回了 WebView + Monaco 的路线,然后用 Rust 作为系统的核心:</p><p><img src="/img/remote/1460000040133545" alt="Uncode Architecture" title="Uncode Architecture"></p><p>上图是当前 Uncode 的架构图:</p><ul><li>基于 Tauri,所以使用的是原生的 WebView 构建。选择 Tauri 的原因是:对 Rust 支持更友好。</li><li>UI。TypeScript + React,选 React 而不是 Angular 的一个原因在于:1. 好久没用 React 了。 2. 外加国内的 Angular 用户变小了。</li><li>App。Tauri 基本上作为是一个 Proxy 在运行 + 系统接口的访问,将核心功能下沉至 uncode。</li><li>Uncode Core。主要工作:1. 提供众多 DSL 的解析和处理。 2. 封装众多工具,以提供分析功能,用于支撑反应反馈。 3. RPC 支持 —— Tauri 太麻烦了。</li></ul><p>或许,你还注意到了 Golang。</p><p>Uncode 使用 Rust 来实现各类的 DSL 的解析,以及各类的代码生成。但是,Rust 还缺少一个重要的工具,Antlr(至少还不稳定)。依旧是那个老问题,Antlr 官方提供了大量的现有编程语言的语法,这个重要的特性由于支持我们做静态分析。所以,我们将使用 Golang 来暂时实现这样的功能。</p><h2>Uncode 的第一个小目标</h2><p>考虑到 Uncode 不是一件容易的事情:</p><ul><li>设计一系列 DSL 来将设计和流程等代码化</li><li>设计一个语言(即 Datum,原来的 Charj)来转换不同的语言</li><li>设计一系列接口来集成其它一系列工具</li><li>……</li></ul><p>所以,Uncode 的第一个目标,相当的简单:可以用 Uncode 来编写 Uncode 的编辑器。</p><p>IDE 的复杂性。变身为一个 IDE 是一件非常复杂的事情。在那之前,需要准备好稳定可靠的 UI 接口 + 良好的交互性。当然了,还有对于诸多工具的集成。</p><h2>Uncode 演示版</h2><p>所以,在五天里(加上 4.30 号),我开发了演示版本的 Uncode,除了不能写代码,它似乎什么也能做。</p><h3>需求即代码</h3><p>需求代码化,即将软件开发需求抽象为特定的领域语言,并使用管理代码一样的方式来管理需求,追踪需求的变化 。同时,为通过新的 API 来对接版本管理系统,以可视化需求,演变为看板代码化。</p><p>『<a href="https://link.segmentfault.com/?enc=UC9zjuF2R8Pnt0GekgrmVg%3D%3D.2KxLtB%2FJlLZAcCXt5Eab0RQ1EgUdlSELUa4mb78fr2Z64mUtL8xIVt3DQy3MW8enQRDF%2Fv3bVSKPWCCKEFfVWw%3D%3D" rel="nofollow">需求即代码</a>』作为 Uncode 的第一个部分,在演示版里,我实现得比较简单,读取代码库里的 story,解析其中 <code>cucmber</code> 的语法,然后展示到 Uncode 里。对应的 DSL 也还在早期(因为还得改 Cucumber 对应语法的解析器)。</p><pre><code class="cucumber"># status: backlog
Feature: 架构守护 DSL 设计
作为一个架构师,我希望在设计通过类似于 ArchUnit 这样的 DSL 来设定调用规则等。
Scenario: 开发人员违反规则时,无法提交代码
# Enter steps here</code></pre><p>由于,使用的 Kanban 库不是很完善,所以并没有创建对应的编辑功能。对应的功能截图如下:</p><h3>设计即代码</h3><p>设计即代码就稍微复杂一些,分为这么四部分:</p><ul><li>架构描述语言。此处是有一个 DSL,只是我还在设计中。</li><li>Modeling。说是建模,但是也说不上,模型的双向绑定。即扫描代码,生成 UML,然后展示;修改 UML 的话,会修改对应的代码。(前半部分支持,后半部分暂不支持)。</li><li>架构适应度函数。第一个功能是架构守护,即基于 Arch Unit 的 DSL 抽象。其它的暂时懒得写了。</li><li>UI Design。即我在去年设计的:<a href="https://link.segmentfault.com/?enc=5D%2FlHr0Vvous4p14CPcczg%3D%3D.79eaP%2FnOcdyo0ioLh7oGYd%2F7%2Ba6sIfKQaixFy4RcQ%2FFEU2QMIpTEqAqtLOKchsDB" rel="nofollow">https://github.com/phodal/design</a> ,可以实现简单的 DSL 转换为设计图,以及 DSL 转换为 UI 组件。</li></ul><p>UI 依旧很丑。</p><h3>代码的代码化</h3><p>我承认,这就是一个带目录功能的编辑器。哦,不,暂时还支持保存文件,但是基本可用。</p><h3>其它</h3><p>Todoing。</p><p>就这么五天,你还想要什么自行车。</p><h2>小结</h2><p>GitHub 下载预览版:<a href="https://link.segmentfault.com/?enc=OxQv3JJM4vI15MFYBkyWIA%3D%3D.AsBrt6oLy4OpOPqZT30eSlK%2B%2BTvT3%2BMkG7MjYlj3CDzxIR49FvN65VKLTVUEJc1N" rel="nofollow">https://github.com/inherd/unc...</a></p><p>如果你擅长又或者是对以下的内容感兴趣:</p><ul><li>React</li><li>Monaco Editor</li><li>Rust</li><li>Tauri</li><li>对 UI 有美感</li></ul><p>人生苦短,欢迎加入 Uncode 的开发:<a href="https://link.segmentfault.com/?enc=U%2FMFVFeBJJwZ80DQ%2BrMzVQ%3D%3D.rTXZv7FP9L8lCN3GxNBBB2CSDtaisVhxVvvE9g7dsWD7kmfzFamPWkFl%2BmpHAEfg" rel="nofollow">https://github.com/inherd/uncode</a></p>
数字技术战略:开发者体验 —— 内部工具的“最后一公里”
https://segmentfault.com/a/1190000039694944
2021-03-23T14:36:37+08:00
2021-03-23T14:36:37+08:00
phodal
https://segmentfault.com/u/phodal
1
<p>这是一篇迟来的文章,我本应该在很早之前写完,但是一直都发现时机不够成熟。去年,在经历了多个低代码前端项目的售前,以及一个低代码项目的技术实践强化,国内的 IT 企业缺乏对于『开发者体验』缺乏系统性的思考。</p><p>来,先我们来看一个开发者体验(DX,Developer Experience)的定义:</p><blockquote>开发者体验,与用户体验类似,只是对象是软件开发人员。将之与国际进行对应,便是开发人员对于针对使用或期望使用的产品、系统或者服务的认知印象和回应。有所不同的是,用户关注的内容变为库,SDK,文档,框架,开源解决方案,通用工具,API 等的开发人员的体验。</blockquote><p>而完善开发者体验是有一些基础条件的:</p><ul><li>稳定性。SDK、库、框架等具备满足使用方持续可用的状态。</li><li>功能完备。可以满足大部分的正常的功能需求。</li></ul><p>简单来说,就是只有在可用的情况下,设计开发者体验会更加有价值,即开发者体验是一个加分项。而如果我们能在开发的前期就考虑用户体验的话,它会为后续的开发带来便利。</p><h2>开发者在体验什么?</h2><p>什么是开发者体验?那不就是让开发人员觉得爽吗。</p><p>什么才叫爽呢?来一起看几个例子。</p><h3>安装谁更简单?</h3><p>先让我们来看一个软件安装的例子:</p><ol><li>一键命令行安装。如 <code>curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh</code></li><li>按步就班下载和使用。如打开官网,找到对应的平台(操作系统)下载可执行文件,打开安装软件,执行安装步骤,一步步进行。</li></ol><p>这是 Rust 官方提供的两种不同的 Rust 安装方式。</p><h3>谁的引入更简单?</h3><p>再看个使用软件的例子:</p><ol><li>README 即起步文档,添加依赖,复制示例代码。</li><li>README 里没有起步,需要跳转到文档网站,添加依赖,添加构建脚本,复制示例代码。</li></ol><p>两者都是 Rust 里的语法解析器,前者是拥有 2.2k stars 的 pest,后者则是拥有 1.7k stars 的 lalrpop。</p><p>这里,先不讨论两个不同的库在开发上的区别,从直观感受来说,我们就可以直接区别。</p><h3>起步谁更快?</h3><p>来两看两个上手指南</p><ol><li>学习新的语言,学习新的语法,入门文档很长(start),还需要编译,文档不易访问。</li><li>通过 script 标签引入 ,直接开始编写代码。</li></ol><p>你觉得新手会选择 Vue 还是 Angular?</p><h2>开发者体验</h2><p>这样一来,我们对于开发者体验都能有一个简单的体会。也就能理解什么是开发者体验了。</p><h3>什么是开发者体验?</h3><p>开发者体验是指开发人员在使用软件、工具、代码等的体验,它与用户体验类似,只是对象是软件开发人员。将之与国际进行对应,便是开发人员对于针对使用或期望使用的产品、系统或者服务的认知印象和回应。有所不同的是,用户关注的内容变为库,SDK,文档,框架,开源解决方案,通用工具,API 等的开发人员的体验。 </p><p>国内开始关注开发者体验主要有以下的一些原因:</p><ul><li>外部开源。即越来越多的开发人员开源了自己的软件,迫切地需要提升软件的体验。</li><li>内部开源。在内部通过开源来进行部门之间的协同效应。</li><li>内部基础设施共享。让内部的开发人员使用内部的基础设施时,获得比外部开源软件更好的体验,提升其在内部的口碑。</li><li>API 经济。对外提供 API,需要与更多的业内组织竞争。</li><li>云原生。云厂商面向开发者提供服务。</li><li>生态构建。面向合作伙伴需要提供更便捷的接入服务。</li><li>SDK 等工具提供方。</li><li>……</li></ul><p>原因多种多样,但是其核心都是想通过开发者体验来提升竞争力。</p><h2>开发者体验六要素</h2><p>再次强调一下:关注开发者体验之前,应该确保核心功能:完善 + 稳定。即你需要提供可用、稳定的特性,再去提升总体的用户体验。除非,对于你的系统来说,你在一开始就不缺用户。</p><p>从我在开发社区的使用经验、网上了解的相关信息以及与一些专业人士的沟通中,我认为以下几点是进行 DX 时要考虑的要素:</p><ul><li>错误呈现。即出错时,以何种方式来呈现。</li><li>文档体验。</li><li>易用性。如何简化开发。</li><li>交互式。降开发者学习成本。</li><li>触点。让更多的人知道这个软件。</li><li>支持。</li></ul><p>太长不看版:</p><table><thead><tr><th>错误呈现</th><th>文档体验</th><th>易用性</th><th>交互式</th><th>触点</th><th>支持</th></tr></thead><tbody><tr><td>错误描述</td><td>开发者门户</td><td>一键式安装</td><td>低配置/零配置</td><td>文章</td><td>问题反馈渠道</td></tr><tr><td>报错即文档</td><td>发布日志</td><td>自动化版本迁移工具</td><td>声明式使用</td><td>演讲/分享</td><td>问题响应时间</td></tr><tr><td>报错即修改建议</td><td>代码生成文档</td><td>自助式搭建</td><td>可交互文档</td><td>Hackathon</td><td>开发者即服务</td></tr><tr><td> </td><td>版本迁移指南</td><td> </td><td>沙盒及产品环境</td><td> </td><td>开发者社区</td></tr></tbody></table><p>可能还有其它内容,如果有的话,欢迎与我探讨。</p><h3>错误呈现</h3><p>PS:出于安全原因,有些内容不适合在外部暴露,因此并不建议所有的东西都应该对外呈现。</p><p>对于开发者体验来说,错误呈现就是让开发者有办法快速定位问题和修改问题。常见的一些可优化的部分是:</p><ul><li>错误描述。即软件的出错以合理的方式描述出来,可能是一段文字,一个错误码等。</li><li>报错即网站。即复杂的出错场景里,可以通过可访问的链接来告诉开发者如何修改问题。</li><li>报错即修改建议。即出错时,告诉开发人员可以尝试以下的方式来修改问题。</li></ul><p>这里,我们可以看一个 Rust 出错的示例:</p><pre><code class="bash">error[E0425]: cannot find value `RE_ACCESS` in this scope
...
177 | if let Some(capts) = RE_ACCESS.captures(line) {
| ^^^^^^^^^ help: a static with a similar name exists: `RE_CLASS`</code></pre><p>虽然,这个提醒不一定那么智能。但是,它在帮助定位问题的同时,还在一定程度上来解决一点问题。</p><h3>文档体验</h3><p>文档是一种传统知识的载体。优秀的程序员即能通过代码来传递知识,还会通过文档来表达自己。</p><blockquote>软件编程即理论建立与传递。 —— Peter Naur</blockquote><p>对于文档来说,依旧的,我们还会关注于:</p><ul><li>开发者门户。作为整个知识体系的承载</li><li>发布日志/更新日志。即 CHANGELOG.md</li><li>代码生成文档。</li><li>版本迁移指南。针对于出现 Breaking Change 的场景,需要详细给出每一部分的迁移示例。</li><li>测试用例。在开源世界里,除了文档,还有测试用例可以读懂一些特别的用法。</li></ul><p>开发者门户是一个复杂的话题,后面我们会再介绍。如下是 D3.js 6.0 的 <a href="https://link.segmentfault.com/?enc=Z3Dc%2BMBQDNWHMlPfGPCxYQ%3D%3D.xgamvPdKgcU47Za45rG7SyUNkt0s5Cxb4Xld9%2FwqWMZAQDJajmMsKohS5jOaPu7U%2BBCz3HDeGdi0jFztvQeapg%3D%3D" rel="nofollow">migration guide</a> 示例:</p><pre><code>For example,
selection.on("mousemove", function(d) {
… do something with d3.event and d…
})
becomes:
selection.on("mousemove", function(event, d) {
… do something with event and d …
})</code></pre><p>我们还会在易用性里提供更好的方式。</p><h3>易用性</h3><p>易用性本身是非常难度量的,</p><ul><li>一键式安装。可以是一行命令,也可以是 <code>1-click</code> 之类的。</li><li>自动化版本迁移工具。即针对于版本升级时的 API 修改,可以提供自动修改工具,典型的如 Angular CLI 来升级 Angular。</li><li>自助式搭建。如 Spring Initializr,可以让开发人员选择合适的服务和工具,而后生成项目等。</li></ul><p>在高中时期,我尝试了市面上的一个又一个 GNU/Linux 改行版。如 Ubuntu 和 OpenSuSE 会向我们寄一些安装 CD/DVD,用来帮助新手快速安装 GNU/Linux 操作系统。在我体验了 OpenSuSE 之后,我被它文档上的 <code>Install software via 1-click</code> 所惊艳(当时年轻)。</p><p>如我们找到了中文输入法:Fcitx (<a href="https://link.segmentfault.com/?enc=s%2B9IfdNMwHX8g%2B8aH33BSQ%3D%3D.NC7Qx9HbTjKtPD2ERRg1kx4eI4WkBsvAccyPqPSzhiQ%3D" rel="nofollow">https://zh.opensuse.org/Fcitx</a>),文档上包含了各种的介绍、如何安装,以及一键安装 —— 现今的 macOS 和 Windows 似乎也没有这样的功能。</p><h3>交互式</h3><p>在各类 PAAS 服务流行的今天,交互式的 API 体验已经成为了一个主流的方式。进细一步地,我们可以</p><ul><li>低配置/零配置。人们为了灵活性而引入的各种配置本身是反人性的,大部分的配置应该是内置的,不应该由普通的开发者来配置。</li><li>声明式使用。即 API 应该尽可能简化,只需要简单的声明即可使用。</li><li>可交互文档。如 Swagger 可以在线尝试 API。</li><li>沙盒及产品环境。即提供一个在线的类可编程环境。</li></ul><p>最典型的一些例子就是现代化的编程语言里提供的 Playground,如 Golang 和 Rust 都提供了 Playground。它可以让开发人员查看文档,同时运行应用的代码,还能修改代码并运行。</p><h3>触点</h3><p>触点是指如何去提供加深与开发者的关系。虽然有一个更专业的名称是『开发者关系』,但是它有一个更复杂的模型。这里就简化为触点,意思就是如何与开发者进行接触的点:</p><ul><li>内容/文章</li><li>演讲/分享</li><li>Hackathon/黑客松</li><li>论坛。只在形成一定规模时才考虑</li></ul><p>从某种意义上来说,它是叫推广,但是呢,从个人的角度来看,触点这个用法比推广好得多 —— 触点可以让你意识到:现有的机制是不是无法连接到更多的开发者。</p><h3>支持</h3><p>这部分就是对于开发人员的支持了,每个人也都非常熟悉:</p><ul><li>问题反馈渠道</li><li>问题反馈与响应</li><li>开发者即服务。源自我之前的一篇文章《<a href="https://link.segmentfault.com/?enc=lC3itBTRDtiYtkY7LpNCWQ%3D%3D.iT5unbMXrAvOgOFUBQmNL9ddGixWNv4oUSonGwNhjbE4oPEzAacp6vIF%2F7IZOSVUP8E6fPi7gI4XM%2B6hNy5IYw%3D%3D" rel="nofollow">开发者即服务</a>》,意指开发者一对于指导问题。</li><li>开发者社区。</li></ul><p>再说说开发者即服务,是(Developer-as-a-Service)的,亦可称为 “按需即用的开发者”。即当开发者使用某一工具、库,遇到任何相关的问题,可以随时找开发者为我们提供服务。哪怕是使用者使用了我们的 A 框架,但是遇到 B 框架有问题,他/她们也会觉得 A 框架有问题 ——因为 A 框架的开发者们是一种服务,一种开箱即用的服务。</p><h2>度量开发者体验</h2><p>考虑到度量开发者体验是一个复杂的问题,这里只简单列一下我所认为的两个易于度量维度:</p><ul><li>首次运行所需时间</li><li>文档触达速度</li></ul><p>其它的则是<strong>常规度量指标</strong>,以及对于开发者门户的度量。</p><h3>首次运行所需时间</h3><blockquote>首次运行所需时间即开发人员从接触到创建第一个可运行的应用或者测试等所需的时间。</blockquote><p>它可以让我们关注于:</p><ul><li>设计最小的使用步骤</li><li>提供更好的 Get Started 设计</li><li>提供有限次数限制的 Demo Token。</li><li>更快的体验速度。对于下载服务,采用 CDN,适用于国内网络的下载机制。</li><li>……</li></ul><p>这个指标体系可以帮助我们,理解开发人员在过程中遇到的过程痛苦。</p><h3>文档触达速度</h3><blockquote>文档触达速度,即从修改完文档到所有开发人员可见所需的时间。</blockquote><p>我们最常见的一个例子是 GitHub Pages,当我们更新完文档时,它可以实现分钟级的部署。这个维度的指标的目的主要是:</p><ul><li>有意识地加快文档更新过程</li><li>让经常出错的问题可以快速更新</li><li>提醒开发人员修复了哪些问题</li></ul><p>它可以让问题的反馈快起来。</p><h3>常规度量指标</h3><p>接下来,就是我们常见的一些指标,受限于框架和 SDK 等的不同会有些变化 ,典型的如:</p><ul><li>API 响应时间</li><li>API 出错率</li><li>API(可选),『每周活跃调用者数』、『API 响应时长』</li></ul><p>对于开发人员,可以展示 <strong>API 情况的 dashboard</strong>,用于展示 API 服务的当前状况,如 GitHub Status。这样一来,就不需要再回答 API 是否挂了的问题。</p><h3>开发者门户成熟度模型</h3><p>在编写这篇文章的过程中,刚好看到了一篇对于门户的度量模型,《<a href="https://link.segmentfault.com/?enc=nFzQy71xcF9aSwXajZL6bg%3D%3D.janNK98PrNioLncEWR2Ko8GTmn2CCPkv4s%2FZeVwUweAJuWRk57OaQD3qVIlxXaXF5ceb9QfVDDmsSPie2eqnGQ%3D%3D" rel="nofollow">How Mature Are You? A Developer Experience API Maturity Model</a>》简单地翻译了一些<strong>国内适用</strong>的部分(详细见原文):</p><table><thead><tr><th>Level 1</th><th>Level 2</th><th>Level 3</th><th>Level 4</th></tr></thead><tbody><tr><td>封闭的系统</td><td>门户是自服务的,但是不连贯的</td><td>完全自服务</td><td>可个性化的统一门户</td></tr><tr><td>文档缺乏成功调用 API 的信息</td><td>1 天内可以调用 API</td><td>10 分钟内可以调用 API</td><td>分钟级 API 调用</td></tr><tr><td>API 和功能没有正确对应</td><td>快速开始指南、修改日志和教程</td><td>提供沙盒和生产环境</td><td>认证流程</td></tr><tr><td>响应问题需要一周的时间</td><td>交互式文档</td><td>代码示例和库</td><td> </td></tr><tr><td> </td><td>问题在 2 ~ 3 天内被回答</td><td>24 小时回答问题</td><td> </td></tr></tbody></table><h2>如何提升开发者体验</h2><p>从个人的角度来看,提升开发者体验是一个相对麻烦的过程。除了上述的两个指标之外,我觉得还有两种方式可以帮助提升开发者体验:</p><ul><li>竞品对比。</li><li>新用户引导流程。</li></ul><p>嗯,基本上和用户体验是类似的。</p><h3>竞品对比:看齐</h3><p>竞品对比,主要是通过与类似产品的对比,让自己与业内保持一致的水准。</p><p>如下是 Rust 和 Golang 的对比(只选取部分,出自于《<a href="https://link.segmentfault.com/?enc=jRrD6nzj%2FGe9x2wLGz6yng%3D%3D.oeu%2FEOENgOi6F96KHTnBpPGmAUFOeEOuC%2Bo33QrUsh014auMhdLeCUYXA9aUXm1cBjWidfRfxMQ6lPyM2S5N4MMMNgOom1jbwkiVjAgHulZbrZoBHsooZ1m9QsZumMgm" rel="nofollow">Android Go vs. Rust: Features, Similarities & Differences</a>》)</p><table><thead><tr><th> </th><th>Rust</th><th>Golang</th></tr></thead><tbody><tr><td>性能</td><td>高效能,比Swift语言快一点</td><td>GO和Java的性能不及Rust</td></tr><tr><td>方便性</td><td>零成本运行时抽象,非常容易且安全地用于内存等处理</td><td>使用和管理容易</td></tr><tr><td>易于学习</td><td>需要花时间来学习和掌握用于内存管理的语言抽象</td><td>有完整的开发文档,大量的用户社区</td></tr><tr><td>发展速度</td><td>与Go程序相比,RUST的编译时间更长</td><td>既简单又快速</td></tr><tr><td>并发与并行</td><td>RUST没有具体的并发或异步操作。</td><td>Go 具有例程(轻量级线程)和通道(go例程的通信机制),可简化应用程序的创建过程。具有本机测试机制,可在运行时发出警告。</td></tr></tbody></table><p>通过这样的对比,从其它产品学习。</p><h3>新用户引导流程</h3><p>站在开发人员的角度出发,梳理新用户从使用过程中的痛点问题。一个详细的例子可以见:<a href="https://link.segmentfault.com/?enc=O9SP5v8uqDZ5s5%2BGRbHazA%3D%3D.RaJ2z64Rg8CvQxR4vTUdicM23BMYBPa7fCkAo4QlHbvxZUylMwoYnyIj1aA19VIR" rel="nofollow">Onboarding journey</a></p><h2>其它</h2><p>PS:一不小心就把文章写长了……。</p><p>欢迎在评论区进行交流</p><h3>开发者关系与布道师</h3><p>这是两个岗位或者角色:</p><ul><li>开发者关系,主要是要与身为开发者的用户们建立起良好的关系。</li><li>布道师, 将自己热爱与信仰的技术,持续不辍地传递。</li></ul><p>对于以 SDK、云服务等为主的公司而言,他们都会通过这与之相似的岗位来建立与开发者的联系,从而提升开发者体验。</p><h3>相关资料</h3><ul><li>《<a href="https://link.segmentfault.com/?enc=vgpasCg7fKpGyXzPjZEH%2Bw%3D%3D.eY7aHmUWxxcGxnVNy6ga%2BGKTzO%2BwbC357WBzHUMTrmzdTWSjechjZKf9SmDkuQOF" rel="nofollow">Understanding a Path</a>》</li><li>《<a href="https://link.segmentfault.com/?enc=STegbYV9CAj%2FV%2FxoZNKYRg%3D%3D.j5MlfaXepVTomOHWWAGDNt0xqgK5veszZ372LKX2aGegLmqLQl6k0LsNCDU0CSUjzEP9otTs5lWaNo%2B1v9vhSQ%3D%3D" rel="nofollow">APIMatic</a>》 构建 Developer Experience Portal SAAS 服务</li><li>《<a href="https://link.segmentfault.com/?enc=XNYyt5bKuyHb6JSN2HwXfw%3D%3D.AiKJNN0Y1mUMoHAszml0qxsb%2FHO0klQmivncgNnus8qtI8MkeRZGCgwXXb%2Fpg6fz" rel="nofollow">Backstage</a>》 开源 Developer Portal 构建工具</li><li>《活文档:与代码共同演进》如何让文档活起来</li><li>《<a href="https://link.segmentfault.com/?enc=p%2FDh4GTnK%2Bhzrt8euof54Q%3D%3D.NNh8hr8f%2FLEsI%2FQrfU5cNuZW3i%2F3rZkGnG5%2BVMTym%2Bxua%2BtahLWsHY72Nzg8zZgt2ucHntfPZnUtg7j6Xcr9og%3D%3D" rel="nofollow">How Mature Are You? A Developer Experience API Maturity Model</a>》</li></ul>
2020 结点:平凡 & 重新出发
https://segmentfault.com/a/1190000039059112
2021-01-22T11:48:30+08:00
2021-01-22T11:48:30+08:00
phodal
https://segmentfault.com/u/phodal
3
<p>2020 年,庚子年,注定是不平凡的一年,所以就平凡的过去了。年初,疫情让我在家办公了几个月,年中开始了忙碌的几个月,年底又归于平凡。也因为疫情,多了一些 beach 的时间,不得不休完 20 天的看似,还有没机会用上的婚假,所以我有机会尝试一些新的想法。</p><p>太长不读版:</p><ul><li>编程上,回到底层/系统编程,构建基础设施开发能力。</li><li>写作上,在 Ledge 项目上结合前端可视化,展示了知识管理的另一种可能性。</li><li>设计上,依旧还在一天一张画的练习上,暂时没有新的突破。</li><li>方法化上,丰富和完善了 DevOps/系统重构相关等知识体系。</li><li>影响力上,靠影响力带来了几个公司的项目,除此没有进展。</li></ul><p>好像也没了。再对比一下上一年的目标:</p><ol><li>工具,有了更多编程语言、软件工程相关的工具。</li><li>DSL 抽象,设计的 DSL 主要集中在 Charj 相关的项目上,缺少对业务的抽象。</li><li>国际化,几乎没有任务长进。相反的,在做本土化的各种实施。</li><li>婚礼,被迫放在 2021 年了。</li></ol><p>嗯,大部分都没有实现,反正计划就是计划嘛 :) 。</p><h2>编程</h2><p>综合疫情带来的 beach 时间,加上外地出差,额外获得了很多的编码时间。</p><h3>项目相关</h3><p>这一年的项目多少是有些无聊,设计一些方案,指导实施方案的落地,再做一些度量。</p><p>参与了某国产操作系统的 IDE,深入了解 Android Studio 和 Intellij Community 背后相关的知识、各类实践。真正意义上,掌握了编程语言端到端的实践 —— 从开发、构建、优化,再到执行等一系列的过程。源码阅读上,包含但是不限于 Gradle、Proguard、R8/D8、JVM、Intellij Community 等。</p><h3>底层编程 + Rust</h3><p>在那了篇《<a href="https://link.segmentfault.com/?enc=D4K7D2MPS6Gppy1Q0VKwnA%3D%3D.6yAfk1XaZ95brHr0MYXejghjV4SfEKMjIGkG0HIzVM33FmcbYaWzRtcSoEUBT6k2nbDH3csyZRBTi928BhMERA%3D%3D" rel="nofollow">六年之后:回到底层编程</a>》里,我开始了底层编程之旅。</p><ol><li>Electron + Rust 设计 RPC 架构下的客户端:<a href="https://link.segmentfault.com/?enc=MA3Xtu97iAwUEwN6E7dX9g%3D%3D.MsfU8CVr6af3%2F5PqxeThMy6H8Y5buAOwW59zMl6AwkmTYbeQi5Ylkcr%2Fd5pXqyRe" rel="nofollow">Stadal</a></li><li>可执行的 markdown 工具 exemd (支持依赖):<a href="https://link.segmentfault.com/?enc=XvfZ6PL6gcmpOQSbg8uadA%3D%3D.YwJen%2FfGO7Cz4QrV9mt1lV9bng%2F%2BgTcdu%2B363z2gaDVKEw%2FQkaMm4WyhPchSHmo4" rel="nofollow">exemd</a></li><li>Scie 代码识别引擎:<a href="https://link.segmentfault.com/?enc=%2FUI9IbC0dHWEwxFvCFLIXg%3D%3D.xVRzeFgukHbD5flbNk8Qh7p7tNESDHgeN2NQ%2B%2BwJsUtwPCDaMhybouXw5aFP8Hxc" rel="nofollow">Scie</a></li><li>……</li></ol><p>不过,就目前的情况来看,道路依旧还有点长,需要重新掌握的知识有很多 —— 毕竟以前看会的,和现在真正动手的是两码事。</p><h3>重构工具</h3><p>在这一年里,与工作相关的一部分话题依然是重构。所以,也利用了大量的业余时间。</p><ul><li>更完善的分析工具:<a href="https://link.segmentfault.com/?enc=CUwoFP5NruV8NXuVMUMlCA%3D%3D.a%2FUWqwokv%2BGWhzN88OIBkTEAZfYLyOCugAWD7xQELP0%3D" rel="nofollow">Coca</a></li><li>多语言分析工具:<a href="https://link.segmentfault.com/?enc=ekXXlySFjRUPT58eJYXHMA%3D%3D.xdAvu2vd2%2BuBf65HgnTjWgNDUp4sBCpMUQz%2FOFchWEo%3D" rel="nofollow">Chapi</a></li><li>Ant 转 Maven 工具:<a href="https://link.segmentfault.com/?enc=FHoSBtkMpfPRWnSrfbswlQ%3D%3D.5%2FLy%2Baa45mj0Kf9EXsnhGuHPLivefh2%2FKYOQhqMTQg0%3D" rel="nofollow">Merry</a></li><li>和同事搞的 CSS 重构工具:<a href="https://link.segmentfault.com/?enc=1IwOB3epo8OOHTtbGGPlhw%3D%3D.Y%2FFGA%2Fv%2BD2Ju0rPv3OrS5sARbsyN85SVepogl1JWHnI%3D" rel="nofollow">Lemonj</a></li></ul><p>有意思的是,这几个项目的技术栈是:Go + Antlr、Kolint + Antlr、Go + Antlr、TypeScript + Antlr ……。嗯 ,真的是只要涉及编程语言相关、DSL 相关,Antlr 就是一个非常不错的工具。</p><h3>DSL 与 Charj</h3><p>快到年底的时候,和我同事一起开启了 Charj 语言的坑,也是为自己的未来找一些有意思的事情干。我们日常做项目的时候,最难的就是启动一个项目 —— 要搭建架子,相当于设计架构。所以,在这一年里,努力地把整个架子搭建了起来。</p><ul><li><a href="https://link.segmentfault.com/?enc=UOyh3W0bWAYtJGoISKaWWg%3D%3D.bf1Md2Iz2%2FQNzr4%2F3B8vrnQFicr0YMkYQZdbH6MKnLZb8USKwnFv%2FMh3p8UCNWXq" rel="nofollow">Charj</a> 语言工程</li><li><a href="https://link.segmentfault.com/?enc=uVKC111JcE8HhCEKTPyp0A%3D%3D.tZh0k%2F2Whncq5UKjgsgJVJVaYofN1MvqE0aTNHdQksNOZyj4t2NkU7c0eEhXwj%2Bh" rel="nofollow">Movable</a> 语言转换器</li><li><a href="https://link.segmentfault.com/?enc=SmGLC4mvQeLvJzJy0ua01A%3D%3D.stpx11pp5P%2FEom9WuVb3atqY%2Fbj5vLbWovOelnfKfBKC8UyPYf%2B0d%2FFnlvQ20Dr%2ByGmDe%2FtU45bbP9XhloE72w%3D%3D" rel="nofollow">Typography</a> 通用解析器</li><li><a href="https://link.segmentfault.com/?enc=8d%2FExKoz5RLVNRwgbN1xJA%3D%3D.2UmpucDDnkrbzPuGrFonI6CVNNonLELACfTFG0Z%2BXRGssCo1IN6cNEOhTkMAFR0y" rel="nofollow">intellij-charj</a> IDEA 支持</li></ul><p>这一个也作为了下一年,或者是未来几年的的一个方向。(PS:有兴趣的话,欢迎入坑,微信:phodal02 (注明来意))</p><h2>写作</h2><p>写作最重要的是,构建成了一个完整的体系。虽然我平时写的文章多,看上去没有体系,但是还是有一些基本的体系的 —— 也就是围绕着我要去做的东西。</p><h3>万物代码化</h3><p>关于这部分内容的总体思路:《<a href="https://link.segmentfault.com/?enc=h%2B3BmguQG%2BIs1V71gSGKAg%3D%3D.ZImV%2BQE%2BXs6GArKUpauY3tWVe0NsZIFsIFMEHmiu1Nnr%2BZOw%2BAcnu7eLJ6SazuwV" rel="nofollow">万物代码化:从低代码、云开发到云研发</a>》,这部分的各部分文章见:</p><ul><li>《<a href="https://link.segmentfault.com/?enc=P4F8D%2Bj4I0kn2n540MLqFw%3D%3D.zyEOgDmeEkt0GZVPTGn0A3IfeeGnMCEdncGwkqeOYwmtDer7Elerrk8hV%2FJOfu8%2FksqzllljU4ph6p7Mjehcmw%3D%3D" rel="nofollow">需求代码化</a>》</li><li>《<a href="https://link.segmentfault.com/?enc=rpYRhcaRxjSgKh5JfTtm7Q%3D%3D.LFGs42te726lMML7rwdOPqSj8Vesofb8jHrZplc5%2BEUcRopM%2FmyrCTkg94%2Bacj9y" rel="nofollow">文档代码化</a>》</li><li>《<a href="https://link.segmentfault.com/?enc=asiT%2Bu8ZdjlX8iKnwx8BnA%3D%3D.7ruQbE%2FeOaOxeIhwc%2FlQawNUlEo6wyfGA1DpNSLdfUkCDxkN6HLuJHdYuhpkvaSG" rel="nofollow">代码的代码化语言</a>》</li><li>《<a href="https://link.segmentfault.com/?enc=soocbCRJS7AjV8S49wcVXg%3D%3D.Fj5FAnGrkT9L1EakzIlkfUiVdUAslGsmrp0B5LeqF6Gx3sktnueYtxJpkfhsjKqM" rel="nofollow">如何为代码建模?</a>》</li><li>《<a href="https://link.segmentfault.com/?enc=xckWziL3trYDlbqwJp2V2A%3D%3D.B908CrkcfKe0PTJRAgkVwg4LBWaNQhbwK4CElXoP1nfkOHNV5jNljIp%2B5qT7%2FK%2BIdb0GHj4kTFFBdcjZDQbPdQ%3D%3D" rel="nofollow">代码生成模式:未来我们会怎样写代码?</a>》</li></ul><p>完整内容见:<a href="https://link.segmentfault.com/?enc=vo1C1ovsMfE1mYv1hME6cA%3D%3D.rxz7EK6QuRkwHYNd1sD33NaXauR4EDv%2BUuRlV87YhW%2FdzhfYBQsyH%2BFUkBYjKWB3" rel="nofollow">https://github.com/phodal/asc...</a></p><h3>知识体系构建</h3><p>工作时间越长,越发现知识体系的重要性。哪怕是写了一系列的文章,查阅的时候,也算是过于分散了。在这一年里,主要梳理了这两部分的知识体系:</p><ul><li>《遗留系统重构指南》:<a href="https://link.segmentfault.com/?enc=pmVxMSyDEBFdvrkT9pNmPA%3D%3D.G%2BC2zsz6ZzsnNZeAjvLinx4s5Q4%2F1av85AFO4jltn8BHusDq663R9GLjUQ4zKPcl" rel="nofollow">https://github.com/phodal/mig...</a> 。 手把手教你分析、评估现有系统、制定重构策略、探索可行重构方案、搭建测试防护网、进行系统架构重构、服务架构重构、模块重构、代码重构、数据库重构、重构后的架构守护。我原以为这是一个很小众的领域,没想到年底的时候一看,GitHub 上有 2k 的 star。</li><li>DevOps 知识体系:<a href="https://link.segmentfault.com/?enc=hUEmc1AvIQTgaVN2LdPQzw%3D%3D.GSS3j2FIkwTUhtGcQRsdafnkJCx2EOT%2FjRbYUwsfgIY%3D" rel="nofollow">https://github.com/phodal/ledge</a> 。基于在 ThoughtWorks 进行的一系列 DevOps 实践、敏捷实践、软件开发与测试、精益实践提炼出来的知识体系。它包含了各种最佳实践、操作手册、原则与模式、度量、工具,用于帮助您的企业在数字化时代更好地前进,还有 DevOps 转型。 反而是我看好的这个项目,GitHub 上的 star 只有 1.3k 。</li></ul><p>接下来要做的事情就是,在适当的时候构建下一个知识体系。</p><h3>其它</h3><p>其它多数为一些总结,可以在未来用到。又或者是诸如『编程语言开发』这一个还不成统的话题。</p><h2>设计</h2><p>没有特别突出,依旧是画画。</p><p>不过,画得似乎越来越普通了?</p><p>唯一发生的变化是,我换了新的产生力(爱-奇-艺)工具:iPad Pro 11 + Apple Pencil 2。</p><h2>其它</h2><p>我一直有一个想法是:建设一个开源梯队。不过按国内的加班情况来看,这种可能性并不是很大。只能试着围绕 Charj 来构建开源社区了。</p><h2>Helo, 2021</h2><p>简单,然后专注,这就够了。</p><blockquote>本文参与了 <a href="https://segmentfault.com/a/1190000038755701">SegmentFault 思否征文「2020 总结」</a>,欢迎正在阅读的你也加入。</blockquote>
Charj —— 代码的代码化语言
https://segmentfault.com/a/1190000038262782
2020-11-23T22:32:04+08:00
2020-11-23T22:32:04+08:00
phodal
https://segmentfault.com/u/phodal
3
<p>去年,和公司的大佬讨论了一系列关于代码的代码化,还记录了一些笔记。在那之后,我开始了各种尝试:如何将代码转变化代码。原先有一些思路,而后过了一年之后,慢慢地练习,又有了一些新的收获。</p><p>我们想要做的事情是:把<strong>任意的</strong> A 语言转换为<strong>任意的</strong> B 语言(PS:这里的任意 A 和任意 B 语言都是主流语言)。如此一来,我们便可以:</p><ol><li>快速重写任何的系统。</li><li>与编程语言无关的领域建模。</li><li>产生一个更强大的 DSL。</li><li>创建新的语言。</li></ol><h2>引子 0:统一语言模型</h2><blockquote>统一语言模型,即对不同的比编程语言进行抽象,使用同一套数据结构描述编程语言。</blockquote><p>在我使用了 Golang + Antlr 实现了 Coca 之后,我意识到这是一条可行的方案。但是,由于 Coca 的架构和用途所限,外加之 Antlr 对于 Java 的支持远比 Go 要好,我并没有继续在 Coca 上实施这个方案。</p><p>于是乎,我开始了第二个尝试,使用 Kotlin + Antlr 来实现对不同语言的模型统一,也就是我的另外一个开源项目 Chapi。但是呢,随着不断的尝试,我发现了其中的难度和工作量比较大:</p><ol><li><strong>编写不同语言的语法解析</strong>。社区上已经有大量的成熟的轮子,其中最出名的就是 Antlr 相关的语法解析。官方维护的代码仓库(grammars-v4)包含了大量的 Antlr 语法解析案例,可以找到市面上一些主流的和非主流的实现。</li><li><strong>设计统一语言模型</strong>。即设计出一套能兼容不同语言的语言模式。当然了,这是一个持续完善的过程,会随着更多语言的加入,变得更加完整和复杂。</li><li><strong>解析不同语言</strong>。即根据不同语言的语法特性,转换为上述的模型。</li></ol><p>从难度上来说,我们可以看出技术难度主要是在步骤 1 和步骤 2。而步骤 3 呢,则是一个非常繁琐、工作量巨大的体力活。我们还需要熟悉不同的编程语言,并一一解析对应的字段,才能转换每一个语言。</p><p>因此,我尝试建立起了 Chapi 的社区,然后手把手带领一群人干活。尽管,对于不同的语言我已经建立起了统一的编写模式:TDD + Tasking。似乎,很多人对于 AST 有点担心,因此参与的人非常少。所以,对于其它语言的支持就不了了之。</p><p>相关资源:</p><ul><li>详细的设计可以参考我写的那一篇:《<a href="https://link.segmentfault.com/?enc=eYrPEXKTMvxKuQVHgpAoKQ%3D%3D.wYAjS0%2FTF%2BB3HBxry2n%2F%2FADBDDgBJcILLiQ1yk7%2FGYF%2FACJseP2fUsJb2%2FOsfqWc" rel="nofollow">如何为代码建模?</a>》</li><li>详细的实现可以参照:<a href="https://link.segmentfault.com/?enc=xeyI5M7OMa0xiXBPeAN8bg%3D%3D.Uwbj8uQURYuq0NN9ow3aCwo0oDnhkSfD3veJFDiCzM0%3D" rel="nofollow">https://github.com/phodal/chapi</a></li></ul><h2>引子 1:语法高亮的背后</h2><p>与此同时,哪怕有足够的人,Antlr 并非一个完美的答案。在编写不同语言的支持时,我依旧遇到一系列的 Antlr 语法不支持的问题。如 JavaScript 的 Import,Java 的一些 Lambda 问题……。换句话来说,Antlr 官方只是维护这么一个库,真实的效果就不得而知了。</p><p>于是,我就回到了一条老路上,使用正则——当然不会自己写了。在那篇《<a href="https://link.segmentfault.com/?enc=4eo9wQh7q4Azwl%2FkXehWEg%3D%3D.Dtf8SDVgdMFMrl2tyFd0BmJxn8kuWwhL5tlGHHcJkBzq1FiWWSFTYSBcZJxG4dHN" rel="nofollow">编程语言的 IDE 支持</a>》中,我提到了<strong>基于正则表达式来实现语法分析</strong>,其中介绍了两个编辑器的实现方式:</p><ul><li>Sublime Text 基于 YAML 形式的正则匹配方式:<a href="https://link.segmentfault.com/?enc=gp7W9bAYIzk3g4oGY%2BakXg%3D%3D.n7NGH2BvbLQ6pNvazYboTUzKO%2BtKY%2B%2FIXJyQe9%2BrUPykx5R2FdXi%2B4DtzLnIclC6iT3pIWYMsUJCULTlPQhxmw%3D%3D" rel="nofollow">Sublime Syntax files</a></li><li>Textmate、VS Code 基于 JSON 的正则匹配方式:<a href="https://link.segmentfault.com/?enc=RV40eKBdqOf5IfJXob879w%3D%3D.0eyFwPjTFilXRHS2pbvT8mF7W8hPvRc2jr%2Fyslon%2B%2BVLP5vlMDIjRpCBM%2BAP5SRLj3zziz9pElKjY5wsyQY%2FIA%3D%3D" rel="nofollow">Language Grammars</a></li></ul><p>所以,我们选择了 VSCode 作为了语法解析背后的语言。在这种模式之下:</p><ol><li>我们有一个成熟稳定的语言解析工具,并且也有一个巨大的团队在维护它们。</li><li>它的社区是非常庞大的,经过大量的反复提升。</li></ol><p>因此,我和我的同事从几个前开始编写:<a href="https://link.segmentfault.com/?enc=Cuf1R6SB8p2O7jSRc3esMg%3D%3D.hLpihribfnvrR%2Bc5UsOWYPLDDyAMe0aZ9UROtDuhSVY%3D" rel="nofollow">https://github.com/phodal/scie/</a> —— 一个基于 TextMate 语法高亮的库。</p><h2>引子 2:代码生成与 JavaPoet</h2><p>在我们粗糙地完成了 Scie 之后,我开始思考着下一步:<strong>如何从 A 语言转换为 B 语言的时候</strong>,我从 JavaPoet 获取到了一些灵感。JavaPoet 是一个用来生成 <code>.java</code> 源文件的 Java API。如下是一个简单的 JavaPoet 代码示例:</p><pre><code class="java">TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();</code></pre><p>也就是说,我们可以写一个 API,以将某语言转换为 B 语言的源码。而要实现任意语言的转换,那么我们就需要实现一个 DSL:用于<strong>描述不同语言与统一模型的差异</strong>。后来,我意识到我还需要另外一个 DSL,用于<strong>转换统一模型到不同的语言</strong>。</p><h2>引子 3:中间表示的演变</h2><blockquote>编译器的核心数据结构是被编译程序的中间形式。 —— 《编译器设计》</blockquote><p>理论上,通过上述的两种方式,我们就可以直接生成不同领域的模型。但是呢,为了调试方便,可以创建一个中间语言来作为它们的承载物,可以让我们实现更有意思的事情,去统一进行编译器优化——当然,我是瞎说的。</p><p>随后项目的原因,我研究了一小段时间的 Proguard + D8 和 Android R8 的实现上。它们两做的事情是相似的,将 <code>.class</code> 字节码,编译优化,再转换成 Android 手机上的 dex。当然了,转换为 Aot 就是一个更有意思的话题了(虽然我也不熟悉)。但是呢,这期间涉及到了一系列的中间状态:<code>java -> .class -> .dex -> odex -> .oat</code>。即从 Java 代码到 JVM 虚拟机字节码 -> Dalvik 虚拟机字节码 -> 优化过后的 Dalvik 字节码 -> ART 机器码。</p><p>而我们再回过到来看,编码语言本身也是一种中间表示,因为机器运行的是靠机器码。即,那句经典的话:<strong>代码是写给人看的</strong>。</p><h2>引子 4:DSL 的 DSL</h2><p>对于有的编译器来说 ,它们可能有唯一的 IR(中间表示,Intermediate representation),也可能会有一系列的 IR。最常见的一些实现,便是我们看到的那些使用 LLVM 作为后端的语言,它们可以生成中间形式的 LLVM IR。同样的对于我们想做到的事来说,我们可以设计一个类似于 LLVM IR 的高级中间表示,用于承载语言的设计。</p><p>由于项目涉及到一丁点的代码优化,所以我还阅读了一下那本《高级编译器设计与实现》,书中引入了 ICAN 这个中间语言。嗯,这就是已经被论证的结果了,不再需要我去论证它的必要性。所以下一步就是:</p><blockquote>自举,在计算机科学中,它是一种用于生成自编译编译器的技术,即使用打算编译的源编程语言编写的编译器。</blockquote><p>在业内,人们往往往把自举定义在编译器领域中。但是呢,它可以在更多的领域被应用。例如 Java 的构建工具,Gradle 使用 Gradle 来构建自己 —— 当然与编程语言相比,这事要相对容易一些。</p><p>而人的自举就是把自己替换便,让工具做了自己的事,让别人做得了自己的事。所以,我们就需要 Charj 来做自己所能做的事情。</p><h2>Charj Lang</h2><p>终于回到了正题上了,在有了上面的几步之后,我们就能:</p><ol><li>通过正则表达式,解析、生成不同语言的语法树。</li><li>编写 Poet API 将上述的语法树,转换为某一特定语言源码。</li><li>设计某一中间语言,用来作为 A 语言转换为 C 语言的载体。</li><li>实现 A 语言到 C 语言,又或者 C 语言到 A 语言的自由转换。</li></ol><p>这便是从任意语言转换为任意语言的想法和思路。于是乎,我和我的同事们开始设计一个中间语言:Charj。</p><p>当然了,开发一个语言的目的主要是为了锻炼自己的能力,不论是抽象能力,还是算法能力等等。在这个漫长的人生里, 它将会变得有意义。以后,请叫我 Charj 语言作者。PS:你也可以是 Charj 语言作者。</p><p>回过头来看,事实上应该是这样的,我已经尝试造了各式各样的工具,从各类的编辑器到各类的命令行工具。而在学习了 Rust 之后,我研究了 JVM、编辑器底层,也正在逐一尝试创建日常所使用的工具。而在上一年里,因为编写重构工具 Coca,再到随后的转换为统一语言模型的 Chapi。对于编译器前端,我已经有了相当丰富的经验。自然而然的,创造一个语言就成了下一个方向。</p><h3>为什么叫 Charj ?</h3><p>从本义上来说,Char 是更适合 Charj 的定义的,但是 Char(仓颉)的商标已经被注册了。退而求其次,我只好叫 Charj,可以引伸为中英混合式的:字符(Char)集(Ji),又或者是字符(Char)集(姬)。又或者是『字符 J』 —— 至于 J 是什么意思,我还没想清楚。我们可以再定义,再取一个新的名字。</p><h2>Charj 进展</h2><p>Charj 使用的是 Rust 为主的语言编写的。Rust 的自举已经证明了:Rust 用于开发编程语言是没有问题的。当然了,主要原因还在于让我 C++,还不如让我写 Haskell。</p><h3>Charj Lang (设计中)</h3><p>Charj lang 现在的工作分为两部分:</p><ol><li>完善语法设计</li><li>编译器的流程设计</li></ol><p>尽管从理论上来说,Charj 不一定需要编译 + 可运行,但是为了自举,我们需要它们。于是,我们在后端采用了 LLVM,前端使用的是 Rust 里的 LR(1)解析器生成器 <a href="https://link.segmentfault.com/?enc=QTcDnodMf1bdBO%2BPxDvmnw%3D%3D.OqzPQdIJJ7Epa2F%2Ftu8ZRCyuOaydpZLWt7Mw3LpFeJv9A06CAOFbwV%2BywAZ3pm2r" rel="nofollow">lalrpop</a> 。</p><p>GitHub:<a href="https://link.segmentfault.com/?enc=J1YzY%2FZAbOv55lX8LnJ6zQ%3D%3D.yKlC9iHzHuZhaZwA4%2Fzi0UT%2BQ4fc%2FPLN8TJc%2BForduc8gIAg7bRSt%2FHHX2wL0%2Fec" rel="nofollow">https://github.com/charj-lang/charj</a></p><h3>Charj IDE(开发中)</h3><p>当前已经有一个简单的语言插件,当然只有基本的高亮和跳转功能。如果你有一定的 IDEA 插件开发经验,也可以来我们一起搞搞。</p><p>GitHub:<a href="https://link.segmentfault.com/?enc=nCvsvmX3Iw8HWE2IyMP0Zw%3D%3D.XNLpU3xif0zgy2C%2By8bWQk11NPCHUgrv0khGuBoHs7Abnmmnygfk4ZtKbaNBtIEV" rel="nofollow">https://github.com/charj-lang/intellij-charj</a></p><h3>Scie</h3><p>Scie(Simple Code Identify Engine)是一个基于正则表达式的通用语言转换器。主要开发工作基本已经完成了,但是有几个问题需要解决:</p><ol><li>效率优化</li><li>调用 Oniguruma FFI 时会随机出错。</li></ol><p>GitHub:<a href="https://link.segmentfault.com/?enc=sqkFnAscou4SzGQbS1wtrQ%3D%3D.aMgnN0Kj3iysI20KKvvro1%2BchYIxPYcF%2BPUKTwsONz1PL6qLMAVuKxkSoFSoZQzE" rel="nofollow">https://github.com/charj-lang/scie</a></p><h3>Charj Poet(开发中)</h3><p>Charj Poet 是一个是用于生成 Charj 代码的 Rust API。计划等语法设计完,再进一步完善。</p><p>GitHub:<a href="https://link.segmentfault.com/?enc=fxrzG%2BRujbO1Miqktiia%2FA%3D%3D.UlVWW69Eh7T%2BWTW6mRh4rtOuLmy7VYWghL4XNnRlmmtunh5sQucNCyTbrcygWMR%2B" rel="nofollow">https://github.com/charj-lang/charj-poet</a></p><h3>Poet DSL(待定)</h3><p>两部分:</p><ol><li>即设计一个新的 DSL,来描述不同语言转换为 Charj Lang 的 DSL。</li><li>即设计一个新的 DSL,来描述 Charj Lang 转换为不同语言的 DSL。</li></ol><h3>官网</h3><p>简陋和粗糙的官网:<a href="https://link.segmentfault.com/?enc=BCV6pWxdfSCIlOT8xHRnsw%3D%3D.xzf7AiPpGRbHuF2wbmRMrBvgk2TY2dXqMiWYig08u5c%3D" rel="nofollow">https://charj-lang.org/</a></p><h2>其它</h2><p>此时此刻,虽然我翻过几本编译相关的书籍,我也并非一个编译原理相关的专家。所以,如果你也有兴趣,欢迎来加入我们。</p>
文档代码化
https://segmentfault.com/a/1190000022417413
2020-04-18T23:02:38+08:00
2020-04-18T23:02:38+08:00
phodal
https://segmentfault.com/u/phodal
8
<blockquote>文档代码化,将文档以类代码的领域特定语言的方式编写,并借鉴软件开发的方式(如源码管理、部署)进行管理。它可以借助于特定的工具进行编辑、预览、查看,又或者是通过专属的系统部署到服务器上。面向非技术人员的文档代码化的一种常见架构模式是:<a href="https://link.segmentfault.com/?enc=j6UGTMazAcPAeYZc3rmjbw%3D%3D.gHFFhtvLOlOOlGlE8BX%2Fv9A%2FZliMF9xHjExzsxvfval03OZiJYlQrGHN98mThVNHV%2Bl4AGmYguFbIxhkBktwwQ%3D%3D" rel="nofollow">编辑-发布-开发分离</a>』,</blockquote>
<p>最近一个月里,我在开发一个基于 Git + Markdown 的全新文档系统。我定制了一个基于 markdown 的标记语言,以支持起雷达图、条形统计图、思维导图等图表的文档系统。这个系统将在未来几个月内发布。当然了,视进度而看,也可能是月底。</p>
<p>过去的几年里,我们一直在讨论各种各样的代码化,基础设施代码化、设计代码化、需求代码化……。在我的那一篇《<a href="https://link.segmentfault.com/?enc=oePB96L72HxnyTRKWjSlgA%3D%3D.0Xw3HTwoCWewpSDSqild%2FrpEK3ofXKPVSZD5a5S8Ozkn9yxx13ydJx3IUrAfpkgS" rel="nofollow">云研发:研发即代码</a>》中,设计了一个完全代码化的软件开发流程。而今天我们将讨论另外一个有趣的存在:文档。</p>
<p>在《<a href="https://link.segmentfault.com/?enc=NRhTzM3Ek2q0%2B21SbLnGHg%3D%3D.biFAlMMOIeYr%2FYX3yNfMH8TPV2UAQhclr%2BNlimO8ndIMXyHKj4uBHObglaEDyBRasiV8XsevdaX0zGQqrkkxbQ%3D%3D" rel="nofollow">架构金字塔</a>》中,我将文档定义为支撑五层架构模型的一种存在。因为文档在一个系统中是非常重要的存在,我们用它来指导开发工作,用它来记录问题,用它来写下规范……。总而言之,它很重要,所以我们重新讨论一下这个话题。</p>
<h2>引子 1:架构决策记录:格式化文档</h2>
<p>三年前,当我第一次接触到『<a href="https://link.segmentfault.com/?enc=m6G06JyddHKs5TeNuRjBCw%3D%3D.ezX8hb044IuiGqR0AfdMUfHOh8X8Ef65HbzrIEObDbaXmDExkU0cl4CenBk4uRvKCS0DAhGLDBTpwJGwm%2Ftdjg%3D%3D" rel="nofollow">架构决策记录</a>』的概念时,我被它的理念所吸引:</p>
<ul>
<li>使用轻量级文本格式化语言描述重大决策</li>
<li>跟随代码一起版本化</li>
<li>使用某种特定的文档格式(标题、上下文、决策、状态、后果)</li>
</ul>
<p>随后,我使用 Node.js + TypeScript 写了一个 <a href="https://link.segmentfault.com/?enc=5j%2FxRJLKisEhNw9byqC3sA%3D%3D.6dtya3cU3SWDDsqyGKrKFN628XeyxZudZ2xsKAd%2FJT4%3D" rel="nofollow">ADR</a> 工具。现在,在我的大部分开源荐中,我都会使用它来管理一些技术决策。因为基于这个理论设计的这个文档系统真非常棒,我可以查询到:</p>
<ul>
<li>一个技术决策发生的时间和架构改变,对应的修改人</li>
<li>回溯所有的技术决策,从中整理出架构发展过程</li>
<li>所有的决策都是记录在版本控制系统中,可恢复</li>
<li>易于管理和维护</li>
</ul>
<p>对于一个长期开发的系统来说,它真的非常有用。</p>
<h2>引子 2:静态站点生成:数据代码化</h2>
<blockquote>静态站点生成是一种混合式的 Web 开发方法,它通过部署预先构建的静态文件进行部署,来让开发者在本地构建基于服务器的网站。</blockquote>
<p>当 GitHub Pages 成为了程序员首选的 博客/内容/文档 服务器时,他/她也采用了静态站点生成这一项技术。静态站点生成有各种各样的优点:</p>
<ul>
<li>可靠性、安全性、稳定性、可用性等更好</li>
<li>可版本控制</li>
<li>易于测试</li>
<li>易于实践持续部署。提交即可上线</li>
<li>灵活,易于定制</li>
</ul>
<p>而事实上,静态站点生成所做的最主要的一件事是:将数据库中的数据进行代码化。采用诸如 Wordpress 这样的 CMS 时,我们是将数据存储在数据库中,以实现对于数据的 CRUD。一篇文章变为数据库二进制文件中的一个片段。</p>
<p>随后,静态站点生成工具做了第二件事情便是将文本内容可视化出来,便于人们阅读。这样一来,我们便实现了发布-开发分离。</p>
<h2>引子 3:定制的标记语言:扩充</h2>
<p>将数据代码化时,我们面临了一个非常大的挑战:易于编写、阅读的标记语言(如 markdown)只设计了内容的形式,缺少了内容相关的其它信息,诸如于创建时间、作者、修改时间等等。</p>
<p>于是各个静态站点生成器定制了自己的 markdown,添加了一些额外的信息,如 hexo 采用 <code>:year-:month-:day-:title.md</code> 的形式来管理文章的日期和标题等。这样一来说,就不需要通过读取这个文章的 Git 信息来构建出整个信息。</p>
<p>我们所熟悉的 GitHub Flavored Markdown 也是如此,通过不明显破坏内容格式的兼容模式来扩展 markdown 数据字段。</p>
<p>除此,我们可以定制基于 markdown 数据的图表、思维导图等内容。</p>
<h2>引子 4:编辑-发布-开发分离:面向非技术人员</h2>
<p>面向非技术人员设计是代码文档化的一大挑战。作为一个程序员,我们觉得 markdown 语法再简单不过了,但是对于非技术人员来说并非如此。他/她需要:一个易于上手的可视化编程器。而要实现这样一个目的,我们需要在架构上做一些转变,我们可以尝试使用 『编辑-发布-开发分离』 模式来解决这个问题。</p>
<p>即,我们将过程拆为了三步:</p>
<ul>
<li>编辑人员,可以使用常用的编辑器或者是定制的编辑器</li>
<li>开发人员,编写内容的展示</li>
<li>发布的时候,集成这两部分代码</li>
</ul>
<p>我们依旧可以选择用源码管理的方式来管理内容。只需要将数据库接口,转变为 Git 服务器接口即可 —— 当然它们是稍有不同的。不过呢,把本地的 Git 转换为 Git remote 那就基本一致了。</p>
<p>如此一来,最后我们的成本就落在改造出一个基于 Git 的 markdown 编辑器。</p>
<h2>文档代码化</h2>
<p>完美,我又一次在引子里,把中心思想表达完了。</p>
<h3>为什么你需要将文档代码化?</h3>
<p>主要原因有:文档不代码化,就没有重构的可能性。</p>
<p>剩下的原因有:</p>
<ul>
<li>二进制的文档难以进行版本管理。想象一下 <code>2020-02-30.docx</code> 和 <code>2020-02-31.docx</code>。</li>
<li>无法准确地知道谁是文档的修改者,大家可能都是 admin,又或者是会议上的张三</li>
<li>找不到哪个是最新的文档</li>
<li>文档写得很烂,但是你没办法重构二进制文档</li>
<li>供应商绑定</li>
<li>……</li>
</ul>
<p>应该还有更多。</p>
<h3>什么是文档代码化?</h3>
<p>回到正题上:</p>
<blockquote>文档代码化,将文档以类代码的领域特定语言的方式编写,并借鉴软件开发的方式(如源码管理、部署)进行管理。它可以借助于特定的工具进行编辑、预览、查看,又或者是通过专属的系统部署到服务器上。</blockquote>
<p>它具备这么一些特征:</p>
<ul>
<li>使用标记语言编写内容。如 markdown</li>
<li>可通过版本控制系统进行版本控制。如 git</li>
<li>与编程一致的编程体验(除了内容写不了测试)</li>
</ul>
<p>而一个高效的文档代码化系统,还具备这么一些特征:</p>
<ul>
<li>持续部署,即修改完内容可自动发布。</li>
<li>与特定的形式组织内容索引。如以知识库的形式来组织内容。</li>
<li>特定的文本格式。如架构决策记录、静态内容生成,以用于以提供更好的用户体验</li>
<li>可支持 REST API。以通过编辑器来修改内容</li>
<li>可以支持多种方式的输出。如网站标准 HTML,又或者是 Docx、Latex 等</li>
<li>支持编辑、校对工作流</li>
<li>支持搜索</li>
<li>多人协作</li>
</ul>
<p>而事实上,大部分的团队并不需要上述的高级功能,而且它们都已经有了成熟的方案。</p>
<h2>如何设计一个文档代码化系统?</h2>
<p>事实上,我们在四个引子中标明了我们所需要的要素:</p>
<ol>
<li>使用格式化的文档</li>
<li>借助静态站点生成技术来发布系统</li>
<li>通过定制标记语言扩充能力</li>
<li>面向非技术人员实现编辑器</li>
</ol>
<p>设计一个标记语言及其扩展语法,然后实现它即可。</p>
<h3>1. 确立关键因素</h3>
<p>考虑到我和我的同事们最近实现了这么一个系统,我还是忍受一下手的痛楚,简单说一下怎么做这样一个系统。我们所考虑的主要因素是:</p>
<ul>
<li>图表渲染</li>
<li>流程图渲染</li>
<li>可视化展示</li>
</ul>
<p>因为由 DSL 转换成的图表易于修改,并且可以索引。于是乎,我们:</p>
<ol>
<li>通过 markdown 的 Code 语法来扩充这些能力</li>
<li>使用 markdown 的 table 和 list 来提供数据</li>
<li>使用 D3.js 来支持流程图绘制</li>
<li>使用 Echarts 来进行图表绘制</li>
<li>尽量使用 SVG 图片</li>
<li>……</li>
</ol>
<h3>2. 实现一个 MVP</h3>
<p>我们使用 Angular + GitHub,快速实现了一个 MVP:</p>
<ol>
<li>我们使用 Git 作为数据库.它就可以实现多人协作的目的,并且可以追踪所有的变化</li>
<li>我们使用 GitHub Pages 作为服务器。只要一修改文档或者代码,就会部署最新的文档。</li>
<li>我们使用 marked.js,它可以让我们快速扩展语法。</li>
<li>使用 textarea 结合 markdown 制作了一个简易的编辑器。</li>
</ol>
<p>随后,我们在这个基础上进行了快速的迭代。</p>
<h3>3. 扩展语法</h3>
<p>我们使用了 markdown 的 <code>code</code> 作为图表的 DSL,扩展了这么一些语法:</p>
<ul>
<li>echarts。直接渲染 Echarts 图表</li>
<li>mindmap。Markdown List 转为思维导图</li>
<li>radar。Markdown List 转为雷达图</li>
<li>process-table。带流程的图表</li>
<li>process-step。另外一种带流程的图表</li>
<li>pyramid。金字塔图形</li>
<li>quadrant。四象限图</li>
<li>class。直接调用 CSS 的 class</li>
<li>graphviz。使用 Dot 渲染图片</li>
<li>mermaid。使用 mermaid 可视化</li>
<li>webcomponents。调用 WebComponents 组件</li>
<li>
<p>toolset。调用 Toolset 相关的组件</p>
<ul>
<li>slider。权衡滑块</li>
<li>line-chart。表图</li>
</ul>
</li>
</ul>
<p>所以,对于使用者来说,只需要编写下面的代码:</p>
<ul><li>
<p>质量成熟度评估模型</p>
<ul>
<li>质量内建: 3 -> 4</li>
<li>优化业务价值: 2 -> 2</li>
<li>质量统一,可视化: 1 -> 5</li>
<li>全员参与: 3 -> 4</li>
<li>快速交付: 4 -> 5</li>
<li>测试作为资产: 2 -> 3</li>
<li>快速反馈: 5 -> 5</li>
</ul>
</li></ul>
<p>config: {"legend": ["当前", "未来"]}</p>
<p>就可以生成对应的图表:</p>
<p><img src="/img/bVbGdXj" alt="image.png" title="image.png"></p>
<p>又或者是用于制作技术雷达图:</p>
<p><img src="/img/bVbGdXg" alt="image.png" title="image.png"></p>
<p>我们还通过 config 来输入 JSON,进行一定的懒惰化处理(不要累死自己)。</p>
<h3>3.1 重写 markdown 渲染器</h3>
<p>我们在这个过程中,遇到的最大的挑战是,随着我们对 markdown 语法的不断扩充,相关的代码已经变成了一坨大泥球。所以,我们不得不重写了这部分代码:</p>
<ol>
<li>借助于 marked.js 的 lexer 解析出 token</li>
<li>根据 token 修改生成新的 token</li>
<li>遍历新生成的 token,渲染出元素</li>
<li>结合虚拟滚动,解决性能问题</li>
</ol>
<p>已经开源在 GitHub,并发布对应的 npm 包:<code>@ledge-framework/render</code>。</p>
<h3>4. 发布这个项目</h3>
<p>我们已经在 GitHub 上发布了这个文档化系统,你可以参与到其中的使用和开发。</p>
<p>GitHub:<a href="https://link.segmentfault.com/?enc=yqfQEURqxA%2BPbydvWKlAjw%3D%3D.6Zn5Ec02zCnFFfQ363AwF%2Fks5Np0%2Bt%2FtPIbaa%2FDSl98%3D" rel="nofollow">https://github.com/phodal/ledge</a></p>
<p>项目首页:<a href="https://link.segmentfault.com/?enc=vQat9olkp5NBFr8PzpJhbQ%3D%3D.09q%2FucLPtfk%2FB5QoreQK7xR9ulvKrT6PNuo8P111OzQ%3D" rel="nofollow">https://devops.phodal.com/</a></p>
<h2>总结</h2>
<p>然后,你就成为了一个 Markdown 工程师,D3.js 设计师,Echart 配置管理员。</p>
Ledge:一个开源的『DevOps + 研发效能』知识平台
https://segmentfault.com/a/1190000022209444
2020-03-31T09:34:36+08:00
2020-03-31T09:34:36+08:00
phodal
https://segmentfault.com/u/phodal
15
<p>过去的三星期里,因为疫情 + 种种不可告人的原因,我开始建设一个 DevOps 知识平台。</p>
<p>GitHub:<a href="https://link.segmentfault.com/?enc=hOSzZlHq3qLzLLmREy%2BMGQ%3D%3D.bCdG3p2%2BvmBmN2%2BxptEPXA6U7n8nAYnN8ejxbStcqaCUx2kasz8YXx5D%2FHQ2II46" rel="nofollow">https://github.com/phodal/ledge/</a> </p>
<p>在线使用:<a href="https://link.segmentfault.com/?enc=Y%2BOEqY58yq9r%2FY1Hctu8Ww%3D%3D.kHkUFNAH%2F1QrNgnJYeGsmDWF2LU5ksiTITKNbWsXH6o%3D" rel="nofollow">https://devops.phodal.com/</a> </p>
<p>在这个知识平台里, 它包含了这么一些内容:</p>
<ul>
<li>DevOps 工具元素周期表。帮助您进行数字化时代的 DevOps 工具选型。</li>
<li>DevOps 设计工具。帮助您设计组织内的 DevOps 流程,涵盖了流程、人、工具、制品等等。</li>
<li>案例学习。从社区的知识库中,我们总结了传统企业走向 DevOps 的经验,并浓缩到易于使用的内容和材料中。</li>
<li>最佳实践。我们从海量的 DevOps 内容中,提炼出了一系列的最佳实践,以更好地帮助企业进行 DevOps 实践。</li>
<li>模式与原则。基于我们的实践,我们提炼了位于它背后的模式与原则,帮助个人和组织更好地了解 DevOps 文化。</li>
<li>操作手册。只凭实践与原则,无法让中小型 IT 团队进行 DevOps 转型,所以我们准备了详实的操作手册,以帮助您一步步前进。</li>
<li>度量。KPI - 度量、度量 - KPI、KPI - 度量,帮助您更好地度量 DevOps 转型情况。</li>
<li>报告。我们尝试从丰富的 DevOps 报告中,提炼出有用的实践和工具。</li>
<li>Mobile DevOps。我们相信移动应用的 DevOps 改进,才是大多数公司的挑战。</li>
<li>工具。工具,工具,工具是最好的生产力,工具比人的记忆力更加可靠。</li>
</ul>
<p>起先,我是想做一些 DevOps 工具,比如说适合于中国国情的『DevOps 元素周期表』。顺带一说,这个工具不是我首创的,我只是用更好的架构实现了一遍。。如此一来,对于大部分开发人员来说,他/她们就可以从这个表中,组合出适合于自己组织的分子(毕竟周期表上都是原子)。几天之后,我就有了这个工具,根据整个研发体系的每一个过程,你可以从中挑选出适合你的要素:</p>
<h2>DevOps 元素周期表</h2>
<p>为了凑满上面的元素,我不得不找一个又一个大公司的案例,看看他们到底是用什么技术栈。所以,我七拼八凑得差不多了,顺便一想,既然我有这么多大公司的案例,为什么不抽象一下这些案例呢。于是就有了:</p>
<p><img src="/img/remote/1460000022209451" alt="" title=""></p>
<h2>案例学习</h2>
<p>我们从互联网的各个地方(来源见内容中标明的出处),帮你抽取了各大公司的案例:</p>
<p><img src="/img/remote/1460000022209448" alt="" title=""></p>
<ul>
<li>腾讯</li>
<li>小米</li>
<li>招商银行</li>
<li>美团</li>
<li>……</li>
</ul>
<p>在这些案例,背后往往包含、隐藏了各种各样的价值取向。所以,进一步地,我想去提取这些模式,所以就有了:</p>
<p><img src="/img/remote/1460000022209449" alt="" title=""></p>
<h2>模式与原则</h2>
<p>包含了:</p>
<ul>
<li>流畅度模式</li>
<li>度量体系设计</li>
<li>学习型组织构建</li>
<li>……</li>
</ul>
<p>画完这些大包之后,随后,我们就可以进入 DevOps 的设计和实施阶段。我们要找到那些最好的实践:编程、团队、文化、能力、测试等等:</p>
<p><img src="/img/remote/1460000022209447" alt="" title=""></p>
<h2>实践</h2>
<p>太多,不写。 当然了,为了在组织中实施 DevOps,我们还需要一本操作手册,来帮助你一步步构建 DevOps 体系:</p>
<p><img src="/img/remote/1460000022209450" alt="" title=""></p>
<p>从度量,到实践,到工程化,再到流程打通,顺势而来,一步下实践。</p>
<h2>工具</h2>
<p>光有手册是不行的,我们还把各种各样的工具做了上去: 除了工具的名称,还包含:</p>
<ul>
<li>工具的准备事项</li>
<li>工具的操作步骤</li>
<li>工具的示例</li>
<li>该工具的在线工具使用</li>
</ul>
<p>还有更多的功能在开发中。 也欢迎加入我们的开发队伍,更多的案例将帮助每个人更好地成长。 </p>
<p>GitHub:<a href="https://link.segmentfault.com/?enc=xF7kHdRAo%2Fmgwm13UCirnQ%3D%3D.zz49YdEP9Xl%2FVSvMUEwGd2DJjMsynaW%2BdGebXezAl27aURL3mg%2FNQ3oZEmRXuMr6" rel="nofollow">https://github.com/phodal/ledge/</a></p>
<p>在线使用:<a href="https://link.segmentfault.com/?enc=ymhqLxGYqpXlSlu8qRAC9g%3D%3D.ukFskCcxK5Ix1Ehjxqbm4ZzLGMVDaATYmyeJjbr5rFc%3D" rel="nofollow">https://devops.phodal.com/</a></p>
2019 节点: Love Wife & Change Life
https://segmentfault.com/a/1190000021474012
2020-01-03T09:42:55+08:00
2020-01-03T09:42:55+08:00
phodal
https://segmentfault.com/u/phodal
3
<blockquote>为什么你还在 ThoughtWorks?</blockquote>
<p>因为不加班。人生总会有很多的选择,在决策的那一刻,你不知道对与错。但是,开心就好。</p>
<p>12 月初,ThoughtWorks 开始了 Annual Review 的 Kick Off,我开始总结这一年的工作,与此同时,我也开始总结我的 2019 节点。今年仍然是『平淡无奇』也过完了重要的一年。</p>
<p>太长不读版:</p>
<ul>
<li>爱情上,领证了,和 @ 花仲马一起来到了杭州;还差好多钱买房,还得考虑办婚礼的事情。</li>
<li>职业上,从深圳 office 转到了华东 MU,从华东 MU 转到了咨询团队,开始了在 TW 的出差生涯,还有加班生涯。</li>
<li>设计上,每天画了一张画,一年 365 张画;插画,作为文章的一部分,已无处不在。</li>
<li>写作上,出版了《前端架构:从入门到微前端》,印刷量在 7500 左右,有希望在一年内达到 1 万。</li>
<li>编程上,写了更多的工具,愈加丰富的重构经验,顺带深入软件体系的架构。</li>
<li>斜杠上,尝试电子产品的评测,写作相关的收入差不多是前两年之合。</li>
<li>影响力上,开始了 International 的尝试 —— 时间仍然是一个限制因素。</li>
<li>社交上,我退出了大量的微信群,专注于生产内容。</li>
</ul>
<p>嗯,还有游戏,文明大法好。</p>
<p>所以,对比一下上一年的目标:</p>
<ol>
<li>
<strong>技术隐私</strong>,打造了自己的 Serverless 密码管理器:<a href="https://link.segmentfault.com/?enc=%2FGxZG1j4fbLqchRU9ycgEw%3D%3D.i9PLYQ2igMJC82UUaQf%2Bz4i33Gpm%2FqDqVRXRCwatn0WYwboGFgIz5rx3kRR%2FeJz4" rel="nofollow">https://github.com/phodal/mopass</a> ,作为一个 Chrome 插件,它很好地作为了我的二次管理认证工具。</li>
<li>非技术写作。好似没有一个开始,似乎也不是一个好的目标。借这个名义,我看完了《刺客信条》的小说。</li>
<li>工具。开发了更多、更有意思的工具,还有更多的 PoC。</li>
<li>设计。天天练习插画,更快的画画速度,质量上也有所提升。也烧了更多的钱绘画工具上。</li>
<li>Coach。幸好在上一年里,它不是一个目标,扶不起的人太多了。对的人,对的事,才能成——借口。</li>
<li>影响力。受众级别比上一年有所提升,还有更深度的内容。</li>
</ol>
<p>不算太好,也不算太糟糕。</p>
<h2>编程:平台 + 工具 + DSL</h2>
<p>惯例,依旧是工作 + 业余。</p>
<h3>工作:Platform & Tools</h3>
<p>工作上没有圈,也没有点,今年的工作简直是一团糟,还加了人生的第一次班,而明年还会有更多。Work–life balance 不断被打破,就得寻找一个更合适的地方 —— 如果有的话。</p>
<h4>平台</h4>
<p>上半年,工作的主要内容是大前端开发框架 / 平台,所以研究了一段时间低代码编程,写了那篇《无代码编程》的文章。一番操作下来,发觉重点在于 AST 和 DSL。因此,除了开发一些日常的工具之外,我开始撸 dilay 框架,创建了 subal 项目……。作为一个苦逼的 Tech Lead,除了项目相关的两个团队,还要照顾公司的其它多个团队。日常不是一般的忙,开会、开会、开会,还得做架构??还要评绩效??填别人的坑??还有写代码……</p>
<p>一个也不能落下,每个都落下了。</p>
<p>做了一个大前端开发平台,这样一折腾下来,收获倒也是挺大的,我对研发体系有了更深入的研究。考虑问题的时候,比以往更加系统,更加全面。文档、脚手架、示例应用、CLI 工具、IDE / 编辑器集成、售后 Q & A 等等一个都不能少。于是,在项目上写了对应的 CLI 工具,尝试把文档融入了开发工具中……</p>
<p>没毛病,老子可以各种吹了:不要做平台。我 Phodal 就是……,我也不会……。</p>
<h4>工具</h4>
<p>下半年,beach 了两三个月,写了个重量级应用 Inception,然后,转到了咨询团队。来到了新的 U,有了更多的灵感和时间去写工具,也从公司大佬新哥那获得一堆 Todo List。所以,下半年在业余时间写了更多的代码,写了更多的 DSL。所以,DSL 成为我这一年的一个主要风向。</p>
<p>我有了遗留系统重构工具: <a href="https://link.segmentfault.com/?enc=%2FsBDhm%2FhlhTPnwDYCANJJQ%3D%3D.k7sAR125GNJKrbSSoS0V1zcLEz%2BuUlSRI8yEzrfuELI%3D" rel="nofollow">Coca</a>,还有了 Badsmell 识别工具:<a href="https://link.segmentfault.com/?enc=cRlq%2BKLGpFu5bZWbGftsNA%3D%3D.r0RlaN6gW8cOp97k3Lkcby%2BoN%2Bp4jplZUldr8ICJATvM0k%2FAvb5LqpJyn2Yjwlxs" rel="nofollow">Sprite</a>,以及对应的重构建议工具:<a href="https://link.segmentfault.com/?enc=mQeGGTi0JUC1g1%2BFjadsJQ%3D%3D.tQNg633SdL%2BvfPPoFLmSYRz3lDKNDzf4vZSeGZPY940%3D" rel="nofollow">fanta</a> ……。</p>
<p>它们都是使用 Go + Antlr 写的,target 是便宜的后端开发语言 Java。一顿瞎操作下来,除了更懂 Java 语法,我还学会了 Go。明年,我就可以 Rust + WebAssembly 搞 C or TypeScript 的语法分析了,一下子学会好几种东西的感觉好爽。</p>
<p>卧槽,又要兴奋的失眠了。想想,还是很美好的。</p>
<h3>业余:工具 + DSL</h3>
<p>2018 年底,我的 GitHub 数逼近 40,000;2019 年底,也有 48,615了,可不敢说逼近了。明年我的目标就是 50,000 star 的时候,发个朋友圈,哦,不对应该是 Twitter。</p>
<p>依旧的 Serverless 仍然是我的后端最佳选择,我用它写了我的密码管理工具:[moPass] <a href="https://link.segmentfault.com/?enc=Vy%2FNkN33E3CptbA3IdoLsQ%3D%3D.5ANJOrr9ptL1Tz06D1MWn07jl%2Br9V8AVn9Fgbj8SfI49J4BJSp%2Bkw8o5aQKlKnL%2F" rel="nofollow">https://github.com/phodal/mopass</a> 。继 ADR 和 Phodit 之后,我的另外一个日常使用工具。我的业余项目上还上手了 Golang,嗯,真香。</p>
<h4>Architecture</h4>
<p>今年,有幸可以在项目中引入对于前端架构的探索,进一步地完善了我的前端架构体系,也产生了前端架构守护框架 <a href="https://link.segmentfault.com/?enc=cqlqK1W0s9FRoH4nILtzbg%3D%3D.Gu8BEuRiHSwo96yTWlQwxFkIhN9ZCHN9ymwm9X1d8qU%3D" rel="nofollow">Dilay</a>,完美的造了个 PPT。</p>
<p>所以,在实践了 Domain Driven Design 和 Clean Architecture 之后,我开始思考 One Architecture 的可能性,尽管我已经用 JavaScript / TypeScript 证明了它的可能性:<a href="https://link.segmentfault.com/?enc=n%2Blrcwf%2F2362X7QA%2Fih93g%3D%3D.nBw1W8I7rauWPgJlFvRSZTJPsTI%2BgcPp7JIl3Rsepzc%3D" rel="nofollow">https://github.com/phodal/one</a></p>
<p>然而,Java 仍然是后端的主流语言,一个 Java 转 JavaScript 的工具不可缺少,而编程语言有那么多,所以我们需要的一个是 DSL 转任何语言的工具。也就是我最近在做的 Code 项目: <a href="https://link.segmentfault.com/?enc=wCwGsAqYmBXjfZ04v2Vyug%3D%3D.COjA0bPYOJ2MSNxT1Bb5oC7yr1nWQCKAXNxqO7icpIw%3D" rel="nofollow">https://github.com/phodal/code</a> ,实践上还有待完善,只是 hello, world 出来。大抵,还需要半年地时间完善。</p>
<h4>基准化</h4>
<p>考虑到人总是会老的,Phodal 是人,所以 Phodal 会老的。我继续写工具、文章来沉淀知识,以用于以后甩出一个链接(装 x 神器):</p>
<ul>
<li>[Clean Architecture]:<a href="https://link.segmentfault.com/?enc=wkJbisqd1NPaQLYmHR7llA%3D%3D.4GnMOeyFDPpz81Vbp7ec82AKheOCAb%2Bc0w5O1r%2Fs%2FKuZFLcF9rC4olKGmrjl9K%2BC" rel="nofollow">https://github.com/phodal/clean-frontend</a>
</li>
<li>[React Boilerplate]:<a href="https://link.segmentfault.com/?enc=CRr06978aT869UKzsTKvBw%3D%3D.Qh0hvBP4XrHKhSpixsOyVd2WuzwMLyyejzOhTGaoSerhDk%2BKYNklc0X%2B164Rp8m1" rel="nofollow">https://github.com/phodal/react-boilerplate</a>
</li>
<li>[New Project Checklist]:<a href="https://link.segmentfault.com/?enc=V5tKAgIyOpPvWshcgkidLw%3D%3D.b4NsDxWZrfe0RjwwXM302H7QEqxNFSnO1sUgbCE0WBY%2F%2FjyTidYFrQEBnPSJ%2FBL7" rel="nofollow">https://github.com/phodal/new-project-checklist</a>
</li>
<li>Inception 工具:<a href="https://link.segmentfault.com/?enc=%2BDD03M8Iyvqglf3DIovJHw%3D%3D.I7bakyJPbZx%2BYLS1mIzi1GRhF%2B1UFPcM%2FL1j4rwLegbS0AMUm9TmHHtOHj5Qv3t2" rel="nofollow">https://github.com/phodal/inception</a>
</li>
<li>[Path to Productioon]:<a href="https://link.segmentfault.com/?enc=hz82jTYOKH7F4AA6leQt7A%3D%3D.cexbtN4%2BWxKx8tf0JkYhhTIRR1KQ2xMyZy%2F1FhB9FoU%3D" rel="nofollow">https://github.com/phodal/path</a>
</li>
<li>[Tech Lead Assessment]:<a href="https://link.segmentfault.com/?enc=712tWFkjLh08yeSndwWaXQ%3D%3D.ITvRDrEjLQTSG0JQ2LknvQspQGVPNIMoTrdisMgQpko%3D" rel="nofollow">https://github.com/phodal/tla</a>
</li>
</ul>
<p>对应的还有一篇相关的文章:《<a href="https://link.segmentfault.com/?enc=LuzMNwabt%2F%2FBTpLdKsb6gQ%3D%3D.DraNdNHCnYRXyYDxbN1NRNO7jAQHmZorUlk%2FQYcRwNNgRjIG5frWa0wYxfUVK8Ojo9XQWCv%2Bunmoyqm%2Bq9VUopW8BN7AEhYBSRqe6%2Fz4Ew4%3D" rel="nofollow">如何创建你的应用脚手架</a>》,年轻就是好,对了,还有 Tech Lead 的基准化:《<a href="https://link.segmentfault.com/?enc=D5iiwht9hwJihWNuHtIiFA%3D%3D.a3%2Fq1QeOboPuXMJWiV5%2BU8WqgDEjWnWQGTb4raEgW2Dt4JpNFdwcaWymgMMJuPeqFHNJxn1i6ipurNtIj8%2BjiA%3D%3D" rel="nofollow">Tech Lead 的养成</a>》。</p>
<h3>Everything as Code:DSL</h3>
<p>作为一个 Markdown 资深用户,除了进一步完善我的 Phodit,我还结合 Markdown 写了很多工具:</p>
<ul>
<li>Markdown 定制文档工具,见 《<a href="https://link.segmentfault.com/?enc=OwTqttZfySWf8J%2BkK72ICw%3D%3D.Kv7WpQfRJf4AuuGoZ8XCNB6aZ%2Fd%2Bp8pWRWyk712zjGW1HtuRkTMYYYYpe5LwLS3Ed0WnAWGcDIXy%2FCKPNbvtJbq9DhxeVV%2FmVNHIEraezDikgZcPlJuJifyUy8snRGz%2F" rel="nofollow">【架构拾集】基于 Markdown 文档展示系统设计</a>》</li>
<li>Markdown 转思维导图,见 Inception 工具。</li>
<li>Markdown 转 PPT 工具 <a href="https://link.segmentfault.com/?enc=UGkIi8MouI0QDQL5jnZ8Ow%3D%3D.VNVcEmvAwkwPCAbK7bTmNCdRCbClmZsqlQT%2B6LPXY9I%3D" rel="nofollow">mdppt</a>
</li>
</ul>
<p>不过呢,定制别人的 DSL 始终是比较一个比较 hack 的方式,所以如何卓有成效地开发一个 DSL,便成为了一件非常有意思的事。所以,公司大佬说的 DSL as Data, Data as DSL 仍然是一个不错的目标。</p>
<p>在那篇《<a href="https://link.segmentfault.com/?enc=%2B03pp94Vry222ukTxYZC1Q%3D%3D.jE8gqaaAjD8rrBOaVAnR7lZrycOLWwQAn1P6LSrL2%2FBnaegnDxbXK4d044mGSF0c" rel="nofollow">云开发:未来的软件开发方式</a>》中,我提到了在未来几年,我要做的一些事情:</p>
<ul>
<li>更易于实践的微架构</li>
<li>完善的代码化体系</li>
<li>寻求合适的协作设计</li>
</ul>
<p>所以,设计和抽象 DSL(Domain Specific Language)将成为了我未来几年一个重要战略。也因此,从大体上来说,它仍然是我的下一年目标和计划。</p>
<h2>写作:100 万浏览量 + International</h2>
<p>年初,出现了一个新的里程碑,我的博客 phodal.com 累计访问量突破了 1,000,000 万。</p>
<p>考虑到在微前端和 Clean Architecture 的实践,已经和国外的速度差不多,外加国内的 996 环境。所以在在今年年中,我尝试将 International 作为 Impact 的一个新方向。因此,在这篇总结里,我把写作相关的部分分为了大中华区和 International 区。</p>
<h3>“大中华区”</h3>
<p>虽然我在写新书的时候,看了很多小说,试图去改进,但是依旧在豆瓣上被吐槽『写出来太理论太像翻译腔』。没救了,没救了,写过 776 篇博客的我,表达能力依旧还有巨大地提升空间。</p>
<h4>出版</h4>
<p>今年 5 月出版的《[前端架构:从入门到微前端]》,出版社的总印刷数已经有 7500(并非卖完),豆瓣读书上的评分也有 7.6 分 —— 比前两本书多出了一份。瞬间又有动力准备下一本书了,只是怕是没有那么多时间了。</p>
<p>颇为遗憾的是,出于字数少的原因,我在『前端架构』 一书中多加了一个章节。而由于出版时间太早,少了后来实践的『Clean Architecture』——这是另外一个前端所需要的分层架构模式。将它与 Serverless 配合,就形成了我们所需要的 One Architecture。</p>
<h4>文章:体系规划</h4>
<p>从内容上来看,我对今年的文章倒是颇为满意的:</p>
<ul>
<li>《无代码编程》</li>
<li>《整洁架构》</li>
<li>《构建可信的软件架构 10 要素》</li>
<li>《微前端架构》</li>
<li>《管理依赖的 11 个策略》</li>
<li>《云开发:未来的软件开发方式》</li>
<li>……</li>
</ul>
<p>但是如你所料,我创建了一篇又一篇地长文章,手就有点疼,坐久蛋也疼。</p>
<p>只是呢,好像也没有新的亮点了。</p>
<h3>International</h3>
<p>今年从我的观察来看,我在开源领域开始逐步走向非中文世界。Mooa 和 ADR ,迎来了一个又一个的国际友人的支持。我的 GitHub followers 也多了一个又一个的国际友人。</p>
<p>内容国际化,是今年年中开始的一个新的方向 —— 之前的另外一个国际化目标是:<strong>开源软件的国际化</strong>。</p>
<p>虽然我的英语语法并不是那么靠谱,但是 Google Translate 也差。我相信同翻译腔一样,只要会被人吐槽英语语法不行,说明我已经成功引起大家地注意了。</p>
<h4>English Articles</h4>
<p>由于种种原因(诸如文章太长懒得翻译、高质量的文章不够多),产出仍然相对比较少。</p>
<p>不过,也算还行,我在 Dev.to 上创建了我的账号,发了一篇微前端相关的文章,还有一篇 Clean Architecture 相关的文章,也产生了一定的影响力。除了几十个的掌声,一万左右的单篇文章浏览量,还有 StackOverflow 有相关的问题指向了我的文章,笑而不语~。</p>
<p>所以,继续翻译更多的文章吧,是时候依赖反转一下了。</p>
<h4>Review</h4>
<p>过去的几年里,Review 英文书籍显然是国际赛道的一部分,只是呢,当时呢,这个 business line 还没想好,现在也没想好。</p>
<p>今年还是 Review 了 Packt 出版社的一本书籍《Web Development with Angular and Bootstrap - Third Edition》,遗憾的是近一二年 review 的书,都没有被引入国内。</p>
<p>不管怎样,国际化应当成为 2020 的一个继续前进的方向。</p>
<h2>设计</h2>
<p>我换了一个又一个的工具:</p>
<ul>
<li>iPad + Apple Pencil。买前生产力,买后爱奇艺</li>
<li>Wacom Intuos Pro。专业级手绘板,相当的不错。</li>
<li>绘王 Kamvas Pro 16。嗯,解控屏,效率就是高。对于我这种非专业级选手,还是非常好用的。</li>
<li>Wacom Intuos Mini。出差专用,个小板子虽然不那么好用,但是我也算是习惯了。</li>
</ul>
<p>终于,我还是没画好画。</p>
<p>年中的时候,我尝试录制绘画的过程到 B 站、抖音上。但是,画的时候往往发现,录视频的时候,会影响我画画,也就作罢了。</p>
<h3>画:365 天</h3>
<p>上一年考虑到设计的边缘化,我开始采用日常练习的方式,来提升这方面的感觉。</p>
<p><a href="https://link.segmentfault.com/?enc=XuRt6dNI2jVj99XwDP6%2BDg%3D%3D.Hl9Wxe5V2778J1axB6pHQsTuYLgXXD%2FI%2BblBQvjlXag%3D" rel="nofollow">Daily</a></p>
<p>最初设计的目标是每天 0.5 小时,但是受编程状态的影响,往往会被挤压到 15分钟。好在,随着练习的进一步 <del>一深</del> 深入,我还是能在短暂的时间内,画出一些不错的作品。</p>
<p>稍有不同的是,受出差的影响,我有时候不得不在早上画画。</p>
<p>Whatever,我已经有一堆画了。</p>
<p>所以,如果没有问题的话,它仍将作为我下一年的日常。问题的关键在于:如何结合文章的意图,创建对应的作品?</p>
<h3>Design Thinking</h3>
<p>寻找更广泛维度的设计思考。</p>
<p>TBD。</p>
<p>受限于有大量的代码要写,以及好像没有遇到好的设计师。没有灵感嘛,就先这样,慢慢来,路子还长着。万一明年可以遇到可以愉快合作的小伙伴呢。</p>
<h2>其它</h2>
<p>人生苦短,一点点做</p>
<h2>Hello, 2020</h2>
<p>嗯,我要登机回杭州了。</p>
<p>咦,2020 呢,要小康。</p>
<p>哦,不对,说好的婚礼呢。</p>
<blockquote>本文参与了 <a href="https://segmentfault.com/a/1190000021354599">SegmentFault思否征文「2019 总结」</a>,欢迎正在阅读的你也加入。</blockquote>
遗留系统重构工具:Coca
https://segmentfault.com/a/1190000021461834
2020-01-02T09:45:13+08:00
2020-01-02T09:45:13+08:00
phodal
https://segmentfault.com/u/phodal
2
<blockquote>好的代码是可以重构出来的。</blockquote>
<p>如我在先前的文章所说,我最近的工作主要是在做架构重构、代码重构。所以,一如既往地,我又写了个工具来帮助我完成相关的工作。这样一来,下次我可以更快速地完成相关的工作。</p>
<p>在这之前,已经有大量的工具可以做类似的事情。如我司已有大佬开源了 Tequila ( <a href="https://link.segmentfault.com/?enc=lhB66dh9GFmfqlKffhWxOQ%3D%3D.AKqEpWb9tLFZN0bSYX3y%2BwF9B331Cl16V43weCNskOV44sNF3QV2k9gZkcG7qznn" rel="nofollow">https://github.com/newlee/teq...</a> ) 这样的架构、依赖分析工具。只是呢,简单的架构分析是无法满足我的需求的。并且,本着写了工具就是赚经验的思想,我决定写一个自己的工具。</p>
<h2>Coca 简介</h2>
<p>从按我的实践经验来看,我将重构分为四种类型:</p>
<ul>
<li>
<strong>分层架构重构</strong>。 在不改变业务逻辑的情况下,进代码架构进行调整。即根据单一职责和依赖倒置原则的思想,对系统进行模块拆分与合并,以明确职责降低耦合度;对包进行重新规划,划分包之间的边界,减少代码间的耦合。</li>
<li>
<strong>模式重构</strong>。针对特定模式的坏味道,采用设计模式来提升可扩展性,增加可读性。</li>
<li>
<strong>模型重构</strong>。在包含测试的情况下,通过识别和发现模型的行为,将行为聚合到模型中。</li>
<li>
<strong>微重构</strong>。对于一些小的代码坏味道,可以通过 IDE 重构来快速改善即有代码,而不会影响到业务功能。</li>
</ul>
<p>而《重构:改善既有代码的设计》一书主要针对的是微重构。因为重构项目的难度不是一般的大,对于经验不足的个人、团队来说,重写往往比重构来得便捷。</p>
<p>所以,根据我的需要我写了自己的工具,以用于改善即有代码的设计:</p>
<blockquote>Coca 是一个用于遗留系统重构的瑞士军刀。它可以分析代码中的 badsmell,行数统计,分析调用与依赖,进行 Git 分析,以及自动化重构等。</blockquote>
<p>GitHub 地址:<a href="https://link.segmentfault.com/?enc=2wKWUmqJQf7SrYfa5FVfRQ%3D%3D.nXHbgyrQGee4GDFU5u7OwDxMXeqQQB7YW9lJ2JaXnQ8%3D" rel="nofollow">https://github.com/phodal/coca</a></p>
<p>安装:</p>
<ol>
<li><code>go get -u github.com/phodal/coca</code></li>
<li>直接从 GitHub 上下载可执行文件</li>
</ol>
<p>在执行所有的命令之前,需要先执行 <code>coca analysis</code> 以生成对应的依赖关系数据。</p>
<p>So,让我们看看 Coca 1.0 包含了哪些功能?</p>
<h2>API 调用</h2>
<p>这个功能目前主要针对的是 Spring 开发的,它似乎已经是 Java 世界的后端服务的标准。</p>
<h3>REST API 生成</h3>
<p>只需要执行 <code>coca api</code> 就可以生成我们想要的结果:</p>
<ol>
<li>函数调用图</li>
<li>API 调用层级</li>
</ol>
<p>下图是 API 的统计信息:</p>
<table>
<thead><tr>
<th>SIZE</th>
<th>METHOD</th>
<th>URI</th>
<th>CALLER</th>
</tr></thead>
<tbody>
<tr>
<td>36</td>
<td>GET</td>
<td>/aliyun/oss/policy</td>
<td>controller.OssController.policy</td>
</tr>
<tr>
<td>21</td>
<td>POST</td>
<td>/aliyun/osscallback</td>
<td>controller.OssController.callback</td>
</tr>
<tr>
<td>17</td>
<td>GET</td>
<td>/subject/list</td>
<td>controller.CmsSubjectController.getList</td>
</tr>
<tr>
<td>17</td>
<td>GET</td>
<td>/esProduct/search</td>
<td>search.controller.EsProductController.search</td>
</tr>
<tr>
<td>17</td>
<td>GET</td>
<td>/order/list</td>
<td>controller.OmsOrderController.list</td>
</tr>
<tr>
<td>17</td>
<td>GET</td>
<td>/productAttribute/list/{cid}</td>
<td>controller.PmsProductAttributeController.getList</td>
</tr>
<tr>
<td>17</td>
<td>GET</td>
<td>/productCategory/list/{parentId}</td>
<td>controller.PmsProductCategoryController.getList</td>
</tr>
<tr>
<td>17</td>
<td>GET</td>
<td>/brand/list</td>
<td>controller.PmsBrandController.getList</td>
</tr>
<tr>
<td>17</td>
<td>GET</td>
<td>/esProduct/search/simple</td>
<td>search.controller.EsProductController.search</td>
</tr>
</tbody>
</table>
<p>对应的全景图:</p>
<p><img src="/img/remote/1460000021461838" alt="API Call" title="API Call"></p>
<p>当然了,你也可以轻松通过参数过滤一下你想要的内容。</p>
<p>这个功能的目标是用于未来向 DDD 重构时,构建出限界上下文。</p>
<h3>调用关系图</h3>
<p>也可以只看某一部分的依赖关系图:</p>
<pre><code>coca call -c com.phodal.pholedge.book.BookController.createBook -r com.phodal.pholedge.</code></pre>
<p>输入对应的完整方法名,和想要去除的包含即可:</p>
<p><img src="/img/remote/1460000021461837" alt="Method Call" title="Method Call"></p>
<h3>反向依赖关系图</h3>
<p>还能生成对应的反向调用关系图:</p>
<pre><code>coca rcall -c org.bytedeco.javacpp.tools.TokenIndexer.get</code></pre>
<p>结果如下图所示:</p>
<p><img src="/img/remote/1460000021461839" alt="Cocal Rcall" title="Cocal Rcall"></p>
<h2>行为分析</h2>
<p>由于代码只是反应系统的另一部分,我们不得不从版本管理工具中获取更多的信息,于是有了:</p>
<pre><code>coca git</code></pre>
<h3>文件修改统计</h3>
<p>排名靠前的文件,可以帮我们看到一些问题:<code>coca git -t</code>,于是乎,我们就有了:</p>
<table>
<thead><tr>
<th>ENTITYNAME</th>
<th>REVSCOUNT</th>
<th>AUTHORCOUNT</th>
</tr></thead>
<tbody>
<tr>
<td>build.gradle</td>
<td>1326</td>
<td>36</td>
</tr>
<tr>
<td>src/asciidoc/index.adoc</td>
<td>239</td>
<td>20</td>
</tr>
<tr>
<td>build-spring-framework/resources/changelog.txt</td>
<td>187</td>
<td>10</td>
</tr>
<tr>
<td>spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java</td>
<td>170</td>
<td>10</td>
</tr>
<tr>
<td>spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java</td>
<td>159</td>
<td>15</td>
</tr>
</tbody>
</table>
<p>对,这是 Spring Framework 中最常修改的文件,前面三个文件看上去是合理的,但是 <code>AnnotationUtils.java</code> 显然有问题。</p>
<p>对应的 DefaultListableBeanFactory.java 也有 2000+ 行左右的规模。</p>
<p>从代码的行数和修改次数来看,它们都是上帝类,并且经常出现 Bug。</p>
<h3>代码年龄统计</h3>
<p>嗯,还有诸如于 <code>coca git -a</code> 这样的普通功能:</p>
<pre><code>+-------------------------------------------------------------------------------------------------------------------------+-------+
| ENTITYNAME | MONTH |
+-------------------------------------------------------------------------------------------------------------------------+-------+
| helloworld.go | 2.04 |
| README.md | 2.04 |
| imp/imp.go | 2.04 |
| .gitignore | 2.01 |
| cmd/root.go | 2.01 |
| test/coca_suite_test.go | 1.94 |
| learn_go_test.go | 1.94 |
| core/languages/java/JavaLexer.tokens | 1.84 |
| core/languages/java/java_lexer.go | 1.84 |
| examples/step2-Java/domain/ValueObjectD.java | 1.84 |</code></pre>
<h3>其它功能</h3>
<p>还有诸如基本的分析功能等: <code>coca git -b</code></p>
<pre><code>+-----------+--------+
| STATISTIC | NUMBER |
+-----------+--------+
| Commits | 350 |
| Entities | 514 |
| Changes | 255 |
| Authors | 2 |
+-----------+--------+</code></pre>
<p>有兴趣可以自己去探索。</p>
<h2>知识提炼</h2>
<p>嗯,这也是我计划在明年实践的功能。</p>
<h3>方法提取</h3>
<p>作为此功能的第一步,我想的是先从代码中提取单词:<code>coca concept</code>:</p>
<pre><code>+------------------+--------+
| WORDS | COUNTS |
+------------------+--------+
| context | 590 |
| resolve | 531 |
| path | 501 |
| content | 423 |
| code | 416 |
| resource | 373 |
| property | 372 |
| session | 364 |
| attribute | 349 |
| properties | 343 |
| headers | 330 |
+------------------+--------+</code></pre>
<p>作为第一个简单的版本,我只是做了分词和统计,下一步便是专业性的统计。而后,用于生成领域专用的特定语言。</p>
<h3>提交信息提取</h3>
<p>你懂的,这里面的信息才是足够的丰富。</p>
<p>TBD</p>
<h3>提取中文注释</h3>
<p>下一步,我应该做类似的事情,哈哈哈</p>
<h2>坏味道识别</h2>
<p>这是一个非常通用的功能,你可以在各种各样的工具里找到。所以,我并没有特意地去增强里面的功能,也没有添加太多的功能——因为我知道他们比我的工具专业。</p>
<p>只需要:<code>coca bs</code> 就会得到一个建议修改的 JSON 文件:</p>
<pre><code class="json">{
"lazyElement": [
{
"File": "examples/api/model/BookRepresentaion.java",
"BS": "lazyElement"
}
...
]
}</code></pre>
<p>结合我写的 fanta 工具,可以生成对应的修改建议。</p>
<h2>批量重构</h2>
<p>主要是用于结合上述工具的分析结果,通过人工 + 智能的方式来实现批量化自动修正。</p>
<p>当前 API 处于试验阶段,请不要在生产环境使用。支持以下功能:</p>
<ul>
<li>批量重命名</li>
<li>批量移动文件</li>
<li>批量删除未使用的 import</li>
<li>批量删除未使用的类</li>
</ul>
<p>使用的方式也非常简单:</p>
<pre><code>coca refactor -m move.config -p .</code></pre>
<p>你可以试试,哈哈</p>
<h2>代码统计分析</h2>
<p>代码统计工具,采用的是 SCC:</p>
<blockquote>scc is a very fast accurate code counter with complexity calculations and COCOMO estimates written in pure Go</blockquote>
<p>愉快地: <code>coca cloc</code>,然后:</p>
<pre><code>───────────────────────────────────────────────────────────────────────────────
Language Files Lines Blanks Comments Code Complexity
───────────────────────────────────────────────────────────────────────────────
Go 58 31763 7132 890 23741 2847
Java 44 971 208 21 742 62
Markdown 8 238 75 0 163 0
Gherkin Specificati… 2 32 2 16 14 0
Document Type Defin… 1 293 36 0 257 0
License 1 201 32 0 169 0
SQL 1 2 0 0 2 0
SVG 1 199 0 34 165 0
Shell 1 3 1 1 1 0
XML 1 13 0 0 13 0
gitignore 1 61 8 4 49 0
───────────────────────────────────────────────────────────────────────────────
Total 119 33776 7494 966 25316 2909
───────────────────────────────────────────────────────────────────────────────
Estimated Cost to Develop $803,822
Estimated Schedule Effort 14.120551 months
Estimated People Required 6.743156
───────────────────────────────────────────────────────────────────────────────</code></pre>
<p>其它功能可以见 <code>scc</code> 工具的官方文档。</p>
<h2>重构适合度评估</h2>
<p>TBD</p>
<h2>其它</h2>
<p>这是我第一个使用 Golang 写的工具,希望我的用法足够的 Go Style。</p>
<p>首页:<a href="https://link.segmentfault.com/?enc=GtLDgFP9ISmJUXg26WsvTQ%3D%3D.poNpm%2Bi%2Fe%2BNGmEh3Ev5RejWTTg%2BMwkwEBFpi3BtUkzA%3D" rel="nofollow">https://coca.migration.ink/</a></p>
<p>GitHub 地址:<a href="https://link.segmentfault.com/?enc=PFBwq4%2B75ReSR0Et6tcDBQ%3D%3D.XtLK4LaMxfJJkZ17JLMLQXC5PXdT%2B56zxUimTyHgHPE%3D" rel="nofollow">https://github.com/phodal/coca</a></p>
<p>愉快地:<code>go get -u github.com/phodal/coca</code></p>
前端架构,有什么能做的?
https://segmentfault.com/a/1190000019670985
2019-07-05T08:54:37+08:00
2019-07-05T08:54:37+08:00
phodal
https://segmentfault.com/u/phodal
8
<p>前端有架构吗?前端有架构模式吗?</p>
<h2>架构是什么?</h2>
<p>软件架构,是一种为了解决复杂问题的通用模式。软件架构,是关于软件系统的一系列有层次的技术决策的集合。换句话来说,当我们讨论架构的时候,不能只讨论某某架构,而是要包含其实施,以及后期的维护。</p>
<p>因为:</p>
<ul>
<li>一个无法上线的应用架构,算不上是好的软件架构</li>
<li>一个没有人能完成开发的软件架构,算不上是可行的软件架构</li>
<li>一个在现有的技术上不可行的架构,算不上是合理的软件架构</li>
</ul>
<p>诸如微服务,对于复杂的后端系统来说,是一种不错的『低耦合,高内聚』的实施。但是,它并不适合于大部分的小公司——复杂的架构不适合于简单的应用。而小公司也缺乏足够的人才,来实施一个复杂的系统,与此同时还需要有人能维护这个系统。</p>
<p>所以,当我们谈及软件架构的时候,说的是:有这么一些人,他/她们能按时、高质量(或者说有质量)完成这一个系统的设计——即<strong>有能力的个人</strong>。</p>
<p>PS:对于前端架构来说,这些人大概率会来自于看了本书的人,笑~</p>
<h2>前端架构拆解:四层次设计</h2>
<p>从笔者的角度来看,架构设计本身是分层级的,面向不同级别的人时,所展示的内容也是不一样的。如面对的是同一级别、更高一级别的架构师、技术人员,说的便是形而上学的东西,如微前端、前后端分离,并通过各种概念,如构建系统拆份,以抽象的方式介绍如何去设计。这些概念,对于接触过的程序员来说,也是相当好理解的。而当我们面对的是,经验略微丰富的程序员的时候,说的可就不是:去实现微前端这样的东西。而是需要落实到怎样去做这样的一件事。</p>
<p>在不同的时期,不同的阶段,我们考虑的架构相关的因素是不同的。按照这个思想,笔者将架构的设计分为了四个层级:</p>
<p>系统级,即应用在整个系统内的关系,如与后台服务如何通讯,与第三方系统如何集成。<br>应用级,即应用外部的整体架构,如多个应用之间如何共享组件、如何通信等。<br>模块级,即应用内部的模块架构,如代码的模块化、数据和状态的管理等。<br>代码级,即从基础设施来保障架构实施。</p>
<p>对应的层级与实施模式,如下图所示:</p>
<p>![前端四个层级]()</p>
<h3>系统内架构</h3>
<p>在设计前端架构的时候,首先考虑的是应用在整个系统中的位置——它与系统中的其它子系统是怎样的。这种关系包含了架构上的关系、业务上的关系,以及它们之间的协作机制。对于前端应用来说,这一部分的子系统包含了:</p>
<ul>
<li>其它前端应用。侧重于关注如何与这些应用交互,诸如交互、通讯等。</li>
<li>对接的后台服务。关注于如何与后台服务进行通信,诸如权限、授权、API 管理等。</li>
</ul>
<p>若是系统间的数据通信,如与后台服务之间的交互,那么只需要规定好数据通信、数据传递的机制即可。这一类的通讯机制,不仅仅包含了前后端分离架构的 API 设计,还包含了各类的数据传递,诸如 OAuth 跳转的 Token 验证。除此,对于传统的多页面应用来说,也需要关注于其中的数据传递,如 Cookie 作为用户凭据等等。</p>
<p>为此,对于前端开发人员来说,关于与后端间的关系,我们所要考虑的主要因素是前后端分离架构的设计。</p>
<ul>
<li>
<strong>前后端分离架构</strong>。(详见《前端架构:从入门到微前端》)</li>
<li>
<strong>微前端架构</strong>。(详见《前端架构:从入门到微前端》)</li>
</ul>
<p>而后,我们还需要考虑到前端的<strong>客户端展现形式</strong>。在大前端的背景之下,它可能是以 PC Web 应用、移动 Web 应用、混合移动应用(结合 Cordova 构架)、混合桌面应用(结合 Electron 框架)、原生移动应用(结合 React Native)等,具体选择何一种技术,取决于我们在之前调查的利益相关者的需求。</p>
<p>当我们做完上述的三个核心的架构决策之后,就需要考虑一下应用的<strong>部署架构</strong>。不同的客户端形式,或者需要服务端渲染,会在某种程度上影响到前端应用的部署,但是总的影响并不是太大。往往只需要通过反向代理的配置,就可以完成部署的配置。若是与后台服务不在一个域,则需要考虑<strong>支持跨域请求或者是后台做一层代码</strong>。</p>
<p>在有了这些基本的架构设定,便可以往下继续设计下一层级的应用架构。</p>
<h3>应用级架构</h3>
<p>应用级架构,指的是单个应用与外部应用的关系,如微服务架构下的多个应用的协作。它可以是一个团队下的多个前端应用,又或者是一个组织内的前端应用。其在组织中所处的位置,也进一步决定了我们所需要设计的架构方案。</p>
<p>若是从相关的定义上来看,它与系统级应用存在一定的交集。但是,笔者将其视之为系统级架构的进一步细化。如在系统内架构的层级里,我们定义了微前端架构,而具体的实施细则会放在各个应用中实现的。而诸如应用间的数据如何交换,而不同的应用又有各自不同的实现,则会在这个层级里定义了相应的接口。</p>
<p>与此同时,当我们维护了多个前端应用,必然会去考虑在这些应用之间,复用代码、共享组件库、统一设计等,以减少相应的工作量。为此,在这个层级里,我们会考虑下面的一些架构相关的设计内容:</p>
<ul>
<li>
<strong>脚手架</strong>。(详见《前端架构:从入门到微前端》)</li>
<li>
<strong>模式库</strong>。(详见《前端架构:从入门到微前端》)</li>
<li>
<strong>设计系统</strong>。(详见《前端架构:从入门到微前端》)</li>
</ul>
<p>与此同时,在这个层级里,我们决定<strong>选择什么前端框架</strong>,进行相关的技术选型。</p>
<h3>模块级架构</h3>
<p>模块级架构,便是深入单个应用内部,更细致的设计应用内部的架构。它所涉及的部分,便是在日常开发中,我们经常接触到的主要部分。我们所做的便是制定一些规范,又或者是更细致的架构设计。这部分的内容,会在我们开始业务编码之前进行设计,在敏捷软件开发中,它称之为 迭代 0/Sprint 0/Iteration 0。相关的内容有:</p>
<ul>
<li>
<strong>模块化</strong>。(详见《前端架构:从入门到微前端》)</li>
<li>
<strong>组件化</strong>。(详见《前端架构:从入门到微前端》)</li>
</ul>
<p>除此,对于不同的框架,还涉及到一些<strong>框架特定的模式</strong>与架构设计,它们会在一定程度上影响单个应用的架构。对于不同的框架来说,所需要涉及的范围都有所不发。如在 Angular 框架中,不需要操心相关的模式,只需要掌握框架定义的规范即可,如使用 Service 来保存应用的状态,使用 Pipe 来处理数据等。而在 React 框架中,则需要设计<strong>状态和数据流</strong>的管理方式,为此便需要诸如 Flux 或者 Redux 这样的状态管理方案。</p>
<h3>代码级:规范与原则</h3>
<p>当我们真正编码的时候,考虑的架构因素便是更低层级的内容。这部分的架构设计,便称为代码级的架构设计,它关注于实践过程中的相关规范和原则。这部分的内容相当的多,并且繁琐。它包含了以下的内容,但是又不限于下述的部分:</p>
<ul>
<li>
<strong>开发流程</strong>。(详见《前端架构:从入门到微前端》)</li>
<li>
<strong>代码质量及改善</strong>。(详见《前端架构:从入门到微前端》)</li>
<li>
<strong>规范而非默契</strong>。(详见《前端架构:从入门到微前端》)</li>
</ul>
<p>除此,在日常的开发中,还需要注重<strong>代码的可维护</strong>——简单的代码更容易读性和维护。笔者维护一个 Scala 项目的过程中,便是深有体会——越是写得越抽象的代码,越难以维护。诸如函数式编程是一个好的东西,但是好的东西也容易被烂用,导致人们不喜欢这个东西。</p>
<h2>小结</h2>
<p>买, 买,买 </p>
<p>——节选自《前端架构:从入门到微前端》</p>
微前端如何落地?
https://segmentfault.com/a/1190000019663742
2019-07-04T13:20:55+08:00
2019-07-04T13:20:55+08:00
phodal
https://segmentfault.com/u/phodal
50
<p>在过去的几星期里,随着 Martin Fowler 博客上,那篇 Cam Jackson 写的微前端的文章发布,到处都在讨论 Microfrontend。作为一个微前端 “专家”,我也分享一下:如何去落地微前端。</p>
<blockquote>微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将单页面前端应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。各个前端应用还可以独立开发、独立部署。同时,它们也可以在共享组件的同时进行并行开发——这些组件可以通过 NPM 或者 Git Tag、Git Submodule 来管理。</blockquote>
<h2>为什么需要微前端?</h2>
<p>微前端不是银弹,它和微服务一样会带来大量的挑战。</p>
<ul>
<li>遗留系统迁移。解决遗留系统,才是人们采用微前端方案最重要的原因</li>
<li>聚合前端应用。微服务架构,可以解耦后端服务间依赖。而微前端,则关注于聚合前端应用。</li>
<li>热闹驱动开发。新的技术,既然很热闹,那么就学吧。</li>
</ul>
<p>微前端的实现,意味着对前端应用的拆分。拆分应用的目的,并不只是为了架构上好看,还为了提升开发效率。</p>
<p>为此,微前端带来这么一系列的好处:</p>
<ol>
<li>应用自治。只需要遵循统一的接口规范或者框架,以便于系统集成到一起,相互之间是不存在依赖关系的。</li>
<li>单一职责。每个前端应用可以只关注于自己所需要完成的功能。</li>
<li>技术栈无关。你可以使用 Angular 的同时,又可以使用 React 和 Vue。</li>
</ol>
<p>除此,它也有一系列的缺点:</p>
<ul>
<li>应用的拆分基础依赖于基础设施的构建,一旦大量应用依赖于同一基础设施,那么维护变成了一个挑战。</li>
<li>拆分的粒度越小,便意味着架构变得复杂、维护成本变高。</li>
<li>技术栈一旦多样化,便意味着技术栈混乱</li>
</ul>
<p>毕竟没有银弹。</p>
<h2>如何设计微前端架构?</h2>
<p>就当前而言,要设计出一个微前端应用不是一件容易的事——还没有最佳实践。在不同的落地案例里,使用的都是不同的方案。出现这种情况的主要原因是,每个项目所面临的情况、所使用的技术都不尽相同。为此,我们需要了解一些基础的微前端模式。</p>
<h3>架构模式</h3>
<p>微前端应用间的关系来看,分为两种:基座模式(管理式)、自组织式。分别也对应了两者不同的架构模式:</p>
<ul>
<li>
<strong>基座模式</strong>。通过一个主应用,来管理其它应用。设计难度小,方便实践,但是通用度低。</li>
<li>
<strong>自组织模式</strong>。应用之间是平等的,不存在相互管理的模式。设计难度大,不方便实施,但是通用度高。</li>
</ul>
<p>就当前而言,基座模式实施起来比较方便,方案上便也是蛮多的。</p>
<p>而不论种方式,都需要提供一个查找应用的机制,在微前端中称为服务的<strong>注册表模式</strong>。和微服务架构相似,不论是哪种微前端方式,也都需要有一个应用注册表的服务,它可以是一个固定值的配置文件,如 JSON 文件,又或者是一个可动态更新的配置,又或者是一种动态的服务。它主要做这么一些内容:</p>
<ul>
<li>应用发现。让主应用可以寻找到其它应用。</li>
<li>应用注册。即提供新的微前端应用,向应用注册表注册的功能。</li>
<li>第三方应用注册。即让第三方应用,可以接入到系统中。</li>
<li>访问权限等相关配置。</li>
</ul>
<p>应用在部署的时候,便可以在注册表服务中注册。如果是基于注册表来管理应用,那么使用基座模式来开发比较方便。</p>
<h3>设计理念</h3>
<p>在笔者实践微前端的过程中,发现了以下几点是我们在设计的过程中,需要关注的内容:</p>
<ul>
<li>中心化:应用注册表。这个应用注册表拥有每个应用及对应的入口。在前端领域里,入口的直接表现形式可以是路由,又或者对应的应用映射。</li>
<li>标识化应用。 我们需要一个标识符来标识不同的应用,以便于在安装、卸载的时候,能寻找到指定的应用。一个简单的模式,就是通过康威定律来命名应用。</li>
<li>应用生命周期管理。</li>
<li>高内聚,低耦合。</li>
</ul>
<h3>生命周期</h3>
<p>前端微架构与后端微架构的最大不同之处,也在于此——生命周期。微前端应用作为一个客户端应用,每个应用都拥有自己的生命周期:</p>
<ul>
<li>Load,决定加载哪个应用,并绑定生命周期</li>
<li>bootstrap,获取静态资源</li>
<li>Mount,安装应用,如创建 DOM 节点</li>
<li>Unload,删除应用的生命周期</li>
<li>Unmount,卸载应用,如删除 DOM 节点、取消事件绑定</li>
</ul>
<p>这部分的内容事实上,也就是微前端的一个难点所在,如何以合适的方式来加载应用——毕竟每个前端框架都各自不同,其所需要的加载方式也是不同的。当我们决定支持多个框架的时候,便需要在这一部分进入更细致的研究。</p>
<h2>如何拆分?</h2>
<p>随后,我们要面临的一个挑战是:如何去拆分应用。</p>
<h3>技术方式</h3>
<p>从技术实践上,微前端架构可以采用以下的几种方式进行:</p>
<ul>
<li>路由分发式。通过 HTTP 服务器的反向代理功能,来将请求路由到对应的应用上。</li>
<li>前端微服务化。在不同的框架之上设计通讯、加载机制,以在一个页面内加载对应的应用。</li>
<li>微应用。通过软件工程的方式,在部署构建环境中,组合多个独立应用成一个单体应用。</li>
<li>微件化。开发一个新的构建系统,将部分业务功能构建成一个独立的 chunk 代码,使用时只需要远程加载即可。</li>
<li>前端容器化。通过将 iFrame 作为容器,来容纳其它前端应用。</li>
<li>应用组件化。借助于 Web Components 技术,来构建跨框架的前端应用。</li>
</ul>
<p>实施的方式虽然多,但是都是依据场景而采用的。有些场景下,可能没有合适的方式;有些场景下,则可以同时使用多种方案。</p>
<h3>路由分发式</h3>
<p><strong>路由分发式微前端,即通过路由将不同的业务分发到不同的、独立前端应用上。</strong>其通常可以通过 HTTP 服务器的反向代理来实现,又或者是应用框架自带的路由来解决。如下图所示:</p>
<p><img src="/img/remote/1460000019663745?w=1662&h=650" alt="路由分发式" title="路由分发式"></p>
<p>就当前而言,路由分发式的架构应该是采用最多、最容易的 “微前端” 方案。但是</p>
<h3>前端微服务化</h3>
<p><strong>前端微服务化,是微服务架构在前端的实施,每个前端应用都是完全独立(技术栈、开发、部署、构建独立)、自主运行的,最后通过模块化的方式组合出完整的前端应用。</strong>其架构如下图所示:</p>
<p><img src="/img/remote/1460000019663746?w=1612&h=980" alt="路由分发式" title="路由分发式"></p>
<p>采用这种方式意味着,一个页面上同时存在二个及以上的前端应用在运行。而路由分发式方案,则是一个页面只有唯一一个应用。</p>
<h3>组合式集成:微应用化</h3>
<p><strong>微应用化,即在开发时,应用都是以单一、微小应用的形式存在,而在运行时,则通过构建系统合并这些应用,组合成一个新的应用。</strong>其架构如下图所示:</p>
<p><img src="/img/remote/1460000019663747?w=1422&h=958" alt="路由分发式" title="路由分发式"></p>
<p>微应用化更多的是以软件工程的方式,来完成前端应用的开发,因此又可以称之为组合式集成。对于一个大型的前端应用来说,采用的架构方式,往往会是通过业务作为主目录,而后在业务目录中放置相关的组件,同时拥有一些通用的共享模板。</p>
<h3>微件化</h3>
<p><strong>微件(widget),指的是一段可以直接嵌入在应用上运行的代码,它由开发人员预先编译好,在加载时不需要再做任何修改或者编译。</strong>而微前端下的微件化则指的是,每个业务团队编写自己的业务代码,并将编译好的代码部署(上传或者放置)到指定的服务器上,在运行时,我们只需要加载相应的业务模块即可。对应的,在更新代码的时候,我们只需要更新对应的模块即可。下图便是微件化的架构示意图:</p>
<p><img src="/img/remote/1460000019663748?w=1662&h=744" alt="路由分发式" title="路由分发式"></p>
<p>在非单面应用时代,要实现微件化方案,是一件特别容易的事。从远程加载来对应的 JavaScript 代码,在浏览器上执行,生成对应的组件嵌入到页面的相应部分。对于业务组件也是类似的,提前编写好我们的业务组件,当需要对应的组件时再响应、执行。</p>
<h3>前端容器化</h3>
<h4>前端容器:iframe</h4>
<p>iframe 作为一个非常 “古老” 的,人人都觉得普通的技术,却一直很管用。它能有效地将另一个网页/单页面应用嵌入到当前页面中,两个页面间的 CSS 和 JavaScript 是相互隔离的——除去 iframe 父子通信部分的代码,它们之间的代码是完全不相互干扰的。iframe 便相当于是创建了一个全新的独立的宿主环境,类似于沙箱隔离,它意味着前端应用之间可以相互独立运行。</p>
<h4>结合 Web Components 构建</h4>
<p>Web Components 是一套不同的技术,允许开发者创建可重用的定制元素(它们的功能封装在代码之外),并且在 web 应用中使用它们。</p>
<p><img src="/img/remote/1460000019663749" alt="路由分发式" title="路由分发式"></p>
<p>目前困扰 Web Components 技术推广的主要因素,在于浏览器的支持程度。在 Chrome 和 Opera 浏览器上,对于 Web Components 支持良好,而对于 Safari、IE、Firefox 浏览器的支持程度,并没有那么理想。</p>
<h3>业务拆分</h3>
<p>与微服务类似,要划分不同的前端边界,不是一件容易的事。就当前而言,以下几种方式是常见的划分微前端的方式:</p>
<ul>
<li>
<strong>按照业务拆分</strong>。</li>
<li>
<strong>按照权限拆分</strong>。</li>
<li>
<strong>按照变更的频率拆分</strong>。</li>
<li>
<strong>按照组织结构拆分</strong>。利用康威定律来进一步拆分前端应用。</li>
<li>
<strong>跟随后端微服务划分</strong>。实践证明, DDD 与事件风暴是一种颇为有效的后端微前端拆分模式,对于前端来说,它也颇有有效——直接跟踪后端服务。</li>
</ul>
<p>每个项目都有自己特殊的背景,切分微前端的方式便不一样。即使项目的类型相似,也存在一些细微的差异。</p>
<h2>微前端之外</h2>
<p>如果微前端对于你们来说困境重重,还有一些不错的架构模式可以采用。</p>
<h3>应用微化架构</h3>
<p>应用微化架构,是一种开发时整体,构建时拆分,运行时分离的前端架构模式。即应用微化架构从<strong>一份代码中,构建出适用于不同环境的多套目标代码</strong>。实现上它是一种:</p>
<ul>
<li>
<strong>构建时拆分架构</strong>。</li>
<li>
<strong>代码删除架构</strong>。笑,以删代码的方式,来形成每个前端应用。</li>
<li>
<strong>微前端准备式架构</strong>。即,随时可以拆分为多个前端应用。</li>
</ul>
<p>由于它与微应用化的相似性,我们将它与微应用化做一个对比。它与微应用化不同的是,应用微化是在构建时对应用进行拆分,而非在本地模式下对应用拆分。相似的是,它也是基于构建系统的应用拆分方式。</p>
<p><img src="/img/remote/1460000019663750?w=1149&h=566" alt="可拆分式微前端" title="可拆分式微前端"></p>
<p>即:<strong>微应用化,是一个随时可合并式架构。而应用微化,则是一个随时可拆分式架构</strong>。</p>
<p><strong>它不仅仅是一个适合于前端的架构模式,也是一适用于后端的架构模式</strong>。</p>
<h3>整洁前端架构</h3>
<p>Clean Architecture 是由 Robert C. Martin 在 2012 年提出的架构模式。它具有这么一些特点:框架无关性、可被测试、UI 无关性、数据库无关性、外部机构(agency)无关性。</p>
<p>对于前端架构来说,Clean Architecure 实际上是:Clean Architecture + MVP + 组件化。如下图所示:</p>
<p>考虑到应用的规模,这里以 Angular + TypeScript 作为示例:</p>
<p><img src="/img/remote/1460000019663751?w=3241&h=879" alt="Clean Frontend" title="Clean Frontend"></p>
<p>这种架构模式特别适合于:<strong>组织内即写前端又同后端的团队</strong>。它易于映射前后端 API,且可以使用 usecase 作为防腐层。</p>
<p>没有银弹。不得不提及的是,对于小规模的团队来说,它带来的弊端可能会远远大于好处——带来大量冗余的代码。尽管通过 Angular Schematics 可以通过参数来生成代码,但是这种分层架构地于简单的应用来说,还是过于复杂、难以上手。对于不写测试的项目来说 ,usecase 也不能带来它们所承诺的好处。</p>
<h2>结论</h2>
<p>微前端不是银弹,当前也没有最佳实践,但是这是一个非常好的学习机会。</p>
<hr>
<p>节选自《前端架构:从入门到微前端》</p>
无代码编程
https://segmentfault.com/a/1190000018785505
2019-04-08T09:44:13+08:00
2019-04-08T09:44:13+08:00
phodal
https://segmentfault.com/u/phodal
21
<blockquote>中台之后,便无代码。</blockquote>
<p>规模化的组织,经常要面临这样的挑战:每个应用的基础设施是相同的,部分的代码也是相同的,甚至于它们可能只是数据模型不同而已。结果却导致了,他/她们要一次又一次地重新编写一个应用。</p>
<p>对于一个新的应用而言,它需要对接大量的三方(非自己团队)服务。服务之间的不断变化 ,导致了对应的使用方也需要发生变化。不断变化的业务,导致了前台的设计不断变化。为了应对快速谈的的前台服务,后台便诞生了中台,以提供快速的响应能力。而随着中台进一步沉淀,从某种形式上趋于稳定,而前台仍然需要快速地响应能力。</p>
<p>于是乎,作为一个前端开发人员,我们不断提炼和复用代码,想着的模式在上一篇文章 <a href="https://link.segmentfault.com/?enc=rwBD7Dib9fGmQxPefE%2FYaQ%3D%3D.36PVo7a41kKbCr4jfVZ1gRZglYLhD%2BrrE9JKQYc%2FOQchZEV9PhpVHxqMZURpnu92" rel="nofollow">减少代码量的 7~8 种方式</a> 中提到了:</p>
<ul>
<li>脚手架</li>
<li>组件库</li>
<li>模式库</li>
<li>模板和模板应用</li>
</ul>
<p>对应的,我们还创建了一系列的 CLI、工具集、编程器插件以及设计系统,以完成整个系统的快速开发。然而,我们还缺少一套有效的工具,来统一化的管理这些工具。</p>
<p>换句话来说,就是:我们需要一个<strong>前端的中台</strong>,它便是无代码/低代码编程。</p>
<h2>什么是无代码编程?</h2>
<p>无代码/低代码是一种创建应用的方法,它可以让开发人员使用最少的编码知识,来快速开发应用程序。它可以在图形界面中,使用可视化建模的方式,来组装和配置应用程序。开发人员可以直接跳过所有的基础架构,只关注于使用代码来实现业务逻辑。</p>
<p>当然,从开发人员的角度来看,降低代码量,可能是:</p>
<ol>
<li>框架本身处理了复杂性。毕竟 <strong>“复杂度同力一样不会消失,也不会凭空产生,它总是从一个物体转移到另一个物体或一种形式转为另一种形式。”</strong>
</li>
<li>代码生成减少了工作量。大量的复制、粘贴需要更多的时间。</li>
</ol>
<h3>流程</h3>
<p>只是凭借这个概念,我们是无法理解无代码编程的。于是,我画了一张图来展示相应的架构和流程:</p>
<p><img src="/img/remote/1460000018785508?w=2285&h=1681" alt="在这里插入图片描述" title="在这里插入图片描述"></p>
<p>依照我的观点来看,我将无代码编程分为了两部分:</p>
<ul>
<li>用于构建 UI 的编辑器——一种在线的拖拽式 UI 设计和页面构建工具</li>
<li>用于编写业务逻辑的流编辑器——通过流编程的方式来编写业务代码(多数是对于数据的处理)</li>
</ul>
<p><strong>UI 编程器</strong>。为了设计出我们的 UI 构建器,我们需要准备好一系列的基础设施:</p>
<ul>
<li>
<strong>UI 编程器</strong>。用于拖拽式设计 UI。</li>
<li>空白脚手架。一个带有完整的应用生命周期的项目,但是它是一个空白的项目——用于我们在构建 UI 的过程中,随时随地的添加组件和代码。</li>
<li>设计系统。我们需要一个完整的组件库,大量的页面模板,以及一定数量的模板应用,减少相应的开发工具量。</li>
<li>代码片段集。它将设计系统中的组件库进一步实例化成代码段,在完成编辑后通过 CLI 来动态编辑代码。</li>
<li>DSL(领域特定语言,可选)。中间生成物,用于隔离框架与设计。</li>
</ul>
<p><strong>流编程器</strong>。随后,为了在</p>
<ul>
<li>
<strong>流编程器</strong>。用于拖拽式、输入编写业务代码。</li>
<li>后端服务。如果不能提供现成的后端服务,则需要拥有一个标准的 API 规范,以及相应的 mock server。</li>
<li>模式库。包含相应的业务处理代码,如通用的登录、数据获取、UI 交互等。</li>
<li>DSL(领域特定语言,可选)。同上</li>
</ul>
<p>当然了,我们还需要能<strong>实时预览</strong>构建出来的应用。随后,我们执行了构建,而后构建出了一个半成品应用。开发人员只需要在它的基础上开发应用即可。而在开发人员开发的过程中,我们可以设计一系列的工具,来帮助开发人员更快速地构建应用。</p>
<ul>
<li>编辑器插件。包含设计系统、模式库等的自动完成代码,以及组织内部常用的代码库。</li>
<li>调试工具。对于混合类型的应用而言,我们还需要一个开发工具来快速构建应用。</li>
</ul>
<p>从上述的流程上来看,无代码编程还具有以下的特点:</p>
<ul>
<li>拖放式界面。又或者是可视化模型——基于节点和箭头</li>
<li>基于视觉的设计。</li>
<li>可扩展的设计。如对于插件、插件商店,社区等一系列的支持。</li>
<li>跨平台功能。支持 PC Web 应用开发,支持移动应用构架等。</li>
<li>强大的部署后。即平台包含着整个应用的生命周期。</li>
<li>拥有丰富的集成支持。可以随意的找到需要的组件,以及对应的后台服务。</li>
<li>配置化。它也意味着大量的自定义配置。</li>
<li>自制的领域特定语言(可选)。用于构建时优化。</li>
</ul>
<h3>优缺点</h3>
<p>相应的,它具有以下的一些特点:</p>
<ol>
<li>高效。不用多说,节省时间和开发成本。</li>
<li>有限的 Bug,安全性。</li>
<li>低成本。其所需的预算非常有限。</li>
<li>易用(取决于设计)。</li>
<li>开发速度更快。</li>
<li>开发过程中的 AI 。</li>
<li>维护成本低。</li>
</ol>
<p>对应的相应的缺点有:</p>
<ol>
<li>仍然需要编程技能。</li>
<li>受限的自定义能力。</li>
<li>可扩展性成了新的问题。</li>
<li>集成受限。</li>
</ol>
<p>就当前而言,低代码开发平台通常分为两大类:</p>
<ul>
<li>对于外部:制作简单的产品,如网络移动应用程序</li>
<li>对于内部:为您的团队或企业创建业务应用程序</li>
</ul>
<p>诸如只使用 CRUD、表单、验证、简单聚合、分页等简易的服务。最常见的例子就是表单构建了,诸如金数据这样的应用,便是可以直接通过拖拽元素来生成,相应的开源实现有 jQuery Form Builder。对于开发人员来说,我们只需要定义好数据模型,再通过拖拽来决定元素的位置即可。从这种角度来看,只要能使用 Serverless 构建的应用和服务,都可以直接使用低代码开发模式。</p>
<h3>开发流程对比</h3>
<p>从我们的理解来看,传统应用的开发流程是:</p>
<ol>
<li>分析、设计、确认、规划需求</li>
<li>设计系统架构</li>
<li>搭建前后端项目。选择技术栈、从零开始搭建或者从脚手架中创建。</li>
<li>搭建持续集成。</li>
<li>创建线框图和高保真原型。</li>
<li>设计数据模型,定义前后端契约,即 API URI、方法、字段等。</li>
<li>前后端实现业务逻辑。</li>
<li>前端实现 UI 页面。</li>
<li>集成第三方后端服务。</li>
<li>功能需求测试(DEV、QA、ST、UAT)</li>
<li>跨功能需求测试(安全性、性能等)</li>
<li>部署到生产环境。</li>
</ol>
<p>而,低代码开发流程:</p>
<ol>
<li>分析、设计、确认、规划需求</li>
<li>选择需要的第三方 API</li>
<li>在可视 IDE 中绘制应用程序的工作流程、数据模型和用户界面。</li>
<li>连接 API——通常使用服务、函数发现。</li>
<li>编写业务逻辑(可选)。手动代码添加到前端或者自定义自动生成的 SQL 查询。</li>
<li>用户验收测试。</li>
<li>部署到生产环境。</li>
</ol>
<p>从步骤上来看,无代码编程少了几个步骤。这些步骤都因为大量丰富的内部系统集成,而变得非常简单。</p>
<h3>适用场景</h3>
<p>就当前而言,无代码编程实际上是一种高度的场景预设的模式。因此,它存在一定的适用场景:</p>
<ul>
<li>
<strong>模型驱动开发</strong>。</li>
<li>
<strong>快速 UI 构建</strong>。</li>
<li>
<strong>极简的业务功能</strong>。使用这样的工具,也意味着,我们对于交互和可</li>
<li>
<strong>IT 资源受限</strong>。在资源受限的情况下,能快速开发出符合业务需求的应用最重要。</li>
</ul>
<p>而从流程上来看,对于一部分的应用来说,我们并不能实现无代码编程——存在一些业务上的不同之处。因此,<strong>多数场景之下,只是实现了低代码编程</strong>。</p>
<p>若是想真实的无代码编程,则需要一些更特定的场景:</p>
<ul>
<li>设计表格(输入数据)</li>
<li>创建报告(组织数据)</li>
<li>常规调度和自动化过程(操纵数据)</li>
</ul>
<p>更多的场景正在探索中。</p>
<h2>无代码编程的挑战</h2>
<p>无代码编程,除了需要准备好上述的一系列基础设施,还不可避免地会遇到一系列挑战。</p>
<ul>
<li>谁来写这部分代码?</li>
<li>客户端的基础设施准备。</li>
<li>服务端的服务平台搭建。</li>
<li>统一用户体验设计。设计出一系列能便利组合的组件,及对应的模板页面。与此同时,它们还能适应于不同的风格,即有多样性的主题支持。</li>
<li>DevOps 流水线设计。低代码编程,依赖于一系列的自动化工具,以实现构建、调试、部署以及维护,同时还包含应用的测试。</li>
<li>领域语言设计。</li>
<li>自动化测试。如果我们的前端代码是自动生成的,那么我们还需要对它们进行测试吗?这是一个好问题,而如果代码是自动生成的,那么测试也应该是自动生成的。毕竟要在平台上,编写大量的自动化测试,以保证平台的质量。</li>
</ul>
<p>其中,有一些部分略微复杂一些,我们大概可以探索一下。</p>
<h3>谁来写这部分代码?</h3>
<p>在我们创建这样一个平台和工具时,我们要考虑的第一个问题是,我们这个工具是为谁写的?</p>
<ul>
<li>没有编程经验的人。如业务人员,他/她们对于业务系统有着非常丰富的经验。这也意味着,我们</li>
<li>有编程知识,但是没有经验的人。</li>
<li>有一定经验的开发人员。</li>
<li>有丰富经验的开发人员。对于专业的人来说,自动化就意味着缺少灵活度。甚至与自己编写相比,他/她们要花费更多的时间来修复生成的代码。</li>
</ul>
<p>显然,对于相当有经验的开发人员而言,这个工具并不一定是他/她们所需要的。</p>
<h3>客户端基础设施</h3>
<p>从我的理解来看,它适合于 <strong>快速的 MVP 构建</strong>,并且生成的代码还应该方便修改,而不是诸如早期的 DreamWeaver 或者 FrontPage 这样的工具。</p>
<p>而与此同时,由于面向的开发人员水平不同,我们所需要做的工具也不同:</p>
<ol>
<li>支持云构建和调试。</li>
<li>GUI 编程应用。</li>
<li>代码生成。</li>
<li>设计系统体系构建。组件库搭建,模板应用创建等。</li>
<li>…</li>
</ol>
<p>更难的是,容易让开发人员能接受代码生成。</p>
<h3>服务端</h3>
<p>对于一个低代码平台而言,它对应的后端应该:</p>
<ol>
<li>大量可用地现有服务。身份验证、安全性、推送能力、地图等等</li>
<li>快速构建出后端服务。若是有内部 Serverless 或者 FaaS 方案,可以说是再好不过了。</li>
<li>方便与第三方服务集成。</li>
<li>灵活性。支持多语言等。</li>
</ol>
<p>统一的后端服务 API,对于后端服务来说,我们需要一个通用的范式。所有的 API 应该按照这样的范式来设计。不过,作为一个 API 的消费方,我们可能没有这么大的权力,但是我们可以采用<strong>装饰器模式</strong>,即封装第三方 API 成统一的方式。为此,我们采用的方式,仍然是:</p>
<ul>
<li>契约。诸如 Swagger UI,它可以直接创建一个简易可用的服务。</li>
<li>BFF。即我们一一去按我们的需要,去封装这些第三方应用。</li>
<li>查询语言。与自己编写 BFF 相比,更简单的方式是采用:<strong>GraphQL</strong> 这样的后端查询语言,便捷性更高、模式更加灵活。</li>
</ul>
<p>在开发前的设计期里,我们需要首先设计出对应的领域模型。</p>
<h3>领域语言设计</h3>
<p>低代码环境使用(图形)<strong>建模语言</strong>来指定整个系统、产品的行为。它意味着:</p>
<ol>
<li>将数据结构、领域模式应用到程序的各个层级中。</li>
<li>将业务规则、决策融入到应用中(层级)。</li>
</ol>
<p>这也就意味着,我们需要设计一个模型语言。而它对于我们而言,实际上是领域特定语言(DSL)。如下是一个简单的 DSL 示例,用于描述使用到的组件:</p>
<pre><code class="json"> {
'style': '',
'id': 2,
'blocks': [
{
'content': {
'content': 'content',
'title': 'hello'
},
'type': 'card'
}
]
}</code></pre>
<p>除此,我们还需要设计对应的布局 DSL,诸如于:</p>
<pre><code>H:[circle1(circle1.height)] // set aspect-ratio for circle1
HV:[circle2..5(circle1)] // use same width/height for other circles
H:|[circle1]-[circle2]-[circle3]-[circle4]-[circle5]|
V:|~[circle1..5]~| // center all circles vertically</code></pre>
<p>最后,我们还需要将流代码,转换为真实的项目代码。三种类型的 DSL 结合下来,都不是一个轻松的工具。</p>
<h2>原型设计</h2>
<p>写好现有的组件,通用型接口。如常见的登录接口,。对于使用登录接口的业务来说,它们只关心三部分的内容:</p>
<ol>
<li>成功登录。</li>
<li>取消登录。</li>
<li>登录失败。对于客户端而言,可以视为取消登录。对于服务端来说,则可能是密码错误、用户名不存在、账号被锁定等。</li>
</ol>
<p>对应于以上情景,又有一些通用的逻辑处理:</p>
<ol>
<li>登录成功。保存 Token,并返回历史页面。</li>
<li>登录失败。弹出一个自定义内容的提示框。</li>
</ol>
<p>这些代码是相似的。</p>
<h3>前端原型</h3>
<p>在一些简单的前端应用里:</p>
<ul>
<li>模板。只是在使用这些模板,再为这些模板设置相应的属性,绑定对应的值。</li>
<li>数据。其过程都只是在各种保存变量的值,并 CRUD 这些变量的路上。为此,我们需要一个数据处理的管道架构设计,用于处理这些值。</li>
<li>函数。事实上,我们的所有函数都只是一些管理函数,只用于处理这些对应的逻辑。</li>
</ul>
<p>这些常见的功能都可以做成一些组件,它们对于某些应用来说,代码相应的重复。</p>
<ul>
<li>无限加载页面。只需要绑定上 API,再控制一下元素的显示与隐藏即可。</li>
<li>表单。定义好字段即类型,对应的前后台逻辑都有了。除此,我们还需要为它们自定义好常见的规则,如正则表达式。而一旦表单之间有联动,那么这个组件的设计就更加麻烦了。</li>
<li>卡片式元素。</li>
<li>表单和表格展示。</li>
<li>常见图表。事实上,已经有一系列的图表工具了,我们只是在它们在基础上,进行了二次封装而已——使得它们可以变成领域语言的形式。</li>
<li>高级的响应式布局。与每个应用独立开发布局不同的是,低代码平台需要提供一套强大的自定义、响应式布局设计——即要满足移动端的通用模式,还要满足桌面版的通用模式。如对于大量数据来说,桌面端使用的是 Pagination,移动端使用的是无限滚动。</li>
</ul>
<h3>后端原型</h3>
<p>事实上,对于后端来说,低成本平台意味着,代码生成及服务生成。而服务本身是有限的,既然是业务上发生了一些变化,后端服务也可能并不会发生变化。</p>
<p>它也意味着:</p>
<ul>
<li>微服务化。每个后端服务要尽可能的小。</li>
<li>API 规范化。即采用统一的 API 格式,接受统一的权限管理</li>
<li>大量的 API 服务。</li>
<li>快速集成第三方服务方案。集成第三方服务是开发应用不可避免的情况。为了应对这个问题,我们需要做准备好对应的创建服务逻辑,传入第三方服务所需要的参数,便可以直接生成我们的转发服务。</li>
</ul>
<p>那么,问题来了,既然如此,我们是否能提供一个定制的工具呢?让每个人可以创建自己的组件流?</p>
<p>答案,显然是可以的。</p>
<h3>概念证明</h3>
<p>于是乎,在我最近设计的 PoC (概念证明)里,采用的是 Anguar 框架。相应的基本思想如下:</p>
<ol>
<li>使用 Material Design 作为组件库,使用 CDK 的 Drag 来实现拖拽功能。每个拖拽的组件,称为 element(元素),由 ElementDispatcher 由根据数据生成对应的组件。可拖拽的部分由两部分组成:<strong>布局</strong> + <strong>元素</strong>。</li>
<li>UI 构建完后,生成对应的 DSL,目前采用的是 JSON。毕竟数据结构是最简单的领域特定语言。</li>
<li>借由 Angular Schematics 解析这部分的 DSL,来生成相应的项目代码。</li>
</ol>
<h2>其它</h2>
<p>相关开源项目:</p>
<ul>
<li>拖拽式 Web 建造工具:<a href="https://link.segmentfault.com/?enc=mNJywQWLzAR%2BC%2BZS5crOuw%3D%3D.E0U0lkWnivHX8CZQLNTgqMi3mIvERzIKK6tn45Eobho%3D" rel="nofollow">https://grapesjs.com/</a>
</li>
<li>基于 Flow 的编程工具:<a href="https://link.segmentfault.com/?enc=fuh4mqgvxopQcL3aCBZKNQ%3D%3D.LFxdRjsLGtNsp6EE8uCNPrjQaWJmRwkhZLSyqZv4yk8%3D" rel="nofollow">https://noflojs.org/</a>
</li>
<li>DSL 布局生成器:<a href="https://link.segmentfault.com/?enc=IQcIDYkFswgQ%2Fpd3Qoo1xQ%3D%3D.%2FNt2xBltPBKGlabLA9ORft%2ByLNftb61IR6l%2F%2Fq7cMiQU6%2F2avMZ8tGmvdLU66yme" rel="nofollow">https://github.com/ijzerenhei...</a>
</li>
<li>可视化数据流编辑器:<a href="https://link.segmentfault.com/?enc=kwmZQbqwaVAFk6IuC8XDHQ%3D%3D.Gvgejy%2FpuaO7NykCFW2ihLNzDDjXA18UQ92XU528d9FYmUbsSoFGZw6kYHFop9f9" rel="nofollow">https://github.com/Gregwar/bl...</a>
</li>
<li>基于 React 的内容生成器:<a href="https://link.segmentfault.com/?enc=t6Rx8HzgFFMSBRc5RjNCnQ%3D%3D.1fNAQzkvM5smJfhIgYBWmFvCatp3o4lh099KQnQ2aNsmS1xrEUr%2BYBvXGxGzXZ9m" rel="nofollow">https://github.com/vigetlabs/...</a>
</li>
</ul>
<p>参考资料:</p>
<ol>
<li><a href="https://link.segmentfault.com/?enc=btnHihZjfmQT5KCJv932bA%3D%3D.LJuHVat0TT8Ji%2FYGdAwMHzMoCj2i2AKn%2FY5QiQCDntoDCFhrX97ZGonsivm3Brs7" rel="nofollow">https://www.process.st/low-code/</a></li>
<li><a href="https://link.segmentfault.com/?enc=T%2FoKVgC5Y88kD9k2eRSDvg%3D%3D.btGsPBp1%2B1yt84oep0YK2HcvRNjlkPGr14IERa%2FFNqSPQvq4SDFovdbTbeQrhRmKHSa%2FQWyUrNYG0RlWIMB8mFhtIoKKOC%2BEiAKreOxkyHJgeUwhHf8NBFW85o2OGWVZ1%2BtEKOwd3ZQy67%2FD0n%2BmZg%3D%3D" rel="nofollow">https://medium.com/@stefan.dreverman/decisions-to-take-for-your-low-code-architecture-c0768b606f</a></li>
<li><a href="https://link.segmentfault.com/?enc=sOAhbd2e9Nh5FkBDYw%2Fx9A%3D%3D.9wGypdVYLXj3bTd3HiTR4XhiJvWj0GSEAD4AkM94lP7zWqbpAXIAjG6cc7gYY5ETX4Wp1i0yHCkZhD%2FhQj0cfqh16XyxNsn%2BVUJLEzyGyrdD4dA6qrWxQZuSCSMZBDuy" rel="nofollow">https://medium.com/@stefan.dreverman/analyzing-coinmarketcap-data-with-neo4j-4930ba0068e1</a></li>
<li><a href="https://link.segmentfault.com/?enc=Nsfaunk3hHNSKNyi%2B2rqtw%3D%3D.CaZ3h%2BGOUVkwhdqqniZLdAvCjFZtNb%2BU97oKF985UbNbJBpAX%2B%2FRSk288s4czXzShQx4nkD6MQmrj5%2FhKpA3fw%3D%3D" rel="nofollow">https://www.outsystems.com/blog/what-is-low-code.html</a></li>
<li><a href="https://link.segmentfault.com/?enc=vGN6P7PqqPQd3lvIOYFhOQ%3D%3D.IPBqgO48L34fB3su%2FPm7HLOSwof9XYE%2FRKpsx%2FIJrnTCLy2Qv%2BU4gVpqXctbeltzB%2FCfmsinkmdsdyvZukvinMMfTP2vw7ZtkWdc6yQ1lL5co%2F8D4LO24JSVeMF1mExJ" rel="nofollow">https://medium.com/@stefan.dreverman/starting-a-low-code-application-architecture-13170fcd6fc7</a></li>
<li><a href="https://link.segmentfault.com/?enc=DD0qDntVc6JijtnfUG28%2FQ%3D%3D.om42Y147ivrFxOEjfIVMyP5aaIWKV%2Bp8vIpRhyBJ4bD3En%2BIk5zrYDYUF87Lsj89" rel="nofollow">https://www.quora.com/What-is-low-code</a></li>
<li><a href="https://link.segmentfault.com/?enc=AMf8L8W0uO%2BKN21SBibpNA%3D%3D.rXmNUftzEG9eoaIJIzThNg6ZP8kZ1uwnDCDHxkomWDaqTVEJzb5%2BJCFt7h98%2Bnbk8K2XMLfBBg863HcjyDXrTQ%3D%3D" rel="nofollow">无代码编程</a></li>
</ol>
实施微前端的六种方式
https://segmentfault.com/a/1190000015566927
2018-07-08T21:06:09+08:00
2018-07-08T21:06:09+08:00
phodal
https://segmentfault.com/u/phodal
170
<p>微前端架构是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为<strong>多个小型前端应用聚合为一的应用</strong>。</p>
<p>由此带来的变化是,这些前端应用可以<strong>独立运行</strong>、<strong>独立开发</strong>、<strong>独立部署</strong>。以及,它们应该可以在<strong>共享组件</strong>的同时进行并行开发——这些组件可以通过 NPM 或者 Git Tag、Git Submodule 来管理。</p>
<p><strong>注意</strong>:这里的前端应用指的是前后端分离的单应用页面,在这基础才谈论微前端才有意义。</p>
<p>结合我最近半年在<a href="https://link.segmentfault.com/?enc=VkJi2a%2FcLb3Hv9mCLVK5CQ%3D%3D.NGcvlF6qJw6ph8J5J5Glcvi7hHncPrG0b9APcFqSnySkZb1dfF5XqY0USqsB%2F72S" rel="nofollow">微前端</a>方面的实践和研究来看,微前端架构一般可以由以下几种方式进行:</p>
<ol>
<li>使用 HTTP 服务器的路由来重定向多个应用</li>
<li>在不同的框架之上设计通讯、加载机制,诸如 <a href="https://link.segmentfault.com/?enc=z57CA5OWntdvSijSWw0izQ%3D%3D.itCyE9rlPMI96bE4pQSZ%2FjQEYXuWrFu1Rzw6puXg1sU%3D" rel="nofollow">Mooa</a> 和 <a href="https://link.segmentfault.com/?enc=ZrBj9gQ1ExNRgwOjQzgxDQ%3D%3D.Wnqccd7a4tjkSH0vDxkrNFk4ivyd1bpSt0S8vCGRNHEJpx6T%2BuPuAt0YzLdIxkDo" rel="nofollow">Single-SPA</a>
</li>
<li>通过组合多个独立应用、组件来构建一个单体应用</li>
<li>iFrame。使用 iFrame 及自定义消息传递机制</li>
<li>使用纯 Web Components 构建应用</li>
<li>结合 Web Components 构建</li>
</ol>
<p>不同的方式适用于不同的使用场景,当然也可以组合一起使用。那么,就让我们来一一了解一下,为以后的架构演进做一些技术铺垫。</p>
<h2>基础铺垫:应用分发路由 -> 路由分发应用</h2>
<p>在一个单体前端、单体后端应用中,有一个典型的特征,即路由是由<strong>框架</strong>来分发的,框架将路由指定到对应的组件或者内部服务中。微服务在这个过程中做的事情是,将调用由<strong>函数调用</strong>变成了<strong>远程调用</strong>,诸如远程 HTTP 调用。而微前端呢,也是类似的,它是将<strong>应用内的组件调用</strong>变成了更细粒度的<strong>应用间组件调用</strong>,即原先我们只是将路由分发到应用的组件执行,现在则需要根据路由来找到对应的应用,再由应用分发到对应的组件上。</p>
<h3>后端:函数调用 -> 远程调用</h3>
<p>在大多数的 CRUD 类型的 Web 应用中,也都存在一些极为相似的模式,即:首页 -> 列表 -> 详情:</p>
<ul>
<li>首页,用于面向用户展示特定的数据或页面。这些数据通常是有限个数的,并且是多种模型的。</li>
<li>列表,即数据模型的聚合,其典型特点是某一类数据的集合,可以看到尽可能多的<strong>数据概要</strong>(如 Google 只返回 100 页),典型见 Google、淘宝、京东的搜索结果页。</li>
<li>详情,展示一个数据的尽可能多的内容。</li>
</ul>
<p>如下是一个 Spring 框架,用于返回首页的示例:</p>
<pre><code class="java">@RequestMapping(value="/")
public ModelAndView homePage(){
return new ModelAndView("/WEB-INF/jsp/index.jsp");
}</code></pre>
<p>对于某个详情页面来说,它可能是这样的:</p>
<pre><code class="java">@RequestMapping(value="/detail/{detailId}")
public ModelAndView detail(HttpServletRequest request, ModelMap model){
....
return new ModelAndView("/WEB-INF/jsp/detail.jsp", "detail", detail);
}</code></pre>
<p>那么,在微服务的情况下,它则会变成这样子:</p>
<pre><code>@RequestMapping("/name")
public String name(){
String name = restTemplate.getForObject("http://account/name", String.class);
return Name" + name;
}</code></pre>
<p>而后端在这个过程中,多了一个服务发现的服务,来管理不同微服务的关系。</p>
<h3>前端:组件调用 -> 应用调用</h3>
<p>在形式上来说,单体前端框架的路由和单体后端应用,并没有太大的区别:<strong>依据不同的路由,来返回不同页面的模板。</strong></p>
<pre><code class="javascritp">const appRoutes: Routes = [
{ path: 'index', component: IndexComponent },
{ path: 'detail/:id', component: DetailComponent },
];</code></pre>
<p>而当我们将之微服务化后,则可能变成应用 A 的路由:</p>
<pre><code class="javascritp">const appRoutes: Routes = [
{ path: 'index', component: IndexComponent },
];</code></pre>
<p>外加之应用 B 的路由:</p>
<pre><code class="javascritp">const appRoutes: Routes = [
{ path: 'detail/:id', component: DetailComponent },
];</code></pre>
<p>而问题的关键就在于:<strong>怎么将路由分发到这些不同的应用中去</strong>。与此同时,还要负责管理不同的前端应用。</p>
<h2>路由分发式微前端</h2>
<p><strong>路由分发式微前端</strong>,即通过路由将不同的业务<strong>分发到不同的、独立前端应用</strong>上。其通常可以通过 HTTP 服务器的反向代理来实现,又或者是应用框架自带的路由来解决。</p>
<p>就当前而言,通过路由分发式的微前端架构应该是采用最多、最易采用的 “微前端” 方案。但是这种方式看上去更像是<strong>多个前端应用的聚合</strong>,即我们只是将这些不同的前端应用拼凑到一起,使他们看起来像是一个完整的整体。但是它们并不是,每次用户从 A 应用到 B 应用的时候,往往需要刷新一下页面。</p>
<p>在几年前的一个项目里,我们当时正在进行<strong>遗留系统重写</strong>。我们制定了一个迁移计划:</p>
<ol>
<li>首先,使用<strong>静态网站生成</strong>动态生成首页</li>
<li>其次,使用 React 计划栈重构详情页</li>
<li>最后,替换搜索结果页</li>
</ol>
<p>整个系统并不是一次性迁移过去,而是一步步往下进行。因此在完成不同的步骤时,我们就需要上线这个功能,于是就需要使用 Nginx 来进行路由分发。</p>
<p>如下是一个基于路由分发的 Nginx 配置示例:</p>
<pre><code>http {
server {
listen 80;
server_name www.phodal.com;
location /api/ {
proxy_pass http://http://172.31.25.15:8000/api;
}
location /web/admin {
proxy_pass http://172.31.25.29/web/admin;
}
location /web/notifications {
proxy_pass http://172.31.25.27/web/notifications;
}
location / {
proxy_pass /;
}
}
}</code></pre>
<p>在这个示例里,不同的页面的请求被分发到不同的服务器上。</p>
<p>随后,我们在别的项目上也使用了类似的方式,其主要原因是:<strong>跨团队的协作</strong>。当团队达到一定规模的时候,我们不得不面对这个问题。除此,还有 Angluar 跳崖式升级的问题。于是,在这种情况下,用户前台使用 Angular 重写,后台继续使用 Angular.js 等保持再有的技术栈。在不同的场景下,都有一些相似的技术决策。</p>
<p>因此在这种情况下,它适用于以下场景:</p>
<ul>
<li>不同技术栈之间差异比较大,难以兼容、迁移、改造</li>
<li>项目不想花费大量的时间在这个系统的改造上</li>
<li>现有的系统在未来将会被取代</li>
<li>系统功能已经很完善,基本不会有新需求</li>
</ul>
<p>而在满足上面场景的情况下,如果为了更好的用户体验,还可以采用 iframe 的方式来解决。</p>
<h2>使用 iFrame 创建容器</h2>
<p>iFrame 作为一个非常古老的,人人都觉得普通的技术,却一直很管用。</p>
<blockquote>
<strong>HTML 内联框架元素</strong> <code><iframe></code> 表示嵌套的正在浏览的上下文,能有效地将另一个 HTML 页面嵌入到当前页面中。</blockquote>
<p>iframe 可以创建一个全新的独立的宿主环境,这意味着我们的前端应用之间可以相互独立运行。采用 iframe 有几个重要的前提:</p>
<ul>
<li>网站不需要 SEO 支持</li>
<li>拥有相应的<strong>应用管理机制</strong>。</li>
</ul>
<p>如果我们做的是一个应用平台,会在我们的系统中集成第三方系统,或者多个不同部门团队下的系统,显然这是一个不错的方案。一些典型的场景,如传统的 Desktop 应用迁移到 Web 应用:</p>
<p><img src="/img/remote/1460000015566930?w=572&h=264" alt="Angular Tabs 示例" title="Angular Tabs 示例"></p>
<p>如果这一类应用过于复杂,那么它必然是要进行微服务化的拆分。因此,在采用 iframe 的时候,我们需要做这么两件事:</p>
<ul>
<li>设计<strong>管理应用机制</strong>
</li>
<li>设计<strong>应用通讯机制</strong>
</li>
</ul>
<p><strong>加载机制</strong>。在什么情况下,我们会去加载、卸载这些应用;在这个过程中,采用怎样的动画过渡,让用户看起来更加自然。</p>
<p><strong>通讯机制</strong>。直接在每个应用中创建 <code>postMessage</code> 事件并监听,并不是一个友好的事情。其本身对于应用的侵入性太强,因此通过 <code>iframeEl.contentWindow</code> 去获取 iFrame 元素的 Window 对象是一个更简化的做法。随后,就需要<strong>定义一套通讯规范</strong>:事件名采用什么格式、什么时候开始监听事件等等。</p>
<p>有兴趣的读者,可以看看笔者之前写的微前端框架:<a href="https://link.segmentfault.com/?enc=8rb5jLSP2Z87P0JlxhXy5Q%3D%3D.2h2IOisWRsh4NaX625n0oufcv804OsWpyr07o0GxcK0%3D" rel="nofollow">Mooa</a>。</p>
<p>不管怎样,iframe 对于我们今年的 KPI 怕是带不来一丝的好处,那么我们就去造个轮子吧。</p>
<h2>自制框架兼容应用</h2>
<p>不论是基于 Web Components 的 Angular,或者是 VirtualDOM 的 React 等,现有的前端框架都离不开基本的 HTML 元素 DOM。</p>
<p>那么,我们只需要:</p>
<ol>
<li>在页面合适的地方引入或者创建 DOM</li>
<li>用户操作时,加载对应的应用(触发应用的启动),并能卸载应用。</li>
</ol>
<p>第一个问题,创建 DOM 是一个容易解决的问题。而第二个问题,则一点儿不容易,特别是移除 DOM 和相应应用的监听。当我们拥有一个不同的技术栈时,我们就需要有针对性设计出一套这样的逻辑。</p>
<p>尽管 <a href="https://link.segmentfault.com/?enc=AJGjFY%2FeAzfPmOn0hs3JlQ%3D%3D.%2BhKp7ZaVuPUppD3HRDzyltT8B9pR093JlTPKVFXd4a01lPiubIgdNu5VnbxRqEQx" rel="nofollow">Single-SPA</a> 已经拥有了大部分框架(如 React、Angular、Vue 等框架)的启动和卸载处理,但是它仍然不是适合于生产用途。当我基于 Single-SPA 为 Angular 框架设计一个微前端架构的应用时,我最后选择重写一个自己的框架,即 <a href="https://link.segmentfault.com/?enc=7mX2nPoa86p7WLRuibLhAQ%3D%3D.CPxFYSZbPQHCSxf8HDBoqW0ID2ARsJZuBHiEvmIi%2FJ8%3D" rel="nofollow">Mooa</a>。</p>
<p>虽然,这种方式的上手难度相对比较高,但是后期订制及可维护性比较方便。在不考虑每次加载应用带来的用户体验问题,其唯一存在的风险可能是:<strong>第三方库不兼容</strong>。</p>
<p>但是,不论怎样,与 iFrame 相比,其在技术上更具有<strong>可吹牛逼性</strong>,更有看点。同样的,与 iframe 类似,我们仍然面对着一系列的不大不小的问题:</p>
<ul>
<li>需要设计一套管理应用的机制。</li>
<li>对于流量大的 toC 应用来说,会在首次加载的时候,会多出大量的请求</li>
</ul>
<p>而我们即又要拆分应用,又想 blabla……,我们还能怎么做?</p>
<h2>组合式集成:将应用微件化</h2>
<p><strong>组合式集成</strong>,即通过<strong>软件工程</strong>的方式在构建前、构建时、构建后等步骤中,对应用进行一步的拆分,并重新组合。</p>
<p>从这种定义上来看,它可能算不上并不是一种微前端——它可以满足了微前端的三个要素,即:<strong>独立运行</strong>、<strong>独立开发</strong>、<strong>独立部署</strong>。但是,配合上前端框架的组件 Lazyload 功能——即在需要的时候,才加载对应的业务组件或应用,它看上去就是一个微前端应用。</p>
<p>与此同时,由于所有的依赖、Pollyfill 已经尽可能地在首次加载了,CSS 样式也不需要重复加载。</p>
<p>常见的方式有:</p>
<ul>
<li>独立构建组件和应用,生成 chunk 文件,构建后再<strong>归类</strong>生成的 chunk 文件。(这种方式更类似于微服务,但是成本更高)</li>
<li>开发时独立开发组件或应用,集成时合并组件和应用,最后生成单体的应用。</li>
<li>在运行时,加载应用的 Runtime,随后加载对应的应用代码和模板。</li>
</ul>
<p>应用间的关系如下图所示(其忽略图中的 “前端微服务化”):</p>
<p><img src="/img/remote/1460000015566931?w=800&h=543" alt="组合式集成对比" title="组合式集成对比"></p>
<p>这种方式看上去相当的理想,即能满足多个团队并行开发,又能构建出适合的交付物。</p>
<p>但是,首先它有一个严重的限制:<strong>必须使用同一个框架</strong>。对于多数团队来说,这并不是问题。采用微服务的团队里,也不会因为微服务这一个前端,来使用不同的语言和技术来开发。当然了,如果要使用别的框架,也不是问题,我们只需要结合上一步中的<strong>自制框架兼容应用</strong>就可以满足我们的需求。</p>
<p>其次,采用这种方式还有一个限制,那就是:<strong>规范!<strong><em><em>规范!</em></em></strong>规范!</strong>。在采用这种方案时,我们需要:</p>
<ul>
<li>统一依赖。统一这些依赖的版本,引入新的依赖时都需要一一加入。</li>
<li>规范应用的组件及路由。避免不同的应用之间,因为这些组件名称发生冲突。</li>
<li>构建复杂。在有些方案里,我们需要修改构建系统,有些方案里则需要复杂的架构脚本。</li>
<li>共享通用代码。这显然是一个要经常面对的问题。</li>
<li>制定代码规范。</li>
</ul>
<p>因此,这种方式看起来更像是一个软件工程问题。</p>
<p>现在,我们已经有了四种方案,每个方案都有自己的利弊。显然,结合起来会是一种更理想的做法。</p>
<p>考虑到现有及常用的技术的局限性问题,让我们再次将目光放得长远一些。</p>
<h2>纯 Web Components 技术构建</h2>
<p>在学习 Web Components 开发微前端架构的过程中,我尝试去写了我自己的 Web Components 框架:<a href="https://link.segmentfault.com/?enc=Lzu2N5u7upG6ABdMp6GMbg%3D%3D.qpAuooio4hQxqaXdlZHDtfHFLIxYn3f9caPEV03Jgso%3D" rel="nofollow">oan</a>。在添加了一些基本的 Web 前端框架的功能之后,我发现这项技术特别适合于<strong>作为微前端的基石</strong>。</p>
<blockquote>Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的 Web 应用中使用它们。</blockquote>
<p>它主要由四项技术组件:</p>
<ul>
<li>Custom elements,允许开发者创建自定义的元素,诸如 <today-news></today-news>。</li>
<li>Shadow DOM,即影子 DOM,通常是将 Shadow DOM 附加到主文档 DOM 中,并可以控制其关联的功能。而这个 Shadow DOM 则是不能直接用其它主文档 DOM 来控制的。</li>
<li>HTML templates,即 <code><template></code> 和 <code><slot></code> 元素,用于编写不在页面中显示的标记模板。</li>
<li>HTML Imports,用于引入自定义组件。</li>
</ul>
<p>每个组件由 <code>link</code> 标签引入:</p>
<pre><code><link rel="import" href="components/di-li.html">
<link rel="import" href="components/d-header.html"></code></pre>
<p>随后,在各自的 HTML 文件里,创建相应的组件元素,编写相应的组件逻辑。一个典型的 Web Components 应用架构如下图所示:</p>
<p><img src="/img/remote/1460000015566939?w=538&h=459" alt="Web Components 架构" title="Web Components 架构"></p>
<p>可以看到这边方式与我们上面使用 iframe 的方式很相似,组件拥有自己独立的 <code>Scripts</code> 和 <code>Styles</code>,以及对应的用于单独部署组件的域名。然而它并没有想象中的那么美好,要直接使用<strong>纯</strong> Web Components 来构建前端应用的难度有:</p>
<ul>
<li>重写现有的前端应用。是的,现在我们需要完成使用 Web Components 来完成整个系统的功能。</li>
<li>上下游生态系统不完善。缺乏相应的一些第三方控件支持,这也是为什么 jQuery 相当流行的原因。</li>
<li>系统架构复杂。当应用被拆分为一个又一个的组件时,组件间的通讯就成了一个特别大的麻烦。</li>
</ul>
<p>Web Components 中的 ShadowDOM 更像是新一代的前端 DOM 容器。而遗憾的是并不是所有的浏览器,都可以完全支持 Web Components。</p>
<h2>结合 Web Components 构建</h2>
<p>Web Components 离现在的我们太远,可是结合 Web Components 来构建前端应用,则更是一种面向未来演进的架构。或者说在未来的时候,我们可以开始采用这种方式来构建我们的应用。好在,已经有框架在打造这种可能性。</p>
<p>就当前而言,有两种方式可以结合 Web Components 来构建微前端应用:</p>
<ul>
<li>使用 Web Components 构建独立于框架的组件,随后在对应的框架中引入这些组件</li>
<li>在 Web Components 中引入现有的框架,类似于 iframe 的形式</li>
</ul>
<p>前者是一种组件式的方式,或者则像是在迁移未来的 “遗留系统” 到未来的架构上。</p>
<h3>在 Web Components 中集成现有框架</h3>
<p>现有的 Web 框架已经有一些可以支持 Web Components 的形式,诸如 Angular 支持的 createCustomElement,就可以实现一个 Web Components 形式的组件:</p>
<pre><code>platformBrowser()
.bootstrapModuleFactory(MyPopupModuleNgFactory)
.then(({injector}) => {
const MyPopupElement = createCustomElement(MyPopup, {injector});
customElements.define(‘my-popup’, MyPopupElement);
});</code></pre>
<p>在未来,将有更多的框架可以使用类似这样的形式,集成到 Web Components 应用中。</p>
<h3>集成在现有框架中的 Web Components</h3>
<p>另外一种方式,则是类似于 <a href="https://link.segmentfault.com/?enc=t%2B35I%2F0KMjl6xABTLSYGzQ%3D%3D.kBh5dsgoQG3nO%2F9Z9Gii3im2pDp6p%2BwBIx5jIX3XV8tYJfrQr%2F470WKiE3%2Ftwjma" rel="nofollow">Stencil</a> 的形式,将组件直接构建成 Web Components 形式的组件,随后在对应的诸如,如 React 或者 Angular 中直接引用。</p>
<p>如下是一个在 React 中引用 Stencil 生成的 Web Components 的例子:</p>
<pre><code class="javascriptA">import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import 'test-components/testcomponents';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();</code></pre>
<p>在这种情况之下,我们就可以构建出独立于框架的组件。</p>
<p>同样的 Stencil 仍然也只是支持最近的一些浏览器,比如:Chrome、Safari、Firefox、Edge 和 IE11</p>
<h2>复合型</h2>
<p><strong>复合型</strong>,对就是上面的几个类别中,随便挑几种组合到一起。</p>
<p>我就不废话了~~。</p>
<h2>结论</h2>
<p>那么,我们应该用哪种微前端方案呢?答案见下一篇《微前端快速选型指南》</p>
<p>相关资料:</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=5R2dgwVnXOzT30LGfY6kVg%3D%3D.4zTR5TcR8bOs5%2FtlPeSc9swjudwkMJFE7cEwzz7yakFhkmSI%2BZMwyYwIio3H9dyQfIOUBYNIphsDrWciCBwY5D0zSBfTNiV6iYE1Go3aCh632SuIZbkThlbCaWgfPC6%2F" rel="nofollow">MDN 影子DOM(Shadow DOM)</a></li>
<li><a href="https://link.segmentfault.com/?enc=H1AJ04rAveE9lb%2FCMy2P7A%3D%3D.rkdgxDeiNAhpHORNs2H4YqgC1SYBEUW1cLG1y2mLHATe2nPxnpNHy6C8RKc8wY%2F8x%2BTpyr0cJkez%2FOXlCBjo2g%3D%3D" rel="nofollow">Web Components</a></li>
<li><a href="https://link.segmentfault.com/?enc=zeCq8KMjQq8GJOpQW6ZFAQ%3D%3D.unTBZtDcP76bd33VfE7yrT%2FvdoFmPF8ig9JfFfVUQfxkxkBaLClKobkqXjfFjZj%2FyXkO45rKD%2FsWai771%2BY7QyfVptfKhE6p1jjmQJo1jatVQ%2FhXaNVVJYkTNNZbuVkK" rel="nofollow">Shadow DOM v1:独立的网络组件</a></li>
</ul>
为什么微前端开始在流行:后端解耦,前端聚合
https://segmentfault.com/a/1190000015269379
2018-06-12T22:00:25+08:00
2018-06-12T22:00:25+08:00
phodal
https://segmentfault.com/u/phodal
9
<blockquote>采用新技术,更多不是因为先进,而是因为它能解决痛点。</blockquote>
<p>过去,我一直有一个疑惑,人们是否真的需要微服务,是否真的需要微前端。毕竟,没有银弹。当人们考虑是否采用一种新的架构,除了考虑它带来好处之外,仍然也考量着存在的大量的风险和技术挑战。</p>
<h2>前端遗留系统迁移</h2>
<p>自微前端框架 <a href="https://link.segmentfault.com/?enc=kpG4RJpIiZlini55xaqhkA%3D%3D.ubBCR27Twk7K9bzsxrvhkS2xPNv21WgcUYh476zbLcI%3D" rel="nofollow">Mooa</a> 及对应的《<a href="https://link.segmentfault.com/?enc=SWRZiyVPFUgdhBXyiayOsA%3D%3D.rw7H%2B%2BoHNYWpdbeEHlBlFgJJ7Ns22NPN%2FWTcRDWziGAOEvAuA9Xb7XandlB2nLvd" rel="nofollow">微前端的那些事儿</a>》发布的两个多月以来,我陆陆续续地接收到一些微前端架构的一些咨询。过程中,我发现了一件很有趣的事:<strong>解决遗留系统,才是人们采用微前端方案最重要的原因</strong>。</p>
<p>这些咨询里,开发人员所遇到的情况,与我之前遇到的情形并相似,我的场景是:设计一个新的前端架构。他们开始考虑前端微服务化,是因为遗留系统的存在。</p>
<p>过去那些使用 Backbone.js、Angular.js、Vue.js 1 等等框架所编写的单页面应用,已经在线上稳定地运行着,也没有新的功能。对于这样的应用来说,我们也没有理由浪费时间和精力重写旧的应用。这里的那些使用旧的、不再使用的技术栈编写的应用,可以称为遗留系统。而,这些应用又需要结合到新应用中使用。我遇到的较多的情况是:旧的应用使用的是 Angular.js 编写,而新的应用开始采用 Angular 2+。这对于业务稳定的团队来说,是极为常见的技术栈。</p>
<p>在即不重写原有系统的基础之下,又可以抽出人力来开发新的业务。其不仅仅对于业务人员来说, 是一个相当吸引力的特性;对于技术人员来说,不重写旧的业务,同时还能做一些技术上的挑战,也是一件相当有挑战的事情。</p>
<h2>后端解耦,前端聚合</h2>
<p>而前端微服务的一个卖点也在这里,去兼容不同类型的前端框架。这让我又联想到微服务的好处,及许多项目落地微服务的原因:</p>
<p>在初期,后台微服务的一个很大的卖点在于,可以使用不同的技术栈来开发后台应用。但是,事实上,采用微服务架构的组织和机构,一般都是中大型规模的。相较于中小型,对于框架和语言的选型要求比较严格,如在内部限定了框架,限制了语言。因此,在充分使用不同的技术栈来发挥微服务的优势这一点上,几乎是很少出现的。在这些大型组织机构里,采用微服务的原因主要还是在于,<strong>使用微服务架构来解耦服务间依赖</strong>。</p>
<p>而在前端微服务化上,则是恰恰与之相反的,人们更想要的结果是<strong>聚合</strong>,尤其是那些 To B(to Bussiness)的应用。</p>
<p>在这两三年里,移动应用出现了一种趋势,用户不想装那么多应用了。而往往一家大的商业公司,会提供一系列的应用。这些应用也从某种程度上,反应了这家公司的组织架构。然而,在用户的眼里他们就是一家公司,他们就只应该有一个产品。相似的,这种趋势也在桌面 Web 出现。<strong>聚合</strong>成为了一个技术趋势,体现在前端的聚合就是微服务化架构。</p>
<h2>兼容遗留系统</h2>
<p>那么,在这个时候,我们就需要使用新的技术、新的架构,来容纳、兼容这些旧的应用。而前端微服务化,正好是契合人们想要的这个卖点呗了。</p>
<p>你说呢?</p>
如何解构单体前端应用——微前端应用的微服务式拆分
https://segmentfault.com/a/1190000013826698
2018-03-19T12:42:30+08:00
2018-03-19T12:42:30+08:00
phodal
https://segmentfault.com/u/phodal
18
<blockquote>刷新页面?路由拆分?No,动态加载组件。</blockquote>
<p>本文分为以下四部分:</p>
<ul>
<li>前端微服务化思想介绍</li>
<li>微前端的设计理念</li>
<li>实战微前端架构设计</li>
<li>基于 Mooa 进行前端微服务化</li>
</ul>
<h2>前端微服化</h2>
<p>对于前端微服化来说,有这么一些方案:</p>
<ul>
<li>Web Component 显然可以一个很优秀的基础架构。然而,我们并不可能去大量地复写已有的应用。</li>
<li>iFrame。你是说真的吗?</li>
<li>另外一个微前端框架 Single-SPA,显然是一个更好的方式。然而,它并非 Production Ready。</li>
<li>通过路由来切分应用,而这个跳转会影响用户体验。</li>
<li>等等。</li>
</ul>
<p>因此,当我们考虑前端微服务化的时候,我们希望:</p>
<ul>
<li>独立部署</li>
<li>独立开发</li>
<li>技术无关</li>
<li>不影响用户体验</li>
</ul>
<h3>独立开发</h3>
<p>在过去的几星期里,我花费了大量的时间在学习 Single-SPA 的代码。但是,我发现它在开发和部署上真的太麻烦了,完全达不到独立部署地标准。按 Single-SPA 的设计,我需要在入口文件中声名我的应用,然后才能去构建:</p>
<pre><code>declareChildApplication('inferno', () => import('src/inferno/inferno.app.js'), pathPrefix('/inferno'));</code></pre>
<p>同时,在我的应用里,我还需要去指定我的生命周期。这就意味着,当我开发了一个新的应用时,必须更新两份代码:主工程和应用。这时我们还极可能在同一个源码里工作。 </p>
<p>当出现多个团队的时候,在同一份源码里工作,显然变得相当的不可靠——比如说,对方团队使用的是 Tab,而我们使用的是 2 个空格,隔壁的老王用的是 4 个空格。</p>
<h3>独立部署</h3>
<p>一个单体的前端应用最大的问题是,构建出来的 js、css 文件相当的巨大。而微前端则意味着,这个文件被独立地拆分成多个文件,它们便可以独立去部署应用。</p>
<h3>我们真的需要技术无关吗?</h3>
<p>等等,我们是否真的需要<strong>技术无关</strong>?如果我们不需要技术无关的话,微前端问题就很容易解决了。</p>
<p>事实上,对于大部分的公司和团队来说,技术无关只是一个无关痛痒的话术。当一家公司的几个创始人使用了 Java,那么极有可能在未来的选型上继续使用 Java。除非,一些额外的服务来使用 Python 来实现人工智能。因此,在大部分的情况下,仍然是技术栈唯一。</p>
<p>对于前端项目来说,更是如此:一个部门里基本上只会选用一个框架。</p>
<p>于是,我们选择了 Angular。</p>
<h3>不影响用户体验</h3>
<p>使用路由跳转来进行前端微服务化,是一种很简单、高效的切分方式。然而,路由跳转地过程中,会有一个白屏的过程。在这个过程中,跳转前的应用和将要跳转的应用,都失去了对页面的控制权。如果这个应用出了问题,那么用户就会一脸懵逼。</p>
<p>理想的情况下,它应该可以被控制。</p>
<h2>微前端的设计理念</h2>
<h3>设计理念一:中心化路由</h3>
<p>互联网本质是去中心化的吗?不,DNS 决定了它不是。TAB,决定了它不是。</p>
<p>微服务从本质上来说,它应该是去中心化的。但是,它又不能是完全的去中心化。对于一个微服务来说,它需要一个<strong>服务注册中心</strong>:</p>
<blockquote>服务提供方要注册通告服务地址,服务的调用方要能发现目标服务。</blockquote>
<p>对于一个前端应用来说,这个东西就是路由。</p>
<p><img src="/img/remote/1460000013826703?w=1200&h=430" alt="Menu" title="Menu"></p>
<p>从页面上来说,只有我们在网页上添加一个菜单链接,用户才能知道某个页面是可以使用的。</p>
<p><img src="/img/remote/1460000013826704?w=768&h=512" alt="404" title="404"></p>
<p>而从代码上来说,那就是我们需要有一个地方来管理我们的应用:**发现存在哪些应用,哪个应用使用哪个路由。</p>
<p><strong>管理好我们的路由,实际上就是管理好我们的应用</strong>。</p>
<h3>设计理念二:标识化应用</h3>
<p>在设计一个微前端框架的时候,为<strong>每个项目取一个名字的</strong>问题纠结了我很久——怎么去规范化这个东西。直到,我再一次想到了康威定律:</p>
<blockquote>系统设计(产品结构等同组织形式,每个设计系统的组织,其产生的设计等同于组织之间的沟通结构。</blockquote>
<p><img src="/img/remote/1460000013826705?w=700&h=682" alt="康威定律" title="康威定律"></p>
<p>换句人话说,就是同一个组织下,不可能有两个项目的名称是一样的。</p>
<p>所以,这个问题很简单就解决了。</p>
<h3>设计理念三:生命周期</h3>
<p>Single-SPA 设计了一个基本的生命周期(虽然它没有统一管理),它包含了五种状态:</p>
<ul>
<li>load,决定加载哪个应用,并绑定生命周期</li>
<li>bootstrap,获取静态资源</li>
<li>mount,安装应用,如创建 DOM 节点</li>
<li>unload,删除应用的生命周期</li>
<li>unmount,卸载应用,如删除 DOM 节点</li>
</ul>
<p>于是,我在设计上基本上沿用了这个生命周期。显然,诸如 load 之类对于我的设计是多余的。</p>
<h3>设计理念四:独立部署与配置自动化</h3>
<p>从某种意义上来说,整个每系统是围绕着应用配置进行的。如果应用的配置能自动化,那么整个系统就自动化。</p>
<p>当我们只开发一个新的组件,那么我们只需要更新我们的组件,并更新配置即可。而这个配置本身也应该是能自动生成的。</p>
<h2>实战微前端架构设计</h2>
<p>基于以上的前提,系统的工作流程如下所示:</p>
<p><img src="/img/remote/1460000013826706?w=800&h=795" alt="系统工作流" title="系统工作流"></p>
<p>整体的工程流程如下所示:</p>
<ol>
<li>主工程在运行的时候,会去服务器获取最新的应用配置。</li>
<li>主工程在获取到配置后,将一一创建应用,并为应用绑定生命周期。</li>
<li>当主工程监测到路由变化的时候,将寻找是否有对应的路由匹配到应用。</li>
<li>当匹配对对应应用时,则加载相应的应用。</li>
</ol>
<p>故而,其对应的架构如下图所示:</p>
<p><img src="/img/remote/1460000013826707" alt="Architecture" title="Architecture"></p>
<h3>独立部署与配置自动化</h3>
<p>我们做的部署策略如下:我们的应用使用的配置文件叫 <code>apps.json</code>,由主工程去获取这个配置。每次部署的时候,我们只需要将 <code>apps.json</code> 指向最新的配置文件即可。配置的文件类如下所示:</p>
<ol>
<li>96a7907e5488b6bb.json</li>
<li>6ff3bfaaa2cd39ea.json</li>
<li>dcd074685c97ab9b.json</li>
</ol>
<p>一个应用的配置如下所示:</p>
<pre><code class="javascript">{
"name": "help",
"selector": "help-root",
"baseScriptUrl": "/assets/help",
"styles": [
"styles.bundle.css"
],
"prefix": "help",
"scripts": [
"inline.bundle.js",
"polyfills.bundle.js",
"main.bundle.js"
]
}</code></pre>
<p>这里的 <code>selector</code> 对应于应用所需要的 DOM 节点,prefix 则是用于 URL 路由上。这些都是自动从 <code>index.html</code> 文件和 <code>package.json</code> 中获取生成的。</p>
<h3>应用间路由——事件</h3>
<p>由于现在的应用变成了两部分:主工程和应用部分。就会出现一个问题:<strong>只有一个工程能捕获路由变化</strong>。当由主工程去改变应用的二级路由时,就无法有效地传达到子应用。在这时,只能通过事件的方式去通知子应用,子应用也需要监测是否是当前应用的路由。</p>
<pre><code class="javascript">if (event.detail.app.name === appName) {
let urlPrefix = 'app'
if (urlPrefix) {
urlPrefix = `/${window.mooa.option.urlPrefix}/`
}
router.navigate([event.detail.url.replace(urlPrefix + appName, '')])
}</code></pre>
<p>相似的,当我们需要从应用 A 跳转到应用 B 时,我们也需要这样的一个机制:</p>
<pre><code>window.addEventListener('mooa.routing.navigate', function(event: CustomEvent) {
const opts = event.detail
if (opts) {
navigateAppByName(opts)
}
})</code></pre>
<p>剩下的诸如 Loading 动画也是类似的。</p>
<h2>使用 Mooa 进行</h2>
<p>So,我们就有了前端微服务框架 Mooa。它基于 <a href="https://link.segmentfault.com/?enc=jE4bmLp6yqozEm%2BcUr6e%2BA%3D%3D.q4x39kr7%2F6dIZ7pa8%2FG%2BSbPsjK%2F6UaeDBiSTQmxCrKgnHuviimrrnk4xvGs%2FIJ6g" rel="nofollow">single-spa</a> && <a href="https://link.segmentfault.com/?enc=zgpRlBnoAIBsI1OLL1yPLw%3D%3D.c1Wc1C6XnQ0kS4eCBI2fKFBLXvkceXMIXLtes4g%2FqN5bN2f20sXME%2BsgZk3mzrrLjFhwqEwCRpOg14ch%2BHydrA%3D%3D" rel="nofollow">single-spa-angular-cli</a>,并且符合以上的设计思想。</p>
<p>GayHub 地址:<a href="https://link.segmentfault.com/?enc=swgw1NbSBomL0eg%2FzILcKg%3D%3D.512dIy6Kq6ewjlCtS3ozPOQd4c8X8yZ4Vf%2BWWs6zeHk%3D" rel="nofollow">https://github.com/phodal/mooa</a></p>
<p>对于主工程而言,只需要以下的几行代码就可以完成上面的功能:</p>
<pre><code class="javascript">http.get<any[]>('/assets/apps.json')
.subscribe(data => {
data.map((config) => {
that.mooa.registerApplication(config.name, config, mooaRouter.matchRoute(config.prefix));
});
this.mooa.start();
});
this.router.events.subscribe((event: any) => {
if (event instanceof NavigationEnd) {
that.mooa.reRouter(event);
}
});</code></pre>
<p>并添加一个对应的子应用路由:</p>
<pre><code class="javascript">{
path: 'app/:appName/:route',
component: HomeComponent
}</code></pre>
<p>则如上所述的四个步骤。</p>
<p>对于子工程而言,则只需要一个对应的 Hook 操作:</p>
<pre><code class="javascript">mooaPlatform.mount('help').then((opts) => {
platformBrowserDynamic().bootstrapModule(AppModule).then((module) => {
opts['attachUnmount'](module);
});
});</code></pre>
<p>并设置好对应的 base_href:</p>
<pre><code>providers: [
{provide: APP_BASE_HREF, useValue: mooaPlatform.appBase()},
]</code></pre>
<p>嗯,就是这么简单。DEMO 视频如下:</p>
<p>Demo 地址见:<a href="https://link.segmentfault.com/?enc=G17Ig%2FqBkrZg2NkFVR679g%3D%3D.rDAm1xOFzwWKxpJ43BJaQZk2L2Ydye3M%2B8HdOv%2FvnaU%3D" rel="nofollow">http://mooa.phodal.com/</a></p>
<p>GitHub 示例:<a href="https://link.segmentfault.com/?enc=hRKLhKtsFxpE5GYlSlscyg%3D%3D.hCz4SzlSkmO4j0WteC0e0HzQJ5nxEt0JNfPso4%2B7Ky8%3D" rel="nofollow">https://github.com/phodal/mooa</a></p>
2017 年节点——我的技术投资学习策略:超越前端、物联网、事件驱动
https://segmentfault.com/a/1190000012729621
2018-01-06T18:19:30+08:00
2018-01-06T18:19:30+08:00
phodal
https://segmentfault.com/u/phodal
6
<p>软件开发不是一份稳定的工作:每年都会涌现一个又一个新的技术,每隔几年都会出现一些革命性的技术。尽管从代码、表现及差异上来看,新技术和旧的技术有一些概念上的相似,但是在使用的过程中,仍然是需要花费时间去学习的。</p>
<p>除了,加深自己对现有一些语言、技术、软件工程的领域深度。为了保持一定的技术洞见,每年我们都会学习一些新的技术,使用一些新的技术栈。因此,这篇文章就是介绍一些我对于新技术的感受,以及在这一年学习中的一些经验。</p>
<p>本文主要包含以下内容:</p>
<ul>
<li>回顾前端:超越交互</li>
<li>回顾移动开发</li>
<li>技术投资策略</li>
<li>物联网与去中心化</li>
<li>AI 与技术成熟度</li>
<li>Serverless 与事件驱动</li>
<li>区块链评估</li>
</ul>
<p>(PS:本文的阅读时间大概 10 分钟)</p>
<h2>回顾前端:超越交互</h2>
<p>年初,我在写电子书《我的职业是前端工程师》的时候,已经 “钦定” 了 React、Angular(没有.js)、Vue 作为未来前端开发的主流趋势。在国内来说,还是以 Vue 为主,Vue 好上手,但是不都是所有的用户都是小白 :) 。但是作为一个前端开发人员,你把时间投资在 React 和 Angular 上,在未来你会有更大的赢面。</p>
<p>Facebook 一系的 React,还有 React Native 和 React VR,未来可能还会有 React Game。Angular 受<strong>奥斯本效应</strong>的影响,采用程度受到一定的影响。在目前来看,对于开发人员的主要成长得益于 TypeScript,对于项目的优势主要静态类型使得代码更加强壮。</p>
<p>可是这一年 AI 的火热,让我开发思考 AI 带来的一些交互上的变化。现在,我们所指的前端多数是一些 Web、移动端相关的 UI 开发,其本质就是与用户交互。诸如前几年火热的虚拟现实、智能音箱,都是一个交互的新入口。</p>
<p>可视化仍然是人类最好的<strong>获取数据</strong>的方式。可当用户大量的日常工作,被 AI 自动化之后,有些工作就变得有些多余。如你出门前要看的天气,以决定穿什么衣服,它都可以由计算机算出来。带不带雨伞,都可以由雨伞提醒你。如 Amazon 设计的 Dash Button,可以让你不需要任何的 UI 就可以购买日常用品。</p>
<h2>回顾移动开发</h2>
<p>今年,我在工作上主要做的都是移动开发相关的工作,从基于 Ionic 与 Cordova 的混合应用,到嵌入 WebView 到 React Native,再到年底的 Android 原生应用,中间写了一些 Cordova 和 React Native 的 Android、iOS 插件。也算是见证了移动应用的不同技术选型,成为了 3/4 个移动应用开发,对整个移动端的技术有所了解。</p>
<p>在最近几年里,原生移动应用开发没有像过去那么火热。一来是,受到混合应用和 React Native 的影响;二来是,移动端的开发技术已经趋于成熟;</p>
<p>尽管在年初,看到越来越多的原生应用开发人员正在转向 React Native。过去,我们认为的操作系统市场份额是这样的:</p>
<p><img src="/img/remote/1460000012729626?w=2314&h=1160" alt="桌面操作系统份额" title="桌面操作系统份额"></p>
<p>但是实际上,在讨论市场份额的时候已经是这样了:</p>
<p><img src="/img/remote/1460000012729627?w=2318&h=1124" alt="总的操作系统份额" title="总的操作系统份额"></p>
<p>以 VR、AR 和 AI 的技术趋势来看,移动平台才是未来的重点——除非有更好的平台出现。触手可及的技术,拥有更多的可能性,也拥有更多的数据及美好的未来。</p>
<h2>技术投资策略</h2>
<p>(PS:在这里,所谓的技术投资是指,在一定时期内向一定的新领域投放时间来学习新技术。)</p>
<p>新的技术每天都在不断地涌现,很多就是黑天鹅一样看不出。并且,一来我们已经习惯了新的轮子的产生,二来我们也<strong>没有那么多的精力</strong>去关心每一个新技术。可是当有一些技术在不断被<strong>提及被应用</strong>的时候,你就差不多应该关注、学习、采用了。</p>
<p>如我在年初看到那一本《灰犀牛》:<strong>灰犀牛体型笨重、反应迟缓,你能看见它在远处,却毫不在意,一旦它向你狂奔而来,定会让你猝不及防,直接被扑倒在地</strong>。你明明看得见,新的趋势在那里,却固执的不肯去了解。那么,未来你就轻松地进入了中午危机。</p>
<p><img src="/img/remote/1460000012729628" alt="技术投资" title="技术投资"></p>
<p>在 Gartner 的技术曲线上,定义了五个阶段:科技诞生的促动期、过高期望的峰值、泡沫化的底谷期、稳步爬升的光明期、实质生产的高峰期。对于个人来说,这个技术曲线就没有那么复杂。</p>
<h3>成为早期投入者</h3>
<p>对于一个技术人员来说,这个流程就比较简单了。通常来说,作为一个初入这个行业几年的 “新人” 来说,我们是没有经验、能力去开扩一个新的领域。这时成为早期投资者,可能就是最早期的选择了。</p>
<p>在新技术的早期就采用它,<strong>是一件相当有风险的事,也可能会有相当大的收益</strong>。新技术在早期充满了不确认性,如果你押错了,那么你在这段时间的时间可能就白费了。因此,一般是到一个新的技术、概念出现一段时间后,去填这些相关的坑。</p>
<p>如我在这一年进入 Serverless 的坑一样,虽然有一些晚,但是好在国内的云服务产商的服务还不怎么行。通过使用感受、社区及 Google Trends 来看,来看看是否押错地方。</p>
<h3>在光明期前采用</h3>
<p>通常意味着,这个新技术栈还有很多坑。但是,大家都认可它是一个有前途的技术。如在今年被大规模采用(淘宝、QQ 等,有一些因为版权已经换坑了)的 React Native 就是一个很好的例子。</p>
<p>一般来说,经历了一些大公司的踩坑,以及网上的一些踩坑经验,那么就意味着采用它已经没有多少问题了——前提是你已经做好了技术储备。这个时候你就可以为了今年的 KPI,去说服你的领导了。我们应该这么做,xx 公司都已经上线了,而且这些 blabla 的优势很明显。</p>
<h3>跟随项目使用</h3>
<p>当一个新技术的采用,已经在市场上达成共识。那么,也不要再采用旧的技术了。</p>
<p>到了今天,前端的<strong>单页面应用</strong>已经是 React、Angular、Vue 的天下了。这时要开发一个<strong>复杂的前端应用</strong>,那么你应该从这些主流的框架中去选择,而不是采用早期、过期(已经不维护)的技术了。除非,你们是一家大公司,有自己的轮子。</p>
<p>当然,只在该用某技术的时候,才在项目上使用新技术。对于一些简单的页面,jQuery + Bootstrap 还是万能的。为了使用新技术,而在项目上使用,有时候容易造成问题。</p>
<h2>物联网与去中心化</h2>
<p>在这一年里,为 InfoQ 编写《物联网周报》的时候,我花费了大量的时间在索引互联网上的 IoT 相关内容。随之也翻译了一系列的文章,也有了 “<a href="https://link.segmentfault.com/?enc=Vt5Tu2%2BSWJH1GU0%2FnM5QdA%3D%3D.%2FE6Y8NzlHTvL8QaU%2BlSzgOlBSPSvVn40%2FFqy4gGnKzk%3D" rel="nofollow">玩点什么</a>” 网站 、APP和小程序,其目的主要用于储备技术。通过翻译一些物联网及前沿的技术实践——<strong>持续关注某一领域,可以通过翻译相关的文章来投资</strong>。将一些国内没有的,又可能在未来用到的技术翻译成了中文,再增加一些提升 SEO 提名的文章。(PS:因为在我的<a href="https://link.segmentfault.com/?enc=KiONq81HDibnvs030GEqHA%3D%3D.EYekCPkKt0pYq74loqFM6xnRP%2F8uAVYo%2Fi1d4yLVeec%3D" rel="nofollow">技术博客</a>)上,650+ 的博客大部分都是原创的技术笔记,所以不打算放在我的博客上——同时,也可以避免版权带来的问题。)</p>
<p>除了,我大学学的电子信息工程;还有一点,硬件是虚拟世界与现实世界的接口,而物联网则可以让我们真实的、从远程访问世界的各个角落。于是,在年中的时候,我花费时间去研究各式各样的智能家居系统,也有了我的 <a href="https://link.segmentfault.com/?enc=KK9br9OlHJLv%2F1zbnwAErQ%3D%3D.p1YFSFE37KtWJYFuNXNain4cnjjMSgLChM%2BSnlplDHlYQj2%2B75GQMhJ5l2jnPbOr" rel="nofollow">smart-home</a> 项目。</p>
<p><strong>当自己擅长的技术,有一些新的突破,那么就应该去学习它。</strong></p>
<p>2017 年里,IoT 领域的技术趋势,已经在不断地结合各种时髦的技术,如:</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=zC0pmO589nU1yU2o3pAPxg%3D%3D.O4pfRpvvcoj1b9rQ6DhCc%2BLetNRKx7%2F0qmQeq2UZcfbJCvUqApykMCApO8%2FBohUVuLJ06z97iLAtwspfDu7c4hLySL2DGxz6ueASD%2Fp4wbtUJT%2FzgObiu%2BKy0GrS2UaDT6xn2t2iJu%2BkkfAqrDfSjQ%3D%3D" rel="nofollow">Serverless Framework 与 AWS IoT 构建 Serverless 的花园监控系统</a></li>
<li><a href="https://link.segmentfault.com/?enc=gOoCkGnfWzE07VpQliq9Ew%3D%3D.q3iAgdwLrDRQ7rGco6ebZ%2BZMS9hgPiYlIvQlbWEBanLs%2F9wLr6T5X9XCEQqzWSMlfxhQ8jiRteN8%2FhmI7cELSHWhDqGFqPi%2BwO5ySUnm1HB%2B9g4Kl3j%2BV8oTIGXFcYJs" rel="nofollow">Raspberry Pi 教程:在 Docker 上使用 Go 点亮 LED</a></li>
<li><a href="https://link.segmentfault.com/?enc=Y%2BY46p8YZrft6bhgz%2Fc2%2FA%3D%3D.21l9XaUB8Yg2kppA%2Bm32NMFJdB3s1RRG%2BIfeFQg9jEvK2i16sr8a2gPDLPhNr0w4h7pJ8DUinOqhsWVyq4JJJzs449UzsVuBZEosYmP4rxtEEem8qTKFF0y%2BmRsH7mbm" rel="nofollow">Raspberry Pi 与 OpenFaaS 构建 Serverless Kubernetes 集群</a></li>
<li><a href="https://link.segmentfault.com/?enc=w6tgKDYpkPF0yKUauieNXg%3D%3D.b78pu6VyhP2eqUjnkCGH4qYDwwcgpoWGh7hy8lNX784Kt7Rw96uHu219JONmD06WbTSLcbe0fWVDgOIaHuHrsLnRsLrOjK1gY31FC8RZkWKed7Ftm4Ymb9239Ylo1LFM" rel="nofollow">Raspberry Pi 自动驾驶:使用 Keras 与 Tensorflow 构建车道跟踪自动汽车</a></li>
</ul>
<p>他们解决了一些嵌入式设备的自身缺陷,不过在一年里,<strong>安全仍然是这方面的主流考虑</strong>。</p>
<blockquote>当一个流行的技术能在 PC 上运行,它也终将在智能手机、Raspberry Pi 这一类小型计算设备上运行。</blockquote>
<p>在数字化浪潮不断扩张的今天,物联网作为一个底层的技术合集,越来越受关注。但是,这不意味着它会马上火爆起来,只是我们可以看到它在不断地被<strong>应用</strong>。</p>
<p>在这上面花时间,不一定有收益,但是总觉得<strong>自己改变了什么</strong>。</p>
<h2>AI 与技术成熟度</h2>
<p>AI 火热的这一年,在为玩点什么做应用的过程中,便想结合一些机器学习的技术。所以我又复习了一下机器学习,顺便打算 GET 一下深度学习的技能。也就有了玩点什么的<a href="https://link.segmentfault.com/?enc=8uSbv%2FlMnspjYgi5JBMlmg%3D%3D.qbpC3punGssbiEuBVjhkRNEVGyVcctGlAFMtZ4sE4i9EV81YDQUGWNTT%2B7Y3%2FjJ2LBO8ck4pnuF64OWTwTVoZGCp0r4ued5hy0oDjC5KJqrjrutc3NJZYHb%2BFdf5R%2BBU" rel="nofollow">推荐系统</a> ,也应用上了 jieba 分词,加上之前对于朴素贝叶斯、聚类等的研究。发现一般的机器学习,对于我这种已经远离高等数学的人来说,还不算太难,概念上基本能理解得差不多——主要是这些算法,都能应用到一些真实的场景。</p>
<p>于是,我打算试水一下深度学习。然后,我发现我 TM 的数据都没有,下载个数据都要半天。再看看书上的理论,我发现我抽不到足够的空余时间来学习——我的意思是,<strong>足够集中的学习时间</strong>。还有一个问题是,没有<strong>数据</strong>——尽管网上已经有大量的公开数据,但是这些公开数据多数是英语的。这些基于英语语言的数据,会导致出现的算法以英语为主,导致算法本身难以采用,这一类问题最常见于 NLP(自然语言学习)。</p>
<p>假使我将一个周末的两天 + 每天三个小时的晚上时间都投在上面,那么从短期来看收益太少——我可以花这些时间,强化我在其它领域的知识。</p>
<p>好在深度学习的 API,也很容易上手。这一点与我之间在试水智能音箱有点类似,实现一个简单的智能音箱本身并不需要太多的新算法——利用一些现有的公开 API、公开算法,我们可以轻松也用深度学习做一个音箱。但是我们缺乏足够的音频数据,它将会导致我们无法进入下一步。</p>
<p>这就是为什么 Mozilla 会推出 Common Voice Project 项目的原因,没有足够的财力就不会有足够的数据;同理于,今年阿里在双 11 送的大量 99 元的天猫精灵一样,只有大量的数据才能造出 AI。<strong>只能有足够的数据或者财富,才能拥有与之匹配的人工智能</strong>。</p>
<h2>Serverless 与事件驱动</h2>
<p>在第四个季度里,我花费了大量的时间在学习 Serverless 系统,编写大量的 Serverless 应用。有兴趣的同学可以了解 <a href="https://link.segmentfault.com/?enc=4j%2FMKGmbnMp2Vkx7X5p3%2BQ%3D%3D.X3tWWJFYx%2FA7jSyuDiwXdvYZHI6q%2Fdto16cLu7PNHTyGD9JIMSBxkMMQElcEMkUbunb7jUe%2FldeyixXP0RELY4WpRxuEnQHZIe2HpqlM6XbAQcr%2Bt3CzmXgTKfQe1Rw6" rel="nofollow">花了 1000G,我终于弄清楚了 Serverless 是什么</a>,以及基于 Serverless 架构的 <a href="https://link.segmentfault.com/?enc=gj4Q1e%2FGApoPMyF3Q4JDlg%3D%3D.%2FgIkL4BaZt6YB8Hk8MQOKj5sVdBs6fyc16f4jQqcXy4%3D" rel="nofollow">简单日志系统 molog</a>、<a href="https://link.segmentfault.com/?enc=W9RR1KlAncbrHtfQGr%2BXmg%3D%3D.lpUv5L15R%2Bmf77er5Fld%2B8JtJP95FwbbTh0lNtHQmvduZVldOL02GOgbwakMsz%2BP" rel="nofollow">表单系统 moform</a>,这些 Serverless 应用都可以在一天内开发、上线。</p>
<p>Serverless 事件驱动的特性,使得它适合物联网、人工智能、数据分析等场景。其按需付费,又能让我愉快地(毕竟穷)各种 Serverless 应用。</p>
<p>如上所示,Serverless 将是一种新的技术架构趋势。而 Serverless 本身算不上一个新的技术,只是 FaaS 结合一堆现有的技术组成的技术架构。因此我将学习 Serverless 的行为,定义为<strong>一种 “短平快” 的学习投资</strong>。</p>
<p>由于国内的内容匮乏,我采用的策略是:边写 DEMO,边写文章、编写自己的 Serverless 应用、写一系列的总结文章、整理成电子书。这样一来在 2018 年,我的博客可能就会受长尾效应的影响,带来额外的更多流量。</p>
<h2>区块链评估</h2>
<p>我对区块链采用的是评估,也就是还没有进入我的采用、Demo 期——主要是因为区块链技术采用的领域有限。早期我在思考在边缘计算、物联网领域引入,但是场景有限。不过,在未来我应该会尝试引入这个技术的。</p>
<p><strong>常规的物联网系统都是中心化的</strong>,采集数据、加工数据、存储数据、可视化数据、预测数据,它也不需要担心中间的数据修改。如果以一个传感器采集点,作为一个节点,采用区块链的存储成本过高。如果以一个区域作为结点,它也不需要担心其它结点的数据的修改问题。</p>
<p><strong>只当每一个节点自身需要记录变更</strong>的时候,那么这个物联网应用才适合采用区块链技术,如物流场景等等。</p>
<p>又或者是,在你的智能家居系统的每一个控制节点上,都记录每一次控制的变化。如你使用手机控制了台灯的开和关,这个数据就会同步到你的手机上。问题是,控制个灯都需要记录一下的场景,这可能是监狱吧?</p>
<p>或许,我还需要一个区块链的 markdown 同步工具,而不是使用 GitHub 或者各种云笔记。这样我的 <a href="https://link.segmentfault.com/?enc=6t5p3ImnMgIwtS8KOyWxtA%3D%3D.9m6cpGXLVa%2BIxJHGgQC429eiyW4bRMX1bS2foK2WmUyzr43ylPRfVQPk28VCF7FN" rel="nofollow">markdown-improve</a> 计划,又多了一个新的成员了。</p>
<h2>结论</h2>
<p>从上述的学习经历来看,学习技术的期限主要是:</p>
<ul>
<li>短期学习未来将采用的技术</li>
<li>中期了解一些新的技术知识</li>
<li>长期接触自己喜欢的技术</li>
</ul>
<p>不过,简单的来说,这一年的经验就是:</p>
<ul>
<li><strong>持续关注某一领域,可以通过翻译相关的文章来投资</strong></li>
<li><strong>当自己擅长的技术,有一些新的突破,那么就应该去学习它。</strong></li>
<li><strong>如果一个与自己工作相关的技术是未来的趋势,那么请拥抱它。</strong></li>
<li><strong>如果有一个新的技术可以影响到你的生活,不妨去学学它。</strong></li>
</ul>
花了1000G,终于弄清楚了Serverless (中):Serverless 架构的优缺点
https://segmentfault.com/a/1190000012088527
2017-11-21T07:21:58+08:00
2017-11-21T07:21:58+08:00
phodal
https://segmentfault.com/u/phodal
4
<h2>Serverless 的优势</h2>
<p>在我使用 Serverless Framework 开发 AWS Serverless 应用的过程中,最方便的莫过于,第一次部署和第二次、第三次部署没有什么区别。只需要执行 <code>serverless deploy</code>,几分钟后,我们代码就运行在线上。如果是一个传统的 AWS 应用,我需要 SSH 到我的服务器上部署,这样才能写好我的自动部署脚本。除此,我还需要担忧这个过程中,有哪些用户有使用。</p>
<p>除了,我觉得的部署方便,还有就是价格合理。我的 AWS EC2 实例上运行着我的博客、以及其他的一些网络。然而,我那 PV 只有 500 左右的博客,大部分时间都是在空转。便觉得有些浪费,可是运行才收费的 Serverless 就不会有这样的问题。可以让我大胆地去使用这些服务。当然了,还有其它一些显著的优势。</p>
<h3>降低启动成本</h3>
<p>当我们作为一家公司开发一个 Web 应用时,在开发的时候,我们需要版本管理服务器、持续集成服务器、测试服务器、应用版本管理仓库等作为基础的服务。线上运行的时候,为了应对大量的请求,我们需要一个好的数据库服务器。当我们的应用面向了普通的用户时,我们需要:</p>
<ul>
<li><p>邮件服务,用于发送提醒、注册等服务</p></li>
<li><p>短信服务(依国家实名规定),用于注册、登录等用户授权操作</p></li>
</ul>
<p>对于大公司而言,这些都是现成的基础设施。可对于新创企业来说,这都是一些启动成本。</p>
<h4>减少运营成本</h4>
<p>对于初创公司来说,他们没有基础设施,也没有财力,也可能没有能力去建设基础设施。采用云服务往往是最好的选择,可以节省大量的资金。他们可以将注意力放在:创造对用户有价值的产品上。如果一家创业公司采用云服务,而不是自己搭建服务器。那么,他就会拥有<strong>更多的时间</strong>开发业务功能,而不是关注在这些。只需要为运行时的软件付钱。</p>
<p>而采用<strong>函数计算</strong>的 Serverless 与云服务器最大的不同之处在于:<strong>云服务器需要一直运行,而函数计算是按需计算</strong>。按需计算就意味着,在请求到来的时候,才运行函数。没有请求的时候,是不算钱的。</p>
<p>项目初期,其用户数往往是缓慢增长的,而我们在选择服务器的时候,往往会依可能出现的用户来估算。在这个时候,往往会浪费一些不必要的成本。不过,就算用户突然间爆发,Serverless 应用也可以轻松处理。只需要修改一下数据库配置,再重新部署一份。</p>
<h4>降低开发成本</h4>
<p>一个成功的 Serverless 服务供应商,应该能提供一系列的<strong>配套服务</strong>。这意味着,你只需要在配置文件上写下,这个数据库的表名,那么我们的数据就会存储到对应的数据库里。甚至于,**如果一个当服务提供者提供一系列的函数计算模板,那么我们只需要写好我们的配置即可。这一系列的东西都可以自动、高效的完成。</p>
<p>在这种情况下,<strong>使用某一个云服务,就会调用某一个系统自带的 API 一样简单</strong>。</p>
<p>当然,将应用设计成无状态应用,对于早期的系统,可能是一种挑战。除此,诸如 AWS 这样庞大的系统,对于新手程序员来说,也不能容易消化掉的一个系统。</p>
<h3>实现快速上线</h3>
<p>对于一个 Web 项目来说,启动一个项目需要一系列的 hello, world。当我们在本地搭建环境的时候,是一个 hello, world,当我们将程序部署到开发环境时,也是一个部署相关的 hello, world。虽然看上去有些不同,但是总的来说,都是 it works!。</p>
<p>Serverless 在部署上的优势,使得你可以轻松地实现上线。</p>
<h4>更快的部署流水线</h4>
<p>实际上,Serverless 应用之所以在部署上有优势,是因为其相当于<strong>内建自动化部署</strong>——我们在开发应用的时候,已经在不断地增强部署功能。</p>
<p>在我们日常的开发中,为了实现自动化部署,我们需要先手动部署,以设计出一个相关无错的部署配置,如 Docker 的 Dockerfile,又或者是 Ansible 的 playbook。除此,我们还需要设计好蓝绿发布等等的功能。</p>
<p>而在函数计算、Serverless 应用里,这些都是由供应商提供的功能。每次我们写完代码,只需要运行一下:<code>sls deploy</code> 就足够了。在诸如 AWS Lambda 的函数计算里,函数一般在上传后几秒钟内,就能做好调用准备。</p>
<p>这就意味着,当我们和日常一样,使用一个模板来开发我们的应用。我们就可以在 Clone 完代码后的几分钟内,完成第一次部署。</p>
<p>唯一的难点,可能是要选用什么配置类型的服务,如选用哪个级别吞吐量的 DynamoDB、哪个内存大小的 Lambda 计算。</p>
<h4>更快的开发速度</h4>
<p>由于 Serverless 服务提供者,已经准备好了一系列的基础服务。作为开发人员的我们,只需要关注于如何更好去实现业务,而非技术上的一些限制。</p>
<p>服务提供者已经向我们准备,并测试好了这一系列的服务。它们基本上是稳定、可靠的,不会遇上特别大的问题。事实上,当我们拥有足够强大的代码,如使用测试来保证健壮性,那么结合持续集成,我们就可以在 PUSH 代码的时候,直接部署到生产环境。当然,可能不需要这么麻烦,我们只需要添加一个 predeploy 的 hook,在这个 hook 里做一些自动测试的工作,就可以在本地直接发布新的版本。</p>
<p>这个过程里,我们并不需要考虑太多的发布事宜。</p>
<h3>系统安全性更高</h3>
<p>依我维护我博客的经验来看,要保持服务器一直运行不是一件容易的事。在不经意的时候,总会发现有 Cracker 在攻击你网站。我们需要防范不同类型的攻击,如在我的服务器里一直有黑客在尝试密码登录,可是我的博客的服务器是要密钥才能登录的。在一次神奇的尝试登录攻击后,我的 SSH 守护进程崩溃了。这意味着,我只能从 EC2 后台重启服务器。</p>
<p>有了 Serverless,我不再需要担心有人尝试登录系统,因为我都不知道怎么登录服务器。</p>
<p>我不再需要考虑系统底层安全问题,每次登录 AWS EC2,我总需要更新一遍软件;每当我看到某个软件有漏洞时,如之前的 OpenSSH,我就登录上去看一下版本,更新一下软件。真 TM 费时又费力,还没有一点好处。</p>
<p>唯一需要担心的,可能是有人发起 DDOS 攻击。而根据<a href="https://link.segmentfault.com/?enc=vqtwYEN6JcgNdI5tvqVo0w%3D%3D.n3besxSW1Y0g8zBo9RpvIfSgy3nefP9oX1L4BkQu9bfGAIa%2BFCv0GDSSNMBWz%2FNLUFKu9zzjwNz7aW9APJnNTQ%3D%3D" rel="nofollow">Could Zombie Toasters DDoS My Serverless Deployment?</a>的计算,每百万的请求,大概是 0.2 刀,每小时 360000000 个请求,也就 72 刀。</p>
<h3>适应微服务架构</h3>
<p>如我们所见在最近几年里看到的那样,微服务并不没有大量地替换掉单体应用——毕竟使用新的架构来替换旧的系统,在业务上的价值并不大。因此,对于很多企业来说,并没有这样的强烈需求及紧迫性。活着,才是一件更紧迫的事。</p>
<p>而 Serverless 天生就与微服务架构是<strong>相辅相成</strong>的。一个 Serverless 应用拥有自己的网关、数据库、接口,你可还以使用自己喜欢的语言(受限于服务提供者)来开发服务。换句话来说,在这种情形下,一个 Serverless 可能是一个完美的微服务实例。</p>
<p>在可见的一二年里,Serverless 将替换到某些系统中的一些组件、服务。</p>
<h3>自动扩展能力</h3>
<p>Serverless 的背后是 诸如 AWS Lambda 这样的 FaaS(Function as a Services)。</p>
<p>对于传统应用来说,要应对更多的请求的方式,就是部署更多的实例。然而,这个时候往往已经来不及了。而对于 FaaS 来说,我们并不需要这么做,FaaS 会自动的扩展。它可以在需要时尽可能多地启动实例副本,而不会发生冗长的部署和配置延迟。</p>
<p>这依赖于我们的服务是无状态的,我们才能次无忌惮地不断运行起新的实例。</p>
<h2>Serverless 的问题</h2>
<p>作为一个运行时,才启动的应用来说,Serverless 也存在着一个个我们所需要的问题。</p>
<h3>不适合长时间运行应用</h3>
<p>Serverless 在请求到来时才运行。这意味着,当应用不运行的时候就会进入 “休眠状态”,下次当请求来临时,应用将会需要一个启动时间,即<strong>冷启动</strong>。这个时候,可以结合 CRON 的方式或者 CloudWatch 来定期唤醒应用。</p>
<p>如果你的应用需要一直长期不间断的运行、处理大量的请求,那么你可能就不适合采用 Serverless 架构。在这种情况下,采用 EC2 这样的云服务器往往是一种更好的选择。因为 EC2 从价格上来说,更加便宜。</p>
<p>引用 <a href="https://link.segmentfault.com/?enc=PrT3cXqnfq9J9mX4VctO4g%3D%3D.p9CziUH4DAGfCuKSx5EGjjarA1ptvrQzuWVAMhVDW14jUldHecCaQRMlPYDWQ6mH" rel="nofollow">Lu Zou</a> 在 《<a href="https://link.segmentfault.com/?enc=se%2Bbd7BXerQeInRNKeMZow%3D%3D.OGZlXa886bQvSdy5Y%2FQmQxqjkUKFQvuFIlZjuNZMAFps08R3p7DUi5FlxbvEMK3j" rel="nofollow">花了 1000G,我终于弄清楚了 Serverless 是什么(上):什么是 Serverless 架构?</a>》上的评论:</p>
<blockquote><p>EC2 相当于你买了一辆车,而 Lambda 相当于你租了你一辆车。</p></blockquote>
<p>长期租车的成本肯定比买车贵,但是你就少掉了一部分的维护成本。因此,这个问题实际上是一个值得深入计算的问题。</p>
<h3>完全依赖于第三方服务</h3>
<p>是的,当你决定使用某个云服务的时候,也就意味着你可能走了一条不归路。在这种情况下,只能将不重要的 API 放在 Serverless 上。</p>
<p>当你已经有大量的基础设施的时候,Serverless 对于你来说,并不是一个好东西。当我们采用 Serverless 架构的时候,我们就和特别的服务供应商绑定了。我们使用了 AWS 家的服务,那么我们再将服务迁到 Google Cloud 上就没有那么容易了。</p>
<p>我们需要修改一下系列的底层代码,能采取的应对方案,便是建立隔离层。这意味着,在设计应用的时候,就需要:</p>
<ul>
<li><p>隔离 API 网关</p></li>
<li><p>隔离数据库层,考虑到市面上还没有成熟的 ORM 工具,让你即支持 Firebase,又支持 DynamoDB</p></li>
<li><p>等等</p></li>
</ul>
<p>这些也将带给我们一些额外的成本,可能<strong>带来的问题会比解决的问题多</strong>。</p>
<h3>冷启动时间</h3>
<p>如上所说,Serverless 应用存在一个冷启动时间的问题。</p>
<p>据 New Relic 官方博客《<a href="https://link.segmentfault.com/?enc=t6krwPeSXtnWCtx4KmTVnA%3D%3D.jEDZSbsE%2BjxMEq2PQfKDqVQ6AWxYtZSA820Td5mAq6JjcBjwcD7pwZfbuKhta%2FUNff7%2F6wddTpYcdpt0XPsZ4EPNoqeuAGfgBDbDi5hwqpo%3D" rel="nofollow">Understanding AWS Lambda Performance—How Much Do Cold Starts Really Matter?</a>》称,AWS Lambda 的冷启动时间。</p>
<p><img src="/img/remote/1460000012088532?w=975&h=246" alt="AWS 启动时间" title="AWS 启动时间"></p>
<p>又或者是我之前统计的请求响应时间:</p>
<p><img src="/img/remote/1460000012042635?w=2326&h=428" alt="Serverless 请求时间" title="Serverless 请求时间"></p>
<p>尽管这个冷启动时间大部分情况下,可以在 50ms 以内。而这是对于 Node.js 应用来说,对于拥有虚拟机的 Java 和 C# 可能就没有那么幸运了。</p>
<h3>缺乏调试和开发工具</h3>
<p>当我使用 Serverless Framework 的时候,遇到了这样的问题:缺乏调试和开发工具。后来,我发现了 serverless-offline、dynamodb-local 等一系列插件之后,问题有一些改善。</p>
<p>然而,对于日志系统来说,这仍然是一个艰巨的挑战。</p>
<p>每次你调试的时候,你需要一遍又一遍地上传代码。而每次上传的时候,你就好像是在部署服务器。然后 Fuck 了,我并不能总是快速地定位出问题在哪。于是,我修改了一下代码,添加了一行 <code>console.log</code>,然后又一次地部署了下代码。问题解决了,挺好的,我删了一下 <code>console.log</code>,然后又一次地部署了下代码。</p>
<p>后来,我学乖了,找了一个类似于 log4j 这样的可以分级别纪录日志的 Node.js 库 <code>winston</code>。它可以支持 error、warn、info、verbose、debug、silly 六个不同级别的日志。</p>
<h3>构建复杂</h3>
<p>Serverless 很便宜,但是这并不意味着它很简单。</p>
<p>早先,在知道 AWS Lambda 之后,我本来想进行一些尝试。但是 CloudForamtion 让我觉得太难了,它的配置是如此的复杂,并且难以阅读及编写(JSON 格式)。</p>
<p>考虑到 CloudForamtion 的复杂度,我是在接触了 Serverless Framework 之后,才重新燃起了一些信心。</p>
<p>Serverless Framework 的配置更加简单,采用的是 YAML 格式。在部署的时候,Serverless Framework 会根据我们的配置生成 CloudForamtion 配置。</p>
<p>在那篇《<a href="https://link.segmentfault.com/?enc=%2FDIWOrGm7b%2FEdRDKRpjXoA%3D%3D.QkGpeTjAmeAWKr2tKk1jSC9dVSPBkZl1DE8c8xZCDhYEmhzFg%2BX5CvSeNp%2BNzhOdTwdPvhLgvNdVWvG2JtgA6ecLRSWYq%2B1IyDdcvGjSf8frWPrwZrdMT5CSnBkuD4BY" rel="nofollow">Kinesis Firehose 持久化数据到 S3</a>》想着的数据统计文章里,我们介绍了 Serverless 框架的配置。与一般的 Lambda 配置来说,这里的配置就稍微复杂一些。然而,这也并非是一个真正用于生产的配置。我的意思是,真实的应用场景远远比这复杂。</p>
<h3>语言版本落后</h3>
<p>在 Node.js 6 出来的时候,AWS Lambda 只支持 Node.js 4.3.2;在 Node.js 9.0 出来的时候,AWS Lambda 支持到 6.10.3。</p>
<p>如下是 AWS Lambda 支持以下运行时版本:</p>
<ul>
<li><p>Node.js – v4.3.2 和 6.10.3</p></li>
<li><p>Java - Java 8</p></li>
<li><p>Python – Python 3.6 和 2.7</p></li>
<li><p>.NET 内核 – .NET 内核 1.0.1 (C#)</p></li>
</ul>
<p>对于 Java 和 Python 来说,他们的版本上可能基本都是够用的,我不知道 C# 怎么样。但是 Node.js 的版本显然是有点老旧的,但是都 Node.js 9.2.0 了。不过,话说来说,这可能与版本帝 Chrome 带来的前端版本潮有一点关系。</p>
<p>节选自《<a href="https://link.segmentfault.com/?enc=676oHIVMjNhN5QyUj451yw%3D%3D.bcsbhu6zA16KznDO6PQ1Q4wWivMI4ChzMMnvKyl7UhWqDg984VC9p5Y5%2Bzoa6w%2Fm" rel="nofollow">Serverless 架构应用开发指南 </a>》</p>
花了 1000G,我终于弄清楚了 Serverless 是什么(上):什么是 Serverless 架构?
https://segmentfault.com/a/1190000012042629
2017-11-17T07:58:19+08:00
2017-11-17T07:58:19+08:00
phodal
https://segmentfault.com/u/phodal
39
<blockquote><p>花了 1000G,我终于弄清楚了 Serverless 是什么?</p></blockquote>
<p>在过去的 24 小时,我通过微信公号的『电子书』一事,大概处理了 8000 个请求:</p>
<p><img src="/img/remote/1460000012042634" alt="Serverless 请求统计" title="Serverless 请求统计"></p>
<p>大部分的请求都是在 200ms 内完成的,而在最开始的请求潮里(刚发推送的时候,十分钟里近 1500 个请求),平均的响应时间都在 50ms 内。</p>
<p><img src="/img/remote/1460000012042635?w=2326&h=428" alt="Serverless 请求时间" title="Serverless 请求时间"></p>
<p>这也表明了,Serverless 相当的可靠。显然,当请求越多的时候,响应时间越快,这简直有违常理——一般来说,随着请求的增加,响应时间会越来越慢。</p>
<p>毫无疑问,在最近的几年里,微服务渐渐成为了一个相当流行的架构风格。微服务大致从 2014 年起,开始流行开来,如下图所示:</p>
<p><img src="/img/remote/1460000012042636?w=1200&h=638" alt="microservices vs serverless" title="microservices vs serverless"></p>
<p>而微服务是从 2016 年起,开始受到开发者的关注。并且从其发展趋势来看,它大有可能在两年后,拥有今天微服务一样的地位。可见,它是一个相当具有潜力的架构。</p>
<h2>什么是 Serverless 架构??</h2>
<p>为了弄清 Serverless 究竟是什么东西?Serverless 到底是个什么?我使用 Serverless 尝试了一个又一个示例,我自己也做了四五个应用,总算是对 Serverelss 有了一个大致上的认识。</p>
<h3>虚拟化与隔离</h3>
<blockquote><p>开发人员为了保证开发环境的正确(即,这个 Bug 不是环境因素造成的),想出了一系列的隔离方式:虚拟机、容器虚拟化、语言虚拟机、应用容器(如 Java 的 Tomcat)、虚拟环境(如 Python 中的 virtualenv),甚至是独立于语言的 DSL。<sup id="fnref-1"><a href="#fn-1" class="footnote-ref">1</a></sup></p></blockquote>
<p>从最早的物理服务器开始,我们都在不断地抽象或者虚拟化服务器。</p>
<p><img src="/img/remote/1460000012042637?w=638&h=359" alt="服务器发展" title="服务器发展"></p>
<ul>
<li><p>我们使用 XEN、KVM等虚拟化技术,隔离了硬件以及运行在这之上的操作系统。</p></li>
<li><p>我们使用云计算进一步地自动管理这些虚拟化的资源。</p></li>
<li><p>我们使用 Docker 等容器技术,隔离了应用的操作系统与服务器的操作。</p></li>
</ul>
<p>现在,我们有了 Serverless,我们可以隔离操作系统,乃至更底层的技术细节。</p>
<h3>为什么是花了 200G ?</h3>
<p>现在,让我简单地解释『花了 200G,我终于弄清楚了 Serverless 是什么?』这句话,来说说 Serverless 到底是什么鬼?</p>
<p>在实践的过程中,我采用的是 AWS Lambda 作为 Serverless 服务背后的计算引擎。AWS Lambda 是一种函数即服务(Function-as-a-Servcie,FaaS)的计算服务,简单的来说就是:开发人员直接编写运行在云上的函数、功能、服务。由云服务产商提供操作系统、运行环境、网关等一系列的基础环境,我们只需要关注于编写我们的业务代码即可。</p>
<p>是的,你没听错,我们只需要<strong>考虑怎么用代码提供价值即可</strong>。我们甚至连可扩展、蓝绿部署等一系列的问题都不用考虑,Amazon 优秀的运营工程师已经帮助我们打造了这一系列的基础设施。并且与传统的 AWS 服务一样,如 Elastic Compute Cloud(EC2),它们都是按流量算钱的。</p>
<p>那么问题又来了,它到底是怎么对一个函数收钱的。我在 Lambda 函数上运行一个 Hello, world 它会怎么收我的钱呢?</p>
<p>如果要对一个运行的函数收费,那么想必只有运行时间、CPU、内存占用、硬盘这几个条件。可针对于不同的需求,提供不同的 CPU 是一件很麻烦的事。对于代码来说,一个应用占用的硬盘空间几乎可以忽略不计。当然,这些应用会在你的 S3 上有一个备份。于是,诸如 AWS 采用的是运行时间 + 内存的计算方式。</p>
<table>
<thead><tr>
<th>内存 (MB)</th>
<th>每个月的免费套餐秒数</th>
<th>每 100ms 的价格 (USD)</th>
</tr></thead>
<tbody>
<tr>
<td>128</td>
<td>3,200,000</td>
<td>0.000000208</td>
</tr>
<tr>
<td>192</td>
<td>2,133,333</td>
<td>0.000000313</td>
</tr>
<tr>
<td>256</td>
<td>1,600,000</td>
<td>0.000000417</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>1024</td>
<td>400,000</td>
<td>0.000001667</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
<p>在运行程序的时候,AWS 会统计出一个时间和内存,如下所示:</p>
<pre><code>REPORT RequestId: 041138f9-bc81-11e7-aa63-0dbab83f773d Duration: 2.49 ms Billed Duration: 100 ms Memory Size: 1024 MB Max Memory Used: 20 MB</code></pre>
<p>其中的 <code>Memory Size</code> 即是我们选用的套餐类型,Duration 即是运行的时间,Max Memory Used 是我们应用运行时占用的内存。根据我们的 Max Memory Used 数值及应用的计算量,我们可以很轻松地计算出我们所需要的套餐。</p>
<p>因此,如果我们选用 1024M 的套餐,然后运行了 320 次,一共算是使用了 320G 的计算量。而其运行时间会被舍入到最近的 100ms,就算我们运行了 2.49ms,那么也是按 100ms 算的。那么假设,我们的 320 次计算都花了 1s,也就是 10<em>100ms,那么我们要支付的费用是:10</em>320*0.000001667=0.0053344刀,即使转成人民币也就是不到 4 毛钱的 0.03627392。</p>
<p>如果我们先用的是 128M 的套餐,那么运行了 2000 次,就是 200G 的计算量了。</p>
<p>如果我们先用的是 128M 的套餐,那么运行了 8000 次,就是 1000G 的计算量了。</p>
<p>不过如上表所示,AWS 为 Lambda 提供了一个免费套餐(无期限地提供给新老用户)包含每月 1M 免费请求以及每月 400 000 GB 秒的计算时间。这就意味着,在很长的时间里,我们一分钟都不用花。</p>
<h3>Serverless 是什么?</h3>
<p>而从上节的内容中,我们可以知道这么几点:</p>
<ul>
<li><p>在 Serverless 应用中,开发者只需要专注于业务,剩下的运维等工作都不需要操心</p></li>
<li><p>Serverless 是<strong>真正的按需使用</strong>,请求到来时才开始运行</p></li>
<li><p>Serverless 是按运行时间和内存来算钱的</p></li>
<li><p>Serverless 应用严重依赖于特定的云平台、第三方服务</p></li>
</ul>
<p>当然这些都是一些虚无缥缈地东西。</p>
<p>按 AWS 官方对于 Serverless 的介绍是这样的:</p>
<blockquote><p>服务器架构是基于互联网的系统,其中应用开发不使用常规的服务进程。相反,它们仅依赖于第三方服务(例如AWS Lambda服务),客户端逻辑和服务托管远程过程调用的组合。”<sup id="fnref-2"><a href="#fn-2" class="footnote-ref">2</a></sup></p></blockquote>
<p>在一个基于 AWS 的 Serverless 应用里,应用的组成是:</p>
<ul><li><p>网关 API Gateway</p></li></ul>
<p>来接受和处理成千上万个并发 API 调用,包括流量管理、授权和访问控制、监控等</p>
<ul>
<li><p>计算服务 Lambda 来进行代码相关的一切计算工作,诸如授权验证、请求、输出等等</p></li>
<li><p>基础设施管理 CloudFormation 来创建和配置 AWS 基础设施部署,诸如所使用的 S3 存储桶的名称等</p></li>
<li><p>静态存储 S3 作为前端代码和静态资源存放的地方</p></li>
<li><p>数据库 DynamoDB 来存储应用的数据</p></li>
<li><p>等等</p></li>
</ul>
<p>以博客系统为例,当我们访问一篇博客的时候,只是一个 GET 请求,可以由 S3 为我们提供前端的静态资源和响应的 HTML。</p>
<p><img src="/img/remote/1460000012042638?w=1198&h=614" alt="Serverless SPA 架构" title="Serverless SPA 架构"></p>
<p>而当我们创建一个博客的时候:</p>
<ul>
<li><p>我们的请求先来到了 API Gateway,API Gateway 计费器 + 1</p></li>
<li><p>接着请求来到了 Lambda,进行数据处理,如生成 ID、创建时间等等,Lambda 计费器 + 1</p></li>
<li><p>Lambda 在计算完后,将数据存储到 DynamoDB 上,DynamoDB 计费器 + 1</p></li>
<li><p>最后,我们会生成静态的博客到 S3 上,而 S3 只在使用的时候按存储收费。</p></li>
</ul>
<p>在这个过程中,我们使用了一系列稳定存在的云服务,并且只在使用时才计费。由于这些服务可以自然、方便地进行调用,我们实际上只需要关注在我们的 Lambda 函数上,以及如何使用这些服务完成整个开发流程。</p>
<p>因此,Serverless 并不意味着没有服务器,只是服务器以特定功能的第三方服务的形式存在。</p>
<p>当然并不一定使用这些云服务(如 AWS),才能称为 Serverless。诸如我的同事在 《<a href="https://link.segmentfault.com/?enc=N%2Frwhf6iuu2dQhbL0iStRA%3D%3D.TDHu%2B8qYpHlOLn2F%2FzI%2BJ2ZrKfRrFfEYIicAbiKDAWLdgAhtUnJIa4PjUSSC4h%2BB4yREbHrDTa2ppGiDkdX4DycWuLPCJn69kBhrS8h9d6qjPowZYNq6GGb6wiyNdcxKVQuAOHqNkDRtbpsUHTehCw%3D%3D" rel="nofollow">Serverless 实战:打造个人阅读追踪系统</a>》,采用的是:IFTTT + WebTask + GitHub Webhook 的技术栈。它只是意味着,你所有的应用中的一部分服务直接使用的是第三方服务。</p>
<p>在这种情况下,系统间的分层可能会变成一个又一个的服务。原本,在今天主流的微服务设计里,每一个领域或者子域都是一个服务。而在 Serverless 应用中,这些领域及子域因为他们的功能,又可能会进一步切分成一个又一个 Serverless 函数。</p>
<p><img src="/img/remote/1460000012042639" alt="更小的函数" title="更小的函数"></p>
<p>只是这些服务、函数比以往的粒度更加细致。</p>
<p>节选自《<a href="https://link.segmentfault.com/?enc=fGRaXJ0KaIvvFdMSJkQdBA%3D%3D.rQ8qZQkywQUYv4T%2FmDDUw1gsembxoyJaNm0pFo1ix%2BZPs8kfaHeqYRm%2BjqKbCn5d" rel="nofollow">Serverless 架构应用开发指南</a>》</p>
<hr>
<ol>
<li id="fn-1">《全栈应用开发:精益实践》 <a href="#fnref-1" class="footnote-backref">↩</a>
</li>
<li id="fn-2"> <a href="https://link.segmentfault.com/?enc=nSZp15oClxsfhzv0FBDnBA%3D%3D.WHOm1LcpMJtTC3%2F1jXXBjRi3VaOewX9PkDPs6Pf8KdjhVb6btyi5PRUvhdxpy8D8eHqtlB7%2FFXqbzot5OykVUA%3D%3D" rel="nofollow">https://aws.amazon.com/cn/blo...</a> <a href="#fnref-2" class="footnote-backref">↩</a>
</li>
</ol>
开源编程学习应用 Growth 发布 3.0,在碎片时间里练习
https://segmentfault.com/a/1190000010280327
2017-07-20T21:06:42+08:00
2017-07-20T21:06:42+08:00
phodal
https://segmentfault.com/u/phodal
2
<p><a href="https://link.segmentfault.com/?enc=s1L2%2BUI5vKCelDXdpyITFg%3D%3D.TyPe8Nx95FV9NRkNeBo5ZoUraS1WdNeR8ch70XOhCa3DnSn9YECJ3g1krlDWqqaE" rel="nofollow">Growth</a> 1.0~2.0 已经有 2101 次提交,而 Growth 3.0 则已经拥有了 900+ 提交。这意味着 Growth 整个项目有多达 3000 次提交,感谢每一个为 Growth 项目作为贡献的开源先锋。</p>
<p>特别感谢:@travelgeezer 为 Growth 3.0 编写了大量的功能。</p>
<p>现在,让我来开启装逼模式。</p>
<h2>使用 React Native 重写,性能提升了 N + 1 倍</h2>
<p>在 Growth 1.x 里,我们使用了 Ionic 1.x + Angular 1.x 来开发,而当时 Angular 1.x 已经过时了。</p>
<p>在 Growth 2.x 里,我们使用了 Ionic 2.x + Angular 2.x~4.x 重写了一遍,而我们发现性能不能让人满意。</p>
<p>因此在 Growth 3.x 里,我们使用了 React Native 重写了整个应用,再一次。</p>
<p>使用 React Native,从开发速度上来说,真心没有 WebView 来得快,但是性能上的提升是有目共睹的。至少,打开速度比原来快了好多倍。如我们所料,RN 的坑很多,过些日子,我再写一篇文章吧。</p>
<p>现在,让我们再看看 Growth 这个应用里,有什么新功能。</p>
<h2>LeetCode 试题,适合于地铁思考模式</h2>
<p>记得很多新手程序员问过我,他们每天要在地铁上花很多的时间,有什么东西可以在这段时间学习的。看书吧,不是很合适,如我这样的人就容易因引此而晕车——所以我一般不喜欢在公交车上玩手机。我给的答案是:<strong>这个时候适合思考问题</strong>。</p>
<p>那么,在这个时候可以来一个 LeetCode 题,每天在路上成长一点点。LeetCode 据说是,一些经典的公司用来面试应聘者的面试题。</p>
<p><img src="/img/remote/1460000010280332" alt="LeetCode" title="LeetCode"></p>
<p>目前相应的题目是在 GitHub 上的,下载速度有点慢,希望有产商可以提供个服务器,哈哈哈~~。</p>
<p><strong>如果你每天要在地铁上站一小时,那么请带上一个 LeetCode 问题练习。</strong></p>
<h2>算法动画学习工具</h2>
<blockquote><p>基于 <a href="https://link.segmentfault.com/?enc=PMGMmg9n8dejZYakJuGaWA%3D%3D.STDNq2tYfH1igrhouSXeSxo94z%2FzC%2BGS2X%2FyIfbdhSMzKsb4nZGPyxcB%2BsquPlHSgKuwDjk%2BG%2FBKxJ%2BtP1tVrg%3D%3D" rel="nofollow">AlgorithmVisualizer</a></p></blockquote>
<p>既然,我们已经有了那么多算法题,那么我们也应该在手机上学习。于是,Growth 的用户 @ ivanberry 便提出了可以参考:<a href="https://link.segmentfault.com/?enc=S9IO5ssWLaUPSdJbZYc7gg%3D%3D.9PI90ykqHI2pnXamSBQ1onlRJWp0veGXaUytw8uHSac%3D" rel="nofollow">https://visualgo.net/en</a>,可是这个是不开源的。</p>
<p>找了一二个星期,看到了 <a href="https://link.segmentfault.com/?enc=d2ndkBcVcSZrHD7n7m2nmw%3D%3D.Ex3Y%2B%2B7VqQ0NUL7q1RmLyyNJ5Gkkg0lXLCScy7LHDxhwcWwbrsfqHcb8a5u%2FBOU3iHwG4P3PEfon84Ked8CgxQ%3D%3D" rel="nofollow">Algorithm Visualizer</a> 项目,它可以用<strong>动画展示算法、展示每一步的执行过程、拥有相应的示例代码</strong>。便花了两星期的时间,做成了下面的模样。</p>
<p><img src="/img/remote/1460000010280333" alt="Algorithm" title="Algorithm"></p>
<p>上半部分的动画内容是 WebView,下面则是 React Native。</p>
<p><strong>如果你每天要等上半个小时的公交,那么不妨学一个算法知识。</strong></p>
<h2>正则表达式练习</h2>
<blockquote><p>基于 <a href="https://link.segmentfault.com/?enc=q6n2xCQrTyfA10SnI6a3Hg%3D%3D.sKNF6fDph4nSHdCIN2VW0f70c3iRAfRH%2Bg%2BZsEoZA0ssjY94jMdb3GScLEc5KO%2Fo" rel="nofollow">Regexper</a></p></blockquote>
<p>同样的这个功能,也是由 Growth 用户 @allmelgr 提出来的,他对于 <a>regexer.com</a> 的评价是:用过的人都说简单易懂。是的,一看就懂的样子:</p>
<p><img src="/img/remote/1460000010280334" alt="Regex" title="Regex"></p>
<p>而为了更方便的学习,便结合 <a href="https://link.segmentfault.com/?enc=tpHG68JAOQSCpaOIMAcoHQ%3D%3D.7LnAKSvfZQauOMQ7xbAspfCSCK4TjE9fe4elMwdDf08vrYOCYAYd%2BN56shxPX94D" rel="nofollow">RegexHub</a> 提供一些正则表达式示例,只需要选择对应的正则表达式,就可以展示 相应的正则关系。</p>
<p><strong>如果你每天要在公交车上坐一小时,那么不妨练习一人小时的正则。</strong></p>
<h2>设计模式简介</h2>
<blockquote><p>基于 <a href="https://link.segmentfault.com/?enc=QxImszHRLvCA%2BDPg60%2Bdaw%3D%3D.N%2BHMkinicuaaGL7YVmF4QodygUJxtCFIUGPuJMQYlJDhMuxZ4BoV69CsN%2BDgomTbZ26aczYo2Iq5aslNlmQVHg%3D%3D" rel="nofollow">Design Patterns for Humans</a></p></blockquote>
<p>既然我们都已经有算法和数据结构、正则表达式,我便将 GitHub 上的 <a href="https://link.segmentfault.com/?enc=1X%2BRueFinzNQbzPGjcimyg%3D%3D.HVCMZNfLKPYU3NQFinhJC1ynIMMT9mu3uhl9%2F836QEZA9WfDMgzl2cib5%2BwoiuoCUgs7YD2FRZ4gNFHdDCQ30g%3D%3D" rel="nofollow">Design Patterns for Humans</a> 也集成了到 APP 里。</p>
<p><img src="/img/remote/1460000010280335" alt="Design Pattern" title="Design Pattern"></p>
<p><strong>如果你已经能完成工作,但是迷茫,那么试试设计模式</strong></p>
<h2>内置 VS Code 的编辑器</h2>
<blockquote><p>基于 <a href="https://link.segmentfault.com/?enc=Y%2BEu5wmtKFTeZQqJoGnaXA%3D%3D.xdviY0Oo9bXe2JtgkObhoMNbJ1UHmdpti7QvcT3RiakKnO%2FFDeOqRUKFFu0GbGPU" rel="nofollow">Monaco Editor</a></p></blockquote>
<p>既然都已经有了 LeetCode,那么我们应该还有一个编辑器。找了几天,终于找到将 VisualStudio Code 的同款编辑器 <a href="https://link.segmentfault.com/?enc=I%2BDSKAPI9K62o2oFEpqP3w%3D%3D.a71MSLnzlNMtKi1PiVa8Emf7gkgDxxc0bBHlFTgSHcB8w5b4m0UGtTmtokCJaqz3" rel="nofollow">Monaco Editor</a> 集成进去了。</p>
<p><img src="/img/remote/1460000010280336" alt="Code" title="Code"></p>
<p>而显然它不仅仅是一个编辑器,它还可以运行 JavaScript 代码,一个『伟大的手机 IDE』就此诞生了。输入完代码,点击运行,即可运行代码。console.log 会以 Toast 的形式展示出来。</p>
<p>这绝逼是我在 Growth 做的最吊的,但是没有什么卵用的功能。</p>
<p><strong>如果你想试试某行代码,那么试试新的编辑器。</strong></p>
<h2>成长路线与技能树</h2>
<p>依旧的,仍然可以在上面看到技能树,并且速度更快了:</p>
<p><img src="/img/remote/1460000010280337" alt="SkillTree" title="SkillTree"></p>
<p><strong>如果你对某一个领域迷茫,那么来看看对应的技能树。</strong></p>
<p>与此同时,我们更新了一下成长路线:</p>
<p><img src="/img/remote/1460000010280338" alt="Roadmap" title="Roadmap"></p>
<p><strong>如果你对某一个领域迷茫,那么来看看对应的学习路线。</strong></p>
<p>未来,我们将结合网上的资源,整合学习路线和技能树——如果有更多的人来参与的话。</p>
<h2>Awesome 列表</h2>
<blockquote><p>基于 <a href="https://link.segmentfault.com/?enc=w%2F52rpyNST%2FiU%2FBXziIgyQ%3D%3D.E6LeHG33WlzHRZomv3Y8HQTy%2FLQ9bx4UxskHjy%2BjEPY0kzXO4WWRSMA6%2ByvrykLV" rel="nofollow">Awesome</a></p></blockquote>
<p>某一天,我在对着 Awesome React Native 发呆的时候,便顺手集成了 Awesome Awesomes 项目——写了个脚本,解析 awesome 项目生成对应的 JSON 文件。</p>
<p><img src="/img/remote/1460000010280339" alt="Extends" title="Extends"></p>
<p>不过就是这个列表有点太长太长太长太长太长,即使使用了 RN 的 FlatList 也不能很好地解决问题。</p>
<p><strong>如果你找不到合适的灵感,那么不妨看看 Awesome 列表吧。</strong></p>
<h2>开源电子书</h2>
<blockquote><p>基于<a href="https://link.segmentfault.com/?enc=rAWJMRYkk5ttV6lJEVWyww%3D%3D.Ir%2FYfGy8s2OwnvUv4lk6C9FrFDjOQbsxzMWbW1DHui4348Y84DPLTcsPaU8igx1bRaCUubuMj1TEIMPIH9RP1w%3D%3D" rel="nofollow">免费的编程中文书籍索引</a></p></blockquote>
<p>有一天,我在想虽然我号称是最伟大的『md程序员』,但是一个我,肯定比不上一百个我的写作速度快。于是,我们便想将那些在 GitHub、GitBook 上的书,制作成方便在手机上阅读的内容。便有了:</p>
<p><img src="/img/remote/1460000010280339" alt="Extends" title="Extends"></p>
<p>实际上,这个功能实现得是最早的。但是在当前并没有什么用,当前只是链接。未来,将制作对应的 API 来获取不同书的内容,就是工作量有点巨大巨大巨大巨大巨大。</p>
<p><strong>如果你找不到免费的电子书,那么试试开源的编程中文书籍。</strong></p>
<h2>Growth</h2>
<p>在 Growth 3.0,Growth 原先的内容仍然还在,只是还有一些 Bug,啊哈哈</p>
<p><img src="/img/remote/1460000010280340" alt="Growth" title="Growth"></p>
<p>对于支持 Growth 指南对应的纸质书籍《<a href="https://link.segmentfault.com/?enc=VwEUf3dsZxQPonx2dkyPzQ%3D%3D.YykIrDhR7q3r0kj%2BA2VLCvAYC3X%2FOPu8mETAF6BiooEZ6amTjjlJdi%2B%2F%2FSPpeoQB" rel="nofollow">全栈应用开发:精益实</a>》,觉得好的就给个好评,差的就算了吧~~。</p>
<p><strong>如果你想成长顶尖开发者,那么试试 Growth 吧~。</strong></p>
<h2>Discover</h2>
<p>在探索栏目里,我们依旧准备了丰富的阅读、练习资源。</p>
<p><img src="/img/remote/1460000010280341" alt="Discover" title="Discover"></p>
<p><strong>如果你觉得无聊,那么可以在探索和社区里,了解更广泛的世界。</strong></p>
<h2>So</h2>
<p><strong>这就是 Growth 3.0,让你练习更多。</strong></p>
<p><strong>这就是 Growth 3.0,让你练习更多。</strong></p>
<p><strong>这就是 Growth 3.0,让你练习更多。</strong></p>
<p>PS:应用已在 App Store、Google Play、应用宝、小米应用商店、360应用商店上架,其它用户可以从<a href="https://link.segmentfault.com/?enc=tnLYnRocBgyAaFwzgdvikQ%3D%3D.l%2Fz%2Bck11YWdxWlcrodbs3a6Ij0%2FMcqblJHB8KUuC%2FIBEcdeJXw291NBErpH6MmR9RdTlPQezIgU56W%2BQTiY%2FdA%3D%3D" rel="nofollow">应用宝</a> 直接下载 APK。</p>
<p>欢迎到 GitHub 来支持我们的开发:<a href="https://link.segmentfault.com/?enc=a6sBpY8ceAerBwjmXyrj9g%3D%3D.ohOziVK1lWWKALOYv0VpMz6UaqDkDRiC6R6vBU6%2B%2BRbAtcPC4JjjOS7boE7Agqn2" rel="nofollow">https://github.com/phodal/growth</a></p>
为什么所有的 Web 应用都将被重写?——Web 应用的生命周期
https://segmentfault.com/a/1190000009746894
2017-06-12T12:37:04+08:00
2017-06-12T12:37:04+08:00
phodal
https://segmentfault.com/u/phodal
2
<blockquote><p>一个Web应用在其生命周期里,都要经历搭建开发环境、创建构建系统、编写代码、进行数据分析等等,直至最后使用新的系统来替换这个遗留系统。</p></blockquote>
<h2>Web应用的生命周期</h2>
<p>在我所经历的项目以及我所看到的Web应用里,它们都有相同的很有意思的生命周期。我们经常在网上看到某个知名的网站使用某个新的技术、语言来替换旧的系统,某个APP使用开发新的框架来替换现有的APP。我们所看到的都只是这些公司正在重构现有的系统,这实际上是一个周期的结束,以及一个新的周期的开始。其过程如下图所示:</p>
<p><img src="/img/remote/1460000009746899?w=600&h=501" alt="" title=""></p>
<p>仔细一想就会发现:我们所经历的项目,都在以不同的时间长度经历相同的生命周期。</p>
<h3>遗留系统与新架构</h3>
<p>在我开始工作的时候,接触的第一个项目就是一个遗留系统。在一次休息时,我们在比赛找最古老的源码文件,最后找到了一个10年前写下的文件。尽管在我们的代码里有单元测试、针对具体业务功能的测试,项目的代码已经超过20万行,项目中仍然有相当多的代码超出了我们所理解的业务范围。毕竟在这些年头里,有相当多的功能已经不存在了。后来,我们选用微服务重构了这个系统。对于中型的遗留系统来说,这算是一剂良药。</p>
<p><img src="/img/remote/1460000009746900?w=600&h=314" alt="" title=""></p>
<p>让我们先从某某网站使用新架构重新设计说起。当我们决定使用新架构重新设计系统,原因可能是多种多样的,如果我们排除一些无法抗拒的因素,如政治,那么剩下的原因可能就只有两个:</p>
<ol>
<li><p><strong>系统已经变得难以维护。</strong>这里的原因仍然有很多:大量的代码已经没有人知道其业务逻辑,变得难以修改;代码间耦合度过高,重构系统的难度过于复杂;项目所使用的技术栈已经过时,已经被市场所淘汰;团队的技术栈在成员变动的过程中,团队中的大部分成员的技术栈已经和当前的项目不匹配了。</p></li>
<li><p><strong>系统的技术栈已经难以符合业务的需求。</strong>绝大多数情况下,我们在最初的开始创建项目的时候,所选择的技术栈都是符合当时业务需求的技术栈、可以快速验证其业务价值的技术栈。而随着业务的扩张,现有的技术栈很快将难以满足当前业务的需求,或出现性能优化上的限制。</p></li>
</ol>
<p>在多数情况下,我们都会将这种系统称之为遗留系统。在这时团队里的气氛,便是“能不动这些代码就尽量不去动它”。我们已经很难将项目的问题归根到人的因素上,多数的时候都是受业务扩张的影响。</p>
<p><img src="/img/remote/1460000009746901?w=600&h=300" alt="" title=""></p>
<p>作为一个专业的程序员,我们的本能就是将程序写好。而我们往往没有这样的机会:</p>
<p>业务人员对项目经理说:“<strong>我们的竞争对手已经在本周上线了这个功能</strong>”。</p>
<p>项目经理对开发人员说:“<strong>这个功能下星期就要上线</strong><strong>!</strong>”</p>
<p>是的,我们的功能不得不马上上线。这时候,我们往往要在代码质量和交付速度上作出一些妥协。妥协多了,系统也就变烂了。</p>
<p>开发人员:“这个代码我不太敢修改,要是出了什么大bug怎么办?”。慢慢地人们就开始讨论起重构系统的事宜,并开始着手设计新的架构—使之可以满足当前的业务需求、可预测时间内的业务与技术需求。</p>
<h3>技术选型与验证</h3>
<p>在我们讨论新的架构的过程中,不同的人可能会有不同的技术偏好,也会因存在一些政治因素导致不同技术方案的产生。如团队中的一些人可能出于稳定缘故而使用Java,而一些人可能出于对新技术的需求使用Scala,而团队中大部分会可能因为都会使用JavaScript而选择使用JavaScript。如下图所示,我们的考虑应该不仅仅取决于这一系列的技术因素:</p>
<p><img src="/img/remote/1460000009746902?w=600&h=298" alt="" title=""></p>
<p>需要注意的是:在做技术选型的时候尽,要尽最大可能的以团队为核心。在我们最后决定之前,我们要提出不同语言、框架下的技术模型,并且进行验证。随后我们就需要快速搭建出一个原型,并针对这个原型进行假想式开发,然后验证原型本身是经得起考验的。</p>
<p>在这一阶段,我通常喜欢在GitHub上搜索一些名字中带有boilerplate的项目,即模块文件。而当一个框架很流行的时候,我就会去相应的awesome-xx寻找,如awesome-react就可以寻找到react相关的项目集。然后,我们Clone这样的一个项目,开始依照现有的系统创建简单的Demo。随后,我们就可以依据我们的业务试图在这上面进行扩展。最后,再决定是否使用这门技术、是否使用这个框架。</p>
<p>通常来说,在选择一门新技术来设计系统时,需要承担的风险相当的大,而如果能成功的话,那么它也很可能会带来巨大的收益。从这里看,使用最新的技术和赌博无益。在一些成熟的公司里,会有专门的技术委员会负责对新技术进行审核,来决定是否可以在某个项目里使用新的技术。除了,考虑其为开发带来的便利性,他们更多的会考虑其成熟度、安全以及技术风险等等。</p>
<h3>搭建构建系统</h3>
<p>决定好了架构、选择完技术栈后,我们就得着手于创建项目的构建系统,设计项目的部署流程。构建系统不仅仅包含了项目相关的构建流程,还从某种意义上反应了这个项目的工作流程。</p>
<p>在我们创建完hello, world程序后,我们要着手做的事情就是创建一个持续集成环境。这样的环境包含了一系列的工具、步骤及实践,从工具上来说,我们需要选择版本管理工具、代码托管环境、持续集成工具、打包工具、自动部署脚本等等一系列的流程,这些流程将会在“第四章 构建流及工作流”进行详细的讨论。</p>
<p>下图便是笔者之前经历过的一个项目的构建流程:</p>
<p><img src="/img/remote/1460000009746903" alt="" title=""></p>
<p>这是一个后台语言用的是 Java,前台语言用的是 JavaScript 的项目的构建流程。</p>
<h3>迭代</h3>
<p>在互联网行业里,能越快速地对市场需求做出反应,就越能有更好的发展。只要你细心观察就可以发现,大部分的互联网公司都在以一定的规律更新产品,或者一周,或者两周,又或者是一个月等等,这种不断地根据反馈来改进产品的过程称之为<strong>迭代</strong>。如下图是一个简化的迭代模型:</p>
<p><img src="/img/remote/1460000009746904?w=450&h=264" alt="" title=""></p>
<p>当一个迭代开始时,我们需要收集上一个迭代的反馈,又或者是新的需求,然后开始开发代码,最后再发布我们的产品。我们开发的产品在这个过程中,不断地增强功能。为此,我们还需要选择一个好的迭代周期。一个好的迭代周期,即应该有充足的时间,即可以修复上一个迭代的Bug,又能在下一个迭代开始之前交付重要的功能。当然,如果不幸交付的软件包里出现了重要的Bug,那么我们也能在第一时间使用旧版本的包,并在下个迭代交付。当然在这样的开发节奏里,一个星期显得太短,一个月又显得太长,两星期会是一个很不错的时间。</p>
<p>当一个团队在这方面做得不好时,那么他们可能在一次上线后,发现重要的bug,不得不在当晚又或者是第二天更新他们的产品。即使是有经验的团队,在开发的初期也会经常遇到这些问题,而这些问题可以依赖于在迭代中的回顾来改进这些流程问题。好的迭代实践都是依据团队自身的需求而发展而来的,这意味着有时候适合团队 A 的实践并不一定适合团队B。</p>
<p>随后,我们就会在这个 hello, world 的基础上不断地添加各种功能。</p>
<p>节选自:《全栈应用开发:精益实践》</p>
<p>亚马逊:<a href="https://link.segmentfault.com/?enc=47NKh78MDE%2BR%2FdaFEoEWDg%3D%3D.NHfnVJhQ0Po1uAjS3FGVToxFuaDoQjQAgNoLCEc2LeG8Fy3efxXa%2FqhBbQC3CbU22KDrdRLPH6zUiRV8XovPuSmcWi5u18h4n6MyTahQ%2F0g%3D" rel="nofollow">《全栈应用开发:精益实践》 黄峰达【摘要 书评 试读】图书</a><br>京东:<a href="https://link.segmentfault.com/?enc=y8hJ799YiJyC4%2Fr7s4saUQ%3D%3D.koi4%2BUkX6cw0JIDYOv%2BJS3FV8faG82oMgAMOFmzDITS%2B%2FAMN7cAi00jfegHoVk82G9oSijsvCbPsiPF6MUwdJDIu8DLznQbvqzWY7qVmSzU%3D" rel="nofollow">《全栈应用开发:精益实践》(黄峰达)【摘要 书评 试读】- 京东图书</a><br>当当:<a href="https://link.segmentfault.com/?enc=txI5fgQz1TBGFW2iXhB2%2Bg%3D%3D.1OQA2jSd5Fw6Kl%2B5Yr6Qwkx4byqvD5oGsLx08EBRwf3UXAEAZtIobUvlBJWtchD5HCNfA26poDkNZWiJxLyxvjG6toNNXGO25tfPOf4Y6tM%3D" rel="nofollow">《全栈应用开发:精益实践》(黄峰达)【简介_书评_在线阅读】 - 当当图书</a></p>
Stepping.js——两步完成前后端分离架构设计
https://segmentfault.com/a/1190000008912471
2017-04-01T00:00:11+08:00
2017-04-01T00:00:11+08:00
phodal
https://segmentfault.com/u/phodal
3
<p>一周前,参加了公司的一个架构设计与建模的工作坊——『事件风暴』。从某种意义上来说,这是一个关于架构设计与软件建模的工作坊。于是便闪现了一个灵感,便有了 Stepping.js。</p>
<p>当我们结束事件风暴(Event Stroming)的时候,我们需要拍照,又或者是其他手段来记录下相关的内容。因此,整理这些文档又不是一个的容易的事。而事实上,我们只需要一个 DSL(领域特定语言),我们就可以直接将这个文档转换为图片,还有文档等等的内容。</p>
<p>因此,我写了一个名为 Stepping 的工具,来简化这方面的工作。</p>
<h2>Stepping</h2>
<p>步骤一:安装 Stepping</p>
<p>为了使用 Stepping,我们需要先安装 Stepping,可以能过 <code>yarn</code> 或者 <code>npm</code> 来安装:<code>yarn global add stepping</code>。</p>
<p>步骤二:设计领域模型</p>
<p>除此,我们还需要设计好系统相关的领域模型,如:</p>
<p><img src="/img/bVLyGl?w=638&h=479" alt="图片描述" title="图片描述"></p>
<p>再以 DSL 的形式来描述这个模型:</p>
<pre><code>domain: 库存子域
aggregate: 库存
event: 库存已增加
event: 库存已恢复
event: 库存已扣减
event: 库存已锁定
command: 编辑库存
aggregate: 商品
event: 商品已创建
command: 添加商品</code></pre>
<p>保存这个文件为<code>phodal.ddd</code>,然后执行<code>stepping -i phodal.ddd</code>。就可以得到一个 <code>stepping.svg</code> 的文件,这个 SVG 文件便能得到上面的领域模型。</p>
<p><img src="/img/bVLyGt?w=959&h=497" alt="图片描述" title="图片描述"></p>
<p>同时,如果你愿意的话,你还可以在这个 DDD 文件里写上相关的聚合的 Model:</p>
<pre><code>aggregate-detail: 商品
model: product
field:
id: int
name: string
number: string
manufacturers: string</code></pre>
<p>再执行下 <code>stepping -i phodal.ddd</code>,就可以得到一份前后端分离的示例 API 接口,即:</p>
<pre><code>{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 0,
"name": "name",
"number": 0,
"manufacturers": "manufacturers"
}
]
}</code></pre>
<p>结合一下 moco 或者 pretender,就可以直接变成一个 Mock 服务器。</p>
<p>在未来,我们还将结合这个 Model 来与 Django 做集成——只需要写好设计稿的 DSL,就可以生成相关的文档。</p>
<p>末了,让我们了解一下什么是领域风暴。</p>
<h2>事件风暴</h2>
<blockquote><p>事件风暴就是把所有的关键参与者都召集到一个很宽敞的屋子里来开会,并且使用便利贴来描述系统中发生的事情。</p></blockquote>
<p>一张桔黄色的便利贴代表一个领域事件,在上面用一句过去时的话描述曾经发生过什么事情,格式一般是:xx 已 xx。于是,我们需要整理系统相关的所有<strong>事件</strong>,也因此需要业务与开发人员共同进行风暴。如针对一个订单,会有这么一些相关的事件:</p>
<ul>
<li><p>订单已创建</p></li>
<li><p>订单已支付</p></li>
<li><p>订单已投诉</p></li>
<li><p>订单已撤销</p></li>
</ul>
<p>便会产生相关的便利贴:</p>
<p><img src="/img/bVLyGz?w=860&h=384" alt="图片描述" title="图片描述"></p>
<p>再按事件发生的时间轴,来对这些事件发生的顺序进行排序:</p>
<p><img src="/img/bVLyGB?w=646&h=148" alt="图片描述" title="图片描述"></p>
<p>紧接着,我们需要结合软件的用户的相关操作,写着与这些操作相关的<strong>命令</strong>。然后,结合这些<strong>命令</strong>与<strong>事件</strong>。如订单相关的命令就有:</p>
<ul>
<li><p>提交订单,可以触发事件『订单已创建』</p></li>
<li><p>提交投诉,可以触发事件『订单已投诉』</p></li>
<li><p>等等</p></li>
</ul>
<p>完成这个之后, 我们就有了系统相关的所有<strong>事件与命令</strong>:</p>
<p><img src="/img/bVLyGF?w=1476&h=640" alt="图片描述" title="图片描述"></p>
<p>换句话来说,这些相关的<strong>事件与命令</strong>就是我们编写细节代码时,需要完成的功能。最后,完成相关的聚合,我们就可以得到一份完整的模型:</p>
<p><img src="/img/bVLyGm?w=1232&h=686" alt="图片描述" title="图片描述"></p>
<p>依据这个模型,我们可以轻松地做出微服务设计。</p>
<p><img src="/img/bVzHnD?w=1880&h=924" alt="图片描述" title="图片描述"></p>
<p>欢迎试用,并在 GitHub 上提出建议:<a href="https://link.segmentfault.com/?enc=0rwhRIKcrO0SUTEaQuA9qQ%3D%3D.iHzCaeJzAuMABM7gsEa%2B3q8x6XJcKMcnpsV2xxbY4g2fxaeDyNhdapX2dGPWpwvs" rel="nofollow">https://github.com/phodal/ste...</a></p>
写给程序员的 18 幅对联,你能看懂几个?
https://segmentfault.com/a/1190000008140478
2017-01-17T07:33:29+08:00
2017-01-17T07:33:29+08:00
phodal
https://segmentfault.com/u/phodal
3
<blockquote><p>本文对联纯属虚构,如有雷同关我屁事。</p></blockquote>
<h2>辛酸版</h2>
<p>横批:谁能懂我</p>
<p>上联:敲一夜代码,流下两三行泪水,掏空四肢五体,六杯咖啡七桶泡面,还有八个测试九层审批,可谓十分艰难;</p>
<p>下联:经十年苦读,面过九八家公司,渐忘七情六欲,五年相亲四个对象,乃知三番加班两次约会,新年一鸣惊人。</p>
<h2>祈福版</h2>
<p>横批:鞠躬尽瘁</p>
<p>上联:文档注释一应具全</p>
<p>下联:脊柱腰椎早日康复</p>
<h2>生活版</h2>
<p>横批:1024</p>
<p>上联:西瓜包子带一斤三个</p>
<p>下联:大米白面少二十四克</p>
<h2>新手程序员</h2>
<p>横批:!@#$%^&*()</p>
<p>上联:红红火火过大年</p>
<p>下联:烫烫屯屯码三天</p>
<h2>高级程序员</h2>
<p>横批:一代键客</p>
<p>上联:坐北朝南一个需求满足东西</p>
<p>下联:思前想后几行代码安抚中央</p>
<h2>学生版</h2>
<p>横批:运鼠帷幄</p>
<p>上联:读码上万行</p>
<p>下联:下键如有神</p>
<h2>送产品版(和平版)</h2>
<p>横批:团结一致</p>
<p>上联:谈业务定需求必能安内攘外</p>
<p>下联:促稳定寻发展才好升职加薪</p>
<h2>送产品版(开战版)</h2>
<p>横批:你行你来</p>
<p>上联:去他大爷,十个需求,九处修改,八个扯淡,七番六次急忙上线</p>
<p>下联:改你妈逼,五日凌晨,四点加班,三里灯火,两排一个猝倒桌前</p>
<h2>老板送程序员版</h2>
<p>横批:画饼充饥</p>
<p>上联:百个功能愿你一气呵成</p>
<p>下联:一年年终奖你十月工资</p>
<h2>隔壁老王送程序员</h2>
<p>横批:人艰不拆</p>
<p>上联:少赚钱多说话,免得死得早</p>
<p>下联:别加班勤陪聊,不会戴绿帽</p>
<h2>前端版</h2>
<p>横批:瞬息万变</p>
<p>上联:微博知乎占头条谁与争锋</p>
<p>下联:桌面移动待前端一统江湖</p>
<h2>后台版</h2>
<p>横批:后方安定</p>
<p>上联:存数据订接口如探囊取物</p>
<p>下联:锁异步释内存似手到擒来</p>
<h2>梦想版</h2>
<p>横批:人生巅峰</p>
<p>上联:抬头不见八阿哥</p>
<p>下联:低头迎娶白富美</p>
<h2>形象版</h2>
<p>横批:员媛猿</p>
<p>上联:格子衣,牛仔裤,背跨双肩包</p>
<p>下联:文化衫,运动鞋,眼戴八百度</p>
<h2>社区</h2>
<blockquote><p>感谢 Growth 群里的群友</p></blockquote>
<p>横批:生不如死</p>
<p>上联:一年三百四十五天天天打代码</p>
<p>下联:十兆九千八百七行行行见bug</p>
<h2>Phodal 版</h2>
<p>横批:没钱买房</p>
<p>上联:待我代码编成</p>
<p>下联:娶你为妻可好</p>
<p>来,我出个上联:<strong>上班写 JavaScript 处处见 $</strong> </p>
<p>你的下联呢?</p>
<p><img src="/img/remote/1460000008140481" alt="Phodal" title="Phodal"></p>
2016年节点——增长的一年:不只前端,不止编程
https://segmentfault.com/a/1190000007926333
2016-12-26T22:02:13+08:00
2016-12-26T22:02:13+08:00
phodal
https://segmentfault.com/u/phodal
3
<p>时间过得很快很短,十二个月前在准备回南方的机票;八个月前在西安忍受着雾霾;四个月前在陪 @花仲马 吐槽桂林山水;今天我在这里,码下了这一行行的字。</p>
<p>在最近的一年里,有一个明显的变化是:进步不再那么明显。虽然这是一个不可避免,但是也找不到合适的突破点。这也意味着我拥有更多的非编程时间,我可以去探索更多的东西。</p>
<p><img src="/img/bVHp9H?w=600&h=416" alt="图片描述" title="图片描述"></p>
<p>我开始关注于社交(技术型社交)、增长(技术营销),还有一些细分领域。从这个秋天开始,我开始走路回家,大概 4.5 公里左右。除了感觉更能走以外,还没有别的明显的变化——按理说,多运动应该能提高某方面的能力。</p>
<p>依旧的让我们继续从 <strong>Line Of Business</strong> 说起,即编程、写作、设计。LoB,即业务线,当我第一次听到这个名字,我就喜欢上它了——简单的来说,就是业务及收入来源。下面的这张图非常适合当前的情况:</p>
<p><img src="/img/bVHp9L?w=600&h=336" alt="图片描述" title="图片描述"></p>
<p>在过去的一年,编程(即工资)依旧是最主要的收入来源。不过值得让人高兴的是写作的收入,也相当于一个季度的工资收入。只是设计依旧不起色,这主要是没有时间投入造成的。在未来的一年,<strong>是否咨询与演讲也可以转换变 LoB 的一部分</strong>?</p>
<h2>编程</h2>
<p>在十月的那个国庆里,我停止了 GitHub 上的连击——「不要用战术上的勤奋,掩盖战略上的懒惰」。其实我只是想说,有些人应该关心一下上面的 Star 数好吗?</p>
<p><a href="https://link.segmentfault.com/?enc=3eKWG4A7LCjJt4z0zEjxLA%3D%3D.P%2FsG4eUNe%2B2gf5RawnR9z%2FJcTaG7qC0VGkoWypns7VwcLkhcAAiO5rc2UZKr%2BKhi" rel="nofollow">Growth</a> 毫无疑问是今年编程方面的主角了:</p>
<p><img src="/img/bVHp9S?w=2020&h=536" alt="Growth Project" title="Growth Project"></p>
<p>当它还是 Ionic 1 时就有一千两百次左右的提交,而升级到 Ionic 2 时又重写了一遍,就有了上图中的 1964 次提交,差不多是 2016 提交量的 1/3。每一次更新 Growth 的时候,都有大量的代码修改,如下图所示:</p>
<p><img src="/img/bVHp9T?w=1498&h=414" alt="图片描述" title="图片描述"></p>
<p>然而这是一个面向最终的产品,而我一直想创建一个开发者日常使用的工具、框架等等。 Growth 可能有点难以达到这个目标,但是它对于大部分的新手程序员来说还是挺有帮助的——日活量接近 300 说明了这一点。</p>
<p>在工作上的变化不是很大,从之前西安的国外交付项目变为了深圳的国内交付项目。Title 变小了,做的事情也变少了,时间变多了,写代码的时间也变多了。原来的项目都要熟悉项目上的一切技术栈,还要负责架构的设计,现在只需要关注于混合应用。这是不是也意味着,<strong>可以考虑在明年写一本混合应用的电子书</strong>?</p>
<p><img src="/img/bVHp9U?w=2560&h=1600" alt="图片描述" title="图片描述"></p>
<p>再回到一个老生常谈的问题,<strong>没有一个代表性的开源作品</strong>。这是去年 Annual Review,同事对我的建议:缺乏代表性作品。那么问题来了,<strong>Growth 算是一个代表性的作品吗</strong>?我需要有一个代表性的开源作品吗?我依旧没有想明白答案到底是:是还是不是。不过在下一年里,依然还需要有一个作品,不是吗?</p>
<h2>写作</h2>
<p>在这一年里,写作里面已经有相当多可圈可点的内容了。年初设定的公众号 10000 粉的目标,现在已经达到了 -> 目前是 12100 左右。</p>
<p>在微信公众号方面仍然有一个问题:<strong>缺乏一个合理的定位</strong>。开始运营公众号的时候,一方面是为了帮我们家 @花仲马 引粉,一方面则是普及读者——博客的大部分流量来源是 Google。</p>
<p>不过有一件事,我发现很奇怪:每当我秀恩爱的时候都有很多赞赏,但是干货的时候都很少有赞赏~。</p>
<p><img src="/img/bVHp9W?w=979&h=653" alt="图片描述" title="图片描述"></p>
<p>写作上已经完成了第一步的<strong>战略性转移</strong>。年中的时候,我审阅完 Packt 出版社的《Smart IoT Projects》一书时,向编辑提出审阅前端书籍的请求。随后,我用 GitHub 和 Growth 项目证明了:写 Angular 2 和 TypeScript 的经验。于是我将审阅的方向转向了前端领域,这就意味着,我可以在<strong>真正的第一时间</strong>阅读到国外的前端书籍——作者在写书的时候,我就可以读到相关的内容。然后等这本书出版了,需要两三个月。再等到翻译成中文出版,估计到一年多以后了。现在正在审阅两本 Angular 2 的书,预期下个月和明年四月份会出版。</p>
<p><img src="/img/bVHp9Y?w=906&h=570" alt="图片描述" title="图片描述"></p>
<p>除此,<strong>写作上正在尝试另外一个实验</strong>。《自己动手设计物联网》最早是来源于我的毕业设计及 GitHub 上的开源电子书《教你设计物联网系统》,除此它还有一个开源的 APP。简单的总结一下这些步骤就是:精益出版 + 技术营销,而这一点特别适合 Growth 系列的内容。</p>
<p>Growth 最初推出的时候是一个开源的 APP,它被验证是有一定用处的。随后,推出了两本相关内容的电子书,即《Growth:全栈增长工程师指南》及《Growth:全栈增长工程师实战》。从项目的 Star 数和 APP 的用户来看,它特别适合于有一本纸质书籍。因为他在前期的市场宣传上已经不错了,同时它还有 APP 的用户在持续使用着。因此,你可能在明年看到这本书电子书——目前在写最后一章,出版合同还未签下。</p>
<p><img src="/img/bVHp92?w=800&h=533" alt="图片描述" title="图片描述"></p>
<p>这也存在一个问题是:<strong>编写技术书籍相当的花费时间</strong>。从长期来看,这是有益于国内的 IT 环境,并且有利于自己梳理自己的知识体系——电子书可以完成梳理,并且周期更短。但是编写技术书籍,对于<strong>待我代码编成,娶你为妻可好</strong> 的进度条影响比较小。</p>
<h2>设计</h2>
<p>尽管我一直想在设计上投入一些时间, 但是并没有那么多的可用时间。让我看到一点希望的是:我创建了一个项目:<a href="https://link.segmentfault.com/?enc=Ct8n640YvX29HdL2DQeqtg%3D%3D.biuj98qOekg86PIiIwuqvgJx4uzY4jGQZB7izzM1MAY%3D" rel="nofollow">Brand</a>,它让看到编程和设计之间的一些交集:即,用代码来生成一设计。SVG 是在这方面的一个很好突破点,它甚至让我有了<strong>创建一个图形框架</strong>的冲动。挖坑之前,让我再想想<strong>有没有这方面的必要性</strong>——需要在数学方面投入相当多的精力。</p>
<p><img src="/img/bVHp95?w=672&h=130" alt="图片描述" title="图片描述"></p>
<p>不过我计划在下一年里:<strong>拍摄一些照片还当作公众号的题图</strong>。同时,尝试去整理出一个自己的色彩集,又或者整理一个前端 CSS 框架。</p>
<h2>生活</h2>
<p>在这一年里,陪我们家花仲马去了桂林,完成她想要的毕业旅行。在国庆期间,我们去逛了逛张家界。</p>
<p><img src="/img/bVHp99?w=1138&h=640" alt="图片描述" title="图片描述"></p>
<p>工作的时候,只要不下雨,轻轻松松地走上 8000 步,打卡如下:</p>
<p><img src="/img/bVHqab?w=1080&h=853" alt="图片描述" title="图片描述"></p>
<p>并且,我发现这个距离的散步,很容易让我想好一篇推送的内容。</p>
<p>年初的时候,从西安搬到了深圳。从 1500 大概 60 平的房子,变成了 30 平不到 2400 的房子,让我感概这涨了四倍的房租,这就意味着没有那么多的地方可以放下书了。然而总算远离了雾霾的重灾区,在西安的期间动不动就咽喉炎,2333~。这高高在上的房价,让我有了挪屋的想法。</p>
<p>今年终于等到我们老家的房子装修年,只是年后又要身无分文的节奏。不过看这情况,明年就可以存下钱了,然后你们就可以准备三年后的份子钱了~~。</p>
<h2>2017</h2>
<p>下一年里,我应该就会变成一个『Senior Consultant』, 是不是可以靠着这个 Title 出去刷刷脸?</p>
<p>No</p>
<p>欢迎关注我的微信公众号:Phodal</p>
<p><img src="/img/bVHqad?w=344&h=344" alt="图片描述" title="图片描述"></p>
从最新的技术趋势看前端的未来
https://segmentfault.com/a/1190000007412104
2016-11-08T07:21:37+08:00
2016-11-08T07:21:37+08:00
phodal
https://segmentfault.com/u/phodal
4
<blockquote><p>本文仅代表 Phodal 的个人观点,来听听一个前端程序员的 YY。</p></blockquote>
<p>新一期的ThoughtWorks技术雷达有点出乎意料,使用<code>new</code>标签的框架、工具、技术、语言等等超过了一半——Vue.js、ES2017上榜,Three.js凭着VR的火又上榜了,还有熟悉的Electron,以及微前端的概念。</p>
<p>让我们先看看一些技术亮点~~。</p>
<h2>前端在可见的未来</h2>
<p>在那篇《<a href="https://link.segmentfault.com/?enc=sbE9qcgKE8ElQGqkB0qv%2FA%3D%3D.RH2vmWyXGUBVgV%2BBO%2FS1rs3d3870s8e0E0%2BlP43kDvPbIia0wIW0OrH3lpbElIgJ3zfFJ0%2Bxg2mthvhqE9KAo3dTB9%2BCE0dksdq9oZusGa1O21SHN%2F5wy%2Fnd7AXMpPDRv5ms7UoKJxn3XdjOdI178yNphwr5MFKG1N4kP%2BOBQ2Fv6pECkQ3G2F%2FpFRUejjm9pS%2F7aLggmzdXGOaloCJe8g%3D%3D" rel="nofollow">最流行的编程语言JavaScript能做什么?</a>》的文章里,我们看到了JavaScript在各个领域的应用。在这一期里,仍然有很多亮点(new):</p>
<p><img src="/img/remote/1460000007412107?w=1280&h=720" alt="Vue.js" title="Vue.js"></p>
<p><strong>Vue.js</strong>,如果你在使用Vue.js,那么你更应该找到相当的自信了,现在它已经被列入了评估期了。Vue.js是一个简单易上手的框架,并且相当的轻量,在最近的这段时间里,它发挥得相当的出色。</p>
<p>可惜,宝宝现在在用Angular.js 和 Angular 2,毕竟我现在是开发混合应用的。不过相信在半年后,Angular 2 和 Ionic 2是会上榜的。</p>
<p><strong>Ember.js</strong>,尽管没有证据表明这个框架在国内将火起来的趋势,我现在还对这个框架缺乏深入的了解。</p>
<p><strong>ECMAScript 2017</strong>,尽管我现在已经倾向于使用TypeScript,不过 ES2017 还是会用到的,只是我觉得 Babel 对我来说就是个坑啊</p>
<p><img src="/img/remote/1460000007412108?w=1440&h=882" alt="PWA" title="PWA"></p>
<p><strong>Electron</strong>,如果你是一个老读者,那么你已经知道我在很多场合里使用了这个框架,从NodeWebkit开始写编辑器,再到用Electron完成Growth 1.0的桌面版。</p>
<p><strong>Physical Web</strong>,现在我们可以在浏览器上来控制真实世界,通过蓝牙低功耗技术。</p>
<p>不过与此相比,我更看好 <strong>Progressive Web App</strong>,毕竟他可以让Web应用接触到更多的底层API,而不是局限于蓝牙,还可以是Push Notification等等。</p>
<p><img src="/img/remote/1460000007412109?w=1920&h=1080" alt="VR" title="VR"></p>
<p><strong>Three.js</strong>,它上榜的原因是因为 WebVR 的流行。这一点可以从我去年写的那篇《<a href="https://link.segmentfault.com/?enc=aDejNvzxGWXfPSTNkpG6JA%3D%3D.BESd7TwWt%2F0z1XxraonqZqYgiW5EjwWz3ddZyJgwQRhmp4i4fZnQYB0EdNoz69ZjT8%2BBucHyXiX41CfrQBPhlw%3D%3D" rel="nofollow">Oculus + Node.js + Three.js 打造VR世界</a>》,就可以看到一些趋势。这些就和现在的单页面应用一样,虽然运行起来不是那么流畅,但是还是行得通。因而在可见的未来使用 Web 技术来开发 VR 也有一点苗头,未来浏览器上应该是可以运行编译过后的代码,而不是在运行时。</p>
<p><strong>WebRTC</strong>,它可以让我们在浏览器端实现实时视频聊天。第一次接触到这个视频流技术是在两年多以前,上一次接触则是在半年多以前使用 WebRTC + Oculus,你可以在我博客的那篇《<a href="https://link.segmentfault.com/?enc=%2BN9XGYT5%2BPVLJNIGr3NQ6g%3D%3D.BEzO6A19ZLoe3ZT4tTevAY1n1YsB4jFa3SZfKV%2BYja5ISc2xgLYqzaXUPgR32z3YA9BabMJS1XFa%2F5yvj1bNqUo2OLbD2NEzIY1tfEZiB380GQBCEJ6l%2FFNjimv3TkZ8sa5BLrBFJBiod0hlOjPUAbcNIVOot1a7W16kfC2NXePdR4x93%2BCgLd3PSQ4eLAjuYD2I3pG32e6dAcqn%2BnqfMg%3D%3D" rel="nofollow">JavaScript在VR世界的应用</a>》中了解到更多的详细信息。当然如雷达所说,WebRTC将会形成未来在Web上进行AR/VR 协作的基础。</p>
<p>接着再让我们看看一些架构上的变化吧。</p>
<h2>前端引起的架构变化</h2>
<p>在过去的两三年里,前端火得一塌糊涂——对于后端程序员来说,这有点 winter is coming 的感觉。我在那篇《<a href="https://link.segmentfault.com/?enc=ZuTgzBjc8Xh73GS2u%2BuZvg%3D%3D.DDPUw7%2BCEbtncBokmDVjGbIId0j7mVu78DzGVjo5yuaoAQYCJNB%2F1eY%2BPviFEgWS4Iaur1lagH6j3uPGgm2uqLZ285utE1cbsOJLKEofRpnsKufNbER2VgM3nBpFUB2jy3xbT1rWoSuQ9yy%2BDH6RrVZSCgQ%2BWrjbN4y4FXRfeR8R%2Fm3kuTJ8v%2FoeA9MXEO5iCRG8mJhXzDfNgJrWIFs3HQ%3D%3D" rel="nofollow">前端演进史</a>》对前端的演进做了相当多的介绍,并在《<a href="https://link.segmentfault.com/?enc=6xVF8hOQ64M4qud9wxiu%2FA%3D%3D.X%2FNLPkb3I3B%2BLJsH7Wr0YkkI8%2B%2FakCAJMdKvzKkzeg065fb1LMxjqVrhv7j0zyHTSiAdTIwiTZqCFZGa17gdpz7cv5MvLKy%2Bw9AM4XtiUd24btatGQoYz7oeO8MZmSENiFZ9VqVboQSJlUBEyFIvO0zyJyi2a4V31AjaHEAj1o2Qt2oIZGImMKRH6OhhTY5RPLQQ%2FBDRu3iF3Dm%2B7Psimg%3D%3D" rel="nofollow">后台即服务演进史</a>》里对后台即服务开了个头,在这篇文章里让我们根据《技术雷达》来继续补几刀。</p>
<p><img src="/img/remote/1460000007412110?w=834&h=416" alt="前后端分离" title="前后端分离"></p>
<p>我们可以看到在中大型团队里,已经分解为前端和后台两个小组,沟通可以通过接口、契约等等的方式来进行。但是这一点儿也不精益,沟通在这时仍然是一个问题,让我有点怀念起之前前后端都做的项目了——自己可以创建自己想要的接口。</p>
<p>不过,这意味着前端和后台在技术选型上更加独立了。</p>
<h3>臃肿的前端——微前端</h3>
<p><img src="/img/remote/1460000007412111?w=1664&h=1024" alt="前端单体应用" title="前端单体应用"></p>
<p>在上一个项目里,我们一步步地将一个有近10年系统的系统替换掉。起初这是一个传统的Spring + JSP网站,然后我们用JSP创建了JSON API,后来创建了一个新的 API 来服务移动应用和单页面应用,再后来这个 API 被拆分成了几个 API。我们的后台已经成一个单体应用变成了一个微服务架构的应用,但是这一点并没有在前端上应用——前端应用正在变得难以维护。</p>
<p>因此在这一期的雷达里,你可以看到微前端的概念(micro frontends)。这也是在上一个项目里,我们尝试做的一部分,遗憾的是并没有成功完全实施。这是一个搜索类型的网站,网站的首页承担着大部分的访问量,而详情页的主要流量来源则是搜索引擎。我们在首页上使用jQuery + Require.js技术栈,而在其他页面(搜索结果页 + 详情页)使用 React.js,我们在最初的时候考虑过将详情页静态化——因为需要 SEO 的缘故,这样可以让我们降低 SEO 带来的复杂度。</p>
<p><img src="/img/remote/1460000007412112?w=328&h=154" alt="MicroServices" title="MicroServices"></p>
<p>后来,我也在我的博客上解耦了两部分,为了更快的访问首页的速度——将首页独立出来,不使用JS,直接使用Pure.css来担重任;在其他页面里使用Material Design Lite作为 UI 部分。</p>
<p>有一点值得考虑的是:对于微服务架构来说,在一个系统的不同的部分使用不同的技术栈是一种不错的体验;而对于一个前端团队来说,在同一个系统的使用不同的技术栈<strong>就不是</strong>一种不错的体验。</p>
<h3>API 设计——应该变得简单</h3>
<p><img src="/img/remote/1460000007412113?w=1200&h=500" alt="Backend" title="Backend"></p>
<p>如我们所见的<strong>Spring Boot</strong>已经变成推荐采用的程度了,按雷达上的习惯用语:“我们已经在多个项目上使用这个框架”——反正我最近的项目都是用这个框架。如果你考虑使用 Java,那么你一定不要错过这个框架,以及使用这个框架来实施前后端分享。</p>
<p>对于大部分不需要考虑 SEO 的应用来说,将后台变成一系列 RESTful 的 API 并不是一件复杂的事,但是在后台 API 上的设计就变成一件麻烦的事。因此尽管在实见的过程中,有契约来作为保证,但是不一定是可靠的。作为一个前端程序来说,我们在调用后台 API 的过程中,总会遇到这样、那样的问题。除此,还有接口不好用的问题——“要是你可以在这里使用超媒体 API,那么我的代码就会更加简单了”。</p>
<p>因此在 API 设计上,雷达上给出了两个不错的案例:</p>
<h4>强化后台查询</h4>
<p><img src="/img/remote/1460000007412114?w=1602&h=418" alt="GraphQL" title="GraphQL"></p>
<p>代表的例子就是 Facebook 的 GraphQL,它是在 Facebook 内部应用多年的一套数据查询语言和 runtime。原本为了请求一个用户及其好友信息的请求,需要发起多个 API 请求。现在,我们只需要在客户端拼装好对应的 Query语句,在这个语句里将大部分需要查询的东西写好,即 JSON 格式的数据,然后发给服务端来处理。而在我们客户端上,我们所获取到的结果都是我们所需要的,不需要再做特殊处理了。</p>
<p>这一切,看上去很美好——除了,在客户端上拼查询语句。</p>
<p>过去,我们使用搜索引擎来搜索数据,就需要在前端拼好对应的 Query,再传给后台 API,由后台 API 返回我们需要的结果。在这个过程里,我们在Query做一些对应的数据处理。</p>
<p>反正,他们都是使用查询语言来搜索结果。如果你考虑使用 QL 的话,不妨做一层 Wrapper,以后好做迁移。</p>
<h4>前后端同时优化</h4>
<p><img src="/img/remote/1460000007412115?w=1992&h=1008" alt="Falcor" title="Falcor"></p>
<p>Netflix对于这样复杂的API请求下,创建了 自己的库Falcor——它可以从多个数据源获取数据,并在服务端上汇总成一个 JSON model;在客户端上,请求的时候我们只需要在请求的时候加上对应的参数即可——可以将多个请求合并到一起,也可以只针对某一个部分发出请求。这样可以减少发出多个请求,所带来的复杂度。</p>
<p>我想,一种最实用的做法:就是将一些更新频率较低的API合并成一个大的 API 了——大部分人都会这样做吧。</p>
<h3>简化的后台——无服务器架构</h3>
<p><img src="/img/remote/1460000007412116?w=960&h=540" alt="ServerLess" title="ServerLess"></p>
<p>除了上面的这些内容,后台还有一些东西还蛮好玩的,其中一个就是 Serverless 架构,即无服务器架构。不过,这种架构目前在国内运行起来还是有点难度的,缺少一系列的配套措施。如在这期的雷达上的Auth0可以为我们提供一个授权服务,以及AWS Lambda可以直接使用 AWS系列云服务来对数据进行处理。</p>
<p>我就不多说了~~,读者可以自己去看。</p>
<p>那么未来,你看想玩哪种技术。</p>
<p>访问 <a href="https://link.segmentfault.com/?enc=Y9cRk9OwD2ODnUU0H%2FIWlA%3D%3D.HSFQjmgeJa7uqjYULe83eUZvReKykyw5vhtNoez3KxVFljQWZQXTSVszrCrqZKhs" rel="nofollow">https://www.thoughtworks.com/...</a> 获取最新一期ThoughtWorks技术雷达(PS:如果你访问不了原文链接,可以修改DNS为 8.8.8.8,或者放在我的GitHub Page上的备份:<a href="https://link.segmentfault.com/?enc=hhB8EhZIwFs%2BsOtd6a92iQ%3D%3D.UWqvlY0VjsGUyDUMYDdO6%2BnbfGQv0YkoYo%2FBO5e71F%2F%2BJliWY%2BfPoq9pG%2F6bP7PL" rel="nofollow">http://radar.phodal.com/2016.pdf</a> )</p>
译书《物联网实战指南》出版 | 新成就:翻译自己的英文简介
https://segmentfault.com/a/1190000007101766
2016-10-09T07:42:29+08:00
2016-10-09T07:42:29+08:00
phodal
https://segmentfault.com/u/phodal
0
<p>这本书有一个很长的故事,到今天算是走到了一个意想中的结局。从审阅这本书开始、英文版出版、翻译成中文就这样走了两年的时间,这是一本值得纪念的书籍。</p>
<p>英文书名《Learning Internet of Things》,中文书名 《物联网实战指南》。</p>
<h2>《物联网实战指南》简介</h2>
<p>简单的先上个简介啦~:</p>
<blockquote><p>本书从探讨流行的HTTP、UPnP、CoAP、MQTT和XMPP等物联网协议开始,并从实战角度介绍了现有的协议、通信模式、构架以及物联网安全的重要性。本书适合那些对物联网感兴趣的开发者和工程师阅读。那些对电子学、树莓派(RaspberryPi)或者卡片电脑有基本的了解(高中水平)以及有一些代码托管的编程经验的人,通过本书将会很快学到当前先进的物联网解决方案。</p></blockquote>
<p>简单的一名话就是:</p>
<blockquote><p>在Raspberry Pi上使用 C# 开发物联网应用。</p></blockquote>
<p>我从这本书中,学到了相当多的东西——我写的《自己动手设计物联网》的一些知识点,如MQTT协议,也是从这本书上了解到的。还好我的书是用JavaScript写的,而且是以我的毕业设计为思路写的,中间加了个MQTT协议和CoAP协议。</p>
<p>这本书也介绍了其他的相关物联网协议,总得来说内容相关的不错,除了用 C#。</p>
<h3>审阅者介绍</h3>
<p>先让我装个逼~~~</p>
<p>当时在写Reviewer简介的时候,考虑到我刚毕业,又没有什么内容可以写。我只好写我在Web开发和硬件上有四年经验,不敢在上面写我从小开始写代码。</p>
<blockquote><p>Phodal Huang has over 4 years of experience in hardware and web development. He graduated from Xi'an University of Arts and Science. He currently works at ThoughtWorks as a developer. He is the owner of the mini IoT project (<a href="https://link.segmentfault.com/?enc=61gmn3gg3D933%2FHTReqZHQ%3D%3D.7JAEJVFlm6g8benrz4pajhIDOFRWSzfUUY6bBRvSqFU%3D" rel="nofollow">https://github.com/phodal/iot)</a> and the author of the eBook, Design IoT (<a href="https://link.segmentfault.com/?enc=3J6baMJenqsU3MP8l597JA%3D%3D.9FmEIk9opmenIdvqHRAWIkaVN4Y3MImJz%2BUfC3d84e8%3D" rel="nofollow">http://designiot.phodal.com,</a> in Chinese). <br>He loves designing, painting, writing, traveling, and hacking; you can nd out more about him on his personal website at <a href="https://link.segmentfault.com/?enc=ixET3P93J%2FkdxNrb1DvkKA%3D%3D.ClQdAuBGLZ1%2BKJysmCLQ5s2y4jfvB6koirdqBz%2Bhs%2BE%3D" rel="nofollow">http://www.phodal.com.</a></p></blockquote>
<p>我在翻译的时候美化了一下:</p>
<blockquote><p>黄峰达(Phodal Huang)目前是ThoughtWorks公司的一名软件工程师。其是最小物联网系统(<a href="https://link.segmentfault.com/?enc=afIMBh4M%2ByaBKqVBUrBGUw%3D%3D.YW6V3nQGP1i5jQTgd1oRbYh4g1CeWSIto2y9jyA8XSw%3D" rel="nofollow">https://github.com/phodal/iot</a>)项目的创建者,同时也是电子书《一步步设计物联网》(<a href="https://link.segmentfault.com/?enc=vuqlsiXeTTGA%2BxkACu2KvQ%3D%3D.zKdlxKtiZyD4Is1CG%2Fw1yCcYhvElqMA%2B5H7MGQ2Dr7w%3D" rel="nofollow">http://designiot.phodal.com</a>)的作者。其喜欢设计、画画、写作、旅行以及Hacking,在其个人网站(<a href="https://link.segmentfault.com/?enc=PTkA5VDm6FeJiQgaV2DSKA%3D%3D.IUhwHrwWm2B6ft%2Bg6qDFEa6geocfCZvwR2e7vPrTkL0%3D" rel="nofollow">http://www.phodal.com</a>)可看到更详细的信息。</p></blockquote>
<p>在最近出版的《Smart Internet of Things Projects》我是这样写自己的简介的——把四年改成了六年。</p>
<blockquote><p>Phodal Huang has over six years' experience in hardware development & web development<br> ...</p></blockquote>
<p>于是就占用大家宝贵的阅读时间,来让我介绍一下这本书的简史。</p>
<h3>英文版流水帐</h3>
<p>2014年7月28日 20:22 (星期一) 收到Packt出版社的邮件,他们在GitHub上发现了我(PS:当时我的GitHub并没有今天的这么拿得出手——只有我的毕业设计IoT项目。只是当时在GitHub上搜索IoT的时候,他们都在首页):</p>
<blockquote><p>I came across your profile on github and I believe you’ll be best fit to review our current project on Internet of Things.</p></blockquote>
<p>并表示,他们正在出版《Learning Internet of Things》,问我有没有意愿审阅这本书。</p>
<blockquote><p>We're currently developing a book on Internet of Things titled ‘Learning Internet of Things’ with a page count of approximately 186 pages. <br> ...<br>Would you be interested in reviewing this book?</p></blockquote>
<p>于是,我回了这个邮件。说明了情况:当时我刚毕业,并表示我的英语不好。</p>
<blockquote><p>Recently, I join the ThoughtWorks as a developer. I'm a Chinese and not very good at English except reading, so I practice lots of these days. As you known,I have some experience on IOT. If you think is OK,I would to do.</p></blockquote>
<p>然后,然后我们就继续了。</p>
<p>2014年8月8日(星期五) 上午8:51 我收到这本书的第一章和第二章,并开始审阅这本书。</p>
<p>。。。 中间,我就不断地审阅不同的章节</p>
<p>2014年11月27日(星期四) 下午4:04 (UTC+05:00 伊斯兰堡、塔什干时间) 我收到最后一章的内容。</p>
<p>2015年1月28日(星期三) 下午3:37 (UTC+05:00 伊斯兰堡、塔什干时间) 这本书的电子版发布。</p>
<p>2015年2月10日(星期二) 中午1:15 这本书的纸质版发布。</p>
<h3>中文版流水账</h3>
<p>2015年2月9日 01:51 (星期一) 收到机械工业出版社的邮件:</p>
<blockquote><p>我们计划引进Packt Publishing的《Learning Internet of Things》这本书,通过试读样张中得知你是这本书的技术审阅者。。。</p></blockquote>
<p>2015年7月22日 14:32 (星期三) 这本书翻译完~~。</p>
<p>2016年9月19日 这本书的出版日期。</p>
<h2>《物联网实战指南》与《自己动手设计物联网》</h2>
<p>这里面又有好多的故事,不过正是因为《Learning Internet of Things》一书让我有了写书的想法——不过,最开始的时候是电子书。即是我GitHub上的《一步步搭建物联网系统》,已更名为《教你设计物联网系统》的项目。最开始的时候,是图灵教育出版社的编辑先联系我的,时间线上是 2014年11月21日 17:09 (星期五) 。</p>
<p>因为我刚毕业所以没这么大的能耐去写这样的书,就先拒绝了,继续以电子书的形式存在。</p>
<p>直到我翻译完《Learning Internet of Things》,电子工业出版社的编辑开始联系我写书,我便开始写《自己动手设计物联网》。</p>
<p>期间我还顺手审阅了 Manning 出版社的一本 IoT 相关的书的目录——因为我觉得目录不好,估计也有同样的审阅者也是这么觉得的。后来,这本书就没有机会出版了。</p>
<p>欢迎购买这两本书,并在各大网店上给个好评哈。</p>
<p><img src="/img/bVDXEL?w=1280&h=960" alt="图片描述" title="图片描述"></p>
<p>我们将在未来的两周末,赠送出几本的《物联网实战指南》和《自己动手设计物联网》,详情见:</p>
<p><img src="/img/bVsHJ9" alt="图片描述" title="图片描述"></p>
让你的「微信小程序」运行在Chrome浏览器上,让我们使用WebStorm
https://segmentfault.com/a/1190000007016058
2016-09-27T07:32:49+08:00
2016-09-27T07:32:49+08:00
phodal
https://segmentfault.com/u/phodal
0
<p>「微信小程序」的开发框架体验起来,还不错——自带了UI框架。但是问题是他的IDE,表现起来相当的糟糕——其实主要是因为,我当时买WebStorm License买了好多年。所以,我觉得他的IDE真不如我这个付费好用。</p>
<p>而且,作为一个拥护自由和开源的 「GitHub 中国区首席Markdown程序员」。微信在「微信小程序」引导着Web开向封闭,我们再也不能愉快地分享我们的代码了。</p>
<p>如果我们放任下去,未来的Web世界令人堪忧。</p>
<p>好了,废话说完了:</p>
<p>文章太长不想看,可以直接看Demo哈哈:</p>
<p>GitHub: <a href="https://link.segmentfault.com/?enc=Qra6b9tbAn9kfZ%2BKFeIi%2FA%3D%3D.lJsip2%2FB6ZUX2oc9uGrUgIkK3qe1%2FuQidYEGq2xqxOAHESKFrIBhXD6GgvH7JtKH" rel="nofollow">https://github.com/phodal/weapp-webdemo</a><br>预览:<a href="https://link.segmentfault.com/?enc=veKfYggFl%2BvnoG92hNTGOw%3D%3D.dGMB7mNp0oKh8aZC0OHiOjYDLsQd2SYAHc%2F5RmrXXZw%3D" rel="nofollow">http://weapp.phodal.com/</a></p>
<h2>真实世界下的MINA三基本元素</h2>
<p>「微信小程序」的背后运行的是一个名为MINA框架。在之前的几篇文章里,我们介绍得差不多了。现在让我们来作介绍pipeline:</p>
<h3>Transform wxml和wxss</h3>
<p>当我们修改完WXML、WXSS的时候,我们需要重新编译项目才能在浏览器上看到效果。这时候后台就会执行一些<strong>transform</strong>动作:</p>
<ol>
<li><p>wcc来转换wxml为一个genrateFun,执行这个方法将会得到一个virtual dom</p></li>
<li><p>wxss就会转换wxss为css——这一点有待商榷。</p></li>
</ol>
<p>wcc和wxss,可以从vendor目录下获取到,在“微信web开发者工具”下敲入<code>help</code>,你就会得到下面的东东:</p>
<p><img src="/img/bVDBmn?w=1876&h=248" alt="图片描述" title="图片描述"></p>
<p>运行<code>openVendor()</code>,你就会得到上面的wcss、wxss、WAService.js、WAWebview.js四个文件了。</p>
<h3>Transform js文件</h3>
<p>对于js文件来说,则是一个拼装的过程,如下是我们的app.js文件:</p>
<pre><code class="javascript">App({
onLaunch: function () { }
})</code></pre>
<p>它在转换后会变成:</p>
<pre><code class="javascript">define("app.js", function(require, module){var window={Math:Math}/*兼容babel*/,location,document,navigator,self,localStorage,history,Caches;
App({
onLaunch: function () {
}
})
});
require("app.js");</code></pre>
<p>我假装你已经知道这是什么了,反正我也不想、也不会解释了~~。同理于:</p>
<pre><code class="javascript">define("pages/index/index.js", function(require, module){var window={Math:Math}/*兼容babel*/,location,document,navigator,self,localStorage,history,Caches;
Page({
data: {
text: initData
}
});
require("pages/index/index.js");</code></pre>
<p>至于它是如何replace或者apend到html中,我就不作解释了。</p>
<h2>MINA如何运行?</h2>
<p>为了运行一个Page,我们需要有一个virtual dom,即用wcc转换后的函数,如:</p>
<pre><code class="javascript"> /*v0.7cc_20160919*/
var $gwxc
var $gaic={}
$gwx=function(path,global){
function _(a,b){b&&a.children.push(b);}
function _n(tag){$gwxc++;if($gwxc>=16000){throw 'enough, dom limit exceeded, you don\'t do stupid things, do you?'};return {tag:tag.substr(0,3)=='wx-'?tag:'wx-'+tag,attr:{},children:[]}}
function _s(scope,env,key){return typeof(scope[key])!='undefined'?scope[key]:env[key]}
function _wl(tname){console.warn('template `' + tname + '` is being call recursively, will be stop.')}
function _ai(i,p,e,me){var x=_grp(p,e,me);if(x)i.push(x);else{console.warn('path `'+p+'` not found from `'+me+'`')}}
function _grp(p,e,me){if(p[0]!='/'){var mepart=me.split('/');mepart.pop();var ppart=p.split('/');for(var i=0;i<ppart.length;i++){if( ppart[i]=='..')mepart.pop();else if(!ppart[i])continue;else mepart.push(ppart[i]);}p=mepart.join('/');}if(me[0]=='.'&&p[0]=='/')p='.'+p;if(e[p])return p;if(e[p+'.wxml'])return p+'.wxml';}
//以下省略好多字。</code></pre>
<p>然后在我们的html中加一个script,如</p>
<pre><code class="javascript">document.dispatchEvent(new CustomEvent("generateFuncReady", {
detail: {
generateFunc: $gwx('index.wxml')
}
}))</code></pre>
<p>就会凑发这个事件了。我简单的拆分了WXWebview.js得到了几个功能组件:</p>
<ul>
<li><p>define.js,这里就是定义AMD模块化的地方</p></li>
<li><p>exparser.js,用于转换WXML标签到HTML标签</p></li>
<li><p>exparser-behvaior.js,定义不同标签的一些行为</p></li>
<li><p>mobile.js,应该是一个事件库,好像我并不关心。</p></li>
<li><p>page.js,核心代码,即Page、App的定义所在。</p></li>
<li><p>report.js,<strong>你所说的一切都能够用作为你的呈堂证供</strong>。</p></li>
<li><p>virtual_dom.js,一个virtual dom实现结合wcc使用,里面应该还有component.css,也可能是叫weui</p></li>
<li><p>wa-wx.js,定义微信各种API以及WebView和Native的地方,和下面的WX有冲突。</p></li>
<li><p>wx.js,同上,但是略有不同。</p></li>
<li><p>wxJSBridge.js,Weixin JS Bridge</p></li>
</ul>
<p>于是,我就用上面的组件来定义不同的位置好了。当我们触发自定义的<code>generateFuncReady</code>事件时,将由virtual_dom.js来接管这次Render:</p>
<pre><code class="javascript">document.addEventListener("generateFuncReady", function (e) {
var generateFunc = e.detail.generateFunc;
wx.onAppDataChange && generateFunc && wx.onAppDataChange(function (e) {
var i = generateFunc((0, d.getData)());
if (i.tag = "body", e.options && e.options.firstRender){
e.ext && ("undefined" != typeof e.ext.webviewId && (window.__webviewId__ = e.ext.webviewId), "undefined" != typeof e.ext.downloadDomain && (window.__downloadDomain__ = e.ext.downloadDomain)), v = f(i, !0), b = v.render(), b.replaceDocumentElement(document.body), setTimeout(function () {
wx.publishPageEvent(p, {}), r("firstRenderTime", n, Date.now()), wx.initReady && wx.initReady()
}, 0);
} else {
var o = f(i, !1), a = v.diff(o);
a.apply(b), v = o, document.dispatchEvent(new CustomEvent("pageReRender", {}));
}
})
})</code></pre>
<p>因此,这里就是负责DOM初始化的地方了,这里得到的Dom结果是这样的:</p>
<pre><code class="html"><wx-view class="btn-area">
<wx-view class="body-view">
<wx-text><span style="display:none;"></span><span></span></wx-text>
<wx-button>add line</wx-button>
<wx-button>remove line</wx-button>
</wx-view>
</wx-view></code></pre>
<p>而我们写的wxml是这样的:</p>
<pre><code class="html"><view class="btn-area">
<view class="body-view">
<text>{{text}}</text>
<button bindtap="add">add line</button>
<button bindtap="remove">remove line</button>
</view>
</view></code></pre>
<p>很明显view会被转换为wx-view,text会被转换为wx-text等等,以此类推。这个转换是在virtual dom.js中调用的,调用的方法就是exparser。</p>
<p>遗憾的是我现在困在 data初始化上面了~~,这里面有两套不同的事件系统,有一些困扰。其中有一个是:WeixinJSBridge、还有一个是app engine中的事件系统,两个好像不能互调。。。</p>
<h2>使用WebStorm开发</h2>
<p>在浏览器上运行之前,我们需要简单的mock一些方法,如:</p>
<ul>
<li><p>window.webkit.messageHandlers.invokeHandler.postMessage</p></li>
<li><p>window.webkit.messageHandlers.publishHandler.postMessage</p></li>
<li><p>WeixinJSCore.publishHandler</p></li>
<li><p>WeixinJSCore..invokeHandler</p></li>
</ul>
<p>然后把 <code>config.json</code>中的一些内容变成<code>__wxConfig</code>,如:</p>
<pre><code class="javascript">__wxConfig = {
"debug": true,
"pages": ["index"],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
},
"projectConfig": {
},
"appserviceConfig": {
},
"appname": "fdfafafafafafafa",
"appid": "touristappid",
"apphash": 2107567080,
"isTourist": true,
"userInfo": {}
}</code></pre>
<p>如这里我们的appname是<code>哈哈哈哈哈哈哈</code>——我家在福建。</p>
<p>然后在我们的html中引入各个js文件,啦啦。</p>
<p>我们还需要一个自动化的glup脚本来watch wxml和wxss的修改,然后编译,如:</p>
<pre><code class="javascript">exec('./vendor/wcc -d ' + inputPath + ' > ' + outputFileName, function(err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
});</code></pre>
<p>说了这么多,你还不如去看代码好了:</p>
<p>GitHub: <a href="https://link.segmentfault.com/?enc=%2Fnz0E3s971m9PHFoKSzyKg%3D%3D.4i%2Fv3%2FidAnOyGnBgKJ3pQo3ULkqICqfW4756cIRtQQHvbpNg1OVjMDFVUWYjsbP1" rel="nofollow">https://github.com/phodal/weapp-webdemo</a><br>预览:<a href="https://link.segmentfault.com/?enc=kDqiLBpvCxW%2FQRwlIoRfLw%3D%3D.tuC3IqZJdK14W4Kc1kuC2M7kdpruK1pfYfxFx7SzJcM%3D" rel="nofollow">http://weapp.phodal.com/</a></p>
JavaScript在物联网中的应用
https://segmentfault.com/a/1190000006223271
2016-08-10T10:25:41+08:00
2016-08-10T10:25:41+08:00
phodal
https://segmentfault.com/u/phodal
6
<blockquote><p>凡是能用JavaScript写出来的,最终都会用JavaScript写出来。</p></blockquote>
<p>—— Atwood定律</p>
<p>在那篇《<a href="https://link.segmentfault.com/?enc=mno%2B0%2BjcydZMdjRCpIHjNA%3D%3D.RaNvfJ3do5KZtc%2FmnFph6VdQRbKIav%2FNHwGOx9LBz4wbED85IjFoeMr6vo%2FAc8%2FNE4AmD84SVptScJWiWE4peycF8QdQQwx7Pyik73f7TKEH1D6apnMj0c1OBc0EsX8hGZB1YVGv7XYmHobFnPAPIqJ2dQBTcWgeodpQU%2BD2udd7O0FFIsOP5ilSedv3P1fGBAeBub%2B8fOkUlQT0KU1Opg%3D%3D" rel="nofollow">最流行的编程语言JavaScript能做什么?</a>》里,我们列举了JavaScript在不同领域的使用情况,今天让我们来详解一下JavaScript在物联网中的应用。</p>
<h2>基础:物联网的三个层级</h2>
<p>开始之前, 先让我们简单地介绍点物联网的基础知识。如果你有点Web开发经验的话,都知道下图是CS架构:</p>
<p><img src="/img/remote/1460000006774589" alt="Client-Server架构" title="Client-Server架构"></p>
<p>相比于一个物联网系统,无非就是多了一层硬件层以及可选的协调层。</p>
<p><img src="/img/remote/1460000006223276" alt="源自《自己动手设计物联网》" title="源自《自己动手设计物联网》"></p>
<p>这个硬件层决定了物联网应用比Web应用更加复杂。对于大部分的Web应用来说 ,客户端都是手机、电脑、平板这些设备,都有着强大的处理能力,不需要考虑一些额外的因素。</p>
<p>对于物联网应用来说,我们需要考虑设备上的MCU的处理能力,根据其处理能力和使用环境使用不同的通信协议,如我们在一些设备上需要使用CoAP协议。在一些设备上不具备网络功能,需要考虑借助于可以联网的协助层,并且还需要使用一些短距离的无线传输协议,如低功耗蓝牙、红外、Zigbee等等。</p>
<h2>一个物联网系统:六种语言</h2>
<p>两年半以前,大四,电子信息工程,我选定的毕业论文是一篇关于物联网的论文——《基于REST服务的最小物联网系统设计》。这是一篇入门级的物联网论文,如果大部分学习CS的人有一点硬件基础,都能写出这样的论文。</p>
<p>这篇论文是之前参加比赛的作品论文的“最小化”,里面使用到的主要就是创建RESTful服务,而它甚至称不上是一种技术。在这个作品里:</p>
<ul>
<li><p>我们使用Python语言里的Django框架作为Web服务框架,使用Django REST Framework来创建RESTful服务。</p></li>
<li><p>为了使用手机当控制器,我们还要用Java写一个Android应用。</p></li>
<li><p>我们使用Raspberry Pi作为硬件端的协调层,用于连接网络,并传输控制信号给硬件。</p></li>
<li><p>我们在硬件端使用Arduino作为控制器,写起代码特别简单,可以让我们关注于业务。</p></li>
<li><p>最后,我们还需要在网页上做一个图表来显示实时数据。</p></li>
</ul>
<p>所有的这些,我们需要使用Python、Java、JavaScript、C、Arduino五种语言。而如果我们要写相应的iOS应用,我们还需要Objective-C。</p>
<p><img src="/img/remote/1460000006223297" alt="你是在逗我吗?" title="你是在逗我吗?"></p>
<h2>JavaScript在物联网领域的发展</h2>
<p>同样的,两年多以前,刚实习,在我们的项目里,我们的新项目里我们使用Backbone作为单页面应用框架的核心来打造Web应用。这时,我开始关注Node.js实现物联网应用的可能性。</p>
<p><img src="/img/remote/1460000006223289" alt="Node.js Express Mongodb" title="Node.js Express Mongodb"></p>
<p>当时,已经有了物联网协议MQTT和CoAP协议的库,于是我照猫画虎地写了一个支持HTTP、CoAP、WebSocket和MQTT的物联网。由于,当时缺乏一些大型应用的开发经典,所以做得并不是很好,但是已经可以看到JavaScript在这方面的远景。</p>
<p><img src="/img/remote/1460000006223299" alt="Ionic Cordova" title="Ionic Cordova"></p>
<p>一年多以前,Ionic还没推出正式版的时候,我发现到了这个框架真的很棒——它自带了一系列的UI,还用NgCordova集成了Cordova的一系列插件。我便开始使用Ionic写了一些移动应用,发现还挺顺手的。接着,我就开始拿这个框架尝试写物联网应用,这需要一些原生的插件,如BLE、MQTT。后来,我也写了一个简单的CoAP插件。</p>
<p><img src="/img/remote/1460000006223305" alt="Iot" title="Iot"></p>
<p>后来我们不再需要编译Node.js,就可以在ARM处理器上运行Node.js。并且我们已经有Tessel、Espruino、Kinoma Create、Ruff这些可以直接运行JavaScript的开发板。三星还推出iot.js,可以让更多的嵌入式设备可以使用JavaScript语言作为开发语言。</p>
<p><img src="/img/remote/1460000006223303" alt="Node.js Future" title="Node.js Future"></p>
<p>人们开始在硬件上使用JavaScript的原因有很多,如Web的开发人员是最多的、JavaScript很容易上手。</p>
<p>现在,这次我们在这三个层级上都可以使用JavaScript,只需要一种语言。</p>
<h2>使用一种语言开发物联网应用:JavaScript</h2>
<p>在我写的那本《自己动手设计物联网》中,我就试图去展示JavaScript在这方面的威力。使用Node.js + Node-CoAP + MQTT.js + MongoDB + Express搭建了一个支持多协议的物联网:</p>
<p><img src="/img/remote/1460000006223307" alt="Lan IoT" title="Lan IoT"></p>
<p>不过,上图是完善版的物联网,代码自然是在GitHub上啦:<a href="https://link.segmentfault.com/?enc=iFvXU2OPRLxhRpvtfDH7jg%3D%3D.NDQ92xTQix%2Bie9%2B6MF13awFajgd6imeA7%2BomGBRSwrE%3D" rel="nofollow">Lan</a>。作为服务端来说,Node.js的能力已经是经过验证的。而在混合应用上,仍然也可以经受住考验,混合应用在手机上做个图表是轻轻松松的事(只需要获取数据,然后显示):</p>
<p><img src="/img/remote/1460000006223318" alt="混合应用图表" title="混合应用图表"></p>
<p>作一个控制端也是轻轻松松的事(我们只需要发个POST请求,更具逻辑一点的就是先获取状态):</p>
<p><img src="/img/remote/1460000006223312" alt="Led控制" title="Led控制"></p>
<p>而在硬件端,我并没有在书中以JavaScript作为例子来展示JavaScript的用法,因为这会局限了用户的硬件设备。</p>
<p>不过,我们仍然可以使用类似于Johnny-Five这样的库来做硬件方面的编程,只是它没有那么好玩~~。</p>
<p>既然我们可以JavaScript来实现,为什么我们还要喝杯咖啡等它用C编译完呢?</p>
<p>你想知道的答案都在这本书里,已在亚马逊、京东、当当上架:</p>
<p><img src="/img/remote/1460000006223314" alt="自己动手设计物联网" title="自己动手设计物联网"></p>
<p>亚马逊:<a href="https://link.segmentfault.com/?enc=oyFQGmjm1Q9steOyr3TJnQ%3D%3D.goV1HSw3omLZzwFJLxe%2FgFRiL6IgXikku5wcMb9xY0YOYVt5XPPG6VSUZshCauOW" rel="nofollow">https://www.amazon.cn/dp/B01I...</a></p>
<p>京东:<a href="https://link.segmentfault.com/?enc=pxRXHUPx%2FwXur4i%2FdkQxnw%3D%3D.yHd9kPCcVyhEXUidmAGo%2BxcFSVAMahX73l7v5Rr2fJwmd81%2Bp19tQsoSFXMzAUIh" rel="nofollow">http://item.jd.com/11946585.html</a></p>
<p>毕竟:</p>
<blockquote><p>凡是能用JavaScript写出来的,最终都会用JavaScript写出来。</p></blockquote>
学好编程,你还需要这个开源APP | Growth 2.0现已发布
https://segmentfault.com/a/1190000006176105
2016-08-05T07:09:10+08:00
2016-08-05T07:09:10+08:00
phodal
https://segmentfault.com/u/phodal
1
<p>终于等来了Growth 2.0从APP Store审核通过了,想想觉得这个过程也蛮不容易的——从最早的一篇文章开始,然后变成了一个APP,它还衍生出了两本电子书。今天它仍然再前进着,也希望它能带领大家一起前进。</p>
<h2>Web应用开发过程与Growth</h2>
<p>在那篇RePractise文章里,我们提到过Web的开发过程是这样的七个步骤:</p>
<ul>
<li><p>前期准备</p></li>
<li><p>编码</p></li>
<li><p>上线</p></li>
<li><p>数据分析</p></li>
<li><p>持续交付</p></li>
<li><p>遗留系统</p></li>
<li><p>回顾与新架构</p></li>
</ul>
<p>再加上一个初学者在最开始的时候需要一些基础知识,就构成了Growth的基本内容了。</p>
<p><img src="/img/remote/1460000006767575" alt="main-view.png" title="main-view.png"></p>
<p>经历了一个又一个的项目,我们就会得到这样的经验。大部分的项目也是这样的开发过程,那么这就是很理想的学习资料了——这相当于是我们的业务,既然我们的业务已经稳定了。那么我们就可以在这之外一点点补充我们的技术即可,而这些技术并不局限于任何特有的框架和技术。</p>
<p>换句话来说,这只是一系列的理由知识。所以在第一个版本之后,人们就希望上面可以有实战的内容,还希望有一些实战项目。因此就有了两本电子书《Growth:增长工程师实战》和《Ideabook:练手项目集》,在2.0里这两本电子书也放到了里面:</p>
<p><img src="/img/remote/1460000006176108" alt="ebooks.png" title="ebooks.png"></p>
<p>最开始的时候,在写实战这本电子书的时候我是拒绝的——不想受限于技术栈。在里面我们使用了Python语言,并使用了Django作为Web开发框架,使用Ionic作为移动应用的框架。也因此我们的开发速度相当的快,我想这也会让大家有更快的学习速度。</p>
<h2>在编程世界里探索</h2>
<p>在学习的过程中,人们需要有一些测验、有一些练手项目、还有一些发展路线,这就变成了我们的探索栏目。对于测验功能来说,要做起来倒也是容易——无非就是收集一些面试题,然后提问呗。</p>
<p><img src="/img/remote/1460000006176110" alt="quiz.png" title="quiz.png"></p>
<p>对于学习路线来说,可能就不是那么容易了。需要尽可能地去收集某一个领域的技术栈,然后一一分类,再做出一些合适的判断。</p>
<p><img src="/img/remote/1460000006176112" alt="roadmaps.png" title="roadmaps.png"></p>
<p>然后,我们还需要一些练手项目,但是现在有太多的新技术。我们还需要尽可能多地将他们一一地罗列出来:</p>
<p><img src="/img/remote/1460000006176114" alt="practises.png" title="practises.png"></p>
<p>某个瞬间想到了自己整理的自己的工具箱也可以变成大家的工具箱,就有了这样的一个新栏目:</p>
<p><img src="/img/remote/1460000006176118" alt="toolbox.png" title="toolbox.png"></p>
<p>当人们学习到一定的程度就想着去寻找一些解决方案了,想了想这似乎也是我擅长的内容,就有了:</p>
<p><img src="/img/remote/1460000006176116" alt="solutions.png" title="solutions.png"></p>
<p>这个APP很快地就变成了一个Awesome Lists了。收集用户反馈在很多时候都不是一件容易的事,而在这个时候我们只能一点点的去判断。有时候,难免会做出一些错误的判断。</p>
<h2>累!</h2>
<p>很多琐事做多了也就觉得有点累,而这时候你的APP突然又有可能遭遇这样的场景!</p>
<p><img src="/img/remote/1460000006176120" alt="1stars.png" title="1stars.png"></p>
<p>还是会觉得心里有点不爽。所以,如果你觉得这个APP好,那么你就给个好评呗:</p>
<p><img src="/img/remote/1460000006176122" alt="ratings.png" title="ratings.png"></p>
<p>它开源并且免费,而且数以万计的人正在使用它。</p>
<p>下载地址:<a href="https://link.segmentfault.com/?enc=3cHr6ajtMAoz3S2Va%2BKqow%3D%3D.wYKG0CytDFH9umCr5lGhX6j2LD27SZcPwgQcNk6yaEo%3D" rel="nofollow">http://growth.ren/</a></p>
<p>或者在APP Store以及相应的Android应用商店搜索:Growth,或者搜索Fengda。</p>
【福利】那些年我总结的Web开发者成长路线
https://segmentfault.com/a/1190000005980464
2016-07-15T23:18:12+08:00
2016-07-15T23:18:12+08:00
phodal
https://segmentfault.com/u/phodal
2
<p>从实习后的那些年里,我就开始经常总结一下自己的学习路线,成长路线等等。今天,就重新把这些资料再放出来啦啦。</p>
<p>当然,这些资料也都是在我的GitHub上有的啦。</p>
<h2>Developer成长路线图</h2>
<p>最开始的时间,我并没有想到这张大图可以如此的受欢迎。在最开始的时候,我只想整理一下,我学习了什么东西、觉得什么东西不错。。</p>
<p><img src="http://articles.phodal.com/roadmap/tree.gif" alt="Tree" title="Tree"></p>
<p>GitHub: <a href="https://link.segmentfault.com/?enc=n9BcgS%2BiGx%2FLuJuuc36H4A%3D%3D.Tv6NQY8xvWb0U4V6IMQH5bKjpnmv0uL1QzymEeNlhIlzUBdpv6YzW3xXAdly9xek" rel="nofollow">https://github.com/phodal/developer</a></p>
<h2>Sherlock 技能树</h2>
<p>最开始的时候这是一个Fork的项目,后来它用D3.js动态生成了技能树·~。</p>
<p>除了Developer上的一些路线,它还有推荐书籍。</p>
<p><img src="http://articles.phodal.com/roadmap/sherlock.jpg" alt="Sherlock" title="Sherlock"></p>
<p>GitHub: <a href="https://link.segmentfault.com/?enc=05W7DqJ7%2BPx8nY6YamlKig%3D%3D.60gdNMD9lBixAAl1AcObJ9W%2BvABMydJKH6AaLsiwUlO89t0N0Y8HbCthchneLD3Q" rel="nofollow">https://github.com/phodal/sherlock</a></p>
<h2>Developer进阶书单</h2>
<p>同样的,Sherlock的主要问题是,没有提供一个好的学习路线,就变成了这个项目。这是基本的模式:</p>
<p><img src="http://articles.phodal.com/roadmap/booktree.png" alt="Book Tree" title="Book Tree"></p>
<p>还推荐了不同的学习路线图,如前端、DDD、架构等等。</p>
<p><img src="http://articles.phodal.com/roadmap/code.jpg" alt="Code" title="Code"></p>
<p>GitHub: <a href="https://link.segmentfault.com/?enc=g%2BJvxe5KJmPXehqY5jtoIA%3D%3D.hmBRu12qMoXvGlfAvcpZb0XJ8soBhrWaAVRLyhg%2FMxRfbxrljoxUhUkImpWNw96o" rel="nofollow">https://github.com/phodal/booktree</a></p>
<h2>Growth</h2>
<p>遗憾的是,上面的内容都是分散的,于是我们就有了Growth。它是一款专注于Web开发者成长的应用,涵盖Web开发的流程及技术栈,Web开发的学习路线、成长衡量等各方面。在这里,你将通过不断检验自己的学习成效,形成属于你自己的独一无二的个性技能图谱。</p>
<p><img src="http://articles.phodal.com/roadmap/2.pic.jpg" alt="Growth" title="Growth"></p>
<p>GitHub: <a href="https://link.segmentfault.com/?enc=RPnKwmS4nAFWVY8%2FI1lFxw%3D%3D.Cd25ly7V4YtJi8DW6lS0huVCJukiuEiAo33KzpxjJN%2Fab7D9G7FUisdn40p49lFG" rel="nofollow">https://github.com/phodal/growth</a></p>
<h2>Growth: 全栈工程师增长指南</h2>
<p>当我整理出APP的时候,发现这个APP太重了,于是我又整理出了一本电子书。依据在《Repractise简介篇:Web开发的七天里》中所说的 Web 开发的七个步骤而展开的电子书。当然它也是一个 APP,它是一本关于如何成为全栈增长工程师的指南。</p>
<p>GitHub: <a href="https://link.segmentfault.com/?enc=hMgfeH4JCjbrx%2FUBBM5HKA%3D%3D.nC9cfqTfP3gA0wL3ndzFX%2FT4rOaaM3tkj%2F0X0KFutosdf2HZMeDL9UhoAyI22mGU" rel="nofollow">https://github.com/phodal/growth-ebook</a></p>
<h2>Growth:全栈增长工程师实践</h2>
<p>我总以为这时候就结束了,后来发现并没有,人们还想要一个实战手册。于是,我们就有了这样的一个实战手册。</p>
<p>在Growth中我们介绍的只是一系列的实践,而Growth实战则会带领读者去履行这些实践。你将会看到:如何开发一个Web应用(博客)、如何编写测试——单元测试、功能测试、自动化UI测试、搭建并使用持续集成、添加SEO支持——Sitemap、站长工具和Google Analytics、创建API,制作AutoComplete</p>
<p>开发相应的APP及其API——查看文章、用户登录、发表文章等等。</p>
<p>GitHub: <a href="https://link.segmentfault.com/?enc=xS87rTQtzz0trA8gWOSCyg%3D%3D.SFYpFBW%2BdMYHFf0HZuPXnHIsybBuPa7RML1Wni%2BjehLbbQi1IPqrgfGneHTGDVVa" rel="nofollow">https://github.com/phodal/growth-in-action</a></p>
<h2>Ideabook: 一个全栈增长工程师的练手项目集</h2>
<p>后来的后来,人们想要更多的实践项目,于是就有了这本电子书。</p>
<p>你是不是在为提高编程技术而发愁?</p>
<p>你是不是在为找不到合适的练手项目而烦恼?</p>
<p>你是不是在为有合适的项目,但是没有指南而烦恼?</p>
<p>GitHub: <a href="https://link.segmentfault.com/?enc=Ii357fSaBoJSjxwpzMMIFg%3D%3D.%2FM4b6M6kLCMQ2WSJld%2BtUdb4NfPwVGMA9Hqz%2BnA7oo2RNTSvddSOs%2FMNgtbUisVF" rel="nofollow">https://github.com/phodal/ideabook</a></p>
<h2>Growth 2.0</h2>
<p>事情还没有结束,Growth 2.0将推出更棒的解决方案。</p>
<p>GitHub: <a href="https://link.segmentfault.com/?enc=grq4gINoqVN%2F%2BezyMQaX%2Fw%3D%3D.ZFRBpm%2BjwCfwTqgiEqLpt2ofOY42Uy%2FRdqNfRDKbji0G5jN0VMm4WgqG6rXIhkJj" rel="nofollow">https://github.com/phodal/growth2</a></p>
<p>敬请期待!</p>
如何成为全栈增长工程师?
https://segmentfault.com/a/1190000005173922
2016-05-21T23:38:59+08:00
2016-05-21T23:38:59+08:00
phodal
https://segmentfault.com/u/phodal
5
<p>(文末有惊喜)</p>
<p>记得我们在《<a href="https://link.segmentfault.com/?enc=MnrCWWRAvn9T2UNJBSSPeg%3D%3D.LEY7K9KOdKVstIYp%2FHK49xCLTo%2BZIXvs9vwYOod1GNLdSeiNWjSHqJZZWRmud%2BLntbxFxtXhLXwEHU3UJOekTFJpw8X3s%2Fb%2FrhqariJlw7KAf411ecBrTL5oZTuPNe8TpCo%2BZUofQ6TcnWEw53iKmTa8ANvWUxdiiIWuVL6PK7prXVcj%2BuTZv4wF8zcOY3coIQNWexpUyNvw85NF1DOO1yzW%2FIXoJGLjf82gk8hIqEjlkXLGsNjoWyP7bYnvHE1TqLhd0ii3CuliLU4fNejxwzh1GyNJr%2FItFYxukMGa%2BRi89lHajnLps1GQBbVeiV8y" rel="nofollow">RePractise前端篇: 前端演进史</a>》中提到技术在最近十几年的飞速发展,当然最主要的就是:技术的复杂度不断地从应用层抽象到了框架层。虽说:</p>
<blockquote><p>技术的复杂度同力一样不会消失,也不会凭空产生,它总是从一个物体转移到另一个物体或一种形式转为另一种形式。</p></blockquote>
<p>然而这也意味着成为一个全栈工程师,比以往的任何一个时间要容易得多。这也意味着一个全栈工程师也可以很快地成为一个Growth Hacking(中文:增长黑客)。所以,我们开始谈论如何成为一名<code>全栈增长工程师</code>。</p>
<h2>先成为全栈工程师</h2>
<p>在电子书《<a href="https://link.segmentfault.com/?enc=1ig3KXsCkwlCt5rJBC5Rxg%3D%3D.ltzdaaFsQNYh11CNeGqtuFYMLFaQ2AiG8CnDfRGBh3354ueoCzPyOfJTRfWWzCJkMXsmwvTM%2Bzn6tzqTnyTM4TUWsZ6l7s4J9PcVS8GrAOOapYaa0xL9BUmm5nCTeNQ82I9LvmAbaAC2%2F2akJGQXjuu6tCa4w6SmQkflEuPCpYxZIc%2BLZ2Q%2FGvxjIIEr3hx35gKSfS1MjRh7gR%2F9pkhXLn6aLYhXtWMhNsGQg9Ok2hStt6EPBV%2FF4R3WSi%2FzKML%2FMoZ99IlucvHrNdT6190%2BlKnSpd808Lao0xraEjp7bEibRF5SaxRxq1LGzUQpRKtT" rel="nofollow">全栈增长工程师指南</a>》中,我们提到过成为全栈增长工程师的技术基础,但是没有并没有谈论到如何成为这样的全栈工程师——这是一个漫长的过程。</p>
<p>早期,当我们有一个想法的时候,我们会去搭建一个网站——如以WordPress作为CMS,以RoR、Django来开发应用等等。随后,我们将我们的网站推向市场,发现市场有点反应。</p>
<p>接着,我们不断地开发出一些新的功能——如CMS的留言、Sitemap等等。在这个过程中,我们会开发一些API来满足我们的需求。</p>
<p>在一个新的阶段里,我们开始推出移动应用。基于先前的API,我们不断地构建出了不同的API。或以单体应用的形式出现,或以微服务的形式产生出新的API。</p>
<p>然后,我们发现并不是所有的移动用户都愿意去下载我们的API。于是,我们推出了SPA(单页面应用),以此来迎接那些移动设备用户。</p>
<p>最后,我们的业务逐渐稳定了下来。我们开始了一些优化工作,或者如Facebook一样优化PHP,推出HHVM。或者如Netflix一样使用微服务解耦系统。又或者,我们使用新的架构对我们的系统进行重新的设计。</p>
<p>在整个过程中,我们将学习到如何去做网站后台、移动应用、API设计、前端单页面应用等等。从这种意义上来说,全栈工程师非常match初创企业所需要的技术要求。</p>
<h2>再成为增长工程师</h2>
<p>Growth整一个系列:APP、社区、电子书《全栈增长工程师指南》、电子书《全栈增长工程师实战》算是我对Growth Hacking的一个研究。不过,对于一个人来说这工作量还是蛮大的——在完成两本电子书后,我们将继续研究。在这一个过程中,我发现一些很有意思的东西——只有开发出用户想要的东西,这个过程才容易实践起来的。</p>
<p>增长可以分为两部分:一个是自身的增长,一个是用户的增长。两者实际上是一种相互促进的关系,当我们的能力增长到一定的程度,我们才能推进用户的增长。相用户增长到一定的程度,也会推进我们的技能增长。</p>
<p>只是要在技术、数据分析、用户分析、创新等等有所突破,看上去好像不是一件容易的事。只是对于大部分的全栈工程师来说,实现技术、数据抓取和分析是一件容易的事。要实现对数据的敏感是一种很难的事,但是可视化过后的数据就一样了。对于用户的行为分析也是类似的,只是因为我们缺乏一些有效的练习。</p>
<p>更让人惊讶的是创新也是可以练习的,每次我们遇到一个问题的时候,就是我们离创新最近的时候——难道不是吗?当你遇到一个难解的问题,就是你开拓一个新的能力的时候。</p>
<p>好好享受这个学习的过程吧!</p>
<h2>全栈增长工程师实战</h2>
<p>终于来到了我们的主题了——我们很高兴宣布《全栈增长工程师实战》已经可以阅读了,地址:<a href="https://link.segmentfault.com/?enc=A0wyzTJBYK8mhb49HXHbPQ%3D%3D.l8Npn1APW1ZPmZ734bVC9By4VzV7PzXMIZKuMh2FbdRwcyZPR3yU9sX6eCkdvnsv" rel="nofollow">http://growth-in-action.phodal.com</a></p>
<p>你将会看到:</p>
<ul>
<li><p>如何去开发一个Web应用(博客)</p></li>
<li><p>如何编写单元测试、功能测试、自动化UI测试</p></li>
<li><p>搭建并使用持续集成</p></li>
<li><p>添加SEO支持——Sitemap、站长工具和Google Analytics</p></li>
<li><p>使用API,制作AutoComplete</p></li>
<li><p>开发相应的APP及其API——查看文章、用户登录、发表文章</p></li>
<li><p>制作单页面应用</p></li>
<li><p>可配置管理</p></li>
</ul>
<p>在这本电子书里,我们将使用Django + Bootstrap,完成我们的桌面版:</p>
<p><img src="/img/remote/1460000006807358" alt="desktop.png" title="desktop.png"></p>
<p>以及移动版:</p>
<p><img src="/img/remote/1460000005173925" alt="mobile.png" title="mobile.png"></p>
<p>不仅仅如此,我们还提供前后端分享的实践——基于Riot.js的单页面移动版:</p>
<p><img src="/img/remote/1460000005173931" alt="mobile-web.png" title="mobile-web.png"></p>
<p>同时,我们还用基于混合应用框架Ionic提供了Android版</p>
<p><img src="/img/remote/1460000005173927" alt="android.png" title="android.png"></p>
<p>还有iOS版,当然也有WP版——只是当前我没有Windows 10的机器:</p>
<p><img src="/img/remote/1460000005173929" alt="ios.png" title="ios.png"></p>
<h2>ENJOY CREATE & SHARE</h2>
<p>地址:<a href="https://link.segmentfault.com/?enc=BbsUXB%2FsOo90AOMQbH6vdw%3D%3D.2fDD%2F%2FFa01W2DSr7cblTvll3OT2wnlwZwpyryTK1d7e8YFXrebSvDOFjXeQ5yawp" rel="nofollow">https://github.com/phodal/growth-in-action</a></p>
技术文章写作及运营的技巧:分析篇 | 什么样的文章受欢迎?
https://segmentfault.com/a/1190000004954988
2016-04-17T14:17:36+08:00
2016-04-17T14:17:36+08:00
phodal
https://segmentfault.com/u/phodal
4
<p>对于以技术为核心的技术博客来说,人们是冲着他们需要的内容去的,绝大多数情况下都不是在闲逛。如果你的网站里没有他想要的东西的话,他便会离开,人们是出于目的去搜索,基于动机,而不是无聊的在闲逛。无聊的话,他们更多的会去刷刷朋友圈的,看看鸡汤。</p>
<h2>文章的类型</h2>
<p>依据我的经验,我将技术博客分为下面几种类型:</p>
<table>
<thead><tr>
<th>文章类型</th>
<th>频率</th>
<th>时间</th>
</tr></thead>
<tbody>
<tr>
<td>技术细节型</td>
<td>几次/天</td>
<td>15~30分钟</td>
</tr>
<tr>
<td>干货型</td>
<td>几次/月</td>
<td>1~2小时</td>
</tr>
<tr>
<td>实践总结型</td>
<td>几次/ 周</td>
<td>2+小时</td>
</tr>
<tr>
<td>杂谈与鸡汤型</td>
<td>几次/季度</td>
<td>1 + 小时 ~ 几个月</td>
</tr>
</tbody>
</table>
<p>并且他们的流量来源大概来源于下图所示:</p>
<p><img src="/img/remote/1460000006831932" alt="traffic-source.png" title="traffic-source.png"></p>
<p>分析如下:</p>
<ul>
<li><p><strong>前三者可以带来很多的流量,后者可以带来大量的评论啦。</strong></p></li>
<li><p><strong>前三者可以让你在Google上有一个好位置,后者可以让你在用户心中有个好位置。</strong></p></li>
<li><p><strong>社交媒体可以让第四种类型的文章,有大量的评论和转载。</strong></p></li>
<li><p><strong>干货型和实践总结型的文章通过聚合网站来传播的效果最好。</strong></p></li>
<li><p><strong>前三种类型的用户获取到他们想要的信息就走了,不会留下评论</strong>。</p></li>
<li><p><strong>第四种类型,没有大V的光环,偶尔文章被转企业V转转也能多个几十个粉丝。所以我没有第四种类型的文章太多的经验哈。</strong></p></li>
</ul>
<p>不过,下面我们要依据不同的流量来源来分析文章的用户群体。</p>
<h3>搜索引擎:完成工作</h3>
<p>使用搜索引擎来找内容的用户一般都是为了<code>完成工作</code>,这一类的流量主要是以技术细节型的文章为主。先让我们简单地了解一下,什么是技术细节型的文章——就是看一些标题啦:</p>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=bgmqU6R0x6un22OGqb19vg%3D%3D.z8%2B2wEMe5%2FFEmGhwgVDtLfKmzFiHJ23Km9fRFkCuM2ApQ7JVlyc6ZW2PFPb3pvzh6zgYSDGK8Gy5eq0I4AiKtwprM%2BkEEfMzUxLrzdRHnrk%3D" rel="nofollow">Django ElasticSearch Ionic 打造GIS 移动应用</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=UDVOFEgYzlrPyBRpuHU%2FCg%3D%3D.5EVg1iHFAGfFmfZK12ZQFW0M7nlVmgCnD%2BG8XuAwFLFicmjbw8sU%2Bt2va4LeUIYEKlAQ4xNjP5Hu2pVHvK%2F403rL223Lh69WQMcblgN6Iis%3D" rel="nofollow">Django Haystack ElasticSearch 环境准备</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=lUNvEcntoaWQ41Dz8MIz7w%3D%3D.WXb3K9VIFzGi1qDLUURoGmgKkg5jrS5ihSVy5ChFIKg1c3ZL6O0jHKo3hS%2F4d2J5qIK6%2FZH1FxSwYwNqjmLd9EucWJQnIP871YG9UnQHGJYTFWC4Os%2FgaKOPVyVJsTwA" rel="nofollow">Ionic ElasticSearch 搜索服务</a></p></li>
</ul>
<p>这一些文章展现了特别多的技术细节。对于不懂这个领域的人来说,他们完成没有兴趣。因此就算你把这一类的文章推送到各个平台上,也不会有效果的。所以写好内容,然后取个好标题,然后将这件事交给搜索引擎就可以了。他们想要的只是获取他们想要的内容,而我们所要做的就是给他们想要的内容。因为:</p>
<blockquote><p>用户不想在你的网站花费太多的时间,他们需要有足够的时间去做别的事情</p></blockquote>
<p>我们只想解决我们当前的问题,完成后我们就可以去刷朋友圈了。</p>
<h4>用户行为分析</h4>
<p>下图是我博客的流量来源分析:</p>
<p><img src="/img/remote/1460000004954999" alt="user-behavior.jpg" title="user-behavior.jpg"></p>
<p>我们可以看到在第二栏“起始页”里,绝大部分的用户(13万/15万)都离开了——即用户找到了他们想要的内容,随后就离开了,这对于其他类型的文章也是一样的。不过,值得注意的是大概会有2万的用户会继续访问其他页面。</p>
<p>主要的用户来源都来自Google,这时就依赖于我们在Google中有一个好的排名。上神图:</p>
<p><img src="/img/remote/1460000004954992" alt="google.png" title="google.png"></p>
<p>依据Mediative在2014年的研究(<a href="https://link.segmentfault.com/?enc=3hF%2BqAmycLBO%2BnJge43P8g%3D%3D.pPx9jX1sWrsW3TLA%2FL3z7HWpuD%2BLL8gDA3tarMVvyIdRm86Od91agAYWzWnKnnotEHMYnyQAaZAP7d5amR3OLXHIEeauemyyTUYKALYVmzewav1T09DgGB9EKr1zNs4EpQJSaby0WdHH%2FTclbWUxjw%3D%3D" rel="nofollow">The Evolution of Google’s Search Results Pages & Effects on User Behaviour</a>)表明,新的眼动研究如下图所示:</p>
<p><img src="/img/remote/1460000004955008" alt="new-google-user-traffic.jpg" title="new-google-user-traffic.jpg"></p>
<p>这只是在说明Google的语义化搜索,已经初显成效。值得注意的是:2005年时,前4位的链接点击比例是47.8%,而在2014年这次研究的结果是62.6%。</p>
<p>作为一个专业的程序员,我们会用问题的关键词去搜索,如<code>InsecurePlatformWarning: A true SSLContext object is not available.</code> 。 如果这时你的标题就是《<a href="https://link.segmentfault.com/?enc=Hhph3KiLHgKPuQMmu8GxHw%3D%3D.upwKohJei%2BXziYhB4vXcq8q77b7a0xiUc%2FGTDIiux15hS7xaR%2FaNxOujkW9neN%2BqVYkRaBWaj4j4R1Ar9r3nsg%3D%3D" rel="nofollow">Python 解决 InsecurePlatformWarning: A true SSLContext object is not available</a>》,那么你就找到了你的用户了。</p>
<p><strong>这是我之前用了5分钟不到的时间写的一篇文章,它在半年的时间里带来了6,599个访问量</strong>。它在Google的搜索结果中排第一:</p>
<p><img src="/img/remote/1460000004955006" alt="google-search-python-issue.jpg" title="google-search-python-issue.jpg"></p>
<p>(PS: 虽然很不情愿地被排名第二的文章抄袭了——但是我还是第一,这该死的<strong>伪原创</strong>。)</p>
<p>对于大部分用户来说,一看到这个标题就深深觉得这会解决自己的问题,并且它还是中文的。同时,随着用户点击量的升高这篇文章的排名就会越来越高——这就是Google。</p>
<p>用户试了一下上面的解决问题的思路,发现可以工作,于是就走了。当用户发现按上面的步骤来不行,大部分用户就直接离开了。除非这是唯一的解决方案,那么用户可能会抱一点点希望的留个言。</p>
<h3>聚合网站</h3>
<p>对于现今的大部分技术类聚合网站来说,他们的特点都是差不多的——即由用户来提交技术文章的链接,再由用户来对文章进行“顶”和“踩”来决定其在首页的位置。这一类比较流行的有:</p>
<ul>
<li><p>CSDN的极客头条 (http://geek.csdn.net)</p></li>
<li><p>开发者头条 (http://toutiao.io/)</p></li>
<li><p>稀土掘金 (http://gold.xitu.io/)</p></li>
</ul>
<p>他们的特点都和上面是一样的,唯一有所区别的是<code>开发者头条</code>是由编辑来选出可以上头条的,随后也会进入用户顶和踩的通道。</p>
<p>这里是我们能统计到流量的网站的数据:</p>
<p><img src="/img/remote/1460000004955010" alt="csdn-geek.png" title="csdn-geek.png"></p>
<p>上图是文章发布在CSDN极客头条上的,可以看到阅读数有5k+。</p>
<p><img src="/img/remote/1460000004955012" alt="gold-xitu.jpg" title="gold-xitu.jpg"></p>
<p>而在稀土掘金上也有1k+的阅读量,而如果是一些更好的技术文章,那么它就会有更高的阅读量。在这些网站上比较受欢迎的文章类型就是:</p>
<ul>
<li><p>干货型</p></li>
<li><p>实践总结型</p></li>
<li><p>杂谈与鸡汤型</p></li>
</ul>
<p>除了好的内容之外,他也应该是普通用户能读懂的读物,如:</p>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=M5%2BjyRQpJ0nHtHjF3qeang%3D%3D.tTHe6eDKxBsyKtpvPn9%2BGfAPNP23FVc8Tg5pUaN2nMR578lq3Ug7VvuxM9OY41b%2B" rel="nofollow">程序员如何提高影响力 2.0</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=riqIoQ9sDg76Ax8OaMC1Pg%3D%3D.vApqfZf3UyONpfl42KJRH%2F3Bx8MDv7Wkl6fTk2IZKcb24dDI7lsh%2Bno2rXeVkMqF" rel="nofollow">Developer进阶书单</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=kN8Znb9nr6s1nHrIzrYy7A%3D%3D.f0XVzURBWgoNLaAnh2q%2BH%2B%2BZjjCWp4Q3SWedEwCL3wjmG6iOiC59wQQthQK12u4Buy01frgRyDZgeD4WirswX%2BZLjdSZHcmnGI%2Bd%2FcuW%2B%2FI%3D" rel="nofollow">我的编程生涯里启发我的15本书</a></p></li>
</ul>
<p>对于这部分的内容来说,我们只需要关注一点:<strong>人们只关注和自己相关的,并且也是可以达到的</strong>。</p>
<p>除此,还要有一个好的标题:</p>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=UZsVUFdJfD9dv4VVFjNx3Q%3D%3D.4Iqej%2B%2BSMC%2B2v9rfQACvL2QGmLZQkNRHRw3zAIee9l2tNowaLqI93Fhe%2FW7LzWDg" rel="nofollow">最流行的编程语言JavaScript能做什么?</a> 这只是一篇介绍JavaScript在不同领域的应用。</p></li>
<li><p><a href="https://link.segmentfault.com/?enc=LqQbgb%2Fzo4VDWV3D4I0W0Q%3D%3D.W6rrSgVZrS4s6EUdGM2hnzhME8N0eUHiG63%2BLSmZRULXbymPL6Kg0gcEafEV57jcUZSBigFY0shcma2j1BFLFQ%3D%3D" rel="nofollow">听我说说我的博客: 月访问量过万的个人IT博客的技术史</a> 这只用来介绍我博客的技术发展史。</p></li>
</ul>
<p>这些文章和总结只要标题好、内容实在,都很容易在聚合网站上有一个好的排名。并且越容易获得比较高的关注,即“马太效应”。</p>
<p>实际上,如果你是在一个恰当的时间发布了你的文章,那么你也会获得一个高的关注。这是个秘密,就不要问我为什么了。</p>
<h3>社交与新媒体</h3>
<p>这一类主要于<strong>传播性</strong>为主,主要适合那些“杂谈与鸡汤型”和不需要代码的“干货型”。因为我不是一个大V啦,我只能介绍一些有限的经验。主要以下面的几个为主:</p>
<ul>
<li><p>微博</p></li>
<li><p>微信公众号</p></li>
<li><p>知乎</p></li>
<li><p>论坛</p></li>
<li><p>媒体平台,如今日头条的头条号、企鹅媒体平台等等</p></li>
</ul>
<p>需要注意的一点是,微信公众号是封闭的,即如果我们的粉丝量少,那么我们要推广我们的文章难度非常大。这一点同理于微博,但是我们可以在微博上@大V,如果内容好,那么大V自然也会帮你转。而知乎、论坛则是开放型的,而这主要是取决于在这个平台上用户的质量比较高。媒体平台则是另外一种形式,主要是由于笔者尝试了几次,发现在这些平台上也是可行的,除了读者的质量比较不高。</p>
<p>对比如下:</p>
<table>
<thead><tr>
<th>类型</th>
<th>开放度</th>
<th>传播难度</th>
<th>粉丝质量</th>
<th>保护原创</th>
<th>上手难度</th>
<th>受众</th>
<th>持续性</th>
</tr></thead>
<tbody>
<tr>
<td>微博</td>
<td>中等</td>
<td>一般</td>
<td>中等</td>
<td>特别差</td>
<td>容易</td>
<td>依赖于粉丝数</td>
<td>一天~几天</td>
</tr>
<tr>
<td>微信公众号</td>
<td>封闭</td>
<td>难</td>
<td>高</td>
<td>高</td>
<td>难</td>
<td>依赖于粉丝数</td>
<td>几天</td>
</tr>
<tr>
<td>知乎</td>
<td>高</td>
<td>容易</td>
<td>高</td>
<td>中等</td>
<td>容易</td>
<td>关注话题的人</td>
<td>几天~几个月</td>
</tr>
<tr>
<td>论坛</td>
<td>中等</td>
<td>容易</td>
<td>中等</td>
<td>一般</td>
<td>容易</td>
<td>论坛中的用户</td>
<td>几天</td>
</tr>
<tr>
<td>媒体平台</td>
<td>高</td>
<td>容易</td>
<td>一般</td>
<td>高</td>
<td>难</td>
<td>几乎所有相关的用户</td>
<td>一天~几天</td>
</tr>
</tbody>
</table>
<p>下面就让我们展开上面的内容吧!</p>
<h4>微博</h4>
<p>技术文章要在微博上传播开来的难度还是挺大的,除非你是大V。但是如果是一些好的内容也可以传播开来,主要看大V的扩散了——大V就是你的文章的引爆点。如下图是我之前写的 《<a href="https://link.segmentfault.com/?enc=%2BBjJQFiq2kTOaujyphYHVw%3D%3D.r7xMK2GtqAygmoBk5WR%2BIGJHT8o8uhM2A1EPIMnr4fZPK3r7dYPvRcrytonkqBBh" rel="nofollow">全栈增长工程师指南</a>》,有幸得图灵教育和慕课网的扩散,因此有了195个转发。</p>
<p><img src="/img/remote/1460000004955017" alt="growth-weibo.jpg" title="growth-weibo.jpg"></p>
<p>然并卵,由于没有分析工具,就没有办法帮分析了。值得注意的是,文章在微博上的传播时间比较短。换句话说,在明天没有人转发你的微博了。它唯一的主要作用是,帮你提高粉丝数。</p>
<h4>微信公众号</h4>
<p>微信公众号就是一个门槛更高的活动了,坑比较深。如果好友不多、粉丝不高、自身知识度不高,传播起来难度就更大了。由于我的粉丝数量还不是非常多,我只能从中挑选中阅读量最高的一篇文章。这篇《<a href="https://link.segmentfault.com/?enc=tPUNTn%2F5PHpDZCdoXjfVrg%3D%3D.t0suSXRMAU9KeF0Ncsaz63vsLdUWYabCMKFTajex7GzdqxECtVWr1f3Criw%2FkVmcFoZE7HTj7HjzK75bN3bReoE6npPOK1F0icj78oBivxB8efmPee98mDOdrrm5iQxk0iEAsMZSv59KZyDqlAebeJmm6UkVZR2mW4seAUI4SOU%3D" rel="nofollow">为什么说全栈工程师是未来?</a>》一共有2897个阅读,244个转发。</p>
<p><img src="/img/remote/1460000004955015" alt="growth-wp.jpg" title="growth-wp.jpg"></p>
<p>所以,你也猜到了主要是靠朋友圈转发。而这有一个明显的限制是,假设1000个粉丝里只有20个转发,那么这篇文章的每天阅读人数就会是就会呈现递减趋势。通常这也是大部分文章的阅读量模型,除非中间有大V转发。</p>
<h4>知乎</h4>
<p>知乎也是我最近尝试去玩的平台,总得来说上手是最容易的——只要你的内容好,那么完全没有难度。如下图是《<a href="https://link.segmentfault.com/?enc=LoNv0hVyvNjBvWRefFisZA%3D%3D.RWx9kcZWaCC3kPlw5u%2Fc9YhFkMd%2BV15NRCMV81fY7yxlliZsgKSJCHtfjCep9jAw" rel="nofollow">全栈增长工程师指南</a>》的流量来源,会发现知乎占了很大的一部分。这种软植入的广告,在大家看来还是可以接受的。</p>
<p><img src="/img/remote/1460000004955019" alt="zhihu.jpg" title="zhihu.jpg"></p>
<p>然,如果我们尝试在上面推广微信公众号的话,可能会遇到一些挫折。不过可以放上微信上的文章的链接。除此还有一个小技巧便是,把你写过的文章贴在相关的答案里加上原文链接即可。</p>
<p>这里还需要注意的一点是,如果一个问题的关注人数比较少,那么可能以后关注的人数也不会多。所以如果你还只是一个小V的话,不烦和我一样将目标放在那些关注人数稍微多一点的问题上。在自己的关注人数上升后,再转到这些问题上,要不会吃力不讨好。</p>
<h4>媒体平台</h4>
<p>今日头条的头条号是让我最吃惊的一个平台——随便一篇文章都可以有上千、上万的阅读量。如下图就是我最近发的一篇文章:</p>
<p><img src="/img/remote/1460000004955021" alt="mp-toutiao.jpg" title="mp-toutiao.jpg"></p>
<p>开始在这个平台上使用的原因是,自己发的文章经常到转载到上面,并且被修改得面目全非。同样的,我在开始使用微信公众号时也是如此。随后,我发现在上面可以有极大的阅读量,这下我就放心了。让更多的人可以接触我的文章这就足够了~~。</p>
<p>今日头条采用的是推荐机制,即你只要看过某篇技术文章,那么你还会类似的技术文章。因此我们的文章就很容易被推荐了,那么阅读量就上去了。唯一的缺点是,由于面向的读者是大众,很容易遇到很多喷子。</p>
<h4>简书</h4>
<p>简书也是一个很不错的写作平台,只是如果我们的文章上不了首页推荐的话,那么我们也不难获得很高的阅读。当你上了首页的推荐后,就会有下面的阅读量和喜欢。受限于技术文章的阅读群体比较小,也不会特别广泛。</p>
<p><img src="/img/remote/1460000004955023" alt="jianshu.jpg" title="jianshu.jpg"></p>
<p>不过,如果你写的是什么值得推荐的APP那就还好,一般人都看得懂。并且简书上是打不了广告的,我们只依靠于在文中放点链接,哈哈~~。</p>
<p>这也恰恰是它为什么是一个非常好的写作平台的原因。</p>
<h3>小结: 大连接</h3>
<p>最后,让我们一张图结束这一篇的内容吧!你能想到什么??</p>
<p><img src="/img/remote/1460000004955025" alt="longtail.png" title="longtail.png"></p>
<p><strong>如果我们有一篇文章很火爆,那么它应该也要带动其他文章的增长。</strong></p>
<p>精彩内容期待下一篇《创作篇:写作的要点》</p>
<p>欢迎关注我的微信公众号Phodal</p>
<p><img src="/img/remote/1460000006831933" alt="QRCODE" title="QRCODE"></p>
我的编程生涯里启发我的15本书
https://segmentfault.com/a/1190000004702687
2016-03-28T12:34:56+08:00
2016-03-28T12:34:56+08:00
phodal
https://segmentfault.com/u/phodal
4
<p>从几百本书中整理出一份书单是一件困难的事,但是从这些书中挑选出对自己影响比较大的书确是一件容易的事。</p>
<p>在是一份迟来的书单,但是并不是一份适用于每个人的书单。这是我在学习编程过程中看的一些书,启发到我的书,有很多你可能没有听过,也有很多可能是你耳熟能详的。之所以说是启发是因为很多说并没有那么好,但是我从上面获取到了一些灵感。</p>
<h2>高中时期</h2>
<p>高中时期,因为想开发游戏的热情才深入计算机世界。并且高中也是一个相当无聊的时期,除了为高考准备的考试,还有就是上课</p>
<h3>《C++游戏开发》</h3>
<p>有一些书,你就没有必要去看了,比如这里的第一本书《C++游戏开发》,这本书是我在高中的时候翻了好几遍的书</p>
<p><img src="/img/bVtTwz" alt="C++游戏开发" title="C++游戏开发"></p>
<p>这本书一直在说“C++是一门优雅的语言”,也介绍了很多我在之前编程中不懂的知识点:数组、指针,以及高级点的重载和继承这一些面向对象的知识。这本书在游戏开发方面讲得不是很多,但是当时让我脑洞大开的是——2.5D编程。简单地来说,就是二张图片叠加在一起形成的3D效果。我才意识到当然我玩的很多游戏是这样做出来的。</p>
<p>我那之前我只会写点代码,并不知道一个游戏是如何在操作系统上运行的。在这本书中我学会了代码是如何通过操作系统的API来创建窗口、操作文件、操作声音等等。</p>
<p>其实上面说到的游戏开发的书应该是一整个系列,但是很多书启发性似乎并不大就不提了。记得还有《Java5 游戏编程》、《网络游戏开发》、《游戏开发核心技术•剧本与角色创造》等等的书。</p>
<h3>《3D游戏》</h3>
<p>这是一本讲述3D游戏编程的书,实际上说的是如何去造一个游戏引擎。上图:</p>
<p><img src="/img/bVtTxf" alt="图片描述" title="图片描述"></p>
<p>当然很多内容都看得不是很懂,这本书对我的帮助并不是技术本身,而是对于数学的提高和3D空间的理解。高中的知识本身并不多,多的是知识本身衍生出来的试题。换句话说,这样的书和上一本书一样,在我们遇到瓶颈的时候是很有帮助的,相当于帮我们打开了另外一片天空。原本我们以为世界是这样的,后来我们意识到了世界并不是这样的,世界比我们想象中的要大。</p>
<h3>《Linux内核设计与实现》</h3>
<p>这本书并不是同上两本是一本启发性的书籍,但是是一本奠定基础的书籍。</p>
<p><img src="/img/bVtTxi" alt="linux-kernel.jpg" title="linux-kernel.jpg"></p>
<p>出于某种原因,加之发现一个人很难开发3D游戏。便开始学习游戏底层之下的技术——操作系统。于是很快地就从一个游戏开发爱好者变成了一个Kernel Hacker。很多东西都已经忘了,但是很多东西都记得很清楚。如Linux是一个宏内核,但是借助了微内核的很多思想。在当时的环境下,所谓的多进程是怎么一回事,他们多久切换一次进程等等。</p>
<p>除此,不得不提及的一本书是《Orange'S:一个操作系统的实现》,这本书在当时的映像是非常不错的。但是这本书放在我的家里吃灰尘了~~。</p>
<h3>《代码之美》</h3>
<p>这本书开启了我的装B史~~,上图</p>
<p><img src="/img/bVtTxl" alt="代码之美" title="代码之美"></p>
<p>由于这是一本合集,所以书中的大部分内容我没有看懂。但是,这本书让我看到了各种很Diao的代码。不过这并不重要,重要的是我在这本书里面认识了Emacs。这就是为什么这本书会上榜的原因。</p>
<p><img src="/img/bVtTxp" alt="图片描述" title="图片描述"></p>
<p>这本书中有多篇提到了Emacs,然后我就去试了!!!<br>这本书中有多篇提到了Emacs,然后我就去试了!!!<br>这本书中有多篇提到了Emacs,然后我就去试了!!!</p>
<h2>大学</h2>
<p>大学学的是硬件,所以在软件方面花费的经历并不是特别多,成长比较大的是大学最后的学习。</p>
<h3>《设计模式》</h3>
<p>之所以买这本书的原因是:很多书中都提到了这本书,强调这本书是多么的重要。然后,我就买了。</p>
<p><img src="/img/bVtTxq" alt="图片描述" title="图片描述"></p>
<p>当时写的语言基本上就是C、Python还有汇编,所以在当时并不特别懂,后面我们会继续说到。它给我的感觉就是,WOCAO,我当年的C++连入门都算不上。虽然能看懂,但是一知半解。主要是当时代码写得并不多,主要是在博览群书。</p>
<h3>《领域特定语言》</h3>
<p>DSL大法好,DSL大法真的是一个很棒的想法。</p>
<p><img src="/img/bVtTxw" alt="图片描述" title="图片描述"></p>
<p>它可以用更简洁的语言业表达你的想法。这是很美好的一个想法,如果我们可以把我们的业务代码抽象成外部DSL的话,那么我们就不担心架构的变更、技术框架的落后。</p>
<h3>《SEO艺术》</h3>
<p>当你有一篇好的文章,还有一个好的产品,你还需要什么?SEO就是其中一个。</p>
<p><img src="/img/bVtTxx" alt="图片描述" title="图片描述"></p>
<p>让我意识到一点:事物间的关联性——也就是Page Rank。当时对论文这种东西没啥概念,后来发现这是非常有趣的一个算法。</p>
<p><img src="/img/bVtTxy" alt="图片描述" title="图片描述"></p>
<p>这个原则不仅仅适用于SEO领域,还适用于社交领域、大V经济领域。PR高的网站链接PR高的网站可能没有啥明显的作用,但是对提交PR低的网站特别有用。</p>
<h3>《重来》</h3>
<p>《重来》似乎是一本经常被提及的书,好在我也是看过的,还写了几篇相关的文章。</p>
<p><img src="/img/bVtTxz" alt="图片描述" title="图片描述"></p>
<p>这本书有三点说得很不错,换句话来说,这三点和我产生了共鸣:</p>
<ol>
<li><p>卖掉代码的副产品。事物间存在的连接属性。</p></li>
<li><p>招聘笔杆子。这个就不用多说了,我写了那么多文章。</p></li>
<li><p>音乐应在你的指尖流淌。好的装备确实能带来一些帮助,但事实是,你的演奏水平是由你自己的手指决定的。</p></li>
</ol>
<h2>ThoughtWorks面试与实习</h2>
<p>大四的时候就开始找工作了,然后我就遇到了ThoughtWorks。面试的时候醒悟到了《重构》可以到《设计模式》,但是光醒悟并没有啥卵用,下面这本书是我后来的后来才遇到的。</p>
<h3>《重构与模式》</h3>
<p>不知道在多少的场合里,我一直在吐槽这本书应该是叫《重构到设计模式》。</p>
<p><img src="/img/bVtTxB" alt="图片描述" title="图片描述"></p>
<p>虽然并没有学到太多的实质性的东西——很多东西都在实习的时候已经学到了。重构和设计模式一样,光一天天做一些智力练习并没有啥用。从重构代码到抽象出设计模式,是一种很迷人的体验。</p>
<h3>《重构》</h3>
<p>这本书虽然看得很早,但是提得很晚的原因是:主角总是最后才出场的。</p>
<p><img src="/img/bVtTxE" alt="图片描述" title="图片描述"></p>
<p>在当时我也花了很多时间去识别书中的一些Code Smell,然后去重构。亲手把自己的代码从一坨x变得更易读是一种很棒的体验,你说呢?</p>
<h3>《敏捷软件开发》</h3>
<p>这又是一本Jolt效率大奖的书,这似乎也是进入我们公司应该读的一本书。</p>
<p><img src="/img/bVtTxJ" alt="图片描述" title="图片描述"></p>
<p>这本书结合了敏捷方法、模式和面向对象的一些思想,并提出了SOLID(单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)这五个基本原则。</p>
<h2>工作</h2>
<h3>《持续交付 发布可靠软件的系统方法》</h3>
<p>这倒不是一本对我启发非常大的书,主要是因为这已经是我们的日常工作流程。</p>
<p><img src="/img/bVtTxL" alt="![图片描述" title="![图片描述">][15]</p>
<p>如果你先实践了,然后再去看一本书,那么你就不会觉得一本是多么的棒。软件本身是一种持续的过程,特别是Web开发来说。在这一点上来看,几乎所有的互联网公司都可以持续交付软件。但是国内的大部分互联网公司的代码都是没有测试的,并且呈现的是一种几乎有问题的持续交付模式。</p>
<h3>《面向模式的软件架构 卷1:模式系统》</h3>
<p>作为另外一本Jolt效率大奖的书,这本书名副其实。</p>
<p><img src="/img/bVtTxM" alt="图片描述" title="图片描述"></p>
<p>能从混乱到有序就可以抽象成一种架构模式,书中向我们展示了各个层面的模式是如何发挥作用的。这本书让我意识到了模式不仅仅存在于代码之间,又存在于架构之间,更是可以存在于我们的日常生活之中。</p>
<h3>《实现领域驱动设计》</h3>
<p>尽管这本书提到的很多东西我都很了解,我还是不非常懂这本书。或许只是因为没有连接概念到代码上,让我在有空的时候再细细理解这本书。</p>
<p><img src="/img/bVtTxO" alt="图片描述" title="图片描述"></p>
<p>虽是如此,但是这本书中提到的六边形架构让我映象深刻,又可以称之为“端口和适配器架构”。</p>
<p><img src="/img/bVtTxP" alt="图片描述" title="图片描述"></p>
<p>还有CQRS(命令和查询责任分离)架构,这种风格和我们现有系统的架构是类似的。在我们系统的架构中,读和写是两个不同的数据源,我相信这种结构也存在于很多的系统中。</p>
<p><img src="/img/bVtTxK" alt="图片描述" title="图片描述"></p>
<p>还有我之前提到过的<a href="https://link.segmentfault.com/?enc=CD1EHwhWQTyL9Ji1kTnxEA%3D%3D.mhRmimd4YCZZbXU1MqInqNF%2FKz8FuzG4b3wsmYeVKTTcu1F%2Fo6gc6cVEWqpETSqZc6eBunM2rUVCSn9TohBZrw%3D%3D" rel="nofollow">编辑-发布-开发分离</a>也是类似于这种风格的架构。</p>
<p><img src="/img/bVtTx4" alt="图片描述" title="图片描述"></p>
<h3>《浮现式设计 专业软件开发的演进本质》</h3>
<p>最后出现的这本书,让我重新理解了软件开发的演进。</p>
<p><img src="/img/bVtTxR" alt="图片描述" title="图片描述"></p>
<p>浮现式设计这个概念很棒,软件不是一开始就产生的,面向Web开发的软件更是如此。由于业务发现的需要,我们不可能在一开始想到一切的可能性,我们只能在业务演进的同时发展我们的架构。</p>
<p><strong>更多精彩内容欢迎关注我的微信公众号: Phodal</strong></p>
<p><img src="/img/bVsHJ9" alt="图片描述" title="图片描述"></p>
为什么未来是全栈工程师的世界?
https://segmentfault.com/a/1190000004654179
2016-03-21T15:49:47+08:00
2016-03-21T15:49:47+08:00
phodal
https://segmentfault.com/u/phodal
4
<blockquote><p>谨以此文献给每一个为成为优秀全栈工程师奋斗的人。</p></blockquote>
<p>节选自《<a href="https://link.segmentfault.com/?enc=Xn5eD332yv90Na6geKrVxA%3D%3D.HuOk1UwbZmYYztNP2cpIQ%2FjnX%2BHUqOLTEbxugWRU1Vvc0oigEQyPTj9QRqw8CHC%2B" rel="nofollow">Growth: 全栈增长工程师指南</a>》</p>
<p>技术在过去的几十年里进步很快,也将在未来的几十年里发展得更快。今天技术的门槛下降得越来越快,原本需要一个团队做出来的Web应用,现在只需要一两个人就可以了。</p>
<p>同时,由于公司组织结构的变迁,以及到变化的适应度,也决定了赋予每个人的职责将会越来越多。尽管我们看到工厂化生产带来的优势,但是我们也看到了<strong>精益思想</strong>带来的变革。正是这种变革让越来越多的专家走向全栈,让组织内部有更好的交流。</p>
<p>你还将看到专家和全栈的两种不同的学习模式,以及全栈工程师的未来。</p>
<h3>技术的革新史</h3>
<p>从开始的CGI到MVC模式,再到前后端分离的架构模式,都在不断地降低技术的门槛。而这些门槛的降低,已经足以让一两个人来完成大部分的工作了。</p>
<h4>CGI</h4>
<p>二十年前的网站以静态的形式出现,这样的网站并不需要太多的人去维护、管理。接着,人们发明了CGI(通用网关接口,英语:Common Gateway Interface)来实现动态的网站。下图是一个早期网站的架构图:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/cgi-arch.gif" alt="CGI网站架构" title="CGI网站架构"></p>
<p>当时这种网站的URL类似于: <a href="https://link.segmentfault.com/?enc=ZNVUDRzAlwtYbijinU8W2w%3D%3D.v72hHXbj94Vg66XRsG%2FBBQAe2L6NHavIO4ruS%2BvNNEMtjL4wKCIpW27Cu%2BZrFX%2BT" rel="nofollow">https://www.phodal.com/cgi-bin/getblog</a></p>
<p>(PS:这个链接是为了讲解而存在的,并没有真实存在。)</p>
<p>用户访问上面的网页的时候就会访问,cgi-bin的路径下对应的getblog脚本。你可以用Shell返回这个网页:</p>
<pre><code>#!/bin/sh
echo Content-type: text/plain
echo hello,world
</code></pre>
<p>Blabla,各种代码混乱地夹杂在一起。不得不说一句:这样的代码在2012年,我也看了有一些。简单地来说,这个时代的代码结构就是这样的:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/cgi-script.png" alt="CGI脚本文件 " title="CGI脚本文件 "></p>
<p>这简直就是一场恶梦。不过,在今天好似那些PHP新手也是这样写代码的。</p>
<p>好了,这时候我们就可以讨论讨论MVC模式了。</p>
<h4>MVC架构</h4>
<p>我有理由相信Martin Fowler的《企业应用架构模式》在当时一定非常受欢迎。代码从上面的耦合状态变成了:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/caf_mvc_arch.png" alt="MVC架构" title="MVC架构"></p>
<p>相似大家也已经对这样的架构很熟悉了,我们就不多解释了。如果你还不是非常了解的话,可以看看这本书后面的部分。</p>
<h4>后台服务化与前端一致化架构</h4>
<p>在今天看来,我们可以看到如下图所示的架构:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/oneui-serviceful-arch.png" alt="后台服务化与前台一致化架构" title="后台服务化与前台一致化架构"></p>
<p>后台在不知不觉中已经被服务化了,即只提供API接口和服务。前端在这时已经尽量地和APP端在结合,使得他们可以保持一致。</p>
<h3>软件开发的核心难题:沟通</h3>
<p>软件开发在过去的几十年里都是大公司的专利,小公司根本没有足够的能力去做这样的事。在计算机发明后的几十年里,开发软件是大公司才能做得起的。一般的非技术公司无法定制自己的软件系统,只能去购买现有的软件。而随着技术成本的下降,到了今天一般的小公司也可以雇佣一两个人来做同样的事。这样的演进过程还真是有意思:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/develop-history.png" alt="开发演进" title="开发演进"></p>
<p>在这其中的每一个过程实质上都是为了解决沟通的问题。从瀑布到敏捷是为了解决组织内沟通的问题,从敏捷到精益不仅仅优化了组织内的沟通问题,还强化了与外部的关系。换句话说,精益结合了一部分的互联网思维。</p>
<h4>瀑布式</h4>
<p>在最开始的时候,我们预先设计好我们的功能,然后编码,在适当的时候发布我们的软件:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/old-se.jpg" alt="预先式设计的瀑布流" title="预先式设计的瀑布流"></p>
<p>然而这种开发方式很难应对市场的变化——当我们花费了几年的时间开发出了一个软件,而这个软件是几年前人们才需要的。同时,由于软件开发本身的复杂度的限制,复制的系统在后期需要大量的系统集成工作。这样的集成工作可能要花费上大量的时间——几星期、几个月。</p>
<p><img src="http://growth.phodal.com/chapters/prelude/waterfall-process.png" alt="瀑布流的沟通模型" title="瀑布流的沟通模型"></p>
<p>当人们意识到这个问题的时候,开始改进工作流程。出现了敏捷软件开发,这可以解释为什么产品经理会经常改需求。如果一个功能本身是没必要出现的话,那么为什么要花功夫去开发。但是如果一个功能在设计的初期就没有好好设计,那么改需求也是必然的。</p>
<h4>敏捷式</h4>
<p>现有的互联网公司的工作流程和敏捷软件开发在很多部分上是相似的,都有迭代、分析等等的过程:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/scrum.png" alt="敏捷软件开发" title="敏捷软件开发"></p>
<p>但是据我的所知:国内的多数互联网公司是不写测试的、没有Code Review等等。当然,这也不是一篇关于如何实践敏捷的文章。敏捷与瀑布式开发在很大的区别就是:沟通问题。传统的软件开发在调研完毕后就是分析、开发等等。而敏捷开发则会强调这个过程中的沟通问题:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/scrum-communication.png" alt="敏捷软件开发的沟通模型" title="敏捷软件开发的沟通模型"></p>
<p>在整个过程中都不断地强调沟通问题,然而这时还存在一个问题:组织结构本身的问题。这样的组织结构,如下图所示:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/scrum-issues.png" alt="组织结构" title="组织结构"></p>
<p>如果市场部门/产品经理没有与研发团队坐一起来分析问题,那么问题就多了。当一个需求在实现的过程中遇到问题,到底是哪个部门的问题?</p>
<p>同样的如果我们的研发部门是这样子的结构:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/tech-org.png" alt="研发部门" title="研发部门"></p>
<p>那么在研发、上线的过程中仍然会遇到各种的沟通问题。</p>
<p>现在,让我们回过头来看看大公司的专家与小公司的全栈。</p>
<h3>大公司的专家与小公司的全栈</h3>
<p>如果你经常看一些关于全栈和专家的技术文章的时候,你就会发现不同的人在强调不同的方向。大公司的文章喜欢强调成为某个领域的专家,小公司喜欢小而美的团队——全栈工程师。</p>
<p>如我们所见的:大公司和小公司都在解决不同类型的问题。大公司要解决性能问题,小公司都活下去需要依赖于近乎全能的人。并且,大公司和小公司都在加班。如果从这种意义上来说,我们可以发现其实大公司是在剥削劳动力。</p>
<p><strong>专家</strong></p>
<p>我们所见到的那些关于技术人员应该成为专家的文章,多数是已经成为某个技术领域里的专家写的文章。并且我们可以发现很有意思的一点是:他们都是<strong>管理者</strong>。管理者出于招聘的动机,因此更需要细分领域的专家来帮助他们解决问题。</p>
<p><strong>全栈</strong></p>
<p>相似的,我们所看到的那些关于成为全栈工程师的文章,多数是初创公司的CTO写的。而这些初创公司的CTO也多数是全栈工程师,他们需要招聘全栈工程师来帮助他们解决问题。</p>
<h4>两种不同的学习模型</h4>
<p>而不知你是否也注意到一点:专家们也在强调<strong>“一专多长”</strong>。因为单纯依靠于一个领域的技术而存在的专家已经很少了,技术专家们不得不依据于公司的需求去开拓不同的领域。毕竟“公司是指全部资本由股东出资构成,以营利为目的而依法设立的一种企业组织形式;”,管理人们假设技术本身是相通的,既然你在技术领域里有相当高的长板,那么进入一个新的技术也不是一件难的事。</p>
<p>作为一个技术人员,我们是这个领域中的某个子领域专家。而作为这样一个专家,我们要扩展向另外一个领域的学习也不是一件很难的事。借鉴于我们先前的学习经验,我们可以很快的掌握这个新子域的知识。如我们所见,我们可以很快地补齐图中的短板:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/bucket.jpg" alt="木桶" title="木桶"></p>
<p>在近来的探索中发现有一点非常有意思:如果依赖于20/80法则的话,那么成为专家和全栈的学习时间是相当的。在最开始的时候,我们要在我们的全栈工程和专家都在某个技术领域达到80分的水平。</p>
<p>那么专家,还需要80%的时间去深入这个技术领域。而全栈工程师,则可以依赖于这80%的时候去开拓四个新的领域:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/expert-vs-fullstack.png" alt="全栈与专家学习时间" title="全栈与专家学习时间"></p>
<p>尽管理论上是如此,但是专家存在跨领域的学习障碍——套用现有模式。而全栈也存在学习障碍——如何成为专家,但是懂得如何学习新的领域。</p>
<h4>解决问题的思路:不同的方式</h4>
<p>有意思的是——成为专家还是成为全栈,取决于人的天性,这也是两种不同的性格决定的。成为管理者还是技术人员看上去就像一种简单的划分,而在技术人员里成为专家还是全栈就是另外一种划分。这取决于人们对于一个问题的思考方式:这件事情是借由外部来解决,还是由内部解决。下面这张图刚好可以表达我的想法:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/in-out-thinking.jpeg" alt="内向与外向思维" title="内向与外向思维"></p>
<p>而这种思维依据于不同的事情可能会发生一些差异,但是总体上来说是相似的。当遇到一个需要创轮子的问题时,我们就会看到两种不同的方式。</p>
<p>对于全栈工程师来说,他们喜欢依赖于外部的思维,用于产生颠覆式思维。如Angular.js这样的框架便是例子,前端结合后端开发语言Java的思维而产生。而专家则依赖于内部的条件,创造出不一样的适应式创新。如之前流行的Backbone框架,适应当时的情况而产生。</p>
<h3>全栈工程师的未来:无栈</h3>
<p>全栈工程师本身不应该仅仅局限于前端和后台的开发,而可以尝试去开拓更广泛的领域——因为全栈本身是依赖于工程师本身的学习能力,正是这种优秀的学习能力可以让他们可以接触更广泛的知识。</p>
<h4>全栈的短板</h4>
<p>如果你也尝试过面试过全栈工程师,你会怎么去面试他们呢?把你知道的所有的不同领域的问题都拿出来问一遍。是的,这就是那些招聘全栈工程师的公司会问你的问题。</p>
<p>人们以为全栈工程师什么都会,这是一个明显的误区——然而要改变这个误区很难。最后,导致的结果是大家觉得全栈工程师的水平也就那样。换句来说,人们根本不知道什么是全栈工程师。在平时的工作里,你的队伍都知道你在不同领域有丰富的知识。而在那些不了解你的人的印象里,就是猜测你什么都会。</p>
<p>因此,这就会变成一个骂名,也是一个在目前看来很难改变的问题。在这方面只能尽可能地去了解一些通用的问题,并不能去了解所有的问题。在一次被面试全栈工程师的过程中,有一个面试官准备了几个不同语言(Javascript、Java、Python、Ruby)的问题来问我,我只想说Ciao——意大利语:你好!</p>
<p>除了这个问题——人们不了解什么是全栈工程师。还有一个问题,就是刚才我们说的成为专家的老大难问题。</p>
<h4>无栈</h4>
<p>让我毫不犹豫地选择当全栈工程师有两个原因:</p>
<ol>
<li><p>这个世界充满了未解的迷,但是我只想解开我感兴趣的部分。</p></li>
<li><p>没有探索,哪来的真爱?你都没有探索过世界,你就说这是你最喜欢的领域。</p></li>
</ol>
<p>当我第一次看到全栈工程师这个名字的时候,我发现我已然是一个全栈工程师。因为我的学习路线比较独特:</p>
<p>中小学:编程语言 -> 高中:操作系统、内核、游戏编程 -> 大学: 硬件、Web开发 -> 工作:后端 + 前端</p>
<p>而在当时我对SEO非常感兴趣,我发现这分析和Marketing似乎做得还可以。然后便往Growth Hacking发展了:</p>
<p><img src="http://growth.phodal.com/chapters/prelude/growth-hacking.jpg" alt="Growth Hacking" title="Growth Hacking"></p>
<p>而这就是全栈学习带来的优势,学过的东西多,学习能力就变强。学习能力往上提的同时,你就更容易进入一个新的领域。</p>
<p>参考书籍</p>
<ul>
<li><p>《精益企业: 高效能组织如何规模化创新》</p></li>
<li><p>《企业应用架构模式》</p></li>
<li><p>《敏捷软件开发》</p></li>
<li><p>《技术的本质》</p></li>
</ul>
<p>更多内容欢迎关注我的微信公众号(搜索Phodal):</p>
<p><img src="/img/bVs6fy" alt="图片描述" title="图片描述"></p>
我的编程成长四步曲
https://segmentfault.com/a/1190000004561933
2016-03-08T13:09:46+08:00
2016-03-08T13:09:46+08:00
phodal
https://segmentfault.com/u/phodal
3
<p>这次我决定不耍流氓的写一篇鸡汤,这篇是以过程到结果的文章——以前老是写结果,总感觉不好~~。</p>
<p>Blabla,群聊的时候,看到一个网站有一个<a href="https://gist.github.com/paulmillr/2657075/">Most active GitHub users</a>的排名,发现我在里面的位置是20——在我前面还有一个台湾的同学。虽然我现在已经超过他了,但是这个榜单没有更新。???</p>
<p><img src="https://www.phodal.com/static/media/uploads/github-active.png" alt="Most active GitHub users" title="Most active GitHub users"></p>
<p>而实际上成为最活跃的中国大陆用户,只是有意图的练习的结果。。。</p>
<p>在我找到大四找到工作后,默默制作了一个计划。这个计划大概分为三步,而每一步大概会花一年时间去实践:</p>
<ol>
<li><p>用更好的方法来实现功能</p></li>
<li><p>Better Code & Architecture</p></li>
<li><p>运营(Growth Hacker)</p></li>
</ol>
<p>所以,其实这也算这篇文章的三个小节。在你跟着这篇文章走之前,你需要具备一个根本的能力:<strong>能做到你想做的</strong>。</p>
<h2>能做到你想做的: Tasking与学习能力</h2>
<p>虽然这看上去是一件很难的事,但是也不是一件很难的事。这也不依赖于你是否对某一个技术栈的理解,这只实际上只是学习能力的一种体现。</p>
<p><img src="https://www.phodal.com/static/media/uploads/todo-example.png" alt="todo-example.png" title="todo-example.png"></p>
<p>在扯这些废话之前,先让我们来看看我们是如何实现一个功能、应用的。这也是在Growth里提到的Tasking:</p>
<ol>
<li><p>有一个明确的实现目标。</p></li>
<li><p>评估目标并将其拆解成任务(TODO)。</p></li>
<li><p>规划任务的步骤(TODO)</p></li>
<li><p>学习相关技能</p></li>
<li><p>执行Task,遇到难题就跳到第二步。</p></li>
</ol>
<p>以本文的写作为例,细分上面的过程就是:</p>
<ol>
<li><p>我有了一个中心思想——在某种意义上来说就是标题。</p></li>
<li><p>依据中心思考我将这篇文章分成了四小节。</p></li>
<li><p>然后我开始写四小节的内容。</p></li>
<li><p>直到完成。</p></li>
</ol>
<p>而如果将其划分到一个编程任务,那么也是一样的:</p>
<ol>
<li><p>我们想到做一个xxx的idea。</p></li>
<li><p>为了这个idea我们需要分成几步,或者几层设计。</p></li>
<li><p>对于每一步,我们应该做点什么</p></li>
<li><p>我们需要学习怎样的技能</p></li>
<li><p>集成每一步的代码,就有了我们的系统。</p></li>
</ol>
<p>所以,实际上这并不是考验你技术能力的点,而是考验你如何划分任务和学习能力的点。在我不断地实现一个个系统的时候,最后我才意识到了这一点——学习能力和Tasking才是最重要的。换句话说,你现在学会什么并不重要,重要的是你以后有没有学习的能力。</p>
<p>有了一直想着过安逸的生活,所以就会安乐的死去。不过反正人生在世,自己开心就行,不要管别人怎么说。随后,我发现大部分我想到的都可以实现。</p>
<p>那么问题来了:</p>
<ol>
<li><p>用铅笔写字也是写,用钢笔写字也是写,用电脑打字也是写——到底要用哪个工具来写。</p></li>
<li><p>用楷体来显示也是显示,用宋体显示也是显示——到底要怎样的实现。</p></li>
</ol>
<p>这实际上就是:用更好的方法来实现功能。</p>
<h2>用更好的方法来实现功能</h2>
<p>对于这一步来说,有太多的东西值得去探索:</p>
<ol>
<li><p>更好的架构</p></li>
<li><p>更好的工具</p></li>
<li><p>更好的语言</p></li>
<li><p>。。。</p></li>
</ol>
<p>对于语言这一点上来说,Python和Ruby是两个不同的极端,不过看上Ruby是继承Perl的思想的基础上,就不多说了。Python,一个问题只有一个方法;Ruby,一个问题有几个方法。因而在这两个语言来,想要快感就用Ruby,还有近年来流行的Scala。想要便捷就是Python,不容易出现别的问题。</p>
<p>而这些只是我得到的结论,每个人都会得到不同听结论的。因而,探索是一件很有意思的事~~,如图:</p>
<p><img src="https://www.phodal.com/static/media/uploads/civ5-worst.jpg" alt="civ5-worst.jpg" title="civ5-worst.jpg"></p>
<p>所以实际上,这一步是去探索一个更广阔的天空。我们需要知道不同的语言和技术栈的一些优势,了解并知道如何去应用即可。</p>
<p>如,以前我用的是Django的RESTful方案Tastypie管理来解决问题。后来,又发现了Django REST Framework,又用它实现了原来的逻辑。大四时,我发现PHP语言中的Laravel在RESTful这件事情上,似乎很有表现力,并且更容易“部署”——LNMP。接着实习时,发现Java在部署上更不错,它只需要一个Java包。而这个过程,只是出自于部署方面来考虑问题的。</p>
<p>即使现在我是一个使用Node.js的人,我还是觉得Java的打包就是优势。</p>
<p>在探索的过程中,我们就知道<strong>挖掘技术哪家强</strong>?</p>
<h2>Better Code & Architecture</h2>
<p>在实际编程的过程中,我们就会遇到很多代码问题。在这时,我们尽管有好的架构,然而并不能从根本上解决问题。只能保证从蓝图上看是对的,而不能导致质量问题。</p>
<p><img src="https://www.phodal.com/static/media/uploads/better-architecture.jpg" alt="better-architecture.jpg" title="better-architecture.jpg"></p>
<p>代码的腐烂都是源自于平时的工作习惯问题,而在很多时候是需求导致的。这些看上去就是噩梦——加班,没有时间学习,效率低。没有时间学习,效率低,加班。</p>
<p><img src="https://www.phodal.com/static/media/uploads/eff-worknight.png" alt="eff-worknight.png" title="eff-worknight.png"></p>
<p>而对于代码质量来说,也是如此的一种循环:</p>
<p><img src="https://www.phodal.com/static/media/uploads/code-time.png" alt="code-time.png" title="code-time.png"></p>
<p>而在这时可以学的东西可多了,如Bad Smell,重构,设计模式,编写测试等等。</p>
<p>最后,我只得到了一个短句:</p>
<blockquote><p>重构到设计模式</p></blockquote>
<p>好的代码是重构出来的。</p>
<p>再往深层次之上就是架构,而架构往往是一种不容易得到的。尽管我看了一系列的书:</p>
<ul>
<li><p>《领域驱动设计》</p></li>
<li><p>《企业应用架构模式》</p></li>
<li><p>《领域特定语言》</p></li>
<li><p>《恰如其份的软件架构》</p></li>
<li><p>《面向模式的软件架构》</p></li>
<li><p>。。。</p></li>
</ul>
<p>最后,我还是依据一本书的书名,才领悟了一本书的书名《浮现式设计》。也是同样的话:</p>
<blockquote><p>好的架构是演进出来的。</p></blockquote>
<p>而这些都依赖于我们的实践,听信别人说的话,并不能让我们学会什么。只有自己做了,才知道什么是好,什么是不好。</p>
<h2>Growth Hacking</h2>
<p>当我们有了一个好的Idea,一个好的架构以及一份良好的代码,并且我们还能将其执行下去。那么,我们还需要什么?</p>
<p><img src="https://www.phodal.com/static/media/uploads/what-is-growth-hacking.jpg" alt="what-is-growth-hacking.jpg" title="what-is-growth-hacking.jpg"></p>
<p>这也是我现在正在尝试的领域,也是过去我想做的,但是却找不到一个好的方向。后来,想了想就有了Growth这个APP。毕竟从一个领域到一个新的领域,最好是通过现有的领域作为过渡。</p>
<p>自媒体正在最近几年内崛起,同时由于聚合网站的存在,信息在不断地爆炸。一个好的作品、文章要传播起来已经越来越越难了。</p>
<p>在现有的领域里,知乎、微博、公众号已经开始在垄断人们的碎片时间。一个东西可能在火了几天之后,你就很难得到他的信息了。</p>
<p>所以在适当的时候去推销,在适当的时候持续下去。</p>
<h2>小结</h2>
<p>在那之前你需要明白的一点是,你要成为一个怎样的人?是行业的专家?还是领域的专家?等等!</p>
<p><img src="https://www.phodal.com/static/media/uploads/cat-interest.jpg" alt="cat-interest.jpg" title="cat-interest.jpg"></p>
<p>我想的是一直可以做一些有趣的事,单纯深入一个垂直领域对我来说太枯燥了。对于我来说,我只会在造轮子的时候去研究一个轮子。而不是深入研究某个轮子,我会得到造某种轮子的方法。我只会在创造一些有意思的东西的时候,才会深入某个领域去学习。而不是为了深入某个领域,再去学习这个领域的知识 。。</p>
<p>每个人都可以用不同的方式成长,知道自己的喜欢的然后去计划。</p>
<p>(PS: 欢迎关注我的微信公众号(搜索:Phodal))</p>
<p><img src="https://www.phodal.com/static/phodal/images/qrcode.jpg" alt="enter image description here" title="enter image description here"></p>
我的编程之路:创造、分享是我编程的乐趣
https://segmentfault.com/a/1190000004513191
2016-02-29T23:09:32+08:00
2016-02-29T23:09:32+08:00
phodal
https://segmentfault.com/u/phodal
8
<h4>简单介绍一下自己和目前的工作</h4>
<p>我是黄峰达,常用Phodal这个ID活跃于网络~~。自小开始接触软件编程,毕业于西安文理学院电子信息工程专业,资深硬件爱好者,偶尔搞搞移动应用开发,人工智能等等。喜欢写技术文章,分享研发收获,长期活跃于CSDN、SegmentFault、Github等网站。<strong>个人喜欢的语言是Python和JavaScript,但是从工作上使用的Java语言学到的知识是最多的</strong>。</p>
<p>现在是ThoughtWorks公司的一名软件开发工程师,又称为Consultant,主要工作是Web开发。至于工作的内容就不是很方便透露了~~。如果你熟悉我司的Martin Flower的文章,你就知道我们是乙方啦。</p>
<h4>详细写写你是如何走上编程的道路</h4>
<p>小学时,镇上的学区(即镇上的所有小学)会举办计算机比赛——虽然是山区农村,但是也要参加。那段时间,整天在学校机房里玩DOS,不用上课,不用做作业——学生时代最爽的时期。</p>
<p>小学时期的比赛:</p>
<ul>
<li><p>四年级, UCDOS下的五笔打字比赛</p></li>
<li><p>五年级,使用Logo语言的比赛。</p></li>
<li><p>六年级,Windows 95、Windows 98上的Office操作比赛。</p></li>
</ul>
<p>初一的时候就是QBASIC的比赛,初中的后来就是用小霸王(家里穷~~,买不起电脑),用Game Basic——游戏编程。当时年少无知,写了各种Goto语句,觉得很屌。</p>
<p>高一的时候小霸王被玩坏了,基本就不玩电脑~~。高二的时候,在学3D游戏编程。接着,家里攒了点钱、借了点钱就给我买了个电脑。随后发现GUI编程时候的需要更深入底层的基础,就学Linux内核去了。在搞3D建模的时候,发现一个人搞3D游戏的难度太大了,就没继续了。等高中毕业的时候,发现大学的计算机专业好像没啥有意思的,就去学硬件了。</p>
<p>大学,就是各种焊电路了——最有成就的莫过于拿元件焊了个可以正常使用的电话。因为学校不好找工作的时候,投的硬件公司全都直接被拒了~~。然后发现软件公司工资高,而且好找工作。我的意思是:<strong>不看学校</strong>,就改找软件方向的工作了。</p>
<h4>编程上遇到的问题,你都是怎么解决的?</h4>
<p>大部分的问题都是小问题,都可以Google解决啦。偶尔遇到一些比较复杂的问题就是看源码了——因为前端框架都是使用开源的框架,所以就是到GitHub上看历史提交,然后理解,再修复。</p>
<p>还有一些问题就是选择自己造轮子啦。</p>
<h4>有没有写过一些黑科技(狂拽酷炫屌炸天)程序?</h4>
<p>我觉得最近我在写的Growth就属于<strong>狂拽酷炫屌炸天</strong>的程序。 Growth是一款专注于Web开发者成长的应用,涵盖Web开发的流程及技术栈,Web开发的学习路线、成长衡量等各方面。在这里,你将通过不断检验自己的学习成效,形成属于你自己的独一无二的个性技能图谱。在这里你可以了解到:完整的Web开发,运维,部署,维护、如何写好代码——重构、测试、模式、遗留代码、遗留系统的形成。</p>
<p>广告打完了,说说正题——它是用一份代码构建移动、桌面、Web全平台应用。代码的核心是基于Ionic框架,它是一个混合应用框架,基于Angular.js和Cordova封装。因此,这份代码是可以直接在浏览器上运行的,于是乎我就写了一个脚本来自动部署这个静态网页到AWS S3上。同时,通过Electron框架来封装成桌面应用。最后对特定的屏幕进行一些处理,这样就完美了。</p>
<p>由于原先的规划是拿一年的时候来写这个应用,现在只过了1/6大家有兴趣可以来参与。项目的GitHub: <a href="https://link.segmentfault.com/?enc=Ifjx4V%2FbVwcerdeiwO%2B6%2FA%3D%3D.24DlA7ppptPTu00GwpAwRfghVZaZirK0eoI1TeO1T0wPsL9gXz%2BK58kODOmFdm0f" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=7BC5UA1WkUuWYNM0EafWhg%3D%3D.HsGITUQc4csLv7Yk23IndOF07gdMtYotoFW7ecXEfjjDYZdW2Z8SM5CWNlRpF0O7" rel="nofollow">https://github.com/phodal/growth</a></p>
<h4>推荐一些书籍或者编程大杀器给大家呗,加 1W 个推荐理由</h4>
<p>编程大杀器就是Growth,推荐新手程序员都去试试。</p>
<p>至于编程书籍,大家可以看看我的GitHub进阶书单:<a href="https://link.segmentfault.com/?enc=gk4TM0hDVsgH5MFtnQAikw%3D%3D.0QqopuEgnW8jwDIql9fdtNV98m%2BINzIpMyY%2F6AfXaGbb6I79eZ1AlnwVh5DJL5tw" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=dSZsyRrnyyu085m8AVgIWw%3D%3D.p9ozKho3Iv8tIhIGEespYm2MndnXXoh0PTR4c7ADnfbGt%2BIaW1DHdMxCoEepFY2b" rel="nofollow">https://github.com/phodal/booktree</a>。我写几本不错的推荐给大家:</p>
<ol>
<li><p>《重构到模式》 这本书的中文名字应该叫《重构到模式》!!!重构代码到设计模式就这么简单。它开创性地深入揭示了重构与模式这两种软件开发关键技术之间的联系,说明了通过重构实现模式改善既有的设计,往往优于在新的设计早期使用模式。</p></li>
<li><p>《持续交付:发布可靠软件的系统方法》 这本书讲述如何实现更快、更可靠、低成本的自动化软件交付,描述了如何通过增加反馈,并改进开发人员、测试人员、运维人员和项目经理之间的协作来达到这个目标。</p></li>
<li><p>《浮现式设计:专业软件开发的演进本质》 浮现式设计是一种敏捷技术,强调在开发过程中不断演进。软件本身就不应该是一开始就设计好的,他需要经历一个演化的过程。</p></li>
</ol>
<p>这三本书很具有启发性,大家有空可以去看看。</p>
<h4>哪些好的习惯程序员值得拥有?</h4>
<p>暂时,只想到了下面这三个:</p>
<ol>
<li><p>使用快捷键。这是我到ThoughtWorks学到很重要的一点,以前在学校的时候偶尔使用快捷键。现在变成了一个快捷键强迫者,加上原本的打字比赛的时候练就的手速,操作起来就比较快。</p></li>
<li><p>写博客。总的来说,写博客对我的成长很大,不仅仅可以提高影响力,还可以改善技术人员交流不够的问题。</p></li>
<li><p>写脚本替换重复操作。这是我最近感触最深的,以前能力不足的时候就只会手动去做,现在就开始写脚本来实现功能。</p></li>
</ol>
<h4>能介绍一些你最喜欢的软件和硬件吗?</h4>
<p>最喜欢的软件好像就是Emacs,只是因为RMS发起了GNU项目,而这是GNU项目的最早作品。并且Emcas的架构很不错,底层是C语言,顶层是Emacs Lisp。</p>
<p>因为我是搞硬件的,硬件方面首选就是Arduino和Raspberry Pi,我有各种Arduino的开发板,加起来有十几块。还有5块Raspberry Pi开发板,听说Raspberry Pi 3已经出来了,等涨工资了再去买一个。</p>
<h4>对哪方面技术的发展比较看好?</h4>
<p>觉得AI很棒,但是门槛挺高的——需要花费很高时间和精力。最近一直在玩Oculus,感觉VR也不很不错。不过,我们最近做了一个Web RTC和Oculus结合的业余项目,但是发现虽然很看好,也没有比较好的应用场景。</p>
<h4>请推荐一些程序员专用的追妹子 or 汉子大法。</h4>
<p>去看看我女朋友写的《<a href="https://link.segmentfault.com/?enc=mVjCtWA0EeZogeCp5ErShQ%3D%3D.rLDUdiKVcU4xml2hWFD0UT4BV6ttd7c5PYhOx8L3JKM65G84nkU7s3yB1JKjBIjlXAr9OuEQWTul4JjzZhvPfLhDwIzkm23HpNIp78TasSpcq9qZpo%2B6BMns4nHNaRV%2Bx%2FUK7imM9uVelMbFUjMm4WWhoOTbjOpgYVlt8vZD3i0%3D" rel="nofollow">极客爱情</a>》。</p>
<hr>
<blockquote><p>本文参与了 <a href="https://segmentfault.com/a/1190000004509593">SegmentFault「我的编程之路」计划</a>,欢迎正在阅读的你也加入,一起分享。</p></blockquote>
<hr>
<p>欢迎关注我的微信公众号(Phodal):</p>
<p><img src="/img/bVs6fy" alt="图片描述" title="图片描述"></p>
如何优雅地共享Header和Footer
https://segmentfault.com/a/1190000004458280
2016-02-19T14:10:42+08:00
2016-02-19T14:10:42+08:00
phodal
https://segmentfault.com/u/phodal
3
<p>偶然在微博看到在SegmentFault上的一个共享Header和Footer的问题,而正好我们也解决了这个问题。在这里,就分享一下我们的经验咯。</p>
<h2>业务场景</h2>
<p>我们的业务环境和58同城、搜房网这一类站点差不多。我们维护的站点主要有三个页面:</p>
<ul>
<li><p>一个用于搜索的首页</p></li>
<li><p>一个搜索结果页</p></li>
<li><p>一个用于显示商品信息的详细页。</p></li>
</ul>
<p>总体上和Google类似,有一个简洁的首页,一个搜索结果页,以及目标网站页。在旧有的系统中,这三个不同的页面都是同一个代码库中。</p>
<p>而对于我们的新系统来说,这些都是独立的项目。因为我们需要同Google一样可以快速打开首页,首页就变成了一个部分内容是动态的,但是大部分时间是静态网站。这一点可以参考我之前的另外一篇文章《<a href="https://link.segmentfault.com/?enc=23SmVSguX8f5V%2BSeqxv9Sg%3D%3D.VNEbhBA1aDku8wRTBCVZHKA%2FEfgZMdwg1LGeoYe%2BL2%2Fj2IqScJyGUUQ%2BNtBGYTqZ1Ou4XAqNsOuEu3KXdQ53zw%3D%3D" rel="nofollow">编辑-发布-开发分离</a>》,仅仅只在编辑更新内容的时候,才生成新的首页(静态页面)。在这时,由于能限制用户的访问速度,莫过于CDN了。</p>
<p>同时,我们还有几个不同的博客及十几个引流站点,这些都需要使用同样的Header和Footer。对应于我们的其他页面,我们使用React来构建,这意味着我们需要不同的模板。</p>
<p>并且当我们在设计新的系统的时候,我们有了一个更新网站UI的计划。这意味着我们在替换旧有的系统完成之前,我们需要更新所有网站的UI,WTF!</p>
<p><img src="https://www.phodal.com/static/media/uploads//header-footer.png" alt="Header和Footer架构" title="Header和Footer架构"></p>
<p>So,在这时我们设计了第一个Share Header和Footer的架构。</p>
<h2>基于包与模板的共享</h2>
<p>接着,由于Release时间的限制,我们并没有在一开始的时候实现基于脚本的共享方式。因此我们使用了内部的UI框架,同时这个UI框架将会在我们的所有站点上使用——我们可以使用同样的HTML和CSS。</p>
<p>因此在我们的新站点上,我们使用基于Bower与GitHub的Release方案——我们使用Grunt作为构建工具。每次在本地启动Server的时候,我们都会更新依赖。因此,在我们的站点上,我们只需要更新bower.json中的版本号即可。</p>
<p>而对于我们的旧有站点上,因为是一个遗留系统,没有这么先进的工具。并且从理论上来说,不应该会太多的时间和精力在上面,于是我们选择了手动复制的方案。</p>
<p>对于博客和其他站点,一部分使用手动复制,一部分使用iFrame加载。因此在Release新版本的时候,我们会上传新的Header和Footer到AWS S3上。</p>
<p>对于iFrame的站点来说,他们就实现了动态更新。对于其他站点来说,就需要手动更新。虽是如此,但是对于业务已经固定的网站来说,Header和Footer只会一年更新一两次。不过,如果发生收购和被收购时,会多更新一两次。</p>
<p>那么,对于那些使用的不是基于HTML,而是使用模板的站点怎么办?</p>
<h2>脚本生成的共享方式</h2>
<p>最明显的一个问题就是使用React的站点。因为大部分的模板引擎都是可以支持class="",而React你只能用className=""。所以要么,我们将HTML写在基本的模板文件里,如base.html;要么我们将class替换成className。架构变换成如下所示的结构:</p>
<p><img src="https://www.phodal.com/static/media/uploads/header-footer-with-react.png" alt="Header和Footer架构与React" title="Header和Footer架构与React"></p>
<p>所以,在这时理想的方式就是通过某种类型的模板来生成对应的模板。即我们只需要有一个JSX的模板文件,然后替换其中的相应内容即可。</p>
<p><strong>欢迎关注我的微信公众号(或搜索phodal)</strong>:</p>
<p><img src="/img/bVsHJ9" alt="图片描述" title="图片描述"></p>
一份代码构建移动、桌面、Web全平台应用
https://segmentfault.com/a/1190000004417979
2016-02-05T15:27:03+08:00
2016-02-05T15:27:03+08:00
phodal
https://segmentfault.com/u/phodal
6
<p>Web本身就是跨平台的,这意味着这中间存在着无限的可能性。</p>
<p>我是一名Web Developer,对于我来能用Web开发的事情就用Web来完成就好了——不需要编译,不需要等它编译完。我想到哪我就可以写到哪,我改到哪我就可以发生哪发生了变化。</p>
<p>最近我在写Growth——一个帮助开发人员成长的应用,在近一个月的业余时间里,完成了这个应用的:</p>
<ul>
<li><p>移动应用版:Android、Windows Phone、iOS(等账号和上线)</p></li>
<li><p>Web版</p></li>
<li><p>桌面版:Mac OS、Windows、GNU/Linux</p></li>
</ul>
<p>截图合并如下:</p>
<p><img src="https://www.phodal.com/static/media/uploads/growth-full-platforms.jpg" alt="growth-full-platforms.png" title="growth-full-platforms.png"></p>
<p>而更重要的是它们使用了同一份代码——除了对特定设备进行一些处理就没有其他修改。相信全栈的你已经看出来了:</p>
<p>Web = Chrome + Angular.js + Ionic</p>
<p>Desktop = Electron + Angular.js + Ionic</p>
<p>Mobile = Cordova + Angular.js + Ionic</p>
<p>除了前面的WebView不一样,后面都是Angular.js + Ionic。</p>
<h2>从Web到混合应用,再到桌面应用</h2>
<p>在最打开的时候它只是一个单纯的混合应用,我想总结一下我的学习经验,分享一下学习的心得,如:</p>
<ul>
<li><p>完整的Web开发,运维,部署,维护介绍</p></li>
<li><p>如何写好代码——重构、测试、模式</p></li>
<li><p>遗留代码、遗留系统的形成</p></li>
<li><p>不同阶段所需的技能</p></li>
<li><p>书籍推荐</p></li>
<li><p>技术栈推荐</p></li>
<li><p>Web应用解决方案</p></li>
</ul>
<p>接着我用Ionic创建了这个应用,这是一个再普通不过的过程。在这个过程里,我一直使用Chrome在调度我的代码。因为我是Android用户,我有Google Play的账号,便发布了Android版本。这时候遇到了一个问题,我并没有Apple Developer账号(现在在申请ing。。),而主要的用户对象程序员,这是一群<strong>不土</strong>的土豪。</p>
<p><img src="https://www.phodal.com/static/media/uploads/iphone.jpg" alt="iPHONE" title="iPHONE"></p>
<p>偶然间我才想到,我只要上传Web版本的代码就可以暂时性实现这个需求了。接着找了个AWS S3的插件,直接上传到了AWS S3上托管成静态文件服务。</p>
<p>几天前在Github上收到一个issue——关于创造桌面版, 我便想着这也是可能的,我只需要写一个启动脚本和编译脚本即可。</p>
<p>所以,最后我们的流程图就如下所示:</p>
<p><img src="https://www.phodal.com/static/media/uploads/growth-arch.png" alt="Growth Arch" title="Growth Arch"></p>
<p>除了显示到VR设备上,好像什么也不缺了。并且在我之前的文章《<a href="https://link.segmentfault.com/?enc=CuF8VzCk2%2B6lOvJGE623rw%3D%3D.5beIVE5ulQVhZ6ZJ0laCSwAgH%2BfL3TcU3CMsH1203Di89H4b8M8iVkSVkanFZAAZ%2Foc2fvir%2BnTUI1fJkMFv%2FA%3D%3D" rel="nofollow">Oculus + Node.js + Three.js 打造VR世界</a>》,也展示了Web在VR世界的可能性。</p>
<p>在这实现期间有几个点可以分享一下:</p>
<ol>
<li><p>响应式设计</p></li>
<li><p>平台/设备特定代码</p></li>
</ol>
<h2>响应式设计</h2>
<p>响应式设计可以主要依赖于Media Query,而响应式设计主要要追随的一点是不同的设备不同的显示,如:</p>
<p><img src="https://www.phodal.com/static/media/uploads/full-platforms.jpg" alt="full-platforms.jpg" title="full-platforms.jpg"></p>
<p>这也意味着,我们需要对不同的设备进行一些处理,如在大的屏幕下,我们需要展示菜单:</p>
<p><img src="https://www.phodal.com/static/media/uploads/gnu-linux.jpg" alt="gnu-linux.png" title="gnu-linux.png"></p>
<p>而这可以依赖于Ionic的<strong>expose-aside-when="large"</strong>,而并非所有的情形都是这么简单的。如我最近遇到的问题就是图片缩放的问题,之前的图片针对的都是手机版——经过了一定的缩放。</p>
<p>这时在桌面应用上就会出现问题,就需要限定大小等等。</p>
<p>而这个问题相比于平台特定问题则更容易解决。</p>
<h2>平台特定代码</h2>
<p>对于特定平台才有的问题就不是一件容易解决的事,分享一下:</p>
<h3>存储</h3>
<p>我遇到的第一个问题是<strong>数据存储</strong>的问题。最开始的时候,我只需要开始混合应用。因此我可以用<strong>Preferences</strong>、或者<strong>SQLite</strong>来存储数据。</p>
<p>后来,我扩展到了Web版,我只好用LocalStoarge。于是,我就开始抽象出一个<strong>$storageServices</strong>来做相应的事。接着遇到一系列的问题,我舍弃了原有的方案,直接使用LocalStoarge。</p>
<h3>数据分析</h3>
<p>为了开发方便,我使用Google Analytics来分析用户的行为——毕竟数据对我来说也不是特别重要,只要可以看到有人使用就可以了。</p>
<p>这时候遇到的一个问题是,我不需要记录Web用户的行为,但是我希望可以看到有这样的请求发出。于是对于Web用户来说,只需要:</p>
<pre><code class="js"> trackView: function (view) {
console.log(view);
}</code></pre>
<p>而对于手机用户则是:</p>
<pre><code class="js"> trackView: function (view) {
$window.analytics.startTrackerWithId('UA-71907748-1');
$window.analytics.trackView(view)
}</code></pre>
<p>这样在我调试的时候我只需要打个Log,在产品环境时就会Track。</p>
<h3>更新</h3>
<p>同样的,对于Android用户来说,他们可以选择自行下载更新,所以我需要针对Android用户有一个自动更新:</p>
<pre><code>var isAndroid = ionic.Platform.isAndroid();
if(isAndroid) {
$updateServices.check('main');
}</code></pre>
<h3>桌面应用</h3>
<p>对于桌面应用来说也会有类似的问题,我遇到的第一个问题是Electron默认开启了AMD。于是,直接删之:</p>
<pre><code class="html"><script>
//remove module for electron
if(typeof module !== 'undefined' && module && module.exports){
delete module;
}
</script></code></pre>
<p>类似的问题还有许多,不过由于应用内容的限制,这些问题就没有那么严重了。</p>
<p>如果有一天,我有钱开放这个应用的应用号,那么我就会再次献上这个图:</p>
<p><img src="https://www.phodal.com/static/media/uploads/hexoarch.png" alt="六边形架构" title="六边形架构"></p>
<h2>未来</h2>
<p>我就开始思索这个问题,未来的趋势是合并到一起,而这一个趋势在现在就已经是完成时了。</p>
<p>那么未来呢?你觉得会是怎样的?</p>
<p>项目源码: <a href="https://link.segmentfault.com/?enc=FWKF8f5xrjSRSG%2BwP4zEeg%3D%3D.AoPUzs7AHLNJwpjAi7RigmWoD5JKwuqj1mRQWzRxv5RObHKZvPswYVrdsFl%2FPd43" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=fSvhL39e0qdxMAsiAj%2FO7Q%3D%3D.IRgB005pNoFKcPS2jUWsktmqYSw3AZUHpdc508xIXCw1B19cUn2b9UzOztscsOjW" rel="nofollow">https://github.com/phodal/growth</a></p>
<p>更多内容请关注我的微信公众号:phodal</p>
<p><img src="/img/bVsHJ9" alt="图片描述" title="图片描述"></p>
学习的艺术——如何学好一门技术、语言
https://segmentfault.com/a/1190000004363726
2016-01-25T21:11:49+08:00
2016-01-25T21:11:49+08:00
phodal
https://segmentfault.com/u/phodal
9
<p>还在继续开发Growth(Github: <a href="https://link.segmentfault.com/?enc=2uZf8LCtBHxJWSnsyi9mbA%3D%3D.CeuBsfEom1ukAs5FyvhtZSberCgJmQFWEuieEkyJ75UhvAUVIS3ZWOSO4GGrSM8B" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=05nKLoyIEffsGYCw7BJhXA%3D%3D.TVqLAZYSeEF5%2BBm8QmNGIauHcK2QDWUl8G7UZQ1s88xKuuk2Qsyry71dFC2NcLcj" rel="nofollow">https://github.com/phodal/growth</a>)的路上,很多功能似乎已经趋于完善,有些功能也让我百玩不厌——如最近添加的规则引擎。接着我开始陆陆续续地添加了这么多功能。直接今天我想我需要一篇文章放在最前面来告诉用户,你需要怎样去学习?因此,也就有了此文。</p>
<p>我不是一个学霸,大学挂了六科。也没有受过非常好的教育,上大的大学是二本倒数的。好在计算机学得比较早,也可以混点自学能力,也算混得还行。一直想着做一些有意义的事,并且可以获得更多的用户反馈。想法一直有的,直到有一天在整理资料的时候才想起来可以整理出一个APP。</p>
<p>尽管之前已经有技能树、成长书单和Web Developer 成长路线图的经验,说到底这也是很大的挑战。</p>
<h3>一次语言学习体验</h3>
<p>在我们开始学习一门语言或者技术的时候,我们可能会从一门hello,world开始。</p>
<p>好了,现在我是Scala语言的初学者,接着我用搜索引擎去搜索『Scala』来看看『Scala』是什么鬼:</p>
<blockquote><p>Scala 是一门类Java 的编程语言,它结合了面向对象编程和函数式编程。</p></blockquote>
<p>接着又开始看『Scala 'hello,world'』,然后找到了这样的一个示例:</p>
<pre><code>object HelloWorld {
def main(args: Array[String]): Unit = {
println("Hello, world!")
}
}</code></pre>
<p>GET到了5%的知识。</p>
<p>看上去这门语言相比于Java语言来说还行。然后我找到了一本名为『Scala 指南』的电子书,有这样的一本目录:</p>
<ul>
<li><p>表达式和值</p></li>
<li><p>函数是一等公民</p></li>
<li><p>借贷模式</p></li>
<li><p>按名称传递参数</p></li>
<li><p>定义类</p></li>
<li><p>鸭子类型</p></li>
<li><p>柯里化</p></li>
<li><p>范型</p></li>
<li><p>Traits</p></li>
<li><p>...</p></li>
</ul>
<p>看上去还行, 又GET到了5%的知识点。接着,依照上面的代码和搭建指南在自己的电脑上安装了Scala的环境:</p>
<pre><code class="bash">brew install scala</code></pre>
<p>Windows用户可以用:</p>
<pre><code>choco install scala</code></pre>
<p>然后开始写一个又一个的Demo,感觉自己GET到了很多特别的知识点。</p>
<p>到了第二天忘了!</p>
<p><img src="https://www.phodal.com/static/media/uploads/wrong.jpg" alt="Bro Wrong" title="Bro Wrong"></p>
<p>接着,你又重新把昨天的知识过了一遍,还是没有多大的作用。突然间,你听到别人在讨论什么是<strong>这个世界上最好的语言</strong>——你开始加入讨论了。</p>
<p>于是,你说出了Scala这门语言可以:</p>
<ul>
<li><p>支持高阶函数。lambda,闭包...</p></li>
<li><p>支持偏函数。 match..</p></li>
<li><p>mixin,依赖注入..</p></li>
<li><p>等等</p></li>
</ul>
<p>虽然隔壁的Python小哥赢得了这次辩论,然而你发现你又回想起了Scala的很多特性。</p>
<p><img src="https://www.phodal.com/static/media/uploads/popular.jpg" alt="最流行的语言" title="最流行的语言"></p>
<p>你发现隔壁的Python小哥之所以赢得了这场辩论是因为他把Python语言用到了各个地方——机器学习、人工智能、硬件、Web开发、移动应用等。而,你还没有用Scala写过一个真正的应用。</p>
<p>让我想想我来能做什么?我有一个博客。对,我有一个博客,我可以用Scala把我的博客重写一遍:</p>
<ol>
<li><p>先找一Scala的Web框架,Play看上去很不错,就这个了。这是一个MVC框架,原来用的Express也是一个MVC框架。Router写这里,Controller类似这个,就是这样的。</p></li>
<li><p>既然已经有PyJS,也会有Scala-js,前端就用这个了。</p></li>
</ol>
<p>好了,博客重写了一遍了。</p>
<p>感觉还挺不错的,我决定向隔壁的Java小弟推销这门语言,以解救他于火海之中。</p>
<p>『让我想想我有什么杀手锏?』</p>
<p>『这里的知识好像还缺了一点,这个是什么?』</p>
<p>好了,你已经GET到了90%了。如下图所示:</p>
<p><img src="https://www.phodal.com/static/media/uploads/learn.jpg" alt="Learn" title="Learn"></p>
<p>希望你能从这张图上GET到很多点。</p>
<h3>输出是最好的输入</h3>
<p>上面那张图『学习金字塔』就是在说明——输出是最好的输入。</p>
<p>如果你不试着去写点博客、整理资料、准备分享,那么你可能并没有意识到你缺少了多少东西。虽然你已经有了很多的实践,然并卵。</p>
<p>因为你一直在完成功能、完成工作,你总会有意、无意地漏掉一些知识,而你也没有意识到这些知识的重要性。</p>
<p><img src="https://www.phodal.com/static/media/uploads/output-input.png" alt="Output is Input" title="Output is Input"></p>
<p>从我有限的(500+)博客写作经验里,我发现多数时候我需要更多地的参考资料才能更好也向人们展示这个过程。为了输出我们需要更多的输入,进而加速这个过程。</p>
<p>而如果是写书的时候则是一个更高水平的学习,你需要发现别人在他们的书中欠缺的一些知识点。并且你还要展示一些在别的书中没有,而这本书会展现这个点的知识,这意味着你需要挖掘得更深。</p>
<p>所以,如果下次有人问你如果学一门新语言、技术,那么答案就是写一本书。</p>
<h3>如何应用一门新的技术</h3>
<p>对于多数人来说,写书不是一件容易的事,而应用新的技术则是一件迫在眉睫的事。</p>
<p>通常来说,技术出自于对现有的技术的改进。这就意味着,在掌握现有技术的情况下,我们只需要做一些小小的改动就更可以实现技术升级。</p>
<p>而学习一门新的技术的最好实践就是用这门技术对现有的系统行重写。</p>
<p>第一个系统(v1): <code>Spring MVC</code> + <code>Bootstrap</code> + <code>jQuery</code></p>
<p>那么在那个合适的年代里, 我们需要单页面应用,就使用了Backbone。然后,我们就可以用Mustache + HTML来替换掉JSP。</p>
<p>第二个系统(v2): <code>Spring MVC</code> + <code>Backbone</code> + <code>Mustache</code> </p>
<p>在这时我们已经实现了前后端分离了,这时候系统实现上变成了这样。</p>
<p>第二个系统(v2.2): <code>RESTful Services</code> + <code>Backbone</code> + <code>Mustache</code> </p>
<p>或者</p>
<p>第二个系统(v2.2): <code>RESTful Services</code> + <code>Angular.js 1.x</code> </p>
<p>Spring只是一个RESTful服务,我们还需要一些问题,比如DOM的渲染速度太慢了。</p>
<p>第三个系统(v3): <code>RESTful Services</code> + <code>React</code></p>
<p>系统就是这样一步步演进过来的。</p>
<p>尽管在最后系统的架构已经不是当初的架构,而系统本身的业务逻辑变化并没有发生太大的变化。</p>
<p>特别是对于如博客这一类的系统来说,他的一些技术实现已经趋于稳定,而且是你经常使用的东西。所以,下次试试用新的技术的时候,可以先从对你的博客的重写开始。</p>
浮现式设计
https://segmentfault.com/a/1190000004351421
2016-01-22T15:37:57+08:00
2016-01-22T15:37:57+08:00
phodal
https://segmentfault.com/u/phodal
1
<p>设计模式不是一开始就有的,好的软件也不是一开始就设计成现在这样的,好的设计亦是如此。</p>
<p>导致我们重构现有系统的原因有很多,但是多数是因为原来的代码变得越来越不可读,并且重构的风险太大了。在实现业务逻辑的时候,我们快速地用代码实现,没有测试,没有好的设计。</p>
<p>而下图算是最近两年来想要的一个答案:</p>
<p><img src="/img/remote/1460000007056528?w=1280&h=720" alt="浮现式设计" title="浮现式设计"></p>
<p>浮现式设计是一种敏捷技术,强调在开发过程中不断演进。软件本身就不应该是一开始就设计好的,他需要经历一个演化的过程。</p>
<h2>意图导向</h2>
<p>就和Growth一样在最开始的时候,我不知道我想要的是怎样的——我只有一个想法以及一些相对应的实践。接着我便动手开始做了,这是我的风格。不得不说这是结果导向编程,也是大部分软件开发采用的方法。</p>
<p>所以在一开始的时候,我们就有了下面的代码:</p>
<pre><code>if (rating) {
$scope.showSkillMap = true;
skillFlareChild[skill.text] = [rating];
$scope.ratings = $scope.ratings + rating;
if (rating >= 0) {
$scope.learnedSkills.push({
skill: skill.text,
rating: rating
});
}
if ($scope.ratings > 250) {
$scope.isInfinite = true;
}
}
</code></pre>
<p>代码在不经意间充斥着各种Code Smell,如:</p>
<ol>
<li><p>Magic Number</p></li>
<li><p>超长的类</p></li>
<li><p>等等</p></li>
</ol>
<h2>重构</h2>
<p>还好我们在一开始的时候写了一些测试,这让我们可以有足够的可能性来重构代码,而使得其不至于变成遗留代码。而这也是我们推崇的一些基本实践:</p>
<blockquote><p>红 -> 绿 -> 重构</p></blockquote>
<p>测试是系统不至于腐烂的一个后勤保障,除此我们还需要保持对于Code Smell的嗅觉。如上代码:</p>
<pre><code> if ($scope.ratings > 250) {
$scope.isInfinite = true;
}
</code></pre>
<p>上面代码中的“250”指的到底是?这样的数字怎么能保证别人一看代码就知道250到底是什么?</p>
<p>如下的代码就好一些:</p>
<pre><code> var MAX_SKILL_POINTS = 250;
if ($scope.ratings > MAX_SKILL_POINTS) {
$scope.isInfinite = true;
}
</code></pre>
<p>而在最开始的时候我们想不到这样的结果。最初我们的第一直觉都是一样的,然而只要我们保持着对Code Smell的警惕,情况就会发生更多的变化。</p>
<p>重构是区分普通程序员和专业程序员的一个门槛,而这也是练习得来的一个结果。</p>
<h2>模式与演进</h2>
<p>如果你还懂得一些设计模式,那么想来,软件开发这件事就变得非常简单——我们只需要理解好需求即可。</p>
<p>从一开始就使用模式,要么你是专家,要么你是在自寻苦恼。模式更多的是一些实现的总结,对于多数的实现来说,他们有着诸多的相似之处,他们可以使用相同的模式。</p>
<p>而在需求变化的过程中,一个设计的模式本身也是在不断的改变。如果我们还固执于原有的模式,那么我们就会犯下一个又一个的错误。</p>
<p>在适当的时候改变原有的模式,进行一些演进变显得更有意义一些。如果我们不能在适当的时候引进一些新的技术来,那么旧有的技术就会不断累积。这些技术债就会不断往下叠加,那么这个系统将会接近于崩塌。而我们在一开始所设定的一些业务逻辑,也会随着系统而逝去,这个公司似乎也要到尽头了。</p>
<p>而如果我们可以不断地演进系统——抽象服务、拆分模块等等。业务在技术不断演进地过程中,得以保留下来。</p>
程序员如何提高影响力2.0
https://segmentfault.com/a/1190000004291196
2016-01-12T12:54:31+08:00
2016-01-12T12:54:31+08:00
phodal
https://segmentfault.com/u/phodal
4
<p>尽管之前已经有一篇非常不错的关于《<a href="https://link.segmentfault.com/?enc=HbswDjIFVKIBiz0MWg%2F60w%3D%3D.6UvKLpebU22JCjBn6mAxeyf3Qg0HdEjgiRq017aRtAplEdiy9JfZE2aL5iH3YVB4rML3zJSqL49NNVPOGPm9pQ%3D%3D" rel="nofollow">如何提高影响力</a>》的文章,但是那篇文章缺少一些理论的支持。</p>
<p>不知道你发现了没有你在Github上的代码并不比别人差,但是无论你怎么努力你都换取不了同样的关注。当别人开始使用微信公众号的时候,你也开始使用,但是你怎么努力也不及别人的百分之一的阅读量。</p>
<p>然而尽管你觉得非常不爽,但是你并不知道为什么!我记得mruby刚刚只写了一个README.md的时候,就获得了上千个star。</p>
<p>因为松本行弘很有影响力,也因为它也很有影响力。</p>
<p>这也算得上是<strong>粉丝经济</strong>,在这方面做得最好的有:</p>
<p><img src="/img/remote/1460000004836565" alt="smartisan" title="smartisan"></p>
<p>而作为一个程序员,可以有怎样的『粉丝经济』呢?</p>
<p>阮一峰与《ECMAScript 6入门》就是一个很好的例子,尽管我见过的阮一峰的技术博客都写得很浅、不够深入。我也见过很多写得比他好的,但是阅读量却远远不及。</p>
<p><strong>THE WORLD IS CHANGED</strong></p>
<p>所以,有时候并不是你实力够了就行了。现在已经比过去好了许多,但是还是不够。</p>
<blockquote><p>影响力方程式: IMPACT = C x ( R + E + A + T + E)</p></blockquote>
<p>尽管这是一本书的名字,但是我们还是稍微展开一下:</p>
<ul>
<li><p>C: 对比度</p></li>
<li><p>R: 触及率</p></li>
<li><p>E:曝光度</p></li>
<li><p>A:表达方式</p></li>
<li><p>T:信任度</p></li>
<li><p>E:共鸣水平</p></li>
</ul>
<h3>对比度——证明你写的代码比别人好!</h3>
<p>什么算得上是对比度?</p>
<p><img src="/img/remote/1460000004836567" alt="Python框架对比" title="Python框架对比"></p>
<p>如上图所示的Python框架中的性能对比算是一个很不错的对比。尽管我是一个重度Django爱好者,和一个轻度Flask爱好者。但是他们的目的是不一样,在后来我需要一个RESTful API的时候,我选择了Falcon,因为上图表明了Flacon的性能更好!</p>
<p>如果你正在写一个框架,而且你发现这个时候已经有别的轮子存在了,而且用户很多。那么你有多大的把握把用户拉过来?</p>
<p>jQuery框架就是一个很受欢迎的前端框架。那么谁做到了可以替换jQuery?</p>
<ul>
<li><p>Zepto:更小的jQuery</p></li>
<li><p>React:更快的DOM操作</p></li>
</ul>
<p>又比如,已经有人写了一系列的ES6教程,那么你怎么超越他?连Google在显示搜索结果的时候,也会删除内容重复。机器是如此,更何况是人呢。试试不同的角度,不再是只写ES6的语法,而是实战之类的。</p>
<p>这就是为什么在上述的表达式中,如果对比度为零,那么整个结果就是零。如果你不能证明你和别人有差异,那么你就很难有明显的影响力。</p>
<p>如果你在一个领域没有啥想法,而另外一个领域是空白的,不凡去试试这个领域。</p>
<h3>表达方式——简单即是好</h3>
<p>与代码相比,这点更适合在写博客上。通常来说,受欢迎的博客都是那些能轻轻松松地帮助别人解决问题的博客。</p>
<p>工作的时候,我们并不没有那些时间去看别人的长篇大论,我们要做的是:</p>
<ul>
<li><p>搜索某个关键词</p></li>
<li><p>点击相应的结果</p></li>
<li><p>然后离开</p></li>
</ul>
<p>如下图所示,打开第一个结果,然后离开。</p>
<p><img src="/img/remote/1460000004836581" alt="Search Result" title="Search Result"></p>
<p>因为这存在一个能力的金字塔模型,处于底层的人往往更多,如下图所示:</p>
<p><img src="/img/remote/1460000004836583" alt="70" title="70"></p>
<p>而那对于大部分的初学者来说,一篇简单易懂的博客、一行简单易读的代码就可以帮他们的代码。甚至是我在平时的时候,也会直接Copy/Paste,尽管会对长的代码做一些Review,但是短的瞬间就能理解了。</p>
<p>并且对于程序员来说,表达的问题足以让多数人不愿意写博客。而写简单的博客往往更易受欢迎,因为可以帮人解决问题。</p>
<h3>触及率——你的发声平台</h3>
<p>在开始尝试这一点之间,你需要有一个Unique ID。它用来说明我是谁,你在xx网站上看到的那个xx就是我。所以我在后来的经验中学会了这一点,不管在哪里,我都用phodal这个ID。</p>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=7mFVfofpoL3VXXgT8tbd1w%3D%3D.fLOO7RPI%2BgIAKVzTdtBDQqoW8Mav9Orn6Z3gkyXpghI%3D" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=oqQpKoRjeauxH4DdZGKa0w%3D%3D.t1FkWT81gbc%2F8b6Vbud4e0lTxhBasuYQbZK0cQHmSak%3D" rel="nofollow">http://www.phodal.com/</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=JF27kReLsdR9lWjmAPAmdw%3D%3D.AbM7%2FtO6evHd23pfPqKv2VoEwAo8HhaVdCPSnphNr9o%3D" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=kaZoWgONcYTmTw9bbsJYkQ%3D%3D.3WLGHxDXZ0BjxiGbbyI6lxehC%2Fr58hmgNphqM3BjKoI%3D" rel="nofollow">http://weibo.com/phodal</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=kQV6NmUH7GnW9mMQxZjs2g%3D%3D.V9cAkW8PHogm6v0TA6siU%2FUBn7vehBEekkJOtN2HiSO3uIGTBsa5qrKkpLoIzbL2" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=LVPfA6%2BEn2m%2FLlrYfQi3GQ%3D%3D.WCT2ndSZkKoLJguGRVSDFLqJBM2xEtN6hOvBk6dDamPsIf7oU3kMMflMx9JJJHR0" rel="nofollow">http://www.zhihu.com/people/phodal</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=7BLp4sRPHQxCCHW2OjwXTw%3D%3D.COo8uTBpStnOFxrU%2FuZCXjnxbu8FNpqeMmzwmbJhQ3c%3D" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=6R98%2B2SFXq0KXX9PMTxfpQ%3D%3D.AuVUXg95mBveq3EK4t2spaITvGEcDTzpTcsfIt2BEt4%3D" rel="nofollow">http://github.com/phodal</a></p></li>
<li><p><a href="http://segmentfault.com/u/phodal"></a><a href="http://segmentfault.com/u/phodal">http://segmentfault.com/u/phodal</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=s4DjX%2BnnJjL%2FnRTTTI5liQ%3D%3D.%2BfZZ3wgvfwM6VLtNxINbMnn3T06fLawDwAfLVWjGQm1MBc9%2BK%2Bbzaq4OGEZwvD7W" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=zlccORgtzYvQjlXDWppYLg%3D%3D.%2FtDcbki9ZCqFplRJGrjgiFmV3iO0fYlQr%2FMt%2F84pcWx%2BM238xPHst1ZFA2xkVMw3" rel="nofollow">http://www.douban.com/people/phodal/</a></p></li>
<li><p>...</p></li>
</ul>
<p>我就是Phodal,你在哪看到的Phodal都是我。</p>
<p>而这是一个很漫长的过程,开发人员通常喜欢在不同的社区聚集。这就意味着,当我们完成某个软件、文章的时候,我们需要去让不同的人看到。</p>
<p>我们先在我们的公众平台、博客上发,然后是知乎、CSDN、图灵等等专栏,最后推荐到如掘金、开发者头条、极客头条这样的聚合网站上。</p>
<p>我们就可以慢慢地积累人气。</p>
<h3>曝光度——让自己无处不在</h3>
<p>这是一个非常有意思的话题。如果我们每天都可以上头条,那么我们的粉丝肯定会一直往上走。而如果我们很久一句话也不说,那么会掉很多关注。</p>
<p>而对于程度员来说,这点还算很难。如果你一直在加班,那么你会有时间去发这些么?对于这一点来说,还是有几个不错的东西可以做的~~</p>
<ol>
<li><p>针对不同的平台有不同的内容。如我喜欢在我的微博发一些Github上有意思的项目,在我的博客上发一些解决问题的博客。</p></li>
<li><p>整理一些电子书。如果你也写了很多文章,那么要整理出一本电子书一定是一件很容易的事——我整理了《一步步搭建物联网》、《GitHub漫游指南》</p></li>
<li><p>避免内容多平台同步。当你来到一个新的平台的时候, 这个策略是相当有效的。但是到了后期就不是了,用户在不同的地方都看到了相同的内容。</p></li>
</ol>
<p>接着就是不断地坚持下去,去年我写了127篇博客。然后已经在GitHub上刷了461天,你呢?</p>
<p><img src="/img/remote/1460000006912788?w=600&h=238" alt="Github 461" title="Github 461"></p>
<p>坚持比完美更重要哦!</p>
<h3>信任度——跟着你就是对的!</h3>
<p>人们为什么就相信罗永浩能做出好的手机?因为人们相信他。</p>
<p>人们为什么跟随你?因为相信你能写一些有意思的玩意儿、能帮助他们成长、能看到更广阔的世界。</p>
<p>而人们由粉转黑的原因往往是因为你错了:</p>
<ul>
<li><p>Angular 2.0的升级把很多人坑了,所以多了很多黑粉。</p></li>
<li><p>Ruby on Rails的后期维护把很多人坑了,所以多了很多黑粉。</p></li>
<li><p>Node.js把TJ坑了,所以他转向了GO。</p></li>
</ul>
<p>让人们相信你 ,就是最好的事,而这一点则是另外一个持久的过长。而影响力都是持久的过程,不是么??</p>
<h3>共鸣水平——你说得对!</h3>
<p>我希望我码了这么多次,你可以回一个你说得对!这就说明了我们是有共鸣的。而引起共鸣的很多原因,则是因为我们分享了我们的真实体验、感受。</p>
<ul>
<li><p>使用Python来玩大数据和机器学习就是那么爽。</p></li>
<li><p>使用JavaScript来开发Web、移动应用就是那么快。</p></li>
</ul>
<p>而如果你不是出自于提高影响力的目的来看此文,那么只会是这是一篇瞎扯的文章——因为从一开始我们就没有共同话题。</p>
<p>我想说Node.js的回调是一个大坑,但是你却觉得Node.js的回调让人爽爆了。那我怎么指望你能和我同一路。</p>
<p>当我们试图去分享一个内容的时候,我们也希望别人能引起共鸣。当我写一篇说Java在架构模式中的应用时,我就指望读者说:你说得对!</p>
<p>React.js引起了大家巨大的共鸣,所以它也就有了更大的影响力。</p>
<h2>TODO</h2>
<p>那么,我应该怎么做?</p>
<ul>
<li><p>创建自己的博客分享自己的心得</p></li>
<li><p>回答别人的问题</p></li>
<li><p>。。。</p></li>
</ul>
<p>帮助别人才是提高影响力的基础。</p>
Growth: 一个关于如何成为优秀Web Developer 的 App
https://segmentfault.com/a/1190000004255215
2016-01-05T23:36:51+08:00
2016-01-05T23:36:51+08:00
phodal
https://segmentfault.com/u/phodal
0
<p>想了想还是决定在今天发布一个预览版,这样才能持续改进。Growth是一个关于如何成为优秀的Web Developer的APP——结合技能树、成长路线图、进阶书单、Web七日谈以及一些小测验。 </p>
<p>它是我对于之前学习经验的一些总结,尽管如此也尽量想让它变得简单、可衡量。</p>
<p>Growth主要关注的点是<strong>Web开发的流程以及其技术栈、学习路线</strong>,里面有:</p>
<ul>
<li><p>近乎完整的Web开发流程介绍</p></li>
<li><p>如何写好代码——重构、测试、模式</p></li>
<li><p>Web应用的分析和部署</p></li>
<li><p>如何不再写遗留代码</p></li>
</ul>
<h2>来源</h2>
<p>APP来源于: 对Web应用开发的一个流程认识。</p>
<p>当我到了一个项目时,我发现这是一个遗留系统(没有人知道为什么这里是这样的),尽管我们有足够的测试覆盖率。接着在我们的另外一个项目里,我们不得不选择了基于别的项目组的代码(毕竟是同样的业务),这是一份遗留代码(充满bug、难以维护),并且没有人有兴致去维护好别人留下的代码。随后,我们开始重构现有的系统,使用新的技术、新的架构。尽管如此,我们的新代码却一直徘徊在遗留代码的边缘。</p>
<p>最后,我就想到了Web开发实际上就是七个步骤:</p>
<ol>
<li><p>从零开始</p></li>
<li><p>编码</p></li>
<li><p>上线</p></li>
<li><p>数据分析</p></li>
<li><p>持续交付</p></li>
<li><p>遗留系统</p></li>
<li><p>回顾与新架构</p></li>
</ol>
<p>即之前的Web七日谈,也算是开发的本质:</p>
<p><img src="http://img.blog.csdn.net/20160105233449700" alt="这里写图片描述" title="这里写图片描述"></p>
<p>而在每一天里,我们又要学习到不同的知识。每个点都会有自己的一些简单、工具、TODO事项,那么为什么我们不集合到一起呢?</p>
<p><img src="http://img.blog.csdn.net/20160105233507065" alt="这里写图片描述" title="这里写图片描述"><br><img src="http://img.blog.csdn.net/20160105233521799" alt="这里写图片描述" title="这里写图片描述"></p>
<p>想法就是这么简单,只是结合了太多的东西,反而变得不好整理。</p>
<p>快来试用吧~~~</p>
<ul>
<li><p>在线版:<a href="https://link.segmentfault.com/?enc=bFwxGnfYFvpY3kcKgUpHow%3D%3D.LBPJ95PlQLAF%2FPFqvV86REZbNB3PtYuCDp%2FB51Q10Xg%3D" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=kIMFr276nU28vzHVSuLKBg%3D%3D.QIohYCc759vFwaNNBDL2fDvfK6vI%2BwxE9cDVpNJnrrs%3D" rel="nofollow">http://www.growth.ren/</a></p></li>
<li><p>APP下载: <a href="https://link.segmentfault.com/?enc=oc%2Fm7Hu%2BIt6u7ohd0rmqqA%3D%3D.d%2BmvmpiqYCkaEqMcmd2a0N9v8mg0vjnmhMrgc2DcyyI%3D" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=EjYT6Xp2MYTNVAKjDTfTKg%3D%3D.aYCRTNbrjIOECXwu%2Bc92%2BetZmL%2FQFyFj3dBJRs3vRcU%3D" rel="nofollow">http://fir.im/phodal</a> (Google Play、360、豌豆荚已经可以下载)</p></li>
<li><p>Github: <a href="https://link.segmentfault.com/?enc=uKoct7wnOm6luguMlQlw9w%3D%3D.hf6fhQO89%2F4%2BFi1jtNG3rahgaHQDRY5%2BJAGvUroLXYpTFT%2BozNAoOuwYsPzYgKLZ" rel="nofollow">phodal/growth · GitHub</a></p></li>
</ul>
Repractise基础篇:Web应用开发七日谈
https://segmentfault.com/a/1190000004212020
2015-12-28T12:35:10+08:00
2015-12-28T12:35:10+08:00
phodal
https://segmentfault.com/u/phodal
0
<p>本来想的只是画一个如下的七日图来说说Web开发的,随后又想了想这似乎是一个非常棒的Web开发相关的知识介绍。应用开发是一个很有意思的循环,多数时候我们总会觉得别人的代码写得不好。即使它使用了一种非常好的设计,我们也可能会觉得他很复杂。</p>
<p>而它总结下来就是下面的七天过程里发生的一些事情。</p>
<h2>七日谈</h2>
<p>其实对于Web开发,有下面这张图就足够了。</p>
<p><img src="http://repractise.phodal.com/img/web/web.png" alt="Web开发的七天里" title="Web开发的七天里"></p>
<h3>第一天:新的开始</h3>
<blockquote><p>我们迎来了我们的新的一个项目,看起来每个人都很兴奋。这一天过得很快,也做了特别多的事。</p></blockquote>
<p>首先,我们搭建了自己本地的开发环境。我们选择了一门新的语言,也开始使用新的IDE,一个全新的开始。</p>
<p>接着,我们开始创建一个很简单的Hello,World——在绝大多数语言里都有这样的一个传统。这是一个Web项目,看来我们选用的框架里的Hello,World是一个TODO MVC。</p>
<p>呀!这个框架比原来那个框架看起来更简单,更直接也更加好用。</p>
<p>然后,我们开始去创建我们的构建系统了。让我们告别Ant,迎来新的构建工具,Gradle比他们强大多了。我们可以用这个构建工具来做很多的事情——依赖管理、编译和构造、打包。Gulp看上去很流行,让我们用Gulp吧。顺便再创建一个或多个用于发布和构建的服务器。</p>
<p>最后,在我们的持续构建系统中搭载相应的PipeLine来完成这些事。</p>
<p>第一天,就这样兴奋地结束了。</p>
<h3>第二天:令人期待的新体验</h3>
<blockquote><p>“没办法,第一天就是得做那些事。”</p></blockquote>
<p>现在,才开始真正的编码工作。我们拿到了一个任务,知道了它是做什么之后。</p>
<p>我们开始对其分步,第一步做什么,下一步做什么,每一步都很清楚了。可以编写我们的第一个测试,看来这个测试好像并没有想象中对么简单,我们需要Mock对象。</p>
<p>啊!这个组件需要Fake一个Service。第一个任务看来是完成编码了,让我们对其进行简单的重构。</p>
<p>我们已经有了单元测试,现在让我们添加一个功能测试。在我们这个例子里,似乎也需要一个集成测试。</p>
<p>终于可以Commit,并Push代码。</p>
<h3>第三天:上线准备</h3>
<p>在我们不断地重复第二个步骤的时候,我们也要开始去考虑如何上线了。</p>
<p>我们是直接部署在Docker容器里呢?还是直接部署在服务器上呢?接着,我们还为其配置了缓存服务和均衡负载等等。</p>
<p>咦!这个配置是写死的!这里需要一个Toggle来控制功能是否上线!</p>
<h3>第四天: 数据分析</h3>
<p>上线了几天后,发现一些数据发生了变化。网站的访问速度变快了,使得访问网站的人越来越多。</p>
<p>等等,这个地方好像没有人用过!</p>
<p>唔!这是一个Bug!</p>
<p>应用的性能比以前好多了,一个服务器可以顶以前的两个,一下子省了好多服务器。</p>
<p>看来,用户比较喜欢那个功能,我们增强一下这个功能吧。</p>
<h3>第五天:持续交付</h3>
<blockquote><p>又修了一个bug。</p></blockquote>
<p>噢!我不觉得这个功能用户会喜欢。</p>
<p>哈!这个新功能看上去不错。</p>
<h3>第六天:恶梦</h3>
<p>唉!这代码是谁写的!</p>
<p>这里需要重构一下,这里也需要重构一下。</p>
<p>什么!没有测试!</p>
<p>Shit!</p>
<h3>第七天:总结与计划</h3>
<p>哈!我们的竞争对手使用了新的技术,而且我们的系统已经不行了。让我们设计一个更好的系统出来,这个组件我们可以使用这个技术,这个组件我们可以使用那个技术。</p>
<p>前途又是光明的。</p>
<hr>
<p>现在,我们又回到了第一天了。</p>
<p>(ps:临时广告区,欢迎关注我的微信公众号——首发哦!搜索:phodal,或者扫描下面的二维码)</p>
<p><img src="https://raw.githubusercontent.com/phodal/github-roam/gh-pages/img/qrcode.jpg" alt="Phodal 微信公众号" title="Phodal 微信公众号"></p>
成为一名优秀的Developer的书单
https://segmentfault.com/a/1190000004179628
2015-12-21T23:30:46+08:00
2015-12-21T23:30:46+08:00
phodal
https://segmentfault.com/u/phodal
22
<blockquote><p>这是一份关于如何<a href="https://link.segmentfault.com/?enc=xyVYL1Quz8DpA5AiYff%2BhA%3D%3D.iZ3%2F8tumwxox386dcTcAEnbA6d5IlwRMgGlLX6pXHmoFiILKCMaojM%2Bybtj7n%2Bk9" rel="nofollow">Re-Practise</a>的技术书籍推荐书单。</p></blockquote>
<p>一直画/写一个推荐书单来供大家参考,无奈找不到一本合适的形式。有一天,想到了之前的技术树 <a href="https://link.segmentfault.com/?enc=uDqHimMe8hbVtpxKCV4IYQ%3D%3D.DTAGmEeJ5wwJERoD2b8yQWbR%2FPbYprOKD4qjqQ%2FD%2F6BGMQlccFtkUYHa8tOgAxCe" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=4uJjtQ0fs1Q7tnZGO%2Fmv%2Bg%3D%3D.Z0yF6XahZpxlZthv5lZQu7p7vVn8UoaJxPZycgK%2BhKOVS1g4QCI8USYW%2FQRs%2Bfg7" rel="nofollow">https://github.com/phodal/sherlock</a>,便开始落地了。</p>
<p>我深知自己的知识有限,所以写下本文以便和大家切磋交流。欢迎通过 GitHub 的Issues或者直接Pull Requests方式来分享你的经验。期待你的反馈。</p>
<p>基本阅读路线图:</p>
<p><img src="https://raw.githubusercontent.com/phodal/booktree/gh-pages/app/booktree.png" alt="BookTree" title="BookTree"></p>
<p>首先,你需要选择一门语言,然后学习之。这时有一些还不错,如JavaScript实战,JavaScript Cookbook,前者是一门实站手册,后者则是参考手册。再来一本某某语言,或者框架的实战,与权威指南,你就可以完成工作了。</p>
<p>然后呢?</p>
<p><strong>说明</strong>: 完成工作是一件很容易的事,如何更好地完成工作就需要更多地学习。并且我坚信每一个有技术热情的人,都希望可以看到自己写的框架可以无处不在。</p>
<h3>《技术的本质》 - W. Brain Arthur早已看穿了一切</h3>
<p>这是一本关于『技术是什么,它是如何进化』的书。新的技术是根据于我们当前的目的、一个可实现的原理、一种新现象而发明的。并且,新的技术都是基于之前的技术发展出来的。</p>
<p>So,通过研究现有技术的一些发展,我们可以预料到一些新的技术的出现。尽管新的技术并没有解决一些根本性问题,如业务,但是它是为了业务而简化现有的技术。</p>
<h2>如何写好代码——重构与设计模式</h2>
<p>从编写可读的代码,到重构现有的代码,再到设计模式,是编码的必备法则。</p>
<p><img src="https://raw.githubusercontent.com/phodal/booktree/gh-pages/screenshots/code.jpg" alt="如何写好代码——重构与设计模式" title="如何写好代码——重构与设计模式"></p>
<ul>
<li><p>《测试驱动开发》: 尽管在国内,我们很少听到开发人员写测试,就更少听人们谈论TDD。虽然我不是TDD的死忠,但是我觉得TDD还是很有好处的。特别是当你的函数特别长的时候,它可以驱使你写出更短的函数——更多的函数。红->绿->重构,就是这么简单。</p></li>
<li><p>《重构: 改善既有代码的设计》: 红->绿->重构。如果重构之前没有测试,我相信你可能是想换个工作了。好的代码是重构出来的,而不是一开始就写出来的——除非你的代码不用于任何业务。反正,就是程序员必读,不想多说。</p></li>
<li><p>《Head First设计模式》: GoF的《设计模式》一书很薄,所以也很抽象。第一次看《设计模式》的时候,硬着头皮看了几天,然后放弃了。。。不过,Head First系列可以将各种枯燥的设计生起起来,这是一本非常适合入门设计模式的书籍没有之一。</p></li>
<li><p>《设计模式解析》: 比Head First更深入,但是比DP简单。作者选用了一些常用或者说用到的模式,讲述了为什么在这里适合用它。</p></li>
<li><p>《易读代码的艺术/编写可读代码的艺术》: 纳尼!你居然不想买这本书?</p></li>
<li><p>《代码整洁之道》: 书中给了很方法与规范,遵循它们可以写出整洁的代码。但是整洁并不意味着你应该让代码简单!</p></li>
<li><p>《重构与模式》: 这本书的中文名字应该叫《重构到模式》!!!重构代码到设计模式就这么简单。</p></li>
<li><p>《设计模式》: 必读,并且值得多读几遍。</p></li>
<li><p>《元素模式》: 首先书名很扯!!!这本书更适合叫《设计模式要素》!这本书提取了设计模式中的一些基本元素,很具有启发性。</p></li>
</ul>
<h2>如何写前端框架——前端读书路线图</h2>
<p>以JavaScript作为例子来解释如何去学习一门语言,从语法到最后的MVC。</p>
<p><img src="https://raw.githubusercontent.com/phodal/booktree/gh-pages/screenshots/frontend.jpg" alt="如何写前端框架——前端读书路线图" title="如何写前端框架——前端读书路线图"></p>
<h2>机器学习读书路线</h2>
<p>机器学习依赖于大量的数据和理论知识,而数据又可以创造出美妙的数据可视化图像。</p>
<p><img src="https://raw.githubusercontent.com/phodal/booktree/gh-pages/screenshots/ml.jpg" alt="机器学习读书路线" title="机器学习读书路线"></p>
<h2>持续交付读书路线图</h2>
<p>敏捷是过去,持续交付是现在,未来呢?</p>
<p>想要构建一个好的项目,构建系统、自动化测试、可配置缺一不可。</p>
<p><img src="https://raw.githubusercontent.com/phodal/booktree/gh-pages/screenshots/cd.jpg" alt="持续交付读书路线图" title="持续交付读书路线图"></p>
<h2>领域特定语言读书路线图</h2>
<p>最好的语言就是可以表达你业务的语言。</p>
<p>如果你熟悉前端的知识的话,你会发现DSL无处不在。</p>
<p><img src="https://raw.githubusercontent.com/phodal/booktree/gh-pages/screenshots/dsl.jpg" alt="领域特定语言读书路线图" title="领域特定语言读书路线图"></p>
<h2>架构与模式读书路线图——每个人都是架构师。</h2>
<p>成为架构师最简单的方法就是学习别人的模式。</p>
<p>成为成功架构师便是创建模式。</p>
<p><img src="https://raw.githubusercontent.com/phodal/booktree/gh-pages/screenshots/arch.jpg" alt="架构与模式读书路线图" title="架构与模式读书路线图"></p>
基于Virtual DOM与Diff DOM的测试代码生成
https://segmentfault.com/a/1190000004172216
2015-12-20T17:09:01+08:00
2015-12-20T17:09:01+08:00
phodal
https://segmentfault.com/u/phodal
1
<blockquote><p>尽管是在年末,并且也还没把书翻译完,也还没写完书的第一稿。但是,我还是觉得这是一个非常不错的话题——测试代码生成。</p></blockquote>
<p>当我们在写一些UI测试的时候,我们总需要到浏览器去看一下一些DOM的变化。比如,我们点击了某个下拉菜单,会有另外一个联动的下拉菜单发生了变化。而如果这个事件更复杂的时候,有时我们可能就很难观察出来他们之间的变化。</p>
<h2>Virtual DOM</h2>
<p>尽管这里的例子是以Jasmine作为例子,但是我想对于React也会有同样的方法。</p>
<h3>一个Jasmine jQuery测试</h3>
<p>如下是一个简单的Jamine jQuery的测试示例:</p>
<pre><code class="javascript"> describe("toHaveCss", function (){
beforeEach(function (){
setFixtures(sandbox())
})
it("should pass if the element has matching css", function (){
$("#sandbox").css("display", "none")
$("#sandbox").css("margin-left", "10px")
expect($("#sandbox")).toHaveCss({display: "none", "margin-left": "10px"})
})
});</code></pre>
<p>在beforeEach的时候,我们设定了固定的DOM进去,按照用户的行为做一些相应的操作。接着依据这个DOM中的元素变化 ,来作一些断言。</p>
<p>那么,即使我们已经有一个固定的DOM,想要监听这个DOM的变化就是一件容易的事。在我们断言之前,我们就会有一个新的DOM。我们只需要Diff一下这两个DOM的变化,就可以生成这部分测试代码。</p>
<h3>virtual-dom与HyperScript</h3>
<p>在寻觅中发现了<a href="https://link.segmentfault.com/?enc=P%2BjdZiJBUc1H%2B0HiOsSriA%3D%3D.MAhtXMErp7T5yiXyxOz18EFt%2Bc9sUe3kleEf%2FFjUEqa4jutoJXW0SX8QPZTl0PKt" rel="nofollow">virtual-dom</a>这个库,一个可以支持创建元素、diff计算以及patch操作的库,并且它效率好像还不错。</p>
<p>virtual-dom可以说由下面几部分组成的:</p>
<ol>
<li><p>createElement,用于创建virtual Node。</p></li>
<li><p>diff,顾名思义,diff算法。</p></li>
<li><p>h,用于创建虚拟树的DSL——HyperScript。HyperScript是一个JavaScript的HyperText。</p></li>
<li><p>patch,用于patch修改的内容。</p></li>
</ol>
<p>举例来说,我们有下面一个生成Virtual DOM的函数:</p>
<pre><code class="javascript">function render(count) {
return h('div', {
style: {
textAlign: 'center',
lineHeight: (100 + count) + 'px',
border: '1px solid red',
width: (100 + count) + 'px',
height: (100 + count) + 'px'
}
}, [String(count)]);
}</code></pre>
<p>render函数用于生成一个Virtual Node。在这里,我们可以将我们的变量传进去,如1。就会生成如下图所示的节点:</p>
<pre><code class="javascript">{
"children": [
{
"text": "1"
}
],
"count": 1,
"descendantHooks": false,
"hasThunks": false,
"hasWidgets": false,
"namespace": null,
"properties": {
"style": {
"border": "1px solid red",
"height": "101px",
"lineHeight": "101px",
"textAlign": "center",
"width": "101px"
}
},
"tagName": "DIV"
}</code></pre>
<p>其中包含中相对应的属性等等。而我们只要调用createElement就可以创建出这个DOM。</p>
<p>如果我们修改了这个节点的一些元素,或者我们render了一个count=2的值时,我们就可以diff两个DOM。如:</p>
<pre><code class="javascript">virtualDom.diff(render(2), render(1))</code></pre>
<p>根据两个值的变化就会生成如下的一个对象:</p>
<pre><code class="javascript">{
"0": {
"patch": {
"style": {
"height": "101px",
"lineHeight": "101px",
"width": "101px"
}
},
"type": 4,
"vNode": {
...
}
},
"1": {
"patch": {
"text": "1"
},
"type": 1,
"vNode": {
"text": "2"
}
},
...
}</code></pre>
<p>第一个对象,即0中包含了一些属性的变化。而第二个则是文本的变化——从2变成了1。我们所要做的测试生成便是标记这些变化,并记录之。</p>
<h2>标记DOM变化</h2>
<p>由于virtual-dom依赖于虚拟节点vNode,我们需要将fixtures转换为hyperscript。这里我们就需要一个名为html2hyperscript的插件,来解析html。接着,我们就可以diff转换完后的DOM:</p>
<pre><code class="javascript">var leftNode = "", rightNode = "";
var fixtures = '<div id="example"><h1 class="hello">Hello World</h1></div>';
var change = '<div id="example"><h1 class="hello">Hello World</h1><h2>fs</h2></div>';
parser(fixtures, function (err, hscript) {
leftNode = eval(hscript);
});
parser(change, function (err, hscript) {
rightNode = eval(hscript);
});
var patches = diff(leftNode, rightNode);</code></pre>
<p>接着,我们需要调用patch函数来做一些相应的改变。</p>
<pre><code class="javascript">luffa.patch(virtualDom.create(leftNode), patches)</code></pre>
<p>并且,我们可以尝试在patch阶段做一些处理——输出修改:</p>
<pre><code class="javascript">function printChange(originRootNodeHTML, applyNode) {
var patchType;
for (var patchIndex = 0; patchIndex < applyNode.newNodes.length; patchIndex++) {
patchType = applyNode.newNodes[patchIndex].method;
switch (patchType) {
case 'insert':
printInsert(applyNode);
break;
case 'node':
printNode(applyNode, originRootNodeHTML, patchIndex);
break;
case 'remove':
printRemove(applyNode, originRootNodeHTML, patchIndex);
break;
case 'string':
printString(applyNode, originRootNodeHTML, patchIndex);
break;
case 'prop':
printProp(applyNode, originRootNodeHTML, patchIndex);
break;
default:
printDefault(applyNode, originRootNodeHTML, patchIndex);
}
}
}</code></pre>
<p>根据不同的类型,作一些对应的输出处理,如pringNode:</p>
<pre><code class="javascript">function printNode(applyNode, originRootNodeHTML, patchIndex) {
var originNode = $(applyNode.newNodes[patchIndex].vNode).prop('outerHTML') || $(applyNode.newNodes[patchIndex].vNode).text();
var newNode = $(applyNode.newNodes[patchIndex].newNode).prop('outerHTML');
console.log('%c' + originRootNodeHTML.replace(originNode, '%c' + originNode + '%c') + ', %c' + newNode, luffa.ORIGIN_STYLE, luffa.CHANGE_STYLE, luffa.ORIGIN_STYLE, luffa.NEW_STYLE);
}</code></pre>
<p>用Chrome的console来标记修改的部分,及添加的部分。<br><img src="/img/bVrFx3" alt="图片描述" title="图片描述"><br>最后,我们似乎就可以生成相应的测试代码了。。。</p>
<h3>其他</h3>
<p>源码见:<a href="https://link.segmentfault.com/?enc=NkHyFeLmFxX5Jhu5f2%2ByOw%3D%3D.3bkAM8DMRxALWsSi29RSCzGVGm54UzWy%2FWJ6VV%2B6plc%3D" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=5NsfzArcTVHKVMQYlN0Q3w%3D%3D.0VepLFNySF3BaV4B7o1fvYYCTbRC38LW%2FtuVepChcT8%3D" rel="nofollow">https://github.com/phodal/luffa</a><br>原文:<a href="https://link.segmentfault.com/?enc=w8PYGE6gl2bGQQ8Bf%2FdYWQ%3D%3D.vy3FUrcCgkd7WxVJOhGKokr4TP3HCsOfxiB22qM5Xceon25lVUQFPgtkeWRt%2Baateeh3aYypQNlmQBUxT3TPLA%3D%3D" rel="nofollow">基于Virtual DOM与Diff DOM的测试代码生成</a></p>
Oculus + Node.js + Three.js 打造VR世界
https://segmentfault.com/a/1190000004029834
2015-11-22T21:18:27+08:00
2015-11-22T21:18:27+08:00
phodal
https://segmentfault.com/u/phodal
4
<h2>Oculus + Node.js + Three.js 打造VR世界</h2>
<blockquote><p>Oculus Rift 是一款为电子游戏设计的头戴式显示器。这是一款虚拟现实设备。这款设备很可能改变未来人们游戏的方式。</p></blockquote>
<p>周五Hackday Showcase的时候,突然有了点小灵感,便将闲置在公司的Oculus DK2借回家了——已经都是灰尘了~~。</p>
<p>在尝试一个晚上的开发环境搭建后,我放弃了开发原生应用的想法。一是没有属于自己的电脑(如果Raspberry Pi II不算的话)——没有Windows、没有GNU/Linux,二是公司配的电脑是Mac OS。对于嵌入式开发和游戏开发来说,Mac OS简直是手机中的Windows Phone——坑爹的LLVM、GCC(Mac OS )、OpenGL、OGLPlus、C++11。并且官方对Mac OS和Linux的SDK的支持已经落后了好几个世纪。</p>
<p>说到底,还是Web的开发环境到底还是比较容易搭建的。这个repo的最后效果图如下所示:</p>
<p><img src="https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/master/docs/demo.jpg" alt="最后效果图" title="最后效果图"></p>
<p>效果:</p>
<ol>
<li><p>WASD控制前进、后退等等。</p></li>
<li><p>旋转头部 = 真实的世界。</p></li>
<li><p>附加效果: 看久了头晕。</p></li>
</ol>
<p>现在,让我们开始构建吧。</p>
<h3>Node Oculus Services</h3>
<p>这里,我们所要做的事情便是将传感器返回来的四元数(Quaternions)与欧拉角(Euler angles)以API的形式返回到前端。</p>
<h4>安装Node NMD</h4>
<p>Node.js上有一个Oculus的插件名为node-hmd,hmd即面向头戴式显示器。它就是Oculus SDK的Node接口,虽说年代已经有些久远了,但是似乎是可以用的——官方针对 Mac OS和Linux的SDK也已经很久没有更新了。</p>
<p>在GNU/Linux系统下,你需要安装下面的这些东西的</p>
<pre><code>freeglut3-dev
mesa-common-dev
libudev-dev
libxext-dev
libxinerama-dev
libxrandr-dev
libxxf86vm-dev</code></pre>
<p>Mac OS如果安装失败,请使用Clang来,以及GCC的C标准库(PS: 就是 Clang + GCC的混合体,它们之间就是各种复杂的关系。。):</p>
<pre><code>export CXXFLAGS=-stdlib=libstdc++
export CC=/usr/bin/clang
export CXX=/usr/bin/clang++</code></pre>
<p>(PS: 我使用的是Mac OS El Captian + Xcode 7.0. 2)clang版本如下:</p>
<pre><code>Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.0.0
Thread model: posix</code></pre>
<p>反正都是会报错的:</p>
<pre><code>ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Service/Service_NetClient.o) was built for newer OSX version (10.7) than being linked (10.5)
ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Tracking/Tracking_SensorStateReader.o) was built for newer OSX version (10.7) than being linked (10.5)
ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Util/Util_ImageWindow.o) was built for newer OSX version (10.7) than being linked (10.5)
ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Util/Util_Interface.o) was built for newer OSX version (10.7) than being linked (10.5)
ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Util/Util_LatencyTest2Reader.o) was built for newer OSX version (10.7) than being linked (10.5)
ld: warning: object file (Release/obj.target/hmd/src/platform/mac/LibOVR/Src/Util/Util_Render_Stereo.o) was built for newer OSX version (10.7) than being linked (10.5)
node-hmd@0.2.1 node_modules/node-hmd</code></pre>
<p>不过,有最后一行就够了。</p>
<h4>Node.js Oculus Hello,World</h4>
<p>现在,我们就可以写一个Hello,World了,直接来官方的示例~~。</p>
<pre><code class="javascript">var hmd = require('node-hmd');
var manager = hmd.createManager("oculusrift");
manager.getDeviceInfo(function(err, deviceInfo) {
if(!err) {
console.log(deviceInfo);
}
else {
console.error("Unable to retrieve device information.");
}
});
manager.getDeviceOrientation(function(err, deviceOrientation) {
if(!err) {
console.log(deviceOrientation);
}
else {
console.error("Unable to retrieve device orientation.");
}
});</code></pre>
<p>运行之前,记得先连上你的Oculus。会有类似于下面的结果:</p>
<pre><code class="javascript">{ CameraFrustumFarZInMeters: 2.5,
CameraFrustumHFovInRadians: 1.29154372215271,
CameraFrustumNearZInMeters: 0.4000000059604645,
CameraFrustumVFovInRadians: 0.942477822303772,
DefaultEyeFov:
[ { RightTan: 1.0923680067062378,
LeftTan: 1.0586576461791992,
DownTan: 1.3292863368988037,
UpTan: 1.3292863368988037 },
{ RightTan: 1.0586576461791992,
LeftTan: 1.0923680067062378,
DownTan: 1.3292863368988037,
UpTan: 1.3292863368988037 } ],
DisplayDeviceName: '',
DisplayId: 880804035,
DistortionCaps: 66027,
EyeRenderOrder: [ 1, 0 ],
...</code></pre>
<p>接着,我们就可以实时返回这些数据了。</p>
<h4>Node Oculus WebSocket</h4>
<p>在网上看到<a href="https://link.segmentfault.com/?enc=grnGiszNgGhE7eA5UPZ9kA%3D%3D.scb16NEpFjLsK0D%2FEDYM%2FkcFUflnGCVpkFuFdcduz1Pbd6S9dp11WKo8cMbxN0Uk" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=cqxC%2BezI1xnnCfhxCk5Hpg%3D%3D.bcZBufyHjCl7cHwiB%2BjRgGWEHkRPodes%2B%2F%2Fs7MZs7yolEVuoLN9x7ucKllkz1o25" rel="nofollow">http://laht.info/WebGL/DK2Demo.html</a>这个虚拟现实的电影,并且发现了它有一个WebSocket,然而是Java写的,只能拿来当参考代码。</p>
<p>现在我们就可以写一个这样的Web Services,用的仍然是Express + Node.js + WS。</p>
<pre><code class="javascript">var hmd = require("node-hmd"),
express = require("express"),
http = require("http").createServer(),
WebSocketServer = require('ws').Server,
path = require('path');
// Create HMD manager object
console.info("Attempting to load node-hmd driver: oculusrift");
var manager = hmd.createManager("oculusrift");
if (typeof(manager) === "undefined") {
console.error("Unable to load driver: oculusrift");
process.exit(1);
}
// Instantiate express server
var app = express();
app.set('port', process.env.PORT || 3000);
app.use(express.static(path.join(__dirname + '/', 'public')));
app.set('views', path.join(__dirname + '/public/', 'views'));
app.set('view engine', 'jade');
app.get('/demo', function (req, res) {
'use strict';
res.render('demo', {
title: 'Home'
});
});
// Attach socket.io listener to the server
var wss = new WebSocketServer({server: http});
var id = 1;
wss.on('open', function open() {
console.log('connected');
});
// On socket connection set up event emitters to automatically push the HMD orientation data
wss.on("connection", function (ws) {
function emitOrientation() {
id = id + 1;
var deviceQuat = manager.getDeviceQuatSync();
var devicePosition = manager.getDevicePositionSync();
var data = JSON.stringify({
id: id,
quat: deviceQuat,
position: devicePosition
});
ws.send(data, function (error) {
//it's a bug of websocket, see in https://github.com/websockets/ws/issues/337
});
}
var orientation = setInterval(emitOrientation, 1000);
ws.on("message", function (data) {
clearInterval(orientation);
orientation = setInterval(emitOrientation, data);
});
ws.on("close", function () {
setTimeout(null, 500);
clearInterval(orientation);
console.log("disconnect");
});
});
// Launch express server
http.on('request', app);
http.listen(3000, function () {
console.log("Express server listening on port 3000");
});</code></pre>
<p>总之,就是连上的时候不断地发现设备的数据:</p>
<pre><code class="javascript">var data = JSON.stringify({
id: id,
quat: deviceQuat,
position: devicePosition
});
ws.send(data, function (error) {
//it's a bug of websocket, see in https://github.com/websockets/ws/issues/337
});</code></pre>
<p>上面有一行注释是我之前一直遇到的一个坑,总之需要callback就是了。</p>
<h3>Three.js + Oculus Effect + DK2 Control</h3>
<p>在最后我们需要如下的画面:</p>
<p><img src="https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/master/docs/oculus-vr.jpg" alt="Three.js Oculus Effect" title="Three.js Oculus Effect"></p>
<p>当然,如果你已经安装了Web VR这一类的东西,你就不需要这样的效果了。如标题所说,你已经知道要用Oculus Effect,它是一个Three.js的插件。</p>
<p>在之前的版本中,Three.js都提供了Oculus的Demo,当然只能用来看。并且交互的接口是HTTP,感觉很难玩~~。</p>
<h3>Three.js DK2Controls</h3>
<p>这时,我们就需要根据上面传过来的<code>四元数</code>(Quaternions)与欧拉角(Euler angles)来作相应的处理。</p>
<pre><code class="javascript">{
"position": {
"x": 0.020077044144272804,
"y": -0.0040545957162976265,
"z": 0.16216422617435455
},
"quat": {
"w": 0.10187230259180069,
"x": -0.02359195239841938,
"y": -0.99427556991577148,
"z": -0.021934293210506439
}
}</code></pre>
<h4>欧拉角与四元数</h4>
<p>(ps: 如果没copy好,麻烦提出正确的说法,原谅我这个挂过高数的人。我只在高中的时候,看到这些资料。)</p>
<blockquote><p>欧拉角是一组用于描述刚体姿态的角度,欧拉提出,刚体在三维欧氏空间中的任意朝向可以由绕三个轴的转动复合生成。通常情况下,三个轴是相互正交的。</p></blockquote>
<p>对应的三个角度又分别成为roll(横滚角),pitch(俯仰角)和yaw(偏航角),就是上面的postion里面的三个值。。</p>
<pre><code>roll = (rotation about Z);
pitch = (rotation about (Roll • Y));
yaw = (rotation about (Pitch • Raw • Z));”</code></pre>
<p>-- 引自《Oculus Rift In Action》</p>
<p>转换成代码。。</p>
<pre><code>this.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10);</code></pre>
<blockquote><p>四元数是由爱尔兰数学家威廉·卢云·哈密顿在1843年发现的数学概念。</p></blockquote>
<p>从明确地角度而言,四元数是复数的不可交换延伸。如把四元数的集合考虑成多维实数空间的话,四元数就代表着一个四维空间,相对于复数为二维空间。</p>
<p>反正就是用于<code>描述三维空间的旋转变换</code>。</p>
<p>结合下代码:</p>
<pre><code class="javascript">this.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10);
this.headQuat.set(sensorData.quat.x, sensorData.quat.y, sensorData.quat.z, sensorData.quat.w);
this.camera.setRotationFromQuaternion(this.headQuat);
this.controller.setRotationFromMatrix(this.camera.matrix);</code></pre>
<p>就是,我们需要设置camera和controller的旋转。</p>
<p>这使我有足够的理由相信Oculus就是一个手机 + 一个6轴运动处理组件的升级板——因为,我玩过MPU6050这样的传感器,如图。。。</p>
<p><img src="https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/master/docs/mpu6050.jpg" alt="Oculus 6050" title="Oculus 6050"></p>
<h4>Three.js DK2Controls</h4>
<p>虽然下面的代码不是我写的,但是还是简单地说一下。</p>
<pre><code class="javascript">/*
Copyright 2014 Lars Ivar Hatledal
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
THREE.DK2Controls = function (camera) {
this.camera = camera;
this.ws;
this.sensorData;
this.lastId = -1;
this.controller = new THREE.Object3D();
this.headPos = new THREE.Vector3();
this.headQuat = new THREE.Quaternion();
var that = this;
var ws = new WebSocket("ws://localhost:3000/");
ws.onopen = function () {
console.log("### Connected ####");
};
ws.onmessage = function (evt) {
var message = evt.data;
try {
that.sensorData = JSON.parse(message);
} catch (err) {
console.log(message);
}
};
ws.onclose = function () {
console.log("### Closed ####");
};
this.update = function () {
var sensorData = this.sensorData;
if (sensorData) {
var id = sensorData.id;
if (id > this.lastId) {
this.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10);
this.headQuat.set(sensorData.quat.x, sensorData.quat.y, sensorData.quat.z, sensorData.quat.w);
this.camera.setRotationFromQuaternion(this.headQuat);
this.controller.setRotationFromMatrix(this.camera.matrix);
}
this.lastId = id;
}
this.camera.position.addVectors(this.controller.position, this.headPos);
if (this.camera.position.y < -10) {
this.camera.position.y = -10;
}
if (ws) {
if (ws.readyState === 1) {
ws.send("get\n");
}
}
};
};</code></pre>
<p>打开WebSocket的时候,不断地获取最新的传感器状态,然后update。谁在调用update方法?Three.js</p>
<p>我们需要在我们的初始化代码里初始化我们的control:</p>
<pre><code class="javascript">var oculusControl;
function init() {
...
oculusControl = new THREE.DK2Controls( camera );
...
}</code></pre>
<p>并且不断地调用update方法。</p>
<pre><code class="javascript">function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
oculusControl.update( clock.getDelta() );
THREE.AnimationHandler.update( clock.getDelta() * 100 );
camera.useQuaternion = true;
camera.matrixWorldNeedsUpdate = true;
effect.render(scene, camera);
}</code></pre>
<p>最后,添加相应的KeyHandler就好了~~。</p>
<h4>Three.js KeyHandler</h4>
<p>KeyHandler对于习惯了Web开发的人来说就比较简单了:</p>
<pre><code class="javascript">this.onKeyDown = function (event) {
switch (event.keyCode) {
case 87: //W
this.wasd.up = true;
break;
case 83: //S
this.wasd.down = true;
break;
case 68: //D
this.wasd.right = true;
break;
case 65: //A
this.wasd.left = true;
break;
}
};
this.onKeyUp = function (event) {
switch (event.keyCode) {
case 87: //W
this.wasd.up = false;
break;
case 83: //S
this.wasd.down = false;
break;
case 68: //D
this.wasd.right = false;
break;
case 65: //A
this.wasd.left = false;
break;
}
};</code></pre>
<p>然后就是万恶的if语句了:</p>
<pre><code class="javascript">if (this.wasd.up) {
this.controller.translateZ(-this.translationSpeed * delta);
}
if (this.wasd.down) {
this.controller.translateZ(this.translationSpeed * delta);
}
if (this.wasd.right) {
this.controller.translateX(this.translationSpeed * delta);
}
if (this.wasd.left) {
this.controller.translateX(-this.translationSpeed * delta);
}
this.camera.position.addVectors(this.controller.position, this.headPos);
if (this.camera.position.y < -10) {
this.camera.position.y = -10;
}</code></pre>
<p>快接上你的HMD试试吧~~</p>
<h3>结语</h3>
<p>如我在<a href="https://link.segmentfault.com/?enc=grv2LePXOnPoR%2Ba5r%2B2xTg%3D%3D.9sGG%2FoREAyYFY9SV6DIlAcoRkyJ2WhpY6EbwO7o2oj%2BMbyWGqfwzggoM4kvrAIkU9XVFZA8B6pFV%2BME2FhomdvvXekACM6%2FTZM6j%2BxWAOKM%3D" rel="nofollow">《RePractise前端篇: 前端演进史》</a>一文中所说的,这似乎就是新的"前端"。</p>
编辑-发布-开发分离:git作为NoSQL数据库
https://segmentfault.com/a/1190000004021327
2015-11-20T12:59:36+08:00
2015-11-20T12:59:36+08:00
phodal
https://segmentfault.com/u/phodal
0
<blockquote><p>动态网页是下一个要解决的难题。我们从数据库中读取数据,再用动态去渲染出一个静态页面,并且缓存服务器来缓存这个页面。既然我们都可以用Varnish、Squid这样的软件来缓存页面——表明它们可以是静态的,为什么不考虑直接使用静态网页呢?</p></blockquote>
<p>为了实现之前说到的<code>编辑-发布-开发分离</code>的CMS,我还是花了两天的时间打造了一个面向普通用户的编辑器。效果截图如下所示:</p>
<p><img src="https://www.phodal.com//static/media/uploads/eche-editor-screenshot.png" alt="Echeveria Editor" title="Echeveria Editor"></p>
<p>作为一个普通用户,这是一个很简单的软件。除了Electron + Node.js + React作了一个140M左右的软件,尽管打包完只有40M左右 ,但是还是会把用户吓跑的。不过作为一个快速构建的原型已经很不错了——构建速度很快、并且运行良好。</p>
<p>尽管这个界面看上去还是稍微复杂了一下,还在试着想办法将链接名和日期去掉——问题是为什么会有这两个东西?</p>
<h2>从Schema到数据库</h2>
<p>我们在我们数据库中定义好了Schema——对一个数据库的结构描述。在《<a href="https://link.segmentfault.com/?enc=eKgUpeytvDvKyn2MwFPBlQ%3D%3D.skr%2FWZ0COuV%2BOAjRJJwfTfnMy4y%2BxNJRM5mQdCgJWnqv9mpV2ztVcRRnmozhXSl5KWhdLLRJ6c1ORdaSE7fORw%3D%3D" rel="nofollow">编辑-发布-开发分离</a><br>》一文中我们说到了echeveria-content的一个数据文件如下所示:</p>
<pre><code class="javascript"> {
"title": "白米粥",
"author": "白米粥",
"url": "baimizhou",
"date": "2015-10-21",
"description": "# Blog post \n > This is an example blog post \n Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"blogpost": "# Blog post \n > This is an example blog post \n Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \n Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
}</code></pre>
<p>比起之前的直接生成静态页面这里的数据就是更有意思地一步了,我们从数据库读取数据就是为了生成一个JSON文件。何不直接以JSON的形式存储文件呢?</p>
<p>我们都定义了这每篇文章的基本元素:</p>
<ol>
<li><p>title</p></li>
<li><p>author</p></li>
<li><p>date</p></li>
<li><p>description</p></li>
<li><p>content</p></li>
<li><p>url</p></li>
</ol>
<p>即使我们使用NoSQL我们也很难逃离这种模式。我们定义这些数据,为了在使用的时候更方便。存储这些数据只是这个过程中的一部分,下部分就是取出这些数据并对他们进行过滤,取出我们需要的数据。</p>
<p>Web的骨架就是这么简单,当然APP也是如此。难的地方在于存储怎样的数据,返回怎样的数据。不同的网站存储着不同的数据,如淘宝存储的是商品的信息,Google存储着各种网站的数据——人们需要不同的方式去存储这些数据,为了更好地存储衍生了更多的数据存储方案——于是有了GFS、Haystack等等。运营型网站想尽办法为最后一公里努力着,成长型的网站一直在想着怎样更好的返回数据,从更好的用户体验到机器学习。而数据则是这个过程中不变的东西。</p>
<p>尽管,我已经想了很多办法去尽可能减少元素——在最开始的版本里只有标题和内容。然而为了满足我们在数据库中定义的结构,不得不造出来这么多对于一般用户不友好的字段。如链接名是为了存储的文件名而存在的,即这个链接名在最后会变成文件名:</p>
<pre><code class="javascript">repo.write('master', 'contents/' + data.url + '.json', stringifyData, 'Robot: add article ' + data.title, options, function (err, data) {
if(data.commit){
that.setState({message: "上传成功" + JSON.stringify(data)});
that.refs.snackbar.show();
that.setState({
sending: 0
});
}
});</code></pre>
<p>然后,上面的数据就会变成一个对象存储到“数据库”中。</p>
<p>今天 ,仍然有很多人用Word、Excel来存储数据。因为对于他们来说,这些软件更为直接,他们简单地操作一下就可以对数据进行排序、筛选。数据以怎样的形式存储并不重要,重要的是他们都以文件的形式存储着。</p>
<h2>git作为NoSQL数据库</h2>
<p>在控制台中运行一下 <code>man git</code>你会得到下面的结果:</p>
<p><img src="https://www.phodal.com//static/media/uploads/man-git.png" alt="Man Git" title="Man Git"></p>
<p>这个答案看起来很有意思——不过这看上去似乎无关主题。</p>
<p>不同的数据库会以不同的形式存储到文件中去。blob是git中最为基本的存储单位,我们的每个content都是一个blob。redis可以以rdb文件的形式存储到文件系统中。完成一个CMS,我们并不需要那么多的查询功能。</p>
<blockquote><p>这些上千年的组织机构,只想让人们知道他们想要说的东西。</p></blockquote>
<p>我们使用NoSQL是因为:</p>
<ol>
<li><p>不使用关系模型</p></li>
<li><p>在集群中运行良好</p></li>
<li><p>开源</p></li>
<li><p>无模式</p></li>
<li><p>数据交换格式</p></li>
</ol>
<p>我想其中只有两点对于我来说是比较重要的<code>集群</code>与<code>数据格式</code>。但是集群和数据格式都不是我们要考虑的问题。。。</p>
<p>我们也不存在数据格式的问题、开源的问题,什么问题都没有。。除了,我们之前说到的查询——但是这是可以解决的问题,我们甚至可以返回不同的历史版本的。在这一点上git做得很好,他不会像WordPress那样存储多个版本。</p>
<h3>git + JSON文件</h3>
<p>JSON文件 + Nginx就可以变成这样一个合理的API,甚至是运行方式。我们可以对其进行增、删、改、查,尽管就当前来说查需要一个额外的软件来执行,但是为了实现一个用得比较少的功能,而去花费大把的时间可能就是在浪费。</p>
<p>git的“API”提供了丰富的增、删、改功能——你需要commit就可以了。我们所要做的就是:</p>
<ol>
<li><p>git commit</p></li>
<li><p>git push</p></li>
</ol>
你不再需要动态网页——编辑-发布-开发分离
https://segmentfault.com/a/1190000004006820
2015-11-17T15:33:17+08:00
2015-11-17T15:33:17+08:00
phodal
https://segmentfault.com/u/phodal
4
<p>尽管没有特别的动力去构建一个全新的CMS,但是我还是愿意去撰文一篇来书写如何去做这样的事——编辑-发布-开发分离模式是如何工作的。微服务是我们对于复杂应用的一种趋势,编辑-发布-开发分离模式则是另外一种趋势。在上篇文章《<a href="https://link.segmentfault.com/?enc=SJ63Vv9xN9Oh1gn0ep8ing%3D%3D.YBSipe8%2BlwO9s%2Fc4KS32kxY8dIV6x1MbYf%2BnQXtyi7a5p1j5IdPEzx2qWyUJDG2fhiUg9opvGsaNtlwetThmDTIDN1CA1ukhJvxSymVPwTg%3D" rel="nofollow">Repractise架构篇一: CMS的重构与演进</a>》中,我们说到编辑-发布-开发分离模式。</p>
<h2>系统架构</h2>
<p>如先前提到的,Carrot使用了下面的方案来搭建他们的静态内容的CMS。</p>
<p><img src="https://www.phodal.com/static/media/uploads/carrot.png" alt="Carrot" title="Carrot"></p>
<p>在这个方案里内容是用Contentful来发布他们的内容。而在我司<a href="https://link.segmentfault.com/?enc=1%2BJsOlkZK3k2qSAje4Zx7w%3D%3D.8Oz4xQNeKpdZJNVpHOsriiEwChQX5oXVUgUMNjjgo04%3D" rel="nofollow">ThoughtWorks</a>的官网里则采用了Github来管理这些内容。于是如果让我们写一个基于Github的CMS,那么架构变成了这样:</p>
<p><img src="https://www.phodal.com/static/media/uploads/travis-edit-publish-code.png" alt="Github 编辑-发布-开发" title="Github 编辑-发布-开发"></p>
<p>或许你也用过Hexo / Jekyll / Octopress这样的静态博客,他们的原理都是类似的。我们有一个代码库用于生成静态页面,然后这些静态页面会被PUSH到Github Pages上。</p>
<p>从我们设计系统的角度来说,我们会在Github上有三个代码库:</p>
<ol>
<li><p>Content。用于存放编辑器生成的JSON文件,这样我们就可以GET这些资源,并用Backbone / Angular / React 这些前端框架来搭建SPA。</p></li>
<li><p>Code。开发者在这里存放他们的代码,如主题、静态文件生成器、资源文件等等。</p></li>
<li><p>Builder。在这里它是运行于Travis CI上的一些脚本文件,用于Clone代码,并执行Code中的脚本。</p></li>
</ol>
<p>以及一些额外的服务,当且仅当你有一些额外的功能需求的时候。</p>
<ol>
<li><p>Extend Service。当我们需要搜索服务时,我们就需要这样的一些服务。如我正考虑使用Python的whoosh来完成这个功能,这时候我计划用Flask框架,但是只是计划中——因为没有合适的中间件。</p></li>
<li><p>Editor。相比于前面的那些知识这一步适合更重要,也就是为什么生成的格式是JSON而不是Markdown的原理。对于非程序员来说,要熟练掌握Markdown不是一件容易的事。于是,一个考虑中的方案就是使用 Electron + Node.js来生成API,最后通过GitHub API V3来实现上传。</p></li>
</ol>
<p>So,这一个过程是如何进行的。</p>
<h3>用户场景</h3>
<p>整个过程的Pipeline如下所示:</p>
<ol>
<li><p>编辑使用他们的编辑器来编辑的内容并点击发布,然后这个内容就可以通过GitHub API上传到Content这个Repo里。</p></li>
<li><p>这时候需要有一个WebHooks监测到了Content代码库的变化,便运行Builder这个代码库的Travis CI。</p></li>
<li><p>这个Builder脚本首先,会设置一些基本的git配置。然后clone Content和Code的代码,接着运行构建命令,生成新的内容。</p></li>
<li><p>然后Builder Commit内容,并PUSH内容。</p></li>
</ol>
<p>这里还依赖于WebHook这个东西——还没想到一个合适的解决方案。下面,我们对里面的内容进行一些拆解,Content里面由于是JSON就不多解释了。</p>
<h2>Builder: 构建工具</h2>
<p>Github与Travis之间,可以做一个自动部署的工具。相信已经有很多人在Github上玩过这样的东西——先在Github上生成Token,然后用travis加密:</p>
<pre><code class="bash">travis encrypt-file ssh_key --add</code></pre>
<p>加密后的Key就会保存到<code>.travis.yml</code>文件里,然后就可以在Travis CI上push你的代码到Github上了。</p>
<p>接着,你需要创建个deploy脚本,并且在<code>after_success</code>执行它:</p>
<pre><code class="yml">after_success:
- test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "master" && bash deploy.sh</code></pre>
<p>在这个脚本里,你所需要做的就是clone content和code中的代码,并执行code中的生成脚本,生成新的内容后,提交代码。</p>
<pre><code>#!/bin/bash
set -o errexit -o nounset
rev=$(git rev-parse --short HEAD)
cd stage/
git init
git config user.name "Robot"
git config user.email "robot@phodal.com"
git remote add upstream "https://$GH_TOKEN@github.com/phodal-archive/echeveria-deploy.git"
git fetch upstream
git reset upstream/gh-pages
git clone https://github.com/phodal-archive/echeveria-deploy code
git clone https://github.com/phodal-archive/echeveria-content content
pwd
cp -a content/contents code/content
cd code
npm install
npm install grunt-cli -g
grunt
mv dest/* ../
cd ../
rm -rf code
rm -rf content
touch .
if [ ! -f CNAME ]; then
echo "deploy.baimizhou.net" > CNAME
fi
git add -A .
git commit -m "rebuild pages at ${rev}"
git push -q upstream HEAD:gh-pages</code></pre>
<p>这就是这个builder做的事情——其中最主要的一个任务是<code>grunt</code>,它所做的就是:</p>
<pre><code class="javascript">grunt.registerTask('default', ['clean', 'assemble', 'copy']);</code></pre>
<h2>Code: 静态页面生成</h2>
<p>Assemble是一个使用Node.js,Grunt.js,Gulp,Yeoman 等来实现的静态网页生成系统。这样的生成器有很多,Zurb Foundation, Zurb Ink, Less.js / lesscss.org, Topcoat, Web Experience Toolkit等组织都使用这个工具来生成。这个工具似乎上个Release在一年多以前,现在正在开始0.6。虽然,这并不重要,但是还是顺便一说。</p>
<p>我们所要做的就是在我们的<code>Gruntfile.js</code>中写相应的生成代码。</p>
<pre><code class="javascript"> assemble: {
options: {
flatten: true,
partials: ['templates/includes/*.hbs'],
layoutdir: 'templates/layouts',
data: 'content/blogs.json',
layout: 'default.hbs'
},
site: {
files: {'dest/': ['templates/*.hbs']}
},
blogs: {
options: {
flatten: true,
layoutdir: 'templates/layouts',
data: 'content/*.json',
partials: ['templates/includes/*.hbs'],
pages: pages
},
files: [
{ dest: './dest/blog/', src: '!*' }
]
}
}</code></pre>
<p>配置中的site用于生成页面相关的内容,blogs则可以根据json文件的文件名生成对就的html文件存储到blog目录中。</p>
<p>生成后的目录结果如下图所示:</p>
<pre><code> .
├── about.html
├── blog
│ ├── blog-posts.html
│ └── blogs.html
├── blog.html
├── css
│ ├── images
│ │ └── banner.jpg
│ └── style.css
├── index.html
└── js
├── jquery.min.js
└── script.js
7 directories, 30 files</code></pre>
<p>这里的静态文件内容就是最后我们要发布的内容。</p>
<p>还需要做的一件事情就是:</p>
<pre><code class="javascript">grunt.registerTask('dev', ['default', 'connect:server', 'watch:site']);</code></pre>
<p>用于开发阶段这样的代码就够了,这个和你使用WebPack + React 似乎相差不了多少。</p>
<h2>编辑-发布-开发分离</h2>
<p>在这种情形中,编辑能否完成工作就不依赖于网站——脱稿又少了 个借口。这时候网站出错的概率太小了——你不需要一个缓存服务器、HTTP服务器,由于没有动态生成的内容,你也不需要守护进程。这些内容都是静态文件,你可以将他们放在任何可以提供静态文件托管的地方——CloudFront、S3等等。或者你再相信自己的服务器,Nginx可是全球第二好(第一还没出现)的静态文件服务器。</p>
<p>开发人员只在需要的时候去修改网站的一些内容。</p>
<p>So,你可能会担心如果这时候修改的东西有问题了怎么办。</p>
<ol>
<li><p>使用这种模式就意味着你需要有测试来覆盖这些构建工具、生成工具。</p></li>
<li><p>相比于自己的代码,别人的CMS更可靠?</p></li>
</ol>
<p>需要注意的是如果你上一次构建成功,你生成的文件都是正常的,那么你只需要回滚开发相关的代码即可。旧的代码仍然可以工作得很好。</p>
<p>其次,由于生成的是静态文件,查错的成本就比较低。</p>
<p>最后,重新放上之前的静态文件。</p>
Repractise架构篇一: CMS的重构与演进
https://segmentfault.com/a/1190000003998183
2015-11-15T21:55:07+08:00
2015-11-15T21:55:07+08:00
phodal
https://segmentfault.com/u/phodal
5
<p>重构系统是一项非常具有挑战性的事情。通常来说,在我们的系统是第二个系统的时候才需要重构,即这个系统本身已经很臃肿。我们花费了太量的时间在代码间的逻辑,开发新的功能变得越来越慢。这不仅仅可能只是因为我们之前的架构没有设计好,而且在我们开发的过程中没有保持着原先设计时的一些原则。如果是这样的情况,那么这就是一个复杂的过程。</p>
<p>还有一种情况是我们发现了一种更符合我们当前业务的框架。</p>
<h2>动态CMS</h2>
<h3>CMS简介</h3>
<p>CMS是Content Management System的缩写,意为"内容管理系统".它可以做很多的事情,但是总的来说就是Page和Blog——即我们要创建一些页面可以用于写一些About US、Contact Me,以及持续更新的博客或者新闻,以及其他子系统——通常更新不活跃。通过对这些博客或者新闻进行分类,我们就可以有不同的信息内容,如下图:</p>
<p><img src="http://repractise.phodal.com/img/cms/cms-blogs.png" alt="不同分类的内容" title="不同分类的内容"></p>
<p>CMS是政府和企业都需要的系统,他们有很多的信息需要公开,并且需要对其组织进行宣传。在我有限的CMS交付经验里(大学时期),一般第一次交付CMS的时候,已经创建了大部分页面。有时候这些页面可能直接存储在数据库中,后来发现这不是一个好的方案,于是很多页面变成了静态页面。随后,在CMS的生命周期里就是更新内容。</p>
<p>因而,CMS中起其主导的东西还是Content,即内容。而内容是一些持续可变的东西。这也就是为什么WordPress这么流行于CMS界,它是一个博客系统,但是多数时候我们只需要更新内容。除此不得不提及的一个CMS框架是Drupal,两者一对比会发现Drupal比较强大。通常来说,强大的一个负作用就是——复杂。</p>
<p>WordPress和Drupal这一类的系统都属于发布系统,而其后台可以称为编辑系统。</p>
<p>一般来说CMS有下面的特点:</p>
<ul>
<li><p>支持多用户。</p></li>
<li><p>角色控制-内容管理。如InfoQ的编辑后台就会有这样的机制,社区编辑负责创建内容,而审核发布则是另外的人做的。</p></li>
<li><p>插件管理。如WordPress和Drupal在这一方面就很强大,基本可以满足日常的需要。</p></li>
<li><p>快捷简便地存储内容。简单地来说就是所见即所得编辑器,但是对于开发者来说,Markdown似乎是好的选择。</p></li>
<li><p>预发布。这是一个很重要的特性,特别是如果你的系统后台没有相对应的预览机制。</p></li>
<li><p>子系统。由于这属于定制化的系统,并不方便进行总结。</p></li>
<li><p>...</p></li>
</ul>
<p>CMS一直就是这样一个紧耦合的系统。</p>
<h3>CMS架构与Django</h3>
<p>说起来,我一直是一个CMS党。主要原因还在于我可以随心所欲地去修改网站的内容,修改网站的架构。好的CMS总的来说都有其架构图,下图似乎是Drupal的模块图</p>
<p><img src="http://repractise.phodal.com/img/cms/drupal-modular.png" alt="Drupal 框架" title="Drupal 框架"></p>
<p>一般来说,其底层都会有:</p>
<ul>
<li><p>ORM</p></li>
<li><p>User Management</p></li>
<li><p>I18n / L10n</p></li>
<li><p>Templates</p></li>
</ul>
<p>我一直在使用一个名为Django的Python Web框架,它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是CMS(内容管理系统)软件。它是一个MTV框架——与多数的框架并没有太大的区别。</p>
<table>
<thead><tr>
<th>层次</th>
<th>职责</th>
</tr></thead>
<tbody>
<tr>
<td>模型(Model),即数据存取层</td>
<td>处理与数据相关的所有事务:如何存取、如何验证有效性、包含哪些行为以及数据之间的关系等。</td>
</tr>
<tr>
<td>模板(Template),即表现层</td>
<td>处理与表现相关的决定: 如何在页面或其他类型文档中进行显示。</td>
</tr>
<tr>
<td>视图(View),即业务逻辑层</td>
<td>存取模型及调取恰当模板的相关逻辑。模型与模板之间的桥梁。</td>
</tr>
</tbody>
</table>
<p>从框架本身来上看它和别的系统没有太大的区别。</p>
<p><img src="http://repractise.phodal.com/img/cms/django-architecture.jpg" alt="Django Architecture" title="Django Architecture"></p>
<p>但是如果我们已经有多外模块(即Django中app的概念),那么系统的架构就有所不同了。</p>
<p><img src="http://repractise.phodal.com/img/cms/django-apps.jpg" alt="Django App架构 " title="Django App架构 "></p>
<p>这就是为何我喜欢用这个CMS的原因了,我的每个子系统都以APP的形式提供服务——博客是一个app,sitemap是一个app,api是一个app。系统直接解耦为类似于混合服务的架构,即不像微服务一样多语言化,又不会有宏应用的紧耦合问题。</p>
<h2>编辑-发布分离</h2>
<p>我们的编辑和发布系统在某种意义上紧耦合在一起了,当用户访问量特别大的时候,这样会让我们的应用变得特定慢。有时候编辑甚至发布不了新的东西,如下图引示:</p>
<p><img src="http://repractise.phodal.com/img/cms/editor-publisher.png" alt="发布-编辑" title="发布-编辑"></p>
<p>或者你认识出了上图是源自Martin Folwer的<a href="https://link.segmentfault.com/?enc=ILii6m%2Ffnlujik7YrT9f2A%3D%3D.VhqtuQDy%2F6LDaSpIupd%2FlzMMAsH8%2Bmw0pdtUnBvEoo7rPRcfpAfCYvzzgrBJ3j8lIpwr09z1Iave93DpATYZfg%3D%3D" rel="nofollow">编辑-发布分离</a></p>
<p>编辑-发布分离是几年前解耦复杂系统游来开来带来的一个成果。今天这个似乎已经很常见了,编辑的时候是在后台进行的,等到发布的时候已经变成了一个静态的HTML。</p>
<p>已经有足够多的CMS支持这样的特性,运行起来似乎特别不错,当然这样的系统也会有缓存的问题。有了APP这后,这个趋势就更加明显了——人们需要提供一个API。到底是在现有的系统里提供一个新的API,还是创建一个新的API。</p>
<p>这时候,我更愿意选择后者——毕竟紧耦合一个系统总会在后期带来足够多的麻烦。而且基于数据库构建一个只读的RESTful API并不是一个复杂的过程,而且也危险。这时候的瓶颈就是数据库,但是似乎数据库都是多数系统的瓶颈。人们想出了各种各样的技术来解决这个瓶颈。</p>
<p>于是之前我试着用Node.js + RESTify将我的博客重构成了一个SPA,当然这个时候CMS还在运行着。出于SEO的原因我并没有在最后采用这个方案,因为<a href="https://link.segmentfault.com/?enc=Y0ZYyj%2Fs7JeNMdfMPJFPKQ%3D%3D.%2FqArTDpNACQ%2FL6GxXglNR%2BITGvFnO8EdT%2Bf3bsvjfs4%3D" rel="nofollow">我网站</a>的主要流量来源是Google和是百度。但是我在另外的网站里混合了SPA与MPA,其中的性能与应用是相当的,除了第一次加载页面的时候会带来一些延时。</p>
<p>除了Node.js + RESTify,也试了试Python + Falcon(一个高性能的RESTful框架)。这个API理论上也应该可以给APP直接使用,并且可以直接拿来生成静态页面。</p>
<h2>编辑-发布-开发分离:静态站点生成</h2>
<p>如React一样解决DOM性能的问题就是跳过DOM这个坑,要跳过动态网站的性能问题就是让网站变成静态。</p>
<p>越来越多的开发人员开始在使用Github Pages作为他们的博客,这是一个很有意思的转变。主要的原因是这是免费的,并且基本上可以保证24x7小时是可用的——当且仅当Github发现故障的时候才会不可访问。</p>
<p>在这一类静态站点生成器(Github)里面,比较流行的有下面的内容(数据来源: <a href="http://segmentfault.com/a/1190000002476681"></a><a href="http://segmentfault.com/a/1190000002476681">http://segmentfault.com/a/1190000002476681</a>):</p>
<ol>
<li><p>Jekyll / OctoPress。Jekyll和OctoPress是最流行的静态博客系统。</p></li>
<li><p>Hexo。Hexo是NodeJS编写的静态博客系统,其生成速度快,主题数量相对也比较丰富。是OctoPress的优秀替代者。</p></li>
<li><p>Sculpin。Sculpin是PHP的静态站点系统。Hexo和Octopress专注于博客,而有时候我们的需求不仅仅是博客,而是有类似CMS的页面生成需求。Sculpin是一个泛用途的静态站点生成系统,在支持博客常见的分页、分类tag等同时,也能较好地支持非博客的一般页面生成。</p></li>
<li><p>Hugo。Hugo是GO语言编写的静态站点系统。其生成速度快,且在较好支持博客和非博客内容的同时提供了比较完备的主题系统。无论是自己写主题还是套用别人的主题都比较顺手。</p></li>
</ol>
<p>通常这一类的工具里会有下面的内容:</p>
<ol>
<li><p>模板</p></li>
<li><p>支持Markdown</p></li>
<li><p>元数据</p></li>
</ol>
<p>如Hexo这样的框架甚至提供了<code>一键部署</code>的功能。</p>
<p>在我们写了相关的代码之后,随后要做的就是生成HTML。对于个人博客来说,这是一个非常不错的系统,但是对于一些企业级的系统来说,我们的要求就更高了。如下图是Carrot采用的架构:</p>
<p><img src="http://repractise.phodal.com/img/cms/carrot.png" alt="Editor Develoepr" title="Editor Develoepr"></p>
<p>这与我们在项目上的系统架构目前相似。作为一个博主,通常来说我们修改博客的主题的频率会比较低, 可能是半年一次。如果你经常修改博客的主题,你博客上的文章一定是相当的少。</p>
<p>上图中的编辑者通过一个名为Contentful CMS来创建他们的内容,接着生成RESTful API。而类似的事情,我们也可以用Wordpress + RESTful 插件来完成。如果做得好,那么我想这个API也可以直接给APP使用。</p>
<p>上图中的开发者需要不断地将修改的主题或者类似的东西PUSH到版本管理系统上,接着会有webhook监测到他们的变化,然后编译出新的静态页面。</p>
<p>最后通过Netlify,他们编译到了一起,然后部署到生产环境。除了Netlify,你也可以编写生成脚本,然后用Bamboo、Go这类的CI工具进行编译。</p>
<p>通常来说,生产环境可以使用CDN,如CloudFront服务。与动态网站相比,静态网站很容易直接部署到CDN,并可以直接从离用户近的本地缓存提供服务。除此,直接使用AWS S3的静态网站托管也是一个非常不错的选择。</p>
<h3>基于Github的编辑-发布-开发分离</h3>
<p>尽管我们已经在项目上实施了基于Github的部分内容管理已经有些日子里,但是由于找不到一些相关的资料,便不好透露相关的细节。直到我看到了《<a href="https://link.segmentfault.com/?enc=c0SHbmmp7%2FAFn1v%2BhunoJg%3D%3D.Ch7rxnLoxU%2FNu%2FBCmL7uNGxOALE3VG3B3xPKzIgjaXzsQLcMUFa1oarwDkZMu6pWKo9OnRiq8j3fC1sX2z8jifS%2BHaBvyFz5iUVv7Jj%2Fn%2BzzzNWEVX8oP%2Bi3kdQ2bFXJ" rel="nofollow">An Incremental Approach to Content Management Using Git 1</a>》,我才意识到这似乎已经是一个成熟的技术了。看样子这项技术首先已经应用到了ThoughtWorks的官网上了。</p>
<p>文中提到了使用这种架构的几个点:</p>
<ol>
<li><p>快速地开始项目,而不是学习或者配置框架。</p></li>
<li><p>需要使用我们信奉的原则,如TDD。而这是大部分CMS所不支持的。</p></li>
<li><p>基于服务的架构。</p></li>
<li><p>灵活的语言和工具</p></li>
<li><p>我们是开发人员。</p></li>
</ol>
<p>So,so,这些开发人员做了些什么:</p>
<ol>
<li><p>内容存储为静态文件</p></li>
<li><p>不是所有的内容都是平等的</p></li>
<li><p>引入内容服务</p></li>
<li><p>使用Github。所有的content会提交到一个repo里,同时在我们push内容的时候,可以实时更新这些内容。</p></li>
<li><p>允许内容通过内容服务更新</p></li>
<li><p>使用Github API</p></li>
</ol>
<p>于是,有了一个名为<a href="https://link.segmentfault.com/?enc=0WT%2BCHTikyXVd3P%2Bn7W%2FfQ%3D%3D.qMOmLDPwxH6Th86W8kXJM2P99w2AgNErK2%2F0qyLxR6Nr4mouTcPwXeM28NYG7JPL" rel="nofollow">Hacienda</a>的框架用于管理内容,并存储为JSON。这意味着什么?</p>
<p><img src="http://repractise.phodal.com/img/cms/github-edit-publish-code.png" alt="基于Github的编辑-发布-开发分离" title="基于Github的编辑-发布-开发分离"></p>
<p>因为使用了Git,我们可以了解到一个文件内容的历史版本,相比于WordPress来说更直观,而且更容易 上手。</p>
<p>开发人员修改完他们的代码后,就可以直接提交,不会影响到Editor使用网站。Editor通过一个编辑器添加内容,在保存后,内容以JSON的形式出现直接提交代码到Github上相应的代码库中。CI或者Builder监测到他们的办法,就会生成新的静态页面。在这时候,我们可以选择有一个预览的平台,并且可以一键部署。那么,事情似乎就完成得差不多了。</p>
<p>如果我们有APP,那么我们就可以使用Content Servies来做这些事情。甚至可以直接拿其搭建一个SPA。</p>
<p>如果我们需要全文搜索功能,也变得很简单。我们已经不需要直接和数据库交互,我们可以直接读取JSON并且构建索引。这时候需要一个简单的Web服务,而且这个服务还是只读的。</p>
<p>在需要的时候,如手机APP,我们可以通过Content Servies来创建博客。</p>
<h2>Repractise</h2>
<p>思考完这些后,我想到了一个符合学习的场景。</p>
<p><img src="http://repractise.phodal.com/img/cms/travis-edit-publish-code.png" alt="基于Travis CI的编辑-发布-开发分离" title="基于Travis CI的编辑-发布-开发分离"></p>
<p>我们构建的核心都可以基于Travis CI来完成,唯一存在风险的环节是我们似乎需要暴露我们的Key。</p>
<h2>其他</h2>
<p>原文: <a href="https://link.segmentfault.com/?enc=1igHk9LkpMDDdGDn2Pcibg%3D%3D.%2B9rYe%2FPnG7xxmXNNOYXSg7jP7bBjcj5%2B%2BAXxAYitDUY%2F2Up32g0%2Ff5Zi2mB2b29tiU%2BQ4oGsalBbMFxNzEY2NYNfVPDj%2B%2FAihcHCi35DhRQ%3D" rel="nofollow">Repractise架构篇一: CMS的重构与演进</a></p>
<p>参考文章:</p>
<ol>
<li><p><a href="https://link.segmentfault.com/?enc=snJr0Lo8i5jW317JxRtx8A%3D%3D.oOT9MYQvdZ2dG2re9vMqgRlaWHKIX85kK3NHkKOunsDKUu2zprSn%2BId7PkT%2BjpTt" rel="nofollow">静态网站生成器将会成为下一个大热门</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=alK5%2BICKWzF7R3CkP9GMxg%3D%3D.ia%2FmYiTxASSvIqDFFZ%2FDRLf%2FDWA5lCJvgKLlvVer1MbOybxd9f64nOaiLYzFFbvgkYciki83meBOHjt0%2BNm%2F1w%3D%3D" rel="nofollow">EditingPublishingSeparation</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=VaGAQADESYrwbasgMJhuNQ%3D%3D.QOA5bedHyH%2F8NcqTgvWQGs5mdps7vmXPs4T0GfVnBxilg0Z%2BDInQWxRKAKj9mKxnzg%2Fg77xjFK7hrNJNYE%2BPEaMqZ3qH14p5VfAdwPt1BbpyUcR3OChOUBsQ0Jsr9wet" rel="nofollow">An Incremental Approach to Content Management Using Git 1</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=ZB9ZacYxxajA%2Bvf50DSf4w%3D%3D.UxgLm56AwgZ3gt14Ib3Wm7iQSSe%2FNjZy2zNcbC1uueX%2BcROPRFq8YaX5VkdvZDtOhfpS0vJdmTP8hLCD%2BnzfSaHO7AZTILolUqAaauhHgjDczEowhYOE8FZatcPX6AkdvWAcsGH2SH4Sg4HR5tOAPw%3D%3D" rel="nofollow">Part 2: Implementing Content Management and Publication Using Git</a></p></li>
</ol>
程序员必知之前端演进史
https://segmentfault.com/a/1190000003973286
2015-11-10T08:42:23+08:00
2015-11-10T08:42:23+08:00
phodal
https://segmentfault.com/u/phodal
5
<h2>RePractise前端篇: 前端演进史</h2>
<p>细细整理了过去接触过的那些前端技术,发现前端演进是段特别有意思的历史。人们总是在过去就做出未来需要的框架,而现在流行的是过去的过去发明过的。如,响应式设计不得不提到的一个缺点是:<strong>他只是将原本在模板层做的事,放到了样式(CSS)层来完成</strong>。</p>
<p>复杂度同力一样不会消失,也不会凭空产生,它总是从一个物体转移到另一个物体或一种形式转为另一种形式。</p>
<p>如果六、七年前的移动网络速度和今天一样快,那么直接上的技术就是响应式设计,APP、SPA就不会流行得这么快。尽管我们可以预见未来这些领域会变得更好,但是更需要的是改变现状。改变现状的同时也需要预见未来的需求。</p>
<h4>什么是前端?</h4>
<p>维基百科是这样说的:前端Front-end和后端back-end是描述进程开始和结束的通用词汇。前端作用于采集输入信息,后端进行处理。计算机程序的界面样式,视觉呈现属于前端。</p>
<p>这种说法给人一种很模糊的感觉,但是他说得又很对,它负责视觉展示。在MVC结构或者MVP中,负责视觉显示的部分只有View层,而今天大多数所谓的View层已经超越了View层。前端是一个很神奇的概念,但是而今的前端已经发生了很大的变化。</p>
<p>你引入了Backbone、Angluar,你的架构变成了MVP、MVVM。尽管发生了一些架构上的变化,但是项目的开发并没有因此而发生变化。这其中涉及到了一些职责的问题,如果某一个层级中有太多的职责,那么它是不是加重了一些人的负担?</p>
<h3>前端演进史</h3>
<p>过去一直想整理一篇文章来说说前端发展的历史,但是想着这些历史已经被人们所熟知。后来发现并非如此,大抵是幸存者偏见——关注到的都知道这些历史。</p>
<h4>数据-模板-样式混合</h4>
<p>在有限的前端经验里,我还是经历了那段用Table来作样式的年代。大学期间曾经有偿帮一些公司或者个人开发、维护一些CMS,而Table是当时帮某个网站更新样式接触到的——ASP.Net(maybe)。当时,我们启动这个CMS用的是一个名为<code>aspweb.exe</code>的程序。于是,在我的移动硬盘里找到了下面的代码。</p>
<pre><code class="html"><TABLE cellSpacing=0 cellPadding=0 width=910 align=center border=0>
<TBODY>
<TR>
<TD vAlign=top width=188><TABLE cellSpacing=0 cellPadding=0 width=184 align=center border=0>
<TBODY>
<TR>
<TD><IMG src="Images/xxx.gif" width=184></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=184 align=center
background=Images/xxx.gif border=0></code></pre>
<p>虽然,我也已经在HEAD里找到了现代的雏形——DIV + CSS,然而这仍然是一个Table的年代。</p>
<pre><code class="html"><LINK href="img/xxx.css" type=text/css rel=stylesheet></code></pre>
<p><strong>人们一直在说前端很难,问题是你学过么???</strong></p>
<p><strong>人们一直在说前端很难,问题是你学过么???</strong></p>
<p><strong>人们一直在说前端很难,问题是你学过么???</strong></p>
<p>也许,你也一直在说CSS不好写,但是CSS真的不好写么?人们总在说JS很难用,但是你学过么?只在需要的时候才去学,那肯定很难。<strong>你不曾花时间去学习一门语言,但是却能直接写出可以work的代码,说明他们容易上手</strong>。如果你看过一些有经验的Ruby、Scala、Emacs Lisp开发者写出来的代码,我想会得到相同的结论。有一些语言可以让写程序的人Happy,但是看的人可能就不Happy了。做事的方法不止一种,但是不是所有的人都要用那种方法去做。</p>
<p>过去的那些程序员都是<strong>真正的全栈程序员</strong>,这些程序员不仅仅做了前端的活,还做了数据库的工作。</p>
<pre><code class="sql">Set rs = Server.CreateObject("ADODB.Recordset")
sql = "select id,title,username,email,qq,adddate,content,Re_content,home,face,sex from Fl_Book where ispassed=1 order by id desc"
rs.open sql, Conn, 1, 1
fl.SqlQueryNum = fl.SqlQueryNum + 1</code></pre>
<p>在这个ASP文件里,它从数据库里查找出了数据,然后Render出HTML。如果可以看到历史版本,那么我想我会看到有一个作者将style=""的代码一个个放到css文件中。</p>
<p>在这里的代码里也免不了有动态生成JavaScript代码的方法:</p>
<pre><code class="javascript">show_other = "<SCRIPT language=javascript>"
show_other = show_other & "function checkform()"
show_other = show_other & "{"
show_other = show_other & "if (document.add.title.value=='')"
show_other = show_other & "{"</code></pre>
<p>请尽情嘲笑,然后再看一段代码:</p>
<pre><code class="javascript">import React from "react";
import { getData } from "../../common/request";
import styles from "./style.css";
export default class HomePage extends React.Component {
componentWillMount() {
console.log("[HomePage] will mount with server response: ", this.props.data.home);
}
render() {
let { title } = this.props.data.home;
return (
<div className={styles.content}>
<h1>{title}</h1>
<p className={styles.welcomeText}>Thanks for joining!</p>
</div>
);
}
static fetchData = function(params) {
return getData("/home");
}
}</code></pre>
<p>10年前和10年后的代码,似乎没有太多的变化。有所不同的是数据层已经被独立出去了,如果你的component也混合了数据层,即直接查询数据库而不是调用数据层接口,那么你就需要好好思考下这个问题。你只是在追随潮流,还是在改变。用一个View层更换一个View层,用一个Router换一个Router的意义在哪?</p>
<h4>Model-View-Controller</h4>
<p>人们在不断地反思这其中复杂的过程,整理了一些好的架构模式,其中不得不提到的是我司Martin Folwer的《企业应用架构模式》。该书中文译版出版的时候是2004年,那时对于系统的分层是</p>
<table>
<thead><tr>
<th>层次</th>
<th>职责</th>
</tr></thead>
<tbody>
<tr>
<td>表现层</td>
<td>提供服务、显示信息、用户请求、HTTP请求和命令行调用。</td>
</tr>
<tr>
<td>领域层</td>
<td>逻辑处理,系统中真正的核心。</td>
</tr>
<tr>
<td>数据层</td>
<td>与数据库、消息系统、事物管理器和其他软件包通讯。</td>
</tr>
</tbody>
</table>
<p>化身于当时最流行的Spring,就是MVC。人们有了iBatis这样的数据持久层框架,即ORM,对象关系映射。于是,你的package就会有这样的几个文件夹:</p>
<pre><code>|____mappers
|____model
|____service
|____utils
|____controller</code></pre>
<p>在mappers这一层,我们所做的莫过于如下所示的数据库相关查询:</p>
<pre><code class="java">@Insert(
"INSERT INTO users(username, password, enabled) " +
"VALUES (#{userName}, #{passwordHash}, #{enabled})"
)
@Options(keyProperty = "id", keyColumn = "id", useGeneratedKeys = true)
void insert(User user);</code></pre>
<p>model文件夹和mappers文件夹都是数据层的一部分,只是两者间的职责不同,如:</p>
<pre><code class="java">public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}</code></pre>
<p>而他们最后都需要在Controller,又或者称为ModelAndView中处理:</p>
<pre><code class="java">@RequestMapping(value = {"/disableUser"}, method = RequestMethod.POST)
public ModelAndView processUserDisable(HttpServletRequest request, ModelMap model) {
String userName = request.getParameter("userName");
User user = userService.getByUsername(userName);
userService.disable(user);
Map<String,User> map = new HashMap<String,User>();
Map <User,String> usersWithRoles= userService.getAllUsersWithRole();
model.put("usersWithRoles",usersWithRoles);
return new ModelAndView("redirect:users",map);
}</code></pre>
<p>在多数时候,Controller不应该直接与数据层的一部分,而将业务逻辑放在Controller层又是一种错误,这时就有了Service层,如下图:</p>
<p><img src="http://repractise.phodal.com/img/frontend/service-mvc.png" alt="Service MVC" title="Service MVC"></p>
<p>然而对于Domain相关的Service应该放在哪一层,总会有不同的意见:</p>
<p><img src="http://repractise.phodal.com/img/frontend/mvcplayer.gif" alt="MVC Player" title="MVC Player"></p>
<p><img src="http://repractise.phodal.com/img/frontend/ms-mvc.png" alt="MS MVC" title="MS MVC"></p>
<p>Domain(业务)是一个相当复杂的层级,这里是业务的核心。一个合理的Controller只应该做自己应该做的事,它不应该处理业务相关的代码:</p>
<pre><code class="java">if (isNewnameEmpty == false && newuser == null){
user.setUserName(newUsername);
List<Post> myPosts = postService.findMainPostByAuthorNameSortedByCreateTime(principal.getName());
for (int k = 0;k < myPosts.size();k++){
Post post = myPosts.get(k);
post.setAuthorName(newUsername);
postService.save(post);
}
userService.update(user);
Authentication oldAuthentication = SecurityContextHolder.getContext().getAuthentication();
Authentication authentication = null;
if(oldAuthentication == null){
authentication = new UsernamePasswordAuthenticationToken(newUsername,user.getPasswordHash());
}else{
authentication = new UsernamePasswordAuthenticationToken(newUsername,user.getPasswordHash(),oldAuthentication.getAuthorities());
}
SecurityContextHolder.getContext().setAuthentication(authentication);
map.clear();
map.put("user",user);
model.addAttribute("myPosts", myPosts);
model.addAttribute("namesuccess", "User Profile updated successfully");
return new ModelAndView("user/profile", map);
}</code></pre>
<p>我们在Controller层应该做的事是:</p>
<ol>
<li><p>处理请求的参数</p></li>
<li><p>渲染和重定向</p></li>
<li><p>选择Model和Service</p></li>
<li><p>处理Session和Cookies</p></li>
</ol>
<p>业务是善变的,昨天我们可能还在和对手竞争谁先推出新功能,但是今天可能已经合并了。我们很难预见业务变化,但是我们应该能预见Controller是不容易变化的。在一些设计里面,这种模式就是Command模式。</p>
<p>View层是一直在变化的层级,人们的品味一直在更新,有时甚至可能因为竞争对手而产生变化。在已经取得一定市场的情况下,Model-Service-Controller通常都不太会变动,甚至不敢变动。企业意识到创新的两面性,要么带来死亡,要么占领更大的市场。但是对手通常都比你想象中的更聪明一些,所以这时<strong>开创新的业务是一个更好的选择</strong>。</p>
<p>高速发展期的企业和发展初期的企业相比,更需要前端开发人员。在用户基数不够、业务待定的情形中,View只要可用并美观就行了,这时可能就会有大量的业务代码放在View层:</p>
<pre><code class="jsp"><c:choose>
<c:when test="${ hasError }">
<p class="prompt-error">
${errors.username} ${errors.password}
</p>
</c:when>
<c:otherwise>
<p class="prompt">
Woohoo, User <span class="username">${user.userName}</span> has been created successfully!
</p>
</c:otherwise>
</c:choose> </code></pre>
<p>不同的情形下,人们都会对此有所争议,但只要符合当前的业务便是最好的选择。作为一个前端开发人员,在过去我需要修改JSP、PHP文件,这期间我需要去了解这些Template:</p>
<pre><code class="php">{foreach $lists as $v}
<li itemprop="breadcrumb"><span{if(newest($v['addtime'],24))} style="color:red"{/if}>[{fun date('Y-m-d',$v['addtime'])}]</span><a href="{$v['url']}" style="{$v['style']}" target="_blank">{$v['title']}</a></li>
{/foreach}</code></pre>
<p>有时像Django这一类,自称为Model-Template-View的框架,更容易让人理解其意图:</p>
<pre><code class="html">{% for blog_post in blog_posts.object_list %}
{% block blog_post_list_post_title %}
<section class="section--center mdl-grid mdl-grid--no-spacing mdl-shadow--2dp mdl-cell--11-col blog-list">
{% editable blog_post.title %}
<div class="mdl-card__title mdl-card--border mdl-card--expand">
<h2 class="mdl-card__title-text">
<a href="{{ blog_post.get_absolute_url }}" itemprop="headline">{{ blog_post.title }} › </a>
</h2>
</div>
{% endeditable %}
{% endblock %}</code></pre>
<p>作为一个前端人员,我们真正在接触的是View层和Template层,但是MVC并没有说明这些。</p>
<h4>从桌面版到移动版</h4>
<p>Wap出现了,并带来了更多的挑战。随后,分辨率从1024x768变成了176×208,开发人员不得不面临这些挑战。当时所需要做的仅仅是修改View层,而View层随着iPhone的出现又发生了变化。</p>
<p><img src="http://repractise.phodal.com/img/frontend/wap.gif" alt="WAP 网站" title="WAP 网站"></p>
<p>这是一个短暂的历史,PO还需要为手机用户制作一个怎样的网站?于是他们把桌面版的网站搬了过去变成了移动版。由于网络的原因,每次都需要重新加载页面,这带来了不佳的用户体验。</p>
<p>幸运的是,人们很快意识到了这个问题,于是就有了SPA。<strong>如果当时的移动网络速度可以更快的话,我想很多SPA框架就不存在了</strong>。</p>
<p>先说说jQuery Mobile,在那之前,先让我们来看看两个不同版本的代码,下面是一个手机版本的blog详情页:</p>
<pre><code class="html"><ul data-role="listview" data-inset="true" data-splittheme="a">
{% for blog_post in blog_posts.object_list %}
<li>
{% editable blog_post.title blog_post.publish_date %}
<h2 class="blog-post-title"><a href="{% url "blog_post_detail" blog_post.slug %}">{{ blog_post.title }}</a></h2>
<em class="since">{% blocktrans with sometime=blog_post.publish_date|timesince %}{{ sometime }} ago{% endblocktrans %}</em>
{% endeditable %}
</li>
{% endfor %}
</ul></code></pre>
<p>而下面是桌面版本的片段:</p>
<pre><code class="html">{% for blog_post in blog_posts.object_list %}
{% block blog_post_list_post_title %}
{% editable blog_post.title %}
<h2>
<a href="{{ blog_post.get_absolute_url }}">{{ blog_post.title }}</a>
</h2>
{% endeditable %}
{% endblock %}
{% block blog_post_list_post_metainfo %}
{% editable blog_post.publish_date %}
<h6 class="post-meta">
{% trans "Posted by" %}:
{% with blog_post.user as author %}
<a href="{% url "blog_post_list_author" author %}">{{ author.get_full_name|default:author.username }}</a>
{% endwith %}
{% with blog_post.categories.all as categories %}
{% if categories %}
{% trans "in" %}
{% for category in categories %}
<a href="{% url "blog_post_list_category" category.slug %}">{{ category }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
{% endif %}
{% endwith %}
{% blocktrans with sometime=blog_post.publish_date|timesince %}{{ sometime }} ago{% endblocktrans %}
</h6>
{% endeditable %}
{% endblock %}</code></pre>
<p>人们所做的只是<strong>重载View层</strong>。这也是一个有效的SEO策略,上面这些代码是我博客过去的代码。对于桌面版和移动版都是不同的模板和不同的JS、CSS。</p>
<p><img src="http://repractise.phodal.com/img/frontend/mobile-web.png" alt="移动版网页" title="移动版网页"></p>
<p>在这一时期,桌面版和移动版的代码可能在同一个代码库中。他们使用相同的代码,调用相同的逻辑,只是View层不同了。但是,每次改动我们都要维护两份代码。</p>
<p>随后,人们发现了一种更友好的移动版应用——APP。</p>
<h4>APP与过渡期API</h4>
<p>这是一个艰难的时刻,过去我们的很多API都是在原来的代码库中构建的,即桌面版和移动版一起。我们已经在这个代码库中开发了越来越多的功能,系统开发变得臃肿。如《Linux/Unix设计思想》中所说,这是一个伟大的系统,但是它臃肿而又缓慢。</p>
<p>我们是选择重新开发一个结合第一和第二系统的最佳特性的第三个系统,还是继续臃肿下去。我想你已经有答案了。随后我们就有了APP API,构建出了博客的APP。</p>
<p><img src="http://repractise.phodal.com/img/frontend/mobile-app.jpg" alt="应用" title="应用"></p>
<p>最开始,人们越来越喜欢用APP,因为与移动版网页相比,其响应速度更快,而且更流畅。对于服务器来说,也是一件好事,因为请求变少了。</p>
<p>但是并非所有的人都会下载APP——<strong>有时只想看看上面有没有需要的东西</strong>。对于刚需不强的应用,人们并不会下载,只会访问网站。</p>
<p>有了APP API之后,我们可以向网页提供API,我们就开始设想要有一个好好的移动版。</p>
<h4>过渡期SPA</h4>
<p>Backbone诞生于2010年,和响应式设计出现在同一个年代里,但他们似乎在同一个时代里火了起来。如果CSS3早点流行开来,似乎就没有Backbone啥事了。不过移动网络还是限制了响应式的流行,只是在今天这些都有所变化。</p>
<p>我们用Ajax向后台请求API,然后Mustache Render出来。因为JavaScript在模块化上的缺陷,所以我们就用Require.JS来进行模块化。</p>
<p>下面的代码就是我在尝试对我的博客进行SPA设计时的代码:</p>
<pre><code class="javascript">define([
'zepto',
'underscore',
'mustache',
'js/ProductsView',
'json!/configure.json',
'text!/templates/blog_details.html',
'js/renderBlog'
],function($, _, Mustache, ProductsView, configure, blogDetailsTemplate, GetBlog){
var BlogDetailsView = Backbone.View.extend ({
el: $("#content"),
initialize: function () {
this.params = '#content';
},
getBlog: function(slug) {
var getblog = new GetBlog(this.params, configure['blogPostUrl'] + slug, blogDetailsTemplate);
getblog.renderBlog();
}
});
return BlogDetailsView;
});</code></pre>
<p>从API获取数据,结合Template来Render出Page。但是这无法改变我们需要Client Side Render和Server Side Render的两种Render方式,除非我们可以像淘宝一样不需要考虑SEO——因为它不那么依靠搜索引擎带来流量。</p>
<p>这时,我们还是基于类MVC模式。只是数据的获取方式变成了Ajax,我们就犯了一个错误——将大量的业务逻辑放在前端。这时候我们已经不能再从View层直接访问Model层,从安全的角度来说有点危险。</p>
<p>如果你的View层还可以直接访问Model层,那么说明你的架构还是MVC模式。之前我在Github上构建一个Side Project的时候直接用View层访问了Model层,由于Model层是一个ElasticSearch的搜索引擎,它提供了JSON API,这使得我要在View层处理数据——即业务逻辑。将上述的JSON API放入Controller,尽管会加重这一层的复杂度,但是业务逻辑就不再放置于View层。</p>
<p>如果你在你的View层和Model层总有一层接口,那么你采用的就是MVP模式——MVC模式的衍生(PS:为了区别别的事情,总会有人取个表意的名称)。</p>
<p>一夜之前,我们又回到了过去。我们离开了JSP,将View层变成了Template与Controller。而原有的Services层并不是只承担其原来的责任,这些Services开始向ViewModel改变。</p>
<p>一些团队便将Services抽成多个Services,美其名为微服务。传统架构下的API从下图</p>
<p><img src="http://repractise.phodal.com/img/frontend/api-gateway.png" alt="API Gateway" title="API Gateway"></p>
<p>变成了直接调用的微服务:</p>
<p><img src="http://repractise.phodal.com/img/frontend/microservices.png" alt="Micro Services" title="Micro Services"></p>
<p>对于后台开发者来说,这是一件大快人心的大好事,但是对于应用端/前端来说并非如此。调用的服务变多了,在应用程序端进行功能测试变得更复杂,需要Mock的API变多了。</p>
<h4>Hybird与ViewModel</h4>
<p>这时候遇到问题的不仅仅只在前端,而在App端,小的团队已经无法承受开发成本。人们更多的注意力放到了Hybird应用上。Hybird应用解决了一些小团队在开发初期遇到的问题,这部分应用便交给了前端开发者。</p>
<p>前端开发人员先熟悉了单纯的JS + CSS + HTML,又熟悉了Router + PageView + API的结构,现在他们又需要做手机APP。这时候只好用熟悉的jQuer Mobile + Cordova。</p>
<p>随后,人们先从Cordova + jQuery Mobile,变成了Cordova + Angular的 Ionic。在那之前,一些团队可能已经用Angular代换了Backbone。他们需要更好的交互,需要data binding。</p>
<p>接着,我们可以直接将我们的Angular代码从前端移到APP,比如下面这种博客APP的代码:</p>
<pre><code class="javascript"> .controller('BlogCtrl', function ($scope, Blog) {
$scope.blogs = null;
$scope.blogOffset = 0;
//
$scope.doRefresh = function () {
Blog.async('https://www.phodal.com/api/v1/app/?format=json').then(function (results) {
$scope.blogs = results.objects;
});
$scope.$broadcast('scroll.refreshComplete');
$scope.$apply()
};
Blog.async('https://www.phodal.com/api/v1/app/?format=json').then(function (results) {
$scope.blogs = results.objects;
});
$scope.loadMore = function() {
$scope.blogOffset = $scope.blogOffset + 1;
Blog.async('https://www.phodal.com/api/v1/app/?limit=10&offset='+ $scope.blogOffset * 20 + '&format=json').then(function (results) {
Array.prototype.push.apply($scope.blogs, results.objects);
$scope.$broadcast('scroll.infiniteScrollComplete');
})
};
})</code></pre>
<p>结果<strong>时间轴又错了</strong>,人们总是<strong>超前一个时期做错了一个在未来是正确的决定</strong>。人们遇到了网页版的用户授权问题,于是发明了JWT——Json Web Token。</p>
<p>然而,由于WebView在一些早期的Android手机上出现了性能问题,人们开始考虑替换方案。接着出现了两个不同的解决方案:</p>
<ol>
<li><p>React Native</p></li>
<li><p>新的WebView——Crosswalk</p></li>
</ol>
<p>开发人员开始欢呼React Native这样的框架。但是,他们并没有预见到<strong>人们正在厌恶APP</strong>,APP在我们的迭代里更新着,可能是一星期,可能是两星期,又或者是一个月。谁说APP内自更新不是一件坏事,但是APP的提醒无时无刻不在干扰着人们的生活,噪声越来越多。<strong>不要和用户争夺他们手机的使用权</strong></p>
<h4>一次构建,跨平台运行</h4>
<p>在我们需要学习C语言的时候,GCC就有了这样的跨平台编译。</p>
<p>在我们开发桌面应用的时候,QT有就这样的跨平台能力。</p>
<p>在我们构建Web应用的时候,Java有这样的跨平台能力。</p>
<p>在我们需要开发跨平台应用的时候,Cordova有这样的跨平台能力。</p>
<p>现在,React这样的跨平台框架又出现了,而响应式设计也是跨平台式的设计。</p>
<p>响应式设计不得不提到的一个缺点是:<strong>他只是将原本在模板层做的事,放到了样式(CSS)层</strong>。你还是在针对着不同的设备进行设计,两种没有什么多大的不同。复杂度不会消失,也不会凭空产生,它只会从一个物体转移到另一个物体或一种形式转为另一种形式。</p>
<p>React,将一小部分复杂度交由人来消化,将另外一部分交给了React自己来消化。在用Spring MVC之前,也许我们还在用CGI编程,而Spring降低了这部分复杂度,但是这和React一样降低的只是新手的复杂度。在我们不能以某种语言的方式写某相关的代码时,这会带来诸多麻烦。</p>
<h3>RePractise</h3>
<p>如果你是一只辛勤的蜜蜂,那么我想你应该都玩过上面那些技术。你是在练习前端的技术,还是在RePractise?如果你不花点时间整理一下过去,顺便预测一下未来,那么你就是在白搭。</p>
<p>前端的演进在这一年特别快,Ruby On Rails也在一个合适的年代里出现,在那个年代里也流行得特别快。RoR开发效率高的优势已然不再突显,语法灵活性的副作用就是运行效率降低,同时后期维护难——每个人元编程了自己。</p>
<p>如果不能把Controller、Model Mapper变成ViewModel,又或者是Micro Services来解耦,那么ES6 + React只是在现在带来更高的开发效率。而所谓的高效率,只是相比较而意淫出来的,因为他只是一层View层。将Model和Controller再加回View层,以后再拆分出来?</p>
<p>现有的结构只是将View层做了View层应该做的事。</p>
<p>首先,你应该考虑的是一种可以让View层解耦于Domain或者Service层。今天,桌面、平板、手机并不是唯一用户设备,虽然你可能在明年统一了这三个平台,现在新的设备的出现又将设备分成两种类型——桌面版和手机版。一开始桌面版和手机版是不同的版本,后来你又需要合并这两个设备。</p>
<p>其次,你可以考虑用混合Micro Services优势的Monolithic Service来分解业务。如果可以举一个成功的例子,那么就是Linux,一个混合内核的“Service”。</p>
<p>最后,Keep Learning。我们总需要在适当的时候做出改变,尽管我们觉得一个Web应用代码库中含桌面版和移动版代码会很不错,但是在那个时候需要做出改变。</p>
<p>对于复杂的应用来说,其架构肯定不是只有纯MVP或者纯MVVM这么简单的。如果一个应用混合了MVVM、MVP和MVC,那么他也变成了MVC——因为他直接访问了Model层。但是如果细分来看,只有访问了Model层的那一部分才是MVC模式。</p>
<p>模式,是人们对于某个解决方案的描述。在一段代码中可能有各种各样的设计模式,更何况是架构。</p>
<p>原文: <a href="https://link.segmentfault.com/?enc=yXjW4NnNgH%2F1eNpTjoq%2FBg%3D%3D.HZoubsuIMz3SC7C6uQKwyaXwF%2BvzPYSD%2Biagbiram5NtclPGm3J%2BbrjUw8BxZc2tB1CdRXaMjUKg39YQckaJ21BA3L3bsa6kYZXZn9zWxGY%3D" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=ENVx%2BeCS0fznMxP6nByoLg%3D%3D.YYWdidzRiPKFytMHrFLd1%2BvIKEduvYdZQykIqskRemnFWRrn%2FfUVK4EZMePSR%2BxFuHioCiiJ7ydl7xkc0bzCeiwVsz7jWPxpmCsBEPELGm4%3D" rel="nofollow">https://github.com/phodal/repractise/blob/gh-pages/chapters/frontend.md</a></p>
<p>关注微信公众号:</p>
<p><img src="http://repractise.phodal.com/img/frontend/baimizhou.jpg" alt="Baimizhou" title="Baimizhou"></p>