
官方介绍
自 2.6.0 起有所更新。已废弃使用 slot
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot>
元素作为承载分发内容的出口。资料来源于 Vue中文官网
它允许你像这样合成组件
<navigation-link url="/profile">
Your Profile
</navigation-link>
然后你在 <navigation-link>
的模板中可能会写为
<a v-bind:href="url" class="nav-link">
<slot></slot>
</a>
当组件渲染的时候,<slot></slot>
将会被替换为“Your Profile”。插槽内可以包含任何模板代码,包括 HTML:
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
甚至其它的组件
<navigation-link url="/profile">
<!-- 添加一个图标的组件 -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
如果 <navigation-link>
的 template
中没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃
组件插槽介绍
在 Vue 中,组件是嵌套着使用的。子组件 嵌套在 父级组件 的 对应目标 位置就需要用 插槽 方式 来实现
<slot></slot>
slot 定义插槽,它的name 值为 Vue 模板组件的名字,<slot>标签的位置 会直接作为 Vue模板的 渲染位置。实现快速构建;
当定义了模板组件后,除了可以用 v-bind:自定义成员 = 值 传给组件需要的参数以外,
我们还可以使用 v-on:自定义成员 = 方法 传递自定义的事件方法
<div id="app">
<!-- 绑定参数 循环对象 成员绑定给对应接收参数 方法赋值给对应方法 -->
<todo-list v-for="(item,index) in todo" v-bind:item="item" v-bind:index="index" v-on:remove="removeItem">
</todo-list>
<button></button>
</div>
<script>
// 定义 全局组件 todo-list
Vue.component('todo-list', {
//绑定 标签上 v-bind:对应参数 取得的值
props: ['item', 'index'],
// 定义模板样式 赋值内容
template: '<li>{{index}}:{{item}} <button v-on:click="remove">删除</button></li>',
// 定义方法对象
methods: {
// remove方法 引用 对应方法
remove() {
this.$emit('remove');
}
},
});
let app = new Vue({
el: '#app',
data() {
return {
todo: ['学习', '唱歌', '跳舞'],
}
},
methods: {
// 传入 index 值
removeItem(index) {
// 删除 对应 index 下标中 1个成员
this.todo.splice(index, 1);
}
}
})
</script>
部署完 vue脚手架 后,项目中文件对应的描述
vuecil-demo # 项目目录
– node_modules # 项目依赖的第三方包
– public # 静态文件目录
– favicon.ico # 浏览器小图标
– index.html # 单页面的html文件
– src # 业务文件夹
– assets # 静态资源
– login.png # vue 的 logo 图片
– components # 组件目录
– helloword.vue # 欢迎页面的 vue 代码文件
– app.vue # 整个应用的根目录
– main.js # 入口 js 文件
– .gitignore # jit 忽略提交配置
– babel.config.js # babel 配置
– package.json # 依赖包列表
– README.md # 项目说明
– yarn.lock # 项目包版本锁定和缓存地址


若需要修改 Vue默认配置,需在 vue项目 的根目录新建 vue.config.js 文件,使用 module.exports 导入配置文件的方式修改具体的参数值。
修改端口 自动打开
运行 vue项目时,默认运行在8080端口,如需修改,并实现自动浏览器打开项目,可在 vue.config.js 中添加项
module.exports={
devServer:{
// 端口
port:3000,
// 自动打开
open:true,
}
}
实际测试开发,如若需要 可以关闭 eslint 检查

eslint 是代码检查工具,它会在 vue项目运行时候执行检查,如果代码中违反了 eslint 规则,则会抛出报错 并 提示
若需要关闭该 检查工具 可在 vue.config.js 添加项:
module.exports = {
// 关闭 eslint 检查 (eslint为代码检查工具,违反该规则就会报错)
lintOnSave: false,
}
Vue推荐采用 .vue 文件来开发项目
<templat> 里只有一个根标签,这样的优势在于:
1. js独立作用域,变量重名互不影响
2. style 配合 scoped 属性,可以保证样式只针对当前 template 内标签生效
一个 .vue文件的结构如下所示
<!-- 界面层 -->
<template>
</template>
<!-- 数据层 -->
<script>
export default {
}
</script>
<!-- 样式层 -->
<style>
</style>
插入本地图片 的方式需要 引入模块,因为在vue的打包程序 webpack 中,万物皆模块
css 与 html 标签中引入图片可依旧以原生方式,但是 js 中若想插入图片,需要引入图片模块
<template>
<img :src="img">
</template>
<script>
export default {
data() {
return {
img: './assets/img/01.jpg'
}
}
}
</script>
与 jQuery中的 template包 类似,vue也使用了 {{value}} 的方式对 数据 + 界面 进行了关联
- 数据层将数据对象存放在 data 中,创建成员并给成员赋值对象值
- 界面层使用 {{ 成员名 or 表达式 }} 直接使用数据层 data 中对应成员名的值
在 dom 标签中,直接插入 vue 数据变量
- 又叫声明式渲染/文本插值
- 语法{{ 表达式 }}
<template>
<div>
<span>{{sayHi}}</span>
</div>
</template>
<script>
export default {
data(){
return{
sayHi:'Hello Vue',
}
}
}
</script>
<style>
</style>
vue 采用 MVVM 设计模式来处理 数据 与 界面 之间的联系
- 设计模式 是对代码的分层,引入的一种架构概念
- vue 的 MVVM 设计思路 让开发过程中 减少 DOM 操作,提高开发效率

M (模型) V (视图 VM (视图模型)
DOM listeners vue会监听界面层的数据变动,当发生变动时候,会通知数据层更新数据。
Data Bindings vue会让数据层与界面层的对应区域的内容进行绑定,数据发生变化同步界面
vue中文官网 详细文档
MVVM表示如下:
1. Model:模型层,在这里表示 javaScript 对象
2. View:视图层,在这里表示 DOM (HTML操作对象)
3. ViewModel:连接视图和数据的中间件 (Vue.js 就是 MVVM 中的 ViewModel 层的实现者)
ViewModel 是 数据 与 视图 之间的观察者,在MVVM架构中,是不允许 数据 和 视图 直接通信,而是通过 ViewModel 通信
v-bind 可以给标签属性设置 Vue 变量的值
例如 <a> 标签的 harf ,<img> 标签的 src
v-bind:属性名=”vue变量” 可简写为 :属性名=”vue变量”
<template>
<div>
<a v-bind:href="url">无名喵站</a>
<img :src="img">
</div>
</template>
<script>
export default {
data(){
return{
img:'https://www.dmoe.cc/random.php',
url:'https://smmcat.cn',
}
}
}
</script>
上方代码实际打包后,相当于 :
- <a> 标签的 src 属性为 https://smmcat.cn,
- <img> 的 src 属性为 https://www.dmoe.cc/random.php
基本使用
v-on 可以给标签绑定事件
例如 v-on:click=”fnn”,当用户在该标签触发了对应事件,会执行绑定事件中对应的方法
语法
- v-on:事件名= “要执行的少量代码”
- v-on:事件名= “methods 中的函数名”
- v-on:事件名= “methods 中的函数名(实参)”
v-on:事件名=”方法” 可简写为 :事件名=”方法”
<template>
<div>
<p>{{num}}</p>
<button v-on:click="num++">+1</button>
<button v-on:click="num--">+1</button>
<button @click="zero">归零</button>
</div>
</template>
<script>
export default {
data(){
return{
num:0,
}
},
methods:{
zero(){
this.num=0;
}
}
}
</script>
阻止默认事件
使用 v-on:事件名.prevent=”方法” 或 v-on:事件名.prevent 来阻止标签中的默认行为
每个标签都有默认的事件,例如 <a> 标签的跳转,<input:submit> 的提交表单。某些情况下我们不需要这些默认行为,需要去阻止默认事件的响应。
<--! 不再进行跳转 -->
<a @click.prevent="fnn">百度</a>
获取事件对象
v-on:事件名=”方法” 如若不携带任何参数,默认直接在行参中获取 事件对象
<template>
<div>
<button @click="getEven">获取事件对象</button>
</div>
</template>
<script>
export default {
methods: {
// 当 标签绑定 @事件名 未设置行参,默认传入事件对象
getEven(e) {
console.log(e);
}
}
}
</script>
v-on:事件名=”方法(行参)” 如若携带参数,传入 事件对象 可以在行参中以 $event 表示
- v-on:click=”fnn(num,$event)”
<template>
<div>
<button @click="getEven(5,$event)">获取事件对象</button>
</div>
</template>
<script>
export default {
methods: {
// 当 标签绑定 @事件名,若有传参,$event 作为事件对象
getEven(num, e) {
console.log(e);
}
}
}
</script>
修饰符
在事件后面.修饰符名 给事件增加更多的效果
语法
- v-on:事件名.修饰符=”methods里函数”
修饰符列表
方法名 | 效果 |
---|---|
.stop | 阻止事件冒泡 |
.prevent | 阻止默认行为 |
.once | 程序运行期间,只触发一次事件处理函数 |
可以以连写方式实现多个效果
<!-- 实现单击时 阻止默认事件 + 阻止冒泡 -->
<a v-on:click.prevent.stop href="http://baidu.com">百度</a>
按钮修饰符
在 @keyup 事件后面.键位修饰符 可以直接针对对应的按钮绑定事件处理函数
语法
- v-on:keyup.enter – 监听回车按键
- v-on:keyup.esc – 监听esc按键
- 更多内容 可以访问 官方文档
通过绑定 input 标签,使用 keyup.enter 实现捕获 按键回车 事件
<template>
<div>
<input type="text" @keydown.enter="entFnn" v-model="text">
</div>
</template>
<script>
export default {
data() {
return {
text: '',
}
},
methods: {
entFnn() {
alert(this.text);
}
}
}
</script>
原理
v-model 实现 value 属性 和 vue 数据实现 双向绑定
适用于针对 表单元素 与数据之间的绑定
双向绑定
- 当表单的 value 值发生变化时,v-model 所绑定的 成员也会发生该改变
- 当成员 发生变化后,对应的所绑定 v-model 的 表单的值也发生变化
案例演示
上方的案例,模拟了下方代码的效果:
<template>
<div>
<input type="text" v-model="msg">
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
data() {
return {
msg: '',
}
}
}
</script>
多选框、下拉框、单选框 案例
针对不同的 input 类型,对应的绑定操作需要了解
下拉框
v-model 应绑定给 <select> 上,而不是 <option>,但是 model 取的是 option 选中的 value 值
<template>
<div>
<select v-model="area">
<option value="北京">北京</option>
<option value="北京">上海</option>
</select>
</div>
</template>
<script>
export default {
data() {
return {
area: ''
}
}
}
</script>
单选框
按照 radio 类型的特性,radio 对应的 name 需要绑定一致才有 单选的效果
<template>
<div>
<input type="radio" name="sex" value="男" v-model="sex">男
<input type="radio" name="sex" value="女" v-model="sex">女
</div>
</template>
<script>
export default {
data() {
return {
sex:'男',
}
}
}
</script>
复选框
复选框使用 v-model 需要绑定的数组类型的变量,若非数组类型,则绑定的只是 复选框 的 checked 状态
<template>
<div>
<input type="checkbox" value="篮球" v-model="like">篮球
<input type="checkbox" value="跳舞" v-model="like">跳舞
<input type="checkbox" value="rup" v-model="like">rup
<input type="checkbox" value="画画" v-model="like">画画
<input type="checkbox" value="敲代码" v-model="like">敲代码
</div>
</template>
<script>
export default {
data() {
return {
like:[],
}
}
}
</script>
model修饰符
v-model 提供了修饰符,方便在对应的需求中使用。让 v-model 拥有更多功能
方法名 | 效果 |
---|---|
.number | 以parseFloat转成数字类型 |
.trim | 去除首尾空白字符 |
.lazy | 在change时触发而不是inupt时 |
介绍
与原生的 innerText、innerHTML 类似,v-text 是以字符串格式显示,v-html 是尝试标签形式解析内容
语法:
- v-text=”Vue数据变量”
- v-html=”Vue数据变量”
该语法会覆盖标签内的插值表达式 {{value}} 的输出结果
演示
<template>
<div>
<p v-text="sayHi"></p>
<p v-html="sayHi"></p>
</div>
</template>
<script>
export default {
data() {
return {
sayHi: '<h3>Hello vue</h3>'
}
}
}
</script>
输出结果:

介绍
v-show 与 v-if 都可以实现将标签隐藏,但是它们隐藏的方式有所不同
- v-show 使用 display:none 的方式隐藏 (适合频繁切换)
- v-if 是直接将标签从 DOM树 上移除
当左侧绑定的变量值为 true,会将标签元素显示。否则 v-show 会将其隐藏,而 v-if 会直接移除该标签
语法:
- v-show=”Vue变量”
- v-if=”Vue变量”
- v-if 可以配合 v-else 或者 v-else-if 使用
演示
<template>
<div>
<p v-show="type">Hello</p>
<p v-if="score == 100">满分</p>
<p v-else-if="score > 60">及格</p>
<p v-else>不及格</p>
</div>
</template>
<script>
export default {
data() {
return {
type: true,
score: 70,
}
}
}
</script>
输出结果:

效果
v-for 用于遍历对应数组成员,按照数组数量,循环生成对应标签与标签中值
语法:
- v-for=” (值变量,索引值) in 目标结构 “
效果:
- 可以遍历数组 / 对象 /数字
- v-for 的临时变量不可以用到 v-for 对应标签 范围外
演示
使用 插值表达式,让循环后的结果在对应的位置上显示
<template>
<div>
<ul>
<li v-for="(item, index) in arr" :key="index">
{{ index }} --- {{ item.name }} --- {{ item.sex }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
arr: [
{ name: '小明', sex: '男' },
{ name: '屎猫', sex: '男' },
{ name: '七罪', sex: '男' },
{ name: '蝶衣', sex: '女' },
{ name: '小可', sex: '女' },
]
}
}
}
</script>
输出结果:

Key的指定
:key=”参照物” 用于优化渲染,以便按照 key 去比较变化,要求为循环中不重复的值
v-for 会最大限度的尝试就地修改/复用相同类型元素,这和 vue 使用 diff算法 有关

虚拟DOM
与 原生DOM 不同,虚拟DOM剔除了大部分不常用参数,可更有效的提高DOM更新的性能
虚拟DOM 在内存中找到变化部分,再更新到真实DOM中 (打补丁)

选择 key 的参照物
从数据库中接收到的对象,一般id值是自增生成的,具有唯一性。适合用作于 循环的 key
有 参照物 用 参照物,无 参照物 用 索引
<template>
<div>
<ul>
<li v-for="(item, index) in arr" :key="item.id">
{{ index }} --- {{ item.name }} --- {{ item.sex }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
arr: [
{id:1, name: '小明', sex: '男' },
{id:2, name: '屎猫', sex: '男' },
{id:3, name: '七罪', sex: '男' },
{id:4, name: '蝶衣', sex: '女' },
{id:5, name: '小可', sex: '女' },
]
}
}
}
</script>
只有部分数组方法会造成 v-for 更新
某些情况下,v-for 绑定的数组发生改变后,v-for对应 循环生成的标签的内容也会更新,且是尽可能减少渲染区域
- 数组翻转 (会变更)
- 数组截取 (不会变更)
- 数组值发生更新 (不会变更)
数组变更方法,就会导致 v-for 更新,页面更新
例如:
- push() 数组向后插入
- pop() 删除数组末尾一个元素
- shift() 删除首个数组元素,并把其他元素索引向前位移补位
- unshift() 在数组的开头向数组添加新元素,并“反向位移”旧元素
- splice() 方法可用于向数组添加新项,或用于删除元素
- sort() 数组排序
- reverse() 翻转数组
针对数组非变更方法,或返回新数组的方式的方法,不会导致 v-for 更新,可采用 覆盖数组 或者 this.$set()
演示
针对不同的方法,为让视图层也同步更新,对应不同的解决策略
<template>
<div>
<ul>
<li v-for="(item, index) in arr" :key="index">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
arr: [1, 2, 3, 4, 5, 6],
}
},
methods: {
revBtn() {
// 翻转数组 会直接改变数组
this.arr.reverse();
},
silceBtn() {
// 裁剪数组(下标0数量3) 返回一个新数组
let temp = this.arr.slice(0, 3);
// 将得到的新数组 覆盖 原数组 触发变更
this.arr = temp;
},
updataBtn() {
/*
$set 实现 视图层同步更新 数组中成员值
参数1:数组目标 参数2:位置 参数3:更新后的值
*/
this.$set(this.arr, 0, 7);
}
}
}
</script>
效果展示

说明
使用 :class 来让 Vue 动态操作选中的 class类 是否被使用
v-bind 的 :class 操作可以动态的设置和切换 标签中的目标 class 是否被引用
语法:
- :class = “{ 类名:布尔值 }”
- 当花括号 右侧 表达式的结果为 true 时,标签使用右侧类名
演示
使用 vue 来控制 class 的操作
<template>
<div>
<p :class="{ red: bool }">红色?</p>
<button @click="btn">变色</button>
</div>
</template>
<script>
export default {
data() {
return {
bool: true,
}
},
methods: {
btn() {
this.bool = !this.bool;
}
}
}
</script>
<style>
.red {
color: red;
}
</style>
效果展示

说明
使用 :style 动态设置标签中的 style 行内式的 样式属性
v-bind 的 :style 操作可以被vue动态的设置 :style 中的 行内样式 对应的属性
语法:
- :style=” { css属性名:变量值 } “
- 花括号 左侧为 css属性样式,右侧 对应 数据层 data 中该属性样式的 值
演示
使用 vue 来对 行内样式 的 属性赋值 操作
<template>
<div>
<p :style="{ color:colorStr }">红色</p>
</div>
</template>
<script>
export default {
data() {
return {
colorStr: 'red',
}
}
}
</script>
效果展示

自定义方法定义在 methods 对象中,通常用于绑定事件的回调方法
说明
基本使用
methods 对象中定义方法,在视图层由 触发事件 绑定的 方法名 调用执行,
<template>
<div>
<input type="button" @click="entFnn" value="sayHi">
</div>
</template>
<script>
export default {
methods: {
entFnn() {
alert('hello vue');
}
}
}
</script>
效果展示

获取参数
若想让方法获取参数,首先在方法绑定对应的事件标签中传入 对应的参数
标签的嵌套规则可作为一个作用域的嵌套规则,方法定义后可直接获取对应标签绑定的参数
<template>
<div>
<ul v-for="(item, index) in text" :key="index">
<li @click="entFnn(item)">{{ item }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
text: [1, 2, 3, 4],
}
},
methods: {
entFnn(i) {
alert(i);
}
}
}
</script>
效果展示

获取事件对象
当没有传入参数的方法定义行参,默认传入事件对象,直接使用即可
当有参数传入到行参中,需要在行参中传入 $even 作为 事件对象 传入
<template>
<div>
<input type="text" @keydown.enter="entFnn" v-model="text">
</div>
</template>
<script>
export default {
data() {
return {
text: '',
}
},
methods: {
entFnn(e) {
// 事件对象
console.log(e);
}
}
}
</script>
效果展示

过滤器定义在 filter方法 或 filter 对象中,视图层使用 | 绑定过滤器处理的 值
说明
定义过滤器的目的是在界面层将 插值表达式 和 v-bind 动态属性 进行处理 并返回处理后的值
过滤器只能用在 {{ value }} 与 v-bind 动态属性中,使用过滤器的方式为 变量值 | 过滤器名
语法
- 使用 {{变量值 | 过滤器名 }} 或 v-bind:属性名=” 变量值 | 过滤器名 “
- 定义全局 main.js – Vue.filter( ‘过滤器名字’,函数体 );
- 定义局部 filerts:{ 过滤器名字:函数体 };
全局过滤器 定义在 main.js 入口上,所有绑定的 vue 组件均可使用
// Vue.filter('过滤器名字',传入的值 => 处理后的值);
Vue.filter('reverse', val => val.split('').resverse().join(''));
局部过滤器 只能在 单 vue 文件下定义并使用
<template>
<div>
<p>{{ sayHi }}</p>
<!-- 使用过滤器 -->
<p>{{ sayHi | reverse }}</p>
</div>
</template>
<script>
export default {
data() {
return {
sayHi: 'Hello word',
}
},
// 只在当前 vue 文件内使用
filters: {
reverse(val) {
return val.split('').reverse().join('');
}
}
}
</script>
效果展示

计算属性定义在在 computed 对象中,视图层使用 计算属性名 绑定
说明
为了优化性能,计算属性将处理后 得到的结果的值 缓存在内存中,用于需要时候使用
计算属性 在 computed 项中定义,它可使用类似当作 data 下的 成员调用方式被调用 例如 this.value、{{value}}
计算属性根据依赖变量结果缓存, 依赖变化重新计算结果存入缓存, 比普通方法性能更高
注意
- 计算属性和data属性都是变量,不能重名。用法和data相同
- 函数内变量变化,会自动重新计算结果返回
语法
- 基本写法 computed: { 计算属性名()=>处理后的值 }
- 完整写法 computed: { 计算属性名: { get(){} , set(){} } }
演示
<template>
<div>
<p>{{ sayHi }}</p>
<p>{{ up }}</p>
<p>{{ up }}</p>
<p>{{ up }}</p>
<p>{{ up }}</p>
</div>
</template>
<script>
export default {
data() {
return {
sayHi: 'Hello word',
}
},
computed:{
up(){
console.log('执行计算属性方法');
return this.sayHi.toUpperCase();
}
}
}
</script>
效果展示

完整操作
如要想要对 计算属性 直接赋值 或者 额外的操作 ( 如 获取 v-model 传入前面页面改变的新值 ),需要使用完整写法
<template>
<div class="app">
<span>全选</span>
<input type="checkbox" v-model="isAll">
<button>反选</button>
<hr />
<ul>
<li v-for="(item, index) in arr" :key="index">
<input type="checkbox" v-model="item.checked">
<span>{{ item.name }}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
arr: [
{ name: '屎猫', checked: false, },
{ name: 'AI婆', checked: false, },
{ name: '腐喵', checked: false, },
{ name: '夜夜', checked: false, },
{ name: '泥垢', checked: false, },
],
}
},
computed: {
isAll: {
// 当 有值 写入到 计算属性中 参数中读取这个值
set(val) {
// 执行写入后的操作方法
this.arr.forEach(item => item.checked = val);
},
// 当发生读取 计算属性 操作后
get() {
/*
执行读取后的操作方法
* every 方法会遍历验证每个数组
*
* 如若其中有一个 不符合条件 返回 false
* 如若都符合条件 返回 true
*/
return this.arr.every(obj => obj.checked === true);
},
}
}
}
</script>
效果展示

监听器定义在 watch 对象中,命名需与监听的 变量 或 计算属性 同名
说明
监听器 用于 操作 对应变量值 的 变化的 返回的 回调函数
可以侦听 data/计算属性 中值的变化
当值发生改变时 会被 watch 对应名字的方法所 捕获
简单写法:
<template>
<div>
<input type="text" v-model="data">
</div>
</template>
<script>
export default {
data() {
return {
data: ''
}
},
watch: {
// 参数1 当前更新后的值 参数2 上一个状态的值
data(newVal, oldVal) {
console.log(newVal, oldVal);
}
}
}
</script>
效果展示

完整写法:
为了监听对象的变化,需要进行深度监听。将 deep 的值设置为 true
<template>
<div>
<input type="text" v-model="text.name">
</div>
</template>
<script>
export default {
data() {
return {
text: { name: '' },
}
},
watch: {
/*
* [问题!] 我们发现 newVal 与 oldVal 的值相同
* watch监听到对象的变化,但是在对对象或数组做变更操作时,删除或添加属性,
* 监听函数中的新值和旧值都指向了同一个对象或数组,并且Vue不会保留变更之前的副本
* [解决方法]
* 1. 计算属性 使用 JSON.stringify 将需要监听 目标 转为字符串
* 2. 引入监听对应名称的 计算属性 值 并使用 JSON.parse 转换成对象 完成比较效果
* tip. 由于字符串是 简单数据 更换值时是直接覆盖 故新值与旧值不存在相同的指向下标
*/
text: {
handler(newVal) {
// 返回 text 对象
console.log(newVal);
},
// 深度监听 (监听对象属性中的变化)
deep: true,
// 立即执行 (刷新页面时 立即执行)
immediate: true,
}
}
}
</script>
效果展示

解决 watch 监听对象时候 新旧值 一致问题
<template>
<div>
<input type="text" v-model="user.name">
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: ''
}
}
},
watch: {
/*
* 使用序列化与反序列化的方式 相当于创建了一个新的对象
* 从字符串转对象 是创建新对象的操作 并对 计算属性 重新赋值
* [触发更新] 计算属性的值发生变化后 原下标更换 因此得到的结果就不同了
* https://www.cnblogs.com/MaiRen19970323/p/15160198.html
*/
username: {
handler(newVal, oldVal) {
let temp_1 = JSON.parse(newVal);
let temp_2 = JSON.parse(oldVal);
console.log(temp_1.name, temp_2.name);
},
deep: true,
}
},
computed: {
// 定义 计算属性
username() {
return JSON.stringify(this.user);
}
}
}
</script>
效果展示

说明
使单页面的 CSS 样式就只能作用于当前的组件
当给 单vue文件中的 <style> 加上 scoped 后,会自动给标签添加 data-v-hash(哈希值)
也就是说,该样式只能适用于当前组件元素。通过该属性,可以使得组件之间的样式不互相污染
<style scoped>
.btn {
width: 100px;
height: 70px;
border-radius: 10px;
margin: 10px;
border: 2px solid #66cc;
}
</style>
效果展示

加上scoped之后Vue会在这段样式的最后一段加上[data-v-hash]属性,然后通过属性选择器选中这个元素,从而实现元素样式的隔离
组件是可复用的 Vue 实例,封装标签、样式和JS代码

组件是 vue 的特性,将不同业务或界面划分为组件,并且各组件作用域独立,实现重复利用。
介绍:
- 组件化:封装的思想,把页面上 可重用的部分 封装成 组件,从而方便项目的开发和维护
- 一个页面,可以拆分成一个个组件,每个项目可以有自己独立的结构和样式还有行为
优势:
- 变量区独立,解决频繁命名过多的问题
- 各标签作用域都相互独立,便于集成复用
- 减少重复的内容复制粘贴,方便项目的开发和维护
- 各自独立,互不影响
使用 import 引用的 vue组件,该组件被称为 子级组件
引用该 子级组件 的 单文件,称为它的 父级组件
组件的实际操作可分为:
创建组件 / 引入组件 / 注册组件 / 使用组件
创建组件
创建 .vue 文件,封装复用的标签、样式和 JS代码
<template>
<div class="title">
<div>Hello Vue</div>
</div>
</template>
<script>
export default {
}
</script>
<style>
.title {
width: 200px;
height: 120px;
display: flex;
justify-content: center;
align-items: center;
border: 5px solid #ccc;
border-radius: 18px;
margin: 10px;
}
</style>
注册组件 – 全局注册
声明在全局的组件,会允许在所有 vue 组件上使用
在 main.js 中引用该 vue 文件,并用 vue.component( “组件名” , 组件对象 ); 进行注册
import Vue from 'vue'
// 引用 组件
import hellovue from './helloVue.vue'
// 全局注册 参数1:组件名 参数2:组件对象
Vue.component('hellovue',hellovue);
其他 vue 单文件都可以在 <template> 视图层 直接使用 <hellovue> 显示该组件样式
<template>
<div>
<hellovue></hellovue>
<hellovue></hellovue>
<hellovue></hellovue>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
效果展示

注册组件 – 局部注册
声明在局部的组件,只能在已使用 import 引用该组件的 单文件上使用 (可引用多个)
在 vue 文件中,使用 import 组件名 from ‘文件路径’ 方式引入,再与 component 对象成员中注册
<template>
<div>
<hellovue></hellovue>
<hellovue></hellovue>
<hellovue></hellovue>
</div>
</template>
<script>
import hellovue from './helloVue.vue';
export default {
components: {
hellovue: hellovue,
}
}
</script>
<style>
</style>
效果展示

子级组件允许父级组件向子级组件传递对应数据,以便填充样式中对应的数据操作

当父级组件需要像子级组件传参,方便显示对应样式时候,子级需要使用 props 对象接收,
props 是可作为 字符串数组,定义的字符串用作 父级传递数据时 子级接收的 变量
props 也可用做对象,来对传参的过程进行更多的约束或指定操作,例如:限制传参类型(type)、默认值(default)、必填项(require)
接收参数
子级组件 定义接收数据的的 props 数组,用于接收父级组件传递过来的参数,可放置在对应位置中 (简单方法)
<template>
<div class="title">
<div>{{ text }}</div>
</div>
</template>
<script>
export default {
props: ['text'],
}
</script>
<style>
.title {
width: 200px;
height: 120px;
display: flex;
justify-content: center;
align-items: center;
border: 5px solid #ccc;
border-radius: 18px;
margin: 10px;
}
</style>
[扩展] props 另外有复杂写法,可以约束接收的值的类型、默认值、是否必填等 相关文档
export default {
props: {
background: String, // 外部插入此变量的值 必须是字符串 否则报错
color: {
type: String, // 约束 color值的类型
default: '#fff' // color变量 默认值 (若不传值 默认为白色)
},
title: {
type: String,
require: true, // 约束必须传值 否则报错
}
}
}
传入参数
父级通过 子级 props 数组中对应标识的 变量名,使用 v-model 或 :变量名 传递给子级数据
<template>
<div>
<hellovue v-for="(item, index) in arr" :key="index" :text="item"></hellovue>
</div>
</template>
<script>
import hellovue from './helloVue.vue';
export default {
components: {
hellovue,
},
data() {
return {
arr: ['hello word', 'hello dart', 'hello vue'],
}
}
}
</script>
<style>
</style>
效果展示

单向数据流
父级传给子级数据,但当子级组件的数据发生改变但父级不变时候,这状态叫做单向数据流

子级获取父级传递过来的参数后,虽然在子级的数据层看起来是可以修改这个参数,
但是这次修改的操作是单向的。并没有通知父级。因此父级值并不因此受到影响
正确的操作是需要 同时影响父级 对应的属性,
tip
- 父子数据不一致,而且子组件是依赖父级传入的值
- 从父级到子级的数据流向,叫做单向数据流
- props 的变量本身是只读的
因此直接在子级里面修改传入的参数的操作是错误的!
子级向父级传参
子级应使用 $emit() 向父级 绑定的 对应自定义事件 传递数据
this.$emit(‘自定义事件名’,传递的值);

子级组件 (实现样式)
<template>
<div class="btn">
<div>{{ num }}</div>
<button @click="addbtn">点赞</button>
</div>
</template>
<script>
export default {
props: ['num', 'ind'],
methods: {
addbtn() {
// 发送给父级 对应的事件 需要的参数
this.$emit('clickbtn', this.num, this.ind, 1);
}
}
}
</script>
<style scoped>
.btn {
width: 100px;
height: 70px;
border-radius: 10px;
margin: 10px;
border: 2px solid #66cc;
}
</style>
父级组件 (操作数据)
<template>
<div>
<!-- 传给子级参数 -->
<hellovue v-for="(item, index) in numArr"
:key="index"
:ind="index"
:num="item"
<!-- 无需再写传值 否则会被忽略 -->
@clickbtn="fn"></hellovue>
</div>
</template>
<script>
import hellovue from './addDemo.vue';
export default {
components: {
hellovue,
},
data() {
return {
numArr: [0, 0],
}
},
methods: {
// 欲从 子级 获取传入的参数
fn(num, index, add) {
let temp = num + add;
// 通知组件更新
this.$set(this.numArr, index, temp);
},
}
}
</script>
<style>
</style>
效果展示

说明
使用一个空白的vue类,作为组件的公共区域,组件引入 此公共区域 进行传值
通过此公共区域,各个组件引用该公共区域后,事件参数就可以 通过约定的 自定义事件 实现 发送参数 $emit 与接收参数 $on

实现
创建公共区域 例如: EvenBus/index.js
import Vue from 'vue';
// 创建 vue 空白对象
export default new Vue();
引用该公共区域 App.vue
<template>
<div>
<div style="float:left;">
<likeMenu v-for="(item, index) in arr"
:key="index"
:ind="index"
:num="item.num"
:name="item.name"
@checkOn="fn"></likeMenu>
</div>
<likeDemo :list="arr" style="float:left;"></likeDemo>
</div>
</template>
<script>
import likeMenu from './likeMenu.vue';
import likeDemo from './likeDemo.vue';
export default {
components: {
likeMenu,
likeDemo,
},
data() {
return {
arr: [
{ name: '张三', num: 0 },
{ name: '李四', num: 0 },
],
}
},
methods: {
fn(index, i) {
this.arr[index].num = this.arr[index].num + i;
}
}
}
</script>
<style>
</style>
子组件(发送参数方) likeMenu.vue
<template>
<div class="btn">
<p>{{ name }}</p>
<button @click="fn">点赞({{ num }})</button>
</div>
</template>
<script>
import evenBus from '../EvenBus/index.js';
export default {
props: ['num', 'ind', 'name'],
methods: {
fn() {
this.$emit('checkOn', this.ind, 1);
evenBus.$emit('putNum', this.ind, this.num);
}
}
}
</script>
<style>
.btn {
padding: 10px;
border: 2px solid #66cc !important;
text-align: center;
background: #66cc;
margin: 4px;
}
</style>
子组件(接收参数方) likeDemo.vue
<template>
<div class="demo">
<li v-for="(item, index) in list" :key="index" @click="fn(index)">
<span>{{ item.name }}</span> ---- <span>点赞数:{{ item.num }}</span>
</li>
</div>
</template>
<script>
import evenBus from '../EvenBus/index.js';
export default {
props: ['list'],
created() {
evenBus.$on('putNum', (index, num) => {
console.log(`子组件传过来参数为:${index} ${num}`);
});
},
}
</script>
<style>
.demo {
border: 2px solid #66cc !important;
padding: 5px;
margin: 4px;
}
</style>
效果展示

注意
即使你的子组件能修改数据源的数据,
也尽量保证数据源操作等使用在同一个区域,使用该区域去影响所有组件变化
当父级组件传递给子组件为对象或数组时,实际上传递的为 数组或对象对应的 下标,
子组件 修改父级传入的对象成员值,若对象下标一致是会影响父级 对应的 值

组件 name 可用做注册组件的名字
组件在 name 成员中定义的字符串值,可以在被调用时用作组件名称
- 组件定义 name 属性和值
<script>
export default {
name: 'ComNameDemo'
}
</script>
- 注册组件可用上面 name 的值
<template>
<div>
<ComNameDemo></ComNameDemo>
</div>
</template>
<script>
import ComName from './nameDemo.vue'
export default {
components: {
// ES6语法 将数组中对应的对象原地复制出来
// 实际下方的操作等同于 'ComNameDemo':ComName
[ComName.name]: ComName,
}
}
</script>
效果同理之前在父级 自定义组件命名 的操作,只不过该组件提前在 name 中被赋予了名字,
可直接调用子组件内置的 name 名字作为 子组件 命名在components 上
委派 <components> 来切换需显示的组件配合 :is 传值实现动态组件效果
- 使用 <components> 标签
- 在 <components> 标签中加入 vue属性 :is=’值’
- 在数据层的引入组件并在 components 中注册组件
- <components> 会根据 :is 中字符串对应的组件名 动态显示 对应的组件
<template>
<div>
<button @click="comName='UserName'">账号信息填写</button>
<button @click="comName='UserInfo'">个人信息填写</button>
<p>下方显示注册组件-动态切换</p>
<div style="border:1px solid red;">
<components :is="comName"></components>
</div>
</div>
</template>
<script>
import UserInfo from './01_UserInfo.vue';
import UserName from './UserName.vue';
export default {
data(){
return{
comName:'UserName'
}
},
components:{
UserInfo,
UserName
}
}
</script>
效果展示

使用 <keep-alive> 包裹在 <components> 外侧,实现组件缓存
当组件频繁切换的时候 实际上是进行了多次组件的创建和销毁 这样的操作将降低性能
- 使用 <keep-alive> 包裹在 <components> 外侧
- <keep-alive> 将会使包裹着的 <components> 首次创建后 将动态组件放置在缓存中
- 当动态组件切换状态时 需要创建或销毁 时会与内存交互 而不直接销毁和创建
不嵌套 <keep-alive>
<!-- 组件切换时 会不断的执行销毁创建的动作 -->
<components :is="comName"></components>
<script>
export default {
created(){
console.log('组件创建完成');
},
destroyed(){
console.log('组件销毁完成');
}
}
</script>
效果展示

外层嵌套 <keep-alive>
<!-- vue 内置 keep-alive 组件 实现组件的缓存 -->
<keep-alive>
<components :is="comName"></components>
</keep-alive>
<script>
export default {
created(){
console.log('组件创建完成');
},
destroyed(){
console.log('组件销毁完成');
}
}
</script>
效果展示

可用于获取 缓存组件 是否在页面中显示 的钩子函数 (需要搭配 <keep-alive>)
当需判断缓存的组件是被切走时候,使用 activated 获得显示时的回调,deactivated 获得切走时的回调
- activated 获得激活状态时触发
- deactivated 失去激活状态时触发
给在 <components> 中注册的动态组件监听 激活/取消激活 事件的钩子函数
- 被缓存的组件,无法取得 组件 创建 和 销毁 的状态,因此需要使用 激活 与 未激活 状态
- 依据 激活 与 未激活 状态,我们可以让数据层得知当前界面已显示的缓存组件
export default {
activated(){
console.log('xxx 组件被激活');
},
deactivated(){
console.log('xxx 组件取消激活');
}
}
效果展示

当不确定一个组件中标签的类型,可以用 <slot></slot> 进行占位
通过 slot 标签,让组件内可以接收不同的标签结构显示。给 slot 组件自由插入标签样式
描述
“ 就像照片展示用的相框一样,可以按着喜好插入需要的照片 ”

说明
- 子级组件定义 <slot></slot> 标签,实际就是划分 父级 在给子级组件中写 标签 的占位区域
- 当子级组件 被 父级引用时候,在标签内容嵌套的标签会显示在 子级组件 定义 slot 标签的区域
<!-- 子级 定义组件中的插槽 -->
<template>
<div class="Box">
<hr />
<p>展示的内容</p>
<slot></slot>
<hr />
</div>
</template>
<script>
export default {}
</script>
<style>
.Box {
text-align: center;
width: 300px;
height: 230px;
padding: 10px;
border: 2px solid #000;
margin: 10px;
}
</style>
<!-- 父级 引用组件并插入标签 -->
<template>
<div>
<Pannel>
<img src="https://www.dmoe.cc/random.php" width="70%">
</Pannel>
<Pannel>
<ul>
<li>锄禾日当午</li>
<li>汗滴禾下土</li>
<li>谁知盘中餐</li>
<li>粒粒皆辛苦</li>
</ul>
</Pannel>
</div>
</template>
<script>
import Pannel from './插槽.vue';
export default {
components: {
Pannel,
}
}
</script>
效果展示

在子级组件的 <slot></slot> 标签内部布置的标签,就是 slot 的默认内容

描述
- 子级组件定义了默认内容后,父级若不在组件内写标签,默认子级的 slot 标签将显示自己嵌套着的内容
效果
- 不给组件传标签,slot 内容原地显示
- 给组件传标签,则slot 整体被换掉
演示
<template>
<div class="Box">
<hr />
<p>展示的内容</p>
<slot>
<p>无内容</p>
</slot>
<hr />
</div>
</template>
<template>
<div>
<Pannel>
<ul>
<li>锄禾日当午</li>
<li>汗滴禾下土</li>
<li>谁知盘中餐</li>
<li>粒粒皆辛苦</li>
</ul>
</Pannel>
<Pannel></Pannel>
</div>
</template>
<script>
import Pannel from './插槽.vue';
export default {
components: {
Pannel,
}
}
</script>
效果展示

子组件中当中存在多个 slot 标签时,可依据 name 属性进行区分

若子组件中有多个 slot 标签,父级想将标签插入到子级,需要使用 <template v-shot:slot名字> 进行区分
用法
- slot 使用 name 属性区分名字
- template 分发需要配合 v-slot:名字 来区分对应的插槽
- v-slot:名字 可以简化成 #名字 方式区分
语法
<template>
<div>
<slot name="filst"></slot>
<slot name="item"></slot>
</div>
</template>
<template>
<div>
<Pannel>
<template v-slot:filst>
插入的内容
</template>
<template v-slot:item>
插入的内容
</template>
</Pannel>
</div>
</template>
演示
<template>
<div class="Box">
<hr />
<p>展示的内容</p>
<slot name="item_1">
<p>展示内容1</p>
</slot>
<slot name="item_2">
<p>展示内容2</p>
</slot>
<hr />
</div>
</template>
<script>
export default {
}
</script>
<template>
<div>
<Pannel>
<template v-slot:item_1>
<img src="https://www.dmoe.cc/random.php" width="70%">
</template>
<template v-slot:item_2>
<p>无名喵站</p>
</template>
</Pannel>
</div>
</template>
<script>
import Pannel from './插槽.vue';
export default {
components: {
Pannel,
}
}
</script>
效果展示

子组件中的插槽若想传值给父级 使用 :自定义变量名=”值” 方式发送父级 进行接收

说明
- 子级组件中 slot插槽 传给父级 <slot :自定义变量名=’值’></slot>
- 父级 <template> 接收子级 slot插槽 数据 v-slot=“自定义变量名” (注意不是v-slot:插槽名)
方法
- 子组件在 slot 上绑定属性和子组件内的值,使用 :自定义变量名=”值”
- 使用组件,传入自定义标签,用 template 和 v-slot=“自定义变量名”
- 自定义变量名 自动绑定 slot 上所有 属性 和 值
- 自定义变量名 = {子级传入的变量}
演示
<template>
<div class="Box">
<hr />
<!-- 向父级 传入变量 seyHi -->
<slot :row="sayHi"></slot>
<hr />
</div>
</template>
<script>
export default {
data() {
return {
sayHi: {
one: 'Hello',
tow: 'Vue',
}
}
}
}
</script>
<template>
<div>
<Pannel>
<!-- v-slot 会收集传入的所有数据 并以子级中书写的 :变量 作为它们的变量名 -->
<template v-slot="scope">
<p>{{ scope.row.one }}</p>
<p>{{ scope.row.tow }}</p>
</template>
</Pannel>
</div>
</template>
<script>
import Pannel from './插槽.vue';
export default {
components: {
Pannel,
}
}
</script>
效果展示

一个人从出生到逝去的过程
关于生命周期
vue 框架从 创建 到 销毁 的整个过程,叫 Vue 生命周期
vue组件 的创建到销毁也是一个 vue实例 的生命周期

vue 框架内置钩子函数,随着组件的生命周期阶段,自动执行
初始化 / 挂载 / 更新 / 销毁
- 随着特定的时间点,执行特定的操作
- 组件创建完毕后,可以在 crearetd 生命周期函数中发起 Ajax 请求,从而初始化 data数据
- vue 的钩子函数由4大阶段8个方法组成
描述 | 方法名[之前] | 方法名[之后] |
---|---|---|
初始化 | beforeCreate | created |
挂载 | beforeMount | mounted |
更新 | beforeUpdate | update |
销毁 | beforeDestroy | destroyed |
初始化流程
- new Vue() 实例化 [组件也有生命周期]
- init Events & Lifecycle 初始化时间何生命周期函数
- beforeCreate 生命周期钩子函数被执行
- Init injections & reactivity Vue内部添加 data 和 methods 等
- created 生命周期钩子函数被执行 实例创建
- 编译模板阶段 – 开始分析
- 是否有 el选项 – 检查要挂在哪里 (会选择挂载在成员 el 指定的位置)
- 没有 调用 $mount()方法
- 有 继续检查 template 选项
- 是否有 el选项 – 检查要挂在哪里 (会选择挂载在成员 el 指定的位置)

初始化的过程执行 (初始化前 → 初始化后)
beforeCreate() ➡️ created()
在初始化完成 (cerated) 的时候,能获取 data 中的成员,但是无法获取真实 DOM
实际演示
- 在 App.vue 上建立对应的 钩子函数 检查效果
- created() 中常用于 网络请求的场景 或者 注册全局事件
<template>
<div>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
data() {
return {
msg: 'Hello Vue',
}
},
beforeCreate() {
/*
- beforeCreate 是组件创建前的 钩子函数
- data 与 methods 创建之前
*/
console.log('准备初始化 Vue实例阶段');
console.log(this.msg); // undefind (因为 data 未创建)
},
created() {
/*
- created 是组件创建后的 钩子函数
- data 与 methods 创建完成后
- 常用于 网络请求的场景 或者 注册全局事件
*/
console.log('创建完毕 Vue实例');
console.log(this.msg); // Hello Vue
}
}
</script>
效果展示

挂载流程
- template 选项检查
- 有 编译 template 返回的 render 渲染函数
- 无 编译 el 选项对应标签作为 template (要渲染的模板)
- 虚拟DOM挂载成真实DOM之前
- beforeMount 生命周期钩子函数被执行
- Create … 把虚拟DOM和渲染的数据一并挂载到真实DOM上
- 真实DOM挂载完毕
- mounted 生命周期钩子函数被执行

挂载虚拟DOM到真实DOM的过程执行 (挂载前→挂载后)
beforeMount() ➡️ mounted()
在 creared 钩子函数中无法获取对应的 真实DOM ,但是在 mounted 中可以获取到对应的 真实DOM
实际演示
- 在 App.vue 上建立对应的 钩子函数 检查效果
- beforeMount 进行预处理 data,不会触发生命周期中的 updated 钩子函数
<template>
<div id="appDemo">
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
data() {
return {
msg: 'Hello Vue',
}
},
beforeMount(){
/*
- beforeMount 是虚拟DOM挂载到真实DOM操作之前的 钩子函数
- 未在真实 DOM 中绑定 vue实例 中建立的 虚拟DOM内容
*/
console.log('虚拟DOM 挂载到 原生DOM 前');
// 尝试使用原生获取 vue项目创建的 DOM
console.log(document.querySelector('#appDemo')); // null (初次操作返回空 因为未挂载)
},
mounted(){
/*
- mounted 是虚拟DOM挂载到真实DOM操作完成的 钩子函数
- 已完成 虚拟DOM 绑定到 真实DOM 的操作
*/
console.log('虚拟DOM 完成挂载到 原生DOM');
// 尝试使用原生获取 vue项目创建的 DOM
console.log(document.querySelector('#appDemo')); // <div id="appDemo">...</div>
}
}
</script>
效果展示

更新流程
- 当data里数据改变,更新DOM之前
- beforeUpdate 生命周期钩子函数被执行
- Virtual DOM …虚拟DOM重新渲染,打补丁到真实DOM
- updated 生命周期钩子函数被执行
- 当有 data 数据改变 重复这个循环

[循环执行] 更新虚拟DOM到真实DOM的过程执行 (更新前→更新后)
beforeUpdate() ↩️updated()
在 beforeUpate 中可以取到更新前的内容,在 updated 中获取更新后的内容
实际操作
- 在 App.vue 上建立对应的 钩子函数 检查效果
- 数据发生改变后被监听器捕获,依次触发 beforeUpdate、updated 钩子函数
<template>
<div>
<ul id="appDemo">
<li v-for="item, index in arr" :key="index">{{ item }}</li>
</ul>
<button @click="arr.push(1)">增加值</button>
</div>
</template>
<script>
export default {
data() {
return {
arr: [1, 2, 3, 4],
}
},
/*
- beforeUpdate 是数据更新到 真实DOM 前的钩子函数
*/
beforeUpdate() {
console.log('准备更新');
// 虚拟DOM未更新到真实DOM 因此无法获取增加的值
console.log(document.querySelectorAll('#appDemo>li')[4]); // undefined
},
updated() {
/*
- beforeUpdate 是数据更新到 真实DOM 后的钩子函数
- 用于获取更新后的真实 DOM
*/
console.log('更新完成');
// 虚拟DOM完成更新到真实DOM阶段 因此可以取得最后更新的内容
console.log(document.querySelectorAll('#appDemo>li')[4]); // <li>1</li>
}
}
</script>
效果展示

例如 给组件绑定 v-if 并变更值为 false 的时候,触发销毁过程
销毁过程
- 当 $destroy() 被调用 – 例如组件DOM被移除(v-if)
- beforeDestroy 生命周期钩子函数被执行
- 拆卸数据监视器、子组件 和 事件监听器
- 实例销毁后,最后触发一个钩子函数
- destroyed 生命周期钩子函数被执行

销毁组件前到销毁组件完成 (销毁前→销毁后)
beforeDestroy() ➡️ destroyed()
销毁组件过程 原生DOM 也随之移除
实际操作
- 在 delvue.vue 上建立对应的 钩子函数,App.vue 根组件引用,并检查效果
- v-if 可以直接移除组件,触发 销毁过程 的生命周期,步骤依次为 beforeDestroy、 destroyed
- 多用于移除全局事件、定时器、当前页组件、(eventBus中 $off 移除全局自定义事件方法)
定义定时器后销毁组件 即使销毁成功后 定时器依然在占用后台资源
<!-- App.vue -->
<template>
<div>
<deldemo v-if="isLife"></deldemo>
<button @click="isLife = false">销毁组件</button>
</div>
</template>
<script>
import deldemo from './delvue.vue';
export default {
components: {
deldemo
},
data() {
return {
isLife: true,
}
},
}
</script>
<!-- delvue.vue -->
<template>
<div id="deldemo">
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
data() {
return {
msg: 'hello Vue',
timer: null,
}
},
// 组件创建完成后 创建定时器
created() {
this.tiemr = setInterval(() => { console.log('组件在运行'); }, 1000);
},
}
</script>
效果展示

正确的方式是 在销毁阶段前的 beforeDestroy 钩子函数下,应移除当前组件占用的全局资源
<template>
<div id="deldemo">
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
data() {
return {
msg: 'hello Vue',
timer: null,
}
},
created() {
// 组件创建完成后 创建定时器
this.timer = setInterval(() => {
console.log('组件在运行');
}, 1000);
},
beforeDestroy() {
console.log('准备销毁');
// 移除当前组件占用的全局资源
this.timer && clearInterval(this.timer);
this.timer = null;
},
destroyed() {
}
}
</script>
效果展示

Axios 是专门用于发送 ajax 请求的库
- ajax 是一种前端异步请求后端的技术 [ 参考 Ajax笔记 ]
- ajax请求 的底层使用的是 XMLhttpRequert 方式
- axios 是基于原生 ajax + Promise 技术封装通用于前后端的请求库
介绍

npm 下载
npm install axios
yarn 下载
yarn add axios
import 引用
import axios from 'axios';
Get 请求
一般 axios 是在 钩子函数 mounted() 完成数据的赋值,默认请求为 GET请求
下方演示为 查询指定书籍 的操作,parame为 GET 传递的参数
<template>
<div>
<input type="text" placeholder="输入要查询的书名" v-model="bookname">
<button @click="getFn">获取数据</button>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
bookname: '',
}
},
methods: {
getFn() {
console.log('获取数据');
// 发起 ajax请求
axios({
// 请求地址
url: 'http://123.57.109.30:3006/api/getbooks',
// 请求方式 (默认GET)
method: 'GET',
// 查询参数最终会拼接在 url? 后面
params: {
bookname: this.bookname,
}
})
// 返回结果后 返回 Promise 对象 用 then 接收
.then(res => { console.log(res); })
// 接收 错误结果
.catch(err => { console.log(err); });
}
}
}
</script>
效果展示

POST 请求
在 POST 请求中,传递的参数放置在请求体里,可在axios对象中 data 成员里挂载请求体内容
下方演示的为添加图书操作,axios 默认发给后台的请求体数据为 json字符串格式
<template>
<div>
<input type="text" placeholder="书名" v-model="obj.bookname">
<input type="text" placeholder="作者" v-model="obj.author">
<input type="text" placeholder="出版社" v-model="obj.publisher">
<br/>
<button @click="getFn">上传图书</button>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
obj:{
bookname:'',
author:'',
publisher:'',
}
}
},
methods: {
getFn() {
console.log('获取数据');
// 发起 ajax请求
axios({
// 请求地址
url: 'http://123.57.109.30:3006/api/addbook',
// 请求方式 (默认GET)
method: 'POST',
// 查询参数最终会拼接在 url? 后面
data:{
appkey:'7250d3eb-18e1-41bc-8bb2-11483665535a',
...this.obj,
}
})
// 返回结果后 返回 Promise 对象 用 then 接收
.then(res => { console.log(res); })
// 接收 错误结果
.catch(err => { console.log(err); });
}
}
}
</script>
效果展示

使用 axios.defaults.baseURL=”公共URL地址” 设置公共请求 url
- 配置 公共url地址 后,会在每次请求时进行 公共url + url路径 的拼接,axios.defaults.baseURL 相当于进行全局公共配置
- 该操作极大降低了维护成本,当后台地址发生变动时候,只需要修改 axios.defaults.baseURL 项目即可
import axios from 'axios';
axios.defaults.baseURL='http://123.57.109.30:3006';
export default {
methods: {
getFn() {
console.log('获取数据');
// 发起 ajax请求
axios({
// 请求地址
url: '/api/addbook',
// 请求方式 (默认GET)
...
})
// 返回结果后 返回 Promise 对象 用 then 接收
.then(res => { console.log(res); })
// 接收 错误结果
.catch(err => { console.log(err); });
}
}
}
请求参数
成员名 | 参数 | 介绍 |
---|---|---|
url | string | 用于请求的服务器 URL |
method | string | 创建请求时使用的方法 |
baseURL | string | url 公共链接 (将会拼接在所有 url 参数前面) |
params | object | 与请求一起发送的 URL 参数 |
data | object | 作为请求主体被发送的数据(只适用于这些请求方法 ‘PUT’, ‘POST’, 和 ‘PATCH’) |
paramsSerializer | function | 负责 `params` 序列化的函数 |
transformRequest | array | 允许在向服务器发送前,修改请求数据(只能用在 ‘PUT’, ‘POST’ 和 ‘PATCH’ 这几个请求方法) |
transformResponse | array | 在传递给 then/catch 前,允许修改响应数据 |
headers | object | 发送的自定义请求头,例如({‘X-Requested-With’: ‘XMLHttpRequest’}) |
timeout | number | 指定请求超时的毫秒数(0 表示无超时时间) |
withCredentials | bool | 表示跨域请求时是否需要使用凭证 |
adapter | function | 允许自定义处理请求,以使测试更轻松 |
auth | object | 应该使用 HTTP 基础验证,并提供凭据 |
responseType | string | 表示服务器响应的数据类型,可以是 ‘arraybuffer’, ‘blob’, ‘document’, ‘json’, ‘text’, ‘stream’ |
responseEncoding | string | 指示用于解码响应的编码 例如:’utf8′ |
xsrfCookieName | string | 用作 xsrf token 的值的cookie的名称 默认:’X-XSRF-TOKEN’ |
onUploadProgress | function | 允许为上传处理进度事件 参数1:progressEvent |
onDownloadProgress | function | 允许为下载处理进度事件 参数1:progressEvent |
maxContentLength | number | 定义允许的响应内容的最大尺寸 例如:2000 |
validateStatus | function | 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`), promise 将被 resolve; 否则,promise 将被 rejecte |
maxRedirects | number | 定义在 node.js 中 follow 的最大重定向数目(如果设置为0,将不会 follow 任何重定向) |
proxy | object | 定义代理服务器的主机名称和端口 |
响应结构
成员名 | 参数 | 介绍 |
---|---|---|
data | object | 由服务器提供的响应 |
status | number | 来自服务器响应的 HTTP 状态码 |
statusText | string | 来自服务器响应的 HTTP 状态信息 |
headers | object | 服务器响应的头 |
在vue的数据层中,有部分指令前缀为$,一般为 this.$指令,该指令的 this 需需注意指向
指令名 | 介绍 |
---|---|
this.$refs | 获取在标签上对应ref属性值的原生DOM(无论有多少个只能拿到一个) |
this.$set | 更新对应数组中下标成员值(参数1:数组目标 参数2:位置,参数3:更新后的值) |
this.$emit | 发送到对应自定义事件数据并触发事件(参数1:自定义事件,参数2:发送的参数…) |
this.$on | 接收对应自定义事件 返回的值(参数1:自定义事件,参数2:接收的参数…) |
this.$off | 删除对应自定义事件 如果没有提供参数,则移除所有的事件监听器 如果只提供了事件,则移除该事件所有的监听器 如果同时提供了事件与回调,则只移除这个回调的监听器 |
this.$nextTick | 由于数据更新是异步操作,$nextTick相当于获取更新完成后的数据(相当于updated()) |
获取在标签上对应ref属性值的原生DOM
在恰当的时机,例如在 mounted 钩子函数中获得需要的 原生DOM对象
<template>
<div>
<p ref="sayHi">Hello vue</p>
</div>
</template>
<script>
export default {
mounted() {
// 获取原生DOM
console.log(this.$refs.sayHi);
// 获取内容
console.log(this.$refs.sayHi.innerText);
}
}
</script>
效果展示

获取或执行子组件中的定义方法
在子组件标签中添加 ref 属性,使用 this.$refs.该属性.子属性方法 可以直接调用子组件方法
* 该方法较少用,一般仍然使用 $emit依赖自定义事件传参让父级执行方式通信 了解即可 *
// 父级组件
<template>
<div>
<demo ref="child"></demo>
</div>
</template>
<script>
import demo from './demo.vue'
export default {
components:{
demo
},
mounted() {
this.$refs.child.sayhi();
}
}
</script>
// 子级组件
<template>
<div>我是子组件</div>
</template>
<script>
export default {
methods:{
sayhi(){
console.log('我是子组件功能');
}
}
}
</script>
效果展示

数据更新完成后的回调函数
- 由于vue数据更新的操作是异步的,因此当值发生改变后,原生DOM方法 需要在 updated 中获取最新的内容
- this.$nextTick(()=>{ }) 让后续取值的操作 相当于在 updated() 函数体中操作
<template>
<div>
<p ref="num">{{ num }}</p>
<button @click="add">+1</button>
</div>
</template>
<script>
export default {
data() {
return {
num: 0
}
},
methods: {
add() {
this.num++;
/*
匿名函数的 this 指向的是 作用域区域 也就是组件对象
this.$nextTick 为数据更新完成后的回调函数
*/
this.$nextTick(() => {
// 使用 取原生DOM 方式 获取目标 DOM对象值
console.log(this.$refs.num.innerText);
})
}
}
}
</script>
效果展示
