目录
一、Vue3 从入门到进阶
1.1、Vue3 相比于 Vue2 好在哪里?
1.2、创建 vue-cli3 脚手架
1.3、Vue3 的使用
1.3.1、拉开序幕的 setup
1.3.2、ref 函数
什么是响应式数据
ref 响应式处理
1.3.3、reactive
1.3.4、reactive 对比 ref
1.3.5、setup 的参数(props、context)
1.3.6、computed 计算属性
一、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 - 从入门到进阶,这一套就够了(案例 + 效果演示)(二)”