说一下对vue的理解?
web是World Wide Web的简称,我们把她划分成四个时代来讲解
石器时代:静态页面,可以理解为就是一张可以在网上浏览的报纸,没有数据库
文明时代:微软出现ASP和java,增强了web和服务端的交互的安全性,其实就是java+HTML,jsp有一个很大的缺点,就是不太灵活
工业革命时代:移动设备普及,SPA雏形,Backbone EmberJS AngularJS 这样一批前端框架随之出现,通过几年的飞速发展,节约了开发人员经历,降低了开发者和发展过程的门槛
百花齐放时代:各种前端框架不断出现
vue是什么?
Vue.js(/vjuː/,或简称为Vue)是一个用于创建用户界面的开源JavaScript框架,也是一个创建单页应用的Web应用框架,vue关注的核心是MVC模式中的视图层,他方便的获取数据更新,通过组件内部特定的方法实现视图于模型的数据更新
vue的核心特性:
数据驱动(MVVM):MVVM
表示的是 Model-View-ViewModel
- Model:模型层,负责处理业务逻辑以及和服务器端进行交互
- View:视图层:负责将数据模型转化为UI展示出来,可以简单的理解为HTML页面
- ViewModel:视图模型层,用来连接Model和View,是Model和View之间的通信桥梁
组件化:
就是把图形、非图形的各种逻辑均抽象为一个统一的概念来实现开发的模式
降低整个系统的耦合度,在保持接口不变的情况下,可以替换不同的组件快速完成需求
调试方便,组件之间低耦合
提高可维护性,组件职责单一,组件在系统中是被复用的,所以对代码及逆行优化获得系统整体升级
常见的指令:
- 条件渲染指令
v-if
- 列表渲染指令
v-for
- 属性绑定指令
v-bind
- 事件绑定指令
v-on
- 双向数据绑定指令
v-model
vue和传统开发的区别?
vue所有的界面事件,都是去操作数据的,jquery操作DOM
vue所有界面的变动,都是根据数据自动绑定出来的,jquery操作DOM
vue和react的对比
相同点:
都有组件化思想
都支持服务器端渲染
都有虚拟dom
数据驱动视图
都是单项数据流
都有支持native的方案,vue的weex、React的react native
都有自己的构建工具:vue的vue-cli React的Create React App
区别:
- 都是单项的数据流,只不过vue采用双向数据绑定(这是一个误区)
- 数据变化的实现原理不同。
react
使用的是不可变数据,而Vue
使用的是可变的数据 - 组件化通信的不同。
react
中我们通过使用回调函数来进行通信的,而Vue
中子组件向父组件传递消息有两种方式:事件和回调函数 - diff算法不同。
react
主要使用diff队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM。Vue
使用双向指针,边对比,边更新DOM
你对SPA单页面的理解,优缺点?如何实现SPA应用呢?
SPA翻译过来就是单页面应用,是一种网络应用程序或网站的模型,通过动态重写当前页面于用户交互,避免了页面之间切换打断用户体验在单页应用中,所有必要的代码都通过单个页面加载而检索
SPA和MPA的区别?
MPA翻译过来就是多页面应用,每个页面都是一个主页面,都是独立的
单页面应用(SPA) | 多页面应用(MPA) | |
---|---|---|
组成 | 一个主页面和多个页面片段 | 多个主页面 |
刷新方式 | 局部刷新 | 整页刷新 |
url模式 | 哈希模式 | 历史模式 |
SEO搜索引擎优化 | 难实现,可使用SSR方式改善 | 容易实现 |
数据传递 | 容易 | 通过url、cookie、localStorage等传递 |
页面切换 | 速度快,用户体验良好 | 切换加载资源,速度慢,用户体验差 |
维护成本 | 相对容易 | 相对复杂 |
单页应用的优缺点:
优点:
具有桌面应用的即时性,网站的可移植和可访问性,
用户体验好,快,内容的改变不需要重新加载整个页面
良好的前后端分离,分工明确
缺点:
不利于搜索引擎的抓取
首次渲染速度相对于较慢
原理
- 监听地址栏中
hash
变化驱动界面变化 - 用
pushsate
记录浏览器的历史,驱动界面发送变化
v-show和v-if有什么区别?使用场景?
共同点:
都是用来控制元素在页面显示隐藏
区别:
控制手段:v-show隐藏是为元素添加一个css display:none,dom元素依然存在,v-if显示隐藏是将dom元素整个添加或者删除
编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适的销毁和重建内部的事件监听和子组件,v-show只是简单的基于css的切换
编译条件:v-if是真正的条件渲染,他会确保在切换过程中条件块内的事件监听器和子组件适当的被销毁和重建,只有渲染条件为假,并不做操作,直到为真才渲染
性能消耗:v-if有更高的切换消耗,v-show有更高的初始化渲染消耗
v-show原理:
不管初始条件是什么,元素总会被渲染
v-if原理
v-if在实现上比v-show要复杂的多,因为还有else-if等条件需要处理
使用场景
v-if和v-show都能控制dom元素在页面的显示
v-if相比v-show开销更大(直接操作dom节点增加与删除)
如果我们非常频繁的切换,则使用v-show较好
如果在运行条件很少改变,则使用v-if较好
vue实例挂载的过程
-
new Vue
的时候调用会调用_init
方法- 定义
$set
、$get
、$delete
、$watch
等方法 - 定义
$on
、$off
、$emit
、$off
等事件 - 定义
_update
、$forceUpdate
、$destroy
生命周期
- 定义
-
调用
$mount
进行页面的挂载 -
挂载的时候主要是通过
mountComponent
方法 -
定义
updateComponent
更新函数 -
执行
render
生成虚拟DOM
-
_update
将虚拟DOM
生成真实DOM
结构,并且渲染到页面中
说一下你对vue生命周期的理解?在create和mounted这两个生命周期中请求数据有什么区别?
生命周期(Life Cycle)
的概念应用很广泛,特别是在政治、经济、环境、技术、社会等诸多领域经常出现,其基本涵义可以通俗地理解为“从摇篮到坟墓”整个过程在Vue
中实例从创建到销毁的过程就是生命周期,即指从创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程
Vue生命周期总共可以分为8个阶段:创建前后, 载入前后,更新前后,销毁前销毁后,以及一些特殊场景的生命周期
生命周期 | 描述 |
---|---|
beforeCreate | 组件实例被创建之初 |
created | 组件实例已经完全创建 |
beforeMount | 组件挂载之前 |
mounted | 组件挂载到实例上去之后 |
beforeUpdate | 组件数据发生变化,更新之前 |
updated | 组件数据更新之后 |
beforeDestroy | 组件实例销毁之前 |
destroyed | 组件实例销毁之后 |
activated | keep-alive 缓存的组件激活时 |
deactivated | keep-alive 缓存的组件停用时调用 |
errorCaptured | 捕获一个来自子孙组件的错误时被调用 |
具体分析
beforeCreate -> created
- 初始化
vue
实例,进行数据观测
created
- 完成数据观测,属性与方法的运算,
watch
、event
事件回调的配置 - 可调用
methods
中的方法,访问和修改data数据触发响应式渲染dom
,可通过computed
和watch
完成数据计算 - 此时
vm.$el
并没有被创建
created -> beforeMount
- 判断是否存在
el
选项,若不存在则停止编译,直到调用vm.$mount(el)
才会继续编译 - 优先级:
render
>template
>outerHTML
vm.el
获取到的是挂载DOM
的
beforeMount
- 在此阶段可获取到
vm.el
- 此阶段
vm.el
虽已完成DOM初始化,但并未挂载在el
选项上
beforeMount -> mounted
- 此阶段
vm.el
完成挂载,vm.$el
生成的DOM
替换了el
选项所对应的DOM
mounted
vm.el
已完成DOM
的挂载与渲染,此刻打印vm.$el
,发现之前的挂载点及内容已被替换成新的DOM
beforeUpdate
- 更新的数据必须是被渲染在模板上的(
el
、template
、render
之一) - 此时
view
层还未更新 - 若在
beforeUpdate
中再次修改数据,不会再次触发更新方法
updated
- 完成
view
层的更新 - 若在
updated
中再次修改数据,会再次触发更新方法(beforeUpdate
、updated
)
beforeDestroy
- 实例被销毁前调用,此时实例属性与方法仍可访问
destroyed
- 完全销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器
- 并不能清除DOM,仅仅销毁实例
使用场景
生命周期 | 描述 |
---|---|
beforeCreate | 执行时组件实例还未创建,通常用于插件开发中执行一些初始化任务 |
created | 组件初始化完毕,各种数据可以使用,常用于异步数据获取 |
beforeMount | 未执行渲染、更新,dom未创建 |
mounted | 初始化结束,dom已创建,可用于获取访问数据和dom元素 |
beforeUpdate | 更新前,可用于获取更新前各种状态 |
updated | 更新后,所有状态已是最新 |
beforeDestroy | 销毁前,可用于一些定时器或订阅的取消 |
destroyed | 组件已销毁,作用同上 |
数据请求在created和mouted的区别
created
是在组件实例一旦创建完成的时候立刻调用,这时候页面dom
节点并未生成;mounted
是在页面dom
节点渲染完毕之后就立刻执行的。触发时机上created
是比mounted
要更早的,两者的相同点:都能拿到实例对象的属性和方法。 讨论这个问题本质就是触发的时机,放在mounted
中的请求有可能导致页面闪动(因为此时页面dom
结构已经生成),但如果在页面加载前完成请求,则不会出现此情况。建议对页面内容的改动放在created
生命周期当中
为什么vue中的v-if和v-for不建议一起使用?
v-if指令用于条件性的渲染一块内容,这块内容只会在指令的表达式返回为true值时候被渲染
v-for指令基于一个数组来渲染一个列表,v-for指令需要使用item in items形式的特殊语法,其中 items
是源数据数组或者对象,而 item
则是被迭代的数组元素的别名
在 v-for
的时候,建议设置key
值,并且保证每个key
值是独一无二的,这便于diff
算法进行优化
优先级:
v-if与v-for都是vue模板系统中的指令,在vue模板编译的时候,会将指令系统转换成可执行的render函数
v-for的优先级高于v-if
注意事项:
永远不要把v-if和v-for同时 用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
如果避免出现这种情况,则在外层嵌套template
(页面渲染不生成dom
节点),在这一层进行v-if判断,然后在内部进行v-for循环
如果条件出现在循环内部,可通过计算属性computed
提前过滤掉那些不需要显示的项
SPA首屏加载速度慢怎么解决?
首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容
加载速度慢的原因?
- 网络延时问题
- 资源文件体积是否过大
- 资源是否重复发送请求去加载了
- 加载脚本的时候,渲染内容堵塞了
解决方案
减小入口文件体积:常用的就是路由懒加载
静态资源本地缓存:采用http缓存,采用Service Worker
离线缓存,合理使用本地存储,前端合理利用localStorage
ui框架按需加载:elementui antd
图片资源的压缩:对图片进行适当压缩
组件重复打包:在webpack
的config
文件中,修改CommonsChunkPlugin
的配置
开启GZip压缩:拆包之后,在使用gzip做压缩
使用SSR:也就是服务端渲染,组件或页面通过服务器生成html字符串,再发送到浏览器
为什么data属性是一个函数而不是一个对象?
vue
实例的时候定义data
属性既可以是一个对象,也可以是一个函数
组件中定义data属性,只能是一个函数,如果直接定义为一个对象,会得到警告信息
- 根实例对象
data
可以是对象也可以是函数(根实例是单例),不会产生数据污染情况 - 组件实例对象
data
必须为函数,目的是为了防止多个组件实例对象之间共用一个data
,产生数据污染。采用函数的形式,initData
时会将其作为工厂函数都会返回全新data
对象
动态给vue的data添加一个新的属性时会发生什么?怎样解决?
数据更新了,但是页面没有进行更新操作
vue2是通过Object.defineProperty
实现数据响应式,当我们访问foo属性或者设置foo值得时候能够触发setter和getter,但是当我们添加新属性时,无法触发事件属性的拦截,原因是后面添加的属性没有通过Object.defineProperty
设置成响应式数据
解决方案
- Vue.set():Vue.set( target, propertyName/index, value )通过
Vue.set
向响应式对象中添加一个property
,并确保这个新property
同样是响应式的,且触发视图更新 - Object.assign()直接使用
Object.assign()
添加到对象的新属性不会触发更新 - $forcecUpdated()
$forceUpdate
迫使Vue
实例重新渲染
vue中组件和插件有什么区别?
组件:
组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在Vue
中每一个.vue
文件都可以视为一个组件
组件优势:
-
降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快速完成需求,例如输入框,可以替换为日历、时间、范围等组件作具体的实现
-
调试方便,由于整个系统是通过组件组合起来的,在出现问题的时候,可以用排除法直接移除组件,或者根据报错的组件快速定位问题,之所以能够快速定位,是因为每个组件之间低耦合,职责单一,所以逻辑会比分析整个系统要简单
-
提高可维护性,由于每个组件的职责单一,并且组件在系统中是被复用的,所以对代码进行优化可获得系统的整体升级
插件
插件通常用来为 Vue
添加全局功能。插件的功能范围没有严格的限制
- 添加全局方法或者属性。如:
vue-custom-element
- 添加全局资源:指令/过滤器/过渡等。如
vue-touch
- 通过全局混入来添加一些组件选项。如
vue-router
- 添加
Vue
实例方法,通过把它们添加到Vue.prototype
上实现。 - 一个库,提供自己的
API
,同时提供上面提到的一个或多个功能。如vue-router
区别:
编写形式
编写组件:编写一个组件,可以有很多方式,vue单文件格式,template属性编写一个组件
编写插件:vue
插件的实现应该暴露一个 install
方法。这个方法的第一个参数是 Vue
构造器,第二个参数是一个可选的选项对象
注册形式
vue
组件注册主要分为全局注册与局部注册,全局注册通过Vue.component
方法,第一个参数为组件的名称,第二个参数为传入的配置项
局部注册只需在用到的地方通过components
属性注册一个组件
插件注册:插件的注册通过Vue.use()
的方式进行注册(安装),第一个参数为插件的名字,第二个参数是可选择的配置项
注意:注册插件的时候,需要在调用 new Vue()
启动应用之前完成
使用场景
组件 (Component)
是用来构成你的 App
的业务模块,它的目标是 App.vue
插件 (Plugin)
是用来增强你的技术栈的功能模块,它的目标是 Vue
本身
vue组件之间通信方式有哪些?
都知道组件是vue
最强大的功能之一,vue
中每一个.vue
我们都可以视之为一个组件通信指的是发送者通过某种媒体以某种格式来传递信息到收信者以达到某个目的。广义上,任何信息的交通都是通信组件间通信即指组件(.vue
)通过某种方式来传递信息以达到某个目的举个栗子我们在使用UI
框架中的table
组件,可能会往table
组件中传入某些数据,这个本质就形成了组件之间的通信
组件通信解决了什么问题?
组件间的数据是无法共享的但实际开发工作中我们常常需要让组件之间共享数据,这也是组件通信的目的要让它们互相之间能进行通讯,这样才能构成一个有机的完整系统
组件通信的分类
- 父子组件之间的通信
- 兄弟组件之间的通信
- 祖孙与后代组件之间的通信
- 非关系组件间之间的通信
通信方案
通过 props 传递:父传子,子传父通信
- 适用场景:父组件传递数据给子组件
- 子组件设置
props
属性,定义接收父组件传递过来的参数 - 父组件在使用子组件标签中通过字面量来传递值
通过 $emit 触发自定义事件:
- 适用场景:子组件传递数据给父组件
- 子组件通过
$emit触发
自定义事件,$emit
第二个参数为传递的数值 - 父组件绑定监听器获取到子组件传递过来的参数
使用 ref
- 父组件在使用子组件的时候设置
ref
- 父组件通过设置子组件
ref
来获取数据
EventBus
- 使用场景:兄弟组件传值
- 创建一个中央事件总线
EventBus
- 兄弟组件通过
$emit
触发自定义事件,$emit
第二个参数为传递的数值 - 另一个兄弟组件通过
$on
监听自定义事件
$parent 或$root
- 通过共同祖辈
$parent
或者$root
搭建通信桥连
兄弟组件
this.$parent.on('add',this.add)
另一个兄弟组件
this.$parent.emit('add')
attrs 与 listeners
- 适用场景:祖先传递数据给子孙
- 设置批量向下传属性
$attrs
和$listeners
- 包含了父级作用域中不作为
prop
被识别 (且获取) 的特性绑定 ( class 和 style 除外)。 - 可以通过
v-bind="$attrs"
传⼊内部组件
Provide 与 Inject
- 在祖先组件定义
provide
属性,返回传递的值 - 在后代组件通过
inject
接收组件传递过来的值
Vuex
-
适用场景: 复杂关系的组件数据传递
-
Vuex
作用相当于一个用来存储共享变量的容器 -
state
用来存放共享变量的地方 -
getter
,可以增加一个getter
派生状态,(相当于store
中的计算属性),用来获得共享变量的值 -
mutations
用来存放修改state
的方法。 -
actions
也是用来存放修改state的方法,不过action
是在mutations
的基础上进行。常用来做一些异步操作
双向数据绑定
我们先从单向绑定切入单向绑定非常简单,就是把Model
绑定到View
,当我们用JavaScript
代码更新Model
时,View
就会自动更新双向绑定就很容易联想到了,在单向绑定的基础上,用户更新了View
,Model
的数据也自动被更新了
双向数据绑定的原理
双向数据绑定由三个部分构成:数据层,视图层,业务逻辑层
MVVM
这里的控制层的核心功能便是 “数据双向绑定” 。自然,我们只需弄懂它是什么,便可以进一步了解数据绑定的原理
双向数据绑定的实现
先创建一个构造函数,执行初始化,对data执行响应化处理
vue中的$nextTick有什么作用?
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
可以理解为,vue再更新DOM时是异步执行的,当数据发生变化时,vue将开启一个异步更新队列,视图需要等待队列中所有数据变化完成之后,再统一进行更新
为什么要有$nextTick?
如果没有 nextTick
更新机制,那么 num
每次更新值都会触发视图更新(上面这段代码也就是会更新10万次视图),有了nextTick
机制,只需要更新一次,所以nextTick
本质是一种优化策略
使用场景
如果想要在修改数据后立即得到更新后的DOM结构,可以使用Vue.nextTick()
第一个参数是一个回调函数(可以获取最近的DOM结构)
第二个参数为:执行函数的上下文
实现原理
- 把回调函数放入callbacks等待执行
- 将执行函数放到微任务或者宏任务中
- 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调
对vue中mixin的理解?有什么场景
Mixin
是面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问mixin
类的方法而不必成为其子类
Mixin
类通常作为功能模块使用,在需要该功能时“混入”,有利于代码复用又避免了多继承的复杂
本质就是一个js对象,它可以包含我们组件中任意功能选项
vue中可以局部混入和全局混入
局部混入:
- 定义一个
mixin
对象,有组件options
的data
、methods
属性 - 组件通过
mixins
属性调用mixin
对象 - 该组件在使用的时候,混合了
mixin
里面的方法,在自动执行created
生命钩子,执行hello
方法
全局混入
- 通过
Vue.mixin()
进行全局的混入 - 使用全局混入需要特别注意,因为它会影响到每一个组件实例(包括第三方组件)
- PS:全局混入常用于插件的编写
注意事项:
当组件存在与mixin
对象相同的选项的时候,进行递归合并的时候组件的选项会覆盖mixin
的选项
但是如果相同选项为生命周期钩子的时候,会合并成一个数组,先执行mixin
的钩子,再执行组件的钩子
使用场景
在开发中,我们经常会遇到在不同的组件中经常回需要用到一些相同或者相似的代码,这些代码的功能相对独立,我们就可以通过vue的mixin提取出来
谈谈你对slot的理解
Slot
艺名插槽,花名“占坑”,我们可以理解为solt
在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中slot
位置),作为承载分发内容的出口
使用场景
通过插槽可以让用户拓展组件,去更好的复用组件和做定制化处理
如果父组件在使用到一个复用组件时,获取这个组件在不同的地方有少量的更改,如果去重写组件是不明智的选择
这时我们就可以使用插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用
比如布局组件,表格列,下拉选项,弹框显示内容等
分类
默认插槽:
- 子组件用
<slot>
标签来确定渲染的位置,标签里面可以放DOM
结构,当父组件使用的时候没有往插槽传入内容,标签内DOM
结构就会显示在页面 - 父组件在使用的时候,直接在子组件的标签内写入内容即可
具名插槽
- 子组件用
name
属性来表示插槽的名字,不传为默认插槽 - 父组件中在使用时在默认插槽的基础上加上
slot
属性,值为子组件插槽name
属性值
作用域插槽
- 子组件在作用域上绑定属性来将子组件的信息传给父组件使用,这些属性会被挂在父组件
v-slot
接受的对象上 - 父组件中在使用时通过
v-slot:
(简写:#)获取子组件的信息,在内容中使用
总结
v-slot
属性只能在<template>
上使用,但在只有默认插槽时可以在组件标签上使用- 默认插槽名为
default
,可以省略default直接写v-slot
- 缩写为
#
时不能不写参数,写成#default
- 可以通过解构获取
v-slot={user}
,还可以重命名v-slot="{user: newName}"
和定义默认值v-slot="{user = '默认值'}"
谈谈你对Observable的理解?
翻译过来就是可观察的
Vue.observable
,让一个对象变成响应式数据。Vue
内部会用它来处理 data
函数返回的对象
返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器
使用场景
在非父子组件通信时,可以使用通常的bus
或者使用vuex
,但是实现的功能不是太复杂,而使用上面两个又有点繁琐。这时,observable
就是一个很好的选择
谈谈你对vue中key中的理解?
key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点
当我们在使用v-for
时,需要给单元加上key
-
如果不用key,Vue会采用就地复地原则:最小化element的移动,并且会尝试尽最大程度在同适当的地方对相同类型的element,做patch或者reuse。
-
如果使用了key,Vue会根据keys的顺序记录element,曾经拥有了key的element如果不再出现的话,会被直接remove或者destoryed
- 当拥有新值的rerender作为key时,拥有了新key的Comp出现了,那么旧key Comp会被移除,新key Comp触发渲染
设置key值一定能提高diff效率吗?
其实不一定 ,当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出
建议尽可能在使用 v-for
时提供 key
,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升
说一下对keep-alive的理解?
keep-alive是vue中内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM
keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁他们
keep-alive可以设置props属性:
-
include
- 字符串或正则表达式。只有名称匹配的组件会被缓存 -
exclude
- 字符串或正则表达式。任何名称匹配的组件都不会被缓存 -
max
- 数字。最多可以缓存多少组件实例 -
设置了 keep-alive 缓存的组件,会多出两个生命周期钩子(
activated
与deactivated
): -
首次进入组件时:
beforeRouteEnter
>beforeCreate
>created
>mounted
>activated
> ... ... >beforeRouteLeave
>deactivated
-
再次进入组件时:
beforeRouteEnter
>activated
> ... ... >beforeRouteLeave
>deactivated
使用场景
当我们在某些场景下不需要让页面重新加载时我们可以使用keep-alive
当我们从首页
–>列表页
–>商详页
–>再返回
,这时候列表页应该是需要keep-alive
在路由中设置keepAlive
属性判断是否需要缓存
Vue中常用的修饰符有哪些?有什么作用?
在程序世界里,修饰符是用于限定类型以及类型成员的声明的一种符号
在Vue
中,修饰符处理了许多DOM
事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理
vue的修饰符分为五种:
表单修饰符
在我们填写表单的时候用得最多的是input
标签,指令用得最多的是v-model
- lazy:在我们填完信息,光标离开标签的时候,才会将值赋予给
value
,也就是在change
事件之后再进行信息同步 - trim:自动过滤用户输入的首空格字符,而中间的空格不会过滤
- number:自动将用户的输入值转为数值类型,但如果这个值无法被
parseFloat
解析,则会返回原来的值
事件修饰符
- stop:阻止了事件冒泡,相当于调用了
event.stopPropagation
方法 - prevent:阻止了事件的默认行为,相当于调用了
event.preventDefault
方法 - self:只当在
event.target
是当前元素自身时触发处理函数 - once:绑定了事件以后只能触发一次,第二次就不会触发
- capture:使事件触发从包含这个元素的顶层开始往下触发
- passive:在移动端,当我们在监听元素滚动事件的时候,会一直触发
onscroll
事件会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给onscroll
事件整了一个.lazy
修饰符 - native:让组件变成像
html
内置标签那样监听根元素的原生事件,否则组件上使用v-on
只会监听自定义事件
鼠标按键修饰符
- left 左键点击
- right 右键点击
- middle 中键点击
键值修饰符
- 普通键(enter、tab、delete、space、esc、up...)
- 系统修饰键(ctrl、alt、meta、shift...)
v-bind修饰符
- async:能对
props
进行一个双向绑定
使用async
需要注意以下两点:
-
使用
sync
的时候,子组件传递的事件名格式必须为update:value
,其中value
必须与子组件中props
中声明的名称完全一致 -
注意带有
.sync
修饰符的v-bind
不能和表达式一起使用 -
将
v-bind.sync
用在一个字面量的对象上,例如v-bind.sync=”{ title: doc.title }”
,是无法正常工作的
- prop:设置自定义标签属性,避免暴露数据,防止污染HTML结构
- camel:将命名变为驼峰命名法,如将
view-Box
属性名转换为viewBox
应用场景
- .stop:阻止事件冒泡
- .native:绑定原生事件
- .once:事件只执行一次
- .self :将事件绑定在自身身上,相当于阻止事件冒泡
- .prevent:阻止默认事件
- .caption:用于事件捕获
- .once:只触发一次
- .keyCode:监听特定键盘按下
- .right:右键
自定义指令?自定义指令使用场景有哪些?
指令系统是计算机硬件的语言系统,也叫机器语言,它是系统程序员看到的计算机的主要属性。因此指令系统表征了计算机的基本功能决定了机器所要求的能力
在vue
中提供了一套为数据驱动视图更为方便的操作,这些操作被称为指令系统
v-开头的行内属性,都是指令,不同的指令用于完成不同的功能
//会实例化一个指令,但这个指令没有参数
`v-xxx`
// -- 将值传到指令中
`v-xxx="value"`
// -- 将字符串传入到指令中,如`v-html="'<p>内容</p>'"`
`v-xxx="'string'"`
// -- 传参数(`arg`),如`v-bind:class="className"`
`v-xxx:arg="value"`
// -- 使用修饰符(`modifier`)
`v-xxx:arg.modifier="value"`
实现方式
注册分为全局注册和局部注册
全局注册主要是通过Vue.directive
方法进行注册
Vue.directive
第一个参数是指令的名字(不需要写上v-
前缀),第二个参数可以是对象数据,也可以是一个指令函数
局部注册通过在组件options
选项中设置directive
属性
然后你可以在模板中任何元素上使用新的 v-focus
property
应用场景
使用自定义指令可以满足我们日常一些场景,这里给出几个自定义指令的案例:
- 表单防止重复提交
- 图片懒加载
- 一键 Copy的功能
谈谈对vue中过滤器的了解?应用场景有哪些?
过滤器就是把我们不想要或者是不必要的东西过滤掉
过滤器实质不改变原始数据,只是对数据进行加工处理后返回过滤后的数据再进行调用处理,可以理解为是一个纯函数
使用
过滤器可以用在两个地方,双花括号插值和v-bind表达式,过滤器应该被添加在javascript表达式尾部,由管道符号指示
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
过滤器可以进行串联,有全局过滤器和局部过滤器,当全局过滤器和局部过滤器重名时,采用局部过滤器
- 局部过滤器优先于全局过滤器被调用
- 一个表达式可以使用多个过滤器。过滤器之间需要用管道符“|”隔开。其执行顺序从左往右
应用场景
单位转换、数字打点、文本格式化、时间格式化之类的等
什么是虚拟DOM?如何实现?说一下实现思路?
虚拟Dom是对真实dom的抽象,以Javscript对象作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上
创建虚拟dom是为了更好的将虚拟的节点渲染到页面视图中,所以虚拟dom对象的节点与真实dom的属性一一照应
为什么需要虚拟dom
dom是很慢的,而且其元素非常庞大,页面的性能问题,大部分都是由dom操作引起的
真实的dom节点,哪怕是最简单的div也包裹着很多属性
操作真实dom代价非常昂贵,频繁操作dom还是会引起页面卡顿,影响用户体验
使用:
- 构造子类构造函数
Ctor
installComponentHooks
安装组件钩子函数- 实例化
vnode
vue的diff算法使用?
diff
算法是一种通过同层的树节点进行比较的高效算法
其有两个特点:
- 比较只会在同层级进行, 不会跨层级比较
- 在diff比较的过程中,循环从两边向中间比较
diff
算法在很多场景下都有应用,在 vue
中,作用于虚拟 dom
渲染成真实 dom
的新旧 VNode
节点比较
diff
整体策略为:深度优先,同层比较
- 比较的过程中,循环从两边向中间收拢
- 比较只会在同层级进行, 不会跨层级比较
vue项目中有封装过axios吗?主要是哪方面?
axios是一个轻量的HTTP客户端
他是基于XMLHttpRequest服务来执行HTTP请求,支持丰富的配置,支持Promise,支持浏览器端和Node.js端,axios已经成为大部分vue开发的首选
特性
- 从浏览器中创建
XMLHttpRequests
- 从
node.js
创建http
请求 - 支持
Promise
API - 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换
JSON
数据 - 客户端支持防御
XSRF
为什么要进行封装
axios
的 API 很友好,你完全可以很轻松地在项目中直接使用。
不过随着项目规模增大,如果每发起一次HTTP
请求,就要把这些比如设置超时时间、设置请求头、根据项目环境判断使用哪个请求地址、错误处理等等操作,都需要写一遍
这种重复劳动不仅浪费时间,而且让代码变得冗余不堪,难以维护。为了提高我们的代码质量,我们应该在项目中二次封装一下 axios
再使用
如何封装
在封装的同时,我们需要和后端协商好一些约定,请求头,状态码,请求超时的时间
设置接口请求前缀:根据开发、测试、生产环境的不同,前缀需要加以区分
请求头 : 来实现一些具体的业务,必须携带一些参数才可以请求(例如:会员业务)
状态码: 根据接口返回的不同status
, 来执行不同的业务,这块需要和后端约定好
请求方法:根据get
、post
等方法进行一个再次封装,使用起来更为方便
请求拦截器: 根据请求的请求头设定,来决定哪些请求可以访问
响应拦截器: 这块就是根据 后端`返回来的状态码判定执行不同业务
axios的原理?
SSR解决了什么问题,如何实现的?
Server-Side Rendering
我们称其为SSR
,意为服务端渲染
指由服务侧完成页面的 HTML
结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程
先来看看Web
3个阶段的发展史:
- 传统服务端渲染SSR
- 单页面应用SPA
- 服务端渲染SSR
Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序
服务器渲染的 Vue.js 应用程序也可以被认为是"同构"或"通用",因为应用程序的大部分代码都可以在服务器和客户端上运行
我们从上面解释得到以下结论:
Vue SSR
是一个在SPA
上进行改良的服务端渲染- 通过
Vue SSR
渲染的页面,需要在客户端激活才能实现交互 Vue SSR
将包含两部分:服务端渲染的首屏,包含交互的SPA
解决了什么问题?
- seo:搜索引擎优先爬取页面
HTML
结构,使用ssr
时,服务端已经生成了和业务想关联的HTML
,有利于seo
- 首屏呈现渲染:用户无需等待页面所有
js
加载完成就可以看到页面视图(压力来到了服务器,所以需要权衡哪些用服务端渲染,哪些交给客户端
但是他也有一定的缺点:
复杂度
库的支持性,代码兼容
性能问题
服务器负载变大,相对于前后端分离服务器只提供静态资源来说,服务器负载更大,所以要慎重使用
在使用SSR之前,我们需要了解到SEO的页面是否只有少数几个,是否可以使用预渲染,
首屏的请求响应逻辑是否复杂,数据返回是否大量且缓慢
说一下vue的项目目录,如果是大型项目该如何划分结构和组件?
首先我们要了解为什么要划分结构,项目结构清晰会提高我们的开发效率,熟悉项目的各种配置会让我们开发效率更高
在划分项目结构的时候,需要遵守一些规则:
文件夹和文件夹的内部文件的语义一致性
单一的入口/出口:这样引用的好处就是无论文件夹内部多么混乱,外部引用时,都是从一个入口文件引入,这样就很好的实现了隔离,如果后续有重构的需求,更加高效
就近原则,紧耦合的文件应该放到一起,且应以相对路径引用
公共的文件就应该以绝对路径的方式从根目录引用
/src外的文件不应该被引入:vue-cli脚手架已经帮我们做了相关的约束了,正常的前端项目都会有一个src文件夹,里面放着的所有项目需要的资源,项目的配置,依赖,环境等文件
vue的权限管理怎么做?如果控制到按钮级别的权限怎么做?
权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配的资源
前端的权限归根结底是请求的发起权,请求的发起可能是由两种形式触发
页面加载触发
页面上的按钮点击触发
对触发权限的源头进行控制,最终要实现的目标是:
路由方面,用户登录之后只能看到自己有权限访问的导航菜单
视图方面:用户只能看到自己有权浏览的内容和有权操作的控件
如何做?
前端权限控制可以分为四个方面:
接口权限:一般通过jwt进行验证,登录拿到token,将token存起来,通过axios请求拦截器进行拦截,每次请求的时候头部携带token
按钮权限:v-if判断 自定义指令
菜单权限:菜单权限可以理解成将页面与理由进行解耦(菜单和路由都由后端返回,菜单和路由分离,菜单由后端返回)
路由权限:初始化挂载全部路由,并且在路由上标记相应的权限信息
初始化的时候先挂载不需要权限控制的路由
vue是怎么解决跨域的?
跨域的本质是浏览器基于同源策略的一种安全手段
同源策略是一种约定,他是浏览器最核心也是最基本的安全功能
所谓同源:协议,主机,端口相同
一定要注意跨域是浏览器的限制,你用抓包工具抓取接口数据,是可以看到接口已经把数据返回回来了,只是浏览器的限制,你获取不到数据。用postman请求接口能够请求到数据。这些再次印证了跨域是浏览器的限制
如何解决?
jsonp:jsonp有一个缺点,只能发送get请求
cors:跨域资源共享,是一个系统,它是由一系列传输的HTTP头组成
proxy: 网络代理,是一种特殊的网络服务
vue项目本地开发完成后部署到服务器后报404是什么原因?
前后端是独立部署的,前端只需要将最后的构建物上传至目标服务器的web容器指定的静态目录下即可
404问题
HTTP404错误意味着连接指向的资源不存在
问题在于为什么不存在?为什么只有history模式下会出现这个问题?
vue属于 单页应用,而SPA是一种网络应用程序或网站的模型,所有用户交互是可以通过动态重写当前页面,前面我们也看到了,不管我们应用有多少页面,构建物都只会产生出一个index.html
为什么hsah模式下没有问题?
hash下我们用符号#表示,hash模式下,仅hash符号之前的内容会被包含在请求中,在请求中,即使没有配置location,也不会返回404
如何解决vue项目中的错误?
错误类型:
代码本身逻辑错误:全局错误处理函数
后端接口错误 :通过axios实现网络请求前添加一层拦截
handleError
在需要捕获异常的地方调用,首先获取到报错的组件,之后递归查找当前组件的父组件,依次调用errorCaptured
方法,在遍历调用完所有errorCaptured
方法或errorCaptured
方法有报错时,调用globalHandleError
方法globalHandleError
调用全局的errorHandler
方法,再通过logError
判断环境输出错误信息invokeWithErrorHandling
更好的处理异步错误信息logError
判断环境,选择不同的抛错方式。非生产环境下,调用warn
方法处理错误
vue3了解过吗?跟vue2有什么区别?
vue3利用信的语言特性(es6)
解决框架问题
vue3的新特性:
- 速度更快:重写了虚拟dom,编译模板的实现,更高效的组件实例化,SSR速度提高,ubdate性能提高
- 体积减少:使用webpack打包,
- 更易维护:可与现有的Options API一起使用,灵活的逻辑组合与复用,vue3可以和其他框架搭配使用
- 更接近原生:可以之定义Api
- 更易使用:响应式,将Api暴露出来,轻松识别组件重新渲染
vue新增的特性
- framents:组件支持有多个根节点
- Teleport:
Teleport
是一种能够将我们的模板移动到DOM
中Vue app
之外的其他位置的技术,就有点像哆啦A梦的“任意门” - composition Api:composition Api,也就是组合式
api
,通过这种形式,我们能够更加容易维护我们的代码,将相同功能的变量进行一个集中式的管理 - createRenderer:通过
createRenderer
,我们能够构建自定义渲染器,我们能够将vue
的开发模型扩展到其他平台