生命周期钩子
我们可以直接看生命周期图来认识都有哪些生命周期钩子(图片是根据官网翻译后绘制的):
从图中我们可以看到Vue3.0新增了 setup
,这个在前面我们也详细说了, 然后是将Vue2.x中的 beforeDestroy
名称变更成 beforeUnmount
; destroyed
表更为 unmounted
,作者说这么变更纯粹是为了更加语义化,因为一个组件是一个 mount
和 unmount
的过程。其他Vue2中的生命周期仍然保留。
上边 生命周期图 中并没包含全部的生命周期钩子, 还有其他的几个, 全部生命周期钩子如图所示:
我们可以看到 beforeCreate
和 created
被 setup
替换了(但是Vue3中你仍然可以使用, 因为Vue3是向下兼容的, 也就是你实际使用的是vue2的)。其次,钩子命名都增加了 on
; Vue3.x还新增用于调试的钩子函数 onRenderTriggered
和 onRenderTricked
1.性能 {#1性能}
- 双向响应原理由
Object.defineProperty
改为基于ES6
的Proxy
,使其颗粒度更大,速度更快,且消除了之前存在的警告; - 重写了
Vdom ,突破了
Vdom` 的性能瓶颈 - 进行了模板编译的优化
- 进行了更加高效的组件初始化
2.Tree-Shaking 的支持 {#2tree-shaking-的支持}
支持了 tree-shaking
(剪枝):像修剪树叶一样把不需要的东西给修剪掉,使 Vue3 的体积更小。
需要的模块才会打入到包里,优化后的 Vue3.0 的打包体积只有原来的一半(13kb)。哪怕把所有的功能都引入进来也只有23kb,依然比 Vue2.x
更小。像 keep-alive
、 transition
甚至 v-for
等功能都可以按需引入。
3.Composition API {#3composition-api}
composition-api
是一个 Vue3
中新增的功能,它的灵感来自于 ReactHooks
,是比 mixin
更强大的存在。
语法糖介绍
compositon-api提供了一下几个函数
- reactive
- watchEffect
- computed
- ref
- toRefs
composition-api
可以提高代码逻辑的可复用性,从而实现与模板的无关性;同时使代码的可压缩性更强。另外,把 Reactivity
模块独立开来,意味着 Vue3.0 的响应式模块可以与其他框架相组合。
4.Fragments {#4fragments}
不再限制 template
只有一个根节点。
render
函数也可以返回数组了,有点像 React.Fragments
5.Better TypeScript Support {#5better-typescript-support}
更好的类型推导,使得 Vue3
把 TypeScript
支持得非常好
6.Custom Renderer API {#6custom-renderer-api}
实现用DOM
的方式进行 WebGL
编程
体验Vue3.0 {#体验vue30}
初始化项目 {#初始化项目}
-
使用脚手架创建项目
vue create my-Project
-
安装composition-api,体验新特性
npm i @vue/composition-api -s
-
使用插件
// main.js
import Vue from 'vue' import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
Setup函数 {#setup函数}
setup()
函数是Vue3.0中,专门为组件提供的新属性。它为基于Composition API
的新特性提供了统一的入口。
在Vue3中,定义
methods
、watch
、computed
、data
数据都放在了setup()
函数中
- 执行时机
setup()
函数会在created()
生命周期之前执行。
setup执行时机.png
2. 接收props
数据
props
是setup()
函数的一个形参,组件接收的props
数据可以在setup()
函数内访问到。
setup(props) {
console.log(props.p1)
}
-
context上下文对象
context
是setup()
的第二个形参,它是一个上下文对象,可以通过context
来访问Vue的实例 this。setup(props,context) { console.log(this) console.log(context) }
=======setup的context参数.png=========
注意:在 setup() 函数中访问不到Vue的 this 实例
Teleport {#teleport}
如果用过React
的同学,可能对于 Portals 比较熟悉,详见。React 的 Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案,我理解,Vue 3 中的 Teleport 跟这个其实是类似的
在 Vue2,如果想要实现类似的功能,需要通过第三方库 portal-vue 去实现,感兴趣可以了解一下
本篇文章主要来探讨以下两个点:
Teleport
是什么?它解决的是什么问题?- 通过一个小实例介绍
Teleport
的使用
为什么我们需要 Teleport {#为什么我们需要-teleport}
Teleport 是一种能够将我们的模板移动到DOM
中Vue app
之外的其他位置的技术,就有点像哆啦A梦的"任意门"
场景:像modals
,toast等这样的元素,很多情况下,我们将它完全的和我们的
Vue应用的
DOM`完全剥离,管理起来反而会方便容易很多
原因在于如果我们嵌套在 Vue 的某个组件内部,那么处理嵌套组件的定位z-index
和样式就会变得很困难
另外,像modals
,toast
等这样的元素需要使用到Vue
组件的状态(data 或者 props)的值
这就是Teleport
派上用场的地方。我们可以在组件的逻辑位置写模板代码,这意味着我们可以使用组件的 data 或 props。然后在 Vue 应用的范围之外渲染它
Teleport
的使用
准备
快速搭建一个vue3
的项目
$ npm init vite-app learn-vue3
$ cd learn-vue3
$ npm install
$ npm run dev
用
yarn
$ yarn create vite-app learn-vue3
$ cd learn-vue3
$ yarn
$ yarn dev
打开: http://localhost:3000/
,看到如下页面,说明成功了
toast {#toast}
index.html
中
<div id="app"></div>
+ <div id="teleport-target"></div>
<script type="module" src="/src/main.js"></script>
src/components/HelloWorld.vue
中,添加如下,留意to
属性跟上面的id
选择器一致
<button @click="showToast" class="btn">打开 toast</button>
<!-- to 属性就是目标位置 -->
<teleport to="#teleport-target">
<div v-if="visible" class="toast-wrap">
<div class="toast-msg">我是一个 Toast 文案</div>
</div>
</teleport>
import { ref } from 'vue';
export default {
setup() {
// toast 的封装
const visible = ref(false);
let timer;
const showToast = () => {
visible.value = true;
clearTimeout(timer);
timer = setTimeout(() => {
visible.value = false;
}, 2000);
}
return {
visible,
showToast
}
}
}
效果展示:
可以看到,我们使用 teleport 组件,通过 to 属性,指定该组件渲染的位置与
同级,也就是在 body 下,但是 teleport 的状态 visible 又是完全由内部Vue
组件控制
与 Vue components 一起使用 ------ modal {#与-vue-components-一起使用--modal}
如果<teleport>
包含Vue
组件,则它仍将是 父组件的逻辑子组件
接下来我们以一个 modal 组件为例
<div id="app"></div>
<div id="teleport-target"></div>
+ <div id="modal-container"></div>
<script type="module" src="/src/main.js"></script>
<teleport to="#modal-container">
<!-- use the modal component, pass in the prop -->
<modal :show="showModal" @close="showModal = false">
<template #header>
<h3>custom header</h3>
</template>
</modal>
</teleport>
JS 核心代码如下:
import { ref } from 'vue';
import Modal from './Modal.vue';
export default {
components: {
Modal
},
setup() {
// modal 的封装
const showModal = ref(false);
return {
showModal
}
}
}
在这种情况下,即使在不同的地方渲染Modal
,它仍将是当前组件(调用 Modal 的组件)的子级,并将从中接收show prop
这也意味着来自父组件的注入按预期工作,并且子组件将嵌套在 Vue Devtools 中的父组件之下,而不是放在实际内容移动到的位置
看实际效果以及在Vue Devtool
中。
总结 {#总结}
本文主要介绍了 Vue 3
的新特性------Teleport
,从为什么要使用Teleport
,以及通过两个小 demo
,演示它的基础使用,希望能够对你有帮助
emits选项 {#emits选项}
- 官方文档传送门
- Vue官方建议我们在组件中所有的emit事件都能在组件的emits选项中声明
- emits参数有俩种形式对象和数组,对象里面可以配置带校验emit事件,为null的时候代表不校验,校验的时候,会把emit事件的参数传到校验函数的参数里面
- 当校验函数不通过的时候,控制台会输出一个警告,但是emit事件会继续执行
- 记录一个坑:比如你emit事件的名称正好和原生事件的名字重复了,那么这个事件会执行俩次,那么配置了emits这个选项的话,就能很好的解决这个问题,下去自己实验一下,这篇文章中不做演示
我们看一下带校验和不带校验的emit事件一个例子
子组件
Emiter.vue
<template>
<button @click="handleClick">点击emit-click事件</button>
<button @click="handleOpen">点击emit-open事件</button>
</template>
<script lang="ts">
import {defineComponent} from "vue";
export default defineComponent({
emits: {
click: null,//click事件没有检验
open: (value) => {
if (typeof value === "string") {
return true;
} else {
return false;
}
},
},
setup(props, {emit}) {
const handleClick = function() {
emit("click");
};
const handleOpen = function() {
emit("open", 1);
};
return {
handleClick,
handleOpen,
};
},
data() {
return {};
},
methods: {},
});
</script>
<style scoped></style>
父组件Emit.vue
<template>
<emiter @click="onClick" @open="onOpen"></emiter>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import Emiter from "@/components/Emiter.vue";
export default defineComponent({
components: {
Emiter,
},
data() {
return {};
},
methods: {
onClick() {
console.log("click me!");
},
onOpen() {
console.log("open me!");
},
},
});
</script>
<style scoped></style>
看一下结果,控制台输出警告信息
状态和事件绑定 {#状态和事件绑定}
Vue 3.0 中定义状态的方法改为类似 React Hooks 的方法,下面我们在 Test.vue 中定义一个状态 count:
<template>
<div class="test">
<h1>test count: {{count}}</h1>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup () {
const count = ref(0)
return {
count
}
}
}
</script>
Vue 3.0 中初始化状态通过setup
方法,
定义状态需要调用ref
方法。接下来我们定义一个事件,用来更新count
状态:
<template>
<div class="test">
<h1>test count: {{count}}</h1>
<button @click="add">add</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup () {
const count = ref(0)
const add = () => {
count.value++
}
return {
count,
add
}
}
}
</script>
这里的add
方法不再需要定义在methods
中,
但注意更新count
值的时候不能直接使用count
++,而应使用 count.value++
,
更新代码后,点击按钮count
的值就会更新了:
计算属性和监听器 {#计算属性和监听器}
Vue 3.0 中计算属性和监听器的实现依赖computed
和watch
方法:
computed {#computed}
<template>
<div class="test">
<h1>test count: {{count}}</h1>
<div>count * 2 = {{doubleCount}}</div>
<button @click="add">add</button>
</div>
</template>
<script>
import { ref, computed, watch } from 'vue'
export default {
setup () {
const count = ref(0)
const add = () => {
count.value++
}
watch(() => count.value, val => {
console.log(`count is ${val}`)
})
const doubleCount = computed(() => count.value * 2)
return {
count,
doubleCount,
add
}
}
}
</script>
计算属性computed
是一个方法,里面需要包含一个回调函数,当我们访问计算属性返回结果时,会自动获取回调函数的值:
const doubleCount = computed(() => count.value * 2)
watch {#watch}
监听器watch
同样是一个方法,它包含2
个参数,2
个参数都是 function:
watch(() => count.value,
val => {
console.log(`count is ${val}`)
})
第一个参数是监听的值,count.value
表示当count.value
发生变化就会触发监听器的回调函数,即第二个参数,第二个参数可以执行监听时候的回调
如果是2
个以上的监听属性 就这样
watch(
[refA, () => refB.value],
([a, b], [prevA, prevB]) => {
console.log(`a is: ${a}`)
console.log(`b is: ${b}`)
}
)
持续更新,未完待续....