Vue基础.js

目录:

Vue基础.js
语法:
指令
v-if :判断
v-for:循环
注意事项
1. 关于提高性能
v-on:事件绑定
修饰符
v-bind:动态绑定
2. 切换class属性
v-model:数据双向绑定
修饰符
过滤器
Vue的生命周期
Vue的Ajax异步请求:axios
Vue组件
入门案例
组件名
组件注册
组件通讯--父传子
Props
组件通讯--子传父
$emit
父子组件的双向数据绑定
插槽

点击文字跳转,点击背景或三角展开

使用Vue的好处:

  1. 解耦视图和数据
  2. 可复用的组件
  3. 前端路由技术
  4. 状态管理
  5. 虚拟Dom

文档传送门:Vue官网

语法:

el:挂载点

  1. el设置Vue实例的挂载的元素

  2. Vue会管理el选中的及其内部的后代元素(不用js或jq操作不了挂载点之外的)

  3. 可以使用选择器(dom也可以),但推荐使用id选择器

  4. 可以选择使用其他双标签,但不能是html和body(id或类选择器等则也可以选单标签)

注:在methods中,想用dom就可以this.$el(等价于document.getElementById("app"))

data:数据对象

  1. Vue中用到的数据都定义在data

  2. data中可以写复杂类型的数据

  3. 渲染复杂类型的数据时,遵守js的语法即可

computed:计算属性

  1. 用于预处理data中的属性
  2. 内容都是方法,相较于methods而言,其方法大多为名词
  3. 除非方法内的data值发生改变,会再次执行方法,否则方法只执行一次。执行后会将值进行缓存,下次调用直接从缓存中获取
  4. 例如用插值表达式使用时,是不需要在方法名后加()进行使用的

示例:

<div id="app">
    {{fullName}} 他花了 {{totalMoney}} 巨资充实了自己
</div>	
<script>
    var app = new Vue({
        el: '#app',
        data: {
            finalName: "猫巷",
            lastName: "Number One",
            books: [{
                id: 1,
                name: "Thinking In Java",
                price: 40
            },
                    {
                        id: 2,
                        name: "On Java",
                        price: 30
                    },
                    {
                        id: 3,
                        name: "深入理解计算机原理",
                        price: 20
                    },
                    {
                        id: 4,
                        name: "现代操作系统",
                        price: 10
                    }
                   ]
        },
        computed: {
            fullName: function() {
                return this.finalName + " " + this.lastName
            },
            totalMoney: function() {
                let totalMoney = 0;
                for (let i = 0; i < this.books.length; i++) {
                    totalMoney += this.books[i].price;
                }
                return totalMoney;
            }
        }
    });

结果:image-20210830210945895

以上computed里的两个方法是简写,每个计算属性的方法都有一对getter和setter,取值时调用get(),赋值时调用set()。具体如下:

computed: {
            fullName: {
				get: function(){
					return this.finalName + " " + this.lastName
				},
				set: function(newValue){
					// newValue为此fullname的新赋的值
					// set方法通常都不写,因为computed为计算属性。因此通常也被称为只读属性
				}
            },
            fullName: {
				get: function(){
					let totalMoney = 0;
                	for (let i = 0; i < this.books.length; i++) {
                    	totalMoney += this.books[i].price;
                	}
                	return totalMoney;
				},
				set: function(newValue){
					// newValue为此fullname的新赋的值
					// set方法通常都不写,因为computed为计算属性。因此通常也被称为只读属性
				}
            }
        }

methods:定义函数

  1. Vue中用到的方法都定义在methods中,且大多为动词形式
  2. 写法与普通方法无异,遵守js的语法即可
  3. 相较于computed,没有缓存,因此效率与computed相比较低

指令

插值表达式

挂载完对应元素后,可通过{{message}} 取出data中的message值

插值表达式之间支持简单Js,例如:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
	{{5+5}}<br>
	{{ ok ? 'YES' : 'NO' }}<br>
	{{ message.split('').reverse().join('') }}
	<div v-bind:id="'list-' + id">菜鸟教程</div>
</div>
	
<script>
new Vue({
  el: '#app',
  data: {
	ok: true,
    message: 'RUNOOB',
	id : 1
  }
})
</script>
</body>
</html>

效果如下:

10
YES
BOONUR
菜鸟教程 (div的id为list-1)

v-once:只展示初始值

使用v-once后,在渲染数据后,修改data的值,将不会再进行响应式更新。即只展示最初始的值

<div id="app">
    <span v-once>{{message}}</span>
</div>
<script>
	new Vue({
        el: "#app",
        data:{
            message: "hello vue"
        }
    })
</script>

注意:v-once 后面不需要加 =""

v-pre:不解析语法

等价于<pre></pre>标签,例如:

<div id="app">
    <span v-pre>{{message}}</span>
</div>
<script>
	new Vue({
        el: "#app",
        data:{
            message: "hello vue"
        }
    })
</script>

此时页面展示不是hello vue ,而是{{message}}

v-cloak:等待vue解析完数据后再显示页面

浏览器解析html文件是从上往下,有时候js代码卡住,用户就会先看见还未渲染的数据,如{{message}},而不是data中的hello vue,这体验是不好的。v-cloak原理是,加上v-cloak属性的元素,默认会看不见,等待vue渲染完毕后就会删除所有v-cloak属性,然后就自动展示出数据了。

v-text:设置文本值

  1. 设置双标签内的文本值
  2. 会覆盖原双标签里的文本值
  3. 内部可写表达式,也会识别为文本

v-html:设置innerHtml

  1. 设置双标签的innerHtml值
  2. 需要在内部写html语句采用v-html,只需要纯文本则用v-text。v-html写的会被解析为html语句

v-if :判断

根据 if 表达式后的值(true 或 false )来决定是否插入此元素

<div id="app">
    <span v-if="flag">
        <label>账号:</label>
        <input type="text" id="account" placeholder="请输入账号">
    </span>
    <span v-else>
        <label>邮箱:</label>
        <input type="text" id="emails" placeholder="请输入邮箱">
    </span>
    <button type="button" @click="flag = !flag">切换登录</button>
</div>

<script type="text/javascript">
    new Vue({
        el:"#app11",
        data:{
            flag : true
        }
    })
</script>

点击按钮效果如图:image-20210901212032121image-20210901212052854

注意:表单里的值之所以没清空,是因为Vue底层采用的虚拟Dom元素Dom结构极为相似,且是对立情况出现的情况下,Vue底层会渲染同一个的虚拟Dom到页面上,再比较所渲染元素的具体区别(属性值等),进行页面的渲染。

虚拟Dom

浏览器解析Dom时,会将元素直接渲染到页面上。而Vue、React等底层采用了虚拟Dom,元素会先被Vue渲染到虚拟Dom中,在渲染到页面的同时会比较是否有可重复利用的元素,如果有 则渲染时直接复制原虚拟Dom的元素,再根据具体区别(属性值等)进行修改。这么做大大提升了页面渲染的效率。

总结:

  • 获取监听变化后生成的虚拟节点树
  • 与上一次虚拟DOM节点树进行比较
  • 找到差异的部分,渲染到真实的DOM节点上面
  • 更新视图

特殊情况需要解决虚拟Dom的复用时

上述例子,切换表单明显需要清空表单内的值,此时虚拟Dom带来了反面效果,可通过添加key属性来解决。

  • key值相同,代表可以复用
  • key值不同,代表不能被复用

示例:

	<div id="app11">
		<span v-if="flag">
			<label>账号:</label>
			<input type="text" id="account" placeholder="请输入账号" key="username">
		</span>
		<span v-else>
			<label>邮箱:</label>
			<input type="text" id="emails" placeholder="请输入邮箱" key="email">
		</span>
		<button type="button" @click="flag = !flag">切换登录</button>
	</div>

上述例子label没写key,所以可被复用,input二者的key不同,即不可复用。在点击按钮时,input内的值自然就被清空了

v-show:是否展示此元素

  1. 当显示条件为true时:

    v-ifv-show效果相同

  2. 当显示条件为false时:

    v-if不渲染dom元素,而v-show仍然渲染;渲染时会加上行内样式:style = "display: none"

当数据要在显示隐藏间频繁切换时,使用v-show

当数据只有一次切换根据条件及进行渲染时使用v-if

v-for:循环

常用场景:

  1. 数组集合循环

    <div id="app10">
        <ul>
         	<!-- 
    			1.item为值,index为索引
    			2.就写一个默认就为item,即是值
    			3.括号可省略但不建议
    		-->   
            <li v-for="(item,index) in arr">{{item}}==={{index}}</li>
        </ul>
    </div>
    
    <script type="text/javascript">
        new Vue({
            el: "#app10",
            data: {
                arr: [1,2,3,4,5]
            },
        })
    </script>
    

    结果:v-for循环图1

  2. 遍历Json形式的数据(超好用)

    <div id="app10">
        <ul>
            <!-- 
    			1.注意value和key的顺序是反的,先写的始终是值value,后写的是索引index或键key
    			2.其他点同上
    		-->   
            <li v-for="(value,key) in person">{{key}}==={{value}}</li>
        </ul>
    </div>
    
    <script type="text/javascript">
        new Vue({
            el: "#app10",
            data: {
                person: {
                    id:1001,
                    username:"admin",
                    password:"123"
                }
            },
        })
    </script>  
    

    结果:v-for循环图2

  3. json集合(两者结合)

    <div id="app10">
        <table cellspacing="1" border="1">
            <tr>
                <th>编号</th>
                <th>id</th>
                <th>产品名称</th>
                <th>产品价格</th>
            </tr>
            <tr v-for="(product,index) in persons">
    			<td>{{ index + 1 }}</td>
                <td v-for="value in product">{{ value }}</td>
                
            </tr>
        </table>
    </div>
    
    <script type="text/javascript">
        new Vue({
            el: "#app10",
            data: {
                persons: [
                    {id: 1001, proName: "手机", proMoney: "123"},
                    {id: 1002, proName: "平板", proMoney: "1090"},
                    {id: 1003, proName: "电脑", proMoney: "2222"},
                    {id: 1004, proName: "摄像机", proMoney: "3041"},
                ]
            },
        })
    </script>  
    

    结果:v-for结果图3

注:获取对象索引还可以:v-for="(value, key, index) in Person"

注意事项

1. 关于提高性能

官方推荐我们在使用v-for时,给对应的元素或组件加上:key,即绑定key属性。且绑定的属性值为当前元素(item)

原因:这么做其实与其虚拟Dom的Diff算法有关

Diff算法:Diff算法的作用是用来计算出 Virtual DOM (即虚拟Dom) 中被改变的部分,然后针对该部分进行原生DOM操作,而不用重新渲染整个页面

案例:

页面使用ul-li标签展示数组arr=[A,B,C,D,E],此时往第三个位置插入个F元素(即创建新元素)。

此时高效率做法应是直接往第三个位置插入F元素①,而虚拟Dom采用的是极低效率的“将C变成F,D变成C,E变成D,创建新li标签且值为E“。数据更新后,采用Diff算法渲染时会”判断不同的数量,再一个一个更替不同的地方“②。此做法会提升消耗,降低效率

此时,若在遍历时加入key属性,且与单个遍历对象item一一绑定,遍历时虚拟Dom会优先将keyitem对应的值进行绑定渲染,再将新创建的(无对应key值)元素进行插入,即可达到①的效果

用代码总结就是:<ul><li v-for="item in arr" :key="item"> </li></ul>

注意:

  1. 绑定index是没用的,因为新增或删除元素时,index会发生变化,此时固定的item对应的index就会发生改变。
  2. 绑定item的前提是保证arr数组中的元素是唯一(不重复的)
2. 关于响应布局实时渲染

对于数组,调用push、pop、shift、unshift、splice、sort、reverse等方法,都可以达到响应式布局。

但是arr[2] = "aaa"这种直接通过索引下标修改元素的,Vue是没有监听的,因此不会实时渲染到页面上。

通常解决方式有两种:

  1. arr.splice(2, 1, "aaa");
  2. Vue.set(arr, 2, "aaa"), 即Vue(要修改的对象,要修改的索引值,修改后的值)

v-on:事件绑定

  1. 指令作用为:为元素绑定事件
  2. 事件名不需要写on (v-on:click)
  3. 指令可简写成@(@click)
  4. 绑定的方法定义在methods中
  5. 方法内部可通过this访问定义在data中的属性
  1. 当绑定的方法没有参数时,括号可加可不加。

  2. this中有很多$开头的属性。如$event表示原生dom的事件。this.$el = document.getElementById("app")

  3. 使用@click后想要获取点击本事件本体,有两种方式:

    ​ a. this.$refs.ref可获得el绑定的所有带有ref属性的标签,再通过下标即可获取对应元素。

    ​ b. 通过event.currentTarget获取本身标签元素this

事例:绑定methods中的方法名

<div id="example-2">
  <!-- `greet` 是在下面定义的方法名,不写括号时,默认方法的`第一个参数`可接收原生event事件-->
  <button v-on:click="greet">Greet</button>
</div>
<script>
    var example2 = new Vue({
      el: '#example-2',
      data: {
        name: 'Vue.js'
      },
      // 在 `methods` 对象中定义方法
      methods: {
        greet: function (event) {
          // `this` 在方法里指向当前 Vue 实例
          alert('Hello ' + this.name + '!')
          // `event` 是原生 DOM 事件
          if (event) {
            alert(event.target.tagName)
          }
        }
      }
    })
</script>

事件数量:

Dom有的事件Vue基本都有,总的事件如图:

v-on事件

修饰符

修饰符是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault(),即阻止元素的默认行为。event.stopPropagation()阻止事件冒泡等。常用的修饰符有以下几种:

  • .stop - 阻止冒泡
  • .prevent - 阻止默认事件
  • .capture - 阻止捕获
  • .self - 只监听触发该元素的事件
  • .once - 只触发一次
  • .left - 左键事件
  • .right - 右键事件
  • .middle - 中间滚轮事件
事例
<!-- 阻止单击事件继续传播 -->
<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>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<!-- 这个 .passive 修饰符尤其能够提升移动端的性能 -->
<div v-on:scroll.passive="onScroll">...</div>

注:

  1. 使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
  2. 不要把 .passive.prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。

v-bind:动态绑定

v-bind 被用来响应地更新 HTML 属性,可简写为一个冒号:

例子:

1. 用以挂载data中的数据

<div id="app">
    <pre>
    	<a v-bind:href="url">菜鸟教程</a>
    </pre>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    url: 'http://www.runoob.com'
  }
})
</script>

注意:插值表达式无法直接赋值标签的属性值。

2. 切换class属性

可根据布尔值来确定是否拼接class属性

固定的class可直接在前面定义,即 class="class1",因为:class是拼接上去的,不会覆盖原来

a. 传入数组,根据布尔值进行是否拼接class属性
<html>
	<head>
		<meta charset="utf-8">
		<title>v-bind</title>
	</head>
	<body>
		<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>

		<div id="app">
			<div v-bind:class="{'active': isActive,'line':isLine}">
				v-bind:class 指令
			</div>
		</div>

		<script>
			new Vue({
				el: '#app',
				data: {
					isActive: true,
					isLine:false
				}
			});
		</script>
	</body>
</html>

效果如图:image-20210828153643637

上述例子等价于:

<html>
	<head>
		<meta charset="utf-8">
		<title>v-bind</title>
	</head>
	<body>
		<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>

		<div id="app">
            <!-- 也可以指向method或computed -->
			<div v-bind:class="getClasses()">
				v-bind:class 指令
			</div>
		</div>

		<script>
			new Vue({
				el: '#app',
				data: {
					isActive: true,
					isLine: false
				},
				methods: {
					getClasses: function() {
						return {
							'active': this.isActive,
							'line': this.isLine
						};
					}
				}
			});
		</script>
	</body>
</html>
b. 与v-model结合使用,达到切换class效果
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
</head>
<style>
    .class1{
      background: #444;
      color: #eee;
    }
</style>
<body>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>

<div id="app">
  <label for="r1">修改颜色</label><input type="checkbox" v-model="use" id="r1">
  <br><br>
  <div v-bind:class="{'class1': use}">
    v-bind:class 指令
  </div>
</div>
    
<script>
new Vue({
    el: '#app',
  data:{
      use: false
  }
});
</script>
</body>
</html>

效果: image-20210828160321623,点击选择框,下方div背景会跟随选择框的状态改变

c. 传入数组,批量绑定属性
<div id="app">
    <div class="test1" v-bind:class="[test2, test3]">
        v-bind:class 指令
    </div>
</div>

<script>
    new Vue({
        el: '#app',
        data: {
            test2: "class2",
            test3: "class3"
        }
    });
</script>

结果:image-20210828161501270

3. 动态绑定style

实例:

<div id="app">
    <!-- 格式: :style="key(属性名): value(属性值)" -->
    <h2 :style="{fontSize: '5px'}">动态绑定style</h2>
    <h2 :style="{fontSize: finalSize+'px'}">动态绑定style</h2>
</div>

<script>
    var app = new Vue({
        el: '#app',
        data: {
            finalSize: 5
        }
    });
</script>

结果:image-20210828191248217

同上,也可以传入数组,进行批量设值

v-model:数据双向绑定

用来在 inputselecttextareacheckboxradio等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值

input示例

<div id="app">
    <p>{{ message }}</p>
    <input v-model="message">
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    message: 'Runoob!'
  }
})
</script>

radio示例

<div id="app">
    <label>
        <input type="radio" value="男" v-model="sex" />男
    </label>
    <label>
        <input type="radio" value="女" v-model="sex" />女
    </label><br>
    性别是: {{ sex }}
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            sex: "女"
        }
    })
</script>

结果:image-20210912212708461

checked示例

<div id="app">
    <input type="checkbox" value="篮球" v-model="hobbies">篮球
    <input type="checkbox" value="足球" v-model="hobbies">足球
    <input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
    <input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
    <p>你的爱好有:{{ hobbies}}</p>
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            hobbies:['篮球', '足球']
        }
    })
</script>

结果:image-20210912213253887

值绑定

示例:

<div id="app">
    <label v-for="item in hobbies" :for="item">
        <input type="checkbox" :id="item" :value="item" v-model="myHobbies" />
    </label>
    <p>你的爱好有:{{ myHobbies}}</p>
</div>
<script>
    new Vue({
        el: "#app",
        data: {
            hobbies: ['篮球', '足球', '乒乓球', '羽毛球', '保龄球', '高尔夫球'],
            myHobbies:[]
        }
    })
</script>

结果:image-20210912215625508

修饰符

.lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步。你可以添加 lazy 修饰符,从而转为在 change 事件_之后_进行同步:

<!-- 
	在“change”时而非“input”时更新 
	即敲下回车或表单失焦后触发,而不是敲一下触发一次事件,降低事件使用频率
-->
<input v-model.lazy="msg">
.number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符(用户只能输入数字类型):

<input v-model.number="age" type="number">

这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。

.trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">

过滤器

用作一些常见的文本的格式化,并且多个过滤器可串联{{ message | filterA | filterB }}

格式:

<!-- 在两个大括号中 -->
{{ message | capitalize }}

<!-- 在 v-bind 指令中 -->
<div v-bind:id="rawId | formatId"></div>

示例

插值表达式以及class属性的过滤(首个字母大写):

<!-- 过滤器 -->
<div id="app9">
	<div v-bind:class="clsTest|clsTest">
		{{message | toLowerCase}}
	</div>
</div>

<script type="text/javascript">
			new Vue({
				el:"#app9",
				data:{
					message:"runoob!",
					clsTest:"class"
				},
				filters:{
					toLowerCase:function(value){
						return value.charAt(0).toUpperCase() + value.substr(1)
					},
					clsTest:function(cls){
						return cls+"1";
					}
				}
			})
</script>

结果为:

<div class="class1">Runoob</div>

过滤器是 JavaScript 函数,因此可以接受参数:

{{ message | filterA('arg1', arg2) }}

Vue的生命周期

整体流程图:

文字解析

Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。

Vue一共有8中生命周期(4种状态的前后):分别为:

  • 创建Vue实例前 ---beforeCreate
  • 创建Vue实例后 ---created
  • 挂载前 ---beforeMount
  • 挂载后 ---mounted
  • 数据更新前 ---beforeUpdate
  • 数据更新后 ---update
  • 实例销毁前 ---beforeDestroy
  • 实例销毁后 ---destroyed

其中,数据的渲染在mounted阶段渲染完毕。

每个过程对应一个事件钩子(callHook()),对应一个函数的执行,如 挂载前会执行beforeMount()方法

实例解析

<div id="app11">
    {{ message }}
</div>

<script type="text/javascript">
    var app = new Vue({
        el: "#app11",
        data: {
            message: "Hello Vue.js!"
        },
        beforeCreate: function () {
            showDate("vue实例创建前",this);
        },
        created: function () {
            showDate("vue实例创建后",this);
        },
        beforeMount: function () {
            showDate("挂载前",this);
        },
        mounted: function () {
            showDate("挂载后",this);
        },
        beforeUpdate: function () {
            showDate("数据更新前",this);
        },
        updated: function () {
            showDate("数据更新后",this);
        },
        beforeDestroy: function () {
            showDate("vue实例销毁前",this);
        },
        destroyed: function () {
            showDate("vue实例销毁后",this);
        }
    });

    /** 展示真实dom结构 **/
    function realDom() {
        console.log("真实dom结构:"+document.getElementById("app11").innerHTML)
    }

    /** 展示data的数据以及将要挂载的对象 **/
    function showDate(process,obj) {
        console.log(process);
        console.log("data数据:"+obj.message);
        console.log("挂载的对象:");
        console.log(obj.$el);
        realDom();
        console.log('-------------------')
        console.log('-------------------')
    }
    app.message="good"
    app.$destroy();
</script>

结果:

  1. 当app.message="good"作用,app.$destroy()注释时:
vue实例创建前
data数据:undefined
挂载的对象:
	undefined
真实dom结构:
    {{ message }}
-------------------
-------------------
vue实例创建后
data数据:Hello Vue.js!
挂载的对象:
	undefined
真实dom结构:
    {{ message }}
-------------------
-------------------
挂载前
data数据:Hello Vue.js!
挂载的对象:
	<div id="app11"> {{ message }} </div>
真实dom结构:
    {{ message }}
-------------------
-------------------
挂载后
data数据:Hello Vue.js!
挂载的对象:
	<div id="app11"> good </div>
真实dom结构:
    Hello Vue.js!
<!-- 注:此时这里挂载对象为“good”是因为message已经被赋值,但挂载上去的仍是“Hello Vue.js!”,原因是因为赋值一定会有一个值的变化的过程,而这个过程在Vue中发生在Mounted阶段,即挂载后 -->
-------------------
-------------------
数据更新前
data数据:good
挂载的对象:
	<div id="app11"> good </div>
真实dom结构:
    Hello Vue.js!
-------------------
-------------------
数据更新后
data数据:good
挂载的对象:
	<div id="app11"> good </div>
真实dom结构:
    good
-------------------
-------------------
  1. 当app.$destroy()作用,app.message="good"注释时:
vue实例创建前
data数据:undefined
挂载的对象:
	undefined
真实dom结构:
    {{ message }}
-------------------
-------------------
vue实例创建后
data数据:Hello Vue.js!
挂载的对象:
	undefined
真实dom结构:
    {{ message }}
-------------------
-------------------
挂载前
data数据:Hello Vue.js!
挂载的对象:
	<div id="app11"> {{ message }} </div>
真实dom结构:
    {{ message }}
-------------------
-------------------
挂载后
data数据:Hello Vue.js!
挂载的对象:
	<div id="app11"> Hello Vue.js! </div>
真实dom结构:
    Hello Vue.js!
-------------------
-------------------
vue实例销毁前
data数据:Hello Vue.js!
挂载的对象:
	<div id="app11"> Hello Vue.js! </div>
真实dom结构:
    Hello Vue.js!
-------------------
-------------------
vue实例销毁后
data数据:Hello Vue.js!
挂载的对象:
	<div id="app11"> Hello Vue.js! </div>
真实dom结构:
    Hello Vue.js!
<!-- 之所以销毁前后与之前没变化,是因为destroy只是销毁了vue对象,然而vue已渲染、产生的效果是不会消失的。   -->
-------------------
-------------------

Vue的Ajax异步请求:axios

通用写法格式:

// 发送 POST 请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }.then(function(){...}).catch(function(){...})
});

在axios中的this无法指代vue实例,拿取不到vue中data的数据。因此可在axios定义上方定义类似:

var _this = this; 来获取vue实例对象。然后获得user对象可通过_this.user获取

GET 请求

axios.get('/user?ID=12345')
.then(function (response) {
// then方法等价于success方法,Ajax正确执行后的回调函数
    console.log(response);
})
.catch(function (error) {
// catch方法等价于error方法,Ajax执行出现异常后的回调函数
    console.log(error);
});

// 上面的请求也可以这样做(get的参数必须加params)
axios.get('/user', {
    params: {
        ID: 12345
    }
})
.then(function (response) {
    console.log(response);
})
.catch(function (error) {
    console.log(error);
});

POST 请求

// post请求不需要加params,直接花括号里加Json数据
axios.post(‘/user’, {
firstName: ‘Fred’,
lastName: ‘Flintstone’
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

<!-- =========== 另一种例子 ============== -->
 var app = new Vue({
        el: "#app1",
        data: {
            user: {
                username: "",
                password: ""
            }
        },
        methods: {
            reg: function () {
                var _this = this;
                axios.post('/sshTest/user/save',_this.user).then(function (response) {
                    console.log(response);
                }).catch(function (message) {
                    console.log(message);
                })
            }
        }
    })

多个并发请求

function getUserAccount() {
	return axios.get(‘/user/12345’);
}
function getUserPermissions() {
	return axios.get(‘/user/12345/permissions’);
}
// axios.all()方法里传入Ajax方法的数组
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// 两个请求现在都执行完成
}));

axios(config) 写法

// 发送 POST 请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});

使用 application/x-www-form-urlencoded 请求头

import qs from 'qs';
const data = { 'bar': 123 };
const options = {
  method: 'POST',
  headers: { 'content-type': 'application/x-www-form-urlencoded' },
  data: qs.stringify(data),
  url,
};
axios(options);

Vue组件

入门案例

<div id="app">
        <button-counter></button-counter>
</div>

<script>
    // 定义一个名为 button-counter 的新组件
    Vue.component('button-counter', {
        data() {
            return {
                count: 0
            }
        },
        template: '<button @click="count++">我点击了 {{ count }} 次</button>'
    })
	/* 以上也可以等价于以下
	// 创建组件构造器对象
    const cpn = Vue.extend({
        data() {
            return {
                count: 0
            }
        },
        template: '<button @click="count++">我点击了 {{ count }} 次</button>'
    });
    // 组件注册
    Vue.component('button-counter',cpn)
	*/
    
    new Vue({ el: '#app' })
</script>

效果如图:image-20210902142042895 ,点击按钮则count+1. 且在页面上是看不到button绑定的事件的。

注意:

  1. 例中的 ”button-counter" 即为组件名,自定义组件名遵循规则 (字母全小写且必须包含一个连字符)。这会避免和当前以及未来的 HTML 元素相冲突。
  2. 组件是可以重复使用的,组件内的data相互独立互不影响
  3. data必须是个函数,而不能是对象(Vue的规定,以此来保证每个组件的data独立)
  4. data函数的返回是个对象(即要{}包裹)

组件名

定义组件名的方式有两种:

使用 kebab-case(推荐)
Vue.component('my-component-name', { /* ... */ })

当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如 <my-component-name>

使用 PascalCase
Vue.component('MyComponentName', { /* ... */ })

当使用 PascalCase (首字母大写命名) 定义一个组件时,在JS中引用两种命名法都可以使用。但是,HTML不区分大小写,因此只能使用 kebab-case

模板分离

在实际开发中,直接写template='<div>...</div>'一个是可读性不好,一个是没有复用性,还有就是在写字符串时许多开发工具并不会语法提示。因此就有了模板分离:将HTML字符串用特殊标签包住后,直接写在其内部,引用其id给template即可直接使用

<div id="app">
    <my-button></my-button>
</div>


<template id="test">
	<div>
        <input type='text'>
        <input type='button' value="模板">
        <input type='checkbox' checked="checked">模板显示
    </div>
</template>

<script>
    Vue.component('MyButton', {
        // 引用其模板的id
        template: test
    })

    new Vue({el: '#app'})
</script>

效果:image-20210916100200823

  1. 可用<template></template>与<script type="text/x-template" id="test"></script>进行包裹,效果一样,更推荐前者
  2. 模板不用写在Vue实例所挂载的内部

组件注册

全局注册

Vue.component('my-component-name', {
  // ... 选项 ...
})

这些组件是全局注册的。它们在注册之后可以用在任何新创建的 Vue 实例 (new Vue) 的模板中。比如:

Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })

new Vue({ el: '#app' })
<div id="app">
  <component-a></component-a>
  <component-b></component-b>
  <component-c></component-c>
</div>

在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以相互使用。

局部注册

全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。

在这些情况下,可以通过一个普通的 JavaScript 对象来定义组件。然后在 Vue实例内用components 选项中定义你想要使用的组件:


<div id="app">
    <!-- 标签名不区分大小写,因此必须采用kebab-case风格 -->
    <component-a></component-a>
</div>

<script>
    // JS定义对象
    var ComponentA = {
        data() {
            return {
                count: 0
            }
        },
        template: '<button @click="count++">我点击了 {{ count }} 次</button>'
    }

    new Vue({
        el: '#app',
        components: {
            // 等价于 "CompunentA" : ComponentA
            // 注意,全局注册的组件不能写在这,会报ComponentA is not define
            ComponentA
        }
    })
</script>

​ 对于 components 对象中的每个 property 来说,其 property 名就是自定义元素的名字,其 property 值就是这个组件的选项对象。

注意局部注册的组件在其子组件中不可用。例如,如果希望 ComponentAComponentB 中可用,则需要这样写:

var ComponentA = { /* ... */ }

var ComponentB = {
  components: {
    'component-a': ComponentA
  },
  // ...
}

或者如果你通过 Babel 和 webpack 使用 ES6 模块,那么代码看起来更像:

import ComponentA from './ComponentA.vue'

export default {
  components: {
    ComponentA
  },
  // ...
}

在 ES6 中,在对象中放一个类似 ComponentA 的变量名其实是 ComponentA: ComponentA 的缩写,即这个变量名同时是:

  • 用在模板中的自定义元素的名称
  • 包含了这个组件选项的变量名

组件通讯--父传子

Props

写在props中的值,接收数据通过组件上的同名属性接收,使用方式与在data中无异(因此可使用v-bind进行数据绑定)。

示例:

<div id="app">
    <!-- 
        posts为Vue实例中data的数据
        v-bind:post,:arr绑定post,arr属性值,通过props传入。使用时与从data中取值无异
        在组件上使用vue实例的方法要加.native,声明是本页面的方法,否则只能调用Vue中的方法
	-->
    <component-a v-for="post in posts" @click.native="vueFun(post.id)" :post="post" :arr="[1,2,3,4,5]"></component-a>
</div>


<script>
    var ComponentA = {
        data() {
            return {
                count: 0
            }
        },
        props: ["post","arr"],
        template:
        '<ul><li @click.stop="componentFun(post.title)" v-for="(item, index) in arr">{{item}} {{post.title}} {{index + count}}</li></ul>',
        methods: {
            componentFun(obj) {
                console.log(obj)
            }
        }
    }

    new Vue({
			el: '#app',
			components: {
				ComponentA
			},
			data: {
				posts: [
						{ id: 1, title: 'My journey with Vue' },
						{ id: 2, title: 'Blogging with Vue' },
						{ id: 3, title: 'Why Vue is so fun' }
					]
			},
			methods: {
				vueFun(obj) {
					showData(obj)
				}
			}
		})
    
		function showData(obj) {
			console.log(obj)
		}
</script>

结果:image-20210913161233337,点击后

data与props用法相似,组件私有的数据、可读可写的写在data中。只读不可写的写在props中

Props的命名
  1. 由于HTML是大小写不敏感的,使用标签的属性统一采用小写。
  2. 对于Props中驼峰命名的数据,需要使用其等价的 kebab-case (短横线分隔命名)

建议:在JS中采用驼峰命名,属性使用kebab-case(短横线分隔命名)

Props的类型限定

简略写法(数组):

props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

限定类型写法(对象):

props: {
    title: String,
    likes: Number,
    isPublished: Boolean,
    commentIds: Array,
    author: Object,
    callback: Function,
    contactsPromise: Promise // or any other constructor
}

使用错误类型时页面不会报错,但控制台会报错。

注意[]{}。简略写法必须加引号'',限定类型写法可省略。

Props语法
Vue.component('my-component', {
  props: {
    // 基础的类型检查
    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
      }
    }
  }
})

nullundefined 会通过任何类型验证

Props类型检查的种类

type 可以是下列原生构造函数中的一个:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

额外的,type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认。例如,给定下列现成的构造函数:

function Person (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

你可以使用:

Vue.component('blog-post', {
  props: {
    author: Person
  }
})

来验证 author prop 的值是否是通过 new Person 创建。

Props的单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。

每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告(Avoid mutating a prop directly since the value will be overwritten whenever the parent component)。

在两种情况下,我们很容易忍不住想要去修改prop中数据:

  1. Prop作为初始值传入后,子组件想把它当作局部数据来用;
  2. Prop作为原始数据传入,由子组件处理成其它数据输出。

对这两种情况,正确的应对方式是:

  1. 在子组件中定义一个局部变量,并用prop的值初始化它:
props: ['initialCounter'],
data: function () {
  // this可调用props中的属性值
  return { counter: this.initialCounter }
}
  1. 定义一个计算属性,处理prop的值并返回:
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

在JavaScript中对象和数组是引用类型,指向同一个内存空间,如果prop是一个对象或数组,在子组件内部改变它会影响父组件的状态

非Props的Attribute属性

当在组件上写有Attribute属性,却在props中没有进行定义(接收数据),即为非Props的Attribute属性

因为显式定义的 prop 适用于向一个子组件传入信息,然而组件库的作者并不总能预见组件会被用于怎样的场景。这也是为什么组件可以接受任意的 attribute,而这些 attribute 会被添加到这个组件的根元素上。

实例:

<div id="app">
    <component-b test type="text" class="componentClass" style="margin-left: 50px;">		</component-b>
</div>

<script>
    var ComponentB = {
        template:'<input type="date" class="myClass" style="width : 500px">'
    }
</script>

结果:image-20210908110658651

  1. test可以是任意属性,可以是hiden、disabled等具有实际意义属性,或是test这种自定义属性。
  2. 当组件与模板根元素有相同属性type时,组件的属性值会覆盖模板根元素的属性值。
  3. classstyle比较特殊,不会覆盖原来的属性值,而是拼接上去。
  4. 如果不想让组件上的属性影响到模板根元素,可设置禁用Attribute继承

禁用Attribute继承

不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false

<div id="app">
    <component-a type='text' ></component-a>
</div>

<script>
	var ComponentA = {
        template:'<input class="myClass" type="date" style="width : 500px">',
        // 不继承任何组件上传来的属性
        inheritAttrs: false
    }
</script>

结果:image-20210913114645142

  1. type=date不会被type=text覆盖
  2. inheritAttrs对class与style无效,仍然会继续拼接

$attrs

作用:在指定元素位置传递attribute属性。

使用v-bind='$attrs'非Props的Attribute属性绑定到指定的元素上,而不是只能绑定在根元素上

父组件想传值给曾子组件时,如果使用inheritAttrs,子组件会有很多无关的属性在组件上。此时禁用子组件的Attribute继承后,使用$attrs子组件上使用即可达到传值效果。

inheritAttrs是会覆盖相同的属性值的,但$attrs是不会覆盖的(value会覆盖?) -lyx todo

<body>
    <div id="app">
        <component-a type='submit' width="20px"></component-a>
    </div>

    <script>
        var ComponentB = {
            template: `
			<div>
<!-- 在此位置使用type='submit',width因为已经有了,$attrs无法覆盖原属性值,因此width仍然是30 -->
				<input width="30px" v-bind="$attrs" value="测试">
     	   </div>
			`,
            inheritAttrs: false
        }

        var ComponentA = {
            components:{
                ComponentB
            },
            template: `
				<div>
					<input class="myClass" type="date" style="width : 500px">
					<!-- 在组件上使用v-bind='$attrs' -->
					<component-b v-bind="$attrs"></component-b>
 		       </div>`,
            // 不继承任何组件上传来的属性
            inheritAttrs: false
        }

        new Vue({el:"#app",components:{ComponentA}})
    </script>

结果:image-20210913150232454

$listener:事件监听器

<div id="app">
    A{{msg}}
    <my-button :msg="msg" @todo="handleClick"></my-button>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            msg: '100'
        },
        methods: {
            handleClick() {
                console.log('点击事件')
            }
        },
        components: {
            'MyButton': {
                // 这里的v-on='$listeners' 与 v-bind='$attrs'异曲同工
                template: `<div>B<my-radio v-on="$listeners"></my-radio></div>`,
                components: {
                    'MyRadio': {
                        template: `<div @click="$listeners.todo">C</div>`,
                    }
                },
                created() {
                    console.log(this.$listeners)
                }
            },
        }
    })
</script>

注:

  1. 爷孙组件传递规则,同$attrs传递规则一样,逐级传递

  2. v-bind='$attrs'一样,直接使用v-on='$listeners'也可以把所有原生事件附加在指定氧元素上。自定义事件由于不知道处罚方式(event = click or blur 等),所以无法触发。

  3. 触发指定指定的事件有两种情况:

    <my-button @todo="handleClick" @click='clickFun'></my-button>
    
    1. 触发原生事件:此时要在附加的元素上添加@click="$listeners.click"即可添加事件

      注意:click为触发的事件event

    2. 触发自定义事件:@click="$listeners.todo",@event='$listeners.点击事件名'

组件通讯--子传父

$emit

格式:$emit.("父组件方法名", 传给父组件的值)

作用:可触发指定父组件的方法,父组件会监听子组件,而触发条件就是$emit

我们开发一个子组件,有时他需要与父级组件沟通,如:通知父组件数据变化、根据子组件给父组件赋值等。

示例:

<div id="app">
    <div :style="{ fontSize: finalSize + 'em' }">
        <!-- 自定义事件 -->
        <component-a @sendvalue='getChildValue' @big='finalSize += 0.5' :key="post.id" :post="post" :finalSize="finalSize"></component-a>
        <div>
            {{childValue}}
        </div>
    </div>
</div>

<script>
    var ComponentB = {
        data() {
            return {
                son: -1
            }
        },
        template: `
            <div>
                <button @click='sendToChild(1)'>点击传值1</button>
            </div>
			`,
        methods: {
            sendToChild(value) {
                this.$emit('sendParent', value)
            }
        }
    }

    var ComponentA = {
        props: ['post'],
        components: {
            ComponentB
        },
        template: `
            <div class="blog-post">
           		<button @click="$emit('big')">
           			{{post.name1}}
                </button>
                <component-b @sendParent='sendPatent'></component-b>
                <div v-html="post.content"></div>
             </div>
            `,
        methods: {
            sendPatent(value) {
                this.$emit('sendvalue', value)
            }
        }
    }

    new Vue({
        el: "#app",
        components: {
            ComponentA
        },
        data: {
            post: {
                id: 1,
                name1: "文本变大",
                name2: "子组件传递值给父组件",
                age: 13,
                content: "测试文本"
            },
            obj: "test",
            finalSize: 1,
            childValue: 0
        },
        methods: {
            getChildValue(value) {
                this.childValue = value;
            }
        }
    })
</script>

点击之前: image-20210913150647153 点击之后:image-20210913150547021

自定义事件名

不同于组件和 prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。例如:我们监听这个事件

this.$emit('myEvent')

则监听这个名字的 kebab-case 版本是不会有任何效果的:

<!-- 没有效果 -->
<my-component v-on:my-event="doSomething"></my-component>

并且 v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。

因此,推荐始终使用 kebab-case 的事件名

父子组件的双向数据绑定

表单

有时候需要在组件上使用v-model,输入修改子组件的表单input的同时修改父组件的data值。

示例:

<div id="app">
    <my-input v-model="message"></my-input>{{message}}
</div>

<script>
    var MyInput = {
        props:["value"],
        template:`
	<div>
		<input :value="value" @input="$emit('input',$event.target.value)">
    </div>
	`,
        inheritAttrs:false
    }
    new Vue({
        el:"#app",
        components:{
            MyInput
        },
        data:{
            message: "测试文本"
        }
    })

原理:

v-model本质上可分解为v-bind:value以及@input事件,在组件上使用v-model没有什么差别,差别在于:使用子组件时,@input事件不再是默认的改变自己this.value = $event.target.value,而是修改调用父类的input方法,并将$event.target.value赋值给message

自定义组件的v-model -lyx? todo

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:

不写:checked='select' 没有初始化,且子组件的checked值始终为false,即使复选框被选中也为false

<div id="app2">
    {{select}}
    <my-checkbox v-model='select' :checked='select'></my-checkbox>
    <input type="checkbox" v-model="select" />
</div>
<script>
var MyCheckbox = {
    model: {
        props: "checked",
        event: "change"
    },
    props: {
        checked: Boolean
    },
    template: `
<div><input type='checkbox' :checked='checked' @change='$emit("change",$event.target.checked)'>111</div>
`,
    inheritAttrs: false
}

new Vue({
    el: "#app2",
    data: {
        select: true
    },
    components: {
        MyCheckbox
    },
    inheritAttrs: true
})
</script>
.sync修饰符

在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。但是,子组件不能直接修改父组件的值,通过子父组件的数据双向绑定也可以达到效果,但是十分繁琐,在Vue中提供了.sync的语法糖 -lyx todo

<div id="app">
    <div style="width: 309px;height: 200px;border: 1px solid black;">
        <my-button :child-show.sync="show" v-show="show"></my-button>
    </div>
</div>

<script>
    let vm = new Vue({
            el: '#app',
            data: {
                show: true
            },
            components: {
                'MyButton': {
                props: ["child-show"],
                template: 
                `<div>
					<button @click.stop='$emit("update:child-show",false)'>
                        test
                    </button>
    			</div>`
                }
            }
        })
</script>

当我们用一个对象同时设置多个prop双向绑定的时候,也可以将这个 .sync 修饰符和 v-bind 配合使用:

<my-button v-bind.sync="doc"></my-button>

插槽

插槽,也就是slot,是组件的一块HTML模板。这块模板显示不显示、以及怎样显示由父组件来决定;但是插槽显示的位置却由子组件自身决定

实际上,插槽最核心的两个问题在这里就点出来了,是显示不显示怎样显示

具名插槽:带名字的插槽

实例:

<div id="app">
    <my-button>
        <!-- 等价于<template #parents> -->
        <template v-slot:parents>
			<div>
    			<input value='我是父类的插槽内容'>
            </div>
        </template>
    </my-button>
</div>

<template id="temp1">
	<div>
        <div class="child">
            我是子类的插槽内容
        </div>
        <slot name='parents'></slot>
    </div>
</template>

<script>
    Vue.component('MyButton', {
        template: temp1
    })

    new Vue({el: '#app'})
</script>

效果:image-20210916155749787

  1. 当组件渲染的时候,<slot></slot> 将会直接被替换为组件双标签内的内容。插槽内可以包含任何模板代码,包括 HTML,甚至可以是其他的组件。

  2. <slot></slot>标签无name属性时,默认name='default',会渲染出组件起始标签内的所有未设置v-sloat的节点以及v-sloat:default的节点。这类插槽也称作:默认插槽/匿名插槽/单个插槽

  3. v-onv-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #

    例如 v-slot:header 可以被重写为 #header

    注:采用#缩写时,default不可省略

  4. 除了v-slot:header#header外,还有slot='header'也是一样的,但是已被废弃,了解即可

作用域插槽:带数据的插槽

作用域插槽,与前一种插槽相比,区别就是携带数据

<!-- 匿名插槽 -->
<slot></slot>
<!-- 具名插槽 -->
<slot name="up"></slot>
<!-- 作用域插槽 -->
<slot name="up" :data="data"></slot>

实例:

...待更新中

后备内容

有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。

<div id="app">
    <my-button>
        <template #slot1>
            <div>
                <input class='parent' value='我是父类的插槽内容1'>
            </div>
		</template>
        <template>
            <div>
                <input class='parent' value='我是父类的插槽内容2'>
            </div>
		</template>
    </my-button>
</div>

<template id="temp1">
	<div>
        <slot name='slot1'>一个slot='sloat1'的插槽都没有</slot>
    	<slot name='slot2'>一个slot='sloat2'的插槽都没有</slot>
    </div>
</template>

<script>
    Vue.component('MyButton', {
        template: temp1
    })

    new Vue({el: '#app'})
</script>

结果:image-20210916142141990

注:后备内容也同样支持HTML语法

后续内容待更新。。。

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/catcode/p/15307235.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!