2021-4-27 前端達人
不管哪種模式,前端路由都是客戶端路由的實現方式,也就是當路徑發生變化時,不會向服務器發送請求,是利用js監視路徑的變化。然后根據不同的地址渲染不同的內容,如果需要服務器內容,會發送Ajax請求來獲取。
https://music.163.com/#/discover/toplist
地址中會存在 # 號
https://music.163.com/discover/toplist
地址中沒有# 類似于普通的地址,但是需要服務端配置支持
在 node 環境下,啟用對history模式的支持可以通過 connect-history-api-fallback 這個中間件來完成
// 導入處理 history 模式的模塊 const history = require('connect-history-api-fallback') // 導入 express const express = require('express') const app = express() // 注冊處理 history 模式的中間件 app.use(history())
運行nginx服務器基本指令
啟動
start nginx
重啟
nginx -s reload
停止
nginx -s stop
location / { root html; index index.html index.htm; #新添加內容
#嘗試讀取$uri(當前請求的路徑),如果讀取不到讀取$uri/這個文件夾下的首頁
#如果都獲取不到返回根目錄中的 index.html
try_files $uri $uri/ /index.html; }
從上圖,可以大致了解一下 VueRouter 這個類中的結構:
上半部分是屬性,下半部分是方法,其中+ 是實例方法,- 是靜態方法。
install 是用來實現Vue.use 插件機制的方法。
要實現install方法,首先先分析一下該方法要做的事情:
let _Vue; export default class VueRouter { static install(Vue) { // 1. 判斷當前插件是否已經被安裝 if(VueRouter.install.installed) return VueRouter.install.installed = true // 2. 把Vue構造函數記錄到全局變量 _Vue = Vue // 3. 把創建Vue實例時候傳入的router對象注入到所有的Vue實例上 // 利用混入讓所有的vue實例加載router _Vue.mixin({ beforeCreate(){ // this.$options.name用來獲取vue實例 data以外的屬性 // new Vue( { router } ) if(this.$options.router) { _Vue.prototype.$router = this.$options.router } } }) } }
VueRouter 的構造函數要初始化三個屬性,分別是: options、data、routeMap。
constructor(options){ this.options = options this.data = _Vue.observable({ current:'/' }) this.routeMap = {} }
接下來我們來實現VueRouter類中 createRouterMap 這個方法,它的作用就是把 options 中rules 路由規則解析出來以鍵值對的形式存儲在routeMap上。
createRouterMap() { this.options.rules.forEach(route => this.routeMap[route.path] = route.component) }
下一步,來創建initComponents 方法,這個方法里我們要創建兩個組件。分別是:RouterLink 和 RouterView
let _Vue; export default class VueRouter { static install(Vue) { // 1. 判斷當前插件是否已經被安裝 if (VueRouter.install.installed) return VueRouter.install.installed = true // 2. 把Vue構造函數記錄到全局變量 _Vue = Vue // 3. 把創建Vue實例時候傳入的router對象注入到所有的Vue實例上 // 利用混入讓所有的vue實例加載router _Vue.mixin({ beforeCreate() { // this.$options.name用來獲取vue實例 data以外的屬性 // new Vue( { router } ) if (this.$options.router) { _Vue.prototype.$router = this.$options.router this.$options.router.init() } } }) } constructor(options) { this.options = options this.routeMap = {} this.data = _Vue.observable({ current: '/' }) } createRouterMap() { this.options.routes.forEach(route => this.routeMap[route.path] = route.component) } initComponents(Vue) { // 創建RouterLink組件 Vue.component('router-link', { props: { 'to': { type: String } }, template: `<a :href="to"><slot></slot></a>` }) } init() { this.createRouterMap() this.initComponents(_Vue) } }
用自己的VueRouter 替換掉官方的運行后,發現報錯
報錯的意思是,運行時版本的Vue 不支持 tempalte 模板,需要打包的時候提前編譯。
如果要讓我們的template被支持可以使用完整版的Vue,完整包包含運行時和編譯器,體積比運行時版本大10k左右,程序運行的時候把模板轉換成render函數
@vue/cli 自動安裝的就是 運行時版本
第一種方案——引入完整版Vue,可以在vue.config.js中 加入配置
module.exports = { runtimeCompiler: true }
第二種方案——使用render函數替換掉tempalte
render(h) { return h('a', { attrs: { href: this.to } }, [this.$slots.default]) } // template: `<a :href="to"><slot></slot></a>`
// 記錄一下this let self = this Vue.component('router-view',{ render(h){ // routeMap以key value形式記錄了path和component // data.current 記錄了當前頁面的path return h(self.routeMap[self.data.current]) } })
為了能夠讓鏈接成功完成跳轉展示組件,我們需要對routerlink中的a標簽添加點擊事件
并且要在點擊的時候,把最新的path更新到router實例的current上.
我們借助于history的pushState方法 該方法會修改瀏覽器地址欄中的地址,但不會向服務器發起請求,并且還可以將新地址記錄在歷史中
Vue.component('router-link', { props: { 'to': { type: String } }, render(h) { return h('a', { attrs: { href: this.to }, on: { click: this.clickHandle } }, [this.$slots.default]) }, methods: { clickHandle(e) { history.pushState({}, "", this.to) // 把點擊的鏈接地址 更新到 current 上 this.$router.data.current = this.to
e.preventDefault() } } // template: `<a :href="to"><slot></slot></a>` })
現在功能基本上已經差不多了,但是還存在一個小問題,就是當我們點擊瀏覽器的前進或者后退按鈕的時候,組件不能實現切換展示,主要思路就是通過添加popstate監聽地址變化,下面我們來完善該功能
initEvent(){ // window.addEventListener("popstate",()=>{ this.data.current = window.location.pathname }) }
完整代碼
let _Vue; export default class VueRouter { static install(Vue) { // 1. 判斷當前插件是否已經被安裝 if (VueRouter.install.installed) return VueRouter.install.installed = true // 2. 把Vue構造函數記錄到全局變量 _Vue = Vue // 3. 把創建Vue實例時候傳入的router對象注入到所有的Vue實例上 // 利用混入讓所有的vue實例加載router _Vue.mixin({ beforeCreate() { // this.$options.name用來獲取vue實例 data以外的屬性 // new Vue( { router } ) if (this.$options.router) { _Vue.prototype.$router = this.$options.router console.log(this.$options.router.init); this.$options.router.init() } } }) } constructor(options) { this.options = options this.routeMap = {} this.data = _Vue.observable({ current: '/' }) } createRouterMap() { this.options.routes.forEach(route => this.routeMap[route.path] = route.component) } initComponents(Vue) { // 創建RouterLink組件 Vue.component('router-link', { props: { 'to': { type: String } }, render(h) { return h('a', { attrs: { href: this.to }, on: { click: this.clickHandle } }, [this.$slots.default]) }, methods: { clickHandle(e) { history.pushState({}, "", this.to) // 把點擊的鏈接地址 更新到 current 上 this.$router.data.current = this.to
e.preventDefault() } } // template: `<a :href="to"><slot></slot></a>` }) let self = this Vue.component('router-view', { render(h) { // routeMap以key value形式記錄了path和component // data.current 記錄了當前頁面的path return h(self.routeMap[self.data.current]) } }) } init() { this.createRouterMap() this.initComponents(_Vue) this.initEvent() } initEvent() { // window.addEventListener("popstate", () => { this.data.current = window.location.pathname }) } }
轉自:csdn 作者:Holyforsaken_FHC
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
藍藍設計的小編 http://www.syprn.cn