美文网首页IT 全栈开发
vue3 知识点 和 对比 部分总结, 更新中。。。。。

vue3 知识点 和 对比 部分总结, 更新中。。。。。

作者: 醋留香 | 来源:发表于2022-09-28 11:46 被阅读0次

    1. node 环境 > 16

    2. 给 VS code 安装 Volar 扩展插件

    3. 整个应用程序的app暴露出一个全局配置选项

    const app = createApp(App)
    
    // 应用实例会暴露一个 .config 对象允许我们配置一些应用级的选项
    app.config.errorHandler = (err)=> {
        console.log("程序的错误信息。。。")
        console.log(err)
    }
    
    app.mount("#app")
    

    4. 整个应用程序的app暴露出一个全局组件注册方法

    const app = createApp(App)
    
    // 应用实例还提供了一些方法来注册应用范围内可用的资源,例如注册一个组件:
    // app.component('全局组件名称', 一个全局组件)
    
    app.mount("#app")
    

    5. 给整个应用程序添加全局数据

    // 添加全局数据
    app.config.globalProperties = {
        sx: 777
    }
    
    // 接下来在组件的模板中可直接获取使用 
    <div>
            {{sx}}
    </div>
    
    // 在组件脚本中, 需要先获取全局实例对象
    <script setup lang="ts">
        import { getCurrentInstance } from 'vue'
        console.log(getCurrentInstance())
    </script>
    
    20220928095455148.png

    6. 创建一个响应式的 对象 或 数组

    // 注意: reactive() 返回的是一个原始对象的 Proxy
    const raw = {}
    const proxy = reactive(raw)
    只有代理对象是响应式的,更改原始对象不会触发更新。
    
    为保证访问代理的一致性,
    对同一个原始对象调用 reactive() 会总是返回同样的代理对象,
    而对一个已存在的代理对象调用 reactive() 会返回其本身:
    
    // 在同一个对象上调用 reactive() 会返回相同的代理
    console.log(reactive(raw) === proxy) // true
    
    // 在一个代理上调用 reactive() 会返回它自己
    console.log(reactive(proxy) === proxy) // true
    
    这个规则对嵌套对象也适用。依靠深层响应性,响应式对象内的嵌套对象依然是代理:
    const proxy = reactive({})
    
    const raw = {}
    proxy.nested = raw
    
    console.log(proxy.nested === raw) // false
    
    因为 Vue 的响应式系统是通过属性访问进行追踪的,
    因此我们必须始终保持对该响应式对象的相同引用。
    这意味着我们不可以随意地“替换”一个响应式对象,
    因为这将导致对初始引用的响应性连接丢失:
    let state = reactive({ count: 0 })
    // 上面的引用 ({ count: 0 }) 将不再被追踪(响应性连接已丢失!)
    state = reactive({ count: 1 })
    
    // n 是一个局部变量,同 state.count
    // 失去响应性连接
    let n = state.count
    // 不影响原始的 state
    n++
    
    // count 也和 state.count 失去了响应性连接
    let { count } = state
    // 不会影响原始的 state
    count++
    
    // 该函数接收一个普通数字,并且
    // 将无法跟踪 state.count 的变化
    callSomeFunction(state.count)
    
    // 组合式 api
    <script setup lang="ts">
    
        // 创建一个响应式的 对象 或 数组
        import { reactive } from 'vue'
        const state = reactive({ name: 0 })
    
        function zengjia(cc: any) {
            state.name = cc
        }
    </script>
    
    // 选项式 api
    <script>
    import { reactive } from '_vue@3.2.39@vue'
    
        export default {
            setup() {
                const state = reactive({name: "张三"})
                function zengjia(cc) {
                    state.name = cc
                }
    
                return {
                    state,
                    zengjia
                }
            }
        }
    
    </script>
    

    7. nextTick() 等 编程了 全局api

    import { nextTick } from 'vue'
    

    8. 深层响应式对象 & 浅层响应式对象

    9. 响应式变量

    Vue 提供了一个 ref() 方法来允许我们创建可以使用任何值类型的响应式 ref:
    import { ref } from 'vue'
    
    const count = ref(0)
    
    ref() 将传入参数的值包装为一个带 .value 属性的 ref 对象
    当值为对象类型时,会用 reactive() 自动转换它的 .value。
    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的解包
    ref在模板中解包
    当 ref 在模板中作为顶层属性被访问时,它们会被自动“解包”,所以不需要使用 .value
    <button @click="increment">
        {{ count }} <!-- 无需 .value -->
      </button>
      
    function increment() {
      count.value++ // 需要.value
    }
    
    ref 在响应式对象中的解包
    当一个 ref 被嵌套在一个响应式对象中,作为属性被访问或更改时,它会自动解包,因此会表现得和一般的属性一样
    
    数组和集合类型的 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)
    

    10. 计算属性

    <script setup>
    import { reactive, computed } from 'vue'
    
    const xxx = computed(() => {
      return 数据 ? '111' : '222'
    })
    </script>
    
    <template>
      <span>{{ xxx }}</span>
    </template>
    

    computed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 xxx.value 访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value

    11. 计算属性 & 方法

    计算属性值会基于其响应式依赖被缓存

    一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 依赖的响应式数据 不改变,无论多少次访问 计算属性 都会立即返回先前的计算结果,而不用重复执行 getter 函数。

    方法调用总是会在重渲染发生时再次执行函数。

    像下面的计算属性永远不会更新, 因为 Date.now() 并不是一个响应式依赖:

    const now = computed(() => Date.now())
    

    12. 计算属性为什么要缓存

    想象一下我们有一个非常耗性能的计算属性 list,需要循环一个巨大的数组并做许多计算逻辑,并且可能也有其他计算属性依赖于 list。没有缓存的话,我们会重复执行非常多次 list 的计算函数,然而这实际上没有必要!如果你确定不需要缓存,那么也可以使用方法调用。

    13. 可写计算属性

    只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建:

    <script setup>
    import { ref, computed } from 'vue'
    
    const firstName = ref('John')
    const lastName = ref('Doe')
    
    const fullName = computed({
      // getter
      get() {
        return firstName.value + ' ' + lastName.value
      },
      // setter
      set(newValue) {
        // 注意:我们这里使用的是解构赋值语法
        [firstName.value, lastName.value] = newValue.split(' ')
      }
    })
    </script>
    

    现在当你再运行 fullName.value = 'John Doe' 时,setter 会被调用而 firstNamelastName 会随之更新。

    另注意:

    在计算属性中使用 reverse()sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:

    - return 数组.reverse()
    + return [...数组].reverse()
    

    14. 数组变化侦测

    变更方法

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

    变更: 就是改变数组内的元素, 但不会生成新的数组

    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
    • reverse()

    替换一个数组

    例如 filter()concat()slice()等,这些方法都不会更改原数组,而总是返回一个新数组

    在这种情况下, 我们需要用新生成的数组 来 替换 老的数组

    比如 :item 是一个数组的 ref
    那么: 可以这样来操作

    item.value = item.value.concat(...)
    

    其本质就是 , 保证 ref 的引用不变, 还是响应式的

    你可能认为这将导致 Vue 丢弃现有的 DOM 并重新渲染整个列表——幸运的是,情况并非如此。Vue 实现了一些巧妙的方法来最大化对 DOM 元素的重用,因此用另一个包含部分重叠对象的数组来做替换,仍会是一种非常高效的操作。

    15. 在点击事件中 访问事件对象 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>
    
    function warn(message, event) {
      // 这里可以访问原生事件
      if (event) {
        event.preventDefault()
      }
      alert(message)
    }
    

    16. 生命周期

    lifecycle.16e4c08e.png

    17. 监听器 watch() 和 watchEffect()

    watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。
    默认情况是惰性的,也就是说仅在侦听的源数据变更时才执行回调。

    <script setup>
    import { ref, watch } from 'vue'
      
      // 响应式
      // 响应式对象
      // 响应式变量
    
    const aaa = ref(url)
    const bbb = ref(...)
    
    // 可以直接侦听一个 ref
    watch(aaa, async (新值, 老值) => {
      // 做一些处理
        // 也可以在次进行其他ref的修改
      // 也可以执行异步函数
    })
    </script>
    

    你不能直接侦听响应式对象的属性值

    const obj = reactive({ count: 0 })
    
    // 错误,因为 watch() 得到的参数是一个 number
    watch(obj.count, (count) => {
      console.log(`count is: ${count}`)
    })
    

    这里需要用一个返回该属性的 getter 函数

    
    // 提供一个 getter 函数
    watch(
      () => obj.count,
      (count) => {
        console.log(`count is: ${count}`)
      }
    )
    

    简单来说,
    侦听reactive数据

    const state = reactive({ nickname: "xiaofan", age: 20 });
    // 修改age值时会触发 watch的回调
    watch(
      () => state.age,
      (curAge, preAge) => {
        console.log("新值:", curAge, "老值:", preAge);
      }
    );
    

    侦听ref数据

    const year = ref(0);
    watch(year, (newVal, oldVal) => {
      console.log("新值:", newVal, "老值:", oldVal);
    });
    

    同时侦听多个数据

    // 当我们需要侦听多个数据源时, 可以进行合并, 同时侦听多个数据:
    watch([() => state.age, year], ([curAge, newVal], [preAge, oldVal]) => {
    console.log("新值:", curAge, "老值:", preAge); console.log("新值:", newVal,
    "老值:", oldVal); });
    

    侦听复杂的嵌套对象

    const state = reactive({
      room: {
        id: 100,
        attrs: {
          size: "140平方米",
          type: "三室两厅",
        },
      },
    });
    // 注意在此使用的 watch() 方法中的第三个参数: deep:true
    watch(
      () => state.room,
      (newType, oldType) => {
        console.log("新值:", newType, "老值:", oldType);
      },
      { deep: true }
    );
    
    //  补充: 默认情况下,watch 是惰性的, 那什么情况下不是惰性的, 可以立即执行回调函数呢?
    // 其实使用也很简单, 给第三个参数中设置immediate: true 即可。
    

    stop 停止监听
    组件中创建的watch监听,会在组件被销毁时自动停止
    如果在组件销毁之前我们想要停止掉某个监听, 可以调用watch()函数的返回值

    const xxx = watch(.....);
    
    
    setTimeout(()=>{
        // 停止监听
        xxx()
    }, 3000)
    

    和 watchEffect() 对比

    // watch已经能满足监听的需求,为什么还要有watchEffect呢
    // 还是观察对比, 找区别吧
    
    watchEffect(
         () => {
            console.log(数据1);
            console.log(数据2);
          }
    );
    

    上方代码执行结果如下:
    执行结果首先打印一次数据1数据2的值;
    然后每隔一秒,再打印数据1数据2的值
    从上面的代码可以看出, watchEffect 并没有像 watch 一样需要先传入依赖,
    watchEffect 会自动收集依赖, 只要指定一个回调函数即可。
    在组件初始化时, 会先执行一次来收集依赖,
    然后当收集到的依赖中数据发生变化时, 就会再次执行回调函数。
    所以总结对比如下:

    • watchEffect 不需要手动传入依赖
    • watchEffect 会先执行一次用来自动收集依赖
    • watchEffect 无法获取到变化前的值, 只能获取变化后的值

    18. 事件

    在组件的模板表达式中,可以直接使用 $emit 方法触发自定义事件

    <button @click="$emit('someEvent')">click me</button>
    

    可以给事件带参数

    <button @click="$emit('increaseBy', 1)">
      Increase by 1
    </button>
    

    $emit 方法不能在组件的 <script setup> 部分中
    可以显式地通过 defineEmits() 宏来声明

    <script setup>
      // defineEmits() 会返回一个相同作用的函数供我们使用
      const emit = defineEmits(['inFocus', 'submit'])
    
      function buttonClick() {
        emit('submit')
      }
    </script>
    

    defiineEmits() 宏函数使用的注意事项

    • defineEmits()不能在子函数中使用, 它必须直接放置在 <script setup> 的顶级作用域下

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

      export default {
        emits: ['inFocus', 'submit'],
        setup(props, ctx) {
          ctx.emit('submit')
        }
      }
      
    • 这个 emits 选项还支持对象语法,它允许我们对触发事件的参数进行验证:

      <script setup>
      const emit = defineEmits({
        submit: function ({ email, password }) {
          // 该函数也称为 事件校验 函数
          // 比如该 提交校验 函数
          if (email && password) {
            return true
          } else {
            console.warn('Invalid submit event payload!')
            return false
          }
          // 通过返回值为 `true` 还是为 `false` 来判断
          // 验证是否通过
        }
      })
      </script>
      
    • 如果你正在搭配 TypeScript 使用 <script setup>,也可以使用纯类型标注来声明触发的事件:

      <script setup lang="ts">
      const emit = defineEmits<{
          (e: 'change', id: number): void
          (e: 'update', value: string): void
        }>()
      </script>
      

    19. 事件 配合 v-model 来完成 组件内的 input 输入框数据绑定

    事件 配合 v-model 来完成 组件内的 input 输入框数据绑定

    <!-- CustomInput.vue -->
    <script setup>
    defineProps(['modelValue'])
    defineEmits(['update:modelValue'])
    </script>
    
    <template>
      <input
        :value="modelValue"
        @input="$emit('update:modelValue', $event.target.value)"
      />
    </template>
    
    
    <!-- 使用改 组件 -->
    <CustomInput v-model="searchText" />
    
    
    <!-- 另: 注意一下 -->
    <!-- 普通标签上 v-model 的拆分 -->
    <input v-model="searchText" />
    
    <input
      :value="searchText"
      @input="searchText = $event.target.value"
    />
    
    <!-- 组件上 v-model 的拆分 -->
    <组件
      :modelValue="searchText"
      @update:modelValue="newValue => searchText = newValue"
    />
    
    
    

    20. getter setter 配合 v-model 来完成 组件内的 input 输入框数据绑定

    <!-- CustomInput.vue -->
    <script setup>
    import { computed } from 'vue'
    
    const props = defineProps(['modelValue'])
    const emit = defineEmits(['update:modelValue'])
    
    const value = computed({
      get() {
        return props.modelValue
      },
      set(value) {
        emit('update:modelValue', value)
      }
    })
    </script>
    
    <template>
      <input v-model="value" />
    </template>
    
    <MyComponent v-model:title="bookTitle" />
    

    21. 依赖注入(vue3, 所以只能出现在 setup 中)

    provide-inject.3e0505e4.png
    • 数据提供者
    // 在组件内使用
    import { provide } from 'vue'
    provide(名称 , 值)
    
    // 给整个app使用
    app.provide(名称 , 值)
    
    // 值 也可以是 响应式的值, 
    // 使后代组件可以由此和提供者建立响应式的联系
    // 一个组件可以多次调用 provide(),
    // 使用不同的注入名,注入不同的依赖值。
    
    // 给值套上 readonly, 来不让注入方修改值
    app.provide(名称 , readonly(值))
    
    
    • 数组注入
    // 使用 inject() 函数
    const aaa = inject('名称')
    
    // 没有提供者的时候, 可以使用默认值
    // 如果没有祖先组件提供 "message"
    // `value` 会是 "这是默认值"
    const value = inject('名称', '这是默认值')
    
    // 默认值 也可以是一个工厂函数
    const value = inject('名称', ()=>{。。。。})
    
    
    

    22. 异步组件加载

    
    // 异步组件 的 局部注册
    import { defineAsyncComponent } from 'vue'
    
    const 异步组件 = defineAsyncComponent(() => {
      
      // 情况1:
      return new Promise((resolve, reject) => {
        // ...从服务器获取组件
        resolve(/* 获取到的组件 */)
      })
      
      // 情况2: 
      return import('./组件.vue')
      
      
    })
    
    // 异步组件 的 全局注册
    app.component('异步组件', defineAsyncComponent(() =>
      import('./组件.vue')
    ))
    
    // 附加属性或功能
    const AsyncComp = defineAsyncComponent({
      // 加载函数
      loader: () => import('./Foo.vue'),
    
      // 加载异步组件时使用的组件
      loadingComponent: LoadingComponent,
      // 展示加载组件前的延迟时间,默认为 200ms
      delay: 200,
    
      // 加载失败后展示的组件
      errorComponent: ErrorComponent,
      // 如果提供了一个 timeout 时间限制,并超时了
      // 也会显示这里配置的报错组件,默认值是:Infinity
      timeout: 3000
    })
    
    // ***也可以搭配 <Suspense> 组件
    
    
    

    23. 内部组件 --- <Suspense>

    <Suspense>让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,
    并可以在等待时渲染一个加载状态。

    <Suspense> 可以等待的异步依赖有两种

    1. 带有异步 setup() 钩子的组件。这也包含了使用 <script setup> 时有顶层 await 表达式的组件。

      export default {
        async setup() {
          const res = await fetch(...)
          const posts = await res.json()
          return {
            posts
          }
        }
      }
      
    2. 异步组件

      /*
      异步组件默认就是“suspensible”的。
      这意味着如果组件关系链上有一个 <Suspense 菊花>,
      那么这个异步组件就会被当作这个 <Suspense> 的一个异步依赖。
      
      在这种情况下,加载状态是由 <Suspense> 控制,
      而该组件自己的加载、报错、延时和超时等选项都将被忽略。
      
      异步组件也可以通过在选项中指定 suspensible: false 表明不用 Suspense 控制,
      并让组件始终自己控制其加载状态。
      */
      
      // 在 <script setup>中,顶层 await 表达式会自动让该组件成为一个异步依赖:
      <script setup>
      const res = await fetch(...)
      const posts = await res.json()
      </script>
      
      <template>
        {{ posts }}
      </template>
      

    <Suspense> 组将用法:

    <Suspense>
      <!-- 具有深层异步依赖的组件 -->
      <Dashboard />
    
      <!-- 在 #fallback 插槽中显示 “正在加载中” -->
      <template #fallback>
        Loading...
      </template>
    </Suspense>
    

    <Suspense> 组件有两个插槽:#default#fallback

    #default#fallback 中都只能接受一个根元素

    在可能的时候都将显示默认槽中的节点。否则将显示后备槽中的节点。

    <Suspense> 组件会触发三个事件:pendingresolvefallback

    我们常常会将 <Suspense><Transition><Keeplive><RouterView> 等组件结合。
    要保证这些组件都能正常工作,嵌套的顺序非常重要。

    <RouterView v-slot="{ Component }">
      <template v-if="Component">
        <Transition mode="out-in">
          <KeepAlive>
            <Suspense>
              <!-- 主要内容 -->
              <component :is="Component"></component>
    
              <!-- 加载中状态 -->
              <template #fallback>
                正在加载...
              </template>
            </Suspense>
          </KeepAlive>
        </Transition>
      </template>
    </RouterView>
    

    24. ctx 属性

    vue3.x 开发环境之下, 可以看到$router$store、声明的变量和方法等
    但 在 vue3.x 生产换件之下, 生产环境的ctx$router$store没有了,其他属性也都没有了,不能通过ctx.$routerctx.$store访问router和store,因此ctx可以说对我们没有用,应该避免在代码中使用ctx

    所以, : 对于网上一些其他文档使用ctx.$routerctx.$store访问router和store的应该小心避坑,注意开发环境和生产环境的差别

    25. 关于生命周期中的一些区别:

    执行顺序方面:

    3中的setup相当于2中的beforeCreate和created的合并。
    vue3.x中会先执行`setup`方法,再执行兼容2.x的其他方法,比如`data`、`computed`、`watch`等,
    并且在`setup`执行过程中,无法访问`data`中定义的属性,因为此时还未执行到`data`方法
    又并且,setup 函数只走一遍
    

    mount挂载的区别:

    • 2.x会使用挂载元素的outerHTML作为template,并替换挂载元素
    • 3.x会使用挂载元素的innerHTML作为template,并且只替换挂载元素的子元素

    26. 关于元素或组件引用的问题

    2.x可以在组件挂载之后通过this.$el访问组件根元素
    3.x中没有this了, 一切都发生变化了:

    3.x去掉了this,但支持了Fragment,所以this.$el没有存在的意义,建议通过refs访问DOM
    在组合式 api 中, reactive refstemplate refs 的概念已经是统一的。为了获得对模板内元素或组件实例的引用,我们可以像往常一样在 setup() 中声明一个 ref 并返回它

    <template>
      <div ref="root"></div>
    </template>
    
    <script>
      import { ref, onMounted, getCurrentInstance } from 'vue'
    
      export default {
        setup() {
          const vm = getCurrentInstance()
          const root = ref(null)
    
          onMounted(() => {
            // 在渲染完成后, 这个 div DOM 会被赋值给 root ref 对象
            console.log(root.value) // <div/>
            console.log(vm.refs.root) // <div/>
            console.log(root.value === vm.refs.root) // true
          })
    
          return {
            root
          }
        }
      }
    </script>
    

    在 v-for 中 使用 refs

    <template>
      <div v-for="(item, i) in list" :key="i" :ref="el => { divs[i] = el }">
        {{ item }}
      </div>
    </template>
    <script>
      import { ref, reactive, onBeforeUpdate } from 'vue'
      export default {
        setup() {
          const list = reactive([1, 2, 3])
          const divs = ref([])
    
          // 确保在每次变更之前重置引用
          onBeforeUpdate(() => {
            divs.value = []
          })
    
          return {
            list,
            divs
          }
        }
      }
    </script>
    

    27. 自定义指令 directive 的改动

    // vue2.x
    export default {
      name: 'YourDirectiveName',
      bind(el, binding, vnode, oldVnode) {},
      inserted(...) {},
      update(...) {},
      componentUpdated(...) {},
      unbind(...) {}
    }
    
    // vue3.x
    export default {
      beforeMount(el, binding, vnode, oldVnode) {},
      mounted(...) {},
      beforeUpdate(...) {},
      updated(...) {},
      beforeUnmount(...) {},
      unmounted() {...}
    }
    

    28. 对 render 方法的 改动

    vue、react都提供了render方法渲染html模板,直接使用render方法的还是比较少,毕竟有templateJSX,对于确实需要自定义render方法渲染模板内容的,具体变动如下:

    // vue2.x
    export default {
      render(h) {
        return h('div')
      }
    }
    
    // vue3.x
    import { h } from 'vue'
    export default {
      render() {
        return h('div')
      }
    }
    

    29. 对事件总线 EventBus 的改动

    在Vue2.x中可以通过EventBus的方法来实现组件通信

    // 声明实例
    var EventBus = new Vue()
    Vue.prototype.$globalBus = EventBus
    // 组件内调用
    this.$globalBus.$on('my-event-name', callback)
    this.$globalBus.$emit('my-event-name', data)
    

    在vue3.x中移除了 $on$off等方法,而是推荐使用mitt方案来代替:

    // 声明实例
    import mitt from 'mitt'
    const emitter = mitt()
    
    
    // 组件内调用
    // 监听所有事件
    emitter.on('*', (type, e) => console.log(type, e))
    
    // 监听个别事件
    emitter.on('my-event-name', callback)
    emitter.emit('my-event-name', data)
    
    
    // 清除所有事件
    emitter.all.clear()
    

    30. 状态管理---vuex的区别

    创建

    // Vue2
    export default new Vuex.Store({
      state: {
        count:1
      },
      mutations: {
        inc(state){
          state.count ++ 
        }
      },
      actions: {
      },
      modules: {
      }
    })
    
    // Vue3
    export default Vuex.createStore({
      state: {
        count:1
      },
      mutations: {
        add(state){
          state.count ++ 
        }
      },
      actions: {
      },
      modules: {
      }
    });
    

    使用

    // Vue2
    export default {
      name: "Home",
      data() {
        return {
            state: this.$store.state
        };
      },
      computed: {
        double() {
          return this.$store.state.count * 2;
        },
      },
      methods: {
        add() {
          this.$store.commit("add");
        }
      }
    };
    // Vue3
    import { computed,  reactive } from "vue";
    import { useStore } from "vuex";
    export default {
      setup() {
        const store = useStore()
        const state = store.state
        const double = computed(() => store.state.count * 2)
    
        const add = () => {
          store.commit("add");
        };
        return { state, add ,double};
      }
    };
    

    31. 在vue3中自定义 hook 函数

    类似的, 钩子函数以 use 开头
    创建 usePerosn.js 文件

    import { computed, reactive } from "vue"
    
    export default function (per = {name: "zhangsan", age:12}) {
        
        const ren = reactive(per)
        const changeName = (name) => {
            ren.name = name
        }
        const zengjiaAge = (num) => {
            ren.age = ren.age + num
        }
        const getBirth = computed(()=> {
            return 2022 - ren.age
        })
        return {
            ren , 
            changeName,
            zengjiaAge,
            getBirth
        }
    }
    

    在组件中 使用 该 person 钩子

    <template>
        <div>
            ren的信息{{JSON.stringify(ren)}}
            
            <button @click="changeName('wangwu')">修改名称</button>
            <button @click="zengjiaAge(2)">增加年龄</button>
            <hr>
            {{getBirth}}年出生的
        </div>
    </template>
    
    <script setup>
        import usePerson from "./usePerson"
    
        const {ren , changeName , zengjiaAge , getBirth} = usePerson();
    
    </script>
    

    32. teleport , 将其所包含的组件的层级结构显示到特定的 地方

    // 比如说组件A这样定义
    <template>
      <teleport to="#任何一个div的id>
        <div class="dialog">
          比如说弹框组件
        </div>
      </teleport>
    </template>
    
    // 加入home界面要使用组件A这个弹框
    // home界面
    <A v-if="is_show" />
    
    // 这样就是: 虽然home界面通过数据控制了弹框组件A,
    // 但其实该弹框组件A是展示在 其他DIV中的, 而不是 home 界面里面
    

    相关文章

      网友评论

        本文标题:vue3 知识点 和 对比 部分总结, 更新中。。。。。

        本文链接:https://www.haomeiwen.com/subject/xclcartx.html