Vue3.0语法重点API整理一

一、Vue3.0项目搭建

1、使用脚手架Vite
npm init vite vue3.0-demo --template vue

2、通过脚手架vue-cli进行搭建
(1)首先查看电脑中vue-cli的版本,查看版本号:vue -V
(2)如果是旧版本的(1.x或2.x),需要先卸载旧的版本: npm uninstall vue-cli -g
(3)安装新的版本:npm install -g @vue/cli
(4)创建项目:vue create <文件名称>
(5)根据项目所需选择自己所需的配置

二、为什么升级Vue3.0

使用 Vue2.x 的小伙伴都熟悉,Vue2.x 中所有数据都是定义在data中,方法定义在methods中的,并且使用this来调用对应的数据和方法,当一个组件的代码超过几百行时,这时增加或者修改某个需求, 就要在 data、methods、computed 以及 mounted 中进行反复的跳转,甚至一个功能还有会依赖其他功能,全搅合在一起。如图所示:
Vue3.0语法重点API整理一_第1张图片
当我们业务复杂了就会大量出现上面的情况, 随着复杂度上升,就会出现这样一张图, 每个颜色的方块表示一个功能:
Vue3.0语法重点API整理一_第2张图片

Vue2.x 版本给出的解决方案就是 Mixin, 但是使用 Mixin 也会遇到让人苦恼的问题:

  • 命名冲突问题
  • 不清楚暴露出来的变量的作用
  • 逻辑重用到其他 component 经常遇到问题

如果可以按照逻辑进行分割,将上面这张图变成下边这张图,是不是就清晰很多了呢, 这样的代码可读性和可维护性都会更高,所以,Vue3.x 就推出了Composition API主要就是为了解决上面的问题,将零散分布的逻辑组合在一起来维护,并且还可以将单独的功能逻辑拆分成单独的文件 。

Vue3.0语法重点API整理一_第3张图片
三、Setup

setup函数是 Composition API(组合API)的入口
Vue3.0语法重点API整理一_第4张图片
运行结果:
Vue3.0语法重点API整理一_第5张图片
setup 执行时机是在 beforeCreate 之前执行的
1、使用方法:
使用setup的时候,需要接收两个参数:
(1)props:组件传入的属性
(2)context
setup 中接受的props是响应式的, 当传入新的 props 时,会及时被更新。由于是响应式的, 所以不可以使用 ES6 解构,解构会消除它的响应式。 错误代码示例, 这段代码会让 props 不再支持响应式:

export default defineComponent ({
    setup(props, context) {
        const { name } = props
        console.log(name)
    },
})

那在开发中我们想要使用解构,还能保持props的响应式,有没有办法解决呢?大家可以思考一下,在后面toRefs学习的地方再进行介绍。 接下来我们来说一下setup接受的第二个参数context,我们前面说了setup中不能访问 Vue2 中最常用的this对象,所以context中就提供了this中最常用的三个属性:attrs、slot 和emit,分别对应 Vue2.x 中的 a t t r 属 性 、 s l o t 插 槽 和 attr属性、slot插槽 和 attrslotemit发射事件。并且这几个属性都是自动同步最新的值,所以我们每次使用拿到的都是最新值。

export default {
  setup(props, context) {
    // Attribute (非响应式对象,等同于 $attrs)
    console.log(context.attrs)

    // 插槽 (非响应式对象,等同于 $slots)
    console.log(context.slots)

    // 触发事件 (方法,等同于 $emit)
    console.log(context.emit)

    // 暴露公共 property (函数)
    console.log(context.expose)
  }
}

context 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着可以安全地对 context 使用 ES6 解构。

export default {
  setup(props, { attrs, slots, emit, expose }) {
    ...
  }
}

attrs 和 slots 是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的方式引用 property。请注意,与 props 不同,attrs 和 slots 的 property 是非响应式的。如果你打算根据 attrs 或 slots 的更改应用副作用,那么应该在 onBeforeUpdate 生命周期钩子中执行此操作。

四、响应式API

1、响应式基础API

(1)reactive、ref
在 vue2.x 中, 定义数据都是在data中, 但是 Vue3.x 可以使用reactive和ref来进行数据定义。

setup() {
    let num1 = 10;
    let num2 = ref(100);
    let num3 = relative(100);
    console.log(num1);
    console.log(num2);
    console.log(num3);
    return {num1,num2,num3}
  }

打印结果如图所示:
Vue3.0语法重点API整理一_第6张图片
在控制台输出一个警告信息, 提示100这个值不能被reactive创建,官方也推荐在定义数据的时候,reactive定义复杂的数据类型的数据,ref推荐定义基本数据类型,所以如果要使用reactive定义基本数据类型的话,我们需要在reactive中将数据包装一下:

let num3 = relative({val:10});

ref定义的数据打印结果需要.value才能获取到结果,而reactive则不需要
ref和relative的区别:
(1)reactive 和 ref 都是用来定义响应式数据的 reactive更推荐去定义复杂的数据类型 ref 更推荐定义基本类型。
(2)ref 和 reactive 本质我们可以简单的理解为ref是对reactive的二次包装, ref定义的数据访问的时候要多一个.value
(3)使用ref定义基本数据类型,ref也可以定义数组和对象。

isRef:检查值是否为一个 ref 对象。
isReactive:检查对象是否是由 reactive 创建的响应式代理。

import { ref,reactive, isRef,isReactive } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: 'John'
    })
    const num = ref(10);
    console.log(isRef(num))  //true
    console.log(isReactive(state)) // -> true
  }
}
<template>
  <div class="homePage">
    <p>{{ year }}</p>
    <p>姓名: {{ user.nickname }}</p>
    <p>年龄: {{ user.age }}</p>
  </div>
</template>

<script>
import { defineComponent, reactive, ref} from "vue";
export default defineComponent({
  setup() {
    const year = ref(0);
    const user = reactive({ nickname: "xiaofan", age: 26, gender: "女" });
    setInterval(() => {
      year.value++;
      user.age++;
    }, 1000);
    return {
      year,
      user
    };
  },
});
</script>

上面的代码中,我们绑定到页面是通过user.name,user.age;这样写感觉很繁琐,我们能不能直接将user中的属性解构出来使用呢? 答案是不能直接对user进行结构, 这样会消除它的响应式, 这里就和上面我们说props不能使用 ES6 直接解构就呼应上了。那我们就想使用解构后的数据怎么办,解决办法就是使用toRefs。 toRefs 用于将一个 reactive 对象转化为属性全部为 ref 对象的普通对象。具体使用方式如下:

<template>
  <div class="homePage">
    <p>{{ year }}</p>
    <p>姓名: {{ nickname }}</p>
    <p>年龄: {{ age }}</p>
  </div>
</template>

<script>
import { defineComponent, reactive, ref, toRefs } from "vue";
export default defineComponent({
  setup() {
    const year = ref(0);
    const user = reactive({ nickname: "xiaofan", age: 26, gender: "女" });
    setInterval(() => {
      year.value++;
      user.age++;
    }, 1000);
    return {
      year,
      // 使用reRefs
      ...toRefs(user),
    };
  },
});
</script>

(2)Readonly

  • 获取一个对象 (响应式或纯对象) 或 ref并返回原始代理的只读代理。
  • 只读代理是深层的:访问的任何嵌套 property也是只读的。
const foo = {
      name:'lzihi',
      other:{
        age:18,
        sex:'男'
      }
}
const readonlyFoo = readonly(foo)
readonlyFoo.name = 'lisi' // 报错 Set operation on key "name" failed: target is readonly.
readonlyFoo.other.age ++ // 报错 Set operation on key "age" failed: target is readonly.

isReadonly:检查对象是否是由 readonly 创建的只读代理。

2、Computed 与 Watch

(1)computed
接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 错误

或者,接受一个具有 get 和 set 函数的对象,用来创建可写的 ref 对象。

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

(2)watch
watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。默认情况是惰性的,也就是说仅在侦听的源数据变更时才执行回调。watch(source, callback, [options])
参数说明:
source: 可以支持 string,Object,Function,Array,用于指定要侦听的响应式变量
callback: 执行的回调函数
options:支持 deep、immediate 和 flush 选项。

侦听 reactive 定义的数据:

import { defineComponent, ref, reactive, toRefs, watch } from "vue";
export default defineComponent({
  setup() {
    const state = reactive({ nickname: "xiaofan", age: 20 });
    setTimeout(() => {
      state.age++;
    }, 1000);
    // 修改age值时会触发 watch的回调
    watch(
      () => state.age,
      (curAge, preAge) => {
        console.log("新值:", curAge, "老值:", preAge);
      }
    );
    return {
      ...toRefs(state),
    };
  },
}

侦听 ref 定义的数据:

const year = ref(0);

setTimeout(() => {
  year.value++;
}, 1000);


watch(year, (newVal, oldVal) => {
  console.log("新值:", newVal, "老值:", oldVal);
})

侦听多个数据:
上面两个例子中,我们分别使用了两个 watch, 当我们需要侦听多个数据源时, 可以进行合并, 同时侦听多个数据:

watch( 
   [() => state.age, year], 
   ([curAge, newVal], [preAge, oldVal]) => {
       console.log("新值:", curAge, "老值:", preAge); 
       console.log("新值:", newVal, "老值:", oldVal); 
});

侦听复杂的嵌套对象:

const state = reactive({
  room: {
    id: 100,
    attrs: {
      size: "140平方米",
      type: "三室两厅",
    },
  },
});
watch(
  () => state.room,
  (newType, oldType) => {
    console.log("新值:", newType, "老值:", oldType);
  },
  { deep: true }
);

如果不使用第三个参数deep:true, 是无法监听到数据变化的。
默认情况下,watch 是惰性的, 不会立即执行回调函数,这时候,如果想要进入页面就立即执行回调函数,只需要给第三个参数中设置immediate: true即可。

stop 停止监听:
在组件中创建的watch监听,会在组件被销毁时自动停止。如果在组件销毁之前我们想要停止掉某个监听, 可以调用watch()函数的返回值,操作如下:

const stopWatchRoom = watch(() => state.room, (newType, oldType) => {
    console.log("新值:", newType, "老值:", oldType);
}, {deep:true});


setTimeout(()=>{
    // 停止监听
    stopWatchRoom()
}, 3000)

watchEffect的使用:

import { defineComponent, ref, reactive, toRefs, watchEffect } from "vue";
export default defineComponent({
  setup() {
    const state = reactive({ nickname: "xiaofan", age: 20 });
    let year = ref(0)

    setInterval(() =>{
        state.age++
        year.value++
    },1000)

    watchEffect(() => {
        console.log(state);
        console.log(year);
      }
    );

    return {
        ...toRefs(state)
    }
  },
}

执行结果首先打印一次state和year值;然后每隔一秒,打印state和year值。 从上面的代码可以看出, 并没有像watch一样需要先传入依赖,watchEffect会自动收集依赖, 只要指定一个回调函数。在组件初始化时, 会先执行一次来收集依赖, 然后当收集到的依赖中数据发生变化时, 就会再次执行回调函数。所以总结对比如下:

  • watchEffect 不需要手动传入依赖
  • watchEffect 会先执行一次用来自动收集依赖 watchEffect
  • 无法获取到变化前的值, 只能获取变化后的值

你可能感兴趣的:(前端,javascript,vue3.0)