Vue基础入门(持续更新)
1、vue特点
- 组件化开发,提高代码复用率,代码更好维护
- 声明式编码,无需直接操作dom,提高开发效率
- 虚拟dom+diff算法
2、数据代理(Object.defineProperty)
Vue监视数据的原理:
-
vue会监视data中所有层次的数据;
-
如何监测对象中的数据?
通过setter实现监测,且要在new Vue时就传入要监测的数据。
- 对象中,后追加的属性,Vue默认不做响应式处理
- 如需给后添加的属性做响应式,需使用Vue.set或vm.$set
-
如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
- 调用原生对应的方法对数组进行更新。
- 重新解析模板,进而更新页面。
-
再Vue修改数组中的某个元素一定要用如下方法:
- 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set()不能给vm或vm的根数据(vm._data )对象添加属性!!!
2.1、数据代理的基本使用方法
const obj = {
name:'张三',
sex:'男'
}
let num = 18
//基本使用方法,给obj设置一个属性age,属性值为18
Object.defineProperty(obj,'age',{
//枚举:不可枚举的属性,不能通过遍历等方式获取到属性
enumerable:true,//控制代码属性是否可以枚举,默认false
writable:true,//控制代码属性是否可以被修改,默认false
configurable:true,//控制代码属性是否可以被删除,默认false
value:18
})
//使用getter和setter设置属性
Object.defineProperty(obj,'age',{
//枚举:不可枚举的属性,不能通过遍历等方式获取到属性
enumerable:true,//控制代码属性是否可以枚举,默认false
writable:true,//控制代码属性是否可以被修改,默认false
configurable:true,//控制代码属性是否可以被删除,默认false
//每次读取obj对象的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get(){
return num
},
//每次修改obj对象的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value){
num = value
}
})
console.log(obj)//{name:'张三',sex:'男',age:18}
2.2、vue中的数据代理
-
Vue中的数据代理:
通过vm对象来处理data对象中的属性的操作(读/写)
-
Vue中数据代理的好处
更加方便的操作data中的数据
-
基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter
在getter/setter内部去操作(读/写)data中对应的属性
2.3、Vue监听数据改变的原理
const vm = new vue({
el:'#root',
data:{
name:'张三',
age:1
}
})
console.log(vm);
function vue(options){
this._data = new Observer(options.data)
Object.keys(this._data).forEach((key) => {
Object.defineProperty(this, key, {
enumerablefalse:true,
configurabletrue:true,
get() {
return this._data[key];
},
set(newVal) {
this._data[key] = newVal;
}
});
});
}
function Observer(obj){
let keys = Object.keys(obj)
keys.forEach(key=>{
Object.defineProperty(this,key,{
enumerable:true,//控制代码属性是否可以枚举,默认false
configurable:true,//控制代码属性是否可以被删除,默认false
get(){
return obj[key]
},
set(value){
console.log('修改了'+key+'属性');
obj[key] = value
}
})
})
}
2.3、Vue.set()
new Vue({
el:'#root',
data:{
obj:{id:4,name:"温兆伦",age:18},
},
created:{
//Vue.set(this.obj,'sex','男')
this.$set(this.obj,'sex','男')
}
})
3、事件修饰符
- prevent:阻止默认事件
- stop:阻止时间冒泡
- once:事件只触发一次
- capture:使用事件的捕获模式
- self:只有event.target是当前操作的元素时才触发事件
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
- native:用于给组件绑定原生dom事件
//1、prevent:阻止默认事件
<a herf="www.baidu.com" @click.prevent='show'>点我打开弹窗</a>
//2、stop:阻止事件冒泡
<div @click='show1'>
<div @click.stop='show2'></div>
</div>
//3、once:事件只触发一次
<div @click.once='show1'></div>
//4、capture:使用事件的捕获模式
//事件执行时,是先由外向内捕获事件,然后才是冒泡,而事件是在冒泡阶段执行的,
//capture就是在事件捕获阶段就开始执行
//5、self:只有event.target是当前操作的元素时才触发事件
<div @click.self='show1'>
<div @click='show2'></div>
</div>
//只有点击show1事件所绑定的dom元素时,才会执行show1事件,点击show2时,并未点击到show1,所以在点击show2时并不会通过冒泡执行show1,从某种程度上来说,同样可以阻止事件冒泡
//6、passive:事件的默认行为立即执行,无需等待事件回调执行完毕
//7、native:用于给组件绑定原生dom事件
<HelloWorld @click.native='show1'/>
new Vue({
el:'#root',
data:{
name:'张三'
},
methods:{
show(){
alert("0000")
},
show1(){
alert("1111")
},
show2(){
alert("2222")
},
}
})
4、键盘事件
-
Vue中常用的按键别名:
回车 => enter
删除 => delete 退出 => esc 空格 => space 换行 => tab(特殊,必须配合keydown使用) 上 => up 下 => down 左 => left 右 => right -
Vue未提供别名的按键,可以通过按键原始的key值去绑定,但注意要转为kebab-code(短横线命名)
<input @keyup='show(11,$event)'/> new Vue({ el:'#root', data:{ name:'张三' }, methods:{ show(a,event){ //event.key为键名,event.keyCode为按键编码 //假设要为大小写的按键CapsLock添加按键事件 //这由多个单词组成的名字,在html中绑定事件时就要注意,转为kebab-code //<input @keyup.caps-lock='show(11,$event)'/> console.log(event.key,event.keyCode) }, } }) -
系统修饰键(用法特殊):ctrl、alt、shift、win
- 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
- 配合keydown使用:正常触发事件
//只有按ctrl+c时才触发 <input @keyup.ctrl.c='show(11,$event)'/> <input @keydown='show(11,$event)'/> -
也可以使用keyCode去指定具体的按键
<input @keyup='show(11,$event)'/> new Vue({ el:'#root', data:{ name:'张三' }, methods:{ show(a,event){ //keyCode为13时为enter if(event.keyCode !== 13) return }, } }) -
Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
Vue.config.keyCodes.huiche = 13
<input @keyup.huiche='show(11,$event)'/>
5、计算属性
- 定义:要用的属性不存在,要通过已有属性计算得来。
- 原理:底层借助了Object.defineproperty方法提供的getter和setter。
- get函数什么时候执行?
- 初次调用时会执行一次
- 当依赖的数据发生改变时会被再次调用
- 优势:内部有缓存机制(复用),效率更高,调试方便
- 备注:
- 计算属性最终会出现在vm上,直接读取使用即可
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
5.1、完整写法
new Vue({
el:'#root',
data:{
name1:'张',
name2:'三'
},
computed:{
fullName:{
//当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
//调用时机:1、初次调用的时候;2、数据发生变化时
get(){
return this.name1 + '-' + this.name2
},
set(value){
let arr = value.split('-')
this.name1 = arr[0]
this.name2 = arr[1]
}
}
}
})
5.2、简写
new Vue({
el:'#root',
data:{
name1:'张',
name2:'三'
},
computed:{
//fullName:{
// //当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
// //调用时机:1、初次调用的时候;2、数据发生变化时
// get(){
// return this.name1 + '-' + this.name2
// },
// set(value){
// let arr = value.split('-')
// this.name1 = arr[0]
// this.name2 = arr[1]
// }
//}
//当代码需求不需要set函数来对数据进行修改时,可以省略set函数
//fullName可以省略为一个函数,这个函数就代表着get函数
fullName(){
return this.name1 + '-' + this.name2
}
//以上代码等同于:
//fullName:{
// get(){
// return this.name1 + '-' + this.name2
// }
//}
}
})
6、监听属性
- 当被监视的属性变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种写法:
- new Vue时传入watch配置
- 通过vm.$watch监视
- watch可以监听计算属性
- 深度监听:
- Vue中的watch默认不监听对象内部值的改变
- 配置deep:true可以监听对象内部值改变
- 备注:
- Vue自身可以监听对象内部值的改变,但Vue提供的watch默认不可以
- 使用watch时根据数据的具体结构,决定是否采用深度监听
6.1、完整写法
//组件内写法
const vm = new Vue({
el:'#root',
data:{
isShow:true,
num:{
a:1,
b:2
}
},
watch:{
//监听的属性可加引号,也可不加
'isShow':{
immediate:true, //初始化时让handler调用一次
//handler
handler(newValue,oldValue){
//newValue新改变的值,oldValue改变之前的值
console.log(newValue,oldValue)
}
},
//监听多级结构中某个属性的变化
'num.a':{
immediate:true, //初始化时让handler调用一次
//handler
handler(newValue,oldValue){
//newValue新改变的值,oldValue改变之前的值
console.log(newValue,oldValue)
}
},
//深度监听多级结构中某个属性的变化
'num':{
immediate:true, //初始化时让handler调用一次
deep:true, //深度监听
//handler
handler(newValue,oldValue){
//newValue新改变的值,oldValue改变之前的值
console.log(newValue,oldValue)
}
},
}
})
//全局写法,可在main.js中使用
vm.$watch('isShow',{
immediate:true, //初始化时让handler调用一次
//handler
handler(newValue,oldValue){
}
})
6.2、简写
当代码逻辑需求不需要immediate初始化时调用一次,和deep深度监听时,可以使用简写
//组件内写法
const vm = new Vue({
el:'#root',
data:{
isShow:true,
num:{
a:1,
b:2
}
},
watch:{
isShow(newValue,oldValue){
//newValue新改变的值,oldValue改变之前的值
console.log(newValue,oldValue)
}
}
})
//全局写法,可在main.js中使用
vm.$watch('isShow',function(newValue,oldValue){
})
7、watch对比computed
watch和computed的区别
- computed能完成的功能watch都可以完成
- watch能完成的功能,computed不一定能完成,例如watch可以进行异步操作
两个重要的原则:
- 所有被Vue所管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数、Promise的回调函数等),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象
8、绑定class
<style>
.normal{}
.normal1{}
.normal2{}
.normal3{}
</style>
<div id='root'>
//绑定class样式,字符串写法,适用于:样式的类名不确定,需要动态指定
<div :class='mood'></div>
//绑定class样式,数组写法,适用于:要绑定的样式个数不确定、名字不确定
<div :class='classArr'></div>
//绑定class样式,对象写法,适用于:要绑定的样式个数确定、名字确定,但是要动态决定用不用
<div :class='classObj'></div>
</div>
new Vue({
el:'#root',
data:{
mood:'normal',
classArr:['normal1','normal2','normal3'],
classObj:{
normal1:false,
normal2:false,
normal3:false,
}
},
})
9、绑定style
<div :style='{'fontSize':40+'px'}'></div>
//对象写法
<div :style='styleObj'></div>
//数组写法
<div :style='styleaArr'></div>
new Vue({
el:'#root',
data:{
styleObj:{
fontSize:'40px'
},
styleaArr:[
this.styleObj,
{color:'red'}
]
},
})
10、条件渲染
-
v-if
写法:
-
v-if=‘表达式’
-
v-else-if=‘表达式’
-
v-else
适用于:切换频率较低的场景
特点:不展示的dom元素直接被移除
注意:v-if可以和v-else-if、v-else一起使用,但是要求结构不能被打断
-
-
v-show
写法:v-show=‘表达式’
适用于:切换频率较高的场景
特点:不展示的dom元素未被移除,仅仅是使用样式隐藏
-
备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到
-
v-if和v-show都是为true时显示,为false时隐藏
11、列表渲染
11.1、基本使用
v-for指令:
- 用于展示列表数据
- 用法:v-for=‘(item,index) in xxx’ :key=‘yyy’
- 可遍历:数组、对象、字符串、指定次数
<ul>
//遍历数组时,item为值,index为索引,
//item:111,222,333
//index:0,1,2
<li v-for="(item,index) in arr" :key='index'></li>
//遍历对象时,value为键值,key为键名
//value:'111','222','333'
//key:'name','price','color'
<li v-for="(value,key) in obj" :key='key'></li>
//遍历字符串时,char为字符,index为索引
//item:1,1,1,2,2,2,3,3,3
//index:0,1,2,3,4,5,6,7,8
<li v-for="(char,index) in str" :key='index'></li>
//遍历指定次数时,num为数值,index为索引
//num:1,2,3,4,5
//index:0,1,2,3,4
<li v-for="(num,index) in 5" :key='index'></li>
</ul>
new Vue({
el:'#root',
data:{
arr:[111,222,333],
obj:{
name:'111',
price:'222',
color:'333'
},
str:'111222333'
},
})
11.2、key的作用和原理
-
虚拟dom中key的作用
key是虚拟dom对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟dom】
随后Vue进行【新虚拟dom】与【旧虚拟dom】的差异比较,比较规则如下
- 旧虚拟dom中找到了与新虚拟dom相同的key
- 若虚拟dom中内容没变,直接使用之前的真实dom
- 若虚拟dom中内容变了,则生成新的真实dom,随后替换掉页面中之前的真实dom
- 旧虚拟dom中未找到与新虚拟dom相同的key
- 创建新的真实dom,随后渲染到页面
- 旧虚拟dom中找到了与新虚拟dom相同的key
-
用index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序的操作:
- 会产生没有必要的真实dom更新 ===> 界面效果没问题,但效率低
- 如果结构中还包含输入类的dom
- 会产生错误dom更新 ==> 界面有问题
- 若对数据进行:逆序添加、逆序删除等破坏顺序的操作:
-
开发中如何选择key:
- 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
11.3、列表过滤(模糊搜索)
<input v-modal='keyWord' />
<ul>
<li v-for="(item,index) in filterArr" :key='item.id'>{{item}}</li>
</ul>
new Vue({
el:'#root',
data:{
arr:[
{id:1,name:"马冬梅",age:18,sex:'女'},
{id:2,name:"周冬雨",age:18,sex:'女'},
{id:3,name:"周杰伦",age:18,sex:'男'},
{id:4,name:"温兆伦",age:18,sex:'男'},
],
keyWord:''
},
//computed实现
computed:{
filterArr:{
get(){
return this.arr.filter((item) => item.name.indexOf(this.keyWord))
},
}
},
//watch实现
watch:{
'keyWord':{
immediate:true, //初始化时让handler调用一次
handler(newValue){
this.filterArr = this.arr.filter((item) => item.name.indexOf(newValue))
}
}
}
})
11.4、列表排序
<input v-modal='keyWord' />
<ul>
<button @click='sortType = 2'>年龄升序</button>
<button @click='sortType = 1'>年龄降序</button>
<button @click='sortType = 0'>原顺序</button>
<li v-for="(item,index) in filterArr" :key='item.id'>{{item}}</li>
</ul>
new Vue({
el:'#root',
data:{
arr:[
{id:1,name:"马冬梅",age:18,sex:'女'},
{id:2,name:"周冬雨",age:18,sex:'女'},
{id:3,name:"周杰伦",age:18,sex:'男'},
{id:4,name:"温兆伦",age:18,sex:'男'},
],
keyWord:'',
sortType:0,//0原顺序,1降序,2升序
},
//computed实现
computed:{
filterArr:{
get(){
const arr = this.arr.filter((item) => item.name.indexOf(this.keyWord))
if(this.sortType){
//a-b为从小到大,b-a为从大到小
arr.sort((a,b)=>this.sortType === 1 ? b.age-a.age : a.age-b.age)
}
return arr
},
}
},
})
12、收集表单数据
- 若:input标签type属性为text,则v-model收集的是value值,用户输入的就是value值
- 若:input标签type属性为radio,则v-model收集的是value值,用户输入的就是value值
- 若:input标签type属性为checkbox
- v-model的初始值是非数组,那么收集的就是checked(勾选 or 为勾选,是布尔值)
- v-model的初始值是数组,那么收集的就是value组成的数组
13、过滤器
过滤器:
-
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
-
语法:
- 注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:
- 使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”
-
备注:
- 过滤器也可以接收额外参数、多个过滤器也可以串联
- 并没有改变原本的数据,是产生新的对应的数据
<template>
<div>
<!-- 过滤器的基本使用,name会被当做参数传入 -->
<h1>{{name | filtersName}}</h1>
<!-- 过滤器传参 -->
<h1>{{name | filtersText('今年20岁')}}</h1>
<!-- 多个过滤器 -->
<!-- 原理:首先将name值传入filtersText过滤器,filtersText过滤器处理完后的返回值最后再传入filtersTextSlice -->
<h1>{{name | filtersText('今年20岁') | filtersTextSlice}}</h1>
<!-- 全局过滤器 -->
<h1>{{name | globalfilter}}</h1>
</div>
</template>
<script>
import Vue from 'vue';
Vue.filter('globalfilter',function(value,str='今年20岁'){
console.log(value); //小明
console.log(str); //今年20岁
return value + str
})
export default {
data () {
return {
name:'小明'
};
},
filters:{
filtersName(value){
console.log(value); //小明
return value + '今年18岁'
},
//参数默认值,如果有参数参入,使用传入的参数,如果没有参数传入,使用默认值
filtersText(value,str='今年18岁'){
console.log(value); //小明
console.log(str); //今年20岁
return value + str
},
filtersTextSlice(value){
console.log(value); //小明今年20岁
return value.slice(0,2)
}
}
}
</script>
<style lang='less' scoped>
</style>
14、Vue指令/语法糖(v-)
Vue指令语法:用于解析标签(包括标签属性、标签体内容、绑定事件…)
14.1、v-model
//v-model修饰符
//v-model中输入的内容默认为string
//v-model.number:将输入的内容转为有效的number( 输入的内容必须为数字 )
//通常配合input中type属性为number时使用
<input type='number' v-model.number='' />
//v-model.lazy:失去焦点再收集数据
<input type='text' v-model.lazy='' />
//v-model.trim:过滤首尾空格
<input type='text' v-model.trim='' />
14.2、v-once
v-once指令:
- v-once所在节点在初次动态渲染后,就视为静态内容了。
- 以后数据的变化不会引起v-once所在结构的更新,可以用于性能优化
<template>
<div>
<!-- 保存初次渲染时num的值,且始终保持初始值,不会随着按钮的点击增加 -->
<h1 v-once>{{num}}</h1>
<h1>{{num}}</h1>
<button @click='num++'>点击num+1</button>
</div>
</template>
<script>
export default {
data () {
return {
num:1
};
},
}
</script>
<style lang='less' scoped>
</style>
14.3、v-text、v-html、v-pre、v-bind等
v-text指令:
1. 作用:向其所在的节点中渲染文本内容
2. 与插值语法({{}})的区别:v-text会替换掉节点中的内容,{{xxx}}则不会
v-html指令:
1. 作用:向指定节点中渲染包含html结构的内容。
2. 与插值语法({{}})的区别:
- v-html会替换掉节点中所有的内容,{{xxx}}则不会
- v-html可以识别html结构
v-pre:
1. 跳过其所在节点的编译过程
2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
v-bind:单向绑定解析表达式,可简写为:xxx
v-for:遍历数组/对象/字符串
v-on:绑定事件监听,可以简写为@
v-if:条件渲染(动态控制节点是否存在)
v-else:条件渲染(动态控制节点是否存在)
v-show:条件渲染(动态控制节点是否展示)
14.4、自定义指令(directives)
自定义指令:
-
定义语法:
-
局部指令:
new Vue({
directives:{指令名:配置对象} 或 directives
})
-
全局指令:
Vue.directives(指令名,配置对象) 或 Vue.directives(指令名,回调函数)
-
-
配置对象中常用的3个回调:
- bind:指令与元素成功绑定时(初始加载时)调用。
- inserted:指令所在元素被插入页面时调用。
- update:指令所在的模板被重新加载时调用。
-
备注:
- 指令定义时不加v-,但是使用时要加v-;
- 指令名如果是多个单词,要使用kebab-case(xx-xx-xx)命名方式,不要使用camelCase(驼峰)命名
<template>
<!-- 需求:自定义一个tenNum指令,将num值*10 -->
<div>
<!-- 过滤器的基本使用,name会被当做参数传入 -->
<h1>{{`${name}有${num}块钱`}}</h1>
<!-- 函数式自定义指令 -->
<h1 v-ten-num='num'></h1>
<!-- 对象式自定义指令 -->
<input type="text" v-d-bind='num'>
<!-- 全局函数式自定义指令 -->
<h1 v-two-num='num'></h1>
<!-- 全局对象式自定义指令 -->
<input type="text" v-f-bind='num'>
</div>
</template>
<script>
import Vue from 'vue';
// 全局函数式自定义指令
Vue.directive('two-num',function(element,binding){
element.innerText = `小明有${binding.value * 2}块钱`
})
// 全局对象式自定义指令
Vue.directive('f-bind',{
// 指令与元素成功绑定时(初始加载时)
bind(element,binding){
element.value = binding.value
},
// 指令所在元素被插入页面时
inserted(element){
// 获取焦点
element.focus()
},
// 指令所在的模板被重新加载时
update(element,binding){
element.value = binding.value
// 获取焦点
element.focus()
}
})
export default {
data () {
return {
name:'小明',
num:20
};
},
directives:{
// 函数式
// element:dom元素
// binding:dom元素所绑定的属性等
'ten-num'(element,binding){
// tenNum什么时候会被调用?1、指令与元素成功绑定时(初始加载时);2、指令所在的模板被重新加载时
element.innerText = `小明有${binding.value * 10}块钱`
},
// 对象式
'd-bind':{
// 指令与元素成功绑定时(初始加载时)
bind(element,binding){
element.value = binding.value
},
// 指令所在元素被插入页面时
inserted(element){
// 获取焦点
element.focus()
},
// 指令所在的模板被重新加载时
update(element,binding){
element.value = binding.value
// 获取焦点
element.focus()
}
}
}
}
</script>
<style lang='less' scoped>
</style>
15、生命周期
16、脚手架目录分析
-
.gitignore
git的忽略文件,用于配置不想接受git管理的文件和文件夹
-
babel.config.js
babel的控制文件,babel用于es6转es5
-
vue.config.js
webpack配置文件
-
package.json
npm包管理文件,里面有详细的包名、版本以及采用的哪些依赖,用了哪些库等说明
-
yarn.lock/package.lock.json
npm包版本控制文件
-
工程描述文件,例如:这是什么项目,应该怎么使用,都有什么功能等等
-
src
-
main.js
// 引入Vue import Vue from 'vue' // 引入app组件,它是所有组件的父组件 import App from './App.vue' //关闭Vue的生产提示 Vue.config.productionTip = false //创建Vue实例对象 new Vue({ render: h => h(App), }).$mount('#app')- 该文件为整个项目的入口文件
-
assets
- 静态资源文件夹
-
components
- 公共组件
-
App.vue
- 入口组件
-
-
public
-
favicon.ico:网站页签图标
-
index.html:入口页面
<!DOCTYPE html> <html lang=""> <head> <meta charset="utf-8"> <!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 --> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- 开启移动端的理想视口 --> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <!-- 配置页面图标 --> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <!-- 配置网页标题 --> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <!-- 当浏览器不支持js时,noscript中的元素就会被渲染 --> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <!-- 容器 --> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>
-
17、ref属性
- 被用来给元素或子组件注册引用信息(id的替代品)
- 应用在html标签上获取的是真实dom元素,应用在组件标签上是组建实例对象(vc)
18、mixins混入
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
-
第一步定义混合:
export default { data() { return { num: 20 }; }, methods: { btn() { this.num++; } } }; -
第二步组件内引入以及使用:
<script> import hunru from "@/utils/mixins" export default { data() { return { num2: 10, }; }, mixins:[hunru] }; </script> -
全局使用:
在main.js中引入
import xxx from "xxxxxxxx" Vue.mixin(xxx)
19、插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
定义插件:
{
insrall(Vue,options){
// 全局过滤器
Vue.filter(....)
Vue.prototype.$xxx = xxxx
}
}
使用插件:
在main.js中引入
import xxx from 'xxxxxx'
// 使用并传参
Vue.use(xxx,1111)
20、组件
20.1、组件传参
-
父传子
-
父组件
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App"/> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'App', components: { HelloWorld } } </script> <style> </style> -
子组件
<template> <div> <h1>{{ msg }}</h1> </div> </template> <script> export default { props:['msg'], data() { return { }; }, created(){ console.log(this.msg); } }; </script> <style lang='less' scoped> </style>
-
-
子传父第一种方式
-
父组件
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <HelloWorld :getSon='getSon'/> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'App', components: { HelloWorld }, methods:{ getSon(e){ console.log(e); } } } </script> <style> </style> -
子组件
<template> <div> </div> </template> <script> export default { props:['getSon'], data() { return { }; }, created(){ this.getSon('给父组件的文字') } }; </script> <style lang='less' scoped> </style>
-
-
子传父第二种方式(组件自定义事件)
-
父组件
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <HelloWorld @send='getSon'/> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'App', components: { HelloWorld }, methods:{ getSon(e){ console.log(e); } } } </script> <style> </style> -
子组件
<template> <div> <button @click="sendFather">发送</button> </div> </template> <script> export default { props:['getSon'], data() { return { }; }, methods:{ sendFather(){ this.$emit("send",'给父组件传递的文字') } } }; </script> <style lang='less' scoped> </style>
-
20.2、组件自定义事件
-
一种组件间通信的方式,适用于:子传父
-
使用场景:子组件想给父组件传数据,那么就要在父组件中给子组件绑定自定义事件
-
绑定自定义事件:
-
第一种方式,在父组件中:<HelloWorld @send=‘getSon’/>,然后在methods中调用getSon
-
第二种方式,在父组件中:
<HelloWorld ref='helloWorld'/> ...... methods:{ getSon(e){ console.log(e); } } mounted(){ this.$refs.helloWorld.$on('send',()=>{ ...... }) // 或 this.$refs.helloWorld.$on('send',this.getSon) } -
若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法
this.$refs.helloWorld.$on.once('send',this.getSon)
-
-
触发自定义事件:(在子组件中)this.$emit(“send”,‘给父组件传递的文字’)
-
解绑自定义事件:this.$off(“send”)
-
组件上也可以绑定原生dom事件,需要使用native修饰符
-
注意:通过this.on(“xxx”,回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题
20.3、全局事件总线
-
注册$bus
import Vue from 'vue'; import App from './App.vue'; new Vue({ render: (h) => h(App), beforeCreate() { // 注册全局事件总线 Vue.prototype.$bus = this }, }).$mount('#app'); -
父组件
<template> <div id="app"> <HelloWorld msg="Welcome to Your Vue.js App"/> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'App', components: { HelloWorld }, mounted(){ // 绑定自定义事件 this.$bus.$on('demoBus',(e)=>{ alert(e) }) }, beforeDestroy(){ // 解绑自定义事件 this.$bus.$off('demoBus') } } </script> <style> ...... </style> -
HelloWorld组件
<template> <div class="hello"> <h1>{{ msg }}</h1> <demo /> </div> </template> <script> import demo from "./demo" export default { name: 'HelloWorld', components: { demo, }, props: { msg: String } } </script> <style scoped> </style> -
demo组件
<template> <div @click="demo"> 中央事件总线$bus </div> </template> <script> export default { name: 'bus', methods:{ // 触发demoBus自定义事件并传递参数 demo(){ this.$bus.$emit('demoBus','11111') } }, } </script> <style lang="sass" scoped> </style>
20.4、消息订阅与发布
什么是消息订阅与发布?说白了就是不使用全局事件总线来实现组件通信,适用于任意组件间通信
这里推荐使用pubsub-js
使用步骤:
-
安装pubsub-js:yarn add pubsub-js / npm i pubsub-js
-
在需要使用的组件中引入: import pubsub from “pubsub-js”
-
订阅消息:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
<template> <div id="app"> <HelloWorld msg="Welcome to Your Vue.js App"/> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' import pubsub from "pubsub-js" export default { name: 'App', components: { HelloWorld }, data(){ return { pubsub:'' } }, mounted(){ // 绑定自定义事件 // this.$bus.$on('demoBus',(e)=>{ // alert(e) // }) // 订阅消息 this.pubId = pubsub.subscribe('demoBus',(msgName,data)=>{ // msgName:消息名 // data:接收的数据 alert(data) }) }, beforeDestroy(){ // 取消订阅 pubsub.unsubscribe(this.pubId) } } </script> <style> ...... </style> -
发布消息
<template> <div @click="demo"> 消息发布 </div> </template> <script> import pubsub from "pubsub-js" export default { name: 'bus', methods:{ // 触发demoBus自定义事件并传递参数 demo(){ // this.$bus.$emit('demoBus','11111') // 发布消息 pubsub.publish('demoBus','111111') } }, } </script> <style lang="sass" scoped> </style> -
最好在beforeDestroy钩子中,用**pubsub.unsubscribe(this.xxx)**去取消订阅
21、this.$nextTick
- 语法:this.$nextTick(回调)
- 作用:在下一次dom更新元素之后执行其指定的回调
- 什么时候用:当改变数据后,要基于更新后的新dom进行某些操作时,要在nextTick所指定的回调函数中执行
22、配置代理
修改vue.config.js
module.exports = {
devServer: {
proxy: {
// 请求时如需使用代理转发,加"/api"前缀,如不需代理转发则不加前缀
'/api': {
// 需要代理转发的地址
target: '"http://xxx.xxx.xxx.xxx:xxxx',
// 重写请求地址,匹配请求地址中所有“/api”,替换空("")
pathRewrite:'"^/api":""',
// 用于支持websocket,默认为true
// ws: true,
// 用于控制请求头中的host值,默认为true
// changeOrigin: true
},
// 配置多个代理
'/foo': {
target: '<other_url>'
}
}
}
}
使用:
axios
.get("http://localhost:8081/api/xxxxxx", {
params,
})
.then((response) => {
resolve(response.data);
})
.catch((err) => {
reject(err);
});
23、插槽
-
默认插槽
// 父组件插入 <HelloWorld> <div>1111111111</div> </HelloWorld> // 子组件使用 <div> // 父组件未传入内容时,可显示默认值,若父元素未传入内容,且无默认值,则渲染一个空的div标签 <slot>默认值</slot> </div> -
具名插槽
// 父组件插入 <HelloWorld> <div slot="name">111</div> <div slot="name">222</div> <div slot="name">333</div> <div slot="name">444</div> <div slot="name">555</div> </HelloWorld> // 子组件使用 <div> <slot name="name">默认值</slot> </div> -
作用域插槽
-
在父组件中使用子组件的数据,通常用于:数据相同,但dom结构不同时
// 父组件插入 <HelloWorld> <!-- <template slot-scope="data"> <div> <h1>{{data.obj}}</h1> </div> </template> --> <template slot-scope="{obj}"> <h1>{{obj}}</h1> </template> </HelloWorld> // 子组件使用 <div> <slot :obj="obj">默认值</slot> </div> // 子组件数据 data() { return { obj:{a:1,b:2,c:3,d:4} } },
-