Web前端On the Road(成为大牛)VUE我爱编程

Vue.js学习笔记-基础部分+完整实现代码

2017-04-27  本文已影响1737人  冥冥2017

下载安装搭建环境

第一个demo

Hello Vue!
关键词:模板语法、声明式

这里有个小bug,JSFiddle Hello World例子中,并没有为new Vue赋值变量,于是教程中接下来说的,“修改app.message”就没法实现,认真看教程中代码可以看到它为其赋值命名为app,下一个例子命名为app-2,命名后就可以在控制台采用“名称.属性”的形式访问修改内容了。

第二个demo

新指令:v-bind
用法


新指令:v-if
用法:根据值的真假决定是否渲染DOM内容
原理:Vue可以绑定DOM结构到数据


新指令:v-for
用法

原理:v-for是一个包装过的for in循环,对应的data内需要有一个数组


新指令:v-on
用法


新指令:v-model
用法:

原理:v-model可实现表单输入和应用状态的双向绑定,修改表单内容,message对应值也会修改。

组件化应用构建

Vue.component('todo-item',{
      template:'<li>这是一个待办项</li>'
})
<ol>
    <todo-item></todo-item>
</ol>

但是这样实现,会为每个元素绑定相同文本,Vue可以将数据从父作用域传到子组件。

Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})
<div id="app-7">
  <ol>
    <todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
  </ol>
</div>
var app7 = new Vue({
  el: '#app-7',
  data: {
    groceryList: [
      { text: '蔬菜' },
      { text: '奶酪' },
      { text: '随便其他什么人吃的东西' }
    ]
  }
})

Vue实例

vm.$el===document.getElementById('exzample');

模板语法

<span>Message:{{ msg}}</span>

也可以通过v-once执行一次性插值,不再更新内容,但是这会影响这个节点的所有数据绑定

<span v-once>This will never change:{{message}}</span>
<div id="app-8" v-html='rawHtml'>   </div>
var app8=new Vue({
        el:'#app-8',
        data:{
            rawHtml:'<li>纯html</li>'
        }
    })
//输出:● 纯html

但是要注意只对可信内容使用html插值,不然容易导致xss攻击

<div id="app-2">
    <span v-bind:title="message">悬停几秒看信息</span>
</div>
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>

然而,语句和流控制是不行滴,三元表达式是可以的

{{var a = 1 }}
{{if(ok){return message}}}
<form v-on:submit.prevent="onSubmit"></form>
<div id="app-9">{{message|format}}</div>
var app9=new Vue({
        el:'#app-9',
        data:{
            message:'hello,Filters!'
        },
        filters:{
            format:function(value){
                value=value.toLowerCase()
            return value.charAt(0).toUpperCase()+value.slice(1)
            }
        }
    })

过滤器可以串联,可以接受参数

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

v-on缩写为@,并省略了后面的冒号

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

计算属性

var app11=new Vue({
        el:'#app-11',
        data:{
            firstName:'Foo',
            lastName:'Bar'
        },
        computed:{
            fullName:{
                get:function(){
                    return this.firstName+' '+this.lastName
                },
                set:function(newValue){
                    var names = newValue.split(' ')
                    this.firstName=names[0]
                    this.lastName=names[names.length-1]
                }   
            }
            
        }
    })

class和style绑定

对class的增强绑定:

<div id="app-01" v-bind:class="{active:isActive}"></div>
var app01=new Vue({
        el:'#app-01',
        data:{
            isActive:true
        }
    })
//div class="active"
<div class="static"
     v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
<div id="app-02" v-bind:class='classObject'></div>
var app02=new Vue({
        el:'#app-02',
        data:{
            classObject:{
            active:true,
            'text-danger':false
            }
        }
        
    })
computed: {
  classObject: function () {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal',
    }
  }
}

总结:只要冒号后的数据形式为‘名称:布尔值’就可以

<div v-bind:class="[activeClass, errorClass]">
data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}
<div v-bind:class="[{ active: isActive }, errorClass]">
<my-component v-bind:class="{ active: isActive }"></my-component>

和内置元素的使用差不多

对style样式增强绑定:
由于默认格式里就包括一对引号,要注意单双引号的区分,不然会出错

<div id='app-03' v-bind:style='{color:activeColor,fontSize:fontSize + "px"}'>
<p>1111</p>
</div>
var app03=new Vue({
        el:'#app-03',
        data:{
            activeColor:'red',
            fontSize:30
        }
    })

条件渲染

<div id="app-04">
    <p v-if="ok">Yes</p>
    <p v-else>No</p>
</div>
var app04=new Vue({
        el:'#app-04',
        data:{
            ok:false
        }
    })
<div id="app-04">
    <template v-if="ok">
    <h1>Title</h1>
    <p>Para</p>
    </template>
    <p v-else>No</p>
</div>
<div id="app-05">
<template v-if="loginType==='username'">
    <label for="">Username</label>
    <input placeholder="Enter your username">
</template> 
<template v-else>
    <label for="">Email</label>
    <input placeholder="Enter your email address">
</template>
<button v-on:click="toggle">Toggle login type</button>
</div>
var app05=new Vue({
        el:'#app-05',
        data:{
            loginType:'username'
        },
        methods:{
            toggle:function(){
                this.loginType==='username'?this.loginType='email':this.loginType='username'
            }
        }

    })

不复用的如下:

<div id="app-05">
<template v-if="loginType==='username'">
    <label>Username</label>
    <input placeholder="Enter your username" key="username">
</template> 
<template v-else>
    <label>Email</label>
    <input placeholder="Enter your email address" key="email">
</template>
<button v-on:click="toggle">Toggle login type</button>
</div>
<h1 v-show='ok'>222</h1>

v-show vs v-if
v-show适合频繁切换,v-if适合条件不太改变的情况

列表渲染

<ul id="app-06">
    <li v-for="item in items">{{item.message}}</li>
</ul>
var app06=new Vue({
        el:'#app-06',
        data:{
            items:[
            {message:'Foo'},
            {message:'Bar'}
            ]
        }
    })
<ul id="app-06">
    <li v-for="(item,index) in items">{{parentMessage}}-{{index}}-{{item.message}}</li>
</ul>
<ul id="app-07">
    <template v-for="(item,index) in items">
        <span>组合列表{{index+1}}</span>
        <li>{{item.message}}</li>       
    </template>
</ul>
<ul id="app-08">
    <li v-for="(item,key,index) in object">
        {{item}}-{{key}}-{{index}}
    </li>
</ul>
<ul id='app-09'>
  <li v-for="n in 10">{{n}}</li>
</ul>

但是同样需要建立Vue对象,

var app09=new Vue({
        el:'#app-09'
    })
<ul id="app-10" >
<li v-for="(item,index) in items"  :key='item.id=index+3'>{{item.message}}-{{index}}-{{item.id}}</li>
</ul>

这里我绑了元素的index在id上

vm.items[indexOfItem] = newValue
Vue.set(example1.items,indexOfItem,newValue)
example1.items.splice(indexOfItem,1,newValue)

注意这里的第二个参数为"1",表示原地替换原元素

vm.items.length = newLength

但是依旧可以用万能的splice:

example1.items.splice(newLength)
第二个参数为删除个数,不填第二个参数时表示删除到末尾

此处翻阅了犀牛书和高程,高程中没提到splice()省略第二个参数的情况,犀牛书提到了,省略第二个参数,从起始点到结尾的所有元素都将被删除。查了ecma-262的文档,原文是:

5  If the number of actual arguments is 0, then
     a. Let insertCount be 0.
     b. Let actualDeleteCount be 0.
6  Else if the number of actual arguments is 1, then 
     a. Let insertCount be 0. 
     b. Let actualDeleteCount be len ‑ actualStart.
<ul id="app-11">
    <li v-for="n in evenNumbers">{{n}}</li>
</ul>
var app11=new Vue({
        el:'#app-11',
        data:{
            numbers:[1,2,3,4,5]
        },
        computed:{
            evenNumbers:function(){
                return this.numbers.filter(function(number){
                    return number%2 === 0
                })
            }
        }
    })
<ul id="app-11">
    <li v-for="n in evenNumbers(numbers)">{{n}}</li>
</ul>
var app11=new Vue({
        el:'#app-11',
        data:{
            numbers:[1,2,3,4,5]
        },
        methods:{
            evenNumbers:function(){
                return this.numbers.filter(function(number){
                    return number%2 === 0
                })
            }
        }
    })

事件处理器

<div id="app-12">
    <button v-on:click="counter+=1">增加1</button>
    <p>这个按钮被点击了{{counter}}次</p>
</div>
var app12=new Vue({
        el:"#app-12",
        data:{
            counter:0
        }
    })
<div id="app-13">
    <button v-on:click="say('hi')">1</button>
    <button v-on:click="say('what')">2</button>
</div>
var app13=new Vue({
        el:'#app-13',
        data:{
            message:'new event!'
        },
        methods:{
            say:function(m){
                alert(m)
            }
        }
    })
<div id="app-14">
    <button v-on:click="mod('event is modified',$event)">submit</button>
</div>
var app14=new Vue({
        el:'#app-14',
        methods:{
            mod:function(message,e){
                if(e)e.preventDefault()
                alert(message)
            }
        }
    })

代码中点击button会触发一个click事件,把event作为参数传入,就可以对这个事件进行操作

<input type="text" id="app-1" v-on:keyup.13="submit()">
var app1=new Vue({
   el:'#app-1',
   methods:{
       submit:function(){
           alert('success')
       }
   }
})

keyup.13是"enter"
还有别名:

<input type="text" v-on:keyup.13="submit">
<input type="text" v-on:keyup.enter="submit">
<input type="text" @keyup.enter="submit">
Vue.config.keyCodes.f2=113

表单控件绑定

v-model本质上是语法糖,监听输入事件以更新数据

<div id="app-2">
    <input type="text" v-model="message" placeholder="'edit me">
    <p>Message is:{{message}}</p>   
</div>
var app2=new Vue({
    el:'#app-2',
    data:{
        message:''
    }   
})

多行文本同理

<div id="app-3">
    <input type="checkbox" id="chechbox" v-model="checked">
    <label for="checkbox">{{checked}}</label>
</div>
var app3=new Vue({
    el:'#app-3',
    data:{
        checked:false
    }
})

这里,checked预设值如果设为空,则开始时label没有内容,如果设为true,则选框会默认选上,所以最好只设为false

<div id="app-4">
    <input type="checkbox" value="Jack" v-model="checkedNames" id="jack">
    <label for="jack">jack</label>
    <input type="checkbox" value="John" v-model="checkedNames" id="john">
    <label for="john">john</label>
    <input type="checkbox" value="Mike" v-model="checkedNames" id="mike" >
    <label for="mike">mike</label>
    <br>
    <span>checked names: {{ checkedNames }}</span>
</div>
var app4=new Vue({
    el:'#app-4',
    data:{
        checkedNames:[]
    }
})
<div id="app-6">
    <select name="" id="" v-model="selected">
        <option>A</option>
        <option>B</option>
        <option>C</option>
    </select>
    <span>selected:{{selected}}</span>
</div>
var app06=new Vue({
    el:'#app-6',
    data:{
        selected:""
    }
})

此处遇到一小问题,建立option时,emmet的补全加了属性value='',如果不改动的话,默认option的值是为空的,选中无效,需要删除这个属性或者显式填值

<div id="app-6">
    <select v-model="selected">
        <option v-for="option in options" v-bind:value="option.value">{{option.text}}</option>
    </select>
    <span>selected:{{selected}}</span>
</div>
var app06=new Vue({
    el:'#app-6',
    data:{
        selected:'A',
        options:[
        {text:'One',value:'A'},
        {text:'Two',value:'B'},
        {text:'Tree',value:'C'}
        ]
    }
})

此例子将选项内容与选项值区分成两组数据,而selected显示的是value的内容,同上一个小问题相同,option不显式设置值时,option标签内容即为值,显式设置后,value值会覆盖标签内容被发送。

<div id="app-7">
    <input id="bindvalue" type="checkbox" v-model="toggle" v-bind:true-value="a" v-bind:false-value='b'>
    <label for="bindvalue">{{toggle}}</label>
</div>
var app07=new Vue({
    el:'#app-7',
    data:{
        toggle:'false',
            a:'true',
            b:'false'       
    }
})

例子中绑定了true-value到a变量,false-value到b变量
单选框同理:

<input type="radio" v-model='pick' v-bind:value="a">
<input v-model.lazy="msg">
<input v-model.number='age' type='number'>

这个例子玄机很多:
输入字母e/E情况特殊,会被理解为指数标志,不同设定效果不同:
1. 只设置type为number:字母输入无效;e后为有效数字时会正常显示,比如:

输入:123abc456,
框内显示为:123456,值显示为123456
、、、
输入:123e12,
框内显示为:123e12  值显示为:123e12

但是超过一定数值会显示为空:

(很大很大一个数字)显示为:
123e1234 值显示为:

然而负指数会正确显示:

123e-1234 值显示为 123e-1234
   2. 只设置v-model为number时:
输入:123abc456,
框内显示为123abc456,失去焦点时更新为123,
值显示为123
、、、
输入:abc123,
框内显示为abc123,失去焦点没变化,
值显示为abc123,说明判定失效啦
、、、
123e12 值显示为:123000000000000
——同时输入框内会被更新成123000000000000

指数e超过一定数值会显示为Infinity

123e1234值显示为:Infinity
——同时输入框内会被更新成Infinity

在一个范围内的负指数会被格式化后显示,
超过一定值的负指数会被显示为0:

123e-123  值显示为:1.23e-121
123e-1234 值显示为:0
——同时输入框内会被更新
   3. 设置type为number并且v-model为number时:
输入123abc456,
框内显示为:123456,值显示为123456
、、、
输入abc123,
框内显示为:123,值显示为123
、、、
123e12 值显示为:123000000000000
——同时输入框内会被更新成123000000000000
指数大于20位会转化成指数形式

超过一定数值显示为空:

123e1234 值显示为:

在一个范围内的负指数会被格式化后显示,
超过一定值的负指数会被显示为0:

123e-123  值显示为:1.23e-121
123e-1234 值显示为:0
——同时输入框内会被更新

总结:控制数字输入的主要依赖type="number",v-model.number是将输入值转化为Number类型,但是表现不太稳定。

空空abc空空def空空  ——>  abc空空def

组件

<div id="app-01">
    <my-component></my-component>
</div>
Vue.component('my-component',{
    template:'<div>A custom component!</div>'
})
var app01=new Vue({
    el:'#app-01'
})

要确保,先注册组件,再初始化实例,调换前后顺序会出错

<div id="app-01">
    <my-component></my-component>
</div>
var app01=new Vue({
    el:'#app-01',
    components:{
        'my-component':{
            template:'<div>A custom component!</div>'
        }
    }
})

注意此处相当于先实例再在实例中注册组件,因此可以判断,注册组件行为不能落后于实例化,但是可以包含在实例的同时进行。

<table>
  <my-row>...</my-row>
</table>

需要使用特殊的is属性:

<table id="app-01">
    <tr is="my-component"></tr>
</table>

这相当于把组件伪装成一个tr ,查看浏览器文档结构解析为:

<table id="app-01">
        <tbody>
        <div>A custom component</div>——(组件内容)
        </tbody>
</table>

tbody问题
此处出现的tbody是html解析自动补全的,tbody功能为,划分表格结构,默认情况下是一个tbody,也可手动建立多个并行的tbody,这影响表格分别渲染及切分(很长的表格换页的时候断在哪里就是切分)

<div id="app-02">
    <simple-counter></simple-counter>
    <simple-counter></simple-counter>
    <simple-counter></simple-counter>
</div>
如果是:
var data={counter:0}
var app02=new Vue({
    el:'#app-02',
    components:{
        'simple-counter':{
            template:'<button v-on:click="counter +=1">{{counter}}</button>',
            data:function(){
                    return data
                }
        }
        
    }
则修改一个模板,其他模板数字也被修改,因为data的返回值是data自身,而所有模板都会引用data,都有修改权。
改一下:
var app02=new Vue({
    el:'#app-02',
    components:{
        'simple-counter':{
            template:'<button v-on:click="counter +=1">{{counter}}</button>',
            data:function(){
                    return {counter:0}
                }
        }
        
    }
每个模板可以有独立的值了,因为data作为一个函数返回的是不同的counter,不会被共用

模板内部的data与外部是冲突的

构成组件
<div id="app-03">
    <child message="hello"></child>
    <div>{{value}}</div>
</div>
var app03=new Vue({
    el:'#app-03',
    components:{
        'child':{
            template:'<span>{{message}}</span>',
            props:['message']
        }
    },
    data:{
        value:'yyy'
    }
})

此例子中,props请求的message通过组件元素传入,即特性传入,props与外部的data不冲突

props:['myMessage']——js内
<child-11 my-message="hello"></child-11>——html内
<div id="app-03">
    <input type="text" v-model='parentMsg'>
    <child-11 v-bind:my-message="parentMsg"></child-11>
</div>
var app03=new Vue({
    el:'#app-03',
    components:{
        'child-11':{
            template:'<span>{{myMessage}}</span>',
            props:['myMessage']
        }
    },
    data:{
        parentMsg:''
    }
})
<comp some-prop='1'></comp>
由于这是一个字面prop,传递了一个字符串'1',它的值是字符串‘1’而不是number
<comp v-bind:some-prop='1'></comp>
这样会把值当做一个javascript表达式计算,才是number
(存疑)
- prop是单向绑定:父组件属性变化时,将传到给子组件,但不会反过来。同时,每次父组件更新时,子组件的所有prop都会更新为最新值,这意味着,不应该在子组件内部改变prop。但是还要用怎么办呢?
  - 定义一个局部变量,用prop初始化
  - 定义一个计算属性,处理prop值
然而,js中,对象和数组是引用类型,指向同一个内存空间,如果prop是对象或者数组,在子组件内部改变也会影响父组件的状态。
- Prop验证

<div id="app-04">
<child prop-c="111"></child>

<child prop-c="333" prop-d="222"></child>
</div>
var app04=new Vue({
el:'#app-04',
components:{
'child':{
template:'<span>{{propC}}-{{propD}}-{{propE}}</span>',
props:{
propC:{
type:String,
required:true
},
propD:[String,Number],
propE:{
type:[String,Number],
default:100
}
}
}
}
})

有几种验证:
 - 指定类型验证:

单个的,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 value>0
}
这个验证失败时是控制台报错


- type的类型是以下原生构造器:
 - String
 - Number
 - Boolean
 - Function
 - Object
 - Array

#####自定义事件

<div id="app-05">
<p>{{total}}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
var app05=new Vue({
el:'#app-05',
components:{
'button-counter':{
template:'<button v-on:click="increment">{{counter}}</button>',
data:function(){
return{
counter:0
}
},
methods:{
increment:function(){
this.counter+=1
this.$emit('increment')
}
}
}
},
data:{
total:0
},
methods:{
incrementTotal:function(){
this.total+=1
}
}
})

这个例子中,创建了父元素和内部的子组件,子组件包含一个点击事件,v-on:click=‘increment’,点击后改变counter数值,同时为了上传事件,在函数increment内部自定义了一个事件,并在点击时触发此事件,$emit,也叫increment,并在子元素上绑定了这个事件,v-on:increment,然后对此在父元素上绑定了一个函数,incrementTotal,从而达到计算总数的效果。
我觉得这个名字区分一下比较好,子组件的方法不必和触发事件同一个名字,所以改一下:

<div id="app-05">
<p>{{total}}</p>
<button-counter v-on:user-defined="incrementTotal"></button-counter>
<button-counter v-on:user-defined="incrementTotal"></button-counter>
</div>
var app05=new Vue({
el:'#app-05',
components:{
'button-counter':{
template:'<button v-on:click="increment">{{counter}}</button>',
data:function(){
return{
counter:0
}
},
methods:{
increment:function(){
this.counter+=1
this.$emit('user-defined')
}
}
}
},
data:{
total:0
},
methods:{
incrementTotal:function(){
this.total+=1
}
}
})

- 给组件绑定原生事件
用.native修饰v-on

<new-component v-on:click.native="doSomeThing"></new-component>

- v-on与$on的不同:
v-on可以被父组件用来监听子组件的事件
(存疑):$on的用法
- v-model的使用:
 - ref与$.refs——注册引用信息和使用:
   - 在元素上注册:<p ref="a">hello</p>
使用注册:vm.$refs.a就是p这个DOM节点
   - 在组件上注册:<input ref="b">
使用注册:this.$refs.b就是指向了组件实例,this.$refs.b.value指向的是组件input.value
  - mounted ——生命周期钩子,当el被新创建的vm.$el替换,并挂载到实例上之后调用该钩子
 - NumberObject.toFixed(num)——把Number四舍五入为指定小数位数的数字

<script src="https://cdn.rawgit.com/chrisvfritz/5f0a639590d6e648933416f90ba7ae4e/raw/98739fb8ac6779cb2da11aaa9ab6032e52f3be00/currency-validator.js"></script>
//载入一个验证插件
<div id="app-07">
<currency-input label="Price" v-model="price"></currency-input>
<currency-input label="Shipping" v-model="shipping"></currency-input>
<currency-input label="Handling" v-model="handling"></currency-input>
<currency-input label="Discount" v-model="discount"></currency-input>
//此处有一个合计
<p>Total: ${{total}}</p>
</div>
//组件部分
Vue.component('currency-input',{
template:'<div>
//如果有label,则显示,否则不显示
<label v-if="label">{{label}}</label>
$<input ref="input" v-bind:value="value" v-on:input="updateValue($event.target.value)"
v-on:focus="selectAll" v-on:blur="formatValue"></div>',
props:{
//对参数进行验证
value:{
type:Number,
default:0
},
label:{
type:String,
default:''
}
},
//生命周期钩子,当组件被挂载到实例时会触发
mounted:function(){
this.formatValue()
},
methods:{
updateValue:function(value){
var result=currencyValidator.parse(value,this.value)
if(result.warning){
this.$refs.input.value=result.value
}
this.$emit('input',result.value)
},
formatValue:function(){
this.$refs.input.value =currencyValidator.format(this.value)
},
//做setTimeout是由于safari此处有bug,不考虑兼容的话不必如此
selectAll:function(event){
setTimeout(function(){
event.target.select()
},0)
}
}
})

- 非父子组件间的通信(简单场景)——创建一个空的Vue实例作为连接:

var bus = new Vue()
//在A组件内
bus.$emit('id-selected', 1)
//在B组件内
bus.$on('id-selected', function (id) {
// ...
})

- slot 内容分发
 - 单个slot:子组件只有一个没有名字的slot时,父组件的整个内容片断会插入到slot所在的DOM位置
 - 具名slot:

子模板
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
父模板
<app-layout>
<h1 slot="header">这里可能是一个页面标题</h1>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
<p slot="footer">这里有一些联系信息</p>
</app-layout>
渲染结果
<div class="container">
<header>
<h1>这里可能是一个页面标题</h1>
</header>
<main>
<p>主要内容的一个段落。</p>
<p>另一个主要段落。</p>
</main>
<footer>
<p>这里有一些联系信息</p>
</footer>
</div>

 - 作用域插槽:子组件中把数据传递到插槽slot,父元素用一个template scope="props" 获取数据,然后就可以使用了

Vue.component('child',{
template:'<div><slot text="hello from child"></slot></div>'
})
var app03=new Vue({
el:'#app-03',
components:{
'parent-component':{
template:'<div>
<child>
<template scope="props">
<span>hello from parent</span>
<span>{{props.text}}</span>
</template>
</child></div>'
}
}
})
//显示为:
hello from parent hello from child

 - 更复杂的列表组件的例子:

//需要把mylist传递进parent-list
<parent-list id="app-04" v-bind:mylist="mylist"></parent-list>
Vue.component('child-list',{
props:['mylist'],
template:'<ul><slot name="item" v-for="item in mylist" v-bind:text="item.text"></slot></ul>'
})
var app04=new Vue({
el:'#app-04',
data:{
mylist:[
{text:'蔬菜'},
{text:'水果'},
{text:'肉'}
]
},
components:{
'parent-list':{
props:['mylist'],
//此处也需要把mylist传递进child-list
template:'<child-list v-bind:mylist="mylist">
<template slot="item" scope="props">
<li>{{props.text}}</li>
</template>
</child-list>'
}
}
})
显示为:

- 渲染一个元组件为动态组件,根据is的值来决定渲染哪个组件

<component id="app-06" v-bind:is="currentView"></component>
var app06=new Vue({
el:'#app-06',
data:{
currentView:'posts'
},
components:{
'home':{
template:'<span>home</span>'
},
'posts':{
template:'<span>posts</span>'
},
'archive':{
template:'<span>archive</span>'
}
}
})
//输入为posts

- keep-alive保留状态或避免重新渲染

<keep-alive><component id="app-06" v-bind:is="currentView"></component></keep-alive>

- 可复用组件,三个接口:
 - props
 - events
 - slots

<my-component
:foo="baz"
:bar="qux"
@event-a="doThis"
@event-b="doThat"

<img slot="icon" scr="...">
<p slot="main-text">Hello!</p>
</my-component>

- 子组件索引:使用ref指定一个索引id

<div id="parent">
<user-profile ref="profile"></user-profile>
</div>
var parent = new Vue({el:'#parent'})
var child = parent.$refs.profile

当ref与v-for一起使用时,ref是一个数组或对象,包含相应的子组件,是非响应式的,应避免在模板或计算属性中使用
- 异步组件,接受一个工厂函数,可以动态解析组件的定义:
<div id="app-07">
  <recursion-component :count="0"></recursion-component>
</div>
Vue.component('recursion-component',{
  props:['count'],
  name:'count',
  template:'<div><span>{{count}}</span><recursion-component :count="count+1" v-if="count<10"></recursion-component></div>'
})
var app07=new Vue({
  el:'#app-07'
})
//输出是:0 1 2 3 4 5 6 7 8 9 10
beforeCreate:function(){
this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue')
}

此处(存疑,以后用了webpack再说)
文件目录树的例子:

<div id="app-08">
  <tree-folder :folder="folder"></tree-folder>
</div>
Vue.component('tree-folder',{
  template:'<p><span>{{folder.name}}</span><tree-folder-contents :children="folder.children"/></p>',
  props:['folder']
})
Vue.component('tree-folder-contents',{
  template:'<ul><li v-for="child in children">\
  <tree-folder v-if="child.children" :folder="child"/>\
  <span v-else>{{child.name}}</span>\
  </li></ul>',
  props:['children']
})
var app08=new Vue({
  el:'#app-08',
  data:{
    folder:{
      name:'总目录',
      children:[
        {name:"二层目录1"},
        {name:"二层目录2",
          children:[
          {name:"三层目录1"},
          {name:"三层目录1"}
          ]
        },
        {name:'二层目录3'}
      ]
    }
  }
})
//输出:
总目录
  - 二层目录1
  - 二层目录2
     - 三层目录1
     - 三层目录2
  - 二层目录3
<parent-com><span>啦啦啦</span></parent-com>
\\js部分
Vue.component('parent-com',{
  template:'<div><child-com><span>this is part of parents</span></child-com></div>'
})
\\默认情况下,“啦啦啦”作为分发内容,不会显示,而是显示this is part of parents.
\\如果改为:
<parent-com inline-template><span>啦啦啦</span></parent-com>
\\则显示为“啦啦啦”,原设定的template被覆盖了
<div id="app-10">
  <hello-world></hello-world>
</div>
\\先单独一个js,类型为type="text/x-template",并且需要有id
<script type="text/x-template" id="hello-world-template">
  <p>Hello hello hello</p>
</script>
\\然后在另一个js里创建组件,只不过模板可以直接引用这个id:
Vue.component('hello-world', {
  template: '#hello-world-template'
})
\\别忘了创建实例(掩面)
var app10=new Vue({
  el:'#app-10'
})
Vue.component('terms-of-service', {
  template: '\
    <div v-once>\
      <h1>Terms of Service</h1>\
      ... a lot of static content ...\
    </div>\
  '
})

基础部分结束

上一篇 下一篇

猜你喜欢

热点阅读