The separation of front-end and back-end development has become a consensus in the industry, but it also brings deployment problems. In traditional Web mode, the front end and back end belong to the same project, and the template is rendered by the back end of course. However, with the popularity of Node and webpack’s modular packaging, the front-end is fully capable of being detached from the back-end environment during development: start a server with a local Node, Mock data, and you’re ready for business development.

But when it comes to deployment, the question becomes: where are all the JS, CSS, and index.html that the front-end finally packages? Static files js, CSS or images can also be uploaded to the CDN server at the CI stage, but the final HTML template index. HTML must be put on a server. However, is this server maintained by the front end or the back end?

Front-end maintenance HTML

If the HTML template is maintained by the front end, then the front end needs to use a static server of its own: rendering the HTML and forwarding the API. Nginx is recommended for deployment of common single-page applications.

With Nginx deployment, there are two cases:

  • Static resources are completely hosted by Nginx, i.e. js, CSS and index.html are all in one placedistIn the directory. In this case, webpack’spublicPathGenerally no special Settings, use the default/Can.
  • Static resource upload CDN, Nginx only providesindex.html. In this case, webpack’spublicPathThe address to be set to CDN, for example://static.demo.cn/activity/share/. However, this raises a problem, because the CDN address of qa environment, validation environment, and production environment are often different, in order to makeindex.htmlTo introduce the correct static file path, you need to package three times just to generate three copies of HTML that refer to different paths (even though the js content is exactly the same three times).

Nginx configuration

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /app/dist; Package path
        index  index.html index.htm;
        try_files $uri $uri/ /index.html; # single-page applications prevent a refresh to return 404. This command is not required for multi-page applications
    }

    location /api {
        proxy_pass https://anata.me; # background forwarding address
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Real-IP         $remote_addr; }}Copy the code

In theory, the qa, Yz, and PROd environments have different interface forwarding addresses, so you need three more nginx.conf configurations

The back end maintains HTML

In many cases, we need to render a page with dynamic data injected from the back end, or the page needs to support SEO, in which case we have to hand the template to the back end. How does the backend maintained HTML template get the packaged hash value?

  • The front end is packedindex.htmlSend directly to the back end (simple and crude, not recommended)
  • The front end is packaged through plug-inswebpack-manifest-pluginAnd then generate amanifest.jsonThis file is actually a key-value pair. Key represents the name of the resource and value records the hash of the resource
{
  "common.css": "/css/common/common-bundle.804a717f.css"."common.js": "/js/common/common-bundle.fcb76db9.js"."manifest.js": "/js/manifest/manifest-bundle.551ff423.js"."vendor.js": "/js/vendor/vendor-bundle.d99dc0e4.js"."page1.css": "/css/demo/demo-bundle.795bbee4.css"."page1.js": "/js/demo/demo-bundle.e382801f.js",}Copy the code

The backend index. HTML


      
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>demo</title>
  <link href="<%= manifest['page1.css']%>" rel="stylesheet">
</head>

<body>
  <h1>demo</h1>
  <script src="<%= manifest['page1.js'] %>"></script>
</body>
</html>
Copy the code

By reading the JSON file, the back end can dynamically render the file’s reference path.

If you’ve ever used Baidu’s packaging tool FIS, the map.json it produces is a similar list of resource files.

There is another benefit to using this approach: as mentioned earlier, if a file is uploaded to a CDN, the HTML maintained by the front end may need to be packaged up to three times because of the different CDN addresses in different environments. Now that THE HTML is maintained by the back end, this problem can be easily solved. The front end only needs to be packaged once, and the CDN addresses of different environments can be dynamically assembled by the back end.

Of course, using this method also raises a question: how do you get the JSON file from the back end?

  • The JSON file is packaged together with other static resources and uploaded to the CDN. When the back-end server starts up, it first obtains the JSON file from the CDN and saves it in memory
wget --tries=3 --quiet -O manifest.json http://static.demo.cn/demo/manifest.json?`date +%s` ## Prevent caching
Copy the code

The advantages of the solution: simple and convenient, every time the front-end packaging, manifest.json will automatically update, upload to the CDN and overwrite the previous version. Disadvantages: If manifest.json is updated, the backend needs to restart the service to get the new configuration, which can be costly when there are many clusters.

  • willmanifest.jsonThe content ofConfiguration centerThe backend needs to access the configuration center. Each time a CI is packaged, the configuration center update interface is called, and the back end automatically gets the latest configuration.

In my daily work projects, both of these schemes have been realized.

The middle tier of the Node

When deploying with Nginx, to address cross-domain issues, we typically need to configure proxy_pass to point to the back-end service that provides the API.

When the backend adopts SOA and microservice architecture, the API server pointed by proxy_pass is also a forwarding service in essence.

Front-end Ajax request

// Get the list of items
ajax.get('/api/queryProductList')

// Get the price list
ajax.get('/api/queryPriceList')
Copy the code

Nginx forward

location /api {
    proxy_pass https://demo.com; # background forwarding address
    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_set_header   X-Real-IP         $remote_addr;
}
Copy the code

Interface forward to

  1. https://demo.com/api/queryProductList

  2. https://demo.com/api/queryPriceList

Querying commodity lists and querying price lists are actually provided by two different SOA services:

Check out product.soa.neko.com Check out prices: price.soa.neko.com

Thus, https://demo.com is essentially a service that forwards interfaces and partially assembs data. This service can then be replaced by the Node middle tier. Using the Node middle tier, template rendering can also be moved from Nginx to Node.

Of course, with the addition of a layer of Node, the comprehensive requirements for the front end will also be improved, and the back-end deployment, monitoring, logging, performance and other problems will also come along, and the full stack (dry) engineer arises at the right moment.

Work status

Most of the front-end projects of our company to C adopt the development mode of Node layer rendering template and forwarding interface, and a small number of projects adopt Java Tomcat rendering HTML template.

Most pages are multi-page apps, not typical single-page apps.

Node layer render template, again divided into two cases:

  • To support SEO, traditional template rendering is used to populate the presentation data. However, the JS business code, still separated from the front and back ends, is not in the Node project. Such pages are generally packaged in Jquery+ WebPack modularity.
  • Without SEO support, Node renders an empty HTML template and the page content is generated entirely by JS. These pages typically use the latest front-end MVC frameworks, such as Vue and React.

Of course, the more popular SSR scheme in recent years allows Node to directly use the isomorphic components of Vue and React when rendering templates. After straight out of the page, the user’s interaction experience is as smooth as a single page application. I can only say: history is always surprisingly similar.

To some extent, SSR is a return to the traditional model, but this return is not a regression, but a spiral development.

In actual combat

So much for the theory, now let’s do it. In the last article, I introduced the principle of webpack multi-page packaging and built a simple Webpack 4-Boilerplate. This template is just a front-end development template, but it also corresponds to a Node back-end template koA2-multipage-Boilerplate.

The most important aspect of the Node project is that it implements the manifest.json file and dynamically renders the reference path to the static file, separating development and deployment from the front and back ends.

For details, see the source code for chunkmap.js, the KOA2 middleware.

const chunkmap = require('./chunkmap');
app.use(chunkmap({
  staticServer: '/ / 0.0.0.0:9001'.staticResourceMappingPath: './mainfest.json'
}));
Copy the code

This middleware takes two parameters

  • StaticServer: IP address of the static resource server, used for local developmentwebpack4-boilerplateThis front-end project starts on the server. When arriving at QA and production line, fill in the real CDN address
  • StaticResourceMappingPath: resource mapping file path, that ismanifest.jsonfile

Manifest.json for local development, without hash values

{
  "home.css": "/css/home/home-bundle.css"."home.js": "/js/home/home-bundle.js",}Copy the code

Manifest.json packaged with hash values

{
  "home.css": "/css/home/home-bundle.d2378378.css"."home.js": "/js/home/home-bundle.cb49dfaf.js",}Copy the code

Using this middleware, koA’s ctx.state global variable has a bundle property that says:

{
  "home.css": "/ / 0.0.0.0:9001 / CSS/home/home - bundle. D2378378. CSS"."home.js": "/ / 0.0.0.0:9001 / js/home/home - bundle. Cb49dfaf. Js." ",}Copy the code

The actual page is then dynamically rendered through the template engine. Of course, you can also support SEO by dynamically generating display content in your pages.


      
<html lang="zh-cn">
<head>
  <title><% = title% ></title>
  <link href="<%= bundle['home.css']%>" rel="stylesheet">
</head>

<body>
  <div id="app"></div>
  <script src="<%= bundle['home.js']%>"></script>
</body>
</html>
Copy the code

conclusion

The front and back end separation brings productivity improvements, while the Node middle layer opens up a way for the front end to enter the back end. Of course, opportunities always coexist with challenges. Today, front-end technology changes with each passing day. I really want to say: I can’t learn anymore!

reference

How do you develop and deploy front-end code in large companies?