一. Vue概述
1. 什么是Vue?
- Vue是一套用于构建用户界面的渐进式框架
- vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合
2. Vue:渐进式JavaScript框架
其中框架指的是提供一些基础性的服务
库指的是以提供API为主
渐进式: 声明式渲染-> 组件系统->客户端路由->集中式状态管理->项目构建
官网
Vue的优点
- 易用: 熟悉HTML、CSS、JavaScript知识后,可快速上手Vue
- 灵活:在一个库和一套完整框架之间自如伸缩
- 高效:20kB运行大小,超快虚拟DOM
二. Vue的基本使用
1. 使用Vue将helloworld 渲染到页面上
<!-- Vue代码运行原理分析:
概述编译过程的概念(vue语法->原生语法) -->
<div id="app">
<!-- {{}}为插值表达式-->
<div>{{msg}}</div>
<div>{{1 + 2}}</div>
<div>{{msg + '----' + 133}}</div>
</div>
<script src="js/vue.js"></script>
<script>
// Vue的基本使用步骤
// 1. 需要提供标签用于填充数据
// 2. 引入vue.js库文件
// 3. 可以使用vue的语法做功能了
// 4. 把vue提供的数据填充到标签里面
var num = new Vue({
el: '#app',
data: {
msg: 'hello Vue'
}
})
</script>
1. 实例参数分析
- el: 元素的挂载位置(值可以是CSS选择器或者DOM元素)
- data: 模型数据(值是一个对象)
2. 插值表达式用法
- 将数据填充到HTML标签中
- 插值表达式支持基本的计算操作
3. Vue代码运行原理分析
- 概述编译·过程的概念(Vue语法->原生语法)
三. Vue模板语法
3.1 模板语法概述
1. 如何理解前端渲染?
把数据填充到HTML标签中
2. 前端渲染方式
- 原生js拼接字符串
- 使用前端模板引擎
- 使用vue特有的模板语法
3. 原生js凭借字符串
基本上就是将数据以字符串的方式拼接到HTML标签中。
缺点:不同开发人员的代码风格差别很大,随着业务的复杂,后期的维护变得逐渐困难起来。
4. 使用前端模板引擎
优点:大家都遵循同样的规则写代码,代码可读性明显提高了,方便后期维护。
缺点:没有专门提供事件机制。
5. 模板语法概览
- 差值表达式
- 指令
- 事件绑定(即事件的处理)
- 属性绑定
- 样式绑定
- 分支循环结构
3.2 指令
1. 什么是指令?
- 什么是自定义属性
- 指令的本质就是自定义属性
- 指令的格式: 以 v-开始 (比如: v-cloak)
2. v-cloak指令用法
- 差值表达式存在的问题: “闪动”
- 如何解决该问题: 使用 v-cloak 指令
- 解决该问题的原理: 先隐藏,替换好值之后再显示最终的值
官网
3. 数据绑定指令
- v-text 填充纯文本
① 相比插值表达式更加简洁 - v-html 填充HTML片段
① 存在安全问题
② 本网站内部数据可以使用,来自第三方的数据不可以使用 - v-pre 填充原始信息
① 显示原始信息,跳过编译过程(分析编译过程)
4. 数据响应式
- 如何理解响应式
① html5的响应式(屏幕尺寸的变化导致样式的变化)
② 数据的响应式(数据的变化导致页面内容的变化) - 什么式数据绑定
① 数据绑定: 将数据填充到标签中 - v-once 只编译一次
① 显示内容之后不再具有响应式功能
3.3 双向数据绑定指令
1. 什么是双向数据绑定事件?(① 用户去改页面中插值表达式的值,数据也会跟着改变;② 数据发生变化,插值表达式的值也会发生变化)
2. 双向数据绑定分析
- v-mode 指令用法
<input type='text' v-model='uname'/>
3. MVVM设计思想
① M(model) 即data中的数据
② V(view) 即模板 (本质上来说是DOM元素)
③ VM(View-Model)实现控制逻辑
3.4 事件绑定
1. Vue如何处理事件?
-
v-on指令用法
<input type="button" v-on:click="num++"/>
-
v-on简写形式
`<input type="button" @click="num++"/>
2. 事件函数的调用方式
-
直接绑定函数名称
<button v-on:click="say">Hello</button>
-
调用函数
<button v-on:click="say()">Say hi</button>
3.事件函数参数传递
- 普通参数和事件对象
<button v-on:click="say('hi',$event)">Say hi</button>
4. 事件修饰符
-
.stop 阻止冒泡
<a v-on:click.stop="handle">跳转</a>
-
.prevent 阻止默认行为
<a v-on:click.prevemt="handle">跳转</a>
5. 按键修饰符
- .enter 回车键
<input v-on:keyup.enter="submit">
- .esc 退出键
<input v-on:keyup.delete="handle">
6. 自定义按键修饰符
- 全局config.keyCodes对象
Vue.config.keyCodes.f1=112
规则:自定义按键修饰符名字是自定义的,但是对应的值必须是按键对应event.keyCode值
3.5 属性绑定
1. Vue如何动态处理属性?
- v-bind指令用法
<a v-bind:herf='url'>跳转</a>
- 缩写形式
`跳转
2. v-mode的底层实现原理分析
<input v-bind:value="msg" v-on:input="msg=$event.target.value">
3.6 样式绑定
1. class样式处理
-
对象语法
<div v-bind:class="{active: isActice}"></div>
-
数组语法
<div v-bind:class="{activeClass: errorClass}"></div>
样式绑定的相关语法细节:
1) 对象绑定和数组绑定可以结合使用
2)class绑定的值可以简化操作
3)默认的class如何处理? 默认的class会被保留
<body>
<div id="app">
<div :class="[activeClass,errorClass, {test: isTest}]">测试样式</div>
<div :class="arrClasses">测试样式</div>
<div :class="objClasses">测试样式</div>
<div class="base" :class="objClasses">测试样式</div>
<button v-on:click='handle'>切换</button>
</div>
<script src="js/vue.js"></script>
<script>
// 样式绑定相关语法细节:
// - 对象那个绑定和数组绑定可以结合使用
// - class绑定的值可以简化操作
// - 默认的class如何处理? ,默认的class会被保留
var vm = new Vue({
el: '#app',
data: {
activeClass: 'active',
errorClass: 'error',
isTest: true,
arrClasses: ['active', 'error'],
objClasses: {
active: true,
error: true,
}
},
methods: {
handle: function() {
this.isTest = false;
}
}
});
</script>
</body>
2. style样式处理
-
对象语法
<div v-bind:style="[color: activerColor, fontSize: fontSize]"></div>
-
数组语法
<div v-bind:style="[baseStyles, overridingStyles]"></div>
3.7 分支循环结构
1. 分支结构
- v-if
- v-else
- v-else-if
- v-show
<body>
<div id="app">
<div v-if='score>=90'>优秀</div>
<div v-else-if='score<90 && score>=80'>良好</div>
<div v-else-if='score<80 && score >=60'>一般</div>
<div v-else>较差</div>
<div v-show='flag'>测试v-show</div>
<button v-on:click='handle'>点击</button>
</div>
<script src="js/vue.js"></script>
<script>
// 分支结构
// - v-if:控制元素是否渲染到页面 (出现之后用的就比较少了,就用)
// - v-else
// - v-else-if
// - v-show的原理: 控制元素的样式是否显示 display:none/block (平凡的让一个元素显示或隐藏时用)
var vm = new Vue({
el: '#app ',
data: {
score: 10,
flag: false,
},
methods: {
handle: function() {
this.flag = !this.flag;
}
}
})
</script>
</body>
2. v-if与v-show的区别
- v-if 控制元素是否渲染到页面 如果平凡的让一个元素显示和隐藏则有v-if
- v-show 控制元素是否显示(已经渲染到了页面,但有可能没有显示,是隐藏的) 控制样式的变化用v-show
3. 循环结构
- v-for遍历数组
<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">
<div>水果列表</div>
<ul>
<li v-for='item in fruites'>{{item}}</li>
<li v-for='(item,index) in fruites'>{{item +'----'+index}}</li>
<li :key='item.id' v-for='(item,index) in myFruites'>
<span>{{item.ename}}</span>
<span>---------</span>
<span>{{item.cname}}</span>
</li>
</ul>
</div>
<script src="js/vue.js"></script>
<script>
// 循环结构:
// - v-for遍历数组
// - key的作用: 帮助Vue区分不同的元素,从而提高性能 (在遍历的时候必须加上key)
var vm = new Vue({
el: '#app',
data: {
fruites: ['apple', 'orange', 'banana'],
myFruites: [{
id: 1,
ename: 'apple',
cname: '苹果'
}, {
id: 2,
ename: 'orange',
cname: '橘子'
}, {
id: 3,
ename: 'banana',
cname: '香蕉'
}]
},
methods: {
}
})
</script>
</body>
运行结果为:
4. 循环结构
-
v-for遍历对象
<div v-for='{value,key, index) in object'></div>
-
v-if和v-for结合使用
<div v-if='value==12' v-for='(value, key, index) in object'></div>
<body>
<div id="app">
<div v-if='v==18' v-for='(v,k,i) in obj'>{{v +'----'+ k +'----'+ i}}</div>
</div>
<script src="js/vue.js"></script>
<script>
// 原生js遍历对象
var obj = {
uname: 'lisi',
age: 18,
sex: 'male'
}
for (var key in obj) {
console.log(key, obj[key]);
}
var vm = new Vue({
el: '#app',
data: {
obj: {
uname: 'lisi',
age: 18,
sex: 'male'
}
},
methods: {
}
})
</script>
</body>
运行结果:
四. 基础案例
五. Vue常用特性
5.1 常用特性概览
- 表单操作
- 自定义指令
- 计算属性
- 侦听器
- 过滤器
- 生命周期
5.2 表单操作
1. 基于Vue的表单操作
- input 单行文本
- textarea 多行文本
- select 下拉对选
- radio 单选框
- checkbox 多选框
2. 表单域修饰符
- number: 转换为数值
- trim: 去掉开始和结尾的空格
- lazy: 将input事件切换为change事件
<input v-model.number='age' type="number">
5.3 自定义指令
1. 为何需要自定义指令?
内置指令不满足需求
2. 自定义指令的语法规则(获取元素焦点)
Vue.directive('focus' {
inserted: function(el) {
// 获取元素的焦点
el.focus();
}
})
3. 自定义指令用法
<input type="text", v-focus>
4. 带参数的自定义指令(改变元素背景色)
Vue.directive(‘color', {
inserted: function(el, binding) {
el.style.backgroundColor = binding.value.color;
}
})
5. 指令用法
<input type="text" v-color='{color:"orange"}'>
6. 局部指令
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
5.4 计算属性
1. 何为需要计算属性
表达式的计算逻辑可能会比较复杂,使用计算属性可以使模板内容更加简洁
2. 计算属性的用法
computed: {
reversedMessage: function () {
return this.msg.split('').reverse().join('')
}
}
3. 计算属性与方法的区别
- 计算属性是基于它们的依赖进行缓存的
- 方法不存在缓存
<body>
<div id="app">
<div>{{msg}}</div>
<div>{{reverseMessage()}}</div>
<div>{{reverseMessage()}}</div>
<div>{{reverseString}}</div>
<div>{{reverseString}}</div>
</div>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: 'hello'
},
methods: {
reverseMessage: function() {
console.log('methods');
return this.msg.split('').reverse().join('');
}
},
computed: {
reverseString: function() {
console.log('computed');
return this.msg.split('').reverse().join('');
}
}
});
</script>
</body>
运行结果如下:
5.5 侦听器
侦听器的应用场景
数据变化时执行异步或开销较大的操作
2. 侦听器的用法
watch: {
firstName: function(val){
// val表示变化之后的值
this.fullName = val + this.lastName;
},
lastName: function(val) {
this.fullName = this.firstName + val;
}
}
5.6 过滤器
1. 过滤器的作用是什么?
格式化数据,比如将字符串格式化为首字母大写,佳能日期格式化为指定的格式等
2. 自定义过滤器
Vue.filter('过滤器名称', function(value) {
// 过滤器业务逻辑
})
3. 过滤器的使用
<div>{{msg | upper}}</div>
<div>{{msg | upper lower}}</div>
<div v-bind:id='id | formatId'></div>
案例:
<body>
<div id="app">
<input type="text" v-model='msg'>
<div>{{msg | upper}}</div>
<div>{{msg | upper | lower}}</div>
<div :abc='msg | upper'>测试数据</div>
<div :abc='msg | lower'>测试数据形式</div>
</div>
<script src="js/vue.js"></script>
<script>
// 过滤器
// 定义过滤器
Vue.filter('upper', function(val) {
return val.charAt(0).toUpperCase() + val.slice(1);
});
Vue.filter('lower', function(val) {
return val.charAt(0).toLowerCase() + val.slice(1);
})
var vm = new Vue({
el: '#app',
data: {
msg: ''
},
})
</script>
</body>
运行结果:
4. 局部过滤器
filters: {
capitalize: function() {}
}
5. 带参数的过滤器
Vue.filter(‘format’, function(value, arg1){
// value就是过滤器传递过来的参数
})
6. 过滤器的使用
<div>{{date | format(‘yyyy-MM-dd')}}</div>
日期格式化规则
y: 年,
M:年中的月份(1-12),
d: 月中的天(1-31),
h: 小时(0-23),
m: 分(0-59),
s: 秒(0-59),
S: 毫秒(0-999),
q: 季度(1-4)
案例:日期格式化
<body>
<div id="app">
<div>{{date | format('yyyy-MM-dd')}}</div>
</div>
<script src="js/vue.js"></script>
<script>
// 过滤器案例: 格式化日期
// Vue.filter('format', function(val, age) {
// if (age == 'yyyy-MM-dd') {
// var ret = '';
// ret += val.getFullYear() + '-' + (val.getMonth() + 1) + '-' + val.getDate();
// return ret;
// }
// return val;
// })
// 日期格式化规则
// y: 年,
// M:年中的月份(1-12),
// d: 月中的天(1-31),
// h: 小时(0-23),
// m: 分(0-59),
// s: 秒(0-59),
// S: 毫秒(0-999),
// q: 季度(1-4)
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: {
date: new Date()
}
})
</script>
</body>
6.1 生命周期
1. 主要阶段
-
挂载(初始化相关属性)
① beforeCreate
② created
③ beforeMount
④ mounted -
更新(元素或组件的变化操作)
① beforeUpdate
② uodated -
销毁(销毁相关的属性)
① beforeDestroy
② destroyed
2. Vue 实例的产生过程
① beforeCreate 在实例初始化之后,数据观测和事件配置之前被调用。
② created 在实例创建完成后被立即调用。
③ beforeMount 在挂载开始之前被调用。
④ mounted el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。
⑤ beforeUpdate 数据更新时调用,发生在虚拟DOM打补丁之前。
⑥ updated 由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
⑦ beforeDestroy 实例销毁之前调用。
⑧ destroyed 实例销毁后调用。
6.2 数组相关API(重点)
1. 变异方法(修改原有数据)
Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。
- push()
- pop()
- shift()
- unshift()
- splice() 实现删除数组中指定的元素
- sort() 实现排序
- reverse() 实现数组元素的翻转
2. 替换数组(生成新的数组)
- filter()
- concat()
- slice()
3. 修改相应式数据
- Vue.set(vm.item, indexOfItem, newValue)
- vm.$set(vm.items, indexOfItem, newValue)
① 参数一表示要处理的数组名称
② 参数二表示要处理短的数组的索引
③ 参数三表示要处理的数组的值
原始方法:
<body>
<div id="app">
<ul>
<li v-for='item in list'>{{item}}</li>
</ul>
</div>
<script src="js/vue.js"></script>
<script>
// 动态处理响应式数据
var vm = new Vue({
el: '#app',
data: {
list: ['apple', 'orange', 'banbana']
}
});
vm.list[1] = 'lemon';
</script>
</body>
运行结果:
7. 组件化开发思想
7.1 现实中的组件化思想体现
- 标准
- 分治
- 重用
- 组合
7.2 编程中的组件化思想体现
7.3 组件化规范:Web Components
- 我们希望尽可能多的重用代码
- 自定义组件的方式不太容易
- 多次使用组件可能导致冲突
Web Components 通过创建封装好的功能的定制元素解决上述问题
官网: https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
8. 组件注册
8.1 全局组件注册为语法
Vue.component(组件名称, {
data: 组件数据,
template: 组件模板内容
})
// 注册一个名为button-counter 的新组件
Vue.component('button-counter', {
data: function() {
return {
count: 0
}
}
trmplate: '<button v-on:click="count++">点击了{{count}}此</button>'
})
8.2 组件用法
<div id="app">
<button-counter></button-counter>
</div>
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
8.3 组件注册注意事项
1. data必须是一个函数
- 分析函数与普通对象的对比
2. 组件模板内容必须是一个根元素
- 分析演示实际的效果
3. 组件模板内容可以是模板字符串
- 模板字符串需要浏览器提供支持(ES6语法)
4. 组件命名方式
- 短横线方式
Vue.component('my-compontent', { /* ... */ })
- 驼峰方式
Vue.compontent('MyComponent', { /* ... */ })
注意事项:
如果使用驼峰式命名组件,那么在使用组建的时候,只能在字符串中用驼峰的方式使用组件,但是在普通的标签模板中,必须使用短横线的方式使用组件
8.4 局部组件注册
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponemtC = { /* ... */ }
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB,
'componemt-c': ComponentC,
}
})
局部组件只能在注册它的组件中才能使用,全局组件可以在所有的组件中使用
9. Vue 调试工具
9.1 调试工具安装
① 克隆仓库
② 安装依赖包
③ 构建
④ 打开Chrome扩展页面
⑤ 选中开发者模式
⑥ 加载已解压的扩展,选择shells/chrom
9.2 调试工具用法
10. 组件间的数据交互
10.1 父组件向子组件传值
1. 组件内部通过props 接收传递过来的值
Vue.component(‘menu-item', {
props: ['title'],
template: '<div>{{ title }}</div>'
})
2. 父组件通过属性将值传递给子组件
<menu-item title="来自父组件的数据"></menu-item>
<menu-item :title="title"></menu-item>
3. props属性名规则
- 在props中使用驼峰形式,模板中需要使用短横线的形式
- 字符串形式的模板中没有这个限制
Vue.component(‘menu-item', {
// 在 JavaScript 中是驼峰式的
props: [‘menuTitle'],
template: '<div>{{ menuTitle }}</div>'
})
<!– 在html中是短横线方式的 -->
<menu-item menu-title=“nihao"></menu-item>
4. props属性值类型
- 字符串String
- 数值Number
- 布尔型Boolean
- 数组Array
- 对象Object
10.2 子组件向父组件传值
1. 子组件通过自定义事件向父组件传递信息
<button v-on:click='$emit("enlarge-text") '>扩大字体</button>
2. 父组件监听子组件的事件
<menu-item v-on:enlarge-text='fontSize += 0.1'></menu-item>
3. 子组件通过自定义事件向父组件传递信息
<button v-on:click='$emit("enlarge-text", 0.1) '>扩大字体</button>
4. 父组件监听子组件的事件
<menu-item v-on:enlarge-text='fontSize += $event'></menu-item>
10.3 非父子组件间传值
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>父组件</div>
<div>
<button @click='handle'>销毁事件</button>
</div>
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script src="js/vue.js"></script>
<script>
// 兄弟组件之间数据传递
// 1. 提供事件中心
var hub = new Vue();
Vue.component('test-tom', {
data: function() {
return {
num: 0
}
},
template: `
<div>
<div>TOM:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function() {
// 3. 触发兄弟组件的事件
hub.$emit('jerry-event', 2);
}
},
mounted: function() {
// 2. 监听事件
hub.$on('tom-event', (val) => {
this.num += val;
})
}
});
Vue.component('test-jerry', {
data: function() {
return {
num: 0
}
},
template: `
<div>
<div>JERRY:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function() {
hub.$emit('tom-event', 1);
}
},
mounted: function() {
hub.$on('jerry-event', (val) => {
this.num += val;
})
}
});
var vm = new Vue({
el: '#app',
data: {
},
methods: {
handle: function() {
// 4. 销毁事件
hub.$off('tom-event');
hub.$off('jerry-event')
}
}
})
</script>
</body>
运行结果:
11. 组件插槽
11.1 组件插槽的作用
- 父组件向子组件传递内容(其中内容指的时模板的内容)
11.2 组件插槽基本用法
1. 插槽位置
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
2. 插槽内容
<alert-box>Something bad happened.</alert-box>
举例为
<body>
<div id="app">
<alert-box>有bug发生</alert-box>
<alert-box>有一个警告</alert-box>
<alert-box></alert-box>
</div>
<script src="js/vue.js"></script>
<script>
Vue.component('alert-box', {
template: `
<div>
<strong>ERROR:</strong>
<slot>默认内容</slot>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
运行结果:
11.3 具名插槽用法
1. 插槽定义
<div class="container">
<header>
<slot name="header"><slot>
</header>
<main>
<slot></slot>
<main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
2. 插槽内容
<base-layout>
<h1 slot="header">标题内容</h1>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部内容</p>
</base-layout>
案例:
<body>
<div id="app">
<base-layout>
<h1 slot="header">标题内容</h1>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部内容</p>
</base-layout>
<base-layout>
<template slot="header">
<h1>标题内容1</h1>
<h1>标题内容2</h1>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<template slot="footer">
<h1>底部内容1</h1>
<h1>底部内容2</h1>
</template>
</base-layout>
</div>
<script src="js/vue.js"></script>
<script>
// 具名插槽
Vue.component('base-layout', {
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>
运行结果:
11.4 作用域插槽
- 应用场景: 父组件对子组件的内容进行加工处理
1. 插槽定义
<ul>
<li v-for="item in list" v-bind:key="item.id">
<slot v-bind:item="item">
{{item.name}}
</slot>
</li>
</ul>
2. 插槽内容
<fruit-list v-bind:list="list">
<template slot-scope="slotProps">
<strong v-if="slotProps.item.current">
{{slotProps.item.text}}
</strong>
</template>
</fruit-list>
12. 前后端交互模式
12.1 接口调用方式
- 原生 ajax
- 基于jQuery的 ajax (侧重点为DOM操作)
- fetch (ajax的升级版)
- axios
客户端和服务器的通讯模式
12.2 URL 地址格式
1. 传统形式的URL
- 格式: schema://host:port/path?query#fragment
① schema:协议。例如http、https、ftp等
② host:域名或者IP地址
③ port:端口,http默认端口为80,可以省略
④ path:路劲,例如/abc/a/b/c (这个路劲是虚拟的,其作用为区分不同的资源)
⑤ query:查询参数,例如 uname=lisi$age=12
⑥ fragment:锚点(哈希Hash),用于定位页面的某个位置
2. Restful形式的 URL
- HTTP请求方式
① GET 查询
② POST 添加
③ PUT 修改
④ DELETE 删除
- 符合规定的URL地址
① http://www.hello.com/books GET
② http://www.hello.com/books POST
③ http://www.hello.com/books/123 PUT 修改id=123的这本书的信息
④ http://www.hello.com/books/123 DELETE 删除id=123的这本书的信息
13. Promise用法(es6)
13.1 异步调用
- JavaScript的执行环境是单线程
- 所谓单线程,是指JS 引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任务,这个任务执行完之后才能执行下一个,它会阻塞其他任务。这个任务可称为主线程。
- 异步模式可以一起执行多个任务
- 异步效果分析
① 定时任务
② Ajax
③ 事件函数 - 多次异步调用的依赖分析
① 多次异步调用的结果顺序不确定
$.ajax({
url: 'http://localhost:3000/data',
success: function(data) {
console.log(data);
},
});
$.ajax({
url: 'http://localhost:3000/data1',
success: function(data) {
setTimeout(function() {
console.log(data);
}, 1000)
},
});
$.ajax({
url: 'http://localhost:3000/data2',
success: function(data) {
console.log(data);
},
});
② 异步调用结果如果存在依赖需要嵌套
// 异步调用结果如果存在依赖需要嵌套
$.ajax({
url: 'http://localhost:3000/data',
success: function(data) {
console.log(data);
$.ajax({
url: 'http://localhost:3000/data1',
success: function(data) {
console.log(data);
$.ajax({
url: 'http://localhost:3000/data2',
success: function(data) {
console.log(data);
},
});
},
});
},
});
13.2 Promise 概述
Promise 是异步编程的一种解决方案,从语法上讲,promise是一个对象,从它可以获取异步操作的消息。
使用promise主要有一下好处:
- 可以避免多层异步调用嵌套问题(回调地狱)
- promise 对象提供了简洁的API,使得控制异步操作更加容易
13.3 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) {
// 这里用于实现异步任务
setTimeout(function() {
var flag = false;
if (flag) {
// 正常情况
resolve('hello');
} else {
// 异常情况
reject('出错了');
};
}, 100);
});
p.then(function(data) {
console.log(data);
}, function(info) {
console.log(info);
})
13.4 基于Promise处理Ajax请求
1. 处理原生Ajax
<script>
// 基于Promise发送Ajax请求
function queryData(url) {
var p = new Promise(function(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://localhost:3000/data')
.then(function(data) {
console.log(data);
}, function(info) {
console.log(info);
})
</script>
2. 发送多次ajax请求
queryData('http://localhost:3000/data')
.then(function(data) {
console.log(data);
return queryData('http://localhost:3000/data1')
})
.then(function(data) {
console.log(data);
return queryData('http://localhost:3000/data2')
})
.then(function(data) {
console.log(data);
});
13.5 then参数中的函数返回值
1. 返回Promise 实例对象
- 返回的该实例对象会调用下一个then
<body>
<script>
// 基于Promise发送Ajax请求
function queryData(url) {
return new Promise(function(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);
});
};
queryData('http://localhost:3000/data')
.then(function(data) {
return queryData('http://localhost:3000/data1');
})
.then(function(data) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(123);
}, 1000);
});
})
.then(function(data) {
console.log(data); // 123
})
</script>
</body>
2. 返回普通值
- 返回的普通值会直接传递给下一个then,通过then参数中函数的参数接收该值。
<body>
<script>
// 基于Promise发送Ajax请求
function queryData(url) {
return new Promise(function(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);
});
};
queryData('http://localhost:3000/data')
.then(function(data) {
return queryData('http://localhost:3000/data1');
})
.then(function(data) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(123);
}, 1000);
});
})
// .then(function(data) {
// console.log(data); // 123
// })
.then(function(data) {
return 'hello'
})
.then(function(data) {
console.log(data); // hello
})
</script>
</body>
13.6 Promise常用的API
1. 实例方法
- p.then()得到异步任务的正确结果
- p.catch()获取异常信息
- p.finally() 成功与否都会执行(尚且不是正式标准)
<body>
<script>
// Promise常用API--实例方法
function foo() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
// resolve(123);
reject('error')
}, 100);
});
};
foo()
// 得到异步任务的正确结果
.then(function(data) {
console.log(data);
})
// 获取异步信息
.catch(function(data) {
console.log(data);
})
// 成功与否都会执行
.finally(function() {
console.log('finished');
})
</script>
</body>
2. 对象方法
- Promise.all() 并发处理多个异步任务,所有任务都执行完成才能得到结果
- Promise.race() 并发处理多个异步任务,只要有一个任务完成就能得到结果
<body>
<script>
// Promise常用API-对象方法
function queryData(url) {
return new Promise(function(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);
});
};
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(result) {
console.log(result); // 返回三个值 ["Hello TOM", "Hello JERRY", "Hello SPIKE"]
});
Promise.race([p1, p2, p3]).then(function(result) {
console.log(result); // Hello TOM
});
</script>
</body>
14. 接口调用-fetch用法
14.1 fetch 概述
1. 基本特性
- 更加简单的数据获取方式、功能更强大、更灵活、可以看做是xhr的升级版
- 基于Promise实现
2. 语法结构
fetch(url).then(fn2)
.then(fn3)
...
.catch(fn)
14.2 fetch 的基本使用
<body>
<script>
// Fetch API基本用法
fetch('http://localhost:3000/fdata').then(function(data) {
// text() 方法属于fetchAPI的一部分,他返回一个Promise实例对象,用于获取后台返回的数据
return data.text();
}).then(function(data) {
// 注意这里得到的才是最终的数据
console.log(data);
})
</script>
</body>
14.3 fetch请求参数
1. 常见配置选项
- method(String): Http请求方式,默认为GET(GET、POST、PUT、DELETE)
- body(String): HTTP的请求参数
- headers(Object): HTTP的请求头,默认为{}
2. GET请求方式的参数传递
- 传统的URL传递参数
// 1. 传统的URL传递参数
fetch('http://localhost:3000/books?id=123', {
method: 'get'
})
.then(function(data) {
// text() 方法属于fetchAPI的一部分,他返回一个Promise实例对象,用于获取后台返回的数据
return data.text();
}).then(function(data) {
// 注意这里得到的才是最终的数据
console.log(data);
});
app.get('/books', (req, res) => {
res.send('传统的URL传递参数!' + req.query.id);
});
- Restful形式的URL传递参数
// Restful形式的URL传递参数
fetch('http://localhost:3000/books/456', {
method: 'get'
})
.then(function(data) {
// text() 方法属于fetchAPI的一部分,他返回一个Promise实例对象,用于获取后台返回的数据
return data.text();
}).then(function(data) {
// 注意这里得到的才是最终的数据
console.log(data);
})
app.get('/books/:id', (req, res) => {
res.send('Restful形式的URL传递参数!' + req.params.id);
});
运行结果
3. DELETE请求方式的参数传递
// 2. DELETE请求方式的参数传递
fetch('http://localhost:3000/books/789', {
method: 'delete'
})
.then(function(data) {
// text() 方法属于fetchAPI的一部分,他返回一个Promise实例对象,用于获取后台返回的数据
return data.text();
}).then(function(data) {
// 注意这里得到的才是最终的数据
console.log(data);
});
app.delete('/books/:id', (req, res) => {
res.send('DELETE请求方式传递参数!' + req.params.id);
});
运行结果
4. POST请求方式的参数传递
- 传递方式的post传递参数
fetch('http://localhost:3000/books', {
method: 'post',
body: 'uname=lisi&pwd=123',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(function(data) {
// text() 方法属于fetchAPI的一部分,他返回一个Promise实例对象,用于获取后台返回的数据
return data.text();
}).then(function(data) {
// 注意这里得到的才是最终的数据
console.log(data);
});
- json格式的post传递参数
// 2> json格式的post传递参数
fetch('http://localhost:3000/books', {
method: 'post',
body: JSON.stringify({
uname: '张三',
pwd: 456
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
// text() 方法属于fetchAPI的一部分,他返回一个Promise实例对象,用于获取后台返回的数据
return data.text();
}).then(function(data) {
// 注意这里得到的才是最终的数据
console.log(data);
});
app.post('/books', (req, res) => {
res.send('POST请求方式传递参数!' + req.body.uname + '---' + req.body.pwd);
});
传统的post传递参数和json格式的post传递参数格式相同是由于app.use(bodyParser.json())这个中间件
运行结果
5. PUT请求方式传递参数
fetch('http://localhost:3000/books/123', {
method: 'put',
body: JSON.stringify({
uname: '张三',
pwd: 789
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
// text() 方法属于fetchAPI的一部分,他返回一个Promise实例对象,用于获取后台返回的数据
return data.text();
}).then(function(data) {
// 注意这里得到的才是最终的数据
console.log(data);
});
app.put('/books/:id', (req, res) => {
res.send('PUT请求方式传递参数!' + req.params.id + '---' + req.body.uname + '---' + req.body.pwd);
});
运行结果
14.4 fetch响应结果
响应数据格式
- text(): 将返回体处理成字符串类型
- json(): 返回结果和JSON.parse(responseText)一样
<body>
<script>
// fetch响应结果的数据格式
fetch('http://localhost:3000/json').then(function(data) {
// return data.json();
return data.text();
}).then(function(data) {
// console.log(data.uname);
// console.log(data); // {"uname":"lisi","age":13,"gender":"male"} 类型为字符串
var obj = JSON.parse(data);
console.log(obj.uname, obj.age, obj.gender); // lisi 13 male
})
</script>
</body>
app.get('/json', (req, res) => {
res.json({
uname: 'lisi',
age: 13,
gender: 'male'
})
});
15. 接口调用-axios用法
15.1 axios的基本特征
axios是一个基于Promise用于浏览器和node.js的HTTP客户端。
axios官网
它具有一下特征:
- 支持浏览器和node.js
- 支持Promise
- 能拦截请求和响应
- 自动转换JSON数据
15.2 axios的基本用法
<body>
<script src="../../js/axios.map" type="text/babel"></script>
<script src="../../js/axios.js"></script>
<script>
axios.get('http://localhost:3000/adata').then(function(ret) {
// 注意data属性是固定的用法,用于获取后台的实际数据
// console.log(ret.data);
console.log(ret);
})
</script>
</body>
运行结果:
15.3 axios的常用API
- get: 查询数据
- post: 添加数据
- put: 修改数据
- delete: 删除数据
15.4 axios的参数传递
1. GET传递参数
- 通过URL传递参数
axios.get('http://localhost:3000/axios?id=123').then(function(ret) {
console.log(ret.data);
})
app.get('/axios', (req, res) => {
res.send('axios get 传递参数' + req.query.id);
})
运行结果:
- 通过Restful传递参数
axios参数传递.html
// 2> 通过parmas选项传递参数
axios.get('http://localhost:3000/axios/456').then(function(ret) {
console.log(ret.data);
});
index.js(后台接口)
app.get('/axios/:id', (req, res) => {
res.send('axios get (Restful) 传递参数' + req.params.id);
});
- 通过params选项传递参数
html文件
axios.get('http://localhost:3000/axios', {
params: {
id: 789
}
}).then(function(ret) {
console.log(ret.data);
});
后台接口
app.get('/axios', (req, res) => {
res.send('axios get 传递参数' + req.query.id);
});
2. POST传递参数
- 通过选项传递参数(默认传递的是json格式的数据)
axios.post('/adata', {
uname: 'tom',
pwd: 123
}).then(ret => {
console.log(ret.data)
})
app.post('/axios', (req, res) => {
res.send('axios post 传递参数' + req.body.uname + '---' + req.body.pwd);
});
- 通过URLSearchParams传递参数(application/x-www-form-urlencoded)
var params = new URLSearchParams();
params.append('uname', 'zhangsan');
params.append('pwd', 111);
axios.post('http://localhost:3000/axios', params).then(function(ret) {
console.log(ret.data);
});
app.post('/axios', (req, res) => {
res.send('axios post 传递参数' + req.body.uname + '---' + req.body.pwd);
});
3. PUT传递参数
- 通过选项传递参数(默认传递的是json格式的数据)
axios.put('http://localhost:3000/axios/123', {
uname: 'lisi',
pwd: 123
}).then(function(ret) {
console.log(ret.data);
});
app.put('/axios/:id', (req, res) => {
res.send('axios put 传递参数' + req.params.id + '---' + req.body.uname + '---' + req.body.pwd);
});
- 通过URLSearchParams传递参数(application/x-www-form-urlencoded)
var params = new URLSearchParams();
params.append('uname', 'zhangsan');
params.append('pwd', 111);
axios.put('http://localhost:3000/axios/123', params).then(function(ret) {
console.log(ret.data);
});
app.put('/axios', (req, res) => {
res.send('axios put 传递参数' + req.body.uname + '---' + req.body.pwd);
});
4. DELETE传递参数
- 通过URL传递参数
- 通过Restful传递参数
- 通过params选项传递参数
axios.delete('http://localhost:3000/axios', {
params: {
id: 111
}
})
.then(ret => {
console.log(ret.data);
});
app.delete('/axios', (req, res) => {
res.send('axios delete 传递参数' + req.query.id);
});
15.5 axios 的响应结果
响应结果的主要属性
- data: 实际响应回来的数据
- headers:响应头信息
- status:响应状态码
- statusText:响应状态信息
15.6 axios 的全局配置
- axios.defaults.timeout = 3000; //超时时间
- axios.defaults.baseURL = 'http:localhost:3000/app'; //默认地址
- axios.defaults.headers['mytoken'] = 'aqwerwqwerqwer2ewrwe23eresf23'; // 设置请求头 值为自定义的
<script>
// axios的全局配置
// 配置请求的基准URL地址
axios.defaults.baseURL = 'http://localhost:3000';
// 配置请求头信息
axios.defaults.headers['mytoken'] = 'hello';
axios.get('axios-json').then(function(ret) {
console.log(ret.data.uname);
});
</script>
15.6 axios拦截器
1. 请求拦截器
在请求发出之前设置一些信息
2. 响应拦截器
在获取数据之前对数据做一些加工处理
<script>
// axios拦截器
// 1. 请求拦截器
// 添加一个请求拦截器
axios.interceptors.request.use(function(config) {
console.log(config.url);
config.headers.mytoken = 'nihao';
// 在请求发出之前进行一些信息设置
return config;
}, function(err) {
// 处理响应的错误信息
console.log(err);
});
// 2. 响应拦截器
// 添加一个响应拦截器
axios.interceptors.response.use(function(res) {
// console.log(res);
var data = res.data;
// 在这里对返回的数据进行处理
return data;
}, function(err) {
// 处理响应的错误信息
console.log(err);
});
axios.get('http://localhost:3000/adata').then(function(data) {
console.log(data);
});
</script>
16. 接口调用-async/await用法
16.1 async/await的基本用法
- async/await是ES7引入的新的语法,可以更加方便的进行异步操作
- async 关键字用于函数上(async函数的返回值是Promise实例对象)
- await 关键字用于async 函数当中(await可以得到异步的结果)
// async function queryData() {
// var ret = await axios.get('adata');
// // console.log(ret.data);
// return ret.data
// };
async function queryData() {
var ret = await new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('nihao');
})
})
// console.log(ret.data);
return ret;
};
queryData().then(function(data) {
console.log(data);
});
16.2 async/await 处理多个异步请求
多个异步请求的场景
async function queryData() {
var ret = await new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('nihao');
})
})
// console.log(ret.data);
return ret;
};
queryData().then(function(data) {
console.log(data);
});