笔记-13-Vue
不应该使用箭头函数来定义method函数。理由是箭头函数绑定了父级作用域的上下文,所有this将不会按照期望指向组件实例,将是undefined
Mustache 插值语法,支持表达式,三元运算符
指令:
v-once 内容只渲染一次,数据修改后也不会重新渲染包括子元素
v-text 将数据绑定到标签上展示,和Mustache语法没啥区别
v-html 会解析html
v-pre 用于跳过元素和他子元素的编译过程,显示原始的Mustache标签,跳过不需要编译的节点,加快编译速度。
v-cloak 中文翻译“斗篷”,搭配CSS属性选择器和display:none使用,当JS代码加载完成后,v-clock会消失,这样元素就会显示出来,防止用户看到插值表达式
v-memo 接收一个数组,指定元素可以响应式,没指定的不响应,比v-once更灵活。
v-bind 绑定属性
v-on 绑定事件,它有许多修饰符:
.stop 阻止冒泡
.prevent 阻止默认行为
.capture 添加事件监听时监听捕获阶段
.self 当事件是从被监听元素自身触发时才回调
.{keyAlias} 仅当事件是从特定按键触发时回调
.once 只触发一次
.left 点击鼠标左键触发
.right 点击鼠标右键触发
.middle 点击鼠标中键触发
.passive 以 {passive:true}的模式添加监听 (提升滚动性能)
想获取event对象,并有其他传参时,在最后一位实参传$event
<template>
标签可以用来在上面写指令,它并不会渲染到页面中,类似小程序中的block
v-show 不可写在<template>
上,不可和v-else
使用
v-if 是不会渲染元素,v-show是样式display:none
v-for 还可以遍历对象(val,key,index) in obj
v-for中的 in和of的效果是完全一样的,key属性主要是用于虚拟DOM算法,在新旧nodes对比时辨识VNodes
computed 计算属性 任何包含响应式数据的复杂逻辑都应该使用计算属性
对比方法,它是有缓存的,基于他的依赖关系进行缓存,当数据不变化时,计算属性不重新计算
计算属性有getter和setter,但基本不会使用
watch里可以完成异步操作,计算属性不可以
watch 深度监听,第一次渲染时就立即执行 xxx:{ handler(newV,oldV){},deep:true,immediate:true }
“obj.key”:function(newV, oldV){} // 一种监听对象内属性的写法
$watch // 在created生命周期函数里使用this.$watch(想监听的属性,(newV,oldV)=>{},{deep:true,immediate:true})
Vue.toRow(代理对象) 可拿到原始对象
Vue3把过滤器删除了,可以用方法替代
v-model 针对表单的双向绑定,本质上是v-bind和@input的语法糖,但实际它底层还是很复杂
v-model的修饰符.lazy 就会将input事件改为change ,输入完内容后失去焦点或者按回车后才会触发
.number修饰符 自动将内容转换为数值
.trim 修饰符 去除首尾空格
注册全局组件:app.component(组件名称, 组件),即使不用也会被打包,增大打包后js文件体积
局部组件 components
npm install @vue/cli -g 全局安装vue cli (脚手架)
vue create xxx 创建xxx项目
.browserslistrc 是用来控制兼容的浏览器,它会自动去caniuse查询
.jsconfig.json 是给vs code用的,给予更好的代码提示
比如配置了路径别名,但vs code不知道,就可以在此文件配置
.vue文件在脚手架中被vue-loader解析,但直接写template中的代码需要引入包含编译器的vue版本
npm init vue@latest 自动安装create-vue 并使用vite创建项目
父传子 props 子组件接收props可用数组、对象(可设默认值、类型、required) 注意:如果类型是对象/数组,默认值必须用函数返回值。否则会被共享一个对象
子组件没有接收的元素会被添加到子元素的根节点,如果有内部的节点想拿到就通过bind $attrs.xxx的方式拿到
如果不希望组件的根拿到attribute,可在组件中设置inheritAttrs:false,如果有多根节点,会报警告,必须手动绑定在某个根节点上 v-bind=“$attrs”
当子组件的最外层不是一个根节点时,他不知道要哪个元素绑定样式,就会不显示
子传父 this.$emit(触发的事件名称,参数),子组件通过事件传递信息。
在子组件中设置emits属性,可为数组也可对象,对象时可以传一个方法,在内部验证
插槽 slot 抽取共性、预留不同
当有多个插槽时需要用到具名插槽,<slot name=“xxx”></slot>
父元素<template v-slot:xxx>
没有名字的默认是default
动态插槽名v-slot:[变量]
简写 # 插槽名
代替 v-slot
渲染作用域
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的
作用域插槽
在父组件中拿到子组件的数据。
子组件 <slot :data=“data”></slot>
父组件 #xxx=”xxx.data”
这是vue特有功能,react中没有
如果没有名字,默认插槽的传值就直接v-slot=“xxx”【独占默认插槽的简写】
Provide和Inject 适用于孙子或更深层级 子组件通过数组的形式接收就可以
将data中的数据提供到provide,provide的定义形式类似data也是函数,搭配computed函数 可以做到动态的变化
全局事件总线
Vue3从实例中移除了$on、$off、$once方法,所以我们如果想继续使用,需要通过第三方库,例如mitt或tiny-emitter
生命周期
created里组件实例已创建,可以发送网络请求、事件监听等
mounted里可使用DOM
unmounted里面可以坐回收操作比如取消事件监听
$refs 拿DOM
$parent 获取当前组件的父组件 $root 获取根组件 Vue3中移除了$children
动态组件 <component :is=“xxx”>
<keep-alive></keep-alive>
包裹动态组件,切换时可以缓存组件状态,也能提升一些性能
属性:include=“aaa,bbb” 只缓存这两个组件,这里的名称要对应在组件定义时name属性
exclude 排除、max 最多缓存多少组件实例
此时监控组件的进入和离开可用生命周期函数activated和deactivated
webpack的分包处理规则
自己编写的业务代码在app.js 第三方的在chunk-vendors里面
当业务写的很多时,app.xxx.js就会很大,导致首屏加载时间过长
import()方法导入的文件可以分包,这个方法返回的是个promise
组件分包,通过defineAsyncComponent用异步的形式导入组件,会形成一个单独的js文件
defineAsyncComponent接受两种类型的参数:
类型一:工厂函数,该工厂函数需要返回一个Promise对象;
类型二:接受一个对象类型,对异步函数进行配置
当你修改了响应式状态时,DOM 会被自动更新。但是需要注意的是,DOM 更新不是同步的。Vue 会在“next tick”更新周期中缓冲所有状态的修改,以确保不管你进行了多少次状态修改,每个组件都只会被更新一次。
要等待 DOM 更新完成后再执行额外的代码,可以使用 nextTick() 全局 API:
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// 现在 DOM 已经更新了
}
reactive的局限性之3 对解构操作不友好
当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:
const state = reactive({ count: 0 })
// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收到的是一个普通的数字
// 并且无法追踪 state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count)
组件的v-model ,和在表单中一样,做了两件事。一是props传值,相当于:modelValue=“xxx”
子组件要接收,二是监听事件@update:modelValue=“xxx = $event”
需要在emits:[]里声明 , 参数会被赋值给xxx
自定义名称(不使用modelValue这个名字)
v-model:myname=“xxx”
这样就可以绑定多个属性了
Mixin Vue2中比较常用,3中用的少了
组件和组件之间有时候会存在相同的代码逻辑,对相同的代码逻辑进行抽取
组件属性 mixins:[xxx] 完成混入,如果冲突自身优先,生命周期挨个执行,方法都会执行,对象key相同时优先组件自身
组件自己的data比mixin的data优先级高如果是生命周期函数,会先执行mixin的再执行组件自己的全局的mixin性能不好修改优先级策略
CompositionAPI对比OptionsAPI
原来操作一个数据 是分散的,methods、watch、等等分散到各个配置项
新的可以将一个数据和操作数据的各种方法抽离,抽离成一个方法,使用更灵活
setup函数有两个参数,props,context。
无this 文档说没创建,但看源码应该是没绑定
conetxt中包含三个属性:attrs、slots、emit
ref 定义响应式数据,基本类型,也可以复杂类型,从网络中获取的数据一般用ref
reactive 用于定义复杂类型数据,一般用于本地数据,聚合的数据,组织在一起有作用
父传子对象,子可以修改父组件的数据,并且修改的是同一个对象,这样是不好的,不符合单向数据流的规范,应该通过发送事件让父去修改。
readonly 是为了避免有的人不懂单向数据流,readonly(响应式数据) 包裹后就不可修改了
父组件修改它被包裹前的响应式对象即可【本质劫持了proxy的set方法】
isProxy 检查对象是否由reactive或readonly创建的proxy
isReactive 检查对象是否由reactive创建的响应式代理,如果该代理是readonly创建的,但包裹了reactive创建的另一个代理也会返回true
isReadonly 检查对象是否由readonly创建的只读代理
toRaw 返回reactive或readonly代理的原始对象
shallowReactive 浅层响应式
shallowReadonly 浅层只读
toRefs reactive响应式复杂数据结构后不再拥有响应式,如果希望每个结构出来的值是响应式,可以在结构的时候用toRefs()包裹对象,这样结构出来的值全是ref的响应式。
如果想单独结构出响应式的值,toRef(对象,“键”)
如果对象里没有一个属性,toRefs不能用了,要用toRef(),如果没有这个age属性,他就会给返回一个空的来,下面再再修改这个空的值就是响应式的,不会报错
isref 判断是否ref对象 shallowRef 创建浅层ref
computed() 方法 在setup中使用计算属性
通过ref方法拿DOM,绑定在元素上的ref名称和setup函数里定义的ref名称一致,在onMounted里面就可以拿到了
无onBeforeCreate/onCreated,以前在这两个生命周期函数里做的事可直接在setup里用
Provide函数和Inject函数
声明provide(“name”,xxx) 后代组件中使用 inject(“name”),共享ref响应式数据也是可以同步改变的
watch和watchEffect
watch(xxx,(newV, oldV)=>{})
监听响应式对象,对象属性改变会被监听到。也可传函数返回对象。也可传数组同时监听多个
watchEffect 会自动执行传入的函数,自动收集依赖,当依赖的变量变化时自动执行
停止监听 保存watchEffect的返回值,返回值() 调用 即可停止监听
在setup语法糖中 props
const props = defineProps({})
const emit = defineEmits([])
setup语法糖中默认不暴露组件的方法,如果父组件想拿到子组件的DOM然后使用它的方法
defineExpose({想暴露出去的属性/方法}) 父组件就可以使用实例的属性/方法
路由主要维护的是一个映射表
history接口是HTML5新增的,它有6种模式改变URL而不刷新页面:
replaceState 替换原来的路径
pushState 使用新的路径
popState 路径的回退
go 向前或向后改变路径
forward 向前改变路径
back 向后改变路径
useRoute() 拿到当前活跃的路由对象 route对象默认是响应式的
懒加载 ()=>import(xxxx) 打包时就会进行分包处理,引入时可以配置魔法注释,webpack在打包时会识别,让组件在打包后有名字,webpackChunkName(webpack3开始有的功能)
动态路由 /user:id 在route的params里拿数据
跳转路由时可以在query属性里传递参数,会在地址栏中以query string的方式传递(查询字符串) $route.query 可以拿到
404路由 path 设为 /:pathMatch(.),最后面的*,加上会按/解析为数组
.push和replace,push可以回退
动态添加路由
管理后台里可以根据权限生成菜单,一些人偷懒可能只是隐藏菜单的显示,但其实已注册
router.addRoute(parentName, {path,component})
相同name后添加的会覆盖
删除路由:router.removeRoute(‘name’)
另外addRoute会返回一个值,它是一个函数,调用它就会删除这个路由
router.hasRoute() 检测某个路由是否存在
router.getRoutes()获取所有路由信息
路由导航守卫
全局前置守卫 router.beforeEach()
Vuex 状态管理
State、Getters、Mutations、Actions、Modules 5个核心概念
状态是响应式的,在setup函数中通过useStore来获取store,修改store里的状态需要通过
提交【commit】Mutation修改(Pinia里已经取消)便于vue-devtools跟踪状态
Vuex使用单一状态树,用一个对象就包含了全部的应用层级的状态(SSOT,单一数据源),和module不冲突
Pinia可以创建多个store实例
在计算属性映射状态,使用…mapState([xxx,aaa,bbb]) 如果与当前组件的data属性名称冲突,可传入对象{newName:state=>state.name} 但在Vue3的CompositionAPI中使用不方便。所以3推荐使用Pinia
getters 可以对状态进行一些处理 然后返回,使用时$store.getters.xxx(类似计算属性)
mapGetters 映射getters
更改状态的唯一方法是提交mutation,里面不可有异步操作,否则追踪不到数据变化
mapMutations在methods里面解构,直接使用
actions
它不直接改变状态而是提交mutation
它有一个非常重要的参数“context”,和store实例有相同方法的对象,里面有commit方法、state属性、getters属性,但它并不是store对象
通过dispatch派发,它也有辅助函数mapActions
有一种设计方案将一些网络请求放到vuex中管理,然后在分模块。
异步函数自动返回promise,所以在派发action的时候,如果是个异步请求的方法就可以通过then方法拿到action的执行结果
modules 拿数据的时候要从模块名上拿,派发和提交时还是直接提交即可
默认情况模块内部的action和mutation和getter是注册在全局命名空间中,如果希望使模块拥有自己的命名空间需添加namespaced:true
Pinia
让它用起来更像组合式API
与TS一起使用时具有可靠的类型推断支持
相比vuex,mutations不再存在,它经常被认为非常冗长,当初带来了devtools集成,但现在不是问题。更友好地TS支持,不再有modules的嵌套结构,可以灵活的使用每一个store,他们通过扁平化的方式相互使用。不再有命名空间的概念,不需要记住他们复杂的关系
它有三个核心概念,state、getters、actions相当于组件的data、computed、methods
defineStore定义Store
结构出来的时候无响应式,可以用toRefs包裹或者pinia提供的storeToRefs
可直接修改state,store的$reset()可以重置所有状态为初始值,$state 替换state为新对象、$patch({})一次修改多个状态
action里面可以直接用this给state赋值
axios
多种请求方式:
axios(config)
axios.request(config)
axios.get(url [,config])
axios.post(url [,data [,config]])
axios.delete(url [,config])
axios.head(url [,config])
axios.put(url [,data[,config]])
axios.patch(url [,data[,config]])
axios.all 可以放入多个请求数组,返回结果是个数组可以用axios.spread将数组展开
URL查询对象 params:{id:1}
查询对象序列化函数 paramsSerializer:function(params){ }
request body data:{key:’abc’}
vue 项目
1.创建项目 npm init vue@latest
2.项目配置 配置项目icon、配置项目标题、配置jsconfig.json
3.项目目录结构划分
src / assets 存放图片、css、font等静态资源
src / components 存放一些通用的组件
src / hooks 抽取的公共代码逻辑
src / mock 模拟一些数据
src / router 路由
src / service 网络请求 request文件夹里封装axios modules文件夹里是各个模块的请求
src / stores 状态管理
src / utils 工具
src / views 页面
4.css样式重置
normalize.css 和 reset.css 都要用
5.配置路由、状态管理
使用pinia 创建index.js 使用createPinia 在main中use。再创建文件夹modules,里面是各个store
6.创建页面 views里创建页面,每个页面一个文件夹,可以起名或者index.vue,但多个index可能不便于查找,名称使用大驼峰或者小写+杠
在webpack中[动态]加载本地图片用require
在vite中需要合成一个URL,new URL(‘../../assets/img/xxx’,import.meta.url) // 获取当前文件所处的路径,拼接url
修改第三方UI库的类时,因为作用域不同,导致选择器无法找到。可以通过vue的deep方法穿透 :deep(.leiming){xxx} 找到子组件的类
当一个组件中网络请求比较多,而且它还要给多个子组件传值的时候,就可以将这些网络请求抽取到store里面,以前用vuex的时候还要抽到module里面,比较复杂,但pinia很方便
前端时间处理库-Day.js与Moment.js
tree shaking 摇树 将没用到的东西摇下来减少包的体积
如何触发拉到底部自动加载数据的方法:
document.documentElement.scrollTop 获取已滚动的值
document.documentElement.clientHeight 获取文档高度
document.documentElement.scrollHeight 获取滚动条总共能够滚动的长度
当scrollHeight <= scrollTop + clientHeight(当前文档的高度) 时,说明拉到底了
将此功能抽取到hooks,监听频次比较高,可以用节流
组件卸载时记得移除监听
reactive解构后无响应式,ref可以
watch只监听响应式数据
不能监听:数组通过索引修改数组元素、对象属性增加/删除、计算属性
防抖:一直触发,最后一个才会触发,连续点击一个,只有最后一次有效
节流:一直触发,每隔一段时间可以触发一次
除了面试手写,真正项目用第三方提供的,如:underscore、lodash
throttle 节流
pxtovw
vite/webpack -> postcss -> plugins -> postcss-px-to-viewport 打包的时候就将px转vw
npm i postcss-px-to-viewport -D // 过时了 用8 版本
vite使用rollup打包的
Vue高级语法
通常情况下,需要对DOM元素进行底层操作时可以用到自定义指令
局部指令:组件内通过directives选项
全局指令:app的directive方法
自定义指令(为了一个功能更好的复用)它里面有生命周期函数,beforeMount mounted beforeUpdate updated beforeUnmount unmounted 定义一个全局指令
做一个为输入框自动获取焦点的功能
directives:{
focus:{
mounted(el){
el.focus()
}
}
}
指令的生命周期
created 在绑定元素的attribute 或事件监听器被应用之前调用
beforeMount 当指令第一次绑定到元素并且挂在父组件之前调用
mounted 在绑定元素的父组件被挂载后调用
beforeUpdate 在更新包含组件的VNode之前调用
updated 在包含组件的VNode及其子组件的VNode更新后调用
beforeUnmount 在卸载绑定元素的父组件之前调用
unmounted 当指令与元素解除绑定且父组件已卸载
v-xxx:mmm="vvv”
mmm是可以传参的
内置组件
teleport 传送门,类似react的Portals
它有两个属性:to 要移动到的目标元素,可使用选择器,disabled 是否禁用teleport的功能
suspense
实验性特性
加载一些异步组件时(比如懒加载)由于网络原因可能需要等待,就可以先试用一个替代组件展示,加载完成后再替换回来,比如展示loading
<suspense>
<template #default>
要加载的组件
</template>
<template #fallback>
<loading />组件
</template>
</suspense>
Vue插件
app.use()就是在安装插件,里面只能传对象/函数
传入的对象里面必须有个install方法,接收参数app。use方法就是执行传入的install方法
它很强大,能够添加全局方法、全局资源 指令、过滤器、过渡等,全局mixin添加一些组件选项等等
h函数
Vue推荐在绝大多数情况下使用模板来创建你的HTML,然后一些特殊场景,你真的需要JavaScript的完全编程的能力,这个时候你可以使用渲染函数,它比模板更接近编译器
Vue在生成真实的DOM前,会将我们的节点转换成VNode,而VNode组合在一起形成一棵树结构,就是虚拟DOM
事实上之前编写的template中的HTML最终也是使用渲染函数生成对应的VNode
如果想充分利用JS的编程能力,可以自己编写createVNode函数,生成对应VNode
那么怎么做?
使用h函数,h函数是用于创建VNode的一个函数
其实更准确的命名是createVNode,但是为了简便在Vue中就是h函数,Vue中这两个函数是一样的,都存在
它接收3个参数,标签名、属性、内容
“div”, {class:”xxx”},[ 子元素 ]
一个组件要有template标签,如果没有就要有个render函数,render函数返回h函数创建的节点
render函数是一个组件的选项,通过optionsAPI的方式写
export default{
render(){
return h(“div”, {}, [])
}
}
在CompositionAPI中使用
setup(){
xxx
return ()=> h()
}
在语法糖中 需要在template里面用一下<render>
标签
语法糖中定义redner函数返回函数 返回h方法
jsx语法
如果想在项目中使用jsx,需要添加对jsx的支持,通常使用babel转换,对于Vue来说只需要在babel中配置对应插件即可。(template使用vue-loader转换的)
webpack环境:
npm i @vue/babel-plugin-jsx -D
在babel.config.js配置
module.exports = {
presets:[‘@vue/cli-plugin-babel/preset’],
plugins:[“@vue/babel-plugin-jsx”]
}
如果是vite环境需要安装
npm i @vitejs/plugin-vue-jsx -D
然后就可以写jsx了
render函数中返回jsx即可 不需要h函数了
render(){
return( <div>aaa</div> )
}
好处就是能用一些方法了,比如map啥的,以前在template里只能用v-for
<script lang="jsx”>
这里面插入js代码时使用{}不要双大括号
过渡动画
react本身没有提供任何动画相关API,所以在react中要使用第三方库react-transition-group
Vue中提供了内置API
它并不是写好动画,只是在合适的时间帮你添加进对应的类
设置进入动画
.v-enter-from{初始状态 比如 透明度为0}
.v-enter-active { 进入过程中,用来写动画的地方 transition xxx xxx }
.v-enter-to { 结束状态 比如 透明度为1 }
然后用<transition></transition>
标签将元素包裹
离开动画是-leave-
可以对元素、组件添加进入/离开过渡,条件渲染if/show、动态组件
给组件加,默认加到组件的根节点上
transition 标签增加name属性 就可以不用v-了 可自定义
也可以使用关键帧动画,直接-active 里面设置animation即可 不需要在定义form和to了
mode="in-out/out-in" 设置先后,appear 首次出现也有动画
<transition-group>
来包裹列表,该列表中添加、删除数据也有动画,默认情况下它不会渲染一个元素的包裹器,可以添加属性tag="div” 让一个div包裹这些列表,不能使用mode属性,里面的循环必须加key,动画会被应用到所有列表元素中
.v-move
响应式原理
什么是响应式?
数据改变页面自动刷新(或者说数据改变,其他一段代码自动重新执行)
vue2/3响应式原理
DevOps开发模式
是Development和Operations两个词的结合,将开发和运维结合起来
伴随DevOps一起出现的两个词就是持续集成CI、持续交付(手动部署)(持续部署自动部署)CD
持续集成服务器jenkins,监控git代码仓库的改变,然后打包、测试,反馈给前端开发人员
git bash 比 cmd好用,自带ssh