?? 簡述:Vue2和Vue3開發(fā)區(qū)別

  • 響應(yīng)式原理api的改變
    Vue2響應(yīng)式原理采用的是defineProperty,而vue3選用的是proxy。這兩者前者是修改對象屬性的權(quán)限標(biāo)簽,后者是代理整個(gè)對象。性能上proxy會更加優(yōu)秀。

  • diff算法,渲染算法的改變
    Vue3優(yōu)化diff算法。不再像vue2那樣比對所有dom,而采用了block tree的做法。此外重新渲染的算法里也做了改進(jìn),利用了閉包來進(jìn)行緩存。這使得vue3的速度比vue2快了6倍。

  • 建立數(shù)據(jù) data
    這里就是Vue2與Vue3 最大的區(qū)別 — Vue2使用選項(xiàng)類型API(Options API)對比Vue3合成型API(Composition API)
    舊的選項(xiàng)型API在代碼里分割了不同的屬性(properties):data,computed屬性,methods,等等。新的合成型API能讓我們用方法(function)來分割,相比于舊的API使用屬性來分組,這樣代碼會更加簡便和整潔。

區(qū)別于vue2組件寫法,我們在定義一個(gè)vue2的組件的時(shí)候,更多是通過一個(gè)對象來表達(dá)組件,像這樣:

而在vue3中,我們會通過方法的組合調(diào)用來完成組件的定義,像這樣:

vue2

export default {
  props: {
    title: String
  },
  data () {
    return {
      username: '',
      password: ''
    }
  }
}

Vue3 使用以下三步來建立反應(yīng)性數(shù)據(jù):

  1. 從vue引入reactive
  2. 使用reactive()方法來聲名我們的數(shù)據(jù)為反應(yīng)性數(shù)據(jù)
  3. 使用setup()方法來返回我們的反應(yīng)性數(shù)據(jù),從而我們的template可以獲取這些反應(yīng)性數(shù)據(jù)
import { reactive } from 'vue'

export default {
  props: {
    title: String
  },
  setup () {
    const state = reactive({
      username: '',
      password: ''
    })

    return { state }
  }
}
  • 創(chuàng)建一個(gè) template
    組件來說,大多代碼在Vue2和Vue3都非常相似。Vue3支持碎片(Fragments),就是說在組件可以擁有多個(gè)根節(jié)點(diǎn)。
    這種新特性可以減少很多組件之間的div包裹元素。在開發(fā)vue的時(shí)候,我們會發(fā)現(xiàn)每一個(gè)組件都會有個(gè)div元素包裹著。就會出現(xiàn)很多層多余的div元素。碎片(Fragments)解決了這個(gè)問題。

  • 在Vue3的唯一真正的不同在于數(shù)據(jù)獲取。Vue3中的反應(yīng)數(shù)據(jù)(Reactive Data)是包含在一個(gè)反應(yīng)狀態(tài)(Reactive State)變量中?!?所以我們需要訪問這個(gè)反應(yīng)狀態(tài)來獲取數(shù)據(jù)值。
    vue2

<template>
  <div class='form-element'>
    <h2> {{ title }} </h2>
    <input type='text' v-model='username' placeholder='Username' />
    <input type='password' v-model='password' placeholder='Password' />
    <button @click='login'>
      Submit
    </button>
    <p> 
      Values: {{ username + ' ' + password }}
    </p>
  </div>
</template>

vue3

<template>
  <div class='form-element'>
    <h2> {{ state.title }} </h2>
    <input type='text' v-model='state.username' placeholder='Username' />
    <input type='password' v-model='state.password' placeholder='Password' />
    <button @click='login'>
      Submit
    </button>
    <p> 
      Values: {{ state.username + ' ' + state.password }}
    </p>
  </div>
</template>
  • Vue2 對比 Vue3的 methods 編寫
    Vue2 的選項(xiàng)型API是把methods分割到獨(dú)立的屬性區(qū)域的。我們可以直接在這個(gè)屬性里面添加方法來處理各種前端邏輯。
export default {
  props: {
    title: String
  },
  data () {
    return {
      username: '',
      password: ''
    }
  },
  methods: {
    login () {
      // 登陸方法
    }
  }
}

Vue3 的合成型API里面的setup()方法也是可以用來操控methods的。創(chuàng)建聲名方法其實(shí)和聲名數(shù)據(jù)狀態(tài)是一樣的?!?我們需要先聲名一個(gè)方法然后在setup()方法中返回(return), 這樣我們的組件內(nèi)就可以調(diào)用這個(gè)方法了。

export default {
  props: {
    title: String
  },
  setup () {
    const state = reactive({
      username: '',
      password: ''
    })

    const login = () => {
      // 登陸方法
    }
    return { 
      login,
      state
    }
  }
}
  • 生命周期鉤子 — Lifecyle Hooks
    在 Vue2,我們可以直接在組件屬性中調(diào)用Vue的生命周期的鉤子。以下使用一個(gè)組件已掛載(mounted)生命周期觸發(fā)鉤子。
export default {
  props: {
    title: String
  },
  data () {
    return {
      username: '',
      password: ''
    }
  },
  mounted () {
    console.log('組件已掛載')
  },
  methods: {
    login () {
      // login method
    }
  }
}

現(xiàn)在 Vue3 的合成型API里面的setup()方法可以包含了基本所有東西。生命周期的鉤子就是其中之一!
但是在 Vue3 生周期鉤子不是全局可調(diào)用的了,需要另外從vue中引入。和剛剛引入reactive一樣,生命周期的掛載鉤子叫onMounted。

引入后我們就可以在setup()方法里面使用onMounted掛載的鉤子了。

import { reactive, onMounted } from 'vue'

export default {
  props: {
    title: String
  },
  setup () {
    // ..

    onMounted(() => {
      console.log('組件已掛載')
    })

    // ...
  }
}
  • 計(jì)算屬性 - Computed Properties
    我們一起試試添加一個(gè)計(jì)算屬性來轉(zhuǎn)換username成小寫字母。

在 Vue2 中實(shí)現(xiàn),我們只需要在組件內(nèi)的選項(xiàng)屬性中添加即可

export default {
  // .. 
  computed: {
    lowerCaseUsername () {
      return this.username.toLowerCase()
    }
  }
}

Vue3 的設(shè)計(jì)模式給予開發(fā)者們按需引入需要使用的依賴包。這樣一來就不需要多余的引用導(dǎo)致性能或者打包后太大的問題。Vue2就是有這個(gè)一直存在的問題。

所以在 Vue3 使用計(jì)算屬性,我們先需要在組件內(nèi)引入computed。

使用方式就和反應(yīng)性數(shù)據(jù)(reactive data)一樣,在state中加入一個(gè)計(jì)算屬性:

import { reactive, onMounted, computed } from 'vue'

export default {
  props: {
    title: String
  },
  setup () {
    const state = reactive({
      username: '',
      password: '',
      lowerCaseUsername: computed(() => state.username.toLowerCase())
    })

    // ...
  }
  • 接收 Props
    接收組件props參數(shù)傳遞這一塊為我們帶來了Vue2和Vue3之間最大的區(qū)別?!猼his在vue3中與vue2代表著完全不一樣的東西。

在 Vue2,this代表的是當(dāng)前組件,不是某一個(gè)特定的屬性。所以我們可以直接使用this訪問prop屬性值。就比如下面的例子在掛載完成后打印處當(dāng)前傳入組件的參數(shù)title。

mounted () {
    console.log('title: ' + this.title)
}

但是在 Vue3 中,this無法直接拿到props屬性,emit events(觸發(fā)事件)和組件內(nèi)的其他屬性。不過全新的setup()方法可以接收兩個(gè)參數(shù):

props - 不可變的組件參數(shù)
context - Vue3 暴露出來的屬性(emit,slots,attrs)
所以在 Vue3 接收與使用props就會變成這樣:

setup (props) {
    // ...

    onMounted(() => {
      console.log('title: ' + props.title)
    })

    // ...
}
  • 事件 - Emitting Events
    在 Vue2 中自定義事件是非常直接的,但是在 Vue3 的話,我們會有更多的控制的自由度。

舉例,現(xiàn)在我們想在點(diǎn)擊提交按鈕時(shí)觸發(fā)一個(gè)login的事件。

在 Vue2 中我們會調(diào)用到this.$emit然后傳入事件名和參數(shù)對象。

login () {
      this.$emit('login', {
        username: this.username,
        password: this.password
      })
 }

但是在 Vue3中,我們剛剛說過this已經(jīng)不是和vue2代表著這個(gè)組件了,所以我們需要不一樣的自定義事件的方式。

那怎么辦呀?! ??益?)?

不用慌,在setup()中的第二個(gè)參數(shù)content對象中就有emit,這個(gè)是和this.$emit是一樣的。那么我們只要在setup()接收第二個(gè)參數(shù)中使用分解對象法取出emit就可以在setup方法中隨意使用了。

然后我們在login方法中編寫登陸事件:

setup (props, { emit }) {
    // ...

    const login = () => {
      emit('login', {
        username: state.username,
        password: state.password
      })
    }

    // ...
}

vue2

<template>
  <div class='form-element'>
    <h2> {{ title }} </h2>
    <input type='text' v-model='username' placeholder='Username' />
    
    <input type='password' v-model='password' placeholder='Password' />

    <button @click='login'>
      Submit
    </button>
    <p> 
      Values: {{ username + ' ' + password }}
    </p>
  </div>
</template>
<script>
export default {
  props: {
    title: String
  },
  data () {
    return {
      username: '',
      password: ''
    }
  },
  mounted () {
    console.log('title: ' + this.title)
  },
  computed: {
    lowerCaseUsername () {
      return this.username.toLowerCase()
    }
  },
  methods: {
    login () {
      this.$emit('login', {
        username: this.username,
        password: this.password
      })
    }
  }
}
</script>

vue3

<template>
  <div class='form-element'>
    <h2> {{ state.title }} </h2>
    <input type='text' v-model='state.username' placeholder='Username' />
    
    <input type='password' v-model='state.password' placeholder='Password' />

    <button @click='login'>
      Submit
    </button>
    <p> 
      Values: {{ state.username + ' ' + state.password }}
    </p>
  </div>
</template>
<script>
import { reactive, onMounted, computed } from 'vue'

export default {
  props: {
    title: String
  },
  setup (props, { emit }) {
    const state = reactive({
      username: '',
      password: '',
      lowerCaseUsername: computed(() => state.username.toLowerCase())
    })

    onMounted(() => {
      console.log('title: ' + props.title)
    })

    const login = () => {
      emit('login', {
        username: state.username,
        password: state.password
      })
    }

    return { 
      login,
      state
    }
  }
}
</script>

感謝文章: https://zhuanlan.zhihu.com/p/139590941

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容