Vue2.x
咱们用了好久好久了,然而咱们在平常开发时,有一些好用却没发现的大陆。。javascript
在这里,咱们就不讲v-for
啊、v-if
啊等一些很基础很基础的。 想了解这些基本东西的话,能够vue(戳我)html
本篇引用了vue官网绝大部份内容,可直接去官网查看。vue
本篇只是记录(窝)平常开发时,比较少用却好用的功能java
Prop
的用法不言而喻,这里就来恭恭非Prop
。node
一个非 prop
的 attribute
是指传向一个组件,可是该组件并无相应 prop
定义的 attribute
。express
由于显式定义的 prop
适用于向一个子组件传入信息,然而组件库的做者并不总能预见组件会被用于怎样的场景。这也是为何组件能够接受任意的 attribute
,而这些 attribute
会被添加到这个组件的根元素上。bootstrap
例如,想象一下你经过一个 Bootstrap
插件使用了一个第三方的 <bootstrap-date-input>
组件,这个插件须要在其<input>
上用到一个 data-date-picker
attribute
。咱们能够将这个 attribute
添加到你的组件实例上:api
<bootstrap-date-input data-date-picker="activated"></bootstrap-date-input>
复制代码
而后这个 data-date-picker="activated"
attribute
就会自动添加到 <bootstrap-date-input>
的根元素上。浏览器
(伪装我是分割线-------------------------------------)缓存
以上是官文的介绍,看着是否是蒙蒙的。咱们这里简单的讲解下。
假如,假如(假如是谁?)bootstrap-date-input
组件的定义是酱紫滴。
// bootstrap-date--input
<div>
<input></input>
</div>
...
props: {
// 我是空的,注意,我是空的
}
复制代码
耳后,咱们使用bootstrap-date-input
这个组件。
<bootstrap-date-input data-date-picker="activated"></bootstrap-date-input>
复制代码
这里咱们知道,在bootstrap-date--input
组件内咱们并无在props
里的定义data-date-picker
属性。
到这里你们应该开始明白了吧?官网解释(一个非 prop
的 attribute
是指传向一个组件,而这些 attribute
会被添加到这个组件的根元素上。)
bootstrap-date--input
组件会变成这样
// bootstrap-date--input
<div data-date-picker="activated">
<input></input>
</div>
复制代码
这里直接引号官网的介绍。
想象一下 <bootstrap-date-input>
的模板是这样的:
<input type="date" class="form-control">
复制代码
为了给咱们的日期选择器插件定制一个主题,咱们可能须要像这样添加一个特别的类名:
<bootstrap-date-input
data-date-picker="activated"
class="date-picker-theme-dark"
></bootstrap-date-input>
复制代码
在这种状况下,咱们定义了两个不一样的 class
的值:
form-control
,这是在组件的模板内设置好的date-picker-theme-dark
,这是从组件的父级传入的对于绝大多数 attribute
来讲,从外部提供给组件的值会替换掉组件内部设置好的值。因此若是传入 type="text"
就会替换掉 type="date"
并把它破坏!庆幸的是,class
和 style
attribute
会稍微智能一些,即两边的值会被合并起来,从而获得最终的值:form-control date-picker-theme-dark
。
若是你不但愿组件的根元素继承 attribute
,你能够在组件的选项中设置 inheritAttrs: false
。例如:
Vue.component('my-component', {
inheritAttrs: false,
// ...
})
复制代码
这尤为适合配合实例的 $attrs
属性使用,该属性包含了传递给一个组件的 attribute
名和 attribute
值,例如:
{
required: true,
placeholder: 'Enter your username'
}
复制代码
有了 inheritAttrs: false
和$attrs
,你就能够手动决定这些 attribute
会被赋予哪一个元素。在撰写基础组件的时候是常会用到的:
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
`
})
复制代码
注意 inheritAttrs: false 选项不会影响 style 和 class 的绑定。
复制代码
这个模式容许你在使用基础组件的时候更像是使用原始的 HTML
元素,而不会担忧哪一个元素是真正的根元素:
<base-input
v-model="username"
required
placeholder="Enter your username"
></base-input>
复制代码
不一样于组件和 prop
,事件名不存在任何自动化的大小写转换。而是触发的事件名须要彻底匹配监听这个事件所用的名称。举个例子,若是触发一个 camelCase
名字的事件:
this.$emit('myEvent')
复制代码
则监听这个名字的 kebab-case
版本是不会有任何效果的:
<!-- 没有效果 -->
<my-component v-on:my-event="doSomething"></my-component>
复制代码
不一样于组件和 prop
,事件名不会被用做一个 JavaScript
变量名或属性名,因此就没有理由使用 camelCase
或 PascalCase
了。而且 v-on
事件监听器在 DOM
模板中会被自动转换为全小写 (由于 HTML 是大小写不敏感的),因此 v-on:myEvent
将会变成v-on:myevent
——致使 myEvent
不可能被监听到。
所以,咱们推荐你始终使用 kebab-case
的事件名。
一个组件上的 v-model
默认会利用名为 value
的 prop
和名为 input
的事件,可是像单选框、复选框等类型的输入控件可能会将 value attribute
用于不一样的目的。model
选项能够用来避免这样的冲突:
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
复制代码
如今在这个组件上使用 v-model
的时候:
<base-checkbox v-model="lovingVue"></base-checkbox>
复制代码
这里的 lovingVue
的值将会传入这个名为checked
的 prop
。同时当 <base-checkbox>
触发一个 change
事件并附带一个新的值的时候,这个 lovingVue
的属性将会被更新。
注意 你仍然须要在组件的
props
选项里声明checked
这个prop
。
你可能有不少次想要在一个组件的根元素上直接监听一个原生事件。这时,你可使用 v-on
的 .native
修饰符:
<base-input v-on:focus.native="onFocus"></base-input>
复制代码
在有的时候这是颇有用的,不过在你尝试监听一个相似 <input>
的很是特定的元素时,这并非个好主意。好比上述 <base-input>
组件可能作了以下重构,因此根元素其实是一个<label>
元素:
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
复制代码
这时,父级的 .native
监听器将静默失败。它不会产生任何报错,可是 onFocus 处理函数不会如你预期地被调用。
为了解决这个问题,Vue
提供了一个 $listeners
属性,它是一个对象,里面包含了做用在这个组件上的全部监听器。例如:
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
复制代码
有了这个 $listeners
属性,你就能够配合 v-on="$listeners"
将全部的事件监听器指向这个组件的某个特定的子元素。对于相似 <input>
的你但愿它也能够配合 v-model
工做的组件来讲,为这些监听器建立一个相似下述 inputListeners
的计算属性一般是很是有用的:
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 将全部的对象合并为一个新对象
return Object.assign({},
// 咱们从父级添加全部的监听器
this.$listeners,
// 而后咱们添加自定义监听器,
// 或覆写一些监听器的行为
{
// 这里确保组件配合 `v-model` 的工做
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: ` <label> {{ label }} <input v-bind="$attrs" v-bind:value="value" v-on="inputListeners" > </label> `
})
复制代码
如今 <base-input>
组件是一个彻底透明的包裹器了,也就是说它能够彻底像一个普通的<input>
元素同样使用了:全部跟它相同的 attribute
和监听器均可以工做。
在有些状况下,咱们可能须要对一个 prop
进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,由于子组件能够修改父组件,且在父组件和子组件都没有明显的改动来源。
这也是为何咱们推荐以 update:myPropName
的模式触发事件取而代之。举个例子,在一个包含 title prop
的假设的组件中,咱们能够用如下方法表达对其赋新值的意图:
this.$emit('update:title', newTitle)
复制代码
而后父组件能够监听那个事件并根据须要更新一个本地的数据属性。例如:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
复制代码
为了方便起见,咱们为这种模式提供一个缩写,即 .sync
修饰符:
<text-document v-bind:title.sync="doc.title"></text-document>
复制代码
注意带有
.sync
修饰符的v-bind
不能和表达式一块儿使用 (例如v-bind:title.sync=”doc.title + ‘!’”
是无效的)。取而代之的是,你只能提供你想要绑定的属性名,相似v-model
。
当咱们用一个对象同时设置多个 prop
的时候,也能够将这个 .sync
修饰符和 v-bind
配合使用:
<text-document v-bind.sync="doc"></text-document>
复制代码
这样会把 doc
对象中的每个属性 (如 title
) 都做为一个独立的prop
传进去,而后各自添加用于更新的 v-on
监听器。
将
v-bind.sync
用在一个字面量的对象上,例如v-bind.sync=”{ title: doc.title }”
,是没法正常工做的,由于在解析一个像这样的复杂表达式的时候,有不少边缘状况须要考虑。
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components
规范草案,将 <slot>
元素做为承载分发内容的出口。
它容许你像这样合成组件:
<navigation-link url="/profile">
Your Profile
</navigation-link>
复制代码
而后你在 <navigation-link>
的模板中可能会写为:
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
复制代码
当组件渲染的时候,<slot></slot>
将会被替换为“Your Profile”。插槽内能够包含任何模板代码,包括 HTML:
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
复制代码
甚至其它的组件:
<navigation-link url="/profile">
<!-- 添加一个图标的组件 -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
复制代码
若是 <navigation-link>
没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
当你想在一个插槽中使用数据时,例如:
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
复制代码
该插槽跟模板的其它地方同样能够访问相同的实例属性 (也就是相同的“做用域”),而不能访问<navigation-link>
的做用域。例如 url
是访问不到的:
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!--
这里的 `url` 会是 undefined,由于 "/profile" 是
_传递给_ <navigation-link> 的而不是
在 <navigation-link> 组件*内部*定义的。
-->
</navigation-link>
复制代码
做为一条规则,请记住:
父级模板里的全部内容都是在父级做用域中编译的;子模板里的全部内容都是在子做用域中编译的。
有时为一个插槽设置具体的后备 (也就是默认的) 内容是颇有用的,它只会在没有提供内容的时候被渲染。例如在一个 <submit-button>
组件中:
<button type="submit">
<slot></slot>
</button>
复制代码
咱们可能但愿这个 <button>
内绝大多数状况下都渲染文本“Submit”。为了将“Submit”做为后备内容,咱们能够将它放在 <slot>
标签内:
<button type="submit">
<slot>Submit</slot>
</button>
复制代码
如今当我在一个父级组件中使用 <submit-button>
而且不提供任何插槽内容时:
<submit-button></submit-button>
复制代码
后备内容“Submit”将会被渲染:
<button type="submit">
Submit
</button>
复制代码
可是若是咱们提供内容:
<submit-button>
Save
</submit-button>
复制代码
则这个提供的内容将会被渲染从而取代后备内容
<button type="submit">
Save
</button>
复制代码
有时咱们须要多个插槽。例如对于一个带有以下模板的 <base-layout>
组件:
<div class="container">
<header>
<!-- 咱们但愿把页头放这里 -->
</header>
<main>
<!-- 咱们但愿把主要内容放这里 -->
</main>
<footer>
<!-- 咱们但愿把页脚放这里 -->
</footer>
</div>
复制代码
对于这样的状况,<slot>
元素有一个特殊的 attribute:name
。这个 attribute
能够用来定义额外的插槽:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
复制代码
一个不带 name
的 <slot>
出口会带有隐含的名字“default”
在向具名插槽提供内容的时候,咱们能够在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
复制代码
如今 <template>
元素中的全部内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot
的 <template>
中的内容都会被视为默认插槽的内容。0
然而,若是你但愿更明确一些,仍然能够在一个 <template>
中包裹默认插槽的内容:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
复制代码
任何一种写法都会渲染出:0
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
复制代码
注意 v-slot
只能添加在 <template>
上
有时让插槽内容可以访问子组件中才有的数据是颇有用的。例如,设想一个带有以下模板的 <current-user>
组件:
<span>
<slot>{{ user.lastName }}</slot>
</span>
复制代码
咱们可能想换掉备用内容,用名而非姓来显示。以下:
<current-user>
{{ user.firstName }}
</current-user>
复制代码
然而上述代码不会正常工做,由于只有 <current-user>
组件能够访问到 user
而咱们提供的内容是在父级渲染的。
为了让 user
在父级的插槽内容中可用,咱们能够将 user
做为 <slot>
元素的一个 attribute
绑定上去:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
复制代码
绑定在 <slot>
元素上的 attribute
被称为插槽 prop
。如今在父级做用域中,咱们可使用带值的 v-slot
来定义咱们提供的插槽 prop
的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
复制代码
在这个例子中,咱们选择将包含全部插槽 prop
的对象命名为 slotProps
,但你也可使用任意你喜欢的名字。
在上述状况下,当被提供的内容只有默认插槽时,组件的标签才能够被看成插槽的模板来使用。这样咱们就能够把 v-slot
直接用在组件上:
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
复制代码
这种写法还能够更简单。就像假定未指明的内容对应默认插槽同样,不带参数的 v-slot
被假定对应默认插槽:
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
复制代码
注意默认插槽的缩写语法不能和具名插槽混用,由于它会致使做用域不明确:
<!-- 无效,会致使警告 -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
复制代码
只要出现多个插槽,请始终为全部的插槽使用完整的基于 <template>
的语法:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
<template v-slot:other="otherSlotProps">
...
</template>
</current-user>
复制代码
做用域插槽的内部工做原理是将你的插槽内容包括在一个传入单个参数的函数里:
function (slotProps) {
// 插槽内容
}
复制代码
这意味着 v-slot
的值实际上能够是任何可以做为函数定义中的参数的 JavaScript
表达式。因此在支持的环境下 (单文件组件或现代浏览器),你也可使用 ES2015
解构来传入具体的插槽 prop
,以下:
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
复制代码
这样可使模板更简洁,尤为是在该插槽提供了多个 prop
的时候。它一样开启了 prop
重命名等其它可能,例如将 user
重命名为 person
:
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>
复制代码
你甚至能够定义后备内容,用于插槽 prop
是 undefined
的情形:
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>
复制代码
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
复制代码
跟 v-on
和 v-bind
同样,v-slot
也有缩写,即把参数以前的全部内容 (v-slot
:) 替换为字符 #
。例如v-slot:header
能够被重写为 #header
:
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>
复制代码
然而,和其它指令同样,该缩写只在其有参数的时候才可用。这意味着如下语法是无效的:
<!-- 这样会触发一个警告 -->
<current-user #="{ user }">
{{ user.firstName }}
</current-user>
复制代码
若是你但愿使用缩写的话,你必须始终以明确插槽名取而代之:
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>
复制代码
插槽 prop
容许咱们将插槽转换为可复用的模板,这些模板能够基于输入的 prop
渲染出不一样的内容。这在设计封装数据逻辑同时容许父级组件自定义部分布局的可复用组件时是最有用的。
例如,咱们要实现一个 <todo-list>
组件,它是一个列表且包含布局和过滤逻辑:
<ul>
<li
v-for="todo in filteredTodos"
v-bind:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
复制代码
咱们能够将每一个 todo
做为父级组件的插槽,以此经过父级组件对其进行控制,而后将 todo
做为一个插槽prop
进行绑定:
<ul>
<li
v-for="todo in filteredTodos"
v-bind:key="todo.id"
>
<!--
咱们为每一个 todo 准备了一个插槽,
将 `todo` 对象做为一个插槽的 prop 传入。
-->
<slot name="todo" v-bind:todo="todo">
<!-- 后备内容 -->
{{ todo.text }}
</slot>
</li>
</ul>
复制代码
如今当咱们使用 <todo-list>
组件的时候,咱们能够选择为 todo
定义一个不同的 <template>
做为替代方案,而且能够从子组件获取数据:
<todo-list v-bind:todos="todos">
<template v-slot:todo="{ todo }">
<span v-if="todo.isComplete">✓</span>
{{ todo.text }}
</template>
</todo-list>
复制代码
从新建立动态组件的行为一般是很是有用的,可是有一些标签的组件实例可以被在它们第一次被建立的时候缓存下来。为了解决这个问题,咱们能够用一个 <keep-alive>
元素将其动态组件包裹起来。
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
复制代码
当页面加载时,该元素将得到焦点 (注意:autofocus
在移动版 Safari
上不工做)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当仍是处于聚焦状态。如今让咱们用指令来实现这个功能:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
复制代码
若是想注册局部指令,组件中也接受一个 directives
的选项:
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
复制代码
而后你能够在模板中任何元素上使用新的 v-focus
属性,以下:
<input v-focus>
复制代码
一个指令定义对象能够提供以下几个钩子函数 (均为可选):
bind
:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不必定已被插入文档中)。
update
:所在组件的 VNode
更新时调用,可是可能发生在其子 VNode
更新以前。指令的值可能发生了改变,也可能没有。可是你能够经过比较更新先后的值来忽略没必要要的模板更新 (详细的钩子函数参数见下)。
componentUpdated
:指令所在组件的 VNode
及其子 VNode
所有更新后调用。
unbind
:只调用一次,指令与元素解绑时调用。
接下来咱们来看一下钩子函数的参数 (即 el
、binding
、vnode
和 oldVnode
)。
指令钩子函数会被传入如下参数:
el
:指令所绑定的元素,能够用来直接操做 DOM
。binding
:一个对象,包含如下属性:
name
:指令名,不包括 v-
前缀。value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为 2
。oldValue
:指令绑定的前一个值,仅在 update
和 componentUpdated
钩子中可用。不管值是否改变均可用。expression
:字符串形式的指令表达式。例如 v-my-directive="1 + 1"
中,表达式为 "1 + 1"
。arg
:传给指令的参数,可选。例如 v-my-directive:foo
中,参数为 "foo"
。modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
。vnode
:Vue
编译生成的虚拟节点。移步 VNode
API 来了解更多详情。oldVnode
:上一个虚拟节点,仅在 update
和 componentUpdated
钩子中可用。除了
el
以外,其它参数都应该是只读的,切勿进行修改。若是须要在钩子之间共享数据,建议经过元素的dataset
来进行。
Vue.js
容许你自定义过滤器,可被用于一些常见的文本格式化。过滤器能够用在两个地方:双花括号插值和 v-bind
表达式 (后者从 2.1.0+
开始支持)。过滤器应该被添加在 JavaScript
表达式的尾部,由“管道”符号指示:
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
复制代码
你能够在一个组件的选项中定义本地的过滤器:
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
复制代码
或者在建立 Vue 实例以前全局定义过滤器:
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
复制代码
当全局过滤器和局部过滤器重名时,会采用局部过滤器。
过滤器是 JavaScript 函数,所以能够接收参数:
{{ message | filterA('arg1', arg2) }}
复制代码
这里,filterA
被定义为接收三个参数的过滤器函数。其中 message
的值做为第一个参数,普通字符串 'arg1'
做为第二个参数,表达式arg2
的值做为第三个参数。
分享不易,喜欢的话必定别忘了点💖!!!
只关注不点💖的都是耍流氓,只收藏也不点💖的也同样是耍流氓。
结束👍👍👍。