vue3和vue2在API使用上有哪些差別?

一.概要

文章內容有點多,加不了樣式了,你們將就看一下。javascript

上週,vue3 one piece正式版發佈了(話說尤大這個用動漫名定義版本號的習俗何時輪到火影啊啊啊),在給尤大瘋狂打call的同時,也是我們開始爆肝學習的時候了。html

我對比了vue2vue3,在這裏列舉出使用方法,功能或者已經廢棄的一些api。供一些vue2的同窗快速的瞭解和上手vue3。在這篇文章裏,只列出一些使用上有所差別的內容,不涉及到源碼方面。vue

除了安裝以及vue3一些全新的方法外,API方面我會按照vue2的官方api文檔的目錄順序來列舉。java

  • 安裝
  • vue3新內容
  • 應用配置(原全局配置和全局API)
  • 全局API
  • 選項/數據
  • 選項/DOM
  • 選項/生命週期鉤子
  • 選項/資源
  • 選項/組合
  • 選項/其它
  • 實例property
  • 實例方法/數據
  • 實例方法/事件
  • 實例方法/生命週期
  • 指令
  • 特殊attribute
  • 內置的組件

本人技術有限,這裏有些api可能我也不經常使用到或者沒怎麼使用過,因此若是有所錯誤或者遺漏,請在下面評論指出,我會及時進行修改。react

這裏是我我的的理解和記錄,如今vue3官方文檔已出,你們也能夠去官網查看vue2的遷移內容。程序員

二.安裝

項目的安裝如今有三種方式。vuex

  • 經過script引入
<script src="https://unpkg.com/vue@next"></script>
複製代碼
  • 腳手架vue-cli
// vue-cli在4.5.0的版本纔開始支持建立vue3項目
npm install -g @vue/cli
vue create hello-vue3
複製代碼
  • 腳手架vite
npm init vite-app hello-vue3
複製代碼

vite是尤大新的開源工具,借用尤大的原話vue-cli

一個基於瀏覽器原生 ES imports 的開發服務器。利用瀏覽器去解析 imports,在服務器端按需編譯返回,徹底跳過了打包這個概念,服務器隨起隨用。同時不只有 Vue 文件支持,還搞定了熱更新,並且熱更新的速度不會隨着模塊增多而變慢。針對生產環境則能夠把同一份代碼用 rollup 打包。雖然如今還比較粗糙,但這個方向我以爲是有潛力的,作得好能夠完全解決改一行代碼等半天熱更新的問題。npm

總的來講,就是js的引入使用了瀏覽器自身的es imports方法。開發模式下不須要打包,只須要編譯修改過的文件便可。生產環境下使用rollup打包。編程

這是一個新的方向,雖然目前還很稚嫩,並且對於ES imports瀏覽器的兼容仍是頗有問題,不過再過幾年,這可能也會變成一個主流的工具。

三.vue3新內容

1.Composition API

將數據、方法、computed、生命週期函數,集中寫在一個地方。

1.1 setup()

setup()做爲在組件內使用Composition API的入口點。執行時機是在beforeCreatecreated之間,不能使用this獲取組件的其餘變量,並且不能是異步。setup返回的對象和方法,均可以在模版中使用。

setup有兩個參數,props,context

<script lang="js">
import {toRefs} from 'vue'
export default {
    name: 'demo',
    props:{
      name: String,
    },
    setup(props, context){
      // 這裏須要使用toRefs來進行解構
      // 這裏的props與vue2基本一致,固然這裏的name也能夠直接在template中使用
      const { name }=toRefs(props);
      console.log(name.value);
      // 只能獲取到這三個屬性,也是不能使用ES6的解構
      // 屬性,同vue2的$attrs
      console.log(context.attrs);
      // 插槽
      console.log(context.slots);
      // 事件,同vue2的$emit
      console.log(context.emit);
  }
}
</script>
複製代碼

1.2 reactive

vue3將響應式的功能和vue代碼解耦,單獨做爲一個js庫來調用。(在其餘任何js運行的框架當中,均可以引入和使用vue3的這個響應式功能)。

<template>
  <div>{{ state.text }}</div>
  <button @click="test">測試一下?</button>
</template>
<script> import { reactive } from 'vue'; export default { setup() { // 經過reactive聲明一個可響應式的對象 const state = reactive({ text: "我帥嗎" }); // 經過reactive聲明一個可響應式的數組 const state1 = reactive([ text: "我帥嗎" ]); // 聲明一個修改方法 const test = () => state.text = '你很醜'; // 返回state和方法 return { state,test }; } }; </script>
複製代碼

這是一個跟vue2比較大的區別,使用上面的方法,才能實現數據的響應式。而後這裏的對象能夠隨意賦值,不會出現vue2中深層未定義的對象值賦值不能響應式的問題。由於如今vue的底層是用proxy實現的數據監聽。

注意:這裏的元素只能是對象或者數組,基本數據類型須要使用ref

1.3 ref

聲明響應式的基本數據類型

import { ref } from 'vue';
export default {
  setup(){
    const a = ref(1);
    const b = ref('1');
    const c = ref(true);
    // 聲明一個修改方法
    const test = () => {
      // 注意:在setup裏面修改ref的值是須要經過.value的,template作了解套,因此不須要。
      // ref 做爲 reactive的對象的值時不須要.value
      // ref 做爲 reactive的數組的值時須要.value
      a.value = 2;
      b.value = '1';
      c.value = false;
    };
    return { a, b, c, test };
  }
}
複製代碼

獲取DOM元素

<template>
  <div id="test" ref="testDom"></div>
</template>

<script> import { ref, onMounted } from 'vue'; export default { setup(){ const testDom = ref(null); //使用onMounted鉤子,相似在vue2的mounted生命週期中操做元素 onMounted(() => { // 記得要加.value console.log(testDom.value); }) return { testDom }; } } </script>
複製代碼

1.4 readonly

能夠傳入一個對象或ref,返回一個只讀的對象,深層次的只讀

const state = reactive({ name: 0 })
const readOnlyState = readonly(state)
// 能夠修改
state.name = '帥';
// 沒法修改並警告
readOnlyState.name = '帥';
複製代碼

2 reactive類工具API

2.1 isProxy, isReactive,isReadonly

isProxy是檢查一個對象是不是由 reactive 或者 readonly 方法建立的代理。後面兩個是單獨的檢測。 注意:只要檢測的對象裏有reactive,就算被readonly處理過了,isReactive也爲true。

2.2 toRaw

把被代理或者響應式的對象轉換成普通對象

const state = {}
const stateReactive = reactive(state)
console.log(toRaw(stateReactive) === state) // true
複製代碼

2.3 markRaw

禁止這個對象成爲響應式

const state = markRaw({ name: '帥' })
console.log(isReactive(reactive(state))) // false
複製代碼

注意,markRaw只做用於根元素,若是使用子元素去賦值,仍是能夠轉成響應式代理

const state = markRaw({
  data: { id: 123 },
})
const newState = reactive({
  data: state.data,
})
console.log(isReactive(newState.data)) // true
複製代碼

2.4 shallowReactive, shallowReadonly

跟上面reactive,readonly的做用同樣,區別是這兩個方法是淺層的監聽。 只會對第一層響應式,深層次的數據不會響應

const state = shallowReactive({ name: '123', test: { id: 1 } })
const test = () => {
  // 深層次數據的變化不會觸發響應式
  state.test.id = 2;
};
複製代碼

3 ref類工具API

3.1 isRef

檢查一個值是否爲一個ref對象

3.2 toRef

用來做爲一個reactive對象的屬性,保持響應式

const state = reactive({
  a: 0
})
// 做爲一個reactive對象的屬性
const aRef = toRef(state, 'a')
// 在這裏修改讀取Ref要使用.value
aRef.value++
console.log(state.a) // 1

state.a++
console.log(aRef.value) // 2
複製代碼

3.3 toRefs

將響應式對象轉換爲普通對象(相似解構),可是還能夠保留響應式。能夠用來對一些響應式變量屢次複用。

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })
  return toRefs(state)
}
export default {
  setup() {
    // 能夠在不失去響應式的狀況下複用
    const { foo, bar } = useFeatureX()
    return {
      foo,
      bar
    }
  }
}
複製代碼

3.4 unref

獲取ref值的語法糖

val = isRef(val) ? val.value : val
複製代碼

3.5 customRef

建立一個自定義的ref,對它的讀寫進行手動控制。這是文檔上的一個防抖的例子

<input v-model="text" />
複製代碼
function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        // 讀值的時候,觸發依賴
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          // 寫值的時候,進行防抖,觸發依賴
          trigger()
        }, delay)
      }
    }
  })
}

export default {
  setup() {
    return {
      text: useDebouncedRef('hello')
    }
  }
}
複製代碼

3.6 shallowRef, triggerRef

shallowRef數據是沒有響應式的,可是可使用triggerRef來手動觸發

const shallow = shallowRef({
  greet: 'Hello, world'
})
shallow.value.greet = 'Hello, universe'
triggerRef(shallow) //手動觸發響應式
複製代碼

4. computed and watch

4.1 computed

  • 傳入一個 getter 函數,返回一個默認不可手動修改的 ref 對象
  • 具備 get 和 set 函數的對象來建立可寫的 ref 對象
const count = ref(1)
// 只讀的
const plusOne = computed(() => count.value + 1)
// 可更改的
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  },
})

plusOne.value = 1
console.log(count.value) // 0
複製代碼

4.2 watchEffect

當即執行傳入的一個函數,並響應式追蹤其依賴,並在其依賴變動時從新運行該函數。

const count = ref(0)
watchEffect(() => console.log(count.value))
// 打印出 0,初始化的時候就會打印
setTimeout(() => {
  count.value++
  // -> 打印出 1
}, 100)
複製代碼

中止觀察

卸載組件的時候會自動中止,也能夠手動中止監聽

const stop = watchEffect(() => {
  /* ... */
})
// 中止監聽程序
stop()
複製代碼

反作用

若是一個函數內外有依賴於外部變量或者環境時,經常咱們稱之爲其有反作用,若是咱們僅經過函數簽名不打開內部代碼檢查並不能知道該函數在幹什麼,做爲一個獨立函數咱們指望有明確的輸入和輸出,反作用是bug的發源地,做爲程序員開發者應儘可能少的開發有反作用的函數或方法,反作用也使得方法通用性降低不適合擴展和可重用性

運行時機

// 初始化運行須要讀取dom
onMounted(() => {
  watchEffect(() => {
  })
})
//設置運行時機
watchEffect(
  () => {/* ... */},
  {flush: 'pre'}
)
// pre是默認,組件更新前執行
// post, 組件更新後執行
// sync, 同步運行
複製代碼

4.3 watch

跟vue2的watch一致

// 偵聽一個 getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 直接偵聽一個 ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})
複製代碼

監聽多個數據源

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})
複製代碼

與 watchEffect 共享的行爲

中止監聽,清除反作用,反作用刷新時機,偵聽器調試方面跟watchEffect參數是同樣的。

四.應用配置(原全局配置和全局API)

注意:全局配置的Vue都用如今的createApp生成的實例來替代

createApp(新增)

// vue2掛載dom方法
new Vue({
  render: (h) => h(App)
}).$mount("#app");
// vue3
createApp(App).mount('#app')
複製代碼

注意,以前用到的全局配置的Vue都用如今的createApp生成的實例來替代

import { createApp } from 'vue'
const app = createApp({})

// 好比註冊全局組件
app.component('my-component', {
  /* ... */
})
// 是否容許devtools檢查代碼
app.config.devtools = true  
複製代碼

globalProperties(新增)

app.config.globalProperties.foo = 'bar'
app.component('child-component', {
  mounted() {
    console.log(this.foo) // 'bar'
  }
})
複製代碼
  • 添加可在程序內的任何組件實例中訪問的全局屬性。當存在鍵衝突時,組件屬性將優先
  • 替代掉Vue2.x的 Vue.prototype屬性放到原型上的寫法

unmount(新增)

卸載DOM元素上應用程序實例的根組件

const app = createApp(App)
app.mount('#app')
// 5秒以後,卸載APP組件
setTimeout(() => app.unmount('#app'), 5000)
複製代碼

五. 全局API(新增)

createApp

相似以前全局的Vue變量,整個組件樹共享的上下文,第二個參數做爲props值傳入

h

就是以前的createElement。

jsx偶爾在用,可是這個底層的相對用的比較少,就不細講了

render() {
  return Vue.h('h1', {}, 'Some title')
}
複製代碼

defineComponent(組件)

建立一個組件

import { defineComponent } from 'vue'
const MyComponent = defineComponent({
  data() {
    return { count: 1 }
  },
  methods: {
    increment() {
      this.count++
    }
  }
})
複製代碼

defineAsyncComponent(異步組件)

建立一個異步組件

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
   /*或者*/
  import('./components/AsyncComponent.vue')
   /*或者*/
  new Promise((resolve, reject) => {
  /*能夠reject*/
      resolve({
        template: '<div>I am async!</div>'
      })
    })
)

app.component('async-component', AsyncComp)
複製代碼

resolveComponent

只能在rendersetup中使用,經過名稱來尋找組件

app.component('MyComponent', {
  /* ... */
})
const MyComponent = resolveComponent('MyComponent')
複製代碼

resolveDynamicComponent

只能在rendersetup中使用,解析活動的組件active

resolveDirective

只能在rendersetup中使用,容許經過名稱解析指令

withDirectives

只能在rendersetup中使用,容許應用指令到VNode。返回一個帶有應用指令的VNode

const bar = resolveDirective('bar')

return withDirectives(h('div'), [
  [bar, this.y]
])
複製代碼

createRenderer

接受兩個泛型參數:HostNodeHostElement,對應於宿主環境中的NodeElement類型。

這個東西不太清楚具體的使用場景

六.選項/數據

emits(新增)

在$emit觸發事件以前驗證參數

const app = Vue.createApp({})

// 對象語法
app.component('reply-form', {
  created() {
    this.$emit('check')
  },
  emits: {
    // 沒有驗證函數
    click: null,

    // 帶有驗證函數
    submit: payload => {
      if (payload.email && payload.password) {
        return true
      } else {
        console.warn(`Invalid submit event payload!`)
        return false
      }
    }
  }
})
複製代碼

七.選項/DOM(不變)

八.選項/生命週期鉤子

  • beforeDestroy -> beforeUnmount
  • destroyed -> unmounted
  • renderTracked(新增)

告訴你哪一個操做跟蹤了組件以及該操做的目標對象和鍵

renderTracked({ key, target, type }) {
  // 這裏的target爲更新以前的值,type是get
  console.log({ key, target, type })
}
複製代碼
  • renderTriggered(新增)

告訴你是什麼操做觸發了從新渲染,以及該操做的目標對象和鍵。 target爲更新以後的值,type是set

這些生命週期還能夠經過API在setup()裏使用

  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

九.選項/資源

filters(廢棄)

過濾器仍是有時候在用的

十.選項/組合

parent(廢棄)

這個屬性平時開發基本不會用到

setup(詳情見上面)

十一.選項/其它

delimiters(廢棄)

functional(廢棄)

使用了新的異步組件的生成方法

model(廢棄)

v-model修改了,不須要固定的屬性名和事件名了,手動去處理

comments(廢棄)

十二.實例property

vm.$attrs(修改)

如今$attrs不但會獲取父做用域中不做爲組件props的值,也能夠獲取到自定義事件(包含了$listeners的功能)。

vm.$children(廢棄)

vm.$scopedSlots(廢棄)

vm.$isServer(廢棄)

vm.$listeners(廢棄)

十三.實例方法/數據

vue3底層使用了proxy進行數據監聽,因此不須要這兩個方法了。

vm.$set(廢棄)

vm.$delete(廢棄)

十四.實例方法/事件

由於如今能夠直接在setup調用生命週期api,並且能夠引入別的js裏的方法來使用,因此這些方法也能夠不須要了。

vm.$on(廢棄)

vm.$once(廢棄)

vm.$off(廢棄)

十五.實例方法/生命週期

vm.$mount(廢棄)

統一使用createApp的mount方法來掛載

vm.$destroy(廢棄)

統一使用createApp的unmount方法來卸載

十六.指令

v-bind(修改)

  • .prop 去除
  • .sync 去除(如今須要手動去同步)
  • .camel 將 kebab-case attribute 名轉換爲 camelCase

v-model(修改)

對於組件能夠綁定多個屬性值

<template>
  <!--在vue3.0中,v-model後面須要跟一個modelValue,即要雙向綁定的屬性名-->
  <!-- 在Vue3.0中也能夠繼續使用`Vue2.0`的寫法 -->
  <a-input v-model:value="value" placeholder="Basic usage" />
</template>
複製代碼

自定義一個組件

<template>
  <div class="custom-input"> <input :value="value" @input="_handleChangeValue" /> </div>
</template>
<script> export default { props: { value: { type: String, default: "" } }, name: "CustomInput", setup(props, { emit }) { function _handleChangeValue(e) { // vue3.0 是經過emit事件名爲 update:modelValue來更新v-model的 emit("update:value", e.target.value); } return { _handleChangeValue }; } }; </script>
複製代碼

v-on(修改)

.{keyAlias} - 僅當事件是從特定鍵觸發時才觸發回調。再也不經過鍵碼的修飾符來觸發。

v-is(新增)

相似vue2中的:is綁定,可讓組件在某些特定的html標籤中渲染,好比table,ul。

<!-- 不正確,不會渲染任何內容 -->
<tr v-is="blog-post-row"></tr>
<!-- 正確 -->
<tr v-is="'blog-post-row'"></tr>
複製代碼

十七.特殊attribute

key(修改)

循環的時候,key要設置在template上。

<template v-for="item in list" :key="item.id">
  <div>...</div>
  <span>...</span>
</template>
複製代碼

ref(修改)

v-for裏使用的ref將不會再自動建立數組

解決方式:

<div v-for="item in list" :ref="setItemRef"></div>
複製代碼
export default {
  data() {
    return {
      itemRefs: []
    }
  },
  methods: {
    setItemRef(el) {
      this.itemRefs.push(el)
    }
  },
  beforeUpdate() {
    this.itemRefs = []
  },
  updated() {
    console.log(this.itemRefs)
  }
}
複製代碼

十八.內置的組件

transition(修改)

  • Props新增:

persisted - boolean 若是爲true,則表示這是一個轉換,實際上不會插入/刪除元素,而是切換顯示/隱藏狀態。 transition 過渡掛鉤被注入,但會被渲染器跳過。 相反,自定義指令能夠經過調用注入的鉤子(例如v-show)來控制過渡

enter-class ->enter-from-class

leave-class ->leave-from-class

  • 事件

before appear

teleport(新增)

將內容插入到目標元素中

<teleport to="#popup" :disabled="displayVideoInline">
  <h1>999999</h1>
</teleport>
複製代碼

to必填屬性,必須是一個有效的query選擇器,或者是元素(若是在瀏覽器環境中使用)。中的內容將會被放置到指定的目標元素中

disabled這是一個可選項 ,作一個是能夠用來禁用的功能,這意味着它的插槽內容不會移動到任何地方,而是按沒有teleport組件通常來呈現【默認爲false】

適合場景,全局的loading,多個內容合併等等等。

十九.總結

整體來講,vue3在儘量的兼容vue2的同時,又引入了全新的組合式API的編程方式,這種新的模式有點相似react的思想,解決了以前版本對於業務代碼難複用的問題,並且對一個頁面來講,不一樣的功能代碼能夠更好的去區分,不會有之前各類變量和方法擠在一堆,後期難於維護的問題。加上良好的ts支持,很好很強大。

周邊的一些組件vue Router4.0, vuex4.0也都提供了vue3支持。組件庫這塊,ant design vuevant已經支持了vue3

不過正如官方文檔中建議的,目前還不建議把一些重要的項目遷移到vue3當中,由於vue3還有不少須要完善的地方,並且目前還不支持ie,等兼容的工做完成,仍是隻能兼容到ie11。

後面就是一些開發中的比較好的實踐了,等過段時間,用vue3作具體項目以後再作總結吧。

二十.感謝

感謝您的耐心閱讀,寫文章不易,但願能給我點個贊,謝謝各位。

相關文章
相關標籤/搜索