参考:https://www.bilibili.com/video/BV1dS4y1y7vd?p=41&vd_source=f37aed835423b3341b4323bc48e10402
实现一个函数同时支持 hook 和 directive 去监听 dom 宽高的变化
- 如何监听dom宽高变化
- 如何用vite打包库
- 如何发布npm
基本准备
-
初始化 packge.json 文件:
npm init -y
-
下载 Vue、vite 插件:
npm install vue vite -D
注意:这个插件是专门为 Vue 提供的,所以没有必要安装插件的时候再去安装Vue
-
初始化 ts 配置文件:
tsc --init
-
最终目录结构:
监听dom宽高变化
-
编写 hook 和 directive 函数: src/index.ts
import type {App, DirectiveBinding} from 'vue' /** * 监听元素宽高变化 * @param el {HTMLElement} - 需要监听宽高变化的元素 * @param callback {Function} - 回调函数 * - entries {ResizeObserverEntry[]} * - observer {ResizeObserver} ResizeObserver实例 * @returns void */ function useResizeObserver(el: HTMLElement, callback: (entries: ResizeObserverEntry[], observer: ResizeObserver) => void): void { // MutationsObserver API 侦听DOM树变化(子集、属性、增删改查) // Resize Observer API 侦听元素宽高变化 // IntersectionObserver API 侦听元素交叉区域变化,也就是元素是否在视口内,常用于图片懒加载、虚拟列表 // 三者使用上是一样的 let resize = new ResizeObserver((entries) => { callback(entries, resize) }) resize.observe(el) } /** * 编写Vue自定义指令 * @param app {App} - Vue实例,通过 Vue.use(useResizeObserver) 安装的时候传入 */ const install = (app: App) => { app.directive('resize-observer', { mounted(el: HTMLElement, binding: DirectiveBinding) { useResizeObserver(el, binding.value) } }) } useResizeObserver.install = install export default useResizeObserver
使用vite打包库
-
配置 vite 打包信息: vite.config.ts
import {defineConfig} from 'vite'; // es module 语法 // umd 支持 amd cmd cjs 全局变量模式 export default defineConfig({ // https://cn.vitejs.dev/config/build-options.html#build-lib build: { lib: { entry: 'src/index.ts', // 入口文件 name: 'useResizeObserver', // 设置为你想要暴露的全局变量名 }, // 给 rollup 透传 external rollupOptions: { external: ['vue'], // 不想打包进库的依赖 output: { // 提供一个全局变量 globals: { useResizeObserver: 'useResizeObserver', } } } } })
-
声明库: index.d.ts
import type { App } from 'vue' declare const useResizeObserver: { (el: HTMLElement, callback: (entries: ResizeObserverEntry[], observer: ResizeObserver) => void): void install: (app: App) => void } export default useResizeObserver
-
配置 package.json
{ "scripts": { "build": "vite build" }, }
-
build:
npm run build
执行成功后会生成dist目录,里面有两个文件
发布npm
-
配置 package.json: 注意 main 和 module 的文件要对应 dist 打包的结果文件。
{ "name": "dom-resize-vivian", "version": "0.0.1", "main": "dist/dom-resize-vivian.umd.js", "module": "dist/dom-resize-vivian.mjs", "files": [ "dist", "index.d.ts" ], }
通常,
main
字段应该指向 CommonJS 规范的入口文件,而module
字段应该指向 ES 模块规范的入口文件文件解释:
-
umd.js:支持 Common.js,也就是Node.js 规范。当我们 require 的时候,就会去找 main 对应的文件 dom-resize-vivian.umd.js
-
mjs:支持 import 和 export。当我们 import 的时候,就会去找 module 对应的文件 dom-resize-vivian.mjs
files:要向npm发布的目录
version: 版本号
-
-
npm 操作
-
添加 npm 账号:
npm adduser
-
登录 npm:
npm login
-
发布:
npm publish
报错:
这是因为包名冲突,将 package.json 的 name 和 main 、module 都修改下,
dom-resize
改成dom-resize-vivian
再重新发布就能够成功了
-
使用 dom-resize-vivian
- 下载依赖:
npm i dom-resize-vivian
- 编写测试代码
- 测试 hook
<template> <div class="resize" ref="resizeRef"></div> </template> <script setup lang="ts"> import {ref, onMounted} from 'vue' import useResizeObserver from 'dom-resize-vivian' const resizeRef = ref<HTMLDivElement>() onMounted(() => { useResizeObserver( resizeRef.value as HTMLDivElement, (entries: ResizeObserverEntry[], observer: ResizeObserver) => { console.log(entries, observer) }, ) }) </script> <style lang="scss"> .resize { width: 200px; height: 300px; background-color: red; resize: both; overflow: hidden; } </style>
- 测试自定义指令
main.tsimport useResizeObserver from 'dom-resize-vivian' const app = createApp(App) app.use(useResizeObserver)
<template> <div class="resize" v-resize-observer="onChange"></div> </template> <script setup lang="ts"> const onChange = (entries: ResizeObserverEntry[], observer: ResizeObserver) => { console.log(entries, observer) } </script> <style lang="scss"> .resize { width: 200px; height: 300px; background-color: red; resize: both; overflow: hidden; } </style>
- 测试 hook