美文网首页
Vue.js破冰系列-2HTML模板与内部指令

Vue.js破冰系列-2HTML模板与内部指令

作者: 书上得来终觉浅 | 来源:发表于2019-08-16 17:23 被阅读0次

    1概述

    上一章我们说了Vue是用于构建用户界面的框架,它最终呈现给用户的是一个一个HTML标签。Vue可以使用HTML模板和render函数这2种方式来构建这些HTML标签。当开发者使用了HTML模板来构建界面时,Vue内部会将其编译成render函数,最后渲染到挂载点上。render函数生成的是虚拟DOM,虚拟DOM是基于js的,相比于DOM的操作,开销会小很多。

    Vue是实例需要一个挂载点,这个挂载点是由Vue的el选项指定的,el的类型可以是string或HTMLElement类型,当为string时,使用css选择器。

    <html>
      <body>
        <div id="app">
          <h1>Hello World!</h1>
        </div>
        <script>
            var vm = new Vue({
            el:"#app"
          });
        </script>
      </body>
    </html>
    

    上面使用了el类型为string的方式查找挂载点,#app为css的id选择器。Vue实例会将自身生成的DOM替换掉挂载点的元素,这里我们没有定义如何生成DOM,所以,Vue会将挂载点的HTML提取出来当作模板生成DOM,替换掉原来的HTML标签。

    2 HTML模板

    HTML模板有4种方式定义,分别是:

    • Vue实例不定义,由Vue将挂载点的元素提取出来生成模板,可以理解为将模板写在挂载点内,如上面的例子。

    • 将模板以字符串的形式写在实例的配置选项template中

      <div id="app"></div>
      <script>
        var vm = new Vue({
            el:"#app",
          template:"<h1>Hello World!</h1>"
        });
      </script>
      

      实例挂载后,会将template中的字符串,转换为html标签,替换掉app的内容。

    • 使用x-template,在template中定义模板有些弊端,因为它是字符串,所以缺少高亮的语法支持,当需要多行显示是,需要用到\,体验不好。x-template是将模板的定义放在scirpt标签中,然后在template中使用#引用该模板。

      <div id="app"></div>
      <!--使用x-template定义模板-->
      <script type="text/x-template" id="tempHello">
        <h1>Hello World!</h1>
      </script>
      <!--使用template选项引用x-template定义的模板-->
      <script>
        var vm = new Vue({
          el:"#app",
          template:"#tempHello",
        });
      </script>
      

      在定义x-template模板时,需要注意的是,x-template定义的模板在script标签中,type属性为text/x-template,需要配上id,后续的template中才能够引用。在template选项中要使用#引用x-template模板(类似使用css的id选择器)。

    • 使用单文件组件(single-file components)的方式定义模板(最常用),单文件组件是以.vue结尾的文件,一个文件就是一个组件,后续讲解组件时再详细说明。

    3 内部指令(directive)

    在HTML模板中,html元素仅仅是界面的呈现,我们还需要和vue实例进行交互,这就需要用到vue的指令。在vue中,指令是写在html元素中,以v-开头,后接表达式(有些不需要表达式),它是Vue实例数据与用户界面之间的纽带。如:

    <div id="app">
      <p v-text="msg">text</p>
    </div>
    <script>
        var vm = new Vue({
        el:"#app",
        data:{
          msg:"Hello World!",
        }
      });
    </script>
    

    v-text指令后接表达式msg,msg是对实例data中Hello World!的引用。当实例运行时,v-text指令所在的标签内容会替换为Hello World!。有些指令可以在其后加上修饰符,从而达到特殊的效果。指令大致可以分为显示类、渲染类,事件类3种。

    3.1 显示类指令

    指令 用法 说明
    v-text <div v-text="msg"></div> 以文本的方式更新元素的内容,即使msg中是html标签,它只当做字符串的方式处理,不会解析标签。即,只改变元素的textContent,与插值表达式相同<div>{{msg}}</div>
    v-html <div v-html=“html”></div> 更新元素的 innerHTML 。注意:内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译
    v-cloak <div v-cloak>{{msg}}</div> v-cloak不接表达式,当网速较慢时,vue实例还没有加载完成,这时界面上会显示{{msg}},在vue实例加载完成后,{{msg}}会替换为对应的内容,不过这使得页面会闪动。当使用v-cloak指令后,vue实例在编译完成后,会移除该指令。通过这一特征,使用v-cloak再配合css的属性选择器[v-cloak]{display:none;}使用,达到在网速慢时界面不闪动的效果。

    3.2 渲染类指令

    指令 用法 说明
    v-once <div v-once>{{msg}}</div> v-once不接表达式,通常配合其他指令使用,表示只渲染一次。渲染后,当msg的内容发生变化时,也不会重新渲染。
    v-show <div v-show=“show”></div> v-show后接表达式,表达式返回true或false,根据表达式的值,修改该元素的display的样式,从而达到显示隐藏的效果。注意:因为是使用的display样式,所以该元素始终都会被渲染,只是显示/隐藏而已。

    v-if v-if-else v-else

    v-ifv-if-elsev-else标签需要组合使用,后接表达式。与其它语言的if/else一样,表达式条件为true时进入该分支。

    <div id="app">
      <p v-if="status==0">0</p>
      <p v-else-if="status==1">1</p>
      <p v-else="status==2">2</p>
    </div>
    <script>
        var vm = new Vue({
        el:"#app",
        data:{
          status:0,
        }
      });
    </script>
    

    v-show不同,使用这组指令时,当条件为true时才渲染该分支对应的元素/组件,当条件为false时,对应的元素/组件会被移除。

    Vue会出于效率考虑,会复用已有的元素,而非重新渲染,这样做有时会出现不符合需求的情况。比如,当复用input的时候,不会改变input的value值。

    <div id="app">
      <template v-if="showQQ">
        <label>QQ</label>
        <input placeholder="QQ">
      </template> 
      <template v-else>
        <label>Email</label>
        <input placeholder="Email">
      </template> 
    </div>
    

    当showQQ为true时,显示QQ输入框,用户在输入内容后,切换到Email,即改变了showQQ的值为false,这时第一个template的内容将会被移除,但是vue为了提高效率,会重新引用这个label和input,并设置了对应的值,不过,vue并没有修改input的value的值,所以Email输入框中的值还是我们之前输入的QQ内容。

    要解决这个问题,只需要在input上加入key属性,告诉vue这两个input是完全独立的,不要复用它们,注意key的值必须是唯一的。

    v-ifv-show的区别:

    • v-if是条件渲染,v-show是条件显示,v-if会根据条件销毁元素/组件以及其绑定事件,而v-show总是会渲染,根据条件切换css样式来控制显示/隐藏。
    • v-show不能使用在template标签上

    v-for

    v-for通过遍历数据源渲染指定内容。

    <ul>
      <li v-for="person in persons">{{person.name}}</li>
    </ul>
    
    <script>
      var vm = new Vue({
        data:{
          persons:[{name:"张三"},{name:"李四"},]
        }
      });
    </script>
    

    将v-for加到要渲染的标签上,使用alias in expression语法为当前遍历的元素提供别名。v-for的数据源可以是数组,也可以是对象,还可以是数字。

    • 当数据源为数组时,别名指向的是数组元素,
    • 当数据源为对象时,别名指向的是对象的值集合中的值。
    • 当数据源为数字时,别名指向的是从1开始的数字,迭代到等于数据源的数字

    v-for有多种可选参数形式:

    <div v-for="item in array"></div>
    <div v-for="(item,index) in array"></div>
    
    <div v-for="value in object"></div>
    <div v-for="(value,key) in object"></div>
    <div v-for="(value,key,index) in object"></div>
    
    <div v-for="n in number"></div>
    

    3.3 事件类指令

    事件类指令包括v-onv-bindv-model3个指令,如果说显示类指令和渲染类指令负责界面的呈现,那么事件类指令就负责定义交互逻辑,这些指令非常常用。

    3.3.1 v-on 监听事件

    使用v-on来监听事件,这些事件包括HTML元素的DOM原生事件,也可能是组件的自定义事件,组件使用$emit()方法来触发事件。v-on缩写为@符号

    <div id="app">
      <!-- v-on指令接收javascript代码 -->
      <button v-on:click="counter += 1">点击</button>
      <!-- v-on语法糖形式 -->
      <button @click="counter += 1">点击</button>
      <p>点击了按钮 {{ counter }} 次</p>
    </div>
    <script>
        var vm = new Vue({
      el: "#app",
      data: {
        counter: 0
      }
    })
    </script>
    

    事件的处理有3种方式,第1种为直接将js代码加在v-on指令中,如上所示。当代码量较多时,这种方式不可行,此时可用第2种方式,将事件的处理逻辑封装到方法中,把方法与事件绑定起来。

    <div id="app">
      <!-- v-on指令接收方法名 -->
      <button @click="increment">点击</button>
      <p>点击了按钮 {{ counter }} 次</p>
    </div>
    <script>
        var vm = new Vue({
      el: "#app",
      data: {
        counter: 0
      },
      methods:{
        increment(){
          this.counter++;
        }
      }
    })
    </script>
    

    第3种是使用内敛语句调用方法,这种方式和第1种基本一样,只是第1种是执行语句,这里是执行方法,这种方式很常用,它比第2种方式的优势在于,可以传递$event参数。Vue中使用变量$event表示原始DOM事件的Event 对象,拿到event对象后我们可以做很多事,比如获取触发事件的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态等。

    <div id="app">
      <!-- v-on指令使用内敛语句调用方法 -->
      <button @click="sayHello('张三',$evnet)">点击</button>
      <p>{{ msg }} </p>
    </div>
    <script>
        var vm = new Vue({
       el: "#app",
       data: {
         msg: "",
       },
       methods:{
         sayHello(name,event){
           if(evnet){
             event.preventDefault();
           }
           this.msg = "Hello " + name;
         }
       }
        })
    </script>
    

    下面列出一些常用的event事件标准属性和方法,具体参考MDN DOM Event接口

    属性/方法 描述
    ctrlKey 当鼠标事件触发时,如果 control 键被按下,则返回 true;
    altKey 当鼠标事件触发的时,如果alt 键被按下,返回true;
    shiftKey 当鼠标事件触发时,如果 shift 键被按下,则返回 true;
    button 当鼠标事件触发的时,如果鼠标按钮被按下,将会返回一个数值。
    clientX 鼠标指针在点击元素(DOM)中的X坐标。
    clientY 鼠标指针在点击元素(DOM)中的Y坐标。
    screenX 鼠标指针相对于全局(屏幕)的X坐标
    screenY 鼠标指针相对于全局(屏幕)的Y坐标
    bubbles 表示该事件是否在DOM中冒泡。
    cancelable 用来表示这个事件是否可以取消。
    currentTarget 返回注册这个事件监听的对象。
    eventPhase 事件流正在处理哪个阶段。
    target 触发该事件的元素。
    type 返回当前 Event 对象表示的事件的名称,及事件的类型(不区分大小写)
    initEvent() 初始化新创建的 Event 对象的属性。
    preventDefault() 取消事件,不执行与事件关联的默认动作。
    stopPropagation() 停止事件冒泡。

    v-on修饰符

    Vue为v-on提供了事件修饰符,在一定程度上简化了在方法中处理$event对象的逻辑。

    • .stop - 调用 event.stopPropagation()
    • .prevent - 调用 event.preventDefault()
    • .capture - 添加事件侦听器时使用 capture 模式。
    • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
    • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
    • .native - 监听组件根元素的原生事件。
    • .once - 只触发一次回调。
    • .left - (2.2.0) 只当点击鼠标左键时触发。
    • .right - (2.2.0) 只当点击鼠标右键时触发。
    • .middle - (2.2.0) 只当点击鼠标中键时触发。
    • .passive - (2.3.0) 以 { passive: true } 模式添加侦听器

    其中.capture表示该事件使用捕获模式(默认是冒泡模式),我们在用js原生添加事件监听使用如下方法,event是事件名称,如click;listener是为该事件注册的方法,useCapture是使用捕获模式还是冒泡模式。

    addEventListener(event, listener, useCapture)  
    

    useCapture默认是false的,也就是说默认使用冒泡事件,关于捕获和冒泡可以简单的理解,捕获模式是事件传播的方向是由根元素向目标前进,冒泡模式表示事件的传播方向是由目标元素向根元素前进。

    3.3.2 v-bind 属性绑定

    将表达式动态绑定到HTML元素/组件的属性上,缩写为:符号

    <div id="app">
      <a v-bind:href="url">百度</a>
        <!-- v-bind语法糖形式 -->
        <a :href="url">百度</a>
    </div>
    
    <script>
        var vm = new Vue({
        el:"#app",
        data:{
          url:"www.baidu.com",
        }
      });
    </script>
    

    v-bind的写法为v-bind:foo="bar",其中foo表示v-bind的参数,代表的是原生/组件的属性名,bar是预期值,即属性对应的值。

    参数时可选的,也就是说,v-bind可指定属性名也可不指定属性名,当不带参数时,要绑定到一个包含键值对的预期值对象,对象的key就是参数,对象key对应的value就是参数对应的值,如下:

    <img v-bind="{src:'a.png',alt='呵呵',height:32,width:32}">
    <!--等价于-->
    <img src="a.png" alt="呵呵" height="32" width="32">
    

    可以利用无参指令,一次性绑定多个属性。

    在向自定义组件绑定属性值时(通过props传递),数据是单向流动的,父组件向子组件传递数据,反之不行。常规的做法是,父组件通过子组件的props传值给子组件,同时在子组件上注册监听事件,子组件通过$emit方法触发父组件的监听方法,修改props对应的值。

    Vue提供了.sync修饰符,它实际上是一个语法糖,加了.sync的prop,vue会自动的为其生成一个事件监听,监听的名称为update:propName,如要子组件要修改prop的值,只需调用this.$emit('update:propName',value)即可,如下所示:

    <body>
        <div id="app">
            <h3>{{msg}}</h3>
            <!-- 引用组件,将msg赋值到组件的title属性上,
                并使用.sync修饰符(语法糖)添加v-on事件监听,
                该事件名称的格式为 update:propName,对应到这里是update:title-->
            <son :title.sync="msg" />
        </div>
    
        <!-- 使用x-template定义组件的HTML模板 -->
        <script type="text/x-template" id="son">
            <div>
                <p>{{title}}</p>
                <button @click="btnClicked">点我</button>
            </div>
        </script>
    
        <script>
            // 组件定义
            Vue.component('son', {
                //定义组件属性
                props:['title'],
                //找到id为son的HTML模板
                template: "#son",
                methods: {
                    btnClicked() {
                        //触发由.sync生成的upadte:title事件
                        this.$emit("update:title","子组件改变后的标题")
                    }
                },
            });
            var vm = new Vue({
                el: "#app",
                data: {
                    msg: "标题"
                }
            });
        </script>
    </body>
    

    注意:.sync修饰符后面不能接表达式。

    3.3.3 class与style增强绑定

    v-bind可以绑定元素的所有属性,其中最常用的是元素的class和style属性,不过当这两个属性的逻辑较为复杂时,使用字符串的方式返回该属性值体验非常差,所以,Vue增强了对class和style的绑定。

    class的绑定

    使用v-bind绑定class有对象语法和数字语法两种方式,在对象语法中key表示css的类名,value是个bool值,为true时,该key对应的类名加入class中,为false时,对应的项从class中移除。

    <div class="cell" :class="{'active':isActive,'error':isError}"></div>
    <script>
    var vm = new Vue({
      data:{
        isActive:true,
        isError:false,
      }
    });
    </script>
    

    上面的代码最终生成的class为:

    <div class="cell active"></div>
    

    v-bind:class生成的动态绑定类会与class属性中的类合并。这isActive的值为true,表示active会加入到class类中,isError为false,div的class中会移除error类。

    数组语法就相对简单,只要是数组中的字符串,都会加到class类中。在数组中可以使用三元表达式来动态的加载类。

    <div :class="[cell,isActive?'active':'',isError?'error':'']"></div>
    

    style内联样式绑定

    v-bind:sytle也有对象语法和数据语法,但是对象语法较为常见

    <div :style="cellStyle"></div>
    
    <script>
    var vm = new Vue({
      cellStyle:{
        fontSize:14+'px',
        color:'red'
      }
    });
    </script>
    

    结果为:

    <div style="color:red;font-size:14px;"></div>
    

    3.3.4 v-model双向绑定

    v-model指令用于在表单类元素上实现数据的双向绑定,它的本质也是监听表单类元素的输入事件,更新绑定的数据,vue对以下列表单元素做了处理。

    <!-- 输入框元素,绑定value属性,监听input事件 -->
    <input type="text" v-model="text">
    
    <!-- 文本域元素,绑定value属性,监听input事件 -->
    <textarea v-model="text"></textarea>
    
    <!-- 单个复选框时,v-model绑定一个bool值,该值指示checkbox添加/移除checked属性,监听change事件 -->
    <input type="checkbox" v-model="checked" id="checkbox1">
    <label for="cbox1">{{checkd}}</label>
    
    <!-- 
      多个复选框时,v-model绑定一个数组,与value属性配合使用,监听change事件。
        选中复选框时,将value值push到数组中,否则,将value值从数组中移除。     
    -->
    <input type="checkbox" id="jack" value="jack" v-model="names">
    <label for="jack">Jack</label>
    <input type="checkbox" id="john" value="john" v-model="names">
    <label for="john">John</label>
    
    <!-- 单选按钮,v-model绑定字符串,与value值配合使用-->
    <input type="radio" id="one" value="One" v-model="picked">
    <label for="one">One</label>
    <br>
    <input type="radio" id="two" value="Two" v-model="picked">
    <label for="two">Two</label>
    
    <!-- 
        选择框,v-model绑定value值,监听change事件,也分为单选和多选,
        因为实际使用select标签较少(样式不统一),这里不在演示
    -->
    

    修饰符

    • .lazy修饰符适用于输入框,设置后该修饰符后,v-model不再监听input事件,转而监听change事件。也就是当用户输入文本时,数据不是事实的同步,而是在文本框失去焦点或回车时更新。

      <input type="text" v-model.lazy="text">
      
    • .number将输入框的内容转换为数字类型

    • .trim自动过滤用户输入的首尾空白字符

    3.3.5 自定义组件的v-model

    v-model指令不仅可以在表单元素上实现数据的双向绑定,还可以在自定义组件上实现双向绑定。上面说了,v-model其实是语法糖,它的本质是绑定值到元素的指定属性上,并监听元素的输入事件,vue在表单元素中为我们实现了该功能,而在自定义组件上,这个功能需要我们自己实现。

    v-model在自定义组件上默认绑定value属性,监听input事件。

    <body>
        <div id="app">
            <h3>{{count}}</h3>
            <!-- 在自定义组件上使用v-model指令 -->
            <my-component v-model="count" />
        </div>
    
        <script type="text/x-template" id="myComponent">
            <div>
                <p>{{value}}</p>
                <button @click="btnClicked">点我</button>
            </div>
        </script>
    
        <script>
            // 组件定义
            Vue.component('myComponent', {
                //组件中定义value属性
                props:["value"],
                template: "#myComponent",
                methods: {
                    btnClicked() {
                        // 发送input事件
                        this.$emit("input",this.value+1);
                    }
                },
            });
            var vm = new Vue({
                el: "#app",
                data: {
                    count: 0,
                },
            });
        </script>
    </body>
    

    因为v-model默认绑定的是value属性,所以需要在组件中定义一个value。v-model指令默认监听input事件(自动生成),所以,执行通过$emit触发input事假即可。

    如果希望改变v-model在组件上默认的属性和事件,可以使用model选项,model选项接收一个对象,这个对象有两个属性:prop和event。prop的值默认是value,event的值默认是input。

    <script>
        Vue.component('myComponent',{
        model:{
          //修改v-model的默认绑定的value属性和input监听事件
          prop:"count",
          event:"countChange"
        },
        props:["count"],
        template:"#myComponent",
        methods:{
          btnClicked(){
            this.$emit("contChange",this.count+1);
          }
        }
      });
    </script>
    

    相关文章

      网友评论

          本文标题:Vue.js破冰系列-2HTML模板与内部指令

          本文链接:https://www.haomeiwen.com/subject/mimqsctx.html