SSR 응용 프로그램과 원래 CSR 응용 프로그램 간의 변경 사항을 동기화하는 관행

75034a7bd064540e7a5adb88a60e4bba.gif

이전 기사 "Tmall Auto Dealer 세부 정보 페이지의 SSR 변환 사례" 에서 언급했듯이 온라인 응용 프로그램에 영향을 미치지 않기 위해 통합 응용 프로그램(이하 SSR 응용 프로그램)은 원래 CSR 프로젝트를 기반으로 하는 새로운 응용 프로그램입니다. .

7443077d8775647bd591f87b5d42c444.png

배경

비즈니스 세부 정보에 대한 새로운 수요 반복이 있고 CSR 창고가 변경되면 SSR 애플리케이션도 이에 따라 변경됩니다. 변경 프로세스는 다음과 같습니다.

5cdfbc51a8e69e38ba381648ec766571.png

즉, 요구사항의 지속적인 반복으로 기술적인 측면은 두 개의 코드 웨어하우스와 두 개의 R&D 애플리케이션을 동시에 유지해야 하며 테스트, CR 및 릴리스 비용은 두 배가 됩니다 .

이는 프런트 엔드와 테스트 모두에 막대한 유지 관리 비용이 듭니다. 이 문제를 해결하기 위해 처음에는 코드 측에서 SSR 및 CSR의 적용을 일관되게 유지하고 build.json에서 스위치 ssr만 구성하려고 했습니다.

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

현지 시운전 후 구성 전환 후 개발 및 시공에 문제가 없습니다. 그러나 Ali Group의 R&D 플랫폼에 액세스할 때 장애가 발생했습니다. 플랫폼은 동일한 코드 웨어하우스에 있는 두 개의 애플리케이션 액세스를 지원하지 않습니다. 이후 SSR 애플리케이션이 출시될 때 해당 페이지의 CSR 링크가 동시에 해제되어 R&D 플랫폼에서 하나의 애플리케이션만 유지하고 한 번만 출시하면 된다는 사실을 알게 되었습니다 . 이로 인해 웨어하우스와 애플리케이션을 병합할 수 있다는 희망이 생겼습니다. 예상되는 병합 프로세스는 다음과 같습니다.

768f61ef5d2383a4e4f1c99c9ee2f288.png

다음은 변환 과정입니다.

5b0278d39a8e91e0f4dceb47822071e9.png

라우팅 구성

사용자의 관점에서 라우팅은 웹 링크의 중요한 부분입니다. 예를 들어 a, b, c라는 웹페이지가 있고 각각 www.taobao.com/a.html, www.taobao.com/page/b.html를 통해 www.taobao.com/page/blog/index/c.html접근 하는데, 도메인 이름 뒤에 빨간색으로 표시된 부분이 페이지의 경로입니다.

개발 관점에서 라우팅은 페이지 소스 코드 디렉터리프로젝트 구성 구성 모두와 관련됩니다 .

SSR 애플리케이션과 관련된 네 가지 라우팅이 있습니다.

  1. 페이지 소스 코드 디렉토리 : 일반적으로 중첩이 적은 src/pages/의 루트 디렉토리에 위치합니다.

  2. app.json 구성 : app.json에서 각 페이지의 액세스 경로 및 해당 페이지 리소스를 구성합니다.

  3. SSR 렌더링 기능 디렉터리 : SSR 페이지의 렌더링 논리가 있는 곳, src/apis/render/ 아래에 중첩이 있을 수 있습니다.

  4. 렌더링 기능의 PAGE_NAME : 렌더링 기능이 사용해야 하는 페이지 리소스 경로이며, 서버는 이 페이지 파일을 실행하여 콘텐츠가 포함된 문서를 생성합니다.

이 네 가지 구성은 서로 영향을 미치며 관련 문서가 모호합니다. 예를 들어 공식 문서에는 PAGE_NAME이 언급되지 않지만 초기화 프로젝트에는 "페이지 이름은 기본적으로 페이지 아래의 디렉토리 이름에 해당합니다"라는 문장만 주석 처리됩니다. 그러나 실제적인 관점에서 이 매개변수는 매우 중요하며 서버가 성공적으로 렌더링할 수 있는지 여부를 직접 결정하기도 합니다.

다음은 이러한 라우팅 구성에 대한 나의 확인 및 이해입니다.

▐소스   디렉토리 및 라우팅 구성

다중 페이지 애플리케이션에서 서로 다른 페이지의 소스 코드는 src/pages/다음과 .

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

라우팅 설정이 없는 경우 기본적으로 域名/a.html액세스 . 즉, 페이지 아래에 있는 페이지의 디렉토리 이름(소문자)이 사용됩니다 .

라우팅을 사용자 정의하려면 다음 두 페이지 구성과 같이 app.json에서 구성을 수정하십시오.

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

source페이지의 소스 코드 위치를 지정하고 name페이지 경로를 지정하여 페이지를 전달 域名/myhome.html하고 액세스할 수 있도록 합니다.域名/pages/about.html

빌드 결과의 저장 경로는 name구성을 .

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

   렌더링 기능의 PAGE_NAME

렌더링 기능은 사용자가 페이지를 방문할 때 nodejs 서버 측에서 정의하는 렌더링 로직입니다.

PAGE_NAME사용 방법을 살펴보겠습니다 .

// 页面名称,默认对应 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_NAMESSR 문서를 생성하기 위해 전달됩니다 useSSRRenderer.

useSSRRenderer의 소스 코드 에서 PAGE_NAME소비 방법을 확인할 수 있습니다.

17f90d9b1761463c11fcad0f7c04da52.png

함수 내에서 페이지 코드에 의해 생성된 경로가 PAGE_NAME이어지고 이 경로에서 해당 파일을 찾아 ssrRender객체로 반환되고 최종적으로 실행에 의해 문서가 생성됩니다.

그렇다면 페이지 코드는 빌드된 후 어디에 배치됩니까? SSR 프로젝트에서 빌드 결과를 살펴보십시오.

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

노드 리소스의 디렉터리 구조는 app.json 의 name구성을 .

따라서 PAGE_NAME의 값은 소위 "기본적으로 페이지 아래의 디렉토리 이름에 해당"이 아니라 페이지 리소스의 구성 디렉토리, 즉 app.json의 페이지 이름 구성에 해당합니다.

   렌더링 기능의 디렉토리 경로

먼저 결론에 대해 말씀드리자면, 렌더링 기능의 디렉토리 경로는 SSR 링크의 액세스 경로를 직접 결정합니다.

다음 구조를 예로 들어 보겠습니다.

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

프로젝트는 두 개의 SSR 링크 생성에 해당합니다: ssr域名/myhome, ssr域名/pages/child/about. 렌더링 기능의 디렉토리 경로가 액세스 경로임을 알 수 있습니다 . SSR 링크는 문서 리소스가 아닌 서비스에 접근하기 때문에 링크가 .html로 끝나지 .

렌더 파일의 이름과 페이지 디렉토리는 이해하기 쉽도록 동일하게 유지합니다.PAGE_NAME의 도입에 따라 서버 측 렌더링에 사용되는 페이지 리소스는 렌더 파일의 이름과 관련이 없음을 알고 있습니다. . 비즈니스에서 요구하는 경우 이름을 abcd로 지정하는 것은 중요하지 않습니다.

따라서 다음과 같은 경우를 제외하고는 렌더링 함수의 디렉토리 경로에 대한 제한이 거의 없습니다.

로컬에서 시작할 때 응용 프로그램은 기본적으로 브라우저에서 처음 생성된 링크를 열며 여기에서 생성된 링크는 app.json에 의해 결정됩니다. 렌더링에 해당하는 디렉토리 경로에 해당 리소스가 없고 브라우저가 자동으로 링크를 열면 서버에서 오류를 보고하거나 직접 연결을 끊습니다. 따라서 렌더링 기능에 대한 모범 사례는 app.json에서 해당 페이지의 이름 구성과 일치하는 것입니다.

▐요약   _

요약하자면, SSR 애플리케이션에서 관련 경로와 페이지 라우팅 간의 관계는 다음 그림과 같습니다.

70cce8345328873a5e24125f95a6e9b4.png

  1. 페이지 페이지 리소스 경로는 app.json의 소스 구성을 결정합니다.

  2. app.json의 이름 구성은 빌드된 제품(CSR/SSR 제품은 모두 이 값에 의존함)의 위치, CSR 액세스 경로를 결정합니다.

  3. 렌더링 함수 파일의 경로에 따라 SSR 액세스 경로가 결정됩니다.

  4. 렌더링 함수의 PAGE_NAME은 해당 페이지의 이름 값 구성과 일치해야 하는 함수에서 사용하는 페이지 리소스를 결정합니다.

b4e89e0115ef7f8e724352a8f866f601.png

기술적 문제

   클라우드 빌드 문제

위의 라우팅 문제를 파악한 후 CSR 애플리케이션 라우팅에서 SSR 애플리케이션으로의 마이그레이션이 신속하게 완료되고 페이지가 로컬에서 성공적으로 시작되었으며 SSR 페이지 및 CSR 페이지의 기능이 성공적으로 확인되었습니다.

그러나 여전히 사고가 발생했습니다. 시험판 클라우드가 구축되었을 때 친숙한 창 오류가 나타났습니다. 환경이 잘못된 것입니까?

diff를 확인하고, 변환하는 동안 window가 있는 객체를 모두 삭제하고 다시 시도해 보았지만 여전히 오류가 지속되었고, 변경이 완전히 삭제될 때까지 계속 삭제하고 삭제하여 오류가 계속 발생했습니다.

참고로 jsdom은 서버측 환경을 시뮬레이트하기 위해 도입한 것으로, 시뮬레이션이 미흡하더라도 윈도우가 존재하지 않을 뿐더러 로컬 개발 및 구축의 성공 여부는 말할 것도 없다.

이 오류는 빌드 중에 페이지 코드를 실행한 것으로 보여 아키텍처 팀에 문제를 제출했습니다.

곧 건축반 학생들은 문제의 존재를 확인하고 해결책을 제시했습니다. 공식적으로 출시되지 않은 빌더입니다.

   코드 실행 환경 수동 시뮬레이션

사전 발급된 SSR 링크를 테스트하는 동안 새로운 환경 문제가 나타났습니다.

49fbd6adcacfaf7f254bda7c731246bd.png

문제를 찾은 후 디버깅 플러그인의 냄비라는 것을 알았습니다. 아마도 다음과 같이 실행 논리를 정렬하십시오.

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


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

변수는 창 아래에 걸려 있지만 창을 통해 접근하지는 않는다 명확하게 정의되지 않은 변수를 직접 읽어들이는 이 방법은 런타임 시 현재 환경의 globalThis에서 찾을 수 있을 것이다 노드 측의 globalThis는 창이 아니다. . 결과적으로 변수를 찾을 수 없고 not define오류가 보고됩니다. .

이 문제에 대한 몇 가지 해결책이 있습니다.

  1. 플러그인 실행 타이밍 수정

  2. 플러그인 삽입 시점 수정 : 서버에서 문서 콘텐츠 생성만 하면 되므로 이 플러그인은 문서와 독립적이며 시험판 ​​리소스에 삽입할 필요가 없습니다.

  3. 환경 시뮬레이션 : 프레임워크 기능을 사용하여 특정 변수를 시뮬레이션합니다.

이 플러그인은 시험판 파일에만 삽입되며 공식적으로 출시된 파일에는 영향을 미치지 않기 때문입니다. 따라서 플러그인이나 기능 구현을 수정하는 것이 다소 무겁게 느껴집니다. 전체적으로 환경 시뮬레이션 계획이 선택되었습니다.

통합 애플리케이션은 다음 단계를 통해 프레임워크 측에서 사용자 정의 환경 변수를 시뮬레이트합니다.

// 第一步、在 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: {}, // 待模拟变量的对应实现
  });
  // 。。。
}

그런데 여기에서는 프레임워크 측면에서 환경 변수 구현에 대해 연구했는데 꽤 흥미롭습니다. useSSRRenderer의 소스 코드에서 구성된 것으로 확인되면 mockBrowserEnv: true다음 논리로 이동하며 핵심은 문자열 구성 기능입니다.

d0395e18a91fb1f7b5b30416f414380e.png

실행 핵심 논리를 제거합니다.

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


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

프레임워크 계층은 페이지 기능에 대한 외부 기능을 랩핑하고 외부 기능에 대한 형식 매개변수 목록을 정의한 다음 외부 기능을 실행하여 페이지 기능이 형식 매개변수의 컨텍스트에 있도록 함으로써 환경 시뮬레이션을 실현합니다.

   미디어/스크립트 태그 주입

CSR의 app.json에는 페이지 코드가 문서에 실행되기 전에 일부 미디어 속성 및 관련 도구 라이브러리 스크립트를 삽입할 수 있는 메타 속성 및 스크립트 속성이 있습니다. 그러나 SSR 모드에서는 이 두 속성이 적용되지 않으며 문서에 다시 작성해야 합니다. metas 태그와의 한 가지 차이점은 js 실행 스크립트는 dangerlySetInnerHTML 속성에 가장 잘 배치되며, 실행 위치는 <Script /> 구성요소 위와 <Root /> 태그 아래에 있다는 것입니다.

실제 빌드 제품에서 CSR 링크는 app.json의 메타/스크립트 구성을 가져오고 SSR 링크는 문서의 레이블을 가져옵니다 .

SSR 링크가 CSR 모드로 다운그레이드되면 metas/scripts가 app.json의 구성을 가져옵니다. 따라서 SSR 응용 프로그램이 CSR 링크를 배치할 필요가 없더라도 메타/스크립트는 동시에 두 곳에서 유지되어야 합니다. 그렇지 않으면 SSR이 다운그레이드된 후 성능이 일정하지 않을 수 있습니다.

728fce95caf4ab11f24852c4ae6b21f8.jpeg

비즈니스 문제

R&D 플랫폼에서 웨어하우스는 하나의 애플리케이션에만 액세스할 수 있으며 애플리케이션에서 생성된 액세스 링크는 애플리케이션 이름과 긴밀하게 연결됩니다. 웨어하우스를 닫은 후 릴리스된 CSR 링크를 원래 애플리케이션의 CSR 링크와 다르게 만들려면 테스트 측에서 기능 회귀를 수행하고 비즈니스 측에서 링크 교체를 수행해야 합니다.

리소스 접근 기능으로 인한 테스트 문제  

SSR 응용 프로그램이 출시될 때 빌드되지 않기 때문에 일일 및 시험판 빌드의 문서는 온라인으로 사용되며 문서의 리소스 액세스 주소도 기본적으로 온라인으로 입력됩니다(액세스할 수 있는 링크 리소스는 모두 g.alicdn.com입니다). 그러나 리소스의 릴리스는 변경되지 않았으므로(리소스는 프리릴리즈 시 dev.alicdn.com에, 리소스는 온라인 시 g.alicdn.com에 있음) 프리릴리즈 시에는 온라인 리소스를 얻을 수 없습니다. 릴리스 문서가 로드됩니다.

SSR 서버는 webpack_public_path를 동적으로 설정하여 빌드 문서의 리소스 링크를 변경할 수 있습니다.

fdb5c881808fa032f6d47b5936d400c9.png

사전 릴리스된 링크는 사전 릴리스된 CDN 아래의 리소스에 정상적으로 액세스할 수 있습니다.

수정 __webpack_public_path__본질적으로 SSR은 런타임 구성을 획득하고 액세스할 때 리소스 링크를 동적으로 대체합니다. CSR 모드에서 문서는 빌드 후에 생성되며 런타임에 리소스 링크를 동적으로 변경할 수 있는 방법이 없습니다.

학생들이 정상적으로 작업할 수 있는지 테스트하기 위해서는 웹 페이지에 접속할 때 리소스 에이전트를 통해 리소스 경로를 리디렉션해야 합니다.

비즈니스 링크 대체 문제  

비즈니스 영역에서 온라인 링크를 원활하게 대체하는 방법은 각 비즈니스의 특정 상황과 관련이 있습니다.

자동차 대리점을 예로 들면 검색, 장소, 매장, 주문, 인터랙션 등의 기본 링크 도메인에 접근할 수 있을 뿐만 아니라 애플릿과 같은 자동차 포지션에 대한 쇼핑 가이드, 외자 유치를 위한 상업적 단서도 있습니다. 다른 시나리오에 따라 다른 링크를 시작해야 하며 별도로 홍보해야 합니다.

c52897483edc19209bc3562e961fb1e8.jpeg

요약하다

지금까지 SSR 변신이 이루어졌고, 다양한 공유가 많이 이루어졌습니다. 다른 팀의 엔지니어가 한 번은 저에게 왜 이러한 문제가 발생하지 않았습니까?

자체 검토 후 원래 변신 프로젝트라고 결론을 내렸고 족쇄에서 춤을 추어야했습니다. 원래 프로젝트는 1년 이상 진행되었으며 복잡성이 상대적으로 높습니다. 또한 비즈니스 시나리오의 다양성으로 인해 CSR 및 SSR 링크를 동시에 유지해야 하며 이는 대부분의 SSR 애플리케이션이 부담할 필요가 없는 책임이기도 합니다.

이 세 가지 이유 때문에 사람들이 거의 가지 않는 길을 탐험해야 하는데, 구불구불하지만 다행스럽게도 통과했고, 이 또한 건축팀 큰형님들의 지원 덕분입니다.

그 외에도 몇 가지 다른 경험이 있습니다. 팀 내에서 구현될 기술을 추진하는 과정에서 요소의 대부분은 기술 수준이 아니라 요구 사항의 포지셔닝, 리소스 조정 및 비즈니스 구현에 있습니다. 이러한 비기술적인 문제를 전면적으로 해결할 수 있다면 기술 구현에 큰 도움이 될 것입니다.

d901d4c13d16bd0edc0366494d185e0e.jpeg

팀 소개

우리는 Alibaba의 Taobao Technology의 자동차 기술 팀입니다.R&D, 데이터 및 알고리즘을 통합하는 부서입니다.인터넷 + 디지털을 사용하여 자동차 산업을 수직적으로 통합하여 소비자가 자동차를 보고, 구매하고, 유지하는 최고의 경험을 만듭니다. 온라인. 여기에서 신유통, 거래, 공급망, 결제, 포지션 운영 등의 핵심 기술을 접하게 됩니다. 여기에서는 팀 분위기가 조화롭고 비즈니스가 빠르게 발전하고 있으며 기술적인 문제가 많아 비즈니스 사고와 기술적 깊이를 가질 수 있습니다. Ali Automobile의 급속한 발전의 길에 여러분의 참여를 기대합니다!

¤  확장 독서  ¤

3DXR 기술  |  터미널 기술  |  오디오 및 비디오 기술

서버 기술  |  기술 품질  |  데이터 알고리즘

추천

출처blog.csdn.net/Taobaojishu/article/details/130776144