文章目录

一、 vue基础

1. vue的介绍

1.1 vue的特性

  1. 数据驱动视图
    优点:vue监听数据的变化,进而自动渲染刷新页面
    缺点:数据驱动视图是单向的(数据变化—>刷新页面)

  2. 双向数据绑定
    在网页中,表单负责采集数据,ajax负责提交数据

  • js数据的变化会被自动更新到页面上
  • 页面上表单采集的数据发生变化时,会被vue获取并自动更新到js

1.2 MVVM

  • vue是参考MVVM模型进行设计的

M:Model 页面渲染数据所依赖的数据源,对应vue中的data 数据
V:View 视图,当前页面所渲染的DOM结构,对应页面DOM结构
VM:ViewModel 是MVVM的核心,表示vue的实例对象

  • data中的所有属性,都会出现在vm上
  • vm上的所有属性及Vue原型上的所有属性,在Vue模板中都可以直接使用

1.3 vue数据代理

1.3.1 Object.defineproperty()
  • 给对象添加属性

Object.defineProperty(obj, prop, descriptor)

参数1: obj 要定义属性的对象
参数2:prop 要定义或修改的属性的名称或 Symbol
参数3:descriptor 要定义或修改的属性值
返回值:被传递给函数的对象

<script>
        let person = {
            name:"dudu",
            age:4,
        }
        Object.defineProperty(person,'gender',{
            value:'女'
        })
        console.log(person) //{name: 'dudu', age: 4, gender: '女'}
    </script>
  • 通过Object.defineproperty设置的属性不可以枚举(不可以被遍历)
  • value:设置属性值
  • enumerable:是否可枚举,默认为false
  • writable:是否可修改,默认为false
  • configurable:是否可删除,默认为false
let person = {
            name:"dudu",
            age:4,
        }
        Object.defineProperty(person,'gender',{
            value:'女',
            enumerable:true, //是否可枚举
        })
        console.log(Object.keys(person)) //['name', 'age','gender'] console.log(Object.keys(person)) //['name', 'age']
  • get() :当读取“参数2”时就会调用get方法(getter),其返回值为参数2的值(必须要有返回值),此时不可以在设置value
  • set():当修改参数2时会调用的set方法(setter),接收一个参数
<script>
        let number = 19
        let person = {
            name:'嘟嘟',
            sex:'女'
        }
        Object.defineProperty(person,'age',{
            // 当读取age属性时会调用
            // 必须有返回值,返回值就是age的值
            get() {
                console.log("有人在读取age属性")
                return number
            },
            set(val) {
                console.log("age属性被修改了!")
                number = val
            }
        })
    </script>

在这里插入图片描述

1.3.2 数据代理
  1. 数据代理是什么
  • 通过一个对象代理对另一个对象中的属性的操作(读/写)
let obj1 = {x:100}
        let obj2 = {y:200}
        // 通过obj2操作obj1中的x
        Object.defineProperty(obj2,'x',{
            get() {
                return obj1.x
            },
            set(val) {
                obj1.x = val
            }
        })

在这里插入图片描述
2. vue中的数据代理
在这里插入图片描述

  • 通过vm对象来代理data对象中属性(读/写,即getter / setter)
  • 这样更加方便操作data中的数据
  • 基本原理

通过Object.defineProperty()把data对象中的所有属性添加到vm上。
为每一个添加到vm上的属性,指定一个getter、setter
在getter、setter内部去操作(读、写)data对应的属性

  • _data内部是通过数据劫持实现的

2. vue基础用法

2.1 基本使用步骤

  1. 导入vue.js的script脚本
  2. 声明DOM区域
  3. 创建vm实例对象(vue实例对象)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue基本用法</title>
    <!-- 1. 导入vue的库,此时在window全局就有了vue构造函数 -->
    <script src="./lib/vue-2.6.14.js"></script>
</head>
<body>
    <div id="app">{{username}}</div>
    <!-- 2. 创建vue实例 -->
    <script>
        const vm = new Vue({
            // el是固定用法,表示当前vm实例要控制的区域,值是一个选择器
            el:'#app',
            // data对象就是要渲染到页面的数据
            data:{
                username:'嘟嘟'
            }
        })
    </script>
</body>
</html>

2.2 vue调试工具

  1. 安装vue-devtools调试工具
    vue调试工具插件下载地址
    在这里插入图片描述
  2. 将下载的文件解压,打开谷歌浏览器,右上角(…) ==》 更多工具 ==》扩展程序,将解压的Vue.jsxxxxx.crx文件拖入扩展程序中

安装的参考链接

:安装好后需要重启谷歌才生效

安装好的样子

3. vue指令

在这里插入图片描述

3.1 内容渲染指令

作用:用于辅助开发者渲染DOM元素的文本内容

3.1.1 v-text
  • 用于更新元素的textContent
  • v-text会覆盖元素内部原有的内容,一般不常用
3.1.2 插值语法 {{}}
  • 插值表达式(Mustache),是最常用的用法
  • 只能用在元素的内容节点中,不可用于属性节点
  • {{}}中只能写简单的js表达式,不能写if等复杂的js语句
3.1.3 v-html
  • 用于更新元素的innerHTML,渲染有标签的字符串为真正的DOM元素
  • 会覆盖子组件
  • 会有XXS风险
<body>
    <div id="app">
        <p>
            姓名:
            <span v-text="username"></span>
        </p>
        <p>
            性别:{{gender}}
        </p>
        <div v-html="info"></div>
    </div>
    <!-- 2. 创建vue实例 -->
    <script>
        const vm = new Vue({
            // el是固定用法,表示当前vm实例要控制的区域,值是一个选择器
            el:'#app',
            // data对象就是要渲染到页面的数据
            data:{
                username:'嘟嘟',
                gender:"女",
                info:`<h4 style="color:red">vue.js学习</h4>`
            }
        })
    </script>
</body>

运行效果截图:
在这里插入图片描述

3.2 属性绑定指令 v-bind

3.2.1 简写成 :

动态绑定属性值

<input type="text" :placeholder="tips">
3.2.2 JS表达式

在vue的模板渲染语法中除了支持绑定数据值,还可以支持JavaScript表达式的运算,但一般不推荐在HTML中写js表达式

<div>{{age+1}}</div>
  • 一旦使用了v-bind就会被认为js表达式
3.2.3 动态绑定class样式

:class=“xxx” xxx可以是字符串、对象、数组。

字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

3.2.4 动态绑定style样式

:style="{fontSize: xxx}“其中xxx是动态值。
:style=”[a,b]"其中a、b是样式对象。

3.3 事件绑定指令 v-on

3.3.1 简化写法 @

和事件处理函数methods搭配使用,@定义的方法要写在methods中
在绑定事件处理函数时可以进行传参

<body>
    <div id="app">
        <p>值:{{num}}</p>
        <button @click="addNum(5)">加法</button>
        <button @click="subNum">减法</button>
    </div>
    <!-- 2. 创建vue实例 -->
    <script>
        const vm = new Vue({
            // el是固定用法,表示当前vm实例要控制的区域,值是一个选择器
            el:'#app',
            // data对象就是要渲染到页面的数据
            data:{
                num:0
            },
            methods: {
                addNum(n) {
                    this.num += n
                },
                subNum() {
                    this.num--
                }
            }
        })
    </script>
</body>

:vue2 中的methods方法中this指向的是vm实例

3.3.2 事件对象 $event
  • 在绑定事件时如果不传参,就会默认接收一个事件对象 e
<body>
    <div id="app">
        <p>值:{{num}}</p>
        <!-- 如果num为偶数背景色为蓝色 -->
        <button @click="addNum">加法</button>
    </div>
    <!-- 2. 创建vue实例 -->
    <script>
        const vm = new Vue({
            // el是固定用法,表示当前vm实例要控制的区域,值是一个选择器
            el:'#app',
            // data对象就是要渲染到页面的数据
            data:{
                num:0
            },
            methods: {
                addNum(e) {

                    this.num += 1
                    // // 判断奇偶
                    if(this.num % 2 ===0){
                        // e.target 获取当前操作的DOM
                        e.target.style.backgroundColor = "skyBlue"
                        e.target.style.color = "#fff"
                    }else {
                        e.target.style.backgroundColor = ""
                        e.target.style.color = "#000"
                    }
                }
            }
        })
    </script>
</body>
  • 如果传了参数要获取e,可以通过 $event(原生的DOM事件对象e)获取
<button @click="addNum(2,$event)">加法</button>
3.3.3 事件修饰符
  • e.preventDefault()e.stopPropagation() 阻止默认行为(原生js中)
  • vue中通过事件修饰符 .prevent 阻止默认行为
<!-- 阻止a链接跳转 -->
<a href="https://www.baidu.com/" @click.prevent="showInfo">百度</a>

vue中的事件修饰符

  1. .prevent 阻止默认行为(如a链接跳转,表单的提交等)
  2. .stop 阻止事件冒泡
  3. .capture 以捕获模式触发当前的事件处理函数
  4. .once 绑定的事件值触发一次
  5. .self 只有在e.target是当前元素时触发
3.3.4 按键修饰符

用于监听详细的键盘事件

<body>
    <div id="app">
        <!-- 按下esc键清空输入框 -->
        <!-- 按下enter键输出值 -->
        <input type="text" @keyup.esc="cleanInput" @keyup.enter="enterFun">
    </div>
   <script>
        const vm = new Vue({
            el:'#app',
            data: {

            },
            methods: {
                cleanInput(e) {
                    // 清空输入
                    e.target.value = ''
                },
                enterFun(e) {
                    console.log(`输入的值为"${e.target.value}"`)
                }
            }
        })
   </script>
</body>

3.4 双向数据绑定指令 v-model

作用:在不操作DOM的前提下,快速捕获表单数据

  • v-model是双向绑定,数据变化会引起页面变化,页面变化会引起数据变化,适用于表单元素
  • v-bind 是单向绑定,只有数据变化会引起页面变化
<body>
    <div id="app">
        <p>用户名:{{username}}</p>
        <!-- v-model是双向绑定,数据变化会引起页面变化,页面变化会引起数据变化 -->
        <input type="text" v-model="username">
        <!-- v-bind 是单向绑定,只有数据变化会引起页面变化 -->
        <p><input type="text" :value="username"></p>
    </div>
   <script>
        const vm = new Vue({
            el:'#app',
            data: {
                username:"嘟嘟"
            }
        })
   </script>
</body>

效果图:
在这里插入图片描述
常见的表单元素

  • input输入框

    type=“radio”
    type=“checkbox”
    type=“text”

  • textarea

  • select

3.4.1 v-model的指令修饰符
  1. .number:将用户输入值转为数值(number)类型
    如:输入电话号码时
  2. .trim:过滤输入的收尾空白字符
    如:登录注册时
  3. .lazy:在"change"时更新而不是在“input”时更新

3.5 条件渲染指令:v-if 和 v-show

3.5.1 共同点

控制元素的显示和隐藏

3.5.2 区别
  1. v-if
    每次都会创建或移除元素
    适用于初始默认不展示,后面也可能不展示
  2. v-show
    通过display来控制元素的隐藏或显示
    适用于要频繁的切换元素的显示隐藏状态
3.5.3 v-else

v-if可以和v-else-ifv-else搭配使用

<div v-if="type === 'A'">优秀</div>
        <div v-else-if="type === 'B'">良好</div>
        <div v-else-if="type === 'C'">及格</div>
        <div v-else></div>

3.6 列表渲染指令 v-for

3.6.1 基本列表

v-for=“被循环的每一项 in 待循环数组”
或者
v-for=“被循环的每一项 of 待循环数组”

<body>
    <script src="./lib/vue-2.6.14.js"></script>
    <div id="app">
        <table class="table table-borderd table-hover">
            <thead>
                <th>索引</th>
                <th>ID</th>
                <th>姓名</th>
            </thead>
            <tbody>
                <tr v-for="(item,index) of list" :key="item.id">
                    <td>{{index}}</td>
                    <td>{{item.id}}</td>
                    <td>{{item.name}}</td>
                </tr>
            </tbody>
        </table>
    </div>
    <script>
            const vm = new Vue({
                el:'#app',
                data: {
                    list: [{
                       id:1,
                       name:"张三" 
                    },{
                       id:2,
                       name:"李四" 
                    },{
                       id:3,
                       name:"王二" 
                    },{
                       id:4,
                       name:"麻子" 
                    }]
                }
            })
    </script>
</body>
  • v-for支持 可选的 第二个参数,即当前的索引,语法为:(item,index) of list
  • v-for 要搭配 :key属性;尽量将循环项的id作为key的值,不推荐用index作为key值,容易重复不具备唯一性;key值要求为字符串或者数值类型;key值不允许重复(唯一性)
3.6.2 key的原理与作用
  • 作用:对节点进行标识,相当于身份证
  1. 虚拟DOM中key的作用:
    key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

  2. 对比规则:
    (1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
    ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
    ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
    (2). 旧虚拟DOM中未找到与新虚拟DOM相同的key
    创建新的真实DOM,随后渲染到到页面。

  3. 用index作为key可能会引发的问题
    1) 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
    2) 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。

  4. 开发中如何选择key?:
    1)最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
    2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

3.8 案例

效果图
在这里插入图片描述

<body>

    <div id="app">
      <!-- 卡片区域 -->
      <div class="card">
        <div class="card-header">
          添加品牌
        </div>
        <div class="card-body">
          <!-- 添加品牌的表单区域 -->
          <form>
            <div class="form-row align-items-center">
              <div class="col-auto">
                <div class="input-group mb-2">
                  <div class="input-group-prepend">
                    <div class="input-group-text">品牌名称</div>
                  </div>
                  <input type="text" class="form-control" placeholder="请输入品牌名称" v-model.trim="addName">
                </div>
              </div>
              <div class="col-auto">
                <button type="submit" class="btn btn-primary mb-2" @click.prevent="addRow">添加</button>
              </div>
            </div>
          </form>
        </div>
      </div>

      <!-- 表格区域 -->
      <table class="table table-bordered table-hover table-striped">
        <thead>
          <tr>
            <th scope="col">#</th>
            <th scope="col">品牌名称</th>
            <th scope="col">状态</th>
            <th scope="col">创建时间</th>
            <th scope="col">操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="item of list" :key="item.id">
            <td>{{item.id}}</td>
            <td>{{item.name}}</td>
            <td>
              <div class="custom-control custom-switch">
                <input type="checkbox" class="custom-control-input" :id="item.id" v-model="item.status">
                <label class="custom-control-label" :for="item.id" v-if="item.status">已启用</label>
                <label class="custom-control-label" :for="item.id" v-else>已禁用</label>
              </div>
            </td>
            <td>{{item.time}}</td>
            <td>
              <a href="javascript:;" @click="delRow(item.id)">删除</a>
            </td>
          </tr>
        </tbody>
      </table>
    </div>

    <script>
      const vm = new Vue({
        el:"#app",
        data:{
          addName:'', //要添加的品牌名
          list:[
            {
              id:1,
              name:'宝马',
              status:true,
              time: new Date()
            },{
              id:2,
              name:'奔驰',
              status:false,
              time: new Date()
            },{
              id:3,
              name:'奥迪',
              status:false,
              time: new Date()
            },{
              id:4,
              name:'红旗',
              status:true,
              time: new Date()
            }
          ]
        },
        methods: {
          // 删除
          delRow(id) {
            // 过滤掉id不等于要删除的元素id,然后重新赋值给list
           this.list = this.list.filter(item => item.id !== id)
          },
          // 添加
          addRow() {
            let keyword = this.addName
            
            if(keyword === "") {
              alert("您没有输入任何内容!!")
              return
            }else {
              let len = this.list.length + 1
              let item = {
                id:len,
                name:keyword,
                status:true,
                time:new Date()
              }
              // push()在数组最后添加元素
              this.list.push(item)
              this.addName = ''
            }
          }
        },
      })
    </script>
</body>

3.9 过滤器 Filters

注: 过滤器只适用于vue2 ,vue3中已删除过滤器

  • filters用于文本的格式化,可用在插值表达式v-bind属性绑定
  • 过滤器应该添加在JS表达式的尾部,由管道符( | )调用
  • 过滤器通过filters声明,且一定要有返回值
  • 过滤器函数的形参是管道符前面的值
<body>
    <div id="app">
        <p>{{message | capi}}</p>
    </div>
   <script>
        const vm = new Vue({
            el:'#app',
            data: {
                message:"hello!你好呀~"
            },
            methods: {
                
            },
            // 声明过滤器
            // 过滤器本质为一个函数
            filters:{
                // val指向的是message的值
                capi(val) {
                    // 实现首字母大写
                    // charAt()从字符串中取出对应索引的字符
                    const first = val.charAt(0).toUpperCase()
                    const other = val.slice(1)
                    // 过滤器一定要有返回值
                    return first + other
                }
            }
        })
   </script>
</body>
  • 可以连续调用多个过滤器,最后得到的是最后一个过滤器的返回值
  • 过滤器可以传参,在接收时要通过第二个开始
3.9.1 私有过滤器和全局过滤器
  • 私有过滤器:定义在vue实例的filters中的过滤器
  • 全局过滤器:可以实现多个vue实例之间共享过滤器,独立于每个vm实例
  • 全局过滤器的定义:
    Vue.filters(全局过滤器名字,全局过滤器的方法)
  • 全局过滤器要在vue实例之前声明
  • 全局和私有过滤器重名,则按就近原则调用私有过滤器
3.9.2 全局过滤器使用Day.js完善案例中的时间格式

Day.js官网

<script>
// 声明格式化时间的全局过滤器
      Vue.filter('dateFormat',(time) => {
        const timeformat = dayjs(time).format('YYYY-MM-DD HH:mm:ss')
        return timeformat
      })
</script>
// 使用全局过滤器
<p>{{item.time | dateFormat}}</p>

4. 侦听器 watch

监视data数据变化,接收两个参数,第一个参数是新值,第二个参数是旧值

4.1 方法格式

  • 方法格式的监听器,只有在需要监听的数据发生变化时,才会触发,一般采用方法格式
<body>
    <div id="app">
        <p>
            用户名:<input type="text" v-model="userName">
        </p>
    </div>
    <script src="./lib/vue-2.6.14.js"></script>
    <script>
        var vm = new Vue({
            el:"#app",
            data:{
                userName:""
            },
            // 侦听器
            watch:{
            	// 方法格式的侦听器
                userName(newVal,oldVal) {
                    console.log(`新值:${newVal}`)
                    console.log(`旧值:${oldVal}`)
                }
            }
        })
    </script>
</body>

4.2 对象格式

  • 对象格式的侦听器可以通过 immediate 让侦听器立即触发,immediate的默认值为false
  • 在对象格式中,通过handler定义侦听器的处理函数
// 对象格式的侦听器
                userName: {
                    immediate:true,
                    handler(newVal,oldVal) {
                        console.log(`新值:${newVal}`)
                    }
                }
  • 深度侦听:deep
  • 方法格式的侦听器在侦听对象时,如果对象中的属性发生了变化,不会触发侦听器。此时可以通过对象侦听的方式,设置deep属性,让侦听器监听对象中每个属性的变化
<body>
    <div id="app">
        <p>
            用户名:<input type="text" v-model="info.userName">
        </p>
    </div>
    <script src="./lib/vue-2.6.14.js"></script>
    <script>
        var vm = new Vue({
            el:"#app",
            data:{
                info: {
                    userName:"嘟嘟"
                }
            },
            // 侦听器
            watch:{
                // 深度侦听
                info: {
                    immediate:true,
                    handler(newVal,oldVal) {
                        console.log(`${newVal}`)
                    },
                    deep:true, //开启深度监听
                }
            }
        })
    </script>
</body>
  • 如果要监听对象的字书写变化,则必须包裹一层单引号
 'info.userName'(newVal){
                    console.log(`新值:${newVal}`)
                }
  • watch支持异步,不支持缓存
  • 应用场景
    注册时,判断用户名是否被占用

5. 计算属性 computed

本质是一个属性,是通过一系列运算得到的属性
计算属性可用在 模板结构 或者 methods 方法中

  • 计算属性要定义为方法格式,返回最终结果
  • 使用时通过属性的形式。computed可以在template模板结构中可以使用;也可以在methods方法中使用,此时通过this.计算属性调用
  • 优点1: 实现了代码复用
  • 优点2:只有依赖的数据源变化才会自动重新计算
  • computed支持缓存,不支持异步
<body>
    <div id="app">
        <p>r:<input type="text" v-model="r"></p>
        <p>g:<input type="text" v-model="g"></p>
        <p>b:<input type="text" v-model="b"></p>
        <div class="box" :style="{backgroundColor:rgb}">
            {{rgb}}
        </div>
    </div>
    <script src="./lib/vue-2.6.14.js"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
               r:0,
               g:0,
               b:0 
            },
            // 就算属性
            computed: {
                rgb() {
                    // 动态生成rgb字符串
                    return `rgb(${this.r},${this.g},${this.b})`
                }
            }
        })
    </script>
</body>

6. watch和computed区别

  • computed能完成的功能,watch都可以实现
  • watch能完成的功能,computed不一定可以实现
    比如:Watch可以进行异步操作,而计算属性不可以进行异步操作,它必须接收一个返回值
  • 当watch和computed都可以实现时,优先选择computed

两个小原则

  • 所有被vue管理的函数最好写成普通函数,这样this指向的才是vm或者组件实例对象
  • 所有不被vue管理的函数(定时器、ajax、promise),最好写成箭头函数,这样this指向的才是vm或者组件实例对象

7. axios

基本语法

axios({
                    // 请求方式:get、post
                    method:'',
                    // 请求地址
                    url:'',
                    // get时的参数
                    params:{},
                    // post的参数
                    data:{}
                }).then(()=>{})

可以用async、await 代替then

二、 vue-cli

1. 单页面应用程序(SPA)

  1. 含义:一个web网站中只有一个唯一的HTML页面

2. vue-cli

  • 是vue,js开发的标准工具,简化了程序员基于webpack创建工程化vue项目的过程

2.1 全局安装vue-cli

cnpm install -g @vue/cli

2.2 vue-cli创建项目

winpty vue.cmd create 项目名

  • 选择最后一项,然后回车
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.3 vue项目结构目录

在这里插入图片描述

  1. node_modules:项目依赖

  2. public:一般放置静态资源(如:图片),webpack打包时会原封不动的打包到dist文件夹

  3. src:源代码文件夹
    assets:放置静态资源(一般是多个组件公用的静态资源),webpack在打包时会把assets文件夹当做一个模块,打包到js文件中
    components:放置非路由组件、全局组件
    pages/views:放置路由组件(需创建)
    api:存放axios相关文件(需自己创建)
    store: 存放vuex相关文件(需自己创建)
    router:存放路由的配置文件(需创建)
    mock:存放模拟数据相关的(需创建)

    App.vue:唯一的根组件
    main.js:入口文件,整个程序中最先执行的文件

  4. gitignore git的忽略文件,一般不动

  5. babel.config.js:babel相关的配置文件,一般不动

  6. package.json:包管理配置文件,记录项目信息(项目怎么运行,有哪些依赖),类似于项目的“身份证”

  7. package-lock.json:可以删除,缓存性的文件

  8. readme.md:说明性文件

2.4 vue项目的运行流程

通过 main.jsApp.vue 渲染到 index.html 指定区域中

其中:

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

main.js代码

// 导入vue的包,得到Vue构造函数
import Vue from 'vue'
// 导入根组件,
import App from './App.vue'

Vue.config.productionTip = false

// 创建vue实例
new Vue({
  // 把render指定的组件渲染到HTML页面中
  render: h => h(App),
}).$mount('#app') //$mount()相当于el

三、vue组件

组件化开发:根据封装的思想,把页面上可复用的UI结构封装为组件,从而方便项目的开发和维护

vue中的组件后缀名为 .vue

1. vue组件的三个组成部分

  • template:组件的模板结构只能有一个根节点
  • script:组件的JavaScript行为
    script标签的内部必须写 默认导出(export default{}
    组件中的data为一个函数,且必须返回一个对象
  • style:组件的样式

在组件中,this指向当前组件的实例对象
组件的template中只能有一个根节点

2. 组件的使用

2.1 组件使用的三个步骤

  1. 通过import导入需要的组件
import Left from '@/components/left.vue'
import Right from '@/components/right.vue'
  1. 通过components节点注册组件
export default {
  name: 'App',
  components: {
    Left,
    Right
  }
}
  1. 以标签的形式使用刚才注册的组件
<Left></Left>
   <right></right>

2.2 vscode配置@路径提示的插件

  1. 安装插件 Path Autocomplete

  2. 修改setting.json配置
    在这里插入图片描述

在这里插入图片描述
在setting.json开头输入以下代码

// 导入文件时是否携带文件的扩展名
  "path-autocomplete.extensionOnImport": true,
  // 配置@的路径提示
  "path-autocomplete.pathMappings": {
    "@":"${folder}/src"
  },

:如果不起作用,可能是因为同一个文件夹下有多个项目

vscode中目前已经安装的插件

在这里插入图片描述

2.3 全局注册

  • 通过components注册的是私有子组件,只能在注册的组件中使用
  • 如果某组件需要在多个地方使用,可以注册为全局组件,全局组件只需要注册一次就可以直接使用

main.js入口文件中,通过 Vue.component(参数1,参数2) 方法注册
参数1:字符串格式,表示组件的“注册名”
参数2:需要被全局注册的组件

// 引入三级联动组件
import TypeNav from '@/components/TypeNav'
// 注册为全局组件
/**
 * 第一个参数:全局组件的名字
 * 第二个参数:哪一个组件
 */
Vue.component('nav',TypeNav)

3. 组件的props

props是组建的自定义属性,在封装通用组件时可以提高组件的复用性

  • 通过数组定义props
export default {
   props:['自定义属性1','自定义属性2',...]
}

  • 通过对象定义props
props: {
	自定义属性1: {
		// default设置默认值
		default:0,
		// type定义类型
		type:所需的基本数据类型,
		// 是否是必填项
		required:false //默认是false
		}
}

3.1 props是只读的

  • props中的数据可以直接在模板中使用,这点和data一样
  • 但props中的值是只读的,不可以直接修改props中的值,否则会报错如下
    在这里插入图片描述
  • 可以通过将props的值转存到data中(通过 this. 实现),来修改props的值
<!--  -->
<template>
  <div>
      和:{{sum}}
      <button @click="add">+</button>
  </div>
</template>

<script>
export default {
   name:'',
   props:['init'],
   data () {
    return {
        sum:this.init
    };
   },
   methods: {
       add() {
           this.sum++
       }
   },
}
</script>
<style lang='scss' scoped>
</style>

// 使用props的值
<count :init="9"></count>

3.2 props的default默认值

  • 如果需要定义props的默认值,需要通过default实现,此时props应该写为对象形式
  • 当在使用props时没有传递数据就会使用默认值
props: {
	自定义属性1: {
		// default设置默认值
		default:0
		}
}

3.3 props的type值类型

  • 在声明自定义属性时,可以通过type来自定义属性的值类型
props: {
        init: {
            default:0,
            type:Number
        }
    },
  • 使用时如果传递的不是规定的类型,就会报错
<count init="9"></count>

在这里插入图片描述

  • 如果不使用v-bind,init=‘9’中传递的是一个字符串’9’,通过v-bind就会解析为数值,正确写法
<count :init="9"></count>

3.4 props required必填项

  • 设置了required和default,但是没有传值,也会报错,required只会校验有没有传值,不会去看默认值

4. 样式冲突问题

  • vue组件中的样式会在全局生效,容易造成多个组件的样式冲突
  • 可以通过给style标签添加scoped属性,限定样式只对当前组件生效
<style lang='scss' scoped>
</style>
  • scss/sass/less中使用 /deep/ 样式穿透

5. 组件中的数据共享

  • 组件之间常见的关系为父子关系、兄弟关系

5.1 父子组件传值

在这里插入图片描述

5.1.1 父向子传:props
  • 父组件提供数据(v-bind),子组件通过props接收
  • 子组件不能直接修改父组件传过来的值
    在这里插入图片描述
5.1.2 子传父

子组件通过自定义事件向父组件传值

  1. 子组件通过 this.$emit(‘自定义事件’,要传递的数据)
  2. 父组件中通过 @自定义事件=“事件名” 的形式来调用事件
  3. 父组件通过methods定义“事件名(子组件传递的数据){}”来使用子组件传递的数据
<!-- 父组件 -->
<template>
  <div id="app">
   <test :message="msg" @changeParent="getSonData"></test>
   <hr>
   <div>
     <p>父组件接收子组件传过来的值:{{countFromSon}}</p>
   </div>
  </div>
</template>

<script>
import Test from '@/components/test.vue'
export default {
  name: 'App',
  data() {
    return {
      msg:"hello,我是父组件中的数据!",
      // 接收子组件的值
      countFromSon:0
    }
  },
  components: {
    Test
  },
  methods: {
    getSonData(val) {
      this.countFromSon = val
    }
  },
}
</script>

<style lang="scss">
</style>

<!-- 子组件 -->
<template>
  <div>
      <div>来自父组件的值:{{message}}</div>
      <hr>
      <div>
          <p>{{sonMsg}}:{{count}}</p>
          <button @click="add">+</button>
      </div>
  </div>
</template>

<script>
export default {
   name:'',
   props:{
       message: {
           type:String
       }
   },
   data () {
    return {
        sonMsg:"我是子组件的数据!",
        count:0
    };
   },
   methods: {
       add() {
           this.count++
           this.$emit('changeParent',this.count)
       }
   },
}

</script>
<style lang='scss' scoped>
</style>

运行效果
在这里插入图片描述

5.2 兄弟组件传值——bus总线

vue2中兄弟组件通过EventBus

  1. 创建eventBus.js模块,并向外共享一个Vue的实例对象
  2. 在数据发送方,调用 bus.$emit(‘自定义事件’,要发送的数据) 方法触发自定义事件
  3. 在数据接收方,调用 bus.$on(‘自定义事件’,,事件处理函数) 方法注册一个自定义事件
    在这里插入图片描述

6. 数组方法的回顾

6.1 some()循环

从数组中查找某个元素用some,可以通过return true来终止循环

const ARR = ['红红','蓝蓝','白白','灰灰','绿绿','紫紫']
        // 查找'白白'对应的索引
        ARR.some((item,index) => {
            console.log("查找了1次")
            if(item === '白白') {
                console.log(index)
                return true
            }
        })

6.2 every()

判断数组中的每一项是否都满足条件,如果全部都满足返回true,只要有一项不满足则返回false

const arr = [
            {id:1,name:"西瓜",status:true},
            {id:2,name:"草莓",status:true},
            {id:3,name:"哈密瓜",status:true}
        ]
        // 判断每一项的status是否为true
        const result = arr.every(item => item.status)
        console.log(result) //true

6.3 reduce()

将每一次循环的结果进行累加,返回最后累加的结果

语法:

reduce( (累加的结果,当前循环项) => {},初始值)

前一次循环累加的结果会作为下一次的初始值,直到循环结束,返回最终累加结果

示例:

const arr = [
            {id:1,name:"西瓜",status:true,price:10,count:2},
            {id:2,name:"草莓",status:false,price:30,count:3},
            {id:3,name:"哈密瓜",status:true,price:20,count:5}
        ]
        // 计算数组中status为true的水果的总价
        const newArr = arr.filter( item => item.status)
        const res =  newArr.reduce((total,item) => {
           return total += item.price * item.count
        },0)
        console.log(res) // 120

7. 组件的拆分

  • 组件的拆分要根据“单一原则”判定范围,一个组件负责一个功能,如果需要更多功能,需要拆分为更小的组件
  • 组件的特点:可复用、可维护、可组合

四、vue生命周期

包含创建 、 运行 、销毁三个阶段
vue生命周期
在这里插入图片描述

  • created 常用来发起ajax请求
  • 如果要操作DOM元素,要在mounted之后(包含mounted)
  • 如果要在数据发生变化后操作最新的DOM要在updated中实现
<script>
export default {
   name:'',
   props:['info'],
   data () {
    return {
      msg:"hello!"
    };
   },
   methods: {
     show() {
       console.log("methods方法")
     }
   },
  //  beforeCreate 
  // 此时props、data、methods还没有被创建,是不可使用的
  beforeCreate() {
    console.log("beforeCreate此时拿不到任何数据")
  },
  // created
  // 此时props、data、methods已经被创建好,但还不可以操作DOM
  // 该阶段常用来发起ajax请求
  created() {
    console.log(this.info)
    console.log(this.msg)
    this.show()
  },
  // beforeMount
  beforeMount() {
    console.log("beforeMount,此时DOM还没有渲染")
  },
  // mounted
  mounted() {
    console.log("mounted!此时DOM已经渲染完成")
    console.log(this.$el)
  },
  // beforeUpdate
  // 当组件的数据发生变化时才会触发
  beforeUpdate() {
    console.log("beforeUpdate!数据发生了变化")
  },
  // updated
  // 根据最新的数据,重新渲染dom
  // 如果要在数据发生变化后操作最新的DOM要在updated中实现
  updated() {
    
  },
  // beforeDestroy
  // 将要销毁组件
  beforeDestroy() {
    
  },
  // destroyed
  // 组件销毁完成
  destroyed() {
    
  },
}
</script>

五、ref引用

vue中几乎不需要操作DOM,只需要维护好数据。如果一定要操作DOM可以通过ref实现。
每个vue组件的实例上都包含一个 $refs对象,里面存储着对应的DOM元素或者组件的引用。默认情况下,组件的 $refs 指向一个空对象。

$开头的都是vue内置成员,都是程序员可用的

  • ref的名字不可以重复,要是唯一的

1. ref引用DOM

<div class="root_box" ref="rootBox">
      <h2>这是根组件</h2>
      <button @click="changeBtn">切换背景颜色</button>
    </div>
methods: {
     changeBtn() {
       this.$refs.rootBox.style.backgroundColor = "pink"
     }
   },

2. ref引用组件实例

在这里插入图片描述

3. this.$nextTick(callback)应用场景

  • this.$nextTick 将回调延迟到下次DOM更新循环之后执行。在修改数据之后立即使用它,然后等待DOM更新
  • 即:等组件的DOM更新完毕,再执行callback回调函数,以保证cb回调函数可以操作到最新的DOM元素
 methods: {
    // ...
    example() {
      // 修改数据
      this.message = 'changed'
      // DOM 尚未更新
      this.$nextTick(function() {
        // DOM 现在更新了
        // `this` 被绑定到当前实例
        this.doSomethingElse()
      })
    }
  }

六、动态组件

  • 含义:动态切换组件的显示与隐藏
  • 通过 < component :is=“组件名”> 组件实现动态组件的渲染,其中 is 用来指定要渲染的组件

1. keep-alive的使用

  • 使用< component :is=“组件名”>时,当组件名发生改变时,原有的组件会被销毁
  • 此时可以通过keep-alive保持状态
  • keep-alive可以把内部的组件进行缓存
<keep-alive>
       <component :is="componentId"></component>
    </keep-alive>

2. keep-alive的生命周期函数

  • 组件被缓存时,会触发组件的deactived生命周期函数
  • 组件被激活时,会触发组件的actived的生命周期函数,组件第一次被创建就会激活一次

3. keep-alive的include和exclude

  • include:指定要缓存的组件,只有名称匹配的组件被缓存,多个组件之间用逗号分隔
  • exclude:指定不被缓存的组件,和include不可以同时使用
<keep-alive exclude="Right">
       <component :is="componentId"></component>
    </keep-alive>

4. 组件注册名称和组件声明时name的区别

  • 在声明组件时,如果没有指定name值,则组件的名称就默认是注册时的注册名
  • 当提供了name,组件名就是name名
  • 建议在创建组件时设置name值
name:'LeftOwn',

在这里插入图片描述

  • 组件的注册名,一般应用于:以标签的形式,将注册好的组件渲染和使用到页面结构
  • 组件声明时的name名,一般应用于:结合< keep-alive>标签实现组件缓存功能,以及在调试工具中看到组件的name名
<keep-alive include="LeftOwn">
       <component :is="componentId"></component>
    </keep-alive>

七、插槽 slot

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

1. 普通插槽

<!-- 父组件 -->
<子组件名>
     需要的HTML结构
</子组件名>
<!-- 子组件 -->
<template>
     <div>
         <!-- 定义插槽(占个位,等组件的使用者填充内容) -->
          <slot>插槽默认内容...当父组件没有传递内容时,就会显示默认值</slot>
     </div>
</template>

2. 具名插槽

  • 当有多个插槽时,可以通过name给每个插槽命名
  • 在使用时,通过 slot=“name” 指定传内容到名为name的插槽
<!-- 子组件 -->
<slot name="slotName">默认内容</slot>
<!-- 父组件 -->
<div slot="slotName">插槽内容</div>
  • 可以使用template包裹多个内容
  • template标签是一个虚拟的标签,起包裹作用,实际不会被渲染
  • template 可以和 v-slot:slotName 搭配使用,v-slot只能和template搭配使用
<!-- 子组件 -->
<slot name="slotName"></slot>
<!-- 父组件 -->
<left>
      <template v-slot:slotName>
        插槽内容
      </template>
    </left>
  • v-slot 可以简写为 #,即 #slotName

3. 作用域插槽

  • 数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
  • slot标签传值,然后通过 < template slot-scope=“scopeData”> 使用所传的值
 <slot name="slotName" :msg="要传的插槽内容!"></slot>
<template slot-scope="scopeData">
        <p>
          {{scopeData.msg}}
        </p>
</template>
  • 作用域插槽可以使用解构赋值
<category title="游戏">
      <template slot-scope="{msg}">
          <h4 >{{msg}}</h4>
      </template>
    </category>
  • 作用域插槽也可以命名

八、自定义指令

1. 私有自定义指令

在每个组件中,可以通过directives节点声明私有自定义指令,如果通过v-自定义指令名进行使用

<h1 v-color>根组件</h1>
 // 私有自定义指令
  directives: {
    color: {
      // 一旦绑定到元素就会触发bind方法
      // el代表所绑定的DOM
      bind(el) {
        el.style.color="red"
      }
    }
  }
  • 自定义指令的bind函数接收两个参数,第一个参数el为所绑定的DOM元素,第二个参数为binding对象,可以通过binding.value获取所传值
 <h3 v-color="color">自定义指令1</h3>
    <h3 v-color="'pink'">自定义指令2</h3>

 // 私有自定义指令
  directives: {
    color: {
      // 一旦绑定到元素就会触发bind方法
      // el代表所绑定的DOM
      bind(el,binding) {
        el.style.color= binding.value
      }
    }
  }
  • bind 函数只调用一次,后续DOM更新时不会再触发bind,update函数会在每次DOM更新时被调用
  • 如果bindupdate函数的逻辑完全一致,则对象格式的自定义指令可以简写为函数格式
directives: {
    color(el,binding) {
      el.style.color= binding.value
    }
  }

2. 全局自定义指令

//main.js

// 参数1:字符串,name表示全局自定义指令的名字
// 参数2:对象,用来接收指令的参数值
Vue.directive('name',(el,binding) => {
})

九、vuex

1. vuex的理解

1.1 是什么

用于集中式状态管理,可以进行数据的读写操作和多组件数据共享,相当于一个大型仓库,可以进行任意组件的通信

1.2 适用场景(数据共享)

  • 多组件依赖于同一状态
  • 来自不同组件的行为要变更同一状态

2. vuex原理

在这里插入图片描述

理解:vuex是一个餐厅(store)
vue Component 为客人,客人点菜(store.dispatch)
actions 为服务员,获取到客人点的菜,然后提交给后厨(store.commit)
mutations 为后厨,根据服务员点的菜,进行处理
state 为点的菜

3. vuex环境搭建

  1. 安装

cnpm i vuex@3

  • vue2 使用的是 vuex3
  • vue3 使用的是 vuex4
  1. 创建store
    src目录下创建store文件夹,store文件夹下新建index.js
    index.js中的代码如下
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 使用vuex
Vue.use(Vuex)
// 创建actions,用于响应组件中的动作
const actions = {}
// 创建mutations,用于操作数据(state)
const mutations = {}
// 创建state,用于存储数据
const state = {}
// 创建并向外暴露(导出)store
export default new Vuex.Store({
    actions,
    mutations,
    state
})

  1. 引入store(main.js)
// 引入store
import store from '@/store'
new Vue({
  store, // 注册store
  render: h => h(App),
}).$mount('#app')

如果是在main.js中使用vuex,此时会报如下错误:
在这里插入图片描述
错误原因:会先执行所有的import,在store/index.js中vuex还没有被创建

4. vuex案例(熟悉vuex的使用)

  1. state中存储需要共享的数据
const state = {
    sum:0,
}
  1. 组件中通过dispatch触发需要的方法

this.$store.dispatch(‘方法名’,要传的数据)

addOdd() {
       this.$store.dispatch('oddAdd',this.num)
     },
  • actions没有任何业务逻辑,只是单纯的提交一个mutation时,可以不用通过dispatch触发action,而是直接在组件中commit需要的方法即可
increase() {
       this.$store.commit('ADD',this.num)
     },
  1. actions中定义方法,然后commit提交mutation
  • actions中方法的名字要和dispatch提交的方法名一致
  • actions中的方法接收两个参数
  • 第一个参数:上下文参数,需要到使用其中的commit,用于提交mutation
  • 可以通过解构赋值直接拿到commit,然后直接使用commit提交

add ( {commit} ,val)

  • 第二个参数:接收到dispatch所传的值

  • actions中 可以处理业务逻辑

const actions = {
    // 第一个参数:上下文
    // 第二个参数:所传的值
    oddAdd(context,val) {
        if(context.state.sum % 2) {
            context.commit('ADD',val)
        }
    }
}
  • commit提交的方法,一般方法名要大写
  1. mutation 中定义actions传递的方法,然后进行相应的处理
// 创建mutations,用于操作数据(state)
const mutations = {
   ADD(state,val) {
        console.log(val)
        state.sum += val
   }
}
  • mutations中一般不进行任何业务逻辑,只需要根据需求进行加工处理

5. getters的使用

  1. 用于加工处理state中的数据
  2. 创建
const getters = {
    xxx(state) {
        return xxxxxx
    }
}
export default new Vuex.Store({
    ....
    getters
})
  • 接收state参数,返回需要的值
  1. 组件中读取

$store.getters.xxx

6. 优化:四个map方法的使用

  • 当在组件中频繁的需要state或getters中数据,即书写 $store.xxx.xxx时,可以通过vuex的mapState和mapGetters方法从state或getters获取数据

import {mapState,mapGetters} from ‘vuex’

6.1 mapState()

把state中的数据映射为计算属性

  • 对象写法:

…mapState({key1:‘value1’,key2:'value2…}),

value要和state中的key名保持一致
value和key都是字符串,key可以不加引号,value必须加引号

computed: {
 	...mapState({sum:'sum',school:'school',con:'con'}),
 }
  • 数组写法

…mapState([‘xxx’,‘xxx’,‘xxx’]),

多个内容用逗号分隔,且xxx要与state中的key名保持一致

computed: {
...mapState(['sum','school','con']),
}

6.2 mapGetters()

把getters中的数据映射为计算属性,语法和mapState一致

  • 数组写法
computed: {
...mapGetters(['bigSum'])
}
  • 对象写法
computed: {
...mapGetters({bigSum:'bigSum'})
}

6.3 mapActions

生成与actions对话的方法,即生成包含 $store.dispatch(xxx) 的函数

语法和mapState一致,但是需要在调用方法时传参

  • 对象写法
...mapActions({addOdd:'oddAdd'})
<button @click="addOdd(num)">和为奇数再加</button>
  • 数组写法
...mapActions(['oddAdd'])

6.4 mapMutations

生成与mutations对话的方法,即生成包含 $store.commit(xxx) 的函数
语法和mapState一致,但是需要在调用方法时传参

  • 对象写法
...mapMutations({increase:'ADD',substraction:'SUB'}),
<button @click="increase(num)">+</button>
      <button @click="substraction(num)">-</button>
  • 数组写法
...mapMutations(['ADD','SUB'])

7. vuex的模块化(modules)

  • 让代码更好维护,让多种数据分类更加明确
  • 每个组件的state、actions、mutations、getters单独管理,最后引入
  1. 在store目录下建立需要使用vuex的组件文件夹,如:home/index.js、search/index.js,分别管理需要的vuex内容
    在这里插入图片描述
const state = {
   ...
}
const mutations = {
   ...
}
// getters:计算属性,项目中主要用于简化仓库中的数据
const getters = {
    ...
}
const actions = {
   ...
}

export default {
    state,
    mutations,
    getters,
    actions
}
  1. store/index.js中引入
import Vue from 'vue'
import Vuex from 'vuex'
// 使用一次vuex
Vue.use(Vuex)
// 引入小仓库
import Home from './Home'
import Search  from './Search'
// 对外暴露store的一个实例
export default new Vuex.Store({
    // 实现vuex仓库模块式开发存储数据
    modules: {
        Home,
        Search
    }
})

注: 项目不大时,可以直接在store/index.js中分模块

const countAbout = {
     namespaced:true,//开启命名空间
     state:{x:1},
     mutations: { ... },
     actions: { ... },
     getters: {
       bigSum(state){
          return state.sum * 10
       }
     }
   }
   
   const personAbout = {
     namespaced:true,//开启命名空间
     state:{ ... },
     mutations: { ... },
     actions: { ... }
   }
   
   const store = new Vuex.Store({
     modules: {
       countAbout,
       personAbout
     }
   })
  1. 开启命名空间(namespace)后,组件中读取state数据:
// 直接读取
 this.$store.state.模块名.要读取的state数据
 
// mapState读取
 ...mapState('模块名',['xxx','xxx','xxx']),
  1. 开启命名空间后,组件中读取getters数据:
// 直接读取
this.$store.getters['模块名/要读取的getters']
// mapState读取
 ...mapGetters('模块名',['xxx'])
  1. 开启命名空间后,组件中调用dispatch
// 直接dispatch
this.$store.dispatch('模块名/actions方法名',数据)
// 借助mapActions
...mapActions('模块名',{xxx:'xxxx',xxx:'xxxxx'})
  1. 开启命名空间后,组件中调用commit
// 直接commit
this.$store.commit('模块名/xxx',数据)
// mapMutations
 ...mapMutations('模块名',{xxx:'xxxxx'...})

十、 路由(vue-router)

在这里插入图片描述

1. 相关理解

1.1 vue-router的理解

vue的一个插件库,用来实现SPA应用

1.2 SPA的理解

  • 单页web应用(single page web application,SPA)
  • 整个应用只有一个index.html页面
  • 点击页面中的导航不会刷新整个页面,知乎进行局部更新
  • 数据需要通过ajax获取

1.3 路由的理解

  1. 是什么?
  • 一个路由就是一组映射关系(key-value)
  • key为路径,value为function或component
  1. 前端路由
  • value是component,用于展示页面内容
  • 当浏览器的路径改变时,会显示对应的组件

2. 基本路由

2.1 使用步骤

  1. 安装

cnpm i vue-router@3 --save

  1. 配置路由(router),新建 src / router / index.js
import Vue from 'vue'
// 引入路由
import VueRouter from 'vue-router'
// 使用
Vue.use(VueRouter)
// 引入组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
// 向外导出
export default new VueRouter({
    routes: [
        {
            path:'/home',
            component:Home
        },
        {
            path:'/about',
            component:About
        },
    ]
})
  1. 注册路由(main.js)
// 引入路由
import router from '@/router'

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

备注:

  • src/views常用来存储路由组件
  • src/components 用来存储非路由组件和全局组件
  • 点击切换时,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载
  • 每个组件都有自己的 $route 属性,里面存储着自己的路由信息。
  • 整个应用只有一个router,可以通过组件的 $router 属性获取到。
  1. 路由的跳转
  • 声明式导航:router-link,必有to属性
<router-link to="/路径">xxxxx</router-link>
  • 编程式导航:push、replace,编程式导航除了路由跳转还可以进行其他的业务逻辑
  1. 指定展示位置
<router-view></router-view>

3. 嵌套路由(多级路由)

  • 路由里的路由(套娃)
  • 通过给路由设置children属性来配置子路由
  • 子路由的path不能加斜杠( / )
routes: [
        {
            path:'/home',
            component:Home,
            children:[
                {
                    path:'news',
                    component:New
                },
                {
                    path:'messages',
                    component:Message,
                    children:[
                        {
                            path:'detail',
                            component:Detail
                        }
                    ]
                },
            ]
        },
    ]
  • 跳转要写完整路径
<router-link to='/home/news'>News</router-link>
  • 需要展示的区域设置
 <router-view></router-view>

4. 路由传参

4.1 query参数

  • 不属于路径中,不需要占位,通过问号(?)拼接,多个参数用 & 连接
<router-link :to="`/home/messages/detail?id=${item.id}&title=${item.title}`">{{item.title}}</router-link>
  • 通过 $route.query.xxx 获取所传的参数
<p>标题:{{$route.query.title}}</p>

4.2 params参数

  • 属于路径中的一部分,在配置路由时要进行占位
{
      path:'detail/:id/:title',
      component:Detail
}
  • 使用时直接在路径后面拼接
<router-link :to="`/home/messages/detail/${item.id}/${item.title}`">{{item.title}}</router-link>
  • 通过 $route.params.xxx 获取所传的参数

4.3 路由传参的几种形式

4.3.1 字符串形式

```javascript
// 方法一:通过字符串直接传参
      // params形式
       this.$router.push('/search/'+this.searchValue)
      // query形式
       this.$router.push('/search?key='+this.searchValue)
      // params+query
      this.$router.push('/search/'+this.searchValue+'?key='+this.searchValue.toUpperCase())
4.3.2 模板字符串
this.$router.push(`/search/${this.searchValue}?k=${this.searchValue.toUpperCase()}`)
4.3.3 对象写法(最常用的)

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

// 通过name命名路由
name:'search',
path:'/search/:keyword'
// 方法三:对象的写法(常用的),要对路由命名(name)
      this.$router.push({
        name:'search',
        params:{
          keyword:this.searchValue
        },
        query:{
          key:this.searchValue.toUpperCase()
        }
      })

5. 命名路由

  • 给路由添加name属性
routes: [
        {
            name:'about',
            path:'/about',
            component:About
        },
        
    ]
  • 作用:可以简化路由的跳转,简化后直接通过名字跳转
<!--简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome">跳转</router-link>

<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'hello'}">跳转</router-link>

<!--简化写法配合传递参数 -->
<router-link 
	:to="{
		name:'hello',
		query:{
		   id:666,
            title:'你好'
		}
	}"
>跳转</router-link>

6. 路由的props配置

  • 作用:让路由组件更方便的收到参数
  • 在配置路由时进行配置

6.1 对象写法

  • 值为对象,该对象中所有key-value都会以props的形式传递给路由组件
  • 缺点:传递的是死数据,预先设置好的,不会改变
props: {
	id:'0001',
	title:'标题'
 }

6.2 布尔值写法

  • 值为布尔值,值为true,则会把该路由组件所有接收到的params参数,以props的形式传给detail组件
  • 路由组件中通过props接收
 props:['id','title'],
<p>标题:{{title}}</p>

6.3 函数写法

  • 接收 $route 参数
props($route) {
      return {
               id:$route.query.id,
               title:$route.query.title
               }
 }
  • 接收参数时可以解构赋值
props({query}) {
      return {
             id:query.id,
             title:query.title
            }
 }
  • 接收参数时使用解构赋值的连续写法(连续解构赋值),不推荐改写法,语义化不够好
props({query:{id,title}}) {
   return {
          id:id,
          title:title
        }
}
  • 该函数返回的对象中每一组key-value都会通过props传给Detail组件

7. 路由跳转的方式

7.1 声明式导航 router-link

  • 通过router-link实现,必有to属性
<router-link to="路径">xxxxx</router-link>
7.1.1 router-link的replace属性
  • 作用:控制路由跳转时操作浏览器历史记录的模式
  • 浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  • 如何开启replace模式
<router-link replace .......>xxx</router-link>

7.2 编程式导航 push、replace

  • push、replace,编程式导航除了路由跳转还可以进行其他的业务逻辑
  • 通过 this.$router.push() 或 this. $router.replace() 使用
this.$router.push({
               name:'message',// 和路由配置中的name保持一致
               query: {
                   id:item.id,
                   title:item.title
               }
           })
  • 后退:this.$router.back()
  • 前进:this.$router.forward()
  • this.$router.go(num),num为正n表示向前走n步,为负数表示后退n步

8. 缓存路由组件

8.1 基本使用

  • 作用:让不展示的路由组件保持挂载,不被销毁。
  • 通过 keep-alive 实现
<keep-alive include="需要缓存的组件的名字(name)"> 
    <router-view></router-view>
</keep-alive>
  • 不加include表示缓存所有的组件
  • 需要缓存多个时,动态绑定数组
<keep-alive :include="[name1,name2,....]"> 
    <router-view></router-view>
</keep-alive>

8.2 两个新的生命周期

  • 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态
  • activated 路由组件被激活时触发。
  • deactivated 路由组件失活时触发

9. 路由守卫

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

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

9.1 全局前置守卫

  • 路由向外导出前进行配置
  • 通过beforeEach(),在每次路由切换之前和初始化会调用里面的函数,该函数接受三个参数 to,from,next
  • 必须加上 next() 才会进行跳转,继续接下来的操作
const router =  new VueRouter({
    routes: [.....]
})

// 全局前置路由守卫
router.beforeEach((to,from,next)=>{
	console.log('beforeEach',to,from)
	// meta:路由元信息,需要在routes中配置
	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
		if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
			next() //放行
		}else{
			alert('暂无权限查看')
			// next({name:'guanyu'})
		}
	}else{
		next() //放行
	}
})
export default router

9.2 全局后置路由守卫

  • 初始化时执行、每次路由切换后执行
  • 没有next参数
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
	console.log('afterEach',to,from)
	if(to.meta.title){ 
		document.title = to.meta.title //修改网页的title
	}else{
		document.title = 'vue_test'
	}
})

9.3 独享路由守卫

  • 每个路由单独的守卫
  • beforeEnter,包含to、from、next三个参数
  • 写在routes配置中
beforeEnter(to,from,next){
	console.log('beforeEnter',to,from)
	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
		if(localStorage.getItem('school') === 'atguigu'){
			next()
		}else{
			alert('暂无权限查看')
			// next({name:'guanyu'})
		}
	}else{
		next()
	}
}

9.4 组件内路由守卫

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

  • 离开守卫:beforeRouteLeave ,通过路由规则,离开该组件时被调用
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
  • 和methods、data…平级,即写在组件 export default { } 中

10. hash和history模式

10.1 hash模式

  • hash模式:浏览器地址栏中,#及其后面的内容就是hash值
  • hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器
  • 默认是hash模式
  • 地址中永远带着#号,不美观
  • 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
  • 兼容性较好。

10.2 history模式

  • 地址干净,美观 。
  • 兼容性和hash模式相比略差。
  • 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
  • router中配置

mode: ‘history’

十一、 Vue UI组件库

1. 移动端UI

  1. Vant
  2. Cube UI
  3. Mint UI

2. PC端UI

  1. Element UI
  2. IView UI
Logo

前往低代码交流专区

更多推荐