一、如何引用(使用)vue
<!-- 生产模式 优点:拥有完整的错误提示 --> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<!-- 上线模式 优点:体积小,没有开发提示 --> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
二、去除控制台生产开发提示
Vue.config.productionTip = false;
三、去除控制台favicon.ico 404问题
- 由于vue底层会自动到根目录寻找favicon.ico图标,由于项目目录的子级没有找到该图标,所有导致404问题,
- 解决方案,下载一个图标,将图标名称改为favicon,并将其放入根目录下面
四、了解基本结构
<html>
<head>
<title>网页标题</title>
</head>
<body>
<div id="root">
</div>
<script type = "type/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el:"#root",
data:{
}
});
</script>
</body>
</html>
- #root:代表 将那个容器委托给vue管理
- el:vue要接管的容器,其中不仅仅可以为id选择器,css选择器的其他选择器都可以
- data:在vue中自定义的数据,并且可以在vue容器中访问到
- vue容器和vue实例是一一对应的
- {{xxx}}中的xxx要写js表达式,且xxx可以自动读取data中的所有子级属性,其他通过子级.孙级
- 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新
注意区分:js表达式 和 js代码(js语句)
表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
- a
- a+b
- demo(1) 必须要return
- x === y ? ‘a’ : ‘b’
js代码(js语句)
- if(){}
- for(){}
五、插值语法和指令语法
1、插值语法
功能:用于解析标签体内容
写法:{{xxx}},xxx是js表达式,其可以直接读取到data中的所有子级属性,其他通过子级.孙级
2、指令语法
功能:用于解析标签内容
举例:v-bind:href = “xxx” 简写方式 :href = “xxx” xxx同样要写js表达式
备注:Vue中可以有很多的指令,且形式都是:v-xxx
六、数据绑定
Vue中有两种绑定的方式:
单向绑定(v-bind、v-text):数据只能冲data流向页面
双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
- 双向绑定一般都应用于表单元素上(如:input、select、radio、checkbox等)
- v-mode:value 可以简写为v-model,因为v-model默认收集的就是value的值
七、el和data的两种绑定方式
-
el的两种绑定方式
el:“[css选择器]”
vm.$mount(“[css选择器]”);
-
data的两种绑定方式
data:{
}
//使用组件是不推荐使用es6函数定义方式
data(){
return{
xxx:“”
}
}
如果函数为以上那个形式那么this代表的是vue实例对象
如果函数为es6函数定义方式,那么this代表的是window
八、MVVM模式
View:视图 用于将数据显示给用户
Model:数据
Vm(ViewMode):用于监听页面数据变化,如页面变化那么vm所管理的数据也会变化(双向绑定)
用于将数据绑定至页面,一旦vm管的数据发生变化,那么页面的数据也随之变化
九、数据代理:通过一个对象代理对另一个对象中的属性的操作(读/写)
<script type="text/javascript">
Object,definProperty(person,'age',{
//value:18 //设置代理属性的值
//enumerable:true //控制属性是否可以被枚举(遍历获取该属性),默认值为false
//writable:true //控制属性是否可以被修改,默认为false
//configurable:true //控制属性是否可以被删除,默认值为false
//删除对象属性 delete object.xxx
});
</script>
<script type = "text/javascript">
let obj = {x:100}
let obj2 = {y:200}
Ojbect.defineProperty(obj2,'x',{
get(){
return obj.x;
},
set(value){
obj.x = value;
}
});
</script>
<!--
通过defineProperty方法实现简单的数据代理,让obj2代理obj.
obj2除了本身的y属性,还有代理的属性x
通过getter、setter函数,实现obj2和obj的x值同步(相等)
-->
个人理解vue代理:
vm.xxx 代理了 vm._data.xxx
vm._data 劫持了 代码中自定义的data
最终影响页面的变化
十、事件处理
事件的基本使用
- 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名称
- 事件的回调需要配置在methods对象中,最终会在vm实例上
- methods中配置的函数,不要使用es6箭头函数,否则this就是不是vue实例对象
- methods中配置的函数,都是被vue所管理的函数,this的指向是vm 或 组件实例对象
- @click = “demo” 和 @click = “demo($event)” 效果一致,但后者可以传递更多的参数
十一、事件修饰符
- prevent:阻止默认事件
- stop:阻止冒泡事件
- once:事件只触发一次
- capture:使用事件的捕获模式:先捕获后冒泡
- self:只有event.target是当前操作的元素才会触发事件
- passive:时间的默认行为立即执行,无需等待事件回调执行完毕
阻止冒泡的同时也阻止默认事件
@click.prevent.stop
十二、键盘事件
Vue中常用的按键别名
- 回车 => enter
- 删除 => delete (捕获“删除”和“退格”键)
- 退出 => esc
- 空格 => space
- 换行 => tab
- 上 => up
- 下 => down
- 左 => left
- 右 => right
Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转换为kebab-casecaps-lock(短横线命名)
系统修饰键(用法特殊):ctrl、alt、shift、win、table
- 配合keyup使用:按下修饰键的同时,在按下其他键,随后释放其他键,事件才能被触发
- 配合keydown使用:正常出发
也可以使用keyCode去指定具体的按键(不推荐)
Vue.config.keyCodes.自定义键名=键码,可以去定制按键别名
组合键使用:@keyup.ctrl.y
十三、计算属性
定义:要用属性的值不存在,要通过已有的属性计算得来
原理:底层借助了Object.defineproperty()方法提供的getter和setter。
getter函数什么时候执行?
出自读取时会执行一次
当依赖的数据发生变化时会被再次调用
优势:与methods实现相比,计算属性内部有缓存机制(复用),效率更高,调试方便
备注:
计算属性最终会出现在vm上,直接读取使用即可
如果计算属性要被修改,那么必须要写set行数去响应修改,且set中要引起计算时依赖的数据发生改变改变
修改计算属性 => setter改变依赖的属性 => 依赖属性发生改变 => 页面发生改变 == 调用getter
十四、属性侦听
一、普通侦听
- 当被侦听的属性变化时,回调函数自动调用,进行相关操作
- 侦听的属性必须存在,才能进行监视,可以监视data中的属性、computed中的计算属性。如果属性不存在也不会报错
- 侦听的两种写法
- new vue时传入watch配置
- 通过vm.$watch监视
<script>
//new vue时传入watch配置
const vm = new Vue({
el: '#root',
data: {
firstName:'张',
lastName: '三'
},
computed:{
fullName(){
return xxx;
}
},
watch:{
lastName:{
immediate:true,//初始化时让handler调用一下
handler(newValue,oldValue){
//newValue 代表修改之后的值
//oldValue 代表修改之前的值
}
}
}
});
//通过vm.$watch监视
vm.$watch('lastName',{
immediate:true,//初始化时让handler调用一下
handler(newValue,oldValue){
//newValue 代表修改之后的值
//oldValue 代表修改之前的值
}
});
</script>
二、深度侦听
- Vue中的watch默认不支持监测对象内部值的改变(一层)
- 配置deep:true可以监测对象内部值改变(多层)
备注:
- Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
- 使用watch时更具数据的具体结构,决定是否采用深度监视
<script>
const vm = new Vue({
el: '#root',
data: {
person:{name:"张三",age:"男"}
},
watch:{
//监听对象中具体的某个键
'person.name':{
handler(newValue,oldValue){
console.log("name改变了");
}
},
//监听对象中的所有键
person:{
deep:true,
handler:(newValue,oldValue){
console.log("person改变了");
}
}
}
});
</script>
十五、computed和watch之间的区别
- computed能完成的功能,watch都可以完成
- watch能完成的功能,computed不一定能完成,例如watch可以进行异步操作
两个重要的原则
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象
十六、动态绑定样式
一、绑定样式
- 写法:class=“xxx” xxx可以是字符串、数组、对象
- 字符串写法适用于:类名不确定,要动态获取
- 对象写法适用于:要绑定多个央视个数不确定,名字也不确定
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
- style样式
- :style=“{fontSize : xxx}” 其中xxx是动态值
- :style = “[a,b]” 其中a、b是样式对象
二、绑定案例
<html>
<header>
<title></title>
<style>
.line{
border:1px solid red;
}
.radius{
border-radius: 20px;
}
.bg-color{
background-color: gold;
}
.font{
font-size: 30px;
color: gray;
}
</style>
</header>
<body>
<div id="root">
<h1>class绑定</h1>
<div :class = "strs">字符串绑定样式</div>
<button @click="changeColor">点我改变样式</button>
<br><br>
<div :class = "cls">对象绑定</div>
<br><br>
<div :class = "clsArr">数组绑定</div>
<br><br>
<h1>style行内样式绑定</h1>
<div :style="obj">对象绑定</div>
<br><br>
<div :style="arr">数组方式绑定</div>
</div>
<script type="text/javascript" src="vue.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:"#root",
data:{
strs:"font",
cls:{
font:true,
radius:true,
'bg-color':true
},
clsArr:["radius","bg-color"],
obj:{
fontSize:"18px",
color:"red",
backgroundColor:"gray"
},
arr:[
{
fontSize:"18px"
}
,{
color:"gold",
backgroundColor:"skyblue",
borderRadius:"30px"
}
]
},
methods:{
changeColor(){
this.strs = 'bg-Color';
}
}
});
</script>
</body>
</html>
三、注意事项
- 动态绑定class,需要事先准备好对应的Css样式,动态绑定style不需要事先准备样式
- 如果css原名中带有横线,需要使用小驼峰命名。如( background-color ==> backgroundColor )
- 通过数组方式动态绑定class,数组中放的是实现定义好的样式名称字符串,也可以放对象
- 通过数组方式动态绑定style,数组中放的样式是对象,对象中的键必须是原生css中存在的
十七、条件渲染(v-show和v-if)
- v-if
写法
- v-if = “表达式”
v-else-if = “表达式”
v-else
什么时候用?切换频率较低的场景
特点:不满足条件的dom元素直接移除
注意:v-if可以和v-else-if、v-else一起使用,当要求结构不能打断(if结构中不能添加其他dom)
- v-show
写法:v-show=“表达式”
什么时候用:切换频率较高的场景
特点:不展示的dom未被移除,仅仅是使用display样式显示、隐藏
- 备注:使用v-if的时,元素可都能无法获取到,而使用v-show一定可以获取到(原因:一个是移除dom,一个是显示隐藏dom)
十八、v-if和template的配合使用
<html>
<header>
<titile></titile>
</header>
<body>
<div id="root">
<h1>使用Template实现</h1>
<button @click="isShowTemplate=!isShowTemplate">点我隐藏全部</button>
<template v-if = "isShowTemplate">
<div>打篮球</div>
<div>打羽毛球</div>
<div>踢足球</div>
</template>
<br><br>
<h1>通过改变原有结构实现实现</h1>
<button @click="isShowStruck=!isShowStruck">点我隐藏全部</button>
<div v-if = "isShowStruck">
<div>打篮球</div>
<div>打羽毛球</div>
<div>踢足球</div>
</div>
</div>
<script type = "text/javascript" src="vue.js"></script>
<script type = "text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el:"#root",
data:{
isShowTemplate:true,
isShowStruck:true
}
});
</script>
</body>
</html>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4zg9xCqs-1651027848981)(https://note-photo-1257322974.cos.ap-shanghai.myqcloud.com/Vue/v-if%E5%92%8C%E6%A8%A1%E6%9D%BF%E9%85%8D%E5%90%88%E4%BD%BF%E7%94%A8.jpg?q-sign-algorithm=sha1&q-ak=AKIDGJzRej2oshI1Uha9QSyHgqkOs7Ay7gKn&q-sign-time=1638148900;172800001638148900&q-key-time=1638148900;172800001638148900&q-header-list=&q-url-param-list=&q-signature=055b852ac5de7acf1c7a57054f54ecd7506c3b9f)]
十九、列表渲染
-
v-for指令
- 用于展示列表数据
- 语法:v-for = “(item,index) in xxx” :key=“yyy”(一般为xxx.id)
- 可遍历:数组、对象、(字符串、指定次数)[用的很少]
二十、深度了解Key
- key有什么作用(key的内部原理)
- 虚拟Dom中key的作用
- key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的xuniDOM】
- 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较
- 对比规则
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若虚拟DOM中内容没变,直接使用之前的真实DOM
- 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换页面中之前的真实DOM
- 旧虚拟DOM中未找到与新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到页面
- 用Index作为key可能会引发的问题
- 若数据进行:逆序添加、逆序删除等破环顺序操作
- 会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低(真实DOM没有重用)
- 如果结构中还包含输入类的DOM(input、textare等)
- 会产生错误DOM更新 ==> 界面有问题
- 开发中如何选择key?
- 最好使用每条数据的标识作为key,如id、手机号码、身份证等唯一标识
- 如果不存在数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index座位key是没有问题的
- 逆序:不在对象数组后面追加,而是在开始部分或中间部分追加数据,导致数据顺序发生变化
二十一、Vue监视数据原理
-
vue会监视data中所有层次的数据
-
如何监视对象中的数据
通过setter实现监视,且要在new Vue时就传入要监测的数据
(1)、对象中后面追加的属性,vue默认不做响应式处理
(2)、如需给后添加的属性做响应式,使用以下api
Vue.set(target,propertyName/index,value); vm.$set(target,propertyName/index,value);
-
如何监测数组中的数据
通过包裹(封装)数组更新元素的方法实现,本质上就是做了两件事
(1)、调用原生对应的方法对数组进行更新
(2)、重新解析模板,进而更新页面数据
-
在Vue修改数组中的某个元素一定要用如下方法
- 使用这些API:push()、pop()、 shift()、 unshift()、 splice()、 sort()、 reverse()
- Vue.set() 和 vm.$set()
特别注意:不能给vm 或 vm的根数据(vm、_data)对象添加属性
二十二、收集表单数据
若:则v-model收集的是value值,用户输入的就是value值
若:则v-model收集的是value值,且要给标签配置value
若
- 没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选 是布尔值)
- 配置input的value属性
- v-model的初始值是非数组,那么收集的就是checked
- v-model的初始值是数组,那么收集的就是value组成的数组
备注:v-model的三个修饰符
lazy:控件失去焦点之后开始收集数据
number:输入字符串转为有效数组
trim:去除首尾的空字符串
二十三、过滤器
定义:对要显示的数据进行特定格式化后在显示(适用于一些简单的逻辑处理)
语法:
- 注册过滤器:Vue.filter(name,callback); 或 new Vue(filters:{})
- 使用过滤器:{{xxx | 过滤器名称}} 或 v-bind:属性=“xxx | 过滤器名称”
备注:
- 过滤器也可以接受额外参数,多个过滤器也可以串联
- 并没有改变原有的数据,是产生新的对应的数据
二十四、内置指令
1、v-html指令
- 作用:向指定节点中渲染包含html结构的内容
- 与插值语法的区别
- v-html 会替换掉节点中的所有内容,{xx}则不会
- v-html可以识别html
- 严重注意:v-html有安全性问题
- 在网站上动态渲染任意html是非常危险的,容易导致xss攻击
- 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上
2、v-cloak(没有值)
- 本质就是一个特殊属性,Vue实例创建完毕并接管容器后,会删除v-cloak属性
- 使用css配合v-cloak可以解决网速慢时页面展示{{xxx}}的问题
3、v-once
- v-once所在节点在初次动态选然后,就是为静态内容了
- 以后数据的改变就不会引起v-once所在结构的更新,可以用于优化性能
4、v-pre指令
- 跳过其所在节点的编译过程
- 可以用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
5、自定义指令
- 定义语法
- 局部指令
```javascript new Vue({ directives:{指令名:配置对象} }) ```
new Vue({ directives(){
} })
- 配置对象中常用的3个回调
- bind:指令与元素成功绑定时调用
- inserted:指令所在元素被插入页面时调用
- update:指令所在模板结构被重新解析时调用
- 备注
- 指令名定义时不要加v-,但调用时要加v-
- 指令名如果是多个单词,要使用xxx-xxx(横杠隔开)命名方式,不要用xxXxx(驼峰)命名
二十五、什么是Vue的生命周期
- 又名:生命周期回调函数、生命周期函数、生命周期钩子
- 是什么:Vue在关键时刻帮我们调用一些特殊名称的函数
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
- 生命周期函数中的this指向的是vm 或 组件实例对象
- 常用的生命周期狗子
- mounted:发送ajax请求,启动定时器,绑定自定义事件,订阅消息等(初始化操作)
- beforeDestroy:清除定时器、解绑自定义事件,取消订阅消息等(收尾工作)
- 关于销毁Vue实例
- 销毁后借助Vue开发者工具看不到任何消息
- 销毁后自定义事件会失效,但原生dom事件依然有效
- 一般不会再beforeDestroy操作数据,因为即便操作事件,也不会触发更新流程了
二十六、初认组件
- Vue使用组件的三大步骤
- 定义组件
- 注册主键
- 使用组件
- 如何定义一个组件
- 使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样但也有点区别
- 区别如下
- el不要写,为什么?----- 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务那个容器
- data必须写成函数,为什么?----- 避免组件被复用时,数据存在应用关系(数据相互影响)
- 备注:使用template可以配置组件结构
- 如何注册组件
- 局部注册:靠new Vue的时候传入components选项
- 全局注册:靠Vue.component(‘组件名’,组件)
- 如何使用组件
- <组件名></组件名>
二十七、组件的几个注意点
关于组件名
一个单词组成:
- 首字母小写:school
- 首字母大写:School
多个单词组成
- kebab-case命名:my-school
- CamelCase命名:MySchool(需要脚手架支持)
备注
- 组件名尽可能回避HTML中已有的元素名称,例如:H2、h2都不行
- 可以使用name配置之项指定组件在开发者工具中呈现的名字
关于组件标签
<!-- 成双成对 --> <school></school>
<!-- 自闭和 不使用脚手架会导致后续组件不能渲染--> <school/>
创建组建的简写方式
const school = Vue.extend(options); //const school = Vue.extend({}) const school = options; //const school = {}
二十八、关于VueComponent
- school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
- 我们只需要写或,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
- 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent
- 关于this指向
- 组件配置中:
- data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是【VueComponent实例对象】。
- new Vue(options)配置中:
- data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是【Vue实例对象】
- VueComponent的实例对象,以后简称vc(也可以称之为组件实例对象)
- Vue的实例对象,以后简称vm
二十九、关于不同版本的vue
- vue.js与vue.runtime.xxx.js的区别
- vue.js是完整版的vue,包含:核心功能+模板解析器
- vue.runtime.xxx.js是运行版的vue,只包含核心功能,没有模板解析器
- 因为vue.runtime.xxx.js没有模板解析器,所有不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容
三十、ref属性
被用来给元素或子组件注册引用信息(id的替代者)
应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
使用方式
设置标识:
<h1 ref = "xxx"></h1> <School ref="xxx"></School>
获取标识
this.$refs.xxx
三十一、配置项props
功能:让组件接受外部传过来的数据
- 传递数据
<Demo name="xxx"></Demo>
接收数据
简单接收
props:['name']
限制类型
props:{ name:String }
限制类型,限制必要性,指定默认值
props:{ name:{ type:String, //类型限制 required:true, //必要性,不能不传递 default:'老王' //默认值 } }
备注:
props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
export default{ name:"xxxComp", data:{ tempName:this.name }, props:['name'], methods:{ updateName(){ this.tempName = this.name; } } }
三十二、mixin(混合\混入)
功能:可以把多个组件公用的配置提取成一个混入对象
使用方式如下
- 第一步定义混合
{ data(){}, methods:{}, ....... }
- 第二步使用混合
//引入 import {'x1','x2'} from '../mixin' //全局混合 Vue.mixin(x1) Vue.mixin(xx) //局部混合 new Vue({ mixins:['x1','x2'] });
三十三、使用插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
定义插件
对象.install = function(){
//添加全局过滤器
Vue.filter()
//添加全局指令
Vue.directive()
//配置全局混入
Vue.mixin()
//......
}
也可以是
export default {
install(){
//添加全局过滤器
Vue.filter()
//添加全局指令
Vue.directive()
//配置全局混入
Vue.mixin()
//......
}
}
使用插件
//必须在vm创建前使用插件
//一、引用插件
import plugins from './plugins'
//二、使用
Vue.use(plugins);
new vm({
});
三十四、TodoList案例总结
- 组件化编码流程
- 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突
- 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件再用
- 一个组件在用:放在组件自身即可
- 一些组件在用:放在它们共同的父组件上(状态提升)
- 实现交互:从绑定事件开始
- props适用于
- 父组件 ==> 子组件 通信
- 子组件 ==> 父组件 通信(要求父组件先给子组件传递一个要操作的函数)
- 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
- props传过来的若是对象类型的值,修改对象中的属性是Vue不会报错,当时不推荐这么做
三十五、组建的自定义事件
一种组件间通信的方式,适用于子组件 ==> 父组件
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件回调在A中定义)
绑定自定义事件
第一种方式
<Demo @atguigu="test"/> //或者 <Demo v-on:atguigu="test"/>
第二种方式
<Demo ref="demo"/> //js mounted(){ //test回调函数必须先配置好 this.$regs.demo.$on('atguigu',this.test); }
若想让自定义事件只能触发一次,可以使用once修饰符,或者$once方法
触发自定义事件
this.$emit('atguigu',数据)
- 解绑自定义事件
this.$off('atguigu') //多个 this.$off(['atguigu','other'])
组件上也可以绑定原生DOM事件,需要使用native修饰符
注意:通过this. r e f s . x x x . refs.xxx. refs.xxx.on(‘atguigu’,回调)绑定自定义事件时,回调要么在methods中,要么用箭头函数,否者this指向会有问题
三十六、全局事件总线
一种组件通信的方式,使用于任意组件间通信
安装全局事件总线
new Vue({ ...... beforeCreate(){ Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm } })
使用事件总线
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
methods(){ demo(data){} } ...... mounted(){ this.$bus.$on('xxx',this.demo); }
提供数据:
this.$bus.$emit('xxx',data);
最好在beforeDestroy钩子中,用$off去解绑当前组件中所用到的自定义事件
三十七、消息订阅与发布
一种组件间通信的方式,适用于任意组件间通信
使用步骤
安装pubsub:
npm i pubsub-js
引入:
import pubsub from 'pubsub-js'
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
methods(){ demo(msgName,data){......} } mounted(){ this.pid = pubsub.subscribe('xxx',this.demo); //订阅消息 }
提供消息
pubsub.publish('xxx',data)
最好在beforeDestroy钩子中,用pubsub.unsubscribe(pid)去取消订阅
三十八、nextTick
- 语法
this.$nextTick(回调函数);
作用:在下一次DOM更新结束后执行器指定的回调
什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
三十九、Vue封装的过度和动画
作用:再插入、更新或移除DOM元素时,再合适的时候给元素添加样式类名
图示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5SyYa2mf-1651027848982)(https://note-photo-1257322974.cos.ap-shanghai.myqcloud.com/Vue/%E5%8A%A8%E7%94%BB%E5%92%8C%E8%BF%87%E6%B8%A1.png?q-sign-algorithm=sha1&q-ak=AKIDGJzRej2oshI1Uha9QSyHgqkOs7Ay7gKn&q-sign-time=1649919012;86401649832612&q-key-time=1649919012;86401649832612&q-header-list=&q-url-param-list=&q-signature=63ad051eca81d36c611119e825d85162f41e8fa9)]
写法
准备好样式
- 元素进入的样式
- v-enter:进入的起点
- v-enter-active:进入过程中
- v-enter-to:进入的终点
- 元素离开的样式
- v-leave:离开的七点
- v-leave-active:离开过程中
- v-leave-to:离开的终点
使用
transition
包裹要过度的元素,并配置name属性<transtion name="hello"> <h1 v-show="isShow">你好啊</h1> </transtion>
备注:若有多个元素需要过度,则需要使用:
<transition-group>
,且每个元素都要指定key值其他补充
如果transition没有声明name属性 那么使用v-xxx,反之用 name-xxx
如何使用animation.css
安装animation.css
npm install animation.css
引用animation.css
import 'animation.css'
使用animation.css
<transition appear name="animate_animated animate_bounce" enter-active-class="xxxx" leave-active-class=""> </transition> <!-- appear:代表一开始就应用动画或过渡样式 name:(动画、过渡)名称 enter-active-class:动画进入样式 enter-leave-class:动画离开样式 --!>
四十、Vue脚手架配置代理(解决跨域问题)
- 简单配置
devServer:{ proxy:"http://localhost:5000" }
说明
- 优点:配置简单,请求资源时直接发给前端(8080)即可
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么请求会转发给服务器(优先匹配前端资源)
- 配置多个代理
module.exports = { devServer: { proxy: { '/api': { //匹配所有以 'api'开头的请求路径 target: '<url>', //代理目标的基础路径 ws: true, //是否支持websocket changeOrigin: true, //是否对服务器隐瞒自己的真实请求端口 pathRewrite: {'^/api',''} //将请求地址中的api替换为空字符 }, '/foo': { //匹配所有以 'api'开头的请求路径 target: '<url>', //代理目标的基础路径 ws: true, //是否支持websocket changeOrigin: true, //是否对服务器隐瞒自己的真实请求端口 pathRewrite: {'^/api',''} //将请求地址中的api替换为空字符 } } } }
说明
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理
- 缺点:配置略微繁琐,请求资源是必须加前缀
四十一、插槽
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===>子组件
分类:默认插槽、具名插槽、作用域插槽
使用方式
默认插槽
<!-- 父组件中 --> <Category> <div> html结构一 </div> </Category> <!-- 子组件中 --> <template> <div> <!-- 定义插槽 --> <slot>插槽默认内容...</slot> </div> </template>
- 具名插槽
<!-- 父组件中 --> <Category> <template slot="center"> <div> html结构1 </div> </template> <!-- v-slot属性只能用与template标签上,其他标签无法使用 --> <template v-slot:footer> <div> html结构2 </div> </template> </Category> <!-- 子组件中 --> <template> <div> <!-- 定义插槽 --> <slot name="center">插槽默认内容...</slot> <slot name="footer">插槽默认内容...</slot> </div> </template>
作用域插槽
理解:数据在组建的自身,但根据数据生成的结构需要组建的使用者来决定。(games数据在Category组件中,但使用数据所遍历里出来的结构由App组件决定)
具体编码
<!-- 在父组件中 -->
- {{g}}
<Category> <template slot-scope="scopeData"> <h4 v-for="g in scopeDate.games" :key="g"></h4> </template> </Category> <!-- 在子组件中 --> <template> <div> <slot :games="games"></slot> </div> </template> <script> export default { name:'Category', props:['title'], data(){ return { games:['红色警戒','穿越火线','劲舞团','超级玛丽'] } } } </script> ```
注意:
scope="{games}"
接收的就是子组件转递的参数,而slot-scope或scope接收到的是一个对象,该对象中包含子组件传递过来的参数,区别如下<!-- 子组件 --> <template> <div> <slot :games="games" msg="你好"></slot> </div> </template> export default { name:'StudentComp', data(){ return { games:['穿越火线','QQ飞车','地下城与勇士'] } } }
<!-- 父组件(使用者) --> <!-- scope这个属性可以使用但已经过时 作用域属性必须定义在template标签上--> <StudentComp > <!-- {games} 代表一个对象games 如果要获取子组件的games 那么games.games --> <template scope="games"> <ul> <li v-for="(item) in games.games" :key="item">{{item}}</li> </ul> </template> </StudentComp> <StudentComp > <!-- {games} 代表一个对象games 如果要获取子组件的games 那么games.games --> <template slot-scope="games"> <ol> <li v-for="(item) in games.games" :key="item">{{item}}</li> </ol> </template> </StudentComp>
<StudentComp > <!-- {games} 代表着子组件中的games --> <template slot-scope="{games}"> <h4 v-for="(item) in games" :key="item">{{item}}</h4> </template> </StudentComp> ```
四十二、Vuex
在vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组建的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信
多个组件需要共享数据时
//引入Vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex插件 Vue.use(Vuex); //准备actions对象 ==> 响应组件中用户的动作 const actions = {} //准备mutations对象 ==> 修改state中的数据 const mutations = {} //准备state对象 ==> 保存具体的数据(状态) const state = {} //创建并暴露store export default new Vuex.Store({ actions, mutations, state })
2. 在```main.js```中创建vm时传入```store```配置项
//引入store.js 如果文件名称为index.js 那么脚手架会去该目录下寻找 index.js import store from './store' //创建vm new Vue({ el:'el', reder: h => h(App), store //如果key和value重名,写一个就行 })
//引入Vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex插件 Vue.use(Vuex); //准备actions对象 ==> 响应组件中用户的动作 const actions = { jia(minStore,value){ minStore.commit('JIA',value); } } //准备mutations对象 ==> 修改state中的数据 const mutations = { JIA(state,value){ state.sum += value; } } //准备state对象 ==> 保存具体的数据(状态) const state = { sum:0 } //创建并暴露store export default new Vuex.Store({ actions, mutations, state })
组件中读取vuex中的数据:
$store.state.sum
组件中修改vuex中的数据:
$store.dispatch('actions中的方法名称',数据)
或$store.commit('mutations中的方法名称',数据)
备注
若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写
dispatch
,直接编写commit
getters的使用
概念:当state中的数据需要经过加工后在使用,可以使用getters加工
在
store.js
中追加getters
配置const getters = { bigSum(state){ return state.sum*10; } } //创建并暴露store export default new Vuex.Store({ ..... getters, })
四个map方法的使用
mapState方法:用于帮助我们映射state中的数据为计算属性
import {mapState} from vuex computed:{ //借助mapState生成计算属性:sum、school、subject(对象写法) ...mapState({sum:'sum',school:'school',subject:'subject'}) //借助mapState生成计算属性:sum、school、subject(数组写法) ...mapState(['sum','school','subject']) }
mapGetters方法:用于帮助我们映射getters中的数据为计算属性
import {mapGetters} from vuex computed:{ //借助mapGetters生成计算属性:bigSum(对象写法) ...mapGetters({bigSum:'bigSum'}) //借助mapGetters生成计算属性:bigSum(数组写法) ...mapGetters(['bigSum']) }
mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)函数
import {mapActions} from vuex methods:{ //靠mapActions生成:incrementOdd、incrementWait(对象形式) ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) // //靠mapActions生成:incrementOdd、incrementWait(数组形式) ...mapActions(['jiaOdd','jiaWait']) }
mapMutations方法:用于帮助我们生成与
mutations
对话的方法,即:包含$store.commit(xxx)
的函数import {mapMutations} from vuex methods:{ //靠mapMutations生成:incrementOdd、incrementWait(对象形式) ...mapMutations({incrementOdd:'JIA',incrementWait:'JIAN'}) // //靠mapActions生成:JIA、JIAN(数组形式) ...mapMutations(['JIA','JIA']) }
模块化+命名空间
目的:让代码更好维护,让多种数据分类更加明确
修改store.js
const countAbout = { namespaced:true, //开启命名空间 state:{x:1}, mutations:{....}, actions:{....}, getters:{ bigSum(state){ return state.sum * 10 } } } const personAbout = { namespaced:true, //开启命名空间 state:{x:1}, mutations:{....}, actions:{....}, getters:{ bigSum(state){ return state.sum * 10 } } } const store = new Vuex.Store({ modules:{ countAbout, // 不简写 ==> countAbout:countAbout personAbout // 不简写 ==> personAbout:personAbout } })
开启命名空间后,组件中读取
state
数据//方式一,自己直接读取 this.$store.state.personAbout.list //方式二,借助mapState读取 ...mapState('countAbout',['sum','school','subject'])
开启命名空间后,组件中读取
getters
数据//方式一,自己直接读取 this.$store.getters['personAbout/firstPersonName'] //方式二,借助mapGetters读取 ...mapGetters("countAbout",['bigSum'])
开启命名空间后,组件中调用
dispatch
即actions
//方式一,自己直接读取 this.$store.dispatch['personAbout/addPersonWang'] //方式二,借助mapActions读取 ...mapActions("countAbout",{incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) //方式三、名称一致的情况下可以使用以下这种方式
…mapActions(“countAbout”,[‘jiaOdd’,‘jiaWait’])
6. 开启命名空间后,组件中调用```commit```即 ```mutations``` ```js //方式一,自己直接读取 this.$store.commit['personAbout/ADD_PERSON',person] //方式二,借助mapMutations读取 ...mapMutations("countAbout",{increment:'JIA',decrementWait:'JIAN'})
四十三、Vue-Router(路由)
一、基本使用
安装vue-router,命令
npm i vue-router
应用插件:
Vue.use(VueRouter)
编写router配置项
//引入VueRouter import VueRouter from 'vue-router' //引入路由组件 import About from '../components/About' import Home from '../components/Home' //创建router实例对象,去管理一组一组的路由规则 const router = new VueRouter({ routers:[ { path:'/about', component:About }, { path:'/home', component:Home } ] })
- 实现切换(active-class可配置高亮样式)
<router-link active-class='active' to='/about'>About</router-link>
- 指定展示位置
<router-view></router-view>
二、几个注意点
路由组件通常放在
pages
文件夹,一般组件通常放在components
文件夹通过切换,“隐藏”了的路由组件,默认是被销毁了,需要的时候再去挂载
每个组件都有自己的
$route
属性,里面存储着自己的路由信息整个应用只有一个router,可以通过组件的
$router
属性获得```js //方式一,自己直接读取 this.$store.dispatch['personAbout/addPersonWang'] //方式二,借助mapActions读取 ...mapActions("countAbout",{incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) //方式三、名称一致的情况下可以使用以下这种方式
…mapActions(“countAbout”,[‘jiaOdd’,‘jiaWait’])
```
开启命名空间后,组件中调用
commit
即mutations
//方式一,自己直接读取 this.$store.commit['personAbout/ADD_PERSON',person] //方式二,借助mapMutations读取 ...mapMutations("countAbout",{increment:'JIA',decrementWait:'JIAN'})
四十三、Vue-Router(路由)
一、基本使用
安装vue-router,命令
npm i vue-router
应用插件:
Vue.use(VueRouter)
编写router配置项
//引入VueRouter import VueRouter from 'vue-router' //引入路由组件 import About from '../components/About' import Home from '../components/Home' //创建router实例对象,去管理一组一组的路由规则 const router = new VueRouter({ routers:[ { path:'/about', component:About }, { path:'/home', component:Home } ] })
- 实现切换(active-class可配置高亮样式)
<router-link active-class='active' to='/about'>About</router-link>
- 指定展示位置
<router-view></router-view>
二、几个注意点
- 路由组件通常放在
pages
文件夹,一般组件通常放在components
文件夹- 通过切换,“隐藏”了的路由组件,默认是被销毁了,需要的时候再去挂载
- 每个组件都有自己的
$route
属性,里面存储着自己的路由信息- 整个应用只有一个router,可以通过组件的
$router
属性获得- 所有路由组件的
$route
属性是不同的,而$router
是一样的