一文掌握 vue3.2

一文掌握 vue3.2

  • 前言
    • 1、setup语法糖
      • 1.1 defineProps
      • 1.2 defineEmits
      • 1.3 defineExpose
    • 2 、ref、reactive创建响应式数据
      • 3.1 ref
      • 3.2 reactive
    • 3、watchEffect和watch的使用
      • 3.1 watchEffec
      • 3.2 watch
    • 4、computed计算属性的使用
    • 5、自定义hook的使用
    • 5、await的支持
    • 6、provider/inject 祖孙传值
    • 7、路由新玩法useRoute 和useRouter
    • 8、响应式的判断
    • 9、Vue2.X和Vue3.X生命周期对比

前言

在Vue3.2 版本之前 变量函数必须return出来非常繁琐,而在 Vue3.2 中只需要
在 script 标签上加上 setup 属性就无需return出来,非常方便。

1、setup语法糖

使用setup语法糖后,不用写setup函数;组件只需要引入不需要注册;属性和方法也不需要再返回,也不用写export default,可以直接在template模板中使用。
只需在 script 标签上写上setup

代码如下(示例):

<template>
</template>
<script setup>
</script>

setup语法糖提供了三个新的API来供我们使用:definePropsdefineEmitsuseContext

defineProps:子组件接收父组件中传来的props
defineEmits:子组件调用父组件中的方法
defineExpose:子组件暴露属性,可以在父组件中拿到

1.1 defineProps

父组件如下:

<template>
  <child :name='name'/>  
</template>

<script setup>
    import {ref} from 'vue'
    import child from './child.vue'    // 引入子组件
    let name= ref('父组件')
</script>

子组件如下:

<template>
  <span>{{props.name}}</span>
</template>

<script setup>
  import { defineProps } from 'vue'
  const props = defineProps({  // 声明props
    name: {
      type: String,
      default: '子组件'
    }
  })  
  // 或者
  //const props = defineProps(['name'])
</script>

1.2 defineEmits

父组件如下:

<template>
  <child @but="clickBut" />
</template>
<script setup>
import {reactive} from 'vue'
// 导入子组件
  import child from './child.vue'  
const data = reactive({
  flg: false, 
})

const clickBut= () => {
  data.flg= false
}

子组件如下:

<template>
   <a-button @click="but1">
     子组件按钮
   </a-button>
</template>
<script setup>
import { defineEmits } from 'vue';

// emit
const emit = defineEmits(['but'])
/**
 * 方法
 */
// 点击确定按钮
const but1 = () => {
  emit('but');
}
</script>

1.3 defineExpose

父组件如下:

	<template>
		<child ref="mynum"></my-comp>
		<button @click="onButton">获取子组件中暴露的值</button>
	</template>
	<script setup>
		import {ref} from 'vue';
        import child from './child.vue'  		// 导入子组件
		//注册ref,获取组件
		const myComponent = ref();
		function onButton(){
			//在组件的value属性中获取暴露的值
			console.log(mynum.value.num)  //0
		}
	</script>

注意:但在setup中立即使用获取为为undefined

子组件如下:

	<template>
		<div>子组件中的值{{num}}</div>
		<button @click="onButton">数值加1</button>
	</template>
	<script setup>
		import {ref,defineExpose} from 'vue';
		let num= ref(0);
		function onButton(){
			num.value++;	
		}
		//暴露出子组件中的属性
		defineExpose({
			num
		})
	</script>

2 、ref、reactive创建响应式数据

3.1 ref

接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property .value。
代码如下(示例):

	<template>
	</template>
	<script setup>
		import {ref} from 'vue';
		let num= ref(0);
        console.log(num.value) // 0
	</script>

3.2 reactive

reactive 用于为对象添加响应式状态。获取数值的时候不用加.value
代码如下(示例):

	<template>
	</template>
	<script setup>
	import {reactive} from 'vue'

			//数据
			let person = reactive({
				name:'李四',
				age:18,
				job:{
					type:'前端工程师',
					salary:'9000',
					a:{
						b:{
							c:'字符串'
						}
					}
				},
				hobby:['vue','js','css']
			})
                 console.log(person.name,person.job.type); //李四,前端工程师
		   const {name,age} = person
		   console.log(name,age); //李四,18
           
	</script>

注意:直接 const {name,age} = person 结构会失去响应式,需要加toRefs

代码如下(示例):

	<template>
	</template>
	<script setup>
	import {reactive,toRefs} from 'vue'

			//数据
			let person = reactive({
				name:'李四',
				age:18,
				job:{
					type:'前端工程师',
					salary:'9000',
					a:{
						b:{
							c:'字符串'
						}
					}
				},
				hobby:['vue','js','css']
			})
  
		   const { name,age} = toRefs(person)

		   console.log(name.value,age.value);//李四,18
           
	</script>

使用toRefs解构不会失去响应式,但需要加.value

3、watchEffect和watch的使用

3.1 watchEffec

watchEffec它是立即执行的,在页面加载时会主动执行一次,只能访问当前最新的值访问不到修改之前的值,另外在watchEffect 中只有使用到了变量,它才会自动进行追踪

代码如下(示例):

<template>
	<button @click="sum++">点我+1</button>
	<button @click="person.name+='~'">修改姓名</button>

</template>

<script setup>
	import {ref,reactive,watch,watchEffect} from 'vue'
			//数据
			let sum = ref(0)
			let person = reactive({
				name:'张三',
			})
			watchEffect(()=>{
				const value1 = sum.value
				const value2 = person.name
		
				console.log('watchEffect所指定的回调执行了',value1 ,value2 )
			})

		
	
</script>

注意:打印的数值都是改变后最新的值

3.2 watch

watch运行的时候不会立即执行,可以最新值和修改之前的值,可以添加配置项

配置项:
1.immediate:配置watch属性是否立即执行,值为 true 时,一旦运行就会立即执行,值为false时,不会立即执行。
2.watch : 是否深度监听,值为 true 时,可以监听对象所有属性,值为 false 时,则必须指定到具体的属性上。

代码如下(示例):

情况一:监视ref所定义的一个响应式数据

<template>
	<h2>当前求和为:{{sum}}</h2>
	<button @click="sum++">点我+1</button>
</template>

<script setup>
	import {ref,reactive,watch} from 'vue'
			//数据
			let sum = ref(0)
			  watch(sum,(newValue,oldValue)=>{
				console.log('sum变了',newValue,oldValue)
		 },{immediate:true})
</script>


//情况二:监视ref所定义的多个响应式数据

<template>
	<h2>当前求和为:{{sum}}</h2>
	<button @click="sum++">点我+1</button>
	<hr>
	<h2>当前的信息为:{{msg}}</h2>
	<button @click="msg+='!'">修改信息</button>
</template>

<script setup>
	import {ref,reactive,watch} from 'vue'
			//数据
			let sum = ref(0)
			let msg = ref('watch好啊')
			 watch([sum,msg],(newValue,oldValue)=>{
				console.log('sum或msg变了',newValue,oldValue)
			},{immediate:false}) 

	
</script>


情况三:监视reactive所定义的一个响应式数据的全部属性

<template>

	<h2>姓名:{{person.name}}</h2>
	<h2>年龄:{{person.age}}</h2>
	<h2>职位:{{person.job.j1.salary}}K</h2>
	<button @click="person.name+='~'">修改姓名</button>
	<button @click="person.job.j1.salary+='123'">跳槽</button>
</template>

<script setup>
	import {ref,reactive,watch} from 'vue'

			let person = reactive({
				name:'张三',
				job:{
					j1:{
						salary:'前端'
					}
				}
			})
		watch(person,(newValue,oldValue)=>{
				console.log('person变化了',newValue,oldValue)
			},{deep:false}) //此处的deep配置无效 


	
</script>


此处无法正确的获取oldValue,强制开启了深度监视(deep配置无效)

情况四:监视reactive所定义的一个响应式数据中的某个属性

<template>

	<h2>姓名:{{person.name}}</h2>
	<button @click="person.name+='~'">修改姓名</button>

</template>

<script setup>
	import {ref,reactive,watch} from 'vue'

			let person = reactive({
				name:'张三',
				job:{
					j1:{
						salary:'前端'
					}
				}
			})
	 watch(()=>person.name,(newValue,oldValue)=>{
				console.log('person的name变化了',newValue,oldValue)
			}) 
</script>

情况五:监视reactive所定义的一个响应式数据中的某些属性

<template>

	<h2>姓名:{{person.name}}</h2>
	<h2>年龄:{{person.age}}</h2>
	<button @click="person.name+='~'">修改姓名</button>
	<button @click="person.age++">增长年龄</button>
</template>

<script setup>
	import {ref,reactive,watch} from 'vue'

			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:'前端'
					}
				}
			})
	 watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
				console.log('person的name或age变化了',newValue,oldValue)
			}) 

</script>

4、computed计算属性的使用

与vue2中的写法一致,并且需要引入computed

代码如下(示例):

<template>
</template>

<script setup>
    import {
      reactive,
      computed,
    } from 'vue'

    //数据
    let person = reactive({
       firstName:'第一个名字',
       lastName:'最后一个名字'
     })
    // 计算属性简写
    person.fullName = computed(()=>{
        return person.firstName + '-' + person.lastName
      }) 
    // 完整写法
    person.fullName = computed({
      get(){
        return person.firstName + '-' + person.lastName
      },
      set(value){
        const nameArr = value.split('-')
        person.firstName = nameArr[0]
        person.lastName = nameArr[1]
      }
    })

</script>

5、自定义hook的使用

vue3 中的 hooks 就是函数的一种写法,就是将文件的一些单独功能的js代码进行抽离出来,放到单独的js文件中,和 vue2 中的 mixin 有点类似

hooks的用法:一般会在src中创建一个hooks文件夹,专门用来存放hook文件来复用
某项目中的hook实例:

hooks目录下代码,将弹出的属性、展示、关闭封装复用

import {reactive} from 'vue'
import { DialogModel } from "@/type/BastType";
export default function useDialog(){
    //定义弹框属性
    const dialog = reactive<DialogModel>({
        title:'',
        visible:false,
        width:630,
        height:280
    })

    //展示
    const onShow = () =>{
        dialog.visible = true;
    }

    //关闭
    const onClose = () =>{
        dialog.visible =false;
    }

    //确定
    const onConfirm = () =>{
        dialog.visible = false;
    }

    return {
        dialog,
        onShow,
        onClose,
        onConfirm
    }
}

弹出组件

<template>
    <SysDialog
        :title="dialog.title"
        :width="dialog.width"
        :height="dialog.height"
        :visible="dialog.visible"
        @onClose="onClose"
        @onConfirm="confirm"
    >
        <template v-slot:content>
            <el-tree
                ref="parentTree"
                :data="treeData.data"
                node-key="id"
                :props="defaultProps"
                default-expand-all
                :highlight-current="true"
                :expand-on-click-node="false"
                @node-click="handleNodeClick"
            >
             <template #default="{ node, data }">
                    <div class="custom-tree-container">
                        <!-- 长度为0说明没有下级 -->
                        <span v-if="data.children.length == 0">
                            <DocumentRemove style="width: 1.3em; height: 1.3em; margin-right: 5px;color: #8c8c8c;"></DocumentRemove>
                        </span>
                        <!-- 点击展开和关闭 -->
                        <span v-else @click.stop="openBtn(data)">
                            <component style="width: 1.1em; height: 1.1em; margin-right: 5px;color: #8c8c8c;" :is="data.open ? Plus : Minus" />
                        </span>
                        <span>{{ node.label }}</span>
                    </div>
                </template>
            </el-tree>
        </template>
    </SysDialog>
</template>
<script setup lang='ts'>
import {DocumentRemove,Plus,Minus} from '@element-plus/icons-vue'
import SysDialog from '@/components/SysDialog.vue';
import useDialog from '@/hooks/useDialog';     //自定义hooks
import useParent from '@/composables/department/useParent';
//弹框属性
const { dialog, onClose, onShow } = useDialog()

//子组件传值给父组件
const emit = defineEmits(['select'])


//弹框确定事件
const confirm = () => {
    emit('select',selectNode)
    onClose();
}

//供父组件调用,显示弹框
const show = async () => {
    //获取树的数据
    await getTreeData();
    //设置弹框属性
    dialog.title = '选择上级部门'
    dialog.height = 420;
    dialog.width = 300;
    //显示
    onShow();
}

//把方法暴露出去
defineExpose({
    show
})

//树相关数据
const { treeData, defaultProps, handleNodeClick, getTreeData ,openBtn,parentTree,selectNode} = useParent();


</script>

<style lang="scss">

</style>

5、await的支持

setup 语法糖中可直接使用 await,不需要写 async , setup 会自动变成 async setup

代码如下(示例):

<script setup>
  import Api from '../api/Api'
  const data = await Api.getData()
  console.log(data)
</script>

6、provider/inject 祖孙传值

vue3.2 中 provide 和 inject 的用法和 vue2 中用法相似;只不过在vue3.2中 为了增加响应式数据的处理,我们可以在定义值的时候使用 ref 或者 reactive;

父组件代码:

<template>
	<div class="app">
		<h3>我是父组件,{{name}}--{{price}}</h3>
		<Child/>
	</div>
</template>

<script setup>
	import { reactive,toRefs,provide } from 'vue'
	import Child from './components/Child.vue'
    let car = reactive({name:'奔驰',price:'40W'})
    const {name,price}=toRefs(car)

    provide('car',{
        car, 
        changeName: (e) => {
           name.value = e
       }
    }) //给自己的后代组件传递数据
		
</script>

<style>
	.app{
		background-color: gray;
		padding: 10px;
	}
</style>

子组件代码:

<template>
	<div class="child">
		<h3>我是Child组件(子)</h3>
		<Son/>
	</div>
</template>

<script setup>
	import Son from './Son.vue'

</script>

<style>
	.child{
		background-color: skyblue;
		padding: 10px;
	}
</style>

孙子组件代码:

<template>
	<div class="son">
		<h3>我是Son组件(孙), 拿到爷爷组件传过来的值{{car.car.name}}--{{car.car.price}}</h3>
        <button @click="change">向爷爷组件传参</button>
	</div>
</template>

<script setup>
	import {inject} from 'vue'
	let car = inject('car')

    function change(){   //向爷爷组件传参
        car.changeName('宝马')
    }
</script>

<style>
	.son{
		background-color: orange;
		padding: 10px;
	}
</style>

7、路由新玩法useRoute 和useRouter

原来的vue2路由是通过this. r o u t e 和 t h i s . route和this. routethis.router来控制的。现在vue3有所变化,useRoute相当于以前的this. r o u t e ,而 u s e R o u t e r 相当于 t h i s . route,而useRouter相当于this. route,而useRouter相当于this.router

代码如下(示例):

<script setup>
  import { useRoute, useRouter } from 'vue-router'
    
  // 声明
  const route = useRoute()
  const router = useRouter()
    
  // 获取query
  console.log(route.query)
  // 获取params
  console.log(route.params)

  // 路由跳转
 	router.push({
			path: '/',
			query: {
				name: 'dx',
				age: 18
			}
		})

	router.push({
			name: 'Home',
			params: {
				name: 'dx',
				age: 18
			}
		})
</script>

8、响应式的判断

  1. isRef:检查一个值是否为ref对象
  2. isReactivce:检查一个对象是否是由reactive创建的响应式代理
  3. isReadonly:检查一个对象是否由readonly创建的只读代理
  4. isProxy:检查一个对象是否由reactive或者readonly方法创建的代理

9、Vue2.X和Vue3.X生命周期对比

1、setup() : 开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 method
2、onBeforeMount() : 组件挂载到节点上之前执行的函数;
3、onMounted() : 组件挂载完成后执行的函数;
4、onBeforeUpdate(): 组件更新之前执行的函数;
5、onUpdated(): 组件更新完成之后执行的函数;
6、onBeforeUnmount(): 组件卸载之前执行的函数;
7、onUnmounted(): 组件卸载完成后执行的函数;
8、onActivated(): 被包含在 中的组件,会多出两个生命周期钩子函数,被激活时执行;
9、onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行;
10、onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数。

vue2           ------->      vue3
 
beforeCreate   -------->      setup(()=>{})
created        -------->      setup(()=>{})
beforeMount    -------->      onBeforeMount(()=>{})
mounted        -------->      onMounted(()=>{})
beforeUpdate   -------->      onBeforeUpdate(()=>{})
updated        -------->      onUpdated(()=>{})
beforeDestroy  -------->      onBeforeUnmount(()=>{})
destroyed      -------->      onUnmounted(()=>{})
activated      -------->      onActivated(()=>{})
deactivated    -------->      onDeactivated(()=>{})
errorCaptured  -------->      onErrorCaptured(()=>{})```

你可能感兴趣的:(javascript,vue.js,前端)