第一章 vue介绍
1.1Vue配置
vue是构建用户界面的渐进式框架 特点是易用 灵活 性能优
vue脚手架的安装 vue CLI
安装 node.js 安装vue cli
创建vue工程
1.2 vue工程目录介绍
工程介绍
node_modules:存放npm命令下载的开发环境和生产环境的依赖包
public:项目的入口文件
src:存放项目源码和需要引用的资源的文件
assets:存放项目中需要用到的资源文件 css js images 等
componets:存放vue开发中的一些公共组件 例如项目初始的header.vue footer.vue就是公共组件
router:vue路由的配置文件
views:存放页面文件
app.vue :使用标签渲染整个工程的vue组件
main.js:vue-cli工程的入口文件
第二章 Vue的双向绑定
2.1 声明式渲染
我们知道app.vue 是工程的根组件,所以我们将很多代码放在根组件app.vue中
每一个vue文件都由三个部分组成:template script style分别对应 html js 和css
template里面只能有一个块状标签 一般为div 有时也可以是其他
vue采用差值表达式来渲染数据{{}} 两层花括号
例如使用差值表达式渲染字符串
<template>
<h2>{{title}}</h2>
</template>
<script>
// export default是固定格式,不需要纠结
export default {
// 模块的名字
name: 'app',
// 页面中数据存放的地方
data() {
return {
title: '优课达--学的比别人好一点',
};
},
};
</script>
<!-- scope的意思表示这段样式只在本xxx.vue文件中生效,其他xxx.vue文件中不会生效,有锁定的意思 -->
<style scope>
h2 {
color: deeppink;
border: 1px solid #cccccc;
}
</style>
或者用差值表达式渲染数组
<template>
<div>
<h2 class="title">{{title}}</h2>
<ul class="list">
<li>{{todoList[0]}}</li>
<li>{{todoList[1]}}</li>
<li>{{todoList[2]}}</li>
<li>{{todoList[3]}}</li>
<li>{{todoList[4]}}</li>
</ul>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
title: "今日待完成事项",
todoList: [
"完成HTML标签学习",
"完成CSS文字样式学习",
"完成CSS盒模型学习",
"完成Flex布局学习",
"完成JavaScript入门学习"
]
};
}
};
</script>
注意点 :data方法用来存放数据或者全局变量
scope可以理解为锁,将代码锁在本文件内,只有本文件内有用
当然还可以用差值表达式来渲染对象
2.2 处理用户输入
用户输入有哪些:input textarea select 单选 复选
v-model(双向绑定)–input
什么是双向绑定呢?我们拿input标签来说,如果有一个变量a绑定了input标签,a变量发生改变那么标签的值也会发生改变并且始终和a标签保持一致
如果input标签发生改变,a变量也会改变为和input标签相同的值
例如
<template>
<p class="page">{{message}}</p>
<input type="text" v-model="message" placeholder="请输入你想输入的内容" />
</template>
<script>
export default {
name: "app",
data() {
return {
message: ""
};
}
};
</script>
同理可以适用到text-area上 <textarea v-model="message" placeholder="请在输入框内输入...">
v-model(双向绑定)checkbox(复选框)
当checkbox绑定一个变量a时,复选框就会和变量a保持同步,举一个例子
<template>
<div class="food">
<div class="check-box">
<input type="checkbox" value="新奥尔良鸡腿堡" v-model="checkedGoods" /> //在这里checkeGoods这个变量和 checkbox这个复选框双向
绑定了 当选中复选框时,变量checkedGoods就会被赋予checkbox的值 这里为:新奥尔良鸡腿堡
</div>
<div class="food-name">新奥尔良鸡腿堡</div>
<div class="food-price">¥24</div>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
checkedGoods: []
};
}
};
</script>
这就是常用的三种用户输入
2.3处理用户事件
v-on:事件绑定 例子
<button v-on:click="add">按钮</button> //这是绑定点击事件
还可以简写为
<button @click="add">按钮</button> //v-on:可以简写为@
methods方法:一般我们绑定了事件后,都需要给事件添加方法来告诉系统当这个事件发生之后,将会调用什么方法
<template>
<p>{{ counter }}</p>
<button @click="add">点击</button> //绑定事件的位置 add就是方法名 也可以写成add()括号里面可以传递参数
</template>
<script>
export default {
name: "app",
data() {
return {
// 初始次数
counter:0
};
},
methods:{ //这是方法书写的位置,很重要不要写错了位置
add:function(){ //这是出发了事件后要调用的方法
// 这里的this指向当前的vue实例
this.counter++ //注意这个this,当你需要使用data里面定义的变量的时候你就需要在前面添加this
}
}
};
</script>
常用的三种事件修饰符
1 阻止冒泡事件
<div @click.stop="fn2"></div>
2捕获事件
<div class="div2" @click.capture="fn2"></div
3阻止默认时间
<div class="div2" @click.prevent="fn2"></div>
2.4 监听数据变化
在vue中通过侦听器来监听数据变化
<script>
export default {
name: 'app',
data: function () {
return {
count: 1,
};
},
methods: {
add: function () {
this.count++; //运行的时机,当data的数据发生改变时运行
},
},
watch: { //侦听器书写的位置
count() { //监听的对象 一般是data里面的数据
console.log('count发生了变化');
},
},
};
</script>
我们还可以获得前一次的数据和现在的数据
watch:{
inputValue(value,oldValue) { // 第一个参数为新值,第二个参数为旧值,不能调换顺序
console.log(`新值:${value}`);
console.log(`旧值:${oldValue}`);
}
}
浏览器第一次渲染页面的时候是不会触发侦听器的,我们我可以使用 handler方法和immediate属性来使监听初次渲染的数据
<script>
export default {
name: 'app',
watch: {
firstName: {
handler: function (newName, oldName) {
this.fullName = newName + ' ' + this.lastName;
},
immediate: true, //当immediate属性值为true时,第一次渲染就可以侦听
},
},
};
</script>
第三章 vue template
3.1 html属性渲染语法
标签是有属性的,我们可以通过属性绑定来使标签属性的值发生动态变化
动态绑定 v-bind 简写为:
<template>
<div id="app">
<img src="#" v-bind:alt="imgText"> //alt属性表示当图片加载不出来时,有一个文字去说明图片是什么内容。用v-bind绑定此属性后,动态
输入此内容
</div>
</template>
<script>
export default {
name: "app",
// 数据
data() {
return {
imgText:'周杰伦演唱会图片'
};
}
};
</script>
3.2 模板中使用表达式
在模板中我们可以进行很多操作 例如:计算 使用三元表达式 写方法
计算:
<template>
<div id="app">
<ul>
<li>{{ goods[0].index + 1 }}---{{ goods[0].name }}</li>
<li>{{ goods[1].index + 1 }}---{{ goods[1].name }}</li>
<li>{{ goods[2].index + 1 }}---{{ goods[2].name }}</li>
</ul>
</div>
</template>
使用三元表达式
<template>
<div id="app">
<p>{{ flag?'你已经通过了考试:'你还没有通过考试' }}</p> //三元表达式 问号前面是条件 true则执行前面的 false执行后面的
<button @click="exchange">转换</button>
</div>
</template>
在模板中写方法;
<template>
<div id="app">
<p>{{ message.split('').reverse().join('') }}</p> //方法尽量不写在模板中
</div>
</template>
3.3 条件渲染语句
条件渲染语句可以理解为根据不同的条件来渲染不同的模块
v-if
<p v-if="isShow">{{ message }}</p> // 当isshow的结果为true时,渲染p标签
v-else:当if条件不满足的时候就执行 v-else渲染的标签
<p v-if="isShow">{{ message }}</p>
<p v-else>{{ defaultMessage }}</p> // 如果if条件不满足,就渲染else所在的p标签
v-else-if:表示多种条件出现时满足某一种条件时 渲染某个标签
<p v-if="questions[0].type==='PICK'">{{ questions[0].content }}</p> //当
<p v-else-if="questions[1].type==='MULT'">{{ questions[1].content }}</p>//当
<p v-else>题目还没有加载进来...</p> //当
v-show 也是一种条件表达式,但是和v-if有所区别
如果条件不满足,v-show只是把display设置为none 而v-if则直接不存在。如果要求这个条件语句多次执行,可以使用v-show,次数不多则使用v-if。
###3.4列表渲染语句
循环渲染数字:v-for
<div id="app">
<ul>
<li v-for="item in 5" :key="item">{{ item }}</li> //从1遍历到5 得到5个li标签 item就是遍历出来的数字
</ul>
</div>
循环渲染数组
<ul>
<li v-for="(item,index) in nameList" :key="index">{{ index + 1 }}---{{ item }}</li> //遍历nameList里面的内容和索引,然后
</ul> 将索引+1 和内容作为标签的内容
<script>
export default {
name: "app",
// 数据
data() {
return {
nameList:["张淮森","周逸依","梁澄静","宁古薄","丘约靖"]
};
}
};
</script>
循环渲染对象
<!--
value:对象中每一项的值
key:对象中每一项的键
index:索引
-->
<li v-for="(value,key,index)" :key="index"></li> //注意循环对象时候的三个参数
例子
```bash
<ul>
<li v-for="(value,key,index) in book" :key="index">值:{{ value }}--键:{{ key }}--索引:{{ index }}</li>
</ul>
<script>
export default {
name: "app",
// 数据
data() {
return {
book:{
bookName:'指环王',
author:'JK 罗琳'
}
};
}
};
</script>
多数情况下循环渲染数组和渲染对象是同时进行的。即渲染数组中的对象
<ul>
<li v-for="(item,index) in books" :key="index">
{{ index+1 }}----{{ item.title }}----{{ item.author }}----{{ item.publishedTime }}
</li>
</ul>
<script>
export default {
name: "app",
// 数据
data() {
return {
books: [
{
title: '《魔戒》',
author: '约翰·罗纳德·瑞尔·托尔金',
publishedTime: '1954'
},{
title:'《哈利·波特》',
author:'J·K·罗琳',
publishedTime:'1997'
},{
title:'《人性的弱点》',
author:'戴尔•卡内基',
publishedTime:'2008'
}
]
};
}
};
:key
<li v-for="(item,index) in books" :key=""> //为了确保每个项目item是唯一的,我们需要添加一个唯一的key,一般是从后台取到的数据的id
第四章
4.1计算属性
书写位置:
<script> //也是写在script中,和data methods watch 并列
export default {
name: 'app',
// 计算属性
computed: {},
};
</script>
计算属性的写法:是一系列方法组成的键值对 键是方法名 值是方法体
<script>
export default {
name: "app",
data:function(){
return {
message:"优课达--学的比别人好一点~"
}
}
// 计算属性
computed:{
reverseMessage:function(){
return this.message.split('').reverse().join('')
}
}
};
</script>
计算属性和方法有时可以同时达到一个目的,那么我们为什么还要用计算属性呢?
计算属性有两个性质 :1缓存 上一次得到的值
2依赖(可以是data里面的变量) 当依赖发生改变,计算属性会重新计算返回结果 而依赖不变,就返回缓存
而方法是执行方法体里面的逻辑,然后得到结果
4.2 动态class
动态样式绑定
<div class="base" v-bind:class="{ active: isActive }"></div> //绑定active样式 isActive表示是否使用此样式
.active {
border: 1px solid green;
}
类名的书写
<div v-bind:class="{ 'base-active': isActive }"></div> //单个单词的类名直接书写 多个单词需要用单引号或双引号引起来
引号的规则
<!-- 外双内单 -->
<div v-bind:class="{ 'base-active': isActive }"></div>
<!-- 外单内双 -->
<div v-bind:class='{ "base-active": isActive }'></div> //简单来说就是内双外单,内单外双
多类名的写法
<div v-bind:class='{ "base-active": isActive,"base":true}'></div> //在花括号里面书写类名
动态样式绑定的写法
对象形式
<div v-bind:class="{ active: isActive }"></div>
<!--
类名中间有中划线的,要用单引号引起来或者使用驼峰命名如:lightText
-->
<div v-bind:class="{ active: isActive,'light-bg':true,lightText:false}"></div>
对象写在计算属性里面
<template>
<ul>
<div
class="base"
:class="hoverObj"
@mouseover="index=1"
@mouseout="index=-1"
>
选项选项
</div>
</ul>
</template>
<script>
export default {
name: "app",
// 数据
data() {
return {
index:-1
};
},
computed:{ //方法写在了计算属性里面
hoverObj(){
return {
hover: this.index === 1
}
}
}
};
</script>
数组语法
<!-- 数组中可以使用三元表达式 -->
<div class="base" v-bind:class="['red','size',isActive?'blue':'']">
选项选项
</div>
<!-- 数组中可以使用三元表达式 -->
<div class="base" v-bind:class="['red','size',{blue:isActive}]">选项选项</div>
4.3 动态style
数组方式和对象方式是绑定内联样式的两种方法
对象语法
<div :style="{background:'red','font-weight':700,'font-size':20+'px'}"></div>
为了便于阅读,我们可以把样式写在data中
<script>
export default {
name: "app",
// 数据
data() {
return {
baseStyle:{
background:'red',
'font-weight':700,
'font-size':'20px'
}
};
}
};
</script>
数组语法
对象语法是引入一个样式,数组语法才是引入多个样式
<div :style="[baseStyle,hoverStyle]">多姿多彩</div>
还可以使用三元表达式:<div :style="[baseStyle,isActive ? hoverStyle:'']">多姿多彩</div>
<script>
export default {
name: "app",
// 数据
data() {
return {
baseStyle:{
width:'300px',
height:'100px',
border:'1px solid black'
},
hoverStyle:{
background:'red',
'font-weight':700,
'font-size':'20px'
}
};
}
};
</script>
第五章 Vue组件
5.1 自定义组件
组件的组织:通常一个应用会以一棵嵌套的组件树的形式来组织,组件必须先注册才能被识别。
组件的注册:全局注册:用vue.component来创建组件,注册之后可以在任何新建的vue根实例中使用
局部注册:在单个的vue格式的文件中创建组件,在需要的地方进行注册
一般我们还是选择局部注册
组件的创建:每个vue格式的文件都可以作为一个组件
组件的局部注册: 1 需要一vue格式的文件,一般工程新建之后都会有一个helloword.vue的组件 我们来注册它
2 将其作为组件,在app.vue中使用,组件可以重复使用
第一步 引入组件
import HelloWorld from "./components/HelloWorld.vue";
第二步 注册组件
components: {
HelloWorld
}
第三步 使用组件
<HelloWorld></HelloWorld>
组件中的数据:自定义组件中的数据data必须是一个函数
data: function () {
return {
count: 0
}
}
###5.2 组件单向数据流
如何从父组件中把内容传递给子组件?
prop的使用方法
基础使用:当父组件给子组件的prop传递一个值时,这个值就变成了子组件实例的一个属性
例子:我们给helloword子组件传递一个标题
1 在父组件中传递一个title给子组件
<template>
<div id="app">
<!-- 注意!title1 和 title2 是父组件的 data 中定义的数据,title 则是子组件中接收数据时的变量名 -->
<HelloVue :title="title1"></HelloVue> // title前面必须加冒号,否则子组件中受到的title值就是title1 title2
<HelloVue :title="title2"></HelloVue>
</div>
</template>
2 在子组件中,用prop接受title
<template>
<div class="hello">
<!-- 第二步:在页面上显示 title 的值,写法和显示 data 里定义的数据一样 -->
<h1>{{ title }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloVue',
// 第一步:在 prop 属性中接收 title
props: ['title']
};
</script>
进阶使用:附带类型声明
可以给值声明类型,首字母要大写例如
<script>
export default {
name: 'HelloVue',
// 在 prop 属性中接收 title,其类型为 String
props: {
title: String
}
};
</script>
当传入的值有多个时,可以用逗号隔开,还可以给值设置一些要求,例如
props: {
title: String,
// 多类型
likes: [String, Number],
// 带有默认值
isPublished: {
type: Boolean,
default: true
},
// 必填
commentIds: {
type: Array,
required: true
},
author: Object,
callback: Function,
contactsPromise: Promise
}
什么是单向数据流?
单向数据流指的是父子prop之间形成了一个单向下行绑定:即父级prop的更新会向下流动到子组件中,但是反过来不可以。
这样可以防止子组件意外改变父组件的状态,使数据流难以理解
当传入到子组件的数据需要处理这时候怎么办?如排序 格式化
1 prop传入的数据需要处理时
可以用计算属性,例如
props: ['initialTitle'],
computed: {
normalizedTitle: function () {
// 对传入的 initialTitle 进行去空格、字母小写化处理
return this.initialTitle.trim().toLowerCase()
}
}
2 prop传入的数据作为本地数据使用
可以定义一个data属性并将prop用作属性的初始值
props: ['initialTitle'],
data: function () {
return {
// 要读取 prop 里的 initialTitle,要在前面加 “this.”
// 将传入的 initialTitle 作为本地属性 title 的初始值
title: this.initialTitle
}
}
接下来就可以改变title而不必改变父组件的initialTitle
5.3自定义组件绑定原生事件
事件修饰符.native:可以理解为该修饰符的作用就是把一个vue组件转化为一个普通的HTML标签,并且该修饰符对普通HTML标签是没有任何作用的。
当你想在某个组件的根元素上监听一个原生事件,你就需要在父组件的监听事件上添加.native修饰符,否则事件所对应的方法将不会执行。
<Article
v-for="article in articleList"
:key="article.title"
:article="article"
@click.native="print(article)"
></Article>
按键修饰符
回车键监听
<button @keyup.enter="print(article)">按回车键执行 print</button>
<button @keyup.13="print(article)">按回车键执行 print</button> // 也可以根据回车键的ASCLL码来书写
5.4 自定义事件
1 给子组件article.vue绑定自定义事件
<!-- 自定义事件 upVote,调用该事件时会执行 handleLikes 方法 -->
<Article
v-for="article in articleList"
:key="article.title"
:article="article"
v-on:upVote="handleLikes(article)" //绑定的自定义事件
></Article>
2 在article.vue中调用自定义事件
<!-- 在 template 中直接调用自定义事件 upVote -->
<button @click="$emit('upVote')">点赞</button>
如果在点赞的时候还有其他方法要执行可以这样写
<button @click="childEvent">点赞</button>
在上文中自定义事件是绑定在子组件中的所以当子组件触发绑定的事件时也会触发自定义事件
自定义事件的参数
我们可以通过自定义事件的参数将数据从子组件传递给父组件
在父组件中
<!-- 自定义事件 upVote,调用该事件时会执行 handleLikes 方法 -->
<!-- 注意:和上面代码有个区别,我们没有在这里给 handleLikes 传参数,因为我们接下来会在子组件里给 handleLikes 传参数 -->
<Article
v-for="article in articleList"
:key="article.title"
:article="article"
v-on:upVote="handleLikes" //没有传入参数
></Article>
// 在 `methods` 对象中定义方法
methods: {
handleLikes(article) {
article.likes++
}
}
在子组件中调用自定义方法
<button @click="childEvent">点赞</button>
methods: {
childEvent: function() {
// 调用自定义事件 upVote,这里的第二个参数最后会传到父组件中的 handleLikes 方法里
this.$emit('upVote', this.article); //第一个参数是自定义事件的名称 后面的参数会成为自定义事件的方法的参数
// do other things
}
}
自定义事件中的双向绑定用到修饰符: .sync
在父组件中 使用修饰符.sync实现双向绑定
<MyCount class="count" :count.sync="count"></MyCount>
// 在 `methods` 对象中定义方法
data: function() {
return {
count: 0
}
}
在子组件中用update:count 的模式触发事件,把 count+1 赋值给 count
<div class="my-count">
<button @click="$emit('update:count', count+1)">加一</button>
{{ count }}
</div>
虽然count是定义在父组件中,但因为是双向绑定,如果改变子组件中count的值,父组件的count值也会改
###组件函数调用
使用ref属性访问子组件实例,调用子组件中的方法
1 给要访问的子组件添加ref属性
<template>
<Modal ref="modal"></Modal> //之后就可以通过 this.$refs.modal 来访问自定义组件Model.vue
</template>
2调用子组件中的方法
调用子组件中的show方法来改变visible值
<script>
export default {
methods: {
showModal() {
// 调用子组件中的 show 方法
this.$refs.modal.show();
}
}
};
</script>
ref访问子元素
<template>
<div id="app">
<input ref="input" type="text" /> // 访问子元素
<button @click="focusInput">点击使输入框获取焦点</button>
</div>
</template>
<script>
export default {
name: 'app',
methods: {
focusInput() {
// this.$refs.input 访问输入框元素,并调用 focus() 方法使其获取焦点
this.$refs.input.focus();
}
}
}
</script>
###5.6组件slot入门
slot:插槽 可以理解为在子组件的DOM中空出一个位置,父组件如果有需要就可以往插槽中添加内容
插槽的基础使用
1 在子组件中使用slot标签预留位置,标签的内容是后备内容 也可以为空
<div class="modal-content">
<slot>这是个弹框</slot>
<div class="footer">
<button @click="close">close</button>
<button @click="confirm">confirm</button>
</div>
</div>
后备内容就是当父组件不往插槽里面添加内容时,插槽显示的内容
2 在父组件中使用子组件
不向插槽中添加内容
<Modal :visible.sync="visible"></Modal>
加入个性化内容
<Modal :visible.sync="visible">个性化内容</Modal> //会替换掉原来插槽中的内容
5.7组件slot进阶
具名插槽:带有name属性的插槽 匿名插槽相反
当我们需要多个插槽时,可以使用name属性
在子组件中
<div class="modal" v-if="visible">
<div class="modal-content">
<header>
<slot name="header"></slot> // 用name属性来定义多个插槽
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</div>
在父组件中
<Modal :visible.sync="visible">
<template v-slot:header> //使用 v-slot指令 使用对应名字的具名插槽 并且更换内容
<h1>Modal title</h1>
</template>
<div>main content</div>
<div>main content</div>
<template v-slot:footer>
<p>Modal footer</p>
</template>
</Modal>
第六章 Vue-router
6.1安装 配置router
vue-router是vue.js官方的路由管理器,它和vue.js的核心高度集成,使我们更容易构建单页面应用
什么是单页面应用?
SPA(single page application)单一页面应用程序。它只有一个完整的页面,它在加载页面时,不会加载整个页面,只会更新指定容器里面的内容,它的核心
是更新视图而不重新请求页面
路由:spa路径管理器
vue的单页面应用将路径和组件映射起来,路由用于设定访问路径,通过路劲的切换来实现组件的切换
安装配置vue-router
安装路由
npm install vue-router
// 或者
yarn add vue-router
配置路由
在main.js中配置路由
// 0. 导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
import Foo from "./views/Foo.vue";
import Bar from "./views/Bar.vue";
// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router
}).$mount('#app')
路由是路劲和组件之间的映射
路由的使用
在app.vue中,使用<router-link>组件来导航
<template>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link> // to属性指定路径,在这里路径/foo映射组件Foo.vue
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<!-- App.vue 中的 <router-view></router-view> 是路由的最高级出口 -->
<router-view></router-view> //相当于一个插槽,渲染路由匹配到的组件
</div>
</template>
当router配置过多时,我们可以把它抽离出来,可以放在src/router/index.js中
6.2 添加路由
我们知道路径所对应的组件将在router-view标签处渲染,让我们来添加一些路由
// 1. 将要用到的组件从其他文件 import 进来
import Foo from './views/Foo.vue';
import Bar from './views/Bar.vue';
import User from './views/User.vue'; // 引入组件
// 2. 定义路由,每个路由应该映射一个组件
// 添加路径即在 routes 数组中增加新的成员
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar },
// 新增一项
{ path: '/user', component: User } //定义路由
];
// 3. 创建 Router 实例,然后传 `routes` 配置
const router = new VueRouter({
routes //创建实例
});
导入组件的写法 两种
第一种 只导入 没有定义路由
import Foo from './views/Foo.vue';
import Bar from './views/Bar.vue';
import User from './views/User.vue';
第二种导入路由并且定义路由
const routes = [
{ path: '/foo', component: () => import('./views/Foo.vue') },
{ path: '/bar', component: () => import('./views/Bar.vue') },
{ path: '/user', component: () => import('./views/User.vue') }
];
命名路由
我们可以在定义路由的时候,引入组件并命名路由的名字
// 0. 导入 Album、List、Add、Empty 三个组件
// 1. 定义路由
const routes = [
{ path: '/foo',
name: 'fooName',
component: () => import('./views/Foo.vue')
}
];
通过命名跳转
<!-- to 的值是一个对象而不是字符串,所以要在 to 前加 : -->
<router-link :to="{name: 'fooName'}">Go to Foo</router-link>
通过路径跳转
<router-link to="/foo">Go to Foo</router-link>
6.3 路由布局管理
在实际开发过程中,工程会十分复杂,应用界面通常由多层嵌套的组件组合而成。相应的,路由也会按某种结构对应嵌套的组件。
1在组件中配合路由使用router-view
在app.vue组件中
<template>
<div id="app">
<h1>Hello App!</h1>
<p>
<router-link to="/album/list">Go to Album List</router-link>
|
<router-link to="/album/add">Go to Album Add</router-link>
</p>
<!-- 路由匹配到的组件将渲染在这里 -->
<!-- 在本例中为 Album.vue 组件 -->
<router-view></router-view>
</div>
</template>
在album.vue组件中
<template>
<div id="album">
<div class="banner">banner</div>
<!-- 路由匹配到的组件将渲染在这里 -->
<!-- 在本例中为 List.vue 组件或 Add.vue 组件 -->
<router-view></router-view>
</div>
</template>
2定义嵌套路由 :路由的嵌套关系和组件的嵌套关系一致
// 0. 导入 Album、List、Add 三个组件
const routes = [
{
path: '/album',
component: Album, //这是app.vue中<router-view>对应routes里的第一层路由
// children 属性可以用来配置下一级路由(子路由)
children: [
{ path: 'list', component: List },
{ path: 'add', component: Add } //这是album.vue中<router-view>对应routes里的第二层路由
]
}
];
根路径/
如果希望用路径“/album/list”对应在album.vue中渲染相册列表,子路由中的path写法:
path: 'list'
path: '/album/list'
空的子路由
// 0. 导入 Album、List、Add、Empty 三个组件
const routes = [
{
path: '/album',
component: Album,
// children 属性可以用来配置下一级路由(子路由)
children: [
// 空的子路由
{ path: '', component: Empty },
{ path: 'list', component: List },
{ path: 'add', component: Add }
]
}
];
###6.4 动态路由
项目中也会出现多个路径对应一个组件的情况
动态路由的写法
import User from "./views/User.vue";
const routes = [
// id 就是路径参数
{ path: '/user/:id', component: User } //当url匹配到路由中的一个路径时,参数值会被
设置到this.$route.params中,可以在组件中被读取
]
捕获404页面
用404 notfound组件将页面渲染,使用通配符*来匹配任意路径
import NotFound from "./views/NotFound.vue";
const routes = [
{
// 会匹配所有路径
path: '*', //通配符的路由要放在最后面
component: NotFound
}
]
如何读取到匹配到的路径值呢?
this.$route.params.pathMatch // '/non-existing/file' 可以在$route.params的参数pathMatch中找到通配符匹配url的一部分
6.5 页面跳转
编程式导航:我们除了可以使用router-link创建a标签来实现导航,还可以借助router的实例方法,编码来实现导航
用router.push进行页面跳转及参数传递
1 router.push的参数为字符串
router.push('user')
router.push('/user') //匹配根路径,'/user' 这样的写法不管原路径 localhost:8080/??
中的 ?? 是什么,跳转后 url 都会变为 localhost:8080/user
2 router.push的参数为描述地址的对象
// 对象
// 这种写法和字符串类型的参数一样
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }}) // 对应的命名路由{ path:'user/:userId', name:'user' }
跳转后url为 localhost:8080/user/123
取参数 $route.params.userId
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }}) // 对应的路由 { path:'register' }
跳转后url为 localhost:8080/register?plan=private
取参数 $route.query.plan
name对应params,路径形式:user/123
path对应query,路径形式:user?id=123
如果使用path页面进行跳转,写成params进行传参会被忽略
// params 会被忽
router.push({ path: 'user', params: { userId: '123' }})
router.push({ path: 'user/123'}) //可以写成这样 router-link中的to属性也是这样的规则
页面跳转后如何获取参数?
可以使用route.query route.params route.hash获取
###6.6 重定向和别名
别名:两个名字指向同一个路由
const routes: [
// 定义 alias 属性
{ path: '/a', alias: '/b', component: A } // 访问/a和/b时都渲染组件A
];
例子 :通过别名将/layout/home变成/home
const routes: [
{
path: '/layout',
// 别名定为 /
alias: '/',
component: ()=> import('@/pages/nest/Layout.vue'),
children: [
{ path: 'home', component: Home },
{ path: 'courseall', component: CourseAll },
{ path: 'coursedetail/:courseId', component: CourseDetail }
]
}
];
路由重定向
如果将路由/a重定向到/b,即访问/a时 url自动跳转到/b然后匹配到路由/b.
const routes: [
// 定义 redirect 属性,将 /a 重定向到 /b
{ path: '/a', redirect: '/b' }
]
例子:将/home重定向到/
const routes: [
{
path: '/layout',
alias: '/',
// 重定向到 /home
redirect: '/home',
component: ()=> import('@/pages/nest/Layout.vue'),
children: [
{ path: 'home', component: Home },
{ path: 'courseall', component: CourseAll },
{ path: 'coursedetail/:courseId', component: CourseDetail }
]
}
6.7监听路由
监听路由route的变化:watch 和route
监听路由的写法
watch: {
$route(to,from){ // from是变化前的路由 to是变化后的路由
console.log(to, from);
}
} //第一种方法比较常用
// 或者
watch: {
$route: {
handler: function(to,from) {
console.log(to,from);
},
// 深度观察监听
deep: true
}
},
监听路由的使用
1 基础样式
2 定义tab点击事件
<script>
export default {
methods: {
// 点击 tab 时会执行 changeTab 方法
changeTab(type) {
// 使用 Router 实例方法改变路径参数
this.$router.push({ query: { type: type } });
}
}
};
</script>
3 监听路由变化,更新tab样式
<script>
export default {
watch: {
$route(to, from) {
// 路由变化了就执行更新样式的方法
this.updateTab();
console.log(to, from);
}
},
methods: {
changeTab(type) {
this.$router.push({ query: { type: type } });
},
// 更新样式的方法
updateTab() {
this.tabList.map(menu => {
menu.active = menu.type === this.$route.query.type;
});
}
}
};
</script>
4 处理特殊情况
如果路径最开始没有路径参数type我们默认选择第一个tab,如果有参数我们需要更新tab
<script>
export default {
mounted() {
if (this.$route.query.type) {
this.updateTab();
}
}
};
</script>
6.8网络请求async和await
js中的fetch请求数据
fetch(
'https://www.fastmock.site/mock/b73a1b9229212a9a3749e046b1e70285/f4/f4-11-1-1'
)
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson);
});
缺点:fetch返回一个promise对象,然后使用then对数据进行response.json()处理 ,但是要求上一个处理完成后才能继续向下处理
解决方法:async 和 await
异步async:返回的也是promise对象,但是关键字await不同
async function asyncFn() {
return {
"company": "优课达",
"slogan": "学的比别人好一点"
};
}
const result = asyncFn();
console.log(result);
等待异步:await
async function getAsyncFn() { //只能出现在async函数中
const result = await asyncFn();
console.log(result);
}
getAsyncFn();
多个请求并发执行Promise.all
async function asyncFn1() {
return "优课达";
}
async function asyncFn2() {
return "学的比别人好一点";
}
async function getAsyncFn() {
const result = await Promise.all([asyncFn1(), asyncFn2()]);
console.log(result);
}
getAsyncFn();
在vue中运用async和await请求数据
<script>
export default {
data: function() {
return {
courseList: []
};
},
async mounted() {
// 在生命周期 mounted 中调用获取课程信息的方法
await this.queryAllCourse();
},
methods: {
// 在 methods 对象中定义一个 async 异步函数
async queryAllCourse() {
// 在 fetch 中传入接口地址
const res = await fetch('https://www.fastmock.site/mock/2c5613db3f13a5c02f552c9bb7e6620b/f5/api/queryallcourse');
// 将文本体解析为 JSON 格式的promise对象
const myJson = await res.json();
// 获取返回数据中的 data 赋值给 courseList
this.courseList = myJson.data;
}
}
}
</script>
给api传递参数
<script>
export default {
data: function() {
return {
course: []
};
},
async mounted() {
await this.getCourse();
},
methods: {
async getCourse() {
// 从路径中获取课程 id
const courseId = this.$route.params.courseId
// 在接口地址后传入参数 id
const res = await fetch('https://www.fastmock.site/mock/2c5613db3f13a5c02f552c9bb7e6620b/f5/api/getcourse?id=${' + courseId + '}');
const myJson = await res.json();
this.course = myJson.data;
}
}
}
</script>