vue3的一些知识点plus--4

二十六,hooks使用

hooks,复用代码进行封装,钩子函数。和vue2中的mixins相似,将共同部分抽离出来。也有开源的库,vueUse,包含各种hooks,可以在 官网 查看。

我们自己怎么编写呢,要知道hook底层就是个函数,返回promise方便我们后续then处理,函数里面就是具体功能的实现了,同时也有onMounted等生命周期处理不同钩子下的内容。

// 自定义hooks
import {onMounted} from 'vue'

type Options = {
  el:string
}

export default function(options:Options):Promise<{baseurl:string}>{
  return new Promise((resolve)=>{
    onMounted(()=>{
      let img:HTMLImageElement = document.querySelector(options.el) as HTMLImageElement
      img.onload = ()=>{
        resolve({
          baseurl:base64(img)
        })
      }
    })

    const base64 = (el:HTMLImageElement)=>{
      const canvas = document.createElement('canvas')
      const ctx = canvas.getContext('2d')
      canvas.width = el.width
      canvas.height = el.height
      //参数:使用的图像,剪切x,剪切y,图像宽,图像高,画布x,画布y,使用图像的宽,使用图像的高
      ctx?.drawImage(el,0,0,canvas.width,canvas.height)
      return canvas.toDataURL('image/jpeg')
    }
  })

}

我们编写一个图片转 base64 的 hook 叫 useBase64 ,然后的是一个promise函数,里面已经在onMounted中获取dom元素,然后canvas转换,返回结果;那在使用的时候就简单了。

import useBase64 from './index'

useBase64({
  el:'#img'
}).then((res)=>{
  console.log(res.baseurl);
})

二十七,全局变量和方法

全局的变量和方法,那就是在入口文件,main.ts中进行定义了,可以挂载到app(createApp)上,这样全局都可以访问到,之前全局定义的组件也是在main.ts中。

vue2中是,Vue.prototype.$xxx=xxx
在vue3中,对象有些不同,是在app.config.globalProperties上

// 定义全局变量和方法
app.config.globalProperties.$env = 'dev'
app.config.globalProperties.$filter = {
  format<T>(str:T){
    return 'myfilter-'+str
  }
}
type Filter = {
  format<T>(str:T):string
}
// 对应是声明文件
declare module 'vue'{
  export interface ComponentCustomProperties{
    $filter:Filter,
    $env:string
  }
}

在使用中,template中可以直接进行使用,在script中需要通过实例获取得到。

// js
var app = getCurrentInstance()
console.log(app?.proxy?.$filter.format('好好'));

// html
<div>{{ $env }}</div>
<div>{{ $filter.format('111') }}</div>

二十八,样式框架使用 elplus

vue3 支持新版样式框架,支持ts,这里介绍的是 element-plus。

安装,引入,use注册,然后使用,不多介绍;写法和按需引入官网都有。

import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
app.use(ElementPlus);

这里对scoped进行一个解释,html单页面,css需要进行模块化,避免样式错乱。
规则:

1,给html的dom节点加一个不重复的data属性,(data-v-123)来表示唯一性。

2,在每句css选择器末尾(生成之后的)加一个当前组件的data属性,来私有化。
3,如果组件内部包括其他组件,只会给其他组件的最外层加上当前的data属性。

有时候 2,3 会冲突,因为data属性是加在末尾的,组件里面的层次没有data属性,无法命中,所以提供了一个样式穿透vue2--> /deep/   vue3--> :deep(选择器)

 源码里,scoped是通过postCss中的一个插件来处理的,接收一个css文件和一个api来分析,修改它的规则(css抽象树的方式),跟babel类似。
还有 :slotted() 插槽,:global() 全局 , v-bind() 动态修改css 的用法。

1,:slotted(选择器),处理插槽中的样式。

// html
<slot></slot>

// css
:slotted(.a) {
  color: red;
}

2,:global(选择器) ,处理全局的样式

:global(div) {
  background-color: antiquewhite;
}

 3,v-bind(),参数如果是值,直接使用;如果是对象,需要引号包裹

// js
let style1 = ref('blue')
let style = ref({
  color: "pink",
});

// css
.dym {
  background-color : v-bind(style1); /* 如果是值,直接使用 */
  color: v-bind("style.color"); /* 如果是对象,使用引号包裹 */
}

也可以使用 module 模块化的形式,类似于react。给style定义module名称,然后对象形式访问。
css module 使用,默认叫$style ,可以自定义名字,使用名字.

// html
<div :class="[mName.dym2, mName.border]">
  css module 使用,默认叫$style ,可以自定义名字,使用名字
</div>

<style module="mName">
.dym2 {
  color: blueviolet;
}
.border {
  border: 1px solid #2cc;
}
</style>

这里介绍一个库 tailwindcss,js编写css, postcss解析,更加的快捷,不过打包编译时间也会增加。

二十九,编写全局插件,app.use() 注册

定义一个loading组件,通过封装为一个插件,可以在全局注册使用。要知道,能够被app.use()注册的应该具有什么,首先导出一个对象,也可以是个函数,必须具有install()的方法,因为app.use()就是会调用函数或者对象里的install来实现注册。

需要将loading组件引入,然后使用 createVNode(loading)来生成vNode类型,抽象元素,然后render(VNode,document.body)挂载在body下,最后,app.config.globalProperties 上挂载,vNode.component?.exposed?.xxx 可以得到exposed暴露出来的属性方法。install会返回app参数,main.ts中的app。

// 可以导出一个对象,也可以是函数
// 通过在main.ts中使用 app.use()进行注册

import type { App, VNode } from "vue";
import loading from "./index.vue";
import { createVNode, render } from "vue";

// 1,对象里必须有install,会进行调用
export default {
  // install 会回传一个app函数,(main.ts中的全局app)
  install(app: App) {
    // 需要将 组件 挂载到全局,vNode类型
    const vNode: VNode = createVNode(loading);
    // 挂载 render 函数 (vnode,挂载点)
    render(vNode, document.body);
    // 可以获取到 component 的方法
    // vNode.component.setupState 可以获得到所有的函数,最好使用 exposed 访问暴露出的方法

    // 可以全局挂载
    app.config.globalProperties._loading = {
      show: vNode.component?.exposed?.show,
      hide: vNode.component?.exposed?.hide,
    };

    console.log(vNode, vNode.component?.exposed);
  },
};

然后 main.ts 中引入loading,app.use(loading)注册。用以下方法就可以使用了。

var instance = getCurrentInstance()
instance?.proxy?._loading.show()
setTimeout(() => {
  instance?.proxy?._loading.hide()
}, 3000);

三十,nextTick的使用

知识准备:

event loop,js是单线程,不然同时操作dom会出现问题。h5中的web workder支持多线程,也不能操作dom,一般是将费时的任务分出去,优化体验。
js 因为单线程,会出现很多排队,会等待很久,所以出现了异步。

同步:代码从上到下按顺序执行。
异步:分为宏任务和微任务。
           宏任务:script,setTimeout,setInterval,ui交互,postMessage,Ajax
           微任务:promise then catch finally,mutationObserver(监听dom变化),process.nextTick(nodejs环境),await。

同步任务在主进程形成一个执行栈,除此之外,还有一个任务队列。异步任务先执行宏任务,然后清空当次宏任务的所有微任务。 
nextTick 就是创建下一个异步任务,等到同步任务执行完毕后执行。

我们先来看个例子,看和输出结果一样吗:

async function Prom() {
  console.log("X");
  await Promise.resolve(); // 微任务
  console.log("Y");
}

setTimeout(() => {
  console.log(1);
  Promise.resolve().then(() => {
    console.log(2);
  });
}, 0);

setTimeout(() => {
  console.log(3);
  Promise.resolve().then(() => {
    console.log(4);
  });
}, 0);

Promise.resolve().then(() => {
  console.log(5);
});
Promise.resolve().then(() => {
  console.log(6);
});
Promise.resolve().then(() => {
  console.log(7);
});
Promise.resolve().then(() => {
  console.log(8);
});

Prom();
console.log(0);


// 执行结果: X 0 5 6 7 8 Y 1 2 3 4

nextTick() 的执行原理就是 flushJobs() 函数。
执行watch的对应队列,flush:pre的,顺序队列,父级在子级前创建,然后执行flush:post的回调。
永远返回promise,微任务,所以会先走上面的任务,然后走微任务,nextTick。

const change = async () => {
  message.value = "小小";
  console.log(div.value?.innerText, "同步情况下打印的值并没有改变");  // 原始
  await nextTick();
  console.log(div.value?.innerText, "异步情况下打印的值改变了");  // 小小
  for (let i = 0; i < 5; i++) {
    num.value = i;
  }

  for (var i = 0; i < 5; i++) {
    setTimeout(() => {
      console.log(i);
    }, 0);
  }
  // 5 5 5 5 5
};

三十一,unocss 原子化css ☆

unocss,目的是实现原子化css,可以在html中直接写css内容,动态根据你的规则生成对应的css,可以简化css的编写,也可以在熟练后更清晰的查看html。我形容的是我的理解,更深入的了解请到官网。

安装unocss,然后在 vite.config.ts 中引入注册。这里可以配置一些预设,也可以根据规则自定义

  • presetIcons图标库,需要安装库@iconify-json (icones官网 https://icones.js.org/ 找一套)
  • presetAttributify这个预设,就可以直接在标签上使用 属性 方式。
  • presetUno 工具类,可以直接使用,比如tailwind里的属性。
import unoCss from 'unocss/vite'

plugins: [
    unoCss({
      // 预设
      /* presetIcons图标库,需要安装库@iconify-json (icones官网 https://icones.js.org/ 找一套)
         presetAttributify这个预设,就可以直接在标签上使用 属性 方式。
         presetUno 工具类,可以直接使用,比如tailwind里的属性。
      */
      presets:[presetIcons(),presetAttributify(),presetUno()],
      rules:[
        ['flex',{display:'flex'}],
        ['red',{color:'red'}],
        [/^m-(\d+)$/,([,d])=>({margin:`${Number(d)*10}px`})]
      ],
      shortcuts:{
        cike:['flex','red','m-3']
      }
    })
  ],

然后使用的方法,可以是class,也可以是属性形式,还可以使用一些内设好的样式。

  <div class="flex red m-1">刺客</div>
  <div class="cike">刺客2</div>
  <div m="2" red>刺客3</div>
  <div class="bg-red-100">tailwind</div>
  图标使用:
  <div class="i-ic-baseline-add-shopping-cart"></div>

现阶段还是试验阶段,是否可以使用到团队里,进行规则的规范也很重要,可以多了解一些tailwind和unocss的关系。

 三十二,h函数

h函数,是vue中插件虚拟节点的辅助函数。接收三个参数,标签名,属性对象,子节点
这里和 createVNode() 方法一样的,render进行渲染。

  <btn @on-click="getBtn">
    <template #default>232</template>
  </btn>


// h 函数
type Props = {
  text?: string;
};

const btn = (props: Props, ctx: any) => {
  return h(
    "div",
    {
      class: ["rounded", "bg-green-400", "text-gray-50", "text-center", "p-2"],
      onClick: () => {
        ctx.emit("on-click", "按钮click");
      },
    },
    // props.text
    ctx.slots.default()
  );
};
var getBtn = (str: string) => {
  console.log(str);
};

三十三,了解use函数基础的实现

之前的例子里,写了一个loading插件,通过app.use()来注册的,那我们来了解一下use的基础实现

要知道,之前强调一定要有install函数,接收的参数是 插件和options配置

import type {App} from 'vue'
// 引入 app
import {app} from './main'

// 对 泛型T 做个约束,必须有install函数
interface Use{
  install:(app:App,...options:any[])=>void
}

// 进行缓存,避免重复的添加
const installList = new Set()

export function MyUse<T extends Use>(plugin:T,...options:any[]){

  if(installList.has(plugin)){
    console.error('已经注册',plugin);
  }else{
    plugin.install(app,...options)
    installList.add(plugin)
  }

  // 这里可以进行链式调用。
  return app

}

通过一个set来管理插件,避免重复,如果重复,给出提示;如果没有重复,执行插件的install方法,然后加入到set里,最后,返回app,可以支持链式调用。类似于app.use(A).use(B)的用法也是很常见的。

import { MyUse } from "./myUse";

MyUse(Loading)

这样测试也是可以实现loading的使用的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值