Vue 升級小記

WilliamsGeh 8年前發布 | 26K 次閱讀 Vue.js開發 Vue.js

Vue 升級小記

Vue 升級小記

最近接手了一個 Vue 1.0 的陳年老項目,需要將其升級到Vue 2.0。下面記錄一下升級過程:

安裝遷移工具

首先需要安裝 vue-migration-helper CLI 工具:

  1. 控制臺運行命令: npm install --global vue-migration-helper CLI 工具來幫助項目從Vue 1.x 遷移到 2.x。 它掃描文件以查找特定于 Vue 的代碼,并對需要升級的代碼提供詳細的警告。 vue-migration-helper 的介紹說明告訴我們它大概能捕獲 80% 的升級幫助信息,而不是全部。所以終端輸出的幫助信息并不是完全正確的,在升級時不要盲目 copy & paste ,還是要根據實際情況去改寫。
  2. 進入當前的項目:運行: vue-migration-helper

工具識別出了108 個需要升級的點:

Vue 升級小記

由于這個古董項目不是用 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 文檔 ,異同如下表格:

Vue 升級小記

因此主要的改動點是:使用 mounted 鉤子函數替換 ready 鉤子函數

4. transition

Vue 2.0 對動畫做了非常大的更新,原來的 transition 屬性已經被徹底廢棄掉了,而使用 transition 或 <transition-group> 來包裹元素去實現過渡效果,項目中關于動畫的代碼都要重新寫,包括 CSS、 HTML ,還有 Javascript 鉤子函數的改變。

在要升級的這個項目中,關于 transition 的升級差不多有10多個,其中大部分的改動都可以按照遷移工具去 copy & paste , 但是還是要清楚其中的異同,再去做修改。不然就會出現,錯誤提示消失了,但是動畫不生效的情況。

過渡CSS 變化

Vue 升級小記

舉個栗子:

// 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-by to :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

 

 本文由用戶 WilliamsGeh 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!