摘要:的全稱是統(tǒng)一資源定位符英文,可以這么說,是一種標準,而網(wǎng)址則是符合標準的一種實現(xiàn)而已。渲染器,將組件渲染到頁面上。
0x000 概述
從這一章開始就進入路由章節(jié)了,并不直接從如何使用react-route來講,而是從路由的概念和實現(xiàn)來講,達到知道路由的本質(zhì),而不是只知道如何使用react-route庫的目的,畢竟react-route只是一個庫,是路由的一個實現(xiàn)而已,而不是路由本身。
0x001 URL的概念很多人對url的理解就是網(wǎng)址,我們在瀏覽器地址欄輸入網(wǎng)址,便可以訪問到特定網(wǎng)頁,但其實url的含義遠遠不止是網(wǎng)址。url的全稱是統(tǒng)一資源定位符(英文:Uniform Resource Locator),可以這么說,url是一種標準,而網(wǎng)址則是符合url標準的一種實現(xiàn)而已。
讓我們做幾個實驗:
打開瀏覽器,訪問segmentfault的主頁,此時地址欄顯示的是:
https://segmentfault.com
桌面新建from-url-to-spa.txt文件,輸入內(nèi)容from url to spa,并拖拽到瀏覽器,此時瀏覽器顯示的是
file:///Users/FollowWinter/Desktop/from-url-to-spa.txt
打開一個github項目,并選擇ssh訪問,我們可以得到以下地址:
git@github.com:followWinter/flex-layout.git
說明:其中,1訪問了一個網(wǎng)頁,2訪問了一個本地文件,3訪問了一個開源項目,從以上可以看出,url有多種用途各異的實現(xiàn),但是我們可以這么歸納,網(wǎng)絡上(包括本地和遠程)所有的的東西都看作資源,我們可以通過一種符合某種標準的格式來訪問這種資源,從而忽略設備類型(服務器、路由器、硬盤......)、網(wǎng)絡類型(遠程、本地......)、資源類型(文本、圖片、音樂、電影......),而這種標準就是url,也就是我對統(tǒng)一資源定位符的理解。
統(tǒng)一資源定位符的標準格式如下:
協(xié)議類型:[//服務器地址[:端口號]][/資源層級UNIX文件路徑]文件名[?查詢][#片段ID]
統(tǒng)一資源定位符的完整格式如下:
協(xié)議類型:[//[訪問資源需要的憑證信息@]服務器地址[:端口號]][/資源層級UNIX文件路徑]文件名[?查詢][#片段ID]0x002 spa是什么
SPA全稱是single page web application,也就是只有一個頁面的web應用程序,我們訪問一個網(wǎng)頁,能夠在這個網(wǎng)頁上完成所有的業(yè)務操作,我們就可以稱之為SPA,是和框架無關、技術無關的一個概念。并不是說用angular、vue、react實現(xiàn)的web應用才叫SPA,因為這些框架也可以在多頁應用中使用。
0x003 如何實現(xiàn)spa只要在一個頁面完成所有業(yè)務操作,就可以稱之為SPA了,所以實現(xiàn)所謂的SPA也很簡單,就是將原本多頁的步驟轉(zhuǎn)化為一個頁面就行了。
0x004 SPA和路由有啥關系啊回答:沒有關系。SPA不一定要使用路由,不使用也沒有關系,但是隨著單頁應用了擴大,將所有的邏輯都卸載一個頁面上,會導致邏輯爆炸,維護痛苦,所以在邏輯上又分為多個頁面,達到好維護的效果。
0x005 路由出現(xiàn)一開始是沒有路由的,但是做的應用多了,便有了路由。對于路由的需求有兩個:
維護上的需求,過多的邏輯寫在一個頁面上,容易混亂,所以用路由分離多帶帶邏輯和頁面。
狀態(tài)保存的需求,比如一個SPA,我們有文章和文章詳情頁,有一天我們需要分享一個文章,希望可以通過一個鏈接直接訪問到這篇文章。但是單頁應用是無狀態(tài)的,而網(wǎng)址又是唯一的,比如a.com/index.html,無法做到直接訪問詳情頁,所以就出現(xiàn)了一些方案:
hash:a.com/index.html#detail/1,訪問 id 為1的文章詳情頁
url:a.com/index/detail/1,訪問 id 為1的文章詳情頁
這樣我們就可以分享一篇文章給其他用戶了,方案1實現(xiàn)比較簡單,但是路由丑陋并且占用了 hash 符,頁面中就不能亂用 hash 符了。方案2好但是需要后端配合,實現(xiàn)也很簡單,不管這個 url 是什么,都返回單頁應用的 html 就好了。
0x006 實現(xiàn)簡單的SPA
架構:
組件,每個組件都是獨立的,可以渲染出自己的dom,并且可以綁定事件,擁有生命周期。
渲染器,將組件渲染到頁面上。
服務,做數(shù)據(jù)管理等一些邏輯服務。
項目初始化:
整個項目起始沒有啥特別的,只是支持了es6而已,而整個項目我們也將會用es6來實現(xiàn)
初始化項目及其目錄
+ 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") }) ] }
渲染器實現(xiàn)
渲染器的作用起始就是渲染組件而已,而每個組件都有一個render方法,該方法返回一個dom字符串,也就是說,渲染器的本質(zhì)就是將dom字符串掛載和卸載。
core/LeactDom.js
class LeactDom { static render(child, parent) { parent.innerHTML=child } } export default LeactDom
測試index.js
import LeactDom from "./core/LeactDom"; import LeactDom from "./core/LeactDom"; LeactDom.render(`這是一個p
`, document.getElementById("app")) document.getElementById("p").addEventListener("click", () => { LeactDom.render("這是一個span", document.getElementById("app")) })
查看瀏覽器
如圖,我們已經(jīng)實現(xiàn)了切換了,只需要將之封裝為組件就行了 ![圖片描述][1]
組件
core/Component.js
// 這是組件根類, 所有的組件都繼承這個根 class Component { // 返回 dom 字符串 render() { return "" } // dom 掛載上去以后 執(zhí)行該方法, 可以在這個方法上執(zhí)行 dom 查詢和事件綁定 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
查看效果
框架完成開始編寫服務
文章獲取服務service/AticleService.js
const articles = [ { id: 1, title: "Redux入門0x101: 簡介及`redux`簡單實現(xiàn)", summary: "簡介及`redux`簡單實現(xiàn)", detail: "詳情1" }, { id: 2, title: "Redux入門0x102: redux 栗子之 counter", summary: "redux 栗子之 counter", detail: "詳情2" }, { id: 3, title: "Redux入門0x103: 拆分多個 reducer", summary: "拆分多個 reducer", detail: "詳情3" }, { id: 4, title: "Redux入門0x104: Action Creators", summary: "Action Creators", detail: "詳情4" }, { id: 5, title: "Redux入門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
開始編寫自定義組件
文章列表組件
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 總結這里要做的只是一個案例,而不是寫一個完整的框架,所以在很多地方并沒有完善,只是為了驗證實現(xiàn)SPA的方式,而結果也確實驗證了。也將一些問題暴露出來了,其他的問題我們不關心,我們只關心我們之前提出的問題,只有一個網(wǎng)址,如何將某個頁面分享出去,很明顯,做成SPA之后,無法將文章詳情頁面分享給他人。解決 方法也已經(jīng)給出來了:
hash
url
將在下一張講述如何解決
0x008 資源源碼
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/108512.html
摘要:說起,其實早在出現(xiàn)之前,網(wǎng)頁就是在服務端渲染的。沒有涉及流式渲染組件緩存對的服務端渲染有更深一步的認識,實際在生產(chǎn)環(huán)境中的應用可能還需要考慮很多因素。選擇的服務端渲染方案,是情理之中的選擇,不是對新技術的盲目追捧,而是一切為了需要。 作者:威威(滬江前端開發(fā)工程師)本文原創(chuàng),轉(zhuǎn)載請注明作者及出處。 背景 最近, 產(chǎn)品同學一如往常笑嘻嘻的遞來需求文檔, 縱使內(nèi)心萬般拒絕, 身體倒是很誠實...
摘要:概述上一章講了如何實現(xiàn)組件頁面切換,這一章講如何解決上一章出現(xiàn)的問題以及如何優(yōu)雅的實現(xiàn)頁面切換。在中監(jiān)聽了事件,這樣就可以在變化的時候,需要路由配置并調(diào)用。 0x000 概述 上一章講了SPA如何實現(xiàn)組件/頁面切換,這一章講如何解決上一章出現(xiàn)的問題以及如何優(yōu)雅的實現(xiàn)頁面切換。 0x001 問題分析 回顧一下上一章講的頁面切換,我們通過LeactDom.render(new Articl...
摘要:路由模塊的本質(zhì)就是建立起和頁面之間的映射關系。這時候我們可以直接利用傳值了使用來匹配路由,然后通過來傳遞參數(shù)跳轉(zhuǎn)對應路由配置于是我們可以獲取參數(shù)六配置子路由二級路由實際生活中的應用界面,通常由多層嵌套的組件組合而成。 一、前言 要學習vue-router就要先知道這里的路由是什么?為什么我們不能像原來一樣直接用標簽編寫鏈接哪?vue-router如何使用?常見路由操作有哪些?等等這些問...
摘要:路由模塊的本質(zhì)就是建立起和頁面之間的映射關系。這時候我們可以直接利用傳值了使用來匹配路由,然后通過來傳遞參數(shù)跳轉(zhuǎn)對應路由配置于是我們可以獲取參數(shù)六配置子路由二級路由實際生活中的應用界面,通常由多層嵌套的組件組合而成。 一、前言 要學習vue-router就要先知道這里的路由是什么?為什么我們不能像原來一樣直接用標簽編寫鏈接哪?vue-router如何使用?常見路由操作有哪些?等等這些問...
摘要:路由模塊的本質(zhì)就是建立起和頁面之間的映射關系。這時候我們可以直接利用傳值了使用來匹配路由,然后通過來傳遞參數(shù)跳轉(zhuǎn)對應路由配置于是我們可以獲取參數(shù)六配置子路由二級路由實際生活中的應用界面,通常由多層嵌套的組件組合而成。 一、前言 要學習vue-router就要先知道這里的路由是什么?為什么我們不能像原來一樣直接用標簽編寫鏈接哪?vue-router如何使用?常見路由操作有哪些?等等這些問...
閱讀 3189·2021-11-24 10:30
閱讀 1312·2021-09-30 09:56
閱讀 2384·2021-09-07 10:20
閱讀 2596·2021-08-27 13:10
閱讀 697·2019-08-30 11:11
閱讀 2050·2019-08-29 12:13
閱讀 757·2019-08-26 12:24
閱讀 2897·2019-08-26 12:20