Vue系列学习指南
2024-02-21 11:47:00 0 举报
AI智能生成
登录查看完整内容
Vue系列学习指南
作者其他创作
大纲/内容
通过Vue.use来注册插件(如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入)
一开始不给src添加值 这样渲染的时候不加载图片判断图片是否在可视区域 根据滚动事件判断 记得节流是的话通过new Image()请求图片 核心再设置setAttribute
let { top } = this.el.getBoundingClientRect(); return top < window.innerHeight
判断是否在可视区域
加载图片
懒加载原理
声明一个ReactiveListener类,一个图片相当于ReactiveListener一个实例
只要通过v-lazy指令就会创建一个实例,然后push到listenerQueue队列,实例具有checkInView判断该实例是否在可视区域,load请求加载图片
监听最外层父级dom滚动事件,触发scroll事件时,其回调函数就会调用checkInView,如果为true则调用load进行加载图片
vue-lazyload插件原理
vue-lazyload
Vue插件开发
v-for和v-if连用问题
从AST转化成JS语法的时候内部会调用一个_l的函数,参数为n表示循环几次
v-for
v-if 如果条件不成立不会渲染当前指令所在节点的 dom 元素
从AST转化成JS语法的时候v-if会被转化为三元表达式
v-if
v-show 只是通过样式切换当前 dom 的显示或者隐藏
v-show
v-model本质就是一个语法糖,可以看成是value + input方法的语法糖。 可以通过model属性的prop和event属性来进行自定义。原生的v-model,会根据标签的不同生成不同的事件和属性
就是一个语法糖 解析成value和事件 挂载到propsData,被当成组件的props属性(一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件)
应用在组件上
真正的一个指令
应用在标签上
v-model
v-on:绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。缩写:@
vm.$on:监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。
注意不要跟$on混淆
v-on
v-bind:动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。缩写::
主要是通过ast语法解析,实现的将对应属性转化成变量
v-bind
概述:我们可以自定义vue中的指令来实现功能的封装 (全局指令、局部指令)
bind:只调用一次,指令第一次绑定到元素时调用
inserted:被绑定元素插入父节点时调用
unbind:只调用一次,指令与元素解绑时调用
指令定义对象可以提供如下几个钩子函数:
自定义指令
指令
nextTick批量异步更新策略,一句话概括在下次DOM更新循环结束之后执行延迟回调。它主要是为了解决:例如一个data中的数据它的改变会导致视图的更新,而在某一个很短的时间被改变了很多次,假如是1000次,每一次的改变如果都都将促发数据中的setter并按流程跑下来直到修改真实DOM,那DOM就会被更新1000次,这样的做法肯定是非常低效的。
而在目前浏览器平台并没有实现nextTick方法,所以Vue.js 源码中分别用 Promise、Observer、setTimeout、setImmediate 等方式定义了一个异步方法nextTick,它接收的是一个回调函数,多次调用nextTick会将传入的回调函数存入队列中,当当前栈的任务都执行完毕之后才来执行这个队列中刚刚存储的那些回调函数,并且通过这个异步方法清空当前队列。
let observe = new MutationObserver(flushCallbacks)observe.observe 采用文本节点 文本节点内容一变化 触发flushCallbacks回调
监听dom变化,是个微任务
MutationObserver
$nextTick
computed计算属性
watch用于监控用户的data变化,数据变化后会触发对应的watch的回调方法watch属性实现是合并配置,将对象形式、数组形式、字符串形式统统转化为key-value形式内部调用$watch方法,这个方法会创建一个用户watcher,传入user为true的配置watcher存在 get 方法。而watch属性传入的getter方法是key值,会内部将这个key转化为函数,目的是触发依赖收集当这个data变化时候,再触发callback
computed 和 watch 都是基于 Watcher来实现的,computed是具有缓存特性 (默认不执行)只有当取值时执行,内部依靠dirty变量实现缓存效果。 watch则是默认会执行一次,属性值变化后触发回调函数
Vue 中 computed 和 watch 的区别?
$watch
如果修改的是数组对象,则通过splice来进行修改
$set
每个组件就是一个vue实例,实例下都有一个对应的渲染watcher属性_watcher,触发vm._watcher.update就会更新页面
$forceUpdate
自定义事件基于发布订阅实现,然后是存在实例的_events对象上的(vm就是一个调度中心)
$on、$emit...
实例属性和方法
动态组件
路由组件
用途:主要用于保留组件状态(比如离开一个页面再回来,组件会被重新创建)或避免重新渲染
keep-alive是Vue.js的一个内置组件(实际所有组件都是options配置对象)。它能够不活动的组件实例保存在内存中,而不是直接将其销毁,它是一个抽象组件,不会被渲染到真实DOM中,也不会出现在父组件链中。
它提供了include与exclude两个属性,允许组件有条件地进行缓存 include表示白名单 exclude表示黑名单
保存组件缓存实际就是保存其对应的虚拟dom节点就行
分支主题
LRU ( Least Recently Used :最近最少使用 )缓存淘汰策略,故名思义,就是根据数据的历史访问记录来进行淘汰数据,其核心思想是 如果数据最近被访问过,那么将来被访问的几率也更高 ,优先淘汰最近没有被访问到的数据。
判断缓存中是否已缓存了该实例,缓存了则直接获取,并调整 key 在 keys 中的位置(移除 keys 中 key ,并放入 keys 数组的最后一位)
如果没有缓存,则缓存该实例,若 keys 的长度大于 max (缓存长度超过上限),则移除 keys[0] 缓存
如果配置了 max 并且缓存的长度超过了 this.max,还要从缓存中删除第一个
在 keep-alive 缓存超过 max 时,使用的缓存淘汰算法就是 LRU 算法,它在实现的过程中用到了 cache 对象用于保存缓存的组件实例及 key 值,keys 数组用于保存缓存组件的 key ,当 keep-alive 中渲染一个需要缓存的实例时:
我明白了 因为这里vue是用了数组所以如果直接index==>0 表示头 那么每次都unshift头部会牵一发而动全身所以这里是这样理解的 数组尾部表示优先级最高的 头部是优先级最低的 溢出就会被淘汰
存在max原因,所以该组件内部采用LRU淘汰缓存算法来控制最大缓存数量
原理
keep-alive
全局组件
主要调用mergeOptions方法合并options配置
mixin
基于原型式继承也就是Object.create继承Vue构造函数,并且调用mergeOptions合并配置,返回一个新的Vue子类
extend
同vm.$set
set
同vm.$nextTick
nextTick
安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入
当 install 方法被同一个插件多次调用,插件将只会被安装一次
用途
use
全局API
beforeCreate 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
created 实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。这里没有$el
beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用。
mounted el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
beforeUpdate 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
updated发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新
beforeDestroy 实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。
生命周期
概述:Vue的编译过程就是将template转化为render函数的过程,会经历三个阶段
生成AST树
优化
codegen(将优化后的AST树转换为可执行的代码)
模板编译
观察者模式
发布订阅模式
设计模式
v-if 是 真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。
v-if 和 v-show 区分使用场景
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
computed 和 method 区分使用场景
在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff 。
v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成 computed 属性
v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
Vue 会通过 Object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止 Vue 劫持我们的数据呢?可以通过 Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。
虚拟列表进行优化
长列表性能优化
事件的销毁
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vue 的 vue-lazyload 插件
图片资源懒加载
路由懒加载
一、代码层面的优化
Vue项目如何做性能优化
视图(View):用户界面。
控制器(Controller):业务逻辑
模型(Model):数据保存(数据库访问)
简单理解就是:(用户)View 传送指令到 Controller==>Controller 完成业务逻辑后,要求 Model 改变状态==>Model 将新的数据发送到 View,用户得到反馈
MVC
概述:在 MVVM 模式中,View(视图) 和 Model(数据) 是不可以直接通讯的,在它们之间存在着 ViewModel 这个中间介充当着观察者的角色。当用户操作 View(视图),ViewModel 感知到变化,然后通知 Model 发生相应改变;反之当 Model(数据) 发生改变,ViewModel 也能感知到变化,使 View 作出相应更新。这个一来一回的过程就是我们所熟知的双向绑定。
Model模型层,泛指后端进行的各种业务逻辑处理和数据操控,主要围绕数据库系统展开(ajax数据)
View视图层:负责将数据模型转化为UI展示出来,可以简单的理解为HTML页面
ViewMode视图模型层:用来连接Model和View,是Model和View之间的通信桥梁(vue的核心)
数据绑定(英語:Data binding)是将“提供器”的数据源与“消费者”绑定并使其同步的一种通用技术。 这通常用两种不同语言的数据/信息源完成,如XML数据绑定。 在UI数据绑定中,相同语言但不同逻辑功能的数据与信息对象被绑定在一起(例如Java UI元素到Java对象)。
数据绑定
注意这里的双向绑定功能指的是ViewModal<===>View,因为Modal层不是vue来实现,我们可以不管(但要注意MVVM模式双向绑定还有这种情况modal<===>ViewModal)
Vue是数据劫持和观察者模式结合的一种数据绑定方式
数据劫持+事件实现数据双向绑定
v-model只是一个语法糖,并不是等同于双向数据绑定
vue如何实现双向绑定
MVVM的主要核心就是双向绑定
MVVM
软件架构模式
基于Object.defineProperty方法,递归调用这个方法去初始化data的属性
对象的侦测
使用了函数劫持的方式,重写了数组的方法,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化
索引不会去侦测,所以官方采用vue.$set去修改数组元素才会触发更新
数组的侦测
变化侦测
数据改变,视图自动更新
什么是响应式
Vue响应式原理
我们写的组件实际就是一个options对象
首先要明白,props是相对于组件来说的
生成组件虚拟dom时会增加一个属性叫propsData存放着用户传递的数据在初始化过程中会进行属性的初始化操作。会用用户编写的props和propsData进行检测(进行校验 validateProp)。最终将属性存放到_props上。并使用proxy方法进行代理,将其代理到实例上。
props原理
父子通信基于Props实现
组件通信
Vue组件化原理
在 Web 前端单页应用 SPA(Single Page Application)中,路由描述的是 URL 与 UI 之间的映射关系,这种映射是单向的,即在无需刷新页面的情况下 URL 变化引起 UI 更新
什么是前端路由?
如何改变 URL 却不引起页面刷新?
如何检测 URL 变化了?
概述:要实现前端路由,需要解决两个核心
hash 是 URL 中 hash (#) 及后面的那部分,常用作锚点在页面内进行导航,改变 URL 中的 hash 部分不会引起页面刷新
通过浏览器前进后退改变 URL
通过<a>标签改变 URL
通过window.location改变URL
通过 hashchange 事件监听 URL 的变化,t通过hash改变 URL 的方式只有这三种:
hash
概述:(HTML API)History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。
返回一个整数,该整数表示会话历史中元素的数目,包括当前加载的页。例如,在一个新的选项卡加载的一个页面中,这个属性返回1。
History.length
返回一个表示历史堆栈顶部的状态的值。这是一种可以不必等待popstate 事件而查看状态的方式。
History.state
属性
History.forward() 在浏览器历史记录里前往下一页,用户可点击浏览器左上角的前进按钮模拟此方法. 等价于 history.go(1).
通过当前页面的相对位置从浏览器历史记录( 会话记录 )加载页面
History.go()
按指定的名称和URL(如果提供该参数)将数据push进会话历史栈,数据被DOM进行不透明处理;你可以指定任何可以被序列化的javascript对象
History.pushState()
按指定的数据,名称和URL(如果提供该参数),更新历史栈上最新的入口。这个数据被DOM 进行了不透明处理。你可以指定任何可以被序列化的javascript对象
History.replaceState()
方法
history 提供了 pushState 和 replaceState 两个方法,这两个方法改变 URL 的 path 部分不会引起页面刷新
history 提供类似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同:
通过浏览器前进后退改变 URL 时会触发 popstate 事件
通过pushState/replaceState或<a>标签改变 URL 不会触发 popstate 事件
拦截 pushState/replaceState的调用和<a>标签的点击事件来检测 URL 变化
通过js 调用history的back,go,forward方法课触发该事件
如何监听URL变化
history
两种实现方式
如何实现前端路由?
前端路由
vue-router
概述:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式Vuex 是单向数据流的一种实现。
state:驱动应用的数据源
view:以声明方式将 state 映射到视图
actions,响应在 view 上的用户输入导致的状态变化
多个组件会共享状态时,共享状态和组件间(兄弟组件)通信变的不容易。我们把共享状态抽取出来,用单向数据流的方式会变得容易。
单向数据流的使用场景
单向数据流
一图胜千言
如何使用Vuex
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新
你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用
Vuex 和单纯的全局对象有以下两点不同:
首先提供一个Store类和install方法,通过install将store注入到vue组件上,install方法通过Vue.mixin混入breforeCreate生命周期函数,然后注入store实例
暴露Store和install
Vuex有个模块类 Module
ModuleCollection收集模块类提供了getNamespace获取命名空间的方法,通过传入的path就能知道模块之间的父子关系,然后进行拼接返回就行
实现命名空间
实现模块机制
Vuex的原理是通过new Vue产生实例,达到响应式数据变化的目的
state作为data属性$$state传入,通过new Vue初始化后变成响应式数据
state
getters
实现Vuex响应式
Store类内部维护了_actions和_mutatios,我们通过这两个属性即可获取到
不同的是mutation传入的是该模块下state,而action传入的是store,所以它就可以拿到commit方法
mutations、actions的实现
Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函数,它接收 store 作为唯一参数
在new Store时,会执行插件传入store,Store里维护了_subscribers数组,用来存放插件的订阅,基于发布订阅,会在mutation触发执行时候发布事件,Store还提供了一个replace方法,用来替换state
插件机制的实现
概述:使 Vuex store 进入严格模式,在严格模式下,任何 mutation 处理函数以外修改 Vuex state 都会抛出错误。
严格模式的实现
根据传递过来的数组选项,循环遍历生成对应的key-value然后返回一个对象
辅助函数的实现
Vuex的实现原理
使用Vuex只需执行 Vue.use(Vuex),并在Vue的配置中传入一个store对象的示例,store是如何实现注入的?
内部有个installModule方法,该方法的参数是modules也就是模块树,path是记录了模块的父子关系,遍历这个模块树,根据path的父子关系,然后依次往state添加属性
state内部支持模块配置和模块嵌套,如何实现的?
Vuex如何区分state是外部直接修改,还是通过mutation方法修改的?
devtoolPlugin中提供了此功能。因为dev模式下所有的state change都会被记录下来,’时空穿梭’ 功能其实就是将当前的state替换为记录中某个时刻的state状态,利用 store.replaceState(targetState) 方法将执行this._vm.state = state 实现
调试时的”时空穿梭”功能是如何实现的?
多组件通信时可以采用Vuex,vuex缺点是无法持久化数据,可用插件解决
什么时候用vuex,vuex的缺点有哪些
Vuex常问问题
Vuex
要进行diff检测差异,操作真实DOM浪费性能。 速度快,减小了页面渲染过程的次数
Vue为什么需要虚拟DOM
什么是虚拟DOM
如果标签类型不一样: 直接替换
判断是否是文本节点 是的直接更新文本节点
比对属性
旧节点没有子元素,新节点有子元素
旧节点有子元素,新节点没有子元素
vue中调用updateChildren这个核心方法,它是这样操作的: 声明四个指针,老节点的首尾指针,新节点的首尾指针,然后进行新旧节点双端比较然后我们接着进入 diff 过程,每一轮都是同样的对比,其中某一项命中了,就递归的进入patchVnode 针对单个 vnode 进行的过程(如果这个 vnode 又有 children,那么还会来到这个 diff children 的过程
旧首节点和新首节点用 sameNode 对比
旧尾节点和新尾节点用 sameNode 对比
旧首节点和新尾节点用 sameNode 对比
旧尾节点和新首节点用 sameNode 对比
暴力比对:如果以上逻辑都匹配不到,再把所有旧子节点的 key 做一个映射到旧节点下标的 key -> index 表,然后用新 vnode 的 key 去找出在旧节点中可以复用的位置
旧节点和新节点都有子元素(核心 采用双端比较)
比对子元素
如果标签类型一样:
比对标签
vue中的diff算法由patch方法实现 传入旧节点和新节点进行同层级比对
DIFF算法
虚拟DOM及DIFF算法
发布订阅:Event的就是调度中心 只有一个类 提供发布和订阅功能 解耦
观察者模式是有两个类 观察目标observer 观察者subject 耦合 一旦观察目标变化 需要观察者做出反应
所以单纯从结构来看可以看出这两个设计模式的区别
可以认为是观察者模式的升级版
• 灵活由于订阅发布模式的发布者和订阅者是解耦的,只要引入订阅发布模式的事件中心,无论在何处都可以发布订阅。同时订阅发布者相互之间不影响。(发布订阅 订阅者不把自身this放入调度中心 而是注册回调就行,同时这也是为啥父子通信是单向的原因)
优点
容易导致代码不好维护灵活是有点,同时也是缺点,使用不当就会造成数据流混乱,导致代码不好维护
缺点
• 响应式目标变化就会通知观察者,这是观察者最大的有点,也是因为这个优点,观察者模式在前端才会这么出名。
• 不灵活相比订阅发布模式,由于目标和观察者是耦合在一起的,所以观察者模式需要同时引入目标和观察者才能达到响应式的效果。而订阅发布模式只需要引入事件中心,订阅者和发布者可以不再一处。
区别
Vue系列学习指南
0 条评论
回复 删除
下一页