• vue3 搬砖要会那些


    Ⅰ. vue3 简介

    vue3 代号 onepiece 【海贼王】

    2022年2月7日 成为默认版本 , 2023年底 vue2将彻底淘汰

    Ⅱ. 优化内容

    • 更新渲染 快了 1 ~ 2 倍之间 ;
    • 首次渲染 快了 50% 左右 ;
    • 运行内存 减少 50% 左右 ;
    • 打包内存 减少 40% 左右 ;
    • 更强的 ts (typescript) 支持 => 引用下作者 ( 尤雨溪 ) 的原话 ;

    真的对 类型 特别特别 较真的用 React 也没什么不好,无非就是性能差点。

    • 逻辑代码模块化 composition Api => 如图: ( 逻辑变的不再零散 )
      在这里插入图片描述

    Ⅲ. 源码优化

    proxy 代理虚拟dom (diff算法 + 静态标记)
    采用 ES6 proxy对象 去数据监听
    实现原理 => 点击学习
    对可能变化的dom 进行静态标记
    只监听标记了的

    Ⅳ. 项目创建

    • vue3的项目创建有2种方式 => 更加推荐 Vite
    • 使用脚手架创建要首先安装 vue脚手架 ,项目启动速度没有 Vite 快捷
    ① vue 脚手架 创建② Vite 创建
    #安装或升级 => npm install -g @vue/cli
    #创建项目    => npm create vue_test
    #创建项目 => npm init vite-app vue-test2

    Ⅴ. 测试你到那个阶段呢 ?

    阶段 ① 简单的页面编写 (需要掌握)
    1.setup 函数 与 setup单文组件
    setup函数setup单文组件
    1.其中的变量和方法,需要return 才能使用1.单文组件中的 变量不需要 retrun
    2.不要对响应数据解构,防止响应失效2.只要是reactive和ref 创建的就可以数据响应
    3.通过函数形参,获取父子组件通信对象 (props 、emit)3.通过内置函数去访问,来组件通信 (defineProps、defineEmits )

    Ⅰ.setup 函数 【响应数据写法、父子通信写法】=>

    父组件 :
    <templete> 
       <Children :fatherName = 'fatherName'  @handle = 'fun' />
    templete>
    <script>
    export default {
      setup(){
    	function fun(num){ console.log('接受子组件传递过来的值'+num ); }
        return {
    	  fun,
    	  fatherName: '张xx',
        }
      }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    子组件 ( Children ) :
    <templete>  
       <span> {{ name }} span>   
       <span> {{ age  }} span>
       <span> {{ fatherName }} span>
    templete>
    <script>
    import { reactive , toRefs } from 'vue';
    export default {
        props:{
             fatherName:{ type:String } 
        },
        setup(props,{ emit }){
            console.log( props.fatherName );  // => 获取父组件  传递  子组件的参数
            emit('handle' , 123);             // => 子组件 委托 父组件 调用方法接受参数
            
    		const state = reactive({ name: '张三', age: 18 })
    		return {
    			//...state   => 会失去响应
    			...toRefs(state)   // => 正确写法
    			...toRefs(props)
    		}
        }
    }
    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

    setup 单文组件 【响应数据写法、父子通信写法】=>

    父组件 :
    <templete>
       <Children :fatherName = 'fatherName'  @handle = 'fun' />
    templete>
    <script setup>
    export default {
      const fun = (num) =>{ console.log('接受子组件传递过来的值'+num );  }
      const fatherName = '李xx',
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    子组件 ( Children ) :
    <templete>
       <span> {{ state.name }} span>
       <span> {{ state.age  }} span>
       <span> {{ fatherName }} span>
    templete>
    <script setup>
        import { reactive } from 'vue';
        const state = reactive({name:'李四', age:20 });
        
        const { fatherName } = defineProps({     fatherName : { type : String },    }) ; // => 获取父组件  传递  子组件的参数
        const emit = defineEmit(['handle']);
        emit('handle', 123 )     // => 子组件 委托 父组件 调用方法接受参数
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.组件通信
    1. 除了 【1】中 提到的嵌入 setup 形参 的 propsemit 还有 :
    2. proxy.$parent【直接获取,或调用父组件方法】和 ref 【直接获取、或调用子组件方法】;

    注意事项 : proxy.$parent ref 只能 获取和调用 子或父 setup函数 return 中的 。

    <templete>
    	<Children  ref = 'children' />
    templete>
    <script>
    import { getCurrentInstance , ref } from 'vue';
    export default {
        setup(){
        	const { proxy } = getCurrentInstance();
        	console.log( proxy.$parent );  // => 获取父组件的 return 中的所有对象 .
    		
    		const children = ref(null);  
    		// 变量名要和组件的ref值 对应  , 赋值必须为 ref(null) .
    		console.log(children.value);  //=> 获取父组件的 return 中的所有对象 .
        }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3 . 也可以通过 provide 、inject 传递方法和变量 ;

    注意事项:provide 、inject 一个接收一个传递 ,只能从 父 => 子 => 孙 => … ( 从上往下单向传递

    ①父组件:   
        import { provide } from 'vue'
        export default {
    		setup(){
    		 	const name = '张三'
    		 	provide('name ', name )    //向下传递
    		 	return  { name }
    		}
    	}
    -------------------------------------------------------------
    ②子组件:
        import { inject } from 'vue'
    	export default {
    		setup(){
    			const getName = inject( 'name');  //接收父传递下来的
    			return  { getName }
    		}
    	}	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 组后也可以通过 全局状态管理库 ( vuex 和 pinia ) ,组件都可以同时使用其中的属性和方法 ,阶段③中会讲到 。

    3.生命周期
    vue3与vue2相比较3处修改
    • beforcreate 和 create 直接写到 setup 函数中
    • 所有钩子函数前加 ‘on’ 如 :mounted => onMounted
    • destory 更名为 onUnmounted
    • 引入方式 (vue3 都是模块化) 如 : import { onMounted } from ‘vue’
    vue2vue3详情
    beforcreate
    create
    setup( )new Vue (开始→结束)
    beforeMounted
    Mounted
    onBeforeMounted
    onMounted
    模板、DOM渲染 (开始→结束)
    beforeUpdate
    update
    onBeforeUpdate
    onUpdate
    数据发生更新 (开始→结束)
    beforeDestory
    destory
    onBeforeUnmount
    onUnmounted
    离开当前组件 (开始→结束)

    4.数据响应 (ref 、reactive)

    ref 对象 (一般用于基本数据类型 )

    • js中 修改和获取 ref 对象的值 需要加 value属性 ,在html中则不需要
    • html 中 是通过判断ref 对象的 __V_isRef 是否需要加 .value的 。
    ......
    <template>
    	<p> 姓名 : {{ name }} </p>
    </template>
    ......
    setup(){
     	const name = ref('张三')
     	name.value = '李四';  
     	return {name}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • ref 若是 基本类型 是通过 vue2 同样的 Object.defineProperty 对象进行数据响应 。
    • ref 若是 应用类型 则 “求助” => 内部转换为 reactive 采用 proxy 对象进行数据响应。
     - ref('123')   =>     采用 Object.defineProperty() 进行响应 
    
     - ref( {name:'123'} )  =>   reactive( value:{ name : 123} )  => 采用 proxy 进行响应 
    
    • 1
    • 2
    • 3

    reactive 对象 (一般用于引用数据类型 )

    • reactive 采用 proxy 对象,省却了 Object.defineProperty 的 key 的遍历 ,从而有于 应引用数据类型 有更快的响应速度 ;
    • reactive 在监听数组时不要直接创建数组就行修改

    错误写法:点击按钮,执行方法后 ,页面数据无法响应

     let  arr =	reactive([1,2]);
     function changeArr(){
     	arr = ['3','4','5'];
     }  
    
    • 1
    • 2
    • 3
    • 4

    正确写法:

     let  arr =	reactive({ val:[1, 2] });
     function changeArr(){
     	arr.val = ['3', '4', '5'];
     }  
    
    • 1
    • 2
    • 3
    • 4
    • reactive 的 proxy原理 :
     const proxyObj = new Proxy({ name:'123' }, {
    	get:(obj,key,receiver)=>{    
    		  return Reflect.get(obj, key,receiver) 
    	},   
    	set:(obj,key,value,receiver) => { 
             return Reflect.set(obj, key, value, receiver) 
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    详细了解vue2和vue3数据响应的区别 => 点击这里

    • reactive 大多是和 toRefs 配合使用 从而让模板中变量的,更加简便。

    5.监听与计算属性 (watch、watchEffct… )
    • watch 和 computed区别
    区别watch (监听)computed(计算属性)
    资源消耗不走缓存 (消耗资源)默认走缓存 (消耗资源)
    异步支持异步不支持异步
    监听一对多 (或一对一)多对一 (或一对一)
    import {watch, computed, ref} from 'vue'
    ......
    setup(){
        const num = ref (1);
        watch(num,(newValue,oldValue)=>{  console.log(newValue); });  //num 改变就会执行
        //-------------------------------------------------------------------------
        const  a =  ref(2);
        const  b =  ref(2);
        let S = computed(()=>{return a.value * b.value }});  //a,b 一个改变就 重新计算 S
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • vue3 新增了 watchEffect , 它会监听 出现在 回调函数中 ref 和reactive对象,中有一个变化就会执行
    ...
      watchEffect(()=>{  console.log(a,b));  })   // 只监听ref 和 reactive 且出现在回调函数中的对象
    ...
    
    • 1
    • 2
    • 3

    6.Api 模块化
    • 需要什么导入什么 ,更加节约内存 如:
    import { onMounted , reactive} from 'vue';
    
    • 1

    阶段 ② 特殊组件的封装 (需要掌握)
    1. 组件递归 的应用
    • 组件递归 说到底就是 组件 import 组件本身 ,通过不断向下传参, 最后控制结束,达到组件封装的目的,
    • vue3 本组件 导入本组件 ,只需要添加个 name 属性 ,而 vue2 需要 多写一步 import 该组件

    实用场景 => 树形菜单 、流程图 、层级类组件 …
    以树形组件为例 :
    在这里插入图片描述

    • 首先要给该对象传递一个 具有层级关系对象 ,如: 我们需要根据 (是否有 children 去判断是否要在 递归 import 下去)
      1、使用这个treeList 组件:
    <tree-list :Arr="Arr" />
    ...
    const Arr = [
            {
              text: "菜单1",
              children: [
                {
                  text: "菜单1-1",
                  children: [{ text: "菜单1-1-1" }, { text: "菜单1-1-2" }],
                },
              ],
            },
            { id: "2", text: "菜单2" },
          ];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、编辑这个组件 => ( 通过 )

     <div v-for="item in Arr">
      	<p>{{ item.text }}</p>
        <tree-list
           v-if="item.children"
          :Arr="item.children"
          :index="index"
          :key="item.id"
        />
    </div>
    ...
    export default {
    	name: "tree-list",
    	props: {
           Arr: { type: Array }
      	},
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    详细的样式、代码 请参考 => 点击这里


    2.插槽 的应用
    • 插槽的作用 主要就是将组件中间的内容,插入到组件对应的位置 ,让组件更灵活。
    • 插槽分为 三种 (普通插槽, 具名插槽 , 作用域插槽)
    普通插槽具名插槽作用域插槽
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    slot 标签接收,默认插入的内容
    多写该标签只会重复插入
    v-slot =‘aa’ 可简写为 #aa
    插入位置和slot的name一 一对应
    通过props 传入 参数达到控制模板的目的
    3.标签传送 的使用
    • Teleport 标签主要是将 一块html 内容 传送到 对应的 标签内部 ;
    • Teleport 和 dom 的 appendChild 方法的区别,主要是操作的是 虚拟dom ( 主要在性能 方面 ) 。
    • 参数 to 对应插入的位置 填写对于css选择器
    • 参数 disabled 是否插入 该位置 ,true => 已插入、false => 未插入
    <template>
    	<div id="box">
    		<p> 标题 p> 
    		<button @click='appendContent'> 插入内容 button>
    		<p> 内容:<span id='content'> span>  <p/>
    	div>
    	
    	<Teleport to="#content"  :disabled = "bool">
    		teleport 插入到 改标签 中.
    	Teleport>
    template>
    <script>
    ...
    const bool = ref(false);
    const appendContent = ()=>{ bool.value = true }
    return { bool, appendContent }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    阶段 ③ 全局状态管理 (需要掌握)
    1.vuex 4.0

    ①.下载和导入

     npm  i  vuex   --save
     ---------------------------
     import  Vuex  from  'vuex'
     Vue.use(Vuex);
    
    • 1
    • 2
    • 3
    • 4

    ②.创建store仓库: /store/index.js

    import { createStore } from 'vuex';
    export default createStore({
    state: {name: 123 },
    mutations:{    getname(state,newVal){this.state.name=newVal;}  }, 
    //同步方法:(只有mutations才能改state的值)
    actions:{   getnameAsync(){ ... }     },  //异步方法
    geeter:{},  //相当于计算属性
    modules: {}  //将vuex分块
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ③.简单使用

    import {useStore} from 'vuex'
    export default {
    	setup(){
    		const store  = useStore();
    		console.log(store.state.name);    //获取
    		store.commit("getname", 12345);  //修改
    		store.dispatch("getnameAsync", 12345);  //修改
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ④.需要了解与 vue2 的差别 => 点击这里

    2.pinia (基于vuex5.0 理念)
    • 相比vuex ,pinia 更具有优势
    pinavuex
    pinia 同时支持vue2和vue3vue2要用vuex 3 版本
    vue3要用vuex 4 版本
    不分同步异步,更好的ts支持分同步异步,不太兼容ts
    享受自动补全
    需要注入,导入函数,调用他们

    ① 安装

    npm install pinia
    
    • 1

    在 main.js 中 加入

    import { createApp } from 'vue'
    import App from './App.vue'
    
    import { createPinia } from 'pinia'  //导入pinia
    const  pinia = createPinia();        //调用创建pinia
    
    createApp(App)
    			.use(pinia)
    			.mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ③去创建 pinia 仓库
    一般选在 /src下的 store 文件夹下 例:创建为 pinia.js

    import { defineStore } from 'pinia'
    
    export const PiniaStore = defineStore('main',{  //导出 pinia仓库
        state:() => { //相当于全局的 data()
            return {
                name:'张三',
                age:18
            }
        },
        getters:{},  //相当于全局的computed
        actions:{}   //相当于全局methods
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ③使用 (非常容易)
    以/src/view/index.vue 为例:

    <template>
        <h3>{{pinia.name}}</h3>  <!--使用-->
        <h3>{{pinia.age}}</h3>
        <button @click="pinia.age++">修改pinia数据</button>   <!--修改-->
    </template>
    <script setup>
        import { PiniaStore } from '../../store/pinia'
        const pinia = PiniaStore();
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    阶段 ④ 框架的搭建 (需要掌握)
    1.vue3路由、及与vue2的区别

    创建路由

    import { createRouter, createWebHistory } from 'vue-router'
    const routerHistory = createWebHistory()
     const router = createRouter({
        history: routerHistory,
        routes: []
    })
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.全局组件 批量导入
    • 批量导入
    • 主文件Main.js
     import toMore from './components/toMore '
     createApp(App)
                 .use(toMore)
                 .mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • components 文件夹下创建 导入文件 如 : toMore.js
     let toMore=  () => {
          install(app) {
               const com = import.meta.globEager("./*.vue");
                for (const path in com ) {
                    app.component( com[path].name, com[path].default);
                }}
         };
     export default toMore;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    阶段 ⑤ 3.x性能优化
    非递归监听 (优化reactive 、ref)
    • 当数据量非常大时,可以考虑用 shallowRefshallowReactive 去替代 reactive 、ref
    • shallowRef 只监听 .value的变化 ,如果value 是对象下面还有层数则不监听;
    • shallowReactive 只 监听 第一层的变化。
    • 可以多次修改 通过 triggerRef方法 去多次修改后去更新一次页面 如 triggerRe(obj)
  • 相关阅读:
    93. 复原 IP 地址(力扣LeetCode)
    Rn开发社区推荐组件
    Nvidia Jetson/Orin +FPGA+AI大算力边缘计算盒子:潍柴雷沃智慧农业无人驾驶
    详解:-bash: nohup:: command not found​​​​
    以梦为马,不负韶华|电巢科技&延安大学飞鹰计划实习班精彩回顾
    Vue 2与Vue 3生命周期钩子的对比分析
    【DM8】达梦8 DEM部署
    人生苦短,我用python
    如何在three.js中画3D圆弧及半圆弧组成圆
    Git代码回归到指定commit
  • 原文地址:https://blog.csdn.net/weixin_42232622/article/details/125961836