Vue 介绍

什么是 vue ?

  1. 构建用户界面

    • 用 vue 往 html 页面中填充数据,非常的方便
  2. 框架
    • 框架是一套现成的解决方案,程序员只能遵守框架的规范,去编写自己的业务功能!
    • 要学习 vue,就是在学习 vue 框架中规定的用法!
    • vue 的指令、组件(是对 UI 结构的复用)、路由、Vuex、vue 组件库
    • 只有把上面老师罗列的内容掌握以后,才有开发 vue 项目的能力!

vue 的两个特性

  1. 数据驱动视图:

    • 数据的变化会驱动视图自动更新
    • 好处:程序员只管把数据维护好,那么页面结构会被 vue 自动渲染出来!
  2. 双向数据绑定:

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

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

注意:数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例)

MVVM

MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。

MVVM 的工作原理

ViewModel作为 MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起。

当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构。

当单元素的值发生变化表时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中

起步

第一个Vue程序:

  1. 导入开发版本的 Vue.js

  2. 创建 Vue实例对象,设置el属性和data属性

  3. 使用简洁的模板语法把数据渲染到页面

    {{message}}

基本代码与 MVVM 的对应关系

插件库

day.js 快速日期格式化

解决格式化Vue文件时 逗号、分号问题 :

在根目录中,添加一个 .prettierrc.json 配置文件,写入:

{"singleQuote": true,  "semi": false,"trailingComma": "none"
}

一劳永逸方法:

  1. 在电脑上的用户目录下新建一个 .prettierrc.json 文件,同理上面的参数。

  2. 在 **Vs code **的 setting.json 中添加配置路径:

      // 设置 配置文件,解决格式化vue文件时逗号、分号爆红问题// 这里注意配置路径,记得使自己计算机上的用户名"prettier.configPath": "C:/Users/mi/.prettierrc.json",
    

解决格式化时函数括号前的空格问题 :

  • Prettier 格式化插件无法处理函数括号前添加空格问题

解决办法一、在当前项目里的 .eslintrc.js 里 rules 规则里添加忽略加空格爆红提示 :

'space-before-function-paren':['error','never']

解决办法二: 使用 Prettier now 插件可以解决此问题。

vue 指令

1. 内容渲染指令

  1. v-text 设置标签的内容, 默认写法会 覆盖元素内部原有的内容 , 内部支持写表达式。
  2. {{ }} 插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!
  3. v-html 指令的作用:可以把带有标签的字符串,渲染成真正的 HTML 内容!

注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!

2. v-bind 属性绑定属性

  • 在 vue 中,可以使用 v-bind: 指令,为元素的属性动态绑定值;
  • 简写是英文的 :
  1. 在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:

    <div :title="'box' + index">这是一个 div</div>
    
  2. 使用 v-bind 在元素绑定时希望内传入 Number数值时,避免被解析成字符串

    <select    v-model='value'><option  :value='数字'>...</option>
    </select>
    

3. v-on 事件绑定属性

  1. v-on: 简写是 @

  2. 语法格式为:

    <p> count的值是:{{ count }}</p><button @click="add"></button>methods: {add() {// 如果在方法中要修改 data 中的数据,可以通过 this 访问到this.count += 1}
    }
    

$event

在事件绑定时,会有一个原生DOM 的事件对象 e,如果事件对象传入参数,默认的事件对象会被覆盖。

$event 的应用场景:如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event。

例如:

// 点击按钮让count值 递增,绑定点击事件并传参<button @click="add(3, $event)"></button>methods: {add(n, e) {// 如果在方法中要修改 data 中的数据,可以通过 this 访问到this.count += 1}
}

事件修饰符:

事件绑定期间非常好玩的一个东西,对事件的触发进行控制。

  • .prevent

    <a @click.prevent="xxx">链接</a>
    
  • .stop

    <button @click.stop="xxx">按钮</button>
    

事件修饰符

说明

.prevent

阻止默认行为 (如:阻止a链接的跳转,阻止表单提交等)

.stop

阻止冒泡事件

.capture

以捕获模式触发当前的事件处理函数

.once

绑定的事件只触发一次

.self

只有在event.target 是当前元素自身时触发事件处理函数

按键修饰符

按键

键码值

使用

Enter

13

.enter

Tab

9

.tab

Delete

46

.delete (捕获“删除”和“退格”按键)

Esc

27

.esc

BackSpace

8

.space

Up Arrow

38

.up

Left Arrow

37

.left

Right Arrow

39

.right

Dw Arrow

40

.down

1.自定义其他的按键别名:

Vue.config.keyCodes.f6 = 118<input @keyup.f6="xxx" />  // 只有单击f6键才会触发xxx的回调

2.多个按键一并触发该事件

@keyup.ctrl.enter="XXX"   //  按下ctrl和enter才触发事件执行

【案例应用】:表单输入后按回车键添,按esc则清空表单

<input type="text" v-model="newbrand" @keyup.enter="add" @keyup.esc="newbrand=''" />

4. v-model 表单绑定

在不操作 DOM 的前提下,实现表单元素和数据的双向绑定。

v-model 指令的修饰符

修饰符

作用

示例

.number

自动将用户输入值转为 Number

<input v-model.number=“age” />

.trim

去除首尾空白字符

<input v-model.trim=“msg” />

.lazy

表单输入后失去焦点时更新页面数据,而非实时更新

<input v-model.lazy=“msg” />

  1. input 输入框

    • type=“text”

    • type=“radio”

    • type=“checkbox”

    • type=“xxxx”

  2. textarea

  3. select

5. 条件渲染指令

条件渲染指令用来辅助开发者按需控制 DOM 的显示与隐藏。

v-if 和 v-show

  1. v-show 的原理是:动态为元素添加或移除style= " display: none; " 样式,从而控制元素的显示与隐藏。

    • 如果要频繁的切换元素的显示状态,用 v-show 性能会更好。
  2. v-if 的原理是:每次动态创建或移除 DOM 元素,实现元素的显示和隐藏。

    • 如果在运行时条件很少改变,则使用 v-if 较好。

v-if 指令在使用的时候,有两种方式:

  1. 直接给定一个布尔值 true 或 false

    <p v-if="true">被 v-if 控制的元素</p>
    
  2. 给 v-if 提供一个判断条件,根据判断的结果是 true 或 false,来控制元素的显示和隐藏

    <p v-if="type === 'A'">良好</p>
    

v-else-if

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

6. 列表渲染指令

基于一个数组来循环渲染一个列表结构。

v-for

v-for 指令需要使用 item in items 形式的特殊语法

  • items 是源数据 (待循环) 的数组,而
  • item 被循环的每一项。

【示例】:动态渲染表单数据:

 <link rel="stylesheet" href="./lib/bootstrap.css"><!-- 希望 Vue 能够控制下面的这个 div,帮我们把数据填充到 div 内部 --><div id="app"><table class="table table-bordered table-hover table-striped"><thead><th>索引</th><th>Id</th><th>姓名</th></thead><tbody><!-- 官方建议:只要用到了 v-for 指令,那么一定要绑定一个 :key 属性 --><!-- 而且,尽量把 id 作为 key 的值 --><!-- 官方对 key 的值类型,是有要求的:字符串或数字类型 --><!-- key 的值是千万不能重复的,否则会终端报错:Duplicate keys detected --><tr v-for="(item, index) in list" :key="item.id"><td>{{ index }}</td><td>{{ item.id }}</td><td>{{ item.name }}</td></tr></tbody></table></div>

使用 key 维护列表的状态

官方推荐, 使用 v-for 指令绑定一个:key 属性,key的作用是为了高效的更新虚拟DOM。

  • key 的值类型,是有要求的:字符串或数字类型

  • 把数据项 id 属性的值作为 key 的值(因为 id 属性的值具有唯一性)

  • 改变 data 顺序,index 会重新排序,所以 index 的值不具有唯一性。

计算属性 computed

实时监听 data 中数据的变化,并 return 一个计算后的新值, 供组件渲染 DOM 时使用。

可以被模板结构 (插值、v-bind ) 或 methods 方法使用。

  • 但是在某些情况下,我们可能需要对数据进行一些转化后在显示,或者需要将多个数据结合起来进行显示,这时候我们可以使用计算属性。

实例 1:

<div id="app"><h2>{{getFullName()}}</h2><h2>{{fullName}}</h2>
</div>const vm = new Vue({el: '#app',data: {firstName: 'lin',lastName: 'willen'},computed: {fullName () {return this.firstName + ' ' + this.lastName;}},// 使用 methods: 每次都会调用方法methods: {getFullName () {return this.firstName + ' ' + this.lastName;}}})

特点:

  1. 定义的时候,要被定义为 “方法”
  2. 在使用计算属性的时候,当普通的属性使用即可
  3. 实现了代码的复用,只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值。

实例2:

<div id="app"><h2>总价格:{{totalPrice}}</h2>
</div>const vm = new Vue({el: '#app',data: {books:[{id: 1001, name: 'Unix编程艺术',price: 119},{id: 1002, name: '代码大全',price: 105},{id: 1003, name: '深入理解计算机原理',price: 99},{id: 1004, name: '现代操作系统',price: 109}]},computed: {totalPrice () {let totalPrice = 0;for (let i in this.books) {totalPrice += this.books[i].price;}// 也可以使用 for of for (let book of this.books) {totalPrice += book.price;}return totalPrice;}}})

计算属性 vs 方法

methodscomputed看起来都可以实现我们的功能,那么为什么还要多一个计算属性

  • methods: 每次使用都会调用方法
  • computed: 计算属性会缓存计算的结果, 不变的情况下只调用一次, 除非原属性发生改变,才会重新调用.

计算属性 vs 侦听器

侧重的应用场景不同侧重的应用场景不同

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

vue组件

什么是组件化开发 ?

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

vue 中的组件化开发

  • vue 是一个支持组件化开发的前端框架。
  • vue 中组件的后缀名是.vue

组件的构成

每个 .vue 组件都由 3 部分构成,分别是:

  1. template :组件的模板结构,且每个组件中必须包含template模板结构。

  2. script : 组件的 JavaScript 行为

  3. style :组件的样式

    // 标签上添加 lang=“less” 属性,即可使用 less 语法编写组件的样式
    // scoped 防止样式冲突

script 节点:

(1)script 中的 name 节点

  • 用来定义组件的名称,调试的时候可以清晰的区分每个组件

(2)script 中的 data 节点

  • 组件渲染期间需要用到的数据, data 必须是函数, 不能直接指向对象数据。

(3)script 中的 methods 节点

  • 组件中的事件处理函数(方法),必须定义到 methods 节点中

注册私有组件

通过 components 注册的是私有子组件,被注册的组件只能用在当前组件中。

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

import Left from '@/components/Left.vue'

2.在 script 标签中使用 components 节点注册组件

<script>
export default {comments:{Left}
}
</script>

3.以标签的形式使用刚才注册的组件

<template><Left></Left>
</template>

注册全局组件

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

注册:

import Vue from 'vue'
import App from './App.vue'// 导入需要被全局注册的那个组件
import Count from '@/components/Count.vue'// 参数一: 组件的 '注册名称',将来以标签形式使用时要求和这个名称一样。
// 参数二: 需要被全局注册的那个组件。
Vue.component('MyCount', Count)// 消息提示的环境配置,设置为开发环境或者生产环境
Vue.config.productionTip = falsenew Vue({// render 函数中,渲染的是哪个 .vue 组件,那么这个组件就叫做 “根组件”render: h => h(App)
}).$mount('#app')

使用:

<template>// 这里需要注意 '注册名称'<MyCount ></MyCount>
</template>

组件注册时名称的大小写

在 Vue 定义组件注册名称的方式有两种:

  1. 短横线命名法,例如 my-swipermy-search

    • 使用组件时也必须使用短横线命名
  2. 大驼峰命名法,例如 MySwiperMySearch
    • 既可以按照大驼峰命名法使用,也可以转化为短横线名称进行使用。

注意: 在开发中,推荐使用大驼峰命名法为组件注册名称,因为它的适用性更强。

组件之间的样式冲突问题

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

scoped 属性

让当前组件的样式对其子组件是不生效。

Vue 中提供了在style 节点添加 scoped属性,来防止样式冲突问题:

  • 原理是为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域。

<template><div class="container" data-v-001><h3 data-v-001 > 轮播图组件件</h3></div>
</template><style>// 通过中括号'属性选择器',防止样式冲突问题// 因为每个组件分配的自定义属性是'唯一的'. container[data-v-001]{border: 1px solid red;}
</style>

为了提高开发效率和开发体验,直接在 style 节点使用 scoped 属性:

<style lang="less" scoped>
</style>

/deep/ 样式穿透

让某些样 式对子组件生效。

使用场景: 当使用第三方组件库的时候,需要修改第三方组件默认样式的时候。

<style lang="less" scoped>/*不加 /deep/ 时,生成的选择器格式为 .title[data-v-052242de]*/
.title{color: blue;
}/*加/deep/ 时,生成的选择器格式为 [data-v-052242de] .title*/
/deep/ .title {color: pink;
}
</style>

Class 与 Style 绑定

通过 v-bind动态操作元素样式。

1. 动态绑定 HTML 的 class:

通过三元表达式,动态的为元素绑定 class 的类名:

<h3 class="thin" :class="isItalic ?'italic':''">MyDeep 组件</h3>
<button @click="isItalic=!lisItalic"> Toggle Italic </button>data(){return { isItalic:true }
.thin{font-weight:200;
.italic{font-style:italic;
}

2. 以数组语法绑定 HTML 的 class

如果元素需要动态绑定多个 class 的类名,此时可以使用数组的语法格式

<h3 class="thin":class="[isItalic? 'italic': '',isDelete? 'delete':'']">MyDeep组件</h3>
<button @click="isItalic= !isItalic"> 字体变细 </button>
<button @click="isDelete= !isDelete"> 添加删除线 e</button><script>
export default {data () {return {isItalic: true,isDelete: false,}}
}
</script>

3. 以对象语法绑定 HTML 的 class:

<h3 class="thin":class="classObj">MyDeep组件</h3>
<button @click="classObj.isItalic = !classObjisItalic"> 字体变细 </button>
<button @click="classObj.isDelete = !classObj.isDelete"> 添加删除线 e</button><script>
export default {data () {return {classObj:{isItalic: true,isDelete: false,    }}}
}
</script>

4. 以对象语法绑定内联的 style

命名可以用驼峰式短横线分隔 (记得用引号括起来) 来命名:

<div :style="{color:active, fontSize: fsize +'px','background-color': bgcolor}">Hello world!!
</div><button @click="fsize += 1">字号+1</button>
<button @click="fsize -= 1">字号-1</button>data () {return {active: 'red',fsize: 30, bgcolor: 'pink'}
}

自定义属性 props

props是组件的自定义属性,允许使用者通过自定义属性,为当前组件指定初始值,极大的提高组件的复用性。

在组件中声明 prpos

my-article 组件的定义如下:

<template>
<h3>标题:{{title}}</h3>
<h5>作者:{{author}}</h5>
</template>

父组件传递给my-article组件的数据,必须在props节点中声明 :

<script>export default {props:['title','author'],
</script>

无法使用未声明的 props

如果父组件给子组件传递了未声明的 props 属性,则这些属性会被忽略,无法被子组件使用。

动态绑定 props 的值

使用 v-bind 属性绑定的形式,可以为组件动态绑定 props 的值。

<!--通过V-bind属性绑定,为author动态赋予一个表达式的值<my-article :title="info.title" :author="'post by'+info.author"></my-article>

props 的大小写命名

组件中如果使用“camelCase (驼峰命名法)”声明了 props 属性的名称,则有两种方式为其绑定属性的值:

<script>export default {props:['pubTime'],  // 使用'驼峰命名'法为当前组件声明 pubTime 属性
</script>

使用时既可以用驼峰命名,亦可以用短横线分隔命名的形式为组件绑定属性的值 :

<my-article pubTime="2021" ></my-article>
// 等价于
<my-article pub-time="2021" ></my-article>

props 验证

  1. 基础的类型检查 type

    props:{//支持的8种基础类型propA:String,    //字符串类型propB:Number,    //数字类型propC:Boolean,   //布尔值类型propD:Array,     //数组类型propE:Object,    //对象类型propF:Date,      //日期类型propG:Function,  //函数类型propH:Symbol     //符号类型}
    
  2. 多个可能的类型

  3. 必填项校验 required

  4. 属性默认值 default

    props:{// 通过数组形式,为当前属性定义多个可能的类型type: [String, Number],required: true,default: 0,
    }
    
自定义验证函数

在封装组件时,可以为 prop 属性指定自定义的验证函数,从而对 prop 属性的值进行更加精确的控制:

 props:{type:{// 通过 validataor函数,对type 属性进行校验,属性值 通过val形参接收validataor(val){// 必须匹配下列字符串中的一个return ['success','warning','danger'].indexOf(val) !== -1}}
}

prpos 传入不同的初始值

在实际开发中我们经常会碰到下面的情况:

  • 在不同组件使用同一个注册的组件时候希望赋值一个不同的初始值

1.组件的封装者通过 props 允许使用者自定义初始值:

<template><div><h5>Count是全局组件,将被Left 和 Right组件使用</h5><p>count 的值是:{{ init }}</p><button @click="count += 1">+1</button></div>
</template><script>
export default {// props: ['init'],props: {// 自定义属性的名字,是封装者自定义的(只要名称合法即可)init: {// 如果外界使用 Count 组件的时候,没有传递 init 属性,则默认值生效default: 0,// 指定值类型必须是 Number 数字type: Number,// 必填项校验(表示必须传入值)required: true}},data() {return {//props是只读的且不可修改// 要想修改 props 的值,可以把 props 的值转存到 data中count: this.init}}
}

2.组件的使用者通过属性节点传入初始值

Left 组件、Right 组件:

<template> <div class="left-container"><h3>Left 组件</h3><hr /><MyCount :init="9"></MyCount></div>
</template><template><div class="right-container"><h3>Right 组件</h3><hr /><MyCount :init="6"></MyCount></div>
</template>

props 里传参注意点

  • props 可以通过[]给数据定义多个可能的数据类型 ;

  • props 传入 Object 默认值必须是一个 fn

    props: {
    commCount: {
    type: String,
    default: ‘’
    },
    pubdate: {
    // 通过数组形式,为当前属性定义多个可能的类型
    type: [String, Number],
    default: ‘’
    },
    cover: {
    type: Object,
    // 通过 default 函数,返回 cover 的默认值
    default: function() {
    // 这个 return 的对象就是 cover 属性的默认值
    return { cover: 0 }
    }
    }
    }

  • props 是只读的,想修改 props 的值,可以把 props 的值转存到 data 中。
  • props 的三个属性值:default、type、required。

自定义事件

封装组件时,为了让组件的使用者可以监听到组件内状态的变化,此时需要用到组件的自定义事件

vue2 中自定义事件的 3 个使用步骤:

  • 在封装组件时 (子组件):触发自定义事件

  • 在使用组件(父组件)时:监听自定义事件

触发自定义事件

// 子组件<button @click="onBtnClick"> +1 </button><script>
export default {data() {// 子组件自己的数据,将来希望把 count 值传给父组件return { count: 0 }},methods: {onBtnClick() {this.count t= 1//修改数据时,通过 $emit()触发自定义事件// 当点击 '+1' 按钮时 调用this.$emit 触发自定义的 numchange 事件this.$emit( 'numchange')}}
}
</script>

监听自定义事件

// 父组件
<Son @numchange="getNewCount"></Son>methods : {getNewCount(val) {console.log('监听到了 count 值的变化', val)
},

自定义事件传参

在调用 this.$emit() 方法触发自定义事件时,可以通过第 2 个参数为自定义事件传参:

methods: {onBtnClick() {this.count t= 1this.$emit( 'numchange' , this.count)} // 触发自定义事件,通过第二个参数传参
}

filter 过滤器

在 vue 3.x 的版本中剔除了过滤器相关的功能。

在 vue 3.x 使用计算属性或方法代替被剔除的过滤器功能。

过滤器(Filters)常用于文本的格式化。过滤器可以用在两个地方:双括号插值表达式v-bind属性绑定。

<!-- 在双花括号中通过 | 调用capitalize过滤器,对message值进行格式化  -->
{{ message | capitalize }}<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

私有过滤器

在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。

创建一个私有过滤器,示例代码如下:

const vm = new Vue({el: '#app',data: {message: 'hello world!',info: 'title info'},// 在 filters 节点下定义过滤器filters: {// 把首字母转换为大写的过滤器capitalize (value) {  return value.charAt(0).toUpperCase() + value.slice(1)}}

全局过滤器

如果希望在多个 vue 实例之间共享过滤器,则可以按照如下的格式定义全局过滤器:

// Vue.filters()方法接收两个参数:
// 第一个参数:过滤器名字   第二个参数:过滤器的处理函数Vue.filter('capitalize',(str)=>{return value.charAt(0).toUpperCase() + value.slice(1)'
} )

调用多个过滤器:

{{ message | filterA | filterB }}
  • 先把 message 的值交给 filterA 处理,再把 filterA 处理结果交给 filterB 进行处理,最终把 filterB 的处理结果,作为最终值渲染到页面上。

过滤器传参

过滤器是 JavaScript 函数,因此可以接收参数:

{{ message | filterA('arg1', arg2) }}// 第一个参数永远是 '管道符' 前面待处理的值,第二个参数开始才是调用过滤器时传递的参数Vue.filter('filterA',(msg,arg1,arg2)=>{// 过滤器的逻辑代码
})

这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。

<p>{{message | cap | maxl(5)}}</p>Vue.filter('cap', (str) => {return str.charAt(0).toUpperCase() + str.slice(1) + '----'
})Vue.filter('maxl', (str, len = 10) => {if (str.length <= len) return strreturn str.slice(0, len) + '....'
})
var app = new Vue({el: '#app',data: {message: 'hello Vue 2021年10月30日00:07:45!'}

过滤器的注意点

  1. 在过滤器函数中,一定要有 return 值
  2. 在过滤器的形参中,可以获取到“管道符”前面待处理的那个值
  3. 如果全局过滤器和私有过滤器名字一致,此时按照“就近原则”,调用的是”私有过滤器“

label的for属性

使用lable自带的属性进行单选钮的启用和禁用:

<input type="checkbox" :id="'cb' + item.id" v-model="item.status">
<label  :for="'cb' + item.id" v-if="item.status">已启用</label>
<label  :for="'cb' + item.id" v-else>已禁用</label>

watch 侦听器

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

侦听器的格式

  1. 方法格式的侦听器

    • 无法在刚进入页面的时候,自动触发!!
    • 如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器!
  2. 对象格式的侦听器
    • 可以通过 immediate 选项,让侦听器自动触发!
    • 可以通过 deep 选项,让侦听器深度监听对象中每个属性的变化!

使用方法格式创建的侦听器:

监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用:

import axios from 'axios'export default{data(){return{ username: ''}}
},
watch: { // newVal 是'变化后的新值',oldVal 是'变化之前的旧值'async username(newVal,oldVal) {  if (newVal === '') return // 使用 axios 发起请求,判断用户名是否可用 const { data: res } = await axios.get(`https://www.escook.cn/api/finduser/${newVal}` ) console.log(res) }
}

使用方法创建时:组件在初次加载完毕后不会调用 watch 侦听器。

immediate 选项

如果想让 watch 侦听器在浏览器打开时立即被调用,则需要使 用 immediate 选项。

使用对象格式创建的侦听器

watch: {username: {// handler 是固定写法,表示当 username 的值变化时,自动调用 handler 处理函数async handler(newVal,oldVal) {if (newVal === '') returnconst { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)console.log(res)},// 表示页面初次渲染好之后,就立即触发当前的 watch 侦听器immediate: true}
}
  1. 使用 handler 定义侦听器函数
  2. immediate控制侦听器是否自动触发, 默认值为 false不立即触发。

deep 选项

如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 开启 深度监听

<input type="text" v-model.trim="username"/>data: {info: {username: 'admin'}
},
watch:{// 监听info对象的变化info:{handler(newVal){console.log(newVal.username)},// 开启深度监听,监听每个属性值的变化,默认值为 falsedeep: true}
}

监听对象单个属性的变化

如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器。

const vm = new Vue({el: '#app',data: {info: {username: 'admin',}},watch:{'info.username':{handler(newVal){console.log(newVal)},}}
})

组件的生命周期

  • 生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。
  • 生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。

生命周期强调的是时间段,生命周期函数强调的是时间点。

生命周期函数的分类

生命周期图示:

需要注意的三个周期函数:

在实际开发中,created 是最常用的生命周期函数 !

组件之间的数据共享

父→子

父组件通过 v-bind 属性绑定向子组件共享数据,子组件使用 props接收数据。

// 父组件
// 注册子组件
<Son :msg="message" :user="userinfo"></Son><script>// 导入子组件import Son from '@component/Son.vue'export default {data:{return{message:'hello vue.js'userinfo:{name: 'lilei',age: 21},components: {Son}}}
}
</script>// 子组件
<template><div><h5>Son组件</h5><p>父组件传递过来的 msg值是: {{ msg }</p><p>父组件传递过来的user值是: {{ user }}</p></div>
</template>export default {props: [ 'msg' , 'user ']
}

子 → 父

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

// 子组件export default {data() {// 子组件自己的数据,将来希望把 count 值传给父组件return { count: 0 }},methods: {add() {this.count t= 1//修改数据时,通过 $emit()触发自定义事件this.$emit( 'numchange' , this.count)}}
}// 父组件
<h1>App 根组件</h1>
<h3>子组件传过来的数据是 : {{ countFromSon }}</h3>
<Son @numchange="getNewCount"> </Son><script>
import Son from '@component/Son.vue'export default {data() {return { countFromSon: 0 }},methods : {getNewCount(val) {console.log('numchange 事件被触发了!', val)this.countFromSon = val},components: {Son}
}
}</script>

兄弟组件数据共享

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

EventBus 的使用步骤

  1. 创建 eventBus.js模块,并向外共享一个 Vue 的实例对象
  2. 在数据发送方,调用 bus.$emit('事件名称', 要发送的数据)方法触发自定义事件 。
  3. 在数据接收方,调用 bus.$on('事件名称', 事件处理函数)方法注册一个自定义事件。

ref 引用 操作DOM

不依赖于 jQuery 和调用 DOM API 的情况下,获取 DOM 元素组件的引用

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

使用 ref 引用 DOM 元素

<!--使用ref属性,为对应的DOM添加引用名称--><h3 ref="myh3">MyRef 组件</h3>
<button@click="getRef">获取$refs 引用</button>methods:{getRef(){//通过this.$refs.引用的名称可以获取到DOM元素的引用console.log(this.$refs.myh3)//操作DOM元素,把文本颜色改为红色this.$refs.myh3.style.color='red'
},

使用 ref 引用组件实例

需求: 在根组件控制子组件

<!--使用ref属性,为对应的“组件”添加引用名称--><my-counter ref="counterRef"> </my-counter>
<button @click="getRef"> 获取$refs 引用 </button>methods:{getRef(){// 通过this.$refs.引用的名称可以引用组件的实例console.log(this.$refs.counterRef)// 引用到组件的实例之后,就可以调用 子组件上的 methods 方法this.$refs.counterRef.add()
},

点击文本框自动获得焦点

添加 ref 引用,并调用原生 DOM 对象的.focus() 方法即可。

this.$nextTick(cb) 方法

$nextTick(cb)保证 cb 回调函数可以操作到最新的 DOM 元素(推迟到下一个 DOM 更新周期之后执行)。

  • 解决我们在页面没有渲染完成前使用 ref 操作DOM元素报错问题。

控制文本框和按钮的按需切换

点击按钮展示文本框,文本框输入时隐藏按钮:

<template><input type="text" v-if="inputVisible" ref="ipt"><button v-else @click="showInput">展示input输入框</button>
</template><script>
export default{data(){return{//控制文本框和按钮的按需切换inputVisible:false,
},
methods:{showInput(){//切换布尔值,显示文本框this.inputVisible=true//获取文本框的DOM引用,并调用.focus()使其自动获得焦点// this.$refs.ipt.focus() 错误,此时页面未渲染完毕,无法获取文本框//把对input文本框的操作,推迟到下次DOM更新之后。否则页面上根本不存在文本框元素this.$nextTick(() =>{this.$refs.ipt.focus()})}
},</script>

动态组件

实现不同组件之间的按需展示。( 动态切换组件显示和隐藏 ) ,类似于 Vue-Router

动态组件的基本使用

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

  1. component 标签是 vue 内置的,作用:组件的占位符

  2. 通过 :is 属性,动态指定要渲染的组件

    • is 属性的值,表示要渲染的组件的名字。

    • is 属性的值,应该是组件在 components 节点下的注册名称。

  3. 使用 keep-alive保持组件的状态 (避免组件切换时重新渲染)。

    • keep-alive 会把内部的组件进行缓存,而不是销毁组件。
  4. 通过 include 指定哪些组件需要被缓存。

  5. 通过 exclude属性指定哪些组件不需要被缓存。

    // 点击按钮,动态切换组件的名称

    <button @click=“comName = ‘Left’”>展示 Left
    <button @click=“comName = ‘Right’”>展示 Right

在组件中定义 name 名称:

  • 当提供了 name 属性之后,组件的名称就是 name 属性的值。

    export default {
    name: ‘MyRight’
    }

声明 name 应用场景:结合<keep-alive>标签实现组件缓存功能;以及在调试工具中看到组件的 name 名称。


插槽 slot

在签形式使用的组件中内容节点插入内容

  • 通过 slot 元素 定义插槽,从而为用户预留内容占位符。
  • 封装组件时,没有预留插槽的内容会被丢弃。

后备内容

slot 标签内添加的内容会被作为后备内容。

// 子组件 my-com
<template><slot>这是后备内容</slot>
</template>// 使用插槽<my-com>// 如果用户没有提供内容,上面 slot标签 内定义的内容会生效,此时页面会有 "这是后备内容"// 如过提供了,下面的 img 将会被渲染到页面上<img src="../assets/log.png" alt=""></my-com>

具名插槽

// MyArticle 组件
<template>
<div class="container"><header><slot name="header"></slot></header><main><slot></slot></main><footer><slot name="footer"></slot></footer></div>
</template>
  • Vue 官方规定,每个 slot插槽,都要有一个 name名称,一个不带 name<slot> 出口会带有隐含的名字“default”。

为具名插槽提供内容

如果要把内容填充到指定名称的插槽中,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:

<MyArticle><template v-slot:header>// 把内容放在 MyArticle组件的 header 标签内<h1>如今最好,没有来日方长。</h1></template>// 上一个代码块中,main 标签未指定具名插槽,所以默认渲染到 main标签中<p>现在是2021年11月7日 22点05分 星期天</p><p>今天不仅是周日,也是冬至</p><template #footer>   <p>最后,祝大家冬至快乐!</p></template></MyArticle>
  • v-slot:可以简写 #。例如 v-slot:header 可以被重写为 #header。

  • v-slot 属性只能放在 组件标签和 <template> 元素上 (否则会报错)。

作用域插槽

在封装组件的过程中,可以为预留的<slot>插槽绑定 props 数据,这种带有 props 数据的<slot> 叫做“作用域插槽”。示例代码如下:

<template><div><h1> 这是 Left 组件</h1><!--下面的slot 是一个作用域插槽 --><slot v-for="item in list" :user="item"></slot> </div></template>

接收作用域插槽对外提供的数据, 使用解构赋值简化数据的接收过程 :

<!-- 使用自定义组件 -->
<Left><!--作用域插槽对外提供的数据对象,可以通过“解构赋值”简化接收的过程--><template #default="{user}"><tr><td>{{user.id}}</td><td>{{user.name}}</td><td>{{user.state}}</td></tr></template>
</Left>

自定义指令

vue 官方提供了 v-text、v-for、v-model、v-if 等常用的指令。除此之外 vue 还允许开发者自定义指令。

私有自定义指令 directives: { }

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

1. 定义一个私有自定义指令:
directives:{color:{//为绑定到的HTML元素设置红色的文字bind(el){//形参中的el是绑定了此指令的、原生的DOM对象el.style.color=‘red'}
2. 使用自定义指令:
  • 使用自定义指令时,需要加上v-指令前缀

    <!–声明自定义指令时,指令的名字是color 使用时就是 v-color -->

    App组件

3. 为自定义指令动态绑定参数值

template 结构中使用自定义指令时,可以通过等号 = 的方式,为当前指令动态绑定参数值:

<template><!--在使用指令时,动态为当前指令绑定参数值 color-->
<h1 v-color="color">App组件</h1></template>data(){return{color:'red’
}
4. 通过 binding 获取指令的参数值:

在上面的实例中我们为自定义指令绑定了一个动态的参数值,如何拿到这个值呢 ?

在声明自定义指令时,可以通过形参中的第二个参数,来接收指令的参数值

<h1 v-color="color">App组件</h1>// 此时传入的是字符串,不会在data 数据中查找
<p v-color="'blue'" ></p>directives:{color:{bind(el,binding){//通过binding对象的.value属性,获取动态的参数值el.style.color=binding.value
}
5. 使用 update 函数更新 DOM

update 函数会在每次 DOM 更新时被调用。

注意:在 vue3 的项目中使用自定义指令时, bind 必须改为 mounted 、 update 改为 updated

bind 函数只调用 1 次:当指令第一次绑定到元素时调用,当 DOM 更新时 bind 函数不会被触发

directives:{color:{//当指令第一次被绑定到元素时被调用bind(el,binding){el.style.color=binding.value},//每次DOM更新时被调用update(el,binding){el.style.color=binding.value}
6. 同时使用 bind 和 update 函数简写

如果 bindupdate 函数中的 逻辑完全相同,则对象格式的自定义指令可以简写成函数格式:

directives:{//在 bind 和 update 时,会触发相同的业务逻辑color( el,binding ){el.style.color=binding.value
}

全局自定义指令 Vue.directive ()

通过“ Vue.directive()” 进行声明 。

注意:在使用 Vue-cli ( 脚手架) 时,要把全局自定义指令写在main.js文件中:

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

vue-cli

Vue CLI 是官方发布的vue.js项目脚手架, 可以快速搭建vue开发环境以及Webpack配置。

安装和使用

一、安装Vue脚手架

npm install -g @vue/cli

二、创建项目

vue create project(项目名称)

目录详解:

|- public         // 静态页面目录|- index.html // 项目入口
|- src            // 源码目录|- assets     // 存放项目中用到的静态资源文件,例如:css 样式表、图片资源|- components     // 封装的、可复用的组件,都要放到 components 目录下|- App.vue        // 根组件|- main.js        // 项目的入口文件。整个项目的运行,要先执行

vue 项目的运行流程:

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

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

axios

axios 是一个专注于网络请求的库, 调用 axios 方法得到的返回值是 Promise 对象

axios 的基本使用

  1. 发起 GET 请求:

    axios({// 请求方式method: 'GET',// 请求的地址url: 'http://www.liulongbin.top:3006/api/getbooks',// URL 中的查询参数(GET请求传参)params: {id: 1}
    }).then(function (result) {console.log(result)
    })
    
    • params 表示传递到服务器端的数据,以url参数的形式拼接在请求地址后面

      • 如 : params: { page:1,per:3 }
      • 最终生成:http://jsonplaceholder.typicode.com/page=1&per=3
  2. 发起 POST 请求:

    document.querySelector('#btnPost').addEventListener('click', async function () {// 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!// await 只能用在被 async “修饰”的方法中// 1. 调用 axios 之后,使用 async/await 进行简化// 2. 使用解构赋值,从 axios 封装的大对象中,把 data 属性解构出来// 3. 把解构出来的data属性,使用冒号进行重命名,一般都重命名为 { data: res }const { data: res } = await axios({method: 'POST', url: 'http://www.liulongbin.top:3006/api/post',//  POST请求体传参data: {name: 'zs',age: 20}})console.log(res)
    })
    

axios 封装的 6 个属性:

axios 在请求到数据之后,在真正的数据之外,套了一层外壳。

{    config:{ },  // data 才是服务器返回的真实数据data:{ status: 200,msg: "获取数据成功!",data: Array(6)}, headers:{ ... },request:{ },status: 200,statusText: 'OK',
}

axios直接发起GET和POST请求

document.querySelector('#btnGET').addEventListener('click', async function () {/* axios.get('url地址', {// GET 参数params: {}}) */const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/getbooks', {params: { id: 1 }})console.log(res)
})document.querySelector('#btnPOST').addEventListener('click', async function () {// axios.post('url', { /* POST 请求体数据 */ })const { data: res } = await axios.post('http://www.liulongbin.top:3006/api/post', { name: 'zs', gender: '女' })console.log(res)
})

在 Vue-cli 中使用 axios

一般我们会直接这么用:

// 在组件内
<template><button @click="postInfo">发起 POST 请求</button>
</template><script>
// 1. 导入 axios
import axios from 'axios'
export default {//2. 在 methods 定义 axios请求方法methods: {async postInfo() {const { data: res } = await axios.post('http://www.liaoyia.top:3306/api/post', { name: 'zs', age: 20 })console.log(res)}}
}
</script>

缺点: 在每次使用时候都要导入 axios 文件,写请求地址 (对后期维护不友好) 。

把 axios 挂载到 Vue原型上并配置请求根路径

避免重复导入axios 和重复写入完整请求地址。

main.js中配置

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'Vue.config.productionTip = false// 全局配置 axios 的请求根路径 (官方提供配置项)
axios.defaults.baseURL = 'http://www.liaoyia.top:3006'// 把 axios 挂载到 Vue.prototype 上,供每个 .vue 组件的实例直接使用
Vue.prototype.$http = axios// 今后,在每个 .vue 组件中要发起请求,直接调用 this.$http.xxx
// 但是,把 axios 挂载到 Vue 原型上,有一个缺点:不利于 API 接口的复用!!!new Vue({render: h => h(App)
}).$mount('#app')

使用:

<template><button @click="btnGetBooks">获取图书列表的数据</button>
</template><script>
export default {methods: {// 点击按钮,获取图书列表的数据async btnGetBooks() {const { data: res } = await this.$http.get('/api/getbooks')console.log(res)}}
}
</script>

但是把 axios 挂载到 Vue原型上并配置请求根路径也有缺点:

无法实现API接口的复用 : 在多个组件想使用同一个请求方法(API)的时候,只能在每个组件重新定义一次。

axios 封装

1.如果项目中有多个请求地址,我们可以根据多个地址使用工厂模式封装 js 模块,创建多个 axios 实例对象,并设置请求根路径 (baseURL) :

步骤如下: 在项目的 src目录下创建utils文件夹并新建一个 request.js文件:

import axiox from 'axios'const request =axiox.create({//baseURL会在发送请求的时候拼接在url参数的前面baseURL:'http://jsonplaceholder.typicode.com/',timeout:5000
})// 向外导出
export default  request
  • 使用这种方法时: 一般我们只会在一个 js 模块创建一个axios 实例对象,并向外导出。
  • 如果有多个服务器地址,那就创建多个 js模块,并在里面创建axios实例对象。

2.为了实现复用性,我们还可以把所有请求,都封装在API模块里,在API模块中,按需导出一个方法,这个方法调用 request.get 或 request.post 来请求一个接口,最后return 一个Promise 对象。

比如想调用接口获取用户相关信息:

在根目录新建 utils 文件夹并在里面新建 userAPI.js 文件

//导入 utils 文件夹下的 request.jsimport request from '@/utils/request.js'export const getArticleListAPI = function(_page, _limit) {return request.get('/articles', {params: {_page,_limit}})
}

【实例】:点击按钮发起 GET 请求并自动调用请求拦截和添加响应拦截器

<template><div class="home"><button @click="getByMineHandle">调用封装的get请求</button></div>
</template><script>// 导入 get 方法
import  { get } from '../utils/request'export default {name: 'Home',methods:{getByMineHandle(){get('',{page:3,per:2}).then(res=>console.log(res))}}
}
</script>import axiox from 'axios'const instance =axiox.create({//baseURL会在发送请求的时候拼接在url参数的前面baseURL:'http://jsonplaceholder.typicode.com/',timeout:5000
})//请求拦截
// 所有的网络请求都会先走这个方法
// 添加请求拦截器,所有的网络请求都会先走这个方法
// 我们可以在它里面为请求添加一些自定义的内容 比如 token 或者在 headers 提供信息
instance.interceptors.request.use(function (config) {// 在发送请求之前做些什么console.group('全局请求拦截')console.log(config)console.groupEnd()config.headers.token ='12343'return config;
}, function (error) {// 对请求错误做些什么return Promise.reject(error);
});// 添加响应拦截器
//此处可以根据服务器的返回状态码做响应的处理
//404 404 500
instance.interceptors.response.use(function (response) {// 对响应数据做点什么console.group('全局响应拦截')console.log(response)console.groupEnd()return response;
}, function (error) {// 对响应错误做点什么return Promise.reject(error);
});// 从服务器查看获取数据
export function get(url,params) {return instance.get(url,{params})
}// 向服务器创建数据
export function post(url,data) {return instance.post(url,data)
}// 从服务器删除数据
export  function del(url) {return instance.delete(url)
}//  向服务器发送更新数据
export  function put(url,data) {return instance.put(url,data)
}


Vue-router

SPA 与前端路由

SPA (单页面网页),所有组件的展示与切换都在这唯一的一个页面内完成。

此时,不同组件之间的切换需要依赖 前端路由(router)来实现。

什么是前端路由?

  • Hash 地址与组件之间的对应关系,不同的Hash 展示不同的页面。

前端路由的工作方式:

vue-router 的基本用法

vue-router 是 vue.js 官方给出的路由解决方案。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目中组件的切换。

  1. 安装 vue-router 包

    npm i vue-router@3.5.2 -S
    

    此时 src 源码目录下会新增一个 router 文件夹。

  2. 创建路由模块

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

    //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
    
  3. 导入并挂载路由模块

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

    import Vue from'vue'
    import App from'./App.vue'//1.导入路由模块
    import router from@/router'new Vue ({
    render:h=>h(App),//2.挂载路由模块
    router:router
    }).$mount('#app')
    
  4. 声明路由链接和占位符

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

    <tcmplate><div class="app-container"><h1>App组件</h1><!--1.定义路由链接--><router-link to="/home">首页</router-link><router-link to="/movie">电影</router-link><router-link to="/about">关于</router-link><!--2.定义路由的占位符--><router-view></router-view></div>
    </template>
    
  5. 声明路由的匹配规则

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

//导入需要使用路由切换展示的组件import Home from'@/components/Home.vue'
import Movie from'@/components/Movie.vue'
import About from'@/components/About.vue'//2.创建路由的实例对象
const router=new VueRouter({//在routes数组中,声明路由的匹配规则routes:[//path 表示要匹配的hash地址;component表示要展示的路由组件{path:'/home',component: Home},{path:'/movie',component: Movie},{path:'/about',component: About}
]
})

路由重定向

当访问地址 A 的时候,强制用户跳转 到地址C, 从而展示特定的组件页面。

  • 通过路由规则的 redirect 属性,指定一个新的路由地址来实现路由的重定向。

下面这个应用场景,当用户访问网页 / 目录时候,跳转到首页:

const router=new VueRouter({
//在routes数组中,声明路由的匹配规则routes:[//当用户访问 / 的时候,通过 redirect 属性跳转到 /home 对应的路由规则{ path: '/',redirect:'/home' },{ path: '/home',component: Home },{ path: '/movie',component: Movie },{ path: '/about',component: About }
})

路由高亮

可以通过如下的两种方式,将激活的路由链接进行高亮显示:

  1. 使用默认的高亮 class 类
  2. 自定义路由高亮的 class 类

默认的高亮 class 类

被激活的路由链接,默认会应用一个叫做 router-link-active 的类名, 可以使用此类名选择器,为激活 的路由链接设置高亮的样式:

/*在index.css全局样式表中,重新router-link-active的样式*/.router-link-active {color:#ef4238;font-weight:bold;border-bottom: 2px #ef4238 solid;
}

自定义路由高亮的 class 类

在创建路由的实例对象时,开发者可以基于 linkActiveClass 属性,自定义路由链接被激活时所应用的类名:

const router=createRouter({history:createlebHashHistory(),// 指定被激活的路由链接,会应用 router-active 这个类名,// 默认的 router-link-active 类名会被覆盖掉1inkActiveclass:'router-active',routes:[{ path: '/',redirect: '/home' },{ path: '/home',component: Home },{ path:'/movie',component: Movie },{ path: '/about',component: About },
]
})

嵌套路由

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

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

上图中,我们在 About.vue 组件中套娃了 tab1 和 tab2 组件 ,如果我们想展示它,则需要声明子路由链接以及子路由占位符 :

<template><div class="about-container"><h3>About 组件</h3><!--1.在关于页面中,声明两个子路由链接--><router-link to="/about/tab1">tab1</router-link><router-link to="/about/tab2">tab2</router-link><hr/><!--2.在关于页面中,声明子路由的占位符--><router-view></router-view></div>
</template>

到此为止,我们已经有了 子路由链接子路由占位符,以及链接对应的组件,但是要在页面显示,我们还缺少对应关系,也就是路由规则

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

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

// 导入组件
import Tab1 from '@/components/tabs/Tab1.vue'
import Tab2 from '@/components/tabs/Tab2.vue'const router=new VueRouter({routes:[{  //about 页面的路由规则(父级路由规则)path: '/about',component: About,//1.通过children属性,嵌套声明子级路由规则children:[{ path: 'tab1',component: Tab1 },//2.访问/about/tab1时,展示Tab1组件{ path: 'tab2',component: Tab2 } //2.访问/about/tab2时,展示Tab2组件
}]
})
  • 注意:使用 children 属性嵌套声明子级路由规则时,path 名称不需要加 /
3. 默认子路由

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

// src/router/index.jsconst router=new VueRouter({routes:[{ path: '/about',component: About,// rediect:  'about/table'  // 通过重定向设置默认展示页面children:[//  path值为空字符串,此时Tab1为默认子路由,一进入About页面,默认展示 Tab1{ path: '',component: Tab1 },{ path: 'tab2',component: Tab2 } }]
})
  • 所以,默认子路由路由重定向都可以用来设置 展示特定的组件页面

动态路由匹配

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

有如下3个路由链接:

<router-link to="/movie/1"> 电影1 </router-link>
<router-link to="/movie/2"> 电影2 </router-link>
<router-link to="/movie/3"> 电影3 </router-link>

如果定义下面3个路由规则,虽然可行,但太过繁琐且复用性差:

{path:'/movie/1',component:Movie}
{path:'/movie/2',component:Movie}
{path:'/movie/3',component:Movie}

使用动态路由,将上面创建3个路由规则,合并成一个,提高了路由规则的复用性:

//  src/router/index.js 文件//路由中的动态参数以 : 进行声明,冒号后面的是动态参数的名称
{ path:'/movie/:id',component:Movie}
  • 在 vue-router 中使用英文的冒号(:)来定义路由的参数项 。
$route.params 访问动态匹配的参数值

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

<template><div class="movie-container"><!--this.$route是路由的“参数对象”--><h3> Movie 组件-- {{this.$route.params.id}} </h3></div>
</template><script>export default{name:'Movie'}
</script>
使用 props 接收路由参数

为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参, 在定义路由规则时,声明props : true 选项 ,

// router下的 index.js 文件
// 1. 声明 props : true 选项
{ path:'/movie/:id',component: Movie,props:true }

定义好后,即可在Movie组件中,以props的形式接收到路由规则匹配到的参数项。

<template><!-- 3、直接使用props中接收的路由参数 --><h3> MyMovie组件--{{id}}</h3>
</template><script>
export default{//2、使用props接收路由规则中匹配到的参数项props:['id']
</script>

编程式导航 API

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

  1. this.$router. push(‘hash 地址’)

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

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

<template><div class="home-container"><h3> Home组件 </h3><button @click="gotoMovie">跳转到Movie页面</button></div>
</template><script>
export default{methods:{gotollovie(){this.$router.push("/movie/1")}
}
</script>

push 和 replace 的区别:

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

调用 this.$router.go() 方法,可以在浏览历史中前进和后退:

<template><h3> MyMovie组件---{{id}}</h3><button @click="goBack" >后退</button>
</template><script>
export default{props:['id'],methods:{goBack(){this.$router.go(-1) //后退到之前的组件页面}
},
</script>

导航守卫

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

//创建路由实例对象
const router = new VueRouter({..…})//调用路由实例对象的beforeEach方法,即可声明“全局前置守卫"
//每次发生路由导航跳转的时候,都会自动触发这个“回调函数”router.beforeEach((to,from,next) =>{/* 必须调 next 函数 */
})

守卫方法的 3 个形参:

  • to 是将要访问的路由的信息 (对象)
  • from 是将要离开的路由的信息对象
  • next 是一个函数,一定要调用 next () 才表示放行,允许这次路由导航 。

注意:

  1. 在守卫方法中如果不声明 next 形参,则默认允许用户访问每一个路由
  2. 在守卫方法中如果声明了 next 形参,则必须调用 next() 函数,否则不允许用户访问任何一个路由!

next 函数的 3 种调用方式

  1. 直接放行:next()
  2. 强制其停留在当前页面:next(false)
  3. 强制其跳转到登录页面:next(’/login’)

结合 token 控制后台主页的访问权限

router.beforeEach((to,from,next) => {//获取浏览器缓存的用户信息const userInfo = window.localStorage.getItem('token');if (to.path === '/main' && !userInfo ){ //访问的是 main 页面且 token 不存在// 访问的是后台主页,但是没有token的值,跳转到登入页next('/login')  } else{next()//访问的不是后台主页,直接放行
})

上面代码中,如果项目中有多个页面都需要设置访问权限怎么办呢

  • 使用路由导航守卫 判断访问的是否是一个有权限的 Hash 地址问题:

    // 第一种: 使用 || 多重判断,代码臃肿

    router.beforeEach((to,from,next) => {
    if (to.path === ‘/main’ || to.path === ‘/user’ || to.path === ‘seeting’){
    // code…
    } else{
    // …
    }
    })

我们可以把多个地址存入到一个数组或者一个json文件、js文件:

const patArr = ['/mian', '/home', '/home/users', '/home/rights']router.beforeEach((to,from,next) => {if (patArr.indexOf(to.path)  !== -1){// code....} else{// ....  }
})  // 存入 js 使用// 1. 定义一个 pathArr.js文件
export default ['/mian', '/home', '/home/users', '/home/rights']
// 2. 使用时导入
import  pathArr from '@/router'

meta 路由原信息

上面的例子中,还是不够方便,vue-router 中给我们提供了 meta 字段,传入一个对象,设置requiresAuth: true ,就相当于开起来跳转验证。

const router = new VueRouter({routes: [{path: '/blog',component: Blog,path: 'bar',component: Bar,// 设置 meta ,开启跳转验证meta: { requiresAuth: true }}]
})

全局守卫中定义页面权限:

router.beforeEach((to, from, next) => {console.log(to) //判断 $route.matched数组 是否中有 meta 字段值if (to.matched.some(record => record.meta.requiresAuth)) {如果当前页面的 是否有 userif (!localStorage.getItem("user")) {next({path: '/login',//传入完整的路径query: { redirect: to.fullPath }})} else {next()}} else {next() // 确保一定要调用 next() 这次放行说明在白名单}
})

更改登入页面的逻辑

// Login.vue
methods:{handLeLonge(){// 获取用户名和密码setTimeout(()=>{let data = {user: this.user}// 保存用户名到本地localStorage.setItem('user',JSON.stringify(data))this.$route.push({path: this.$route.query.redirect})})}}

拓展 - 网页链接详解

 // APP 组件<router-link to="/movie/1"> 洛基 </router-link><router-link to="/movie/2?name=zs&age=20"> 雷神< /router-link><router-link to="/movie/3"> 复联 </router-link><router-link to="/about">   关于 </router-link><template><div class="movie-container"><!-- this.$route  是路由的“参数对象” --><!-- this.$router 是路由的“导航对象” --><h3>Movie 组件 --- {{ $route.params.mid }} --- {{ mid }}</h3><button @click="showThis"> 打印 this</button><!-- 在行内使用编程式导航跳转的时候,this 必须要省略,否则会报错! --><button @click="$router.back()">back 后退</button><button @click="$router.forward()">forward 前进</button></div>
</template><script>
export default {name: 'Movie',// 接收 props 数据props: ['mid'],methods: {showThis() {console.log(this)},}
}
</script>

点击雷神电影打印出的 this 里,有一个 $route 对象, 如下:

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

    • 在路由“参数对象”中,需要使用 this.$route.params 来访问路径参数
    • 但是我们一般会使用 props传参来接收路径参数。
  2. 在 hash 地址中,? 后面的参数项,叫做**“查询参数”**
    • 在路由“参数对象”中,需要使用 this.$route.query 来访问查询参数
  3. this.$route 中,path 只是路径部分;fullPath 是完整的地址。

开发思路:

  • 要想从一个地址跳到另一个地址 (如: 登录后立即跳转到首页,并且首页里设置默认的展示的页面)

    • 先要确定 离开谁要去哪儿
    • 找到 要离开 的页面,给它添加 redirect指向新地址。

注意点 :

  • 标签节点不平级时无法使用 v-if 和 v-else 结合來判断

  • 在 vue 中:

    • 在使用组件的时候,如果某个属性名是“小驼峰”形式,则绑定属性的时候,建议改写成“连字符”格式。例如 cmtCount 建议写成 cmt-count


Vuex

Vuex 是什么

vuex 是终极的组件之间的数据共享方案。

  • Vuex 是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享。

优点:

  1. 集中管理共享的数居,易于开发和维护
  2. 高效地实现组件之间的数据共享,提高开发效率
  3. 响应式数据,与页面的同步
什么样的数据适合存储到 Vuex 中 ?

一般情况下,只有组件之间共享的数据,才有必要存储到 vuex 中;对于组件中的私有数据,依旧存储在组件自身的data中即可 !

Vuex 的基本使用:
  1. 安装 vuex 依赖包

     npm install vuex --save
    
  2. store / index.js 导入包 并创建 store 对象

    //导入 vuex 包
    import Vuex from 'vuex'
    Vue.use(Vuex)// 创建 store 对象
    const store = new Vuex.Store({// state 中存放的就是全局共享的数据state: { count: 0 },mutations: {},actions: {},modules: {}
    })
    
  3. 将 store 对象挂载到 vue 实例main.js

    import store from './store/index';new Vue({el: '#app',render: h => h(app),router,// 将创建的共享数据对象,挂载到 Vue 实例中// 所有的组件,就可以直接从 store 中获取全局的数据了store
    })
    

一张图看懂 vuex:

state 数据源

State 提供唯一的公共数据源,所有共享的数据都要统一放到 Storestate 中进行存储。

访问 state 中数据两种方式 :

this.$store.state.全局数据名称 访问:
// this.$store.state.全局数据名称
<h3>当前最新的count值为:{{$store.state.count}}</h3>
  • 由于在模板字符串中,是不需要写 this 的。
mapState 映射为计算属性:

通过 mapState 函数,将当前组件需要的全局数据,映射为 computed 计算属性:

// 1. 在想使用数据的组件中 从 vuex 中按需导入 mapState 函数
import { mapState } from 'vuex'// 2. 将全局数据,映射为当前组件的计算属性
computed: {...mapState(['count'])
}

mutations 变更数据

mutation 用于变更 store 中的数据。

  • 只能通过 mutation 变更 Store 数据,不可以直接操作 Store 中的数据。
  • 虽然操作起来稍微繁琐,但可以集中监控所有数据的变化。
this.$store.commit() 触发 mutations
// 定义 Mutation
const store = new Vuex.Store({state: {count: 0},mutations: {add(state) {// 变更状态state.count++}}
})// 在组件中 触发 mutation
methods: {handle1() {// 触发 mutations 的第一种方式this.$store.commit('add')}
}

可以在触发 mutations 时传递参数

// 定义Mutation
const store = new Vuex.Store({state: {count: 0},mutations: {addN(state, step) {// 变更状态state.count += step}}
})// 触发mutation
methods: {handle2() {// 在调用 commit 函数,// 触发 mutations 时携带参数this.$store.commit('addN', 3)}
}
mapMutations 映射为方法
  1. 从vuex中按需导入 mapMutations函数

    import { mapMutations } from 'vuex'
    
  2. 通过刚才按需导入的 mapMutations 函数,映射为当前组件的methods函数。

    // 2. 将指定的 mutations 函数,映射为当前组件的 methods 函数
    methods: {...mapMutations(['add', 'addN'])
    }
    

实例:

// store
mutations: {add(state) {// 变更状态state.count++},sub(state) {state.count--},addN(state, step) {// 变更状态state.count += step},subN(state, step) {state.count -= step}
},// 组件A
import { mapState,mapMutations } from 'vuex'
methods:{...mapMutations(['sub','subN']),decrement(){// 调用 this.sub()},decrementN(){this.subN(5)}
}

Actions 处理异步操作

如果通过异步操作变更数据,必须通过 Action,而不能使用Mutation,但是在 Action中还是要通过触发Mutation的方式间接变更数据

注意: 在Actions 中不能直接修改 state中的数据,要通过 mutations修改

this.$store.dispatch 触发 Actions
// store/index.js 定义 Actionconst store = new Vuex.store({// ...省略其他代码mutations: {// 只有 mutations中的函数才有权利修改 state。// 不能在 mutations里执行异步操作。add(state) {state.count++}},actions: {// 在Actions 中不能直接修改 state中的数据,要通过 mutations修改。addAsync(context) {setTimeout(() => {context.commit('add')}, 1000);}},
})// 在组件中 触发 Action
methods:{handle(){// 触发 actions 的第一种方式this.$store.dispatch('addAsync')}
}
mapActions 映射为方法
// 1. 从Vuex中按需导入 mapActions 函数。import {mapActions} from 'vuex'// 2. 将指定的 actions 函数,映射为当前组件 methods 的方法。
methods:{...mapActions(['subAsync']),decrementAsync(){this.subAsync()}
}// store/index.js
actions: {// 在Actions 中不能直接修改 state中的数据,要通过 mutations修改。subAsync(context){setTimeout(() => {context.commit('sub')}, 1000);}
}

Getter 按需展示数据

Getter 用于对 Store中的数据进行加工处理形成新的数据。筛选或者排序显示。

  1. Getter 不会修改 Store 中的原数据,它只起到一个包装器的作用,将Store中的数据加工后输出出来。

  2. Store 中数据发生变化, Getter 的数据也会跟着变化。

    //定义 Getter
    const store = new Vuex.Store({
    state:{
    count:0
    },
    getters: {
    showNum(state) {
    return ‘当前最新的数量是【’ + state.count + ‘】’
    }
    },
    })

this.$store.getters.名称 访问
this.$store.getters.名称
mapGetters 映射为计算属性
import { mapGetters } from 'vuex'computed:{...mapGetters(['showNum'])
}

简写 !!!

其实,通过mapState,mapMutations,mapActions,mapGetters映射过来的计算属性,或者方法都可以直接调用,不用在 commit 或者 dispatch

正常写法:

<button @click="decrementAsync"> -1Async</button>import {mapActions} from 'vuex'methods: {...mapActions(['subAsync']),decrementAsync(){this.subAsync()}
},

其实可以简写成:

<button @click="decrementAsync"> -1Async</button>import {mapActions} from 'vuex'//...省略一些代码methods: {...mapActions(['subAsync']),
},

有参数的时候,也可以直接把参数带上,就像这样:

<button @click="subAsync(5)"> +5 </button>import {mapActions} from 'vuex'//...省略一些代码methods: {...mapActions(['addAsync']),
},

Vue 3

组件之间的数据共享

Vue 3 中的自定义事件

在vu3中使用使用自定义事相较vu2中多了一个 emits 节点声明:

vue 3中使用自定义事件:

在封装组件时:

1. 在`emits`节点声明自定义事件
2. `触发`监听事件

在使用组件时: 监听自定义事件

声明自定义事件:

// 子组件
<button @click="onBtnClick"> +1 </button><script>
export default {// 自定义事件必须声明到 emits 节点中emits: ['chage']
}
</script>

触发自定义事件:

// 子组件
<button @click="onBtnClick"> +1 </button><script>
export default {// 自定义事件必须声明到 emits 节点中emits: ['chage'],data() {// 子组件自己的数据,将来希望把 count 值传给父组件return { count: 0 }},methods: {onBtnClick() {this.count t= 1//修改数据时,通过 $emit()触发自定义事件// 当点击 '+1' 按钮时 调用this.$emit 触发自定义的 numchange 事件this.$emit( 'numchange')}}
}
</script>

监听自定义事件:

// 父组件
// 使用 v-on 指令绑定监听事件
<Son @numchange="getNewCount"></Son>methods : {getNewCount(val) {console.log('监听到了 count 值的变化', val)
},

组件上的 v-model

需要维护组件内外数据的同步时,可以在组件上使用 v-model 指令。示意图:

父子组件数据共享

实现父子组件数据双向同步。

子组件向父组件共享数据:

  • 父组件通过 v-bind 属性绑定向子组件共享数据,子组件使用 props接收数据。

父组件向子组件共享数据:

  • 通过自定义事件 emits ,然后触发 $emit(), 子组件监听。

  1. 在 v-bind: 指令之前添加 v-model 指令
  2. 在子组件中声明 emits 自定义事件,格式为必须 update:xxx
  3. 调用 $emit() 触发自定义事件,更新父组件中的数据

使 v-model 实现子→父组件共享数据好处:

在父组件中 不用再监听自定义事件了,也不用再额外声明事件处理函数。

父组件:

// 使用双向数据绑定指令
<my-son v-model:number="count"> </my-son>data(){return{count: 0}
}

子组件:

// 子组件<button @click="add"> +1 </button><script>
export default {prpos:['number']emits:['update:number']methods: {add() {this.$emit( 'update:number',this.num+1)}}
}
</script>

后代关系组件之间的数据共享

指的是父节点的组件向其子孙组件共享数据。

使用步骤:

  1. 父节点通过 provide 方法,对子孙组件共享数据。

    export default {data() {return {color: 'red', // 1. 定义"父组件"要向"子孙组件"共享的数据}},provide() {  // provide函数 返回要共享的数据对象return {color: this.color,count: 1,}},
    }
    
  2. 子孙节点通过 inject 接收数据。

    <template><h5>三级组件 --- {{ color }} --- {{ count }}</h5>
    </template><script>
    export default {// 子孙组件,使用 inject 接收父节点共享的数据inject: ['color', 'count'],
    }
    </script>
    

父节点对外共享响应式的数据

父节点使用 provide 向下共享数据时并非响应式,我们可以结合 computed 函数向下共享响应式的数据

import { computed } from 'vue' // 1. 从vue中按需导入 computed 函数export default {data() {return {color: 'red',}},provide() {  return {// 2. 使用 computed 函数,把共享数据包装为"响应式"的数据color: computed(() => this.color),count: 1,}},
}

子孙节点使用响应式数据, 注意接收的响应式数据必须以 .value 的形式使用:

<template>// 响应式数据,必须以.value 的形式进行使用<h5>子孙组件 --- {{ color.value }} --- {{ count }}</h5>
</template><script>
export default {// 子孙组件,使用 inject 接收父节点共享的数据inject: ['color', 'count'],
}
</script>

全局数据共享

vuex 是终极的组件之间的数据共享方案。

  • 解决大量、频繁的共享数据麻烦问题。
  • vuex 就是用来管理组件中需要共享的数据

数据共享总结

父子关系:

  1. 父 → 子 : 自定义属性
  2. 子 → 父 :自定义事件
  3. 父子组件数据共享 使用组件上的 v-model

兄弟关系: EvenBus

后代关系:provide & inject

全局数据共享: vuex

在vue3中全局配置 axios

import { createApp } from 'vue'import './assets/css/bootstrap.css'
import './index.css'import axios from 'axios'// 创建一个单页面应用程序实例
const app = createApp(App)// 配置全局请求根路径
axios.defaults.baseURL = 'https://www.escook.cn'// 全局挂载到app 根路径中
// $http 是模仿 vue封装成员的方式
app.config.globalProperties.$http = axios// 实现挂载
app.mount('#app')

在组件中使用:

methods: {async getInfo() {const { data: res } = await this.$http.get('/api/get', {// get 请求必须通过 params 传参params: {name: 'ls',age: 33,},})console.log(res)},},

vue3 中创建路由

  • vue3 中需要安装 4.x的版本

  • 在 src 目录下创建 router.js 文件并配置

    import { createRouter, createWebHashHistory } from ‘vue-router’

    import Login from ‘./components/MyLogin.vue’
    import Home from ‘./components/Home.vue’

    // 创建路由实例对象
    const router = createRouter({
    history: createWebHashHistory(), // 指定通过 hash 管理路由的切换
    routes: [
    { path: ‘/home’, redirect: ‘/login’ },
    { path: ‘/login’, component: Login, name: ‘login’ },
    ],
    })

    export default router //4. 向外共享路由对象

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

import { createApp } from 'vue'
import App from './App.vue'import router from './router'// 创建 app 实例
const app = createApp(App)app.use(router)// 挂载 app 实例
app.mount('#app')

事件:

// 父组件
// 使用 v-on 指令绑定监听事件
<Son @numchange="getNewCount"></Son>methods : {getNewCount(val) {console.log('监听到了 count 值的变化', val)
},

组件上的 v-model

需要维护组件内外数据的同步时,可以在组件上使用 v-model 指令。示意图:

父子组件数据共享

实现父子组件数据双向同步。

子组件向父组件共享数据:

  • 父组件通过 v-bind 属性绑定向子组件共享数据,子组件使用 props接收数据。

父组件向子组件共享数据:

  • 通过自定义事件 emits ,然后触发 $emit(), 子组件监听。

  1. 在 v-bind: 指令之前添加 v-model 指令
  2. 在子组件中声明 emits 自定义事件,格式为必须 update:xxx
  3. 调用 $emit() 触发自定义事件,更新父组件中的数据

使 v-model 实现子→父组件共享数据好处:

在父组件中 不用再监听自定义事件了,也不用再额外声明事件处理函数。

父组件:

// 使用双向数据绑定指令
<my-son v-model:number="count"> </my-son>data(){return{count: 0}
}

子组件:

// 子组件<button @click="add"> +1 </button><script>
export default {prpos:['number']emits:['update:number']methods: {add() {this.$emit( 'update:number',this.num+1)}}
}
</script>

后代关系组件之间的数据共享

指的是父节点的组件向其子孙组件共享数据。

使用步骤:

  1. 父节点通过 provide 方法,对子孙组件共享数据。

    export default {data() {return {color: 'red', // 1. 定义"父组件"要向"子孙组件"共享的数据}},provide() {  // provide函数 返回要共享的数据对象return {color: this.color,count: 1,}},
    }
    
  2. 子孙节点通过 inject 接收数据。

    <template><h5>三级组件 --- {{ color }} --- {{ count }}</h5>
    </template><script>
    export default {// 子孙组件,使用 inject 接收父节点共享的数据inject: ['color', 'count'],
    }
    </script>
    

父节点对外共享响应式的数据

父节点使用 provide 向下共享数据时并非响应式,我们可以结合 computed 函数向下共享响应式的数据

import { computed } from 'vue' // 1. 从vue中按需导入 computed 函数export default {data() {return {color: 'red',}},provide() {  return {// 2. 使用 computed 函数,把共享数据包装为"响应式"的数据color: computed(() => this.color),count: 1,}},
}

子孙节点使用响应式数据, 注意接收的响应式数据必须以 .value 的形式使用:

<template>// 响应式数据,必须以.value 的形式进行使用<h5>子孙组件 --- {{ color.value }} --- {{ count }}</h5>
</template><script>
export default {// 子孙组件,使用 inject 接收父节点共享的数据inject: ['color', 'count'],
}
</script>

全局数据共享

vuex 是终极的组件之间的数据共享方案。

  • 解决大量、频繁的共享数据麻烦问题。
  • vuex 就是用来管理组件中需要共享的数据

数据共享总结

父子关系:

  1. 父 → 子 : 自定义属性
  2. 子 → 父 :自定义事件
  3. 父子组件数据共享 使用组件上的 v-model

兄弟关系: EvenBus

后代关系:provide & inject

全局数据共享: vuex

在vue3中全局配置 axios

import { createApp } from 'vue'import './assets/css/bootstrap.css'
import './index.css'import axios from 'axios'// 创建一个单页面应用程序实例
const app = createApp(App)// 配置全局请求根路径
axios.defaults.baseURL = 'https://www.escook.cn'// 全局挂载到app 根路径中
// $http 是模仿 vue封装成员的方式
app.config.globalProperties.$http = axios// 实现挂载
app.mount('#app')

在组件中使用:

methods: {async getInfo() {const { data: res } = await this.$http.get('/api/get', {// get 请求必须通过 params 传参params: {name: 'ls',age: 33,},})console.log(res)},},

vue3 中创建路由

  • vue3 中需要安装 4.x的版本

  • 在 src 目录下创建 router.js 文件并配置

    import { createRouter, createWebHashHistory } from ‘vue-router’

    import Login from ‘./components/MyLogin.vue’
    import Home from ‘./components/Home.vue’

    // 创建路由实例对象
    const router = createRouter({
    history: createWebHashHistory(), // 指定通过 hash 管理路由的切换
    routes: [
    { path: ‘/home’, redirect: ‘/login’ },
    { path: ‘/login’, component: Login, name: ‘login’ },
    ],
    })

    export default router //4. 向外共享路由对象

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

import { createApp } from 'vue'
import App from './App.vue'import router from './router'// 创建 app 实例
const app = createApp(App)app.use(router)// 挂载 app 实例
app.mount('#app')

)

Vue2-Vue3.0学习笔记(2021年黑马程序员最新视频)相关推荐

  1. Linux 学习笔记(借鉴黑马程序员Linux课程)

    Linux视频课程 简介 Linux诞生于1991年,由林纳斯·托瓦兹在21岁时完成.此后成为最为流行的服务器操作系统之一. Linux内核和系统发行版 由Linux系统内核和系统级应用程序两部分组成 ...

  2. Go语言学习笔记bi站黑马程序员一天刷完

    go语言的优势可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了.静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问 ...

  3. maven学习笔记之配置(黑马程序员)

    1.去官网下载maven的安装包 2.解压到你要安装的目录下,打开安装包就如下: 其中:bin下是所有maven可运行的指令,mvn是核心文件,运行时候运行的就是mvn.cmd boot里面有一个ja ...

  4. 黑马程序员 c++视频学习详细笔记 下载地址

    黑马程序员 c++视频学习详细笔记 下载地址: https://download.csdn.net/download/m0_47891203/84348174 建议结合博主博文学习

  5. asp.net2.0学习历程 菜鸟到中级程序员的飞跃【月儿原创】

    asp.net2.0学习历程 菜鸟到中级程序员的飞跃 --30本好书点评 作者:清清月儿 主页:http://blog.csdn.net/21aspnet/           时间:2007.5.1 ...

  6. asp.net2.0学习历程 菜鸟到中级程序员的飞跃[z转]

    asp.net2.0学习历程 菜鸟到中级程序员的飞跃 --30本好书点评 学历历程 如果你是一个菜鸟或者自认为初学者那么本文非常适合你: 不能说这30本书就是最佳组合,但是可以说这个组合不差: 本人曾 ...

  7. 【C++学习汇总】【黑马程序员】

    [C++学习汇总] 1 黑马程序员 2 深蓝学院 3 自发式收集学习 1 黑马程序员 [C++][第一篇][黑马 p84 - p105 ][引用][重载][类和对象-struct.class] [C+ ...

  8. asp.net2.0学习历程 菜鸟到中级程序员的飞跃 转载

    asp.net2.0学习历程 菜鸟到中级程序员的飞跃 --30本好书点评 主页:http://blog.csdn.net/21aspnet/           时间:2007.5.16 学历历程 如 ...

  9. 如何系统学习一门编程语言? | 黑马程序员

    一.从认识编程语言开始: 语言有很多种,包括汉语.英语.法语.韩语等,尽管输出的形式不同,但可以达到同样的目的.同样,我们也可以通过「语言」来控制计算机,让计算机为我们做事情,这样的语言就叫做编程语言 ...

  10. 尚硅谷+黑马程序员MongoDB视频学习笔记(一)

    本学习笔记是来源于学习B站上的尚硅谷和黑马的MongoDB教学视频而做的知识总结. 一.数据库(Database) 数据库是按照数据结构来组织.存在和管理数据的仓库.说白了,数据库就是存在数据的仓库. ...

最新文章

  1. 清华本硕男,月入5W征婚引群嘲“普通却自信”!本人回应了……
  2. 【C语言简单说】八:分支结构之if...else...(2)
  3. 循序渐进Python3(十二) --2--  web框架之django简单实现oracle数据库操作
  4. 关于用java做微信机器人的艰辛过程
  5. linux on android 项目,好累,终于完成了 Android-on-Yeeloong 项目的搭建
  6. 改变Oracle 默认显示格式
  7. SAS编程基础 - 数据获取与数据集操作(1)
  8. xshell官网免费版下载
  9. 实现 Excel 多列数据组合
  10. 小白一键重装有linux,linux小白说说用linux的感受
  11. 微型计算机温度控制系统,单片机实现PC机温度控制系统方案
  12. 比例导引 matlab,比例导引法Matlab仿真.docx
  13. 阿联酋esma认证_阿联酋发布ECAS认证计划
  14. vue+elementUI中使用Echarts (懒人无脑版)
  15. android平板电脑怎么样,苹果还是安卓?一图教你如何选择适合自己的平板
  16. 卧推动作发力技巧分析
  17. 三招通过Apollo和nacos的能力进行国际化热更新
  18. mfc7420调整复印浓度_兄弟MFC7420一体机复印要加浓怎么操作 – 手机爱问
  19. chrome浏览器network报错:ERR_CERT_AUTHORITY_INVALID
  20. docker的容器操作命令及其使用技巧

热门文章

  1. webpack之基础篇(四):webpack-dev-server介绍
  2. 解决octavia failed to run
  3. android jni介绍
  4. Paypal支付跳转失败的原因及解决办法
  5. 建立自己的kindle书库
  6. Web前端面试指导(四):面试前准备-有备而去百战百胜
  7. 搭配-最全的配色方案和色彩搭配
  8. 护眼html颜色,在电脑中设置护眼颜色、更换网页背景色、一键护眼
  9. 开源磁力搜索爬虫dhtspider原理解析
  10. 【C系列】结构体数组初始化方法