Vue2学习笔记

1. 初识Vue

1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2.root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
3.root容器里的代码被称为【Vue模板】;
4.容器与实例一一对应
5.root容器通过{{XXX}}读取data属性,且里面为JS表达式。(区分JS表达式和JS语句(代码))

示例:

    Vue.config.productionTip = false;//阻止 vue 在启动时生成生产提示
    //创建Vue实例对象,
    new Vue({
        el: '#root',    //el(element)指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
        data: {         //data中用于存储数据,数据供el所指定的容器去使用,
            name: '搞',
        }
    })

2. 模板语法

2.1 插值语法

  • 功能:用于解析标签体的内容;
  • 写法:{{XXX}}读取data属性,且里面为JS表达式;

2.2 指令语法

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

备注: Vue中有很多的指令,且形式都是v-xxx,此处只是拿v-bind举例

3. 数据绑定

Vue中有2种数据绑定的方式:

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

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

4. el与data的两种写法

  1. el有2种写法
    (1).new Vue时候配置el属性。
    (2).先创建Vue实例,随后再通过vm.$mount('#root')指定el的值。
    (容器里面的模板交给Vue实例进行解析,解析之后将内容挂载(mount)到页面指定位置上)
  2. data有2种写法
    (1).对象式–data{}
    (2).函数式–data() { return { } }
    在组件时,data必须使用函数式,否则会报错
  3. 一个重要的原则:
    由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了(而是全局对象window)。
    箭头函数没有自己的this,要往上一级找
    const vm = new Vue({
        // el: '#root',//el第一种写法
        data: {name: '搞成',}//data第一种写法
        data: function () {//data第二种写法,简写为 data(){ return{ }}
            return {
                name: '搞成',
            }
        }
    })
    console.log(vm);
    vm.$mount('#root')//el第二种写法

5. MVVM模型

  1. M:模型(Model):对应data中的数据
  2. V:视图(View):模板
  3. VM:视图模型(ViewModel):Vue实例对象

Vue2学习笔记_第1张图片

  1. data中所有的属性,最后都出现在了vm身上。
  2. vm身上所有的属性及 Vue原型上所有属性,在Vue模板中都可以直接使用。

6. 数据代理

6.1 数据代理

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

  • Object.defineProperty(obj, prop, descriptor)

    obj:要定义属性的对象。
    prop:要定义或修改的属性的名称
    descriptor:要定义或修改的属性描述符

    let number = 18;
    let person = {
        name: 'GC',
        sex: '男',
    }
    Object.defineProperty(person, 'age', {
        // value: 20,
        // enumerable: true,//控制属性是否可以枚举(遍历),默认值时false
        // writable: true,//控制属性是否可以被修改,默认值时false
        // configurable: true,//控制属性是否可以被删除,默认值时false

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

    })
    console.log(Object.keys(person));//遍历person对象中的所有属性名
    for (const key in person) {
        console.log(person[key]);
    }

6.2 最简单的数据代理

    let vm = {};
    let data = {
        name: 'ds',
        age: 18,
    };
    Object.defineProperty(vm, 'age', {
        get() {
            return data.age;
        },
        set(value) {
            data.age = value;
        },
    });
  1. Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
  2. Vue中数据代理的好处:更加方便的操作data中的数据
  3. 基本原理:
    (1)通过object.defineProperty()把data对象中所有属性添加到vm上;
    (2)为每一个添加到vm上的属性,都指定一个getter/setter;
    (3)在getter/setter内部去操作(读/写)data中对应的属性。
    let data = {
        name:'搞成'
    }
    const vm = new Vue{
        el:'#root',
        data
    }

    vm._data = options.data(配置对象的data) = data(传递的data)

7. 事件处理

7.1 事件的基本使用

  1. 使用v-on :xxx或@xxx绑定事件,其中xxx是事件名;绑定事件的时候:@xxx=“yyy” yyy可以写一些简单的语句;
  2. 事件的回调需要配置在methods对象中,最终会在vm上;
  3. methods中配置的函数,不要用箭头函数!否则this就不是vm了;
  4. methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或组件实例对象;
    5. @click="demo”@click="demo($event)”效果一致,但后者可以传参;
    <div id="root">
        <h1>我叫{{name}}h1>
        <button v-on:click="showInfo1">点击1(不传参)button>
        <button @click="showInfo2($event,666)">点击2(传参)button>
    div>
    const vm = new Vue({
        el: '#root',
        data: {
            name: '搞成',
        },
        methods: {
            showInfo1() {
                alert('信息1')
            },
            showInfo2(event, number) {
                console.log(event.target.innerText);
                console.log(this);//此处的this是vm(Vue)
                console.log(event, number);
            }
        }
    })

7.2 事件修饰符

  1. prevent: 阻止默认事件(常用);
  2. stop:阻止事件冒泡(常用);
  3. once:事件只触发一次(常用);
  4. capture:使用事件的捕获模式;
  5. self:只有event.target是当前操作的元素时才触发事件;
  6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
<div id="root">
    
    <a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息a>

    
    <div class="demo1" @click="showInfo">
        <button @click.stop="showInfo">点我提示信息button>
        
        
    div>

    
    <button @click.once="showInfo">点我提示信息button>

    
    <div class="box1" @click.capture="showMsg(1)">捕获到的时候就直接触发
        div1
        <div class="box2" @click="showMsg(2)">
            div2
        div>
    div>

    
    <div class="demo1" @click.self="showInfo">
        <button @click="showInfo">点我提示信息button>
    div>

    
    
    
    <ul @wheel.passive="demo" class="list">
        <li>1li>
        <li>2li>
        <li>3li>
        <li>4li>
    ul>
div>
    new Vue({
        el: '#root',
        data: {
            name: '搞成'
        },
        methods: {
            showInfo(e) {
                alert('同学你好!')
                // console.log(e.target)
            },
            showMsg(msg) {
                console.log(msg)
            },
            demo() {
                for (let i = 0; i < 100000; i++) {
                    console.log('#')
                }
                console.log('累坏了')
            }
        }
    })

7.3 键盘事件

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

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

  1. Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名,如大写键:caps-lock)
  2. 系统修饰键(用法特殊):ctrl、alt、shift、meta
    (1). 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
    (2). 配合keydown使用:正常触发事件。
  3. 也可以使用keyCode去指定具体的按键(不推荐)
  4. Vue.config.keyCodes.自定义键名=键码,可以去定制按键别名

8. 计算属性

8.1 插值法与methods实现

    <div id="root">
        姓:<input type="text" v-model="firstName"><br>
        名:<input type="text" v-model="lastName"><br>
        
        {{firstName.slice(0,3)}}-{{lastName}}<br>
        
        {{fullname()}}<br>
        
        {{flname}}
    div>
    new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三',
        },
        methods: {  methods实现
            fullname() {
                return this.firstName + '-' + this.lastName;
            }
        },
        computed: { 计算属性实现
            flname: {
                // 当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
                // get调用时间:1. 初次读取fullName时。2. 所依赖的数据发生变化时。
                get() {
                    console.log('get被调用了');
                    return this.firstName + '-' + this.lastName;
                },
                // set在flname被修改是调用。 可以主动在控制台修改fullName来查看情况
                set(value) {
                    console.log('set', value);
                    const arr = value.split('-');
                    this.firstName = arr[0]
                    this.lastName = arr[1]
                }
            }
            /* flname() {   //简写成函数的形式,只能被读取,即会自动调用get方法
                console.log('get被调用了');
                return this.firstName + '-' + this.lastName;
            } */
        }
    })

8.2 计算属性

  1. 定义:要用的属性不存在,要通过已有属性计算得来;
  2. 原理:底层借助了objcet.defineproperty方法提供的getter和setter;
  3. get函数什么时候执行?
    (1).初次读取时会执行一次;
    (2).当依赖的数据发生改变时会被再次调用。
  4. 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便;
    (缓存:methods被模板读取几次就被调用几次,而计算属性被模板读取多次调用一次就行。)

备注:
(1).计算属性最终会出现在vm上,直接读取使用即可。
(2).如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。

9. 监视属性

9.1 监视属性watch

  1. 当被监视的属性变化时,回调函数自动调用,进行相关操作
  2. 监视的属性必须存在,才能进行监视!!
  3. 监视的两种写法:
    (1).new Vue时传入watch配置
    (2).通过vm.$watch监视
    <div id="root1">今天天气{{info}}
        <button v-on:click="changeWeather()">切换button>
    div>
    const vm = new Vue({
        el: '#root1',
        data: {
            isHot: true,
            numbers: {
            a: 1,
            b: 1,
            c: {
                d: {
                    e: 100  //Vue自身可以监测对象内部值的改变
                }
            }
      }
        },
        computed: {
            info() {
                return this.isHot ? '炎热' : '凉爽'
            }
        },
        methods: {
            changeWeather() {
                this.isHot = !this.isHot
            }
        },
        watch: {    //wacth第一种写法,创建Vue实例时就明白监视谁
            immediate: true,    //初始化就可以调用一次handler
            isHot: {    //info 同样可以被监视
                // 当isHto发生改变时调用handler
                handler(newValue, oldValue) {
                    console.log('isHot被修改了', newValue, oldValue);
                }
            }
        }
    })
    // watch属性的第二种写法,根据用户的行为监视后再写这个API
    vm.$watch('isHot', {
        immediate: true,
        handler(newValue, oldValue) {
            console.log('isHot被修改了', newValue, oldValue);
        }
    })

9.2 深度监视

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

备注:
(1).Vue自身可以监测对象内部值的改变(不管属性里面多少层的值被改变),但Vue提供的watch默认不可以!
(2).使用watch时根据数据的具体结构,决定是否采用深度监视。

*监视多级结构中某个属性的变化,将属性名写完整–'number.a'*
'numbers.a':{handler(){console.log('a被改变了')}}

<div id="root">
        <h3>a的值是:{{numbers.a}}h3>
        <button @click="numbers.a++">点我让a+1button>
        <h3>b的值是:{{numbers.b}}h3>
        <button @click="numbers.b++">点我让b+1button>
        <button @click="numbers = {a:666,b:888}">彻底替换掉numbersbutton>
        
    div>
    Vue.config.productionTip = false
    const vm = new Vue({
        el: '#root',
        data: {
            isHot: true,
            numbers: {
                a: 1,
                b: 1,
                c: {
                    d: {
                        e: 100
                    }
                }
            }
        },
        watch: {
            // 监视多级结构中某个属性的变化
            /* 'numbers.a':{
                        handler(){
                            console.log('a被改变了')
                        }
                    } */
            // Vue可以监视多级结构中所有属性的变化,但提供的watch默认不行,要加入   deep:true
            numbers: {
                deep: true,     //深度监视, 监视多层
                handler() {
                    console.log('numbers改变了')
                }
            }
        }
    })
  • 如果监视属性除了handler没有其他配置项的话,可以进行简写
watch: {
      //简写
      isHot(newValue, oldValue) {
        console.log('isHot被修改了', newValue, oldValue)
      }
    }

  //简写
  // vm.$watch('isHot', funtion(newValue, oldValue){
  // 	console.log('isHot被修改了', newValue, oldValue, this)
  // })

9.3 computed和watch之间的区别

  1. computed能完成的功能,watch都可以完成。
  2. watch能完成的功能,computed不一定能完成,例如: watch可以进行异步操作(如下面的延时函数)。
    两个重要的小原则:
    (1)所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
    (2)所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数、Promise的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象。
    new Vue({
    el:'#root',
    data:{
        firstName:'张',
        lastName:'三',
        fullName:'张-三'
    },
    watch:{
        firstName(val){
            setTimeout(()=>{    //必须使用箭头函数,不然this指向的就不是Vue了
                this.fullName = val + '-' + this.lastName
            },1000);
        },
        lastName(val){
            this.fullName = this.firstName + '-' + val
        }
    }
    })

10. 绑定样式

10.1 绑定class样式

字符串写法
绑定class样式–字符串写法,适用于:样式的类名不确定,需要动态指定

	// .normal{background-color: skyblue;}
    // 
{{name}}
const vm = new Vue({ el:'#root', data:{ name:'搞成', mood:'normal' }, methods:{ changeMood(){ cosnt arr = ['class1','class2','class3']; this.mood = arr[Math.floor(Math.random()*3)] //点击随机切换class样式 } } })

数组写法
绑定class样式–数组写法,适用于:要绑定的样式个数不确定、名字也不确定

// 
{{name}}
const vm = new Vue({ el:'#root', data:{ classArr: ['class1','class2','class3'] } })

对象写法
绑定class样式–对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用

    // 
{{name}}
const vm = new Vue({ el:'#root', data:{ classObj:{ class1:false, class2:false, } } })

10.2 绑定style样式

字符串写法
绑定style样式-- :style = "fontSize:xxx"其中xxx是动态值

    // 
{{name}}
const vm = new Vue({ el:'#root', data:{ styleObj:{ fontSize: '40px', color:'red', } } })

数组写法
绑定style样式-- :style="[a,b]"其中a、b是样式对象

    // 
{{name}}
const vm = new Vue({ el:'#root', data:{ styleArr:[ { fontSize: '40px', color:'blue', }, { backgroundColor:'gray' } ] } })

11.条件渲染

  1. v-if
    (1).v-if="表达式"
    (2).v-else-if="表达式"
    (3).v-else="表达式",适用于:切换频率较低的场景。
    特点:不展示的DOM元素直接被移除。
    注意:v-if可以和v-else-ifv-else一起使用,但要求结构不能被“打断”。
  2. v-show
    写法: v-show="表达式"
    适用于:切换频率较高的场景。
    特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
  3. 备注:使用v-if的时,元素可能无法获取到(v-if="flase"时,从结构上就没了),
    而使用v-show一定可以获取到(v-show="flase"只是被隐藏了)。
  • template只能配合v-if不能配合v-show
    <div id="root">
        我叫{{name}}<br>
        <button @click="n++">点击n+1button>{{n}}
        
        <h2 v-show="!a">我是谁h2>
        <h2 v-show="a">我恁爹h2> <br>
        
        <h2 v-if="!a">我是谁h2>
        <h2 v-if="1 === 1">我恁爹h2> <br>
        
        <div v-if="n === 1">div>
        <div v-else-if="n === 2">嘿嘿div>
        <div v-else="n === 3">好好好div>
        
        <template v-if="n === 1">
            <h2>你好h2>
            <h2>他好h2>
            <h2>我好h2>
        template>
    div>
    new Vue({
        el: '#root',
        data: {
            name: '搞成',
            n: 0,
            a: true
        }
    })

12. 列表渲染

12.1 v-for指令

  1. 用于展示列表数据
  2. 语法: v-for="(item,index) in xxx" :key="yyy"
  3. 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
<div id="root">
    
    <ul>
        <li v-for="(p,index) in persons" :key="index"> {{p.name}}-{{p.age}} li>
    ul>
    
    <ul>
        <li v-for="(value,k) in car" :key="k"> {{k}}-{{value}} li>
    ul>
    
    <ul>
        <li v-for="(char,index) in str" :key="index"> {{char}}-{{index}} li>
    ul>
    
    <ul>
        <li v-for="(numbers,index) in 5" :key="index"> {{numbers}}-{{index}} li>
    ul>
div>
    new Vue({
        el: '#root',
        data: {
            name: '搞成',
            persons: [
                { id: "0", name: "张三", age: "18" },
                { id: "1", name: "李四", age: "14" },
                { id: "2", name: "王五", age: "17" },
            ],
            car: {
                name: '奥迪', price: '70万', color: '黑色'
            },
            str: 'hello',
        }
    })

12.2 key的作用于原理

面试题:react、vue中的key有什么作用?(key的内部原理)

  1. 虚拟DOM中key的作用:
    key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
  2. 对比规则:
    (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
    ①. 若虚拟DOM中内容没变,直接使用之前的真实DOM!
    ②. 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
    (2).旧虚拟DOM中未找到与新虚拟DOM相同的key
    创建新的真实DOM,随后渲染到到页面。
  3. 用index作为key可能会引发的问题:
    (1).若对数据进行:逆序添加、逆序删除等破坏顺序操作:
    会产生没有必要的真实DOM更新==>界面效果没问题,但效率低。
    (2).如果结构中还包含输入类的DOM:
    会产生错误DOM更新==>界面有问题。
  4. 开发中如何选择key? :
    (1).最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值;
    (2).如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

Vue2学习笔记_第2张图片
Vue2学习笔记_第3张图片

12.3 列表过滤

    // 
    // 
  • {{p.name}}-{{p.age}}-{{p.sex}}
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: '男' } ], filPersons: [] //filter过滤后不影响原数组,所以将过滤后的数组写到新的空数组中 }, // 通过侦听属性进行列表过滤 watch: { keyWord: { immediate: true,//indexOf对于空串的结果是0,这样上面代码初始过滤就是全部数据 handler(val) { this.filPersons = this.persons.filter((p) => { return p.name.indexOf(val) !== -1 }) } } }, // 第二种用计算属性进行列表过滤,不需要创建filPersons:[] computed: { //计算属性初次读取时会执行一次,依赖的数据发生改变时会被再次调用 filPersons() { return this.persons.filter((p) => { return p.name.indexOf(this.keyWord) !== -1 }) } } })

12.4. 列表排序

    /* 
    
    
    
    
  • {{p.name}}-{{p.age}}-{{p.sex}}
*/
new Vue({ el: '#root', data: { keyWord: '', sortType: 0,//0为原顺序,1为降序,2为升序 persons: [ { id: '001', name: '马冬梅', age: 30, sex: '女' }, { id: '002', name: '周冬雨', age: 20, sex: '女' }, { id: '003', name: '周杰伦', age: 25, sex: '男' }, { id: '004', name: '温兆伦', age: 18, sex: '男' } ], }, computed: { filPersons() { const arr = this.persons.filter((p) => { return p.name.indexOf(this.keyWord) !== -1 }) console.log(arr); if (this.sortType) { arr.sort((a, b) => { return this.sortType === 1 ? b.age - a.age : a.age - b.age }) } return arr } } })

13. Vue监视数据

13.1 Vue监视数据的原理_对象

    let data = {
        name: 'GC',
        address: 'gg'
    }
    const obs = new Observer(data)
    console.log(obs);
    let vm = {};
    vm._data = data = obs;
    function Observer(obj) {
        const key = Object.keys(obj);
        key.forEach((k) => {
            Object.defineProperty(this, k, {
                get() {
                    return obj[k];
                },
                set(value) {
                    obj[k] = value;
                },
            });
        })
    }

13.2 Vue.set

Vue.set(target, propertyName/index, value)vm.$set(target, propertyName/index, value)

{Object | Array} target 目标
{string | number} propertyName/index 属性名
{any} value 值

向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = ‘hi’)

  • 注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。

只能给data里面的某一个对象添加属性,而不能给data追加属性

13.3 Vue监视数据的原理_数组

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括

push()
pop()
shift()
unshift()
splice()
sort()
reverse()

你可以打开控制台,然后对前面例子的 items 数组尝试调用变更方法。比如 example1.items.push({message: 'Baz'})

13.4 Vue监视数据总结

Vue监视数据的原理:

  1. vue会监视data中所有层次的数据。
  2. 如何监测对象中的数据?
    通过setter实现监视,且要在new Vue时就传入要监测的数据。
    (1).对象中后追加的属性,Vue默认不做响应式处理
    (2).如需给后添加的属性做响应式,请使用如下API:
    Vue.set(target. propertyName/index, value)
    vm.$set(target.propertyName/index,value)
  3. 如何监测数组中的数据?
    通过包裹数组更新元素的方法实现,本质就是做了两件事:
    (1).调用原生对应的方法对数组进行更新。
    (2).重新解析模板,进而更新页面。
  4. 在Vue修改数组中的某个元素一定要用如下方法:
    (1). 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
    (2). Vue.set()或vm.$set()
  • 特别注意:Vue.set()和 vm.$set()不能给vm或 vm的根数据(vm._data)对象添加属性!!!

14. 收集表单数据

收集表单数据:
若:,则v-model收集的是value值,用户输入的就是value值。
若:,则v-model收集的是value值,且要给标签配置value值。
若:
1.没有配置input的value属性,那么收集的就是checked(勾选or未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选or未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组

备注:v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤

<div id="root">
    <form @click.prevent="demo">
        <label for="demo1">账号:label>
        <input type="text" id="demo1" v-model.trim="userInfo.account"> <br /><br />
        
        密码:<input type="password" v-model="userInfo.password"> <br /><br />
        年龄:<input type="number" v-model.number="userInfo.age"> <br /><br />
        
        性别:
        男<input type="radio" name="sex" v-model="userInfo.sex" value="male"><input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br /><br />
        
        爱好:
        学习<input type="checkbox" v-model="userInfo.hobby" value="study">
        打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
        吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat"><br /><br />
        
        所属校区
        <select v-model="userInfo.city">
            <option value="">请选择校区option>
            <option value="beijing">北京option>
            <option value="shanghai">上海option>
            <option value="shenzhen">深圳option>
            <option value="wuhan">武汉option>
        select><br /><br />
        其他信息:<textarea v-model.lazy="userInfo.message">textarea><br /><br />
        <input type="checkbox" v-model="userInfo.agree">阅读并接受
        <a href="#">《用户协议》a><br /><br />
        <button>提交button>
    form>
div>
    Vue.config.productionTip = false
    new Vue({
        el: '#root',
        data: {
            userInfo: {
                account: '',
                password: '',
                sex: 'male',
                hobby: [],
                city: '',
                message: '',
                agree: '',
                age: '',
            }
        },
        methods: {
            demo() {
                console.log(JSON.stringify(this.userInfo));
            }
        },
    })

15. 过滤器

定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
语法:
1. 注册过滤器:Vue.filter(name,callback)new Vue{filters:{}}
2. 使用过滤器:{{ xxx│过滤器名}} 或 v-bind:属性=“xxx│过滤器名”,

备注:
1.过滤器也可以接收额外参数、多个过滤器也可以串联,
2.并没有改变原本的数据,是产生新的对应的数据。

<div id="root">
        <h3>计算属性实现:{{fmtime}}h3>
        <h3>methods属性实现:{{getFmtime()}}h3>
        <h3>过滤器实现:{{time | timeFormat}}h3>
        <h3>过滤器实现(传参):{{time | timeFormat('YYYY_MM_DD') | mySlice}}h3>
        
div>
<div id="root2">{{name | mySlice}}div>
    //全局过滤器,必须在Vue实例之前
    Vue.filter('mySlice', function (value) {
        return value.slice(0, 4)
    });
    new Vue({
        el: '#root',
        data: {
            time: 1658995670583,
        },
        computed: {
            fmtime() {
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
            }
        },
        methods: {
            getFmtime() {
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
            }
        },
        filters: {      //局部过滤器
            timeFormat(value, str = 'YYYY-MM-DD HH:mm:ss') {//过滤器传参,str有值则传值,没值则传默认的
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')//this.time为实时时间
                return dayjs(value).format(str)//value为数据对应的时间
            },
            mySlice(value) {
                return value.slice(0, 4)
            }
        }
    })
    new Vue({
        el: '#root2',
        data: {
            name: '搞成aaaaa',
        }
    })

16. 内置指令

16.1 v-text指令

  1. 作用:向其所在的节点中渲染文本内容。
  2. 与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
    <div v-text="name">div>
    <div v-html="str2">div>
    new Vue({
        el: '#root',
        data: {
            name: '张三',
            str: '

你好啊!

'
, str2: '快来!', } })

16.2 v-html指令

  1. 作用:向指定节点中渲染包含html结构的内容。
  2. 与插值语法的区别:
    (1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
    (2).v-htm1可以识别htm1结构。
  3. 严重注意:v-html有安全性问题!!!
    (1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
    (2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!

16.3 v-cloak

该指令(没有值):

  1. 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
  2. 使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题(可以让这种未经解析的模板别跑到页面上去)。
/* 选择=所有的v-cloak属性 */
    [v-cloak]{      
        display:none;
    }
```html

{{name}}

/* 延时五秒跳转效果 */
    console.log(1)
    Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
    new Vue({
        el:'#root',
        data:{
            name:'搞'
        }
    })

16.4 v-once指令

  1. v-once所在节点在初次动态渲染后,就视为静态内容了。
  2. 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。

16.5 v-pre指令

  1. 跳过其所在节点的编译过程。
  2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

17. 自定义指令

指令的this指向window,因为不需要vm相关的资源,需要的数据已经被传进去了(如"n")

  • 定义全局指令与过滤器相同,Vue.directive('fbind',{})

17.1 定义v-big指令

定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。

  • big函数何时会被调用?1.指令与元素成功绑定时(一上来);2.指令所在的模板被重新解析时。
    <div id="root">
        <h2>当前n值:<span v-text="n">span>h2>
        <h2>放大之后的n值:<span v-big="n">span>h2>
        <h2>放大之后的n值:<span v-big-number="n">span>h2>
        <button @click="n++">点击button>
    div>
    new Vue({
        el: '#root',
        data: {
            name: '搞成',
            n: 1
        },
        directives: {
            big(element, binding) {
                element.innerText = binding.value * 10
            },
            'big-number'(element, binding) {
                element.innerText = binding.value * 100
            }
        }
    })

17.2 定义v-fbind指令

定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
fbind:{
(1)bind(){}:指令与元素成功绑定时(一上来);
(2)inserted(){}:指令所在元素被插入页面时;
(3)update(){}:指令所在的模板被重新解析时。}

    <div id="root">
        <h2>当前n值:<span v-text="n">span>h2>
        <h2>放大之后的n值:<span v-big="n">span>h2>
        <button @click="n++">点击button>
        <input type="text" v-fbind="n">
    div>
    new Vue({
        el: '#root',
        data: {
            name: '搞成',
            n: 1
        },
        directives: {
            // big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
            big(element, binding) {
                element.innerText = binding.value * 10
            },
            fbind: {       //写成对象的形式,分为三步函数来实现
                //指令与元素成功绑定时(一上来)
                bind(element, binding) {
                    element.value = binding.value
                },
                // 指令所在元素被插入页面时
                inserted(element, binding) {
                    element.focus()
                },
                // 指令所在的模板被重新解析时
                update(element, binding) {
                    element.focus()
                    element.value = binding.value
                }
            }
        }
    })

17.3 自定义指令总结

一、定义语法:
(1).局部指令:
new Vue({ new Vue({
directives:{指令名:配置对象} 或 directives{指令名:回调函数}
}) })
(2).全局指令:
Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
二、配置对象中常用的3个回调:
(1).bind:指令与元素成功绑定时调用。
(1).inserted:指令所在元素被插入页面时调用。
(3).update:指令所在模板结构被重新解析时调用。
三、备注:
(1).指令定义时不加v-,但使用时要加v-;
(1).指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。

18. 生命周期

18.1 生命周期引导

  1. 又名:生命周期回调函数、生命周期函数、生命周期钩子;
  2. 是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数;
  3. 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的;
  4. 生命周期函数中的this指向是或组件实例对象。
    <div id="root">
        
        <h2 :style="{opacity}">搞成啊h2>
    div>
    Vue.config.productionTip = false
    const vm = new Vue({
        el: '#root',
        data: {
            name: '搞成',
            opacity: 1
        },
        mounted() {
            // Vue 完成模板的解析并把初始的真实 DOM 元素放入页面后(挂载完毕)调用 mounted
            setInterval(() => {
                this.opacity -= 0.01
                if (this.opacity <= 0)
                    this.opacity = 1
            }, 16);
        },
    })

18.2 分析生命周期

Vue2学习笔记_第4张图片

outerHTML指的是root整个

都是模板

<div id="root" :x="n">
    <h2>当前值:{{n}}h2>
    <button @click="n++">点击button>
    <button @click="bye">销毁button>
div>
    const vm = new Vue({
        el: '#root',
        //root容器没有内容时,可用template模板,得有一个根元素(加入div),可直接替换root容器,组件用
        /* template: `    
        

当前值:{{n}}

`, */
data: { name: '搞成', n: 1 }, methods: { bye() { vm.$destroy(); } }, beforeCreate() { //无法访问data中的数据、methods中的方法 console.log("beforeCreate"); console.log(this); // debugger }, created() { //可以访问data中的数据、methods中的方法 console.log("created"); console.log(this); // debugger }, beforeMount() { //页面呈现未经Vue编译的DOM结构 console.log("beforeMount"); // debugger }, mounted() { //页面呈现经Vue编译的DOM结构 console.log("beforeMount", this.$el);//Vue自己存一份真实DOM // debugger }, beforeUpdate() { //数据新的,页面旧的 console.log("beforeUpdate"); console.log(this.n); // debugger }, updated() { //数据新的,页面新的 console.log("updated"); console.log(this.n); // debugger }, beforeDestroy() { //vm中的data、methods、指令处于可用但马上要销毁 console.log("beforeDestroyed"); // debugger }, destroyed() { console.log("destroyed"); // debugger }, })

vm.$destroy()
用法:完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器(自定义事件)。
触发beforeDestroy和destroyed的钩子。在大多数场景中你不应该调用这个方法。最好使用v-if和v-for 指令以数据驱动的方式控制子组件的生命周期。

常用的生命周期钩子:

  1. mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
  2. beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。

关于销毁Vue实例:

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

19. 组件

19.1 非单文件组件

  • 一个文件包含n个组件
    Vue中使用组件的三大步骤:
    1、定义组件(创建组件)
    2、注册组件
    3、使用组件(写组件标签)
    1、如何定义一个组件?
    使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有区别
    区别如下:
    (1). el不要写,为什么?— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器,
    (2). data必须写成函数,为什么?— 避免组件被复用时,数据存在引用关系。

备注:使用template可以配置组件结构。

2、如何注册组件?
(1). 局部注册:靠 new Vue 的时候传入components选项
(2). 全局注册:靠 Vue.component('组件名’,组件)
3、编写组件标签:

    <div id="root">
        
        <hello>hello>
        <school>school>
        <my-student>my-student>
        <hr>
        <my-student>my-student>
    div>
    Vue.config.productionTip = false
    // 第一步:创建school组件
    const school = Vue.extend({
        // el: '#root',     //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。
        template: `
        

学校名称:{{schoolName}}

学校地址:{{address}}

`
, data() { return { schoolName: '浙江', address: '杭州', } }, methods: { sc() { alert(this.schoolName) } }, }) const student = Vue.extend({ template: `

学生名字:{{studentName}}

学生年龄:{{age}}

`
, data() { return { studentName: '搞成', age: 18, } } }) //第一步:创建全局组件 const hello = Vue.extend({ template: `

你好!

`
, }) // 第二步:注册全局组件 Vue.component('hello', hello) new Vue({ el: '#root', data: { schoolName: '浙江', address: '杭州', studentName: '搞成', age: 18, }, // 第二步:注册组件(局部注册) components: { // 左边是组件名,右边是组件在哪 // school:school,简写school school, 'my-student': student, // 多个单词用 'xx-xx' 形式 } })

注意点

  1. 关于组件名:
    一个单词组成:
    第一种写法(首字母小写):school
    第二种写法(首字母大写):School
    多个单词组成:
    第一种写法(kebab-case命名):my-school
    第二种写法(CamelCase命名):MySchool(需要Vue脚手架支持)

备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。(在创建组件的时候)

  1. 关于组件标签:
    第一种写法:
    第二种写法:
    备注:不用使用脚手架时,会导致后续组件不能渲染。
  2. 一个简写方式:
    const school = Vue.extend(options)可简写为:const school = options

组件的嵌套

    const student = Vue.extend({
        template: `
        

你好{{name}}!

`
, data() { return { name: '搞成', } }, }) const school = Vue.extend({ template: `
`
, components: { student } }) const hello = Vue.extend({ template: `

以{{lt}}击碎黑暗!

`
, data() { return { lt: '雷霆' } } }) // 每次调用Vue.extend,返回的都是一个全新的VueComponent。 console.log('@', school); console.log('#', student); const app = Vue.extend({ template: `
`
, components: { school, hello } }) new Vue({ el: '#root', template: ``, components: { app } })

VueComponent

  1. school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
  2. 我们只需要写或,Vue解析时会帮我们创建school组件的实例对象,即vue帮我们执行的:new VueComponent(options)。
    • 特别注意 :每次调用Vue.extend,返回的都是一个全新的VueComponent!!!
  3. 关于this指向:
    (1).组件配置中:
    data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。
    (2).new Vue(options)配置中:
    data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
  4. VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。
    Vue的实例对象,以后简称vm。

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

Vue2学习笔记_第5张图片

    console.log('@', Vue.prototype === school.prototype.__proto__);
    // 定义一个构造函数
    function Demo() {
        this.a = 1
        this.b = 2
    }
    // 创建一个Demo的实例对象
    const d = new Demo()
    console.log(Demo.prototype);    //显示原型属性
    console.log(d.__proto__);       //隐式原型属性
    console.log(Demo.prototype === d.__proto__);
    // 程序员通过显示原型属性操作原型对象,追加一个x属性,值为99
    Demo.prototype.x = 99
    console.log(d.x);
    console.log(d);
  • 为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。

19.2 单文件组件

一个文件只包含1个组件

组件暴露三种方式:

分别暴露:export const school=Vue.extend({})
统一暴露:export {school}
默认暴露:export default school
推荐第三种,简写为 export default{},里面再配置项name:‘School’。

20. 脚手架

20.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: 包版本控制文件

  • 1、单文件组件StudentName.vue和SchoolName.vue直接引入
  • 2、引入App.vue
    

    
  • 3、main.js
    // 该文件是整个项目的入口文件
    // 引入Vue,此处引入vue为残缺
    import Vue from 'vue'
    // 引入App组件,它是所有组件的父组件
    import App from './App.vue'
    // 关闭生产提示
    Vue.config.productionTip = false
    // 创建Vue实例对象---vm
    new Vue({
    // 将App组件放入容器中
    render: h => h(App),

    // 原来的样子,createElement能创建具体的元素
    // render(createElement) {return createElement(App)},

    }).$mount('#app')   //与el:'#app' 一个意思
  • 4、index.html
    DOCTYPE html>
    <html lang="">

    <head>
    <meta charset="utf-8">
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    
    <title>
        <%= htmlWebpackPlugin.options.title %>
    title>
    head>

    <body>
    <noscript>
        <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
            Please enable it to continue.strong>
    noscript>
    <div id="app">div>
    
    body>

    html>

20.2 关于不同版本的Vue

  1. vue.js 与vue.runtime.xxx.js的区别:
    (1).vue.js是完整版的Vue,包含:核心功能+模板解析器。
    (2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
  2. 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。

20.3 vue.config.js配置文件

  • 使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
  • 在package.json同级目录新建vue.config.js,使用vue.config.js可以对脚手架进行个性化定制,详情见: https://cli.vuejs.org/zh
    module.exports = {
    pages: {
        index: {
        entry: 'src/index/main.js' // 选择入口
        }
    },
    lineOnSave: false	// 关闭语法检查
    }

21. Vue知识

21.1 ref 属性

  1. 被用来给元素或子组件注册引用信息(id的替代者)
  2. 应用在html标签上获取的是真实DOM元素,应用在组件杯签上是组件实例对家(VC)
  3. 使用方式:打标识:

    .....

    获取: this.$refs.xxx



21.2 props配置项

功能:让组件接收外部传过来的数据

  1. 传递数据:
    字符串可进行动态绑定,引号里面即为js表达式
  2. 接收数据:
  • 第一种方式(只接收)∶props: [‘name’]
  • 第二种方式(限制类型):
    props:{
    name : String
    }
  • 第三种方式(限制类型、限制必要性、指定默认值):
    props:{
    name:{
    type:String,//类型
    required:true,//必要性
    default:'老王’//默认值
    }
    }

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


  <div>
    <StudentName name="张三" address="杭州" :age="18">StudentName>
  div>




21.3 mixin(混入、混合)

功能:可以把多个组件共用的配置提取成一个混入对象,使用方式:
第一步定义混合,例如:(创建mixin.js文件)
{
data(){…},
methods:{…}

}
第二步使用混入,例如:
(1).全局混入:Vue.mixin(xxx)
(2).局部混入: mixins: [‘xxx’]

// StudentName.vue与SchoolName.vue有相同的部分
import { hunhe, hunhe2 } from "../mixin";

export default {
  name: "StudentName",
  data() {
    return {
      name: "搞成",
      address: "浙江",
      x: 666, //以组件中原有数据为主
    };
  },
  mounted() {
    console.log("组件钩子被调用");
  },
  mixins: [hunhe, hunhe2], //生命周期钩子来者不拒,混合优先输出
};
// mixin.js
export const hunhe = {
  methods: {
    showName() {
      alert(this.name);
    },
  },
  mounted() {
    console.log("混合对象钩子被调用");
  },

}
export const hunhe2 = {
  data() {
    return {
      x: 33,
      y: 77
    }
  }
}

21.4 插件

功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
定义插件:(新建plugins.js)
对象.install = function (Vue,options) {
// 1.添加全局过滤器
Vue.filter(…)
// 2.添加全局指令
Vue.directive(…)
// 3.配置全局混入(合)
vue.mixin(…)
// 4.添加实例方法
Vue.prototype. m y M e t h o d = f u n c t i o n ( ) . . . . V u e . p r o t o t y p e . myMethod = function () {....} Vue.prototype. myMethod=function()....Vue.prototype.myProperty = xxxx
}
使用插件:Vue.use()

// 在main.js中引入插件
import plugin from './plugins'
// 应用(使用)插件
Vue.use(plugin)

21.5 scoped样式

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

22. TodoList案例

22.1 总结TodoList案例

  1. 组件化编码流程:
    (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
    (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
    1).一个组件在用:放在组件自身即可。
    2).一些组件在用:放在他们共同的父组件上(状态提升)。
    3).实现交互:从绑定事件开始。
  2. props适用于:
    (1).父组件==>子组件通信
    (2).子组件==>父组件通信(要求父先给子一个函数)
  3. 使用v-model时要切记: v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
  4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

22.2 组件的自定义事件

  1. 一种组件间通信的方式,适用于:子组件==>父组件
  2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
  3. 绑定自定义事件:
    (1)第一种方式,在父组件中:
    (2)第二种方式,在父组件中:
    <Demo ref="demo"/>
    ......
    mounted(){
        this.$refs.xxx.$on('zdy',this.test)
    }
    
  4. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。
  5. 触发自定义事件: this.$emit('zdy',数据)
  6. 解绑自定义事件this.$off('zdy')
  7. 组件上也可以绑定原生DOM事件,需要使用native修饰符(如@click.native)。
  8. 注意:通过 this.refs.x.x.son ('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!

App.vue




Student.vue




22.3 全局事件总线(GlobalEventBus)

  1. 一种组件间通信的方式,适用于任意组件间通信。
  2. 安装全局事件总线:
    // main.js
    new Vue({
        ......
        beforeCreate() {
        Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm},
        },
        ......
    })
  1. 使用事件总线:
    (1) 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
    methods(){
        demo(data){......}
    }
    ......
    mounted() {
        this.$bus.$on('xxxx',this.demo)
    }

(2) 提供数据:this.$bus.$emit( 'xxxx',数据)
4. 最好在beforeDestroy钩子中(接收数据方),用$off去解绑当前组件所用到的事件。

    beforeDestroy() {
        this.$bus.$off("xxxx");
    },

22.4. 消息订阅与发布(pubsub)

  1. 一种组件间通信的方式,适用于任意组件间通信。
  2. 使用步骤:
    1.安装pubsub: npm i pubsub-js
    2.引入: import pubsub from 'pubsub-js'
    3.接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
    methods(){
        demo(data){......}
    }
    ......
    mounted() {
        this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
    }
  1. 提供数据:pubsub.publish('xxx',数据)
  2. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)取消订阅。
  methods: {
    sendStudentName() {
      pubsub.publish("hello", this.name);
    },
  }
  mounted() {
    this.pubid = pubsub.subscribe("hello", (masgName, data) => {
      console.log("订阅了消息", this, masgName, data);
    });
  },
  beforeDestroy() {
    pubsub.unsubscribe(this.pubid);
  }

22.5. nextTick

1.语法: this.$nextTick(回调函数)
2.作用:在下一次DOM更新结束后执行其指定的回调。
3.什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时要在nextTick所指定的回调函数中执行。

(在下次DOM更新 循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。)

23. Vue过渡的过渡与动画

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

  2. 图示:
    Vue2学习笔记_第6张图片

  3. 写法:
    1.准备好样式:
    * 元素进入的样式:
    1.v-enter:进入的起点
    2.v-enter-active:进入过程中–(动画)
    3.v-enter-to:进入的终点
    * 元素离开的样式:
    1.v-leave:离开的起点
    2.v-leave-active:离开过程中–(动画)
    3.v-leave-to:离开的终点
    2. 使用包裹要过度的元素,并配置name属性:

    
        

你好啊!

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

例子
(1)直接用动画写效果



(2)用过渡写效果



(3)用第三方库写效果(例如:https:/ /animate.style/)

// App.vue 中引入import 'animate.css';


24. vue脚手架配置代理

24.1 配置代理方法介绍

解决跨域
1、用cors,在服务器响应时,后端配置几个特殊的响头;
2、jsonp,借助了script标签中的src,在引入外部资源时,不受同源策略限制(前后端配合,只能解决get请求跨域问题);
3、代理服务器,客服端–同域名代理服务器–服务器。

例:本机端口号为8081,服务器端口号为5000,就需要端口号为8081的服务器。

方法一
在vue.config.js中添加如下配置:

    devServer:{
        proxy:"http://localhost:5000"
    }

说明:

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

方法二
编写vue.config.js配置具体代理规则:

module.exports = {
  devServer: {
    proxy: {
      '/api': {//匹配所有以'/api'开头的请求路径
        target: 'http://localhost:5000',//代理目标的基础路径
        pathRewrite: { '^/api': '' },
        ws: true,//用于支持websocket
        changeOrigin: true  //用于控制请求头中的host值,为true时将代理服务器端口号变为服务器端口号
      },
      '/api2': {
        target: 'http://localhost:5001',
        pathRewrite: { '^/api2': '' },
        ws: true,//用于支持websocket
        changeOrigin: true  
      },
      /* '/foo': {
        target: ''
      } */
    }
  }
}
/* 
changeOrigin设置为true时,服务器收到的请求头中的host为: localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为: localhost:8080
changeOrigin默认值为true 
*/

说明:

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

App.vue




24.2 github案例

SearchGc.vue




25. 插槽

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于父组件==>子组件。
  2. 分类:默认插槽、具名插槽、作用域插槽

25.1 默认插槽

使用的组件指定的位置留一个坑,如果在外部,使用其组件包裹某内容(可以是任何模板代码,也可以是HTML,还可以是组件),则该内容就会被分发到处(一个有趣的说法就是把“坑”补上),渲染出来。当然,也可以不放任何内容,不影响组件渲染,就好比最开始的情况。

// 父组件中:
    
        
html结构1
// 子组件中:Category

25.2 具名插槽

所谓具名插槽,顾名思义就是起了名字的插槽。有时我们需要多个插槽,例如当我们想使用某种通用模板:
对于这样的情况, 元素有一个特殊的attribute:name。这个 attribute 可以用来定义额外的插槽:

// B.vue

// App.vue