The practice of synchronizing changes between SSR application and original CSR application

75034a7bd064540e7a5adb88a60e4bba.gif

As mentioned in the previous article "SSR Transformation Practice of Tmall Auto Dealer Details Page" , in order to avoid affecting online applications, our integrated application (hereinafter referred to as SSR application) is a new one based on the original CSR project. application repository.

7443077d8775647bd591f87b5d42c444.png

background

When there is a new demand iteration for the business details, and the CSR warehouse changes, the SSR application will also change accordingly. The change process is as follows:

5cdfbc51a8e69e38ba381648ec766571.png

That is, with the continuous iteration of requirements, the technical side needs to maintain two code warehouses and two R&D applications at the same time, and the cost of testing, CR, and release will double .

This has a heavy maintenance cost both for the front end and for testing. In order to solve this problem, initially we wanted to keep the application of SSR and CSR consistent on the code side, and only configure the switch ssr in build.json:

{
  "targets": [
    "web"
  ],
  "web": {
    "ssr": true, // 通过调整 ssr 配置,让仓库应用发布 mpa 或 ssr 资源
    "mpa": true
  },
}

After a local test run, there is no problem with the development and construction after switching the configuration. However, when accessing Ali Group's R&D platform, it encountered obstacles: the platform does not support accessing two applications in the same code warehouse. Later, it was discovered that when the SSR application is released, the CSR link of the relevant page will be released at the same time, so that only one application needs to be maintained on the R&D platform and released once. This gave me the hope of merging warehouses and applications. The expected merged process is as follows:

768f61ef5d2383a4e4f1c99c9ee2f288.png

The following is our transformation process.

5b0278d39a8e91e0f4dceb47822071e9.png

routing configuration

From the user's point of view, routing is an important part of web links. For example, there are webpages a, b, and c, which are accessed through www.taobao.com/a.html, www.taobao.com/page/b.html, respectively. The part marked in red after the domain name is the route of the page.www.taobao.com/page/blog/index/c.html

From the perspective of development, routing is related to both the page source code directory and the project construction configuration .

There are four routing related in the SSR application:

  1. Page source code directory : usually placed in the root directory of src/pages/, with less nesting;

  2. app.json configuration : configure the access routes and corresponding page resources of each page in app.json;

  3. SSR render function directory : where the rendering logic of the SSR page is located, under src/apis/render/, there may be nesting;

  4. PAGE_NAME in the render function : the page resource path that the render function needs to use, and the server generates a document with content by executing this page file.

These four configurations affect each other, and the relevant documents are vague. For example, PAGE_NAME is not mentioned in the official documents, but only a sentence is commented in the initialization project: "The name of the page corresponds to the directory name under pages by default". But from a practical point of view, this parameter is very important, and it even directly determines whether the server can render successfully.

The following is my verification and understanding of these routing configurations.

▐Source   directory and routing configuration

In a multi-page application, the source codes of different pages are written in the same directory src/pages/under :

├── src
│   ├── app.json                    # 路由及页面配置
│   ├── components/                 # 自定义业务组件
│   ├── apis/                       # 服务端代码
│   └── pages                        # 页面源码目录
│       ├── a 页面                   
│       ├── b 页面
|       └── c 页面
├── build.json                      # 工程配置
├── package.json
└── tsconfig.json

When there is no routing configuration, it is 域名/a.htmlaccessed , that is, the directory name (lowercase) of the page under pages is used .

To customize routing, modify the configuration in app.json, such as the following two page configurations:

{
  "routes": [
    {
      "name": "myhome",
      "source": "pages/Home/index"
    },
    {
      "name": "pages/about",
      "source": "pages/About/index"
    }
  ]
}

sourceSpecify the source code location of the page, and namespecify the page route, so that we can pass 域名/myhome.htmland 域名/pages/about.htmlaccess the page.

The storage path of the build result reads namethe configuration :

└── build
     └── web      # csr 资源的构建结果放在 web 目录下
        ├── myhome.html/js/css                   
        └── pages               
              └── about.html/js/css

   PAGE_NAME in the render function

The render function is the rendering logic we define on the nodejs server side when the user visits the page.

Let's take a look at how PAGE_NAMEit is used:

// 页面名称,默认对应 pages 下的目录名
const PAGE_NAME = 'pages/index/index';


export default withController({
  middleware: [
    downgradeOnError(PAGE_NAME), // 降级中间件
  ],
}, async () => {
  const ctx = useContext();
  // nodejs 服务端的业务逻辑
  // ……
  // 生成渲染文档
  const ssrRenderer = await useSSRRenderer(PAGE_NAME);
  await ssrRenderer.renderWithContext(ctx);
});

PAGE_NAMEPassed to useSSRRendererto generate SSR documentation.

useSSRRendererFrom the source code of , we can see PAGE_NAMEhow is consumed:

17f90d9b1761463c11fcad0f7c04da52.png

Inside the function, the path constructed by the page code PAGE_NAMEis spliced ​​out, and then the corresponding file is found from this path and returned to ssrRenderthe object, and finally a document is generated by execution.

So where is the page code placed after it is built? Take a look at the build results under the SSR project:

└── build
      └── client                              # 客户端资源目录
      |      └── web                         # csr 资源依旧在 web 目录下
      │           ├── myhome.html/js/css                   
      |           └── pages               
      |                 └── about.html/js/css
      | 
      └── node                               # 服务端资源目录
           ├── myhome.js                     # node 端只生成 js 文件
           └── pages               
                └── about.js

The directory structure of node resources follows namethe configuration .

Therefore, the value of PAGE_NAME is not the so-called "corresponding to the directory name under pages by default", but corresponding to the construction directory of page resources, that is, the page name configuration in app.json.

   The directory path of the render function

Let me talk about the conclusion first, the directory path of the render function directly determines the access route of the SSR link.

Take the following structure as an example:

└── src
      └── apis                                  # 客户端资源目录
            └── render                         # csr 资源依旧在 web 目录下
                 ├── myhome.ts                   
                 └── pages               
                       └── child
                              └── about.ts

The project corresponds to generate two SSR links: ssr域名/myhome, ssr域名/pages/child/about. It can be seen that the directory path of the render function is its access route. Since an SSR link accesses a service, not a document resource, the link does not .htmlend with .

The names of the render file and the pages directory are kept the same for ease of understanding. According to the introduction of PAGE_NAME, we know that which page resource is used for server-side rendering has nothing to do with the name of the render file. It doesn't matter if you name it abcd if the business requires it.

Therefore, there are almost no restrictions on the directory path of the render function, except for the following cases.

When starting locally, the application will open the first generated link in the browser by default, and the generated link here is determined by app.json. If there is no corresponding resource in the directory path corresponding to your render, and the browser automatically opens the link for you, the server will report an error, or even disconnect directly. So the best practice for the render function is to be consistent with the name configuration of the corresponding page in app.json.

▐Summary   _

To sum up, the relationship between relevant paths and page routing in SSR applications is shown in the following figure:

70cce8345328873a5e24125f95a6e9b4.png

  1. The pages page resource path determines the source configuration in app.json

  2. The name configuration of app.json determines the location of the built product (CSR/SSR products all rely on this value), CSR access route

  3. The path of the render function file determines the SSR access route

  4. The PAGE_NAME in the render function determines the page resource used by the function, which needs to be consistent with the name value configuration of the corresponding page

b4e89e0115ef7f8e724352a8f866f601.png

technical problem

   Cloud Build Issues

After figuring out the routing problem above, the migration from the CSR application routing to the SSR application was quickly completed, the page was successfully launched locally, and the functions of the SSR page and the CSR page were successfully verified.

But an accident still happened: when the pre-release cloud was built, a familiar window error appeared-is the environment wrong?

Checked the diff, and deleted all the objects with window during the transformation, and tried again, and the error still persisted, and continued to delete and delete until the change was completely deleted, and the error still existed.

Recalling that, jsdom was introduced to simulate the server-side environment. Even if the simulation is not good enough, the window will not exist, not to mention the success of local development and construction.

This error seems to have executed the page code during the build, so the problem was submitted to the architecture team.

Soon, the students in the architecture group confirmed the existence of the problem and gave a solution: a builder that has not been officially released.

▐Manually   simulate the code execution environment

While testing the pre-issued SSR link, a new environmental issue emerged.

49fbd6adcacfaf7f254bda7c731246bd.png

After locating the problem, I found that it is a pot of debugging plug-ins. Sort out its execution logic, probably like this:

window.__mito_result = 'something'; // 定义变量,挂载在 window 上


console.log(__mito_result); // 使用变量时,没有通过 window

The variable is hung under the window, but it is not accessed through the window. This method of directly reading the undefined variable will be found from the globalThis of the current environment at runtime. The globalThis on the node side is not the window. As a result, the variable is not found and not definean error .

There are several solutions to this problem:

  1. Modify plugin execution timing

  2. Modify the timing of plug-in injection : because the server only needs to generate the document content, this plug-in is independent of the document and does not need to be injected into the pre-release resource

  3. Environment Simulation : Use framework capabilities to simulate specific variables

Because this plugin will only be injected into the pre-release files and will not affect the officially released files. Therefore, it feels a bit heavy to modify the plug-in or function implementation. On the whole, the environmental simulation scheme was selected.

The integrated application simulates user-defined environment variables on the framework side with the following steps:

// 第一步、在 build.json 里配置 mockEnvBrowser 
// build.json
{
  "web": {
    "ssr": {
      "mockBrowserEnv": true
    },
  },
}


// 第二步、在 render 函数中进行传参
// src/apis/render/render-function-path.js
export default withController({
  middleware: [
    downgradeOnError(PAGE_NAME),
    phaIntercept(PAGE_NAME),
  ],
}, async () => {
  // 。。。
  const ssrRenderer = await useSSRRenderer(PAGE_NAME, {
    mockBrowserEnv: true, // 需要再次配置为 true
    globalVariableNameList: [ // 待模拟的变量名列表
      '__mito_data',
      '__mito_result'
    ],
    // 可以不对上面的变量进行实现,这时上面的变量在执行时值为 undefined
    browserEnv: {}, // 待模拟变量的对应实现
  });
  // 。。。
}

Here, by the way, I have studied the implementation of environment variables on the framework side, which is quite interesting. In the source code of useSSRRenderer, if it is found to be configured mockBrowserEnv: true, it will go to the following logic, the core of which is the function of string construction:

d0395e18a91fb1f7b5b30416f414380e.png

Strip off its execution core logic:

// 定义一个执行函数
function mockEnvFn (...globalVariableNameList) { // 定义形参
  execute('page.js');
  // 页面处在 mockEnvFn 函数的上下文,页面逻辑中需要用到 window 的,可以从该函数的传参中取得
}


// 执行该函数
mockEvnFn(globalVariableList); // 传入实参

The framework layer wraps an outer function for the page function, defines the formal parameter list for the outer function, and then executes the outer function, so that the page function is in the context of the formal parameter, thereby realizing the environment simulation.

   Media/script tag injection

In the app.json of CSR, there are metas attributes and scripts attributes, which can insert some media attributes and relevant tool library scripts before the page code is executed into the document. However, in SSR mode, these two attributes will not take effect and need to be rewritten into documents. One difference from the metas tag is that the js execution script is best placed in the dangerouslySetInnerHTML attribute, and the execution location is above the <Script /> component and below the <Root /> tag.

In the actual build product, the CSR link takes the metas/scripts configuration in app.json, and the SSR link takes the label in the document .

It should be noted that when the SSR link is downgraded to CSR mode, metas/scripts takes the configuration in app.json. Therefore, even if the SSR application does not need to place CSR links, metas/scripts should be maintained in two places at the same time, otherwise the performance may be inconsistent after the SSR is downgraded.

728fce95caf4ab11f24852c4ae6b21f8.jpeg

business problem

In the R&D platform, a warehouse can only access one application, and the access link generated by the application is tightly coupled with the application name. To make the CSR link released after closing the warehouse different from the CSR link of the original application , it is necessary to perform functional regression on the test side and link replacement on the business side.

▐Testing problems due to resource access features  

Due to the feature that the SSR application is not built when it is released, the documents of daily and pre-release builds will be used as online, and the resource access addresses in the documents are also typed online by default (the links to access resources are all g.alicdn.com) . However, the release of resources has not changed (resources are at dev.alicdn.com when pre-release, and resources are at g.alicdn.com when online), which leads to the fact that online resources cannot be obtained when pre-release documents are loaded.

The SSR server can change the resource link in the build document by dynamically setting webpack_public_path:

fdb5c881808fa032f6d47b5936d400c9.png

The pre-released link can normally access the resources under the pre-released CDN.

Modification __webpack_public_path__In essence , SSR obtains the runtime configuration and dynamically replaces the resource link when accessing. In CSR mode, the document is generated after the build, and there is no way to dynamically change the resource link at runtime.

In order to test that the students can work normally, it is necessary to redirect the resource path through the resource agent when accessing the webpage.

▐Business link replacement problem  

As for how to smoothly replace online links in the business domain, this is related to the specific situation of each business.

Taking car dealers as an example, we not only have access to basic link domains such as search, venues, stores, orders, and interactions, but also have shopping guides for car sites such as applets, and commercial clues for foreign capital retention. It is necessary to launch different links according to different scenarios, and it needs to be promoted separately.

c52897483edc19209bc3562e961fb1e8.jpeg

Summarize

The SSR transformation has been done so far, and a lot of various sharing has been done. Engineers from other teams once asked me, why have I never encountered any of these problems you encountered?

After my own review, I concluded that it was originally a transformation project, and I needed to dance in shackles. The original project has been done for more than a year, and its complexity is relatively high. In addition, the diversity of business scenarios makes us have to maintain CSR and SSR links at the same time, which is also a responsibility that most SSR applications do not have to bear.

These three reasons make me have to explore the road that few people take. Although it is tortuous, fortunately it has gone through, and this is also thanks to the support of the big brothers of the architecture team.

In addition, there are some other experiences. In the process of promoting a technology to be implemented within the team, a large part of the factors are not at the technical level, but in the positioning of requirements, coordination of resources, and business implementation. If these non-technical problems can be solved in the front, it will only be of great benefit to the implementation of technology.

d901d4c13d16bd0edc0366494d185e0e.jpeg

team introduction

We are the automotive technology team of Alibaba’s Taobao Technology. We are a department integrating R&D, data, and algorithms. We use the Internet + digital to vertically integrate the automotive industry to create the ultimate experience for consumers to watch, buy, and maintain cars online. Here, you will come into contact with the core technology of new retail, transaction, supply chain, settlement, position operation, etc. Here, the team atmosphere is harmonious, the business is developing rapidly, and there are many technical challenges, allowing you to have business thinking and technical depth. On the road of rapid development of Ali Automobile, we look forward to your joining!

¤  Extended reading  ¤

3DXR Technology  |  Terminal Technology  |  Audio and Video Technology

Server Technology  |  Technical Quality  |  Data Algorithms

おすすめ

転載: blog.csdn.net/Taobaojishu/article/details/130776144