vue2知识点概括

167 阅读5分钟

Vue2

特征

  1. 更好的性能
  2. 更小的包体积
  3. 更好的TypeScript集成
  4. 更优秀的API设计

MVVM模型

MVCModel-View-Controller的简称 是在前期被使用非常多的一种架构模式 比如ios 其实在前端里面 - html -> view 页面时图 **- javaScript -> controller **从服务器获取到数据

  • model -> 取到的数据 然后通过controller 来返回给view

MVVM 是 Model-View-ViewModel 的简称 是目前非常流行的架构模式 ViewModel vue 就当个中间商 来承接 Model 和 View

VNode(虚拟节点)

vue 无论组件还是元素 最终表现的都是一个个VNode 多个VNode会将组成VNode Tree(虚拟DOM/也就是JS对象) 需要DOM的作用

  • 跨平台性 1.可以渲染到真实DOM -> 渲染到浏览器上
  1. 移动端的一些控件(button/view/image)->移动原生控件在移动端显示出来
  2. 桌面端一些控件
  3. VR设备的空间
  • DIFF算法 在vue中 对于相同父元素的子节点发生了改变 并不会重新渲染整个列表 只需要操作更新的Vnode就行 如果没有设置key的话vue会使用一种最大限度减少动态元素 并且尽可能的尝试就地修改/复用相同类型元素的算法 在第一次虚拟DOM和最新的虚拟DOM做对比 它会找到有变化的Vnode的第一个的位置 之后的所有的Vnode都会重新改变 渲染。 在源码对比中是patchUnkeyedChildren方法 如果有设置key的话它会基于key的变化重新排列元素顺序 并且会移除/销毁key不存在的元素 在第一次虚拟DOM和最新的虚拟DOM做对比 它可以根据key的值来对比 这样会更快的 更高效的寻找插入的Vnode 从而在真实DOM中 只渲染这个改变的key 在源码中是patchKeyedChildren 他的查找规则
  • 从头部开始遍历 遇到相同节点就继续 遇到不同节点就跳出循环
  • 从尾部开始遍历 遇见相同节点就继续 遇见不同节点就跳出循环
  • 如果都遍历完了,依然还有新的节点 那么新的节点就做新增操作 (新节点多 就新增)
  • 如果都遍历完了,依旧还有旧的节点 那么旧的节点就做删除操作 (旧节点多 就删除)
  • 如果中间存在不知道如何排列的位置序列 那么就使用key建立索引图 最大限度的使用旧节点就是通过key来判断中间的是否有相同的key 如果有就复用旧节点 反之则进行删除/新增操作vue的diff特色 处理未知的或者乱序的节点

data属性

在vue2的时候 可以传入一个对象(官方推荐是一个函数) 在vue3的时候 必须传入一个函数

methods

为什么不可以使用箭头函数? 不使用箭头函数 this的指向是什么呢? 1.因为我们将要在methods中使用data返回对象的数据 所以说这个this必须得有值(vue里面this手动绑定了当前组件的实例) 2.因为如果使用了箭头函数 他的this查找规则是自己的上层作用域来查找this 他的上层找到了是一window 3.使用普通函数可以找到this 因为在vue源码中 它先获取到组件实例 然后判断是否有methods属性 然后便利methods 取出来每一个函数 并且手动绑定了bind函数 帮组件实例传入进去当参数

v-model

  • 本质:他就是input v-bind绑定value属性的值;v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;
  • 修饰符
  1. lazy 它会把input的input事件切换成change事件 只有提交的时(回车时)才会触发
  2. number 绑定一个number数值 并且如果是一个string类型的话 在可以转换的时候 进行隐式转换
  3. trim 自动过滤用户输入的空白字符

插槽

默认插槽:这个默认内容只会在美欧提供插入内容的时候现实 具名插槽:给插槽制定对应名字 然后希望达到插槽对应的现实

<slot name='left'></slot>
<template v-slot:left></template>
动态插槽名
<template v-slot:[dataName]></template>
data(){
 return {
    name:'left'
 }
}
具名插槽的缩写
<template #left></template>

作用域插槽 :可以访问到子组件中的内容 过程:

  1. 在父组件定好数据
  2. 通过组件slot传递给子组件
  3. 子组件遍历数据
  4. 子组件定义插槽prop
  5. 通过v-slot:default方式获取到子组件定义的prop
  6. 使用slotProps中的item和index

111.jpg

provide和inject

一些深度嵌套的组件 子组件想要获取父组件的内容 在这种情况下,如果我们仍然将props沿着组件链逐级传递下 去,就会非常的麻烦; 无论层级结构有多深,父组件都可以作为其所有子组件的依赖 提供者;

  • 父组件有一个 provide 选项来提供数据;
  • 子组件有一个 inject 选项来开始使用这些数据;
  • 父组件不需要知道哪些子组件使用它 provide 的 property
  • 子组件不需要知道 inject 的 property 来自哪里
provide (){
    return {
        name:this.name
    }
}
这样写 是吧 data 的 name 值绑定给 provide 属于值绑定值 当 data 里面的 name 发生改变的话 provide 不会改变活着直接传入this
provide (){
    return {
        name:this
    }
}
这时候就需要引入 import { computed} from 'vue'
provide (){
    return {
        name:computed()=>{
            return this.message
        }
    }
}

其他组件使用时候
inject :['name']

全局事件总线

vue3从实例中移除了onon off $once方法 如果我还要希望使用全局事件总线 就需要通过第三方库 Vue3官方有推荐一些库,例如 mitt 或 tiny-emitter;

通过事件总线eventBus 来实现跨组件监听事件 当组件被销毁的时候 需要移除监听
可以使用mitt库

事件发送
eventBus.emit('click','why',12,15)

事件监听
created (){
eventBus.on('click',this.eventFun
}
methods:{
  eventFun(name,age){
console.log(name,age)
  }
}
事件移除 
unmounted:{
 eventBus.off('click',this.eventFun)
}

生命周期钩子函数

概念:每一个组件都会经历创建 挂在 更新 卸载等一些列操作

  1. 创建home组件
  2. 将组件挂在到组件树上(虚拟DOM上)
  3. 修改data中的数据 组件被更新
  4. 组件卸载

根据vue官方生命周期的流程图解析 当我们创建了组件的时候 也就是app=Vue.createApp(options) app.mount(el)

  • 初始化vue 内部自己初始化工作 (也就是内部自己的准备工作)
  • beforeCreate vue打算创建组件了 可以做一些创建之前的操作
  • created 组件实例已经被创建好了 但是还没有挂在到虚拟DOM上面 因为它只是在创建实例 但是没有对里面的模版进行编译 组件对象本身会有个render函数 他会返回组件内部自身的vnode节点这个时候他就会判断是否有 template 如果有template的话 他会创建对应的vnode 如果没有 它会编译里面el对应的东西的innerHtml 如果在el里面还有render函数的话 他也会执行render函数
  • beforeMount 准备去吧Vnode挂在到虚拟DOM上
  • mounted 已经把Vnode挂在到了虚拟DOM上面 这个时候会根据特定的算法 挂在道真实DOM上 用户已经可以看到所有元素了
  • beforeUpdate 当挂在上的数据发生了改变 他会先回调beforeUdate函数 然后根据改变的新生成的Vnode 然后通过diff算法进行对比 然后进行更新
  • updated 真实dom已经跟新修改完毕
  • beforeUmount 当发现组件不再使用了 准备卸载之前回调
  • umounted 已经从虚拟dom中移除掉了 然后回调这个函数

自我总结

  • beforeCreate 创建组件实例
  • created 拿到template 模版进行编译 因为需要拿到vnode(如果使用了 vue-loader 它会直接创建好对应的render函数 「它会吧里面的标签元素直接创建好 使用的是createVnode函数{在vue3中他是创建的createElementBlack()函数 进行了优化}」
  • beforeMount 挂在到虚拟DOM
  • mounted 根据虚拟DOM生成了真实DOM -> 界面可以看到元素标签
  • beforeUpdate 挂在上面的数据发生了改变
  • updated 根据最新生成的新的vnode生成新的虚拟DOM 生成真实DOM
  • beforeUmount 当组件不再使用 v-if=false
  • unmounted 将之前挂载在虚拟DOM中的Vnode从虚拟DOM移除 将会把对应的真实DOM移除

created 一般会做 他不会获取到 dom 1.发送网络请求 2.事件监听(evebtbus)/监听数据(this.$watch()) mounted 当前元素已经被挂载了 1.可以获取到 DOM 并且使用 DOM unmounted 一般回收的操作(取消事件监听)

$refs

在 vue 中不推荐原生 DOM 操作 这个时候可以给组件绑定一个 refs 属性 注意事项 在同一个组件 设置了相同的ref多次使用的话 获取到的不是同一个组件实例(instance) 因为vue内部其实做了吧组件当成了一个对象的形式导出创建,其实他在内部吧对象当成了一个class类的方式来实现因为class类每一次创建都是一个新的)这就会实现每一个相同的组件 他都是不同的组件实例 通过refs获取到的也是不同的(在react中已经把组件通过class的继承方式来的 但是vue还是按照对象模拟class的方式) 如果通过 ref 拿到子组件的元素的时候 他会获取子组件的根元素 如果子组件会有多个根组件的话 他会获取到第一个 node 节点 this.refs.banner.refs.banner.el

动态组件 component

    is 来源的组件
    1.全局注册的组件(app.compoents())
    2.局部注册的组件
<component :is="tab[icuretndex]"></component>
tab:['home','about','']

keep alive

当前组件切换继续存活 实例不会被销毁 并且被缓存起来 include->{String|RegExp|Arry(['a','b'])} 包含 那些组件被缓存 它来源于组件定义时的name属性 它不是跟组件注册名称一致 (name第二个用处就是跟着vue-devtools有关系) exclude 不被缓存 max 最大缓存多少个组件 它vue内部有个算法 当超出最大值的时候 它会将长时间不使用的组件 移除缓存

<keep-alive include='home,about'>
</keep-alive>
对于保持keep-alive的组件 监听有没有进行切换 他有自己的生命周期
进入活跃的状态钩子函数 进入
activated(){

}
离开活跃的时候状态钩子 离开
deactivated(){

}

异步组件

dist 文件夹的目录分析

  • webpack 代码分包
  • app 业务代码
  • chunk-vendors 第三方插件依赖的代码 都打包到这个js中
  • 在dist中打包后编译的文件 后缀为js.map为映射的源代码

为什么要分包 因为vue会默认编译打包把所有的业务代码文件打包都放到静态服务器上 等页面下载的时候 虽然页面显示的是home组件 但是也得需要等当前这个文件的全部下载下来 才会执行js代码 这就回导致首屏渲染速度过慢的问题。所以我们吧组件进行拆分成小的代码文件


使用import("./main.js").then(res=>{

}) 
这种就是异步导入  并且可以让webpack对导入的文件进行分包处理

因为在注册组件的时候 不能把promise注册到component里面 进行注册
在业务使用的时候 vue提供了defineAsyncComponent
类型一:工厂函数,该工厂函数需要返回一个Promise对象;
类型二:接受一个对象类型,对异步函数进行配置;

1.
const AsyncComponent =defineAsyncComponent(//返回一个promise(可以使用工厂函数) ()=>{import('./main.js')}) 

222.jpg

组件上的v-model (默认的modelValue)

当返回的时候 可以修改父组件的值 <componets :modelValue='oppCounter' @update:modelValue="oppCounter=$event">

可以自定义修改传入的默认值 也可以传入多个 <componets v-model:counter='oppCounter' v-model:name='name'

Mixin 组件的混入

import messageMixin from './mixin' mixins:[messageMixin] 合并规则 1.如果都有data 默认情况是合并到一起 在data属性名字冲突 会优先使用自身组件 2.如果有都有生命周期 他会两个生命周期都会执行