Vue.js高效前端开发

 一、初始Vue.js

1.Vue概述

1.1.MVVM模式

MVVM设计模式是由Model(模型)、View(视图)和ViewModel(视图模型)三部分组成,是MVC设计模式的进化版,即Controller转变为ViewModel,这种模式可以使View的变化自动更新到ViewModel,而 ViewModel的变化也会自动同步到View上显示

1.2.安装Vue

<script src="js/vue.min.js"></script>

2.Vue的使用

v-model指令双向绑定

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>初始Vue</title>
	</head>
	<body>
		<!-- mvvm -->
		<!-- 1.创建视图 -->
		<div id="app">
			Vue绑定数据
			{{message}}
			<!-- {{test}} -->
			<!-- 双向绑定 -->
			<input type="text" v-model="message"/>
		</div>
		<!-- 2.导入Vue -->
		<script src="js/vue.min.js"></script>
		<!-- 3.创建模型 -->
		<!-- 4.创建vm -->
		<script>
			var tmp={
				message:"hello world"
			}
			var vm=new Vue({
				el:'#app',
				data:tmp
				// data:{test:"玛卡巴卡"}
			})
		</script>
	</body>
</html>

3.Vue生命周期

3.1.Vue的实例和数据 

Vue应用的创建

var vm=new Vue({

               //选项参数

});

Vue的常用选项参数
选项参数说明
el提供一个在页面上已经存在的DOM元素作为Vue实例的挂在目标
dataVue实例的数据对象。Vue会递归地将data的属性转换为getter/setter,从而让data的属性能够响应数据变化
methodsVue实例的方法集合,可以在Vue直接调用或将方法绑定到DOM元素的事件上
computedVue实例的计算属性集合
watch观察Vue实例变化的一个表达式或计算属性函数
components包含Vue实例可用组件的哈希表
filters包含Vue实例可用的过滤器的哈希表
template定义字符串模板作为Vue实例的标识使用

必不可少的选项参数是el 。el用于指定一个页面中已存在的DOM元素来挂载Vue实例,它可以是HTMLElement,也可以是CSS选择器

当挂在成功后,就可以通过vm.$el来访问元素

在Vue实例内部访问data中的数据时,一般使用“this.数据”的方式

3.2.Vue生命周期函数的使用

Vue实例从创建到销毁的过程就是它的生命周期

在Vue中每一个组件或者实例都会经历一个完整的生命周期,总共分为3个阶段:初始化、运行中和销毁阶段

3.2.1.Vue实例的生命周期过程具体说明

A.实例、组件通过new Vue()创建出来之后会初始化事件和生命周期,然后执行beforeCreate()函数,这个时候数据并没有挂载,只是一个空壳,无法访问到数据和真实DOM

B.挂载数据、绑定事件等,然后执行created()函数,这个时候已经可以使用到数据,也可以更改数据。在这里更改数据并不会触发updated()函数,在渲染前有倒数第二次更改数据的机会,这样不会触发其他的钩子函数,一般可以在这里做初始数据的获取

C.接下来开始找实例或者组件对应的模板,编译模板为虚拟DOM放入render()函数中准备渲染,然后执行beforeMount()函数,在这个函数中虚拟DOM已经创建完成,马上就要渲染,此处也可以更改数据,不会触发updated()函数,这里在渲染前有最后一次更改数据的机会,不会触发其他的钩子函数,所以一般可以在这里做初始数据的获取

D.接下来开始渲染,渲染出真实DOM,然后执行mounted()函数,此时,组件已经出现在页面中,数据、真实的DOM都已经处理好了,事件也已经挂载好了,可以在这里操作真实DOM事件

E.当组件或实例的数据更改之后,会立即执行beforeUpdated()函数,然后Vue的虚拟DOM机制会重新构建虚拟DOM并与上一次的虚拟DOM利用diff算法进行对比之后重新渲染。

F.当更新完成后,执行updated()函数,数据已经更改完成,DOM也重新渲染完成,可以操作更新后的虚拟DOM

G.当经过某种途径调用$destroy()方法后,立即执行beforeDestroy()函数,一般在这里做一些善后工作,如清除计时器和非指令绑定的事件等

H.最后将组件或实例的数据绑定、监听等去掉后只剩下DOM空客,这时也可以执行destroy()函数,在这里做善后工作

3.2.2.Vue实例的生命周期中钩子函数
created()函数实例建立完成后调用。此阶段完成了数据的观测等,但尚未挂载,$el此时还不可用,需要初始化处理一些数据时会比较有用
mounted()函数el挂载到实例上之后调用。一般开发者的第一个业务逻辑会在这里开始,也可以在此处调用AJAX来获取服务端的数据
beforeDestroy()函数实例被销毁之前调用。主要解绑一些使用addEventListener监听的事件等。

例:使用生命周期钩子函数实现定时显示Banner文本功能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>钩子函数生命周期——文字轮播</title>
    <style>
        #app{
            text-align: center;
            padding: 10px;
        }
        #banner{
            background-color: blue;
            width: 600px;
            height: 150px;
            line-height: 150px;
            font-size: 40px;
            color: white;
            border-radius: 50px;
            margin: 50px auto;
        }
    </style>
</head>
<body>
    <!-- 1.创建view -->
    <div id="app">
        <div id="banner">
            {{adTitle}}
        </div>
    </div>
    <!-- 2.导入Vue -->
    <script src="js/vue.min.js"></script>
    <!-- 3.创建模型 -->
    <!-- 4.创建vm -->
    <script>
        var bannerAd={
            ads:[],
            adTitle:"",
            index:0
        }
        var vm=new Vue({
            el:"#app",
            data:bannerAd,
            //在create函数初始化模型
            created:function(){
                this.ads=["地球交通委提醒你","道路千万条","安全第一条","行车不规范","亲人两行泪"];
                this.adTitle=this.ads[0];
            },
            //添加定时器
            mounted:function() {
                //声明一个变量指向Vue实例,保证作用域一致
                var _this=this;//保留this外层指向,有回调函数
                this.timer=setInterval(function(){
                    if(_this.index<_this.ads.length-1){
                        _this.index++;
                    }else{
                        _this.index=0;
                    }
                    //修改Banner中的内容
                    _this.adTitle=_this.ads[_this.index];
                },3000)
            },
            beforeDestroy:function() {
                if(this.timer){
                    //在Vue实例被销毁前,清除定时器
                    clearInterval(this.timer);
                }
            },
        })
    </script>
</body>
</html>

4.Vue过滤器

Vue.js支持在{{}}的插值的尾部添加一个管道符(“|”)对数据进行过滤,经常用于格式化文本,比如字母全部大写、货币千位使用逗号分隔等

4.1.全局创建过滤器

可在整个页面中使用

Vue.filter("过滤器名",function(val){

        过滤语句

});

4.2.局部创建过滤器

在当前Vue实例的作用域中有效

var vm=new Vue({

        el:"#app",

        filter:{

                过滤器名:function(val){

                        过滤语句

                }

        }

});

在HTML中使用过滤器的语法如下

<div id="app">

        {{模型数据|过滤器}}

</div> 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>过滤器</title>
</head>
<body>
    <!-- 1.创建视图 -->
    <div id="app">
        {{message|Transformessage}}
    </div>
    <!-- 2.引入Vue -->
    <script src="js/vue.min.js"></script>
    <!-- 3.创建model -->
    <script>
        var tmp={
            message:"action louder than speak"
        }
        var vm=new Vue({
            el:"#app",
            data:tmp,
            filters:{
                Transformessage:function(val){
                    //分割成数组
                   var str= val.split(' ');
                   var rs=" ";
                    //循环数组
                    for(var i=0;i<str.length;i++){
                        //把每一个首字母变成大写
                        //拼接后面的单词
                        rs+=str[i].charAt(0).toUpperCase()+str[i].substring(1)+' '
                    }
                    return rs;
                }
            }
        })
    </script>
    <!-- 4.创建vm -->
</body>
</html>

二、Vue基本指令

1.Vue模板语法

1.1.文本插值

文本插值是最基本的形式,使用双大括号(Mustache 语法){{}}表示

<span>Text:{{text}}</span>

上述代码中的标签{{text}}将会被相应的数据对象text属性的值替换掉,当text的值改变时,文本中的值也会联动地发生变化。有时候只需渲染一次数据,后续数据变化不在关心,可以通过v-once指令来实现 

<span v-once>Text:{{text}}</span>

文本插值还可以使用v-text指令来代替双大括号

 <span v-text="text"></span>

双大括号和v-text指令会把匿名的值全部当作字符串来处理,如果值是HTML片段,双大括号和v-text指令会将“<>”转义为“&lt; &gt;”,如需要解析HTML内容,则使用v-html指令来绑定

注意:在网站上动态渲染任意HTML是非常危险的,因为容易导致XSS攻击。只在可信内容上使用v-html,永远不要用来在用户提交的内容上 

说明:XSS攻击全称为跨站脚本攻击,XSS是一种在Web应用中的计算机安全漏洞,它允许恶意Web用户将代码植入到提供给其他用户使用的页面中

1.2.表达式

文本插值也接受表达式的形式,表达式由JavaScript表达式和过滤器构成,其中过滤器可以没有,也可以有多个

表达式时各种数值、变量和运算符的综合体。简单的表达式可以是常量或者变量名称。表达式的值使其运算结果

??可以为空

Vue只接受单个表达式,不支持语句和控制流

在表达式中,不能使用用户自定义的全局变量,只能使用JavaScript的全局变量,如Math和Date

1.3.指令

指令是Vue.js中一个重要的特性,主要提供了一种机制数据的变化映射为DOM行为

指令是带有v-前缀的特殊属性

指令属性的值预期时单一的JavaScript表达式(除了v-for)

指令的职责就是当起表达式的值改变时相应的将某些行为应用到DOM上

1.3.1.指令中的参数

一些指令能接受一个参数,在指令后以冒号指明

例如,v-bind指令被用来更新HTML属性

<a v-bind:href="url"></a>

在这里href是参数,告知v-bind指令将该元素的href属性与表达式url的值绑定

另一个例子是v-on指令,它用于监听DOM事件

 <a v-on:click="doSomething"></a>

        点击事件      方法

1.3.2.指令中的修饰符

修饰符是以半角句号“.”指明的特殊后缀,用于指出一个指令应该以特殊方式绑定

例如:.prevent修饰符告诉v-on指令对于触发的事件调用event.preventDefault()

<form v-on:submit.prevent="onSubmit"></form>

                              绑定前

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>文本插值</title>
  </head>
  <body>
    <!-- 文本插值 -->
    <!-- 1.创建视图 -->
    <div id="app">
      <p>{{text}}</p>
      <p v-text="text"></p>
      <p v-html="text"></p>
      <p v-once>{{text}}</p>
      <input type="text" name="" id="" v-model="text" />
      <!-- 表达式 -->
      <p>{{num+1}}</p>
      <p>{{1+3}}</p>
      <p>三目运算符{{ok?true:false}}</p>
      <p>{{message.split("").reverse().join("")}}</p>
    </div>

    <!-- 2.导入Vue -->
    <script src="js/vue.min.js"></script>
    <!-- 3.创建模型 -->
    <!-- 4.创建vm -->
    <script>
      var tmp = {
        text: "<h2>今天不学习,明天捡垃圾</h2>",
        num: 1,
        ok: false,
        message: "Hello Word",
      };

      var vm = new Vue({
        el: "#app",
        data: tmp,
      });
    </script>
    
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>综合案例——计算工资</title>
</head>
<body>
    <h1>个人工资计算器(指令)</h1>
    <!-- 1.创建视图 -->
    <div id="app">
        <fieldset>
            <legend>个人工资计算器</legend>
            <p>
                <label for="">基本工资</label>
                <input type="number" name="" id="" v-model="base">
            </p>
            <p>
                <label for="">岗位工资</label>
                <input type="number" name="" id="" v-model="job">
            </p>
            <p>
                <label for="">绩效工资</label>
                <input type="number" name="" id="" v-model="reward">
            </p>
            <span>工资总和:{{(parseFloat(job)+parseFloat(base)+parseFloat(reward)).toFixed(2)}}</span>
        </fieldset>
    </div>
    <!-- 2.导入Vue -->
    <script src="js/vue.min.js"></script>
    <!-- 3.创建模型 -->
    <!-- 4.创建vm -->
    <script>
        var tmp={
            base:0,
            job:0,
            reward:0
        }
        var vm=new Vue({
            el:"#app",
            data:tmp
        })
    </script>
</body>
</html>

2.Vue绑定类样式和内联样式

2.1.Vue绑定类样式

2.1.1.对象语法

<div v-bind:class="{类样式名:绑定数据,...}"></div>

类样式名可以有多个,通过绑定数据为true或false来控制样式是否应用到元素中 

2.1.2.数组语法

<div v-bind:class="[绑定数据1,绑定数据2,...]"></div>

2.2.Vue绑定内联样式

2.2.1.对象语法

v-bind:style的对象语法十分直观——看着非常像CSS,但其实是一个JavaScript对象。CSS属性名可以用驼峰式或者短横线分隔,需要用引号括起来命名

<div v-bind:style="{样式属性:绑定数据}"/>

或者

<div v-bind:style="styleObject"/>

<script>

        var vm=new Vue({

                el:"#app",

                data:{

                        styleObject:{

                                样式属性1:样式值1,

                                样式属性2:样式值2

                        }

                }

        });

</script>

2.2.2.数组语法

<div v-bind:class="[baseStyles,overridingStyle]"></div>

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>绑定样式</title>
    <style>
      #app {
        width: 300px;
        height: 300px;
      }
      #app .border {
        width: 300px;
        height: 300px;
        border: 3px solid black;
      }
      #app .shadow {
        box-shadow: 0px 0px 10px 10px gainsboro;
      }
      #app .bg {
        background-color: lavender;
      }
      #app .hover:hover {
        background-color: lightcoral;
        width: 100px;
        height: 100px;
        transition: all 3s 0.2s;
      }
    </style>
  </head>
  <body>
    <!-- 1.创建视图 -->
    <div id="app">
      <input type="checkbox" v-model="isborder" name="" id="" />边框
      <input type="checkbox" v-model="isshadow" name="" id="" />阴影
      <input type="checkbox" v-model="isbg" name="" id="" />背景
      <input type="checkbox" v-model="ishover" name="" id="" />悬停
      <!-- 对象方式 -->
      <!-- <div v-bind:class="{border:isborder,shadow:isshadow,bg:isbg,hover:ishover}"></div> -->
      <!-- 等同于 -->
      <!-- <div class="border"></div> -->
      <!-- 数组方式 -->
      <!-- <div v-bind:class="[isborder,isshadow,isbg,ishover]"></div> -->
      <!-- 内联样式 -->
      <div v-bind:style="[isborder,isshadow,isbg,ishover]"></div>
      <div style="width: 300px; height: 300px; border: 1px solid black"></div>
    </div>
    <!-- 2.导入Vue -->
    <script src="js/vue.min.js"></script>
    <!-- 3.创建模型 -->
    <!-- 4.创建vm -->
    <script>
      // 对象方式
      // var tmp={
      //     isborder:true,
      //     isshadow:true,
      //     isbg:true,
      //     ishover:true
      // }
      //数组方式
      // var tmp={
      //     isborder:"border",
      //     isshadow:"shadow",
      //     isbg:"bg",
      //     ishover:"hover"
      // }
      //内联方式
      var tmp = {
        isborder: {
          width: "300px",
          height: "300px",
          border: "3px solid black",
        },
        isshadow: "shadow",
        isbg: {
          width: "300px",
          height: "300px",
          "background-color": "lavender",
        },
        ishover: "hover",
      };
      var vm = new Vue({
        el: "#app",
        data: tmp,
      });
    </script>
  </body>
</html>

3.条件渲染指令

3.1.v-if和v-else指令

v-if是条件渲染指令,它根据表达式的真假来删除和插入元素

<div v-if="expression"></div>

 expression是一个返回bool值的表达式,表达式可以是一个bool属性,也可以是一个返回bool的运算符

可以用v-else指令为v-if添加一个else块

v-else元素必须立即跟在v-if元素的后面,否则它不能被识别

3.2.v-show指令

也可以实现条件渲染

与v-if指令不同的是带有v-show指令的元素始终会被渲染并保留在DOM中,而v-show指令只是简单的切换元素的CSS属性display

注意:v-show指令不支持<template>元素,也不支持v-else指令

如果需要非常频繁的切换,则使用v-show指令较好;如果在运行时条件很少改变,则使用v-if指令较好

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用户登录注册v-if v-else</title>
</head>
<body>
    <div id="app">
        <h1>用户登录</h1>
        <input type="checkbox" name="" id="" v-model="isRegister">是否注册
        <div class="box">
            <div v-if="isRegister">
                <h2>用户登录</h2>
                <p>
                    用户名:<input type="text" id="" value=""/>
                </p>
                <p>
                    密码:<input type="text" id="" value=""/>
                </p>
            </div>
            <div v-else>
                <h2>用户注册</h2>
                <p>
                    用户名:<input type="text" id="" value=""/>
                </p>
                <p>
                    密码:<input type="text" id="" value=""/>
                </p>
                <p>
                    确认密码:<input type="text" id="" value=""/>
                </p>
            </div>

            <!-- v-show用法 -->
            <!-- <div v-show="isRegister">
                <h2>用户登录</h2>
                <p>
                    用户名:<input type="text" id="" value=""/>
                </p>
                <p>
                    密码:<input type="text" id="" value=""/>
                </p>
            </div>
            <div v-show="!isRegister">
                <h2>用户注册</h2>
                <p>
                    用户名:<input type="text" id="" value=""/>
                </p>
                <p>
                    密码:<input type="text" id="" value=""/>
                </p>
                <p>
                    确认密码:<input type="text" id="" value=""/>
                </p>
            </div> -->



        </div>
    </div>
    <script src="js/vue.min.js"></script>
    <script>
        var tmp={
            isRegister:true
        }
        var vm=new Vue({
            el:"#app",
            data:tmp
        })
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>条件指令</title>
</head>

<body>
    <!-- v-if v-show -->
    <div id="app">
        <div v-if="ok">
            <div>满足条件输出:什么是快乐星球</div>
            <p>玛卡巴卡</p>
        </div>
        <!-- template等于{} -->
        <!-- <template v-if="ok">
            <div>满足条件输出:什么是快乐星球</div>
            <p>玛卡巴卡</p>
        </template> -->
        <div v-else>
            不满足条件:这是一个测试
        </div>
    </div>
    <script src="js/vue.min.js"></script>
    <script>
        var tmp = {
            ok: true
        }
        var vm = new Vue({
            el: "#app",
            data: tmp
        })
    </script>
</body>

</html>

4.事件绑定

4.1.v-on指令

v-on指令监听DOM事件,并在触发时运行一些JavaScript代码。通过v-on指令可以绑定Vue实例选项参数methods中的方法作为事件的处理代码

“v-on:”后参数接受所有的原生事件名称

<button v-on:click='事件处理方法'>按钮</button>

可改写为

<button @:click='事件处理方法'>按钮</button>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>动态绑定事件</title>
</head>
<body>
    <div id="app">
        <fieldset>
            <legend>计算器</legend>
            <!-- 输入框 -->
            <p>
                数字1:<input type="number" name="" id="" v-model="num1">
            </p>
            <p>
                数字2:<input type="number" name="" id="" v-model="num2">
            </p>
            <!-- 按钮 -->
            <button type="button" v-on:click="doADD">+</button>
            <button type="button" @click="doSub">-</button>
            <button type="button" @click="alert(vm.num1*vm.num2)">*</button>
            <button type="button" @click="docf">/</button>
            <p>
                计算结果:{{sum}}
            </p>
        </fieldset>
        
    </div>
    <script src="js/vue.min.js"></script>
    <script>
        var tmp={
            num1:0,
            num2:0,
            sum:0
        }
        var vm=new Vue({
            el:"#app",
            data:tmp,
            methods:{
                doADD(){
                    this.sum=parseFloat(this.num1)+parseFloat(this.num2)
                },
                doSub(){
                    this.sum=parseFloat(this.num1)-parseFloat(this.num2)
                },
                docf(){
                    this.sum=parseFloat(this.num1)/parseFloat(this.num2)
                }
            }
        })
    </script>
</body>
</html>
4.2.事件修饰符

在JavaScript的事件处理程序中一般调用event.preventDefault()方法取消事件的默认动作,以及调用event.stopPropagation()方法阻止事件冒泡

.stop等同于调用event.stopPropagation()方法
.prevent等同于调用event.preventDefault()方法
.capture使用capture模式添加事件监听器
.self只有当事件是从监听元素本身触发时才触发回调
.once点击事件将只会触发一次

当一个元素上的事件被触发的时候,比如鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发,这一过程被称为事件冒泡。这个事件从原始的元素开始将一直冒泡到DOM树的最上层 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件修饰符</title>
</head>
<body>
    <div id="app">
        <!-- v-on=======@   v-bind=======: -->
        <div @click="alert('这是父亲的点击事件')" v-bind:style="classOne">
            <button type="button" @click.stop="alert('这是儿子的点击事件')" >你点击我试试看</button>
        </div>
    </div>
    <script src="js/vue.min.js"></script>
    <script>
        var vm=new Vue({
            el:"#app",
            data:{
                classOne:{
                    width:"300px",
                    height:"300px",
                    "background":"skyblue"
                }
            }
        })
    </script>
</body>
</html>

三、Vue列表渲染

1.v-for指令

v-for指令是基于一个数组来重复渲染的元素,通常用于显示列表和表格数据

v-for指令需要使用“item in items”形式的特殊语法,其中items是数据源,item则是被迭代的别名

v-for指令还支持一个可选的第二个参数,即当前项的索引

为了给Vue一个提示,以便它能跟踪每一行的数据,从而重用和重新排序现有元素,v-for指令需要为每项提供一个唯一的key属性

三个值:属性值,属性,索引

两个值:值,索引(index)

提示:建议尽可能在使用v-for指令时提供key属性,除非遍历输出的DOM内容非常简单,或者时刻意依赖默认行为以获取性能上的提升

注意:不要使用对象或数组之类的非基本类型值作为v-for的key属性,请用字符串或数值类型的值

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>v-for指令</title>
    <link rel="stylesheet" href="css/bootstrap.min.css">
</head>

<body>
    <div id="app">
        <div>
            <ul>
                <!-- 不显示索引 -->
                <!-- <li v-for="item in preface">
                            <a href="#"> {{item}}</a>
                        </li> -->
                <!-- 显示索引 -->
                <li v-for="(item,index) in preface">
                    <a href="#">{{index}}--------- {{item}}</a>
                </li>
            </ul>
        </div>
        <!-- 属性值,属性,key -->
        <div v-for="(item,index,key) in user" :key="index">
            {{key}}------------ {{index}} -------{{item}}
        </div>
        <!-- <div v-for="(item,key,index) in user" :key="index">
            {{key}}------------ {{index}} -------{{item}}
        </div> -->
        <table class="table table-bordered table-striped">
            <caption>学生信息表</caption>
            <thead class="bg-info">
                <tr>
                    <td>序号</td>
                    <td>姓名</td>
                    <td>年龄</td>
                    <td>职位</td>
                </tr>
            </thead>
            <tbody>
                <tr v-for="(item,key,index) in stus" v-if="item.age>18">
                    <td>{{key}}</td>
                    <td>
                        {{item.name}}
                    </td>
                    <td>
                        {{item.age}}
                    </td>
                    <td>
                        {{item.job}}
                    </td>
                </tr>
            </tbody>

        </table>
    </div>
    <script src="js/vue.min.js"></script>
    <script>
        var tmp = {
            preface: ["首页", "订单管理", "购物车"],
            user: {
                name: "pxy",
                age: 20,
                job: "前端工程师",
            },
            stus: [{
                name: "tyy",
                age: 10,
                job: "前端工程师",
            }, {
                name: "lky",
                age: 20,
                job: "前端工程师",
            }, {
                name: "hy",
                age: 19,
                job: "后端工程师",
            }]
        }
        var vm = new Vue({
            el: "#app",
            data: tmp,
        })
    </script>
</body>

</html>

2.计算属性

在计算属性里可以完成各种复杂的逻辑,包括运算、函数调用等

计算属性还可以依赖多个Vue实例的数据,只要其中任一数据发生变化,计算属性就会重新执行,视图也会更新

所有计算属性都以函数的形式写在Vue实例内的computed选项中(支持缓存)

methods()方法与计算属性的区别是计算属性支持缓存,所以在遍历大数组或做大量计算时,计算属性更高效

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>计算属性</title>
</head>

<body>
    <div id="app">
        <p>
            原始数据:{{message}}
        </p>
        <p>
            转换后:{{message.split("").reverse().join("")}}
        </p>
        <p>
            方法调用 {{reverMsg()}}
        </p>
        <p>
            计算属性得到{{revolovMsg}}
        </p>

    </div>
    <script src="js/vue.min.js"></script>
    <script>
        var tmp = {
            message: "Hello word"
        }
        var vm = new Vue({
            el: "#app",
            data: tmp,
            methods: {
                reverMsg() {
                    return this.message.split("").reverse().join("");
                }
            },
            computed: {
                revolovMsg() {
                    return this.message.split("").reverse().join("");
                },
            }
        })
    </script>
</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>用计算属性做筛选</title>
</head>

<body>
    <div id="app">
        <p>
            <input type="text" placeholder="请输入搜索字符" name="" id="" v-model="wordKey">
        </p>

        <div v-for="item in findItems">
            {{item}}
        </div>

    </div>
    <script src="js/vue.min.js"></script>
    <script>
        var tmp = {
            centence: ["qwer", "asdf", "zxcv", "asqw"],
            wordKey: "",
            stus: [{
                name: "tyy",
                age: 10,
                job: "前端工程师",
            }, {
                name: "lky",
                age: 20,
                job: "前端工程师",
            }, {
                name: "hy",
                age: 19,
                job: "后端工程师",
            }]
        }
        var vm = new Vue({
            el: "#app",
            data: tmp,
            computed: {
                //数组
                // findItems() {
                //     var _this = this;
                //     return _this.centence.filter(function(val) {
                //         return val.indexOf(_this.wordKey) != -1;
                //     });
                //数组对象
                findItems() {
                    var _this = this;
                    return _this.stus.filter(function(val) {
                        return val.name.indexOf(_this.wordKey) != -1;
                    });
                }
            }
        })
    </script>
</body>

</html>

3.侦听属性

Vue提供了一种更通用的方式来观察和响应Vue实例上的数据变动——侦听属性

当有一些数据需要随着其他数据的变动而改变时,就可以使用侦听属性watch

watch是一个对象,其中watch对象的属性是需要侦听的目标,一般是data中的某个数据项,而watch对象的属性值是一个函数,是当被侦听的数据项发生变化时需要执行的函数

这个函数有两个形参:第一个是当前值(想要的值),第二个是更新后的值

如果想要一开始让最初绑定的时候就执行,就需要在watch中使用handler()方法和immediate属性

handler()方法:其值是一个回调函数,即监听到变化时应该执行的函数

immediate属性:其值是true或false,确认是否以当前的初始值执行handler的函数

说明:如果模型数据是一个对象时,Vue默认并不能侦听对象属性的变化,watch里面还有一个deep属性,默认值是false,代表是否需要使用深度侦听。当deep属性为ture时,就能侦听对象属性的变化

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>侦听属性</title>
    <style>
        .warnColor {
            color: red;
        }
        .warnDisa {
            display: none;
        }
    </style>
</head>

<body>
    <div id="app">
        <fieldset>
            <legend>商品信息</legend>
            <p>
                商品名称:<input type="text" name="" id="" v-model="title">
                <span :class="{warnColor:isname,warnDisa:!isname}">
                    商品名称不能为空
                </span>
            </p>
            <p>
                商品价格:<input type="number" name="" id="" v-model="price">
                <span :class="{warnColor:isprice,warnDisa:!isprice}">
                    商品价格不能为空
                </span>
            </p>
            <p>
                商品数量:<input type="number" name="" id="" v-model="num">
                <span :class="{warnColor:isnum,warnDisa:!isnum}">
                    商品数量不能为空
                </span>
            </p>
            <p>
                商品总额:{{sum}}
            </p>
        </fieldset>
    </div>
    <script src="js/vue.min.js"></script>
    <script>
        var tmp = {
            title: "Vue.JS高效前端开发",
            price: 39,
            num: 1,
            isname: false,
            isprice: false,
            isnum: false,
            sum: 0,
        }
        var vm = new Vue({
            el: "#app",
            data: tmp,
            watch: {
                title: function (oldVal, newVal) {
                    if (oldVal != "") {
                        this.isname = false;
                    } else {
                        this.isname = true;
                    }
                },
                price: {
                    handler: function (oldVal, newVal) {
                        console.log(oldVal);
                        console.log(newVal);
                        if (oldVal != "") {
                            this.isprice = false;
                        } else {
                            this.isprice = true;
                        }
                        this.sum = parseFloat(oldVal) * parseInt(this.num);

                    },
                    immediate: true
                },
                num: function (oldVal, newVal) {
                    if (oldVal != "") {
                        this.isnum = false;
                    } else {
                        this.isnum = true;
                    }
                    this.sum = parseFloat(this.price) * parseInt(oldVal);
                },
            },
        })
    </script>
</body>

</html>

4.综合案例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>综合案例</title>
    <link rel="stylesheet" href="css/bootstrap.min.css">
</head>

<body>
    <div id="app">
        <fieldset>
            <legend>新商品信息</legend>
            <div>
                <p>
                    名称:<input type="text" name="" id="" v-model="newProduct.name">
                </p>
                <p>
                    价格:<input type="number" name="" id="" v-model="newProduct.price">
                </p>
                <p>
                    类别:
                    <select name="" id="" v-model="newProduct.categray">
                        <option  v-for="cate in categraies":value="cate">{{cate}} </option>
                    </select>
                </p>
                <p>
                    <button @click="createP" class="btn-info">添加</button>
                </p>
            </div>
            <div>
                查询关键字: <input type="text" name="" id="" v-model="keyWords">
            </div>
            <table class="table table-bordered">
                <thead class="bg-info">
                    <tr>
                        <td>
                            编号
                        </td>
                        <td>
                            名称
                        </td>
                        <td>
                            价格
                        </td>
                        <td>
                            类别
                        </td>
                        <td>
                            删除
                        </td>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="(pro,index) in Findproducts">
                        <td>{{index}} </td>
                        <td>{{pro.name}} </td>
                        <td>{{pro.price}} </td>
                        <td>{{pro.categray}} </td>
                        <td>
                            <button class="btn-danger" @click="deleteP(index)">删除</button>
                        </td>
                    </tr>
                </tbody>
            </table>
        </fieldset>

    </div>
    <script src="js/vue.min.js"></script>
    <script>
        var tmp = {
            newProduct: {
                name: "",
                price: "",
                categray: "",
            },
            categraies: ["手机/电脑", "护肤/美妆", "男装/女装"],
            products: [
                {
                    name: "华为荣耀",
                    price: "1659",
                    categray: "手机/电脑",
                },
                {
                    name: "鸿星尔克",
                    price: "234",
                    categray: "男装/女装",
                },
            ],
            keyWords: "",
        }
        var vm = new Vue({
            el: "#app",
            data: tmp,
            methods: {
                deleteP:function(index) {
                    if (confirm("你真的要删除该条数据吗")) {
                        this.products.splice(index, 1);
                    }
                },
                createP() {
                    this.products.push(this.newProduct);
                    this.newProduct = {
                        name: "",
                        price: "",
                        categray: "",
                    };
                },
            },
            computed: {
                Findproducts() {
                    var _this = this;
                    return _this.products.filter(function (prod) {
                        return prod.name.indexOf(_this.keyWords) != -1;
                    })
                }
            },
        })
    </script>
</body>

</html>

四、Vue组件

1.Vue组件的介绍

组件系统是Vue中一个重要的概念,它提供了一种抽象结构,可以使用独立和可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树

组件是可复用的Vue实例

组件应该挂载到某个Vue实例下,否则它不会生效

组件的使用步骤

A.创建组件:调用Vue.extend()方法创建组件

Vue.extend()方法是Vue的扩展,调用Vue.extend()方法创建的是一个组件

Vue.extend()方法有一个选项对象,选项对象的template属性用于定义组件要渲染的HTML

B.注册组件:调用Vue.component()方法注册组件

两个参数:第一个参数是组件的标签,第二个参数是组件

C.使用组件:使用Vue实例页面内自定义组件标签

注意:组件命名方式有两种短横线分割法(推荐使用)、首字母大写法

2.Vue组件的使用

2.1.组件注册

2.1.1.全局注册

在调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在当前页面的任意Vue实例下使用

<div id="app1">

        <h1>Vue实例1</h1>

        <my-comp></my-comp>

</div>

<div id="app2">

        <h1>Vue实例2</h1>

        <my-comp></my-comp>

</div>

<script src="js/Vue.min.js"></script>

<script>

        //创建组件

        var myComp=Vue.extend({

                template:"<h3>使用全局Vue组件!</h3>"

        });

        //注册组件

        Vue.component("my-comp",myComp)

        //实例化Vue实例1

        var vm1=new Vue({

                el:"#app1"

        });

        //实例化Vue实例2

        var vm1=new Vue({

                el:"#app2"

        });

</script>

2.1.2.局部注册

如果不需要全局组件,或者是让组件在其他组件内使用,可以在Vue实例化时,设置选项参数的components参数属性实现局部注册

<script>

        //创建组件

        var myComp=Vue.extend({

                template:"<h3>使用局部Vue组件!</h3>"

        });

        //实例化Vue

        var vm=new Vue({

                el:"#app",

                //局部注册组件

                components:{

                        "my-comp":myComp

                }

        });

</script>

2.2.组件注册语法糖

即在注册组件时直接创建和注册组件,Vue在背后会自动地调用Vue.extend()

2.2.1.简化全局组件

//全局注册,my-comp是组件标签名称

Vue.component('my-comp',{

        template:'<div><h1>简化全局组件</h1></div>'

})

2.2.2.简化局部组件

var vm=new Vue({

                el:"#app",

                //局部组件,my-comp是组件标签名称

                components:{

                        'my-comp':{

                                template:'<div><h1>简化局部组件</h1></div>'

                        }

                }

        });

2.3.使用<script>或<template>

尽管语法糖简化了组件注册,但在template选项中拼接HTML元素比较麻烦,也导致了HTML和JavaScript的高耦合性。

Vue提供了两种方式将定义在JavaScript中的HTML模板分离出来

2.3.1.使用<script>标签

<div id="app">

        <my-comp></my-comp>

</div>

<script type="text/x-template" id="myComp">

        <div><h1>使用script标签</h1></div>

</script>

<script>

        Vue.component('my-comp',{

                template:'#myComp'

        });

        var vm=new Vue({

                el:"#app",

                components:{

                        'my-comp':{

                                 template:'#myComp'

                        }

                }

        });

</script>

注意:使用<script>标签时,type指定为text-x-template,旨在高数浏览器这不是一段js脚本,这样浏览器在解析HTML文档时会忽略<script>标签内定义的内容

2.3.2.使用<template>标签

如果使用<template>标签,则不需要指定type属性

<div id="app">

        <my-comp></my-comp>

</div>

<template id="myComp">

        <div><h1>使用template标签</h1></div>

</template>

<script>

        var vm=new Vue({

                el:"#app",

                components:{

                        'my-comp':{

                                 template:'#myComp'

                        }

                }

        });

</script>

建议使用 <script>或<template>标签来定义组件的HTML模板,这使得HTML代码和JavaScript代码是分离的,便于维护阅读

2.4.组件的data和el选项

一般实例化Vue的多数选项也可以用在Vue.extend()或Vue.component()中,不过有两个特殊选项参数除外,即data和el。Vue.js规定:在定义组件的选项时,data和el选项必须使用函数

如果data选项指向某个对象,这意味着所有的组件实例共用一个data。

使用一个函数作为data选项,让这个函数返回一个新对象,语法如下:

//全局注册组件

Vue.component("组件名称",{

        el:function(){...},

        data:function(){

                return{

                        属性:值

                }

        },

        template:"组件模板"

})

//局部注册组件

var vm1=new Vue({

        el:"#app",

        components:{

                "组件名称":{

                        el:function(){...},

                        data:function(){

                                return{

                                        属性:值

                                }

                        },

                        template:"组件模板"

                }

        }

});

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>组件</title>
</head>

<body>
    <div id="app">
        <first-comp></first-comp>
        <second-comp></second-comp>
        <three-comp></three-comp>
        <four-comp></four-comp>
        <five-comp></five-comp>
        <six-comp></six-comp>
    </div>
    <p>===================================================</p>
    <div id="app1">
        <first-comp></first-comp>
        <second-comp></second-comp>
        <three-comp></three-comp>
        <four-comp></four-comp>
    </div>
    <!-- 1.4 通过template模板标签实现H5js分离 -->
    <template id="twoTp">
        <h2 style="color: purple">局部组件,基本类型创建</h2>
    </template>
    <!-- 1.3 H5 和js分开 -->                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
    <script src="js/vue.min.js"></script>
    <script type="js/x-handlebars-template" id="oneTp">
        <h2 style='color: cyan'>局部组件,基本类型创建 </h2>
    </script>
    <script>
        //组件使用三步骤
        //1.创建组件
        var firstComp = Vue.extend({
            template: "<h2 style='color: red'>全局组件,基本类型创建</h2>"
        });
        //1.1创建局部组件
        var secondComp = Vue.extend({
            template: "<h2 style='color: orange'>局部组件,基本类型创建</h2>"
        });
        //2.注册组件
        Vue.component("first-comp", firstComp);

        //1.2全局 创建注册一步到位(语法糖)
        Vue.component("three-comp", {
            template: "<h2 style='color: yellow'>全局组件,基本类型创建</h2>"
        });
        //3.使用组件
        var vm = new Vue({
            el: "#app",
            components: {
                "second-comp": secondComp, //局部组件
                //使用语法糖,一步到位创建局部组件
                "four-comp": {
                    template: "<h2 style='color: green'>局部组件,基本类型创建</h2>"
                },
                "five-comp": {
                    template: "#oneTp"
                },
                "six-comp": {
                    template: "#twoTp"
                },
            }
        });
        var vm1 = new Vue({
            el: "#app1",
        });
    </script>
</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>H5 和JS 分离组件</title>
</head>

<body>
    <div id="app">
        <!-- 直接绑定 -->
        <seven-comp message1="下雨啦"></seven-comp>
        <seven-comp :message1="message"></seven-comp>
    </div>
    <template id="oneTp">
        <p>这是data的模板 {{message1}} </p>
    </template>
    <script src="js/vue.min.js"></script>
    <script>
        var vm = new Vue({
            el: "#app",
            data: {

                message: "张三",
            },
            components: {
                "seven-comp": {
                    // data: function() {
                    //     return {
                    //         message: "李四"
                    //     }
                    // },

                    props:["message1"],

                    template: "#oneTp",
                }
            }
        })
    </script>
</body>

</html>

3.组件之间的通信

子组件只能在父组件中使用

通过props向子组件传递数据

props的值是字符串数组

var component=Vue.extend({

        props:["属性名","属性名"],

        template:"模板"

});

HTML属性名的大小写是不敏感的,所以浏览器会把所有的大写字符解释为小写字符 

props的值是对象

var component=Vue.extend({

        props:{

                属性名:String,

                属性名:Number

        },

        template:"模板"

});

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>父子组件</title>
</head>
<body>
    <div id="app">
        <parent-comp></parent-comp>
    </div>
    <!-- 1.创建子组件 -->
    <!-- 2.创建父组件,并在其中注册子组件 -->
    <!-- 3.注册父组件 -->
    <!-- 4.调用父组件 -->
    <template id="oneTP">
        <h2 style="color: lightblue;">我是子组件</h2>
    </template>
    
    <template id="twoTP">
        <p>
            我是父组件
            <child-comp></child-comp>
        </p>
    </template>
    
    
    <script src="js/vue.min.js"></script>
    <script>
       var childComp= Vue.extend({
            template:"#oneTP"
        });
        var parentComp=Vue.extend({
            template:"#twoTP",
            components:{
                "child-comp":childComp
            }
        });
        Vue.component("parent-comp",parentComp)
        var vm=new Vue({
            el:"#app",
            
        })
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>传值</title>
</head>
<body>
    <div id="app">
        <first-Comp :dat1="dat" :title2="title" :content3="content"></first-Comp>
    </div>
    <template id="oneTp">
        <div>
            <p>日期:{{dat1}} </p>
            <p>标题:{{title2}} </p>
            <p>内容:{{content3}} </p>
        </div>
        
    </template>
    <script src="js/vue.min.js"></script>
    <script >
        Vue.component("first-comp",{
            props:["dat1","title2","content3"],
            template:"#oneTp"
        })
        var vm=new Vue({
            el:"#app",
            data:{
                title:"卓越项目启动",
                dat:"2023-06-24",
                content:"今天我们正在做第二个卓越项目,我想把她改成前后端分离的项目",
            }
        })
    </script>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>父组件子组件传值</title>
    <style>
        .banner{
            background-color: lightblue;
            height: 200px;
            line-height: 200px;
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="app">
        <parent-comp message="消暑一下" :is-style="isStyle"></parent-comp>
    </div>
    <!-- 1.定义子组件和父组件 -->
    <!-- 子组件模板 -->
    <template id="childComp">
        <span>{{subMessage}} </span>
    </template>
    <!-- 父组件模板 -->
    <template id="parentComp">
        <h1 :class="{banner:isStyle}">
            {{message}}
            <child-comp sub-message="不玩就out了"></child-comp>
        </h1>
    </template>
    <script src="js/vue.min.js"></script>
    <script>
        var vm=new Vue({
            el:"#app",
            data:{
                isStyle:true,
            },
            components:{
                "parent-comp":{
                    props:["message","isStyle"],
                    template:"#parentComp",
                    components:{
                        "child-comp":{
                            props:{
                                subMessage:String
                            },
                            template:"#childComp"
                        }
                    }
                }
            }
        })
    </script>
</body>
</html>

4.Vue的插槽

将所携带的内容插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性

插槽显不显示、怎么显示是由父组件来控制的,而插槽在哪里显示是由子组件来进行控制

<slot>标签是组件内部的占位符

注意:插槽是为网络组件创建”声明性API“的一种方法。它们混入到用户的DOM中,对整个组件进行渲染,从而将不同的DOM树组合在一起

4.1.默认插槽

只能有一个

4.2.具名插槽

当需要使用多个插槽时,要使用具名插槽

4.3.作用域插槽的使用

插槽可以控制HTML模板的显示与不显示

作用域插槽(slot-scope)其实就是带数据的插槽

原来父组件可以通过绑定数据传递给子组件,而作用域插槽可以通过子组件绑定数据传递给父组件

作用域插槽的使用场景既可以复用子组件的slot,又可以使slot内容不一致

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>插槽</title>
</head>
<body>
    <!-- 默认插槽 没有名字 -->
    <div id="app">
        <input type="checkbox" name="" id="" v-model="ok">
        <div v-if="ok">
            <login-comp>用户登录</login-comp>
        </div>
        <div v-else="ok">
            <login-comp>用户注册</login-comp>
        </div>
    </div>
    <template id="tp1">
        <div>
            <slot></slot>
            <p>
                用户名<input type="text">
            </p>
                                 
            <p>
                密码<input type="text">
            </p>
            
        </div>
    </template>
    <script src="js/vue.min.js"></script>
    <script>
        Vue.component("login-comp",{
            template:"#tp1",
        });
        var vm=new Vue({
            el:"#app",
            data:{
                ok:true,
            },
        })
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>具名插槽</title>
</head>
<body>
    <div id="app">
        <book-list :books="booklist">
            <template slot="book" slot-scope="props">
                <li>
                    {{props.bookname}}  
                </li>
            </template>
        </book-list>
    </div>
    <template id="tp2">
        <div>
            <ul>
                <slot name="book" v-for="item in books" :bookname="item.name"></slot>
            </ul>
        </div>
    </template>
    <script src="js/vue.min.js"></script>
    <script>
        
        var vm=new Vue({
            el:"#app",
            data:{
                booklist:[
                    {name:"第一本书"},
                    {name:"第二本书"},
                    {name:"第三本书"},
                    {name:"第四本书"},
                ]
            },
            components:{
                "book-list":{
                    props:["books"],
                    template:"#tp2"
                }
            }
        })
    </script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值