Vue2.0
文章目录
vue的官方概念:是一套用于构建用户界面的前端框架
构建用户界面:往html页面中填充数据
框架:一套现成的解决方案、具有规范化
主要学习Vue指令、组件、路由、vuex
Vue框架的特性
数据驱动视图
在使用了vue的页面中,vue会监听数据的变化,从而自动重新渲染页面的结构。
- 页面数据发生变化时,页面会自动渲染
- 数据驱动视图时单向的数据绑定
双向数据绑定
-
在网页中form表单负责采集数据,Ajax负责提交数据
-
js数据的变化,会被自动渲染到页面上
-
开发者不再需要手动操作dom,来获取表单元素最新的值
- 数据驱动视图和双向数据绑定的底层原理是MVVM
MVVM
MVVM是vue实现数据驱动视图和双向数据绑定的核心原理。MVVM指的是 Model、View、和ViewModel,他把HTML页面拆分成三部分:
在MVVM概念中:
- Model表示当前页面渲染时所依赖的数据源。
- View表示当前页面所渲染的DOM结构
- ViewModel表示Vue的实例,他是MVVM的核心
MVVM的工作原理
- ViewModel作为MVVM的核心,是他把当前数据页面的数据源(Model)和页面结构(View)连接在了一起
- 当数据源发生变化时,会被ViewModel监听到,VM会根据最新的数据源自动更新页面的结构
- 当DOM数据发生变化时,会被ViewModel监听到,VM会把变化过后最新的值自动同步到Model数据源
vue的基本使用
基本使用步骤
- 导入vue.js的script脚本文件
- 在页面中声明一个将要被vue所控制的DOM区域
- 创建VM实例对象(vue实例对象)
<body>
<!-- 希望vue能够控制这个div,帮助将数据填充到div内部。也就是视图区域 -->
<div id="app">{{username}}</div>
<!-- 导入vue的库文件 -->
<script src="./vue2.6.12.js"></script>
<!-- 创建vue的实例对象 -->
<script>
const vm = new Vue({
// el属性是固定写法,表示vm实例要控制页面上的哪个区域,接受的值是一个选择器。就是指定了当前vm实例要控制页面的哪个区域
el:'#app',
// data对象就是要渲染到页面上的数据,也就是Model数据源
data:{
username:'yxj',
}
})
</script>
vue的指令与修饰符
指令的概念
指令是vue为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构
vue中的指令按照不同的用途可以分为六大类
- 内容渲染指令
- 属性绑定指令
- 事件绑定属性
- 双向绑定指令
- 条件渲染指令
- 列表渲染指令
内容渲染指令
内容渲染指令用来辅助开发者渲染DOM元素的文本内容。常用的内容渲染指令有3个
v-text
会覆盖标签内原有的内容
<div id="app">
<p v-text="username"></p>
<p v-text="grnder">性别</p>
</div>
<script src="./vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
username:'yxj',
grnder:'男'
}
})
</script>
{{}}
插值表达式,用于解决v-text会覆盖文本内容的问题,英文名称为Mustache
<div id="app">
<p>{{username}}</p>
<p>{{grnder}}</p>
</div>
<script src="./vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
username:'yxj',
grnder:'男'
}
})
</script>
除了能渲染绑定的数据,在{{}}里还能对js的表达式进行运算
{{grnder + 1}}
{{message.split('').reverse().join(',')}}
v-html
v-text和插值表达式只能渲染纯文本内容。如果要把包含HTML标签的字符串渲染为页面的HTML元素,则需要用到v-html指令
<div id="app">
<p v-html="info"></p>
</div>
<script src="./vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
username:'yxj',
grnder:'男',
info:` <h4 style="color: red;">vue内容渲染指令</h4>`,
}
})
属性绑定指令
注意:插值表达式不能用到属性节点,只能用到元素的节点
v-bind
为元素的属性动态绑定值,可以简写:属性名
如果绑定内容需要动态拼接,则字符串的外面应该包裹单引号
<div id="app">
<input type="text" v-bind:placeholder="tips">
<img :src="photo" alt="">
</div>
<script src="./vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
tips:'请输入用户名',
photo:'https://publish-pic-cpu.baidu.com/19cb1b17-3857-4f66-93cd-68e25564ba62.png@w_228,h_152'
}
})
</script>
事件绑定指令
v-on
为DOM元素绑定事件监听。
语法v-on:事件名字=“函数名”
<div id="app">
<p>{{count}}</p>
<!-- (参数) -->
<button v-on:click="add(2)">+</button>
<!-- 简写@ -->
<button @click="sub">-</button>
</div>
<script src="./vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
count:0,
},
// 定义事件的处理函数
methods: {
//传递进参数就无法获取事件对象了?
add(n){
vm.count+=n;
console.log(vm === this);//true
},
sub(){
this.count--;
}
}
})
</script>
事件对象的问题
如果事件绑定的函数有参数了,怎么获取事件对象呢?
$event
-
是vue的内置变量,可以得到原生DOM的事件对象
-
如果绑定事件函数时传递了参数,而且还需要用到事件对象时就需要用到$event
<div id="app"> <p>{{count}}</p> <button v-on:click="add(n,$event)">+</button> <button @click="sub">-</button> </div> <script src="./vue2.6.12.js"></script> <script> const vm = new Vue({ el:'#app', data:{ count:0, n:1 }, // 定义事件的处理函数 methods: { add(n,$event){ vm.count+=n; if(this.count%2 === 0){ $event.target.style.backgroundColor='pink'; }else{ $event.target.style.backgroundColor='yellow'; } }, sub(){ this.count--; } } }) </script>
事件修饰符
在事件处理函数中调用event.preventDefault()或event.stopPropagation()是非常常见的需求。因此,vue提供了修饰事件符的概念,更方便对事件的触发进行控制
修饰事件符 | 描述 |
---|---|
.prevent | 阻止默认行为,(例如阻止a连接的跳转、阻止表单的提交) |
.stop | 阻止事件冒泡 |
.capture | 以捕获模式触发当前的事件处理函数 |
.once | 绑定的事件只触发1次 |
.self | 只有在event.target是当前元素自身时触发事件处理函数 |
<div id="app">
<a href="https://www.baidu.com" @click="show">百度一下</a> <br><br>
<a href="https://www.baidu.com" @click.prevent="show2">百度二下</a>
</div>
<script src="./vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
count:0,
},
methods: {
show(e){
// 阻止浏览器跳转行为
e.preventDefault();
console.log("你点了百度一下,被事件对象阻止了");
},
show2(){
console.log("你点了百度二下,被vue的事件修饰符阻止了");
}
}
})
</script>
按键修饰符
在监听键盘事件时,我们经常需要判断详细的按键。此时可以为键盘相关的事件添加案件修饰符
<div id="app">
<!-- 在只有key是enter时调用 submit方法 -->
<input type="text" @keyup.enter="submit">
<!-- 在只有key是ESC时调用 clear方法 -->
<input type="text" @keyup.esc="clear">
</div>
<script src="./vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
},
methods: {
submit(){
console.log("Enter键被按下了");
},
clear(e){
e.target.value="";
console.log("内容被清空了");
}
}
})
</script>
双向绑定指令
v-model
-
vue提供了v-model双向数据绑定指令,可以在不操作DOM的前提下,快速获取表单的数据。
-
v-model在底层自动判断绑定的元素,根据元素的不同绑定的属性也不一样,比如input自动绑定value属性
<div id="app"> <!-- 双向数据绑定 --> <input type="text" v-model="username" @blur="fun1"> <!-- 数据驱动视图,单项传递 不会改变data里username的值 --> <input type="text" :value="username" @blur="fun2"> <hr> <select name="" id="" v-model="city"> <option value="1">北京</option> <option value="2">上海</option> <option value="3">广州</option> <option value="4">深圳</option> </select> </div> <script src="./vue2.6.12.js"></script> <script> const vm = new Vue({ el:'#app', data:{ username:'yxj', city:'1' }, methods: { fun1(){ console.log(this.username); }, fun2(){ console.log(this.username+'666'); } } }) </script>
v-model指令的修饰符
为了方便对用户输入的内容进行处理,vue为v-model指令提供了3个修饰符
修饰符 | 作用 | 实例 |
---|---|---|
.number | 自动将用户输入的值转为数值类型 | |
.trim | 自动过滤用户输入的首尾空白字符 | |
.lazy | 在“change”时而非input时更新 |
<div id="app">
<input v-model.number="n1" > +<input v-model.number="n2">= <span>{{n1 + n2}}</span>
<br>
<input v-model.trim="msg" > <button @click="fun">获取用户名</button>
<br>
<input v-model.lazy="username" @blur="fun2">
</div>
<script src="./vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
n1:'',
n2:'',
msg:'',
age:18,
username:''
},
methods: {
fun(){
console.log(`用户名是:"${this.msg}"`);
},
fun2(){
console.log(this.username);
}
}
})
</script>
条件渲染指令
v-if
- 原理是每次动态创建或者移除元素,实现元素的显示和隐藏
- 如果页面加载时元素默认不需要展示,而且后期也不需要频繁展示时使用
v-show
-
一开始就会创建元素,尽管条件为false,
-
原理是动态为元素添加或者删除display:none属性来实现对元素的隐藏与显示
-
如果需要频繁的切换元素显示与隐藏v-show效率更高一些
v-else
- v-else-if必须配合v-if使用
列表渲染指令
- 用于循环渲染一个列表结构。v-for指令需要使用item in items 形式的特殊语法
- items 是待循环的数组
- item 是被循环的每一项
- v-for还支持一个可选的第二个参数,即当前项的索引。语法为 (item,index)in items
v-for
<div id="app">
<table border="1" align="center" width="50%">
<thead>
<th>索引</th>
<th>ID</th>
<th>姓名</th>
</thead>
<tbody>
<tr v-for="(item,index) in list" :title="item.name">
<td>{{index}}</td>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
</tr>
</tbody>
</table>
</div>
<script src="./vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
list:[
{id:1,name:'yxj'},
{id:2,name:'lqy'},
{id:3,name:'lqd'},
]
}
})
</script>
官方建议
- 只要使用到了v-for指令,一定要绑定一个:key属性,尽量把:id的值作为key的值
- key的值只能是字符串或者数字,且不能重复
- 使用index值时没有意义,他不能保证值得唯一
列表渲染的案例
核心代码
<form @submit.prevent="add">
<div class="min">
<span>品牌名称</span>
<input type="text" class="form-control" v-model.trim="value" placeholder="输入名字 ">
<button type="submit" class="btn">添加</button>
</div>
</form>
<table border="1" align="center" width="80%">
<thead>
<th>#</th>
<th>品牌名称</th>
<th>状态</th>
<th>创建时间</th>
<th>操作</th>
</thead>
<tbody>
<tr v-for="item in list" :key="item.id" :title="item.name">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>
<div>
<input type="checkbox" v-model="item.status" :id="'ub' + item.id" @change="change(item.status,item.id)">
<label v-if="item.status" :for="'ub' + item.id">已启用</label>
<label v-else :for="'ub' + item.id">已禁用</label>
</div>
</td>
<td>{{item.time | fam}}</td>
<td>
<a href="javascript:; " @click="remove(item.id)">删除</a>
</td>
</tr>
</tbody>
</table>
export default {
data(){
return{
value:'',
nextId:9,
list:[
{id:1,name:'宝马',status:true, time:new Date()},
{id:2,name:'奥迪',status:true, time:new Date()},
{id:3,name:'红旗',status:true, time:new Date()},
{id:4,name:'奔驰',status:true, time:new Date()},
{id:5,name:'大众',status:true, time:new Date()},
{id:6,name:'丰田',status:true, time:new Date()},
{id:7,name:'本田',status:true, time:new Date()},
{id:8,name:'五菱',status:true, time:new Date()},
]
}
},
filters:{
fam(time){
return time.toString();
}
},
methods:{
add(){
// 如果判断到他为空则return
if(this.value == '') return alert("品牌名称不能为空");
else{
console.log(this.$dayJS+'666');
const obj = {
id:this.nextId,
name:this.value,
status:true,
time:new Date()
}
this.list.push(obj);
this.value="";
this.nextId++;
}
},
remove(id){
console.log(id);
this.list = this.list.filter((item)=>{
return item.id != id;
})
},
change(item,id){
console.log(item+id.toString());
}
}
}
过滤器(vue3已经淘汰)
私有过滤器
- 过滤器(Filters),常用于文本的格式化。过滤器可以用在插值表达式和v-bind属性绑定
- 过滤器应该被添加在JavaScript表达式的尾部,由“管道符”进行调用
- 本质上是一个函数,必须定义到filters节点下
- 过滤器必须有返回值
- 通过形参获取管道符前的值
<div id="app">
<p>{{msg | capi}}</p>
</div>
<script src="./vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
msg:"message"
},
// 过滤器函数必须被定义到filters节点之下,本质时是一个函数
filters: {
// 形参里的val始终是管道符前的值
capi(val){
// 过滤器中一定要有一个返回值,
return val.charAt(0).toUpperCase()+val.slice(1);
}
}
})
</script>
全局过滤器
- 如果希望在多个vue实例之间共享过滤器,则可以按照以下格式定义全局过滤器
- 全局过滤:独立于每个vm实例之外
- Vue.filter() 方法接收两个参数
- 第一个参数是全局过滤器的名字
- 第二个参数是全局过滤器的数里函数()函数里的参数为管道符之前的值
Vue.filter('capitalize',(str)=>{
return val.charAt(0).toUpperCase()+val.slice(1);
})
- 如果全局过滤器与私有过滤器冲突,则按照就近原则调用
过滤器的其他用法
<p>{{msg | capi |tow | three}}</p>
//可以连续调用过滤器
侦听器
- watch允许监视数据的变化
- 本质是一个函数,要监视个哪数据的变化,就把数据名作方法名即可
- 监听数据必须是 data 中声明过或者父组件传递过来的 props 中的数据
- handler:指向一个处理函数
- immediate 属性为 true,可以立即执行一次 handler 函数
- deep属性为true时可以深度侦听,可以侦听到对象属性值的变化
- 深度侦听时会增大开销,把对象里的属性层层遍历,侦听时只写需要侦听的对象下的某个具体属性值即可
当需要在数据变化时执行异步操作或开销较大的操作时,这个方式是最有用的,这是和 computed 最大的区别。
watch: {
'obj.a': {//使用对象.属性的方式进行侦听,可以优化性能
handler () {
console.log('obj.a changed')
},
immediate: true
// deep: true
}
}
watch:特点
- 不支持缓存( 因为watch监听的函数接收两个参数,第一个参数是最新的值,第二个参数是输入之前的值。而computed属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值 ),数据变化会直接触发响应的操作
- 支持异步操作
// 本质是一个函数,要监视那个数据的变化,就把数据名作方法名即可
watch: {
//第一个值为新值,第二个值为旧值
username(newdata,old){
console.log(newdata,old);
}
}
使用场景
- 需要在数据变化时执行异步操作、或者执行开销较大的操作。
- 使用watch允许执行异步操作,限制
方法格式的侦听器
- 需要在数据变化时执行异步操作、或者执行开销较大的操作。
- 使用watch时是允许执行异步操作的,执行异步操作时可限制执行操作的频率。
<body>
<div id="app">
<input type="text" v-model="username">
</div>
<script src="../vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
username:""
},
// 本质是一个函数,要监视那个数据的变化,就把数据名作方法名即可
watch: {
//第一个值为新值,第二个值为旧值
// 函数格式
username(newdata,old){
console.log(newdata,old);
}
}
})
</script>
对象格式的侦听器
- handler(){}为侦听器处理函数
- 可以通过immediate属性让侦听器自动触发
- 通过deep属性可以侦听到对象属性的变化
watch: {
// 对象格式的侦听器
username:{
// 侦听器处理函数
handler(newdata,old){
console.log(newdata,old);
},
// 控制侦听器是否自动触发一次
immediate:true,
deep: true,//深度侦听
}
}
-
直接通过 ‘对象.属性’ 的形式也可以侦听到
watch: { // 如果要侦听对象子属性的变化,则必须包裹一层单引号 'info.username'(newvalue){ console.log(newvalue); } }
计算属性
计算属性指的是通过一些列运算之后,最终得到一个属性值。
这个动态计算出来的属性值可以被插值表达式或者methods方法使用。
特点:
- 定义的时候定义为方法
- 使用时候当成属性使用
好处:
- 实现了代码的复用
- 只要计算属性中依赖的属性值变化了就会自动求值
<body>
<div id="app">
R:<input type="text" v-model="r"><br>
G:<input type="text" v-model="g"><br>
B:<input type="text" v-model="b"><br>
<br>
<div class="box" :style="{backgroundColor:`rgb(${r},${g},${b})`}">
{{rgb}}
</div>
<button @click="show">按钮</button>
</div>
<script src="../vue2.6.12.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
r:0,
g:0,
b:0
},
methods: {
show(){
console.log(this.rgb);
}
},
computed: {
//rgb作为一个计算属性,被定义成了方法格式
//最终在这个方法中,要返回一个生成好的rgb(x,x,x)的字符串
// 声明的是方法,使用的时候是属性
//可以自动求值,实现代码的复用
rgb(){
return `rgb(${this.r},${this.g},${this.b})`;
}
}
})
</script>
axios
是一个专注于网络请求的库
安装
cnpm install axios -D
- 引入并挂在到vue原型上
import axios from 'axios';
Vue.prototype.$axios = axios
1.发起GET请求
this.$axios({
// 请求方式
method: 'get',
//请求地址
url: 'https://api.apiopen.top/getJoke',
// URL中的查询参数
params:{
sid: '29184338',
},
}).then((result)=>{
console.log(result);
});
2.发起post请求
async send(){
const {data:res} = await this.$axios({
// 请求方式
method: 'POST',
//请求地址
url: 'https://api.apiopen.top/getJoke',
//请求体参数
data: {
name: 'zs',
age: '20'
}
});
console.log(res.result);
},
直接发起get或post请求
<template>
<div>
<button @click="post">直接发送POST</button>
<button @click="get">直接发送GET</button>
</div>
</template>
<script>
export default {
data(){
return{}
},
methods:{
async get(){
const {data:res} = await this.$axios.get('https://api.apiopen.top/getJoke',{
params:{id:1}
});
console.log(res);
},
post(){
this.$axios.post('https://api.apiopen.top/getJoke',{name:'yxj',gender:'男'}).then((res)=>{
console.log(res.data.result);
});
}
}
}
</script>
vue-cli
vue-cli是 vue.js开发的标准工具。他简化了基于webpack创建工程化的vue项目的工程。
好处就是省去webpack配置的问题
安装和使用
安装vue-cli之前已经配置好node.js环境
vue-cli是npm上的一个全局包
npm install -g @vue/cli
然后通过以下命令创建项目
vue create project
根目录构成
node_modules
- 存储了所有的第三方包
src
- 项目的所有源代码
public/favicon.ico
- 网站的小图标
public/index.html
- 单页面文件,生成的源代码文件(new Vue的文件)会被自动注入这个页面里
package.json
- 包管理配置文件,存储一些第三方下载的依赖包
babel.config.js
- babel的配置文件。babel可以打包处理webpack无法处理的高级js语法
.gitignore
- 忽略文件,默认忽略掉一些文件
package-lock.json
- 锁定安装时的包的版本号,并且需要上传到git,以保证其他人在npm install时大家的依赖能保证一致。
src目录
main.js
- 项目的入口文件,整个项目的运行需要先执行main.js文件
app.vue
- 项目的根组件,定义一些ui结构
assets
- 静态资源的文件夹,css、img
components
- 存放封装组件的文件夹
vue项目的运行流程
在工程化的项目中,vue要做的事情很单纯,通过main.js把App.vue的ui结构渲染到index.html的指定区域中。
- 其中App.vue用来编写待渲染的模板结构
- index.html中需要预留一个el区域
- main.js会把App.vue渲染到index.html所预留的区域中
vue组件
- vue是一个支持组件化开发的前端框架
- 组件化就是把页面上可复用的UI结构封装为组件,方便项目的开发和维护
- 组建的后缀名为.vue
组件的三个组成部分
- template 模板结构
- script JavaScript行为
- style 组件样式
组件之间的父子关系
私有组件的使用
-
使用import语法导入需要的组件
import Left from './component/Left.vue'
-
使用components节点注册组件(这样是私有子组件)
export default{ components:{ 'Left':Left, } }
-
以标签的形式使用组件
<div> <Left></Left> </div>
全局注册组件
在vue项目的main.js入口文件中,通过Vue.component()方法,可以注册全局组件,
// 导入需要全局注册的组件
import Right from './components/Right.vue'
// 第一个参数为别名,第二个参数为注册的组件名
Vue.component('MyRirht',Right);
组件的props
props是组件的自定义属性,在封装通用组件的时候,合理地使用props可以极大提高组件的复用性
-
props中的数据可以直接在模板中使用
-
props中的数据是只读的,修改会报错
-
props默认值的设置
-
props的type值类型
// props是自定义属性,允许使用者通过自定义属性,为当前组件指定初始值 props:{ // 自定义属性 init:{ //如果没有设置初始值时会使用默认值 default:1, //指定值类型 type:Number, } },
-
props的required必填项
props:{ // 自定义属性 init:{ //如果没有设置初始值时会使用默认值 default:1, //指定值类型 type:Number, //在调用该组件时必须指定init值,与默认值无关 required:true, } },
示例
- 组件的封装者
<template>
<div class="wrap">
<div>
<span>count的值为{{count}}</span>
<br>
<button @click="count+=1">+1</button>
</div>
</div>
</template>
<script>
export default {
// props是自定义属性,允许使用者通过自定义属性,为当前组件指定初始值
props:['init'],
data(){
return{
count:this.init
}
}
}
</script>
- 组件的使用者
<template>
<div class="wrap">
<h1>left</h1>
<!-- 如果没有:则是字符串类型 -->
<Count :init="9"></Count>
<Count></Count>
</div>
</template>
<script>
import Count from './Acount.vue'
export default {
components:{
Count,
},
data(){
return{
count:0
}
}
}
</script>
组件之间样式冲突问题
默认情况下,写在.vue组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
导致组件之间样式冲突的根本原因:
- 单页面应用程序中,所有组件的DOM结构,都是基于唯一的index.html页面结构进行呈现的
- 每个组建的样式,都会影响整个index.html页面中的DOM元素
组件样式冲突的解决
- 给当前页面组件的所有标签添加一个专有属性
- 不同页面组件的标签属性值是不一样的
- 这样对不同的页面设置样式时就不会冲突了
scoped属性
- 可以解决样式冲突问题
<style lang="scss" scoped>
</style>
/deep/属性
- 当使用第三方组件库的时候,如果有修改第三方组件默认样式的需求,需要用到/deep/
/deep/.switch{
border: #ff0 2px solid;
color: rgb(175, 87, 33);
}
vue文件的解析流程
- package.json里的devDependencies
- 这个包可以将.vue结尾的模板文件编译成.js文件
"vue-template-compiler": "^2.6.11"
组件的生命周期
1.生命周期
- (Life Cycle)是指一个组件从创建->运行->销毁的整个阶段
- 强调的是一个时间段
- 在生命周期里会依次执行生命周期函数
2.生命周期函数
-
是由vue框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行
-
强调的是时间点
组件生命周期函数的分类
生命周期树形图
beforeCreate
- 组件的props、data、methods尚未被创建,都处于不可用状态
created
- 组件的props、data、methods已创建,都处于可用状态
- 常在这个生命周期函数里发起Ajax请求服务器的数据
- 但是组件模板尚未生成,不能操作DOM
beforeMount
- 即将渲染DOM结构,还没有渲染
- 此时还不能操作DOM
mounted
- 成功渲染DOM结构
- 此时已经可以操作DOM
beforeUpdate
- 当data数据发生改变时才会触发该函数
- 将要根据最新数据(已经是最新数据,但是还没渲染)重新渲染组件的模板结构
- 至少执行0次,最多执行n次
updated
- 已经根据最新的数据,完成了组件DOM结构的重新渲染
- 至少执行0次,最多执行n次
beforeDestroy
- 将要销毁组件,但是还未销毁
- 组件还可以正常工作
destroyed
- 已经销毁组件的数据侦听器,子组件,事件监听…
- 组件对应的DOM结构已经被完全移除
组件之间的数据共享
组件之间常见的关系:
- 父子关系
- 兄弟关系
父传子
父组件向子组件共享数据需要自定义props属性
如果传递的数据是引用数据类型则只是浅拷贝该数据
-
父组件(提供数据)
<Left :msg="message" :user="userinfo"></Left> data(){ return{ message:'我是父组件的msg', userinfo:{ name:'yxj', age:'18' } } }
-
子组件(接收数据)
<div class="son"> <h5>Son组件</h5> <p>父组件传递过来的 msg 值是:{{msg}}</p> <p>父组件传递过来的 user 值是:{{user}}</p> </div> props:['msg','user'],
子传父
子组件向父组件共享数据要使用自定义事件
通过$emit()触发自定义事件
第一个参数为自定义的触发的事件名
第二个参数为传递的数据
-
子组件
add(){ this.count+=1; //修改数据时,通过$emit()触发自定义事件 this.$emit('numchange',this.count) },
-
父组件
通过自定义的触发的事件名获取数据
<h1>App根组件{{countFromSon}}</h1> <MyRight @numchange="getNewCount"></MyRight> data(){ return{ // 定义countFromSon来接收子组件传递过来的数据 countFromSon:0 } }, methods:{ getNewCount(val){ this.countFromSon = val; } } }
兄弟之间数据传递
在vue2.x中兄弟组件之间数据共享的方案是EventBus
EventBus的使用步骤
-
创建eventBus.js模块,并向外共享一个Vue的实例对象
import Vue from "vue"; export default new Vue()
-
在数据发送方调用bus.$emit(‘事件名称’,要发送的数据)方法触发自定义事件
<template> <div class="fa"> <div class="wrap"> <h1>left 发送数据</h1> </div> <button @click="send">点击发送数据给我的Right兄弟</button> </div> </template> <script> import bus from'./eventBus' export default { data(){ return{ str:`left 发送的数据`, } }, methods:{ send(){ // 通过eventBus发送数据 bus.$emit('share',this.str); } } } </script>
-
在数据接收方调用bus.$on(‘事件名称’,要发送的数据)方法注册一个自定义事件
<template> <div class="fa"> <div class="wrap"> <h1>Right 接收数据</h1> <h1>{{msgFromLeft}}</h1> </div> </div> </template> <script> import bus from'./eventBus' export default { data(){ return{ msgFromLeft:'' } }, created(){ // 为bus绑定自定义事件 bus.$on('share',(val)=>{ this.msgFromLeft = val; }) } } </script>
总结
-
父组件向子组件传递数据时,父组件通过v-bind把数据绑定给子组件;子组件通过自定义props属性来接收数据。
-
子组件向父组件传递数据时,子组件调用this. e m i t ( ) 方法触发自定义事件, emit()方法触发自定义事件, emit()方法触发自定义事件,emit()第一个参数为自定义事件名称,第二个参数为要发给父组件的数据;父组件通过v-on:绑定自定义事件,在事件处理函数的形参接收子组件传递过来的数据。
-
兄弟组件之间数据共享需要定义一个共享的vue实例对象eventBus.js,并向外导出vue对象。发送方引入该实例对象并调用bus. e m i t ( ‘要触发的事件名’ , ′ 要发送的数 据 ′ ) 方法。接收方同样需要引入公共的实例对象,一般在 c r e a t e d 生命周期函数中调用 b u s . emit(‘要触发的事件名’,'要发送的数据')方法。接收方同样需要引入公共的实例对象,一般在created生命周期函数中调用bus. emit(‘要触发的事件名’,′要发送的数据′)方法。接收方同样需要引入公共的实例对象,一般在created生命周期函数中调用bus.on(‘发送方定义的事件名’,‘事件处理函数’),通过函数的形参接收数据。
ref引用
在不依赖于jQuery、不调用DOM api的情况下直接获取DOM元素或组件的引用。
在每一个vue的组件实例上,都包含了一个 r e f s 对象,里面存储着对应的 D O M 元素或组件的引用。默认情况下,组件的 refs对象,里面存储着对应的DOM元素或组件的引用。默认情况下,组件的 refs对象,里面存储着对应的DOM元素或组件的引用。默认情况下,组件的refs指向一个空对象。
- ref可以获得元素的引用
DOM示例
<template>
<div class="wrap">
<h1 ref="test">refs</h1>
<button @click="showThis">打印 this</button>
</div>
</template>
<script>
export default {
data(){
return{
}
},
methods:{
showThis(){
console.log(this);
this.$refs.test.style.backgroundColor = 'red'
}
}
}
</script>
- ref可以获得组件的引用
- 可以调用组件的方法
- 可以直接操作组件的数据
组件示例
<template>
<div class="wrap">
<h1 ref="test">refs</h1>
<button @click="showThis">打印 this</button>
<button @click="reset">重置Acount组件值</button>
<div class="acount">
<Acount ref="resetAc"></Acount>
</div>
</div>
</template>
<script>
import Acount from '../components/Acount.vue'
export default {
components:{
Acount
},
data(){
return{
}
},
methods:{
reset(){
// console.log(this.$refs);
this.$refs.resetAc.reset();
// this.$refs.resetAc.count = 0
},
showThis(){
// console.log(this);
this.$refs.test.style.backgroundColor = 'red'
}
}
}
</script>
ref练习
this.$nextTick(callback())
this.$nextTick()方法能够将回调函数里的语句延迟调用
- 等待DOM更新完成之后再执行回调函数
- 保证回调函数可以操作到最新的DOM元素
<template>
<div class="wrap">
<input ref="ipRef" type="text" v-if="flag" @blur="showButton">
<button v-else @click="showInput">显示输入框</button>
</div>
</template>
<script>
export default {
data(){
return{
flag:false,
}
},
methods:{
showInput(){
this.flag = true;
this.$nextTick(()=>{
this.$refs.ipRef.focus();
})
},
showButton(){
this.flag = false;
}
}
}
</script>
购物车的案例
动态组件
<component>
组件(这是vue内置的组件)
-
实现不同组件之间的按需展示(动态切换组件的显示和隐藏)
-
vue提供了内置的
<component>
组件,专门用来实现动态组件的渲染。 -
可以将
<component>
理解为一个占位符,一定要属性绑定上is属性 -
is属性值表示要展示的组件名字
<component :is="Left"></component>
简单使用
<button @click="comName = 'Left' ">展示Left</button>
<button @click="comName = 'Right' ">展示Right</button>
<div>
<component :is="comName"></component>
</div>
import Left from'./components/Left.vue';
import Right from './components/Right.vue';
export default {
name: 'App',
components: {
Left,Right
},
data(){
return{
comName:Left,
}
},
注意
- 动态组件再切换的时,隐藏的组件会被销毁(destroyed),显示时会重新渲染(created)
- 此时的数据也会被重新渲染
<keep-alive>
<keep-alive>
是vue内置的标签可以把内部的组件缓存,而不是销毁组件
<keep-alive include="Right,Left">
<component :is="comName"></component>
</keep-alive>
-
include属性是指定缓存的组件,默认全部缓存
-
exclude属性是指定不缓存的组件,不能与include属性一起使用
<keep-alive exclude="Right">
<component :is="comName"></component>
</keep-alive>
<keep-alive>
对应的生命周期函数
- 当组件被缓存时,会自动触发deactivated生命周期函数
deactivated(){
console.log(组件被缓存了);//离开的时候
}
- 当组件被激活时,会自动触发activated生命周期函数
activated(){
console.log(组件被激活了);//首次被创建
}
- 组件的注册名称主要应用场景是以标签的形式,把注册好的组件渲染到页面结构中
- 组件声明时的name名称主要是为了结合
<keep-alive>
标签实现组件缓存功能,以及在调试工具中看到组件的name名称
插槽
插槽(slot)是vue为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望用户指定的部分定义为插槽。
比如你要封装一个组件myCount,组件有个header头部,和内容区;内容区域是由用户自定义的,这个时候就需要再组件内使用slot标签进行占位,在使用时指定自定义内容。
<Left>
<p>left的p标签</p>
</Left>
//直接在组件内插入标签是不会被渲染的
简单使用
-
首先在封装组件的时候预留一个插槽区
//这里是Left组件 <div class="fa"> <!-- 生明一个插槽区 --> <slot name="default"></slot> </div>
-
在使用组件时自定义的标签就会渲染到预留的插槽位
<Left> <p>自定义left的p标签</p> //默认情况下,在使用组件的时候,提供的内容都会被填充到名字为default的插槽中 </Left>
-
vue官方规定,每一个slot插槽,都要有一个name属性,用于指定插槽的名称;不设置的话默认name=“default”
<div class="fa">
<slot name="default"></slot>
</div>
- v-slot指令只能在组件里或者template里使用
<Left>
<!-- template标签只起到包裹性的作用,不会被渲染成其他真实的元素 -->
<template v-slot:default>
<p>将p放置到default插槽中</p>
</template>
</Left>
如果要把内容填充到指定名称的插槽中,需要用到v-slot指令
v-slot必须用在template标签上
template是一个虚拟标签,只起到包裹作用,不会被渲染成html元素
v-slot指令的简写是#
<Left>
<!-- template标签只起到包裹性的作用,不会被渲染成其他真实的元素 -->
<template #default>
<p>将p放置到default插槽中</p>
</template>
</Left>
插槽的后备(默认)内容
防止组件使用者没有指定slot的内容,所以预先放值一个默认的标签;组件使用者指定了slot内容的话就会覆盖掉默认内容
<div class="fa">
<slot name="default">
这是default插槽的默认内容
</slot>
</div>
具名插槽
-
首先新建一个Article组件
<template> <div class="article-container"> <div class="header"> <slot name="title">1</slot> </div> <div class="contain"> <slot name="main"></slot> </div> <div class="footer"> <slot name="body"></slot> </div> </div> </template> <script> export default { name:'Article' }
-
在引用组件时只需要如此如此即可
<Article> <template #title> <h1>《山川》</h1> </template> <template #main> <h5>【作者】许月卿</h5> </template> <template #body> <h2>山川长不老,人意欲如何。</h2> <h2>霜后蒹葭健,秋来洲渚多。</h2> <h2>河汾周旧典,洙泗鲁余波。</h2> <h2>凄怆从陈蔡,令人涕滂沱。</h2> </template> </Article>
作用域插槽
在封装组件时,为预留的提供了一些属性和值,并且在组件引用时使用了原组件中预留的属性和值;这种用法就叫做作用域插槽。
-
在子组件中除了定义name属性外,再定义一个自定义属性用于保存数据
<template> <div class="article-container"> <div class="header"> <slot name="title" scoped="《春晓》"></slot> </div> <div class="contain"> <slot name="main" author="【孟浩然】"></slot> </div> <div class="footer"> <slot name="body" body="春眠不觉晓"></slot> </div> </div> </template>
-
使用组件时指定(定义)一个与插槽name属性对应的属性名obj,通过这个属性名接收来自子组件传递过来的默认值
<Article> //建议使用scoped接收 <template #title="obj"> <h1>{{obj.scoped}}</h1> </template> <template #main="obj"> <h5>{{obj.author}}</h5> </template> <template #body="obj"> <h2>{{obj.body}}</h2> <h2>处处闻啼鸟</h2> <h2>夜来风雨声</h2> <h2>滑落知多少</h2> </template> </Article>
配合解构赋值使用
-
子组件中
<template> <div class="article-container"> <div class="header"> <slot name="title" scoped="《春晓》" :user="users"></slot> </div> <div class="contain"> <slot name="main" author="【孟浩然】" :user="users"></slot> </div> <div class="footer"> <slot name="body" body="春眠不觉晓"></slot> </div> </div> </template> <script> export default { // name:'Article' data(){ return { users:{ name:'yxj', age:25 } } } } </script>
-
组件使用时
<Article> <template #title="{scoped}"> <h1>{{scoped}}</h1> </template> <template #main="{user}"> <h5>{{user.name}}</h5> </template> <template #body="obj"> <h2>{{obj.body}}</h2> <h2>处处闻啼鸟</h2> <h2>夜来风雨声</h2> <h2>滑落知多少</h2> </template> </Article>
自定义指令
自定义指令分为私有自定义指令(单组件使用)、全局自定义指令(全组件使用)。
私有自定义指令
在每个vue组件中,可以在directives节点下声明私有自定义指令
directives节点是一个对象,与data节点平齐,directives对象里的可以设置多个对象,对象名就是自定义的属性名。然后在bind和update方法里就可以编写自定义指令的功能;这两个方法都接受两个参数el、和binding。el表示示当前指令所绑定的DOM元素对象;binding表示一个对象里面存储了指令的名字、值、表达式等。
bind函数只有第一次绑定时触发,只会触发一次。
update函数在每次DOM更新时会触发该函数
示例1
改变颜色
-
在组件中与data节点平级下定义directives对象节点
// 私有自定义指令的节点 directives:{ //定义名为color的指令,指向一个配置对象 color:{//此时已经有了自定义指令 //当指令被第一次绑定到元素身上时,会立即触发bind函数(只有第一次绑定时触发) bind(el){//el表示当前指令所绑定的DOM元素对象 el.style.color = 'red' } } },
-
使用自定义指令v-color
<h1 v-color>app根组件</h1>
示例2
通过data改变颜色
<template>
<h1 v-color="color">app根组件</h1>
<p v-color="'red'">p标签</p>
</div>
</template>
<script>
data(){
return{
color:'blue'
}
},
directives:{
color:{
bind(el,binding){//binding表示一个对象里面存储了指令的名字、值、表达式等
el.style.color = binding.value
console.log(binding);
}
}
},
</script>
示例3
点击改变颜色
<template>
<h1 v-color="color">app根组件</h1>
<p v-color="'red'">p标签</p>
<button @click="color='green'">改变color</button>
</div>
</template>
<script>
data(){
return{
color:'blue'
}
},
directives:{
color:{
bind(el,binding){//只在第一次绑定时执行
el.style.color = binding.value
console.log(binding);
},
update(el,binding){//在DOM更新时会触发该函数
el.style.color = binding.value
console.log(binding);
}
}
},
</script>
函数的简写形式
<template>
<h1 v-color="color">app根组件</h1>
<p v-color="'red'">p标签</p>
<button @click="color='green'">改变color</button>
</div>
</template>
<script>
data(){
return{
color:'blue'
}
},
directives:{
//当binding和update函数里的逻辑代码完全一样时可以简写
color(el,binding){
el.style.color=binding.value;
},
},
</script>
全局自定义指令
全局声明的指令、过滤器等都需要在main.js文件里声明
//全局自定义指令
// Vue.directive('color',{
// binding(el,binding){
// el.style.color=binding.value
// },
// update(el,binding){
// el.style.color=binding.value
// },
// });
Vue.directive('color',function(el,binding){
el.style.color=binding.value
});
eslint
是一个自动检查代码风格的一个工具,
- 设置里的 Tab Size 值修改为2即可
- Editor:Format On Save
新建一个项目
vue create projectName #新建项目
#选择 Manually select features
#按空格选择
Babel
CSS Pre-processors
Linter/Formatter
#回车、选择vue2
#选择Less预处理器
然后就是选择安装ESlint语法规范了
选择Standard config#标准配置
Lint and fix on commit (requires Git)#表示提交代码时自动检查修复
Lint on save#表示保存时自动检查
这里选择Lint on save就可以
#然后选择In dedicated config files放到独立的配置文件中
#最后是否保存为预设;可Y可N
可以看到默认rules
rules: {
// 在代码中禁用console。
//process.env.NODE_ENV可以得到当前的打包模式,如果当前模式为production则警告,否则禁用该规则
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
// 在代码中禁用debugger
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
参考ESLint官网:https://eslint.bootcss.com/
常见的规则
不能有连续的空行
不能有空格
最后一行必须是空行
默认使用单引号
对象的属性:和值之间要有一个空格
对象属性、方法后面不允许有多余的逗号
注释// 后至少加一个空格
缩进多了少了也不行
import/first。import导入模块的语句必须声明在文件的顶部
定义一个变量/常量没有被使用也会报错
方法名 ()之间必须有一个空格
在vsCode里安装ESLint插件
打开csCode的设置
- 然后再配置文件settings.js里加入一个配置项
//ESLint插件的配置
"editor.codeActionsOnSave": {
"source.fixAll": true,
},
- 安装 prettier - Code formatter插件
// prettier配置
"eslint.alwaysShowStatus": true,
"prettier.trailingComma": "none",
"prettier.semi": false,
//每行文字个数超出此限制将会被迫换行
"prettier.printWidth": 300,
//使用单引号替换双引号
"prettier.singleQuote": true,
"prettier.arrowParens": "avoid",
//设置.vue文件中,HTML代码的格式化插件
"vetur.format.defaultFormatter.html": "js-beautify-html",
"vetur.ignoreProjectWarning": true,
"vetur.format.defaultFormatterOptions": {
"js-beautify-html": {
"wrap_attributes": false
},
"prettyhtml": {
"arrowParens":"avoid",
"printWidth": 300,
"singleQuote": true,
"wrapAttributes": false,
"sortAttributes": false,
"trailingComma":"none",
"semi":false
}
},
简直不要太烦,我直接不用了,哈哈哈。。。拜拜
axios
简单演示使用
首先新建两个组件,引入到app.vue里,两个组件展示大概是这样就行
关键代码
<template>
<div class="left-container">
<h3>Left 组件</h3>
<button @click="getInfo">发起GET请求</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
methods: {
async getInfo() {
const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/get')
console.log(res);
}
}
}
</script>
<style>
...
</style>
全局配置使用
-
下载
npm install axios -S
-
挂载
#在main.js里导入axios import axios from 'axios' #全局配置请求根路径 axios.defaults.baseURL = '请求根路径' //在vue的原型上挂载axios,每个vue组件都可以访问到。 Vue.prototype.$http = axios //缺点是无法实现api接口的复用
-
使用
methods: { async getInfo() { const { data: res } = await this.$http.get('api/get') console.log(res); } }
路由
路由就是一个对应关系(组件和地址栏(hash)地址的对应关系)
前端路由的工作方式
- 用户点击了页面上的路由链接
- 导致URL地址栏中的Hash值发生了变化
- 前端路由监听到了Hash地址的变化
- 前端路由把当前的Hash地址对应的组件渲染到浏览器中
模拟前端路由的实现
- 编写三个组件:首页、电影、更多,样式差不多就行
-
通过动态组件引入
<div class="box"> <component :is="components"></component> </div> import MainPage from '@/components/MainPage.vue' import MoviePage from '@/components/MoviePage.vue' import MorePage from '@/components/MorePage.vue' export default { components: { MainPage, MoviePage, MorePage }, data() { return { components: 'MainPage' } },
-
在created生命周期函数里监听url的变化动态切换组件
created() { window.onhashchange = () => { console.log(location.hash); switch (location.hash) { case '#/MorePage': this.components = 'MorePage' break case '#/MoviePage': this.components = 'MoviePage' break case '#/MainPage': this.components = 'MainPage' break default: this.components = 'MainPage' } } }
单页面应用的原理
SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;它是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
优点:
- 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
- 基于上面一点,SPA 相对对服务器压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点:
- 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
- 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
- SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
vue-router安装
安装
在vue2.x项目里安装路由的命令
npm i vue-router@3.5.2 -S
创建路由模块
在src目录下新建router/index.js文件
- 1 导入Vue 和 VueRouter 、 调用Vue.sue() 函数,把VueRouter安装为Vue的插件、 创建路由的实例对象、向外共享路由的实例对象
// 这是当前项目的路由模块
// 1 导入Vue 和 VueRouter 的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// 2 调用Vue.sue() 函数,把VueRouter安装为Vue的插件
Vue.use(VueRouter)
// 3 创建路由的实例对象
const router = new VueRouter()
// 4 向外共享路由的实例对象
export default router
在main.js里配置路由
// 导入路由模块,拿到路由实例对象
//在进行模块化导入(包括common.js和ES6)的时候,如果给定的是文件夹,则默认导入这个文件夹下,名字叫做index.js的文件
import router from '@/router'
new Vue({
render: (h) => h(App),
// 将router挂载到vue实例对象里
router:router
}).$mount('#app')
这里回顾一下实例化vue
render函数用来渲染视图,也提供给el
挂载,所以使用render函数就是为了页面显示出来。
1.render 方法是一个函数,在接受传入的参数 h 函数后,返回 h(App)
的函数调用结果。
2.在创建 vue 实例时,通过调用 render 方法来渲染实例页面的 DOM 结构。
3.当vue 在调用 render 方法时,会传入一个 createElement 函数作为参数,h 的实参是 createElement 函数,然后 createElement 会以 App
为参数进行调用。
render: h => h(App);
//等价于
render: function(createElement) { return createElement(App); }
这里回顾一下模块化语法:common.js和ES6
CommonJS 模块是 Node.js 专用的,与 ES6 模块不兼容。而ES6模块化在浏览器和node.js中都可以用。 语法上面,两者最明显的差异是,CommonJS 模块使用require()和module.exports,ES6 模块使用import和export。 在node.js使用模块化,需要将 CommonJS 脚本的后缀名都改成.cjs,ES6 模块采用.mjs后缀文件名。或者修改package.son里面的文件,type字段为module或commonjs。
运行时加载
就是对引入的模块进行整体的加载,比如说你要使用某个模块里对象的某个方法,就得加载模块里的所有代码,得到具体的对象,然后再从具体的对象上读取响应的方法。 因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。
// CommonJS模块
let { stat, exists, readfile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
编译时加载(静态加载)
相当于是一种按需加载,只加载具体需要的方法,其他方法不加载。
ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。
//ES6模块
import { stat, exists, readFile } from 'fs';
ES6 模块与 CommonJS 模块差异
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。 ( CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值 ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块)
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。( CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成)
- CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。
vue-router使用
<router-view></router-view>
组件
只要在项目中安装和配置了vue-router,就可以使用<router-view></router-view>
组件。他相当于一个占位符,当路由变化时,需要展示不同的组件,这些组件就是通过<router-view></router-view>
组件展示的。
首先在src目录下新建一个App2.vue。里面的内容如此如此:
<template>
<div class="app2">
<h1>App2组件</h1>
<a href="#/MainPage">首页</a>
<a href="#/MoviePage">电影</a>
<a href="#/MorePage">更多</a>
<hr>
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style>
.app2 a {
padding: 3px;
}
</style>
然后在main.js中:
import App from './App2.vue'
//将App.vue改成App2.vue
最后在router/index.js下配置路由:
// 这是当前项目的路由模块
// 1 导入Vue 和 VueRouter 的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入需要的组件
import MainPage from'@/components/MainPage.vue'
import MoviePage from'@/components/MoviePage.vue'
import MorePage from'@/components/MorePage.vue'
// 2 调用Vue.sue() 函数,把VueRouter安装为Vue的插件
Vue.use(VueRouter)
// 3 创建路由的实例对象
const router = new VueRouter({
//routes是一个数组,用于定义hash地址和组件之间的对应关系
routes:[
//路由规则
//path代表要展示的hash地址、component代表组件
{path:'/MainPage',component:MainPage},
{path:'/MoviePage',component:MoviePage},
{path:'/MorePage',component:MorePage}
]
})
// 4 向外共享路由的实例对象
export default router
router-link
当安装和配置了vue-router后,就可以使用router-link来代替普通的a链接了。
<router-link>
用于代替<a>
标签的,他的to属性相当于<a>
标签的href属性,也会被渲染成a标签。
<template>
<div class="app2">
<h1>App2组件</h1>
<router-link to="/MainPage">首页</router-link>
<router-link to="/MoviePage">电影</router-link>
<router-link to="/MorePage">更多</router-link>
<hr>
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style>
.app2 a {
padding: 3px;
}
</style>
redirect
redirect是路由重定向,比如用户访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面。通过路由规则的redirect属性,指定一个新的路由地址,可以很方便地设置路由地重定向。
const router = new VueRouter({
//routes是一个数组,用于定义hash地址和组件之间的对应关系
routes:[
//重定向的路由规则
{path:'/',redirect:'/MainPage'},
//路由规则
{path:'/MainPage',component:MainPage},
{path:'/MoviePage',component:MoviePage},
{path:'/MorePage',component:MorePage}
]
})
嵌套路由
通过路由实现的嵌套展示,叫做嵌套路由。如果某个组件本身就是通过路由的方式展示出来的,然后又在该组件里存放了路由规则。
-
首先在components目录下新建tables目录,然后新建tables/Tab1.vue和tables/Tab2.vue文件
-
然后在MorePage.vue页面里添加子集路由链接和子集路由占位符
<template>
<div class="left-container">
<h3>更多</h3>
<!-- 子级路由链接 -->
<router-link to="/MorePage/tab1">tab1</router-link>
<router-link to="/MorePage/tab2">tab2</router-link>
<hr>
<!-- 子集路由占位符 -->
<router-view></router-view>
</div>
</template>
- 接着通过children属性声明子路由规则:在router/index.js路由模块中导入需要的组件,并使用children属性声明子路由规则:
const router = new VueRouter({
//routes是一个数组,用于定义hash地址和组件之间的对应关系
routes:[
//重定向的路由规则
{path:'/',redirect:'/MainPage'},
//路由规则
{path:'/MainPage',component:MainPage},
{path:'/MoviePage',component:MoviePage},
{
path:'/MorePage',
component:MorePage,
children:[
// 子路由规则
{path:'tab1',component:Tab1},
{path:'tab2',component:Tab2}
]
}
]
})
- 效果就出来了
默认子路由
如果children数组中,某个路由规则的path值为空字符串,则这条路由规则叫做“默认子路由”。
//在MorePage页面中设置
<template>
<div class="left-container">
<h3>更多</h3>
<!-- 子级路由链接 -->
<router-link to="/MorePage">tab1</router-link>
<router-link to="/MorePage/tab2">tab2</router-link>
<hr>
<!-- 子集路由占位符 -->
<router-view></router-view>
</div>
</template>
//路由设置
const router = new VueRouter({
//routes是一个数组,用于定义hash地址和组件之间的对应关系
routes:[
//重定向的路由规则
{path:'/',redirect:'/MainPage'},
//路由规则
{path:'/MainPage',component:MainPage},
{path:'/MoviePage',component:MoviePage},
{
path:'/MorePage',
component:MorePage,
children:[
// 子路由规则
{path:'',component:Tab1},//默认展示Tab1
{path:'tab2',component:Tab2}
]
}
]
})
动态路由
思考:
//如果有3个路由链接
<router-link to="/MorePage/tab1">tab1</router-link>
<router-link to="/MorePage/tab2">tab2</router-link>
<router-link to="/MorePage/tab3">tab3</router-link>
//就需要定义三条路由规则
{path:'/MorePage/tab1',component:Tab1}
{path:'/MorePage/tab2',component:Tab2}
{path:'/MorePage/tab3',component:Tab3}
这样路由规则的复用性差
动态路由的概念
把hash地址中可变的部分定义为动态的参数项,从而提高路由规则的复用性。
在vue-router中使用英文的冒号(:)来定义路由的参数项:
//路由中的动态参数以:进行声明,冒号后面的是动态参数的名称
{path:'/MorePage/tab:id',component:Tab+'id'}//id命名随意
//将以下3个路由规则,合并成一个,提高了路由规则的复用性
{path:'/MorePage/tab1',component:Tab1}
{path:'/MorePage/tab2',component:Tab2}
{path:'/MorePage/tab3',component:Tab3}
动态路由的体验
-
首先在App.vue文件里如此如此:
<template> <div class="app2"> <h1>App2组件</h1> <a href="#/MainPage">首页</a> <a href="#/MoviePage/1">海王</a> <a href="#/MoviePage/2">龙珠</a> <a href="#/MoviePage/3">三国</a> <a href="#/MorePage">更多</a> <hr> <router-view></router-view> </div> </template>
-
在路由规则中配置:
routes:[ // {path:'/',redirect:'/MainPage'}, {path:'/MainPage',component:MainPage}, //在MoviePage中需要根据id的值来展示对应电影的详情信息 {path:'/MoviePage/:id',component:MoviePage}, { path:'/MorePage', component:MorePage, children:[ {path:'',component:Tab1}, {path:'tab2',component:Tab2} ] } ]
-
在MoviePage.vue页面中打印this
<template> <div class="movie"> <h3>电影</h3> <button @click="showThis">打印this</button> </div> </template> <script> export default { methods: { showThis() { console.log(this); } } }
在控制台看到:在$route对象里params对象里的id就是路由规则中配置的id属性,值就是App2.vue页面里的值
-
也就是说你想获取路由后面的参数值只需要通过this.$route.params.id就能获取到:
//MoviePage.vue <template> <div class="movie"> <h3>电影</h3> <h3>电影模块--{{this.$route.params.id}}</h3> <button @click="showThis">打印this</button> </div> </template>
-
$route是路由的参数对象
-
$router是路由的导航对象
路由规则开启props传参
-
想要在组件中用props来接收路由规则里的动态参数,需要在路由规则里添加一个props:true的属性
//可以为路由规则开启props传参,从而方便的拿到动态的参数值 {path:'/MoviePage/:id',component:MoviePage,props:true},
-
在需要接收参数的页面里使用prps进行接收。
<template> <div class="movie"> <h3>电影</h3> <h3>电影模块--{{id}}</h3> <button @click="showThis">打印this</button> </div> </template> <script> export default { props: ['id'], methods: { showThis() { console.log(this); } } } </script>
查询参数和全路径
注意
-
在hash地址中/后面的参数项,叫做“路径参数”
<a href="#/MoviePage/1">海王</a> <a href="#/MoviePage/2">龙珠</a> <a href="#/MoviePage/3">三国</a>
-
在路由参数对象中需要使用this.$route.params来访问路径参数
注意
-
在hash地址中?后面的参数为查询参数
<a href="#/MoviePage/2?name=zs age=18">龙珠</a>
-
在路由参数对象中需要使用this.$route.query来访问查询参数
注意
- path只包含路径
- fullPath是一个完整的hash地址(包含路径和查询参数)