Vue学习笔记——麻麻再也不用担心我的Vue!

Vue学习笔记——步步高点读机,麻麻再也而不用担心我的Vue!

这是一个学习笔记,根据b站蔡蔡小趴菜的vue教学视频做的

注:CSDN会对段落内的html标签屏蔽,所以本文非代码块的html标签以缺半的形式展示

Vue应用结构

Vue大致结构如下:

在这里插入图片描述

  • package-lock.json: 在使用 npm 安装包时自动生成的一个文件,它用于锁定当前项目的包的版本,以确保在不同的环境中安装的 包的版本一致

  • package.json: 项目的配置文件,包含了项目的依赖、脚本命令、版本号等信息。

  • README.MD: 说明

  • vite.config.js: Vite 构建工具的配置文件,它用于配置和定制 Vite 项目的构建行为和开发环境

  • public文件夹: 存放不需要经过Webpack处理的静态资源文件。这些文件会直接复制到构建目录中,并可以在HTML文件中

  • src文件夹: 源代码

  • assets文件夹: 用于存放应用需要的静态资源文件,如图片、字体、样式等。这些文件可以在组件中通过相对路径引用 (与public区别是会经过Webpack处理,如果部署在服务器上的话就把静态资源放在这里)

  • components文件夹: 存放vue组件

  • App.vue: vue的根组件

  • index.html: 这个文件里面是最基本的html页面代码,body中有

    作为引入vue组件的入口

  • main.js:

    import { createApp } from 'vue'  //引入创建vue实例的方法
    import App from './App.vue       // App就是根组件
    
    const app = createApp(App); // 创建 Vue 应用实例——基于根组件创建
    app.mount('#app'); // 将应用挂载到 id 为 "app" 的 DOM 元素上(也就是index.html的入口)
    
  • view文件夹: 存放vue的页面文件,基于路由进行页面跳转

  • router文件夹: 路由文件夹

  • index.js: 路由配置文件,包括路由页面和对应路径

模板语法、属性绑定、条件渲染、列表渲染 忘记笔记了,反正很简单,跳过(

事件处理

事件处理分为内联事件处理器方法事件处理器

事件通过 @click = "handler " 或者 v-on:click="methodName"触发

内联事件处理器

handler处是一段简单的Js代码,为一个可以返回值的表达式

<template>
    <h3>内联处理</h3>
    <button @click="count++">Add</button>
    <p>{{ count }}</p>
</template>


<script>
  export default {
    data() {
      return{
        count:0
      }

    }

  }

</script>
方法事件处理器

和内联事件处理不同,handler是一个方法名,调用methods:{}内的方法

PS:this.可以全局调用data(){}内的变量

<template>
    <h3>内联处理</h3>
    <button @click="addCount">Add</button>
    <p>{{ count }}</p>
</template>


<script>
  export default {
    data() {
      return{
        count:0
      },
	methods:{
        addCount(){
            this.count++;
        }
    }
    }

  }

</script>
事件传参

也就是说methodName(variable) 可以传递参数

其中,$event 表示传递事件对象

<template>
  <h3>事件传参</h3>
  <!-- $event 表示事件传参传递了一个event对象 -->
  <p @click="getNameHandler(item,$event)" v-for="(item,index) of names" key="index">{{ item }}</p>
</template>


<script>
export default {
  // data里的值都可以用this来索引到 
  data() {
    return{
      names:["iwen","ime","frank"]
    }

  },
  methods:{
    addCount(msg) {
      this.count++;
      console.log(msg);
    },
    getNameHandler(name,e){
      console.log(name); 
      console.log(e);
    }
  }

}

</script>

事件修饰符

​ 这里跳过了,好像没啥用

数组变化侦测

Vue会检测到变更方法对数组的更改,并在页面上同步更新数组,

names.push("Alice")

替换方法则需要按照以下代码形式使用:

this.numbers1 = this.numbers1.concat(this.numbers2);

会改变原数组的变更方法

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

不会改变原数组的替换方法

  • filiter()
  • concat()
  • slice()

计算属性

就是:

computed:{

} 内的方法

计算属性的用途:

  1. 比如下面这个

    标签内来判断itbanzhan.content的数组是否有内容

    把三目运算符放在computed内成为方法,有利于代码阅读

  2. 性能优化

    computed仅会计算一次,而methods内的方法每次调用都会执行一次

    也就是说,当这个页面有10个

    {{ itbaizhanContent }}

    时,computed内的itbaizhanContent()也只会执行一次

    而methods内的会执行10次

<template>
  <h3>{{ itbaizhan.name }}</h3>
  <p>{{ itbaizhanContent }}</p>
</template>

<script>

export default{
  data(){
    return{
      itbaizhan:{
        name:"百战程序员",
        content:["前端","Java","Python"]
      }
    }
  },
  //计算属性
  computed:{
    itbaizhanContent(){
      return this.itbaizhan.content.length >0 ? "Yes" : "No";
    }
  }
}


</script>

Class绑定

跟CSS有关

class绑定还是基于 v-bond ,但是和普通v-bond不同的是

class的v-bond支持绑定数组和对象

<template>
  <!-- 单对象绑定 -->
  <p :class="{ active: isActive, 'text-danger': hasError }">Class 样式绑定1</p>
  <!-- 多对象绑定 -->
  <p :class="classObject">Class 样式绑定2</p>
  <!-- 数组形式绑定 -->
  <p :class="[arrActive, arrHasError]">Class 样式绑定3</p>
  <!-- 三目运算符绑定 -->
  <p :class="[isActive?'active':'']">Class 样式绑定4</p>
  <!-- 数组和对象混合 -->
  <p :class="[isActive ? 'active':'',{ 'text-danger':hasError}]">Class 样式绑定5</p>

</template>

<script>
export default {
  data() {
    return {
      isActive: true,
      hasError: true,
      classObject: {
        active: true,
        'text-danger': true,
      },
      arrActive: 'active',
      arrHasError: 'text-danger',
    }
  },
}
</script>

<style>
.active {
  font-size: 30px;
}

.text-danger {
  color: red;
}
</style>

Style绑定

和class绑定同理,v-bond增强至数组和对象

侦听器

侦听器可以侦听
data(){
message:“Hello”
}

内定义的变量变化

侦听器是与data()、methods同级的watch:{}

watch内的一个方法对应监听一个变量

且方法名与被监听变量名要一致

watch:{
message(newValue,oldValue){

}

}

<template>
  <h3>侦听器</h3>
  <button @click="updataHandle">修改数据</button>

</template>

<script>
export default{
  data(){
    return{
      message:"hello"
    }
  },
  methods:{
    updataHandle(){
      this.message = "World"
    }
  },
  //侦听器
  watch:{
    //newVlaue是改变之后的数据
    //oldValue是改变之前的数据
    //函数名必须与侦听的数据对象保持一致
    message(newVlaue,oldValue){
      //数据发生变化,自动执行的函数
      console.log(newVlaue,oldValue)
    }
  }
}

</script>

表单输入绑定

v-model

v-model设置在标签里,绑定一个data()内的变量message

绑定的变量message会同步和Input内容更新

<template>
  <h3>表单输入绑定</h3>
  <form>
    <input type="text" v-model="message">
    <p>{{ message }}</p>
    <input type="checkbox" id="checkbox" v-model="checked">
  </form>
  <label for="checkbox">{{ checked }}</label>

</template>

<script>
export default{
  data(){
    return{
      message:"",
      checked:"false"
    }
  }
}
</script>

v-bond修饰符

.lazy——只有在change事件后才触发更新

.number——只能接受数字

.trim——消除空格

.lazy:

在输入过程中变量不会和输入一起更新,失去焦点这样的事件触发后才会同步更新

模板引用(DOM引用)

模板引用就是DOM引用

通过给标签设置属性 ref=“container”

后面可以在脚本中使用this.$refs.container,使用原生JS来操控该标签

<template>
  <div ref="container" class="container">{{ content }}</div>
  <button @click="getElementHandle">获取元素</button>
</template>

<script>
/**
 * 内容改变: {{ 模板语法 }}
 * 属性改变: v-bond
 * 事件:     @click=""
 * 
 * 没有特殊需求,不要操控DOM
*/
export default {
  data(){
    return{
      content:"内容"
    }
  },
  methods:{
    getElementHandle(){
      // innerHTML:原生JS的属性
      console.log(this.$refs.container.innerHTML = "hhhhh");
    }
  }
}
</script>

组件

组件组成
  1. 首先是文件构成和结构

    Vue页面分为两种:主文件App.vue和Component文件夹内的.vue文件

    Compnent内的.vue文件就是构成页面的组件

    主文件和组件文件一般都包含以下三个部分:

    <template>
    	<!-- html -->
    </template>
    <script>
    	// JavaScript
    </script>
    <style>
    	/* css */
    </style>
    
  2. 组件引入

    下面是组件Mycomponent引入主文件App.vue的例子

    MyComponent:

    <template>
      <div class="container">{{ message }}</div>
    </template>
    <script>
      export default {
        data(){
          return{
            message:"组件基础组成"
          }
        }
      }
    </script>
    <style>
    
    .container{
      font-size: 30px;
      color: brown;
    }
    
    </style>
    

    App.vue

    <template>
      <!-- 3.显示组件 -->
      <MyComponent/>
    </template>
    <script>
      //1. 引入组件
      import MyComponent from './components/MyComponent.vue';
    
      export default {
        //2. 注入组件
        components:{
          MyComponent
        }
      }
    </script>
    <style>
    
    </style>
    

在这里插入图片描述

  1. style scoped

    scoped的作用是限制生效作用域,一般用于组件内,使样式只能作用于本组件内的元素,

    如果元素被引用,样式不会作用于引用页面的元素

    举例:

    对于一个组件

    ComponentDemo.vue:

    <template>
    	<div class = 'title'>这是一个标题</div>
    </template>
    
    <style scoped>
        .title{
            font-size = 50px;
        }
    </style>
    

    APP.vue

    <template>
    	<div class = "title">App的标题</div>
    </template>
    <script>
      //1. 引入组件
      import MyComponent from './components/MyComponent.vue';
    
      export default {
        //2. 注入组件
        components:{
          MyComponent
        }
      }
    </script>
    

    如果没有scoped,APP.vue内的 标题因为与组件的class都是 title,

    会被组件内的style选择器命中,从而会有和组件标题同样的样式

组件嵌套关系

举个例子:

对于下图这个页面:

主要由五个组件构成:Header、Main、Asiede、Article和Item

引用结构:

------Header

App.vue ------Main -----Article

------Aside -----Item

Main组件内import了Article组件

Aside组件内Import了Item组件

App.vue则import了三个组件:Header、Main和Aside

以上便是组件的嵌套关系

在这里插入图片描述

组件的注册方式 ( 引入方式 )

分为全局注册局部注册

局部注册:
就是之前的三步骤,将一个组件引入另一个组件

全局注册:
在main.js内注册,注册后全局都可以在内引入并直接使用

main.js :

import { createApp } from 'vue'
import App from './App.vue'
import Header from './pages/Header.vue'


const app = createApp(App)
//在这之间进行组件注册

// 注册语句
app.component("Header",Header)

app.mount('#app')

尽量使用局部注册,局部注册的父子组件依赖关系比较明确

组件传递数据_Props
  1. 传递在组件之间
  2. 只能父组件传递给子组件
  3. 可以传递任何类型的数据
  4. Props内的变量是只读的,无法被修改

步骤:在父组件的template中,对子组件的展示标签增加属性,这些属性变量就是要传递过去的值

如果仅以 title="Parent"这种形式赋值,传递过去的数据是String型静态数据

如果想要动态数据,或者是数字、数组和对象型数据,就需要用 :age:" " 进行属性绑定,在data内定义变量的类型和值

Parent.vue

<template>
  <h3>Parent</h3>
  <Child title="Parent 数据" demo = "测试" :test="msg" :age="age" :names="names" :people="people"/>
</template>

<script>
import Child from './Child.vue';
  export default {
    data(){
      return{
        msg:"动态Message",
        age:20,
        names:["li",'ime'],
        people:{
          name:"Ak",
          age:16
        }
      }
    },
    components:{
      Child
    }
  }
</script>

Child.vue

<template>
  <h3>Child</h3>
  <p>{{ title }}</p>
  <p>{{ demo }}</p>
  <p>{{ test }}</p>
  <p>{{ age }}</p>
  <ul>
    <li v-for="(item,index) of names" :key="index">{{ item }}</li>
  </ul>
  <p>{{ people.name }} {{ people.age }}</p>
</template>

<script>
  export default {
    data(){
      return{
        
      }
    },
    props:["title","demo","test","age","names","people"]
  }
</script>
组件传递Props校验 (对类型的校验、默认值和必选项的校验)

简单props以数组方式接受父组件传递过来的各个变量
如上面:props:[“title”,“demo”,“test”,“age”,“names”,“people”]

如果要对Props进行校验,则要改写成下面的格式:

<script>
  export default {
    data(){
      return{
        
      }
    },
    props:{
        title:{
            type:[String,Number,Array,Object],
            required:true,
            default:"Test"
        },
        names:{
            type:[Array],
            default(){
                return ["空"]
            }
        }
    }
  }
</script>

type:[] 用数组来表示变量的类型限制,变量接收的数据只能是这几种

required: 表示该变量是否是必须的

default则为默认值…注意: 数字、字符串和布尔值可以直接default: ,但是其他类型要函数返回

组件事件 (可以实现子传父)

组件事件就是子组件methods中定义的一个方法,该方法内用this.$emit进行标识

举例:Child.vue 和 ComponentEvent.vue

Child.vue:

通过按钮触发chickEventHandle方法

该方法内的语句 this.$emit标识了该语句就是组件事件触发器

this.$emit() 方法接收一个或两个参数 ,第一个参数是父组件响应方法的方法名,第二个参数是传给父组件响应方法的参数(可选)

<template>
  <h3>Child</h3>
  <button @click="clickEventHandle">子按钮:传递数据</button>
</template>
<script>
  export default {
    data(){
      return{
        msg:"Child数据"
      }
    },
    methods:{
      clickEventHandle(){
        //自定义事件
        this.$emit("myEvent",this.msg)
      }
    }
  }
</script>

ComponentEvent.vue

父组件内,下引用的子组件标签要以

@myEvent=“methodsname” 的形式接收组件事件

其中 myEvent是响应子组件的名字,methodsname是父组件要调用的方法

<template>
  <h3>组件事件</h3>
  <Child @myEvent="fatherHandle"/>
  <p>{{ message }}</p>
</template>
<script>
import Child from './Child.vue';
  export default {
    components:{
      Child
    },
    data(){
      return{
        message:""
      }
    },
    methods:{
      fatherHandle(data){
        this.message = data
      }
    }
  }
</script>

以上,子组件也可以简写为 @click直接调用匿名函数

<button @click="$emit('increaseBy', 1)"> 

 Increase by 1 

</button>
组件事件配合v-model

一般来说是子父组件的输入v-model绑定

比如子组件Search.vue中有一个输入框,当子组件输入框输入数据时,父组件Main.vue会同步展示

原理:

子组件input框v-model绑定一个变量 search,search与input输入同步更新

同时子组件新增一个watch侦听变量search,同时通过组件事件this.$emit将search每次更新的新值传递给父组件

Search.vue:

<template>
  搜索:<input type="text" v-model="search">
</template>
<script>
  export default {
    data(){
      return {
        search:""
      }
    },
    watch:{
      search(newValue,oldValue){
        this.$emit("MainHandle",newValue)
      }
    }
  }
</script>

父组件在子组件标签内进行接收(这里用了简写的函数形式)

Main.vue

<template>
  <h3>Main</h3>
  <p>搜索内容为:{{ mes }}</p>
  <Search @MainHandle="data=>this.mes = data"/>
</template>
<script>
import Search from './Search.vue';
  export default {
      data(){
        return{
          mes:""
        }
      } ,
      components:{
        Search
      }
  }
</script>

效果展示:

在这里插入图片描述

组件Props子传父 (函数方法)

原理:

父组件A,子组件B

A有一函数Afunction,Afunction接收到的参数会赋值给msg, 声明在组件B标签里 func = “Afunction”

B会通过props设定func为Function,调用func函数,给func赋值,从而实现B的值传递给A(子传父)

ComponentA.vue

<template>
  <h3>ComponentA</h3>
  <ComponentB :func="Afunction"/>
  <p>A: {{ msg }}</p>
</template>

<script>
import ComponentB from './ComponentB.vue';
export default {
  data() {
    return {
      msg:""
    }
  },
  components:{
    ComponentB
  },
  methods:{
    Afunction(data){
      this.msg = data;
    }
  }
}
</script>

ComponentB.vue

<template>
  <h3>ComponentB</h3>
  <p>{{ func("从B传递的数据") }}</p>
</template>

<script>
export default {
  data() {
    return {
      
    }
  },
  props:{
    func:Function
  }
}
</script>
透传Atrribute

看着挺烦的

感觉这个功能会导致代码阅读混乱(

不学了

插槽
插槽基础

插槽实际就是在组件与组件之间,传递html模版,父组件传递给子组件

比如父组件想把 两行标题html代码传递给子组件,从而实现在子组件内展示该代码

实现原理:

父组件

父组件中对子组件标签的引用,由单标签变为双标签

比如子组件Chile.vue

则父组件中对子组件的引用就改为:

<Child

</Child

在两个子组件标签内放置要传递的Html模板内容

子组件

子组件在的某一位置放置

两个slot标签内部就是父组件传递过来的HTML模板代码,从而在子组件内展示


示例:

App.vue:

<template>
  <SlotsBase>
    <div>
      <h3>插槽标题</h3>
      <p>{{ msg }}</p>
    </div>
  </SlotsBase>
  <SlotsTwo>

  </SlotsTwo>
</template>
<script>
import SlotsBase from './components/SlotsBase.vue';

export default {
    data() {
        return {
			msg:"插槽内容"
        };
    },
    components: {
      SlotsBase,
      SlotsTwo
    }
}
</script>
<style>

</style>

SlotBase.vue:

<template>
  <h3>插槽基础知识</h3>
  <slot></slot>
</template>
<script>
  export default {
    
  }
</script>

在这里插入图片描述

插槽作用域

子组件通过插槽可以访问父组件的数据作用域

(上图有显示)

如果父组件通过插槽传递了

//

{{msg}}

而父组件同时在data内定义了msg:“Message”

子组件是可以通过插槽,访问到父组件变量msg的值的

插槽默认内容

很简单,子组件的两个slot标签内的内容就是缺省显示在屏幕上的,如果插槽传递了模板,则不显示

默认值默认值默认值

具名插槽

插槽可以有名字

这种情况主要用于,父组件向子组件传递多个模板,子组件分别而不是一次性接收这些模板

先看一下下面这种情况

父组件:

<template>
  <SlotsTwo>
    <div>
      <h3>插槽标题</h3>
      <p>插槽内容</p>
    </div>
  </SlotsTwo>
</template>
<script>
import SlotsBase from './components/SlotsBase.vue';
import SlotsTwo from './components/SlotsTwo.vue';

export default {
    data() {
        return {
        };
    },
    components: {
      SlotsTwo
    }
}
</script>
<style>

</style>

子组件:

<template>
  <h3>Slot1</h3>
  <slot></slot>
  <h3>Slot2</h3>
  <slot></slot>
</template>
<script>
export default {
  
}
</script>
<style>

</style>

父组件只有一对子组件标签,子组件有两对插槽出口标签

那么最终父组件传递的模板会在子组件中出现两次,如下图

在这里插入图片描述

下面进入具名插槽:

具名插槽就是给父组件中不同的插槽起名字,子组件通过插槽的名字锁定,在不同的位置接收插槽

父组件将每一组Slot用标签圈起来,在template标签中给名字

(v-slot)可以简写成#

template v-slot:Slot1>

</template
<template #:Slot2

</template

子组件则通过

slot name=“Slot1”>

</slot

<slot name = “Slot2”

分别接收

父组件:

<template>
  <SlotsTwo>
    <template v-slot:Slot1>
      <h3>插槽标题</h3>
    </template>
    <template #Slot2>
      <p>插槽内容</p>
    </template>
  </SlotsTwo>
</template>
<script>
import SlotsTwo from './components/SlotsTwo.vue';

export default {
    data() {
        return {
        };
    },
    components: {
      SlotsTwo
    }
}
</script>
<style>

</style>

子组件

<template>
  <h3>Slot1</h3>
  <slot name="Slot1"></slot>

  <h3>Slot2</h3>
  <slot name="Slot2"></slot>
</template>
<script>
export default {
  
}
</script>
<style>

</style>

效果:

在这里插入图片描述

插槽数据子传父

原理就是,子组件在标签内增加一个属性存储子组件的一个变量

比如下面的例子

子组件的插槽出口设置了一个属性 sname

这个属性可供父组件调用

子组件:

<template>
  <h3>插槽基础知识</h3>
  <slot :sname="content"></slot>
</template>
<script>
  export default {
    data(){
      return{
        content:"艾克"
      }
    }
  }
</script>

父组件这里,因为子组件只有一个插槽,父组件设定了子组件插槽的名字: v-slot = “slotp”

此时slotp代表子组件的插槽出口,slotp.sname就是在调用子组件插槽出口的属性

父组件:

<template>
<SlotsBase v-slot="slotp">
    <h3>{{ slotp.sname }}</h3>
</SlotsBase>
</template>
<script>
import SlotsBase from './components/SlotsBase.vue';

export default {
    data() {
        return {
        };
    },
    components: {
    SlotsBase
}
}
</script>
插槽数据子传父(具名情况下)

具名就是多一个指定名字

原理就是,在基础具名的基础上,#slot1=“slotProps”

这个赋值实际上是给插槽接口赋了个别名,如果不这样赋直接用slot1.msg是无法调用的

父组件

<template>

<SlotsAttr>
  <template #slot1="slotProps">
    <h3>{{ slotProps.msg }}</h3>
  </template>
  <template #slot2="slotMsg">
    <h3>{{ slotMsg.content }}</h3>
  </template>
</SlotsAttr>

</template>
<script>
import SlotsAttr from './components/SlotsAttr.vue';
export default {
    data() {
        return {
        };
    },
    components: {
    SlotsAttr,
}
}
</script>

子组件

<template>
  <h3>子组件传递数据</h3>
  <slot name="slot1" :msg="childMessage"></slot>
  <h3>内容如下</h3>
  <slot name="slot2" :content="content"></slot>
</template>
<script>
export default {
  data(){
    return{
      childMessage:"这是标题",
      content:"这是内容"
    }
  }
}
</script>

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

组件的生命周期

组件有四个生命周期:

组件创建期组件挂载期(就是组件渲染在页面的时期)、组件更新期(比如通过按钮触发data的一个变量改变)、组件销毁期

  • 生命周期函数
  • 创建期: beforCreated created (created的时候页面内组件被创建出来)
  • 挂载期: beforeMount mounted (mounted的时候页面DOM才成型)
  • 更新期: beforeUpdate updated
  • 销毁期: beforeUnmount unmounted
<template>
  <h3>组件的生命周期</h3>
  <p>{{ msg }}</p>
  <button @click="this.msg='更新之后'">更新按钮</button>
</template>
<script>
/**
 * 	  生命周期函数
 *    创建期: beforCreated   created
 *    挂载期: beforeMount    mounted
 *    更新期: beforeUpdate   updated
 *    销毁期: beforeUnmount  unmounted
 */
export default {
  data(){
    return{
      msg:"更新之前"
    }
  },
  beforeCreate(){
    console.log("组件创建之前")
  },
  created(){
    console.log("组件创建之后")
  },
  beforeMount(){
    console.log("组件渲染之前")
  },
  mounted(){
    console.log("组件渲染之后")
  },
  beforeUpdate(){
    console.log("组件更新之前")
  },
  updated(){
    console.log("组件更新之后");
  },
  beforeUnmount(){
    console.log("组件销毁之前");
  },
  unmounted(){
    console.log("组件销毁之后")
  }
}
</script>
组件生命周期图示

上面代码的效果:

在这里插入图片描述

生命周期的应用

首先补充一个知识:

可以在标签内定义DOM结构引用

// <p ref = “name”

则可以用this.$refs.name来引用这个p标签结构

<template>
  <h3>组件生命周期函数应用</h3>
  <p ref="name">百战程序员</p>
</template>
<script>
  export default {
    beforeMount(){
      console.log(this.$refs.name)
    },
    mounted(){
      console.log(this.$refs.name)
    }
  }
</script>

这里的知识点是,在页面mounted(渲染)之前,DOM树未形成,所以 this.$refs.name无法获取节点

在这里插入图片描述

在一个前端项目中,一般是网页结构先成型,再填充数据,

所以一般网络请求都放置在mounted() 里面

动态组件

原理:
vue提供了一个用于展示组件的特殊标签<component

<component :is=“tabComponent”

tabComponent变量存储组件名称

可以通过按钮改变tabComponent变量的内容,从而实现组件改变

注意下面三元运算符的运用(

App.vue

<template>
  <component :is="tabComponent"></component>
  <button @click="changeHandle">组件转换</button>
</template>
<script>

import ComponentA from './components/ComponentA.vue';
import ComponentB from './components/ComponentB.vue';

export default {
  data(){
    return{
      tabComponent:"ComponentA"
    }
  },
  components:{
    ComponentA,ComponentB
  },
  methods:{
    changeHandle(){
      this.tabComponent = this.tabComponent =="ComponentA" ? "ComponentB" : "ComponentA" 
    }
  }
  
}
</script>

ComponentA.vue

<template>
  <h3>ComponentA</h3>
</template>
<script>
  export default {
  }
</script>

ComponentB.vue

<template>
  <h3>ComponentB</h3>
</template>
<script>
  export default {
  }
</script>
组件保持存活

蛮重要的

主打一个keep-alive标签

在上一小节中, component :is="tabComponent"标签执行后,组件会从DOM树卸载,也就是进入销毁期

<keep-alive

​ component :is=“tabComponent”>

</keep-alive

通过keep-alive标签的设定,组件被切换后不会销毁,而是会保持存活

举例

上一小节的代码中仅ComponentA改变一点:
增加一个 message变量和数据更新按钮

<template>
  <h3>ComponentA</h3>
  <p>{{ message }}</p>
  <button @click="updateHandle">数据更新</button>
</template>
<script>
  export default {
    data(){
      return{
        message:"老数据"
      }
    },
    methods:{
      updateHandle(){
      this.message = this.message =="老数据"  ? "新数据" : "老数据"
    }
    }
  }
</script>

效果展示:

下面为没有的情况:

初始状态 数据更新 组件转换

在这里插入图片描述

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

从初始状态点击数据更新,A组件的变量变为新数据

再点击组件转换,组件转为ComponentB

转换为B后,再点击组件转换,切回A

在这里插入图片描述

注意ComponentA还是老数据

下面为App.vue中对组件标签环绕了<keep-alive的情况:

按照上面一样的流程

组件转换为B后,再点击组件转换
在这里插入图片描述

注意到ComponentA的数据还是新数据,保持不变

总结:

不进行组件存活保持的话

组件卸载后,数据会被销毁

再使用组件切换,组件会被重新装载,此时组件的data会被重新初始化为原来的值

如果组件存活保持的话,反之

异步组件

正常情况下,一个页面内会将import的组件同时加载(即使这个组件当前不展示,比如切换之前的B组件),在页面上看是网页会一次性请求所有vue组件文件

在大型项目中,为了能够加快项目打开速度,会使用异步组件。也就是说暂时用不到的组件在页面初始化的时候不会加载,而是在组件会被用到的时候被加载

实现只需要在引入的时候改一下引入方式即可:

import { defineAsyncComponent } from 'vue';
const ComponentB = defineAsyncComponent(() =>
  import("./components/ComponentB.vue")
)

此时会发现,B组件的请求是在点击组件切换的时候才会发出的

在这里插入图片描述

依赖注入

依赖注入是为了解决,props参数传递在多级组件嵌套(比如三层父子嵌套)中数据需要沿着组件关系链多次传递的问题

语法就是

上层 provide一个变量

下层 inject 一个接收

相当于跳过传递链,设置一个中转站

下面举个例子

假设 组件链: App<-- A <-- B <-- C

ComponentA.vue

<template>
    <h3>ComponentA</h3>
    <ComponentB />
</template>
<script>
import ComponentB from './ComponentB.vue';
  export default {
    data(){
      return{
        msgA:"A的数据(动态)"
      }
    },
    provide(){
      return{
        message1:this.msgA,
        message2:"A的数据(静态)"
      }
    },
    components:{
    ComponentB
  },
  }
</script>

ComponentC.vue

<template>
  <h3>ComponentC</h3>
  <h3>下面C会接收A的数据</h3>
  <p>{{ message1 }}</p>
  <p>{{ message2 }}</p>
</template>
<script>
  export default {
    inject:["message1","message2"]
  }
</script>

效果展示:

在这里插入图片描述

在父组件A中 使用了函数式返回值:

provider(){

​ return{

​ }

}

这样可以保证静态数据和Data动态数据都可以注入到子组件中

另一种写法只能保证静态:

provider:{

}

路由

路由基础

路由是基于视图的

视图——view,可以看作不同url的页面,由组件组成

http://localhost:8080/

http://localhost:8080/about 这是两个不同的view

路由的作用就是实现view之间的跳转

实现路由需要以下几步:

  1. main.js 文件夹内引入router,vue实例使用router

    // main.js
    
    import { createApp } from 'vue'
    import App from './App.vue'
    import router from './router'
    
    // .use(router) 声明要使用路由功能
    createApp(App).use(router).mount('#app')
    
  2. 路由的前提是需要有视图view,src文件夹下创建views文件夹

    在views文件夹内新建视图文件 HomeView.vueAboutView.vue

  3. src文件夹下创建 router 文件夹 ,router文件夹内创建 index.js

    该文件是路由的配置文件,这里又要分几步:

    1. 引入 vue-router
    2. 引入路由页面
    3. 写路由配置信息(路径和对应页面)
    4. 创建路由
    5. 导出路由
    // index.js
    
    
    // 1.引入 vue-router
    import {createRouter,createWebHistory} from "vue-router"
    // 2.引入需要路由的页面
    import HomeView from '../views/HomeView.vue'
    import AboutView from '../views/AboutView.vue'
    
    
    // 配置信息中需要页面的相关配置
    // 3.路由页面配置
    const routes = [
      {
        path:'/',
        component:HomeView
      },
      {
        path:'/about',
        component:AboutView
      }
    ]
    
    // 4.创建路由
    const router = createRouter({
        /**
         * createWebHashHistory
         *    home:  http://localhost:5173/#/
         *    about: http://localhost:5173/#/about
         * 
         * createWebHistory
         *    home:  http://localhost:5173
         *    about: http://localhot:5173/about
         * 
         * 
         */
        history:createWebHistory(),
        routes
    })
    // 5.导出路由
    export default router带参路由
    
  4. App.vue内进行路由展示和路由链接

    <template>
        <!-- 路由跳转 -->
        <router-link to="/">首页</router-link> |
        <router-link to="/about">关于</router-link>
        <!-- 路由的显示入口 -->
        <router-view></router-view>
    </template>
    
    <script>
      export default {
    
      }
    </script>
    
带参路由

带参路由比较抽象,字面意思是跳转页面的时候会携带参数

拿例子来说

比如在新闻社分类里面有:百度新闻、网易新闻、头条新闻和搜狐新闻四种链接

点进每一个链接就会展示对应新闻社的介绍,也就是说

这四个链接点击任何一个,都会跳进同一个页面,但是可以通过携带他们对应的参数,从而向后端发送不同的请求来获取页面内容

假设我们新建一个页面 NewsDetailsView.vue

下面是带参路由的配置:

  1. index.js

    // 注意到在newsdetails路由后面多了一个key的设置,这个key就作为存储参数的变量,后面接收的时候也使用这个key来接收
    
    import { createRouter, createWebHistory } from 'vue-router'
    import HomeView from '../views/HomeView.vue'
    // 首页直接引入,速度快
    // 其他页面异步引入
    
    
    const router = createRouter({
      history: createWebHistory(import.meta.env.BASE_URL),
      routes: [
        {
          path: '/',
          name: 'home',
          component: HomeView
        },
        {
          path: '/about',
          name: 'about',
          // route level code-splitting
          // this generates a separate chunk (About.[hash].js) for this route
          // which is lazy-loaded when the route is visited.
          component: () => import('../views/AboutView.vue')
        },
        {
          path: '/news',
          name: 'news',
          // 这是异步加载方式,节省内存空间
          component: () => import('../views/NewsView.vue')
        },
        {
          // /:name是key
          path: '/newsdetails/:name',
          name: 'newsdetails',
          component: () => import('../views/NewsDetailsView.vue')
        }
      ]
    })
    
    export default router
    
    
  2. NewView.vue(引用了路由链接的页面)

    <-- 可以看到下面的to内的传参方式 -->
    <template>
      <h3>新闻</h3>
      <ul>
        <li><router-link to="/newsdetails/百度">百度新闻</router-link></li>
        <li><router-link to="/newsdetails/网易">网易新闻</router-link></li>
        <li><router-link to="/newsdetails/头条">头条新闻</router-link></li>
        <li><router-link to="/newsdetails/搜狐">搜狐新闻</router-link></li>
      </ul>
    </template>
    
    
  3. NewDetailsView.vue(路由接收参数页面)

    可以看到接收方法是 $route.params.name

    <template>
      <h3>新闻详情</h3>
      <p> {{ $route.params.name }}</p>
    </template>
    
嵌套路由

应用就是,二级导航菜单

在这里插入图片描述

简单的说就是index.js里面的一级路径属性里面加一个child

不想写了直接上链接

// idnex.js

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView
    },
    {
      path: '/about',
      name: 'about',
      //重定向,就是默认跳转
      redirect:"/about/us",
      // route level code-splitting
      // this generates a separate chunk (About.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import('../views/AboutView.vue'),
      children:[
        {
          //二级导航的路径不要加  '/'
          path:"info",
          component:() => import("../views/AboutSub/AboutInfo.vue")
        },
        {
          path:"us",
          component:() => import("../views/AboutSub/AboutUs.vue")
        }
      ]
    }
  ]
})

export default router

这里还有一个隐藏知识点

redirect 重定向 也就是默认会跳转到那里

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值