全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-08-25_实用的VUE系列——这次用vueuse 学到了两个有意思的干货!!!

您的位置:首页 >> 新闻 >> 行业资讯

实用的VUE系列——这次用vueuse 学到了两个有意思的干货!!! 点击关注公众号,“技术干货”及时达!前言vueuse 是近几年非常火的一个工具库, 确切的说,「他是一款基于 Vue 组合式 API 的函数工具集」。 当然,你也可以叫他vue-hooks 工具集,并且他还人性化的支持了vue2 于是他顺利的勾起了我的好奇心,决心研究一番 让我主要好奇的有两个问题, 1、他到底怎么支持 vue2 又支持 vue3 的2、他到底是怎样实现暗黑模式的接下来我们一个个来研究 1、他到底怎么支持 vue2 同时又支持 vue3 的这个问题,很有意思,在最开始研究源码的时候,我以为他是写了两套,在不同的项目中导入不同的 vue 源码 然而细究之后,我发现,有趣,真的是有趣,他用了一个更有意思的库vue-demi vue-demi?Vue Demi 是一款开发工具。允许你为 Vue 2 和 3 编写通用 Vue 库。而无需担心用户安装的版本。 ?于是我又好奇的研究了一下 vue-demi vue-demi的实现更有趣,他的核心原理是利用用户安装的版本,在项目install 的时时候始化下载对应的 vue 版本导出, 于是当我们引用 vue-demi 的时候,其实就是引用我们当前项目中的vueapi, 他只是很巧妙的做了一个承上启下 我简单的画个图,来帮助大家更快的理解 上图中,我们可以发现,他的本质,就是动态的更改package.json的入口文件内容来解决通用的问题 不信?,我们可以在源码中来验证 在源码中的package.json中发现脚本 执行了postinstall 函数 "scripts": { "postinstall": "node -e \"try{require('./scripts/postinstall.js')}catch(e){}\"", }, 在当前函数中拿到 vue 的版本开始更改package.json中的入口文件内容 const { switchVersion, loadModule } = require('./utils') // 拿到node_modules中的vue内容 const Vue = loadModule('vue') if (!Vue || typeof Vue.version !== 'string') { console.warn('[vue-demi] Vue is not found. Please run "npm install vue" to install.') } // 不同版本执行不同的逻辑 else if (Vue.version.startsWith('2.7.')) { switchVersion(2.7) } else if (Vue.version.startsWith('2.')) { switchVersion(2) } else if (Vue.version.startsWith('3.')) { switchVersion(3) } else { console.warn(`[vue-demi] Vue version v${Vue.version} is not supported.`) } 接下来更改的实际方法如下 const fs = require('fs') const path = require('path') const dir = path.resolve(__dirname, '..', 'lib') function loadModule(name) { try { return require(name) } catch (e) { return undefined } } function copy(name, version, vue) { vue = vue || 'vue' // 当前是vue3 的版本,拿到vue3 的目录, const src = path.join(dir, `v${version}`, name) const dest = path.join(dir, name) // 读取内容 let content = fs.readFileSync(src, 'utf-8') content = content.replace(/'vue'/g, `'${vue}'`) try { fs.unlinkSync(dest) } catch (error) { } // 写入内容 fs.writeFileSync(dest, content, 'utf-8') } function updateVue2API() { const ignoreList = ['version', 'default'] const VCA = loadModule('@vue/composition-api') if (!VCA) { console.warn('[vue-demi] Composition API plugin is not found. Please run "npm install @vue/composition-api" to install.') return } const exports = Object.keys(VCA).filter(i = !ignoreList.includes(i)) const esmPath = path.join(dir, 'index.mjs') let content = fs.readFileSync(esmPath, 'utf-8') content = content.replace( /\/\*\*VCA-EXPORTS\*\*\/[\s\S]+\/\*\*VCA-EXPORTS\*\*\//m, `/**VCA-EXPORTS**/ export { ${exports.join(', ')} } from '@vue/composition-api/dist/vue-composition-api.mjs' /**VCA-EXPORTS**/` ) fs.writeFileSync(esmPath, content, 'utf-8') } function switchVersion(version, vue) { // 更改文件的函数 copy('index.cjs', version, vue) copy('index.mjs', version, vue) copy('index.d.ts', version, vue) // 如果是非2.7的内容,因为要引入composition-api 所以要做个兼容 if (version === 2) updateVue2API() } module.exports.loadModule = loadModule module.exports.switchVersion = switchVersion 上述方法,就是简单将页面中的 v3 源码 直接挪到了最外部 我们看下我刚断点时候的v3源码 // 引用全部的vue3源码 var Vue = require('vue') // 全部导出 Object.keys(Vue).forEach(function(key) { exports[key] = Vue[key] }) // 添加几个方法 exports.set = function(target, key, val) { if (Array.isArray(target)) { target.length = Math.max(target.length, key) target.splice(key, 1, val) return val } tarGET@[key] = val return val } exports.del = function(target, key) { if (Array.isArray(target)) { target.splice(key, 1) return } delete tarGET@[key] } // 导出 exports.Vue = Vue exports.Vue2 = undefined exports.isVue2 = false // 由于是直接替换的,直接写死 exports.isVue3 = true exports.install = function(){} 我们发现,所谓的vue-demi添加了几个简单的变量之外,几乎全部用的是 vue3 的原生api 于是我们只需要在vueuse 中如此引用即可 import { watch, computed, Ref, ref, set, del, nextTick, isVue2, } from 'vue-demi' 上述代码就能无缝的抹平 vue2 和 vue3 的所有差异 他的基本使用方式vueuse 采用monorepo 方式构建,他包含很多插件,分别散落在 `@vueuse/head`[1],`@vueuse/motion`[2],`@vueuse/gesture`[3],`@vueuse/sound`[4] 等内容中,本质上来说,这些插件就是在别的库的基础上包裹一层而已 这不是我们这次要研究的内容,我们要研究的内容是@vueuse/core 也就是hooks 函数 首先我们还是要国际惯例,看看怎样使用 // 安装 npm i @vueuse/core 由于都是函数我们只需要引入执行即可,基本不需要做多余配置 script setup import { useLocalStorage, useMouse, usePreferredDark } from '@vueuse/core' // 跟踪鼠标位置 const { x, y } = useMouse() // 用户是否喜欢暗黑主题 const isDark = usePreferredDark() // 在本地存储中持久化状态 const store = useLocalStorage( 'my-storage', { name: 'Apple', color: 'red', }, ) /script 好了,使用方式讲完了,我们可以来研究源码了,细细研究之下,我瞬间感觉我发现了宝藏 这里我们选取了 6 个业务中常会遇到的需求,从源码角度学习加他们的设计思路 2、 怎样实现暗黑模式的 useDark 原理vueuse 中有一个暗黑模式useDark,相信我们在业务中会经常使用 那么他是怎么实现的呢? 在最开始我以为所谓的 useDark 应该是一肩挑,我只需要简单的使用,就能自动的实现暗黑模式 import { useDark, useToggle } from '@vueuse/core' const isDark = useDark() const toggleDark = useToggle(isDark) 然后我发现,他竟然...... 只是在跟标签中添加了类似于黑白主题的样式而已 于是细细研究他的源码发现,朴实无华, 但又颇有深意 我们来看源码 export function useDark(options: UseDarkOptions = {}) { // 传入默认值 const { valueDark = 'dark', valueLight = '', window = defaultWindow, } = options debugger //创建并管理当前页面的颜色模式,适用于不同类型的主题切换需求。 const mode = useColorMode({ ...options, onChanged: (mode, defaultHandler) = { if (options.onChanged) options.onChanged?.(mode === 'dark', defaultHandler, mode) else defaultHandler(mode) }, modes: { dark: valueDark, light: valueLight, }, }) //获取系统主题 const system = computed(() = { if (mode.system) { return mode.system.value } else { // 兼容 vue2 问题 const preferredDark = usePreferredDark({ window }) return preferredDark.value ? 'dark' : 'light' } }) // 返回最终的计算属性的主题模式 const isDark = computedboolean({ get() { return mode.value === 'dark' }, set(v) { const modeVal = v ? 'dark' : 'light' if (system.value === modeVal) mode.value = 'auto' else mode.value = modeVal }, }) return isDark } 上述问题,主要就解决了一个问题,获取页面的是否是暗黑模式,而获取的方式为usePreferredDark usePreferredDark 其实是一个特别简单的函数 export function usePreferredDark(options?: ConfigurableWindow) { // 主要用了媒体查询函数 return useMediaQuery('(prefers-color-scheme: dark)', options) } 于是我又转而研究useMediaQuery函数 /** * 使用给定的媒体查询字符串来检查它是否与当前窗口的尺寸匹配 * 请注意,此函数依赖于浏览器的 window 对象和 matchMedia API,如果在没有 window 对象或 matchMedia API 的环境中使用,它将无法正常工作。 */ export function useMediaQuery(query: MaybeRefOrGetterstring, options: ConfigurableWindow = {}) { const { window = defaultWindow } = options // 检查当前环境是否支持 matchMedia API const isSupported = useSupported(() = window && 'matchMedia' in window && typeof window.matchMedia === 'function') let mediaQuery: MediaQueryList | undefined const matches = ref(false) // 主题变化回调函数 const handler = (event: MediaQueryListEvent) = { debugger matches.value = event.matches } // 卸载事件监听 const cleanup = () = { if (!mediaQuery) return if ('removeEventListener' in mediaQuery) mediaQuery.removeEventListener('change', handler) else // @ts-expect-error deprecated API mediaQuery.removeListener(handler) } const stopWatch = watchEffect(() = { if (!isSupported.value) return cleanup() // matchMedia 函数 mediaQuery = window!.matchMedia(toValue(query)) if ('addEventListener' in mediaQuery) mediaQuery.addEventListener('change', handler) else // @ts-expect-error deprecated API mediaQuery.addListener(handler) matches.value = mediaQuery.matches }) //在当前活跃的 effect 作用域上注册一个处理回调函数。当相关的 effect 作用域停止时会调用这个回调函数。 tryOnScopeDispose(() = { stopWatch() cleanup() mediaQuery = undefined }) // 返回最后的结果 return matches } 翻山越岭之后懂了,看似上述代码搞了一大堆,本质上就是利用matchMedia 来查询页面是不是暗黑模式 顺着这个知识点,我又去了解了一下matchMedia matchMediaWindow 的 matchMedia() 方法返回一个新的 MediaQueryList 对象,表示指定的媒体查询字符串解析后的结果。返回的 MediaQueryList 可被用于判定 Document 是否匹配媒体查询,或者监控一个 document 来判定它匹配了或者停止匹配了此媒体查询。 这是 mdn 的官话,可能官话各位 jym 都听不懂,我来简单的翻译一下 他就是能查询当前是否命中了当前这个媒体查询内容 比如 @media (prefers-color-scheme: dark) { .day.dark-scheme { background: #333; color: white; } .night.dark-scheme { background: black; color: #ddd; } } 等等等等,有的 jym有问了,这个prefers-color-scheme 啥意思呢? prefers-color-scheme「prefers-color-scheme」CSS[5]媒体特性[6]用于检测用户是否有将系统的主题色设置为亮色或者暗色。 其实翻译过来,他就是用媒体查询写一个规则,当是亮色主题的时候我们用什么 css 样式,暗色主题的时候用什么样式,仅此而已! 「但是,由于他本身的特性,就能被我们利用来判断当前页面的主题是不是夜间模式」 当以上媒体查询命中的时候,matchMedia() 就会返回命中的结果我们只需要 window.matchMedia('(prefers-color-scheme: dark)') 当 matches 为 true 的时候,就表示当前为暗黑模式 说白了,这个useDark 其实就是修改和获取页面主题的,对于我们自定义主题还相去甚远 「那我们要怎么动态修改页面模式呢?」 方案很简单,根据useDark 对于 dom 的更改动态匹配 然后问题又来了,我该怎么匹配呢? 其实方案有很多,我们来看看vueuse 是怎么实现的吧 首先一个color-scheme 映入眼帘 color-scheme「color-scheme」CSS 属性允许元素指示它可以舒适地呈现哪些颜色方案。 官方讲了一大堆,我们直接用中文翻译过来,更直观颜色方案 就是一个 css 属性 他具备以上属性值 color-scheme: normal; //没有 color-scheme: light; // 白天模式 color-scheme: dark; // 暗黑模式 color-scheme: light dark; // 跟随系统的模式 于是有的jym就问了,我都在根标签添加dark的 class 了我直接根据 class 直接设置不就行了吗? 为什么还要「多此一举」 原因很简单,局部的滚动条你解决不了,这个属性能解决滚动条的颜色,所以这是要设置暗黑模式必备的属性 接下来就是常规的 css 变量方案了, 我们声明一个日常 bg 变量,以及一个 dark 变量,当 dark 的是时候覆盖 root :root { --vp-c-bg: #ffffff; --vp-c-bg-alt: #f6f6f7; --vp-c-bg-elv: #ffffff; --vp-c-bg-soft: #f6f6f7 } .dark { --vp-c-bg: #1b1b1f; --vp-c-bg-alt: #161618; --vp-c-bg-elv: #202127; --vp-c-bg-soft: #202127 } 以上示例就完整的介绍了,暗黑模式的的方案,接下来给大家完成的实例演示: template button @click="toggleDark()" i inline-block align-middle i="dark:carbon-moon carbon-sun" / span class="ml-2"{{ isDark ? 'Dark' : 'Light' }}/span /button h1 class="h1"我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 我要好好学习天天向上 /h1 /template script setup lang="ts" import {useDark, useToggle} from '@vueuse/core' const isDark = useDark({ valueDark: 'dark', valueLight: 'light', }) const toggleDark = useToggle(isDark) /script style /* 暗黑模式必备,防止滚动条颜色问题 */ html.dark { color-scheme: dark; } :root { --vp-c-bg: #ffffff; --vp-c-text-1: rgba(60, 60, 67); } .dark { --vp-c-bg: #1b1b1f; --vp-c-text-1: rgba(255, 255, 245, .86); } /* 设置主题 */ body{ color: var(--vp-c-text-1); background-color: var(--vp-c-bg); } .h1{ height: 150px; overflow: auto; } /style 效果如下: 以上代码中,看似复杂,其实很简单,只是根据全局dark 覆盖原有样式,来实现背景以及文字的「不同模式」 最后干货研究完了,收获颇丰,希望和各位 jym共同进步!! 点击关注公众号,“技术干货”及时达! 阅读原文

上一篇:2019-10-02_微软出品,文科生也能学得懂的Python免费入门视频 下一篇:2020-12-08_社区商业的人间烟火气

TAG标签:

11
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价