Vue特性
Vue只是聚焦视图层,是一个构建数据驱动的Web界面的库。
Vue通过简单 API提供高效的数据绑定和灵活的组件系统
轻量
数据绑定
指令
插件化
架构从传统后台MVC 向REST API + 前端MV*迁移
DOM是数据的一种自然映射
Vue核心:组件化
和数据驱动
组件化: 扩展HTML元素,封装可重用的代码
每个组件对应一个工程目录,组件所需要的各种资源在这个目录下就近维护。
Vue与其它框架的区别
对比标准:文件大小(性能)
,入门曲线(易用)
,社区繁荣
,吸取优点
与AngularJs区别
相同点:
支持指令 -- 内置指令和自定义指令
支持过滤器 -- 内置过滤器和自定义过滤器
支持双向绑定
都不支持低端浏览器(IE6/7/8)
不同点:
在性能上,ANgualrJS依赖对数据做脏检查,所以Watcher
越多越慢,Vue使用依赖追中的观察并且使用异步队列更新,所有的数据都是独立触发的。
与React的区别
相同点:
React采用特殊的
JSX
语法,Vue在组建开中也推崇编写.vue
特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用。中心思想相同:一切都是组件,组件实例之间可以嵌套。
都提供合理的钩子函数,可以去定制化的去处理需求。
都不内置类似AJAX,Router等功能到核心包,而是以其它方式(插件)加载。
在组建开发中,都支持mixins的特性。
不同点:
React依赖 Virtual DOM,而Vue使用的DOM模板。React采用的Virtual DOM会对渲染出来的结果做脏检查
Vue在模板中提供了指令,过滤器等。可以方便快捷的操作DOM。
脏检查:在angular中,没有办法对数据是否做了更改,所以设置触发条件,当触发这些条件,就执行一个检查来遍历所有的数据,对比更改的地方,然后执行变化,保留没有更改的地方。
效率不高,很多多余,称之为 脏检查。(过程:数据修改了,结果:保留就数据)
Vue稳定版本:1.0.24
数据绑定
数据绑定是将数据和视图想关联,当数据发生变化时,可以自动更新视图。
插值
mustache标签
文本插值:{{}}
有时候只需渲染一次数据,后续数据变化不再关心,使用:{{*}}
HTML片段:{{{}}}
注意:Vue指令和自身特性内是不可以插值。
表达式
mustache标签可以由JavaScript表达式和过滤器(本质上是一个JavaScript函数)构成。
表达式:各种数值,变量,运算符的综合体。
{{var a = 100;}}
// 错误。 是语句,并不是表达式{{if (true) return 'a'}}
// 条件控制语句是不支持,可以使用 三目运算符
指令
作用:当表达式的值发生变化时,将这个变化也反映到DOM上。
分隔符
-
delimiters
Vue.config.delimiters = ['<%', '%>'];
修改了默认的文本插值的分隔符,则文本插值的语法由{{example}}
变为<%example%>
-
unsafeDelimiters
Vue.config.unsafeDelimiters = ['<$', '$>'];
如果修改了默认的HTML插值的分隔符,则HTML插值的语法由
{{example}}
变为 <$emample$>
指令
指令的值限定为绑定表达式。
作用:当其表达式的值改变时把某些特殊的行为应用到DOM上。
内部指令
v-if
根据表达式的值在DOM中生成或移除一个元素。
v-show
根据表达式的值来显示或隐藏HTML元素。
在切换v-if
模块时,Vue有一个局部编译/卸载过程,因为v-if
中的模板可能包括数据绑定或子组件,v-if
是真实的条件渲染,因为它会包缺条件块在切换时合适地销毁与重建条件块内的时间监听器和子组件
v-if
是惰性的---如果初始渲染时条件为假,则什么也不做,在条件第一次变为真时才开始局部编译(编译会被缓存起来)
相比v-show
-- 元素始终被编译并保留,只是简单的基于CSS切换。
v-if
有更高的切换消耗,而v-show
有更高的初始渲染消耗。因此,如果需要频繁的切换,则使用v-show
较好,如果在运行时条件不大可能变化,则使用v-if
较好
v-else
必须跟着v-if
或v-show
后面,充当else
功能
v-model
用来在 input
, select
, text
, checkbox
, radio
等表单控件元素上创建双向数据绑定。根据控件类型,v-model自动选取正确的方法更新元素。
在v-model
指令可以添加参数number
,lazy
,debounce
<input type="text" v-model="msg" number />
<!-- 输入的自动转换为Number类型(如果原始的转换结果为NaN,则返回原始值) -->
<input type="text" v-model="msg" lazy />
<!-- 默认情况下,v-model 在 input 时间中同步输入框的值与数据, 可以添加一个lazy特性,从而将数据改到 change事件中发生 -->
v-for
基于源数据重复渲染元素,可以使用$index
来呈现相对应的数组索引
<ul id="demo">
<li v-for="item in items" class="item-{{$index}}">
{{item.childMsg}}
</li>
</ul>
Vue 1.0.17及以后支持 of分隔符
.
<div v-for="item of items"></div>
使用v-for
,将得到一个特殊的作用域,需要明确指定props属性传递数据,否则在组建内江获取不到数据。(隔离作用域)
<my-item v-for="item in items" :item="item" :index="$index">
<p>{{item.text}}</p>
</my-item>
Vue包装了被观察数据的变异方法,它们能触发视图更新。push()
,pop()
,shilt()
,unshift()
,splice()
,sort()
,reverse()
Vue重写了这些方法之后,触发了notify
Vue增加了两个方法来观测变化:$set
,$remove
。
$set : 通过索引设置数组元素并触发视图更新。
vm.animals.$set(0, {name: 'Aardvark'});
$set,$remove 底层都是调用splice()
方法。
应该尽量避免直接设置数据绑定的数组元素,因为这些变化不会被Vue检测到,因为也不会更新视图渲染,可以使用$set()
.
Vue不能检测到数组的变化
直接用索引设置元素. 例如:vm.items[0] = {};
修改数据的长度, 例如:vm.items.length = 0;
解决方法:
vm.items.$set(0, {});
第二个问题,用一个空数据替换items即可。
v-for
遍历一个对象,每一个重复的实例都将有一个特殊的属性$key
,或者给对象的简直提供一个别名。
<li v-for="itme in item">{{$key}} : {{item}}</li>
<li v-for="(key, value) in item">{{key}} : {{item.msg}}</li>
v-for
支持整数
<li v-for="itme in 10"></li>
ECMAScript无法检测到新属性添加到一个对象上或者在对象中删除。要处理这样的状况Vue增加三种方法:$add(key,value)
,$set(key, value)
,$delete(key)
这些方法可以用来添加和删除属性,同时可以触发视图的更新。
v-text
v-text
指令可以更新元素的textContent。在内部,{{ Mustache }}
插值也被编译为textNode的一个v-text
指令。
<span v-text="msg"></span>
<span>{{msg}}</span>
v-html
可以更新元素的InnerHTML。内容按普通 HTML插入 -- 数据绑定被忽略。
{{{Mustache}}}
插值也会被编译为锚节点上的一个v-html
指令
不建议直接动态渲染任意的HTML片段,很容易导致XSS
攻击.
<div v-html="html"></div>
<div>{{{html}}}</div>
v-bind
响应更新HTML特性,将一个或多个attribute,或一个组件prop动态绑定到表达式。
<img v-bind:src="imgSrc" />
<img :src="imgSrc" />
在绑定prop时,prop必须在子组件中声明。可以用修饰符指定不同的绑定类型。
修饰符为:
.sync
--- 双向绑定,只能用于prop绑定。.noce
--- 单次绑定,只能用于prop绑定.camel
--- 将绑定的特性名字转换驼峰命名(通常用于绑定用驼峰命名的SVG特性)
<my-component :prop="smoeThing"></my-component>
<my-component :prop.sync="smoeThing"></my-component>
v-on
用于绑定事件监听器,事件类型由参数指定。
在监听原生DOM事件时,如果只定义一个参数。 DOM event 为事件的唯一参数;如果在内联语句处理器中访问原生DOM事件,则可以用特殊变量$event把它传入方法中。
<!-- 方法 -->
<button v-on:click="methods"></button>
<!-- 内联语句 -->
<button v-on:click="methods(123, $event)"></button>
<!-- 缩写 -->
<button @click="methods"></button>
<!-- 停止冒泡 -->
<button @click.stop="methods"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="methods"></button>
<!-- 阻止默认行为,没有表达式 -->
<button @submit.prevent></button>
<!-- 串联修饰符 -->
<button @click.stop.prevent="methods"></button>
<!-- 修饰符,键别名 -->
<button @keyup.enter="onEnter"></button>
<!-- 键修饰符,键代码 -->
<button @keyup.13="onEnter"></button>
v-ref
在父组件上注册一个子组件的索引,便于直接访问。不需要表达式,必须提供参数id。可以通过父组件的$refs
对象访问子组件
v-el
为DOM元素注册一个索引,方便通过所属实例的$els访问这个元素。可以用v-el:smoe-el
设置this.$els.smoeEl
<div class="app">
<span v-el:msg>hello</span>
<span v-el:other-msg>Vue</span>
</div>
<script src="vue1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
new Vue({
el: '.app',
ready: function () {
console.log( this.$els.msg.textContent ); // hello
console.log( this.$els.otherMsg ); // <span>Vue<span>
}
});
</script>
v-pre
编译时跳过当前元素和它的子元素。可以用来显示原始Mustache标签。跳过大量没有指令的节点会加快编译。
v-cloak
v-cloak
这个指令保持在元素上知道关联实例结果编译。
解决闪烁问题
[v-cloak] {
dispnay: none;
}
<div v-cloak>
{{message}}
</div>
自定义指令
自定义指令提供一种机制将数据的变化映射为DOM行为。
钩子函数
Vue中的钩子函数都是可选的,相互之间没有制约关系
bind, 只调用一次,在指令第一次绑定到元素上时调用。
update, 在bind之后立即以初始值为参数第一次调用,之后每当绑定值变化时调用,参数为新值与旧值。
-
unbind,只调用一次,在指令从元素上绑定时调用。
Vue.directive('my-directive', { bind: function () { // 准备工作 // 例如,添加时间处理器或只需要运行一次的高耗任务 }, update: function ( newValue, oldValue ) { // 值更新时的工作 // 也会以初始值为参数调用一次 }, unbind: function () { // 清理工作 // 例如,删除bind() 添加的事件监听器 } });
使用指令:
<div v-my-direactive="someValue"></div>
只需要update函数是,可以传入一个函数替代定义对象
Vue.direactive('my-directive', function () {
// update();
});
指令实例属性
所有的钩子函数都将被复制都实际的指令对象中,在钩子内this指向这个指令对象。
el -- 指令绑定的元素
vm -- 拥有该指令的上下文ViewModel
expression -- 指令的表达式,不包括参数和过滤器。
arg -- 指令的参数
name -- 指令的名字,不包含前缀
modifires -- 一个对象,包括指令的修饰符
descriptor -- 一个对象,包含指令的解析结果
将这些属性视为只读,不要修改他们。
<div id="app" @click="up">
<div v-my-directive:hello.a.b="msg"></div>
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
Vue.directive('my-directive', {
bind: function() {
console.log('bound!');
},
update: function( value ) {
this.el.innerHTML =
'name -' + this.name + '<br />' +
'expression - ' + this.expression + '<br />' +
'argument - ' + this.arg + '<br />' +
'modifiers - ' + JSON.stringify(this.mondifiers) + '<br />' +
'value -' + value + '<br />' +
'vm-msg' + this.vm.msg;
}
});
//name -my-directive
//expression - msg
//argument - hello
//modifiers - undefined
//value -hello
//vm-msghello
new Vue({
el: '#app',
data: {
msg: 'hello'
},
methods: {
up: function() {
console.log('click');
}
}
});
</script>
指令高级选项
自定义指令提供一种机制将数据的变化映射为DOM行为
params
自定义指令可以接收一个params数组,指定一个特性列表,Vue编译器将自定提取绑定元素的这些特性。
<div id="app">
<my-direactvie class="hello" name="hi" a="aaaa"></my-direactvie>
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
Vue.elementDirective('my-direactvie', {
params: ['a'],
bind: function() {
console.log(this.params.a);
console.log(this.el.getAttribute('name'));
}
});
new Vue({
el: '#app'
});
</script>
支持动态(v-bind),this.params[key]会自动保持更新。可以指定一个回调,在值变化时调用。
<div id="app">
<my-direactvie class="hello" name="hi" v-bind:a="someValue"></my-direactvie>
<input type="text" v-model="someValue" name="" id="" value="" />
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
Vue.elementDirective('my-direactvie', {
params: ['a'],
paramWatchers: {
a: function() {
console.log('a changed!');
}
}
});
new Vue({
el: '#app',
data: {
someValue: ''
}
});
</script>
deep
如果自定义指令使用在一个对象上,当对象内部属性变化时要触发update,则在指令定义对象中指定 deep:true
<div id="app">
<div v-my-directive="a"></div>
<button @click="change">change</button>{{a.b.c}}
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
Vue.directive('my-directive', {
deep: true,
update: function( obj ) {
console.log( obj.b.c );
}
});
new Vue({
el: '#app',
data: {
a: {
b: {
c: 2
}
}
},
methods: {
change: function() {
this.a.b.c = 4;
}
}
});
</script>
twoWay
如果指令想VUe实例写回数据,则在指令定义对象中指定twoWay:true
作用:允许在指令中使用this.set(value)
<div id="app">
自定义组件: <input v-exp="a.b.c" /> <br />
父作用域:{{a.b.c}}
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
Vue.directive('exp', {
twoWay: true,
bind: function() {
this.handler = function() {
// 把数据写回 vm
// 如果指令这样绑定 v-exp="a.b.c"
// 这里将会绑定 `vm.a.b.c` 赋值
this.set(this.el.value);
}.bind(this);
this.el.addEventListener('input', this.handler)
},
update: function() {
this.el.removeEventListener('input', this.handler);
}
});
new Vue({
el: '#app',
data: {
a: {
b: {
c: 2
}
}
}
});
</script>
acceptStatement
传入acceptStatement:true
可以让自定义指令接受内联语句,就像v-on那样。
<div id="app">
<div v-my-directive="a++"></div>
{{a}}
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
Vue.directive('my-directive', {
acceptStatement: true,
update: function( fn ) {
// 传入一个是函数
// 调用它是将在所属实例作用域内计算"a++"语句
console.log( fn.toString() );
fn();
}
});
new Vue({
el: '#app',
data: {
a: 5
}
});
</script>
Terminal
Vue通过递归遍历DOM树来编译模块。但是遇到terminal
指令时会停止遍历这个元素的后代,这个指令将会接管编译这个元素及其后代的任务。 v-if
和 v-for
都是terminal
指令
priority
可以给指令指定一个优先级。如果没有指定优先级,普通指令默认是1000,terminal指令默认是2000.同一个元素上优先级高的指令会比其它指令处理得早已一些,优先级一样的指令按照它在元素特性列表中出现的顺序依次处理,但是不能保证这个顺序在不同浏览器中是一致的。
流程控制指令 v-if
和v-for
在编译过程中始终拥有最高的优先级。
指令可使用的配置项:
Vue.directive(id, {
params: [],
deep: true, // 使用对象,对象内部属性变化,触发update
twoWay: true, // 指令把数据写回Vue实例
acceptStatement: true, // 自定义指令接受内联语句 (类似`v-on`)
priority: 2222, // 优先级
bind: function() {},
update: function() {},
unbind: function() {}
});
Vue.directive(id, function() {
});
问题:
v-on可以绑定多个方法吗?
v-on
可以绑定多种类型的方法,可以是click,可以是focus事件,也可以是change事件
但是使用v-on
绑定了两个甚至多个click事件,那么v-on
只会绑定第一个click事件,其它会被自动忽略。
<input type="text" :value="name" @input="onInput" @focus="onFocus" @blur="onBlur" />
一个Vue实例可以绑定多个element元素吗?
el
为实例提供挂载元素,值可以是CSS选择符,或实际的HTML元素,或返回HTML元素的函数。这边,元素只用作挂载点。如果提供了模板,则元素被替换,除非replace为false.元素可以用vm.$el访问。
在Vue中如何让v-for循环出来的列表里面的click事件只对当前列表内元素有效?
从数据角度出发,定好数据结构,然后操作数据
通过$event对象,获取当前事件源,然后操作下面的元素.
<div class="app">
<ul>
<li @click="toggle(item)" v-for="item in items">
<span v-show="item.show">{{item.content}}</span>
</li>
</ul>
</div>
<script src="vue1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
new Vue({
el: '.app',
data: {
items: [
{
content: '1 item',
show: true
}, {
content: '2 item',
show: true
}, {
content: '3 item',
show: false
}
]
},
methods: {
toggle: function ( item ) {
item.show = !item.show;
}
}
});
</script>
计算属性
通常会在模板中绑定表达式,模板是用来描述视图结构的。如果模板中的表达式存在过多的逻辑,模板会变成臃肿不堪,维护变得非常困难,因此,为了简化逻辑,当某个属性值依赖于其它属性的值,可以使用计算属性。
什么是计算属性
计算属性就是当其依赖属性的值发生变化时,这个属性的值会自动更新,与之相关的DOM部分也会同步自动更新。
<div class="app">
<input type="text" v-model="didi" />
<input type="text" v-model="family" />
<br />
didi = {{didi}}, family = {{family}}, didiFamily = {{didiFamily}}
</div>
<script src="vue1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
new Vue({
el: '.app',
data: {
didi: 'didi',
family: 'family'
},
computed: {
didiFamily: {
get: function () {
return this.didi + this.family;
},
set: function ( val ) {
var names = val.split(' ');
this.didi = names[0];
this.family = names[1];
}
}
}
});
</script>
计算属性缓存
计算属性方法中执行大量的耗时操作,则可能会带来一些性能问题。
例如:在计算属性getter中循环一个大的数组以执行很多操作,那么当频繁调用该计算属性时,就会导致大量不必要的运算。
而在 Vue 0.12.8
版本中,在这方面进行了优化,即只有计算属性依赖的属性值发生了改变时才会重新执行getter
这样存在一个问题:就是只有Vue实例中被观察的数据发生了改变时才会重新执行getter。但是有时候计算属性依赖实时的非观察数据属性。
<div class="app">
<input type="text" v-model="welcome" name="" id="" />
{{welcome}}
<p>{{example}}</p>
</div>
<script src="vue1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
new Vue({
el: '.app',
data: {
welcome: 'welcome to join didi'
},
computed: {
example: function () {
return Date.now() + this.welcome;
}
}
});
</script>
在每次访问example时都取得最新的事件而不是缓存的事件。从Vue 0.12.11
版本开始,默认提供了缓存开关。 在计算属性对象中指定cache字段来控制是否开启缓存。
new Vue({
el: '.app',
data: {
welcome: 'welcome to join didi'
},
computed: {
example: {
cache: false, // 关闭缓存,默认为true
get: function () {
return Date.now() + this.welcome;
}
}
}
});
设置cache为false关闭缓存之后,每次直接访问vm.example 时都会重新执行getter方法。
问题:
计算属性getter不执行的场景
当计算属性依赖的数据属性发生改变时,计算属性的getter方法就会执行。在有些情况下,虽然依赖数据属性发生了改变,但计算属性的getter方法并不会执行。
当包含计算属性的节点被移出模板中其它地方没有再引用该属性时,那么对应的计算属性的getter不会执行。
<div class="app">
<button @click="toggleShow">Toggle Show Total Price</button>
<p v-if="showTotal">Total Price = {{totalPrices}}</p>
{{totalPrices}}
</div>
<script src="vue1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
new Vue({
el: '.app',
data: {
showTotal: true,
basePrice: 100
},
computed: {
totalPrices: function () {
return this.basePrice + 1;
}
},
methods: {
toggleShow: function () {
this.showTotal = !this.showTotal;
}
}
});
</script>
表单控件
基本使用
text
设置文本框v-model
为name
<input type="text" v-model="name" />
checkbox
一般的,使用多个复选框,被选中的值将会放入一个数组中。
<div id="app">
<input type="checkbox" id="flash" value="flash" v-model="bizLines" />
<label for="flash">快</label>
<input type="checkbox" id="premium" value="premium" v-model="bizLines" />
<label for="premium">专</label>
<p>Checked lines: {{bizLines | json}}</p>
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
bizLines: []
},
ready: function() {
console.log( this.bizLines );
}
});
</script>
radio
单选按钮被选择时,v-dmoel中的变量值会被赋值为对应的value值。
select
通过v-for
指令来冬天生成option
<div id="app">
<select v-model="bizLine">
<option v-for="option in options" :value="option.value">
{{option.value}} {{option.premium}}
</option>
</select>
<p>bizLine: {{bizLine}}</p>
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
bizLine: 'falsh',
options: [
{ text: '快', value: 'falsh' },
{ text: '专', value: 'premium' }
]
}
});
</script>
值绑定
checkbox
使用 :value
进行绑定
<input type="checkbox" id="falsh" :value="flash" v-model="bizLines" />
<label for="falsh"></label>
vm.bizLines === vm.flash
v-model
视图与Model之间同步数据
lazy
一般的,v-model
在input时间中同步输入框的值与数据,可以添加一个lazy特性。从而改到change事件中去同步。
<input v-model="msg" lazy /> <br />
{{msg}}
debounce
设置一个最小延迟,一般的在 AJAX 请求时,有效。
number
可以在v-model
所在的控件上使用number指令,该指令会在用户输入被同步到Model中时将其转化为数值类型,如果装换结果为NaN,则对应的Model值该是用户输入的原始值。
过滤器
过滤器,本质上都是函数,其作用在于用户输入数据后,它能够进行处理,并返回一个数据结果。
Vue支持在任何出现表达式的地方添加过滤器,除了{{}}
mustache风格的表达式之外,还可以在绑定指令的表达式后调用。
<span v-text="message | uppercase"></span>
过滤器可以接收参数,参数跟在过滤器后面,参数之间以空格分隔。
<p>{{msg | filterFunction 'arg1' arg2}}</p>
过滤器函数始终以表达式的值作为第一个参数,带引号的参数会被当作字符串处理,而不带引号的参数会被当作数据属性名来处理。
Linux shell 的管道符号,上一个命令的输出可以作为下一个命令的输入。
Vue过滤器支持链式调用,上一个过滤器的输出的结果可以作为下一个过滤器的输入。
<span>{{'ddfe' | capitalize | reverse}}</span>
<!--
-> 'ddfe' => 'Defe' => 'efeD'
capitalize 过滤器: 将输入字符串中的单词的首字母大写
reverse过滤器: 反转字符串顺序
-->
内置过滤器
字母操作
capitalize
,uppercase
,lowercase
三个过滤器用于处理英文字符。
capitalize
过滤器: 将表达式中的首字母转大写形式。uppercase
过滤器:所有字母转换为大写形式。lowercase
过滤器:所有字母转为小写形式。
json
json过滤器本质上时JSON.stringify(); 的精简缩略版。
作用:将表达式的值转换为JSON字符串。
限制limitBy
,filterBy
,orderBy
用于处理并返回过滤后的数组。 例如与v-for
搭配使用。
limitBy
limitBy
过滤器的作用时限制数组为开始的前N个元素,其中N由传入的第一个参数指定。第二个参数可选用于指定开始的偏移量。默认偏移量为 0. 如果第二个参数为3,则表示从数组下标第3个的地方开始计数。
<div v-for="item in tiems | limitBy 10"></div>
filterBy
第一个参数可以是字符串或者函数。
<div v-for="item in times | fitlerBy 'hello'"></div>
<!-- 过滤出含有hello 字符串的元素 -->
orderBy
返回排序后的数组
<ul>
<li v-for="user in users | orderBy 'lastName' 'firsetname' 'age'">{{user.lasetName}}--{{user.firsetName}}--{{user.age}}</li>
</ul>
自定义过滤器
fitler语法
Vue.filter(ID, function() {});
单参数
<div id="app">
<p v-text="message | reverse"></p>
</div>
<script type="text/javascript">
Vue.filter('reverse', function( val ) {
return val.split('').reverse().join('');
});
new Vue({
el: '#app',
data: {
message: 'abcdeq'
}
});
</script>
多参数
<div id="app">
<p v-text="message | reverse 'before' 'after'"></p>
</div>
<script type="text/javascript">
Vue.filter('reverse', function( val, begine, end ) {
return begine + '---' + val + '---' + end;
});
new Vue({
el: '#app',
data: {
message: 'abcdeq'
}
});
</script>
双向过滤器
Vue支持把视图(input元素)的值在写回模型前进行转化。
Vue.filter('MSG', {
// model -> view
// read 函数可选
read: function() {
console.log( 123 );
},
// view -> model
// write函数将在数据被写入Model之前调用
// 两个参数分别为表达式的新值和旧值
write: function( newVal, oldVal ) {
console.log( newVal, oldVal );
}
});
动态参数
如果过滤器参数没有用引号包起来,则它会在当前vm作用域内动态计算。过滤器函数的this始终指向调用它的vm
<div id="app">
<input type="text" v-model="userInp" />
<p>{{msg | concats userInp}}</p>
</div>
<script type="text/javascript">
Vue.filter('concats', function( val, inp ) {
if (inp) {
return val + inp;
}
return val;
});
new Vue({
el: '#app',
data: {
msg: 'a'
}
});
</script>
过滤器注意点:
需要给定过滤器一个唯一标识。如果用户自定义的过滤器和Vue内置的过滤器冲突,那么Vue内置的过滤器将会被覆盖。如果后注册的过滤器和之前的过滤器冲突,则之前注册的过滤器层被覆盖。
过滤器函数的作用时输入表达式的值,经过处理后输出。因此,定义的函数最好可以返回有意义的值。函数没有return语句不会报错,但这样的过滤器没有意义。
问题
filterBy/orderBy 过滤后$index 的索引
在使用 filterBy
或者 orderBy
对表达式进行过滤时,如果同时需要将$index 作为参数,此时的$index将会根据表达式数组或对象过滤后的值进行索引。
<div id="app">
<ul>
<li v-for="item in items | orderBy 'age'">
{{item.msg}} -- {{$index}}
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
items: [
{ msg: '顺', age: 1 },
{ msg: '出', age: 10 },
{ msg: '快', age: 6 }
]
}
});
</script>
自定义 filter 的书写位置
<script>
// 第一种写法
Vue.filter('reverse', function () {
});
// 第二种写法
new Vue({
el: '',
data: {},
fitlers: {
// 自定义 filter事件的位置
reverse: function () {
}
},
methods: {}
});
</script>
Vue实例方法
Vue实例提供一些有用的属性和方法,这些属性和方法名都已前缀$
开头
实例属性
组件树访问
$parent 访问当前组件实例的父实例
$root 访问当前组件书的根实例,当前组件没有父实例,$root 表示当前组件的实例本身。
$children 访问当前组件实例的直接子组件实例。
$refs 访问使用v-ref指令的子组件。
DOM访问
$el 访问挂载当前组件实例的DOM元素。
$els 访问$el元素中使用了
v-el
指令的DOM元素。
数据访问
$data 访问组件实例观察的数据对象,该对象引用组件实例化时选项中的data属性。
$options 用来访问组件实例化时的初始化选项对象。
当实例创建后原本不存在的属性,是无法绑定在视图上的。
可以使用Vue.set()
,vm.$set()
来解决.
实例方法
$appendTo
$appednTo();方法用来将el所指的DOM元素或片段插入到目标元素中。
参数:
elementOrSelector(字符串或DOM元素),该参数可以是一个选择器字符串或者DOM元素。
callback -- (可选,该回调函数会在el元素被插入到目标元素后背触发。(如果在el上应用了过渡效果,则回调会在过渡完成后被触发)
$before
用来将el所指的DOM元素或片段插入到目标元素之前
参数:
elementOrSelector
callback-- (可选)
$after
将el所指的DOM元素或片段插入到目标元素之后。
参数:
elementOrSelector
callback-- (可选)
$remove
将el所指的DOM元素或片段从DOM中删除
参数:
callback -- (可选)
$nextITick
在下次DOM更新循环后执行的回调函数,使用该方法可以保证DOM中的内容已经与最新数据保持同步。
参数:
callback -- (可选)该回调函数会在DOM更新循环后被执行。它和全局的Vue.nextTick(); 方法一样,不同的是,callback中的this会自动绑定到调用它的Vue实例上。
实例Event方法的使用
$on
监听实例上的自定义事件
$once
监听实例上的自定义事件,当之触发一次。
$emit
触发事件
参数:
event(字符串),该参数可以是一个事件名称
args (可选),传递给监听函数的参数
$dispatch()
派发事件,即先在当前实例触发,再沿着父链一层一层向上,如果对应的监听函数返回false就停止。
参数:
event(字符串),该参数可以是一个事件名称
args (可选),传递给监听函数的参数
$boradcast()
广播事件,即遍历当前实例的$children,如果对应的监听函数false就停止。
参数:
event(字符串),该参数可以是一个事件名称
args (可选),传递给监听函数的参数
$off()
删除事件监听器
参数:
event(字符串),该参数可以是一个事件名称
args (可选),对应的回调函数
如果没有参数,即删除所有的事件监听器,如果只提供一个参数--事件名称,即删除它对应的所有监听器。如果提供两个参数--事件名称和回调函数,即删除对应的这个回调函数。
组件
组件核心目标是:可重用性高,减少重复性的开发。
Vue的组件可以理解为预先定义好行为的ViewModel类。一个组件可以预定义选项。
组件核心选项:
模板(template) -- 声明了数据和最终展现给用户的DOM之间的映射关系
初始化数据(data) -- 一个组件的初始数据状态。对于可复用的组件来说,通常是私有的状态。
接收的外部参数(props) -- 组件之间通过参数来进行数据的传递和共享。参数默认是单向绑定(由上至下),但也可以显示声明为双向绑定。
方法(methods) -- 对数据的改动操作一般都在组件的方法内进行。可以通过
v-on
指令将用户输入事件和组件方法进行绑定生命周期钩子函数 -- 一个组件会触发多个生命周期钩子函数,比如:
created
,attached
,destoryed
等。在这些钩子函数中,可以封装一些自定义的逻辑,和传统的MVC想必,着可以理解为Controller的逻辑被分散到了这些钩子函数中。
基础
注册
全局注册
Vue.component('wind-component', WindComponet);
参数:
function, 可以是Vue.extend();创建的一个组件构造器,
Object ,Vue在背后自动调用Vue.extend();
组件的模板替换了自定义元素,自定义元素的作用只是作为一个挂载点,可以用实例replace决定是否替换自定义元素。
局部注册
不需要每个组件都全局注册,可以让组件只能用在其它组件内。可以使用 实例选项中componets
注册
<div class="app">
<wind-component></wind-component>
</div>
<script src="vue1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var Child = Vue.extend({
template: '<div>I am Child</div>'
});
var Prent = Vue.extend({
template: '<div>I am Parent</div> <Child></Child>',
components: {
child: Child
}
});
new Vue({
el: '.app',
components: {
'wind-component': Prent
}
});
</script>
为了让事件更简单,可以直接传入选项对象而不是构造器给Vue.component(); 和 components选项
数据传递
Vue组件三种数据传递方式:
props
组件通信
slot
props
'props'是组建数据的一个字段,期望从父组件传下来数据。因为组件的实例的作用域是孤立的,着意味着不能并且不应该在子组件的模板内直接引用父组件的数据,所以子组件需要显示的用props选项来获取父组件的数据。props选项可以是字面量,也可以是表达式,还可以绑定修饰符。
字面量语法
<div class="app">
<child msg="wind"></child>
</div>
<script src="vue1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
// 字面量
var Child = Vue.component('child', {
props: ['msg'],
template: '<div>{{msg}}</div>'
});
new Vue({
el: '.app',
components: {
child: Child
}
});
</script>
动态语法
可以利用v-bind
将动态props绑定到父组件的数据。每当父组件的数据变化时,该变化也会传到给子组件。
动态语法:在父级组件链接 :wind="msg"
。只能在Vue.extend({}).
<div class="app">
<child msg="wind"></child>
</div>
<script src="vue1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
// 动态语法 // v-bind
var Child = Vue.extend({
props: ['wind'],
template: '<div>{{wind}}</div>',
// replace: true
});
var Parent = Vue.extend({
template: '<p>parent</p><br /><child :wind="msg"></child>',
data: function () {
return {
'msg': 'msgConent'
};
},
components: {
'child': Child
}
});
new Vue({
el: '.app',
components: {
child: Parent
}
});
</script>
绑定修饰符
props默认是单向绑定 -- 当父组件的属性变化时,将传导给子组件,但是反过来不会。着是为了防止子组件无意修改父组件的状态。
.sync
,双向绑定.once
, 单次绑定
双向绑定会把子组件的msg属性同步会父组件的parentMsg属性
<!-- 默认为单项绑定 -->
<child :msg="parentMsg"></child>
<!-- 双向绑定 -->
<child :msg.sync="parentMsg"></child>
<!-- 单词绑定 -->
<child :msg.once="parentMsg"></child>
双向绑定会把子组件的msg属性同步到父组件的parentMsg属性,单次绑定在建立之后不会同步之后的变化。如果props是一个对象或数组,那么它是按引用传递的。在子组件内修改会影响父组件的状态,而不管是用哪种类型绑定。
组件通信
子组件可以用this.$parent
访问它的父组件,父组件有一个数组this.$children
,暴行它所有的子元素,根实例的后代可以用this.$root
访问根实例,不过子组件应当避免直接依赖父组件的数据,尽量显式的使用 props传递数据。
在子组件中修改父组件的状态缺点:
父组件与子组件紧密地耦合
只看父组件,很难理解父组件的状态,因为它可能被任意子组件修改。在理解情况下,只有组件自己能修改其状态。
因为作用域是有层次的,所以可以在作用域链上传递时间。
一般的,选择事件传递方式,判断规则: 查看要触发事件的作用域。如果要通知整个事件系统,就要向下广播。
每一个Vue实例都是一个事件触发器:
$on() -- 监听事件
$emit() -- 把事件沿着作用域向上派送
$dispatch() -- 派发事件,事件沿着父链冒泡。 调用
$broadcast() -- 广播事件,事件向下传导给所有的后代
<template id="child-template">
<input v-model="msg" />
<button @click="notify">Dispatch Event</button>
</template>
<div id="app">
<p>Messages: {{messages | json}}</p>
<child></child>
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
// 注册子组件
// 将当前消息派发出去
Vue.component('child', {
template: '#child-template',
data: function() {
return {
msg: 'hello'
}
},
methods: {
notify: function () {
if ( this.msg.trim() ) {
this.$dispatch('child-msg', this.msg);
this.msg = '';
}
}
}
});
// 初始化父组件
// 收到消息时将事件推入一个数组中
var parent = new Vue({
el: '#app',
data: {
messages: []
},
// 在创建实例时 `events` 选项简单的调用`$on`
events: {
'child-msg': function ( msg ) {
// 事件回到内的`this` 自动绑定到组册它的实例上
this.messages.push(msg);
}
}
});
</script>
从父组件的代码中不能直接观看到child-msg
事件来自哪里。如果在模板中子组件用到的地方声明事件处理器。 可以使用v-on
来监听。
<template id="child-template">
<input v-model="msg" />
<button @click="notify">Dispatch Event</button>
</template>
<div id="app">
<p>Message {{messages | json}}</p>
<child @child-msg="shandleIt"></child>
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
// 组册子组件
Vue.component('child', {
template: '#child-template',
data: function () {
return { msg: 'hello' }
},
methods: {
notify: function () {
if ( this.msg.trim() ) {
this.$dispatch('child-msg', this.msg)
this.msg = '';
}
}
}
});
// 初始化父组件
new Vue({
el: '#app',
data: {
messages: []
},
methods: {
'shandleIt': function () {
alert(123);
}
},
events: {
'child-msg': function ( msg ) {
if ( msg ) {
this.messages.push(msg);
}
}
}
});
</script>
当子组件触发了child-msg
事件时,父组件的 hadleIt
方法将被调用。所有影响父组件状态的代码都放到父组件的hadleIt
方法中。 子组件只关注触发事件。
尽管有props
和events
,但是有时候仍需要在JavaScript中直接访问子组件。因此,需要使用v-ref
为子组件指定一个索引ID。
v-ref
直接访问子组件
<div id="app">
<comp v-ref:aa></comp>
<comp v-ref:bb></comp>
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script>
Vue.component('comp', {
template: '<div>嘻嘻哈哈</div>',
});
new Vue({
el: '#app',
data: {
msg: []
},
ready: function () {
console.log( this.$refs.aa );
console.log( this.$refs.bb );
}
});
</script>
slot分发内容
场景:
使用组件时,常常需要组合使用:
<pink>
<pink-header></pink-header>
<pink-footer></pink-footer>
</pink>
注意:
<pink>组件不知道他的挂载点会有什么内容,挂载点的内容是由<pink>的父组件决定的。
<pink>组件很可能有它自己的模板。
为了让组件可以组合,需要一种方式来混合父组件的内容与子组件自己的模板。称之为:内容分发
Vue使用特殊的<slot>元素左右原始内容的插槽
定义在父组件中,父组件中嵌套的其它内容不会被替换。
编译作用域
分发内容是在各自作用域中被编译。
父组件模板的内容在父组件作用域内编译,子组件模板的内容在子组件作用域内编译。
单个 slot
父组件的内容被抛弃,除非子组件模板包含<slot>.如果子组件模板只有一个没有特性的slot,父组件的整个内容将查到slot所在的地方并替换它。
<slot>标签的内容视为回退内容。回退内容在子组件的作用域内编译,当宿主元素为空并且没有内容供插入时显示这个回退内容。
<template id="pink">
<div>
<h1>This is my component!</h1>
<slot>
如果没有分发内容则显示我
</slot>
</div>
</template>
<div id="app">
<pink-component>
<p>This is some original conent</p>
<p>This is some more original conent</p>
</pink-component>
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
Vue.component('pink-component', {
template: '#pink'
});
new Vue({
el: '#app',
});
</script>
具名slot
<slot>
元素可以用一个特殊特性name
配置如何分发内容。多个slot
可以有不同的名字。具名slot
将皮撇内容片段中有对应slot
特性的元素。
·具名slot·仍然可以有一个匿名slot
.作为找不到匹配的内容片段的回退插槽,它是默认slot。如果没有默认slot,这些找不到匹配的内容片段将被抛弃。
<template id="pink">
<div>
<slot name="a"></slot>
<slot></slot>
<slot name="b"></slot>
</div>
</template>
<div id="app">
<pink-multi>
<p slot="a">ONE</p>
<p slot="b">TWO</p>
<p>defalut A</p>
</pink-multi>
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
Vue.component('pink-multi', {
tempate: '#pink'
});
new Vue({
el: '#app'
});
</script>
混合
以一种灵活的方式为组建提供分布复用的功能。混合对象可以包含任意的组件选项。
当组件使用了混合对象时,混合对象的所有选项将别“混入”组件自己的选项中。
<div id="app">
<mixin-com></mixin-com>
</div>
<script type="text/javascript">
Vue.config.debug = true;
// mixin对象
var myMixin = {
created: function () {
this.hello();
},
methods: {
hello: function (){
console.log( 'hello from mixin!' );
}
}
}
// 定义组件,使用 mixin对象
var mixinCom = Vue.extend({
mixins: [myMixin],
template: '<h1>HELLO ~ </h1>'
});
// 创建根实例
new Vue({
el: '#app',
components: {
'mixin-com': mixinCom
}
});
</script>
HTML中是不区分大小写,而JavaScript是区分大小写。在给组件取属性名时,要注意大小写问题。尽量使用小写加中横线
当混合对象和组件包含同名选项时,这些选项将以适当的策略合并。
例如:同名钩子函数被并入一个数组中,因而都会被调用。另外,混合的钩子函数将在组件自己的钩子之前调用。
<div id="app">
<my-componet></my-componet>
</div>
<script src="//cdn.bootcss.com/vue/1.0.2/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var myMixin = {
created: function () {
this.hello();
},
methods: {
hello: function () {
console.log('hello from mixin~');
}
}
}
// 定义组件,使用混合对象
var com = Vue.extend({
mixins: [myMixin],
template: '<h1>HELLO ~</h1>',
created: function () {
console.log('component hook called');
}
});
// 创建根实例
new Vue({
el: '#app',
components: {
'my-componet': com
}
});
</script>
混合全局注册,一旦全局注册混合,它就会影响所有之后创建的Vue实例。
慎用全局混合,因为它会影响到每个所创建的Vue实例,包括第三方组件。大多数情况下,它应当只用于自定义选项。
动态组件
多个组件可以使用同一个挂载点,然后动态的在他们之间切换。使用保留的<component>
元素,懂她id绑定到它的is
特性上。
<component :is="show"></component>
<script>
new Vue({
data: {
show: true
}
});
</script>
keep-alive
作用:切换组件时,保留组件状态。减少内存开销。
<component :is="currentView" keep-alive></component>
activate钩子
作用: 控制切换组件的切换时间。切入组件添加activate
钩子函数
Vue.component('actiate-exp', {
activate: function ( done ) {
var self = this;
loadDataAsync(function ( data ) {
self.smoeData = data;
done();
});
}
});
activate 钩子只作用于动态组件切换或静态组件初始化渲染的过程中,不作用于使用实例方法手工插入的过程中。
transition-mode
transition-mode
特性用于指定连个动态组件之间如何过渡。
在默认情况下,进入与离开平滑的过渡。
in-out --- 新组建先过度进去,等它的过去完成之后当前组件过渡出去。
out-in 当前组件先过渡出去,等它过渡完成后新组件过渡进入
<!-- 先淡出再淡入 -->
<component :is="view" transition="fade" transiion-mode="out-in"></component>
<style>
.fade-transition {
transtion: opacity .3s ease;
}
.fade-enter, .fade-leave {
opacity: 0;
}
</style>
扩展
组件和 v-for
自定义组件可以像普通元素一样直接使用v-for
。
因为组件的作用域是孤立的,无法将数据传递那个到组件内部。
<pink v-for="item in items" :item="item" :index="$index"></pink>
<!-- 显式声明数据来自哪里可以让组件复用再其它地方 -->
<div id="app">
<pink v-for="item in msg" :wind="item" :item="item" :index="$index"></pink>
</div>
<script type="text/javascript">
Vue.component('pink', {
props: ['wind'],
template: '<div>pink -- {{wind}}</div>'
});
new Vue({
el: '#app',
data: {
msg: ['pink', 'tan', 'red', 'yellow']
}
});
</script>
编写可复用组件
在编写组件时,时刻考虑组件是否可复用是否有好处的。
一次性组件跟其它组件紧密耦合没关系,但是可复用组件一定要定义侵袭的公开接口。
Vue组件 API来自三部分 -- prop
, Evnet
, slot
prop允许外部环境传递数据组给组件
事件允许组件发出发布环境的action
slot运行外部环境将内部插入到组件的视图结构内。
使用v-bind
和 v-on
的简写语法,模板的缩进清楚并简洁
异步组件需要将应用拆分为小块,每块按实现按需加载。Vue允许将组建定义为一个工厂函数,动态的解析组件的定义。Vue只在组件需要渲染时触发工厂函数,并把结果缓存起来。
Vue.component('async-exp', function ( resoluve, reject ) {
setTimeout(function () {
reslove({
tempate: '<div>ASYNC!</div>'
});
}, 1000);
});
工厂函数接受一个resolve回调,在收到从服务器下载的组件定义时调用。
片段实例
在使用template选项时,模板的内容将替换实例的挂载元素,因而推荐模板的顶级元素始终是单个元素。
下面的情况会让实例变成一个片段实例
模板包含多个顶级元素
模板只包含普通文本
模板包含其他组件(其它组件可能是一个片段实例)
模板只包含一个元素指令,如<partial> 或 vue-router的<router-view>
模板根节点有一个流程控制指令,如 v-if 或 v-for
让实例有未知数据的顶级元素,它将把其DOM内容当作片段。片段实例仍然会正确的渲染内容。不过没有一个根节点,它的$el指向一个锚节点,即一个空的文本节点(在开发模式下是一个注释节点)
组件元素上的非流程控制指令,非prop特性和过度将被忽略。
<!-- 不可以,因为没有根元素 -->
<examplte v-show="ok" transition="fade"></examplte>
<!-- props 可以 -->
<examplte :prop="someData"></examplte>
<!-- 流程控制可以,但是不能有过渡 -->
<examplte v-if="ok"></examplte>
生命周期
在vue中,在实例化Vue之前,他们以HTML的文本形式保存在文本编辑器中。当实例化后将经历创建
,编译
,销毁
主要三个阶段
生命周期钩子:
init
在实例化开始初始化时同步调用。此时数据观测,事件和Watcher 都尚未初始化created
在实例创建后同步调用。此时实例已经结束解析选项,意味着已建立:数据绑定,计算属性,方法,Watcher/事件回调。但是还没有开始DOM编译,$el还不存在。beforeCompile
在编译开始前调用。compiled
在编译结束后调用。此时所有的指令已生效,因而数据的变化将触发DOM更新。但是不是担保$el已插入文档。-
ready
在编译结束和$el第一次插入文档之后调用,入在第一次attatced钩子之后调用。注意必须是有Vue插入(如vm.$appendTo()等方法或更新)才出发ready钩子的。// { 模板插入到文档中了;相当于window.onload }
attached
vm.$el
操作如DOM时调用。必须是→指令
或实例方法
(如$appednTo()) 插入,直接操作vm.$el
不会触发这个钩子detached
在vm.$el
从DOM中删除时调用。必须是由指令
或实例方法删除
,直接操作vm.$el
不会触发这个钩子beforeDestroy
在开始销毁实例时开始调用。此时实例仍然有功能。destroyed
在实例被销毁之后调用。此时所有的绑定和实例的指令已经解绑,所有的子实例也已经被效果。如果有离开过渡,dsetroyed钩子在过渡完成之后调用。
开发组件
组件格式,把一个组件的模板,样式,逻辑三要素整合在同一个文件中,即方便开发,也方便复用和维护。Vue本身支持对组件的异步加载,配合webpack的分块打包功能,可以实现组件的异步按需加载。
基于第三方组件开发
<scirpt>
import Chart from 'chart.js'
</scirpt>
问题
camelCase & kebab-case
HTML标签中的属性名不区分大小写。设置prop名字为camelCase形式的时候,需要装欢为keba-case形式在HTML中使用。
<!-- HTML中必须是短横线分割 -->
<child my-message="hello"></child>
<script type="text/javascript">
Vue.componet('child', {
props: ['myMessage'],
template: '<span>{{myMessage}</span>'
});
</script>
字面量语法&动态语法
错误用法:
使用字面量语法传递数值。
<!-- 传递字符串 “1” -->
<comp some-prop="1"></comp>
因为它是一个字面量prop,它的值是字符串“1”,而不是一实际的数字传下去。如果需要传递真实的JavaScript类型的数字,则需要使用动态语法。从而让它的值被当作JavaScript表达式计算。
<comp :some-prop="1"></comp>
组件选项问题
传入Vue构造器的多数选项也可以用Vue.extend();不过有两个特列:data
和el
.
场景:简单的吧一个对象作为data选项传给Vue.extend();
var data = { a: 1 };
var MyComponent = Vue.extend({data: data});
存在的问题:MyComponent所有的实例哦给你共享同一个data对象。
解决方式:使用一个函数作为data选项,让这个函数返回一个新对象。
var MyComponent = Vue.extend({
data: function () {
return { a: 1 }
}
});
模板解析
Vue的模板是DOM模板,使用浏览器元素的解析器而不是自己实现一个。
DOM模板缺点:必须是有效的HTML片段。
一些HTML元素对什么元素都可以放在它里面有限制。
a 不能包含其他交互元素(如,按钮、连接)
ul和ol只能包含li
select 只能包含option和optgroup
table只能直接包含thead,tbody,tfoot,tr,caption,col,colgroup
tr只能直接包含th和td
自定义标签(包括自定义元素和特殊标签,如<component>.<template>,<partal>) 不能用在ul,select,table等对内部元素有限制的标签内。放在这些元素内部的自定义标签将被提到呀U尿素外面,因而渲染不正确。
自定义元素应当使用is特性。
<table>
<tr :is="my-component"></tr>
</table>
如何解决数据层级结构太深的问题
使用vm.$set()
手动定义一层数据
vm.$set('depAirprotZh', ticketInfo.flight.fromSegments[ticketInfo.flight.fromSegments.length - 1].depAirprotZh);
$set
用法:
参数:
{String} keyPath
{*} value
设置Vue市里的属性值。在多数情况下应当使用普通对象语法。如vm.a.b=123
.
这个方法只能适用:
使用keyPath动态的设置属性
设置不存在的属性。
如果keyPath不存在,将递归的创建并建立追踪。如果用它创建顶级属性,实例将被强制进入"digset循环",在此过程中重新计算所有Watcher。
var vm = new Vue({
data: {
a: {
b: 1
}
}
});
// keypath 存在
vm.$set('a.b', 2);
vm.a.b // -> 2
// keypath 不存在
vm.$set('c', 3);
vm.c // -> 3
后端数据交互
配合vue-router
new Vue({
el: '#app',
data: {
todos: []
},
created: function () {
this.$http
.get()
.hten(function ( data ) {
this.todos = data;
})
}
});
配合Jquery的AJAX
new Vue({
el: '#app',
data: {
todos: []
},
created: function () {
$.get('')
.done(function ( data ) {
this.todo = data;
});
}
});
data中没有计定义计算属性,它是如何被使用的
没有把计算数据放到$data里面去,而是通过Object.definePrototype(this, key,def) 直接定义到了实例上。
ES6
ES6中的模块
,let 和 const
模块
export
在ES6中,一个文件就是一个模块,一个模块内部的所有变量,对于外部来说是无法获取的,触发是哟个关键词exprot对外暴露接口,暴露的各个接口通过名字来进行区分。
const sqrt = Math.sqrt;
function square ( x ) {
return x * x;
}
function diag ( x, y ) {
return sqrt(square(x) + square(y));
}
// 通过export 暴露接口。使用大括号指定要暴露的接口
export {sqrt, square, diag}
import
通过import
命令加载这个模块(文件).
/**
* 注意大括号中的接口名必须在lib.js模块中暴露。
*/
import { square, diag } from './lib';
import
可以通过as
语法取别名
improt {myVar1 as myCustomVar1} from './lib';
import
会指定加载模块,因此有空的import
语法
// 值加载执行模块,不引用任何接口
import './lib'
import
可以整体加载模块,达到命名空间的效果。
// lib.js
export var myVar1 = ...;
export let myVar2 = ...;
exprot const MY_COMST = ...;
export function myFunc () {
...
}
exprot function* myGeneratorFunc () {
...
}
export class MyClass {
...
}
// mian.js
import * as lib from './lib';
console.log(lib.myVar1);
console.log(lib.myVar2);
new lib.MyCalss();
export default
场景:即使用模块接口的人必须知道该模块export了哪些接口,有时候一个模块实际上只对外暴露一个接口,这个时候没有必要限定暴露的接口名字。
可以使用export default
语法让模块调用者自定义要导入的接口名字。
// myFunc.js
export defalut function () {
}
/**
* 注意:myFunc 不能包含在`{}`里。myFunc可以替换任意变量名。
*/
// main.js
import MyFunc from 'myFunc';
MyFunc();
export/import在Vue.js中的使用
Vue采用export/import
进行模块化开发,文件通过exprot暴露接口,通过improt引用其它文件内容。
模块和组件的区别
Module:
An implementation unit of software that provides a coherent set of responsibilities.
Component:
A component is a reusable building block that can be combined with other components in the same or other computers in a distributed network to form an application.
vue-cli
基础工具:目录结构
,本地调试
,代码部署
,热加载
,单元测试
安装: npm install vue-cli
vue-cli运行之后目录结构
目录或文件 | 说明 |
---|---|
build | webpack配置相关 |
config | webpack配置相关 |
node_modules | npm install 安装依赖代码库 |
src | 存放源码 |
-- main.js | 入口组件 |
static | 第三方静态资源 |
-- .gitkeep | 文件目录为空也可以提交到代码仓库 |
.babelrc | babel 配置文件 |
.editorconfig | 编辑器配置 |
.eslintignore | 忽略语法检查的目录设置 |
.eslintrc.js | eslint的配置文件 |
.gitignore | git 忽略文件或目录提交 |
index.html | 入口文件 |
package.json | 项目的配置文件 |
.babelrc
{
"presets": ["es2015", "stage-2"], # 预设插件 (babel转换预先需要安装的插件) // stage-2 四级 (1,2,3,4)包括了 es2015中没有的插件
"plugins": ["transform-runtime"], # 插件
"comments": false # false 表示转换成代码不生成注释
}
.editorconfig
root = true
[*]
charset = utf-8 # 编码
indent_style = space # 缩进风格 (基于空格作为缩进风格)
indent_size = 2 # 缩进大小
end_of_line = lf # 换行符风格, lf是linux换行符风格
insert_final_newline = true # 创建文件,会自动在末尾插入新行
trim_trailing_whitespace = true # true,自动行尾多余风格
.eslintignore
build/*.js # build 文件夹底下的所有文件
config/*.js # config 文件夹底下的所有文件
.eslintrc.js
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style // 语法规则地址
extends: 'standard', // standard 表示继承一个标准的规则,在vue-cli创建过程中会选择.
// required to lint *.vue files
plugins: [
'html'
],
// add your custom rules here
'rules': { // 配置部分自定义规则
// allow paren-less arrow functions
'arrow-parens': 0, // 箭头函数前面允许不写括号 (值设置为0,表示忽略检查)
// allow async-await
'generator-star-spacing': 0, // async-await 使用
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 // 不允许在代码中有这些选项,生产环境不允许有 debugger
}
}
package.js
"dependencies": { # 项目生产下的依赖
},
"devDependencies": { # 编译过程中的依赖
}
webpack打包
weback.base.conf.js
webapck基本配置
var path = require('path')
var config = require('../config')
var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../') // 项目根目录
module.exports = {
entry: {
app: './src/main.js' // 入口文件
},
output: {
path: config.build.assetsRoot, // 打包的根目录名字
publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, // 根目录
filename: '[name].js' // 对应 entry 的key名字
},
resolve: { // 设置在 require() 或者import 模块一些相关配置
extensions: ['', '.js', '.vue'], // 自动补全文件后缀
fallback: [path.join(__dirname, '../node_modules')], // require() 找不到模块,会充 node_modules模块中寻找
alias: { // 别名
'src': path.resolve(__dirname, '../src'), // require 的时候 使用的别名
'assets': path.resolve(__dirname, '../src/assets'),
'components': path.resolve(__dirname, '../src/components')
}
},
resolveLoader: { // require() 找不到模块,会充 node_modules模块中寻找
fallback: [path.join(__dirname, '../node_modules')]
},
module: {
preLoaders: [// preLoaders 会在loader之前对文件进行处理 // 对某种类型的文件 应用 某个loader进行处理
{
test: /\.vue$/,
loader: 'eslint',
include: projectRoot, // 检查的文件。 只对该文件下的文件进行检查
exclude: /node_modules/ // 排除这些目录,进行该loader处理
},
{
test: /\.js$/,
loader: 'eslint',
include: projectRoot,
exclude: /node_modules/
}
],
loaders: [ // 对某种类型的文件 应用 某个loader进行处理
{
test: /\.vue$/,
loader: 'vue'
},
{
test: /\.js$/,
loader: 'babel',
include: projectRoot,
exclude: /node_modules/
},
{
test: /\.json$/,
loader: 'json'
},
{
test: /\.html$/,
loader: 'vue-html'
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url',
query: {
limit: 10000, // 文件大小小于 10000kb 的时候生产 base64的文件
name: utils.assetsPath('img/[name].[hash:7].[ext]') // 文件名的规则 生产
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url',
query: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
eslint: {
formatter: require('eslint-friendly-formatter') // 检查错误友好的提示错误信息 并提供 es6语法的官网链接
},
vue: {
loaders: utils.cssLoaders() // .vue文件中CSS处理的loader
}
}
webpack.dev.conf.js
dev环境下的webpack配置
var config = require('../config')
var webpack = require('webpack')
var merge = require('webpack-merge')
var utils = require('./utils')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) { // hot relaod 相关代码 // 改变源码在浏览器不刷新的情况下,能够看到更新后的视图.
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
module.exports = merge(baseWebpackConfig, {
module: {
// 独立对 CSS预处理文件进行编译
loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
},
// eval-source-map is faster for development
devtool: '#eval-source-map',
plugins: [
new webpack.DefinePlugin({ // 把源码中的 `process.env` 替换成 config.dev.env
'process.env': config.dev.env
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.optimize.OccurenceOrderPlugin(), // webpack 优化插件. 对插件使用的频率
new webpack.HotModuleReplacementPlugin(), // hot realod
new webpack.NoErrorsPlugin(), // 编译错误,会跳过那段代码
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({ // 通过
filename: 'index.html', // 编译生成的文件名
template: 'index.html', // 处理的模板
inject: true // 表示打包的时候,路径自动添加到index.html中. css 的路径会自动添加到 head头部, js会默认添加到body中
})
]
})
dev-server.js
dev运行文件
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var config = require('../config')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = process.env.NODE_ENV === 'testing'
? require('./webpack.prod.conf')
: require('./webpack.dev.conf')
// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port // 端口号
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable // 代理的接口
var app = express()
var compiler = webpack(webpackConfig) // 编译webapck配置
var devMiddleware = require('webpack-dev-middleware')(compiler, { // webpack专门为express开发的中间件.
publicPath: webpackConfig.output.publicPath, // 静态资源访问目录
stats: {
colors: true,
chunks: false
}
})
var hotMiddleware = require('webpack-hot-middleware')(compiler) // 访问的app文件,并没有生产到项目目录中,而是在内存中,该中间件做了处理,放入内存中.
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
// proxy api requests
Object.keys(proxyTable).forEach(function (context) { // 处理代理接口
var options = proxyTable[context]
if (typeof options === 'string') {
options = { target: options }
}
app.use(proxyMiddleware(context, options))
})
// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())
// serve webpack bundle output
app.use(devMiddleware) // 使用自定义中间件
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware) // 使用自定义中间件
// serve pure static assets
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) // 处理静态资源目录
app.use(staticPath, express.static('./static')) // 静态资源目录
module.exports = app.listen(port, function (err) { // 监听
if (err) {
console.log(err)
return
}
console.log('Listening at http://localhost:' + port + '\n')
})
项目目录
`src`所有源码目录
-`main.js`入口文件
-`App.vue` 整个页面是实例文件
-`components` 组件目录
--`header` 子组件目录
---`header.vue` 具体的组件
--`common` 公共资源
---`js` 基础库
---`stylus` css预处理器
---`fonts` 字体库
数据传递
props
1. 变量名 = data --> 自定义组件的自定义属性
2. props 在child, 变量名在child
父级到子级:
父级:
$broadcast
子级:
evnets: {
events: function () {
}
}
子级到父级:
父级:
evnets: {
events: function () {}
}
子级:
$dispatch
配置选项
new Vue(options)
el
data
methods
components
computed
wtach
replace
init
created
beforeCompile
compiled
ready
attached
detached
beforeDestroy
destoryed
events
mixins: [mixin]
var vm = new Vue({
el: '选择器', // 挂载到页面的那个元素里,即确定vue的作用范围,外部可以通过 vm.$el 访问,得到的是一个原生dom元素,可进行对应操作
a: '', // 自定义属性, 外部课通过vm.$options 访问
data: {}, // 实例属性,外部通过实例名, 即vm.$data调用
computed: {}, // 计算属性, 也是实例属性, 只是以方法的形式存在,并可以有逻辑运算的属性
method: {}, // 实例方法
wtach: {}, // 对data和computed的属性进行监听,当属性有变化时,自动触发,以方法的形式存在 外部通过$.watch调用
// 以上属性和方法,实例内部都通过 this调用,外部则通过对应的实例方法访问.
// 在vue的生命周期过程中,提供了一系列的钩子函数,进行自定义逻辑注入
created: function () { // 实例已经创建 },
beforeCompile: function () { // 模块编译之前 },
compiled: function () { // 模块编译之后,即模板占位符被是内容替换 },
ready: function () { // 模板插入到文档中, 相当于window.onload },
// 上面4个方法,在对象被实例化后即按顺序执行
beforeDestroy: function () { // 对象销毁之前 },
destroyed: function () { // 对象销毁之后 }
// 上面2个方法需通过事件主动触发, vm.$destory(); 才执行
});
Vue.extend(options)
template
data
props
components
name
mixins: [mixin]
methods
Vue.component(id, options)
props (array | object)
templalte
methods
data
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。