vue知识体系之高级篇(内含自定义Hooks和JSX应用)

351 阅读2分钟

Vue高级语法


1.生命周期钩子

  • setup可以代替之前的data,methods,computed,wacth等等这些选项,也可以替代生命周期钩子。

  • 那咋使用在setup函数中?

    • 很简单,直接导入on + X函数注册生命周期钩子
<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("App Mounted");
      })
      
      onUpdated(() => {
        console.log("App onUpdated");
      })
      onUnmounted(() => {
        console.log("App onUnmounted");
      })
​
      return {
        counter,
        increment
      }
    }
  }
</script><style scoped></style>

⚠️ 在Composition API中使用setup代替beforeCreate和created因为setup其实执行的更早一些


2.provide与Inject在setup中使用

  • 我们可以通过provide来提供数据:

    • 可以通过provide方法来定义每个Property

    • provide可以传入两个参数:

      • Name: 提供的属性名称
      • Value: 提供的属性值
    • 在后代组件中可以通过inject来注入需要的属性和对应的值:

    • inject可以传入两个参数

      • inject的property的name
      • 给一个默认值(选写)
  • App.vue

<template>
  <div>
    <Home />
    <button @click="increment"></button>
  </div>
</template>
<script>
  import Home rom "./Home.vue"
  improt {provide,ref,readonly} from "vue"
  export default {
    components: {
      Home
    },
    setup() {
      //尽量使用ref,因为这两个玩意就没什么关联性
      const name = ref("Tim");
      let counter = ref(100);
      //为什么要使用readyonly呢,假设我在子组件使用点击事件把counter或者name进行了修改,这样也会把父组件的东西修改了,我们要遵循一个原则就是单项数据流,使用使用readonly传过去的只可以读取不能进行修改
      provide("name",readonly(name))
      provide("counter",counter)
      
      const increment = () => {
        counter++
      }
      
      return {
        increment,
      }
    }
  }
</script>
  • Home.vue
<template>
  <div>
    <h2>{{name}}</h2>
    <h2>{{counter}}</h2>
  </div>
</template>
<script>
  improt {inject} from "vue"
  export default {
    setup() {
      const name = inject("name")
      const counter = inject("counter")
      
      return {
        name,counter
      }
    }
  }
</script>

3.hooks

  • 如果使用option API会使用代码不易阅读
  • 换成setup就会简介很多,但是也会随着代码量越写越多,所以一般会把功能进行抽离放在js文件中如何再进行使用,这就是hooks的概念。

案例1:

//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
  }
}
<template>
  <div>
    <!-- 计数器案例 -->
    <h2>当前计数: {{counter}}</h2>
    <h2>计数*2: {{doubleCounter}}</h2>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
  </div>
</template>
<script>
  import useCounter from "./hooks/useCounter"
  setup() {
    const {counter,doubleCounter,increment,decrement} = useCounter()
    
    return {
      counter,doubleCounter,increment,decrement
      
    }
  }
</script>

案例2:

//useTittle.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 useTittle from "./hooks/useTittle"
  setup() {
    const titleRef = useTittle("tim")
    setTimeout(() => {
      titleRef.value = "yang"
    },2000)
    return {
      
    }
  }
</script>

案例3:

<p class="content"></p>
<div class="scroll">
  <div class="scroll-x">scrollX: {{scrollX}}</div>
  <div class="scroll-y">scrollY: {{scrollY}}</div>
</div><script>
  import useScrollPosition from "./hooks/useScrollPosition.js"
  export defualt {
    setup() {
      const {scrollX,scrollY} = useScrollPosition()
      
      return {
        scrollX,
        scrollY
      }
    }
  }
</script>
//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
  }
}

案例4:

//useMousePosition.js
import {ref} from "vue"export default function() {
  const mouseX = ref(0)
  const mouseY = ref(0)
  
  
  window.addEventListener("mouseover",(event)=> {
    mouseX.value = event.pageX;
    mouseY.value = event.pageY;
  })
  
  return {
    mouseX,
    mouseY
  }
}
<p class="content"></p>
<div class="mouse">
  <div class="mouse-x">mouseX: {{mouseX}}</div>
  <div class="mouse-y">mouseY: {{mouseY}}</div>
</div><script>
  import useMousePosition from "./hooks/useMousePosition.js"
  export defualt {
    setup() {
      const {mouseX,mouseY} = useMousePosition()
      
      return {
        mouseX,
        mouseY
      }
    }
  }
</script>
​
​
​

案例5:

//useLocalStorage.js
import {ref,watch} from "vue"
export default function(key,value) {
  const data = ref(value)
  
  if(value) {
    window.localStorage.setItem(key,JSON.strigify(value))
  }else {
  data.value=JSON.parse(window.localStorage.getItem(key))
  }
  
  watch(data,(newValue)=> {
    window.localStorage.setItem(key,JSON.strigify(value))
  })
  
  return data
}
​
<template>
  <h2>
    {{data}}
  </h2>
  <button @click="changeData">
    修改data
  </button>
</template>
<script>
   import useLocalStorage from "./hooks/useLocalStorage.js"
  export default {
    setup() {
      //localStroage
      const data = useLocalStorage("info",{name:"Tim",age:21})
    }
    
    const changeData = () => data.value = "修改值"
    
    return {
      data,
      changeData
    }
  }
</script>

4.h函数

  • Vue推荐绝大多数情况下使用模版来创建你的HTML,然后一些特殊的场景,你真的需要JavaScript完全编程能力就可以使用渲染函数

    • Vue在生成真实的DOM之前,会把节点转换成VNode,而VNode组合在一起形成一颗树结构,就是虚拟DOM(VDom)
    • 事实上,template中的HTML最终也会使用渲染函数生成对应的VNode
    • 你可以充分利用javascript编程能力编写createVNode函数,生成对应VNode
  • 那么就可以使用h函数

    • h()函数是用于创建VNode的一个函数
  • 如何进行使用呢?

    • 来看看实际操作

<script>
  import {h} from "vue"
  export default {
    setup() {
      return () => h("h2",{class:"title"},"hello render")
    }
  }
</script>

小案例:

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

如果要把使用第一个参数是组件还有插槽怎么使用呢?

上代码看看👀

  • App.vue
<script>
  import { h } from 'vue';
  import HelloWorld from './HelloWorld.vue';
​
  export default {
    render() {
      return h("div", null, [
        h(HelloWorld, null, {
          default: props => h("span", null, `app传入到HelloWorld中的内容: ${props.name}`)
        })
      ])
    }
  }
</script><style scoped></style>
  • 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: "tim"}): h("span", null, "我是HelloWorld的插槽默认值")
      ])
    }
  }
</script><style lang="scss" scoped></style>

5.JSX

  • 如果要在项目中使用jsx,就需要添加jsx的对应支持

    • jsx通常是用babel进行转换
    • 在Vue中,只需要在Babel配置对应的插件就可以了
  • 安装jsx插件

npm install @vue/babel-plugin-jsx -D
  • 在babel.config.js配置文件中配置插件
//babel.config.js
module.exports = {
  presets: [
    "@/vue/cli-plugin-babel/preset"
  ],
  plugins: [
    "@vue/babel-plugin-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>

jsx组件和插槽使用

  • HelloWorld.vue
<script>
  export default {
    render() {
      return(
        <div>
          <h2>helloworld</h2>
          {this.$slots.default ? this.$slots.default() :            <span>默认值</span>}
        </div>
      )
    }
  }
</script>