摘要:的全稱(chēng)是統(tǒng)一資源定位符英文,可以這么說(shuō),是一種標(biāo)準(zhǔn),而網(wǎng)址則是符合標(biāo)準(zhǔn)的一種實(shí)現(xiàn)而已。渲染器,將組件渲染到頁(yè)面上。
0x000 概述
從這一章開(kāi)始就進(jìn)入路由章節(jié)了,并不直接從如何使用react-route來(lái)講,而是從路由的概念和實(shí)現(xiàn)來(lái)講,達(dá)到知道路由的本質(zhì),而不是只知道如何使用react-route庫(kù)的目的,畢竟react-route只是一個(gè)庫(kù),是路由的一個(gè)實(shí)現(xiàn)而已,而不是路由本身。
0x001 URL的概念很多人對(duì)url的理解就是網(wǎng)址,我們?cè)跒g覽器地址欄輸入網(wǎng)址,便可以訪(fǎng)問(wèn)到特定網(wǎng)頁(yè),但其實(shí)url的含義遠(yuǎn)遠(yuǎn)不止是網(wǎng)址。url的全稱(chēng)是統(tǒng)一資源定位符(英文:Uniform Resource Locator),可以這么說(shuō),url是一種標(biāo)準(zhǔn),而網(wǎng)址則是符合url標(biāo)準(zhǔn)的一種實(shí)現(xiàn)而已。
讓我們做幾個(gè)實(shí)驗(yàn):
打開(kāi)瀏覽器,訪(fǎng)問(wèn)segmentfault的主頁(yè),此時(shí)地址欄顯示的是:
https://segmentfault.com
桌面新建from-url-to-spa.txt文件,輸入內(nèi)容from url to spa,并拖拽到瀏覽器,此時(shí)瀏覽器顯示的是
file:///Users/FollowWinter/Desktop/from-url-to-spa.txt
打開(kāi)一個(gè)github項(xiàng)目,并選擇ssh訪(fǎng)問(wèn),我們可以得到以下地址:
git@github.com:followWinter/flex-layout.git
說(shuō)明:其中,1訪(fǎng)問(wèn)了一個(gè)網(wǎng)頁(yè),2訪(fǎng)問(wèn)了一個(gè)本地文件,3訪(fǎng)問(wèn)了一個(gè)開(kāi)源項(xiàng)目,從以上可以看出,url有多種用途各異的實(shí)現(xiàn),但是我們可以這么歸納,網(wǎng)絡(luò)上(包括本地和遠(yuǎn)程)所有的的東西都看作資源,我們可以通過(guò)一種符合某種標(biāo)準(zhǔn)的格式來(lái)訪(fǎng)問(wèn)這種資源,從而忽略設(shè)備類(lèi)型(服務(wù)器、路由器、硬盤(pán)......)、網(wǎng)絡(luò)類(lèi)型(遠(yuǎn)程、本地......)、資源類(lèi)型(文本、圖片、音樂(lè)、電影......),而這種標(biāo)準(zhǔn)就是url,也就是我對(duì)統(tǒng)一資源定位符的理解。
統(tǒng)一資源定位符的標(biāo)準(zhǔn)格式如下:
協(xié)議類(lèi)型:[//服務(wù)器地址[:端口號(hào)]][/資源層級(jí)UNIX文件路徑]文件名[?查詢(xún)][#片段ID]
統(tǒng)一資源定位符的完整格式如下:
協(xié)議類(lèi)型:[//[訪(fǎng)問(wèn)資源需要的憑證信息@]服務(wù)器地址[:端口號(hào)]][/資源層級(jí)UNIX文件路徑]文件名[?查詢(xún)][#片段ID]0x002 spa是什么
SPA全稱(chēng)是single page web application,也就是只有一個(gè)頁(yè)面的web應(yīng)用程序,我們?cè)L問(wèn)一個(gè)網(wǎng)頁(yè),能夠在這個(gè)網(wǎng)頁(yè)上完成所有的業(yè)務(wù)操作,我們就可以稱(chēng)之為SPA,是和框架無(wú)關(guān)、技術(shù)無(wú)關(guān)的一個(gè)概念。并不是說(shuō)用angular、vue、react實(shí)現(xiàn)的web應(yīng)用才叫SPA,因?yàn)檫@些框架也可以在多頁(yè)應(yīng)用中使用。
0x003 如何實(shí)現(xiàn)spa只要在一個(gè)頁(yè)面完成所有業(yè)務(wù)操作,就可以稱(chēng)之為SPA了,所以實(shí)現(xiàn)所謂的SPA也很簡(jiǎn)單,就是將原本多頁(yè)的步驟轉(zhuǎn)化為一個(gè)頁(yè)面就行了。
0x004 SPA和路由有啥關(guān)系啊回答:沒(méi)有關(guān)系。SPA不一定要使用路由,不使用也沒(méi)有關(guān)系,但是隨著單頁(yè)應(yīng)用了擴(kuò)大,將所有的邏輯都卸載一個(gè)頁(yè)面上,會(huì)導(dǎo)致邏輯爆炸,維護(hù)痛苦,所以在邏輯上又分為多個(gè)頁(yè)面,達(dá)到好維護(hù)的效果。
0x005 路由出現(xiàn)一開(kāi)始是沒(méi)有路由的,但是做的應(yīng)用多了,便有了路由。對(duì)于路由的需求有兩個(gè):
維護(hù)上的需求,過(guò)多的邏輯寫(xiě)在一個(gè)頁(yè)面上,容易混亂,所以用路由分離多帶帶邏輯和頁(yè)面。
狀態(tài)保存的需求,比如一個(gè)SPA,我們有文章和文章詳情頁(yè),有一天我們需要分享一個(gè)文章,希望可以通過(guò)一個(gè)鏈接直接訪(fǎng)問(wèn)到這篇文章。但是單頁(yè)應(yīng)用是無(wú)狀態(tài)的,而網(wǎng)址又是唯一的,比如a.com/index.html,無(wú)法做到直接訪(fǎng)問(wèn)詳情頁(yè),所以就出現(xiàn)了一些方案:
hash:a.com/index.html#detail/1,訪(fǎng)問(wèn) id 為1的文章詳情頁(yè)
url:a.com/index/detail/1,訪(fǎng)問(wèn) id 為1的文章詳情頁(yè)
這樣我們就可以分享一篇文章給其他用戶(hù)了,方案1實(shí)現(xiàn)比較簡(jiǎn)單,但是路由丑陋并且占用了 hash 符,頁(yè)面中就不能亂用 hash 符了。方案2好但是需要后端配合,實(shí)現(xiàn)也很簡(jiǎn)單,不管這個(gè) url 是什么,都返回單頁(yè)應(yīng)用的 html 就好了。
0x006 實(shí)現(xiàn)簡(jiǎn)單的SPA
架構(gòu):
組件,每個(gè)組件都是獨(dú)立的,可以渲染出自己的dom,并且可以綁定事件,擁有生命周期。
渲染器,將組件渲染到頁(yè)面上。
服務(wù),做數(shù)據(jù)管理等一些邏輯服務(wù)。
項(xiàng)目初始化:
整個(gè)項(xiàng)目起始沒(méi)有啥特別的,只是支持了es6而已,而整個(gè)項(xiàng)目我們也將會(huì)用es6來(lái)實(shí)現(xiàn)
初始化項(xiàng)目及其目錄
+ 0x021-spa + src + core + page + services - index.html - index.js - .babelrc - package.json - webpack.config.js
index.html:
React Study
.babelrc
{ "presets": [ "env", "stage-3" ] }
package.json
{ "name": "0x021-spa", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1", "start": "webpack-dev-server --color --process " }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-env": "^1.7.0", "babel-preset-react": "^6.24.1", "babel-preset-stage-3": "^6.24.1", "html-webpack-plugin": "^3.2.0", "webpack": "^4.16.5", "webpack-cli": "^3.1.0", "webpack-dev-server": "^3.1.5" } }
webpack.config.js:
const path = require("path") var HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: path.resolve(__dirname, "src/index.js"), mode: "development", output: { path: path.resolve(__dirname, "dist"), filename: "bundle.js" }, devServer: { open: true }, module: { rules: [ { test: /.js$/, loader: "babel-loader" }, ] }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, "src/index.html") }) ] }
渲染器實(shí)現(xiàn)
渲染器的作用起始就是渲染組件而已,而每個(gè)組件都有一個(gè)render方法,該方法返回一個(gè)dom字符串,也就是說(shuō),渲染器的本質(zhì)就是將dom字符串掛載和卸載。
core/LeactDom.js
class LeactDom { static render(child, parent) { parent.innerHTML=child } } export default LeactDom
測(cè)試index.js
import LeactDom from "./core/LeactDom"; import LeactDom from "./core/LeactDom"; LeactDom.render(`這是一個(gè)p
`, document.getElementById("app")) document.getElementById("p").addEventListener("click", () => { LeactDom.render("這是一個(gè)span", document.getElementById("app")) })
查看瀏覽器
如圖,我們已經(jīng)實(shí)現(xiàn)了切換了,只需要將之封裝為組件就行了 ![圖片描述][1]
組件
core/Component.js
// 這是組件根類(lèi), 所有的組件都繼承這個(gè)根 class Component { // 返回 dom 字符串 render() { return "" } // dom 掛載上去以后 執(zhí)行該方法, 可以在這個(gè)方法上執(zhí)行 dom 查詢(xún)和事件綁定 componentDidMount() { } } export default Component
自定義組件page/Hello.js
import Component from "../core/Component"; class Hello extends Component { render() { return `hello
` } componentDidMount() { document.getElementById("hello").addEventListener("click", () => { alert("hello") }) } } export default Hello
引入Hello組件
import LeactDom from "./core/LeactDom"; import Hello from "./page/Hello"; LeactDom.render(Hello,document.getElementById("app"))
修改LeactDom
class LeactDom { static render(child, parent, props={}) { if (typeof child === "function") { let comp = new child() comp.props = props parent.innerHTML = comp.render() comp.componentDidMount() } else { parent.innerHTML = child } } } export default LeactDom
查看效果
框架完成開(kāi)始編寫(xiě)服務(wù)
文章獲取服務(wù)service/AticleService.js
const articles = [ { id: 1, title: "Redux入門(mén)0x101: 簡(jiǎn)介及`redux`簡(jiǎn)單實(shí)現(xiàn)", summary: "簡(jiǎn)介及`redux`簡(jiǎn)單實(shí)現(xiàn)", detail: "詳情1" }, { id: 2, title: "Redux入門(mén)0x102: redux 栗子之 counter", summary: "redux 栗子之 counter", detail: "詳情2" }, { id: 3, title: "Redux入門(mén)0x103: 拆分多個(gè) reducer", summary: "拆分多個(gè) reducer", detail: "詳情3" }, { id: 4, title: "Redux入門(mén)0x104: Action Creators", summary: "Action Creators", detail: "詳情4" }, { id: 5, title: "Redux入門(mén)0x105: redux 中間件", summary: "redux 中間件", detail: "詳情5" }, ] class ArticleService { static getAll() { return articles } static getById(id) { return articles.find((article) => { return id == article.id }) } } export default ArticleService
開(kāi)始編寫(xiě)自定義組件
文章列表組件
import ArticleService from "../services/ArticleService"; import DetailPage from "./DetailPage"; import LeactDom from "../core/LeactDom"; class ArticlePage { render() { let articlesListString = ArticleService.getAll() .map(article => { return `` }) .reduce((article1, article2) => { return article1 + article2 }) let articleListContrinerString = `${article.title}
${article.summary}
` return articleListContrinerString } componentDidMount() { let articles = document.getElementsByClassName("article") ;[].forEach.call(articles, article => { article.addEventListener("click", () => { LeactDom.render(new DetailPage({articleId: article.getAttribute("data-id")}), document.getElementById("app")) }) } ) } } export default ArticlePage文章列表
${articlesListString}
文章詳情組件
import ArticleService from "../services/ArticleService"; import Component from "../core/Component"; import LeactDom from "../core/LeactDom"; import ArticlePage from "./ArticlePage"; class DetailPage extends Component { constructor(props) { super() this.article = ArticleService.getById(props.articleId) } render() { const {title, summary, detail} = this.article return `` } componentDidMount() { document.getElementById("back").addEventListener("click", () => { LeactDom.render(new ArticlePage(), document.getElementById("app")) }) } } export default DetailPage${title}
${summary}
${detail}
加載組件index.js
import LeactDom from "./core/LeactDom"; import ArticlePage from "./page/ArticlePage"; LeactDom.render(new ArticlePage(),document.getElementById("app"))
8 查看最終效果
0x007 總結(jié)這里要做的只是一個(gè)案例,而不是寫(xiě)一個(gè)完整的框架,所以在很多地方并沒(méi)有完善,只是為了驗(yàn)證實(shí)現(xiàn)SPA的方式,而結(jié)果也確實(shí)驗(yàn)證了。也將一些問(wèn)題暴露出來(lái)了,其他的問(wèn)題我們不關(guān)心,我們只關(guān)心我們之前提出的問(wèn)題,只有一個(gè)網(wǎng)址,如何將某個(gè)頁(yè)面分享出去,很明顯,做成SPA之后,無(wú)法將文章詳情頁(yè)面分享給他人。解決 方法也已經(jīng)給出來(lái)了:
hash
url
將在下一張講述如何解決
0x008 資源源碼
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/108512.html
摘要:說(shuō)起,其實(shí)早在出現(xiàn)之前,網(wǎng)頁(yè)就是在服務(wù)端渲染的。沒(méi)有涉及流式渲染組件緩存對(duì)的服務(wù)端渲染有更深一步的認(rèn)識(shí),實(shí)際在生產(chǎn)環(huán)境中的應(yīng)用可能還需要考慮很多因素。選擇的服務(wù)端渲染方案,是情理之中的選擇,不是對(duì)新技術(shù)的盲目追捧,而是一切為了需要。 作者:威威(滬江前端開(kāi)發(fā)工程師)本文原創(chuàng),轉(zhuǎn)載請(qǐng)注明作者及出處。 背景 最近, 產(chǎn)品同學(xué)一如往常笑嘻嘻的遞來(lái)需求文檔, 縱使內(nèi)心萬(wàn)般拒絕, 身體倒是很誠(chéng)實(shí)...
摘要:概述上一章講了如何實(shí)現(xiàn)組件頁(yè)面切換,這一章講如何解決上一章出現(xiàn)的問(wèn)題以及如何優(yōu)雅的實(shí)現(xiàn)頁(yè)面切換。在中監(jiān)聽(tīng)了事件,這樣就可以在變化的時(shí)候,需要路由配置并調(diào)用。 0x000 概述 上一章講了SPA如何實(shí)現(xiàn)組件/頁(yè)面切換,這一章講如何解決上一章出現(xiàn)的問(wèn)題以及如何優(yōu)雅的實(shí)現(xiàn)頁(yè)面切換。 0x001 問(wèn)題分析 回顧一下上一章講的頁(yè)面切換,我們通過(guò)LeactDom.render(new Articl...
摘要:路由模塊的本質(zhì)就是建立起和頁(yè)面之間的映射關(guān)系。這時(shí)候我們可以直接利用傳值了使用來(lái)匹配路由,然后通過(guò)來(lái)傳遞參數(shù)跳轉(zhuǎn)對(duì)應(yīng)路由配置于是我們可以獲取參數(shù)六配置子路由二級(jí)路由實(shí)際生活中的應(yīng)用界面,通常由多層嵌套的組件組合而成。 一、前言 要學(xué)習(xí)vue-router就要先知道這里的路由是什么?為什么我們不能像原來(lái)一樣直接用標(biāo)簽編寫(xiě)鏈接哪?vue-router如何使用?常見(jiàn)路由操作有哪些?等等這些問(wèn)...
摘要:路由模塊的本質(zhì)就是建立起和頁(yè)面之間的映射關(guān)系。這時(shí)候我們可以直接利用傳值了使用來(lái)匹配路由,然后通過(guò)來(lái)傳遞參數(shù)跳轉(zhuǎn)對(duì)應(yīng)路由配置于是我們可以獲取參數(shù)六配置子路由二級(jí)路由實(shí)際生活中的應(yīng)用界面,通常由多層嵌套的組件組合而成。 一、前言 要學(xué)習(xí)vue-router就要先知道這里的路由是什么?為什么我們不能像原來(lái)一樣直接用標(biāo)簽編寫(xiě)鏈接哪?vue-router如何使用?常見(jiàn)路由操作有哪些?等等這些問(wèn)...
摘要:路由模塊的本質(zhì)就是建立起和頁(yè)面之間的映射關(guān)系。這時(shí)候我們可以直接利用傳值了使用來(lái)匹配路由,然后通過(guò)來(lái)傳遞參數(shù)跳轉(zhuǎn)對(duì)應(yīng)路由配置于是我們可以獲取參數(shù)六配置子路由二級(jí)路由實(shí)際生活中的應(yīng)用界面,通常由多層嵌套的組件組合而成。 一、前言 要學(xué)習(xí)vue-router就要先知道這里的路由是什么?為什么我們不能像原來(lái)一樣直接用標(biāo)簽編寫(xiě)鏈接哪?vue-router如何使用?常見(jiàn)路由操作有哪些?等等這些問(wèn)...
閱讀 3263·2021-11-24 10:30
閱讀 1376·2021-09-30 09:56
閱讀 2477·2021-09-07 10:20
閱讀 2671·2021-08-27 13:10
閱讀 779·2019-08-30 11:11
閱讀 2122·2019-08-29 12:13
閱讀 813·2019-08-26 12:24
閱讀 2987·2019-08-26 12:20