# 学习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
dsada
dadww
```
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生命周期

> 不同之处
* 对应关系 :
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 用计算属性代替