第三章、Vue3高级


目录
  • 十、Vue3的组件化(二)
    • 1、组件的拆分和嵌套
    • 2、组件的css作用域(vue-loader)
    • 3、组件的通信props和$emit
    • 4、插槽slot
  • 十一、Vue3的组件化(三)
    • 1、动态组件component的使用
    • 2、keep-alive
    • 3、异步组件和代码分包
    • 4、$refs的使用
    • 5、$parent、$root、$el
    • 6、生命周期
    • 7、组件的v-model
  • 十二、Vue3过渡&动画实现
    • 1、过渡动画
    • 2、帧动画
    • 3、动画模式mode
    • 4、animate.css
    • 5、gsap
    • 6、列表的过渡
    • 7、列表的交错过渡
  • 十三、composition api(一)
    • 1、mixin基本使用
    • 2、mixin的合并规则
    • 3、mixin全局混入
    • 4、extends
    • 5、setup参数和返回值
    • 6、setup函数源码阅读
    • 7、reactive api
    • 8、ref api
    • 9、ref浅层解包
    • 10、readonly
  • 十四、composition api(二)
    • 1、reactive判断的api
    • 2、toRefs和toRef
    • 3、ref其他的api
    • 4、customRef
    • 5、computed
    • 6、watchEffect
    • 7、setup引用元素和watchEffect执行时机
    • 8、watch
  • 十五、composition api(三)
    • 1、生命周期钩子
    • 2、provide和inject
    • 3、useCounter案例
    • 4、useTitle案例
    • 5、useScrollPosition案例
    • 6、useMousePosition案例
    • 7、useLocalStorage案例
    • 8、setup顶层编写方式
    • 9、render和h函数
    • 10、render函数实现计数器
    • 11、render组件和插槽的使用
    • 12、render函数jsx写法

十、Vue3的组件化(二)

1、组件的拆分和嵌套
* 对组件进行拆分,拆分成一个个小的组件
* 再将这些组件组合嵌套在一起,最终形成我们的应用程序
2、组件的css作用域(vue-loader)


    
3、组件的通信props和$emit
  • 概念
* 父子组件通信:
      - 父传子:通过props属性
      - 子传父:通过$emit触发事件
* 非父子组件通信方式
      - provide和inject
      - mitt全局事件总线
  • props用法

  • props命名
HTML中标签的属性名大小写不敏感,浏览器会把大写字符解释为小写字符。
所以模板中的属性名(prop)需要使用短横线分隔命名(组件名也是这样)
  • 非prop的attribute
* 非prop的attribute:当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,
  就称之为非prop的attribute
* attribute继承:当组件有单个根节点时,非prop的attribute将自动添加到根节点的attribute中




  • 自定义事件








  • provide和inject基本使用








  • 全局事件总线mitt库
# Vue3从实例中移除了$on、$off、$once方法,希望继续使用全局事件总线,要安装第三方的mitt库
npm i -S mitt
// 工具
import mitt from "mitt";

const emitter = mitt()

export default emitter








  • mitt的事件取消
// 取消emitter中所有的监听
emitter.all.clear()

// 定义一个函数
function onFoo() {}
emitter.on("foo", onFoo)// 监听
emitter.off("foo", onFoo)// 取消监听
4、插槽slot
  • 基本使用








  • 渲染作用域
- 父级模板里的所有内容都是在父级作用域中编译的
- 子模板里的所有内容都是在子作用域中编译的
  • 作用域插槽








  • 独占默认插槽的细节

十一、Vue3的组件化(三)

1、动态组件component的使用



2、keep-alive






3、异步组件和代码分包
  • webpack的代码分包
// 通过import函数导入的模块,后续webpack对其进行打包的时候就会进行分包的操作
import('./utils/index').then(res => {
    console.log(res.sum(1, 2))
})
  • 异步组件

  • 异步组件和Suspense


    
    

4、$refs的使用



5、$parent、$root、$el
* 通过组件实例的$parent来访问父组件
* 通过组件实例的$root来访问根组件
* 通过组件实例的$el来访问组件的根元素(测试发现如果是多个根元素$el为#text节点)
* 注意:在Vue3中已经移除了$children的属性
6、生命周期
  • 生命周期



  • 缓存组件的生命周期

7、组件的v-model







十二、Vue3过渡&动画实现

1、过渡动画
  • 基本使用





  • 概念
* 当插入或删除包含在transition组件中的元素时,vue将会做以下处理
      - 自动嗅探目标元素是否应用了css过渡或者动画,如果有,那么在恰当的时机添加/删除css类名
      - 如果transition组件提供了javascript钩子函数,这些钩子函数将在恰当的时机被调用
      - 如果没有找到javascript钩子并且也没有检测到css过渡/动画,
        DOM插入、删除操作将会立即执行
* 我们会发现上面提到了很多个class,事实上vue就是帮助我们在这些class之间来回切换完成的动画
      - v-enter-from:定义进入过渡的开始状态。在元素被插入之前生效,
        在元素被插入之后的下一帧移除
      - v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,
        在元素被插入之前生效,在过渡/动画完成之后移除。
        这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数
      - v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效
       (与此同时v-ente-from被移除),在过渡/动画完成之后移除
      - v-leave-from:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除
      - v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,
        在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。
        这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数
      - v-leave-to:离开过渡的结束状态。在离开过渡被触发之后下一帧生效
        (与此同时v-leave-from被删除),在过渡/动画完成之后移除
* class的name命名规则如下:
      - 如果我们使用的是一个没有name的transition,name所有的class是以v-作为默认前缀
      - 如果我们添加了一个name属性,比如,
        那么所有的class会以fade-开头
2、帧动画
  • 基本使用





  • 过渡动画和帧动画同时使用


  • 明确指定动画的时间





3、动画模式mode
  • mode


  • appear


4、animate.css
  • 配合属性name使用
# 安装
npm i -S animate.css
// 入口文件引入
import "animate.css"





  • 配合属性enter-active-class或leave-active-class使用





5、gsap
  • transition动画钩子函数


  • gsap基本使用
# 安装
npm i -S gsap





  • gsap实现数字翻牌器



6、列表的过渡
# 安装
npm i -S lodash





7、列表的交错过渡



十三、composition api(一)

1、mixin基本使用



2、mixin的合并规则
* 如果是data函数的返回值对象
    - 返回值对象默认情况下会进行合并
    - 如果data返回值对象的属性发生了冲突,那么会保留组件自身的数据
* 如果生命周期钩子函数
    - 生命周期的钩子函数会被合并到数组中,都会被调用
* 值为对象的选项,例如methods、components、directives将被合并为同一个对象
    - 比如都有methods选项,并且都定义了方法,那么它们都会生效
    - 但是如果对象的key相同,那么会取组件对象的键值对
3、mixin全局混入
import {createApp} from 'vue'
import App from './App.vue'

const app = createApp(App)

app.mixin({
    data() {
        return {
            message: ""
        }
    },
    mounted() {
        this.message = "黄婷婷"
    },
    methods: {
        clickHandler() {
            this.message = "孟美岐"
        }
    }
})

app.mount('#app')
4、extends








5、setup参数和返回值



6、setup函数源码阅读
* setup()是在解析其它组件选项之前被调用的,所以setup()内部的this的行为与其它选项
  中的this完全不同。(packages/runtime-core/src/renderer.ts:526)
7、reactive api



8、ref api



9、ref浅层解包
* template模板中直接使用ref对象,会自动解包
* 当ref对象外层包裹一层普通对象,则不会自动解包
* 当ref对象外层包裹的是reactive对象,也会自动解包
10、readonly

十四、composition api(二)

1、reactive判断的api
* isProxy
    - 检查对象是否是由reactive或readonly创建的proxy
* isReactive
    - 检查对象是否是由reactive创建的响应式代理
    - 如果该代理是readonly建的,但包裹了由reactive创建的另一个代理,它也会返回true
* isReadonly
    - 检查对象是否是由readonly创建的只读代理
* toRaw
    - 返回reactive或readonly代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用)
* shallowReactive
    - 创建一个响应式代理,它跟踪其自身property的响应性,但不执行嵌套对象的深层响应式转换
      (深层还是原生对象)
* shallowReadonly
    - 创建一个proxy,使其自身的property为只读,但不执行嵌套对象的深度只读转换
      (深层还是可读、可写的)
2、toRefs和toRef



3、ref其他的api



4、customRef



5、computed



6、watchEffect



7、setup引用元素和watchEffect执行时机



8、watch



十五、composition api(三)

1、生命周期钩子



2、provide和inject








3、useCounter案例




// useCounter.js
import {ref, computed} from "vue"

export default function () {
    const counter = ref(0)
    const doubleCounter = computed(() => counter.value * 2)

    const increment = () => counter.value++
    const decrement = () => counter.value--

    return {
        counter,
        doubleCounter,
        increment,
        decrement,
    }
}
4、useTitle案例




// useTitle.js
import {ref, watch} from "vue"

export default function (title = "姜贞羽") {
    const titleRef = ref(title)

    watch(titleRef, (newValue) => {
        document.title = newValue
    }, {
        immediate: true
    })

    return titleRef
}
5、useScrollPosition案例






// useScrollPosition.js
import {ref} from "vue"

export default function () {
    const scrollX = ref(0)
    const scrollY = ref(0)

    document.addEventListener("scroll", () => {
        scrollX.value = window.scrollX
        scrollY.value = window.scrollY
    })

    return {
        scrollX,
        scrollY
    }
}
6、useMousePosition案例






// useMousePosition.js
import {ref} from "vue"

export default function () {
    const mouseX = ref(0)
    const mouseY = ref(0)

    window.addEventListener("mousemove", (event) => {
        mouseX.value = event.pageX
        mouseY.value = event.pageY
    })

    return {
        mouseX,
        mouseY
    }
}
7、useLocalStorage案例




import {ref, watch} from "vue"

export default function (key, value) {
    const data = ref(value)

    if (value) {
        window.localStorage.setItem(key, JSON.stringify(value))
    } else {
        data.value = JSON.parse(window.localStorage.getItem(key))
    }

    watch(data, (newValue) => {
        window.localStorage.setItem(key, JSON.stringify(newValue))
    })

    return data
}
8、setup顶层编写方式








9、render和h函数




10、render函数实现计数器

11、render组件和插槽的使用




12、render函数jsx写法
  • 安装
# 测试结果不用安装,所以也不用配置
npm i @vue/babel-plugin-jsx -D
  • 配置(babel.config.js)
module.exports = {
    plugins: [
        "@vue/babel-plugin-jsx"
    ]
}
  • jsx计数器案例

  • jsx组件和插槽的使用