Vue.js框架


title: Vue.js框架
date: 2019-08-06 20:00:27
tags: [前端框架,Vue.js]
categories: Web前端


一、关于Vue.js

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。 --摘自官网

总体来说作为一门前端框架,Vue.js本身可以最大程度减少用户操作Dom的工作

二 、数据的双向绑定

Vue.js可以将简洁的模板语法来声明式的将数据渲染到页面中,且声明的数据是双向的。而且对于一些特殊的标签可以通过绑定元素特性。例如

    <div id="app">
        姓氏:<input type="text" v-model = 'firstname'> 
        名字:<input type="text" v-model = 'lastname'>
        <p>{{firstname+lastname}}</p>
    </div>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el:'#app',  //绑定id=app的元素   
            data: {     //声明数据
                firstname:'',
                lastname:''
            }
        })

解释一下上例,功能:输入姓氏和名字来合成名字。

首先为两个Input绑定了v-model,因为数据是双向的,默认值为Vue构造函数中声明的“”,但是当修改文本框中的值时,app.firstnameapp.lastname也会对应发生改变。

三、 指令

在上例中使用的v-model就是一个指令,它可以用于绑定<input>、<select>、<textarea>、components标签的数据。指令一般都以v-开头。

3.1 用v-on指令绑定监听

基本格式:(事件方法内容可以直接为Js语句也可以是方法名)简写格式为@

v-on: 监听方式='事件方法'

例如:功能:输入价格,可以利用按钮来增减数量,并显示价格。通过v-on指令来绑定click事件,

方法应当挂载在methods下。

    <div id="app">
        <div>
            <label for="">价格</label>
            <input type="text" v-model='price'>
        </div>
        <div>
            数量:<button v-on:click='jian'>-</button><span>{{ number }}</span><button v-on:click='jia'>+</button>  
        </div>
        <div>
            <strong>{{ price*number }}</strong>
        </div>
    </div>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <script>
        new Vue({
            el:'#app',
            data:{
                price:0,
                number:0
            },
            methods:{
                jian:function(){
                    --this.number
                },
                jia:function(){
                    ++this.number
                }
            }
        })
    </script>

3.2 条件与循环

  • v-if作为条件指令,在引号内,值可以为绑定的数据,也可以直接填写truefalse,

    当然还包括v-elsev-else-if进行配套使用

   <div id="app3">
        <span v-if='true'>NiKon</span>
        <span v-if='nonSeen'>Canen</span>
    </div>
        new Vue({
            el:"#app3",
            data:{
                nonSeen: true
            }
        })

结果:NiKon Canen

  • v-for作为循环指令。在要要遍历的标签中绑定该指令,值的格式为'xx[,index] in xxs'`
    <div id="app4">
        <ul>
            <li v-for='todo,index in todos'>
                {{ index+'·'+todo.text }}
            </li>
        </ul>
    </div>
        new Vue({
            el:'#app4',
            data: {
                todos:[
                    {
                        index:1,
                        text:'wowo'
                    },
                    {
                        index:2,
                        text:'wowo'
                    },
                    {
                        index:3,
                        text:'wowo'
                    }
                ]
            }
        })

结果:

0·wowo

1·wowo

2·wowo

3.3 v-model指令

用于作为表单控件的双向绑定的指令,格式:v-model="xxx"

3.4 v-bind属性渲染指令

对于原有的HTML标签属性可以通过v-bind来渲染,为单向数据绑定。简写格式为:

<input type="text" v-bind: value="xxx">
或者
<input type="text" :class="{xxx:true of false}">

3.5 v-once只渲染一次指令

数据不再绑定,仅在在第一次加载页面时进行渲染。

3.6 v-text和v-cloak指令

在vue管理的模板入口节点作用v-cloak

这两条指令是为了解决模板语法在页面加载时会出现页面抖动。

<h1 v-text="message" ><h1>
<style>
    [v-cloak]{
        display:none;
    }
</style>
<div v-cloak>
    <h1>{{ message }}</h1>
</div>

一般v-text使用较少

3.7 v-show显示标签指令

这条指令是在渲染层上将改标签隐藏。

<h1 v-show="false">hehe</h1>

v-if不同的是,该指令是将改标签的样式设置为display: none,而v-if是直接将该标签从Dom树中移除。

3.8 v-pre跳过编译指令

跳过不需要Vue编译的节点和其子节点,显示其原始内容。可以加快编译。😂

四、 自定义指令

如果到了不得不自己去操作Dom元素时,且没有Vue内置指令来实现需求时。可以使用自定义指令来解决需求。

4.1 定义自定义指令

自定义指令分为全局和局部,即可以定义为在所有组件下都可以使用的自定义指令,也可以定义为只能在当前组件下使用的自定义指令。

4.1.1 全局自定义指令
Vue.directive('指令名',{
    钩子函数
})
4.1.2 局部自定义指令
new Vue({
    ...,
    directives:{
        指令名:{
            钩子函数
        }
    },
    ...
})

4.2 钩子函数

钩子函数就是当自定义指令在何时出发什么样的动作

形式都为钩子名:function([el[,binding]])

其中,el是当前绑定指令的Dom元素,而binding是指令对象其中value属性包含了指令被赋的值。

Vue官方文档中给出的钩子函数有5个:

  • bind -在指令被绑定时执行一次,无法读取被绑定的Dom元素的父节点el.parentNode
  • inserted -和bind一样是在指令被绑定时执行一次,但是在bind后面执行。并且可以读取被绑定的Dom元素的父节点
  • update -当指令所在的被Vue管理的整个模板更新时执行,binding.value是更新后的值
  • componendUpdated -和update一样,但是binding.value是更新前的值
  • unbind -当指令被解绑时执行,例如清除定时器(移除Dom时自动解绑)

4.3 复现一个v-show

    <div id="app">
        <input type="text" v-focus>
        <div style="width: 100px;height: 100px ;background-color:#005151" v-myshow = "show" ></div>
    </div>
4.3.1 全局定义方式
        Vue.directive('myshow',{
            bind:function(el,binding){
                if(binding.value){
                    el.style.display = "block"
                }else{
                    el.style = "none"
                }
            },
            // inserted:function(){
            //     console.log("inserted  is  called")
            // },
            update:function(el,binding){
                if(binding.value){
                    el.style.display = "block"
                }else{
                    el.style.display= "none"
                }
            },
            // componentUpdated:function(){
            //     console.log("componentUpdated  is  called")
            // },
            // unbind:function(){
            //     console.log("unbind  is  called")
            // }
        })

大多数情况都是bind & update钩子函数配合使用的比较多,且大多情况下两者的代码是一样的,所以可以使用简写方式

        Vue.directive('myshow',function(el,binding){
                if(binding.value){
                    el.style.display = "block"
                }else{
                    el.style.display= "none"
                }
        })
4.3.2 局部定义
        app = new Vue({
            el:'#app',
            data:{
                show: true
            },
            directives:{
                myshow:function(el,binding){
                if(binding.value){
                    el.style.display = "block"
                }else{
                    el.style.display= "none"
                }
                }
            }


五、 计算属性和侦听器

5.1 计算属性computed

计算属性要在computed对象中定义。

计算属性的本质就是方法,在使用时就像data中定义的属性那样去使用。

每一个计算属性都必须有一个getter,还可以根据需求来加入一个setter。

  1. 只包含getter的计算属性的一般使用方法
        computed:{
            remaingCount:{
                get(){
                    return this.todoList.filter(item=>!item.completed).length || 0
                }
            }
        }   


  1. 对于只包含getter的计算属性还有一种简写方法
        computed:{
            remaingCount(){
                return this.todoList.filter(item=>!item.completed).length || 0
            }
        },


  1. 同时存在getter和setter的标准计算属性定义方式
        computed:{
            toggleAll:{
                get(){
                    return this.todoList.every(item => item.completed)
                },
                set([value]){
                    let boolean = !this.toggleAll
                    this.todoList.forEach(element => {
                        element.completed = boolean
                    });
                }
            }
        }


5.2 侦听器watch

侦听器要在watch对象中定义。

侦听器的作用是每当侦听对象发生改变时就作出反应。

同情况下计算属性要比侦听器合适,但是还有一些自定义业务需求需要使用侦听器。

  • hanlder:function(val,oldval) 是在触发监听对象时去执行的那个自己定义的方法
  • deep:boolean 当监听对象为一个引用类型的对象时,只能监听到对象整体,无法监听到对象内的个体。当设置为true时即可同时监听到对象内的个体。
  • immediate:boolean 在监听开始前就会调用该监听器下的hanlder方法
        watch:{
            todoList:{
                handler(val,oldVal){
                    window.localStorage.setItem("todoList",JSON.stringify(val))
                },
                deep: true  //深度监听
            },
            //只包含handler时的简写方法
            toggleAll(){
                console.log('计算属性也可以被监听哦')
            }
        },


六、 组件

Vue中,可以将单独功能的模块可以拆分为一个一个的组件。一个项目的组件构成通常为一个树形结构。一个典型的TodoMVC案例可以拆封为这样一个组件示例图:

X8E3XZP7}UXAGIKZ7U)JA0N.png

6.0 组件的使用方式

定义好的属性,只需要像使用html标签一样使用,只不过把标签名换为组建名就可以了

6.1 组件定义方式

组件定义方式和自定义指令一样,有全局和局部之分。

  • 全局定义
Vue.component('myComponent',{
    template:`<p>hello</p>`,
    data:function(){
        return {
            ...,
            ...
        }
    }
})


  • 局部定义
new Vue({
    el:'#app',
    template:`<app></app>`
    components:{
        app:{
            template = `<header></header> .. `,
            components:{
            header:{
             template:`<p>hello i am header</p>`,
             data:function(){
                    return {
                          ...,
                        ...
                    }
                },
                //当然组件中也可以放子组件
                components:{
                   ....
              }
            },
            ...其他组件
            }
        }
    }
})


在Vue根实例中创建的这些组件的父子节点可能不同,但是他们的根组件都是这个Vue实例。因为这个Vue实例本身也是一个组件。

6.2 组件间的通信

6.2.1 直接父子通信
  • 父传子通信(prop down):

要将父组件中的属性值传送给其子组件

  1. 需要在父组件中的模板中调用子组件的部分加上v-bind属性(如传送name属性给其子组件son)

    const Father = {
        template: `<son v-bind:name="name"></son>`,
        data(){
            return {
                name:"jack"
            }
        }
        componends:{
         son
     }
    }
    
    
    
  2. 在子组件体中,使用props数组属性将name接收过来,之后就可以像自己的data定义的属性一样去用了。

    const son = {
        template:`<p> my father is {{ name }}</p>`,
        props:["name"]
    }
    
    
    

注意:Vue不允许在子组件去修改基本类型的父组件传过来的值。但是很不幸。因为传过来的数据如果是引用l类型的话,你可以修改成功,且不会报错,但是请不要这样去做!,如有子元素要修改穿过来的值的需求,请使用子传夫通信方法*

  • 子传父通信(Event up)

父组件的值就要在父组件中进行,所以子组件可以把修改好的父组件数据整一个副本(不要修改原数据),然后通过订阅事件的方法来传给父组件。

  1. 再子组件的方法中生成数据副本

    const son = {
        template:`<button @click = "handleUpdateName"> my father is {{ name }}</button>`,
        props:["name"],
        methods:{
            handleUpateName(){
                let value = 'maojiankai'
                this.$emit('UpdatedName',value) // 向父组件提交UpdatedName事件
            }
        }
    }
    
    
    
  2. 在父组件中订阅对应事件并处理数据

    const Father = {
        template: `<son v-bind:name="name" @UpatedName="handleName"></son>`, //订阅UpdatedName事件
        data(){
            return {
                name:"jack"
            }
        },
        componends:{
         son
     },
        methods:{
            handleName(name){ //对应的处理
                this.name = name 
            }
        }
    }
    
    
    
  • 非父子关系的组件通信

    • 简单环境中 Event Bus
    1. 设组件a,b非父子关系但他们同属于一个Vue根实例,所以在Vue的根实例之前创建一个名字为bus的空Vue实例
    var bus = new Vue()
    
    
    
    1. 在组件A中向bus提交自定义事件
    bus.$emit('Edited',value)
    
    
    
    1. 在组件B的创建钩子函数中,订阅bus中组件A提交的事件
    bus.$on('Edited',function(e){
        ...
    })
    
    
    
    • 复杂环境中 VueX(状态管理模式)

七、插槽

7.1 关于插槽solt

在父组件调用子组件时,会使用子组件的模板HTML内容,但是如果父组件想要加入一些自己的内容到使用子组件的的模板HTML内容可能就是个问题了,尤其是当父组件多次引用子组件时,想要加入不同得内容到子组件模板当中去。

为此,Vue提供了一个插槽---slot标签。在子组件得template中加入一个slot标签后,父组件只需在引用子组件的标签内加入自己想加入的内容即可。

需要说明的是,如果想要插槽具有默认的内容,只需要在加入的slot标签内添加你需要的标签即可。

<div id='app'>
        <cnp><button>hehe</button></cnp>
        <cnp></cnp>
    </div>


    <template id="cnp">
        <div>
            <h2>我是组件</h2>
            <p>123456789</p>
            <slot ><button>footer</button></slot>
        </div>
    </template>   


        const app = new Vue({
            el:'#app',
            components:{
                cnp:{
                    template:'#cnp'
                }
            }
        })


7.2 具名插槽

如果子组件存在多个不同的插槽,当你在调用时只想改动其中的一小部分插槽,其他的插槽都保留其原始值时,就需要用到Vue提供的具名插槽了。

具名插槽的定义是在插槽的name属性上设置插槽的名称

<template>
    <slot name="left"><span>left</span></slot>
    <slot name="center"><span>center</span></slot>
    <slot name="right"><span>right</span></slot>
    <slot><span>defalut</span></slot>
</template>


具名插槽的使用需要在先使用设置了v-slot:name指令的template标签包裹要改动的内容

    <div id='app'>
        <cnp>
            <template v-slot:center>
                <h1 >123</h1>
            </template>
        </cnp>
        <cnp></cnp>
    </div>


7.3 作用域插槽

首先是Vue的编译作用域。

摘自Vue官网:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

其实很意思很简单,比如:

<div id="app">
    <cnp>
        <button v-show='show'>hello</button>
    </cnp>
</div>

<!-- 子组件模板 -->
<template id='cnp'>
    <div>
        <h1>我是模板</h1>
        <slot v-show='show'></slot>
    </div>
</template>

<!-- 入口Vue实例 -->
<script>
    const app = new Vue({
        el:'#app',
        data:{
            show:false
        },
        components:{
            cnp:{
                template:'#cnp',
                data(){
                    return {
                        show:true
                    }
                }
            }
        }
    })
</script>



通过不断调试Vue实例和组件cnp中的show,可以看出。模板tempate属于组件cnp,所以模板中的show是组件cnp中的show,而在实际调用中的show是属于Vue实例中的show。

这说明Vue的是具有编译作用域的,且父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

虽然Vue的编译作用域是合理且安全的。但是影响到了一种插槽需求,就是当引用该子组件时,需要用到一些将子组件的一些数据以不同的形式在组件的插槽内显示。

所以,Vue提供了作用域插槽。

定义v-bind:属性名='表达式'

    <template id="cnp">
        <div>
            <h2>我是组件</h2>
            <p>123456789</p>
            <!-- 定义一个随意名称的属性 -->
            <slot :data='pLanguages'></slot>
        </div>
    </template> 


使用v-solt:name或者default='数据名' (name是具名时用于替换指定插槽,default是默认插槽)

    <div id='app'>
        <cnp>
            <!-- 完整的 -->
            <template v-slot:default='slot'>
                <span>{{ slot.data.join(' - ') }}</span>
            </template>
        </cnp>
    </div>


如果像上例一样使用的是默认插槽,还可以使用简写方式

简写时请勿与具名插槽混用

    <div id='app'>
        <cnp>
            <!-- 简写的 -->
            <template v-slot='slot'>
                <span>{{ slot.data.join(' - ') }}</span>
            </template>
        </cnp>
    </div>


如果是多个插槽,则需要使用完整的使用格式。

Example

比如现在要来实现将子组件中提供的编程语言数组在插槽中以两种不同形式显示

    <div id='app'>
        <cnp>
            <template v-slot:default='slot'>
                <span>{{ slot.data.join(' - ') }}</span>
            </template>
        </cnp>
        <cnp>
                <template v-slot='slot'>
                        <span>{{ slot.data.join(' * ') }}</span>
                    </template>
        </cnp>
    </div>

    <template id="cnp">
        <div>
            <h2>我是组件</h2>
            <p>123456789</p>
            <slot :data='pLanguages'></slot>
        </div>
    </template>    

    <script>

        const app = new Vue({
            el:'#app',
            components:{
                cnp:{
                    template:'#cnp',
                    data(){
                        return {
                            pLanguages:['Java','C++','JavaScript','Python','Php']
                        }
                    }
                }
            }
        })
    </script>


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • Vue 实例 属性和方法 每个 Vue 实例都会代理其 data 对象里所有的属性:var data = { a:...
    云之外阅读 2,198评论 0 6
  • vue概述 在官方文档中,有一句话对Vue的定位说的很明确:Vue.js 的核心是一个允许采用简洁的模板语法来声明...
    li4065阅读 7,185评论 0 25
  • 组件(Component)是Vue.js最核心的功能,也是整个架构设计最精彩的地方,当然也是最难掌握的。...
    六个周阅读 5,580评论 0 32
  • 一、了解Vue.js 1.1.1 Vue.js是什么? 简单小巧、渐进式、功能强大的技术栈 1.1.2 为什么学习...
    蔡华鹏阅读 3,311评论 0 3
  • 什么是组件? 组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装...
    youins阅读 9,449评论 0 13