以下内容根据Vue.js Guide Essentials部分速记。
不含动画/mixin/SSR/路由/状态管理等部分.
Introduction
建议阅读原文 https://vuejs.org/v2/guide/in…
什么是Vue
开始
声明式渲染
条件与循环
处理用户输入
组件
The Vue Instance
建议阅读原文 https://vuejs.org/v2/guide/in…
创建Vue实例
data & methods
实例生命周期及其钩子函数
生命周期图表
Template Syntax
模板语法 https://vuejs.org/v2/guide/sy…
Mustache模板中插入值
html标签内的文本:使用{{变量名}}
原始html:标签属性内使用v-html=”变量名”
标签内的属性:v-bind:属性名=”变量名”
也可用JS的表达式来替换上述”变量名”,但只能是单条表达式,不能是语句
v-if等指令
argument:如v-bind:href中的href
modifier:如v-on:submit.prevent=”onSubmit”中的.prevent
v-bind、v-on简写
v-bind:href等同于:href
v-on:click=”doSth”等同于@cllick=”doSth”
Computed Properties and Watchers
https://vuejs.org/v2/guide/co…
Computed Properties
js内的computed函数
computed属性会被缓存,出于性能考虑,不建议在{{}}中的表达式内执行method
computed属性会被缓存,即使是一个函数,也并不会每次都执行
computed是根据已有变量计算出的新变量
Watchers
watch用来监视已有变量,执行进一步操作.
Class and Style Bindings
(html标签中的)class与style绑定 https://vuejs.org/v2/guide/cl…
class
v-bind:class内的表达式会自动和class内已有class同时存在
可以:
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
data: {
isActive: true,
hasError: false
}
结果: <div class="static active"></div>
也可以:
<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}
也可以使用computed函数计算出的属性值/对象为class内的属性值/对象做数据来源
也可以使用数组来应用多个class名:
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
结果: <div class="active text-danger"></div>
与其在array内使用三目运算符,不如嵌套使用object与array:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
使用js定义组件时的class先出现,使用组件时添加的class(无论直接使用class还是v-bind:class)后出现.
Vue.component('my-component', {
template: '<p class="foo bar">Hi</p>'
})
1.
<my-component class="baz boo"></my-component>
结果: <p class="foo bar baz boo">Hi</p>
2.
<my-component v-bind:class="{ active: isActive }"></my-component>
结果: <p class="foo bar active">Hi</p>
style 内联样式
和class很像,但必须是object
可以:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
也可以:
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
也可以使用computed
也可以用对象数组:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
(baseStyles/overridingStyles都是object)
使用v-bind:style会自动加上prefix
也可以手动加:
<div v-bind:style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
Conditional Rendering
条件渲染 https://vuejs.org/v2/guide/co…
v-if/v-else/v-else-if
<h1 v-if="ok">Yes</h1>
也可以配合else使用:
<h1 v-else>No</h1>
v-if可以和template元素结合使用,最终结果不包含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:
<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-else-if必须紧跟v-if或v-else-if
相同元素自动复用:
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
修改loginType的值不会产生新的input元素
会自动复用之前的元素
如果想避免自动复用,给input标签加上不同的key:
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
(label标签依然会被复用,因为没有指定key)
v-show
只控制css的display.
不支持template元素,不支持和v-else同时使用
经常切换显示隐藏,使用v-show
偶尔显示,变动少,使用v-if
v-if和v-for配合使用
v-for的优先级高.(见下一节)
List Rendering
列表渲染 https://vuejs.org/v2/guide/li…
v-for遍历array
<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
v-for内部可以访问外部的所有变量,
还可以获取index:
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
注意v-for内的index和parentMessage
v-for遍历object
<ul id="v-for-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
new Vue({
el: '#v-for-object',
data: {
object: {
firstName: 'John',
lastName: 'Doe',
age: 30
}
}
})
结果:
<ul id="v-for-object" class="demo">
<li>John</li>
<li>Doe</li>
<li>30</li>
</ul>
(只有值,不管键名)
或者:
<div v-for="(value, key) in object">
{{ key }}: {{ value }}
</div>
<div v-for="(value, key, index) in object">
{{ index }}. {{ key }}: {{ value }}
</div>
(有index/键名/值)
内部引擎使用Object.keys()来遍历,不保证所有浏览器都表现一致
key
如果数据的顺序变动,vue不是调整各个子元素的顺序,而是直接修改现有子元素的内容
为了规避以上情况,可以给每个子元素绑定key:
<div v-for="item in items" :key="item.id">
<!-- content -->
</div>
探测数组更改情况
以下的数组方法都已经被Vue封装好,vue可以观察到原有数组的更改
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
替换数组:
filter()
concat()
slice()
...
以上方法不改变原有数组,直接返回新数组.
vue的使用方法如下:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
数组更改警告
由于JS的限制,以下两种情况无法被vue观察到,请使用替代方案:
- 直接用索引设置某项的值
// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
或者
// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)
- 修改数组的长度
example1.items.splice(newLength)
对象更改警告
由于JS限制,vue观察不到动态添加根级别的属性到已有的实例:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` is now reactive
vm.b = 2
// `vm.b` is NOT reactive
但是允许内层的object添加属性:
var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})
可以使用
Vue.set(vm.userProfile, 'age', 27)
或者
vm.$set(this.userProfile, 'age', 27)
如果需要更新多个属性,直接使用添加属性后的新对象替换原有对象,
不要:
Object.assign(this.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
而应该这样:
this.userProfile = Object.assign({}, this.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
筛选与排序
不对原始数据修改,仅对需要显示的作筛选排序等.
灵活运用computed或methods:
computed:
<li v-for="n in evenNumbers">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
methods(在嵌套v-for循环内尤其有用):
<li v-for="n in even(numbers)">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
v-for with a Range
v-for可以直接遍历整数:
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
v-for on a <template>
与v-if类似,可以结合template使用
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider"></li>
</template>
</ul>
v-for与v-if
v-for has a higher priority than v-if.
v-if将在每个for循环内都执行
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
如果想先if出结果,再for循环,请嵌套使用(也可用template):
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
v-for与组件
可以在自定义组件内使用v-for,就像其他普通元素一样.
<my-component v-for="item in items" :key="item.id"></my-component>
在组件内使用v-for, :key为必须的
但仍需注意父子组件之间的数据传递.
(子组件的js内使用props, 标签内使用v-bind:变量名,使用$emit与父组件通信)
Event Handling
事件处理 https://vuejs.org/v2/guide/ev…
标签内使用v-on:事件名=”xxx”
xxx可以是表达式(直接修改数据)
<div id="example-1">
<button v-on:click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
</div>
var example1 = new Vue({
el: '#example-1',
data: {
counter: 0
}
})
也可以是method方法名
<div id="example-2">
<!-- `greet` is the name of a method defined below -->
<button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// define methods under the `methods` object
methods: {
greet: function (event) {
// `this` inside methods points to the Vue instance
alert('Hello ' + this.name + '!')
// `event` is the native DOM event
if (event) {
alert(event.target.tagName)
}
}
}
})
// you can invoke methods in JavaScript too
example2.greet() // => 'Hello Vue.js!'
也可以是一段js语句,可以选择性地传递参数到method内:
<div id="example-3">
<button v-on:click="say('hi')">Say hi</button>
<button v-on:click="say('what')">Say what</button>
</div>
new Vue({
el: '#example-3',
methods: {
say: function (message) {
alert(message)
}
}
})
可以传递原始的DOM event参数,使用$event
:
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
// ...
methods: {
warn: function (message, event) {
// now we have access to the native event
if (event) event.preventDefault()
alert(message)
}
}
Event Modifiers 事件修饰符
可以多个同时使用,也可以只使用修饰符,不指定事件handler
.stop 停止向上传播
.prevent preventDefault
.capture 捕获子元素内已处理的事件
.self 只管本元素的事件,不考虑子元素
.once 触发至多一次,可以用在组件的自定义事件中
<!-- the submit event will no longer reload the page -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- modifiers can be chained -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- just the modifier -->
<form v-on:submit.prevent></form>
修饰符是有顺序的.
Therefore using @click.prevent.self will prevent all clicks while @click.self.prevent will only prevent clicks on the element itself.
按键修饰符
<input v-on:keyup.13="submit">
<input v-on:keyup.enter="submit">
<input @keyup.enter="submit">
完整的按键修饰符:
.enter
.tab
.delete (captures both “Delete” and “Backspace” keys)
.esc
.space
.up
.down
.left
.right
自定义按键修饰符:
// enable `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
自动按键修饰符
可以根据KeyboardEvent.key的名称,转换为小写-小写的形式,作为按键修饰符.
如: .page-down
<input @keyup.page-down="onPageDown">
In the above example, the handler will only be called if $event.key === 'PageDown'.
系统按键修饰符
.ctrl
.alt
.shift
.meta
Note: On Macintosh keyboards, meta is the command key (⌘). On Windows keyboards, meta is the windows key (⊞). On Sun Microsystems keyboards, meta is marked as a solid diamond (◆). On certain keyboards, specifically MIT and Lisp machine keyboards and successors, such as the Knight keyboard, space-cadet keyboard, meta is labeled “META”. On Symbolics keyboards, meta is labeled “META” or “Meta”.
.exact
单独按某个键,而不是组合按键中的其中一个键
<!-- this will fire even if Alt or Shift is also pressed -->
<button @click.ctrl="onClick">A</button>
<!-- this will only fire when Ctrl and no other keys are pressed -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- this will only fire when no system modifiers are pressed -->
<button @click.exact="onClick">A</button>
鼠标按钮修饰符
.left
.right
.middle
Form Input Bindings
表单输入绑定 https://vuejs.org/v2/guide/fo…
input(输入框、单选、多选)、textarea、select单选多选 等
基本用法
v-model,将忽略任何标签上的初始值,只把vue实例的data作为数据来源
input text
textarea
input checkbox
单选一 一个checkbox绑定到一个v-model,data为Boolean
多选多 多个checkbox绑定到同一个v-model,data为数组
input radio(多选一 圆按钮)
多选一 多个input radio绑定到同一个v-model,data为字符串,结果为input标签内的value
select
单选 建议第一项为一个disabled的option
多选,添加multiple属性
数据绑定
radio/select options的v-model为字符串,checkbox为布尔值
可以把v-model与自定义的value值做绑定,而不是浏览器默认的value值.
<input
type="checkbox"
v-model="toggle"
true-value="yes"
false-value="no"
>
// when checked:
vm.toggle === 'yes'
// when unchecked:
vm.toggle === 'no'
<input type="radio" v-model="pick" v-bind:value="a">
// when checked:
vm.pick === vm.a
<select v-model="selected">
<!-- inline object literal -->
<option v-bind:value="{ number: 123 }">123</option>
</select>
// when selected:
typeof vm.selected // => 'object'
vm.selected.number // => 123
修饰符
<!-- synced after "change" instead of "input" -->
<input v-model.lazy="msg" >
转换为数字类型
<input v-model.number="age" type="number">
去空格
<input v-model.trim="msg">
Components
组件 https://vuejs.org/v2/guide/co…
使用组件
https://vuejs.org/v2/guide/co…
全局注册、局部注册
data必须是一个函数
父子组件的关系: props down, events up
父组件通过prop把属性传递给子组件;子组件通过触发事件,把数据传递给父组件
Props
https://vuejs.org/v2/guide/co…
使用props传递数据
在子组件的js声明中,添加props(和data同级),props才能从父组件传递到子组件。
同时,需要在父组件标签中添加这个属性,该属性才能传递到子组件内。
camelCase-vs-kebab-case
html标签内不区分大小写,所以使用连接横杠-
,js的props内使用驼峰式命名法(首字母小写)
动态属性
通过v-bind:子组件数据名称=父组件数据名称
也可以v-bind=对象名
todo: {
text: 'Learn Vue',
isComplete: false
}
<todo-item v-bind="todo"></todo-item>
等同于
<todo-item
v-bind:text="todo.text"
v-bind:is-complete="todo.isComplete"
></todo-item>
标签中的属性字面量与v-bind:xxx的动态变量
直接使用属性,是字面量;v-bind:xx是表达式。
因此,直接使用属性,数据类型都是字符串;v-bind的在经过表达式运算之后,可以是数字类型(等等)
单向数据流
父组件的数据通过props传递给子组件,子组件通过computed或data来进行数据处理。
不要在子组件直接改变父组件的数据。
把父组件传递的数据当做子组件的初始值。
js中的对象和数组传递的是引用值,子组件如果修改,会影响父组件
prop验证
可以在子组件的js内,定义父组件需要传递给子组件的props名称、类型、是否必须等
无prop的属性
https://vuejs.org/v2/guide/co…
(子组件的js内)未定义某个prop,父组件直接在标签上传入某个prop
通常被用在class、style上
自定义事件
https://vuejs.org/v2/guide/co…
$on/$emit和浏览器原生的
EventTarget API
没有关系
在父组件的上下文环境中,使用v-on来监听子组件触发($emit)的事件
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
incrementCounter: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
使用.native修饰符去监听原生事件
使用.sync修饰符
<comp :foo.sync="bar"></comp>
实质为:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
在子组件的js内,需要触发update:foo
事件来更新foo的值
this.$emit('update:foo', newValue)
表单输入与自定义事件
https://vuejs.org/v2/guide/co…
<input v-model="something">
实质为:
<input
v-bind:value="something"
v-on:input="something = $event.target.value">
如果想让自定义组件与v-model协作,自定义组件需要:
- accept a
value
prop - 触发
input
事件,并传入新值
自定义组件的v-model
在js中添加model字段
Vue.component('my-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean,
// this allows using the `value` prop for a different purpose
value: String
},
// ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>
以上等同于:
<my-checkbox
:checked="foo"
@change="val => { foo = val }"
value="some value">
</my-checkbox>
非父子组件之间的通信
使用一个中间Vue实例来传递消息,或使用Vuex.
var bus = new Vue()
// in component A's method
bus.$emit('id-selected', 1)
// in component B's created hook
bus.$on('id-selected', function (id) {
// ...
})
内容分发与slot
https://vuejs.org/v2/guide/co…
单slot
用实际使用某个组件时写入到组件标签内的内容,
去替换组件原有的模板中的slot的内容.
(然后再将组件内的所有内容合并到父级元素内)
如果实际使用组件时,组件标签内没有内容,
则显示组件原有的模板中的slot内容.
(然后再将组件内的所有内容合并到父级元素内)
具有名字的slot
<div class="parent" id='parent' >
<my-comp>
content at parent
<div slot="footer">
<h2>
content at parent's footer slot
</h2>
</div>
</my-comp>
</div>
<p>
<h2>h2 inside p (wrong!!!)</h2>
</p>
<script>
;(function () {
// register
Vue.component('my-comp', {
template:
`<div>
content at my component. <br><br>
<slot>content at my-comp's slot</slot>
<pre>
<slot name="footer">content at my-comp's footer slot</slot>
</pre>
</div>`
})
var parent = new Vue({
el:'#parent'
})
})();
</script>
动态组件
https://vuejs.org/v2/guide/co…
var vm = new Vue({
el: '#example',
data: {
currentView: 'home'
},
components: {
home: { /* ... */ },
posts: { /* ... */ },
archive: { /* ... */ }
}
})
<component v-bind:is="currentView">
<!-- component changes when vm.currentView changes! -->
</component>
If you prefer, you can also bind directly to component objects:
var Home = {
template: '<p>Welcome home!</p>'
}
var vm = new Vue({
el: '#example',
data: {
currentView: Home
}
})
keep-alive
<keep-alive>
<component :is="currentView">
<!-- inactive components will be cached! -->
</component>
</keep-alive>
杂项
https://vuejs.org/v2/guide/co…
编写可复用组件
Props/Events/Slots 结合使用
引用子组件
直接用js访问子组件.
需要在使用子组件时,标签内添加ref="xxx"
var parent = new Vue({ el: '#parent' })
// 访问子组件实例
var child = parent.$refs.profile
异步组件
结合webpack的import()
高级异步组件
const AsyncComp = () => ({
// The component to load. Should be a Promise
component: import('./MyComp.vue'),
// A component to use while the async component is loading
loading: LoadingComp,
// A component to use if the load fails
error: ErrorComp,
// Delay before showing the loading component. Default: 200ms.
delay: 200,
// The error component will be displayed if a timeout is
// provided and exceeded. Default: Infinity.
timeout: 3000
})
组件命名约定
(使用JS)声明组件时, PascalCase
,
(标签内)使用组件时, <kebab-case>
递归组件
https://vuejs.org/v2/guide/co…
组件之间的相互引用
添加beforeCreate 函数
beforeCreate: function () {
this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue')
}
内联模板
模板的标签内添加inline-template
X-Templates
<script type="text/x-template" id="hello-world-template">
<p>Hello hello hello</p>
</script>
Vue.component('hello-world', {
template: '#hello-world-template'
})
v-once
很长很长的,只渲染一次的内容,比如,用户协议.