VueCli创建Vue3项目
1.检查vuecli版本 4以上
vue -V
2.创建
vue create 项目名
vite创建vue3项目
1.创建工程
npm init vite-app 项目名
2.进入项目工程目录
cd 项目名
3.安装依赖
npm install
4.运行
npm run dev
setup
1.理解:Vue3.0中一个新的配置项 值为一个函数
2.setup是所有Composition API(组合式API)表演的舞台
3.组件中所用到的数据、方法等均要配置在setup中
4.setup的两种返回值
1.返回一个对象 则该对象中的属性、方法、在模板中均可以直接使用
2.返回一个渲染函数则可以自定义渲染内容
5.注意点
1.尽量不要与Vue2 写法混用
2.setup不能是一个async函数 因为返回值不再是return的对象模板看不到
return对象中的属性
注意后期也可以返回一个Promise实例但需要Sespense和异步动态引入组件的配合
<template>
<!-- vue3中的模板结构可以没有根标签 -->
<h2>展示个人信息</h2>
<h2>姓名: {{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="sayHello">Hello</button>
</template>
<script>
import {h} from 'vue'
export default {
name: 'App',
setup(){
let name = '张三'
let age = 18
function sayHello(){
alert(`我叫${name},今年${age}岁了`)
}
return{
name,
age,
sayHello
}
}
}
</script>
注意:
setup执行的时机:
在beforeCreate之前执行一次 this是undifind
setup的参数
props: 值为对象 包含组件外部传递过来的 且组件内部声明接收了的属性
context: 上下文对象
attrs:值为对象包含组件外部传递过来的且组件内部声明接收了的属性相当于this.$attrs
slots:收到的插槽内容 相当于this.$slots
emit: 分发的自定义事件的函数 相当于 this.$emit
代码实例
Demo组件
<template>
<!-- vue3中的模板结构可以没有根标签 -->
<h2>展示个人信息</h2>
<h2>姓名: {{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<button @click="test">测试触发一下Demo组件的hello事件</button>
<slot></slot>
<slot name="qwe"></slot>
</template>
<script>
import {reactive} from 'vue'
export default {
name: 'Demo',
beforeCreate() {
},
props:['msg','school'],
emits:['hello'],
setup(props,context){
console.log('setup',props,context)
console.log('setup',props,context.attrs)
console.log('setup',props,context.emit)
console.log('setup',props,context.slots)
let person = reactive({
name:'罗浩哲',
age: 18,
})
function test (){
context.emit('hello',666)
}
return{
person,
test
}
}
}
</script>
App组件
<template>
<Demo @hello="showHello" msg="你好啊" school="北大">
<span>北京大学</span>
<template v-slot:qwe>
<span>
清华大学
</span>
</template>
</Demo>
</template>
<script>
import Demo from './components/Demo.vue'
export default {
name: 'App',
components:{
Demo
},
setup() {
function showHello (value){
alert(`你触发了hello事件我收到的参数是${value}`)
}
return {
showHello
}
},
}
</script>
ref函数
1.作用:定义响应式的数据
2.语法
let name = ref('张三')
let age = ref(18)
let job = ref({
type: '前端工程师',
salary: '30k'
})
3js中操作数据
name.value = '李四'
age.value = 90
job.value.type = 'UI设计师',
job.value.salary ='60k'
}
4.读取使用
<h2>展示个人信息</h2>
<h2>姓名: {{name}}</h2>
<h2>年龄:{{age}}</h2>
<hr>
<h2>{{job.type}}</h2>
<h2>{{job.salary}}</h2>
5.备注
接收的数据可以是基本类型 也可以式对象类型
基本类型的数据 响应式依然是靠Object.defineProperty()的get()set()实现的
对象类型的数据内部求助了 Vue3.0中的一个新函数reactive函数
reactive函数
1.作用:定义一个对象类型的响应式数据(基本类型不要用他使用ref)
2.语法
let person = reactive({
name:'罗浩哲',
age: 18,
job:{
type:'前端工程师',
salary:'30k',
a:{
b:{
c:{
d:666
}
}
}
},
hobby:['抽烟','喝酒','烫头','打游戏','睡觉']
})
function changeInfo(){
person.name = '李四'
person.age = 90
person.job.type = 'UI设计师',
person.job.salary ='60k',
person.job.a.b.c.d = 999
}
return{
person,
changeInfo
}
}
}
3.reactive定义的数据响应是深层次的
4.内部基于ES6的Proxy实现 通过代理对象操作源对象内部数据
Vue2.的响应式
1.实现原理
对象类型:通过ObjectdefineProperty()对属性的读取和修改进行拦截(数据劫持)
数组类型:通过重写数组一系列方法实现拦截(对数组变更方法进行了包裹)
存在问题
新增属性 删除属性 界面不会更新
直接通过下表修改数组界面不会更新
Vue3响应式
实现原理
通过proxy代理:拦截对象中任意属性的变化 包括属性值的读写、属性的添加 删除等
通过refiect(反射)对被代理对象属性进行操作
<script>
let person = {
name:'张三',
age: 18
}
const p = new Proxy(person,{
get(target,propName){
console.log(`有人读取p身上的${propName}我要去更新页面了`);
Reflect.get(target,propName)
},
set(target,propName,value){
console.log(`有人修改了p身上的${propName},我要去更新页面了`);
Reflect.set(target,propName,value)
},
deleteProperty(target,propName){
console.log(`有人删除了p身上的${propName}属性,我要去更新页面了`);
return Reflect.deleteProperty(target,propName)
}
})
</script>
计算属性
computed函数
与Vue2中配置功能一致
写法: 全名案例
<template>
<!-- vue3中的模板结构可以没有根标签 -->
<h2>展示全名信息</h2>
姓: <input type="text" v-model="person.Fname">
<br><br>
名: <input type="text" v-model="person.Lname">
<br><br>
<!-- <span>全名:{{person.fullName}}</span> -->
全名: <input type="text" v-model="person.fullName">
</template>
<script>
import {reactive, computed} from 'vue'
export default {
name: 'Demo',
setup(){
// 数据
let person = reactive({
Fname:'罗',
Lname: '浩哲',
})
//计算属性(简写 没有考虑计算属性被修改的情况)
// person.fullName = computed (() =>{
// return person.Fname + ' ' + person.Lname
// })
//计算属性(完整写法 考虑了计算属性被修改的情况)
person.fullName = computed ({
get(){
// 读取时调用
return person.Fname + ' ' + person.Lname
},
set(value){
//修改时调用
//按照 空格 拆分数组
const nameArr = value.split(' ')
person.Fname = nameArr[0],
person.Lname = nameArr[1]
}
})
//返回一个对象(常用)
return{
person,
}
}
}
</script>
watch函数监视
1.与Vue2中watch 配置功能一致
watch:{
sum:{
immediate:true,
deep: true,
handler(newValue,oldValue){
console.log('sum的值改变了',oldValue,newValue);
}
}
},
两个小坑
监视reactive定义的响应式数据时 oldValue无法正确获取 强制开启深度监视deep配置无效
监视reactive定义的响应式数据中的某个属性 deep配置无效
<script>
import {ref,reactive,watch} from 'vue'
export default {
name: 'Demo',
setup(){
let sum = ref(0)
let msg = ref('你好')
let person = reactive({
name: '罗浩哲',
age:19,
job:{
a:{
b:{
d:20000
}
}
}
})
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg的值变化了',oldValue,newValue);
},{immediate:true,deep:true})
watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{
console.log('age或name的值变化了',oldValue,newValue);
},{deep:false})
watch(()=>person.job,(newValue,oldValue)=>{
console.log('job的值变化了',oldValue,newValue);
},{deep:true})
return{
sum,
msg,
person
}
}
}
</script>
watch监视ref数据需不需要.value的问题
setup(){
let sum = ref(0)
let msg = ref('你好')
let person = ref({
name: '罗浩哲',
age:19,
job:{
a:{
b:{
d:20000
}
}
}
})
watch(sum,(newValue,oldValue)=>{
console.log('sum的值变化了',newValue,oldValue);
})
watch(person.value,(newValue,oldValue) =>{
console.log('person的值变化了',newValue,oldValue);
},{deep:true})
watchEffect 监视函数体内使用到的属性
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了');
})
Vue3 生命周期钩子

import {ref, onBeforeMount, onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
export default {
name: 'Demo',
//通过配置项的形式使用生命周期钩子
//#region
// beforeCreate() {
// console.log('beforeCreate');
// },
// created(){
// console.log('created');
// },
// beforeMount(){
// console.log('beforeMount');
// },
// mounted() {
// console.log('mounted');
// },
// beforeUpdate(){
// console.log('beforeUpdate');
// },
// updated(){
// console.log('updated');
// },
// beforeUnmount(){
// console.log('beforeUnmount');
// },
// unmounted(){
// console.log('unmounted');
// },
//#endregion
setup(){
console.log('setup');
// 数据
let sum = ref(0)
//通过组合式API的形式使用生命周期钩子
onBeforeMount(()=>{
console.log('onBeforeMount');
})
onMounted(()=>{
console.log('onMounted');
})
onBeforeUpdate(()=>{
console.log('onBeforeUpdate');
})
onUpdated(()=>{
console.log('onUpdated');
})
onBeforeUnmount(()=>{
console.log('onBeforeUnmount');
})
onUnmounted(()=>{
console.log('onUnmounted');
})
//返回一个对象(常用)
return{
sum,
}
}
}
</script>
自定义hook函数
hook————本质是一个函数 把setup函数中使用的组合式API进行了封装
类似于 vue2 中的minix
自定义hook的优势,复用代码 让setup中的逻辑更加清晰易懂
鼠标点击案列
新建hook文件夹下新建usePoint.js配置代码
import {reactive,onMounted, onBeforeUnmount} from 'vue'
export default function (){
let point = reactive({
x:0,
y:0
})
function savePoint(event){
point.x = event.pageX
point.y = event.pageY
}
onMounted(() =>{
window.addEventListener('click',savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener('click',savePoint)
})
return point
}
在Demo组件内引入使用
import usePoint from '../hocks/usePoint'
import {reactive, ref,onMounted, onUnmounted, onBeforeUnmount} from 'vue'
export default {
name: 'Demo',
setup(){
let sum = ref(0)
let point = usePoint()
return{
sum,
point
}
}
}
</script>
## toRef toRefs
作用:创建一个ref对象 其value值指向另一个对象的某个属性
语法: const name = toRef(person,'name')
应用:要将响应式对象中的某个属性单独提供给外部使用
扩展:toRef和toRefs功能一致 但可以批量创建多个ref对象 语法toRefs(person)
<template>
<h1>{{person}}</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<!-- <h2>薪资:{{salary}}K</h2> -->
<h2>薪资:{{job.j1.salary}}K</h2>
<button @click="name+= '!'">改名</button>
<button @click="age++">增龄</button>
<!-- <button @click="salary++">加薪</button> -->
<button @click="job.j1.salary++">加薪</button>
</template>
<script>
import {reactive, ref,toRef,toRefs} from 'vue'
export default {
name: 'Demo',
setup(){
let person = reactive({
name:'张三',
age: 18,
job:{
j1:{
salary:20
}
}
})
return{
person,
...toRefs(person)
}
}
}
</script>
## shallowReactive,shallowRef
shallowReactive:只处理对象最外层属性的响应式(浅响应式)
shallowRef:只能处理基本类型的响应式不进行对象类型的响应式处理
什么时候使用
如果有一个对象数据 结构比较浅 但变化时只是最外层属性变化 ==> shallowReactive
如果有一个对象数据 后续功能不会修改该对象的属性而是生成新的对象来替换 ==> shallowReacti
## readonly shallowReadonly
readonly:深层只读
shallowReadonly 浅层只读
应用场景
不希望数据被修改时
let person = reactive({
name:'张三',
age: 18,
job:{
j1:{
salary:20
}
}
})
person = readonly(person)
person = shallowReadonly(person)
## toRaw markRaw
toRaw:
作用:将一个由reactive生成的响应式对象转为普通对象
使用场景:用于读取响应式数据对应的普通对象 对这个普通对象的所有操作不会引起页面更新
markRaw
作用:标记一个对象 使其永远不会再成为响应式对象
应用场景
1,有些值不应被设置成响应式的 例如复杂的第三方库
2.当渲染具有不可变的数据源的大列表时跳过响应式转换可提高性能
function showRaw (){
const p = toRaw(person)
console.log(p);
}
customRef(自定义ref)
实现防抖案列
<template>
<input type="text" v-model="Keyword">
<h4>{{Keyword}}</h4>
</template>
<script>
import {ref,reactive,customRef} from 'vue'
export default {
name: 'App',
setup(){
function MyRef(value,delay){
let timer
return customRef((track,trigger)=>{
return {
get(){
console.log('有人从MyRef读取数据了',value);
track()
return value
},
set(newValue){
console.log('有人修改MyRef数据了',newValue);
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger()
},delay)
}
}
})
}
let Keyword = MyRef('hello',500)
return {
Keyword
}
}
}
</script>
## provide与inject
```javascript
作用:实现祖孙组件间通信
套路:父组件中有一个provide选项来提供数据 后代组件有一个inject选项来使用这些数据
具体用法
祖组件中
setup(){
let car = reactive({
name:'奥迪',
price: 100
})
provide('car',car)
return{
car
}
},
后代组件中
setup(){
const car = inject('car')
return{
car
}
}
响应式数据的判断
isRef: 检查一个值是否是一个人Ref对象
isReactive: 检查一个对象是否由reactive创建的响应式代理
isReadonly 检查一个对象是否是由readonly创建的只读代理
isProxy 检查一个对象是否是由reactive或者readonly方法创建的代理
Compositio API 的优势
我们可以更加优雅的组织我们的代码,函数 让相关的代码更加有序的组织在一起
Fragment组件
Vue3中可以没有根标签其实是内部将多个标签包含在一个Fragment虚拟元素中
Teleport组件
Teleport 是一一种可以将我们的组件html结构移动到指定位置的技术
<template>
<button @click="isShow = true">弹出对话框</button>
<teleport to="body"> //移动到body
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个对话框</h3>
<ul>
<li>324234532</li>
<li>324234532</li>
<li>324234532</li>
<li>324234532</li>
</ul>
<button @click="isShow = false">关闭对话框框</button>
</div>
</div>
</teleport>
## Suspense
等待异步组件时渲染一些额外内容 让应用有更好的用户体验
使用步骤
异步引入组件
import { defineAsyncComponent } from "vue";
const Child = defineAsyncComponent(() => import("./components/Child.vue"));
使用Suspense包裹组件 并配置好default 与fallback
<Suspense>
<template v-slot:fallback>
<h2>加载中。。。</h2>
</template>
<template v-slot:default>
<Child />
</template>
</Suspense>