Vue.js的基本使用和相关的介绍

本文介绍Vue.js的基本概念,包括MVVM模式、数据绑定、指令使用、计算属性、过滤器及组件化开发等内容,帮助读者快速掌握Vue.js的核心特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

vue.js

vue简介

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的JavaScript框架。封装了原生的DOM操作,无需进行任何手动的DOM编码即可完成页面数据的渲染。

MVVM模式

前端页面开发需要解决的核心问题是:数据(模型)和页面(视图)的绑定问题。如何将数据展示到页面中,以及当页面的数据发生变化时,如何同步变化到数据?

传统的DOM操作:

手动编码将Model中的数据绑定到View上; //dom对象.属性=数据
当View的发生变化时,也需要手动编码获取变化。//数据 = dom对象.属性

存在着如下的问题:

  • 开发者需要手动编码DOM操作,开发繁琐
  • 当Model频繁发生变化时,开发者需要手动编码更新到View中;当View发生变化时,也需要同步到Model中。这样不仅开发繁琐,而且很难维护复杂多变的数据状态。

MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,其核心是通过 ViewModel 把Model和View关联起来。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model,即所谓的数据双向绑定
在这里插入图片描述

  • M:即Model,模型,主要指的就是要展示在页面的数据
  • V:即View,视图,负责页面效果展示,就是html+css
  • VM:即View-Model,模型与视图间的双向操作(通常由框架负责实现)

MVVM中的VM要做的事情就是把DOM操作完全封装起来,开发人员不用再关心Model和View之间是如何互相影响,把开发者从繁琐的DOM操作中解放出来,把关注点放在如何操作Model上。

Vue.js 是一个提供了 MVVM 风格的双向数据绑定的 Javascript 框架,使得我们无需关注底层DOM操作,专注于业务数据操作,提高页面开发效率。

第1个Vue示例

  • 新建页面,并引入vue.js

<script src="js/vue-2.6.12.js"></script>

  • 定义一个视图标签
<body>
    <div id="app"></div>
</body>
  • 创建一个Vue实例(ViewModel)
<body>
    <!--{{}}插值表达式,可以将vm对象的数据插入到视图中 -->
    <div id="app"><h1>{{msg}}</h1></div>
    <script>
        const vm = new Vue({
            el:"#app",//通过选择器,选则要挂载的标签
            data:{//数据
                msg:"hello "
            }
        })
    </script>
</body> 
  • 总结
  1. 页面要有一个用于挂载数据的标签
  2. 创建Vue实例(ViewModel对象)
  3. el:通过选择器选择标签
  4. data:定义可以渲染到视图的数据

Vue框架要解决的核心问题

Vue作为构建页面的JavaScript框架,简化了页面操作的方式,但其和原生JS在操作页面时要解决的问题是相同的。原生JS操作页面需要解决的问题,也必是Vue需要解决的,而学习Vue其实就是在学习它对这些问题的解决方法。

操作页面需要解决的问题:

  • 渲染数据到页面
  • 为标签属性赋值
  • 事件绑定

当然Vue在解决这些基本问题的同时,也提供了更多的功能,简化开发。下面就让我们开始学习Vue的具体语法。

渲染数据到页面

数据绑定最常见的形式就是使用 {{值}}(双大括号)的文本插值:

<div id="app">
    <h1>{{msg}}</h1>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg:"hello vue"
        }
    })
</script>

说明:**{{}}**中通常是变量,但也可以是表达式(比如 a+b)、有返回值的函数调用。

v-text和v-html指令

v-text

如果数据不是预先定义好,而是通过网络获取时,使用{{}}方式在网速较慢时会出现问题。在数据未加载完成时,页面会显示出原始的 {{}} ,加载完毕后才显示正确数据,称为插值闪烁。此时,可以使用v-text代替插值表达式:

<div id="app">
    <h1 v-text="msg"></h1>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg:"hello vue"
        }
    })
</script>

v-html指令

如果数据中包含有HTML标签,双大括号和 v-text指令 会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令

<div id="app">
    <h1>{{msg}}</h1>
    <h1 v-text="msg"></h1>
    <h1 v-html="msg"></h1>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg:"<span style='color:red'>hello vue</span>"
        }
    })
</script>

v-if和v-show指令

v-if指令
v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true 值的时候被渲染。

<div id="app">
    <h1 v-if="show">
        <span style="color:green">show=true</span>
    </h1>
    <h1 v-if="!show">
        <span style="color:red">show=false</span>
    </h1>
    <button onclick="handleClick()">点我</button>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            show:true
        }
    })

    function handleClick(){
        vm.show = !vm.show;
    }
</script>

v-if后还可以添加v-else 指令来表示 v-if 的“else 块”,上面的示例还可以写出如下代码

<div id="app">
    <h1 v-if="show">
        <span style="color:green">show=true</span>
    </h1>
    <h1 v-else>
        <span style="color:red">show=false</span>
    </h1>
    <button onclick="handleClick()">点我</button>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            show:true
        }
    })

    function handleClick(){
        vm.show = !vm.show;
    }
</script>

v-show指令
另一个用于根据条件展示元素的指令是 v-show 指令。和v-if用法大致一样,不过v-show后不能跟v-else

<div id="app">
    <h1 v-show="show">
        <span style="color:green">show=true</span>
    </h1>
    <h1 v-show="!show">
        <span style="color:red">show=false</span>
    </h1>
    <button onclick="handleClick()">点我</button>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            show:true
        }
    })

    function handleClick(){
        vm.show = !vm.show;
    }
</script>

v-if 和 v-show 的区别

<div id="app">
    <h1 v-if="show">
        <span style="color:green">v-if指令</span>
    </h1>
    <h1 v-show="show">
        <span style="color:green">v-show指令</span>
    </h1>
    <button onclick="handleClick()">点我</button>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            show:true
        }
    })

    function handleClick(){
        vm.show = !vm.show;
    }
</script>

v-if 和 v-show 指令展示效果相同,但是打开开发者工具(F12或者ctrl+shift+i)查看Element面板,会发现2者的区别

  • v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的子组件适当地被销毁和重建。当初始条件为false时,其内部组件不会渲染。
  • v-show 就简单得多,它在切换过程中,只是简单地基于 CSS 进行切换。当初始条件为false时,其内部组件也会预先渲染。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

v-for指令

遍历数据渲染页面是非常常用的需求,Vue中通过v-for指令来实现。

遍历数组
v-for="(item,index) in items"
  • items:要遍历的数组,需要在vue的data中定义好。
  • item:数组元素
  • index:元素下标,可以省略
    示例:
<div id="app">
    <ul>
        <li v-for="(item,index) in favorites">{{index+ ":"+item}}</li>
    </ul>
</div>
<script>
    var vm = new Vue({
        el:"#app",
        data:{
            favorites:['唱','跳','Rap','打篮球']
        }
    });
</script>

再看一个对象数组的示例:

<div id="app">
    <table>
        <thead>
            <tr>
                <th>name</th>
                <th>age</th>
                <th>gender</th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="u in users">
                <td>{{u.name}}</td>
                <td>{{u.age}}</td>
                <td>{{u.gender}}</td>
            </tr>
        </tbody>
    </table>
    <button onclick="addPerson()">点我</button>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            users: [
                {"name": "小明", "age": 13, "gender": "男"},
                {"name": "小红", "age": 13, "gender": "女"},
                {"name": "小绿", "age": 4, "gender": "男"}
            ]
        }
    });
    
    function addPerson(){
        vm.users.push({"name": "小翠", "age": 14, "gender": "女"})
    }
</script>
遍历对象

v-for`除了可以迭代数组,也可以迭代对象。语法基本类似

语法:

v-for="value in object"
v-for="(value,name) in object"
v-for="(value,name,index) in object"
  • 1个参数时,得到的是对象的属性值
  • 2个参数时,第一个是属性值,第二个是属性名
  • 3个参数时,第三个是索引,从0开始

示例:

<div id="app">
    <ul>
        <li v-for="(value,name,index) in person">{{index}} - {{name}}={{value}}</li>
    </ul>
    <button onclick="changePerson()">点我</button>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            person:{
                "name": "zhangsan", 
                "age": 13, 
                "gender": "男", 
                "address": "中国"
            }
        }
    })

    function changePerson(){
        vm.person = {
                "name": "lisi", 
                "age": 16, 
                "gender": "女", 
                "address": "中国"
            }
    }
</script>

注意:由于 v-for优先级比 v-if 高,用在同1个元素上,一定会先执行数组遍历,然后再执行判断,出于性能的考虑,官方并不建议我们连用。

事件绑定:v-on指令

基本使用

在Vue中通过v-on指令给页面元素绑定事件。

语法:

v-on:事件名="js代码片段或函数" //事件名=事件属性-on  比如:click=onclick-on

//函数必须定义在Vue示例的methods属性中。
const vm = new Vue({
    el:"选择器",
  	methods:{
        //定义事件处理函数
        函数名:function(){
            //函数实现
        }
    }  
})

在这里插入图片描述
示例:

<div id="app">
    <h1 v-if="show">
        <span style="color:green">show=true</span>
    </h1>
    <h1 v-else>
        <span style="color:red">show=false</span>
    </h1>
    <!-- 直接使用函数名即可 -->
    <button v-on:click="handleClick">点我</button>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            show:true
        },
        methods:{//注意事件处理函数必须定义在methods中
            handleClick:function(){
                this.show=!this.show;//data中的属性,在函数中可以直接this.访问
            }
        }
    })
</script>

事件绑定还有简写语法:

@事件名="js代码片段或函数"

const vm = new Vue({
    el:"选择器",
    methods:{
        //定义事件处理函数:直接定义函数,甚至无需function关键字
        函数名(){
            ...
        }
    }
})

再看一个案例:

<div id="app">
    <h2>num={{num}}</h2>
    <!-- 事件处理函数代码简单时,可以直接在事件绑定处写代码 -->
    <button @click="num++">自增</button>
    <button @click="decrement">自减</button>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            num:1
        },
        methods:{
            /*
         
            increment:function(){
                this.num--;
            }*/
            //methods定义函数还可以有如下简写形式 函数名(){}
           
            increment(){
                this.num--;
            }
        }
    })
</script>
事件修饰符

在事件处理函数中调用 event.preventDefault() 禁用标签的默认行为或 event.stopPropagation() 停止事件冒泡是非常常见的需求。

<div id="app">
    <a href="https://www.baidu.com" @click="handleClick">点我</a>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        methods:{
            handleClick(event){
                alert("被单击了");
                event.preventDefault();//禁用超链接的默认点击跳转
            }
        }
    })
</script>

尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

  • .stop :阻止事件冒泡
  • .prevent :阻止默认事件发生,比如超链接的默认跳转、表单的提交(重点)
  • .self :只有元素自身触发事件才执行。(子元素的事件冒泡来的不执行)
  • .once :只执行一次
<div id="app">
    <a href="https://www.baidu.com" @click.prevent="handleClick">点我</a>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        methods:{
            handleClick(event){
                alert("被单击了");
                // event.preventDefault();
            }
        }
    })
</script>

标签属性的赋值:v-bind指令

基本使用

有时,我们需要动态的修改标签的属性值,看如下需求:动态修改div的class属性值

<head>
    <style type="text/css">
        .red_bg{
            background-color: red;
        }
        .green_bg{
            background-color: green;
        }
    </style>
</head>
<body>
	<div id="app">
        <!-- 这里意图通过插值表达式动态获取class_name的值,但是失败了 -->
        <div class="{{class_name}}">点击按钮改变背景样式:{{class_name}}</div>
        <button @click="class_name='red_bg'">红色</button>
        <button @click="class_name='green_bg'">绿色</button>
    </div>
    <script>
        const vm = new Vue({
            el:"#app",
            data:{
                class_name:""
            }
        })
    </script>
</body>

无法直接在标签的属性中使用插值表达式动态的设置标签的属性值,这时候就需要使用v-bind指令。语法如下:

v-bind:属性名="Vue中的变量"

例如,在这里我们可以写成:

<div v-bind:class="class_name"></div>

不过,v-bind太麻烦,因此可以省略,直接写成::属性名="属性值" ,即:

<div :class="color"></div>
完整示例
<head>
    <style type="text/css">
        .red_bg{
            background-color: red;
        }
        .green_bg{
            background-color: green;
        }
    </style>
</head>
<body>
    
     <div id="app">
         <div class="{{class_name}}">点击按钮改变背景样式:{{class_name}}</div>
         <div v-bind:class="class_name">点击按钮改变背景样式:{{class_name}}</div>
         <div :class="class_name">点击按钮改变背景样式:{{class_name}}</div>
         <button @click="class_name='red_bg'">红色</button>
         <button @click="class_name='green_bg'">绿色</button>
    </div>
    <script>
        const vm = new Vue({
            el:"#app",
            data:{
                class_name:""
            }
        })
    </script>
</body>

绑定class属性的对象语法

我们可以传给在这里插入代码片 v-bind:class 一个对象,以动态地切换 class:

<div id="app">
   
    <div v-bind:class="{red_bg:showRed,green_bg:!showRed}">点击按钮改变背景样式</div>
    <button @click="showRed=true">红色</button>
    <button @click="shoRed=false">绿色</button>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            showRed:true
        }
    })
</script>
v-for时的key属性

当我们需要更新 v-for 渲染过的元素列表时,比如向数组中添加一个新的元素。为了保证Vue正确并高效的渲染页面,官方建议我们为每项提供一个唯一 key 属性,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素。key一般使用在遍历完后,又增、减数组的元素的时候更有意义。

示例:

<ul>
	<li v-for="(item,index) in items" :key="index"></li>
</ul>

在这里插入图片描述

示例:
在这里插入图片描述

  • 这里使用了属性赋值语法: :key="index"为每一个生成的元素设置一个唯一的key属性
  • 这里绑定的key是数组的索引,应该是唯一的
表单的双向数据绑定:v-model指令

之前我们演示的数据绑定都是单向绑定,数据影响了视图渲染,但是反过来就不行(视图的变化不会影响模型数据)。如下例所示:

<div id="app">
    <input type="text" :value="name"> <br>
    <button @click="setName">点我设置value</button>
    <button @click="getName">点我获取value</button>

</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            name:""
        },
        methods:{
            setName(){
                this.name="xiaohei";
            },
            getName(){
                alert(this.name);
            }
        }
    })
</script>

而对于表单中的控件而言,我们需要数据的绑定是双向的,即:模型数据的变化会影响视图,同时视图发生变化也会同步到模型数据。

接下来学习的v-model是双向绑定,视图(View)和模型(Model)之间会互相影响。既然是双向绑定,一定是在视图中可以修改数据的组件,这样就限定了视图的元素类型。

目前v-model的可使用元素有:

  • input
  • radio
  • checkbox
  • select
  • textarea
  • components(Vue中的自定义组件)

基本上除了最后一项,其它都是表单的输入项。

首先我们先看一个简单的示例:

<div id="app">
    <input type="text" v-model="name"> <br>
    <button @click="setName">点我设置value</button>
    <button @click="getName">点我获取value</button>

</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            name:""
        },
        methods:{
            setName(){
                this.name="xiaohei";
            },
            getName(){
                alert(this.name);
            }
        }
    })
</script>

接下来,我们演示一个完整的示例:

<div id="app">
    <form action="">
        姓名: <input type="text" name="name" v-model="person.name"> <br>
        性别: <input type="radio" name="sex" value="1" v-model="person.sex"><input type="radio" name="sex" value="0" v-model="person.sex"><br>
        爱好:<input type="checkbox" name="favorites" value="0" v-model="person.favorites"><input type="checkbox" name="favorites" value="1" v-model="person.favorites"><input type="checkbox" name="favorites" value="2" v-model="person.favorites"> Rap
        <input type="checkbox" name="favorites" value="3" v-model="person.favorites"> 打篮球 <br>
        学历:<select name="education" v-model="person.education">
        <option value="0">小学</option>
        <option value="1">中学</option>
        <option value="2">大学</option>
        <option value="3">Java练习生</option> 
        </select> <br>
        个性签名: <textarea name="signature" v-model="person.signature" cols="30" rows="10"></textarea> <br>
        <input type="submit" value="提交">
    </form>
    <hr>
    <button @click="updatePerson">点我修改person改变表单</button>
    <button @click="getPerson">点我获取表单同步后的person</button>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            person:{}
        },
        methods:{
            updatePerson(){
                this.person = {
                    name:"xiaohei",
                    sex:1,
                    favorites:[0,1,2,3],
                    education:3,
                    signature:"小黑不是一般的黑"
                };
            },
            getPerson(){
                console.log(this.person);
            }
        }
    })
</script>
  • inputtextarea 默认对应是字符串
  • radio对应的input的value值是预先定义好的,model中对应的值匹配哪个就选中哪个
  • 多个 checkbox 对应的类型是一个数组
  • select 根据option子元素的value属性进行匹配选择
实战案例:todo-list

在这里插入图片描述

<div id="app">
        <form action="" @submit.prevent="addNewTodo">
            Add a todo
            <input type="text" v-model="newTodoText">
            <button type="submit">Add</button>
        </form>

         <ul v-if="todos.length > 0">
            <li v-for="(todo,index) in todos">{{todo}} <button @click="removeTodo(index)">Remove</button></li>
        </ul>
        <h4 v-else>
            当前没有待办事项
        </h4>
    </div>

    <script>
        const vm = new Vue({
            el:"#app",
            data:{
                newTodoText:"",
                todos:[]
            },
            methods:{
                addNewTodo(){
                    if(this.newTodoText.trim().length > 0){
                        this.todos.push(this.newTodoText);
                        this.newTodoText="";
                    }
                },
                removeTodo(index){
                    //从指定下标开始,删除1个元素
                    this.todos.splice(index,1);
                }
            }
        })
    </script>

计算属性和过滤器

计算属性

在插值表达式中可以直接使用js表达式,这是非常方便的。但是如果表达式的内容很复杂,就会影响代码可读性,而且后期维护起来也不方便,例如下面的场景:需要统计多个商品的总价。

<div id="app">
    <h2>总价:{{products.reduce((total,p)=>{return total+p.price*p.count;},0)}}</h2>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            products:[
                {
                    name: 'iPhone 7',
                    price: 7199,
                    count: 2
                },
                {
                    name: 'iPad',
                    price: 2888,
                    count: 1
                }
            ]
        }
    })
</script>

Vue中提供了计算属性,来替代复杂的表达式。通过给Vue实例添加选项computed来定义计算属性:

<div id="app">
    <h2>总价:{{products.reduce((total,p)=>{return total+p.price*p.count;},0)}}</h2>
    <h2>总价:{{totalPrice}}</h2>
</div>
<script>
    const vm = new Vue({
        el:"#app",
        data:{
            products:[
                {
                    name: 'iPhone 7',
                    price: 7199,
                    count: 2
                },
                {
                    name: 'iPad',
                    price: 2888,
                    count: 1
                }
            ]
        },
        computed:{
            totalPrice(){
                return this.products.reduce((total,p)=>{return total+p.price*p.count;},0);
            }
        }
    })
</script>

计算属性本质就是方法,但是一定要返回数据。然后页面渲染时,可以把这个方法当成一个变量来使用。

过滤器

过滤器的作用是对数据进行格式化,比如字母全部大写、日期格式化输出、货币前添加货币符号等场景。Vue.js支持在{{ }}插值的尾部添加一个管道符“(|)”对数据进行过滤。过滤的规则,通过给Vue实例添加选项filters来设置。

<div id="app">
    	<!-- 过滤器只有1个参数时,过滤器不需要写出(),默认传入|前的数据 -->
        <h2>使用过滤器字母全大写:{{str | toUpperCase}}</h2>
        <h2>使用过滤器日期格式化:{{date | dateFormat}}</h2>
    	<!-- 过滤器有多个参数时,第1个参数仍然是|前的数据,后续的参数需要显式给出 -->
        <h2>货币格式化输出:{{money | moneyFormat("¥")}}</h2>
    </div>

    <script>
        const vm = new Vue({
            el:"#app",
            data:{
                str:"hello filter",
                date:new Date(),
                money:1000.0
            },
            filters:{
                toUpperCase(value){
                    return value.toUpperCase();
                },
                dateFormat(value){
					let year = value.getFullYear();
					let month =value.getMonth()+1;
					let day = value.getDate();
					let hours = value.getHours();
	                let minutes = value.getMinutes();
	                let seconds = value.getSeconds();
	                return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
               }dateFormat(value){
                    let year = value.getFullYear();
                    let month =value.getMonth()+1;
                    let day = value.getDate();
                    let hours = value.getHours();
                    let minutes = value.getMinutes();
                    let seconds = value.getSeconds();
                    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
               }moneyFormat(value,symbol){
                    return symbol+" "+value;
                }
            }
        })
    </script>

说明:还可以将过滤器定义为全局过滤器。

Vue.filter('过滤器名称', function (value[,param1,...] ) {  
//逻辑代码
})

示例:(将上面的进行修改)

<html>
	<head>
		<meta charset="utf-8"/>
		<script type="text/javascript" src="js/vue-2.6.12.js"></script>
	</head>
		<body>	
			<div id= "app">
				<h1>{{str | toUpper}}</h1>
				<h2>{{date | dateFormt}}</h1>
				<h1>{{money | moneyFormt("¥")}}</h1>
			</div>
			<script>
				Vue.filter("toUpper",function(value){
				
					return value.toUpperCase();
				});
				
				Vue.filter("moneyFormt",function(value,sign){
				
					return `${sign} ${value}`;
				})
				Vue.filter("dateFormt",function(value){
				
					let year = value.getFullYear();
                    let month =value.getMonth()+1;
                    let day = value.getDate();
                    let hours = value.getHours();
                    let minutes = value.getMinutes();
                    let seconds = value.getSeconds();
                    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
				});
				const vm = new Vue({
					el:"#app",
				
					data:{
						str:"baizhi",
						date:new Date(),
						money:10000.0
						}
				});
			</script>
		</body>
</html>
计算属性和过滤器的异同

相同点:

  • 都基于原始属性值进行变换
  • 都可以在{{}}中调用

不同点:

  • 计算属性可以基于多个原始属性,而过滤器只对一个原始属性操作
  • 计算属性通常用于复杂的逻辑计算,而过滤器用于简单的数据格式化输出

实战案例:购物车

在这里插入图片描述
示例代码:

<body>
    <div id="app">
        <div style="text-align: center;">
            <button @click="addFormShow = true;">添加</button>
        </div>
        <hr>
        <div v-if="shoppingList.length > 0">
            <h2 style="text-align:center">购物列表</h2>
            <table align="center" border="1">
                <thead>
                    <tr>
                        <th>编号</th>
                        <th>商品</th>
                        <th>价格</th>
                        <th>数量</th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="(item,index) in shoppingList">
                        <td>{{index+1}}</td>
                        <td>{{item.name}}</td>
                        <td>{{item.price}}</td>
                        <td>
                            <button @click="increment(index)">+</button>
                            <span>{{item.count}}</span>
                            <button @click="decrement(index)">-</button>
                        </td>  
                    </tr>
                </tbody>
            </table>
            <h2 style="text-align: center;">总价:{{totalPrice | priceFormat}}</h2>
        </div>
        <div v-else>
            <h2 style="text-align: center;">购物车空空如也</h2>
        </div>

        <hr>
        <div style="text-align: center;" v-show="addFormShow">
            <form action="" @submit.prevent="addItem">
                商品: <input type="text" v-model="shoppingItem.name"> <br>
                价格:<input type="number" v-model="shoppingItem.price"> <br>
                数量:<input type="number" v-model="shoppingItem.count"> <br>
                <input type="submit" value="添加">
            </form>
        </div>
    </div>

    <script>
        const vm = new Vue({
            el:"#app",
            data:{
                addFormShow:false,
                shoppingItem:{},
                shoppingList:[
                    {
                        name:"macbookair",
                        price:7999.0,
                        count:1
                    }
                ]
            },
            methods:{
                increment(i){
                    this.shoppingList[i].count++;
                },
                decrement(i){
                    if(this.shoppingList[i].count <= 1){
                        this.shoppingList.splice(i,1);
                    }else{
                        this.shoppingList[i].count--;
                    }
                },
                addItem(){
                    let item = JSON.parse(JSON.stringify(this.shoppingItem));
                    this.shoppingList.push(item);
                    this.shoppingItem={};
                }
            },
            computed:{
                totalPrice(){
                    return this.shoppingList.reduce((total,item)=>total+item.price*item.count,0);
                }
            },
            filters:{
                priceFormat(value){
                    return "¥ "+value;
                }
            }

        })
    </script>
</body>

Vue的生命周期

每个 Vue 实例在被创建时都要经过一系列的初始化过程 :创建实例,装载模板,渲染模板、销毁等。Vue为生命周期中的每个状态都设置了钩子函数(监听函数)。当Vue实例处于不同的生命周期时,对应的函数就会被触发调用。

生命周期图示

下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。
在这里插入图片描述

钩子函数

每个 Vue 实例在被创建时都要经过一系列的初始化过程。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这些函数在生命周期的不同阶段自动触发执行,这给了用户在不同阶段添加自己的代码的机会。

生命周期钩子含义
beforeCreate(vue对象创建前)组件实例刚被创建,组件属性计算之前,此时不能访问属性
created(创建后)组件实例创建完,属性可以访问,但是还不能通过 $el 访问DOM
beforeMount(加载前)模板编译、挂载之前,可以通过 $el 访问渲染前的DOM
mounted(载入后)模板编译、挂载之后,可以通过 $el访问渲染前的DOM
beforeUpdate(更新前)组件更新之前,可以获取组件更新前的DOM
updated(更新后)组件更新之后,可以获取组件更新后的DOM
beforeDestroy(销毁前)组件销毁前调用
destroyed(销毁后)组件销毁后调用

通过以下案例,演示下各个钩子函数的使用:

<div id="app">
    <h1>{{msg}}</h1>
    <button @click="changeMsg">点我修改msg</button>
</div>

<script>
    const vm = new Vue({
        el:"#app",
        data:{
            msg:"hell vue"
        },
        methods:{
            changeMsg(){
                this.msg = "hello baizhi";
            }
        },
        beforeCreate:function(){
            // alert("beforeCreate...");
            console.log("beforeCreate...");
            console.log(this.$el);//undefined
            console.log(this.msg);//undefined
        },
        created:function(){
            // alert("created...");
            console.log("created...");
            console.log(this.$el);//undefined
            console.log(this.msg);//hello vue
        },
        beforeMount:function(){
            // alert("beforeMount...");
            console.log("beforeMount...");
            console.log(this.$el);//加载前的标签,就是原始代码,插值表达式、事件绑定都还没解析
            console.log(this.msg);//hello vue
        },
        mounted:function(){
            // alert("mounted...");
            console.log("mounted...");
            console.log(this.$el);//加载后的标签,插值表达式、事件绑定均已解析
            console.log(this.msg);//hello vue
        },
        beforeUpdate:function(){
            // alert("beforeUpdated...");
            console.log("beforeUpdated...");
            console.log(this.$el.innerHTML);//更新前的DOM
            console.log(this.msg);//hello baizhi
        },
        updated:function(){
            // alert("updated...");
            console.log("updated...");
            console.log(this.$el.innerHTML);//更新后的DOM
            console.log(this.msg);//hello baizhi
        },
        beforeDestroy:function(){//在console中执行vm.$destroy()触发,开发时很少关注
            // alert("destroyed...");
            console.log("beforeDestroy...");
            console.log(this.$el);
            console.log(this.msg);
        },
        destroyed:function(){
            // alert("destroyed...");
            console.log("destroyed...");
            console.log(this.$el);
            console.log(this.msg);
        }
    })
</script>

说明:一般地,我们会在 created 钩子函数中,从服务端获取数据,并对数据进行初始化。
一般用来发起ajax请求

组件基础

在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。

但是如果每个页面都独自开发,这无疑增加了我们开发的成本。所以为了提高代码复用,我们会把页面的可复用部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。

第1个组件案例

有如下需求:在页面上要定义多个记录点击次数的按钮,我们使用之前的所学,可以这么做:

<div id="app">
    <button @click="increment1">你点了我{{count1}}次,我记住了</button>
    <button @click="increment2">你点了我{{count2}}次,我记住了</button>
</div>

<script >
    const vm = new Vue({
        el:"#app",
        data:{
            count1:0,
            count2:0
        },
        methods:{
            increment1(){
                this.count1++;
            },
            increment2(){
                this.count2++;
            }
        }
    })
</script>

注意:不同的按钮,必须操作不同的属性,如果使用同1个属性,多个按钮的点击次数会累加到一起。如果按钮的数量要求更多,按照这种方式,无法代码复用,影响开发效率而且代码也不易维护。

解决方案:可以将需要复用的代码定义成组件,通过组件进行复用。

  1. 注册组件
<script >
     Vue.component("button-counter",{
         template:"<button @click='increment'>你点了我{{count}}次,我记住了</button>",
         data:function(){
             return {
                 count:0
             }
         },
         methods:{
             increment(){
                 this.count++;
             }
         }
     })
</script>
  • 通过component方法注册组件
    • 第1个参数表示组件名
    • 第2个参数表示组件的组成,组件本质上也是一个Vue实例,也就可以定义:datamethods等等
    • 组件在定义时,不会和任何页面元素绑定,因此没有el属性。但多了template属性抽取html片段
    • data必须是一个函数,后面解释
  1. 使用组件
<div id="app">
    <button-counter></button-counter>
    <button-counter></button-counter>
</div>
  • 把组件名当作标签直接使用就可以

3.创建vue实例

<div id="app">
    <!-- 2 使用组件 -->
    <button-counter></button-counter>
    <button-counter></button-counter>    
</div>

<script >
    //1 注册组件
    Vue.component("button-counter",{
        template:"<button @click='increment'>你点了我{{count}}次,我记住了</button>",
        data:function(){
            return {
                count:0
            }
        },
        methods:{
            increment(){
                this.count++;
            }
        }
    })
    //3 创建vue实例
    const vm = new Vue({
        el:"#app"
    })
</script>
  • 因为组件会被多次使用,所以定义组件时,语法上要求data必须是一个函数,每次使用时返回一个新的对象。
  • 组件要在Vue实例中使用,所以最后一定要创建Vue实例。

还有2点需要注意:

  • 组件名如果由多个单词组成,按照规范多个单词全小写,并使用-连接,比如 button-counter。如果使用驼峰命令法,比如 buttonCounter,那么在使用组件时,也需要在单词间添加 -
  • 组件中 template只能有一个根标签,比如实例中只有一个div根标签。如果又有一个同级的div就会报错。
template:"<div><button>按钮</button><br></div>"                  //对
template:"<button @click='increment'>你点了我{{count}}次,我记住了</button>"    //对
template:"<div><button>按钮<button></div><div></div>"           //错误,2个div根标签
组件的注册方式

组件的注册方式有2种:全局注册局部注册

全局注册

全局注册的组件,可以在不同的Vue实例中使用。语法如下:

<script>
	Vue.component("组件名",{
        template:"复用的html片段",
        data:function(){
            return {...}//return的对象,类似于创建Vue实例时的data
        },
        methods:{
            //和定义Vue实例时一样,用于定义函数
        }
    })
</script>

第1个案例就是全局注册,这里不再演示。

局部注册

一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着Vue的加载而加载。因此,对于一些并不频繁使用的组件,我们会采用局部注册。在Vue实例中添加选项 components 语法如下:

<script>
	const vm = new Vue({
        el:"选择器",
        data:{
            //属性
        },
        components:{
            //注册局部组件
            "组件名":{
                template:"复用的html片段",
                data:function(){
                    return {...}//return的对象,类似于创建Vue实例时的data
                },
                methods:{
                    //和定义Vue实例时一样,用于定义函数
                }
            }
        }
    });
</script>

示例:

<div id="app">
    <button-counter></button-counter>
    <button-counter></button-counter>    
</div>
<script>
    const vm = new Vue({
        el:"#app",
        components:{
            "button-counter":{
                template:"<button @click='increment'>你点了我{{count}}次,我记住了</button>",
                data:function(){
                    return {
                        count:0
                    }
                },
                methods:{
                    increment(){
                        this.count++;
                    }
                }
            }
        }
    })

</script>
注册优化

优化1:将组件参数单独抽取成变量

当Vue实例中注册的组件比较多时,Vue实例的代码就会变得非常臃肿,不利于代码管理,此时可以将Vue实例参数抽取成变量。

<script>
    const buttonCounter = {
        template:"<button @click='increment'>你点了我{{count}}次,我记住了</button>",
        data:function(){
            return {
                count:0
            }
        },
        methods:{
            increment(){
                this.count++;
            }
        }
    };

    const vm = new Vue({
        el:"#app",
        components:{
            "button-counter":buttonCounter
        }
    })

</script>

说明:组件注册时,如果组件名和组件变量名同名,还可以简写:

components:{
buttonCounter:buttonCounter
}
// ==> 直接只写一个 buttonCounter即可
components:{
buttonCounter
}

优化2:将html片段抽取到template标签中

template中的html片段比较复杂时,在组件参数中直接定义html片段非常麻烦,此时可以将html片段抽取到特殊的template标签中。

<template id="bc">
    <!-- 再次提示:template标签中也必须只有一个根标签 -->
    <button @click='increment'>你点了我{{count}}次,我记住了</button>
</template>
<script>
    const buttonCounter = {
        template:"#bc",
        data:function(){
            return {
                count:0
            }
        },
        methods:{
            increment(){
                this.count++;
            }
        }
    };

    const vm = new Vue({
        el:"#app",
        components:{
            "button-counter":buttonCounter
        }
    })

</script>
组件和is属性

在html标签中使用组件时,受到 HTML 本身的一些限制。比如table、ul、select内部只能出现特定的子标签,如果在这些标签中使用组件,组件无法正确显示。解决方案:使用is属性

<div id="app">
    <h4>直接在table中使用table-body组件</h4>
    <table border="1">
        <thead>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>age</th>
                <th>gender</th>
            </tr>
        </thead>
		<!-- 直接使用组件-->
        <table-body></table-body>
    </table>
    <h4>在table中通过is属性使用table-body组件</h4>
    <table border="1">
        <thead>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>age</th>
                <th>gender</th>
            </tr>
        </thead>
		<!-- 通过is属性使用组件 -->
        <tbody is="table-body"></tbody>
    </table>
</div>
    
<template id="table-body">
    <tbody>
        <tr v-for="u in users" :key="u.id">
            <td>{{u.id}}</td>
            <td>{{u.name}}</td>
            <td>{{u.age}}</td>
            <td>{{u.gender}}</td>
        </tr>
    </tbody>
</template>

<script>
    const tableBody = {
        template:"#table-body",
        data(){
            return {
                users: [
                    {"id":1, "name": "小明", "age": 13, "gender": "男"},
                    {"id":2, "name": "小红", "age": 13, "gender": "女"},
                    {"id":3, "name": "小绿", "age": 4, "gender": "男"}
                ]
            }
        }
    }

    const vm = new Vue({
        el:"#app",
        components:{
            "table-body":tableBody
        }
    })
</script>

组件的嵌套

在Vue的设计中,一切都可以看作组件。整个页面可以看作是一个根组件,内部的各块组件可以看作子组件。组件之间自然的会发生嵌套关系。
在这里插入图片描述

接下来,我们看一下组件嵌套的示例:

在这里插入图片描述

<div id="app">
    <my-table></my-table>
</div>

<template id="table-head">
    <thead>
        <tr>
            <th>id</th>
            <th>name</th>
            <th>age</th>
            <th>gender</th>
        </tr>
    </thead>
</template>

<template id="table-body">
    <tbody>
        <tr v-for="u in users" :key="u.id">
            <td>{{u.id}}</td>
            <td>{{u.name}}</td>
            <td>{{u.age}}</td>
            <td>{{u.gender}}</td>
        </tr>
    </tbody>
</template>

<template id="my-table">
    <table border="1" align="center">
        <thead is="table-head"></thead>
        <tbody is="table-body"></tbody>
    </table>
</template>
<script>
    var tableHead = {
        template:"#table-head"
    }
    var tableBody = {
        template:"#table-body",
        data:function(){
            return {
                users: [
                    {"id":1, "name": "小明", "age": 13, "gender": "男"},
                    {"id":2, "name": "小红", "age": 13, "gender": "女"},
                    {"id":3, "name": "小绿", "age": 4, "gender": "男"}
                ]
            }
        }
    }

    //使用局部注册,需要将子组件注册到父组件中
    var myTable ={
        template:"#my-table",
        components:{
            "table-head":tableHead,
            "table-body":tableBody
        }
    }

    const vm = new Vue({
        el:"#app",
        components:{
            "my-table":myTable
        }
    });
</script>

说明:事实上,虽然 new Vue() 没有显式的使用组件的语法,但它本质上也是一个父组件。

组件通信

通常一个较为复杂的页面,一定会出现组件的嵌套。各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。

父传子:props

通常父组件的模板中包含子组件,父组件要正向地向子组件传递数据或参数,子组件接收到后根据参数的不同来渲染不同的内容或执行操作。这个正向传递数据的过程就是通过props来实现的。

比如在之前的表格案例中,table-body子组件展示的数据是定义在子组件自个身上的,这么做虽有效果,但降低了该组件的复用价值,更好的做法:子组件中不定义数据,而是由使用它的父组件传递。此时,需要使用 props完成父组件向子组件的数据传递。

语法:
//1.定义子组件中添加props属性
const 组件 = {
	template:"html片段",
	props:["自定义参数名",...]
}
//2 使用组件时,为组件添加 自定义参数名 同名的属性
<组件 :自定义参数名=”值"></组件>

示例:

<div id="app">
    <my-table :us="users"></my-table>
</div>

<template id="my-table">
    <table border="1" align="center">
        <thead>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>age</th>
                <th>gender</th>
            </tr>
        </thead>

        <tbody>
            <tr v-for="u in us" :key="u.id">
                <td>{{u.id}}</td>
                <td>{{u.name}}</td>
                <td>{{u.age}}</td>
                <td>{{u.gender}}</td>
            </tr>
        </tbody>
    </table>
    
</template>


<script>

    var myTable = {
        template:"#my-table",
        props:["us"]
    }
    const vm = new Vue({
        el:"#app",
        data:{
            users: [
                {"id":1, "name": "小明", "age": 13, "gender": "男"},
                {"id":2, "name": "小红", "age": 13, "gender": "女"},
                {"id":3, "name": "小绿", "age": 4, "gender": "男"}
            ]
        },
        components:{
            "myTable":myTable
        }
    });
</script>
子传父:$emit

父组件的模板中包含子组件,那么经常会出现子组件的状态发生变化时,要通知到父组件。所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。如下所示:

<div id="app">
    <h2>{{num}}</h2>
    <!-- 
父组件向子组件传递数据 count=num
子组件中修改count值,不能修改父组件的num
-->
    <counter :count="num"></counter>
</div>

<script>
    Vue.component("counter",{
        template:`
<div>
<button @click="increment">点我自增</button>
<span>{{count}}</span>
<button @click="decrement">点我自减</button>
    </div>`,
        props:["count"],
        methods:{
            increment(){
                this.count++;
            },
            decrement(){
                this.count--;
            }
        }
    })

    const vm = new Vue({
        el:"#app",
        data:{
            num:0
        }
    })
</script>

在这里插入图片描述
所以,当需要子传父的时候,Vue采用事件放射的方式完成。

  • 在子组件中执行 $emit(“父组件的自定义事件”)通知父组件,并发送数据
  • 父组件中定义自定义事件处理函数,并接收数据
<div id="app">
    <h2>{{num}}</h2>
    <!-- 2 绑定自定义事件处理函数,监听子组件的事件触发 change-count
		当子组件触发事件后,执行handleChange事件处理函数,
		$emit发送的数据将成为handleChange的实参
	-->
    <counter :count="num" @change-count="handleChange"></counter>
</div>

<script>
    Vue.component("counter",{
        template:`
            <div>
                <button @click="increment">点我自增</button>
                <span>{{count}}</span>
                <button @click="decrement">点我自减</button>
             </div>`,
        props:["count"],
        methods:{
            increment(){
                this.count++;
                //1 子组件中通过$emit触发父组件中的自定义事件,并发送数据
                this.$emit("change-count",this.count);
            },
            decrement(){
                this.count--;
                //1 子组件中通过$emit触发父组件中的自定义事件,并发送数据
                this.$emit("change-count",this.count);
            }
        }
    })

    const vm = new Vue({
        el:"#app",
        data:{
            num:0
        },
        methods:{
            handleChange(value){
                this.num = value;
            }
        }
    })
</script>
实战案例

在这里插入图片描述
示例:

<div id="app">
    <div>
        <my-table :us="users" @show-user="handleShowUser"></my-table>
        <hr>
        <update-form :u="user" @update-user="handleUpdateUser"></update-form>
    </div>
</div>
<template id="my-table">
    <table border="1">
        <thead>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>age</th>
                <th>operation</th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="(u,index) in us" :key="u.id">
                <td>{{u.id}}</td>
                <td>{{u.name}}</td>
                <td>{{u.age}}</td>
                <td>
                    <button @click="emitShowUser(u)">更新</button>
                </td>
            </tr>
        </tbody>
    </table>
</template>
<template id="update-form">
    <form action="" @submit.prevent="emitUpdateUser">
        <input type="hidden" v-model="u.id" >
        用户名: <input type="text" v-model="u.name"> <br>
        年龄: <input type="number" v-model="u.age"> <br>
        <input type="submit" value="更新">
    </form>
</template>

<script>
    const updateForm = {
        template:"#update-form",
        props:["u"],
        methods:{
            emitUpdateUser(){
                this.$emit("update-user",this.u);
            }
        }
    };

    const myTable = {
        template:"#my-table",
        props:["us"],
        methods:{
            emitShowUser(u){
                this.$emit("show-user",u);
            }
        }
    };


 
    const vm = new Vue({
        el:"#app",
        data:{
            users:[
                {id:1,name:"xiao1hei",age:18},
                {id:2,name:"xiao2hei",age:20},
                {id:3,name:"xiao3hei",age:22}
            ],
            user:{}
        },
        components:{
            myTable,updateForm
        },
        methods:{
            handleShowUser(u){
                this.user = JSON.parse(JSON.stringify(u));
            },
            handleUpdateUser(u){
                this.users.forEach((item,i,users)=>{
                    if(item.id == u.id){
                        //由于Vue2.x无法直接检测到数组中元素的变化,可以使用vue提供的$set方法
                        this.$set(this.users,i,JSON.parse(JSON.stringify(u)));
                    }
                })
            }
        }
    })
</script>

路由 VueRouter

5.1 路由解决什么问题?

什么是路由(这里不要死抠字眼,尝试从字面上理解,只是一个名词而已,重点是理解名词代表的技术解决什么问题)?生活中最能体现路由功能的是路由器,路由器的作用:互联网中的计算机,必须要通过路由器根据目标机器ip地址,将数据转发到目标机器。

路由的作用:根据请求的地址,将请求转发到匹配的组件上。

后端路由

传统的网站大多数是多页面的,当我们点击一个超链接时,请求发送到服务端,由服务端根据请求路径匹配到不同的Controller,进行各种操作,最终将html或数据返回给前端。当然,目前绝大多数的网站都是这种后端路由,也就是多页面的。这样的好处有很多,比如页面可以在服务端渲染好直接返回给浏览器,不用等待前端加载任何js和css就可以直接显示网页内容,再比如对SEO的友好等。后端路由的缺点也是很明显的,就是模板是由后端来维护或改写的。前端开发者需要安装整套的后端服务,必要时还得学习像PHP或Java这些非前端语言来改写html结构,所以html和数据、逻辑混为一谈,维护起来既臃肿又麻烦。

前端路由

然后就有了前后端分离的开发模式,后端不再返回页面只提供API来返回数据,前端通过Ajax获取数据后,再用一定的方式渲染到页面里,这么做的优点就是前后端做的事情分得很清楚,后端专注在数据上,前端专注在交互和可视化上,如果今后再开发移动App,那就正好能使用一套API了。

随着前端技术的发展,前端功能越来越丰富,出现了大前端的概念,有了不少的单页面富应用。也就是在一个页面,可以使用到应用的所有功能。功能视图之前的切换,不再通过服务端完成,而是通过前端控制,这就是前端路由。

在这里插入图片描述
Vue中路由的作用

在Vue中一个功能视图就是一个组件,Vue中路由解决的就是在单页面中组件的显示切换问题。比如有这个一个页面,页面上有2个超链接(登录和注册),点击登录链接显示登录的表单,点击注册连接显示注册的表单。
在这里插入图片描述

第1个路由示例

1.页面引入 vue-router.js

<script type="text/javascript" src="js/vue-2.6.12.js"></script>
<!--注意引入顺序: vue-router.js 必须在vue.js之后引入 -->
<script type="text/javascript" src="js/vue-router-3.4.9.js"></script>

2.注册组件

const loginForm = {
    template:"#login-form"
}
const registerForm = {
    template:"#register-form"
}

const vm = new Vue({
    el:"#app",
    components:{
        loginForm,
        registerForm
    }
})

3.创建 VueRouter对象,配置路由匹配规则

//router对象
var router = new VueRouter({
    routes:[
        {
            name:"login",//路由名称
            path:"/login",//路由路径
            component:loginForm//要路由到的组件
        },
        {name:"register",path:"/register",component:registerForm}
    ]
});

4.向Vue实例中配置router

const vm = new Vue({
    el:"#app",
    components:{
        loginForm,
        registerForm
    },
    //router:router //如果路由对象名也叫router,则可以省略:router
    router
})

5.在页面上使用路由

<div id="app">
    <!-- 超链接的href必须以#开头 -->
    <a href="#/login">登录</a>
    <a href="#/register">注册</a>
    <hr>
    <!-- router-view是路由的锚点,也就是路由到的组件显示的地方 -->
    <router-view></router-view>
</div>
router-link和redirect

router-link

router-link标签用于代替a标签,定义跳转超链接

<div id="app">
    <!-- 超链接的href必须以#开头 -->
    <!--<a href="#/login">登录</a>
    <a href="#/register">注册</a>-->
    <!-- to属性配置跳转路径,不用添加#前缀 -->
    <router-link to="/login">登录</router-link>
    <router-link to="/register">注册</router-link>
    <hr>
    <!-- router-view是路由的锚点,也就是路由到的组件显示的地方 -->
    <router-view></router-view>
</div>

redirect

redirect属性用在路由上,比如在a路由上配置redirect:b路由的路径,当访问a时会自动重定向到b。通常用于配置页面的默认路由地址

var router = new VueRouter({
    routes:[
        {name:"default",path:"/",redirect:"/login"},
        {
            name:"login",//路由名称
            path:"/login",//路由路径
            component:loginForm//要路由到的组件
        },
        {name:"register",path:"/register",component:registerForm}
    ]
});

嵌套路由

实际生活中的应用界面,通常由多层嵌套的组件组合而成。当我们路由到某一个组件后,还经常需要在该组件中继续路由到其子组件,这就需要使用Vue的嵌套路由。比如:当我们路由到登录组件后,又分为手机验证码登录账户名密码登录 2个子组件。
在这里插入图片描述

语法:
const 子组件 = {
    template:"子组件标签"
}

const 父组件 = {
    tempalate:`
		<router-link to="/父组件路径/子组件的路径">链接1</router-link>
		<router-view></router-view>
	`
}

const router = new VueRouter({
    routes:[
        {
            path:"/组件路径",
            component:父组件对象,
            children:[//设置子路由信息
                {
                    path:"子组件路径",//  不以/开头
                    component:子组件
                }
            ]
        }
    ]
})

示例:

<body>
    <div id="app">
        <router-link to="/login">登录</router-link>
        <router-link to="/register">注册</router-link>
        <hr>
        <router-view></router-view>
    </div>

    <template id="phone-login-form">
        <form action="">
            手机号: <input type="number" name="phone" > <br>
            验证码: <input type="password" name="code"> <br>
            <input type="submit" value="登录">
        </form>
    </template>
    <template id="account-login-form">
        <form action="">
            用户名: <input type="text" name="username" > <br>
            密码: <input type="password" name="pwd"> <br>
            <input type="submit" value="登录">
        </form>
    </template>
    <template id="register-form">
        <form action="">
            用户名: <input type="text" name="username" > <br>
            密码: <input type="password" name="pwd1"> <br>
            确认密码: <input type="password" name="pwd2" > <br>
            <input type="submit" value="注册">
        </form>
    </template>

    <script>
        const registerForm = {
            template:"#register-form"
        }

        const phoneLogin = {
            template:"#phone-login-form"
        }
        const accountLogin = {
            template:"#account-login-form"
        }

        const loginForm = {
            template:`
                <div>
                    <router-link to="/login/phone">手机验证码登录</router-link>
                    <router-link to="/login/account">用户名密码登录</router-link> <br> <br>
                    <router-view></router-view>
                </div>
            `
        }

        const router = new VueRouter({
            routes:[
                {path:"/",redirect:"/register"},
                {path:"/register",component:registerForm},
                {
                    path:"/login",
                    component:loginForm,
                    children:[
                        {path:"phone",component:phoneLogin},
                        {path:"account",component:accountLogin}
                    ]
                }
            ]
        })

        const vm = new Vue({
            el:"#app",
            router
        })
    </script>
</body>

路由组件传参

query传参
<div id="app">
    <router-link to="/user?name=李栓蛋&age=38">用户管理</router-link>
    <router-link :to="{path:'/user',query:{name:'王花花',age:28}}">用户管理</router-link>
    <router-view></router-view>
</div>
<script>
    const user = {
        template:`<div>
                    <div>我叫{{$route.query.name}}</div>
                    <div>今年{{$route.query.age}}</div>
                </div>`
    }

    const router = new VueRouter({
        routes:[
            {path:"/user",component:user}
        ]
    })

    const vm = new Vue({
        el:"#app",
        router
    })
param传参
<div id="app">
     <router-link to="/product/macbookair">商品1</router-link>
     <router-link :to="{name:'product',params:{name:'macbookpro'}}">商品2</router-link>		<router-view></router-view>
</div>
<script>
    const product = {
        template:`<div>
					<div>商品名:{{$route.params.name}}</div>  
                </div>`
    }
	//通过:参数名的方式匹配数据
    const router = new VueRouter({
        routes:[
            {name:"product",path:"/product/:name",component:product}
        ]
    })

    const vm = new Vue({
        el:"#app",
        router
    })
</script>

编程式的路由导航

除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

语法:router.push(location, onComplete?, onAbort?)

注意:在 Vue 实例内部,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push

声明式编程式
<router-link :to="...">router.push(...)

该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:

// 字符串
router.push('/login')

// 对象
router.push({ path: '/login' })

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

示例:在注册后,立刻导航到登录

<div id="app">
    <router-link to="/login">登录</router-link>
    <router-link to="/register">注册</router-link>
    <hr>
    <router-view></router-view>
</div>
<template id="login-form">
    <form action="">
        用户名: <input type="text" name="username" > <br>
        密码: <input type="password" name="pwd"> <br>
        <input type="submit" value="登录">
    </form>
</template>
<template id="register-form">
    <form action="" @submit.prevent="handleRegister">
        用户名: <input type="text" name="username" > <br>
        密码: <input type="password" name="pwd1"> <br>
        确认密码: <input type="password" name="pwd2" > <br>
        <input type="submit" value="注册">
    </form>
</template>

<script>
    const loginForm = {
        template:"#login-form"
    }
    const registerForm = {
        template:"#register-form",
        methods:{
            handleRegister(){
                alert("添加成功");
                this.$router.push({path:"/login"})
            }
        }
    }
    var router = new VueRouter({
        routes:[
            {name:"default",path:"/",redirect:"/login"},
            {
                name:"login",//路由名称
                path:"/login",//路由路径
                component:loginForm//要路由到的组件
            },
            {name:"register",path:"/register",component:registerForm}
        ]
    });
    const vm = new Vue({
        el:"#app",
        components:{
            loginForm,
            registerForm
        },
        //router:router //如果路由变量名也叫router,则可以省略:router
        router
    })

</script>

实战案例

登录和注册切换

案例效果:
在这里插入图片描述
示例代码:

<div id="app">
    <router-view></router-view>
</div>
<template id="login-form">
    <form action="" @submit.prevent="handleLogin">
        用户名: <input type="text" > <br>
        密码: <input type="password"> <br>
        <input type="submit" value="登录">
        <router-link to="/register">注册</router-link>
    </form>
</template>
<template id="register-form">
    <form action="" @submit.prevent="handleRegister">
        用户名: <input type="text" > <br>
        密码: <input type="password" > <br>
        确认密码: <input type="password" > <br>
        <input type="submit" value="注册">   
        <router-link to="/login">登录</router-link>
    </form>
</template>
<script>
    const loginForm = {
        template:"#login-form",
        methods:{
            handleLogin(){
                alert("登录成功");
                location.href="https://www.baidu.com";
            }
        }
    };

    const registerForm = {
        template:"#register-form",
        methods:{
            handleRegister(){
                alert("注册成功");
                this.$router.push("/login");
            }
        }
    };

    const router = new VueRouter({
        routes:[
            {path:"/",redirect:"/login"},
            {path:"/login",component:loginForm},
            {path:"/register",component:registerForm}
        ]
    });

    const vm = new Vue({
        el:"#app",
        router
    })
</script>
增删改查的切换

案例效果:
在这里插入图片描述
示例代码:

<div id="app" >
    <div >
        <router-link to="/book/show"><button >查询</button></router-link>
        <router-link to="/book/add"><button >添加</button></router-link>
        <hr>
        <router-view></router-view>
    </div>
</div>
<template id="my-table">
    <table border="1">
        <thead>
            <tr>
                <th>id</th>
                <th>name</th>
                <th>price</th>
                <th>stock</th>
                <th>operation</th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="b in books" :key="b.id">
                <td>{{b.id}}</td>
                <td>{{b.name}}</td>
                <td>{{b.price}}</td>
                <td>{{b.stock}}</td>
                <td>
                    <button>删除</button>
                    <router-link :to="{path:'/book/update',query:{id:b.id}}"><button>更新</button></router-link>
                </td>
            </tr>
        </tbody>
    </table>
</template>
<template id="add-form">
    <form action="">
        name: <input type="text" name="" id=""> <br>
        price: <input type="number" name="" id=""> <br>
        stock: <input type="number" name="" id=""> <br>
        <input type="submit" value="添加">
    </form>
</template>
<template id="update-form">
    <form action="">
        <input type="hidden" name="">
        name: <input type="text" name="" id=""> <br>
        price: <input type="number" name="" id=""> <br>
        stock: <input type="number" name="" id=""> <br>
        <input type="submit" value="更新">
    </form>
</template>
<script>
    const addForm = {
        template:"#add-form"           
    }

    const updateForm = {
        template:"#update-form"
    };

    const myTable = {
        template:"#my-table",
        data(){
            return {
                books:[
                    {id:1,name:"十万个为什么",price:100000.0,stock:1},
                    {id:2,name:"vue从入门到放弃",price:19800.0,stock:2}
                ]
            }
        }
    }

    const router = new VueRouter({
        routes:[
            {path:"/book/show",component:myTable},
            {path:"/book/add",component:addForm},
            {path:"/book/update",component:updateForm},
            {name:"book",path:"/book/:id",component:updateForm}
        ]
    });

    const vm = new Vue({
        el:"#app",
        router
    })
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值