Vue基础入门(持续更新)

1、vue特点

  • 组件化开发,提高代码复用率,代码更好维护
  • 声明式编码,无需直接操作dom,提高开发效率
  • 虚拟dom+diff算法

2、数据代理(Object.defineProperty)

Vue监视数据的原理:

  1. vue会监视data中所有层次的数据;

  2. 如何监测对象中的数据?

    通过setter实现监测,且要在new Vue时就传入要监测的数据。

    1. 对象中,后追加的属性,Vue默认不做响应式处理
    2. 如需给后添加的属性做响应式,需使用Vue.set或vm.$set
  3. 如何监测数组中的数据?

    通过包裹数组更新元素的方法实现,本质就是做了两件事:

    1. 调用原生对应的方法对数组进行更新。
    2. 重新解析模板,进而更新页面。
  4. 再Vue修改数组中的某个元素一定要用如下方法:

    1. 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
    2. 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中的数据代理

  1. Vue中的数据代理:

    通过vm对象来处理data对象中的属性的操作(读/写)

  2. Vue中数据代理的好处

    更加方便的操作data中的数据

  3. 基本原理:

    通过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、事件修饰符

  1. prevent:阻止默认事件
  2. stop:阻止时间冒泡
  3. once:事件只触发一次
  4. capture:使用事件的捕获模式
  5. self:只有event.target是当前操作的元素时才触发事件
  6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕
  7. 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、键盘事件

  1. Vue中常用的按键别名:

    回车 => enter

     	删除  =>  delete
    
     	退出  =>  esc
    
     	空格  =>  space
    
     	换行  =>  tab(特殊,必须配合keydown使用)
    
     	上  =>  up
    
     	下  =>  down
    
     	左  =>  left
    
     	右  =>  right
    
  2. 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)
            },
        }
    })
    
  3. 系统修饰键(用法特殊):ctrl、alt、shift、win

    • 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
    • 配合keydown使用:正常触发事件
    //只有按ctrl+c时才触发
    <input @keyup.ctrl.c='show(11,$event)'/>
    <input @keydown='show(11,$event)'/>    
    
  4. 也可以使用keyCode去指定具体的按键

    <input @keyup='show(11,$event)'/>
        
    new Vue({
        el:'#root',
        data:{
            name:'张三'
        },
        methods:{
            show(a,event){
                //keyCode为13时为enter
                if(event.keyCode !== 13) return
            },
        }
    })
    
  5. Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名

Vue.config.keyCodes.huiche = 13

<input @keyup.huiche='show(11,$event)'/>

5、计算属性

  1. 定义:要用的属性不存在,要通过已有属性计算得来。
  2. 原理:底层借助了Object.defineproperty方法提供的getter和setter。
  3. get函数什么时候执行?
    • 初次调用时会执行一次
    • 当依赖的数据发生改变时会被再次调用
  4. 优势:内部有缓存机制(复用),效率更高,调试方便
  5. 备注:
    • 计算属性最终会出现在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、监听属性

  1. 当被监视的属性变化时,回调函数自动调用,进行相关操作
  2. 监视的属性必须存在,才能进行监视
  3. 监视的两种写法:
    • new Vue时传入watch配置
    • 通过vm.$watch监视
  4. watch可以监听计算属性
  5. 深度监听:
    • Vue中的watch默认不监听对象内部值的改变
    • 配置deep:true可以监听对象内部值改变
  6. 备注:
    • 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的区别

  1. computed能完成的功能watch都可以完成
  2. watch能完成的功能,computed不一定能完成,例如watch可以进行异步操作

两个重要的原则:

  1. 所有被Vue所管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
  2. 所有不被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、条件渲染

  1. v-if

    写法:

    • v-if=‘表达式’

    • v-else-if=‘表达式’

    • v-else

      适用于:切换频率较低的场景

      特点:不展示的dom元素直接被移除

      注意:v-if可以和v-else-if、v-else一起使用,但是要求结构不能被打断

  2. v-show

    写法:v-show=‘表达式’

    适用于:切换频率较高的场景

    特点:不展示的dom元素未被移除,仅仅是使用样式隐藏

  3. 备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到

  4. v-if和v-show都是为true时显示,为false时隐藏

11、列表渲染

11.1、基本使用

v-for指令:

  1. 用于展示列表数据
  2. 用法:v-for=‘(item,index) in xxx’ :key=‘yyy’
  3. 可遍历:数组、对象、字符串、指定次数
<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的作用和原理

  1. 虚拟dom中key的作用

    key是虚拟dom对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟dom】

    随后Vue进行【新虚拟dom】与【旧虚拟dom】的差异比较,比较规则如下

    1. 旧虚拟dom中找到了与新虚拟dom相同的key
      • 若虚拟dom中内容没变,直接使用之前的真实dom
      • 若虚拟dom中内容变了,则生成新的真实dom,随后替换掉页面中之前的真实dom
    2. 旧虚拟dom中未找到与新虚拟dom相同的key
      • 创建新的真实dom,随后渲染到页面
  2. 用index作为key可能会引发的问题:

    1. 若对数据进行:逆序添加、逆序删除等破坏顺序的操作:
      • 会产生没有必要的真实dom更新 ===> 界面效果没问题,但效率低
    2. 如果结构中还包含输入类的dom
      • 会产生错误dom更新 ==> 界面有问题
  3. 开发中如何选择key:

    1. 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
    2. 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用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、收集表单数据

  1. 若:input标签type属性为text,则v-model收集的是value值,用户输入的就是value值
  2. 若:input标签type属性为radio,则v-model收集的是value值,用户输入的就是value值
  3. 若:input标签type属性为checkbox
    • v-model的初始值是非数组,那么收集的就是checked(勾选 or 为勾选,是布尔值)
    • v-model的初始值是数组,那么收集的就是value组成的数组

13、过滤器

过滤器:

  1. 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。

  2. 语法:

    • 注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:
    • 使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”
  3. 备注:

    • 过滤器也可以接收额外参数、多个过滤器也可以串联
    • 并没有改变原本的数据,是产生新的对应的数据
<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指令:

  1. v-once所在节点在初次动态渲染后,就视为静态内容了。
  2. 以后数据的变化不会引起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)

自定义指令:

  1. 定义语法:

    • 局部指令:

      new Vue({

      directives:{指令名:配置对象} 或 directives

      })

    • 全局指令:

      Vue.directives(指令名,配置对象) 或 Vue.directives(指令名,回调函数)

  2. 配置对象中常用的3个回调:

    • bind:指令与元素成功绑定时(初始加载时)调用。
    • inserted:指令所在元素被插入页面时调用。
    • update:指令所在的模板被重新加载时调用。
  3. 备注:

    • 指令定义时不加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、生命周期

生命周期(Vue2)

16、脚手架目录分析

  1. .gitignore

    git的忽略文件,用于配置不想接受git管理的文件和文件夹

  2. babel.config.js

    babel的控制文件,babel用于es6转es5

  3. vue.config.js

    webpack配置文件

  4. package.json

    npm包管理文件,里面有详细的包名、版本以及采用的哪些依赖,用了哪些库等说明

  5. yarn.lock/package.lock.json

    npm包版本控制文件

  6. README.md

    工程描述文件,例如:这是什么项目,应该怎么使用,都有什么功能等等

  7. src

    1. 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')
      
      
      • 该文件为整个项目的入口文件
    2. assets

      • 静态资源文件夹
    3. components

      • 公共组件
    4. App.vue

      • 入口组件
  8. public

    1. favicon.ico:网站页签图标

    2. 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属性

  1. 被用来给元素或子组件注册引用信息(id的替代品)
  2. 应用在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、组件传参

  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>
      
  2. 子传父第一种方式

    • 父组件

      <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>
      
  3. 子传父第二种方式(组件自定义事件)

    • 父组件

      <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、组件自定义事件

  1. 一种组件间通信的方式,适用于:子传父

  2. 使用场景:子组件想给父组件传数据,那么就要在父组件中给子组件绑定自定义事件

  3. 绑定自定义事件:

    • 第一种方式,在父组件中:<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)
      
  4. 触发自定义事件:(在子组件中)this.$emit(“send”,‘给父组件传递的文字’)

  5. 解绑自定义事件:this.$off(“send”)

  6. 组件上也可以绑定原生dom事件,需要使用native修饰符

  7. 注意:通过this.refs.xxx.refs.xxx.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

使用步骤:

  1. 安装pubsub-js:yarn add pubsub-js / npm i pubsub-js

  2. 在需要使用的组件中引入: import pubsub from “pubsub-js”

  3. 订阅消息: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>
    
  4. 发布消息

    <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>
    
  5. 最好在beforeDestroy钩子中,用**pubsub.unsubscribe(this.xxx)**去取消订阅

21、this.$nextTick

  1. 语法:this.$nextTick(回调)
  2. 作用:在下一次dom更新元素之后执行其指定的回调
  3. 什么时候用:当改变数据后,要基于更新后的新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、插槽

  1. 默认插槽

    // 父组件插入
    <HelloWorld>
        <div>1111111111</div>
    </HelloWorld>
    
    // 子组件使用
    <div>
        // 父组件未传入内容时,可显示默认值,若父元素未传入内容,且无默认值,则渲染一个空的div标签
        <slot>默认值</slot>
    </div>
    
  2. 具名插槽

    // 父组件插入
    <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>
    
  3. 作用域插槽

    • 在父组件中使用子组件的数据,通常用于:数据相同,但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}
          }
      },
      

24、Vuex

Vuex

25、路由(vue-router)

vue-router


Vue基础入门(持续更新)
http://localhost:8090//archives/vue
作者
龟龟
发布于
2020年12月18日
更新于
2024年08月28日
许可协议