一、组件
Vue.component(‘组件名’, 组件)
每个组件常用的包含有:
template(相当于html),
data(存放数据)
methods(自定义函数写在里面)
computed(计算属性)
watch(侦听器)
生命周期钩子函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>组件</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
// 局部组件的写法,在使用外部组件时,需要在组件内components注册引入的外部组件
var Header = {
template: "<div>顶部</div>"
}
var Footer = {
template: "<div>底部</div>"
}
var Home = {
template: `<div>
<Header></Header>
<div class="content">内容</div>
<Footer></Footer>
</div>`,
components: {
Header,
Footer
}
}
var Mine = {
template: `<div>
<Header></Header>
<div class="content">我的</div>
<Footer></Footer>
</div>`,
components: {
Header,
Footer
}
}
new Vue({
el: '#app',
template: `<div>
<Home></Home>
<Mine></Mine>
</div>`,
components: {
Home,
Mine
}
})
</script>
</body>
</html>
把上面公共需要的组件写成公共组件
<script>
var Header = {
template: "<div>顶部</div>"
}
var Footer = {
template: "<div>底部</div>"
}
Vue.component('Header', Header) // 公共组件
Vue.component('Footer', Footer) // 公共组件
var Home = {
template: `<div>
<Header></Header>
<div class="content">内容</div>
<Footer></Footer>
</div>`
}
var Mine = {
template: `<div>
<Header></Header>
<div class="content">我的</div>
<Footer></Footer>
</div>`
}
new Vue({
el: '#app',
template: `<div>
<Home></Home>
<Mine></Mine>
</div>`,
components: {
Home,
Mine
}
})
</script>
二、动态组件
作用: 在多标签的界面我们可能需要多个组件来回切换,这里用动态组件会方便很多
动态组件实现:通过 Vue 的 元素加一个特殊的 is attribute 来实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动态组件</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
let ComponentA = {
template: `<div>我是A组件</div>`
}
let ComponentB = {
template: `<div>我是B组件</div>`
}
let ComponentC = {
template: `<div>我是C组件</div>`
}
new Vue({
el: '#app',
template: `<div>
<button @click="componentType = 'ComponentA'">A</button>
<button @click="componentType = 'ComponentB'">B</button>
<button @click="componentType = 'ComponentC'">C</button>
<component :is="componentType"></component>
</div>`,
data() {
return {
componentType: 'componentA'
}
},
components: {
ComponentA,
ComponentB,
ComponentC
}
})
</script>
</body>
</html>
三、slot(插槽)
是什么, 有什么作用?
slot(插槽)顾名思义,就是在建组件的时候预留一个位置,在后期引用组件的时候可以按需给组件分发一些内容,提高组件的灵活性
弊端
是只要一个component中传入了slot,那么如果父组件更新,必定会update子组件,造成性能浪费
普通插槽:
<body>
<div id="app" v-cloak></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
let Child = {
template: `<div>
<span>我是:<slot></slot></span> // 插槽预留位置
</div>`
}
let Parent = {
template: `<div>
<Child>小明</Child> // 分发内容会填充到插槽预留的地方
</div>`,
components: {
Child
}
}
new Vue({
el: '#app',
template: `
<div>
<Parent/>
</div>
`,
components: {
Parent
}
})
</script>
</body>
页面显示:
具名插槽:
具名插槽,就是分发的内容得对号入座了,不能像之前那样随意。具名插槽对我们需要分发多个内容的时候是很有用的
组件内插槽使用name属性给插槽命名
引用组件得时候使用v-slot:name来把内容引入到对应插槽内
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>插槽</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
let Child = {
template: `<div>
<header>我是:<slot name="header"></slot></header>
<div>我是:<slot></slot></div>
<footer>我是:<slot name="footer"></slot></footer>
</div>`
}
let Parent = {
template: `<div>
<Child>
<template v-slot:header>顶部</template>
<template v-slot:footer>底部</template>
内容部分
</Child>
</div>`,
components: {
Child
}
}
new Vue({
el: '#app',
template: `
<div>
<Parent/>
</div>
`,
components: {
Parent
}
})
</script>
</body>
</html>
作用域插槽
每个组件只能访问自己内部的属性方法,如果想往插槽放入子组建内部数据,这里需要用到作用域插槽
v-slot可以缩写为( ‘#’ )
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>插槽</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
let Child = {
template: `<div>
<header>我是:<slot name="header"></slot></header>
<div>我是:<slot :msg='msg'></slot></div> // 将 msg 作为 <slot> 元素的一个 attribute 绑定上去(插槽 prop)
<footer>我是:<slot name="footer"></slot></footer>
</div>`,
data() {
return {
msg: '我是子组件数据'
}
}
}
let Parent = {
template: `<div>
<Child>
<template v-slot:header>顶部</template>
<template v-slot:footer>底部</template>
// 将包含所有插槽 prop 的对象命名为 child,也可以使用任意你喜欢的名字
<template v-slot:default = 'child'>{{child.msg}}</template>
</Child>
</div>`,
components: {
Child
}
}
new Vue({
el: '#app',
template: `
<div>
<Parent/>
</div>
`,
components: {
Parent
}
})
</script>
</body>
</html>
四、计算属性和侦听器
1.计算属性computed
模版内使用简单逻辑表达式是很方便的,但是复杂的逻辑写在模版内会稍显厚重,不利于后期维护,所以复杂的逻辑推荐使用计算属性.
调用函数也可以达到相同效果但区别在于,使用函数的情况只要有数据更新都会调用,但计算属性是基于它们的响应式依赖进行缓存的也就是计算属性只会在对应属性发生改变才会去计算。从性能上来说更推荐使用计算属性
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>计算属性和侦听器</title>
</head>
<body>
<div id="app">
<input v-model="firstName"/>
<input v-model="lastName"/>
<span>{{name}}</span>
<input v-model="val" />
<!-- 函数调用的时候也会实时获取到数据 -->
<span>函数调用{{getName()}}</span>
<!-- 函数和计算属性区别在于:函数调用只要页面有数据刷新都会调用函数,但使用计算属性只会对应属性发生改变才会掉用,比如在v-model为val的输入框输入数据,函数也会调用 -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data() {
return {
firstName: '',
lastName: '',
val: ''
}
},
// 计算属性
computed:{
name() {
return this.firstName + this.lastName
}
},
// 侦听器
watch: {
},
methods:{
getName() {
console.log(1)
return this.firstName + this.lastName
}
}
})
</script>
</body>
</html>
2.侦听器watch
侦听器监听vue实例的变化,如data中的数据、computed、路由的变化
特点:
1.监听获取两个参数一个,一个改变前的,一个改变后的
2.可深度监听
3.可监听路由做页面转换的动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>watch</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<input type="text" v-model="name">
<button @click="person.name = 'lili'">查看</button>
<button @click="person.children.name = 'xiaolili'">深度监听</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data() {
return {
name: 'xiaoxiao',
person: {
name: 'xiaoxiao',
age: 12,
sex: '女',
children: {
name: 'xiaohua',
age: 1,
sex: '男'
}
},
}
},
computed: {
getNewName() {
return this.name
}
},
watch: {
// 监听data中的数据
name(newVal, oldVal) {
console.log(newVal, oldVal)
},
// 引用类型监听
'person.name' (newVal, oldVal) {
console.log(newVal, oldVal)
},
// 深度监听,deep必须为true
person: {
handler(val) {
console.log(val)
},
deep: true
},
// 监听computed
getNewName(newVal, oldVal) {
console.log(newVal, oldVal)
}
}
})
</script>
</body>
</html>
五、filter过滤
filter(过滤器)就是一个javascript函数,filter放在javascript表达式后面由管道("|")符号指示
局部filter过滤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>filter</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
let Home = {
// 可以多个过滤器串联,以管道符号("|")指示,后面的过滤器根据前面数据做处理
template: `<div>
<span>{{count | getCount(1, 2) | getNewCount}}</span>
</div>`,
data() {
return {
count: 1
}
},
// 局部过滤器
filters: {
getCount(val, num1, num2) {
console.log(val) // 1
return val + num1 + num2
},
getNewCount(val) {
return val / 2
},
}
}
new Vue({
el: '#app',
template: `<div>
<Home/>
</div>`,
components: {
Home
}
})
</script>
</body>
</html>
全局过滤器
<script>
// 全局过滤所有组件可以调用
Vue.filter('getDate', function (val) {
return val + 1
})
let Home = {
template: `<div>
<span>{{date | getDate}}</span>
</div>`,
data() {
return {
date: '2020-9-15'
}
}
}
new Vue({
el: '#app',
template: `<div>
<Home/>
</div>`,
components: {
Home
}
})
</script>
六、父子组件双向绑定
方法一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>父子组件双向绑定</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
let Child = {
template: `<div>
<input type="text" v-model="getValue"/>
</div>`,
props: {
value: {
type: String
}
},
computed: {
getValue: {
get: function() {
return this.value // 把父组件传递过来的值赋值给getValue
},
set: function(val) {
this.$emit('input', val) // 所以触发input事件把值传给父组件,使其与子组建输入同步
}
}
}
}
let Parent = {
template: `<div>
<Child v-model="value"/>
{{value}}
</div>`,
data() {
return {
value: 'xxxxx'
}
},
components: {
Child
}
}
new Vue({
el: "#app",
template: `<div>
<Parent/>
</div>`,
components: {
Parent
}
})
</script>
</body>
</html>
方法二
允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>父子组件双向绑定</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
let Child = {
template: `<div>
<input type="text" @input="changeValue($event)"/>
</div>`,
props: {
value: {
type: String
}
},
// v-model 默认prop是值,event是input事件,这儿我们给他自定义属性和方法
model: {
prop: 'value', // 父组件传递过来的数据
event: "change" // changeValue里面$emit定义的事件
},
methods: {
changeValue(e) {
console.log(e)
this.$emit('change', e.target.value)
}
}
}
let Parent = {
template: `<div>
<Child v-model="value"/>
{{value}}
</div>`,
data() {
return {
value: 'xxxxx'
}
},
components: {
Child
}
}
new Vue({
el: "#app",
template: `<div>
<Parent/>
</div>`,
components: {
Parent
}
})
</script>
</body>
</html>
七、ref的使用
1.获取dom元素:
在vue里面是不建议直接操作dom元素的,但是有时候的需求我们需要去操作dom,操作dom我们也可以用原生的方法,但因为vue是单页面应用,所有组件最终会汇聚到一个地方如果用id,我们难免会遇到id重合的时候,使用其他的想象一下工程量一定很大,耗费性能。所有在这种时候我们可以用ref,它会在每个组件内部有自己的一个作用域,也不用担心命名重合的情况
<script>
new Vue({
el: '#app',
template: `<div>
<span ref="name">姓名</span>
</div>`,
data() {
return {}
},
mounted() {
console.log(this.$refs['name'])
},
})
</script>
2.调取子组件内部属性方法
<script>
let componentA = {
template: `<div>
<span ref="name">姓名</span>
</div>`,
data() {
return {}
},
methods: {
getChild() {
console.log('子组建方法')
}
}
}
new Vue({
el: '#app',
template: `<div>
<componentA ref="child"/>
</div>`,
data() {
return {
rootVal: 'dsdsds'
}
},
components: {
componentA
},
mounted() {
this.$refs['child'].getChild() // 调用子组建方法
}
})
</script>