目录
1.vue指令合集
1.1 插值表达式 又叫: 声明式渲染/文本插值
语法: {{ 表达式 }}
1.2 v-bind
目标: 给标签属性设置vue变量的值
-
语法:
v-bind:属性名="vue变量"
-
简写:
:属性名="vue变量"
1.3vue指令-v-on
目标: 给标签绑定事件
-
语法
-
v-on:事件名="要执行的==少量代码=="
-
v-on:事件名="methods中的函数"
-
v-on:事件名="methods中的函数(实参)"
-
-
简写: @事件名="methods中的函数"
1.3.1vue指令-v-on事件对象
目标: vue事件处理函数中, 拿到事件对象
-
无传参, 通过形参直接接收
-
传参, 通过$event指代事件对象传给事件处理函数
1.3.2vue指令-v-on修饰符
目的: 在事件后面.修饰符名 - 给事件带来更强大的功能
语法:
@事件名.修饰符="methods里函数"
-
.stop - 阻止事件冒泡
-
.prevent - 阻止默认行为
-
.once - 程序运行期间, 只触发一次事件处理函数
1.3.3vue指令-v-on按键修饰符
目标: 给键盘事件, 添加修饰符, 增强能力
语法:
-
@keyup.enter - 监测回车按键
-
@keyup.esc - 监测返回按键
1.4vue指令 v-model
目标: 把value属性和vue数据变量, 双向绑定到一起
语法: v-model="vue数据变量"
双向数据绑定
数据变化 -> 视图自动同步
视图变化 -> 数据自动同步
1.4.1v-model修饰符
目标: 让v-model拥有更强大的功能
-
语法:
-
v-model.修饰符="vue数据变量"
-
.number 以parseFloat转成数字类型
-
.trim 去除首尾空白字符
-
.lazy 在change时触发而非inupt时
-
-
1.5vue指令 v-text和v-html
目的: 更新DOM对象的innerText/innerHTML
-
语法:
-
v-text="vue数据变量"
-
v-html="vue数据变量"
-
-
注意: 会覆盖插值表达式
1.6vue指令 v-show和v-if
目标: 控制标签的隐藏或出现
-
语法:
-
v-show="vue变量"
-
v-if="vue变量"
-
-
原理
-
v-show 用的display:none隐藏 (频繁切换使用)
-
v-if 直接从DOM树上移除
-
-
v-if之下 还可以写v-else-if 和v-else
1.7vue指令-v-for
目标: 列表渲染, 所在标签结构, 按照数据数量, 循环生成
-
语法
-
v-for="(值, 索引) in 目标结构"
-
v-for="值 in 目标结构"
-
-
目标结构:
-
可以遍历数组 / 对象 / 数字 / 字符串 (可遍历结构)
-
-
注意:
v-for的临时变量名不能用到v-for范围外
1.8vue指令v-pre
目标: 跳过该元素的渲染 直接显示其字符串
语法
-
v-pre
1.9 vue指令v-cloak
目标:如果vue没有加载完成则元素不会显示,直到编译结束。
1.10vue指令v-once
目标:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。
1.11vue指令v-slot
-
缩写:
#
-
预期:可放置在函数参数位置的 JavaScript 表达式 (在支持的环境下可使用解构)。可选,即只需要在为插槽传入 prop 的时候使用。
-
<template>
-
组件: (对于一个单独的带 prop 的默认插槽)
-
用法:
提供具名插槽或需要接收 prop 的插槽。
2.vue 动态指令
2.1vue基础 动态class
目标: 用v-bind给标签class设置动态的值
语法:
-
:class="{类名: 布尔值}"
<p :class="{red_str: bool}">动态class</p>
2.2vue基础-动态style
目标: 给标签动态设置style的值
语法
-
:style="{css属性: 值}"
<p :style="{backgroundColor: colorStr}">动态style</p>
3.vue过滤器
vue过滤器-定义使用
目的: 转换格式, 过滤器就是一个函数, 传入值返回处理后的值
过滤器只能用在, ==插值表达式和v-bind表达式==
Vue中的过滤器场景
-
字母转大写, 输入"hello", 输出"HELLO"
-
字符串翻转, "输入hello, world", 输出"dlrow ,olleh"
语法:
-
Vue.filter("过滤器名", (值) => {return "返回处理后的值"})
-
filters: {过滤器名字: (值) => {return "返回处理后的值"}
例子:
-
全局定义字母都大写的过滤器
-
局部定义字符串翻转的过滤器
<template> <div> <p>原来的样子: {{ msg }}</p> <!-- 2. 过滤器使用 语法: {{ 值 | 过滤器名字 }} --> <p>使用翻转过滤器: {{ msg | reverse }}</p> <p :title="msg | toUp">鼠标长停</p> </div> </template> <script> export default { data(){ return { msg: 'Hello, Vue' } }, // 方式2: 局部 - 过滤器 // 只能在当前vue文件内使用 /* 语法: filters: { 过滤器名字 (val) { return 处理后的值 } } */ filters: { toUp (val) { return val.toUpperCase() } } } </script> <style> </style>
4.计算属性
目标: 一个数据, 依赖另外一些数据计算而来的结果
语法:
computed: { "计算属性名" () { return "值" } }
实例:
需求: 求2个数的和显示到页面上
<template> <div> <p>{{ num }}</p> </div> </template> <script> export default { data(){ return { a: 10, b: 20 } }, // 计算属性: // 场景: 一个变量的值, 需要用另外变量计算而得来 /* 语法: computed: { 计算属性名 () { return 值 } } */ // 注意: 计算属性和data属性都是变量-不能重名 // 注意2: 函数内变量变化, 会自动重新计算结果返回 computed: { num(){ return this.a + this.b } } } </script> <style> </style>
4.1计算属性 缓存
目标: 计算属性是基于它们的依赖项的值结果进行缓存的,只要依赖的变量不变, 都直接从缓存取结果
会减少函数的调取次数
// 计算属性优势: // 带缓存 // 计算属性对应函数执行后, 会把return值缓存起来 // 依赖项不变, 多次调用都是从缓存取值 // 依赖项值-变化, 函数会"自动"重新执行-并缓存新的值
4.2计算属性的完整写法
export default { computed: { full: { // 给full赋值触发set方法 set(val){ console.log(val) }, // 使用full的值触发get方法 get(){ return "无名氏" } } } }
5vue侦听器-watch
目标: 可以侦听data/computed属性值改变
语法
watch: { "被侦听的属性名" (newVal, oldVal){ } }
实例:
<template> <div> <input type="text" v-model="name"> </div> </template> <script> export default { data(){ return { name: "" } }, // 目标: 侦听到name值的改变 /* 语法: watch: { 变量名 (newVal, oldVal){ // 变量名对应值改变这里自动触发 } } */ watch: { // newVal: 当前最新值 // oldVal: 上一刻值 name(newVal, oldVal){ console.log(newVal, oldVal); } } } </script> <style> </style>
5.1vue侦听器-深度侦听和立即执行
目标: 侦听复杂类型, 或者立即执行侦听函数
语法:
watch: { "要侦听的属性名": { immediate: true, // 立即执行 deep: true, // 深度侦听复杂类型内变化 handler (newVal, oldVal) { } } }
实例:
<template> <div> <input type="text" v-model="user.name"> <input type="text" v-model="user.age"> </div> </template> <script> export default { data(){ return { user: { name: "", age: 0 } } }, // 目标: 侦听对象 /* 语法: watch: { 变量名 (newVal, oldVal){ // 变量名对应值改变这里自动触发 }, 变量名: { handler(newVal, oldVal){ }, deep: true, // 深度侦听(对象里面层的值改变) immediate: true // 立即侦听(网页打开handler执行一次) } } */ watch: { user: { handler(newVal, oldVal){ // user里的对象 console.log(newVal, oldVal); }, deep: true, immediate: true } } } </script> <style> </style>
6.vue组件(重点中的重点)
组件是可复用的 Vue 实例, 封装标签, 样式和JS代码
组件化 :封装的思想,把页面上 可重用的部分
封装为 组件
,从而方便项目的 开发 和 维护
一个页面, 可以拆分成一个个组件,一个组件就是一个整体, 每个组件可以有自己独立的 结构 样式 和 行为(html, css和js)
6.1组件的基础使用
步骤:
-
创建组件 components/Pannel.vue
-
注册组件: 创建后需要注册后再使用
注册组件分为两种 一种是全局组件 一种是局部组件
全局 - 注册使用
全局入口在main.js, 在new Vue之上注册
语法:
import Vue from 'vue' import 组件对象 from 'vue文件路径'
Vue.component("组件名", 组件对象)
// 目标: 全局注册 (一处定义到处使用) // 1. 创建组件 - 文件名.vue // 2. 引入组件 import Pannel from './components/Pannel' // 3. 全局 - 注册组件 /* 语法: Vue.component("组件名", 组件对象) */ Vue.component("PannelG", Pannel)
全局注册PannelG组件名后, 就可以当做标签在任意Vue文件中template里用
单双标签都可以或者小写加-形式, 运行后, 会把这个自定义标签当做组件解析, 使用==组件里封装的标签替换到这 个位置==
局部 - 注册使用
语法:
import 组件对象 from 'vue文件路径'
export default { components: { "组件名": 组件对象 } }
<template> <div id="app"> <h3>案例:折叠面板</h3> <!-- 4. 组件名当做标签使用 --> <!-- <组件名></组件名> --> <PannelG></PannelG> <PannelL></PannelL> </div> </template> <script> // 目标: 局部注册 (用的多) // 1. 创建组件 - 文件名.vue // 2. 引入组件 import Pannel from './components/Pannel_1' export default { // 3. 局部 - 注册组件 /* 语法: components: { "组件名": 组件对象 } */ components: { PannelL: Pannel } } </script>
组件使用总结:
-
(创建)封装html+css+vue到独立的.vue文件中
-
(引入注册)组件文件 => 得到组件配置对象
-
(使用)当前页面当做标签使用
6.2vue组件-scoped作用
目的: 解决多个组件样式名相同, 冲突问题
解决: 给Pannel.vue组件里style标签上加scoped属性即可
6.2.1::v-deep 类名 可以进行样式穿透
可以只app.vue 中style里面 使用样式穿透 修改其他组件里的样式
6.3vue组件通信_父向子-props (重点)
目的:从外面给组件内传值
步骤:
-
创建组件components/MyProduct.vue - 复制下面标签
-
组件内在props定义变量, 用于接收外部传入的值
-
App.vue中引入注册组件, 使用时, 传入具体数据给组件显示
components/MyProduct.vue export default { props: ['title', 'price', 'intro'] } App.vue中使用并传入数据 <Product title="好吃的口水鸡" price="50" intro="开业大酬宾, 全场8折"></Product> <Product title="好可爱的可爱多" price="20" intro="老板不在家, 全场1折"></Product> <Product title="好贵的北京烤鸭" price="290" :intro="str"></Product>
父向子传递参数时可以使用事件循环v-for
6.4vue组件通信_单向数据流
目标: props变量本身是只读不能重新赋值
目标:从==父到子==的数据流向,叫==单向数据流==
原因: 子组件修改, 不通知父级, 造成数据不一致性
6.5vue组件通信_子向父
-
父: @自定义事件名="父methods函数"
-
子: this.$emit("自定义事件名", 传值) - 执行父methods里函数代码
总结: 父自定义事件和方法, 等待子组件触发事件给方法传值
6.6兄弟通信(使用较少)
两个组件的关系非常的复杂,通过父子组件通讯是非常麻烦的。这时候可以使用通用的组件通讯方案:事件总线(event-bus)
核心语法
EventBus/index.js- 定义事件总线bus对象
import Vue from 'vue' // 导出空白vue对象 export default new Vue()
List.vue注册事件 - 等待接收要砍价的值 (==直接复制==) - 准备兄弟页面
<template> <ul class="my-product"> <li v-for="(item, index) in arr" :key="index"> <span>{{ item.proname }}</span> <span>{{ item.proprice }}</span> </li> </ul> </template> <script> export default { props: ['arr'], } </script> <style> .my-product { width: 400px; padding: 20px; border: 2px solid #000; border-radius: 5px; margin: 10px; } </style>
components/MyProduct_sub.vue(==带学生主要写触发eventBus身上事件==)
<template> <div class="my-product"> <h3>标题: {{ title }}</h3> <p>价格: {{ price }}元</p> <p>{{ intro }}</p> <button @click="subFn">宝刀-砍1元</button> </div> </template> <script> import eventBus from '../EventBus' export default { props: ['index', 'title', 'price', 'intro'], methods: { subFn(){ this.$emit('subprice', this.index, 1) // 子向父 eventBus.$emit("send", this.index, 1) // 跨组件 } } } </script> <style> .my-product { width: 400px; padding: 20px; border: 2px solid #000; border-radius: 5px; margin: 10px; } </style>
List.vue正确代码(==EventBus接收方==)
<template> <ul class="my-product"> <li v-for="(item, index) in arr" :key="index"> <span>{{ item.proname }}</span> <span>{{ item.proprice }}</span> </li> </ul> </template> <script> // 目标: 跨组件传值 // 1. 引入空白vue对象(EventBus) // 2. 接收方 - $on监听事件 import eventBus from "../EventBus"; export default { props: ["arr"], // 3. 组件创建完毕, 监听send事件 created() { eventBus.$on("send", (index, price) => { this.arr[index].proprice > 1 && (this.arr[index].proprice = (this.arr[index].proprice - price).toFixed(2)); }); }, }; </script> <style> .my-product { width: 400px; padding: 20px; border: 2px solid #000; border-radius: 5px; margin: 10px; } </style>
总结: 空的Vue对象, 只负责$on注册事件, $emit触发事件, 一定要确保$on先执行
7.生命周期
创建期间的生命周期函数:
初始化阶段
beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性 created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板
挂载阶段(使用多)
beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中 mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
运行期间的生命周期函数:
更新阶段(使用多)
beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点 updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!销毁期间的生命周期函数:
销毁阶段
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。 destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
更新阶段
activated - 激活
deactivated - 失去激活状态
8.axios
特点
-
支持客户端发送Ajax请求
-
支持服务端Node.js发送请求
-
支持Promise相关用法
-
支持请求和响应的拦截器功能
-
自动转换JSON数据
-
axios 底层还是原生js实现, 内部通过Promise封装的
axios的基本使用
axios({ method: '请求方式', // get post url: '请求地址', data: { // 拼接到请求体的参数, post请求的参数 xxx: xxx, }, params: { // 拼接到请求行的参数, get请求的参数 xxx: xxx } }).then(res => { console.log(res.data) // 后台返回的结果 }).catch(err => { console.log(err) // 后台报错返回 })
8.1axios的基本使用 -全局配置
目标: 避免前缀基地址, 暴露在逻辑页面里, 统一设置
axios.defaults.baseURL = "http://123.57.109.30:3006" // 所有请求的url前置可以去掉, 请求时, axios会自动拼接baseURL的地址在前面 getAllFn() { axios({ url: "/api/getbooks", method: "GET", // 默认就是GET方式请求, 可以省略不写 }).then((res) => { console.log(res); }); // axios()-原地得到Promise对象 },
9.$refs与$nextTick
9.1$refs获取dom元素
目标: 利用 ref 和 $refs 可以用于获取 dom 元素
<template> <div> <p>1. 获取原生DOM元素</p> <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1> </div> </template> <script> // 目标: 获取组件对象 // 1. 创建组件/引入组件/注册组件/使用组件 // 2. 组件起别名ref // 3. 恰当时机, 获取组件对象 export default { mounted(){ console.log(document.getElementById("h")); // h1 console.log(this.$refs.myH); // h1 } } </script> <style> </style>
9.2 $refs-获取组件对象
目标: 获取组件对象, 调用组件里方法
components/Child/Demo.vue
<template> <div> <p>我是Demo组件</p> </div> </template> <script> export default { methods: { fn(){ console.log("demo组件内的方法被调用了"); } } } </script>
More.vue - 获取组件对象 - 调用组件方法
<template> <div> <p>1. 获取原生DOM元素</p> <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1> <p>2. 获取组件对象 - 可调用组件内一切</p> <Demo ref="de"></Demo> </div> </template> <script> // 目标: 获取组件对象 // 1. 创建组件/引入组件/注册组件/使用组件 // 2. 组件起别名ref // 3. 恰当时机, 获取组件对象 import Demo from './Child/Demo' export default { mounted(){ console.log(document.getElementById("h")); // h1 console.log(this.$refs.myH); // h1 let demoObj = this.$refs.de; demoObj.fn() }, components: { Demo } } </script>
总结: ref定义值, 通过$refs.值 来获取组件对象, 就能继续调用组件内的变量
9.3 $nextTick使用
Vue更新DOM-异步的
目标: 点击count++, 马上通过"原生DOM"拿标签内容, 无法拿到新值
实例代码
components/Tick.vue
<template> <div> <input ref="myInp" type="text" placeholder="这是一个输入框" v-if="isShow"> <button v-else @click="btn">点击我进行搜索</button> </div> </template> <script> // 目标: 点按钮(消失) - 输入框出现并聚焦 // 1. 获取到输入框 // 2. 输入框调用事件方法focus()达到聚焦行为 export default { data(){ return { isShow: false } }, methods: { async btn(){ this.isShow = true; // this.$refs.myInp.focus() // 原因: data变化更新DOM是异步的 // 输入框还没有挂载到真实DOM上 // 解决: // this.$nextTick(() => { // this.$refs.myInp.focus() // }) // 扩展: await取代回调函数 // $nextTick()原地返回Promise对象 await this.$nextTick() this.$refs.myInp.focus() } } } </script>
9.4 组件name属性使用
目标: 可以用组件的name属性值, 来注册组件名字
问题: 组件名不是可以随便写的?
答案: 我们封装的组件-可以自己定义name属性组件名-让使用者有个统一的前缀风格
components/Com.vue
<template> <div> <p>我是一个Com组件</p> </div> </template> <script> export default { name: "ComNameHaHa" // 注册时可以定义自己的名字 } </script>
App.vue - 注册和使用
<template> <div> <h1>1. 生命周期</h1> <Life v-if="show"></Life> <button @click="show = false">销毁组件</button> <hr> <h1>2. axios使用</h1> <UseAxios></UseAxios> <hr> <h1>3. $refs的使用</h1> <More></More> <hr> <h1>4. $nextTick使用场景</h1> <Tick></Tick> <hr> <h1>5. 组件对象里name属性</h1> <ComNameHaHa></ComNameHaHa> </div> </template> <script> import Life from './components/Life' import UseAxios from './components/UseAxios' import More from './components/More' import Tick from './components/Tick' import Com from './components/Com' export default { data(){ return { show: true } }, components: { Life, UseAxios, More, Tick, [Com.name]: Com // 对象里的key是变量的话[]属性名表达式 // "ComNameHaHa": Com } } </script>
10.组件进阶
10.1动态组件
目标: 多个组件使用同一个挂载点,并动态切换,这就是动态组件
需求: 完成一个注册功能页面, 2个按钮切换, 一个填写注册信息, 一个填写用户简介信息
-
准备被切换的 - UserName.vue / UserInfo.vue 2个组件
-
引入到UseDynamic.vue注册
-
准备变量来承载要显示的"组件名"
-
设置挂载点<component>, 使用is属性来设置要显示哪个组件
-
点击按钮 – 修改comName变量里的"组件名"
<template> <div> <button @click="comName = 'UserName'">账号密码填写</button> <button @click="comName = 'UserInfo'">个人信息填写</button> <p>下面显示注册组件-动态切换:</p> <div style="border: 1px solid red;"> <component :is="comName"></component> </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>
总结: vue内置component组件, 配合is属性, 设置要显示的组件名字
10.2组件缓存
目标: 组件切换会导致组件被频繁销毁和重新创建, 性能不高
Vue内置的keep-alive组件 包起来要频繁切换的组件
<div style="border: 1px solid red;"> <!-- Vue内置keep-alive组件, 把包起来的组件缓存起来 --> <keep-alive> <component :is="comName"></component> </keep-alive> </div>
10.3组件插槽(重点)
目标: 用于实现组件的内容分发, 通过 slot 标签, 可以接收到写在组件标签内的内容
vue提供组件插槽能力, 允许开发者在封装组件时,把不确定的部分定义为插槽
插槽例子:
语法口诀:
-
组件内用<slot></slot>占位
-
使用组件时<Pannel></Pannel>夹着的地方, 传入标签替换slot
10.4插槽默认内容
目标: 如果外面不给传值, 想给个默认显示内容
口诀: <slot>夹着内容默认显示内容, 如果不给插槽slot传东西, 则使用<slot>夹着的内容在原地显示
<slot>默认内容</slot>
10.5具名插槽
目标: 当一个组件内有2处以上需要外部传入标签的地方
传入的标签可以分别派发给不同的slot位置
要求: v-slot一般用跟template标签使用 (template是html5新出标签内容模板元素, 不会渲染到页面上, 一般被vue解析内部标签)
components/04/Pannel.vue - 留下具名slot
<template> <div> <!-- 按钮标题 --> <div class="title"> <slot name="title"></slot> <span class="btn" @click="isShow = !isShow"> {{ isShow ? "收起" : "展开" }} </span> </div> <!-- 下拉内容 --> <div class="container" v-show="isShow"> <slot name="content"></slot> </div> </div> </template>
views/04_UseSlot.vue使用
<template> <div id="container"> <div id="app"> <h3>案例:折叠面板</h3> <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> <span style="color: red;">我是标题</span> </template> <template #content> <p>寒雨连江夜入吴,</p> <p>平明送客楚山孤。</p> <p>洛阳亲友如相问,</p> <p>一片冰心在玉壶。</p> </template> </Pannel> </div> </div> </template> <script> import Pannel from "../components/04/Pannel"; export default { components: { Pannel, }, }; </script>
==v-slot可以简化成#使用==
v-bind可以省略成: v-on: 可以省略成@ 那么v-slot: 可以简化成#
总结: slot的name属性起插槽名, 使用组件时, template配合#插槽名传入具体标签
10.6作用域插槽
目标: 子组件里值, 在给插槽赋值时在父组件环境下使用
复习: 插槽内slot中显示默认内容
例子: 默认内容在子组件中, 但是父亲在给插槽传值, 想要改变插槽显示的默认内容
口诀:
-
子组件, 在slot上绑定属性和子组件内的值
-
使用组件, 传入自定义标签, 用template和v-slot="自定义变量名"
-
scope变量名自动绑定slot上所有属性和值
components/05/Pannel.vue - 定义组件, 和具名插槽, 给slot绑定属性和值
<template> <div> <!-- 按钮标题 --> <div class="title"> <h4>芙蓉楼送辛渐</h4> <span class="btn" @click="isShow = !isShow"> {{ isShow ? "收起" : "展开" }} </span> </div> <!-- 下拉内容 --> <div class="container" v-show="isShow"> <slot :row="defaultObj">{{ defaultObj.defaultOne }}</slot> </div> </div> </template> <script> // 目标: 作用域插槽 // 场景: 使用插槽, 使用组件内的变量 // 1. slot标签, 自定义属性和内变量关联 // 2. 使用组件, template配合v-slot="变量名" // 变量名会收集slot身上属性和值形成对象 export default { data() { return { isShow: false, defaultObj: { defaultOne: "无名氏", defaultTwo: "小传同学" } }; }, }; </script>
views/05_UseSlot.vue
<template> <div id="container"> <div id="app"> <h3>案例:折叠面板</h3> <Pannel> <!-- 需求: 插槽时, 使用组件内变量 --> <!-- scope变量: {row: defaultObj} --> <template v-slot="scope"> <p>{{ scope.row.defaultTwo }}</p> </template> </Pannel> </div> </div> </template> <script> import Pannel from "../components/05/Pannel"; export default { components: { Pannel, }, }; </script>
总结: 组件内变量绑定在slot上, 然后使用组件v-slot="变量" 变量上就会绑定slot身上属性和值
作用域插槽使用场景
总结: 插槽可以自定义标签, 作用域插槽可以把组件内的值取出来自定义内容
11自定义指令
11.1自定义指令注册
目标: 获取标签, 扩展额外的功能
局部注册和使用
<template> <div> <!-- <input type="text" v-gfocus> --> <input type="text" v-focus> </div> </template> <script> // 目标: 创建 "自定义指令", 让输入框自动聚焦 // 1. 创建自定义指令 // 全局 / 局部 // 2. 在标签上使用自定义指令 v-指令名 // 注意: // inserted方法 - 指令所在标签, 被插入到网页上触发(一次) // update方法 - 指令对应数据/标签更新时, 此方法执行 export default { data(){ return { colorStr: 'red' } }, directives: { focus: { inserted(el){ el.focus() } } } } </script> <style> </style>
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
-
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 -
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 -
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
全局注册
以后随便哪个.vue文件里都可以直接用v-(指令名)指令
需要在mian.js中注册
// 全局指令 - 到处"直接"使用 Vue.directive("gfocus", { inserted(el) { el.focus() // 触发标签的事件方法 } })
11.2自定义传值
需求: 定义color指令-传入一个颜色, 给标签设置文字颜色
main.js定义处修改一下
// 目标: 自定义指令传值 Vue.directive('color', { inserted(el, binding) { el.style.color = binding.value }, update(el, binding) { el.style.color = binding.value } })
Direct.vue处更改一下
<p v-color="colorStr" @click="changeColor">修改文字颜色</p> <script> data() { return { theColor: "red", }; }, methods: { changeColor() { this.theColor = 'blue'; }, }, </script>
12.vue路由
12.1路由器的基本介绍
12.1.1什么是路由
前端路由的意义是:路径和组件的映射关系
12.1.2为什么使用路由
单页面应用(SPA): 所有功能在一个html页面上实现
前端路由作用: 实现业务场景切换
优点:
-
整体不刷新页面,用户体验更好
-
数据传递容易, 开发效率高
缺点:
-
开发成本高(需要学习专门知识)
-
首次加载会比较慢一点。不利于seo.
12.1.3vue路由组件 vue-router
vue-router模块包
它和 Vue.js 深度集成
12.1.4路由的分类(经验分类 非官方分类)
src/views(或pages) 文件夹 和 src/components文件夹
-
页面组件 - 页面展示 - 配合路由用
-
复用组件 - 展示数据/常用于复用
总结: views下的页面组件, 配合路由切换, components下的一般引入到views下的vue中复用展示数据
12.1.5vue-router的使用方法
1.下载 vue-router 命令: npm i vue-router 2.src下新建文件 router 文件夹中新建文件 index.js 3.导入文件vue-router 与vue import Vue from 'vue' import VueRouter from 'vue-router' 4.让router与vue有关系 注册vue-router Vue.use(VueRouter) 5.设置匹配规则 path(路径)===组件 (自己常写内容) const routes=[ { path:'/路径' component:'引入的组件名称' } ] 6.实例化router const router = new VueRouter({ routes, //将匹配规则导入 }) 7.将router导出 在main.js中引入 export default router 8.mian.js中 在vue的实例对象中挂载router对象 new Vue({ router,//挂载 .....其他代码 }) 9.将components换成router-view
12.2.vue路由-声明式导航
vue-router提供了一个全局组件 router-link 一般用于代替a标签作为转跳使用
router-link实质上最终会渲染成a链接 to属性等价于提供 href属性(to无需#)
router-link提供了声明式导航高亮的功能(自带类名)
常见写法 <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> 点击高亮的类名与样式 <style scoped> /* 省略了 其他样式 */ .footer_wrap .router-link-active{ color: white; background: black; } </style>
总结: 链接导航, 用router-link配合to, 实现点击切换路由
12.2.1声明式导航 - 跳转传参
目标: 在跳转路由时, 可以给路由对应的组件内传值
router-link上的to属性传值,语法格式如下
-
/path?参数名称=值
-
/path/值-需要路由的对象提前配置path:'/path/参数名称'
对应页面组件接收传递过来的值
-
$route.query.参数名
-
$route.params.参数名
<template> <div> <p>关注明星</p> <p>发现精彩</p> <p>寻找伙伴</p> <p>加入我们</p> <p>人名: {{ $route.query.name }} -- {{ $route.params.username }}</p> </div> </template>
路由定义
{ path: "/part", component: Part }, { path: "/part/:username",
// 有:的路径代表要接收具体的值 component: Part },
3导航转跳
<router-link to="/part?name=小传">朋友-小传</router-link> <router-link to="/part/小智">朋友-小智</router-link>
总结:
?key=value 用$route.query.key 取值
/值 提前在路由规则/path/:key 用$route.params.key 取值
还有第三种模式
可以再路由定义中添加属性porps
{ path: "/part/:username", // 有:的路径代表要接收具体的值 component: Part porps:true },
然后再到被传值的页面中声明
porps:{username}
便可以和父传子一样 调用porps中的参数
12.3vue路由的-重定向和模式
12.3.1 路由的重新定向
目标: 匹配path后, 强制切换到目标path上
-
网页打开url默认hash值是/路径
-
redirect是设置要重定向到哪个路由路径
在路由规则的第一行写上
const routes=[ { path:'/' //默认进入的哈希路径就是/ redirect:'/find' //重定向到/find 这个find可以随意更改看你需要什么 } ]
总结: 强制重定向后, 还会重新来数组里匹配一次规则
12.3.2路由-404页面
目标: 如果路由hash值, 没有和数组里规则匹配
1.创建404页面
2.修改路由配置
import NotFound from '文件的路径' const routes =[ //..省略了其他配置 //404的路径配置必须在数组的最后的最后面 { path:'*' //* 代表是选择所有路径 所以如果写在前面了 所有的页面都会报404错误 component:NotFount } ]
总结: 如果路由未命中任何规则, 给出一个兜底的404页面
12.4编程式导航
12.4.1基础使用
目标: 用JS代码来进行跳转
语法:
this.$router.push({ path: "路由路径", // 都去 router/index.js定义 name: "路由名" })
main.js - 路由数组里, 给路由起名字
{ path: "/find", name: "Find", component: Find }, { path: "/my", name: "My", component: My }, { path: "/part", name: "Part", component: Part },
App.vue - 换成span 配合js的编程式导航跳转
<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, targetName){ // 方式1: path跳转 this.$router.push({ // path: targetPath, name: targetName }) } } }; </script>
12.4.2 编程式导航 - 跳转传参
目标: JS跳转路由, 传参
语法 query / params 任选 一个
this.$router.push({ path: "路由路径" name: "路由名", query: { "参数名": 值 } params: { "参数名": 值 } }) // 对应路由接收 $route.params.参数名 取值 // 对应路由接收 $route.query.参数名 取值
==格外注意: 使用path会自动忽略params==
App.vue
<template> <div> <div class="footer_wrap"> <span @click="btn('/find', 'Find')">发现音乐</span> <span @click="btn('/my', 'My')">我的音乐</span> <span @click="oneBtn">朋友-小传</span> <span @click="twoBtn">朋友-小智</span> </div> <div class="top"> <router-view></router-view> </div> </div> </template> <script> // 目标: 编程式导航 - 跳转路由传参 // 方式1: // params => $route.params.参数名 // 方式2: // query => $route.query.参数名 // 重要: path会自动忽略params // 推荐: name+query方式传参 // 注意: 如果当前url上"hash值和?参数"与你要跳转到的"hash值和?参数"一致, 爆出冗余导航的问题, 不会跳转路由 export default { methods: { btn(targetPath, targetName){ // 方式1: path跳转 this.$router.push({ // path: targetPath, name: targetName }) }, oneBtn(){ this.$router.push({ name: 'Part', params: { username: '小传' } }) }, twoBtn(){ this.$router.push({ name: 'Part', query: { name: '小智' } }) } } }; </script>
总结: 传参2种方式
query方式
params方式
12.5 VUE路由的嵌套和守卫
12.5.1vue-路由的路由嵌套(重点)
目标:现在的一级路由之下 ,在嵌套二级路由
router-view嵌套架构图
-
创建需要用的所有组件
src/views/Find.vue -- 发现音乐页
src/views/My.vue -- 我的音乐页
src/views/Second/Recommend.vue -- 发现音乐页 / 推荐页面
src/views/Second/Ranking.vue -- 发现音乐页 / 排行榜页面
src/views/Second/SongList.vue -- 发现音乐页 / 歌单页面
-
main.js– 继续配置2级路由
一级路由path从/开始定义
二级路由往后path直接写名字, 无需/开头
嵌套路由在上级路由的children数组里编写路由信息对象
-
说明:
App.vue的router-view负责发现音乐和我的音乐页面, 切换
Find.vue的的router-view负责发现音乐下的, 三个页面, 切换
配置二级导航和样式(==可直接复制==) - 在Find.vue中
<template> <div> <!-- <p>推荐</p> <p>排行榜</p> <p>歌单</p> --> <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> </div> </template> <script> export default {}; </script> <style scoped> .nav_main { background-color: red; color: white; padding: 10px 0; } .nav_main a { text-align: center; text-decoration: none; color: white; font-size: 12px; margin: 7px 17px 0; padding: 0px 15px 2px 15px; height: 20px; display: inline-block; line-height: 20px; border-radius: 20px; } .nav_main a:hover { background-color: brown; } .nav_main .router-link-active{ background-color: brown; } </style>
配置路由规则-二级路由展示
const routes = [ // ...省略其他 { path: "/find", name: "Find", component: Find, children: [ { path: "recommend", component: Recommend }, { path: "ranking", component: Ranking }, { path: "songlist", component: SongList } ] } // ...省略其他 ]
-
说明:
-
App.vue, 外层的router-view负责发现音乐和我的音乐页面切换
-
Find.vue 内层的router-view负责发现音乐下的子tab对应的组件切换
-
运行 - 点击导航观察嵌套路由在哪里展示
总结: 嵌套路由, 找准在哪个页面里写router-view和对应规则里写children
12.5.2 声明导航 - 类名区别
目标: router-link自带的2个类名的区别是什么
观察路由嵌套导航的样式
-
router-link-exact-active (精确匹配) url中hash值路径, 与href属性值完全相同, 设置此类名
-
router-link-active (模糊匹配) url中hash值, 包含href属性值这个路径
12.5.3 全局前置守卫
目标:在路由转跳之前,先执行一次前置守卫函数,判断是否可以正常转跳
使用例子:在转跳路由之前,判断用户是否满足了某些条件,如果满足才允许转跳 若未满足则弹出警告
1.在路由对象上使用固定方法beforeEach()
//目标:路由守卫 //场景:当你要对路由的权限判断时 //语法(固定):router.beforeEach((to,from,next)=>{ 路由执行之前先执行这里 如果满足转跳 }) //参数1:to 是要转跳到哪里去 //参数2:from是从哪里来 //参数3:next是函数体 next()是进行正常的转跳 next(false)是停留在原地 next('其他转跳地址是强行转跳到该处') //如果没有写next 不会进行转跳而是停留在原地 //例子:判断用户是否登录,是否决定转跳 const isLogin = true; // 登录状态(未登录) router.beforeEach((to, from, next) => { if (to.path === "/my" && isLogin === false) { alert("请登录") next(false) // 阻止路由跳转 } else { next() // 正常放行 } })
总结: next()放行, next(false)留在原地不跳转路由, next(path路径)强制换成对应path路径跳转