vue学习笔记

58 阅读12分钟

文本插值

{{ }} 即双大括号。

<template>
  <h3>模板语法</h3>
  <p>{{ msg }}</p>
</template><script>
export default {
  data(){
    return{
      msg: "神奇的语法"
    }
  }
}
</script><style>
</style>

原始html

输出原始html,需要使用v-html语法。

<template>
  <p>纯文本:{{ rawHtml }}</p>
  <p>属性:<span v-html="rawHtml"></span></p>
</template>

<script>
export default {
  data() {
    return {
      rawHtml: "<a href='https://itbaizhan.com'>百战程序员</a>"
    }
  }
}
</script>

属性绑定

class、style等属性值的绑定使用v-bing:或:。 也就是与script中的data中的变量的绑定。

<template>
  <div v-bind:id="dynamicId" v-bind:class="dynamicClass">测试</div>
</template>

<script>
export default {
  data() {
    return {
      dynamicClass: "appclass",
      dynamicId: "appid"
    }
  }
}
</script>

条件渲染

v-if v-else v-else-if v-show 表达式返回true值时渲染,否则不渲染。

<template>
  <h3>条件渲染</h3>
  <div v-if="flag">你能看见我么</div>
  <div v-else>那你还是看看我吧</div>
  <div v-if="type === 'A'">A</div>
  <div v-else-if="type === 'B'">B</div>
  <div v-else-if="type === 'C'">C</div>
  <div v-else>Not A/B/C</div>
  <div v-show="flag">你能看见我么</div>
</template>

<script>
export default {
  data() {
    return {
      flag: true,
      type: "B"
    }
  }
}
</script>

image.png

列表渲染

使用v-for指令。
我们可以用v-for指令基于一个数组来渲染一个列表。 v-for 指令需要使用item in items形式的特殊语法,其中items是源数据数组,而item则是被迭代的数组元素的别名。
代码中的index可以理解为数组的下标
为什么要加key属性(不加key的情况下,列表元素变动顺序时,会进行重新渲染每一个元素,而不是只进行排序操作。):
当 Vue 正在更新使用v-for渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一的keyattribute。

<template>
  <div class="hello">
    <h3>列表渲染</h3>
    <ul>
      <li v-for="(item,index) in newsList" :key="item.id">
        {{ item.title }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      newsList:[
        {
          id:1001,
          title:"今日新闻1"
        },
        {
          id:1002,
          title:"今日新闻2"
        },
        {
          id:1003,
          title:"今日新闻3"
        },
        {
          id:1004,
          title:"今日新闻4"
        }
      ]
    }
  }
}
</script>  

事件处理

使用v-on或简写@指令。
下面代码点击后触发addCount()方法。

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

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    addCount() {
      console.log("点击了");
    }
  }
}
</script>

获取event对象,就是JS的event对象:

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

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    // event对象
    addCount(e) {
    // vue中的event对象,就是JS的event对象
      console.log(e.target.innerHTML = "Add" + this.count);
      this.count++;
    }
  }
}
</script>  

传递参数:
传递参数过程中如果需要event对象,记得使用$event

<template>
      <h3>事件传参</h3>
      <p @click="getNameHandler(item,$event)" v-for="(item, index) of names" :key="index">{{ item }}</p>
</template>

<script>
    export default {
      data() {
        return {
          names: ["iwen", "ime", "frank"]
        }
      },
      methods: {
        getNameHandler(name, e) {
          console.log(name);
          console.log(e);
        }
      }
    }
</script>

事件修饰符

  • .stop
  • .prevent
  • .self
  • .capture
  • .once
  • .passive
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>

<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>

<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>

<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>

<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>  

数组变化侦测

data中的数据为数组时,如果使用如下方法变更数组数据,会直接更新网页中显示内容。

image.png
使用下面这些方法会返回一个新的数组,而不会直接更新网页中的显示内容。

image.png

计算属性

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。推荐使用计算属性来描述依赖响应式状态的复杂逻辑。

//模板语法难以维护
<template>
  <h3>{{ itbaizhan.name }}</h3>
  <p>{{ itbaizhan.content.length > 0 ? 'Yes' : 'No' }}</p>
</template>

<script>
export default {
  data() {
    return {
      itbaizhan: {
        name: "百战程序员",
        content: ["前端", "Java", "python"]
      }
    }
  }
}
</script>
//官方建议把复杂逻辑放入computed()处理。
<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绑定

class绑定不只可以使用字符串,还可以使用组或对象

//绑定对象和数组
<template>
  <p :class="{ 'active': isActive, 'text-danger': hasError }">Class样式绑定1</p>    //对象
  <p :class="classObject">Class样式绑定2</p>                      //对象
  <p :class="[arrActive, arrHasError]">Class样式绑定3</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>
//数组嵌套对象。数组和对象嵌套过程中,只能是数组嵌套对象,不能对象嵌套数组。
<template>
  <div :class="[{ 'active': isActive }, errorClass]"></div>    //数组嵌套对象
</template>

<script>
export default {
  data() {
    return {
      isActive: true,
      errorClass: "text-danger"
    }
  }
}
</script>

sytle绑定

sytle绑定不只可以使用字符串,还可以使用组或对象。同class绑定。

侦听器

语法watch,作用是监听页面的数据变化,只针对响应式的数据,也就是在data中所声明的,通过{{ }}模板语法绑定的数据。

<template>
  <h3>侦听器</h3>
  <p>{{ message }}</p>
  <button @click="updateHandle">修改数据</button>
</template>

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

表单输入绑定

语法v-model。下面代码,将输入框的内容与message绑定。

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

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

v-model修饰符
.lazy
默认情况下,v-model 会在每次 input 事件后更新数据 (IME 拼字阶段的状态例外)。你可以添加 lazy 修饰符来改为在每次 change 事件后更新数据。
.number
如果你想让用户输入自动转换为数字,你可以在 v-model 后添加 .number 修饰符来管理输入。
.trim
如果你想要默认自动去除用户输入内容中两端的空格,你可以在 v-model 后添加 .trim 修饰符。

模板引用(直接访问原始的DOM元素)

虽然 Vue 的声明性渲染模型为你抽象了大部分对 DOM 的直接操作,但在某些情况下,我们仍然需要直接访问底层 DOM 元素。要实现这一点,我们可以使用特殊的refattribute。

挂载结束后引用都会被暴露在this.$refs之上。

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

<script>
export default {
  data() {
    return {
      content: "内容"
    }
  },
  methods: {
    getElementHandle() {
      this.$refs.container.innerHTML = "哈哈哈";           //引用DOM元素div,使用原生js属性innerHTML操作。
    }
  }
}
</script>

没有特别的需要,尽量不要操作DOM。因为很麻烦。

组件组成

一个.vue文件,就是一个组件。包括:

<template>
  包括html
</template>
<script>
  包括逻辑,也就是js。
</script>
<style>
  包括样式,也就是css。
</style>

在一个组件中引用另一个组件的步骤(三步走):

<template>
//第三步:显示组件
  <MyComponent />
</template>

<script>
// 第一步:引入组件
import MyComponent from "./components/MyComponent.vue"

export default {
// 第二步:注入组件
  components: {
    MyComponent
  }
}
</script>

<style>
</style>

样式增加scoped,会使样式只在当前组件中生效,否则会全局生效。

<style scoped> 
</style>

组件全局注册

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')

在最外层注册后,在所有组件中都可以直接引用Header.vue组件: <header />
全局注册虽然很方便,但有以下几个问题:
1.全局注册,但并没有被使用的组件无法在生产打包时被自动移除(也叫“tree-shaking”)。如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中。
2.全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性。

组件数据传递(父传子)-props

props传递数据,只能父传子,不能子传父。

//父组件
<template>
  <h3>Parent</h3>
  <Child title="Parent数据1" demo = "Parent数据2" :demo2 = "message" />        //传给子组件
</template>

<script>
import Child from "./Child.vue"
export default {
  data() {
    return {
      message = "Parent数据3"
    }
  },
  components: {
    Child
  }
}
</script>
//子组件
<template>
  <h3>Child</h3>
  <p>{{ title }}</p>  //使用
  <p>{{ demo }}</p>   //使用
  <p>{{ demo1 }}</p>   //使用
</template>

<script>
export default {
  data() {
    return {
    }
  },
  props: ["title","demo","demo2"]    //子组件接收
}
</script>

组件传递多种数据类型

通过 props 传递数据,不仅可以传递字符串类型的数据,还可以是其他类型,例如:数字、对象、数组等。但实际上任何类型的值都可以作为 props 的值被传递。

<template>
  <h3>Parent</h3>
  <Child :title="message" :age="age" :names="names" :userInfo="userInfo" />
</template>

<script>
import Child from "./Child.vue"

export default {
  data() {
    return {
      message: "Parent数据!",
      age: 20,                          //数字
      names: ["iwen", "ime", "frank"],      //数组
      userInfo: {                       //对象
        name: "iwen",
        age: 20                    
      }
    }
  },
  components: {
    Child
  }
}
</script>
<template>
  <h3>Child</h3>
  <p>{{ title }}</p>
  <p>{{ age }}</p>               //数字
  <ul>
    <li v-for="(item, index) of names" :key="index">{{ item }}</li>     //数组
  </ul>
  <p>{{ userInfo.name }}</p>    //对象
  <p>{{ userInfo.age }}</p>
</template>

<script>
export default {
  data() {
    return {
    }
  },
  props: ["title", "age", "names", "userInfo"]     //接收
}
</script>

组件传递props效验

<template>
  <h3>ComponentA</h3>
  <ComponentB :title="title" />    //传递过去
</template>

<script>
import ComponentB from "./ComponentB.vue"

export default {
  data() {
    return {
      title: "标题",  //这里必须是字符串
      name:["lbsjs","wy","kaixin"]  //数组
    }
  },
  components: {
    ComponentB
  }
}
</script>
<template>
  <h3>ComponentB</h3>
  <p>{{ title }}</p>
  <p>{{ age }}</p>    //父组件没有传过来数据,会显示0,因为给出默认值。
  <p v-for="(item, index) of names" :key="index">{{ item }}</p>  
</template>

<script>
export default {
  data() {
    return {
    }
  },
  props: {
    title: {
      type: String  //指定传过来数据的类型
      required:true   //指定title为必填选项,如果父组件不传送数据过来,浏览器报警告。
    }
    age:{
      type:Number
      default:0    //可以指定默认值      
    }
    //数字和字符串可以直接default给出默认值,但数组和对象,必须通过工厂函数返回默认值。
    name:{
      type:Array
      default(){
        return ["空"]
      }
}
</script>

注意:props是只读的。

组件事件(自定义事件,子传父) -$emit

<template>
  <h3>组件事件</h3>
  <Child @someEvent="getHandle" />        
</template>

<script>
import Child from "./Child.vue"

export default {
  data() {
    return {
      message: ""
    }
  },
  components: {
    Child
  },
  methods: {
    getHandle(data) {
      console.log("触发了", data);                      //接收到子组件的数据。
    }
  }
}
</script>
<template>
  <h3>Child</h3>
  <button @click="clickEventHandle">传递数据</button>    //点击按钮后,触发父组件的getHandle函数。
</template>

<script>
export default {
  methods: {
    clickEventHandle() {
      // 自定义事件
      this.$emit("someEvent","发给父元素")             //getHandle函数会接收到第二个参数的数据。
    }
  }
}
</script>

组件事件 配合'v-model'使用

//SearchComponent.vue
<template>
 搜索:<input type="text" v-model="search">
</template>

<script>
export default {
 data() {
   return {
     search: ""
   }
 },
 // 侦听器
 watch: {
   search(newValue, oldValue) {
     this.$emit("searchEvent", newValue)              //将newValue值传递给父组件
   }
 }
}
</script>
<template>
  <h3>Main</h3>
  <p>搜索内容为:{{ search }}</p>
  <SearchComponent @searchEvent="getSearch"/>
</template>

<script>
import SearchComponent from "./SearchComponent.vue"

export default {
  data() {
    return {
      search: ""
    }
  },
  components: {
    SearchComponent
  },
  methods: {
    getSearch(data) {
      this.search = data;
    }
  }
}
</script> 

props实现子传父

//父组件
<template>
  <h3>ComponentA</h3>
  <ComponentB title="标题" :onEvent="dataFn" />         //将dataFn函数传给子组件
  <p>{{ message }}</p>                                 //显示子组件传来的数据
</template>

<script>
import ComponentB from "./ComponentB.vue"

export default {
  data() {
    return {
    }
  },
  components: {
    ComponentB,
    message
  },
  methods: {
    dataFn(data) {
      console.log(data);
      this.message = data
    }
  }
}
</script>
//子组件
<template>
  <h3>ComponentB</h3>
  <p>{{ title }}</p>
  <p>{{ onEvent('传递数据') }}</p>           //使用父组件函数,也就是把子组件数据传给父组件。
</template>

<script>
export default {
  data() {
    return {
    }
  },
  props: {
    title: String,
    onEvent: Function
  }
}
</script>

透传Attributes

本节日常使用较少,只进行了解。
当一个组件以单个元素为根作渲染时,透传的 attribute 会自动被添加到根元素上。

//父组件
<template>
  <AttrComponents class="attr-container"/>     //传给子组件attr-container属性传给子组件
</template>

<script>
import AttrComponents from "./components/AttrComponents.vue"

export default {
  components: {
    ComponentEvent,
    Main,
    ComponentA,
    AttrComponents
  }
}
</script>
//子组件,AttrComponents.vue组件
<template>
  //必须是唯一根元素,不然不生效
     <h3>透传属性</h3>
  // <h3>不生效</h3>  有此标签全部不生效
</template>

<script>
export default {
  inheritAttrs: false    //禁止继承,上面效果不生效。
}
</script>

<style>
.attr-container {
  color: red;
}
</style>  

插槽Slots

父组件传递html内容(模板内容)给子组件:

//父组件
<template>
 <SlotsBase>                   //需要传递给子组件的内容
   <div>
     <h3>插槽标题</h3>
     <p>插槽内容</p>
   </div>
 </SlotsBase>
</template>

<script>
import SlotsBase from "./components/SlotsBase.vue"

export default {
 components: {
   SlotsBase
 }
}
</script>
//子组件 SlotsBase.vue
<template>
  <h3>插槽基础知识</h3>
  <slot></slot>               //在子组件中显示传递过来的内容
</template>

官方描述:

image.png

传递动态数据和插槽默认值:

//父组件
    <template>    
     <SlotsTow>
       <h3>{{ message }}</h3>            //动态数据
     </SlotsTow>
    </template>

    <script>
    import SlotsTow from "./components/SlotsTow.vue"

    export default {
      data() {
        return {
          message: "插槽内容续集"        //绑定数据需要是父组件中
        }
      },
      components: {    
        SlotsTow
      }
    }
    </script>

//子组件SlotsTow.vue
<template>
  <h3>Slots续集</h3>
  <slot>插槽默认值</slot>         //在<slot>标签中写入值,如何父组件没有传入插槽内容,可做为默认值显示
</template>

<script>
export default {
  data() {
    return {
   }
  }
}
</script>

具名插槽,也就是按需要部分显示父组件中html内容(模板内容):

//父组件ComponentA.vue
<template>
  <h3>ComponentA</h3>
  <ComponentB>
    <template v-slot:header>         //具名插槽 v-slot可简写为#
      <h3>标题</h3>
    </template>
    <template v-slot:main>           //具名插槽 v-slot可简写为#
      <p>内容</p>
    </template>
  </ComponentB>
</template>

<script>
import ComponentB from "./ComponentB.vue"

export default {
  data() {
    return {
      message: "message在父级"
    }
  },
  components: {    
    ComponentB
  }
}
</script>
//子组件ComponentB.vue
<template>
  <h3>ComponentB</h3>
  <slot name="header"></slot>       //使用
  <hr>
  <slot name="main"></slot>         //使用
</template>

image.png

插槽中的数据传递(有时候也需要将子组件中定义的数据显示在插槽中):

    //父组件
    <template>
      <h3>ComponentA</h3>
      <ComponentB v-slot="slotProps">                 //父组件会用固定名字slotProps对象接收子组件的数据
        <h3>{{ message }}-{{ slotProps.text }}</h3>       //slotProps.text
      </ComponentB>
    </template>

    <script>
    import ComponentB from "./ComponentB.vue"

    export default {
      data() {
        return {
          message: "message在父级"
        }
      },
      components: {
        ComponentB
      }
    }
    </script>
  //子组件
    <template>
      <h3>ComponentB</h3>
      <slot :text="message"></slot>      //将子组件中定义的数据message传给父组件
    </template>

    <script>
    export default {
      data() {
        return {
          message: "ComponentB中的数据"
        }
      }
    }
    </script>

如果是具名插槽数据传递这么操作:

 //父组件
    <template>
      <h3>ComponentA</h3>
      <ComponentB>   
        <template #header = "slotProps"           //这样操作
          <h3>{{ message }}-{{ slotProps.text }}</h3>       
        <template>
        <template #main = "slotProps">            //这样操作
          <p>{{ slotPros.name }}</p>
        <template>
      </ComponentB>
    </template>

    <script>
    import ComponentB from "./ComponentB.vue"

    export default {
      data() {
        return {
          message: "message在父级"          
        }
      },
      components: {
        ComponentB
      }
    }
    </script>
//子组件
    <template>
      <h3>ComponentB</h3>
      <slot name="header" :msg="childMessage"></slot>  
      <slot name="main"  :name = "nameMessage"></slot>
    </template>

    <script>
    export default {
      data() {
        return {
          message: "ComponentB中的数据"
          nameMessage:"纪舒,你好"
        }
      }
    }
    </script>

组件的生命周期

image.png

/**
 * 生命周期函数
 * 创建期:beforeCreate  created
 * 挂载期:beforeMount  mounted
 * 更新期:beforeUpdate  updated
 * 销毁期:beforeUnmount  unmounted
 */  
//app.vue
<template>
    <h3>生命周期函数</h3>
    <p>{{ message }}</p>
    <button @click="message='数据'">点击</button>
</template>

<script>
export default {
    data(){
        return{
            message:""
        }
    },
    beforeCreate(){
        console.log("beforeCreate:组件创建之前");
    },
    created(){
        console.log("created:组件创建完成");
    },
    beforeMount(){
        console.log("beforeMount:渲染之前");
    },
    mounted(){
        console.log("mounted:组件渲染完成");
        // 把网络请求放到这里
    },
    beforeUpdate(){
        console.log("beforeUpdate:组件更新之前");
    },
    updated(){
        console.log("updated:组件更新之后");
    },
    beforeUnmount(){
        console.log("beforeUnmount:组件卸载之前");
        // 卸载之前,把消耗性能的处理都干掉
        // 定时器
    },
    unmounted(){
        console.log("unmounted:组件卸载之后");
    }
}
</script>
```

生命周期应用

通过ref获取元素DOM结构

<template>
  <h3>组件生命周期函数应用</h3>
  <p ref="name">百战程序员</p>      //ref直接引用DOM元素
</template>

<script>
export default {
  beforeMount() {
    console.log(this.$refs.name); // undefined
  },
  mounted() {
    console.log(this.$refs.name); //只有在挂载成功后才能访问到DOM的p标签。
  }
}
</script>

模拟网络请求渲染数据

<template>
  <h3>组件生命周期函数应用</h3>
  <ul>
      <li v-for="(item, index) of banner" :key="index">
        <h3>{{ item.title }}</h3>
        <p>{{ item.content }}</p>
      </li>
  </ul>
</template>

<script>
export default {
  beforeMount() {
    
  },
  mounted() {                
  //模拟网络请求  
  //网络请求应该放在mounted里面,mounted在组件被渲染后调用。也可以放在created里面,但像京东、淘宝等网站都是先显示框架,再显示渲染数据,明显框架先显示出来更重要,建议放在mounted里面。created是data里面数据初始化后调用,beforeCreate不行,因为banner变量还没有初始化。
    this.banner = [
  {
    "title": "我在爱尔兰",
    "content": "爱尔兰(爱尔兰语:Poblacht na hÉireann;英语:Republic of Ireland),是..."
  },
  {
    "title": "一个人的东京",
    "content": "东京(Tokyo)是日本的首都,是亚洲第一大城市,世界第二大城市。全球最大的经济..."
  },
  {
    "title": "普罗旺斯的梦",
    "content": "普罗旺斯(Provence)位于法国东南部,毗邻地中海和意大利,从地中海沿岸延伸到内..."
  },
  {
    "title": "相约夏威夷之夏",
    "content": "夏威夷州位于北太平洋中,距离美国本土3,700公里,总面积16,633平方公里,属于大..."
  }
 ]
  }
}
</script>

动态组件

点击app.vue中的按钮后,让页面切换显示ComponentA和ComponentB

    //ComponentA.vue
    <template>
      <h3>ComponentA</h3>
    </template>
    //ComponentB.vue
    <template>
      <h3>ComponentB</h3>
    </template>
    //app.vue
    <template>
      <component :is="tabComponent"></component>         //需要vue自带的标签component 使用:is = 进行组件显示
      <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>

组件保持存活

当使用component :is= 使多个组件切换时,被切换掉的组件会被卸载,也就是进入unmounted()。可以通过<keep-alive>组件强制被切换的组件仍然保持“存活”状态。

<template>
  <keep-alive>                     //使用此标签包裹住component,可以使组件不被卸载。
    <component :is="tabComponent"></component>
  </keep-alive>
  <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>
<template>
  <h3>ComponentA</h3>
  <p>{{ message }}</p>
  <button @click="updateHandle">修改数据</button>
</template>

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

<script>
export default {
  data() {
    return {
      message: "老数据"
    }
  },
  beforeUnmount() {
    console.log("组件卸载之前");
  },
  unmounted() {                                     //组件被切换掉之后会调用这个方法
    console.log("组件卸载之后");
  },
  methods: {
    updateHandle() {
      this.message = "新数据";
    }
  }
}
</script>  

异步组件

在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue提供了defineAsyncComponent方法来实现此功能。
也就是说同步组件一般一个网站打开后,会全将全部组件从服务器请求后全部进行加载,而把一个组件定义为异步组件后,只会按需要加载,需要的时候才从服务器请求后再加载,提高运行速度。

<template>
  <h3>组件切换</h3>
  <keep-alive>
    <component :is="currentTab"></component>
  </keep-alive>
  <button @click="changeComponentHandle">切换</button>
</template>

<script>
import { defineAsyncComponent } from 'vue'                 //引入
import ComponentA from "./components/ComponentA.vue"       
const AsyncComponentB = defineAsyncComponent(() =>        //定义为异步组件
  import('./components/ComponentB.vue')
)

export default {
  components: {
    ComponentA,
    AsyncComponentB
  },
  data() {
    return {
      currentTab: "ComponentA",
    }
  },
methods: {
  changeComponentHandle() {
    this.currentTab = this.currentTab === "ComponentA" ? "AsyncComponentB" : "ComponentA"
  }
}
<script/>

依赖注入

通常情况下,当我们需要从父组件向子组件传递数据时,会使用 props。想象一下这样的结构:有一些多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦。

image.png
provideinject可以帮助我们解决这一问题。一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。

image.png

//app.vue
<template>
  <h3>祖宗</h3>
  <Parent/>
</template>

<script>
import Parent from "./components/Parent.vue"

export default {
  data() {
    return {
      message: "爷爷的财产!!!"
    }
  },
  // provide: {
  //   message: "爷爷的财产"             //传递字串符
  // },
  provide() {                           //传递变量数据
    return {
      message: this.message
    }
  },
  components: {
    Parent
  }
}
</script>
//Parent.vue
<template>
  <h3>Parent</h3>
  <Child />
</template>

<script>
import Child from "./Child.vue"

export default {
  components: {
    Child
  }
}
</script>
//Child.vue
<template>
  <h3>Child</h3>
  <p>{{ message }}</p>
  <p>{{ fullMessage }}</p>
  <p>{{ golabData }}</p>
</template>

<script>
export default {
  inject: ["message","golabData"],                //inject接收数据
  data() {
    return {
      fullMessage: this.message      //也可传递给data变量使用
    }
  }
}
</script>
//main.js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
app.provide("goLabData", "我是全局数据")             //全局传递goLabData变量,全局可接收

app.mount('#app')

注意:provideinject只能由上到下的传递,不能反向传递。

vue应用

这段讲的是整个vue项目各个文件、文件夹都是什么。
main.js 项目入口脚本
index.html 是主页。
详细看一下原视频。

vue引放路由配置

在Vue中,我们可以通过vue-router路由管理页面之间的关系

Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。

vue状态管理(Vuex)

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单来说,状态管理可以理解成为了更方便的管理组件之间的数据交互,提供了一个集中式的管理方案,任何组件都可以按照指定的方式进行读取和改变数据。 讲的简单一些就是组件之间共享变量,一个变量变动作用于所有控件。