变动

  • 实例
const app = new Vue({})
Vue.use()
Vue.mixin()
Vue.component()
Vue.directive()
const app = Vue.createApp({})
app.use()
app.mixin()
app.component()
app.directive()
  • createApp 代替 new Vue

Vue3 ~-LMLPHP

  • 允许多个根标签

Vue3 ~-LMLPHP

  • createStore 代替 Vue.use(Vuex)

Vue3 ~-LMLPHP

  • createRouter 代替 Vue.use(VueRouter)

Vue3 ~-LMLPHP

  • 动画
 - v-enter --- v-enter-from
 - v-leave-to  --- vl-eave-to 
 - v-leave  ---  v-leave-from
 - v-enter-to  --- v-enter-to
  • 移除过滤器 filter、keyCode、v-on.native

新特性

  • 组合式 API :定义的数据和使用一并进行处理,达到易读,更便捷、更好的代码组织效果
  • **** options api 对应 react class component
  • **** composition api 对应 react hooks,setup 只会调用一次,hooks 可多次调用
  • 响应式变更:使用 Proxy 代替 Object.defineProperty
  • 全新的全家桶:vue-router、vuex、pinia 等周边库更新
  • TypeScript 支持
  • Vite 支持:依赖于 es module 导致无法直接对 commonJS 的模块化方式进行支持。必须采用依赖预构建

setup

  • options api 中的 data methods computed… 可以发访问setup中的属性和方法
  • setup中不能访问options api中的 data methods computed…
  • 返回一个对象或渲染函数(render 函数)、两个参数:props:参数、context:上下文( attrs, slots, emit )
setup(props, { attrs, slots, emit }) {
	return {}
}
  • thisundefined ,通过 getCurrentInstance 获取实例
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()

生命周期

  • beforeDestroy —> beforeUnmount
  • destroyed —> unmounted
  • setup 等于 beforeCreate 和 created
import {
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted
} from 'vue'

ref、reactive

  • reference对象:创建一个包含响应式的引用对象,接受类型可以是基本类型,也可以是对象类型,除了 template 和reactive,需通过.value修改其值;
  • 响应式实现:基本类型依赖于Object.defineProperty,对象依赖于proxy
<template>
  <p ref="elemRef">文字</p>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const elemRef = ref(null)
onMounted(() => {
  console.log(elemRef.value)
})
</script>
  • reactive:定义一个引用类型响应式数据,不能使用基本类型,解构 reactive 的值会失去响应式
import { ref, reactive } from 'vue'
const nameRef = ref('张三')
const state = reactive({
  name: nameRef
})
</script>

toRef、toRefs

  • toRef:针对一个响应式对象(reactive封装)的属性,创建一个ref类型并且两者保持引用关系;
<script setup>
import { toRef, reactive } from 'vue'
const state = reactive({
  age: 20,
  name: '张三'
})
const ageRef = toRef(state, 'age')
</script>
  • toRefs:将响应式对象(reactive封装)转换为普通对象,对象中的每个属性都是ref,两者保持引用关系
<script setup>
import { toRefs, reactive } from 'vue'
function useFeatureX() {
  const state = reactive({
    x: 1,
    y: 2
  })
  return toRefs(state)
}
const { x, y } = useFeatureX()
</script>

emits

  • 自定义事件需要进行声明
<HelloWorld @onSayHello="sayHello" />
export default {
  emits: ['onSayHello'],
  setup(props, { emit }) {
    emit('onSayHello', '内容')
  }
}
  • 多事件
<button @click="one($event) two($event)">click</button>

watch、watchEffect

  • watch:监听值、处理函数、配置项
  • ref 类型 newValue, oldValue 不需要 .value
  • 引用类型数据设置深度监视无法正确的获取 oldValue
  • reactive 使用函数形式
import { watch } from 'vue'

watch(
  data, // 监听一个基本类型
  // [data1,data2], // 监听多个 ref 基本属性
  // objdata, // ref 引用类型
  (newValue, oldValue) => {},
  { immediate: true } // 初始化监听
)

watch(
  () => obj.xx, // 监听一个 reactive
  // [ () => obj1.xx, () => obj2.xx ], // 监听多个 reactive
  (newValue, oldValue) => {},
  // { immediate: true , deep: true} // 引用类型设置深度监视
)
  • watchEffect:不用指明监视属性,回调中用到什么属性即监视什么属性,默认开启 immediate:true
import { watchEffect } from 'vue'
watchEffect(() => {
  // ...
})

v-model 自定义

  • 父组件
<my-input v-model="val" />
const val = ref('hello')
  • 子组件
props: {
  modelValue: String
},
const handler = (e: Event) => {
  const targetValue = (e.target as HTMLInputElement).value
  context.emit('update:modelValue', targetValue) // 相当于自定义modal $emit
}

.sync

  • vue2.x
<myComponent v-bind:age.sync="age"></myComponent>
  • vue3.x
<template>
  <p>{{ age }}</p>
  <user-info v-model:ageRef="age"></user-info>
</template>

<script>
import { reactive, toRefs } from 'vue'
import UserInfo from './UserInfo.vue'

export default {
  name: 'VModel',
  components: { UserInfo },
  setup() {
    const state = reactive({
      age: '20'
    })
    return toRefs(state)
  }
}
</script>
<template>
  <input :value="ageRef" @input="$emit('update:ageRef', $event.target.value)" />
</template>

<script>
export default {
  name: 'UserInfo',
  props: {
    ageRef: String
  }
}
</script>

异步组件

vue2.x

components:{
	'my-component':() => import('./xx.vue')
}

vue3.x

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

Teleport

直接将元素插入到某个节点之中

<teleport to="body">
	...
</teleport>

Suspense

用于实现异步,组件内部有两个插槽。

<Suspense>
	<template>
		<AsyncComponent/>
	</template>
	<template #fallback>
		<span>loading...</span>
	</template>
</Suspense>

Vue3.3

  • defineModel

before:

// 1
defineProps({
  modelValue: {
    type: Number,
    required: true,
    default: 0
  }
})
// 2
defineProps(['modelValue']) 

now:

const modelValue = defineModel<number>({ default: 0 })
  • defineEmits

before:

const emits = defineEmits<
  SE<{
    clickCount(num: number): void
  }>
>()
const emits = defineEmits<{
  (e: 'clickCount', num: number): void
}>()

now:

const emits = defineEmits<{
  clickCount: [num: number]
}>()

Vue3为何更快

  • Proxy 响应式
  • PatchFlag:编译模板时动态节点会做标记,标记分为不同类型,diff 算法可以区分静态节点以及不同类型的动态节点;
<div>
  <span>hello</span>
  <span>{{ name }}</span>
  <span :class="blue">张三</span>
  <span :id="zhangsan">张三</span>
  <span :id="lisi" :class="black">{{ obj.name }}</span>
</div>
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, normalizeClass as _normalizeClass, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _createElementVNode("span", null, "hello"),
    _createElementVNode("span", null, _toDisplayString(_ctx.name), 1 /* TEXT */),
    _createElementVNode("span", {
      class: _normalizeClass(_ctx.blue)
    }, "张三", 2 /* CLASS */),
    _createElementVNode("span", { id: _ctx.zhangsan }, "张三", 8 /* PROPS */, ["id"]),
    _createElementVNode("span", {
      id: _ctx.lisi,
      class: _normalizeClass(_ctx.black)
    }, _toDisplayString(_ctx.obj.name), 11 /* TEXT, CLASS, PROPS */, ["id"])
  ]))
}

以上TEXT 、PROPS 、CLASS 则为标记的不同类型,只会去比较有标记的区域,静态则不会进行对比。

Vue3 ~-LMLPHP

  • hoistStatic:静态节点的定义提升到父作用域进行缓存,多个相邻的静态节点会被合并起来,拿空间换时间;
  • cacheHandler:缓存事件
  • SSR 优化
  • tree-shaking:模板编译会根据不同情况引入不同的API
09-22 18:20