Vue 升級小記

Vue 升級小記
最近接手了一個 Vue 1.0 的陳年老項目,需要將其升級到Vue 2.0。下面記錄一下升級過程:
安裝遷移工具
首先需要安裝 vue-migration-helper CLI 工具:
- 控制臺運行命令:
npm install --global vue-migration-helperCLI 工具來幫助項目從Vue 1.x 遷移到 2.x。 它掃描文件以查找特定于 Vue 的代碼,并對需要升級的代碼提供詳細的警告。vue-migration-helper的介紹說明告訴我們它大概能捕獲 80% 的升級幫助信息,而不是全部。所以終端輸出的幫助信息并不是完全正確的,在升級時不要盲目copy & paste,還是要根據實際情況去改寫。 - 進入當前的項目:運行:
vue-migration-helper
工具識別出了108 個需要升級的點:

由于這個古董項目不是用 Vue-CLI 構建的,為了避免在升級依賴上出錯,我直接新起了一個 Vue-CLI 項目,將老項目中的業務部分進行遷移,這個是最快最能避免踩坑的解決方案(當然,這么做是因為踩坑了,所以爬起來了=_=)與當前最新的 Vue-CLI 的依賴版本做了對比:
"vue": "^1.0.24"to"vue": "^2.5.2""vue-router": "^0.7.13"to"vue-router": "^3.0.1""vuex": "^2.4.0"to"vuex": "^3.0.1""webpack": "^2.4.1"to"webpack": "^3.6.0""vue-loader": "^8.5.2"to"vue-loader": "^13.3.0"
升級代碼
對于108 個需要升級的點來說,花費的時間遠比想象的要多,除了根據官方文檔進行遷移升級之外,運行項目還是會有很多報錯,下面總結了一下改動比較多的地方。
1. 過濾器
移除內置過濾器
Vue 2.0 不再提供內置過濾器。可以創建自己的過濾器或者引入外部庫如 moment.js , accounting.js 來對時間和貨幣等進行格式化。之前項目中用到的 orderBy 方法已經被棄用了,根據升級指南的建議,直接引入了 lodash 工具庫,并使用計算屬性重構。
// Vue 1.x
<div v-for="tag in productTags | orderBy 'location'">
{{ tag.tagTitle }}
</div>
// Vue 2.x
<div v-for="tag in productTags">
{{ tag.tagTitle }}
</div>
import orderBy from 'lodash/orderBy'
...
...
...
computed: {
productTags: function () {
return orderBy(this.tags, 'location')
}
}
...
過濾器參數符號變更
// Vue 1.x
<div class="time">{{ item.appDate | date 'yyyy-MM-dd' }}</div>
// Vue 2.x
<div class="time">{{ item.appDate | date('yyyy-MM-dd') }}</div>
2. Vue Router
vue-router 的改動是相對來說非常大的,大部分都可以參考 官方文檔 去修改,如:
router.go()改成了router.push()router.map()被廢棄,使用routes選項數組- 使用
router-link替換了v-link route.refresh改成了route.meta.refresh
對于 beforeEach 來說現在是異步工作的,并且攜帶一個 next 函數作為其第三個參數, beforeEach 經常用來設置頁面的 title ,而 Vue 2.0 to 函數的使用也有一些改變,如下:
// Vue 1.x
router.beforeEach(({ to, next }) => {
if (to.title) {
...
}
next()
})
// Vue 2.x
router.beforeEach((to, from, next) => {
if (to.meta.title) {
...
}
next()
})
對于路由掛載根實例的改動,Vue 2.0 不再會有一個特殊的 API 用來初始化包含 Vue Router的 app ,而只需要傳一個路由屬性給 Vue 實例,如下:
// Vue 1.x
router.map(routes)
router.start(Vue.extend({
store,
components: {
app: require('./app.vue')
}
}), 'body')
// Vue 2.x
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
3. 生命周期
生命周期鉤子也是這次升級中比較大的改進點,對照 vue1.0文檔 和 vue2.0 文檔 ,異同如下表格:

因此主要的改動點是:使用 mounted 鉤子函數替換 ready 鉤子函數
4. transition
Vue 2.0 對動畫做了非常大的更新,原來的 transition 屬性已經被徹底廢棄掉了,而使用 transition 或 <transition-group> 來包裹元素去實現過渡效果,項目中關于動畫的代碼都要重新寫,包括 CSS、 HTML ,還有 Javascript 鉤子函數的改變。
在要升級的這個項目中,關于 transition 的升級差不多有10多個,其中大部分的改動都可以按照遷移工具去 copy & paste , 但是還是要清楚其中的異同,再去做修改。不然就會出現,錯誤提示消失了,但是動畫不生效的情況。
過渡CSS 變化

舉個栗子:
// Vue 1.x :
<div v-if="isShow" transition="info-fade">
<span> hello world ! </span>
</div>
.info-fade-transition {
transition: all .3s ease;
}
.info-fade-enter, info-fade-leave {
opacity: 0;
}
// Vue 2.x
<transition name="info-fade">
<span v-if="isShow"> hello world ! </span>
</transition>
.info-fade-enter-active, info-fade-leave-active {
transition: all .3s ease;
}
.info-fade-enter, info-fade-leave {
opacity: 0;
}
Javascript 鉤子
Vue 2.0 transitions 能夠通過組件應用,它們不再只是一種單獨類型,因此全局的 Vue.transition() 方法和 transition 配置都被丟棄。現在可以通過組件的屬性和方法配置內嵌的過渡:
// Vue 1.x
Vue.transition('expand', {
beforeEnter: function (el) {
el.textContent = 'beforeEnter'
},
enter: function (el) {
el.textContent = 'enter'
},
...
})
// Vue 2.x
methods: {
// 過渡進入
// 設置過渡進入之前的組件狀態
beforeEnter: function (el) {
// ...
},
// 設置過渡進入完成時的組件狀態
enter: function (el, done) {
// ...
done()
},
...
}
5. Class 與 Style 綁定
// Vue 1.x
<div class="qa-item-question {{item.viewStatus === 1 ? 'isread' : ''}}"> </div>
如果使用 vue-migration-helper 工具,它會提示你將老的代碼替換成這樣:
// Vue 2.x
<div v-bind:class="'qa-item-question ' + item.viewStatus === 1 ? 'isread' : ''"></div>
然而由于運算符優先級問題,最后的結果可能會是 :
<div class=""></div>
因此,應該注意的不能完全依賴升級工具的提醒去直接 copy & paste 。
6. 雙向數據綁定
Replace :visible.sync="xxx" with :visible="xxx", then $emit an event from the child component to trigger an update to xxx in the parent
Vue 2.x 中,為了規范數據流動,砍掉了 .sync ,用來阻止子組件影響父組件所綁定的值,因為 .sync 破壞了單向數據流。但是很多情況下還是會需要雙向綁定的,比如 dialog 彈窗,當關閉時,將此狀態返回給父組件。
而 Vue 2.x 中,子組件只能被動接收父組件傳遞過來的數據,并在子組件內不能修改由父組件傳遞過來的 props 數據。每次父組件更新時,子組件的所有 prop 都會更新為最新值,因此不應該在子組件內部改變 prop ,如果我們嘗試直接修改 prop 屬性的值,就會有警告提示:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated:
"xxx" (found in component )
Vue2.0 的官方文檔也給出了解決方案:
定義一個局部變量,并用 prop 的值初始化它
而對于 .sync ,Vue 2.3 + v-bind 指令中將其引進,成為一枚語法糖,它會被擴展為一個自動更新父組件屬性的 v-on 監聽器。
7. $loadingRouteData
Vue 2.0 移除了 $loadingRouteData 鉤子。如果我們需要判斷頁面數據是否加載完成,需要自定義一個屬性(例如: isLoading)
8. 其他簡單的改動
- v-for
track-byto:key- 廢棄了
$index
- HTML 的計算插值
{{{ foo }}}已經移除,取代的是v-html指令 v-el和v-ref合并成ref屬性- 在 Vue 的實例中不能使用
Vue.set和Vue.delete - 使用
@click.native監聽根元素的原生事件,@click傳的只是一個方法 - 廢棄了
Array.prototype.$set/$remove,用Vue.set或者Array.prototype.splice取代
9. 其他報錯
瀏覽器報錯:
[Vue warn]: Do not use built-in or reserved HTML elements as component id: dialog
因為 dialog 在 HTML5 里面是個原生的標簽解決方法:重命名 components 里面組件的名稱
總結
上面只是簡單梳理了一下該項目在升級時遇到的一些問題,但并不是所有。單單是升級 webpack ,其實要修改的點就有很多,但是對于陳年的經過 N 手的老項目來說,并不適合直接升級,可能會造成項目更加混亂,因此這里走了一點捷徑,繞過了很多升級 webpack 會遇到的坑。升級遇到的大部分問題官方文檔都有詳細的描述,遇到問題,沉著冷靜別驚慌,我們的目標是:遠離 bug,不受傷。。。
來自:https://zhuanlan.zhihu.com/p/31436018