Vue3

作者: Zindex | 来源:发表于2022-08-18 16:26 被阅读0次

    拉开序幕的 setup

    • setup 是所有 composition API 的表演舞台。组件中所用到的数据、方法等均配置在 setup 中。
    • setup 函数可以返回两种东西
      1. 返回一个对象,对象中的属性、方法在模板中均可直接使用
      2. 可以返回一个渲染函数 render函数,可以自定义渲染内容。
    • vue3 的 compositionAPI 不要与 vue2 的 optionAPI 的数据混用。vue2能访问到vue3的数据,vue3不能访问vue2,具体见生命周期。
    <script>
    import {ref} from 'vue'
    export default {
      name: "HelloWorld",
      props: {
        msg: String,
      },
      setup() {
        let a = ref(0);
        const b = ref(2);
        const fn = ()=>{
          console.log(a.value++)
        }
        return {
          a,b,fn
        }
      },
    };
    </script>
    

    ref 和 ref返回值的value

    数据需要用 ref 函数包装成 RefImpl 的实例才能实现响应式,使用 ref 需在 vue3 库中 import 一下 ref,修改 ref 值需要修改他的 value 而不是直接改,否则报错 error Must use '.value' to read or write the value wrapped by 'ref()',而模板中读取 ref 数据则不需要 .value

    <template>
        <p> a </p>
        <button @click="fn">++</button>
    <template>
    
    <script>
    import {ref} from 'vue'
    export default {
      setup() {
        let a = ref(0);
        const fn = ()=>{
          console.log(a.value++)
        }
        return {
          a,b,fn
        }
      },
    };
    </script>
    

    如果 ref 的参数是一个对象,那么读取或修改对象里的值就不用 .value,控制台输出这个b的值可以看到是一个proxy的实例,借助了vue3的性函数 reactive 函数实现的

    const b = ref({
        b1: 'bbbbb',
        b2: 22222
    }) 
    ;(function(){
        console.log(b.value);
        b.value.b2++
    })()
    

    reactive

    上面说到 ref 传入复杂数据类型会借助 reactive 这个函数,往 reative 传入基础类型数据则会警告说 value cannot be made reactive,使用 reactive 包装复杂类型为响应式数据,修改对象里面的值,就不用 .value了。

    const b = reactive({
        b1: 'bbbbb',
        b2: 22222
    }) 
    ;(function(){
        console.log(b.value);       //输出undefined,因为没有b变成一个proxy对象了
        b.b2++
    })()
    

    proxy响应式原理例子

    const person = {
        name: 'ming',
        age:18
    }
    const p = new Proxy(person,{
        get(target,propName){
            console.log(`有人读取了p身上的${propName}属性`);
            return 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)
        }
    })
    

    setup注意事项

    setup执行时机

    setup 会在 beforeCreate 之前执行一次,this 指向为 undefined

    setup的参数 props 和 context

    setup(props,context){
        console.log(props,context)
    }
    

    props 和 context.attrs

    • 第一个参数 props 是父组件传递过来的数据,context.attrs 类似于vue2里的 $attrs ,是未被props接收的组件通信的数据。
    • 在低版本的vue3中,如果传递了 props而不在子组件中使用 props 接收,则会发出警告。所以推荐传递的 props 都用 props 接收。

    context

    输出 context,可以看到熟悉的有 attrs,emit,slots

    emit:如果父组件给子组件绑定了自定义事件,如果子组件想要触发自定义事件,则需要 context.emit() 触发,不再像vue2那样$emit触发

    slots:就是插槽相关内容。

    computed

    类似于 vue2 中的 计算属性,使用时需要引入 computed ,computed函数里面的参数:如果是简写形式是一个函数,如果是完整形式是一个对象

    import {computed} from 'vue'
    
    setup(){
        let fullName = computed(()=>{
            return person.firstName + person.lastName
        })
    }
    

    完整形式

    setup() {
        const person = reactive({
            firstName: '',
            lastName: ''
        })
        let fullName = computed({
            get() {
                return person.firstName + person.lastName
            },
            set(value) {
                person.firstName = value.slice(0, 1)
                person.lastName = value.slice(1)
            }
        })
        return {
            person, fullName
        }
    }
    

    watch

    vue3中 watch 函数监视属性。

    1. 第一个参数为监视的目标,可以是一个ref,也可以是一个数组里包多个ref。也可以是 reactive,注意 ref 不是 ref.value
    2. 第二个参数是一个回调函数,回调函数里包含两个参数 oldValue 和 newValue
    3. 第三个参数是配置项,用于开启 immediate 和 deep
    <template>
        <h2>{{ auro }}</h2>
        <button @click="auro += 'o'"></button>
    </template>
    <script>
    import { ref, watch } from 'vue'
    export default {
        setup() {
            let auro = ref('auro');
            let panton = ref('panton')
            watch([auro,panton], (newValue, oldValue) => {
                console.log(oldValue, newValue);
            })
            return { auro }
        }
    }
    

    watch监视对象的三种情况

    监视一个对象

    但vue3中监视 reactive 类型数据自动开启深度监视且关不掉。
    同时注意监视 reactive 类型数据无法监测 newValue 和 oldValue,因为是复杂类型数据,改了之后新旧值是一样的。

    监视对象里的属性

    监视对象里的属性(基础数据类型),监视的目标要写成 函数有返回值 形式,这样子可以监视得到newValue 和 oldValue。
    当然也可以是一个数组里包多个函数

    export default {
        setup() {
            let bibu = reactive({
                name: 'bibu',
                age: 22
            })
            watch(() => bibu.age, (newValue, oldValue) => {
                console.log(oldValue, newValue);
            })
            
            watch([() => bibu.age,()=>biubiu.name], (newValue, oldValue) => {
                console.log(oldValue, newValue);
            })
            return { auro, bibu }
        }
    }
    

    监视对象里的属性(复杂数据类型),监视目标写成 函数有返回值 形式以外,还要开启 deep:true 配置项才能监视得到改变进而使用watch里的回调函数。

    export default {
        setup() {
            let bibu = reactive({
                name: 'bibu',
                age: 22,
                hobby: ['抽烟', '喝酒']
            })
            watch(() => bibu.hobby, (newValue, oldValue) => {
                console.log(oldValue, newValue);
                console.log(bibu.hobby);
            }, { deep: true })
            return {bibu}
        }
    }
    

    监视 ref 定义的对象

    监视ref定义的对象,需要监视这个目标的 value 值才能触发 watch 的回调函数,相当于监视了一个 reactive ,如果不写 .value 开启deep:true 也可以触发监视回调

    wathcEffect函数

    watch 是指定监视目标后执行指定的回调。
    watchEffect 是不指定监视目标,watchEffect 的回调执行过程中用到哪个目标,就监视哪个目标,并把回调重新执行一次。
    注意 watchEffect 相当于开启了 immediate:true 回调会先执行一次。

    watchEffect(()=>{
        let x = a.value
        console.log('watchEffect回调执行了')
    })
    

    watchEffect 有点像 computed,都可以检测到依赖值的变化,但computed注重返回值,watchEffect 则只执行回调

    vue3 生命周期钩子

    vue3 除了提供 optionAPI 的生命周期钩子以外,还提供了 compositionAPI 的生命周期钩子。
    详情见官方文档

    自定义 hook

    自定义一个模块,模块里面可以使用各种 vue 的 api,导出之后方便其他vue组件复用。比vue2的mixin好在模块里面可以使用各种vue的api了,例如响应式数据,生命周期钩子,此时 组合式api 的好处就显示出来了。

    import { onMounted, onUnmounted, reactive } from "vue";
    export default function () {
        const axis = reactive({
            x: 0,
            y: 0
        })
        const clickAxis = (event) => {
            axis.x = event.x;
            axis.y = event.y
        }
        onMounted(
            () => {
                window.addEventListener('click', clickAxis)
            }
        )
        return axis
    }
    

    其他组件中导入并使用就可以了

    <template>
        <h3>{{ axis }}</h3>
    </template>
    <script>
    import recordClick from '@/hook/recordClick';
    export default {
        setup() {
            let axis = recordClick();
            console.log(axis);
            return { axis }
        }
    }
    </script>
    

    toRef 与toRefs

    作用:创建一个 ref 对象,其 value 值指向另一个对象中的某个属性。
    参数一为要指向的对象,参数二为要指向对象的属性

    const person = reactive({
        name: 'ming',
        age: 18,
        hobby: ['电影','音乐']
    })
    
    return {
        name: toRef(person,'name'),
        age: toRef(person,'age')
    }
    

    如果不使用 toRef ,则在模板中使用这些数据需要 person.name,person.age这样使用,用了 toRef 之后,就可以单独把 reactive 里面的单条数据包装为一个 ref 数据输出给模板使用。

    <template>
        <p>{{name}}</p>
    </template>
    

    toRefs 则是把 reactive 整个对象的每一条数据都包装成 ref 输出出去,然后再用展开运算符展开return给模板使用。

    const person = reactive({
        name: 'ming',
        age: 18,
        hobby: ['电影','音乐']
    })
    
    return {
        ...toRefs(person)
    }
    

    如果直接展开person,那么数据不是响应式的,用 toRefs 则是响应式的

    shallowReactive 与 shallowRef

    shallowReactive:只处理对象最外层的属性为响应式。
    shallowRef:只处理自身为响应式,如果传入一个复杂类型的则改复杂类型内部的数据不改为响应式。

    let person = shallowReactive({
        name: 'ming',
        age: 18,
        hobby: ['电影','音乐']
    })
    //此时 hobby 里面的每一项改变都不会触发页面更新
    

    shallowRef 官方解释:
    创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的。

    const foo = shallowRef({})
    // 改变 ref 的值是响应式的
    foo.value = {}
    // 但是这个值不会被转换。isReactive(foo.value) // false
    

    readonly 与 shallowReadonly

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

    let person = reactive({
        name: 'ming',
        hobby: ['kk','vv']
    })
    person = shallowReadonly(person)
    

    toRaw 与 markRaw

    toRaw:
    作用:将一个 reacttive 响应式对象传入转换为普通对象后return,官方文档说不建议保留对原始对象的持久引用。
    markRaw:
    作用:标记一个对象,使其永远不会再成为响应式对象。

    customRef

    创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,并且应该返回一个带有 get 和 set 的对象。
    官网示例:使用自定义 ref 通过 v-model 实现 debounce 的示例:

    function useDebouncedRef(value, delay = 200) {
      let timeout
      return customRef((track, trigger) => {
        return {
          get() {
            track()
            return value
          },
          set(newValue) {
            clearTimeout(timeout)
            timeout = setTimeout(() => {
              value = newValue
              trigger()
            }, delay)
          }
        }
      })}
    
    export default {
      setup() {
        return {
          text: useDebouncedRef('hello')
        }
      }}
    

    provide 与 inject 祖后通信

    provide接收两个参数,第一个参数是为这个通信的东西起个名字,第二个参数是需要通信的值。
    祖先组件中

    export default {
      components: {
        HelloWorld,
      },
      setup() {
        const person = reactive({
          name: "ming",
          age: 18
        })
        provide('ming', person)
      }
    }
    

    后代组件中

    export default {
        setup() {
            let qiming = inject('ming')
            console.log(qiming);
            return { qiming }
        }
    }
    

    响应式数据的判断API

    • isRef:检查一个值是否为一个ref对象
    • isReactive:检查是否由 reactive 创建的响应式代理
    • isReadonly:检查是否只读
    • isProxy:

    Fragment 组件

    现在的 vue3 不需要根标签了,使用 Fragment 实现了虚拟根标签,在开发者工具可以看到

    Teleport 组件

    teleport 组件可以实现将DOM节点转移至任何 vue 应用上的节点,在to属性指定即可,例如全局遮罩组件转移至根组件

    <teleport to="body">
    </teleport>
    

    suspense 组件

    异步展示组件时会用到,提供两个插槽,默认插槽是真正要展示的内容,替补插槽是如果默认插槽内容没加载完就展示的内容。
    可见文档案例

    <template>
      <suspense>
        <template #default>
          <todo-list />
        </template>
        <template #fallback>
          <div>
            Loading...
          </div>
        </template>
      </suspense></template>
    
    <script>export default {
      components: {
        TodoList: defineAsyncComponent(() => import('./TodoList.vue'))
      }}</script>
    

    Vue3 其他更改

    vue3 全局 API

    const app = createApp(App)
    app.mount('#app')
    
    2.x全局API 3.x实例API
    Vue.config.xxx app.config.xxx
    Vue.config.productionTip vue3脚手架自动判断环境
    Vue.component app.component
    Vue.directive app.directive
    Vue.mixin app.mixin
    Vue.use app.use
    Vue.prototype app.config.globalProperties

    vue3 动画类名

    vue2.x写法

    .v-enter,
    .v-leave-to{
        opacity:0;
    }
    .v-leave,
    .v-enter-to{
        opacity:1;
    }
    

    vue3.x写法

    .v-enter-from,
    .v-leave-to{
        opacity:0;
    }
    .v-leave-from,
    .v-enter-to{
        opacity:1;
    }
    

    移除 keyCode 作为 v-on 的修饰符

    移除 v-on.native 修饰符

    给组件绑定原生事件不用再加 native,如果想把原生事件改为自定义事件,在子组件中使用 emits 配置项声明
    父组件:

    <my-component
        v-on:click = "handleNativeClickEvent"
    />
    

    子组件:

    export default {
        emits:['click']
    }
    

    相关文章

      网友评论

          本文标题:Vue3

          本文链接:https://www.haomeiwen.com/subject/wmbegrtx.html