- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
介绍
我知道这个问题已经以许多不同的形式提出。但是,我已经阅读了有关该问题的所有帖子和文章,并且无法解决我们面临的先决条件所带来的问题。因此,我想先描述我们的问题并给出我们的动力,然后再描述我们已经尝试或正在使用的不同方法,即使它们不能令人满意。
问题/动机
我们的设置可谓有些复杂。我们的Angular应用程序已部署在多个Kubernetes集群上,并通过不同的路径提供服务。
这些集群本身位于代理之后,Kubernetes本身添加了一个名为Ingress的代理。因此,对我们来说,常见的设置如下所示:
本地
http:// localhost:4200 /
本地群集
https://kubernetes.local/angular-app
开发
https://ourcompany.com/proxy-dev/kubernetes/angular-app
暂存
https://ourcompany.com/proxy-staging/kubernetes/angular-app
客户
https://kubernetes.customer.com/angular-app
始终为应用程序提供不同的基本路径。因为这是Kubernetes,所以我们的应用程序已与Docker容器一起部署。
经典的做法是,使用ng build
转换Angular应用,然后使用例如如下所示的Dockerfile:
FROM nginx:1.17.10-alpine
EXPOSE 80
COPY conf/nginx.conf /etc/nginx/conf.d/default.conf
RUN rm -rf /usr/share/nginx/html/*
COPY dist/ /usr/share/nginx/html/
要告诉CLI如何设置base-href以及从哪里提供 Assets ,可以使用Angular CLI的
--base-href
和
--deploy-url
选项。
environments[.prod].ts
中。
FROM node:13.10.1-alpine
# set working directory
WORKDIR /app
# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH
# install and cache app dependencies
COPY package*.json ./
RUN npm install
COPY / /app/
ENV PROTOCOL http
ENV HOST localhost
ENV CONTEXT_PATH /
ENV PORT 8080
ENV ENDPOINT localhost/endpoint
VOLUME /app/dist
# prebuild ES5 modules
RUN ng build --prod --output-path=build
CMD \
sed -i -E "s@(endpoint: ['|\"])[^'\"]+@\1${ENDPOINT}@g" src/environments/environment.prod.ts \
&& \
ng build \
--prod \
--base-href=${PROTOCOL}://${HOST}:${PORT}${CONTEXT_PATH} \
--deploy-url=${PROTOCOL}://${HOST}:${PORT}${CONTEXT_PATH} \
--output-path=build \
&& \
rm -rf /app/dist/* && \
mv -v build/* /app/dist/
将该容器作为initContainer集成到Kubernetes部署中,可以使用正确的base-href,deploying-url构建该应用程序,并根据部署该应用程序的环境替换特定的变量。
ng build
命令。为了防止它在每次容器已经在前面的ng build
指令中运行RUN
命令来缓存它们时构建ES模块。APP_BASE_HREF
InjectionToken。因此,我们尝试遵循Web上的不同指南,以根据应用程序所部署的环境动态地设置此设置。首先要做的是:
config.json
中添加一个名为/src/assets/config.json
的文件,其内容(以及其他变量)的基本路径为{
"basePath": "/angular-app",
"endpoint": "https://kubernetes.local/endpoint"
}
main.ts
文件中,该文件通过父历史记录以递归方式探查当前window.location.pathname
以查找config.json
。 export type AppConfig = {
basePath: string;
endpoint: string;
};
export const APP_CONFIG = new InjectionToken<AppConfig>('APP_CONFIG');
export async function fetchConfig(): Promise<AppConfig> {
const pathName = window.location.pathname.split('/');
for (const index of pathName.slice().reverse().keys()) {
const path = pathName.slice(0, pathName.length - index).join('/');
const url = stripSlashes(`${window.location.origin}/${path}/assets/config.json`);
const promise = fetch(url);
const [response, error] = await handle(promise) as [Response, any];
if (!error && response.ok && response.headers.get('content-type') === 'application/json') {
return await response.json();
}
}
return null;
}
fetchConfig().then((config: AppConfig) => {
platformBrowserDynamic([{ provide: APP_CONFIG, useValue: config }])
.bootstrapModule(AppModule)
.catch(err => console.error('An unexpected error occured: ', err));
});
app.module.ts
内部,使用APP_BASE_HREF
初始化APP_CONFIG
@NgModule({
providers: [
{
provide: APP_BASE_HREF,
useFactory: (config: AppConfig) => {
return config.basePath;
},
deps: [APP_CONFIG]
}
]
})
export class AppModule { }
重要提示:我们使用这种方法而不是
APP_INITIALIZER
,因为尝试时,
APP_BASE_HREF
的提供程序始终在
APP_INITIALIZER
的提供程序之前运行,因此
APP_BASE_HREF
始终未定义。
main.js
,
vendor.js
,
runtime.js
以及所有其他 Assets ,因此实际上我们的代码都没有运行。
config.json
,我们将代码直接放在
index.html
内并内联。
<body>
<app-root></app-root>
<script>
function addScriptTag(d, src) {
const script = d.createElement('script');
script.type = 'text/javascript';
script.onload = function(){
// remote script has loaded
};
script.src = src;
d.getElementsByTagName('body')[0].appendChild(script);
}
const pathName = window.location.pathname.split('/');
const promises = [];
for (const index of pathName.slice().reverse().keys()) {
const path = pathName.slice(0, pathName.length - index).join('/');
const url = `${window.location.origin}/${path}/assets/config.json`;
const stripped = url.replace(/([^:]\/)\/+/gi, '$1')
promises.push(fetch(stripped));
}
Promise.all(promises).then(result => {
const response = result.find(response => response.ok && response.headers.get('content-type').includes('application/json'));
if (response) {
response.json().then(json => {
document.querySelector('base').setAttribute('href', json.basePath);
for (const node of document.querySelectorAll('script[src]')) {
addScriptTag(document, `${json.basePath}/${node.getAttribute('src')}`);
node.remove();
window['app-config'] = json;
}
});
}
});
</script>
</body>
此外,我们必须对
APP_BASE_HREF
提供程序内部的代码进行如下修改:
useFactory: () => {
const config: AppConfig = (window as {[key: string]: any})['app-config'];
return config.basePath;
},
deps: []
现在发生的事情是,它加载页面,用前缀的URL替换源URL,加载脚本,加载应用程序并设置
APP_BASE_HREF
。
--base-href
选项实际上设置了
APP_BASE_HREF
,但是我找不到
--deploy-url
选项是什么。
最佳答案
花了一些时间,但我们最终设法找到一个足够简单的解决方案,以涵盖我们在问题中提出的所有观点。
该解决方案与原始方法非常接近,该方法仅是脱机构建Angular应用程序,然后将内容复制到nginx
容器中,并与先前在init容器中使用的构建过程混合在一起。
然后,我们的解决方案如下所示。我们首先离线构建应用程序,然后注入(inject)一些可识别的字符串,其中base-href
和deploy-url
应该在以后。
ng build --prod --base-href=http://recognisable-host-name/ --deploy-url=http://recognisable-host-name/
生成的
runtime*.js
和
index.html
文件将在代码内部包含这些内容,并作为加载 Assets 的基础。
FROM nginx:1.17.10-alpine
EXPOSE 80
COPY conf/nginx.conf /etc/nginx/conf.d/default.conf
RUN rm -rf /usr/share/nginx/html/*
COPY dist/ /usr/share/nginx/html/
到这样的新版本
FROM nginx:1.17.10-alpine
EXPOSE 80
COPY conf/nginx.conf /etc/nginx/conf.d/default.conf
RUN rm -rf /usr/share/nginx/html/*
COPY dist/ /usr/share/nginx/html/
ENV CONTEXT_PATH http://localhost:80
ENV ENDPOINT http://localhost/endpoint
ENV VARIABLE foobar
CMD \
mainFiles=$(ls /usr/share/nginx/html/main*.js) \
&& \
for f in ${mainFiles}; do \
envsubst '${ENDPOINT},${VARIABLE}' < "$f" > "${f}.tmp" && mv "${f}.tmp" "$f"; \
done \
&& \
runtimeFiles=$(grep -lr "recognisable-host-name" /usr/share/nginx/html) \
&& \
for f in ${runtimeFiles}; do sed -i -E "s@http://recognisable-host-name/?@${CONTEXT_PATH}@g" "$f"; done \
&& \
nginx -g 'daemon off;'
首先,我们在此处传递我们要进行的替换。首先,
CONTEXT_PATH
是完整的基本路径,它将替换我们之前注入(inject)的
http://recognisable-host-name
字符串,方法是首先找到包含该字符串的所有文件,然后将其替换为
sed
命令。
envsubst
并替换
main*.js
文件中对这些变量的所有提及来替换特定于环境的其他变量。因此,
environments.prod.ts
的准备如下:
export const environment = {
"endpoint": "${ENDPOINT}",
"variable": "${VARIABLE}"
}
这照顾了我参加的所有要点,即:
关于angular - Angular 2+-动态更改应用程序的基本路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63299094/
我正在通过 labrepl 工作,我看到了一些遵循此模式的代码: ;; Pattern (apply #(apply f %&) coll) ;; Concrete example user=> (a
我从未向应用商店提交过应用,但我会在不久的将来提交。 到目前为止,我对为 iPhone 而非 iPad 进行设计感到很自在。 我了解,通过将通用PAID 应用放到应用商店,客户只需支付一次就可以同时使
我有一个应用程序,它使用不同的 Facebook 应用程序(2 个不同的 AppID)在 Facebook 上发布并显示它是“通过 iPhone”/“通过 iPad”。 当 Facebook 应用程序
我有一个要求,我们必须通过将网站源文件保存在本地 iOS 应用程序中来在 iOS 应用程序 Webview 中运行网站。 Angular 需要服务器来运行应用程序,但由于我们将文件保存在本地,我们无法
所以我有一个单页客户端应用程序。 正常流程: 应用程序 -> OAuth2 服务器 -> 应用程序 我们有自己的 OAuth2 服务器,因此人们可以登录应用程序并获取与用户实体关联的 access_t
假设我有一个安装在用户设备上的 Android 应用程序 A,我的应用程序有一个 AppWidget,我们可以让其他 Android 开发人员在其中以每次安装成本为基础发布他们的应用程序推广广告。因此
Secrets of the JavaScript Ninja中有一个例子它提供了以下代码来绕过 JavaScript 的 Math.min() 函数,该函数需要一个可变长度列表。 Example:
当我分别将数组和对象传递给 function.apply() 时,我得到 NaN 的 o/p,但是当我传递对象和数组时,我得到一个数字。为什么会发生这种情况? 由于数组也被视为对象,为什么我无法使用它
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界. 这篇CFSDN的博客文章ASP转换格林威治时间函数DateDiff()应用由作者收集整理,如果你
我正在将列表传递给 map并且想要返回一个带有合并名称的 data.frame 对象。 例如: library(tidyverse) library(broom) mtcars %>% spl
我有一个非常基本的问题,但我不知道如何实现它:我有一个返回数据框,其中每个工具的返回值是按行排列的: tmp<-as.data.frame(t(data.frame(a=rnorm(250,0,1)
我正在使用我的 FB 应用创建群组并邀请用户加入我的应用群组,第一次一切正常。当我尝试创建另一个组时,出现以下错误: {"(OAuthException - #4009) (#4009) 在有更多用户
我们正在开发一款类似于“会说话的本”应用程序的 child 应用程序。它包含大量用于交互式动画的 JPEG 图像序列。 问题是动画在 iPad Air 上播放正常,但在 iPad 2 上播放缓慢或滞后
我关注 clojure 一段时间了,它的一些功能非常令人兴奋(持久数据结构、函数式方法、不可变状态)。然而,由于我仍在学习,我想了解如何在实际场景中应用,证明其好处,然后演化并应用于更复杂的问题。即,
我开发了一个仅使用挪威语的应用程序。该应用程序不使用本地化,因为它应该仅以一种语言(挪威语)显示。但是,我已在 Info.plist 文件中将“本地化 native 开发区域”设置为“no”。我还使用
读完 Anthony's response 后上a style-related parser question ,我试图说服自己编写单体解析器仍然可以相当紧凑。 所以而不是 reference ::
multicore 库中是否有类似 sapply 的东西?还是我必须 unlist(mclapply(..)) 才能实现这一点? 如果它不存在:推理是什么? 提前致谢,如果这是一个愚蠢的问题,我们深表
我喜欢在窗口中弹出结果,以便更容易查看和查找(例如,它们不会随着控制台继续滚动而丢失)。一种方法是使用 sink() 和 file.show()。例如: y <- rnorm(100); x <- r
我有一个如下所示的 spring mvc Controller @RequestMapping(value="/new", method=RequestMethod.POST) public Stri
我正在阅读 StructureMap关于依赖注入(inject),首先有两部分初始化映射,具体类类型的接口(interface),另一部分只是实例化(请求实例)。 第一部分需要配置和设置,这是在 Bo
我是一名优秀的程序员,十分优秀!