Vue 3.0 于 2020-09-18 发布了,使用了 Typescript 进行了大规模的重构,带来了 Composition API RFC 版本,类似 React Hook 一样的写 Vue,可以自定义自己的 hook ,让使用者更加的灵活。

为什么推出3.0?

  • 对 TypeScript 支持不友好(所有属性都放在了 this 对象上,难以推倒组件的数据类型)
  • 大量的 API 挂载在 Vue 对象的原型上,难以实现 TreeShaking
    • TreeShaking:当我引入一个模块的时候,我不引入这个模块的所有代码,我只引入我需要的代码
  • 架构层面对跨平台 dom 渲染开发支持不友好
  • CompositionAPI。受 ReactHook 启发
  • 更方便的支持了 jsx
  • Vue 3 的 Template 支持多个根标签,Vue 2 不支持
  • 对虚拟 DOM 进行了重写、对模板的编译进行了优化操作

一、setup

为我们使用 vue3 的 Composition API 新特性提供了统一的入口, 有些文章说setup 函数会在 beforeCreate 之后、created 之前执行但实践证明这个说法不正确如下图打印顺序, vue3 也是取消了这两个钩子,统一用 setup 代替, 该函数相当于一个生命周期函数,vue 中过去的 data,methods,watch 等全部都用对应的新增 api 写在 setup()函数中。

vue3.0API详解-LMLPHP

1、setup参数

  1. props: 组件传入的属性
  2. context:attrs,emit,slots

props

  setup中接受的props是响应式的, 当传入新的props 时,会及时被更新。由于是响应式的, 所以不可以使用ES6解构,解构会消除它的响应式。需要结构可以用toRefs()

context

  setup中不能访问Vue2中最常用的this对象,所以context中就提供了this中最常用的三个属性:attrsslot 和emit,分别对应Vue2.x中的 $attr属性、slot插槽 和$emit发射事件。

简单用法

vue3.0API详解-LMLPHP

2、reactive、ref与toRefs

在vue2.x中, 定义数据都是在data中, 但是Vue3.x 可以使用reactiveref来进行数据定义。

区别

  reactive用于处理对象的双向绑定,ref则处理js基础类型的双向绑定,reactive不能代理基本类型,例如字符串、数字、boolean等。

简单用法

<template>
  <div>
    <span>{{ year }}</span
    ><span>{{ user.name }}</span
    ><span>{{ user.label }}</span>
  </div>
</template>

<script>
import { reactive, ref } from "vue";
export default {
  props: {
    data: {
      type: Object,
      default: {
        id: 1,
        name: "匹配",
      },
    },
  },
  components: {},

  setup(props, con) {
    const year = ref(10);
    const user = reactive({
      name: "夏利",
      label: "",
    });
  //这里ref取值需要加value
if (year.value > 5) { user.label = "牛逼"; } else { user.label = "一般"; } return { year, user, }; }, }; </script>

  不能直接对user进行结构, 这样会消除它的响应式, 这里就和上面我们说props不能使用ES6直接解构就呼应上了。那我们就想使用解构后的数据怎么办,解决办法就是使用toRefs

   return {
        year,
        // 使用reRefs
        ...toRefs(user)
    }

3、生命周期

vue3.0API详解-LMLPHP

  我们可以看到beforeCreatecreatedsetup替换了(但是Vue3中你仍然可以使用, 因为Vue3是向下兼容的, 也就是你实际使用的是vue2的)。其次,钩子命名都增加了on; Vue3.x还新增用于调试的钩子函数onRenderTriggeredonRenderTricked

vue3.0API详解-LMLPHP

<script>
import {
  defineComponent,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onErrorCaptured,
  onRenderTracked,
  onRenderTriggered,
} from "vue";

export default defineComponent({
  // beforeCreate和created是vue2的
  beforeCreate() {
    console.log("------beforeCreate-----");
  },
  created() {
    console.log("------created-----");
  },
  setup() {
    console.log("------setup-----");

    // vue3.x生命周期写在setup中
    onBeforeMount(() => {
      console.log("------onBeforeMount-----");
    });
    onMounted(() => {
      console.log("------onMounted-----");
    });
    // 调试哪些数据发生了变化
    onRenderTriggered((event) => {
      console.log("------onRenderTriggered-----", event);
    });
  },
});
</script>

 4、全局定义属性 globalProperties

  在项目中往往会全局定义公共属性或方法,方便我们组件间调用。

import { createApp } from 'vue'
import App from './App.vue'
import utils from './utils'

// vue2.0写法
let vue=new Vue()
vue.prototype.utils=utils
// vue3.0写法
const app=createApp(App)
app.config.globalProperties.utils=utils
app.mount(
'#app')

vue3.0中使用全局定义的属性

<script>
import { getCurrentInstance } from "vue";
export
default { components: {}, setup(props, con) {
const { ctx } = getCurrentInstance(); console.log(ctx.utils);
}, };
</script>

5、use与plugin

定义一个组件

vue3.0API详解-LMLPHP

 通过use引入

vue3.0API详解-LMLPHP

 6、readonly 只读属性

在使用readonly后重新进行复制是不允许修改的,这个api不常用

vue3.0API详解-LMLPHP

7、computed计算api

这里引入了proxy内的类似写法,有了set与get的内容写法,该函数用来创造计算属性,和过去一样,它返回的值是一个 ref 对象。里面可以传方法,或者一个对象,对象中包含 set()、get()方法。

vue3.0API详解-LMLPHP

7.1 创建只读的计算属性

<template>
  <div>
    <p>refCount: {{refCount}}</p>
    <p>计算属性的值computedCount : {{computedCount}}</p>
    <button @click="refCount++">refCount + 1</button>
  </div>
</template>

<script>
import { computed, ref } from '@vue/composition-api'
export default {
  setup() {
    const refCount = ref(1)
    // 只读
    let computedCount = computed(() => refCount.value + 1) //2
    console.log(computedCount)
    return {
      refCount,
      computedCount
    }
  }
};
</script>

7.2 通过 set()、get()方法创建一个可读可写的计算属性

<template>
  <div>
    <p>refCount: {{refCount}}</p>
    <p>计算属性的值computedCount : {{computedCount}}</p>
    <button @click="refCount++">refCount + 1</button>
  </div>
</template>

<script>
import { computed, ref } from '@vue/composition-api'
export default {
  setup() {
    const refCount = ref(1)
    // 可读可写
    let computedCount = computed({
      // 取值函数
      get: () => refCount.value + 1,
      // 赋值函数
      set: val => {
        refCount.value = refCount.value -5
      }
    })
  //触发get函数 console.log(computedCount.value)
// 为计算属性赋值的操作,会触发 set 函数 computedCount.value = 10 console.log(computedCount.value) // 触发 set 函数后,count 的值会被更新 console.log(refCount.value)
return { refCount, computedCount } } }; </script>

8、watch与watchEffect

8.1watch

  watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。默认情况是惰性的,也就是说仅在侦听的源数据变更时才执行回调。

    *   1、需要一个明确的数据资源
         *   2、需要有一个回调函数
         *   3、等到改变才会执行函数
<script>
import { ref, watch } from "vue";
export default {
  components: {},
  setup(props, con) {
    const count = ref(0);
    const name = ref("iuuggu");
    setTimeout(() => {
      (count.value = 1), (name.value = "这一行山");
    }, 200);
    watch(
      () => {
        /*
         *   1、需要一个明确的数据资源
         *   2、需要有一个回调函数
         *   3、等到改变才会执行函数
         *
         */
        return count.value;
      },
      () => {}
    );
    // 4、ref简写 ,reactive不可以
    watch(count, () => {});
    // 5、监听多个
    watch(
      () => {
        return [count, name];
      },
      ([newv, oldv], [newv, oldv]) => {}
    );
    // 6、onTrack,onTrigger
    watch(
      () => {
        return [count, name];
      },
      ([newv, oldv], [newv, oldv]) => {},
      {
        onTrack(e) {},
        onTrigger(e) {},
      }
    );
  },
};
</script>

8.1.1 监听数据源

reactive

setup(props, context) {
    const state = reactive<Person>({ name: 'vue', age: 10 })

    watch(
      () => state.age,
      (age, preAge) => {
        console.log(age); // 100
        console.log(preAge); // 10
      }
    )
    // 修改age 时会触发watch 的回调, 打印变更前后的值
    state.age = 100
    return {
      ...toRefs(state)
    }
  }

ref

setup(props, context) {
    const age = ref<number>(10);

    watch(age, () => console.log(age.value)); // 100

    // 修改age 时会触发watch 的回调, 打印变更后的值
    age.value = 100
    return {
      age
    }
  }

8.1.2监听多个

setup(props, context) {
    const state = reactive<Person>({ name: 'vue', age: 10 })

    watch(
      [
      ()
=> state.age,
      () => state.name
    ], ([newName, newAge], [oldName, oldAge])
=> { console.log(newName); console.log(newAge); console.log(oldName); console.log(oldAge); } ) // 修改age 时会触发watch 的回调, 打印变更前后的值, 此时需要注意, 更改其中一个值, 都会执行watch的回调 state.age = 100 state.name = 'vue3' return { ...toRefs(state) } }

8.1.3stop 停止监听

在 setup() 函数内创建的 watch 监视,会在当前组件被销毁的时候自动停止。如果想要明确地停止某个监视,可以调用 watch() 函数的返回值即可,语法如下:

setup(props, context) {
    const state = reactive<Person>({ name: 'vue', age: 10 })

    const stop =  watch(
      [() => state.age, () => state.name],
      ([newName, newAge], [oldName, oldAge]) => {
        console.log(newName);
        console.log(newAge);

        console.log(oldName);
        console.log(oldAge);
      }
    )
    // 修改age 时会触发watch 的回调, 打印变更前后的值, 此时需要注意, 更改其中一个值, 都会执行watch的回调
    state.age = 100
    state.name = 'vue3'

    setTimeout(()=> {
      stop()
      // 此时修改时, 不会触发watch 回调
      state.age = 1000
      state.name = 'vue3-'
    }, 1000) // 1秒之后讲取消watch的监听

    return {
      ...toRefs(state)
    }
  }

8.2 watchEffect

 该函数有点像update函数,但他执行在update与beforeuodate之前。

       *    1、首次加载会立即执行
         *    2、响应的最终所有依赖监听变化(数据改变)
         *    3、在卸载onUnmounte时自动停止
         *    4、执行stop就会停止监听,否则一直监听
         *    5、异步函数先执行再去监听改变
<script>
import { watchEffect, ref, onMounted } from "vue";
export default {
  components: {},
  //con==context(attrs,emit,slots)
  setup(props, con) {
    const count = ref(0);
    setTimeout(() => {
      count.value = 1;
    }, 2000);
    const stop = watchEffect(
      () => {
        /*
         *    1、首次加载会立即执行
         *    2、响应的最终所有依赖监听变化(数据改变)
         *    3、在卸载onUnmounte时自动停止
         *    4、执行stop就会停止监听,否则一直监听
         *    5、异步函数先执行再去监听改变
         */
      },
      {
        // 6、在update之后执行
        flush: "post",
        // 同步执行
        flush: "async",
      }
    );
    setTimeout(() => {
      stop();
    }, 4000);

    // 7、组件挂在ref
    const myRef = ref(null);
    // 避免监听时先见听到null 在监听到h1
    onMounted(() => {
      watchEffect(() => {
        console.log(myRef.value);
      });
    });
    // 8、debugging  开发模式使用
    watchEffect(
      () => {
        console.log(count.value);
      },
      {
        onTrack(e) {
          // 监听到count和改变count
        },
        onTrigger(e) {
          // count改变了会触发
        },
      }
    );
    return {
      myRef,
      count,
    };
  },
};
</script>

 9、ref,torefs,isref,unref

9.1ref

ref() 函数用来根据给定的值创建一个响应式的数据对象,ref() 函数调用的返回值是一个对象,这个对象上只包含一个 .value 属性:

ref数据

<template>
    <div class="mine">
        {{count}} // 10
    </div>
</template>

<script >
import {ref } from 'vue';
export default {
  setup() {
    const count = ref(10)
    // 在js 中获取ref 中定义的值, 需要通过value属性
    console.log(count.value);
    return {
       count
    }
   }
}
</script>

ref 访问dom

通过 refs 来回去真实 dom 元素, 这个和 react 的用法一样,为了获得对模板内元素或组件实例的引用,我们可以像往常一样在 setup()中声明一个 ref 并返回它

  1.  还是跟往常一样,在 html 中写入 ref 的名称
  2.  在steup 中定义一个 ref
  3.  steup 中返回 ref的实例
  4.  onMounted 中可以得到 ref的RefImpl的对象, 通过.value 获取真实dom

vue3.0API详解-LMLPHP

<template>
  <!--第一步:还是跟往常一样,在 html 中写入 ref 的名称-->
  <div class="mine" ref="elmRefs">
    <span>1111</span>
  </div>
</template>

<script >
import { set } from 'lodash';
import { onMounted, ref } from 'vue';
export default{
  setup(props, context) {
    // 获取真实dom
    const elmRefs = ref(null);
    onMounted (() => {
      console.log(elmRefs.value); // 得到一个 RefImpl 的对象, 通过 .value 访问到数据
    })

    return {
      elmRefs
    }
  }
}
</script>

 在 reactive 对象中访问 ref 创建的响应式数据

当把 ref() 创建出来的响应式数据对象,挂载到 reactive() 上时,会自动把响应式数据对象展开为原始的值,不需通过 .value 就可以直接被访问,例如:

<template>
    <div class="mine">
        {{count}} -{{t}} // 10 -100
    </div>
</template>

<script >
import  reactive, ref, toRefs } from 'vue';
export default {
  setup() {
    const count = ref(10)
    const obj = reactive({
      t: 100,
      count
    })
    // 通过reactive 来获取ref 的值时,不需要使用.value属性
    console.log(obj.count);
    return {
       ...toRefs(obj)
    }
   }
}
</script>

9.2 toRefs() 函数

toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象,只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据

<template>
  <div>
    <p>{{ count }} - {{ name }}</p>
    <button @click="count += 1">+1</button>
    <button @click="add">+1</button>
  </div>
</template>

<script>
import { reactive, toRefs } from "@vue/composition-api";
export default {
  setup() {
    // 响应式数据
    const state = reactive({ count: 0, name: "zs" });
    // 方法
    const add = () => {
      state.count += 1;
    };
    return {
      // 非响应式数据
      // ...state,
      // 响应式数据
      ...toRefs(state),
      add
    };
  }
};
</script>

9.3 isref

isRef() 用来判断某个值是否为 ref() 创建出来的对象

 setup(props, context) {
    const name: string = 'vue'
    const age = ref(18)
    console.log(isRef(age)); // true
    console.log(isRef(name)); // false

    return {
      age,
      name
    }
  }

9.4 unref

unRef() 用来判断某个值是否为 ref() 创建出来的对象有抛出该对象

 setup(props, context) {
    const name: string = 'vue'
    const age = ref(18)
    console.log(unRef(age)); // 18
    console.log(unRef(name)); // vue

    return {
      age,
      name
    }
  }
05-21 00:37