欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Vue3.0系列(Composition API:组合API)

程序员文章站 2022-06-07 08:08:28
...

1.Vue3.0六大亮点

  • Performance:性能比Vue 2.x快1.2~2倍
  • Tree shaking support:按需编译,体积比Vue2.x更小
  • Composition API: 组合API(类似React Hooks)
  • Better TypeScript support:更好的 Ts 支持
  • Custom Renderer API:暴露了自定义渲染API
  • Fragment, Teleport(Protal), Suspense:更先进的组件

2.项目快速搭建

2.1 Vue3.0-快速上手

创建Vue3的3种方式
  • Vue-CLI
npm install -g @vue/cli
vue create projectName
cd projectName
vue add vue-next
npm run serve
  • Webpack
git clone https://github.com/vuejs/vue-next-webpack-preview.git projectName
cd projectName
npm install
npm run dev
  • Vite

2.2 什么是Vite?

Vite是Vue作者开发的一款意图取代webpack的工具, 其实现原理是利用ES6的import会发送请求去加载文件的特性,
拦截这些请求, 做一些预编译, 省去webpack冗长的打包时间

  • 安装Vite
    npm install -g create-vite-app

  • 利用Vite创建Vue3项目
    create-vite-app projectName

  • 安装依赖运行项目
    cd projectName
    npm install
    npm run dev

3.Composition API:组合API

为什么要有组合API(又叫注入API)呢?

3.1 vue 2.x存在的问题

因为数据和逻辑是很分散的,当我们的项目非常大时,维护起来是比较麻烦的,需要我们的鼠标不停的滑上滑下,

export default {
  name: 'App',
  data() {
   return{
      // 新增功能1的数据
      // 新增功能2的数据
   }
  },
  methods:{
    // 新增功能1的业务逻辑
    // 新增功能2的业务逻辑
  },
  computed:{
    // 新增功能1的业务逻辑
    // 新增功能2的业务逻辑
  },
  watch:{
    // 新增功能1的业务逻辑
    // 新增功能2的业务逻辑
  }
}

所以才有了compositionAPI,它是如何把数据和逻辑聚合在一起的呢?

3.2 Composition API

3.2.1 组合API入口函数 setup

注意:

  • vue3.0兼容vue2.x的Options API!
  • 且 setup的执行时机是在beforecreate之前完成的,因此是无法使用datamethods
  • 所以Vue为了避免我们错误的使用, 它直接将setup函数中的this修改成了undefined
  • setup只能是同步的,不能是异步的
export default {
  name: 'App',
  // setup函数是组合API的入口函数
  setup(){
 		 //1.数据管理。 通过 ref 或 reactive管理
  		 //2.方法。在组合API中,如果想定义方法,不用定义到methods中,直接在		setup函数中定义即可。
 		 //3. 在组合API中定义的变量/方法,要想在外界使用,必须return出去。
  },
  data(){
  },
  methods:{
  }
}

3.2.2 reactive

作用:是vue3中提供实现响应复杂数据的方法;
​原理:在vue2中 响应数据是通过 defineProperty 来实现的,vue3是通过而是ES6中的 Proxy 来实现的;
注意点:

  • reactive 的参数传递 必须是一个对象(json / array)
    否则无法实现响应式
import {reactive} from 'vue';
export default {
  name: 'App',
  setup() {
  //数据
    let state = reactive({
      stus:[
        {id:1, name:'zs', age:10},
        {id:2, name:'ls', age:20},
        {id:3, name:'ww', age:30},
      ]
    });
    //逻辑
    function remStu(index) {
      state.stus = state.stus.filter((stu, idx) => idx !== index);
    }
    let {state, remStu} = useRemoveStudent();
	//导出
    return {state, remStu}
    //我们可以发现数据和逻辑是在一起的,变得模块化了
    //如果想要setup看起来更简洁,可以把一个功能直接抽成模块导出,在setup导入
  • 如果给 reactive 传递了其他对象(指: 非 json 和 array 对象),此时,修改对象则不更新,需要通过重新赋值的方式。

3.2.3 ref

ref是vue3用于响应简单数据的方法:
​特点: 是对原始数据进行赋值 ,修改时并不修改原始数据

import {ref} from "vue"
export default {
  name: 'App',
  // setup函数是组合API的入口函数
  setup(){
    // 1. 定义了一个名称叫做count变量, 这个变量的初始值是0
    // 这个变量发生改变之后, Vue会自动更新UI
    let count = ref(0);
    // 2.在组合API中, 如果想定义方法, 不用定义到methods中, 直接定义即可
    function myFn() {
    //你知道这里为什么要通过.value来访问count吗?往下看就知道了
      count.value += 1;
    }
    // 注意点:
    // 3. 在组合API中定义的变量/方法, 要想在外界使用, 必须通过return {xxx, xxx}暴露出去
    return{count, myFn}
  }
}

为什么ref创建的数据要通过.value来访问count呢?

ref的本质

它的本质还是reactive,当我们给ref函数传递一个值时,ref函数会自动将ref转换成reactive

ref(0) -->  reactive({
value:0
})
  • 所以我们在script中修改ref创建的数据时,必须通过.value来获取
  • 需要注意的是,如果是通过ref创建出来的数据,
    template中使用的时候不用通过.value来获取。因为Vue会自动给我们添加.value 。

vue是如何判断当前的数据是否是ref类型的?
通过当前数据的__v_ref来判断的,如果有这个私有的属性,并且取值为true,那么就代表是一个ref类型的数据。

开发者自己判断。isRef(数据),返回true或者是false。

import {isRef} from 'vue'

3.2.4 ref和reactive区别:

  • 如果在template里使用的是ref类型的数据, 那么Vue会自动帮我们添加.value
    如果在template里使用的是reactive类型的数据, 那么Vue不会自动帮我们添加.value

  • Vue是如何决定是否需要自动添加.value的
    Vue在解析数据之前, 会自动判断这个数据是否是ref类型的,
    如果是就自动添加.value, 如果不是就不自动添加.value

  • Vue是如何判断当前的数据是否是ref类型的
    通过当前数据的__v_ref来判断的
    如果有这个私有的属性, 并且取值为true, 那么就代表是一个ref类型的数据

import {isRef, isReactive} from 'vue'
...
 setup() {
    let age = ref(18);
    let name = reactive({value: 'zs'});
    function myFn() {
        console.log(isRef(age));
        console.log(isReactive(name));
    }
    return {age, name, myFn}
  }

4.递归监听和非递归监听

4.1 递归监听

1. 默认情况下, 无论是通过ref还是reactive都是递归监听
每一层都监听

<template>
  <div>
      <p>{{state.a}}</p>
      <p>{{state.gf.b}}</p>
      <p>{{state.gf.f.c}}</p>
      <p>{{state.gf.f.s.d}}</p>
    <button @click="myFn">按钮</button>
  </div>
</template>

<script>

import {reactive} from 'vue';
  // import {ref} from 'vue';
export default {
  name: 'App',
  setup() {
    let state = reactive({
        a:'a',
        gf:{
            b:'b',
            f:{
                c:'c',
                s:{
                    d:'d'
                }
            }
        }
    });
    function myFn() {
        state.a = '1';
        state.gf.b = '2';
        state.gf.f.c = '3';
        state.gf.f.s.d = '4';
        
        console.log(state);
        console.log(state.gf);
        console.log(state.gf.f);
        console.log(state.gf.f.s);
    }
    return {state, myFn}
  }
}
</script>

2.递归监听存在的问题
如果数据量比较大, 非常消耗性能

打印后,发现每一层对象都被包装为了Proxy对象,
如果数据量比较大, 非常消耗性能
Vue3.0系列(Composition API:组合API)

4.2 非递归监听

使用shallowReactiveshallowRef

shallowReactive:

<script>
 // 3.非递归监听
import {shallowReactive} from 'vue';

export default {
  name: 'App',
  setup() {
    let state = shallowReactive({
    // let state = ref({
        a:'a',
        gf:{
            b:'b',
            f:{
                c:'c',
                s:{
                    d:'d'
                }
            }
        }
    });
    function myFn() {
        state.a = '1';
        state.gf.b = '2';
        state.gf.f.c = '3';
        state.gf.f.s.d = '4';
        console.log(state);
        console.log(state.gf);
        console.log(state.gf.f);
        console.log(state.gf.f.s);
    }
    return {state, myFn}
  }
}
</script>

再次打印输出结果,发现只有第一层被封装为了Proxy对象
但是第2, 3,4层数据为什么也发生了变化呢?
因为修改了第一层,它就去修改UI了
Vue3.0系列(Composition API:组合API)
shallowRef:

注意点:

  • 如果是通过shallowRef创建数据,
    那么Vue监听的是.value的变化, 并不是第一层的变化
  • Vue3只提供了triggerRef方法, 没有提供triggerReactive方法
  • 所以如果是reactive类型的数据, 是无法主动触发界面更新的
    triggerRef(state);

参考:B站李南江老师