[Front-end engineering] When docker is not used, the front-end project can be rolled back online in seconds

Table of contents

I. Introduction

2. Ideas

3. Practice

3.1 Prepare Single Page Application Project

3.2 Save history and build index.html content

3.3 Simulation server hosts front-end applications

3.4 Quick rollback node server code development

3.5 Quick rollback of front-end visual page development

 3.6 Quick rollback test

4. Summary


I. Introduction

Quick rollback         of the project is a very important part of front-end engineering. After the project is deployed online, if an error cannot be opened or other reasons need to be rolled back to the previous version, the speed of rollback will be particularly important at this time .

        Normal rollback steps: git reset rollback code or git rervet is required to undo the code of the previous version, and then repackage and go online. It takes time to withdraw the code and repackage, which will affect the online time for a few minutes . A faster A solution to achieve second-level rollback .

        Docker is a good implementation solution, but not all companies use docker to deploy front-end applications. Most of them are hosted locally through nginx , or uploaded to oss to access static resources through CDN .

This article will take you step by step to implement a demo         of second-level rollback of a front-end project . It is suitable for manual typing along with the article. You can better understand the implementation of the overall process and quickly apply it to your company's projects.

The complete code of the example in this article has been uploaded: github.com/guojiongwei…

2. Ideas

        After the single-page application is packaged, there is an index.html entry file. Each packaged index.html will include the static resources required by this version. If we can not delete the static resources of the previous version (or upload them to oss ) , and each time the project is packaged, the packaged information and index.html content are saved, and an array list data is saved.

        When you need to roll back the version, you can select the build record in a branch in the project through the front-end visual interface for quick rollback. The specific implementation principle is to replace the index.html content currently used by the current project with the index.html content stored in the rollback version . After replacing the index.html content, the imported static resources will become the static resource paths packaged by this version, so as to realize Quick rollback.

        Static resources can generally be placed on oss , which is also convenient for CDN acceleration. In order to demonstrate the function, this article puts the static resources packaged each time into the local dist of the project , and rebuilding will not delete the original resources.

        This solution is applicable to all single-page application projects, regardless of react or vue, webpack or vite. The overall idea is to preserve static resources such as css, js, and pictures built in history, save the index.html content of each build, and roll back Replace the current content with the index.html content of the corresponding version to achieve real second-level rollback.

3. Practice

3.1 Prepare Single Page Application Project

First prepare a single-page application project, take react  +  vite as an example, execute the command on the command line to create the project:

# npm 6.x 
npm create vite@latest quick_rollback --template react-ts 

# npm 7+, 需要加双-- 
npm create vite@latest quick_rollback -- --template react-ts

After the project is created, enter the project to install dependencies:

cd quick_rollback 
npm install

        By default, vite will delete the original dist folder when building. You need to modify the configuration so that vite does not delete the original dist file when building , so that the static resources of each build will be kept in dist ( webpack also has similar configuration).

Modify vite.config.ts configuration and add build configuration:

build: { 
    emptyOutDir: false, // 每次打包后不删除输出目录 
}

        Note: Real projects generally upload front-end static resources to oss . If CDN access is used, this item does not need to be configured. This item is mainly configured for the convenience of the demo demonstration in this article.

        If the static resources of the company's front-end project are not uploaded to oss , but are basically placed in the project dist file of the current server and hosted with nginx , this item still needs to be configured. It is better to keep the historical construction resources. Realize second-level rollback.

Let's test it, first perform the first packaging:

npm run build

The dist folder is generated in the project

1.png

Modify the code in src/App.tsx and replace:

<h1>Vite + React</h1> 
// 替换为 
<h1>Vite + React + 秒级回滚</h1>

After the replacement is complete, execute npm run build again to package.

2.png

It can be seen that the dist folder was not emptied when repackaging , and the index.js of the last build was retained .

The next thing to do is to record the value of index.html         every time the build is successful , save it, and use it to replace it when it is rolled back.

3.2 Save history and build index.html content

Add a new script file build.mjs in the root directory of the project (use .mjs to facilitate the use of ES Module syntax), add code:

// build.mjs 
console.log('打包记录历史构建index.html内容')

And execute the file after npm run build , modify package.json :

"build": "tsc && vite build && node build.mjs",

        Now the node build.mjs file will be executed every time it is packaged, and the packaged index.html content will be obtained and saved.

The saved build record content needs to be saved and can be stored in the database. This article stores the history.json file in         the project root directory for simple simulation .

Modify build.jms :

// build.js
import path from 'path'
import fs from 'fs'

function start() {
  // 设置存储构建的history.json文件路径
  const historyPath = path.resolve('history.json')
  // 如果json文不存在就创建一个,初始值为 { list: [] }
  if(!fs.existsSync(historyPath)) {
    fs.writeFileSync(historyPath, JSON.stringify({ list: [] }))
  }
  // 读取本次打包后的dist/index.html内容
  const html = fs.readFileSync(path.resolve('./dist/index.html'), 'utf-8')
  // 获取到当前histyory.json的内容
  const history = JSON.parse(fs.readFileSync(historyPath, 'utf-8'))
  // 将当前打包的信息push到history的list中,包含构建时间和index.html内容还有id
  // 实际应用中还可以添加其他的很多信息
  history.list.push({
    time: new Date().toLocaleString('zh-cn'),
    html,
    // 模拟生成一个随机的id
    id: Math.random().toString(16).substr(2),
    // ... 分支信息,commit信息,构建时间,构建人,构建环境等字段
  })

  // 将最新的构建记录内容写入到history.json中
  fs.writeFileSync(historyPath, JSON.stringify(history, null, 2))
}

start()

Specific logic:

  1. First set the history.json file path to store the build.
  2. If the json file does not exist, create one, the initial value is  { list: [] } .
  3. Read the contents of dist/index.html after packaging this time .
  4. Get the content of the current history.json file.
  5. Push the currently packaged information to the list , including build time, index.html content and id (and other information).
  6. Write the latest build record data to the history.json file.

After the modification is completed, perform a package first:

npm run build

Then modify src/App.tsx and change back the changes just now:

<h1>Vite + React + 秒级回滚</h1> 
// 替换为 
<h1>Vite + React</h1>

After the replacement is complete, pack it again:

npm run build

After the packaging is complete, check the history.json file, and you will see that the index.html information of the two builds is preserved in it.

3.png

        After the historical construction records are saved, you need to create a node service and a front-end visual rollback page to implement the rollback logic. The implementation steps are:

  1. After selecting a build record on the front-end visualization page, pass the id to the node server.
  2. The server finds the corresponding html content according to the id , and replaces the content of dist/index.html with the html content .
  3. After the replacement is completed, the user can access the content of the corresponding version when they visit the page, realizing a second-level rollback.

3.3 Simulation server hosts front-end applications

        After the front-end project is packaged into html , css and js static resources, it will generally be hosted by nginx to achieve external network access. This article uses a front-end static resource server serve to host our packaged resources.

Install globally first ( mac needs to add sudo ):

npm i serve -g

        After the installation is complete, go to our quick rollback quick_rollback project, execute the serve command, and host the static resources of the dist folder:

serve -s dist

        After the startup is successful, the terminal will display the hosted access address, and the browser can open the address to see that the project is already accessible.

4.png

3.4 Quick rollback node server code development

First create a server.mjs to write the server-side code, the server-side things to do:

  1. Start a service on port 3001 .
  2. When accessing the root path, a front-end visual rollback page is returned.
  3. Provide  the /history interface to provide historical construction record data for the front-end page.
  4. The /rollback interface is provided  to provide an interface to which version to roll back to for the front-end page.
  5. Process  the /rollback logic, find the corresponding version of the html content, and replace the content of the dist/index.html file.

1. Create a new server.mjs in the project root directory

First create a basic server with http :

import http from 'http';
import url from 'url';
import fs from 'fs';
import path from 'path';

const server = http.createServer((req, res) => {
  // 获取请求的路径
  const { pathname } = url.parse(req.url, true);
  // 获取请求的方法
  const method = req.method.toLowerCase();
	
  // ... 后续的代码都会写在这里
})

server.listen(3001, () => {
  console.log('server is running on http://localhost:3001')
});

2. Add the root path interface and return to the rollback.html visual rollback page

        It will be more convenient to roll back the visual page operation on the front end. Create a rollback.html file in the root directory of the project and add simple code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>rollback</title>
  </head>
  <body>
  </body>
</html>

Then modify the server.mjs file and add the root path interface:

// server.mjs

// 如果请求的是根路径,就返回rollback.html
if(pathname === '/' && method === 'get') {
	res.writeHead(200, { 'Content-Type': 'text/html' }, 'utf-8')
	res.end(fs.readFileSync(path.resolve('./rollback.html'), 'utf-8'))
}

        In this way, when node server.mjs starts the server to access the http://localhost:3001 address, it will access the rollback.html page.

3. Add interface for obtaining construction records

        On the rollback.html visualization page, we need to obtain historical construction records to facilitate the rollback operation. We need to provide an interface on the server side to return the content of the history.json file, modify the server.mjs file, and add code:

// 如果请求的是history,就返回history.json的内容
if(pathname === '/history' && method === 'get') {
  res.writeHead(200, { 'Content-Type': 'application/json' }, 'utf-8')
  res.end(JSON.stringify({
  	code: 200,
  	mssage: '操作成功',
  	data: JSON.parse(fs.readFileSync(path.resolve('./history.json'), 'utf-8'))
  }))
}

4. Add quick rollback to the interface

        After accessing the historical construction records on the visualization page, you can select a historical version to roll back, so the server should provide a rollback interface, modify server.mjs , and add code:

// 如果请求的是rollback,就将对应版本的html内容写入到dist/index.html中
if(pathname === '/rollback' && method === 'get') {
  res.writeHead(200, { 'Content-Type': 'application/json' }, 'utf-8')
  const { query } = url.parse(req.url, true);
  const { id } = query;
  const history = JSON.parse(fs.readFileSync(path.resolve('./history.json'), 'utf-8'));
  const html = history.list.find(item => item.id === id).html;
  fs.writeFileSync(path.resolve('./dist/index.html'), html);
  res.end(JSON.stringify({
    code: 200,
    mssage: '操作成功',
    data: {}
  }));
}

The code logic is relatively simple:

  1. Provides a get request  /rollback interface.
  2. Receive query parameter id .
  3. Obtain the historical construction record data of history .
  4. Compare the id with the historical build record data to find the corresponding build record.
  5. Get the corresponding index.html content, and modify  ./dist/index.html to realize quick rollback operation.
  6. Then respond to the front end.

At this point, the basic logic of the server is written, and the code for the quick rollback visualization page rollback.html is started .

Complete server.mjs code:

import http from 'http';
import url from 'url';
import fs from 'fs';
import path from 'path';

const server = http.createServer((req, res) => {
  // 获取请求的路径
  const { pathname } = url.parse(req.url, true);
  // 获取请求的方法
  const method = req.method.toLowerCase();

  // 如果请求的是根路径,就返回rollback.html
  if(pathname === '/' && method === 'get') {
    res.writeHead(200, { 'Content-Type': 'text/html' }, 'utf-8')
    res.end(fs.readFileSync(path.resolve('./rollback.html'), 'utf-8'))
  }

  // 如果请求的是history,就返回history.json的内容
  if(pathname === '/history' && method === 'get') {
    res.writeHead(200, { 'Content-Type': 'application/json' }, 'utf-8')
    res.end(JSON.stringify({
      code: 200,
      mssage: '操作成功',
      data: JSON.parse(fs.readFileSync(path.resolve('./history.json'), 'utf-8'))
    }))
  }

  // 如果请求的是rollback,就将对应版本的html内容写入到dist/index.html中
  if(pathname === '/rollback' && method === 'get') {
    res.writeHead(200, { 'Content-Type': 'application/json' }, 'utf-8')
    const { query } = url.parse(req.url, true);
    const { id } = query;
    const history = JSON.parse(fs.readFileSync(path.resolve('./history.json'), 'utf-8'));
    const html = history.list.find(item => item.id === id).html;
    fs.writeFileSync(path.resolve('./dist/index.html'), html);
    res.end(JSON.stringify({
      code: 200,
      mssage: '操作成功',
      data: {}
    }));
  }
})

server.listen(3001, () => {
  console.log('server is running on http://localhost:3001')
});

3.5 Quick rollback of front-end visual page development

        The front-end page is also relatively simple. The interface prepares a select box to select the rollback version, and an OK button to confirm the rollback operation.

In order to be freed from dom operations, petite-vue developed by Youda will be used here to develop the front-end page.

        petite-vue  is  an alternative distribution of Vue  optimized for progressive enhancement. It offers   the same templating syntax and reactive model as standard Vue :

  • Only 5.8kb in size
  • Vue Compatible Template Syntax
  • Based on DOM, in-place conversion
  • responsive driver

Modify rollback.html , add select and button button elements, and then import the petite-vue cdn file for instantiation:

  1. Request to get the list of construction records immediately after the instance is initialized, and assign it to this.historyList .
  2. The construction record traversal on the page generates the select option option , and the v-model binding value is this.currentItem .
  3. The button button is a rollback confirmation button. After clicking, it will first judge whether there is a rollback version selected.
  4. Prompt if there is no choice, choose to use confirm to confirm twice.
  5. After confirmation, call the server-side rollback interface to pass the id , and obtain the execution result according to the response content.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>rollback</title>
  </head>
  <body>
    <script src="https://cdn.bootcdn.net/ajax/libs/petite-vue/0.4.1/petite-vue.umd.min.js"></script>
    <div id="app" @vue:mounted="onMounted">
      <select v-model="currentItem">
        <option value="">请选择回滚版本</option>
        <option v-for="item in historyList" :key="item.id" :value="item">发版时间:{
   
   { item.time }}</option>
      </select>
      <button @click="onRollback">回滚</button>
    </div>
  </body>
  <script>
    /** vue实例 */
    PetiteVue.createApp({
      historyList: [], // 构建记录列表
      currentItem: undefined, // 当前选中的项目
      onMounted() {
        this.getHistory();
      },
      /** 获取构建记录列表 */
      getHistory() {
        fetch("/history").then(res => res.json()).then(res => {
          if (res.code === 200) {
            this.historyList = res.data.list;
          }
        });
      },
      /** 代码回滚 */
      onRollback() {
        if (!this.currentItem) return alert("请选择回滚目标版本!");
        const isRollback = confirm(`确认项目回滚到${this.currentItem.time}版本!`);
        if (isRollback) {
          fetch(`/rollback?id=${this.currentItem.id}`).then(res => res.json()).then(res => {
            if (res.code === 200) {
              alert("快速回滚成功!");
            }
          });
        }
      },
    }).mount("#app");
  </script>
</html>

 3.6 Quick rollback test

Vite + ReactThe above package generates two build version pages, and the h1 tags of the two version pages show and         respectively. Vite + React + 快速回滚First, use serve to host and run the static resources of the current dist folder:

serve -s dist

Open the project address browser to see the current content:

4.png

Open a new terminal and start our server code:

node server.mjs

Then open http://localhost:3001 page

5.png

        We choose the version with an earlier release time, and the page corresponding to that version is displayed Vite + React + 快速回滚. After the selection is completed, click Rollback to confirm the second time. If you see the following prompt, it means that the rollback is successful:

6.png

        At this time, return to the front-end react project page, refresh the browser, and you can see that the content of the page has changed to our rolled-back version:

7.png

        At this point, the core function of second-level rollback has been completed.

4. Summary

This way of preserving historical build code also avoids two common problems:

  1. The dist file is cleared when the front-end is built , and the front-end access project will not be accessible at this time.
  2. Lazy loading of routing is used. After the new version is released, the original file disappears, and the user jumps to the page to request resources, resulting in 404 , causing the page to be abnormal.

        It can be said to serve multiple purposes, but only at this point, there is still a problem, because the historical construction files are not deleted, they will accumulate more and more, resulting in waste of storage resources.

        The solution is to modify the build.mjs file, and only keep the last 5 build results, and delete the build resources other than 5 times , so as to achieve second-level rollback without causing too much waste of resources.

Here are some implementation ideas:

  1. Get all the static resource files generated by this build every time you build.
  2. After being obtained, it is saved in the current build record.
  3. At the same time, save the file name in a files.json file, and the file path as the key .
  4. After each build, judge the total number of current builds, if more than 5 .
  5. Get the list of static resource files generated by the earliest build.
  6. Use the earliest generated static resource file for comparison in the overall file files.json .
  7. If the file generated by the earliest build is not used in the next five builds, then the file can be deleted.

        Generally, the rollback is to roll back to the previous version. It is rare to roll back to more than 5 versions. If this is the case, repackage and build.

        This article only provides a second-level rollback idea for front-end online projects, which is suitable for all single-page application projects. The company I work for now uses this solution, but it is more complicated to use in practice and needs to be encapsulated. Projects are decoupled from projects, making it easy for each project to use.

The construction records should be stored in the database or oss json file         on a project-by-project basis . The visual management platform can control all projects and quickly roll back the respective branches of individual projects .

        In addition to this method, there are many other ways to achieve second-level rollback. This method is relatively simple to implement, low in cost, and widely used.

        Later, there will be another article on using docker to achieve rapid rollback of projects. The panacea solution, front-end single-page, multi-page, and server-side projects can all use docker to achieve rapid rollback.

Guess you like

Origin blog.csdn.net/lambert00001/article/details/131985820