文章
问答
冒泡
wujie 微前端框架的应用

什么是微前端

微前端是一种将一个大型前端应用拆分为多个较小的、松耦合的应用的架构和技术方案。它的主要特征和优势包括:
  • 独立开发、独立部署 - 每个微前端应用可以由不同的团队独立开发,无需协调其他应用的开发进度,并可以独立部署上线。
  • 技术栈无关 - 每个微前端可以选用不同的编程语言、框架、构建工具等技术栈。
  • 运行时动态组合 - 在浏览器端动态加载和组合多个微前端,呈现一个完整的应用页面。
  • 高内聚,松耦合 - 每个应用高内聚只关注自己的功能,应用之间松耦合通过预定义接口进行通信。
  • 增量升级 - 可以只升级更新其中几个微前端应用,无需整体重新部署。
  • 隔离状态 - 每个微前端有自己独立的状态,不会互相影响。
  • 定制化的技术栈 - 根据不同应用场景自由选择技术栈。
 

为什么用微前端

在微前端框架之前,我们要解决多个前端应用的整合,只能通过iframe的方式处理,这样虽然也能解决大部分问题,但是由于iframe中的内容是独立渲染的,所以,最难解决的一个问题,就是子应用中,如果我要弹出一个模态框,遮挡层,就只能覆盖子应用,而父应用是完全暴露的。这样的视觉感受就会很差,而微前端的方案,就可以解决这样的问题。
 

微前端的选择

微前端就目前来说 ,虽然很多大厂都有投入应用,但是总的来说,还是远不如服务端的微服务这样大规模的使用。主要原因还是绝大多数项目的体量完全不需要这样的技术方案。但是如果解耦的角度看,虽然使用微前端会增加一些成本,如果项目的体量或者性质比较合适的话,收益也是很划算的。
目前主流的微前端框架有 ,阿里的qiankun,腾讯的wujie,京东的MicroApp 等其他的微前端框架,在参考相关资料以及相关人员的使用咨询后,我们选择了wujie来作为我们的微前端框架
 

wujie

官方是这么介绍wujie的
无界微前端方案基于 WebComponent 容器 + iframe 沙箱 能够完善的解决适配成本、样式隔离、运行性能、页面白屏、子应用通信、子应用保活、多应用激活、vite 框架支持、应用共享等
 
环境
  1. react
  2. antd
 
wujie主应用引入
官方的示例中,所有的子应用都是预先规划好的,所以,只需要按照路由地址,在对应的组件中引入wujie组件即可。与官网示例不同,我们的应用是一个偏管理系统的模式,希望是具体的功能应用都能独立部署。这就意味着我们的子应用是不固定的,考虑到整体的布局结构,这里将所有的功能应用,都放在一个路由页面下,用app 作为前缀,后面跟着应用标识。
这样一来,所有的子应用,其实都在一个组件中,这里,我们通过应用的标识来确定到底展示哪一个子应用。
添加依赖
"wujie-react": "^1.0.18"
子应用组件,保留下所有加载的子应用,根据应用标识进行切换展示
const degrade = window.localStorage.getItem("degrade") === "true" || !window.Proxy || !window.CustomElementRegistry;

const WuJieView = () => {
    const {state} = useLocation();
    const navigate = useNavigate()
    const {identifier} = useParams()
    const [loading, setLoading] = useState(false)
    const [app, setApp] = useState<any>()
    const [apps, setApps] = useState<{ name: string, url: string }[]>([])
    const [appMiss, setAppMiss] = useState(false)

    useEffect(() => {
        if (state) {
            if (!_.find(apps, (item) => {
                return item.name === state.identifier
            })) {
                setApps(_.concat(apps, {name: state.identifier, url: state.url}))
            }
            setApp({name: state.identifier, url: state.url})
        } else {
            if (identifier) {
                appstoreApi.queryAppByIdentifier(identifier).then((app: any) => {
                    setApps(_.concat(apps, {name: app.identifier, url: app.url}))
                    setApp({name: app.identifier, url: app.url})
                }).catch(async (ex) => {
                    message.error(ex.message)
                })
            }

        }
    }, [state]);

    return <div style={{width: '100%', height: '100%'}}>
        {apps.map((appItem) => <div key={appItem.name}
                                    style={{
                                        width: '100%',
                                        height: '100%',
                                        display: (_.isEqual(appItem.name, app.name) ? 'block' : 'none')
                                    }}>
            {_.isEqual(appItem.name, app.name) && <WuJieReact
                height={"100%"}
                width={"100%"}
                name={appItem.name}
                url={appItem.url}
                sync={true}
                alive={true}
                degrade={degrade}
                props={{"DubheToken": getTenantUserToken()}} //传递属性给子应用,这里传递的是Token
                activated={() => setLoading(false)}
                afterMount={() => {
                }}
            />}
        </div>)}
        {appMiss && <div>应用不存在</div>}
    </div>
}
export default WuJieView
这样主应用就集成完成了。
 
wujie子应用引入
由于在ts 下有诸多限制,这里借用lodash 进行相关的属性获取设置。这里主要就是通过全局变量的判断来做一些相应的处理 ,官网资料地址 https://wujie-micro.github.io/doc/guide/variable.html
const root = ReactDOM.createRoot(
    document.getElementById('root') as HTMLElement
);

if (_.get(window, '__POWERED_BY_WUJIE__')) {
    const props = _.get(window, ['$wujie', 'props'])
    setTenantUserToken(props?.DubheToken)  //获取主应用传递的token,设置给当前应用

    _.set(window, '__WUJIE_MOUNT', () => {
        root.render(
            <ConfigProvider locale={zhCN}>
                <RouterProvider router={router}/>
            </ConfigProvider>
        );
    })
    _.set(window, '__WUJIE_UNMOUNT', () => {
        root.unmount()
    })
} else {
    root.render(
        <ConfigProvider locale={zhCN}>
            <RouterProvider router={router}/>
        </ConfigProvider>
    );
}
效果如下
0
到这里,wujie 的基本集成就完成了,后面,再根据自己的需求,参考官方文档进行调整即可。
 
wujie跨域问题的解决
由于主应用和子应用是分别部署的,所以跨域问题在所难免,这里贴出跨域的解决方案
 
开发环境,devServer中proxy的配置
    devServer: {
        historyApiFallback: true,
        port:3104,
        proxy: {
            "/api/*": {
                target: "http://localhost:8094/",
                changeOrigin: true,
                secure: false,
                onProxyRes: function (proxyRes, req, res) {
                    if (req.method === 'OPTIONS') {
                        proxyRes.headers['Access-Control-Allow-Origin'] = req.headers.origin || '*'
                        proxyRes.headers['Access-Control-Allow-Credentials'] = true
                        proxyRes.headers['Access-Control-Allow-Methods'] = 'GET,POST,OPTIONS,PUT,DELETE,FETCH'
                        // 这里的参数,根据自己项目增删
                        proxyRes.headers['Access-Control-Allow-Headers'] = 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,token,source'
                        proxyRes.statusCode = 204
                    } else {
                        proxyRes.headers['Access-Control-Allow-Origin'] = req.headers.origin || '*'
                        proxyRes.headers['Access-Control-Allow-Credentials'] = true
                    }
                }
            },
        }
    },
 
nginx 配置,这里需要同时处理静态文件和接口反向代理的跨域
server{
        listen 80;
        server_name localhost;
        client_max_body_size 50M;

        location /{
            add_header "Access-Control-Allow-Credentials" true;
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, FETCH, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;

            root /workspace;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;
        }

        location /api {
            proxy_pass $SERVER_PROXY_PASS;

            add_header "Access-Control-Allow-Credentials" true;
            add_header 'Access-Control-Allow-Origin' $http_origin;

            if ($request_method = 'OPTIONS') {
                add_header "Access-Control-Allow-Credentials" true;
                add_header 'Access-Control-Allow-Origin' $http_origin;
                add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, FETCH, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,token,source';
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain; charset=utf-8';
                add_header 'Content-Length' 0;
                return 204;
            }
        }
}
 
以上,就是基于wujie的微前端方案的完整应用。 总体来说,wujie的使用成本很低,基本没什么需要改造的,这一点比qiankun 是要强出不少的。
 
wujie

关于作者

落雁沙
非典型码农
获得点赞
文章被阅读