基础
生命周期
可以看到在vue一整个的生命周期中会有很多***钩子函数***提供给我们在vue生命周期不同的时刻进行操作:
beforeCreate
在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
created
实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
beforeMount
在挂载开始之前被调用:相关的 render 函数(模板)首次被调用。
例如通过v-for生成的html还没有被挂载到页面上
mounted
el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
注意 mounted
不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 mounted
内部使用 vm.$nextTick:
有初始值的DOM渲染,例如我们的初始数据list,渲染出来的li,只有这里才能获取
beforeUpdate
数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。 你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
当我们更改Vue的任何数据,都会触发该函数
该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。如果要相应状态改变,通常最好使用**计算属性(computed)**或 watcher取而代之。
注意 updated
不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated
里使用 vm.$nextTick
该钩子在服务器端渲染期间不被调用。
activated
被 keep-alive 缓存的组件激活时调用。
该钩子在服务器端渲染期间不被调用。
deactivated
被 keep-alive 缓存的组件停用时调用。
该钩子在服务器端渲染期间不被调用。
beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。
该钩子在服务器端渲染期间不被调用。
destroyed
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。
该钩子在服务器端渲染期间不被调用。
errorCaptured
-
类型:
(err: Error, vm: Component, info: string) => ?boolean
-
详细:
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回
false
以阻止该错误继续向上传播。你可以在此钩子中修改组件的状态。因此在捕获错误时,在模板或渲染函数中有一个条件判断来绕过其它内容就很重要;不然该组件可能会进入一个无限的渲染循环。
错误传播规则
- 默认情况下,如果全局的
config.errorHandler
被定义,所有的错误仍会发送它,因此这些错误仍然会向单一的分析服务的地方进行汇报。 - 如果一个组件的继承或父级从属链路中存在多个
errorCaptured
钩子,则它们将会被相同的错误逐个唤起。 - 如果此
errorCaptured
钩子自身抛出了一个错误,则这个新错误和原本被捕获的错误都会发送给全局的config.errorHandler
。 - 一个
errorCaptured
钩子能够返回false
以阻止错误继续向上传播。本质上是说“这个错误已经被搞定了且应该被忽略”。它会阻止其它任何会被这个错误唤起的errorCaptured
钩子和全局的config.errorHandler
。
- 默认情况下,如果全局的
数据
data
data 应该只能是数据,类似于普通js中定义的全局变量
注意:
- 以
_
或$
开头的 property 不会被 Vue 实例代理,因为它们可能和 Vue 内置的 property、API 方法冲突。 - 当一个组件被定义,
data
必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果data
仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供data
函数,每次创建一个新实例后,我们能够调用data
函数,从而返回初始数据的一个全新副本数据对象。
props
用于接收来自父组件的数据。
你可以基于对象的语法使用以下选项:
type
:可以是下列原生构造函数中的一种:String
、Number
、Boolean
、Array
、Object
、Date
、Function
、Symbol
、任何自定义构造函数、或上述内容组成的数组。会检查一个 prop 是否是给定的类型,否则抛出警告。Prop 类型的更多信息在此。default
:any
为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。required
:Boolean
定义该 prop 是否是必填项。在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出。validator
:Function
自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 false 的值 (也就是验证失败),一个控制台警告将会被抛出。
computed
根据data中某个值的变换进行计算
<template>
<div id="app">
<input type="number" v-model="val"/>
<br/>
+10计算结果:{{add10}}
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
val: 0
}
},
computed: {
add10: function () {
return parseFloat(this.val) + 10
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
methods
定义方法的位置
注意:
不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++
)。理由是箭头函数绑定了父级作用域的上下文,所以 this
将不会按照期望指向 Vue 实例,this.a
将是 undefined。
watch
监听data中某个值的变化
注意:
不应该使用箭头函数来定义 watcher 函数 (例如 searchQuery: newValue => this.updateAutocomplete(newValue)
)。理由是箭头函数绑定了父级作用域的上下文,所以 this
将不会按照期望指向 Vue 实例,this.updateAutocomplete
将是 undefined。
var vm = new Vue({
data: {
a: 1,
b: 2,
c: 3,
d: 4,
e: {
f: {
g: 5
}
}
},
watch: {
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
// 方法名
b: 'someMethod',
// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
c: {
handler: function (val, oldVal) { /* ... */ },
deep: true
},
// 该回调将会在侦听开始之后被立即调用
d: {
handler: 'someMethod',
immediate: true
},
// 你可以传入回调数组,它们会被逐一调用
e: [
'handle1',
function handle2 (val, oldVal) { /* ... */ },
{
handler: function handle3 (val, oldVal) { /* ... */ },
/* ... */
}
],
// watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
})
vm.a = 2 // => new: 2, old: 1
资源
directives
包含 Vue 实例可用指令的哈希表。
注册方式
-
全局注册
Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时 inserted: function (el) { el.focus(); } })
-
局部注册
directives: { focus: { inserted: function (el) { el.focus(); } } }
然后你可以在模板中任何元素上使用新的
v-focus
property,如下:<input v-focus>
钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
-
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 -
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 -
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。 -
componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。 -
unbind
:只调用一次,指令与元素解绑时调用。
钩子函数参数
指令钩子函数会被传入以下参数:
el
:指令所绑定的元素,可以用来直接操作 DOM。binding
:一个对象,包含以下 property: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
来进行。
这是一个使用了这些 property 的自定义钩子样例:
<div v-demo:foo.a.b="message"></div>
Vue.directive('demo', {
bind: function (el, binding, vnode) {
var s = JSON.stringify
el.innerHTML =
'name: ' + s(binding.name) + '<br>' +
'value: ' + s(binding.value) + '<br>' +
'expression: ' + s(binding.expression) + '<br>' +
'argument: ' + s(binding.arg) + '<br>' +
'modifiers: ' + s(binding.modifiers) + '<br>' +
'vnode keys: ' + Object.keys(vnode).join(', ')
}
})
结果:
name: "demo"
value: "信息"
expression: "message"
argument: "foo"
modifiers: {"a":true,"b":true}
vnode keys: tag, data, children, text, elm, ns, context, fnContext, fnOptions, fnScopeId, key, componentOptions, componentInstance, parent, raw, isStatic, isRootInsert, isComment, isCloned, isOnce, asyncFactory, asyncMeta, isAsyncPlaceholder
动态指令参数
指令的参数可以是动态的。例如,在 v-mydirective:[argument]="value"
中,argument
参数可以根据组件实例数据进行更新!这使得自定义指令可以在应用中被灵活使用。
例如你想要创建一个自定义指令,用来通过固定布局将元素固定在页面上。我们可以像这样创建一个通过指令值来更新竖直位置像素值的自定义指令:
<div v-pin:top="200">测试</div>
Vue.directive('pin', {
bind: function (el, binding) {
el.style.position = "fixed"
var s = binding.arg == "left" ? "left" : "top"
el.style[s] = binding.value + 'px'
}
})
函数简写
在很多时候,你可能想在 bind
和 update
时触发相同行为,而不关心其它的钩子。比如这样写:
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
对象字面量
如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
Vue.directive('demo', function (el, binding) {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})
filters
包含 Vue 实例可用过滤器的哈希表。
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind
表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
-
全局注册
Vue.filter('capitalize', function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toLowerCase() + value.slice(1) })
-
局部注册
filters: { capitalize: function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } },
过滤器函数总接收表达式的值 (之前的操作链的结果) 作为第一个参数。在上述例子中,capitalize
过滤器函数将会收到 message
的值作为第一个参数。
过滤器可以串联:
{{ message | filterA | filterB }}
在这个例子中,filterA
被定义为接收单个参数的过滤器函数,表达式 message
的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB
,将 filterA
的结果传递到 filterB
中。
过滤器是 JavaScript 函数,因此可以接收参数:
{{ message | filterA('arg1', arg2) }}
这里,filterA
被定义为接收三个参数的过滤器函数。其中 message
的值作为第一个参数,普通字符串 'arg1'
作为第二个参数,表达式 arg2
的值作为第三个参数。
components
包含 Vue 实例可用组件的哈希表。
注册或获取全局组件。注册还会自动使用给定的 id
设置组件的名称
-
全局注册
// 注册组件,传入一个扩展过的构造器 Vue.component('my-component', Vue.extend({ /* ... */ })) // 注册组件,传入一个选项对象 (自动调用 Vue.extend) Vue.component('my-component', { /* ... */ }) // 获取注册的组件 (始终返回构造器) var MyComponent = Vue.component('my-component')
-
局部注册
components: { ButtonCounter: { data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' } },
注意
You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
解决方式
// 在vue.config.js中添加如下配置
module.exports = {
runtimeCompiler: true
}
指令
v-text
更新元素的 textContent
。和使用{{}}效果相同,支持数据同步变更
<div><input v-model="textVal"/></div>
<div>输入的值 —— {{}}方式:<span>{{textVal}}</span></div>
<div>输入的值 —— v-text方式:<span v-text="textVal"></span></div>
v-html
更新元素的 innerHTML
。
注意:
- 内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译。
- 在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只在可信内容上使用
v-html
,永不用在用户提交的内容上。 - 在单文件组件里,
scoped
的样式不会应用在v-html
内部,因为那部分 HTML 没有被 Vue 的模板编译器处理。如果你希望针对v-html
的内容设置带作用域的 CSS,你可以替换为 CSS Modules 或用一个额外的全局<style>
元素手动设置类似 BEM 的作用域策略。
<template>
<div id="app">
<div>html:{{html}}}</div>
<div v-html="html"></div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg: "Hello World",
html: `<span class="my-class"> Hello World </span>`
}
}
}
</script>
<style scoped>
.my-class {
// 验证scoped内的样式能否生效
color: green;
}
</style>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.my-class {
color: red;
}
</style>
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KaIFmtoY-1621587137530)(C:\Users\mi\AppData\Roaming\Typora\typora-user-images\image-20210517105634343.png)]
v-show
根据表达式之真假值,切换元素的 display
CSS property。当条件变化时该指令触发过渡效果。
注意:
v-show
不支持<template>
元素,也不支持v-else
。v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS propertydisplay
。
<template>
<div id="app">
<input type="button" @click="flag = !flag" value="变更"/>
<div v-show="flag" class="my-class-1">flag 为 true</div>
<div v-show="!flag" class="my-class-2">flag 为 false</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg: "Hello World",
flag: true
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.my-class-1 {
color: red;
}
.my-class-2 {
color: green;
}
</style>
v-if
根据表达式的值的 truthiness 来有条件地渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。如果元素是 <template>
,将提出它的内容作为条件块。
注意:
v-if
支持<template>
元素,也支持v-else
和v-else-if
。- 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建,如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
- 不推荐同时使用
v-if
和v-for
,当v-if
与v-for
一起使用时,v-for
具有比v-if
更高的优先级。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
v-else和v-else-if
为 v-if
或者 v-else-if
添加“else 块”。可以链式调用。
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
限制:
前一兄弟元素必须有 v-if
或 v-else-if
。
v-for
基于源数据多次渲染元素或模板块。此指令之值,必须使用特定语法 alias in expression
,为当前遍历的元素提供别名:
预期:
Array | Object | number | string | Iterable (2.6 新增)
注意:
- 从 2.6 起,
v-for
也可以在实现了可迭代协议的值上使用,包括原生的Map
和Set
。不过应该注意的是 Vue 2.x 目前并不支持可响应的Map
和Set
值,所以无法自动探测变更。 v-for
的默认行为会尝试原地修改元素而不是移动它们。要强制其重新排序元素,你需要用特殊 attributekey
来提供一个排序提示。- 不要使用对象或数组之类的非基本类型值作为
v-for
的key
。请用字符串或数值类型的值。
触发视图更新:
- 数组
push()
尾部添加一个或者多个元素,并返回数组的新长度。pop()
删除数组的最后一个元素,并返回它的删除值。shift()
数组的第一个元素从其中删除,并返回第一个元素的值。unshift()
可向数组的开头添加一个或更多元素,并返回新的长度。splice()
来删除或插入元素,会修改原数组!sort()
排序reverse()
逆序数组
Vue.$set()可以用来操作数据或者对象
// 数组遍历
<div v-for="item in items"></div>
// 数组遍历,同时获取索引
<div v-for="(item, index) in items"></div>
// 对象遍历,只获取val
<div v-for="val in object"></div>
// 对象遍历,同时获取name
<div v-for="(val, key) in object"></div>
// 对象遍历,同时获取索引
<div v-for="(val, name, index) in object"></div>
<template>
<div id="app">
<input type="button" @click="$set(obj, 'i', 1)" value="新增 i-1"/>
<tr v-for="(val,name,index) in obj" :key="val">
<td>{{index}}_{{name}}:{{val}}</td>
</tr>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg: "Hello World",
flag: true,
obj: {name: "名字", value: '值'}
}
},
methods: {}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.my-class-1 {
color: red;
}
.my-class-2 {
color: green;
}
</style>
v-on
绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。用在普通元素上时,只能监听原生 DOM 事件。用在自定义元素组件上时,也可以监听子组件触发的自定义事件。
缩写:@
修饰符:
.stop
- 调用event.stopPropagation()
。 不再派发事件。.prevent
- 调用event.preventDefault()
。 取消事件的默认动作。.capture
- 添加事件侦听器时使用 capture 模式。.self
- 只当事件是从侦听器绑定的元素本身触发时才触发回调。.{keyCode | keyAlias}
- 只当事件是从特定键触发时才触发回调。.native
- 监听组件根元素的原生事件。.once
- 只触发一次回调。.left
- (2.2.0) 只当点击鼠标左键时触发。.right
- (2.2.0) 只当点击鼠标右键时触发。.middle
- (2.2.0) 只当点击鼠标中键时触发。.passive
- (2.3.0) 以{ passive: true }
模式添加侦听器
注意:
- 在监听原生 DOM 事件时,方法以事件为唯一的参数。如果使用内联语句,语句可以访问一个
$event
property:v-on:click="handle('ok', $event)"
。 - 从
2.4.0
开始,v-on
同样支持不带参数绑定一个事件/监听器键值对的对象。注意当使用对象语法时,是不支持任何修饰器的。
<template>
<div id="app">
<!--右键事件,阻止原有事件-->
<input type="button" v-on:click.right.prevent="addIndex" value="新增"/>
<input type="button" @click.right.prevent="addIndex" value="新增"/>
<h1>Hello World{{index}}</h1>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
index: 0
}
},
methods: {
addIndex: function () {
this.index++
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>
<!-- 动态事件 (2.6.0+) -->
<button v-on:[event]="doThis"></button>
<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
<!-- 动态事件缩写 (2.6.0+) -->
<button @[event]="doThis"></button>
<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button>
<!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter">
<!-- 键修饰符,键代码 -->
<input @keyup.13="onEnter">
<!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>
<!-- 对象语法 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
v-bind
动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。
缩写: :
修饰符:
.prop
- 作为一个 DOM property 绑定而不是作为 attribute 绑定。自定义的attribute不会渲染.camel
- (2.1.0+) 将 kebab-case attribute 名转换为 camelCase。(从 2.1.0 开始支持) ,支持驼峰命名.sync
(2.3.0+) 语法糖,会扩展成一个更新父组件绑定值的v-on
侦听器。
注意:
- 在绑定
class
或style
attribute 时,支持其它类型的值,如数组或对象。 - 在绑定 prop 时,prop 必须在子组件中声明。可以用修饰符指定不同的绑定类型。
- 没有参数时,可以绑定到一个包含键值对的对象。注意此时
class
和style
绑定不支持数组和对象。
<!-- 绑定一个 attribute -->
<img v-bind:src="imageSrc">
<!-- 动态 attribute 名 (2.6.0+) -->
<button v-bind:[key]="value"></button>
<!-- 缩写 -->
<img :src="imageSrc">
<!-- 动态 attribute 名缩写 (2.6.0+) -->
<button :[key]="value"></button>
<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName">
<!-- class 绑定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]">
<!-- style 绑定 -->
<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>
<!-- 绑定一个全是 attribute 的对象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
<!-- 通过 prop 修饰符绑定 DOM attribute -->
<div v-bind:text-content.prop="text"></div>
<!-- prop 绑定。“prop”必须在 my-component 中声明。-->
<my-component :prop="someThing"></my-component>
<!-- 通过 $props 将父组件的 props 一起传给子组件 -->
<child-component v-bind="$props"></child-component>
<!-- XLink -->
<svg><a :xlink:special="foo"></a></svg>
<!-- 子组件 -->
<template>
<div>
<div :style="myStyle">Hello World</div>
<input type="button" @click="$emit('enlarge-text')" value="增大字体"/>
</div>
</template>
<script>
export default {
name: "ChildComponents",
props: ['myStyle']
}
</script>
<style scoped>
</style>
<!-- 父组件 -->
<template>
<div id="app">
<child-components :myStyle="{color:'red',fontSize:fontSize+'px'}"
@enlarge-text="fontSize+=1"></child-components>
</div>
</template>
<script>
import ChildComponents from "@/components/ChildComponents";
export default {
name: 'App',
components: {ChildComponents},
data() {
return {
fontSize: 12
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
v-model
在表单控件或者组件上创建双向绑定。
限制:
<input>
<select>
<textarea>
- components
修饰符:
.lazy
- 取代input
监听change
事件.number
- 输入字符串转为有效的数字.trim
- 输入首尾空格过滤
<template>
<div id="app">
<input v-model="val" type="text"/>
<input v-model="val" type="text"/>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
val: "Hello World"
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
v-slot
提供具名插槽或需要接收 prop 的插槽。
缩写: #
v-pre
跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
<span v-pre>{{ this will not be compiled }}</span>
执行结果:
{{ this will not be compiled }}
v-cloak
这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none }
一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
在使用{{}}展示或更新页面数据时:当网速比较慢时,会出现一个不好的过度现象,会让用户先看到我们的表达式(上面页面中的{{msg}}),然后才看到data中的值------->即所谓的闪烁问题!
{{}}和v-text
1》默认v-text是没有闪烁问题,{{}}存在闪烁的问题
2》 v-text会覆盖掉元素中原本的内容,但是{{}}只会替换自己的这个占位符。
v-once
只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。数据更新不会再触发组件渲染
特殊的属性
key
有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
最常见的用例是结合 v-for
:
<ul> <li v-for="item in items" :key="item.id">...</li> </ul>
它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有用:
- 完整地触发组件的生命周期钩子
- 触发过渡
<transition>
<span :key="text">{{ text }}</span>
</transition>
当 text
发生改变时,<span>
总是会被替换而不是被修改,因此会触发过渡。
ref
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs
对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件
注意:
- 因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!
$refs
也不是响应式的,因此你不应该试图用它在模板中做数据绑定。
<template>
<div id="app">
<p ref="myp">Hello World</p>
<input type="button" @click="changeText" value="转换"/>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
val: "Hello World"
}
},
methods: {
changeText: function () {
console.info(this.$refs)
this.$refs.myp.innerText = "Hello World1"
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
[v-cloak] {
display: none;
}
</style>
is
用于动态组件且基于 DOM 内模板的限制来工作。(自定义组件再详细描述)
内置的组件
component
渲染一个“元组件”为动态组件。依 is
的值,来决定哪个组件被渲染。
<!-- 动态组件由 vm 实例的 `componentId` property 控制 -->
<component :is="componentId"></component>
<!-- 也能够渲染注册过的组件或 prop 传入的组件 -->
<component :is="$options.components.child"></component>
transition
<transition>
元素作为单个元素/组件的过渡效果。<transition>
只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在可被检查的组件层级中。
- Props:
name
- string,用于自动生成 CSS 过渡类名。例如:name: 'fade'
将自动拓展为.fade-enter
,.fade-enter-active
等。默认类名为"v"
appear
- boolean,是否在初始渲染时使用过渡。默认为false
。css
- boolean,是否使用 CSS 过渡类。默认为true
。如果设置为false
,将只通过组件事件触发注册的 JavaScript 钩子。type
- string,指定过渡事件类型,侦听过渡何时结束。有效值为"transition"
和"animation"
。默认 Vue.js 将自动检测出持续时间长的为过渡事件类型。mode
- string,控制离开/进入过渡的时间序列。有效的模式有"out-in"
和"in-out"
;默认同时进行。duration
- number | {enter
: number,leave
: number } 指定过渡的持续时间。默认情况下,Vue 会等待过渡所在根元素的第一个transitionend
或animationend
事件。enter-class
- stringleave-class
- stringappear-class
- stringenter-to-class
- stringleave-to-class
- stringappear-to-class
- stringenter-active-class
- stringleave-active-class
- stringappear-active-class
- string
- 事件:
before-enter
before-leave
before-appear
enter
leave
appear
after-enter
after-leave
after-appear
enter-cancelled
leave-cancelled
(v-show
only)appear-cancelled
transition-group
<transition-group>
元素作为多个元素/组件的过渡效果。<transition-group>
渲染一个真实的 DOM 元素。默认渲染 <span>
,可以通过 tag
attribute 配置哪个元素应该被渲染。
注意,每个 <transition-group>
的子节点必须有独立的 key,动画才能正常工作
<transition-group>
支持通过 CSS transform 过渡移动。当一个子节点被更新,从屏幕上的位置发生变化,它会被应用一个移动中的 CSS 类 (通过 name
attribute 或配置 move-class
attribute 自动生成)。如果 CSS transform
property 是“可过渡”property,当应用移动类时,将会使用 FLIP 技术使元素流畅地到达动画终点。
- Props:
tag
- string,默认为span
move-class
- 覆盖移动过渡期间应用的 CSS 类。- 除了
mode
,其他 attribute 和<transition>
相同。
- 事件:
- 事件和
<transition>
相同。
- 事件和
keep-alive
<keep-alive>
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition>
相似,<keep-alive>
是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
当组件在 <keep-alive>
内被切换,它的 activated
和 deactivated
这两个生命周期钩子函数将会被对应执行。
主要用于保留组件状态或避免重新渲染。
- Props:
include
- 字符串或正则表达式。只有名称匹配的组件会被缓存。exclude
- 字符串或正则表达式。任何名称匹配的组件都不会被缓存。max
- 数字。最多可以缓存多少组件实例。
提升
深入组件
组件注册
-
全局注册
Vue.component('button-counter', { data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' })
-
局部注册
import ChildComponents from "@/components/ChildComponents"; export default { name: 'App', components: [ChildComponents], data() { return {} } }
-
基础组件自动化全局注册
import Vue from 'vue' import upperFirst from 'lodash/upperFirst' import camelCase from 'lodash/camelCase' const requireComponent = require.context( // 其组件目录的相对路径 './components', // 是否查询其子目录 false, // 匹配基础组件文件名的正则表达式 /Base[A-Z]\w+\.(vue|js)$/ ) requireComponent.keys().forEach(fileName => { // 获取组件配置 const componentConfig = requireComponent(fileName) // 获取组件的 PascalCase 命名 const componentName = upperFirst( camelCase( // 获取和目录深度无关的文件名 fileName .split('/') .pop() .replace(/\.\w+$/, '') ) ) // 全局注册组件 Vue.component( componentName, // 如果这个组件选项是通过 `export default` 导出的, // 那么就会优先使用 `.default`, // 否则回退到使用模块的根。 componentConfig.default || componentConfig ) })
父子组件之间通信
prop(向子组件传递数据)
单项数据流
父级组件的prop更新会向下级流动到子组件,但是反过来则不行,这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
-
这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。
在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
-
这个 prop 以一种原始的值传入且需要进行转换。
在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
prop验证
注意:
那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data
、computed
等) 在 default
或 validator
函数中是不可用的。
$emit
prop不建议子组件更新prop的内容,影响父组件,而如果子组件想要更新父组件的值则用$emit调用父组件方法,更新父组件的值
父组件
<template>
<div id="app">
<child-components :myStyle="fontSize" @enlarge-text="changeFontSize"></child-components>
</div>
</template>
<script>
import ChildComponents from "@/components/ChildComponents";
export default {
name: 'App',
components: {ChildComponents},
data() {
return {
fontSize: 12
}
},
methods: {
changeFontSize: function (fontSize) {
this.fontSize = parseFloat(fontSize)
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
子组件
<template>
<div>
<input type="number" v-model="fontSize"/><br/>
<div :style="{fontSize:myStyle+'px'}">Hello World</div>
<input type="button" @click="$emit('enlarge-text',fontSize)" value="增大字体"/>
</div>
</template>
<script>
export default {
name: "ChildComponents",
props: ['myStyle'],
data() {
return {
fontSize: 0
}
}
}
</script>
<style scoped>
</style>
注意:
不同于组件和 prop,事件名不会被用作一个 JavaScript 变量名或 property 名,所以就没有理由使用 camelCase 或 PascalCase 了。并且 v-on
事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent
将会变成 v-on:myevent
——导致 myEvent
不可能被监听到。
因此,我们推荐你始终使用 kebab-case 的事件名。
自定义组件的 v-model
实现数据的双向绑定
子组件
<template>
<div>
<input
type="checkbox"
:checked="checked"
@change="$emit('change', $event.target.checked)"
>复选框
</div>
</template>
<script>
export default {
name: "BaseCheckbox",
model:{
prop: 'checked',
event: 'change'
},
props: {checked: Boolean}
}
</script>
<style scoped>
</style>
父组件
<template>
<div id="app">
<base-checkbox v-model="checked"></base-checkbox>
<br/>
是否点击:{{checked}}
</div>
</template>
<script>
import BaseCheckbox from "@/components/BaseCheckbox";
export default {
name: 'App',
components: {BaseCheckbox},
data() {
return {
checked: false
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
<base-checkbox v-model="checked"></base-checkbox>
model:{
prop: 'checked',
event: 'change'
},
上述代码相当于
<base-checkbox :checked="checked" @change='val => { checked = val }'></base-checkbox>
如果修改model为:
model:{
prop: 'check',
event: 'click'
},
那么上述代码就相当于:
<base-checkbox :check="checked" @click='val => { checked = val }'></base-checkbox>
将原生事件绑定到组件上
v-on
的 .native
修饰符可以监听组件根元素的事件,但是如果组件根元素不是自己想要监听的目标元素就有局限
$attrs
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class
和 style
除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class
和 style
除外),并且可以通过 v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用。
$listeners
包含了父作用域中的 (不含 .native
修饰器的) v-on
事件监听器。它可以通过 v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用。
子组件
<template>
<label>
{{label}}:
<input v-bind="$attrs" v-on="inputListeners" :value="value"/>
</label>
</template>
<script>
export default {
name: "BaseInput",
mounted() {
console.log("attrs", this.$attrs)
console.log("listeners", this.$listeners)
},
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)
}
}
)
}
},
}
</script>
<style scoped>
</style>
父组件
<template>
<div id="app">
<base-input label="输入框" type="text" v-model="value" @focus="value=20"></base-input>
<br/>
输入的内容:{{value}}
</div>
</template>
<script>
import BaseInput from "@/components/BaseInput";
export default {
name: 'App',
components: {BaseInput},
data() {
return {
value: ""
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
注意:
子组件不声明model,则是使用默认的
model: {
prop: 'value',
event: 'input'
},
如果自定义方法则需要对自定义的方法进行覆盖
.sync修饰符
会扩展成一个更新父组件绑定值的 v-on
侦听器。
子组件
<template>
<label>输入框:<input :value="value" type="text" @input="(event)=>$emit('update:value',event.target.value)"/></label>
</template>
<script>
export default {
name: "ChildSync",
props: ["value"]
}
</script>
<style scoped>
</style>
父组件
<template>
<div id="app">
<child-sync :value1="value" @update:value="(val)=>value=val"></child-sync>
<child-sync :value.sync="value"></child-sync>
<br/>
输入的内容:{{value}}
</div>
</template>
<script>
import ChildSync from "@/components/ChildSync";
export default {
name: 'App',
components: {ChildSync},
data() {
return {
value: ""
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
<child-sync :value.sync="value"></child-sync>
等效于(扩展的侦听器)
<child-sync :value="value" @update:value="value = $event"></child-sync>
插槽
如果没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃
作用域:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
具名插槽
<slot>
元素有一个特殊的 attribute:name
。这个 attribute 可以用来定义额外的插槽。一个不带 name
的 <slot>
出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
任何没有被包裹在带有 v-slot
的 <template>
中的内容都会被视为默认插槽的内容。
父组件使用插槽的值
子组件
<!-- 把值绑定到插槽的属性上 -->
<p><slot name="body" :data="item"></slot></p>
父组件
<!-- 通过scope可以获取到子组件绑定的值 -->
<template v-slot:body="scope">{{scope}}</template>