Vue学习笔记
1. 数据双向绑定
<div id="app">
<input type="text" placeholder="请输入" v-model="content" />
<button @click="btnSubmit">提交</button>
<ul>
<li v-for="item,index in list">{{index}}——{{item}}</li>
</ul>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
content:"",
list:["第一个内容","第二个内容"]
},
methods:{
btnSubmit:function(){
this.list.push(this.content);
this.content="";
}
}
});
</script>
2.MVVM模式
传统模式:MVP模式(即MVC模式)
视图层发送事件到控制器,控制器到模型层操作ajax获取数据并渲染到视图层 或者 在view层操作dom元素。
这个模式中,控制器是非常重要核心的,大量的代码都会放在presenter,Presenter中会有很多dom操作,是面向dom开发。
VUE中的MVVM模式:从概念上真正实现了页面与数据的分离。
model:通过ajax获取数据;
view:界面图形和数据;
ViewModel:view和model的连接器,实现了view和model之间的数据双向绑定。通过监听view(或model)的change,对应的修改model(或view)。(vue封装了ViewModel层)
这个模式中,model是重要的核心层,是面向数据开发。
3. 前端组件化
组件化:将页面的一个整体,划分成多个部分组件,每个组件实现不同的功能。方便维护和扩展。
使用组件化的思想,优化第一节的代码:
定义组件时,组件名字驼峰命名,并且首字母大写。在view层使用时,使用小写字母,并使用“-”连接符连接。
例如:定义的名字为:TodoItem,使用时应使用标签<todo-item></todo-item>
全局组件:通过 Vue.component("组件名字",{属性});使用前不需要再次声明;
局部组件:通过 var 组件名字={属性};使用前需要在vue对象中声明:components:{"使用时的名字":"定义时的名字"}
<div id="app">
<input type="text" placeholder="请输入" v-model="content" />
<button @click="btnSubmit">提交</button>
<ul>
<!-- 组件在使用时,需要将大写字母转为小写并使用连接符-连接 -->
<todo-item v-bind:content="item" v-for="item,index in list"></todo-item>
</ul>
</div>
<script type="text/javascript">
//定义全局组件
/*Vue.component("TodoItem",{
props:["content"],
template:"<li>{{content}}</li>"
});*/
//定义局部组件,使用前需要在vue对象中进行注册
var TodoItem={
props:["content"],
template:"<li>{{content}}</li>"
}
var app = new Vue({
el:"#app",
data:{
content:"",
list:["第一个内容","第二个内容"]
},
components:{
TodoItem:TodoItem
},
methods:{
btnSubmit:function(){
if(this.content!=""){
this.list.push(this.content);
this.content="";
}else{
alert("内容不能为空");
}
}
}
});
</script>
4.父子组件传值
4.1 实例
通过父子组件之间的相互监听,实现父子组件之间的传值;
<div id="app">
<input type="text" placeholder="请输入" v-model="content" />
<button @click="btnSubmit">提交</button>
<ul>
<!-- 父组件 -->
<todo-item v-bind:content="item"
:index="index"
@delete="itemDelete"
v-for="item,index in list"
></todo-item>
</ul>
</div>
<script type="text/javascript">
//子组件
Vue.component("TodoItem",{
props:["content","index"],
template:"<li @click='handlerItemClick'>{{content}}</li>",
methods:{
handlerItemClick:function(){
//点击子组件时,触发delete事件,并且可以传递delete事件参数
this.$emit("delete",this.index);
}
}
});
var app = new Vue({
el:"#app",
data:{
content:"",
list:["第一个内容","第二个内容"]
},
methods:{
btnSubmit:function(){
if(this.content!=""){this.list.push(this.content);this.content="";}
},
itemDelete:function(index){
this.list.splice(index,1);
}
}
});
4.2 单项数据流
在vue中,父组件向子组件传值,是通过属性(props)传递的;但是子组件不能随意修改父组件传过来的参数(单项数据流)。父——>子。
子组件如果要修改父组件传递过来的参数,需要借助变量实现,通过修改变量的值,实现数据的修改。
<div id="root">
<counter :count="10" @inc="handleIncrease"></counter>
<counter :count="100" @inc="handleIncrease"></counter>
<div>{{total}}</div>
</div>
<script type="text/javascript">
var counter={
props:["count"],
//父组件的传递值在子组件中不能直接修改,可以赋值给另一个变量,将变量绑定到组件中并进行数值修改
data:function(){
return{
number:this.count
}
},
template:'<div @click="handleClick">{{number}}</div>',
methods:{
handleClick:function(){
this.number+=2;
this.$emit("inc",2);//传递方法和值
}
}
}
var vm=new Vue({
el:"#root",
data:{total:110},
components:{//声明局部组件
counter:counter },
methods:{
handleIncrease:function(step){
this.total += step;
}
}
});
</script>
4.3 父组件传值约束
父组件传值给子组件时,子组件对参数进行校验和约束的过程。
4.3.1 数值类型校验:
type: String、Number
4.3.2 必传 校验:
required: true/false
4.3.3 非必传时的默认值:
default: ' default value '
4.4.4 自定义校验:
validator:function(value){ return (value.length>5) }
<div id="root">
<!-- 将content值传递给子组件。
注意,如果要传递数字(Number类型),需要在属性前加“:”,即:content="1234";
如果要传递字符串,则不需要添加“:”,即content="asdfasf" -->
<child content="safdasf"></child>
</div>
<script type="text/javascript">
Vue.component('child',{
props:{
content:{
type: [Number,String], //接收Number和String。
required:true, //参数必填
validator:function(value){//自定义校验:子组件接收的值长度必须大于5
return (value.length>5)
}
}
},
template:'<div>Child</div>'
});
var vm=new Vue({
el:"#root"
});
</script>
4.4.5 props特性
父组件使用子组件时,通过属性向子组件传值时,并且子组件的props中声明了该参数名(即在子组件的props数组中声明)。父组件传递,子组件接收。
4.4.6 非props特性
父组件传递的参数名,在子组件中的props属性中未定义;即非props特性。生产应用几乎不使用。
非props特性情况下,template在定义组件时使用了该父组件参数名,则会报错。
非props特性情况下,template未使用父组件传递参数的参数名,则父组件的属性会显示在子组件最外层的dom标签的属性中。
4.4 非父子组件间的传值
总线机制/BUS机制/发布订阅模式/观察者模式
<div id="app">
<!-- 兄弟组件之间传值-->
<child content="Wang"></child>
<child content="ZHAO"></child>
</div>
<script type="text/javascript">
Vue.prototype.bus=new Vue() //在vue实例上挂载一个bus
Vue.component('child',{
props:["content"],
data:function(){
return {
temp:this.content
}
},
template:'<div @click="changeContent">{{temp}}</div>',
methods:{
changeContent:function(){
//传递change事件和参数
this.bus.$emit("change",this.temp);
}
},
//组件被挂载后执行,监听bus的改变。由于两个组件,因此会被执行两次。
mounted:function(){
var that=this;//下方函数中使用this时,其作用域会改变,因此可以复制给that
this.bus.$on('change',function(msg){
that.temp=msg;
})
}
})
var vm=new Vue({ el:"#app" });
</script>
5. 生命周期钩子
<div id="app">
<div><input type="text" v-model="content" /></div>
<div>{{content}}</div>
</div>
<script type="text/javascript">
var vm=new Vue({
el:"#app",
data:{
content:"hello world"
},
beforeCreate:function(){//实例的事件和生命周期周期初始化完成后执行
console.info("【声明周期函数】生命周期初始化完成后执行","beforeCreate")
},
created:function(){//实例被创建之后执行
console.info("【声明周期函数】实例创建后执行","created")
//console.log(this.$el);
},
beforeMount:function(){//页面加载之前会执行
console.info("【声明周期函数】页面加载前执行","beforeMount")
//console.log(this.$el);
},
mounted:function(){//页面加载之后执行
console.info("【声明周期函数】页面加载后执行","mounted")
//console.log(this.$el);
},
beforeDestroy:function(){//实例对象执行销毁函数后,实例被销毁前执行
console.info("【声明周期函数】实例被销毁之前执行","beforeDestroy")
},
destroyed:function(){//实例对象执行销毁函数后,实例被销毁后执行
console.info("【声明周期函数】实例被销毁之后执行","destroyed")
},
beforeUpdate:function(){//数据发生改变,重新渲染之前执行
console.info("【声明周期函数】数据改变后重新渲染之前执行","beforeUpdate")
},
updated:function(){//数据发生改变,重新渲染完成后执行
console.info("【声明周期函数】数据改变后重新渲染后执行","updated")
}
});
</script>
6.简单的模板语法
6.1 v-text,v-html
<div id="app">
<div>差值表达式:{{name}}</div>
<div v-text="'类似于差值表达式:'+name"></div>
<div v-html="'可以渲染html标签:<h3>'+name+'</h3>'"></div>
<div v-bind:title="name"></div>
</div>
<script type="text/javascript">
var vm=new Vue({
el:"#app",
data:{
name:"Lenovo"
}
});
</script>
6.2计算属性、方法、侦听器
计算属性拥有缓存机制:当计算所需的变量值不发生改变时,数据重新渲染时不会再次进行计算,而是使用之前计算的结果进行渲染。只有当变量的值改变后,触发数据渲染时,计算属性才会被再次触发。
<div id="app">
<div>{{fullName}}</div>
<div>{{age}}</div>
<div>{{fullName2()}}</div>
<div>{{fullName3}}</div>
</div>
<script type="text/javascript">
var vm=new Vue({
el:"#app",
data:{
firstName:"Lenovo",
lastName:"Wang",
age:22,
fullName3:"Lenovo Wang"
},
computed:{//性能高、简洁
fullName:function(){//计算属性的缓存机制:若变量值不发生改变,进行数据渲染时不会重新计算。
console.log("执行计算");
return this.firstName+" "+this.lastName;
}
},
methods:{
fullName2:function(){//数据渲染一次,方法执行一次,性能不如计算属性
console.log("执行方法");
return this.firstName+" "+this.lastName;
}
},
watch:{
firstName:function(){//监听firstName变量的改变
console.log("执行firstName监听");
this.fullName3=this.firstName+" "+this.lastName;
},
lastName:function(){//监听lastName变量的改变
console.log("执行lastName监听");
this.fullName3=this.firstName+" "+this.lastName;
}
}
});
</script>
6.3 计算属性的setter和getter
<div id="app">
<div>{{fullName}}</div>
</div>
<script type="text/javascript">
var vm=new Vue({
el:"#app",
data:{
firstName:"Lenovo",
lastName:"Wang"
},
computed:{
fullName:{
get:function(){
return this.firstName+" "+this.lastName
},
set:function(value){
var arr=value.split(" ");//根据空格分割成数组
this.firstName=arr[0];
this.lastName=arr[1];
}
}
}
});
</script>
6.4 条件渲染
v-if :隐藏后,页面加载时DOM结点不会被加载出来。(v-if在操作dom时,相同的结点会进行复用:比如input标签,在true时展示出来,在false也会展示出来,此时会进行input组件复用,包括input的值内容。如果不想不用,则赋予input属性key不同的值)key值必须唯一,但是在v-for中,尽量不适用index作为key的值,以便提高性能(并不清楚如何可以提高)。
v-show:隐藏后,DOM结点仍然是被加载出来了,但是被display:none隐藏掉了。(涉及频繁操作DOM时,效率较高)
<div id="app">
<div v-if="show">{{message}}<input key="true" /></div>
<div v-else>v-if操作结点时,dom会被增删<input key="false" /></div>
<div v-show="flag">{{message}}</div>
</div>
<div id="ifelse">
<div v-if="show=='a'">This is A</div>
<div v-else-if="show=='b'">This is B</div>
<div v-else>This is others</div>
</div>
<script type="text/javascript">
var vm=new Vue({
el:"#app",
data:{ message:"Lenovo",show:false,flag:true}
});
var vue=new Vue({
el:"#ifelse",
data:{show:"a"}
})
</script>
6.5 列表渲染
代码的重点有点偏了……
<div id="app">
<div v-for="item,index of list" :key="item.id">
{{index}}:{{item.id}}_{{item.name}}
</div>
</div>
<script type="text/javascript">
/*js操作数组元素:
push(content) 数组末增加元素;参数为元素值。
pop() 数组中的最后一个元素删除;无参。
shift() 把数组中的第一个元素删除;无参,并返回第一个元素值。
unshift(content) 在数组的前端添加一个或多个元素。参数为元素值。并返回新数组的长度。
splice(index,length) 数组修改,返回值为:从index开始删除长度为length个元素的数组(第三个参数为增加的元素)
slice(start,end) 数组截取,返回从start下标开始选取到end下标的数组。参数可以为负数,-1表示倒数第一个。
sort 数组排序
reverse 数组取反*/
var vm=new Vue({
el:"#app",
data:{
list:[{id:1,name:"A"}, {id:2,name:"B"}, {id:3,name:"C"}]
}
});
</script>
6.6 组件绑定事件
组件在定义时,可以绑定两种组件:原生事件(Vue对象中定义)和自定义事件(组件创建时定义)。
6.6.1 绑定自定义事件
<div id="root">
<child></child>
</div>
<script type="text/javascript">
Vue.component('child',{
props:["content"],
template:'<div @click="handleClick">Child</div>',
methods:{
handleClick:function(){
alert("自定义事件绑定");
}
}
});
6.6.2 绑定原生事件—— @click.navive="functionName"
<div id="root">
<child @click.native="clickChild"></child>
</div>
<script type="text/javascript">
Vue.component('child',{
props:["content"],
template:'<div>Child</div>'
});
var vm=new Vue({
el:"#root",
methods:{
clickChild:function(){
alert("原生事件绑定");
}
}
});
</script>
6.6.3 通过this.$emit('click')触发原生事件__很啰嗦啦
<div id="root">
<child @click="clickChild"></child>
</div>
<script type="text/javascript">
Vue.component('child',{
props:["content"],
template:'<div @click="handleClick">Child</div>',
methods:{
handleClick:function(){
this.$emit('click');//组件通过这个触发自定义事件
}
}
});
var vm=new Vue({
el:"#root",
methods:{
clickChild:function(){
alert("原生事件绑定");
}
}
});
</script>