Vue3学习笔记
写在前面
准备开始系统地学习Vue3了,我觉得vue3+ts+vite会是一个不错的技术栈选择。
今天草草看了以下vue3,比如数组深度监听,setup的写法,都是不错的改进。
今天先带来一点vue3的创建和vite-app的使用+一些学习笔记
创建的两种方法
- 使用vue-cli创建
# 安装或者升级
npm install -g @vue/cli
# 保证vue cli版本再4.5.0以上
vue --version
# 创建项目
vue create my-project
这种办法可以选择加入ts,选择人工选择就好,在要选择选项上按空格
可以选择
- 使用vite创建
本地快速开发启动,在生产环境下基于Rollup打包- 快速的冷启动,不需要等待打包操作
- 即时的热模块更新,替换性能和模块数量的解耦让更新更快
- 真正的按需编译,不再等待整个应用编译完成,这是一个巨大的改变
npm init vite-app project-name
cd project-name
npm install
npm run dev
一些笔记
- vue3组件中的html模板中可以没有根标签
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</template>
- script中可以使用ts的代码
<script lang="ts">
- defineComponent函数,目的是定义一个组件,内部可以传入一个配置对象
import { defineComponent } from 'vue';
setup
- 新的option,所有组合API函数都在此使用,只在初始化时执行一次
- 函数如果返回对象,对象中的属性或方法,模板中可以直接使用
真的是很giao的一个更新呢!!!
之前vue2要分开写data,methods,这就是项目大了,读起来,有跨度,不方便。
现在可以全部写在setup里面了,很爽呢,康康下面这个基础案例
<!--
* @Author: 41
* @Date: 2021-12-07 17:12:43
* @LastEditors: 41
* @LastEditTime: 2021-12-10 10:27:32
* @Description:
-->
<template>
<div>123456</div>
<h1>{{number}}</h1>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App',
setup(){
console.log('我来了');
const number=10
return {
number
}
}
});
</script>
vue2和vue3的写法对比
我们实现一个需求,就是页面显示一个数据,我们按按钮,数据更新
- vue2写法如下:
<!--
* @Author: 41
* @Date: 2021-12-07 17:12:43
* @LastEditors: 41
* @LastEditTime: 2021-12-10 10:33:43
* @Description:
-->
<template>
<h1>{{count}}</h1>
<button @click="change">count++</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App',
data(){
return{
count:0
}
},
methods:{
change(){
this.count++
}
}
});
</script>
<style scoped>
button{
width: 100px;
height: 50px;
}
</style>
- vue3写法
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
name: 'App',
setup(){
// let count=0 // 并不是响应式的数据
const count=ref(0) // ref 是一个函数,作用:定义一个响应式数据
function change(){
count.value++
}
return {
count,
change
}
}
});
</script>
ref定义的是一个数据的响应式,多个数据呢?—reactive
reactive:
- 作用:定义多个数据的响应式
- const proxy=reactive(obj):接收一个普通对象然后返回该普通对象的响应式代理器对象(代理和vue2中的defineProperty不同,可以监听到新添加的数据了!!!),我觉得是特别优秀的一个改进!
- 响应式转换时"深层的":会影响对象内部所有嵌套的属性
- 内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据都是响应式的
proxy的代理,我的个人理解是,自动帮你this.$set,哈哈,不知道对不对!
我们来看一个案例:显示用户的相关信息,点击按钮,可以更新用户的相关信息数据
<!--
* @Author: 41
* @Date: 2021-12-07 17:12:43
* @LastEditors: 41
* @LastEditTime: 2021-12-10 12:07:27
* @Description:
-->
<template>
<h1>reactive的使用</h1>
<h3>名字:{{user.name}}</h3>
<h3>年龄:{{user.age}}</h3>
<h3>wife:{{user.wife}}</h3>
<hr>
<button @click="change">更新数据</button>
</template>
<script lang="ts">
import { defineComponent,reactive } from 'vue';
export default defineComponent({
name: 'App',
setup(){
const user=reactive({
name:'41',
age:24,
wife:{
name:'42',
age:18,
cars:['43','44']
}
})
const change= ()=>{
user.name+='=='
user.age+=2
user.wife.name+='++'
}
return {
user,
change
}
}
});
</script>
<style scoped>
button{
width: 100px;
height: 50px;
}
</style>
这样虽然好像比vue2复杂了一点,但是不得不说,代码的可读性上来了,越来越像react了,但是vue比react快啊!!!
比较vue2和vue3的响应式(重要)
- vue2的响应式
核心:- 对象:通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截)
- 数组:通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
Object.defineProperty(data,'count',{
get(){},
set(){}
})
问题:
- 对象直接新添加的属性或删除已有属性,界面不会自动更新
- 直接通过下标替换元素或更新length,界面不会自动更新
- vue3的响应式
核心:- 通过Proxy(代理):拦截对data任意属性和任意操作,包括属性值的读写,属性的添加,属性的删除等…
- 通过Reflect(反射):动态对被代理对象的响应属性进行特定的操作
new Proxy(data,{
// 拦截读取属性值
get(target,prop){
return Reflect.get(target,prop)
},
// 拦截设置属性值或添加新属性
set(target,prop,value){
return Reflect.set(target,prop,value)
},
// 拦截删除属性
deleteProperty(target,prop){
return Reflect.deleteProperty(target,prop)
}
})
setup细节
-
setup在beforeCreate生命周期回调之前就执行了,而且就执行一次
由此可以推断出:setup在执行的时候,当前的组件还没有创建出来,也就意味着:组件实例对象this根本就不能用 -
setup的返回值是一个对象,内部的属性和方法是给html模板使用的
-
setup中数据和data函数中
属性会合并,方法也是如此
Vue3中尽量不要混合使用setup和data,methods
- setup不能访问data和methods
- setup的参数
- props:包含props配置声明且传入了的所有属性的对象
- attrs:包含没有在props配置中声明的属性的对象,相当于this.$attrs
- slots:包含所有传入的插槽内容的对象,相当于this.$slots
- emit:用来分发自定义事件的函数,相当于this.$emit
为了方便理解,我们看看下面父子组件的通信代码!!!
- App.vue
<!--
* @Author: 41
* @Date: 2021-12-07 17:12:43
* @LastEditors: 41
* @LastEditTime: 2021-12-10 16:27:24
* @Description:
-->
<template>
<h2>App父级组件</h2>
<h3>{{msg}}</h3>
<button @click="msg+='==='">更新数据</button>
<hr>
<Child :msg="msg" @xxx="xxx"></Child>
</template>
<script lang="ts">
import {defineComponent,ref} from 'vue'
import Child from './components/Child.vue'
export default defineComponent({
name:'App',
components:{
Child
},
setup(){
const msg=ref('how are you!')
function xxx(txt:string){
msg.value+=txt
}
return {
msg,
xxx
}
}
})
</script>
<style scoped>
button{
width: 100px;
height: 50px;
}
</style>
- Child.vue
<!--
* @Author: 41
* @Date: 2021-12-10 15:32:24
* @LastEditors: 41
* @LastEditTime: 2021-12-10 16:30:09
* @Description:
-->
<template>
<h2>子级组件</h2>
<h3>{{msg}}</h3>
<button @click="emitxxx">分发事件</button>
</template>
<script lang="ts">
import {defineComponent} from 'vue'
export default defineComponent({
name:'Child',
props:['msg'],
setup(props,{attrs,emit,slots}){
// props参数,是一个对象,里面有父级组件向子级组件传递的数据
console.log(props);
function emitxxx(){
emit('xxx','++')
}
return {
emitxxx
}
}
})
</script>
reactive与ref细节
- ref也可以传对象,内部会自动将对象/数组转换为reactive的代理对象