先了解一下前端开发模式的发展。
最初的网页以HTML为主,是纯静态的网页。网页是只读的,信息流只能从服务端到客户端单向流通。开发人员
也只关心页面的样式和内容。
在MVVM之前,开发人员从后端获取需要的数据模型,然后要通过DOM操作Model渲染到View中。而后当用户操作视图,我们还需要通过DOM获取View中的数据,然后同步到Model中。
而MVVM中的VM要做的事情就是把DOM操作完全封装起来,开发人员不用再关心Model和View之间是如何互相影响的:
把开发人员从繁琐的DOM操作中解放出来,把关注点放在如何操作Model上。
而今天要学习的,就是一款MVVM模式的框架:Vue
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
前端框架三巨头:Vue.js、React.js、AngularJS,vue.js以其轻量易用著称,vue.js和React.js发展速度最快。
渐进式:可以选择性的使用该框架的一个或一些组件,这些组件的使用也不需要将框架全部组件都应>用;而且用了这些组件也不要求你的系统全部都使用该框架。
官网:https://cn.vuejs.org/
参考:https://cn.vuejs.org/v2/guide/
Git地址:https://github.com/vuejs
安装vue有三种方式:
下载地址:https://github.com/vuejs/vue
可以下载2.6.10版本https://github.com/vuejs/vue/archive/v2.6.10.zip 或 资料 文件夹中也已下载
下载解压,在 dist 可得到vue.js文件
或者也可以直接使用公共的CDN(内容分发网络)服务:
<!-- 开发环境版本,包含了用帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
或者:
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
使用npm安装前需要安装node.js和配置环境变量
点击下载node安装包:https://kejizhentan.lanzouj.com/iSJqs05hi2cf
安装node:
npm -v
在idea的左下角,有个Terminal按钮,点击打开控制台:
先输入,对项目进行初始化:
npm init -y
这是对项目的基本描述信息。例如名称、版本等,有点类似java中的pom文件
此时,会在项目目录下出现一个package.json文件
一定要先安装node,不然就会提示
安装Vue,输入命令:
# save 的意思是将模块安装到项目目录下,并在package文件的dependencies节点写入依赖
npm install vue --save
如果执行的时候出现错误;那么使用管理员身份进入到项目路径下执行上述命令:
然后就会在项目目录发现一个node_modules目录,并且在下面有一个vue目录
node_modules是通过npm安装的所有模块的默认位置。
此时再查看package.json,会发现有了变化:
会发现,刚刚安装的vue依赖再这里出现了描述信息。是不是跟pom文件很像?
在项目目录新建一个HTML文件 01-demo.html
01-demo.html内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app"><h2>{{name}} 微信公众号!</h2></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {name: "柯基侦探"}
});
</script>
</body>
</html>
打开页面查看效果:
对刚才的案例进行简单修改:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<input type="text" v-model="num">
<h2>
{{name}} 非常酷! 有{{num}}个酷炫学科。
</h2>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {
name: "柯基侦探",
num: 1
}
});
</script>
</body>
</html>
效果如下:
可以观察到,输入框的变化引起了data中的num的变化,同时页面输出也跟着变化。
没有任何dom操作,这就是双向绑定的魅力。
在页面添加一个按钮:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<input type="text" v-model="num"><button v-on:click="num++">点我</button>
<h2>
{{name}} 非常酷! 有{{num}}个酷炫学科。
</h2>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {
name: "柯基侦探",
num: 1
}
});
</script>
</body>
</html>
效果如下:
每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:
var vm = new Vue({
// 选项
})
在构造函数中传入一个对象,并且在对象中声明各种Vue需要的数据和方法,包括:
接下来一 一介绍。
每个Vue实例都需要关联一段Html模板,Vue会基于此模板进行视图渲染;可以通过el属性来指定。
例如一段html模板:
<div id="app">
</div>
然后创建Vue实例,关联这个div
var vm = new Vue({
el:"#app"
})
这样,Vue就可以基于id为 app 的div元素作为模板进行渲染了。在这个div范围以外的部分是无法使用vue特性的。
当Vue实例被创建时,它会尝试获取在data中定义的所有属性,用于视图的渲染,并且监视data中的属性变化,当data发生改变,所有相关的视图都将重新渲染,这就是“响应式“系统。
html:
<div id="app">
<input type="text" v-model="name"/>
</div>
js:
var vm = new Vue({
el:"#app",
data:{
name:"柯基侦探"
}
})
Vue实例中除了可以定义data属性,也可以定义方法,并且在Vue的作用范围内使用。
html:
<div id="app">
<button v-on:click="add">点我</button>
</div>
js:
var vm = new Vue({
el:"#app",
data:{ },
methods:{
add:function(){
console.log("点我了...233")
}
}
})
每个 Vue 实例在被创建时都要经过一系列的初始化过程 :创建实例,装载模板,渲染模板等。Vue为生命周期中的每个状态都设置了钩子函数(监听函数)。每当Vue实例处于不同的生命周期时,对应的函数就会被触发调用。
生命周期:
例如:created代表在vue实例创建后;
可以在Vue中定义一个created函数,代表这个时期的构造函数:
创建示例html页面02-lifecycle.html如下:
<!DOCTYPE html>
<html lang="en">
<head>
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
</head>
<body>
<div id="app"> {{msg}}</div>
<script>
let app = new Vue({
el: "#app", data: {
//初始化为空
msg: ""
},
created() {
//this表示vue实例
this.msg = "hello vue. created";
console.log(this);
}
});
</script>
</body>
</html>
结果如下:
总结: this 就是当前的Vue实例,在Vue对象内部,必须使用 this 才能访问到Vue中定义的data内属性、方法等。
什么是指令?
指令 (Directives) 是带有 v- 前缀的特殊属性。例如在入门案例中的v-model,代表双向绑定。
格式:
{{表达式}}
说明:
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{msg}}
</div>
<script>
let app = new Vue({
el: "#app",
data: {
msg: "hello vue"
}
});
</script>
</body>
</html>
使用{{}}方式在网速较慢时会出现问题。在数据未加载完成时,页面会显示出原始的 {{}} ,加载完毕后才显示正确数据,称为插值闪烁。类似如下的效果(最新vue是几乎没有此问题):
使用v-text和v-html指令来替代 {{}}
说明:
示例: 改造原页面内容为:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
v-text:<span v-text="msg"></span>
<hr>
v-html:<span v-html="msg"></span>
</div>
<script>
let app = new Vue({
el: "#app",
data: {
msg: "hello, vue.
"
}
});
</script>
</body>
</html>
效果:
刚才的v-text和v-html可以看做是单向绑定,数据影响了视图渲染,但是反过来就不行。接下来学习的v-model是双向绑定,视图(View)和模型(Model)之间会互相影响。
既然是双向绑定,一定是在视图中可以修改数据,这样就限定了视图的元素类型。目前v-model的可使用元素有:
基本上除了最后一项,其它都是表单的输入项。
示例:
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="checkbox" value="Java" v-model="language">Java<br>
<input type="checkbox" value="PHP" v-model="language">PHP<br>
<input type="checkbox" value="Swift" v-model="language">Swift<br>
<h2>你选择了:{{language.join(",")}} </h2></div>
<script>
let app = new Vue({
el: "#app",
data: {
language: []
}
});
</script>
</body>
</html>
效果如下:
v-on指令用于给页面元素绑定事件。
语法:
v-on:事件名="js片段或函数名"
简写语法:
@事件名="js片段或函数名"
例如 v-on:click='add'
可以简写为 @click='add'
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--直接写js片段-->
<button @click="num++">增加</button>
<!--使用函数名,该函数必须要在vue实例中定义-->
<button @click="decrement">减少</button>
<h2>num = {{num}} </h2></div>
<script>
let app = new Vue({
el: "#app", data: {
num: 1
},
methods: {
decrement() {
this.num--;
}
}
});
</script>
</body>
</html>
效果:
在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们
可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
.stop
:阻止事件冒泡.prevent
:阻止默认事件发生 .capture
:使用事件捕获模式.self
:只有元素自身触发事件才执行。(冒泡或捕获的都不执行).once
:只执行一次示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--直接写js片段-->
<button @click="num++">增加</button>
<!--使用函数名,该函数必须要在vue实例中定义-->
<button @click="decrement">减少</button>
<h2>num = {{num}} </h2>
<hr>
事件冒泡测试:<br>
<div style="background-color: lightblue;width: 100px;height: 100px" @click="print('你点击了div')">
<button @click.stop="print('点击了button')">点我试试</button>
</div>
<br>阻止默认事件:<br> <a href="https://www.baidu.com" @click.prevent="print('点击超链接')">百度</a>
</div>
<script> let app = new Vue({
el: "#app", data: {num: 1}, methods: {
decrement() {
this.num--;
}, print(msg) {
console.log(msg)
}
}
}); </script>
</body>
</html>
效果如下:
遍历数据渲染页面是非常常用的需求,Vue中通过v-for指令来实现。
语法:
v-for="item in items"
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<ul>
<li v-for="user in users">{{user.name}}--{{user.age}}--{{user.gender}}</li>
</ul>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app", //el即element,要渲染的页面元素
data: {
users: [
{"name": "柯基侦探", "age": 8, "gender": "男"},
{"name": "kjzt", "age": 12, "gender": "女"},
{
"name": "酷丁鱼",
"age": 4,
"gender": "男"
}
]
}
});
</script>
</body>
</html>
在遍历的过程中,如果需要知道数组角标,可以指定第二个参数:
语法
v-for="(item,index) in items"
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<ul>
<li v-for="(user,index) in users">{{index}}、{{user.name}}--{{user.age}}--{{user.gender}}</li>
</ul>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app", //el即element,要渲染的页面元素
data: {
users: [
{"name": "柯基侦探", "age": 8, "gender": "男"},
{"name": "kjzt", "age": 12, "gender": "女"},
{
"name": "酷丁鱼",
"age": 4,
"gender": "男"
}
]
}
});
</script>
</body>
</html>
效果如下:
v-for除了可以迭代数组,也可以迭代对象。语法基本类似
语法:
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<ul>
<li v-for="(user, index) in users" :key="index"> {{index}}--{{user.name}}--{{user.age}}--{{user.gender}}</li>
</ul>
<hr>
<ul>
<li v-for="(value,key,index) in person"> {{index}}--{{key}}--{{value}}</li>
</ul>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {
users: [
{"name": "柯基侦探", "age": 8, "gender": "男"},
{"name": "微信公众号", "age": 12, "gender": "女"},
{"name": "酷丁鱼", "age": 4, "gender": "男"}
],
person: {"name": "kejizhentan", "age": 3, "address": "中国"}
}
});
</script>
</body>
</html>
效果:
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。
如果使用key这个功能可以有效的提高渲染的效率;key一般使用在遍历完后,又增、减集合元素的时候更有意义。
但是要实现这个功能,你需要给Vue一些提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有的且唯一的 id。 也就是key是该项的唯一标识。
示例:
<ul>
<li v-for="(item,index) in items" :key="index"></li>
</ul>
v-if,顾名思义,条件判断。当得到结果为true时,所在的元素才会被渲染。
语法:
v-if="布尔表达式"
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<button @click="show = !show">点我</button>
<h2 v-if="show"> Hello VueJS. </h2></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {
show: true
}
});
</script>
</body>
</html>
效果:
当v-if和v-for出现在一起时,v-for优先级更高。也就是说,会先遍历,再判断条件。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<button @click="show = !show">点我</button>
<h2 v-if="show"> Hello VueJS. </h2>
<hr>
<ul>
<li v-for="(user,index) in users" v-if="user.gender=='女'">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
</ul>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {show: true,
users: [
{"name": "柯基", "age": 8, "gender": "男"},
{"name": "柯基侦探", "age": 12, "gender": "女"},
{"name": "酷丁鱼","age": 4,"gender": "男"},
{"name": "柯基侦探微信公众号", "age": 2, "gender": "女"}
]
}
});
</script>
</body>
</html>
效果:
可以使用 v-else 指令来表示 v-if 的“else 块”:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<button @click="show = !show">点我</button>
<h2 v-if="show"> Hello VueJS. </h2>
<hr>
<ul v-if="show">
<li v-for="(user,index) in users" v-if="user.gender=='女'" style="background-color: deeppink">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
<li v-else style="background-color: blue"> {{index}}--{{user.name}}--{{user.age}}--{{user.gender}}</li>
</ul>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {
show: true,
users: [
{"name": "柯基", "age": 8, "gender": "男"},
{"name": "柯基侦探", "age": 12, "gender": "女"},
{"name": "酷丁鱼","age": 4, "gender": "男"},
{"name": "柯基侦探微信公众号", "age": 2, "gender": "女"}
]
}
});
</script>
</body>
</html>
v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
v-else-if ,顾名思义,充当 v-if 的“else-if 块”,可以连续使用:
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
类似于 v-else , v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。
另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:
Hello!
不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。 v-show 只是简单地切换元素的 CSS 属性display 。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<button @click="show = !show">点我</button>
<h2 v-if="show"> Hello VueJS. </h2>
<hr>
<ul v-if="show">
<li v-for="(user,index) in users" v-if="user.gender=='女'" style="background-color: deeppink">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
<li v-else style="background-color: blue"> {{index}}--{{user.name}}--{{user.age}}--{{user.gender}}</li>
</ul>
<hr>
<h2 v-show="show"> 你好;柯基侦探! </h2></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {
show: true,
users: [
{"name": "柯基侦探", "age": 8, "gender": "男"},
{"name": "柯基", "age": 12, "gender": "女"},
{"name": "酷丁鱼","age": 4,"gender": "男"},
{"name": "柯基侦探微信公众号", "age": 2, "gender": "女"}
]
}
});
</script>
</body>
</html>
效果如下:
看这样一个案例;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<style type="text/css"> div {
width: 100px;
height: 100px;
color: white;
}
.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 class=""> 点击按钮改变背景颜色。</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
color: "red"
}
});
</script>
</body>
</html>
解读:
该如何实现?
大家可能会这么想,既然color值会动态变化为不同的class名称,那么我们直接把color注入到class属性就好了,于是就这样写:
<div class="{{color}}"></div>
这样写是错误的!因为插值表达式不能用在标签的属性中。\
此时,Vue提供了一个新的指令来解决:v-bind,语法:
v-bind:属性名="Vue中的变量"
例如,在这里我们可以写成:
<div v-bind:class="color"></div>
不过,v-bind太麻烦,因此可以省略,直接写成: :属性名=‘属性值’ ,即:
<div :class="color"></div> 1
修改后的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<style type="text/css"> div {
width: 100px;
height: 100px;
color: white;
}
.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 v-bind:class="color"> 点击按钮改变背景颜色。</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
color: "red"
}
});
</script>
</body>
</html>
效果如下:
上面虽然实现了颜色切换,但是语法却比较啰嗦。
Vue对class属性进行了特殊处理,可以接收数组或对象格式
对象语法
可以传给 :class
一个对象,以动态地切换 class:
<div :class="{ red: true,blue:false }"></div>
之前的案例可以改写成这样:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<style type="text/css"> div {
width: 100px;
height: 100px;
color: white;
}
.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 :class="color"> 点击按钮改变背景颜色。</div>
<hr>
<br>
<button @click="bool=!bool">点我改变下面色块颜色</button>
<div :class="{red:bool,blue:!bool}"> 点击按钮改变背景颜色。</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
color: "red",
bool: true
}
});
</script>
</body>
</html>
效果:
在插值表达式中使用js表达式是非常方便的,而且也经常被用到。
但是如果表达式的内容很长,就会显得不够优雅,而且后期维护起来也不方便,例如下面的场景,有一个日期的数据,但是是毫秒值:
data:{
birthday:1429032123201 // 毫秒值
}
在页面渲染,希望得到yyyy-MM-dd的样式则需要如下处理:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app"><h2>
你的生日为:{{new Date(birthday).getFullYear()}}-{{new Date(birthday).getMonth()+1}}-{{new Date(birthday).getDay()}} </h2>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {
birthday:1429032123201
}
});
</script>
</body>
</html>
效果如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app"><h2>你的生日为:{{new Date(birthday).getFullYear()}}-{{new Date(birthday).getMonth()+1}}-{{new
Date(birthday).getDay()}} </h2>
<hr>
<h2>computed计算方式;你的生日为:{{birth}} </h2></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {
birthday: 1429032123201},
computed: {
birth() {
const date = new Date(this.birthday);
return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDay();
}
}
});
</script>
</body>
</html>
计算属性本质就是方法,但是一定要返回数据。然后页面渲染时,可以把这个方法当成一个变量来使用。
效果:
watch可以让我们监控一个值的变化。从而做出相应的反应。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app"><input v-model="message"></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {
message: "hello vue"
},
watch: {
message(newValue, oldValue) {
console.log("新值:" + newValue + ";旧值:" + oldValue);
}
}
});
</script>
</body>
</html>
效果:
如果监控的是一个对象,需要进行深度监控,才能监控到对象中属性的变化,例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app"><input v-model="message"/>
<hr>
<br>
<input v-model="person.name"><br>
<input v-model="person.age">
<button @click="person.age++">+</button>
<h2>姓名为:{{person.name}};年龄为:{{person.age}} </h2></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script> var app = new Vue({
el: "#app",//el即element,要渲染的页面元素
data: {
message: "hello vue",
person: {
"name": "柯基侦探",
"age": 12
}
},
watch: {
message(newValue, oldValue) {
console.log("新值:" + newValue + ";旧值:" + oldValue);
}, person: {
//开启深度监控,可以监控到对象属性值的变化
deep: true,
//监控的处理方法
handler(obj) {
console.log("name = " + obj.name + ", age=" + obj.age);
}
}
}
});
</script>
</body>
</html>
变化:
效果:
在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。
但是如果每个页面都独自开发,这无疑增加了开发的成本。所以会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。
通过Vue的component方法来定义一个全局组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<!--使用定义好的全局组件-->
<counter></counter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//定义组件
const counter = {
template: "",
data() {
return {num: 0}
}
};
//全局注册组件;参数1:组件名称,参数2:组件
Vue.component("counter", counter);
var app = new Vue({
el: "#app"
});
</script>
</body>
</html>
效果:
定义好的组件,可以任意复用多次:
<div id="app">
<!--使用定义好的全局组件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<!--使用定义好的全局组件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//定义组件
const counter = {
template: "",
data() {
return {num: 0}
}
};
//全局注册组件;参数1:组件名称,参数2:组件
Vue.component("counter", counter);
var app = new Vue({
el: "#app"
});
</script>
</body>
</html>
效果如下:
组件的data属性必须是函数!
当定义这个 组件时,它的data 并不是像这样直接提供一个对象:
data: {
num: 0
}
取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
data: function () {
return {
num: 0
}
}
如果 Vue 没有这条规则,点击一个按钮就会影响到其它所有实例!
一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着Vue的加载而加载。
因此,对于一些并不频繁使用的组件,会采用局部注册。
先在外部定义一个对象,结构与创建组件时传递的第二个参数一致:
然后在Vue中使用它:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<!--使用定义好的全局组件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//定义组件
const counter = {
template: "",
data() {
return {num: 0}
}
};
//全局注册组件;参数1:组件名称,参数2:组件
// Vue.component("counter", counter);
var app = new Vue({
el: "#app",
//局部注册组件
components: {
counter: counter
}
});
</script>
</body>
</html>
效果和全局注册一样:
各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。
比如有一个子组件:
Vue.component("introduce",{
// 直接使用props接收到的属性来渲染页面
template:'{{title}}
',
props:[title] // 通过props来接收一个父组件传递的属性
})
父组件使用子组件,同时传递title属性:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app">
<!--使用定义好的全局组件-->
<introduce :title="msg"></introduce>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//定义组件
const introduce = {
//使用props属性title的值渲染模版
template: "{{title}}
",
//定义接收来自父组件的属性
props: ["title"]
};
//全局注册组件;参数1:组件名称,参数2:组件
Vue.component("introduce", introduce);
var app = new Vue({
el: "#app",
data: {
msg: "父组件中的msg属性的内容"
}
});
</script>
</body>
</html>
定义一个子组件:
const myList = {
template:'\
\
- {{item.id}} : {{item.name}}
\
\
',
props:{ // 通过props来接收父组件传递来的属性
items:{// 这里定义items属性
type:Array,// 要求必须是Array类型
default:[] // 如果父组件没有传,那么给定默认值是[]
}
}
}
页面内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app"><h2>柯基侦探听过的技术有:</h2>
<!-- 接受来自父组件的属性值,使用v-bind指向父组件的属性lessons;注意使用my-list -->
<my-list :items="lessons"></my-list>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//定义组件
const myList = {
//可以使用双引号、单引号或者如下使用的 ` 飘号
template: ` <ul>
<li v-for="item in items" :key="item.id">{{item.id}}--{{item.name}}</li>
</ul> `,
//定义接收来自父组件的属性
props: { //定义模版中使用的属性
items: { //必须为数组类型
type: Array, //默认为空数组
default: []
}
}
};
var app = new Vue({
el: "#app",
data: {
msg: "父组件中的msg属性的内容",
lessons: [
{"id": 1, "name": "Java"},
{"id": 2, "name": "PHP"},
{"id": 3, "name": "前端"}
]
},
//注册组件
components: {
//如果组件key和value一致可以简写如下
myList
}
});
</script>
</body>
</html>
来看这样的一个案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app"><h2>num = {{num}}</h2> <!--使用定义好的全局组件-->
<counter :snum="num"></counter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//定义组件
const counter = {
//组件只能是一个元素里面包裹其他元素;如下面,一个div包含两个按钮
template: ` <div><button @click="snum++">+</button> <button @click="snum--">-</button> </div> `,
props: ["snum"]
};
//全局注册组件;参数1:组件名称,参数2:组件
Vue.component("counter", counter);
var app = new Vue({
el: "#app",
data: {num: 0}
});
</script>
</body>
</html>
尝试运行,好像没问题,点击按钮试试:
子组件接收到父组件属性后,默认是不允许修改的。怎么办?
既然只有父组件能修改,那么加和减的操作一定是放在父组件:
var app = new Vue({
el:"#app",
data:{
num:0
},
methods:{
//父组件中定义操作num的方法
numPlus(){
this.num++;
},
numReduce(){
this.num--;
}
}
});
但是,点击按钮是在子组件中,那就是说需要子组件来调用父组件的函数,怎么做?
可以通过v-on指令将父组件的函数绑定到子组件上:
<div id="app">
<h2>num = {{num}}</h2>
<!--使用定义好的全局组件-->
<counter @plus="numPlus" @reduce="numReduce" :snum="num"></counter>
</div>
然后,当子组件中按钮被点击时,调用绑定的函数:
//定义组件
const counter = {
//组件只能是一个元素里面包裹其他元素;如下面,一个div包含两个按钮
template: `
<div>
<button @click="incrNum">+</button>
<button @click="decrNum">-</button>
</div>
`,
props:["snum"],
methods: {
//点击模板中使用的方法
incrNum(){
return this.$emit("plus");
},
decrNum(){
return this.$emit("reduce");
}
}
};
完成页面如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title></head>
<body>
<div id="app"><h2>num = {{num}}</h2>
<!--使用定义好的全局组件-->
<counter @plus="numPlus" @reduce="numReduce" :snum="num"></counter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//定义组件
const counter = {
//组件只能是一个元素里面包裹其他元素;如下面,一个div包含两个按钮
template: ` <div><button @click="incrNum">+</button> <button @click="decrNum">-</button> </div> `,
props: ["snum"],
methods: {
//点击模板中使用的方法
incrNum() {
return this.$emit("plus");
}, decrNum() {
return this.$emit("reduce");
}
}
};
//全局注册组件;参数1:组件名称,参数2:组件
Vue.component("counter", counter);
var app = new Vue({
el: "#app", data: {num: 0}, methods: {
//父组件中定义操作num的方法
numPlus() {
this.num++;
}, numReduce() {
this.num--;
}
}
});
</script>
</body>
</html>
效果如下:
子组件不能直接修改父组件传递参数的引用或者基本类型参数值。
Vuejs 并没有直接处理ajax的组件,但可以使用axios或vue-resource组件实现对异步请求的操作。
vue-resource是Vue.js的插件提供了使用XMLHttpRequest或JSONP进行Web请求和处理响应的服务。 当vue更新 到2.0之后,作者就宣告不再对vue-resource更新,而是推荐axios。
vue-resource的github地址: https://github.com/pagekit/vue-resource
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
axios的github地址:https://github.com/axios/axios
# 如果使用npm则可以如下安装
npm install axios
或者也可以直接使用公共的CDN(内容分发网络)服务:
<!-- 开发环境版本,包含了用帮助的命令行警告 -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
axios可以使用的方法有:
这些是创建请求时可以用的配置选项。只有 url 是必需的。如果没有指定 method ,请求将默认使用 get 方法。
{
// `url` 是用于请求的服务器 URL
url: '/user',
// `method` 是创建请求时使用的方法
method: 'get', // 默认是 get
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// `transformRequest` 允许在向服务器发送前,修改请求数据
// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
transformRequest: [function (data) {
// 对 data 进行任意转换处理
return data;
}],
// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
transformResponse: [function (data) {
// 对 data 进行任意转换处理
return data;
}],
// `headers` 是即将被发送的自定义请求头
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/json'
},
// `params` 是即将与请求一起发送的 URL 参数
// 必须是一个无格式对象(plain object)或 URLSearchParams 对象
params: {
ID: 12345
},
// `data` 是作为请求主体被发送的数据
// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
// 在没有设置 `transformRequest` 时,必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream
data: {
firstName: 'Fred'
},
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // 默认的
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // 默认的
// `maxContentLength` 定义允许的响应内容的最大尺寸
maxContentLength: 2000,
// `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否 则,promise 将被 rejecte
validateStatus: function (status) {
return status >= 200 && status < 300; // 默认的
},
// `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
// 如果设置为0,将不会 follow 任何重定向
maxRedirects: 5 // 默认的
}
{
// `data` 由服务器提供的响应
data: {},
// `status` 来自服务器响应的 HTTP 状态码
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 服务器响应的头
headers: {},
// `config` 是为请求提供的配置信息
config: {}
}
使用 then 时,你将接收下面这样的响应:
axios.get('/user/12345')
.then(function(response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
在使用 catch 时,或传递 rejection callback 作为 then 的第二个参数时,响应可以通过 error 对象可被使用。
注意:如果使用axios访问跨域数据的时候,只需要在服务提供方中,在方法上面使用SpringMVC的跨域注解即可解决数据跨域问题。如在方法上添加:
@CrossOrigin(origins = "http://localhost:10000")
如果请求的地址是使用了网关,那么在网关服务器上配置跨域就可以了;不能同时在网关服务器和服务提供服务工程中同时配置。
可以通过向 axios 传递相关配置来创建请求
步骤:
引入新的vue.js文件
<script src="js/vue-2.6.10.js"></script>
<script src="js/axios.min.js"></script>
创建一个json文件,放入以下数据
[
{"name":"柯基侦探","age":13,"gender":"男"},
{"name":"柯基","age":13,"gender":"女"},
{"name":"酷丁鱼","age":4,"gender":"男"}
]
访问json中的数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="js/axios.min.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="(user, index) in users" :key="index">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
</ul>
</div>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
users: []
},
created() {
//初始化加载数据
axios.post("data.json").then(res => {
console.log(res);
//将数据赋值到vue实例中的数据属性users;
//不能使用this,在axios回调函数中表示窗口,不是vue实例
app.users = res.data;
}).catch(err => alert(err));
}
});
</script>
</body>
</html>
要使用ip+端口的方式访问,否则会报错
将上述示例中的axios操作部分修改为如下:
axios.get("data.json")
.then( res => {
console.log(res);
//将获取数据设置到users属性
app.users = res.data;
}).catch(error =>{
console.log(error)
});
将示例中的axios操作部分修改为如下:
axios.post("data.json")
.then( res => {
console.log(res);
//将获取数据设置到users属性
app.users = res.data;
}).catch(error =>{
console.log(error)
});
}
get请求和post请求类似,以get请求为例访问数据:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private Integer age;
private String gender;
}
HelloController.java
@RestController
public class HelloController {
@GetMapping("/user/8")
public User getUsers(){
return new User("zhangsan",12,"男");
}
}
VueProjectApplication.java
@SpringBootApplication
public class VueProjectApplication {
public static void main(String[] args) {
SpringApplication.run(VueProjectApplication.class, args);
}
}
可以使用axios获取对应服务器数据;如果不是同一个服务的数据则可能会出现跨域请求;需要在响应的服务器上配置跨域。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuejs测试</title>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="js/axios.min.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="(user, index) in users" :key="index">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
</ul>
</div>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
users: []
},
created() {
//初始化加载数据
// axios.post("data.json").then(res => {
// console.log(res);
// //将数据赋值到vue实例中的数据属性users;
// //不能使用this,在axios回调函数中表示窗口,不是vue实例
// app.users = res.data;
// }).catch(err => alert(err));
axios.get("http://localhost:8080/user/8").then(res => {
console.log(res.data);
}).catch(err => alert(err));
/*
axios.get("data.json").then(res=>{
console.log(res);
//将数据赋值到vue实例中的数据属性users;
//不能使用this,在axios回调函数中表示窗口,不是vue实例
app.users = res.data;
}).catch(err=>alert(err));
*/
/*
axios({
url:"data.json",
method:"get"
}).then(res=>{
console.log(res);
//将数据赋值到vue实例中的数据属性users;
//不能使用this,在axios回调函数中表示窗口,不是vue实例
app.users = res.data;
}).catch(err=>alert(err));
*/
}
});
</script>
</body>
</html>
跨域:在前端js中如果发送异步请求的话,请求的地址与当前服务器的ip或者端口号不同都是跨域请求,可以使用如下方式在服务器端进行配置:
可以做以下配置:
添加完@CrossOrigin(origins = "*")
后在访问: