Vue3总结

本文详细比较了Vue2与Vue3在API、组件生命周期、条件渲染、响应式系统、数据传递、自定义Hook、模板引用、依赖注入、数据监听和Pinia状态管理等方面的变化,帮助开发者了解升级路径和新功能应用。
摘要由CSDN通过智能技术生成

Vue2与Vue3的区别?
创建应用

Vue3中的 Vue不再是一个构造器 而是一个对象
创建应用使用createApp函数创建Vue应用
单文件中可以使用script setup语法糖 代替setup函数 作为组合式api入口(不需要手动暴露大量状态的方法给模板)
模块化开发组件可以不用注册直接使用 (仅限于setup语法糖(一个vue文件只有一个语法糖,可有多个script标签)中)
template选项中根标签可以有多个

API

vue3: 组合式API。 setup函数作为组合式api入口 使得代码可读性更强
vue2: 选项卡API。 data methods computed …

Vue3:
<script>
   const { createApp, ref, reactive } = Vue;//js中使用到的
   createApp({
     setup() {
       let obj = reactive({ name: 'terry'  });
       let arr = ref([1,2,3,4]);

       return { obj, arr, }//div中使用到的
     }
   }).mount('#app');//绑定的div
 </script>
 
Vue2:
 <script>
   new Vue({
     el:'#app',
     data:{   },//存放数据,变量,响应式依赖
     created(){ },
     methods:{}computed:{},
     watch:{}
   })
 </script>
生命周期

(仅名字变了,功能没变)

  • setup代替beforeCreate、created。 this不继续指向组件实例 不使用this
  • onBeforeMount(挂载前)
  • onMounted(挂载)
  • onBeforeUpdate(更新前)
  • onUpdate(更新)
  • onBeforeUnmount(解绑前)替代beforeDestory (销毁前)
  • onUnmounted (解绑)替代destoryed(销毁)
  • onErrorCaptured(错误调用)
  • onActivated(激活)
  • onDeactivated(未激活)
  • onRenderTracked(不常用)
  • onRenderTriggered(不常用)
  • onServerPrefetch(不常用)
v-if和v-for优先级

v2: v-for优先级高于v-if优先级
v3: v-if优先级高于v-for优先级
不建议vif和vfor同时使用 优先级不明显 (别的还好,但如果if中正好用到for的数据,就渲染不出来)

nextTick

vueDom更新机制 异步更新 数据发生改变 此时dom不会立即同步更新 而是在下一次回调中更新dom
状态多次发生改变 dom只更新一次
为了数据更新dom同步更新 此时nextTick

  • vue2使用方法:
    • Vue.nextTick().then(()=>{ 获取更新后dom })
    • Vue.nextTick(()=>{ 获取更新后dom })
    • this.$nextTick(()=>{})
  • vue3 以后nextTick小钩子函数
    • await nextTick();
    • nextTick(()=>{})
ref、reactive创建响应式基础 、区别**************

ref 可以接收任意数据类型的值 并自动追踪为响应式依赖,声明得响应式依赖会被包裹在带有.value属性得ref对象中(即访问属性不能直接obj,而是obj.value才能访问得到)
reactive 只能接收引用数据类型 并自动追踪为响应式依赖

reactive和 ref(主要使用)区别?

  1. ref接收任意数据类型值, 具有更深层次响应式系统,嵌套数据结构更深层次,ref也会检测到变化
    reactive只能接收对象类型的值(对象、数组、集合、set)
  2. toRefs和toRef仅对reactive声明得响应式对象生效
    使用ref函数创建响应式对象通过toRef和toRefs无法得到ref
  3. 使用reactive声明响应式对象不能完整替换对象,对象中属性不能解构,都会丢失响应性

如果用reactive声明的是数组或者集合,遇到异步,可以使用.value进行赋值,模板中也使用.value来渲染就可以显示结果,(否则按同步执行异步请求还没获取到就输出了,自然什么都显示不出来)

toref、torefs 便捷访问 与 isref()、unref()类型判断

访问数据{{obj.books.name}}这样很冗余
let nameRef = toRef (obj.books, ‘name’); 然后{{nameRef}}这样就简便很多
注:仅对reactive声明得有效,ref声明得无效

  • toRef是基于响应式对象的一个属性创建一个ref 并自动追踪为响应式依赖
  • toRefs是基于响应式对象的多个属性创建多个ref 并自动追踪为响应式依赖(相当于多个toRef)

isRef()、unref()类型判断
isRef() 检测值是否是ref 只对于ref创建得函数返回true
unref() 参数是ref 返回内部属性 。如果不是 返回参数

模板引用

< div ref=‘box’ />
v2:this. $ refs.box
v3:通过模板引用获取dom元素, 声明和模板引用同名ref
       const box = ref(null);
       onMounted(()=>{ console.log(box.value) })

自定义Hook函数

自定义一个函数,其中可能用到vue3中的一些API,或者逻辑完全取决于业务

组件

局部注册

  1. setup语法糖中(组件不用注册,直接导入使用)
< script setup>
  import Header from './pages/Header.vue'
  import Footer from './pages/Footer.vue'
</ script>
  1. setup函数中(类似vue2里的局部注册组件)
<script>
  import Header from './pages/Header.vue'
  import Footer from './pages/Footer.vue'
  export default {
    // 使用components选项进行组件局部注册
    components: { Header,Footer},
    setup() { }
  }
</script>

全局注册(在main.js文件中)

import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import Header from './pages/Header.vue'
import Footer from './pages/Footer.vue'

// 组件全局注册 component(组件名字,组件所对应vue页面/组件配置项)
let app = createApp(App);
app.component('Header',Header);
app.component('Footer',Footer);
app.mount('#app')

组件传值
  • 父给子传:

    • prop
      1.父组件将数据写入到子组件标签上
      2.子组件接收数据 setup函数接收
      props:['msg','title'],
      setup(props){
       // props--->此时props(第一个参数)就是父组件传递的数据 被包裹在对象中 
       // 如果解构父组件传递的数据会丢失响应性  需要使用toRef或者toRefs创建具有响应式ref
        const {msg,title} = props;//会丢失响应式
        const {msg,title} = toRefs(props);
        return {
          props
        }
      }
      
      2.子组件接收数据 script setup语法糖接收
      使用defineProps宏接收父组件传递的数据
      const props = defineProps(['msg','title']);
      const {msg,title} = toRefs(props);//同上响应性
      
    • mitt
    • 依赖注入
    • V-model
  • 子给父传:

    • emit
      1.setup函数中

      $emit(‘自定义事件’,数据)

      1.或者script setup语法糖

      使用defineEmits宏声明自定义事件 得到发射自定义事件方法emit(event,args)
      const emit = defineEmits([‘my-event’,‘submit’]);
      emit(‘自定义事件’,数据);

      2.父组件在父组件中的模板中(子组件标签上)声明自定义事件

      <child @自定义事件名称=‘事件处理程序’>
      function handler(a,b){ }

    • defineExpose

      子组件使用defineExpose宏暴露数据和方法
      defineExpose({ subMsg, a, increment })
      父组件使用模板引用获取子组件实例中属性和方法
      const child = ref(null);
      onMounted(()=>{
      child.value
      child.value.subMsg/a increment()
      })
      < child ref=‘child’></ child>

  • 兄弟传值(取消了事件总线,而是用mitt插值(父子、兄弟、祖先后代都可以传值))

    • mitt
      • 定义一个js文件
        import mitt from ‘mitt’
        const emitter = mitt();
        export default emitter
      • 兄弟组件引入emitter对象
        import emitter
        emitter.emit(‘自定义事件’,传递数据)
      • 另一个兄弟组件引入emitter对象
        import emitter
        emitter.on(‘自定义事件’,()=>{ })
    • localstorage
      • 传递数据:localStorage.setItem(‘msgH’,JSON.stringify(obj))
      • 获取数据:let res = localStorage.getItem(‘msgH’);
                          console.log(JSON.parse(res),‘获取header数据’);
依赖注入(祖先后代传值)

let msg=123;

  • 祖先组件进行数据传输:provide(‘注入名’,‘注入值’)
    1. provide(‘message’,msg);
    2. provide(‘state’,{ref,方法})
  • 后代组件进行数据注入:const 同上注入名=inject([‘注入名’])
    1. const message = inject([‘message’]);
    2. const {ref,方法} = inject([‘state’]);//解构
watch侦听器
//watch(监听数据源,()=>{},{}) 
 监听ref  
watch(page,                            //侦听数据源
      (n, o) => { console.log(n, o)},   //回调函数
      { immediate: true });            //表示不需要等待状态改变立即触发一次

 监听getter函数   ()=>obj
watch(() => x.value + y.value,
      (result) => { console.log(result, '监听getter函数')});

 监听多个数据源租组成数组
watch([x, () => y.value], 
      ([newX, newY]) => {console.log(`x is ${newX} and y is ${newY}`)});
  • 不可以直接监听对象中得属性(v2可以) 需要使用getter函数监听
// watch(state.count,(res)=>{
//   console.log(res,'无法直接监听响应式对象属性')
// });
watch(()=>state.count,(res)=>{
  console.log(res,'使用getter函数形式监听响应式对象属性')
});
  • 监听响应式对象默认给创建一个深层侦听器 不需要设置deep:true(v2需要)
watch(params,(res)=>{
 console.log(res,'监听响应式对象变化');
})
  • 监听响应式对象中的对象 getter函数也无法监听到变化 要么整个替换对象中的对象(即重新给这个对象赋其他值) 要么强制转为深层侦听器
watch(()=>obj.clazz,(res)=>{
  console.log(res,'8888888888888888');
});                              //出不来

setTimeout(()=>{
  obj.clazz = {no:'b1002'};  // 完全替换对象中的对象 
  arr1.push(5);
},2000);                     //输出888888888
或者
watch(()=>obj.clazz,(res)=>{
  console.log(res,'8888888888888888');}{deep:true});              //使用deep:true 强制转为深层侦听器 
watchEffect

自动收集依赖并进行监听 回调函数会立即执行一次 状态发生改变 回调函数继续执行
深层嵌套的 数据结构 或者 数组 或者是 直接对象 无法直接进行监听
调用watcheffect后会产生一个unwatch函数,通过手动调用实现停止侦听器

// 自动收集响应式状态并监听watchEffect(  ( )=>{ }  )
const unwatch = watchEffect(()=>{
  console.log('count发生改变',count.value);
});
// 手动停止侦听器
unwatch();
watch和watchEffect区别?

1.watch只侦听明确数据源(写在watch数据),回调函数在响应式状态发生改变才会触发
2.watchEffect自动收集所有响应式依赖并进行监听,回调函数会立即执行一次,后续状态发生改变继续执行回函数
3.watch侦听数据源广泛,数组或者对象都可以直接监听,watchEffect无法直接监听

透传 attributes(类似继承)

父组件模板提供了class 、id 、style 、v-on这类属性会透传给子组件模板

组件上的V-model

Parent组件:

<script setup>
import Child from './Child.vue'
import {ref} from 'vue'
const searchText =ref('我是父组件数据')
</script>

<template>
  <div>
    //组件v-model  组件上 
    {{ searchText }}
     Parent组件--<Child v-model="searchText"></Child>
    // v-model参数 v-model:title 
    //Parent组件--<Child v-model:title="searchText"></Child>
  </div>
</template>

Child组件:

<script setup>
import {defineProps,defineEmits} from 'vue'
defineProps(['modelValue'])//接收父组件传递的数据
defineEmits(['update:modelValue'])//发射自定义事件,名字与接收数据名一样
//defineProps(['title']); 
//defineEmits(['update:title']); 
</script>

<template>
 <div>
   <input :value="modelValue" @input="emit('update:modelValue', $event.target.value)"/>
   //<input :value="title" @input="emit('update:title', $event.target.value)"/>
 </div>
</template>
Pinia

组合式API
defineStore( id仓库唯一标识,()={} )

在main.js中:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
在状态机文件中:
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import axios from 'axios';

// 相当于创建了useCounterStore仓库   defineStore(id仓库的唯一标识,()=>{})
export const useCounterStore = defineStore('counter', () => {
  // 类似于state  相当于声明状态
  const count = ref(0);
  const list = ref([]);
  // 类似于getters  
  const doubleCount = computed(() => count.value * 2)
  // 同步方法 同步actions 
  function increment() {
    count.value++
  }
  // 异步actions 
  async function getEmpData(){
    let res = await axios.get("http://121.199.0.35:7001/dashboard/queryDeviceOnlineNumber"{});
    console.log(res,'获取响应');
    // 修改状态 
    list.value = res.data.data;
  }
  return { count, doubleCount, increment,list,getEmpData}
})
在组件中使用pinia
 import {useCounterStore} from 'xxx';

 const store = useCounterStore();
 store.count 或者 store.increment 或者 store.doubleCount

直接解构状态机中state 、 getters、 actions会丢失响应性
应该用storeToRefs() 和 prop的toRefs()一样

  // const {count,doubleCount,increment} = useCounterStore(); 会丢失响应式
  import {storeToRefs} from 'pinia'
  const {count,doubleCount} = storeToRefs(useCounterStore());

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值