提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
这篇文章是我之前学习 Vue3 做的笔记,加上一些官网和其他地方查阅的资料的补充,主要是带领学习了Vue2还未学习Vue3的小伙伴快速入门。通过这篇文章你可以快速的了解Vue3的新特性。
一、Vue3的创建方式
在创建vue3项目之前可以先来了解一下新一代前端构建工具Vite,这里我们只是简单介绍一下
1. Vite的优势
1.基于ESM的开发服务优势:
- 无需打包项目源代码
- 天然的按需加载
- 可以利用文件级的浏览器缓存
2.基于Esbuild的编译性能优化
3.内置的Web构建功能,开箱即用:
在了解vite后,我们就使用vite来创建项目,想要深入的去了解vite可以去官网。
2. 创建vue3项目
1.这里我们选择使用Vite搭建项目
// npm
npm create vite@latest
//Yarn
yarn create vite
//PNPM
pnpm create vite
2.创建模板+选择模板(这里我们选择vue + javaScript)
继续操作:
//进入项目文件夹
cd vite-project
//初始化项目
npm install
//运行项目
npm run dev
这时项目就正常运行了
二、Vue3入门
1. Composition API
Vue3 提出了 Composition API ,它可以把 一个逻辑的代码都收集在一起 单独写个hook,然后再引入,不用再像vue2中把数据,方法拆分开来。
这里引用官网的话来说:
组合式 API (Composition API) 是一系列 API 的集合,使我们可以使用函数而不是声明选项的方式书写 Vue
组件。它是一个概括性的术语,涵盖了以下方面的 API: 响应式 API:例如 ref() 和
reactive(),使我们可以直接创建响应式状态、计算属性和侦听器。生命周期钩子:例如 onMounted() 和 onUnmounted(),使我们可以在组件各个生命周期阶段添加逻辑。
依赖注入:例如 provide() 和 inject(),使我们可以在使用响应式 API 时,利用 Vue 的依赖注入系统。
2.setup
- 理解:Vue3.0中一个新的配置项,值为一个函数。
- setup是所有Composition API(组合API)“ 表演的舞台 ”。
- 组件中所用到的:数据、方法等等,均要配置在setup中。
(1).setup的执行顺序
setup执行在beforeCreate,created之前,它里面的this打印出来是undefined
(2)setup接受的参数
setup(props, context)
props:它接收父组件传递的值。
context: 上下文对象,用于代替以前的this方法可以访问的属性
attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs。
slots: 收到的插槽内容, 相当于 this.$slots。
emit: 分发自定义事件的函数, 相当于 this.$emit。
(3)setup返回值
setup返回的是一个对象,这个对象中的数据可以直接在模板中使用,在data配置项中也可以通过this.xxx拿到setup中返回的值
三、ref函数与reactive函数
在之前我们写的数据都不是响应式的数据,我们可以通过ref函数与reactive来定义响应式的数据,在使用前都需要引入函数。
1. ref函数
● 作用: 定义一个响应式的数据
● 语法: const xxx = ref(initValue)
○ 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
○ JS中操作数据: xxx.value
○ 模板中读取数据: 不需要.value,直接:<div>{
{xxx}}</div>
备注:
接收的数据可以是:基本类型、也可以是对象类型。
基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的。 对象类型的数据:内部 “ 求助 ”
了Vue3.0中新函数—— reactive函数。所以定义响应式数据的时候我们可以直接使用reactive函数
<script lang="js">
//引入ref函数
import {
defineComponent,ref} from "vue";
export default defineComponent({
name:'App',
data(){
return{
}
},
setup(props, context){
//定义一个响应式数据
let num = ref(0)
//定义方法
function add(){
console.log(1)
//在setup中要拿到ref定义的数据需要.value
num.value++
}
//把数据和方法返回出去,模板中才能使用
return {
num,add}
}
})
</script>
<template>
<div @click="add()">{
{
num}}</div>
</template>
2. reactive函数
● 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
● 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
● reactive定义的响应式数据是“深层次的”。
● 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
<script lang="js">
//引入reactive
import {
defineComponent,ref,reactive} from "vue";
export default defineComponent({
name:'App',
data(){
return{
}
},
setup(props, context){
let objs = reactive({
name:'ld',
age:20
})
function updata(){
//读取reactive定义的数据的时候不需要.cvalue
objs.age = 28
}
return {
objs,updata}
}
})
</script>
<template>
<div @click="updata()">{
{
objs}}</div>
</template>
3. reactive对比ref
● 从定义数据角度对比:
ref:用来定义基本类型数据。
reactive:用来定义对象(或数组)类型数据。
备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。
● 从原理角度对比:
ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)。 ○
reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
● 从使用角度对比:
ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。
reactive定义的数据:操作数据与读取数据:均不需要.value。
4. 扩展:在vue3中使用ref拿到dom
在vue2中我们使用ref可以获取元素,在vue3我们同样可以,只是使用的方式不一样了。
//拿到单个DOM
<script lang="js">
import {
defineComponent,ref,reactive} from "vue";
export default defineComponent({
name:'App',
setup(props, context){
let item = ref(null)
console.log(item)
return {
item}
}
})
</script>
<template>
<div ref="item" id="item">20</div>
</template>
//获取多个DOM
<script lang="js">
import {
defineComponent,ref,nextTick} from "vue";
export default defineComponent({
name:'App',
setup(props, context){
// 存储dom数组
const myRef = ref([]);
const setRef = (el) => {
myRef.value.push(el);
};
//下一个DOM 更新周期之后执行
nextTick(() => {
console.dir(myRef.value);
});
return {
setRef}
}
})
</script>
<template>
<div v-for="(item,index) in 3" :ref="setRef">10</div>
</template>
获得多个ref效果图:
四、computed与watch
1. computed
与vue2中的配置方法一致
import {
defineComponent,reactive,computed} from "vue";
...
setup(){
let pers = reactive({
name:'dlh',
age:38
})
//计算属性——简写
let compName = computed(()=>{
return pers.name + '-' + pers.age
})
//计算属性——完整
let compNames = computed({
//读取
get(){
return pers.name + '-' + pers.age
},
//修改
set(value){
const nameArr = value.split('-')
pers.name= nameArr[0]
pers.age = nameArr[1]
}
})
return {
compName,compNames
}
}
2. watch
与vue2中的配置方法一致
watch(data,handler,object)
data:监视的数据
handler(newValue,oldValue):回调函数(改变后的值,改变前的值)
object:可选配置项 { immediate: true,deep:true }
import {
watch,ref,reactive} from "vue";
...
setup(){
let num=ref(5),num2=ref(10)
let person = reactive({
name:'5465',
age:18
}
//情况一:监视ref定义的响应式数据
watch(num,(newValue,oldValue)=>{
console.log('num变化了',newValue,oldValue)
},{
immediate:true})
}
//情况二:监视多个ref定义的响应式数据[数组方式]
watch([num,num2],(newValue,oldValue)=>{
console.log('num或num2变化了',newValue,oldValue)
})
/*
情况三:监视reactive定义的响应式数据,无法正确获得oldValue,并且强制开启了深度监视
*/
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{
immediate:true)
//情况四:监视reactive定义的响应式数据中的某个属性(参数以函数返回值的形式传递)
watch(()=>person.num,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{
immediate:true,deep:true})
//情况五:监视reactive定义的响应式数据中的多个数据[数组]
watch([()=>person.num,()=>person.age],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{
immediate:true,deep:true})
3.watchEffect函数
在watch中,要指明监视的属性,并且指明监视的回调,但是在watchEffect中不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const num1 = sum.value
const num2 = person.age
console.log('watchEffect配置的回调执行了')
})
五、生命周期
生命周期需要 通过 import 导入,在 setup 函数中使用,并且Vue3 的生命周期 比 Vue2.X 的生命周期快。
Vue2.X | Vue3 |
---|---|
beforeCreate | setup() |
created | setup() |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
errorCaptured | onErrorCaptured |
’ | onRenderTracked(新增) |
’ | onRenderTriggered(新增) |
新增加了两个钩子
onRenderTriggered 跟踪虚拟 DOM 重新渲染时调用
onRenderTracked 当虚拟 DOM 重新渲染被触发时调用
在setup中使用生命周期钩子
import {
defineComponent,onMounted} from "vue";
...
setup(){
onMounted(()=>{
console.log('Mounted !!')
})
}
六、自定义hook函数
vue3 中的 hooks 函数相当于 vue2 里面的 mixin 混入,hooks本质是一个函数,
,就是将文件的一些单独功能的js代码进行抽离出来,放到单独的js文件中他的优势就在于, 复用代码, 让setup中的逻辑更清楚易懂。
封装监听鼠标位置的hooks
// src/hooks/useMove.js
import {
ref} from 'vue'
export default function(){
//创建2个响应式变量来储存用户鼠标x轴和y轴的位置
let pageX = ref(0) , pageY = ref(0)
window.addEventListener('mousemove',(event)=>{
pageX.value = event.pageX
pageY.value = event.pageY
})
return {
pageX,
pageY
}
}
//组件中引入使用
<script setup>
import useMove from "./hooks/useMove"
const {
pageX, pageY} = useMove()
</script>
<template>
<div>
X: {
{
pageX }} Y:{
{
pageY }}
</div>
</template>
<style scoped>
</style>
八、provide 与 inject
他们实现了祖与后代组件间通信
父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据
//祖组件
import {
defineComponent, provide, reactive} from "vue";
...
setup(){
let hobby = reactive({
name:'爱好',price:'无价'})
provide('hobby',hobby)
}
//后代组件
import {
inject, ref} from 'vue'
...
setup(){
const hobby = inject('hobby')
return{
hobby
}
}
九、Teleport - 任意传送门
可以将特定的html模板传送到Dom的任何位置
Teleport具有一个必填属性- to to 需要 prop,必须是有效的查询选择器或 HTMLElement
<teleport to='#portal'>
<div v-if="isShow" class='doms'>
Hello ! teleport
</div>
</teleport>
接下来写一个案例就能够清晰的知道它的用法了。
首先找到index.html文件
//在</body>前面添加<div id='portal'></div>
<body>
<div id="app"></div>
<div id='portal'></div>
<script type="module" src="/src/main.js"></script>
</body>
在子组件中
<script lang="js">
import {
ref } from 'vue'
export default {
setup () {
//控制隐藏的变量
const isShow = ref(false)
//定时器id
let closeTime = null;
//当用户点击弹出
const showNotification = () => {
isShow.value = true
clearTimeout(closeTime)
closeTime = setTimeout(() => {
isShow.value = false
}, 2000)
}
return {
isShow,
showNotification
}
}
}
</script>
<template>
<div class='portals'>
<button @click='showNotification'> 显示 </button>
//需要传送的内容写在teleport里面并且to的地址为前面index文件中的id
<teleport to='#portal'>
<div v-if="isShow" class='doms'>
Hello ! teleport
</div>
</teleport>
</div>
</template>
<style scoped>
.doms {
position: fixed;
bottom: 20px;
left: 20px;
width: 120px;
padding: 30px;
color: #1a1a1a;
background-color: #fff;
border-radius: 10px;
}
</style>
最终时间效果:当用户点击后左下方出现提示框,2秒后消失
十、响应式数据的判断
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
- isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
- isProxy: 检查一个对象是否是由eactive 或者 readonly 方法创建的代理
十一、其他的一些Composition API
1.toRaw 与 markRaw
toRaw:
作用:将一个由reactive生成的响应式对象转为普通对象。
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
import {
defineComponent,reactive,toRaw} from "vue";
export default defineComponent({
setup(){
let obj = reactive({
name:'段',
age:20
})
let objToRaw = toRaw(obj)
console.log(obj,objToRaw)
return{
obj,
objToRaw
}
}
})
这时第二个数据就变成了普通数据了
markRaw:
作用:标记一个对象,使其永远不会再成为响应式对象。
应用场景:
1.有些值不应被设置为响应式的,例如复杂的第三方类库等。
2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
2.shallowReactive 与 shallowRef
shallowReactive : 只处理对象内最外层属性的响应式(也就是浅响应式)
使用场景:如果有一个对象结构多层嵌套,我们的需求只是修改最外层的数据,就不用把里面的嵌套结构都转为响应式,只需要把最外层的属性设为响应式这样能够提高性能。
shallowRef : 只处理基本数据类型的响应式, 不进行对象的响应式处理。
使用场景:如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 那么就可以使用它
3.readonly 和 shallowReadonly
readonly : 让一个响应式数据变为只读的(深只读)。
内层的每一个属性都设置为只读,进行修改操作就会报错。
shallowReadonly: 让一个响应式数据变为只读的(浅只读)。
浅度的话只针对最外面一层,修改内层不会报错
应用场景:当数据不想被修改的时候使用,根据不同的程度来选择api
十二、全局api的变化
Vue 2中有许多全局 API 和配置,Vue3中对这些API做出了一些调整:
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 |
例如挂载$api到原型上
import {
createApp } from 'vue'
import './style.css'
import App from './App.vue'
import API from '@/api/api';
const app = createApp(App)
app.config.globalProperties.$API = API;
app.mount('#app')
总结
以上就是今天要讲的内容,本文仅仅简单介绍了vue3中的一些特性,更多的知识大家可以去看看官方文档,希望这篇笔记能够帮助到大家。