zoukankan      html  css  js  c++  java
  • 第十二节:Vue3的Composition Api(生命周期、provide/Inject、综合练习-hook封装) 和 setup顶层开发模式

    一. 生命周期钩子

    1. 说明

     setup 可以用来替代 data 、 methods 、 computed 、watch 等等这些选项,也可以替代 生命周期钩子。

     注:因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

    2. 实战 

    <template>
        <div>
            <h3>我是child1组件</h3>
            <h4>{{counter}}</h4>
            <h4><button @click="AddNum()">加1</button></h4>
        </div>
    </template>
    
    <script>
        import { ref, onMounted, onUpdated, onUnmounted } from 'vue';
        export default {
            setup(props, context) {
                // 编写相关业务
                let counter = ref(100);
                const AddNum = () => counter.value++;
    
                // 测试生命周期
                onMounted(()=>{
                    console.log('App onMounted');
                });
                onUpdated(()=>{
                    console.log('App onUpdated');
                });
                onUnmounted(()=>{
                    console.log('App onUnmounted');
                });
                
                // 返回值
                return {
                    counter,
                    AddNum
                }
            }
        }
    </script>

    二. provide/Inject

    1. 说明

     之前学习的Option Api中有Provide和Inject,Composition API也可以替代之前的 Provide 和 Inject 的选项。

    (1). Provide:用于向子组件中传递数据。provide可以传入两个参数:① name:提供的属性名称; ② value:提供的属性值。

    注:为了保证数据的响应性,一般传递ref对象;而且传递的数据要符合单向数据流原则,即传递的数据只允许子组件调用不允许子组件修改,所有通常再用readonly包裹一下。

    (2). Inject:用于接收父组件传递过来的数据。

     inject 函数有两个参数:

      A. 要 inject 的 property 的 name

      B. 默认值 (可选)

    2. 实战

     这里准备三个组件,从上往下依次是: App.vue → father1.vue → child1.vue, 下面代码实现:App组件 向 child1组件传值。

    App.vue代码

    <template>
        <div>
            <h3>我是最上级App组件</h3>
            <h4>{{counter}}</h4>
            <h4><button @click="AddNum()">App.vue中加1</button></h4>
            
            <father1></father1>
        </div>
    </template>
    
    <script>
        import father1 from './father1.vue';
        import { ref, readonly, provide } from 'vue';
        export default {
            components: {
                father1
            },
            setup() {
                let counter = ref(100);
                const AddNum = () => counter.value++;
    
                provide('myCounter', readonly(counter));
    
                return {
                    counter,
                    AddNum
                }
            }
        }
    </script> 

    father1.vue代码

    <template>
        <div>
            <h3>我是father1组件</h3>
            <child1></child1>
        </div>
    </template>
    
    <script>
        import child1 from './child1.vue';
        export default {
            components:{
                child1
            },
            setup(props, context) {
                return {}
    
            }
        }
    </script>
    View Code

    child1.vue代码

    <template>
        <div>
            <h3>我是child1组件</h3>
            <h4>{{counter}}</h4>
            <h4>{{msg}}</h4>
            <h4><button @click="AddNum()">child.vue加1</button></h4>
        </div>
    </template>
    <script>
        import { inject } from 'vue';
        export default {
            setup(props, context) {        
                let counter = inject('myCounter');
                let msg = inject('myMsg','hello ypf');  //如果没有传递,则为默认值
                
                // 传递过来的是readonly对象,不能被修改,符合单向数据流原则,如果修改,需要通过emit向父组件发送通知,让父组件修改
                const AddNum = () => counter.value++;
    
                // 返回值
                return {
                    counter,
                    msg,
                    AddNum
                }
            }
        }
    </script>

    三. 综合练习-hook封装

    1. 相关规则

    (1). import导入的匹配规则

    (2). ESModule导入导出规则

      详见:https://www.cnblogs.com/yaopengfei/p/14496363.html

    2. 代码实操

    (1). hooks文件夹下5个js文件

    useCounter.js

    /* 
        自增、自减、双倍
     */
    import { ref, computed } from 'vue';
    
    // 对应外面的接收方式  import useCounter from './hooks/useCounter.js'
    export default function() {
        let counter = ref(100);
        let doubleCounter = computed(() => counter.value * 2);
        const AddNum = () => counter.value++;
        const Reduce = () => counter.value--;
        return {
            counter,
            doubleCounter,
            AddNum,
            Reduce
        }    
    }
    View Code

    useTitle.js

    /* 
      修改标题 
     */
    import { ref, watch } from 'vue'
    
    export default function(title = '我是默认title哦') {
        let titleRef = ref(title);
    
        watch(titleRef, (newValue, oldValue) => {
            document.title = newValue;
        }, {
            immediate: true
        });
    
        return titleRef;
    }
    View Code

    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
      }
    }
    View Code

    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
      }
    }
    View Code

    useLocalStorage.js

    import { ref, watch } from 'vue'
    
    export default function(key, value) {
        var data = ref(value);
        if (value) {
            window.localStorage.setItem(key, JSON.stringify(value));
        } else {
            data.value = JSON.parse(window.localStorage.getItem(key));
        }
    
        watch(data, (newValue, oldValue) => {
            window.localStorage.setItem(key, JSON.stringify(newValue));
        });
    
        return data;
    }
    View Code

    (2). hooks文件夹中新建index.js统一入口

    import useCounter from './useCounter.js';
    import useTitle from './useTitle.js';
    import useScrollPosition from './useScrollPosition.js'
    import useMousePosition from './useMousePosition.js'
    import useLocalStorage from './useLocalStorage.js'
    
    export {
        useCounter,
        useTitle,
        useScrollPosition,
        useMousePosition,
        useLocalStorage
    }

    (3). App.vue调用代码 

    <template>
        <div>
            <h4>{{counter}}</h4>
            <h4>{{doubleCounter}}</h4>
            <h4><button @click="AddNum()">加1</button></h4>
            <h4><button @click="Reduce()">减1</button></h4>
    
            <h4><button @click="Edit()">修改缓存</button></h4>
    
            <p class="content"></p>
            <div class="scroll">
                <div class="scroll-x">scrollX: {{scrollX}}</div>
                <div class="scroll-y">scrollY: {{scrollY}}</div>
            </div>
            <div class="mouse">
                <div class="mouse-x">mouseX: {{mouseX}}</div>
                <div class="mouse-y">mouseY: {{mouseY}}</div>
            </div>
        </div>
    </template>
    
    <script>
        // 方式一:逐个导入
        // import useCounter from './hooks/useCounter.js';
        // import useTitle from './hooks/useTitle.js';
        // import useScrollPosition from './hooks/useScrollPosition.js'
        // import useMousePosition from './hooks/useMousePosition.js'
        // import useLocalStorage from './hooks/useLocalStorage.js'
    
        //方式二: 统一封装导入 (可以省略,直接写成 ./hooks)
        import { useCounter, useTitle, useScrollPosition, useMousePosition, useLocalStorage } from './hooks/index.js'
    
        export default {
            setup() {
    
                // 1.自增、自减、双倍
                const { counter, doubleCounter, AddNum, Reduce } = useCounter();
    
                // 2.修改title
                let titleRef = useTitle('ypf');
                setTimeout(() => {
                    titleRef.value = 'lmr';
                }, 3000);
    
                // 3. 页面滚动位置
                const { scrollX, scrollY } = useScrollPosition();
    
                // 4. 鼠标移动位置
                const { mouseX, mouseY } = useMousePosition();
    
                // 5. 缓存的使用
                var myData = useLocalStorage('myName', 'ypf');
                console.log(myData);
                const Edit = () => myData.value = 'lmr';
    
                return {
                    counter,
                    doubleCounter,
                    AddNum,
                    Reduce,
    
                    Edit,
    
                    scrollX,
                    scrollY,
    
                    mouseX,
                    mouseY
                }
            }
        }

    四. setup顶层开发模式

     (详见官网:https://v3.cn.vuejs.org/api/sfc-script-setup.html)

    1. 说明

     <script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 <script> 语法,它具有更多优势:

     (1). 更少的样板内容,更简洁的代码。

     (2). 能够使用纯 Typescript 声明 props 和抛出事件。

     (3). 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。

     (4). 更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。

    2. 用法说明

     (1). 将setup写到<script>标签中。

     (2). 变量,函数声明,以及 import 引入的内容都可以直接在template中使用

     (3). 直接导入组件使用即可

     (4).  父子组件之间相互传值,需要使用:defineProps 和 defineEmits。

    3. 实战

    (1). 父组件

    <template>
        <div>
            <h4>我是主组件</h4>
            <h4>{{counter}}</h4>
            <h4><button @click="AddNum">加1</button></h4>
    
            <child1 msg="hahahhaha" @MyTest='MyTest'></child1>
        </div>
    </template>
    
    <script setup>
        import { ref } from 'vue';
    
        // 1. 变量,函数声明,以及 import 引入的内容都可以直接在template中使用
        var counter = ref(0);
        var AddNum = () => counter.value++;
    
        //2. 直接导入组件使用即可
        import child1 from './child1.vue';
    
        //3. 测试子传父
        const MyTest = (obj) => {
            console.log(`我是子组件传递过来的值:${obj}`);
        }
    </script>

    (2). 子组件

    <template>
        <div>
            <h4>我是child1组件</h4>
            <h4>我是父组件传递过来的值:{{msg}}</h4>
            <h4><button @click="myClick">对外发送</button></h4>
        </div>
    </template>
    
    <script setup>
        import { defineProps, defineEmits } from 'vue';
    
        // 接收父组件传递过来的值
        const props = defineProps({
            msg: {
                type: String,
                default: 'hello ypf'
            }
        })
    
        // 声明对外传递事件,并执行传递
        const emit = defineEmits(['MyTest', '']);
        const myClick = () => {
            emit('MyTest', '10010');
        }
    </script>

     

     

     

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    My SQL
    弹窗
    DBDA
    ThinkPHP验证码与文件上传
    ThinkPHP表单验证
    ThinkPHP增删改
    ThinkPHP模型(查询)
    ThinkPHP跨控制器调用方法
    Superset安装
    Presto资源组配置
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/15390818.html
Copyright © 2011-2022 走看看