vue3从零开始——入门(一)

0、前置条件

1. 预备知识

JavaScript 概述

2. 编辑器设置

VSCode + Volar (and disable Vetur) + TypeScript Vue Plugin (Volar).

一、简介

1. 单文件组件 SFC

单文件组件 ( *.vue 文件,缩写:SFC):将一个组件的逻辑 (JavaScript),模板 (HTML) 和样式 (CSS) 封装在同一个文件里。

2. API 风格

2.1. 选项式 API (Options API)

核心思想:以“组件实例(this)”的概念为中心,与面向对象语言-基于类的心智模型更为一致。
优点:对初学者更为友好、适用于低复杂度的场景。

用包含多个选项的对象来描述组件的逻辑,例如 data、methods 和 mounted。
选项所定义的属性都会暴露在函数内部的 this 上,指向当前的组件实例。

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  mounted() {
    console.log(`The initial count is ${this.count}.`)
  }
}
</script>

<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>

2.2.组合式 API (Composition API)

核心思想:直接在函数作用域内定义响应式状态变量,并将从多个函数中得到的状态组合起来处理复杂问题。需要对Vue 的响应式系统有更深的理解。
优点:形式更自由,便于组织重用。
核心:声明式渲染、响应式状态

使用导入的 API 函数来描述组件逻辑。组合式 API 通常会与 <script setup> 搭配使用。

<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 用来修改状态、触发更新的函数
function increment() {
  count.value++
}
// 生命周期钩子
onMounted(() => {
  console.log(`The initial count is ${count.value}.`)
})
</script>

<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>

3. 学习路径

(1)互动教程:边动手边学
(2)阅读指南:深入了解所有细节
(3)查看示例:浏览核心功能、常见界面示例

二、快速上手

1. 创建一个项目

采用构建工具:Vue 官方的构建流程是基于 Vite 的,一个现代、轻量、极速的构建工具。

前提条件:Node版本>15

(1)创建项目
Vue官方的项目脚手架工具,安装并执行create-vue,npm init vue@latest
(2)安装依赖并启动开发服务器:

cd <your-project-name>
npm install
npm run dev

(3)发布到生产环境

npm run build

2. 模板语法

Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。

2.1. 文本插值{{}}

最基本的数据绑定形式:文本插值(双大括号)——将会将数据插值为纯文本,而不是 HTML。
<span>Message: {{ msg }}</span>双大括号标签会被替换为相应组件实例中msg属性的值。

2.2. 原始 HTMLv-html

想插入 HTML,你需要使用 v-html 指令:<span v-html="rawHtml"></span>

2.3. 属性绑定v-bind:属性名

(1)将元素的属性响应式地绑定到状态上,应该使用 v-bind:属性名 指令,简写为:属性名
<div :id="dynamicId"></div>
(2)动态绑定多个值:包含多个属性的 JavaScript 对象,通过不带参数的 v-bind,将它们绑定到单个元素上。

const objectOfAttrs = {
  id: 'container',
  class: 'wrapper'
}
<div v-bind="objectOfAttrs"></div>

2.4. 使用 JavaScript 表达式

在 Vue 模板内,支持完整的 JavaScript 表达式:双大括号中、 Vue 指令属性(v-xxx)中
每个绑定仅支持单一表达式:判断方法——可以合法地写在 return 后面。

2.5. 指令 v-xxx

指令由 v- 作为前缀,表明它们是一些由 Vue 提供的特殊 attribute。
扩展:内置指令

(1)指令条件
通过 v-if 和 v-for 指令条件性地或循环地渲染内容。v-for="item of list1"
指令的任务:在其表达式的值变化时响应式地更新 DOM。

(2)指令参数
某些指令会需要一个“参数”,在指令名v-xxx后通过一个冒号隔开做标识。
v-bind 指令来响应式地更新一个 HTML attribute:<a :href="url"> ... </a>
v-on 指令将监听 DOM 事件:<a @click="doSomething"> ... </a>

(3)动态参数
attributeName 会作为一个 JavaScript 表达式被动态执行,计算得到的值会被用作最终的参数。<a :[attributeName]="url"> ... </a>
函数绑定到动态的事件名称上<a @[eventName]="doSomething">当 eventName 的值是 "focus" 时,v-on:[eventName] 就等价于 v-on:focus。

(4)修饰符
是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。
<form @submit.prevent="onSubmit">...</form> .prevent 修饰符会告知 v-on 指令对触发的事件调用 event.preventDefault():

3. 响应式

3.0. 响应式状态

<script setup> 中的顶层的导入和变量声明可在同一组件的模板中直接使用。
要等待一个状态改变后的 DOM 更新完成,你可以使用 nextTick() 这个全局 API

function increment() {
  state.count++
  nextTick(() => {
    // 访问更新后的 DOM
  })
}

3.1. reactive()

reactive()函数创建一个响应式对象或数组。
限制:
仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型)。
不可以随意地“替换”一个响应式对象,将导致对初始引用的响应性连接丢失:
限制因为: JavaScript 没有可以作用于所有值类型的 “引用” 机制。

3.2. ref() 存储值

ref() 能创造一种对任意值的 “引用”,并能够在不丢失响应性的前提下传递这些引用。
应用:经常用于将逻辑提取到组合函数 中。
(1)“ref”:用来存储值的响应式数据源。
ref() 将传入参数的值包装为一个带 .value 属性的 ref 对象。
const msg = ref("Hello World!")
(2)通过 .value 属性,访问/修改一个 ref 的值。
msg1.value = msg1.value.split('').reverse().join('')
(3)不需要在模板中写 .value,因为在模板中 ref 会自动“解包”。
(4)通过编译时转换,我们可以让编译器帮我们省去使用 .value 的麻烦let count = $ref(0)

4. 计算属性computed()

4.1. 计算属性API

computed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。
computed()创建一个计算属性 ref,动态地根据其他响应式数据源来计算其 .value。

const filtersListData = computed(()=>{
  // 返回过滤后的 todo 项目
  return showAll.value ? ListData.value : ListData.value.filter((t)=> !t.done)
})

4.2. 计算属性缓存 vs 方法

(1)计算属性值会基于其响应式依赖被缓存。
一个计算属性仅会在其响应式依赖更新时才重新计算。
解释了为什么计算属性永远不会更新,因为 Date.now() 并不是一个响应式依赖:const now = computed(() => Date.now())
(2)方法调用总是会在重渲染发生时再次执行函数。

为什么需要缓存呢?有一个非常耗性能的计算属性 list,需要循环一个巨大的数组并做许多计算逻辑,只用执行一次即可。

4.3. 注意

(1)不要在计算函数中做异步请求或者更改 DOM!
一个计算属性的声明中描述的是如何根据其他值派生一个值。因此计算函数的职责应该仅为计算和返回该值。
(2)计算属性的返回值永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。

5. Class 与 Style 绑定 v-bind

Vue 专门为 class 和 style 的 v-bind 用法提供了特殊的功能增强。绑定支持普通字符串、对象、数组。

5.1. class 绑定

(1)对象中写多个字段

:class="{red: isRed}"
:class="{ active: isActive, 'text-danger': hasError }"

(2)直接绑定一个对象

const classObject = reactive({
  active: true,
  'text-danger': false
})
<div :class="classObject"></div>

(3)绑定一个返回对象的计算属性

const classObject = computed(() => ({
  active: isActive.value && !error.value,
  'text-danger': error.value && error.value.type === 'fatal'
}))
<div :class="classObject"></div>

(4)绑定一个数组

const activeClass = ref('active')
const errorClass = ref('text-danger')
<div :class="[activeClass, errorClass]"></div>

(5)有条件地渲染某个class——三元表达式
<div :class="[isActive ? activeClass : '', errorClass]"></div>
(6)在数组中嵌套对象:
<div :class="[{ active: isActive }, errorClass]"></div>
(7)组件有多个根元素,通过组件的 $attrs 属性来实现指定哪个根元素来接收这个 class

<!-- MyComponent 模板使用 $attrs 时 -->
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>
<!-- 父组件 -->
<MyComponent class="baz" />
<!-- 结果 -->
<p class="baz">Hi!</p>

5.2. style 绑定

:style="{ color: activeColor, fontSize: fontSize + 'px' }"
:style="styleObject"。直接绑定一个样式对象:const styleObject = reactive({color: 'red',fontSize: '13px'})
:style="[baseStyles, overridingStyles]"

6. 列表渲染v-for

6.1. v-for 指令 与 数组

基于一个数组来渲染一个列表。v-for 指令的值需要使用 item in items 形式。
支持使用可选的第二个参数表示当前项的位置索引。
也可以使用 of 作为分隔符来替代 in,这更接近 JavaScript 的迭代器语法。

const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
template
<li v-for="item in items">{{ item.message }}</li>
<li v-for="(item,index) in items">{{ item.message }}</li>
<li v-for="item of items">{{ item.message }}</li>

6.2. v-for 指令 与 对象

使用 v-for 来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.keys() 的返回值来决定。
提供第二个参数表示属性名 (key),第三个参数表示位置索引:

const myObject = reactive({
  title: 'How to do lists in Vue',
  author: 'Jane Doe',
  publishedAt: '2016-04-10'
})
template
<ul>
  <li v-for="value in myObject">{{ value }}</li>
  <li v-for="(value,key) in myObject">{{ value }}</li>
  <li v-for="(value,key,index) in myObject">{{ value }}</li>
</ul>

6.3. v-for与v-if

当它们同时存在于一个节点上时,v-if 比 v-for 的优先级更高。
这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名,需要在外新包装一层template。

<template v-for="todo in todos">
  <li v-for="todo in todos" v-if="!todo.isComplete"></li> <!-- 错误:todo 此时没有在该实例上定义 -->
  <li v-if="!todo.isComplete"> {{ todo.name }} </li>
</template>

6.4. 通过 key 管理状态

Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。
当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
为了跟踪每个节点的标识,从而重用和重新排序现有的元素,需要提供一个唯一的 key attribute:
<div v-for="item in items" :key="item.id"></div>

6.5. 数组变化侦测

侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。

(1)变更方法
会对调用它们的原数组进行变更。
包括:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
注意:reverse()和sort()将变更原始数组,计算函数中在调用这些方法之前创建一个原数组的副本:return [...numbers].reverse()

(2)不可变方法
返回一个新数组。
例如:filter()、concat()、slice()

(3)过滤或排序
显示数组经过过滤或排序后的内容,而不实际变更或重置原始数据。
1> 创建返回已过滤或已排序数组的计算属性。

const numbers = ref([1, 2, 3, 4, 5])
const evenNumbers = computed(() => {
  return numbers.value.filter((n) => n % 2 === 0)
})
<li v-for="n in evenNumbers">{{ n }}</li>

2> 计算属性不可行的情况下(多层嵌套的 v-for 循环)

const sets = ref([
  [1, 2, 3, 4, 5],
  [6, 7, 8, 9, 10]
])
function even(numbers) {
  return numbers.filter((number) => number % 2 === 0)
}
<ul v-for="numbers in sets">
  <li v-for="n in even(numbers)">{{ n }}</li>
</ul>

7. 事件处理v-on

7.1. 监听事件

使用 v-on 指令 (@) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。v-on:click="methodName" 或 @click="handler"
事件处理器的值:内联事件处理器、方法事件处理器。

7.2. 内联事件处理器

通常用于简单场景@click="msg1 += '!'"

7.3. 方法事件处理器

(1)绑定到一个方法/函数,用 @click (v-on:click)语法 。@click="reverseMsg"
(2)访问原生 DOM 事件,传入一个特殊的$event变量/使用内联箭头函数

<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>
<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
  Submit
</button>

7.4. 修饰符

(1)事件修饰符
用 . 表示的指令后缀。 包含 .stop .prevent .self .capture .once .passive。
(2)按键修饰符
监听键盘事件。使用 KeyboardEvent.key 暴露的按键名称作为修饰符:@keyup.enter @keyup.page-down

8. 表单输入绑定v-model

v-model 指令在状态和表单输入之间创建双向绑定,表单输入框的内容同步给JS变量。
可以用于各种不同类型的输入:“input、textarea、select” 元素。
注意:v-model 会忽略任何表单元素上初始的 value、checked 或 selected属性,始终将当前绑定的 JavaScript 状态视为数据的正确来源。

8.1. 表单输入框

(1)根据元素自动使用对应的 DOM 属性和事件组合:

  • 文本类型的input、textarea元素:绑定value属性并侦听 input 事件;
  • radio、checkbox类型的input元素:绑定checked属性并侦听 change 事件;
  • select元素:绑定value属性并侦听 change 事件:
<!-- 单行文本 -->
<input v-model="message" placeholder="edit me" />
<!-- 多行文本 -->
<textarea v-model="message" placeholder="add multiple lines"></textarea>
<!-- 单一复选框 ,绑定布尔类型值 -->
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
<!-- 多个复选框,绑定到同一个v-model数组/集合的值: -->
const checkedNames = ref([])
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<pre>{{checkedNames}}</pre>
<!-- 单选按钮 -->
<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>
<!-- 选择器 -->
<select v-model="selected" multiple>
  <option disabled value="">Please select one</option>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>

8.2. 值绑定

对于单选按钮、复选框、选择器选项,v-model 绑定的值通常是静态的字符串 (复选框是布尔值):

<!-- `picked` 在被选择时是字符串 "a" -->
<input type="radio" v-model="picked" value="a" />
<!-- `toggle` 只会为 true 或 false -->
<input type="checkbox" v-model="toggle" />
<!-- `selected` 在第一项被选中时为字符串 "abc" -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>

值绑定为动态数据,使用 v-bind。

<input type="radio" v-model="pick" :value="first" />
<input type="radio" v-model="pick" :value="second" />
<input type="checkbox" v-model="toggle" :true-value="dynamicTrueValue" :false-value="dynamicFalseValue" />
<!-- selected 会被设为该对象字面量值 { number: 123 } -->
<select v-model="selected">
  <option :value="{ number: 123 }">123</option>
</select>

9. 侦听器watch

情况:在状态变化时执行一些“副作用”:例如更改 DOM,或根据异步操作结果去修改另一处的状态。

9.1. watch()

懒执行:仅当数据源变化时,才会执行回调。
watch 函数在每次响应式状态发生变化时触发回调函数。
(1)watch() 可以直接侦听一个 ref,并且只要 count 的值改变就会触发回调。

<button type="submit" @click="count++">count+1</button><span>{{countText}}</span>
const count = ref(0)
const countText = ref('')
watch(count,(newV,oldV)=>{
    console.log(oldV,newV)
    countText.value ='监听数据更新'+ newV
})

(2)watch参数1:不同形式的“数据源”:ref (包括计算属性)、响应式对象、getter 函数、多个数据源组成的数组

// 响应式对象
const obj = reactive({ count: 0 })
watch(obj, ()=>{})
// 提供一个 getter 函数
watch(
  () => obj.count, // 不能直接侦听响应式对象的属性值,返回响应式对象的 getter 函数
  (count) => {
    console.log(`count is: ${count}`)
  }
)
// getter 函数
watch(
  () => x.value + y.value,
  (sum) => {
    console.log(`sum of x + y is: ${sum}`)
  }
)
// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY]) => {
  console.log(`x is ${newX} and y is ${newY}`)
})

9.2. 响应式执行watchEffect

watchEffect:在创建侦听器时,立即执行一遍回调,在相关状态更改时重新请求数据。
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。

const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=`
const branches = ['main', 'v2-compat']
const currentBranch = ref(branches[0])
const commits:Ref<any> = ref(null)
watchEffect(async()=>{
    const url = `${API_URL}${currentBranch.value}`
    console.log(await fetch(url))
    commits.value = await (await fetch(url)).json()
})

<template v-for="branch in branches">
    <input type="radio" :id="branch" :value="branch" name="branch" v-model="currentBranch">
    <label :for="branch">{{branch}}</label>
</template>
<ul>
  <li v-for="{html_url, sha, author, commit} in commits"> xxx </li>
</ul>

10. 模板引用ref

Vue 的声明性渲染模型抽象了大部分对 DOM 的直接操作,但我们仍然需要直接访问底层 DOM 元素,可以使用特殊的 ref 属性,获得对元素的直接引用。

10.1. 声明一个ref

声明一个 ref 来存放该元素的引用,必须和模板里的 ref 同名。
(1)当"script setup"执行时,DOM 元素还不存在。模板引用 ref 只能在组件挂载后访问。所以ref使用 null 值来初始化。 ref="pContent"
(2)通过 p.value 访问p标签,对其执行一些 DOM 操作(修改它的 textContent)。

const pContent = ref(null)
onMounted(() => {
    pContent.value.textContent='mounted'
})
<p ref="pContent">Hello</p>

10.2. v-for使用ref

ref 中包含的值是一个数组,将在元素被挂载后包含整个列表的所有元素。

const list = ref([/* ... */])
const itemRefs = ref([])
onMounted(() => console.log(itemRefs.value))
<li v-for="item in list" ref="itemRefs">{{ item }}</li>

10.3. ref绑定函数

:ref可以绑定为一个函数,会在每次组件更新时都被调用。
该函数会收到元素引用作为其第一个参数:<input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">

10.4. 子组件上的 ref

模板引用也可以被用在一个子组件上,父组件通过模板引用获取到了子组件的实例

const child = ref(null)
onMounted(() => {
   <!-- child.value 是 <Child /> 组件的实例 -->
})
<Child ref="child" />

11. 组件基础

命名及引用规则:注册使用驼峰,模板使用短横线。
(1)注册组件:命名-驼峰式、引用--短横线 子组件MyComponent 为注册名; 父组件引用<my-component>
(2)props参数传递:声明-驼峰式、引用传递-短横线 子组件const props = defineProps(['greetingMessage']); 父组件greeting-message="hello"
(3)emit方法触发自定义事件:声明-驼峰式、监听-短横线 子组件@click="$emit('someEvent'); 父组件@some-event="callback"

11.1. 使用组件

(1)要使用一个子组件,在父组件,通过script setup,导入的组件都在模板中直接可用。
(2)全局注册一个组件,使得它在当前应用中的任何组件上都可以使用,而不需要额外再导入。
组件注册
每一个组件都维护着自己的状态,是不同的 count。每当你使用一个组件,就创建了一个新的实例。

11.2. 父组件向子组件传参Props

(1)子组件可以通过 props 从父组件接受动态数据。defineProps() 是一个编译时宏,并不需要导入。
注意:需要在子组件的 props 列表上声明注册。

<!-- 父组件 -->
<input type="text" v-model="wishText"><button @click="addWish">添加愿望</button>
<TodoItem v-for="item in todoListData" :key="item.id" :todoItem="item" @click="removeCurList(item)"></TodoItem>
<!-- 子组件 -->
const props = defineProps({
    todoItem:Object
})
<li>{{ todoItem!.text }}</li>

11.3. 子组件向父组件触发事件Emits

defineEmits声明了一个组件可能触发的所有事件,还可以对事件的参数进行验证。
子组件emit()的参数1是事件的名称。其他所有参数都将传递给事件监听器。父组件使用@监听子组件触发的事件。

  • 声明触发的事件 const emit = defineEmits(['response'])。
  • 带参数触发emit('response', 'hello from child')。
<!-- 父组件 -->
<TodoItem v-for="item in filtersListData" :key="item.id" :listItem="item" :canHandle="true" @deleteItem="deleteCurItem"></TodoItem>
<!-- 子组件 -->
<!-- a 声明触发的事件 -->
const emit = defineEmits(['deleteItem'])
<!-- b 带参数触发 -->
function deleteFun(text:string){
    emit('deleteItem',text)
}
<button @click="deleteFun(listItem!.text)">×</button>

11.4. 插槽slot

(1)在子组件中,可以使用 slot 元素作为插槽出口 (slot outlet) 渲染父组件中的插槽内容 (slot content)。
插槽内容可以是任意合法的模板内容:文本、多个元素、组件。
没有提供 name 的slot出口会隐式地命名为“default”。
具名插槽(带 name 的插槽):将多个插槽内容传入到各自目标插槽的出口,使用#name

<!-- 父组件 -->
<TodoItem v-for="item in todoListData" :key="item.id" :todoItem="item">
    描述信息主体
    <template #header>
        描述信息-头部
    </template>
    <template #footer>
        描述信息-尾部
    </template>
</TodoItem>
<!-- 子组件 -->
<template>
    <li >{{ todoItem!.text }}</li>
    <slot name="header"></slot>
    <slot>默认信息========</slot>
    <slot name="footer"></slot>
</template>

(2)作用域插槽

渲染作用域:插槽内容本身是在父组件模板中定义的,插槽内容可以访问到父组件的数据作用域,无法访问子组件的数据。

作用域插槽:能够接受参数的插槽,接受的参数只在该插槽作用域内有效。

场景:插槽的内容可能想要同时使用父组件域内和子组件域内的数据,需要让子组件在渲染时将一部分数据提供给插槽。
解决方法:像对组件传递 props 那样,向一个插槽的出口上传递属性。

2.1)默认插槽
子组件传入插槽的 props 作为 v-slot 指令的值,在 v-slot 中可以使用解构。
通过子组件标签上的 v-slot 指令,直接接收到了一个插槽 props 对象:

<!-- 父组件 -->
<MyComponent v-slot="slotProps">
  {{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
<!-- 子组件 -->
<div>
  <slot :text="greetingMessage" :count="1"></slot>
</div>

2.2)具名作用域插槽
插槽 props 可以作为 v-slot 指令的值被访问到:v-slot:name="slotProps"缩写#name="nameProps"

<!-- 父组件 -->
<MyComponent>
  <template #header="headerProps">
    {{ headerProps }}   <!-- 结果:{ message: 'hello' } -->
  </template>
</MyComponent>
<!-- 子组件 -->
<slot name="header" message="hello"></slot>

(3)作用域插槽场景
逻辑+渲染:封装可重用的逻辑 (数据获取、分页等) 、组合视图界面,只将部分视图输出通过作用域插槽交给了消费者组件来管理。
逻辑(无渲染组件):只包括了逻辑而不需要自己渲染内容,视图输出通过作用域插槽全权交给了消费者组件。

说明:大部分无渲染组件实现的功能都可以通过组合式 API 以另一种更高效的方式实现,并且还不会带来额外组件嵌套的开销。

推荐:纯逻辑复用时:使用组合式函数,同时复用逻辑和视图布局时:使用无渲染组件。

5. 动态组件

Tab 界面:<component :is="tabs[currentTab]"></component>
场景:需要在两个组件间来回切换,通过 Vue 的component元素和特殊的 is 属性实现的。
被传给:is的值可以是:被注册的组件名、导入的组件对象。

注意:多个组件间作切换时,被切换掉的组件会被卸载。通过KeepAlive组件强制被切换掉的组件仍然保持“存活”的状态。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容