• 纸上得来终觉浅,Vue3 新语法练起来


    搜集资源

    Vue3 入门指南与实战案例
    Vue在线演练场
    Vue3.0中Ref与Reactive的区别是什么
    Composition API RFC
    Vue.js 中使用defineAsyncComponent 延迟加载组件
    2022年必会Vue3.0学习 (强烈建议)

    Vue2与Vue3的对比

    • 对TypeScript支持不友好(所有属性都放在了this对象上,难以推倒组件的数据类型)
    • 大量的API挂载在Vue对象的原型上,难以实现TreeShaking。
    • 新推出了CompositionAPI
    • 更方便的支持了 jsx
    • Vue 3 的 Template 支持多个根标签,Vue 2 不支持
    • 对虚拟DOM进行了重写、对模板的编译进行了优化操作

    16. keep-alive用法示例

    <template>
      <router-view v-slot="{ Component }">
        <transition>
          <keep-alive :include="includeList">
            <component :is="Component" />
          </keep-alive>
        </transition>
      </router-view>
    </template>
    
    <script lang="ts"> export default {name:'AppEntry'} </script>
    
    <script lang="ts" setup>
      const router = useRouter();
      const includeList = ref(['AuthByManagerList']);
      router.beforeEach((to, from) => {
        if (from.path === '/authByManagerResult' && to.path === '/authByManagerList') {
          // 从客户认证结果页跳列表页,要清除缓存
          includeList.value = [];
        } else if (from.path === '/authByManagerList' && to.path === '/authByManagerDetail') {
          // 从列表页进入详情页,要缓存列表页
          includeList.value = ['AuthByManagerList'];
        }
      });
    </script>
    
    

    15.vue3中的props在模板中会同步更新,在setup函数中只更新一次。当props值再次发生改变是,在setup中要用watch才能监听到props的变化,如果props某个属性是对象,要设置深度监听,才能监听到变化。

    14.v-model可以定义多个

    <van-list
      v-model:loading="loading"
      v-model:error="error"
      error-text="请求失败,点击重新加载"
      @load="onLoad"
    >
      <van-cell v-for="item in list" :key="item" :title="item" />
    </van-list>
    
    

    13.context的属性

    setup(props, context) {
        context.attrs
        context.slots
        context.parent
        context.root
        context.emit
        context.refs
        
        return {
            
        }
      }
    

    12.computed属性完整写法

    watch的套路是:既要指明监听的属性,也要指明监听的回调函数。
    watchEffect的套路是:不需要指明监听的属性,监听中的回调函数用到了那个属性,就监听那个属性。

    watchEffect跟computed有点像:
    computed注重是计算出来的值,所以必须要有返回值。
    watchEffect更注重是过程,所以不用写返回值。

    const person = reactive({
       fistName:"Mr",
       lastName:"long"
    }) 
    
    // 计算属性简写
    let fullName = computed(()=>{
      return person.fistName + '-' + person.lastName
    })
    
    // 计算属性完整写法
    let fullName = computed({
      get(){
        return person.fistName + '-' + person.lastName
      },
      set(value){
        const newArr = value.split('-')
        person.fistName = newArr[0]
        person.lastName = newArr[1]
      }
    })
    
    const sltEle = computed( ()=>{ 
     return function(index){ 
      console.log('index',index); 
     } 
    }) 
    

    computed 接收参数的语法

    <template> 
     <div> 
      <div v-for="(item,index) in arr" :key="index" @click="sltEle(index)"> 
       {{item}} 
      </div> 
     </div> 
    </template>
    
    const sltEle = computed( ()=>{ 
     return function(index){ 
      console.log('index',index); 
     } 
    }) 
    

    11.watch和watchEffect的区别

    • watch 是需要传入侦听的数据源,而 watchEffect 是自动收集数据源作为依赖。
    • watch 可以访问侦听状态变化前后的值,而 watchEffect 没有,watchEffect获取的改变后的值。
    • watch 是属性改变的时候执行,当然也可以immediate,而 watchEffect 是默认会执行一次,然后属性改变也会执行。
    
        let count = ref(0)
        let countObj = reactive({count: 0})
    
        watch(count, (newVal, oldVal) =>{console.log(newVal, oldVal)} )
        // 只能监听ref、reactiveObject, function, array, 想监听reactive的某个属性,需要转换成函数
        watch(() => countObj.count, (newVal, oldVal) => {console.log(oldVal, newVal)}, {})
        // watch监听reactiveObject时,旧值无法获取正确获取
        watch (countObj, (newVal, oldVal) => {console.log(newVal, oldVal);})
        // 监听多个值,可以写成数组的形势
        watch ([oneName, twoName], ([oneNewName, twoNewName], [oneOldName, twoOldName]) => {
          console.log(oneNewName, oneOldName, twoNewName, twoOldName)
        })
    
    // 情况一监听ref响应式数据
     watch(count,(newValue,oldValue)=>{
         console.log(newValue,oldValue)
     },{immediate:true}) // immediate 立即监听
    
    // 情况二 监听多个ref响应式数据
    watch([count,name],(newValue,oldValue) =>{
        console.log(newValue,oldValue) // 此时value的数据是数组
    })
    
    // 情况三 监听reactvie响应式数据
    // 如果watch监听是reactive定义的响应式数据,则无法获取正确的oldValue,且强制开启深度监听。
       watch(person,(newValue,oldValue)=>{
           console.log(newValue,oldValue) // 两个值一致都是一样的
       })
      
      // 情况四 监听reactive定义的响应式数据的某个属性(基础数据类型)
      watch(()=>person.name,(newValue,oldValue) =>{
          console.log(newValue,oldValue)
      })
      
     // 情况五 监听多个reactive定义的多个响应式数据的属性(基础数据类型)
     watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
      console.log(newValue,oldValue)
     })
     
     // 情况六 监听reactive定义的响应式数据的某个属性(复杂数据类型)
     watch(() => person.class,(newValue,oldValue) =>{
       // 此时的class 为 { b:{c:20 } }, 想要监听c值的变化 则需要开启deep深度监听
       console.log(newValue,oldValue)
     },{deep:true}) 
    
    

    10.useSlots用法

    父组件

    <template>
      <!-- 子组件 -->
      <ChildTSX>
        <!-- 默认插槽 -->
        <p>I am a default slot from TSX.</p>
       
        <!-- 命名插槽 -->
        <template #msg>
          <p>I am a msg slot from TSX.</p>
        </template>
      </ChildTSX>
    
    </template>
    <script setup lang="ts">
    import ChildTSX from '@cp/context/Child.tsx'
    </script>
    

    子组件

    <script lang="ts" setup>
    // 注意:这是一个 .tsx 文件
    import { useSlots } from 'vue'
    const slots = useSlots()
    const ChildTSX = ()=>{
        // 渲染组件
        return () => (
          <div>
            {/* 渲染默认插槽 */}
            <p>{ slots.default ? slots.default() : '' }</p>
    
            {/* 渲染命名插槽 */}
            <p>{ slots.msg ? slots.msg() : '' }</p>
          </div>
        )
      }
    }
    export default ChildTSX
    </script>
    

    9.jsx语法,要借助插件@vitejs/plugin-vue-jsx

    <script lang="tsx" setup>
    const jsxNode = () => {
      return <div>text</div>;
    };
    </script>
    <template>
      <jsxNode />
    </template>
    

    8. 动态class的设置方法

    <script setup>
    import { ref } from 'vue'
    const msg = ref('Hello World!')
    </script>
    <template>
      <h1 :class="`red ${msg === 'Hello World!' && 'green'}`">{{ msg }}</h1>
      <input v-model="msg">
    </template>
    <style scoped>
      .red{
        color:red;
      }
      .green{
        color:green
      }
    </style>
    

    7.父子组件通信 props/emit方式

    父组件

    <template>
      <Child
        ref="child"
        title="用户信息"
        :index="1"
        :uid="userInfo.id"
        :user-name="userInfo.name"
        @update-age="updateAge"
      />
    </template>
    <script lang="ts" setup>
    import { ref, onMounted } from 'vue'
    import Child from '@cp/Child.vue'
    
    type TMember={
      id: number,
      name: string
    }
    const child = ref<typeof Child | null>()
    
    const userInfo: TMember = {
      id: 1,
      name: 'Petter'
    }
    
    // 父组件调用子组件的方法
    onMounted(() => child.value?.childMethod)
    
    const updateAge = (age: number) => {
      console.log(age);
    }
    
    
    </script>
    

    子组件

    <template>
      <p>标题:{{ title }}</p>
      <p>索引:{{ index }}</p>
      <p>用户id:{{ uid }}</p>
      <p>用户名:{{ userName }}</p>
    </template>
    <script lang="ts" setup>
    import { withDefaults, defineProps, defineEmits, toRefs, defineExpose } from 'vue';
    
    const props = withDefaults(defineProps<{
      title: string;
      index: number;
      uid: number;
      userName: string;
    }>(), { userName: 'zhangsan' });
    
    const { title, index, uid, userName } = toRefs(props);
    // 接受子组件传进来的方法
    const emit = defineEmits(['update-age']);
    
    setTimeout(() => {
      emit('update-age', 22);
    }, 2000);
    
    const childMethod = () => {
      console.log('我是子组件的方法')
    }
    
    // 子组件暴露方法给父组件
    defineExpose({ childMethod })
    </script>
    

    6.使用ref操作dom

    <template>
      <!-- 挂载DOM元素 -->
      <p ref="msg">留意该节点,有一个ref属性</p>
      <!-- 挂载DOM元素 -->
    </template>
    <script lang="ts" setup>
    import { onMounted, ref } from "vue";
    
    // 定义挂载节点,声明的类型详见下方附表
    const msg = ref<HTMLElement | null>();
    
    // 请保证视图渲染完毕后再执行节点操作 e.g. onMounted / nextTick
    onMounted(() => {
      // 比如获取DOM的文本
      console.log(msg?.value?.innerText);
    });
    </script>
    

    5.props用法

    <script lang="ts" setup>
      import {withDefaults,defineProps} from 'vue';
      const props = withDefaults(defineProps<{
        btnName: string;
        noBtn?: boolean;
        doAction: () => void;
        classStyle?: string;
      }>(),{
        noBtn:true,      // 设置默认值
        classStyle:''
      });
      
      const mActionClass=`m-action ${props.classStyle}`;
    <script>
    

    4.生命周期函数

    vue2和vue3生命周期对比

    beforeCreate  -> 使用 setup()
    created       -> 使用 setup()
    beforeMount   -> onBeforeMount
    mounted       -> onMounted
    beforeUpdate  -> onBeforeUpdate
    updated       -> onUpdated
    beforeDestroy -> onBeforeUnmount
    destroyed     -> onUnmounted
    errorCaptured -> onErrorCaptured
    
    <template>
      <div class="home">
        <p>{{ count }}</p>
        <p>{{ state.a }}</p>
        <button @click="add">加1</button>
      </div>
    </template>
    
    <script lang="ts" setup>
    import {
      ref,
      reactive,
      onBeforeMount,
      onMounted,
      onBeforeUpdate,
      onUpdated,
      onBeforeUnmount,
      onUnmounted,
      onErrorCaptured,
      onRenderTracked,
      onRenderTriggered,
      onActivated,
      onDeactivated,
    } from "vue";
    const count = ref(1);
    const state = reactive({ a: 10 });
    const add = () => {
      count.value += 1;
      state.a = state.a + 1;
    };
    
    onBeforeMount(() => {
      console.log("onBeforeMount");
    });
    onMounted(() => {
      console.log("onMounted");
    });
    
    onBeforeUpdate(() => {
      console.log("onBeforeUpdate");
    });
    
    onUpdated(() => {
      console.log("onUpdated");
    });
    
    onBeforeUnmount(() => {
      console.log("onBeforeUnmount");
    });
    
    onUnmounted(() => {
      console.log("onUnmounted");
    });
    
    onErrorCaptured((evt) => {
      console.log("onErrorCaptured", evt);
    });
    
    // 只执行一次,有几个响应式api,执行几次
    onRenderTracked((evt) => {
      console.log("onRenderTracked", evt);
    });
    
    // 行为如同onUpdated,每次有数据更新都会执行
    onRenderTriggered((evt) => {
      console.log("onRenderTriggered", evt);
    });
    
    // keep-alive要用到的函数
    onActivated(() => {
      console.log("onActivated");
    });
    
    onDeactivated(() => {
      console.log("onDeactivated");
    });
    </script>
    

    4. shallowReactive, shallowRef, readonly,shallowReadonly,toRaw,markRaw函数区别

    4.1 shallowReactive、shallowRef的之间的区别

    shallowReactive:浅监视
    shallowRef:不做监视
    

    4.2 readonly和shallowReadonly

    readonly:只读属性的数据,深度只读
    const state2 = readonly(state)
    shallowReadonly:只读的数据,浅只读的
    

    4.3 toRaw和markRaw

    toRaw将代理对象变成普通对象,数据变化,界面不会进行更新
    const user = toRaw(state);
    markRaw标记的对象数据,从此以后都不能在成为代理对象了
    const likes = ['吃',‘喝’]
    state.likes = markRaw(likes)
    
    <template>
      
      <h4>
        <button @click="triggerShallowReactiveRender">
          改变第一层才会渲染
        </button>
      </h4>
      <p>{{ shallowReactiveState.a }}</p>
    
        <h4>
        <button @click="triggerShallowRefRender">
          失去响应式
        </button>
      </h4>
      <p>{{ shallowRefState.a }}</p>
      
    </template>
    <script setup>
    import { readonly,reactive, shallowReactive, shallowRef, toRaw } from "vue";
    
    // shallowRef 与shallowReactive
    // shallowRef 与shallowReactive创建的是非递归的响应对象,shallowReactive创建的数据第一层数据改变会重新渲染dom
    const shallowReactiveState = shallowReactive({
      a: "initShallowReactiveState",
      b: {
        c: "c",
      },
    });
    
    //如果不改变第一层 只改变其他的数据 页面不会重新渲染,例如:
    shallowReactiveState.b.c = 2;
    
    //改变第一层的数据会导致页面重新渲染
    const triggerShallowReactiveRender=()=>{
      shallowReactiveState.a = "changeShallowReactiveState";
    }
    
    // shallowRef创建的对象没有响应式特性
    const shallowRefState = shallowRef({
      a: "initShallowRefState",
      b: {
        c: "c",
      },
    });
      
    const triggerShallowRefRender=()=>{
      //失去响应式--除非对整个对象重新赋值或者使用triggerRef(shallowRefState)触发页面更新
      shallowRefState.a = "changeShallowRefState";
    }
    
    // toRaw ---只修改数据不渲染页面
    var obj = { name: "test" };
    var toRawState = reactive(obj);
    var raw = toRaw(toRawState);
    //并不会引起页面的渲染,而obj.name,toRawState.name的值均已改变
    setTimeout(()=>raw.name = "zs",2000);
    
    
    // 不允许修改对象的值 
    const readonlyState = readonly({a:1,b:2});
    // 赋值会引起警告
    readonlyState.a=2;
    
    </script>
    

    3. toRefs和toRef的用途

    借助toRefs可以在template部分,直接使用结构后的对象单个键值,写法简洁,却不失响应式。
    toRef是转换reactive对象的单个值

    <template>
      <ul class="user-info">
        <li class="item">
          <span class="key">ID:</span>
          <span class="value">{{ id }}</span>
        </li>
    
        <li class="item">
          <span class="key">name:</span>
          <span class="value">{{ name }}</span>
        </li>
    
        <li class="item">
          <span class="key">age:</span>
          <span class="value">{{ age }}</span>
        </li>
    
        <li class="item">
          <span class="key">gender:</span>
          <span class="value">{{ gender }}</span>
        </li>
      </ul>
    </template>
    <script lang="ts" setup>
    import { reactive, toRef, toRefs } from "vue";
    
    interface Member {
      id: number;
      name: string;
      age: number;
      gender: string;
    }
    
    // 定义一个reactive对象
    const userInfo: Member = reactive({
      id: 1,
      name: "Petter",
      age: 18,
      gender: "male",
    });
    
    // 结构reactive对象,它的字段全部是ref变量
    const { id, age, gender } = toRefs(userInfo);
    const name: string = toRef(userInfo, 'name');
    
    // 2s后更新userInfo
    setTimeout(() => {
      userInfo.id = 2;
      userInfo.name = "Tom";
      userInfo.age = 20;
    }, 2000);
    
    </script>
    

    2. 响应式数据

    ref可以定义任何类型的数据,但是定义数据和对象时,在script部分使用时,赋值和取值,不如reactive方便
    reactive只能用于数组,对象类型。
    ref的本质是通过reactive创建的,Ref(10)=>Reactive({value:10});
    reactive的本质是将每一层的数都解析成proxy对象,reactive 的响应式默认都是递归的,改变某一层的值都会递归的调用一遍,重新渲染dom。

    <template>
        <p>{{count}}</p>
        <button @click="add">加1</button>
        <p>{{info.name}}</p>
        <button @click="changeName">改姓名</button>
    </template>
    
    <script lang="ts" setup>
    import { reactive, ref } from 'vue';
    interface Info{
      name:string;
      age:number;
    }
    // 响应式基本类型用ref
    const count = ref<number>(0);
    // 响应式引用类型用reactive
    const info:Info = reactive({ age: 100,name:'zhangsan' });
    
    const add = () => {
      // 基本类型赋值时,是赋值给value属性
      count.value += 1;
    };
    
    const changeName = () => {
      info.name="lisi";
    };
    </script>
    

    1.路由取值和跳转

    <script lang="ts" setup>
    import { useRoute, useRouter } from 'vue-router';
    const route = useRoute();
    const router = useRouter();
    // 获取路由信息
    console.log(route.query.id);
    // 编程式路由跳转
    const jump = ()=> {
      router.push({ path: `/about` , query:{id:'xxx}});
    };
    </script>
    
  • 相关阅读:
    返回一个整型数组中最大子数组的和
    matlab的优缺点
    Android 软件自动更新功能的实现
    Android的依赖注入框架:Dagger
    Wireshark解密HTTPS数据流
    QQ恶搞
    QQ恶搞
    编程语言的分类
    Windows常用快捷键与常用命令
    Chrome去掉标签页8个框
  • 原文地址:https://www.cnblogs.com/wangpenghui522/p/15738250.html
Copyright © 2020-2023  润新知