//HTML
<div id="app">
<input type="text" v-model="name" placeholder="你的名字">
<h1>你好,{{ name }}</h1>
</div>
<script>
var abc= new Vue({
//创建一个Vue对象,指定一个页面中已存在的DOM元素来挂载Vue实例,就是关联起来
el: '#app',
data:{
//data中可以声明应用内需要双向绑定的数据,建议所有会用到的数据都预先在data内声明
name: ''
}
})
</script>
//关联成功后可以通过abc.$el来访问该元素,Vue提供了很多常用的实例属性与方法,都以$开头
//v-model="它的值对应于我们创建的Vue实例的data选项中的字段名,这样就进行了数据绑定"
//v-html 输出HTML,而不是将数据解释后的纯文本,如果将用户产生的内容使用v-html输出后,有可能
//导致XSS攻击,所以要在服务端对用户端对用户提交的内容进行处理,一般可将"<>"转义
//如果想显示{{}}而不进行替换,使用v-pre
v-bind 动态更新HTML元素上的属性 v-bind:href="" v-bind:src=""
v-on 绑定事件监听器,v-on:click=“写在Vue实例的methods属性内的方法名,函数内的
this指向的是当前Vue实例本身,因此可以直接使用this.xxx的形式来访问或修改数据”
v-bind 可以直接写成 :
v-on 可以写成 @
计算属性 computed
遇到复杂的逻辑时应用使用计算属性,说白了就是放到方法里
所有的计算属性都以函数的形式写在Vue实例内的computed选项内,最终返回计算后的结果
在一个计算属性里可以完成各种复杂的逻辑,包括运算、函数调用等,只要最终返回一个结果就可以
写在methods中也可以实现,为什么要写在computed中呢?
因为计算属性是基于它的依赖缓存的。一个计算属性所依赖的数据发生变化时,它才会重新取值,所以text只要不改变,计算属性也就不更新。但是methods不同,只要重新渲染,它就会被调用,因此函数也会被执行
内置指令
v-once 一个不需要表达式的指令,作用是定义它的元素或组件只渲染一次,包括元素或组件的所有子节点。首次渲染后,不再随数据的变化重新渲染,将被视为静态内容
<div id="app">
<span v-once>{{message}}</div>
</div>
v-once在业务中也很少使用,当你需要进一步优化性能时,可能会用到
<p v-if="status===1"></p>
<p v-else-if="status===2"></p>
<p v-else></p>
v-show的用法与v-if基本一致,只不过v-show改变元素的CSS属性display,当表达式的值为false时,元素会隐藏
列表渲染指令v-for
当需要将一个数组遍历或枚举一个对象循环显示时,就会用到列表渲染指令v-for
<div id="app">
<ul>
<li v-for = "book in books">{{ book.name }} </li>
</ul>
</div>
<script>
var app = new Vue({
el : '#app',
data:{
books:[
{name: '三国演义'},
{name: '水浒传'},
{name: '红楼梦'},
]
}
})
</script>
数组更新
Vue的核心是数据与视图的双向绑定,Vue包含了一组观察数组变异的方法,使用它们改变数组也会触发视力更新
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
在以下变动的数组中,Vue是不能检测到的,也不会触发视图更新
通过索引直接设置项,比如app.books[3]={}
修改数组长度,比如app.books.length = 1
解决第一个问题可以用两种方法实现同样的效果
1.使用Vue内置的set方法
Vue.set(app.books,3,{
name: ‘《CSS揭秘》’,
author: ‘’
});
如果是在webpack中使用组件化的方式,默认是没能导入Vue的,可以使用
this.$set({
});
2. app.books.splice(3,1,{});
第二个问题也可以直接用splice来解决
app.books.splice(1);
过滤与排序
当你不想改变原数组,想通过一个数组的副本来做过滤或排序的显示时,可以使用计算属性来返回过滤或排序后的数组
表单与v-model
使用v-model时,如果是用中文输入法输入中文,一般在没有选定词组前,也就是在拼音阶段,Vue是不会更新数据的,当敲下汉字时才会触发更新,如果希望总是实时更新,可以用@import来替代v-model
事实上,v-model也是一个特殊的语法糖,只不过它会在不同的表单上智能处理
单选按钮radio
单选按钮在单独使用时,不需要v-model,直接使用v-bind绑定一个布尔类型的值,为真时选中,为否是不选
<div id="app">
<input type="radio" :checked="picked">
<label>单选按钮</label>
</div>
<script>
var app = new Vue({
el:'#app',
data:{
picked:true
}
})
</script>
复选框checkbox
<input type="checkbox" :checked="picked">
选择列表
//给select 添加属性multiple就可以多选了,此时v-model绑定的是一个数组
<select v-model="">
<option value="">
option是备选项,如果含有value属性,v-model就会优先匹配value的值
</option>
</select>
组件Component
Vue.js最核心的功能,也是整个框架设计最精彩的地方
Vue.js的组件就是提高重用性的,让代码可复用
组件需要注册后才可以使用,注册有全局注册和局部注册两种方式
全局注册后,任何Vue实例都可以使用
Vue.component(‘my-component’,{
//选项
})
组件自定义标签名称,推荐使用小写加减号分割的形式命名
要在new Vue实例中使用组件,必须要在实例创建前注册
局部注册组件 components 可以局部注册组件
组件中也可以使用components来注册组件,使组件可以嵌套
<div id="app">
<my-component></my-component>
</div>
<script>
var Child={
template: '<div>局部注册组件的内容</div>'
}
var app = new Vue({
el: '#app'
components:{
'my-component':Child
}
})
</script>
Vue组件的模板在某些情况下会受到HTML的限制,比如<table>内规定只允许是<tr><td><th>
等这些表格元素,这种情况下,可以使用特殊的is属性来挂载组件
如果使用的是字符串模板,是不受限制的,比如后面章节介绍的.vue单文件用法等
除了template选项外,组件中还可以像Vue实例那样使用其他的选项,比如
data、computed、methods等,但是在使用data时,data必须是函数,然后将数据return出去
<div id = "app">
<my-component></my-component>
</div>
<script>
Vue.component('my-component',{
template:'<div>{{message}}</div>',
data:function(){
return {
message:'组件内容'
}
}
});
var app = new Vue({
el:'#app'
})
</script>
使用props传递数据
组件不仅仅是要把模板的内容进行复用,更重要的是组件间要进行通信。
通常父组件的模板中包含子组件,父组件要正向地向子组件传递数据或参数,子组件接收到后根据参数的不同来渲染不同的内容或执行操作,这个下身传递数据的过程就是通过prop来实现的
在组件中,使用选项props来声明需要从父级接收的数据,props的值可以是两种,一种是字符串数组,一种是对象
<div id="app">
<my-component abc="数据"></my-component>
</div>
<scrtip>
Vue.component('my-component',{
//接收来自父组件的数据
props:['abc'],
//使用接收到的数据abc
template:'<div>{{abc}}</div>'
});
var app = new Vue({
el:'#app'
})
</script>
props中声明的数据与组件data函数return的数据主要区别是props来自父级
而data中的是组件自己的数据,作用域是组件本身
这两种数据都可以在模板template及计算属性computed和方法methods中使用
由于HTML特性不区分大小写,当使用DOM模板时,驼峰命名的props名称要转为短横分隔命名,例如
appName app-name
有时候,传送的数据并不是直接写死的,而是来自父级的动态数据,这时可以使用指令v-bind来动态绑定props的值
当父组件的数据变化时,也会传递给子组件
<div id="app">
<input type="text" v-model="parentMessage">
//使用指令v-bind来动态绑定props的值
<my-component :message="parentMessage"></my-component>
</div>
<script>
Vue.component('my-component',{
props:['message'],
template:'<div>{{message}}</div>'
});
var app = new Vue({
el:'#app',
data:{
parentMessage:''
}
})
</script>
组件关系可分为父子组件通信,兄弟组件通信、跨级组件通信
子组件用$emit()来触发事件,父组件用 $on()来监听子组件的事件
父组件也可以直接在子组件的自定义标签上使用v-on来监听子组件触发的自定义事件
<div id="app">
<p>总数:{{total}}</p>
<my-component
@increase="handleGetTotal"
@reduce="handleGetTotal">
</my-component>
</div>
<script>
Vue.component('my-component',{
//template是展示到html页面上的
template:'\
<div>\
<button @click="handleIncrease">+1</button>\
<button @click="handleReduce">-1</button>\
</div>',
data:function(){
return {
counter:0
}
},
methods:{
handleIncrease:function(){
this.counter++;
//改变组件的counter后通过$emit()再把它传递给父组件
//第一个参数是自定义事件的名称,后面是要传递的数据
this.$emit('increase',this.counter);
},
handleReduce:function(){
this.counter--;
this.$emit('reduce',this.counter);
}
}
});
var app = new Vue({
el:'#app',
data:{
total:0
},
methods:{
handleGetTotal:function(total){
this.total = total;
}
}
})
</script>
非父子组件通信
使用一个空的Vue实例作为中央事件总线(bus)
<div id="app">
{{message}}
<component-a></component-a>
</div>
<script>
var bus = new Vue();
Vue.component('component-a',{
template:'<button @click="handleEvent">传递事件</button>',
methods:{
handleEvent:function(){
//点击按钮会通过 bus把事件on-message发出去,此时app就会接收到来自bus的事件
//进而在回调里完成自己的业务逻辑
bus.$emit('on-message','来自组件component-a的内容');
}
}
});
var app = new Vue({
el:'#app',
data:{
message:''
},
mounted:function(){
var _this = this;
//在app实例初始化时,监听来自bus实例的事件
bus.$on('on-message',function (msg){
_this.message = msg;
});
}
})
</script>
使用slot分发内容
当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到slot
这个过程叫作内容分发(transclusion)
props传递数据,events触发事件和slot内容分发构成了Vue组件的3个API来源,再复杂的组件也是由这3个部分构成的
编译的作用域
比如父组件中有如下模板
<child-component>
{{message}}
</child-component>
这里的message就是一个slot,但是它绑定的是父组件的数据,而不是组件<child-commponent>的数据
父组件模板的内容是在父组件作用域内编译
子组件模板的内容是在子组件作用域内编译
<div id = "app">
<child-component v-show="showChild"></child-component>
</div>
<script>
Vue.componetn('child-component',{
template:'<div>子组件</div>'
});
var app = new Vue({
el:"#app",
data:{
showChild:true
}
})
</script>
//以上的showChild绑定的是父组件的数据,因为访问的是外层app里的showChild
如果想在子组件上绑定,那应该是
<div id="app">
<child-component></child-component>
</div>
<script>
Vue.component('child-component',{
template:'<div v-show="showChild">子组件</div>',
data:function(){
return {
showChild:true
}
}
});
var app = new Vue({
el:'#app'
})
</script>
slot颁发内容,作用域是在父组件上的
slot用法
单个slot
在子组件内使用特殊的<slot>元素就可以为这个子组件开启一个slot(插槽),在父组件模板里,
插入在子组件标签内的所有内容将替代子组件的<slot>标签及它的内容
<div id="app">
<child-component>
<p>分发的内容</p>
<p>更多分发的内容</p>
</child-component>
</div>
<script>
Vue.component('child-component',{
template:'\
<div>\
<slot>\
<p>如果父组件没有插入内容(没有使用slot时),我将作为默认出现</p>\
</slot>\
</div>'
});
var app = new Vue({
el:'#app'
})
</script>
自定义指令
自定义指令的注册方法和组件很像,也分全局注册和局部注册
//全局注册 directive
Vue.directive('focus',{
//指令选项
bind:只调用一次,指令第一次绑定到元素时使用
inserted:被绑定元素插入父节点时调用
update:被绑定元素所在的模板更新时调用
componentUpdated:被绑定元素所在模板完成一次更新周期时调用
unbind:只调用一次,指令与元素解绑时调用
});
//局部注册 directives
var app = new Vue({
el:'#app',
directives:{
focus:{
//指令选项
}
}
})
Render函数
正常的DOM节点在HTML中是这样的:
<div id="main">
<p>文本内容</p>
<p>文本内容</p>
</div>
用Virtual Dom创建的JavaScript对象一般会是这样的
var vNode={
tag:'div',
attributes:{
id:'main'
},
children:{
//p节点
}
}
在Vue.js 2中,Virtual Dom就是通过一种VNode类表达的,每个DOM元素或组件都对应一个VNode对象
在Vue.js源码中是这样定义的
export interface VNode{
tag?:string;//当前节点的标签名
data?:VNodeData;//当前节点的数据对象
children?:VNode[];//子节点,数组,也是VNode类型
text?:string;//当前节点的文本,一般文本节点或注释节点会有该属性
elm?:Node;//当前虚拟节点对应的真实的DOM节点
ns?:string;//节点的namespace
context?:Vue;//编译作用域
key?:string|number;//节点的key属性,用于作为节点的标识,有利于patch优化
componentOptions?:VNodeComponentOptions;//创建组件实例时会用到的选项信息
componentInstance?:Vue;//
parent?:VNode;//组件的点位节点
raw?:boolean;//原始html
isStatic?:boolean;//静态节点的标识
isRootInsert:boolean;//是否作为根节点插入,被<transition>包裹的节点,该属性的值为false
isComment:boolean;//当前节点是否是注释节点
}
VNodeData代码如下
export interface VNodeData{
key?:string|number;
slot?:string;
scopedSlots?:{[key:string]:ScopedSlot};
ref?:string;
tag?:string;
staticClass?:string;
class?:any;
staticStyle?:{[key:string]:any};
style?:Object[]|Object;
props?:{[key:string]:any};
attrs?:{[key:String]:any};
domProps?:{[key:string]:any};
hook?:{[key:string]:Function};
on?:{[key:string]:Function|Function[]};
nativeOn?:{[key:string]:Function|function[]};
transition?:Object;
show?:boolean;
inlineTemplate?:{
render:Function;
staticRenderFns:Function[];
};
directives?:VNodeDirective[];
keepAlive:boolean;
}
VNode主要可以分为如下几类
TextVNode文本节点
ElementVNode普通元素节点
ComponentVNode组件节点
EmptyVNode没有内容的注释节点
CloneVNode克隆节点,可以是以上任意类型的节点,isCloned自发性为true