Vue3新语法

本文介绍了Vue3的新特性,包括通过vue-cli和vite创建项目,重点讲解了组合式API如setup、ref、reactive、计算属性和watch监听器的用法。同时对比了Vue2和Vue3的生命周期,介绍了hook函数的概念和基础用法,以及toRef和refs在处理响应式数据时的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、简介

现在企业当中使用的基本都是vue2.x的版本,vue3.0是尤雨溪官方团队于2020年09月18日正式发布的。
vue3.0版本与其他版本完全不同,它最大限度的减少了开发人员配置工具的次数,另外增添了许多丰富的内置功能,还附带了一个完整的GUI用于创建和管理项目。

参考文档
vue3中文官网
菜鸟教程

二、语法特性

2.1初步入门

vue3中文网

2.1.1 vue-cli方式安装

必须保证你的vue-cli的版本在4.5以上,你可以用vue-V测试一下

如果不符合请重新安装

npm install -g @vue/cli

然后执行下面的三个步骤创建vue脚手架项目:

  1. vue create 项目
  2. cd 项目
  3. npm run serve

2.1.1 使用vite方式创建

vitejs官网

操作步骤如下:

  1. npm init vite-app 项目名称
  2. cd 项目名
  3. npm install (安装所有依赖)
  4. npm run dev (启动项目)

三、常用组合式API(Composition API)

3.1 setup

  1. 以前vue2中定义数据放在data中,定义方法放在methods中,现在vue3全部定义在setup中
  2. setup中返回一个对象,对象中的属性在页面直接使用

vue页面如下操作:

<template>
  <div>
    <div>
      <p>{{name}}{{score}}</p>
      <p>{{fn()}}</p>
    </div>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  setup() {
    let name = "tom";
    let score = 80;
    function setData() {
      return `${name}你好,你考了${score}`;
    }
    return {
      name: name,
      score: score,
      fn: setData
    };
  }
};
</script>

但是注意vue3中vue2的语法也是正常可以使用的

<template>
  <div>
    <div>
      <p>{{name}}{{score}}</p>
      <p>{{fn()}}</p>
      <p>{{msg}}</p>
      <button @click="show">点击显示</button>
    </div>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      msg: "hello"
    };
  },
  methods: {
    show() {
      console.log(this.msg);
    }
  },
  setup() {
    let name = "tom";
    let score = 80;
    function setData() {
      return `${name}你好,你考了${score}`;
    }
    return {
      name: name,
      score: score,
      fn: setData
    };
  }
};
</script>

注意
虽然能实现,但是不建议混用,会出各种意向不到的问题

3.2 ref函数

ref函数是把普通的数据进行包裹,形成一个响应式的引用对象

  • 不包裹ref函数调用
<template>
  <div>
    <div>
      <p>{{name}}{{score}}</p>
      <button @click="fn">点击显示</button>
    </div>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  setup() {
    let name = "tom";
    let score = 80;
    function setData() {
      name = "jim";
      score = 100;
      console.log(name); //jim
      console.log(score); //100
    }
    return {
      name: name,
      score: score,
      fn: setData
    };
  }
};
</script>
  • ref函数包裹以后
<template>
  <div>
    <div>
      <p>{{name}}{{score}}</p>
      <button @click="fn">点击显示</button>
    </div>
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    let name = ref("tom");
    let score = ref(80);
    function setData() {
      name.value = "jim";
      score.value = 100;
      console.log(name); //RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 'tom', _value: 'tom'}
      console.log(score); //RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 80, _value: 80}
    }
    return {
      name: name,
      score: score,
      fn: setData
    };
  }
};
</script>
  • 如果是处理对象等复杂类型
<template>
  <div>
    <div>
      <p>{{name}}{{score}}</p>
      <p>{{info.sex}}</p>
      <p>{{info.address}}</p>
      <button @click="fn">点击显示</button>
    </div>
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    let name = ref("tom");
    let score = ref(80);
    let info = ref({
      sex: "男",
      address: "保定"
    });
    function setData() {
      name.value = "jim";
      score.value = 100;
      // 修改引用类型的数据
      info.value.sex = "女";
      info.value.address = "北京";
      console.log(name); //RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 'tom', _value: 'tom'}
      console.log(score); //RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 80, _value: 80}
      console.log(info.value); //Proxy {sex: '女', address: '北京'}
    }
    return {
      name: name,
      score: score,
      info: info,
      fn: setData
    };
  }
};
</script>

3.3 reactive

  • 基本类型用ref,对象类型建议用reactive
  • reactive包裹对象,用的时候用对象.属性
  • 可以处理对象和数组类型数据

3.3.1处理对象类型数据

<template>
  <div>
    <div>
      <p>{{info.sex}}</p>
      <p>{{info.address}}</p>
      <button @click="fn">点击显示</button>
    </div>
  </div>
</template>

<script>
import { reactive } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    let info = reactive({
      sex: "男",
      address: "保定"
    });
    function setData() {
      // 修改引用类型的数据
      info.sex = "女";
      info.address = "北京";
      console.log(info); //Proxy {sex: '女', address: '北京'}
    }
    return {
      info: info,
      fn: setData
    };
  }
};
</script>

3.3.2处理数组数据

<template>
  <div>
    <div>
      <p>{{arr}}</p>
      <button @click="fn">点击显示</button>
    </div>
  </div>
</template>

<script>
import {reactive } from "vue";
export default {
  name: "HelloWorld",
  setup() {

    let arr = reactive(["敲代码", "听音乐", "打游戏"]);
    function setData() {
      arr[0] = "改变后的值";
    }
    return {
      arr: arr,
      fn: setData
    };
  }
};
</script>

3.3.3建议处理数据如下

<template>
  <div>
    <div>
      <p>{{person.name}}{{person.score}}</p>
      <p>{{person.info.sex}}</p>
      <p>{{person.info.address}}</p>
      <p>{{person.arr}}</p>
      <button @click="fn">点击显示</button>
    </div>
  </div>
</template>

<script>
import { reactive } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    let person = reactive({
      name: "tom",
      score: 80,
      info: {
        sex: "男",
        address: "保定"
      },
      arr: ["敲代码", "听音乐", "打游戏"]
    });
   
    function setData() {
      person.name = "jim";
      person.score = "100";
      person.sex = "女";
      person.address = "北京";
      person.arr[0] = "改变后的值";
    }
    return {
      person: person,
      fn: setData
    };
  }
};
</script>

3.4 setup参数

  • setup中有props和context两个参数
  • props是接收组件外部传递过来的对象
  • context是上下文对象
    1. 里面的attrs也是接收组件外部传递的对象,但是如果在props中声明的属性不会获取到
    2. slots相当于接收到的插槽内容
    3. emit相当于组件分发的自定义事件函数

3.4.1 vue3创建组件并注册

  • components中定义Test1.vue
<template>
  <div>
    <div>
      <p>{{person.name}}{{person.score}}</p>
      <button @click="fn">点击显示</button>
    </div>
  </div>
</template>

<script>
import { ref, reactive } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    let person = reactive({
      name: "tom",
      score: 80
    });
    function setData() {
      person.name = "jim";
      person.score = "100";
    }
    return {
      person: person,
      fn: setData
    };
  }
};
</script>

App.vue中引入Test1子组件

<template>
  <!-- <HelloWorld  /> -->
  <Test1/>
</template>

<script>
import Test1 from "./components/Test1.vue";

export default {
  name: "App",
  components: {
    Test1
  }
};
</script>

3.4.2 props使用

App.vue传递如下:

<template>
  <!-- <HelloWorld  /> -->
  <Test1 msg="传递数据" num="传递数量"/>
</template>


Test1.vue接收如下:

<template>
  <div>
    <div>
      <p>{{person.name}}{{person.score}}</p>
      <p>{{msg}}{{num}}</p>
      <button @click="fn">点击显示</button>
    </div>
  </div>
</template>

<script>
import { ref, reactive } from "vue";
export default {
  name: "HelloWorld",
  props: ["msg", "num"],
  setup(props) {
    let person = reactive({
      name: "tom",
      score: 80
    });
    function setData() {
      person.name = "jim";
      person.score = "100";
      console.log(props); //Proxy {msg: '传递数据', num: '传递数量'}
    }
    return {
      person: person,
      fn: setData
    };
  }
};
</script>

3.4.3 context

(1)attrs

3.5 vue3中的计算属性

vue3中的计算属性和vue2中的计算属性一致,都是对数据进行监听计算的。

3.5.1 计算属性基础用法

Test2.vue

<template>
  <div>
    <div>
        <h3>请输入一个人的全名信息</h3>
      <input type="text" v-model="person.firstname">
      <input type="text" v-model="person.lastname">
      <p>全名{{fullName}}</p>
    </div>
  </div>
</template>

<script>
import { ref, reactive, computed } from "vue";
export default {
  name: "HelloWorld",
  setup(props, context) {
    let person = reactive({
      firstname: "张",
      lastname: "三丰"
    });
    // 计算属性
    let fullName = computed(() => {
      return person.firstname + person.lastname;
    });
    return {
      person,
      fullName
    };
  }
};
</script>

App.vue引入并挂载

<template>
  <Test2 msg="hello" num="1000"/>
</template>

<script>
import Test2 from "./components/Test2.vue";

export default {
  name: "App",
  components: {
    Test2
  }
};
</script>

3.5.2 更改计算属性

上面写法计算属性是只读的,不能更改,如果需要更改需要设置成get和set

get是获取计算属性中的值,set是设置计算属性中的值

Test2修改如下,注意set中的value就是你前面get的拼接的结果值

<template>
  <div>
    <div>
        <h3>请输入一个人的全名信息</h3>
      <input type="text" v-model="person.firstname">
      <input type="text" v-model="person.lastname">
      <p>全名{{person.fullName}}</p>
      <!-- 更改计算属性 -->
      <input type="text" v-model="person.fullName">
    </div>
  </div>
</template>

<script>
import { ref, reactive, computed } from "vue";
export default {
  name: "HelloWorld",
  setup(props, context) {
    let person = reactive({
      firstname: "张",
      lastname: "三丰"
    });
    // 计算属性
    person.fullName = computed({
      get() {
        return person.firstname + "-" + person.lastname;
      },
      set(value) {
        var arr = value.split("-");
        person.firstname = arr[0];
        person.lastname = arr[1];
      }
    });
    // person.fullName = computed(() => {
    //   return person.firstname + person.lastname;
    // });
    return {
      person
    };
  }
};
</script>

3.6watch监听器

3.6.1 vue2中监听器复习

(1)普通监听方法

<template>
  <div>
    <p>{{num}}</p>
    <button @click="addNum">点击+1</button>
  </div>
</template>

<script>
import { ref, reactive, computed } from "vue";
export default {
  name: "HelloWorld",
  data() {
    return {
      num: 0
    };
  },
  watch: {
    num(newValue, oldValue) {
      console.log("初识值是" + oldValue + "变化后的值是" + newValue);
    }
  },
  methods: {
    addNum() {
      this.num++;
    }
  }
};
</script>

(2)立即执行和深度监听方法

<template>
  <div>
    <p>{{num}}</p>
    <button @click="addNum">点击+1</button>
  </div>
</template>

<script>
import { ref, reactive, computed } from "vue";
export default {
  name: "HelloWorld",
  data() {
    return {
      num: 0
    };
  },
  watch: {
    num: {
      deep: true,
      immediate: true,
      handler(newValue, oldValue) {
        console.log("初识值是" + oldValue + "变化后的值是" + newValue);
      }
    }
  },
  methods: {
    addNum() {
      this.num++;
    }
  }
};
</script>

3.6.3 vue3中监听器

(1)监听ref函数包裹的响应式数据变化
<template>
  <div>
    <p>{{num}}</p>
    <button @click="addNum">点击+1</button>
  </div>
</template>

<script>
import { ref, watch } from "vue";
export default {
  name: "HelloWorld",
  data() {
    return {
      num: 0
    };
  },
  setup() {
    let num = ref(0);
    function addNum() {
      num.value++;
    }
    // 监听器写法
    watch(num, (newValue, oldValue) => {
      console.log("初识值是" + oldValue + "变化后的值是" + newValue);
    });
    return {
      num,
      addNum
    };
  }
};
</script>

(2)监听ref函数包裹的多个响应式数据变化
<template>
  <div>
    <p>{{num}}</p>
    <button @click="addNum">点击+1</button>
    <!-- 另一个监听项 -->
    <p>{{msg}}</p>
    <button @click="change">点击改变</button>
  </div>
</template>

<script>
import { ref, watch } from "vue";
export default {
  name: "HelloWorld",
  data() {
    return {
      num: 0
    };
  },
  setup() {
    let num = ref(0);
    let msg = ref("hello");
    function addNum() {
      num.value++;
    }
    function change() {
      msg.value += "你好!";
    }
    // 监听器写法
    watch(
      [num, msg],
      (newValue, oldValue) => {
        console.log(newValue); //[1,'hello']
        console.log(oldValue); //[0,'hello']
        console.log("初识值是" + oldValue + "变化后的值是" + newValue);
      },
      { immediate: true }
    );
    return {
      num,
      addNum,
      change
    };
  }
};
</script>

总结
vue3中的watch第一个参数可以是单个值,也可以用数组传递多个监听数据,第二个参数是监听函数,第三个参数是对象的形式传递监听项配置

四、新旧生命周期对比

4.1 vue2中的生命周期函数

总结如下

vue 的生命周期函数,有那些?在项目中怎么使用?以及应用场景?
1.vue的生命周期函数分为:创建前
beforeCreate :在实例初始化之后执行此时对象还未创建,el 和data并未初始化,因此无法访问 methods,data ,computed等方法和数据。

2.创建后:created:最早开始使用data和methods中的数据的钩子函数,这个阶段可以数据请求,但是不能dom操作。

3.挂载前:beforeMount:挂载开始之前被调用,把data里面的数据和模板生成html,完成了el和data初始化,注意此时还没有挂载到html页面上。

4.挂载后:mounted:挂载完成,HTML已经被渲染到了页面上,这个阶段可以执行dom操作,可以进行数据请求。

5.数据更新前:beforeUpdate:数据更新前调用,当data的数据发生改变会执行这个钩子 内存中数据是新的 页面是旧的。

6.数据更新后:updated:由于数据更改导致的虚拟 DOM 重新渲染,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。注意 updated 不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated 里使用 vm.$nextTick。

7.销毁前:beforeDestroy:实例销毁之前调用,在这一步还可以做一些释放内存的操作。

8.销毁后:destroy:实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有.的事件监听器被移除,所有的子实例也都被销毁。

4.2vue3中的生命周期函数

PXAG6e.png

4.2.1 生命周期函数是配置项

新建Test3.vue,代码如下:

<template>
  <div>
      <p>{{num}}</p>
      <button @click="addNum">点击+1</button>
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  name: "Test3",
  setup() {
    let num = ref(0);
    function addNum() {
      num.value++;
    }
    return {
      num,
      addNum
    };
  },
  beforeCreate() {
    console.log("vue实例初始化之前beforeCreate");
  },
  created() {
    console.log("vue实例初始化之后created");
  },
  beforeMount() {
    console.log("DOM结构渲染之前beforeMount");
  },
  mounted() {
    console.log("DOM结构渲染之后mounted,一般用于发网络请求");
  },
  beforeUpdate() {
    console.log("数据更新之前beforeUpdate");
  },
  updated() {
    console.log("数据更新之后updated");
  },
  beforeUnMount() {
    console.log("vue实例销毁之前beforeUnMount");
  },
  unmounted() {
    console.log("vue实例销毁之后unmounted");
  }
};

4.2.2 生命周期函数组合式API

  • beforeCreate====setup
  • created====setup
  • beforeMount===onBeforeMount
  • mounted===onMounted
  • beforeUpdate===onBeforeUpdate
  • updated===onUpdated
  • beforeUnmount===onBeforeUnmount
  • unmouned===onUnmouned

注意
下面这样写是把组合式API放在setup中

<template>
  <div>
     <p>{{num}}</p>
     <button @click="addNum">点击更新</button>
  </div>
</template>

<script>
// 导入reactive函数
import {
  ref,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted
} from "vue";
export default {
  name: "HelloWorld",
  setup() {
    let num = ref(0);
    function addNum() {
      num.value++;
    }
    // 调用组合式API
    onBeforeMount(() => {
      console.log("-----onBeforeMount");
    });
    onMounted(() => {
      console.log("-----onMounted");
    });
    onBeforeUpdate(() => {
      console.log("-----onBeforeUpdate");
    });
    onUpdated(() => {
      console.log("-----onUpdated");
    });
    onBeforeUnmount(() => {
      console.log("-----onBeforeUnmount");
    });
    onUnmounted(() => {
      console.log("-----onUnmounted");
    });

    return {
      num,
      addNum
    };
  }
};
</script>

五、hook函数

5.1 概念

hook函数是把setup中用到的组合式API进行封装,方便代码复用

5.2 基础用法

项目的src文件夹下面新建hook文件,新建useXX格式的js文件,代码如下:

比如我们命名usePoint.js

import { reactive, onMounted, onBeforeUnmount } from "vue";
function getPosition() {
  // 定义数据
  let point = reactive({
    x: 0,
    y: 0
  });
  // 定义处理数据的方法
  function savePoint(event) {
    point.x = event.pageX;
    point.y = event.pageY;
  }
  // 定义生命周期函数
  onMounted(() => {
    window.addEventListener("click", savePoint);
  });

  onBeforeUnmount(() => {
    window.removeEventListener("click", savePoint);
  });
  return point;
}

export default getPosition;

然后在需要使用使用导入并且使用,比如在Test1.vue如下:

<template>
  <div>
      <p>x轴坐标:{{point.x}}y轴坐标:{{point.y}}</p>
  </div>
</template>

<script>
// 导入hook
import position from "../hook/usePoint";
export default {
  name: "Test4",
  setup() {
    let point = position();
    return {
      point
    };
  }
};
</script>

六、toRef和refs

6.1 需求

如果我们在操作一些对象类型数据的时候如果直接返回,页面展示是比较繁琐的

6.2toRef

toRef的第一个参数是包裹的对象,第二个是解析的属性值

<template>
  <div>
      <p>{{name}}</p>
      <p>{{address}}</p>
      <p>{{hot}}</p>
  </div>
</template>

<script>
// 导入ref函数
import { reactive, toRef } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    // 定义初识数据
    let obj = reactive({
      name: "tom",
      info: {
        sex: "男",
        address: "保定",
        hobby: {
          hot: "music"
        }
      }
    });

    return {
      name: toRef(obj, "name"),
      address: toRef(obj.info, "address"),
      hot: toRef(obj.info.hobby, "hot")
    };
  }
};
</script>

6.3toRefs

import { reactive, toRefs } from "vue";
<template>
  <div>
      <p>{{name}}</p>
      <p>{{info.sex}}</p>
      <p>{{info.address}}</p>
  </div>
</template>

<script>
// 导入ref函数
import { reactive, toRef, toRefs } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    // 定义初识数据
    let obj = reactive({
      name: "tom",
      info: {
        sex: "男",
        address: "保定",
        hobby: {
          hot: "music"
        }
      }
    });
    return {
      ...toRefs(obj)
    };
  }
};
</script>

总结
如果需要把对象等复杂数据提供给外部,需要保持响应式属性,可以用toRef和toRefs包裹,一个是传递2个参数,一个是默认全部解析,传递一个参数即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值