1、vue指令以及案例
1.1 学习目标
- 能够说出 vue 的基本用法
- 能够说出 vue 的模板语法
- 能够说出 vue 的常用特性
- 能够基于 vue 实现案例效果
1.2 vue 概述
尤雨溪
vue:渐进式 JavaScript 框架
声明式渲染 => 组件系统 => 客户端路由 => 集中式状态管理 => 项目构建
https://cn.vuejs.org/v2/api/
- 易用:熟悉HTML css JavaScript知识后,可快速上手vue
- 灵活:在一个库和一套完整框架之间自如伸缩
- 高校:20KB运行大小,超快虚拟DOM
1.3 vue 之 hello world
<body>
<div id="app">
<!-- {{}} 插值表达式,里面写变量名字 -->
<!-- {{}} 插值表达式,里面写变量名字 -->
<div>{{msg}}</div>
<div>{{1+2}}</div>
<!-- 3 -->
<div>{{msg+'---'+123}}</div>
<!-- Hello world---123 -->
</div>
<script src="./js/vue.js"></script>
<script>
/*
vue的使用步骤
1、需要提供标签用于填充数据
2、引入 vue.js 库文件
3、使用vue的语法做功能
4、把 vue 提供的数据,填充到标签里面
*/
var vm = new Vue({
// 创建一个vue实例
// el 表示 那个容器,里面写的是选择器
// data 填充数据
el: '#app',
data: {
msg: 'Hello world'
}
})
</script>
</body>
- el:元素的挂载位置(值可以是CSS选择器或者DOM元素)
- data:模型数据(值是一个对象)
插值表达式
- 将数据填充到HTML标签中
- 插值表达式支持基本的计算操作
vue代码运行原理分析
- 概述编译过程的概念(vue语法 = > 原生语法)
- vue代码 经过 vue 框架处理,转变成原生js代码
1.4 模板语法概述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XYsdaJps-1620387784164)(vue基础.assets/image-20201018122455568.png)]
前端渲染方式
- 原生js拼接字符串
- 使用前端模板引擎
- 使用 vue 特定的模板语法
vue 模块语法
- 插值表达式
- 指令
- 事件绑定
- 属性绑定
- 样式绑定
- 分支循环结构
1.5 v-cloak 指令
什么是指令:指令的本质就是自定义属性
v-cloak指令用法
- 插值表达式存在的问题:“ 闪动 ”
- 如何解决此问题:使用 v-cloak 指令
- 解决此问题的原理:先隐藏,替换好值之后再显示最终的值
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<!-- {{}} 插值表达式,里面写变量名字 -->
<div v-cloak>{{msg}}</div>
<div v-cloak>{{1+2}}</div>
<!-- 3 -->
<div v-cloak>{{msg+'---'+123}}</div>
<!-- Hello world---123 -->
</div>
<script src="./js/vue.js"></script>
<script>
/*
v-cloak 指令的用法
1、提供样式
[v-cloak]{
display:none;
}
2、在插值表达式所在的标签中,添加 v-cloak指令
原理:先通过样式隐藏内容,然后在内存中进行值的替换,替换好之后在显示最终的结果
*/
var vm = new Vue({
el: '#app',
data: {
msg: 'Hello world'
}
})
</script>
</body>
</html>
1.6 v-text v-html v-pre 数据填充的三个指令
1、v-text 填充纯文本
相比插值表达式更加简洁
2、v-html 填充HTML片段
存在安全问题
本网站内部数据可以使用,来自第三方的数据不可以用
3、v-pre 填充原始信息
显示原始信息,跳过编译过程(分析编译过程)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGfCHDSy-1620387784166)(vue基础.assets/image-20201018125344603.png)]
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<div>{{msg}}</div>
<div v-text="msg"></div>
<!-- v-text 没有闪动问题,显示结果也是 Hello world -->
<div v-html="msg1"></div>
<!-- 显示的 h1 标签 -->
<div v-pre>{{msg}}</div>
<!-- 显示结果是 {{msg}} -->
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: 'Hello world',
msg1: '<h1>你好</h1>'
}
})
</script>
</body>
</html>
1.7 数据响应式概念 与 v-once 用法
如何理解响应式
- html5中的响应式(屏幕尺寸的变化导致样式的变化)
- 数据的响应式(数据的变化导致页面内容的变化),数据驱动页面内容的变化。
数据绑定:将数据填充到标签中
v-once:只显示一次,显示内容之后不再具有响应式功能
<div v-once>{{msg}}</div>
<!-- 只会显示一次,你在页面的控制台修改了值,他也不会变
v-once的应用场景:如果显示的信息后续不需要再修改,可以使用 v-once ,这样可以提高性能
-->
1.8 双向数据绑定与v-model 的用法
v-model 指令在表单 元素上
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VXeUMw4E-1620387784168)(vue基础.assets/image-20201018131417691.png)]
双向:
1、用户修改页面内容,数据会变 2、数据发生变化,插值表达式也会跟着发生变化
<body>
<div id="app">
<div>{{msg}}</div>
<div>
<input type="text" v-model="msg">
</div>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: 'Hello world',
msg1: '<h1>你好</h1>'
}
})
</script>
</body>
1.9 MVVM 设计思想分析
- M(model):模型,就是data的数据
- V(view):DOM 元素
- VM(View-Model):实现控制逻辑
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YUE1wTtK-1620387784172)(vue基础.assets/image-20201018132213553.png)]
1.10 事件绑定
v-on 指令用法
<input type="button" v-on:click='num++'>
v-on 简写形式
<input type="button" @click='num++'>
在Vue中,方法定义在 vue的实例对象中,和data同一级,值是一个对象
事件函数的调用方式:
<button v-on:click='handle'>点击</button>
<button @click='handle()'>点击2</button>
<body>
<div id="app">
<div>{{num}}</div>
<div>
<button v-on:click='num++'>点击</button>
<button @click='num++'>点击2</button>
</div>
<div>
<button v-on:click='handle'>点击</button>
<button @click='handle()'>点击2</button>
</div>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
num: 0,
},
methods: {
handle: function () {
// 注意:这里不能直接写 num++
// this 就是 vue的实例vm
console.log(this);
this.num++;
}
}
})
</script>
</body>
1.11 事件函数参数传递方式
<body>
<div id="app">
<div>{{num}}</div>
<div>
<button v-on:click='handle1'>点击</button>
<!--
如果事件直接绑定函数名称,那么默认会携带 事件对象 作为事件函数的第一个参数
-->
<button @click='handle(123,456,$event)'>点击2</button>
<!--
如果事件绑定函数调用,那么事件对象必须作为最后一个参数,进行传递,并且名字为 $event
-->
</div>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
num: 0,
},
methods: {
handle: function (p, p1, event) {
// 注意:这里不能直接写 num++
// this 就是 vue的实例vm
//console.log(this);
console.log(event.target);
this.num++;
},
handle1: function (event) {
console.log(event.target);
}
}
})
</script>
</body>
1.12 事件修饰符的用法
<a href="" @click.stop="handle">跳转</a>
<a href="" @click.prevent="handle">跳转</a>
<a href="" @click.prevent.stop="handle">跳转</a>
<!--只执行直接绑定在此元素上的事件,冒泡不会执行找个元素的事件 -->
<a href="" @click.self="handle">跳转</a>
<div style="width: 100px;height: 100px;background: #000;" @click.self='handle'>
<a href="" @click='handle'>跳转</a>
</div>
<!--
如果不给父元素加 .self 则点击 a 的时候,会触发 父元素的点击事件,若给父元素加上 .self 则只会在用户点击父元素的时候触发,不会因为冒泡触发
-->
<body>
<div id="app">
<div style="width: 100px;height: 100px;background: #000;" @click='handle'>
<a href="" @click.stop='handle'>跳转</a>
</div>
<a href="" @click.stop="handle">跳转</a>
<a href="" @click.prevent="handle">跳转</a>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
num: 0,
},
methods: {
handle: function (p, p1, event) {
alert('11')
},
}
})
</script>
</body>
1.13 按键修饰符的用法
<!--
案件修饰符
1、 .enter 回车键
<input v-on:keyup.enter='submit'>
2、 .delete 删除键
<input v-on:keyup.delete='submit'>
-->
1.14 自定义按键修饰符
<input type="text" @keyup.65='handle'>
<!--这样表示 当按了键盘的小写a或者大写A 的时候 触发的事件-->
<input type="text" @keyup.s='handle'>
<!--这样表示 当按了键盘的小写s或者大写S 的时候 触发的事件-->
<!--自定义按键修饰符-->
<input type="text" v-model="msg" @keyup.aaa="handle">
Vue.config.keyCodes.aaa = 65
// 这个后面的值,代表按键的ASII码,前面的值就是一个名字
// 这样表示只有按 a(65 对应的就是a) 的时候,并且绑定 @keyup.aaa 事件才会触发后面的函数
<!--名字是自定义的,但是对应的值必须是按键对应的ASII码 -->
1.15 简单计算器案例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<div>
数值A:<input type="text" v-model="num1">
</div>
<div>
数值A:<input type="text" v-model="num2">
</div>
<div>
<button @click="handle">计算</button>
</div>
<div>
计算结果<span>{{sum}}</span>
</div>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
num1: '',
num2: '',
sum: '',
},
methods: {
handle: function () {
this.sum = parseInt(this.num1) + parseInt(this.num2)
},
}
})
</script>
</body>
</html>
1.16 属性绑定基本用法
不能直接在后面写地址
<body>
<!--
属性绑定
<a v-bind:href='url'>跳转</a>
缩写形式
<a :href='url'>跳转</a>
-->
<div id="app">
<a v-bind:href='url'>跳转</a>
<button v-on:click='handle'>切换</button>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
url: 'http://www.baidu.com'
},
methods: {
handle: function () {
// 修改url
this.url = 'http://itcast.cn'
}
}
})
</script>
</body>
1.17 v-model 底层原理分析
<body>
<div id="app">
<div>{{msg}}</div>
<div>
<input type="text" @input="handle" v-bind:value="msg">
</div>
</div>
<script src=" ./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: ''
},
methods: {
handle: function (e) {
this.msg = e.target.value
}
}
})
</script>
</body>
1.18 class绑定对象用法
<div :class="{active:isActive}"></div>
<!--
键值对的形式,左边是类名,右边是控制类的属性
需要在data 里面写一个 标志变量,控制它 true显示类,false不显示
-->
<style>
.active {
border: 1px solid red;
width: 100px;
height: 100px;
}
.error {
background: orange;
}
</style>
</head>
<body>
<div id="app">
<div class="one" :class="{active:isActive,error:isError}"></div>
<!-- 键值对 左边是类名,右边控制整个类 -->
<!-- 不会覆盖原先的类 -->
<button @click="handle">切换</button>
</div>
<script src=" ./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
isActive: true,
isError: true
},
methods: {
handle: function () {
this.isActive = !this.isActive
}
}
})
</script>
</body>
1.19 class绑定数组用法
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.active {
border: 1px solid red;
width: 100px;
height: 100px;
}
.error {
background: orange;
}
</style>
</head>
<body>
<div id="app">
<div class="one" :class="[activeClass,errorClass]"></div>
<!-- 数组内写的都是变量 -->
<button @click="handle">切换</button>
</div>
<script src=" ./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
activeClass: 'active',
// 左边是变量,右边是类名
errorClass: 'error'
},
methods: {
handle: function () {
this.isActive = !this.isActive
}
}
})
</script>
</body>
</html>
1.20 class绑定3个细节
- 对象绑定和数组绑定可以结合使用
- 默认的class 不会被 :class 覆盖掉
- 如果在data里面写 class的数组形式,则可以直接在里面写 [‘类名’,‘类名’]
- 如果在 data 里面写 class的对象形式,则可以直接在里面写{类名: true或者false}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.active {
border: 1px solid red;
width: 100px;
height: 100px;
}
.error {
background: orange;
}
</style>
</head>
<body>
<div id="app">
<div class="one" :class="[activeClass,{error:isError}]"></div>
<!-- 数组内写的都是变量 -->
<button @click="handle">切换</button>
<div :class="arrClasses"></div>
<div :class="objClasses"></div>
</div>
<script src=" ./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
activeClass: 'active',
// 左边是变量,右边是类名
isError: true,
arrClasses: ['active'],
objClasses: {
active: true,
error: true
}
},
methods: {
handle: function () {
this.isActive = !this.isActive
}
}
})
</script>
</body>
</html>
1.21 style绑定用法
和 class 类似
<!--对象语法 -->
<div :style="{color:activeColor}"></div>
<!--数组语法 --->
<div :style="[baseStyles]"></div>
<body>
<div id="app">
<div :style="{color:isColor}">2222</div>
<div :style="objStyle">2222</div>
<div>数组类型控制style</div>
<div :style="[objStyle,overrideStyles]"></div>
</div>
<script src=" ./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
isColor: 'red',
objStyle: {
border: '1px solid green',
width: '200px',
height: '200px'
},
overrideStyles: {
border: '5px solid orange',
backgroundColor: 'blue'
}
},
methods: {
}
})
</script>
</body>
1.22 分支结构用法
- v-if
- v-else
- v-else-if 判断多个的时候
- v-show
前三个指令渲染出来,只会有一个div
- v-if 控制元素是否渲染到页面
- v-show 控制元素是否显示(已经渲染到了页面)
<body>
<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">测试v-show</div>
<!-- v-show 会渲染到DOM身上,但是不会显示,因为设置了 display none -->
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
score: 99,
flag: false,
}
})
</script>
</body>
1.23 循环结构遍历数组于key的作用分析
<li v-for="item in list ">{{item}}</li>
<li v-for="(item,index) in list">{{item}}+'---'+{{index}}</li>
<!--key的作用:帮助vue区分不同的元素,从而提高性能 --->
<li :key="item.id" v-for="(item,index) in list">{{item}}+'---'+{{index}}</li>
<body>
<div id="app">
<ul>
<!-- in 是关键字,右边是遍历谁,左边是每一项 -->
<li v-for='item in fruits'>{{item}}</li>
<!--
apple
orange
banana
-->
<li v-for='(item,index) in fruits'>{{item}}+'---'+{{index}}</li>
<!--
apple+'---'+0
orange+'---'+1
banana+'---'+2
-->
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
fruits: ['apple', 'orange', 'banana']
}
})
</script>
</body>
1.24 遍历对象
<body>
<div id="app">
<ul>
<li :key="index" v-for="(value,key,index) in obj">{{value}}+'--'{{key}}+'--'{{index}}</li>
<!--
value: 是值
key:是名称
index :是索引
-->
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
obj: {
uname: '呆呆狗',
age: 20,
gender: '前端开发工程师'
}
}
})
</script>
</body>
1.25
1.26
1.27
1.28 选项卡
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style type="text/css">
.tab ul {
overflow: hidden;
padding: 0;
margin: 0;
}
.tab ul li {
box-sizing: border-box;
padding: 0;
float: left;
width: 100px;
height: 45px;
line-height: 45px;
list-style: none;
text-align: center;
border-top: 1px solid blue;
border-right: 1px solid blue;
cursor
}
.tab ul li:nth-child(1) {
border-left: 1px solid blue;
}
.tab ul li.active {
background-color: orange;
}
.tab div {
width: 500px;
height: 300px;
display: none;
text-align: center;
font-size: 30px;
line-height: 300px;
border: 1px solid blue;
border-top: 0px;
}
.tab div.current {
display: block;
}
</style>
</head>
<body>
<div id="app">
<div class="tab">
<ul>
<li v-for='(item,index) in list' @click="handle(index)" :class="hoverIndex==index?'active':''">
{{item.title}}</li>
</ul>
<div v-for='(item, index) in list' :class="hoverIndex==index?'current':''">
<img :src="item.path">
</div>
</div>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
hoverIndex: 0,
list: [
{
id: 1,
title: 'apple',
path: 'img/apple.png'
},
{
id: 2,
title: 'orange',
path: 'img/orange.png'
},
{
id: 3,
title: 'lemon',
path: 'img/lemon.png'
}]
},
methods: {
handle: function (index) {
this.hoverIndex = index
console.log(this.hoverIndex);
}
}
})
</script>
</body>
</html>
1.29 常用特性概述与表单效果
- 表单操作
- 自定义指令
- 计算属性
- 过滤器
- 侦听器
- 生命周期
表单操作
基于vue 的表单操作:input 单行文本、textarea 多行文本 、select 下拉多选 、 radio 单选框 、 checkbox 多选框
1.30 表单 input radio checkbox
<input type="text" v-model="uname">
<div>
<span>性别:</span>
<span>
<input type="radio" id="male" value="1" name="xm" v-model="gender">
<label for="male">男</label>
<input type="radio" id="female" value="2" name="xm" v-model="gender">
<label for="female" id="female">女</label>
</span>
<!-- 要是 radio 类型,必须要设置name,并且name相同 -->
<!--
vue 单选的做法是
1、给这两个input 设置 value 和 v-model
2、第一个 input value为,第二个为2
3、两个input 的v-model 的值必须相同
4、若在data 中 给v-model 变量设置的值等于谁,谁就是默认选择
-->
</div>
<div>
<span>爱好:</span>
<input type="checkbox" id="ball" value="1" v-model="habit">
<label for="ball">篮球</label>
<input type="checkbox" id="sing" value="2" v-model="habit">
<label for="sing">唱歌</label>
<input type="checkbox" id="code" value="3" v-model="habit">
<label for="code">写代码</label>
</div>
<!--
处理多项选择,data中的值必须是数组,也是根据值找与value相同的值
-->
<script>
var vm = new Vue({
el: ' #app',
data: {
uname: "呆呆狗",
gender: 1,// 若是1 则会找绑定v-model gender的输入框,找到value等于1的,2则找到2
habit: [1, 2],
},
methods: {
handle: function () {
console.log(this.uname);
console.log(this.gender);
console.log(this.habit.join(''));// 12
}
}
})
</script>
1.31 表单基本操作 select textarea
<select v-model="selectAnser">
<!-- multiple 表示多选 -->
<option value="0">请选择职业...</option>
<option value="1">教师</option>
<option value="2">软件工程师</option>
<option value="3">律师</option>
</select>
<div>
<span>个人简介:</span>
<textarea v-model="textMore"></textarea>
</div>
<script>
var vm = new Vue({
el: ' #app',
data: {
uname: "呆呆狗",
gender: 1,// 若是1 则会找绑定v-model gender的输入框,找到value等于1的,2则找到2
habit: [1, 2],
selectAnser: 2, // 单选是一个数,多选是数组
textMore: '请输入内容'
},
methods: {
handle: function () {
console.log(this.uname);
console.log(this.gender);
console.log(this.habit.join(''));// 12
}
}
})
</script>
1.32 表单域修饰符用法
- number 转化为数值
- trim 去掉开始和结尾的空格 中间的问题是去不掉的
- lazy 将input 事件切换为 change事件
<input v-model.trim="msg"></input>
v-model 默认是捆绑着 input事件的
1.33 自定义指令用法
<div id="app">
<input type="text" v-focus>
</div>
<script src="./js/vue.js"></script>
<script>
// 第一个参数是指令的名称,自定义的
Vue.directive('focus', {
inserted: function (el) {
// 第一个参数就是元素,就是指令所绑定的元素
// 获取元素的焦点
el.focus()
}
})
var vm = new Vue({
el: ' #app',
data: {
},
methods: {
}
})
</script>
1.34 自定义指令(带参数)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ffhaVaGo-1620387784174)(vue基础.assets/image-20201019163344281.png)]
<div id="app">
<input type="text" v-color="msg">
</div>
<script src="./js/vue.js"></script>
<script>
// 第一个参数是指令的名称,自定义的
Vue.directive('color', {
inserted: function (el, binding) {
// 第一个参数就是元素,就是指令所绑定的元素
el.style.backgroundColor = binding.value
}
})
var vm = new Vue({
el: ' #app',
data: {
msg: 'red'
},
methods: {
}
})
</script>
1.35 自定义局部指令
<div id="app">
<input type="text" v-color="msg">
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: ' #app',
data: {
msg: 'red'
},
methods: {
},
directives: {
color: {
inserted: function (el, binding) {
// 第一个参数就是元素,就是指令所绑定的元素
el.style.backgroundColor = binding.value
}
}
}
})
</script>
1.36 计算属性基本用法
把复杂的表达式抽取出来,使模板内容更加简洁
- 必须要有 return
- 必须写在组件中
- 是基于 data 的值进行变化的
<div id="app">
<div>{{msg}}</div>
<div>{{reverseString}}</div>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: ' #app',
data: {
msg: 'red'
},
methods: {
},
computed: {
// 第一个参数是自定义的名字
reverseString: function () {
return this.msg.split("").reverse().join("")
}
}
})
</script>
1.37 计算属性与方法的区别
- 计算属性是基于他们的依赖进行缓存的
- 方法不存在缓存
<div id="app">
<div>{{msg}}</div>
<div>{{reverseString}}</div>
<div>{{reverseString}}</div>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: ' #app',
data: {
msg: 'red'
},
methods: {
},
computed: {
// 第一个参数是自定义的名字
reverseString: function () {
console.log('执行了')
return this.msg.split("").reverse().join("")
}
}
})
</script>
上面的代码,只会输出一次 ‘执行了’,因为执行完以后,缓存中有,所以不会再执行那个函数。计算属性的值,没有发生变化的时候,是先访问缓存的值
1.38 侦听器的基本用法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3njUR25M-1620387784176)(vue基础.assets/image-20201019170218502.png)]
应用场景:数据变化时执行异步或开销较大的操作
<div id="app">
<div>
<span>名:</span>
<span>
<input type="text" v-model="firstName">
</span>
</div>
<div>
<span>姓:</span>
<span>
<input type="text" v-model="lastName">
</span>
</div>
<div>{{fullName}}</div>
</div>
<script src="./js/vue.js"></script>
<script>
var vm = new Vue({
el: ' #app',
data: {
firstName: "Jim",
lastName: "Green",
fullName: "Jim Green"
},
methods: {
},
watch: {
firstName: function (val) {
// fullName 要和变量名字一致
// val表示 firstName 最新值
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
</script>
1.39 侦听器案例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<span>用户名:</span>
<span>
<input type="text" v-model.lazy='uname'>
</span>
<span>
{{tip}}
</span>
</div>
</div>
<script src="./js/vue.js"></script>
<script>
/*
1、采用侦听器监听用户名的变化
2、如果变化,调用后台接口进行验证
3、根据验证的结果调整提示信息
*/
var vm = new Vue({
el: '#app',
data: {
uname: '',
tip: '',
},
methods: {
checkname: function (uname) {
// 调用接口,但是可以使用定时任务的方式模拟接口调用
// 定时器中的this指向的是window
var that = this;
setTimeout(function () {
// 模拟接口调用
if (uname == 'admin') {
that.tip = '用户名已经存在,请更换用户名'
}
else {
that.tip = '用户名可以使用'
}
}, 2000)
}
},
watch: {
uname: function (val) {
// 调用后台接口进行验证用户名的合法性
this.checkname(val);
// 修改提示信息
this.tip = '正在验证'
}
}
})
</script>
</body>
</html>
1.40 过滤器基本用法
格式化数据,比如将字符串格式首字母大写、日期格式化等
一个插值表达式,一个属性绑定
<div>{{msg | 过滤器名称}}</div>
<div v-bind:id="id | 过滤器名称"></div>
Vue.filter('过滤器名称',function(value){
// 过滤器业务逻辑
})
//也可以局部定义
<div id="app">
<input type="text" v-model="msg">
<div>{{msg|upper}}</div>
</div>
<script src="./js/vue.js"></script>
<script>
Vue.filter('upper', function (val) {
// 获取到的值
return val.charAt(0).toUpperCase() + val.slice(1)
})
var vm = new Vue({
el: ' #app',
data: {
msg: ''
},
filters: {
upper: function (val) {
// 名称,
return val.charAt(0).toUpperCase() + val.slice(1)
}
}
})
</script>
1.41 带参数的过滤器
Vue.filter('过滤器名称',function(val,arg1,arg2){
// val 必须是第一个,他表示传递过来的要处理的参数
// arg 是其他的参数
})
<div id="app">
<div>{{date | format('yyyy-MM-dd')}}</div>
</div>
<script src="./js/vue.js"></script>
<script>
Vue.filter('format', function (val, arg) {
// 这里的arg 指的就是 'yyyy-MM-dd'
if (arg == 'yyyy-MM-dd') {
var ret = ''
ret += val.getFullYear() + '-' + (val.getMonth() + 1) + '-' + val.getDate()
return ret
}
})
var vm = new Vue({
el: ' #app',
data: {
date: new Date()
},
filters: {
}
})
</script>
1.42 vue实例的声明周期
创造前后 => 安装前后 =>更新前后 =>销毁前后
挂载包括:创造和安装
- 挂载(初始化相关属性)
- beforeCreate
- created
- beforeMount
- mounted
- 更新(元素或组件的变更操作)
- beforeUpdate
- updated
- 销毁(销毁相关属性)
- beforeDestroy
- destroyed
vue实例的产生过程
- beforeCreate 在实例初始化之后,数据观测和时间配置之前被调用
- created 在实例创建完成后被立即调用
- beforeMount 在挂载开始之前被调用
- mounted el被新创建的 vm.$el 替换 。并挂载到实例上去之后调用该钩子
- beforeUpdate 数据更新时调用,发生在虚拟DOM打补丁之前
- updated 由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子
- beforeDestr0y 实例销毁之前调用
- destroyed 实例销毁后调用
<body>
<div id="app">
<div>{{msg}}</div>
<button @click='update'>更新</button>
<button @click='destroy'>销毁</button>
</div>
<script src="./js/vue.js"></script>
<script type="text/javascript">
/*
Vue实例的生命周期
*/
var vm = new Vue({
el: '#app',
data: {
msg: '生命周期'
},
methods: {
update: function () {
this.msg = 'hello world';
},
destroy: function () {
this.$destroy();
}
},
// 挂载阶段 初始化相关属性,例如 watch ,methods
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>
</body>
2、图书馆综合案例
2.1 关于数组响应式的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ltLunJrQ-1620387784177)(file://F:/web%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91/%E9%BB%91%E9%A9%AC%E8%87%AA%E5%AD%A6/vue/B%E7%AB%99/B%E7%AB%99vue%E7%AC%94%E8%AE%B0.assets/image-20200801140707017.png?lastModify=1603112008)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TmlOXKXI-1620387784178)(vue基础.assets/image-20200801141548596.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R56otYHh-1620387784179)(vue基础.assets/image-20200801142355344.png)]
在VUE中用索引修改的数据不是响应式变化 的,上面的两个方法,可以用在数组和对象身上
2.2 图书案例源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.grid {
margin: auto;
width: 530px;
text-align: center;
}
.grid table {
border-top: 1px solid #C2D89A;
width: 100%;
border-collapse: collapse;
}
.grid th,
td {
padding: 10;
border: 1px dashed #F3DCAB;
height: 35px;
line-height: 35px;
}
.grid th {
background-color: #F3DCAB;
}
.grid .book {
padding-bottom: 10px;
padding-top: 5px;
background-color: #F3DCAB;
}
.total {
height: 30px;
line-height: 30px;
background-color: orangered;
}
</style>
</head>
<body>
<div id="app">
<div class="grid">
<div>
<h1>图书管理</h1>
<div class="book">
<div>
<label for="id">
编号:
</label>
<input type="text" id="id" v-model='id' :disabled="flag" v-focus>
<label for="name">
名称:
</label>
<input type="text" id="name" v-model='name'>
<button @click='handle' :disabled='submitFlag'>提交</button>
</div>
</div>
</div>
<div class="total">
<span>图书总数:</span>
<span>{{total}}</span>
</div>
<table>
<thead>
<tr>
<th>编号</th>
<th>名称</th>
<th>时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr :key='item.id' v-for='item in books'>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date | format('yyyy-MM-dd hh:mm:ss')}}</td>
<td>
<a href="" @click.prevent='toEdit(item.id)'>修改</a>
<span>|</span>
<a href="" @click.prevent='deleteBook(item.id)'>删除</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.directive('focus', {
inserted: function (el) {
el.focus();
}
})
Vue.filter('format', function (value, arg) {
function dateFormat(date, format) {
if (typeof date === "string") {
var mts = date.match(/(\/Date\((\d+)\)\/)/);
if (mts && mts.length >= 3) {
date = parseInt(mts[2]);
}
}
date = new Date(date);
if (!date || date.toUTCString() == "Invalid Date") {
return "";
}
var map = {
"M": date.getMonth() + 1, //月份
"d": date.getDate(), //日
"h": date.getHours(), //小时
"m": date.getMinutes(), //分
"s": date.getSeconds(), //秒
"q": Math.floor((date.getMonth() + 3) / 3), //季度
"S": date.getMilliseconds() //毫秒
};
format = format.replace(/([yMdhmsqS])+/g, function (all, t) {
var v = map[t];
if (v !== undefined) {
if (all.length > 1) {
v = '0' + v;
v = v.substr(v.length - 2);
}
return v;
} else if (t === 'y') {
return (date.getFullYear() + '').substr(4 - all.length);
}
return all;
});
return format;
}
return dateFormat(value, arg);
})
var vm = new Vue({
el: '#app',
data: {
submitFlag: false,
flag: false,
id: '',
name: '',
books: [{
id: 1,
name: '三国演义',
date: 2525609975000
}, {
id: 2,
name: '水浒传',
date: 2525609975000
}, {
id: 3,
name: '红楼梦',
date: 2525609975000
}, {
id: 4,
name: '西游记',
date: 2525609975000
}]
},
methods: {
handle: function () {
if (this.flag) {
// 编辑图书
// 就是根据当前的ID去更新数组中对应的数据
this.books.some((item) => {
if (item.id == this.id) {
item.name = this.name;
// 完成更新操作之后,需要终止循环
return true;
}
});
this.flag = false;
} else {
// 添加图书
var book = {};
book.id = this.id;
book.name = this.name;
book.date = '';
this.books.push(book);
// 清空表单
this.id = '';
this.name = '';
}
// 清空表单
this.id = '';
this.name = '';
},
toEdit: function (id) {
// 禁止修改ID
this.flag = true;
console.log(id)
// 根据ID查询出要编辑的数据
var book = this.books.filter(function (item) {
return item.id == id;
});
console.log(book)
// 把获取到的信息填充到表单
this.id = book[0].id;
this.name = book[0].name;
},
deleteBook: function (id) {
// 1、根据ID 查找元素的索引
var index = this.books.findIndex(function (item) {
return item.id == id;
});
this.books.splice(index, 1);
/* var book = this.books.filter(function (item) {
return item.id != id;
})
console.log(book); */
}
},
computed: {
total: function () {
return this.books.length
}
},
watch: {
name: function (val) {
var flag = this.books.some(function (item) {
return item.name == val
})
if (flag) {
this.submitFlag = true;
}
else {
this.submitFlag = false
}
}
}
});
</script>
</body>
</html>
3、vue组件
3.1 组件化开发概述
- 标准
- 分治
- 重用
- 组合
组件化规范 web conmponents
- 我们希望尽可能多的重用代码
- 自定义组件的方式也不大容易 html css js
- 多次使用组件可能导致冲突
vue 部分已经实现了上述规范
3.2 组件化基本使用
Vue.component(组件名称,{
data:组件数组,
template:组件模板内容
})
- 组件可以重复调用
- 每个组件的数据互不影响 各自独立
<div id="app">
<button-counter></button-counter>
</div>
<script src="./js/vue.js"></script>
<script>
/* Vue.component(组件名称,{
data:组件数组,
template:组件模板内容
}) */
Vue.component('button-counter', {
data: function () {
// data 里面放的是 组件的数据,是一个函数,return 返回一个对象,把数据放在这个对象里面
return {
count: 0
}
},
template: '<button @click="handle">点击了{{count}}</button> ',
methods: {
handle: function () {
this.count++
}
}
})
var vm = new Vue({
el: '#app',
data: {
}
})
</script>
3.3 组件注册 注意事项 上
- data 必须是一个函数
- 组件模板内容必须是单个根元素(最外层不能有兄弟元素,比如 ’ ',这样就是错误的)
- 组件模板内容可以是模板字符串
3.4 组件注册 注意事项 下 组件命名方式
组件命名的方式
1、短横线方式
Vue.component('my-component',{……})
2、驼峰方式
Vue.component('MyComponent',{……})
- 如果使用驼峰式命名组件,那么只能在字符串模板中 用 驼峰命名的方式使用组件
- 驼峰命名的组件,不能出现在 vue 实例的根元素中
<div id="app">
<button-counter></button-counter>
<!-- 上面的写法 正确 -->
<HelloContent></HelloContent>
<!-- 上面的写法就是错误 -->
</div>
3.5 局部组件注册方式
局部组件只能再注册它的父组件中使用,不能在全局组件中使用
<div id="app">
<hello-world></hello-world>
<hello-tom></hello-tom>
<hello-jerry></hello-jerry>
<!--
hello world
hello tom
hello jerry
-->
</div>
<script src="./js/vue.js"></script>
<script>
var HelloWorld = {
data: function () {
return {
msg: 'hello world'
}
},
template: `<div>{{msg}}</div> `
}
var HelloTom = {
data: function () {
return {
msg: 'hello tom'
}
},
template: `<div>{{msg}}</div> `
}
var HelloJerry = {
data: function () {
return {
msg: 'hello Jerry'
}
},
template: `<div>{{msg}}</div> `
}
var vm = new Vue({
el: '#app',
data: {
},
components: {
'hello-world': HelloWorld,
'hello-tom': HelloTom,
'hello-jerry': HelloJerry,
}
})
</script>
3.6 父组件向子组件传值-基本用法
props 是单项数据流,只允许 父组件给子组件传值
子组件通过 props 接收父组件传递过来的值
父组件通过 属性值 将值传递给子组件
Vue.component('menu-item',{
props:['title'],
template:'<div>{{title}}</div>'
})
<menu-item title="来自父组件的数据"></menu-item>
<menu-item :title="title"></menu-item>
<body>
<div id="app">
<!-- 在这里,app 就相当于是 父组件 menu-item 就是子组件-->
<div>{{pmsg}}</div>
<menu-item title="来自父组件的值"></menu-item>
<menu-item :title="ptitle"></menu-item>
<div>传多个值</div>
<menu-item :title="ptitle" content="hello"></menu-item>
<!--
父组件的内容
子组件本身的数据---来自父组件的值-- - undefined
子组件本身的数据---动态绑定属性-- - undefined
传多个值
子组件本身的数据---动态绑定属性-- - hello
-->
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component('menu-item', {
props: ['title', 'content'],
data: function () {
return {
msg: '子组件本身的数据',
}
},
template: '<div>{{msg +"---"+title+"-- - "+content}}</div>'
})
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件的内容',
ptitle: '动态绑定属性'
},
})
</script>
</body>
3.7 父组件向子组件传值 props 属性命名规则
- 在 props 中使用 驼峰形式,在html标签中必须使用 短横线的形式
- 字符串形式的模板中没有这个限制
//在html中是短横线方式的
<menu-item menu-title="你好"></menu-item>
Vue.conponents('menu-item',{
// 在 JavaScript 中是驼峰形式的
props:['menuTitle'],
template:'<div>{{menuTitle}}</div>'
})
<body>
<div id="app">
<!-- 在这里,app 就相当于是 父组件 menu-item 就是子组件-->
<div>{{pmsg}}</div>
<menu-item my-title="来自父组件的值"></menu-item>
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component('menu-item', {
props: ['MyTitle', 'content'],
data: function () {
return {
msg: '子组件本身的数据',
}
},
template: '<div>{{msg +"---"+MyTitle}}</div>'
})
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件的内容',
ptitle: '动态绑定属性'
},
})
</script>
</body>
3.8 父组件向子组件传值 props 属性值类型 必须加:
- 字符串
- 数值
- 布尔值
- 数组
- 对象
布尔和数值 通过 v-bind 绑定 得到到都是对应的类型,不是通过v-bind 则得到的都是字符串类型
建议传值采用 v-bind进行传值,并且等号右边加引号
<body>
<div id="app">
<!-- 在这里,app 就相当于是 父组件 menu-item 就是子组件-->
<div>{{pmsg}}</div>
<!--
建议 采用 v-bind:href="" 并且值用引号
对于 数字类型和布尔类型的数据
去掉冒号 传递过去的是字符串
加上冒号 传递过去的是数值类型
-->
<menu-item :pstr='pstr' :pnum='12' :pboo='true' :parr='parr' :pobj='pobj'></menu-item>
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component('menu-item', {
props: ['pstr', 'pnum', 'pboo', 'parr', 'pobj'],
data: function () {
return {
msg: '子组件本身的数据',
}
},
template: `
<div>
<div>{{pstr}}</div>
<div>{{pnum}}</div>
<div>{{pboo}}</div>
<ul>
<li v-for='(item,index) in parr'>{{item}}</li>
</ul>
<div>
<span>{{pobj.name}}</span>
<span>{{pobj.age}}</span>
</div>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件的内容',
pstr: 'hello',
parr: ['apple', 'orange', 'banner'],
pobj: {
name: 'lisi',
age: 12,
}
},
})
</script>
</body>
3.9 子组件向父组件传值
<body>
<div id="app">
<div :style="{fontSize:fontsize+'px'}">{{msg}}</div>
<menu-item @click-fontsize="handle"></menu-item>
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component('menu-item', {
template: `
<button @click='$emit("click-fontsize")''>点击我 扩大父组件的字体大小</button>
`
})
var vm = new Vue({
el: '#app',
data: {
msg: '我是父组件内容',
fontsize: 14
},
methods: {
handle: function () {
this.fontsize += 5
}
}
})
</script>
</body>
3.10 子组件向父组件传值,携带参数
<body>
<div id="app">
<div :style="{fontSize:fontsize+'px'}">{{msg}}</div>
<menu-item @click-fontsize="handle($event)"></menu-item>
<!-- $event这个名字是固定的 -->
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component('menu-item', {
template: `
<button @click='$emit("click-fontsize",10)'>点击我 扩大父组件的字体大小</button>
`
// $emit('第一个参数是事件名称','第二个参数是值')
})
var vm = new Vue({
el: '#app',
data: {
msg: '我是父组件内容',
fontsize: 14
},
methods: {
handle: function (val) {
this.fontsize += val
}
}
})
</script>
</body>
<body>
<div id="app">
<div :style="{fontSize:fontsize+'px'}">{{msg}}</div>
<menu-item @change-zi="handle($event)"></menu-item>
<!-- $event这个名字是固定的 -->
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component('menu-item', {
template: `
<button @click='handle'>点击我 扩大父组件的字体大小</button>
`
,
// $emit('第一个参数是事件名称','第二个参数是值')
methods: {
handle: function () {
this.$emit('change-zi', {
"first": 10,
"second": 20,
"third": 30,
})
}
}
})
var vm = new Vue({
el: '#app',
data: {
msg: '我是父组件内容',
fontsize: 14
},
methods: {
handle: function (val) {
console.log(val);
// {first: 10, second: 20, third: 30}
this.fontsize += val.first
}
}
})
</script>
</body>
3.11 兄弟组件之间的传值
1、单独的事件中心管理组件间的通信
var eventHub = new Vue()
2、监听事件与销毁事件
eventHub.$on('add-todo',addTodo)
eventHub.$off('add-todo')
3、触发事件
eventHub.$emit('add-todo',id)
<body>
<div id="app">
<div>{{msg}}</div>
<test-tom></test-tom>
<test-jerry></test-jerry>
<div @click='handle'>销毁</div>
</div>
<script src="./js/vue.js"></script>
<script>
// 提供事件中心
var hub = new Vue();
Vue.component('test-tom', {
data: function () {
return {
num: 0
}
},
template: `
<div>
<div>TOM:{{num}}</div>
<div>
<button @click='handle'>点击我控制 jerry</button>
</div>
</div>
`,
mounted: function () {
// 监听事件
hub.$on('tom-event', (val) => {
// val 是 兄弟组件传过来的
this.num += val
})
},
methods: {
handle: function () {
// 触发兄弟组件的事件
// 要传给兄弟 组件 2
hub.$emit('jerry-event', 2)
}
}
})
Vue.component('test-jerry', {
data: function () {
return {
num: 0
}
},
template: `
<div>
<div>JERRY:{{num}}</div>
<div>
<button @click='handle'>点击我控制 tom</button>
</div>
</div>
`,
mounted: function () {
// 监听事件
hub.$on('jerry-event', (val) => {
// val 是 兄弟组件传过来的
this.num += val
})
},
methods: {
handle: function () {
// 触发兄弟组件的事件
hub.$emit('tom-event', 1)
}
}
})
var vm = new Vue({
el: '#app',
data: {
msg: '我是父组件内容',
},
methods: {
handle: function () {
hub.$off('tom-event')
hub.$off('jerry-event')
}
}
})
</script>
</body>
3.12 组件插槽基本用法
父组件向子组件传递 内容
<body>
<div id="app">
<test-item>插槽的内容</test-item>
<!-- 标签中间的值,会传递给 slot 标签,并且覆盖掉slot标签设置的值-->
<!-- 如果标签中间的值,没有内容,那么会显示 slot的值 -->
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component('test-item', {
template: `
<div>
<b>报错</b>
<slot>222</slot>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
3.13 具名插槽用法
<body>
<div id="app">
<test-item>
<p slot="header">标题信息</p>
<p>主体信息1</p>
<p>主体信息2</p>
<p slot="footer">底部信息</p>
</test-item>
<!--
结果
header 包含 一个 标题信息
main 包含两个 p
footer 包含 一个 底部信息
-->
<!-- 更高级的用法 -->
<!-- template 标签只是起一个临时性包裹的作用 -->
<test-item>
<template slot="header">
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<p>主体信息1</p>
<p>主体信息2</p>
<template slot="footer">
<p>底部信息1</p>
<p>底部信息2</p>
</template>
</test-item>
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component('test-item', {
template: `
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
3.14 作用域插槽
父组件对子组件的内容进行加功处理
<body>
<div id="app">
<test-item :list='list'>
<template slot-scope='slotProps'>
<!-- 这个名字 是自定义的 -->
<b v-if='slotProps.info.id==2'>{{slotProps.info.name}}</b>
<span v-else>{{slotProps.info.name}}</span>
<!-- slotProps 可以得到子组件传递来的数据 -->
</template>
</test-item>
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component('test-item', {
props: ['list'],
template: `
<div>
<li v-for='(item,index) in list'>
<slot :info='item'>{{item.name}}</slot>
</li>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
list: [{
id: 1,
name: 'apple'
}, {
id: 2,
name: 'orange'
}, {
id: 3,
name: 'banner'
},]
}
})
</script>
</body>
4、购物车案例
4.1 购物车结算页面
组件化开发
1、标题组件(展示文本)
2、列表组件(列表展示、商品数量变更、商品删除)
3、结算组件(计算商品总额)
4.2 组件化布局
1、功能实现步骤
- 实现整体布局和样式效果
- 划分独立的功能组件
- 组合所有的子组件形成整体结构
- 逐个实现各个组件功能
- 标题组件
- 列表组件
- 结算组件
<body>
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="./js/vue.js"></script>
<script type="text/javascript">
var CartTile = {
template: `
<div class="title">我的商品</div>
`
}
var CartList = {
template: `
<div>
<div class="item">
<img src="img/a.jpg" />
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/b.jpg" />
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/c.jpg" />
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/d.jpg" />
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/e.jpg" />
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
</div>
`
}
var CartTotal = {
template: `
<div class="total">
<span>总价:123</span>
<button>结算</button>
</div>
`
}
Vue.component('my-cart', {
template: `
<div class="cart">
<cart-title></cart-title>
<cart-list></cart-list>
<cart-total></cart-total>
</div>
`,
components: {
'cart-title': CartTile,
'cart-list': CartList,
'cart-total': CartTotal,
}
})
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
4.3 实现标题和结算组件功能
实现的功能 标题 和 总价计算
<script type="text/javascript">
var CartTile = {
// 得到父组件传递来的数据
props: ['username'],
template: `
<div class="title">{{username}}的商品</div>
`
}
var CartList = {
template: `
<div>
<div class="item">
<img src="img/a.jpg" />
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/b.jpg" />
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/c.jpg" />
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/d.jpg" />
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/e.jpg" />
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
</div>
`
}
var CartTotal = {
props: ['list'],
// 获取父组件传来的 list 列表
template: `
<div class="total">
<span>总价:{{returnPrice}}</span>
<button>结算</button>
</div>
`,
methods: {
},
computed: {
returnPrice: function () {
var sum = 0
this.list.forEach((value, index) => {
sum += value.price * value.num
});
return sum
}
}
}
Vue.component('my-cart', {
data: function () {
return {
username: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
}, {
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
}, {
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
}, {
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
}, {
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
// list 商品列表
}
},
template: `
<div class="cart">
<cart-title :username='username'></cart-title>
<cart-list></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTile,
'cart-list': CartList,
'cart-total': CartTotal,
}
})
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
4.4 删除商品功能
- 父组件给子组件传递list 列表,商品采用 v-for 渲染list列表
- 删除 数据,应该在父组件进行,在子组件中拿到id,把id 传递给父组件
var CartList = {
props: ['list'],
methods: {
del: function (id) {
// 把 id 传递给父组件
this.$emit('cart-del', id)
}
},
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img" />
<div class="name">{{item.name}}</div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del" @click.prevent='del(item.id)'>×</div>
</div>
</div>
`
}
Vue.component('my-cart', {
data: function () {
return {
username: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
}, {
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
}, {
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
}, {
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
}, {
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
// list 商品列表
}
},
template: `
<div class="cart">
<cart-title :username='username'></cart-title>
<cart-list :list='list' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTile,
'cart-list': CartList,
'cart-total': CartTotal,
},
methods: {
delCart: function (id) {
// 根据 id 删除 list对应的数据
// 1、根据 id 找到对应的索引
var index = this.list.findIndex(item => {
return item.id == id
})
// 2、根据 索引 删除
this.list.splice(index, 1)
}
}
})
4.5 实现列表组件更新商品功能 上 emit传递多个参数
注意 当 emit 需要传递多个参数的时候,可以用对象的形式
var CartList = {
props: ['list'],
methods: {
del: function (id) {
// 把 id 传递给父组件
this.$emit('cart-del', id)
},
changeNum: function (id, event) {
// event.target.value 可以获取当前项输入的值
this.$emit('input-num', {
id: id,
num: event.target.value,
})
}
},
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img" />
<div class="name">{{item.name}}</div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" :value='item.num' @blur='changeNum(item.id,$event)' />
<a href="">+</a>
</div>
<div class="del" @click.prevent='del(item.id)'>×</div>
</div>
</div>
`
Vue.component('my-cart', {
data: function () {
return {
username: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
}, {
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
}, {
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
}, {
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
}, {
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
// list 商品列表
}
},
template: `
<div class="cart">
<cart-title :username='username'></cart-title>
<cart-list :list='list' @cart-del='delCart($event)' @input-num='inputChange($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTile,
'cart-list': CartList,
'cart-total': CartTotal,
},
methods: {
delCart: function (id) {
// 根据 id 删除 list对应的数据
// 1、根据 id 找到对应的索引
var index = this.list.findIndex(item => {
return item.id == id
})
// 2、根据 索引 删除
this.list.splice(index, 1)
},
inputChange: function (numObj) {
var index = this.list.findIndex(item => {
return item.id == numObj.id
})
this.list[index].num = numObj.num
}
}
})
4.6 实现列表组件更新商品功能 下 加号 减号 emit传递多个参数
一个事件,包含三个处理程序
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.container {}
.container .cart {
width: 300px;
/*background-color: lightgreen;*/
margin: auto;
}
.container .title {
background-color: lightblue;
height: 40px;
line-height: 40px;
text-align: center;
/*color: #fff;*/
}
.container .total {
background-color: #FFCE46;
height: 50px;
line-height: 50px;
text-align: right;
}
.container .total button {
margin: 0 10px;
background-color: #DC4C40;
height: 35px;
width: 80px;
border: 0;
}
.container .total span {
color: red;
font-weight: bold;
}
.container .item {
height: 55px;
line-height: 55px;
position: relative;
border-top: 1px solid #ADD8E6;
}
.container .item img {
width: 45px;
height: 45px;
margin: 5px;
}
.container .item .name {
position: absolute;
width: 90px;
top: 0;
left: 55px;
font-size: 16px;
}
.container .item .change {
width: 100px;
position: absolute;
top: 0;
right: 50px;
}
.container .item .change a {
font-size: 20px;
width: 30px;
text-decoration: none;
background-color: lightgray;
vertical-align: middle;
}
.container .item .change .num {
width: 40px;
height: 25px;
}
.container .item .del {
position: absolute;
top: 0;
right: 0px;
width: 40px;
text-align: center;
font-size: 40px;
cursor: pointer;
color: red;
}
.container .item .del:hover {
background-color: orange;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="./js/vue.js"></script>
<script type="text/javascript">
var CartTile = {
// 得到父组件传递来的数据
props: ['username'],
template: `
<div class="title">{{username}}的商品</div>
`
}
var CartList = {
props: ['list'],
methods: {
del: function (id) {
// 把 id 传递给父组件
this.$emit('cart-del', id)
},
changeNum: function (id, event) {
// event.target.value 可以获取当前项输入的值
this.$emit('change-num', {
type: 'change',
id: id,
num: parseInt(event.target.value),
})
},
numAdd: function (id) {
this.$emit('change-num', {
id: id,
type: 'add'
})
},
numSub: function (id) {
this.$emit('change-num', {
id: id,
type: 'sub'
})
},
},
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img" />
<div class="name">{{item.name}}</div>
<div class="change">
<a href="" @click.prevent='numSub(item.id)'>-</a>
<input type="text" class="num" :value='item.num' @blur='changeNum(item.id,$event)' />
<a href="" @click.prevent='numAdd(item.id)'>+</a>
</div>
<div class="del" @click.prevent='del(item.id)'>×</div>
</div>
</div>
`
}
var CartTotal = {
props: ['list'],
// 获取父组件传来的 list 列表
template: `
<div class="total">
<span>总价:{{returnPrice}}</span>
<button>结算</button>
</div>
`,
methods: {
},
computed: {
returnPrice: function () {
var sum = 0
this.list.forEach((value, index) => {
sum += value.price * value.num
});
return sum
}
}
}
Vue.component('my-cart', {
data: function () {
return {
username: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
}, {
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
}, {
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
}, {
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
}, {
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
// list 商品列表
}
},
template: `
<div class="cart">
<cart-title :username='username'></cart-title>
<cart-list :list='list' @cart-del='delCart($event)' @change-num='inputChange($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTile,
'cart-list': CartList,
'cart-total': CartTotal,
},
methods: {
delCart: function (id) {
// 根据 id 删除 list对应的数据
// 1、根据 id 找到对应的索引
var index = this.list.findIndex(item => {
return item.id == id
})
// 2、根据 索引 删除
this.list.splice(index, 1)
},
inputChange: function (numObj) {
// 我们要区别这三种类型
// 输入域变更
if (numObj.type == 'change') {
var index = this.list.findIndex(item => {
return item.id == numObj.id
})
this.list[index].num = numObj.num
}
else if (numObj.type == 'sub') {
var index = this.list.findIndex(item => {
return item.id == numObj.id
})
this.list[index].num -= 1
}
else if (numObj.type == 'add') {
var index = this.list.findIndex(item => {
return item.id == numObj.id
})
this.list[index].num += 1
}
// 加号 变更
// 减号变更
}
}
})
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
5、Promise
5.1 学习目标
- 能够说出什么是前后端交互模式
- 能够说出 promise 的相关概念和用法
- 能够使用 fetch 进行接口调用 标准化组织提出的
- 能够使用 axios 进行接口调用 第三方的库
- 能够使用 async / await 方式调用接口 es7
- 能够基于后台接口实现案例
5.2 前后端交互概述与 URL 地址格式
1、接口调用方式
- 原生Ajax
- 基于 jQuery 的Ajax 侧重DOM
- fetch Ajax的升级版,也是标准化组织的新规范
- axios比fetch 更加强大
2、 url地址格式
协议、端口、域名、地址、查询参数、锚点
5.3 异步编程问题与 Promise 概述
- 异步效果分析
- 定时任务
- Ajax
- 事件函数
- 多次异步调用的依赖分析
- 多次异步调用的 结果顺序不确定,需要根据相应的因素
- 异步调用结果如果存在依赖,需要进行嵌套,容易出现回调地狱!
$.ajax({
ulr:'http://www1',
success:function(res){
console.log('1')
}
})
$.ajax({
ulr:'http://www1',
success:function(res){
console.log('1')
}
})
$.ajax({
ulr:'http://www1',
success:function(res){
console.log('1')
}
})
// 他们三个的输出 顺序不会固定,若请求的慢,则输出的 相对而言比较晚
Promise 是异步编程的一种解决方案,从语法上将,Promise 是一个对象,从它可以获取异步操作的消息
- 可以避免多层异步调用 嵌套问题(回调地狱)
- Promise 对象提供了简洁的API ,使得控制异步操作更加容易
5.4 Promise 基本用法
- 实例化 Promise 对象,构造函数中传递函数,该函数中用于处理异步任务
- resolve 和 reject 两个参数用于处理成功和失败两种情况,并通过 p.then 获取处理结果
var p = new Promise(function(resolve,reject){
// 成功 调用 resolve()
// 失败 调用 reject()
})
p.then(function(ret){
// 从 resolve 得到的 成功的结果
},function(ret){
// 从 reject 得到的 失败的结果
})
var p = new Promise(function (resolve, reject) {
// 成功 调用 resolve()
// 失败 调用 reject()
// 这里用于实现 异步任务
setTimeout(function () {
var flag = true
if (flag) {
// 正常情况
resolve('Hello')
}
else {
reject('出错了')
}
}, 1000)
})
p.then(function (ret) {
// 从 resolve 得到的 成功的结果
console.log(ret);
}, function (ret) {
// 从 reject 得到的 失败的结果
console.log(ret);
})
// 最后输出的结果是 hello
5.5 Promise 发送 Ajax请求并处理回调地狱问题
function queryData(url) {
var p = new Promise(function (resolve, reject) {
// 成功 调用 resolve()
// 失败 调用 reject()
// 这里用于实现 异步任务
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) {
return
}
if (xhr.readyState == 4 && xhr.status == 200) {
// 处理正常的情况
resolve(xhr.responseText)
} else {
// 处理异常情况
reject('服务器错误')
}
};
xhr.open('get', url)
xhr.send(null)
})
return p
}
queryData('http://127.0.0.1:3000/data')
.then(function (ret) {
// 从 resolve 得到的 成功的结果
console.log(ret);
return queryData('http://127.0.0.1:3000/data1')
}, function (ret) {
// 从 reject 得到的 失败的结果
console.log(ret);
})
.then(function (ret) {
console.log(ret); // data1 的返回结果
})
5.6 Promise 的 then方法参数中的函数的返回值
then 参数中的函数返回值
1、返回 Promise 实例对象
返回的该实例对象会调用 下一个then
2、返回普通值
返回的普通值 会直接传递给 下一个then,通过 then 参数中函数的参数接收该值
function queryData(url) {
var p = new Promise(function (resolve, reject) {
// 成功 调用 resolve()
// 失败 调用 reject()
// 这里用于实现 异步任务
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) {
return
}
if (xhr.readyState == 4 && xhr.status == 200) {
// 处理正常的情况
resolve(xhr.responseText)
} else {
// 处理异常情况
reject('服务器错误')
}
};
xhr.open('get', url)
xhr.send(null)
})
return p
}
queryData('http://127.0.0.1:3000/data')
.then(function (ret) {
// 从 resolve 得到的 成功的结果
console.log(ret);
return queryData('http://127.0.0.1:3000/data1')
}, function (ret) {
// 从 reject 得到的 失败的结果
console.log(ret);
})
.then(function (ret) {
console.log(ret); // data1 的返回结果
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(111111)
}, 1000)
})
})
.then(function (data) {
console.log(data);// 111
return 'Hello'
// 返回普通值,默认的 promise 实例对象 继续调用
})
.then(function (data) {
console.log(data); // Hello
})
5.7 Promise 常用的API
1、实例方法 都在 Promise的原型中
- p.then() 得到异步任务的正确结果
- p.catch() 获取异常信息
- p.finally() 成功与否都会执行
catch 是可以扑捉到 resolve reject 的异常; reject 只能扑捉到 reject的异常
function queryData(url) {
var p = new Promise(function (resolve, reject) {
// 成功 调用 resolve()
// 失败 调用 reject()
// 这里用于实现 异步任务
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) {
return
}
if (xhr.readyState == 4 && xhr.status == 200) {
// 处理正常的情况
resolve(xhr.responseText)
} else {
// 处理异常情况
reject('服务器错误')
}
};
xhr.open('get', url)
xhr.send(null)
})
return p
}
queryData('http://127.0.0.1:3000/data11')
.then(function (ret) {
// 从 resolve 得到的 成功的结果
console.log(ret);
})
.catch(function (ret) {
// 获取异常信息
// catch 是可以扑捉到 resolve reject 的异常
// reject 只能扑捉到 reject的异常
console.log(ret);
})
.finally(function () {
// 不论执行失败还是成功,都会执行
console.log('执行完毕');
})
5.8 Promise 常用 API 对象方法
直接通过函数名称调用的,在Promise 里面的
- Promise.call() 并发处理多个异步任务,所有任务都执行完成才能得到结果
- Promise.race() 并发处理多个异步任务,只要有一个任务完成就能得到结果
function queryData(url) {
var p = new Promise(function (resolve, reject) {
// 成功 调用 resolve()
// 失败 调用 reject()
// 这里用于实现 异步任务
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) {
return
}
if (xhr.readyState == 4 && xhr.status == 200) {
// 处理正常的情况
resolve(xhr.responseText)
} else {
// 处理异常情况
reject('服务器错误')
}
};
xhr.open('get', url)
xhr.send(null)
})
return p
}
var p1 = queryData('http://localhost:3000/a1')
var p2 = queryData('http://localhost:3000/a2')
var p3 = queryData('http://localhost:3000/a3')
Promise.all([p1, p2, p3]).then(function (results) {
console.log(results);
// ["Hello TOM!", "Hello JERRY!", "Hello SPIKE!"]
})
Promise.race([p1, p2, p3]).then(function (results) {
console.log(results);
// Hello Tom
// 请求都发出去了,只会得到 最先返回的结果
})
6、fetch
6.1 fetch 概述
- 更加简单的数据获取方式,功能更加强大,可以看作是 xhr的升级版
- 基于 Promise 实现
语法结构
fetch(url).then(fn2).then(fn3)…….catch(fn)
<script>
fetch('http://localhost:3000/fdata').then(function (data) {
// 这里的data 不能直接拿到数据
// text 方法 是 fetchAPI 的一部分
// text 用户获取后台返回的数据
return data.text()
// data.text() 返回的是 Promise实例对象
}).then(function (data) {
console.log(data);
})
</script>
6.2 fetch 的 get 和 post 请求
1、常用配置选项
- method(String) HTTP请求方法,默认为 get
- body(String) HTTP 的请求参数
- headers(Object) http 的请求头,默认为 {}
delete 和 get 请求方式,大体一样,只是 method 不同
fetch('http://localhost:3000/books?id=123', {
method: 'get',
}).then(function (data) {
// 这里的data 不能直接拿到数据
return data.text()
// data.text() 返回的是 Promise实例对象
}).then(function (data) {
console.log(data);
})
// 第二种方式
fetch('http://localhost:3000/books/123', {
method: 'get',
}).then(function (data) {
// 这里的data 不能直接拿到数据
return data.text()
// data.text() 返回的是 Promise实例对象
}).then(function (data) {
console.log(data);
})
6.3 fetch 的post put
post put 需要设置额外的 body 和 headers
fetch('http://localhost:3000/books', {
method: 'post',
body: 'uname=lisi&pwd=123',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
// 也可以设置成 json 格式,前提是 把body的内容转化为 json的数据
}
}).then(function (data) {
// 这里的data 不能直接拿到数据
return data.text()
// data.text() 返回的是 Promise实例对象
}).then(function (data) {
console.log(data);
})
fetch 响应结果
- text() 将返回体处理成字符串类型
- json() 将返回结果 和 JSON.parse(response Text) 一样
fetch('http://localhost:3000/json', {
}).then(function (data) {
// 这里的data 不能直接拿到数据
return data.json()
// 不写 json 写 text 返回的是字符串
// data.text() 返回的是 Promise实例对象
}).then(function (data) {
console.log(data);
//{uname: "lisi", age: 13, gender: "male"}
})
7、axios
7.1 概述
axios(官网:https://github.com/axios/axios/)是一个基于 Promise 用于浏览器 和 node.js 的 HTTP客户端
- 支持浏览器和 node.js
- 支持 promise
- 能拦截请求和响应
- 自动转换 JSON 数据
axios.get('http://localhost:3000/adata').then(function (ret) {
// ret.data 才是后台接口返回的数据,data 是固定的属性,用于获取接口返回的实际数据
console.log(ret);
console.log(ret.data);
})
7.2 axios get传参
get
- 通过 url直接传递
- 通过 params 传参
- 第一种 http://localhost/axios/123
- 第二种,通过对象方式传参 这种更常用
axios.get('http://localhost:3000/axios', {
params: {
id: 789
}
}).then(function (ret) {
// ret.data 才是后台接口返回的数据,data 是固定的属性,用于获取接口返回的实际数据
console.log(ret);
console.log(ret.data);
})
axios.get('http://localhost:3000/axios/345').then(function (ret) {
// ret.data 才是后台接口返回的数据,data 是固定的属性,用于获取接口返回的实际数据
console.log(ret);
console.log(ret.data);
})
axios.get('http://localhost:3000/axios?id=2', {
params: {
id: 789
}
}).then(function (ret) {
// ret.data 才是后台接口返回的数据,data 是固定的属性,用于获取接口返回的实际数据
console.log(ret);
console.log(ret.data);
})
7.3 axios post 传参
- 一种是 json 形式的
- 一种 是 表单形式de
// 默认传递的是 json 格式的数据
axios.post('/adara',{
uname:'tom',
pwd:123
}).then(ret=>{
console.log(ret.data)
})
axios.post('http://localhost:3000/axios', {
uname: 'lisis',
pwd: '123'
}).then(function (ret) {
// ret.data 才是后台接口返回的数据,data 是固定的属性,用于获取接口返回的实际数据
console.log(ret.data);
})
传递 表单形式的数据
const params = new URLSearchParams();
params.append('uname', 'zhang')
params.append('pwd', '111')
axios.post('http://localhost:3000/axios', params).then(ret => {
console.log(ret.data)
})
7.4 axios 响应结果与全局配置
响应结果
- data :实际相应回来的数据
- headers 响应头信息
- status 响应状态码
- statusText 响应状态信息
全局配置
axios.defaults.headers['mytoken'] = 'hello' // 配置请求头
axios.defaults.timeout = 3000 // 设置超时
axios.defaults.baseURL = 'http://localhost:3000/' // 设置基准URL地址,再写请求的时候,只需要写请求路径就行了
axios.post('axios', {
uname: 'lisis',
pwd: '123'
}).then(function (ret) {
// ret.data 才是后台接口返回的数据,data 是固定的属性,用于获取接口返回的实际数据
console.log(ret.data);
})
7.5 axios 拦截器用法
1、请求拦截器
再发请求之前设置一些信息,常用于判断是否添加请求头
axios.interceptors.request.use(function (config) {
config.headers.mytoken = 'nihao'
// 必须要 return出去
return config
}, function (err) {
console.log(err);
})
2、响应拦截器
axios.interceptors.response.use(function (res) {
// res 就是响应回来的东西 ,res.data 才是响应回来的数据
var data = res.data
// 这样设置表示,接口响应回来的res 都是 直接的数据,不需要再通过 .data 获取数据了
// 必须要 return出去
return data
}, function (err) {
console.log(err);
})
7.6 async 函数基本用法
- async / await 是 ES7引入的新语法,可以更加方便的进行异步操作
- async 关键字用于函数上(async函数的返回值是Promise 实例对象)
- await 关键字用于 async 函数当中 (await可以得到异步的结果)
<script src="./js/axios.js"></script>
<script>
async function queryData() {
var ret = await axios.get('http://localhost:3000/adata')
return ret.data
}
queryData().then(function (data) {
console.log(data);
})
</script>
7.7 async 函数处理多个异步请求
async function queryData() {
var info = await axios.get('http://localhost:3000/async1')
var ret = await axios.get('http://localhost:3000/async2?info=' + info.data)
return ret.data
}
queryData().then(function (res) {
console.log(res);
})
8、基于后台接口的图书馆案例
8.1 图书列表加载
// 给 axios 设置响应 拦截器,把返回的数据,直接修改成真正的数据
axios.defaults.baseURL = 'http://localhost:3000/';
axios.interceptors.response.use(function (res) {
return res.data;
}, function (error) {
console.log(error)
});
// vue data 里面的方法
queryData: async function () {
// 调用后台接口获取图书列表数据
// var ret = await axios.get('books');
// this.books = ret.data;
this.books = await axios.get('books');
}
建议vue种data的methods对象种的,方法都采用 async、await的形式,这样写更加简洁
9、vue路由
9.1 vue 路由的 学习目标
- 能够说出路由的概念
- 能够说出 vue-router 的基本使用步骤
- 能够说出 vue-router 的嵌套路由用法
- 能够说出 vue-router 动态路由匹配用法
- 能够说出 vue-router 命名路由用法
- 能够说出 vue-router 编程式导航用法
- 能够基于路由的方式实现业务功能
9.2 路由的基本概念
路由的本质就是对应关系
路由分为 后端路由 和 前端路由
1、后端路由
- 概念:根据不同的 用户 URL请求 ,返回不同的内容
- 本质:URL 请求地址 与 服务器资源之间的对应关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ZCl7DT5-1620387784181)(vue基础.assets/image-20201025181259535.png)]
2、SPA
- 后端渲染(存在性能问题)
- Ajax 前端渲染(前端渲染提高性能,但不支持浏览器的前进后退操作)
- SPA单页面应用程序:整个网站只有一个页面,内容的变化通过 Ajax局部更新实现、同时支持浏览器地址栏的前进和后退操作
- SPA实现原理之一:基于URL地址的 hash(hash的变化会导致浏览器记录访问历史的变化,但是 hash的变化不会触发新的 URL 请求)
- 在实现 SPA 过程中,最核心的技术点就是前端路由
3、前端路由
- 概念:根据不同的用户事件,显示不同的页面内容
- 本质:用户事件与事件处理函数之间的对应关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q6JnSNwb-1620387784182)(vue基础.assets/image-20201025183610619.png)]
9.3 实现简易的前端路由
// 监听 window 的 onhashchange 事件,根据获取到的最新的 hash值,切换要显示的组件的名称
window.onhashchange=function(){
// 通过 location.hash 获取到最新的 hash值
}
component标签就是一个组件的占位符,:is 就是指定那个组件渲染到此处,注意is里面的值如果是写死的,必须是一个字符串才行
<body>
<!-- 被 vue 实例控制的 div 区域 -->
<div id="app">
<!-- 切换组件的超链接 -->
<a href="#/zhuye">主页</a>
<a href="#/keji">科技</a>
<a href="#/caijing">财经</a>
<a href="#/yule">娱乐</a>
<!-- 根据 :is 属性指定的组件名称,把对应的组件渲染到 component 标签所在的位置 -->
<!-- 可以把 component 标签当做是【组件的占位符】 -->
<component :is="comName"></component>
</div>
<script>
// 主页组件
const zhuye = {
template: '<h1>主页信息</h1>'
}
// 科技组件
const keji = {
template: '<h1>科技信息</h1>'
}
// 财经组件
const caijing = {
template: '<h1>财经信息</h1>'
}
// 娱乐组件
const yule = {
template: '<h1>娱乐信息</h1>'
}
const vm = new Vue({
el: '#app',
data: {
comName: 'zhuye'
},
// 注册私有组件
components: {
'zhuye': zhuye,
'keji': keji,
'caijing': caijing,
'yule': yule
}
})
// 监听 window 的 onhashchange 事件,根据获取到的最新的 hash 值,切换要显示的组件的名称
window.onhashchange = function () {
// 通过 location.hash 获取到最新的 hash 值
console.log(location.hash);
switch (location.hash.slice(1)) {
case '/zhuye':
vm.comName = 'zhuye'
break
case '/keji':
vm.comName = 'keji'
break
case '/caijing':
vm.comName = 'caijing'
break
case '/yule':
vm.comName = 'yule'
break
}
}
</script>
</body>
9.4 Vue Router
官网 https://router.vuejs.org/zh/ 是 vue.js 的路由管理器
功能
- 支持 HTML5 历史模式 或 hash模式
- 支持嵌套路由
- 支持路由参数
- 支持编程式路由
- 支持命名路由
9.5 vue-router 的基本使用
- 引入相关的库文件
- 添加路由链接
- 添加路由填充位
- 定义路由组件
- 配置路由规则并创建路由实例
- 把路由挂载到 Vue 跟实例中
# 添加路由链接
# router-link 是 vue 中提供的标签,默认会被渲染为 标签
# to 属性 默认会被渲染为 href 属性
# to 属性的值默认会被渲染为 # 开头的 hash 地址
<router-link to='/user'> user </router-link>
# 添加路由填充位,也叫路由占位符
# 将来通过路由规则匹配到的组件,将会被渲染到 router-view 所在的位置
<router-view></router-view>
源代码
<body>
<div id="app">
<!-- 1、添加路由链接 -->
<router-link to='/user'>User </router-link>
<router-link to="/Register">Register </router-link>
<!-- 2、定义路由填充位 -->
<router-view></router-view>
</div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<script>
// 3、定义路由组件
var user = {
template: ' <h1>user 组件</h1>'
}
var Register = {
template: '<h1>Register 组件</h1>'
}
var router = new VueRouter({
// routes 是路由规则数组
routes: [
// 每个路由规则都是一个配置对象,其中至少包括 path 和component 两个属性
// path 表示当前路由规则匹配的 hash地址
// component 表示当前路由规则对应要展示的组件,它只接受组件对象,不接受字符串
{ path: '/user', component: user },
{ path: '/Register', component: Register },
]
})
var vm = new Vue({
el: '#app',
data: {},
// 挂载路由实例对象
router: router,
})
// 4、创建路由实例对象
</script>
</body>
9.6 路由重定向
用户再访问 地址 A 的时候,强制用户跳转到地址 C,从而展示特点的组件页面
通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便的设置路由的定向
var router = new VueRouter({
// routes 是路由规则数组
routes: [
// 每个路由规则都是一个配置对象,其中至少包括 path 和component 两个属性
// path 表示当前路由规则匹配的 hash地址
// component 表示当前路由规则对应要展示的组件,它只接受组件对象,不接受字符串
// '/' 表示需要被重定向的源地址,redirect 表示将要被重定向到的新地址
{ path: '/', redirect: '/user' },
{ path: '/user', component: user },
{ path: '/Register', component: Register },
]
})
9.7 嵌套路由
点击 父级路由链接显示模板内容,模板内容中又有子级路由链接,点击子级路由链接显示子级模板内容
<body>
<div id="app">
<!-- 1、添加路由链接 -->
<router-link to='/user'>User </router-link>
<router-link to="/Register">Register </router-link>
<!-- 2、定义路由填充位 -->
<router-view></router-view>
</div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<script>
// 3、定义路由组件
var user = {
template: ' <h1>user 组件</h1>'
}
var Register = {
template: `
<div>
<h1>Register 组件</h1>
<hr/>
<router-link to="/Register/tab1">Register/tab1 </router-link>
<router-link to="/Register/tab2">Register/tab2 </router-link>
<!-- 子级路由填充位置-->
<router-view></router-view>
</div>
`
}
var tab1 = {
template: '<h3>tab1 子组件</h3>'
}
var tab2 = {
template: '<h3>tab2 子组件</h3>'
}
var router = new VueRouter({
// routes 是路由规则数组
routes: [
// 每个路由规则都是一个配置对象,其中至少包括 path 和component 两个属性
// path 表示当前路由规则匹配的 hash地址
// component 表示当前路由规则对应要展示的组件,它只接受组件对象,不接受字符串
// '/' 表示需要被重定向的源地址,redirect 表示将要被重定向到的新地址
{ path: '/', redirect: '/user' },
{ path: '/user', component: user },
{
path: '/Register',
component: Register,
// children 表示 子路由规则
children: [
{ path: '/Register/tab1', component: tab1 },
{ path: '/Register/tab2', component: tab2 },
]
},
]
})
var vm = new Vue({
el: '#app',
data: {},
// 挂载路由实例对象
router: router,
})
// 4、创建路由实例对象
</script>
</body>
9.8 动态路由匹配
路由规则,有一部分是一样的,另一部分是动态的、变化的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FmCe7FOa-1620387784183)(vue基础.assets/image-20201026201701456.png)]
<body>
<div id="app">
<!-- 1、添加路由链接 -->
<router-link to='/user/1'>User </router-link>
<router-link to='/user/2'>User </router-link>
<router-link to='/user/3'>User </router-link>
<router-link to="/Register">Register </router-link>
<!-- 2、定义路由填充位 -->
<router-view></router-view>
</div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<script>
// 3、定义路由组件
var user = {
template: ' <h1>user 组件--用户id为{{$route.params.id}}</h1>'
}
var Register = {
template: `
<div>
<h1>Register 组件</h1>
<hr/>
</div>
`
}
var router = new VueRouter({
// routes 是路由规则数组
routes: [
// 每个路由规则都是一个配置对象,其中至少包括 path 和component 两个属性
// path 表示当前路由规则匹配的 hash地址
// component 表示当前路由规则对应要展示的组件,它只接受组件对象,不接受字符串
// '/' 表示需要被重定向的源地址,redirect 表示将要被重定向到的新地址
{ path: '/', redirect: '/user' },
// :id 表示动态的参数
{ path: '/user/:id', component: user },
{ path: '/Register', component: Register, },
]
})
var vm = new Vue({
el: '#app',
data: {},
// 挂载路由实例对象
router: router,
})
// 4、创建路由实例对象
</script>
</body>
9.10 路由组件传递参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EtR79QkF-1620387784183)(vue基础.assets/image-20201026203125350.png)]
<body>
<div id="app">
<!-- 1、添加路由链接 -->
<router-link to='/user/1'>User </router-link>
<router-link to='/user/2'>User </router-link>
<router-link to='/user/3'>User </router-link>
<router-link to="/Register">Register </router-link>
<!-- 2、定义路由填充位 -->
<router-view></router-view>
</div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<script>
// 3、定义路由组件
var user = {
props: ['id'],// 使用props 接收路由参数
template: ' <h1>user 组件--用户id为{{id}}</h1>'
}
var Register = {
template: `
<div>
<h1>Register 组件</h1>
<hr/>
</div>
`
}
var router = new VueRouter({
// routes 是路由规则数组
routes: [
// 每个路由规则都是一个配置对象,其中至少包括 path 和component 两个属性
// path 表示当前路由规则匹配的 hash地址
// component 表示当前路由规则对应要展示的组件,它只接受组件对象,不接受字符串
// '/' 表示需要被重定向的源地址,redirect 表示将要被重定向到的新地址
{ path: '/', redirect: '/user' },
// :id 表示动态的参数
// props true 表示 route.params 将会被设置为组件属性
{ path: '/user/:id', component: user, props: true },
{ path: '/Register', component: Register, },
]
})
var vm = new Vue({
el: '#app',
data: {},
// 挂载路由实例对象
router: router,
})
// 4、创建路由实例对象
</script>
</body>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kt4mu4bs-1620387784184)(vue基础.assets/image-20201026204401796.png)]
<body>
<div id="app">
<!-- 1、添加路由链接 -->
<router-link to='/user/1'>User </router-link>
<router-link to='/user/2'>User </router-link>
<router-link to='/user/3'>User </router-link>
<router-link to="/Register">Register </router-link>
<!-- 2、定义路由填充位 -->
<router-view></router-view>
</div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<script>
// 3、定义路由组件
var user = {
props: ['id', 'uname', 'age'],// 使用props 接收路由参数
// 其实这里 是 获取不到 id 的,因为你没有传递过来id
template: ' <h1>user 组件--用户id为{{id}}--{{uname}}---{{age}}</h1>'
}
var Register = {
template: `
<div>
<h1>Register 组件</h1>
<hr/>
</div>
`
}
var router = new VueRouter({
// routes 是路由规则数组
routes: [
// 每个路由规则都是一个配置对象,其中至少包括 path 和component 两个属性
// path 表示当前路由规则匹配的 hash地址
// component 表示当前路由规则对应要展示的组件,它只接受组件对象,不接受字符串
// '/' 表示需要被重定向的源地址,redirect 表示将要被重定向到的新地址
{ path: '/', redirect: '/user' },
// :id 表示动态的参数
// props true 表示 route.params 将会被设置为组件属性
{ path: '/user/:id', component: user, props: { uname: 'zs', age: 12 } },
{ path: '/Register', component: Register, },
]
})
var vm = new Vue({
el: '#app',
data: {},
// 挂载路由实例对象
router: router,
})
// 4、创建路由实例对象
</script>
</body>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NDSRCyXg-1620387784185)(vue基础.assets/image-20201026205729207.png)]
<body>
<div id="app">
<!-- 1、添加路由链接 -->
<router-link to='/user/1'>User </router-link>
<router-link to='/user/2'>User </router-link>
<router-link to='/user/3'>User </router-link>
<router-link to="/Register">Register </router-link>
<!-- 2、定义路由填充位 -->
<router-view></router-view>
</div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<script>
// 3、定义路由组件
var user = {
props: ['id', 'uname', 'age'],// 使用props 接收路由参数
// 其实这里 是 获取不到 id 的,因为你没有传递过来id
template: ' <h1>user 组件--用户id为{{id}}--{{uname}}---{{age}}</h1>'
}
var Register = {
template: `
<div>
<h1>Register 组件</h1>
<hr/>
</div>
`
}
var router = new VueRouter({
// routes 是路由规则数组
routes: [
// 每个路由规则都是一个配置对象,其中至少包括 path 和component 两个属性
// path 表示当前路由规则匹配的 hash地址
// component 表示当前路由规则对应要展示的组件,它只接受组件对象,不接受字符串
// '/' 表示需要被重定向的源地址,redirect 表示将要被重定向到的新地址
{ path: '/', redirect: '/user' },
// :id 表示动态的参数
// props true 表示 route.params 将会被设置为组件属性
{
path: '/user/:id',
component: user,
// route 是一个对象
// 箭头函数 左边是 单表达式,要返回一个对象,需要加括号
props: route => ({
uname: 'zs',
age: 20,
id: route.params.id
})
},
{ path: '/Register', component: Register, },
]
})
var vm = new Vue({
el: '#app',
data: {},
// 挂载路由实例对象
router: router,
})
// 4、创建路由实例对象
</script>
</body>
9.11 命名路由
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0hWcHY1V-1620387784186)(vue基础.assets/image-20201026210546160.png)]
<body>
<div id="app">
<!-- 1、添加路由链接 -->
<router-link to='/user/1'>User </router-link>
<router-link to='/user/2'>User </router-link>
<router-link :to="{name:'user',params:{id:3}}">User </router-link>
<router-link to="/Register">Register </router-link>
<!-- 2、定义路由填充位 -->
<router-view></router-view>
</div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<script>
// 3、定义路由组件
var user = {
template: ' <h1>user 组件--用户id为{{$route.params.id}}</h1>'
}
var Register = {
template: `
<div>
<h1>Register 组件</h1>
<hr/>
</div>
`
}
var router = new VueRouter({
// routes 是路由规则数组
routes: [
{ path: '/', redirect: '/user' },
// :id 表示动态的参数
// name 起个别名,命名路由
{ path: '/user/:id', component: user, name: 'user' },
{ path: '/Register', component: Register, },
]
})
var vm = new Vue({
el: '#app',
data: {},
// 挂载路由实例对象
router: router,
})
// 4、创建路由实例对象
</script>
</body>
9.12 编程式导航
声明式导航:通过点击链接实现导航的方式,比如 a标签,router-link 标签
编程式导航:通过调用 JavaScript 形式的 api 实现导航的方式,如 location.href
常用的编程式导航基本用法
- this.$router.push(‘hash地址’),表示跳转到这个hash地址
- this,$router.go(n),正数表示在历史导航中,前进多少部
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aAFAtXoF-1620387784187)(vue基础.assets/image-20201026211712173.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxugfNqZ-1620387784188)(vue基础.assets/image-20201026212707374.png)]
<body>
<div id="app">
<!-- 1、添加路由链接 -->
<router-link to='/user/1'>User </router-link>
<router-link to='/user/2'>User </router-link>
<router-link :to="{name:'user',params:{id:3}}">User </router-link>
<router-link to="/Register">Register </router-link>
<!-- 2、定义路由填充位 -->
<router-view></router-view>
</div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<script>
// 3、定义路由组件
var user = {
template: `
<div>
<h1>user 组件--用户id为{{$route.params.id}}</h1>
<button @click='handle'>跳转到注册页面</button>
</div>
`,
methods: {
handle: function () {
this.$router.push('/Register')
}
}
}
var Register = {
template: `
<div>
<h1>Register 组件</h1>
<hr/>
</div>
`
}
var router = new VueRouter({
// routes 是路由规则数组
routes: [
{ path: '/', redirect: '/user' },
// :id 表示动态的参数
// name 起个别名,命名路由
{ path: '/user/:id', component: user, name: 'user' },
{ path: '/Register', component: Register, },
]
})
var vm = new Vue({
el: '#app',
data: {},
// 挂载路由实例对象
router: router,
})
// 4、创建路由实例对象
</script>
</body>
10、vue-vouter 案例
10.1 抽离并渲染APP根组件
<body>
<!-- 要被 vue 实例 所控制的区域 -->
<div id="app">
<!-- 路由占位符 -->
<router-view></router-view>
</div>
<script>
// 定义 根组件 APP
const App = {
template: `
<div>
<!-- 头部区域 -->
<header class="header">传智后台管理系统</header>
<!-- 中间主体区域 -->
<div class="main">
<!-- 左侧菜单栏 -->
<div class="content left">
<ul>
<li>用户管理</li>
<li>权限管理</li>
<li>商品管理</li>
<li>订单管理</li>
<li>系统设置</li>
</ul>
</div>
<!-- 右侧内容区域 -->
<div class="content right">
<div class="main-content">添加用户表单</div>
</div>
</div>
<!-- 尾部区域 -->
<footer class="footer">版权信息</footer>
</div>
`
}
// 创建路由对象
var router = new VueRouter({
routes: [
{ path: '/', component: App },
]
})
var vm = new Vue({
el: '#app',
// 将路由挂载到 vm 实例身上
router,
})
</script>
</body>
10.2
<head>
<meta charset="UTF-8" />
<title>基于vue-router的案例</title>
<style type="text/css">
html,
body,
#app {
margin: 0;
padding: 0px;
height: 100%;
}
.header {
height: 50px;
background-color: #545c64;
line-height: 50px;
text-align: center;
font-size: 24px;
color: #fff;
}
.footer {
height: 40px;
line-height: 40px;
background-color: #888;
position: absolute;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
}
.main {
display: flex;
position: absolute;
top: 50px;
bottom: 40px;
width: 100%;
}
.content {
flex: 1;
text-align: center;
height: 100%;
}
.left {
flex: 0 0 20%;
background-color: #545c64;
}
.left a {
color: white;
text-decoration: none;
}
.right {
margin: 5px;
}
.btns {
width: 100%;
height: 35px;
line-height: 35px;
background-color: #f5f5f5;
text-align: left;
padding-left: 10px;
box-sizing: border-box;
}
button {
height: 30px;
background-color: #ecf5ff;
border: 1px solid lightskyblue;
font-size: 12px;
padding: 0 20px;
}
.main-content {
margin-top: 10px;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
ul li {
height: 45px;
line-height: 45px;
background-color: #a0a0a0;
color: #fff;
cursor: pointer;
border-bottom: 1px solid #fff;
}
table {
width: 100%;
border-collapse: collapse;
}
td,
th {
border: 1px solid #eee;
line-height: 35px;
font-size: 12px;
}
th {
background-color: #ddd;
}
</style>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
</head>
<body>
<!-- 要被 vue 实例 所控制的区域 -->
<div id="app">
<!-- 路由占位符 -->
<router-view></router-view>
</div>
<script>
// 定义 根组件 APP
const App = {
template: `
<div>
<!-- 头部区域 -->
<header class="header">传智后台管理系统</header>
<!-- 中间主体区域 -->
<div class="main">
<!-- 左侧菜单栏 -->
<div class="content left">
<ul>
<li><router-link to="/users">用户管理</router-link></li>
<li><router-link to="/rigths">权限管理</router-link></li>
<li><router-link to="/goods">商品管理</router-link></li>
<li><router-link to="/orders">订单管理</router-link></li>
<li><router-link to="/settings">系统设置</router-link></li>
</ul>
</div>
<!-- 右侧内容区域 -->
<div class="content right">
<div class="main-content">
<router-view></router-view>
</div>
</div>
</div>
<!-- 尾部区域 -->
<footer class="footer">版权信息</footer>
</div>
`
}
var users = {
data: function () {
return {
userlist: [
{ id: 1, name: '张三', age: 10 },
{ id: 2, name: '李四', age: 20 },
{ id: 3, name: '呆呆狗', age: 30 },
{ id: 4, name: '张柳', age: 40 },
]
}
},
methods: {
handle: function (id) {
this.$router.push('/userinfo/' + id)
}
},
template: `<div>
<h3>用户管理区域</h3>
<table>
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in userlist"><td>{{item.id}}</td><td>{{item.name}}</td><td>{{item.age}}</td><td><a href="javascript:;" @click='handle(item.id)'>详情</a></td></tr>
</tbody>
</table>
</div>`
}
var rigths = { template: `<div><h3>权限管理区域</h3></div>` }
var goods = { template: `<div><h3>商品管理区域</h3></div>` }
var orders = { template: `<div><h3>订单管理区域</h3></div>` }
var settings = { template: `<div><h3>系统设置区域</h3></div>` }
var userinfo = {
props: ['id'],
template: `<div>
<h5>用户详情页{{id}}</h5>
<button @click='houtui'>后退</button>
</div>`,
methods: {
houtui: function () {
this.$router.go(-1)
}
}
}
// 创建路由对象
var router = new VueRouter({
routes: [
{
path: '/',
component: App,
children: [
{ path: '/users', component: users },
{ path: '/userinfo/:id', component: userinfo, props: true },
{ path: '/rigths', component: rigths },
{ path: '/goods', component: goods },
{ path: '/orders', component: orders },
{ path: '/settings', component: settings },
],
redirect: '/users',
},
]
})
var vm = new Vue({
el: '#app',
// 将路由挂载到 vm 实例身上
router,
})
</script>
</body>