vue3学习笔记

笔记写完后,要复制到博客上的

一、基础语法

1、创建一个Vue应用

  • 应用实例:每个 Vue 应用都是通过 createApp 函数创建一个新的 应用实例

    import { createApp } from 'vue'
    
    const app = createApp({
      /* 根组件选项 */
    })
    
  • 根组件:我们传入 createApp 的对象实际上是一个组件,每个应用都需要一个“根组件”,其他组件将作为其子组件。
    如果你使用的是单文件组件,我们可以直接从另一个文件中导入根组件。

    import { createApp } from 'vue'
    // 从一个单文件组件中导入根组件
    import App from './App.vue'
    
    const app = createApp(App)
    
  • 挂载应用:

    应用实例必须在调用了 .mount() 方法后才会渲染出来。该方法接收一个“容器”参数,可以是一个实际的 DOM 元素或是一个 CSS 选择器字符串。

  • 应用配置:

    应用实例会暴露一个 .config 对象允许我们配置一些应用级的选项,例如定义一个应用级的错误处理器,用来捕获所有子组件上的错误:

    app.config.errorHandler = (err) => {
      /* 处理错误 */
    }
    
  • 多个应用实例:【基本不用】

2、模板语法

  • 文本插值:

  • 原始HTML:双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html 指令

  • Attribute 绑定:

    • 同名简写:如果 attribute 的名称与绑定的 JavaScript 值的名称相同,那么可以进一步简化语法,省略attribute 值:

      <!-- 与 :id="id" 相同 -->
      <div :id></div>
      
      <!-- 这也同样有效 -->
      <div v-bind:id></div>
      
    • 动态绑定多个值:
      果你有像这样的一个包含多个 attribute 的 JavaScript 对象:

      const objectOfAttrs = {
        id: 'container',
        class: 'wrapper',
        style: 'background-color:green'
      }
      

      通过不带参数的 v-bind,你可以将它们绑定到单个元素上:

      <div v-bind="objectOfAttrs"></div>
      
  • 使用JavaScript表达式:

    • 仅支持表达式
    • 调用函数
    • 受限的全局访问
      模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。该列表中会暴露常用的内置全局对象,比如 MathDate
      没有显式包含在列表中的全局对象将不能在模板内表达式中访问,例如用户附加在 window 上的属性。然而,你也可以自行在 app.config.globalProperties 上显式地添加它们,供所有的 Vue 表达式使用。
  • 指令 Directives

    • 参数 Arguments:某些指令会需要一个“参数”,在指令名后通过一个冒号隔开做标识。
      例如:v-bind:href="url",v-bind冒号后面的href就是v-bind指令的参数。

      <a v-bind:href="url"> ... </a>
      
    • 动态参数:
      同样在指令参数上也可以使用一个 JavaScript 表达式,需要包含在一对方括号内:

      <a v-bind:[attributeName]="url"> ... </a>
      

      注意:指令中动态参数值是有限制的,值应当是一个字符串。语法上也有一些限制。

    • 修饰符:修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。
      例如 .prevent 修饰符会告知 v-on 指令对触发的事件调用 event.preventDefault()

      <form @submit.prevent="onSubmit">...</form>
      

3、响应式基础:

  • ref()

    • 为 refs 标注类型

    • <script setup>:在 setup() 函数中手动暴露大量的状态和方法非常繁琐。单文件组件中,我们可以使用 <script setup> 来大幅度地简化代码:

      <script setup>
      import { ref } from 'vue'
      
      const count = ref(0)
      
      function increment() {
        count.value++
      }
      </script>
      
      <template>
        <button @click="increment">
          {{ count }}
        </button>
      </template>
      
  • reactive()

  • DOM更新时机:当你修改了响应式状态时,DOM 会被自动更新。但是需要注意的是,DOM 更新不是同步的。Vue 会在“next tick”更新周期中缓冲所有状态的修改,以确保不管你进行了多少次状态修改,每个组件都只会被更新一次。

    import { nextTick } from 'vue'
    
    async function increment() {
      count.value++
      await nextTick()
      // 现在 DOM 已经更新了
    }
    

4、计算属性:

<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
  ]
})

// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

  • 为计算属性标注类型
  • 可写计算属性
  • 获取上一个值:
    如果需要,可以通过访问计算属性的 getter 的第一个参数来获取计算属性返回的上一个值。

5、侦听器:watch数据监听

<script setup>
import { ref, watch } from 'vue'

const question = ref('')

// 可以直接侦听一个 ref
watch(question, async (newQuestion, oldQuestion) => {

})
</script>
  • 侦听数据源类型:

    watch 的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组

  • 深层侦听器:
    直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:

    const obj = reactive({ count: 0 })
    
    watch(obj, (newValue, oldValue) => {
      // 在嵌套的属性变更时触发
    })
    
    obj.count++
    
  • 即时回调的侦听器:(立刻执行)
    watch 默认是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。

    watch(
      source,
      (newValue, oldValue) => {
        // 立即执行,且当 `source` 改变时再次执行
      },
      { immediate: true } // 设置 立即调用
    )
    
  • 一次性侦听器:
    每当被侦听源发生变化时,侦听器的回调就会执行。如果希望回调只在源变化时触发一次,请使用 once: true 选项。

    watch(
      source,
      (newValue, oldValue) => {
        // 当 `source` 变化时,仅触发一次
      },
      { once: true }
    )
    
  • watchEffect()watchEffect() 允许我们自动跟踪回调的响应式依赖。
    即,watchEffect是watch的简化。watchEffect不需要明确写出依赖的响应式源,自动把回调中使用到的响应式源作为依赖源。默认就是立即执行的,所以不需要指定 immediate: true

    watchEffect(async () => {
      const response = await fetch(
        `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
      )
      data.value = await response.json()
    })
    
  • 副作用清理

  • 回调的触发时机:

    • Post Watchers
    • 同步侦听器:
  • 停止侦听器:

    setup()<script setup> 中用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。因此,在大多数情况下,你无需关心怎么停止一个侦听器。

    一个关键点是,侦听器必须用同步语句创建:如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏。如下方这个例子:

    <script setup>
    import { watchEffect } from 'vue'
    
    // 它会自动停止
    watchEffect(() => {})
    
    // ...这个则不会!
    setTimeout(() => {
      watchEffect(() => {})
    }, 100)
    </script>
    

6、模板引用:DOM引用或组件引用

<input ref="input">
  • 访问模板引用:
    要在组合式 API 中获取引用,我们可以使用辅助函数 useTemplateRef()

    <script setup>
    import { useTemplateRef, onMounted } from 'vue'
    
    // 第一个参数必须与模板中的 ref 值匹配
    const input = useTemplateRef('my-input')
    
    onMounted(() => {
      input.value.focus()
    })
    </script>
    
    <template>
      <input ref="my-input" />
    </template>
    

    vue3.5版本以前是没有useTemplateRef函数的。所以3.5以前的用法是,声明一个与模板里 ref attribute 匹配的引用:

    <script setup>
    import { ref, onMounted } from 'vue'
    
    // 声明一个 ref 来存放该元素的引用
    // 必须和模板里的 ref 同名
    const input = ref(null)
    
    onMounted(() => {
      input.value.focus()
    })
    </script>
    
    <template>
      <input ref="input" />
    </template>
    
  • v-for 中的模板引用
    当在 v-for 中使用模板引用时,对应的 ref 中包含的值是一个数组,它将在元素被挂载后包含对应整个列表的所有元素:

    <script setup>
    import { ref, useTemplateRef, onMounted } from 'vue'
    
    const list = ref([
      /* ... */
    ])
    
    const itemRefs = useTemplateRef('items')
    
    onMounted(() => console.log(itemRefs.value))
    </script>
    
    <template>
      <ul>
        <li v-for="item in list" ref="items">
          {{ item }}
        </li>
      </ul>
    </template>
    
  • 函数模板引用

    <input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">
    
    • ref还可以绑定为一个函数,会在每次组件更新时都被调用。该函数会收到元素引用作为其第一个参数。
    • 当绑定的元素被卸载时,函数也会被调用一次,此时的 el 参数会是 null。你当然也可以绑定一个组件方法而不是内联函数。
  • 组件上的ref:
    模板引用也可以被用在一个子组件上。这种情况下引用中获得的值是组件实例

7、组件基础

  • 传递 props
  • 监听事件
  • 通过插槽来分配内容
  • 动态组件

8、生命周期

二、深入组件

2.1、组件注册

  • 全局注册:

    我们可以使用 Vue 应用实例.component() 方法,让组件在当前 Vue 应用中全局可用。

    app.component(
      // 注册的名字
      'MyComponent',
      // 组件的实现
      {
        /* ... */
      }
    )
    

    如果是单文件组件,可以注册被导入的.vue文件:

    import MyComponent from './App.vue'
    
    app.component('MyComponent', MyComponent)
    

    .component() 方法可以被链式调用:

    app
      .component('ComponentA', ComponentA)
      .component('ComponentB', ComponentB)
      .component('ComponentC', ComponentC)
    
  • 局部注册:
    在使用 <script setup> 的单文件组件中,导入的组件可以直接在模板中使用,无需注册:

    <script setup>
    import ComponentA from './ComponentA.vue'
    </script>
    
    <template>
      <ComponentA />
    </template>
    

    如果没有使用 <script setup>,则需要使用 components 选项来显式注册:

    import ComponentA from './ComponentA.js'
    
    export default {
      components: {
        ComponentA
      },
      setup() {
        // ...
      }
    }
    

2.2、Props

  • Props声明:
    在使用 <script setup> 的单文件组件中,props 可以使用 defineProps() 宏来声明:

    <script setup>
    const props = defineProps(['foo'])
    
    console.log(props.foo)
    </script>
    

    在没有使用 <script setup> 的组件中,props 可以使用 props 选项来声明:

    export default {
      props: ['foo'],
      setup(props) {
        // setup() 接收 props 作为第一个参数
        console.log(props.foo)
      }
    }
    

    注意传递给 defineProps() 的参数和提供给 props 选项的值是相同的,两种声明方式背后其实使用的都是 props 选项。

  • 响应式 Props 解构:

    const { foo } = defineProps(['foo'])
    
    watchEffect(() => {
      // 在 3.5 之前只运行一次
      // 在 3.5+ 中在 "foo" prop 变化时重新执行
      console.log(foo)
    })
    

    可以使用 JavaScript 原生的默认值语法声明 props 默认值。这在使用基于类型的 props 声明时特别有用。

    const { foo = 'hello' } = defineProps<{ foo?: string }>()
    

    难点:这块了解有点 费劲,下次再补充下

  • 传递 prop 的细节

2.3、组件事件

  • 触发与监听事件:同vue2的

    <button @click="$emit('someEvent')">Click Me</button>
    
  • 事件参数:同vue2的

  • 声明触发的事件:
    我们在 <template> 中使用的 $emit 方法不能在组件的 <script setup> 部分中使用,但 defineEmits() 会返回一个相同作用的函数供我们使用:

    <script setup>
    const emit = defineEmits(['inFocus', 'submit'])
    
    function buttonClick() {
      emit('submit')
    }
    </script>
    

    显式地使用了 setup 函数而不是 <script setup>,则事件需要通过 emits 选项来定义,emit 函数也被暴露在 setup() 的上下文对象上:

    export default {
      emits: ['inFocus', 'submit'],
      setup(props, ctx) {
        ctx.emit('submit')
      }
    }
    

    说明:$emit可以在模板中直接使用,但在setup中不能直接使用,需要先声明。

  • 事件校验:
    和对 props 添加类型校验的方式类似,所有触发的事件也可以使用对象形式来描述。

2.4、组件 v-model

v-model 可以在组件上使用以实现双向绑定。

  • 基本用法:
    从 Vue 3.4 开始,推荐的实现方式是使用 defineModel() 宏:

    <!-- Child.vue -->
    <script setup>
    const model = defineModel()
    
    function update() {
      model.value++
    }
    </script>
    
    <template>
      <div>Parent bound v-model is: {{ model }}</div>
      <button @click="update">Increment</button>
    </template>
    
    <!-- Parent.vue -->
    <Child v-model="countModel" />
    
  • v-model 的参数:

posted @ 2025-05-27 15:13  吴飞ff  阅读(43)  评论(0)    收藏  举报