vue3学习手册

news2025/1/20 5:49:29

vue3

  • 1.认识vue3
    • 1.1了解相关信息
    • 1.2 性能提升:
    • 1.3 新增特性
    • 1.4 使用 vue-cli 创建vue项目
    • 1.5 使用 vite 创建
  • 2.全局api
    • 2.1 createApp()
    • 2.2 app.mount()
    • 2.3 app.unmount()
    • 2.4 app.provide()
    • 2.5 app.component()
    • 2.6 app.use()
    • 2.7 app.version
    • 2.8 app.config
    • 2.9 app.config.errorHandler
    • 3.0 app.config.warnHandler
  • 3.setup()
    • 3.1 setup
    • 3.2 ref
    • 3.3 访问 Props
    • 3.4 Setup 上下文
    • 3.5 暴露公共属性
    • 3.6 reactive 引用类型数据响应式
    • 3.7 vue2的响应式
    • 3.8 Vue3的响应式
    • 3.9 setup细节
    • 3.10 reactive与ref-细节
    • 3.11 readonly()
  • 4.计算属性与监视
    • 4.1计算属性
    • 4.2监视
    • 4.3 watchEffect函数
  • 5.vue3生命周期
  • 6.新组件
    • 6.1 Fragment(片断)
    • 6.2 Teleport(瞬移)
    • 6.3 Suspense(不确定的)
    • 6.4 Transition
    • 6.5 KeepAlive
      • 6.5.1 最大缓存实例数
      • 6.5.2 包含/排除
      • 6.5.3 缓存实例的生命周期

1.认识vue3

1.1了解相关信息

  • Vue.js 3.0 “One Piece” 正式版在今年9月份发布
  • 2年多开发, 100+位贡献者, 2600+次提交, 600+次PR
  • Vue3支持vue2的大多数特性
  • 更好的支持Typescript

1.2 性能提升:

  • 打包大小减少41%
  • 初次渲染快55%, 更新渲染快133%
  • 内存减少54%
  • 使用Proxy代替defineProperty实现数据响应式
  • 重写虚拟DOM的实现和Tree-Shaking

1.3 新增特性

  • Composition (组合) API
  • setup
    • ref 和 reactive
    • computed 和 watch
    • 新的生命周期函数
    • provide与inject
  • 新组件
    • Fragment - 文档碎片
    • Teleport - 瞬移组件的位置
    • Suspense - 异步加载组件的loading界面
  • 其它API更新
    • 全局API的修改
    • 将原来的全局API转移到应用对象
    • 模板语法变化

1.4 使用 vue-cli 创建vue项目

文档: https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

## 安装或者升级
npm install -g @vue/cli
## 保证 vue cli 版本在 4.5.0 以上
vue --version
## 创建项目
vue create my-project

接下来的步骤:

Please pick a preset - 选择 Manually select features
Check the features needed for your project - 选择上 TypeScript ,特别注意点空格是选择,点回车是下一步
Choose a version of Vue.js that you want to start the project with - 选择 3.x (Preview)
Use class-style component syntax - 直接回车
Use Babel alongside TypeScript - 直接回车
Pick a linter / formatter config - 直接回车
Use history mode for router? - 直接回车
Pick a linter / formatter config - 直接回车
Pick additional lint features - 直接回车
Where do you prefer placing config for Babel, ESLint, etc.? - 直接回车
Save this as a preset for future projects? - 直接回车

1.5 使用 vite 创建

  • 文档: https://v3.cn.vuejs.org/guide/installation.html
  • vite 是一个由原生 ESM 驱动的 Web 开发构建工具。在开发环境下基于浏览器原生 ES imports 开发,
  • 它做到了本地快速开发启动, 在生产环境下基于 Rollup 打包。
    • 快速的冷启动,不需要等待打包操作;
    • 即时的热模块更新,替换性能和模块数量的解耦让更新飞起;
    • 真正的按需编译,不再等待整个应用编译完成,这是一个巨大的改变。
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev

2.全局api

2.1 createApp()

创建一个应用实例。

创建一个应用实例。

  • 类型

    function createApp(rootComponent: Component, rootProps?: object): App
    
  • 详细信息

    第一个参数是根组件。第二个参数可选,它是要传递给根组件的 props。

  • 示例

    可以直接内联根组件:

    import { createApp } from 'vue'
    import App from './App.vue'
    const app = createApp(App)
    

2.2 app.mount()

将应用实例挂载在一个容器元素中。

  • 类型

    interface App {
      mount(rootContainer: Element | string): ComponentPublicInstance
    }
    
  • 详细信息

    参数可以是一个实际的 DOM 元素或一个 CSS 选择器 (使用第一个匹配到的元素)。返回根组件的实例。

    如果该组件有模板或定义了渲染函数,它将替换容器内所有现存的 DOM 节点。否则在运行时编译器可用的情况下,容器元素的 innerHTML 将被用作模板。

    在 SSR 激活模式下,它将激活容器内现有的 DOM 节点。如果出现了激活不匹配,那么现有的 DOM 节点将会被修改以匹配客户端的实际渲染结果。

    对于每个应用实例, mount() 仅能调用一次。

  • 示例

    import { createApp } from 'vue'
    const app = createApp(/* ... */)
    
    app.mount('#app')
    

    也可以挂载到一个实际的 DOM 元素。

    app.mount(document.body.firstChild)
    

2.3 app.unmount()

卸载一个已挂载的应用实例。卸载一个应用会触发该应用组件树内所有组件的卸载生命周期钩子。

  • 类型

    interface App {
      unmount(): void
    }
    

2.4 app.provide()

提供一个值,可以在应用中的所有后代组件中注入使用。

  • 类型

    interface App {
      provide<T>(key: InjectionKey<T> | symbol | string, value: T): this
    }
    
  • 详细信息

    第一个参数应当是注入的 key,第二个参数则是提供的值。返回应用实例本身。

  • 示例

    import { createApp } from 'vue'
    
    const app = createApp(/* ... */)
    
    app.provide('message', 'hello')
    

    在应用的某个组件中:

    export default {
      inject: ['message'],
      created() {
        console.log(this.message) // 'hello'
      }
    }
    

2.5 app.component()

如果同时传递一个组件名字符串及其定义,则注册一个全局组件;如果只传递一个名字,则会返回用该名字注册组件(如果存在的话)。

  • 类型

    interface App {
      component(name: string): Component | undefined
      component(name: string, component: Component): this
    }
    
  • 示例

    import { createApp } from 'vue'
    
    const app = createApp({})
    
    // 注册一个选项对象
    app.component('my-component', {
      /* ... */
    })
    
    // 得到一个已注册的组件
    const MyComponent = app.component('my-component')
    

    可以在别的模板中直接使用注册到全局的组件

2.6 app.use()

安装一个插件。

  • 类型

    interface App {
      use(plugin: Plugin, ...options: any[]): this
    }
    
  • 详细信息

    第一个参数应是插件本身,可选的第二个参数是要传递给插件的选项。

    插件可以是一个带 install() 方法的对象,亦或直接是一个将被用作 install() 方法的函数。插件选项 (app.use() 的第二个参数) 将会传递给插件的 install() 方法。

    app.use() 对同一个插件多次调用,该插件只会被安装一次。

  • 示例

    import { createApp } from 'vue'
    import MyPlugin from './plugins/MyPlugin'
    
    const app = createApp({
      /* ... */
    })
    
    app.use(MyPlugin)
    

2.7 app.version

提供当前应用所使用的 Vue 版本号。这在插件中很有用,因为可能需要根据不同的 Vue 版本执行不同的逻辑。

  • 类型

    interface App {
      version: string
    }
    
  • 示例

    在一个插件中对版本作判断:

    import { version } from 'vue'
    
    console.log(version)
    
    

2.8 app.config

每个应用实例都会暴露一个 config 对象,其中包含了对这个应用的配置设定。你可以在挂载应用前更改这些属性 (下面列举了每个属性的对应文档)。

import { createApp } from 'vue'

const app = createApp(/* ... */)

console.log(app.config)

2.9 app.config.errorHandler

用于为应用内抛出的未捕获错误指定一个全局处理函数。

  • 类型

    interface AppConfig {
      errorHandler?: (
        err: unknown,
        instance: ComponentPublicInstance | null,
        // `info` 是一个 Vue 特定的错误信息
        // 例如:错误是在哪个生命周期的钩子上抛出的
        info: string
      ) => void
    }
    
  • 详细信息

    错误处理器接收三个参数:错误对象、触发该错误的组件实例和一个指出错误来源类型信息的字符串。

    它可以从下面这些来源中捕获错误:

    • 组件渲染器
    • 事件处理器
    • 生命周期钩子
    • setup() 函数
    • 侦听器
    • 自定义指令钩子
    • 过渡 (Transition) 钩子
  • 示例

    app.config.errorHandler = (err, instance, info) => {
      // 处理错误,例如:报告给一个服务
    }
    

3.0 app.config.warnHandler

用于为 Vue 的运行时警告指定一个自定义处理函数。

  • 类型

    interface AppConfig {
      warnHandler?: (
        msg: string,
        instance: ComponentPublicInstance | null,
        trace: string
      ) => void
    }
    
  • 详细信息

    警告处理器将接受警告信息作为其第一个参数,来源组件实例为第二个参数,以及组件追踪字符串作为第三个参数。

    这可以用户过滤筛选特定的警告信息,降低控制台输出的冗余。所有的 Vue 警告都需要在开发阶段得到解决,因此仅建议在调试期间选取部分特定警告,并且应该在调试完成之后立刻移除。

3.setup()

3.1 setup

  • 新的option, 所有的组合API函数都在此使用, 只在初始化时执行一次
  • 函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h1>名字{{ age }}</h1>
    <h1>年龄{{ name }}</h1>
    <button @click="showMessage">showMessage</button>
    <MessageComponent></MessageComponent>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  setup(){
    let  name = 'admin';
    let age = 20;
    let  showMessage = ()=>{
      alert(`我是${name},今年${age}岁了`)
    }
    return {name,age,showMessage};
  }
}
</script>

<style scoped>

</style>

  • 可以返回一个渲染函数,渲染函数渲染的结果会覆盖掉组件中原有的元素【不常用】
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h1>名字{{ age }}</h1>
    <h1>年龄{{ name }}</h1>
    <button @click="showMessage">showMessage</button>
    <MessageComponent></MessageComponent>
  </div>
</template>

<script>
  import {h} from 'vue'

  export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  setup(){
    return ()=> h('h1','helloWorld')
  }
}
</script>

<style scoped>

</style>

3.2 ref

  • 作用: 定义一个数据的响应式
  • 语法: const xxx = ref(initValue):
    • 创建一个包含响应式数据的引用(reference)对象
    • js中操作数据: xxx.value
    • 模板中操作数据: 不需要.value
  • 一般用来定义一个基本类型的响应式数据
<template>
  <div class="hello">
    <h1>父组件:{{ msg }}</h1>
    <h1>名字:{{ age }}</h1>
    <h1>年龄:{{ name }}</h1>
    <div>行业:{{job.type}}  薪资:{{job.salary}}K</div>
    <button @click="updateData">updateData</button>
    <MessageComponent></MessageComponent>
  </div>
</template>

<script>
import  {ref} from 'vue'
  export default {
  name: 'HelloWorld',
    props: {
      msg: String
    },
  setup(props){
    let  name = ref('admin');
    let age = ref( 20);
    let job = ref({type:'java,',salary:10});
    let  updateData = ()=>{
      name.value = 'root';
      age.value = 25;
       job.value.salary =  ++job.value.salary;

    }
    let  {msg} = props;
    console.log()
    return {name,age,msg,job,updateData};
  }
}
</script>

<style scoped>

</style>

3.3 访问 Props

setup 函数的第一个参数是组件的 props。和标准的组件一致,一个 setup 函数的 props 是响应式的,并且会在传入新的 props 时同步更新。

export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}

请注意如果你解构了 props 对象,解构出的变量将会丢失响应性。因此我们推荐通过 props.xxx 的形式来使用其中的 props。

3.4 Setup 上下文

传入 setup 函数的第二个参数是一个 Setup 上下文对象。上下文对象暴露了其他一些在 setup 中可能会用到的值:

export default {
  setup(props, context) {
    // 透传 Attributes(非响应式的对象,等价于 $attrs)
    console.log(context.attrs)

    // 插槽(非响应式的对象,等价于 $slots)
    console.log(context.slots)

    // 触发事件(函数,等价于 $emit)
    console.log(context.emit)

    // 暴露公共属性(函数)
    console.log(context.expose)
  }
}

该上下文对象是非响应式的,可以安全地解构:

export default {
  setup(props, { attrs, slots, emit, expose }) {
    ...
  }
}

attrsslots 都是有状态的对象,它们总是会随着组件自身的更新而更新。这意味着你应当避免解构它们,并始终通过 attrs.xslots.x 的形式使用其中的属性。此外还需注意,和 props 不同,attrsslots 的属性都不是响应式的。如果你想要基于 attrsslots 的改变来执行副作用,那么你应该在 onBeforeUpdate 生命周期钩子中编写相关逻辑。

3.5 暴露公共属性

expose 函数用于显式地限制该组件暴露出的属性,当父组件通过模板引用访问该组件的实例时,将仅能访问 expose 函数暴露出的内容:
export default {
  setup(props, { expose }) {
    // 让组件实例处于 “关闭状态”
    // 即不向父组件暴露任何东西
    expose()

    const publicCount = ref(0)
    const privateCount = ref(0)
    // 有选择地暴露局部状态
    expose({ count: publicCount })
  }
}

3.6 reactive 引用类型数据响应式

  • 作用: 定义多个数据的响应式
  • const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象
  • 响应式转换是“深层的”:会影响对象内部所有嵌套的属性
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
<template>
  <div class="hello">
    <h1>父组件:{{ msg }}</h1>
    <div>行业:{{job.type}}  薪资:{{job.salary}}K</div>
    <button @click="updateData">updateData</button>
    <MessageComponent></MessageComponent>
  </div>
</template>

<script>
import  {ref,reactive} from 'vue'
  export default {
  name: 'HelloWorld',
    props: {
      msg: String
    },
  setup(props){

    let job = reactive({type:'java,',salary:10});
    let  updateData = ()=>{
      console.log(job)

      job.salary =  ++job.salary;


    }
    let  {msg} = props;
    return {msg,job,updateData};
  }
}
</script>

<style scoped>

</style>

3.7 vue2的响应式

  • 核心:
    • 对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截)
    • 数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
Object.defineProperty(data, 'count', {
    get () {}, 
    set () {}
})
  • 问题
    • 对象直接新添加的属性或删除已有属性, 界面不会自动更新
    • 直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {}

3.8 Vue3的响应式

  • 核心:
    • 通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等…
    • 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
    • 文档:
      • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
      • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect

3.9 setup细节

  • setup执行的时机
    • 在beforeCreate之前执行(一次), 此时组件对象还没有创建
    • this是undefined, 不能通过this来访问data/computed/methods / props
    • 其实所有的composition API相关回调函数中也都不可以
  • setup的返回值
    • 一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法
    • 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性
    • 返回对象中的方法会与methods中的方法合并成功组件对象的方法
    • 如果有重名, setup优先
    • 注意:
    • 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods
    • setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据
  • setup的参数
    • setup(props, context) / setup(props, {attrs, slots, emit})
    • props: 包含props配置声明且传入了的所有属性的对象
    • attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs
    • slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
    • emit: 用来分发自定义事件的函数, 相当于 this.$emit

3.10 reactive与ref-细节

  • 是Vue3的 composition API中2个最重要的响应式API
  • ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式)
  • 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象
  • ref内部: 通过给value属性添加getter/setter来实现对数据的劫持
  • reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据
  • ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value)

3.11 readonly()

接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。

  • 类型

    function readonly<T extends object>(
      target: T
    ): DeepReadonly<UnwrapNestedRefs<T>>
    
  • 详细信息

    只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。

4.计算属性与监视

4.1计算属性

  • computed函数:
    • 与computed配置功能一致
    • 只有getter
    • 有getter和setter
  • watch函数
    • 与watch配置功能一致
    • 监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
    • 默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
    • 通过配置deep为true, 来指定深度监视
  • watchEffect函数
    • 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
    • 默认初始时就会执行第一次, 从而可以收集需要监视的数据
    • 监视数据发生变化时回调
<template>
  <h2>App</h2>
  性: <input v-model="user.firstName"/><br>
  名: <input v-model="user.lastName"/><br>
  只有getter的计算属性: <input v-model="fullName1"/><br>
  有getter与setter的计算属性: <input v-model="fullName2"><br>
</template>

<script lang="js">
  /*
  计算属性与监视
  1. computed函数:
    与computed配置功能一致
    只有getter
    有getter和setter
  2. watch函数
    与watch配置功能一致
    监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
    默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
    通过配置deep为true, 来指定深度监视
  3. watchEffect函数
    不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
    默认初始时就会执行第一次, 从而可以收集需要监视的数据
    监视数据发生变化时回调
  */

  import {
    reactive,
    computed,
  } from 'vue'

  export default {

    setup () {
      const user = reactive({
        firstName: 'admin',
        lastName: 'root'
      })

      // 只有getter的计算属性
      const fullName1 = computed(() => {
        return user.firstName + '-' + user.lastName
      })

      // 有getter与setter的计算属性
      const fullName2 = computed({
        get () {
          return user.firstName + '-' + user.lastName
        },

        set (value) {
          const names = value.split('-')
          user.firstName = names[0]
          user.lastName = names[1]
        }
      })

      return {
        user,
        fullName1,
        fullName2
      }
    }
  }
</script>

4.2监视

vue2写法:

<template>
    <h2>当前sum:{{sum}}</h2>
    <button @click="sum++">按钮</button>
</template>

<script lang="js">
    /*
    计算属性与监视
    1. computed函数:
      与computed配置功能一致
      只有getter
      有getter和setter
    2. watch函数
      与watch配置功能一致
      监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
      默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
      通过配置deep为true, 来指定深度监视
    3. watchEffect函数
      不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
      默认初始时就会执行第一次, 从而可以收集需要监视的数据
      监视数据发生变化时回调
    */

    import {
        reactive,
        computed,
        watch,
        ref
    } from 'vue'

    export default {
        name: "HelloWorld",
        // vue2写法
        watch: {
            // 简单版本
            sum(newValue, oldValue) {
                console.log('handler-sum', {newValue, oldValue})
            }

            /* // 复杂版本形式 具有配置化
                        sum: {
                            // 采用一开始就监视
                            immediate: true,
                            // 开启深度监视
                            deep: true,
                            handler(newValue, oldValue) {
                                console.log('handler-sum', {newValue, oldValue})
                            }
            }*/
        },
        setup() {
            let sum = ref(0)

            return {
                sum,
            }
        }
    }
</script>

vue3写法:

监视 ref 数据

<template>
    <h2>当前sum:{{sum}}</h2>
    <button @click="sum++">按钮</button>
</template>

<script lang="js">
    /*
    计算属性与监视
    1. computed函数:
      与computed配置功能一致
      只有getter
      有getter和setter
    2. watch函数
      与watch配置功能一致
      监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
      默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
      通过配置deep为true, 来指定深度监视
    3. watchEffect函数
      不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
      默认初始时就会执行第一次, 从而可以收集需要监视的数据
      监视数据发生变化时回调
    */

    import {
        reactive,
        computed,
        watch,
        ref
    } from 'vue'

    export default {
        name: "HelloWorld",
        setup() {
            let sum = ref(0);
            let msg = ref("hello");

            // 监视:ref所有定义的一个响应式数据
            // watch(sum,(newValue,oldValue)=>{
            //     console.log("watch:",{newValue,oldValue})
            // })

            // 监视:ref所有定义的多个响应式数据
            // 如果监视的普通数据类型不需要.vlaue【ref】,如果监听的是对象类型需要.value【reactive】
            watch([sum,msg],(newValue,oldValue)=>{
                console.log("watch:[sum,msg]",{newValue:newValue.toString(),oldValue:oldValue.toString()})
                // 配置一开始就监视和深度监视
            },{immediate:true,deep:true})
            return {
                sum,
                msg
            }
        }
    }
</script>

监视 reactive 数据

<template>
    <h2>当前sum:{{person}}</h2>
    <button @click="person.name = (Date.now())">修改姓名</button>
    <button @click="person.age = (Date.now())">修改年龄</button>
    <button @click="person.job.salary++">修改薪资</button>
</template>

<script lang="js">
    /*
    计算属性与监视
    1. computed函数:
      与computed配置功能一致
      只有getter
      有getter和setter
    2. watch函数
      与watch配置功能一致
      监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
      默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
      通过配置deep为true, 来指定深度监视
    3. watchEffect函数
      不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
      默认初始时就会执行第一次, 从而可以收集需要监视的数据
      监视数据发生变化时回调
    */

    import {
        reactive,
        computed,
        watch,
        ref
    } from 'vue'

    export default {
        name: "HelloWorld",
        setup() {
            let person = reactive({name:'admin',age:20,job:{type:'java',salary:15000}});
            // 监视:watch reactive响应式数据
            // 1.无法正确获取oldValue
            // 2.强制开启了深度监视
            // watch(person,(newValue,oldValue)=>{
            //     console.log("watch:[person]",{newValue:newValue,oldValue:oldValue})
            //     // 配置一开始就监视和深度监视
            // },{immediate:true,deep:true})
            // return {
            //     person
            // }

            // 监视:watch reactive响应式数据的某个数据
            // 1.oldValue和new能正确获取到
            // watch(()=>person.age,(newValue,oldValue)=>{
            //     console.log("watch:[age]",{newValue:newValue,oldValue:oldValue})
            //     // 配置一开始就监视和深度监视
            // },{immediate:true,deep:true})
            // return {
            //     person
            // }

            // 监视:watch reactive响应式数据的某些数据
            // watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{
            //     console.log("watch:[age,name]",{newValue:newValue,oldValue:oldValue})
            //     // 配置一开始就监视和深度监视
            // },{immediate:true,deep:true})
            // return {
            //     person
            // }

            // 监视:watch reactive响应式数据的深度监视
            // 定义的deep有效
            // 无法正确获取oldValue
            watch([()=>person.job],(newValue,oldValue)=>{
                console.log("watch:[job]",JSON.stringify({newValue:newValue,oldValue:oldValue}))
                // 配置一开始就监视和深度监视
            },{immediate:true,deep:true})
            return {
                person
            }
        }
    }
</script>

4.3 watchEffect函数

  • 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
  • 默认初始时就会执行第一次, 从而可以收集需要监视的数据
  • 监视数据发生变化时回调
<template>
    <h2>当前sum:{{person}}</h2>
    <button @click="person.name = (Date.now())">修改姓名</button>
    <button @click="person.age = (Date.now())">修改年龄</button>
    <button @click="person.job.salary++">修改薪资</button>
</template>

<script lang="js">
    /*
    计算属性与监视
    1. computed函数:
      与computed配置功能一致
      只有getter
      有getter和setter
    2. watch函数
      与watch配置功能一致
      监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
      默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
      通过配置deep为true, 来指定深度监视
    3. watchEffect函数
      不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
      默认初始时就会执行第一次, 从而可以收集需要监视的数据
      监视数据发生变化时回调
    */

    import {
        reactive,
        computed,
        watchEffect,
        ref
    } from 'vue'

    export default {
        name: "HelloWorld",
        setup() {
            let person = reactive({name:'admin',age:20,job:{type:'java',salary:15000}});
            // 一开始的时候会被执行一次 然后里面监听的变量值改变的时候,该函数还会被再次执行
            // 跟react的useEffect相似
            watchEffect(()=>{
                const name = person.name;
                console.log("watchEffect回调执行");
            })
            return {
                person
            }
        }
    }
</script>

5.vue3生命周期

与 2.x 版本生命周期相对应的组合式 API

  • beforeCreate -> 使用 setup()
  • created -> 使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

新增的钩子函数

组合式 API 还提供了以下调试钩子函数:

  • onRenderTracked
  • onRenderTriggered

MainApp.vue

<template>
    <h2>ChildComponent</h2>
    <button @click="isShow=!isShow">切换</button>
    <hr>
    <Message v-if="isShow"/>
</template>

<script >
    import Message from './ChildComponent'
    export default {

        data () {
            return {
                isShow: true
            }
        },

        components: {
            Message
        }
    }
</script>

ChildComponent.vue

<template>
 <div class="about">
  <h2>msg: {{msg}}</h2>
  <hr>
  <button @click="update">更新</button>
 </div>
</template>

<script >
 import {
  ref,
  onMounted,
  onUpdated,
  onUnmounted,
  onBeforeMount,
  onBeforeUpdate,
  onBeforeUnmount
 } from "vue"

 export default {
  beforeCreate () {
   console.log('beforeCreate()')
  },

  created () {
   console.log('created')
  },

  beforeMount () {
   console.log('beforeMount')
  },

  mounted () {
   console.log('mounted')
  },

  beforeUpdate () {
   console.log('beforeUpdate')
  },

  updated () {
   console.log('updated')
  },

  beforeUnmount () {
   console.log('beforeUnmount')
  },

  unmounted () {
   console.log('unmounted')
  },


  setup() {

   const msg = ref('abc')

   const update = () => {
    msg.value += '--'
   }

   onBeforeMount(() => {
    console.log('--onBeforeMount')
   })

   onMounted(() => {
    console.log('--onMounted')
   })

   onBeforeUpdate(() => {
    console.log('--onBeforeUpdate')
   })

   onUpdated(() => {
    console.log('--onUpdated')
   })

   onBeforeUnmount(() => {
    console.log('--onBeforeUnmount')
   })

   onUnmounted(() => {
    console.log('--onUnmounted')
   })

   return {
    msg,
    update
   }
  }
 }
</script>

配置项生命周期钩子函数和组合式api生命钩子函数,尽量不要重复使用,组合式api生命钩子函数会优先于配置项生命周期钩子函数

6.新组件

6.1 Fragment(片断)

  • 在Vue2中: 组件必须有一个根标签
  • 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处: 减少标签层级, 减小内存占用

6.2 Teleport(瞬移)

  • Teleport 提供了一种干净的方法, 让组件的html在父组件界面外的特定标签(很可能是body)下插入显示

ModalButton.vue

<template>
  <button @click="modalOpen = true">
      Open full screen modal! (With teleport!)
  </button>

  <teleport to="body">
    <div v-if="modalOpen" class="modal">
      <div>
        I'm a teleported modal! 
        (My parent is "body")
        <button @click="modalOpen = false">
          Close
        </button>
      </div>
    </div>
  </teleport>
</template>

<script>
import { ref } from 'vue'
export default {
  name: 'modal-button',
  setup () {
    const modalOpen = ref(false)
    return {
      modalOpen
    }
  }
}
</script>


<style>
.modal {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  background-color: rgba(0,0,0,.5);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.modal div {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: white;
  width: 300px;
  height: 300px;
  padding: 5px;
}
</style>

App.vue

<template>
  <h2>App</h2>
  <modal-button></modal-button>
</template>

<script lang="ts">
import ModalButton from './ModalButton.vue'

export default {
  setup() {
    return {
    }
  },

  components: {
    ModalButton
  }
}
</script>

6.3 Suspense(不确定的)

  • 它们允许我们的应用程序在等待异步组件时渲染一些后备内容,可以让我们创建一个平滑的用户体验

  • App.vue

<template>
  <Suspense>
    <template v-slot:default>
      <AsyncComp/>
      <!-- <AsyncAddress/> -->
    </template>

    <template v-slot:fallback>
      <h1>LOADING...</h1>
    </template>
  </Suspense>
</template>

<script lang="ts">
/* 
异步组件 + Suspense组件
*/
// import AsyncComp from './AsyncComp.vue'
import AsyncAddress from './AsyncAddress.vue'
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => import('./AsyncComp.vue'))
export default {
  setup() {
    return {
     
    }
  },

  components: {
    AsyncComp,
    AsyncAddress
  }
}
</script>
  • AsyncComp.vue
<template>
  <h2>AsyncComp22</h2>
  <p>{{msg}}</p>
</template>

<script lang="ts">

export default {
  name: 'AsyncComp',
  setup () {
    // return new Promise((resolve, reject) => {
    //   setTimeout(() => {
    //     resolve({
    //       msg: 'abc'
    //     })
    //   }, 2000)
    // })
    return {
      msg: 'abc'
    }
  }
}
</script>
  • AsyncAddress.vue
<template>
<h2>{{data}}</h2>
</template>

<script lang="ts">
import axios from 'axios'
export default {
  async setup() {
    const result = await axios.get('/data/address.json')
    return {
      data: result.data
    }
  }
}
</script>

6.4 Transition

<Transition> 是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发:

  • v-if 所触发的切换
  • v-show 所触发的切换
  • 由特殊元素 <component> 切换的动态组件

以下是最基本用法的示例:

<button @click="show = !show">Toggle</button>
<Transition>
  <p v-if="show">hello</p>
</Transition>
/* 下面我们会解释这些 class 是做什么的 */
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
}

6.5 KeepAlive

<KeepAlive> 是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。

在组件基础章节中,我们已经介绍了通过特殊的 <component> 元素来实现动态组件的用法:

<component :is="activeComponent" />

默认情况下,一个组件实例在被替换掉后会被销毁。这会导致它丢失其中所有已变化的状态 —— 当这个组件再一次被显示时,会创建一个只带有初始状态的新实例。

6.5.1 最大缓存实例数

我们可以通过传入 max prop 来限制可被缓存的最大组件实例数。<KeepAlive> 的行为在指定了 max 后类似一个 LRU 缓存:如果缓存的实例数量即将超过指定的那个最大数量,则最久没有被访问的缓存实例将被销毁,以便为新的实例腾出空间。

<KeepAlive :max="10">
  <component :is="activeComponent" />
</KeepAlive>

6.5.2 包含/排除

<KeepAlive> 默认会缓存内部的所有组件实例,但我们可以通过 includeexclude prop 来定制该行为。这两个 prop 的值都可以是一个以英文逗号分隔的字符串、一个正则表达式,或是包含这两种类型的一个数组:

<!-- 以英文逗号分隔的字符串 -->
<KeepAlive include="a,b">
  <component :is="view" />
</KeepAlive>

<!-- 正则表达式 (需使用 `v-bind`) -->
<KeepAlive :include="/a|b/">
  <component :is="view" />
</KeepAlive>

<!-- 数组 (需使用 `v-bind`) -->
<KeepAlive :include="['a', 'b']">
  <component :is="view" />
</KeepAlive>

它会根据组件的 name 选项进行匹配,所以组件如果想要条件性地被 KeepAlive 缓存,就必须显式声明一个 name 选项。

6.5.3 缓存实例的生命周期

当一个组件实例从 DOM 上移除但因为被 <KeepAlive> 缓存而仍作为组件树的一部分时,它将变为不活跃状态而不是被卸载。当一个组件实例作为缓存树的一部分插入到 DOM 中时,它将重新被激活

一个持续存在的组件可以通过 activateddeactivated 选项来注册相应的两个状态的生命周期钩子:

export default {
  activated() {
    // 在首次挂载、
    // 以及每次从缓存中被重新插入的时候调用
  },
  deactivated() {
    // 在从 DOM 上移除、进入缓存
    // 以及组件卸载时调用
  }
}

请注意:

  • activated 在组件挂载时也会调用,并且 deactivated 在组件卸载时也会调用。
  • 这两个钩子不仅适用于 <KeepAlive> 缓存的根组件,也适用于缓存树中的后代组件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/556118.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿拉德手游服务端Centos搭建教程

阿拉德手游服务端Centos搭建教程 大家好我是艾西&#xff0c;又有几天没有更新文章了。这几天看了看还是有不少人对手游感兴趣&#xff0c;今天给大家分享一款早些年大火的pc游戏&#xff0c;现在也有手游了“阿拉德”。 你是否还记得DNF&#xff0c;一天你不小心救了赛丽亚&a…

如何基于LiveNVR实现无人机等RTMP推流转成GB28181协议级联到GB28181视频平台

1、需求介绍 目前很多移动终端设备&#xff08;如无人机等&#xff09;只支持RTMP推流输出&#xff0c;不支持GB28181协议。但是又有需要通过GB28181协议接入到视频平台的需求。比如有些大疆无人机产品不能直接注册国标平台&#xff0c;只能rtmp推流。那么&#xff0c;项目中如…

什么是5G北斗RTK差分定位系统?它有哪些优势和应用领域?

5G技术的普及和应用&#xff0c;使得物联网和智能设备的使用越来越广泛。然而&#xff0c;在实际应用过程中&#xff0c;精准的定位数据是必不可少的。北斗差分定位系统作为一项定位技术&#xff0c;受到了市场的关注。本文将对5G北斗差分定位系统进行分析&#xff0c;并比较其…

作为一名软件测试从业人员,你有弄明白你的发展方向吗?

对于软件测试从业人员来说&#xff0c;职业发展方向的清晰并不仅仅是个人规划的问题&#xff0c;更是行业发展趋势所决定的。随着信息技术的快速发展和社会的变革&#xff0c;软件行业也在不断地演化中。因此&#xff0c;了解这个行业的发展趋势&#xff0c;并且根据自身的实际…

tcp丢包的排查

丢包的排查&#xff1a; 参考资料&#xff1a;1、https://blog.csdn.net/maimang1001/article/details/121786580 2、https://blog.csdn.net/m0_67645544/article/details/124574099 1、 网卡丢包 a) ifconfig b) 查看网卡丢包统计(虚拟机看不到网卡信息)&#xff1a;eth…

【论文阅读】CatSQL: Towards Real World Natural Language to SQL Applications

【论文阅读】CatSQL: Towards Real World Natural Language to SQL Applications 文章目录 【论文阅读】CatSQL: Towards Real World Natural Language to SQL Applications1. 来源2. 介绍3. 方法介绍3.1 CatSQL模板3.2 CatSQL 查询生成3.2.1 GraPPa嵌入网络3.2.2 使用CAT解码器…

Keil Debug 逻辑分析仪使用

Keil Debug 逻辑分析仪使用 基础配置 更改对应的bebug窗口参数 两边的 Dialog DLL 更改为&#xff1a;DARMSTM.DLL两边的 Parameter &#xff08;这里的根据单片机型号更改&#xff09;更改为&#xff1a;-pSTM32F103VE 选择左边的 Use Simulator 选项。 打开Debug和其中的逻…

安卓基础巩固(二):四大组件:Activity、Service、Broadcast、Content Provider

文章目录 Activity生命周期onCreate和onStart的区别onPause和onStop的区别生命周期的变化 Activity的启动IntentBundle Activity携带参数返回Activity启动模式任务&#xff08;task&#xff09;&#xff0c;返回栈&#xff08;back stack&#xff09;Activity的四种启动模式sta…

CHB-麻省理工学院头皮脑电图数据库

数据库介绍 该数据库在波士顿儿童医院收集&#xff0c;包括患有顽固性癫痫发作的儿科受试者的脑电图记录。受试者在停用抗癫痫药物后被监测长达几天&#xff0c;以表征他们的癫痫发作并评估他们手术干预的候选资格。 数据库链接&#xff1a;https://physionet.org/content/chb…

Vue中的Ajax

目录&#xff1a; 1. Vue脚手架配置代理2.GitHub用户搜索案例3.vue-resource4.slot插槽 1.Vue脚手架配置代理 vue脚手架配置代理服务器&#xff1a; 方法一&#xff1a;在vue.config.js中添加如下配置&#xff1a; devServer:{proxy:"http://localhost:5000" …

sqlmap结合dnslog快速注入

目录 修改数据库secure_file_priv的属性值 配置dns服务器 sqlmap结合dnslog注入 实验环境&#xff1a; 攻击机&#xff1a;kail&#xff08;ip&#xff1a;192.168.125.212&#xff09; dns服务器&#xff1a;win server_2008 r2&#xff08;ip&#xff1a;192.168.125.191&…

使用rollup打包vue3+ts+vite组件并发布至npm

创建项目&#xff08;我使用的是yarn&#xff09; 使用vite创建vue3ts项目&#xff0c;这里不演示&#xff0c;自行创建 创建打包区域 src下创建rollup文件夹&#xff1b;cmd控制台进入至rollup&#xff1b;执行yarn --init&#xff0c;根据自己需求生成package.json文件&…

KY222 打印日期

1.题目&#xff1a; 2.分析&#xff1a; 见代码。 3.我的代码&#xff1a; #include <iostream> using namespace std;class Date { public:Date(int year 1): _year(year) {}inline int GetMonthDay(int year, int month) {static int arr[13] { 0, 31, 28, 31, 3…

CE做业(6)

1、判断当前磁盘剩余空间是否有20G&#xff0c;如果小于20G&#xff0c;则将报警邮件发送给管理员&#xff0c;每天检查一次磁盘剩余空间。 &#xff08;1&#xff09;剩余空间 取出剩余空间 &#xff08;1&#xff09;剩余空间 &#xff08;2&#xff09;判断是否<20 #…

自动化测试 selenium

目录 一、了解自动化测试和selenium 1. 什么是自动化测试&#xff1f;为什么要使用自动化测试&#xff1f; 2. 为什么使用selenium&#xff1f; 3. 环境部署 4. 什么是驱动&#xff1f;驱动的工作原理 5. selenium 的依赖代码 二、selenium 的基础语法 1. 元素的定位 …

二叉树的序列化(serialization)与反序列化(de-serialization)

目录 1. 概要 2. 用一维数组表示二叉树 3. python实现 3.1 二叉树节点的表示 3.2 串行化的python实现 3.3 反串行化的python实现 3.4 测试 1. 概要 本文简要介绍二叉树的序列化处理和反序列化处理及对应的python实现。 二叉树通常为了方便而以一维数组&#xff08;比如…

mysql 库的操作

文章目录 mysql 库的操作1. 创建数据库创建数据库案例 2. 字符集和校验规则查看系统默认的字符集合校验规则查看数据库支持的字符集查看数据库支持的字符集较验规则校验规则对数据库的影响 3. 操作数据库查看数据库显示创建语句修改数据库删除数据库查看数据库连接情况 mysql 库…

强化学习:值迭代和策略迭代

值迭代 通过上一章的学习&#xff0c;我们知道了贝尔曼最优方程的求解实际上分两部分&#xff0c;一是给定一个初始值 v k v_k vk​ 找到最优策略 π k 1 π_{k1} πk1​ &#xff0c;二是更新 v k 1 v_{k1} vk1​   下面&#xff0c;我们将详细剖析这个算法&#xff0…

Java字节流battle字符流

目录 Java字节流&#xff08;Byte Stream&#xff09; FileInputStream和FileOutputStream Java字符流&#xff08;Character Stream&#xff09; FileReader和FileWriter 如何在使用是区分什么时候用输出什么时候用输入 Write方法 close方法 Java中的close方法本身抛出…

基本定时器工作模式

计数和定时 BasicTimer支持8位或16位向上计数模式。当计数值大于等于比较寄存器&#xff08;CMPH、CMPL&#xff09;&#xff0c;会产生计数中断标志&#xff0c;并从自动重载寄存器&#xff08;LOADH、LOADL&#xff09;加载新的比较值。这样可以实时调整每个计数周期的计数长…