系列文章的目錄在 ? 這裏javascript
(因爲 我比較懶 最近一段時間在忙其餘事,系列文章拖了很久終於又更新了。。。)html
vue-router 是針對 Vue.js 開發的前端路由工具,能夠很方便的開發單頁應用。vue
單頁應用的概念其實很早就出現了,它是指在同一個頁面內包含了應用的全部功能,一個頁面就是一個應用,整個應用只有一個頁面,是在 Web 場景下提出的一種開發方式。單頁應用的特性在文末討論,這裏先說用法。java
vue-router 是以 Vue.js 插件的形式存在的,使用前必需要引入 Vue.js。由於 WeexSDK (>= 0.9.5)中已經包含了 Vue.js Runtime,因此不須要再引入一遍 Vue.js ,只需引入 vue-router 並註冊便可:git
// import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter)
經過在模板中添加 <router-view>
的方式定義路由出口,路由匹配到的組件將會替換這個標籤。github
<!-- App.vue --> <template> <router-view></router-view> </template>
在模板中也能夠包含其餘標籤, <router-view>
也能夠是其餘標籤的子標籤,和其餘標籤的用法是同樣的;在 vue-router 的內部實現中,router-view 的實現就是一個普通的函數式組件。web
在嚮應用中註冊 router 以前,須要先建立路由實例,而且配置路由規則。vue-router
// router.js import VueRouter from 'vue-router' import HomeView from 'path/to/HomeView.vue' import AboutView from 'path/to/AboutView.vue' Vue.use(VueRouter) export default new VueRouter({ routes: [ { path: '/home', component: HomeView }, { path: '/about', component: AboutView } ] })
上述代碼中建立了 VueRouter 的實例,而且傳入了 routes
配置,當路徑是 home
時,頁面就會跳轉到 HomeView
組件,HomeView.vue 就會渲染到 App.vue 中 <router-view>
標籤的位置。同理,當路徑是 about
時,頁面就會跳轉到 AboutView
組件。vuex
想要在應用中注入路由功能,還有給入口組件添加 router 屬性,使應用和路由創建聯繫。
import App from 'path/to/App.vue' import router from 'path/to/router.js' App.el = '#root' App.router = router new Vue(App)
前邊提到過,單頁應用是在 Web 場景下提出的一種開發方式,它的具體實現依賴了 Web 平臺的功能,如 Histroy API 和 URL Hash 等特性。然而 Weex 的運行環境不僅是瀏覽器,一般是以移動端原生環境爲主,這些特性在 Weex 中並不徹底適用,參考《Weex 和 Web 平臺的差別》。
針對這些平臺差別,有如下兩點須要注意:
在 vue-router 的配置項中,有一個 mode 參數能夠用來指定 vue-router 的運行模式。
hash
: 使用 URL hash 值來做路由。默認模式。
history
: 依賴 HTML5 History API 和服務器配置。查看 HTML5 History 模式。
abstract
: 支持全部 JavaScript 運行環境,如 Node.js 服務器端。
根據平臺差別能夠看出,在 Weex 環境中只支持使用 abstract 模式。 不過,vue-router 自身會對環境作校驗,若是發現沒有瀏覽器的 API,vue-router 會自動強制進入 abstract 模式,因此 在使用 vue-router 時只要不寫 mode 配置便可,默認會在瀏覽器環境中使用 hash 模式,在移動端原生環境中使用 abstract 模式。 (固然,你也能夠明確指定在全部狀況下都使用 abstract
模式)
vue-router 支持使用 <router-link>
建立導航連接,不過在其中使用了基於 DOM 事件的一些特性,這些特性在 Weex 原生環境中並不能很好的工做。因此 在 Weex 中,你必須使用編程式導航來編寫頁面跳轉邏輯。 用法參考 Weex 官方文檔。
除了上述最基本的特性之外,vue-router 還有不少很強大的功能,具體使用方法建議參考其官方文檔,這裏再也不贅述。
在 weex-hackernews 項目中使用了 vue-router 作路由管理。
其中表頭的五個一級菜單(Top 、New 、Show 、Ask 、Job),就對應了五個不一樣的路由路徑,也對應了五個列表組件。除此以外,還有列表頁、評論頁、用戶信息頁也都對應了單獨的路由路徑。具體配置在 src/router.js 中,以下所示:
export default new Router({ routes: [ { path: '/top', component: createStoriesView('top') }, { path: '/new', component: createStoriesView('new') }, { path: '/show', component: createStoriesView('show') }, { path: '/ask', component: createStoriesView('ask') }, { path: '/job', component: createStoriesView('job') }, { path: '/article/:url(.*)?', component: ArticleView }, { path: '/item/:id(\\d+)', component: CommentView }, { path: '/user/:id', component: UserView }, { path: '/', redirect: '/top' } ] })
其中 article 、item 和 user 三個配置項中用到了動態路由匹配,把全部同類路由都映射到了同一個組件上,組件結構相同,可是參數是不一樣的(url 不一樣或者 id 不一樣)。
在最後還配置了路由重定向,將默認根路由 /
重定向到了 /top
,默認加載「最熱」文章列表。
若要應用組件和路由之間創建聯繫,須要給入口組件注入 router
屬性。代碼在 src/entry.js 中。
new Vue(Vue.util.extend({ router }, App)) router.push('/')
代碼在建立 Vue 實例前在 App 上添加了 router
屬性,使得應用組件中都能經過 this.$router
獲取到路由數據。在建立實例後,手動調用 router.push('/')
跳轉到根視圖。
使用 vuex-router-sync 能夠很方便地同步 Vuex 和 vue-router 的狀態,這個步驟並非必須的,可是能夠簡化代碼的使用。實際代碼在 src/entry.js 中,以下所示:
sync(store, router)
代碼的效果就是註冊了 store.state.route
這個變量,使得在 Vuex 的 Store 中能夠獲取到 route
對象,這在一些比較複雜的大型應用中可能會用到。
因爲在根組件中已經注入了
router
屬性,在全部組件中也均可以經過this.$router
獲取到當前的路由狀態。
如下是我我的的觀點,不表明任何人,也不表明 Weex。
不考慮技術細節,從實際應用的角度聊一下我對「單頁應用」的見解。
單頁應用也有很多的優點和缺陷:
優點:頁面無需刷新;能夠實現更好的過渡效果;組件複用;狀態共享。
缺陷:初次加載白屏時間長;不利於 SEO;有大量全局狀態;技術方法相對複雜,有學習成本。
大部分特性都已經有共識,我也再也不展開對比,這裏着重討論一下在 移動端 和 Weex 平臺 中使用單頁應用的狀況。
單頁應用運行在同一個 javascript runtime 裏,也就是說頁面的狀態都是共享的,環境變量也是相同的,這一點實際上是有不少隱患。在移動應用中,單頁應用不只耗費內存,也很容易發生內存泄露。
全部的 Weex 頁面,不管是基於 Vue 仍是 Rax,都共用一個 Weex Runtime,其中的 js 引擎也只初始化一次,除非從新初始化 Weex SDK(應用重啓),全部頁面對環境的操做痕跡都將保留。在這種情景下,頁面間狀態的隔離就尤其重要,共享的全局狀態極可能會成爲內存泄露的元兇。
換句話說,若是某個頁面建立了一個臨時的全局變量,可是在頁面退出後沒有清除,這個變量將一直保留在內存中;若是某個頁面在全局狀態上掛載了某個屬性,可是頁面退出後沒有斷開鏈接,這個屬性也會一直保留在全局狀態中。
原生應用自己就是多頁的場景,頁面間狀態的隔離比共享更重要一些。
單頁應用要把全部頁面用到資源(至少是腳本)提早打包在一塊兒,一次性所有發到客戶端,首次打開的網絡耗時會比較久,容易致使長時間白屏,很難遞進的加載資源,沒法作到「先加載有用的」。若是有多個單頁應用都須要用到某些頁面或者某個組件,是很難複用的,也很難加緩存,難不成整個移動應用都寫成一個大「單頁」的嗎? Web 上或許能夠考慮,可是原生應用裏十分不建議這樣作。應用都規模越大,這些缺陷就越明顯。
更況且如今 HTTP/2 逐漸普及,有了多路複用,把資源粒度劃分的細一些更有利於瀏覽器的加載。還有 prefetch 和 preload 這種特性能夠提高資源加載的效率,Weex 也在考慮支持。瀏覽器也逐漸開始支持 ES6 module,能夠直接經過 <script type="module">
的方式引入 ES6 模塊,代碼的管理和加載可能會以 模塊 爲單元,而不是頁面。
從一系列技術趨勢來看,如今有不少技術方案都講究將資源細分,巴不得使用 code split 把代碼細分到組件級別(「打包」是一個暫時性的讓人又愛又恨的工做)。
單頁應用在技術上基本上都是基於 Histroy API 或 hash 連接實現,即便不直接依賴,也會模擬這方面的行爲(polyfill)。前邊也提到過 Weex 和 Web 平臺有差別, Histroy API 和 hash 這些都是瀏覽器中的概念,和 Weex 裏不徹底對應。
使用 Weex 開發的是原生應用,頁面棧的管理使用的也是原生的特性,沒有 Histroy API 可是有 navigator 模塊 能夠實現頁面的「前進」和「後退」等操做。其實我以爲原生開發中 Navigator 的概念比 web 上 History API 設計的更完善一些(因爲多端行爲有差別,Weex 只暴露了部分通用功能),可操做性也更強。至於 hash,移動端不會輕易暴露頁面 url,更不用說 hash 了。
一句話總結: Weex 是跨平臺的,Histroy API 是針對 Web 平臺設計的,未必適合原生開發。
技術自己是客觀的,有各自的適用場景。weex-hackernews 項目自己的頁面也很少,多個頁面之間的確會共用一些數據;並且原本這就是一個範例項目,爲了展現能力和用法的,因此我用了 vue-router。在你的應用中是否應該引入 vue-router,須要結合應用自身的特性和需求具體分析。