Vue2 基础
Vue 实例
创建 Vue 实例
js
new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
methods: {
greet() {
console.log(this.message)
}
}
})new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
methods: {
greet() {
console.log(this.message)
}
}
})实例选项
- el — 挂载的 DOM 元素选择器
- data — 组件数据,必须是函数(组件中)或对象(根实例)
- methods — 组件方法,this 指向组件实例
- computed — 计算属性,基于依赖缓存结果
- watch — 侦听属性,监听数据变化执行操作
实例属性和方法
通过 vm.$xxx 访问实例属性和方法:
js
vm.$data // 组件 data 对象
vm.$props // 接收的 props
vm.$el // 挂载的 DOM 元素
vm.$options // 初始化选项
vm.$parent // 父组件实例
vm.$root // 根组件实例
vm.$children // 子组件实例数组
vm.$refs // 持有注册了 ref 的 DOM 元素或组件
vm.$slots // 插槽内容
vm.$attrs // 未被 props 接收的属性
vm.$listeners // 父组件传递的事件(Vue2)
vm.$watch // 侦听数据变化
vm.$set // 向响应式对象添加属性
vm.$delete // 删除对象的响应式属性
vm.$emit // 触发自定义事件
vm.$forceUpdate // 强制更新
vm.$nextTick // DOM 更新后执行回调
vm.$destroy // 销毁实例vm.$data // 组件 data 对象
vm.$props // 接收的 props
vm.$el // 挂载的 DOM 元素
vm.$options // 初始化选项
vm.$parent // 父组件实例
vm.$root // 根组件实例
vm.$children // 子组件实例数组
vm.$refs // 持有注册了 ref 的 DOM 元素或组件
vm.$slots // 插槽内容
vm.$attrs // 未被 props 接收的属性
vm.$listeners // 父组件传递的事件(Vue2)
vm.$watch // 侦听数据变化
vm.$set // 向响应式对象添加属性
vm.$delete // 删除对象的响应式属性
vm.$emit // 触发自定义事件
vm.$forceUpdate // 强制更新
vm.$nextTick // DOM 更新后执行回调
vm.$destroy // 销毁实例模板语法
插值
vue
<template>
<div>
<!-- 文本插值 -->
<p>{{ message }}</p>
<p>{{ number + 1 }}</p>
<p>{{ ok ? 'YES' : 'NO' }}</p>
<p>{{ message.split('').reverse().join('') }}</p>
<!-- 原始 HTML -->
<span v-html="rawHtml"></span>
<!-- 属性绑定 -->
<img :src="imageSrc" :alt="altText">
<div :class="{ active: isActive }"></div>
<!-- 动态属性名 -->
<button :[key]="value">动态属性</button>
</div>
</template><template>
<div>
<!-- 文本插值 -->
<p>{{ message }}</p>
<p>{{ number + 1 }}</p>
<p>{{ ok ? 'YES' : 'NO' }}</p>
<p>{{ message.split('').reverse().join('') }}</p>
<!-- 原始 HTML -->
<span v-html="rawHtml"></span>
<!-- 属性绑定 -->
<img :src="imageSrc" :alt="altText">
<div :class="{ active: isActive }"></div>
<!-- 动态属性名 -->
<button :[key]="value">动态属性</button>
</div>
</template>表达式限制
每个绑定只能包含单个表达式,以下情况不支持:
js
// 错误:这不是表达式,是语句
{{ var a = 1 }}
// 错误:流程控制也不支持
{{ if (ok) { return message } }}// 错误:这不是表达式,是语句
{{ var a = 1 }}
// 错误:流程控制也不支持
{{ if (ok) { return message } }}data — 数据
组件中的 data 必须是函数
js
// 组件中
export default {
data() {
return {
count: 0,
user: { name: '张三', age: 18 }
}
}
}// 组件中
export default {
data() {
return {
count: 0,
user: { name: '张三', age: 18 }
}
}
}响应式原理
Vue2 通过 Object.defineProperty 将 data 的属性转为 getter/setter,因此:
- 对象:只有初始化时存在的属性才是响应式的
- 数组:通过索引直接修改元素或修改 length 不会触发更新
js
// 对象 — 需要用 $set 添加新属性
this.$set(this.obj, 'newKey', 'value')
// 或
this.obj = { ...this.obj, newKey: 'value' }
// 数组 — 需要用变异方法或 $set
this.$set(this.list, index, newValue)
this.list.splice(index, 1, newValue)// 对象 — 需要用 $set 添加新属性
this.$set(this.obj, 'newKey', 'value')
// 或
this.obj = { ...this.obj, newKey: 'value' }
// 数组 — 需要用变异方法或 $set
this.$set(this.list, index, newValue)
this.list.splice(index, 1, newValue)methods — 方法
vue
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">+1</button>
<button @click="add(10)">+10</button>
</div>
</template>
<script>
export default {
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++
},
add(num) {
this.count += num
}
}
}
</script><template>
<div>
<p>{{ count }}</p>
<button @click="increment">+1</button>
<button @click="add(10)">+10</button>
</div>
</template>
<script>
export default {
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++
},
add(num) {
this.count += num
}
}
}
</script>注意: methods 中的方法不要使用箭头函数,会导致 this 无法指向组件实例。
computed — 计算属性
计算属性基于依赖缓存,只有当依赖发生变化时才会重新计算。
vue
<template>
<div>
<p>原价:{{ price }}</p>
<p>折扣价:{{ discountPrice }}</p>
<p>反转文字:{{ reversedMessage }}</p>
</div>
</template>
<script>
export default {
data() {
return {
price: 100,
message: 'Hello'
}
},
computed: {
// 简写:只有 getter
discountPrice() {
return this.price * 0.8
},
reversedMessage() {
return this.message.split('').reverse().join('')
},
// 完整写法:包含 getter 和 setter
fullName: {
get() {
return this.firstName + ' ' + this.lastName
},
set(val) {
const names = val.split(' ')
this.firstName = names[0]
this.lastName = names[1]
}
}
}
}
</script><template>
<div>
<p>原价:{{ price }}</p>
<p>折扣价:{{ discountPrice }}</p>
<p>反转文字:{{ reversedMessage }}</p>
</div>
</template>
<script>
export default {
data() {
return {
price: 100,
message: 'Hello'
}
},
computed: {
// 简写:只有 getter
discountPrice() {
return this.price * 0.8
},
reversedMessage() {
return this.message.split('').reverse().join('')
},
// 完整写法:包含 getter 和 setter
fullName: {
get() {
return this.firstName + ' ' + this.lastName
},
set(val) {
const names = val.split(' ')
this.firstName = names[0]
this.lastName = names[1]
}
}
}
}
</script>computed vs methods
| 特性 | computed | methods |
|---|---|---|
| 缓存 | 基于依赖缓存 | 每次调用都执行 |
| 调用方式 | 作为属性使用 {{ reversedMsg }} | 作为方法调用 {{ reverseMsg() }} |
| 返回值 | 必须 return | 可选 |
| 异步 | 不支持 | 支持 |
watch — 侦听器
当数据变化时执行异步或开销较大的操作。
vue
<template>
<div>
<input v-model="question">
<input v-model="obj.deep.nested">
</div>
</template>
<script>
export default {
data() {
return {
question: '',
obj: { deep: { nested: '' } }
}
},
watch: {
// 基本用法
question(newVal, oldVal) {
console.log(`从 ${oldVal} 变为 ${newVal}`)
this.fetchAnswer()
},
// 监听对象内部属性(需要深监听)
obj: {
handler(newVal, oldVal) {
console.log('obj 变化了', newVal)
},
deep: true, // 深度监听
immediate: true // 初始化时立即执行一次
},
// 监听对象的具体属性
'obj.deep.nested'(newVal, oldVal) {
console.log('nested 变化了', newVal)
}
},
methods: {
fetchAnswer() {
setTimeout(() => {
console.log('请求答案...')
}, 1000)
}
}
}
</script><template>
<div>
<input v-model="question">
<input v-model="obj.deep.nested">
</div>
</template>
<script>
export default {
data() {
return {
question: '',
obj: { deep: { nested: '' } }
}
},
watch: {
// 基本用法
question(newVal, oldVal) {
console.log(`从 ${oldVal} 变为 ${newVal}`)
this.fetchAnswer()
},
// 监听对象内部属性(需要深监听)
obj: {
handler(newVal, oldVal) {
console.log('obj 变化了', newVal)
},
deep: true, // 深度监听
immediate: true // 初始化时立即执行一次
},
// 监听对象的具体属性
'obj.deep.nested'(newVal, oldVal) {
console.log('nested 变化了', newVal)
}
},
methods: {
fetchAnswer() {
setTimeout(() => {
console.log('请求答案...')
}, 1000)
}
}
}
</script>简写形式
js
watch: {
// 无其他配置项时可直接写函数
question(newVal, oldVal) {
console.log(newVal)
}
}
// 或者使用 vm.$watch
vm.$watch('question', (newVal, oldVal) => {
console.log(newVal)
}, { deep: true, immediate: true })watch: {
// 无其他配置项时可直接写函数
question(newVal, oldVal) {
console.log(newVal)
}
}
// 或者使用 vm.$watch
vm.$watch('question', (newVal, oldVal) => {
console.log(newVal)
}, { deep: true, immediate: true })computed 与 watch 的选择
- computed:依赖其他值计算出新值,有缓存
- watch:数据变化时执行异步操作或复杂逻辑
js
// 使用 computed(推荐)
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
}
// 使用 watch(适合异步)
watch: {
firstName(val) {
setTimeout(() => {
this.fullName = val + ' ' + this.lastName
}, 1000)
}
}// 使用 computed(推荐)
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
}
// 使用 watch(适合异步)
watch: {
firstName(val) {
setTimeout(() => {
this.fullName = val + ' ' + this.lastName
}, 1000)
}
}其他实例方法
$nextTick
数据变化后等待 DOM 更新完成再执行操作。
js
export default {
data() {
return { message: 'Hello' }
},
methods: {
updateAndLog() {
this.message = 'Updated'
// DOM 还未更新
console.log(document.querySelector('.msg').textContent) // 还是 'Hello'
this.$nextTick(() => {
// DOM 已更新
console.log(document.querySelector('.msg').textContent) // 'Updated'
})
}
}
}export default {
data() {
return { message: 'Hello' }
},
methods: {
updateAndLog() {
this.message = 'Updated'
// DOM 还未更新
console.log(document.querySelector('.msg').textContent) // 还是 'Hello'
this.$nextTick(() => {
// DOM 已更新
console.log(document.querySelector('.msg').textContent) // 'Updated'
})
}
}
}$forceUpdate
强制 Vue 实例重新渲染。需要手动强制更新的场景很少,通常说明数据设计有问题。
js
this.$forceUpdate()this.$forceUpdate()Vue.set / this.$set
向响应式对象添加新属性,确保新属性也是响应式的。
js
// 添加对象属性
this.$set(this.user, 'age', 18)
// 修改数组项
this.$set(this.list, 0, { name: 'new' })
// 全局方式
Vue.set(this.user, 'age', 18)// 添加对象属性
this.$set(this.user, 'age', 18)
// 修改数组项
this.$set(this.list, 0, { name: 'new' })
// 全局方式
Vue.set(this.user, 'age', 18)Vue.delete / this.$delete
删除对象的属性,触发视图更新。
js
this.$delete(this.user, 'age')this.$delete(this.user, 'age')
Cao Note