第一章 初识Vue
1. Vue概述
- Vue是什么
Vue是一套用于构建用户界面的“渐进式”JavaScript框架。
渐进式:Vue可以自底向上逐层的应用,对于简单应用只需一个轻量小巧的核心库即可,对于复杂应用,可以引入各式各样的Vue插件丰富其功能
- Vue框架的迭代过程
- Vue框架的特点
- 组件化模式,提高代码复用率
- 声明式编码,无需直接操作DOM,提高开发效率
2. 使用Vue框架
- 引入vue.js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
<div id="root">
<h3>姓名:{{name}}</h3>
<h3>性别:{{sex}}</h3>
<h3>年龄:{{age}}</h3>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data:{
name:"小明",
sex:"男",
age:25
}
})
</script>
</body>
</html>
- 基于vue-cli搭建项目
第一步:安装vue-cli
npm install -g @vue/cli
第二步:创建一个vue项目
vue create 项目名称
第三步:创建.vue文件
<template>
<div id="app">
<h3>姓名:{{name}}</h3>
<h3>性别:{{sex}}</h3>
<h3>年龄:{{age}}</h3>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
name: "小明",
sex: "男",
age: 25,
};
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
}
</style>
- Vue框架介面解析
页面容器:<div id="app">。。。</div>
模版:<template> 。。。 </ template>
vue实例:<script>。。。</script>,负责解析模版代码
style样式:<style>。。。</style>,负责为模版代码添加样式
第二章 Vue基础
1. 模版语法
- 插值语法
作用:用于解析标签体内容,在插值语法中可以直接读取data中所有的数据
<h3>姓名:{{name}}</h3>
- 指令语法
作用:以“v-”开头,部分有简写形式,主要用于解析标签,包括标签属性、标签体内容、绑定事件等
<p v-bind:age="25+1">hello</p> // 解析为<p v-bind:age="26">hello</p>
2. 数据绑定
- v-bind
作用:单向数据绑定,数据流为“data --> 页面”
简写:“:”
<input type="text" v-bind:value="msg">
或者
<input type="text" :value="msg">
- v-model
作用:双向数据绑定,数据流为“data <--> 页面”,又称为响应式数据绑定
简写:“v-model”,一般作用在表单类元素上,用于收集value属性值,所以可以省略value
<input type="text" v-model:value="msg">
或者
<input type="text" v-model="msg">
修饰符:
- lazy:失去焦点时收集数据
- number:将输入值转为有效数值
- trim:输入值两端空格过滤
3. 定义el的指向
- 指定模版
new Vue({
el: '#root',
})
- 通过Vue实例对象挂载模版
const vm = new Vue({}) // Vue构造函数的实例对象
vm.$mount('#root');
4. data创建内部状态
- 对象类型
多次调用一个组件时,data对象中的属性相互影响
data:{...}
- 函数类型
组件中的data必须是一个函数,每次调用函数都会返回一个新的对象,互不影响
data(){
return{...}
}
5. MV*模式
- MVC模式
一种架构设计模式,其强制将业务数据(Model)与用户界面(View)隔离,用控制器(Controller)管理逻辑和用户输入,MVC 是单向通信,View 跟 Model 必须通过 Controller 来承上启下
Model的作用:
- 负责保存应用数据,与后端交互并同步应用数据,校验数据等
- 当Model改变时,会通过其观察者(视图)作出响应反应
View的作用:
- 表示当前状态的视图
- 用户可以与View进行交互来实现读取编辑Model
Controller的作用:
负责连接View和Model,Model的任何改变会显示在View中,View的任何操作会应用到Model中
MVC模式缺点:一个View可能对应多个Model,一个Model可能对应多个View,逻辑复杂且不易于维护
- MVVM
在MVC的基础上,将Controller替换为ViewModel,实现了双向的数据绑定,即View的数据改变可以直接影响ViewModel,反之亦然
视图模型 ViewModel 是 MVVM 模式的核心,它是连接 View 和 Model 的桥梁,即Vue的实例对象
- 将模型 Model 转化成视图 View,实现的方式是:数据绑定
- 将视图 View 转化成模型 Model,实现的方式是:DOM事件监听
当这两个方向的数据转换都实现时,我们称之为数据的双向绑定
6. 数据代理
- 原理
Vue通过Object.definedPrototype把data对象中的所有的属性添加到new Vue()返回的实例对象上,并为每一个属性都添加了一个getter和setter,分别操作属性内部的读/写操作,并完成Vue的界面更新操作。
- Vue实例对象
7. 事件处理
- 绑定事件
<button v-on:click="handleClick">按钮</button>
<button @click="handleClick">按钮</button>
- 配置函数
methods:{
handleClick(){},
... ...
}
- 传递参数
<button v-on:click="handleClick($event,'2222')">按钮</button>
handleClick(e,value){
console.log(e,value); // e为事件对象,value为传递的参数
}
- this指向
- methods中的函数如果为普通函数,则Vue会统一管理其this,使其指向vm或者vc(vue component)
- methods中的函数如果为箭头函数,则this指向Window全局对象
- 事件修饰符
prevent:阻止默认事件
<button @click.prevent="handleClick">按钮</button>
stop:阻止冒泡
<button @click.stop="handleClick">按钮</button>
once:事件只触发一次
<button @click.once="handleClick">按钮</button>
capture:使用事件的捕获模式
<button @click.capture="handleClick">按钮</button>
self:只有当event.target是当前操作的元素时才触发事件
<button @click.self="handleClick">按钮</button>
passive:事件的默认行为立即执行,无需等待事件回调执行完毕
<button @click.passive="handleClick">按钮</button>
注意:事件修饰符可以连用,如.stop.prevent....
8. 键盘事件
- 按键别名
@keydown.键盘别名 = “事件名称”
- 回车:enter
- 删除:delete
- 退出:esc
- 空格:space
- 换行:tab(必须配合keydown使用)
- 上:up
- 下:down
- 左:left
- 右:right
- 事件分类
keydown:键盘按下时触发
<input type="text" v-model="msg" @keydown.ctrl="handleClick">
keyup:键盘抬起时触发
<input type="text" v-model="msg" @keyup.ctrl="handleClick">
- 系统修饰键
系统修饰键包括:ctr、alt、meta、shift
- 配合keydown使用时正常触发
- 配合keyup使用时,必须同时按下其他键,然后释放其他键时才能触发事件
9. 计算属性
// 使用
<h3>{{aDouble}}</h3>
// 创建
computed: {
// 仅读取
aDouble: function () {
return this.a * 2
},
// 读取和设置
aPlus: {
get: function () {
return this.a + 1
},
set: function (v) {
this.a = v - 1
}
}
}
- 作用
基于data对象中的属性创建一个新的属性,该属性具有缓存机制,该属性会被挂载到vm实例上
computed: {
newAge() {
return this.age + this.name;
},
},
- 原理
计算属性底层依赖Object.definedProperty方法提供的getter和setter
- get函数执行时机
- 初次读取计算属性时
- 当依赖的数据发生改变时
- this指向
- computed中的函数如果为普通函数,则Vue会统一管理其this,使其指向vm或者vc(vue component)
- computed中的函数如果为箭头函数,则this指向Window全局对象
10. 监视属性
- 使用
<div id="root">
<h3>年龄:{{age}}</h3>
<button @click="updateAge">按钮</button>
</div>
data: {
age: 25,
},
watch:{
// 简写形式——函数
age(newValue,oldValue){
console.log('age被修改了',newValue,oldValue);
}
// 完整形式——对象
age:{
immediate:true,
handler(newValue,oldValue){
console.log('age被修改了',newValue,oldValue);
}
}
}
// 通过vm实例动态创建属性监视
vm.$watch('被监视的属性',{
// 相关配置项
})
- 触发时机
当被监视的属性变化时,回调函数自动调用,并进行相关操作
- 配置项
- immediate:布尔值,表示是否在初次加载时进行监视,默认是false
- deep:布尔值,表示是否开启深度监视,默认是false
- this指向
- watch中的函数如果为普通函数,则Vue会统一管理其this,使其指向vm或者vc(vue component)
- watch中的函数如果为箭头函数,则this指向Window全局对象
- watch与computed对比
computed和watch的功能类似,但是computed不可进行异步操作,但是watch可以
11. 绑定样式
- class外联样式
<div :class="mood" class="basic">你好</div>
data: {
mood:"normal1", // 字符串
mood:['nor1','nor2','nor3'] // 数组
mood:{ // 对象
nor1: false,
nor2: true,
nor3: false
}
},
- style内联样式
// 动态值
<div style='{fontSize: font + 'px'}'>你好</div>
// 样式对象
<div :style='style1' :style="[style1,style2]">你好</div>
data: {
style1:{
fontSize:'40px'
}
style2:{
color: 'red'
}
},
12. 条件渲染
- v-if
<ul>
<li v-if="age === 25">1</li>
<li v-else-if="age === 26">2</li>
<li v-else>3</li>
</ul>
适用场景:切换频率较低的场景,元素不会显示在DOM树上,无法通过JS获取元素。
- v-show
<ul>
<li v-show="age <= 30">1</li>
<li v-show="age > 30">2</li>
</ul>
适用场景:切换频率较高的场景,元素会出现在DOM树上,通过添加或移除display:none实现显示和隐藏,可以通过JS获取元素。
13. 列表渲染
- 使用
// 可以遍历数组、对象、字符串、指定次数
<div v-for="(value,index) in list" :key="index">{{value}}</div>
- key的作用
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新的数据生成新的虚拟DOM,随后Vue会进行新虚拟DOM和旧虚拟DOM的对比,过程如下:
1)旧虚拟DOM中找到和新虚拟DOM相同的key
若虚拟DOM中内容没变,直接使用之前的虚拟DOM
若虚拟DOM中内容改变,则生成新的真实DOM,并替换之前的真实DOM
2)旧虚拟DOM中没有找到和新虚拟DOM相同的key
创建新的虚拟DOM,随后重新渲染页面
3)如果使用index作为key,带来的问题
若对数据进行逆序添加、逆序删除等破坏顺序的操作,会产生不必要的更新
若包含表单元素,会导致DOM元素更新错误
14. Vue监测数据
- 对象
Vue会监测data中所有层次的数据,会在底层转换为响应式数据,无需手动处理
Vue.set或者vm.$set
// set函数绑定在Vue构造函数上
Vue.set(target,property/index,value)
// set函数绑定在Vue原型上
vm.$set(target,property/index,value)
- target:要更改的数据源,对象或数组
- property/index:要更改的具体数据,对象属性或数组下标
- value:重新赋予的值
作用:向响应式对象(不能是Vue实例,data,已有属性)中添加一个响应式属性/元素,并且保证这个数据是响应式的,可以触发视图更新。
data(){
return {
goodList: [
{name: '书本', value: '200元', id: 1},
{name: '玩偶', value: '200元', id: 2},
{name: '薯片', value: '300元', id: 3}
]
}
},
methods:{
handleClick(){
this.$set(this.goodList, 3, {name: '口罩', value: '409元', id: 4})
}
}
- 数组
vue中对于数组元素没有实现响应式处理,如果更改数组元素时希望视图刷新,需要使用封装后的Arr原始API,包括push、pop、shift、unshift、splice、sort、reverse。这些API在底层共做了两件事:
第一步:调用原生方法,对数组进行操作 第二步:重新解析模版,进而更新视图
15. 收集表单数据
- text输入框,v-model等于value值
<input type="text" v-model="msg"/>
- radio单选框,v-model等于标签配置的value值
<input type="radio" v-model="msg"/>
- checkbox,无value属性或者value属性值非数组时,v-model等于checked勾选状态;否则为value数组值
<input type="checkbox" v-model="msg"/>
16. 过滤器
- 局部过滤器
filters:{
timefilter(value){
return value.format('YYYY年MM月DD日 HH:mmss');
}
mySort(value){
return value.slice(0,5)
}
}
- 全局过滤器
Vue.filter('mySort',function(value){...});
- 作用
对显示的数据进行特定格式化后在显示,不会改变原有数据,会产生一个新的数据。
过滤器可以接收参数,也可以将多个过滤器串联使用
<div>现在的时间:{{time | timefilter | mySort}}</div>
// 或者
<input type="text" v-bind:value="msg | mySort">
17. 其他v-指令
- v-text
向其所在节点中渲染文本内容,会替换掉节点中的内容,插值语法会整合节点中的内容
<div v-text="msg"></div>
- v-html
向指定节点中渲染包含html结构的内容,可以识别html结构,会替换掉节点中的内容。由于可以解析html结构,所以存在一定的安全问题,容易引发xss攻击。
<div v-html="msg"></div>
- v-cloak
其本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。可以配合css使用,解决网速慢时显示{{xxx}}的中间态。
<div v-cloak>{{name}}</div>
[v-cloak]{
display:none
}
- v-once
所在节点在初次动态渲染后,就改为静态内容,即使数据更新,所在节点也不会随之更新了,一般用于性能优化
<div v-once>{{name}}</div>
- v-pre
所在节点会跳过编译过程,缩短编译时间,一般用于无任何语法的节点的性能优化
<div v-pre>xxx系统</div>
18. 自定义指令
- 使用
// 局部指令——完整形式
new Vue({
directives:{
指令名:{
bind(element,binding){} // 指令与元素成功绑定时触发
insered(element,binding){} // 指令所在元素被插入页面时触发
update(element,binding){} // 指令所在模版被重新解析时触发
}
}
})
// 局部指令——简写形式
new Vue({
directives:{
指令名(){}
}
})
// 全局指令
Vue.directive(指令名,配置对象)
Vue.directive(指令名,回调函数)
- 配置对象
bind(element,binding){} // 指令与元素成功绑定时触发
insered(element,binding){} // 指令所在元素被插入页面时触发
update(element,binding){} // 指令所在模版被重新解析时触发
- 注意事项
- 指令定义时不需要添加v-前缀,使用时需要添加
- 指令名有多个时,需要使用kebab-case形式命令,禁止使用驼峰命名法
19. ref属性
// 绑定
<div ref='title'>{{name}}</div>
// 获取
this.$refs.title
作用:给元素或者是子组件注册引用信息
返回值:应用在html标签上,获取到的是真实DOM元素,应用在组件标签上,获取到的是子组件实例对象
第三章 Vue生命周期
1. 完整流程
init Events&Lifecycle:初始化生命周期、事件,但是数据代理还未开始
beforeCreate:将要创建vm,此时不能通过vm访问到data中的数据、methods中的方法
init injections&reactivity:初始化,数据监测、数据代理
created:vm创建完毕,此时可以通过vm访问到data中的数据、methods中配置的方法
beforeMount:将要挂载vm,页面呈现的是未经Vue编译的DOM结构,在此阶段所有对DOM的操作最钟都不生效
mounted:vm挂载完毕,页面呈现的是经过Vue编译的DOM结构,在此阶段所有对DOM的操作均有效,但不推荐。一般在此阶段开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作
beforeUpdate:将要更新vm,数据已更新,但是页面未同步
updated:vm更新完毕,数据和页面均更新完毕
beforeDestory:将要销毁vm,页面销毁前,一般在此阶段关闭定时器、取消订阅消息、解绑自定义事件等收尾操作
destoryed:vm销毁完毕,此时自定义事件会失效,原生事件依然可用,数据更新也不会触发视图更新
2. 常用钩子函数
mounted:发送ajax请求,启动定时器,绑定自定义事件,订阅消息
beforeDestory:关闭定时器、取消订阅消息、解绑自定义事件
beforeCreate(){},
created(){},
beforeMount(){},
mounted(){},
beforeUpdate(){},
updated(){},
beforeDestroy(){}
3. this指向
普通函数:指向vm实例对象
箭头函数:指向undefined
第四章 组件化模式
1. Vue组件化
实现应用中局部功能代码和资源的集合
2. 组件嵌套
- 父组件
<template>
<div id="app">
<h2>{{ title }}</h2>
<HelloWorld/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
name: "App",
components:{HelloWorld},
data() {
return {
title:'臭臭系统'
};
},
};
</script>
- 子组件
<template>
<div>
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data(){
return{
msg:"臭臭爱吃猫条"
}
},
}
</script>
3. props属性
props只读属性,禁止对其进行修改。
简单接收:
props:['name','age','sex']
对数据进行类型限制:
props:{
name: string,
age:number,
sex:string
}
限制数据类型、必要性及默认值:
props:{
name:{
type:string,
required: true,
defaultValue:'臭臭'
}
}
传递数据:
<HelloWorld name="name"/>
4. mixin混入
作用:可以把多个组件共用的配置提取成一个混入对象
定义混合:
{
data(){},
methods:{},
... ...
}
使用混合:
// 全局混入
Vue.mixin(xxx)
// 局部混入
mixins:['xxx']
5. 插件
作用:提取全局功能与逻辑等,用于增强Vue
定义插件:
export default {
install(Vue){
// 全局过滤器
Vue.filter('mySlice',function(value){
return value.slice(0,4);
});
// 全局指令
Vue.directive('fbind',{
bind(element,binding){
element.value = binding.value;
},
inserted(element,binding){
element.focus();
},
update(element,binding){
element.value = binding.value;
},
});
// 混入
Vue.mixin({
data(){
return{
x:100,
y:200
}
}
});
// Vue原型添加方法(vm与vc共享)
Vue.prototype.hellow = function(){
console.log('hellow');
};
}
}
调用插件:
import Vue from 'vue';
import plugin from './plugins';
Vue.use(plugin);
接收参数:
export default {
install(Vue,options){} // Vue:实例,options:传递参数
}
6. 子组件给父组件传参
- 父组件给子组件传递函数类型的props
————————父组件—————————
<template>
<div id="app">
<HelloWorld :outChangeMsg="outChangeMsg" />
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
name: "App",
components:{HelloWorld},
methods:{
outChangeMsg(value){
console.log('子组件给父组件传递了参数',value);
}
},
};
</script>
——————子组件——————————
<template>
<div>
<button @click="chengeMsg">按钮</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props:['outChangeMsg'],
methods:{
chengeMsg(){
this.outChangeMsg('酸酸');
}
}
}
</script>
- 父组件调用子组件时,给子组件绑定一个自定义事件(v-on或者@)
————————父组件—————————
<template>
<div id="app">
<HelloWorld @outChangeMsg="outChangeMsg" /> // 绑定自定义事件
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
name: "App",
components:{HelloWorld},
methods:{
outChangeMsg(value){
console.log('子组件给父组件传递了参数',value);
}
},
};
</script>
——————子组件——————————
<template>
<div>
<button @click="chengeMsg">按钮</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
methods:{
chengeMsg(){
this.$emit('outChangeMsg','酸酸'); // 触发自定义事件
}
}
}
</script>
- 父组件调用子组件时,给子组件绑定一个自定义事件(ref)
————————父组件—————————
<template>
<div id="app">
<HelloWorld ref="hellowWorld" />
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
name: "App",
components:{HelloWorld},
methods:{
outChangeMsg(value){
console.log('子组件给父组件传递了参数',value);
}
},
mounted(){
setTimeout(()=>{
this.$refs.hellowWorld.$on('outChangeMsg',this.outChangeMsg)
},1000)
}
};
</script>
——————子组件——————————
<template>
<div>
<button @click="chengeMsg">按钮</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
methods:{
chengeMsg(){
this.$emit('outChangeMsg','酸酸');
}
}
}
</script>
- 子组件解绑自定义事件
<template>
<div>
<button @click="chengeMsg">按钮</button>
<button @click="offChangeMsg">解绑</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
methods:{
chengeMsg(){
this.$emit('outChangeMsg','酸酸');
},
offChangeMsg(){
this.$off('outChangeMsg'); // 字符串类型,解绑一个自定义事件
this.$off(['outChangeMsg','outChangeName']); // 数组类型,解绑多个个自定义事件
this.$off(]); // 无参数,解绑当前组件绑定的所有自定义事件
}
}
}
</script>
7. 全局事件总线
作用:一种组件间通信的方式,适用于任意组件间通信
安装全局事件总线:
new Vue((
.....
beforeCreate(){
Vue.prototype.$bus = this // 安装全局事件总线,$bus就是当前应用的vm
}
......
})
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
methods(){
demo(data){......) // 处理接收到的数据
}
mounted(){
this.$bus.$on('xxxx' ,this.demo) // 接收数据
}
传递数据:B组件提供数据源,并向外传递
this.$bus.$emit('xxxx',数据源);
解绑事件:在beforeDestroy钩子中用$off解绑当前组件用到的事件
beforeDestroy(){
this.$off();
}
8. 消息订阅与发布
作用:一种组件间通信的方式,适用于任意组件间通信,可以使用第三方库如pubsub实现
使用方式:
第一步:安装pubsub
npm i pubsub-js
第二步:引入
import pubsub from 'pubsub-js'
第三步:接收数据
// A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
methods(){
demo(data){}
}
mounted(){
const pid = pubsub.subscribe('xxx',this.demo) // 订阅消息
this.pid = pid // 保存订阅事件,方便取消订阅使用
}
第四步:提供数据
pubsub.publish('xxx',数据)
第五步:取消订阅
beforeDestroy(){
Pubsub.unsubscribe(this.pid) // 去取消订阅
}
9. nextTick
this.$nextTick(回调函数)
作用: 在下一次 DOM 更新结束后执行其指定的回调
适用场景:当改变数据后,要基于更新后的新DOM进行某些操作时,可以在nextTick所指定的回调函数中执行。
10. vue脚手架配置代理
- proxy单一配置方式
工作原理:当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
// vue.config.js
devServer:{
proxy:'http://localhost:5000' // 服务器地址
}
优点:配置简单,请求资源时直接发给前端 (8080) 即可
缺点:不能配置多个代理,不能灵活的控制请求是否走代理
- proxy多配置方式
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api1': { // 匹配所有以'/api1'开头的请求路径
target:"http://localhost:5008', // 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
'/api2': { // 匹配所有以/api2'开头的请求路径
target:http://localhost:5001', // 代理目标的基础路径
changeOrigin: true,
pathRewrite: { '^/api2': ''}
}
}
}
}
- changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000,默认值为true
- changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
优点:可以配置多个代理,且可以灵活的控制请求是否走代理
缺点:配置略微繁琐,请求资源时必须加前缀
11. 插槽
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式
分类:默认插槽、具名插槽、作用域插槽
- 默认插槽
// 外层组件调用内层组件时传递数据
<Test1>
<h2>{{ msg }}</h2>
</Test1>
// 内层组件通过slot插槽接收数据,并且可以设置默认值状态
<div>
<h1>Test1</h1>
<slot>loading... ...</slot>
</div>
- 具名插槽
// 外层组件调用内层组件时传递数据,通过slot指定插槽
<Test1>
<h2 slot="name">{{ name }}</h2>
// template作为外层容器,不会出现在DOM树上
<template v-slot:footer>
<div class="foot">
<a href="http://www.atguigu.com">经典</a>
<a href="http://www.atguigu.com">热门</a>
<a href="http://www.atguigu.com">推荐</a>
</div>
<h4>欢迎前来观</h4>
</tempIate>
</Test1>
// 内层组件通过slot插槽接收数据,可以设置默认值和插槽名称name
<div>
<h1>Test1</h1>
<slot name="name">loading... ...</slot>
<slot name="footer">loading... ...</slot>
</div>
- 作用域插槽
外层组件控制插槽的结构,内层组件控制插槽中需要的数据,内层组件需要通过插槽传递数据给外层组件
// 外层组件
<Test1>
<template scope="value"
<ul>
<li v-for="(g,index) in value.games" :key="index">{{g}}</li>
</ul>
</template>
</Test1>
// 内层组件
<div>
<h1>Test1</h1>
<slot :games="games">以我是默认的一些内容</slot>
</div>
第五章 Vuex
1. Vuex简介
概念:专门在 Vue 中实现集中式状态 (数据) 管理的一个 Vue 插件,对vue 应用中多个组件的共享状态进行集中式的管理 (读/写),也是一种组件间通信的方式,适用于任意组件间通信。
适用场景:多个组件依赖于同一状态、来自不同组件的行为需要变更同一状态
2. 搭建Vuex环境
- 创建vuex.js文件
// 引入Vuex
import Vue from 'vue';
import Vuex from 'vuex';
// 使用Vuex插件
Vu e.use(Vuex);
//准备actions,用于响应组件中的动作
const actions = {};
//准备mutations,用于操作数据 (state)
const mutations = {};
//准备state,用于存储数据
const state = {};
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
});
- 在main.js中传入store配置项
//引入store
import store from "./store"
new Vue({
el:'#app',
render: h => h(App),
store
});
- 组件中读取vuex中的数据
this.$store.state.sum
- 组件中修改vuex中的数据
this.$store.dispatch('action中的方法名', 数据)
this.$store.commit('mutations中的方法名', 数据)
注意:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写 dispatch,直按编写 commit
3. getters
作用:可以使用getters加工state中的数据后再使用,通过缓存优化性能
- 在store.js 中追加 getters 配置
const getters = {
bigSum(state){
return state.sum * 19
}
}
//创建并暴露store
export default new Vuex.Store({
getters
})
- 组件中读取数据
this.$store.getters.bigSum
4. mapState、mapGetters、mapActions、mapMutations
mapState:映射state中的数据为计算属性
computed: {
// 借助 mapstate生成计算属性: sum、school、subject (对象写法)
...mapState({sum: 'sum', school: ' school', subject: ' subject'})
// 借助 mapState生成计算属性: sum、school、subject (数组写法)
...mapState(['sum', ' school', ' subject']),
mapGetters:映射 getters 中的数据为计算属性
computed: {
// 借助 mapGetters生成计算属性:bigSum (对象写法)
...mapGetters((bigSum: 'bigSum']),
// 借助 mapGetters生成计算属性: bigSum (数组写法)
...mapGetters(['bigSum'])
}
mapActions:生成与 actions 对话的方法,即包含 $store.dispatch(xxx)的函数
methods:{
// 依赖 mapActions生成: incrementOdd、incrementWait (对象形式)
...mapActions({incrementOdd:'jiaodd',incrementWait:'jiaWait'})
//依赖 mapActions生成: incrementOdd、incrementWait (数组形式)
...mapActions(["jiaodd',"jiawait'])
}
mapMutations:生成与mutations 对话的方法,即包含$store.commit(xxx)的函数
methods:{
//依赖 mapActions生成: increment、decrement (对象形式)
...mapMutations(fincrement:"JIA' ,decrement:'JIAN'])
//依赖 mapMutations生成: JIA、JIAN (对象形式)
...mapMutations(['JIA', JIAN']),
}
备注: mapActions与mapMutations使用时,若需要传递参数,则需要在模板中绑定事件时传递好参数,否则接收到的参数是事件对象
5. Vuex模块化
目的:让代码更好维护,让多种数据分类更加明确
// store.js
const countAbout = {
namespaced:true, //开启命名空间
state:{x:1},
mutations: { ... },
actions: { ... },
getters: {
bigSum(state){
return state.sum * 10
}
},
}
const personAbout = {
namespaced:true, //开启命名空间
state:{ ... },
mutations: {...},
actions: {...},
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
});
开启命名空间后,组件中读取state数据:
// 直接读取
this.$store.state.personAbout.list;
// 借助mapstate
...mapState('countAbout',[ 'sum', 'school', 'subject']);
开启命名空间后,组件中读取getters数据:
// 直接读取
this.$store.getters['personAbout/firstPersonName'];
// 借助mapGetters
...mapGetters('countAbout',['bigSum']);
开启命名空间后,组件中调用dispatch:
// 直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person);
// 借助mapActions
...mapActions('countAbout',[incrementOdd:'jiaodd',incrementwait:'jiawait'));
开启命名空间后,组件中调用commit:
// 直接commit
this.$store.commit('personAbout/ADD_PERSON',person);
// 借助mapMutations
..mapMutations('countAbout',(increment:'JIA',decrement:'JIAN']);
第六章 vue-router
1. vue-router简介
- SPA 应用概念
SPA又名单页Web 应用(single page web application,SPA) ,整个应用只有一个完整的页面 index.html,页面所需数据需要通过 ajax 请求获取。点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
- 路由分类
后端路由:value 是function,用于处理客户端提交的请求。服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。
前端路由:value 是component,用于展示页面内容。当浏览器的路径改变时,对应的组件就会显示。
- 路由
一个路由 (route) 就是一组映射关系 (key - value) ,多个路由需要路由器 (router) 进行管理。
前端路由中key是路径,value是组件。
// 安装vue-router
npm i vue-router
// 应用插件
Vue.use(VueRouter)
// 引入VueRouter
import VueRouter from 'vue-router';
// 引入组件
import About from '../components/About'
import Home from '../components/Home'
// 创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
routes:[
{
path: ' /about',
component: About
},
{
path: ' /home' ,
component :Home
}
]
})
//暴露router
export default router
实现切换 (active-class可配置高亮样式):
<router-link active-class="active"to="/about">About</router-link>
展示位置:
<router-liew></router-view>
使用注意事项:
- 路由组件通常存放在pages 文件夹,一般组件通常存放在 components 文件夹
- 通过切换,被“隐藏”的路由组件默认被销毁掉,需要的时候再去挂载
- 每个组件都有自己的 $route 属性,里面存储着组件自己的路由信息
- 整个应用只有一个router,可以通过组件的 $router 属性获取到
2. 多级嵌套路由
// 使用children配置多级嵌套路由
routes:[
{
path:'/about',
component:About,
},
{
path: '/home',
component:Home,
children:[
{
path:'news',//此处一定不要写: /news
component:News
},
{
path:'message',//此处一定不要写: /message
component:Message
}
]
}
]
多级路由跳转(要写完整路径) :
<router-link to="/home/news">News</router-link>
3. 路由的query参数
传递参数(字符串或者对象):
// 跳转并携带query参数,to的字符串写法
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
// 跳转并携带query参数,to的对象写法
<router-link
:to="{
path: '/home/message/detail'
query: {
id:666,
title:"你好"
}
}">
跳转</router-link>
接收参数:
this.$route.query.id
this.$route.query.title
4. 命名路由
作用:简化路由的跳转操作
第一步:路由命名
{
path: '/demo',
component: Demo,
children: [
{
path: "test",
component: Test,
children:[
{
name: "hello", //给路由命名
path: "welcome",
component: Hello,
}
]
}
]
}
第二步:简化路由跳转
// 简化前,需要写完整的路径
<router-link to="/demo/test/welcome”>跳转</router-link>
// 简化后,直接通过名字跳转
<router-link :to="{name: 'hello'}">跳转</router-link>
// 简化写法配合传递参数
<router-link:to="{
name: ' hello',
query:{ id:666, title:'你好'}
}>
跳转</router-link>
5. 路由的params参数
声明接收params参数:
{
path: '/home',
component: Home,
children:[
{
component: Message,
children:[
{
name: 'xiangging',
path: 'detail/:id/:title', // 使用占位符声明接收params参数
component: Detail
}
]
}
]
}
传递参数:
// 跳转并携带params参数,to的字符串写法
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
// 跳转并携带params参数,to的对象写法
<router-link
:to="{
name: 'xiangqing',
params:{ id: 666, title:"你好"}
}"
>
跳转</router-link>
注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置
接收参数:
this.$route.params.id
this.$route.params.title
6. 路由的props配置
作用:让路由组件更方便的收到参数
{
name:'xiangqing,
path:'detail/:id',
component:Detail,
// props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
props:{ a:900 },
// props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
props:true,
// props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
7. router-link的replace属性
作用:控制路由跳转时操作浏览器历史记录的模式
浏览器的历史记录有两种写入方式: push 和 replace,路由跳转时候默认为push
- push 是追加历史记录
- replace 是替换前记录
// 开启replace模式
<router-link replace>News</router-link>
8. 编程式路由导航
作用:不借助实现路由跳转,让路由跳转更加灵活
// push和replace
this.$router.push({
name: 'xiangqing',
params: { id: xxx, title:xxx }
})
this.$router.replace({
name: 'xiangqing',
params: { id:xxx, title:xxx }
})
this.$router.forward() //前进
this.$router.back() // 后退
this.$router.go() // 跳转
9. 缓存路由组件
作用:让不展示的路由组件保持挂载,不被销毁
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
生命周期钩子函数:路由组件所独有的两个钩子,用于捕获路由组件的激活状态
- activated 路由组件被激活时触发
- deactivated 路由组件失活时触发
10. 路由守卫
作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
- 全局前置守卫
执行时机:初始化时、每次路由切换前
router.beforeEach((to, from, next)=>{
console.log('beforeEach' ,to,from);
if(to.meta.isAuth){
//判断当前路由是否需要进行权限控制
if(localStoragegetItem('school') ===atguigu'){
//权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看)
}
}else{
next() //放行
}
});
- 全局后置守卫
执行时机:初始化时、每次路由切换后
router.afterEach((to, from)=>{
console.log('afterEach',to,from);
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document,title = "vue test
}
});
- 组件独享守卫
执行时机:路由切换后
// routes字典内配置
beforeEnter((to, from, next){
console.log('beforeEnter',to,from);
if(to.meta.isAuth){
//判断当前路由是否需要进行权限控制
if(localstorage.getItem('school') === 'atguigu'){
next();
}else{
alert("暂无权限查看");
}else{
next()
}
});
- 单个组件内守卫
进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next){}
离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next){}
11. 路由器模式
- hash模式
url中#后面的内容即为hash值,hash值不会包含在 HTTP 请求中。
特点:
- 地址中永远带着#号,不美观
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
- 兼容性较好
- history模式
- 地址干净,美观
- 兼容性和hash模式相比略差
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题