2025年3月14日 星期五 甲辰(龙)年 月十三 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > JavaScript

从入门到精通:Vue3一篇文章搞定!

时间:02-12来源:作者:点击数:12
城东书院 www.cdsy.xyz

目录

vue3语法

1、vue3初识

1、vue3简介

2、vue3特点:

3、创建vue3 工程项目

4、vue3目录结构分析

2、Composition API

1、setup函数

2、setup执行时间

3、ref 响应式函数

1、引言:

2、ref 函数

4、reactive 函数

5、vue3响应式原理

1、vue2.x的响应式原理

2、vue3响应式原理

6、Reactive 与 ref 对比

7、vue3中的组件传参

1、父传子

2、子传父

8、vue3中的计算属性

9、vue3 中的 watch监听器

10、vue3生命周期

11、vue3中自定义hook函数

12、toRef 与 toRefs

13、vue3 中的路由

14、vue3 中的vuex

vue3语法

1、vue3初识

1、vue3简介

2020.9 vue.js 发布3版本。 耗时2年多开发完毕。由尤雨溪团队打造。

2、vue3特点:

1、性能提升

  • 打包大小减少41%
  • 初次渲染快51%,更新渲染快133%
  • 内存占用减少54%

2、源码升级

  • 使用Proxy 代替Object.defineProperty 实现双向数据绑定
  • 重写虚拟DOM的实现和Tree-Shaking
  • vue3 更好的支持typescript

3、新的特性

​ composition API(组合式api)

  • setup配置
  • ref和reactive
  • watch 和watchEffect
  • provide和inject
    提供了新的内置组件
  • Fragment
  • Teleport
  • Suspense

其他改变

  • 新的生命周期钩子
  • 移除keyCode 作为v-on的修饰符
3、创建vue3 工程项目
  • // 1. 确保你的vue-cli 脚手架版本在4.5.0以上,这样才能创建
  • // 2. 检查脚手架版本  vue -V 或 vue --version
  • // 3. 如果版本低话,重新安装vue-cli   npm install -g @vue/cli  
  • // 4. vue create vue3-project
  • // 5. cd vue3-project
  • // 6. npm run serve 启动项目

vite模板搭建vue3项目

  • npm init vue@latest
4、vue3目录结构分析
  • // vue3 入口文件 main.js
  • // 引入的不是Vue构造函数了,而是createApp 这个工厂函数(工厂函数就是返回值为一个对象的函数)
  • import { createApp } from 'vue'  
  • import App from './App.vue'  
  • const app = createApp(App).mount('#app'// app 相当于vue2中的vm,但是比vm轻,里面绑定的属性和方法更少,而不是像vm那样用的不用的都绑上,显的有点重
  • // vue2入口文件 main.js
  • import Vue from 'vue'
  • import App from './App.vue'
  • const vm = new Vue({
  •   renderh => h(App),
  • }).$mount('#app')
  • // 比较 
  • // vue3 引入的是createApp 这个函数,而不是vue2中的的 Vue 构造函数啦。
  • // vue3中的 app 类似于之前vue2中的vm, 但是app应用实例对象比vm 更轻,里面绑定的属性和方法更少
  • 打开app.vue,发现vue3模板结构可以不使用根标签。
  • <template>
  • <img alt="Vue logo" src="./assets/logo.png">
  • <HelloWorld msg="Welcome to Your Vue.js App"/>
  • </template>

2、Composition API

Composition API 也叫组合式api 主要包括如下及部分:

1、setup函数
  • 它是vue3新增的一个配置项,值为一个函数,所有的组合式API 方法都写在在setup函数中。
  • 组件中用到的数据,方法 计算属性,事件方法,监听函数,都配置在setup 中。
  • setup的函数值为一个函数,该函数的返回值为一个对象,对象中的属性和方法均可以在模板中直接使用。代码如下:
  • <template>
  • <div>
  • <p>姓名:{{ name }}</p>
  • <p>年龄:{{ age }}</p>
  • <p @click="sayHello(10)">说话</p>
  • </div>
  • </template>
  • <script>
  • export default {
  • name: "App",
  • setup() { //为一个函数
  • //定义变量
  • let name = "张三";
  • let age = 20;
  • // 定义方法
  • function sayHello(m) {
  • alert(`${name}--${age}--${m}`);
  • }
  • return { //setup函数返回值为一个对象
  • name,
  • age,
  • sayHello,
  • };
  • },
  • };
  • </script>
  • 注意:setup 函数尽量不要和vue2.x混合使用
    • vue2.x中的(data,methods,computed)可以访问到 setup中的属性和方法
    • 在setup中不能访问vue2.x中的配置数据和方法如(data,methods,computed)
      代码如下:
    • <template>
    • <div>
    • <p>姓名:{{ name }}</p>
    • <p>年龄:{{ age }}</p>
    • <p @click="sayHello(10)">说话</p>
    • <p>{{ height }}</p>
    • </div>
    • <!-- vue2.x中的事件 -->
    • <div @click="test1">vue2中的事件test1</div>
    • <!-- vue3中的事件 -->
    • <div @click="test2">vue3中的事件test2</div>
    • </template>
    • <script>
    • export default {
    • name: "App",
    • data() {
    • return {
    • sex: "男", // vue2中的变量
    • height: 186, // vue2中的变量
    • };
    • },
    • methods: {
    • test1() {
    • console.log(this.name); // "张三"
    • console.log(this.age); // 20;
    • console.log(this.sayHello); // 函数
    • console.log(this.sex); // 男
    • },
    • },
    • setup() {
    • let name = "张三";
    • let age = 20;
    • let height = 176; // setup中的变量
    • function sayHello(m) {
    • alert(`${name}--${age}--${m}`);
    • }
    • function test2() {
    • console.log(this.name); // "张三
    • console.log(this.sayHello); // 函数
    • console.log(this.sex); // undefined vue3中的setup 访问不了vue2中数据
    • console.log(this.test1);// undefined vue3中的setup 访问不了vue2中方法
    • }
    • return {
    • name,
    • age,
    • sayHello,
    • test2,
    • height,// setup中的变量
    • };
    • },
    • };
2、setup执行时间

1、 setup 在beforeCreate之前执行,且setup中获取不到this this为undefined

  •  beforeCreate() {
  •     console.log("beforeCreate");
  •     console.log(this); // proxy 实例对象
  •   },
  •   setup() {
  •     console.log("setup");
  •     console.log(this); // undefined
  •   }

3、ref 响应式函数

1、引言:

如下代码:当点击执行changeFn函数,修改setup中定义的变量时,发现页面中的name和age的数据并没有修改,说明该数据不是响应式数据

  • <template>
  •   <div>111</div>
  •   <p>{{ name }}--{{ age }}</p>
  •   <button @click="changeFn">changeFn</button>
  • </template>
  • <script>
  • export default {
  •   name"App",
  •   setup() {
  •     //定义变量
  •     let name = "张三";
  •     let age = 20;
  •     // 定义方法
  •     function changeFn({
  •       name = "李四";
  •       age = 30;
  •     }
  •     return {
  •       //setup函数返回值为一个对象
  •       name,
  •       age,
  •       changeFn,
  •     };
  •   },
  • };
  • </script>
2、ref 函数
  • 它是 vue3中的一个函数,一般用于将基本类型数据处理成响应式数据。
  • 作用:定义一个基本类型的响应式的数据,只有数据成为响应式数据,这样当数据变化时,才能被监测到。
  • 使用时需要从vue中引入
    • <script>
    •    import { ref } from "vue"// 引入响应式函数ref
    •    ...
    • </script>
  • 语法:const xxx =ref(数据变量);结果 返回一个 RefImpl 的引用对象,获取时为 xxx.value
  • 在页面模板中使用数据直接 使用插值表达式,不需要加value
    姓名:{{ name }}
    ,因为vue3会自动帮你.value,所以可以拿到值
  • ref 函数实现数据响应式的原理还是利用了vue2的Object.defineProperty() 给数据设置 get set 监听函数,如下图:
    image-20211130230403826
  • 接收的数据类型可以是基本类型(**实现响应式原理为Object.defineProperty()**),也可以是对象类型(当为对象时,实现响应式的原理就是Proxy不是Object.defineProperty())
  • 这里根据大佬的反馈有点小问题哈,
    改正后的表述
    Vue 2 中的响应式系统基于 Object.defineProperty(),它用于对象的属性拦截和响应式处理。不论是基本数据类型还是对象类型,Vue 2 都是通过 Object.defineProperty() 的 getter 和 setter 方法来实现响应式的。
    Vue 3 则采用了 Proxy 实现响应式系统,不再使用 Object.defineProperty()。在 Vue 3 中,Proxy 可以直接代理整个对象或数组,而不仅限于属性级别的拦截。这意味着,Vue 3 可以对任何类型的数据进行响应式处理,包括基本类型和对象类型。
    具体来说,在 Vue 3 中:
  • 基本数据类型:使用 ref 包装,通过 ES6 类的 get 和 set 方法实现响应式,这些方法在引擎层面是通过 Object.defineProperty() 实现的。
  • 对象类型:使用 reactive 或 ref 包装,内部基于 Proxy 实现响应式。

点击如下change事件,修改name 和age

  • <template>
  • <div>
  • <!--name这个ref 引用对象在使用时不需要加value,vue3会自动帮你加value,所以可以拿到值-->
  • <p>{{ name }}--{{ age }}</p>
  • <p>{{ job.type }}--{{job.salary}}</p>
  • <p @click="change">说话</p>
  • </div>
  • </template>
  • <script>
  • import { ref } from "vue"; // 引入响应式函数ref
  • export default {
  • name: "App",
  • setup() {
  • let name = ref("张三"); //返回一个 ref 引用对象
  • let age = ref(20);
  • console.log(name)
  • // 当ref传的参数为对象时
  • let job = ref({
  • type: "前端工程师",
  • salary: "20k",
  • });
  • function change() {
  • name.value = "李四"; // ref对象.value 修改其值
  • age.value = 30;
  • job.value.type = "后端开发工程师";
  • job.value.salary = "30k";
  • }
  • return {
  • name,
  • age,
  • change,
  • job
  • };
  • },
  • };
  • </script>

4、reactive 函数

1、定义一个对象类型的响应式数据,返回一个Proxy 实例对象,不能用于基本数据类型,否则报错。(基本类型响应式数据使用ref)。

语法:const 代理对象= reactive(源对象) ,接收一个对象或数组,返回一个代理对象(即Proxy源对象

  • 使用时先从vue中引入
  • <script>
  • import { reactive } from "vue";
  • ...
  • </script>
  • 代码如下:
  • <template>
  •   <p>{{ job.type }} --{{ job.salary }}--{{ job.like.a.b }}--{{job.content}}</p>
  •   <p v-for="(item, index) in foodArr" :key="index">{{ item }}</p>
  •   <button @click="changeFn">changeFn</button>
  • </template>
  • <script>
  • import { reactive } from "vue";
  • export default {
  •   name"App",
  •   components: {},
  •   setup() {
  •     //定义对象
  •     let job = reactive({
  •       type"前端工程师",
  •       salary"20k",
  •       like:{
  •           a:{
  •               b:'不告诉你'
  •           }
  •       }
  •     });
  •     console.log(job);
  •     //定义数组
  •     let foodArr = reactive(["刷抖音""敲代码"]);
  •     console.log(foodArr);
  •     // 定义方法
  •     function changeFn({
  •       job.type = "后端开发工程师";
  •       job.salary = "30k";
  •       job.content = "写代码"// 给对象添加属性
  •       foodArr[0]='买包包' // 通过下标修改数组
  •     }
  •     return {
  •       //setup函数返回值为一个对象
  •       job,
  •       changeFn,
  •       foodArr
  •     };
  •   },
  • };
  • </script>
  • vue3中使用proxy 实现的数据响应去掉了vue2中不能检测到对象添加属性和通过下标修改数组而无法检测的情况。

5、vue3响应式原理

1、vue2.x的响应式原理
  • 实现原理:
    通过Object.defineProperty()对属性的读取,修改进行拦截。(也叫数据劫持)即对数据的操作进行监听
    •    let data = {
    •         name"张三",
    •         age30
    •     }
    •     let p = {} 
    •     Object.defineProperty(p, 'name', {    
    •         get() {
    •             console.log('访问了name属性');
    •             return data.name
    •         },
    •         set(val) {
    •             console.log('设置name属性');
    •             data.name = val
    •         }
    •     })
    • //如上存在2个问题:
    • // 无法添加和删除属性
    • // 无法捕获到修改数组下标改变对应数组
  • vue2.x存在的问题
    • 对象数据新增属性和删除属性,界面不会响应更新
    • <template>
    • <div id="app">
    • <p>{{ person.name }}</p>
    • <p>{{ person.age }}</p>
    • <p v-if="person.sex">{{ person.sex }}</p>
    • <button @click="addSex">添加属性</button>
    • <button @click="deleteAge">删除属性</button>
    • </div>
    • </template>
    • <script>
    • import Vue from "vue";
    • export default {
    • name: "App",
    • data() {
    • return {
    • person: {
    • name: "张三",
    • age: 20,
    • },
    • };
    • },
    • components: {},
    • methods: {
    • addSex() {
    • // this.person.sex = "男";
    • // console.log(this.person); // 数据被修改,但是vue监测不到
    • // this.$set(this.person, "sex", "男"); // 修改为第一种方式,vue可以检测到
    • Vue.set(this.person, "sex", "男"); // 修改为第二种方式,vue也可以检测到
    • console.log(this.person);
    • },
    • deleteAge() {
    • // delete this.person.age; // age被删除改,但是vue监测不到
    • this.$delete(this.person, "age"); // 修改为第一种方式,vue可以检测到
    • Vue.delete(this.person, "age"); // 修改为第二种方式,vue可以检测到
    • console.log(this.person);
    • },
    • },
    • };
    • </script>
    • 数组数据直接通过修改下标,界面不会响应更新
    • <div id="app">
    • <p v-for="(item, index) in nameArr" :key="index">{{ item }}</p>
    • <button @click="update">添加属性</button>
    • </div>
    • <script>
    • import Vue from "vue";
    • export default {
    • name: "App",
    • data() {
    • return {
    • nameArr: ["刘备", "关羽"],
    • };
    • },
    • methods: {
    • update() {
    • // this.nameArr[0] = "张飞"; // 数据被修改了,但是vue 检测不到
    • // this.nameArr.splice(0, 1, "张飞"); // 修改为第一种方式,数据可以被检测到
    • // this.$set(this.nameArr, 0, "张飞"); // 修改为第二种方式,数据可以被检测到
    • Vue.set(this.nameArr, 0, "张飞"); // 修改为第三种方式,数据可以被检测到
    • console.log(this.nameArr);
    • },
    • },
    • };
    • </script>
2、vue3响应式原理

1、首先看一下vue3是否还存在vue2.x 中的问题。

  • 对象数据新增属性和删除属性,不存在vue2.x 中的问题了。
  • <template>
  • <div>
  • <p>{{ person.name }}</p>
  • <p>{{ person.age }}</p>
  • <p>{{ person.sex }}</p>
  • <button @click="addSex">添加属性</button>
  • <button @click="deleteSex">删除属性</button>
  • </div>
  • </template>
  • <script>
  • import { reactive } from "vue";
  • export default {
  • name: "App",
  • components: {},
  • setup() {
  • let person = reactive({
  • name: "张三",
  • age: 20,
  • });
  • console.log(person);
  • //定义添加属性
  • function addSex() {
  • person.sex = "男";
  • console.log(person);
  • }
  • // 定义删除属性
  • function deleteSex() {
  • delete person.sex;
  • console.log(person);
  • }
  • return {
  • person,
  • addSex,
  • deleteSex,
  • };
  • },
  • };
  • </script>
  • 数组数据直接通过修改下标,修改数组,不存在vue2.x 中的问题了。
  • <template>
  • <div>
  • <p v-for="(item, index) in person.like" :key="index">{{ item }}</p>
  • <button @click="change">修改数组的值</button>
  • </div>
  • </template>
  • <script>
  • import { reactive } from "vue";
  • export default {
  • name: "App",
  • components: {},
  • setup() {
  • let person = reactive({
  • name: "张三",
  • age: 20,
  • like: ["打球", "敲代码"],
  • });
  • console.log(person); // proxy 实例对象
  • function change() {
  • person.like[0] = "打台球";
  • }
  • return {
  • person,
  • change,
  • };
  • },
  • };
  • </script>

2、vue3响应式原理

首先说一下Reflect的作用。

  • // Reflect是window下的一个内置对象
  • // 1. 使用reflect 访问数据
  •     let obj = {
  •         name'张三',
  •         age20
  •     }
  •     console.log(Reflect.get(obj, 'name')); // 张三
  • // 2.使用Reflect 修改数据
  •     Reflect.set(obj, 'age'50)
  •     console.log(obj);
  • //3.使用Reflect删除数据
  •  Reflect.deleteProperty(obj, 'name'
  •     console.log(obj);

vue3响应原理代码

通过Proxy代理,拦截对象中任意属性的变化,包括属性的读取,修改、设置、删除。

通过Reflect 反射对被代理对象的属性进行操作。

  •     let data = {
  •         name"张三",
  •         age30
  •     }
  •     console.log(Proxy);
  •     // 使用p 对象代理data, Proxy为window 下的内置代理函数
  •     let p = new Proxy(data, {
  •         // 读取属性
  •         get(target, propName) {
  •             // target 就是 data
  •             console.log(`读取p上个${propName}属性`);
  •             return Reflect.get(target, propName)
  •         },
  •         // 修改和设置属性
  •         set(target, propName, value) {
  •             // value 为赋的值
  •             console.log(`修改p的${propName}属性`);
  •             // target[propName] = value
  •             Reflect.set(target, propName, value)
  •         },
  •         //删除属性
  •         deleteProperty(target, propName) {
  •             console.log(`删除p上的${propName}属性`);
  •             // return delete target[propName]
  •             return Reflect.deleteProperty(target, propName)
  •         }
  •     })

6、Reactive 与 ref 对比

1、数据定义角度对比:

  • ref用来定义:基本数据类型
  • reactive 用来定义: 对象或数组类型数据
    注意:ref也可以定义对象或数组类型数据,它内部还是通过reactive转换成代理对象

2、原理角度对比:

  • ref通过Object.defineProperty() 的get和set 实现数据的响应式 即(数据劫持)
  • reactive 通过Proxy 实现数据响应式的,并通过Reflect 来操作源对象数据的。

3、从使用角度对比:

  • ref 定义的数据操作时需要使用 .value, 而插值表达式中使用时,不需要使用 .value
  • reactive 定义的数据操作都不需要 .value

7、vue3中的组件传参

组件关系:

父子 props、$panrent

子父 emit自定义事件 refs

兄弟 eventbus中央事件总线 vue3如果需要实现eventbus 安装第三方库mitt

跨层级 provider inject

组件状态共享工具: vuex pinia

1、父传子
  • 在父组件中给子组件设置自定义属性 tit,将要传递的参数赋值给tit属性
  • <!--父组件 -->
  • <template>
  • <p></p>
  • <Testvue3 :tit="schoolName">
  • <span>123</span>
  • </Testvue3>
  • </template>
  • <script>
  • import Testvue3 from "@/components/Testvue3";
  • export default {
  • name: "App",
  • components: { Testvue3 },
  • setup() {
  • let schoolName = "青山"; // 定义要传给子组件的数据
  • return {
  • schoolName, // 要使用的变量抛出去,这样就可以在页面模板中使用该变量
  • };
  • },
  • };
  • </script>
  • 在子组件中接收传过来的属性通过props ,这个和vue2 一样没有变化。
  • <!-- 子组件 -->
  • <template>
  • <p>{{ tit }}</p>
  • <button>点击事件,子传父</button>
  • </template>
  • <script>
  • export default {
  • data() {
  • return {};
  • },
  • props: ["tit"],
  • setup(props) {
  • // 参数props即为父组件传过来的参数
  • console.log(props)
  • return {
  • //setup函数返回值为一个对象
  • };
  • },
  • };
  • </script>
2、子传父
  • 给子组件绑定自定义事件,然后在setup中定义该事件对应的方法,因为setup中没有this ,this为undefined,所以vue的开发者为了解决该问题,在setup中提供了2个形参,prop和context
    • props 为父传子的参数
    • context 上下文对象,里面有emit 方法,可以实现子传父
  • 子组件中多了 emits选项,该选项类似于props,接收父组件给子组件绑定的自定义方法,如果不加该选项,vue3 会提示警告。但不影响功能
  • <!-- 子组件 -->
  • <template>
  • <p>{{ tit }}</p>
  • <button @click="emit">点击事件,子传父</button>
  • </template>
  • <script>
  • import { reactive } from "vue";
  • export default {
  • data() {
  • return {};
  • },
  • emits: ["transfer"], // 在子组件中使用emits配置项,接收父组件给我绑定的自定义事件,用法类似于props, // 不加该配置项,控制台会提示警告
  • setup(props, context) {
  • console.log(11, props);
  • console.log(22, context);
  • // 定义方法
  • function emit() {
  • // 子传父 此处不用this,使用context上下文对象
  • context.emit("transfer", 666);
  • }
  • return {
  • //setup函数返回值为一个对象
  • emit,
  • };
  • },
  • };
  • </script>
  • 在父组件接收自定义事件,该事件对应的执行函数的形参就是传过来的数据,这个就和vue2一样啦。
    • <!--父组件 -->
    • <template>
    • <p></p>
    • <Testvue3 @transfer="showdata">
    • <span>123</span>
    • </Testvue3>
    • </template>
    • <script>
    • import Testvue3 from "@/components/Testvue3";
    • export default {
    • name: "App",
    • components: { Testvue3 },
    • setup() {
    • function showdata(value) {
    • console.log(value);
    • }
    • return {
    • showdata,
    • };
    • },
    • };
    • </script>

8、vue3中的计算属性

同vue2不同,使用计算属性需要引入computed 方法

  • <template>
  • <p>姓:<input type="text" v-model="data.firstname" /></p>
  • <p>名:<input type="text" v-model="data.lastname" /></p>
  • <p>姓名:<input type="text" v-model="data.fullname" /></p>
  • </template>
  • <script>
  • // 引入对应的计算属性方法
  • import { reactive, computed } from "vue";
  • export default {
  • name: "App",
  • setup() {
  • let data = reactive({
  • firstname: "",
  • lastname: "",
  • fullname: "",
  • });
  • // 计算属性--简写
  • // data.fullname = computed(() => {
  • // return data.firstname + data.lastname;
  • // });
  • // 计算属性--完整写法
  • data.fullname = computed({
  • get() {
  • return data.firstname + data.lastname;
  • },
  • set(value) {
  • console.log(value);
  • data.firstname = value.substr(0, 1);
  • data.lastname = value.substr(1);
  • },
  • });
  • return {
  • data,
  • };
  • },
  • };
  • </script>

9、vue3 中的 watch监听器

vue3 中的watch 也是 组合式api中的一个方法,所以使用时,需要引入

  • <template>
  • <p>{{ sum }} <button @click="sum++">sum++</button></p>
  • <p>{{ fullname }} <button @click="fullname += '-'">修改姓名</button></p>
  • <p>
  • {{ userinfo.name }}--{{ userinfo.age }}--{{ userinfo.job.type }}--{{
  • userinfo.job.salary
  • }}K
  • <button @click="userinfo.age++">修改年龄</button>
  • <button @click="userinfo.job.salary++">修改薪水</button>
  • </p>
  • </template>
  • <script>
  • // 引入对应的计算属性方法
  • import { ref, watch, reactive } from "vue";
  • export default {
  • name: "App",
  • setup() {
  • let sum = ref(0);
  • let fullname = ref("张三");
  • let userinfo = reactive({
  • name: "李四",
  • age: 20,
  • job: {
  • type: "web开发",
  • salary: 20,
  • },
  • });
  • //1、监听ref定义的响应式数据 immediate初始化就执行watch
  • watch(sum, (newvalue, oldvalue) => {
  • console.log(newvalue, oldvalue);
  • },{immediate:true});
  • //2、 监听ref定义的多个响应式数据,immediate初始化就执行watch
  • watch([sum, fullname], (newvalue, oldvalue) => {
  • console.log(newvalue, oldvalue);
  • }, { immediate: true });
  • //3、 监听reactive 定义的响应式数据
  • // 注意:此处oldvalue 无效(新值与旧值一样),默认是深度监听,immediate初始化就执行watch
  • watch(
  • userinfo,
  • (newvalue, oldvalue) => {
  • console.log(newvalue, oldvalue);
  • },
  • { immediate: true });
  • return {
  • sum,
  • fullname,
  • userinfo,
  • };
  • },
  • };
  • </script>

watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:

  • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
  • watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。

10、vue3生命周期

1、vue3中的生命周期钩子函数与vue2中基本相同,只有如下发生变化,其他都不变.

  • beforeDestory => beforeUnmount
  • destoryed => unmounted

第一种方式:不使用组合式api的方式使用生命周期函数

  • <!-- 子组件 -->
  • <template>
  • <p>{{ sum }} <button @click="sum++">sum++</button></p> <!--修改sum数据触发update生命周期 -->
  • </template>
  • <script>
  • import { ref } from "vue";
  • export default {
  • setup() {
  • let sum = ref(0);
  • return {
  • sum,
  • };
  • },
  • beforeCreate() {
  • console.log("beforeCreate");
  • },
  • created() {
  • console.log("created");
  • },
  • beforeMount() {
  • console.log("beforeMount");
  • },
  • mounted() {
  • console.log("mounted");
  • },
  • beforeUpdate() {
  • console.log("beforeUpdate"); //页面数据发生改变触发
  • },
  • updated() {
  • console.log("updated"); //页面数据发生改变触发
  • },
  • beforeUnmount() {
  • console.log("beforeUnmount"); //组件卸载前触发
  • },
  • unmounted() {
  • console.log("unmounted"); //组件卸载后触发
  • },
  • };
  • </script>

在app.vue 中引入子组件

  • <template>
  • <Testvue3 v-if="flag"></Testvue3>
  • <button @click="flag = !flag">显示/隐藏</button> <!--修改flag 触发 beforeUnmount和 unmounted 生命周期-->
  • </template>
  • <script>
  • import { ref } from "vue";
  • import Testvue3 from "@/components/Testvue3";
  • export default {
  • name: "App",
  • components: {
  • Testvue3,
  • },
  • setup() {
  • let flag = ref(true);// 修改flag
  • return {
  • flag,
  • };
  • },
  • };
  • </script>
  • 第二种方式:通过使用组合式api的形式使用生命周期 ,vue3和vue2.x的生命周期对照表如下:
    setup 等同于 beforeCreate 和created
  • <!-- 子组件 -->
  • <template>
  • <p>{{ sum }} <button @click="sum++">sum++</button></p>
  • </template>
  • <script>
  • //引入对应的生命周期钩子函数
  • import {
  • ref,
  • onBeforeMount,
  • onMounted,
  • onBeforeUpdate,
  • onUpdated,
  • onBeforeUnmount,
  • onUnmounted,
  • } from "vue";
  • export default {
  • setup() {
  • let sum = ref(0);
  • onBeforeMount(() => {
  • console.log("beforeMount");
  • });
  • onMounted(() => {
  • console.log("mounted");
  • });
  • onBeforeUpdate(() => {
  • console.log("beforeUpdate");
  • });
  • onUpdated(() => {
  • console.log("updated");
  • });
  • onBeforeUnmount(() => {
  • console.log("beforeUnmount");
  • });
  • onUnmounted(() => {
  • console.log("unmounted");
  • });
  • return {
  • sum,
  • };
  • },
  • };
  • </script>

11、vue3中自定义hook函数

hook函数定义:本质是一个函数,将setup中的公共逻辑抽离出来放在一个单独的js文件,这样哪个组件使用导入即可。

实现一个点击页面任意位置,在页面中输出当前点击位置的横纵坐标。

  • 实现代码:未使用hook 钩子函数前
  • <!-- 子组件 -->
  • <template>
  • <p>
  • <span>鼠标点击位置的X坐标为:{{ point.x }}</span>
  • <span>鼠标点击位置的Y坐标为:{{ point.y }}</span>
  • </p>
  • </template>
  • <script>
  • import { reactive, onMounted, onBeforeUnmount } from "vue";
  • export default {
  • setup() {
  • let point = reactive({
  • x: 0,
  • y: 0,
  • });
  • onMounted(() => {
  • document.onclick = function (event) {
  • point.x = event.pageX;
  • point.y = event.pageY;
  • };
  • });
  • onBeforeUnmount(() => {
  • document.onclick = null;
  • });
  • return {
  • point,
  • };
  • },
  • };
  • </script>
  • 使用hook 钩子函数 ,首先在src 目录下创建一个hooks文件夹,然后再该目录下创建usepoint.js文件,该文件用来存放抽来出来的公共方法。代码如下:
  • import { reactive, onMounted, onBeforeUnmount } from "vue";
  • export function usePoint({
  •     let point = reactive({
  •         x0,
  •         y0,
  •     });
  •     onMounted(() => {
  •         document.onclick = function (event{
  •             point.x = event.pageX;
  •             point.y = event.pageY;
  •         };
  •     });
  •     onBeforeUnmount(() => {
  •         document.onclick = null;
  •     });
  •     return point // 最后抛出该point
  • }
  • <!-- 子组件 -->
  • <template>
  • <p>
  • <span>鼠标点击位置的X坐标为:{{ point.x }}</span
  • >,
  • <span>鼠标点击位置的Y坐标为:{{ point.y }}</span>
  • </p>
  • </template>
  • <script>
  • // 在子组件中引入该hook方法
  • import { usePoint } from "@/hooks/usepoint";
  • export default {
  • setup() {
  • let point = usePoint();
  • return {
  • point,
  • };
  • },
  • };
  • </script>

12、toRef 与 toRefs

定义:toRef 创建一个ref 响应数据

语法:let name = toRef(person,'name') 将响应对象person中的name属性单独拿出来变成响应属性。

应用:一般用于将响应对象中的某个属性单独提供给外部使用

  • 如下是使用toRef 前的代码: 插值表达式模板中的 person 有点繁琐
  • <!-- 子组件 -->
  • <template>
  • <div>
  • <p>
  • {{ person.name }} -- {{ person.age }} -- {{ person.job.type }} --
  • {{ person.job.salary }}
  • </p>
  • </div>
  • </template>
  • <script>
  • import { reactive } from "vue";
  • export default {
  • setup() {
  • let person = reactive({
  • name: "张三",
  • age: 20,
  • job: {
  • type: "web前端开发",
  • salary: 30,
  • },
  • });
  • return {
  • person,
  • };
  • },
  • };
  • </script>
  • 如下是使用toRef 后 的代码,
  • <!-- 子组件 -->
  • <template>
  • <div>
  • <p>
  • <!-- 在模板中直接使用name,age,type,salary -->
  • {{ name }} -- {{ age }} -- {{ type }} --
  • {{ salary }}
  • </p>
  • <p>
  • <button @click="name += '-'">修改name</button>
  • <button @click="salary++">修改薪水</button>
  • </p>
  • </div>
  • </template>
  • <script>
  • import { reactive, toRef } from "vue";
  • export default {
  • setup() {
  • let person = reactive({
  • name: "张三",
  • age: 20,
  • job: {
  • type: "web前端开发",
  • salary: 30,
  • },
  • });
  • // 将person 中的name 属性转换成ref 响应式数据,这样就可以直接在模板中使用了,以此类推
  • let name = toRef(person, "name");
  • let age = toRef(person, "age");
  • let type = toRef(person.job, "type");
  • let salary = toRef(person.job, "salary");
  • return {
  • name,
  • age,
  • type,
  • salary,
  • };
  • },
  • };
  • </script>
  • <style scoped>
  • /* @import url(); 引入css类 */
  • </style>
  • 使用toRefs 可以将对象中的多个属性转换成响应数据,代码如下:
  • <!-- 子组件 -->
  • <template>
  • <div>
  • <p>
  • {{ name }} -- {{ age }} -- {{ job.type }} --
  • {{ job.salary }}
  • </p>
  • <p>
  • <button @click="name += '-'">修改name</button>
  • <button @click="job.salary++">修改薪水</button>
  • </p>
  • </div>
  • </template>
  • <script>
  • import { reactive, toRefs } from "vue";
  • export default {
  • setup() {
  • let person = reactive({
  • name: "张三",
  • age: 20,
  • job: {
  • type: "web前端开发",
  • salary: 30,
  • },
  • });
  • //toRefs返回一个响应对象,该对象中每个属性都变成了响应属性了。这样就可以直接拿来在模板插值表达式中使用了
  • let person1 = toRefs(person);
  • // console.log(person1);
  • return {
  • ...person1, // 使用后扩展运算符展开对象
  • };
  • },
  • };
  • </script>
  • <style scoped>
  • /* @import url(); 引入css类 */
  • </style>

关于数据响应式设置的问题:

1、如何设置一个响应式数据? ref reactive

2、如何将非响应式数据转为响应式数据? toRef toRefs

3、如何将数据设置为只读数据?不能够修改 readOnly

13、vue3 中的路由

1、首先下载安装vue3 对应的vue-router

  • npm install vue-router@4

2、在src目录下新建router目录,在该目录下新建index.js 文件,代码如下

  • //1. 从vue-router 引入对应的方法createRouter 和 createWebHashHistory
  • import { createRouter, createWebHashHistory } from "vue-router";
  • //2.配置路由规则
  • const routes = [
  •     { path'/home'component() => import('@/views/Home') },
  •     { path'/category'component() => import('@/views/Category') },
  • ]
  • //3.创建路由对象
  • const router = createRouter({
  •     // 4. Provide the history implementation to use. We are using the hash history for simplicity here.
  •     history: createWebHashHistory(),
  •     routes, // short for `routes: routes`
  • })
  • export default router

3、在main.js 中引入并绑定

  • // 引入路由 
  • import router from '@/router'
  • // 在全局实例上挂载router,这样每个页面都可以访问路由
  • createApp(App).use(store).use(router).mount('#app')

路由参数获取

  • <template>
  • <div>
  • 新闻页
  • </div>
  • </template>
  • <script lang="ts">
  • import { defineComponent } from 'vue'
  • import { useRoute } from 'vue-router';
  • export default defineComponent({
  • setup() {
  • // useRoute获取路由参数
  • const route = useRoute()
  • console.log(route);
  • console.log(route.query.id);
  • return {}
  • }
  • })
  • </script>
  • <style scoped></style>

路由方法使用

  • <template>
  • <div>
  • Index首页
  • <!-- <button @click="$router.push('/news')">新闻</button> -->
  • <button @click="goNews">新闻</button>
  • </div>
  • </template>
  • <script lang="ts">
  • import { defineComponent } from 'vue'
  • // 导入路由相关的组合式函数hook use开头的函数
  • import { useRouter } from 'vue-router'
  • export default defineComponent({
  • setup() {
  • // 组合式函数必须先调用一次
  • // useRouter 获取路由操作方法
  • const router = useRouter()
  • const goNews = () => {
  • // this.$router.push()
  • // console.log(router);
  • router.push('/news')
  • }
  • return { goNews }
  • }
  • })
  • </script>
  • <style scoped></style>

14、vue3 中的vuex

1、首先需要安装vuex

  • npm install vuex@next --save

2、在src目录下创建store 文件夹,该目录下创建index.js文件,代码如下:

  • import { createStore } from 'vuex'
  • // console.log(createStore);
  • export default createStore({
  •     state: {
  •         count100
  •     },
  •     getters: {
  •         getCount(state) {
  •             return state.count * 2
  •         }
  •     },
  •     mutations: {
  •         setTestA(state, value) {
  •             state.count = value
  •         }
  •     },
  •     actions: {
  •     },
  •     modules: {
  •     }
  • })

3、在main.js 引入然后使用挂载

  • import { createApp } from 'vue'
  • import App from './App.vue'
  • // 引入 store
  • import store from '@/store'
  • console.log(store);
  • // 挂载使用 这样全局就可以使用store了
  • createApp(App).use(store).mount('#app')
  • // console.log(createApp(App));

4.在页面组件中使用store

  • <!-- 子组件 -->
  • <template>
  • {{ count }} ----{{ double }}
  • <button @click="updateFun">修改数据</button>
  • </template>
  • <script>
  • import { computed } from "vue";
  • import { useStore } from "vuex";
  • export default {
  • setup() {
  • const store = useStore();
  • // 必须以计算属性形式输出,否则无法修改state中的数据 如count
  • const count = computed(() => {
  • return store.state.count;
  • });
  • const double = computed(() => {
  • return store.getters.getCount;
  • });
  • const updateFun = () => {
  • store.commit("setTestA", 20);
  • };
  • return {
  • count,
  • double,
  • updateFun,
  • };
  • },
  • };
  • </script>

vue官方在使用vue3时,更加推荐使用Pinia状态管理。Pinia和vuex实际一个开发团队在维护。vuex最终版本是4,可以将Pinia看作是vuex5。

src\stores\counter.ts

  • import { ref, computed } from 'vue'
  • // defineStore store构造函数
  • import { defineStore } from 'pinia'
  • export const useCounterStore = defineStore('counter'() => {
  •   // 初始化state
  •   const count = ref(0)
  •   // 使用计算属性获取结果
  •   const doubleCount = computed(() => count.value * 2)
  •   // actions方法  修改state的操作方法
  •   function increment({
  •     count.value++
  •     // console.log(count)
  •   }
  •   // 将需要外部使用到的值和方法按需导出
  •   return { count, doubleCount, increment }
  • })

src\App.vue

  • <template>
  • <div>
  • <button @click="increment"> {{ store.count }}</button>
  • <div>{{ store.doubleCount }}</div>
  • </div>
  • </template>
  • <script lang="ts">
  • import { defineComponent, toRef } from 'vue'
  • // 将usestore引入
  • import { useCounterStore } from './stores/counter'
  • export default defineComponent({
  • setup() {
  • // 调用store获取对象
  • const store = useCounterStore()
  • console.log(store);
  • // 从store对象中获取state值和修改state的方法
  • return { store, increment: store.increment }
  • }
  • })
  • </script>
  • <style scoped></style>
城东书院 www.cdsy.xyz
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐