1.引入vue.js文件包
2.body中,设置Vue管理渲染的容器
3.实例化Vue对象 new Vue();
4.设置Vue实例的选项:如el、data...
new Vue({选项:值});
5.在中通过{{ }}使用data中的数据
注:watch和生命周期钩子函数不能用箭头函数,箭头函数没有this,函数中使用this会报错
Object.freeze()
阻止修改现有的属性,也意味着响应系统无法再追踪变化每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等
注 不要在选项property或回调上使用箭头函数,因为箭头函数的this是和父级上下文绑定到一起的
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循规范的浏览器和 HTML 解析器解析。在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。如果你熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量,你也可以不用模板,直接写渲染 (render) 函数,使用可选的 JSX 语法。
指令 (Directives) 是带有 v- 前缀的html标签的特殊属性。指令属性值应该是一个单独的javascript表达式(V-for除外)。指令的工作是在其表达式的值发生变化时,将其产生的连带影响,响应式地作用于 DOM
使用“{{}}”表示,插值表达式可以用来绑定对象中的数据等
{{msg}}
var vm = new Vue({
el: "#app",
data: {
msg: "hello"
}
})
注 插值表达式绑定数据,会根据data中的数据变化而实时更新。如果想实现一次性更新可以给添加插值表达式的节点添加“v-once”指令,插入数据就不会在更新,但是该指令也会影响到该节点其他数据的绑定
v-text用于绑定普通文本、对象中的data数据
v-html用于绑定文本、数据或解析html代码
注:最好不要使用v-html/v-text
2.6.0及以上版本,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数
...
...
...
...
<标签 v-bind:属性名称="数据信息(变量/常量)">
// 或
<标签 :属性名称="xxx">
data:{active:"active"}
// 或
// 属性值需要通过引号圈选
+ 在一个数组元素中可以绑定多个或一个样式成员
+ 连字符“-”连接的属性可以通过“font-size”或 fontSize解决
+ v-bind绑定类型可以绑定数组或对象,通过data中的变量来控制标签的类名和样式,
通过条件判断标签的class和style不同情况加载不同数据`x
`
**注**:绑定class和style可以通过绑定一个**返回对象的计算属性**来实现
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
v-on事件绑定
事件类型:
鼠标事件:onclick ondblclick onmouseenter onmouseleave onmouseover onmousedown等等
键盘事件:onkeyup onkeydown onkeypress 等等
在vuejs中事件名称前都不加on,onclick-》改为click
v-on绑定的所有方法都在method中定义
再绑定事件中如果想访问原生DOM的事件,可以通过$event事件对象来访问
<标签 v-on:click="事件处理函数名">标签>
<标签 @click="事件处理函数名">标签>
事件传参
当绑定事件方法中有形参时:
1.如果绑定方法时传入实参,方法中形参就是实参
2.如果绑定方法时,方法带有(),形参为undefined
3.如果绑定方法时,未带有(),形参为事件对象
事件对象可以通过$event
的方式传入方法
1.
,形参为事件对象
2.
,形参为undefined
3.
,形参为传入实参
- 在事件处理函数中通过this来调用vm对象中的data数据和method中的方法
**注:**v-bind和v-on都分别可以缩写为: “:” 和“@”
事件修饰符
以“.”开头的指令后缀,用来替代event.preventDefault()等事件对象的方法
包括“.stop.prevent.capture.self.once.passvie”
- .stop 阻止事件冒泡传播
- .prevent 阻止提交事件的默认行为
- .capture 添加事件监听时启用事件捕获模式(即元素自身触发的事件先在此处理,然后才交由内部元素进行处理)
- .self 只有是当前元素自身触发事件时,事件才会触发(即事件不是从内部元素冒泡上来的)
- 新增
- .once 事件将只会触发一次
不像其它只能对原生的 DOM 事件起作用的修饰符,.once 修饰符还能被用到自定义的组件事件上。
- .passive 事件触发时的默认行为将会被立即触发,而不是等待事件完成
注:
- .passive能够提升移动端的性能
- .passive会告诉浏览器不想阻止事件的默认行为
- .passive不能和.prevent一起使用,否则.prevent将会被忽略
<a v-on:click.stop="doThis">a>
<form v-on:submit.prevent="onSubmit">form>
<a v-on:click.stop.prevent="doThat">a>
<form v-on:submit.prevent>form>
<div v-on:click.capture="doThis">...div>
<div v-on:click.self="doThat">...div>
<--新增-->
<a v-on:click.once="doThis">a>
<div v-on:scroll.passive="onScroll">...div>
**注**:
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。
因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
按键修饰符
作用:监听键盘事件时,替代判断按键的按键码
注:event.keyCode已被MND废弃。
现在使用event.key代替keyCode,key对应按键上的字符,keyCode对应按键的按键码。
现在新的浏览器已经不在支持keyCode,但旧的浏览器还支持keyCode
可以将KeyboardEvent.key暴露的任意有效键名转换为kebab-base(带-以小写开头格式)
<--使用keyCode-->
vue提供常见按键别名:
- .enter、.tab、.delete (捕获“删除”和“退格”键)、.esc、.space、.up、.down、.lef、t.right
**另:**可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
系统修饰键
控制只有按下相应按键时才会触发对应鼠标或键盘的事件监听,包括.ctrl、.alt、.shift、.meta
注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。
在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。
在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。
在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。
在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。
Do something
请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。
换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。
如果你想要这样的行为,请为 ctrl 换用 keyCode:keyup.17。
- 新增
- .exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
- 鼠标按钮修饰符(新增)
- 包括:.left.、right、.middle
这些修饰符会限制处理函数仅响应特定的鼠标按钮。
条件渲染
都是通过指令根据接收的boolean值将页面元素进行显示或隐藏,可以把一个 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 元素。
- v-if/v-else-if/v-else
- 使用 v-else 指令来表示 v-if 的“else 块”
- v-else-if 充当 v-if 的“else-if 块”,可以连续使用
- v-else / v-else-if 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面
- 利用这三个指令可以实现多分支结构,在不同情况下显示或隐藏
- 可以给不同模块添加key值,以保证不同模块相互独立
<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>
- 用key管理可服用的元素
- v-show
区别:v-if进行的是元素的删除或添加,v-show只进行元素css样式的现实或隐藏,
如果是频繁的切换使用v-show更合适
v-for列表渲染
对数组或对象进行遍历
**注:**遍历的同时会重复创建被遍历的html标签
<标签 v-for="(遍历出来的小单元, 小单元下标) in 目标">
<标签 v-for="遍历出来的小单元 in 目标">
- key值
v-for使用的同时必须使用“:key”,以便vue能跟踪每个节点的身份
好处:从而重用和重新排序现有元素,需要为每项提供一个唯一 key 属性值 并且是唯一的,可以代表每个节点的有相同父元素的子元素必须有独特的 key, 值不要重复,否则会渲染错误
<标签 v-for="(item, index) in 目标" :key="xxx">
<标签 v-for="(value,key,index) in 目标" :key="xxx">
注 可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法
注 在遍历对象时,会按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。
数组的更新检测
Vue 将被侦听的数组的变异方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法
+ 变异方法 (mutation method)
包括push()、pop()、shift()、unshift()、splice()、sort()、reverse()
+ 一些数组方法vue未包裹,为非变异方法filter()、concat()、slice()。非变异方法不会改变原始数组而是返回一个新的数组,可以使用新数组替换旧数组,vue中数据也会进行相应更新,使用新数组替换原来的数组
注:
1. 利用索引和长度对数据的更改,vue不会检测到
vm.items[indexOfItem] = newValue 和 vm.items.length = newLength对数组修改都不会生效
2. 替换方法
Vue.set(vm.items, indexOfItem, newValue)
vm.items.splice(indexOfItem, 1, newValue)
vm.$set(vm.items, indexOfItem, newValue)
vm.items.splice(newLength)
对象的更新检测
注意事项:
- Vue 不能检测对象属性的添加或删除
obj.key可访问到,obj.key = value 不会响应式更新。
- 对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。
- 动态添加属性可以通过
Vue.set(object, propertyName, value)
或vm.$set(vm.userProfile, 'age', 27)
方法向嵌套对象添加响应式属性
- 向原对象添加多个属性,使用Object.assign()或_.extend(),应该按如下方法添加:
vm.userProfile = Object.assign({}, vm.userProfile, { age: 27, favoriteColor: 'Vue Green' })
- 也可以使用带有v-if或v-for的template渲染一段带有多个元素的内容
避免v-if和v-for一起使用
- v-for优先级高于v-if
- 如果为了有条件的遍历渲染内容,可以新建一个计算属性,将数据过滤筛选再遍历
- 如果想根据不同情况决定是否遍历渲染,可以再遍历元素之外父元素上添加v-if
v-model双向绑定
v-model本质是语法糖
+ 通过v-model可以实现双向数据绑定,vue可以将数据渲染到页面上,当在页面上对数据进行更改时,更改的数据也会被感知到,并更新数据,重新渲染页面
注
1.v-model 只能 和 表单元素 配合使用(针对value属性起作用),具体为 input、select、textarea等
2.v-model只能绑定vue的data成员,不能 绑定各种运算符的表达式 和 常量(例如 v-model="age+5"是错误的)
<标签 v-model="xxx">
- 修饰符
- .lazy 可以把input事件触发输入框的值与数据进行更新,变为使用onchange事件进行事件更新
- .number 自动将用户的输入值转为数值类型
- .trim 能够过滤掉用户输入的首尾空白字符
- 在组件上使用 v-model
Vue 的组件系统允许创建具有完全自定义行为且可复用的输入组件。
这些输入组件甚至可以和v-model 一起使用
计算属性和监听器
计算属性
- 使用场景
用于处理数据的反复使用的、复杂的js逻辑
用于处理复杂数据的js复杂逻辑操作
- 使用:
1.在vm实例中添加computed属性
2.在computed的属性中添加对相应data中数据的处理,并把处理结果返回
3.在试图层绑定computed中的属性
计算属性成员方法用到的data成员数据发生变化了,那么计算属性也会重新计算得出新值。即num1或num2的值发生变化后,result的值会自动更新
Original message: "{{ message }}"
Computed reversed message: "{{ reversedMessage }}"
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
computed中的计算方法依赖于data中的数据,data中数据更新,依赖于computed属性的相应数据也会更新。
+ 计算属性的缓存
将对数据的处理定义为一个方法,在相应位置调用该方法,也能达到和计算属性一样的效果。
区别:
1.计算属性是基于它们的依赖关系进行缓存的。
只有相应关系更改时,才会重新求值,进行相应更新。也就是说,只要计算属性依赖的属性不更新,计算属性的方法就不必重新执行,而是立即返回上一次执行的结果。
2.如果是方法调用,每一次触发渲染,方法都要重新调用
3.方法调用不会有缓存,如果不希望有缓存,可以使用方法调用
4.如果有非常复杂的js逻辑处理,用计算属性可以提高性能,避免多次调用方法(将避免多次执行属性的getter)
+ 计算属性默认只有getter,但需要时也可以提价setter
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
如果只有getter,computed中的属性,计算属性所依赖的data属性不会变化,设置setter后,computed中属性更新,computed中属性所依赖的data数据也会做相应的更新
监听器
应用场景:用于监听某个数据的变化后,执行某些操作
作用:通过watch选项提供了一个更通用的方法,来响应数据的变化
export default {
data(){
return {
xxx:'',
yyy:''
}
},
watch:{
// 成员名称: 方法
xxx:function(newval,oldval){},
yyy:function(newval,oldval){}
}
}
watch成员名称 与 被监听的 data成员名称 是一致的,表示明确监听指定成员,
data有两个成员xxx/yyy,它们都受到watch监听,更新后会立即触发执行watch对应的成员方法newval:更新后的值
oldval:更新前的值
密码:<input type="password" v-model="userpwd" ref="pwd" />
export default {
data(){
return {
userpwd:''
}
},
watch:{
userpwd:function(val){
if(val.length>=8){
this.$refs.pwd.style.color = 'blue'
}else{
this.$refs.pwd.style.color = 'red'
}
}
}
}
除了 watch 选项之外,还可以使用命令式的 vm.$watch的API
监听属性和计算属性的区别
- 计算属性成员方法用到的data成员数据发生变化了,那么计算属性也会重新计算得出新值。
- 计算属性是同时监听一个或多个data数据的变化,对data进行处理,得到一个新值
3.watch成员名称与被监听的data成员名称是一致的,表示明确监听指定成员
4.watch监听属性,对指定data成员进行监听,当数据发生变化会触发watch执行,侧重于在数据变化后做某些事情
特点:watch:在数据变化时执行异步操作或开销比较大的操作,只有watch才可以实现
computed:当一个数据是随着data中一个或多个数据变化而变化,使用computed比watch要方便和简洁,并且可以防止watch的滥用
过滤器
作用:用于对数据信息进行二次处理,本质上就是函数
- 过滤器只可以用在两个地方:插值表达式和 :冒号 属性绑定表达式。
- 过滤器应该被添加在 JavaScript 表达式的尾部,由“|竖线”连接;
全局过滤器
给Vue通过filter方法声明的过滤器是全局的,该Vue的所有实例都可以使用
Vue.filter("过滤器名称",function(origin){return origin处理结果}origin表示被处理的原始数据)
// 使用
{{dt|过滤器名称}}
私有过滤器
在Vue实例中,使用filters选项来添加当前对象的私有过滤器
var vm = new Vue({
el: '#app',
data: {},
filters: {
// 如下是方法成员,是最新的ES6写法【推荐】
过滤器的名称(origin){
return 处理的结果
}
}
})
全局 和 私有 过滤器如果同时存在,并且名称还相同, 则按照 就近原则 调用,级私有的优先
全局过滤器和私有过滤器都可以使用,但如果不能直接创建Vue对象只能创建私有过滤器
数据的处理可以使用多个过滤器
{{ dt | 过滤器 | 过滤器 | 过滤器 ... }}
传参
作用:通过传参,将结果形式根据业务进行调整
{{ dt | 过滤器的名称(实参,实参...) }}
Vue.filter('过滤器的名称', function(origin,形参,形参...){
})
组件
- 好处
- 重复利用
- 提高开发效率
- 更专注于逻辑层面
组件注册
Vue.compnent('组件名称',{组价配置成员})
+ 在组件的配置对象中,使用 `template` 属性指定当前组件要渲染的模板结构
+ template必须有且只能有一个根标签
+ template属性值是`多行`的,可以通过`反单引号`圈选编辑
组件使用
- 定义组件
Vue.component("button-counter",{
data: function () {
return {
count: 0
}
},
template:`
`
})
2. 使用组件
<--在vue对象挂载的模板中使用组件-->
new Vue({ el: '#components-demo' })
组件配置成员
- 组件是特殊的vue实例,与vue实例对象有相同的选项成员(el除外),data、method、computed、watch等以及生命钩子函数
- 组件的data选项是一个函数,必须通过return返回一个字面量对象
(因为只有这样每个复用的组件的实例都只维护自己当前的data)
- 组件vue实例对象必须导出
私有组件
全局组件注册之后可以使用在任何vue实例中,而有些组件只需要在某个对象中,或只需要使用一次,所以需要将组件注册给特定的vue对象
+ 在vue对象中添加一个components
属性,在components中注册需要使用的组件
+ 私有组件只能使用在当前vue对象中
components:{
'组件的名称': { 组件的配置对象成员 },
'组件的名称': { 组件的配置对象成员 }...
}
<--注册-->
new Vue({
components:{
'my-login': {
template: '会员登录系统'
}
}
})
<--使用-->
<div id="app"><my-login></my-login></div>
组件使用v-model
原理: 本质上,v-model是v-bind以及v-on配合使用的语法糖
应用场景:一些表单控件value使用来另做他用的,和input不同,radio和checked用vaue来绑定选项的值,checked控制状态更改
// 就是相当于:
自定义组件model选项
- 不设置model选项:默认prop = value, event = input
- 设置model prop设置绑定属性 event设置绑定事件
条件
为了让它正常工作,这个组件内的 必须:
- 将其更改状态的属性绑定到prop上
- 在其自定义事件类型被触发时,将新的值通过自定义的事件抛出
作用
- 简化了单向数据流的操作,但提高了维护成本
- 偏向于在表单自定义组建中使用该方法
// 自定义组件使用v-model
// 自定义组件中设置model选项
model: {
prop: "text",
event: "onEmitFromChild"
}
// 调用绑定事件
this.$emit("onEmitFormChild","124")
自定义事件
- 事件名称不存在自动大小写转换,触发的事件名称需要完全匹配
- 事件名不会被用足js变量名或property名,不能使用首字母大写和驼峰命名法
- v-on会在DOM模板中自动转换为全小写,导致事件无法监听
v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。
- 使用 kebab-case(短横线分隔命名)命名
Vue单文件组件
定义:以.vue结尾,把一个组件的全部内容汇总到一个文件中
+ .vue文件分为三部分
+ template 结构
template用于设定组件要显示的内容,内部必须有一个根节点,可以理解为View部分
template是H5新标签,,只执行不在浏览器显示,要求有
+ script 行为
script导出一个模块,表现上是一个VueComponent组件,可以理解为VM部分,内部所有的成员与Vue实例基本一致
+ style 样式
设置组件结构的样式
vue单文件组件的使用
- 定义组件
<template>
<div>
<p>{{ msg }}</p>
</div>
</template>
<script>
导出
export default {
data(){
return {msg}
},
methods:{},
components:{},
filters:{},
created(){}
}
</script>
<style lang="less">
p {color:red;}
</style>
在相应位置导入组件并注册使用
导入并注册
import XXX from './components/xxx.vue'
// Vue.component(名称, 组件)
Vue.component('com-xxx',XXX)
或
import XXX from './components/xxx.vue'
var vm = new Vue({
components:{
// 名称: 组件
'xxx': XXX
}
})
使用
<div id="app">
<com-xxx></com-xxx>
</div>
+ scoped 给style添加scoped,保证只在当前组件起作用
+ scoped只能对平级组件进行区分
+ 嵌套组件最好不要使用选择器进行样式设定,scoped不能区分
组件嵌套
嵌套:将一个组件导入并注册为另一个组件的私有组件,并在父组件中使用子组件
组件通信
父子传值
父传子
步骤:
+ 在父组件结构中使用子组件的位置,通过自定义属性
或属性绑定data中数据
,进行传值
+ 在子组件中通过props接收父组件传递过来的数据
+ 在子组件结构中引用传递过来的数据
+ 命名:DOM中自定义属性应该使用短横线分隔命名(post-title)props接收应该使用驼峰命名(‘postTitle’)
html对大小写并不敏感,浏览器会把所有大写字符解析为小写字符
字符串模板不存在这个限制
传值
export default {
data(){
return { myhouce: '2件茅草屋', mymoney:'5万外债' }
}
// 子组件在script中,接收并使用
// 在子组件结构中使用
这是子组件 --- {{house}} --- {{money}}
注:
+ prop 父传子是单向下行绑定,父组件prop更新会流向子组件,传递过去的数据会相应更新,
+ 应该避免子组件中直接更改从父级prop传递过来的数据
+ 如果想在把传入数据作为一个本地叔叔使用最好定义一个变量接收传入数据
+ 如果传入数据需要进行处理在引用,可以定义一个计算属性
子传父
- 在父组件中定义一个事件,同过事件绑定给子组件定义一个事件方法,向子组件传递一个事件方法
- 在子组件中通过$emit()调用父组件传递过来的事件方法
- 在父组件中定义事件的形参
- 在子组件中调用事件方法时,传入实参
// 2. 向子组件声明一个事件方法,事件的名称可以自定义
<com-six-son @back="receive"></com-six-son>
// back是事件名称
// receive是事件的处理函数,定义在当前组件中
// 1. 在父组件中定义一个事件
methods:{
// 接收"子级给父级"传递回来的数据的方法
receive(k1,k2,k3){
console.log(`${k1}-${k2}-${k3}`)
}
}
4.使用传递过来的事件方法
<button @click="huibao">按钮</button>
3. 在子组件中在定义的方法中,通过$emit()调用事件方法并传参,调用的是自定义的名字,不是父组件定义处理函数的名字
huibao(){
// 调用本身组件的back方法
// this.$emit(名字事件方法名称,参数,参数,参数..)
// 参数:可以是各种类型数据(字符串、数组、对象等)
this.$emit('back','ajax','jquery','h5')
// $emit:只能是组件调用本身的 名字事件方法(普通方法不可以)
}
兄弟传值
需要借助第三方vue对象(eventBus)
步骤:
-
- 定义模块car.js
import Vue from 'vue'
export default new Vue()
- 2.在两个组件中都导入car.js模块
import Car from './car.js'
- 3.在要接收数据的组件A中通过$on给car对象定义方法并设置形参
请在生命周期函数created中给Car声明事件方法,这样组件被使用的时候事件方法一并就初始化好了
- 4.在传递数据的组件B中,让car对象通过$emit()调用传递数据的事件并传参
export default {
created(){
// car.$on('事件名称', (形参,形参,形参...) => {事件处理过程 })
Car.$on('callback', (arg1,arg2)=>{
console.log(arg1,arg2)
})
}
}
<button @click="callback">发布命令button>
<script>
export default {
methods:{
send(){
Car.$emit('callback', '1000元', '31号')
}
}
}
script>
通过button按钮点击触发传递数据的实现
-
- 将A、B组件引入并注册给根组件
- 6.在模板中使用两个组件
组件中的this指向vue实例对象,当前组件
其他访问个别组件数据的方法
- r o o t 可以访问根组件的数据( d a t a , m e t h o d s , c o m p u t e d 等),一些公共数据和方法可以放在根组件上通过 root可以访问根组件的数据(data,methods,computed等),一些公共数据和方法可以放在根组件上通过 root可以访问根组件的数据(data,methods,computed等),一些公共数据和方法可以放在根组件上通过root获取数据
- $root只适用于少量组件项目,一般中大型项目用Vuex更好
- $parent可以用来访问到父级的数据
插槽
注:在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了废弃的 slot 和 slot-scope 语法。
1. 父组件中引用子组件位置标签之间添加要插入的内容
2. 在子组件中使用标签定义填充内容的填充位置,slot就是插槽
3. 插槽填充内容可以是文本、元素、组件
插槽优点:
1. 比较灵活,根据不同需要定义不同位置
2. 提高开发效率,避免定义多个组件
- 父组件引用子组件位置
<com-son>
<span>今天是周二span>
<span>天气很好span>
com-son>
- 子组件模板
<template>
<div id="son">
<p>我是Son子组件p>
<p id="keng1"><slot>slot>p>
<p id="keng2"><slot>slot>p>
<p id="keng3"><slot>slot>p>
div>
template>
- 填充在子组件标签之间未被匹配的内容,都会填充到所有子组件模板的匿名插槽中
- 如果子组件模板中未包含slot元素,填充内容将会被抛弃
后备(默认)内容和作用域
- 在slot标签之间可以提供默认内容,如果slot未被填充,slot标签中的默认内容会显示在slot标签位置
- 父组件提供内容对默认内容有覆盖作用
// 当不为子组件slot标签提供内容时,slot标签位置显示默认内容
<button type="submit"><slot>Submitslot>button>
- 父级模板只能访问父级的数据,子级模板只能访问子级数据,在子组件引用位置不能访问子组件数据,设置的插槽内容中也不能访问(父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。)
具命插槽
slot
标签有一个特殊的属性name可以用来定义具名的插槽
- 一个不带name 的
会带有隐含的name—“default”。
- 在向具名插槽提供内容的时候,我们可以在一个
元素上使用v-slot 指令,并以v-slot 的参数 的形式提供其名称
- 根据v-slot指令的参数将template中提供内容填充到name相同的slot标签位置
- v-slot指令只能添加在 t e m p l a t e \color{#FF3030}{template} template标签中(除了提供内容只有默认插槽时,v-slot可以直接用在子组件上)
组件模板
<div class="container">
<header>
<slot name="header">slot>
header>
<main>
<slot>slot>
main>
<footer>
<slot name="footer">slot>
footer>
div>
- 父级引用
组件位置
<base-layout>
<template v-slot:header>
<h1>Here might be a page titleh1>
template>
<p>A paragraph for the main content.p>
<p>And another one.p>
<template v-slot:footer>
<p>Here's some contact infop>
template>
base-layout>
作用域插槽
作用:允许插槽内容访问子组件数据
- 在子组件模板中 v − b i n d \color{#FF3030}{v-bind} v−bind在slot元素上绑定子组件的数据,在给父级插槽内容使用
- 绑定在
元素上的特性被称为插槽prop。
- 通过v-slot可以给插槽prop提供一个名字
- 将子组件提供内容在插槽中使用
<slot v-bind:user="user">
{{ user.lastName }}
slot>
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
template>
current-user>
单个默认插槽缩写
- 当提供的内容只添加到默认插槽时,组件标签可以当做插槽的模板使用,v-slot指令可以放在组件标签上
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
current-user>
注: 默认插槽的缩写不可以和具名插槽混用,只要出现多个插槽,请始终为所有的插槽使用完整的基于 的语法
解构插槽Prop
- 作用域插槽的内部工作原理是将插槽内容包裹在一个传入单个参数的函数
- 因为作用于插槽原理是函数
- 上述例子中从插槽传入数据实际都传入了slotProps中,这意味着 v-slot 的值可以是任何能够作为函数的参数的 js 表达式。
- 在支持环境下(单文件组件或现代浏览器),也可以使用ES6解构传入数据,并且通过别名修改传入数据名称,也可以定义后备内容(默认数据),在prop为undefined时起作用
function (slotProps) {
// 插槽内容
}
结构设置别名
<current-user v-slot="{ user: person }">
{{ person.firstName }}
current-user>
设置默认值
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
current-user>
动态插槽名称与具名插槽缩写
- 和v-bind、v-on一样,v-slot参数也可以使用动态指令参数,并且有缩写#,可以v-slot:header可以重写为#header,
- 但如果v-slot没有参数,则不能缩写并且无效
- 如果不具名想要缩写,也可以写成
#default={user}
...
获取dom元素或子组件
- 在使用标签或子组件的位置添加
ref
标识
这是父组件
2.操作dom或子组件,通过vue.$refs.ref标识
mounted(){
// 操作真实dom
this.$refs.xxx
// 调用子组件方法
this.$refs.comSon.send()
}
将原生事件绑定到组件(.native)和$listener
- .native 作用:将原生事件绑定到组件中的根元素上,当组件根元素触发事件时,执行绑定的事件处理函数
// 给base-input根元素添加onfocus原生事件
- $listener 作用包含了父作用域中当前组件绑定的所有事件监听,使用.native的除外
- l i s t e n e r v − o n 可以将 listener v-on可以将 listenerv−on可以将listener包含的所有事件监听传递到当前组件的任意内部元素(用于创建高级组件)
$listener的使用
- 父子组件事件传递
- 将$listner传递给组件的内部指定元素
focus($event) {
console.log($event.target);
}
子组件
孙子组件
- 将$listener和其他事件到一起通过计算属性绑定给指定元素
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:
` `
})
.sync 修饰符
使用场景: 对数据更改之后自动更新,使用update:myPropName的模式触发事件
作用:自动更新父组件属性的v-on监听器
注 .sync不能与表达式一起使用
// 等价
bar = val">
// 更新foo
this.$emit('update:foo', newValue)
动态组件和异步组件
动态组件实现不同组件之间的动态切换——is attribute
需求: 使用动态组件实现组件的切换
- 使用component元素和is attribute
- is绑定变量可以是已注册组件的名字或一个组件的选项对象
- 在动态组件中value通过v-bind绑定想和普通组件一样起作用需要添加.prop修饰符(将v-bind绑定属性不作为元素attribute,改为按元素proerty处理)
// 对象
<div id="dynamic-component-demo" class="demo">
<button
v-for="tab in tabs"
v-bind:key="tab.name"
v-bind:class="['tab-button', { active: currentTab.name === tab.name }]"
v-on:click="currentTab = tab"
>
{{ tab.name }}
button>
<component v-bind:is="currentTab.component" class="tab">component>
div>
var tabs = [
{
name: "Home",
component: {
template: "<div>Home componentdiv>"
}
},
{
name: "Posts",
component: {
template: "<div>Posts componentdiv>"
}
},
{
name: "Archive",
component: {
template: "<div>Archive componentdiv>"
}
}
];
new Vue({
el: "#dynamic-component-demo",
data: {
tabs: tabs,
currentTab: tabs[0]
}
});
// 组件
<div id="dynamic-component-demo" class="demo">
<button
v-for="tab in tabs"
v-bind:key="tab"
v-bind:class="['tab-button', { active: currentTab === tab }]"
v-on:click="currentTab = tab"
>
{{ tab }}
button>
<component v-bind:is="currentTabComponent" class="tab">component>
div>
Vue.component("tab-home", {
template: "<div>Home componentdiv>"
});
Vue.component("tab-posts", {
template: "<div>Posts componentdiv>"
});
Vue.component("tab-archive", {
template: "<div>Archive componentdiv>"
});
new Vue({
el: "#dynamic-component-demo",
data: {
currentTab: "Home",
tabs: ["Home", "Posts", "Archive"]
},
computed: {
currentTabComponent: function() {
return "tab-" + this.currentTab.toLowerCase();
}
}
});
动态组件保持切换之前的状态——keep-alive
需求: 保持切换之前状态,在渲染第一次后就缓存
- 组件切换时每次切换都会重新创建一个新实例
- 使用keep-alive标签包裹,能够保持状态
注:keep-alive标签要求被切换的标签都有自己的名字,还可和vue-router配合使用
异步组件
作用: 使程序加载更快,优化应用程序,使应用程序的初始包尽可能小
应用程序越来越大会使应用程序的加载越来越慢,解决方式:拆分代码或延迟加载
- 将应用分割成小的代码块,根据需要从服务器加载一个模块
- vue允许将组件定义为一个函数工厂动态解析组件
- 只有组件需要被渲染的时候才会触发该工厂函数
- 触发后的工厂函数会把结果缓存起来, 用于后面渲染
- 异步函数可以和webpack的代码分割功能配合使用
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
- 可以在工厂函数中返回一个Promise,webpack2和ES2015语法一起使用,实现动态导入
Vue.component( 'async-webpack-example',
// 这个动态导入会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
- 局部注册,可以直接返回一个Promise函数
new Vue({
// ...
components: { 'my-component': () => import('./my-async-component') }
})
异步组件处理加载状态
- 2.3.0版本新增异步组件工厂函数可以返回如下格式的对象
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
注:如果Vue Router想使用上述语法的话,版本必须为2.4及以上
异步组件的应用
- 为每个按钮绑定同一个方法,根据不同传参显示不同组件
按需加载组件,或者异步组件,应用component的 is 属性
// 结构
<template slot-scope="scope">
<el-button type="text" size="mini" @click="handleSchedule('CustomerInfoSchedule',scope.row.customer_id)" >
详情
el-button>
<el-button type="text" size="mini" @click="handleSchedule('VisitRecordSchedule',scope.row.customer_id)">
回访
el-button>
<el-button type="text" size="mini" @click="handleSchedule('AddCustomerSchedule',scope.row.customer_id)">
编辑
el-button>
<el-button type="text" size="mini" @click="handleSchedule('AddPeopleSchedule',scope.row.customer_id)">
添加联系人
el-button>
template>
// 动态组件显示位置
<component :is="currentComponent" :customer_id="customer_id" @componentResult="componentResult">component>
// 数据
data() {
return {
currentComponent: "",
customer_id:'',
}
}
// 定义方法
// 这里直接接收name,然后相对应的引入组件,同时传值
handleSchedule(name, id) {
this.customer_id = id;
this.currentComponent = name;
},
// 这是子组件触发父组件返回回来的方法,因为我的组件都是弹出框
// 所以在子组件关闭弹出框的时候,我让this.currentComponent为空
// 同时可以选择性的刷新数据
componentResult(type) {
if (type == "upData") {
this.getTableData()
} else {
this.currentComponent = ""
}
},
// 注册组件
components: {
AddCustomerSchedule(resolve) {
require(["../components/AddCustomer"], resolve);
},
AddPeopleSchedule(resolve) {
require(["../components/AddPeople"], resolve);
},
CustomerInfoSchedule(resolve) {
require(["../components/CustomerInfo"], resolve);
},
VisitRecordSchedule(resolve) {
require(["../components/VisitRecord"], resolve);
},
},
- 异步组件实现组件按需加载
// import HomeHeader from "./components/Header";
// import HomeSwiper from "./components/Swiper";
// import HomeIcons from "./components/Icons";
// import HomeRecommend from "./components/Recommend";
// import HomeWeekend from "./components/Weekend";
export default {
name: "Home",
components: {
HomeHeader: () => import("./components/Header"), //异步组件加载方式
HomeSwiper: () => import("./components/Swiper"),
HomeIcons: () => import("./components/Icons"),
HomeRecommend: () => import("./components/Recommend"),
HomeWeekend: () => import("./components/Weekend")
}
}
- 异步组件实际应用
- 创建4个组件
//first
<template>
<div>我是第一个页面</div>
</template>
//second
<template>
<div>我是第二个页面</div>
</template>
//three
<template>
<div>我是第三个页面</div>
</template>
//four
<template>
<div>我是第四个页面</div>
</template>
+ 在route/index.js组件异步执行两种方式
import Vue from 'vue'
import VueRouter from 'vue-router'
/*import First from '@/components/First'
import Second from '@/components/Second'*/
Vue.use(VueRouter)
//方案1 ,必须添加syntax-dynamic-import插件
const first =()=>import(/* webpackChunkName: "group-foo" */ "../components/first.vue");
const second = ()=>import(/* webpackChunkName: "group-foo" */ "../components/second.vue");
const three = ()=>import(/* webpackChunkName: "group-fooo" */ "../components/three.vue");
const four = ()=>import(/* webpackChunkName: "group-fooo" */ "../components/four.vue");
//方案2
const first = r => require.ensure([], () => r(require('../components/first.vue')), 'chunkname1')
const second = r => require.ensure([], () => r(require('../components/second.vue')), 'chunkname1')
const three = r => require.ensure([], () => r(require('../components/three.vue')), 'chunkname2')
const four = r => require.ensure([], () => r(require('../components/four.vue')), 'chunkname2')
//懒加载路由
const routes = [
{ //当首次进入页面时,页面没有显示任何组件;让页面一加载进来就默认显示first页面
path:'/', //重定向,就是给它重新指定一个方向,加载一个组件;
component:first
},
{
path:'/first',
component:first
},
{
path:'/second',
component:second
}, {
path:'/three',
component:three
},
{
path:'/four',
component:four
}
//这里require组件路径根据自己的配置引入
]
//最后创建router 对路由进行管理,它是由构造函数 new vueRouter() 创建,接受routes 参数。
const router = new VueRouter({
routes
})
export default router;
- webpack设置代码切割
module.exports={
entry:'./src/main.js',
output:{
path:path.resolve(__dirname,'./dist'),
publicPath:'/dist/',
filename:'build.js',
chunkFilename:'chunks/[name]-[chunkhash:8].js'
},
...
}
父子传值prop的使用
prop的大小写
- html attribute大小写不敏感,浏览器会把大写字符解释为小写
- camelCase的属性名需要使用等价的kebab-case命名
- 使用字符串模板不存在该限制
props: ["postTitle"]
<blog-post post-title="hello!"></blog-post>
属性类型
- 可以是数组
- 也可以通过对象指定传入值得类型
属性绑定值
- 可以是任何形式
- 出入一个对象的所有property
- 将所有传入值定义在一个对象中,v-bind不带参数绑定
<blog-post v-bind="post"></blog-post>
=>
<blog-post v-bind:id="post.id" v-bind:title="post.title" ></blog-post>
单向数据流
- 父子传值形成单向下行绑定
- 父级prop更新会数据流向下流向子级,但子级中不能更改父级中的状态
- 两种常见的试图变更一个 prop 的情形:
- 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用
- 最好定义一个本地数据将这个prop作为初始值
- 这个 prop 以一种原始的值传入且需要进行转换
- 最好使用prop的值定义一个计算属性
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
prop验证
- 为组件prop指定验证要求
- 定制 prop 的验证方式,为 props 中的值为带有验证需求的对象,而不是一个字符串数组
- type类型,required 是否必填,default默认值,validator自定义验证
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
}
}
}
})
- 验证失败控制台会产生一个警告
- prop会在组件实例创建之前验证,所以组件中的选项在prop验证的函数中是不可用的
类型检查
- 包括js基本数据类型和引用数据类型以及Symbol
- type也可以是自定义构造函数,并通过instanceof进行检查确认
非prop的Attribute
- 指传递给组件,但并没有相应prop定义的attribute
- 非prop定义attribute会加到组件的根元素上
- 应用场景:给组件添加任意attribute,组件可以接受任意attribute
传入attribute与组件已有attribute替换/合并
- 传入attribute会替换破坏组件内部已设置好的值
- class和style会将传入值与已设置好的值进行合并
禁用attribute的继承
- 在组件中设置inheritAttrs: false
- 适合配合实例的$attrs property
- $attrs包含传递给一个组件的attribute
- inheritAttribute与$attrs可以指定attribute绑定到指定的元素
- inheritAttribute不影响style和class的绑定
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: ` `
})
组件边界情况的处理
- 这里记录的都是和处理边界情况有关的一些需要对 Vue 的规则做一些小调整的特殊情况功能,不过这些功能都是有劣势或危险的场景
访问其他组件选项
- 根组件——$root
- 只适用于小型vue项目或demo演示,不适于中大型项目
- 中大型项目要是用vuex
- 父组件——$parent
- 使逻辑难以理解和调试
- 在共享组件中使用较方便,使所有子组件方便访问父组件中的选项
- 子组件或子元素——ref
- ref和v-for一起使用,得到的$refs.key将会是一个包含对应数据源的这些子组件的数组集合
- $refs只会在组件渲染完成之后生效
- $refs不是响应式的
- 应避免在模板或计算属性中访问$refs
依赖注入——provide/inject
- 2.2新增
- 类型:
- provide:Object | () => Object
- inject:Array | { [key: string]: string | Symbol | Object }
- 应用场景:开发高阶组件/组件库,将一些选项更好的扩展到更深层嵌套的后代组件中
- provide 选项指定想要提供给后代组件的数据/方法
- inject 选项来接收指定的数据/方法
// 在需要传递的组件中传递
provide: function() {
return {
getMap: this.getMap
};
}
// 再用到的组件中接收
inject: ["getMap"]
// 使用
this.getMap
- inject注入值可以作为其他属性默认值使用
- 作为prop默认值和data变量(vue版本必须2.2.1以上)
- 低版本vue在props和data初始化完毕之后才会得到
inject: ['foo'],
props: {
bar: {
default () {
return this.foo
}
}
}
data() {
return {
bar:this.foo
}
}
- 为inject设置默认值,使其变为可选项 ([email protected]+)
const Child = { inject: { foo: { default: 'foo' } } }
// 使用 from 来表示其源 属性
const Child = {
inject: {
foo: {
from: 'bar',
default: 'foo'
}
}
}
注
1. provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。
2. 只要组件上下游关系成立,provide/inject始终生效
3. 类似于React上下文特性
4. provide应该是一个对象或返回对象的函数,inject应该是一个数组或对象
5. provide和inject不是可响应的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
6. 依赖注入将应用程序中的组件与当前的组织方式耦合起来,使重构变得更加困难
7. 创建一个规模化中心化的数据建议使用vuex状态管理方案
可复用性&组合
混入——Mixin
- 可以包含任意组件选项的对象
- 引入后会被混合到组件对应选项中
- 使用场景:提取不同组件的相同数据或方法,并混入组件,方便统一编辑修改
Mixin选项合并
- $data中数据合并发生冲突以组件数据优先
- 同名钩子函数将合并为一个数组,混入对象钩子函数在组件钩子函数之前调用
- 其他值为对象的选项将会与组件中对象进行合并,成员发生冲突,以组件对象成员为主
- Vue.extend()采用同样合并策略
- 混入进行全局注册后,之后创建每个实例都将带有混入选项
- data中数据会递归合并,同名钩子函数会合并为一个数组,所有钩子和数据都是以mixins中的优先,如有重名的成员会被重写
全局混入
将mixins进行全局注册,每一个新建的vm实例都会注入全局mixins的处理逻辑
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: 'hello!'
})
// => "hello!"
自定义选项合并策略
通过Vue.config.optionMergeStrategies api来自定义合并策略
Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
// 返回合并后的值
}
自定义指令
- 适用场景: 对组件中普通DOM元素进行底层操作
局部和全局注册自定义指令
// 注册一个全局自定义指令
Vue.directive(name, {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// doing...
}
})
// 在组件中添加一个directives选项
directives: {
focus: {
// 指令的定义
inserted: function (el) {
//doing...
}
}
}
- 使用: 在元素上添加自定义指令
自定义指令的钩子函数
- bind inserted unbind: 都只调用一次
- bind: 在指令第一次绑定元素时调用,一般进行元素样式设置
- inserted:在绑定元素插入父元素时调用,一般进行与绑定元素有关的js逻辑,可以再此获取绑定元素的父节点
- update:所在组件的VNode更新时调用,无法确定更新发生在其子VNode更新的前后,指令的值无法确认是否更改
- 作用:可以通过比较更新前后的值,减少不必要的更新
- componentUpdated:指令所在组件的VNode及其子VNode全部更新后调用
- unbind: 指令与绑定元素解绑时调用
钩子函数的参数
- el 绑定元素
- binding 一个Object,包括下列属性
- name 指令名称(v-前的名字)
- value 指令的绑定值 直接传入的值或简单逻辑运算后的值
- oldValue指令更新之前的旧值(只在update和componentUpdated中可用)
- expression 字符串形式的指令表达式 eg. v-acl="1+1”,"1+1"就是表达式
- arg 传递给指令的参数,可选参数 eg. v-my-directive:foo 中,参数为 “foo”
- modifiers 包含修饰符的对象 eg. v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
- vnode Vue编译生成的虚拟节点
- oldVnode 上一个虚拟节点(只在update和componentUpdated可用)
注:
- 除el参数,其他均为只读属性,不可以进行修改
- 需要在钩子之间共享数据,建议通过元素的 dataset 来进行
动态参数
将自定义指令的参数绑定为变量,根据情况更新自定义指令绑定参数
<p v-pin:[direction]="200">p>
自定义指令回调简写
在 bind 和 update 时触发相同行为,不关心其它的钩子。可以这样写:
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
自定义指令可接受所有合法的js表达式
如果需要接受多个参数时可以传入一个字面量对象
Vue.directive('demo', function (el, binding) {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})