Vue2.0快速入门

7 篇文章 1 订阅

Vue2.0

文章目录


vue的官方概念:是一套用于构建用户界面的前端框架

构建用户界面:往html页面中填充数据

框架:一套现成的解决方案、具有规范化

主要学习Vue指令、组件、路由、vuex

Vue框架的特性

数据驱动视图

在使用了vue的页面中,vue会监听数据的变化,从而自动重新渲染页面的结构。
在这里插入图片描述

  • 页面数据发生变化时,页面会自动渲染
  • 数据驱动视图时单向的数据绑定

双向数据绑定

  • 在网页中form表单负责采集数据,Ajax负责提交数据

  • js数据的变化,会被自动渲染到页面上

  • 开发者不再需要手动操作dom,来获取表单元素最新的值

在这里插入图片描述

  • 数据驱动视图和双向数据绑定的底层原理是MVVM

MVVM

MVVM是vue实现数据驱动视图和双向数据绑定的核心原理。MVVM指的是 Model、View、和ViewModel,他把HTML页面拆分成三部分:

在这里插入图片描述

在MVVM概念中:

  • Model表示当前页面渲染时所依赖的数据源。
  • View表示当前页面所渲染的DOM结构
  • ViewModel表示Vue的实例,他是MVVM的核心

MVVM的工作原理

  • ViewModel作为MVVM的核心,是他把当前数据页面的数据源(Model)和页面结构(View)连接在了一起
  • 当数据源发生变化时,会被ViewModel监听到,VM会根据最新的数据源自动更新页面的结构
  • 当DOM数据发生变化时,会被ViewModel监听到,VM会把变化过后最新的值自动同步到Model数据源

vue的基本使用

基本使用步骤

  • 导入vue.js的script脚本文件
  • 在页面中声明一个将要被vue所控制的DOM区域
  • 创建VM实例对象(vue实例对象)
<body>
    <!-- 希望vue能够控制这个div,帮助将数据填充到div内部。也就是视图区域 -->
    <div id="app">{{username}}</div>
    <!-- 导入vue的库文件 -->
    <script src="./vue2.6.12.js"></script>
    <!-- 创建vue的实例对象 -->
    <script>
        const vm = new Vue({
            // el属性是固定写法,表示vm实例要控制页面上的哪个区域,接受的值是一个选择器。就是指定了当前vm实例要控制页面的哪个区域
            el:'#app',
            // data对象就是要渲染到页面上的数据,也就是Model数据源
            data:{
                username:'yxj',

            }

        })
    </script>

vue的指令与修饰符

指令的概念

指令是vue为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构

vue中的指令按照不同的用途可以分为六大类

  • 内容渲染指令
  • 属性绑定指令
  • 事件绑定属性
  • 双向绑定指令
  • 条件渲染指令
  • 列表渲染指令

内容渲染指令

内容渲染指令用来辅助开发者渲染DOM元素的文本内容。常用的内容渲染指令有3个

v-text

会覆盖标签内原有的内容

    <div id="app">
        <p v-text="username"></p>
        <p v-text="grnder">性别</p>
    </div>
    <script src="./vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{
                username:'yxj',
                grnder:'男'

            }

        })
    </script>
{{}}

插值表达式,用于解决v-text会覆盖文本内容的问题,英文名称为Mustache

    <div id="app">
        <p>{{username}}</p>
        <p>{{grnder}}</p>
    </div>
    <script src="./vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{
                username:'yxj',
                grnder:'男'

            }

        })
    </script>

除了能渲染绑定的数据,在{{}}里还能对js的表达式进行运算

{{grnder + 1}}
{{message.split('').reverse().join(',')}}
v-html

v-text和插值表达式只能渲染纯文本内容。如果要把包含HTML标签的字符串渲染为页面的HTML元素,则需要用到v-html指令

    <div id="app">
        <p v-html="info"></p>
    </div>
    <script src="./vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{
                username:'yxj',
                grnder:'男',
                info:` <h4 style="color: red;">vue内容渲染指令</h4>`,

            }

        })

属性绑定指令

注意:插值表达式不能用到属性节点,只能用到元素的节点

v-bind

为元素的属性动态绑定值,可以简写:属性名

如果绑定内容需要动态拼接,则字符串的外面应该包裹单引号

    <div id="app">
        <input type="text" v-bind:placeholder="tips">
        <img :src="photo" alt="">
    </div>
    <script src="./vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{
                tips:'请输入用户名',
                photo:'https://publish-pic-cpu.baidu.com/19cb1b17-3857-4f66-93cd-68e25564ba62.png@w_228,h_152'
            }

        })
    </script>

事件绑定指令

v-on

为DOM元素绑定事件监听。

语法v-on:事件名字=“函数名”

    <div id="app">
        <p>{{count}}</p>
        <!-- (参数) -->
        <button v-on:click="add(2)">+</button>
        <!-- 简写@ -->
        <button @click="sub">-</button>
    </div>
    <script src="./vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{
                count:0,
            },
            // 定义事件的处理函数
            methods: {
                //传递进参数就无法获取事件对象了?
                add(n){
                    vm.count+=n;
                    console.log(vm === this);//true
                },
                sub(){
                    this.count--;
                }
            }

        })
    </script>

事件对象的问题

如果事件绑定的函数有参数了,怎么获取事件对象呢?

$event

  • 是vue的内置变量,可以得到原生DOM的事件对象

  • 如果绑定事件函数时传递了参数,而且还需要用到事件对象时就需要用到$event

        <div id="app">
            <p>{{count}}</p>
            <button v-on:click="add(n,$event)">+</button>
            <button @click="sub">-</button>
        </div>
        <script src="./vue2.6.12.js"></script>
        <script>
            const vm = new Vue({
                el:'#app',
                data:{
                    count:0,
                    n:1
                },
                // 定义事件的处理函数
                methods: {
                    add(n,$event){
                        vm.count+=n;
                        if(this.count%2 === 0){
                            $event.target.style.backgroundColor='pink';
                        }else{
                            $event.target.style.backgroundColor='yellow';
                        }
                    },
                    sub(){
                        this.count--;
                    }
                }
            })
        </script>
    

事件修饰符

在事件处理函数中调用event.preventDefault()或event.stopPropagation()是非常常见的需求。因此,vue提供了修饰事件符的概念,更方便对事件的触发进行控制

修饰事件符描述
.prevent阻止默认行为,(例如阻止a连接的跳转、阻止表单的提交)
.stop阻止事件冒泡
.capture以捕获模式触发当前的事件处理函数
.once绑定的事件只触发1次
.self只有在event.target是当前元素自身时触发事件处理函数
    <div id="app">
        <a href="https://www.baidu.com" @click="show">百度一下</a> <br><br>
        <a href="https://www.baidu.com" @click.prevent="show2">百度二下</a>
    </div>
    <script src="./vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{
                count:0,
            },
            methods: {
                show(e){
                    // 阻止浏览器跳转行为
                    e.preventDefault();
                    console.log("你点了百度一下,被事件对象阻止了");
                },
                show2(){
                    console.log("你点了百度二下,被vue的事件修饰符阻止了");
                }
            }
        })
    </script>

按键修饰符

在监听键盘事件时,我们经常需要判断详细的按键。此时可以为键盘相关的事件添加案件修饰符

    <div id="app">
        <!-- 在只有key是enter时调用 submit方法 -->
        <input type="text" @keyup.enter="submit">
        <!-- 在只有key是ESC时调用 clear方法 -->
        <input type="text" @keyup.esc="clear">
    </div>
    <script src="./vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{

            },
            methods: {
                submit(){
                    console.log("Enter键被按下了");
                },
                clear(e){
                    e.target.value="";
                    console.log("内容被清空了");
                }
            }
        })
    </script>

双向绑定指令

v-model
  • vue提供了v-model双向数据绑定指令,可以在不操作DOM的前提下,快速获取表单的数据。

  • v-model在底层自动判断绑定的元素,根据元素的不同绑定的属性也不一样,比如input自动绑定value属性

        <div id="app">
            <!-- 双向数据绑定 -->
            <input type="text" v-model="username"  @blur="fun1">
            <!-- 数据驱动视图,单项传递 不会改变data里username的值 -->
            <input type="text" :value="username" @blur="fun2">
            <hr>
            <select name="" id="" v-model="city">
                <option value="1">北京</option>
                <option value="2">上海</option>
                <option value="3">广州</option>
                <option value="4">深圳</option>
            </select>
        </div>
        <script src="./vue2.6.12.js"></script>
        <script>
            const vm = new Vue({
                el:'#app',
                data:{
                    username:'yxj',
                    city:'1'
                },
                methods: {
                    fun1(){
                        console.log(this.username);
                    },
                    fun2(){
                        console.log(this.username+'666');
                    }
                }
            })
        </script>
    

v-model指令的修饰符

为了方便对用户输入的内容进行处理,vue为v-model指令提供了3个修饰符

修饰符作用实例
.number自动将用户输入的值转为数值类型
.trim自动过滤用户输入的首尾空白字符
.lazy在“change”时而非input时更新
    <div id="app">
        <input v-model.number="n1" > +<input v-model.number="n2">= <span>{{n1 + n2}}</span>
        <br>
        <input v-model.trim="msg"  > <button @click="fun">获取用户名</button>
        <br>
        <input v-model.lazy="username" @blur="fun2">
    </div>
    <script src="./vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{
                n1:'',
                n2:'',
                msg:'',
                age:18,
                username:''
            },
            methods: {
                fun(){
                    console.log(`用户名是:"${this.msg}"`);
                },
                fun2(){
                    console.log(this.username);
                }
            }
        })
    </script>

条件渲染指令

v-if
  • 原理是每次动态创建或者移除元素,实现元素的显示和隐藏
  • 如果页面加载时元素默认不需要展示,而且后期也不需要频繁展示时使用
v-show
  • 一开始就会创建元素,尽管条件为false,

  • 原理是动态为元素添加或者删除display:none属性来实现对元素的隐藏与显示

  • 如果需要频繁的切换元素显示与隐藏v-show效率更高一些

v-else

  • v-else-if必须配合v-if使用

列表渲染指令

  • 用于循环渲染一个列表结构。v-for指令需要使用item in items 形式的特殊语法
  • items 是待循环的数组
  • item 是被循环的每一项
  • v-for还支持一个可选的第二个参数,即当前项的索引。语法为 (item,index)in items
v-for
    <div id="app">
        <table border="1" align="center" width="50%">
            <thead>
                <th>索引</th>
                <th>ID</th>
                <th>姓名</th>
            </thead>
            <tbody>
                <tr  v-for="(item,index) in list" :title="item.name">
                    <td>{{index}}</td>
                    <td>{{item.id}}</td>
                    <td>{{item.name}}</td>
                </tr>
            </tbody>
        </table>
    </div>
    <script src="./vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{
                list:[
                    {id:1,name:'yxj'},
                    {id:2,name:'lqy'},
                    {id:3,name:'lqd'},
                ]
            }

        })
    </script>

官方建议

  • 只要使用到了v-for指令,一定要绑定一个:key属性,尽量把:id的值作为key的值
  • key的值只能是字符串或者数字,且不能重复
  • 使用index值时没有意义,他不能保证值得唯一
列表渲染的案例

在这里插入图片描述

核心代码

<form @submit.prevent="add">
    <div class="min">
        <span>品牌名称</span>
        <input type="text" class="form-control" v-model.trim="value" placeholder="输入名字 ">
        <button type="submit" class="btn">添加</button>
    </div>

</form>
<table border="1" align="center" width="80%">
    <thead>
        <th>#</th> 
        <th>品牌名称</th>
        <th>状态</th>
        <th>创建时间</th>
        <th>操作</th>
    </thead>
    <tbody>
        <tr  v-for="item in list" :key="item.id" :title="item.name">
            <td>{{item.id}}</td>
            <td>{{item.name}}</td>
            <td>
              <div>
                <input type="checkbox" v-model="item.status" :id="'ub' + item.id" @change="change(item.status,item.id)">
                <label v-if="item.status" :for="'ub' + item.id">已启用</label>
                  <label v-else :for="'ub' + item.id">已禁用</label>
              </div>  
            </td>
            <td>{{item.time | fam}}</td>
            <td>
              <a href="javascript:; " @click="remove(item.id)">删除</a>
            </td>
        </tr>
    </tbody>
</table>
export default {
  data(){
    return{
      value:'',
      nextId:9,
        list:[
            {id:1,name:'宝马',status:true, time:new Date()},
            {id:2,name:'奥迪',status:true, time:new Date()},
            {id:3,name:'红旗',status:true, time:new Date()},
            {id:4,name:'奔驰',status:true, time:new Date()},
            {id:5,name:'大众',status:true, time:new Date()},
            {id:6,name:'丰田',status:true, time:new Date()},
            {id:7,name:'本田',status:true, time:new Date()},
            {id:8,name:'五菱',status:true, time:new Date()},
        ]
    }  
  },
  filters:{
    fam(time){
      return time.toString();
    }
  },
  methods:{
    add(){
      // 如果判断到他为空则return
      if(this.value == '') return alert("品牌名称不能为空");
      else{
        console.log(this.$dayJS+'666');
        const obj = {
          id:this.nextId,
          name:this.value,
          status:true,
          time:new Date()
        }
        this.list.push(obj);
        this.value="";
        this.nextId++;
      }
    },
    remove(id){
      console.log(id);
      this.list = this.list.filter((item)=>{
        return item.id != id;
      })
    },
    change(item,id){
      console.log(item+id.toString());
      
    }
  }

}

过滤器(vue3已经淘汰)

私有过滤器

  • 过滤器(Filters),常用于文本的格式化。过滤器可以用在插值表达式和v-bind属性绑定
  • 过滤器应该被添加在JavaScript表达式的尾部,由“管道符”进行调用
  • 本质上是一个函数,必须定义到filters节点下
  • 过滤器必须有返回值
  • 通过形参获取管道符前的值
<div id="app">
    <p>{{msg | capi}}</p>
</div>
<script src="./vue2.6.12.js"></script>
<script>
    const vm = new Vue({
        el:'#app',
        data:{
            msg:"message"
        },
        // 过滤器函数必须被定义到filters节点之下,本质时是一个函数
        filters: {
            // 形参里的val始终是管道符前的值
            capi(val){
                // 过滤器中一定要有一个返回值,
                return val.charAt(0).toUpperCase()+val.slice(1);
            }
        }
    })
</script>

全局过滤器

  • 如果希望在多个vue实例之间共享过滤器,则可以按照以下格式定义全局过滤器
  • 全局过滤:独立于每个vm实例之外
  • Vue.filter() 方法接收两个参数
  • 第一个参数是全局过滤器的名字
  • 第二个参数是全局过滤器的数里函数()函数里的参数为管道符之前的值

Vue.filter('capitalize',(str)=>{
return val.charAt(0).toUpperCase()+val.slice(1);
})
  • 如果全局过滤器与私有过滤器冲突,则按照就近原则调用

过滤器的其他用法

<p>{{msg | capi |tow | three}}</p>
//可以连续调用过滤器

侦听器

  • watch允许监视数据的变化
  • 本质是一个函数,要监视个哪数据的变化,就把数据名作方法名即可
  • 监听数据必须是 data 中声明过或者父组件传递过来的 props 中的数据
  • handler:指向一个处理函数
  • immediate 属性为 true,可以立即执行一次 handler 函数
  • deep属性为true时可以深度侦听,可以侦听到对象属性值的变化
  • 深度侦听时会增大开销,把对象里的属性层层遍历,侦听时只写需要侦听的对象下的某个具体属性值即可

当需要在数据变化时执行异步操作或开销较大的操作时,这个方式是最有用的,这是和 computed 最大的区别。

watch: {
    'obj.a': {//使用对象.属性的方式进行侦听,可以优化性能
      handler () {
        console.log('obj.a changed')
      },
      immediate: true
      // deep: true
    }
  }

watch:特点

  • 不支持缓存( 因为watch监听的函数接收两个参数,第一个参数是最新的值,第二个参数是输入之前的值。而computed属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值 ),数据变化会直接触发响应的操作
  • 支持异步操作
// 本质是一个函数,要监视那个数据的变化,就把数据名作方法名即可
watch: {
    //第一个值为新值,第二个值为旧值
    username(newdata,old){
        console.log(newdata,old);
    }
}

使用场景

  • 需要在数据变化时执行异步操作、或者执行开销较大的操作。
  • 使用watch允许执行异步操作,限制

方法格式的侦听器

  • 需要在数据变化时执行异步操作、或者执行开销较大的操作。
  • 使用watch时是允许执行异步操作的,执行异步操作时可限制执行操作的频率。
<body>
    <div id="app">
        <input type="text" v-model="username">
    </div>
    <script src="../vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{
                username:""
            },
            // 本质是一个函数,要监视那个数据的变化,就把数据名作方法名即可
            watch: {
                //第一个值为新值,第二个值为旧值
                // 函数格式
                username(newdata,old){
                    console.log(newdata,old);
                }
            }
        })
    </script>

对象格式的侦听器

  • handler(){}为侦听器处理函数
  • 可以通过immediate属性让侦听器自动触发
  • 通过deep属性可以侦听到对象属性的变化
watch: {
    // 对象格式的侦听器
    username:{
        // 侦听器处理函数
        handler(newdata,old){
            console.log(newdata,old);
        },
            // 控制侦听器是否自动触发一次
            immediate:true,
            deep: true,//深度侦听
    }
}
  • 直接通过 ‘对象.属性’ 的形式也可以侦听到

    watch: {
        // 如果要侦听对象子属性的变化,则必须包裹一层单引号
        'info.username'(newvalue){
            console.log(newvalue);
        }
    }
    

计算属性

计算属性指的是通过一些列运算之后,最终得到一个属性值。

这个动态计算出来的属性值可以被插值表达式或者methods方法使用。

特点:

  • 定义的时候定义为方法
  • 使用时候当成属性使用

好处:

  • 实现了代码的复用
  • 只要计算属性中依赖的属性值变化了就会自动求值
<body>
    <div id="app">
        R:<input type="text" v-model="r"><br>
        G:<input type="text" v-model="g"><br>
        B:<input type="text" v-model="b"><br>
        <br>
        <div class="box" :style="{backgroundColor:`rgb(${r},${g},${b})`}">
            {{rgb}}
        </div>
        <button @click="show">按钮</button>
    </div>
    <script src="../vue2.6.12.js"></script>
    <script>
        const vm = new Vue({
            el:'#app',
            data:{
                r:0,
                g:0,
                b:0
            },
            methods: {
                show(){
                    console.log(this.rgb);
                }
            },
            computed: {
                //rgb作为一个计算属性,被定义成了方法格式
                //最终在这个方法中,要返回一个生成好的rgb(x,x,x)的字符串
                // 声明的是方法,使用的时候是属性
                //可以自动求值,实现代码的复用
                rgb(){
                    return `rgb(${this.r},${this.g},${this.b})`;
                }
            }
        })
    </script>

axios

是一个专注于网络请求的库

安装

cnpm install axios -D
  • 引入并挂在到vue原型上
import axios from 'axios';
Vue.prototype.$axios = axios

1.发起GET请求

this.$axios({
    // 请求方式
    method: 'get',
    //请求地址
    url: 'https://api.apiopen.top/getJoke',
    // URL中的查询参数
    params:{
        sid: '29184338',
    },
}).then((result)=>{
    console.log(result);
});

2.发起post请求

async send(){
    const {data:res} = await this.$axios({
        // 请求方式
        method: 'POST',
        //请求地址
        url: 'https://api.apiopen.top/getJoke',
        //请求体参数
        data: {
            name: 'zs',
            age: '20'
        }
    });
    console.log(res.result);
},

直接发起get或post请求

<template>
<div>
        <button @click="post">直接发送POST</button>
        <button @click="get">直接发送GET</button>
  </div>
</template>

<script>
export default {
  data(){
    return{}
  },
  methods:{
    async get(){
      const {data:res} = await this.$axios.get('https://api.apiopen.top/getJoke',{
        params:{id:1}
      });
      console.log(res);
    },
     post(){
      this.$axios.post('https://api.apiopen.top/getJoke',{name:'yxj',gender:'男'}).then((res)=>{
        console.log(res.data.result);
      });
    }
  }
}
</script>

vue-cli

vue-cli是 vue.js开发的标准工具。他简化了基于webpack创建工程化的vue项目的工程。

好处就是省去webpack配置的问题

安装和使用

安装vue-cli之前已经配置好node.js环境

vue-cli是npm上的一个全局包

npm install -g @vue/cli

然后通过以下命令创建项目

vue create project

根目录构成

在这里插入图片描述

node_modules

  • 存储了所有的第三方包

src

  • 项目的所有源代码

public/favicon.ico

  • 网站的小图标

public/index.html

  • 单页面文件,生成的源代码文件(new Vue的文件)会被自动注入这个页面里

package.json

  • 包管理配置文件,存储一些第三方下载的依赖包

babel.config.js

  • babel的配置文件。babel可以打包处理webpack无法处理的高级js语法

.gitignore

  • 忽略文件,默认忽略掉一些文件

package-lock.json

  • 锁定安装时的包的版本号,并且需要上传到git,以保证其他人在npm install时大家的依赖能保证一致。

src目录

在这里插入图片描述

main.js

  • 项目的入口文件,整个项目的运行需要先执行main.js文件

app.vue

  • 项目的根组件,定义一些ui结构

assets

  • 静态资源的文件夹,css、img

components

  • 存放封装组件的文件夹

vue项目的运行流程

在工程化的项目中,vue要做的事情很单纯,通过main.js把App.vue的ui结构渲染到index.html的指定区域中。

  • 其中App.vue用来编写待渲染的模板结构
  • index.html中需要预留一个el区域
  • main.js会把App.vue渲染到index.html所预留的区域中

vue组件

  • vue是一个支持组件化开发的前端框架
  • 组件化就是把页面上可复用的UI结构封装为组件,方便项目的开发和维护
  • 组建的后缀名为.vue

组件的三个组成部分

  • template 模板结构
  • script JavaScript行为
  • style 组件样式

组件之间的父子关系

在这里插入图片描述

私有组件的使用

  • 使用import语法导入需要的组件

    import Left from './component/Left.vue'
    
  • 使用components节点注册组件(这样是私有子组件)

    export default{
        components:{
            'Left':Left,
        }
    }
    
  • 以标签的形式使用组件

    <div>
        <Left></Left>
    </div>
    

全局注册组件

在vue项目的main.js入口文件中,通过Vue.component()方法,可以注册全局组件,

// 导入需要全局注册的组件
import Right from './components/Right.vue'
// 第一个参数为别名,第二个参数为注册的组件名
Vue.component('MyRirht',Right);

组件的props

props是组件的自定义属性,在封装通用组件的时候,合理地使用props可以极大提高组件的复用性

  • props中的数据可以直接在模板中使用

  • props中的数据是只读的,修改会报错

  • props默认值的设置

  • props的type值类型

      // props是自定义属性,允许使用者通过自定义属性,为当前组件指定初始值
      props:{
        //   自定义属性
          init:{
              //如果没有设置初始值时会使用默认值
              default:1,
              //指定值类型
              type:Number,
                  
          }
      },
    
  • props的required必填项

     props:{
        //   自定义属性
          init:{
              //如果没有设置初始值时会使用默认值
              default:1,
              //指定值类型
              type:Number,
              //在调用该组件时必须指定init值,与默认值无关
              required:true,
          }
     },
    

示例

  • 组件的封装者
<template>
  <div class="wrap">
      <div>
          <span>count的值为{{count}}</span>
          <br>
          <button @click="count+=1">+1</button>
      </div>
  </div>
</template>
<script>
export default {
  // props是自定义属性,允许使用者通过自定义属性,为当前组件指定初始值
  props:['init'],
  data(){
    return{
      count:this.init
    }
  }
}
</script>
  • 组件的使用者
<template>
  <div class="wrap">
      <h1>left</h1>
		 <!-- 如果没有:则是字符串类型 -->
      <Count :init="9"></Count>
      <Count></Count>
  </div>
</template>
<script>
import Count from './Acount.vue'
export default {
  components:{
    Count,
  },
  data(){
    return{
      count:0
    }
  }
}
</script>

组件之间样式冲突问题

默认情况下,写在.vue组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。

导致组件之间样式冲突的根本原因:

  • 单页面应用程序中,所有组件的DOM结构,都是基于唯一的index.html页面结构进行呈现的
  • 每个组建的样式,都会影响整个index.html页面中的DOM元素

组件样式冲突的解决

  • 给当前页面组件的所有标签添加一个专有属性
  • 不同页面组件的标签属性值是不一样的
  • 这样对不同的页面设置样式时就不会冲突了

scoped属性

  • 可以解决样式冲突问题
<style lang="scss" scoped>
</style>

/deep/属性

  • 当使用第三方组件库的时候,如果有修改第三方组件默认样式的需求,需要用到/deep/
   /deep/.switch{
     border: #ff0 2px solid;
    color: rgb(175, 87, 33);
  }

vue文件的解析流程

  • package.json里的devDependencies
  • 这个包可以将.vue结尾的模板文件编译成.js文件
"vue-template-compiler": "^2.6.11"

组件的生命周期

1.生命周期

  • (Life Cycle)是指一个组件从创建->运行->销毁的整个阶段
  • 强调的是一个时间段
  • 在生命周期里会依次执行生命周期函数

2.生命周期函数

  • 是由vue框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行

  • 强调的是时间点

组件生命周期函数的分类

在这里插入图片描述

生命周期树形图
在这里插入图片描述

beforeCreate
  • 组件的props、data、methods尚未被创建,都处于不可用状态
created
  • 组件的props、data、methods已创建,都处于可用状态
  • 常在这个生命周期函数里发起Ajax请求服务器的数据
  • 但是组件模板尚未生成,不能操作DOM
beforeMount
  • 即将渲染DOM结构,还没有渲染
  • 此时还不能操作DOM
mounted
  • 成功渲染DOM结构
  • 此时已经可以操作DOM
beforeUpdate
  • 当data数据发生改变时才会触发该函数
  • 将要根据最新数据(已经是最新数据,但是还没渲染)重新渲染组件的模板结构
  • 至少执行0次,最多执行n次
updated
  • 已经根据最新的数据,完成了组件DOM结构的重新渲染
  • 至少执行0次,最多执行n次
beforeDestroy
  • 将要销毁组件,但是还未销毁
  • 组件还可以正常工作
destroyed
  • 已经销毁组件的数据侦听器,子组件,事件监听…
  • 组件对应的DOM结构已经被完全移除

组件之间的数据共享

组件之间常见的关系:

  • 父子关系
  • 兄弟关系

父传子

父组件向子组件共享数据需要自定义props属性

如果传递的数据是引用数据类型则只是浅拷贝该数据

  • 父组件(提供数据)

    <Left :msg="message" :user="userinfo"></Left>
    data(){
        return{
          message:'我是父组件的msg',
          userinfo:{
            name:'yxj',
            age:'18'
          }
        }
      }
    
  • 子组件(接收数据)

    <div class="son">
        <h5>Son组件</h5>
        <p>父组件传递过来的 msg 值是:{{msg}}</p>
        <p>父组件传递过来的 user 值是:{{user}}</p>
    </div>
    props:['msg','user'],
    

在这里插入图片描述

子传父

子组件向父组件共享数据要使用自定义事件

通过$emit()触发自定义事件

第一个参数为自定义的触发的事件名

第二个参数为传递的数据

  • 子组件

    add(){
          this.count+=1;
          //修改数据时,通过$emit()触发自定义事件
          this.$emit('numchange',this.count)
        },
    
  • 父组件

    通过自定义的触发的事件名获取数据

    <h1>App根组件{{countFromSon}}</h1>
    <MyRight @numchange="getNewCount"></MyRight>
    data(){
        return{
            // 定义countFromSon来接收子组件传递过来的数据
            countFromSon:0
        }
    },
        methods:{
            getNewCount(val){
                this.countFromSon = val;
            }
        }
    }
    

兄弟之间数据传递

在vue2.x中兄弟组件之间数据共享的方案是EventBus

EventBus的使用步骤

  • 创建eventBus.js模块,并向外共享一个Vue的实例对象

    import Vue from "vue";
    export default new Vue()
    
  • 在数据发送方调用bus.$emit(‘事件名称’,要发送的数据)方法触发自定义事件

    <template>
    <div class="fa">
      <div class="wrap">
          <h1>left 发送数据</h1> 
      </div>
      <button @click="send">点击发送数据给我的Right兄弟</button>
    </div>
    </template>
    <script>
    import bus from'./eventBus'
    export default {
      data(){
        return{
          str:`left 发送的数据`,
        }
      },
      methods:{
        send(){
          // 通过eventBus发送数据
          bus.$emit('share',this.str);
        }
      }
    }
    </script>
    
  • 在数据接收方调用bus.$on(‘事件名称’,要发送的数据)方法注册一个自定义事件

    <template>
    <div class="fa">
      <div class="wrap">
        <h1>Right 接收数据</h1>
        <h1>{{msgFromLeft}}</h1>
      </div>
    </div>
    </template>
    <script>
    import bus from'./eventBus'
    export default {
      data(){
        return{
          msgFromLeft:''
        }
      },
      created(){
        // 为bus绑定自定义事件
        bus.$on('share',(val)=>{
          this.msgFromLeft = val;
        })
      }
    }
    </script>
    

在这里插入图片描述

总结

  • 父组件向子组件传递数据时,父组件通过v-bind把数据绑定给子组件;子组件通过自定义props属性来接收数据。

  • 子组件向父组件传递数据时,子组件调用this. e m i t ( ) 方法触发自定义事件, emit()方法触发自定义事件, emit()方法触发自定义事件,emit()第一个参数为自定义事件名称,第二个参数为要发给父组件的数据;父组件通过v-on:绑定自定义事件,在事件处理函数的形参接收子组件传递过来的数据。

  • 兄弟组件之间数据共享需要定义一个共享的vue实例对象eventBus.js,并向外导出vue对象。发送方引入该实例对象并调用bus. e m i t ( ‘要触发的事件名’ , ′ 要发送的数 据 ′ ) 方法。接收方同样需要引入公共的实例对象,一般在 c r e a t e d 生命周期函数中调用 b u s . emit(‘要触发的事件名’,'要发送的数据')方法。接收方同样需要引入公共的实例对象,一般在created生命周期函数中调用bus. emit(要触发的事件名,要发送的数)方法。接收方同样需要引入公共的实例对象,一般在created生命周期函数中调用bus.on(‘发送方定义的事件名’,‘事件处理函数’),通过函数的形参接收数据。

ref引用

在不依赖于jQuery、不调用DOM api的情况下直接获取DOM元素或组件的引用。

在每一个vue的组件实例上,都包含了一个 r e f s 对象,里面存储着对应的 D O M 元素或组件的引用。默认情况下,组件的 refs对象,里面存储着对应的DOM元素或组件的引用。默认情况下,组件的 refs对象,里面存储着对应的DOM元素或组件的引用。默认情况下,组件的refs指向一个空对象。

  • ref可以获得元素的引用

DOM示例

<template>
  <div class="wrap">
      <h1 ref="test">refs</h1>
      <button @click="showThis">打印 this</button>
  </div>
</template>

<script>
export default {
    data(){
        return{
        }
    },
    methods:{
        showThis(){
            console.log(this);
            this.$refs.test.style.backgroundColor = 'red'
        }
    }
}
</script>
  • ref可以获得组件的引用
  • 可以调用组件的方法
  • 可以直接操作组件的数据

组件示例

<template>
  <div class="wrap">
      <h1 ref="test">refs</h1>
      <button @click="showThis">打印 this</button>
      <button @click="reset">重置Acount组件值</button>
      <div class="acount">
          <Acount ref="resetAc"></Acount>
      </div>
  </div>
</template>
<script>
import Acount from '../components/Acount.vue'
export default {
    components:{
      Acount  
    },
    data(){
        return{
        }
    },
    methods:{
        reset(){
            // console.log(this.$refs);
            this.$refs.resetAc.reset();
            // this.$refs.resetAc.count = 0
        },
        showThis(){
            // console.log(this);
            this.$refs.test.style.backgroundColor = 'red'
        }
    }
}
</script>

ref练习

this.$nextTick(callback())

this.$nextTick()方法能够将回调函数里的语句延迟调用

  • 等待DOM更新完成之后再执行回调函数
  • 保证回调函数可以操作到最新的DOM元素
<template>
  <div class="wrap">
      <input ref="ipRef" type="text" v-if="flag" @blur="showButton">
      <button v-else @click="showInput">显示输入框</button>
  </div>
</template>
<script>
export default {
    data(){
        return{
            flag:false,
        }
    },
    methods:{
        showInput(){
            this.flag = true;
            this.$nextTick(()=>{
                this.$refs.ipRef.focus();
            })    
        },
        showButton(){
            this.flag = false;    
        }
    }
}
</script>

购物车的案例

vue 组件化思想实现一个简单的购物车页面

动态组件

<component>组件(这是vue内置的组件)

  • 实现不同组件之间的按需展示(动态切换组件的显示和隐藏)

  • vue提供了内置的<component>组件,专门用来实现动态组件的渲染。

  • 可以将<component>理解为一个占位符,一定要属性绑定上is属性

  • is属性值表示要展示的组件名字

<component :is="Left"></component>

简单使用

在这里插入图片描述

<button @click="comName = 'Left' ">展示Left</button>
<button  @click="comName = 'Right' ">展示Right</button>
<div>
    <component :is="comName"></component>
</div>
import Left from'./components/Left.vue';
import Right from './components/Right.vue';
export default {
  name: 'App',
  components: {
    Left,Right
  },
  data(){
    return{
      comName:Left,
    }
  },

注意

  • 动态组件再切换的时,隐藏的组件会被销毁(destroyed),显示时会重新渲染(created)
  • 此时的数据也会被重新渲染

在这里插入图片描述

<keep-alive>

  • <keep-alive>是vue内置的标签可以把内部的组件缓存,而不是销毁组件

在这里插入图片描述

<keep-alive include="Right,Left">
    <component :is="comName"></component>
</keep-alive>
  • include属性是指定缓存的组件,默认全部缓存

  • exclude属性是指定不缓存的组件,不能与include属性一起使用

<keep-alive exclude="Right">
    <component :is="comName"></component>
</keep-alive>

<keep-alive>对应的生命周期函数

  • 当组件被缓存时,会自动触发deactivated生命周期函数
deactivated(){
    console.log(组件被缓存了);//离开的时候
}
  • 当组件被激活时,会自动触发activated生命周期函数
activated(){
    console.log(组件被激活了);//首次被创建
}
  • 组件的注册名称主要应用场景是以标签的形式,把注册好的组件渲染到页面结构中
  • 组件声明时的name名称主要是为了结合<keep-alive>标签实现组件缓存功能,以及在调试工具中看到组件的name名称

插槽

插槽(slot)是vue为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望用户指定的部分定义为插槽。

比如你要封装一个组件myCount,组件有个header头部,和内容区;内容区域是由用户自定义的,这个时候就需要再组件内使用slot标签进行占位,在使用时指定自定义内容。

在这里插入图片描述

<Left>
    <p>left的p标签</p>
</Left>
//直接在组件内插入标签是不会被渲染的

简单使用

  • 首先在封装组件的时候预留一个插槽区

    //这里是Left组件
    <div class="fa">
      <!-- 生明一个插槽区 -->
      <slot name="default"></slot>
    </div>
    
  • 在使用组件时自定义的标签就会渲染到预留的插槽位

    <Left>
        <p>自定义left的p标签</p>
    //默认情况下,在使用组件的时候,提供的内容都会被填充到名字为default的插槽中
    </Left>
    
  • vue官方规定,每一个slot插槽,都要有一个name属性,用于指定插槽的名称;不设置的话默认name=“default”

<div class="fa">
  <slot name="default"></slot>
</div>
  • v-slot指令只能在组件里或者template里使用
<Left>
    <!-- template标签只起到包裹性的作用,不会被渲染成其他真实的元素 -->
    <template v-slot:default>
		<p>将p放置到default插槽中</p>
	</template>
</Left>

如果要把内容填充到指定名称的插槽中,需要用到v-slot指令

v-slot必须用在template标签上

template是一个虚拟标签,只起到包裹作用,不会被渲染成html元素

v-slot指令的简写是#

<Left>
    <!-- template标签只起到包裹性的作用,不会被渲染成其他真实的元素 -->
    <template #default>
		<p>将p放置到default插槽中</p>
	</template>
</Left>

插槽的后备(默认)内容

防止组件使用者没有指定slot的内容,所以预先放值一个默认的标签;组件使用者指定了slot内容的话就会覆盖掉默认内容

<div class="fa">
  <slot name="default">
      这是default插槽的默认内容
  </slot>
</div>

具名插槽

  • 首先新建一个Article组件

    <template>
      <div class="article-container">
        <div class="header">
            <slot name="title">1</slot>
        </div>
        <div class="contain">
            <slot name="main"></slot>
        </div>
        <div class="footer">
            <slot name="body"></slot>
        </div>
      </div>
    </template>
    
    <script>
    export default {
        name:'Article'
    }
    
  • 在引用组件时只需要如此如此即可

        <Article>
          <template #title>
            <h1>《山川》</h1>
          </template>
          <template #main>
            <h5>【作者】许月卿</h5>
          </template>
          <template #body>
            <h2>山川长不老,人意欲如何。</h2>
            <h2>霜后蒹葭健,秋来洲渚多。</h2>
            <h2>河汾周旧典,洙泗鲁余波。</h2>
            <h2>凄怆从陈蔡,令人涕滂沱。</h2>
          </template>
        </Article>
    

作用域插槽

在封装组件时,为预留的提供了一些属性和值,并且在组件引用时使用了原组件中预留的属性和值;这种用法就叫做作用域插槽。

  • 在子组件中除了定义name属性外,再定义一个自定义属性用于保存数据

    <template>
      <div class="article-container">
        <div class="header">
            <slot name="title" scoped="《春晓》"></slot>
        </div>
        <div class="contain">
            <slot name="main" author="【孟浩然】"></slot>
        </div>
        <div class="footer">
            <slot name="body" body="春眠不觉晓"></slot>
        </div>
      </div>
    </template>
    
  • 使用组件时指定(定义)一个与插槽name属性对应的属性名obj,通过这个属性名接收来自子组件传递过来的默认值

    <Article>
      //建议使用scoped接收  
        <template #title="obj">
            <h1>{{obj.scoped}}</h1>
        </template>
        <template #main="obj">
            <h5>{{obj.author}}</h5>
        </template>
        <template #body="obj">
            <h2>{{obj.body}}</h2>
            <h2>处处闻啼鸟</h2>
            <h2>夜来风雨声</h2>
            <h2>滑落知多少</h2>
        </template>
    </Article>
    

配合解构赋值使用

  • 子组件中

    <template>
      <div class="article-container">
        <div class="header">
            <slot name="title" scoped="《春晓》" :user="users"></slot>
        </div>
        <div class="contain">
            <slot name="main" author="【孟浩然】" :user="users"></slot>
        </div>
        <div class="footer">
            <slot name="body" body="春眠不觉晓"></slot>
        </div>
      </div>
    </template>
    <script>
    export default {
        // name:'Article'
        data(){
          return {
            users:{
              name:'yxj',
              age:25
            }
          }
        }
    }
    </script>
    
  • 组件使用时

        <Article>
          <template #title="{scoped}">
            <h1>{{scoped}}</h1>
          </template>
          <template #main="{user}">
            <h5>{{user.name}}</h5>
          </template>
          <template #body="obj">
            <h2>{{obj.body}}</h2>
            <h2>处处闻啼鸟</h2>
            <h2>夜来风雨声</h2>
            <h2>滑落知多少</h2>
          </template>
        </Article>
    

自定义指令

自定义指令分为私有自定义指令(单组件使用)、全局自定义指令(全组件使用)。

私有自定义指令

在每个vue组件中,可以在directives节点下声明私有自定义指令

directives节点是一个对象,与data节点平齐,directives对象里的可以设置多个对象,对象名就是自定义的属性名。然后在bind和update方法里就可以编写自定义指令的功能;这两个方法都接受两个参数el、和binding。el表示示当前指令所绑定的DOM元素对象;binding表示一个对象里面存储了指令的名字、值、表达式等。

bind函数只有第一次绑定时触发,只会触发一次。

update函数在每次DOM更新时会触发该函数

示例1

改变颜色

  • 在组件中与data节点平级下定义directives对象节点

    // 私有自定义指令的节点
    directives:{
        //定义名为color的指令,指向一个配置对象
        color:{//此时已经有了自定义指令
            //当指令被第一次绑定到元素身上时,会立即触发bind函数(只有第一次绑定时触发)
            bind(el){//el表示当前指令所绑定的DOM元素对象
                el.style.color = 'red'
            }
        }
    },
    
  • 使用自定义指令v-color

    <h1 v-color>app根组件</h1>
    

示例2

通过data改变颜色

<template>
	<h1 v-color="color">app根组件</h1>
    <p v-color="'red'">p标签</p>
  </div>
</template>
<script>
    data(){
        return{
          color:'blue'
        }
  	},
    directives:{
        color:{
        bind(el,binding){//binding表示一个对象里面存储了指令的名字、值、表达式等
          el.style.color = binding.value
          console.log(binding);
        }
        }
    },
</script>

示例3

点击改变颜色

<template>
	<h1 v-color="color">app根组件</h1>
    <p v-color="'red'">p标签</p>
    <button @click="color='green'">改变color</button>
  </div>
</template>
<script>
    data(){
        return{
          color:'blue'
        }
  	},
    directives:{
        color:{
            bind(el,binding){//只在第一次绑定时执行 
              el.style.color = binding.value
              console.log(binding);
            },
            update(el,binding){//在DOM更新时会触发该函数
              el.style.color = binding.value
              console.log(binding);
         }
        }
    },
</script>

函数的简写形式

<template>
	<h1 v-color="color">app根组件</h1>
    <p v-color="'red'">p标签</p>
    <button @click="color='green'">改变color</button>
  </div>
</template>
<script>
    data(){
        return{
          color:'blue'
        }
  	},
    directives:{
        //当binding和update函数里的逻辑代码完全一样时可以简写
        color(el,binding){
          el.style.color=binding.value;
        },
    },
</script>

全局自定义指令

全局声明的指令、过滤器等都需要在main.js文件里声明

//全局自定义指令
// Vue.directive('color',{
//   binding(el,binding){
//     el.style.color=binding.value
//   },
//   update(el,binding){
//     el.style.color=binding.value
//   },
// });
Vue.directive('color',function(el,binding){
  el.style.color=binding.value
});

eslint

是一个自动检查代码风格的一个工具,

  • 设置里的 Tab Size 值修改为2即可
  • Editor:Format On Save

新建一个项目

vue create projectName #新建项目
#选择 Manually select features
#按空格选择
Babel
CSS Pre-processors
Linter/Formatter
#回车、选择vue2
#选择Less预处理器
然后就是选择安装ESlint语法规范了
选择Standard config#标准配置
Lint and fix on commit (requires Git)#表示提交代码时自动检查修复
Lint on save#表示保存时自动检查
这里选择Lint on save就可以
#然后选择In dedicated config files放到独立的配置文件中
#最后是否保存为预设;可Y可N

可以看到默认rules

  rules: {
    // 在代码中禁用console。
    //process.env.NODE_ENV可以得到当前的打包模式,如果当前模式为production则警告,否则禁用该规则
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    // 在代码中禁用debugger
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
  }

参考ESLint官网:https://eslint.bootcss.com/

常见的规则

不能有连续的空行

不能有空格

最后一行必须是空行

默认使用单引号

对象的属性:和值之间要有一个空格

对象属性、方法后面不允许有多余的逗号

注释// 后至少加一个空格

缩进多了少了也不行

import/first。import导入模块的语句必须声明在文件的顶部

定义一个变量/常量没有被使用也会报错

方法名 ()之间必须有一个空格

在vsCode里安装ESLint插件

打开csCode的设置

  • 然后再配置文件settings.js里加入一个配置项
//ESLint插件的配置
"editor.codeActionsOnSave": {
    "source.fixAll": true,
},
  • 安装 prettier - Code formatter插件
  // prettier配置
  "eslint.alwaysShowStatus": true,
  "prettier.trailingComma": "none",
  "prettier.semi": false,
  //每行文字个数超出此限制将会被迫换行
  "prettier.printWidth": 300,
  //使用单引号替换双引号
  "prettier.singleQuote": true,
  "prettier.arrowParens": "avoid",
  //设置.vue文件中,HTML代码的格式化插件
  "vetur.format.defaultFormatter.html": "js-beautify-html",
  "vetur.ignoreProjectWarning": true,
  "vetur.format.defaultFormatterOptions": {
    "js-beautify-html": {
      "wrap_attributes": false
    },
    "prettyhtml": {
      "arrowParens":"avoid",
      "printWidth": 300,
      "singleQuote": true,
      "wrapAttributes": false,
      "sortAttributes": false,
      "trailingComma":"none",
      "semi":false
    }
  },

简直不要太烦,我直接不用了,哈哈哈。。。拜拜

axios

简单演示使用

首先新建两个组件,引入到app.vue里,两个组件展示大概是这样就行
在这里插入图片描述

关键代码

<template>
  <div class="left-container">
    <h3>Left 组件</h3>
    <button @click="getInfo">发起GET请求</button>
  </div>
</template>
<script>
import axios from 'axios'
export default {
  methods: {
    async getInfo() {
      const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/get')
      console.log(res);
    }
  }
}
</script>
<style>
  ...
</style>

全局配置使用

  • 下载

    npm install axios -S
    
  • 挂载

    #在main.js里导入axios
    import axios from 'axios'
    #全局配置请求根路径
    axios.defaults.baseURL = '请求根路径'
    //在vue的原型上挂载axios,每个vue组件都可以访问到。
    Vue.prototype.$http = axios
    //缺点是无法实现api接口的复用
    
  • 使用

      methods: {
        async getInfo() {
        const { data: res } = await this.$http.get('api/get')
          console.log(res);
        }
    
      }
    

路由

路由就是一个对应关系(组件和地址栏(hash)地址的对应关系)

前端路由的工作方式

  • 用户点击了页面上的路由链接
  • 导致URL地址栏中的Hash值发生了变化
  • 前端路由监听到了Hash地址的变化
  • 前端路由把当前的Hash地址对应的组件渲染到浏览器中

模拟前端路由的实现

  • 编写三个组件:首页、电影、更多,样式差不多就行

在这里插入图片描述

  • 通过动态组件引入

    <div class="box">
        <component :is="components"></component>
    </div>
    import MainPage from '@/components/MainPage.vue'
    import MoviePage from '@/components/MoviePage.vue'
    import MorePage from '@/components/MorePage.vue'
    export default {
      components: {
        MainPage,
        MoviePage,
        MorePage
      },
      data() {
        return {
          components: 'MainPage'
        }
      },
    
  • 在created生命周期函数里监听url的变化动态切换组件

      created() {
        window.onhashchange = () => {
          console.log(location.hash);
          switch (location.hash) {
            case '#/MorePage':
              this.components = 'MorePage'
              break
            case '#/MoviePage':
              this.components = 'MoviePage'
              break
            case '#/MainPage':
              this.components = 'MainPage'
              break
            default: this.components = 'MainPage'
          }
        }
      }
    

单页面应用的原理

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;它是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

优点:

  • 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
  • 基于上面一点,SPA 相对对服务器压力小;
  • 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;

缺点:

  • 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
  • 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
  • SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。

vue-router安装

安装

在vue2.x项目里安装路由的命令

npm i vue-router@3.5.2 -S

创建路由模块

在src目录下新建router/index.js文件

  • 1 导入Vue 和 VueRouter 、 调用Vue.sue() 函数,把VueRouter安装为Vue的插件、 创建路由的实例对象、向外共享路由的实例对象
// 这是当前项目的路由模块

// 1 导入Vue 和 VueRouter 的包
import Vue from 'vue'
import VueRouter from 'vue-router'

// 2 调用Vue.sue() 函数,把VueRouter安装为Vue的插件
Vue.use(VueRouter)

// 3 创建路由的实例对象
const router = new VueRouter()

// 4 向外共享路由的实例对象
export default router

在main.js里配置路由

// 导入路由模块,拿到路由实例对象
//在进行模块化导入(包括common.js和ES6)的时候,如果给定的是文件夹,则默认导入这个文件夹下,名字叫做index.js的文件
import router from '@/router'
new Vue({
  render: (h) => h(App),
  // 将router挂载到vue实例对象里
  router:router
}).$mount('#app')

这里回顾一下实例化vue

render函数用来渲染视图,也提供给el挂载,所以使用render函数就是为了页面显示出来。

1.render 方法是一个函数,在接受传入的参数 h 函数后,返回 h(App) 的函数调用结果。

2.在创建 vue 实例时,通过调用 render 方法来渲染实例页面的 DOM 结构。

3.当vue 在调用 render 方法时,会传入一个 createElement 函数作为参数,h 的实参是 createElement 函数,然后 createElement 会以 App为参数进行调用。

render: h => h(App);
//等价于
render: function(createElement) { return createElement(App); }

这里回顾一下模块化语法:common.js和ES6

CommonJS 模块是 Node.js 专用的,与 ES6 模块不兼容。而ES6模块化在浏览器和node.js中都可以用。 语法上面,两者最明显的差异是,CommonJS 模块使用require()和module.exports,ES6 模块使用import和export。 在node.js使用模块化,需要将 CommonJS 脚本的后缀名都改成.cjs,ES6 模块采用.mjs后缀文件名。或者修改package.son里面的文件,type字段为module或commonjs。

运行时加载

就是对引入的模块进行整体的加载,比如说你要使用某个模块里对象的某个方法,就得加载模块里的所有代码,得到具体的对象,然后再从具体的对象上读取响应的方法。 因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

// CommonJS模块
let { stat, exists, readfile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

编译时加载(静态加载)

相当于是一种按需加载,只加载具体需要的方法,其他方法不加载。

ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。

//ES6模块
import { stat, exists, readFile } from 'fs';

ES6 模块与 CommonJS 模块差异

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。 ( CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值 ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块)
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。( CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成)
  • CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。

vue-router使用

<router-view></router-view>组件

只要在项目中安装和配置了vue-router,就可以使用<router-view></router-view>组件。他相当于一个占位符,当路由变化时,需要展示不同的组件,这些组件就是通过<router-view></router-view>组件展示的。

首先在src目录下新建一个App2.vue。里面的内容如此如此:

<template>
  <div class="app2">
    <h1>App2组件</h1>
    <a href="#/MainPage">首页</a>

    <a href="#/MoviePage">电影</a>

    <a href="#/MorePage">更多</a>
    <hr>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
}
</script>
<style>
.app2 a {
  padding: 3px;
}
</style>

然后在main.js中:

import App from './App2.vue'
//将App.vue改成App2.vue

最后在router/index.js下配置路由:

 // 这是当前项目的路由模块

// 1 导入Vue 和 VueRouter 的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入需要的组件
import MainPage from'@/components/MainPage.vue'
import MoviePage from'@/components/MoviePage.vue'
import MorePage from'@/components/MorePage.vue'
// 2 调用Vue.sue() 函数,把VueRouter安装为Vue的插件
Vue.use(VueRouter)
// 3 创建路由的实例对象
const router = new VueRouter({
  //routes是一个数组,用于定义hash地址和组件之间的对应关系
  routes:[
      //路由规则
      //path代表要展示的hash地址、component代表组件
    {path:'/MainPage',component:MainPage},
    {path:'/MoviePage',component:MoviePage},
    {path:'/MorePage',component:MorePage}
  ]
})
// 4 向外共享路由的实例对象
export default router

router-link

当安装和配置了vue-router后,就可以使用router-link来代替普通的a链接了。

<router-link>用于代替<a>标签的,他的to属性相当于<a>标签的href属性,也会被渲染成a标签。

<template>
  <div class="app2">
    <h1>App2组件</h1>
      <router-link to="/MainPage">首页</router-link>
      <router-link to="/MoviePage">电影</router-link>
      <router-link to="/MorePage">更多</router-link>
    <hr>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
}
</script>
<style>
.app2 a {
  padding: 3px;
}
</style>

redirect

redirect是路由重定向,比如用户访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面。通过路由规则的redirect属性,指定一个新的路由地址,可以很方便地设置路由地重定向。

const router = new VueRouter({
  //routes是一个数组,用于定义hash地址和组件之间的对应关系
  routes:[
    //重定向的路由规则
    {path:'/',redirect:'/MainPage'},
    //路由规则
    {path:'/MainPage',component:MainPage},
    {path:'/MoviePage',component:MoviePage},
    {path:'/MorePage',component:MorePage}
  ]
})

嵌套路由

通过路由实现的嵌套展示,叫做嵌套路由。如果某个组件本身就是通过路由的方式展示出来的,然后又在该组件里存放了路由规则。

  • 首先在components目录下新建tables目录,然后新建tables/Tab1.vue和tables/Tab2.vue文件

  • 然后在MorePage.vue页面里添加子集路由链接和子集路由占位符

在这里插入图片描述

<template>
  <div class="left-container">
    <h3>更多</h3>
    <!-- 子级路由链接 -->
    <router-link to="/MorePage/tab1">tab1</router-link>
    <router-link to="/MorePage/tab2">tab2</router-link>
    <hr>
    <!-- 子集路由占位符 -->
    <router-view></router-view>
  </div>
</template>
  • 接着通过children属性声明子路由规则:在router/index.js路由模块中导入需要的组件,并使用children属性声明子路由规则:
const router = new VueRouter({
  //routes是一个数组,用于定义hash地址和组件之间的对应关系
  routes:[
    //重定向的路由规则
    {path:'/',redirect:'/MainPage'},
    //路由规则
    {path:'/MainPage',component:MainPage},
    {path:'/MoviePage',component:MoviePage},
    {
      path:'/MorePage',
      component:MorePage,
      children:[
        // 子路由规则
        {path:'tab1',component:Tab1},
        {path:'tab2',component:Tab2}
      ]
    }
  ]
})
  • 效果就出来了
    在这里插入图片描述

默认子路由

如果children数组中,某个路由规则的path值为空字符串,则这条路由规则叫做“默认子路由”。

//在MorePage页面中设置
<template>
  <div class="left-container">
    <h3>更多</h3>
    <!-- 子级路由链接 -->
    <router-link to="/MorePage">tab1</router-link>
    <router-link to="/MorePage/tab2">tab2</router-link>
    <hr>
    <!-- 子集路由占位符 -->
    <router-view></router-view>
  </div>
</template>
//路由设置
const router = new VueRouter({
  //routes是一个数组,用于定义hash地址和组件之间的对应关系
  routes:[
    //重定向的路由规则
    {path:'/',redirect:'/MainPage'},
    //路由规则
    {path:'/MainPage',component:MainPage},
    {path:'/MoviePage',component:MoviePage},
    {
      path:'/MorePage',
      component:MorePage,
      children:[
        // 子路由规则
        {path:'',component:Tab1},//默认展示Tab1
        {path:'tab2',component:Tab2}
      ]
    }
  ]
})

动态路由

思考:

//如果有3个路由链接
<router-link to="/MorePage/tab1">tab1</router-link>
<router-link to="/MorePage/tab2">tab2</router-link>
<router-link to="/MorePage/tab3">tab3</router-link>
//就需要定义三条路由规则
{path:'/MorePage/tab1',component:Tab1}
{path:'/MorePage/tab2',component:Tab2}
{path:'/MorePage/tab3',component:Tab3}

这样路由规则的复用性差

动态路由的概念

把hash地址中可变的部分定义为动态的参数项,从而提高路由规则的复用性。

在vue-router中使用英文的冒号(:)来定义路由的参数项:

//路由中的动态参数以:进行声明,冒号后面的是动态参数的名称
{path:'/MorePage/tab:id',component:Tab+'id'}//id命名随意
//将以下3个路由规则,合并成一个,提高了路由规则的复用性
{path:'/MorePage/tab1',component:Tab1}
{path:'/MorePage/tab2',component:Tab2}
{path:'/MorePage/tab3',component:Tab3}

动态路由的体验

  • 首先在App.vue文件里如此如此:

    <template>
      <div class="app2">
        <h1>App2组件</h1>
        <a href="#/MainPage">首页</a>
    
        <a href="#/MoviePage/1">海王</a>
        <a href="#/MoviePage/2">龙珠</a>
        <a href="#/MoviePage/3">三国</a>
    
        <a href="#/MorePage">更多</a>
        <hr>
        <router-view></router-view>
      </div>
    </template>
    
  • 在路由规则中配置:

      routes:[
        // {path:'/',redirect:'/MainPage'},
        {path:'/MainPage',component:MainPage},
        //在MoviePage中需要根据id的值来展示对应电影的详情信息
        {path:'/MoviePage/:id',component:MoviePage},
        {
          path:'/MorePage',
          component:MorePage,
          children:[
            {path:'',component:Tab1},
            {path:'tab2',component:Tab2}
          ]
        }
      ]
    
  • 在MoviePage.vue页面中打印this

    <template>
      <div class="movie">
        <h3>电影</h3>
        <button @click="showThis">打印this</button>
      </div>
    </template>
    
    <script>
    export default {
      methods: {
        showThis() {
          console.log(this);
        }
      }
    }
    

    在控制台看到:在$route对象里params对象里的id就是路由规则中配置的id属性,值就是App2.vue页面里的值

在这里插入图片描述

  • 也就是说你想获取路由后面的参数值只需要通过this.$route.params.id就能获取到:

    //MoviePage.vue
    <template>
      <div class="movie">
        <h3>电影</h3>
        <h3>电影模块--{{this.$route.params.id}}</h3>
        <button @click="showThis">打印this</button>
      </div>
    </template>
    
  • $route是路由的参数对象

  • $router是路由的导航对象

路由规则开启props传参

  • 想要在组件中用props来接收路由规则里的动态参数,需要在路由规则里添加一个props:true的属性

    //可以为路由规则开启props传参,从而方便的拿到动态的参数值
    {path:'/MoviePage/:id',component:MoviePage,props:true},
    
  • 在需要接收参数的页面里使用prps进行接收。

    <template>
      <div class="movie">
        <h3>电影</h3>
        <h3>电影模块--{{id}}</h3>
        <button @click="showThis">打印this</button>
      </div>
    </template>
    
    <script>
    export default {
      props: ['id'],
      methods: {
        showThis() {
          console.log(this);
        }
      }
    }
    </script>
    

查询参数和全路径

注意

  • 在hash地址中/后面的参数项,叫做“路径参数”

        <a href="#/MoviePage/1">海王</a>
        <a href="#/MoviePage/2">龙珠</a>
        <a href="#/MoviePage/3">三国</a>
    
  • 在路由参数对象中需要使用this.$route.params来访问路径参数

注意

  • 在hash地址中?后面的参数为查询参数

    <a href="#/MoviePage/2?name=zs age=18">龙珠</a>
    
  • 在路由参数对象中需要使用this.$route.query来访问查询参数

注意

  • path只包含路径
  • fullPath是一个完整的hash地址(包含路径和查询参数)
    在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独立寒秋-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值