免费查看本文章可前往我的网站:PiQiu
目录
1.3.5、setup 的参数(props、context)
一、Vue3 从入门到进阶
1.1、Vue3 相比于 Vue2 好在哪里?
Ps:以下数据来源于官网
a)性能提升:
- 打包大小减少 41%.
- 初次渲染快 55%,更新渲染快 133%.
- 内存减少 54%.
- ......
b )源码升级:
- 使用 Proxy 代替 defineProperty 实现响应式.
- 重写虚拟 DOM 的实现和 Tree-Shaking
- ......
c)拥抱 TypeScript:
- Vue3 可以更好的支持 TypeScript
d)新特性:
- setup 配置
- ref 与 reactive
- watch 与 watchEffect
- provide 与 inject
1.2、创建 vue-cli3 脚手架
Ps:具体怎么创建,可以看这篇 http://t.csdnimg.cn/rq4slhttp://t.csdnimg.cn/rq4sl
a)查看当前 vue-cli 的版本,确保在 4.5.0 以上.
b)安装或者升级 @vue/cli
npm install -g @vue/cli
c)创建
vue create vue_demo
d)启动
cd vue_demo
npm run serve
e)修改 vue.config.js 内容为如下内容,就是为了关闭语法检查
f)main.js 说明
//引入的不再是 vue 构造函数了, 引入的是一个名为 createApp 的工厂函数
import { createApp } from 'vue'
import App from './App.vue'
//创建应用实例对象 —— app
const app = createApp(App)
//挂载
app.mount('#app')
g)新特性之一,vue3 的模板接口可以没有根标签
1.3、Vue3 的使用
1.3.1、拉开序幕的 setup
vue3 中有一个新的配置项,值是一个函数. 他是所有组合 API 的“舞台”,也就是说,之前 vue2 组件中使用到的 data、methods...... 都要配置在 setup 中.
a)setup 函数有三种返回值:
- 若返回一个对象,那么对象的属性、方法,在模板种就可以使用了.
- 若返回的是一个渲染函数,那么就可以自定义渲染内容(后面细讲,当前只做了解).
b)注意点(请忘掉 vue2 中的 data、methods... 现在是 vue3 的时代):
- 尽量不要和 vue2.x 混用,原因如下:
- vue2.x 配置的 data、methods、 computed....... 种可以访问到 setup 中的属性方法,但是 setup 中不能访问到 vue2.x 配置中的 data、methos、computed......
- 如果有重名,setup 优先.
- setup 不是一个 async 函数,因为返回值不再是 return 的对象,而是 promise ,模板看不到 return 对象中的属性.
c)案例如下:
<template>
<!-- vue3 中模板接口可以没有跟标签 -->
<div>你好</div>
<div>欢迎来到 vue3</div>
<div>{{ name }}</div>
<button @click="sayHi">点我,进行自我介绍~</button>
</template>
<script>
export default {
name: "App",
//这里暂时限不考虑响应式的问题
setup() {
//数据
let name = "cyk";
let age = 18;
//方法
function sayHi() {
alert(`大家好啊,我是${name}, 今年 ${age} 了`);
}
return {
//模板中需要什么数据,就返回什么
name,
age,
sayHi,
};
},
};
</script>
点击按钮效果如下
d)你会发现,当你定义的变量越多,需要 return 的就越多,代码可读性降低了不少. 我们实际上可以把一组关联的数据封装到对象中,这样直接返回这个对象就可以了(这样也要注意的是需要通过 对象.属性名 的方式获取属性).
<template>
<div>name: {{ user.name }}</div>
<div>age: {{ user.age }}</div>
<div>weight: {{ user.weight }}</div>
</template>
<script>
export default {
name: "App",
setup() {
let user = {
name: "cyk",
age: 18,
weight: "60kg",
};
return {
user,
};
},
};
</script>
e)当然,有一天你发现自己定义的对象越来越多,最后 return 的东西越来越多,可读性又降低了,那么你可以借鉴 vue2 的方式这样定义数据(doge)
setup() {
let data = {
student: {
//...
},
teacher: {
//...
},
//......
};
return {
data,
};
},
1.3.2、ref 函数
什么是响应式数据
你可以简单的将响应式数据理解为动态数据,也就是说,你对响应式数据的修改,在页面是会得到及时的反馈.
例如通过点击按钮,对 name 进行修改操作,同时希望页面能得到及时的反馈.
非响应式:
<template>
<div>name: {{ name }}</div>
<button @click="changeName()">点我改变 name</button>
</template>
<script>
export default {
name: "App",
setup() {
let name = "cyk";
function changeName() {
name = "lyj";
alert("修改完成!");
}
return {
name,
changeName,
};
},
};
</script>
点击按钮之后,name 并没有修改,这就是非响应式,那么如果我们希望它是响应式就需要依靠 ref 函数了
ref 响应式处理
a)ref 就是用来处理一个响应式数据.
语法如下:
// 创建一个响应式数据的引用对象(reference 对象,简称 ref 对象)
const xxx = ref(initValue)
// js 中操作数据
xxx.value
// 模板中读取数据
<div>{{xxx}}</div>
使用步骤:
- 引入 ref 组件(这一步很容易忘记)
- 定义 响应式 对象
- 返回对象
通过 console.log(ref("lyj")),就可以观察到以下结果
ref 底层是通过 Object.defineProperty() 的 get 和 set 来实现响应式的
b)例如通过点击按钮,对 name 进行修改操作,同时希望页面能得到及时的反馈.
<template>
<div>name: {{ name }}</div>
<button @click="changeName()">点我改变 name</button>
</template>
<script>
import { ref } from "vue"; //引入 ref 组件
export default {
name: "App",
setup() {
let name = ref("cyk"); //这里返回的是一个对象,通过 .value 就可以拿到值
function changeName() {
name.value = "lyj"; //必须通过 value 才能获取到对应的值
alert("修改完成!");
}
return {
name,
changeName,
};
},
};
</script>
点击按钮前
点击按钮后
c)那么问题来了,如果 ref 中的值是一个嵌套对象,该怎么获取里面的值呢?难道每嵌套一层都需要使用 value 来获取一层么?
如果是嵌套对象,我们可以先这样理解,ref 就相当于对对象的一层封装嘛,通过 .value 的方式就可以拿到对象,此时对象无论怎么封装,原先 js 语法怎么获取,现在就怎么处理.
如下,怎么获取 name 和 c 呢?
let user = ref({
name: "cyk",
a: {
b: {
c: 666,
},
},
});
name 就不多说了,首先 ref 是通过里面的值是一个 {...} 对象,因此通过 value 就可以拿到这个对象,那么接下来通过对象拿属性值的方式获取即可.
function getUser() {
console.log(user);
console.log(user.value.name);
console.log(user.value.a.b.c);
}
对于对象的处理, ref 底层是 调用了 reactive 的 Proxy 来实现响应式的,并通过 Reflect 操作内部的数据.
1.3.3、reactive
a)通过上面讲述可以看出,ref 每次获取值都需要通过 .value 的方法获取,很麻烦. 那么接下来要将的 reactive 就是用来解决这个问题的.
b)reactive 就是用来定义个 对象类型 的响应式数据(基本类型不能用它,只能使用 ref 函数).
Ps:实在想用,你可以把基本类型封装成对象啊
c)reactive 内部是基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作.
d)用法如下:
<template>
<div>{{ user.name }}</div>
<div>{{ user.age }}</div>
<div>{{ user.likes[2] }}</div>
<div>{{ user.a.b.c }}</div>
<button @click="changeInfo">点我改变信息</button>
</template>
<script>
import { ref, reactive } from "vue"; //引入 reactive
export default {
name: "App",
setup() {
let tips = ref(1);
let user = reactive({
name: "cyk",
age: 18,
likes: ["唱歌", "弹琴", "谈情"],
a: {
b: {
c: 666,
},
},
});
function changeInfo() {
// ref 定义的响应式对象需要通过 .value 的方式获取
tips.value = 2;
// reactive 定义的响应式对象可以直接获取
user.name = "lyj";
user.age = 17;
user.likes[2] = "谈锤子";
user.a.b.c = 999;
}
return {
tips,
user,
changeInfo,
};
},
};
</script>
1.3.4、reactive 对比 ref
a)定义数据的角度:
- ref 可定义的数据:基本类型、对象(或数组)类型, 但是对于 对象(或数组)类型,内部是通过 reactive 转为 代理对象。
- reactive 可定义的数据:对象(或数组)类型.
b)使用的角度:
- ref:操作数据需要 .value 在模板中读取数据不需要 .value
- reactive:操作和读取数据都不用 .value
c)原理角度:
- ref:通过 Object.defineProperty() 的 get 与 set 来实现响应式.
- reactive:通过使用 Proxy 来实现响应式,并通过 Reflect 操作源对象内部的数据.
1.3.5、setup 的参数(props、context)
setup 的参数如下:
a)props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
例如父组件向 User 子组件传递数据
<template>
<User name="cyk" :age="age" />
</template>
<script>
import User from "./views/User.vue"; //引入自定义的组件 User
export default {
name: "App",
components: { User }, //声明
setup() {
let age = 18;
return {
age,
};
},
};
</script>
User 子组件中使用数据
<template>
<h1>我是一个学生</h1>
<div>name: {{ user.name }}</div>
<div>age: {{ user.age }}</div>
</template>
<script>
export default {
name: "User",
props: ["name", "age"],
setup(props) {
let user = {
name: props.name,
age: props.age,
};
return {
user,
};
},
};
</script>
b)context:上下文对象
- atts:值为对象,包含:组件外传递过来,但没有在 props 配置中声明的属性,相当于 this.$attrs.
- slots:受到的插槽内容,相当于 this.$slots.
- emit:分发自定义事件的函数,相当于 this.$emit.
父组件中定义 hello 事件,自定义名为 helloBtn 传递给子组件 User.
<template>
<!-- @自定义传递的事件名="当前组件 setup return 的函数" -->
<User @helloBtn="hello" name="cyk" :age="age" />
</template>
<script>
import User from "./views/User.vue"; //引入自定义的组件 User
export default {
name: "App",
components: { User }, //声明
setup() {
let age = 18;
function hello() {
alert("hello");
}
return {
age,
hello,
};
},
};
</script>
子组件中通过 emits 接收 helloBtn 事件
<template>
<h1>我是一个学生</h1>
<div>name: {{ user.name }}</div>
<div>age: {{ user.age }}</div>
<button @click="test">父组件传递的事件</button>
</template>
<script>
export default {
name: "User",
props: ["name", "age"],
//拿到父组件传递的事件,他会交给 setup 中的 context 参数
emits: ["helloBtn"],
setup(props, context) {
let user = {
name: props.name,
age: props.age,
};
function test() {
//拿到父组件的事件
context.emit("helloBtn");
}
return {
user,
test,
};
},
};
</script>
1.3.6、computed 计算属性
与 vue2.x 中的 computed 配置功能一样,凡是需要实时计算的地方就使用它即可
a)简写:没有考虑计算属性被修改的情况
<template>
<h1>求和</h1>
<div>
<input type="number" v-model="test.a" />
</div>
<div>
<input type="number" v-model="test.b" />
</div>
<div>结果: {{ result }}</div>
</template>
<script>
import { computed, reactive } from "vue";
export default {
name: "App",
setup() {
let test = reactive({
a: 0,
b: 0,
});
let result = computed(() => {
return test.a + test.b;
});
return {
test,
result,
};
},
};
</script>
b)完整写法:重写 get set 方法(考虑读和写)
假设需求是:读取时要 a 和 b 的和,修改时要求得到 result 的均分到 a 和 b
<template>
<h1>求和</h1>
<div>
<input type="number" v-model="test.a" />
</div>
<div>
<input type="number" v-model="test.b" />
</div>
<div>
<span>结果: </span>
<input type="number" v-model="result" />
</div>
</template>
<script>
import { computed, reactive } from "vue";
export default {
name: "App",
setup() {
let test = reactive({
a: 0,
b: 0,
});
//简写:没有考虑计算属性被修改的情况
// let result = computed(() => {
// return test.a + test.b;
// });
//完整写法:重写 get set 方法
let result = computed({
get() {
return test.a + test.b;
},
set(value) {
test.a = value / 2;
test.b = value / 2;
},
});
return {
test,
result,
};
},
};
</script>
当 a = 5、b = 9 ,读取如下:
当 result = 15,a 和 b 被修改的情况如下:
Ps:下一篇 “ Vue3 - 从入门到进阶,这一套就够了(案例 + 效果演示)(二)”