vue3 setup语法糖写法基本教程

前言

  • 官网地址:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)
  • 下面只讲Vue3Vue2有差异的地方,一些相同的地方我会忽略或者一笔带过
  • Vue3一同出来的还有Vite,但是现在不使用它,等以后会有单独的教程使用。目前仍旧使用vue-cli创建vue3项目
  • Vue3有两种代码编写形式。下面演示主要使用第二种
    • 一种是和vue2相仿的选项式API写法
    • 另一种是组合式API的写法。
    • 具体两种代码编写的区别与优缺点,可以自行去官网查看。
  • vue3版本向下兼容。所以Vue2的写法依旧可以继续使用,不过只能搭配第一种选项是API的写法一同使用。

1、基础

1、初始化Vue

  • 使用vue-cli创建好项目后,打开main.js文件,可以看到和vue2的区别。
  • vue3中使用起来更加语义明显,并且不需要使用new Vue那么笨重的构造函数,而是封装好了需要的函数,只把这些函数提供给我们。

1.1、vue2中

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'


new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

1.2、vue3中

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

createApp(App).use(store).use(router).mount('#app')

打印下createApp()函数返给我们的内容看看,可以看到几个常用的函数

vue3 setup语法糖写法基本教程_第1张图片

2、生命周期

  • vue2中,组件挂载会执行mounted钩子函数,对应的卸载,会使用beforeDestroy、destroyed钩子函数。
  • vue3中,其他钩子函数依旧不变,包括挂载mounted,但是,卸载改为了beforeUnmounted、unmounted

3、模板语法

  • 模板语法中v-model、v-bind、v-on等,包括其对应的简写形式,都没有改变
  • 唯一的区别就是,标签template中,不再强制必须有一个外层标签嵌套里面所有内容了。

3.1、vue2中

<template>
  
  <div>
    <h1>HomeViewh1>
    
  div>
template>

3.2、vue3中

<template>
  
  <h1>HomeViewh1>
  
template>

4、响应式基础(重要)

  • vue2中,响应式数据全部存放到data函数中。
  • vue3中,也可以使用vue2的方式,不过我们使用的是组合式API,就不能那么写了。
  • 根据数据类型可以分为两种方式
    • ref(xxx):可以将所有类型的数据定义为响应式、基础类型,以及对象。不过在js中访问数据需要添加.value。返回的值为RefImpl对象
    • reactive(xxx):只能将对象\数组类型数据转为响应式。但是在js中访问数据不需要添加.value
  • 其实ref函数中,会根据参数数据类型进行判断,如果是对象,其内部还是使用的reactive将对象转为响应式,不过会将转换完的数据,封装起来,外面包一层。

4.1、基本数据类型(ref)

代码
<template>
  
  <h2>checkbox的值:{{ flag }}h2>
  <input type="checkbox" v-model="flag">


  <h2>input的值:{{ str }}h2>
  <input type="text" v-model="str">
template>


<script setup>
  // 使用什么,就导入什么
  import { ref } from "vue";

  // 此时,flag就是一个响应式对象
  const flag = ref(true);
  // 访问的是包装以后的对象,并不是真正的值,需要.value才能获取到真正的值
  console.log("flag", flag);

  const str = ref("123");
  /* 真正的str的值 */
  console.log("str", str.value);
script>
效果

vue3 setup语法糖写法基本教程_第2张图片

4.2、对象类型(ref)

代码
<template>
  <button @click="changeAge">姓名+1button>
  <h2>姓名:h2>{{ user.name}}
  <h2>年龄:h2>{{ user.age}}
template>

<script setup>
  import { ref } from "vue";

  const user = ref(
    {
      name: "张三",
      age: 18
    }
  )
  // Proxy对象
  console.log("user", user.value);


  function changeAge() {
    // 必须要使用.value,才能获取到对象
    user.value.age++;
  }
script>

效果

vue3 setup语法糖写法基本教程_第3张图片

4.3、对象类型(reactive)

代码
<template>
  <button @click="changeAge">姓名+1button>
  <h2>姓名:h2>{{ user.name}}
  <h2>年龄:h2>{{ user.age}}
template>

<script setup>
  import { ref, reactive } from "vue";

  const user = reactive(
    {
      name: "张三",
      age: 18
    }
  )
  // 还是Proxy对象
  console.log("user", user);


  function changeAge() {
    // 不用使用.value,就可以获取到对象
    user.age++;
  }
script>

效果

vue3 setup语法糖写法基本教程_第4张图片

5、响应式基础总结

  • vue2中
    • 不管是基本数据类型,还是对象类型,都会使用Object.defineProperty的最后一个参数get、set方法,实现对数据的响应式。
    • 基本类型的数据,只能进行获取或者修改操作,实现起来毫无问题。
      • 对象或数组,不仅有获取、修改操作,还可以动态的添加属性,或者删除属性等操作,这种操作使用Object.defineProperty是监听不到的。下面第5.1章有演示
    • 针对上面这种操作,vue提供了几种解决办法
      • Vue重新实现了数组的一系列函数,才实现数组的响应式。
      • 另外,Vue还提供了$set(object,field,val),针对字面的动态添加对象属性/数组值的响应式
  • vue3中
    • 现在ES6提出了Proxy代理,使用代理,我们就可以监听到任何针对对象的操作。上面的问题才得以解决
    • Vue3针对数组和对象,就采取了Proxy的方式,实现了对象数据响应式。不需要再使用$set等方法了。下面5.2章有演示
    • 针对上面演示,也可以发现,针对对象数据类型,返回的值为Proxy类型的数据
  • refreactive区别
    • ref:将所有类型的数据转换为响应式对象。在script使用时,需要.value访问,在模板语法中使用时则不需要。
    • reactive:只能将对象/数组类型数据转为响应式。在script使用时,不需要.value访问,直接访问即可。
  • 建议:
    • 不管是对象/数组还是基本类型数据,都建议使用ref进行封装。详细请看为什么建议使用ref

5.1、vue2响应式失效演示

代码
<template>
  <div class="home">
    <button @click="addUserSex">添加用户性别-男button>
    <button @click="addUserHobby">添加用户爱好-羽毛球button>
    <h2>用户名:h2>{{ user.name }}
    <h2>密码:h2>{{ user.password }}
    <h2>性别:h2>{{ user.sex }}
    <h2>爱好:h2>{{ user.hobby }}
  div>
template>

<script>
export default {
  data() {
    return {
      user: {
        name: 'admin',
        password: '123456',
        hobby:[
          '篮球', '足球'
        ]
      }
    };
  },
  methods: {
    addUserSex() {
      this.user.sex = '男';
      console.log(this.user);
    },

    addUserHobby() {
      this.user.hobby[2] = '羽毛球';
      console.log(this.user);
    }
  },
};
script>
效果

vue3 setup语法糖写法基本教程_第5张图片

5.2、vue3相同赋值方式生效演示

代码
<template>
  <button @click="addUserSex">添加用户性别-男button>
  <button @click="addUserHobby">添加用户爱好-羽毛球button>
  <h2>用户名:h2>{{ user.name }}
  <h2>密码:h2>{{ user.password }}
  <h2>性别:h2>{{ user.sex }}
  <h2>爱好:h2>{{ user.hobby }}
template>

<script setup>
  import { ref, reactive } from "vue";

  const user = reactive(
    {
      name: 'admin',
      password: '123456',
      hobby:[
        '篮球', '足球'
      ]
    }
  )
  // 还是Proxy对象
  console.log("user", user);


  function addUserSex() {
    user.sex = '男';
    console.log(user);
  }

  function addUserHobby() {
    user.hobby[2] = '羽毛球';
    console.log(user);
  }
script>

效果

vue3 setup语法糖写法基本教程_第6张图片

6、条件渲染与列表渲染

  • 条件渲染依旧使用v-if、v-else-if、v-else,目标组件必须相邻
  • 列表渲染也依旧使用v-for="(item, index) in hobbys"。
  • 下面主要讲的是同时使用。其实vue是不建议同时使用的。
    • vue2中,v-if、v-for同时使用的话,v-for的优先级更高,v-if是可以拿v-for中的变量去动态判断的。
    • vue3中,v-if、v-for同时使用的话,则相反了,v-if的优先级更高,意味着在v-if中,是拿不到item去判断的!

6.1、vue2中演示

代码
<template>
  <div class="home">
    <ul>
      
      <li v-for="item in hobby" :key="item" v-if="item != '打球'">{{ item }}li>
    ul>
  div>
template>

<script>
export default {
  data() {
    return {
      hobby:[
        '篮球', '足球', '排球', '打球'
      ]
    };
  },
};
script>
效果

vue3 setup语法糖写法基本教程_第7张图片

6.2、vue3中演示

代码
<template>
  <ul>
      
      <li v-for="item in hobby" :key="item" v-if="item != '打球'">{{ item }}li>
  <ul>
template>

<script setup>
  import { ref, reactive } from "vue";

  const hobby = reactive(
    [
      '篮球', '足球', '排球', '打球'
    ]
  )
script>
效果

vue3 setup语法糖写法基本教程_第8张图片

7、模板引用

  • 在vue2中,我们给dom标签上使用ref="xxx",然后在js中,使用this.$refs.xxx获取绑定的dom元素。
  • 在vue3中,我们需要在script中,定义和ref绑定的同名的变量(ref响应式函数返回),vue即可自动绑定上。

7.1、vue2中

代码
<template>
  <div class="home">
  <button @click="focusInput">激活光标button>
    <input type="text" ref="input">
  div>
template>

<script>
export default {
  methods: {
    focusInput() {
      console.log(this.$refs.input);
      this.$refs.input.focus();
    }
  }
};
script>
效果

vue3 setup语法糖写法基本教程_第9张图片

7.3、vue3中

代码
<template>
  <button @click="focusInput">激活光标button>
  <input type="text" ref="input">
template>

<script setup>
  import { ref, reactive } from "vue";

  // 变量名必须与dom标签中ref绑定的变量一致 参数不管写不写,值都是ref绑定的dom。且必须放到script中,不能放到函数中
  const input = ref();
  console.log("input", input);

  function focusInput() {
    input.value.focus();
  }
script>
效果

vue3 setup语法糖写法基本教程_第10张图片

8、计算属性

  • vue2中,我们使用的是选项式API(computed{})定义,此时它是一个对象。
  • vue3中,我们使用的是vue提供的computed()函数定义,此时它是一个函数,并且没有参数。
    • 如果参数使用的是函数的话,那么默认是get方法
    • 如果参数是一个对象的话,那么可以单独定义get、set方法

8.1、vue2中

代码
<template>
  <div class="home">
    <button @click="changeVal">val值+1button>
    <h2>val值:h2>{{ val }}
    <h2>val是否为偶数:h2>{{ flag }}
  div>
template>

<script>
export default {
  computed: {
    flag() {
      return this.val % 2 == 0;
    }
  },
  data() {
    return {
      val: 1,
    }
  },
  methods: {
    changeVal() {
      this.val++;
    }
  }
};
script>
效果

vue3 setup语法糖写法基本教程_第11张图片

8.2、vue3中

代码
<template>
  <button @click="changeVal">val值+1button>
  <h2>val值:h2>{{ val }}
  <h2>val是否为偶数:h2>{{ flag }}
template>

<script setup>
  import { ref, reactive, computed } from "vue";

  const val = ref(1);

  const flag = computed(() => {
    // 记得使用.value
    return val.value % 2 == 0;
  })
  
  function changeVal() {
    val.value++;
  }
script>

效果

vue3 setup语法糖写法基本教程_第12张图片

9、侦听器-watch

  • 监听按数量可以分为两类:单个数据监听、多个数据监听
  • 按监听的数据类型分类可以分为四类:基本数据类型、对象、对象基本属性、对象中对象/数组属性
  • 注意:如果监听由reactive创建出来的对象的话,不需要什么配置,默认就是深度监听。如果是ref创建出来的对象的话,需要手动打开deep:true配置
  • 监听对象的话,newValue和oldValue的值相同

9.1、语法

watch(被监听的双向绑定的数据, 数据改变的回调, 配置)

9.2、监听基本数据类型

代码
<template>
  <button @click="changeVal">val值+1button>
  <h2>val值:h2>{{ val }}
template>

<script setup>
  import { ref, reactive, computed, watch } from "vue";

  const val = ref(1);

  watch(val, (newVal, oldVal) => {
    console.log("val的值改变了", newVal, oldVal);
  })
  
  function changeVal() {
    val.value++;
  }
script>
效果

vue3 setup语法糖写法基本教程_第13张图片

9.3、监听对象

  • 默认是深度监听
  • 如果想监听整个user改变的话,就不能使用reactive了,要使用ref,然后修改user.value = ...即可监听到
代码
<template>
  <button @click="changeAge">用户年龄+1button>

  <h2>用户名h2>{{ user.name }}
  <h2>用户年龄h2>{{ user.age }}
template>

<script setup>
  import { ref, reactive, computed, watch } from "vue";

  let user = reactive({
    name: "张三",
    age: 18
  })

  // 只有由reactive创建出来的对象,才不需要手动开启deep配置,默认深度监听
  watch(user, (newVal, oldVal) => {
    console.log("user的值改变了", newVal, oldVal);
  })

  
  function changeAge() {
    user.age++;
  }
script>
效果

vue3 setup语法糖写法基本教程_第14张图片

9.4、监听对象中基本属性

监听对象中的属性,要提供一个get函数才可以

代码
<template>
  <button @click="changeAge">用户年龄+1button>

  <h2>用户名h2>{{ user.name }}
  <h2>用户年龄h2>{{ user.age }}
template>

<script setup>
  import { ref, reactive, computed, watch } from "vue";

  let user = reactive({
    name: "张三",
    age: 18
  })

  // 参数1为get函数
  watch(() => user.age, (newVal, oldVal) => {
    console.log("userAge的值改变了", newVal, oldVal);
  })
   
   
  // 错误,因为得到的是一个基本数据类型number。如果监听的是user中的对象/数组的话,则这么写
  //watch(user.age, (newVal, oldVal) => {
  //  console.log("userAge的值改变了", newVal, oldVal);
  //})
  

  function changeAge() {
    user.age++;
  }
script>
效果

vue3 setup语法糖写法基本教程_第15张图片

9.5、监听对象中对象/数组

代码
<template>
  <button @click="changeAge">用户年龄+1button>
  <button @click="changeHobby">修改用户爱好button>
  <button @click="changeAddress">修改用户地址button>


  <h2>用户名h2>{{ user.name }}
  <h2>用户年龄h2>{{ user.age }}
  <h2>爱好h2>{{ user.hobbys }}
template>

<script setup>
  import { ref, reactive, computed, watch } from "vue";

  let user = reactive({
    name: "张三",
    age: 18,
    hobbys: ["吃饭", "睡觉", "打豆豆"],
    address: {
      province: "浙江省",
      city: "杭州市",
      county: "西湖区"
    }
  })

  // 不需要get函数了
  watch(user.hobbys, (newValue, oldValue) => {
    console.log("爱好改变了", newValue, oldValue);
  })

  // 不需要get函数了
  watch(user.address, (newValue, oldValue) => {
    console.log("地址改变了", newValue, oldValue);
  })

  function changeHobby() {
    user.hobbys.push("打游戏");
  }
  
  function changeAge() {
    user.age++;
  }

function changeAddress() {
  user.address.city = "温州市";
}
script>
效果

vue3 setup语法糖写法基本教程_第16张图片

9.6、监听配置

  • vue2一样,同样有两个配置immediate、deep
  • 如果监听的是由reactive创建出来的对象的话,deep失效,默认深度监听。如果是由ref创建出来的对象的话,则需要手动开启deep配置
代码
watch(user, (newVal, oldVal) => {
    console.log("user的值改变了", newVal, oldVal);
}, {
    immediate: true, // 立即执行
    deep: true // 深度监听
})

10、侦听器-watchEffect

  • watchEffect和计算属性差不多,不用显式的声明监视哪个属性,它会自动收集函数中用到的响应式数据,如果响应式数据改变了,就会自动执行
  • 如果你想监听一个对象中的多个属性的话,使用watchEffect会更好,因为它只会监听函数中用到的属性,而不是整个对象中的所有属性
  • 如果函数里面有数组类型的数据,则监听不到整个数组的变化,只能监听某一个下标值的变化
  • 刚加载出来的时候,会执行一次,相当于默认immediate: true

10.1、代码

<template>
  <button @click="changeAge">用户年龄+1button>
  <button @click="changeAddress">修改用户地址button>
  <button @click="changeHobbys">修改用户爱好button>


  <h2>用户名h2>{{ user.name }}
  <h2>用户年龄h2>{{ user.age }}
  <h2>爱好h2>{{ user.hobbys }}
template>

<script setup>
  import { ref, reactive, computed, watch, watchEffect } from "vue";

  let user = reactive({
    name: "张三",
    age: 18,
    hobbys: ["吃饭", "睡觉", "打豆豆"],
    address: {
      province: "浙江省",
      city: "杭州市",
      county: "西湖区"
    }
  })


  /* 没有新旧值的参数 */
  watchEffect((aaa) => {
    console.log("userAge可能改变了", user.age);
    console.log("userHobby可能改变了", user.hobbys[0]);
    // console.log("userHobby可能改变了", user.hobbys); 监听失效
    console.log("userAddress可能改变了", user.address.city);
  })

  function changeHobbys() {
    user.hobbys[0] = "打游戏";
  }

  function changeAge() {
    user.age++;
  }

function changeAddress() {
  user.address.city = "温州市";
}
script>

10.2、效果

vue3 setup语法糖写法基本教程_第17张图片

11、侦听器总结

  • 参考官网:侦听器 | Vue.js (vuejs.org)
  • 取消侦听器:watch、watchEffect都会返回一个取消当前侦听器的函数,调用函数即可取消侦听。
  • watch
    • 可以直接对双向绑定的基本类型数据进行监听。
    • 如果监听由reactive创建出来的对象,则默认是深度监听,ref创建出来的话,需要手动开启深度监听配置。并且newValue和oldValue的值相同。
    • 如果监听对象里面的基本属性,则第一个参数需要传入那个属性的一个get函数
    • 如果监听对象里的的对象/数组,则第一个参数直接使用对象.属性即可,不需要封装为get函数
  • watchEffect
    • 不需要指明被监听的对象,函数里用到的响应式数据,会自动被监听到。
    • 非常适合对象里的多个属性被监听都会执行同一段逻辑的时候。
    • 如果函数里用到数组的话,则只会在函数内指明数组下标获取数据时,对应下标数据发生变化才会被监听到。监听数组还是建议使用watch监听

2、深入组件

1、组件注册

1.1、全局注册

方法一
import { createApp } from 'vue'
import App from './App.vue'


createApp(App)
.use(ElementPlus)
.component('MyButton2', {
    // 组件实现。。。
})
.mount('#app')
方法二
import { createApp } from 'vue'
import App from './App.vue'
// 全局导入
import MyButton2 from '@/components/MyButton2';


createApp(App)
.component('MyButton2', MyButton2)
.mount('#app')
方法三
import { createApp } from 'vue'
import App from './App.vue'
// 全局导入
import MyButton2 from '@/components/MyButton2';
import MyButton3 from '@/components/MyButton3';

createApp(App)
.component('MyButton2', MyButton2) // 链式导入
.component('MyButton3', MyButton3)
.mount('#app')

1.2、局部注册

  • 无需像vue2一样,需要导入->注册了。直接导入即可使用
<template>
  
  <MyButton>MyButton>
  
  <MyButton2>MyButton2>
template>

<script setup>
  import { ref, reactive, nextTick } from 'vue';
  import MyButton from '@/components/MyButton.vue';
  

script>

2、props

  • 在使用

你可能感兴趣的:(vue.js,前端,javascript)