文章目录

  • 一、 vue基础
    • 1. vue的介绍
      • 1.1 vue的特性
      • 1.2 MVVM
      • 1.3 vue数据代理
        • 1.3.1 Object.defineproperty()
        • 1.3.2 数据代理
    • 2. vue基础用法
      • 2.1 基本使用步骤
      • 2.2 vue调试工具
    • 3. vue指令
      • 3.1 内容渲染指令
        • 3.1.1 v-text
        • 3.1.2 插值语法 {{}}
        • 3.1.3 v-html
      • 3.2 属性绑定指令 v-bind
        • 3.2.1 简写成 :
        • 3.2.2 JS表达式
        • 3.2.3 动态绑定class样式
        • 3.2.4 动态绑定style样式
      • 3.3 事件绑定指令 v-on
        • 3.3.1 简化写法 @
        • 3.3.2 事件对象 $event
        • 3.3.3 事件修饰符
        • 3.3.4 按键修饰符
      • 3.4 双向数据绑定指令 v-model
        • 3.4.1 v-model的指令修饰符
      • 3.5 条件渲染指令:v-if 和 v-show
        • 3.5.1 共同点
        • 3.5.2 区别
        • 3.5.3 v-else
      • 3.6 列表渲染指令 v-for
        • 3.6.1 基本列表
        • 3.6.2 key的原理与作用
      • 3.8 案例
      • 3.9 过滤器 Filters
        • 3.9.1 私有过滤器和全局过滤器
        • 3.9.2 全局过滤器使用Day.js完善案例中的时间格式
    • 4. 侦听器 watch
      • 4.1 方法格式
      • 4.2 对象格式
    • 5. 计算属性 computed
    • 6. watch和computed区别
    • 7. axios
  • 二、 vue-cli
    • 1. 单页面应用程序(SPA)
    • 2. vue-cli
      • 2.1 全局安装vue-cli
      • 2.2 vue-cli创建项目
      • 2.3 vue项目结构目录
      • 2.4 vue项目的运行流程
  • 三、vue组件
    • 1. vue组件的三个组成部分
    • 2. 组件的使用
      • 2.1 组件使用的三个步骤
      • 2.2 vscode配置@路径提示的插件
      • vscode中目前已经安装的插件
    • 2.3 全局注册
    • 3. 组件的props
      • 3.1 props是只读的
      • 3.2 props的default默认值
      • 3.3 props的type值类型
      • 3.4 props required必填项
    • 4. 样式冲突问题
    • 5. 组件中的数据共享
      • 5.1 父子组件传值
        • 5.1.1 父向子传:props
        • 5.1.2 子传父
      • 5.2 兄弟组件传值——bus总线
    • 6. 数组方法的回顾
      • 6.1 some()循环
      • 6.2 every()
      • 6.3 reduce()
    • 7. 组件的拆分
  • 四、vue生命周期
  • 五、ref引用
    • 1. ref引用DOM
    • 2. ref引用组件实例
    • 3. this.$nextTick(callback)应用场景
  • 六、动态组件
    • 1. keep-alive的使用
    • 2. keep-alive的生命周期函数
    • 3. keep-alive的include和exclude
    • 4. 组件注册名称和组件声明时name的区别
  • 七、插槽 slot
    • 1. 普通插槽
    • 2. 具名插槽
    • 3. 作用域插槽
  • 八、自定义指令
    • 1. 私有自定义指令
    • 2. 全局自定义指令
  • 九、vuex
    • 1. vuex的理解
      • 1.1 是什么
      • 1.2 适用场景(数据共享)
    • 2. vuex原理
    • 3. vuex环境搭建
    • 4. vuex案例(熟悉vuex的使用)
    • 5. getters的使用
    • 6. 优化:四个map方法的使用
      • 6.1 mapState()
      • 6.2 mapGetters()
      • 6.3 mapActions
      • 6.4 mapMutations
    • 7. vuex的模块化(modules)
  • 十、 路由(vue-router)
    • 1. 相关理解
      • 1.1 vue-router的理解
      • 1.2 SPA的理解
      • 1.3 路由的理解
    • 2. 基本路由
      • 2.1 使用步骤
    • 3. 嵌套路由(多级路由)
    • 4. 路由传参
      • 4.1 query参数
      • 4.2 params参数
      • 4.3 路由传参的几种形式
      • 4.3.1 字符串形式
        • 4.3.2 模板字符串
        • 4.3.3 对象写法(最常用的)
    • 5. 命名路由
    • 6. 路由的props配置
      • 6.1 对象写法
      • 6.2 布尔值写法
      • 6.3 函数写法
    • 7. 路由跳转的方式
      • 7.1 声明式导航 router-link
        • 7.1.1 router-link的replace属性
      • 7.2 编程式导航 push、replace
    • 8. 缓存路由组件
      • 8.1 基本使用
      • 8.2 两个新的生命周期
    • 9. 路由守卫
      • 9.1 全局前置守卫
      • 9.2 全局后置路由守卫
      • 9.3 独享路由守卫
      • 9.4 组件内路由守卫
    • 10. hash和history模式
      • 10.1 hash模式
      • 10.2 history模式
  • 十一、 Vue UI组件库
    • 1. 移动端UI
    • 2. PC端UI

一、 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 = 19let 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中的xObject.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 获取当前操作的DOMe.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,然后重新赋值给listthis.list = this.list.filter(item => item.id !== id)},// 添加addRow() {let keyword = this.addNameif(keyword === "") {alert("您没有输入任何内容!!")return}else {let len = this.list.length + 1let 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、postmethod:'',// 请求地址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是否为trueconst 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生命周期

包含创建 、 运行 、销毁三个阶段

  • 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()},// beforeMountbeforeMount() {console.log("beforeMount,此时DOM还没有渲染")},// mountedmounted() {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代表所绑定的DOMbind(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代表所绑定的DOMbind(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, // 注册storerender: 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+querythis.$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

vue2理论学习(全套教程,包含vuex、路由等)相关推荐

  1. 尚硅谷Vue2.0 全套教程 Part2丨vuejs从入门到精通笔记 | Vue_Test

    Vue2+3 - 尚硅谷Vue技术全家桶 (天禹老师主讲) 笔记 把Part1也一并加入过来了

  2. office全套教程(2003~2016)

                                                                                    教程简介 office全套教程包含了PP ...

  3. Vue3全套教程合集

    Vue3全套教程合集 点击跳转具体教程,以下所有教程基于脚手架书写,运行代码需要在脚手架环境. 一.Vue3学习-初识Vue3.创建Vue3.0工程 二.Vue3学习-分析工程结构.初识setup 三 ...

  4. [Vue]学习笔记目录 【Vue2与Vue3完结】 (尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通)

    文章目录 前言 遇见的问题及其解决方案 之前笔记 Vue2 Vue3 前言 本笔记根据如下笔记和视频进行整理 老师的课件笔记,不含视频 https://www.aliyundrive.com/s/B8 ...

  5. 【Vue学习笔记】尚硅谷Vue2.0+Vue3.0全套教程丨vue.js从入门到精通

    尚硅谷Vue2.0+Vue3.0全套教程丨vue.js从入门到精通 1.Vue核心部分 1.1 Vue简介 1.1.1 Vue是什么? Vue是一套用于构建用户界面的渐进式JavaScript框架. ...

  6. 尚硅谷Vue2.0+Vue3.0全套教程视频笔记 + 代码 [P001-050]

    视频链接:尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通_哔哩哔哩_bilibili P1-50:当前页面.  P51-100:尚硅谷Vue2.0+Vue3.0全套教程视频笔记 + ...

  7. JavaWeb全套教程笔记_前端技术

    JavaWeb全套教程笔记第一阶段_前端技术 自己整理一套详细的笔记资料,可以满足平时查阅复习,还能帮助别人学习JavaWeb知识.JavaWeb教程分为四个阶段 前端技术 1.HTML.2.CSS. ...

  8. 云之梦php免费教学视频下载_2017年8月云知梦php入门到精通全栈开发全套教程+laravel商城...

    php全栈工程师 云知梦 2017.7月底更新截止. 全套教程一共172集,每天两小时深入讲解,注意只是讲,课下自己要练习,总和86天课程,直到完全掌握php后端开发 从基础变量函数.mysql.缓存 ...

  9. k8s之Pod详解(五)【Kubernetes(K8S) 入门进阶实战完整教程,黑马程序员K8S全套教程(基础+高级)】

    参考于Kubernetes(K8S) 入门进阶实战完整教程,黑马程序员K8S全套教程(基础+高级) Pod Pod的结构 每个Pod中都可以包含一个或者多个容器 这些容器可以分为两类: 用户自定义用的 ...

  10. HackSky复活无特征码免杀全套教程(本教程主要分为三部分)

    HackSky复活无特征码免杀全套教程(本教程主要分为三部分) 下载地址 https://pan.baidu.com/s/1y77oiff8qzFjQdDBYkeBug 创业资料/视频资料/安全相关 ...

最新文章

  1. HTMl中内联边框是怎样实现连接的
  2. 坦白讲!做 Java 工程师,挺好!
  3. php,cgi,nginx关系
  4. Java: 面向对象程序设计(上)
  5. tensorrt动态输入分辨率尺寸
  6. [Unity优化]overdraw01:不可见遮罩
  7. fstream的用法-----------------2012.12.26
  8. CES 2018 七大看点前瞻:模块化电视、枪型游戏设备……
  9. [机器学习]-K近邻-最简单的入门实战例子
  10. python no such file or directory_Python3 no such file or directory
  11. 从SVN上拉取代码到本地进行开发
  12. 解决向日葵远程不能退出腾讯安全管家,点退出时没反应,也不能远程卸载
  13. php加密=>python解密或者python加密=>php解密
  14. 计算机网络的概述以及网络的组成
  15. jvm的类加载和运行时数据区和垃圾回收
  16. android 电容屏多点触控协议
  17. 双百双新产业项目是什么_全区“双百双新”产业项目推进工作电视电话会议召开...
  18. MySQL登录时出现 Access denied for user ‘root‘@‘xxx.xxx.xxx.xxx‘ (using password: YES) 的原因及解决办法
  19. 郭明錤预计苹果明年推出3款新iPhone 两款支持5G全是OLED屏幕
  20. 如何在外网上中山大学的校园网

热门文章

  1. vue updated
  2. 黑苹果是否会成为mac电脑的竞争对手?
  3. 走进量子计算的大门——使用量桨PaddleQuantum创建单量子比特门
  4. php-fpm服务启动命令,PHP-fpm服务的启动和停止
  5. 美团 python_Python | 美团差评数据分析
  6. Tomcat开启为什么会秒退
  7. Pycharm debug崩溃、长时间不响应 解决
  8. LPC17XX系列ISP升级流程
  9. 联想计算机不能进入系统桌面,win10电脑开机后进不了系统桌面只有联想logo
  10. 使用pyhton+adb实现自动公主连结闯关