当前位置:   article > 正文

深入vue3学习(3)_setup在beforecreated之前执行那为什么没有created这个生命周期

setup在beforecreated之前执行那为什么没有created这个生命周期

生命周期

onX

   onMounted(()=>{
      console.log('123');
    })
  • 1
  • 2
  • 3

setup没有create和beforeCreated这两个声明周期,因为这两个做的事情可以在setup中做,而其他生命周期就是onX的i形式出现。

自定义hooks

我们知道vue2的痛点就是逻辑都写一起了,只要业务一复杂,代码量大。vue3的好处就是能将功能拆分出来,形成每个文件,这样就将代码逻辑抽离出来,一个功能一块代码。
比如我们现在做一个计数器,那么我们可以

import {ref, Ref} from 'vue'

export const useConter = () => {

    const state = ref<number>(123)

    const add = () => {
        state.value++
    }

    return {state, add}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

创建一个文件,写一个hooks
然后

 setup(props, context) {
    const { state, add } = useConter()
    return {  state, add };
  },
  • 1
  • 2
  • 3
  • 4

回到app.vue中在setup中使用。
这样我们的计数器这块的功能代码就已经被抽离出去了。
改变title


export const useTitle = (title: string) => {
    const titleRef = ref<string>(title)

    watch(titleRef, (n: string) => {
        document.title = n
    })

    return { titleRef }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可以看到,我们需要依赖watch的值监听n的改变才会改变document.title,这也是与react不同的一点,react只要值改变了就会重新刷新组件,所以会自动更新其依赖的值,而vue不会,如果你将document.title = titleRef.value,当其值改变的时候,document.title并不会刷新,所以需要依赖watch来手动改变它的值。
比如

export const useTitle = (title: string) => {
    const titleRef = ref<string>(title)
    document.title = titleRef.value
 

    return { titleRef }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这样写,当titleRef改变的时候,其不会运行。响应式只会去修改template上的内容,不会重新刷新该hooks。这也是与react相差很大的一点。

实验性语法

vue3还有一个大胆的实验性语法

<script lang="ts" setup>
import { ref } from 'vue'
import AA from './component/aa.component.vue'

const test = ref('test')

</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在script上面加上setup,那么就不用返回了,这个定义的变量或者引入的组件可以直接在template使用,而
props和emit则是这样使用

<script lang="ts" setup>
import { ref, defineEmits, defineProps } from 'vue'
import AA from './component/aa.component.vue'

const test = ref('test')

const props = defineProps({
  test: String
})

const emit = defineEmits(['in', 'on'])

emit('in', '传过去的值')

</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

通过defineProps还有defineEmits来使用props和emit。不过这个还是实验性语法,不知道会不会正式被更新。

高级特性 H函数

vue需要使用模板来创建Html,但是在某种特殊情况上需要使用js来,那么就需要渲染函数,它比模板更接近编译器。
template会被渲染函数生成对应的vnode,如果想用js的编程能力,可以自己来编写createVnode函数,生成对应的vNode。
h函数就是用来创建vnode的,准备的命名应该是CreateVnode函数,但是为了简便就称为h函数。

h接受三个参数,(了解)

第一个: 标签名 组件 异步组件 函数组件等等 必须

第二个: props 可选

第三个:string array object 子vnode 另一个h()等等 可选

import {defineComponent, h} from 'vue'
export default defineComponent({


    render(){
        return h('h2', {class: 'aa'}, '123123')
    }

})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

渲染出来就是

<h2 class="aa" data-v-55c42084="">123123</h2>
  • 1

setup代替render

export default defineComponent({
  setup() {
    return () => {
      return h("h2", { class: "aa" }, "123123");
    };
  },
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

只要返回的时候返回一个函数,该函数返回一个vnode即可代替render。

插槽的使用

import CC from "./cc.component.vue";
export default defineComponent({
  setup() {
    return () => {
      return h(
        CC,
        {},
        {
          default: (props) => h("span", null, `插槽${props.name}呀`),
        }
      );
    };
  },
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

将default这个插槽传进去cc

import { h, defineComponent } from "vue";
export default defineComponent({
  setup(props, context) {
    return () => {
      return h("div", null, [
        h("h2", null, "cc"),
        context.slots.default
          ? context.slots.default({ name: "我是cc传出去的" })
          : h("span", {}, "默认插槽"),
      ]);
    };
  },
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

cc里通过context.slots.default去拿到该函数并且调用返回vnode。

jsx的babel配置

在vue中使用jsx,只需要在Babel中配置对应的插件即可。
npm install @vue/babel-plugin-jsx -D
在babel.config.js文件里面配置

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  plugins: [
    '@vue/babel-plugin-jsx'
  ]
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
<script lang='tsx' >
import { h, defineComponent, render, ref } from "vue";
export default defineComponent({
  setup() {
    const state = ref<number>(0);
    return () => {
      return (
        <div>
          <h2>{state.value}</h2>
          <button
            onClick={() => {
              state.value++;
            }}
          >
            +
          </button>
        </div>
      );
    };
  },
});
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

有template就没有render,有render就没template
好家伙,我看到了react的影子。就是组件的传递这些还是用slot这些。

自定义指令

<input type="text" v-focus>
  • 1

这个v-focus就是自定义指令

export default defineComponent({
  name: "AA",
  components: {},
  props: {
    data: String,
  },

//自定义指令
  directives: {
    focus: {
      mounted(el, bindings, vnode, preVnode){   
        console.log('ipt标签已挂载');
        el.focus()
        console.log(el)
        console.log(bindings);
        console.log(vnode);
        console.log(preVnode);
        
         }
    }
  },

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

通过directives来定义自定义指令。
focus就是自定义指令,里面有几个生命周期,mounted就是v-focus对应的dom挂载的时候调用的。
el能拿到dom,然后调用.focus就可以获取焦点。
这是局部指令,只能在该组件使用。

修饰符

bindings是一个对象,可以拿到v-focus.ccc=“123”中的123的值和ccc的修饰符
vnode是该虚拟节点对象

全局指令

app.directive("focus", {
  mounted(el) {
    el.focus();
  },
});
  • 1
  • 2
  • 3
  • 4
  • 5

在main.ts中的app上挂载这方法,就可以在全局上使用。

其他生命周期

created 在绑定元素挂在前调用
beforeMounted
mounted
beforeUpdate
updared
beforeUnmount
unmount

封装一个自定义指令

import { App, onMounted } from "vue";

const formater = (app: App<Element>, key: string) => {
  app.directive(key, {
    mounted(el,bindings) {
      const textContent = el.textContent;
      const foramtter = bindings.value || 'HH-MM-YY'
      console.log('textContent', textContent);
      console.log('bindings', bindings.value);
      
      
    },
  });
};

export { formater }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在main.ts中使用即可


const app = createApp(App);
app
  .use(store, key)
  .use(router)
  .use(i18n)
  .use(ElementPlus)
  .mount("#app");

app.config.globalProperties.$tt = t;

//注册指令
formater(app, 'formater')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  <div v-formater="'hh-mm-cc'">12321321321</div>
  • 1

认识Teleport

我们知道,组件A在在组件B中使用的时候,那么a的template会被挂载到b的template的某个位置。
teleport的功能是将组件挂载到另一个app
类似于react的Portals
他有两个属性,
to:指定将其中的内容移动到目标元素,可以选择选择器
disabled:是否禁用teleport功能。

 <teleport to="#lin">aaa</teleport>
  • 1

这样aaa就会挂载在id为lin的组件上,与app同为兄弟节点,

Vue插件

我们刚才使用全局指令就是给全局添加一个功能,但其实vue也可以使用插件的模式。
有两种模式:
1 一个对象,但是必须包含install函数,会在安装插件时执行
2 函数类型, 这个函数会在安装插件时自动执行

const testPlugins = {
    install(){}
}

export { testPlugins }
  • 1
  • 2
  • 3
  • 4
  • 5

在main.ts中

app
  .use(store, key)
  .use(router)
  .use(i18n)
  .use(ElementPlus)
  .use(testPlugins)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用app.use()包裹起来,源码中会调用该对象的install方法并且执行。
就像 testPlugins.install(app)这样操作,将app传进去。

const testPlugins = {
    install(app:  App<Element>){
       app.config.globalProperties.name = '123'
        
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

app.config就是其配置属性,在globalProperties上面增加属性相当于在全局增加属性,也就是说在其他组件可以通过this.name拿到123这个值。

而想通过setup里面拿就比较麻烦

因为setup是没有this的,所以只能通过

  const instance = getCurrentInstance()
      console.log(instance?.appContext.config.globalProperties.$name);
  • 1
  • 2

getCurrentInstance可以拿到当前的组件实例,而全局属性只能从appcontext全局上下文中拿到,这样就能拿到我们定义的属性。

另一种plugin写法


const testPlugins = (app: App<Element>) => {
  app.config.globalProperties.$name = "123";
};
  • 1
  • 2
  • 3
  • 4

通过函数,vue内部还是会调用testPlugins(app)调用。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/69779
推荐阅读
相关标签
  

闽ICP备14008679号