Troubleshooting of upgrading Vue from 1.0 to 2.0vue
系列文章:react
Vue 2.0 升(cai)級(keng)之旅 (本文)webpack
從單頁應用(SPA)到服務器渲染(SSR)angularjs
本文不包含 Vue 2.0 全部新特性,如 SSR 等,本文並無涉及,本文只包含 我的博客項目 升級中所遇到的經驗分享,若有興趣,能夠查看 Vue 2.0 changes log。github
這節淨是些嘮叨,只想看升(tian)級(keng)的可直接跳過。web
從去年年末開始寫博客,那時對怎麼搞個博客網站一竅不通,看別人用 Github Pages 寫博客挺讚的,就也想搞個玩玩。技術選型時,在 jekyll 和 hexo 中選擇了前者,或許你會問爲何?估計當時大腦的供氧量不足了吧...vue-router
因而,個人博客就這麼誕生了。(jekyll 版的博客已經廢棄了,若是你有興趣,能夠查看以前的提交)docker
但是,用久了就發現並不怎麼好用,雖然支持 markdown,可代碼塊要轉換成 highlighter 標籤;其次,主題模板是挺好看,可換成中文字雜就那麼彆扭哪;還有,對 jekyll 的模板又不熟,自定義也不方便。npm
年初有一天,忽然想到本身也是搞技術的,爲啥不本身搭一個博客網站哪?對,順帶還能學學新技術,何樂而不爲。又到了技術選型的時候了,此次擺在我面前又有 2 個選擇,React 和 Vue,此次我選擇了後者。
Why?由於,後者更輕量級,也更貼近我熟悉的 Angular 的語法,還有,那時網上就有說今年 4 月 Vue 會升級到 2.0 和 Vue 兼具 React 和 Angular 的優勢等等。(好吧,老實說,不選 React 只是由於不喜歡 JSX 而已。-_-||)
So,我就用 Vue 1.10+ 搭建了本身的新博客——Disciple.Ding Blog(點這裏看源碼),並漸漸地往裏添加一些新學到的東西,ES6, webpack, docker 等,並在 DAOcloud 上發佈了。(免費用了人家那麼久的服務,在這裏作個硬廣也是應該的,DAOcloud 的確很好用,特別和 Github 綁定以後能自動構建,應用更新也及其簡單,只是有個缺點就是有帶寬限制。)
在不久以前,Vue 如約發佈了 2.0 版本。正如計劃之初,博客 Vue 的版本也將升級到 2.0。
說了那麼多,再不進入正題就要變成標題黨了。好,那就開始咱們的升(cai)級(keng)之旅。
首先,升級依賴。
npm install vue@next vue-router@next --save
順利安裝完成並按 changelog 作了修改以後,啓動項目也正常,當我興致勃勃地打開 Browser,得心應手地輸入 localhost,並天然而然地按下 Enter,一切水到渠成。
然而,迎接個人竟是一片白板,控制檯裏赫然映着一串紅字。
[Vue warn] : You are using the runtime-only build of Vue where the template option is not available. Either pre-compile the templates into render functions, or use the compiler-included build. (found in root instance)
What? template 選項不能用了,changelog 沒提到啊?但 vue-router 的例子中都在用啊,什麼鬼?甚至我將代碼所有替換成例子中的代碼依舊沒法運行,但在 vue-router 項目裏就能跑,什麼鬼啊!
可是,我並不妥協,分別打斷點運行,發現二者居然跑的不是同一段代碼,納尼!
import vue from 'vue'
一樣的 import
語句,卻有不同的結果,vue-router 中引的是 vue.js,而在個人項目中引的居然是 vue.common.js...common...mon...n...
爲何會引 vue.common.js,from 'vue'
不應引的是 vue.js 麼?這就要引入另外一個知識點:package.json。
package.json 中的 main
屬性決定了,當項目被引入時,輸出的是哪一個文件,而 vue 的 package.json 中的 main
指向的是 dist/vue.common.js
。
福利時間:推薦一個網站 json.is,它對 package.json 裏的每條屬性都有詳細的解釋。
找到了問題產生的緣由,那麼解決也就垂手可得了。
import vue from 'vue/dist/vue.js'
每次引用 vue 的時候都要寫那麼長,一點都不優雅,並且爲何 vue-router 的例子能夠用啊?
我要一探究竟。確認了 vue-router 中依賴的 vue 的 package.json 文件中的 main
字段指向的也是 dist/vue.common.js
。那就只有一個可能了,webpack 對引入作了處理,查看 webpack.config.js
module.exports = { // 省略... resolve: { alias: { 'vue': 'vue/dist/vue.js' } }, ...
果真啊~他用 webpack 的別名功能把 vue/dist/vue.js
命名成了 vue,防不勝防。
在本身項目的 wepack.config.js 裏一樣給 vue 起別名,這樣就又能愉快地使用 import vue from 'vue'
了。
你是否是覺得這樣就結束了?不,對待一個問題要刨根問底,不能不求甚解。
爲何 vue 默認導出的是 vue.common.js,它和 vue.js 的區別在哪裏,又有什麼關係?
這個問題在囧克斯的博客中有提到。
Vue 最先會打包生成三個文件,一個是 runtime only 的文件 vue.common.js,一個是 compiler only 的文件 compiler.js,一個是 runtime + compiler 的文件 vue.js。
也就是說,vue.js = vue.common.js + compiler.js
,而若是要使用 template
這個屬性的話就必定要用 compiler.js,那麼,引入 vue.js 是最恰當的。
vue-router 的升級並不困難,參照 Releases Note 上的註釋修改應該沒有什麼大問題,主要的變化有兩點:
路由配置從一系列的方法調用,變成了傳遞一個配置對象
原先的 v-link
指令,變成了 router-link
Component,路徑指向用 to
屬性
正當你覺得會一帆風順順水,輕鬆升級路由完成的時候,現實總會給你當頭一棒。
以前博客的 vue-router 中使用了 beforeEach
和 afterEach
方法,根據 Release Note
router.beforeEach (replaced by the beforeEach option)
router.afterEach (replaced by the afterEach option)
行,那我把它改到配置裏
const ROUTER_SETTING = { routes: [ // 省略... ], beforeEach: () => { /* some function */ }, afterEach: () => { /* some function */ } }
But, not work. What's wrong?
難道我哪裏寫錯了?又通過我一番谷哥和查閱文檔以後,發如今下一個版本的 Release Note 中有這麼一段
beforeEach and afterEach are reverted as router instance methods (options removed). This makes it more convenient for plugins/modules to add hooks after the router instance has been created.
好吧,它又被恢復迴路由實例的方法了。那麼,改回去
const router = new VueRouter(ROUTER_SETTING); router .beforeEach(() => { /* some function */ }) .afterEach(() => { /* some function */ });
OK,這樣總好了吧。然而,並無...console 中報出沒法從 undefined
中讀取 afterEach
,好吧,我猜這應該是 beforeEach
中沒有像以前同樣返回路由對象,因此不能鏈式調用。
class VueRouter { // 省略... beforeEach (fn: Function) { this.beforeHooks.push(fn) } afterEach (fn: Function) { this.afterHooks.push(fn) } // 省略... }
看一眼源碼,果真如此。
那再將以前的代碼稍做修改就能夠了。
const router = new VueRouter(ROUTER_SETTING); router.beforeEach(() => { /* some function */ }); router.afterEach(() => { /* some function */ });
不過,不能鏈式調用彷佛沒以前的優雅了哪~
最後,提一下 vue-router 2.0 裏全部的 hook(就像以前的 beforeEach
, afterEach
,以及每一個路由狀態中的 beforeEnter
, beforeRouteLeave
等)都具備相同的參數簽名,這在 Release Note 中也有提到。
fn (toRoute, redirect, next) { // toRoute: {Object} 當前路由對象 // redirect: {Function} 調用跳轉至另外一路由 // next: {Function} 調用繼續當前路由跳轉 // 什麼都不作,則取消當前跳轉 }
路由升級完成後,若是控制檯沒有什麼報錯,那麼,路由能夠相互切換了,那些不依賴數據讀取的組件已經能夠正常顯示了。
那些依賴數據讀取的組件哪?
這就要提到組件的生命週期鉤子(即 lifecycle hooks)。
生命週期鉤子應該算 vue 此次升級中 broken changes 最多的一部分了,對照 1.0 的文檔和 release note,做了下面這張表
vue 1.0+ | vue 2.0 | Description |
---|---|---|
init | beforeCreate | 組件實例剛被建立,組件屬性計算以前,如 data 屬性等 |
created | created | 組件實例建立完成,屬性已綁定,但 DOM 還未生成,$el 屬性還不存在 |
beforeCompile | beforeMount | 模板編譯/掛載以前 |
compiled | mounted | 模板編譯/掛載以後 |
ready | mounted | 模板編譯/掛載以後(不保證組件已在 document 中) |
- | beforeUpdate | 組件更新以前 |
- | updated | 組件更新以後 |
- | activated | for keep-alive ,組件被激活時調用 |
- | deactivated | for keep-alive ,組件被移除時調用 |
attached | - | 不用了還說啥哪... |
detached | - | 那就不說了吧... |
beforeDestory | beforeDestory | 組件銷燬前調用 |
destoryed | destoryed | 組件銷燬後調用 |
知道了 hooks 升級先後的對應關係,那麼升級起來就垂手可得了,改改組件的屬性名就能夠了。
那麼,改完屬性名是否是就完成了?然而並無。
由於,在 vue 1.0+ 中,若是一個組件和路由相關,那麼,它就可能不僅僅有本身組件的 lifecycle hooks,它還會有基於 vue-router 的 lifecycle hooks。
而在 vue 2.0 中,router lifecycle hooks 所有被移除了,由於,這些 hooks 能夠經過其餘的方式來代替,這樣不但簡化了配置,還不用在組件中去處理路由相關的業務,下降了耦合。那這些 hooks 該如何替換,咱們接下來就來看一下。
activate
& deactivate
:使用組件自身的 lifecycle hook 替代
data
:經過組件 watch
屬性來監聽當前路由 $route
的變化
canActivate
:由路由屬性 beforeEnter
來代替
canDeactivate
:由路由屬性 beforeRouteLeave
來代替
canReuse
:去除
那個這個是否是也直接改改屬性名就行了哪?
恩,差很少。不過須要注意的是,若是原先 hooks 中使用了有關路由信息的 transition
參數是確定不能用了。好比,根據路由參數來進行查詢,原先經過 transition.to.params
獲取路由參數,如今就要經過剛剛提到的當前路由對象 this.$route.params
來獲取。
在升級這裏的過程當中,還遇到一個問題:當用戶輸入的 URL 知足路由匹配,但根據路由參數沒法得到正確的文章時,我想讓路由直接跳轉到首頁。
在 1.0 版本中,我經過 transition.redirect('/');
就輕鬆的回到了首頁,因爲 2.0 中沒有 transition
參數,而 $route
只包含當前路由的信息,並不包換路由切換的操做。那該怎麼作哪?再一次谷哥和查閱文檔,然而一無所得。
最後在 vue-router 的例子中找到了解決問題的鑰匙——$router
。
$router
返回的是整個項目路由的實例,它是隻讀的。因而,剛剛那個問題就能夠經過 this.$router.replace('/');
來解決。
這裏還有一點,在 1.0 版本中組件配置 route 屬性時還能夠設置一個叫 waitForData
的屬性。這個在 2.0 中,我尚未找到直接的替換方式,不過,我在整個組件上添加 v-if
來處理。從理論和效果的角度上講,v-if
是能夠替代原先的 waitForData
屬性,就彷佛不那麼優雅。
剩餘其餘小點,看控制檯報錯信息,而後查查 Release Note 都能輕鬆處理啦~
若是如今再讓我選一個技術來搭博客的話,我會選 React。爲啥?
由於 vue 我已經玩過啦,哈哈哈~
最後,借用外國網友的一句話:
I'm constantly rewriting / refactoring this silly little blog using the latest and buzziest tech, so that I can stay up to date on these libraries and frameworks.
這也是我本身搭博客,而不是直接使用博客系統的主要緣由。
最後的最後,安利下本身的 Blog,以及 Source Code。
歡迎交流,噴子繞道。