概念
什么是vue
Vue (读音 /vjuː/,类似于 view) 是一套 **构建用户界面 ** 的 渐进式 框架
构建用户界面
基于数据渲染出用户可以看到的界面
渐进式
所谓渐进式就是循序渐进,不一定非得把Vue中的所有API都学完才能开发Vue,可以学一点开发一点
框架
所谓框架:就是一套完整的解决方案
提到框架,不得不提一下库。
- 库,类似工具箱,是一堆方法的集合,比如 axios、lodash、echarts等
- 框架,是一套完整的解决方案,实现了大部分功能,我们只需要按照一定的规则去编码即可。
下图是 库 和 框架的对比。
框架的特点:有一套必须让开发者遵守的规则或者约束
响应式
简单理解就是数据变,视图对应变。
data中的数据, 最终会被添加到实例上:
① 访问数据: “实例.属性名”
② 修改数据: “实例.属性名”= “值”
基础命令
Vue的两种开发方式:
Vue核心包开发
场景:局部模块改造
引入vue.js文件,进行开发。
Vue核心包&Vue插件&工程化
场景:整站开发
使用vue-cli等工具创建一个vue项目,进行开发。
helloworld
创建index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <div id="app"> {{msg}} </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script> const app = new Vue({ el:"#app", data: { msg: "hello world" } }) </script>
|
- 准备容器(div)
- 引包(官网) — 开发版本/生产版本(js文件)
- 创建Vue实例 new Vue()
- 指定配置项,渲染数据
- el:指定挂载点
- data提供数据
插值表达式
插值表达式是一种Vue的模板语法。
插值表达式语法:
表达式:是可以被求值的代码,JS引擎会讲其计算出一个结果
表达式可以是:
1 2 3 4 5 6 7 8 9
| money + 100 money - 100 money * 10 money / 10 price >= 100 ? '真贵':'还行' obj.name arr[0] fn() obj.fn()
|
错误用法:
1 2 3 4 5 6 7 8
| 1.在插值表达式中使用的数据 必须在data中进行了提供 <p>{{hobby}}</p> //如果在data中不存在 则会报错
2.支持的是表达式,而非语句,比如:if for ... <p>{{if}}</p>
3.不能在标签属性中使用 {{ }} 插值 (插值表达式只能标签中间使用) <p title="{{username}}">我是P标签</p>
|
Vue开发者工具安装
Chorme插件 搜索 vue devtools
- 通过谷歌应用商店安装(国外网站)
- 极简插件下载(推荐) https://chrome.zzzmh.cn/index
指令
指令(Directives)是 Vue 提供的带有 v- 前缀 的 特殊 标签属性。
指令是 vue 开发中最基础、最常用、最简单的知识点。
内容渲染指令
内容渲染指令用来辅助开发者渲染 DOM 元素的文本内容。常用的内容渲染指令有如下2 个:
v-text(类似innerText)
- 使用语法:
<p v-text="uname">hello</p>
,意思是将 uame 值渲染到 p 标签中
- 类似 innerText,使用该语法,会覆盖 p 标签原有内容
v-html(类似 innerHTML)
- 使用语法:
<p v-html="intro">hello</p>
,意思是将 intro 值渲染到 p 标签中
- 类似 innerHTML,使用该语法,会覆盖 p 标签原有内容
- 类似 innerHTML,使用该语法,能够将HTML标签的样式呈现出来。
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <div id="app"> <h2>个人信息</h2> // 既然指令是vue提供的特殊的html属性,所以咱们写的时候就当成属性来用即可 <p v-text="uname">姓名:</p> <p v-html="intro">简介:</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el:'#app', data:{ uname:'张三', intro:'<h2>这是一个<strong>非常优秀</strong>的boy<h2>' } }) </script>
|
条件渲染指令
条件判断指令,用来辅助开发者按需控制 DOM 的显示与隐藏。条件渲染指令有如下两个,分别是:
v-show
- 作用: 控制元素显示隐藏
- 语法: v-show = “表达式” 表达式值为 true 显示, false 隐藏
- 原理: 切换 display:none 控制显示隐藏
- 场景:频繁切换显示隐藏的场景
v-if
- 作用: 控制元素显示隐藏(条件渲染)
- 语法: v-if= “表达式” 表达式值 true显示, false 隐藏
- 原理: 基于条件判断,是否创建 或 移除元素节点
- 场景: 要么显示,要么隐藏,不频繁切换的场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <div id="app"> <div v-show="flag" class="box">我是v-show控制的盒子</div> <div v-if="flag" class="box">我是v-if控制的盒子</div> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { flag: false } }) </script>
|
v-else 和 v-else-if
- 作用:辅助v-if进行判断渲染
- 语法:v-else v-else-if=”表达式”
- 需要紧接着v-if使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <div id="app"> <p v-if="gender==1">性别:♂ 男</p> <p v-else>性别:♀ 女</p> <hr> <p v-if="score>90">成绩评定A:奖励电脑一台</p> <p v-else-if="score>80">成绩评定B:奖励周末郊游</p> <p v-else-if="score>60">成绩评定C:奖励零食礼包</p> <p v-else>成绩评定D:惩罚一周不能玩手机</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script>
const app = new Vue({ el: '#app', data: { gender: 2, score: 95 } }) </script>
|
事件绑定指令
使用Vue时,如需为DOM注册事件,及其的简单,语法如下:
- <button v-on:事件名=”内联语句”>按钮
- <button v-on:事件名=”处理函数”>按钮
- <button v-on:事件名=”处理函数(实参)”>按钮
v-on:
简写为 @
内联语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <div id="app"> <button @click="count--">-</button> <span>{{ count }}</span> <button v-on:click="count++">+</button> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { count: 100 } }) </script>
|
事件处理函数:
注意:
- 事件处理函数应该写到一个跟data同级的配置项(methods)中
- methods中的函数内部的this都指向Vue实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <div id="app"> <button @click="changeShow" >切换显示隐藏</button> <h1 v-show="isShow">黑马程序员</h1> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { isShow: true }, methods: { changeShow() { this.isShow = !this.isShow; } } }) </script>
|
给事件处理函数传参:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <style> .box { border: 3px solid #000000; border-radius: 10px; padding: 20px; margin: 20px; width: 200px; } h3 { margin: 10px 0 20px 0; } p { margin: 20px; } </style>
<div id="app"> <div class="box"> <h3>小黑自动售货机</h3> <button @click="buy(5)">可乐5元</button> <button @click="buy(10)">咖啡10元</button> <button @click="buy(8)">牛奶8元</button> </div> <p>银行卡余额:{{ money }}元</p> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { money: 100 }, methods: { buy(itemMoney) { this.money = this.money - itemMoney } } }) </script>
|
属性绑定指令
- 作用:动态设置html的标签属性 比如:src、url、title
- 语法:**v-bind:**属性名=“表达式”
- **v-bind:**可以简写成 => :
比如,有一个图片,它的 src
属性值,是一个图片地址。这个地址在数据 data 中存储。
则可以这样设置属性值:
<img v-bind:src="url" />
<img :src="url" />
(v-bind可以省略)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <div id="app"> <img v-bind:src="imgUrl" v-bind:title="msg" alt=""> <img :src="imgUrl" :title="msg" alt=""> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { imgUrl: './imgs/10-02.png', msg: 'hello 波仔' } }) </script>
|
列表渲染指令
Vue 提供了 v-for 列表渲染指令,用来辅助开发者基于一个数组来循环渲染一个列表结构。
v-for 指令需要使用 (item, index) in arr
形式的特殊语法,其中:
- item 是数组中的每一项
- index 是每一项的索引,不需要可以省略
- arr 是被遍历的数组
此语法也可以遍历对象和数字
1 2 3 4 5 6 7 8 9
| //遍历对象 <div v-for="(value, key, index) in object">{{value}}</div> value:对象中的值 key:对象中的键 index:遍历索引从0开始
//遍历数字 <p v-for="item in 10">{{item}}</p> item从1 开始
|
v-for中的key
语法: key=”唯一值”
作用:给列表项添加的唯一标识。便于Vue进行列表项的正确排序复用。
为什么加key:Vue 的默认行为会尝试原地修改元素(就地复用)
实例代码:
1 2 3 4 5 6 7
| <ul> <li v-for="(item, index) in booksList" :key="item.id"> <span>{{ item.name }}</span> <span>{{ item.author }}</span> <button @click="del(item.id)">删除</button> </li> </ul>
|
注意:
- key 的值只能是字符串 或 数字类型
- key 的值必须具有唯一性
- 推荐使用 id 作为 key(唯一),不推荐使用 index 作为 key(会变化,不对应)
双向绑定指令
所谓双向绑定就是:
- 数据改变后,呈现的页面结果会更新
- 页面结果更新后,数据也会随之而变
作用: 给表单元素(input、radio、select)使用,双向绑定数据,可以快速 获取 或 设置 表单元素内容
语法:v-model=”变量”
需求:使用双向绑定实现以下需求
- 点击登录按钮获取表单中的内容
- 点击重置按钮清空表单中的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <div id="app"> 账户:<input v-model="username" type="text"> <br><br> 密码:<input v-model="password" type="password"> <br><br> <button @click="login()">登录</button> <button @click="reset()">重置</button> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { username: '', password: '' }, methods: { login(){ console.info(this.username, this.password) }, reset(){ this.username = '' this.password = '' } } }) </script>
|
指令修饰符
所谓指令修饰符就是通过“.”指明一些指令后缀 不同的后缀封装了不同的处理操作 —> 简化代码
按键修饰符
@keyup.enter —>当点击enter键的时候才触发
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <div id="app"> <h3>@keyup.enter → 监听键盘回车事件</h3> <input @keyup.enter="getUsername" v-model="username" type="text"> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { username: '' }, methods: { getUsername() { console.info(this.username) } } }) </script>
|
v-model修饰符
- v-model.trim —>去除首位空格
- v-model.number —>转数字
事件修饰符
- @事件名.stop —> 阻止冒泡
- @事件名.prevent —>阻止默认行为
- @事件名.stop.prevent —>可以连用 即阻止事件冒泡也阻止默认行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| <style> .father { width: 200px; height: 200px; background-color: pink; margin-top: 20px; } .son { width: 100px; height: 100px; background-color: skyblue; } </style>
<div id="app"> <h3>v-model修饰符 .trim .number</h3> 姓名:<input v-model.trim="username" type="text"><br> 年纪:<input v-model.number="age" type="text"><br>
<button @click="getForm">获取</button>
<h3>@事件名.stop → 阻止冒泡</h3> <div @click="fatherFn" class="father"> <div @click.stop="sonFn" class="son">儿子</div> </div>
<h3>@事件名.prevent → 阻止默认行为</h3> <a @click.prevent href="http://www.baidu.com">阻止默认行为</a> </div>
<script src="vue.js"></script> <script> const app = new Vue({ el: '#app', data: { username: '', age: '', }, methods: { getForm() { console.info("###" + this.username + "###") console.info("###" + this.age + "###") }, fatherFn () { alert('老父亲被点击了') }, sonFn (e) { alert('儿子被点击了') } } }) </script>
|
v-bind对样式控制的增强-操作class
为了方便开发者进行样式控制, Vue 扩展了 v-bind 的语法,可以针对 class 类名 和 style 行内样式 进行控制 。
语法:
1
| <div :class = "对象/数组">这是一个div</div>
|
对象语法:
当class动态绑定的是对象时,键就是类名,值就是布尔值,如果值是true,就有这个类,否则没有这个类
1
| <div class="box" :class="{ 类名1: 布尔值, 类名2: 布尔值 }"></div>
|
适用场景:一个类名,来回切换
数组语法:
当class动态绑定的是数组时 → 数组中所有的类,都会添加到盒子上,本质就是一个 class 列表
1
| <div class="box" :class="[ 类名1, 类名2, 类名3 ]"></div>
|
使用场景:批量添加或删除类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <style> .box { width: 200px; height: 200px; border: 3px solid #000; font-size: 30px; margin-top: 10px; } .pink { background-color: pink; } .big { width: 300px; height: 300px; } </style>
<div id="app"> <div :class="boxobj">黑马程序员</div> <div :class="boxarr">黑马程序员</div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { boxobj: { "pink" : true, "big" : false }, boxarr: ["pink", "box"] } }) </script>
|
v-bind对有样式控制的增强-操作style
语法:
1
| <div class="box" :style="{ CSS属性名1: CSS属性值, CSS属性名2: CSS属性值 }"></div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <style> .box { width: 200px; height: 200px; background-color: rgb(187, 150, 156); } </style> <div id="app"> <div class="box" :style="styleobj">xxxxx</div> </div> <script src="vue.js"></script> <script> const app = new Vue({ el: '#app', data: { styleobj:{ "color": "red", "opacity": 0.5 } } }) </script>
|
v-model在其他表单元素的使用
常见的表单元素都可以用 v-model 绑定关联 → 快速 获取 或 设置 表单元素的值
它会根据 控件类型 自动选取 正确的方法 来更新元素
1 2 3 4 5
| 输入框 input:text ——> value 文本域 textarea ——> value 复选框 input:checkbox ——> checked 单选框 input:radio ——> checked 下拉菜单 select ——> value
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| <style> textarea { display: block; width: 240px; height: 100px; margin: 10px 0; } </style> <div id="app"> <h3>小黑学习网</h3> 姓名: <input v-model="username" type="text"> <br><br> 是否单身: <input v-model="single" type="checkbox"> <br><br> 性别: <input v-model="gender" type="radio" value="男">男 <input v-model="gender" type="radio" value="女">女 <br><br> 所在城市: <select v-model="city"> <option value="北京">北京</option> <option value="上海">上海</option> <option value="成都">成都</option> <option value="南京">南京</option> </select> <br><br> 自我描述: <textarea v-model="description"></textarea> <button @click="register">立即注册</button> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2"></script> <script> const app = new Vue({ el: '#app', data: { username: '', single: null, gender: null, city: null, description: '' }, methods: { register() { console.info(this.username, this.single, this.gender, this.city, this.description) } } }) </script>
|
computed计算属性
概念
基于现有的数据,计算出来的新属性。 依赖的数据变化,自动重新计算。
语法
- 声明在 computed 配置项中,一个计算属性对应一个函数
- 使用起来和普通属性一样使用
注意
- computed配置项和data配置项是同级的
- computed中的计算属性虽然是函数的写法,但他依然是个属性
- computed中的计算属性不能和data中的属性同名
- 使用computed中的计算属性和使用data中的属性是一样的用法
- computed中计算属性内部的this依然指向的是Vue实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <div id="app"> <h3>小黑的礼物清单</h3> <table> <tr> <th>名字</th> <th>数量</th> </tr> <tr v-for="(item, index) in list" :key="item.id"> <td>{{ item.name }}</td> <td>{{ item.num }}个</td> </tr> </table> <p>礼物总数:{{ totalGifts }} 个</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { list: [ { id: 1, name: '篮球', num: 1 }, { id: 2, name: '玩具', num: 2 }, { id: 3, name: '铅笔', num: 5 }, ] }, computed: { totalGifts: function () { return this.list.reduce((total, item) => total + item.num, 0); } } }) </script>
|
computed计算属性 VS methods方法
1.computed计算属性
作用:封装了一段对于数据的处理,求得一个结果
语法:
- 写在computed配置项中
- 作为属性,直接使用
- js中使用计算属性: this.计算属性
- 模板中使用计算属性:
2.methods计算属性
作用:给Vue实例提供一个方法,调用以处理业务逻辑。
语法:
1 2 3 4
| 1. 写在methods配置项中 2. 作为方法调用 - js中调用:this.方法名() - 模板中调用 {{方法名()}} 或者 @事件名=“方法名”
|
3.计算属性的优势
缓存特性(提升性能)
计算属性会对计算出来的结果缓存,再次使用直接读取缓存,
依赖项变化了,会自动重新计算 → 并再次缓存
methods没有缓存特性
4.总结
1.computed有缓存特性,methods没有缓存
2.当一个结果依赖其他多个值时,推荐使用计算属性
3.当处理业务逻辑时,推荐使用methods方法,比如事件的处理函数
计算属性的完整写法之getter、setter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <div id="app"> 姓:<input type="text" v-model="firstName"> + 名:<input type="text" v-model="lastName"> = <span>{{fullName}}</span><br><br> <button @click="fullName='张 三'">改名卡</button> </div> <script src="vue.js"></script> <script> const app = new Vue({ el: '#app', data: { firstName: '刘', lastName: '备' }, computed: { fullName: { get() { return this.firstName + ' ' + this.lastName; }, set(newValue) { const names = newValue.split(' '); this.firstName = names[0]; this.lastName = names.slice(1).join(' '); } } },
}) </script>
|
watch侦听器(监视器)
1.作用:
监视数据变化,执行一些业务逻辑或异步操作
2.语法:
watch同样声明在跟data同级的配置项中
简单写法: 简单类型数据直接监视
完整写法:添加额外配置项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| data: { words: '苹果', obj: { words: '苹果' } },
watch: { 数据属性名 (newValue, oldValue) { 一些业务逻辑 或 异步操作。 }, '对象.属性名' (newValue, oldValue) { 一些业务逻辑 或 异步操作。 } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
| <style> * { margin: 0; padding: 0; box-sizing: border-box; font-size: 18px; } #app { padding: 10px 20px; } .query { margin: 10px 0; } .box { display: flex; } textarea { width: 300px; height: 160px; font-size: 18px; border: 1px solid #dedede; outline: none; resize: none; padding: 10px; } textarea:hover { border: 1px solid #1589f5; } .transbox { width: 300px; height: 160px; background-color: #f0f0f0; padding: 10px; border: none; } .tip-box { width: 300px; height: 25px; line-height: 25px; display: flex; } .tip-box span { flex: 1; text-align: center; } .query span { font-size: 18px; }
.input-wrap { position: relative; } .input-wrap span { position: absolute; right: 15px; bottom: 15px; font-size: 12px; } .input-wrap i { font-size: 20px; font-style: normal; } </style>
<div id="app"> <div class="query"> <span>翻译成的语言:</span> <select> <option value="english">英语</option> <option value="italy">意大利</option> <option value="german">德语</option> </select> </div>
<div class="box"> <div class="input-wrap"> <textarea v-model="obj.words"></textarea> <span><i>⌨️</i>文档翻译</span> </div> <div class="output-wrap"> <div class="transbox">{{result}}</div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script> const app = new Vue({ el: '#app', data: { obj: { words: '' }, lang: 'english', result: '', }, watch: {
'obj.words' (newValue) { clearTimeout(this.timer) this.timer = setTimeout(async () => { const res = await axios({ url: 'https://applet-base-api-t.itheima.net/api/translate', params: { 'lang': this.lang, 'words': newValue } }) this.result = res.data.data console.log(res.data.data) }, 300) } } }) </script>
|
完整写法及添加额外的配置项
- deep:true 对复杂类型进行深度监听
- immdiate:true 初始化 立刻执行一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| data: { obj: { words: '苹果', lang: 'italy' }, },
watch: { 对象: { deep: true, immdiate:true, handler (newValue) { console.log(newValue) } } }
|
Vue生命周期
什么时候可以发送初始化渲染请求?(越早越好)什么时候可以开始操作dom?(至少dom渲染出来才能操作)
Vue生命周期:就是一个Vue实例从创建 到 销毁 的整个过程。
生命周期四个阶段:① 创建 ② 挂载 ③ 更新 ④ 销毁
1.创建阶段:创建响应式数据
2.挂载阶段:渲染模板
3.更新阶段:修改数据,更新视图
4.销毁阶段:销毁Vue实例
Vue生命周期钩子
Vue生命周期过程中,会自动运行一些函数,被称为【生命周期钩子】→ 让开发者可以在【特定阶段】运行自己的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <div id="app"> <h3>{{ title }}</h3> <div> <button @click="count--">-</button> <span>{{ count }}</span> <button @click="count++">+</button> </div> </div> <script src="vue.js"></script> <script> const app = new Vue({ el: '#app', data: { count: 100, title: '计数器' }, beforeCreate: function () { console.info("beforeCreate") }, created: function () { console.info("created") }, beforeMount: function () { console.info("beforeMount") }, mounted: function () { console.info("mounted") }, beforeUpdate: function () { console.info("beforeUpdate") }, updated: function () { console.info("updated") }, beforeDestroy: function () { console.info("beforeDestroy") } }) </script>
|
vue工程式开发
上面都是html中引入js进行开发,核心包传统开发模式。
- 核心包传统开发模式:基于html / css / js 文件,直接引入核心包,开发 Vue。
- 工程化开发模式:基于构建工具(例如:webpack)的环境中开发Vue。
工程化开发模式优点:
提高编码效率,比如使用JS新语法、Less/Sass、Typescript等通过webpack都可以编译成浏览器识别的ES3/ES5/CSS等
工程化开发模式问题:
- webpack配置不简单
- 雷同的基础配置
- 缺乏统一的标准
为了解决以上问题,所以我们需要一个工具,生成标准化的配置
脚手架Vue CLI
基本介绍:
Vue CLI 是Vue官方提供的一个全局命令工具
可以帮助我们快速创建一个开发Vue项目的标准化基础架子。【集成了webpack配置】
好处:
- 开箱即用,零配置
- 内置babel等工具
- 标准化的webpack配置
使用步骤:
- 全局安装(只需安装一次即可) yarn global add @vue/cli 或者 npm i @vue/cli -g
- 查看vue/cli版本: vue –version
- 创建项目架子:vue create project-name(项目名不能使用中文)
- 启动项目:yarn serve 或者 npm run serve(命令不固定,找package.json)
VueCli 默认模板项目
1 2 3 4 5
| Vue CLI v5.0.8 ? Please pick a preset: Default ([Vue 3] babel, eslint) > Default ([Vue 2] babel, eslint) // vue2 模板项目 Manually select features
|
VueCli 自定义创建项目
1 2 3 4 5
| Vue CLI v5.0.8 ? Please pick a preset: Default ([Vue 3] babel, eslint) Default ([Vue 2] babel, eslint) > Manually select features 选自定义
|
手动选择功能:
babel = es6 转 es3
css processors = less / scss
linter formter = eslint
选择vue的版本: 2.x
是否使用history模式: n
选择css预处理: 自行选择
选择eslint的风格: 自行选择
选择校验的时机: lint on save
选择配置文件的生成方式: in dedicated config files 单独配置文件
是否保存预设,下次直接使用?: n 不保存
项目目录介绍和运行流程
1.项目目录介绍
2.运行流程
组件化开发
组件化:一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为。
好处:便于维护,利于复用 → 提升开发效率。
组件分类:普通组件、根组件。
比如:下面这个页面,可以把所有的代码都写在一个页面中,但是这样显得代码比较混乱,难易维护。咱们可以按模块进行组件划分
根组件 App.vue
1.根组件介绍
整个应用最上层的组件,包裹所有普通小组件
2.组件是由三部分构成
普通组件的注册使用
局部注册
1.特点:
只能在注册的组件内使用
2.步骤:
- 创建.vue文件(三个组成部分)
- 在使用的组件内先导入再注册,最后使用
3.使用方式:
当成html标签使用即可 <组件名></组件名>
4.注意:
组件名规范 —> 大驼峰命名法, 如 HmHeader
5.语法:
1 2 3 4 5 6 7 8 9 10 11
| import 组件对象 from '.vue文件路径' import HmHeader from './components/HmHeader'
export default { components: { '组件名': 组件对象, HmHeader:HmHeaer, HmHeader } }
|
全局注册
1.特点:
全局注册的组件,在项目的任何组件中都能使用
2.步骤
- 创建.vue组件(三个组成部分)
- main.js中进行全局注册
3.使用方式
当成HTML标签直接使用
<组件名></组件名>
4.注意
组件名规范 —> 大驼峰命名法, 如 HmHeader
5.语法
Vue.component(‘组件名’, 组件对象)
例:
1 2 3
| import HmButton from './components/HmButton' Vue.component('HmButton', HmButton)
|
scoped属性
1.默认情况:
写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。
全局样式: 默认组件中的样式会作用到全局,任何一个组件中都会受到此样式的影响
局部样式: 可以给组件加上scoped 属性,可以让样式只作用于当前组件
2.scoped原理
- 当前组件内标签都被添加data-v-hash值 的属性
- css选择器都被添加 [data-v-hash值] 的属性选择器
最终效果: 必须是当前组件的元素, 才会有这个自定义属性, 才会被这个样式作用到
data必须是一个函数
1、data为什么要写成函数
一个组件的 data 选项必须是一个函数。目的是为了:保证每个组件实例,维护独立的一份数据对象。
每次创建新的组件实例,都会新执行一次data 函数,得到一个新对象。
组件通信
1.什么是组件通信?
组件通信,就是指组件与组件之间的数据传递
- 组件的数据是独立的,无法直接访问其他组件的数据。
- 想使用其他组件的数据,就需要组件通信
2.组件关系分类
- 父子关系
- 非父子关系
3.解决方案
父子关系可以使用 props 和 $emit
非父子关系可以使用 provide 和 inject 或者 evenbus
通用通信组件:vuex
父子通信
- 父组件通过 props 将数据传递给子组件
- 子组件利用 $emit 通知父组件修改更新
父向子通信代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <div class="app" style="border: 3px solid #000; margin: 10px"> 我是APP组件 <Son :title="myTitle"></Son> </div> </template>
<script> import Son from './components/Son.vue' export default { name: 'App', data() { return { myTitle: '学前端,就来黑马程序员', } }, components: { Son, }, } </script>
<style> </style>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <template> <div class="son" style="border:3px solid #000;margin:10px"> 我是Son组件-{{title}} </div> </template> <script> export default { name: 'Son-Child', props: ['title'] } </script> <style> </style>
|
子向父通信代码
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <template> <div class="app" style="border: 3px solid #000; margin: 10px"> 我是APP组件 -{{msg}} <Son @changeFMsg="handlerMsg"></Son> </div> </template>
<script> import Son from './components/Son.vue' export default { name: 'App', data() { return { msg: '学前端,就来黑马程序员', } }, components: { Son, }, methods: { handlerMsg: function(msg) { this.msg = msg } } } </script>
<style> </style>
|
Son.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <div class="son" style="border:3px solid #000;margin:10px"> 我是Son组件 <button @click="changeMsg">修改标题</button> </div> </template>
<script> export default { name: 'Son-Child', methods: { changeMsg: function() { this.$emit('changeFMsg', '子修改父') } } } </script>
<style>
</style>
|
props
1.Props 定义
组件上 注册的一些 自定义属性
2.Props 作用
向子组件传递数据
3.特点
- 可以 传递 任意数量 的prop
- 可以 传递 任意类型 的prop
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <template> <div class="app"> <UserInfo :username="username" :age="age" :isSingle="isSingle" :car="car" :hobby="hobby" ></UserInfo> </div> </template>
<script> import UserInfo from './components/UserInfo.vue' export default { data() { return { username: '小帅', age: 28, isSingle: true, car: { brand: '宝马', }, hobby: ['篮球', '足球', '羽毛球'], } }, components: { UserInfo, }, } </script>
<style> </style>
|
UserInfo.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <template> <div class="userinfo"> <h3>我是个人信息组件</h3> <div>姓名:{{ username }}</div> <div>年龄:{{ age }}</div> <div>是否单身:{{ isSingle }}</div> <div>座驾:{{ car }}</div> <div>兴趣爱好:{{ hobby }}</div> </div> </template> <script> export default { name: 'userInfo', props: ['username', 'age', 'isSingle', 'car', 'hobby'] } </script> <style> .userinfo { width: 300px; border: 3px solid #000; padding: 20px; } .userinfo > div { margin: 20px 10px; } </style>
|
props校验
1.作用
为组件的 prop 指定验证要求,不符合要求,控制台就会有错误提示 → 帮助开发者,快速发现错误
2.语法
1 2 3 4 5 6 7 8 9 10 11
| props: { 校验的属性名: { type: 类型, // Number String Boolean ... required: true, // 是否必填 default: 默认值, // 默认值 validator (value) { // 自定义校验逻辑 return 是否通过校验 } } },
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <script> export default { // 完整写法(类型、默认值、非空、自定义校验) props: { w: { type: Number, //required: true, default: 0, validator(val) { // console.log(val) if (val >= 100 || val <= 0) { console.error('传入的范围必须是0-100之间') return false } else { return true } }, }, }, } </script>
|
1.default和required一般不同时写(因为当时必填项时,肯定是有值的)
2.default后面如果是简单类型的值,可以直接写默认。如果是复杂类型的值,则需要以函数的形式return一个默认值
props&data、单向数据流
1.共同点
都可以给组件提供数据
2.区别
- data 的数据是自己的 → 随便改
- prop 的数据是外部的 → 不能直接改,要遵循 单向数据流
3.单向数据流:
父级props 的数据更新,会向下流动,影响子组件。这个数据流动是单向的
非父子通信-event bus 事件总线
1.作用
非父子组件之间,进行简易消息传递。(复杂场景→ Vuex)
2.步骤
创建一个都能访问的事件总线 (空Vue实例)
1 2 3
| import Vue from 'vue' const Bus = new Vue() export default Bus
|
A组件(接受方),监听Bus的 $on事件
1 2 3 4 5
| created () { Bus.$on('sendMsg', (msg) => { this.msg = msg }) }
|
B组件(发送方),触发Bus的$emit事件
1
| Bus.$emit('sendMsg', '这是一个消息')
|
EventBus.js
1 2 3
| import Vue from 'vue' const Bus = new Vue() export default Bus
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <div class="app"> <BaseA></BaseA> <BaseB></BaseB> </div> </template>
<script> import BaseA from './components/BaseA.vue' import BaseB from './components/BaseB.vue' export default { components:{ BaseA, BaseB } } </script>
<style>
</style>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <template> <div class="base-a"> 我是A组件(接收方) <p>{{msg}}</p> </div> </template> <script> import Bus from '../utils/EventBus' export default { data() { return { msg: '', } }, created () { Bus.$on('sendMsg', (msg) => { this.msg = msg }) } } </script> <style scoped> .base-a { width: 200px; height: 200px; border: 3px solid #000; border-radius: 3px; margin: 10px; } </style>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <template> <div class="base-b"> <div>我是B组件(发布方)</div> <button @click="sendMsg">发送消息</button> </div> </template> <script> import Bus from '../utils/EventBus' export default { methods: { sendMsg: function() { Bus.$emit('sendMsg', '这是一个消息') } } } </script> <style scoped> .base-b { width: 200px; height: 200px; border: 3px solid #000; border-radius: 3px; margin: 10px; } </style>
|
非父子通信-provide&inject
1.作用
跨层级共享数据
2.语法
- 父组件 provide提供数据
1 2 3 4 5 6 7 8 9 10
| export default { provide () { return { color: this.color, userInfo: this.userInfo, } } }
|
2.子/孙组件 inject获取数据
1 2 3 4 5 6
| export default { inject: ['color','userInfo'], created () { console.log(this.color, this.userInfo) } }
|
3.注意
- provide提供的简单类型的数据不是响应式的,复杂类型数据是响应式。(推荐提供复杂类型数据)
- 子/孙组件通过inject获取的数据,不能在自身组件内修改
v-model原理
1.原理:
v-model本质上是一个语法糖。例如应用在输入框上,就是value属性 和 input事件 的合写
1 2 3 4 5 6 7
| <template> <div id="app" > <input v-model="msg" type="text">
<input :value="msg" @input="msg = $event.target.value" type="text"> </div> </template>
|
2.作用:
提供数据的双向绑定
- 数据变,视图跟着变 :value
- 视图变,数据跟着变 @input
3.注意
$event 用于在模板中,获取事件的形参
4.代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <div class="app"> <input type="text" /> <br /> <input type="text" /> </div> </template>
<script> export default { data() { return { msg1: '', msg2: '', } }, } </script> <style> </style>
|
5.v-model使用在其他表单元素上的原理
不同的表单元素, v-model在底层的处理机制是不一样的。比如给checkbox使用v-model底层处理的是 checked属性和change事件。
不过咱们只需要掌握应用在文本框上的原理即可
sync修饰符
实现App.vue中的selectId和子组件选中的数据进行双向绑定
就是app.vue中有默认值,向子组件通信,子组件select标签修改选择后向父组件通信。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <template> <div class="app"> 父{{selectId}} <BaseSelect :selectId="selectId" @updateSelectId="handlerSelectId"></BaseSelect> </div> </template>
<script> import BaseSelect from './components/BaseSelect.vue' export default { data() { return { selectId: 102, } }, components: { BaseSelect, }, methods: { handlerSelectId: function(selectId) { this.selectId = selectId } } } </script>
<style> </style>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <template> <div> <select :value="selectId" @change="changeId"> <option value="101">北京</option> <option value="102">上海</option> <option value="103">武汉</option> <option value="104">广州</option> <option value="105">深圳</option> </select> </div> </template> <script> export default { props: { "selectId" : { type: Number, required: true } }, methods: { changeId: function(e) { this.$emit('updateSelectId', e.target.value) } } } </script> <style> </style>
|
简化代码
父组件通过v-model 简化代码,实现子组件和父组件数据 双向绑定
v-model其实就是 :value和@input事件的简写
- 子组件:props通过value接收数据,事件触发 input
- 父组件:v-model直接绑定数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <template> <div class="app"> 父{{selectId}} <BaseSelect :selectId="selectId" v-model="selectId"></BaseSelect> </div> </template>
<script> import BaseSelect from './components/BaseSelect.vue' export default { data() { return { selectId: 102, } }, components: { BaseSelect, } } </script>
<style> </style>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <template> <div> <select :value="selectId" @change="changeId"> <option value="101">北京</option> <option value="102">上海</option> <option value="103">武汉</option> <option value="104">广州</option> <option value="105">深圳</option> </select> </div> </template> <script> export default { props: { "selectId" : { type: Number, required: true } }, methods: { changeId: function(e) { this.$emit('input', e.target.value) } } } </script> <style> </style>
|
sync修饰符
可以实现 子组件 与 父组件数据 的 双向绑定,简化代码
简单理解:子组件可以修改父组件传过来的props值
.sync修饰符 就是 :属性名 和 @update:属性名 合写
语法
父组件
1 2 3 4 5 6 7 8
| //.sync写法 <BaseDialog :visible.sync="isShow" /> -------------------------------------- //完整写法 <BaseDialog :visible="isShow" @update:visible="isShow = $event" />
|
子组件
1 2 3 4 5
| props: { visible: Boolean },
this.$emit('update:visible', false)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <template> <div class="app"> 父{{selectId}} <BaseSelect :selectId.sync="selectId" ></BaseSelect> </div> </template>
<script> import BaseSelect from './components/BaseSelect.vue' export default { data() { return { selectId: 102, } }, components: { BaseSelect, } } </script>
<style> </style>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <template> <div> <select :value="selectId" @change="changeId"> <option value="101">北京</option> <option value="102">上海</option> <option value="103">武汉</option> <option value="104">广州</option> <option value="105">深圳</option> </select> </div> </template> <script> export default { props: { "selectId" : { type: Number, required: true } }, methods: { changeId: function(e) { this.$emit('update:selectId', e.target.value) } } } </script> <style> </style>
|
ref和$refs
1.作用
利用ref 和 $refs 可以用于 获取 dom 元素 或 组件实例
2.特点:
查找范围 → 当前组件内(更精确稳定)
3.语法
1.给要获取的盒子添加ref属性
1
| <div ref="chartRef">我是渲染图表的容器</div>
|
2.获取时通过 $refs获取 this.$refs.chartRef 获取
1 2 3
| mounted () { console.log(this.$refs.chartRef) }
|
4.注意
之前只用document.querySelect(‘.box’) 获取的是整个页面中的盒子
5.代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <div class="app"> <BaseChart></BaseChart> </div> </template>
<script> import BaseChart from './components/BaseChart.vue' export default { components:{ BaseChart } } </script>
<style> </style>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <template> <div class="base-chart-box" ref="baseChartBox">子组件</div> </template> <script> // yarn add echarts 或者 npm i echarts import * as echarts from 'echarts' export default { mounted() { // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(this.$refs.baseChartBox) // 绘制图表 myChart.setOption({ title: { text: 'ECharts 入门示例', }, tooltip: {}, xAxis: { data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'], }, yAxis: {}, series: [ { name: '销量', type: 'bar', data: [5, 20, 36, 10, 10, 20], }, ], }) }, } </script> <style scoped> .base-chart-box { width: 400px; height: 300px; border: 3px solid #000; border-radius: 6px; } </style>
|
异步更新 & $nextTick
1.需求
编辑标题, 编辑框自动聚焦
- 点击编辑,显示编辑框
- 让编辑框,立刻获取焦点
2.代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <template> <div class="app"> <div v-if="isShowEdit"> <input type="text" v-model="editValue" ref="inp" /> <button>确认</button> </div> <div v-else> <span>{{ title }}</span> <button @click="editFn">编辑</button> </div> </div> </template>
<script> export default { data() { return { title: '大标题', isShowEdit: false, editValue: '', } }, methods: { editFn() { // 显示输入框 this.isShowEdit = true // 获取焦点 this.$refs.inp.focus() } }, } </script>
|
3.问题
“显示之后”,立刻获取焦点是不能成功的!
原因:Vue 是异步更新DOM (提升性能)
4.解决方案
$nextTick:等 DOM更新后,才会触发执行此方法里的函数体
语法: this.$nextTick(函数体)
1 2 3
| this.$nextTick(() => { this.$refs.inp.focus() })
|
注意:$nextTick 内的函数体 一定是箭头函数,这样才能让函数内部的this指向Vue实例
自定义指令
自定义指令
1.指令介绍
2.自定义指令
概念:自己定义的指令,可以封装一些DOM操作,扩展额外的功能
3.自定义指令语法
4.指令中的配置项介绍
inserted:被绑定元素插入父节点时调用的钩子函数
el:使用指令的那个DOM元素
5.代码示例
需求:当页面加载时,让元素获取焦点(autofocus在safari浏览器有兼容性)
App.vue
1 2 3 4
| <div> <h1>自定义指令</h1> <input v-focus ref="inp" type="text"> </div>
|
自定义指令-指令的值
1.需求
实现一个 color 指令 - 传入不同的颜色, 给标签设置文字颜色
2.语法
1.在绑定指令时,可以通过“等号”的形式为指令 绑定 具体的参数值
1
| <div v-color="color">我是内容</div>
|
2.通过 binding.value 可以拿到指令值,指令值修改会 触发 update 函数
1 2 3 4 5 6 7 8 9 10
| directives: { color: { inserted (el, binding) { el.style.color = binding.value }, update (el, binding) { el.style.color = binding.value } } }
|
3.代码示例
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <template> <div> <!--显示红色--> <h2 v-color="color1">指令的值1测试</h2> <!--显示蓝色--> <h2 v-color="color2">指令的值2测试</h2> <button> 改变第一个h1的颜色 </button> </div> </template>
<script> export default { data () { return { color1: 'red', color2: 'blue' } } } </script>
<style>
</style>
|
自定义指令-v-loading指令的封装
1.场景
实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态 => 用户体验不好
2.需求
封装一个 v-loading 指令,实现加载中的效果
3.分析
1.本质 loading效果就是一个蒙层,盖在了盒子上
2.数据请求中,开启loading状态,添加蒙层
3.数据请求完毕,关闭loading状态,移除蒙层
4.实现
1.准备一个 loading类,通过伪元素定位,设置宽高,实现蒙层
2.开启关闭 loading状态(添加移除蒙层),本质只需要添加移除类即可
3.结合自定义指令的语法进行封装复用
1 2 3 4 5 6 7 8 9
| .loading:before { content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: #fff url("./loading.gif") no-repeat center; }
|
5.准备代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
| <template> <div class="main"> <div class="box" v-loading="isLoading"> <ul> <li v-for="item in list" :key="item.id" class="news"> <div class="left"> <div class="title">{{ item.title }}</div> <div class="info"> <span>{{ item.source }}</span> <span>{{ item.time }}</span> </div> </div>
<div class="right"> <img :src="item.img" alt=""> </div> </li> </ul> </div> <div class="box2" v-loading="isLoading2"></div> </div> </template>
<script>
import axios from 'axios'
export default { data () { return { list: [], isLoading: true, isLoading2: true } }, async created () { const res = await axios.get('http://hmajax.itheima.net/api/news') setTimeout(() => { this.list = res.data.data this.isLoading = false }, 2000) }, directives: { loading: { inserted (el, binding) { binding.value ? el.classList.add('loading') : el.classList.remove('loading') }, update (el, binding) { binding.value ? el.classList.add('loading') : el.classList.remove('loading') } } } } </script>
<style> .loading:before { content: ''; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: #fff url('./assets/logo.png') no-repeat center; }
.box2 { width: 400px; height: 400px; border: 2px solid #000; position: relative; }
.box { width: 800px; min-height: 500px; border: 3px solid orange; border-radius: 5px; position: relative; } .news { display: flex; height: 120px; width: 600px; margin: 0 auto; padding: 20px 0; cursor: pointer; } .news .left { flex: 1; display: flex; flex-direction: column; justify-content: space-between; padding-right: 10px; } .news .left .title { font-size: 20px; } .news .left .info { color: #999999; } .news .left .info span { margin-right: 20px; } .news .right { width: 160px; height: 120px; } .news .right img { width: 100%; height: 100%; object-fit: cover; } </style>
|
插槽
插槽-默认插槽
1.作用
让组件内部的一些 结构 支持 自定义
2.需求
将需要多次显示的对话框,封装成一个组件
3.问题
组件的内容部分,不希望写死,希望能使用的时候自定义。怎么办
4.插槽的基本语法
- 组件内需要定制的结构部分,改用****占位
- 使用组件时, ****标签内部, 传入结构替换slot
- 给插槽传入内容时,可以传入纯文本、html标签、组件
5.代码示例
MyDialog.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| <template> <div class="dialog"> <div class="dialog-header"> <h3>友情提示</h3> <span class="close">✖️</span> </div> <div class="dialog-content"> <slot></slot> </div> <div class="dialog-footer"> <button>取消</button> <button>确认</button> </div> </div> </template> <script> export default { data () { return { } } } </script> <style scoped> * { margin: 0; padding: 0; } .dialog { width: 470px; height: 230px; padding: 0 25px; background-color: #ffffff; margin: 40px auto; border-radius: 5px; } .dialog-header { height: 70px; line-height: 70px; font-size: 20px; border-bottom: 1px solid #ccc; position: relative; } .dialog-header .close { position: absolute; right: 0px; top: 0px; cursor: pointer; } .dialog-content { height: 80px; font-size: 18px; padding: 15px 0; } .dialog-footer { display: flex; justify-content: flex-end; } .dialog-footer button { width: 65px; height: 35px; background-color: #ffffff; border: 1px solid #e1e3e9; cursor: pointer; outline: none; margin-left: 10px; border-radius: 3px; } .dialog-footer button:last-child { background-color: #007acc; color: #fff; } </style>
|
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <template> <div> <MyDialog> 你确定删除吗 </MyDialog> </div> </template>
<script> import MyDialog from './components/MyDialog.vue' export default { data () { return {
} }, components: { MyDialog } } </script>
<style> body { background-color: #b3b3b3; } </style>
|
插槽-后备内容(默认值)
1.问题
通过插槽完成了内容的定制,传什么显示什么, 但是如果不传,则是空白
能否给插槽设置 默认显示内容 呢?
2.插槽的后备内容
封装组件时,可以为预留的 <slot>
插槽提供后备内容(默认内容)。
3.语法
在 标签内,放置内容, 作为默认显示内容
4.效果
5.代码示例
App.vue
1 2 3 4
| <MyDialog></MyDialog> <MyDialog> 你确定删除吗 </MyDialog>
|
插槽-具名插槽
1.需求
一个组件内有多处结构,需要外部传入标签,进行定制
弹框中有三处不同,但是默认插槽只能定制一个位置,这时候怎么办呢?
2.具名插槽语法
3.v-slot的简写
v-slot写起来太长,vue给我们提供一个简单写法 v-slot —> #
1 2 3 4 5 6 7 8 9 10 11 12
| <div class="dialog"> <div class="dialog-header"> <slot name="head"></slot> </div> <div class="dialog-content"> <slot name="content">我是后备内容</slot> </div> <div class="dialog-footer"> <slot name="footer"></slot> </div> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12
| <MyDialog> <template v-slot:head> 友情提示 </template> <template v-slot:content> 你确定删除吗 </template> <template v-slot:footer> <button>确定</button> <button>取消</button> </template> </MyDialog>
|
作用域插槽
1.插槽分类
默认插槽
具名插槽
插槽只有这两种,作用域插槽不属于插槽的一种分类
2.作用
定义slot 插槽的同时, 是可以传值的。给 插槽 上可以 绑定数据,将来 使用组件时可以用
3.场景
封装表格组件
4.使用步骤
给 slot 标签, 以 添加属性的方式传值
1
| <slot :id="item.id" msg="测试文本"></slot>
|
所有添加的属性, 都会被收集到一个对象中
在template中, 通过 #插槽名= "obj"
接收,默认插槽名为 default
1 2 3 4 5
| <MyTable :list="list"> <template #default="obj"> <button @click="del(obj.id)">删除</button> </template> </MyTable>
|
5.代码示例
MyTable.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| <template> <table class="my-table"> <thead> <tr> <th v-for="col in colums" :key="col">{{col}}</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="(item, index) in data" :key="index"> <td v-for="col in colums" :key="col"> {{ item[col] }} </td> <td> <slot :id="item.id"></slot> </td> </tr> </tbody> </table> </template> <script> export default { props: { data: Array, colums: Array } } </script> <style scoped> .my-table { width: 450px; text-align: center; border: 1px solid #ccc; font-size: 24px; margin: 30px auto; } .my-table thead { background-color: #1f74ff; color: #fff; } .my-table thead th { font-weight: normal; } .my-table thead tr { line-height: 40px; } .my-table th, .my-table td { border-bottom: 1px solid #ccc; border-right: 1px solid #ccc; } .my-table td:last-child { border-right: none; } .my-table tr:last-child td { border-bottom: none; } .my-table button { width: 65px; height: 35px; font-size: 18px; border: 1px solid #ccc; outline: none; border-radius: 3px; cursor: pointer; background-color: #ffffff; margin-left: 5px; } </style>
|
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <template> <div> <MyTable :data="list" :colums="cloums"> <template #default="obj"> <button @click="del(obj.id)">删除</button> </template> </MyTable> </div> </template>
<script> import MyTable from './components/MyTable.vue' export default { data () { return { cloums: ['id','name','age'], list: [ { id: 1, name: '张小花', age: 18 }, { id: 2, name: '孙大明', age: 19 }, { id: 3, name: '刘德忠', age: 17 }, ], list2: [ { id: 1, name: '赵小云', age: 18 }, { id: 2, name: '刘蓓蓓', age: 19 }, { id: 3, name: '姜肖泰', age: 17 }, ] } }, components: { MyTable }, methods: { del: function(removeId) { let newList = this.list.filter(item => item.id !== removeId); this.list = newList } } } </script>
|
keep-alive
什么是keep-alive
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件中。
优点:
在组件切换过程中把切换出去的组件保留在内存中,防止重复渲染DOM,
减少加载时间及性能消耗,提高用户体验性。
App.vue
1 2 3 4 5 6 7
| <template> <div class="h5-wrapper"> <keep-alive> <router-view></router-view> </keep-alive> </div> </template>
|
问题:
缓存了所有被切换的组件
keep-alive的三个属性
① include : 组件名数组,只有匹配的组件会被缓存
② exclude : 组件名数组,任何匹配的组件都不会被缓存
③ max : 最多可以缓存多少组件实例
App.vue
1 2 3 4 5 6 7
| <template> <div class="h5-wrapper"> <keep-alive :include="['LayoutPage']"> <router-view></router-view> </keep-alive> </div> </template>
|
额外的两个生命周期钩子
keep-alive的使用会触发两个生命周期函数
activated 当组件被激活(使用)的时候触发 → 进入这个页面的时候触发
deactivated 当组件不被使用的时候触发 → 离开这个页面的时候触发
组件缓存后就不会执行组件的created, mounted, destroyed 等钩子了
所以其提供了actived 和deactived钩子,帮我们实现业务需求。