该材料的作者(我们今天将其翻译发表)希望分享一个有关他使用哪些技术快速开发Web应用程序原型的故事。 这些技术包括Fastify和Preact库。 他还使用htm库。 它可以轻松地与Preact集成在一起,并且可以使用类似于JSX的直观结构来描述DOM元素。 同时,不需要像Babel这样的编译器来处理它。 在演示了原型开发工具和使用它的方法之后,该材料的作者将展示如何将此类应用程序打包到Docker容器中。 这样可以很容易地向感兴趣的每个人演示应用程序。

开始
几周前,当我需要创建一个非常简单的原型Web应用程序时,我开始使用上述一组技术,旨在与同事一起测试一些假设。
我的实验非常成功。 我能够很快地创建原型,我的同事们能够方便地进行试验,他们能够快速表达出对原型的印象。 同时,即使未在计算机上安装Node.js和NPM,他们也可以测试项目。
所有这些使我想到了我应该编写有关我的Web应用程序快速原型设计方法的材料。 这种方法可能对其他人有用。 对于已经熟悉Fastify和Preact的人,我将立即概述最重要的事情,这将使他们立即将我的想法付诸实践。
主要思想
如果您已经熟悉Fastify和Preact,并且想了解如何组织基于这些技术的项目开发,那么您实际上要走几步。 即,我们正在谈论以下命令:
git clone https://github.com/lmammino/fastify-preact-htm-boilerplate.git my-new-project cd my-new-project rm -rf .git npm install
当然,您可以将项目名称
my-new-project
更改为您的项目名称。
安装完所需的所有内容之后,就可以开始进行项目了。 即,我们正在谈论以下内容:
src/ui
文件夹包含应用程序客户端部分的文件(此处使用Preact和htm)。src/server
文件夹包含与应用程序服务器端相关的文件(此处使用Fastify)。
通过编辑适当的文件,您可以运行项目:
npm start
之后,您可以通过在浏览器中转到
localhost:3000
地址来对其进行测试。
还有一件事。 如果您喜欢我的开发,我将非常感谢
GitHub上的明星。
现在,让我们看一下这里使用的技术以及使用它们的功能。
固定
Fastify是用于Node.js的快速,经济的Web框架。 这个项目最初是由两个程序员创建的。 现在,该项目的团队有10个人,有130多人帮助开发了该项目,他在GitHub上收集了近10,000个星星。
Fastify受诸如Express和Hapi之类的Node.js框架的影响,这种框架已经存在了很长时间。 它最初旨在提高生产力,为程序员提供便利并借助插件来扩展其功能。 顺便说一下,这是我最喜欢的Fastify功能之一。
如果您不熟悉Fastify框架或想更好地了解它,我可以推荐其官方
文档 。
应当指出,我与Fastify有关。 我是主要开发团队的成员,主要负责支持项目站点并处理其文档。
事前
Preact是一个用于开发Web项目用户界面的库,由一个人创建,它是React的紧凑而快速的替代品。 事实证明,该项目非常成功,现在整个开发团队都参与其中,他在GitHub上获得了20,000多颗星。
我喜欢Preact的原因之一是该库具有用于描述应用程序可视组件的可扩展层。 在正常情况下,可以结合使用JSX和Babel来使用该库来翻译代码,但是,如果您不想安装Babel并配置应用程序构建过程,则可以将Preact与
htm库一起使用,后者使用模板文字,并且不需要启动在现代浏览器中使用的项目时进行编译。
在本文中,我们将使用htm库,并将很快考虑一些示例。
项目概况
在这里,我们介绍了创建项目的整个过程。 我们的目标是开发一个简单的Web应用程序,在启动时在服务器上显示有关时间的信息。 在这里,为了使我们的工作更加明确。
浏览器中的应用这是一个单页应用程序(SPA),其中Preact和htm用于形成其客户端部分,而Fastify用于创建旨在接收服务器时间的API。
细心的读者可能会注意到上图中显示的页面上有一个漂亮的图标图标。 是的,他在那里很小,所以对于那些想睁大眼睛看看的人,我将为他提供帮助。 这是它的放大版本。
网站图标设置应用程序的服务器端
让我们从创建一个新文件夹开始:
mkdir server-time cd server-time
现在初始化NPM项目并安装Fastify:
npm init -y npm i --save fastify@next fastify-static@next fastify-cli
请注意,在描述某些依赖
@next
时,我使用了
@next
构造。 这样做是为了确保项目使用Fastify 2库,该库当前处于候选发布状态,但很快将成为主要的稳定版本。
请注意,您还可以使用
fastify-cli
命令行
fastify-cli
基于Fastify创建一个新项目:
npx fastify-cli generate server-time
在撰写本文时,该团队创建了一个旨在使用Fastify 1.x的项目,但是很快在Fastify 2发布之后,该工具将得到更新。
让我们分析一下已安装的软件包:
fastify
是框架的核心组件。fastify-static
是一个附加插件,可让您方便地通过Fastify服务器提供静态文件。fastify-cli
是一个命令行工具,可让您基于Fastify创建项目。
目前,我们准备创建基于Fastify的API。 因此,让我们将服务器代码放入
src/server/server.js
:
const path = require('path') module.exports = async function(fastify, opts) { // `src/ui` fastify.register(require('fastify-static'), { root: path.join(__dirname, '..', 'ui'), }) // API fastify.get('/api/time', async (request, reply) => { return { time: new Date().toISOString() } }) }
我相信上面的代码可以很好地说明自己,但是有一些有趣的细节值得一谈。 这对于没有Fastify经验的人尤其有用。
在此代码中需要注意的第一件事是此处使用了
async
关键字。 Fastify支持异步/等待样式开发和更传统的回调方法。 究竟要选择什么取决于特定开发人员的偏好。
另一个有趣的细节是,我们在这里将服务器定义为导出模块。 该模块(在Fastify术语中称为“插件”)是一个功能,它以Fastify的实例(
fastify
)和一组选项(
opts
)作为参数。 在模块声明中,我们可以使用
fastify
实例注册插件。 这正是
fastify-static
插件发生的情况。 我们还可以使用诸如
fastify.get
和
fastify.post
类的特殊方法来描述HTTP端点。
尽管看起来有点不寻常,但此处使用的模块化方法具有其优势。 首先,应注意,它允许您组合多个服务器。 想象一下,您已经创建了一个用于博客服务的服务器,以及一个用于论坛的服务器。 通过将它们附加到
/blog
和
/forum
类的路径,可以轻松地将它们集成到现有应用程序中。
此外,这种方法允许您从服务器绑定(例如套接字绑定)中抽象应用程序和子应用程序,将解决方案传递给根应用程序或
fastify-cli
。
使用
fastify
命令行
fastify
启动服务器:
node_modules/.bin/fastify start --log-level info src/server/server.js
为了简化我们的生活,我们可以将此命令添加到
package.json
文件的
scripts
部分:
{ "scripts": { "start": "fastify start --log-level info src/server/server.js" } }
在实际启动服务器之前,我们需要确保有一个静态资源将位于其中的文件夹。 否则,
fastify-static
将
fastify-static
错误。 创建此文件夹:
mkdir src/ui
现在,我们可以使用
npm start
命令启动应用程序,并使用浏览器导航到
localhost:3000/api/time
。
如果一切正常,在浏览器中,您将看到类似以下内容:
{ "time": "2019-02-17T19:32:03.354Z" }
此时,您可以欣赏Fastify的另一个不错的功能。 事实是,在某些路由返回对象的情况下,会自动应用JSON序列化。
现在,有关服务器API的工作已完成。 让我们有一个前端。
前端设定
与前端相关的所有项目代码都位于
src/ui
文件夹中。 它将包含5个文件:
- app.js-精确的应用程序代码。
bootstrap.min.css
用于设置应用程序样式的CSS代码(直接从Bootstrap框架获取)。favicon.ico
-favicon文件。 如果您正在开发一个认真的应用程序,那么没有一个良好的favicon文件就无法做。index.html
是我们的一页应用程序的主要HTML文件。preacthtm.js
-Preact和htm库的代码。
首先,将文件放置在文件夹中,它们是样式,库和图标图标:
curl "https://unpkg.com/htm@2.0.0/preact/standalone.js" > src/ui/preacthtm.js curl "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" > src/ui/bootstrap.min.css curl "https://github.com/lmammino/fastify-preact-htm-boilerplate/blob/master/src/ui/favicon.ico?raw=true" > src/ui/favicon.ico
现在创建
src/ui/index.html
文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <!-- Bootstrap CSS --> <link rel="stylesheet" href="/bootstrap.min.css" /> <title>My awesome server time</title> </head> <body> <div id="app"></div> <!-- JavaScript --> <script src="/preacthtm.js"></script> <script src="/app.js"></script> </body> </html>
我们面前有一个非常普通的HTML页面,通过该页面我们可以加载所有资源(CSS和JS),并使用标识符
app
创建一个空的
<div>
元素,我们将在项目执行期间将应用程序输出到其中。
现在看一下应用程序代码,它应该在
src/ui/app.js
:
/* htmPreact */ const { html, Component, render } = htmPreact class App extends Component { componentDidMount() { this.setState({ loading: true, time: null }) fetch('/api/time') .then(response => response.json()) .then(data => this.setState({ loading: false, time: data.time })) } render(props, state) { return html` <div class="container mt-5"> <div class="row justify-content-center"> <div class="col"> <h1>Hello from your new App</h1> <div> ${state.loading && html` <p>Loading time from server...</p> `} ${state.time && html` <p>Time from server: <i><font color="#999999">${state.time}</font></i> </p> `} </div> <hr /> <div> Have fun changing the code from this boilerplate: <ul> <li>UI code available at <code>/src/ui</code></li> <li>Server-side code available at <code>/src/server</code></li> </ul> </div> </div> </div> </div> ` } } render( html` <${App} /> `, document.getElementById('app') )
此应用程序中只有一个有状态的组件,称为
App
。 该组件的状态包括2个变量:
loading
是一个逻辑变量,用于指示是否正在某个特定时间点执行对服务器API的请求,以获得有关服务器时间的信息。time
一个字符串,其中包含从服务器接收的最新时间信息。
如果您熟悉React,那么您可以轻松理解以上代码。
使用Preact和htm,我们可以通过声明扩展内置
Component
类的类来创建组件。
在此类中,我们可以使用生命周期方法(例如
componentDidMount()
来描述组件的行为,还可以使用行为类似于React中常规
render()
方法的方法。
在我们的例子中,将组件附加到页面上(
componentDidMount()
方法),我们就设置状态
loading
属性并使用
fetch
执行API请求。
请求完成后,我们设置
time
状态属性的值,并将
loading
属性重置为
false
。
每当组件状态更改或向其传递新属性时,都会自动调用
render()
方法。 在这种方法中,我们使用htm描述DOM组件。
htm库允许您使用带有特殊标记
html
标记模板文字来描述DOM节点。 在我们的模板文字中,可能会出现动态表达式,就像我们用来检查状态并决定如果应用程序正在从服务器加载数据以及是否已经在数据中显示的内容那样,已加载。
还值得注意的是,我们需要创建该应用程序的实例并将其显示在HTML页面上。 这是通过使用
htmPreact
全局对象的
render()
函数完成的。
现在,前端应用程序上的工作已完成。 您可以重新启动服务器,转到
localhost:3000
并尝试使用我们刚刚创建的内容。 例如,您可以在此应用程序的基础上开发自己的应用程序。 而且,当您构建的内容看起来足以将其展示给他人时,对于将应用程序打包到Docker容器中可能很有用。
应用程序容器化
我相信向他人展示您的新小型项目的最佳方法是为此目的使用Docker的功能。
多亏了Docker,任何试图在家中运行您的应用程序的人都无需考虑是否已安装了适当版本的Node.js和NPM,因此,他无需下载应用程序源代码即可通过输入正确的命令顺序来保重,安装其依赖项并启动服务器。
为了将应用程序打包到Docker容器中,我们需要在项目的根文件夹中创建一个非常简单的
Dockerfile
:
FROM node:11-alpine WORKDIR /app COPY . /app RUN npm install --production EXPOSE 3000 CMD ["npm", "start"]
在这里,我们描述以下操作:
- 该映像是基于基于Alpine Linux构建的Node.js 11映像创建的。
- 当前文件夹中的所有内容都会复制到容器的
/app
文件夹中。 - 之后,我们运行
npm install
命令下载并安装依赖项。 使用--production
标志会导致这样的事实,即只会安装生产中部署项目所需的依赖项。 如果项目使用许多开发依赖项,则可以加快图像创建速度。 - 我们指示容器应具有开放孔3000,默认情况下,服务器将在该孔上工作。
- 最后,我们描述了一个命令
npm start
,它将在容器启动时执行。 她启动该应用程序。
为了收集容器的图像,请执行以下命令:
docker build -t server-time .
几秒钟后,映像应已准备就绪,您应该能够启动容器:
docker run -it -p 3000:3000 server-time
-p
允许您配置容器端口3000和本地端口3000之间的连接。这将允许您访问位于
localhost:3000
的容器化应用程序。
现在,您可以与他人共享您的应用程序了。 为了在Docker环境中运行它,只要在计算机上安装了Docker,就足以在其文件夹中执行上述两个命令。
总结
在本文中,我们讨论了如何使用Fastify和Preact创建一个用于快速开发Web应用程序的环境。 此外,我们讨论了如何使用Docker与其他人共享应用程序。
如上所述,建议的工具是为快速原型设计的,因此现在您可能想知道开发实际应用程序时缺少什么。 最有可能的是,当您提到“真实的应用程序”时,是指以下功能:
- 应用程序前端部分的资源汇编:可能使用Webpack,Babel或其他工具来创建优化的文件(捆绑包)。
- 在应用程序的前端进行路由。
- 服务器渲染
- 永久数据存储方式。
开发实际应用程序的所有这些可能性尚未添加到此处讨论的技术集合中,因此,现在我将其视为开发原型的工具。 我敢肯定,如果您喜欢自己所看到的内容,并且将所有这些内容视为将来解决实际问题的应用程序的基础,则可以轻松找到所需内容并创建可用于Fastify和Preact的应用程序。生产发布。
亲爱的读者们! 您如何原型化Web应用程序?
