目录
2.为什么是计算属性computed,而不是方法methods?
0.前言
闲聊无事,都会来Vue官网逛逛,每次来的时候,总会得到新的感悟,因此,想记录下来,这样日积月累逐渐增强自己;
1.为什么要有计算属性computed?
为了防止在template/模板中出现大量的逻辑代码,导致用户无法在短时间内理清头绪,因此,需要把大量的逻辑代码独立出来,而template/模板只负责展示结果、简单逻辑;
2.为什么是计算属性computed,而不是方法methods?
计算属性是基于响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。
通俗来讲就是,如果计算属性所依赖的变量没有变化,则计算属性仅仅运行一次,然后缓存运行结果,而方法则是每调用一次就运行一次,因此,计算属性在面对复杂逻辑时,更能节省系统资源;
3.计算属性computed与侦听器watch
既然computed是基于响应式依赖进行缓存的,说明Vue会监控依赖,当依赖发生变化后,再次调用computed时,会重新计算结果;
这就说明Vue有个“监视”功能,这里有个侦听器watch,可以让开发者去自定义自己的侦听逻辑,特别是computed面对一个异步任务/开销较大的操作时,需要开发者时时关注细节,此刻,watch侦听器要比computed计算属性更合适;
4.当v-bind绑定HTML Class属性时
当v-bind绑定class属性时,其属性值可以是:
①Object对象
②数组
③计算属性
5.当v-bind绑定HTML Style属性时
当v-bind绑定class属性时,其属性值可以是:
①Object对象:CSS property名可以用驼峰式(camelCase)或短横线分隔 (kebab-case,记得用引号括起来)来命名;
②数组:
③多重值:从2.3.0起可以为style绑定中的property提供一个包含多个值的数组,常用于提供多个带前缀的值,浏览器只会渲染数组中最后一个被浏览器支持的值;
④计算属性
6.v-bind:style与浏览器引擎前缀
浏览器引擎前缀:浏览器厂商们有时会给实验性的或者非标准的 CSS属性和JavaScript API添加前缀,这样开发者就可以用这些新的特性进行试验,同时(理论上)防止他们的试验代码被依赖,从而在标准化过程中破坏web开发者的代码。开发者应该等到浏览器行为标准化之后再使用未加前缀的属性。
6.1.CSS 前缀
主流浏览器引擎前缀:
-webkit- (谷歌,Safari,新版Opera浏览器,以及几乎所有iOS系统中的浏览器(包括 iOS 系统中的火狐浏览器);基本上所有基于WebKit 内核的浏览器)
-moz- (火狐浏览器)
-o- (旧版Opera浏览器)
-ms- (IE浏览器 和 Edge浏览器)
6.2.JS接口前缀
需要使用大写的前缀修饰接口名:
WebKit (谷歌, Safari, 新版Opera浏览器, 以及几乎所有iOS系统中的浏览器(包括iOS 系统中的火狐浏览器); 简单的说,所有基于WebKit 内核的浏览器)
Moz (火狐浏览器)
O (旧版Opera浏览器)
MS (IE浏览器 和 Edge浏览器)
6.3.JS属性和方法前缀
需要使用小写的前缀修饰属性或者方法
webkit (谷歌, Safari, 新版Opera浏览器, 以及几乎所有iOS系统中的浏览器(包括iOS 系统中的火狐浏览器); 简单的说,所有基于WebKit 内核的浏览器)
moz (火狐浏览器)
o (旧版Opera浏览器等
ms (IE浏览器 和 Edge浏览器)
6.4.v-bind:style与浏览器引擎前缀
当v-bind:style使用需要添加浏览器引擎前缀的CSS property时,Vue.js会自动侦测并添加相应的前缀;
7.v-if与条件渲染
①用于HTML标签时:
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no</h1>
注意:v-else元素必须紧跟在带v-if或者v-else-if元素的后面,否则它将不会被识别。
②用于<template>元素时:
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
注意:v-else元素必须紧跟在带v-if或者v-else-if元素的后面,否则它将不会被识别。
③v-else-if注意事项:
2.1.0新增
v-else-if也必须紧跟在带v-if或者v-else-if元素之后
④key属性:
如果v-if、v-else-if、v-else分割的模板内容,如果整体标签类型、顺序一样的话,则Vue本着尽可能高效渲染元素的原则,会复用已有元素而不是从头开始渲染;
如果你想Vue停止复用,则针对想停止复用的元素,添加一个具有唯一值的key属性即可,这样,每次Vue都会重新渲染该元素;
总结:想重新渲染哪个元素,就在哪个元素上添加key属性;
⑤v-if与v-show区别
v-if在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建,且只有v-if条件第一次变为真时,才会开始渲染条件块;
v-show不管初始条件是什么,元素总是会被渲染,且只是基于 display属性进行切换;
总结:v-if切换开销大,v-show初始渲染开销大,如果需要非常频繁地切换,则使用v-show,如果在运行时条件很少改变,则使用v-if较好;
8.v-for与列表渲染
①v-for遍历数组
<li v-for="item in items" :key="item.message">
<li v-for="(item, index) in items">
其中items是源数据数组,item则是被迭代的数组元素的别名,index代表下标索引,从0开始;
②v-for遍历对象(v-for其实遍历的是对象的data属性)
<li v-for="value in object">
<div v-for="(value, name) in object">
<div v-for="(value, name, index) in object">
其中object是对象data属性下的一个对象类型的属性,value则是object对象下的属性值,name则是object对象下的属性名,index则是代表object对象下的属性顺序,从0开始;
③key与就地更新策略
Vue默认处理策略:v-for渲染列表时,如果列表数据发生了变化,Vue默认使用就地更新策略,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法,也就是说默认的情况下Vue会尽量使用已经存在的DOM元素,直接在已有的DOM上进行复用修改,这样可以带来一定性能上的提升;
要想打破Vue的默认策略,方便跟踪每个节点的身份,从而重用和重新排序现有元素,建议尽可能在使用v-for时提供一个唯一的key属性,除非遍历输出的DOM内容非常简单,或者是刻意依赖默认行为以获取性能上的提升;
注意:不要使用对象或数组之类的非基本类型值作为v-for的key,应该使用字符串或数值类型的值来作为key的值;
④数组更新检测
当调用数组下列方法时,会触发Vue视图更新:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
⑤v-for与计算属性、方法
想在v-for时候,对数组中的元素进行某种操作,但又不影响原始数组,此刻可以配合使用计算属性、方法;
既然不能影响原始数组,那么就需要我们把筛选之后的数据组成一个新的数组;
⑥v-for与整数
v-for也可以接受整数,它会把模板重复对应次数;
⑦v-for与v-if同时使用
<li v-for="todo in todos" v-if="!todo.isComplete">
当它们处于同一节点,v-for优先级比v-if高,这意味着v-if将分别重复运行于每个v-for循环中;
⑧组件与v-for
2.2.0+版本里,当在组件上使用v-for时,key必须存在;
为了把迭代数据传递到组件里,我们要使用prop;
9.Vue的特殊属性:key
key的取值类型:number | string | boolean (2.4.2 新增) | symbol (2.5.12 新增)
按照官方介绍,在虚拟DOM算法,新旧nodes对比时辨识VNodes;如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用key时,它会基于key的变化重新排列元素顺序,并且会移除key不存在的元素;
通俗来讲,为了提高效率,Vue是尽可能的复用/修改已经存在的、类型相同的元素,但这样会造成显示错乱,为了避免这种情况,属性key就可以打破这种局面;
10.v-on与事件处理
①v-on的取值
Function|Inline Statement|Object,可以用v-on指令监听DOM事件;
②event与$event
可以在Vue的methods中用event来访问原始的DOM事件,也可以通过$event把原始的DOM事件传入方法;
③事件修饰符
调用event.preventDefault()/event.stopPropagation()这类常见的功能时,这种写法既显得繁琐,同时,本来需要自定义的methods中添加了DOM事件细节,显得逻辑混乱;
为了解决这个问题出现了事件修饰符:.stop/.prevent/.capture/.self/.once/.passive
注意:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生;
④按键修饰符
监听键盘按键,Vue允许为v-on在监听键盘事件时添加按键修饰符:.enter/.tab/.delete/.esc/.space/.up/.down/.left/.right
⑤系统修饰键
2.1.0 新增,可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器:
.ctrl/.alt/.shift/.meta/.exact/.left/.right/.middle
11.v-model与表单输入绑定
①绑定HTML标签
v-model只能用在<input>、<textarea>、<select>标签上;当v-model绑定上这3个元素后,v-model忽视这3个元素的value、checked、selected属性的初始值,转而将Vue实例的数据作为数据来源,鉴于这种设置,我们应该在Vue实例的data选项中声明初始值;
②针对不同的标签,提供不同的逻辑
text和textarea使用value属性和input事件;
checkbox和radio使用checked属性和change事件;
select标签使用option标签的value作为属性和change作为事件;
③v-model与v-bind值绑定
对于radio单选按钮/checkbox复选框/select选择框,v-model 绑定的值通常是静态字符串(对于复选框也可以是布尔值);
例如:radio和checkbox绑定的是checked属性,因此v-model取值就是true/false,而select选择框绑定的是option的value属性,v-model取值就是option的value值;
但,我们还可以把v-model取值绑定到Vue实例的一个动态属性上,这种需求可以用v-bind实现,且这个Vue实例动态属性值可以不是字符串;
④v-model的修饰符
.lazy:默认v-model在每次input事件触发后将输入框的值与数据进行同步,而lazy修饰符转为在change事件之后进行同步;
.number:自动将用户的输入值转为数值类型;
.trim:自动过滤用户输入的首尾空白字符;
12.Vue的响应式原理
12.1.如何追踪变化
当一个object传入Vue实例来作为data选项,Vue遍历object的所有属性并通过Object.defineProperty把所有属性转化为getter/setter方式,这样就可以对各个属性进行跟踪;
Vue会为每个组件实例分配一个watcher实例,当data选项中属性setter方法被触发后,会通知watcher,watcher再去触发re-render函数,从而使它关联的组件重新渲染;
12.2.检测变化的注意事项
由于JS限制,Vue不能检测数组和对象的变化,为此,Vue作出如下变动:
对于对象而言,你必须把对象挂载到data项后,Vue对其属性转化成setter/getter方法之后,才能对其进行监控;
对于数组而言,主要是数组的2种操作没法监控,但我们可以变通,变通之后就可以进行监控了;
12.3.声明响应式property
Vue不允许动态添加根级响应式property,所以必须在初始化实例前声明所有根级响应式property,哪怕只是一个空值;
12.4.异步更新队列
Vue在更新DOM时是异步执行的。
只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。
为了在数据变化之后等待Vue完成更新 DOM,可以在数据变化之后立即使用Vue.nextTick(callback)。这样回调函数将在DOM更新完成后被调用;
13.组件
13.1.基础
组件是可复用的Vue实例,且带有一个名字;
我们可以在一个通过new Vue创建的Vue根实例中,把这个组件作为自定义元素来使用;
因为组件是可复用的Vue实例,所以它们与new Vue接收相同的选项,除了el属性之外,其他一切不变;
13.2.data选项
一个组件的data选项必须是一个函数,只有这样才能保证各个实例有自己独立的变量域;
13.3.组件的组织
通常一个应用会以一棵嵌套的组件树的形式来组织:
为了能在模板中使用,这些组件必须先注册以便Vue能够识别。这里有2种组件的注册类型:全局注册和局部注册;
全局注册的组件可以用在其被注册之后的任何(通过 new Vue)新创建的Vue根实例,也包括其组件树中的所有子组件的模板中;
13.4.通过Prop向子组件传递数据
Prop是你可以在组件上注册的一些自定义属性。当一个值传递给一个prop属性的时候,它就变成了那个组件实例的一个属性,就像访问data中的值一样;
13.5.单个根元素
①每个组件必须只有一个根元素;
②props的取值类型是:Array<string> | Object,这里如果要继承的属性比较多时,可以采用object,不论何时为父类对象添加一个新的属性,它都会自动地在templdate中自由使用;
13.6.监听子组件事件
假如,我通过Vue.component方式注册一个全局组件child,然后我在一个app的Vue实例的el中进行使用,那么谁是父组件,谁是子组件呢?
按照官网的案例,这种情况下,app就是父组件,而child就是子组件,也就是说对于全局组件,使用方就是父组件,被使用方就是子组件;
按照网上给出的父子组件的生命周期过程:
加载渲染过程:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程:父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程:父beforeUpdate->父updated
销毁过程:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
从上面的看出,无论是哪个过程,都是先操作父组件,然后再去操作子组件
13.6.1.使用事件抛出一个值
假如父组件app,有N个子组件child,那我可以通过某个子组件来修改父组件的属性,进而对所有子组件进行统一修改,其操作过程如下:
第一步:核心在于父级组件可以像处理本地DOM事件一样通过v-on来监听子组件实例的任意事件;
第二步:子组件通过调用内建的$emit方法并传入事件名称来触发一个事件;
第三步:父组件通过v-on来定义子组件$emit方法中指明的事件名称;
当需要通过$emit传递额外的参数时,该怎么处理呢?
$emit的第二个参数可以用来传递其他值,当在父级组件监听这个事件的时候,父组件可以通过$event访问到被抛出的这个值,或者如果父组件的这个事件处理函数是一个方法,则这个值将会作为第一个参数传入这个方法;
13.6.2.在组件上使用v-model
自定义事件也可以用于创建支持v-model的自定义输入组件(自定义事件、支持v-model的自定义组件、自定义输入组件)
要想达到这个目标,需要这个组件内的<input>标签必须达到下面2个要求:
①将<input/>标签的value属性通过v-bind绑定到一个名叫 value的prop上;
②在<input/>标签的input事件被触发时,将新的值通过自定义的input事件抛出;
核心代码如下:
Vue.component('custom-input', { props: ['value'], template: ` <input v-bind:value="value" v-on:input="$emit('input', $event.target.value)" > }) |
13.7.动态组件
在不同组件之间进行动态切换时,可以通过Vue的<component> 元素加一个“is”属性来实现:
<!-- 组件会在`currentTabComponent`改变时改变-->
<component v-bind:is="currentTabComponent"></component>
其中currentTabComponent可以是:已注册组件的名字、一个组件的选项对象;
13.8.解析DOM模板时的注意事项
Vue针对el绑定的HTML代码有要求:
有些HTML元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的;
有些元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部;
这会导致我们使用这些有约束条件的元素时遇到一些问题,最后导致最终渲染结果出错;
针对这种情况,可以通过“is”属性来变通折中;但如果从以下来源使用模板的话,这条限制是不存在的:
①字符串 (例如:template: '...')
②单文件组件 (.vue)
③<script type="text/x-template">
14.组件注册
14.1.组件名
定义组件名的方式有两种:
①使用kebab-case:短横线分隔命名,当使用kebab-case(短横线分隔命名)定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case;
②使用PascalCase:首字母大写命名,当使用PascalCase(首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用,但是,直接在DOM (即非字符串的模板)中使用时只有kebab-case是有效的;
14.2.全局注册
通过Vue.component创建的组件是全局注册,注册之后可以用在任何新创建的Vue根实例(new Vue)的模板中;
14.3.局部注册
全局注册有几个缺点,例如:在webpack此类的构建系统中,全局注册所有的组件意味着即便你已经不再使用一个组件了,但它仍然会被包含在你最终的构建结果中,从而造成用户下载的JS量无谓的增加;
而局部组件就不存在这个问题,通过Vue的components选项中定义的组件,就是局部组件;
对于components对象中的每个属性来说,其属性名就是自定义元素的名字,其属性值就是这个组件的选项对象;
14.4.模块系统
①在模块系统中局部注册
②基础组件的自动化全局注册:通过require.context只全局注册这些非常通用的基础组件,注意:全局注册的行为必须在根Vue实例(通过new Vue)创建之前发生;
15.Prop
15.1.Prop大小写
①如果要在DOM模板中使用时,则必须使用kebab-case (短横线分隔命名),如果是camelCase (驼峰命名法),则使用其等价的kebab-case (短横线分隔命名) 命名;
②如果使用的是字符串模板,则不存在这个限制;
15.2.Prop类型
类型:Array<string> | Object
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
props: { title: String, likes: Number, isPublished: Boolean, commentIds: Array, author: Object, callback: Function, contactsPromise: Promise // or any other constructor } |
15.3.传递静态或动态Prop
实际上任何类型的值都可以传给一个prop,常用的场景有:
①传入一个数字
②传入一个布尔值
③传入一个数组
④传入一个对象
⑤传入一个对象的所有property
鉴于props的取值类型是:Array<string> | Object,这里可以采用Object更省时省力;
15.4.单向数据流
父组件prop单向、向下传递数据给子组件prop;
这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解;
15.5.Prop验证
type、default、required、validator
15.6.非Prop的属性
一个非prop的属性是指传向一个组件,但是该组件并没有相应 prop定义的属性;
这个章节是什么意思呢?
假如我开发了一个T组件,我开发时定义了3个属性,但谁能保证,在面向众多开发者时,我这组件的3个属性就能满足他们的所有业务需求,这不可能吧,所以,Vue允许组件可以接受任意数量的属性,并把这些属性添加到该组件的根元素上;
15.6.1.替换/合并已有的属性
对于绝大多数属性来说,从外部提供给组件的属性值会替换掉组件内部同名的属性值;
但class和style属性则是合并这两边的值;
15.6.2.禁用Attribute继承
如果你不希望组件的根元素继承属性,你可以在组件的选项中设置inheritAttrs: false;这尤其适合配合实例的$attrs使用,该$attrs包含了传递给一个组件的属性名、属性值;
这样,你就可以手动决定这些属性会被赋予哪个元素,在使用基础组件的时候更像是使用原始的HTML元素,而不会担心哪个元素是真正的根元素;
16.动态组件、异步组件
16.1.在动态组件上使用 keep-alive
<component v-bind:is="currentTabComponent"></component>
每次切换新标签的时候,Vue都新建currentTabComponent实例,如果我们希望标签的组件实例能够被在它们第一次被创建的时候缓存下来,此刻可以用一个<keep-alive>元素将其动态组件包裹起来:
<!-- 失活的组件将会被缓存!--> <keep-alive> <component v-bind:is="currentTabComponent"></component> </keep-alive> |
16.2.异步组件
个人水平有限,实在是看不懂;