# 学习VEU3 **Repository Path**: ancientelement/learning---veu3 ## Basic Information - **Project Name**: 学习VEU3 - **Description**: 学习VEU3学习VEU3学习VEU3 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-12-15 - **Last Updated**: 2022-12-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## VUE3 ONE-PIECE 张天禹 ## 1.初识VUE3 ### 1.使用vite创建vue3工程 ```js npm init vite-app vue3_test_vite ``` * vite 启动速度快* ### 2.工程结构 > ==VUE3==不再引入vue构造函数而是 引入一个 ==createapp== 的==工厂函数== ```js import { createApp } from 'vue' import App from './App.vue' //创建app实例对象类似于vm 但是app比vm更加轻 //mount 挂载 unmount 卸载 ceateApp(App).mount('#app') ``` > 原==VUE2== ```js import Vue from 'vue' new Vue({ render: (h)=> {return h('App')} }).$mount('#app') ``` ### 3.templete标签 > 可以直接 写标签不用再在 templete 里包 div ```html ``` ## 2.常用的composition API (组合式API) > compositionAPI 太抽象了 兄弟们 ### 1.拉开序幕的setup > vue3 的新配置项 ==setup== 是所有的 composition API 表演的舞台 > > vue3 中所有的: data method computed 均要配置到==setup==里 > setup 的==返回值==可以直接在 模板 、方法 中调用 > > setup 可以返回一个 渲染函数 ```js setup() { let name = 'zs'; let age = 12; function showName() { alert(name) } return { //setup的返回值 name, age, showName } } ``` > 注意 : 1. 不要将 vue2 与 vue3 混用,当两者发生冲突是会以vue3为主 > > ​ 2.setup 不能是一个 async 函数,因为其返回值不是==return里的对象==而是一个==promise== ### 2.ref函数 > 定义一个相应式数据 #### 1.==普通数据== 与 ==响应式== 数据 傻缺儿子: 非相应式数据 ```js setup() { let name = 'zs'; let age = 12; } ``` 响应式数据: ```js setup() { let name = ref('zs'); let age = ref(12); } ``` > ref 加工后的数据变为: ref把数据包转成对象的形式 ```js RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 'zs', _value: 'zs'} RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 12, _value: 12} ``` > 从单词层面理解ref : RefImpl = reference + implement ==引用实现对象== 简称 ==引用对象== > > ​ 引用 实现 通过objectpropoty 与 getter、setter 实现 > 下面让我们打开 ==RefImpl== 进一步观察 ```js RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 'zs', _value: 'zs'} |----dep: Set(1) {ReactiveEffect} |----__v_isRef: true |----__v_isShallow: false |----_rawValue: "zs" |----_value: "zs" |----value: (...) |----[[Prototype]]: Object |----constructor: class RefImpl |----value: (...) |----get value: ƒ value() |----set value: ƒ value(newVal) |----[[Prototype]]: Object ``` > 这里我们看到了 getter 与 setter 可以确定 ref 是通过==数据劫持==来实现响应式 现在我们知道要改变页面中的数据必须使用 ==<数据>.value== 的形式 但模板中使用数据 不需要 ==<数据>.value== 因为vue已经帮你用了.value #### 2. ref包裹对象 ```js setup() { let name = ref("zs"); let age = ref(12); let job = ref({ type: '前端工程师', salary: '2K' }) function showName() { // alert(name) console.log(name,age,job.value); } ``` > 可以看到输出的==job.value== ```js Proxy {type: '前端工程师', salary: '2K'} |----[[Handler]]: Object |----[[Target]]: Object |----[[IsRevoked]]: false ``` > 他并没用将 job 对象中的每个 属性都用 ref 包裹而是用了 ==Proxy== 的形式,并且我们使用 job 中的属性只要 `job.value.type` 不要 `job.value.type.value` 因为元素不在用 refimpl 包裹 具体要如何 用到 proxy 我们看到下一小节 ### 3.reactive函数 > 定义一个 ==对象类型== 的响应式数据 ref 即可 用到 ==对象类型== 又可用到 ==普通类型== > 那么问题来了: 为什么还用reactive呢? ```js let job1 = reactive({ type: "前端工程师", salary: "2K", }); ``` 我们可以在定义数据时做这样的处理 : ```js let person = reactive({ name: "zs", age: 10, job: { type: "前端工程师", salary: "10k", }, }); ``` 于是在修改数据时我们就要这样处理 : ```js function showName() { // alert(name) person.name = "张三", person.age = 100, person.job.type = "UI", person.job.salary = "20K"; console.log(person); } ``` 于是我们就不用在person.value.<属性>了 确实在这里我还有一些疑惑(这也没方便多少呀?) 但是老师说了 后面会解决 ### 4.vue3里的响应式原理 #### 1.下回顾一下vue2 里的响应式原理 : > 对象类型 通过`definePropoty()`对属性的读取、修改进行拦截(数据劫持) > > 数组类型 通过重写数组的方法进行拦截 > > 总而言之就是要调用 对象上 的方法使其可以检测到数据的改变 ```js this.$set() 使得修改数据得到检测 其实还有Vue.set() 同时还有我们没讲到的 $delete 检测删除 ``` 修改数组 ```js 也可以用$set(<数组>,<索引>,<值>) 或者<数组>.splice(开始的索引,删除几个,值) ``` > 存在问题 :删除或者新增元素不会更新 #### 2.vue3的响应式原理 : git强推 直接覆盖远程仓库: ```js 方法二:强推 即利用强覆盖方式用你本地的代码替代git仓库内的内容 git push -f origin master ``` ##### 模拟vue2 ```html ``` > 我们可以看到在这里 无法用 delete 和 添加属性 > 小知识 //#region //#endregion 强制折叠 ##### 模拟vue3 ```js //模拟vue3响应式 const p = new Proxy(person, { get(target, propName) { console.log(`有人读取了p身上的${propName}属性`); return target[propName] }, set(target, propName, value) { console.log(`有人修改了p上的${propName}属性`); target[propName] = value }, delete(target, propName) { console.log(`有人删除了p上的${propName}属性`); delete target[propName] } }) ``` 下面再用介绍一下 reflexct reflect与Object有着差不多的操作 reflect有返回值 如果要reflect进行错误处理跟加方便 而Objcet要进行try catch包裹 ```js Reflect.defineProperty(p, 'name', { get() { console.log('有人读取了name'); return person.name }, set(value) { console.log('@@@有人修改了name'); person.name = value } }); Reflect.defineProperty(p, 'age', { get() { console.log('有人读取了age'); return person.age }, set(value) { console.log('@@@有人修改了age'); person.age = value } }) ``` reflect 模拟 ```js 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) //返回Reflect.deleteProperty(target, propName)的结果否者返回undefind } }) ``` 用reflet 可以返回成功或者失败 也就可以更方便的进行 错误处理 ### 5.对比ref与reactive ref: 定义基本数据类型 reactive: 对象或数组 随让ref都可以用但是 用reactive 更加方便 ### 6.setup两个注意点 > setup 在 ==beforeCreate== 之前执行 > > 所以在setup 里使用 this 是undefined > setup的参数: * props:值为对象包含外部传进的属性且组件要声明接受 * `props: ["msg", "school"],` * context: 上下文* * attrs: 值为对象,包含外部组件传进来的值 与$attrs一致,也就是没用申明接收的参数 * slots: 收到的插槽的具体内容 this.$slorts * emit: 分发自定义事件的函数 this.$emit ,同样也要申明接受 * `emits: ["hello"]` App.vue ```html ``` Demo.vue ```js export default { name: "Demo", props: ["msg", "school"], emits: ["hello"], setup(props, context) { console.log("--props--", props); console.log("--context--", context.slots); let test = function () { context.emit("hello", 777); }; return { test }; }, }; ``` ### 7.组合式api computed 简单易懂 ```js setup(props, context) { let person = reactive({ firstName: "张", lastName: "三", }); let fullName = computed({ get() { return person.firstName + "-" + person.lastName; }, set(value) { console.log(value); const nameArr = value.split("-"); person.firstName = nameArr[0]; person.lastName = nameArr[1]; }, }); //... return { test, person, fullName }; }, ``` ### 8.组合式api watch 回顾一下vue2 ==watch== ```js watch: { sum: { immediate: true, //立即监视 deep: true, //深层监视 handler(newValue, oldValue) { console.log(newValue, oldValue); }, }, }, ``` > 老师说这里有坑 > //普通watch ```js // watch(sum, (nv, ov) => { // console.log(nv, ov); // }); ``` > //多个 ```js // watch([() => person.name, () => person.msg], (nv, ov) => { // console.log(nv, ov); // }); ``` > //监视一个reactive //此时oldValue不奏效 deep也不起作用 ```js // watch(person, (nv, ov) => { // console.log(nv, ov); // }); //此时oldValue不奏效 deep也不起作用 ``` > // 监视一个reactive里的对象 需要deep ```js watch( () => person.job, (nv, ov) => { console.log(nv, ov); }, { deep: true } ); ``` ### 9.watch的两个小问题 如果我们将 person 用ref处理 ```js let person = ref({ name: "张三", msg: "dass", job: { job1: { salary: 20, }, }, }); let sum = ref(0); let msg = ref("你好"); ``` > 在监视一个普通数据类型用ref包裹的时候 不能用 ==<属性>.value== > > 如果用ref包裹一个对象 要用 ==<属性>.value== 因为 ==<属性>.value== 才是一个proxy代理对象 ```js //两个小问题 watch(sum, (nv, ov) => { console.log(nv, ov); }); //这里不能写sum.value sum.value是一个字符串 watch(person.value, (nv, ov) => { console.log(nv, ov); }); //这里要用person.value 应为person.value才是一个proxy代理对象 person是一个refimpl包裹的对象 return { sum, person }; ``` ​ ### 10.watchEffect > 回调里用到了谁就监视谁 ```js watchEffect(() => { const x1 = person.msg; const x2 = person.job.job1.salary; const x3 = person; console.log("watchEffect执行了"); }) return { sum, person }; ``` 如上代码 用到谁就监视谁 但是无法深度监视 > `const x3 = person;`直接写在watchEffect里不能监视person里的数据变化 ### 11.Vue3生命周期 ![](./02e3b550-2d3b-11eb-8ac2-41a88e51bce8.png) > 不同之处 * 对应关系 : 1. beforeCreate ===> setup() 2. created ===> setup() 3. beforeMount ===> onBeforeMount() 4. mounted ===> onMounted() 5. berforeUpdate ===> onBeforeUpdate() 6. update ===> onUpdate() 7. berforeUnmount ===> OnBeforeUnmuont() 8. unmount ===> OnUnmount() ​ ### 12.自定义HOOK函数 本质上是一个函数把setup函数中使用的组合式api进行封装。 > 类似于mixin > 这里开始体会到组合式api的思想了 来我们看一下文档结构 ```js |----src |----coponents |----Demo.vue |----hooks |---usePoint.js ``` 这里我们可以看到 hook 函数写在hooks里 |---usePoint.js ```js import { onMounted, onUnmounted, reactive } from "vue"; export default () => { let point = reactive({ X: 0, Y: 0, }); let savePoint = (e) => { point.X = e.pageX; point.Y = e.pageY; console.log(e.pageX, e.pageY); }; onMounted(() => { window.addEventListener("click", savePoint); }); onUnmounted(() => { window.removeEventListener("click", savePoint); }); return point } ``` |----Demo.vue ```js setup(props, context) { let sum = ref(0); let point = usePoint(); console.log('dawdaw',point); return { sum, point }; }, ``` > 所以 可以看出为什么叫做 ==组合式API== 把api组合在一个文件里提高服用率 ### 13.toRef与toRefs > 句句话不提指针 句句话都是指针 > toRef()创建一个ref对象其value值指向另一个ref对象 ```js toRef(person,'name'); toRef(person.job.job1,'salary') ``` > toRef(<对象>,<对象里的某个属性>) > toRefs(<对象>) ```js export default { setup(props, context) { let person = reactive({ name: "张三", msg: "前端", job: { job1: { salary: 20, }, }, }); return { person,...toRefs(person) }; //此toRefs创造的对象是和person关联的 }, }; ``` > //此toRefs创造的对象是和person关联的 ## 3.其他组合式API ### 1.shallowRef与shallowReactive > 顾名思义 ==浅层的== Ref 和 reactive 只考虑第一层的响应式 > 里面的属性没用proxy了 > 如果 只要用第一层 或者 以后不要修改对象中的属性 只要替换 就用==shallow==行 ### 2.readonly()与 shallowReadonly > ==只读== 与 ==浅层只读== ```js person = readonly(person) ``` > 可对 ref 与 reactive 都可限制 > 那么readonly的意义何在: > > 当接受别人要求不可更改的数据时加上readonly ### 3.toRaw 与 markToRaw > toRaw 响应式的数据变为普通数据 > > toRaw 只可处理 ==reactive== > > 读取 响应式对象 中的 普通对象 ```js const p = toRaw(person) ``` > markToRaw 标记一个对象使其永远无法成为一个响应式对象 > > 有些值不适合设置为一个响应式对象 > > 渲染具有不可变的数据源大表格时 浪费性能 ```js let car = {name: '奔驰',price: 12132121} person.car = markRaw(car) ``` ### 4.customerRef 自定义ref > customRef(track, trigger) 两个参数 > > track: 追踪 //通知vue追踪value的变化 在get里使用 > > trigger: 触发 //通知vue更新模板 在set里使用 以下为一个模板: ```js setup() { function myRef(value,delay ) { let timer; return customRef((track, trigger) => { return { get() { console.log(`我读取了 修改值为: ${value}`); track(); //通知vue追踪value的变化 return value; }, set(newValue) { console.log(`我修改了 修改值为: ${value}`); clearTimeout(timer); timer = setTimeout(() => { value = newValue; trigger(); //通知vue更新模板 }, delay); }, }; }); } let keyWord = myRef("wda",500); return { keyWord }; }, ``` ### 5.provide 与inject > 祖孙 组件间通信方式 祖组件 ```js setup{ .......... let car = reactive({name: '奔驰',price: '30K'}); provide('car',car); .......... } ``` 子组件 ```js setup{ .......... let car = inject('car'); return {car} } ``` ### 6.响应式数据的判断 * isRef() * isReactive() * isReadonly() * isProxy() 检查一个对象是否由reactive()或readonly()方法创建的代理 ## 4.组合式API的对比 ### 1.option API 的问题 传统option API 流程分离 要在 data methods computed 修改 ### 2.composition API的优势 让我们 相关的代码流程连在一起

001.jpg

002.jpg
## 5.其他组件 ### 1.Fragment > 虚拟根标签 ### 2.teleport 传送 > 将一个嵌套的结构放在 根组件下 ### 3.Suspense > App.vue ```js // import Child from "./components/Child.vue"; //静态引入 import {defineAsyncComponent } from 'vue' const Child = defineAsyncComponent(() => import('./components/Child.vue')) //动态引入 ``` > Child.vue ```js setup() { let sum = ref(0); return new Promise((reslove, reject) => { setTimeout(() => { reslove({ sum }); }, 2000); }); // return {sum} }, ``` > 这样实现了 一个异步引入 组件 ### 4.vue3中的其他改变 | 2.x 全局 API(Vue) | 3.x 实例 API (app) | | ------------------------ | --------------------------- | | Vue.config.xxxx | app.config.xxxx | | Vue.config.productionTip | 移除 | | Vue.component | app.component | | Vue.directive | app.directive | | Vue.mixin | app.mixin | | Vue.use | app.use | | Vue.prototype | app.config.globalProperties | > 移除keyCode 修饰符 > 移除v-on.native > 移除filter 用计算属性代替