vue3笔记2之“组合函数、setup的2个注意点、reactive和ref3个区别”

一、Composition API

1.组合式 API

在Vue2中,我们使用的是Options API ,配置项式的API,我们要创建一个Vue实例,然后在里面传入一个配置对象,里面要写data、methods、watch等的东西,而Vue3提出了全新的 Composition API,组合式API,我们不用直接创建Vue实例,而是创建一个app,然后按需引入需要的API,来进行使用...

2.Options API 存在的问题

使用传统Options API(配置式API)中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。

3.Composition API 的优势

我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。

二、拉开序幕的setup

  1. 理解:Vue3.0中一个新的配置项,值为一个函数。

  2. setup是所有Composition API(组合API)“ 表演的舞台 ”。

  3. 组件中所用到的:数据、方法等等,均要配置在setup中。

  4. setup函数的两种返回值:

    1. 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)

    2. 若返回一个渲染函数:则可以自定义渲染内容。(了解) (不常用)

<template>
  <h1>博主的信息</h1>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>性别:{{gender}}</h2>
  <button @click="sayInfo">显示信息</button>
</template>

<script>
// import {h} from 'vue'
export default {
  name: "App",
  //此处只是测试一下setup,暂时不考虑响应式的问题。
  setup(){
    // 数据
    let name = "YK菌"
    let age = 18
    let gender = "男"

    // 方法
    function sayInfo(){
      alert(`你好${name},你太厉害了吧`)
    }
    //返回一个对象常用
    return {
      name,age, gender,sayInfo
    }
    //返回一个函数不常用
    // return ()=> h('h1','YK菌yyds')
  }
};
</script>

如果返回的是渲染函数,那你在template里写的模板都不奏效了,页面渲染的就是你写的h函数中的内容

注意点:

  1. 尽量不要与Vue2.x配置混用

    • Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。

    • 但在setup中不能访问到Vue2.x配置(data、methos、computed...)。

    • 如果有重名, setup优先。

  2. setup不能是一个async函数,因为返回值不再是对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)

三、响应式ref数据上面的数据不是响应式的数据,我们修改它,页面不会有更新,如何定义响应式的数据呢?

  • 作用: 定义一个响应式的数据

  • 语法: const xxx = ref(initValue)

  • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。

  • JS中操作数据: xxx.value

  • 模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>

  • 备注:

  • 接收的数据可以是:基本类型、也可以是对象类型,但是一般用来定义基本类型。

  • 基本类型的数据:响应式依靠的是类上的getter与setter完成的(我们等下看下源码你就知道了)。

  • 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。

<template>
  <h1>博主的信息</h1>
  <h2>姓名:{{ name }}</h2>
  <h2>年龄:{{ age }}</h2>
  <h2>职业: {{ job.type }}</h2>
  <h2>工资:{{ job.salary }}</h2>
  <button @click="sayInfo">显示信息</button>
  <button @click="changeInfo">修改信息</button>
</template>

<script>
import { ref } from "vue";
export default {
  name: "App",
  setup() {
    // 数据
    let name = ref("测试");
    let age = ref(18);
    let job = ref({
      type: "前端工程师",
      salary: "30K",
    });
    // 方法
    function sayInfo() {
      alert(`你好${name.value},你太厉害了吧,薪水${job.value.salary}这么高`);
    }
    function changeInfo() {
      name.value = "三十年后的测试";
      age.value = 48;
      job.value.type = "工程师";
      job.value.salary = "200K";
    }
    return {
      name,
      age,
      job,
      sayInfo,
      changeInfo,
    };
  },
};
</script>

  • 通过看源码可以知道调用ref会返回一个RefImpl的实例对象,RefImpl类中有getter和setter可以检测到数据的变化

function ref(value) {
    return createRef(value, false);
}

function createRef(rawValue, shallow) {
    if (isRef(rawValue)) {
        return rawValue;
    }
    return new RefImpl(rawValue, shallow);
}

class RefImpl {
    constructor(value, _shallow) {
        this._shallow = _shallow;
        this.dep = undefined;
        this.__v_isRef = true;
        this._rawValue = _shallow ? value : toRaw(value);
        this._value = _shallow ? value : convert(value);
    }
    get value() {
        trackRefValue(this);
        return this._value;
    }
    set value(newVal) {
        newVal = this._shallow ? newVal : toRaw(newVal);
        if (hasChanged(newVal, this._rawValue)) {
            this._rawValue = newVal;
            this._value = this._shallow ? newVal : convert(newVal);
            triggerRefValue(this, newVal);
        }
    }
}

四、响应式reactive

  • 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)

  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)

  • reactive定义的响应式数据是“深层次的”。

  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。

<template>
  <h1>博主的信息</h1>
  <h2>姓名:{{ yk.name }}</h2>
  <h2>年龄:{{ yk.age }}</h2>
  <h2>职业: {{ yk.job.type }}</h2>
  <h2>工资:{{ yk.job.salary }}</h2>
  <h2>爱好:{{ yk.hobby }}</h2>
  <h3>测试数据:{{ yk.job.a.b.c }}</h3>
  <button @click="changeInfo">修改信息</button>
</template>

<script>
import { reactive } from "vue";
export default {
  name: "App",
  setup() {
    // 数据
    let yk = reactive({
      name: "YK菌",
      age: 18,
      hobby: ["写博客", "学习", "看书"],
      job: {
        type: "前端工程师",
        salary: "30K",
        a: {
          b: {
            c: 666,
          },
        },
      },
    });

    // 方法
    function changeInfo() {
      yk.name = "三十年后的YK菌";
      yk.age = 48;
      yk.job.type = "工程师";
      yk.job.salary = "200K";
      yk.job.a.b.c = 888;
      // 直接通过数组下标修改,可以触发响应式
      yk.hobby[0] = "写小说";
    }
    return {
      yk,
      changeInfo,
    };
  },
};
</script>

五、reactive和ref区别

1.从定义数据角度对比

  • ref用来定义:基本类型数据。

  • reactive用来定义:对象(或数组)类型数据。

  • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。

2.从原理角度对比

  • ref通过类中的的getter与setter来实现响应式(数据劫持)。

  • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。

3.从使用角度对比

  • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。

  • reactive定义的数据:操作数据与读取数据:均不需要.value。

六、setup的两个注意点

1.setup执行的时机

在beforeCreate之前执行一次,this是undefined。

2.setup的参数

将setup接收的两个参数(props, context)打印在控制台,如下

  • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。

  • context:上下文对象

  • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs。

  • slots: 收到的插槽内容, 相当于 this.$slots。

  • emit: 分发自定义事件的函数, 相当于 this.$emit。

App组件和HelloWorld组件-父组件向子组件传递属性参数

<template>
  <h1>博主的信息</h1>
  <HelloWorld msg="你好啊" school="ABC"></HelloWorld>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
  name: "App",
  components: { HelloWorld },
};
</script>

<style></style>

<template>
  <h2>姓名:{{ yk.name }}</h2>
</template>

<script>
import { reactive } from "@vue/reactivity";
export default {
  name: "HelloWorld",
  props: ['msg'], // 不写全会报警告
  setup(props, context) {
    let yk = reactive({
      name: "YK菌",
    });
    console.log('props-----',props);
    console.log()
    console.log('context.attrs-----', context.attrs)
    return { yk };
  },
};
</script>

自定义事件-子传父

<template>
  <h1>博主的信息</h1>
  <HelloWorld @hello="showHelloMsg"></HelloWorld>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
  name: "App",
  setup() {
    function showHelloMsg(value) {
      alert(`你好啊,你触发了hello事件,我收到的参数是:${value}!`);
    }
    return { showHelloMsg };
  },
  components: { HelloWorld },
};
</script>
<template>
  <h2>姓名:{{ yk.name }}</h2>
  <button @click="test">测试触发一下HelloWorld组件的Hello事件</button>
</template>

<script>
import { reactive } from "@vue/reactivity";
export default {
  name: "HelloWorld",
  emits:["hello"], // 不写能执行,但是会报警告
  setup(props, context) {
    let yk = reactive({
      name: "YK菌",
    });
    function test() {
      context.emit("hello", "**子组件的信息**");
    }
    return { yk,test };
  },
};
</script>

七、Vue3.0中的响应式原理

实现原理

  • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。

  • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

Object.defineProperty(data, 'count', {
    get () {}, 
    set () {}
})

存在问题

  • 新增属性、删除属性, 界面不会更新。

  • 直接通过下标修改数组, 界面不会自动更新。

解决方案

  • 使用Vue.set、Vue.delete或者vm.$set、vm.$delete这些API

模拟Vue2中实现响应式

//源数据
let person = {
	name:'张三',
	age:18
}
//模拟Vue2中实现响应式
let p = {}
Object.defineProperty(p,'name',{
	configurable:true,
	get(){ //有人读取name时调用
		return person.name
	},
	set(value){ //有人修改name时调用
		console.log('有人修改了name属性,我发现了,我要去更新界面!')
		person.name = value
	}
})
Object.defineProperty(p,'age',{
	get(){ //有人读取age时调用
		return person.age
	},
	set(value){ //有人修改age时调用
		console.log('有人修改了age属性,我发现了,我要去更新界面!')
		person.age = value
	}
})

Vue3.0的响应式

上面例子中看到数组可以通过下标进行修改,我们再测试下增加属性和删除属性在Vue3中好不好使

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值