17Vue-API(三)

85 阅读3分钟

生命周期钩子

图片.png

图片.png

setup里没有Created了,因为可以直接在setup中写曾经写在created中的数据,并且setup的执行时间比beforeCreated还要早

<template>

</template>

<script>
import {onMounted,onUpdated,onUnmounted} from 'vue';

export default{
//这里是生命周期可以定义多次
    setup(){
    //这里面需要传入一个回调函数
        onMounted(()=>{
            console.log('Mounted')
        })
        onUpdated(()=>{
            console.log('Updated')
        })
        onUnmounted(()=>{
            console.log('Unmounted7')
        })
    }
}
</script>

Provide和inject

vue官方推荐我们更多的去使用ref,虽然reactive会更方便。但是后期我们做代码抽离的时候,抽离到单独的hook中,reactive并不方便做抽离,所以希望我们使用ref。

App.vue

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

<script>
import {provide,ref,readonly} from 'vue'
import Home from './Home.vue'
要想把数据传输给子孙组件,首先要注册
export default{
    components:{
        Home
    }
    setup(){
        const name = ref("coderwhy")
        let counter = ref(100)
        
        //提供给子组件
        //将我共享出去的数据变成readonly,防止篡改
        provide("name",readonly(name))
        provide("counter",readonly(counter))
        
        return{
            name,
            counter
        }
    }
}
</script>

Home.vue

<template>
    {{name}}--{{counter}}
    <button @click = "homeIncrement"></button>
</template>

<script>
    import {inject} from 'vue'
    export default{
        setup(){
        接收父组件传来的数据
            const name = inject("name")
            const counter = inject("counter")
            
            //我对父组件传过来的数据进行++操作
            //父组件中的数据会被修改,但是这不是一个好的习惯
            //代码规范是单向数据流,就算改也是我顶层数据来修改。
            //所以为了杜绝这种操作,我们使用readonly
            const homeIncrement(()=>{
                counter.value++
            })
            
            return{
                name,
                counter,
                homeIncrement
            }
        }
    }
</script>

create:页面还没渲染就执行 mounted:页面渲染完毕,并且组件挂载完毕

练习1(hook的入门理解)

App.vue

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

<script>
import {ref,computed} from 'vue'
import useCounter from './hook/useCounter'
export default{
    setup(){
    //直接使用刚刚编写的hook,第一种写法
    //const {counter,doubleCounter,increment,decrement}
    = useCounter();
    
    /*
        const counter = ref(0)
        const doubleCounter = computed(()=>{
            return counter.value*2
        })
        
        const increment(()=>{
            return counter.value++
        })
        
        const decrement(()=>{
            return counter.value--
        })
   */
   
        return{
        /*第一种写法
            counter,
            doubleCounter,
            increment,
            decrement,
        */
        //第二种写法(实际上更推荐第一种写法,阅读性强)
        ...useCounter()
        }
    }
}
</script>

随着后期组件代码的增多,我们需要创建一个hook文件夹,里面存放共性的代码,一般我们对他的命名是以 : useXXX

useCounter.js

import {ref,computed} from 'vue'
export default function(){
    const counter = ref(0)
    const doubleCounter = computed(()=>{
        return counter.value*2
    })

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

    const decrement(()=>{
        return counter.value--
    })
    
    return{
        counter,doubleCounter,increment,decrement
    }
}

练习2(修改页面title)

修改页面的名字,如果我每次都通过DOM来获取title然后进行修改,一旦要修改的页面多了,会非常的麻烦,所以我们现在通过自定义hook来方便我们修改页面的名字。给title()传入一个参数名字,然后来修改title

./hooks/useTitle.js

ipmort {ref,watch} from 'vue'
export default function(title="默认的title"){
//这样后期我还可以通过.value再修改title的值
    const titleRef = ref(title)

//当我对titleRef中传值后,我的watch需要知道什么时候传值的,然后做出改变.后续我直接通过.value进行修改就可以了,因为已经是ref的了,响应式
    watch(titleRef,(newValue)=>{
        document.title = newValue;
    },{
        immediate:true;
    })
    
    return titleRef
}

App.vue

<template>

</template>

<script>
import useTitle from './hooks/useTitle'
export default{
    const titleRef = useTitle("默认值")
    setTimeout(()=>{
        titleRef.value = '后续改的值'
    },5000)
}
</script>

练习3(滚轮)

在页面右下角 显示我们X和Y坐标(滚动位置)

useScrollPostion.js

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

App.vue

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

<script>
import usescrollPostion from './hooks/scrollPostion'
export default{
    setup(){
        const {scrollX,scrollY} = usescrollPostion();
        
        return{
            scrollX,
            scrollY
        }
    }    
}
</script>
    
<style scoped>
    .content{
        width:1000px;
        height:1000px;
    },
    .scroll{
        position:fixed;
        right:30px;
        bottom:30px;
    }
</style>

练习4(鼠标位置)

useMousePosition.js

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

App.vue

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

<script>
import useMousePosition from './hooks/useMousePosition'
export default{
    setup(){
        const {mouseX,mouseY} = useMousePosition();
        
        return{
            mouseX,
            mouseY
        }
    }    
}
</script>
    
<style scoped>
    .content{
        width:1000px;
        height:1000px;
    },
    .mouse{
        position:fixed;
        right:30px;
        bottom:80px;
    }
</style>

对数据做缓存(hook)

useLocalStorage.js

import {ref,watch} from 'vue'
export default function(key,value){
    我们要对使用方法做一个区分
        只有一个参数key:取值
        两个参数全都有:保存值
    老生常谈了,对一个数据绑定成ref,这样可以返回出去
    也就是用户可以拿到他,并且通过.value对他进行修改
    然后重新拿到,重新进行缓存    
    
const data = ref(value);
    if(value){
    //存储进去的得是一个string格式
    //有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;

}

App.vue

<template>
    {{data}}
</template>
<script>
import useLocalStorage from './hooks/useLocalStorage'
export default{
    setup(){
        const data = useLocalStorage("info",{
            name:"coderwhy",
            age:18
        });
        
        return{
            data
        }
    }    
}
</script>
个人习惯(批量也可以,后期会学)

App.vue中有太多的import语句 我们可以单独建一个文件夹,里面专门存放import语句

图片.png

后期用哪个,就import哪个

图片.png

认识h函数

图片.png 嘴上说不想在template中书写代码,实际上大部分还是这样,真实开发中一般只在库里,写h函数,大部分还是使用template模板

vue会先将template中的节点代码 经过compiled 变成render函数,然后调用render函数,他的返回值就是VNode,他们组合在一起就是一个树的结构,就是虚拟DOM;

我们直接使用render函数,会更快生成VNode;也就是自己编写render函数,充分的用JavaScript代码来编写代码

想要使用render函数,最好使用h函数。因为render函数返回值是一个VNode,而h函数的执行可以返回一个VNode

1.h()函数是一个用于创建vnode的一个函数

2.其实更准确的命名是createVNode()函数,但是为了简便,Vue将他加你化成h()函数,大致过程如下:

export default{
    render(){
        return h(参数)
    }
}

图片.png

App.vue

使用render函数,就不再需要template
<script>
    import {h} from 'vue' 
    export default{
        render(){
            return h(
            "h2",            标签名
            {class:"title"}, 类名
            "Hello Render"   数据
            )
        }
    }
</script>

render计数器的案例

<script>
    import {h,ref} from 'vue'
    export default{
        setup(){
            const counter = ref(0)
            return{
                counter
            }
        },
        //render是可以绑定this的,他不在setup里面
        render(){
            return h(
            "div",
            {class:"app"},
            [
               h("h2",null,`当前计数:${this.counter}`),
               
               h("button",{
                   onClick:()=>this.counter++
               },"+1"),
               
               h("button",{
                   onClick:()=>this.counter--
               },"-1")
            ]
            )
        },
    }
</script>

setup非常的awesome,它里面还可以包含render函数

再次提示:setup中的ref是不会解包的,只会在template中解包

<script>
    import {h,ref} from 'vue'
    export default{
        setup(){
            const counter = ref(0)
            return{
               return h("div",{class:"app"},[
                   h("h2",null,`当前计数:${this.counter.value}`),

                   h("button",{
                       onClick:()=>counter.value++
                   },"+1"),

                   h("button",{
                       onClick:()=>counter.value--
                   },"-1")]) 
            }
    }
</script>

jsx的babel配置

图片.png

jsx的babel:可以让我们编写的jsx代码转换为 render、h函数

最新的脚手架已经可以默认支持jsx转Babel,但是我们最好也需要知道安装的步骤

babel.config.js配置

图片.png

App.vue

<script>
import HelloWorld from '...'
export default{
    data(){
        return{
            counter:0
        }
    },
    render(){
        const increment=()=>this.counter++;
        const decrement=()=>this.counter--;
        return{
            <div>
            jsx中使用的是一个大括号
                <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>