[Vue3] 学习笔记

130 阅读4分钟

——如果,你努力了但是并没有太大的改观,并不能代表你没有用,
而是代表你在赎罪,你总得为过去的懒散付出点代价,你应该更加努力, 欠的账总会还完,日子总会阳光明媚的!

1.main.js中创建vue实例方法变了

vue2

import Vue form "vue"
import App from xxxx

new Vue({
    
})

vue3

//引入的不是构造函数,而是createApp工厂函数
import {creatApp} form vue
import App from xxxx

// 创建应用实例对象
const app = creatApp(App)
app.mount(#app)

2.vue3可以没有根标签

3.setup

setup在beforCreat之前执行一次,this指向undefined


a.返回一个对象,对象中属性是setup中定义的变量或者函数(常用)
b.返回一个渲染函数,渲染函数要手动引入下 (不常用)
import {h} from "vue" 
return () => h("h1","xxxx") // 渲染函数内容会代替页面中的内容
setup接收两个参数props和context
props: 值为对象,包括组件传输过来并且在当前组件中接收了的属性,需要在setup外面用props:【‘xxx’】接受,在setup中的打印props才有值
context:上下文对象
        - attrs:值为对象,包括组件传输过来但是没有在当前组件中接收的属性
        - emit: 分发自定义事件的函数,相当于this.$emit,需要像props一样接收下,不然会报警告
        - slot: 插槽

4.尽量不要与vue2配置混用

  • vue2配置(data、methods、computed)可以访问到setup中的属性、方法
  • setup不能访问到vue2的配置
  • 如果有重名,setup优先

5.setup不能用async修饰

因为返回值不再是一个对象是一个promise,模板看不到return对象中的属性

6.ref函数

import {ref} from "vue"

setup() {
    // 使用ref函数包裹数据,使数据成为ref对象
    let name = ref("张三");
    let info = ref({
        job: "前端工程师"salary: 30
    })
    
    function change () {
        name.value = "李四"; // 此时name已经是ref对象,所以修改值要用value
        info.value.job = "后端工程师" // info是fef对象,info.value被vue3处理成proxy对象,所以job 不用再.value
    }
}

7.reactive 函数

作用: 用来定义对象类型的响应式数据

import {reactive} from "vue"

setup{
    const person = reactive({
        name: "张三",
        hobby: ['抽烟'],
        info: {
            salary: 30
        }
    })
    
    function change() {
        person.name = "李四";
        person.hobby[0] = "学习";
        person.info.salary = 40;
    }
}

8.vue3使用Proxy实现对象数据的响应式

vue3底层用proxy时设置set,get,deleteProperty时使用了Reflect反射对象,Reflect相当于Object,w3c友谊将Object的属性方法移植到Reflect上,Reflect在执行时会有一个返回值,let b = Reflect.set(a,'name','李四'); console.log(b) => true

const person = {
    name: "张三",
    age: 16
}

// 通过Reflect操作对象,可以得到一个返回值,在封装中可以减少try...catch的使用
const p = new Proxy(person,{
    get(target,properName) {
        console.log(`读取了person上的${properName}属性`)
        return Reflect.get(person,properName)
    }
    // 修改和新增都会出发set
    set(target,properName,value) {
        console.log(`修改了p身上的${properName},修改为value`)
        Reflect.set(person,properName,value)
    }
    deleteProperty(target,properName) {
        console.log(`删除了p身上的${,properName}属性`)
        return Reflect.deleteProperty(target,properName)
    }
})

9.计算属性computed

import {computed} from vue

setup{
...
    // 简写形式,此时只能只读
    peoson.fullName = computed(() => {
        return firstName + '-' + lastName
    })
    // 完整形式,此时可以读写
    person.fullName = computed({
        get() {
            return firstName + '-' + lastName
        };
        set(value) {
            firstName = fullName.split('-')[0]
            lastName = fullName.split('-')[1]
        }
    })
}

10.监视属性 watch

    import {watch} from vue

情况一,监视ref定义的响应式数据

    watch(name,(newValue,oldValue) => {},{immediate: true})

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

    watch([name,school],(newValue,oldValue) => {})

情况三,监视一个reactive定义的数据,此时不能正确或得oldValue,deep配置无效,强制开启深度监视

    watch(person,(newValue,oldValue) => {})

情况四,监视reactive定义的数据中的某个属性

    watch(() => person.name,(newValue,oldVaule) => {})

情况五,监视reactive定义的数据的多个属性

    watch([() => person.name,() => person.age],(newValue,oldVaule) => {})

特殊情况, 监视reactive定义的数据中的某个属性,这个属性是个对象,此时deep配置有效果

10.watchEffect

不用指明具体要监视的属性,回调函数函数中用到哪个数据,就会监视哪个属性,属性变动,执行watchEffect回调,有点像computed计算属性,不过computed注重return的结果,watchEffect不需要return,注重过程

import {watchEffect} from vue
...
let sum = ref(0)
setup() {
    watchEffect(() => {
    // sum如果变动就会执行打印
        const x = sum.value
        console.log(xxxxx)
    })
}

11.生命周期函数

配置项形式生命周期修改对应关系如下:

vue2vue3

beforeDestroy

beforeUnmount

destroy

unmounted

vue3中提供了composition API形式的生命周期钩子,对应关系如下:

composition APIvue2

setup()

beforeCreate

setup()

createed

onBeforeMount

beforeMount

onMounted

mounted

onBeforeUpdate

beforeUpdate

updated

updated

onBeforeUnmount

beforeUnmount

onUnmounted

unmounted

12.自定义hook

本质是一个函数,将setup中的composition API进行封装 类似于vue2中的mixin 优势:复用代码,让setup中代码逻辑更简单

13.toRef 和 toRefs

作用:创建一个ref对象,其value指向另一个对象的某个属性
语法: const name = toRef(person,'name')
应用: 要将响应式对象中的某个属性单独提供给外部使用
扩展: toRefs和toRef功能一致,但可以批量创建多个ref对象,语法:toRefs(person)

14.shallowReactive与 shallowRef

shallowReactive:只处理对象最外层属性的响应式(浅响应式)
shallowRef:只处理基本数据类型的响应式,不进行对象响应式的处理

15.readonly和shallowReadonly

readonly:让一个响应式数据变为只读(深只读)
shallowReadonly: 让一个响应式数据变为只读(浅只读)

16.toRaw与markRaw

toRef
作用:将一个由reactive生成的响应式对象转为普通对象;
场景:用于读取响应对应的普通对象,对这个对象的所有操作,不会引起页面的更新
markRef
作用:标记一个对象,使其永远不会再成为响应式对象
应用场景:1有些值不应该被设置成响应式,例如第三方类库;2当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能

17.cuntomRef

自定义ref,使用如下案例:

<template>
  
    <input type="text" v-model="message">
    <div>{{ message }}</div>
  </template>
  
  <script>
  import {customRef} from "vue";
  
  export default {
    name: 'HelloWorld',
    setup(){
        function debounce(fn,time) {
            let timer = null;
            return function() {
                let context = this;
                let arg = arguments;
                if(timer) clearTimeout(timer)
                timer = setTimeout(() => {
                    fn.apply(context,arg)
                    timer = null
                }, time);
            }
        }
        function myRef(value) {
            return customRef((track,trigger) => {
                return {
                    set(newValue) {
                        value = newValue
                        // 这里自定义了一个防抖函数,如果不好理解可以先去掉防抖功能,直接trigger()
                        //trigger的作用是触发模板重新渲染
                        //,因为设置了新的值
                        debounce(trigger,2000)()
                    },
                    get() {
                        // 调用track函数 告诉get追踪这个值, 必须调用,不然页面中不会展示新的值
                        track()
                        return value
                    }
                }
            })
        }
        let message = myRef('hello')
      return {
        message
      }
    }
  }
  </script>

18.provide和inject

用于组建向后代组件传递数据
组件:
import {provide} from 'vue'
...
setup (){
    provide('car',car)
}
子孙组件:
import {inject} from 'vue'
...
setup() {
    let car = inject('car') // 接收到的数据为响应式
}

19.Suspense

父组件

<template>
  <div class="main">
    <suspense>
       // 默认展示
      <template v-slot:default>
        <Demo2></Demo2>
      </template>
      // loading展示
      <template v-slot:fallback>
        <h1>正在加载中....</h1>
      </template>
    </suspense>
  </div>
</template>

<script>
import { defineAsyncComponent } from 'vue';
// 异步引入组件
const  Demo2 = defineAsyncComponent(() => import('./components/Demo2.vue'))

export default {
  name: 'App',
  components: {
    // HelloWorld,
    Demo2,
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.main {
  width: 100%;
  background: blue;
  padding: 50px;
}
</style>

子组件

<template>
    <div class="demo2"></div>
</template>

<script>

export default ({
    name: "demo2",
    setup() {
        
    },
})
</script>

<style scoped>
.demo2 {
    width: 500px;
    height: 500px;
    background: orange;
}
</style>

20.Options API和Composition API比较

Options API: 新增或更改一个需求,就要分别在data、methods、watch、computed中进行修改
Composition API: 我们可以更优雅的组织我们的代码、函数,让相关功能的代码组织在一起放到一个hook中