文章目录
初识VUE(项目搭建)
VUE渐进式javacript框架, 一套拥有自己规则的语法,官网地址: https://cn.vuejs.org/,渐进式: 逐渐进步, 想用什么就用什么, 不必全都使用。库是方法的集合, 而框架是一套拥有自己规则的语法。
- Vue推荐采用.vue文件来开发项目
⚫ template里只能有一个根标签,什么标签都行,但是只能有一个
⚫ vue文件-独立模块-作用域互不影响
⚫ style配合scoped属性, 保证样式只针对当前template内标签生效
⚫ vue文件配合webpack, 把他们打包起来插入到index.html
@vue/cli 创建项目
@vue/cli是Vue官方提供的一个全局模块包(得到vue命令), 此包用于创建脚手架项目.脚手架里面已经帮我们配置好了webpack
- yarn global add @vue/cli / npm install @vue/cli –g 进行安装,全局包会在计算机中配置全局命令(例: vue命令)
- vue -V 可以查看安装的版本
- 创建项目 vue create 项目名 项目名不能有大写字母, 中文和特殊符号
- 进入到项目目录下 yarn serve 启动项目
- 生成的项目,各个文件代表的含义总结如下:
main.js 打包了APP.vue,然后又把相关的内容放到了index.html上让我们看到。
6. 关系总结如下:
@vue/cli 自定义配置
- 例如我们想要修改端口号这样的需求,需要在src并列处新建vue.config.js, 填入配置, 重启webpack开发服务器。项目中没有webpack.config.js文件,因为Vue脚手架项目用的vue.config.js。
- 关闭代码检查工具:eslint是一种代码检查的工具,如果写代码违反了eslint的规则-报错,暂时关闭eslint检查, 在vue.config.js中配置后重启服务:
module.exports = {
// ...其他配置
lintOnSave: false // 关闭eslint检查
}
3.vue报错示例:
vue指令
基础指令
- 变量同一放在data函数的return里面(固定写法)。然后再标签里面使用{{ }}将变量插入到里面。
export default {
data(){
return {
count: 1
}
},
// 2. 定义函数
methods: {
addFn(){ // this代表export default后{}对象, data和methods里的属性都直接挂在它身上
this.count++
},
}
}
- MVVM设计模式:设计模式: 是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结,MVVM(模型, 视图, 视图模型双向关联的一种设计模式),减少DOM操作, 提高开发效率
- 指令给标签添加额外的功能,将变量值绑定给标签的属性,语法: v-bind:属性名=“vue变量”。简写: :属性名="vue变量
- 给标签绑定事件,v-on: 可以简写成@:
⚫ v-on:事件名=“要执行的少量代码"
⚫ v-on:事件名=“methods中的函数名"
⚫ v-on:事件名=“methods中的函数名(实参)" - 获取事件对象:
⚫ 无传参, 通过形参直接接收
⚫ 传参, 通过$event指代事件对象传给事件处理函数
<template>
<div>
<a @click="one" href="http://www.baidu.com">百度</a>
<hr />
<a @click="two(10, $event)" href="http://www.taobao.com">淘宝</a>
</div>
</template>
<script>
export default {
methods: {
// 1. 事件触发, 无传值, 可以直接获取事件对象是
one(e){
e.preventDefault()
},
// 2. 事件触发, 传值, 需要手动传入$event
two(num, e){
e.preventDefault()
}
}
};
</script>
- 在事件后面.修饰符名 :@事件名.修饰符=“methods里函数”
⚫ .stop - 阻止事件冒泡
⚫ .prevent - 阻止默认行为
⚫ .once - 程序运行期间, 只触发一次事件处理函数(第一次后事件还会触发,只是函数不再执行) - 检测按键的方法:
⚫ @keyup.enter=“methods里函数” - 监测回车按键
⚫ @keyup.esc =“methods里函数”- 监测返回按键
更多修饰符: https://cn.vuejs.org/v2/guide/events.html - 案例:
将字符串变为数组 let arr = string.split(“”)
然后再进行拼接 msg = arr.join(“”) - v-model 双向数据绑定 。value属性 - vue变量,详细用法如下,v-model:
<div>
<span>来自于: </span>
<!-- 下拉菜单要绑定在select上 -->
<select v-model="from">
<option value="北京市">北京</option>
<option value="南京市">南京</option>
<option value="天津市">天津</option>
</select>
</div>
<div>
<!-- (重要)
遇到复选框, v-model的变量值,正常情况下我们需要把变量定义为数组
非数组 - 关联的是复选框的checked属性
数组 - 关联的是复选框的value属性
-->
<span>爱好: </span>
<input type="checkbox" v-model="hobby" value="抽烟">抽烟
<input type="checkbox" v-model="hobby" value="喝酒">喝酒
<input type="checkbox" v-model="hobby" value="写代码">写代码
</div>
<div>
<span>性别: </span>
<input type="radio" value="男" name="sex" v-model="gender">男
<input type="radio" value="女" name="sex" v-model="gender">女
</div>
<div>
<span>自我介绍</span>
<textarea v-model="intro"></textarea>
</div>
</div>
- 语法: v-model.修饰符=“Vue数据变量”
⚫ .number 以parseFloat转成数字类型,如果不是数字则不进行转换
⚫ .trim 去除首尾空白字符
⚫ .lazy 在change时触发而非inupt时(失去焦点的时候在进行赋值) - v-text=“Vue数据变量” v-html=“Vue数据变量” 注意: 会覆盖插值表达式
- 控制标签的隐藏或出现, 语法: v-show=“Vue变量” 或者 v-if=“Vue变量”
v-show 用的display:none隐藏 (频繁切换使用)。 v-if 直接从DOM树上移除
高级用法:
<!-- v-if和v-else使用 -->
<p v-if="age >= 18">成年了</p>
<p v-else>未成年</p>
- 列表渲染, 所在标签结构, 按照数据数量, 循环生成,v-for=“(值变量, 索引变量) in 目标结构” 或者 v-for="值变量 in 目标结构,循环谁就加在谁身上,可以遍历数组 / 对象 / 数字 / 字符串 (可遍历结构)。v-for的临时变量名不能用到v-for范围外举例如下:
<div>
<!-- 语法1:
v-for="(值变量名, 索引变量名) in 目标结构"
口诀: 想要谁循环就放到谁身上
-->
<ul>
<li v-for="(item, index) in arr" :key="index">
{{ item }} ---- {{ index }}
</li>
</ul>
<!-- 语法2:
v-for="值变量名 in 目标结构"
-->
<ul>
<li v-for="obj in stuArr" :key="obj.id">
<span>{{ obj.name }}</span>
<span>{{ obj.sex }}</span>
<span>{{ obj.hobby }}</span>
</li>
</ul>
<!-- 语法3:(了解)
v-for="(value, key) in 对象"
-->
<div>
<p v-for="(value, key) in tObj" :key="value">
<span>{{ value }}</span>
=======
<span>{{ key }}</span>
</p>
</div>
<!-- 语法4: (了解)
v-for="变量名 in 固定数字"
从1开始遍历
-->
<div v-for="n in count" :key="n">{{ n }}</div>
</div>
- js中需要导入图片文件(不能使用图片路径),改为变量,而在css/标签中可以直接使用图片路径,数组的方法如果能够改变数组,那么v-for也会进行改变。如果只是单纯赋值,则不会发生改变,但可以使用this.$set(参数1: 更新目标结构,参数2: 更新位置,参数3: 更新值),也可以把修改过后的数值重新赋值给原来的数组变量进行更新修改。
- 动态添加类名和动态添加样式
语法 :class=“{类名: 布尔值}” 当bool值为true的时候才有这个类名
:style=“{css属性名: 值}” 给标签添加css样式
yarn add bootstrap可以直接下载样式,但是还是记得在main.js中引入
虚拟DOM与key
- template标签转给虚拟DOM(本质就是一个JS对象, 保存DOM关键信息),再转为真实DOM,方面后续更改(同级比较-根元素不变-属性改变更新属性,)。
- 没有key属性:最大限度重用。
有key属性, 基于key的来比较新旧虚拟DOM, 移除key不存在元素,key要求是唯一不重复的字符串或者数值 - 如果key是索引,那么还是就地更新,但是,如果key是id就会根据id进行更新,有id用id, 无id用索引,key主要是配合虚拟DOM提高更新的性能。
过滤器
- 转换格式, 过滤器就是一个函数, 传入值返回处理后的值,想要把数据转为另一种的形式,过滤器只能用在, 插值表达式和v-bind动态属性里
- 语法:
⚫ Vue.filter(“过滤器名”, (值) => {return “返回处理后的值”})
⚫ filters: {过滤器名字: (值) => {return “返回处理后的值”} - 如果定义在了main.js 文件里面,那么是全局的过滤器,使用上述的第一种写法,如果放在对应文件的export default 里面,和data同级别,那么就是局部的过滤器。
- 可以在属性中使用,也可以在标签中使用如:
<p>使用翻转过滤器: {{ msg | reverse }}</p>
<p :title="msg | toUp">鼠标长停</p>
- 过滤器传参: vue变量 | 过滤器(实参),对应的 filters: {过滤器名字: (值,参数) => {return “返回处理后的值”}**
- 多个过滤器的使用 : vue变量 | 过滤器1 | 过滤器2
- 注意过滤器不写参数的时候,默认会有一个要处理数据的值,不需要在使用的时候额外把这个数据传过来,但接收的时候要有一个参数
计算属性
- 一个变量的值, 依赖另外一些数据计算而来的结果,计算属性也是vue数据变量, 所以不要和data里重名。
<div>
<p>{{ num }}</p>
</div>
//与data同级别
export default {
data(){
return {
a: 10,
b: 20
}
},
// 注意: 计算属性和data属性都是变量-不能重名
// 注意2: 函数内变量变化, 会自动重新计算结果返回
computed: {
num(){
return this.a + this.b
}
}
}
- 计算属性, 基于依赖项的值进行缓存,依赖的变量不变, 都直接从缓存取结果,如果值改变,函数自动执行并重新缓存。因此多次调用可能只执行一次,但是如果函数的话,调用一次就会执行一次。
- 若想给计算属性赋值,实现计算属性的双向绑定,需要使用计算属性的完整写法:
<div>
<span>姓名:</span>
<input type="text" v-model="full">
</div>
export default {
computed: {
full: {
// 给full赋值触发set方法
set(val){
console.log(val)
},
// 使用full的值触发get方法
get(){
return "无名氏"
}
}
}
}
侦听器
- 目标: 可以侦听data/computed属性值的改变
/* 语法:
watch: {
变量名 (newVal, oldVal){
// 变量名对应值改变这里自动触发
}
}
*/
watch: {
// newVal: 当前最新值
// oldVal: 上一刻值
name(newVal, oldVal){
console.log(newVal, oldVal);
}
}
- 如果想要侦听对象值的变化,使用下面的写法:
/*语法:
对象名: {
handler(newVal, oldVal){
},
deep: true, // 深度侦听(对象里面层的值改变)
immediate: true // 立即侦听(网页打开handler执行一次)
}
}
*/
watch: {
user: {
handler(newVal, oldVal){
// user里的对象
console.log(newVal, oldVal);
},
deep: true,
immediate: true
}
}
VUE组件
组件是可复用的 Vue 实例, 封装标签, 样式和JS代码。组件化 :封装的思想,把页面上可重用的部分封装为 组件,从而方便项目的 开发 和 维护。 一个页面, 可以拆分成一个个组件,一个组件就是一个整体, 每个组件可以有自己独立的 结构 样式 和 行为(html,css和js)
创建使用
- 全局注册 – main.js中 ,定义组件时最好都以大写字母开头
main.js:
import 组件对象 from 'vue文件路径'
Vue.component("组件名",组件对象)
使用的时候:
直接<组件名></组件名>
- 局部注册:
// 1. 创建组件 - 文件名.vue
// 2. 引入组件
import Pannel from './components/Pannel_1'
export default {
// 3. 局部 - 注册组件
/*
语法:
components: {
"组件名": 组件对象
}
*/
components: {
PannelL: Pannel
//如果同名可以简写成一个即可
}
}
使用:
<PannelL></PannelL>
- 给style上添加scoped,可以使得Vue组件内样式, 只针对当前组件内标签生效,其会自动给标签添加data-v-hash值属性,所有选择都带属性选择,便于和其他进行区分。
组件通信
- 父向子传值:被引入的谁是子(如app引入了compoents,则app是父),从父亲写数据传给儿子,让儿子动态生成不一样的数据
<!--
目标: 父(App.vue) -> 子(MyProduct.vue) 分别传值进入
需求: 每次组件显示不同的数据信息
步骤(口诀):
1. 子组件 - props - 变量 (准备接收)
2. 父组件 - 传值进去
-->
子文件:
<template>
<div class="my-product">
<h3>标题: {{ title }}</h3>
<p>价格: {{ price }}元</p>
<p>{{ intro }}</p>
</div>
</template>
<script>
export default {
props: ['title', 'price', 'intro']
}
</script>
父亲文件:
<template>
<div>
<MyProduct v-for="obj in list" :key="obj.id"
:title="obj.proname"
:price="obj.proprice"
:intro="obj.info"
></MyProduct>
</div>
</template>
<script>
// 目标: 循环使用组件-分别传入数据
// 1. 创建组件
// 2. 引入组件
import MyProduct from './components/MyProduct'
export default {
data() {
return {
list: [
{
id: 1,
proname: "超级好吃的棒棒糖",
proprice: 18.8,
info: "开业大酬宾, 全场8折",
},
{
id: 2,
proname: "超级好吃的大鸡腿",
proprice: 34.2,
info: "好吃不腻, 快来买啊",
},
{
id: 3,
proname: "超级无敌的冰激凌",
proprice: 14.2,
info: "炎热的夏天, 来个冰激凌了",
},
],
};
},
// 3. 注册组件
components: {
// MyProduct: MyProduct
MyProduct
}
};
</script>
- 最好不要再子组件中改父组件传过来的值,而且本身props定义的变量本身就是只读的。从父到子的数据流向, 叫单向数据流
- 子向父亲传值
(1)父组件内, 绑定自定义事件和事件处理函数 语法: @自定义事件名=“父methods里函数名”,注意只需要函数名,如果有参数也不需要,因为参数是子组件传过来的
(2)子组件内, 触发父给我绑的自定义事件, 导致父methods里事件处理函数执行,使用this.$emit(‘父组件自定义的事件名’,传入的参数) - 兄弟组件之间传递值:首先创建一个空白对象并且导出,然后再两个组件中进行导入,并传值:
空白组件/index.js://注意名字,通常是eventBus文件夹
import Vue from 'vue'
// 导出空白vue对象
export default new Vue()
传值文件:
import eventBus from '../EventBus'
export default {
methods: {
subFn(){
eventBus.$emit("send", 传递参数) // 跨组件
}
}
}
接受文件:
// 1. 引入空白vue对象(EventBus)
// 2. 接收方 - $on监听事件
import eventBus from "../EventBus";
export default {
// 3. 组件创建完毕, 监听send事件(事件名,自定义)
created() {
eventBus.$on("send", (参数) => {
......
});
},
};
生命周期
- 从创建到销毁的整个过程就是 – Vue实例的生命周期,拥有四大阶段八个方法:
- 上述函数执行的总体流程图如下所示:
created确定能否拿到data,mouted确定能否拿到真实的dom(即文档标签),updated能否获取更新的数据(更新后的真实dom),destory前提: v-if=“false” 销毁Vue实例。主要用于移除全局事件, 移除当前组件, 计时器, 定时器。
axios使用
axios 是一个专门用于发送ajax请求的库(用于前端异步请求后端的技术),官网: http://www.axios-js.com/, axios 底层还是原生js实现, 内部通过Promise封装的,axios()-原地得到的是Promise对象
网络 ->xhr 进行调试
- 发起get请求:
// 1. 下载axios
// 2. 引入axios
// 3. 发起axios请求
import axios from "axios";
// 4. 全局配置
axios.defaults.baseURL = "http://123.57.109.30:3006"//便于后续工作修改
export default {
data() {
return {
bName: "",
bookObj: { // 参数名提前和后台的参数名对上-发送请求就不用再次对接了
bookname: "",
author: "",
publisher: ""
}
};
},
methods: {
findFn() {
axios({
url: "/api/getbooks",
method: "GET",
params: { // 都会axios最终拼接到url?后面
bookname: this.bName
}
}).then(res => {
console.log(res);
})
},
- 发起post请求:
sendFn() {
axios({
url: "/api/addbook",
method: "POST",
data: {
appkey: "7250d3eb-18e1-41bc-8bb2-11483665535a",
...this.bookObj,
//等同于下面
// bookname: this.bookObj.bookname,
// author: this.bookObj.author,
// publisher: this.bookObj.publisher
},//参数名和值, 会被axios拼接到请求体里
});
},
$ refs和$nextTick使用
- 通过id或ref属性获取原生DOM,下述两个都能打印出标签
- 通过给组件加入ref属性后,可以再父亲中调用子件内部的方法,下述fn()就是内部的方法(但是不推荐使用这种方法进行通信 ):
- vue如果数据进行更新之后,不会立刻将其更新到dom中,会把代码都执行完了在更新到dom中,因此如果此时获取dom元素的该属性,则会出现问题,所以就需要语法: **this. n e x t T i c k ( 函数体 ) ∗ ∗ ,等 D O M 更新后 , 触发此方法里函数体执行,或者使用 u p d a t e d 方法, nextTick(函数体)**,等DOM更新后, 触发此方法里函数体执行,或者使用updated方法, nextTick(函数体)∗∗,等DOM更新后,触发此方法里函数体执行,或者使用updated方法,nextTick函数原地返回也是一个promise对象
methods: {
btn(){
this.count++; // vue监测数据更新, 开启一个DOM更新队列(异步任务)
// console.log(this.$refs.myP.innerHTML); // 0
// 原因: Vue更新DOM异步
// 解决: this.$nextTick()
// 过程: DOM更新完会挨个触发$nextTick里的函数体
this.$nextTick(() => {
console.log(this.$refs.myP.innerHTML); // 1
})
}
}
- 在组件内部自定义一个那么属性后,别人就可以使用你自己定义的名字调用自己这个组件:
购物车案例
- 自定义引入的组件的样式 props,可以使用props的校验方式,如:
组件内部:
<template>
<div class="my-header" :style="{backgroundColor: background, color}">{{ title }}</div>
</template>
export default {
props: {
background: String, // 外部插入此变量的值, 必须是字符串类型, 否则报错
color: {
type: String, // 约束color值的类型
default: "#fff" // color变量默认值(外部不给 我color传值, 使用默认值)
},
title: {
type: String,
required: true // 必须传入此变量的值
}
arr:{
// 自定义校验规则
validator(value) {
// value就是接到数组arr
if (value.length >= 2 && value.length <= 5) {
return true; // 符合条件就return true
} else {
console.error("数据源必须在2-5项");
return false;
}
},
}
}
调用者:
<MyHeader title="购物车案例"></MyHeader>
- 把axios添加为一个全局的属性,使其在每个组件中都能够进行使用:
main.js:
/ 目标: 请求数据 - 打印
// 1. 下载axios库, main.js - 全局绑定属性 (确保任意.vue文件可以都访问到这个axios方法)
import axios from 'axios'
// 2. 基础地址
axios.defaults.baseURL = "https://www.escook.cn"
// 3. axios方法添加到Vue的原型上
Vue.prototype.$axios = axios
- 如果要是进行数据的传递的时候,最好把对象当作一个整体的变量进行传递,不用分开传递。同时这样也方便修改对象的属性,如数量控制要通过对象"互相引用的关系"来影响外面对象里的数量值, 所以最好传对象进来。
- 通过label标签的for属性与指定表单元素的id绑定来实现关联表单,见博客
组件切换
实现一个页面动态切换不同的界面,两个的话可以考虑v-if,但是多个不行太麻烦,我们可以采用下述的方法 component是一个内置组件,is是固定的属性,依赖 “is” 的值来决定渲染哪一个组件,但是频繁的切换会导致组件频繁创建和销毁,性能不高,因此可以将其用keep-alive标签包起来,如下:
<template>
<div>
<button @click="comName = 'UserName'">账号密码填写</button>
<button @click="comName = 'UserInfo'">个人信息填写</button>
<p>下面显示注册组件-动态切换:</p>
<div style="border: 1px solid red;">
<keep-alive>
<component :is="comName"></component>
</keep-alive>
</div>
</div>
</template>
<script>
// 目标: 动态组件 - 切换组件显示
// 场景: 同一个挂载点要切换 不同组件 显示
// 1. 创建要被切换的组件 - 标签+样式
// 2. 引入到要展示的vue文件内, 注册
// 3. 变量-承载要显示的组件名
// 4. 设置挂载点<component :is="变量"></component>
// 5. 点击按钮-切换comName的值为要显示的组件名
import UserName from '../components/01/UserName'//被切换的组件一
import UserInfo from '../components/01/UserInfo'//被切换的组件二
export default {
data(){
return {
comName: "UserName"
}
},
components: {
UserName,
UserInfo
}
}
</script>
- 在组建切换的组件内部可以书写两个组件函数,来确定组件是否被激活(即被展示出来),便于后续的其它操作。
export default {
// 组件缓存下 - 多了2个钩子函数
activated(){//获得激活状态
console.log("02-UserName-激活");
},
deactivated(){//获得激活状态
console.log("02-UserName-失去激活");
}
}
组件插槽
- 如果标签不确定,就使用占位,等用的时候给其插 入具体的标签,每次传入的数值都可以不同:
使用的组件,里面的内容会替代slot标签位置,多个插槽可以使用下述方法:
1. slot使用name属性区分名字
2. template配合v-slot:名字来分发对应标签
<Pannel>
<template v-slot:title>
<h4>芙蓉楼送辛渐</h4>
</template>
<template v-slot:content>
<img src="../assets/mm.gif" alt="">
<span>我是内容</span>
</template>
</Pannel>
<Pannel>
<template #title>//v-slot:可以简化成#
<span style="color: red;">我是标题</span>
</template>
<template #content>
<p>寒雨连江夜入吴,</p>
<p>平明送客楚山孤。</p>
<p>洛阳亲友如相问,</p>
<p>一片冰心在玉壶。</p>
</template>
</Pannel>
<Pannel></Pannel>
被使用的Pannel组件,如果没有传入数值的时候,会显示默认的内容:
<template>
<slot name="title">默认显示的内容</slot>
<slot name="content">默认显示的内容</slot>
</template>
- 作用域插槽:使用被使用插槽里面的变量,可以用下面的方法:
⚫ 1. 子组件, 在slot上绑定属性和子组件内的值
⚫ 2. 使用组件, 传入自定义标签, 用template和v-slot=“自定义变量名” //不能简写,那个是冒号
⚫ 3. scope变量名自动绑定slot上所有属性和值
defaultTwo是defaultObj里面的变量或者属性
自定义指令
自己定义指令:
//全局注册,在main.js
// 全局指令 - 到处"直接"使用
// 目标: 自定义指令传值(如果没有值的时候,就只写一个参数,其是获取当前标签)
Vue.directive('color', {
inserted(el, binding) {//inserted方法 - 指令所在标签, 被插入到网页上触发(一次)
el.style.color = binding.value
},
update(el, binding) {//update方法 - 指令对应数据/标签更新时, 此方法执行
el.style.color = binding.value
}
})
//局部注册
<template>
<div>
<input type="text" v-focus>
<p v-color="colorStr">修改文字颜色</p>
</div>
</template>
export default {
data(){
return {
colorStr: 'red'
}
},
directives: {
focus: {
inserted(el){
el.focus()
}
}
}
}
tabbar案例
- 注意我们在v-for里面的循环的变量如果要对每个元素设置属性和方法,不要使用一个名字的变量,最好能找到区别每一个循环项的变量(如id)进行绑定
- v-if的使用
VUE路由
基础概念
前端路由:路径和组件的映射关系,主要使用vue-router这个第三方包,单页面应用(SPA): 所有功能在一个html页面上实现
前端路由作用: 实现业务场景切换。
⚫ 优点:整体不刷新页面,用户体验更好。 数据传递容易, 开发效率高
⚫ 缺点: 开发成本高(需要学习专门知识)。 首次加载会比较慢一点。不利于seo
- 组件分类:vue文件分2类, 一个是页面组件, 一个是复用组件(本质并无区别)
src/views文件夹: 页面组件 - 页面展示 - 配合路由用
src/components文件夹:复用组件 - 展示数据/常用于复用 - vue-router的使用(一般固定写法):
main.js:
import Vue from 'vue'
import App from './App.vue'
import Find from '@/views/Find' // @是src的绝对地址
import My from '@/views/My'
import Part from '@/views/Part'
import NotFound from '@/views/NotFound'
// 目标: vue-router基础使用
// 1. 下载vue-router (yarn add vue-router)
// 2. 引入
import VueRouter from 'vue-router'
// 3. 注册全局组件
Vue.use(VueRouter)
// 4. 规则数组
const routes = [
//路径从上到下逐一配对
//重定向,默认打开的路径
{
path: "/", // 默认hash值路径
redirect: "/find" // 重定向到/find
// 浏览器url中#后的路径被改变成/find-重新匹配规则
},
{
path: "/find",
name: "Find",
component: Find,
},
{
path: "/my",
name: "My",
component: My
},
{
path: "/part",
name: "Part",
component: Part
},
{
path: "/part/:username", // 有:的路径代表要接收具体的值
component: Part
},
// 404在最后(规则是从前往后逐个比较path)
{
path: "*",
component: NotFound
}
]
// 5. 生成路由对象
const router = new VueRouter({
routes,// routes是固定key(传入规则数组)
// mode: "history" // 默认不写是"hash"
})
Vue.config.productionTip = false
// 6. 路由对象注入到vue实例中, this可以访问$route和$router
new Vue({
router,
render: h => h(App),
}).$mount('#app')
在使用的App.vue文件中:
<template>
<div>
<div class="footer_wrap">
<a href="#/find">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/part">朋友</a>
</div>
<div class="top">
<!-- 7. 设置挂载点-当url的hash值路径切换, 显示规则里对应的组件到这 -->
<router-view></router-view>
</div>
</div>
</template>
声明式导航
- vue-router提供了一个全局组件 router-link,router-link实质上最终会渲染成a链接 to属性等价于提供 href属性(to无需#)。router-link提供了声明式导航高亮的功能(自带类名)
App.vue文件中:
<template>
<div>
<div class="footer_wrap">
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/part">朋友</router-link>
</div>
<div class="top">
<router-view></router-view>
</div>
</div>
</template>
<script>
// 目标: 声明式导航 - 基础使用
// 本质: vue-router提供的全局组件 "router-link"替代a标签
// 1. router-link 替代a标签
// 2. to属性 替代href属性
// 好处: router-link自带高亮的类名(激活时类名)
// 3. 对激活的类名做出样式的编写
export default {};
</script>
<style scoped>
.footer_wrap {
position: fixed;
left: 0;
top: 0;
display: flex;
width: 100%;
text-align: center;
background-color: #333;
color: #ccc;
}
.footer_wrap a {
flex: 1;
text-decoration: none;
padding: 20px 0;
line-height: 20px;
background-color: #333;
color: #ccc;
border: 1px solid black;
}
.footer_wrap a:hover {
background-color: #555;
}
.top {
padding-top: 62px;
}
/*激活时样式 */
.footer_wrap .router-link-active{
color: white;
background: black;
}
</style>
路由传值的方式
第一种:$route.query.参数名 接收参数
<router-link to="/part?name=小传">朋友-小传{{$route.query.name}}</router-link>
第二种: $route.params.参数名 接收参数
<router-link to="/part/小智">朋友-小智{{$route.params.username}}</router-link>
main.js
{
path: "/part/:username", // 有:的路径代表要接收具体的值
component: Part
},
编程式导航
- 路由的模式:
⚫ hash路由例如: http://localhost:8080/#/home
⚫ history路由例如: http://localhost:8080/home (以后上线需要服务器端支持, 否则找的是文件夹)
const router = new VueRouter({
routes,// routes是固定key(传入规则数组)
// mode: "history" // 默认不写是"hash"
})
- 使用js代码实现路由的跳转:
APP.VUE:
<template>
<div>
<div class="footer_wrap">
<span @click="btn('/find', 'Find')">发现音乐</span>
<span @click="btn('/my', 'My')">我的音乐</span>
<span @click="btn('/part', 'Part')">朋友</span>
</div>
<div class="top">
<router-view></router-view>
</div>
</div>
</template>
<script>
// 目标: 编程式导航 - js方式跳转路由
// 语法:
// this.$router.push({path: "路由路径"})
// this.$router.push({name: "路由名"})
// 注意:
// 虽然用name跳转, 但是url的hash值还是切换path路径值
// 场景:
// 方便修改: name路由名(在页面上看不见随便定义)
// path可以在url的hash值看到(尽量符合组内规范)
export default {
//方式一:
// methods:{
// btn(targetPath){
// // 方式1: path跳转
// this.$router.push({
// path: targetPath,
// })
// }
// }
//方式二:需在main.js路由那里给路径定义name值
methods: {
btn(targetPath, targetName){
this.$router.push({
name: targetName
})
}
}
};
</script>
- js跳转路径并且进行传参(给对应路径下的文件进行传参)。 path会自动忽略params,主要是看上述方法选择方法几:query传, $route.query接;params传, $route.params接,使用其中一个就行
嵌套和守卫
- 路由的嵌套
main.js– 继续配置2级路由
⚫ 一级路由path从/开始定义
⚫ 二级路由往后path直接写名字, 无需/开头
⚫ 嵌套路由在上级路由的children数组里编写路由信息对象
记得引入页面组件
{
path: "/find",
name: "Find",
component: Find,
children: [
{
path: "recommend",
component: Recommend
},
{
path: "ranking",
component: Ranking
},
{
path: "songlist",
component: SongList
}
]
},
然后再对应的find组件中定义路由
<div class="nav_main">
<router-link to="/find/recommend">推荐</router-link>
<router-link to="/find/ranking">排行榜</router-link>
<router-link to="/find/songlist">歌单</router-link>
</div>
<div style="1px solid red;">
<router-view></router-view>
</div>
- 虽然二级路由激活的时候还是那个类名,但是vue在处理的时候会进行区分,具体如下:
⚫ router-link-exact-active (精确匹配) url中hash值路径, 与href属性值完全相同, 设置此类名(第二级)
⚫ router-link-active (模糊匹配) url中hash值, 包含href属性值这个路径(第一级) - 路由守卫:路由跳转之前, 会触发一个函数,进行权限的判断
//目标:路由守卫
//场景:当你要对路由权限判断时
语法:router .beforeEach ((to,from,next)=>{//路由跳转"之前"先执行这里,决定是否跳 转})
参数1:要跳转到的路由(目标路由对象信息)
参数2:从哪里跳转的路由(来源 路由对象信息)
参数3:函数体-next()才会让路由正常的跳转切换,
next(false )在原地停留,next("强制修 改到另一个路由路径上")
//注意:如果不调用next,页面留在原地工
如下图的例子所示://main.js文件中
Vant组件库
Vant是一个轻量、可靠的移动端 Vue 组件库, 开箱即用(引用下载的时候注意看清是vue的版本)
网址:https://vant-contrib.gitee.io/vant/#/zh-CN/
解决跨域,可以在本地写一个服务器(node.js)可以去百度上去找对应的API的代码,再用服务器去访问跨域的资源,因为跨域是针对浏览器之间的,服务器是可以进行访问的
VUE3
详见笔记:http://zhoushugang.gitee.io/erabbit-client-pc-document/
VUE 原理
尝试使用原生的js实现VUE的功能,便于对基本原理的掌握,详见笔记https://cp_fe.gitee.io/course-advance/#/