web服务器和web客户端
No, I’m not just talking about server-side rendered web components. I’m talking about web components that you can use to build servers.
不,我不仅在谈论服务器端渲染的Web组件。 我说的是可用于构建服务器的Web组件。
As a refresher, web components are a set of proposed standards that offer a way to modularize and package UI and functionality into reusable and declarative components that can be easily shared and composed to create entire applications. Currently their greatest use has been in front-end web development. What about the back-end? It turns out that web components are not only useful for UI, as the Polymer project has shown us, but they are also useful for raw functionality. We'll look at how these can be used, and talk about the key benefits:
作为复习,Web组件是一组提议的标准 ,它们提供了一种将UI和功能模块化并将其打包到可重用和声明性组件中的方法,这些组件可以轻松共享并组成整个应用程序。 当前,它们最大的用途是在前端Web开发中。 后端呢? 事实证明,Web组件不仅如Polymer项目向我们展示的那样对UI有用,而且对原始功能也有用。 我们将研究如何使用它们,并讨论其主要优点:
- Declarative 陈述式
- Modular 模块化的
- Universal 普遍
- Shareable 可共享的
- Debuggable 可调试的
- Smaller Learning Curve 较小的学习曲线
- Client-side Structure 客户端结构
If you would like to stay in touch as the project progresses, join the email list.
如果您想在项目进行过程中保持联系,请加入电子邮件列表 。
##Declarative
##声明性
First off, you get declarative servers. Here’s a quick sample of an Express.js application, written with Express Web Components:
首先,您将获得声明式服务器。 这是用Express Web Components编写的Express.js应用程序的快速示例:
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/express-web-components/express-app.html">
<link rel="import" href="bower_components/express-web-components/express-middleware.html">
<link rel="import" href="bower_components/express-web-components/express-router.html">
<dom-module id="example-app">
<template>
<express-app port="5000">
<express-middleware method="get" path="/" callback="[[indexHandler]]"></express-middleware>
<express-middleware callback="[[notFound]]"></express-middleware>
</express-app>
</template>
<script>
class ExampleAppComponent {
beforeRegister() {
this.is = 'example-app';
}
ready() {
this.indexHandler = (req, res) => {
res.send('Hello World!');
};
this.notFound = (req, res) => {
res.status(404);
res.send('not found');
};
}
}
Polymer(ExampleAppComponent);
</script>
</dom-module>
Instead of writing routes purely in JavaScript imperatively, you can write them declaratively in HTML. You can actually build a visual hierarchy of your routes, which is easier to visualize and understand than the equivalent in pure JavaScript. Taking a look at the example above, all endpoints/middleware pertaining to the Express application are nested in an <express-app>
element, and middleware is hooked up to the app in the order that it is written in the HTML with <express-middleware>
elements. Routes can be nested easily as well. Every middleware inside of an <express-router>
is hooked up to that router, and you can even have <express-route>
elements inside of <express-router>
elements.
您可以用HTML声明式地编写路由,而不必纯粹用JavaScript来编写路由。 实际上,您可以构建路线的可视层次结构,与纯JavaScript中的等效结构相比,它更易于可视化和理解。 考虑看看以上,关于该Express应用程序的所有端点/中间件嵌套在一个示例中<express-app>
元件,和中间件被钩到该应用的顺序,它被写在HTML与<express-middleware>
元素。 路线也可以轻松嵌套。 <express-router>
内的每个中间件都已连接到该路由器,您甚至可以在<express-router>
元素内包含<express-route>
<express-router>
元素。
##Modular
##模块化
We already have modularity with vanilla Express and Node.js, but I feel like modular web components are even easier to reason about. Let’s take a look at a good example of modular custom elements with Express Web Components:
我们已经具有Vanilla Express和Node.js的模块化功能,但是我觉得模块化Web组件甚至更容易推理。 让我们来看一个使用Express Web Components的模块化自定义元素的好例子 :
<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<script src="../../node_modules/scram-engine/filesystem-config.js"></script>
<link rel="import" href="components/app/app.component.html">
</head>
<body>
<na-app></na-app>
</body>
</html>
Everything starts out in the index.html
file. There is really only one place to go, <na-app></na-app>
:
一切都始于index.html
文件。 实际上只有一个地方可以去<na-app></na-app>
:
<!--components/app/app.component.html-->
<link rel="import" href="../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../../bower_components/express-web-components/express-app.html">
<link rel="import" href="../../../../bower_components/express-web-components/express-middleware.html">
<link rel="import" href="../api/api.component.html">
<dom-module id="na-app">
<template>
<express-app port="[[port]]" callback="[[appListen]]">
<express-middleware callback="[[morganMW]]"></express-middleware>
<express-middleware callback="[[bodyParserURLMW]]"></express-middleware>
<express-middleware callback="[[bodyParserJSONMW]]"></express-middleware>
<na-api></na-api>
</express-app>
</template>
<script>
class AppComponent {
beforeRegister() {
this.is = 'na-app';
}
ready() {
const bodyParser = require('body-parser');
const morgan = require('morgan');
this.morganMW = morgan('dev'); // log requests to the console
// configure body parser
this.bodyParserURLMW = bodyParser.urlencoded({ extended: true });
this.bodyParserJSONMW = bodyParser.json();
this.port = process.env.PORT || 8080; //set our port
const mongoose = require('mongoose');
mongoose.connect('mongodb://@localhost:27017/test'); // connect to our database
this.appListen = () => {
console.log(`Magic happens on port ${this.port}`);
};
}
}
Polymer(AppComponent);
</script>
</dom-module>
We start up an Express app listening on port 8080
or process.env.PORT
, and then we declare three middleware, and one custom element. I'm hoping that intuition leads you to believe that those three middleware will be run before anything inside of <na-api></na-api>
, because that's exactly how it works:
我们启动一个Express应用程序,监听端口8080
或process.env.PORT
,然后声明三个中间件和一个自定义元素。 我希望凭直觉使您相信这三个中间件将在<na-api></na-api>
内的任何内容之前运行,因为这就是它的工作方式:
<!--components/api/api.component.html-->
<link rel="import" href="../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../../bower_components/express-web-components/express-middleware.html">
<link rel="import" href="../../../../bower_components/express-web-components/express-router.html">
<link rel="import" href="../bears/bears.component.html">
<link rel="import" href="../bears-id/bears-id.component.html">
<dom-module id="na-api">
<template>
<express-router path="/api">
<express-middleware callback="[[allMW]]"></express-middleware>
<express-middleware method="get" path="/" callback="[[indexHandler]]"></express-middleware>
<na-bears></na-bears>
<na-bears-id></na-bears-id>
</express-router>
</template>
<script>
class APIComponent {
beforeRegister() {
this.is = 'na-api';
}
ready() {
// middleware to use for all requests with /api prefix
this.allMW = (req, res, next) => {
// do logging
console.log('Something is happening.');
next();
};
// test route to make sure everything is working (accessed at GET http://localhost:8080/api)
this.indexHandler = (req, res) => {
res.json({ message: 'hooray! welcome to our api!' });
};
}
}
Polymer(APIComponent);
</script>
</dom-module>
Everything inside of <na-api></na-api>
is wrapped inside of an <express-router></express-router>
. All middleware in this component are available at /api
. Let's continue following to <na-bears></na-bears>
and <na-bears-id></na-bears-id>
:
<na-api></na-api>
内部的所有内容都包装在<express-router></express-router>
。 该组件中的所有中间件都可从/api
。 让我们继续关注<na-bears></na-bears>
和<na-bears-id></na-bears-id>
:
<!--components/bears/bears.component.html-->
<link rel="import" href="../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../../bower_components/express-web-components/express-middleware.html">
<link rel="import" href="../../../../bower_components/express-web-components/express-route.html">
<dom-module id="na-bears">
<template>
<express-route path="/bears">
<express-middleware method="post" callback="[[createHandler]]"></express-middleware>
<express-middleware method="get" callback="[[getAllHandler]]"></express-middleware>
</express-route>
</template>
<script>
class BearsComponent {
beforeRegister() {
this.is = 'na-bears';
}
ready() {
var Bear = require('./models/bear');
// create a bear (accessed at POST http://localhost:8080/bears)
this.createHandler = (req, res) => {
var bear = new Bear(); // create a new instance of the Bear model
bear.name = req.body.name; // set the bears name (comes from the request)
bear.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Bear created!' });
});
};
// get all the bears (accessed at GET http://localhost:8080/api/bears)
this.getAllHandler = (req, res) => {
Bear.find(function(err, bears) {
if (err)
res.send(err);
res.json(bears);
});
};
}
}
Polymer(BearsComponent);
</script>
</dom-module>
<!--components/bears-id/bears-id.component.html-->
<link rel="import" href="../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../../bower_components/express-web-components/express-middleware.html">
<link rel="import" href="../../../../bower_components/express-web-components/express-route.html">
<dom-module id="na-bears-id">
<template>
<express-route path="/bears/:bear_id">
<express-middleware method="get" callback="[[getHandler]]"></express-middleware>
<express-middleware method="put" callback="[[updateHandler]]"></express-middleware>
<express-middleware method="delete" callback="[[deleteHandler]]"></express-middleware>
</express-route>
</template>
<script>
class BearsIdComponent {
beforeRegister() {
this.is = 'na-bears-id';
}
ready() {
var Bear = require('./models/bear');
// get the bear with that id
this.getHandler = (req, res) => {
console.log(req.params);
Bear.findById(req.params.bear_id, function(err, bear) {
if (err)
res.send(err);
res.json(bear);
});
};
// update the bear with this id
this.updateHandler = (req, res) => {
Bear.findById(req.params.bear_id, function(err, bear) {
if (err)
res.send(err);
bear.name = req.body.name;
bear.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Bear updated!' });
});
});
};
// delete the bear with this id
this.deleteHandler = (req, res) => {
Bear.remove({
_id: req.params.bear_id
}, function(err, bear) {
if (err)
res.send(err);
res.json({ message: 'Successfully deleted' });
});
};
}
}
Polymer(BearsIdComponent);
</script>
</dom-module>
As you can see, all of the routers are split into their own components, and are easily included into the main app. The index.html
file is the beginning of an easy-to-follow series of imports that lead you through the flow of routes.
如您所见,所有路由器都被拆分成自己的组件,并且很容易包含在主应用程序中。 index.html
文件是一系列易于遵循的导入的开始,这些导入将引导您完成路由的流程。
##Universal
##普遍
One of the reasons I love JavaScript is the possibility of sharing code across client and server. To some extent that is possible today, but there are still client-side libraries that don't work server-side because of missing APIs, and vice-versa from the server to the client. Basically, Node.js and browsers are still different platforms offering different APIs. What if you could combine both? That’s what Electron is for. Electron combines Node.js and the Chromium project into a single runtime, allowing us to use client-side and server-side code together.
我喜欢JavaScript的原因之一是可以在客户端和服务器之间共享代码。 从某种程度上说,今天这是可能的,但是由于缺少API,仍然有一些客户端库无法在服务器端工作,反之亦然。 基本上,Node.js和浏览器仍然是提供不同API的不同平台。 如果可以将两者结合起来怎么办? 那就是电子的目的。 Electron将Node.js和Chromium项目组合到一个运行时中,从而使我们可以一起使用客户端和服务器端代码。
Scram.js is a small project that helps run Electron in a headless state, making it possible to run your server-side web components just like you would run any other Node.js application.
Scram.js是一个小型项目,可以帮助在无头状态下运行Electron,从而可以像运行其他任何Node.js应用程序一样运行服务器端Web组件。
I already have some basic apps working in a production environment using Dokku. If you’re curious, check out the Dokku Example.
我已经有一些基本的应用程序在使用Dokku的生产环境中工作。 如果您好奇,请查看Dokku示例 。
Now let me explain one of the neatest things that I've seen while developing with server-side web components. I was using this client-side JavaScript library to make some specific API requests. It became apparent that the library was compromising the credentials to our database by providing them client-side. That wasn't going to work for us. We needed to keep those credentials secure, which meant that we needed to perform the requests server-side. It could have taken a significant amount of effort to rewrite the functionality of the library to work in Node.js, but with Electron and Scram.js, I just popped the library in and was able to use it server-side without modification!
现在,让我解释一下在使用服务器端Web组件进行开发时所见过的最巧妙的事情之一。 我正在使用此客户端JavaScript库发出一些特定的API请求。 很明显,该库通过向客户端提供凭据而损害了我们数据库的凭据。 那对我们没有用。 我们需要确保这些凭据的安全,这意味着我们需要在服务器端执行请求。 重写该库的功能以使其可以在Node.js中工作可能需要花费大量的精力,但是使用Electron和Scram.js,我只是将库弹出并可以在服务器端使用它而无需修改!
I just popped the library in and was able to use it server-side without modification!
我只是将库弹出,并且可以在服务器端使用它而无需修改!
Also, I had been working on some mobile apps built with JavaScript. We were using localForage as our client-side database. The apps were being designed to communicate with each other without any central authority, using a distributed database design. I wanted to be able to use localForage in Node.js so that we could reuse our models and have things work without major modifications. We couldn’t before, but now we can.
另外,我一直在研究一些使用JavaScript构建的移动应用程序。 我们使用localForage作为客户端数据库。 这些应用程序被设计为使用分布式数据库设计,无需任何中央权限即可彼此通信。 我希望能够在Node.js中使用localForage,以便我们可以重用我们的模型并使事情无需大量修改即可工作。 我们以前没有,但是现在我们可以。
Electron with Scram.js offers access to LocalStorage, Web SQL, and IndexedDB, which makes localForage possible. Simple server-side databases!
带有Scram.js的Electron提供对LocalStorage,Web SQL和IndexedDB的访问,这使localForage成为可能。 简单的服务器端数据库!
I'm not sure how the performance will scale, but at least it’s a possibility.
我不确定性能会如何扩展,但至少有可能。
Also, now you should be able to use components like iron-ajax and my redux-store-element server-side, just like you would client-side. I’m hoping this will allow reuse of paradigms that work well on the client, and close the gap between the context-shifting that inevitably occurs when going from the client to the server.
另外,现在您应该能够像Iron-ajax和我的redux-store-element服务器端那样使用组件,就像在客户端一样。 我希望这将允许重用在客户端上运行良好的范例,并缩小从客户端到服务器时不可避免发生的上下文转换之间的差距。
##Shareable
##可共享
This benefit just comes with web components. One of the major hopes of web components is that we will be able to share them easily, use them across browsers, and stop reimplementing the same solutions over and over again, just because frameworks and libraries change. This sharing is possible because web components are based on current or proposed standards that all major browser vendors are working to implement.
Web组件仅具有此好处。 Web组件的主要希望之一是,我们将能够轻松地共享它们,在浏览器中使用它们,并不再一遍又一遍地重新实现相同的解决方案,因为框架和库会发生变化。 这种共享是可能的,因为Web组件基于所有主要浏览器供应商正在努力实现的当前或提议的标准。
That means web components don’t rely on any one framework or library, but will work universally on the platform of the web.
这意味着Web组件不依赖任何框架或库,而是可以在Web平台上通用地工作。
I'm hoping many people will create server-side web components that package functionality just like front-end components. I’ve started with Express components, but imagine components for Koa, Hapi.js, Socket.io, MongoDB, etc.
我希望很多人会创建与前端组件一样打包功能的服务器端Web组件。 我已经开始使用Express组件,但可以想象一下Koa,Hapi.js,Socket.io,MongoDB等组件。
##Debuggable
##可调试
Scram.js has an option -d
allowing you to open up an Electron window when you are debugging. Now you have all of the Chrome dev tools available to you to help debug your server. Breakpoints, console logging, network info...it’s all there. Server-side debugging in Node.js has always seemed second-class to me. Now it’s built right into the platform:
Scram.js具有-d
选项,可让您在调试时打开Electron窗口。 现在,您可以使用所有的Chrome开发工具来帮助调试服务器。 断点,控制台日志记录,网络信息...全都在那里。 在我看来,Node.js中的服务器端调试一直是第二流的。 现在它已内置到平台中:
![](https://scotch-res.cloudinary.com/image/upload/w_auto,q_auto:good,f_auto/media/1614/CILiuE9kThuL1iqBuEij_Screenshot%20from%202016-06-07%2013:17:24.png)
##Smaller Learning Curve
##更小学习曲线
Server-side web components could help level the playing field of back-end programming. There are a lot of people, web designers, UX people, and others who might not know "programming" but do understand HTML and CSS. Server-side code as it is today probably seems untouchable to some of them. But if it's written partly in the familiar language of HTML, and especially the semantic language of custom elements, they might be able to work with server-side code more easily. At the least we can lower the learning curve.
服务器端Web组件可以帮助平衡后端编程的竞争环境。 有很多人,Web设计人员,UX用户以及其他可能不了解“编程”但确实了解HTML和CSS的人。 今天的服务器端代码对于其中一些来说似乎是无法触及的。 但是,如果它是用熟悉HTML语言(尤其是自定义元素的语义语言)编写的,则它们可能能够更轻松地使用服务器端代码。 至少我们可以降低学习曲线。
##Client-side Structure
##客户端结构
The structure of client-side and server-side apps can now mirror each other more closely. Each app can start with an index.html
file, and then use components from there. This is just one more way to unify things. In the past I’ve found it somewhat difficult to find where things start in server-side apps, but index.html
seems to have become the standard starting place for front-end apps, so why not for the back-end?
客户端和服务器端应用程序的结构现在可以更加紧密地相互镜像。 每个应用程序都可以从index.html
文件开始,然后从那里使用组件。 这只是统一事物的另一种方法。 过去,我发现很难找到从服务器端应用程序开始的地方,但是index.html
似乎已经成为前端应用程序的标准起点,那么为什么不为后端应用程序呢?
Here's an example of a generic structure for a client-side application built with web components:
这是使用Web组件构建的客户端应用程序的通用结构示例:
app/
----components/
--------app/
------------app.component.html
------------app.component.js
--------blog-post/
------------blog-post.component.html
------------blog-post.component.js
----models/
----services/
----index.html
Here's an example of a generic structure for a server-side application built with web components:
这是使用Web组件构建的服务器端应用程序的通用结构示例:
app/
----components/
--------app/
------------app.component.html
------------app.component.js
--------api/
------------api.component.html
------------api.component.js
----models/
----services/
----index.html
Both structures potentially work equally well, and now we've reduced the amount of context switching necessary to go from the client to the server and vice-versa.
两种结构都可能同样运作良好,现在我们减少了从客户端到服务器(反之亦然)所需的上下文切换量。
##Possible Problems
##可能的问题
The biggest thing that could bring this crashing down is the performance and stability of Electron in a production server environment. That being said, I don’t foresee performance being much of a problem, because Electron just spins up a renderer process to run the Node.js code, and I assume that process will run Node.js code more or less just like a vanilla Node.js process would. The bigger question is if the Chromium runtime is stable enough to run without stopping for months at a time (memory leaks).
可能导致崩溃的最大原因是在生产服务器环境中Electron的性能和稳定性。 话虽这么说,我不认为性能会有太大问题,因为Electron只是启动了一个渲染器进程来运行Node.js代码,并且我认为该进程将像香草一样或多或少地运行Node.js代码。 Node.js的过程就可以了。 更大的问题是Chromium运行时是否足够稳定,足以一次运行数月而不停止(内存泄漏)。
Another possible problem is verbosity. It will take more lines of code to accomplish the same task using server-side web components versus vanilla JavaScript because of all the markup. That being said, my hope is that the cost of verbose code will be made up by that code being easier to understand.
另一个可能的问题是冗长。 由于所有标记,使用服务器端Web组件而不是普通JavaScript将需要更多代码行来完成同一任务。 话虽这么说,但我希望冗长代码的成本将由易于理解的代码来弥补。
##Benchmarks
##基准
For the curious, I’ve done some basic benchmarks to compare the performance of this basic app written for and running on vanilla Node.js with Express, versus the same app written with Express Web Components and running on Electron with Scram.js. The graphs below show the results of some simple stress tests on the main route using this library. Here are the parameters of the tests:
出于好奇,我已经做了一些基本的基准测试,以比较该为使用Express在香草Node.js上运行的基本应用程序与在Express Web Components和Scram.js在Electron上运行的相同应用程序的性能。 下图显示了使用此库的主要路线上一些简单压力测试的结果。 这是测试的参数:
- Run on my local machine 在我的本地机器上运行
- 100 GET request increase per second 每秒增加100个GET请求
- Run until 1% of requests return unsuccessfully 运行直到1%的请求返回失败
- Run 10 times for the Node.js app and the Electron/Scram.js app 为Node.js应用程序和Electron / Scram.js应用程序运行10次
- Node.js app
- Using Node.js v6.0.0
- Using Express v4.10.1
- 使用Node.js v6.0.0
- 使用Express v4.10.1
- Electron/Scram.js app
- Using Scram.js v0.2.2
- Default settings (loading starting html file from local server)
- Debug window closed
- Using Express v4.10.1
- Using electron-prebuilt v1.2.1
- 使用Scram.js v0.2.2
- 默认设置(从本地服务器加载开始的html文件)
- 调试窗口关闭
- 使用Express v4.10.1
- 使用电子预制v1.2.1
- Using Scram.js v0.2.2
- Run with this library: https://github.com/doubaokun/node-ab 使用此库运行: https : //github.com/doubaokun/node-ab
- Run with this command:
nab http://localhost:3000 --increase 100 --verbose
使用以下命令运行:nab http://localhost:3000 --increase 100 --verbose
Here are the results (QPS stands for "Queries Per Second"):
结果如下(QPS代表“每秒查询”):
![](https://i-blog.csdnimg.cn/blog_migrate/3dabaafb6249a5d63f97a93c504b36af.png)
![](https://i-blog.csdnimg.cn/blog_migrate/12b4fd25911a67949eb277f43c0f85be.png)
![](https://scotch-res.cloudinary.com/image/upload/w_auto,q_auto:good,f_auto/media/1614/yVMSAsmTnCaT9HbjOknQ_node.png)
Surprisingly, Electron/Scram.js outperformed Node.js...we should probably take these results with a grain of salt, but I will conclude from these results that using Electron as a server is not drastically worse than using Node.js as a server, at least as far as short spurts of raw request performance are concerned. These results add validity to my statement earlier when I said that "I don’t foresee performance being much of a problem".
出乎意料的是,Electron / Scram.js的性能胜过Node.js ...我们应该对这些结果抱有少许怀疑,但我将从这些结果得出的结论是,将Electron用作服务器并不比使用Node.js严重得多。服务器,至少就短时间的原始请求性能而言。 这些结果为我早些时候说“我不认为性能有太大问题”的陈述增加了有效性。
##Wrap Up
##结语
Web components are awesome. They are bringing a standard declarative component model to the web platform. It’s easy to see the benefits on the client, and there are so many benefits to be gained on the server. The gap between the client and server is narrowing, and I believe that server-side web components are a huge step in the right direction. So, go build something with them!
Web组件很棒。 他们正在将标准的声明性组件模型引入Web平台。 在客户端上很容易看到好处,而在服务器上可以获得那么多好处。 客户端和服务器之间的差距正在缩小,我相信服务器端Web组件是朝着正确方向迈出的一大步。 所以,去与他们建立一些东西!
- Running Electron on the server: Scram.js 在服务器上运行Electron: Scram.js
- Basic server-side web components: Express Web Components 服务器端基本Web组件: Express Web Components
- Live demo: Dokku Example 现场演示: Dokku示例
- Example 1: Simple Express API 示例1: Simple Express API
- Example 2: Modular Express API example 示例2: Modular Express API示例
- Example 3: Todo App 示例3: Todo应用
- Example 4: Simple REST SPA 示例4: 简单的REST SPA
- Example 5: Basic App for Frontend Devs 示例5: 前端开发人员的基本应用
P.S. Join the email list if you would like to stay in touch as the project progresses.
PS如果您想在项目进行过程中保持联系,请加入电子邮件列表 。
学分 ( Credits )
Node.js is a trademark of Joyent, Inc. and is used with its permission. We are not endorsed by or affiliated with Joyent.
Node.js是Joyent,Inc.的商标,并经其许可使用。 Joyent不认可或不隶属于我们。
翻译自: https://scotch.io/tutorials/server-side-web-components-how-and-why
web服务器和web客户端