vue3要点个人总结

setup函数

  • 与data、methods中的数据合并时,setup返回的数据的优先级更高
  • 没有this,setup执行在beforeCreate之前
  • 参数1,props
  • 参数2,context对象,包括arrtrs,slots,emit,expose

expose 函数

  • expose 函数用于显式地限制该组件暴露出的属性,当父组件通过模板引用(组件上的ref)访问该组件的实例时,将仅能访问 expose 函数暴露出的内容

ref

  • 会将传入的数据(基本类型或者复杂类型)包装成对象,在script中修改时要使用.value,在模板中不需要.value(模板渲染上下文的顶层属性时才适用自动“解包”)

unref

  • 如果参数是 ref,则返回内部值,否则返回参数本身。

shallowRef

  • 只有重新赋值才是响应式

reactive

  • 只接受对象类型的参数
  • script中不需要使用.value

shallReactive

  • 只有对象的第一层属性是响应式的

toRefs

  • 将响应式对象中的所有属性包装为ref对象并返回
  • setup返回reactive定义的对象时,如果使用...展开对象,则对象中基本类型失去响应性,可以使用...toRefs(obj),使每个属性变为响应性

toRef

  • 为响应性对象上的某个属性创建一个ref引用,属性更新时,引用对象会同步更新

readonly

  • 只读代理对象,不可修改

shallowReadonly

  • 修改第一层属性会报错

toRow

markRow

  • 将一个对象标记为不可被转为代理。返回该对象本身。

computed函数

  • 接收一个包含返回值的回调函数,或者包含getter/setter函数的对象(可写计算属性)
  • getter 不应有副作用,应该使用监听器根据其他响应式状态的变更来创建副作用
  • 计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算
  • 返回值为一个计算属性 ref,与ref对象表现一致
  • 计算属性值会基于其响应式依赖被缓存

watch函数

  • 监听一个、多个、getter函数等,监听多个时第一个使用数组参数
  • 监听reactive对象的属性时,第一个参数必须使用函数形式(也就是用一个返回该属性的 getter 函数)

watchEffect

  • 初始化时就执行一次,不需要指定监听的数据

provide函数

  • provide('color','red')

inject函数

  • color = inject('color')

响应性数据的判断

  • isRef
  • isReactive
  • isReadonly
  • isProxy

生命周期

  • onBeforeMount
  • onMounted
  • onBeforeUpdate
  • onUpdated
  • onBeforeUnmount
  • onUnmounted
  • onErrorCaptured
  • onRenderTracked
  • onRenderTriggered
  • onActivated
  • onDeactivated

组件

  • Fragment,允许多个根元素
  • Suspense,展示中间过渡状态
  • TransitionGroup,添加动画效果

css伪类

  • :deep
  • :slotted
  • :global

css v-bind

  • 单文件组件的 <style> 标签支持使用 v-bind CSS 函数将 CSS 的值链接到动态的组件状态:
<script setup>
const theme = {
  color: 'red'
}
</script>

<template>
  <p>hello</p>
</template>

<style scoped>
p {
  color: v-bind('theme.color');
}
</style>

获取dom

<input ref = inputDom />
const inputDom = ref(null);
onMounted(() => {
  nextTick(() => {
    inputDom.value.focus()
  })
})

customRef

  • 使用customRef,写一个组合式函数,实现自动依赖追踪,触发响应
import { customRef } from 'vue'

export function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      }
    }
  })
}

<script setup>
import { useDebouncedRef } from './debouncedRef'
const text = useDebouncedRef('hello')
</script>
<template>
  <input v-model="text" />
</template>

Vue 的两个核心功能

  • 声明式渲染:Vue 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。
  • 响应性:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。

API风格怎么选

  • 当你不需要使用构建工具,或者打算主要在低复杂度的场景中使用 Vue,例如渐进增强的应用场景,推荐采用选项式 API。
  • 当你打算用 Vue 构建完整的单页应用,推荐采用组合式 API + 单文件组件。

根组件模板选择

当根组件没有设置 template 选项时,Vue 将自动使用容器的 innerHTML 作为模板。

响应性丢失

  • 不可以随意地“替换”一个响应式对象,因为这将导致对初始引用的响应性连接丢失
let state = reactive({ count: 0 })
// 上面的引用 ({ count: 0 }) 将不再被追踪(响应性连接已丢失!)
state = reactive({ count: 1 })
  • 将响应式对象的属性赋值或解构至本地变量时,或是将该属性传入一个函数时,我们会失去响应性
const state = reactive({ count: 0 })
// n 是一个局部变量,同 state.count
// 失去响应性连接
let n = state.count
// 不影响原始的 state
n++
// count 也和 state.count 失去了响应性连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收一个普通数字,并且
// 将无法跟踪 state.count 的变化
callSomeFunction(state.count)

ref保持响应性

  • 一个包含对象类型值的 ref 可以响应式地替换整个对象
const objectRef = ref({ count: 0 })
// 这是响应式的替换
objectRef.value = { count: 1 }
  • ref 被传递给函数或是从一般对象上被解构时,不会丢失响应性
const obj = {
  foo: ref(1),
  bar: ref(2)
}
// 该函数接收一个 ref
// 需要通过 .value 取值
// 但它会保持响应性
callSomeFunction(obj.foo)
// 仍然是响应式的
const { foo, bar } = obj

数组和集合类型的 ref 解包

  • 跟响应式对象不同,当 ref 作为响应式数组或像 Map 这种原生集合类型的元素被访问时,不会进行解包。
const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)
const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)

关于v-for中的key

  • 推荐在任何可行的时候为 v-for 提供一个 key attribute,除非所迭代的 DOM 内容非常简单 (例如:不包含组件或有状态的 DOM 元素),或者你想有意采用默认行为来提高性能。
  • key 绑定的值期望是一个基础类型的值,例如字符串或 number 类型。不要用对象作为 v-for 的 key。

计算属性中操作数组注意事项

  • 在计算属性中使用 reverse() 和 sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做
- return numbers.reverse()  // bad
+ return [...numbers].reverse()  //good

watch vs. watchEffect

  • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
  • watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。

监听器回调的触发时机

  • 默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用,意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态。
  • 如果想在侦听器回调中能访问被 Vue 更新之后的 DOM,你需要指明 flush: 'post' 选项

停止侦听器

  • 侦听器会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止,所以侦听器必须用同步语句创建。
  • 如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏。

函数模板引用

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

组件ref

  • 如果一个子组件使用的是选项式 API 或没有使用 <script setup>,被引用的组件实例和该子组件的 this 完全一致,这意味着父组件对子组件的每一个属性和方法都有完全的访问权
  • 使用了 <script setup> 的组件是默认私有的:一个父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西,除非子组件在其中通过 defineExpose 宏显式暴露

使用props & emit

  • setup标签中必须在组件的 props 列表上声明它。这里要用到 defineProps 宏和definEmits宏
<!-- BlogPost.vue -->
<script setup>
const props = defineProps(['title'])
const emit = defineEmits(['enlarge-text'])
emit('enlarge-text')
</script>

<template>
  <h4>{{ title }}</h4>
</template>
  • defineProps 是一个仅 <script setup> 中可用的编译宏命令,并不需要显式地导入。声明的 props 会自动暴露给模板。defineProps 会返回一个对象,其中包含了可以传递给组件的所有 props
  • 如果你没有使用 <script setup>,props 必须以 props 选项的方式声明,props 对象会作为 setup() 函数的第一个参数被传入
export default {
  props: ['title'],
  emits: ['enlarge-text'],
  setup(props,ctx) {
    console.log(props.title);
    ctx.emit('enlarge-text')
  }
}

全局注册组件的问题

  1. 全局注册,但并没有被使用的组件无法在生产打包时被自动移除 (也叫“tree-shaking”)
  2. 全局注册在大型项目中使项目的依赖关系变得不那么明确,这可能会影响应用长期的可维护性

使用一个对象绑定多个 prop

const post = {
  id: 1,
  title: 'My Journey with Vue'
}
<BlogPost v-bind="post" />
// 上面写法等价于下面的写法
<BlogPost :id="post.id" :title="post.title" />

透传attributes时注意

  • 虽然 attrs 对象总是反映为最新的透传 attribute,但它并不是响应式的 (考虑到性能因素)。你不能通过侦听器去监听它的变化。如果你需要响应性,可以使用 prop。或者你也可以使用 onUpdated() 使得在每次更新时结合最新的 attrs 执行副作用。

无渲染组件

  • 一些组件可能只包括了逻辑而不需要自己渲染内容,视图输出通过作用域插槽全权交给了消费者组件
<MouseTracker v-slot="{ x, y }">
  Mouse is at: {{ x }}, {{ y }}
</MouseTracker>

组合式函数

  • 组合式函数名以“use”开头,例如:useMouse
  • 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数。
  • 你可以嵌套多个组合式函数:一个组合式函数可以调用一个或多个其他的组合式函数
  • 有状态逻辑,负责管理会随时间而变化的状态,例如:跟随鼠标位置
  • 无状态的逻辑,例如:lodash、date-fns
  • 相比无渲染组件,组合式函数不会产生额外的组件实例开销
  • 推荐在纯逻辑复用时使用组合式函数,在需要同时复用逻辑和视图布局时使用无渲染组件。

自定义指令

  • 简化形式:一个很常见的情况是仅仅需要在 mounted 和 updated 上实现相同的行为,除此之外并不需要其他钩子。这种情况下我们可以直接用一个函数来定义指令,如下所示:
<div v-color="color"></div>
app.directive('color', (el, binding) => {
  // 这会在 `mounted` 和 `updated` 时都调用
  el.style.color = binding.value
})

Vue 自身的安全机制

  • HTML 内容转译
// userProvidedString
<h1>{{ userProvidedString }}</h1>
  • Attribute 绑定转译
// 动态 attribute 的绑定会被自动转义
<h1 :title="userProvidedString">
  hello
</h1>

潜在的危险

  1. HTML注入:v-html、使用渲染函数、以JSX的形式使用渲染函数
  2. URL 注入:使用第三方库、后端在保存至数据库之前处理
<a :href="userProvidedUrl">
  click me
</a>
  1. 样式注入
// bad,恶意用户仍然能利用 CSS 进行“点击劫持”,例如,可以在“登录”按钮上方覆盖一个透明的链接
<a
  :style="userProvidedStyles"
>
  click me
</a>

// good,给用户提供特定的属性名
<a
  :style="{
    color: userProvidedColor,
    background: userProvidedBackground
  }"
>
  click me
</a>

  1. JavaScript 注入
    我们强烈建议任何时候都不要在 Vue 中渲染 <script>

vue构建各种应用

  1. web应用
  2. 配合 ElectronTauri 构建桌面应用
  3. 配合 Ionic Vue 构建移动端应用
  4. 使用 Quasar 用同一套代码同时开发桌面端和移动端应用

集成其他库

  • 集成Immer,使用不可变数据
  • 集成XState,使用状态机
  • 集成RxJS,处理异步事件流

响应性语法糖

配置打开支持响应性语法糖

// vite.config.js
export default {
  plugins: [
    vue({
      reactivityTransform: true
    })
  ]
}
  1. 响应性变量
    实验性的功能,不需要.value即可修改数据,是因为可以编译时转换
// 这些宏函数都是全局可用的、无需手动导入。但如果你想让它更明显,你也可以选择从 vue/macros 中引入它们
import { $ref } from 'vue/macros'
let count = $ref(3) // 3
  1. 通过 $() 解构
import { useMouse } from '@vueuse/core'
const { x, y } = $(useMouse())
console.log(x, y) // 434 349
  • 对 $() 的解构在响应式对象和包含数个 ref 的对象都可用。
  • 用 $() 将现存的 ref 转换为响应式对象
function myCreateRef() {
  return ref(0)
}
let count = $(myCreateRef())
  • 响应式 props 解构
const { msg, foo: bar,count=1 } = definProps()
  • 保持在函数间传递时的响应性
// 使用$$对函数的参数进行包装
let count = $ref(0)
trackChange($$(count))
  • 作为函数返回值
// 如果将响应式变量直接放在返回值表达式中会丢失掉响应性:
function useMouse() {
  let x = $ref(0)
  let y = $ref(0)
  // 监听 mousemove 事件
  // 不起效!
  return {
    x,
    y
  }
// 修改后起效
  return $$({
    x,
    y
  })
}

  • 在已解构的 props 上使用 $$()
const { count } = defineProps<{ count: number }>()
passAsRef($$(count))

VueRouter.createWebHashHistory()

  • 应用hash路由模式

VueRouter.createWebHsitory()

  • 应用路由history模式

useRouter 或 useRoute

  • 可以在 setup 函数中访问路由

Sensitive 与 strict 路由配置

  • 默认情况下,路由 /users 将匹配 /users、/users/、甚至 /Users/。这种行为可以通过 strict 和 sensitive 选项来修改,它们可以既可以应用在整个全局路由上,又可以应用于当前路由上:
  • sensitive,使路由匹配区分大小写
  • strict,严格检查路径末尾是否有尾部斜线(/)

可选参数

const routes = [
  // 匹配 /users 和 /users/posva
  { path: '/users/:userId?' },
  // 匹配 /users 和 /users/42
  { path: '/users/:userId(\\d+)?' },
]

全局解析守卫

  • 在 每次导航时都会触发,但是确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用。

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
  5. 在路由配置里调用 beforeEnter。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter。
  8. 调用全局的 beforeResolve 守卫(2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

动态路由

  • router.addRoute()
router.addRoute({ path: '/about', component: About })
// 我们也可以使用 this.$route 或 route = useRoute() (在 setup 中)
router.replace(router.currentRoute.value.fullPath)

// 添加嵌套路由
router.addRoute({ name: 'admin', path: '/admin', component: Admin })
router.addRoute('admin', { path: 'settings', component: AdminSettings })

  • router.removeRoute()
router.addRoute({ path: '/about', name: 'about', component: About })
// 删除路由
router.removeRoute('about')

pinia api

  • defineStore,定义一个store

在pinia中使用选项式api

  • pinia的store中也可以使用state、getters、actions选项,类似vuex
  • 它也提供类似的辅助函数mapStores()、mapState() 或 mapActions()

pinia中定义 store

  • 你可以定义任意多的 store,但为了让使用 pinia 的益处最大化(比如允许构建工具自动进行代码分割以及 TypeScript 推断),你应该在不同的文件中去定义 store。

解构store

  • store 是一个用 reactive 包装的对象,这意味着不需要在 getters 后面写 .value,就像 setup 中的 props 一样,如果你写了,我们也不能解构它,但你可以直接从 store 中解构 action。你可以使用storeToRefs来解决解构state和getter时丢失响应性的问题

访问state

  • 默认情况下,你可以通过 store 实例访问 state,直接对其进行读写。
const store = useStore()
store.count++

重置state

  • 你可以通过调用 store 的 $reset() 方法将 state 重置为初始值。
const store = useStore()
store.$reset()

变更 state

  • 除了用 store.count++ 直接改变 store,你还可以调用 $patch 方法。
  • $patch方法可以接受对象语法或者一个回调函数

替换state

  • 你不能完全替换掉 store 的 state,因为那样会破坏其响应性。但是,你可以 patch 它。

订阅state

<script setup>
const someStore = useSomeStore()
// this subscription will be kept even after the component is unmounted
someStore.$subscribe(callback, { detached: true })
</script>

订阅action

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

推荐阅读更多精彩内容