Ⅰ. vue3 简介

vue3 代号 onepiece 【海贼王】

Ⅱ. 优化内容

  • 更新渲染 快了 1 ~ 2 倍之间 ;
  • 首次渲染 快了 50% 左右 ;
  • 运行内存 减少 50% 左右 ;
  • 打包内存 减少 40% 左右 ;
  • 更强的 ts (typescript) 支持 => 引用下作者 ( 尤雨溪 ) 的原话 ;
  • 逻辑代码模块化 composition Api => 如图: ( 逻辑变的不再零散 )
    vue3 看完这就够搬砖了-LMLPHP

Ⅲ. 源码优化

Ⅳ. 项目创建

  • vue3的项目创建有2种方式 => 更加推荐 Vite
  • 使用脚手架创建要首先安装 vue脚手架 ,项目启动速度没有 Vite 快捷

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

阶段 ① 简单的页面编写 (需要掌握)
1.setup 函数 与 setup单文组件

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

<templete> <!-- vue3 可以不用,单个盒子包裹起来 -->
   <Children :fatherName = 'fatherName'  @handle = 'fun' />
</templete>
<script>
export default {
  setup(){
	function fun(num){ console.log('接受子组件传递过来的值'+num ); }
    return {
	  fun,
	  fatherName: '张xx',
    }
  }
}
</script>
<templete>  <!-- vue3 可以不用,单个盒子包裹起来 -->
   <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>

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

<templete>
   <Children :fatherName = 'fatherName'  @handle = 'fun' />
</templete>
<script setup>
export default {
  const fun = (num) =>{ console.log('接受子组件传递过来的值'+num );  }
  const fatherName = '李xx',
}
</script>
<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>

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

3 . 也可以通过 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 }
		}
	}
  • 组后也可以通过 全局状态管理库 ( vuex 和 pinia ) ,组件都可以同时使用其中的属性和方法 ,阶段③中会讲到 。

3.生命周期
  • beforcreate 和 create 直接写到 setup 函数中
  • 所有钩子函数前加 ‘on’ 如 :mounted => onMounted
  • destory 更名为 onUnmounted
  • 引入方式 (vue3 都是模块化) 如 : import { onMounted } from ‘vue’

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

 - ref( {name:'123'} )  =>   reactive( value:{ name : 123} )  => 采用 proxy 进行响应
  • reactive 采用 proxy 对象,省却了 Object.defineProperty 的 key 的遍历 ,从而有于 应引用数据类型 有更快的响应速度 ;
  • reactive 在监听数组时不要直接创建数组就行修改

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

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

正确写法:

 let  arr =	reactive({ val:[1, 2] });
 function changeArr(){
 	arr.val = ['3', '4', '5'];
 }
  • 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)
    }
})

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

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

5.监听与计算属性 (watch、watchEffct… )
  • 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
}

  • vue3 新增了 watchEffect , 它会监听 出现在 回调函数中 ref 和reactive对象,中有一个变化就会执行
...
  watchEffect(()=>{  console.log(a,b));  })   // 只监听ref 和 reactive 且出现在回调函数中的对象
...

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

阶段 ② 特殊组件的封装 (需要掌握)
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" },
      ];

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 }
  	},
}

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


2.插槽 的应用
  • 插槽的作用 主要就是将组件中间的内容,插入到组件对应的位置 ,让组件更灵活。
  • 插槽分为 三种 (普通插槽, 具名插槽 , 作用域插槽)
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.vuex 4.0

①.下载和导入

 npm  i  vuex   --save
 ---------------------------
 import  Vuex  from  'vuex'
 Vue.use(Vuex);

②.创建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分块
})

③.简单使用

import {useStore} from 'vuex'
export default {
	setup(){
		const store  = useStore();
		console.log(store.state.name);    //获取
		store.commit("getname", 12345);  //修改
		store.dispatch("getnameAsync", 12345);  //修改
	}
}

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

2.pinia (基于vuex5.0 理念)
  • 相比vuex ,pinia 更具有优势

① 安装

npm install pinia

②在 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')

③去创建 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
})

③使用 (非常容易)
以/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.vue3路由、及与vue2的区别

创建路由

import { createRouter, createWebHistory } from 'vue-router'
const routerHistory = createWebHistory()
 const router = createRouter({
    history: routerHistory,
    routes: []
})
export default router
2.全局组件 批量导入
  • 批量导入
  • 主文件Main.js
 import toMore from './components/toMore '
 createApp(App)
             .use(toMore)
             .mount('#app')
  • 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;
阶段 ⑤ 3.x性能优化
非递归监听 (优化reactive 、ref)
  • 当数据量非常大时,可以考虑用 shallowRef、shallowReactive 去替代 reactive 、ref
  • shallowRef 只监听 .value的变化 ,如果value 是对象下面还有层数则不监听;
  • shallowReactive 只 监听 第一层的变化。
  • 可以多次修改 通过 triggerRef方法 去多次修改后去更新一次页面 如 triggerRe(obj)
07-28 10:44