zoukankan      html  css  js  c++  java
  • vue.js基础知识篇(2):指令详解

    第三章:指令

    1.语法

    指令以v-打头,它的值限定为绑定表达式,它负责的是按照表达式的值应用某些行为到DOM上。

    内部指令有v-show,v-else,v-model,v-repeat,v-for,v-text,v-el,v-html,v-on,v-bind,v-ref,v-pre,v-cloak,v-if。

    2.内部指令

    (1)控制元素的显示与否:v-if,v-show、v-else

        v-if是真实的条件渲染,根据表达式的true/false在DOM中生成或移除一个元素。

        第一,这个指令是惰性的,初始条件为false,那么什么都不做,直到条件第一次会真时才开始局部编译(编译会被缓存起来)。

        第二,它支持template包装元素,所以可以对多个元素进行切换(查看“v-if与v-show的使用”代码)。

        v-show是简单的基于css切换,当为false时,会看到对应的元素多了一个内联样式"style=display:none"。它不支持template语法。

        比较而言,v-if有更高的切换消耗,v-show有更高的初始渲染消耗。那么如果需要频繁的切换,则使用v-show较好。如果运行时条件不大可能改变,则使用v-if较好。

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title>v-if与v-hide</title>
     6 </head>
     7 <body class="native">
     8     <div id="example">
     9         <p v-if="greeting">Hello</p>
    10         <template v-if="ok">
    11             <h1>我是标题</h1>
    12             <p>我是段落1</p>
    13             <p>我是段落2</p>
    14         </template>
    15         <p v-show="greeting">Hello2</p>
    16     </div>
    17 <script src="js/vue.js"></script>
    18 <script>
    19     var exampleVM2=new Vue({
    20         el:"#example",
    21         data:{
    22             greeting:false,
    23             ok:true
    24         }
    25     })
    26 </script>
    27 </body>
    28 </html>
    v-if与v-show的使用

    效果如图

         v-else必须跟着v-if,充当else功能,相当于一个反义词的意思。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>v-else</title>
    </head>
    <body class="native">
    <div id="example">
        <p v-if="ok">我是对的</p>
        <p v-else="ok">我是错的</p>
    </div>
    <script src="js/vue.js"></script>
    <script>
        var exampleVM2=new Vue({
            el:"#example",
            data:{
                ok:false
            }
        })
    </script>
    </body>
    </html>
    v-else的使用

    (2)v-model

    v-model用在input、select、text、checkbox、radio表单控件元素上创建双向数据绑定。根据控件类型v-model自动选取正确的方法更新元素。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>v-else</title>
    </head>
    <body class="native">
    <div id="example">
         <form>
             姓名:
             <input type="text" v-model="data.name" placeholder="">
             <br/>
             性别:
             <input type="radio" id="man" value="One" v-model="data.sex">
             <label for="man"></label>
             <input type="radio" id="woman" value="Two" v-model="data.sex">
             <label for="woman"></label>
             <br/>
             兴趣:
             <input type="checkbox" id="book" value="book" v-model="data.interest">
             <label for="book">阅读</label>
             <input type="checkbox" id="swim" value="swim" v-model="data.interest">
             <label for="book">阅读</label>
             <input type="checkbox" id="game" value="game" v-model="data.interest">
             <label for="game">游戏</label>
             <input type="checkbox" id="song" value="song" v-model="data.interest">
             <label for="song">唱歌</label>
             <br/>
             身份:
             <select v-model="data.identity">
                 <option value="teacher" selected>教师</option>
                 <option value="doctor" >医生</option>
                 <option value="lawyer" >律师</option>
             </select>
         </form>
    </div>
    <script src="js/vue.js"></script>
    <script>
        var vm=new Vue({
            el:"#example",
            data:{
                data:{
                    name:"",
                    sex:"",
                    interest:[],
                    identity:""
                }
            }
        })
    </script>
    </body>
    </html>

    无一例外,表单元素的值使用value属性定义,v-model定义数据属性,其中checkbox是多选,所以是数组。

    除基本用法外,v-model指令有3个参数。

    (1)number

     代码演示:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>v-else</title>
    </head>
    <body class="native">
    <div id="example">
        <form>
            <h5>1.字符串形式的累加</h5>
           <input type="text"  id="age" v-model="age">
            <span>大6岁:{{age+3}}</span>
            <h5>2.数字类型的加法</h5>
           <input type="text"  number id="age2" v-model="age2">
            <span>大6岁:{{age2+6}}</span>
        </form>
    </div>
    <script src="js/vue.js"></script>
    <script>
        var vm=new Vue({
            el:"#example",
            data:{
            }
        })
    </script>
    </body>
    </html>

    显示效果:

    (2)lazy

    默认情况下,v-model在input事件中同步输入框的值与数据。当添加了lazy属性时,要等到change事件发生后才会将数据更新掉。那么什么是change事件呢。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>v-model</title>
    </head>
    <body class="native">
    <div id="example">
        <form>
            <h5>1.默认情况下,</h5>
            <input  v-model="msg" type="text" placeholder="" />
            <br/>
            {{msg}}
            <h5>2.lazy修饰词</h5>
            <br/>
            <input  v-model="msg2" type="text" placeholder="" lazy/>
            <br/>
            {{msg2}}
        </form>
    </div>
    <script src="js/vue.js"></script>
    <script>
        var vm=new Vue({
            el:"#example",
            data:{
                msg:"1.内容是在change事件后才改变的",
                msg2:"2.内容是在change事件后才改变的"
            }
        })
    </script>
    </body>
    </html>

    首先,看默认情况。也是我们平常看到的样子。当我们输入的同时,数据的值同时被更新掉。

    那么,lazy又是怎样的情况呢?

    当只是输入的时候,由于lazy的作用,数据并没有更新。

    当输入事件结束,点击其他地方,input框失去焦点,那么数据被更新掉了。

    (3)debounce

    用来设置延时同步的,以毫秒为单位,那就比lazy更加精确。要更新,需要一个等待时间咯。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>v-model</title>
    </head>
    <body class="native">
    <div id="example">
        <form>
            <h5>1.默认情况下,</h5>
            <input  v-model="msg" type="text" placeholder="" />
            <br/>
            {{msg}}
            <h5>2.lazy修饰词</h5>
            <input  v-model="msg2" type="text" placeholder="" lazy/>
            <br/>
            {{msg2}}
        </form>
    </div>
    <script src="js/vue.js"></script>
    <script>
        var vm=new Vue({
            el:"#example",
            data:{
                msg:"1.内容是在change事件后才改变的",
                msg2:"2.内容是在change事件后才改变的"
            }
        })
    </script>
    </body>
    </html>

    (3)v-for

    v-for指令基于源数据重复渲染元素。$index表示索引。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
       <div id="didi-navigator">
           <ul>
               <li v-for="tab in tabs">
                   {{tab.text}}
               </li>
           </ul>
           <ul>
               <li v-for="tab of tabs">
                   {{tab.text}}
               </li>
           </ul>
       </div>
       <script src="js/vue.js"></script>
       <script>
           new Vue({
               el:"#didi-navigator",
               data:{
                   tabs:[
                       {text:"巴士"},
                       {text:"快车"},
                       {text:"顺风车"},
                       {text:"出租车"},
                       {text:"代驾"}
                   ]
               }
           });
    
    
       </script>
    </body>
    </html>

    v-for="tab in tabs" 代码里tabs是数组名,tab是数组元素。除了in语法,也支持of关键字哦。

    v-for指令的源码分析:

    源码进入调试,逐行验证

    inMatch通过正则匹配,返回一个匹配数组,查看变量结果是

    if语句进入,itMatch为null,那么会把索引为1的元素tab作为数组元素的别名。

    this.expresssion当然是数组的名字。

    接下来是如果别名为空,那么可能会给出出错信息,就是控制台的报错了。

    v-for如何应用到组件上呢

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="didi-navigator">
        <one v-for="tab in tabs">
            <p>{{tab.text}}</p>
        </one>
    </div>
    <script src="js/vue.js"></script>
    <script>
        Vue.component("one",{
            template:""
        })
        new Vue({
            el:"#didi-navigator",
            data:{
                tabs:[
                    {text:"巴士"},
                    {text:"快车"},
                    {text:"顺风车"},
                    {text:"出租车"},
                    {text:"代驾"}
                ]
            }
        });
    
    
    </script>
    </body>
    </html>

    数组数据变动时如何检测?

    首先上DEMO

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <ul>
            <li v-for="tab in tabs">
                {{tab.text}}
            </li>
        </ul>
        <button v-on:click="pushData">向tabs数组添加内容</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
                tabs:[
                    {text:"巴士"},
                    {text:"快车"},
                    {text:"顺风车"},
                    {text:"出租车"},
                    {text:"代驾"}
                ]
            },
            methods:{
                pushData:function(){
                    this._data.tabs.push({text:"其他"});
                }
            }
        });
    
    
    </script>
    </body>
    </html>

    当我们点击按钮,那么“其他”这一项内容被添加到tabs数组里,并且v-for指令有被再次渲染。这也就是现在的问题,数组的数据变动,会触发试图更新。

    首先,使用original遍历的第一个push方法存起来。

    再次,把参数也就是DEMO里面的{text:"其他"},插入被观察的数组里。

    if (inserted) ob.observeArray(inserted);

    最后,在返回结果数组前触发更新。

    ob.dep.notify();

    所以,vue.js重写了这些方法,触发了一次notify。

    第二个是vue.js还增加了$set和$remove方法来观测变化。

    源代码:

    def(arrayProto, '$set', function $set(index, val) {
        if (index >= this.length) {
          this.length = Number(index) + 1;
        }
        return this.splice(index, 1, val)[0];
      });

    可以看到$set通过Splice来插入值。

    源代码:

    def(arrayProto, '$remove', function $remove(item) {
        /* istanbul ignore if */
        if (!this.length) return;
        var index = indexOf(this, item);
        if (index > -1) {
          return this.splice(index, 1);
        }
      });

    $remove也是通过splice来删除的。

    尽量避免直接设置数据绑定的数组元素,这些变化不会被vue.js检测到,从而不渲染视图。这时,可使用$set方法。 

    也可以使用filter、concat、slice方法,返回的数组将是一个不同的实例。

    filter的DEMO:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <ul>
            <li v-for="tab in tabs">
                {{tab.text}}
            </li>
        </ul>
        <button v-on:click="filter">添加内容</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
                tabs:[
                    {text:"巴士"},
                    {text:"快车"},
                    {text:"顺风车"},
                    {text:"出租车"},
                    {text:"代驾"}
                ]
            },
            methods:{
                filter:function(){
                    this._data.tabs=this._data.tabs.filter(function(item){
                         item.text=item.text+"添加字符串";
                        return item;
                    })
                }
            }
        });
    
    
    </script>
    </body>
    </html>

    concat方法的示例:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <button v-on:click="concatArr">合并数组</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            methods:{
                concatArr:function(){
                    var listdata= [{
                        "smallclassid": "21",
                        "smallclassname": "长沙菜"
                    }, {
                        "smallclassid": "6",
                        "smallclassname": "湘菜分类"
                    }];
    
                    var data = [{
                        smallclassid:0,
                        smallclassname:'全部'
                    }];
    
                    var newdata = data.concat(listdata);
                    console.log(newdata);
                }
            }
        });
    
    
    </script>
    </body>
    </html>

    track-by让数据替换更加高效

    默认情况下,v-for可能重新渲染整个列表,可能局部渲染,看数据的特征。那么,如果每个对象都有一个唯一的ID属性,那么用track-by特性给vue.js提示,以尽可能的复用已有实例。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <ul>
            <li v-for="tab in tabs" track-by="_uid">
                {{tab.text}}
            </li>
        </ul>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
                tabs:[
                    {text:"巴士",_uid:"01"},
                    {text:"快车",_uid:"02"},
                    {text:"顺风车",_uid:"03"},
                    {text:"出租车",_uid:"04"},
                    {text:"代驾",_uid:"05"}
                ]
            },
            methods:{
            }
        });
    
    
    </script>
    </body>
    </html>

    当替换数组tabs时,如果vue.js遇到一个包含_uid的新对象,那么vue.js就可以知道可以复用已有对象的作用域与DOM元素,避免重复工作。当然也可以使用track-by="$index",它强制v-for进入原位更新模式。

    但是DOM节点不再映射数组元素顺序的改变,不能同步临时状态(比如input元素的值),以及组件的私有状态。因此v-for包含input元素或者子组件时,则要小心使用track-by="$index"。

    vue.js不能检测到变化

    直接用索引设置元素,比如,vm.items[0]={}。

    修改数据的长度,vm.items.length=0.

    v-for遍历一个对象,每个重复的实例都有一个特殊的属性$key。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <ul>
           <li v-for="item in items">{{$key}}:{{item.msg}}</li>
        </ul>
        <ul>
           <li v-for="(key,item) in items">{{key}}:{{item.msg}}</li>
        </ul>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
                items:{
                    one:{
                        msg:"Hello"
                    },
                    two:{
                        msg:"DIDI FE"
                    }
                }
            }
        });
    
    
    </script>
    </body>
    </html>

    v-for也支持整数

    <div v-for="n in 10">Hi!{{$index}}</div>

    filterBy的使用

    语法:filterBy searchKey [in dataKey...]

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <input v-model="searchText" type="text"/>
        <ul>
            <li v-for="user in users | filterBy searchText in 'name'">{{user.name}}</li>
            <!--在users的name字段中过滤出我们想要的信息,并展示出来。-->
        </ul>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
                 users:[
                     {
                         name:"开车",
                         tag:"1"
                     },
                      {
                         name:"出租车",
                         tag:"2"
                     },
                      {
                         name:"顺风车",
                         tag:"3"
                     },
                      {
                         name:"专车",
                         tag:"4"
                     },
                     
                 ]
            }
        });
    
    
    </script>
    </body>
    </html>

    效果看起来很棒,像使用搜索引擎的感觉,当输入某个关键字后就会下拉展示相关搜索词。

    orderBy的使用

    语法:orderBy sortKey[reverseKey]

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <ul>
            <li v-for="user in users | orderBy field  reverse">{{user.name}}</li>
            <!--在ul标签中根据field代表的tag字段正序排列数据。-->
        </ul>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
                field:"tag",
                reverse:false,
                users:[
                    {
                        name:"快车",
                        tag:"1"
                    },
                    {
                        name:"出租车",
                        tag:"3"
                    },
                    {
                        name:"顺风车",
                        tag:"2"
                    },
                    {
                        name:"专车",
                        tag:"0"
                    },
    
                ]
            }
        });
    
    
    </script>
    </body>
    </html>

    (4)v-text、v-html和v-bind

    v-text指令类似于文本插值,会构建一个文本节点的。v-html插入html片段。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <h5>1.v-text指令</h5>
        <span v-text="msg"></span>
        <h5>2.v-html</h5>
        <span v-html="html"></span>
        
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
               msg:"v-text内容",
                html:"<span>html内容</span>"
            }
        });
    
    
    </script>
    </body>
    </html>

    v-bind可以绑定一个或者多个attribute属性。

    v-bind动态绑定src属性:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <img v-bind:src="imgSrc" width="121px" height="75px"/>
        <h5>简写为:</h5>
        <img :src="imgSrc"/>
    
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
                imgSrc:"images/1.jpg"
            }
        });
    
    
    </script>
    </body>
    </html>

    v-bind绑定class或者style时,支持变量,还支持数组或者对象哦。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            .yellow{
                color:yellow;
            }
            .size{
                font-size:20px;
            }
        </style>
    </head>
    <body>
    <div id="didi-navigator">
       <p :class="[classA,classB]">我是段落</p>
       <div v-bind="{id:div1}">我是div的内容</div>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
                classA:"yellow",
                classB:"size",
                div1:"div1"
            }
        });
    
    
    </script>
    </body>
    </html>

    v-bind没有参数时,直接绑定到一个对象。

    组件绑定,使用 v-bind:prop 绑定。到组件一章再说咯。

    (5)v-on指令

    v-on指令用于绑定事件监听器。普通元素,只能监听原生的DOM事件,并可使用变量$event访问原生DOM事件。使用在自定义元素的组件上,也可以监听子组件触发的自定义事件。

    和v-bind:src缩写为:src一样,v-on:click也可以简写,@click。除了事件参数外,还可以增加修饰符。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <!--方法处理器-->
        <button v-on:click="doThis">do this</button>
        <!--内联语句-->
        <button v-on:click="doThat('hello',$event')">do that</button>
        <!--缩写-->
        <button @click="doThis">缩写</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
                tabs:[
                    {text:"巴士"},
                    {text:"快车"},
                    {text:"顺风车"},
                    {text:"出租车"},
                    {text:"代驾"}
                ]
            },
            methods:{
            }
        });
    
    
    </script>
    </body>
    </html>

    .stop—调用event.stopPropagation(),阻止冒泡

    .prevent—调用event.preventDefault(),阻止默认行为。

    .captrue—添加事件侦听器使用capture模式。

    .self—只当事件是从侦听器绑定的元素本身触发时才触发回调。

    .{keyCode|keyAlias}—只在指定按键上触发回调。

    vue.js提供的键值有:esc是27,tab是9,enter是13,delete是8和46,up是38,left是37,right是39,down是40。

    enter键盘事件DEMO:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <input @keyup.13="onEnter" type="text"/>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
                tabs:[
                    {text:"巴士"},
                    {text:"快车"},
                    {text:"顺风车"},
                    {text:"出租车"},
                    {text:"代驾"}
                ]
            },
            methods:{
                onEnter:function(){
                    alert("enter键");
                }
            }
        });
    
    
    </script>
    </body>
    </html>

    (6)v-ref、v-el、v-pre和v-cloak指令

    v-ref是在父组件上注册一个子组件的索引,便于直接访问,必须提供参数id,也可以通过父组件的$ref对象访问子组件。(涉及到组件,略讲。)

    v-el为DOM元素注册一个索引,方便通过所属实例的$els访问这个元素,

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <span v-el:msg>hello</span>
        <button @click="getHello">获取hello的值</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
        new Vue({
            el:"#didi-navigator",
            data:{
            },
            methods:{
                getHello:function(){
                    var content=this.$els.msg.textContent;
                    alert(content);
                }
            }
        });
    
    
    </script>
    </body>
    </html>

    v-pre编译时跳过当前元素和它的子元素。

    v-cloak指令保持在元素上直到关联实例结束编译,类似于angularJS的ng-cloak。它用来未被编译的mustache标签直到实例准备完毕。它相当于增加了一个v-locak类。

    [v-cloak]{
    
       display:none;
    
    }

    3.自定义指令

    vue.js提供了两种方式,一种是Vue.directive注册全局指令,另一种是组件的directives注册一个局部指令。

    (1)钩子函数:bind是准备工作,update是值更新时的工作,unbind是清理工作。当只需要update函数时,可以传入一个函数替代定义对象。

    (2)钩子函数的this对象。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
         <div v-hello="msg"></div>
    </div>
    <script src="js/vue.js"></script>
    <script>
        Vue.directive("hello",{
           bind:function(){
               console.log(this);
           },
            update:function(value){
               console.log(value);
            }
        });
        var vm=new Vue({
            el:"#didi-navigator",
            data:{
                msg:"hello,everyone"
            }
        });
    
    
    </script>
    </body>
    </html>

    打印的this对象如截图

    所有的钩子函数都将被复制到实际的指令对象中,在钩子内this指向这个指令对象。this对象的各个属性含义:

    el-指令绑定的元素。

    vm-拥有该指令的上下文ViewModel。

    expression-指令的表达式,不包括参数和过滤器。

    arg-指令的参数。

    name-指令的名字,不包含前缀。

    modifiers-一个对象,包含指令的修饰符。

    descriptor-一个对象,包含指令的解析结果。

    注意:我们应当将这些属性视为只读,不要修改它们。也可以给指令对象添加自定义属性,但不要覆盖原有的内部属性。

    (3)指令支持传入一个JS对象字面量

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <div v-hello="{color:'white',text:'hello,,everyone!'}"></div>
    </div>
    <script src="js/vue.js"></script>
    <script>
        Vue.directive("hello",{
    
            update:function(value){
                console.log(value.color);
                console.log(value.text);
            }
    
        });
        var vm=new Vue({
            el:"#didi-navigator",
            data:{
            }
        });
    
    
    </script>
    </body>
    </html>

    同时当只需要一个update函数时,那么可以简写为

    Vue.directive("hello",function(value){
        console.log(value.color);
        console.log(value.text);
    );

    (4)当使用了字面修饰符时,它的值将按照普通字符串处理,update只调用一次,那么普通字符串不能响应数据变化。

    (5)元素指令

    元素指令的意思是指令当做一个元素来使用,元素指令可以看做一个轻量组件。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <hello class="demo" name="hi"></hello>
    </div>
    <script src="js/vue.js"></script>
    <script>
        Vue.elementDirective("hello",{
            bind:function(){
                console.info(this.el.className);
                console.info(this.el.getAttribute("name"));
            }
        });
        var vm=new Vue({
            el:"#didi-navigator",
            data:{
    
            }
        });
    
    
    </script>
    </body>
    </html>

    (6)高级选项:params

    自定义指令接受一个params数组,指定一个特性列表。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <hello class="demo" name="hi" a="ddd" b="cccc">hello内容</hello>
    </div>
    <script src="js/vue.js"></script>
    <script>
        Vue.elementDirective("hello",{
            params:["a","b"],
            bind:function(){
                console.log(this.params)
            }
        });
        var vm=new Vue({
            el:"#didi-navigator",
            data:{
    
            }
        });
    
    
    </script>
    </body>
    </html>

    params也支持动态属性,this.params[key]会自动保持更新,使用paramWathers对象来定义的。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        {{someValue}}
        <my-directive class="hello" name="hi" v-bind:a="someValue"></my-directive>
        <input type="text" v-model="someValue"/>
    </div>
    <script src="js/vue.js"></script>
    <script>
        Vue.elementDirective("my-directive",{
            params:["a"],
            paramWatchers:{
                a:function(val,oldVal){
                    console.log("a changed");
                }
            }
            //param监听a变量。如果a发生变化,那么就会执行响应的代码咯
        });
        var vm=new Vue({
            el:"#didi-navigator",
            data:{
                someValue:"value"
            }
        });
    
    
    </script>
    </body>
    </html>

    (7)高级选项:deep

    当自定义指令使用在一个对象上,当对象内部属性变化时要出发update(响应变化),那么需要在指令对象上指定deep:true。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        {{someValue}}
        <div v-demo="a"></div>
        <span>{{a.b}}</span>
        <button @click="change">change</button>
    </div>
    <script src="js/vue.js"></script>
    <script>
        Vue.directive("demo",{
            deep:true,
            update:function(obj){
                console.log(obj.b)
            }
        });
        var vm=new Vue({
            el:"#didi-navigator",
            data:{
                a:{
                    b:2
                }
            },
            methods:{
                change:function(){
                    vm.a.b=4;
                }
            }
        });
    
    
    </script>
    </body>
    </html>

    (8)高级选项:twoWay

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        自定义组件:<input v-example="a.b.c"/>
        父作用域:{{a.b.c}}
    </div>
    <script src="js/vue.js"></script>
    <script>
        Vue.directive('example', {
            twoWay: true,
            bind: function () {
                this.handler = function () {
                    // 将数据写回 vm
                    // 如果指令这样绑定 v-example="a.b.c"
                    // 它将用给定值设置 `vm.a.b.c`
                    this.set(this.el.value)
                }.bind(this)
                this.el.addEventListener('input', this.handler)
            },
            unbind: function () {
                this.el.removeEventListener('input', this.handler)
            }
        });
        var vm=new Vue({
            el:"#didi-navigator",
            data:{
                a:{
                    b:{
                        c:2
                    }
                }
            }
        });
    </script>
    </body>
    </html>

    (9)高级选项:acceptStatement

    允许接收内联语句。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
    </head>
    <body>
    <div id="didi-navigator">
        <div v-demo="a++"></div>
        {{a}}
    </div>
    <script src="js/vue.js"></script>
    <script>
        Vue.directive("demo",{
            acceptStatement:true,
            update:function(fn){
                console.log(fn.toString());
                fn();
    //传入值是一个函数,调用它,就是调用"a++"语句。 } });
    var vm=new Vue({ el:"#didi-navigator", data:{ a:5 } }); </script> </body> </html>

    (10)高级选项:terminal

    遇到terminal指令会停止遍历这个元素的后代元素,然后由这个指令接管编译这元素及其后代元素的任务。v-if和v-for都是terminal指令。

    (11)高级选项:priority

    给指令指定一个优先级。没有,默认是1000,terminal指令默认是2000.优先级高的指令处理的早,一样的按顺序处理,但却不能保证在所有浏览器都一直。

    另外,流程控制指令v-if和v-for在编译过程中始终有最高的优先级。

    4.源码实现

    内部指令是如何实现的?

    v-model指令的实现源码

    var model = {
    
        priority: MODEL,
        twoWay: true, 
        handlers: handlers,
        params: ['lazy', 'number', 'debounce'],
    
        /**
         * Possible elements:
         *   <select>
         *   <textarea>
         *   <input type="*">
         *     - text
         *     - checkbox
         *     - radio
         *     - number
         */
    
        bind: function bind() {
          // friendly warning...
          this.checkFilters();
          if (this.hasRead && !this.hasWrite) {
            'development' !== 'production' && warn('It seems you are using a read-only filter with ' + 
    'v-model="' + this.descriptor.raw + '". ' + 'You might want to use a two-way filter to ensure correct behavior.', this.vm); } var el = this.el; var tag = el.tagName; var handler; if (tag === 'INPUT') { handler = handlers[el.type] || handlers.text; } else if (tag === 'SELECT') { handler = handlers.select; } else if (tag === 'TEXTAREA') { handler = handlers.text; } else { 'development' !== 'production' && warn('v-model does not support element type: ' + tag, this.vm); return; } el.__v_model = this; handler.bind.call(this); this.update = handler.update; this._unbind = handler.unbind; }, /** * Check read/write filter stats. */ checkFilters: function checkFilters() { var filters = this.filters; if (!filters) return; var i = filters.length; while (i--) { var filter = resolveAsset(this.vm.$options, 'filters', filters[i].name); if (typeof filter === 'function' || filter.read) { this.hasRead = true; } if (filter.write) { this.hasWrite = true; } } }, unbind: function unbind() { this.el.__v_model = null; this._unbind && this._unbind(); } };

    表单元素radio的实现

    var radio = {
    
        bind: function bind() {
          var self = this;
          var el = this.el;
    
          this.getValue = function () {
            // value overwrite via v-bind:value
            if (el.hasOwnProperty('_value')) {
              return el._value;
            }
            var val = el.value;
            if (self.params.number) {
              val = toNumber(val);
            }
            return val;
          };
    
          this.listener = function () {
            self.set(self.getValue());
          };
          this.on('change', this.listener);
    
          if (el.hasAttribute('checked')) {
            this.afterBind = this.listener;
          }
        },
    
        update: function update(value) {
          this.el.checked = looseEqual(value, this.getValue());
        }
      };
  • 相关阅读:
    Day01 基本SQL SELECT
    Java IO流
    排序: 选择排序
    Java的数据存储机制
    Java反射基础笔记
    学习面向对象的三条主线之三 面向对象的三大特征 关键字
    学习面向对象的三条主线之二 面向对象的三大特征
    Oracle数据库知识积累
    office技巧
    如何读书
  • 原文地址:https://www.cnblogs.com/chenmeng2062/p/7067081.html
Copyright © 2011-2022 走看看