日安。
问题定义:
我想在动态路径上部署我的Vue应用程序,该应用程序利用vue-路由器,动态路径应该由WebServer控制,在历史模式下使用vue-路由器,并避免为每个部署重新打包应用程序。
F.e.
在http://localhost/subpath/index.html和http://localhost/another-subpath/index.html运行相同的应用程序
由于vue路由器配置是在打包阶段(f.e. )完成的。由webpack),因此设计不被控制后的包装阶段,使这一简单但如此常见的设置不可行。
此外,vue-具有相当复杂的生命周期,不允许在应用程序级别轻松重写基本设置。
此外,webpack
最终将实际资源硬编码到html
主体中,这确保了客户端在加载时正确地使用它们,但是由于块是相互依赖的--它们几乎不可能被动态注入/编辑。应用程序完整性失败的情况下,那些修改后-多莫雷事件.
研究:我的搜索到目前为止还没有找到任何可行的选项来设置这样的配置。
发布于 2020-12-21 12:13:05
我想出了几个可行的解决方案。
问题之一是,要动态更改应用程序的根,并使其在动态URL上正常运行,更改路由器基础并不是唯一需要的东西。
在vue+webpack的情况下,将实际脚本添加到构建阶段的index.html
中,从而--因此--最终会被硬编码。
我想出了几乎没有办法解决这个问题。
丑陋的解决方案:
Regex-在index.html
上,在web服务器级别替换资源URL,并为您的vue路由器基础设置硬编码。
这是很有问题的做法,但是可行的。
不幸的是,我没有那种方法的示例,所以-我不能提供任何示例,但是这应该非常简单:
在servlet端,伪代码:
let fileContent = getFileContent(requested_file_name);
if (servingFile == "index.html") {
fileContent = fileContent.regexReplace("http://url_hardcoded_at_packaging_stage", "http://url_application_is_deployed_at");
fileContent = fileContent.regexReplace("setting_token_in_your_index_html", "http://url_application_is_deployed_at");
}
return fileContent;
此外,您还需要在您的setting_token_in_your_index_html
中添加此index.html
<head>
<script type="text/javascript">
window.my_app_settings = {routerBase:"setting_token_in_your_index_html"}
</script>
</head>
并在vue-路由器级别使用它:
export default new Router({
mode: "history",
base: window.my_app_settings.routerBase,
routes: [...]
...
});
从某种意义上说,这种方法是好的,没有必要在vue-app级别上修改任何东西,而且所有的更改只能保存在servlet端,不管它是什么。
此外,它对vue-app本身也有0的性能影响。
仍然很难看,但至少从代码的角度看要好得多:
让vue-app base-aware。
这个解决方案并不简单,但在所有主要的Web服务器上都能可靠地工作,可以概括为基于Web提供的cookie在应用程序init上动态添加vue-资源。
这种方法允许“优雅地”修改所有资源URL,对性能方面的影响最小,并尽可能保持动态。
这种方法包括对打包和应用程序级别的一些小更改,并且还依赖于cookie
向应用程序提供有关自定义基础的信息。
提供来自服务器的自定义URL:
在您的web服务器上添加cookie报头(所有主要的web服务器都支持这样的功能):
(f.e.在斯卡巴特拉)
val contextShiftCookie = "subpath_where_ui_deployed";
val cookie = new Cookie("ui_deployment_root", contextShiftCookie );
cookie.setPath("/");
response.addCookie( cookie );
(**index.html
**): 应用程序修改
在<head>
部分:
<script type="text/javascript">
// Build script names, for later injection.
let stringsJs = [
<% for (let js in htmlWebpackPlugin.files.js) { %>
"<%= htmlWebpackPlugin.files.js[js] %>",
<% } %>
];
let stringsCss = [
<% for (let css in htmlWebpackPlugin.files.css) { %>
"<%= htmlWebpackPlugin.files.css[css] %>",
<% } %>
];
// Simple vanilla Cookie getter (replace with something else if needed).
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
// Simple vanilla onDomReady handler (replace with something else if needed).
(function(exports, d) {
function domReady(fn, context) {
function onReady(event) {
d.removeEventListener("DOMContentLoaded", onReady);
fn.call(context || exports, event);
}
function onReadyIe(event) {
if (d.readyState === "complete") {
d.detachEvent("onreadystatechange", onReadyIe);
fn.call(context || exports, event);
}
}
d.addEventListener && d.addEventListener("DOMContentLoaded", onReady) ||
d.attachEvent && d.attachEvent("onreadystatechange", onReadyIe);
}
exports.domReady = domReady;
})(window, document);
// Calculating vue router base.
let routerBase = "/";
if (getCookie("ui_deployment_root")) {
routerBase = getCookie("ui_deployment_root");
// console.log("Found base cookie", routerBase);
} else {
console.log("No base cookie found");
}
let scriptsBase = routerBase == "/"? "" : routerBase;
// Prefilling basic settings.
window.my_vue_app_config = {
routerBase: routerBase
};
function loadOneScript(){
if (window.my_vue_app_scripts.length > 0) {
let currentScript = window.my_vue_app_scripts.shift();
currentScript.onload = loadOneScript;
document.getElementsByTagName("body").item(0).append(currentScript);
}
}
// Injecting scripts using deployment context.
domReady(function(event) {
let head = document.getElementsByTagName("head").item(0);
stringsCss.forEach(css => {
let script = document.createElement("link");
script.setAttribute("rel","stylesheet");
script.setAttribute("href", scriptsBase + css);
head.append(script);
});
window.my_vue_app_scripts = [];
stringsJs.forEach(js => {
let script = document.createElement("script");
script.setAttribute("type","text/javascript");
script.setAttribute("src", scriptsBase + js);
window.my_vue_app_scripts.push(script);
});
loadOneScript();
});
</script>
一步一步解释:
在加载nicely.
base
设置,以确保vue-router
可以使用WebPack中的所有脚本名(请参见let stringsJs
和let stringsCss
)css
中的DomReady (因为css
是在运行中使用的,所以加载它们没有任何具体的内容)。它们应该是可用的)。请参阅stringsCss.forEach
。js
块,因为这是确保webpack编辑的应用程序被正确初始化的唯一方法(参见stringsJs.forEach
,loadOneScript
vue-路由器更改:
在路由器级别使用新设置:
export default new Router({
mode: "history",
base: window.my_vue_app_config.routerBase,
routes: [...]
...
});
Webpack机制变化:
为了支持webpack的定制捆绑:
<% for (let js in htmlWebpackPlugin.files.js) { %>
"<%= htmlWebpackPlugin.files.js[js] %>",
<% } %>
,您需要修改/app/build/webpack.ENV.conf.js
文件:
...
new HtmlWebpackPlugin({
...
inject: false,
...
}),
.ENV.
应该是您想要的包目标,但我建议对所有环境进行更改,因为这样您就可以确保您的index.html
更改在所有环境中都能正常工作。
性能考虑事项:
这是我对这种“顺序脚本注入”最关心的问题,因为从性能的角度来看,这听起来很糟糕。
但是,让我最惊讶的是,根据所有主要Web客户端(浏览器)的测试,与应用程序的完全静态服务相比,实际测试只显示了JavaScript processing time
中的~ major。
这基本上使性能问题在我的案例中变得无关紧要,但我们担心这在您的情况下可能会有所不同。
对应用程序生命周期的其他影响是完全可以忽略的,至少在我的情况下是这样的(对App的总体影响小于0.05
ms )。
附注:
由于这种方法是我自己发展出来的--我希望有建设性的批评和改进建议:)
https://stackoverflow.com/questions/65392586
复制相似问题