Vue3
Vue3优点
1.首次渲染更快
2.diff
算法更快
- 因为vue2中的虚拟dom是进行全量的对比,而vue3中新增了静态标记,只比对带有PF的节点,并且通过Flag的信息得知当前节点要比对的具体内容
3.内存渲染更好
4.打包的体积更小
5.更好的TS支持
6.template
中不用在嵌套一层根标签
- 因为内部会将多个标签包含在一个
Fragment
的虚拟元素中 ,且该元素不会再dom树中呈现
template>
<div>芜湖</div>
<div>起飞</div>
</template>
- 1
- 2
- 3
- 4
7.组合式API,vue2则是选项是API ; 组合式api可以提高组件复用性 更好维护
Vue3中的main文件挂在组件
import { createApp } from 'vue'
import App from './'
// 根据App组件创建一个应用实例
const app = createApp(App)
// app应用挂载(管理)的 #app 容器
app.mount('#app')
- 1
- 2
- 3
- 4
- 5
- 6
注意:vue3中的是使用的createApp()
管理容器,不是new Vue()
setup
函数
setup
函数是vue3中特有函数,作为api中的起点 ; 从组件生命周期看,它在beforeCreate
之前执行
函数中的this
不是组件实例,是undefined
; 如果数据或者函数在模板中使用,需要setup
返回
<template>
<div class="container">
<h1 @click="say()">{{msg}}</h1>
</div>
</template>
<script>
export default {
setup () {
return{}
}
}
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
注意:在vue3中的setup
中用不到this
,所有东西通过函数获取
setup
语法糖
为了让代码更加简洁,可以用语法糖 ; 这样省略了导出配置项,setup
函数声明和需要return
数据
<template>
<button @click="toggle">显示隐藏图片</button>
<img v-show="show" alt="Vue logo" src="./assets/" />
<hr />
计数器:{{ count }} <button @click="increment">累加</button>
</template>
<script setup>
// 显示隐藏
const show = ref(true)
const toggle = () => {
= !
}
// 计数器
const count = ref(0)
const increment = () => {
++
}
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
注意:script setup
中的变量也可以直接在模板中使用
reactive
函数
使用reactive
定义对象数组类型的响应式数据
使用:
- 从
vue
中导出reactive
函数 - 在
setup
函数中,使用reactive
函数,传入一个普通对象,返回一个响应式数据对象 - 最后在
setup
函数中返回一个对象,包含该响应式对象就可以在模板中使用
<template>
<div>
<p>姓名:{{state.name}}</p>
<p>年龄:{{state.age}} <button @click="++">一年又一年</button></p>
</div>
</template>
<script>
// 1. 导入函数
import { reactive } from "vue";
export default {
setup() {
// 2. 创建响应式数据对象
const state = reactive({ name: 'tom', age: 18 })
// 3. 返回数据
return { state }
}
};
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
reactive
函数通常定义复杂类型响应式数据,不可以转换简单数据
ref
函数
可以用来定义响应式数据,不限制类型
使用步骤
- 从
vue
中导出ref
函数 - 在
setup
函数中,使用ref
函数,传入普通数据(简单or复杂),返回一个响应式数据 - 最后
setup
函数返回一个对象,包含该响应式数据即可 - 注意:使用
ref
创建的数据,js
中需要.value
,template
中可省略
<template>
<div>
<p>
计数器:{{ count }}
<button @click="count++">累加1</button>
<!-- template中使用可省略.value -->
<button @click="increment">累加10</button>
</p>
</div>
</template>
<script>
// 1. 导入函数
import { ref } from "vue";
export default {
setup() {
// 2. 创建响应式数据对象
const count = ref(0);
const increment = () => {
// js中使用需要.value
+= 10;
};
// 3. 返回数据
return { count, increment };
},
};
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
reactive
与ref
的选择
reactive
可以转换对象成为响应式数据对象,但是不支持简单数据类型
ref
可以将简单数据类型转为响应式数据类型,也支持复杂数据类型,但是操作的时候需要.value
推荐:如果能确定数据类型是对象,而且字段名也确定可以使用reactive
转换成响应式数据,其他一概使用ref
// 1. 明确表单对象有两个字段
const form = reactive({
username: '',
password: ''
})
// 2. 后台返回的数据对象
const data = ref(null)
const res = await axios.get('/user/100')
data.value = res.data
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
computed
函数
大致步骤
- 从
vue
中导出computed
函数 - 在
setup
函数中,使用computed
函数,传入一个函数,函数返回计算好的数据 - 最后
setup
函数返回一个对象,包含该对象属性即可,然后在模板内部直接使用
<template>
<div>
<p>分数:{{ scoreList }}</p>
<p>优秀:{{ betterList }}</p>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
const scoreList = ref([80, 100, 90, 70, 60]);
// 计算属性
const betterList = computed(() => ((item) => item >= 90));
// 改变数据,计算属性改变
setTimeout(() => {
(92, 66);
}, 3000);
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
watch
函数
使用watch
监听一个响应式数据 : watch
(数据, 改变后回调函数)
<template>
<p>计数器:{{ count }}</p>
</template>
<script setup>
import { ref, watch } from "vue";
const count = ref(0);
// 1. 监听一个响应式数据
// watch(数据, 改变后回调函数)
watch(count, () => {
("count改变了");
});
// 2s改变数据
setTimeout(() => {
++;
}, 2000);
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
使用watch
监听多组响应式数据 : watch
([数据1, 数据2, …], 改变后回调函数)
<template>
<p>计数器:{{ count }}</p>
</template>
<script setup>
import { ref, watch } from "vue";
const count = ref(0);
const count1 = ref(1);
// 1. 监听多个响应式数据
// watch([数据1, 数据2, ...], 改变后回调函数)
watch([count,count1], () => {
("count改变了");
});
// 2s改变数据
setTimeout(() => {
++;
}, 2000);
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
使用watch
监听响应式对象数据源中的一个简单属性 : watch
(()=>数据, 改变后回调函数)
<template>
<p>
姓名:{{ }} 性别:{{ }} 年龄:{{ }}
</p>
</template>
<script setup>
import { reactive, watch } from "vue";
const user = reactive({
name: "tom",
info: {
gender: "男",
age: 18,
},
});
// 3. 监听响应式对象数据的一个数据,简单类型
// watch(()=>数据, 改变后回调函数)
watch(()=>, () => {
("数据改变了");
});
// 2s改变数据
setTimeout(() => {
= 'jack';
}, 2000);
// 4s改变数据
setTimeout(() => {
= 60;
}, 4000);
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 使用
watch
监听响应式对象数据中的一个复杂属性,配置深度监听 : watch(()=>数据, 改变后回调函数, {deep: true})
<template>
<p>
姓名:{{ }} 性别:{{ }} 年龄:{{ }}
</p>
</template>
<script setup>
import { reactive, watch } from "vue";
const user = reactive({
name: "tom",
info: {
gender: "男",
age: 18,
},
});
// 4. 监听响应式对象数据的一个数据,复杂类型
// watch(()=>数据, 改变后回调函数, {deep: true})
watch(
() => ,() => {
("数据改变了");
},
{
// 开启深度监听
deep: true,
// 默认执行一次
immediate: true
}
);
// 2s改变数据
setTimeout(() => {
= 60;
}, 2000);
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
注意: watch( 需要监听的数据 , 数据执行改变的函数体 , 配置对象 )
来进行监听 ; 属性复杂需要开启deep
生命周期函数
vue3中的生命周期钩子可以调用多次 且 均以on开头
- Vue3和vue2的生命周期对比
选项式API下的生命周期函数使用 | 组合式API下的生命周期函数使用 |
---|---|
beforeCreate | 不需要(直接写到setup函数中) |
created | 不需要(直接写到setup函数中) |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroyed | onBeforeUnmount |
destroyed | onUnmounted |
activated | onActivated |
deactivated | onDeactivated |
使用:
- 先从
vue
中导入生命周期钩子 - 在
setup
函数轴调用生命周期钩子并且传入回到函数
<script setup>
import { onMounted } from "vue";
// 生命周期函数:组件渲染完毕
onMounted(()=>{
('onMounted触发了')
})
onMounted(()=>{
('onMounted也触发了')
})
</script>
<template>
<div>生命周期函数</div>
</template>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
注意:一般常用的是onMounted
,即组件渲染完毕后调用,可以用来:发请求,操作dom
等
ref
获取dom
属性
元素上使用ref
属性关联响应式数据,获取DOM
元素
步骤:
- 创建
ref
- 在模板中建立关联
- 使用
<script setup>
import { ref } from 'vue'
const hRef = ref(null)
const clickFn = () => {
= '我不是标题'
}
</script>
<template>
<div>
<h1 ref="hRef">我是标题</h1>
<button @click="clickFn">操作DOM</button>
</div>
</template>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
注意:如果想直接访问值,需要在onMounted
钩子里访问,如果直接在js代码中访问是访问不到的,因为当时dom
还未挂载实例对象
ref
操作组件实例 — defineExpose
如果使用<script setup>
语法糖,组件是默认关闭的,组件实例对象使用不到顶层的数据和函数
需要配合defineExpose
来暴露给组件实例对象使用,但是暴露的响应式数据会自动解除响应式
<template>
<h3>我是son组件</h3>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const validate = () => {
('表单校验方法')
}
// 暴露属性给外部组件使用
defineExpose({count, validate})
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
<template>
<Form ref="formRef"></Form>
</template>
<script setup>
import { ref } from 'vue'
import Form from './components/'
// 1. 提供一个ref
const formRef = ref(null)
// 2. 使用组件组件和方法
const fn = () => {
()
()
}
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
注意:配合 defineExpose
暴露数据和方法,ref
获取的组件实例才可以使用
父传子 - defineProps
函数
步骤
- 父组件提供数据
- 父组件将数据传递给子组件
- 子组件通过
defineProps
进行接受 - 子组件渲染父组件传递的数据
<template>
<div>
<h1>我是父组件</h1>
<ChildCom :money="money" :car="car"></ChildCom>
</div>
</template>
<script setup>
import { ref } from 'vue'
import ChildCom from './components/'
const money = ref(100)
const car = ref('玛莎拉蒂')
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
<template>
<div>
<h3>我是子组件</h3>
<div>{{ money }} --- {{ car }}</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
// defineProps: 接收父组件传递的数据
const props = defineProps({
money: Number,
car: String,
})
// 使用props
()
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
注意:如果只用defineProps
来接受数据的话,此数据只能在模板中使用 ; 想要子js中也操作props
属性应该接受返回值
子传父 - defineEmits函数
步骤
- 子组件通过
defineEmits
来获取emit
函数 - 子组件通过
emit
触发事件,并且传递数据 - 父组件提供方法
- 父组件通过自定义事件的方式给子组件注册事件
<template>
<div>我是子组件</div>
</template>
<script setup>
// 得到emit函数,显性声明事件名称
const emit = defineEmits(['changeMoney'])
const change = () => {
emit('changeMoney', 10)
}
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
<template>
<div>我是子组件</div>
<ChildCom @changeMoney="changeMoney"></ChildCom>
</template>
<script setup>
import { ref } from 'vue'
import ChildCom from './components/'
const changeMoney = (num) => {
(num)
}
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
跨级组件通讯provide
与inject
函数
通过provide
和inject
函数可以简便的实现跨组件通讯
步骤
-
provide
提供后代组件需要依赖的数据或函数 -
inject
获取provide
提供的数据或者函数
<template>
<div >爷爷组件 {{ count }}</div>
</template>
<script setup>
import { provide, ref } from 'vue';
import ParentCom from './';
// 1. app组件数据传递给child
const count = ref(0);
provide('count', count);
// 2. app组件函数传递给child,调用的时候可以回传数据
const updateCount = (num) => {
+= num;
};
provide('updateCount', updateCount);
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
<template>
<div>
孙子 组件 {{ count }}
<button @click="updateCount(100)">修改count</button>
</div>
</template>
<script setup>
const count = inject('count');
const updateCount = inject('updateCount');
</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
注意:数据是谁提供谁负责修改,获取方不能修改提供方的数据
保持响应式 - toRefs
函数
如果使用reactive
创建的响应式数据被展开或者解构就会失去响应式,想保持需要使用toRefs
函数
toRefs
会把对象中每一个属性做一次包装成为响应式数据
如果使用解构或者展开响应式数据对象的时候使用toRefs
数据来保持响应式
import { reactive, toRefs } from "vue";
const user = reactive({ name: "tom", age: 18 });
const { name, age } = toRefs(user)
- 1
- 2
- 3
Suspense
等待组件
Suspense用于在等待异步组件渲染时添加一些额外的内容,提高用户体验
// 异步引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/'))
// 使用`Suspense`包裹组件,并配置好`default` 与 `fallback`
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
Vue2和Vue3中的响应式实现原理区别
Vue2中使用(
)进行数据劫持,来操作读取和修改
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
- 1
- 2
- 3
- 4
但是存在着新增属性和删除属性和数组直接修改下标界面不会更新
vue3中是使用ES6的新语法Proxy()
和Reflect()
来实现的
proxy(代理):拦截对象中的任意属性的变化,包括属性值的读写、添加、删除等操作
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)
}
})
proxy.name = 'tom'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15