vue3.0 Composition API上手初体验

vue3.0 对比 vue2.0 优点

  • vue3.0 相比 vue2.0 性能提高了近 50%,框架内部作了大量性能优化,包括:虚拟dom,编译模板,Proxy 的新数据监听,更小的打包文件等
  • vue3.0 新的组合式 API(即 Composition API)根据逻辑相关性组织代码,相比于 vue2.0 的 Options API,提升了代码的可读性和可维护性,更适合大型项目的编写方式
  • vue3.0 对 Typescript 支持更好,去除了繁琐的 this 操做,更强大的类型推导

vue3.0 对比 vue2.0 语法区别

  • vue3.0 支持组件中使用多个根节点,能够减小节点层级深度,也但愿开发者可以明确语义javascript

  • vue3.0 删除了过滤器(filters),将不在支持,官方建议使用计算属性(computed)替换过滤器html

  • vue3.0 暴露出不少 API 供开发者使用,能够根据需求,将所须要的 API 从 Vue 中导入。考虑到 tree-shaking,利用了 import 和 export 的语法,实现了按需打包模块的功能vue

  • vue3.0 中新增了一个新的全局 API createApp,调用 createApp 返回一个应用实例,该应用实例暴露出来全局 API(vue3.0 将能够全局改变 Vue 行为的 API 从原来的 Vue 构造函数上转移到了实例上了)java

    //vue2.0建立import Vue from 'vue'import App from './App.vue'
      Vue.config.productionTip = false
      new Vue({  render: h => h(App),
    }).$mount('#app')  
      
    //vue3.0建立import { createApp } from 'vue'import App from './App.vue'
      createApp(App).mount('#app');复制代码

vue3.0 对比 vue2.0 响应式

Object.defineProperty 只能监听到属性的读写,而 Proxy 除读写外还能够监听属性的删除,方法的调用等react

  • vue2.0 数据响应式的原理是经过遍历 data 属性,利用 Object.definePrototype 将其转化成 setter/getter,在数据变更时发布消息给订阅者,触发相应的监听回调,因为 js 的限制,vue 不能检测数组和对象属性的添加和删除
  • vue3.0 基于 Proxy 来作数据的劫持代理,能够原生支持到数组的响应式,不须要重写数组的原型,还能够直接检测数组和对象属性的新增和删除(解决了 vue2.0 不能检测数组和对象属性的添加和删除的问题)

vue3.0 对 Typescript 支持更好

  • Typescript 是 javascript 类型的超集,它能够编译成纯的 javascript
  • Typescript 能够在任何浏览器、任何计算机和任何操做系统上运行,而且是开源的
  • Typescript 能够提供静态类型检查,规范团队的编码及使用方式,适合大型项目的开发
  • IDE 的友好提示,也是适合大型项目的开发

vue3.0 经常使用 API

Vue 组合式 API:composition-api.vuejs.org/zh/api.html…git

setup

setup 函数是一个新的组件选项,做为在组件内使用 Composition API 的入口函数,变量、方法都定义在该函数中github

  • setup 函数代替了 beforeCreate 和 created 两个生命周期函数,在生命周期 beforeCreate 以前被调用
  • this 在 setup() 中不可用,直接使用声明的变量名来访问数据(因为 setup() 在解析 2.x 选项前被调用,setup() 中的 this 将与 2.x 选项中的 this 彻底不一样。同时在 setup() 和 2.x 选项中使用 this 时将形成混乱)
  • setup 函数有两个参数:props 和 context:
    • props 对象为当前组件容许外界传递过来的参数名称以及对应的响应式的值
    • context 是一个上下文对象,暴露出三个属性:attrs 、slots、emit
  • setup 函数内定义的变量使用 return 暴露出去,供 template 中使用
<template>   <div>  {{ count }}
      {{ obj.count }}   </div></template><script>//引入须要的APIimport { ref, reactive } from 'vue'export default {   name: 'home',   setup() {      const count = ref(0)      export let obj = reactive({ count: 0 })      
      //经过return暴露出去,供template中使用  return {
         count,
         obj
      }
   }
}</script><style scoped>
  </style>复制代码

使用 <script setup>vue-router

  • < script> 标签具备 setup 属性时,组件在编译的过程当中代码运行的上下文是 setup() 函数中
  • 定义的变量使用 export 暴露出去,供 template 中使用
<template>   <div>  {{ count }}
      {{ obj.count }}   </div></template><script setup>//引入须要的APIimport { ref, reactive } from 'vue'//经过export暴露出去,供template中使用export let num = ref(0)export let obj = reactive({ count: 0 })</script><style scoped>
  </style>复制代码

reactive

reactive 接收一个普通对象而后返回该普通对象的响应式代理,建立一个响应式的数据对象vue-cli

<template>   <div>  {{ obj.count }}   </div></template><script>//引入须要的APIimport { reactive } from 'vue'export default {   name: 'home',   setup() {      //经过reactive定义响应式对象  const obj = reactive({ count: 0 }) 

      //经过return暴露出去,供template中使用  return {
         obj
      }
   }
}</script>复制代码

ref

ref 接受一个参数值并返回一个响应式且可改变的 ref 对象。ref 对象拥有一个指向内部值的单一属性 .valuenpm

  • 能够简单地把 ref(obj) 理解为 reactive({ value: obj })
  • 在 setup 函数中访问 ref 包装后的对象时才须要使用 .value,在 template 模板中访问会自动识别其是否为 ref 包装过,无需在模板内额外书写 .value
<template>   <div>  {{ count }}   </div></template><script>//引入须要的APIimport { ref } from 'vue'export default {   name: 'home',   setup() {      //经过ref定义响应式对象  const count = ref(0) 

      //经过value属性访问值  console.log(count.value)   //0  //经过return暴露出去,供template中使用  return {
         count
      }
   }
}</script>复制代码

toRef

toRef 能够用来为一个 reactive 对象的属性建立一个 ref,这个 ref 能够被传递而且可以保持响应性(将 reactive 对象中的某个值转化为响应式数据)

与 ref 的区别:

  • ref 是对传入数据的拷贝,toRef 是对传入数据的引用
  • ref 的值改变会更新视图,toRef 的值改变不会更新视图
<script>//引入须要的APIimport { reactive, toRef  } from 'vue'export default {   name: 'home',   setup() {      //经过reactive定义响应式对象  const obj = reactive({ count: 0, name: '廊坊吴彦祖' }) 

      //经过toRef将obj中的count属性建立为ref  const count = toRef(obj, 'count')      //经过return暴露出去,供template中使用  return {
         obj,
         count
      }
   }
}
</script>复制代码

toRefs

toRefs 把一个响应式对象转换成普通对象,该普通对象的每一个属性都是一个 ref ,和响应式对象属性一一对应(将 reactive 对象中的全部值转化为响应式数据)

<script>//引入须要的APIimport { reactive, toRefs  } from 'vue'export default {   name: 'home',   setup() {      //经过reactive定义响应式对象  const obj = reactive({ count: 0, name: '廊坊吴彦祖' }) 

      //经过toRefs将obj中的全部属性建立为ref  const refObj = toRefs(obj)      console.log(refObj)      //经过return暴露出去,供template中使用  return {
         obj,
         refObj
      }
   }
}
</script>复制代码

console.log(refObj):在这里插入图片描述

readonly

readonly 传入一个对象(响应式或普通)或 ref,返回一个原始对象的只读代理(没法修改)

  • 只读的代理是“深层的”,对象内部任何嵌套的属性也都是只读的
<template>   <div>  {{ count }}   </div></template><script>//引入须要的APIimport { readonly } from 'vue'export default {   name: 'home',   setup() {      //经过readonly定义只读对象  const count = readonly(0) 

      count.value++   //修改报错!Cannot create property 'value' on number '0'  
      //经过return暴露出去,供template中使用  return {
         count
      }
   }
}</script>复制代码

computed

computed(计算属性)传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象,使用和 vue 2.0 区别不大

<template>   <div>  {{ count }}
      {{ addCount }}   </div></template><script>//引入须要的APIimport { ref, computed } from 'vue'export default {   name: 'home',   setup() {      //经过ref定义响应式对象  const count = ref(0) 

      //经过computed方法实现计算属性  const addCount = computed(() => count.value + 1)      console.log(addCount.value)   //1          //经过return暴露出去,供template中使用  return {
         count,
         addCount
      }
   }
}</script>复制代码

watch

watch(侦听器)须要侦听特定的数据源,并在回调函数中执行反作用,使用和 vue 2.0 区别不大

  • 默认状况是懒执行的,仅在侦听的源变动时才执行回调
<script>//引入须要的APIimport { ref, reactive, watch } from 'vue'export default {   name: 'home',   setup() {      //经过ref定义响应式对象  const count = ref(0) 

      //经过watch方法实现对count的数据侦听  watch(count, (newVal, oldVal) => {         console.log('newVal:' + newVal)         console.log('oldVal:' + oldVal)
      })      //经过reactive定义响应式对象  const obj = reactive({ count: 0 }) 
      
      //经过watch方法实现对obj.count的数据侦听  watch(() => obj.count, (newVal, oldVal) => {         console.log('newVal:' + newVal)         console.log('oldVal:' + oldVal)
      })	  //经过watch方法实现同时对count和obj.count的数据侦听  watch([count, () => obj.count], ([newVal1, oldVal1], [newVal2, oldVal2]) => {         console.log('newVal1:' + newVal1)         console.log('oldVal1:' + oldVal1)         console.log('newVal2:' + newVal2)         console.log('oldVal3:' + oldVal2)
      })      
      //经过return暴露出去,供template中使用  return {
         count,
         obj
      }
   }
}
</script>复制代码

watchEffect

watchEffect 当即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变动时从新运行该函数

  • 当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时, 侦听器会被连接到该组件的生命周期,并在组件卸载时自动中止(也能够显式调用返回值以中止侦听)
    //watchEffect方法返回stop方法,执行该方法中止侦听const stop = watchEffect(() => {
      ......
    })  
    //中止侦听stop()复制代码

与 watch 的区别:

  • 不须要手动传入依赖(不须要手动指定侦听对象)
  • 每次初始化时会执行一次回调函数来自动获取依赖
  • 只能获得变化后的值,没法获得原值(变化前的值)
<script>//引入须要的APIimport { ref, reactive, watchEffect } from 'vue'export default {   name: 'home',   setup() {      //经过ref定义响应式对象  const count = ref(0) 

      //经过reactive定义响应式对象  const obj = reactive({ count: 0 }) 
      
      //经过watchEffect方法实现数据侦听(只能获得变化后的值)  watchEffect(() => {         console.log(count.value)         console.log(obj.name)
      })      
      //经过return暴露出去,供template中使用  return {
         count,
         obj
      }
   }
}
</script>复制代码

模板 Refs

使用组合式 API 时,reactive refs 和 template refs 的概念已是统一的。为了得到对模板内元素或组件实例的引用,须要在 setup() 中声明一个值为 null 的 ref 并返回它

  • 变量名必须和元素 ref 属性设置的名相同(获取 ref = box 的元素:const box = ref(null) )
<template>   <div ref="box"></div></template><script>//引入须要的APIimport { ref, onMounted } from 'vue'export default {   name: 'home',   setup() {      //经过ref(null)获取指定元素  const box = ref(null) 

      onMounted(() => {         console.log(box.value)   //<div></div>  })      
      //经过return暴露出去,供template中使用  return {
         box
      }
   }
}</script>复制代码

vue3.0 生命周期

  • vue3.0 生命周期部分有所变化(setup 函数代替了 beforeCreate 和 created 两个生命周期函数,在生命周期 beforeCreate 以前被调用)

    vue 2.0 vue3.0
    beforeCreate setup
    created setup
    beforeMount onBeforeMount
    mounted onMounted
    beforeUpdate onBeforeUpdate
    updated onUpdated
    beforeDestory onBeforeUnmount
    destoryed onUnmounted
  • 使用 vue3.0 的生命周期,一样须要先从 vue 中导入,再在 setup 函数中使用

    <script>//引入生命周期import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, unMounted } from 'vue'
      export default {   name: 'home',   setup() {      //组件挂载前  onBeforeMount(() => {
             ......
          }) 
          //组件挂载先后  onMounted(() => {
             ......
          })      //组件更新前  onBeforeUpdate(() => {
             ......   
          })      //组件更新后  onUpdated(() => {
     	     ......
          })      //组件销毁前  onBeforeUnmount(() => {
             ......  
          })      //组件销毁后  unMounted(() => {
             ......  
          })        
          return {
             ......
          }
       }
    }
    </script>复制代码

vue2.0 项目中使用 vue3.0 新语法

安装 @vue/composition-api:

npm install @vue/composition-api --save

main.js:
import Vue from 'vue'import App from './App.vue'//引入@vue/composition-apiimport VueCompositionAPI from '@vue/composition-api'Vue.use(VueCompositionAPI)

Vue.config.productionTip = falsenew Vue({  render: h => h(App),
}).$mount('#app')复制代码

vue-cli4 建立 vue3.0 项目

  • 脚手架要求:vue-cli4.0 以上
使用 vuecli4 脚手架建立 vue3.0 项目:

vue create vue3.0-demo

选择 Vue 3在这里插入图片描述

路由配置:

vue3.0 下 vue-router4.0 相关 API:

  • createRouter:建立路由实例
  • createWebHashHistory:建立 hash 路由模式
  • createWebHistory:建立 history 路由模式
路由文件 router.js:
//引入路由相关APIimport { createRouter, createWebHashHistory } from 'vue-router'import Home from '../views/home/index.vue'import About from '../views/about/index.vue'//建立hash路由模式const routerHashHistory = createWebHashHistory()//建立路由实例const router = createRouter({history: routerHashHistory,routes: [
      {name: 'home',path: '/',component: Home
      },
      {name: 'about',path: '/about',component: About
      }
    ]
})export default router复制代码
main.js:
import { createApp } from 'vue'import App from './App.vue'//引入路由实例import router from './router'//将路由实例注入到vue根实例中createApp(App).use(router).mount('#app')复制代码