共计 6126 个字符,预计需要花费 16 分钟才能阅读完成。
# 概述
- ref toRef toRefs 进阶,深刻了解
- vue3 setup
- Vue3 为何比 Vue2 快?
- 怎么了解 Vue 的单向数据流?
- Vue 中事件绑定原理
- vue3 mitt 应用
ref toRef toRefs 进阶,深刻了解
-
为何须要 ref?
- 返回值类型,会失落响应式
- setup、computed、合成函数,都有可能返回值类型
- Vue 如果不定义 ref,用户将自定义 ref,反而凌乱
-
为何须要.value?
- ref 是一个对象(不失落响应式),value 存储值
- 通过.value 属性的 get 和 set 实现响应式
- 用于模板、reactive 时,不须要.value,其余状况都须要
-
为何须要 toRef toRefs
- 初衷:不失落响应式的状况下,把对象数据进行合成和扩散
- 前提:针对的事响应式对象,不是一般对象
- 留神:不发明响应式,而是连续响应式
更多精彩内容,请 微信搜寻“前端爱好者
“, 戳我 查看。
vue3 setup
script setup 是干啥的?
scrtpt setup 是 vue3 的语法糖,简化了组合式 API 的写法,并且运行性能更好。
应用 script setup 语法糖的特点:
- 属性和办法无需返回,能够间接应用。
- 引入组件的时候,会主动注册,无需通过 components 手动注册。
- 应用 defineProps 接管父组件传递的值。
- useAttrs 获取属性,useSlots 获取插槽,defineEmits 获取自定义事件。
- 默认不会对外裸露任何属性,如果有须要可应用 defineExpose。
setup 中如何获取组件实例?
- setup 和其余 Composition API 中都没有 this
- 在 Options API 中依然能够应用 this
- Composition API 中能够应用 getCurrentInstance 办法获取
vue3 getCurrentInstance
Vue2 中,能够通过 this 来获取以后组件实例;
Vue3 中,在 setup 中无奈通过 this 获取组件实例,console.log(this)打印进去的值是 undefined。
在 Vue3 中,getCurrentInstance()能够用来获取以后组件实例 vue3 官网文档解释
let {proxy} = getCurrentInstance();
在 setup 中别离打印上面 3 个值,后果如下:
console.log(getCurrentInstance,typeof(getCurrentInstance));
console.log(getCurrentInstance(),typeof(getCurrentInstance()));
console.log(proxy,typeof(proxy));
后果
能够看到:
- getCurrentInstance 是一个 function 办法,
- getCurrentInstance()是一个对象,proxy 也是一个对象。
- proxy 是 getCurrentInstance()对象中的一个属性,通过对象的解构赋值形式拿到 proxy。
getCurrentInstance 只能在 setup
或生命周期钩子
中应用。
在 onMunted 生命周期中打印 getCurrentInstance
定义一个 test 办法,通过 click 事件触发办法
onMounted(() => {console.log(getCurrentInstance(), typeof getCurrentInstance());
});
function test() {console.log(getCurrentInstance(), typeof getCurrentInstance());
}
能够看到在 function 中是无奈获取该实例的。
let {ctx} = getCurrentInstance();
console.log(ctx, typeof ctx);
let {proxy} = getCurrentInstance();
console.log(proxy, typeof proxy);
ctx 和 proxy 都是 getCurrentInstance()对象中的属性,通过解构赋值的形式拿到。能够看到,2 者有所区别。
ctx 是一般对象,proxy 是 Proxy 对象。
补充:Vue3 中对于 getCurrentInstance 的大坑
开发中只实用于调试!不要用于线上环境,否则会有问题!
解决方案:
计划 1.
获取挂载到全局中的办法
const instance = getCurrentInstance()
console.log(instance.appContext.config.globalProperties)
计划 2.
应用 proxy 线上也不会呈现问题
const {proxy} = getCurrentInstance()
Vue3 为何比 Vue2 快?
- Proxy 实现响应式
-
patchFlag https://vue-next-template-explorer.netlify.app/
- 编译模板时,动静节点做标记
- 标记,分为不同的类型,如 TEXT,PROPS
- diff 时,辨别动态节点和不同类型的动静节点
-
hoistStatic
- 将动态节点的定义,晋升到父作用域,缓存起来,空间换工夫
- 多个相邻的动态节点,会被合并起来,编译优化
- cacheHandler
缓存事件 - SSR 优化
动态节点间接输入为 dom,绕过 vdom - tree-shaking
编译时,按需引入 API
怎么了解 Vue 的单向数据流?
所有的 prop 都使得其父子 prop 之间造成了 一个 单向上行
绑定:
父级 prop 的更新会向下流动到子组件中,然而反过来则不行。这样会避免从子组件意外扭转父级组件的状态,从而导致你的利用的数据流向难以了解。
额定的,每次父级组件产生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件外部扭转 prop。
如果你这样做了,Vue 会在浏览器的控制台中收回正告。子组件想批改时,只能通过 $emit 派发一个自定义事件,父组件接管到后,由父组件批改。
有两种常⻅的试图扭转一个 prop 的情景 :
这个 prop 用来 传递一个初始值
;这个子组件接下来 心愿将其作为一个本地的 prop 数据来应用
。
在这种状况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {counter: this.initialCounter}
}
这个 prop 以一种 原始的值传入且须要进行转换
。
在这种状况下,最好应用这个 prop 的值来定义 一个计算属性
props: ['size'],
computed: {normalizedSize: function () {return this.size.trim().toLowerCase()}
}
Vue 中事件绑定原理
$emit
vm.$emit('事件名称', 可选参数)
触发以后实例上的事件,要传递的数据会传给监听器;
$on
vm.$on('事件名称', callback) callback 回调 emit 要传送的数据
监听以后实例上自定义事件;
$off
vm.$off([event, callback] )
移除自定义事件监听器。
- 如果没有提供参数,则移除所有的事件监听器;
- 如果只提供了事件,则移除该事件所有的监听器;
- 如果同时提供了事件与回调,则只移除这个回调的监听器。
vue2 应用例子
// 父组件
<template>
<ratingselect @select-type="onSelectType"></ratingselect>
</template>
<script>
data () {
return {selectType: 0,},
methods: {onSelectType (type) {this.selectType = type}
}
</script>
父组件应用 @select-type=”onSelectType”@就是 v -on 的简写,监听由子组件 vm.$emit 触发的事件,通过 onSelectType()承受从子组件传递过去的数据,告诉父组件数据扭转了。
// 子组件
<template>
<div>
<span @click="select(0, $event)" :class="{'active': selectType===0}"></span>
<span @click="select(1, $event)" :class="{'active': selectType===1}"></span>
<span @click="select(2, $event)" :class="{'active': selectType===2}"></span>
</div>
</template>
<script>
data () {
return {selectType: 0,},
methods: {select (type, event) {
this.selectType = type
this.$emit('select-type', type)
}
}
</script>
子组件通过 $emit 来触发事件,将参数传递进来。
vue3 应用例子 — Vue3.x 举荐应用内部库 mitt 来代替 $on $emit $off
mitt 源码应用的是 typescript 编写的,源码加正文一共不到 90 行,浏览起来比拟轻松。
typescript 不是本次的重点,所以将 mitt 源码以 js 的模式展现如下。
/**
* 向外裸露的默认函数
* @param 入参为 EventHandlerMap 对象(ts 真香,咱们能分明的晓得参数的类型是什么,返回值是什么)* @returns 返回一个对象,对象蕴含属性 all,办法 on,off,emit
*/
export default function mitt (all) {
/*
此处实参可传一个 EventHandlerMap 对象,实现多个 mitt 的合并。例如:const m1 = mitt();
m1.on('hi', () => {console.log('Hi, I am belongs to m1.'); });
const m2 = mitt(m1.all);
m2.emit('hi') // Hi, I am belongs to m1.
m2.on('hello', () => {console.log('Hello, I am belongs to m2.'); });
m1.emit('hello'); // Hello, I am belongs to m2.
m1.all === m2.all // true
*/
all = all || new Map();
return {
// 事件键值对映射对象
all,
/**
* 注册一个命名的事件处理
* @param type 事件名,官网示意事件名如是 *,用来标记为通用事件,调用任何事件,都会触发命名为 * 的事件
* @param handler 事件处理函数
*/
on (type, handler) {
// 依据 type 去查找事件
const handlers = all.get(type);
// 如果找到有雷同的事件,则持续增加,Array.prototype.push 返回值为增加后的新长度,const added = handlers && handlers.push(handler);
// 如果已增加了 type 事件,则不再执行 set 操作
if (!added) {all.set(type, [handler]); // 留神此处值是数组类型,能够增加多个雷同的事件
}
},
/**
* 移除指定的事件处理
* @param type 事件名,和第二个参数一起用来移除指定的事件,* @param handler 事件处理函数
*/
off (type, handler) {
// 依据 type 去查找事件
const handlers = all.get(type);
// 如果找到则进行删除操作
if (handlers) {// 这里用了个骚操作,其实就是找到了,则删除(多个雷同的只会删除找到的第一个),没找到则不会对原数组有任何影响
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
},
/**
* 触发所有 type 事件,如果有 type 为 * 的事件,则最初会执行。* @param type 事件名
* @param evt 传递给处理函数的参数
*/
emit (type, evt) {
// 找到 type 的事件循环执行
(all.get(type) || []).slice().map((handler) => {handler(evt); });
// 而后找到所有为 * 的事件,循环执行
(all.get('*') || []).slice().map((handler) => {handler(type, evt); });
}
};
}
vue3 mitt 应用
mitt 劣势
- 首先它足够小,仅有 200bytes。
- 其次反对全副事件的监听和批量移除。
- 它还不依赖 Vue 实例,能够跨框架应用,React 或者 Vue,甚至 jQuery 我的项目都能应用同一套库。
API
// 创立 mitt 实例
mitt()
// 事件名称到注册处理程序函数的映射。all
// 触发事件,两个参数:name:触发的办法名,data:须要传递的参数
emit(name,data)
// 绑定事件,两个参数:name:绑定的办法名,callback:触发后执行的回调函数
on(name,callback)
// 解绑事件,一个参数:name:须要解绑的办法名
off(name)
装置 mitt:
npm install mitt -save
新建 EventBus.js
文件:
// 事件总线第三方库:import mitt from 'mitt';
const bus = mitt();
export default bus;
案例应用
收回事件的页面:send.vue
<template>
<div class="box">
<h2>send 页面视图 </h2>
<button @click="sendData"> 部分事件总线:点击之后给 cc 页面传递一个值 </button>
</div>
</template>
<script setup>
import bus from "../utils/EventBus"
const sendData = () => {bus.emit("data",18)
}
</script>
接管事件的页面:response.vue
<template>
<div>
<h2>response 页面视图 </h2>
</div>
</template>
<script setup>
import bus from "../utils/EventBus"
import {ref ,onMounted} from "vue";
onMounted(()=>{bus.on("data",(info)=>{
// info 就是 emit 传过来的数据
console.log("dd 页面接管到的值:",info)
})
})
</script>
移除监听事件
Bus.off('Event');
参考地址:
- https://tangjiusheng.com/web/4935.html
- https://blog.csdn.net/weixin_41759744/article/details/125305021
- https://www.jb51.net/article/263720.htm