Vue基础知识

Vue核心

一、模板语法

1.插值语法
​ 功能:用于解析标签体内容
​ 写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。

2.指令语法
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件)。
举例:v-bind:href=“xxx” 或 简写为 :href=“xxx”,xxx同样要写js表达式,且可以直接读取 到data中的所有属性。

<h1>插值语法h1>
<h3>你好,{{name}}h3>
<hr/>
<h1>指令语法h1>
<a v-bind:href="school.url.toUpperCase()" x="hello">点我去{{school.name}}学习1a>
<a :href="school.url" x="hello">点我去{{school.name}}学习2a>

二、数据绑定

1.单向绑定(v-bind):数据只能从data流向页面。
2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。

  • 双向绑定一般都应用在表单类元素上(如:input、select等)
  • v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值。

单向数据绑定:<input type="text" v-bind:value="name"><br/>
双向数据绑定:<input type="text" v-model:value="name"><br/>


单向数据绑定:<input type="text" :value="name"><br/>
双向数据绑定:<input type="text" v-model="name"><br/>

三、el与data的两种写法

1.el有2种写法
(1).new Vue时候配置el属性。
(2).先创建Vue实例,随后再通过vm.$mount(’#root’)指定el的值。

//el的两种写法
const v = new Vue({
    el:'#root', //第一种写法
    data:{
        name:'尚硅谷'
    }
})

v.$mount('#root') //第二种写法

2.data有2种写法
(1).对象式
(2).函数式

//data的两种写法
new Vue({
    el:'#root',
    //data的第一种写法:对象式
     data:{	
         name:'尚硅谷'	
     } 
    
    //data的第二种写法:函数式
    data(){
        console.log('@@@',this) //此处的this是Vue实例对象
        return{
            name:'尚硅谷'
        }
    }
})

如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。

3.一个重要的原则
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。

四、理解MVVM

M:模型(Model) :data中的数据

V:视图(View) :模板代码

VM:视图模型(ViewModel):Vue实例
观察发现:
1.data中所有的属性,最后都出现在了vm身上。
2.vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PDecwpwu-1641102674781)(C:\Users\骑上这只不归鹿\AppData\Roaming\Typora\typora-user-images\image-20211213170007781.png)]

五、数据代理

1.回顾Object.defineProperty方法

let ageNumber=18
let person={
    name:'张三',
    sex:'男',
}
Object.defineProperties(person,{
    'age':{
        value:18,
       	writable:true, //控制属性是否可以被修改,默认值是false
        
        enumerable:true, //控制属性是否可以枚举,默认值是false
        configurable:true //控制属性是否可以被删除,默认值是false
        
        //数据描述符和存取描述符  (value和writable)与 (get和set)不能同时存在

        //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
        get(){
            return this.value
        },
        //当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
        set(value){
            this.value=value
        }

    },

    'add':{
        value:'c'
    }
})

对象里目前存在的属性描述符有两种主要形式:数据描述符存取描述符**。数据描述符一个具有值的属性,该值可以是可写的,也可以是不可写的存取描述符由 getter 函数和 setter 函数所描述的属性。一个描述符只能是这两者其中之一;不能同时是两者。

  • Object.defineProperty(obj, prop, descriptor) 定制对象属性
const object1 = {};

Object.defineProperty(object1, 'property1', {
  value: 42,
  writable: false
});
  • Object.defineProperties(obj, props) 批量定制对象属性
var obj = {};
Object.defineProperties(obj, {
  'property1': {
    value: true,
    writable: true
  },
  'property2': {
    value: 'Hello',
    writable: false
  }
  // etc. etc.
});
  • Object.getOwnPropertyDescriptor(obj, prop) 获取属性描述符
const object1 = {
  property1: 42
};

const descriptor1 = Object.getOwnPropertyDescriptor(object1, 'property1');

console.log(descriptor1.configurable);
// expected output: true

console.log(descriptor1.value);
// expected output: 42

Object.getOwnPropertyDescriptors(obj) 方法用来获取一个对象的所有自身属性的描述符。

2.何为数据代理

数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)

let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{    
    get(){        
        return obj.x    
    },    
    set(value){        
        obj.x = value    
    }
})

3.Vue中的数据代理

1.Vue中的数据代理:
`通过vm对象来代理data对象中属性的操作(读/写)

2.Vue中数据代理的好处:
​ 更加方便的操作data中的数据

3.基本原理:
​ 通过Object.defineProperty()把data对象中所有属性添加到vm上。
​ 为每一个添加到vm上的属性,都指定一个getter/setter。
​ 在getter/setter内部去操作(读/写)data中对应的属性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sj8rOp4I-1641102674783)(C:\Users\骑上这只不归鹿\AppData\Roaming\Typora\typora-user-images\image-20211213222403514.png)]

六、事件处理

1.事件的基本使用

  • 使用v-on:xxx@xxx 绑定事件,其中xxx是事件名;

    <button v-on:click="showInfo">点击button><button @click="showInfo">点击button>
    
  • 事件的回调需要配置在methods对象中,最终会在vm上;

  • methods中配置的函数,不要用箭头函数!否则this就不是vm了;

  • methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象

  • @click="demo"@click="demo($event)" 效果一致,但后者可以传参

    <button @click="showInfo2($event,args)">点击button> //$event为事件参数,args为需要传递的参数
    

2.事件的修饰符

事件传播:捕获阶段(不会触发事件)、目标阶段(开始在目标元素上触发事件)、冒泡阶段(依次触发祖先元素上的事件)

  • prevent:阻止默认事件(常用);

    <a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息a>//阻止页面跳转
    
  • stop:阻止事件冒泡(常用);

    <button @click.stop="showInfo">点我提示信息button>
    
  • once:事件只触发一次(常用);

    <button @click.once="showInfo">点我提示信息button>
    
  • capture:使用事件的捕获模式;

    <div class="box1" @click.capture="showMsg(1)">    div1    <div class="box2" @click="showMsg(2)">        div2    div>div>
    
  • self:只有event.target是当前操作的元素时才触发事件;

    <div class="demo1" @click.self="showInfo">    <button @click="showInfo">点我提示信息button>div>
    
  • passive:事件的默认行为立即执行,无需等待事件回调执行完毕;

3.键盘事件

键盘事件修饰符

<
input type="text" @keydown.Enter="showInfo">

<input type="text" @keydown.enter="showInfo">

<input type="text" @keydown.13="showInfo">

部分按键别名

回车 enter

删除 delete (捕获“删除”和“退格”键)

退出 esc

空格 space

换行 tab (特殊,必须配合keydown去使用)

上 up

下 down

左 left

右 right

Vue未提供别名的按键,可以使用按键原始的key值(键本名)去绑定多个单词组成的键名注意要转为短横线命名


<input type="text" @keydown.caps-lock="showInfo">

系统修饰键ctrlaltshiftmeta

  • 配合keyup使用:按下修饰键的同时,再按下其他键随后释放其他键事件才被触发
  • 配合keydown使用:正常触发事件
<input type="text" @keyup.ctrl.up="showInfo">

Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名

Vue.config.keyCodes.huiche = 13 //定义了一个别名按键,将enter的别名定义为huiche

七、计算属性computed

  • 定义:要用的属性不存在,要通过已有属性计算得来。

  • 原理:底层借助了Objcet.defineproperty方法提供的getter和setter。

  • get函数什么时候执行?

    ①初次读取时会执行一次。

    ②当依赖的数据发生改变时会被再次调用。

  • 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。

  • 备注:

    计算属性最终会出现在vm上,直接读取使用即可

    如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变

//完整写法
computed:{    
    fullName:{        
    //get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值        
    //get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。        
    get(){            
        console.log('get被调用了')            
        // console.log(this) //此处的this是vm            
        return this.firstName + '-' + this.lastName        },            
            //set什么时候调用? 当fullName被修改时。        
            set(value){            
                console.log('set',value)            
                const arr = value.split('-')            
                this.firstName = arr[0]            
                this.lastName = arr[1]      	
            }    
}}
//简写
computed:{    
    fullName(){        
        console.log('get被调用了')        
        return this.firstName + '-' + this.lastName    
    }}

八、监视属性watch

被监视的属性变化时, 回调函数自动调用, 进行相关操作。监视的属性必须存在,才能进行监视!!

监视的两种写法:

//1.new Vue时传入watch配置
watch:{    
    isHot:{        
        immediate:true, //初始化时让handler调用一下        
            //handler什么时候调用?当isHot发生改变时。        
            handler(newValue,oldValue){        	
            console.log('isHot被修改了',newValue,oldValue)        
        }    
    }
}

//2.通过vm.$watch监视
vm.$watch('isHot',{    
    immediate:true, //初始化时让handler调用一下    
    //handler什么时候调用?当isHot发生改变时。    
    handler(newValue,oldValue){        
        console.log('isHot被修改了',newValue,oldValue)    
    }
})    

深度监视
(1).Vue中的watch默认不监测对象内部值的改变,只能监测到对象的改变(一层)
(2).配置deep:true可以监测对象内部属性值改变(多层)。

data:{    
    isHot:false,         
        stu:{            
            age:18,            
                address:{                
                    province:'sichuan',                
                        city:'chengdu'            
                }        
        }
},    
//深度监视,能监视对象内部属性值的变化,若为false则监测不到
watch:{    
   'stu.age':{        
       deep:true,        
       handler(newValue,oldValue){            
           console.log('stu.age修改了')        
        }    
   },    
     stu:{        
       deep:true,        
       handler(newValue,oldValue){            
        console.log('num被修改了')        
       }
     },
}

备注

​ Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
​ 使用watch时根据数据的具体结构,决定是否采用深度监视。

简写:

watch:{    
    //正常写法    
    isHot:{        
        immediate:true, //初始化时让handler调用一下        
            deep:true,//深度监视        
                handler(newValue,oldValue){            
                console.log('isHot被修改了',newValue,oldValue)        
            }    
    },     
        //简写    
        isHot(newValue,oldValue){        
            console.log('isHot被修改了',newValue,oldValue,this)    
        } 
}
//正常写法
vm.$watch('isHot',{    
    immediate:true, //初始化时让handler调用一下    
    deep:true,//深度监视    
    handler(newValue,oldValue){        
        console.log('isHot被修改了',newValue,oldValue)    
    }
}) 
//简写
vm.$watch('isHot',(newValue,oldValue)=>{    
    console.log('isHot被修改了',newValue,oldValue,this)
})

​ 注意:当变更(不是替换)对象或数组并使用 deep 选项时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本

computed和watch之间的区别

  • computed能完成的功能,watch都可以完成。
  • watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作

两个重要的小原则

  • 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
  • 所有不被Vue所管理的函数(定时器的回调函数ajax的回调函数等Promise的回调函数最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。

九、绑定样式

通过class绑定样式

写法:class=“xxx” xxx可以是字符串、对象、数组。

字符串写法 样式的类名不确定,需要动态指定

<div class="box" :class="boxStyle" >div><button id="btn" @click="change">切换button>
const vm=new Vue({    
    el:"#app",    
    data:{        
        boxStyle:'box1',  //去CSS样式中寻找.box1    
    },    
    methods: {        
        change(){            
            this.boxStyle='box2'                      
        }    
    },
})

数组写法 要绑定多个样式,个数不确定,名字也不确定

<div class="box" :class="boxStyleArr" >div>
const vm=new Vue({    
    el:"#app",    
    data:{        
        boxStyleArr:['box1','box2','box3'] //去CSS样式中寻找.box1 .box2 .box3    
    },            
})		

对象写法 要绑定多个样式,个数确定,名字也确定,但不确定用不用

<div class="box" :class="boxStyleObj" >div>
const vm=new Vue({   
    el:"#app",    
    data:{        
        boxStyleObj:{            
            box1:false,            
            box2:true,            
            box3:true        
        }    
    },            
})

通过style绑定样式

写法:

:style="{ fontSize: xxx }"其中xxx是动态值

<div class="box" :style="boxStyle" >div>
const vm=new Vue({    
    el:"#app",    
    data:{        
        boxStyle:{            
            borderRadius:"50%"  //短横线转为驼峰        
        }    
    },            
})

:style="[a,b]"其中a、b是样式对象。


<div class="box" :style="boxStyleArr" >div>

<div class="box" :style="[backgroundStyle,boderStyle]" >div>
//第一种写法
const vm=new Vue({    
    el:"#app",    
    data:{        
        boxStyleArr:[            
            {                
                borderRadius:"50%"  //短横线转为驼峰            
            },            
            {               
                backgroundColor:"blue"            
            }        
        ]    
    },            
})
//第二种写法
const vm=new Vue({    
    el:"#app",    
    data:{        
        backgroundStyle:{            
            borderRadius:"50%"  
            //短横线转为驼峰        
        },        
        boderStyle:{            
            backgroundColor:"blue"        
        }    
    },            
})

十、条件渲染

v-if

写法

  • v-if="表达式"
  • v-else-if="表达式"
  • v-else=“表达式” 一般不写表达式

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

特点:不展示的DOM元素节点直接被移除。

注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”(中间不能出现其他兄弟节点)

<div v-if="n === 1">Angulardiv><div v-else-if="n === 2">Reactdiv>    <div v-else-if="n === 3">Vuediv><div v-else>框架div>

v-if与template的配合使用

<template v-if="n === 1">    <h2>你好h2>    <h2>尚硅谷h2>    <h2>北京h2>template>

v-show

写法:v-show="表达式"

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

特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉(display:none)

<h2 v-show="false">欢迎来到{{name}}h2><h2 v-show="1 === 1">欢迎来到{{name}}h2>

十一、列表渲染

v-for

语法:v-for="(item, index) in xxx" :key="yyy"

遍历数组

<li v-for="(person, index) of persons" :key="index">    {{person.name}}_{{person.age}}li>

遍历对象

<li v-for="(value, key) of car" :key="key">    {{key}}_{{value}}li>

遍历字符串

<li v-for="(char, index) in str" :key="index">    {{index}}_{{char}}li>

遍历指定次数

<li v-for="(number,index) of 5" :key="index">    {{index}}-{{number}}li>

十二、key的作用和原理

虚拟DOM中key的作用

key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较

对比规则

旧虚拟DOM中找到了与新虚拟DOM相同的key

  • 若虚拟DOM中内容没变, 直接使用之前的真实DOM
  • 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM

旧虚拟DOM中未找到与新虚拟DOM相同的key

  • 创建新的真实DOM,随后渲染到到页面。

用index作为key可能会引发的问题

  • 若对数据进行逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新(界面效果没问题, 但效率低
  • 如果结构中还包含输入类的DOM,会产生错误DOM更新(界面有问题

开发中如何选择key

  • 好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值
  • 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的

十三、列表过滤

watch实现

new Vue({
    el:'#root',
    data:{
        keyWord:'',
        persons:[
            {id:'001',name:'马冬梅',age:19,sex:'女'},
            {id:'002',name:'周冬雨',age:20,sex:'女'},
            {id:'003',name:'周杰伦',age:21,sex:'男'},
            {id:'004',name:'温兆伦',age:22,sex:'男'}
        ],
        filPerons:[]
    },
    watch:{
        keyWord:{
            immediate:true,
            handler(val){
                this.filPerons = this.persons.filter((p)=>{
                    return p.name.indexOf(val) !== -1
                })
            }
        }
    }
})

computed实现

new Vue({
    el:'#root',
    data:{
        keyWord:'',
        persons:[
            {id:'001',name:'马冬梅',age:19,sex:'女'},
            {id:'002',name:'周冬雨',age:20,sex:'女'},
            {id:'003',name:'周杰伦',age:21,sex:'男'},
            {id:'004',name:'温兆伦',age:22,sex:'男'}
        ]
    },
    computed:{
        filPerons(){
            return this.persons.filter((p)=>{
                return p.name.indexOf(this.keyWord) !== -1
            })
        }
    }
}) 

更新时候的小问题

methods: {    
    updateMei(){        
    // this.persons[0].name = '马老师' //奏效        
    // this.persons[0].age = 50 //奏效        
    // this.persons[0].sex = '男' //奏效                
    // this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'} //不奏效
       this.persons.splice(0,1,{id:'001',name:'马老师',age:50,sex:'男'})    
    }
}

十四、列表排序

<body>    
        
    <div id="root">        
        <h2>人员列表h2>        
        <input type="text" placeholder="请输入名字" v-model="keyWord">        
        <button @click="sortType = 2">年龄升序button>        
        <button @click="sortType = 1">年龄降序button>        
        <button @click="sortType = 0">原顺序button>        
        <ul>            
            <li v-for="(p,index) of filPerons" :key="p.id">                
                {{p.name}}-{{p.age}}-{{p.sex}}                
                <input type="text">            
            li>        
        ul>    
    div>    
    <script type="text/javascript">        
        Vue.config.productionTip = false        
        new Vue({            
            el:'#root',           
            data:{              
                keyWord:'',               
                sortType:0, //0原顺序 1降序 2升序              
                persons:[                  
                    {id:'001',name:'马冬梅',age:30,sex:'女'},                    
                    {id:'002',name:'周冬雨',age:31,sex:'女'},                    
                    {id:'003',name:'周杰伦',age:18,sex:'男'},                    
                    {id:'004',name:'温兆伦',age:19,sex:'男'}          
                ]        
            },        
            computed:{          
                filPerons(){          
                    const arr = this.persons.filter((p)=>{                       
                        return p.name.indexOf(this.keyWord) !== -1                    
                    })                    
                    //判断一下是否需要排序         
                    if(this.sortType){                
                        arr.sort((p1,p2)=>{          
                            return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age                        
                        })                    
                    }                  
                    return arr             
                }         
            }      
        })  
    script>
body>

十五、Vue检测数据原理

检测对象

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

  • 对象中后追加的属性,Vue默认不做响应式处理

  • 如需给后添加的属性做响应式,请使用如下API

    Vue.set(obj,key,value) vm.$set(obj,key,value)
    

检测数组

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。因此不能通过索引值修改数组来实现响应式处理

在Vue修改数组中的某个元素一定要用如下方法

  • push()、pop()、shift()、unshift()、splice()、sort()、reverse()
  • Vue.set(arr,index,value) 或 vm.$set(arr,index,value)

​ Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!

十六、收集表单数据

v-model的三个修饰符:

lazy:失去焦点再收集数据

number:输入字符串转为有效的数字

trim:输入首尾空格过滤

输入框(text)以及富文本框(textarea)

v-model收集的是value值,用户输入的就是value值

<label for="userName">账号:label>
<input type="text" id="userName" v-model="userInfo.userName"><br><br>
<label for="userPwd">密码:label>
<input type="password" id="userPwd" v-model="userInfo.userPwd"><br><br>
<label for="userAge">年龄:label>
<input type="number" id="userAge" v-model.number="userInfo.userAge">
其他信息:<textarea name="other" id="other" v-model="userInfo.other">textarea>

单选按钮(radio)

v-model收集的是value值,且要给标签配置value值

<label for="male">label>
<input type="radio" id="male" name="sex" value="male" v-model="userInfo.sex">
<label for="female">label>
<input type="radio" id="female" name="sex" value="female" v-model="userInfo.sex">

复选框(checkbox)

  • 没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)

  • 配置input的value属性

    (1)v-model的初始值(配置项data中的值)是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)

    (2)v-model的初始值(配置项data中的值)是数组,那么收集的的就是value组成的数组(重点)

爱好:<label for="drink">喝酒label>
<input type="checkbox" name="hobby" id="drink" value="drink" v-model="userInfo.hobby">
<label for="smoke">抽烟label>
<input type="checkbox" name="hobby" id="smoke" value="smoke" v-model="userInfo.hobby"><label for="perm">烫头label>
<input type="checkbox" name="hobby" id="perm" value="perm" v-model="userInfo.hobby">

选择列表(select)

v-model收集的是value值

所属小区<select name="region" id="region" v-model="userInfo.region">    
    <option value="">请选择校区option>    
    <option value="chengdu">成都option>  
    <option value="guangzhou">广州option>  
    <option value="beijing">北京option>
select>
<body> 
    <form id="app">     
        <label for="userName">账号:label>   
        <input type="text" id="userName" v-model="userInfo.userName">  
        <br><br>      
        <label for="userPwd">密码:label>     
        <input type="password" id="userPwd" v-model="userInfo.userPwd">   
        <br><br>     
        <label for="userAge">年龄:label>    
        <input type="number" id="userAge" v-model.number="userInfo.userAge">        <br><br> 
        性别:   
        <label for="male">label>   
        <input type="radio" id="male" name="sex" value="male" v-model="userInfo.sex">        <label for="female">label>  
        <input type="radio" id="female" name="sex" value="female"  v-model="userInfo.sex">  
        <br><br>     
        爱好:      
        <label for="drink">喝酒label>    
        <input type="checkbox" name="hobby" id="drink" value="drink" v-model="userInfo.hobby">  
        <label for="smoke">抽烟label>   
        <input type="checkbox" name="hobby" id="smoke" value="smoke" v-model="userInfo.hobby">   
        <label for="perm">烫头label>   
        <input type="checkbox" name="hobby" id="perm" value="perm" v-model="userInfo.hobby">      
        <br><br>      
        所属小区    
        <select name="region" id="region" v-model="userInfo.region">            
            <option value="">请选择校区option>   
            <option value="chengdu">成都option>   
            <option value="guangzhou">广州option>     
            <option value="beijing">北京option>    
        select>    
        <br><br>     
        其他信息:      
        <textarea name="other" id="other" v-model="userInfo.other">
        textarea>       
        <br><br>    
        <input type="checkbox" name="agreement" id="agreement"  v-model="userInfo.checked">阅读并接受
        <a href="">《用户协议》a>     
        <br><br>    
        <input type="submit" value="提交" :disabled="userInfo.disabled"> 
    form>  
    <script type="text/javascript">   
        const vue=new Vue({      
            el:"#app",        
            data:{      
                userInfo:{        
                    userName:'',    
                    userPwd:'',       
                    userAge:'',           
                    sex:'',              
                    hobby:[],         
                    region:'',           
                    other:'',          
                    disabled:true,      
                    checked:false      
                }         
            },         
            methods: {     
            },          
            watch:{          
                'userInfo.checked':{     
                    handler(val){       
                        val == true ? this.userInfo.disabled=false : this.userInfo.disabled=true           
                    }         
                }        
            }     
        })   
    script>
body>

十七、过滤器

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

注册过滤器

//Vue.filter(name,callback) 或 new Vue{filters:{}}
//全局过滤器
Vue.filter('mySlice',function(value){  
    return value.slice(0,4)
})
//局部过滤器
new Vue({  
    filters:{   
        timeFormater(){   
            return      
        }   
    }
})

使用过滤器:

	
<h3>现在是:{{time | timeFormater}h3>

<h3>现在是:{{time | timeFormater('YYYY_MM_DD')}h3>

<h3 :x="msg | mySlice">尚硅谷h3>
  • 过滤器也可以接收额外参数、多个过滤器也可以串联
  • 并没有改变原本的数据, 是产生新的对应的数据

十八、内置指令

v-bind : 单向绑定解析表达式, 可简写为 :xxx

v-model : 双向数据绑定

v-for : 遍历数组/对象/字符串

v-on : 绑定事件监听, 可简写为@

v-if : 条件渲染(动态控制节点是否存存在)

v-else : 条件渲染(动态控制节点是否存存在)

v-show : 条件渲染 (动态控制节点是否展示)

v-text指令

向其所在的节点中渲染文本内容

v-text会替换掉节点中的内容,插值语法{{xx}}则不会。

<div v-text="name">div>  //尚硅谷<div v-text="name">你好div>  //尚硅谷

v-html指令

向指定节点中渲染包含html结构的内容 v-html可以识别html结构

v-html会替换掉节点中所有的内容,插值语法{{xx}}则不会

<div v-html="str" style="background-color: blue;">div> //你好啊!

// str:'<h3>你好啊!h3>',

v-cloak指令

Vue实例创建完毕并接管容器后,会删掉v-cloak属性

使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题

[v-cloak]{  
    display:none;
}
<h2 v-cloak>{{name}}h2>

v-once指令

  • v-once所在节点在初次动态渲染后,就视为静态内容了。
  • 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
<div id="root">   
    <h2 v-once>初始化的n值是:{{n}}h2>   
    <h2>当前的n值是:{{n}}h2> 
    <button @click="n++">点我n+1button>
div>

v-pre指令

  • Vue跳过其所在节点的编译过程。
  • 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
<div id="root">  
    <h2 v-pre>Vue其实很简单h2> 
    <h2 >当前的n值是:{{n}}h2>   
    <button @click="n++">点我n+1button>
div>

十九、自定义指令

局部指令

函数式

  • 成功绑定时调用
  • 模板重新解析时调用
directives:{ 
    big(element,binding){  
        console.log('big',this) //注意此处的this是window     
        element.innerText = binding.value * 10  
    },
}

对象式

钩子函数

  • bind()成功绑定时调用
  • inserted()元素插入页面时调用
  • update() 模板重新解析时调用
directives:{  
    fbind:{        
        //指令与元素成功绑定时(一上来)       
        bind(element,binding){           
            element.value = binding.value   
        },        
        //指令所在元素被插入页面时    
        inserted(element,binding){      
             element.focus()      
        }       
        //指令所在的模板被重新解析时        
        update(element,binding){       
            element.value = binding.value       
        }    
    }
}

全局指令

函数式

Vue.directive('fbind',function(element,binding){	
    element.innerText = binding.value * 10
})

对象式

Vue.directive('fbind',{    
    //指令与元素成功绑定时(一上来)
    bind(element,binding){       
        element.value = binding.value   
    },  
    //指令所在元素被插入页面时  
    inserted(element,binding){    
        element.focus()    
    },    
    //指令所在的模板被重新解析时 
    update(element,binding){     
        element.value = binding.value  
    }
})

1.指令定义时不加v-,但使用时要加v-

2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。

'big-number'(element,binding){   
    // console.log('big')  
    element.innerText = binding.value * 10
}

二十、生命周期

image-20211219124905030

beforeCreate

在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用。this指数据监测、数据代理

此时无法通过vm访问data上中的数据、methods中的方法

created

在实例创建完成后被立即同步调用。在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且 $el property 目前尚不可用。this指数据监测、数据代理

此时可以通过vm访问data上中的数据、methods中配置的方法

beforeMount

  • 页面呈现的是未经Vue编译的DOM结构
  • 所有对DOM的操作,最终都不奏效

mounted(常用)

Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)

  • 页面中呈现的是经过Vue编译的DOM
  • 对DOM的操作均有效(尽可能避免)至此初始化过程结束,一 般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件、等初始化操作。

发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】

beforeUpdate

数据是新的,但页面是旧的,即:页面尚未和数据保持同步。

updated

数据是新的,页面也是新的,即:页面和数据保持同步。

beforeDestroy(常用)

vm中所有的: data、methods、指令等等,都处于可用状态,马上要执行销毁过程,一般在此段:关闭定时器、取消订阅消息、解绑自定义事件等收尾操作

清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。

destroyed

  • 销毁后借助Vue开发者工具看不到任何信息。
  • 销毁后自定义事件会失效,但原生DOM事件依然有效。
  • 一般不会在beforeDestroy操作数据,因为即便操作数据,也**不会再触发更新**流程了。

Vue组件化编程

一、组件

  • 实现局部功能的代码和资源的集合

  • 组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的

  • 当我们创建组件标签时,Vue解析时会帮我们创建组件的实例对象

  • 每次调用Vue.extend,返回的都是一个全新的VueComponent构造函数

  • this指向

    组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】

    new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】

  • VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。

    Vue的实例对象,以后简称vm。

  • 一个重要的内置关系:VueComponent.prototype._proto_ === Vue.prototype

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BokKHvmy-1641102674784)(C:\Users\骑上这只不归鹿\AppData\Roaming\Typora\typora-user-images\image-20211223135643387.png)]

1. 非单文件组件

一个文件包含多个组件

  • 创建组件

    //第一步:创建school组件
    const school = Vue.extend({
        template:`
            

    学校名称:{{schoolName}}

    学校地址:{{address}}

    `
    , //模板只有一个根节点 将替换组件标签 //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。 data(){ return { schoolName:'尚硅谷', address:'北京昌平' } }, methods: { showName(){ alert(this.schoolName) } }, })
  • 注册组件

    new Vue({
        el:'#root',
        data:{
            msg:'你好啊!'
        },
        //第二步:注册组件(局部注册)只能在root中使用
        components:{
            school,
            student
        }
    })
    
    
    //第二步:全局注册组件,在root1 root2都能使用
    Vue.component('hello',hello)
    
    
  • 使用组件(写组件标签)

    <div id="root">
        <hello>hello>
        <hr>
        <h1>{{msg}}h1>
        <hr>
        
        <school>school>
        <hr>
        
        <student>student>
        <hello>hello>
    div>
    
    <div id="root2">
        <hello>hello>
    div>
    

关于组件名

  • 一个单词组成

    第一种写法(首字母小写):school

    第二种写法(首字母大写):School

  • 多个单词组成

    第一种写法(kebab-case命名):my-school

    第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)

  • 备注

    (1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。

    (2).可以使用name配置项指定组件在开发者工具中呈现的名字。

    const s = Vue.extend({
        name:'atguigu', // 指定组件在开发者工具
        template:`
        

    学校名称:{{name}}

    学校地址:{{address}}

    `
    , data(){ return { name:'尚硅谷', address:'北京' } } })

组件嵌套

子组件在父组件中注册

// 创建学生组件
const student=Vue.extend({
    template:`
    

{{stuName}}

{{stuAge}}

`
, data() { return { stuName: '欧泽巍', stuAge:18, }; }, }) // 创建学校组件 const school=Vue.extend({ template: `

{{schName}}

`
, components:{ student }, data() { return { schName: '西南石油大学', }; }, }) // 创建app组件 const app=Vue.extend({ template: `
`
, components:{ school } }) // 创建VM实例 const vm=new Vue({ el:"#root", template:`
`
, components:{ app } })

2. 单文件组件

一个文件包含一个组件(.vue)

命名:

  • school.vue
  • School.vue(常用)
  • my-school.vue
  • MySchool.vue(常用)

一个.vue 文件的组成(3 个部分)

  • 模板页面

    
    
  • JS 模块对象

    
    
  • 样式

    
    

ES6模块化语法

模块功能主要由两个命令构成:export 和 import。

⚫ export 命令用于规定模块的对外接口
⚫ import 命令用于输入其他模块提供的功能


//1.分别暴露 m1.js
export let school = '尚硅谷';

export function teach() {
    console.log("我们可以教给你开发技能");
}

//2.统一暴露 m2.js
let school = '尚硅谷';

function findJob(){
    console.log("我们可以帮助你找工作!!");
}

export {school, findJob};


//3.默认暴露 m3.js
//第一种写法
export default {
    school: 'ATGUIGU',
    change: function(){
        console.log("我们可以改变你!!");
    }
}
//第二种写法
school: 'ATGUIGU',
change: function(){
    console.log("我们可以改变你!!");
}
export default {school,change}
<script type="module">
//1.通用的导入方式
import * as m1 from "./src/js/m1.js";
import * as m2 from "./src/js/m2.js";
import * as m3 from "./src/js/m3.js";

//2. 解构赋值形式
import {school, teach} from "./src/js/m1.js";
import {school as guigu, findJob} from "./src/js/m2.js";//重名使用别名as
import {default as m3} from "./src/js/m3.js";  //default得用别名


//3. 简便形式  针对默认暴露
import m3 from "./src/js/m3.js";

二、脚手架

Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)

文档: https://cli.vuejs.org/zh/。

初始化脚手架

第一步(仅第一次执行):全局安装@vue/cli。

npm install -g @vue/cli

第二步:切换到你要创建项目的目录,然后使用命令创建项目 或者vue ui创建项目

vue create xxxx

第三步:启动项目

npm run serve

如出现下载缓慢请配置 npm 淘宝镜像:

npm config set registry https://registry.npm.taobao.org

Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,

请执行:vue inspect > output.js

三、模板项目的结构

3.1脚手架文件结构

├── node_modules 
├── public
│   ├── favicon.ico: 页签图标
│   └── index.html: 主页面
├── src
│   ├── assets: 存放静态资源
│   │   └── logo.png
│   │── component: 存放组件
│   │   └── HelloWorld.vue
│   │── App.vue: 汇总所有组件
│   │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件 
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

3.2 一些错误

image-20211220172923708

​ 会报错,解决方案:要么使用render函数,要么引入完整版Vue

3.2.1 引入完整版vue.js

import Vue from 'vue/dist/vue.js'
image-20211220172758933

vue.js与vue.runtime.xxx.js的区别

(1).vue.js是完整版的Vue,包含:核心功能+模板解析器。

(2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。

因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。

3.2.2 使用render函数

new Vue({
	el:'#app',
  	render: h => h(App), //箭头函数
})

四、vue.config.js配置文件

  • 使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
  • 使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh

五、ref属性

  • 被用来给元素或子组件注册引用信息(id的替代者)(标记)
  • 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)

打标识


获取DOM元素或者组件实例

console.log(this.$refs.title) //真实DOM元素
console.log(this.$refs.btn) //真实DOM元素
console.log(this.$refs.sch) //School组件的实例对象(vc)

六、 props配置项(父传子)

功能:vc的配置项,让组件接收外部传过来的数据(父组件传递到子组件)

传递数据

传递数字,布尔值,数组,对象,我们仍然需要 v-bind 来告诉 Vue这是一个 JavaScript 表达式而不是一个字符串


接收数据

//简单声明接收
props:['name','age','sex']

//接收的同时对数据进行类型限制
props:{
    name:String,
    age:Number,
    sex:String
}

//接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
props:{
    name:{
        type:String, //name的类型是字符串
        required:true, //name是必要的
    },
    age:{
        type:Number,
        default:99 //默认值  //若未传递数据则使用默认值
    },
    sex:{
         type:String,
         required:true
    }
}

​ 备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。(相当于需要用形参去接收实参)




七、mixin(混入)配置项

功能:可以把多个组件共用的配置提取成一个混入对象(复用配置项)

定义混合

//mixin.js
export const xxx={
    data(){....},
    methods:{....}
    ....
}export const yyy={
    data(){....},
    methods:{....}
    ....
}

使用混合

//局部混入
import {xxx,yyy} from 'mixin.js'

mixins:[xxx,yyy]

//全局混入
Vue.mixin(xxx) //所有的vm,vc均混入

​ 混合中数据和组件中数据发生冲突,以组件中的数据为主(若都有钩子函数,则钩子函数合并)

八、插件

功能:用于增强Vue

本质:包含install方法的一个对象,install的第一个参数是Vue第二个及以后的参数是插件使用者传递的数据

定义插件

//plugins.js
//写法1

const obj.install = function (Vue, options) {
    // 1. 添加全局过滤器
    Vue.filter(....)

    // 2. 添加全局指令
    Vue.directive(....)

    // 3. 配置全局混入(合)
    Vue.mixin(....)

    // 4. 添加实例方法
    Vue.prototype.$myMethod = function () {...}
    Vue.prototype.$myProperty = xxxx
}
    
//plugins.js  
//第二种写法
export default {
    install(Vue, options){
    // 1. 添加全局过滤器
    Vue.filter(....)

    // 2. 添加全局指令
    Vue.directive(....)

    // 3. 配置全局混入(合)
    Vue.mixin(....)

    // 4. 添加实例方法
    Vue.prototype.$myMethod = function () {...}
    Vue.prototype.$myProperty = xxxx
    }
}

使用插件

//main.js
//引入插件
import plugins from './plugins'
//应用(使用)插件
Vue.use(plugins,1,2,3) 
//插件的形参options将收到实参[]

<template>	
    <div>        
    		
        <h2>学校名称:{{name | mySlice}}h2>  		
        <h2>学校地址:{{address}}h2>		
        <button @click="test">点我测试一个hello方法button>	
    div>template><script>	
        export default {	
            name:'School',	
            data() {	
                return {	
                    name:'尚硅谷atguigu',	
                    address:'北京',		
                }		
            },		
            methods: {	
                test(){        
                    //使用插件中给Vue原型对象添加的hello方法		
                    this.hello()			
                }		
            },	
        }
script>

九、scoped样式

组件中的样式都要汇总

作用:让样式在局部生效,防止命名冲突。

<style scoped>style>

十、总结TodoList案例(子传父)

  1. 组件化编码流程:

    ​ (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

    ​ (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​ 1**).一个组件在用:放在组件自身即可**。

    ​ 2). 一些组件在用:放在他们共同的父组件上状态提升)。

    ​ (3).实现交互:从绑定事件开始。

  2. props适用于:

    (1).父组件 ==> 子组件 通信

    (2).子组件 ==> 父组件 通信

    父组件定义一个函数,参数为要接收的数据,然后将函数通过props传递给子组件,随后子组件调用该函数将数据传递给父组件,在父组件中的函数中对数据进行处理(间接实现兄弟组件数据传递)

    //父组件
    methods: {
        addTodo(todoObj){
            this.todos.unshift(todoObj)
        }
    },
    
    //子组件
    methods: {
        //包装为todo对象
        add(e){
            /*  if(e.target.value=='')
            {
            return alert("不能输入为空")
            } */
            if(this.task.trim()==''){
                return alert('不能输入为空')
            }
            const todoObj={id:nanoid(),name:e.target.value,done:false}
            console.log(todoObj)
            this.addTodo(todoObj)
            // e.target.value=''
            this.title = ''
        }
    },
    props:['addTodo']
    
  3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的

  4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做

十一、webStorage

  • 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)

  • 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。

  • 相关API

    该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。

    sessionStorage.setItem('key', 'value')

    localStorage .setItem('key', 'value')

    该方法接受一个键名作为参数,返回键名对应的值。

    sessionStorage.getItem('key', 'value')

    localStorage .getItem('key', 'value')

该方法接受一个键名作为参数,并把该键名从存储中删除。

sessionStorage.removeItem('key')

localStorage .removeItem('key')

该方法会清空存储中的所有数据。

localStorage .clear()

sessionStorage.clear()

备注:

  1. webStorage存储的value是String类型,若需要存储对象、数组类型需要通过JSON.stringify()来转换

  2. SessionStorage存储的内容会随着浏览器窗口关闭而消失

  3. LocalStorage存储的内容,需要手动清除才会消失

  4. xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。

  5. JSON.parse(null)的结果依然是null。

十二、组件的自定义事件(子传父)

  • 一种组件间通信的方式,适用于:子组件 ===> 父组件
  • 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在父组件中给子组件绑定自定义事件(事件的回调在父组件中)。

1.绑定自定义事件



methods:{	
test(数据) 
{ }
}

//若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。
    
......
mounted(){    
this.$refs.demo.$on.$once('atguigu',this.test)}
methods:{ 
test(数据)   
{  }
}

组件上绑定原生DOM事件,需要使用native修饰符


2.触发自定义事件

//子组件中
this.$emit('atguigu',数据)

注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!

在哪里处理数据事件绑定就在哪儿,事件的回调函数就写在哪儿

例如:在父组件中给子组件绑定事件,就在父组件中写回调函数

十三、全局事件总线(GlobalEventBus)(任意组件)

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 安装全局事件总线:

    new Vue({
    	......
    	beforeCreate() {
    		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
    	},
        ......
    }) 
    
  3. 使用事件总线:

    绑定事件

    A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调函数留在A组件自身

    methods(){
      demo(data){......}
    }
    ......
    mounted() {
      this.$bus.$on('xxxx',this.demo)
    }
    

    触发事件(发送数据)

    this.$bus.$emit('xxxx',数据)

  4. 最好在beforeDestroy钩子中用$off去解绑当前组件所用到的事件

十四、消息订阅与发布(pubsub)(任意组件)

  1. 一种组件间通信的方式,适用于任意组件间通信。

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'在订阅者和发布者中引入

    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身

      methods(){
        demo(msgName,data){......}
      }
      ......
      mounted() {
        this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
      //若不在methods中配置回调函数,则在回调函数要写成箭头函数
       //this.pid = pubsub.subscribe('xxx',(msgName,data)=>{})   
      
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用pubsub.unsubscribe(pid)去取消订阅。

十五、插槽(slot)(父传子结构)

让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件

默认插槽

适用于同一组件数据、结构不同


        
           
html结构1

具名插槽

适用于有多个插槽


        
            

            
        


        

作用域插槽

数据在组件的自身但结构需要组件的使用者来决定

同一数据不同结构

  • 在子组件中slot标签通过v-bind发送数据
  • 在父组件中template标签中scope属性来接收子组件的数据

		
			
		

		
			
		


        
		
        

十六、nextTick

  1. 语法:this.$nextTick(回调函数)

    this.$nextTick(function () {
        this.$refs.inputName.focus();
    });
    
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。

  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

十六、Vue封装的过度与动画

在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。

Vue基础知识_第1张图片

  • 元素进入的样式:

    • v-enter:进入的起点
    • v-enter-active:进入过程中
    • v-enter-to:进入的终点
  • 元素离开的样式:

    • v-leave:离开的起点

    • v-leave-active:离开过程中

    • v-leave-to:离开的终点

v为默认的,要指定相应元素,则v替换为相应元素的name属性

使用包裹要过度的元素(只能有一个元素),并配置name属性


	

你好啊!

若有多个元素需要过度,则需要使用:,且每个元素都要指定key值。

使用第三方库

引入

import 'animate.css'

使用

enter-active-class

leave-active-class


    

你好啊!

尚硅谷!

Vue 中的 ajax

一、解决开发环境 Ajax 跨域问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Fn4yma4-1641102674784)(C:\Users\骑上这只不归鹿\AppData\Roaming\Typora\typora-user-images\image-20211224215549746.png)]

配置代理服务器

方法一

在vue.config.js中添加如下配置

devServer:{
  proxy:"http://XXX:5000" //开启代理服务器,并指定服务器地址
}

在客户端中

getStudents(){
    axios.get('http://localhost:8080/students').then(
        response => {
            console.log('请求成功了',response.data)
        },
        error => {
            console.log('请求失败了',error.message)
        }
    )
},

上述例子

  • 想要请求http://XXX:5000/students的资源,

  • 先将请求发送给代理服务器http://localhost:8080,

  • 查找是否有http://localhost:8080/students资源,

  • 若有则返回,若没有则去http://XXX:5000/students找

说明:

  • 优点:配置简单,请求资源时直接发给前端(8080)即可。
  • 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
  • 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
  • 请求服务器资源,将请求发送给代理服务器,随后代理服务器转发给服务器

方法二

在vue.config.js中添加如下配置

module.exports = {
	devServer: {
      proxy: {
      '/demo': {// 匹配所有以 '/api1'开头的请求路径
        target: 'http://localhost:5000',// 代理目标的基础路径
        changeOrigin: true,
        pathRewrite: {'^/demo': ''} //用正则表达式 将路径重写
      },
      '/api2': {// 匹配所有以 '/api2'开头的请求路径
        target: 'http://localhost:5001',// 代理目标的基础路径
        changeOrigin: true,
        pathRewrite: {'^/api2': ''}
      }
    }
  }
}
/*
   changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
   changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
   changeOrigin默认值为true
*/

在客户端中

getCars(){
    axios.get('http://localhost:8080/demo/cars').then(
        response => {
            console.log('请求成功了',response.data)
        },
        error => {
            console.log('请求失败了',error.message)
        }
    )
}

上述例子

  • 想要请求http://XXX:5000/cars的资源,

  • 先将请求发送给代理服务器http://localhost:8080/demo/cars,配置将地址转为http://localhost:8080/cars

  • 查找是否有http://localhost:8080/cars资源,

  • 若有则返回,若没有则去http://XXX:5000/cars找

说明:

  • 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
  • 缺点:配置略微繁琐,请求资源时必须加前缀

二、github案例

**问题:**若使用import引入样式(样式放在assets中),脚手架将使用严格模式检查,若样式中的其他第三方样式未引入则会报错

**解决办法:**将样式放入public中,通过link标签引入

<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">

Vuex(任意组件)

1.概念

在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写)也是一种组件间通信的方式且适用于任意组件间通信

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oFqjq2fW-1641102674784)(C:\Users\骑上这只不归鹿\AppData\Roaming\Typora\typora-user-images\image-20211227151241652.png)]

2.何时使用?

​ 多个组件需要共享数据

3.搭建vuex环境

  1. 创建文件:src/store/index.js

    //引入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
    import store from './store'
    ......
    
    //创建vm
    new Vue({
    	el:'#app',
    	render: h => h(App),
    	store
    })
    

4.store中的配置项

组件中读取vuex中的数据$store.state.sum

组件中修改vuex中的数据this.$store.dispatch('action中的方法名',数据)this.$store.commit('mutations中的方法名',数据)

配置src/store/index.js

  • state 共享的状态/数据)必须

      state:{
        sum:0,
      },
    
  • actions 处理数据(过滤数据)、接收从后端传递的数据 必须

      actions:{
          //context相当于一个mini store
        jiaOdd(context,value){  
          if(context.state.sum%2!==0)
          {
            context.commit('JIA',value)
          }
        },
        jiaWait(context,value){
          setTimeout(() => {
            context.commit('JIA',value)
          }, 500);
        }
      },
    
  • mutations 转换数据 必须

    函数名用大写

      mutations:{
        //value=>payload载荷
        JIA(state,value){
          state.sum+=value
        },
        JIAN(state,vlaue){
          state.sum-=vlaue
        }
      },
    
  • getters 将state中的数据进行处理 可选

    Getter 也可以接受其他 getter 作为第二个参数

    	getters:{
    		bigSum(state,getters){
    		return state.sum*10
    		}
    	}
    

5.四个map方法的使用

5.1 mapState

mapState 将 store 中的 state映射到局部计算属性

import { mapState } from 'vuex'

.....

computed: {

	//一般写法
	sum(){
		return this.$store.state.sum
	},
	school(){
		return this.$store.state.school
	},
	subject(){
		return this.$store.state.subject
	},
    
    //简写
    //借助mapState生成计算属性:sum、school、subject(对象写法)
    //计算属性名:'子节点名'
     ...mapState({sum:'sum',school:'school',subject:'subject'}),
         
    
    //借助mapState生成计算属性:sum、school、subject(数组写法)
    //映射的计算属性的名称与 state 的子节点名称相同时
    ...mapState(['sum','school','subject']),
},

5.2 mapGetters

mapGetters 将 store 中的 getter 映射到局部计算属性

import { mapState } from 'vuex'

.....

computed: {
    //一般写法
    bigSum(){
		return this.$store.getters.bigSum
	},
    
    //简写   
    //借助mapGetters生成计算属性:bigSum(对象写法)
    //计算属性名:'子节点名'
    ...mapGetters({bigSum:'bigSum'}),

    //借助mapGetters生成计算属性:bigSum(数组写法)
    //映射的计算属性的名称与 state 的子节点名称相同时
    ...mapGetters(['bigSum'])
},

5.3 mapActions

mapActions 用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

import { mapActions } from 'vuex'

......

computed: {
	//一般写法
	incrementOdd(){
		this.$store.dispatch('jiaOdd',this.n)
	},
	incrementWait(){
		this.$store.dispatch('jiaWait',this.n)
	},
        
    //简写   
	//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
	...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
    //借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
    //回调函数名称与 actions 的子节点名称相同时
	...mapActions(['jiaOdd','jiaWait'])
    
}

5.4 mapMutations

mapMutations用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

/*



若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
*/



import { mapActions } from 'vuex'

......

computed: {
	//一般写法
	increment(){
		this.$store.commit('JIA',this.n)
    },
	decrement(){
		this.$store.commit('JIAN',this.n)
	},
     
    //简写    
	//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
	...mapMutations({increment:'JIA',decrement:'JIAN'}),

	//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
    //回调函数名称与 actions 的子节点名称相同时
	...mapMutations(['JIA','JIAN']),
    
}

备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

6.模块化(store中modules配置项)+命名空间

目的:让代码更好维护,让多种数据分类更加明确。

第一种写法:引入相应的模块

//index.js
import Vue from 'vue'
import Vuex from 'vuex';

//引入模块
import count from './count';
import person from './person';

//应用插件
Vue.use(Vuex)


export default new Vuex.Store({
  modules:{
    countAbout:count,
    personAbout:person
  }
})

//在count.js中

export default {
    namespaced: true, //开启命名空间
    state: {
    },
    actions: {
    },
    mutations: {
    },
    getters: {
    }
}
....

第二种写法:在集中写在store/index.js

//index.js
import Vue from 'vue'
import Vuex from 'vuex';

Vue.use(Vuex)

const countAbout = {
  namespaced:true,//开启命名空间
  state:{x:1},
  mutations: { ... },
  actions: { ... },
  getters: {
    bigSum(state){
       return state.sum * 10
    }
  }
}

export default new Vuex.Store({
  modules: {
    countAbout,
    personAbout
  }
})
  • 组件中读取state数据

    //方式一:一般写法	this.$store.state.模块名.数据名
    this.$store.state.personAbout.list  
    
    
    //方式二:借助mapState读取 	...mapState('模块名',数组方法/对象方法)
    ...mapState('countAbout',['sum','school','subject']),
    
  • 组件中读取getters数据

    //方式一:一般写法	this.$store.getters['模块名/计算属性名']
    this.$store.getters['personAbout/firstPersonName']
    
    //方式二:借助mapGetters读取 ...mapGetters('模块名',数组方法/对象方法)
    ...mapGetters('countAbout',['bigSum'])
    
  • 组件中调用dispatch

    //方式一:一般写法	this.$store.dispatch['模块名/方法名',传递的数据]
    this.$store.dispatch('personAbout/addPersonWang',person)
    
    //方式二:借助mapActions读取 ...mapActions('模块名',数组方法/对象方法)
    ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
  • 组件中调用commit

    //方式一:一般写法 	this.$store.commit['模块名/方法名',传递的数据]
    this.$store.commit('personAbout/ADD_PERSON',person)
    
    //方式二:借助mapMutations ...mapGetters('模块名',数组方法/对象方法)
    ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
    

VueRouter

1.概念

vue 的一个插件库,专门用来实现 SPA(单页面) 应用

对 SPA 应用的理解

  1. 单页 Web 应用(single page web application,SPA)。
  2. 整个应用只有一个完整的页面。
  3. 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
  4. 数据需要通过 ajax 请求获取

什么是路由?

  1. 一个路由就是一组映射关系(key - value)

    key 为路径, value 可能是 function 或 component

路由分类

  1. 后端路由:
  • 理解:value 是 function, 用于处理客户端提交的请求。
  • 工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数
    来处理请求, 返回响应数据
  1. 前端路由:
  • 理解:value 是 component,用于展示页面内容。
  • 工作过程:当浏览器的路径改变时, 对应的组件就会显示。

2.基本使用

安装vue-router,命令:npm i vue-router

编写src/router/index.js 配置项:

import Vue from 'vue'
//引入VueRouter
import VueRouter from 'vue-router'

//引入相应组件
import Home from '../components/Home'
import About from '../components/About'

//应用插件
Vue.use(VueRouter)

export default new VueRouter({
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home
		}
	]
})

实现跳转切换(active-class可配置高亮样式)

router-link标签相当于a标签

<router-link active-class="active" to="/about">Aboutrouter-link>

指定组件展示位置

<router-view>router-view>

几个注意点

  • 路由组件通常存放在views文件夹,一般组件通常存放在components文件夹。
  • 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  • 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  • 整个应用只有一个router,可以通过组件的$router属性获取到。

3.多级路由(多级路由)

  1. 配置路由规则,使用children配置项
routes:[
	{
		path:'/about',
		component:About,
	},
	{
		path:'/home',
		component:Home,
		children:[ //通过children配置子级路由
			{
				path:'news', //第一种写法不加/
				component:News
			},
			{
				path:'/home/message', //第二种写法 写完整路径
				component:Message
			}
		]
	}
]
  1. 跳转(要写完整路径
<router-link to="/home/news">Newsrouter-link>

4.路由的query参数

传递参数


<router-link :to="/home/message/detail?id=666&title=你好">跳转router-link>
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">跳转router-link>
				

<router-link 
	:to="{
		path:'/home/message/detail',
		query:{
		   id:m.id,
            title:m.title
		}
	}"
>跳转router-link>

5.命名路由

简化路由的跳转

给路由命名

{
    path:'/home',
        component:Home,
            children:[
                {
                    name:'news', //给路由命名
                    path:'news',
                    component:News,
                },
                {
                    path:'message',
                    component:Message,
                    children:[
                        {
                            path:'detail',
                            component:Detail
                        }
                    ]					
                }
            ]
}

简化跳转


<router-link to="/home/news">跳转router-link>


<router-link class="list-group-item" active-class="active" :to="{name:'news'}">Newsrouter-link>


<router-link 
	:to="{
		name:'news',
		query:{
		   id:666,
            title:'你好'
		}
	}"
>跳转router-link>

6.路由的params参数

配置路由,声明接收params参数

{
    path:'/home',
        component:Home,
            children:[
                {
                    name:'news', //给路由命名
                    path:'news',
                    component:News,
                },
                {
                    path:'message',
                    component:Message,
                    children:[
                        {
                            path:'detail/:id/:title',
                            component:Detail
                        }
                    ]					
                }
            ]
}

传递参数


<router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{m.title}}router-link>


<router-link 
	:to="{
		name:'detail',
		params:{
		   id:m.id,
           title:m.title
		}
	}"
>跳转router-link>

接收参数

<li>消息编号:{{$route.params.id}}li>
<li>消息标题:{{$route.params.title}}li>

特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

7.路由的props配置(接收参数)

让路由组件更方便的收到参数

{
	name:'xiangqing',
	path:'detail/:id',
	component:Detail,

	//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
	// props:{a:900}

	//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
	 props:true
	
	//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
	props(route){
		return {
			id:route.query.id,
			title:route.query.title
		}
	}
}

8.的replace属性

  • 控制路由跳转时操作浏览器历史记录的模式

  • 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push

    push:跳转后不会删除跳转前的记录

    replace:跳转后将替换跳转前的记录

  • 如何开启replace模式:News

9.编程式路由导航

不借助实现路由跳转,让路由跳转更加灵活

//$router的两个API
this.$router.push({
	name:'xiangqing',
		params:{
			id:xxx,
			title:xxx
		}
})

this.$router.replace({
	name:'xiangqing',
		params:{
			id:xxx,
			title:xxx
		}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go(n) //可前进也可后退

10.缓存路由组件

让不展示的路由组件保持挂载,不被销毁。

include为组件名

缓存多个:include:”[]”

<keep-alive include="News">    
    <router-view>router-view>
keep-alive>

11.两个新的生命周期钩子

activated路由组件被激活时触发。

deactivated路由组件失活时触发。

12.路由元信息(meta配置)

定义路由的时候可以配置 meta 字段

meta配置项中用户可自定义的数据

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      children: [
        {
          path: 'bar',
          component: Bar,
          // a meta field
          meta: { requiresAuth: true }
        }
      ]
    }
  ]
})

13.路由守卫

对路由进行权限控制

全局路由守卫

全局前置守卫:初始化时执行、每次路由切换前执行

router.beforeEach((to,from,next)=>{
	//to:即将要进入的目标 路由对象
    //from:当前导航正要离开的路由
    
	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
		if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
			next() //放行
		}else{
			alert('暂无权限查看')
			// next({name:'guanyu'})
		}
	}else{
		next() //放行
	}
})

全局后置守卫:初始化时执行、每次路由切换后执行

router.afterEach((to,from)=>{
	if(to.meta.title){ 
		document.title = to.meta.title //修改网页的title
	}else{
		document.title = 'vue_test'
	}
})

独享守卫

可以在路由配置上直接定义 beforeEnter 守卫

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

组件内守卫

在路由组件内直接定义以下路由导航守卫

  • beforeRouteEnter
  • beforeRouteUpdate
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

14.路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器
  3. hash模式:
    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  4. history模式:
    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题(后端错误地将前端路由认为是资源请求)。
    4. 因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

Vue UI组件库

移动端常用 UI 组件库

Vant https://youzan.github.io/vant

Cube UI https://didi.github.io/cube-ui

Mint UI http://mint-ui.github.io

PC 端常用 UI 组件库

Element UI https://element.eleme.cn
IView UI https://www.iviewui.com

你可能感兴趣的:(Vue,前端知识,vue.js,javascript,前端)