本文主要是介绍跟着小满老师学习vue+pinia+ts(笔记1),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1-vue3
相比于vue2
改进
vue3允许多个根节点,都是虚拟节点不会渲染;
支持tsx写法和jsx写法;
同时新增了teleport和suspense两个内置组件和多v-model的写法;
tree-shaking去除无用代码;
compositionAPI
2-构建项目
使用vite构建
npm init vite@latest
使用vue构建
npm init vue@latest
目录结构
public目录下存放静态资源,不会被vite编译
src-assets下面存放图片等静态资源,图片比较小可以通过配置打包成base64
env.d.ts:通过declare module进行ts类型扩充
webpack使用js作为入口文件,vite使用html作为入口文件
vue3:volar
vue2:vuter
模板语法
optionsAPI,setup函数模式(变量手动return出去),setup语法糖模式
@事件触发
@[event].stop="clickEvent"
const event="click"
:绑定
绑定id,style,class
v-model双向绑定
v-memo对于数组渲染有一定优化,缓存
3-虚拟dom
和diff
算法
虚拟dom就是通过js生成的AST节点树,AST是抽象语法树(ts转js的过程中,babel插件es6转es5的时候,js通过v8引擎转字节码)。
因为在dom上面的属性是非常多的,所以操作实际dom比较耗费性能,操作js是比较快的,我们使用js的计算性能换取操作dom消耗的性能。
有key的diff算法
通过前序对比算法和尾序对比算法,比较新旧node,如果新节点多出来,挂载;旧节点多出来,卸载。
无序的情况:先构建一个新旧节点的对应关系(map),新节点在旧节点中位置的数组。多余旧节点删掉,多余新节点(新节点不包括在旧节点)删除,存在交叉,利用最长递增子序列算法。当前遍历的这个节点不在子序列说明需要进行移动。(为什么)
没有key的diff算法
渲染-新增-删除,没有key是新node对新node进行一个替换。
vue2是双向diff算法
4-Ref
全家桶
ref包裹普通数据转为响应式数据
import {ref}from'vue'
import type{Ref}from 'vue'
type M{
name:string
}
const R:Ref(<M>)=ref({name:"yyy})
通过.value获取普通数据值
console.log(R.value.name)
isRef()//判断
shadowRef//同ref一样,但是只能做浅层次响应式,不能和ref混用
triggerRef//强制更新收集的依赖,也是不能和shadowRef一起用,ref底层用了这个
chrome浏览器启用自动化格式工具
ref和reactive的区别
ref支持所有类型,reactive支持引用类型,Array,Object,Map,Set
ref取值赋值需要通过.value,reactive不需要
reactive包裹得到的是proxy响应式对象,不能被直接赋值,直接赋值破坏响应式对象。可以通过一些方法,比如直接利用数组内置方法,或者添加一个对象,将数组作为对象的一个属性。reactive一般绑定表单数据。
const list=reactive<string[]>([])
list.push(...res)
const list=reactive<
arr:string[]
>({
arr:[]
})
list.arr=res
readonly(list)//只读
shadowReactive//浅层
ref和reactive都是通过proxy进行代理。
ref更改会触发重新渲染,shadowRef不会触发重新渲染,重新渲染的时候所有组件模板都被更新为最新数据。
toRef,toRefs,toRaw
toRef修改响应式对象的值,toRef(obj,key)
toRefs:接收一个引用类型的参数数据,泛型传入,将引用类型的属性toRef
const toRefs=<T extends Object>(obj:T)=>{
const map:any={}
for(let key in obj){map[key]=toRef(obj,key)
}
return map
}
可用于结构响应式对象的属性
const man=reactive({
name:"yyy",age:21
})
const {name,age}=toRefs(man)
toRaw使响应式对象脱离响应式,得到非响应式数据
const r=toRaw(man)
5-响应式原理
6-computed
和watch
computed
是怎么实现的?
effect 函数,它用来注册副作用函数,同时它也允许指定一些选项参数 options,例如指定 scheduler 调度器来控制副作用函数的执行时机和方式;也介绍了用来追踪和收集依赖的 track 函数,以及用来触发副作用函数重新执行的 trigger 函数。
实际上,综合这些内容,我们就可以实现 Vue.js 中一个非常重要并且非常有特色的能力——计算属性。通过options指定lazy得到懒执行的effect函数,延迟执行传递给它的副作用函数。
传递给 effect 函数的参数 fn 才是真正的副作用函数,而 effectFn 是我们包装后的副作用函数。为了通过 effectFn 得到真正的副作用函数 fn 的执行结果,我们需要将其保存到 res 变量中,然后将其作为 effectFn 函数的返回值。
现在我们已经能够实现懒执行的副作用函数,并且能够拿到副作用函数的执行结果了。effectFn函数返回res变量,effect函数返回改造后的effectFn函数,传入的参数是effect副作用函数和配置项options。
计算属性和方法的区别:计算属性在依赖项改变的时候才会重新计算,否则使用上一次缓存的值。
极少数的情况下,可以通过get、set可写一个计算属性。
避免更改计算属性的值,因为依赖项更新的时候,计算属性相当于一次快照,改变快照是没有意义的,因此计算属性的返回值应当视为只读且永远不会被改变,它只依赖于依赖项状态的改变而改变。
watch
watch
本质就是观测一个响应式数据,数据发生变化的时候执行回调函数。
watch(source,(oldVal,newVal)=>{
},{
deep:true//reactive不需要手动开启监听,
immediate:true,
flush:"pre"//pre组件更新之前,sync,post
})
watch
源码
watchEffect
watchEffect
接收一个函数参数,当这个函数中的响应式变量变化的时候,函数重新执行。
watchEffect(() => {console.log(`Count is now: ${count.value}`)// 随着 count 变化而自动执行的代码})
组件和生命周期
生命周期钩子函数在vue
组件实例的不同阶段被自动调用,我们可以在钩子函数调用的时候进行一些操作。
因为钩子函数中this指向调用的组件实例,所以不使用箭头函数。
生命周期钩子函数包括setup,beforeMount,mounted,beforeUpdate,updated,onBeforeUnmount,onUnmouted
,
beforeCreated,created函数已经被替换成了setup函数,初始化响应式数据、设置props、引入全局依赖等。获取组件的$el\$ref
beforeMount
已经将模板编译成了render
函数,但是还没有执行函数开始渲染,组件实例化之前
mounted
组件dom渲染完成,可以进行dom操作或者网络请求。
beforeUpdate
数据更新之前,可以在此操作一些数据或状态
updated
数据更新完成,操作dom或者新数据;
OnBeforeUnmount
组件卸载之前,可以关闭定时器和网络请求等操作
onUnmounted
组件已经被销毁
除了以上钩子函数,还有activated和deactiveated函数,
使用keep-alive
缓存组件的时候可以通过这两个钩子函数检测组件是否被激活。
同时还提供了onRenderTracked
函数,追踪渲染过程,render
函数执行的时候被调用,更细致一点说是在dom更新或者创建的时候被调用。可以用来做性能检测,比如统计渲染次数、时间,得到虚拟dom树。
我们可以通过getCurrentInstance()获取当前组件实例
7-组件
全局组件和局部组件、递归组件
全局组件app.component("vname",vname)
,全局组件可以批量注册
递归组件,tree.vue
中<tree/>
也可以改名字
export default {name:"yyy"}
<yyy/>
动态组件
多个组件使用同一个挂载点,做到动态切换
导航切换
<component :is="now/>
import AVue from "
const now=shadowRef(AVue)
const change=function(item){
now.value=item.com
}
const data=reactive({
{
name:"AVue",
com:markRaw(AVue)
}
})
插槽
子组件用插槽占位,父组件使用子组件的时候就可以向子组件插入内容,同时还支持命名插槽
子组件son
<template>
<slot></slot>
</template>
父组件
<son>
<h1>这是我想插入的内容啊</h1>
</son>
命名的这样写
子组件son
<template>
<slot name="y1"></slot>
<slot name="y2"></slot>
</template>
父组件
<son>
<template #y1>
#是v-slot的语法糖~
<h1>这是我想插入的内容啊</h1>
</template>
</son>
异步组件、代码分包
异步组件主要可用于写骨架屏,请求到数据之后再展示。
defineAsyncComponent
异步加载组件可以使组件的代码在使用的时候被加载,而不是前置加载,减少初始加载时间,仅加载必要的内容。需要搭配suspence
使用,
使用异步组件将暂时不会用到的包打包到分包而不是主包,减少首屏加载白屏的时间
<Suspense>
<template #default><AsyncComponent/></template>
<template #fallback>放置骨架屏</template>
</Suspense>
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() =>import('./AsyncComponent.vue')
)
teleport
组件
帮助我们在dom
树中独立于父组件的位置渲染元素,不会受父组件的布局影响。
<Teleport to="body" :disabled="false" >
//将组件传送到body标签下
<A/>
</Teleport>
keep-alive
组件
<keep-alive excludes="">
</keep-alive>
一次请求接口放在onMounted
函数里面,多次请求接口放在onActivated
函数里面,卸载放在onDeactivated
函数里面。
transition
组件
①属性和类
插入删除元素的时候添加过渡动画效果,平滑过渡提升用户体验。
属性:name,appear(是否初始渲染执行过渡效果)enter(后续渲染也执行过渡效果)
设置时间:duration="dur"
类:当插入删除元素的时候,类依次执行从enter-from-class
、enter-class
、enter-active-class
、enter-to-class
、leave-from-class
、leave-class
、leave-active-class
、leave-to-calss
、
初次渲染
appear-from-class appear-active-class appear-to-class
②transition
结合animate.css
npm install animate.css
import "animate.css"
class=“animate-animated 类名”
③生命周期
beforeEnter,enter,afterEnter,enterCanceled
beforeLeave,leave,afterLeave,leaveCanceled
@before-enter="beforeEnter"//这样写
④transitionGroup
可以包裹多个元素
v-for
遍历
移动过渡
安装lodash
npm install lodash -S
npm install @types/lodash -D
使用lodash的shuffle函数乱序数组
<script src="https://cdn.jsdelivr.net/npm/lodash"></script>
<script>const arr2 = []const arr = Array.apply(null, { length: 80 }).map((_, index) => {arr2.push(index + 1)// console.log(index % 9 + 1)})// console.log(arr2 + 1)const shaffled = _.shuffle(arr2)console.log(shaffled)
状态过渡
给数字,svg添加过渡
npm install gsap //一个响应式的动画库
import gsap from "gsap"
com num=reactive(
{
current:0,
tweenedNum:0
})
watch(num.current,(old,new)=>{
gsap.to(num,{duration:1,tweenedNum:new})//gsap(要改变的元素,元素改变后的状态)
})
父子组件传值
①父组件通过props向子组件传递数据,子组件通过emit触发事件向父组件传递
<son :title="name1" onclick="getName" ref="sonValue"/>const sonValue=ref<InstanceType<typeof som>>()
const name1="我是爸爸"
const getName=function(name){console.log(name)}
const name2=sonValue?.value.name
const emits=defineEmits(["on-click"])
const send=()=>{
emits("on-click","yyy")
}
const props=defineProps({
{name:string,default:"yyy"},
{arr:Array,default:[]}
})
//通过defineEmit触发自定义事件向父组件传值
defineEmits<
{
(e:"on-click",name:string):void
}
>()
//通过defineExpose向父组件暴露数据,比如表单校验validate
defineExpose({
name:"syc"
})
//通过defineProps获取父组件的值
withDefaults(
defineProps<
{title:string,arr:number[]
}
>(),
{
title:"yyy",
arr:()=>[666]
}
)
②provide 和inject
用于深层嵌套组件,避免prop
逐层传递不需要的数据,祖先组件provide
的数据在任何后代组件都可以访问。
函数式使用provide只能在vue3中,optionsAPI
祖先组件
const myColor=ref("red")
//provide("color",myColor)
provide("color",readonly(myColor))//不想被子组件修改provide传的值
后代组件
import type{Ref}from "vue"
const color=inject<Ref<string>>("color")
当我们访问一个必须存在的属性的时候使用非空断言!.
//const change=()=>{
//color!.value="yellow"//provide传过来的值可以被子组件修改
//}
③兄弟组件传参
1-最基本的方式可以通过父组件传递,
2-使用eventbus
实现eventbus
使用mitt
库
npm install mitt -S
import mitt from "mitt"
3-使用vuex或者pinia(最方便的方法,但肯定不能一股脑全放仓库,页面刷新的时候数据丢失)
pinia相比于vuex,只需要实例化pinia即可访问,vuex需要创建store实例,
$patch是更新状态的方法,不是替代哦
(替换是`$state`,store.`$`state={}),
只能更新已经存在的状态,$set可以添加新状态
在actions里面需要通过this得到store里面的值,
如果是箭头函数,通过传入state参数,操作state参数,不用this。
解构store,不能直接解构,这样无法继续保持响应式,使用storeToRefs
$reset()重置store到原始状态。
订阅store中状态的改变,$subscribe(args,state)=>{},{detached:true}
订阅actions,Actions被调用就走这个函数$on'Action
pinia和vuex都是页面刷新丢失,跟着小满老师写个插件持久化存储,
这篇关于跟着小满老师学习vue+pinia+ts(笔记1)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!