[TOC]
简介
Vue 是一个用于构建 Web 用户界面的渐进式 JavaScript 框架。其核心库只关注视图层(view layer),同时具备良好的第三方支持库生态用以应付构建复杂大型单页应用(SPA:Single-Page Application)。
MVVM 模型
Vue 虽然没有完全遵循 MVVM(Model-View-ViewModel)模型,当其设计也受到了 MVVM 的启发,以数据驱动界面,如下图所示:
在 Vue 中,充当 ViewModel 的是一个 Vue 实例(new Vue({})
),该 Vue实例 作用于某一个 HTML 元素上,全权代理该元素节点的所有操作。
Vue实例 内部通过 DOM Listeners 可以观测到页面上 DOM 元素的变化,从而将该种变化同步更改到 Model 中的对应数据。
同时通过 Data Bindings,当 Model 中的数据改变时,则会对相应视图上的显示进行更改,从而实现了 View 和 Model 的数据双向绑定。
注:传统的 Web 编程模型是 结构驱动,即要对一个 DOM 节点进行操作,第一步就是要获取该 DOM 节点对象,然后再修改数据更新到节点上。
而 Vue 的中心思想是 数据驱动,要更改界面,其实就是要更改数据。
简而言之,在 Vue 中,不应当考虑操作 DOM,而是专注于 操作数据。
安装
Vue 的安装有多种方法,这里主要介绍两种方法:
- 通过
<script>
标签直接引入:
<!-- 对于制作原型或学习,你可以这样使用最新版本 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 或者-->
<!-- 对于生产环境,我们推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
注:通过<script>
标签引入,Vue
会被注册为一个全局变量
- 直接使用官方提供的快速搭建复杂单页面应用 (SPA) 的脚手架 vue-cli:
- 首先全局安装该脚手架 vue-cli:
npm install -g @vue/cli
vue create <project-name>
注:使用 vue-cli 前需确保系统已安装 nodejs。
以上两步操作完成,我们便创建完成一个 Vue 项目。
在项目的 package.json
中,可以看到 vue-cli 提供了两个脚本命令让我们运行与打包项目:
-
npm run serve
:运行项目 -
npm run build
:打包项目到 dist 文件夹
组件化
Vue 的两大特性为 数据驱动 和 组件化。
通常一个大的页面可以划分为许多个小区块,这些小区块有些结构是相似的,我们可以将这些相似的区块抽象出一个统一的结构,方便复用,这种抽象结构的方法即称为组件化。
在实际项目开发中,一个大的页面通常都是由许多个小的组件构造而成的,如下图所示:
Vue 提供了两种组件定义的方式:
-
全局组件:全局组件只需定义一次,便可被其他任意组件使用。
定义方式:Vue.component(id, [definition])
<body>
<div id="app">
<my-component />
</div>
<script>
const myComponent = Vue.component('my-component',{
data(){
return {
'message': 'Hello Global Component'
}
},
template:`
<h1>{{message}}</h1>
`
});
const vm = new Vue({
el: "#app",
});
</script>
</body>
-
局部组件:对于全局组件来说,即使页面没有使用该组件,组件也会被注入到最终的构建结果中,导致了 JavaScript 文件的无谓增加。而局部组件可以做到按需加载,需要哪些组件,按需引入即可,更加灵活高效。
定义方式:通过一个普通的 JavaScript 对象来定义组件,然后 Vue实例 按需引入需要的组件即可。
<body>
<div id="app">
<component-a></component-a>
<component-b />
</div>
<script>
const componentA = {
template: `
<h1>Component A</h1>
`
};
const componentB = {
template: `
<h1>Component B</h1>
`
};
const vm = new Vue({
el: '#app',
components: { // 按需引入需要的组件
componentA,
componentB
}
});
</script>
</body>
最佳实践:在 Vue 中,组件通常都定义到一个单独的.vue
文件中,其他组件需要时,导入相应组件的.vue
文件即可。
// MyComponent.vue
<template>
<h1>{{message}}</h1>
</template>
<script>
export default {
name: "MyCompoent",
data() {
return {
message: "Hello MyComponent!"
};
}
};
</script>
<style scoped>
h1 {
background: red;
}
</style>
可以直接使用以下命令直接运行.vue
文件,查看组件展示效果:
vue serve MyComponent.vue --open
也可以在其他组件内导入该组件,进行使用:
<template>
<div id="app">
<h1>Parent Component</h1>
<!-- 使用组件 -->
<my-component />
</div>
</template>
<script>
// 导入组件
import MyComponent from "./MyComponent.vue";
export default {
name: "app",
components: {
MyComponent // 引入组件
}
};
</script>
注:在 Vue 中,组件实质是带有一个名字的 Vue实例,其性质与 Vue实例 基本一致(遵循 Vue实例 的生命周期等内容),特点是多了个组件复用功能。
Vue实例
Vue 实例充当 ViewModel 角色,负责 View 和 Model 之间的数据绑定:
new Vue(Options)
当创建一个 Vue 实例时,你可以传入一个选项对象Options
,该Options
的选项列表有如下可选:
下面列举一些Options
常用选项
<body>
<div id="app">
<h1>{{message}}</h1>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
message: "Hello Vue!"
}
});
</script>
</body>
注:Vue 将会递归将data
的属性转换为getter/setter
,从而让data
的属性能够响应数据变化。比如在控制台输入vm.message = 'Hi Vue!!!
,可以观察到页面数据发生了更改。
注:组件 中的data
属性必须是Fcuntion
类型,其返回一个Object
,原因是组件复用时,保证每个新组件都有独一的一份数据拷贝。
// MyComponent.vue
<template>
<h1>{{message}}</h1>
</template>
<script>
export default {
name: 'MyComponent',
data(){ // 函数类型
return { // 返回数据对象
'message': 'Hello Vue!'
}
}
}
</script>
-
选项 / 数据:
props
描述:该属性用于接收来自父组件的数据。
类型:Array<string> | Object
。
当传递的是Object
类型时,则可以基于对象的语法使用以下选项:
▫type
:指定数据类型,该值可以为原生类型(String
,Number
,Boolean
,Array
,Object
,Date
,Function
,Symbol
),自定义构造函数,或上述内容组成的数组。
▫default:any
:为该prop
指定一个默认值。如果该prop
没有被传入,则使用该默认值。对象或数组的默认值必须从一个工厂函数返回。
▫required: Boolean
:定义该prop
是否为必填项。
▫validator: Function
:自定义验证函数,对该prop
进行校验。
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
var Comp = Vue.extend({
props: ['msg'],
template: '<div>{{ msg }}</div>'
})
var vm = new Comp({
propsData: {
msg: 'hello'
}
})
注:propsData
属性只能用于new
创建的实例中。
var vm = new Vue({
data: { a: 1 },
methods: {
plus: function () {
this.a++
}
}
})
vm.plus()
vm.a // 2
注:methods
中的this
自动绑定到当前 Vue实例。
-
选项 / 数据:
computed
描述:计算属性,主要用于对data
数据进行计算转换。
类型:{ [key: string]: Function | { get: Function, set: Function } }
<template>
<div>
<h1>获取数据: {{computedData}}</h1>
<h1>设置数据:{{setData = 3}}</h1>
<h1>获取数据: {{setData}}</h1>
</div>
</template>
<script>
export default {
name: "Computed",
data() {
return {
count: 1
};
},
computed: {
// 只读取 data
computedData() {
return this.count + 10;
},
// 读取和设置 data
setData: {
get() {
return this.count + 1;
},
set(value) {
this.count = value;
}
}
}
};
</script>
注:computed
类型为Object
,其具有如下特点:
-
computed
内部定义的属性为访问器属性,即具备getter
和setter
,且其内部this
自动绑定到当前 Vue实例。 -
computed
会自动 缓存 计算结果,只有当依赖的响应式属性变化时,computed
才会重新进行计算。
缓存 是computed
与methods
的最大区别之处,methods
每次调用一定会运行函数,而computed
则不一定。
-
选项 / 数据:
watch
:
描述:侦听属性,用于监控data
或computed
的数据,当数据变更时进行回调通知。
类型:{ [key: string]: string | Function | Object | Array }
注:watch
类型为Object
,其内部属性的类型有多种:string | Function | Object | Array
,这里简单介绍 3 种:
-
string
:字符串表示回调函数名,当数据改变时,回调该函数:
const vm = new Vue({
el: '#app',
data: {
a: 1
},
methods: {
aChanged(value, oldValue) {
console.log(`a changed: new:${value} --> old:${oldValue}`);
}
},
watch: {
a: 'aChanged'
}
});
-
Function
:当数据改变时,直接回调该函数:
const vm = new Vue({
el: '#app',
data: {
a: 1
},
watch: {
a(value, oldValue) {
console.log(`a changed: new:${value} --> old:${oldValue}`);
}
}
});
-
Object
:对监控的属性为对象时,Vue 默认只能监控到对象重新被赋值的变化,而如果需要监听对象内部属性的变化,则可使用该选项,其中:
handler
代表回调函数。
deep
用来控制监听对象属性的层级,deep=true
时只要对象内部 property 改变(不管嵌套有多深),都会监听到。
immediate
用来设置是否立即产生回调。当immediate=true
时,回调函数会立即被调用,传递的是属性当前的值。
const vm = new Vue({
el: '#app',
data: {
a: {
aa: {
aaa: 3
}
}
},
watch: {
a: {
handler(value, oldValue) {
console.log(`a changed: new:${value} --> old:${oldValue}`);
},
deep: true // 被监听对象的 property 改变时被调用,无论嵌套的有多深
}
}
});
注:大多数情况下,观察和响应数据变更使用计算属性(computed
)便足够了,但是当在数据变化时需要执行异步或开销较大的操作时,则此时使用侦听属性(watch
)会更加适合。
new Vue({
el: '#app'
});
注:如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显式调用vm.$mount()
手动开启编译。
<div id="app"></div>
const vm = new Vue({
el: '#app',
template: '<h1>template</h1>' // <div> 会被 <h1> 完全覆盖
})
-
选项 / DOM:
render
描述:字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个createElement
方法作为第一个参数用来创建VNode
。
类型:(createElement: () => VNode) => VNode
<div id="app"></div>
new Vue({
render(createElement) {
return createElement('div', {
class: 'rendered'
},
[
createElement('h1', {
domProps: {
innerHTML: 'div>h1 rendered by vue'
}
})
]
);
}}).$mount('#app');
注:Vue 推荐在绝大多数情况下使用模板来创建你的 HTML,只有在一些特殊场景下,比如模板冗长且具备重复元素,则此时使用渲染函数render
通过编写 JavaScript 代码来渲染出页面会更加方便简洁。
- 选项 / 生命周期钩子:见下文
生命周期
每个 Vue实例 在挂载到页面时,都会经历一系列的初始化过程,例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。在创建 Vue实例 的这整个过程中,Vue 为我们预留出了一些 Hook 点,方便我们在 Vue实例 创建过程的某个生命周期中进行一些操作。如下图所示:
注:图片来源于网上,侵删。
这些预留的生命周期钩子函数总共有如下几个:
beforeCreate
:Vue实例 初始化之后,此时data
和methods
中的数据还未进行初始化,因此无法获取。created
:表示 Vue实例 创建完成,但还未挂载到页面上,此时data
和methods
都已经初始化成功,可以对其进行调用获取,而挂载阶段未开始,所以$el
属性目前不可见。beforeMount
:在挂载开始之前被调用,此时模板已在内存中被编译完成,只是尚未挂载到页面上,因此,此时页面上显示的还是未渲染的结构。mounted
:挂载完成,此时页面会显示我们渲染的视图。如果想要操作页面上的 DOM 节点,最早的时间就是该处。
注:mounted
不会 承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用vm.$nextTick
替换掉mounted
:
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
beforeUpdate
:数据更新时调用,此时内存中data
数据已更新,但页面中显示的数据还未更新,数据与页面不同步。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。updated
:新数据成功渲染到页面,此时数据与页面处于同步状态。
注:updated
不会 承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用vm.$nextTick
替换掉mounted
:activated
:激活状态,表示当前组件处于前台页面,用户可与该组件进行交互。
注:只有组件被内置组件keep-alive
包裹时,该钩子才有可能被调用。
<template>
<keep-alive>
<my-component />
</keep-alive>
</template>
deactivated
:停用状态,表示当前组件处于后台页面,用户不能与之交互。
注:只有组件被内置组件keep-alive
包裹时,该钩子才有可能被调用。beforeDestroy
:实例销毁之前调用。在这一步,实例仍然完全可用,即此时实例的data
,methods
等所有数据完全可用。destroyed
:Vue 实例销毁后调用。此时 Vue实例 指向的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。errorCaptured
:当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回false
以阻止该错误继续向上传播。
指令
指令 (Directives) 是带有v-
前缀的特殊属性。
Vue 提供了以下内置的指令:
-
v-text
:更新元素的textContent
,该指令与使用{{ Mustache }}
插值效果一样。
类型:string
<span v-text="msg"></span>
<!-- 和下面的一样 -->
<span>{{msg}}</span>
-
v-html
:更新元素的innerHTML
。
类型:string
<template>
<div id="app">
<div v-html="html"></div>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
html: '<a href="https://www.baidu.com" >baidu</a>'
};
},
};
</script>
注:v-html
的内容只会按普通 HTML 插入,不会作为 Vue 模板进行编译。
注:在单文件组件里,scoped
的样式不会应用在 v-html
内部,因为那部分 HTML 没有被 Vue 的模板编译器处理。
注:在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只在可信内容上使用v-html
,永不用在用户提交的内容上。
-
v-show
:条件渲染,根据表达式之真假值,切换元素的display
CSS 属性。
类型:any
<h1 v-show="ok">Hello!</h1>
注:v-show
不支持<template>
元素,也不支持v-else
。
-
v-if
:条件渲染,根据表达式的值的真假条件渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。如果元素是<template>
,将提出它的内容作为条件块。
类型:any
<h1 v-if="awesome">Vue is awesome!</h1>
注:当和v-if
一起使用时,v-for
的优先级比v-if
更高。
注:v-show = false
时只是把元素设置为:display:none
,元素还留着 DOM 树上。
而v-if = false
时,元素会被整个移除,其上绑定的数据/组件都会被销毁。
-
v-else
:v-if
或v-if-else
的分支。
类型:无
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
-
v-else-if
:v-if
的分支。
类型:any
<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-for
:遍历源数据,渲染元素列表。
类型:Array | Object | number | string | Iterable
<div v-for="item in items">
{{ item.text }}
</div>
<!-- 另外也可以为数组索引指定别名 (或者用于对象的键):-->
<div v-for="(item, index) in items"></div>
<div v-for="(val, key) in object"></div>
<div v-for="(val, name, index) in object"></div>
注:v-for
渲染元素时,默认使用“就地更新”策略,即当列表数据改变时,Vue 不会移动当前 DOM 元素来重新匹配数据项,而是根据索引位置重新渲染数据。比如:
现在我们有数据项:
data() {
return {
datas: [
{ id: 1, name: "one" },
{ id: 2, name: "two" },
{ id: 3, name: "three" }
]
}
将这些数据项渲染到页面上:
<template>
<div>
<ul>
<li v-for="(item,index) in datas">
<input type="checkbox" />
{{item.name}}
</li>
</ul>
</template>
我们使用v-for
将每条数据项渲染到一个<li>
上,此时显示效果如下:
如果此时我们勾选第一个<li>
的checkbox
,即one
勾选上,然后再往数据列表前面添加一个数据:this.datas.unshift({ id: 4, name: "four" })
,则可以看到显示效果如下:
可以看到,我们想要的是one
被勾选了,但是效果是数据列表首位被勾选。出现这种现象的原因就是v-for
默认采用的“就地更新”策略:它会复用已渲染完成的 DOM 元素,然后只对变化的数据进行修改,比如这里复用了第一条<li><checkbox>one</li>
,添加数据项,对第一条<li>
来说,他的数据改变了,但是<checkbox>
不包含在数据项里,因此只会修改数据,将one
修改为four
,而checkbox
仍保持勾选状态。
因此,“就地更新”策略是高效的,但是 只适用于不依赖子组件状态或临时 DOM 状态(例如:表单输入值) 的列表渲染输出。
而要解决上述问题,只需为v-for
提供一个key
属性(key
必须是唯一的),这样 Vue 就可以识别出数据项对应的渲染条目,从而重用和重新排序现有元素:
<template>
<div>
<ul>
<li v-for="(item,index) in datas" :key="item.id">
<input type="checkbox" />
{{item.name}}
</li>
</ul>
</template>
由于新添加的数据id=4
,当前已存在的<li>
没有与之对应的标识key
,因此 Vue 会重新渲染一个新的<li>
,并将其与id=4
对应起来,结果如下图所示:
注:“就地更新”策略其实就是使用索引作为节点标识,即:key=index
。
-
v-on
:绑定事件监听器。
缩写:@
类型:Function | Inline Statement | Object
参数:event
修饰符:
▫.stop
:调用event.stopPropagation()
,停止事件分发。
▫.prevent
:调用event.preventDefault()
,取消事件的默认动作。
▫.capture
:添加事件侦听器时使用capture
(捕获)模式。
▫.self
:只有当事件是从侦听器绑定的元素本身触发时才触发回调。
▫.{keyCode | keyAlias}
:只有当事件是从特定键触发时才触发回调。
▫.native
:监听组件根元素的原生事件。
▫.once
:只触发一次回调。
▫.left
: 只有当点击鼠标左键时触发。
▫.right
: 只有当点击鼠标右键时触发。
▫.middle
: 只有当点击鼠标中键时触发。
▫.passive
:以{ passive: true }
模式添加侦听器
<!-- 方法处理器 -->
<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-on
用在普通元素上时,只能监听 原生 DOM 事件。用在自定义组件上时,也可以监听子组件触发的自定义事件。
<my-component @my-event="handleThis"></my-component>
<!-- 内联语句 -->
<my-component @my-event="handleThis(123, $event)"></my-component>
<!-- 组件中的原生事件 -->
<my-component @click.native="onClick"></my-component>
-
v-bind
:动态绑定
缩写::
类型:any (with argument) | Object (without argument)
参数:attrOrProp (optional)
修饰符:
▫.prop
:被用于绑定 DOM 属性 (property)
▫.camel
:将 kebab-case 特性名转换为 camelCase(驼峰式)
▫.sync
:会扩展成一个更新父组件绑定值的 v-on 侦听器
<!-- 绑定一个属性 -->
<img :src="imageSrc">
<!-- 动态特性名 (2.6.0+) -->
<button :[key]="value"></button>
<!-- 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>
<!-- 绑定一个有属性的对象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
<!-- 通过 prop 修饰符绑定 DOM 属性 -->
<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>
<!-- 支持绑定驼峰命名属性 -->
<svg :view-box.camel="viewBox"></svg>
-
v-model
:表单控件与数据属性的双向绑定。
修饰符:
▫.lazy
:使用<input>
的change
事件进行同步。
▫.number
:自动将字符串转为数字。
▫.trim
:输入首尾空格过滤。
<input type="text" v-model="message" />
<script>
export default {
name: 'VModel',
data() {
return {
message: ''
};
}
};
</script>
-
v-slot
:插槽。
缩写:#
当我们定义组件的时候,有些内容可能需要由父组件传入,因此,此时可以使用插槽,预留出位置给到父组件进行自定义内容传入:
// 子组件:预留插槽
<template>
<div>
<h1>Son Component</h1>
<!-- 预留插槽 -->
<slot></slot>
</div>
</template>
// 父组件:传入插槽内容
<template>
<div>
<h1>Parent Component</h1>
<son-component>
<h2>slot: content from Parent Component</h2>
</son-component>
</div>
</template>
▫ 后备内容:可以通过为<slot>
内部提供默认内容,只有当父组件显示传入内容时,才会覆盖默认内容:
<slot>
<h1>Default Content</h1>
</slot>
▫ 具名插槽:我们可以给插槽进行命名(使用name
属性),这样父组件就可指定名字(使用v-slot
指令)对特定的插槽进行覆盖:
// 子组件模板
<template>
<div>
<h1>Son Component</h1>
<!-- 预留命名插槽 -->
<slot name="header"></slot>
<main>
<!-- name="default" -->
<slot></slot>
</main>
<slot name="footer"></slot>
</div>
</template>
// 父组件
<template>
<div>
<h1>Parent Component</h1>
<son-component>
<template v-slot:header>
<h2>替换 header 插槽</h2>
</template>
<h3>替换默认插槽</h3>
<template #footer>
<h2>替换 footer 插槽</h2>
</template>
</son-component>
</div>
</template>
注:v-slot
只能添加在一个<template>
或 组件 上。
注:默认插槽其实也是一个具名插槽,其名称为:default
。
▫ 插槽 prop:使用 插槽 prop 可以传递子组件的数据给到父组件,使父组件可以在覆盖插槽的内容上使用子组件的数据:
// 子组件
<slot :msg="message"></slot>
<script>
export default {
name: 'SonComponent',
data() {
return {
message: 'Hello from Son Component!'
};
}
};
</script>
// 父组件:slotProps 接收子组件的 插槽props
<son-component #default="slotProps">
{{slotProps.msg}}
</son-component>
-
v-pre
:跳过该元素及其子元素的编译过程。可以用来显示原始 Mustache 标签。
类型:无
<span v-pre>{{ this will not be compiled }}</span>
-
v-cloak
:这个指令保持在元素上直到关联实例结束编译。通常结合 CSS 规则来达到隐藏未编译的 Mustache 标签直到实例准备完毕。
类型:无
[v-cloak] {
display: none;
}
<div v-cloak>
{{ message }}
</div>
-
v-once
:只渲染元素和组件 一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
<span v-once>This will never change: {{msg}}</span>
- 自定义指令:在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
Vue 提供了两种自定义指令的方式:
-
全局指令:使用
Vue.directive
:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {...})
-
局部指令:组件中定义一个
directives
属性:
// 注册一个局部自定义指令 `v-focus`
directives: {
focus: {...}
}
-
钩子函数:一个指令定义对象可以提供如下几个钩子函数 (均为可选):
▫bind
:指令第一次绑定到元素时调用。改钩子只会被调用一次,可在此做一些初始化设置。
▫inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
▫update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。
▫componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
▫unbind
:指令与元素解绑时调用。改钩子只会被调用一次,可在此做一些资源释放操作。
示例:使用自定义指令v-customtext
模拟v-text
:
<template>
<h1 v-customtext="msg"></h1>
</template>
<script>
export default {
name: 'customeDirective',
data() {
return {
msg: 'Hello Custom Directives!'
};
},
directives: {
customtext: {
inserted(el, binding, vnode, oldVnode) {
el.innerText = binding.value;
}
}
}
};
</script>
其他
- 组件间通信:
▫ 父传子:子组件通过props
属性可接收父组件传递过来的变量:
// ParentComponent.vue
<template>
<son-component :msg="message" />
</template>
<script>
import SonComponent from './SonComponent';
export default {
name: 'ParentComponent',
components: {
SonComponent
},
data() {
return {
message: 'data from Parent Component'
};
}
};
</script>
// SonComponent.vue
<template>
<h1>{{msg}}</h1>
</template>
<script>
export default {
name: 'SonComponent',
props: {
msg: {
type: String,
required: true
}
}
};
</script>
▫ 子传父:子组件可以通过$emit
发送自定义事件向父组件传值,父组件直接注册接收该事件即可:
// SonComponent.vue
<template>
<button @click="sendEvent">点击发送事件</button>
</template>
<script>
export default {
name: 'SonComponent',
methods: {
sendEvent() {
// 发送自定义事件
this.$emit('eventFromChild', 'data from Son Component!!');
}
}
};
</script>
// ParentComponent.vue
<template>
<div>
<!-- 接收事件 -->
<son-component @eventFromChild="recvChildEvent" />
<h1>{{data}}</h1>
</div>
</template>
<script>
import SonComponent from './SonComponent';
export default {
name: 'ParentComponent',
components: {
SonComponent
},
data() {
return {
data: 'hhhh'
};
},
methods: {
recvChildEvent(data) {
this.data = data;
}
}
};
</script>
▫ 父传子孙:父组件/祖先组件通过provide
提供变量,子孙组件通过inject
来接收该变量:
// ParentComponent
import SonComponent from './SonComponent.vue'
export default {
name: 'ParentComponent',
components: {
SonComponent
},
provide: {
message: 'data from Parent Component'
}
}
// SonComponent
<template>
<h1>{{message}}</h1>
</template>>
<script>
export default {
name: 'SonComponent',
inject: ['message']
}
</script>>
更多组件间通信方式,请参考:Vue组件间通信6种方式