【Vue】Vue3吐血整理(待更新...)

创建

创建应用

const app = Vue.createApp({ /* 选项 */ })

链式

Vue.createApp({})
  .component('SearchInput', SearchInputComponent)
  .directive('focus', FocusDirective)
  .use(LocalePlugin)

加载应用

const vm = app.mount('#app')

生命周期

实例的生命周期

动态绑定js表达式

绑定js表达式,根据表达式的值动态绑定

<a v-on:[js表达式]=""></a>
<a @[event]="doSomething"> ... </a>

约束

如空格和引号,放在 HTML attribute 名里是无效的

<a v-bind:['foo' + bar]="value"> ... </a>

变通的办法是使用没有空格或引号的表达式,或用computed替代这种复杂表达式

在 DOM 中使用模板时,避免使用大写字符来命名键名(全部强制转为小写,相当于someattr的property

<a v-bind:[someAttr]="value"> ... </a>

data

命名

内置 API:$ 前缀

内部 property: _ 前缀

避免使用这两个字符开头的的顶级 data property 名称。

method

Vue 自动为 methods 绑定 this,以便于它始终指向组件实例
在定义 methods 时应避免使用箭头函数,因为这会阻止 Vue 绑定恰当的 this 指向

防抖和节流

防抖:对于短时间内连续触发的事件,使某个时间期限内,事件处理函数只执行一次。(读条)
节流:函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活(CD)

app.component('save-button', {
  created() {
    // 用 Lodash 的防抖函数
    this.debouncedClick = _.debounce(this.click, 500)
  },
  unmounted() {
    // 移除组件时,取消定时器
    this.debouncedClick.cancel()
  },
  methods: {
      click: _.debounce(function() {
        // ... 响应点击 ...
      }, 500)
  },
  template: `
    <button @click="debouncedClick">
      Save
    </button>
  `
})

computed

Getter:

与methods的区别:

​ 计算属性是基于它们的反应依赖关系缓存的。计算属性只在相关响应式依赖发生改变时它们才会重新求值。

Setter:

​ 默认只有getter,默认时可以提供setter

computed: {
  fullName: {
    // getter
    get() {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set(newValue) {
      const names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

watch

使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态

watch: {
	xxx(new,old){
		...
	}
}

(同样可以使用vm.$watch API

class

<div
  class="static"
  :class="{ active: isActive, 'text-danger': hasError }"
></div>
也可以绑定一个返回对象的computed
<div :class="classObject"></div>
...
computed: {
  classObject() {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}
//数组,三元表达式同样允许
<div :class="[activeClass, errorClass]"></div>
<div :class="[isActive ? activeClass : '', errorClass]"></div>

组件中绑定class,可以选择将传进来的attr绑定到特定的tag上

app.component('my-component', {
  template: `
    <p :class="$attrs.class">Hi!</p>
    <span>This is a child component</span>
  `
})

style

直接绑定一个样式对象,当然也可以绑定分散的data,语法类似css

<div :style="styleObject"></div> //数组语法可以将多个样式对象应用到同一个元素上
data() {
  return {
    styleObject: {
      color: 'red',
      fontSize: '13px'
    }
  }
}

自动添加前缀

在 :style 中使用需要 (浏览器引擎前缀) vendor prefixes 的 CSS property 时,如 transform,Vue 将自动侦测并添加相应的前缀。

多重值

<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

只会渲染最后一个被浏览器支持的值

v-if & v-show

  • 如果需要非常频繁地切换,则使用 v-show 较好;
  • 如果在运行时条件很少改变,则使用 v-if 较好。

当 v-if 与 v-for 一起使用时,v-if 具有比 v-for 更高的优先级,不推荐同时使用
修正方法:

<template v-for="todo in todos">
  <li v-if="!todo.isComplete">
    {{ todo }}
  </li>
</template>

v-for

<li v-for="(item, index) in items"></li>  <!-- 可以用 of 替代 in 作为分隔符 -->
<li v-for="(value, name) in myObject"> <!-- 遍历对象 -->
  {{ name }}: {{ value }}
</li>
<li v-for="(value, name, index) in myObject"> <!-- 第三个参数为索引 -->
  {{ index }}. {{ name }}: {{ value }}
</li>

建议尽可能在使用 v-for 时提供 key attribute

除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升,用于强制替换元素/组件而不是重复使用它

更变或替换数组

更变:push() pop() shift() unshift() splice() sort() reverse()
替换:filter() concat() slice()
Vue 不会重新渲染整个列表

组件数据传递

组件中使用v-for不会将数据自动传递到组件里,因为组件有自己独立的作用域。
为了把迭代数据传递到组件里,我们要使用 props:

<my-component
  v-for="(item, index) in items"
  :item="item"
  :index="index"
  :key="item.id"
></my-component>

v-on

$event可以传入方法,获得原生DOMevent

  • $event是指当前触发的是什么事件(鼠标事件,键盘事件等)
  • $event.target则指的是事件触发的目标,即哪一个元素触发了事件,这将直接获取该dom元素
  • $event.target.tagName指向事件发生目标的tag
<button @click="one($event), two($event)">
  Submit
</button>
methods: {
  one(event) {
    // first handler logic...
  },
  two(event) {
    // second handler logic...
  }
}

事件修饰符

.stop .prevent .capture .self .once .passive

顺序不同产生的效果不同

<!-- 阻止单击事件继续传播 -->
<a @click.stop="doThis"></a>

按键、系统、鼠标修饰符

按键:.enter .tab .delete .esc .space .up .down .left .right

系统:.ctrl .alt .shift .meta

鼠标:.left .right .middle

.exact

精确控制按下的系统修饰符组合

v-model

v-model 会忽略所有表单元素的 value、checked、selected attribute的初始值而总是将当前活动实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。

多选绑定
<select v-model="selected" multiple>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>

值绑定
通过:value来绑定v-model的值
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />

v-model
.lazy 当change而非input
.number 转换输入值为数值类型
.trim 过滤首尾空白字符

component

组件可以在Vue根实例中作为自定义元素使用
与new Vue接受相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等(无el)

<!-- Prop -->
通过 Prop 向子组件传递数据,被注册之后,就可以作为自定义attribute传递

<!-- emit -->
$emit 附加参数都会传给监听器回调,可以附加额外参数
@click="$emit('enlarge-text')"
@click="$emit('enlarge-text',0.1)"
使用$event来访问到被抛出的值
@enlarge-text="postFontSize += $event"
如果处理函数是一个方法,那么抛出的值会作为第一个参数来传入这个方法
emits选项也可以抛出事件
app.component('blog-post', {
  props: ['title'],
  emits: ['enlarge-text']
})
这将允许你检查组件抛出的所有事件(validate)

<!-- 组件下的v-model -->
<custom-input v-model="searchText"></custom-input>
app.component('custom-input', {
  props: ['modelValue'],
  template: `
    <input
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    >
  `
})
使用computed方法实现:
app.component('custom-input', {
  props: ['modelValue'],
  template: `
    <input v-model="value">
  `,
  computed: {
    value: {
      get() {
        return this.modelValue
      },
      set(value) { this.$emit('update:modelValue', value)
      }
    }
  }
})

<!-- 动态组件 -->
<component :is="currentTabComponent"></component>
组件会在currentTabComponent改变时改变,cTC可以包括已注册组件的名字,或一个组件选项对象

v-is 可以让本来渲染出错(不该出现在特定元素内部或外部的元素,比如ul只可以有li)的tag有了变通方法
<table>
  <tr v-is="'blog-post-row'"></tr>
</table>
值应为js字符串文本

<!-- 命名法 -->
组件命名:(推荐kebab-case,避免与当前以及未来的 HTML 元素发生冲突)
(对于非字符串模板或单文件组件,即对于直接在DOM中使用组件时)
1.全部小写
2.包含连字符 (及:即有多个单词与连字符符号连接)

对于PascalCase(首字母大写命名)
定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用

HTML 属性名不区分大小写,在JavaScript中的驼峰
app.component('blog-post', {
  props: ['postTitle'],
  template: `
    <h3>{{ postTitle }}</h3>
  `
})
在HTML则是横线字符分割
<blog-post post-title="hello!"></blog-post>

???
需要注意的是如果我们从以下来源使用模板的话,这条限制是不存在的:
字符串模板 (例如:template: '...')
单文件组件
<script type="text/x-template"></script>

<!-- 注册 -->
全局注册:
app.component('component-a', {
  /* ... */
})
局部注册:(局部注册的组件在其子组件中不可用
const ComponentA = {
  /* ... */
}
const app = Vue.createApp({
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

Props

-- 字符串数组形式
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

-- 每个Prop指定类型
props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // 或任何其他构造函数
}

-- 传递静态或动态的 Prop
任何类型的值都可以通过v-bind:的attribute传给一个prop

-- 单向数据流
父子 prop 之间形成了一个单向下行绑定:
	父级 prop 的更新会向下流动到子组件中,反之不行
每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值
	--> 不应该在一个子组件内部改变 prop,引用传入将会影响到父组件的状态
如果需要改变prop:
1.如果希望作为本地prop使用,最好定义一个本地data property接收父传递
2.如果要转换,可以定义一个计算属性,对原始的值进行转换然后保存

<!-- 验证 -->
propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function() {
        return { message: 'hello' }
      }
    },
propF: {
	validator: function(value) {
	// 这个值必须匹配下列字符串中的一个
	return ['success', 'warning', 'danger'].indexOf(value) !== -1
	}
}
注意:prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data、computed 等) 在 default 或 validator 函数中是不可用的。
type 可以是String Number Boolean Array Object Date Function Symbol

<!-- 命名 -->
当你使用 DOM 中的模板时,
camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
app.component('blog-post', {
  // camelCase in JavaScript
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<!-- kebab-case in HTML -->
<blog-post post-title="hello!"></blog-post>

非 Prop 的 Attribute

一个非 prop 的 attribute 定义:
传向一个组件,但是该组件并没有相应 props 或 emits 定义的 attribute
包括 class、style 和 id 等属性。

<!-- 单个根节点上的 Attribute 继承 -->
当组件返回单个根节点时,非 prop attribute 将自动添加到根节点的 attribute 中。
app.component('date-picker', {
  template: `
    <div class="date-picker">
      <input type="datetime" />
    </div>
  `
})

<!-- 禁用 Attribute 继承 -->
如果你不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false

可以访问组件的 $attrs property获取

app.component('date-picker', {
  inheritAttrs: false,
  template: `
    <div class="date-picker">
      <input type="datetime" v-bind="$attrs" />
    </div>
  `
})

<!-- 多个根节点上的 Attribute 继承 -->
有多个根节点的组件不具有自动 attribute 回退行为
如果未显式绑定 $attrs,将发出运行时警告。

自定义事件

不同于组件和 prop,事件名不存在任何自动化的大小写转换。
而是触发的事件名需要完全匹配监听这个事件所用的名称

!触发一个 camelCase 名字的事件,则监听这个名字的 kebab-case 版本是不会有任何效果的:

this.$emit('myEvent')
<my-component @my-event="doSomething"></my-component>

推荐你始终使用 kebab-case 的事件名

<!-- 定义自定义事件 -->
通过 emits 选项在组件上定义已发出的事件。
app.component('custom-form', {
  emits: ['in-focus', 'submit']
})
当在 emits 选项中定义了原生事件 (如 click) 时,将使用组件中的事件替代原生事件侦听器。
建议定义所有发出的事件,以便更好地记录组件应该如何工作。

-- 验证抛出的事件
app.component('custom-form', {
  emits: {
    // 没有验证
    click: null,

    // 验证submit 事件
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {
    submitForm() {
      this.$emit('submit', { email, password })
    }
  }
})

<!-- v-model -->
已在v-model中介绍过,回顾一下:(多个绑定)
<user-name
  v-model:first-name="firstName"
  v-model:last-name="lastName"
></user-name>
app.component('user-name', {
  props: {
    firstName: String,
    lastName: String
  },
  template: `
    <input 
      type="text"
      :value="firstName"
      @input="$emit('update:firstName', $event.target.value)">
    <input
      type="text"
      :value="lastName"
      @input="$emit('update:lastName', $event.target.value)">
  `
})

<!-- 定义自定义修饰符 -->
v-model 有内置修饰符——.trim、.number 和 .lazy
3.x中,添加到组件 v-model 的修饰符将通过 modelModifiers prop 提供给组件

slot

<slot></slot>
组件渲染时替换内容

!template 中没有包含一个 <slot> 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃

<!-- 作用域 -->
<todo-button action="delete">
  Clicking here will {{ action }} an item
  <!-- `action` 未被定义,因为它的内容是传递*到* <todo-button>,而不是*在* <todo-button>里定义的。  -->
</todo-button>
    
规则:
    父级模板里的所有内容都是在父级作用域中编译的;
    子模板里的所有内容都是在子作用域中编译的

<!-- 后备内容 --> 
<button type="submit">
  <slot>Submit</slot>
</button>
当slot不显示时显示
    
<!-- 具名插槽 -->
-定义:
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
一个不带 name 的 <slot> 出口会带有隐含的名字“default”。
-使用:
<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <template v-slot:default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

!v-slot 只能添加在 <template> 上 (只有一种例外情况:
当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用)

<!-- 插槽 prop -->
要使 item 可用于父级提供的 slot 内容,我们可以添加一个 <slot> 元素并将其绑定为属性
<ul>
  <li v-for="( item, index ) in items">
    <slot :item="item"></slot>
  </li>
</ul>
    
-- 重命名
<todo-list v-slot="{ item: todo }">
  <i class="fas fa-check"></i>
  <span class="green">{{ todo }}</span>
</todo-list>
    
-- 后备内容 
<todo-list v-slot="{ item = 'Placeholder' }">
  <i class="fas fa-check"></i>
  <span class="green">{{ item }}</span>
</todo-list>
    
<!-- 动态插槽名 -->
<template v-slot:[dynamicSlotName]>
    
<!-- 缩写 -->
v-slot: --> #
    <template #header>
    	<h1>Here might be a page title</h1>
  	</template>

提供 / 注入

父组件可以作为其所有子组件的依赖项提供程序,而不管组件层次结构有多深
-- 父组件有一个 provide 选项来提供数据
-- 子组件有一个 inject 选项来开始使用这个数据。

//provide
provide: {
	xxx: 
}
//inject
${this.xxx}

要访问组件实例 property,我们需要将 provide 转换为返回对象的函数

//返回对象
provide() {
	return {
    	todoLength: this.todos.length
    }
}
处理响应性

默认情况下,provide/inject 绑定不是被动绑定,无响应性。

-- 增加computed达到响应

​ provide() {
​ return {
​ todoLength: Vue.computed(() => this.todos.length)
​ }
}

-- ref property 或 reactive
import { provide, reactive, ref } from 'vue'

export default {
  components: {
    MyMarker
  },
  setup() {
    const location = ref('North Pole')
    const geolocation = reactive({
      longitude: 90,
      latitude: 135
    })
	provide('location', location)
	provide('geolocation', geolocation)
  }
}

动态组件 & 异步组件

keep-alive
<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component :is="currentTabComponent"></component>
</keep-alive>
defineAsyncComponent

?

components: {
    AsyncComponent: defineAsyncComponent(() =>
      import('./components/AsyncComponent.vue')
    )
}

模板引用???

返回对象

应该避免在模板或计算属性中访问 $refs

处理边界情况

强制更新

大概率出现错误,非常罕见的情况使用$forceUpdate强制更新

低级静态组件与 v-once

在 Vue 中渲染纯 HTML 元素的速度非常快,但有时你可能有一个包含很多静态内容的组件。在这些情况下,可以通过向根元素添加 v-once 指令来确保只对其求值一次,然后进行缓存

Mixin

// define a mixin object
const myMixin = {
  created() {
    this.hello()
  },
  methods: {
    hello() {
      console.log('hello from mixin!')
    }
  }
}

// define an app that uses this mixin
const app = Vue.createApp({
  mixins: [myMixin]
})

app.mount('#mixins-basic') // => "hello from mixin!"
选项合并
  • 数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先
  • 同名钩子函数将合并为一个数组,因此都将被调用;混入对象的钩子将在组件自身钩子之前调用。
自定义选项合并策略
const app = Vue.createApp({})

app.config.optionMergeStrategies.customOption = (toVal, fromVal) => {
  // return mergedVal
}
customOption是冲突值的名字
?
toVal为Mixin提供
fromVal为原参数
合并策略接收在父实例和子实例上定义的该选项的值,分别作为第一个和第二个参数

自定义指令

除了核心功能默认内置的指令 (v-modelv-show),Vue支持自定义指令。

全局指令
const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  mounted(el) {
    // Focus the element
    el.focus()
  }
})
局部指令
directives: {
  focus: {
    // 指令的定义
    mounted(el) {
      el.focus()
    }
  }
}

使用方法:<input v-focus />

钩子函数

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

app.directive('my-directive', {
  // 指令是具有一组生命周期的钩子:
  // 在绑定元素的父组件挂载之前调用
  beforeMount() {},
  // 绑定元素的父组件挂载时调用
  mounted() {},
  // 在包含组件的 VNode 更新之前调用
  beforeUpdate() {},
  // 在包含组件的 VNode 及其子组件的 VNode 更新之后调用
  updated() {},
  // 在绑定元素的父组件卸载之前调用
  beforeUnmount() {},
  // 卸载绑定元素的父组件时调用
  unmounted() {}
})
el

指令绑定到的元素。这可用于直接操作 DOM。

binding

包含以下 property 的对象。

  • instance:使用指令的组件实例。
  • value:传递给指令的值。例如,在 v-my-directive="1 + 1" 中,该值为 2
  • oldValue:先前的值,仅在 beforeUpdateupdated 中可用。值是否已更改都可用。
  • arg:参数传递给指令 (如果有)。例如在 v-my-directive:foo 中,arg 为 "foo"
  • modifiers:包含修饰符 (如果有) 的对象。例如在 v-my-directive.foo.bar 中,修饰符对象为 {foo: true,bar: true}
  • dir:一个对象,在注册指令时作为参数传递。例如,在以下指令中
app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})

dir 将会是以下对象 { mounted(el) { el.focus() } }

vnode

上面作为 el 参数收到的真实 DOM 元素的蓝图。

prevNode

上一个虚拟节点,仅在 beforeUpdateupdated 钩子中可用。

动态指令参数

v-mydirective:[argument]="value"

可以使用 binding.arg 来获取 argument

可以使用 binding.value 来获取 value

以此根据组件实例数据进行更新

函数简写

去掉 mounted(){}

app.directive('pin', (el, binding) => {
  el.style.position = 'fixed'
  const s = binding.arg || 'top'
  el.style[s] = binding.value + 'px'
})
组件使用

在 3.0 中,有了片段支持,组件可能有多个根节点。如果在具有多个根节点的组件上使用自定义指令,则会产生问题。

Teleport

Teleport允许深度嵌套下直接控制DOM中哪个父节点下呈现HTML,而不必求助于全局状态或将其拆分为两个组件。

<teleport to="body">
	...
</teleport>
Vue components 中使用
app.component('parent-component', {
  template: `
    <h2>This is a parent component</h2>
    <teleport to="#endofbody">
      <child-component name="John" />
    </teleport>
  `
})

即使在不同的地方渲染 child-component,它仍将是 parent-component 的子级,并将从中接收 name prop。

在同一目标上使用多个 teleport

多个 <teleport> 组件可以将其内容挂载到同一个目标元素,顺序将是一个简单的追加

渲染函数

当需要可变地渲染 template 的时候,就可以使用 render 函数

app.component('anchored-heading', {
  render() {
    const { h } = Vue

    return h(
      'h' + this.level, // tag name
      {}, // props/attributes
      this.$slots.default() // array of children
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

h()

h() 函数是一个用于创建 vnode 的实用程序

// @returns {VNode}
h(
  // {String | Object | Function | null} tag
  // 一个 HTML 标签名、一个组件、一个异步组件,或者 null。
  // 使用 null 将会渲染一个注释。
  //
  // 必需的。
  'div',

  // {Object} props
  // 与 attribute、prop 和事件相对应的对象。
  // 我们会在模板中使用。
  //
  // 可选的。
  {},

  // {String | Array | Object} children
  // 子 VNodes, 使用 `h()` 构建,
  // 或使用字符串获取 "文本 Vnode" 或者
  // 有 slot 的对象。
  //
  // 可选的。
  [
    'Some text comes first.',
    h('h1', 'A headline'),
    h(MyComponent, {
      someProp: 'foobar'
    })
  ]
)

render下的v-model

v-model 指令扩展为 modelValueonUpdate:modelValue 在模板编译过程中,我们必须自己提供这些prop:

props: ['modelValue'],
render() {
  return Vue.h(SomeComponent, {
    modelValue: this.modelValue,
    'onUpdate:modelValue': value => this.$emit('update:modelValue', value)
  })
}

render下的v-on

我们必须为事件处理程序提供一个正确的prop名称,例如,要处理 click 事件,prop名称应该是 onClick

render() {
  return Vue.h('div', {
    onClick: $event => console.log('clicked', $event.target)
  })
}

事件修饰符

修饰符 处理函数中的等价操作
.stop event.stopPropagation()
.prevent event.preventDefault()
.self if (event.target !== event.currentTarget) return
按键: .enter, .13 if (event.keyCode !== 13) return (对于别的按键修饰符来说,可将 13 改为另一个按键码
修饰键: .ctrl, .alt, .shift, .meta if (!event.ctrlKey) return (将 ctrlKey 分别修改为 altKey, shiftKey, 或 metaKey)

插槽

props: ['message'],
render() {
  // `<div><slot :text="message"></slot></div>`
  return Vue.h('div', {}, this.$slots.default({
    text: this.message
  }))
}
子组件插槽???
render() {
  // `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`
  return Vue.h('div', [
    Vue.h('child', {}, {
      // pass `slots` as the children object
      // in the form of { name: props => VNode | Array<VNode> }
      default: (props) => Vue.h('span', props.text)
    })
  ])
}

响应式

reactice

返回对象的响应式副本

响应性基础 API

ref

ref 会返回一个可变的响应式对象,只包含一个名为 value 的 property

展开

Ref 展开仅发生在被响应式 Object 嵌套的时候:可以省略value

当从 Array 或原生集合类型如 Map访问 ref 时,不会进行展开

解构

普通的解构会使property的响应性丢失

let { author, title } = book

转换为一组ref后保留响应式关联:

let { author, title } = toRefs(book)

Refs API

使用 readonly 防止更改响应式对象
const original = reactive({ count: 0 })
const copy = readonly(original)

Compositional API

setup 组件选项

新的 setup 组件选项在创建组件之前执行,一旦 props 被解析,并充当合成 API 的入口点。

(由于在执行 setup 时尚未创建组件实例,因此在 setup 选项中没有 this。这意味着,除了 props 之外,你将无法访问组件中声明的任何属性——data computed method

Props

setup 函数中的第一个参数是 props,是响应式的,当传入新的 prop 时,它将被更新。

context

传递给 setup 函数的第二个参数是 contextcontext 是一个普通的 JavaScript 对象,它暴露三个组件的 property,不是响应式的,因此可以安全地对 context 使用 ES6 解构。

// MyBook.vue

export default {
  setup(props, context) {
    // Attribute (非响应式对象)
    console.log(context.attrs)

    // 插槽 (非响应式对象)
    console.log(context.slots)

    // 触发事件 (方法)
    console.log(context.emit)
  }
}
setup中的生命周期钩子
选项式 API Hook inside setup
beforeCreate Not needed*
created Not needed*
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered

*Not needed:在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

Compositional API的提供/注入

我们也可以在组合式 API 中使用 provide/inject。两者都只能在当前活动实例的 setup() 期间调用。

  • provide 函数允许你通过两个参数定义 property:
  1. property 的 name (<String> 类型)
  2. property 的 value
  • inject 函数有两个参数:
  1. 要注入的 property 的名称
  2. 一个默认的值 (可选)

https://vue3js.cn/docs/zh/guide/change-detection.html#声明响应式-property???

posted @ 2021-04-24 22:34  Zeiion  阅读(987)  评论(0编辑  收藏  举报