• Vue入门详解


    一,VUE的核心

    1.数据驱动(mvvm模型,只要改变model的数据,视图层就会自动更新)

    2.视图组件: 把整一个网页的拆分成一个个区块,每个区块我们可以看作成一个组件。网页由多个组件拼接或者嵌套组成。

    二,使用vue

    1.引入vue之后就可以进行下面的步骤

    2.创建语法(创建 ,设置数据,挂载,渲染)

    var vm = new Vue({})//参数是一个对象
     

    三、定义一个vue实例的4个常用选项

    1.过滤器 filter属性:

    例如我们从后台取出来的数据都是带有小数位的,但是需求上面要求的是只展示整数部分,这个时候我们就可以用过滤器属性了。我们先创建一个实例,数据中的格式都是带有小数点的,此时我们创建filters属性(记住此属性的值是一个对象,所以":"之后要带上大括号“{}”,在filters里面我们写一个转换函数,这个函数的功能就是将传参转换为整数后返回,此处用的是es6的语法),写好函数后要怎么使用呢?我们在div里面,将参数用管道符号"|"传给toInt函数,这样就可以了!

    <div id="app6">

        数字1:{{num1 | toInt}}

        数字2:{{num2 | toInt}}

        数字3:{{num3 | toInt}}

    </div>



    let app6 = new Vue({

        el:'#app6',

        data:{

            num1:123.4,

            num2:520.1314,

            num3:1314.520

        },

        filters:{

            toInt(value){

                    return parseInt(value);

                 }

        }

    });
    4.计算属性computed

     

    有时候,我们拿到一些数据,需要经过处理计算后得到的结果,才是我们要展示的内容。

    比如:我们有三个数,但是需要展示的是三个数字之和。这种情况,就适合用我们的计算属性computed。(可以直接调用计算属性里面的值。)

    let app6 = new Vue({

        el:'#app6',

        data:{

            num1:123.4,

            num2:520.1314,

            num3:1314.520

        },

        filters:{

            toInt(value){

                    return parseInt(value);

                 }

        },

        computed:{

            sum(){

                return parseInt(this.num1+this.num2+this.num3);

            }

        }

    });
    计算属性computed的定义语法和过滤器filters类似,但是用法不一,如下:

    总和是{{sum}}

    我们提供的函数将用作属性app6.sum 的 getter 函数:

    计算属性拥有getter和setter两个属性,也就是我们可以设置这两个属性,例如

    computed: {

      fullName: {

        // getter

        get: function () {

          return this.firstName + ' ' + this.lastName

        },

        // setter

        set: function (newValue) {

          var names = newValue.split(' ')

          this.firstName = names[0]

          this.lastName = names[names.length - 1]

        }

      }

    }

    那么每次运行fullname的时候,就是使用getter,返回一个值,而每次执行fullname = ""的时候,就会执行set里面的函数,将firstname和lastname都进行改变。

     

    需要注意的是,sum的值是依赖data中的数据num1,num2,num3的,一旦它们发生了变化,sum的值也会自动更新

     

    5.methods  方法

    顾名思义,在methods中,我们可以定义一些方法,供组件使用。

    比如,我们定义一个数据a,当用户点击按钮的时候,a的值加1。这种情况,我们可以利用methods来实现。

     

    定义一个plus( )的方法在methods中,下面我们把plus( ) 绑定在按钮的点击事件上

    let vm = new Vue({

        //挂载元素

      el:'#app',

        //实例vm的数据

      data:{

             a:0

        },

        //方法

      methods:{

            plus(){

                return this.a++;

            }

        }

    });

    <div id="app">

        {{ a }}

        <button v-on:click="plus">加1</button>

      </div>
      

     

    1. 计算属性computed和方法methods都能用于计算,说说两者的区别?

    答: 假设我们计算一个值sum,它是经过a和b相加得到的结果。

          如果我们使用计算属性的方式来实现得到sum,由于计算属性computed是基于它的依赖进行缓存的,也就是sum的值只有在a或者b的值发生变化的时候才会重新计算求值。这就意味着,倘若a和b的值不发生改变,即使重新渲染或者多次访问计算属性中的sum,都无需重新计算,节省运算开销。

            反观,如果我们用methods方法来定义一个求和函数sum( )来计算a和b的求和,如果多个地方需要渲染a和b的和,我们则需要多此调用sum( )方法,这样的后果就是多次重复执行函数,造成不必要的运算开销。

     

     

    6.watch 观察

    watch选项是Vue提供的用于检测指定的数据发生改变的api,也就是说,当这个数据发生变化的时候会触发对应的函数。

    上面点击按钮a的值加1的例子,不就是数据发生变化了吗?我们就用watch选项来监听数字a是否发生了变化,如果了监听到了变化,我们就在控制台输入以下a的最新值。

    在上个例子的代码基础上,我们加上watch部分的代码

    let vm = new Vue({

        //挂载元素

      el:'#app',

        //实例vm的数据

      data:{

             a:0

        },

        //方法

      methods:{

            plus(){

                return this.a++;

            }

        },

        //观察

      watch:{

            a(){

              console.log(`有变化了,最新值:`);

              console.log(this.a);

            }

        }

    });
    最后一部分watch就是我们新加的代码,a( ) 表示我们要观察监听的就是数据a,

     

    四.vue实例的生命周期:


    不要在选项属性或者回调函数上面使用箭头函数,因为箭头函数是和父级上下文绑定在一起的。

     

    1. beforeCreate(还没创建实例)

    此阶段为实例初始化之后,此时的数据观察和事件配置都没好准备好。

    我们试着console一下实例的数据data和挂载元素el,代码如下:

      

    <div id="app7">{{name}}</div>

      <script>

        let app7 = new Vue({

          el:"#app7",

            data:{

             name:"krys"

          },

          beforeCreate(){

             console.log('即将创建');

             console.log(this.$data);

             console.log(this.$el);

          }

        });

    </script>

     

    得到的结果是:

     

    此时的实例中的data和el还是undefined,不可用的。

     

    2. created(创建实例)

    beforeCreate之后紧接着的钩子就是创建完毕created,我们同样打印一下数据data和挂载元素el,看会得到什么?

    在上一段代码的基础上,加上下面这段代码:

    created(){

        console.log('创建完毕');

        console.log(this.$data);

        console.log(this.$el);

    }
    我们看到打印的结果:

     

    此时,我们能读取到数据data的值,但是DOM还没生成,所以属性el还不存在,输出$data为一个Object对象,而$el的值为undefined。

     

    3. beforeMount(生成dom)

    上一个阶段我们知道DOM还没生成,属性el还为undefined,那么,此阶段为即将挂载,我们打印一下此时的$el是什么?

    增加一下代码:

    beforeMount(){

        console.log('即将挂载');

        console.log(this.$el);

    }
    我们看到打印结果:

     

    此时的$el不再是undefined,而是成功关联到我们指定的dom节点<div id=”app”></div>,但此时{{ name }}还没有被成功地渲染成我们data中的数据。没事,我们接着往下看。

    4. mounted(将实例与dom相连)

    mounted也就是挂载完毕阶段,到了这个阶段,数据就会被成功渲染出来,我们编写mounted的钩子,打印$el 看看:

     

    mounted(){

        console.log('挂载完毕');

        console.log(this.$el);

    }

    打印结果

     

     

    如我们所愿,此时打印属性el,我们看到{{ name }}已经成功渲染成我们data.name的值:“krys”。

    5. beforeUpdate(实例更新前)

    我们知道,当修改vue实例的data时,vue就会自动帮我们更新渲染视图,在这个过程中,vue提供了beforeUpdate的钩子给我们,在检测到我们要修改数据的时候,更新渲染视图之前就会触发钩子beforeUpdate。

    html片段代码我们加上ref属性,用于获取DOM元素。

    <div ref="app7" id="app7">

        {{name}}

    </div>

    Vue实例代码加上beforeUpdate钩子代码:

    beforeUpdate(){

      console.log('=即将更新渲染=');

      let name = this.$refs.app7.innerHTML;

      console.log('name:'+name);

    },
    我们试一下,在控制台修改一下实例的数据name,在更新渲染之前,我们打印视图中文本innerHTML的内容会是多少:

     

     

    我们在控制台把app.name的值从原来的 “krys” 修改成 “斑马”,在更新视图之前beforeUpdate打印视图上的值,结果依然为原来的值:“krys”,表明在此阶段,视图并未重新渲染更新。

    6. updated(实例更新完成之后)

    此阶段为更新渲染视图之后,此时再读取视图上的内容,已经是最新的内容,接着上面的案例,我们添加钩子updated的代码,如下:

    updated(){

      console.log('==更新成功==');

      let name = this.$refs.app7.innerHTML;

      console.log('name:'+name);

    }
    结果如下:

     

    大家注意两个不同阶段打印的name的值是不一样,updated阶段打印出来的name已经是最新的值:“斑马”,说明此刻视图已经更新了。

    7. beforeDestroy

    调用实例的destroy( )方法可以销毁当前的组件,在销毁前,会触发beforeDestroy钩子。

    8. destroyed

    成功销毁之后,会触发destroyed钩子,此时该实例与其他实例的关联已经被清除,它与视图之间也被解绑。

    案例:我们通过在销毁前通过控制台修改实例的name,和销毁之后再次修改,看看情况。

    beforeDestroy(){

       console.log('销毁之前');

    },

    destroyed(){

       console.log('销毁成功');

    }
    效果如下图:

     

     

    销毁之前,修改name的值,可以成功修改视图显示的内容为:“更新视图”,一旦效用实例的$destroy( )方法销毁之后,实例与视图的关系解绑,再修改name的值,已于事无补,视图再也不会更新了,说明实例成功被销毁了。

     

    9. actived

    keep-alive组件被激活的时候调用。

    10. deactivated

    keep-alive 组件停用时调用。

    关于keep-alive组件的激活和停用

     

    以后最为常用的钩子是:created 成功创建。

     

     五.在html中绑定数据

    1.Mustache语法:{{}}的写法,在里面写入需要渲染的数据

    <div id ="app7">{{name}}</div>
    let app7 = new Vue({

        el:"#app7",

        data:{

            name:"krys"

        }

    }
     

    2.绑定纯html

    如果上面的例子中,name的值是一些html语句,如果单纯的是像上面这样写的话,会原样输出,不会转化为html语句,例如

    <div id="app8">

        {{name}}

    </div>

    <script>

        let app8  = new Vue({

            el:"#app8",

            data:{

                name:"<strong>krys</strong>"

            }

        })

    </script>


    并不能得到我们想要的效果,这时候需要用到vue提供的v-html指令,

    <div id="app8" v-html="name">

        {{name}}

    </div>

    <script>

        let app8  = new Vue({

            el:"#app8",

            data:{

                name:"<strong>krys</strong>"

            }

        })

    </script>
     

     

    3.绑定属性:attr或者v-bind:attr,其中attr是属性名, 需要注意的是:当渲染的属性值是布尔值的时候(true和false),效果就不一样了,并不是简单地将true或者false渲染出来,而是当值为false的时候,属性会被移除。

    <div id="app9">

       <a v-bind:href="link" >baidu</a>

    </div>

    let app9 = new Vue({

        'el':"#app9",

        data:{

            link:"https://www.baidu.com"

        }

    })

    4.支持javascript表达式

    值得注意的是,只能包含单个表达式,多个表达式组成的不会生效!

    上面讲到的都是将数据简单地绑定在视图上,但事实上,vue还支持我们对数据进行简单的运算:javascript表达式支持。

    <div id="app">{{ num+3 }}</div>

    <script>

    let app = new Vue({

        el:"#app",

        data:{

            num:2

        }

    });

    </script>

    <div id="app">

      <a :href="'http://'+host">hello官网</a>

    </div>

    <script>

    let app = new Vue({

        el:"#app",

        data:{

            host:'hello.com'

        }

    });

    </script>

    六,必须要掌握的指令

    1.v-text指令

    v-text指令用于更新标签包含的文本,他的作用跟双大括号{{}}一样,

    <div id="app">

        <p v-text="msg"></p>

    </div>

    <script>

        let app = new Vue({

            el:"#app",

            data:{

                msg:"hello,vue"

            }

        })

    </script>


    2.v-html指令(执行html)

    这个指令帮助我们绑定一些包含html代码的数据在视图上。

    <div id="app">

        <p v-html="msg"></p>

    </div>

    <script>

        let app = new Vue({

            el:"#app",

            data:{

                msg:"<b>hello,vue</b>"

            }

        })

    </script>


     

    3.v-show指令

    控制元素的css中display属性,v-show指令的取值为true/false,分别对应着显示/隐藏。

    <div id="app">

        <p v-show="show1">我是true</p>

        <p v-show="show2">我是false</p>

    </div>

    <script>

        let app = new Vue({

            el:"#app",

            data:{

               show1:true,

                show2:false

            }

        })

    </script>


    4.v-if(其实就相当于if)

    v-if指令的取值也是true或者false,它控制元素是否需要被渲染出来,设置为true的<p>标签,成功渲染出来,而设置为false的<p>标签,直接被一行注释代替了,并没有被解析渲染出来。

    那么如何区别v-show和v-if这两个指令呢?就是一个会被渲染出来,一个不会。记住一个很重要的点:如果需要频繁切换显示/隐藏的,就用 v-show ;如果运行后不太可能切换显示/隐藏的,就用 v-if 。

    <div id="app">

        <p v-if="if_1">我是true</p>

        <p v-if="if_2">我是true</p>

    </div>

    <script>

        let app = new Vue({

            el:"#app",

            data:{

               if_1:true,

                if_2:false

            }

        })

    </script>
     

     

    5.v-else指令(相当于else)

     

    1) if和else指令,在一般的变成语言都是结对出现,在vue里也不例外,它没有对应的值,但是要求前一个兄弟节点必须使用v-if指令。(没有if哪来的else),如果v-if的值为true那么else里面的东西将不会渲染出来

    <div id="app">

        <p v-if="if_1">我是if</p>

        <p v-else="">我是else</p>

    </div>

    <script>

        let app = new Vue({

            el:"#app",

            data:{

               if_1:true,

            }

        })

    </script>



    <script>

        let app = new Vue({

            el:"#app",

            data:{

               if_1:false,

            }

        })

    </script>

     


     

     

    3)v-else-if

    <div v-if="type === 'A'">

      A

    </div>

    <div v-else-if="type === 'B'">

      B

    </div>

    <div v-else-if="type === 'C'">

      C

    </div>

    <div v-else>

      Not A/B/C

    </div>
    v-else,v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。

     

    4)使用key来管理可复用的元素

    Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:

    <template v-if="loginType === 'username'">

      <label>Username</label>

      <input placeholder="Enter your username">

    </template>

    <template v-else>

      <label>Email</label>

      <input placeholder="Enter your email address">

    </template>





    new Vue({

        el: '#app',

        data: {

            loginType:"username"

        },

        methods:{

            change:function () {

                this.loginType == "username" ? this.loginType = "email":this.loginType = "username";

            }

        }

    });

    那么在上面的代码中切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,<input> 不会被替换掉——仅仅是替换了它的 placeholder。

     

    这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可。

    <template v-if="loginType === 'username'">

      <label>Username</label>

      <input placeholder="Enter your username" key="username-input">

    </template>

    <template v-else>

      <label>Email</label>

      <input placeholder="Enter your email address" key="email-input">

    </template>





    new Vue({

        el: '#app',

        data: {

            loginType:"username"

        },

        methods:{

            change:function () {

                this.loginType == "username" ? this.loginType = "email":this.loginType = "username";

            }

        }

    });

    现在,每次切换时,输入框都将被重新渲染。

    6.v-for指令

    v-for指令用来进行循环,不但可以迭代数组,还可以迭代对象和整数,下面例子中,index是索引,item是值(有先后顺序,第一个是值,第二个是索引)。可以看到生成了4个div

    <div id="app">

       <div v-for="(item,index) in list">{{index}}.我们的成员有:{{item}}</div>

    </div>

    <script>

        let app = new Vue({

            el:"#app",

            data:{

               list:['krys','zebra','tom','jackson']

            }

        })

    </script>


     

    用for in或者for of遍历对象的时候,第一个参数是值,第二个参数是键名,第三个参数是索引。在遍历对象时,是按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下是一致的。

    <ul id="v-for-object" class="demo">

        <li v-for="(value,key) of object">

           {{key}}-- {{value}}

        </li>

    </ul>





    new Vue({

        el:"#v-for-object",

        data:{

            object:{

                firstName:"liang",

                lastName:"krys",

                fullName:"krys liang",

                age:23

            }

        }

    });


    尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。因为它是 Vue 识别节点的一个通用机制。

    当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index" 。

    这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。

    为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有的唯一 id。这个特殊的属性相当于 Vue 1.x 的 track-by ,但它的工作方式类似于一个属性,所以你需要用 v-bind 来绑定动态值 (在这里使用简写):

    <div v-for="item in items" :key="item.id">

      <!-- 内容 -->

    </div>

     

     

    当对数组进行下面两个操作的时候,数组不会进行响应式的变化,也就是说,即使进行了下面的两个操作,数据也不会重新渲染

    1)利用索引直接设置一个值

    2)修改数组的长度

     

    var vm = new Vue({

      data: {

        items: ['a', 'b', 'c']

      }

    })

    vm.items[1] = 'x' // 不是响应性的

    vm.items.length = 2 // 不是响应性的

     

    为了解决第一类问题,改用下面两个方法来实现同样的效果

    Vue.set(vm.items, indexOfItem, newValue)

     

    vm.items.splice(indexOfItem, 1, newValue)

     

    vm.$set(vm.items, indexOfItem, newValue)

     

     

    为了解决第二类问题,可以改用下面的方法

    vm.items.splice(newLength)

     

     

    还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:

    var vm = new Vue({

      data: {

        a: 1

      }

    })

    // `vm.a` 现在是响应式的

     

    vm.b = 2

    // `vm.b` 不是响应式的

    对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:

    var vm = new Vue({

      data: {

        userProfile: {

          name: 'Anika'

        }

      }

    })

    你可以添加一个新的 age 属性到嵌套的 userProfile 对象:

    Vue.set(vm.userProfile, 'age', 27)

    你还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:

    vm.$set(vm.userProfile, 'age', 27)

    有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性

    vm.userProfile = Object.assign({}, vm.userProfile, {

      age: 27,

      favoriteColor: 'Vue Green'

    })

     

     

    当v-for和v-if处于同一个节点的时候,v-for的优先级比v-if要高,这就是说v-if将分别运行于每个v-for循环中。

    <li v-for="todo in todos" v-if="!todo.isComplete">

      {{ todo }}

    </li>

     

    当在自定义组件中使用 v-for 时,key 现在是必须的。然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要用 props 

    <my-component

      v-for="(item, index) in items"

      v-bind:item="item"

      v-bind:index="index"

      v-bind:key="item.id">

     

    </my-component>

     

     

     

     

    7.v-bind指令

    v-bind指令用于动态绑定DOM元素的属性(例如href,src等),其中:href等价于v-bind:href

    <div id="app">

        <a :href="link">百度官网</a>

    </div>

    <script>

        let app = new Vue({

            el:"#app",

            data:{

               link:"https://www.baidu.com"

            }

        })

    </script>


     

    8.v-on指令

     

    v-on指令相当于绑定事件的监听器,绑定的事件触发了,可以指定事件的处理函数。

    <div id="app">

       <button v-on:click="say('krys')">调用say函数</button>

    </div>

    <script>

        let app = new Vue({

            el:"#app",

            methods:{

                say(name){

                    console.log("hello"+name);

                }

            }

        })

    </script>


    在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

    为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

     

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

    .stop ==stopPropagation->阻止事件继续传播

    .prevent == preventDefault ->去除默认行为(form提交不再重载页面)

    .capture ->添加事件监听器时使用事件捕获模式,也就是元素自身出发的事件先在此处理,然后才交给内部元素进行处理

    .self ->只在event.target是当前元素自身时才触发处理函数

    .once->点击事件只会触发一次

    .passive->passive是不拦截默认事件

    使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

    Vue 还对应 addEventListener 中的 passive 选项提供了 .passive 修饰符。

    <!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->

    <!-- 而不会等待 `onScroll` 完成  -->

    <!-- 这其中包含 `event.preventDefault()` 的情况 -->

    <div v-on:scroll.passive="onScroll">...</div>

    这个 .passive 修饰符尤其能够提升移动端的性能。

    不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。

     

    按键修饰符

     

    在监听键盘事件的时候,经常需要检查常见的键值。

    <input v-on:keyup.13="submit"><!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->

     

     

    上面的keycode是按键的编码,等同于下面

     

    <!-- 同上 -->

    <input v-on:keyup.enter="submit">

     

    <!-- 缩写语法 -->

    <input @keyup.enter="submit">

     

    所有的按键别名

    .enter

    .tab

    .delete (捕获“删除”和“退格”键)

    .esc

    .space

    .up

    .down

    .left

    .right

     

    可以通过全局 config.keyCodes 对象自定义按键修饰符别名:

    // 可以使用 `v-on:keyup.f1`

    Vue.config.keyCodes.f1 = 112

     

    9、v-model指令(在input、textarea、select这三个元素上创建双向数据绑定。)

    v-model会忽略所有表单元素的value、checked、selected特性的初始值而总是将vue实例的数据作为数据来源。应该在data中声明初始值,

    这个指令是非常重要且非常常用,一般用在表单输入,它帮助我们轻易的实现表单控件和数据的双向绑定,相对于之前的手动更新DOM,简直就是我们的天使。要注意的是v-model中的值,data中变量的名字,p标签中的{{}}里面的值,三者要一致。因为就是iniput输入的值被绑定到了data中,然后输出到p中。

    <div id="app">

       <input v-model="msg" type="text">

        <p>上面输入的值是:{{msg}}</p>

    </div>

    <script>

        let app = new Vue({

            el:"#app",

           data:{

                msg:""

           }

        })

    </script>
     

     

    10.v-once指令

    这个指令的特点是只渲染一次,后面元素中的数据再更新变化,都不会重新渲染。

    <div id="app">

        <input v-model="msg"  type="text">

        <p v-once>你输入:{{ msg }}</p>

    </div>

    <script>

        let app = new Vue({

            el:"#app",

            data:{

                msg:'hello,我是krys啊哈哈哈哈'

            },

            watch:{

              msg(){

                  console.log(this.msg);

              }

            }

        });

    </script>


     

    select元素中,如果表达式的初始值没有匹配任何选项(也就是没有指定默认值),select元素将会被渲染为未被选中的状态。这个状况下面再ios就会引发一些bug。所以,在option中提空一个空的禁用的选项。

    <div id="example-5">

      <select v-model="selected">

        <option disabled value="">请选择</option>

        <option>A</option>

        <option>B</option>

        <option>C</option>

      </select>

      <span>Selected: {{ selected }}</span>

    </div>



    new Vue({

      el: '#example-5',

      data: {

        selected: ''

      }

    })
    多选的时候,象下面那样绑定到一个数组

    <div id="example-6">

      <select v-model="selected" multiple style=" 50px;">

        <option>A</option>

        <option>B</option>

        <option>C</option>

      </select>

      



      <span>Selected: {{ selected }}</span>

    </div>

    new Vue({

      el: '#example-6',

      data: {

        selected: []

      }

    })



    <select v-model="selected">

      <option v-for="option in options" v-bind:value="option.value">

        {{ option.text }}

      </option>

    </select>

    <span>Selected: {{ selected }}</span>

    new Vue({

      el: '...',

      data: {

        selected: 'A',

        options: [

          { text: 'One', value: 'A' },

          { text: 'Two', value: 'B' },

          { text: 'Three', value: 'C' }

        ]

      }

    })

    <!-- 当选中时,`picked` 为字符串 "a" -->

    <input type="radio" v-model="picked" value="a">

     

    <!-- `toggle` 为 true 或 false -->

    <input type="checkbox" v-model="toggle">

     

    <!-- 当选中第一个选项时,`selected` 为字符串 "abc" -->

    <select v-model="selected">

        <option value="abc">ABC</option>

    </select>

     

     

    data: {

        picked:this.value,

        toggle:"false",

        selected:this._value

    },

     

     

     

    修饰符

    1).lazy,只有当值改变的时候才进行同步

    <!-- 在“change”时而非“input”时更新 -->

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

    2).number

    将用户的输入值自动转换为数值类型

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

    3).trim

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

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

     

     

     

     

     

     

    七.vue中的组件

    1)一个组件的data选项必须是一个函数,所有的数据放在return里面。只有这样,每个实例才可以维护一份被返回对象的独立的拷贝。

    data: function () {

      return {

        count: 0

      }

    }

     

    2)注册组件

    全局注册:Vue.component.这样全局注册之后,可以用在任何新创建的vue实例中

    局部注册:先声明组件,然后再实例中的components中使用组件。

    局部注册中可以通过一个不同的JavaScript对象来定义组件

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

    var ComponentB = { /* ... */ }

    var ComponentC = { /* ... */ }

     

    然后再components选项中定义想要使用的组件

    new Vue({

      el: '#app',

      components: {

        'component-a': ComponentA,

        'component-b': ComponentB

      }

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

    注意局部注册的组件在其子组件中不可用。

     

    例如,如果你希望 ComponentA 在 ComponentB 中可用,则你需要这样写:

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



    var ComponentB = {

      components: {

        'component-a': ComponentA

      },

      // ...

    }
     

    在模块系统中局部注册

    在这些情况下,我们推荐创建一个 components 目录,并将每个组件放置在其各自的文件中。

    然后你需要在局部注册之前导入每个你想使用的组件。例如,在一个假设的 ComponentB.js 或 ComponentB.vue 文件中:

    import ComponentA from './ComponentA'

    import ComponentC from './ComponentC'



    export default {

      components: {

        ComponentA,

        ComponentC

      },

      // ...

    }
    这样就可以在conponentB中的模板使用componentA和c了。

     

     

     

    在模块中,基础组件的自动化全局注册

    1)什么是基础组件

    许多组件只是包裹了一个输入框或者按钮这类的元素,是相对通用的,把它们称之为基础组件,它们会在各个组件中被频繁的使用。

    这样就会导致很多包含基础组件的长列表

    import BaseButton from './BaseButton.vue'

    import BaseIcon from './BaseIcon.vue'

    import BaseInput from './BaseInput.vue'



    export default {

      components: {

        BaseButton,

        BaseIcon,

        BaseInput

      }

    }

    所以在webpack中,使用require.context只在全局注册这些通用的基础组件。

    在入口文件中(src/main.js中全局导入基础组件的实例代码)

    import Vue from 'vue'

    import upperFirst from 'lodash/upperFirst'

    import camelCase from 'lodash/camelCase'



    const requireComponent = require.context(

      // 其组件目录的相对路径

      './components',

      // 是否查询其子目录

      false,

      // 匹配基础组件文件名的正则表达式

      /Base[A-Z]w+.(vue|js)$/

    )



    requireComponent.keys().forEach(fileName => {

      // 获取组件配置

      const componentConfig = requireComponent(fileName)



      // 获取组件的 PascalCase 命名

      const componentName = upperFirst(

        camelCase(

          // 剥去文件名开头的 `./` 和结尾的扩展名

          fileName.replace(/^./(.*).w+$/, '$1')

        )

      )



      // 全局注册组件

      Vue.component(

        componentName,

        // 如果这个组件选项是通过 `export default` 导出的,

        // 那么就会优先使用 `.default`,

        // 否则回退到使用模块的根。

        componentConfig.default || componentConfig

      )

    })
    记住全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生。

     

    3)通过props向子组件传递数据

    HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

    Vue.component('blog-post', {

      // 在 JavaScript 中是 camelCase 的

      props: ['postTitle'],

      template: '<h3>{{ postTitle }}</h3>'

    })

    <!-- 在 HTML 中是 kebab-case 的 -->

    <blog-post post-title="hello!"></blog-post>
    如果你使用字符串模板,这个限制就不存在。

     

    Prop 是你可以在组件上注册的一些自定义特性。当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样

    Vue.component('blog-post', {

      props: ['title'],

      template: '<h3>{{ title }}</h3>'

    })
     

    一个 prop 被注册之后,你就可以像这样把数据作为一个自定义特性传递进来:

    <blog-post title="My journey with Vue"></blog-post>

    <blog-post title="Blogging with Vue"></blog-post>

    <blog-post title="Why Vue is so fun"></blog-post>

     

    此外还可以定义传进来的prop数据的类型

    props: {

      title: String,

      likes: Number,

      isPublished: Boolean,

      commentIds: Array,

      author: Object

    }

     

    当子组件需要对prop传入的数据进行处理的时候,最好要有一个本地的数据来进行接收,而不要直接改变prop传进来的数据。

    我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。

    为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:

    Vue.component('my-component', {

      props: {

        // 基础的类型检查 (`null` 匹配任何类型)

        propA: Number,

        // 多个可能的类型

        propB: [String, Number],

        // 必填的字符串

        propC: {

          type: String,

          required: true

        },

        // 带有默认值的数字

        propD: {

          type: Number,

          default: 100

        },

        // 带有默认值的对象

        propE: {

          type: Object,

          // 对象或数组默认值必须从一个工厂函数获取

          default: function () {

            return { message: 'hello' }

          }

        },

        // 自定义验证函数

        propF: {

          validator: function (value) {

            // 这个值必须匹配下列字符串中的一个

            return ['success', 'warning', 'danger'].indexOf(value) !== -1

          }

        }

      }

    })

    当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。

    注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 在 default 或 validator 函数中是不可用的(这些是在实例创建之后才可以使用)。

    非prop的特性

    一个非prop特性是值传向一个组件,但是该组件并没有相应prop定义的特性。但是组件可以接收这样的特性,并且添加到这个组件的跟元素上。

    替换/合并已有的特性

    如果组件内部已经设置好的属性,再从组件的父级传入进来,就会替换掉组件内部设置好的值。但是,class和style这两个特性会只能一些,会把组件内部和外部的值合并起来,得到最终的值。

    禁用特性继承(就是不要让组件的根元素,继承父级的所有属性,将父元素的元素拆开来,赋予给不同的组件元素)

     

    <base-input

            v-model="username"

            class="username-input"

            placeholder="Enter your username"

            label = "123"

    ></base-input>

     

    Vue.component('base-input', {

          inheritAttrs: false,

          props: ['label', 'value'],

          template: `

      <label>

        {{ label }}

        <input

          v-bind="$attrs"

          v-bind:value="value"

          v-on:input="$emit('input', $event.target.value)"

        >

      </label>

    `

      })

     

      new Vue({

          el: '#todo-list-example',

          data: {

              username:""

          }

      })

     

    可以看到父元素的label属性赋予了组件中的label标签,而其他的属性李如玉placeholder、class等则绑定在了$attrs上面,赋予给了input元素

    4)通过事件向父级组件发送消息

    在下面的例子中blog-post是父组件,父组件中的postFontSize是用来控制博文的字号

    new Vue({

      el: '#blog-posts-events-demo',

      data: {

        posts: [/* ... */],

        postFontSize: 1

      }

    })

     

    然后在模板中添加一个按钮用来放大字号(利用自定义事件,将enlarge-text事件传给父组件)

    Vue.component('blog-post', {

      props: ['post'],

      template: `

        <div class="blog-post">

          <h3>{{ post.title }}</h3>

          <button v-on:click="$emit('enlarge-text')">

            Enlarge text

          </button>

          <div v-html="post.content"></div>

        </div>

      `

    })

     

    然后再父组件中的模板上面监听这个事件

    <div id="blog-posts-events-demo">

      <div :style="{ fontSize: postFontSize + 'em' }">

        <blog-post

          v-for="post in posts"

          v-bind:key="post.id"

          v-bind:post="post"

            v-on:enlarge-text="postFontSize += 0.1"

        ></blog-post>

      </div>

    </div>

     

    此外,还可以通过这个自定义的事件给父组件传数据

    在$emit的第二个参数中填写的是这个数据

    例如在上面的例子中,我们由子组件来决定放大多少倍,在模板中的button改成下面

    <button v-on:click="$emit('enlarge-text', 0.1)">

      Enlarge text

    </button>

     

    可以看到,这里的参树为0.1,也就是每次点击都放大0.1em

     

    然后在父级组件监听这个事件的时候,访问这个数据

    <blog-post

      ...

      v-on:enlarge-text="postFontSize += $event"

    ></blog-post>

     

    如果这个处理函数是一个方法,那么这个值将作为参数被传入

    <blog-post

      ...

      v-on:enlarge-text="onEnlargeText"

    ></blog-post>



    methods: {

      onEnlargeText: function (enlargeAmount) {

        this.postFontSize += enlargeAmount

      }

    }
     

    自定义事件

    事件名

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

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

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

     

    自定义组件的v-model

    一个组件上的v-model会利用value的prop和input的事件,但是像单选框,复选框等类型的输入控件可能会将value特性用在不同的地方(亲测,当勾选单选框或者复选框的时候,值都为undefined)。

    所以如果我们想要获得这个值(就是究竟有没有被选中的时候,就可以利用组件的这个model属性)

    首先给组件添加model属性,可以看到下面的model含有checked这个属性以及change的这个事件,当组件被改变状态的时候,就会触发这个change事件,并且将自己的checked的值传给父元素,也就是base-checkbox里面的lovingVue这个值绑定了组件中的chencked所对应的值,当base-checkbox触发一个change事件并且附带一个新的值的时候,lovingVue就会被更新。需要注意的是,仍然需要在组件的props里面声明checked这个属性。

    Vue.component('base-checkbox', {

      model: {

        prop: 'checked',

        event: 'change'

      },

      props: {

        checked: Boolean

      },

      template: `

        <input

          type="checkbox"

          v-bind:checked="checked"

          v-on:change="$emit('change', $event.target.checked)"

        >

      `

    })

     

    <base-checkbox v-model="lovingVue"></base-checkbox>

     

    将原生事件绑定到组件(.native)

    组件根元素直接监听事件(亲测,不添加.native是不生效的),

    <div id="test">

        <myinput @focus.native="console"></myinput>

    </div>

     

    Vue.component("myinput",{

        template:"<input type='text' />"

    })

    let vm = new Vue({

        el:"#test",

        data:{

     

        },

        methods:{

            console:function () {

                console.log("focus");

            }

        }

    });

     

    但有的时候组件的根元素不具有这些原生事件的时候,这个事件触发函数就不会生效,例如

    <label>

      {{ label }}

      <input

        v-bind="$attrs"

        v-bind:value="value"

        v-on:input="$emit('input', $event.target.value)"

      >

    </label>

     

    于是vue提供了一个$listeners 属性,里面包含了所有作用在这个组件的监听器

    {

      focus: function (event) { /* ... */ }

      input: function (value) { /* ... */ },

    }

     

    于是上面修改为

    Vue.component('base-input', {

      inheritAttrs: false,

      props: ['label', 'value'],

      computed: {

        inputListeners: function () {

          var vm = this

          // `Object.assign` 将所有的对象合并为一个新对象

          return Object.assign({},

            // 我们从父级添加所有的监听器

            this.$listeners,

            // 然后我们添加自定义监听器,

            // 或覆写一些监听器的行为

            {

              // 这里确保组件配合 `v-model` 的工作

              input: function (event) {

                vm.$emit('input', event.target.value)

              }

            }

          )

        }

      },

      template: `

        <label>

          {{ label }}

          <input

            v-bind="$attrs"

            v-bind:value="value"

            v-on="inputListeners"

          >

        </label>

      `

    })

     

     

    5)在自定义组件里面使用v-model,一定要做一下两件事

    将其value属性绑定在一个value的props上

    在其input事件被触发的时候,将新的值通过自定义的input事件抛出

     

    Vue.component('custom-input', {

      props: ['value'],

      template: `

        <input

          v-bind:value="value"

          v-on:input="$emit('input', $event.target.value)"

        >

      `

    })

     

    <custom-input v-model="searchText"></custom-input>

     

    6)对于特殊的html元素中,其内部元素为指定的元素,所以当我们自定义的元素出现在这里的时候会被视为无效的内容。

    此时使用is属性

    <table>

      <tr is="blog-post-row"></tr>

    </table>

     

    7)组件名

    全局注册的时候第一个参数就是组件名

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

     

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

    使用 kebab-case  

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

    使用 PascalCase

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

    当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 <my-component-name> 和 <MyComponentName> 都是可接受的。注意,尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。

    因为HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

    <div id="todo-list-example">

        <custom-input v-model="searchText"></custom-input>

        <p>{{searchText}}</p>

    </div>

     

    Vue.component('CustomInput', {

          props: ['value'],

          template: `

      <input

        v-bind:value="value"

        v-on:input="$emit('input', $event.target.value)"

      >

    `

      })

     

      new Vue({

          el: '#todo-list-example',

          data: {

              searchText:""

          }

      })

     

     

    但是,使用首字母大写的情况下就会出错

    <div id="todo-list-example">

        <CustomInput v-model="searchText"></CustomInput>

        <p>{{searchText}}</p>

    </div>

     

     

     

     

    8)插槽

    所谓插槽,就是在自定义组件中包含的内容,例如

    <myelement>something intresting</myelement>

    那么上面的something..就是插槽

     

    官网中写道,如果不在自定义组件中添加<slot></slot>元素,那么所有被放进来的元素将会被抛弃。

    例如上面的myelement模板实现可能如下

    <a

      v-bind:href="url"

      class="nav-link"

    >

      <slot></slot>

    </a>

     

    这样写,当组件渲染的时候,这个slot元素就会代替上面的something intresting

     

    插槽中可以包含任何模板代码,包括html

    <navigation-link url="/profile">

      <!-- 添加一个 Font Awesome 图标 -->

      <span class="fa fa-user"></span>

      Your Profile

    </navigation-link>

     

     

    具名插槽

    结合父组件上使用slot从而指定插槽的位置,例如

    模板中的内容可能如下,每个位置拥有不一样的元素

    <div class="container">

      <header>

        <slot name="header"></slot>

      </header>

      <main>

        <slot></slot>

      </main>

      <footer>

        <slot name="footer"></slot>

      </footer>

    </div>



    那么我们在使用父组件的时候

    <base-layout>

      <template slot="header">

        <h1>Here might be a page title</h1>

      </template>



      <p>A paragraph for the main content.</p>

      <p>And another one.</p>



      <template slot="footer">

        <p>Here's some contact info</p>

      </template>

    </base-layout>
    可以看到父组件里面的模板就会出现在slot里面name属性所对应的位置下面

     

    如果不像上面,用template元素以及slot属性包裹,直接用在一个普通元素上但是结合slot属性使用,也可以

    <base-layout>

      <h1 slot="header">Here might be a page title</h1>

     

      <p>A paragraph for the main content.</p>

      <p>And another one.</p>

     

      <p slot="footer">Here's some contact info</p>

    </base-layout>

     

    我们还是可以保留一个未命名插槽,这个插槽是默认插槽,也就是说它会作为所有未匹配到插槽的内容的统一出口

     

    插槽的默认内容

    有的时候为插槽提供的内容是很有用的,例如,个 <submit-button> 组件可能希望这个按钮的默认内容是“Submit”,但是同时允许用户覆写为“Save”、“Upload”或别的内容。

    你可以在组件模板里的 <slot> 标签内部指定默认的内容来做到这一点。

    <button type="submit">

      <slot>Submit</slot>

    </button>

    如果父组件为这个插槽提供了内容,那么这个默认的内容就会被替换掉

     

     

     

     

     

    9)动态组件&异步组件

    在一个多标签的界面中使用is特性来切换不同的组件

    Vue.component('tab-home', {

        template: '<div>Home component</div>'

    })

    Vue.component('tab-posts', {

        template: '<div>Posts component</div>'

    })

    Vue.component('tab-archive', {

        template: '<div>Archive component</div>'

    })

     

    new Vue({

      el: '#dynamic-component-demo',

      data: {

        currentTab: 'Home',

        tabs: ['Home', 'Posts', 'Archive']

      },

      computed: {

        currentTabComponent: function () {

          return 'tab-' + this.currentTab.toLowerCase()

        }

      }

    })

     

     

     

    <div id="dynamic-component-demo" class="demo">

      <button

        v-for="tab in tabs"

        v-bind:key="tab"

        v-bind:class="['tab-button', { active: currentTab === tab }]"

        v-on:click="currentTab = tab"

      >{{ tab }}</button>

     

      <component

        v-bind:is="currentTabComponent"

        class="tab"

      ></component>

     

    这样实现的话,每次切换,都会重新渲染的。

    当在这些组件进行切换的时候,有时会想保持这些组件的状态,以免反复重新渲染所导致的性能问题,为了解决这个问题,用一个keep-alive元素将动态组件包裹起来

    <!-- 失活的组件将会被缓存!-->

    <keep-alive> 

    <component

        v-bind:is="currentTabComponent"

        class="tab"

      ></component>

    </keep-alive>

     

    异步组件

    所谓异步组件就是等到需要的时候,才会去服务器加载这个模块。vue用工厂函数的的方式定义组件,这个函数会异步的解析运行,也就是只有在需要被渲染的时候才会触发这个函数。

    Vue.component('async-example', function (resolve, reject) {

      setTimeout(function () {

        // 向 `resolve` 回调传递组件定义

        resolve({

          template: '<div>I am async!</div>'

        })

      }, 1000)

    })

     

     

     

    我们来写一个类似于公众号历史记录这样的东西(就是div结构,样式都一样,仅仅是内容不一样的多个div)

    <div class="unit">

        <div class="content">

            <h1>这是文章的标题</h1>

            <div class="info">

                <span>2018年11月14日</span>

                <span>原创</span>

            </div>

        </div>

        <img src="img/logo.jpg">

    </div>

    .unit{

        height: 200px;

         90%;

        overflow: hidden;

        margin: 0 auto;

        background-color: #ffffff;

    }

    .content{

         60%;

        height: 100%;

        float: left;

    }

    img{

         40%;

        height: 100%;

        float: left;

    }

    .info{

        font-size: 16px;

        color: #cccccc;

    }

    .info span{

        margin: 0 15px;

    }

    .info span:nth-child(2){

        border: 1px solid #cccccc;

        border-radius: 7px;

    }




    其中基本的css代码和html的代码如下:下来定义一个组件,代码如下:,这个组件名叫myarticle,拥有props属性和template属性,其中pros属性用来接收参数,这些参数会在模板里面用到。

    Vue.component('myarticle',{

        props:['detail'],

        template :' <div class="unit"> ' +

        '        <div class="content"> ' +

        '            <h1>{{detail.title}}</h1> ' +

        '            <div class="info"> ' +

        '                <span>{{detail.date}}</span> ' +

        '                <span v-show="detail.is_oringinal">原创</span> ' +

        '            </div> ' +

        '        </div> ' +

        '        <img :src="detail.imgurl"> ' +

        '    </div>'

    });
     

    然后创建一个vue实例,需要注意的是,一定要确保实例vm在创建之前,组件已经成功注册。

    let vm = new Vue({

        el:"#app",

        data:{

            articles:[

                {

                    title:"这是第一个标题",

                    date:"2018年11月14日",

                    is_oringinal:true,

                    imgurl:"img/logo.jpg"

                },

                {

                    title:"这是第二个标题",

                    date:"2018年11月15日",

                    is_oringinal:true,

                    imgurl:"img/logo.jpg"

                },

                {

                    title:"这是第三个标题",

                    date:"2018年11月16日",

                    is_oringinal:true,

                    imgurl:"img/logo.jpg"

                }

            ]

        }

    })
    注册完组件之后,就开始使用这个自定的组件

    <div id="app">

        <myarticle v-for="item in articles" :detail = "item"></myarticle>

    </div>

     

     

     

     

    八.组件之间的通信

     

    组件实例的作用域都是孤立的,也就是子组件在模板中不能引用父组件的数据。

    1.父->子,父组件将数据传递给子组件

    先创建一个实例

    <div id="app"></div>

    <script>

        //创建一个vue实例

      const app = new Vue({

            el:"#app",

            data:{

                msg:"我是父组件的数据哦嘻嘻嘻"

            }

        });

    </script>

    实例中含有数据msg。

    注册子组件

    Vue.component('son',{

        template:"<div></div>"

    })

    并将子组件插入到父组件下面:

    <div id="app">

        <son></son>

    </div>

     

    接下来我们开始传递数据,父组件向子组件传递参数,用组件提供的props属性,在下面的代码中,父组件app向son组件的:message属性传输了它自己的值msg。子组件message接收了这个参数,然后在组件的定义里面,用props来接收这一个值,并将这个值在div中输出。这样就完成了父组件传递给子组件了。props选项声明了它要接受的参数是message,而接收到的对应的值是父组件的数据msg。我们在子组件顺利地把message展示出来。

    <div id="app">

        <son :message = "msg"></son>

    </div>

    Vue.component('son',{

        props:['message'],

        template:"<div>{{message}}</div>"

    })

     

     

    而props是单向绑定的,也就是说子组件接收到这个值之后无论怎么修改,父组件中也不会受到影响。因为vue为了防止子组件无意修改了父组件的数据和状态,如果多个子组件任意地对父组件进行修改,会让数据流难以阅读。

     

    2.子->父,子组件传数据给父组件。

    父组件监听子组件的事件,并接收子组件传过来的数据

    vue实例都有一个事件的接口,这个接口就是$emit(eventname)来触发一个事件

    1.注册一个新组件

    Vue.component('son',{

       template:`<button @click="send">

     

                   点击

     

                </button>`,

     

       methods:{

           send(){

               this.$emit('connect');

     

           }

       }

    });

    一个按钮button,点击它的时候,会触发组件内部的send( )方法,而方法体里面会触发一个事件,事件名取名为:“connect”。

    然后我们就去父组件监听这个事件‘connect’,监听方式跟普通原生的事件一模一样,也是用 v-on 指令,缩写用@符号。 我们采用缩写来监听:

    <div id="app">

        <son @connect="say"></son>

    </div>

    const app = new Vue({

        el:"#app",

        methods:{

            say(){

                console.log(`大家好,我监听到了

      事件connect的触发`);

            }

        }

    });

     

    这样,当点击按钮的时候,就成功调用了父组件的say函数。这时,我们只需要把子组件的数据,通过这个事件connect传递给父组件,就可以实现子->父的通信。在data函数里面返回需要返回的数据,并在$emit里面讲msg一并发射出去,在父组件中的say中接收

    Vue.component('son',{

        props:['message'],

        template:"<button @click = 'send'>点击</button>",

        data(){

            return{

                msg:'大家好,我是儿子的数据'

            }

        },

        methods:{

            send(){

                this.$emit('connect',this.msg);

            }

        }

    })

    const  app = new Vue({

        el:"#app",

        methods:{

            say(msg){

                console.log("hello,我监听到了子组件的connect事件的触发,儿子的数据是"+msg);

            }

        }

    })

     

    这样就完成了子->父组件的数据传输

     

     

    九.动态绑定class和style

    1.class的绑定

    1)对象法:动态绑定的class的值是一个对象{},键是一个样式名,而值是是否显示这个样式。在对象中,若该属性对应的value为true,则该属性会被渲染出来,为false,则不会渲染出来。并且这写法可以与单独的class属性可以共存

    <p :class="{'active':isActive}">这是文字</p>

    <p :class="{'active':notActive}">这是文字</p>

    <p class="static" :class="{'active':isActive}}"></p>

    let  vm = new Vue({

        el:"#app",

        data:{

            isActive:true,

            error:"active",

            active:"erro",

            color:"red",

            ifshow:"block",

        }

    })

     

    2)数组法,数组语法中也可以使用对象语法:

    <p :class="[active,errorc]">hello</p>

    数组中的值在实例中对应的值是一个属性样式,也就是说数组法中html中数组的值可以不是一个样式名,只要它在vue实例中data中对应的键值的值是一个样式名。

     

     

     

     

    2.style的绑定(绑定内联样式style)

    CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) 来命名:

    1)对象语法,需要注意的是这样绑定的属性之间要用逗号隔开,不要用分号

    <p :style="{color:color,display:ifshow}">world</p>

     

     

    2)数组语法

    v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:

    <div v-bind:style="[baseStyles, overridingStyles]"></div>

     

    当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀。

     

    你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:

    <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

    这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。

     

    3.用在组件上

    当在一个自定义组件上使用class属性的时候,这些类将被添加到该组件的根元素上面。这个元素上已经存在的类不会被覆盖。

    例如,如果你声明了这个组件:

    Vue.component('my-component', {

      template: '<p class="foo bar">Hi</p>'

    })

    然后在使用它的时候添加一些 class:

    <my-component class="baz boo"></my-component>

    HTML 将被渲染为:

    <p class="foo bar baz boo">Hi</p>

    对于带数据绑定 class 也同样适用:

    <my-component v-bind:class="{ active: isActive }"></my-component>

    当 isActive 为 truthy 时,HTML 将被渲染成为:

    <p class="foo bar active">Hi</p>

     

     

     

     

     

     

    十.demo,做一个TODOlist

    确定T

     

     

     

    十一.用transition组件轻松实现过渡效果

    1.transition过度原理分析

    vue提供的transition组件会在以下四种情况下起作用

    条件渲染(v-if)

    条件展示(v-show)

    动态组件

    组件根节点

    在上述的任意一种情况发生的时候(比如v-show的值本来为true的时候,当发生某种变化导致值变为false的时候),就可以给transition组件包含的节点元素添加enter或leave过度动画元素。

    当插入或删除包含在transition组件中的元素时,vue将会做以下的处理

    自动嗅探目标元素是否应用了css过渡或动画,如果是,在适当的时候添加或者删除css类名

    如果过渡组件提供了JavaScript钩子函数,这些钩子函数将会在恰当的时机被调用

    如果没有找到JavaScript钩子也没有检测到css过渡/动画,dom操作在下一帧立即执行

     

    下面的v在实际应用改成transition中name属性的值

    v-enter:定义过渡的开始状态,在元素被插入之前生效,在元素被插入之后的下一帧移除。

    v-enter-active:定义过渡生效时的状态,在整个进入过渡的阶段都会应用,在元素被完全插入之前生效,在过渡或者动画完成之后被移除。(就是这个类来定义过渡的时间等)

    v-enter-to:定义过渡的结束状态,在元素被插入之后下一帧生效,在过渡或者动画完成之后被移除。

    v-leave:离开过渡的开始的那一刻(用在从显示到隐藏的时候)

    v-leave-active:离开过渡的的持续状态,在整个离开过渡的阶段中应用(在这里定义过渡的时间等)

    v-leave-to离开过渡的结束状态,在离开过渡被触发之后下一帧生效。

     

    需要注意的是

    节点一定要被transition元素包含

    transition的name属性一定要与css类名开始的地方相同(如果没有使用name属性,那么类名前缀为v)

    2.例子

    <div id="test">

       <button @click="show = !show">toggle</button>

        <transition name="fade">

            <p v-if="show">hello</p>

        </transition>

    </div>

    <script>

      let vm = new Vue({

        el:"#test",

        data:{

            show:false

        }

    });

    </script>

    .fade-enter-active, .fade-leave-active {

        /*定义过渡的状态*/

        transition: all 1.5s ease;

    }

    .fade-enter, .fade-leave-to  {

        /*定义进入动画的状态,以及离开动画最后结束的状态*/

        transform: translateX(20px);

    }

     

     

    (2)css动画

    在动画中,v-enter类名在节点插入dom后不会立即删除,而是在animationend时间触发时删除。

    (也就是说,直到触发动画事件的时候才会删除这个类名)

     

    .animate-enter-active{

        animation: bounze 1s;

    }

    .animate-leave-active{

         animation: bounze 1.5s reverse;

    }

    @keyframes bounze {

       0%{

       transform: translateX(0px);

       }

         50%{

             transform: translateX(20px);

         }

         100%{

       transform: translateX(40px);

         }

    }

     

    (3)自定义过渡类名

    可以通过自定义类名来使用其他第三方css动画库里面的类名(在transition元素上面指定自定义的类名)

    * enter-class

    * enter-active-class

    * enter-to-class (2.1.8+)

    * leave-class

    * leave-active-class

    * leave-to-class (2.1.8+)

    <link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

     

    <div id="example-3">

      <button @click="show = !show">

        Toggle render

      </button>

      <transition

        name="custom-classes-transition"

        enter-active-class="animated tada"

        leave-active-class="animated bounceOutRight"

      >

        <p v-if="show">hello</p>

      </transition>

    </div>

     

    (4)JavaScript钩子

    就是在transition 过渡或者动画中使用JavaScript的的处理函数进行处理,有一下八个钩子

    before-enter

    enter

    after-enter

    enter-cancelled

    before-leave

    leave

    after-leave

    leave-cancelled

     

    <transition

      v-on:before-enter="beforeEnter"

      v-on:enter="enter"

      v-on:after-enter="afterEnter"

      v-on:enter-cancelled="enterCancelled"

     

      v-on:before-leave="beforeLeave"

      v-on:leave="leave"

      v-on:after-leave="afterLeave"

      v-on:leave-cancelled="leaveCancelled"

    >

      <!-- ... -->

    </transition>

     

     

    // ...

    methods: {

      // --------

      // 进入中

      // --------

     

      beforeEnter: function (el) {

        // ...

      },

      // 当与 CSS 结合使用时

      // 回调函数 done 是可选的

      enter: function (el, done) {

        // ...

        done()

      },

      afterEnter: function (el) {

        // ...

      },

      enterCancelled: function (el) {

        // ...

      },

     

      // --------

      // 离开时

      // --------

     

      beforeLeave: function (el) {

        // ...

      },

      // 当与 CSS 结合使用时

      // 回调函数 done 是可选的

      leave: function (el, done) {

        // ...

        done()

      },

      afterLeave: function (el) {

        // ...

      },

      // leaveCancelled 只用于 v-show 中

      leaveCancelled: function (el) {

        // ...

      }

    }

    当仅仅使用JavaScript过渡的时候,需要有两个注意事项

    在enter和leave中必须使用done来进行回调

    在transition中使用v-bind:css="false",这样vue会跳过css的检测。

     

    (5)初始渲染的过渡

    可以通过appear特性来设置节点在初始渲染的过渡

     

    (6)多个元素的过渡

    就是多个元素之间只显示其中一个,当状态发生改变的时候,显示的元素不一样,就会出现元素之间的过渡。值得注意的是,官网推荐在相同标签名的元素之间进行切换的时候,需要给这些元素设置key特性,并拥有不一样的值,否则vue为了效率只会替换相同标签内部的内容。

    <button v-if="isEditing" @click="isEditing = !isEditing" key="save">

        Save

    </button>

    <button v-else @click="isEditing = !isEditing" key="edit">

        Edit

    </button>

     

    此外,在vue中,当两个元素进行过度的时候,transition的默认行为是进入和离开同时发生,那么此时两个元素就都会同时发生过度效果,这会影响效果,所以vue中有一个过渡模式

    这个模式有两个值,分别是

    out-in 离开元素先过渡然后再到进入的元素过渡

    in-out 与上面相反

    <transition name="mutiple" mode="out-in">

        <button v-if="isEditing" @click="isEditing = !isEditing" key="save">

            Save

        </button>

        <button v-else @click="isEditing = !isEditing" key="edit">

            Edit

        </button>

    </transition>

     

    (6)多个组件的过渡

    多个组件的过渡使用动态组件

     

      Vue.component("tab-home",{

          template:`<p>this is home</p>`

      });

      Vue.component("tab-aboutus",{

          template:`<p>this is aboutus</p>`

      });

      Vue.component("tab-vechicle",{

          template:`<p>this is vechicle</p>`

      });

    let vm = new Vue({

        el:"#test",

        data:{

            buts:["home","aboutus","vechicle"],

            currentTab:"home"

        },

        computed:{

            currentActiveTab:function () {

                return "tab-"+this.currentTab;

            }

        }

    });

     

    <div id="test">

            <button v-for="but in buts" @click="currentTab = but" :key="but">

                {{but}}

            </button>

        <transition name="mutiple" mode="out-in">

           <keep-alive>

               <component :is="currentActiveTab"></component>

           </keep-alive>

        </transition>

    </div>

     

    (7)列表过渡

    使用transition-group组件包含列表元素

    使用transition-group的特点

    不同于transition,它会以一个真实的元素呈现,默认为一个span,可以通过tag特性来更改为其他元素

    过渡模式不可以用,因为不再切换其他元素

    内部元素总是需要有一个唯一的key属性

    new Vue({

        el: '#list-demo',

        data: {

            items: [1,2,3,4,5,6,7,8,9],

            nextNum: 10

        },

        methods: {

            randomIndex: function () {

                return Math.floor(Math.random() * this.items.length)

            },

            add: function () {

                this.items.splice(this.randomIndex(), 0, this.nextNum++)

            },

            remove: function () {

                this.items.splice(this.randomIndex(), 1)

            },

        }

    })

     

    <div id="list-demo" class="demo">

        <button v-on:click="add">Add</button>

        <button v-on:click="remove">Remove</button>

        <transition-group name="mutiple" tag="p">

        <span v-for="item in items" v-bind:key="item" class="mutiple-item">

          {{ item }}

        </span>

        </transition-group>

    </div>

     

    .mutiple-item {

        display: inline-block;

        margin-right: 10px;

    }

    .mutiple-enter-active,.mutiple-leave-active{

       transition: all 1s ;

    }

    .mutiple-enter,.mutiple-leave-to{

        transform: translateY(10px);

    }

     

    列表的排序过渡,transition-group组件还有一个特殊之处,不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需要了解新增的v-move特性,他会在元素的改变定位的过程中应用。

    css的类名可以使用name属性自定义前缀,也可以通过move-class属性手动设置。

     

     

     

     

    十二.vue-router

    1.vue-router是vue官方的路由插件,适合用于构建单页面应用。

    2.先引入vue-router

    3.准备组件,vue-route给我们提供了两个新组件,<router-link>和<router-link>组件,其中link是用于帮助用户进行视图导航,也就是传统页面中a标签所做的事情,用to来指定组件的位置。而view组件负责渲染匹配到的视图组件,也就是渲染link指向的目标地址,

    <div id="app">

        <div class="nav">

            <router-link to="/hins">张敬轩</router-link>

            <router-link to="/ste">孙燕姿</router-link>

            <router-link to="/adele">Adele</router-link>

        </div>

        <div class="content">

            <router-view></router-view>

        </div>

    </div>

     

    接下来定义视图组件以及与导航地址关联起来。创建一个router实例,创建实例的时候我们需要传参数routes来进行配置。

    const hins = {

        template:`<div>这是张敬轩的页面</div>`

    };

    const ste = {

        template:`<div>这是孙燕姿</div>`

    };

    const  adele = {

        template:`<div>这是Adele</div>`

    };

    const router = new VueRouter({

        routes:[

            {

                path:"/hins",

                component:hins

            },

            {

                path:"/ste",

                component:ste

            },

            {

                path:"/adele",

                component:adele

     

            }

        ]

    });

     

     

    最后,创建一个vue实例,创建的时候通过配置router参数来注入我们刚定义好的router路由

     

    //创建vue实例,注入路由router

    const app = new Vue({

        el:"#app",

        router //此处是ES6语法,

     

     

          //相当于router:router

    });

     

     

     

     

     

     

     

     

    十三.vuex

    1.vuex是什么?

    vuex是一个专为vue开发的应用程序的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex采用类似全局对象的形式来管理所有组件的公用数据,如果你想想修改这个全局对象的数据?是可以的,但没那么简单,你得按照Vuex提供的方式来修改,不能自己随意用自己的方式来修改

     

    (1)vue的状态存储是响应式的,方你的组件使用到了这个vuex的状态,一旦它改变了,所有关联的组件都会自动更新相对应的数据。

    (2)不能直接修改vuex的状态,想修改就得使用vuex提供的唯一途径:显示地提交mutations来实现修改,

     

    2.vuex的3个核心概念

    (1)要使用vuex,就要创建一个实例store,我们称之为仓库,利用这个仓库对状态进行管理

    const store = new Vuex.Store({});

     

    (2)State

    vuex使用单一状态树,用一个对象state包含了整个应用层级的所有状态。在下面代码中,假如我们有一个全局状态count,值为5,就可以将其定义为state对象里面的一对键值对,作为全局状态供我们使用。

    const store = new Vuex.Store({

        state:{

            count:5

        }

    });

     

    (2)Getters

    当我们需要从state中派生出一些状态的时候,就会使用到getters,相当于getters是state的计算属性。

    const store = new Vuex.Store({

        state:{

            count:5

        },

        getters:{

            newCount:state =>state.count*3

        }

     

    });

     

    (3)mutations

    vuex给我们提供修改仓库store中的状态的唯一方法就是通过提交mutation。

    定义一个mutations:

    const store = new Vuex.Store({

        state:{

            count:5

        },

        getters:{

            newCount:state =>state.count*3

        },

        mutations:{

            increment(state){

                state.count ++;

            }

        }

     

    });

     

    定义好了后就必须要提交mutation,语法如下:

    store.commit('increment');

     

    4.例子

    需求:做一个计算器,那么运算的结果result和输入的值enter就是会跟每一个组件(按键)

    相关联的,所以这里我们将result和enter作为应用层的状态(全局数据)来处理,这里就会简单的使用到vuex提供的仓库store。

     

    首先完成上面界面的实现。

    一共有3个主要区域,一个运算的结果,一个输入的结果,还有按键区域。

    按键区域的使用可以使用组件来实现,定义一个keyboard组件,根据传入的参数来定义不一样的按键。

    <div id="app">

        <div class="result"></div>

        <div class="enter"></div>

        <div class="keys">

            <div class="list">

                <keyboard v-for="item in keys" :value="item"></keyboard>

            </div>

        </div>

    </div>

    <script>

        Vue.component("keyboard",{

            props:['value'],

            template:`<div

               :data-value="value">

                {{value}}

             </div>`

        });

       

        const app = new Vue({

            el:"#app",

            data:{

                keys:[

                    'clear','+','-','*',

                    '7','8','9','/',

                    '4','5','6','0',

                    '1','2','3','=',

                ]

            }

        });

     

    </script>

     

    #app{

         100%;

        height: 100%;

    }

    .result{

         324px;

        height: 56px;

        font-size: 34px;

        margin-left: 178px;

    }

    .enter{

         324px;

        height: 50px;

        font-size: 24px;

        margin-left: 218px;

    }

    .keys{

         324px;

        height: 324px;

    }

    .list div{

         80px;

        height: 80px;

        line-height: 80px;

        background-color: #efefef;

        border: 0.5px solid #cccccc;

        border-bottom: none;

        border-right: none;

        float: left;

        text-align: center;

        font-size: 18px;

        vertical-align: center;

    }

    .list div:first-child{

        color: #ff8282;

    }

    .list div:last-child{

        color: white;

        background-color: #ff8282;

    }

     

     

    界面实现了,接下来就是数据的显示以及如何进行数据绑定,当按键被按下的时候,显示输入的值以及运算的结果。这时候,组件们就共用到了result和enter这两个值,所以我们首先需要建立仓库来管理它们,并初始化为空字符串

    const store = new VUex.Store({

        state:{

            result:"",

            enter:""

        }

    })

     

    vuex提供了store选项,允许我们将仓库store引入到根组件中,并且此跟组件的所有子组件都可以使用仓库store,而且子组件无需显示的引入。

    首先我们在vue实例下面引入我们的仓库store

     

    const app = new Vue({

        el:"#app",

        data:{

            keys:[

                'clear','+','-','*',

                '7','8','9','/',

                '4','5','6','0',

                '1','2','3','=',

            ]

        },

        store,

     

    }

    引入之后我们就来使用,那么怎么使用仓库的result和enter呢?我们这里用到vue的computed计算属性。通过this.$store来回去仓库,然后this.$store.state来获取属性,最后用this.$store.state.result来获取仓库里的result。同理获得enter

    computed:{

        result(){

            return this.$store.state.result;

        },

        enter(){

            return this.$store.state.enter;

        }

    }

     

    将result和enter显示在html代码里面

    <div class="result">{{result}}</div>

    <div class="enter">{{enter === ""?0:enter}}</div>

     

    最后,绑定按键的点击事件,当我们点击键盘的时候,对仓库的result和enter进行修改。但是vuex中不能任意修改应用层的状态,所以就通过它提供的唯一的途径:通过commit提交mutation。当用户点击键盘的时候我们就提交一个mutation,并把当前按的键对应的值传过去。所以我们要重新定义keyboard组件的事件。

    Vue.component("keyboard",{

        props:['value'],

        template:`<div

            @click="getKeyboardValue"

           :data-value="value">

            {{value}}

         </div>`,

        methods:{

            getKeyboardValue(event){

                let value=event.target.dataset.value;

                this.$store.commit('calculate',value)

            }

        }

    });

     

    增加了一个监听点击事件的处理函数getKeyboardValue,我们会提交体格名为calculate的mutation给仓库,并将当前按键的值一起提交过去。这个calculate的mutation要在仓库中定义

    const store = new Vuex.Store({

        state:{

            result:"",

            enter:""

        },

        mutations:{

            calculate(state,value){

                if (value === '='){

                    state.result = eval(state.enter);

                    state.enter+=value;

                }else if (value === 'clear'){

                    state.result = state.enter = "";

                }else {

                    state.enter +=value;

                }

            }

        }

    });

     

     

     

    十四.使用axios

    (1)get请求

     

    axios.get('detail.html?id=10').then(function (res) {

        console.log(res);

    }).catch(function (err) {

        console.log(err);

    });

     

     

     

    axios.get('detail.html',{

        params:{

            id :10

        }

    }).then(function (res) {

        console.log(res);

    }).catch(function (err) {

        console.log(err)

    })

     

     

    (2)post请求

     

     

    axios.post('detail.html',{

        name:"krys",

        age:22

    }).then(function (res) {

        console.log(res)

    }).catch(function (err) {

        console.log(err);

    })

     

    (3)多个请求并发

     

    function getProfile(){

        //请求1

        return axios.get('vuex.html');

    }

    function getUser(){

        //请求2

        return axios.get('todo.html');

    }

     

    //并发请求

    axios.all([

        getProfile(),

        getUser()

    ]).then(axios.spread((res1, res2)=>{

        //两个请求现已完成

        console.log(res1);

        console.log(res2);

    }));

     

     

     

    我们事先定义了两个方法getProfile()和getUser()帮我们实现get请求。在某个业务场景中,我们需要同时产生以上两个get请求,并需要等待它们都请求完毕后再做逻辑处理,你也许会想到回调嵌套,但现在你可以使用axios的并发请求API: axios.all()

    当这两个请求都完成的时候会触发 axios.spread() 函数,res1和res2两个参数分别代表返回的结果,相当实用的API。

     

    上面的写法当中是axios提供给我们的别名写法,也可以写成下面这样

    axios({

        method: 'post',

        url: 'detail.html',

        data: {

            name: 'krys',

        }

    });

     

     

     

    十五、官网上看到的一些总结

    1.使用object.freeze()会阻止修改现有的属性,也就是说,当代码中使用了Object.freeze(obj),那么在vue实例中将不会再追踪这个对象的变化。(仅仅针对data属性中的值是外部的一个对象)

    <div id="app">

      <p>{{ foo }}</p>

      <!-- 这里的 `foo` 不会更新! -->

      <button v-on:click="foo = 'baz'">Change it</button>

    </div>

     

    var obj = {

      foo: 'bar'

    }

    Object.freeze(obj);

    new Vue({

      el: '#app',

      data: obj

    })

     

     

    2.混入

    (1)什么是混入?

    混入(mixins)是一种分发vue组件中可以重复使用的功能的方式。

    混入对象可以包含任意组件选项。当组件使用混入对象是,所有混入独享的选项将被混入该组件本身的选项。(其实就是多个组件中相同功能的部分)

     

    组件在引用之后相当于在父组件内开辟了一块单独的空间,来根据父组件props过来的值进行相应的操作,单本质上两者还是泾渭分明,相对独立。

     

    而mixins则是在引入组件之后,则是将组件内部的内容如data等方法、method等属性与父组件相应内容进行合并。相当于在引入后,父组件的各种属性方法都被扩充了。

     

     

     

    (2)当组件和混入对象含有同名选项的时候,以下面这些方式进行混合

    数据对象在内部会进行递归合并,在和组件的数据发生冲突的时候以组件数据为优

    同名钩子函数将混为同一个数组,因此两个都会被调用,但是混入对象的钩子将会在组件自身钩子之前被调用

    值为对象的选项,例如methods,components等将会被混合为同一个对象,当两个对象里面属性名冲突的时候,取组件对象的键值对

    var mixin = {

         data: function () {

             return {

                 message: 'hello',

             }

         },

         created: function () {

             console.log('混入对象的钩子被调用')

         },

         methods: {

             foo: function () {

                 console.log('foo')

             },

             conflicting: function () {

                 console.log('from mixin')

             }

         }

    };

     

    let vm =  new Vue({

         el:"#list-demo",

         mixins: [mixin],

         data: function () {

             return {

                 message: 'goodbye',

             }

         },

         created: function () {

             console.log('组件的钩子被调用')

         },

         methods:{

             bar: function () {

                 console.log('bar')

             },

             conflicting: function () {

                 console.log('from self')

             }

         }

    });

    vm.foo() // => "foo"

    vm.bar() // => "bar"

    vm.conflicting() // => "from self"

     

    (3)全局混入

     

    全局声明混入对象,在全局混入对象中声明的属性,会影响所有的vue实例。

    全局声明混入对象的代码

    // 为自定义的选项 'myOption' 注入一个处理器。

    Vue.mixin({

      created: function () {

        var myOption = this.$options.myOption

        if (myOption) {

          console.log(myOption)

        }

      }

    })

     

    let vm =  new Vue({

        el:"#list-demo",

        data: function () {

            return {

                message: 'goodbye',

            }

        },

        myOption:"hello"

    });

    这样控制台会立即打印出hello

    大多数情况下,只应当应用于自定义选项,就像上面示例一样。

     

     

    3.自定义指令

    自定义全局指令

    Vue.directive("myfirst",{

        inserted:function (el) {

            el.focus();

        }

    });

    自定义局部指令

    directives: {

      focus: {

        // 指令的定义

        inserted: function (el) {

          el.focus()

        }

      }

    }

    然后可以在模板中的任何元素上面使用

    <input v-myfirst>

     

     

    此外,指令定义对象有如下几个钩子函数(均为可选)

    bind:只调用一次,指令第一次绑定到元素时调用

    inserted:被绑定你元素插入父节点时调用(也就是dom生成的时候调用)

    update:所在组件爱你的vnode更新时调用

    componentUpdated所在组件的vnode及其子vnode全部更新后调用

    unbind:只调用一次,指令与元素解绑时调用

     这些钩子函数会被传入下面这些参数

    * el:指令所绑定的元素,可以用来直接操作 DOM 。

    * binding:一个对象,包含以下属性:

        * name:指令名,不包括 v- 前缀。

        * value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。

        * oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。

        * expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1"中,表达式为 "1 + 1"。

        * arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。

        * modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。

    * vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。

    * oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

     

    4、渲染函数以及JSX

    在vue中使用render生成html代码

    Vue.component('anchored-heading', {

        render: function (createElement) {

            return createElement(

                'h' + this.level,   // 标签名称

                this.$slots.default // 子元素数组

            )

        },

        props: {

            level: {

                required: true

            }

        }

    });

     

    (2)虚拟DOM

    虚拟节点:包含的信息会告诉vue页面上需要渲染什么样的节点,以及其子节点。如下面会返回一个虚拟及诶单

    createElement('h1', this.blogTitle)

     

    createElement(

      // {String | Object | Function}

      // 一个 HTML 标签字符串,组件选项对象,或者

      // 解析上述任何一种的一个 async 异步函数。必需参数。

      'div',

     

      // {Object}

      // 一个包含模板相关属性的数据对象

      // 你可以在 template 中使用这些特性。可选参数。

      {

        // (详情见下一节)

      },

     

      // {String | Array}

      // 子虚拟节点 (VNodes),由 `createElement()` 构建而成,

      // 也可以使用字符串来生成“文本虚拟节点”。可选参数。

      [

        '先写一些文字',

        createElement('h1', '一则头条'),

        createElement(MyComponent, {

          props: {

            someProp: 'foobar'

          }

        })

      ]

    )

    转载链接:https://blog.csdn.net/krysliang/article/details/87284483

    墨秋の
  • 相关阅读:
    MySql存储引擎MyISAM和InnoDB的区别
    Nginx下载安装
    科目三考试训练大纲
    解决The current branch is not configured for pull No value for key branch.master.merge found in config
    java实现截取6个汉字字母数字
    如何将git上的代码迁移到Coding上
    Python抓取博客园首页文章列表(带分页)
    Python实现抓取CSDN博客首页文章列表
    Python实现抓取CSDN热门文章列表
    linux目录的操作
  • 原文地址:https://www.cnblogs.com/yztdd99/p/14487135.html
Copyright © 2020-2023  润新知