关于微前端实现原理与 ngx-planet(四) 服务端渲染

本贴最后更新于 1367 天前,其中的信息可能已经沧海桑田

1.为什么要服务端渲染

因为公司后端服务在 k8s 上,是分布式的微服务,之前端全部打包部署在了物理机器(虚拟机)nginx 上,如果通过 helm 做应用商店的话,nginx 前端这部分无法处理,包括灰度部署,CI 等,全部都只能做到接口级别的处理,并不能连带静态资源文件一起处理,所以基于分布式的前端整改迫在眉睫。

偶然发现了 ngx-planet,整篇文章基于前几篇文章,可以看之前的几篇文章。

2.如何基于 ngx-palnet 进行服务端渲染

2.0 有路由前缀的情况下服务端渲染 ngx-planet 如何改进

image.png

start 函数中监听路由阶段,其中的 startWith 过滤路由要把 location.pathname 修改好,要将前缀去除掉,至于如何动态去除不写死,仁者见仁智者见智。

2.1 首先,准备打包好的静态文件

build 命令修改,注意 --deploy-url 的意思是,打包完成后,静态资源路径是什么

    "build": "ng build --prod --deploy-url=/static/star-universe/ --base-href=/star-universe",

image.png

打包后的 index.html 如图,静态资源路径变成了 /static/star-universe 前缀,这里和 base-href 不能冲突,否则在渲染时有死循环问题

其次 base-href 就是项目访问路径前缀

然后修改 angular.json 中的 build 放到你的静态资源目录,这样在执行 npm run build 后静态资源自动放入后台项目中的静态目录中

image.png

2.2 准备 Portal 后台项目

我的项目基于 SpringBoot 自动生成,项目结构如下

image.png

以下是用到的 maven 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yunzainfo.cloud</groupId>
    <artifactId>star-universe</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>star-universe</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

一个 Controller 负责重定向路由到 index.html

package com.yunzainfo.cloud.staruniverse.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

@Controller
public class StaticController {

    @RequestMapping(value = {"/star-universe", "/star-universe/**"})
    public String index(HttpServletRequest request) {
        System.out.println(request.getRequestURI());
        return "index";
    }

}

portal 的静态资源处理,通过实现 WebMvcConfigureraddResourceHandlers 函数,注意函数传参数的静态资源路径谓 /static/star-universe/** 要和 --deploy-url 对应起来,保证 index.html 中的静态资源被正确加载

package com.yunzainfo.cloud.staruniverse.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/star-universe/**").addResourceLocations("classpath:/static/");
    }
}

然后是 portal 的 applicaiton.yml,给一个端口吧,这里我选择的是 3000,和 proxy.config 中的相同,至于静态资源路径为什么是 /static 也是为了和注册应用时的路径保持一致

server:
  port: 3000

spring:
  thymeleaf:
    cache: false
    prefix: classpath:/static/


关于 app.component 中的应用注册,resourcePathPrefixmanifest 全都加入应用路径完成远程注册

[
  {
    "name": "star-dust",
    "hostParent": "#wormhole",
    "hostClass": "star-dust",
    "routerPathPrefix": "/star-dust",
    "stylePrefix": "star-dust",
    "resourcePathPrefix": "http://localhost:3001/static/star-dust/",
    "loadSerial": true,
    "preload": false,
    "switchMode": "coexist",
    "scripts": [
      "main.js"
    ],
    "styles": [
      "styles.css"
    ],
    "manifest": "http://localhost:3001/static/star-dust/manifest.json"
  }
]

2.3 准备子项目前台项目

同样修改 buildangular.json 然后打包

2.4 准备子项目后台项目

这里唯一和 portal 项目不同的时,要加入跨域访问允许,因为 portal 加载 js/css 的时候是远程加载的。

package com.yunzainfo.cloud.stardust.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/star-dust/**").addResourceLocations("classpath:/static/");
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowCredentials(true)
                .allowedOrigins("http://localhost:3000", "http://localhost:3001", "http://localhost:3002", "http://localhost:3003")
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                .maxAge(3600)
                .allowCredentials(true);

    }
}

其余和 portal 一致,只是路径不一样

3.看效果

image.png

image.png

这样,静态资源全都被远程请求过来了

4.这种远程技术可以运用到什么地方

首先组件共享的好处不说了,结合了后端微服务的 真正的 前端微服务而不是依托于 nginx 静态部署的前端微服务

可以优化开发流程,可以做灰度部署了,可以完善 CI 了等等,好处也有,坏处也有。有利有弊,看各自需要吧。

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...