VUE全家桶手册

VUE 核心

1 开始

1 引入Vue后自动定义了一个全局变量Vue

2 每个Vue应用都是通过Vue函数创建一个新的Vue实例开始的

3 {{里面可用表达式语句}}

4 Vue.config.productionTip=false //阻止 vue 在启动时生成产生提示

2 Vue使用简单举例

    <div id="app"> 
        {{message}}
    </div>
    <script type="text/javascript">
        var app=new Vue({
            el:'#app', //el用于指定元素(#id)
            data:{ //data用于申明变量,初始化赋值
                message:'Hello Vue!'
            }
        });
    </script>
    app.$mount('#root')//第二种绑定元素的写法,更加灵活

3 绑定样式

标签中用 v-bind:属性=“值” 用变量动态设置属性值,简写 :属性

1 字符串写法

用于:样式的类名不确定,需要动态指定

<body>
    <div id="root">
        <div class="basic" :class="mood" @click="change">test</div>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            mood:'normal'
        },
        methods:{
            change(){
                const arr=['normal','sad','happy'];
                const x=Math.floor(Math.random()*3) //Math.floor() 为向下取整
                this.mood=arr[x]
            }
        }
    })
</script>
<style>
    .basic{}
    .normal{}
    .sad{}
    .happy{}
</style>

2 数组写法

用于:要绑定的样式个数不确定、类名也不确定,可动态修改数组中的类

<body>
    <div id="root">
        <div class="basic" :class="moodArr" @click="change">test</div>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            moodArr:['normal','sad','happy']
        }
    })
</script>
<style>
    .basic{}
    .normal{}
    .sad{}
    .happy{}
</style>

3 对象写法

用于:要绑定的样式个数、类名确定,可动态确定对象中的样式是否开启使用

<body>
    <div id="root">
        <div class="basic" :class="moodObj" @click="change">test</div>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            moodObj:{
                normal:false,
                sad:false
            }
        }
    })
</script>
<style>
    .basic{}
    .normal{}
    .sad{}
    .happy{}
</style>

4 指令

https://cn.vuejs.org/v2/guide/syntax.html#%E6%8C%87%E4%BB%A4

1 v-once

标签中加入则{{message}}后续不会被改变

2 v-html

标签中加入 v-html=“变量名” 可将变量编译为html元素而非字符串

    <div id="app">
        <p>没加v-html输出的rawHtml:{{rawHtml}}</p>
        <!-- 加了v-html会将该标签内容清空,改为变量内容 -->
        <p v-html="rawHtml">加了v-html输出的rawHtml:{{rawHtml}}</p>
    </div>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                rawHtml: '<span style="color:red">rawHtml</span>'
            }
        })
    </script>

3 v-text

  • 标签中使用,标签内容会渲染成 v-text=“…” 中的变量值,若标签中本来也内容,会直接将其替换掉
  • 若变量内容为标签节点,它也不会解析

4 v-clock

  • 在标签中加入 v-clock ,配合 css,可避免网速过慢导致 vue 未接管时页面显示出未经 vue 处理的插值语法
  • v-clock 在 vue 接管后会自动消失
<h1 v-clock>{{msg}}</h1>
[v-clock]{display:none}

5 v-pre

  • 可以跳过 vue 对该节点的处理,标签中写什么就出现什么
  • 该指令常用在静态节点中,加快页面的编译速度

6 自定义指令

注:

  1. 指令命名如遇多单词:元素中用 - 分隔单词(v-big-number),函数中用完整形式(单驼峰单引号包裹)

    (‘bigNumber’,(element,binding){})

  2. 自定义指令函数中的 this 为 window 而非 vm

  3. 在某个 vm 中写的指令只能用于该 vm,需要定义全局指令则写在 vue 实例外面

    Vue.directive('fbind',{
                    bind(element,binding){
                        element.value=binding.value
                    },
                    inserted(element,binding){
                        element.focus()
                    },
                    update(element,binding) {
                        element.value=binding.value
                    }
                })
    
1 函数式
  • element 参数为真实 DOM 元素
  • binding 参数中包含被绑定元素的各个关键参数,最常用到的是其 value,value 值为指令中的变量值
  • 指令所在的模板(所在的 div#root)被重新解析指令会调用,即 data 有任何一个被修改,指令都会调用
<body>
    <div id="root">
        原始n的值:<span v-text="n"></span><br><br>
        经v-big处理后方法十倍:<span v-big="n"></span><br><br>
        <button @click="n++">n++</button>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            n:1
        },
        directives:{
            big(element,binding){
                element.innerText=binding.value*10
            }
        }
    })
</script>
2 对象式
  • 此为完整写法
  • bind() 为指令与元素绑定时;insert() 为元素插入页面时;update() 为指令所在的模板(所在的 div#root)被重新解析时

此处例子为实现页面加载后 input 自动获得焦点

<body>
    <div id="root">
        <input type="text" v-fbind:value="n">
        <button @click="n++">n++</button>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            n:1
        },
        directives:{
            fbind:{
                bind(element,binding){
                    element.value=binding.value
                },
                inserted(element,binding){
                    element.focus()
                },
                update(element,binding) {
                    element.value=binding.value
                },
            }
        }
    })
</script>

5 函数申明在methods中

1 静态

    <div id="app">
        <span v-bind:class="spanClass"  @click.stop="click">点我变红</span>
        <!-- .stop后缀防止事件冒泡 -->
    </div>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                spanClass:'spanClass'
            },
            methods:{
                click:function(){
                    vm.spanClass='spanClassClicked';
                }
            }
        })
    </script>
    <style>
        .spanClassClicked{
            color: red;
        }
    </style>

2 动态

用v-bind:class动态改变样式,即上代码可改为

    <div id="app">
        <div v-bind:class="{active:isActive}" 
        style="width: 100px; height: 40px;"
        @click.stop="click">点我变红</div>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                isActive:false
            },
            methods:{
                click:function(){
                    vm.isActive=true
                }
            }
        });
    </script>
    <style>
        .active{
            background-color: red;
            color: #fff;
        }
    </style>

6 条件渲染

https://cn.vuejs.org/v2/guide/conditional.html

1 v-show

会将元素一直留在页面中,改变其true/false只是改变display:none

判断中可使用表达式

<div v-show="false">test v-show</div>

2 v-if | v-else-if | v-else

若判断为false会直接将元素消除

包含这三个判断的元素结构需要在一起,结构不允许被破坏

<body>
    <div id="root">
        <div v-if="n===1">a</div>
        <div v-else-if="n===2">b</div>
        <div v-else>n={{n}}</div>
        <button @click="n++">点击+1</button>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            n:0
        }
    })
</script>

template 标签结合 v-if 使用,页面初始化后 template 标签会自动消失

<body>
    <div id="root">
        <button @click="n++">点击+1</button>
        <template v-if="n===2">
            <div>使用template,此时n=2</div>
        </template>
    </div>

</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            n: 0
        }
    })
</script>

7 列表

1 列表渲染

  • 渲染对象数组,index 为索引,取值从 0 开始
  • 渲染对象,index 为对象属性名
<body>
    <div id="root">
        <ul>
            <!-- 以对象自带的标识做 key 可避免比较算法导致的问题 -->
            <li v-for="(p,index) in personList" :key="p.id">
                 {{p.name}} ---- {{p.age}} --- {{p.id}}
                 <input type="text">
            </li>
        </ul>
        <button @click="add">在列表头添加</button>
    </div>

</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            personList:[
                {id:"001",name:'张三',age:18},
                {id:"002",name:'李四',age:20},
                {id:"003",name:'王五',age:24}
            ]
        },
        methods: {
            add(){
                const p={id:"004",name:"老刘",age:40}
                this.personList.unshift(p)
                //unshift() 为在对象数组头部添加
            }
        }
    })
</script>

2 列表过滤

eg: 模糊搜索

<body>
    <div id="root">
        <input type="text" placeholder="请输入查询姓名" v-model="keyWord">
        <ul>
            <li v-for="(p,index) in filPersonList" :key="p.id">
                {{p.name}} ---- {{p.age}} --- {{p.id}}
            </li>
        </ul>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            personList: [
                { id: "001", name: '马冬梅', age: 18 },
                { id: "002", name: '周冬雨', age: 20 },
                { id: "003", name: '周杰伦', age: 24 },
                { id: "004", name: '温兆伦', age: 22 }
            ],
            keyWord: ''
        },
        computed: {
            filPersonList() {
                return this.personList.filter((p) => p.name.indexOf(this.keyWord) != -1)
            }
        }
    })
</script>

3 列表排序

<body>
    <div id="root">
        <input type="text" placeholder="请输入查询姓名" v-model="keyWord">
        <button @click="sortType=2">年龄升序</button>
        <button @click="sortType=1">年龄降序</button>
        <button @click="sortType=0">原顺序</button>
        <ul>
            <li v-for="(p,index) in filPersonList" :key="p.id">
                {{p.name}} ---- {{p.age}} --- {{p.id}}
            </li>
        </ul>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            personList: [
                { id: "001", name: '马冬梅', age: 28 },
                { id: "002", name: '周冬雨', age: 20 },
                { id: "003", name: '周杰伦', age: 24 },
                { id: "004", name: '温兆伦', age: 22 }
            ],
            keyWord: '',
            sortType: 0 // 0 原顺序/ 1 降序/ 2 升序
        },
        computed: {
            filPersonList() {
                const arr = this.personList.filter((p) => p.name.indexOf(this.keyWord) != -1)
                if (this.sortType) {
                    arr.sort((p1, p2) => this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age)
                }
                return arr
            }
        }
    })
</script>

8 事件处理

1 事件绑定

v-on | @

    <div id="app">
        <button v-on:click="handleClick('abc',$event)">{{msg}}</button>
        //v-on可简写为@ =》v-on:click == @click
        //$event即告诉函数要传个event参数过去
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                msg: 'goood'
            },
            methods: {
                handleClick(str, e) {//e表示当前节点
                    vm.msg = "yeeaaahhhhh";//或者this.msg
                }
            }
        });
    </script>

2 事件修饰符

.prevent 阻止元素的默认行为的触发

<a href="www.bing.com" @click.prevent="handleClick"></a>
    //可阻止 <a></a> 的默认跳转行为

.stop 阻止事件冒泡

.once 事件只触发一次

修饰符可叠加,要注意修饰符的顺序

3 按键绑定

<body>
    <div id="root">
        <input @keyup.enter="entered" type="text">
        //keyup 为按键按下抬起后触发
    </div>
</body>
<script>
    new Vue({
        el:'#root',
        methods:{
            entered(e){
                console.log(e.target.value)
            }
        }
    })
</script>
<div id="root">

常用按键:

  • enter 回车
  • delete 删除(删除[Delete] 和 退格[Backspace] 都可触发)
  • esc 退出
  • space 空格
  • tab 换行(由于本身具有切换焦点的功能,需配合@keydown使用 @keydown.tab)
  • up 方向键上
  • down 下
  • left 左
  • right 右

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvXdb4QM-1661758418338)(C:\Users\Gucci\AppData\Roaming\Typora\typora-user-images\image-20220222103147285.png)]

9 表单绑定

  • v-model 双向绑定,即一处改变,另一处随之改变

  • 可在 data 申明时设置默认值

  • v-model 修饰符

    • .trim 去掉前后空格
    • .number 一般结合 type=“number” 使用,data中对应属性读取为数值型
    • .lazy 失去焦点后 data 才收集输入内容
  • data 中与表单对应的

    • text 框——字符串
    • 单选——字符串,标签中配置 value
    • 复选
      • 配置 value (多项选择)——数组,标签中配置 value
      • 不配置 value (是否同意协议)——字符串,收集为布尔值:勾选为 true;不勾选为 false
    • 下拉菜单——字符串,标签中配置 value
<div id="app">
        <!-- text 和 textarea 元素使用 value property 和 input 事件;
        checkbox 和 radio 使用 checked property 和 change 事件;
        select 字段将 value 作为 prop 并将 change 作为事件。 -->
        <input v-model="msgForInput" placeholder="此为输入框" @input="input" />
        <p>输入框内容:{{msgForInput}}</p>
        <!-- 复选按钮组 -->
        <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
        <label for="jack">Jack</label>
        <input type="checkbox" id="john" value="John" v-model="checkedNames">
        <label for="john">John</label>
        <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
        <label for="mike">Mike</label>
        <br>
        <span>选中的: {{ checkedNames }}</span>
        <br>
        <!-- 单选按钮 -->
        <input type="radio" id="one" value="One" v-model="picked">
        <label for="one">One</label>
        <br>
        <input type="radio" id="two" value="Two" v-model="picked">
        <label for="two">Two</label>
        <br>
        <span>选中的: {{ picked }}</span>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                msgForInput: '',
                checkedNames: ["Jack"],
                picked:'Two'
            },
            methods: {
                input: function () {
                    console.log(this.msgForInput);
                }
            }
        });
    </script>

10 数据代理

1 HTML中数据代理

<script>
    let person={
        name:"gucci",
        gender:"male",
    }
    Object.defineProperty(person,"age",{
        value:20,
        enumerable:true, //是否可以枚举(是否可以被遍历读出来)
        writable:true, //是否可以修改
        configurable:true //是否可以被删除
        //上述3个参数默认值为 false
    }) 
</script>
<script>
    let num=20
    let person={
        name:"gucci",
        gender:"male",
    }
    Object.defineProperty(person,"age",{
        //当age被读取时,get函数(getter)就会被调用
        get(){
            return num
        },
        //当age被修改时,set函数(setter)就会被调用
        set(value){
            num=value //由此也可实现变量和对象属性的双向绑定
        }
    })
</script>

2 VUE中数据代理

VUE中直接完成了数据代理的复杂操作

11 计算属性

用 data 中已有的属性进行计算得到新的属性

<body>
    <div id="root">
        <input type="text" v-model:value="firstName">
        <input type="text" v-model:value="lastName"><br /><br />
        <span>姓名:{{fullName}}</span>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            firstName: "张",
            lastName: "三"
        },
        computed: {
            fullName: {
                get() {
                    return this.firstName + '-' + this.lastName
                },
                set(value) {
                    const arr = value.split('-');//以 - 拆分字符串为字符数组
                    this.firstName = arr[0];
                    this.lastName = arr[1];
                }
            }
        }
    })
</script>

当确定不需要setter时可简写(大部分时候都不需要setter)

        computed: {
            fullName(){
                return this.firstName + '-' + this.lastName
            }
        }

12 监视属性

1 监视一般属性

用于监视属性是否被改变,改变时做出操作

计算属性也可被监视

<body>
    <div id="root">
        <h3>天气 {{info}}</h3>
        <button @click="change">点击切换天气</button>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            isHot: true
        },
        computed: {
            info() {
                return this.isHot ? '炎热' : '凉爽';
            }
        },
        methods: {
            change() {
                this.isHot = !this.isHot;
            }
        },
        watch: {
            isHot: {
                immediate:true, //初始化时调用一次handler,默认为false
                handler(newValue, oldValue) {
                    console.log("isHot被修改", newValue, oldValue)
                }
            }
        }
    })
</script>

或将watch写在vm配置外面

    vm.$watch('isHot', {
        immediate: true, //初始化时调用一次handler,默认为false
        handler(newValue, oldValue) {
            console.log("isHot被修改", newValue, oldValue)
        }
    })

2 监视多级结构属性

<body>
    <div id="root">
        <h3>a:{{numbers.a}}</h3>
        <button @click="numbers.a++">点击+1</button>
        <h3>b:{{numbers.b}}</h3>
        <button @click="numbers.b++">点击+1</button>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            numbers:{
                a:1,
                b:1
            }
        },
        watch: {
            //监视多级结构中某个属性
            'numbers.a': {
                handler(newValue, oldValue) {
                    console.log("numbers.a被修改", newValue, oldValue)
                }
            },
            //监视多级结构是否被修改(需要开启深度监听 | 其中一个属性被修改也算)
            numbers:{
                deep:true, //深度监听,默认false
                handler(newValue, oldValue) {
                    console.log("numbers被修改", newValue, oldValue)
                }
            }
        }
    })
</script>

3 简写

确认不需要 immediate 和 deep 时可简写

watch: {
    isHot(newValue, oldValue){
        console.log("isHot被修改", newValue, oldValue)
    }
}
    vm.$watch('isHot', function(newValue, oldValue){
        console.log("isHot被修改", newValue, oldValue)
    })

13 生命周期

1 挂载流程

1 beforeCreate()

vm 将要创建了,此时数据 data 还未放入实例 vm 中

2 created()

vm 完成创建,数据放入 vm

3 beforeMount()

vm 将要挂上页面解析模板去处理 DOM

4 mounted()
  • Vue 完成模板的解析并把初始的真实 DOM 元素放入页面(挂载完成)
  • mounted 中的 this 为 vm

2 更新流程

1 beforeUpdate()

data 数据更新时调用,此时数据是新的,但页面还没有刷新

2 updated()

data 数据更新完成调用,此时数据和页面都是新的

3 销毁流程

1 beforeDestroy()

使用了 vm.$destroy() 后,vm 被销毁之前调用,此时 vm 中的所有东西包括 data、methods 等等都还可以使用,但调用了 methods 中的方法页面不会再更新了

2 destroyed()

几乎没用

API

1 Vue.set()

  • 在已配置过的 vm 中添加新对象属性,也可用于修改数组中的值
  • 被添加的对象不能为 vm 或 vm 的根数据对象,需是 data 中已有的对象
  • 添加入的新属性自动完成数据代理,响应式
<body>
    <div id="root">
        <h3>姓名:{{stu.name}}</h3>
        <h3>年龄:{{stu.age}}</h3>
        <h3 v-if="stu.gender">性别:{{stu.gender}}</h3>
        <button @click="addGender">添加性别</button>
    </div>
</body>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            stu:{
                name:'Gucci',
                age:20
            }
        },
        methods: {
            addGender(){
                Vue.set(this.stu,'gender','男')
            }
        },
    })
</script>

2 数组监测

​ 因为 Vue 不会为数组的内容自动生成 getter 和 setter ,所以对数组内容添加、修改需要使用如下七个数组函数才能实现响应式,触发视图更新,亦可使用 Vue.set()

  • push(val) 末尾添加元素 val

  • pop() 删除最后一个元素

  • shift() 删除第一个元素

  • unshift(val) 开头添加元素 val

  • splice()

    表达式 arr.splice(index, num, item1, item2, …);

    参数说明

    第一个参数为 一个整数,用来指定添加/删除元素的位置,可以用负数来从尾部开始数 必填参数
    第二个参数为 删除元素的数量,若不想删除可以设置为0 可选参数
    再后面的参数为 向数组添加的元素 可选参数
    如果是删除操作,那么会把删除的元素放在一个新数组中返回。

    1.操作的元素会包括开始的元素

    2.如果数组的元素不够,会一直删除到数组的最后一位

  • sort(a,b) 排序,a-b 升序 | b-a 降序

  • reverse() 反转数组

1 filter() 函数

  • 筛选符合条件的元素组成新的数组并返回
  • x 为数组中的各个元素,返回值为对 x 的判断条件,返回值为筛选过后的新数组
  • 不会修改原数组
   var 数组 = 数组.filter(function(x){return 条件})

2 => 箭头函数

  • 可将函数体简单的函数写为箭头函数
    var f1 = () => 12;

    function f1(){return 12;}

    //----------------------

    var f2 = x => x * 2;

    function f2(x){return x*2;}

    //----------------------

    var f3 = (x, y) => {
        var z = x * 2 + y;
        y++;
        x *= 3;
        return (x + y + z) / 2
    };

    function f3(x,y){
        var z = x * 2 + y;
        y++;
        x *= 3;
        return (x + y + z) / 2
    }

3 sort() 函数

  • 对数组排序,对进行操作的数组直接修改,不返回新数组
  • a-b 为升序
  • b-a 为降序
数组.sort(function(a,b){return a-b})

4 禁止页面滑动

禁止左右滑动

<meta name="viewport" content="width=device-width,maximum-scale=1.0,initial-scale=1,user-scalable=no">

禁止左右滑动

通常用于盒子的大 > 视角框(即手机、PC屏幕大小)

    body {
        overflow:hidden;
    }

5 webStorage 本地存储

localStorage

浏览器存在本地,浏览器关闭后也不会消失

存 / 改

  • 只能存字符串
  • 要存对象,value 处用一下 JSON.stringify(obj)
localStorage.setItem('key','value')

  • 要取对象,将取出的对象用 JSON.parse(obj) 变回来
  • 读不存在的,读出来为 null
localStorage.getItem('key','value')

localStorage.removeItem('key')

清空

localStorage.clear()
sessionStorage

其 API 同上

浏览器关闭后数据清空

VUE 组件化

定义:实现应用中局部功能代码(css、js、html)和资源的集合

基础使用

组件使用三步骤:

  1. 创建组件
  2. 注册组件
  3. 使用组件

注:

  • 组件名为多单词,使用 - 连接,注册时使用完整写法,使用时也用 - 表示

    component:{
    	'my-school':school
    }
    
    <my-school></my-school>
    
  • 不要使用 html 已有的标签

  • VueComponent 组件实例对象,简称 vc

1 非单文件组件(不常用)

<body>
    <div id="root">
        <!-- 第三步:使用组件 -->
        <school></school>
        <hr>
        <student></student>
    </div>
</body>
<script>
    //第一步:注册school、student组件
    const school = Vue.extend({
        //模板 html 内容必须要 div 包起来
        template: `
        <div>
            <h3>学校名称:{{name}}</h3>
            <h3>学校地址:{{address}}</h3>
            <button @click="show">显示学校名</button>
        </div>
        `,
        data(){
            return{
                name:'西华大学',
                address:'四川成都'
            }
        },
        methods: {
            show(){
                alert(this.name)
            }
        },
    })
    const student=Vue.extend({
        template:`
        <div>
            <h3>学生姓名:{{name}}</h3>
            <h3>学生年龄:{{age}}</h3>
        </div>
        `,
        data(){
            return{
                name:'张三',
                age:20
            }
        }
    })
   
    new Vue({
        el: '#root',
        components:{//第二步:注册组件(局部注册)
            school,student
            /* 完整写法
            'school'(组件标签命名):school(组件),
            'student':student
            */
        }
    })
</script>

全局注册:

所有的 vm 都会注册上这个组件

Vue.component('组件的命名',创建组件的名字)

2 组件嵌套

注:

  • 子组件需要再父组件之前创建
  • 实际开发中需要一个最顶层的组件 app 来管理之下所有组件,vm 只注册管理 app
  • 所有的多标签组件必须有一个根元素(div)包裹!!!!!!

eg:student 为 school 的子组件

<body>
    <div id="root">

    </div>
</body>
<script>
    const student = Vue.extend({
        template: `
        <div>
            <h3>学生姓名:{{name}}</h3>
            <h3>学生年龄:{{age}}</h3>
        </div>
        `,
        data() {
            return {
                name: '张三',
                age: 20
            }
        }
    })

    const school = Vue.extend({
        template: `
        <div>
            <h3>学校名称:{{name}}</h3>
            <h3>学校地址:{{address}}</h3>
            <student></student>
        </div>
        `,
        data() {
            return {
                name: '西华大学',
                address: '四川成都'
            }
        },
        components: {
            student
        }
    })

    const hello = Vue.extend({
        template: `<h1>欢迎来到西华大学</h1>`,
    })

    //最顶层的组件 app
    const app = Vue.extend({
        template: `
        <div>
            <hello></hello>
            <school></school>
        </div>  
        `,
        components: {
            hello, school
        }
    })
    
    new Vue({
        el: '#root',
        template: `<app></app>`,
        components: { app },

    })
</script>

VUE 脚手架 (CLI)

单文件组件

开始

下载新版 vue/cli

npm install -g @vue/cli

vue --version  //查看cli版本
  1. 打开存放项目的文件夹
  2. 在文件夹中以管理员打开 cmd
  3. vue create 项目名(小写)
  4. 读条完了选择 vue2
  5. 用 vscode 打开项目文件夹
  6. 删除 HelloComponent.vue
  7. 防止出现组件命名或语法报错,在 vue.config.js 中加入一句 lintOnSave: false

1 基础

  • 编写的组件存放在 components 文件夹
  • 创建 .vue 文件,输入 <v 快速生成结构
  • 模板必须要一个根元素(div)包裹!!!
  • 每个组件需要 name 属性命名,最好与文件名一致,首字母大写
  • 组件在 App.vue 中导入、注册、使用

eg:

School.vue

<template>
    <div>
        <h3>学校名称:{{name}}</h3>
        <h3>学校地址:{{address}}</h3>
        <button @click="show">显示名称</button>
    </div>
</template>

<script>
    export default {
        name:'School',
        data(){
            return{
                name:'西华大学',
                address:'四川成都'
            }
        },
        methods:{
            show(){
                alert(this.name)
            }
        }
    }
</script>

Student.vue

<template>
    <div>
        <h3>学生姓名:{{name}}</h3>
        <h3>学生年龄:{{age}}</h3>
    </div>
</template>

<script>
    export default {
        name:'Student',
        data(){
            return{
                name:'张三',
                age:20
            }
        }
    }
</script>

App.vue

<template>
  <div>
      <School></School>
      <Student></Student>
  </div>
</template>

<script>
import School from './components/School.vue';
import Student from './components/Student.vue';

export default {
    name:'App',
    components:{
        School,Student
    }
};
</script>

2 ref 属性

用法:

this.$refs.ref
  • 用在组件模板的标签上
  • 对于 html 元素是 id 属性的平替
  • 对于组件标签,可以获得该组件的 vc,便于组件间通信
<template>
    <div>
        <Student ref="stu"></Student>
        <button @click="showStu">获得Student vc</button>
    </div>
</template>

<script>
import Student from "./components/Student.vue";

export default {
    name: "App",
    components: { Student },
    methods: {
        showStu() {
            console.log(this.$refs.stu);
        },
    },
};
</script>

3 prop属性

让组件接收外部传来的数据

1 传递数据

在组件标签被使用的地方将要传的数据作为属性写入标签

<Student name="xxx"></Student>

2 接收数据

接收后可直接在组件模板中使用

(1) 方式一:只接收(常用)

props:['name']

(2) 方式二:限制类型

props:{
	name:String
}

(3) 方式三:限定类型、限制必要性、指定默认值

props:{
	name:{
		type:String,
		required:true,
		default:'张三'
	}
}

注:一般不要对 props 读到的数据直接进行修改,如需修改,在 data 中定义后,在模板中使用 data 中的数据

data:{
	return{
		myName:this.name
	}
}

4 mixin 混入

可把多个组件都有的配置项提取写入 mixin.js 文件中一个对象

eg:两个组件都有 show 函数

(1)第一步:定义混入

mixin.js

export const mix={
    methods: {
        show(){
            console.log(this.name);
        }
    },
}

(2)第二步:使用混入

a 局部混入

Student.vue 和 School.vue 中导入 mix 并使用

import { mix } from "../mixin";
mixins: [mix]

b 全局混入

在 mian.js 中导入后,使用 API 使所有组件都混入 mix

Vue.mixin(mix)

5 插件

用于增强 vue

plugins.js 中为一个包含 install 函数的一个对象,其第一个参数为 Vue,第二个参数可以是插件使用者自己要传入的数据

定义插件

plugins.js

export default {
    install(Vue,options){
        //添加全局过滤器
        Vue.filter()
        //添加全局自定义指令
        Vue.directives()
        //配置全局混入
        Vue.mixin()
        //添加实例方法
        Vue.prototype.$myMethod=function(){}
    }
}

使用插件

导入

import plugin from './plugins'

使用:一定写在 new Vue({}) 之前

Vue.use(plugin)

6 scoped

在组件的 style 标签上写上 scoped,则样式只会作用于该组件,不会影响其他组件

一般用于处理不同组件样式重名


7 TodoList (通用组件化编码流程)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TBeLVPox-1661758418340)(C:\Users\Gucci\AppData\Roaming\Typora\typora-user-images\image-20220307112255730.png)]

  1. 组件化编码流程:

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

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

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

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

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

  2. props适用于:

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

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

  3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

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


8 自定义事件

可通过自定义事件实现子向父传递数据

eg:自定义事件 name,Student 组件将姓名传给 App

App.vue

方法一:直接绑定

<template>
  <Student @name="getName"/>
</template>

<script>
import Student from './components/Student.vue'
export default {
  name:'App',
  components:{Student},
  methods:{
    getName(n,...params){
      console.log('App从子组件获得姓名',n,params);
    }
  }
}
</script>

方法二:通过 ref 在生命周期钩子里绑定,用法更灵活

<template>
  <Student ref="student"/>
</template>

<script>
import Student from './components/Student.vue'
export default {
  name:'App',
  components:{Student},
  methods:{
    getName(n,...params){
      console.log('App从子组件获得姓名',n,params);
    }
  },
  mounted(){
    this.$refs.student.$on('name',this.getName)
  }
}
</script>

Student.vue

<template>
  <div>
    <h3>学生姓名:{{ name }}</h3>
    <h3>学生年龄:{{ age }}</h3>
    <button @click="sendName">获得姓名</button>
  </div>
</template>

<script>
export default {
    name:'Student',
    data(){
        return{
            name:'张三',
            age:20
        }
    },
    methods:{
        sendName(){
            //触发Student组件实例身上的name事件,同时可以传值
            this.$emit('name',this.name,param1,param2)
        }
    }
};
</script>

解绑

this.$off('demo')            //解绑一个
this.$off(['demo1','demo2']) //解绑多个
this.$off()                  //解绑所有

销毁实例对象

this.$destroy()

9 全局事件总线

任意组件间通信

1 安装全局事件总线

new Vue({
  beforeCreate(){
    Vue.prototype.$bus=this //$bus就是当前应用的vm
  }
})

2 使用

**接收:**A 组件要接收数据,在 A 中给 $bus 绑定自定义事件,事件回调也写在 A 自身

  • 字符串为事件名,各个事件名不要重复
  • data 为其他组件调用该事件传给 A 的数据
  • 若传来多个数据,参数 (…params) 接收为 params[] 数组
mounted(){
    this.$bus.$on('事件名',(data)=>{})
}

**解绑:**最好在接收数据的组件的 beforeDestroy 钩子中解绑用到的事件

  • 若要解绑多个,this. b u s . bus. bus.off(‘xxx’) 就多写几个
  beforeDestroy(){
    this.$bus.$off('xxx')
  }

**调用/提供:**B 组件要调用 A 绑定的自定义事件,数据写在后面

this.$bus.$emit('事件名',data)

10 消息订阅与发布

用于任意组件间通信

**安装pubsub-js:**管理员打开项目控制台

npm i pubsub-js

**引入:**在发送、接收的组件中都要引入

import pubsub from 'pubsub-js'

**接收(订阅):**回调函数参数一为消息名,参数二为接收的参数

mounted(){
  this.pubId=pubsub.subscribe('hello',(msgName,data)=>{
    console.log('有人发布了hello消息,回调调行',data);
  })
}

解绑:

  beforeDestroy(){
    pubsub.unsubscribe(this.pubId)
  }

提供(发布):

pubsub.publish('hello',param)

11 nextTick

在下一次 DOM 更新结束后执行其指定的回调

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

this.$nextTick(function(){})

什么时候用:

  1. created() 钩子函数进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中:原因是在 created() 钩子函数执行的时候 DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,并且不用 nextTick 会报错
  2. 更改数据后当想立即使用js操作新的视图的时候

12 动画 / 过渡

1 动画

(1)第一步:定义动画

@keyframes xhu {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0px);
  }
}

(2)第二步:将要写动画的元素用 包裹,并为其命名

<transition name="hello" appear>
    <h1 v-show="isShow">你好啊</h1>
</transition>

(3)第三步:使用动画

进入

.hello-enter-active {
  animation: xhu .5s;
}

离开

.hello-leave-active {
  animation: xhu .5s reverse;
}

完整代码:

<template>
  <div>
    <button @click="isShow = !isShow">显示/隐藏</button>
    <transition name="hello" appear>
      <h1 v-show="isShow">你好啊</h1>
    </transition>
  </div>
</template>

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

<style scoped>
h1 {
  background-color: orange;
}

.hello-enter-active {
  animation: xhu .5s;
}

.hello-leave-active {
  animation: xhu .5s reverse;
}

@keyframes xhu {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0px);
  }
}
</style>

2 过渡

不需要写关键帧 @keyframe,模板同动画一致

h1 {
  background-color: orange;
}

/* 进入的起点 *//* 离开的终点 */
.hello-enter,.hello-leave-to {
  transform: translateX(-100%);
}

.hello-enter-active,.hello-leave-active{
  transition: .2s;
}

/* 进入的终点 *//* 离开的起点 */
.hello-enter-to,.hello-leave {
  transform: translateX(0);
}

3 多个元素需要同一效果

其中的每个元素都要一个唯一的 key

<transition-group name="hello" appear>
    <h1 v-show="!isShow" key="1">你好啊</h1>
    <h1 v-show="isShow" key="2">你好啊</h1>
 </transition-group>

4 集成第三方动画

(1)安装 animate.css

管理员打开项目 cmd

npm install animate.css

(2)导入

import 'animate.css'

(3)使用

在要动画的元素的 <trasition(-group)> 上命名:

name="animate__animated animate__bounce"

在官网挑选动画,并复制其类名并使用

enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"

13 配置代理

1 方式一

  • 只能配置一个代理
  • 若请求的东西的名字本地存在,就不会走代理,直接拿本地的用

(1)下载 axios

npm i axios

(2)请求服务器的组件导入 axios,地址为当前服务器端口:端口号后写请求要的东西

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

(3)开启代理服务器:在 vue.config.js 中加入,地址为目标服务器端口

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

2 方式二

  • 可以配置多个代理
  • 可灵活选择是走代理,还是用本地资源
  • changeOrigin 默认为 true,为 true 目标服务器收到的地址为目标服务器地址;为 false 则受到本地地址

(1)配置代理

  devServer:{
    proxy:{
      '/xhu':{//匹配所有以 '/xhu' 开头的请求路径
        target:'http://localhost:5000',
        pathRewrite:{'^/xhu':''},
        ws:true, //用于支持websocket
        changeOrigin:false  //用于控制请求头中的host值
      },
      '/demo':{//匹配所有以 '/demo' 开头的请求路径
        target:'http://localhost:5001',
        pathRewrite:{'^/demo':''},
        ws:true, //用于支持websocket
        changeOrigin:false  //用于控制请求头中的host值
      },
    }
  }

(2)请求:端口号为本地端口,在端口号后写需要的请求头

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

14 slot 插槽

  • 在组件模板的某个位置写上 ,即可在组件被使用时组件体内写具体结构,写的结构会被放在 slot 的位置
  • slot 内部可写默认文字,当没有结构传入时显示
  • 写的具体结构的样式写在使用处的组件内,或本体组件内都可以

1 默认插槽

  • 结构要使用的数据在使用者的内部

eg:同一组件内传入不同的具体结构

组件:Category.vue

<template>
  <div class="category">
    <h3>{{title}}分类</h3>
    <slot>slot的默认值,使用者没传具体结构时出现</slot>
  </div>
</template>

<script>
export default {
  name: "Category",
  props:['title']
};
</script>

<style>
.category{
  background-color: skyblue;
  width: 200px;
}
h3{
  text-align: center;
  background-color: orange;
}

</style>

使用处:App.vue

<template>
  <div class="container">
    <Category title="美食">
      <img src="./assets/3.jpg" alt="" />
    </Category>
    <Category title="游戏">
      <ul>
        <li v-for="(g, index) in games" :key="index">
          {{ g }}
        </li>
      </ul>
    </Category>
    <Category title="电影">
      <video src="http://clips:vorwaerts-gmbh.de/big_duck_bunny.mp4"></video>
    </Category>
  </div>
</template>

<script>
import Category from "./components/Category.vue";
export default {
  name: "App",
  components: { Category },
  data() {
    return {
      foods: ["火锅", "烧烤", "啤酒", "小龙虾", "牛扒"],
      games: ["守望先锋", "英雄联盟", "原神", "我的世界"],
      films: ["教父", "触不可及", "肖申克"],
    };
  },
};
</script>

<style scoped>
.container {
  display: flex;
  justify-content: space-around;
}
video{
  width: 100%;
}
img{
  width: 100%;
}
</style>

2 具名插槽

  • 可以把不同的结构放在不同的 slot 中
  • 在模板中为 slot 命名: name=“xxx”
  • 使用时注明结构放在哪个 slot: slot=“name”
  • 结构用 template 包裹,可用 v-slot:name

eg:为上例多加点结构

组件:Category.vue

<template>
  <div class="category">
    <h3>{{title}}分类</h3>
    <slot name="center">center</slot>
    <slot name="footer">footer</slot>
  </div>
</template>

使用处:App.vue

<template>
  <div class="container">
    <Category title="美食">
      <img slot="center" src="./assets/3.jpg" alt="" />
      <a slot="footer" href="http://www.bing.com">更多美食</a>
    </Category>
    <Category title="游戏">
      <ul slot="center">
        <li v-for="(g, index) in games" :key="index">
          {{ g }}
        </li>
      </ul>
      <div slot="footer" class="foot">
        <a href="http://www.bing.com">单机游戏</a>
        <a href="http://www.bing.com">网络游戏</a>
      </div>
    </Category>
    <Category title="电影">
      <video
        slot="center"
        controls
        src="http://clips:vorwaerts-gmbh.de/big_duck_bunny.mp4"
      ></video>
      <template v-slot:footer>
        <div class="foot">
          <a href="http://www.bing.com">classic</a>
          <a href="http://www.bing.com">trend</a>
          <a href="http://www.bing.com">recommend</a>
        </div>
        <h4 style="text-align: center">欢迎观影</h4>
      </template>
    </Category>
  </div>
</template>

3 作用域插槽

  • 要使用的数据在被使用的组件内部
  • slot 绑定 :xxx=“xxx”,将数据传给使用者
  • 使用者中的具体结构要用 template 包裹,使用 scope=“xxx” 接收从被使用组件传来的数据,也可使用 slot-scope=“xxx”,接收到的 xxx 为一个对象,使用时 xxx.xxx
  • 也可给插槽命名,template 中写明用那个插槽

eg:使用被使用组件的数据

组件:Category.vue

<template>
  <div class="category">
    <h3>{{ title }}分类</h3>
    <slot :games="games"></slot>
  </div>
</template>

使用者:App.vue

<template>
  <div class="container">
    <Category title="游戏">
      <template scope="xhu">
        <ul>
          <li v-for="(g, index) in xhu.games" :key="index">
            {{ g }}
          </li>
        </ul>
      </template>
    </Category>
    <Category title="游戏">
      <template scope="xhu">
        <ol>
          <li v-for="(g, index) in xhu.games" :key="index">
            {{ g }}
          </li>
        </ol>
      </template>
    </Category>
    <Category title="游戏">
      <template slot-scope="xhu">
        <h4 v-for="(g, index) in xhu.games" :key="index">
          {{ g }}
        </h4>
      </template>
    </Category>
  </div>
</template>

VUEX

用于多组件共享同一状态(数据)

开始

vue2 只能用 vuex3

(1)在 src 中创建 store 文件夹,包含 index.js

(2)index.js 中创建 store

//该文件用于创建vuex核心的store

import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
Vue.use(Vuex)

//准备actions:用于响应组件中的动作
const actions={}
//准备mutations:用于操作数据(state)
const mutations={}
//准备state:用于存储数据
const state={}

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

(3)main.js 中导入并使用

...
import store from './store/index'


new Vue({
...
  store,
...
}).$mount('#app')

求和案例

效果如图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rGCeNsVI-1661758418341)(C:\Users\Gucci\AppData\Roaming\Typora\typora-user-images\image-20220317162958188.png)]

  • 组件模板中使用数据 {{$store.state.xxx}}
  • 组件脚本中
    • 需要有业务逻辑,则调用 actions 用 this.$store.dispatch(‘事件名(小写)’,参数)
    • 无业务逻辑,只需修改数据,则调用 mutations 用 this.$store.commit(‘事件名(大写)’,参数)
  • actions 相当于 Serve 层,处理业务逻辑、调用 Dao 层方法
    • 方法有 context、value 两个参数,context 中包含 commit、state 等需要用的,value 为组件传过来的参数
  • mutations 相当于 Dao 层,只包含数据修改方法
    • 方法有 state、value 两个参数,state 中为数据,value 为 actions 或组件调用传来的值

index.js

//准备actions:用于响应组件中的动作
//业务逻辑写在actions
const actions={
  jiaOdd(context,value){
    if(context.state.sum%2){
      context.commit('JIA',value)
    }
  },
  jiaTime(context,value){
    setTimeout(() => {
      context.commit('JIA',value)
    }, 500);
  },
}
//准备mutations:用于操作数据(state)
//mutations只进行数据操作
const mutations={
  JIA(state,value){
    state.sum+=value
  },
  JIAN(state,value){
    state.sum-=value
  }
}
//准备state:用于存储数据
const state={
  sum:0, //当前和
}

Count.vue

<template>
  <div>
    <h1>当前求和:{{ $store.state.sum }}</h1>
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementOdd">当前求和为奇数再加</button>
    <button @click="incrementWait">等一等再加</button>
  </div>
</template>
  methods: {
    increment() {
      this.$store.commit("JIA", this.n);
    },
    decrement() {
      this.$store.commit("JIAN", this.n);
    },
    incrementOdd() {
      this.$store.dispatch("jiaOdd", this.n);
    },
    incrementWait() {
      this.$store.dispatch("jiaTime", this.n);
    },
  },

1 getters

vuex 里面的 computed

(1)创建

//准备getters:用于将state的数据加工
const getters={
  bigSum(state){
    return state.sum*10
  }
}

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

(2)调用

$store.getters.bigSum

2 mapXXX

1 mapState 和 mapGetters

借助这两个生成计算属性,分别从 state / getters 中读取数据

(1)导入

import { mapState,mapGetters } from "vuex";

(2)使用

注:…obj 意为将 obj 内容展开放在这句话所在的位置

1)对象写法

computed: {
	...mapState({ sum: "sum", school: "school", subject: "subject" }),
	...mapGetters({bigSum:"bigSum"})
}

2)数组写法:计算属性名与 state / getters 数据名一致

computed: {
    ...mapState(["sum", "school", "subject"]),
    ...mapGetters(["bigSum"]),
}

2 mapMutations 和 mapActions

借助这两个生成方法,方法中调用 commit / dispatch 去联系 mutations / actions

(1)导入

import { mapMutations,mapActions } from "vuex";

(2)使用

在模板中绑定函数时,将参数传入

@click="increment(n)"

1)对象写法

methods: {
    ...mapMutations({increment:'JIA',decrement:'JIAN'}),
    ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaTime'})
},

2)数组写法:函数名和 mutations / actions 中函数名一致

methods: {
    ...mapMutations(['JIA','JIAN']),
    ...mapActions(['jiaOdd','jiaTime'])
},

3 模块化+命名空间

(1)模块化

  • 内部开启命名空间:
namespaced: true,
  • index.js 内导入并命名
...
import countOptions from './count.js'
import personOptions from './person.js'
...

export default new Vuex.Store({
  modules: {
    countAbout: countOptions,
    personAbout: personOptions
  }
})

count.js

export default {
  namespaced: true,
  actions: {
    jiaOdd(context, value) {
      if (context.state.sum % 2) {
        context.commit('JIA', value)
      }
    },
    jiaTime(context, value) {
      setTimeout(() => {
        context.commit('JIA', value)
      }, 500);
    },
  },
  mutations: {
    JIA(state, value) {
      state.sum += value
    },
    JIAN(state, value) {
      state.sum -= value
    },
  },
  state: {
    sum: 0,
    school: 'xhu',
    subject: 'web',
  },
  getters: {
    bigSum(state) {
      return state.sum * 10
    }
  }
}

person.js

import axios from "axios"
import { nanoid } from "nanoid"
export default {
  namespaced: true,
  actions: {
    addPersonWang(context, value) {
      if (value.name.indexOf('王') === 0) {
        context.commit('ADD_PERSON', value)
      } else
        alert("这人不姓王")
    },
    addPersonServer(context){
      axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
        response=>{
          context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
        },
        error=>{
          alert(error.message)
        }
      )
    }
  },
  mutations: {
    ADD_PERSON(state, value) {
      state.personList.unshift(value)
    }
  },
  state: {
    personList: [
      { id: '001', name: 'gucci' }
    ]
  },
  getters: {
    firstPersonName(state) {
      return state.personList[0].name
    }
  }
}

(2)组件中读取 state 数据

// 1 直接读取
this.$store.state.personAbout.personList
// 2 借助 mapState
...mapState("personAbout", ["personList"])

(3)组件中读取 getters 数据

// 1 直接读取
this.$store.getters["personAbout/firstPersonName"]
// 2 借助 mapGetters
...mapGetters("countAbout",["bigSum"])

(4)组件中调用 dispatch

// 1 直接dispatch
this.$store.dispatch('personAbout/addPersonWang',personObj)
// 2 借助 mapActions
...mapActions("countAbout",{ incrementOdd: "jiaOdd", incrementWait: "jiaTime" })

(5)组件中调用 commit

// 1 直接commit
this.$store.commit("personAbout/ADD_PERSON", personObj)
// 2 借助 mapMutations
...mapMutations("countAbout",{ increment: "JIA", decrement: "JIAN" })

VUE 路由

  1. 是一组 key-value 的对应关系
  2. 多个路由,需要经过路由器管理
  3. value 即为 component

单页 web 应用,只有一个完整的页面,点击连接不会刷新页面,只做局部更新,数据通过 ajax 请求

1 基本使用

  • 路由组件一般放在 pages 文件夹,一般组件放在 components 文件夹
  • 路由切换,是将隐藏的组件销毁,显示的组件挂载
  • 每个组件都有自己的 $route,整个应用只有一个 $router

(1)安装

vue2 必须安装 @3

npm i vue-router@3

(2)配置

src 中创建 router 文件夹,创建 index.js

为了方便写路由守卫,这里写完整,路由守卫写在路由器下面,最后暴露路由器

//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
import About from '../pages/About'
import Home from '../pages/Home'

//创建一个路由器
const router = new VueRouter({
  routes: [
    {
      path:'/about',
      component:About
      //写法二:在路由表中导入组件
      component:()=>import('../pages/About')
    },
    {
      path:'/home',
      component:Home
    },
  ]
})

//这里写全局路由守卫
router.beforeEach((to,from,next)=>{})

//最后暴露路由器
export default router;

(3)使用

main.js 中

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import router from './router/index'

Vue.config.productionTip = false
Vue.use(VueRouter)

new Vue({
  render: h => h(App),
  router:router
}).$mount('#app')

(4)路由切换

router-link 会自动被浏览器编译为超链接

<!-- 实现路由的切换 -->
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>

(5)组件显示

<!-- 指定组件呈现的位置 -->
<router-view></router-view>

2 嵌套路由

(1)配置

子路由前不写 “/”

routes: [
    {
      path:'/about',
      component:About
    },
    {
      path:'/home',
      component:Home,
      children:[
        {
          path:'news',
          component:News
        },
        {
          path:'message',
          component:Message
        },
      ]
    },
  ]

(2)跳转

写完整路径,将父组件路由地址都要写上

<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>

3 路由 query 参数

(1)传递参数

<!-- 跳转路由并携带query参数,to字符串写法 -->
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link>

<!-- 跳转路由并携带query参数,to对象写法 -->
<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

4 命名路由

(1)给路由命名:一般用给子路由

{
   path: '/home',
   component: Home,
   children: [
     {
        path: 'news',
        component: News
     },
     {
        path: 'message',
        component: Message,
        children: [
          {
            //============
            name:'xiangqing',
            //============
            path: 'detail',
            component: Detail
          }
        ]
      },
    ]
},

(2)简化跳转路径:path 用 name 代替

<router-link :to="{
          name:'xiangqing',
          query:{
            id:m.id,
            title:m.title
          }
        }">
          {{ m.title }}
        </router-link>

5 路由 params 参数

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

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

(2)传递参数

对象写法,路径只能用 name

<!-- 跳转路由并携带params参数,to字符串写法 -->
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{ m.title }}</router-link>

<!-- 跳转路由并携带params参数,to对象写法 -->
<router-link :to="{
   name:'xiangqing',
   params:{
     id:m.id,
     title:m.title
   }
}">
   {{ m.title }}
</router-link>

(3)接收参数

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

6 路由的 props 配置

让路由组件更方便收到数据

{
    name: 'xiangqing',
    path: 'detail',
    component: Detail,
    //写法一:值为对象,将对象中的key-value以props传给detail组件
    props:{a:1,b:'hello'}

    //写法二:值为布尔值,true:把该组件收到的params参数以props传给detail组件
    props: true

    //写法三:值为函数,$route为参数,params和query都可以传
    props($route){             
        return{
            id:$route.query.id,
            title:$route.query.title
        }
    }
}

7 replace 属性

  • 控制路由跳转时操作浏览器历史记录的模式
  • 浏览器历史记录写入有两种:push 和 replace,push 为追加(可返回、前进), replace 为替换(不可返回、前进)
  • 使用:
<router-link replace ...>About</router-link>

8 编程式路由导航

不借助 router-link 进行路由跳转

//跳转 API
this.$router.push({
    name: "xiangqing",
    query: {
    id: m.id,
    title: m.title,
    },
})
this.$router.replace({
    name: "xiangqing",
    query: {
    id: m.id,
    title: m.title,
	},
})
this.$router.forward() //相当于浏览器的前进
this.$router.back()    //相当于浏览器的后退
this.$router.go(number)//走多少步,+为前进,-为后退

9 缓存路由组件

  • 让不展示的路由组件保持挂载,不被销毁
  • 可以保留用户输入
  • include 中写要缓存的组件名
<keep-alive include="News">
  <router-view></router-view>
</keep-alive>
  • 缓存多个:绑定 include,写成数组
<keep-alive :include="['News','Message']">
...
</keep-alive>

10 两个生命周期

  • 路由组件独有,用于捕获路由组件的激活状态
activated(){console.log("组件被激活");},
deactivated(){console.log("组件失活");}

11 路由守卫

  • 对路由进行权限控制
  • 种类:全局、独享、组件内

1 全局守卫

  • 为路由添加元数据用 meta 属性:meta:{key:value}
  • to 为目标组件的信息,from 为来自组件的信息
  • next() 为放行,允许路由组件跳转
//全局前置路由守卫:初始化、每次路由切换之前被调用
router.beforeEach((to, from, next) => {
  if (to.meta.isAuth) {
    if (localStorage.getItem('school') === 'xhu') {
      next()
    } else {
      console.log('school name is not right');
    }
  } else {
    next()
  }
})

//后置路由守卫:初始化、每次路由切换之后被调用
router.afterEach((to, from) => {
  document.title = to.meta.title || 'xhu'
})

2 独享路由守卫

  • 写在路由配置中,与其他配置属性同级
  • 函数前面有个 " : "
  • 只有前置,可配合全局后置使用
{
	...
  beforeEnter: (to, from, next) => {
    if (to.meta.isAuth) {
      if (localStorage.getItem('school') === 'xhu') {
        next()
      } else {
        alert('school name is not right');
      }
    } else {
      next()
    }
  }
},

3 组件内

  • 离开不是后置守卫
<script>
export default {
  name: "About",

  //通过路由规则,进入该组件时调用
  beforeRouteEnter(to, from, next) {
    if (to.meta.isAuth) {
      if (localStorage.getItem("school") === "xhu") {
        next();
      } else {
        alert("school name is not right");
      }
    } else {
      next();
    }
  },

  //通过路由规则,离开该组件时调用
  beforeRouteLeave(to, from, next) {
    next();
  },
};
</script>

12 路由器的两种工作模式

  1. hash 模式:
    1. 地址中带 #
    2. 兼容性好
    3. 若将地址通过第三方手机app分享,若校验严格,地址会标记为不合法
  2. history 模式:
    1. 地址干净
    2. 兼容性略差,需要和后端配合识别 url,解决 404 问题
  3. 对于 url,# 及其后面的内容就是 hash 值
  4. hash 值不会包含在 HTTP 请求中,不会带给服务器

VUE UI 组件库

PC 端常用 UI 组件库

Element UI https://element.eleme.cn

全部引入会导致 js 文件过大,一般使用按需引入,详见官网

注:

修改 babel.config.js

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

1 表单验证

详见官网

  1. 表单标签绑定 rules 属性
  2. 表单 ref 属性和 :model 属性都写表单名
  3. data 编写 rules 规则
  4. 待验证的输入框 prop 接受 rules 中对应的规则
  5. 提交按钮绑定函数并传入表单名
  6. 编写提交函数(官网复制)
<el-form
      :rules="rules"
      ref="loginForm"
      :model="loginForm"
    >
      <el-form-item prop="username">
        <el-input></el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input></el-input>
      </el-form-item>
      <el-form-item prop="code">
        <el-input></el-input>
      </el-form-item>
      <el-button @click="submitLogin('loginForm')">登录</el-button
      >
</el-form>
rules: {
        username: [
          { required: true, message: "请输入用户名", trigger: "blur" },
        ],
        password: [{ required: true, message: "请输入密码", trigger: "blur" }],
        code: [{ required: true, message: "请输入验证码", trigger: "blur" }],
      },
    submitLogin(formName) {
      this.$refs[formName].validate((valid) => {
          if (valid) {
            alert('submit!');
          } else {
            this.$message.error('请输入所有字段');
            return false;
          }
        });
    },

面试题

  1. Vue 不支持 jsx

  2. 样式穿透可用 >>>,::v-deep,/deep/

  3. vue-lazyload 懒加载:延时加载,在需要用的时候再加载。一般用于 img

    1. 先安装 npm i vue-lazyload

    2. 全局注册

      main.js 文件
      import VueLazyload from 'vue-lazyload'
      Vue.use(VueLazyload)
      // 配置项
      Vue.use(VueLazyload, {
        preLoad: 1.3,
        error: 'dist/error.png',
        loading: 'dist/loading.gif',
        attempt: 1
      })
      
    3. 使用 v-lazy 代替 :src

    4. 属性 :key 可以不加,但是不加可能以为 key 值相同而图片不刷新

  4. 数组直接通过索引修改属性值,不能触发 watch 方法

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值