Init.js:全栈 JavaScript 的原因和方法指南
已发表: 2022-03-11故事
所以,你和你的联合创始人有这个伟大的创业点子,对吧?
您一直在脑海中添加功能。
通常,您会询问潜在客户的意见,他们都很喜欢。
好的,所以人们想要它。 甚至还有一些钱可以赚。 他们不能拥有它的唯一原因是因为您还没有实现它。
所以最后,有一天你坐下来说:“让我们开始吧!” 很快,您将尝试弄清楚如何实现应用程序的业务逻辑,这是推动产品向前发展的杀手级功能:您知道如何去做,并且知道自己可以做到。
“完毕! 有用!” 你说。 您的概念证明是成功的! 剩下的就是将其打包成一个 Web 应用程序。
“好吧,让我们创建网站”,你说。
然后,您意识到一个事实:您需要选择一种编程语言; 您需要选择一个(现代)平台; 您需要选择一些(现代)框架; 您需要配置(和购买)存储、数据库和托管服务提供商; 你需要一个管理界面; 你需要一个权限系统; 你需要一个内容管理器。
您需要做出数十个架构决策。 你想做出正确的:你想使用允许快速开发、不断迭代、最大效率、速度、稳健性等的技术。 你想要精益,你想要敏捷。 您希望使用能够帮助您在短期和长期内取得成功的技术。 而且它们并不总是容易挑选出来的。
“我不知所措,”你说,因为你感到不知所措。 你的能量和以前不一样了。 你试图把事情拼凑起来,但工作量太大了。
你的概念证明慢慢枯萎和死亡。
提案
在以这种方式自己放弃了大量的想法之后,我决定设计一个解决方案。 我将其称为“Init”项目(或 init.js)。
这个想法的核心是有一个单一的项目来启动它们,让开发人员或技术创始人一次做出所有这些重要的决定,并根据这些决定获得一个合适的启动模板。 我知道批评者会说什么,“一个解决方案不能适用于所有问题”(仇恨者会讨厌)。 他们可能是对的。 但是我们可以尽最大努力创建一个近似的解决方案,我认为 Init 非常接近。
为了最好地实现这一目标,我们必须牢记一些关键思想。 在开发 Init 时,我考虑过:
成分
组件化是任何系统的一个关键特征,因为它允许您在不同的项目中重用软件组件——这是 Init 的主要目标。 但是组件化也带来了一个副产品,“可替换性”,这将是我们用“几乎”相同的解决方案解决几个不同问题的最佳盟友。
易于开发
有些问题,在某个地方有一个最好用 Brainf*ck 编写的解决方案。 但是实现该解决方案(在 Brainfuck 中)几乎不可能编写,更不用说阅读了。 这将花费您的时间和大量的精力。 一般来说,您应该使用使开发更容易的语言和平台,而不是让您(或以后可能从事开发工作的任何人)更难。
社区
无论您选择什么平台,请确保它有一个庞大的社区,并且可以帮助您解决最常见和不常见的问题。 记住:jQuery 可能不是最快、最干净或最优雅的库——但仅仅因为它的社区而成为赢家。
牢记这些目标,接下来我将向您展示我是如何在创建 Init 时做出自己的决定的。
在其核心,Init 利用了“全栈 JavaScript”范式(有些人将它或它的一个子集称为 MEAN Stack)。 通过使用这样的堆栈,Init 能够只使用一种语言,同时为开发 Web 应用程序创建一个极其灵活且功能齐全的环境。 简而言之,Init 让您不仅可以将 JavaScript 用于客户端和服务器开发,还可以用于构建、测试、模板等。
但是让我们放慢脚步,问问自己:使用 JavaScript 真的是个好主意吗?
为什么我选择 JavaScript
自 1998 年以来,我一直是一名 Web 开发人员。当时我们使用 Perl 进行大部分的服务器端开发,但即使从那时起,我们也已经在客户端使用了 JavaScript。 从那时起,Web 服务器技术发生了巨大的变化:我们经历了一波又一波的语言和技术,例如 PHP、AP、JSP、.NET、Ruby、Python 等等。 开发人员开始意识到在客户端和服务器环境中使用两种不同的语言会使事情变得复杂。 在单一语言下统一的最初尝试试图在服务器上创建客户端组件并将它们编译为 JavaScript。 这并没有按预期工作,并且大多数项目都失败了(例如:ASP MVC 替换 ASP.NET Web 表单,并且 GWT 可能会在不久的将来被 Polymer 替换)。 但它本质上是一个好主意:客户端和服务器上的单一语言,允许我们重用组件和资源(这就是关键字:资源)。
答案很简单:将 JavaScript 放在服务器上。
JavaScript 实际上是在 Netscape Enterprise Server 中与 JavaScript 服务器端一起诞生的,但当时这种语言还没有准备好。 经过多年的反复试验,Node.js 终于出现了,它不仅将 JavaScript 放在了服务器上,还推动了非阻塞编程的思想,永远改变了我们编写“fread”(I/O)的方式(阅读这里更多)。
但这些想法并不新鲜——那么为什么它们会在 Node.js 中如此流行呢? 可以通过多种方式实现简单的非阻塞编程。 也许最简单的是使用回调和事件循环。 在大多数语言中,这不是一件容易的事:虽然“回调”是其他一些语言的常见功能,但事件循环不是,而且您经常发现自己在与外部库(例如:Python、Tornado)搏斗。 但是在 JavaScript 中,回调和事件循环一样内置在语言中,几乎每个涉足 JavaScript 的程序员都熟悉它们(或者至少使用过它们,即使他们不太了解事件是什么)循环是)。 突然间,地球上的每家初创公司都可以在客户端和服务器端重用开发人员(即资源),从而解决“需要 Python Guru”的职位发布问题。
所以现在我们有了一个非常快的平台(感谢非阻塞编程)和一种非常容易使用的编程语言(感谢 JavaScript)。 但够了吗? 它会持续吗? 我确信 JavaScript 将在未来占有重要的位置。 让我来告诉你为什么:
函数式编程
JavaScript 是第一个将函数式范式推向大众的编程语言(当然,Lisp 最先出现,但大多数程序员从未使用 Lisp 构建过可用于生产的应用程序)。 Javascript 的主要影响 Lisp 和 Self 充满了创新的想法。 这些想法可以解放我们的思想来探索新技术、模式和范式。 它们都延续到 JavaScript。 看看 monads、Church numbers,甚至(举个更实际的例子)Underscore.js 的集合函数,它可以为你节省一行又一行的代码。
动态对象和原型继承
没有类的面向对象编程(并且没有无穷无尽的类层次结构)允许快速开发(创建对象、添加方法和使用它们),但最重要的是,通过允许程序员修改对象实例来减少维护任务期间的重构时间的类。 这种速度和灵活性为快速发展铺平了道路。
JavaScript 是互联网
JavaScript 是为 Internet 设计的,它从一开始就存在,并且不会消失。 所有破坏它的尝试都失败了:例如,Java Applets 的衰落,VBScript 被微软的 TypeScript(编译成 JavaScript)取代,以及 Flash 在移动市场和 HTML5 手中的消亡。 在不破坏数百万网页的情况下更换 Javascript 是不可能的,因此我们的目标应该是改进它。 没有人比 ECMA 的 Technical Committee 39 更适合这项工作了。
好的,JavaScript 的替代品每天都在诞生,比如 CoffeeScript、TypeScript 和数百万种编译成 JavaScript 的语言。 这些替代方案可能对开发阶段有用(通过源映射),但从长远来看,它们将无法取代 JavaScript,原因有两个:它们的社区永远不会变得更大,它们的最佳功能将被 ECMA 脚本采用(即 JavaScript )。 JavaScript 不是一种汇编语言:它是一种高级编程语言,具有您可以理解的源代码——所以您应该理解它。
端到端 JavaScript:Node.js 和 MongoDB
因此,这些就是使用 JavaScript 的原因。 现在,我将使用 JavaScript 作为使用 Node.js 和 MongoDB 的理由。
节点.js
Node.js 是一个用于构建快速且可扩展的网络应用程序的平台——这几乎就是 Node.js 网站所说的。 但 Node.js 不仅如此:它是任何具有 I/O 访问权限的 JavaScript 应用程序的首选运行时环境。 即使您不打算使用 Node.js 编写主服务器应用程序,您也可以使用构建在 Node.js 之上的工具来改进您的开发过程。 例如:用于单元测试的 Mocha.js,用于自动构建任务的 Grunt.js,甚至用于全文代码编辑的括号。
因此,如果您要为服务器或客户端编写 JavaScript 应用程序,您应该查看一些 Node.js 示例,因为您每天都需要并使用它。 有一些有趣的替代方案,但它们都没有占到 Node.js 社区的 10%。
MongoDB
MongoDB 是一个基于 NoSQL 文档的数据库,它使用 JavaScript 作为其查询语言,让我能够完成端到端的 JavaScript 平台。 但这甚至不是选择这个数据库的主要原因。
MongoDB 是一个无模式数据库,它允许您以灵活的方式持久化对象,从而更快地适应需求的变化。 此外,它具有高度可扩展性和基于 map-reduce 的特性,使其适用于大数据应用程序。 MongoDB 非常灵活,它可以用作无模式文档数据库、关系数据存储(尽管它缺少事务),甚至可以用作缓存响应的键值存储。
使用 Express.js 进行服务器组件化
服务器端组件化绝非易事。 但是随着 Express.js(和 Connect.js)的出现,“中间件”的概念出现了。 在我看来,中间件是在服务器上定义组件的最佳方式。 如果您想将其与已知模式进行比较,它非常接近管道和过滤器。

基本思想是您的组件是管道的一部分。 管道处理请求(输入)并生成响应(输出),但您的组件不负责整个响应。 相反,它只修改它需要的内容,然后委托给管道的下一部分。 当管道的最后一段完成处理时,将响应发送回客户端。
我们将这些“管道片段”称为“中间件”。 显然,我们可以创建两种中间件:
中间件:处理请求和响应的那些,但不完全负责响应本身,因此他们委托给下一个中间件。
决赛:对最终反应负有全部责任的人。 他们处理和修改请求和响应,但不需要委托给下一个中间件。 在实践中,建议您委托给下一个中间件以实现架构灵活性(即,稍后添加更多中间件),即使该中间件不存在(在这种情况下,响应将直接发送到客户端)。
作为一个具体的例子,考虑服务器上的“用户管理器”组件。 就中间件而言,我们会有决赛和中间件。 对于我们的决赛,我们将具有创建用户和列出用户等功能。 但在我们可以执行这些操作之前,我们需要我们的中间体进行身份验证(因为我们不希望未经身份验证的请求进入并创建用户)。 一旦我们创建了这些身份验证中间体,我们就可以将它们插入任何我们想要将以前未经身份验证的功能转换为经过身份验证的功能的地方。
单页应用程序
Init 项目专注于创建单页应用程序 (SPA)。 大多数 Web 开发人员都不止一次地尝试过 SPA。 我已经构建了几个(大部分是专有的),我可以自信地说它们只是 Web 应用程序的未来。 您是否曾经将 SPA 与移动连接上的常规 Web 应用程序进行比较? 响应能力的差异在几十秒的数量级上。
SPA 是 Web 的未来——那么您为什么要以传统形式构建您的产品呢? 我听到的一个常见论点是人们担心 SEO。 但如果你处理得当,这应该不是问题:谷歌本身有一个非常好的教程,关于如何做到这一点,这里也有一些很好的评论。
带有 Backbone.js、Marionette.js 和 Twitter Bootstrap 的客户端 MV*
关于 SPA 的 MVC* 框架已经谈了很多。 这是一个艰难的选择,但我想说前三名是 Backbone.js、Ember.js 和 Angular.js。
这三个都非常受欢迎。 但是哪一个最适合你?
不幸的是,我必须承认我对 Angular.js 的经验非常有限,所以我将把它排除在讨论之外(有关更多信息,请参阅 Angular.js 教程)。 现在,Ember.js 和 Backbone.js 代表了解决同一问题的两种不同方式。
Backbone.js 是最小的、简单的,并且为您提供了足够创建一个简单的 SPA。 另一方面,Ember.js 是一个用于创建 SPA 的完整且专业的框架。 它有更多的花里胡哨,但也有更大的学习曲线。
根据您的应用程序的大小,决定可以像查看 featuresUsed/featuresAvailable 比率一样简单,这会给您一个很大的提示。
在 Init 的情况下,我想涵盖大多数场景,所以我选择 Backbone.js 来轻松创建 SPA,而 Backbone.Marionette.View 用于组件化。 这样,每个组件都是一个简单的应用程序,最终的应用程序可以像我们想要的那样复杂。
造型也是一个挑战,但我们可以再次依靠框架来拯救我们。 对于 CSS,没有比 Twitter Bootstrap 更好的了,它提供了一整套开箱即用且易于定制的样式。
Bootstrap 是使用 LESS 语言创建的,它是开源的,因此我们可以根据需要对其进行修改。 它带有大量的 UX 控件,这些控件在 Bootstrap 站点上有详细记录。 此外,还有一个自定义模型,可让您创建自己的模型。 这绝对是这份工作的人选。
最佳实践:Grunt.js、Mocha.js、Chai.js、RequireJS 和 CoverJS
最后,我们应该定义一些我们的最佳实践,并看看 Init 如何帮助您实现和维护它们。 我们的解决方案以几个基于 Node.js 本身的工具为中心。
Mocha.js和Chai.js :
这些工具允许您通过应用 TDD 或 BDD 来改进开发过程,提供组织单元测试的基础设施和自动运行它们的运行器。
有数以千计的 JavaScript 单元测试框架。 那么为什么要使用 Mocha.js? 简短的回答:它灵活且完整。
长答案:它有两个重要的特性(界面、报告器)和一个重要的缺席(断言)。 让我解释。
接口:也许您习惯于套件和单元测试的 TDD 概念,或者您更喜欢带有“描述”和“应该”的行为规范的 BDD 理念。 Mocha.js 允许您使用这两种方法。
Reporters :运行您的测试将生成结果报告,您可以使用各种报告器格式化这些结果。 例如,如果您需要为持续集成服务器提供数据,您可以找一个报告员来做这件事。
缺少断言库:Mocha.js 远非问题,旨在让您使用您选择的断言库,为您提供更大的灵活性。 有很多选择,但这就是 Chai.js 发挥作用的地方。
Chai.js 是一个灵活的断言库,可让您使用三种主要断言样式中的任何一种:
Assert :来自 TDD 老派的经典断言风格。 例如:
assert.equal(variable, ”value”);
Expect :可链接的断言样式,最常用于 BDD。 例如:
expect(variable).to.equal(“value”);
应该:也用在 BDD 中,但我更喜欢期望,因为应该与行为规范“它(“应该做某事..”)'听起来重复。 例如:
variable.should.equal(“value”);
Chai.js 与 Mocha.js 完美结合。 仅使用这两个库,您就可以使用 TDD、BDD 或任何可以想象的样式编写测试。
咕噜声:
Grunt.js 允许您自动执行构建任务,从简单的复制粘贴和文件连接到模板预编译、样式语言(即 SASS 和 LESS)编译、单元测试(使用 mocha.js)、linting 和代码缩小(例如,使用 UglifyJS 或 Closure Compiler)。 您可以将自己的自动化任务添加到 Grunt,或者搜索 Grunt 注册表,那里有成百上千的插件可用(同样,使用具有强大社区的工具是值得的)。 Grunt 还可以监视您的文件并在修改文件时触发操作。
要求:
RequireJS 可能听起来像是用 AMD 加载模块的另一种方式,但我可以向您保证,它远不止于此。 要理解为什么,我们首先需要提到模块命名空间的概念(例如,demo.views.hello),它通过将每个模块包装在自己的命名空间中来避免污染全局命名空间。 问题是,这些模块是不可重用的:如果你修改一个“实例”的命名空间,你正在修改所有“实例”的命名空间。 与此相反,RequireJS 允许您从一开始就定义可重用的模块。 (此外,它将帮助您采用依赖注入以避免让您的模块访问全局变量。)
封面JS :
代码覆盖率是评估测试的指标。 顾名思义,它告诉您当前的测试套件覆盖了多少代码。 CoverJS 通过在代码中检测语句(而不是像 JSCoverage 之类的代码行)并生成代码的检测版本来衡量测试的代码覆盖率。 它还可以生成报告以提供给您的持续集成服务器。
使用分支切换功能
当我启动 Init 时,我需要一种方法让用户激活和停用他们可能在项目中需要的各种功能。 我决定对 git 的分支系统采取激进的方法来实现这个功能。
本质上,每个分支都代表用户可能想要包含的特性或功能。 如果您从头开始一个项目,请从您需要的最小分支开始,然后通过与所需的分支合并来添加其他技术。 例如,假设您想使用 Backbone.js 和 Marionette.js 开始您的项目。 好吧,您可以从 Backbone.js 分支开始,并将其与 Marionette 分支合并,继续添加您想要添加的每一个功能。
目前,这种合并以添加功能的想法只能用于技术模板(例如,Backbone、Node、Express)。 但在未来,您将能够在后端(例如,从 MongoDB 到 Postgres)和客户端实现之间切换。
使用 Init 启动项目并立即部署到 Heroku
从未有过更简单的方式来启动项目。 只需前往 GitHub 存储库,检查具有最新提交的分支(现在是用户管理器,尽管将来可能会改变)然后:
- 为您的项目创建目录(或使用现有目录)。
- 使用“git init”创建您的存储库(或使用现有存储库)。
使用 init 添加遥控器
git remote add init git://github.com/picanteverde/init.git
获取你想要的分支
git pull init usermanager
获取 Heroku 进程文件
git pull init heroku-webprocess
安装 Heroku Toolbelt 后,创建一个 Heroku 应用程序
heroku create
将你的 master 分支推送到 Heroku
git push heroku master
- 访问您的应用,在 Heroku 上启动并运行!
现在您只需几行代码即可开始开发您的杀手级功能。 不仅如此,您还将在尽可能自动化的开发套件中使用最新、最高效的技术进行开发。
我希望您可以使用 Init 来启动您的下一个大创意。 请记住检查 Init 存储库以获取新的修复和功能 - 它的开发非常活跃,我期待听到您的反馈。