摘要:研究下,地址,當然它也可以作為的中間件使用中介紹的很清楚解釋一下首頁比如我使用訪問,發現顯示,點擊,顯示,地址欄變為,一切正常,這時候刷新當前頁面或或點擊瀏覽器的刷新按鈕或在地址欄上再敲一下回車,發現了哦文檔
研究下connect-history-api-fallback v1.3.0,地址:https://github.com/bripkens/c...,當然它也可以作為express的中間件使用
README中介紹的很清楚
解釋一下:Single Page Applications (SPA) typically only utilise one index file that is accessible by web browsers: usually index.html. Navigation in the application is then commonly handled using JavaScript with the help of the HTML5 History API. This results in issues when the user hits the refresh button or is directly accessing a page other than the landing page, e.g. /help or /help/online as the web server bypasses the index file to locate the file at this location. As your application is a SPA, the web server will fail trying to retrieve the file and return a 404 - Not Found message to the user.
This tiny middleware addresses some of the issues. Specifically, it will change the requested location to the index you specify (default being /index.html) whenever there is a request which fulfills the following criteria:
The request is a GET request
which accepts text/html,
is not a direct file request, i.e. the requested path does not contain a . (DOT) character and
does not match a pattern provided in options.rewrites (see options below)
server.js
const path = require("path") const express = require("express") const router = express.Router() const indexRoute = router.get("/", (req, res) => { res.status(200).render("index", { title: "首頁" }) }) app.set("views", path.join(__dirname, "templates")) app.set("view engine", "html") app.engine("html", ejs.__express) app.use("/static", express.static(path.join(__dirname, "public"))) app.use(history({ rewrites: [ { from: /^/abc$/, to: "/" } ] })) app.get("/", indexRoute) app.use((req, res) => { res.status(404).send("File not found!") }) app.listen(9090, "127.0.0.1", () => { console.log("ther server is running at port " + 9090) })
index.html
index.js
Vue.use(VueRouter) var s = "" var Home = { template: "Go to Foo -Go to Bar -Go to Home " + s + "", created: function() { console.log("home") } } var Foo = { template: "home" + "" + s + "", created: function() { console.log("foo") }} var Bar = { template: "foo" + "" + s + "", created: function() { console.log("bar") }} var NotFoundComponent = { template: "bar" + "" + s + "", created: function() { console.log("not found") }} var routes = [ { path: "/", component: Home }, { path: "/foo", component: Foo }, { path: "/bar", component: Bar }, { path: "*", component: NotFoundComponent } ] var router = new VueRouter({ mode: "history", routes: routes }) new Vue({ router: router }).$mount("#test")not found" + "
比如我使用vue-router, 訪問http://localhost:9090,發現顯示home,點擊Go to Foo,顯示foo,地址欄變為http://localhost:9090/foo,一切正常,ok
這時候刷新當前頁面(ctrl+R或ctrl+command+R或點擊瀏覽器的刷新按鈕或在地址欄上再敲一下回車),發現404了哦
vue-router文檔針對這種情況做了很好的解釋:
Not to worry: To fix the issue, all you need to do is add a simple catch-all fallback route to your server. If the URL doesn"t match any static assets, it should serve the same index.html page that your app lives in. Beautiful, again!
如果express server使用了connect-history-api-fallback middleware,在你定義router的前面app.use(history({ rewrites: [ { from: /^/abc$/, to: "/" } ] }))一下
再刷新頁面,發現地址仍然http://localhost:9090/foo,然而走進了咱們的前端路由,chrome控制臺顯示了foo,真的是beautiful again
其實過程也很簡單啦,請求/foo,走到了咱們的history-api-fallback中間件,然后他看你沒有rewrite,那么好,我把req.url改成"/",于是vue-router發現地址/foo,所以根據routes的map,渲染了Foo組件
但是萬一有人輸入地址/abc,怎么辦? vue-router定義了{ path: "*", component: NotFoundComponent }用來catch-all route within your Vue app to show a 404 page
Alternatively, if you are using a Node.js server, you can implement the fallback by using the router on the server side to match the incoming URL and respond with 404 if no route is matched.
地址輸入/abc,回車,走到vue-router,會顯示not found
地址輸入/xyz,回車,走到服務端路由,http狀態為404,然后顯示File not found!
source code 分析貼下代碼
"use strict"; var url = require("url"); exports = module.exports = function historyApiFallback(options) { options = options || {}; var logger = getLogger(options); return function(req, res, next) { var headers = req.headers; if (req.method !== "GET") { logger( "Not rewriting", req.method, req.url, "because the method is not GET." ); return next(); } else if (!headers || typeof headers.accept !== "string") { logger( "Not rewriting", req.method, req.url, "because the client did not send an HTTP accept header." ); return next(); } else if (headers.accept.indexOf("application/json") === 0) { logger( "Not rewriting", req.method, req.url, "because the client prefers JSON." ); return next(); } else if (!acceptsHtml(headers.accept, options)) { logger( "Not rewriting", req.method, req.url, "because the client does not accept HTML." ); return next(); } var parsedUrl = url.parse(req.url); var rewriteTarget; options.rewrites = options.rewrites || []; for (var i = 0; i < options.rewrites.length; i++) { var rewrite = options.rewrites[i]; var match = parsedUrl.pathname.match(rewrite.from); if (match !== null) { rewriteTarget = evaluateRewriteRule(parsedUrl, match, rewrite.to); logger("Rewriting", req.method, req.url, "to", rewriteTarget); req.url = rewriteTarget; return next(); } } if (parsedUrl.pathname.indexOf(".") !== -1 && options.disableDotRule !== true) { logger( "Not rewriting", req.method, req.url, "because the path includes a dot (.) character." ); return next(); } rewriteTarget = options.index || "/index.html"; logger("Rewriting", req.method, req.url, "to", rewriteTarget); req.url = rewriteTarget; next(); }; }; function evaluateRewriteRule(parsedUrl, match, rule) { if (typeof rule === "string") { return rule; } else if (typeof rule !== "function") { throw new Error("Rewrite rule can only be of type string of function."); } return rule({ parsedUrl: parsedUrl, match: match }); } function acceptsHtml(header, options) { options.htmlAcceptHeaders = options.htmlAcceptHeaders || ["text/html", "*/*"]; for (var i = 0; i < options.htmlAcceptHeaders.length; i++) { if (header.indexOf(options.htmlAcceptHeaders[i]) !== -1) { return true; } } return false; } function getLogger(options) { if (options && options.logger) { return options.logger; } else if (options && options.verbose) { return console.log.bind(console); } return function(){}; }
getLogger, 默認不輸出,options.verbose為true,則默認console.log.bind(console),但不知道這里bind意義何在 - -,也可以直接傳logger,比如debug
如果req.method != "GET",灰,結束
如果!headers || !headers.accept != "string" (有這情況?),灰,結束
如果headers.accept.indexOf("application/json") === 0,灰,結束
acceptsHtml函數a判斷headers.accept字符串是否含有["text/html", "/"]中任意一個
當然不夠這兩個不夠你可以自定義到選項options.htmlAcceptHeaders中
!acceptsHtml(headers.accept, options),灰,結束
然后根據你定義的選項rewrites(沒定義就相當于跳過了)
按定義的數組順序,字符串依次匹配路由rewrite.from,匹配成功則走rewrite.to,to可以是字符串也可以是函數,綠,結束
判斷dot file,即pathname中包含.(點),并且選項disableDotRule !== true,即沒有關閉點文件限制規則,灰,結束
那么剩下的情況(parsedUrl.pathname不含點,或者含點但關閉了點文件規則)
rewriteTarget = options.index || "/index.html",綠結束
稍微注意下,他是先匹配自定義rewrites規則,再匹配點文件規則
測試部分用的是nodeunit,具體用法https://github.com/caolan/nod...
隨便看兩個測試用例
var sinon = require("sinon"); var historyApiFallback = require("../lib"); var tests = module.exports = {}; var middleware; var req = null; var requestedUrl; var next; tests.setUp = function(done) { middleware = historyApiFallback(); requestedUrl = "/foo"; req = { method: "GET", url: requestedUrl, headers: { accept: "text/html, */*" } }; next = sinon.stub(); done(); }; // .... tests["should ignore requests that do not accept html"] = function(test) { req.headers.accept = "application/json"; // 調用middleware middleware(req, null, next); // 測試req.url是否等于requestedUrl test.equal(req.url, requestedUrl); // next是否被調用過 test.ok(next.called); // 測試結束 test.done(); }; // ... tests["should rewrite requests when the . rule is disabled"] = function(test) { req.url = "js/app.js"; middleware = historyApiFallback({ disableDotRule: true }); middleware(req, null, next); // 測試req.url是否等于/index.html // 因為pathname中有點,且關閉了點規則 // req.url應該被rewrit成了/index.html test.equal(req.url, "/index.html"); test.ok(next.called); test.done(); }; // ... tests["should test rewrite rules"] = function(test) { req.url = "/socer"; middleware = historyApiFallback({ rewrites: [ {from: //soccer/, to: "/soccer.html"} ] }); middleware(req, null, next); // 因為沒有匹配上rewrites里的規則 // 而req.url pathname又不含點 // 所以req.url 倒退到了index.html test.equal(req.url, "/index.html"); test.ok(next.called); test.done(); };
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86595.html
摘要:前言由于博主最近又閑下來了,之前覺得的官方文檔比較難啃一直放到現在。文章會逐步分析每個處理的用意當然是博主自己的理解,不足之處歡迎指出溝通交流。后續將會補上構建生產的配置分析,案例參考。前端臨床手札構建逐步解構下 前言 由于博主最近又閑下來了,之前覺得webpack的官方文檔比較難啃一直放到現在。細心閱讀多個webpack配置案例后覺得還是得自己寫個手腳架,當然這個案例是基于vue的,...
摘要:是目前使用最為火熱的打包工具,各大知名的框架類庫都用其打包,國內使用最近也火熱起來。但是坑也很多,比如說圖片,字體等文件的路徑。 webpack 是目前使用最為火熱的打包工具,各大知名的框架類庫都用其打包,國內使用最近也火熱起來。它在單頁應用和類庫打包上幫助許多人從代碼管理中解脫了出來,成為了當下風靡一時的打包工具。 但是坑也很多,比如說圖片,字體等文件的路徑。 剛開始用webpack...
摘要:項目結構為我們搭建開發所需要的環境目錄結構及主要功能項目構建相關代碼生產環境構建代碼檢查等版本熱重載相關構建本地服務器構建工具相關基礎配置開發環境配置生產環境配置項目開發環境配置開發環境 Vue-cli 項目結構 vue-cli 為我們搭建開發所需要的環境 目錄結構及主要功能 |-- build // 項目構建(webpack)...
摘要:項目結構為我們搭建開發所需要的環境目錄結構及主要功能項目構建相關代碼生產環境構建代碼檢查等版本熱重載相關構建本地服務器構建工具相關基礎配置開發環境配置生產環境配置項目開發環境配置開發環境 Vue-cli 項目結構 vue-cli 為我們搭建開發所需要的環境 目錄結構及主要功能 |-- build // 項目構建(webpack)...
閱讀 2603·2021-10-14 09:43
閱讀 3564·2021-10-13 09:39
閱讀 3295·2019-08-30 15:44
閱讀 3146·2019-08-29 16:37
閱讀 3711·2019-08-29 13:17
閱讀 2739·2019-08-26 13:57
閱讀 1831·2019-08-26 11:59
閱讀 1250·2019-08-26 11:46