Vue3 Composition API

目录

1、reactive API

2、Ref API

3、readonly API

4、isProxy

5、isReactive

6、isReadonly

7、toRaw

8、shallowReactive

9、shallowReadonly

10、toRefs

11、toRef

12、unref

13、isRef

14、shallowRef

15、triggerRef

16、customRef

17、computed

 18、watchEffect 侦听

1、watchEffect 侦听的基本使用

 2、watchEffect 停止侦听

 3、watchEffect 清除副作用

19、setup 中使用ref

20、生命周期钩子

21、provide(为子孙组件提供数据) 和 inject

 22、compositionAPI 的练习

 1. opstionsAPI 和 compositionAPI的对比

2. 通过hook对compositionAPI中的选项进行抽取(useCounter案例)

 3. hook 修改title(useTitle案例)

4. 获取滚动条位置(useScrollPosition案例)

 5. 获取鼠标位置(useMousePosition 案例)

6. userLocalStorage 案例

 23、setup 顶层编写方式

24、h 函数

1. render函数 和 h函数的使用

2. 通过render函数实现计数器

3. render 函数中组件和插槽的使用

 25、自定义指令

一、案例:当某个元素挂载完成后可以自动获取焦点

二、指令的生命周期

三、自定义指令的参数和修饰符

四、hook 方式注册全局自定义指令

26、Vue 的插件 plugins

一、对象方式书写插件

二、函数方式书写插件


1、reactive API

<template>
    <div>
        <h2>{{ info.name }} - {{ info.age }}</h2>
        <button @click="changeAge">修改age</button>

        <h2>{{ reactiveInfo.counter }}</h2>
        <button @click="increment">+1</button>
    </div>
</template>

<script>
    import { reactive } from 'vue'
    export default {
        setup() {
            const info = reactive({ name: "why", age: 18 });
            const changeAge = () => {
                info.age++
            }

            const reactiveInfo = reactive({
                counter: 1
            })
            const increment = () => {
                reactiveInfo.counter++;
                console.log(reactiveInfo.counter)
            }
            return {
                info,
                changeAge,
                reactiveInfo,
                increment
            }
        }
    }
</script>

2、Ref API

<template>
    <div>
        <h2>{{ message }}</h2>

        <!-- 在template 模板中使用ref 对象,它会自动进行解包 -->
        <!-- ref 解包是浅层解包 -->
        <h2>当前计数: {{ counter }}</h2>

        <!-- 如果外层是reactive包裹的可响应式对象,是可以自动解包的 -->
        <h2>{{ reactiveInfo.counter }}</h2>
        <button @click="increment">+1</button>
    </div>
</template>

<script>
    import { ref, reactive } from 'vue'
    export default {
        props: {
            message: {
                type: String,
                required: true
            }
        },
        setup() {
            // counter 编程一个ref的可响应式的引用
            let counter = ref(100);
            
            const reactiveInfo = reactive({
                counter
            })

            const increment = () => {
                counter.value++;
                console.log(counter.value)
            }
            return {
                counter,
                reactiveInfo,
                increment
            }
            
        }
    }
</script>

3、readonly API

<template>
    <div>
        <button @click="updateState">修改状态</button>
    </div>
</template>

<script>
    import { reactive , readonly } from 'vue'
    export default {
        setup() {
            // 1. 普通对象
            const info1 = { name: "why" };
            const readonlyInfo = readonly(info1);

            // 2. 响应式对象reactive
            const info2 = reactive({
                name: "why"
            });
            const reactiveInfo = readonly(info2);

            // 3. 响应式对象ref
            const info3 = ref("why");
            const refInfo = readonly(info3);

            const updateState = () => {
                info1.name = "coderwhy";     // 可以修改
                info2.name = "coderwhy";     // 可以修改
                info3.value = "coderwhy";    // 可以修改
                readonlyInfo.name = "coderwhy";   // 只读不可修改
                reactiveInfo.name = "coderwhy";   // 只读不可修改
                refInfo.value = "coderwhy"        // 只读不可修改   
            }
            return {
                updateState
            }
        }
    }
</script>

4、isProxy

检查对象是否是由reactive 或 readonly 创建的proxy

5、isReactive

检查对象是否是由reactive 创建的响应式代理

如果该代理是readonly 创建的,但包裹了有reactive 创建的另一个代理,它也会返回true

6、isReadonly

检查对象是否是由readonly 创建的只读代理

7、toRaw

返回reactive 或 readonly 代理的原始对象

8、shallowReactive

创建一个响应式代理,它跟踪其自身property 的响应性,但不执行嵌套对象的深层响应式转换(深层还是原生对象)

9、shallowReadonly

创建一个proxy,使其自身的property 为只读,但不执行嵌套对象的深度只读转换(深层还是可读、可写的)

10、toRefs

<template>
    <div>
        <h2>{{ name }} - {{ age }}</h2>
        <button @click="changeAge">修改age</button>
    </div>
</template>
<script>
    import {  reactive, toRefs } from 'vue'
    export default {
        setup() {
            const info = reactive({name: "why", age: 18});
            //toRefs: 将reactive 对象中的所有属性都转为ref,建立链接
            let { name, age } = toRefs(info);
            const changeAge = () => {
                age.value++;
                info.age++;
            }
            return {
                name,
                age,
                changeAge
            }
        }
    }
</script>

11、toRef

<template>
    <div>
        <h2>{{ name }} - {{ age }}</h2>
        <button @click="changeAge">修改age</button>
    </div>
</template>
<script>
    import {  reactive, toRef } from 'vue'
    export default {
        setup() {
            const info = reactive({name: "why", age: 18});
            //toRef: 对其中一个属性进行转换ref,建立链接
            let { name } = info;
            let age = toRef(info, "age");
            const changeAge = () => {
                age.value++;
                info.age++;
            }
            return {
                name,
                age,
                changeAge
            }
        }
    }
</script>

12、unref

如果我们想要获取一个ref 引用中的value,那么也可以通过unref 方法

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

这是val = isRef( val ) ? val.value: val 的语法糖函数

13、isRef

判断是否是一个ref 对象

14、shallowRef

创建一个浅层的ref对象

15、triggerRef

手动触发和shallowRef 相关联的副作用

<template>
    <div>
        <h2>{{ info2 }}</h2>
        <button @click="changeInfo2">修改info</button>
    </div>
</template>
<script>
    import { ref, shallowRef, triggerRef } from 'vue'
    export default {
        setup() {
            // 1.深层响应式
            const info1 = ref({ name: "why" })
            const changeInfo1 = () => {
                info1.value = { name: "james"};
                info1.name.value = "james";
            } 

            // 2.浅层响应式
            const info2 = shallowRef({ name: "why" })
            const changeInfo2 = () => {
                info2.name.value = "james"; // 浅层响应式
                triggerRef(info2);   // 深层响应式
            } 
            
            return {
                info1,
                info2,
                changeInfo1,
                changeInfo2
            }
        }
    }
</script>

16、customRef

创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制

它需要一个工厂函数,该函数接收track 和trigger 函数作为参数

并且应该返回一个带有get 和set 的对象

import { customRef } from 'vue';
// 自定义ref
export default function( value ) {
    let timer = null;
    return customRef(( track, trigger ) => {
        return {
            get() {
                track();  // 收集依赖
                return value;
            },
            set(newValue) {
                // 实现防抖
                clearTimeout( timer );
                timer = setTimeout(() => {
                    value = newValue;
                    trigger();  // 触发更新
                },1000)
            }
            
        }
    })
}

17、computed

<template>
    <div>
        <h2>{{ firstName }}</h2>
        <h2>{{ lastName }}</h2>
        <button @click="changeName1">修改firstName</button>
    </div>
</template>

<script>
    import { ref, computed } from 'vue'
    export default {
        setup() {
            const firstName = ref("Kobe");
            const lastName = ref("Bryant");

            // fullName 可以展示但不是响应式
            const fullName = firstName.value + lastName.value;
            
            // 1. 用法一: 传入一个getter函数
            // fullName1 是响应式的
            // computed 的返回值是一个ref 对象
            const fullName1 = computed(() => { firstName.value + lastName.value})
            const changeName1 = () => {
                firstName.value = "James"
            }

            // 2. 用法二:传入一个对象,对象包含getter 和setter
            const fullName2 = computed({
                get: () => firstName.value + " " + lastName.value,
                set(newValue) {
                    const names = newValue.split(" ");
                    firstName.value = names[0];
                    lastName.value = names[1];
                }
                
            })
            const changeName2 = () => {
                fullName2.value = "coder why"
            }

            return {
                firstName,
                lastName,
                fullName,
                fullName1,
                fullName2,
                changeName1,
                changeName2
            }
        }
    }
</script>

 18、watchEffect 侦听

1、watchEffect 侦听的基本使用

<template>
    <div>
        <h2>{{ name }}</h2>
        <h2>{{ age }}</h2>
        <button @click="changeName">修改name</button>
        <button @click="changeAge">修改age</button>
    </div>
</template>

<script>
    import { ref, watchEffect } from 'vue'
    export default {
        setup() {
            // watchEffect: 自动收集响应式的依赖
            const name = ref("why");
            const age = ref(18);
            
            const changeName = () => name.value = "kobe";
            const changeAge = () => age.value++;

            // 会立即执行一次,自动收集内部的可响应式依赖(name.value)
            watchEffect(() => {
                console.log("name", name.value)
            })
            
            return {
                name,
                age,
                changeName,
                changeAge
            }
        }
    }
</script>

 2、watchEffect 停止侦听

<template>
    <div>
        <h2>{{ name }} - {{age}}</h2>
        <button @click="changeName">修改name</button>
        <button @click="changeAge">修改age</button>
    </div>
</template>

<script>
    import { ref, watchEffect } from 'vue'
    export default {
        setup() {
            // watchEffect: 自动收集响应式的依赖
            const name = ref("why");
            const age = ref(18);
           
            // 会立即执行一次,自动收集内部的可响应式依赖(name.value)
            const stop = watchEffect(() => {
                console.log("name", name.value)
            })

            const changeName = () => name.value = "kobe";
            const changeAge = () => {
                age.value++;
                if(age.value > 25) {
                    stop(); // 停止侦听
                }
            }
            
            return {
                name,
                age,
                changeName,
                changeAge
            }
        }
    }
</script>

 3、watchEffect 清除副作用

<template>
    <div>
        <h2>{{ name }} - {{age}}</h2>
        <button @click="changeName">修改name</button>
        <button @click="changeAge">修改age</button>
    </div>
</template>

<script>
    import { ref, watchEffect } from 'vue'
    export default {
        setup() {
            // watchEffect: 自动收集响应式的依赖
            const name = ref("why");
            const age = ref(18);
           
            // 会立即执行一次,自动收集内部的可响应式依赖(name.value)
            const stop = watchEffect((onInvalidate) => {
                const timer = setTimeout(() => {
                    console.log("网络请求成功")
                }, 2000)

                // 根据name 和age 两个变量发送网络请求
                onInvalidate(() => {
                    // 在这个函数中清除额外的副作用
                    request.cancel();
                    clearTimeout(timer);
                    console.log("onInvalidate")

                })
                console.log("name", name.value, "age:", age.value)
            })

            const changeName = () => name.value = "kobe";
            const changeAge = () => {
                age.value++;
                if(age.value > 25) {
                    stop(); // 停止侦听
                }
            }
            
            return {
                name,
                age,
                changeName,
                changeAge
            }
        }
    }
</script>

19、setup 中使用ref

<template>
    <div>
        <h2 ref="title">哈哈哈</h2>
    </div>
</template>

<script>
    import { ref, watchEffect } from 'vue'
    export default {
        setup() {
            const title = ref(null);

            // 默认情况下watchEffect 是先立即执行一次的
            watchEffect(() => {
                console.log(title.value)
            },{
                // flush: "pre" // 默认情况下watchEffect 是先立即执行一次的
                flush: "post"  // dom 挂载完之后再执行watchEffect
            })

            return {
                title
            }
        }
    }
</script>

20、生命周期钩子

<template>
    <div>
        <button @click="increment">{{counter}}</button>
    </div>
</template>

<script>
    import { onMounted, onUpdated, onUnmounted, ref } from 'vue';
    export default {
        setup() {
            const counter = ref(0);

            const increment = () => {
                counter.value++
            }

            onMounted(() => {
                console.log("onMounted")
            });
            onUpdated(() => {
                console.log("onMounted")
            });
            onUnmounted(() => {
                console.log("onMounted")
            });
            
            return {
                counter,
                increment
            }
        }
    }
</script>

21、provide(为子孙组件提供数据) 和 inject

// provide.vue

<template>
    <div>
        <button @click="incrment">+1</button>
        <inject/>
    </div>
</template>

<script>
    import { provide, ref, readonly } from 'vue';
    import Inject from './Inject.vue'
    export default {
        components: {
            Inject
        },
        setup() {
            const name = ref("why");
            let counter = ref(100);

            const increment = () => counter.value++;
            
            // provide(key, value)
            // readonly() 子孙组件不能修改数据
            provide("name",readonly(name));
            provide("counter",readonly(counter));

            return {
                increment
            }
        }
    }
</script>
// inject.vue

<template>
    <div>
        <h2>{{ name }} - {{ counter }}</h2>
        <button @click="homeIncrement">+1</button>
    </div>
</template>

<script>
    import { inject } from 'vue';
    export default {
        setup() {
            // inject(key,默认值)
            const name = inject("name", "why");
            const counter = inject("counter", 18);
            
            const homeIncrement = () => { counter.value++ }
            return {
                name,
                counter,
                homeIncrement
            }
        }
    }
</script>

 22、compositionAPI 的练习

 1. opstionsAPI 和 compositionAPI的对比

// OptionsAPI
<template>
    <div>
        <h2>当前计数: {{ counter }}</h2>
        <h2>计数 * 2: {{ doubleCounter }}</h2>
        <button @click="incrment">+1</button>
        <button @click="decrment">-1</button>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                counter: 0,
            }
        },
        computed: {
            doubleCounter() {
                return this.counter * 2;
            }
        },
        methods: {
            increment() {
                this.counter++;
            },
            decrement() {
                this.counter++;
            }
        }
    }
</script>


// CompositionAPI
<template>
    <div>
        <h2>当前计数: {{ counter }}</h2>
        <h2>计数 * 2: {{ doubleCounter }}</h2>
        <button @click="incrment">+1</button>
        <button @click="decrment">--1</button>
    </div>
</template>

<script>
    import { ref, computed } from 'vue'
    export default {
        setup() {
            const counter = ref(0);
            const doubleCounter = computed(() => counter.value * 2);

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

            return {
                counter,
                doubleCounter,
                increment,
                decrement
            }
        }
    }
</script>

2. 通过hook对compositionAPI中的选项进行抽取(useCounter案例)

// userCounter.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
    }
}



// App.vue
<template>
    <div>
        <h2>当前计数: {{ counter }}</h2>
        <h2>计数 * 2: {{ doubleCounter }}</h2>
        <button @click="incrment">+1</button>
        <button @click="decrment">--1</button>
    </div>
</template>

<script>
    import useCounter from './hooks/useCounter'
    export default {
        setup() {
            const { counter, doubleCounter, increment, decrement } = useCounter();

            return {
                counter,
                doubleCounter,
                increment,
                decrement
            }
        }
    }
</script>
<template>
    <div>
        <h2>当前计数: {{ counter }}</h2>
        <h2>计数 * 2: {{ doubleCounter }}</h2>
        <button @click="incrment">+1</button>
        <button @click="decrment">--1</button>
    </div>
</template>

<script>
    import useCounter from './hook/useCounter'
    export default {
        setup() {
            return {
                ...useCounter()
            }
        }
    }
</script>

 3. hook 修改title(useTitle案例)

// useTitle.js
import { ref, watch } from 'vue'
export default function(title = "默认的title") {
    const titleRef = ref(title);
    
    watch(titleRef, (newValue) => {
        document.title = newValue
    },{
        immediate: true
    })

    return titleRef
}


// 
<template>
    <div>
    </div>
</template>

<script>
    import useTitle from './hooks/useTitle';
    export default {
        setup() {
            const titleRef = useTitle("coder");
            
            setTimeout(() => {
                titleRef.value = "123";
            }, 1000)

            return {
                
            }
        }
    }
</script>

4. 获取滚动条位置(useScrollPosition案例)

<template>
    <div>
        <p class="content"></p>
        <div class="scroll">
            <div class="scroll-x">scrollX: {{ scrollX }} </div>
            <div class="scroll-y">scrollY: {{ scrollY }} </div>
        </div>
    </div>
</template>

<script>
    import { ref } from 'vue'
    export default {
        setup() {
            const scrollX = ref(0);
            const scrollY = ref(0);

            document.addEventListener('scroll', () => {
                scrollX.value = window.scrollX;
                scrollY.value = window.scrollY;
            });
            
            return {
                scrollX,
                scrollY
            }
        }   
    }
</script>

<style scoped>
    .content {
        width: 3000px;
        height: 5000px;
    }
    .scroll {
        position: fixed;
        right: 30px;
        bottom: 30px;
    }
</style>>
// 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
    }
}


// 
<template>
    <div>
        <p class="content"></p>
        <div class="scroll">
            <div class="scroll-x">scrollX: {{ scrollX }} </div>
            <div class="scroll-y">scrollY: {{ scrollY }} </div>
        </div>
    </div>
</template>

<script>
    import useScrollPosition from './useScrollPosition'
    export default {
        setup() {
            const { scrollX, scrollY } = useScrollPosition();
            return {
                scrollX,
                scrollY
            } 
        }   
    }
</script>

<style scoped>
    .content {
        width: 3000px;
        height: 5000px;
    }
    .scroll {
        position: fixed;
        right: 30px;
        bottom: 30px;
    }
</style>>

 5. 获取鼠标位置(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
    }
}


//
<template>
    <div>
        <p class="content"></p>
        <div class="mouse">
            <div class="mouse-x">mouseX: {{ mouseX }} </div>
            <div class="mouse-y">mouseY: {{ mouseY }} </div>
        </div>
    </div>
</template>

<script>
    import useMousePosition from './useMousePosition'
    export default {
        setup() {
            const { mouseX, mouseY } = useMousePosition();

            return {
                mouseX,
                mouseY
            }
        }   
    }
</script>

<style scoped>
    .content {
        width: 3000px;
        height: 5000px;
    }
    .mouse {
        position: fixed;
        right: 30px;
        bottom: 30px;
    }
</style>>

6. userLocalStorage 案例

// useLocalStorage.js
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;
}

// 一个参数: 取值
const data1 = useLocalStorage("name");

// 两个参数: 保存值
const data2 = useLocalStorage("name","coderWhy");




//
<template>
    <div>

        <h2>{{ data }}</h2>
        <button @click="changeData">修改data</button>
    </div>
</template>

<script>
    import useLocalStorage from './useLocalStorage';
    export default {
        setup() {
            const data = useLocalStorage("info", { name: "coder", age: 18 });
            
            const changeData = () => data.value = "哈哈哈";

            return {
                data,
                changeData
            }
        }   
    }
</script>


 23、setup 顶层编写方式

// 父组件
<template>
    <div>
        <h2>当前计数: {{ counter }}</h2>
        <button @click="increment">+1</button>
        
        <hello-world message="呵呵呵" @increment="getCounter"></hello-world>
    </div>
</template>

<script setup>
    import { ref } from 'vue';
    import HelloWorld from './HelloWorld.vue';
    
    const counter = ref(0);
    const increment = () => counter.value++;
    const getCounter = (payload) => {
        console.log(payload);
    };
</script>



// 子组件
<template>
    <div>
        <h2>Hello World</h2>
        <h2>{{ message }}</h2>
        <button @click="emitEvent">发射事件</button>
    </div>
</template>

<script setup>
    import { defineProps, defineEmit } from 'vue';
    
    const props = defineProps({
        message: {
            type: String,
            define: "哈哈哈"
        }
    })
    
    const emit = defineEmit(["increment"]);
    const emitEvent = () => {
        emit("increment","10000");
    }
</script>

24、h 函数

h() 函数式一个用于创建vnode 的一个函数,更准确的命名是createVNode() 函数

h() 函数有三个参数:(标签名称 | 组件,第一个参数中有哪些属性,子元素 | 子组件)

1. render函数 和 h函数的使用

<script>
    import { h } from 'vue' 
    export default {
        render() {
            return h("h2",{ class: "title" },"Hello Render");
        }
    }
</script>

2. 通过render函数实现计数器

<script>
    import { h } from 'vue' 
    export default {
        data() {
            return {
                counter: 0
            }
        },
        render() {
            return h("div",{ class: "app" },[
                h("h2",null,`当前计数: ${this.counter}`),
                h("button",{
                    onClick: () => this.counter++
                },"+1"),
                h("button",{
                    onClick: () => this.counter--
                },"-1")
            ]);
        }
    }
</script>
<script>
    import { h, ref } from 'vue' 
    export default {
        setup() {
            const counter = ref(0);

            return () => {
                    return h("div",{ class: "app" },[
                        h("h2",null,`当前计数: ${counter.value}`),
                        h("button",{
                            onClick: () => counter.value++
                        },"+1"),
                        h("button",{
                            onClick: () => counter.value--
                        },"-1")
                    ]);
                }
        },
        
    }
</script>

3. render 函数中组件和插槽的使用

//App.vue 父
<script>
    import { h, ref } from 'vue' 
    import HelloWorld from './HelloWorld.vue'
    export default {
        render() {
            return h(HelloWorld, null, {
                default: props => h("span", null, `app传入到HelloWorld中的内容: ${props.name}`)
            })
        }
    }
</script>



//HelloWorld.vue 
<script>
    import { h } from 'vue'
    export default {
        render() {
            return h("div", null, [
                h("h2", null, "Hello World"),
                this.$slots.default ? this.$slots.default({name: "coder"}) : h("span", null, "我是Hello World的默认值")
            ])
        }
    }
</script>

25、jsx的使用

// App.vue
<script>
    import HelloWorld from './HelloWorld.vue' 
    export default {
        data() {
            return {
                counter: 0
            }
        },
        render() {
            const increment = () => this.counter++;
            const decrement = () => this.counter--;
            return (
                <div>
                    <h2>当前计数: { this.counter }</h2>
                    <button onClick={ increment }>+1</button>
                    <button onClick={ decrement }>-1</button>
                    <HelloWorld>
                        {{default: props => <button>我是按钮</button>}}    
                    </HelloWorld>
                </div>
            )
        }
    }
</script>


// HelloWorld.vue
<script>
    export default {
        render() {
            return (
                <div>
                    <h2>HelloWorld</h2>
                    { this.$slots.default ? this.$slots.default() : <span>哈哈</span>}
                </div>
            )
        }
    }
</script>

 25、自定义指令

一、案例:当某个元素挂载完成后可以自动获取焦点

// 默认的实现方式
<template>
    <div>
        <input type="text" ref="input">
    </div>
</template>

<script>
    import { ref, onMounted } from 'vue'
    export default {
        setup() {
            const input = ref(null);
            
            onMounted(() => {
                console.log(input.value);
                input.value.focus();
            })

            return {
                input
            }
        }
    }
</script>
// 局部指令
<template>
    <div>
        <input type="text" v-focus>
    </div>
</template>

<script>
    export default {
        // 局部指令
        directives: {
            focus: {
                mounted(el, bindings, vnone, preVnode) {
                    console.log("focus");
                    el.focus();
                }
            }
        },
    }
</script>
// 全局定义自定义指令
const app = createApp(App);

app.directive("focus", {
    mounted(el, bindings, vnone, preVnode) {
        console.log("focus");
        el.focus();
    }
});

app.mounted('#app');

二、指令的生命周期

<template>
    <div>
        <button v-if="counter < 2" v-why @click="increment">当前计数: {{ counter }}</button>
    </div>
</template>

<script>
    import { ref } from 'vue';
    export default {
        setup() {
            const counter = ref(0);
            const increment = () => counter.value++;
            return {
                counter,
                increment
            }
        },
        directives: {
            why: {
                created(el, bindings, vnode, preVnode) {
                    console.log("created");
                },
                beforeMounted(el, bindings, vnode, preVnode) {
                    console.log("beforeMounted");
                },
                mounted(el, bindings, vnode, preVnode) {
                    console.log("mounted");
                },
                beforeUpdate(el, bindings, vnode, preVnode) {
                    console.log("beforeUpdate");
                },
                updated(el, bindings, vnode, preVnode) {
                    console.log("updated");
                },
                beforeUnmounted(el, bindings, vnode, preVnode) {
                    console.log("beforeUnmounted");
                },
                unmounted(el, bindings, vnode, preVnode) {
                    console.log("unmounted");
                }
            }
        }
    }
</script>

三、自定义指令的参数和修饰符

<template>
    <div>
        <button v-if="counter < 2" v-why.aaa.bbb="'coderWhy'" @click="increment">当前计数: {{ counter }}</button>
    </div>
</template>

<script>
    import { ref } from 'vue';
    export default {
        setup() {
            const counter = ref(0);
            const increment = () => counter.value++;
            return {
                counter,
                increment
            }
        },
        directives: {
            why: {
                // el 表示dom元素, bindings 表示自定义指令的修饰符和参数
                created(el, bindings, vnode, preVnode) {
                    console.log("created");
                    console.log(bindings.value); // coderWhy
                    console.log(bindings.modifiers); // {aaa: true, bbb: true}
                }
            }
        }
    }
</script>

四、hook 方式注册全局自定义指令

// main.js
import registerDirectives from './index.js'
registerDirectives (app);



// App.vue
<template>
    <div>
        <h2 v-format-time="YYYY/MM/DD">{{ timeStamp }}</h2>
    </div>
</template>

<script>
    export default {
        setup() {
            const timeStamp = 1624452193;

            return {
                timeStamp
            }
        }
    }
</script>



// index.js
import registerFormatTime from 'format-time.js';

export default function registerDirectives(app) {
    registerFormatTime(app);
}

// format-time.js
import dayjs from 'dayjs';
export default function() {
    let formatString = "YYYY-MM-DD HH:mm:ss";
    app.directive("format-time", {
        created(el, bindings) {
            if(bindings.value) {
                formatString = bindings.value;
            }
        },
        mounted(el, bindings) {
            const textContent = el.textContent;
            let timestamp = parseInt(textContent);
            if( textContent.length === 10 ) {
                textContent = textContent * 1000;
            }
            el.textContent = dayjs(timestamp).format(formatString);
        }
    })
}

26、Vue 的插件 plugins

一、对象方式书写插件

// plugin_object.js
export default {
    install(app) {
        console.log(app);
        app.config.globalProperties.$name = "coder";
    }
}


// main.js
import pluginObject from './plugins_object';
app.use(pluginObject);


// App.vue
<script>
    import { getCurrentInstance } from 'vue';
    export default {
        // 1. composition_API写法
        setup() {
            const instance = getCurrentInstance();
            console.log(instance.appContext.config.globalProperties.$name);
        },
        // 2. options_API 写法
        mounted() {
            console.log(this.$name);
        }
    }
</script>

二、函数方式书写插件

// plugin_function.js
export default function(app) {
        console.log(app);
        app.config.globalProperties.$name = "coder";
}


// main.js
import pluginFunction from './plugins_function';
app.use(pluginFunction);


// App.vue
<script>
    import { getCurrentInstance } from 'vue';
    export default {
        // 1. composition_API写法
        setup() {
            const instance = getCurrentInstance();
            console.log(instance.appContext.config.globalProperties.$name);
        },
        // 2. options_API 写法
        mounted() {
            console.log(this.$name);
        }
    }
</script>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值