1.简介
Vue是一套用于构建用户界面的渐进式框架, 发布于2014年2月。与其它大型框架不同的是, Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层, 不仅易于上手, 还便于与第三方库(如:vue-router,vue-resource,vue x) 或既有项目整合。
1.1 vue的实现模式
MVVM模式
- Model:模型层, 在这里表示JavaScript对象
- View:视图层, 在这里表示DOM(HTML操作的元素)
- ViewModel:连接视图和数据的中间件, Vue.js就是MVVM中的View Model层的实现者
在MVVM架构中, 是不允许数据和视图直接通信的, 只能通过ViewModel来通信, 而View Model就是定义了一个Observer观察者
- ViewModel能够观察到数据的变化, 并对视图对应的内容进行更新
- ViewModel能够监听到视图的变化, 并能够通知数据发生改变
至此, 我们就明白了, Vue.js就是一个MV VM的实现者, 他的核心就是实现了DOM监听与数据绑定
1.2 vue的优点
- 轻量级, 体积小是一个重要指标。Vue.js压缩后有只有20多kb(Angular压缩后56kb+,React压缩后44kb+)
- 移动优先。更适合移动端, 比如移动端的Touch事件
- 易上手,学习曲线平稳,文档齐全
- 吸取了Angular(模块化) 和React(虚拟DOM) 的长处, 并拥有自己独特的功能,如:计算属性
- 开源,社区活跃度高
2.HelloVue
首先在idea中下载vue.js插件
注意:Vue不支持IE 8及以下版本, 因为Vue使用了IE 8无法模拟的ECMAScript 5特性。但它支持所有兼容ECMAScript 5的浏览器。
2.1 第一个vue程序
1)下载地址
- 开发版本
- 包含完整的警告和调试模式:https://yuejs.org/js/vue.js
- 删除了警告, 30.96KB min+gzip:https://vuejs.org/js/vue.min.js
- CDN
<script src=“https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js”></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
2) 代码编写
1.创建一个html文件 demo01.html
2.引入 vue.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</body>
</html>
3.创建一个vue实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
//Model : 数据层
var vm = new Vue({
el: "#app",
data:{
message: "hello,vue!"
}
});
</script>
</body>
</html>
说明:
el: 'vue'
:绑定元素的IDdata:{message:'Hello Vue!'}
:数据对象中有一个名为message的属性,并设置了初始值 Hello Vue!
4.将数据绑定页面元素
只需要在绑定的元素中使用双花括号将Vue创建的名为message属性包裹起来, 即可实现数据绑定功能, 也就实现了View Model层所需的效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>狂神说Java</h1>
<!-- view : 视图层 -->
<div id="app">
<h1>{{message}}</h1>
</div>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
//Model : 数据层
var vm = new Vue({
el: "#app",
data:{
message: "hello,vue!"
}
});
</script>
</body>
</html>
5.这时可以直接在页面中修改我们的message属性的值,页面也会随之变化
语句为 vm.message='xxx';
3. Vue的基础语法指令
3.1 v-bind(单向绑定)
我们已经成功创建了第一个Vue应用!看起来这跟渲染一个字符串模板非常类似, 但是Vue在背后做了大量工作。现在数据和DOM已经被建立了关联, 所有东西都是响应式的。我们在控制台操作对象属性,界面可以实时更新!
我们还可以使用v-bind来绑定元素特性!
简写形式: :value="xxx"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>狂神说Java</h1>
<!-- view : 视图层 -->
<div id="app">
<h1>{{message}}</h1>
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
</div>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
//Model : 数据层
var vm = new Vue({
el: "#app",
data:{
message: "hello,vue!"
}
});
</script>
</body>
</html>
你看到的v-bind等被称为指令。指令带有前缀v以表示它们是Vue提供的特殊特性。它们会在渲染的DOM上应用特殊的响应式行为,该指令的意思是:“将这个元素节点的title特性和Vue实例的message属性保持一致”。如果我们在页面中修改了message属性的值,title绑定的message属性的值也会随之改变。
3.2 v-model(双向绑定)
当对data数据进行更改时,View层也会进行改变,也就是说我们的视图页面也会实时更改
<input v-model:value="name" />
v-model只能用在表单类元素中(输入类元素)
也就是说,有一个属性是value的元素
v-model:value='xxx'可以简写成v-model='xxx'
3.3. 事件处理
v-if
v-else
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h1 v-if="ok">yes</h1>
<h2 v-else>no</h2>
</div>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data:{
ok: true
}
});
</script>
</body>
</html>
测试:
1.在浏览器上运行,打开控制台!
2.在控制台输入vm.ok=false
然后回车,你会发现浏览器中显示的内容会直接变成NO
注:使用v-*
属性绑定数据是不需要双花括号
包裹的
v-else-if
- v-if
- v-else-if
- v-else
注:===
三个等号在JS中表示绝对等于(就是数据与类型都要相等) 的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h1 v-if="type === 'A'">A</h1>
<h1 v-else-if="type === 'B'">B</h1>
<h1 v-else-if="type === 'C'">C</h1>
</div>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data:{
type: 'A'
}
});
</script>
</body>
</html>
3.3.1 v-on
v-on
监听事件
emsp;事件有Vue的事件、和前端页面本身的一些事件!我们这里的click
是vue的事件, 可以绑定到Vue中的methods
中的方法事件!
创建一个html页面,当我们打开页面点击对应按钮时,就会调用对应的方法实现点击事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件处理</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<h2>欢迎来到{{name}}学习先进技术</h2>
<button v-on:click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2(66)">点我提示信息2(传参)</button>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'牛客网'
},
methods:{
showInfo1(event){
console.log(event.target.innerText)
alert('同学你好hhe!')
},
showInfo2(number){
console.log(event.target.innerText)
alert('同学' + number +'你好hah!')
}
}
})
</script>
</body>
</html>
v-on:click="xxx"事件我们往往会简写成@click="xxx"形式
3.3.2 事件修饰符
常用的事件修饰符
1.prevent: 阻止默认事件(常用) 2.stop:阻止事件冒泡(常用) 3.once:事件只触发一次(常用) 4.capture: 使用事件的捕获模式 5.self:只有event.target是当前操作的元素是才触发事件 6.passive: 事件的默认行为立即执行,无需等待事件回调执行完毕
使用:(css可以根据自行水平更改 = =)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件修饰符</title>
<style type="text/css">
*{
margin-top: 20px;
}
.demo1{
height: 50px;
background-color: #066aec;
}
.box1{
padding: 5px;
background-color: darkred;
}
.box2{
padding: 5px;
background-color: greenyellow;
}
.list{
height: 200px;
weight: 200px;
background-color: saddlebrown;
overflow: auto;
}
li{
height: 100px;
}
</style>
</head>
<body>
<!--
Vue中的事件修饰符
1.prevent: 阻止默认事件(常用)
2.stop:阻止事件冒泡(常用)
3.once:事件只触发一次(常用)
4.capture: 使用事件的捕获模式
5.self:只有event.target是当前操作的元素是才触发事件
6.passive: 事件的默认行为立即执行,无需等待事件回调执行完毕
-->
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<h1>欢迎来到{{name}}学习</h1>
<!-- 阻止默认事件 (常用) -->
<a href="http://www.baidu.com" @click.prevent="showInfo1">点我跳转</a>
<!-- 阻止事件冒泡 (常用) -->
<div class='demo1' @click="showInfo1">
<button @click.stop="showInfo1">点我提示信息</button>
</div>
<!-- 事件只触发一次 (常用) -->
<div class="demo2">
<button @click.once="showInfo1">点我提示信息</button>
</div>
<!-- 使用事件的捕获模式 -->
<div class="box1" @click.capture="showMessage(1)">
div1
<div class="box2" @click="showMessage(2)">
div2
</div>
</div>
<!-- 只有event.target是当前操作的元素是才触发事件 -->
<div class="demo1" @click.self="showInfo1">
<button @click="showInfo1">点我提示信息</button>
</div>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
<script type="text/javascript">
let vm = new Vue({
el: "#root",
data:{
name: '牛客网'
},
methods: {
showInfo1(e) {
// e.preventDefault()
// e.stopPropagation()
alert('同学你好hhe!')
},
showMessage(msg) {
alert(msg)
},
demo(){
for (let i = 0; i = 1000; i++) {
console.log('@')
}
}
}
});
</script>
</body>
</html>
注意:
1. 当我们使用stop时,会阻止冒泡的发生,那什么是冒泡呢?经过测试,我们去除stop发现alert了两次,
说明我们既执行了button中的click也执行了外层div的click,说明是从内到外执行的
2.当我们使用capture时,会阻止捕获的发生,即当我们会先执行外层再执行内层,例如当我们点击div2时,我们会先输出1,再输出2。但当我们不适用capture时,默认为冒泡执行
3.3.3 键盘事件
Vue中常用的按键别名
回车 => enter 删除 => delete(捕获”删除“和”退格“键) 退出 => esc 空格 => space 换行 => tab(特殊,必须配合keydown使用) 上 => up 下 => down 左 => left 右 => right
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>键盘事件</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<h1>欢迎来到{{name}}学习</h1>
<input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo1">
<!--
1.Vue中常用的按键别名
回车 => enter
删除 => delete(捕获”删除“和”退格“键)
退出 => esc
空格 => space
换行 => tab(特殊,必须配合keydown使用)
上 => up
下 => down
左 => left
右 => right
2.Vue未提供别名的按键,看一看使用按键原始的key值去绑定,但主要要转为kebab-case(短横线命名)
3.系统修饰键(用法特殊): ctrl、alt、shift、meta
(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
(2).配合keydown使用:正常触发事件
4.也可以使用keyCode去指定具体的按键(不推荐)
5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
-->
</div>
<script type="text/javascript">
let vm = new Vue({
el: "#root",
data:{
name: "heima"
},
methods:{
showInfo1(e){
// if(e.key == 'Enter') {
// console.log("您操作了键盘:" + e.target.value);
// }
console.log("您操作了键盘:" + e.target.value);
}
}
})
</script>
</body>
</html>
注意:
1.Vue未提供别名的按键,看一看使用按键原始的key值去绑定,但主要要转为kebab-case(短横线命名) 2.系统修饰键(用法特殊): ctrl、alt、shift、meta (1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发 (2).配合keydown使用:正常触发事件 3.也可以使用keyCode去指定具体的按键(不推荐) 4.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
3.4 条件渲染
3.4.1 v-for
简单形式
v-for = " 元素名 in 数组名"
完整形式
v-for = "(元素名,key的值) in 数组名" :key="index"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表渲染</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<!-- 遍历数组 -->
<h1>人员列表(遍历数组)</h1>
<ul>
<!--<li v-for="person in persons" :key="person.id">
{{person.name}}-{{person.age}}
</li>-->
<li v-for="(person,index) in persons" :key="index">
{{person.name}}-{{person.age}}
</li>
</ul>
<!-- 遍历对象 -->
<h1>汽车信息(遍历对象)</h1>
<ul>
<li v-for="(value,key) of car" :key="key">
{{key}}--{{value}}
</li>
</ul>
<!-- 测试遍历字符串 (用的少)-->
<h1>测试遍历字符串</h1>
<ul>
<li v-for="(value,index) of str" :key="index">
{{value}}--{{index}}
</li>
</ul>
<!-- 测试遍历指定次数 (用的少)-->
<h1>测试遍历指定次数</h1>
<ul>
<li v-for="(number,index) of 5" :key="index">
{{number}}--{{index}}
</li>
</ul>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
persons:[
{id:'001',name:'张三',age:'28'},
{id:'002',name:'李四',age:'30'},
{id:'003',name:'王五',age:'25'},
],
car:{
name:'迈巴赫',
price:'300w',
color:'black'
},
str:'hello'
}
})
</script>
</body>
</html>
当我们在每一个元素后面增加一个input框并且输入内容时使用数组下标作为key时,我们在数组头部添加一个元素,会发现原来input框对应的元素错乱了,因此在某些情况下,我们应该使用数组的唯一标识作为key
注意:
我们也可以使用计算属性,监视属性等改变数组的返回值遍历
3.4.2 v-else 和 v-else-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>条件渲染</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<!-- v-else和v-else-if -->
<div v-if="n === 1">Angluar</div>
<div v-else-if="n === 2">React</div>
<div v-else-if="n === 3">Vue</div>
<div v-else>我不需要条件哦~</div>
<template v-if="n === 1">
<h2>湖南</h2>
<h2>北京</h2>
<h2>广东</h2>
</template>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
name:'王者荣耀',
n:1
}
})
</script>
</body>
</html>
注意:当我们使用v-if时,即使第一条判断后,后面的v-if仍然会判断,但是使用v-else-if时,如果第一条判断语句为true时,else-if就不会执行了
3.4.3 v-show
v-show功能和v-if一样,但v-show性能略强于v-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>条件渲染</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<!-- 使用v-show做条件渲染 -->
<!-- <h2 v-show="false">欢迎来到{{name}}</h2>-->
<!-- <h2 v-show="1 === 1">欢迎来到{{name}}</h2>-->
<h2 v-show="true">n的值为:{{n}}</h2>
<button @click="n++">点我n值+1</button>
<div v-show="n === 1">Angluar</div>
<!-- 使用v-if做条件渲染 -->
<!-- <h2 v-if="true">欢迎来到{{name}}</h2>-->
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
name:'王者荣耀',
n:1
}
})
</script>
</body>
</html>
3.4.4 v-text
类似于插值语法,但不能识别html标签结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue的内置指令</title>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<!--
v-test指令:
1.作用: 向其所在的节点中渲染文本内容
2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会
-->
<div id="root">
<div><h2>你好,{{name}}</h2></div>
<div v-text="name"></div>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
name: '黑麻',
}
})
</script>
</body>
</html>
3.4.5 v-html
类似于插值语法,但可以识别html标签结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue的内置指令</title>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<!--
v-html指令:
1.作用:向指定节点中渲染包含html结构的内容
2.与插值语法的却别:
(1).v-html会替换掉节点中所有的内容,{{xx}}则不会
(2).v-html可以识别html结构
3.严重注意: v-html有安全性问题
(1).再网站上动态渲染任意html是非常危险的,容易导致XSS(跨站脚本攻击)攻击
(2).一定要在可信的内容上使用v-html,不要用在用户提交的内容上
-->
<div id="root">
<div><h2>你好,{{name}}</h2></div>
<div v-html="message"></div>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
message: '<h1>你好啊!</h1>'
}
})
</script>
</body>
</html>
3.4.6 v-cloak
解决网速慢时页面显示出{{xxx}}的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue的内置指令2</title>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<style type="text/css">
</style>
</head>
<body>
<!--
Vue常用指令:
v-cloak指令(没有值):
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
2.使用css配合v-cloak可以解决网速慢时页面显示出{{xxx}}的问题
-->
<div id="root">
<div v-cloak><h2>{{name}}</h2></div>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
name: '黑麻',
}
})
</script>
</body>
</html>
3.4.7 v-once
与事件中的.once不同,v-once是使所在节点仅在初次加载时渲染,后固定为静态内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue的内置指令3</title>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<style type="text/css">
</style>
</head>
<body>
<!--
Vue常用指令:
v-once指令(没有值):
1.v-once所在的节点再初次动态渲染后,就视为静态内容了
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
-->
<div id="root">
<div>
<h2 v-once>初始化的n值是: {{n}}</h2>
<h2>当前的n值是: {{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
n: 1,
}
})
</script>
</body>
</html>
3.4.8 v-pre
没有使用指令语法,没有使用插值语法的节点,会加快编译
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue的内置指令3</title>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<style type="text/css">
</style>
</head>
<body>
<!--
Vue常用指令:
v-pre指令(没有值):
1.跳过其所在节点的编译过程
2.可利用它跳过:没有使用指令语法,没有使用插值语法的节点,会加快编译
-->
<div id="root">
<div>
<h2 v-pre>Vue其实很简单</h2>
<h2>当前的n值是: {{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
n: 1,
}
})
</script>
</body>
</html>
3.4.9 自定义指令
函数式:
使用directives定义自定义指令,function xxx(){}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue的自定义指令(函数式)</title>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<style type="text/css">
</style>
</head>
<body>
<!--
自定义指令:
-->
<div id="root">
<div>
<h2>{{name}}</h2>
<h2>当前的n值是: <span v-text="n"></span></h2>
<h2>放大10倍的n值是: <span v-big="n"></span></h2>
<button @click="n++">点我n+1</button>
</div>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
n: 1,
name:'黑麻'
},
directives:{
//big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时
big(element,binding){
console.log('@@')
element.innerText = binding.value * 10
}
}
})
</script>
</body>
</html>
对象式:
使用directives定义自定义指令,xxx: { 方法(){} }
使用对象式自定义指令时,配置对象中常用的3个问题
(1).bind:指令与元素成功绑定时回调
(2).inserted:指令所在元素被插入页面时调用
(3).update:指令所在模板结构被重新解析时调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue的自定义指令(对象式)</title>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<style type="text/css">
</style>
</head>
<body>
<!--
自定义指令:
语法:
(1).局部指令:
new Vue({ new Vue({
directives:{指令名:配置对象} 或 directives{指令名:回调函数}
}) })
(2).全局指令:
Vue.directive(指令名:配置对象) 或 Vue.directive(指令名:回调函数)
二、配置对象中常用的3个问题
(1).bind:指令与元素成功绑定时回调
(2).inserted:指令所在元素被插入页面时调用
(3).update:指令所在模板结构被重新解析时调用
三、备注
1.指令定义时不加v-,但使用时要加v-
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用驼峰命名
-->
<div id="root">
<div>
<h2>{{name}}</h2>
<h2>当前的n值是: <span v-text="n"></span></h2>
<h2>放大10倍的n值是: <span v-big="n"></span></h2>
<button @click="n++">点我n+1</button>
<hr/>
<input type="text" v-fbind:value="n">
<input type="text" v-big-number:value="n">
</div>
</div>
<script type="text/javascript">
//配置全局指令
/*Vue.directive('big',function (element,binding){
console.log('big',this)//此处的this是window
// console.log('@@')
element.innerText = binding.value * 10
})*/
const vm = new Vue({
el: '#root',
data:{
n: 1,
name:'黑麻'
},
directives:{
//big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时
big(element,binding){
console.log('big',this)//此处的this是window
// console.log('@@')
element.innerText = binding.value * 10
},
fbind:{
bind(element,binding){
//指令与元素成功绑定时(一上来)
console.log('bind被调用')
element.value = binding.value
},
inserted(element,binding){
//指令所在元素被插入页面时
console.log('inserted被调用')
element.focus();
},
update(element,binding){
//指令所在的模板被重新解析时
console.log('update被调用')
element.value = binding.value
}
},
'big-number':{
}
}
})
</script>
</body>
</html>
注意:
1.指令定义时不加v-,但使用时要加v-
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用驼峰命名
4. 表单双向绑定
4.1 什么是双向数据绑定
Vue.js是一个MV VM框架, 即数据双向绑定, 即当数据发生变化的时候, 视图也就发生变化, 当视图发生变化的时候,数据也会跟着同步变化。这也算是Vue.js的精髓之处了。值得注意的是,我们所说的数据双向绑定,一定是对于UI控件来说的非UI控件不会涉及到数据双向绑定。单向数据绑定是使用状态管理工具的前提。如果我们使用vue x那么数据流也是单项的,这时就会和双向数据绑定有冲突。
(1)为什么要实现数据的双向绑定
在Vue.js
中,如果使用vuex
, 实际上数据还是单向的, 之所以说是数据双向绑定,这是用的UI控件来说, 对于我们处理表单, Vue.js
的双向数据绑定用起来就特别舒服了。即两者并不互斥,在全局性数据流使用单项,方便跟踪;局部性数据流使用双向,简单易操作。
4.2 在表单中使用双向数据绑定
你可以用v-model
指令在表单、及元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇, 但v-model
本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
注意:v-model
会忽略所有表单元素的value
、checked
、selected
特性的初始值而总是将Vue
实例的数据作为数据来源。你应该通过JavaScript
在组件的data
选项中声明初始值!
1.单行文本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 单行文本 -->
<input type="text" v-model="message"/>{{message}}
<span>{{selected}}</span>
</div>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data:{
message: "狂神说java"
}
});
</script>
</body>
</html>
2.多行文本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 多行文本 -->
<textarea v-model="message"></textarea>
{{message}}
</div>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data:{
message: "狂神说java"
}
});
</script>
</body>
</html>
3.单选按钮
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 单选框 -->
<input type="radio" name="sex" value="男" v-model="gender"> 男
<input type="radio" name="sex" value="女" v-model="gender"> 女
<p>
选中了谁: {{gender}}
</p>
</div>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data:{
gender: ''
}
});
</script>
</body>
</html>
4.下拉框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 下拉框 -->
下拉框:
<select v-model="selected">
<option value="" disabled>--请选择--</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>{{selected}}</span>
</div>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app",
data:{
selected: ''
}
});
</script>
</body>
</html>
5.单复选框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
单复选框:
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{checked}}</label>
</div>
<script src="../js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
checked:false
}
});
</script>
</body>
</html>
6.多复选框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
多复选框:
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="join" value="Join" v-model="checkedNames">
<label for="join">Jack</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<span>选中的值:{{checkedNames}}</span>
</div>
<script src="../js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
checkedNames:[]
}
});
</script>
</body>
</html>
5. 计算属性
计算属性的重点突出在属性
两个字上(属性是名词),首先它是个属性
其次这个属性有计算
的能力(计算是动词),这里的计算
就是个函数:简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;可以想象为缓存!
5.1.原理:
底层借助了Object.defineProperty方法提供的getter和setter
5.2.执行时机
(1).初次读取fullName时 (2).所依赖的数据发生变化时
5.3.优势:
与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
首先我们以method方法实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>姓名案例-methods实现</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
姓:<input type="text" v-model:value="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名: <span>{{fullName()}}</span>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
firstName:'张',
lastName:'三'
},
methods:{
fullName(){
return this.firstName + "-" + this.lastName;
}
}
});
</script>
</body>
</html>
接下来以计算属性实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>姓名案例-计算属性实现</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
姓:<input type="text" v-model:value="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名: <span>{{fullName}}</span>
</div>
<!--
计算属性:
1.定义:要用的属性不存在,要通过已有属性计算得来
2.原理:底层借助了Object.defineProperty方法提供的getter和setter
3.get函数什么时候执行:
(1).初次读取fullName时
(2).所依赖的数据发生变化时
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
5.备注:
(1).计算属性最终会出现在vm上,直接读取使用即可
(2).如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生变化
-->
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
fullName:{
//get当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
//get什么时候调用?1.初次读取fullName时,2.所依赖的数据发生变化时
get(){
console.log('get被调用了..')
return this.firstName + "-" + this.lastName
},
//set什么时候调用? fullName的值被修改时
set(value){
console.log('set被调用了',value);
const arr = value.split("-");
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
});
</script>
</body>
</html>
注意:methods和computed里的东西不能重名
(1).计算属性最终会出现在vm上,直接读取使用即可
(2).如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生变化
说明:
-
methods:定义方法, 调用方法使用fullName(), 需要带括号
-
computed:定义计算属性, 调用属性使用fullName, 不需要带括号
-
如何在方法中的值发生了变化,则缓存就会刷新!
结论:
调用方法时,每次都需要讲行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这点,计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销;
另:
计算属性可以采用简写形式,但仅当我们只读取时使用
computed:{ //简写,只考虑读取,不考虑修改时才可使用简写形式 fullName:function(){ console.log('get被调用了..') return this.firstName + "-" + this.lastName }
完整形式:
//完整写法 fullName:{ get(){ console.log('get被调用了..') return this.firstName + "-" + this.lastName }, set(value){ console.log('set被调用了',value); const arr = value.split("-"); this.firstName = arr[0] this.lastName = arr[1] } }
6. Watch监视
当我们需要在数据发生改变时,执行一些操作的时候,我们可以使用watch监视进行操作
6.1 普通监视
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>天气案例_监视属性</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
<!--
监视属性watch
1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在才能进行监视!
3.监视的两种写法:
(1). new Vue时传入watch配置
(2). 通过vm.$watch监视
-->
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
isHot:true
},
computed:{
info(){
return this.isHot?'炎热':'凉爽'
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot;
}
},
watch:{
isHot:{
immediate:true, //初始化时调用handler一次
//handler当isHot发生改变时调用
handler(newValue,oldValue){
console.log('isHot被修改了!' + '原始值:' + oldValue + ",新值:" + newValue);
}
}
}
})
</script>
</body>
</html>
说明:1.我们定义了方法,当我们点击事件时,会对isHot进行更改,我们在watch中定义了监视
2.当我们更改了isHot时,会执行watch里的handler方法
6.2 配置监视的属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>天气案例_监视属性02</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
<h2>a的值{{number.a}}</h2>
<button @click="number.a++">点我a的值+1</button>
<h2>a的值{{number.b}}</h2>
<button @click="number.b++">点我b的值+1</button>
</div>
<!--
深度监视
(1).Vue中的watch默认不监测的对象内部值的变化(一层)
(2).配置deep:true可以监测对象内部值的改变(多层)
备注:
(1).Vue自身可以检测对象内部值的改变,但Vue提供给的watch默认不可以!
(2).使用Watch时根据数据的具体结构,决定是否采用深度监视
-->
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
isHot:true,
number:{
a:1,
b:1
}
},
computed:{
info(){
return this.isHot?'炎热':'凉爽'
}
},
methods:{
changeWeather(){
this.isHot = !this.isHot;
}
},
watch:{
isHot:{
immediate:true, //初始化时调用handler一次
//handler当isHot发生改变时调用
handler(newValue,oldValue){
console.log('isHot被修改了!' + '原始值:' + oldValue + ",新值:" + newValue);
}
},
//监视多级结构中某个属性的变化
/*'number.a':{
immediate:true, //初始化时调用handler一次
//handler当isHot发生改变时调用
handler(){
console.log('a被改变了');
}
}*/
number: {
//监视多级结构中所有属性的变化, 深度监视
deep:true,
handler(){
console.log('number的值被修改了!');
}
}
}
})
</script>
</body>
</html>
说明:1.当我们需要在初始化数据时就默认执行一次watch,我们需要配置一个immediate:true
2.当我们监视一个对象时,当对象内部的数据更改时我们的普通监测监测不到数据的变化,这时我们需要使用深度监视,,也就是在对应数据配置中配置deep:true开启深度监视
6.3 简写形式
//简写 vm.$watch('isHot',function (newValue,oldValue){ console.log('isHot修改了',newValue,oldValue) })
6.4 Watch 与 computed
computer和watch之间的区别:
1.computer能完成的功能,watch都可以完成
2.watch能完成的功能,computer不一定可以完成,例如: watch可以进行异步操作
两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或 组件实例对象
2.所有不被Vue所管理的函数(定时器的回调函数,ajax的回调函数,Promise的回调函数等),最好写成箭头函数, 这样this的指向才是vm 或 组件实例对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>姓名案例-watch监视实现</title>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
姓:<input type="text" v-model:value="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名: <span>{{fullName}}</span>
</div>
<!--
computer和watch之间的区别:
1.computer能完成的功能,watch都可以完成
2.watch能完成的功能,computer不一定可以完成,例如: watch可以进行异步操作
两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或 组件实例对象
2.所有不被Vue所管理的函数(定时器的回调函数,ajax的回调函数,Promise的回调函数等),最好写成箭头函数,
这样this的指向才是vm 或 组件实例对象
-->
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data:{
firstName:'张',
lastName:'三',
fullName:'张-三'
},
computed:{
},
watch:{
firstName(newValue){
this.fullName = newValue + '-' +this.lastName;
},
lastName(newValue){
this.fullName = this.firstName + '-' + newValue;
}
}
});
</script>
</body>
</html>
6.5 数据监测总结
Vue监测数据的原理:
1.vue会监测data中所有层次的数据
2.如何监测对象中的数据: 通过setter实现监测,且要在new Vue时就传入要监测的数据.
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下api: Vue.set(target.propertyName/index.value)或 vm.$set(target.propertyName/index.value)
3.如何监测数组中的数据: 通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新
(2).重新解析模板,进而更新页面
4.在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set()或 vm.$set() 特别注意:Vue.set() 和 vm.$set() 不能给vm或vm的根数据对象添加属性!!!
7. 绑定样式
7.1 class样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>绑定样式</title>
<style type="text/css">
.basic{
width: 400px;
height: 100px;
border: 1px solid lightslategrey;
}
.happy{
width: 300px;
height: 80px;
border: 1px solid red;
font-size: 50px;
background-color: cornflowerblue;
}
.sad{
width: 300px;
height: 80px;
border: 1px solid black;
background-color: greenyellow;
}
.normal{
width: 300px;
height: 80px;
border: 1px solid red;
background-color: #303133;
}
.atguigu1{
width: 500px;
height: 120px;
border: 2px blue;
background-color: purple;
}
.atguigu2{
width: 500px;
height: 120px;
border: 2px yellow;
background-color: deeppink;
}
.atguigu3{
width: 500px;
height: 120px;
border: 2px darkred;
background-color: orange;
}
</style>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<!-- 绑定class样式 -- 字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div>
<!-- 绑定class样式 -- 数组写法,适用于:要绑定的样式个数不确定,名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div>
<!-- 绑定class样式 -- 对象写法,适用于:要绑定的样式个数确定,名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div>
</div>
<!--
绑定样式
1:class样式
写法: :class='xxx' xxx可以是字符串、数组、对象
字符串写法适用于:类名不确定,要动态获取
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定是否使用
-->
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
name:"黑麻",
mood:'normal',
classArr:['atguigu1','atguigu2','atguigu3'],
classObj:{
atguigu1:false,
atguigu2:false
}
},
methods:{
changeMood(){
const arr = ['happy','sad','normal']
var i = Math.floor(Math.random() * 3);
this.mood = arr[i];
// this.mood = 'happy'
}
}
})
</script>
</body>
</html>
7.2 style样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>绑定样式</title>
<style type="text/css">
</style>
</head>
<body>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<div class="basic" :style="styleObj">{{name}}</div>
</div>
<!--
绑定样式
2:style样式
:style="{fontSize:xxx}"其中xxx是动态的
:style="[a,b]"其中a、b是样式对象
-->
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
styleObj:{
fontSize: '50px',
color: 'red'
}
}
})
</script>
</body>
</html>
8. 过滤器
对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>过滤器</title>
</head>
<body>
<!--
过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
语法:
1.注册过滤器:Vue.filter(name,callback)或new Vue(filters:{})
2.使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
备注:
1.过滤器也可以接受额外参数,多个过滤器也可以串联
2.并没有改变原本的数据,是产生新的对应的数据
-->
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<div id="root">
<!-- 过滤器 -->
<h2>使用过滤器: {{time | timeFormater}}</h2>
</div>
<script type="text/javascript">
//配置全局过滤器
Vue.filter('timeFormater',function (value){
console.log('执行了过滤器方法...')
return 'hello'
})
const vm = new Vue({
el:'#root',
data:{
time:123456789
},
filters:{
//配置局部过滤器
helloFilter(){
console.log('执行了filter过滤器方法...')
return 'hello'
}
}
})
</script>
</body>
</html>
语法:
1.注册过滤器:Vue.filter(name,callback)或new Vue(filters:{})
2.使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
注意:
1.过滤器也可以接受额外参数,多个过滤器也可以串联
2.并没有改变原本的数据,是产生新的对应的数据
9. Vue的生命周期
1又名:生命周期回调函数,生命周期函数,生命周期钩子
9.1 定义:
Vue在关键时刻帮我们调用的一些特殊名称的函数
生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
生命周期函数的this指向是vm 或 组件实例对象
9.2 解析生命周期
beforeCreate():还未能访问到data数据,method方法,页面还是未传值
created() :能访问到data数据,method方法
beforeMount()未经解析的DOM结构,所有对DOM的操作都无效
mounted():经过编译的DOM,所有对DOM的操作都有效(但尽量避免直接操作DOM)
beforeUpdate():数据是新的,但页面是旧的
updated():此时数据是新的,页面也是新的
beforeDestroy():vm中data.methods等都处于可用状态,但马上要执行销毁程序
destroyed():程序销毁
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue的生命周期2</title>
<!-- 导入 vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
</head>
<body>
<!--
生命周期:
-->
<div id="root">
<h2>当前n值是:{{n}}</h2>
<button @click="add">点我n+1</button>
<button @click="destroyFox">执行销毁程序</button>
<!-- <h2 :style="{opacity: opacity}">欢迎学习</h2>-->
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
// template:`
// <div>
// <h2>当前n值是:{{n}}</h2>
// <button @click="add">点我n+1</button>
// </div>`,
data:{
n:0,
// opacity:1
},
methods:{
add(){
console.log('add被调用')
this.n ++
},
destroyFox(){
console.log('我快要上西天了')
this.$destroy();
}
},
beforeCreate(){
//还未能访问到data数据,method方法
console.log('beforeCreate')
// console.log(this)
// debugger;
},
created(){
//能访问到data数据,method方法
console.log('create')
// console.log(this)
// debugger;
},
beforeMount(){
//未经解析的DOM结构,所有对DOM的操作都无效, 例如控制台: document.querySelector('h2').innerText = '哈哈哈'
console.log('beforeMount')
// console.log(this)
// debugger;
},
mounted(){
//经过编译的DOM,所有对DOM的操作都有效(但尽量避免)
console.log('mounted')
// console.log(this)
// debugger;
},
beforeUpdate(){
//数据是新的,但页面是旧的
console.log('beforeUpdate')
// console.log(this)
// debugger;
},
updated(){
//此时数据是新的,页面也是新的
console.log('updated')
// console.log(this)
// debugger;
},
beforeDestroy(){
//vm中data.methods等都处于可用状态,但马上要执行销毁程序
console.log('beforeDestroy')
this.add()
// console.log(this)
// debugger;
},
destroyed(){
console.log('destroyed')
console.log(this)
}
})
</script>
</body>
</html>
11. Vue实战
使用ElementUI组件库,进行vue实战
11.1 创建工程
注意:命令行都要使用管理员模式运行
1、创建一个名为hello-vue的工程vue init webpack hello-vue
2、安装依赖, 我们需要安装vue-router、element-ui、sass-loader和node-sass四个插件
#进入工程目录
cd hello-vue
#安装vue-routern
npm install vue-router --save-dev
#安装element-ui
npm i element-ui -S
#安装依赖
npm install
# 安装SASS加载器
cnpm install sass-loader node-sass --save-dev
#启功测试
npm run dev
3、Npm命令解释:
npm install moduleName
:安装模块到项目目录下npm install -g moduleName
:-g的意思是将模块安装到全局,具体安装到磁盘哪个位置要看npm config prefix的位置npm install -save moduleName
:–save的意思是将模块安装到项目目录下, 并在package文件的dependencies节点写入依赖,-S为该命令的缩写npm install -save-dev moduleName
:–save-dev的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖,-D为该命令的缩写
11.2 创建登录页面
把没有用的初始化东西删掉!
在源码目录中创建如下结构:
- assets:用于存放资源文件
- components:用于存放Vue功能组件
- views:用于存放Vue视图组件
- router:用于存放vue-router配置
创建首页视图,在views目录下创建一个名为Main.vue的视图组件:
<template>
<div>首页</div>
</template>
<script>
export default {
name:"Main"
}
</script>
<style scoped>
</style>
创建登录页视图在views目录下创建名为Login.vue的视图组件,其中el-*的元素为ElementUI组件;
<template>
<div>
<el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit('loginForm')">登录</el-button>
</el-form-item>
</el-form>
<el-dialog title="温馨提示" :visible.sync="dialogVisible" width="30%" :before-close="handleClose">
<span>请输入账号和密码</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "Login",
data(){
return{
form:{
username:'',
password:''
},
//表单验证,需要在 el-form-item 元素中增加prop属性
rules:{
username:[
{required:true,message:"账号不可为空",trigger:"blur"}
],
password:[
{required:true,message:"密码不可为空",tigger:"blur"}
]
},
//对话框显示和隐藏
dialogVisible:false
}
},
methods:{
onSubmit(formName){
//为表单绑定验证功能
this.$refs[formName].validate((valid)=>{
if(valid){
//使用vue-router路由到指定界面,该方式称为编程式导航
this.$router.push('/main');
}else{
this.dialogVisible=true;
return false;
}
});
}
}
}
</script>
<style lang="scss" scoped>
.login-box{
border:1px solid #DCDFE6;
width: 350px;
margin:180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
.login-title{
text-align:center;
margin: 0 auto 40px auto;
color: #303133;
}
</style>
创建路由,在router目录下创建一个名为index.js
的vue-router路由配置文件
//导入vue
import Vue from 'vue';
import VueRouter from 'vue-router';
//导入组件
import Main from "../views/Main";
import Login from "../views/Login";
//使用
Vue.use(VueRouter);
//导出
export default new VueRouter({
routes: [
{
//登录页
path: '/main',
component: Main
},
//首页
{
path: '/login',
component: Login
},
]
})
APP.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from "./router"
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(router)
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render:h=>h(App)
})
测试:在浏览器打开 http://localhost:8080/#/login
如果出现错误: 可能是因为sass-loader的版本过高导致的编译错误,则需要更换版本去package.json文件里面的 "sass-loader"的版本进行更改,然后重新cnpm install就可以了;
11.3 路由嵌套
嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。
demo
1、 创建用户信息组件,在 views/user 目录下创建一个名为 Profile.vue 的视图组件;
Profile.vue
<template>
<h1>个人信息</h1>
</template>
<script>
export default {
name: "UserProfile"
}
</script>
<style scoped>
</style>
2、在用户列表组件在 views/user 目录下创建一个名为 List.vue 的视图组件;
List.vue
<template>
<h1>用户列表</h1>
</template>
<script>
export default {
name: "UserList"
}
</script>
<style scoped>
</style>
3、 修改首页视图,我们修改 Main.vue 视图组件,此处使用了 ElementUI 布局容器组件,代码如下:
Main.vue
<template>
<div>
<el-container>
<el-aside width="200px">
<el-menu :default-openeds="['1']">
<el-submenu index="1">
<template slot="title"><i class="el-icon-caret-right"></i>用户管理</template>
<el-menu-item-group>
<el-menu-item index="1-1">
<!--插入的地方-->
<router-link to="/user/profile">个人信息</router-link>
</el-menu-item>
<el-menu-item index="1-2">
<!--插入的地方-->
<router-link to="/user/list">用户列表</router-link>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-caret-right"></i>内容管理</template>
<el-menu-item-group>
<el-menu-item index="2-1">分类管理</el-menu-item>
<el-menu-item index="2-2">内容列表</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<!--在这里展示视图-->
<router-view />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped lang="scss">
.el-header {
background-color: #B3C0D1;
color: #333;
line-height: 60px;
}
.el-aside {
color: #333;
}
</style>
4、 配置嵌套路由修改 router 目录下的 index.js 路由配置文件,使用children放入main中写入子模块,代码如下
index.js
//导入vue
import Vue from 'vue';
import VueRouter from 'vue-router';
//导入组件
import Main from "../views/Main";
import Login from "../views/Login";
//导入子模块
import UserList from "../views/user/List";
import UserProfile from "../views/user/Profile";
//使用
Vue.use(VueRouter);
//导出
export default new VueRouter({
routes: [
{
//登录页
path: '/main',
component: Main,
// 写入子模块
children: [
{
path: '/user/profile',
component: UserProfile,
}, {
path: '/user/list',
component: UserList,
},
]
},
//首页
{
path: '/login',
component: Login
},
]
})
11.4 参数传递
用的还是上述例子的代码 修改一些代码 这里不放重复的代码了
第一种取值方式
1、 修改路由配置, 主要是router下的index.js中的 path 属性中增加了 :id 这样的占位符
{
path: '/user/profile/:id',
name:'UserProfile',
component: UserProfile
}
2、传递参数
此时我们在Main.vue中的route-link位置处 to 改为了 :to,是为了将这一属性当成对象使用,注意 router-link 中的 name 属性名称 一定要和 路由中的 name 属性名称 匹配,因为这样 Vue 才能找到对应的路由路径;
<!--name是组件的名字 params是传的参数 如果要传参数的话就需要用v:bind:来绑定-->
<router-link :to="{name:'UserProfile',params:{id:1}}">个人信息</router-link>
12
3、在要展示的组件Profile.vue中接收参数 使用 {{$route.params.id}}来接收
Profile.vue 部分代码
<template>
<!-- 所有的元素必须在根节点下-->
<div>
<h1>个人信息</h1>
{{$route.params.id}}
</div>
</template>
第二种取值方式 使用props 减少耦合
1、修改路由配置 , 主要在router下的index.js中的路由属性中增加了 props: true 属性
{
path: '/user/profile/:id',
name:'UserProfile',
component: UserProfile,
props: true
}
2、传递参数和之前一样 在Main.vue中修改route-link地址
<!--name是组件的名字 params是传的参数 如果要传参数的话就需要用v:bind:来绑定-->
<router-link :to="{name:'UserProfile',params:{id:1}}">个人信息</router-link>
3、在Profile.vue接收参数为目标组件增加 props 属性
Profile.vue
<template>
<div>
个人信息
{{ id }}
</div>
</template>
<script>
export default {
props: ['id'],
name: "UserProfile"
}
</script>
<style scoped>
</style>
11.5 组件重定向
重定向的意思大家都明白,但 Vue 中的重定向是作用在路径不同但组件相同的情况下,比如:
在router下面index.js的配置
{
path: '/main',
name: 'Main',
component: Main
},
{
path: '/goHome',
redirect: '/main'
}
说明:这里定义了两个路径,一个是 /main ,一个是 /goHome,其中 /goHome 重定向到了 /main 路径,由此可以看出重定向不需要定义组件;
使用的话,只需要在Main.vue设置对应路径即可;
<el-menu-item index="1-3">
<router-link to="/goHome">回到首页</router-link>
</el-menu-item>
11.6 路由模式与 404
路由模式有两种
- hash:路径带 # 符号,如 http://localhost/#/login
- history:路径不带 # 符号,如 http://localhost/login
修改路由配置,代码如下:
export default new Router({
mode: 'history',
routes: [
]
});
404 demo
1.创建一个NotFound.vue视图组件
NotFound.vue
<template>
<div>
<h1>404,你的页面走丢了</h1>
</div>
</template>
<script>
export default {
name: "NotFound"
}
</script>
<style scoped>
</style>
2.修改路由配置index.js
import NotFound from '../views/NotFound'
{
path: '*',
component: NotFound
}
11.7 路由钩子与异步请求
beforeRouteEnter:在进入路由前执行
beforeRouteLeave:在离开路由前执行
在Profile.vue中写
export default {
name: "UserProfile",
beforeRouteEnter: (to, from, next) => {
console.log("准备进入个人信息页");
next();
},
beforeRouteLeave: (to, from, next) => {
console.log("准备离开个人信息页");
next();
}
}
参数说明:
to:路由将要跳转的路径信息
from:路径跳转前的路径信息
next:路由的控制参数
next() 跳入下一个页面
next(’/path’) 改变路由的跳转方向,使其跳到另一个路由
next(false) 返回原来的页面
next((vm)=>{}) 仅在 beforeRouteEnter 中可用,vm 是组件实例
11.8 在钩子函数中使用异步请求
1、安装 Axios
cnpm install --save vue-axios
2、main.js引用 Axios
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
3、准备数据 : 只有我们的 static 目录下的文件是可以被访问到的,所以我们就把静态文件放入该目录下。
数据和之前用的json数据一样 需要的去上述axios例子里
// 静态数据存放的位置
static/mock/data.json
4.在 beforeRouteEnter 中进行异步请求
Profile.vue
export default {
//第二种取值方式
// props:['id'],
name: "UserProfile",
//钩子函数 过滤器
beforeRouteEnter: (to, from, next) => {
//加载数据
console.log("进入路由之前")
next(vm => {
//进入路由之前执行getData方法
vm.getData()
});
},
beforeRouteLeave: (to, from, next) => {
console.log("离开路由之前")
next();
},
//axios
methods: {
getData: function () {
this.axios({
method: 'get',
url: 'http://localhost:8080/static/mock/data.json'
}).then(function (response) {
console.log(response)
})
}
}
}