1.MVVM模式
- M:模型,包括数据和一些基本操作。
- V:视图、页面渲染结果。
- VM:View-Nodel,模型与视图间的双向操作(无需开发人员干涉)。
官网:https://cn.vuejs.org/v2/guide/
github:https://github.com/vuejs
2.Node和NPM
下载之后,在控制台中查看node-v和npm -v查看是否自动配置好环境变量。
npm默认的仓库地址是在国外网站,速度较慢,建议设置淘宝镜像。但是切换镜像是比较麻烦的,推荐一款切换镜像工具:nrm。
首先安装nrm,-g代表全局安装
npm install nrm -g
3.快速入门
3.1新建项目
3.2项目初始化
npm init -y
初始化之后项目下面多了一个package.json
3.3安装vue
–save 只针对项目使用
-g 全局使用
安装之后,项目多了一个文件夹。
3.4编写html
3.4.1 新建html
3.4.2 在html中引入依赖
<script src="node_modules/vue/dist/vue.js"></script>
3.4.2 入门实例
vue实例
new vue({})
<body>
<div id="app">
<h1>
{{name}}非常帅!
</h1>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"jack",
}
});
</script>
</body>
F12,去修改name属性,vue会监测属性值的变化。
v-model双向绑定
v-model:模型数据,即有v-model表示的标签与模型数据关联。
<body>
<div id="app">
<input type="text" v-model="num">
<h1>
{{name}}非常帅!<br>
{{num}}个人。
</h1>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"jack",
num:1,
}
});
</script>
</body>
因为input框中数据与模型数据关联,所以无论谁改变都会影响到另一个的改变。
事件绑定
@click=函数
@click="num++”
<div id="app">
<input type="text" v-model="num">
<button @click="num++">+</button>
<h1>
{{name}}非常帅!<br>
{{num}}个人。
</h1>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"jack",
num:1,
}
});
</script>
</body>
点击“+”,num就加一。
@click=方法名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<button @click="handleClick">hello</button><br>
<input type="text" v-model="num">
<h1>
{{name}}非常帅!<br>
{{num}}个人。
</h1>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"jack",
num:1,
},
methods:{
handleClick(){
console.log("hello");
}
}
});
</script>
</body>
</html>
3.5vue的生命周期
- 初始化
- 页面渲染
- 是否有el或template元素,若el或template存在就找到关联的html标签,对标签进行编译(对vue代码编译成浏览器可以识别的代码),然后创建真实的html代码,把它挂在el或template关联的元素上。
挂载之前交beforeMount,挂载之后叫mounted。 - dom元素的重新渲染和挂载。
执行之前叫beforUpdate,之后叫updated。 - 销毁
销毁之前叫beforeDestory,销毁之后叫Destoryed。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<button @click="handleClick">hello</button><br>
<input type="text" v-model="num">
<h1>
{{name}}非常帅!<br>
{{num}}个人。
</h1>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"jack",
num:1,
},
methods:{
handleClick(){
console.log("hello");
}
},
created(){
//向后台发起ajax请求,完成对data数据初始化,延迟1s后再赋值
setTimeout(() => this.name = "li",1000);
}
});
</script>
</body>
</html>
1s后
4.指令
指令是带有v-前缀的特殊属性。eg:v-model,代表双向绑定。
@click = v-on:click
4.1插值表达式
4.1.1花括号
{{}}
4.1.2插值闪烁
当用户网速延迟时使用{{}}会出现问题,在数据未加载完成时,页面会显示出原始的{{}},加载完毕后才显示正确数据,称为插值闪烁。
测试:
将网速调慢
4.1.3解决插值闪烁v-text,v-html
这两个都是单向绑定。
使用v-text和v-html来替代{{}}。
- v-text:将数据输出到元素内部,如果输出的数据中有html代码,html代码会原样输出。
- v-html:将数据输出到元素内部,若输出的数据中有html代码,会被渲染。
v-text和v-html的区别:
<span v-text="name"></span><br>
<span v-html="name"></span>
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"<font color='#a52a2a'>jack</font>",
num:1,
},
methods:{
handleClick(){
console.log("hello");
}
},
created(){
//向后台发起ajax请求,完成对data数据初始化,延迟1s后再赋值
//setTimeout(() => this.name = "li",1000);
}
});
但是v-html不安全,若黑客利用v-html传输,就会在页面上挂木马。
4.2 v-model
双向绑定。
用户可以输入可以操作的都可以使用v-model。
<input type="checkbox" value="java" v-model="lessons"/>java<br>
<input type="checkbox" value="ios" v-model="lessons"/>ios<br>
<input type="checkbox" value="php" v-model="lessons"/>php<br>
<h1>您已购买下列课程:{{lessons.join(",")}}</h1>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"<font color='#a52a2a'>jack</font>",
num:1,
lessons:[]
}
});
4.3 v-on
4.3.1基本用户
v-on用于给页面元素绑定事件。
语法:@事件名=“js片段或者函数名”
4.3.1.1事件冒泡
<div style="width:100px;height: 100px;background-color: cadetblue" @click="print('div')">
<button @click="print('button')">点我试试</button>
</div>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"<font color='#a52a2a'>jack</font>",
num:1,
lessons:[]
},
methods:{
handleClick(){
console.log("hello");
},
print(msg) {
console.log(msg);
}
}
});
点击div时:
点击button时:
当子元素父元素都有点击事件,当点击子元素的点击事件时,父元素的也被触发了,这就叫事件冒泡。
4.3.1.1解决事件冒泡
在子元素的时间上加上.stop
<button @click.stop="print('button')">点我试试</button>
事件委托
4.4事件修饰符
在事件处理程序中调用event.preventDefault或event.stopPropagation()是非常常见的需求。
vue.js为v-on提供了事件修饰符:
- .stop:阻止事件冒泡。
- .prevent:阻止默认事件发生。
- .eapture:使用事件捕获模式。
- .self:只有元素自身出发事件才执行。(冒泡或捕获的都不执行)。
- .once:只执行一次。
阻止默认事件发生
<div style="width:100px;height: 100px;background-color: cadetblue" @click="print('div')">
<button @click.stop="print('button')">点我试试</button>
<a href="http://www.baidu.com" @click.prevent="print('百度')">百度一下!</a>
</div>
<script>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"<font color='#a52a2a'>jack</font>",
num:1,
lessons:[]
},
methods:{
handleClick(){
console.log("hello");
},
print(msg) {
console.log(msg);
}
}
});
</script>
点击百度一下,并没有跳转到href指定的url。
4.5 v-for
4.5.1遍历数组
4.5.1.1只取数组中对象(一个参数)
<ul>
<li v-for="user in users">{{user.name+","+user.age}}</li>
</ul>
<hr>
<ul>
<li v-for="user in users"><p v-text="user.name+','+user.age"></p></li>
</ul>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"<font color='#a52a2a'>jack</font>",
num:1,
lessons:[],
users:[
{name:"li",age:21,gender:"女"},
{name:"wang",age:20,gender:"男"},
{name:"liu",age:21,gender:"女"},
{name:"zhang",age:21,gender:"男"}
]
}
})
4.5.1.2数组角标
在遍历过程中,如果我们需要知道数组角标,可以指定第二个参数。
<ul>
<li v-for="(user,index) in users">{{index}}-{{user.name+","+user.age}}</li>
</ul>
<hr>
<ul>
<li v-for="(user,index) in users"><p v-text="index+'-'+user.name+','+user.age"></p></li>
</ul>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"<font color='#a52a2a'>jack</font>",
num:1,
lessons:[],
users:[
{name:"li",age:21,gender:"女"},
{name:"wang",age:20,gender:"男"},
{name:"liu",age:21,gender:"女"},
{name:"zhang",age:21,gender:"男"}
]
}
})
4.5.2遍历对象
4.5.2.1遍历对象属性
<ul>
<li v-for="user in users[0]"><p v-text="user"></p></li>
</ul>
<ul>
<li v-for="(v,k,i) in users[0]"><p v-text="i+':'+k+','+v"></p></li>
</ul>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"<font color='#a52a2a'>jack</font>",
num:1,
lessons:[],
users:[
{name:"li",age:21,gender:"女"},
{name:"wang",age:20,gender:"男"},
{name:"liu",age:21,gender:"女"},
{name:"zhang",age:21,gender:"男"}
]
}
})
4.5.3遍历常量
<ul>
<li v-for="i in 5"><p v-text="i"></p></li>
</ul>
4.5.4 key
vue.js用v-for,当数据发生改变时,v-for需要重新从开始变迁来渲染元素列表,这样效率会低,vue有内部优化,就是使用:key=“这个标签关联的唯一标识”。
<ul>
<li v-for="(user,index) in users" :key="index"><p v-text="index+'-'+user.name+','+user.age"></p></li>
</ul>
该例子中绑定的key是数组的索引,是唯一的。
4.6 v-if与v-show
判断,如果为true,则渲染,否则不渲染。
语法:
v-if=“表达式”
4.6.1 v-if
<button @click="show = !show">点击切换</button><br>
<h1 v-if="show">你好!</h1>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
name:"<font color='#a52a2a'>jack</font>",
num:1,
lessons:[],
users:[
{name:"li",age:21,gender:"女"},
{name:"wang",age:20,gender:"男"},
{name:"liu",age:21,gender:"女"},
{name:"zhang",age:21,gender:"男"}
],
show: true,
}
})
点击后
4.6.2v-if和v-for结合使用
<ul>
<li v-for="i in 5" v-if="i%2 === 0"><p v-text="i"></p></li>
</ul>
4.6.3v-if与v-else
<ul>
<li v-for="i in 5">
<p v-if="i%2 === 0" >{{i}}</p>
<p v-else style="background-color: cadetblue">{{i}}</p>
</li>
</ul>
4.6.4v-if、v-else、v-else-if
<ul>
<li v-for="i in 7">
<p v-if="i <= 5 " >工作日</p>
<p v-else-if="5 < i < 7" style="background-color: cadetblue">星期{{i}}</p>
<p v-else style="background-color: cadetblue">星期{{i}}</p>
</li>
</ul>
4.6.4v-show
<button @click="show = !show">点击切换</button><br>
<h1 v-if="show">你好!</h1>
<h1 v-show="show">hello!</h1>
点击切换后
v-if标识的标签直接从html页面上消失了,而v-show标识的标签是通过css样式来控制。从性能方面看v-show更好,从安全角度v-if更好。
页面加载时的一次性的判断,针对不同用户角色显示不同内容,使用v-if合适,防止通过源码查看。
v-show适合需要多次加载经常切换的场景。
4.7v-bind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
width: 100px;
height: 100px;
color: darkgray;
}
.red{
background-color: red;
}
.blue{
background-color: blue;
}
</style>
</head>
<body>
<div id="app">
<button @click="color='red'">红色</button>
<button @click="color='blue'">蓝色</button>
<div id="box" v-bind:class="color">box</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
color: "red",
},
methods:{
}
});
</script>
</body>
</html>
点击蓝色按钮
注意:
class="{{color}}"这样写是不可以的,插值表达式不能用在标签的属性中。
可以使用v-bind:class="color"或者:class=“color”。
优化代码
把class作为一个对象。
通过对象去指定。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
width: 100px;
height: 100px;
color: darkgray;
}
.red{
background-color: red;
}
.blue{
background-color: blue;
}
</style>
</head>
<body>
<div id="app">
<button @click="isred = !isred">点我</button>
<div id="box" v-bind:class="{red:isred,blue:!isred}">box</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
color: "red",
isred:true,
},
methods:{
}
});
</script>
</body>
</html>
点击按钮
4.8计算属性
<!-- 计算属性-->
<h1>
日期:{{birth}}
</h1>
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
isred:true,
birthday: 1529032123201
},
computed:{
birth(){
const day = new Date(this.birthday);
return day.getFullYear()+"-"+day.getMonth()+"-"+day.getDay();
}
},
});
他只在最开始计算一次。后续再使用,就不会再计算,会直接渲染。
4.8watch
4.8.1浅监控
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
isred:true,
birthday: 1529032123201,
num:1,
person:{name:"jack",age:21}
},
computed:{
birth(){
const day = new Date(this.birthday);
return day.getFullYear()+"-"+day.getMonth()+"-"+day.getDay();
}
},
watch:{
<!--浅监控-->
num(arg1,arg2){
console.log(arg1,arg2);
},
}
}
});
能监控值的变化,上个值和现在的值为多少。
4.8.2 深监控
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
isred:true,
birthday: 1529032123201,
num:1,
person:{name:"jack",age:21}
},
computed:{
birth(){
const day = new Date(this.birthday);
return day.getFullYear()+"-"+day.getMonth()+"-"+day.getDay();
}
},
watch:{
//写成一个对象
person:{
<!--person地址发生改变可以监控到,但是引用发生改变就监控不到了-->
<!--加上该属性就是深度监控-->
deep:true,
<!--定义一个方法,方法名必须为handler-->
handler(val){
console.log(val.age);
}
}
}
});
5.组件化
在开发大型应用时,页面可以分成很多部分,往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。
但是如果每个页面都独自开发,这无疑增加了我们开发的成本。所以会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。
5.1定义全局组件
使用vue的静态方法component(“组件id-String”,“定义组件{}”)
vue组件也是一个vue实例。
vue组件的定义方式和vue实例的定义方式几乎一样。vue实例的定义是跟html绑定了。
案例:
<body>
<div id="app">
<!-使用自定义组件--->
<conter></conter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//定义全局组件,参数:组件id,
Vue.component("conter",{
template:"<button @click='count++'>点我{{count}}次!</button>",
data(){
return{
count:0
}
}
})
//vue实例
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
}
});
</script>
</body>
重复使用自定义组件,不会共享属性。
- 组件其实也是一个vue实例,因此他在定义时也会接受:data、method、生命周期函数等。
- 不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
- 但是组件渲染需要html模板,所以增加了template属性,值就是html模板。
- 全局组件定义完毕,任何vue实例都可以直接在html中通过组件名称来使用组件了。
- data的定义方式比较特殊,必须是一个函数。
5.2局部注册
局部组件就是个变量,需要把它声明到vue实例内部。
<body>
<div id="app">
<!-使用自定义组件--->
<conter></conter>
<conter></conter>
<conter></conter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//定义全局组件,参数:组件id,
/* Vue.component("conter",{
template:"<button @click='count++'>点我{{count}}次!</button>",
data(){
return{
count:0
}
}
})*/
//局部组件,只能在当前实例中使用。
const conter = {
template:"<button @click='count++'>点我{{count}}次!</button>",
data(){
return{
count:0
}
}
}
//vue实例
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
},
components:{
//若key=value完全一致,可以简写成conter.
conter:conter
}
});
</script>
</body>
components就是当前vue对象子组件集合。(key就是子组件名称,value就是组件对象属性)
想过与全局注册类似,不同的是,局部组件只能在当前vue实例中使用。
5.3组件通信
通常一个单页应用会以一棵嵌套的组件树的形式来组织:
每个组件之间以潜逃的关系组合在一起,那么这时就需要有组件间通信。
5.3.1父向子传递props
5.3.1.1传递常量
<body>
<div id="app">
<introduce :title="msg"></introduce>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//局部组件,只能在当前实例中使用。
const introduce = {
template:"<h1>{{title}}</h1>",
props:['title']
}
//vue实例
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
msg: "hello!"
},
components:{
introduce
}
});
</script>
</body>
5.3.1.2传递数组
<div id="app">
<for-component :items="lessons"></for-component>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//定义局部组件,他是一个对象。
const forComponent = {
template: "<ul><li v-for='item in items'>{{item}}</li></ul>",
//传递的组件中的属性名,使用的时候:属性名=“父类中的变量名”
props:['items']
}
//vue实例
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
msg: "hello!",
lessons:['java','php','ios']
},
components:{
forComponent
}
});
</script>
</body>
5.3.2子向父传递props
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 需要自定义事件-->
<counter :num="count" @incr="add" @decr="reduce"></counter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const counter = {
//在子组件中修改值,当父亲改的时候,会覆盖掉子组件中的值。所以应该在父组件中修改值。
//修改前:template: "<button @click='num++'>点了{{num}}次!</button>",
//修改后,要调用父组件的方法,就是子向父通信。
template: `
<div>
<button @click="handleAdd">+</button>
<button @click="handleReduce">-</button>
<h1>
num:{{num}}
</h1>
</div>
`,
//传递的组件中的属性名,使用的时候:属性名=“父类中的变量名”
props:['num'],
methods: {
//触发事件
handleAdd(){
this.$emit('incr');
},
handleReduce(){
this.$emit('decr')
}
}
}
//vue实例
const app =new Vue({
el:"#app",//element元素,一个标签就是一个元素,即vue所作用的标签
data:{
count:0
},
components:{
counter
},
methods:{
add(){
this.count++;
},
reduce(){
this.count--;
}
}
});
</script>
</body>
</html>