The author:Gavin, prohibit reprinting without authorization.
preface
In front and back end data interaction, the most common data interaction modes in China are as follows:
Gateway-to-microservices typically interact using RPC, which is not covered in this article.
Domestic companies usually use Rest for the interaction between front-end and gateway, while many foreign companies use GraphQL. In terms of technical implementation, these two ways have their own advantages and disadvantages, and are not difficult.
For developers, if there is no need to distinguish between client and server, the original set of code can be called each other, but after the distinction, they have to use URL + GET/POST/DELETE + query/body/params for data interaction, which will generate a lot of repeated logic and code at the front and back ends, such as: Of course, we have to define a set of interface specifications that have nothing to do with JS/TS. If React and Vue are used, we may also introduce Redux, Vuex and other state management libraries, which are a little tedious for small projects.
The problem
Is there a way to reuse back-end code, logic, like RPC calls, or even manage state?
The answer, of course, is yes.
Traditional solutions
Server side rendering
Taking next as an example (native server rendering or NUxt is similar), you can write gateway code to node, which does two things:
- Gateway layer: RPC communication with the service layer
- Route differentiation: Routes to next are identified and handed to next for rendering, routes to interfaces are identified and handed to corresponding controllers for processing
Sample pseudocode
const Koa = require('koa');
const next = require('next');
// const config = require('xxx'); // Basic configuration
// const log = require('xxx'); / / log
if (!module.parent) {
nextApp.prepare()
.then(() = > {
nextApp.setAssetPrefix('/next');
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(config[env].port, config[env].host, () = > {
log.info(`API server listening on ${config[env].host}:${config[env].port}, in ${env}`);
});
});
} else {
app.use(router.routes());
app.use(router.allowedMethods());
}
Copy the code
The use of server-side rendering can solve the problem of code reuse, the front and back end code can call public functions, enumeration, etc., but this is not really reuse, in the process of the first screen dynamic GENERATION of JS or will extract the dependent code dynamically compiled into the first screen dynamic generation of JS. Of course, in traditional development mode, you still have to use the traditional HTTP interface for communication.
In server-side rendering libraries such as Next/Nuxt, data interaction over HTTP is more cumbersome than traditional Ajax because of the need to differentiate the context in which the request is currently being executed.
Sample pseudocode
const request = method= > async (path, data = {}, req = {}) => {
const keys = Object.keys(data);
const config = {
method,
headers: {
'Content-Type': 'application/json'}};let baseUrl;
if (typeof window= = =void 0) {
/ / the server
config.headers['User-Agent'] = req.headers['user-agent'];
config.headers['Cookie'] = req.headers['cookie'];
baseUrl = 'http://127.0.0.1:3000/api'; // The current Node service listens on port 3000
} else {
/ / the client
config.credentials = 'include';
baseUrl = '/api';
}
const api = baseUrl + path;
const res = await (async() = > {if (method === 'GET') {
return! keys.length ?await fetch(api, config) : await fetch(`${api}?${ _.compact(keys.map(key => data[key] ? `${key}=${data[key]}` : ' ')).join('&')}`, config)
} else {
return awaitfetch(api, { ... config,body: JSON.stringify(data),
})
}
})().catch(e= > {
log.error(e);
});
const resJson = await res.json();
return resJson
};
Copy the code
For state management, server-side rendering can also introduce redux, VUex and other similar state management libraries, but they are a little more complicated:
- The first screen initializes the store;
- Store Synchronization between the front and back ends
Single-page application + Node
In addition to the top server rendering, pure single-page application + Node can also achieve code logic reuse, which is similar to the principle of the top server rendering, but next has a dynamic first screen generation process, while single-page application all code is static, of course, there is no real sense of code logic sharing. Mainly through webpack and other tools to pack dependencies into the front-end static resources, through Node to start a static resource access directory, or to the third-party static resource management center, hang a CDN.
A better way:Layr
Introduction to the
Layr is an object-oriented JS/TS library for RPC-like communication.
How to use
- The back end consists of one or more classes, each of which exposes some attributes and methods to the front end;
- The front end generates a proxy for the back-end class through which it communicates.
The principle of
On the surface, Layr is similar to Java RMI, but the principle is very different:
- Layr is not a distributed object, the backend is stateless, there is no shared object in the stack;
- Layr does not involve any template code, code generation, configuration files, etc.
- Use the Deepr protocol
The sample
The back-end
import {
Component,
primaryIdentifier,
attribute,
method,
expose
} from '@layr/component';
import {ComponentHTTPServer} from '@layr/component-http-server';
class Counter extends Component {
@expose({get: true.set: true}) @primaryIdentifier() id;
@expose({get: true.set: true}) @attribute() value = 0;
@expose({call: true}) @method() increment() {
this.value++; }}const server = new ComponentHTTPServer(Counter, {port: 3210});
server.start();
Copy the code
The front end
import {ComponentHTTPClient} from '@layr/component-http-client';
(async() = > {// Establish a connection
const client = new ComponentHTTPClient('http://localhost:3210');
const Counter = await client.getComponent();
// Data subscription example
class ExtendedCounter extends Counter {
async increment() {
await super.increment();
if (this.value === 3)
console.log('State changed, trigger XXX'); }}}// Get and modify data
const counter = new Counter(); // new ExtendedCounter();
console.log(counter.value); / / = > 0
await counter.increment();
console.log(counter.value); / / = > 1
await counter.increment();
console.log(counter.value); / / = > 2}) ();Copy the code
conclusion
Pay attention to
This paper only provides a new idea of front and back end data interaction. In my opinion, Layr is currently only suitable for small projects with rapid iteration. For large projects, whether Layr or GraphQL is used, the resource requirements on the server (Node) will be high.
Layr is designed with the idea of object-oriented, which is a bit rude for students who are used to react functional programming mode. Layr is more friendly to students who are used to Java development mode.
Unlike the traditional Node/PHP+ template format, the front and back ends of Layr are completely independent and can be deployed independently.
A link to the
- Layr
- Good Bye Web APIs
- More Use Cases