Vue基础
第一章:认识Vue
1.1-Vue概述
Vue是什么
:Vue是一个渐进式的JavaScript框架。
渐进式开发
:声明式渲染→组件系统→客户端路由→集中状态管理→项目构建
- 声明式渲染:Vue最简单的用法。
- 组件系统:通用的开发方式。
- 客户端路由:单页面应用开发。
- 集中状态管理:业务较为复杂的项目开发。
- 项目构建:前端项目独立开发、测试、部署上线。
Vue的优点
:
- 易用:有HTML、CSS、JavaScript基础就可以快速上手Vue。
- 灵活:在一个库和一套完整框架之间伸缩自如。
- 高效:20kb运行大小,超快虚拟DOM。
Vue官网
:https://cn.vuejs.org
1.2-快速入门
代码-HelloWorld
【入门步骤】
- 页面定义一个呈现数据的容器(如div)
- 引入vue.js库文件
- 创建Vue对象
- 在Vue中定义数据并绑定到div中
<!-- 页面定义一个呈现数据的标签(如div) -->
<div id="app">
{{msg}}
</div>
<!-- 引入vue.js库文件 -->
<script src="lib/vue.js"></script>
<script>
// 创建Vue对象
new Vue({
el:"#app", // 呈现数据的容器
data:{ // 数据
msg:"Hello World!"
}
});
</script>
入门分析
下载vue库文件,具体安装参照官网指导:https://cn.vuejs.org/v2/guide/installation.html
Vue对象参数实例属性分析:
- el,元素的挂载位置(和Vue对象操作关联的元素),值是css选择器标识。
- data,模型数据,值是一个对象。
插值表达式:{{msg}}
- 将数据填充到HTML标签中。
- 插值表达式支持基本的运算操作(如:
{{10 + 2}}
呈现结果12)。
Vue代码运行原力分析:归根结底,vue源代码通过Vue.js编译成了原生js代码执行。
第二章:模板语法
前端开发,归根结底就是讲数据填充到页面标签中,我们称之为”前端渲染“。
早期,我们可以通过以下下方式实现前端渲染:
- 字符串拼接
- 缺点:代码风格无法统一,难以维护。大量字符串拼接降低了程序的执行效率。
- 模板引擎(如:art-template)
- 优点:语法风格统一,易于维护。
- 缺点:没有提供事件机制。
Vue模板语法,解决了上述缺点。
2.1-模板语法概览
- 插值表达式
- 指令
- 事件绑定
- 属性绑定
- 样式绑定
- 分支、循环结构
2.2-插值表达式
格式:{{表达式}}
,支持基本的运算操作。
代码:
<div>
{{msg}}
</div>
问题:若仅仅使用插值表达式,则页面在渲染期间会出现"闪动"问题。
解决:可以通过指令方式解决。
2.3-指令
2.3.1 指令概述
指令本质上就是HTML标签中的自定义属性。
格式:以v-开始(如v-cloak)。
2.3.2 v-cloak指令
作用,可以解决插值表达式存在的“闪动”问题
使用方式如下:
<!--定义样式-->
<style>
[v-cloak] {
display: none;
}
</style>
<!--在标签上使用v-cloak指令-->
<div id="app" v-cloak>
{{msg}}
</div>
<!--js-->
<script src="lib/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
msg:"Hello World!"
}
});
</script>
原理分析:先隐藏元素,当在内存中完成插值替换时,则再显示元素。
2.3.3 数据绑定指令
v-text指令
作用:向元素中填充纯文本。相比插值表达式更加简洁且没有“闪动”问题。
v-html指令
作用:向元素中填充HTML片段。在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击(跨站脚本攻击)。只在可信内容上使用 v-html
,永不用在用户提交的内容上。
v-pre指令
作用:显示原始信息,跳过编译过程。
代码
<div id="app">
<!-- 插值表达式 -->
<p>{{msg}}</p>
<!-- v-text指令 -->
<p v-text="msg"></p>
<!-- v-html指令 -->
<p v-html="msg"></p>
<!-- v-pre指令 -->
<p v-pre>{{msg}}</p>
</div>
<script src="lib/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
msg:"<h2>Hello World</h2>",
}
});
</script>
执行结果:
<h2>Hello World</h2>
<h2>Hello World</h2>
Hello World
{{msg}}
2.3.4 数据响应式指令
什么是数据响应式?
Vue中数据的变化会驱动页面内容同步变化。
代码:
<div id="app">
<p v-text="msg"></p>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:"Hello World",
}
});
// 开启定时器,2秒后更改vue中的msg数据
setTimeout(()=>{vm.msg="123"},2000);
</script>
在浏览器中打开执行程序,2秒后可以发现页面数据自动发送改变。
v-once指令
对于含有v-once指令的元素,只监听一次,当数据再次发生改变时,该元素内容不会发生改变。
应用场景:对于固定的数据所绑定的元素只监听一次,后续不再监听,可以提高程序性能。
<div id="app">
<p v-text="msg"></p>
<p v-text="msg" v-once></p>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:"Hello World",
}
});
// 开启定时器,2秒后更改vue中的msg数据
setTimeout(()=>{vm.msg="123"},2000);
</script>
执行结果:带有v-once的元素内容没有发生改变
123
Hello World
2.3.5 双向数据绑定
什么是双向数据绑定?
用户通过更改界面内容,可以使Vue中的数据模型同步变化。
Vue中的数据模型发生改变,界面内容同步变化。
MVVM,MVVM是Model-View-ViewModel的简写
- M, Model数据模型
- V, View视图
- VM,视图模型(控制器)
视图和数据模型无法直接交互,需要通过视图模型控制交互过程。
v-model指令
<div id="app">
<p>{{msg}}</p>
<input type="text" v-model="msg">
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:"Hello World",
}
});
// 开启定时器,2秒后更改vue中的msg数据
setTimeout(()=>{vm.msg="123"},2000);
</script>
执行结果:
在浏览器中打开并执行程序,2秒后,页面中的p元素内容和文本框内容发送改变。
用户在文本框中输入内容,vue中的数据模型msg同步发生改变。
2.3.6 事件绑定指令
基本语法
指令:v-on:事件名="事件处理程序"
简写:@事件名=事件处理程序
事件名:原生的事件名称一致。如:click点击事件
事件处理程序:可以是处理逻辑、函数名或函数调用。
- 对应简单的业务逻辑可以直接抒写代码
- 对于复杂的业务逻辑需要函数封装,在vue中定义methods,在methods中定义事件处理程序。
- 函数名,如handler
- 函数调用,如handler(),适合传参时使用。
- 在事件处理程序中,
this关键字
表示vue实例
代码演示
点击按钮,数字加1
<div id="app">
<div v-text="num"></div>
<button v-on:click="num++">按钮1</button>
<button @click="num++">按钮2</button>
<button @click="handler">按钮3</button>
<button @click="handler()">按钮4</button>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
num:1
},
methods: {
handler(){
this.num++
}
},
});
</script>
事件传参
<button @click="handler">按钮</button>
若事件处理程序是函数名,则事件函数的第一个参数是事件对象。
<button @click="handler(2,$event)">按钮</button>
若事件处理程序是函数调用,一般用于传参。需要注意的是,若需要传入事件对象时,最后一个实参必须传入$event
<div id="app">
<button @click="handler1">按钮1</button>
<button @click="handler2(10,$event)">按钮2</button>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
methods: {
handler1(event){
console.log(event)
},
handler2(num,event){
console.log(num,event)
}
},
});
</script>
事件修饰符
.stop
- 调用event.stopPropagation()
。.prevent
- 调用event.preventDefault()
。
其他事件修饰符操作官网api
<div id="app">
{{num}}
<div @click="handler1">
<a href="javascript:" @click="handler2">点击1</a>
<!-- 停止冒泡 -->
<a href="javascript:" @click.stop="handler2">点击2</a>
</div>
<br>
<a href="http://www.baidu.com" @click="handler2">点击3</a>
<!-- 阻止默认行为 -->
<a href="http://www.baidu.com" @click.prevent="handler2">点击4</a>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
num:1
},
methods: {
handler1(){
console.log("点击了div")
this.num++
},
handler2(){
console.log("点击了a")
}
},
});
</script>
按键修饰符
为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
在密码框中,按回车键提交。
在用户名文本框中,按delete键清空用户名。
<div id="app">
用户名:<input type="text" v-model="uname" @keyup.delete="clearContent"><br>
密码:<input type="password" v-model="pwd" @keyup.enter="submit"><br>
<button @click="submit">提交</button>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
uname:'',
pwd:''
},
methods: {
submit(){
console.log(this.uname,this.pwd)
},
clearContent(){
this.uname = ''
}
},
});
</script>
自定义按键修饰符
- 语法1:
Vue.config.keyCodes.自定义修饰符名称 = keycode
- 语法2:
@keyup.keycode = "事件处理程序"
<div id="app">
<input type="text" @keyup.65="fn1"><br>
<input type="text" @keyup.up="fn2"><br>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.config.keyCodes.up=38;
var vm = new Vue({
el:"#app",
methods: {
fn1(){
console.log("按下了a键");
},
fn2(){
console.log("按下↑up键");
},
},
});
</script>
案例:简单计算器
需求:
分析:(先设计,再编码)
- 根据界面需求,需要定义三个数据,第一个数字、第二数字通过指令v-model绑定给文本框、两个数字的结果通过指令v-text绑定给strong 。
- 根据界面需求:需要定义一个事件处理函数,并同过@click指令绑定给计算按钮
代码:
<div id="app">
<h2>简单计算器(计算任意两个数字的和)</h2>
<input type="text" placeholder="第一个数字" v-model="num1"><br>
<input type="text" placeholder="第二个数字" v-model="num2"><br>
<button @click="handler">计算</button><br>
结果:<strong v-text="result"></strong>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
num1:'',
num2:'',
result:''
},
methods: {
handler(){
this.result = Number(this.num1) + Number(this.num2);
}
},
});
</script>
2.3.7 属性绑定指令
指令
指令:v-bind:属性名="值"
简写::属性名="值"
代码
<div id="app">
<a :href="url">跳转</a><br>
<a v-bind:href="url">跳转</a><br>
<button @click="handler">切换</button>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
url:"http://www.baidu.com"
},
methods: {
handler(){
this.url = "http://www.jd.com"
}
},
});
</script>
2.3.8 v-model底层实现方式
通过v-bind指令绑定文本框的 value属性,并同步数据。
通过v-on:input事件监听文本框中值的边框,并同步数据。
<div id="app">
<div>{{msg}}</div>
<!-- 通过属性绑定和事件实现双向数据绑定 -->
<input type="text" :value="msg" @input="msg=$event.target.value">
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:""
},
methods: {
handler(){
this.url = "http://www.jd.com"
}
},
});
</script>
2.3.9 样式绑定
class属性
方式1:值是对象,v-bind:class="{类名:布尔值,类名:布尔值}"
- 类名,定义的样式集
- 布尔值,若是true时,则该类名有效,否则无效
方式2:值是数组,v-bind:class="[类名数据标识1,类名数据标识2]"
- 类名数据标识,表示在vue中定义的数据
<style>
.active {
width: 100px;
height: 100px;
border:5px solid blue;
}
.error{
background-color: red;
}
</style>
<div id="app">
<!-- 值是对象 -->
<div :class="{active:isActive,error:isError}">测试样式属性1</div>
<div :class="objClass">测试样式属性2</div>
<!-- 值是数组 -->
<div :class="[activeClass,errorClass]">测试样式属性3</div>
<div :class="arrClass">测试样式属性4</div>
<!-- 数组对象混合 -->
<div :class="[activeClass,{error:isError}]">测试样式属性5</div>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
isActive:true,
isError:false,
activeClass:"active",
errorClass:"error",
objClass:{error:true,active:true},
arrClass:["active","error"]
}
});
</script>
style属性
方式1:值是对象时,v-bind:style="{样式属性名:值,样式属性名:值}"
方式2:值是数组时,v-bind:style="[样式1对象,样式2对象]"
<div id="app">
<!-- 值是对象时 -->
<div :style="{width:widthVal,height:heightVal,border:borderVal}">测试样式1</div>
<div :style="ObjStyle">测试样式2</div>
<!-- 值是数组时 -->
<div :style="[baseStyle,overStyle]">测试样式3</div>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
widthVal: "100px",
heightVal: "100px",
borderVal: "1px solid red",
ObjStyle: { width: "100px", height: "100px", background: "red" },
baseStyle: { width: "50px", height: "50px", background: "green" },
overStyle: { width: "150px", height: "150px", background: "green" }
}
});
</script>
2.4-分支循环结构
2.4.1 分支结构
指令
- v-if
- v-else-if
- v-else
- v-show
代码
<div id="app">
<div v-if="score>=90">优秀</div>
<div v-else-if="score>=80">良好</div>
<div v-else-if="score>=60">一般</div>
<div v-else>不及格</div>
<div v-show="flag">是否显示</div>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
score:69,
flag:false
}
});
</script>
v-if和v-show的区别
- v-if控制元素是否渲染到页面
- v-show控制元素是否显示(已经渲染到页面)
2.4.2 循环结构
指令
v-for
遍历数组
v-for="item in 数组"
或 v-for="(item,index) in 数组"
- item表示元素
- index表示索引
:key="item.id"
,key指令用来辅助vue区分不同的元素,提供程序性能。
<div id="app">
<h2>水果列表1</h2>
<ul>
<li :key="index" v-for="(item,index) in fruits" v-text="item+'---' + index"></li>
</ul>
<h2>水果列表2</h2>
<ul>
<li v-for="(item, index) in fruitsList" :key="item.id">
第{{item.id}}个水果
<ul>
<li v-text="'中文名称:'+item.cname "></li>
<li v-text="'英文名称:'+item.ename "></li>
</ul>
</li>
</ul>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
fruits:["apple","orange","banana"],
fruitsList:[
{id:1,ename:"apple",cname:"苹果"},
{id:2,ename:"banana",cname:"香蕉"},
{id:4,ename:"orange",cname:"橘子"},
]
}
});
</script>
遍历对象
方式:v-for="(value,key,index) in 对象"
- value 表示属性值
- key表示属性名
- index表示索引
v-for也可结合v-if使用,标识是否渲染显示某个对象属性。
<div id="app">
<ul>
<li v-for="(value,key, index) in person">
{{index}}----{{key}}---{{value}}
</li>
</ul>
<hr>
<ul>
<li v-if="value==12" v-for="(value,key, index) in person">
{{index}}----{{key}}---{{value}}
</li>
</ul>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
person:{
id:"1001",
name:"张三",
age:12,
gender:"男"
}
}
});
</script>
2.5-案例-tab切换
需求
点击按钮切换显示项
静态页面
样式
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
img {
vertical-align: middle;
}
.clearfix::after{
content: "";
display: block;
clear: both;
visibility: hidden;
height: 0;
line-height: 0;
}
.tab {
width: 500px;
margin:0 auto;
}
ul li {
border:1px solid black;
width: 100px;
text-align: center;
padding: 10px;
float: left;
}
/*【按钮项加active类名,则突出高亮显示】*/
ul li.active {
background: gold;
}
.items .item {
display: none;
}
/*【显示项加active类名,则显示,否则隐藏】*/
.items .item.active {
display: block;
}
结构
<div id="app" class="app">
<div class="tab">
<ul class="clearfix">
<li class="active">李小龙</li>
<li>成龙</li>
<li>李云龙</li>
</ul>
<div class="items">
<div class="item active">
<img src="./img/01.jpg" alt="">
</div>
<div class="item">
<img src="./img/02.jpg" alt="">
</div>
<div class="item">
<img src="./img/03.jpg" alt="">
</div>
</div>
</div>
</div>
分析(先设计)
- 根据页面结构,需要抽取tab项的数据并定义在vue中。
- 通过指令控制类名active在tab按钮和tab显示项上是否有效。
- 通过指令给tab按钮绑定事件,实现当前点击按钮显示,对应的tab展示项显示。
代码(再编码)
<div id="app" class="app">
<div class="tab">
<!--tab按钮-->
<ul class="clearfix">
<li :class="tabCurrentIndex==index?'active':''" v-for="(item, index) in tabList" :key="index" v-text="item.title" @click="handle(index)"></li>
</ul>
<!--tab显示项-->
<div class="items">
<div class="item" :class="tabCurrentIndex==index?'active':''" v-for="(item, index) in tabList" :key="index">
<img :src="item.img" alt="">
</div>
</div>
</div>
</div>
<script src="lib/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
// tab切换显示当前项标识
tabCurrentIndex:0,
// tab切换相关数据
tabList:[
{title:"李小龙",img:"img/01.jpg"},
{title:"成龙",img:"img/02.jpg"},
{title:"李云龙",img:"img/03.jpg"},
]
},
methods: {
handle(index){
this.tabCurrentIndex=index;
}
},
});
</script>
总结
声明式编程:模板结构和最终效果显示基本一致
第三章:Vue常用特性
3.1-常用特性概览
- 表单操作
- 自定义指令
- 计算属性
- 过滤器
- 侦听器
- 生命周期
3.2-表单操作
常用表单类型
- text 文本框
- textarea 文本域
- checkbox 多选框
- radio 单选框
- select 下拉框
- password 密码框
案例操作常用的表单项
<div id="app">
<form action="http://baidu.cn">
<div>
<span>姓名:</span>
<span>
<input type="text" v-model="uname" >
</span>
</div>
<div>
<span>性别:</span>
<span>
<input type="radio" id="male" value="boy" name="sex" v-model="sex">
<label for="male">男</label>
<input type="radio" id="female" value="girl" name="sex" v-model="sex" >
<label for="female">女</label>
</span>
</div>
<div>
<span>爱好:</span>
<input type="checkbox" id="ball" value="1" v-model="hobby" >
<label for="ball">篮球</label>
<input type="checkbox" id="sing" value="2" v-model="hobby" >
<label for="sing">唱歌</label>
<input type="checkbox" id="code" value="3" v-model="hobby" >
<label for="code">写代码</label>
</div>
<div>
<span>职业:</span>
<select multiple v-model="jobs">
<option value="1">教师</option>
<option value="2">软件工程师</option>
<option value="3">律师</option>
</select>
</div>
<div>
<span>个人简介:</span>
<textarea v-model='desc'></textarea>
</div>
<div>
<input type="submit" value="提交" @click.prevent="handle">
</div>
</form>
</div>
<script src="lib/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
uname:"张三",
sex:"boy",
hobby:[1,2],
jobs:[1,3],
desc:"我是一名老师,也是一名律师"
},
methods: {
handle(){
console.log(this.uname,this.sex,this.hobby,this.jobs,this.desc)
}
},
});
</script>
表单域修饰符
- v-model.number 转换为数值
- v-model.trim 去除两边空格
- v-model.lazy 将input事件切换为change事件
<div id="app">
<h2>v-model.number</h2>
<div>
<input type="text" v-model.number="num">
<button @click="handle1">点击增加</button>
</div>
<h2>v-model.trim</h2>
<input type="text" v-model.trim="desc">
<span v-text="desc.length"></span>
<h2>v-model.lazy</h2>
<input type="text" v-model.lazy="uname">
<span v-text="uname" :style="{color:'red'}"></span>
</div>
<script src="lib/vue.js"></script>
<script>
new Vue({
el: "#app",
data:{
num:'',
desc:'',
uname:''
},
methods: {
handle1(){
this.num = this.num + 10;
}
},
});
</script>
3.3-自定义指令
3.3.1-全局指令
定义自定义指令
Vue.directive("指令名称",{
inserted:function(el,binding){
// el 表示指令绑定的元素
// binding.value 表示获取指令传入的参数值
}
})
定义指令时,指令名称,不加v-
使用自定义指令
若没参数:v-指令名称
若有参数:v-指令名称="数据标识"
<div id="app">
<p>
账号:<input type="text" v-focus>
</p>
<p>
密码:<input type="text" v-color="colorVal">
</p>
</div>
<script src="lib/vue.js"></script>
<script>
// 定义自定义指令-获取焦点
Vue.directive("focus",{
inserted:function(el){
el.focus();
}
})
// 定义自定义指令-设置背景色
Vue.directive("color",{
inserted:function(el,binding){
el.style.backgroundColor = binding.value;
}
})
new Vue({
el: "#app",
data:{
colorVal:"red"
},
});
</script>
3.3.2-局部指令
定义局部指令
如果想注册局部指令,组件中也接受一个 directives
的选项:
directives: {
指令名称: {
// 指令的定义
inserted: function (el,binding) {
// el 表示指令绑定的元素
// binding.value 表示获取指令传入的参数值
}
}
}
使用局部指令
若没参数:v-指令名称
若有参数:v-指令名称="数据标识"
<div id="app">
<p>
账号:<input type="text" v-focus>
</p>
<p>
密码:<input type="text" v-color="colorVal">
</p>
</div>
<script src="lib/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
colorVal: "red"
},
// 局部定义自定义指令
directives: {
// 定义自定义指令-获取焦点
focus: {
inserted: function (el) {
el.focus();
}
},
// 定义自定义指令-设置背景色
color: {
inserted: function (el, binding) {
el.style.backgroundColor = binding.value;
}
}
}
});
</script>
3.4-计算属性
3.4.1-为什么需要计算属性
表达式的计算逻辑可能比较复杂,使用计算属性可以使模板内容更加简洁。
3.4.2-计算属性用法
定义方式
computed: {
函数名(){
// 处理逻辑
}
}
代码
需求:反转输出内容
<div id="app">
<div>{{msg}}</div>
<hr>
<!-- 不使用计算属性 -->
<div>{{msg.split('').reverse().join('')}}</div>
<hr>
<!-- 使用计算属性 -->
<div>{{reverseVal}}</div>
</div>
<script src="lib/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
msg:"Hello"
},
// 定义计算属性
computed: {
reverseVal(){
return this.msg.split('').reverse().join('')
}
},
})
</script>
3.4.3-计算属性和方法的区别
在computed和methods中定义函数的区别
- 计算属性computed是基于数据依赖进行缓存的。
- 方法methods不存在缓存。
<div id="app">
<div>{{reverseVal}}</div>
<div>{{reverseVal}}</div>
<div>{{reverseMsg()}}</div>
<div>{{reverseMsg()}}</div>
</div>
<script src="lib/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
msg:"Hello"
},
computed: {
reverseVal(){
console.log("computed")
return this.msg.split('').reverse().join('')
}
},
methods: {
reverseMsg(){
console.log("methods")
return this.msg.split('').reverse().join('')
}
},
})
</script>
执行结果
computed
methods
methods
3.5-侦听器
侦听器的引用场景
数据变化时执行异步或开销大的操作。
侦听器的用法
watch:{
数据名称:function(val){
// val 表示数据变化后的值
}
}
简单使用
<div id="app">
<p>
名:<input type="text" v-model="lastName">
</p>
<p>
姓:<input type="text" v-model="firstName">
</p>
<p>
全名:<span v-text="fullName"></span>
</p>
</div>
<script src="lib/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
firstName:'',
lastName:'',
fullName:''
},
watch: {
// 侦听数据firstName
firstName:function(val){
this.fullName = this.lastName + '.' + val;
},
// 侦听数据lastName
lastName:function(val){
this.fullName = val+ '.' + this.firstName;
}
},
})
</script>
模拟检测用户名是否存在
<div id="app">
<p>
用户名:<input type="text" v-model.lazy="uname">
<span v-text="tip"></span>
</p>
</div>
<script src="lib/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
uname: '',
tip:''
},
methods: {
checkUname() {
setTimeout(function () {
if (this.uname == "admin") {
this.tip = "用户名已存在"
} else {
this.tip = "该用户名可以使用"
}
}.bind(this), 1000)
}
},
watch: {
uname: function () {
// 发送请求
this.checkUname();
}
},
})
</script>
3.6-过滤器
定义过滤器
全局过滤器
Vue.filter("过滤器名称",function(value,arg1,arg2...){
// 业务逻辑
// return 结果
});
// value 表示过滤的值
// arg1、arg2...等表示过滤器传入的参数
局部过滤器
// 在vue组件中定义
// 局部过滤器
filters:{
过滤器名称:function(value,arg1,arg2...){
// 业务逻辑
// return 结果
}
}
// value 表示过滤的值
// arg1、arg2...等表示过滤器传入的参数
代码
<div id="app">
<!-- 插值表达式使用过滤器 -->
<div>{{msg|upper|pack}}</div>
<!-- 属性操作使用过滤器 -->
<div :data="msg|upper">测试属性</div>
<!-- 反转 -->
<div>{{msg|reverse}}</div>
<!-- 数值加12 -->
<div>{{num|add(12)}}</div>
</div>
<script src="lib/vue.js"></script>
<script>
// 创建全局过滤器-upper实现首字母转换为大写
Vue.filter("upper",function(val){
return val.charAt(0).toUpperCase() + val.slice(1);
});
// 创建全局过滤器-pack实现书名号包裹值《Hello》
Vue.filter("pack",function(val){
return "《"+val+"》";
})
// 创建全局过滤器-add实现对数值的增加指定的值
Vue.filter("add",function(val,num){
return val+=num;
});
var vm = new Vue({
el:"#app",
data:{
msg:"hello",
num:10
},
// 局部过滤器
filters:{
reverse:function(val){
return val.split("").reverse().join("");
}
}
})
</script>
3.7-Vue的生命周期
-
beforeCreate,在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
-
created,在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,
$el
属性目前尚不可用。 -
beforeMount,在挂载开始之前被调用:相关的
render
函数首次被调用。该钩子在服务器端渲染期间不被调用。
-
mounted,实例被挂载后调用,这时
el
被新创建的vm.$el
替换了。 如果根实例挂载到了一个文档内的元素上,当mounted
被调用时vm.$el
也在文档内。 -
beforeUpdate,数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
-
updated,由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
-
beforeDestroy,实例销毁之前调用。在这一步,实例仍然完全可用。
-
destroyed,实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
<div id="app">
<div>{{msg}}</div>
<button @click='update'>更新</button>
<button @click='destroy'>销毁</button>
</div>
<script type="text/javascript" src="lib/vue.js"></script>
<script type="text/javascript">
/*
Vue实例的生命周期
*/
var vm = new Vue({
el: '#app',
data: {
msg: '生命周期'
},
methods: {
update: function () {
this.msg = 'hello';
},
destroy: function () {
this.$destroy();
}
},
beforeCreate: function () {
console.log('beforeCreate');
},
created: function () {
console.log('created');
},
beforeMount: function () {
console.log('beforeMount');
},
mounted: function () {
console.log('mounted');
},
beforeUpdate: function () {
console.log('beforeUpdate');
},
updated: function () {
console.log('updated');
},
beforeDestroy: function () {
console.log('beforeDestroy');
},
destroyed: function () {
console.log('destroyed');
}
});
</script>
第四章:Vue组件
4.1-概览
- 组件化开发思想
- 组件注册
- Vue调试工具用法
- 组件间数据交互
- 组件插槽
4.2-组件化开发思想
生活中的组件化
组件化就好像我们的 PC 组装机一样,整个机器(应用)由不同的部件组成,例如显示器、主板、内存、显卡、硬盘等等。自己组装的 PC 有这么几个好处:
- 在保持硬件的兼容性前提下,随意更换每一个部件,都不会影响整个机器的运行
- 当机器出现了问题的时候,我们可以通过插拔法快速定位硬件错误
- 假如 PC 跑不起四路泰坦的仙剑6,我们可以单独更换显卡或者升级内存
标准
、分治
、重用
、组合
。
前端领域的组件化
假如把以上特点对应到前端领域中,组件化开发有如下的好处:
- 降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快速完成需求,例如输入框,可以替换为日历、时间、范围等组件作具体的实现。
- 调试方便,由于整个系统是通过组件组合起来的,在出现问题的时候,可以用排除法直接移除组件,或者根据报错的组件快速定位问题,之所以能够快速定位,是因为每个组件之间低耦合,职责单一,所以逻辑会比分析整个系统要简单。
- 提高可维护性,由于每个组件的职责单一,并且组件在系统中是被复用的,所以对代码进行优化可获得系统的整体升级。例如某个组件负责处理异步请求,与业务无关,我们添加缓存机制,序列化兼容,编码修正等功能,一来整个系统中的每个使用到这个组件的模块都会受惠;二来可以使这个组件更具健壮性。
在团队开发中,组件化带来的优势是便于协同开发,由于代码中的耦合度降低了,每个模块都可以分拆为一个组件,例如异步请求组件,路由组件,各个视图组件。团队中每个人发挥所长维护各自组件,对整个应用来说是精细的打磨。
组件化规范:Web Components
为什么要规范化?
- 我们希望尽可能多的重用代码
- 自定义组件方式不太容易(html、css、js)
- 多次使用组件可能会造成冲突
Web Components通过创建封装好功能的定制元素解决上述问题。
而Vue部分实现了上述规范。
4.3-组件的注册
注册组件
// 全局注册
Vue.componet("组件名称",{
data:function(){
return 组件的数据。
},
template:"组件的模板内容"
});
// 局部注册
new Vue({
componets:{
"组件的名称":{
data:function(){
return 组件的数据。
},
template:"组件的模板内容"
}
}
})
注意事项
- data必须是一个函数(闭包)。
- 组件模板内容必须是单个根元素。
- 组件模板内容可以使用模板字符串(ES6语法)
- 组件的名称
- 短横线方式:
button-counter
- 驼峰式:
buttonCounter
- 在普通模板中无法使用,但在自定义注册组件中可以使用。
- 短横线方式:
- 局部组件只能在父组件中使用,其他组件中无法使用。
代码演示
点击按钮数字累计加2
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<login-input></login-input>
</div>
<script src="lib/vue.js"></script>
<script>
// 全局注册组件
Vue.component("button-counter",{
data:function(){
return {
count:0
}
},
template:'<button v-text="count" @click="handle"></button>',
methods: {
handle(){
this.count+=2;
}
},
})
// 创建vue实例
var vm = new Vue({
el:"#app",
data:{},
components:{ // 局部注册组件
'login-input':{
data:function(){
return {
uname:'admin',
pwd:''
}
},
template:`
<div>
<p>账号:<input type="text" v-model="uname" /></p>
<p>密码:<input type="password" v-model="pwd"/></p>
<p><button @click="login">登录</button></p>
</div>
`,
methods:{
login(){
alert(this.uname+"," + this.pwd);
}
}
}
},
})
</script>
4.4-Vue调试工具
安装
1.克隆此仓库
2.cd vue-devtools 新创建的文件夹 npm install yarn -g
3.执行 yarn install
4.然后执行 yarn run build
5.打开Chrome扩展程序页面(当前位于菜单>更多工具>扩展程序下)
6.检查右上角的“开发人员模式”
7.点击左侧的“加载解压缩”按钮,然后选择文件夹: vue-devtools/packages/shell-chrome/
8.在第3步中,您还可以yarn dev:chrome用来构建和观看解压后的扩展程序
使用
4.5-组件间的数据交互
4.5.1-父组件向子组件传递数据
实现方式
子组件内部通过props
接收传递过来的值
Vue.component("组件名称",{
props:["接收的参数标识1","接收的参数标识2"],
template:`<div>{{接收的参数标识1 + '----' + 接收的参数标识2 }}</div>`,
})
父组件通过属性
将值传递给子组件
<!-- 静态传递 -->
<组件名称 接收的参数标识1="值" 接收的参数标识2="值" ></组件名称>
<!-- 动态态传递 -->
<组件名称 :接收的参数标识1="值" :接收的参数标识2="值" ></组件名称>
代码演示
<div id="app">
<!-- 静态传递 -->
<my-div1 val1="静态传递的数据1" val2="静态传递的数据2" ></my-div1>
<!-- 动态传递 -->
<my-div1 :val1="val1" ></my-div1>
</div>
<script src="lib/vue.js"></script>
<script>
// 全局注册子组件
Vue.component("my-div1",{
props:["val1","val2"],
data:function(){
return {
msg:"hello"
}
},
template:`<div>{{msg +'---' +val1 + '----' + val2 }}</div>`,
})
// 创建vue实例父组件
var vm = new Vue({
el:"#app",
data:{
val1:'动态传递的数据1'
},
})
</script>
props命名规则
- 在props中使用驼峰形式,普通模板中需要使用短横线的形式。
- 字符串模板中没有这个限制
<div id="app">
<my-div1 hello-val1="普通模板中传递的值-短横线" ></my-div1>
<my-div2></my-div2>
</div>
<script src="lib/vue.js"></script>
<script>
// 全局注册子组件1
Vue.component("my-div1",{
props:["helloVal1"],
template:`<div>{{helloVal1}}</div>`,
})
// 全局注册子组件2
Vue.component("my-div2",{
props:["helloVal1"],
template:`
<div>
<my-div1 hello-val1="模板字符串标签传递的值-短横线" ></my-div1>
<my-div1 helloVal1="模板字符串标签传递的值-驼峰式" ></my-div1>
</div>
`,
})
// 全
// 创建vue实例父组件
var vm = new Vue({
el:"#app",
})
</script>
props传值类型
- String
- Number
- Boolean
- Array
- Object
注意:对于传值类型,在v-bind命令下使用才能区分。
4.5.2-子组件向父组件传值
子组件通过自定义事件向父组件传递信息
<子组件模板 @事件名="$emit('自定义事件名',参数名)">点击子组件更改父组件的标签大小</子组件模板>
父组件监听子组件的事件
<子组件名 @自定义事件="事件处理函数($event)"></子组件名>
$event接收子组件传递的参数
代码
<div id="app">
<h2 :style="{fontSize:size + 'px'}">父组件的内容</h2>
<my-btn @big-text="handle($event)"></my-btn>
</div>
<script src="lib/vue.js"></script>
<script>
// 全局注册子组件1
Vue.component("my-btn",{
template:`
<div>
<button @click="$emit('big-text',5)">点击子组件更改父组件的标签大小</button>
<button @click="$emit('big-text',10)">点击子组件更改父组件的标签大小</button>
</div>
`
})
// 创建vue实例父组件
var vm = new Vue({
el:"#app",
data:{
size:50
},
methods: {
handle(val){
this.size+=val;
}
},
})
</script>
4.5.3-兄弟组件相互传值
首先,需要创建单独的事件中间管理组件间的通信。
var eventHub = new Vue();
事件中心监听事件与销毁事件
eventHub.$on("事件名称",事件处理函数)
eventHub.$off("事件名称")
触发事件
eventHub.$emit("事件名称",参数列表)
需求
代码
<div id="app">
<my-btn1></my-btn1>
<my-btn2></my-btn2>
</div>
<script src="lib/vue.js"></script>
<script>
// 事件中心
var eventHub = new Vue();
// 兄弟组件1
Vue.component("my-btn1", {
data: function () {
return {
num: 0
}
},
template: `
<button @click="handle">btn1-{{num}}</button>
`,
methods: {
handle() {
// 触发组件2事件btn2
eventHub.$emit("btn2", 2)
}
},
mounted() {
// 监听事件
eventHub.$on('btn1', (val) => {
this.num += 1;
})
},
})
// 兄弟组件2
Vue.component("my-btn2", {
data: function () {
return {
num: 0
}
},
template: `
<button @click="handle">btn2-{{num}}</button>
`,
methods: {
handle() {
// 触发组件1事件btn1
eventHub.$emit("btn1", 1)
}
},
mounted() {
// 监听事件
eventHub.$on('btn2', (val) => {
this.num += 2;
})
},
})
// 创建vue实例父组件
var vm = new Vue({
el: "#app"
})
</script>
4.6-组件插槽
4.6.1-基本使用
组件插槽的作用:父组件向子组件传递内容
插槽的位置
Vue.component("组件名称",{
template:`
<div>
其他内容
<slot>插槽默认的内容</slot>
</div>
`,
})
插槽的内容
<组件的名称>替代插槽的内容</组件的名称>
代码演示
<div id="app">
<alert-box></alert-box>
<alert-box>有错误</alert-box>
<alert-box>有警告</alert-box>
</div>
<script src="lib/vue.js"></script>
<script>
// 组件
Vue.component("alert-box", {
template: `
<div>
<strong>信息提示:</strong>
<slot>暂无信息</slot>
</div>
`,
})
// 创建vue实例父组件
var vm = new Vue({
el: "#app"
})
</script>
4.6.2-具名插槽
插槽的定义
Vue.component("组件名称",{
template:`
<div>
其他内容
<slot name="插槽的名字1">插槽默认的内容</slot>
<slot></slot>
<slot name="插槽的名字2">插槽默认的内容</slot>
</div>
`,
})
插槽的使用
<组件的名称>
<template slot="插槽名字1">内容</template>
默认插槽
<template slot="插槽名字2">内容</template>
</组件的名称>
代码演示
<div id="app">
<base-box>
<ul slot="header">
<li>头部信息1</li>
<li>头部信息2</li>
<li>头部信息3</li>
</ul>
<div slot="main">
<ul>
<li>主题信息</li>
<li>主题信息</li>
<li>主题信息</li>
</ul>
</div>
<template slot="footer">
<ul>
<li>底部信息</li>
<li>底部信息</li>
<li>底部信息</li>
</ul>
</template>
<ol>
<li>侧边栏1</li>
<li>侧边栏2</li>
<li>侧边栏3</li>
</ol>
</base-box>
</div>
<script src="lib/vue.js"></script>
<script>
// 组件
Vue.component("base-box", {
template: `
<div>
<header>
<h2>头部</h2>
<slot name="header"></slot>
</header>
<main>
<h2>主体</h2>
<slot name="main"></slot>
</main>
<slot>侧边栏</slot>
<footer>
<h2>底部</h2>
<slot name="footer"></slot>
</footer>
</div>
`,
})
// 创建vue实例父组件
var vm = new Vue({
el: "#app"
})
</script>
4.6.3-作用域插槽
应用场景:父组件对子组件的内容进行加工处理
定义组件
Vue.component("组件名称",{
template:`
<div>
其他内容
<slot :自定义键名="值"></slot>
</div>
`,
})
使用组件
<组件的名称>
<template slot-scope="接收值的名称">
接收值的名称.自定义键名
</template>
</组件的名称>
代码演示
<div id="app">
<name-list :list="list">
<template slot-scope="props">
<strong v-if="props.obj.id==1" v-text="props.obj.value"></strong>
<span v-else v-text="props.obj.value"></span>
</template>
</name-list>
</div>
<script src="lib/vue.js"></script>
<script>
// 组件
Vue.component("name-list", {
props:["list"],
template: `
<ul>
<li v-for="(item,index) in list" :key="index">
<slot :obj="{id:index,value:item}"></slot>
</li>
</ul>
`,
})
// 创建vue实例父组件
var vm = new Vue({
el: "#app",
data:{
list:['张三','李四','王五']
}
})
</script>
第五章:前后端交互
5.1-概览
- 前后端交互模式
- Promise的使用
- 接口调用-fetch用法
- 接口调用-axios用法
- 接口调用-async/await用法
5.2-前后端交互模式
5.2.1-接口调用方式
- 原生ajax
- 基于jquery的ajax
- fetch
- axios
5.2.2-URL
传统的URL
Restful形式的URL
5.3-Promise
5.3.1-Promise概述
Promise是异步编程的一种解决方案,从语法上讲Promise是一个对象,从它可以获取异步操作的消息。
异步编程现有的问题:
- 若多个异步程序之间没有依赖,则不用关心异步程序执行顺序问题。
- 若多个异步程序之间含有依赖,则异步之间需要多层嵌套。而多层嵌套不易于程序的阅读和维护。这种现象被称为“回调地狱”
使用Promise有以下好处:
- 可以避免回调地狱问题
- Promise对象提供了简洁的API,使得控制异步操作更加容易。
5.3.2-Promise基本用法
语法
- 实例化Promise对象,构造函数中传递函数,该函数中用于处理异步任务。
resolve
和reject
两个参数,分别用于处理成功和失败两种情况,并通过Promise实例对象then方法获取处理结果。
var p = new Promise((resolve,reject)=>{
// 成功时,调用 resolve()
// 失败时,调用 reject()
});
p.then((result)=>{
// 从resolve中得到成功时的结果
},(error)=>{
// 从reject中得到失败时的结果
})
或
p.then((result)=>{
// 从resolve中得到成功时的结果
}).catch((error)=>{
// 从reject中得到失败时的结果
});
代码
var p = new Promise((resolve,reject)=>{
var flag = false;
if(flag){
resolve("success data");
}else {
reject("error data")
}
});
p.then((result)=>{
console.log(result);
},(error)=>{
console.log(error)
})
// 或
/*
p.then((result)=>{
console.log(result);
})
.catch((error)=>{
console.log(error)
})
*/
5.3.3-基于Promise发送ajax请求
代码演示
<script src="lib/jquery.js"></script>
<script>
function getResult(url){
var p = new Promise((resolve,reject)=>{
$.ajax({
url:url,
type:'get',
success:function(data){
resolve(data);
},
error:function(msg){
reject(msg);
}
})
});
return p;
}
// 发送请求1
getResult('http://localhost:3000/data1')
.then((result)=>{
console.log(result);
// 发送请求2
return getResult('http://localhost:3000/data2')
})
.then((result)=>{
console.log(result);
// 发送请求3
return getResult('http://localhost:3000/data3')
})
.then((result)=>{
console.log(result);
})
</script>
执行结果:
数据1
数据2
数据3
then参数中的函数返回值
- 返回Promise实例对象
- 返回的该实例对象可以调用下一个then方法
- 返回普通值
- 返回普通值会直接传递给下一个then,通过then参数中的函数参数接收值。
<script src="lib/jquery.js"></script>
<script>
function getResult(url){
var p = new Promise((resolve,reject)=>{
$.ajax({
url:url,
type:'get',
success:function(data){
resolve(data);
},
error:function(msg){
reject(msg);
}
})
});
return p;
}
// 发送请求1
getResult('http://localhost:3000/data1')
.then((result)=>{
console.log(result);
// 发送请求2
return getResult('http://localhost:3000/data2')
})
.then((result)=>{
console.log(result);
return "普通数据"
}).then((result)=>{
console.log(result);
})
</script>
执行结果
数据1
数据2
普通数据
5.3.4-Promise常用api
实例方法
- then(callback) 得到异步任务的正确结果
- catch(callback) 得到异步任务的异常结果
- finally(callback) 成功与否都会执行
function fn(){
return new Promise((resolve,reject)=>{
setTimeout(function(){
if(true){
resolve('success');
}else {
reject('error');
}
},2000);
});
}
fn()
.then((result)=>{
console.log(result);
})
.catch((error)=>{
console.log(error);
})
.finally(()=>{
console.log('end');
})
对象方法
api:
- Promise.all() 并发处理多个异步任务,所有任务完成才能得到结果
- Promise.race() 并发处理多个异步任务,只有一个任务完成就能得到结果。
调用格式:
// all
Promise.all([Promise实例对象1,Promise实例对象2,...])
.then((result)=>{
// result返回的是一个数组,该数组中的结果分别是all中数组的结果,顺序一致。
})
// race
Promise.all([Promise实例对象1,Promise实例对象2,...])
.then((result)=>{
// result返回的是第一个Promise实例对象的结果。
})
代码:
function getResult(url) {
var p = new Promise((resolve, reject) => {
$.ajax({
url: url,
type: 'get',
success: function (data) {
resolve(data);
},
error: function (msg) {
reject(msg);
}
})
});
return p;
}
// 发送请求1
var p1 = getResult('http://localhost:3000/data1');
// 发送请求2
var p2 = getResult('http://localhost:3000/data2');
// 发送请求3
var p3 = getResult('http://localhost:3000/data3');
// 并发处理多个异步
Promise.all([p1, p2, p3]).then((result) => {
console.log(result)
});
// 执行结果:["数据1", "数据2", "数据3"]
// 并发处理多个异步
Promise.race([p1, p2, p3]).then((result) => {
console.log(result)
})
// 执行结果:数据1
5.4-fetch基本使用
5.4.1-概述
基本特性
- 更加简单的数据获取方式,功能更加强大、灵活,可以看作是xhr的升级版
- 基于Promise语法实现
语法结构
fetch('url')
.then((response)=>{
// 返回一个Promise对象,用于获取异步请求的数据
return response.text();
})
.then((value)=>{
// 这里才是获取的真正数据
console.log(value);
})
代码
fetch('http://localhost:3000/data1')
.then((response)=>{
// 返回一个Promise对象,用于获取异步请求的数据
return response.text();
})
.then((value)=>{
// 这里才是获取的真正数据
console.log(value);
})
// 执行结果:数据1
5.4.2-fetch请求参数
常用的配置选项
- method:String,http的请求方式,默认为GET(GET、POST、PUT、DELETE)
- body:String,http的请求参数
- headers:Object,http的请求头,默认为{}
get和delete请求方式
fetch('http://localhost:3000/books?id=123',{
method:"get"
})
.then((response)=>{
return response.text();
})
.then((value)=>{
console.log(value);
})
// 执行结果:传统的URL传递参数!123
fetch('http://localhost:3000/books/123',{
method:"get"
})
.then((response)=>{
return response.text();
})
.then((value)=>{
console.log(value);
})
// 执行结果:Restful形式的URL传递参数!123
fetch('http://localhost:3000/books/123',{
method:"delete"
})
.then((response)=>{
return response.text();
})
.then((value)=>{
console.log(value);
})
// DELETE请求传递参数!123
post和put请求
// 【发送post请求】
fetch('http://localhost:3000/books', {
method: "post",
body: "uname=admin&pwd=123",
headers: {
// 这个不能省略
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then((response) => {
return response.text();
})
.then((value) => {
console.log(value);
})
// 【发送put请求】
fetch('http://localhost:3000/books/12', {
method: "put",
body: "uname=admin&pwd=123",
headers: {
// 这个不能省略
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then((response) => {
return response.text();
})
.then((value) => {
console.log(value);
})
5.4.3-fetch响应结果
- text() 返回字符串
- json() 返回结果是一个对象
// 【text】
fetch('http://localhost:3000/json')
.then((response) => {
// 返回一个Promise对象,用于获取异步请求的数据
return response.text();
})
.then((value) => {
// 这里才是获取的真正数据
console.log(typeof value); // string
})
// 【json】
fetch('http://localhost:3000/json')
.then((response) => {
// 返回一个Promise对象,用于获取异步请求的数据
return response.json();
})
.then((value) => {
// 这里才是获取的真正数据
console.log(typeof value); // object
})
5.5-axios
下载:https://github.com/mzabriskie/axios
5.5.1-axios常用api
- get
- post
- delete
- put
<script src="lib/axios.js"></script>
<script>
// 【发送get请求】
axios.get('http://localhost:3000/adata')
.then((result) => {
console.log(result.data);
// 结果:Hello axios!
})
// 【发送get请求-带参数】
axios.get('http://localhost:3000/axios', {
params: { id: 10010 }
})
.then((result) => {
console.log(result.data);
// 结果:axios get 传递参数10010
})
// 【发送delete请求带参数】
axios.delete('http://localhost:3000/axios', {
params: { id: 10010 }
})
.then((result) => {
console.log(result.data);
// 结果:axios get 传递参数10010
})
// 【发送post请求-json】
axios.post('http://localhost:3000/axios', {
uname: 'admin',
pwd: '123456'
}).then((result) => {
console.log(result.data);
// 结果:axios post 传递参数admin---123456
})
// 【发送post请求-application/x-www-form-urlencoded】
var params = new URLSearchParams();
params.append('uname', 'admin');
params.append('pwd', '99999');
axios.post('http://localhost:3000/axios', params).then((result) => {
console.log(result.data);
// 结果:axios post 传递参数admin---99999
})
// 【发送post请求-json】
axios.put('http://localhost:3000/axios', {
id:'10010',
uname: 'admin',
pwd: '123456'
}).then((result) => {
console.log(result.data);
// 结果:axios put 传递参数10010---admin---123456
})
</script>
5.5.2-axios响应结果
- data,返回的实际的数据
- status ,响应结果状态码
- statusText ,响应结果状态描述
- headers ,响应头信息
{data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, …}
config: {url: "axios-json", method: "get", headers: {…}, baseURL: "http://localhost:3000/", transformRequest: Array(1), …}
data: {uname: "lisi", age: 12}
headers: {content-length: "25", content-type: "application/json; charset=utf-8"}
request: XMLHttpRequest {readyState: 4, timeout: 3000, withCredentials: false, upload: XMLHttpRequestUpload, onreadystatechange: ƒ, …}
status: 200
statusText: "OK"
__proto__: Object
5.5.3-axios全局配置
// 全局配置-超时时间
axios.defaults.timeout = 3000;
// 全局配置-默认地址
axios.defaults.baseURL = 'http://localhost:3000/';
// 全局配置-设置请求头
axios.defaults.headers['mytoken']='hello';
5.5.4-拦截器
请求拦截器
响应拦截器
代码
// 全局配置-默认地址
axios.defaults.baseURL = 'http://localhost:3000/';
// 请求拦截器
axios.interceptors.request.use(function(config){
// 设置请求头
config.headers.mytoken = 'nihao';
return config;
});
// 响应拦截器
axios.interceptors.response.use(function(result){
// 设置响应的结果
result = result.data;
return result;
})
// 发送请求
axios.get('axios-json')
.then((result) => {
console.log(result);
})
5.5.5-async/await
- async和await是关键字,在ES7中引入的新的语法
- 可以更加方便的进行异步操作
- async关键字用于函数上,async函数的返回值是Promise对象
- await关键字用于async函数中,await可以得到异步的结果
// 全局配置-默认地址
axios.defaults.baseURL = 'http://localhost:3000/';
// 异步函数-按顺序处理多个请求
async function run(){
var r1 = await axios.get('a1');
var r2 = await axios.get('a2');
var r3 = await axios.get('a3');
console.log(r1.data);
console.log(r2.data);
console.log(r3.data);
}
run(
参考:Promise对象
第六章:路由
6.1-概览
- 路由的基本概念与原理
- vue-router的基本使用
- vue-router嵌套路由
- vue-router动态路由匹配
- vue-router命名路由
- vue-router编程式导航
6.2-路由的基本概念与原理
6.2.1-什么是路由
认识路由
路由是一个比较广义和抽象的概念,路由的本质就是对应关系。
在开发中路由分为:
- 前端路由
- 后端路由
后端路由
概念:根据不同的URL请求返回不同的内容。
本质:URL请求地址与服务器资源之间的对应关系。
SPA
SPA(Single Page Application),单页面应用程序。
- 后端渲染,存在性能问题
- Ajax操作,提高了前端渲染性能,但不支持浏览器的前进后退操作。
- SPA,整个网站只有一个页面,内容变化通过Ajax实现局部更新,同时支持浏览器地址栏的前进后退操作。
- SPA实现原理之一,基于URL和hash,hash变化会导致浏览器记录访问历史的变化、但是hash的变化不会触发新的URL请求。
- 在实现SPA的过程中,最核心的技术就是前端路由
前端路由
- 概念:根据不同的用户事件,显示不同的页面内容。
- 本质:用户事件与事件处理函数之间的对应关系。
6.2.2-模拟简单的路由
效果
代码
<div id="app">
<div class="tab">
<!-- tab-button -->
<ul class="clearfix">
<li><a href="#keji">科技</a></li>
<li><a href="#vlog">vlog</a></li>
<li><a href="#shehui">社会</a></li>
<li><a href="#dongman">动漫</a></li>
</ul>
<!-- tab-content -->
<!-- component一个组件占位符,is属性表示哪个组件 -->
<component :is="tabName"></component>
</div>
</div>
<script src="lib/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
tabName: 'keji'
},
components: {
keji: {
template: `<h2>科技世界</h2>`,
},
shehui: {
template: `<h2>社会新闻</h2>`,
},
vlog: {
template: `<h2>vlog,你的世界</h2>`
},
dongman: {
template: `<h2>动漫,一起二次元哈</h2>`
}
}
});
// onhashchange监听页面hash锚点的变化
window.onhashchange = function () {
console.log(location.hash.slice(1));
switch (location.hash.slice(1)) {
case 'keji':
vm.tabName = 'keji';
break;
case 'shehui':
vm.tabName = 'shehui';
break;
case 'dongman':
vm.tabName = 'dongman';
break;
case 'vlog':
vm.tabName = 'vlog';
break;
}
}
</script>
6.2.3-Vue-Router
Vue-Router,(官网https://router.vuejs.org/zh/ )
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。
- 支持HTML5 历史模式或 hash 模式
- 支持嵌套路由
- 支持路由参数
- 支持编程式路由
- 支持命名路由
6.3-Vue-Router的基本使用
6.3.1-基本使用步骤
- 引入相关的库文件
- 添加路由链接
- 添加路由填充位
- 定义路由组件
- 配置路由规则并创建路由实例
- 把路由挂载到Vue根实例中
6.3.2-引入相关的库文件
<!-- 引入vue,为全局对象window挂载Vue构造函数 -->
<script src="lib/vue.js"></script>
<!-- 引入vue-router,为全局对象window挂载VueRouter构造函数 -->
<script src="lib/vue-router.js"></script>
6.3.3-添加路由连接
<!-- router-link是vue中提供的标签,默认会被渲染为a标签 -->
<!-- to属性默认会被渲染为href属性 -->
<!-- to属性值默认会被渲染为#开头的hash值 -->
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>
6.3.4-添加路由填充位
<!-- 路由填充位(路由占位符) -->
<!-- 将来会通过路由规则匹配到的组件,将会被渲染到router-view所在的位置 -->
<router-view></router-view>
6.3.5-定义路由组件
// 定义login组件
const login = {
template:`<h2>Login组件</h2>`
}
// 定义register组件
const register = {
template:`<h2>Register组件</h2>`
}
6.3.6-配置路由规则并创建实例
// 配置路由规则,并创建路由实例
const router = new VueRouter({
// routers是路由规则数组
routes: [
// 每一个路由规则都是一个配置对象,其中至少包含path和component两个属性
// path表示当前路由规则匹配的hash地址
// component表示当前路由规则要展示的路由组件
{ path: '/login', component: Login },
{ path: '/register', component: Register }
]
})
6.3.7-把路由挂载到Vue实例中
// 把路由挂载到vue实例中
var vm = new Vue({
el: '#app',
data: {},
router:router
})
6.3.8-完整代码
<div id="app">
<!-- router-link是vue中提供的标签,默认会被渲染为a标签 -->
<!-- to属性默认会被渲染为href属性 -->
<!-- to属性值默认会被渲染为#开头的hash值 -->
<router-link to="/login">Login</router-link>
<router-link to="/register">Register</router-link>
<!-- 路由填充位(路由占位符) -->
<!-- 将来会通过路由规则匹配到的组件,将会被渲染到router-view所在的位置 -->
<router-view></router-view>
</div>
<!-- 引入vue,为全局对象window挂载Vue构造函数 -->
<script src="lib/vue.js"></script>
<!-- 引入vue-router,为全局对象window挂载VueRouter构造函数 -->
<script src="lib/vue-router.js"></script>
<script>
// 定义login组件
const Login = {
template: '<h2>Login组件</h2>'
}
// 定义register组件
const Register = {
template: '<h2>Register组件</h2>'
}
// 配置路由规则,并创建路由实例
const router = new VueRouter({
// routers是路由规则数组
routes: [
// 每一个路由规则都是一个配置对象,其中至少包含path和component两个属性
// path表示当前路由规则匹配的hash地址
// component表示当前路由规则要展示的路由组件
{ path: '/login', component: Login },
{ path: '/register', component: Register }
]
})
// 把路由挂载到vue实例中
var vm = new Vue({
el: '#app',
data: {},
router
})
</script>
6.4-路由重定向
路由重定向指的是,用户在访问地址a的时候,强制用户跳转到地址c,从而展示特点的组件页面
通过路由规则的redirect属性,指定一个新的路由地址,可以很方便的设置路由从定向
// 配置路由规则,并创建路由实例
const router = new VueRouter({
// routers是路由规则数组
routes: [
// 其中path表示重定向的原地址,redirect表示新地址
{path:'/',redirect:'/login'},
{ path: '/login', component: Login },
{ path: '/register', component: Register }
]
})
6.5-路由嵌套
嵌套路由功能分析
- 点击父级链接显示模板内容
- 模板内容又有子路由链接
- 点击子路由链接显示子级模板内容
父路由组件模板
- 父路由链接
- 父路由填充位
<!-- 父路由链接 -->
<p>
<router-link to="/login">Login</router-link>
<router-link to="/register">Register</router-link>
</p>
<!-- 父路由填充位 -->
<div>
<router-view></router-view>
</div>
子路由组件模板
<div class="register">
<h2>Register组件</h2>
<hr></hr>
<!--子路由链接-->
<router-link to="/register/tab1">Tab1子组件</router-link>
<router-link to="/register/tab2">Tab2子组件</router-link>
<!--子路由填充位-->
<router-view></router-view>
</div>
嵌套路由的使用
- 通过children属性,实现子路由规则配置
const router = new VueRouter({
// routers是路由规则数组
routes: [
// 其中path表示重定向的原地址,redirect表示新地址
{ path: '/', redirect: '/login' },
{ path: '/login', component: Login },
{
path: '/register',
component: Register,
// 通过children属性,为register添加子路由规则配置
children: [
{ path: '/register', redirect: '/register/tab1' },
{ path: '/register/tab1', component: Tab1 },
{ path: '/register/tab2', component: Tab2 },
]
}
]
})
完整代码
<div id="app">
<!-- 父路由链接 -->
<p>
<router-link to="/login">Login</router-link>
<router-link to="/register">Register</router-link>
</p>
<!-- 父路由填充位 -->
<div>
<router-view></router-view>
</div>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// 定义login组件
const Login = {
template: '<h2>Login组件</h2>'
}
// 定义register组件
const Register = {
template: `
<div class="register">
<h2>Register组件</h2>
<hr></hr>
<router-link to="/register/tab1">Tab1子组件</router-link>
<router-link to="/register/tab2">Tab2子组件</router-link>
<router-view></router-view>
</div>
`
}
// 定义register子组件1
const Tab1 = {
template: `<h2>Tab1子组件</h2>`
}
// 定义register子组件2
const Tab2 = {
template: `<h2>Tab2子组件</h2>`
}
// 配置路由规则,并创建路由实例
const router = new VueRouter({
// routers是路由规则数组
routes: [
// 其中path表示重定向的原地址,redirect表示新地址
{ path: '/', redirect: '/login' },
{ path: '/login', component: Login },
{
path: '/register',
component: Register,
// 通过children属性,为register添加子路由规则配置
children: [
{ path: '/register', redirect: '/register/tab1' },
{ path: '/register/tab1', component: Tab1 },
{ path: '/register/tab2', component: Tab2 },
]
}
]
})
// 把路由挂载到vue实例中
var vm = new Vue({
el: '#app',
data: {},
router
})
</script>
6.6-动态路由匹配
6.6.1-基本使用
通过路由参数的方式进行路由匹配
路由规则
const router = new VueRouter({
routes: [
// 动态路径参数,以:开头
{path:'/user/:id',component:User}
]
})
路由模板
const User = {
// 路由组件中通过$route.params获取路由参数
template: '<h2>用来信息-id为{{$route.params.id}}</h2>'
}
完整代码
<div id="app">
<router-link to="/user/1">user1</router-link>
<router-link to="/user/2">user2</router-link>
<router-link to="/user/3">user3</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
const User = {
// 路由组件中通过$route.params获取路由参数
template: '<h2>用来信息-id为{{$route.params.id}}</h2>'
}
const router = new VueRouter({
routes: [
// 动态路径参数,以:开头
{path:'/user/:id',component:User}
]
})
// 把路由挂载到vue实例中
var vm = new Vue({
el: '#app',
data: {},
router
})
</script>
6.6.2-props传递参数
传递基本数据类型
路由规则
const router = new VueRouter({
routes: [
// props设置为布尔值
{path:'/user/:id',component:User,props:true}
]
})
路由模板
const User = {
props:['id'], // props接收路由参数
// 路由组件中通过$route.params获取路由参数
template: '<h2>用来信息-id为{{id}}</h2>'
}
完整代码
<div id="app">
<router-link to="/user/1">user1</router-link>
<router-link to="/user/2">user2</router-link>
<router-link to="/user/3">user3</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
const User = {
props:['id'],
// 路由组件中通过$route.params获取路由参数
template: '<h2>用来信息-id为{{id}}</h2>'
}
const router = new VueRouter({
routes: [
// 动态路径参数,以:开头
{path:'/user/:id',component:User,props:true}
]
})
// 把路由挂载到vue实例中
var vm = new Vue({
el: '#app',
data: {},
router
})
</script>
传递对象类型参数
路由规则
const router = new VueRouter({
routes: [
// props值为对象
{path:'/user/:id',component:User,props:{uname:'admin',age:10}}
]
})
路由模板
const User = {
props:['id','uname','age'],
// 路由组件中通过$route.params获取路由参数
template: '<h2>用来信息-id为{{id}}-{{uname}}-{{age}}</h2>'
}
完整代码
<div id="app">
<router-link to="/user/1">user1</router-link>
<router-link to="/user/2">user2</router-link>
<router-link to="/user/3">user3</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
const User = {
props:['id','uname','age'],
// 路由组件中通过$route.params获取路由参数
template: '<h2>用来信息-id为{{id}}-{{uname}}-{{age}}</h2>'
}
const router = new VueRouter({
routes: [
// 动态路径参数,以:开头
{path:'/user/:id',component:User,props:{uname:'admin',age:10}}
]
})
// 把路由挂载到vue实例中
var vm = new Vue({
el: '#app',
data: {},
router
})
</script>
此时id值无法获取。
props值为函数
路由规则
const router = new VueRouter({
routes: [
// 动态路径参数,以:开头
{
path:'/user/:id',
component:User,
props:(router)=>{
return {id:router.params.id,uname:'admin',age:10}
}
}
]
}
路由模板
const User = {
props:['id','uname','age'],
// 路由组件中通过$route.params获取路由参数
template: '<h2>用来信息-id为{{id}}-{{uname}}-{{age}}</h2>'
}
完整代码
<div id="app">
<router-link to="/user/1">user1</router-link>
<router-link to="/user/2">user2</router-link>
<router-link to="/user/3">user3</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
const User = {
props:['id','uname','age'],
// 路由组件中通过$route.params获取路由参数
template: '<h2>用来信息-id为{{id}}-{{uname}}-{{age}}</h2>'
}
const router = new VueRouter({
routes: [
// 动态路径参数,以:开头
{
path:'/user/:id',
component:User,
props:(router)=>{
return {id:router.params.id,uname:'admin',age:10}
}
}
]
})
// 把路由挂载到vue实例中
var vm = new Vue({
el: '#app',
data: {},
router
})
</script>
此时id值也可访问
6.7-命名路由
6.7.1-命名路由的配置规则
为了更加方便的表示路由路径,可以给路由规则起一个别名,即为“命名路由”
路由规则
const router = new VueRouter({
routes: [
{
// 命名路由
name:'user',
path:'/user/:id',
component:User,
props:(router)=>{
return {id:router.params.id,uname:'admin',age:10}
}
}
]
})
路由模板
const User = {
props:['id','uname','age'],
// 路由组件中通过$route.params获取路由参数
template: '<h2>用来信息-id为{{id}}-{{uname}}-{{age}}</h2>'
}
路由链接
<div id="app">
<router-link :to="{name:'user',params:{id:10}}">user1</router-link>
<router-link :to="{name:'user',params:{id:20}}">user2</router-link>
<router-link :to="{name:'user',params:{id:30}}">user3</router-link>
<router-view></router-view>
</div>
完整代码
<div id="app">
<router-link :to="{name:'user',params:{id:10}}">user1</router-link>
<router-link :to="{name:'user',params:{id:20}}">user2</router-link>
<router-link :to="{name:'user',params:{id:30}}">user3</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
const User = {
props:['id','uname','age'],
// 路由组件中通过$route.params获取路由参数
template: '<h2>用来信息-id为{{id}}-{{uname}}-{{age}}</h2>'
}
const router = new VueRouter({
routes: [
{
// 命名路由
name:'user',
path:'/user/:id',
component:User,
props:(router)=>{
return {id:router.params.id,uname:'admin',age:10}
}
}
]
})
// 把路由挂载到vue实例中
var vm = new Vue({
el: '#app',
data: {},
router
})
</script>
6.8-编程式导航
6.8.1-编程式导航基本使用
常用API
- this.$router.push('hash地址');
- this.$router.go(n);
<div id="app">
<router-link :to="{name:'user',params:{id:10}}">user1</router-link>
<router-link :to="{name:'user',params:{id:20}}">user2</router-link>
<router-link :to="{name:'user',params:{id:30}}">user3</router-link>
<router-link :to="{name:'register'}">Register</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
const User = {
props:['id','uname','age'],
// 路由组件中通过$route.params获取路由参数
template: `
<div>
<h2>用来信息-id为{{id}}-{{uname}}-{{age}}</h2>
<button @click="toRegister">跳转到register</button>
</div>
`,
methods: {
toRegister(){
this.$router.push('/register');
}
},
}
const Register = {
template:`
<div>
<h2>Register组件</h2>
<button @click="toBack">返回</button>
</div>
`,
methods:{
toBack(){
this.$router.go(-1);
}
}
}
const router = new VueRouter({
routes: [
// user路由
{
// 命名路由
name:'user',
path:'/user/:id',
component:User,
props:(router)=>{
return {id:router.params.id,uname:'admin',age:10}
}
},
// register路由
{
name:'register',
path:'/register',
component:Register
}
]
})
// 把路由挂载到vue实例中
var vm = new Vue({
el: '#app',
data: {},
router
})
</script>
6.8.2-编程式导航参数规则
this.$router.push(参数) 参数规则
// 字符串-路径名称
this.$router.push('/home');
// 对象-路径名称
this.$router.push({path:'/home'});
// 命名路由,传递参数
this.$router.push({name:'/home',params:{id:123}});
// 带查询参数,变成/register?uname=admin
this.$router.push({path:'/home',query:{uname:'admin'}});