定义
v-if
指令是通过销毁和重建DOM
来使元素显示或隐藏。
v-show
指令是通过修改元素的display
属性让其显示或隐藏。
应用场景
v-if
适用于不需要频繁切换条件的场景。
v-show
适用于需要频繁切换条件的场景。
Vue的生命周期
生命周期 | 描述 |
---|---|
beforeCreate (创建前) |
组件实例被创建之初,组件的属性生效之前。 |
created (创建后) |
组件实例已经完全创建,属性也绑定,但真实DOM 还没有生成,$el 还不可用。 |
beforeMount (载入前) |
在挂载开始之前被调用,相关的render 函数首次被调用。 |
mounted (载入后) |
el 被新创建的vm.$el 替换,并挂载到实例上去之后调用该钩子。 |
beforeUpdate (更新前) |
组件数据更新之前调用,发生在虚拟DOM 打补丁之前。 |
update (更新后) |
组件数据更新之后。 |
beforeDestory (销毁前) |
实例销毁前调用,实例仍然可用。 |
destoryed (销毁后) |
实例销毁之后调用,Vue 实例指示的所有东西都会解绑,所有事件监听器和所有子实例都会被移除。 |
渲染顺序
1、先父后子,完成顺序:先子后父
2、父beforeCreate
=> 父created
=> 父beforeMount
=> 子beforeCreate
=> 子created
=> 子beforeMount
=> 子mounted
=> 父mounted
更新顺序
1、父更新导致子更新,子更新完成后父更新完成
2、父beforeUpdate
=> 子beforeUpdate
=> 子updated
=> 父updated
销毁顺序
1、先父后子,完成顺序:先子后父
2、父beforeDestroy
=> 子beforeDestroy
=> 子destroyed
=> 父destroyed
MVVM
是Model-View-ViewModel
的缩写,也就是把MVC
中的controller
演变成ViewModel
。
1、Model
表示数据模型层。
2、View
表示视图层,也就是UI
组件。
3、ViewModel
是View
和Model
层的桥梁,数据绑定到viewModel
层并自动渲染到页面中,视图变化会通知viewModel
层更新数据。
v-for
的优先级高于v-if
,连用的话每个循环出来的元素都添加了v-if
,会造成性能问题,影响渲染速度。
同一个组件被复用多次会创建多个实例,在
JavaScript
中,对象是引用关系。如果data
是一个对象的话,这些实例用的是同一个构造函数。为了保证组件的数据独立,要求每个组件都必须通过data
函数返回一个对象作为组件的状态。
1、使用数据劫持和发布订阅模式结合的方式实现,也就是说数据和视图同步,数据发生变化,视图跟着变化,视图发生变化,数据也随之发生变化。
2、核心就是使用Object.defineProperty()
方法来实现。
3、v-model
本质上是语法糖,v-model
在内部为不同的输入元素使用不同的属性并抛出不同的事件。
3.1、text
和textarea
元素标签使用value
属性和input
事件。
3.2、checkbox
和radio
使用checked
属性和change
事件。
3.3、select
字段将value
作为prop
并将change
作为事件。
1、
$route
是路由信息对象,包括path
、params
、hash
、query
、fullPath
、matched
、name
等路由信息参数。
2、$router
是路由实例对象,包括了路由的跳转方法和钩子函数等。
概念
vue-router
有3
种路由模式,分别是hash
、history
、abstract
。
1、hash
:使用URL hash
值来作路由。支持所有浏览器,包括不支持HTML5 History Api
的浏览器。
2、history
:依赖HTML5 History Api
和服务器配置实现。
3、abstract
:支持所有JavaScript
运行环境,如Node.js
服务器端。如果发现没有浏览器的Api,路由会自动强制进入abstract
模式。
源码
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base);
break;
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback);
break;
case 'abstract':
this.history = new AbstractHistory(this, options.base);
break;
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`);
}
};
相关链接
1、掘金-原文
定义
1、
computed
计算属性是用来声明式的描述一个值依赖了其它的值。当模板里的数据绑定到一个计算属性上时,Vue
会在其依赖的值发生改变时更新DOM
。
2、watch
监听的是定义的变量,当定义的变量值发生变化时,调用对应的回调方法。
应用场景
1、当需要进行数值计算,并且依赖于其它数据时,应该使用
computed
,因为可以利用computed
的缓存特性,避免每次获取值时,都要重新计算。
2、当需要在数据变化时执行异步或开销较大的操作时,应该使用watch
,因为watch
允许执行异步操作,限制了执行该操作的频率,并在得到最终结果前,可以设置中间状态。这些都是计算属性无法做到的。
Proxy的优势
1、
Proxy
可以直接监听对象而非属性。
2、Proxy
可以直接监听数组的变化。
3、Proxy
有多达13
种拦截方法,不限于apply
、ownKeys
、deleteProperty
、hash
等等是Object.defineProperty
不具备的。
4、Proxy
返回的是一个新对象,可以只操作新的对象达到目的,而Object.defineProperty
只能遍历对象属性直接修改。
5、Proxy
作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利。
Object.defineProperty的优势
兼容性好,支持
IE9
,而Proxy
存在浏览器兼容性问题,而且无法用polyfill
磨平,因此Vue
的作者才声明需要等到下个大版本3.0
或以上才能用Proxy
重写。
相关链接
1、掘金-原文
定义
VueX
是一个专为Vue.js
应用程序开发的状态管理模式和库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
模块
1、
state
:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
2、getter
:允许组件从store
中获取数据,mapGetters
辅助函数仅仅是将store
中的getter
映射到局部计算属性。
3、mutation
:是唯一更改store
中状态的方法,且必须是同步函数。
4、action
:用于提交mutation
,而不是直接变更状态,可以包含任意异步操作。
5、module
:允许将单一的store
拆分为多个store
且同时保存在单一的状态树中。
异步方法,异步渲染最后一步,与
JavaScript
事件循环联系紧密。主要使用了宏任务微任务定义了一个异步方法,多次调用$nextTickt
会将方法存入队列,通过异步方法清空当前队列。
父子组件通信
在父组件标签上使用冒号声明自定义属性,给自定义属性赋值。在子组件中使用
props
接收父组件传过来的值即可。
子父组件通信
1. 在父组件标签上使用
@
自定义函数,并给自定义函数绑定一个函数名。在子组件中使用$emit
触发父组件中的函数即可。
2. 在父组件标签上使用ref
给组件重新取个新名字,并且在需要获取子组件值的地方使用this.$refs
新名字value
即可。
兄弟组件通信
1.
eventBus
方法通过一个空的Vue
实例作为中央事件总线,也就是事件中心,用它来触发事件和监听事件,从而实现各组件间的通信。
2.VueX
是一个专为Vue.js
应用程序开发的状态管理模式和库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
1、在
query
对象中定义属性和值,在需要的地方使用this.$route.query
接收即可。
2、在params
对象中定义属性和值,在需要的地方使用this.$route.params
接收即可。
注意:query
传参需要使用path
和name
来引入。params
传参需要使用name
来引入。
1、
delete
只是被删除的元素变成了empty/undefined
, 其他的元素的键值还是不变。
2、Vue.delete
直接删除数组, 改变数组的键值。
定义
slot
通俗的理解就是占位,在组件模板中占好了位置,当使用该组件标签时,组件标签里面的内容就会自动填充到该位置,也就是替换组件模板中slot
的位置。
内容插槽
1、插槽内可以包含普通文本。
2、插槽内可以包含任何模板代码,包括HTML
。
3、插槽内可以添加其他组件。
5、插槽内可以使用data
中的数据。
规则
1、父模板里的所有内容都在父级作用域中编译。
2、子模板里的所有内容都在子级作用域中编译。
后备内容,也就是默认内容插槽
有时候需要给插槽设置一个具体的默认内容,当别的组件没有给内容的时候,默认的内容就会被渲染。
具名插槽
当一个组件里面需要多个插槽的时候,
slot
标签元素有一个特殊的特性,那就是name
属性。slot
元素的这个特性可以用来定义额外的插槽。
实现
1、在父组件中的子组件标签里面使用template
标签来实现,并且在template
标签上绑定v-slot:value
。
2、在子组件的slot
标签上使用name
等于父组件中v-slot
冒号后面的值即可。
作用于插槽
插槽里面的数据不是直接写死在标签上,而是在
data
函数中获取。
实现
1、在父组件的子组件标签里面使用双花括号绑定语法即可。
2、在子组件的slot标签里面使用双花括号绑定语法即可。
相关链接
1、掘金-原文
2、源码实现
定义
Vue
在初始化数据时,会使用Object.defineProperty
重新定义data
中的所有属性,Object.defineProperty
可以使数据的获取与设置增加一个拦截的功能,拦截属性的获取,进行依赖收集。拦截属性的更新操作,进行通知。
具体的过程
首先
Vue
使用initData
初始化用户传入的参数,然后使用new Observer
对数据进行观测,如果数据是一个对象类型就会调用this.walk
操作对象,内部使用defineeReactive
循环对象属性定义响应式变化,核心就是使用Object.defineProperty
重新定义数据。
19.1、在什么阶段才能访问操作DOM
在钩子函数
mounted
被调用前,Vue
已经将编译好的模板挂载到页面上,所以在mounted
中可以访问操作DOM
。
注意:mounted
不会承诺所有的子组件也都一起被挂载。如果希望等到整个视图都渲染完毕,可以用vm.$nextTick
替换掉mounted
。
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
19.2、虚拟DOM的优缺点
19.2.1、优点
1、保证性能下限:框架的虚拟
DOM
需要适配任何上层API
可能产生的操作,它的一些DOM
操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的DOM
操作性能要好很多,因此框架的虚拟DOM
至少可以保证在不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限。
2、无需手动操作DOM
:程序员不再需要手动去操作DOM
,只需要写好ViewModel
的代码逻辑即可,框架会根据虚拟DOM
和数据双向绑定原理来实现视图的更新,极大的提高开发效率。
3、跨平台:虚拟DOM
本质上是JavaScript
对象,而DOM
与平台强相关,相比之下虚拟DOM
可以进行更方便地跨平台操作,例如服务器渲染、weex
开发等等。
19.2.2、缺点
无法进行极致优化:虽然虚拟
DOM
做了合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟DOM
无法进行针对性的极致优化。
19.3、虚拟DOM实现原理
1、使用
JavaScript
对象模拟真实DOM
树,对真实DOM
进行抽象。
2、使用diff
算法比较两棵虚拟DOM
树的差异。
3、使用pach
算法将两个虚拟DOM
对象的差异应用到真正的DOM
树上。
19.4、如何从真实DOM到虚拟DOM
1、将模板转换成
AST
树,AST
用对象来描述真实的JavaScript
语法,也就是将真实DOM
转换成虚拟DOM
。
2、优化树。
3、将AST
树生成代码。
19.5、用VNode来描述一个DOM结构
虚拟节点就是用一个对象来描述一个真实的
DOM
元素。首先将template
,真实DOM
先转成AST
,AST
树通过codegen
生成render
函数,render
函数里的_c
方法将它转为虚拟DOM
。
19.6、虚拟DOM
1、由于
DOM
操作耗时长,且DOM
对象的体积大,单个div
的DOM
属性就有294
个之多。
2、Virtual DOM
就是用一个原生的JavaScript
对象去描述一个DOM
节点,所以它比创建一个DOM
的代价要小很多。
3、VNode
是对真实DOM
的一种抽象描述,它的核心定义无非就几个关键属性,标签名、数据、子节点、键值等,其它属性都是用来扩展VNode
的灵活性以及实现一些特殊feature
的。由于VNode
只是用来映射到真实DOM
的渲染,不需要包含操作DOM
的方法,因此它是非常轻量和简单的。
4、Virtual DOM
到真实的DOM
需要经过以下过程:VNode
的create
、diff
、patch
。
一个树的完全
diff
算法是一个时间复杂度为O(n*3)
,vue进行优化转化成O(n)
。
1、最小量更新,key
很重要。这个可以是这个节点的唯一标识,告诉diff
算法,在更改前后它们是同一个DOM
节点。
2、v-for
为什么要有key
,没有key
会暴力复用,举例子的话随便说一个比如移动节点或者增加节点(修改DOM
),加key
只会移动对应节点,减少了DOM
操作。
3、只有是同一个虚拟节点才会进行精细化比较,否则就是暴力删除旧的,插入新的节点。
4、只进行同层比较,不会进行跨层比较。
diff
算法的优化策略:四种命中查找,四个指针
1、旧前与新前(先比开头,后插入和删除节点的这种情况)
2、旧后与新后(比结尾,前插入或删除的情况)
3、旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)
4、旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)
21.1、说说你对SPA单页面的理解,它的优缺点分别是什么
理解
SPA(single page application)
仅在Web页面初始化时加载相应的HTML
、JavaScript
和CSS
。一旦页面加载完成,SPA
不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现HTML
内容的变换,UI
与用户的交互,避免页面的重新加载。
优点
1、用户体验好且快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染。
2、基于上面一点,SPA
相对服务器压力小。
3、前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理。
缺点
1、初次加载耗时多:为实现单页
Web
应用功能及显示效果,需要在加载页面的时候将JavaScript
、CSS
统一加载,部分页面按需加载。
2、前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理。
3、SEO
难度较大:由于所有的内容都在一个页面中动态替换显示,所以在SEO
上有其天然的弱势。
21.2、如何优化SPA应用的首屏加载速度慢的问题
1、将公用的
JavaScript
库通过script
标签外部引入,减小app.bundle
的大小,让浏览器并行下载资源文件,提高下载速度。
2、在配置路由时,页面和组件使用懒加载的方式引入,进一步缩小app.bundle
的体积,在调用某个组件时再加载对应的js
文件。
3、加一个首屏loading
图,提升用户体验。
21.3、Vue的SPA如何优化加载速度
1、减少入口文件体积。
2、静态资源本地缓存。
3、开启Gzip
压缩。
4、使用SSR
和nuxt.js
。
v-on可以监听多个方法吗
可以。
<input type="text" :value="name" @input="onInput" @focus="onFocus" @blur="onBlur" />
v-on常用修饰符
1、
stop
该修饰符将阻止事件向上冒泡。同理于调用event.stopPropagation()
方法。
2、prevent
该修饰符会阻止当前事件的默认行为。同理于调用event.preventDefault()
方法。
3、self
该指令指当事件是从事件绑定的元素本身触发时才触发回调。
4、once
该修饰符表示绑定的事件只会被触发一次。
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的$refs
对象上。如果在普通的DOM
元素上使用,引用指向的就是DOM
元素;如果用在子组件上,引用就指向组件实例。
Vue模板渲染的原理
Vue
中的模板template
无法被浏览器解析并渲染,因为这不属于浏览器的标准,不是正确的HTML
语法,所以需要将template
转化成一个JavaScript
函数,这样浏览器就可以执行这一个函数并渲染出对应的HTML
元素,就可以让视图跑起来了,这一个转化的过程,就成为模板编译。
模板编译又分三个阶段,分别是解析parse
、优化optimize
和生成generate
,最终生成可执行函数render
。
1、parse
阶段:使用大量的正则表达式对template
字符串进行解析,将标签、指令、属性等转化为抽象语法树AST
。
2、optimize
阶段:遍历AST
,找到其中的一些静态节点并进行标记,方便在页面重新渲染的时候进行diff
比较,在进行diff
比较时直接跳过这些静态节点,优化runtime
的性能。
3、generate
阶段:将最终的AST
转化为render
函数字符串。
Vue template模板的预编译
1、对于
Vue
组件来说,模板编译只会在组件实例化的时候编译一次,生成渲染函数之后再也不会进行编译。因此,编译对组件的runtime
是一种性能损耗。
2、模板编译的目的仅仅是将template
转化为render
函数,这个过程,正好可以在项目构建的过程中完成,这样可以让实际组件在runtime
时直接跳过模板渲染,进而提升性能,这个在项目构建的编译template
的过程,就是预编译。
25.1、class
对象语法
<div v-bind:class="{ active: isActive }">div>
数组语法
<div v-bind:class="[activeClass, errorClass]">div>
25.2、style
对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">div>
数组语法
<div v-bind:style="[baseStyles, overridingStyles]">div>
官网链接
1、官网-原文
可以在
created
、beforeMount
、mounted
钩子函数中调用异步请求,因为在这三个钩子函数中,data
已经创建,可以将服务端返回的数据进行赋值。推荐在created
钩子函数中调用异步请求,因为在created
钩子函数中调用异步请求能更快获取到服务端数据,减少页面loading
时间;且ssr
不支持beforeMount
和mounted
钩子函数,所以在created
中调用异步请求有助于一致性。
Vue
是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue
被设计为可以自底向上逐层应用。Vue
的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue
也完全能够为复杂的单页应用提供驱动。
概念
keep-alive
是Vue
的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁。
作用
组件在切换过程中将状态保留到内存中,防止重复渲染
DOM
,减少加载时间及性能消耗,提高用户体验性。
原理
在
created
函数调用时将需要缓存的VNode
节点保存在this.cache
中;在render
执行时,如果VNode
的name
符合缓存条件,则会从this.cache
中取出之前缓存的VNode
实例进行渲染。
原文链接
1、CSDN-vue中keep-alive的使用及详解
2、CSDN-Vue中keep-alive的使用详解
将当前组件的
修改为
即可。
数据驱动和组件系统。
当
Vue
用v-for
正在更新已渲染过的元素列表时,它默认执行就地复用策略。如果数据项的顺序被改变,Vue
不是移动DOM
元素来匹配数据项的改变,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。为了给Vue
一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,就需要为每项提供一个唯一key
属性。key
属性的类型只能为string
或者number
类型。
key
的特殊属性主要用在Vue
的虚拟DOM
算法,在新旧nodes
对比时辨识VNodes
。如果不使用key
,Vue
会使用一种最大限度减少动态元素并且尽可能的尝试修复,且再利用相同类型元素的算法。使用key
,它会基于key
的变化重新排列元素顺序,并且会移除key
不存在的元素。
Web技术优化
1、开启
Gzip
压缩
2、浏览器缓存
3、CDN
的使用
4、使用Chrome Performance
查找性能瓶颈
编码优化
1、事件代理和事件销毁
2、路由懒加载和图片资源懒加载
3、第三方插件的按需引入
4、防抖和节流
5、v-if
和v-show
区分使用场景
6、computed
和watch
区分使用场景
7、v-for
遍历必须为item
添加key,且避免同时使用v-if
8、长列表性能优化
9、
Webpack优化
1、使用
Webpack
对图片进行压缩
2、减少ES6
转为ES5
的冗余代码
3、提取公共代码
4、模板预编译
5、提取组件的CSS
6、构建结果输出分析
7、Vue
项目的编译优化
1、如果目标是数组,直接使用数组的splice方法触发响应式。
2、如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用defineReactive
方法进行响应式处理。defineReactive
方法就是Vue
在初始化对象时,给对象属性采用Object.defineProperty
动态添加getter
和setter
功能所调用的方法。
从
rollup
配置文件中找到编译的入口,在入口文件中主要做了两件事情。
1、重写了$mount
方法增加新的功能,首先判断有没有render
函数如果没有,将template
编译成render
函数;如果有调用mount
渲染到DOM
上。
2、注册Vue
静态方法complier
将HTML
字符串编译成render
。
所有的
prop
都使得其父子prop
之间形成了一个单向下行绑定,父级prop
的更新会向下流动到子组件中,但是反过来则不行。这样是防止从子组件意外改变父级组件的状态,从而导致应用的数据流向难以理解。除此之外,每次父级组件发生更新时,子组件中所有的prop
都会刷新为最新的值。这意味着不应该在一个子组件内部改变prop
。如果这样做了,Vue
会在浏览器的控制台中发出警告。子组件想修改时,只能通过$emit
派发一个自定义事件,父组件接收到后,由父组件修改。
使用babel-polyfill插件。
生命周期 | 描述 |
---|---|
beforeCreate : 创建前 |
组件实例被创建之初,组件的属性生效之前。 |
created : 创建后 |
组件实例已经完全创建,属性也绑定,但真实DOM 还没有生成,$el 还不可用。 |
beforeMount : 载入前 |
在挂载开始之前被调用,相关的render 函数首次被调用。 |
mounted : 载入后 |
el 被新创建的vm.$el 替换,并挂载到实例上去之后调用该钩子。 |
beforeUpdate : 更新前 |
组件数据更新之前调用,发生在虚拟DOM 打补丁之前。 |
update : 更新后 |
组件数据更新之后。 |
beforeDestory : 销毁前 |
实例销毁前调用,实例仍然可用。 |
destoryed : 销毁后 |
实例销毁之后调用,Vue 实例指示的所有东西都会解绑,所有事件监听器和所有子实例都会被移除。 |
类库或者框架都是重量级的,里面包含很多方法,但是实际项目开发中,我们用不到这么多东西,所以在开发他们的时候,会把功能按照模块进行单独开发,使用者可根据自身情况选择一个模块导入使用。