07-Vue2

编程知识 更新时间:2023-04-25 06:46:29

导论

  • 官网:https://cn.vuejs/

搭建 Vue 开发环境

1、安装 Vue.js devtools

  • 老版本的vue网址:https://v2.vuejs/v2/guide/installation.html

2、安装开发者调试工具

注意:此时会出现

Download the Vue Devtools extension for a better development experience:https://github/vuejs/vue-devtools

提示

  • 将下载的 Vue.js devtools 拖入chome的拓展程序中,并在详情里面勾选“允许访问文件地址”

3、关闭了那个烦人的提示

  • 进入 vue.js 设置,就可以了
productionTip: false

一、Vue 核心

入门程序

1、想让vue工作,就必须创建一个vue实例,且要传入一个配置对象。

2、root容器里的代码依然符合html规范,只不过混入了一些特殊的vue语法

3、root容器里的代码被称为【vue模板】

4、vue实力和容器时一一对应的

5、真是开发中只有一个vue实力,并且回配合着组件一起使用

6、{{xxx}}中的xxx要谢js表达式,且xx可以自动读取data中的所有属性

7、一旦data中的数据发生改变,那么页面中用该数据的地方也会自动更新

<div id="root">
    <h1>Hello {{name.toUpperCase()}}</h1>
</div>

<script>
    new Vue({
        el: '#root',
        data: {
            name: "Robber"
        }
    })
</script>

模板语法

1、插值语法:用于解析标签体内容

2、指令语法:适用解析标签

插值语法

<div id="root">
    <h1>Hello {{name}}</h1>
</div>

<script>
    // Vue.config.productionTip = false;
    new Vue({
       el: '#root',
       data: {
           name: "Robber"
       }
    })
</script>

指令语法

<div id="div">
        <h1>指令表达式</h1>
        <hr>
        <a v-bind:href="url">拉克斯基的离开房间撒地方</a>
    </div>
    <script>
        new Vue({
            el: '#div',
            data: {
                url: 'http://www.baidu'
            }
        })
    </script>

数据绑定

单向数据绑定

  1. 语法:v-bind:href =“xxx” 或简写为 :href

  2. 特点:数据只能从 data 流向页面

双向数据绑定

  1. 语法:v-mode:value=“xxx” 或简写为 v-model=“xxx”

  2. 特点:数据不仅能从 data 流向页面,还能从页面流向 data

<div id="root">
    <!-- 1、常规写法 -->
    单向数据绑定<input type="text" v-bind:value="name"><br>
    双向数据绑定<input type="text" v-model:value="name">
    <hr>
    <!-- 2、简写 -->
    单向数据绑定<input type="text" :value="name"><br>
    双向数据绑定<input type="text" v-model="name">
</div>
<script>
    new Vue({
        el: '#root',
        data: {
            name: 'Robber'
        }
    })
</script>

data 与 el 的两种写法

1、el 有两种写法

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

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

2、data有两种写法

​ 1)对象式

​ 2)函数时

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

3、一个重要的原则:

​ 由vue管理的函数,一定不要谢箭头函数,this就不再时vue实例了。

el 两种写法

//第一种
let vue = new Vue({
            el: '#asdf',
            data: {
                name: 'Robber'
            }
        })

//第二种
let vue = new Vue({
    data: {
        name: 'Robber'
    }
})
vue.$mount('#asdf');

data 两种写法

//函数式
let vue = new Vue({
    data() {
        return {
            name: 'Robber'
        }
    }
})
vue.$mount('#asdf');

//对象式
let vue = new Vue({
    data: {
        name: 'Robber'
    }
})
vue.$mount('#asdf');

MVVM 模型

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

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

3、VM:试图模型(ViewModel):Vue实例

观察发现:

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

Object.defineProperty

  • 该方法可以动态的修改对象的属性数值
let number = 18;
let person = {
    name: 'Robber',
    sex: '男'
}
Object.defineProperty(person, 'age', {
    get() {
        console.log('有人读取');
        return number;
    },
    set(value) {
        number = value;
    }
})

数据代理

  • 可以通过一个对象代理obj对象
<script>
    let obj = { x: 100 }
    let obj2 = { y: 100 }
    Object.defineProperty(obj2, 'x', {
        get() {
            return obj.x;
        },
        set(value) {
            obj.x = value;
        }
    })
</script>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eKcO8cIB-1678617517273)(D:\01_Software\03-markdownImages\image-20230201100331824.png)]

事件绑定

1、使用v-on:xxx 或 @xxx 绑定事件,其中xxx式期间名

2、事件的回调需要配置再methods对象中,最终会在vm上

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

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

5、@click=“demo” 和 @click=“demo{$(event)}” 效果一样,但后者可以传参

<div id="box">
    <h1>事件处理</h1>
    <hr>
    <button v-on:click="fun1">{{functionName}}</button>
    <button @click="fun2($event,66)">哈哈哈哈</button>
</div>
<script>
    const vm = new Vue({
        el: '#box',
        data: {
            functionName: '第一个函数'
        },
        methods: {
            fun1() {
                alert('fun1');
            },
            fun2(event,data) {
                alert('fun2');
            },
        }
    })
</script>

点击事件

1.prevent:阻止默认事件

2.stop:阻止事件冒泡

3.once:事件只触发一次

​ 4.capture:使用事件的捕获模式

​ 5.self:只有event.target式当前操作的元素时才会触发事件(可以阻止冒泡)

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

<div id="box">
    <h1>事件修饰符</h1>
    <hr>
    <!-- prevent 阻止默认事件 -->
    <a @click.prevent="tiaozhuan" href="http://www.baidu">跳转链接</a>
    <hr>
    <!-- stop 阻止事件冒泡 -->
    <div @click="div1">div1
        <div @click.stop="div2">div2</div>
    </div>
    <!-- once:事件只触发一次 -->
    <button @click.once="tiaozhuan">
        只能触发一次
    </button>
</div>
<script>
    new Vue({
        el: '#box',
        data: {

            },
        methods: {
            tiaozhuan() {
                alert('asdfsadf');
            }, div1() {
                console.log('div1');
            },
            div2() {
                console.log('div2');
            }
        }

        })
</script>

键盘事件

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

  • 回车 => enter
  • 删除 => delete
  • 退出 => esc
  • 空格 => space
  • 换行 => tab
  • 上 => up
  • 下 => down
  • 左 => left
  • 右 => right

2、Vue未提供别名的按键,可以使用按键原始的key值取绑定,但注意要转为kebab-case

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

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

​ 2)配合keydown使用:正常触发事件

4、也可以使用keyCode去指定具体的按键(不推荐)

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

<div id="root">
    <h1>欢迎你</h1>
    <hr>
    <input type="text" placeholder="输入文字" @keyup.enter="fun">
</div>
<script>
    new Vue({
        el: '#root',
        data: {

            },
        methods: {
            fun(e) {
                alert(e.target.value);
            }
        }
    })
</script>

计算属性与监听

插值计算

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

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

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

    1)初次读取时会执行一次

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

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

  5. 备注:

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

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

插值算法

<div id="root">
    姓:<input type="text" v-model:value="firstName"><br>
    名:<input type="text" v-model:value="lastName"><br>
    全名:<span>{{hahah()}}</span>
</div>
<script>
    const vue = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        methods: {
            hahah() {
                return this.firstName + this.lastName;
            }
        }
    })
</script>             

计算机属性

<div id="root">
    姓:<input type="text" v-model:value="firstName"><br>
    名:<input type="text" v-model:value="lastName"><br>
    全名:<span>{{fullName}}</span>
</div>
<script>
    const vue = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        computed: {
            fullName: {
                get() {
                    console.log('get方法被触发')
                    return this.firstName + '-' + this.lastName
                },
                set(value) {
                    var arr = value.split('-');
                    this.firstName = arr[0];
                    this.lastName = arr[1];
                }
            }
        }
    })
</script>

简写方法

  • 当该fullName属性只有get方法时,就可以直接使用简写的形式,形式跟函数一样返回值则是该属性的数值
computed: {
    // 1、完整写法
    // fullName: {
    //     get() {
    //         console.log('get方法被触发')
    //         return this.firstName + '-' + this.lastName
    //     },
    //     set(value) {
    //         var arr = value.split('-');
    //         this.firstName = arr[0];
    //         this.lastName = arr[1];
    //     }
    // }

    //2、简写
    fullName() {
        console.log('get方法被调用');
        return this.firstName + '-' + this.lastName;
    }
}

监听属性

  1. 当被监视的属性变化时,回调函数自动调用,进行相关操作

  2. 监视的属性必须存在,才能进行监听

  3. 监视的两种写法:

​ 1)new Vue时传入watch配置

​ 2)通过vm.$watch监视

<div id="box">
    <h1>今天天气{{tianqi()}}</h1>
    <button @click="isHost = !isHost">切换天气</button>
</div>
<script>
    new Vue({
        el: '#box',
        data: {
            isHost: true
        },
        methods: {
            tianqi() {
                return this.isHost ? '炎热' : '凉爽';
            }
        },
    })
</script>

通过watch监视

const vue = new Vue({
    el: '#box',
    data: {
        isHost: true
    },
    methods: {
        tianqi() {
            return this.isHost ? '炎热' : '凉爽';
        }
    },
    watch: {
        isHost: {
            immediate: true, // 初始化让handler调用一次
            handler(newValue, oldValue) {
                console.log('isHot被修改', newValue, oldValue);
            }
        }
    }
})

第二种方式

vue.$watch('isHost', {
    immediate: true, // 初始化让handler调用一次
    handler(newValue, oldValue) {
        console.log('isHot被修改', newValue, oldValue);
    }
})

深度监视

  1. Vue中的watch默认不检测对象内布置的改变(一层)
  2. 配置deep:true可以检测对象内布置改变(多层)

备注

​ 1.Vue自身可以检测对象内布置的改变,单Vue提供的watch默认不可以!

​ 2.使用watch时根据数据的具体解构,决定大哥是否采用深度监视。

<div id="box">
    <h1>看这里{{numbers.a}}</h1>
    <button @click="numbers.a++">a+1</button>
</div>
<script>
    const vue = new Vue({
        el: '#box',
        data: {
            numbers: {
                a: 100,
                b: 200
            }
        },
        watch: {
            numbers: {
                handler() {
                    console.log('numbers被修改');
                }
            }
        }
    })
</script>

深度监视的简写

const vue = new Vue({
    el: '#box',
    data: {
        isHost: true
    },
    methods: {
        tianqi() {
            return this.isHost ? '炎热' : '凉爽'
        },
        change() {
            this.isHost = !this.isHost
        }
    },
    watch: {
        isHost() {
            console.log('isHost被修改');
        }
    }
})

第二种形式

vue.$watch('isHost', {
            deep: true,
            immediate: true,
            handler() {
                console.log("asdfkjasdflj");
            }
        })

computed和watch的区别

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

两个重要的小原则:

  1. 所有被vue管理的函数,最好使用普通函数,这样this的指向才是vm 或者是组件实例对象
  2. 所有不被vue所管理的函数(定时器的回调函数、ajax的回调函数等),这样this的指向才是vm或者时组件实例对象

修改名字案例

<div id="root">
    姓:<input type="text" v-model:value="firstName"><br>
    名:<input type="text" v-model:value="lastName"><br>
    全名:<span>{{fullName}}</span>
</div>
<script>
    const vue = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三',
            fullName: '张-三'

            },
        watch: {
            firstName(value) {
                console.log('asldfjslakdjf ');
                this.fullName = value + '-' + this.lastName;
            },
            lastName(value) {
                this.fullName = this.firstName + '-' + value;
            }
        }
    })
</script>

class 与 style 绑定

  1. 字符串的写法,适用于:样式的类名不确定,需要动态指定。
  2. 数组写法,适用于:要绑定的样式个数不确定、名字不确定
  3. 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定要不要
<div id="root">
    <!-- 1. 字符串的写法,适用于:样式的类名不确定,需要动态指定。 -->
    <div class="style1" :class="change" @click="hhh">
        aslkdjflsadjf
    </div>
    <!-- 2. 数组写法,适用于:要绑定的样式个数不确定、名字不确定 -->
    <div class="style1" :class="classArr">
        aslkdjflsadjf
    </div>
    <!-- 3. 对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定要不要 -->
    <div class="style1" :class="classTemp">
        asdfkjhasdkfjhasjkdfhjk
    </div>
</div>
<script>
    new Vue({
        el: '#root',
        data: {
            change: '',
            classArr: ['style1', 'style2', 'style3'],
            classTemp: {
                style2: true,
                style3: true
            }
        },
        methods: {
            hhh() {
                this.change = 'style2';
            }
        },
    })
</script>

条件渲染

1、v-if

​ 1)v-if=“表达式”

​ 2)v-else-if=“表达式”

​ 3)v-else=“表达式”

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

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

注意:v-if可以和v-eles-if、v-else一起使用,但要求解构不能被"破坏"

2、v-show

写法:v-show=“表达式”

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

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

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

<div id="root">
    <h1>条件渲染</h1>
    <hr>
    <button @click="fun">n+1</button>
    <h1>{{n}}</h1>
    <div v-show="n === 1">哈哈哈哈</div>
    <div v-if="n === 1">n = 1</div>
    <div v-else-if="n === 1">n = 2</div>
    <div v-else-if="n === 2">n = 3</div>
    <div v-else>n = 0</div>
</div>
<script>
    const vue = new Vue({
        el: '#root',
        data: {
            n: 0
        },
        methods: {
            fun() {
                this.n = this.n + 1;
            }
        },
    })
</script>

列表渲染

  • v-for指令
    1. 用于展示列表数据
    2. 语法:v-for=“(item, index) in xxx” :key=“yyy”
    3. 可遍历:数组、对象、字符串、指定次数
<div id="root">
    <ul>
        <!-- 1、遍历数组 -->
        <li v-for="(person,index) in persons" :key="person.id">
            {{index}} : {{person}}
        </li>
        <!-- 2、遍历对象 -->
        <li v-for="c,index in car" :key="index">
            {{index}} :{{c}}
        </li>
    </ul>
</div>
<script>
    new Vue({
        el: '#root',
        data: {
            persons: [
                { id: 1, name: "Robber" },
                { id: 2, name: "Luck" },
                { id: 3, name: "Arr" }
            ],
            car: {
                name: "奥迪A8",
                price: 80000
            }
        }
    })
</script>

面试题:react、vue的key有什么作用?

1、虚拟DOM中key的作用:

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

2、对比规则:

​ (1)旧虚拟DOM中找到了与新虚拟DOM相同的key:

​ a:若虚拟DOM中内容没有变,直接使用之前的内容DOM!

​ b:若虚拟DOM中内容变了,则生成新的真实DOM,随后替换页面中之前的真实DOM

​ (2)旧虚拟DOM中未找到与新虚拟DOM相同的key

​ 创建新的真是DOM,随后渲染到页面

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

​ (1)若对数据进行:逆序提娜佳、逆序删除等破坏顺序操作:

​ 会产生没有必要的真实DOM更i性能 ===> 界面没有问题,但效率底

​ (2)如果结构中还包括输入类的DOM:

​ 会产生错误DOM更新 ==> 界面有问题

4、开发中如何选择key?

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

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

列表过滤

<div id="root">
    <h1>computed</h1>
    <hr>
    <input type="text" placeholder="请输入" v-model="keyWord">
    <ul>
        <li v-for="person in fillPerson">
            {{person.name}}
        </li>
    </ul>
</div>
<script>
    new Vue({
        el: '#root',
        data: {
            keyWord: '',
            persons: [
                { name: '李信' },
                { name: '韩信' },
                { name: '蔡文姬' },
                { name: '虞姬' },
                { name: '甄姬' }
            ]
        },
        computed: {
            fillPerson() {
                return this.persons.filter((item) => {
                    return item.name.indexOf(this.keyWord) !== -1;
                })
            }
        }
    })
</script>

列表排序

<div id="root">
    <h1>computed</h1>
    <hr>
    <input type="text" placeholder="请输入" v-model="keyWord">
    <button @click="sortType=1">升序数组</button>
    <button @click="sortType=2">降序数组</button>
    <button @click="sortType=0">原数组</button>
    <ul>
        <li v-for="person in fillPerson">
            {{person.name}}-{{person.age}}
        </li>
    </ul>
</div>
<script>
    new Vue({
        el: '#root',
        data: {
            sortType: 0, // 1为升序 2为降序
            keyWord: '',
            persons: [
                { name: '李信', age: 19 },
                { name: '韩信', age: 20 },
                { name: '蔡文姬', age: 23 },
                { name: '虞姬', age: 11 },
                { name: '甄姬', age: 23 }
            ]
        },
        computed: {
            fillPerson() {
                let temp = this.persons.filter((item) => {
                    return item.name.indexOf(this.keyWord) !== -1;
                })
                if (this.sortType) {
                    temp.sort((p1, p2) => {
                        return this.sortType === 1 ? p1.age - p2.age : p2.age - p1.age;
                    })
                }
                return temp;
            }
        }
    })
</script>

vue原理

问题的提出

  • 问题:为什么通过整体替换对象就不可行
  • 原因:Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括push()、shift()、unshift()、splice()、sort()
  • 解决:
    • 1、通过vue包装的push来修改数组中的元素,如果不调用vue包装数组中的方法则不会被vue管理
    • 2、也可以通过Vue.set()方法
<script>
    new Vue({
        el: '#root',
        data: {
            persons: [
                { name: 'Robber', age: 19 },
                { name: '海盗', age: 33 }
            ]
        },
        methods: {
            change() {
                //1、可行,此时对象中的属性是存在get、set方法
                 this.persons[0].age = 22;
                 this.persons[0].name = '哈哈哈哈';
                
                //2、不可行
                this.persons[0] = { name: 'hahahah', age: 123 }
            }
        },
    })
</script>

vue检测数据的原理

<script>
    let data = {
        name: '海盗',
        address: '上海'
    }
    let obs = new Observer(data)
    let vm = {};
    vm._data = data = obs
    function Observer(obj) {
        //汇总所有的属性
        const arr = Object.keys(obj);
        arr.forEach((k) => {
            console.log(k);
            Object.defineProperty(this, k, {
                get() {
                    return obj[k];
                },
                set(value) {
                    obj[k] = value;
                }
            })
        })
    }
</script>

Vue.set()方法

  • 如果后面添加的属性需要响应式的属性就必须使用该方法,并且该方法只能用在 _data ** 中的对象**添加属性
  1. target:要更改的数据源
  2. key:要更改的具体数据
  3. value:重新赋的值
<div id="root">
    <button @click="addSex">添加性别</button>
    <h1>学生信息</h1>
    <h2>学生姓名:{{student.name}}</h2>
    <h2>学生性别:{{student.sex}}</h2>
</div>
<script>
    let vm = new Vue({
        el: '#root',
        data: {
            student: {
                name: 'Robber',
                sex: ''
            }
        },
        methods: {
            addSex() {
                this._data.student.sex = '男'
            }
        },
    })
</script>

Vue监视数据的原理

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

2、如何检测对象中的数据?

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

​ 1)对象中吼追加的属性,Vue默认不做响应式处理

​ 2)如需给后添加的属性做响应式,清使用如下API:

​ a)Vue.set(target, propertyName/index, value

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

3、如何检测数组中的数据?

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

​ 1)调用原生对应的方法对数组进行更新。

​ 2)重新解析模板,进而更新页面

4、再Vue修改数组中的某个元素一定要用如下方法:

​ 1)使用这些API:push()、shift()、unshift()、splice()、sort()

​ 2)Vue.set() 或 vm.$set()

特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据 添加属性!

<div id="box">
    <button @click="student.age++">年龄加一</button>
    <button @click="addSex">添加性别属性,默认值为:男</button>
    <button @click="addFriend">再列表首位添加一个朋友</button>
    <button @click="updateFirst">修改第一个朋友的名字为:张三</button>
    <button @click="addHobby">添加一个爱好</button>
    <button @click="updateFirstHobby">修改第一个爱好:开车</button>
    <hr>
    <h1>学生信息</h1>
    <hr>
    <h2>{{student.name}}</h2>
    <h2>{{student.age}}</h2>
    <h2 v-if="student.sex">{{student.sex}}</h2>
    <ul>
        <li v-for="h in student.hobby">
            {{h}}
        </li>
    </ul>
    <h2>这是我的朋友</h2>
    <ul>
        <li v-for="f in student.friend">
            {{f.name}}-{{f.age}}
        </li>
    </ul>
</div>
<script>
    new Vue({
        el: '#box',
        data: {
            student: {
                name: 'Robber',
                age: 20,
                hobby: ['抽烟', '喝酒', '烫头'],
                friend: [
                    { name: "luck", age: 22 },
                    { name: "jack", age: 10 },
                    { name: "lyq", age: 20 }
                ]
            },

            },
        methods: {
            addSex() {
                Vue.set(this.student, 'sex', '男')
            },
            addFriend() {
                this.student.friend.unshift({ name: "hahahah", age: 222 })
            },
            updateFirst() {
                this.student.friend[0].name = '张三'
            },
            addHobby() {
                this.student.hobby.push('洗澡')
            },
            updateFirstHobby() {
                // this.student.hobby.splice(0, 1, '开车')
                Vue.set(this.student.hobby, 0, '开车')
            }
        },
    })
</script>

收集表单数据

1、若,则v-model收集的时value值,用户输入的就是value值

2、若,则v-model收集的时value值,且要给标签配置value值。

3、若

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

​ 2)配置input的value属性:

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

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

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

​ lazy:失去焦点再收集数据

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

​ trim:输入收尾空格过滤

<div id="root">
    <form action="">
        账号<input type="text" v-model="account"><br><br>
        密码<input type="text" v-model="password"><br><br>
        年龄<input type="text" v-model="age"><br><br>
        性别:男 <input type="radio" name="sex" v-model="sex" value="male"><input type="radio" name="sex" v-model="sex" value="female"><br><br>
        爱好:
        学习<input type="checkbox" name="" id="" value="study" v-model="hobby">
        打游戏<input type="checkbox" name="" id="" value="play" v-model="hobby">
        吃饭<input type="checkbox" name="" id="" value="eat" v-model="hobby"><br><br>
        所属校区
        <select name="" id="" v-model="address">
            <option value="">请选择</option>
            <option value="beijing">北京</option>
            <option value="shanghai">上海</option>
            <option value="shenzhen">深圳</option>
            <option value="">广州</option>
        </select><br><br>
        其他信息:<textarea v-model="other"></textarea><br><br>
        <input type="checkbox" name="" id="">阅读病接受
        <a href="">《用户协议》</a><button @click.prevent="">提交</button>
    </form>
</div>
<script>
    new Vue({
        el: '#root',
        data: {
            account: '',
            password: '',
            age: '',
            sex: '',
            hobby: [],
            address: '',
            other: ''
        }
    })
</script>

过滤器

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

  • 语法:

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

    1.过滤器也可以接受额外参数、多个过滤器也可以串联

    2.并没有改变原本的数据,是产生信的对应的数据

<div id="box">
    <h1>过滤器</h1>
    <hr>
    <!-- computed方式 -->
    <h2>{{nowTime}}</h2>
    <!-- methods形式 -->
    <h2>{{nowtime1()}}</h2>
    <!-- 过滤器 -->
    <h2>{{time |filter1}}</h2>
    <!-- 串联过滤器 -->
    <h2>{{time | filter1("YYYY-MM-DD") | mySlice}}</h2>
    <!-- 全局过滤器 -->
    <h2>{{time | filter2}}</h2>
</div>
<script>
    //1、全局过滤器
    Vue.filter('filter2', function (value) {
        return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
    })
    // 2、局部过滤器
    const vm = new Vue({
        el: "#box",
        data: {
            time: 1675345179721
        },
        computed: {
            nowTime() {
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
            }
        },
        methods: {
            nowtime1() {
                return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
            }
        },
        filters: {
            filter1(time, str = "YYYY-MM-DD HH:mm:ss") {
                return dayjs(this.time).format(str)
            },
            mySlice(value) {
                return value.slice(0, 4)
            }
        }
    })
</script>

内置指令

我们学过指令:

  • v-bind:单项绑定解析表达式,可简写:xxx
  • v-model:双向绑定数据
  • v-for:遍历数组/对象/字符串
  • v-on:绑定事件监听,可简写为@
  • v-if:条件渲染(动态控制节点是否存在)
  • v-else:条件渲染(动态控制节点是否存在)
  • v-show:条件渲染(动态控制结点是否展示)

v-text指令

1、作用:向其所在的节点中渲染文本内容

2、与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会

v-html指令

1、作用:向指定节点中渲染包含html结构的内容

2、与插值语法的区别:

​ 1)v-html会替换节点中所有内容,{{xx}}则不会

​ 2)v-html可以识别的html结构

3、严重注意:v-html由安全性问题!!!

​ 1)在网站上动态渲染人一HTML是非常微信啊的,容易导致XSS攻击

​ 2)一定要在可信的内容上使用v-html,永远不要在用户提交的内容上!

v-cloak指令

1、本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。

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

CSS

[v-cloak] {
    display: none;
}

HTML

<div id="box">
    <h1>v-cloak指令</h1>
    <h2 v-cloak>{{name}}</h2>
</div>
<script>
    new Vue({
        el: '#box',
        data: {
            name: 'Robber'
        }
    })
</script>

v-once

1、v-once所在节点在初次动态渲染后,就视为静态内容了。

2、以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能

<div id="box">
    <h1>v-once</h1>
    <button @click="n++">n+1</button>
    <h2 v-once>初始值:{{n}}</h2>
    <h2>n加一:{{n}}</h2>
</div>
<script>
    new Vue({
        el: '#box',
        data: {
            n: 1
        }
    })
</script>

v-pre

1、跳过其所在节点的编译过程。

2、可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译

自定义指令

一、定义语法

​ 1)局部指令:

directives: {
    bing(element, binding) {
        console.log(element);
        console.log(binding);
        element.innerText = binding.value * 10
    },
    fbing: {
        bind(element, binding) {
            element.value = binding.value
        },
        inserted(element, binding) {
        },
        update(element, binding) {
            this.n = binding.value * 100
            element.value = binding.value
        }
    }
}

​ 2)全局指令:

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

二、配置对象中常用的3个回调

​ 1)bind:指令元素成功绑定时回调

​ 2)inserted:指令所在元素被插入页面时调用

​ 3)update:指令所在模板结构被重新解析调用

三、备注

​ 1)指令定义不加v-,但使用时要加v-;

​ 2)指令名如果是多个单词,要使用user-name名命方式,不要用userName

//1、需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍

//2、需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点
<div id="box">
    <h1>自定义属性</h1>
    <h2>初始值:{{n}}</h2>
    <h2>自定义指令n的数值:<span v-bing="n">{{n}}</span></h2>
    <button @click="n++">点击n+1</button>
    <input type="text" v-fbing="n">
</div>
const vm = new Vue({
    el: '#box',
    data: {
        n: 1
    },
    directives: {
        bing(element, binding) {
            console.log(element);
            console.log(binding);
            element.innerText = binding.value * 10
        },
        fbing: {
            bind(element, binding) {
                element.value = binding.value
            },
            inserted(element, binding) {
            },
            update(element, binding) {
                this.n = binding.value * 100
                element.value = binding.value
            }
        }
    }
})
//全局配置
vm.directive('bing', function (element, binding) {
    element.innerText = binding.value * 10
})

Vue生命周期

一、常用的生命周期钩子:

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

​ 2、beforeDestroy:清楚定时器、解绑自定义事件、取消订阅消息等【收尾工作】

二、关于销毁Vue实例

​ 1、销毁后借助Vue开发者工具看不到任何消息

​ 2、销毁后自定义事件会时效,但原生DOM事件依然有效

​ 3、一般不会再beforeDestroy操作数据,因为即使操作数据,也不会再触发更新流程了

三、vm的生命周期

​ 将要创建 ===> 调用beforeCreate函数

​ 创建完毕 ===> 调用created函数

​ 将要挂载 ===> 调用beforeMount函数

挂载完毕 ===> 调用mounted函数

​ 将要更新 ===> 调用beforeUpdate函数

​ 更新完毕 ===> 调用update函数

将要销毁 ===> 调用beforeDestroy函数

​ 销毁完毕 ===> 调用destroyed函数

<div id="box">
    <h1>生命周期</h1>
    <h2 :style="{opacity}">透明度</h2>
    <button @click="destroy">销毁vm</button>
</div>
<script>
    new Vue({
        el: '#box',
        data: {
            opacity: 0.5
        },
        methods: {
            destroy() {
                this.$destroy()
            }
        },
        mounted() {
            this.timer = setInterval(() => {
                this.opacity -= 0.1
                if (this.opacity <= 0) this.opacity = 1
            }, 100);
        },
        beforeDestroy() {
            console.log("销毁前工作");
            clearTimeout(this.timer);
        },
    })
</script>

二、Vue 组件化编程

非单文件组件

Vue使用组件的三大步骤:

​ 一、定义组件(创建组件)

​ 二、注册组件

​ 三、使用组件(写组件标签)

一、如何定义一个组件?

  • 使用Vue.extend(option)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别:
    1. el不要写,为什么? 最终所有的组件都要经过一个vm管理,由vm中的el决定符无哪个容器
    2. data必须咬写成函数,为什么? 避免组件被复用时,数据存在引用关系
  • 备注:使用template可以配置组件结构

二、如何注册组件?

​ 1、局部注册:靠new Vue的时候传入components选项

​ 2、全局注册:靠Vueponent(‘组成名’,组成)

三、编写组件标签:

<div id="box">
    <!-- 3、使用组件 -->
    <xuexiao></xuexiao>
    <hr>
    <xuesheng></xuesheng>
</div>
<script>
    // 1、创建组件
    const school = Vue.extend({
        template: `
            <div>
                <h2>{{name}}</h2>    
                <h2>{{address}}</h2>    
            </div>
        `,
        data() {
            return {
                name: '珠海科技学院',
                address: '珠海三灶'
            }
        }
    })
    const student = Vue.extend({
        template: `
            <div>
                <h2>{{name}}</h2>    
                <h2>{{age}}</h2>    
            </div>
        `,
        data() {
            return {
                name: 'Robber',
                age: 18
            }
        }
    })
    //2、注册逐渐
    new Vue({
        el: '#box',
        components: {
            xuexiao: school,
            xuesheng: student
        }
    })
</script>

Vue组件注意点

一、关于组件名:

  1. 一个单词组成
    • 第一种写法(首字母小写):school
    • 第二种写法(首字母大写):School
  2. 多个单词组成:
    • 第一种写法:my-school
    • 第二种写法:MySchool(需要Vue脚手架支持)
  3. 备注
    • 组件尽可能回避HTML中已有的元素名称。例如:h2、H2都不行
    • 可以使用name配置项指定组件在开发者工具中呈现的名字

二、关于组件标签:

  • 第一种写法:
  • 第二种写法:
  • 备注:不用脚手架时,会导致后续组件不能渲染

三、一个简写的方式:

  • const school = Vue.extend(options) 可简写为:const school = option
<div id="box">
    <h1>vue组件的注意点</h1>
    <hr>
    <!-- 使用组件 -->
    <school></school>
    <hr>
    <my-school></my-school>
</div>
<script>
    const school = {
        template: `
            <div>
                <h2>{{name}}</h2>    
            </div>
        `,
        data() {
            return {
                name: 'Robber',
                age: 24
            }
        }
    }
    const MySchool = {
        template: `
        
        <div>
            <h2>{{name}}</h2>    
        </div>
        `,
        data() {
            return {
                name: '珠海科技学院'
            }
        }
    }
    new Vue({
        el: '#box',
        components: {
            school: school,
            'my-school': MySchool
        }
    })
</script>

VueComponent构造函数

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

2、我们只需要写或者是,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponenet(options)

3、特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!

4、关于this指向:

​ 1)组件配置中:

  • data函数、method中的函数、wathch中的函数、computed中的函数它们的this均是【VueComponent实例对象】

​ 2)new Vue(options)配置中:

  • data函数、methods中的函数、watch中的函数、computed中的函数 它们this均为【Vue实例对象】

5、VueComponent的实例对象,以后简称vc(也可成为:组件实例对象),则Vue实例对象,以后简称vm

重要的内置对象关系

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

2、为什么要有这个关系:让组件实例对象(vc)可以访问到Vue原型上的属性、方法。

单文件组件的结构

1、组件:Student.vue

2、老大:APP.vue

3、主页:index.html

4、main.js

Student.vue

<template>
  <div class="demo">
    <h2>{{ name }}</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: "Robber",
    };
  },
};
</script>

<style>
.demo {
  background-color: orange;
}
</style>

APP.vue

<template>
  <div>
    <school></school>
    <student></student>
  </div>
</template>

<script>
import school from "./School";
import student from "./Student";
export default {
  components: {
    school,
    student,
  },
};
</script>

<style>
</style>

index.html

<!DOCTYPE html>
<html lang="en">

<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">
    <title>Document</title>
    <script src="../js/vue.min.js"></script>
</head>

<body>
    <div id="box">
        <app></app>
    </div>
    <script src="./main.js"></script>
</body>

</html>

main.js

import app from "./App.vue"

new Vue({
    el: '#box',
    data: {

    },
    components: {
        app
    }
})

三、Vue 脚手架

入门案例

第一步:配置 npm 淘宝镜像

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

第二步:全部安装@vue/cli

npm install -g @vue/cli

第三步:切换到你要创建项目的目录

vue create xxxx

第四步:启动项目

npm run serve

render函数

关于不同版本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函数去指定具 体内容

模板项目的结构

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

vue.config.js配置文件

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

ref属性

  1. 被用来给元素或子组件注册引用信息(id的替代者)

  2. 应用在html标签上获取的是真是DOM元素,应用在组件标签上是组件实例对象(vc)

  3. 使用方式:

    • 打标识:

      或者
    • 获取:this.$refs.xxx

pops属性

  • 功能:让组件接收外部传过来的数据
  1. 传递数据:
  2. 接受数据:
    • 第一种(只接收):pops:[‘name’]
    • 第二种(限制类型):props:{ name: String }
    • 第三种(限制类型、限制必要性、指定默认值):
props:{
    name:{
        type:String, //类型
        required: true, //必要性
        default: '老王' //默认值
    }
}

备注

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

注意

若前端想传进来的是一个数字或者一个对象等非字符串的形式,则要在 属性上加前v-bind,或者是使用冒号代替

mixins混入

  • 通过mixins的属性来达到公共方法的途径

特点:

​ 1、vue会先使用自己写,如果没有的话再使用mixins属性引入的

​ 2、mixins混入的mouted函数会在,自己定义的mouted函数后面执行

mixins.js

export const hunhe = {
    methods: {
        showName() {
            alert("哈哈哈哈哈")
        }
    },
}

Student.vue

import {hunhe} from "../mixins.js"
new Vue({
	data(){
		return{
			name: 'Robber'
		}
	},
	mixins:[hunhe] // 此时就拥有了mixins中的方法
})

插件

  • 功能:用于增强Vue
  • 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后是插件使用者传递的数据。
  • 定义插件:
对象.install = function(Vue, options){
    //1、添加全局过滤器
    Vue.filter(....)
    
    //2、添加全局指令
    Vue.directive(....)
    
    //3、配置全局混入
    Vue.mixin(....)
    
    //4、添加实例方法
    Vue.prototype.$myMethod = function(){...}
    Vue.prototype.$myProperty = xxxx
}
  • 使用插件:Vue.use()

scopred 样式

  • 作用:让样式再局部生成,防止组件中相互影响
  • 写作:
<style scoped>
    .demo {
        background-color: pink
    }
</style>

总结TodoList案例

1、组件化编码流程:

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

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

​ a)一个组件在用:放在组件自身即可。

​ b)一些组件在用:放在她们公共的父组件上(状态提升)

2、propos适用于:

​ 1)父组件 ===> 子组件 通信

​ 2)子组件 ===> 父组件 通信(要求父先给子一个函数)

3、使用v-model时要切记:v-model并当的值不能时props传进来的值,因为props是不可以修改的!

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

自定义事件

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

  2. 使用场景:A是父组件,B是子组件,B享给A传数据,那么就要在A中给B帮i的那个自定义事件(事件的回调在A中)

  3. 绑定自定义事件:

    1)第一种方式:在父组件中:<Demo @robber=“test”/> 或

    2)第二种方式:在父组件中:

    <Demo ref="demo"/>
    .....
    mounted(){
    	this.$refs.xxx.$on('robber', this.test)
    }
    

    3)若想让自定义事件只能触发一次,可以使用once 修饰符,或$once方法

  4. 触发自定义事件:this.$emit(‘robber’, 数据)

  5. 解绑自定义事件:this.$off(‘robber’)

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

  7. 注意通过this. r e f s . x x x . refs.xxx. refs.xxx.on(‘robber’, 回调)绑定自定义事件时,回调要么配置在method中,要么用箭头函数,否则this指向会出问题

全局事件总线

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

  2. 安装全局事件总线:

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

    1)接受数据:A组件想接受数据,则在A组件总给$bus绑定自定义事件,事件的回调留在A组件自身。

    School.vue

    mounted(){
        this.$bus.$on('robber', (data)=>{
            csl(data);
        })
    }
    

    2)提供数据:this. b u s . bus. bus.emit(‘xxxx’, 数据)

    Student.vue

    methods:{
        sendStudentName(){
            this.$bus.$emit('robber', this.name)
        }
    }
    
  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用的事件

消息订阅

  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('robber', this.demo) //订阅消息
    }
    

    4)提供数据:pubsub.publish('robber', 数据)

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

nextTick

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

过度与动画

  1. 作用:在插入、更新或移除DOM元素时,在核实的时候给元素添加样式类名
  2. 图示:
  1. 写法:

    1)准备好样式:

    • 元素进入的样式:

      a)v-enter:进入的起点

      b)v-enter-active:进入过程中

      c)v-enter-to:进入的终点

    • 元素离开的样式:

      a)v-leave:离开的起点

      b)v-leave-active:离开过程中

      c)v-leave-to:离开的终点

    2)使用<transition>包裹要过度的元素,并配置name属性:

    <transition name="demo">
    	<h1 v-show="isShow">你好呀</h1>
    </transition>
    

    3)备注:若有多个元素需要过度,则需要使用<transition-group>,且每个元素都要指定key

  2. 第三方库:animate.style

    <transition
          appear
          name="animate__animated animate__bounce"
          enter-active-class="animate__swing"
          leave-active-class="animate__backOutUp"
        >
          <h1 v-show="isShow">哈哈哈哈</h1>
        </transition>
    

四、Vue中的Ajax

解决开发环境Ajax跨域问题

  1. 引入axios包 npm i axios
  2. 编写请求资源代码
methods:{
    getStudents(){
        axios.get('http://localhost:8080/robber/students').then(
        	response => { 
                csl(response.data)
            },
            error => {
                csl(error.message)
            }
        )
    }
}

方式一

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

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

说明:

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

方式二

​ 编写vue.config.js配置具体代理规则:

module.exports = {
  devServer: {
    proxy: {
      '/robber': { //匹配所有以 '/api'开头的请求路径
        target: 'http://localhost:5000', // 代理目标的基础路径
        changeOrigin: true // 屏蔽端口号
        pathRewrite: {'^/robber': ''}
      },
    }
  }
}

slot 插槽

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

  2. 分类:默认插槽、具体插槽、作用域插槽

  3. 使用方式:

    1)默认插槽

    父组件:
    	<Category>
    		<div>html结构</div>
    	</Category>
    子组件:
    	<div>
            <!--定义插槽-->
    		<slot>插槽默认内容...</slot>       
    	</div>
    

    2)居名插槽:

    父组件:
    	<Category>
    		<template slot="center">
            	<div>html结构</div>
            </template>
            
            <template v-slot:footer>
            	<div>html结构</div>
            </template>
    	</Category>
    子组件:
    	<template>
    		<div>
                <slot name="center">插槽默认内容</slot>
                <slot name="footer">插槽默认内容</slot>
            </div>
    	</template>
    

    3)作用域插槽:

    ​ a:理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定 (games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)

    ​ b:具体编码

    父组件:
    	<Category>
    		<template scope="scopeData">
            	<!--生成的ul列表-->
                <ul>
                    <li v-for="g in scopeData.games" :key="g">
                        {{g}}
                    </li>
                </ul>
            </template>
    	</Category>
    
    	<Category>
            <template slot-scope="scopeData">
                <!--生成的h4列表-->
                <h4 v-for="g in scopeData.games" :key="g">
                    {{g}}
                </h4>
            </template>
    	</Category>
    
    子组件:
    	<template>
    		<div>
                <slot :games="games"></slot>
            </div>
    	</template>
    
    	<sciprt>
    		export default{
            	name: 'Category',
            	props: ['title'],
            	data(){
            		return {
            			games:['红色警戒','穿越火线']
            		}
            	}
            }
    	</sciprt>
    

五、Vuex

理解Vuex

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

  2. 何时使用?

    多个组件需要共享数据时

  3. 搭建Vuex环境

    1)下载npm i vuex@3

    2)创建文件:src/store/index.js

    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //应用Vuex组件
    Vue.use(Vuex)
    
    //准备action对象-响应组件中用户的动作
    const actions = {}
    //准备mutations独享-修改state中的数据
    const mutations = {}
    //准备state对象-保存具体的数据
    const state = {}
    
    //创建并暴露store
    export default new Vuex.Store({
        actions,
        mutations,
        state
    })
    

    3)在 main.js 中创建vm时传入 store 配置项

    .....
    //引入store
    import store from './store'
    ......
    
    //创建vm
    new Vue({
        el: '#app',
        render: h => h(App),
        store
    })
    
  4. 基本使用

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

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

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

求和案例

index.js

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex组件
Vue.use(Vuex)

//准备action对象-响应组件中用户的动作
const actions = {
    jia(context, value) {
        console.log("actions-jia被调用");
        context.commit('JIA', value)
    },
    jian(context, value) {
        console.log('actions-jian被调用');
        context.commit('JIAN', value)
    },
    odd(context, value) {
        if (context.state.sum % 2) {
            context.commit('JIA', value)
        }

    },
    time(context, value) {
        setTimeout(() => {
            context.commit('JIA', value)
        }, 1000);
    }
}
//准备mutations独享-修改state中的数据
const mutations = {
    JIA(state, value) {
        console.log('mutations-jia被调用');
        state.sum += value
    },
    JIAN(state, value) {
        console.log('mutations-jian被调用');
        state.sum -= value
    }
}
//准备state对象-保存具体的数据
const state = {
    sum: 0
}

//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state
})

ComputedDemo.vue

<template>
  <div id="ComputedDemo">
    <h1>求和案例:{{ this.$store.state.sum}}</h1>
    <hr>
    <select v-model.number="m">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
    </select>
    <button @click="incream">+</button>
    <button @click="decream">-</button>
    <button @click="increamOdd">奇数加一</button>
    <button @click="increamWait">等会儿再加</button>
  </div>
</template>

<script>

export default {
  data() {
    return {
      m:1
    }
  },
  compute:{
      sum(){
          return this.$store.state.sum
      }
  },
  methods: {
    incream(){
      this.$store.dispatch('jia',this.m)
    },
    decream(){
      this.$store.dispatch('jian',this.m)
    },
    increamOdd(){
      this.$store.dispatch('odd',this.m)
    },
    increamWait(){
      this.$store.dispatch('time',this.m)
    }
  },mounted() {
    console.log(this);
  },
}
</script>

<style>
 button {
  margin-left: 5px;
 }
</style>

getters的使用

  1. 概念:当state中的数据需要经过加工后再使用时,可以用getters家加工。
  2. store.js 中追加getters配置
......

const getters = {
    bigSum(state){
        return state.sum * 10
    }
}

//创建并暴露store
export default new Vuex.Store({
    ....
    getters
})
  1. 组件中读取数据:$store.getters.bigSum

四个map方法的使用

  1. mapState方法:用于帮助我们映射 state 中的数据为计算属性
computed:{
    //sum(){
    //    return this.$store.state.sum
    //}
    //借助mapState生成计算属性:sum、school、subject(对象写法)
    ...mapState({sum: 'sum', school: 'school', subject: 'subject'})
    //借助mapState生成计算属性:sum、school、subject(数组写法)
    ...mapState(['sum', 'school', 'subject'])
},
  1. mapGetter方法:用于帮助我们映射 getters 中的数据为计算属性
computed:{
    //bigSum(){
    //    return this.$store.getters.bigSum
    //}
    //借助mapGetter生成计算属性:sum、school、subject(对象写法)
    ...mapGetter({bigSum: 'bigSum'})
    //借助mapGetter生成计算属性:sum、school、subject(数组写法)
    ...mapGetter(['bigSum'])
}
  1. mapActions方法:用于帮助我们生成与 actions 对话的方法,即:包含 $store.dipatch(xxx) 函数
methods: {
  // incream(){
  //   this.$storemit('JIA',this.m)
  // },
  // decream(){
  //   this.$storemit('JIAN',this.m)
  // 
    
  //第一种 对象的形式访问
  ...mapMutations({incream: 'JIA',decream: 'JIAN'}),
  //第二种:数组的形式(需要方法和mutations的方法名相同)
  ...mapMutations(['JIA','JIAM']),  
}
  1. mapMutations方法:用于帮助我们生成与 mutation 对话的方法,即:包含$storemit(xxx) 的函数
methods: {
  // increamOdd(){
  //   this.$store.dispatch('odd',this.m)
  // },
  // increamWait(){
  //   this.$store.dispatch('time',this.m)
  // }
  //第一种 对象的形式访问
  ...mapActions({increamOdd:'odd',increamWait: 'time'}),
  //第二种:数组的形式(需要方法和mutations的方法名相同)
  ...mapActions(['odd','time'])
  
}

模块化+ 命名空间

  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
    }
})
  1. 开启命名空间后,组件种读取state数据
//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取
...mapState('personAbout',['sum', 'school', 'subject'])
  1. 开启命名空间后,组件种读取getters数据:
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取
...mapGetters('countAbout',['bigSum'])
  1. 开启命名空间后,组件中调用dispatch
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout', {incrementOdd:'jiaOdd', incrementWait: 'jiaWait'})
  1. 开启命名后组件中调用commit
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations
...mapMutations('countAbout', {increment: 'JIA', decrement: 'JIAN'})

六、路由

  1. 理解:一个路由(route)就是一组映射关系(key-value),多个路由需要路由器(router)管理
  2. 前端:key是路劲,value是组件

基本使用

  1. 安装vue-router,命令:npm i vue-router@3
  2. 应用插件:Vue.use(VueRouter)
  3. 创建文件 /router/index.js
//引入VueRouter
import VueRouter from "vue-router";
import AboutCop from "../components/AboutCop"
import HomeCop from '../components/HomeCop'
//创建路由器
export default new VueRouter({
    routes: [
        {
            path: '/home',
            component: HomeCop
        },
        {
            path: '/about',
            component: AboutCop
        },

    ]
})
  1. 实现切换(actice-class可配置高亮样式)
<router-link active-class='active' to="/home">Home</router-link>
  1. 指定展示位置
<rout-view></rout-view>

几个注意点

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

多级路由

  1. 配置路由规则,使用children配置项:

    routers:[
        {
            path:'/about',
            component: About,
        },
        {
            path:'/home',
            component:Home,
            children:[
               {
                path: 'news',
                component:News
            	},
                {
                    path:'message',
                    component:Message
                }
            ]
            
        }
    ]
    
  2. 跳转(要写完完整路径)

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

路由传参

  1. 传递参数

    <!-- 第一种传参 -->
    	<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
         }">{{m.title}}</router-link>
    
  2. 接受参数

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

命名路由

  1. 作用:可以简化路由的跳转

  2. 如何使用

    1)给路由命名

    children:[
               {
                path: 'news',
                component:News
            	},
                {
                    name:'robber'
                    path:'message',
                    component:Message
                }
            ]
    

    2)简化跳转

    <!-- 简化前,需要些完整的路径 -->
    <router-link :to="`/home/message/detail`">跳转</router-link>
    
    <!--简化后,直接通过名字跳转-->
    <router-link :to="{name: robber}">跳转</router-link>
    
    <!-- 第二种对象形式传参 -->
    <router-link :to="{
    name:'robber',
        query:{
        id: m.id,
        title: m.title
    }">{{m.title}}</router-link>
    

路由的params参数

  1. 配置路由,声明接受params参数

    children:[
               {
                path: 'news',
                component:News
            	},
                {
                    name:'messsage'
                    path:'message',
                    component:Message,
                    children:[
                    	{
                    		name:'robber',
                    		path: 'detail/:id/:title',
                    		component: Detail
                    	}
                    ]
                }
            ]
    
  2. 传递参数

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

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

  1. 接受参数:

    $route.params.id
    $route.params.title
    

props配置项

  • 作用:让路由组件更方便的接受参数

    {
        name: 'robber',
        path: 'detail',
        components: 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
            }
        }
    }
    

<router-link>的replace属性

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

编程式路由导航

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

  2. 具体编码:

    //$router的两个API
    this.$router.push({
      name: 'robber',
      query: {
        id: m.id,
        title: m.titlem
      }
    })
    
    this.$router.replace({
        name: 'robber',
        query: {
            id: m.id,
            title: m.title
        }
    })
    
    this.$router.forward() //前进
    this.$router.back() //后退
    this.$router.go(1) //前进一步
    

缓存路由组件

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

  2. 具体编码:

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

特别注意:News是组件名(vue开发工具看到的那个)

两个新的声明周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态

  2. 具体名字:

    1)activated 路由组件被激活时触发

    2)deactivated 路由组件失活时触发

路由守卫

全局守卫

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

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

  3. 全局守卫:

    //全局前置守卫,初始化执行、每次路由切换前执行
    router.beforeEach((to,from,next)=>{
        if(to.meta.isAuth){
            if(localStorage.getItem('name') === 'robber'){
                next()
            }else{
                alert('暂无权限查看')
            }
        }else{
            next()
        }
    })
    
    //全局后置守卫,初始化执行,每次路由切换后执行
    router.afterEach((to,from)=>{
        if(to.meta.title){
            document.title = to.meta.title
        }else{
            document.title = 'vue_test'
        }
    })
    

独享路由

routes:[
    {
        path: '/home',
        component: HomeCop
        children:[],
    	beforeEnter(to,from,next){
    		if(to.meta.isAuth){
                if(localStorage.getItem('name') === 'robber'){
                    next()
                }else{
                    alert('暂无权限查看')
                }
            }else{
                next()
            }
		}
    }
]

组件内守卫

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

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

区别各个守卫

1、全局路由:相当于紫禁成大门的守卫不管是【皇上、太子、妃子】都要进行检查

2、独享路由:相当于【皇上】的护卫

3、组件守卫:相当于进了【皇上寝宫】室内守卫(太监)

路由器的两种工作模式

  1. 对呀一个url来说,什么屎hash值?-------#/及其后面的内容就是hash值

  2. hash值不会包括含在 HTTP 请求中,即:hash值不会带给服务器

  3. hash模式:

    1)地址中永远带着#号,不美观

    2)若以后将地址通过第三方收集app分享,若app校验严格,则地址会被标记为不合法

    3)兼容性较好。

  4. history模式:

    1)地址干净,美观

    2)兼容性和hash模式相对略差

    3)应用部署上线时需要后端人员支持,解决刷新页面服务器404的问题

七、Vue UI 组件库

移动端常用 UI 组件库

  1. Vant:https://youzan.github.io/vant
  2. Cube UI:https://didi.github.io/cube-ui
  3. Mini UI:http://mint-ui.github.io
  4. Nut UI:https://nutui.jd/#/(京东)

PC端常用 UI 组件库

  1. Element UI:https://element.eleme
  2. IView UI:https://www.iwiewui

Element UI 全局使用

  1. 安装npm i element-ui

  2. 引入

    import Vue from 'vue'
    import App from './App.vue'
    import ElementUI from 'element-ui';
    import 'element-ui/lib/theme-chalk/index.css';
    
    Vue.config.productionTip = false
    Vue.use(ElementUI);
    export default new Vue({
      render: h => h(App),
      beforeCreate() {
        Vue.prototype.$bus = this
      }
    }).$mount('#app')
    

Element UI 安需引入

  1. 修改main.js文件

    import Vue from 'vue'
    import App from './App.vue'
    import { Button } from 'element-ui';
    Vue.config.productionTip = false
    
    // Vueponent(Button.name, Button);
    
    export default new Vue({
      render: h => h(App),
      beforeCreate() {
        Vue.prototype.$bus = this
      }
    }).$mount('#app')
    
    
    
    
  2. 修改babel.config.js文件

    module.exports = {
      presets: [
        '@vue/cli-plugin-babel/preset',
        ["@babel/preset-env", { "modules": false }]
      ],
      plugins: [
        [
          "component",
          {
            "libraryName": "element-ui",
            "styleLibraryName": "theme-chalk"
          }
        ]
      ]
    }
    
    
  3. 分类:全局守卫、独享守卫、组件内守卫

  4. 全局守卫:

    //全局前置守卫,初始化执行、每次路由切换前执行
    router.beforeEach((to,from,next)=>{
        if(to.meta.isAuth){
            if(localStorage.getItem('name') === 'robber'){
                next()
            }else{
                alert('暂无权限查看')
            }
        }else{
            next()
        }
    })
    
    //全局后置守卫,初始化执行,每次路由切换后执行
    router.afterEach((to,from)=>{
        if(to.meta.title){
            document.title = to.meta.title
        }else{
            document.title = 'vue_test'
        }
    })
    

独享路由

routes:[
    {
        path: '/home',
        component: HomeCop
        children:[],
    	beforeEnter(to,from,next){
    		if(to.meta.isAuth){
                if(localStorage.getItem('name') === 'robber'){
                    next()
                }else{
                    alert('暂无权限查看')
                }
            }else{
                next()
            }
		}
    }
]

组件内守卫

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

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

区别各个守卫

1、全局路由:相当于紫禁成大门的守卫不管是【皇上、太子、妃子】都要进行检查

2、独享路由:相当于【皇上】的护卫

3、组件守卫:相当于进了【皇上寝宫】室内守卫(太监)

路由器的两种工作模式

  1. 对呀一个url来说,什么屎hash值?-------#/及其后面的内容就是hash值

  2. hash值不会包括含在 HTTP 请求中,即:hash值不会带给服务器

  3. hash模式:

    1)地址中永远带着#号,不美观

    2)若以后将地址通过第三方收集app分享,若app校验严格,则地址会被标记为不合法

    3)兼容性较好。

  4. history模式:

    1)地址干净,美观

    2)兼容性和hash模式相对略差

    3)应用部署上线时需要后端人员支持,解决刷新页面服务器404的问题

七、Vue UI 组件库

移动端常用 UI 组件库

  1. Vant:https://youzan.github.io/vant
  2. Cube UI:https://didi.github.io/cube-ui
  3. Mini UI:http://mint-ui.github.io
  4. Nut UI:https://nutui.jd/#/(京东)

PC端常用 UI 组件库

  1. Element UI:https://element.eleme
  2. IView UI:https://www.iwiewui

Element UI 全局使用

  1. 安装npm i element-ui

  2. 引入

    import Vue from 'vue'
    import App from './App.vue'
    import ElementUI from 'element-ui';
    import 'element-ui/lib/theme-chalk/index.css';
    
    Vue.config.productionTip = false
    Vue.use(ElementUI);
    export default new Vue({
      render: h => h(App),
      beforeCreate() {
        Vue.prototype.$bus = this
      }
    }).$mount('#app')
    

Element UI 安需引入

  1. 修改main.js文件

    import Vue from 'vue'
    import App from './App.vue'
    import { Button } from 'element-ui';
    Vue.config.productionTip = false
    
    // Vueponent(Button.name, Button);
    
    export default new Vue({
      render: h => h(App),
      beforeCreate() {
        Vue.prototype.$bus = this
      }
    }).$mount('#app')
    
    
    
    
  2. 修改babel.config.js文件

    module.exports = {
      presets: [
        '@vue/cli-plugin-babel/preset',
        ["@babel/preset-env", { "modules": false }]
      ],
      plugins: [
        [
          "component",
          {
            "libraryName": "element-ui",
            "styleLibraryName": "theme-chalk"
          }
        ]
      ]
    }
    
    

更多推荐

07-Vue2

本文发布于:2023-04-19 02:32:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/852dcd9940a67f3d47d90ca2e5e56192.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!

  • 86896文章数
  • 19013阅读数
  • 0评论数