生命周期钩子
、
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语句
后期用哪个,就import哪个
认识h函数
嘴上说不想在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(参数)
}
}
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配置
jsx的babel:可以让我们编写的jsx代码转换为 render、h函数
最新的脚手架已经可以默认支持jsx转Babel,但是我们最好也需要知道安装的步骤
babel.config.js配置
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>