vue3基本使用笔记(汇集精华版)

时间:2024-12-13 07:00:17

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可以提高组件复用性 更好维护

img

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 中需要 .valuetemplate 中可省略
<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

reactiveref的选择

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函数

步骤

  1. 父组件提供数据
  2. 父组件将数据传递给子组件
  3. 子组件通过defineProps进行接受
  4. 子组件渲染父组件传递的数据
<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函数

步骤

  1. 子组件通过defineEmits来获取emit函数
  2. 子组件通过emit触发事件,并且传递数据
  3. 父组件提供方法
  4. 父组件通过自定义事件的方式给子组件注册事件
<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

跨级组件通讯provideinject函数

image-20220707111326617

通过provideinject函数可以简便的实现跨组件通讯

步骤

  1. provide提供后代组件需要依赖的数据或函数
  2. 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