1. Vue概述
前端演变的过程
1.随着JavaScript的诞生,我们可以操作页面的DOM元素及样式,页面有了一些动态的效果,但是依然是以静态
为主。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="js/jquery.min.js"></script>
<script>
function change(){
var str = document.getElementById("testh2").value
if(str === '文字内容'){
document.getElementById("testh2").value = "改变文字内容"
}else{
document.getElementById("testh2").value = "文字内容"
}
}
</script>
</head>
<body>
<input id="testh2" value="文字内容">
<button onclick="change()">点我</button>
</body>
</html>
上图中的代码,通过操作dom元素来改变dom显示的内容
== equality 等同,=== identity 恒等。
==, 两边值类型不同的时候,要先进行类型转换,再比较。
下面分别说明:先说 =,这个比较简单。下面的规则用来判断两个值是否=相等:
1、如果类型不同,就[不相等]
2、如果两个都是数值,并且是同一个值,那么[相等];(!例外)的是,如果其中至少一个是NaN,那么[不相等]。(判断一个值是否是NaN,只能用isNaN()来判断)
3、如果两个都是字符串,每个位置的字符都一样,那么[相等];否则[不相等]。
4、如果两个值都是true,或者都是false,那么[相等]。
5、如果两个值都引用同一个对象或函数,那么[相等];否则[不相等]。
6、如果两个值都是null,或者都是undefined,那么[相等]。
再说 ==,根据以下规则:
1、如果两个值类型相同,进行 === 比较。
2、如果两个值类型不同,他们可能相等。根据下面规则进行类型转换再比较:
a、如果一个是null、一个是undefined,那么[相等]。
b、如果一个是字符串,一个是数值,把字符串转换成数值再进行比较。
c、如果任一值是 true,把它转换成 1 再比较;如果任一值是 false,把它转换成 0 再比较。
d、如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的toString或者valueOf方法。 js核心内置类,会尝试valueOf先于toString;例外的是Date,Date利用的是toString转换。非js核心的对象,令说(比较麻 烦,我也不大懂)
e、任何其他组合,都[不相等]。
= 赋值运算符
== 等于
=== 严格等于
例:var a = 3;
var b = “3”;
ab 返回 true
a=b 返回 false因为a,b的类型不一样===用来进行严格的比较判断
2.开发人员通过ajax与后端交互,然后通过JS操作Dom元素来实现页面动态效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="js/jquery.min.js"></script>
<script>
function change(){
var str = $('#testh2').val()
str==='文字内容'? $('#testh2').val("改变文字内容"):$('#testh2').val("文字内容")
}
</script>
</head>
<body>
<input id="testh2" value="文字内容">
<button onclick="change()">点我</button>
</body>
</html>
通过jquery来操作dom元素,会简单很多。这个阶段ajax流行起来,通过ajax调用后端接口,然后使用jquery刷新dom对象显示。
3.MVVM(Model-View-ViewModel)中的VM要做的事情就是把DOM操作完全封装起来,开发人员不用再关心Model和View之间是如何互相影响
- 只要Model发生了改变,View上自然就会表现出来。
- 当用户修改了View,Model中的数据也会跟着改变。
data : {
mvvm : '文字内容'
},
changeMvvm(){
if(this.mvvm === '文字内容'){
this.mvvm = '改变文字内容'
}else{
this.mvvm = '文字内容'
}
}
不需要操作dom元素,只需要操作数据,框架会自动处理dom元素和数据的绑定关系
也就是不需要关系model(数据)和view(dom元素)的关系
vue介绍
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。
前端框架三巨头:Vue.js、React.js、AngularJS,vue.js以其轻量易用著称,vue.js和React.js发展速度最快。
渐进式:可以选择性的使用该框架的一个或一些组件,这些组件的使用也不需要将框架全部组件都应用;而且
用了这些组件也不要求你的系统全部都使用该框架。
2. 搭建示例工程
目标:使用IDEA创建示例工程并在工程中通过npm安装下载vue.js
分析:
vue是一个前端框架,也是其实是一个js文件;下载vue.js文件并在页面中引入该js文件。
vue.js的下载方式:
-
可以引用在线的vue.js
<!-- 开发环境版本,包含了用帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue"></script>
-
可以离线下载vue.js;
下载地址:https://github.com/vuejs/vue
可以下载2.6.10版本https://github.com/vuejs/vue/archive/v2.6.10.zip 或资料文件夹中也已下载
下载解压,在dist 可得到vue.js文件。<script src="js/vue-2.6.10.js"></script>
-
npm包资源管理器,可以下载vue.js
使用了npm的方式安装vue模块:
#初始化 npm init -y #下载vue模块 npm install vue --save
注意:
刚安装完之后idea需要重启才可以在terminal使用npm命令进行操作。
引入vue:
<script src="node_modules/vue/dist/vue.js">
nodejs和npm安装方式
https://www.jianshu.com/p/96f2f01a4f3e
vue插件安装方式:
https://www.cnblogs.com/wbl001/p/11063613.html
3. 演示双向绑定与事件处理
目标:创建01-demo.html页面并初始化Vue实例,通过console修改Vue数据实现双向绑定效果和创建按钮实现点击即自增
分析:
- 创建页面,初始化vue;
- {{}}获取显示数据;
- v-model实现双向绑定;
- v-on演示事件处理
小结:
<!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="text" v-model="num"><button v-on:click="num++">点我</button>
<h2>{{name}} 非常酷!有{{num}}个学科。</h2>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
name:"黑马",
num: 1
}
});
</script>
</body>
</html>
初始化代码中el里标注vue生效的div
data里包含了要和dom元素进行数据绑定的内容
拓展阅读:
当一个 Vue 实例被创建时,它将 data
对象中的所有的属性加入到 Vue 的响应式系统中。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
// 获得这个实例上的属性
// 返回源数据中对应的字段
vm.a == data.a // => true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2
// ……反之亦然
data.a = 3
vm.a // => 3
当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data
中的属性才是响应式的。也就是说如果你添加一个新的属性,比如:
vm.b = 'hi'
那么对 b
的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个属性,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:
data: {
newTodoText: '',
visitCount: 0,
hideCompletedTodos: false,
todos: [],
error: null
}
除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $
,以便与用户定义的属性区分开来。例如:
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})
vm.$data === data // => true
vm.$el === document.getElementById('example') // => true
// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
// 这个回调将在 `vm.a` 改变后调用
})
4. Vue实例生命周期及钩子函数
目标:了解Vue实例生命周期,生命周期的钩子函数及created函数常用场景
分析:
在创建vue实例的时候可以指定模板id、数据和方法;而如果要在实例化、模板渲染的过程中需要执行一些其它操作的话;那么可以使用钩子函数。
小结:
模板或元素
每个Vue实例都需要关联一段Html模板,Vue会基于此模板进行视图渲染;可以通过el属性来指定。
例如一段html模板:
<div id="app">
</div>
然后创建Vue实例,关联这个div
var vm = new Vue({
el:"#app"
})
数据
当Vue实例被创建时,它会尝试获取在data中定义的所有属性,用于视图的渲染,并且监视data中的属性变化,当
data发生改变,所有相关的视图都将重新渲染,这就是“响应式“系统。
<div id="app">
<input type="text" v-model="name"/>
</div>
var vm = new Vue({
el:"#app",
data:{
name:"黑马"
}
})
- name的变化会影响到input 的值
- input中输入的值,也会导致vm中的name发生改变
方法
var vm = new Vue({
el:"#app",
data:{
},
methods:{
//add:function(){
add(){
console.log("点我了...233")
}
}
})
<div id="app">
<button v-on:click="add">点我</button>
</div>
生命周期钩子
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。通俗说就是 Vue 实例从创建到销毁的过程,就是生命周期。
-
beforecreate :
完成实例初始化,初始化非响应式变量
this指向创建的实例;
可以在这加个loading事件;
data computed watch methods上的方法和数据均不能访问 -
created
实例创建完成
完成数据(data props computed)的初始化 导入依赖项。
可访问data computed watch methods上的方法和数据
未挂载DOM,不能访问 e l , el, el,ref为空数组
可在这结束loading,还做一些初始化,实现函数自执行,
可以对data数据进行操作,可进行一些请求,请求不易过多,避免白屏时间太长。
若在此阶段进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中 -
berofeMount
有了el,编译了template|/outerHTML
能找到对应的template,并编译成render函数 -
mounted
完成创建vm. e l , 和 双 向 绑 定 , 完 成 挂 载 D O M 和 渲 染 ; 可 在 m o u n t e d 钩 子 对 挂 载 的 d o m 进 行 操 作 即 有 了 D O M 且 完 成 了 双 向 绑 定 可 访 问 D O M 节 点 , el,和双向绑定, 完成挂载DOM 和渲染;可在mounted钩子对挂载的dom进行操作 即有了DOM 且完成了双向绑定 可访问DOM节点, el,和双向绑定,完成挂载DOM和渲染;可在mounted钩子对挂载的dom进行操作即有了DOM且完成了双向绑定可访问DOM节点,ref
可在这发起后端请求,拿回数据,配合路由钩子做一些事情;
可对DOM 进行操作 -
beforeUpdate
数据更新之前
可在更新前访问现有的DOM,如手动移除添加的事件监听器; -
updated :
完成虚拟DOM的重新渲染和打补丁;
组件DOM 已完成更新;
可执行依赖的dom 操作
注意:不要在此函数中操作数据,会陷入死循环的。 -
activated:
在使用vue-router时有时需要使用来缓存组件状态,这个时候created钩子就不会被重复调用了,
如果我们的子组件需要在每次加载的时候进行某些操作,可以使用activated钩子触发 -
deactivated
for keep-alive 组件被移除时使用- beforeDestroy:
在执行app.$destroy()之前
可做一些删除提示,如:你确认删除XX吗?
可用于销毁定时器,解绑全局时间 销毁插件对象
- beforeDestroy:
-
destroyed :
当前组件已被删除,销毁监听事件 组件 事件 子实例也被销毁 这时组件已经没有了,你无法操作里面的任何东西了。
**钩子函数不要使用箭头函数的方式编写。**比如
created: () => console.log(this.a)
或vm.$watch('a', newValue => this.myMethod())
。因为箭头函数并没有this
,this
会作为变量一直向上级词法作用域查找,直至找到为止,经常导致Uncaught TypeError: Cannot read property of undefined
或Uncaught TypeError: this.myMethod is not a function
之类的错误。钩子函数中的this就是vue的实例
//钩子函数
created() {
//this表示vue实例
this.msg = "hello vue. created.";
console.log(this);
}
这是es6的写法,如果报错,可以在idea->settings->language & frameworks ->javascript中修改language version为ECMAScript 6
5. 插值、v-text和v-html
目标:插值使用场景和要求;v-text和v-html的作用
小结:
插值可以使用在有需要显示vue实例数据的地方,可以在插值表达式中调用实例的数据属性和函数。
v-text和v-html的作用:可以将数据在模板中进行显示;区别:v-html会对内容中出现的html标签进行渲染,而v-text会将内容当做普遍文本输出到元素里面。
<span v-text="msg"></span><br>
<span v-html="msg"></span><br>
这类dom元素,只能由我们的数据去影响我们dom元素的展示(因为元素本身没有动态操作能力),所以叫单向绑定
<div id="app">
text: <span>{{msg}}</span><br>
v-text:<span v-text="msg"><h2>hello, vue</h2></span><br>
v-html:<span v-html="msg"></span><br>
v-text-h2:<h2 v-text="msg"></h2>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
msg: ""
}
});
</script>
v-text会输出文本格式的
hello, vue
v-html会输出html格式的
hello, vue
拓展:
迄今为止,在我们的模板中,我们一直都只绑定简单的属性键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
6. 指令-v-model使用
目标:使用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">
<input type="checkbox" value="Java" v-model="language">Java<br>
<input type="checkbox" value="Python" v-model="language">Python<br>
<input type="checkbox" value="Swift" v-model="language">Swift<br>
<h2>
你选择了:{{language.join(",")}}
</h2>
<input type="checkbox" value="single" v-model="single">single<br>
<select v-model = "select">
<option value ="volvo">Volvo</option>
<option value ="saab">Saab</option>
<option value="opel">Opel</option>
<option value="audi">Audi</option>
</select>
<textarea v-model="textarea"></textarea>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
language:[],
single:false,
select:'opel',
textarea:'this is a textarea'
}
});
</script>
</body>
</html>
v-model限制在、、中使用
多个 checkbox 对应一个model时,model的类型是一个数组,单个checkbox值是boolean类型,radio对应的值是input的value值
input 和 textarea 默认对应的model是字符串
select 单选对应字符串,多选对应也是数组
安装插件的方式:
https://www.cnblogs.com/wbl001/p/11063613.html
7. 指令-v-on使用
目标:了解v-on指令的语法实现按钮点击后的递增和递减
分析:
在没有使用vue之前;页面标签可以通过设置onXXX响应事件;在vue中可以通过v-on指令响应事件。
小结:
语法:
v-on:事件名="js片段或函数名"
简写语法:
@事件名="js片段或函数名"
v-on的使用:
<div id="app">
<button v-on:click="num++">增加</button>
<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="http://www.itcast.cn" @click.prevent="print('点击了超链接')" >传智播客</a>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
num:1
},
methods: {
//递减
decrement(){
this.num--;
},
//打印
print(str){
console.log(str);
}
}
});
</script>
事件修饰符:语法v-on:xxxx.修饰符,常用的修饰符有:
.stop :阻止事件冒泡
.prevent :阻止默认事件发生
.capture :使用事件捕获模式,.capture在父级上加,先执行父级的函数,再执行子级的触发函数(一般用法)
.self :只有元素自身触发事件才执行。(冒泡或捕获的都不执行)
.once :只执行一次
可以通过@click.stop.prevent进行多个修饰
8. 指令v-for使用
目标:了解v-for指令语法实现对数组、对象的遍历
分析:
实现:可以在vue实例化的时候指定要遍历的数据,然后通过v-for指令在模板中遍历显示数据。一般情况下,要遍历的数据可以通过钩子函数created发送异步请求获取数据。
小结:
可以使用v-for遍历数组、对象:
<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 type="text/javascript">
var app = new Vue({
el:"#app",
data:{
users:[
{"name":"黑马","age":13,"gender":"男"},
{"name":"传智播客","age":13,"gender":"女"},
{"name":"酷丁鱼","age":4,"gender":"男"}
],
person:{"name":"传智汇","age":13,"gender":"男","address":"中国"}
}
});
</script>
如果遍历的时候需要使用到索引号,可以在循环遍历的位置,添加一个参数;该索引号是从0开始的。
遍历数组:
v-for="(item,index) in users"
v-for="item in users"
items:要迭代的数组
item:迭代得到的数组元素别名
index:迭代到的当前元素索引,从0开始。
遍历对象:
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
1个参数时,得到的是对象的值
2个参数时,第一个是值,第二个是键
3个参数时,第三个是索引,从0开始
key:
你需要给Vue一个唯一 key 属性。理想的 key 值是每项都有的且唯一的 id。 也就是key是该项的唯一标识。
<ul>
<li v-for="(item,index) in items" :key="index"></li>
</ul>
这里绑定的key是数组的索引,应该是唯一的
拓展:
类似于 v-if
,你也可以利用带有 v-for
的 <template>
来循环渲染一段包含多个元素的内容。比如:
<ul>
<div v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</div>
</ul>
9. 指令-v-if和v-show使用
目标:说出v-if与v-show的区别;通过一个按钮的点击,实现遍历数组结果的显示存在与否并在遍历过程中使用v-if对数据进行判断处理;实现文本内容的隐藏
分析:
- v-if:通过一个按钮的点击,实现遍历数组结果的显示存在与否并在遍历过程中使用v-if对数据进行判断处理
- v-show:实现文本内容的隐藏
小结:
v-if在条件不满足的时候元素不会存在;v-show条件不满足的时候只是对元素进行隐藏。
v-if:
v-if="布尔表达式"
<h2 v-if="show">
Hello VueJS.
</h2>
data: {
show:true
}
当v-if和v-for出现在一起时,v-for优先级更高。也就是说,会先遍历,再判断条件。
可以使用 v-else 指令来表示 v-if 的“else 块”:
<li v-for="(user,index) in users" v-if="user.gender=='女'" style="backgroundcolor:
deeppink">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
<li v-else style="background-color: blue">
{{index}}--{{user.name}}--{{user.age}}--{{user.gender}}
</li>
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-show:
带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性
display 。
<h1 v-show="ok">Hello!</h1>
v-if vs v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
10. 指令-v-bind使用
目标:了解v-bind语法和作用;实现点击不同按钮切换不同的属性值;使用class属性中的特殊用法实现一个按钮切换背景色
分析:
其中src和height的值如果不想写死,而是想获取vue实例中的数据属性值的话;那可以通过使用v-bind实现:
<img v-bind:src="vue实例中的数据属性名" :height="vue实例中的数据属性名"/>
小结:
可以使用v-bind:
<div id="app">
<button @click="color='red'">红色</button>
<button @click="color='blue'">蓝色</button>
<div :class="color">
点击按钮改变背景颜色
</div>
<hr>
<br>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
color:"red",
bool:true
}
});
</script>
v-bind的作用:可以对所有元素的属性设置vue实例的数据。
v-bind简写方式:直接使用冒号:
上面虽然实现了颜色切换,但是语法却比较啰嗦。
Vue对class属性进行了特殊处理,可以接收数组或对象格式
可以传给 :class 一个对象,以动态地切换 class:
<div :class="{ red: true,blue:false }"></div>
对象中,key是已经定义的class样式的名称,如本例中的: red 和blue
对象中,value是一个布尔值,如果为true,则这个样式会生效,如果为false,则不生效。
<button @click="bool=!bool">点我改变下面色块的颜色</button>
<div :class="{red:bool, blue:!bool}">
点击按钮改变背景颜色
</div>
11. 计算属性的使用
目标:计算属性的应用场景,实现将一个日期时间值转换为yyyy-MM-dd格式字符串
分析:
一个日期的毫秒值要显示为格式化(yyyy-MM-dd)的日期字符串的话;可以使用computed计算属性里面的方法进行处理。
小结:
计算属性的应用:
<div id="app">
<h2>
你的生日是:
{{new Date(birthday).getFullYear()}}-{{new Date(birthday).getMonth()+1}}-{{new Date(birthday).getDay()}}
</h2>
<hr>
<h2>
你的生日是:
{{birth}}
</h2>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
birthday:1429032123201
},
computed:{
birth(){
const date = new Date(this.birthday);
return date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDay();
}
}
});
</script>
computed计算属性的应用场景:可以应用在插值或者指令表示式复杂的时候。可以将一些属性数据经过方法处理之后返回。
12. watch基本和深度监控
目标:watch的使用场景;并使用其监听简单属性值及其对象中属性值的变化
分析:
在vue实例中数据属性;因为在页面中修改而产生了变化;可以通过watch监控获取其改变前后的值。
如果是修改的对象数据属性,可以开启深度监控获取修改后最新的对象数据。如:person.name
小结:
可以如下使用watch进行数据属性的监控:
<div id="app">
<input type="text" v-model="message">
<hr><br>
<input type="text" v-model="person.name"><br>
<input type="text" v-model="person.age"><button @click="person.age++">+</button>
<h2>
姓名为:{{person.name}};年龄为:{{person.age}}
</h2>
</div>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
message:"黑马",
person:{"name":"heima", "age":13}
},
watch:{
message(newValue, oldValue){
console.log("新值:" + newValue + ";旧值:" + oldValue);
},
person: {
//开启深度监控;监控对象中的属性值变化
deep: true,
//可以获取到最新的对象属性数据
handler(obj){
console.log("name = " + obj.name + "; age=" + obj.age);
}
}
}
});
</script>
watch使用场景:当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。可以监控视图中数据的变化而做出响应,;如:下拉框列表中,当如果选择了对于的下拉框选项之后,要根据最新的值去加载一些其它数据的话。
很多情况下,我们需要对对象中的某个字段进行监控
'person.age':function f(newValue, oldValue) { console.log("person.age变更" + newValue + ";旧值:" + oldValue) }
这样的写法没有用es6的语法
13. 组件使用
目标:了解组件的使用场景;定义点击则计数的组件并使用全局注册和局部注册方式
分析:
可以将通用或者公用的页面模块抽取成vue组件,在vue实例中引用。
小结:
在页面中可以如下实验组件:
<div id="app">
<!--使用组件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script type="text/javascript">
//定义组件
const counter = {
template:"<button @click='num++'>你点击了{{num}}次</button>",
data(){
return {num:0}
}
};
//全局注册组件:在所有的vue实例中都可以使用组件
//参数1:组件名称,参数2:具体的组件
//Vue.component("counter", counter);
var app = new Vue({
el:"#app",
components:{
counter: counter
}
});
</script>
一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。
组件使用场景:在项目需要重用某个模块(头部、尾部、新闻。。。)的时候,可以将模块抽取成组件,其它页面中注册组件并引用。
-
全局注册:在任何vue实例中都可以引用,如:一般网站的头部导航菜单
Vue.component("counter", counter);
-
局部注册:可以在有需要的页面引入组件,如:商城网站首页页面中各种活动模块
var app = new Vue({ el:"#app", components:{ counter: counter } });
组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等
不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板
全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
data的定义方式比较特殊,必须是一个函数。
14. 父组件向子组件通信
目标:父组件与子组件通信的意义;实现父组件将简单字符串和对象更新到子组件
小结:
组件通信意义:父子组件之间数据的交换,能够及时更新组件内容。
- 父组件将简单字符串更新传递到子组件
<!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/vue-compo.js"></script>
</head>
<body>
<div id="app">
<!--使用组件-->
<introduce :title="msg" :title2="test"></introduce>
</div>
<script type="text/javascript">
//定义组件
const introduce = {
template:`<div><h2>{{title}}</h2>
<h3>{{title2}}</h3></div>
`,
//定义接收父组件的属性
props:["title","title2"]
};
//全局注册组件:在所有的vue实例中都可以使用组件
//参数1:组件名称,参数2:具体的组件
Vue.component("introduce", introduce);
var app = new Vue({
el:"#app",
data:{
msg:"父组件的msg属性数据内容",
test:"这是第二个属性"
}
});
</script>
</body>
</html>
通过props来定义需要从父组件中接收的属性
- 父组件将数组更新传递到子组件
<!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">
<!--使用组件-->
<my-list :items="lessons" :items2="lessons2"></my-list>
</div>
<script type="text/javascript">
//定义组件
const myList = {
template:`
<div>
<h2>{{items2.id}}--{{items2.name}}</h2>
<ul>
<li v-for="item in items" :key="item.id">{{item.id}}--{{item.name}}</li>
</ul>
</div>
`,
//定义接收父组件的属性
props:{
items:{
//数据类型,如果是数组则是Array,如果是对象则是Object
type:Array,
//默认值
default(){
var data = [{"id":4, "name":"Java"},
{"id":5, "name":"Php"},
{ "id":6, "name":"前端"}]
return data
}
},
items2:{
type:Object,
default:[]
}
}
};
var app = new Vue({
el:"#app",
data:{
msg:"父组件的msg属性数据内容",
lessons:[
{"id":1, "name":"Java"},
{"id":2, "name":"Php"},
{"id":3, "name":"前端"}
],
lessons2:{"id":4, "name":"Java"}
},
components:{
myList
}
});
</script>
</body>
</html>
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
组件名如果有大写比如myList,需要在使用时写成my-list,大写改成小写,中间添加-
type:Array, 限定父组件传递来的必须是数组,否则报错;type的值可以是Array或者Object(传递对象的
时候使用)default:默认值,如果是对象则需要写成方法的方式返回默认值。如:
default(){ return {"xxx":"默认值"}; }
15. 子组件向父组件通信
目标:在子组件中点击对应按钮实现父组件中属性数据的改变
小结:
<div id="app">
<h2>num = {{num}}</h2>
<!--使用组件-->
<counter @plus="numPlus" @reduce="numReduce" :snum="num"></counter>
</div>
<script type="text/javascript">
//定义组件
const counter = {
template:`
<div>
<button @click='incrNum'>+</button>
<button @click='decrNum'>-</button>
</div>
`,
props:["snum"],
methods:{
//递增
incrNum(){
//调用到父组件中的方法
return this.$emit("plus");
},
decrNum(){
//调用到父组件中的方法
return this.$emit("reduce");
}
}
};
//全局注册组件:在所有的vue实例中都可以使用组件
//参数1:组件名称,参数2:具体的组件
//Vue.component("counter", counter);
var app = new Vue({
el:"#app",
components:{
counter: counter
},
data:{
num:0
},
methods:{
numPlus(){
this.num++;
},
numReduce(){
this.num--;
}
}
});
</script>
子组件接收到父组件属性后,默认是不允许修改的。怎么办?
既然只有父组件能修改,那么加和减的操作一定是放在父组件首先在组件中定义方法,
//递增 incrNum(){ //调用到父组件中的方法 return this.$emit("plus"); },
通过$emit调用了父组件的方法
在父组件中,声明自己的方法,这个名字可以随意定
numPlus(){ this.num++; },
通过v-on建立关联:
@plus="numPlus"
@后面跟着的是在子组件中定义的父组件方法的名字,等于后面跟的是父组件中的方法名
拓展slot的用法:
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot>
元素作为承载分发内容的出口。
它允许你像这样合成组件:
<navigation-link url="/profile">
Your Profile
</navigation-link>
然后你在 <navigation-link>
的模板中可能会写为:
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
当组件渲染的时候,<slot></slot>
将会被替换为“Your Profile”。插槽内可以包含任何模板代码,包括 HTML:
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
具名插槽
有时我们需要多个插槽。例如对于一个带有如下模板的 <base-layout>
组件:
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这样的情况,<slot>
元素有一个特殊的特性:name
。这个特性可以用来定义额外的插槽:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一个不带 name
的 <slot>
出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
完整示例代码:
<!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">
<!--使用组件-->
<navigation-url url="http://www.baidu.com">
<h2>Your Profile</h2>
</navigation-url>
<navigation-url2 url="http://www.baidu.com" url2="http://www.jd.com">
<template v-slot:a1>
<h2>slot1</h2>
</template>
<template v-slot:a2>
<h3>slot2</h3>
</template>
</navigation-url2>
</div>
<script type="text/javascript">
//定义组件
const navigationUrl = {
template:`
<a :href="url" class="nav-link">
<slot></slot>
</a>
`,
props:["url"]
};
//定义组件
const navigationUrl2 = {
template:`
<div>
<a :href="url" class="nav-link">
<slot name="a1"></slot>
</a>
<a :href="url2" class="nav-link">
<slot name="a2"></slot>
</a>
</div>
`,
props:["url","url2"]
};
//全局注册组件:在所有的vue实例中都可以使用组件
//参数1:组件名称,参数2:具体的组件
//Vue.component("counter", counter);
var app = new Vue({
el:"#app",
components:{
navigationUrl: navigationUrl,
navigationUrl2:navigationUrl2
},
data:{
num:0
}
});
</script>
</body>
</html>
16. axios概述
目标:axios的用途及了解常见方法
小结:
Vuejs 并没有直接处理ajax的组件,但可以使用axios或vue-resource组件实现对异步请求的操作。
axios的作用:发送异步请求获取数据。常见的方法:get、post;在发送的时候可以指定参数(地址、请求方式和请求头部信息);返回数据结构(data/status/statusText/headers/config)
axios可以使用的方法有:
- axios(config)
- axios.get(url[, config])
- axios.delete(url[, config])
- axios.head(url[, config])
- axios.post(url[, data[, config]])
- axios.put(url[, data[, config]])
- axios.patch(url[, data[, config]])
示例:
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(() => {
//出现异常时的回调
});
在使用 catch 时,或传递 rejection callback 作为 then 的第二个参数时,响应可以通过 error 对象可被使用。
17. axios方法及get、post方法使用
目标:使用axios方法获取数据并在页面中将数据遍历显示;切换改为get/post方法实现数据加载
小结:
可以使用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
// app.users = res.data;
this.users = res.data;
}).catch(err=>alert(err));
axios.get("http://localhost/user/8").then(res=>{
console.log(res.data);
}).catch(err=>alert(err));
/*
axios.get("data.json").then(res=>{
console.log(res);
app.users = res.data;
}).catch(err=>alert(err));
*/
/*
axios({
url:"data.json",
method:"get"
}).then(res=>{
console.log(res);
app.users = res.data;
}).catch(err=>alert(err));
*/
}
});
</script>
</body>
</html>
回调中通过this也能获取到vue对象
可以简化直接通过get post进行调用
跨域:在前端js中如果发送异步请求的话,请求的地址与当前服务器的ip或者端口号不同都是跨域请求,可以使用如下方式在服务器端进行配置:
拓展
跨域,指的是浏览器不能执行其他网站的脚本。
它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。
所谓同源是指,域名,协议,端口均相同,不明白没关系,
举个栗子:
http://www.123.com/index.html 调用
http://www.123.com/server.PHP (非跨域)
http://www.123.com/index.html 调用
http://www.456.com/server.php (主域名不同:123/456,跨域)
http://abc.123.com/index.html 调用
http://def.123.com/server.php(子域名不同:abc/def,跨域)
http://www.123.com:8080/index.html调用
http://www.123.com:8081/server.php(端口不同:8080/8081,跨域)
http://www.123.com/index.html 调用
https://www.123.com/server.php(协议不同:http/https,跨域)
通过注解的方式允许跨域
非常简单,我们可以在Controller类或其方法上加@CrossOrigin注解,来使之支持跨域。
举例:
@CrossOrigin(origins = "*", maxAge=3600)
@RestController@RequestMapping("/User")
public class UserController {
}
其中origins为CrossOrigin的默认参数,即跨域来源,*即任何来源,也可以是其他域名。即可以以以下形式:
@CrossOrigin("http://test.com")
@CrossOrigin(origins="http://test.com",maxAge=3600)
该注解用于方法上,写法相同,处理时,SpringMVC会对类上标签和方法上标签进行合并。
通过配置文件的方式允许跨域
在web.xml中添加如下配置:
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
使用这个Filter即可让整个服务器全局允许跨域。
jsonp
jsonp 全称是JSON with Padding,是为了解决跨域请求资源而产生的解决方案,是一种依靠开发人员创造出的一种非官方跨域数据交互协议。
1.AJAX直接请求普通文件存在跨域无权限访问的问题,不管是静态页面也好.
2.不过我们在调用js文件的时候又不受跨域影响,比如引入jquery框架的,或者是调用相片的时候
3.如果想通过纯web端跨域访问数据只有一种可能,那就是把远程服务器上的数据装进js格式的文件里.
4.而json又是一个轻量级的数据格式,还被js原生支持
5.为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback 参数给服务端,
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'onBack'
}).then((res) => {
console.log(res);
})
nginx进行代理
前端发布在nginx,域名和端口是nginx域名和端口