Vue2知识点

Vue2知识点

一、模板语法

1、插值语法

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

2、指令语法

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

data与el的2种写法

​ 1.el有2种写法

​ (1).new Vue时候配置el属性。

​ (2).先创建Vue实例,随后再通过vm.$mount(‘#root’)指定el的值。

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

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

​ 2.data有2种写法

​ (1).对象式

​ (2).函数式

new Vue({
    el:'#root',
    //data的第一种写法:对象式
    data:{
        name:'my'
    }

    //data的第二种写法:函数式
    data(){
        console.log('@@@',this) //此处的this是Vue实例对象
        return{
            name:'my'
        }
    }
})

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

​ 3.一个重要的原则:

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

二、数据绑定

1、单向绑定(v-bind):数据只能从data流向页面。

​ 简写方式(v-bind:)->(:)

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

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

三、MVVM模型

MVVM模式是通过DOM监听 + 数据绑定完成的

  • M是指Model 是模型:对应的是data中的数据(有自己写的,有通过请求从数据库中获取的)
  • V是指View视图:是我们看到的页面 , 对应的是一大串的HTML+CSS+JS交互代码+img等一系列的资源,即模板
  • VM是指ViewModel视图模型:Vue实例对象

img

data中所有的属性,最后都出现在了vm身上。

vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。

四、数据代理

1、Object.defineProperty(对象, 属性名, 属性描述)

let number = 18
let person = {
    name:'张三',
    sex:'男',
}
let obj = {x:18}

Object.defineProperty(person,'age',{
    // value:18,
    enumerable:true, //控制属性是否可以枚举,默认值是false
    // writable:true, //控制属性是否可以被修改,默认值是false
    configurable:true //控制属性是否可以被删除及属性特性在第一次设置之后可否被重新定义特性,默认值是false

    //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
    get(){
        console.log('有人读取age属性了')
        return obj.x
    },

    //当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
    set(value){
        console.log('有人修改了age属性,且值是',value)
        number = value
    }

})

// console.log(Object.keys(person))

console.log(person)

注:当使用了getter或setter方法,不允许使用writable和value这两个属性

2、Vue中的数据代理

通过vm对象来代理data对象中属性的操作(读/写),可以更加方便的操作data中的数据

原理:

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

五、事件处理

1、事件的基本使用

  • 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
  • 事件的回调需要配置在methods对象中,最终会在vm上;
  • methods中配置的函数,不要用箭头函数!否则this就不是vm了;
  • methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
  • @click=“demo” 和 @click=“demo($event)” 效果一致,但后者可以传参;

2、事件修饰符

DOM事件流(event flow )存在三个阶段:事件捕获阶段(由外到内进行事件传播)、处于目标阶段、事件冒泡阶段(由内到外进行事件传播)。

  • prevent:阻止默认事件(常用) event.preventDefault()

    <a href=“http://www.baidu.com” @click.prevent=“showInfo”>点我提示信息

  • stop:阻止事件冒泡(常用) event.stopPropagation()

    <a href=“http://www.baidu.com” @click.prevent.stop=“showInfo”>点我提示信息

  • once:事件只触发一次(常用)

  • capture:使用事件的捕获模式

  • self:只有event.target是当前操作的元素时才触发事件

  • passive:事件的默认行为立即执行,无需等待事件回调执行完毕

3、键盘事件

常用按键别名:

  • 回车 => enter
  • 删除 => delete (捕获“删除”和“退格”键)
  • 退出 => esc
  • 空格 => space
  • 换行 => tab (特殊,必须配合keydown去使用)
  • 上 => up 下 => down 左 => left 右 => right

Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)

系统修饰键(用法特殊):ctrl、alt、shift、meta

(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。

(2).配合keydown使用:正常触发事件。

自定义按键别名:

Vue.config.keyCodes.自定义键名 = 键码

六、计算属性computed

  • 要显示的数据不存在,要通过计算得来
  • 在 computed 对象中定义计算属性
  • 在页面中使用{{方法名}}来显示计算的结果

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

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

3、get函数什么时候执行?

​ (1)初次读取时会执行一次。

​ (2)当依赖的数据发生改变时会被再次调用。

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

5.备注:

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

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

var vm = new Vue({
    el: '#demo',
    data: {
        firstName: 'Foo',
        lastName: 'Bar'
},
computed: {
    fullName: {
        // getter
        get: function () {
            return this.firstName + ' ' + this.lastName
        },
        // setter
        set: function (newValue) {
            var names = newValue.split(' ');
            this.firstName = names[0];
            this.lastName = names[names.length - 1];
        }
    }
}
});
//现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。

计算属性禁用箭头函数

箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,例如 aDouble: () => this.a * 2 // this.a 将是 undefined

七、侦听属性watch

  1. 通过通过 vm 对象的$watch()或 watch 配置来监视指定的属性
  2. 当属性变化时, 回调函数自动调用, 在函数内部进行计算
  3. 监视的属性必须存在,才能进行监视!!
  4. 监视的两种写法:

​ (1).new Vue时传入watch配置

​ (2).通过vm.$watch监视

<div id="root">
    <h2>今天天气很{{info}}</h2>
    <button @click="changeWeather">切换天气</button>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false

    const vm = new Vue({
        el:'#root',
        data:{
            isHot:true,
        },
        computed:{
            info(){
                return this.isHot ? '炎热' : '凉爽'
            }
        },
        methods: {
            changeWeather(){
                this.isHot = !this.isHot
            }
        },
        // 第一种方法
        /* watch:{
            isHot:{
                immediate:true, //初始化时让handler调用一下
                // handler什么时候调用?当isHot发生改变时。
                handler(newValue,oldValue){
                    console.log('isHot被改了',newValue,oldValue)
                }
            }
        } */
    })

    // 第二种方法
    vm.$watch('isHot',{
        immediate:true, //初始化时让handler调用一下
        //handler什么时候调用?当isHot发生改变时。
        handler(newValue,oldValue){
            console.log('isHot被修改了',newValue,oldValue)
        }
    })
</script>

深度监视:

​ (1)Vue中的watch默认不监测对象内部值的改变(一层)

​ (2)配置deep:true可以监测对象内部值改变(多层)

备注:

​ (1)Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!

​ (2)使用watch时根据数据的具体结构,决定是否采用深度监视。

watch:{
    isHot:{
        // immediate:true, //初始化时让handler调用一下
        //handler什么时候调用?当isHot发生改变时。
        handler(newValue,oldValue){
            console.log('isHot被修改了',newValue,oldValue)
        }
    },
    //监视多级结构中某个属性的变化
    /* 'numbers.a':{
        handler(){
            console.log('a被改变了')
        }
    } */
    //监视多级结构中所有属性的变化
    numbers:{
        deep:true,
        handler(){
            console.log('numbers改变了')
        }
    }
}
computed和watch的区别
  • 是否支持缓存

    • computed支持缓存,只有依赖数据改变才会从新计算
    • watch不支持缓存,数据一变直接触发相应操作
  • 是否支持异步

    • computed不支持异步,当computed内有异步操作时无效,无法监听数据的变化
    • watch支持异步
  • 流程

    • computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
    • 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值
  • 属性于对应操作

    • computed:一对多或一对一
    • watch:一对多
  • 默认

    • computed属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法
    • 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作

八、绑定样式

  1. class样式

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

​ 字符串写法适用于:类名不确定,要动态获取。

​ 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。

​ 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

  1. style样式

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

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

<div id="root">
    <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
    <div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/>

    <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
    <div class="basic" :class="classArr">{{name}}</div> <br/><br/>

    <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
    <div class="basic" :class="classObj">{{name}}</div> <br/><br/>

    <!-- 绑定style样式--对象写法 -->
    <div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
    <!-- 绑定style样式--数组写法 -->
    <div class="basic" :style="styleArr">{{name}}</div>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    const vm = new Vue({
        el:'#root',
        data:{
            name:'my',
            mood:'normal',
            classArr:['my1','my2','my3'],
            classObj:{
                my1:false,
                my2:false,
            },
            styleObj:{
                fontSize: '40px',
                color:'red',
            },
            styleObj2:{
                backgroundColor:'orange'
            },
            styleArr:[
                {
                    fontSize: '40px',
                    color:'blue',
                },
                {
                    backgroundColor:'gray'
                }
            ]
        },
        methods: {
            changeMood(){
                const arr = ['happy','sad','normal']
                const index = Math.floor(Math.random()*3)
                this.mood = arr[index]
            }
        },
    })
</script>

九、条件渲染

1、v-if

满足条件是会渲染到html中,不满足条件时是不会渲染到html中的,是通过操纵dom元素来进行切换显示

  • 写法:
    • v-if=“表达式”
    • v-else-if=“表达式”
    • v-else=“表达式”
  • 适用于:切换频率较低的场景。
  • 特点:不展示的DOM元素直接被移除。
  • 注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。

2、v-show

元素始终被渲染到HTML,它只是简单的伪元素设置css的style属性,当不满足条件的元素被设置style=“display:none”的样,是通过修改元素的的CSS属性(display)来决定实现显示还是隐藏

  • 写法:v-show=“表达式”
  • 适用于:切换频率较高的场景。
  • 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉

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

3、比较v-if和v-show

  • v-if需要操作dom元素,有更高的切换消耗,v-show只是修改元素的的CSS属性有更高的初始渲染消耗,如果需要非常频繁的切换,建议使用v-show较好,如果在运行时条件很少改变,则使用v-if较好
  • 当条件不成立时, v-if 的所有子节点不会解析(项目中使用)

十、列表渲染(v-for)

v-for指令:

  • 用于展示列表数据
  • 语法:v-for=“(item, index) in xxx” :key=“yyy”
  • 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

1、虚拟DOM中key的作用:

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

对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。

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

(1)若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

(2)如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。

开发中如何选择key?

(1)最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。

(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
使用index作为key是没有问题的。

2、列表过滤

<div id="root">
    <h2>人员列表</h2>
    <input type="text" placeholder="请输入名字" v-model="keyWord">
    <ul>
        <li v-for="(p,index) of filPerons" :key="index">
            {{p.name}}-{{p.age}}-{{p.sex}}
        </li>
    </ul>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    //用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
                })
            }
        }
    }) 
</script>
过滤器filter

功能: 对要显示的数据进行特定格式化后再显示

注意: 并没有改变原本的数据, 是产生新的对应的数据

vue中的过滤器可以用在两个地方:双花括号插值和 v-bind 表达式

{{ msg | capitalize }}

<div v-bind:id="rowId | formatId"></div>

定义filter

// 在组件的选项中定义本地的过滤器(与data同级)
filters:{
    capitalize(val){
        if(!val) return '';
        val = val.toString();
        return val.charAt(0).toUpperCase + val.slice(1)
}
    
// 定义全局过滤器: 
Vue.filter('capitalize',function(val){
    if(!val) return '';
    val = val.toString();
    return val.charAt(0).toUpperCase + val.slice(1)
})
new Vue({
    // ...
})

注:当全局过滤器和局部过滤器重名时,会采用局部过滤器

filter中的回调函数有一个要求:必须返回一个Boolean值

  • true:函数内部会自动将这次回调的val加入到新的数组中

  • false:函数内部会过滤掉这次的val

map函数(映射)
  • map()不会对空数组进行检测
  • map()不会改变原始数组

array.map(function(currentValue, index, arr), thisIndex)

  1. currentValue必须。当前元素的的值。
  2. index可选。当前元素的索引。
  3. arr可选。当前元素属于的数组对象。
let array = [1, 2, 3, 4, 5];

let newArray = array.map((item) => {
    return item * item;
})

console.log(newArray)  // [1, 4, 9, 16, 25]

reduce函数

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

// 将数组内小于100的数字乘以2再相加
const nums = [10,20,111,222,444,40,50]
let total = nums.filter(function(n){
    return n < 100
}).map(function(n){
    return n * 2
}).reduce(function(preVal,n){
    return preVal + n
},0)

// 更简洁的方法
let total = nums.filter(n => n < 100).map(n => n*2).reduce((pre,n) => pre + n);

3、列表排序

<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>

十一、收集表单数据

收集表单数据:

​ 若:,则v-model收集的是value值,用户输入的就是value值。

​ 若:<input type=“radio” value="男” v-model=“sex”/>,则v-model收集的是value值,且要给标签配置value值。

​ 若:

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

​ 2.配置input的value属性:

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

​ (2)v-model的初始值是数组,那么收集的的就是value组成的数组

​ 备注:v-model的三个修饰符:

​ lazy:失去焦点再收集数据 v-model.lazy

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

​ trim:首尾空格过滤

<div id="app">
    <!-- 单选框 -->
    <input type="checkbox" v-model="idAgree" />同意协议
    <button :disable="!isAgree">下一步</button>
    
    <!-- 多选框 -->
    <input type="checkbox" value="" v-model="hobby" /><input type="checkbox" value="" v-model="hobby" /><input type="checkbox" value="rap" v-model="hobby" />rap
    <input type="checkbox" value="篮球" v-model="hobby" />篮球
</div>
<script>
    const app = new Vue({
        el:'#app',
        data:{
            isAgree:false,
            hobby:[]
        }
    })
</script>

十二、内置指令与自定义指令

1、常用内置指令

  • v-text : 更新元素的 textContent
  • v-html : 更新元素的 innerHTML
  • v-if : 如果为 true, 当前标签才会输出到页面
  • v-else: 如果为 false, 当前标签才会输出到页面
  • v-show : 通过控制 display 样式来控制显示/隐藏
  • v-for : 遍历数组/对象
  • v-on : 绑定事件监听, 一般简写为@
  • v-bind : 绑定解析表达式, 可以省略 v-bind
  • v-model : 双向数据绑定 10. v-cloak : 防止闪现, 与 css 配合: [v-cloak] { display:none}

2、自定义指令

// 注册全局指令
Vue.directive('my-directive', function(el, binding){
    el.innerHTML = binding.value.toupperCase()
})

// 注册局部指令
directives : {
    'my-directive' : {
        bind (el, binding) {
            el.innerHTML = binding.value.toupperCase()
        }
    }
}
// 使用指令
v-my-directive = 'xxx'

十三、Vue实例生命周期

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

常用的生命周期方法

  • mounted(): 发送 ajax 请求, 启动定时器等异步任务
  • beforeDestory(): 做收尾工作, 如: 清除定时器

十四、构建vue-cli脚手架

  • 全局安装@vue/cli,npm install -g @vue/cli
  • 切换至创建项目的目录,vue create xxx
  • 启动项目,npm run serve

注:如出现下载缓慢配置 npm 淘宝镜像:npm config set registry https://registry.npm.taobao.org

十五、组件间通信

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

  • 父子组件通信: props; $parent / $children; provide / inject ; ref ; $attrs/ $listeners
  • 兄弟组件通信: eventBus ; vuex
  • 跨级通信: eventBus;Vuex;provide / inject 、$attrs / $listeners

1、父传子

(1)ref

作用:给节点打标识

读取方式:this.$refs.xxx

<template>
	<div>
		<h1 v-text="msg" ref="title"></h1>
		<button ref="btn" @click="showDOM">输出上方的DOM元素</button>
		<School ref="sch"/>
	</div>
</template>

<script>
	//引入School组件
	import School from './components/School'

	export default {
		name:'App',
		components:{School},
		data() {
			return {
				msg:'欢迎学习Vue!'
			}
		},
		methods: {
			showDOM(){
				console.log(this.$refs.title) //<h1>欢迎学习Vue!</h1>
				console.log(this.$refs.btn) //<button>点我输出上方的DOM元素</button>
				console.log(this.$refs.sch) //School组件的实例对象(VueComponent)
			}
		},
	}
</script>
(2)props

作用:父组件给子组件传递数据

父组件
<template>
	<div>
		<Student name="李四" sex="女" :age="18"/>
	</div>
</template>
<script>
	import Student from './components/Student'
	export default {
		name:'App',
		components:{Student}
	}
</script>

子组件
<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
		<h2>学生年龄:{{myAge+1}}</h2>
		<button @click="updateAge">尝试修改收到的年龄</button>
	</div>
</template>
<script>
	export default {
		name:'Student',
		data() {
			console.log(this)
			return {
				myAge:this.age
			}
		},
		methods: {
			updateAge(){
				this.myAge++
			}
		},
		//简单声明接收
		// 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
			}
		}
	}
</script>
(3)provide/inject

父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。

注意: 这里不论子组件嵌套有多深, 只要调用了inject 那么就可以注入provide中的数据,而不局限于只能从当前父组件的props属性中回去数据

  • provide:是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。
  • inject:一个字符串数组,或者是一个对象。属性值可以是一个对象,包含from和default默认值。

2、子传父

(1)自定义事件events

子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件

// 子组件
<template>
  <header>
    <h1 @click="changeTitle">{{title}}</h1>//绑定一个点击事件
  </header>
</template>
<script>
export default {
  name: 'app-header',
  data() {
    return {
      title:"Vue.js Demo"
    }
  },
  methods:{
    changeTitle() {
      this.$emit("titleChanged","子向父组件传值");//自定义事件  传递值“子向父组件传值”
    }
  }
}
</script>

// 父组件
<template>
  <div id="app">
    <app-header @titleChanged="updateTitle" ></app-header>//与子组件titleChanged自定义事件保持一致
   // updateTitle($event)接受传递过来的文字
    <h2>{{title}}</h2>
  </div>
</template>
<script>
import Header from "./components/Header"
export default {
  name: 'App',
  data(){
    return{
      title:"传递的是一个值"
    }
  },
  methods:{
    updateTitle(e){   //声明这个函数
      this.title = e;
    }
  },
  components:{
   "app-header":Header,
  }
}
</script>
(2)$children / $parent

使用前提是指定已建立父子关系,子实例可以用this.

parent访问父实例,子实例被推入父实例的children数组中

注意:节制使用parent和children 它们主要的目的是作为访问组件的应急方法。更推荐用props和events实现父子组件的通信

// 父组件
<template>
 <div class="hello_world">
 <div>{{msg}}</div>
 <com-a></com-a>
 <button @click="changeA">点击改变子组件值</button>
 </div>
</template>
 
<script>
import ComA from './test/comA.vue'
export default {
    name: 'HelloWorld',
    components: { ComA },
    data() {
        return {
            msg: 'Welcome'
        }
    },
     methods: {
        changeA() {
            // 获取到子组件A
            this.$children[0].messageA = 'this is new value'
        }
    }
}
</script>

// 子组件中
<template>
 <div class="com_a">
 <span>{{messageA}}</span>
 <p>获取父组件的值为: {{parentVal}}</p>
 </div>
</template>
<script>
export default {
    data() {
        return {
            messageA: 'this is old'
        }
    },
    computed:{
        parentVal(){
        return this.$parent.msg;
        }
    }
}
</script>

要注意边界情况,如在#app上拿parent得 到 的 是new Vue() 的 实 例 , 在 这 实 例 上 再 拿 parent得到的是new Vue()的实例,在这实例上再拿parent得到的是new Vue()的实例,在这实例上再拿parent得到的是undefined,而在最底层的子组件拿children是个空数组 。要注意得到children是个空数组,也要注意得到parent和children的值不一样, c h i l d r e n 的值是数组,而 children的值是数组,而 children的值是数组,而parent是个对象

(3)全局事件总线eventBus

eventBus 又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。

eventBus来实现组件之间的数据通信步骤:

// 1、初始化
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()

// 2、发送事件
// additionNum 和 showNum 为兄弟组件
<template>
 <div>
  <show-num-com></show-num-com>
  <addition-num-com></addition-num-com>
 </div>
</template>
<script>
import showNumCom from './showNum.vue'
import additionNumCom from './additionNum.vue'
export default {
 components: { showNumCom, additionNumCom }
}
</script>

// addtionNum.vue 中发送事件
<template>
 <div>
  <button @click="additionHandle">+加法器</button>  
 </div>
</template>
<script>
import {EventBus} from './event-bus.js'
console.log(EventBus)
export default {
    data(){
        return{
        num:1
        }
    },
 
    methods:{
        additionHandle(){
            EventBus.$emit('addition', {
            num:this.num++
            })
        }
    }
}
// 3、接收事件
// showNum.vue 中接收事件
<template>
 <div>计算和: {{count}}</div>
</template>
<script>
import { EventBus } from './event-bus.js'
export default {
 data() {
  return {
   count: 0
  }
 },
 mounted() {
  EventBus.$on('addition', param => {
   this.count = this.count + param.num;
  })
 }
}
</script>

移除事件监听者
import { eventBus } from ‘event-bus.js’
EventBus.$off(‘addition’, {})

Vue原型对象上包含事件处理的方法:

  • $on(eventName, listener): 绑定自定义事件监听
  • $emit(eventName, data): 分发自定义事件
  • $off(eventName): 解绑自定义事件监听
  • $once(eventName, listener): 绑定事件监听, 但只能处理一次

所有组件实例对象的原型对象的原型对象就是 Vue 的原型对象

  • 所有组件对象都能看到 Vue 原型对象上的属性和方法
  • Vue.prototype. b u s = n e w V u e ( ) , 所有的组件对象都能看到 bus = new Vue(), 所有的组件对象都能看到 bus=newVue(),所有的组件对象都能看到bus 这个属性 对象
(4)$attrs与 $listeners
(5)vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上。

Vuex各个模块

  • state:用于数据的存储,是store中的唯一数据源
  • getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
  • mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件
  • actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作
  • modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护
(6)消息订阅与发布(PubSubJS)

a. 订阅消息 --对应绑定事件监听

b. 发布消息 --分发事件

c. 取消消息订阅 --解绑事件监听

步骤:

  • 下载: npm install -S pubsub-j

  • import PubSub from ‘pubsub-js’ // 引入

  • PubSub.subscribe(‘msgName’, functon(msgName, data){ })

  • PubSub.publish(‘msgName’, data): 发布消息, 触发订阅的回调函数调用

  • PubSub.unsubscribe(token): 取消消息的订阅

十六、插槽

父组件向子组件传递带数据的标签,当一个组件有不确定的结构时, 就需要使用 slot 技术,注意:插槽内容是在父组件中编译后, 再传递给子组件的。

1、默认插槽

没有设置name属性的插槽。

<slot>这是个匿名插槽(没有name属性),这串字符是匿名插槽的默认值。</slot>

1)可以放置在组件的任意位置。
2)一个组件中只能有一个匿名插槽。
3)匿名插槽只能作为没有slot属性的元素的插槽。

2、具名插槽

具有名字的插槽,名字通过属性name来定义,一个组件中可以有很多具名插槽,出现在不同的位置。

<!-- <base-layout>组件-->
<div class="container">
    <header>
        <slot name="header"></slot>
    </header>
    <main>
        <slot></slot><!-- 一个不带name的<slot>出口会带有隐含的名字“default”。-->
    </main>
    <footer>
        <slot name="footer"></slot>
    </footer>
</div>

<base-layout>
  <template v-slot:header>
    <h1>我是头header</h1>
  </template>

  <p>我是main的内容111</p>   
  <p>我也是main的内容222</p>

  <template v-slot:footer>
    <p>我是footer</p>
  </template>
</base-layout>

1)带有 v-slot 的<template> 元素中的所有内容都将会被传入相应的插槽。

2)任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。

3、作用域插槽

父级模板里的所有内容都是在父级作用域中编译的;子级模板里的所有内容都是在子作用域中编译的。

<!-- <Child> 组件: -->
<template>
  <div>
    <h1>hey,我是组件Child的标题</h1>
    <slot v-bind:childData="childUser"></slot>
  </div>
</template>
<script>
export default {
  data() {
     return {
        childUser: { Name:"Tom", Age:23 }
    }
}
</script>

<!-- 这是父组件<Father>-->
<div>
  <h1>hey,我是父组件Father的标题</h1>
  <Child>
    <template v-slot:default="slotProps">
      {{ slotProps.childData.Name}}
      {{ slotProps.childData.Age}}
    </template>
  </Child>
</div>

十七、Vue-router路由

一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。

前端路由:key是路径,value是组件。

1、基本使用

  • 安装vue-router,命令:npm i vue-router(vue2中安装vue-router@3)

  • 应用插件:Vue.use(VueRouter)

  • 编写router配置项:

    import Vue from 'vue'
    //引入VueRouter
    import VueRouter from 'vue-router'
    
    // 引入路由插件
    Vue.use(VueRouter)
    
    //创建router实例对象,去管理一组一组的路由规则
    const router = new VueRouter({
    	routes:[
            {
                path:'/',
                redirect:'/home'
            },
    		{
    			path:'/home',
    			component:Home
    		},
    		{
    			path:'/about',
    			component:About
    		}
    	]
    })
    
    //暴露router
    export default router
    
  • 实现切换(active-class可配置高亮样式)

    About

  • 指定展示位置

    注意:

    • 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。

    • 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。

    • 每个组件都有自己的$route属性,里面存储着自己的路由信息。

    • 整个应用只有一个router,可以通过组件的$router属性获取到。

2、嵌套路由

routes:[
	{
		path:'/about',
		component:About,
	},
	{
		path:'/home',
		component:Home,
		children:[ //通过children配置子级路由
			{
				path:'news', //此处一定不要写:/news
				component:News
			},
			{
				path:'message',//此处一定不要写:/message
				component:Message
			}
		]
	}
]

跳转(要写完整路径):

<router-link to="/home/news">News</router-link>

3、参数传递(query)

1)传递参数

<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
				
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link 
	:to="{
		path:'/home/message/detail',
		query:{
		   id:666,
           title:'你好'
		}
	}"
>
    跳转
</router-link>

2)接收参数

$route.query.id
$route.query.title

4、命名路由(params参数)

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

{
	path:'/home',
	component:Home,
	children:[
		{
			path:'news',
			component:News
		},
		{
			component:Message,
			children:[
				{
					name:'xiangqing',
					path:'detail/:id/:title', //使用占位符声明接收params参数
					component:Detail
				}
			]
		}
	]
}

2)传递参数

<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
				
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link 
	:to="{
		name:'xiangqing',
		params:{
		   id:666,
            title:'你好'
		}
	}"
>跳转</router-link>

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

3)接收参数

$route.params.id

$route.params.title

5、路由的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
		}
	}
}

6、编程式路由导航

作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活

具体编码:

//$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() //可前进也可后退

注:replace属性

  • 作用:控制路由跳转时操作浏览器历史记录的模式
  • 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  • 如何开启replace模式:<router-link replace .......>News</router-link>

7、路由的懒加载

  • 当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。
  • 如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
  • 路由懒加载的主要作用是将路由对应的组件打包成一个个js代码块,只要在这个路由被访问到的时候,才加载对应的组件
import Vue from "vue";
import VueRouter from "vue-router";
 
Vue.use(VueRouter);

// 新增路由懒加载代码
const Home = () => import('../views/Home')
const About = () => import('../views/About')
const User = () => import('../views/User')
 
const routes = [
  {
    path: "/",
    name: "Home",
    component: Home,
  },
  {
    path: "/about",
    name: "About",
    component: About
  },
  {
    path: "/user/:userId",
    name: "User",
    component: User
  }
];

8.缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。

  2. 具体编码:

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

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

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  2. 具体名字:
    1. activated路由组件被激活时触发。
    2. deactivated路由组件失活时触发。

10、路由守卫

  • 作用:对路由进行权限控制

  • 分类:全局守卫、独享守卫、组件内守卫

  • 全局守卫:

//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{    
    console.log('beforeEach',to,from)    
    if(to.meta.isAuth){ 
        //判断当前路由是否需要进行权限控制        
        if(localStorage.getItem('school') === 'atguigu'){ 
            //权限控制的具体规则            
            next() //放行        
        }else{           
            alert('暂无权限查看')            
            // next({name:'guanyu'})       
        }    
    }else{       
        next() //放行    
    }
})
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
    console.log('afterEach',to,from)    
    if(to.meta.title){
        document.title = to.meta.title //修改网页的title
    }else{
        document.title = 'vue_test'
    }
})
  • 独享守卫:
beforeEnter(to,from,next){
	console.log('beforeEnter',to,from)
	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
		if(localStorage.getItem('school') === 'atguigu'){
			next()
		}else{
			alert('暂无权限查看')
			// next({name:'guanyu'})
		}
	}else{
		next()
	}
}

组件内守卫:

//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}

11、路由器的两种工作模式

  • 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。

  • hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。

  • hash模式:

    • 地址中永远带着#号,不美观 。
    • 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    • 兼容性较好。
  • history模式:

    • 地址干净,美观 。
    • 兼容性和hash模式相比略差。
    • 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。

十八、Vuex

1.概念

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.何时使用?

​ 多个组件需要共享数据时

3.搭建vuex环境

  • 创建文件: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
})
  • main.js中创建vm时传入store配置项
......
//引入store
import store from './store'
......

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	store
})

4.基本使用

  1. 初始化数据、配置actions、配置mutations,操作文件store.js

    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //引用Vuex
    Vue.use(Vuex)
    
    const actions = {
        //响应组件中加的动作
    	jia(context,value){
    		// console.log('actions中的jia被调用了',miniStore,value)
    		context.commit('JIA',value)
    	},
    }
    
    const mutations = {
        //执行加
    	JIA(state,value){
    		// console.log('mutations中的JIA被调用了',state,value)
    		state.sum += value
    	}
    }
    
    //初始化数据
    const state = {
       sum:0
    }
    
    //创建并暴露store
    export default new Vuex.Store({
    	actions,
    	mutations,
    	state,
    })
    
  2. 组件中读取vuex中的数据:$store.state.sum

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

    备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

5.getters的使用

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。(相当于计算属性)

  2. store.js中追加getters配置

    ......
    
    const getters = {
    	bigSum(state){
    		return state.sum * 10
    	}
    }
    
    //创建并暴露store
    export default new Vuex.Store({
    	......
    	getters
    })
    
  3. 组件中读取数据:$store.getters.bigSum

6.四个map方法的使用

  1. mapState方法:用于帮助我们映射state中的数据为计算属性

    computed: {
        //借助mapState生成计算属性:sum、school、subject(对象写法)
         ...mapState({sum:'sum',school:'school',subject:'subject'}),
             
        //借助mapState生成计算属性:sum、school、subject(数组写法)
        ...mapState(['sum','school','subject']),
    },
    
  2. mapGetters方法:用于帮助我们映射getters中的数据为计算属性

    computed: {
        //借助mapGetters生成计算属性:bigSum(对象写法)
        ...mapGetters({bigSum:'bigSum'}),
    
        //借助mapGetters生成计算属性:bigSum(数组写法)
        ...mapGetters(['bigSum'])
    },
    
  3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    methods:{
        //靠mapActions生成:incrementOdd、incrementWait(对象形式)
        ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
        //靠mapActions生成:incrementOdd、incrementWait(数组形式)
        ...mapActions(['jiaOdd','jiaWait'])
    }
    
  4. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

    methods:{
        //靠mapActions生成:increment、decrement(对象形式)
        ...mapMutations({increment:'JIA',decrement:'JIAN'}),
        
        //靠mapMutations生成:JIA、JIAN(对象形式)
        ...mapMutations(['JIA','JIAN']),
    }
    

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

7.模块化+命名空间

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

  2. 修改store.js

    const countAbout = {
      namespaced:true,//开启命名空间
      state:{x:1},
      mutations: { ... },
      actions: { ... },
      getters: {
        bigSum(state){
           return state.sum * 10
        }
      }
    }
    
    const personAbout = {
      namespaced:true,//开启命名空间
      state:{ ... },
      mutations: { ... },
      actions: { ... }
    }
    
    const store = new Vuex.Store({
      modules: {
        countAbout,
        personAbout
      }
    })
    
  3. 开启命名空间后,组件中读取state数据:

    //方式一:自己直接读取
    this.$store.state.personAbout.list
    //方式二:借助mapState读取:
    ...mapState('countAbout',['sum','school','subject']),
    
  4. 开启命名空间后,组件中读取getters数据:

    //方式一:自己直接读取
    this.$store.getters['personAbout/firstPersonName']
    //方式二:借助mapGetters读取:
    ...mapGetters('countAbout',['bigSum'])
    
  5. 开启命名空间后,组件中调用dispatch

    //方式一:自己直接dispatch
    this.$store.dispatch('personAbout/addPersonWang',person)
    //方式二:借助mapActions:
    ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
  6. 开启命名空间后,组件中调用commit

    //方式一:自己直接commit
    this.$store.commit('personAbout/ADD_PERSON',person)
    //方式二:借助mapMutations:
    ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
    

十九、axios网络模块封装

1、jsonp的封装

在前端开发中, 我们一种常见的网络请求方式就是JSONP

  • 使用JSONP最主要的原因往往是为了解决跨域访问的问题.

2、认识axios

2 .1 功能特点:
  • 在浏览器中发送 XMLHttpRequests 请求
  • 在 node.js 中发送 http请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
2 .2 axiox请求方式
  • axios(config)
  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])
2 .3 axios的get与post请求

3、axios发送基本请求

  • 全局安装axios: npm install axios --save
3. 1 发送get请求

img

import axios from 'axios'
 
// 1.axios的基本使用 axios(config)
axios({
  url: 'http://123.207.32.32:8000/home/multidata', // 项目接口
  // 默认是get请求 可以用method指定
  // method: 'post'
}).then(res => { // 获取返回的数据
  console.log(res);
})

// 专门针对get请求的参数拼接
axios({
  //  url:'http://123.207.32.32:8000/home/data?type=sell&page=1'
  // 参数除了可以直接拼接在url,也可以用params
  url: 'http://123.207.32.32:8000/home/data',
  params: {
    type: 'pop',
    page: 1
  }
}).then(res => {
  console.log(res);
})

3. 2 发送post请求
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
3. 3 执行delete删除数据
3. 4 使用axios 完成高并发请求
function getUserAccount() {
  return axios.get('/user/12345');
}
 
function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 两个请求现在都执行完成
  })
);

3. 3 全局配置
 
// 3.使用全局的axios和对应的配置在进行网络请求
axios.defaults.baseURL = 'http://123.207.32.32:8000'
axios.defaults.timeout = 5000 // 超时
 
axios.all([axios({
  url: '/home/multidata'
}), axios({
  url: '/home/data',
  params: {
    type: 'sell',
    page: 5
  }
})]).then(axios.spread((res1, res2) => {
  // 使用 axios.spread 可将数组 [res1,res2] 展开为 res1, res2
  console.log(res1);
  console.log(res2);
}))

4、axios的请求配置项

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 创建请求时可用的配置选项,注意只有 url 是必需的。
  • 如果没有指定 method,请求将默认使用 get 方法。
{
  // `url` 是用于请求的服务器 URL
  url: "/user",

  // `method` 是创建请求时使用的方法
  method: "get", // 默认是 get

  // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
  // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
  baseURL: "https://some-domain.com/api/",

  // `transformRequest` 允许在向服务器发送前,修改请求数据
  // 只能用在 "PUT", "POST" 和 "PATCH" 这几个请求方法
  // 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
  transformRequest: [function (data) {
    // 对 data 进行任意转换处理

    return data;
  }],

  // `transformResponse` 在传递给 then/catch 前,允许修改响应数据
  transformResponse: [function (data) {
    // 对 data 进行任意转换处理

    return data;
  }],

  // `headers` 是即将被发送的自定义请求头
  headers: {"X-Requested-With": "XMLHttpRequest"},

  // `params` 是即将与请求一起发送的 URL 参数
  // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
  params: {
    ID: 12345
  },

  // `paramsSerializer` 是一个负责 `params` 序列化的函数
  // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function(params) {
    return Qs.stringify(params, {arrayFormat: "brackets"})
  },

  // `data` 是作为请求主体被发送的数据
  // 只适用于这些请求方法 "PUT", "POST", 和 "PATCH"
  // 在没有设置 `transformRequest` 时,必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属:FormData, File, Blob
  // - Node 专属: Stream
  data: {
    firstName: "Fred"
  },

  // `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
  // 如果请求花费了超过 `timeout` 的时间,请求将被中断
  timeout: 1000,

  // `withCredentials` 表示跨域请求时是否需要使用凭证
  withCredentials: false, // 默认的

  // `adapter` 允许自定义处理请求,以使测试更轻松
  // 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
  adapter: function (config) {
    /* ... */
  },

  // `auth` 表示应该使用 HTTP 基础验证,并提供凭据
  // 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
  auth: {
    username: "janedoe",
    password: "s00pers3cret"
  },

  // `responseType` 表示服务器响应的数据类型,可以是 "arraybuffer", "blob", "document", "json", "text", "stream"
  responseType: "json", // 默认的

  // `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
  xsrfCookieName: "XSRF-TOKEN", // default

  // `xsrfHeaderName` 是承载 xsrf token 的值的 HTTP 头的名称
  xsrfHeaderName: "X-XSRF-TOKEN", // 默认的

  // `onUploadProgress` 允许为上传处理进度事件
  onUploadProgress: function (progressEvent) {
    // 对原生进度事件的处理
  },

  // `onDownloadProgress` 允许为下载处理进度事件
  onDownloadProgress: function (progressEvent) {
    // 对原生进度事件的处理
  },

  // `maxContentLength` 定义允许的响应内容的最大尺寸
  maxContentLength: 2000,

  // `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject  promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
  validateStatus: function (status) {
    return status &gt;= 200 &amp;&amp; status &lt; 300; // 默认的
  },

  // `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
  // 如果设置为0,将不会 follow 任何重定向
  maxRedirects: 5, // 默认的

  // `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
  // `keepAlive` 默认没有启用
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // "proxy" 定义代理服务器的主机名称和端口
  // `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
  // 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
  proxy: {
    host: "127.0.0.1",
    port: 9000,
    auth: : {
      username: "mikeymike",
      password: "rapunz3l"
    }
  },

  // `cancelToken` 指定用于取消请求的 cancel token
  // (查看后面的 Cancellation 这节了解更多)
  cancelToken: new CancelToken(function (cancel) {
  })
}

5、响应的结构

{
  // `data` 由服务器提供的响应
  data: {},

  // `status`  HTTP 状态码
  status: 200,

  // `statusText` 来自服务器响应的 HTTP 状态信息
  statusText: "OK",

  // `headers` 服务器响应的头
  headers: {},

  // `config` 是为请求提供的配置信息
  config: {}
}

6、拦截器

axios拦截器工作流程

  • axios 发起请求
  • 执行 请求拦截器 : 添加ajax发送请求之前的操作
  • 服务器 接收、处理、响应 请求
  • 执行 响应拦截器 : 添加服务器响应之后的操作
  • axios 接收响应(执行then方法)

拦截器的经典应用场景

  • 免登录 =>相当于token储存值

  • loading的应用

axios提供了拦截器,用于我们在发送每次请求或者得到相应后,进行对应的处理。

在这里插入图片描述

在请求或响应被 then 或 catch 处理前拦截它们。

// 添加请求拦截器
axios.interceptors.request.use(config => {
    // 在发送请求之前做些什么
    return config;
  }, error => {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(response => {
    // 对响应数据做点什么
    return response;
  },error => {
    // 对响应错误做点什么
    return Promise.reject(error);
  });

移除拦截器

var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

自定义 axios 实例添加拦截器

var instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});

请求拦截可以做到的事情:

在这里插入图片描述

请求拦截中错误拦截较少,通常都是配置相关的拦截。可能的错误比如请求超时,可以将页面跳转到一个错误页面中。

响应拦截中完成的事情:

  • 响应的成功拦截中,主要是对数据进行过滤。

在这里插入图片描述

在这里插入图片描述

  • 响应的失败拦截中,可以根据status判断报错的错误码,跳转到不同的错误提示页面。

在这里插入图片描述

// request.js
 
export function request(config) {
  // 1.创建axios的实例
  const instance = axios.create({
    baseURL: 'http://123.207.32.32:8000',
    timeout: 5000
  })
 
  // 2.axios的拦截器
  // 2.1.请求拦截的作用 成功/失败
  instance.interceptors.request.use(config => {
    // console.log(config);
    // 1.比如config中的一些信息不符合服务器的要求
 
    // 2.比如每次发送网络请求时, 都希望在界面中显示一个请求的图标
 
    // 3.某些网络请求(比如登录(token)), 必须携带一些特殊的信息
    // 得把config再返回
    return config
  }, err => {
    // console.log(err);
  })
 
  // 2.2.响应拦截 成功/失败
  instance.interceptors.response.use(res => {
    // console.log(res);
    return res.data
  }, err => {
    console.log(err);
  })
 
  // 3.发送真正的网络请求
  return instance(config) // 本身的返回值就是个promise
}
// main.js
 
//  request封装v3.0 / v4.0的调用
request({
  url: '/home/multidata'
}).then(res => {
  console.log(res);
}).catch(err => {
  // console.log(err);
})

7、文件的上传

  • 文件上传的input框
accept 属性表示可选择的文件类型
image表示只允许选择图片类型的文件
<input type="file" id="iptFile" accept="image/*" multiple />

重要点 :在文件上传时我们需要new一个FormData对象 , 以此来保存文件

 const app = new FormData();

在创建FormData对象后可以使用FormData.append来添加键/值对到表单里面;

 // 向对象中添加属性=>active属性名:this.files[0]属性值
  app.append("active", this.files[0]);

8、模块封装

  • 在src中新建config文件夹,新建request.js文件
  • 创建对应的axios实例
  • 发送真正的网络请求

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用时

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

二十、git代码上传

1、创建仓库

img

img

2、上传代码文件

img
img-blog.csdnimg.cn/4af96b5167d94de6bc622a5d07c0870d.jpeg#pic_center)

  • 响应的失败拦截中,可以根据status判断报错的错误码,跳转到不同的错误提示页面。

在这里插入图片描述

// request.js
 
export function request(config) {
  // 1.创建axios的实例
  const instance = axios.create({
    baseURL: 'http://123.207.32.32:8000',
    timeout: 5000
  })
 
  // 2.axios的拦截器
  // 2.1.请求拦截的作用 成功/失败
  instance.interceptors.request.use(config => {
    // console.log(config);
    // 1.比如config中的一些信息不符合服务器的要求
 
    // 2.比如每次发送网络请求时, 都希望在界面中显示一个请求的图标
 
    // 3.某些网络请求(比如登录(token)), 必须携带一些特殊的信息
    // 得把config再返回
    return config
  }, err => {
    // console.log(err);
  })
 
  // 2.2.响应拦截 成功/失败
  instance.interceptors.response.use(res => {
    // console.log(res);
    return res.data
  }, err => {
    console.log(err);
  })
 
  // 3.发送真正的网络请求
  return instance(config) // 本身的返回值就是个promise
}
// main.js
 
//  request封装v3.0 / v4.0的调用
request({
  url: '/home/multidata'
}).then(res => {
  console.log(res);
}).catch(err => {
  // console.log(err);
})

7、文件的上传

  • 文件上传的input框
accept 属性表示可选择的文件类型
image表示只允许选择图片类型的文件
<input type="file" id="iptFile" accept="image/*" multiple />

重要点 :在文件上传时我们需要new一个FormData对象 , 以此来保存文件

 const app = new FormData();

在创建FormData对象后可以使用FormData.append来添加键/值对到表单里面;

 // 向对象中添加属性=>active属性名:this.files[0]属性值
  app.append("active", this.files[0]);

8、模块封装

  • 在src中新建config文件夹,新建request.js文件
  • 创建对应的axios实例
  • 发送真正的网络请求

[外链图片转存中…(img-0M3owrB6-1726043945141)]

[外链图片转存中…(img-7mErpOEM-1726043945141)]

使用时

[外链图片转存中…(img-H6z4xJCq-1726043945141)]

二十、git代码上传

1、创建仓库

img

img

2、上传代码文件

img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值