基本操作
1)Vue用于实现虚拟dom的render函数。vue1和vue2最大的区别是vue2使用了虚拟DOM来更新dom节点,提升渲染性能。
第一个参数createElement
第二个参数context,来提供临时上下文,比如this.level要改写成context.props.level。
render函数不需要vue的内置指令,也没办法使用他们,无论什么功能都可以运用原生js写。
适应场景
Render函数一般用于比如在很多文章类的网站中都有区分一级标签,二级标签...为了方便分享url,他们都做成锚点,点击一下会将内容加在网址后面,以“#”分割。如果把它分装成一个组件,不用render代码会很长。一般这个使用使用render函数。
createElement构建虚拟dom模板
createElement(
第一个参数必选可以是html标签,也可以是组件或者函数
第二个参数可选数据对象,如class,style,attrs,props,domProps,on
)
<div id="app">
<my-component :level='2' title="特性">特性</my-component>
</div>
<script src="vue.min.js"></script>
<script>
Vue.component('my-component',{
props:{
level:{
type:Number,
required:true
},
title:{
type:String,
default:''
}
},
render:function(createElement){
return createElement('h'+this.level,
[
createElement('a',{
domProps:{
href:'#'+this.title
}
},
this.$slots.default
)
])
}
});
var app=new Vue({
el:"#app"
})
</script>
2)函数化组件:vue.js提供一个functional的布尔值,设置为true可以使组件无状态和无实例,也就是无状态和this上下文。这样用render函数返回虚拟节点可以更容易渲染。
适合场景
程序化的在多个组件中选择一个或者在将children,props,data传递给子组件之前操作它们
2)计算属性computed:当运算过长或者逻辑复杂时,这时难以维护,计算属性可以解决这个问题。
计算属性可以依赖多个vue实例的数据,只要其中一个数据发生变化,计算属性就会重新执行,视图也会更新。
每一个计算属性都包含一个getter,setter,getter是读取默认,当手动修改计算属性值时就会触发setter函数,修改时就像修改一个普通数据一样。
app.fullName='jack doce';触发setter函数
计算属性实现过滤
<div id="app">
{{reversedText}}
</div>
<script src="vue.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
text:'123,456'
},
computed:{
reversedText:function(){
return this.text.split('').reverse().join('');
}
}
})
3)methods和computed/watch关系
3.1》methods和computed/watch的对比
1》作用机制上
1.watch和computed都是以vue的依赖追踪机制为基础的,他们都试图处理这样一件事:当某一个数据(称为依赖数据)发生改变的时候,所有依赖这个数据的“相关”数据“自动”发生变化,也就是自动调用相关的函数去实现数据的变动。
2.methods里面用来定义的函数,是需要手动调用才能执行
2》性质上
1.computed是计算属性,事实上的data对象里的数据属性是同一类(使用上)
2.methods里面定义的是函数,需要“fun()”这样去调用
3.2》computed和watch的对比
共同点:他们都是以vue的依赖追踪机制为基础的,都是希望依赖数据发生改变的时候,被依赖的数据根据预先定义好的函数,发生“自动”变化
不同点:watch擅长处理的场景:一个数据影响多个数据
<div id="app">
<input type="text" v-model="haizeituan_name"/>
</div>
var app=new Vue({
el:"#app",
data:{
haizeituan_name:"草帽海贼团",
suolong:"草帽海贼团索隆",
namei:"草帽海贼团娜美"
},
watch:{
haizeituan_name:function(newname){
this.suolong=newname+"索隆";
this.namei=newname+"娜美";
console.log(this.suolong);
console.log(this.namei);
}
}
})
computed擅长处理的场景:一个数据受对个数据影响
<div id="app">
<input type="text" v-model="lufei_name"/>
</div>
var app=new Vue({
el:"#app",
data:{
firstname:"草帽海贼团",
secname:"h索隆"
},
computed:{
lufei_name:function(newname){
return this.firstname+this.secname
}
}
})
methods不处理数据逻辑关系,只提供可调用的函数
var app=new Vue({
el:"#app",
template:"<p>{{say()}}</p>",
methods:{
say:function(){
return "我要成为火影忍者"
}
}
})
3.3》从功能互补上
computed是用来处理在使用watch和methods的时候无法处理或者处理不太恰当的情况。
利用computed处理methods存在的重复计算情况
methods里面的函数只要去调用它,它就会执行并返回结果,即使这些结果可能相同是不需要的
computed它会以vue依赖追踪系统为基础,只要依赖数据没有发生变化,computed就不会再度调用,使用的是缓存数据
new Date()不是依赖数据(不是放在data等对象下的实例对象),所有computed只提供缓存的值
<div id="app">
<button @click="getMethodsDate">methods
</button>
<button @click="getComputedDate">computed
</button>
</div>
var app=new Vue({
el:"#app",
methods:{
getMethodsDate:function(){
console.log(new Date())
},
getComputedDate:function(){
console.log(this.computeddate)
}
},
computed:{
computeddate:function(){
return new Date();
}
}
})
利用computed处理watch在特定情况下代码冗余的现象,简化代码
4)根容器:el指定一个页面已用DOM来挂载vue实例,可以使HTMLElement也可以是css选择器。挂在成功后可以使用app.$el来访问该元素。
5)存储数据:data(用于数据储存)
6)插值方法:
1》{{}}最基本的插值方法,他会自动将我们双向绑定的数据实时显示出来。
如果想{{}}里的内容不进行替换可以使用v-pre即可是这个元素以及他的子元素跳过编译过程。
{{}}中不支持语句和流控制
|管道符对数据进行过滤,{{message|filter(‘arg1’,’arg2’)}},这里的字符串arg1,arg2分别是过滤器的第二和第三个参数,第一个参数是数据本身。
filter是vue的过滤器
<span v-pre>{{name}}</span>//输出{{name}}
{{var book=”vue”}}//不支持
{{if (ok) return msg}}//不支持
<div id="app">
{{date|foramtDate}}
</div>
<script src="vue.min.js"></script>
<script>
var padDate=function(value){
return value<10?'0'+value:value;
}
var app=new Vue({
el:"#app",
data:{
date:new Date()
},
filters:{
foramtDate:function(value){
var date=new Date(value);
var year=date.getFullYear();
var month=padDate(date.getMonth()+1);
var day=padDate(date.getDate());
var hours=padDate(date.getHours());
var minutes=padDate(date.getMinutes());
var seconds=padDate(date.getSeconds());
return year+'-'+month+'-'+day+'-'+hours+'-'+minutes+'-'+seconds;
}
},
mounted:function(){
var _this=this;
this.time=setInterval(function(){
_this.date=new Date();
},1000)
},
beforeDestroy:function(){
if(this.time){
clearInterval(this.time)
}
}
})
6)V-on事件监听(可用@表示)
v-on:click=”方法名”或者v-on:click=”内联语句”
<button v-on:click="show=!show">
Vue提供了一个特殊变量$event用于访问原生dom事件
修饰符:@click.修饰符
.stop阻止冒泡事件 .prevent提交事件不在重载页面 .capture添加事件侦查器的时候使用事件捕获模式 .self 只有事件在该元素本身(不是子元素)触发回调函数
.once只触发一次 .native监听的是一个原生事件,监听的是该组件的根元素
@keyup触发键盘事件也可以使用修饰符
@keyup.13
自己配置具体的键Vue.config.keyCodes.f1=112;之后就可以使用@keyup.f1
<div id="app">
<p v-if="show">这是一段文本</p>
<button v-on:click="handleClose">点击隐藏</button>
</div>
<script src="vue.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
show:true
},
methods:{
handleClose:function(){
this.show=!this.show
}
}
})
7)v-model用于在表单类元素实现双向绑定(表单类元素单选,双选,下拉框,输入框等)
使用v-model后,表单控件显示的值只依赖所绑定的数据
如果使用中文输入法输入中文,一般在拼音阶段,vue是不会更新数据的,当敲下汉字时才会触发更新,如果希望一直更新可以使用@input来代替v-model相当是v-model的语法糖不过他会在不同的表单上智能处理
使用时注意事项:
单选框单独使用可以直接使用v-bind实现,复选框单独使用v-model绑定一个布尔值
组合使用来实现互斥选择结果,需要v-model配合value来使用。
下拉框中<option>如果含有value属性,v-model会优先匹配value值,没有时直接匹配text,给select加multiple就可以多选
绑定动态值需要v-bind来配合使用
修饰符:用于控制数据同步的时机
.lazy转变为在change事件中同步(就是输入后按enter键后同步)
.number可以将你输入的值转换成Number类型,否则你输入的是数字但是其实他是字符串类型
.trim自动过滤输入的首尾字符串
<div id="app">
<input type="text" v-model="name" placeholder="你的名字"/>
<h1>你好,{{name}}</h1>
</div>
<script src="vue.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
name:''
}
})
</script>
8)v-bind(简写为:)动态更新html元素上的属性,比如id,class
主要用于绑定class和style的绑定
绑定class的方法:1》v-bind:class设置一个对象,可以动态的切换class
2》当:class的表达式过长时或者逻辑比较复杂时,可以绑定一个计算属性
3》当需要多个class时,可以使用数组语法,给class绑定一个数组,也可以在数组中使用对象的语法
绑定style方法与class类似也是有对象和数组语法
<div id="app">
<div :class="{'active':isActive}"></div>
</div>
<script src="vue.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
isActive:true
}
})
2》v-html是想要输出HTML时使用,但是要注意如果将用户产生的内容使用v-html可能会产生xss,所以要在服务端对用户提交的内容进行处理。
<div id="app">
<span v-html="link"></span>
</div>
<script src="vue.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
link:"<a href='#'>这是一个连接</a>"
}
})
9>V-if,v-else-if,v-else:可以根据表达式的值在dom中渲染或者销毁元素/组件
如果一次判断是多个元素,可以在vue的内置<tempalte>元素上使用条件指令,最终渲染结果不包括该元素
出于效率的考虑vue在渲染元素的时候会尽可能的复用已经有的元素而非重新渲染,如果你不希望这样vue提供了key,可以让你自己决定是否复用,key必须是唯一的。
<div id="app" v-cloak>
<tempalte v-if='states==1'>
<p>11223</p>
<p>11223</p>
<p>11223</p>
<p>11223</p>
</tempalte>
</div>
<script src="vue.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
states:1
}
})
11)v-show用法基本与v-if一样,但是v-show是改变元素display的属性。不能在template中使用,在template中使用不起作用。
<p v-show="status"></p>
12)v-if和v-show的区别
v-if是真正的条件渲染,它会根据表达式适当的重建或者销毁元素以及绑定事件和子组件。如果表达式的初值是false那么一开始元素/组件并不会渲染,只有当第一次条件为真时才开始编译。
v-show只是简单地css属性的切换,无论条件是否为真都会被编译。
v-if更适合不经常改变的场景因为他的切换开销大,v-show适合频繁切换的场景
13)v-for将一个数组/对象循环遍历显示时使用,他的表达式结合in/of来使用。
V-for遍历数组时支持一个可选参事作为当前项的索引值,遍历对象时支持两个可选参事
v-for也可以用在内置标签中tempalte中使用
<div id="app">
<ul>
<li v-for="(book,index) in books">{{index}}-{{book.name}}</li>
</ul>
</div>
<script src="vue.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
books:[
{name:'vue实战'},
{name:'实战es2015'},
{name:'高级程序设计'},
{name:'node.js开发设计'}
]
}
})
</script>
14)数组更新改变数值时会触发视图更新,除此之外vue包含了一组观察数据变异的方法,使他们改变数值也会触发视图更新。
数组变化时并不是直接重新渲染整个列表,而是最大化的复用dom元素,含有相同元素的项不会重新渲染。
有些变动,vue是不能检测到的:
- 通过索引值直接设置项app.books[3]={name:"shisha"};
- 修改数组长度:app.books.length=1;
解决方案:
解决第一个问题:
方法一:vue内置了set方法:Vue.set(app.books,3,{name:"shisha"})
在webpack中使用组件化的方法,默认没有导入vue,这时可以用this.$set,
在非webpack也可以使用app.$set()
方法二:splise来解决app.books.splice(3,1,{name:"shisha"})
解决第二个问题:
splice也可以解决:app.books.splice(1)
改变原数组push(),unshift(),pop(),shift(),splice(),sort(),reverse(),
还有些方法不会改变原数组是返回一个新的数组,filter(),concat().slice()
<div id="app">
<ul>
<li v-for="(book,index) in books">{{index}}-{{book.name}}</li>
</ul>
</div>
<script src="vue.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
books:[
{name:'vue实战'},
{name:'实战es2015'},
{name:'高级程序设计'},
{name:'node.js开发设计'}
]
}
})
app.books.push({
name:'css揭秘',
author:'石莎'
})
11>v-cloak:不需要表达式,他会在vue实例结束编译时从绑定的html元素上移除,它主要解决的简单项目时是当网速比较慢的时候页面闪动问题,vue.js还没有加载完时,在页面上会显示{{message}}的字样,直到vue创建实例,编译模板,dom才会被替换,这个时候屏幕是有闪动的,它可以解决。
<div id="app" v-cloak>
{{message}}
</div>
<script src="vue.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
message:"这是一文本",
}
})
12>v-once:不需要表达式,作用是定义它的元素或者组件只渲染一次,包括元素或组件的所有子节点,首次渲染后,不再随数据的变化重新渲染,将被视为静态元素
<div id="app" v-cloak>
<input type="text" v-model="message"/>
<div v-once>{{message}}</div>
</div>
<script src="vue.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
message:"这是一个文本"
}
})//不发生改变结果一直是这是一个文本
11)组件:vue组件提高重用性
使用组件:
先要注册组件有全局注册和局部注册
全局注册:Vue.component('自定义的标签名',{}),组件选项中添加template就可以显示组件内容
template的dom结构必须被一个元素包含,如果直接写‘这是组件的内容’是无法渲染的
局部注册:注册以后只能在该实例作用域下有效
var child={
template:'<div>这里是组件的内容</div>'
}
var app=new Vue({
el:"#app",
components:{
'my-template':child
}
})
注意事项:Vue组件模板在一些情况下会受到html限制,比如<table>内规定只允许<tr>,<td>,<th>等元素,所以在<table>内直接使用组件无效,这种情况下,可以使用特殊的属性来挂载组件,常见的限制元素<ul>,<ol>,<select>
<div id="app">
<table>
<tbody is="my-template"></tbody>
</table>
</div>
<script src="vue.min.js"></script>
<script>
Vue.component('my-template',{
template:'<div>这里是组件的内容</div>'
})
var app=new Vue({
el:"#app"
})
组件内的选项:template,data,computed,methods但是data与实例的稍有不同,组件data必须是函数,将数据return出去。
Vue.component('my-template',{
template:'<div>{{message}}</div>',
data:function(){
return {
message:'组件内容'
}
}
})
如果return出的对象引用了外部的一个对象,那这个对象就共享,任何一方修改都会同步。解决方案是给data返回一个新的data对象来独立。
Vue.component('my-component',{
template:'<button @click="counter++">{{counter}}</button>',
data:function(){
return{
counter:0
}
}
})
var app=new Vue({
el:"#app"
})
12)组件传值(父子组件,兄弟组件,跨级组件)
父->子props:父组件的模板中包含子组件,父组件要正向向子组件传递数据或者参数,子组件接受后根据参数的不同来渲染不同的内容或执行操作
props值两种:字符串数组,对象。
动态绑定props用v-bind绑定
<div id="app">
<input type="text" v-model="parentmassage"/>
<my-component :message="parentmassage"></my-component>
</div>
<script src="vue.min.js"></script>
<script>
Vue.component('my-component',{
props:['message'],
template:'<sapn>{{message}}</sapn>'
})
var app=new Vue({
el:"#app",
data:{
parentmassage:''
}
})
</script>
子->父:子组件用$emit()来触发事件,父组件用$on()来监听事件,父组件也可以直接在子组件的自定义标签使用v-on来监听子组件触发自定义事件。
<div id="app">
<p>总数:{{total}}</p>
<my-component @increase="handleGet"
@reduce="handleGet"></my-component>
</div>
<script src="vue.min.js"></script>
<script>
Vue.component('my-component',{
template:'\
<div>\
<button @click="handleincrease">+1</button>\
<button @click="handlereduce">-1</button>\
</div>',
data:function (){
return {
counter:0
}
},
methods:{
handleincrease:function(){
this.counter++;
this.$emit('increase',this.counter)
},
handlereduce:function(){
this.counter--;
this.$emit('reduce',this.counter)
}
}
})
var app=new Vue({
el:"#app",
data:{
total:0
},
methods:{
handleGet:function(total){
this.total=total;
}
}
})
</script>
解释:改变组件中的counter,通过$emit()把它传递给父组件,父组件用@increace和@reduce。$emit方法第一个参数是自定义事件名称,后面的参数是要传递的参数,可以不填或多填几个。
非父子组件通信:兄弟组件和跨级组件
vue2中使用一个空的vue实例作为中央事件总线
<div id="app">
{{message}}
<my-component></my-component>
</div>
<script src="vue.min.js"></script>
<script>
var bus=new Vue()
Vue.component('my-component',{
template:'<button @click="handleEvent">传递事件</button>',
methods:{
handleEvent:function(){
bus.$emit('on-message','来自组件my-component的内容')
}
}
})
var app=new Vue({
el:"#app",
data:{
message:""
},
mounted:function(){
var _this=this;
bus.$on('on-message',function(msg){
_this.message=msg
})
}
})
</script>
父链:在子组件中可以使用this.$parent直接访问该组件的父类实例或组件,父组件可以使用this.$children访问他的所有子组件,可以向上向下无限访问,直到根实例或最内层的组件。
<div id="app">
{{message}}
<my-component></my-component>
</div>
<script src="vue.min.js"></script>
<script>
Vue.component('my-component',{
template:'<button @click="handleEvent">传递事件</button>',
methods:{
handleEvent:function(){
this.$parent.message='来自组件my-component的内容';
}
}
})
var app=new Vue({
el:"#app",
data:{
message:""
}
})
子组件索引:当子组件比较多时,通过this.$children一一遍历出我们需要的一个组件实例是比较困难的尤其是组件动态渲染的时候,他们的序列是不固定的,vue提供子组件的索引的方法,用特殊的属性ref来为子组件指定一个索引名称。
<div id="app">
<button @click="handleref">通过ref获取子组件实例</button>
<my-component ref="comA"></my-component>
</div>
<script src="vue.min.js"></script>
<script>
Vue.component('my-component',{
template:'<div>子组件</div>',
data:function(){
return {
message:"子组件内容"
}
}
})
var app=new Vue({
el:"#app",
methods:{
handleref:function(){
var msg=this.$refs.comA.message;
console.log(msg)
}
}
})
13>vue-loader:在webpack中使用vue-loader就可以对.vue格式的文件进行处理
.vue文件一般包含3部分,即<template>,<script>,<style>,使用.vue文件前要安装vue-loader,vue-style-loader等加载器并做配置。
<template></template>之间的代码就是该组件的模板html
<style></style>之间的是css样式,其中有scoped属性表示当前css只在这个组件有效,还可以结合css预编译,如使用less处理可以写成<style lang=”less”>
14>vue-router前端路由:
路由:通俗来讲就是网址,在专业一点就是每次get或者post等请求在服务端有一个专门的正则匹配列表,然后匹配到具体的一跳路径后,分配到不同的controller,进行各种操作,最终将html或数据返回给前端。
前端路由:由前端来维护一个路由的规则,实现有两种,一种是利用url的hash,另一种是html5的history模式,不过使用这种模式需要服务端的支持。
前端路由的优点:页面持久性,像大部分的音乐网站,你可以在播放音乐的同时跳转到别的页面,而音乐没有中断。
前后端彻底分离