重点说明:当前笔记内容侧重点为各个知识点的应用及需要注意的地方,而不是每个知识点的概念。概念相关请查看官网:https://cn.vuejs.org/v2/guide/installation.html vue入门教程。
代码及xmind源文件Gitee:https://gitee.com/zhiyaoyun/vue-project
代码下载地址:https://gitee.com/zhiyaoyun/vue-project.git
目的
处理模板中需要复杂逻辑计算的属性,简化模板中的表达式。
优点
a. 减少模板中的计算逻辑
b. 能够进行数据缓存,提高性能【和普通函数相比】
c. 响应式数据
使用
<h2>计算属性h2>
<p v-if="show">{{fullName}}p>
<h2>函数获取h2>
<p v-if="show">{{getFullName()}}p>
<script>
el: "#app",
data: {
firstName: 'yun',
lastName: 'Zhiyao',
show: true
},
computed: {
fullName() {
console.log("计算属性")
return this.firstName + this.lastName;
}
},
methods: {
getFullName() {
console.log("函数获取")
return this.firstName + this.lastName;
}
}
})
script>
目的: 执行一些复杂的逻辑,包括异步或者开销比较大的计算
和计算属性相比:
a. 更加的灵活、通用
b. 计算属性执行不了的逻辑可以在侦听器中进行执行,反之则不可
使用
<h2>侦听器h2>
<p>当前年龄:<input type="text" v-model="age">p>
<p>应该做的事情:{{doSomeThing}}p>
<p>应该做的事情:{{doSomeThing2}}p>
<script>
let vm = new Vue({
el: "#app",
data: {
age: 0,
doSomeThing: '',
},
computed: {
doSomeThing2(){
// 无效,不可以在计算属性中使用异步操作
setTimeout(()=>{
return this.age + '岁的事情'
},1000)
}
},
watch: {
age(newAge){
setTimeout(()=>{
this.doSomeThing = newAge + '岁的事情'
},1000)
}
}
})
script>
实现原理:为不同的元素监听的相对应的事件
a. input / textarea : value属性 + input事件
b. checkbox / radio:checked属性 + change 事件
c. select :value属性 + change事件
应用
<h1>使用v-modelh1>
<h3>message:{{message}}h3>
<input type="text" v-model="message">
<h1>v-model的实现原理h1>
<h3>message2:{{message2}} ; checkbox:{{checkbox}}h3>
<input type="text" :value="message2" @input="handleInput($event)">
<input type="checkbox" @change="handleChange($event)">
<script>
const vm = new Vue({
el: "#app",
data: {
message: "你好",
message2:'hello',
checkbox:true,
},
methods:{
handleInput(event){
this.message2 = event.target.value
},
handleChange(event){
console.log(event)
this.checkbox = event.target.checked
}
}
})
script>
<h1>修饰符h1>
<input type="text" v-model.lazy="message">
<input type="text" v-model.number="numberData">
<input type="text" v-model.trim="message">
<p>{{total}}p>
<my-component v-model="total">my-component>
<my-component @input="handleInputCom">my-component>
Vue.component('my-component', {
template: `<button @click="handleClick">+1button>`,
data() {
return {
count: 0,
}
},
methods: {
handleClick() {
this.count++;
this.$emit("input", this.count)
}
}
})
<h1>自定义组件使用-model2h1>
{{checkbox}}
<base-checkbox v-model="checkbox">base-checkbox>
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
:checked="checked"
@change="$emit('change', $event.target.checked)"
>`
})
组件在Vue中是一个非常重要的基础,主要有以下几点注意:
注册组件时,需要注意组件的命名方式,组件的命名有两种方式:
Vue.component('组件名称',{组件配置项})
import MyComponent from './components/MyComponent'
// 使用数组的方式接受props
props: ['text','mystyle'],
// 使用对象的方式接受props
props:{
text:{
type:String
},
mystyle: {
type:Object
}
},
(a)prop只作为初始值,后续可能会在组件内部发生改变,但不会影响到父元素。这种情况下可以定义一个data属性接收prop传递过来的值,后续的改变在data属性上处理。
props:{
text:{
type:String
},
mystyle: {
type:Object
}
},
data(){
return {
buttonText:this.text
}
},
template:``,
methods:{
changeText(){
this.buttonText = '自定义文字'
}
}
(b)prop作为依赖源,当prop发生改变时,可能会对prop进行处理后响应显示。
// 接受到的prop属性
props:{
text:{
type:String
},
},
// 使用计算属性处理prop值
computed:{
buttonText2(){
return this.text.split('').reverse().join('')
}
},
// 验证类型
propA:Number,
// 可能会传递过来多种类型
propB:[String,Number],
// 验证是否为自定义类型的实例
propC:Person,
// 使用type属性验证
propD:{
type:Number
},
// 使用默认值,当没有传值过来时使用该值
propE:{
type:String,
default:"默认值"
},
// 验证必填项
propF:{
type:Number,
required:true
},
// 自定义验证函数,validatoe函数返回一个布尔值
propG:{
type:String,
validator(value){
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
},
// 子组件中定义的模板
template:`<div><button :style="mystyle" @click="changeText">{{buttonText2}}button>div>`
props:{
text:{
type:String
},
mystyle: {
type:[String , Number]
},
}
// 在调用子组件时
<base-button :text="text" :mystyle="mystyle" disabled data-name="button"/>
// 渲染结果
<div disabled="disabled" data-name="button">
<button style="background: red;">钮按击点button>
div>
(a)使用属性:inheritAttrs:false 先禁止根节点继承属性,使用该属性后,没有被接受的属性值则直接被忽略
// 子组件中定义的模板
template:`<div><button :style="mystyle" @click="changeText">{{buttonText2}}button>div>`
props:{
text:{
type:String
},
mystyle: {
type:[String , Number]
},
}
inheritAttrs:false
// 渲染结果
<div>
<button style="background: red;">钮按击点button>
div>
(b)指定元素继承未被接受的组件:使用 v-bind = “$attrs”
template:`<div><button v-bind="$attrs" :style="mystyle" @click="changeText">{{buttonText2}}button>div>`,
// 渲染结果
<div>
<button disabled="disabled" data-name="button" style="background: red;">钮按击点button>
div>
props
具体使用方法同 3-2 组件传值
$emit / $on: $emit用于子组件向父组件传值
// 子组件 Header.vue
<template>
<div>
<h1 @click="changeTitle">{{ title }}h1> //绑定一个点击事件
div>
template>
<script>
export default {
name: 'header',
data() {
return {
title:"hellow"
}
},
methods:{
changeTitle() {
// 通过自定义事件,将自己的参数值传递给父组件
this.$emit("titleChanged","子向父组件传值");
}
}
}
script>
// 父组件接收值
<template>
<div id="app">
// 与子组件titleChanged 自定义事件保持一致
// updateTitle($event)接受传递过来的参数值
<header @titleChanged="updateTitle">header>
<h2>{{ title }}h2>
div>
template>
<script>
import Header from "./components/Header"
export default {
name: 'Parent',
data(){
return{
title:"传递的是一个值"
}
},
methods:{
//声明这个函数
updateTitle(e){
this.title = e;
}
},
components:{
"app-header":Header,
}
}
script>
缺点:无法跨层级使用。$refs 只在组件渲染完成之后可以使用,而且是非响应式的。
说明:在日常开发过程中,应该避免直接使用这几个方法,这种方法使得组件之间的耦合性比较强。且数据流向不明确,代码不好维护。
// 模板
<div id="app">
<h1>子组件1h1>
<child ref="child1">child>
<h1>子组件2h1>
<child ref="child2">child>
<h1>子组件3h1>
<child ref="child3">child>
div>
<script>
Vue.component('Child',{
template:`{{message}}{{parentData}}`,
data(){
return {
message:'子组件',
}
},
mounted(){
let parentMessage = this.$parent.parentMessage;
console.log("从父组件实例中获取数据")
console.log(parentMessage);
},
computed:{
parentData(){
return this.$parent.parentMessage
}
}
})
const vm = new Vue({
el:"#app",
data:{
parentMessage:'父组件'
},
mounted() {
let childMessage = this.$children[0].message;
console.log("从子组件实例中获取数据")
console.log(childMessage);
let childHref = this.$refs;
console.log(childHref)
},
})
script>
作用:允许一个祖先组件向其所有子孙后代注入一个依赖,不 论 组件层次有多深,并在起上下游关系成立的时间里始终生效。祖先组件中通过 provider 来 提供变量,然后在子孙组件中通过 inject 来注入变量
优点:主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系
// 基本的使用:这种情况下数据是不响应的
//父组件.vue
export default {
provide: {
color: 'red'
}
// 子组件.vue
export default {
inject: ['color'],
mounted () {
console.log(this.color) //输出red
}
}
// 响应式方法1:直接传递整个父组件的实例过去,这样当父组件的值改变时,子组件接受到的数据也会改变
//父组件.vue
export default {
provide(){
return parent:this
}
}
// 子组件.vue
export default {
inject: ['parent'],
mounted () {
console.log(this.parent.color) //输出red
}
}
// 响应方法2:使用2.6+版本以后的 Vue.observable
//父组件.vue
export default {
provide(){
this.theme = Vue.observable({
color:'red'
})
return {
theme:this.theme
}
}
}
// 子组件.vue
export default {
inject: ['theme'],
mounted () {
console.log(this.theme.color) //输出red
}
}
** a t t r s : ∗ ∗ 包 含 了 父 作 用 域 中 不 被 p r o p 所 识 别 ( 且 获 取 ) 的 特 性 绑 定 ( c l a s s 和 s t y l e 除 外 ) 。 当 一 个 组 件 没 有 声 明 任 何 p r o p 时 , 这 里 会 包 含 所 有 父 作 用 域 的 绑 定 ( c l a s s 和 s t y l e 除 外 ) , 并 且 可 以 通 过 v − b i n d = " attrs:** 包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个 组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=" attrs:∗∗包含了父作用域中不被prop所识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v−bind="attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
**KaTeX parse error: Unexpected character: '' at position 60: …n 事件监听器。它可以通过 v̲on="listeners" 传入内部组件
具体使用方式可查看3-2 prop传值中
slot:插槽,需要动态的向组件中传递内容时,可以使用插槽传递。根据业务使用场景,插槽主要分为以下几类:
- 默认插槽
- 具名插槽
- 作用域插槽:作用域插槽编译时作用域和组件调用同级,所以不能访问组件内部的作用域。
const vm = new Vue({
el:"#app",
data:{
message:'这是一个父组件',
},
})
<!--在子组件中使用slot标签占位-->
Vue.component('ChildMiddle',{
template:`
{{message}}
`,
data(){
return {
message:'默认插槽',
}
},
})
// 使用子组件时
<h1>父组件信息</h1>
<p>{{message}}</p>
// 直接插入信息
<child-middle>
你好,默认插槽
</child-middle>
v-slot:footer,v-slot的缩写 #
/具名插槽
Vue.component('Child2',{
template:`
{{message}}
组件内容
组件内容2
`,
data(){
return {
message:'具名插槽',
}
},
})
// 调用组件时
<child2>
<template v-slot:header>
<div>header</div>
</template>
<template v-slot:default>
<div>default</div>
</template>
<template #footer>
<div>使用缩写footer</div>
</template>
</child2>
// 作用域插槽,传递了info数据和user数据
Vue.component('Child3',{
template:`
{{message}}
组件内容
组件内容2
`,
data(){
return {
message:'作用域插槽',
info:'info属性子组件信息',
user:{
name:'slot',
version:2.6
}
}
},
})
// 调用子组件时,接收到的是props对象,可以单独一个命名,也可以自己把需要的字段解构出来
<child3>
<template #header="{info}">
<div>{{info}}</div>
</template>
<p>default</p>
<template #footer="{user}">
<div>{{user.name}} - {{user.version}}</div>
</template>
</child3>
Vue.component('ChildCom', {
name: 'child-com',
template: `
{{ message }}+{{count}}
`,
props:['count'],
data() {
return {
message: '递归组件',
}
},
})
const vm = new Vue({
el:"#app",
data:{
message:'这是一个父组件',
count:0
},
})
// 定义的内联模板组件
Vue.component('ChildCom2', {
name: 'child-com2',
data() {
return {
msg: '内联模板',
}
},
})
// 使用内联模板组件
<child-com2 inline-template>
<div>
<div>里面内容使用内联模板渲染</div>
<p>{{msg}}</p>
</div>
</child-com2>
// 定义多个组件
Vue.component('comA', {
template: `组件A`,
})
Vue.component('comB', {
template: `组件B`,
})
Vue.component('comC', {
template: `组件C`,
})
// 定义父组件
const vm = new Vue({
el: "#app",
data: {
currentView:'comA',
},
methods:{
changeCom(comName){
this.currentView = comName;
}
}
})
// 动态展示各个组件
<component :is="currentView"></component>
<button @click="changeCom('comA')">comA</button>
<button @click="changeCom('comB')">comB</button>
<button @click="changeCom('comC')">comC</button>
<button @click="changeCom('comD')">comD</button>
<keep-alive>
<component :is="currentView">component>
keep-alive>
// 定义一个异步组件
Vue.component('comE',function (resolve,reject){
window.setTimeout(function (){
resolve( {
template: `{{message}}`,
data(){
return {
message:'异步组件,5000后渲染'
}
}
})
},5000)
})
// 使用异步组件
<com-e></com-e>