Vue

1、vue概述


Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面渐进式框架,发布于2014年2月。与其它大型框架不同的是,Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库(如:vue-router:跳转,vue -resource:通信,vuex:管理)或既有项目整合

官网

构建用户界面

  • 指令:用于辅助开发者渲染页面的模板语法
  • 数据驱动视图:数据源变化,页面结构自动重新渲染
  • 事件绑定:用于处理用户和网页之间的交互行为

官方给 vue 的定位是前端框架,因为它提供了构建用户界面的一整套解决方案(俗称 vue 全家桶)

  • vue(核心库)
  • vue-router(路由方案)
  • vuex(状态管理方案)
  • vue 组件库(快速搭建页面 UI 效果的方案)

以及辅助 vue 项目开发的一系列工具:

  • vue-cli(npm 全局包:一键生成工程化的 vue 项目 - 基于 webpack、大而全)
  • vite(npm 全局包:一键生成工程化的 vue 项目 - 小而巧)
  • vue-devtools(浏览器插件:辅助调试的工具)
  • vetur(vscode 插件:提供语法高亮和智能提示)

Vue3相关技术栈

  • ECMAScript6 VUE3中大量使用ES6语法
  • Nodejs 前端项目运行环境
  • npm 依赖下载工具
  • vite 前端项目构建工具
  • VUE3 优秀的渐进式前端框架
  • router 通过路由实现页面切换
  • pinia 通过状态管理实现组件数据传递
  • axios ajax异步请求封装技术实现前后端数据交互
  • Element-plus 可以提供丰富的快速构建网页的组件仓库

1.1、前端三板斧

  • HTML (结构) :超文本标记语言(Hyper Text Markup Language),决定网页的结构和内容
  • CSS (表现) :层叠样式表(Cascading Style Sheets),设定网页的表坝样式
  • JavaScript (行为) :是一种弱类型脚本语言,其源代码不需经过编译,而是由浏览器解释运行,用于控制网页的行为

1.2、JavaScript框架

  • jQuery:众所周知的JavaScript框架,优点是简化了DOM操作,缺点是频繁操作DOM,影响前端性能;在前端眼里使用它仅仅是为了兼容IE6、7、8
  • Angular:Google收购的前端框架,由一群Java程序员开发,基特点是将后台的MVC模式搬到了前端并增加了模块化开发的理念,与微软合作,采用TypeScript语法开发;对后台程序员友好,对前端程序员不太友好
  • React:Facebook 出品,一款高性能的JS前端框架;特点是提出了***新概念【虚拟DOM】***于减少真实DOM操作,在内存中模拟DOM操作,有效的提升了前端渲染效率;缺点是使用复杂,需要额外学习一门【JSX】语言
  • Vue:一款渐进式JavaScript框架,所谓渐进式就是逐步实现新特性的意思,如实现模块化开发、路由、状态管理等新特性。其特点是综合了Angular (模块化)和React (虚拟DOM)的优点
  • Axios :前端通信框架,因为Vue的边界很明确,就是为了处理DOM,所以并不具备通信能力,此时就需要额外使用一个通信框架与服务器交互;当然也可以直接选择使用jQuery提供的Ajax通信功能

1.3、UI框架

  • Ant-Design:阿里巴巴出品,基于React的UI框架
  • ElementUI: iview、ice,饿了么出品,基于Vue的UI框架
  • Bootstrap: Twitter 推出的一个用于前端开发的开源工具包
  • AmazeUI:又叫"妹子UI",一款HTML5跨屏前端框架

1.4、JavaScript 构建工具

  • Babel:JS 编译工具,主要用于浏览器不支持的ES新特性,比如用于编译TypeScript
  • webpack:前端项目工程化的具体解决方案

1.5、MVVM模式

MVVM源自于经典的MVC (Model-View-Controller) 模式。MVVM的核心是ViewModel层,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起

  • 该层向上与视图层进行双向数据绑定
  • 向下与Model层通过接口请求进行数据交互

在这里插入图片描述

MVVM已经较为成熟,当下流行的MVVM框架有vue.jsAngular.js

1.5.1、View

View是视图层,也就是看到的用户界面。前端主要由HTML CSS来构建,为了更方便地展现ViewModel或者Model 层的数据,已经产生了各种各样的前后端模板语言,比如FreeMarker、Thymeleaf等等,各大MVVM框架如Vue.js, AngularJS, EJS 等也都有自己用来构建用户界面的内置模板语言

1.5.2、Model

Model是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控主要围绕数据库系统展开。这里的难点主要在于需要和前端约定统一的接口规则

1.5.3、ViewModel

ViewModel是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的Model数据进行转换处理,做二次封装,以生成符合View层使用预期的视图数据模型。MVVM框架已经把最繁琐的一块做好了,开发者只需要处理和维护ViewModel,更新数据视图就会自动得到相应更新,真正实现事件驱动编程

View层展现的不是Model层的数据,而是ViewModel 的数据,由ViewModel 负责与Model层交互,这就完全解耦了View层和Model层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环

1.6、为什么要使用vue.js

  • 轻量级、体积小:压缩后只有33k
  • 更高的运行效率:基于虚拟DOM,一种可以预先通过JavaScript进行各种计算,把最终的DOM操作计算出来并优化的技术,由于这种DOM操作属于预处理操作,并没有真实的操作DOM,所以叫做虚拟DOM
  • 双向数据绑定:让开发者不用再去操作DOM对象,把更多的精力投入到业务逻辑上
  • 生态丰富、学习成本低:市场上拥有大量成熟、稳定的基于vue.js的ui框架及组件,拿来即用实现快速开发;对初学者友好、入门容易、学习资料多

1.7、MVVM的实现者

  • M:模型层(Model),表示当前页面渲染时所依赖的数据源
  • V:视图层(View),表示当前页面所渲染的 DOM 结构
  • VM:视图模型(ViewModel):连接视图和数据的中间件,Vue实例对象

在MVVM架构中,是不允许数据和视图直接通信的,只能通过ViewModel来通信,而ViewModel就是定义了一个Observer观察者

  • ViewModel能够观察到数据的变化。并对视图对应的内容进行更新
  • ViewModel能够监听到视图的变化,并能够通知数据发生改变

Vue.js就是一个MVVM的实现者。他的核心就是实现了DOM监听与数据双向绑定

核心:数据驱动视图双向数据绑定组件化

优点:借鉴了AngulaJS 的模块化开发和React的虚拟Dom ,虚拟Dom就是把Dom操作放到内存中去执行!(集大成者)

遵循SoC关注度分离原则,Vue是纯粹的视图框架,并不包含比如Ajax之类的通信功能,为了解决通信问题,需要使用Axios框架做异步通信;

Vue的开发都是要基于Node.js,实际开发采用vue-cli脚手架开发,vue-router路由;vuex做状态管理; Vue UI界面我们一般使用**ElementUl(饿了么出品)**来快速搭建前端项目

2、双向数据绑定


Vue.js是一个MVVM枢架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也是Vue.js较为特殊的功能

需要注意的是:v-model 会忽略所有表单元素的 valuecheckedselected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值

2.2.1、单行文本框
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>数据双向绑定</title>
        <!-- 导入vue.js -->
        <script src="../../js/vue.min.js"></script>
    </head>
    <body>
        <!-- 容器 -->
        <div id="app">
            <p>
                <label>用户名:</label>
                <input type="text" v-model="msg"/>
                <!-- {{ }} 插值表达式 主要用于渲染标签体内容 -->
                <span style="color: coral">值为:{{msg}}</span>
            </p>
        </div>

        <script>
            let vm = new Vue({
                el: "#app",
                data: {
                    msg: "Hello vue",
                },
            });
        </script>
    </body>
</html>
2.2.2、多行文本
<!--view层:模板-->
<div id="app">
    <!--输入框的值和{{value}}的值动态绑定,实时刷新,可以使用v-model绑定value-->
    <p>
        <label>个人简介:</label>
        <textarea cols="20" rows="10" v-model="value"></textarea>
        <span style="color: coral">值为:{{value}}</span>
    </p>
</div>

<script>
    let vm = new Vue({
        el: "#app",
        data: {
            value: "hello vue",
        },
    });
</script>
2.2.3、单选框
<!--view层:模板-->
<div id="app">
    <!--输入框的值和{{value}}的值动态绑定,实时刷新,可以使用v-model绑定value-->
    <p>
        <label>性别:</label>
        <input type="radio" name="gender" value="" v-model="checked"/><input type="radio" name="gender" value="" v-model="checked"/><span style="color: coral">选中了:{{checked}}</span>
    </p>
</div>

<script>
    let vm = new Vue({
        el: "#app",
        data: {
            checked: "男",
        },
    });
</script>
2.2.4、复选框
<!--view层:模板-->
<div id="app">
    <!--输入框的值和{{value}}的值动态绑定,实时刷新,可以使用v-model绑定value-->
    <p>
        <label>爱好:</label>
        <input type="checkbox" value="swim" v-model="checkValue"/> 游泳
        <input type="checkbox" value="football" v-model="checkValue"/> 篮球
        <input type="checkbox" value="playGame" v-model="checkValue"/> 游戏
        <span style="color: coral">选中了:{{checkValue}}</span>
    </p>
</div>

<script>
    let vm = new Vue({
        el: "#app",
        data: {
            checkValue: [],
        },
    });
</script>
2.2.5、下拉列表
<!--view层:模板-->
<div id="app">
    城市:
    <select name="city" v-model="selected">
        <option value="" disabled>---请选择---</option>
        <option name="henan">河南</option>
        <option name="luoyang">洛阳</option>
        <option name="yichuan">伊川</option>
    </select>
    <p>选中了:{{selected}}</p>
</div>

<script>
    let vm = new Vue({
        el: "#app",
        // Model:数据
        data: {
            selected: '',
        },
    });
</script>

2.3、单向数据绑定与双向数据绑定

单向数据绑定

  • 具体语法:v-bind:href ="xxx" 或简写为 :href
  • 特点:数据只能从 data 流向页面

双向数据绑定

  • 具体语法:v-model:value="xxx" 或简写为 v-model="xxx"
  • 特点:数据不仅能从 data 流向页面,还能从页面流向data

2.4、过滤器&计算属性&watch侦听

何为过滤器?

过滤器(Filters)是 vue 为开发者提供的功能,常用于文本的格式化。过滤器可以用在两个地方:插值表达式 和 v-bind 属性绑定

过滤器应该被添加在 JavaScript 表达式的尾部,由“管道符”进行调用

<script>
    new Vue({
        el: "#app",
        data: {
            time: new Date()
        },
        filters: {
            // 过滤器本质也是一个函数
            dateFormat(value) {
                // 过滤器形参中的value值,永远是管道符前面的那个值
                console.log(value)
                // 过滤器必须要有返回值
                return dayjs(this.time).format("YYYY-MM-DD HH:mm:ss")
            }
        }
    })
</script>

在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。 如果希望在多个 vue 实例之间共享过滤器,则可以按照如下的格式定义全局过滤器:

<script>
    // 使用 Vue.filter() 定义全局过滤器
    // 参数一:全局过滤器的名称
    // 参数二:全局过滤器的处理函数
    Vue.filter('cap', (str) => {
        const first = str.substring(0, 2).toUpperCase()
        const other = str.slice(2)
        return first + other
    })

    new Vue({
        el: "#app1",
        data: {
            msg: 'mr zhao'
        }
    })

    new Vue({
        el: "#app2",
        data: {
            msg: 'hello Vue.js'
        }
    })
</script>

注:一定要在Vue实例对象创建之前定义全局过滤器,如果全局过滤器和私有过滤器名称一致,按照就近原则,调用的是私有过滤器!

何为计算属性?

计算属性指的是通过一系列运算之后,最终得到一个属性值,一个数据的结果要依赖其他数据的变化来动态计算!

计算属性的特点

  • 计算属性的本质是一个函数,虽然计算属性在声明的时候被定义为方法,但在使用的时候被当做属性
  • 计算属性会缓存计算的结果,只有计算属性依赖的数据变化时,才会重新进行运算
  • 计算属性必须返回一个计算的结果
<div id="app">
    <div>
        <span>R:</span>
        <input type="text" v-model.number="r">
    </div>
    <div>
        <span>G:</span>
        <input type="text" v-model.number="g">
    </div>
    <div>
        <span>B:</span>
        <input type="text" v-model.number="b">
    </div>
    <hr>
    
    <div class="box" :style="{ backgroundColor: getColor }">
        {{ getColor}}
    </div>
    <button @click="show">按钮</button>
</div>

<script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
        el: '#app',
        data: {
            r: 0,
            g: 0,
            b: 0
        },
        methods: {
            show() {
                console.log(this.getColor)
            }
        },
        computed: {
            getColor() {
                // 动态生成 RGB 字符串 rgb(x,x,x)
                return `rgb(${this.r},${this.g},${this.b})`
            }
        }
    });
</script>
何为watch侦听?

watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作

<div id="app">
    <input type="text" v-model.lazy="uname">
    <span>{{uname}}</span>
</div>

<script>
    new Vue({
        el: "#app",
        data: {
            uname: '小白'
        },
        watch: {
            // 函数形式
            // uname(newValue, oldValue) {
                // console.log(newValue, oldValue)
                // 判断用户名是否被占用,假定数据库的用户名是 admin
                // if (!newValue) return alert('用户名不能为空!')
                // if (newValue === 'admin') return alert('用户名已被占用,请更换一个')
                //console.log('ok')
            // },

            // 对象形式
            uname: {
                // 立即触发当前的 watch 侦听器处理函数
                immediate: true,
                // 开启深度监视(监视对象) 
                deep: true,
                // 侦听器的处理函数
                handler(newValue, oldValue) {
                    if (!newValue) return alert('用户名不能为空!')
                    if (newValue === 'admin') return alert('用户名已被占用,请更换一个')
                    console.log('ok')
                }
            }
        }
    })
</script>
  • immediate 选项:默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要将 immediate 的值改为true,默认为false
  • deep 选项:如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要将 deep 的值改为true,默认为false

如果要侦听对象中的某个属性变化,可以采用'对象名.属性名'的方式!

<script>
    new Vue({
        el: "#app",
        data: {
            user: {
                uname: 'admin'
            }
        },
        watch: {
            'user.uname': {
                handler(newVal) {
                    console.log(newVal)
                }
            }
        }
    })
</script>
计算属性 vs 侦听器

计算属性和侦听器侧重的应用场景不同:

  • 计算属性侧重于监听多个值的变化,最终计算并返回一个新值
  • 侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值

2.5、常用内置指令

常用属性:

  • 内容渲染指令

    • v-text:更新元素的 textContent,会替换掉节点文本内容
    • v-html:把带有 HTML 标签的字符串,渲染为真正的 DOM 元素
    • {{ }}:插值只能用在元素的内容节点,无法用在属性节点
  • 条件渲染指令

    • v-if:如果为 true,当前标签才会输出到页面(动态创建和移除元素)
    • v-else-if:如果为 true,当前标签才会输出到页面
    • v-else:如果为前面判断为 false,当前标签才会输出到页面
    • v-show:通过控制 display 样式来控制显示/隐藏
  • 属性绑定指令

    • v-bind:属性名:绑定解析表达式,简写::href
  • 双向数据绑定指令

    • v-model:value:双向数据绑定,简写:v-model,因为 v-model 默认收集的就是 value 值
  • 循环渲染指令

    • v-for:遍历数组/对象
  • 事件绑定指令

    • v-on:绑定事件监件,简写:@事件名称
  • v-cloak:防止闪现,与 css 配合

  • 隐含属性对象:$event

事件修饰符:

  • 具体格式:@事件名称.prevent/stop
  • .prevent:阻止事件的默认行为 event.preventDefault()
  • .stop:停止事件冒泡 event.stopPropagation()
  • .once:事件只触发一次

按键修饰符

  • 具体格式:@事件名称.enter/delete/esc/space/tab/up/down/left/right
  • keycode:获取某个键的 keyCode 值(keyCode编码),不推荐使用
  • key:获取某个键的名称

表单修饰符:

  • 具体格式:@model.lazy/number/trim
  • lazy:输入框的值与数据同步,默认是在输入时触发(input事件),较为频繁,添加lazy修改符,在“change”时而非“input”时更新
  • number:自动将用户的输入值转为数值类型
  • trim:自动过滤用户输入的首尾空白字符

组件化:

  • 计算属性的特色,缓存计算数据
  • 组件内部绑定事件需要使用到this.$emit("事件名",参数);

2.6、Vue 实例生命周期

生命周期(Life Cycle)是指一个组件从创建 —> 运行 —> 销毁的整个阶段,强调的是一个时间段

生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行,强调的是一个时间点

在这里插入图片描述

vue 生命周期分析
  • 组件创建阶段
    • beforeCreate()
    • created()
    • beforeMount()
    • mounted()
  • 组件运行阶段
    • beforeUpdate()
    • updated()
  • 组件销毁阶段
    • beforeDestory()
    • destoryed()
常用的生命周期方法
  • created:组件在内存中被创建完毕,发送 ajax/axios最早的时期,请求数据
  • mounted:组件初次被渲染到浏览器中,操作DOM的最早时期
  • updated:组件在页面中被重新渲染(组件中的数据发生改变)
  • destoryed:组件在页面和内存中被销毁

3、工程化项目构建


3.1、Vue-cli(Vue2)

vue-cli是Vue 官方提供的标准化开发工具(开发平台),它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程

主要功能:

  • 统一的目录结构
  • 本地调试
  • 热部署
  • 单元测试
  • 集成打包上线

需要环境:Node.js

确认nodejs安装成功:

  • cmd输入node -v测试是否安装成功,显示版本号
  • cmd输入npm -v测试是否安装成功,显示版本号

安装位置:C:\Users\86186\AppData\Roaming\npm

安装vue-cli

vue-cli 是 npm 上的一个全局包,使用 npm install 命令,即可方便的把它安装到自己的电脑上

  • 全局安装@vue/cli(仅第一次执行),使用vue -V命令,查看当前脚手架版本号
    • npm install -g @vue/cli
  • 切换到要创建项目的目录,然后使用命令创建项目
    • vue create xxx
  • 启动项目
    • npm run serve

配置参考

3.2、Vite(Vue3)

在浏览器支持 ES 模块之前,JavaScript 并没有提供原生机制让开发者以模块化的方式进行开发。这也正是我们对 “打包” 这个概念熟悉的原因:使用工具抓取、处理并将我们的源码模块串联成可以在浏览器中运行的文件。时过境迁,我们见证了诸如 webpackRollupParcel 等工具的变迁,它们极大地改善了前端开发者的开发体验

  • 当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。
  • 包含数千个模块的大型项目相当普遍。基于 JavaScript 开发的工具就会开始遇到性能瓶颈:通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用模块热替换(HMR),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。

Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。https://cn.vitejs.dev/guide/why.html前端工程化的作用包括但不限于以下几个方面:

  1. 快速创建项目:使用脚手架可以快速搭建项目基本框架,避免从零开始搭建项目的重复劳动和繁琐操作,从而节省时间和精力。
  2. 统一的工程化规范:前端脚手架可以预设项目目录结构、代码规范、git提交规范等统一的工程化规范,让不同开发者在同一个项目上编写出风格一致的代码,提高协作效率和质量。
  3. 代码模板和组件库:前端脚手架可以包含一些常用的代码模板和组件库,使开发者在实现常见功能时不再重复造轮子,避免因为轮子质量不高带来的麻烦,能够更加专注于项目的业务逻辑。
  4. 自动化构建和部署:前端脚手架可以自动进行代码打包、压缩、合并、编译等常见的构建工作,可以通过集成自动化部署脚本,自动将代码部署到测试、生产环境等。
Vite创建Vue3工程化项目
Vite+Vue3项目的创建、启动、停止

1 使用命令行创建工程

  • 在磁盘的合适位置上,创建一个空目录用于存储多个前端项目
  • 用vscode打开该目录
  • 在vocode中打开命令行运行如下命令
npm create vite@latest
  • 第一次使用vite时会提示下载vite,输入y回车即可,下次使用vite就不会出现了
    在这里插入图片描述
  • 注意: 选择vue+JavaScript选项即可

2 安装项目所需依赖

  • cd进入刚刚创建的项目目录
  • npm install命令安装基础依赖
cd vue-quickstart
npm install

3 启动项目

  • 查看项目下的package.json
{
    "name": "vue-quickstart",
    "private": true,
    "version": "0.0.0",
    "type": "module",
    "scripts": {
        "dev": "vite",
        "build": "vite build",
        "preview": "vite preview"
    },
    "dependencies": {
        "vue": "^3.3.8"
    },
    "devDependencies": {
        "@vitejs/plugin-vue": "^4.5.0",
        "vite": "^5.0.0"
    }
}
npm run dev

5 停止项目

  • 命令行上 ctrl+c
Vite+Vue3项目的目录结构

1.下面是 Vite 项目结构和入口的详细说明:

在这里插入图片描述

  • public/ 目录:用于存放一些公共资源,如 HTML 文件、图像、字体等,这些资源会被直接复制到构建出的目标目录中。
  • src/ 目录:存放项目的源代码,包括 JavaScript、CSS、Vue 组件、图像和字体等资源。在开发过程中,这些文件会被 Vite 实时编译和处理,并在浏览器中进行实时预览和调试。以下是src内部划分建议:
    1. assets/ 目录:用于存放一些项目中用到的静态资源,如图片、字体、样式文件等。
    2. components/ 目录:用于存放组件相关的文件。组件是代码复用的一种方式,用于抽象出一个可复用的 UI 部件,方便在不同的场景中进行重复使用。
    3. layouts/ 目录:用于存放布局组件的文件。布局组件通常负责整个应用程序的整体布局,如头部、底部、导航菜单等。
    4. pages/ 目录:用于存放页面级别的组件文件,通常是路由对应的组件文件。在这个目录下,可以创建对应的文件夹,用于存储不同的页面组件。
    5. plugins/ 目录:用于存放 Vite 插件相关的文件,可以按需加载不同的插件来实现不同的功能,如自动化测试、代码压缩等。
    6. router/ 目录:用于存放 Vue.js 的路由配置文件,负责管理视图和 URL 之间的映射关系,方便实现页面之间的跳转和数据传递。
    7. store/ 目录:用于存放 Vuex 状态管理相关的文件,负责管理应用程序中的数据和状态,方便统一管理和共享数据,提高开发效率。
    8. utils/ 目录:用于存放一些通用的工具函数,如日期处理函数、字符串操作函数等。
  • vite.config.js 文件:Vite 的配置文件,可以通过该文件配置项目的参数、插件、打包优化等。该文件可以使用 CommonJS 或 ES6 模块的语法进行配置。
  • package.json 文件:标准的 Node.js 项目配置文件,包含了项目的基本信息和依赖关系。其中可以通过 scripts 字段定义几个命令,如 dev、build、serve 等,用于启动开发、构建和启动本地服务器等操作。
  • Vite 项目的入口为 src/main.js 文件,这是 Vue.js 应用程序的启动文件,也是整个前端应用程序的入口文件。在该文件中,通常会引入 Vue.js 及其相关插件和组件,同时会创建 Vue 实例,挂载到 HTML 页面上指定的 DOM 元素中。

2.vite的运行界面

  • 在安装了 Vite 的项目中,可以在 npm scripts 中使用 vite 可执行文件,或者直接使用 npx vite 运行它。下面是通过脚手架创建的 Vite 项目中默认的 npm scripts:(package.json)
{
  "scripts": {
    "dev": "vite", 		// 启动开发服务器,别名:`vite dev`,`vite serve`
    "build": "vite build", // 为生产环境构建产物
    "preview": "vite preview" // 本地预览生产构建产物
  }
}
  • 运行设置端口号:(vite.config.js)
// 修改 vite 项目配置文件 vite.config.js
export default defineConfig({
  plugins: [vue()],
  server:{
    port:8000
  }
})

4、Vue 组件


4.1、前端工程化

  • 模块化(js的模块化、CSS 的模块化、资源的模块化)
  • 组件化(复用现有的UI结构、样式、行为)
  • 规范化(目录结构的划分、编码规范化、接口规范化、文档规范化、Git 分支管理)
  • 自动化(自动化构建、自动部署、自动化测试)

前端工程化指的是:在企业级的前端项目开发中,把前端开发所需的工具、技术、流程、经验等进行规范化、标准化

好处:复用编码,提高运行效率,降低成本,更有利于项目的维护!

4.2、什么是组件?

组件是可复用的Vue 实例,它是一组可以重复使用的模板,通常一个应用会以一棵嵌套的组件树的形式来组织:

例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册局部注册

全局注册:在 vue 项目的 main.js 入口文件中,通过方法Vue.component('注册的名称',导入的组件名称) ,注册全局组件

局部注册:使用 components 节点注册组件

4.3、单文件组件

vue 组件的三个组成部分(3 个部分)

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

其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分

Goods.vue

<template>
    <div>
        <h2>商品名称:{{ gname }}</h2>
        <h2>商品价格:{{ gprice }}</h2>
        <h2>商品描述:{{ gdesc }}</h2>
    </div>
</template>

<script>
    export default {
        name: 'Goods',   // 可以使用name配置项指定组件在开发者工具中呈现的名字
        data() {
            return {
                gname: '红米K40',
                gprice: '1999',
                gdesc: '有点狠的真旗舰'
            }
        },
    }
</script>

<style lang="less" scoped>
    div {
        h2 {
            font-weight: normal;
            color: salmon;
        }
    }
</style>

User.vue

<template>
    <div>
        <h2>欢迎你,{{ uname }}</h2>
    </div>
</template>

<script>
    export default {
        name: 'User',
        data() {
            return {
                uname: '小白'
            }
        },
    }
</script>

App.vue

<template>
    <div>
        <!-- 使用组件 -->
        <User />
        <Goods />
    </div>
</template>

<script>
    // 导入 Goods 组件
    import Goods from './components/Goods.vue'

    export default {
        name: 'App',        	// 可以使用name配置项指定组件在开发者工具中呈现的名字
        components: { Goods }     // 注册组件(局部注册)
    }
</script>

main.js

// 该文件是整个项目的入口文件
// 引入 Vue
import Vue from 'vue'
// 引入 App 根组件,它是所有组件的父组件
import App from './App.vue'

// 关闭生产提示
Vue.config.productionTip = false

// 导入 User 组件
import user from './components/User.vue'
// 注册组件(全局注册)
Vue.component('User', user)

// 创建 Vue 实例对象
new Vue({
    el: '#app',
    // 把 render 函数指定的组件,渲染到 HTML 页面中
    render: h => h(App),
})
  • template
    • template 是 vue 提供的容器标签,只起到包裹性质的作用,它并不会被渲染为真正的 DOM 元素
    • template 中只能有一个根节点
  • script
    • 可以在该节点中封装组件的 JavaScript 业务逻辑
    • vue 组件中的 data 必须是一个函数,不能直接指向一个数据对象,因为那样会导致多个组件实例共用同一份数据的问题
  • style
    • 可以在该节点下编写样式,美化当前组件的 UI 结构
    • 如果在<style>标签上添加 lang=“less” 属性,即可使用 less 语法编写组件的样式

4.4、组件之间的通信

组件之间的关系

在项目开发中,组件之间的最常见的关系,分为如下两种:

  • 父子关系
  • 兄弟关系

父子组件之间的数据共享又分为:

  • 父 —> 子共享数据
  • 子 —> 父共享数据
4.4.1、ref 与 props
ref 引用

ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用

每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下, 组件的 $refs 指向一个空对象

  • 作用:用于给元素/组件打标识
  • 读取方式:this.$refs.xxx
<template>
    <div>
        <h1 v-text="msg" ref="title"></h1>
        <Goods ref="gds" />
    </div>
</template>

<script>
    // 导入 Goods 组件
    import Goods from './components/Goods.vue'

    export default {
        name: 'App',
        components: { Goods },
        data() {
            return {
                msg: '欢迎学习Vue.js'
            }
        },
        mounted(){
            console.log(this.$refs.title)       // 获取的是真实DOM元素
            console.log(this.$refs.gds)         // 获取的是 Goods 组件的实例对象(vc)
        }
    }
</script>
props 自定义属性
  • 作用:用于父组件给子组件传递数据(通信)
  • 方式一:只指定名称
    • props:['name', 'gender', 'age']
  • 方式二:指定名称和类型
    • props: { name: String, gender: String, age: Number}
  • 读取方式三:指定名称/类型/必要性/默认值
    • props: { name: {type: String, required: true, default:xxx},... }

App.Vue

<template>
    <div>
        <User name="赵云" gender="" :age="18" />
    </div>
</template>

<script>
// 导入 User 组件
import User from './components/User.vue'

export default {
    name: 'App',
    components: { User },
}
</script>

User.vue

<template>
    <div>
        <h2>{{ msg }}</h2>
        <h2>学生姓名:{{ name }}</h2>
        <h2>学生性别:{{ gender }}</h2>
        <h2>学生年龄:{{ myAge }}</h2>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                msg: '用户信息',
                myAge: this.age
            }
        },
        props: ['name', 'gender', 'age'],

        // 接收同时对数据进行类型限制
        // props: {
        //     name: String,
        //     age: Number,
        //     gender: String
        // }

        // 接收的同时对数据进行类型限制+默认值的指定+必要性的限制
        // props: {
        //     name: {
        //         type: String,       // name的类型是 String
        //         require: true,       // name是必要的
        //     },
        //     age: {
        //         type: Number,
        //         require: true
        //     },
        //     gender: {
        //         type: String,
        //         default: '女'       // 默认值
        //     }
        // }
    }
</script>
  • 自定义属性是只读的,不能直接修改 props 的值。否则会直接报错!
  • 要想修改 props 的值,可以把 props 的值转存到 data 中,因为 data 中的数据是可读可写的
  • 在声明自定义属性时,可以通过 default 来定义属性的默认值
  • 在声明自定义属性时,可以通过 type 来定义属性的值类型
  • 在声明自定义属性时,可以通过 required 选项,将属性设置为必填项,强制用户必须传递属性的值
4.4.2、自定义事件
  • 父组件中绑定自定义事件,并指定一个处理函数,子组件中触发自定义事件($emit)

Son.vue

<template>
    <div>
        <h2>子组件</h2>
        <button @click="send(uid)">点我给父组件传递数据</button>
    </div>
</template>

<script>
    export default {
        name: 'Son',
        data() {
            return {
                uid: 8866
            }
        },
        methods: {
            send(uid) {
                // 通过 $emit() 触发自定义事件
                // 参数一:自定义事件的名称
                // 参数二:传递的值,要发送给父组件的数据
                this.$emit('diyEvent', uid)
            }
        }
    }
</script>

<style scoped>
    div {
        width: 600px;
        height: 300px;
        margin: 0 auto;
        text-align: center;
        background-color: sandybrown;
    }
</style>

Father.vue

<template>
    <div class="father">
        <h2>父组件</h2>
        <Son :msg="msg" @diyEvent="getId" />

        <h3>接收到子组件发送过来的数据:{{ id }}</h3>
    </div>
</template>

<script>
    import Son from '@/components/Son.vue'

    export default {
        name: 'Father',
        components: { Son },
        data() {
            return {
                id: 0
            }
        },
        methods: {
            getId(val) {
                // 把子组件传递过来的数据赋值给父组件的数据中
                this.id = val
            }
        }
    }
</script>

<style scoped>
    .father {
        width: 800px;
        height: 415px;
        text-align: center;
        background-color: salmon;
    }
</style>
4.4.3、EventBus
  • 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
  • 在数据发送方,调用 bus.$emit(‘触发自定义事件名称’, 要发送的数据) 方法触发自定义事件
  • 在数据接收方,调用 bus.$on(‘声明自定义事件名称’, 事件处理函数) 方法注册一个自定义事件

eventBus.js

import Vue from 'vue'

// 向外共享 Vue 的实例对象
export default new Vue()

BrotherOne.vue

<template>
    <div class="b1">
        <h2>兄弟组件1</h2>
        <button @click="sendData()">点我向兄弟组件发送数据</button>
    </div>
</template>

<script>
    // 导入 eventBus.js 模块
    import bus from '@/eventBus'

    export default {
        name: 'BrotherOne',
        data() {
            return {
                user: {
                    uname: '小白',
                    age: 26,
                    email: 'xiaobai@qq.com'
                }
            }
        },
        methods: {
            sendData() {
                // 通过 eventBus 发送数据
                bus.$emit('share', this.user)
            }
        }
    }
</script>

BrotherTwo.vue

<template>
    <div class="b2">
        <h2>兄弟组件2</h2>
        <h3>接收到兄弟组件的数据(user):{{ receive }}</h3>
    </div>
</template>

<script>
    import bus from '@/eventBus'

    export default {
        name: 'BrotherTwo',
        data() {
            return {
                receive: {}
            }
        },
        created() {
            // 接收传递过来的数据
            bus.$on('share', val => {
                this.receive = val
            })
        }
    }
</script>
数据共享三种方案
  • 父—>子:自定义属性
  • 子—>父:自定义事件
  • 任意组件:eventBus

Vue 原型对象上包含事件处理的方法

  • $on(eventName, listener):绑定自定义事件监听
  • $once(eventName, listener):绑定自定义事件监听,但只能处理一次
  • $emit(eventName, data):触发自定义事件
  • $off(eventName):解绑自定义事件监听

解绑事件

  • 解绑单个事件:this.$off('事件名称')
  • 解绑多个事件:this.$off(['事件名称1','事件名称2','事件名称n'])

注:组件上如果绑定原生DOM事件,那么需要使用native修饰符!

5、Axios异步通信


5.1、什么是Axios?

Axios是一个开源的可以用在浏览器端和NodeJS的异步通信框架,它是一个专注于网络请求的库,其功能特点如下:

  • 从浏览器中创建XMLHttpRequests
  • 从 node.js 创建http请求
  • 支持Promise API(JS中的链式编程)
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御XSRF(跨站请求伪造)

中文文档

5.2、为什么要使用Axios?

由于Vue.js是一个视图层框架,并且作者(尤雨溪)严格准守SoC (关注度分离原则),所以Vue.js并不包含Ajax的通信功能,为了解决通信问题,作者单独开发了一个名为vue-resource的插件,不过在进入2.0版本以后就停止了对该插件的维护并推荐了Axios 框架,少用jQuery,因为它操作Dom太频繁!

5.3、使用 Axios 发送请求

<body>
    <button>发起GET请求</button>
    <button>发起POST请求</button>

    <script>
        const result = axios({
            method: "GET",
            url: 'http://localhost:8086/api/getUser',
            // URL 中的查询参数  ?id = 1002
            params: {
                id: 1002
            },
            // 请求体参数
            data: {}
        })

        // axios 默认返回的是一个 Promise 对象
        console.log(result)

        result.then((resp) => {
            console.log(resp.data)
        })


        document.querySelectorAll('button')[1].addEventListener('click', async () => {
            // 如果调用某个方法的返回值是 Promise 对象,则前面可以添加 await!
            // await 只能用在被 async 修饰的方法中
            // 解构赋值的时候,使用 : 重命名
            const { data: res } = await axios({
                method: "GET",
                url: 'http://localhost:8086/api/getUser',
            })
            console.log(res.data)
        })

        document.querySelectorAll('button')[0].addEventListener('click', async () => {
            const { data } = await axios({
                method: "POST",
                url: 'http://localhost:8086/api/addUser',
                // 请求体参数
                data: {
                    name: '小昭',
                    age: 22
                }
            })
            console.log(data)
        })
    </script>
</body>

6、动态组件与插槽


6.1、什么是动态组件?

动态组件指的是动态切换组件的显示与隐藏

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

<template>
    <div id="app">
        <p>
            <button @click="cpName = 'Father'">展示 Father 组件</button>
            <button @click="cpName = 'Son'">展示 Son 组件</button>
        </p>

        <div class="box">
            <!-- is 属性的值,表示要渲染的组件的名称 -->
            <!-- keep-live 会把内部的组件缓存 而不是销毁组件 -->
            <keep-alive>
                <component :is="cpName"></component>
            </keep-alive>
        </div>
    </div>
</template>

<script>
    import Father from './components/Father.vue'
    import Son from './components/Son.vue'

    export default {
        name: 'App',
        data() {
            return {
                // 默认要渲染的组件名称
                cpName: 'Father'
            }
        },
        components: { Father, Son }
    }
</script>

<style scoped>
    #app {
        background-color: gray;
        padding: 20px;
    }

    .box {
        display: flex;
        justify-content: space-between;
        color: white;
        text-align: center;
    }
</style>
keep-alive

默认情况下,切换动态组件时无法保持组件的状态。此时可以使用 vue 内置的<keep-alive>组件保持动态组件的状态

<div class="box">
    <!-- keep-live 会把内部的组件缓存 而不是销毁组件 -->
    <keep-alive>
        <component :is="cpName"></component>
    </keep-alive>
</div>
keep-alive 对应生命周期函数

当组件被激活时,会自动触发组件的 activated 生命周期函数

当组件被缓存时,会自动触发组件的 deactivated 生命周期函数

<script>
    created() {
        console.log('Left 组件被创建了 ^-^')
    },
    destroyed() {
        console.log('Left 组件被销毁了-_-')
    },
    // 组件第一次被创建的时候,即会执行 created 生命周期,也会执行 activated 声明周期
    activated(){
        console.log('组件被激活了 ^-^')
    },
    deactivated(){
        console.log('组件被缓存了 -_-')
    }
</script>
include 属性

include 属性用来指定:只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号,分隔

<div class="box">
    <keep-alive include="Father,Son">
        <component :is="cpName"></component>
    </keep-alive>
</div>

6.2、什么是插槽?

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

可以把插槽认为是组件封装期间,为用户预留的内容的占位符(就好比榨汁机,用户放什么水果,就榨出来什么果汁)

如果在封装组件时没有预留任何<slot>插槽,则用户提供的任何自定义内容都会被丢弃!

插槽基本使用

App.vue

<template>
    <div id="app">
        <h2>App 根组件</h2>
        <hr>

        <div class="box">
            <Left>
                <!-- 默认情况下,在使用组件的时候,提供的内容都会被填充到名字为 default 的插槽之中 -->
                <!-- 1.v-slot 指令不能用在元素身上,必须使用template标签包裹 -->
                <!-- 2.v-slot: 指令 简写为 # -->
                <template #default>
                    <p>这是在 Left 组件定义的内容区域</p>
                </template>
            </Left>
        </div>
    </div>
</template>

<script>
import Left from '@/components/Left.vue'

export default {
    name: 'App',
    components: { Left }
}
</script>

Left.vue

<template>
    <div class="left">
        <h3>Left</h3>
        <!-- 声明一个插槽区域 -->
        <!-- 每一个 slot 插槽,都要有一个 name 名称-->
        <!-- 如果省略了 slot 的 name 属性,则有一个默认名称叫做 default -->
        <slot name="default"></slot>
    </div>
</template>

<script>
    export default {
        name: 'Left',
    }
</script>

<style scoped>
    .left {
        width: 500px;
        height: 300px;
        background-color: salmon;
    }
</style>
具名插槽

没有指定 name 名称的插槽, 会有隐含的名称叫做 “default”,叫做默认插槽!

如果在封装组件时需要预留多个插槽节点,则需要为每个<slot>插槽指定具体的 name 名称。带有具体名称的插槽叫做“具名插槽”

App.vue

<template>
    <div id="app">
        <h2>App 根组件</h2>
        <hr>

        <Article>
            <template v-slot:title>
                <h2>增广贤文·上集</h2>
            </template>

            <template #content>
                <p>十年寒窗无人问</p>
                <p>一举成名天下知</p>
            </template>
        </Article>
    </div>
</template>

<script>
    import Article from '@/components/Article.vue'

    export default {
        name: 'App',
        components: { Article }
    }
</script>

Article.vue

<template>
    <div class="article-container">
        <!-- 文章标题 -->
        <div class="art-title">
            <slot name="title"></slot>
        </div>
        <!-- 文章内容 -->
        <div class="art-content">
            <slot name="content"></slot>
        </div>
        <!-- 文章作者 -->
        <div class="art-author">
            <slot name="author"></slot>
        </div>
    </div>
</template>

<script>
    export default {
        name: 'Article'
    }
</script>

<style lang="less" scoped>
    .article-container {
        width: 600px;
        color: #fff;
        text-align: center;

        div {
            height: 200px;
        }

        .art-title {
            background-color: salmon;
        }

        .art-content {
            background-color: slateblue;
        }

        .art-author {
            background-color: sandybrown;
        }
    }
</style>

7、Vue-router 路由


7.1、什么是路由?

路由(英文:router)就是 Hash 地址与组件之间的对应关系

SPA 与前端路由

SPA 指的是一个 web 网站只有唯一的一个 HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。 此时,不同组件之间的切换需要通过前端路由来实现

在这里插入图片描述

Vue Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成,能够轻松的管理 SPA 项目 中组件的切换,让构建单页面应用变得易如反掌

官方地址

7.2、路由基本使用

安装和配置的步骤

  • 安装 vue-router 包
  • 创建路由模块
  • 导入并挂载路由模块
  • 声明路由链接和占位符

安装:npm install vue-router@3.5.2 -S

注:

  • vue3对应的 vue-router 版本是4
  • vue2对应的 vue-router 版本是3

创建路由模块

在 src 源代码目录下,新建 router/index.js 路由模块,并初始化如下代码:

<script>
    // 1.导入 Vue 和 VueRouter 的包
    import Vue from 'vue'
    import VueRouter from 'vue-router'

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

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

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

导入并挂载路由模块

src/main.js 入口文件中,导入并挂载路由模块

<script>
    import Vue from 'vue'
    import App from './App.vue'

    // 导入路由模块  拿到路由的实例对象
    import router from '@/router/index'

    Vue.config.productionTip = false

    new Vue({
        render: h => h(App),
        // 在 Vue 项目中,想要使用路由,必须把路由实例对象,挂载到 Vue 上
        router
    }).$mount('#app')

</script>

声明路由链接和占位符

src/App.vue 组件中,使用 vue-router 提供的<router-link>和<router-view> 声明路由链接和占位符:

<template>
    <div class="app-container">
        <h1>App 组件</h1>

        <!-- 只要在项目中配置了 vue-router 就可以使用 router-link/router-view 这个组件 定义路由链接 -->
        <router-link to="/friend">我的朋友</router-link>
        <router-link to="/music">我的音乐</router-link>
        <router-link to="/about">关于</router-link>
        <router-link to="/download">下载客户端</router-link>

        <!-- 定义路由的占位符 -->
        <router-view></router-view>
    </div>
</template>

声明路由的匹配规则

src/router/index.js 路由模块中,通过 routes 数组声明路由的匹配规则

<script>
    // 导入需要的组件
    import Friend from '@/components/Friend'
    import Music from '@/components/Music'
    import About from '@/components/About'
    import Download from '@/components/Download'

    // 3.创建路由实例对象
    const router = new VueRouter({
        // routers 是一个数组,作用:用来定义路由规则
        routes: [
            { path: '/friend', component: Friend },
            { path: '/music', component: Music },
            { path: '/about', component: About },
            { path: '/download', component: Download }
        ]
    })
</script>
路由重定向

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

<script>
    const router = new VueRouter({
        routes: [
            // 重定向路由规则
            { path: '/', redirect: '/music' },
            { path: '/friend', component: Friend },
            { path: '/music', component: Music },
            { path: '/about', component: About },
            { path: '/download', component: Download }
        ]
    })
</script>
嵌套路由

通过路由实现组件的嵌套展示,叫做嵌套路由

声明子路由链接和子路由占位符

在 Music.vue 组件中,声明 NewSong 和 PopMusic 的子路由链接以及子路由占位符

<template>
    <div class="music-container">
        <h3>Music 组件</h3>

        <!-- 子级路由链接 -->
        <h3>歌曲分类</h3>
        <router-link to="/music/new">新歌</router-link>
        <router-link to="/music/pop">流行音乐</router-link>
        <hr />

        <!-- 子级路由占位符 -->
        <router-view></router-view>
    </div>
</template>

通过 children 属性声明子路由规则

src/router/index.js 路由模块中,导入需要的组件,并使用 children 属性声明子路由规则:

<script>
    // 导入所需子路由组件
    import NewSong from '@/components/type/NewSong.vue'
    import PopMusic from '@/components/type/PopMusic.vue'

    const router = new VueRouter({
        routes: [
            { path: '/', redirect: '/music' },
            { path: '/friend', component: Friend },
            {
                path: '/music',
                component: Music,
                redirect: '/music/pop',
                // 通过 children 属性,嵌套声明子路由规则
                children: [
                    { path: 'new', component: NewSong }, // 访问 #/music/new 展示 新歌 组件
                    { path: 'pop', component: PopMusic } // 访问 #/music/pop 展示 流行音乐 组件
                ]
            },
            { path: '/about', component: About },
            { path: '/download', component: Download }
        ]
    })
</script>

注:子路由不建议编写以/开头路径,直接写映射地址值即可

动态路由匹配

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

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

<router-link to="/music/1001">纸短情长</router-link>
<router-link to="/music/1002">我来人间一趟</router-link>
<router-link to="/music/1003">刚好遇见你</router-link>

<script>
    // 路由中的动态参数以 : 声明,冒号后面的是动态参数的名称(可自定义)
    routes: [
        { path: '/music/:mid',component: Music }
    ]
</script>

在动态路由渲染出来的组件中,可以使用 this.$route.params 对象访问到动态匹配的参数值

<template>
    <div class="music-container">
        <!-- this.$route 是路由的"参数对象" -->
        <h3>Music 组件 --- {{ this.$route.params.mid }}</h3>
    </div>
</template>

<script>
    export default {
        name: 'Music'
    }
</script>

使用 props 接收路由参数

为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参

<template>
    <!-- 3.直接使用 props 接收到的路由参数 -->
    <p>{{ mid }}</p>
</template>

<script>
    // index.js
    { 	path: '/music/:mid', 
        component: Music,
        // 1.开启 props 路由传参,从而方便的拿到动态参数的值
        props: true
    }

    // music.vue
    // 2.接收 props 数据
    props: ['mid'],
</script>

7.3、导航

声明式导航 & 编程式导航
  • 在浏览器中,点击链接实现导航的方式,叫做声明式导航
    • 普通网页中点击<a>链接、vue 项目中点击<router-link>,都属于声明式导航
  • 在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航
    • 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航
vue-router 中的编程式导航 API

vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是:

  • this.$router.push(‘hash 地址’)
    • 跳转到指定 hash 地址,并增加一条历史记录
  • this.$router.replace(‘hash 地址’)
    • 跳转到指定的 hash 地址,并替换掉当前的历史记录
  • this.$router.go(数值 n)
    • 实现导航历史前进、后退

调用 this.$router.push() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面

调用 this.$router.replace() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面

<template>
    <div class="friend-container">
        <h3>Friend 组件</h3>
        <button @click="goToDownload">点我跳转到下载客户端页面</button>
    </div>
</template>

<script>
    export default {
        name: 'Friend',
        methods: {
            goToDownload() {
                // 通过编程式 API 跳转到指定的页面
                this.$router.push('/download')
            }
        }
    }
</script>

push 和 replace 的区别:

  • push 会增加一条历史记录
  • replace 不会增加历史记录,而是替换掉当前的历史记录

调用 this.$router.go() 方法,可以在浏览历史中前进和后退,跟原生JS history 方法基本一致!

在实际开发中,一般只会前进和后退一层页面。因此 vue-router 提供了如下两个便捷方法:

  • $router.back()
    • 在历史记录中,后退到上一个页面
  • $router.forward()
    • 在历史记录中,前进到下一个页面
<template>
    <div class="friend-container">
        <p>
            <button @click="$router.forward()">前进</button>
            <button @click="$router.back()">后退</button>
        </p>
    </div>
</template>
导航守卫

导航守卫可以控制路由的访问权限

在这里插入图片描述

全局前置守卫

每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限的控制

全局前置守卫的回调函数中接收 3 个形参

  • to 表示将要访问的路由信息对象
  • from 表示已经离开的路由信息对象
  • next是一个函数,调用 next() 表示放行,允许本次路由导航
<script>
    import Vue from 'vue'
    import VueRouter from 'vue-router'

    // 导入需要的组件
    import Friend from '@/components/Friend'
    import About from '@/components/About'
    import Download from '@/components/Download'

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

    // 3.创建路由实例对象
    const router = new VueRouter({
        routes: [
            { path: '/', redirect: '/friend' },
            { path: '/friend', component: Friend },
            { path: '/about', component: About },
            { path: '/download', component: Download }
        ]
    })

    // 4.为 router 实例对象声明 全局前置导航守卫
    // 只要发送路由跳转,必定会触发 beforeEach 的 function 回调
    router.beforeEach((to, from, next) => {
        console.log(to)
        console.log(from)
        console.log(next)

        next()
        if (to.path === '/main') {
            const token = localStorage.getItem('token')
            if (!token) {
                // 如果没有登录 跳转到 login 页面
                next('/login')
            }
            // 如果用户登录了 放行
            next()
        }
    })

    // 4.向外共享路由的实例对象
    export default router
</script>
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐