股票配资网站建设,苏州网站建设丶好先生科技,监利网站建设,win2008怎么做网站2022年10月25日首发于掘金#xff0c;现在同步到公众号。11. 前言大家好#xff0c;我是若川。我倾力持续组织了一年多源码共读#xff0c;感兴趣的可以加我微信 lxchuan12 参与。另外#xff0c;想学源码#xff0c;极力推荐关注我写的专栏《学习源码整体架构系列》…2022年10月25日首发于掘金现在同步到公众号。11. 前言大家好我是若川。我倾力持续组织了一年多源码共读感兴趣的可以加我微信 lxchuan12 参与。另外想学源码极力推荐关注我写的专栏《学习源码整体架构系列》目前是掘金关注人数4.6k人第一的专栏写有20余篇源码文章。我们开发业务时经常会使用到组件库一般来说很多时候我们不需要关心内部实现。但是如果希望学习和深究里面的原理这时我们可以分析自己使用的组件库实现。有哪些优雅实现、最佳实践、前沿技术等都可以值得我们借鉴。相比于原生 JS 等源码。我们或许更应该学习正在使用的组件库的源码因为有助于帮助我们写业务和写自己的组件。如果是 Vue 技术栈开发移动端的项目大多会选用 vant 组件库目前2022-10-24 star 多达 20.3k。我们可以挑选 vant 组件库学习我会写一个组件库源码系列专栏[1]欢迎大家关注。这次我们来分析 vant4 新增的暗黑主题是如何实现的。文章中的 vant4 的版本是 4.0.0-rc.6。vant 的核心开发者是chenjiahan[2]一直在更新vant 。预计不久后就会发布 vant4 正式版。暗黑主题如图所示:暗黑主题也可以打开官方文档链接[3]自行体验。学完本文你将学到1. 学会暗黑主题的原理和实现
2. 学会使用 vue-devtools 打开组件文件并可以学会其原理
3. 学会 iframe postMessage 和 addEventListener 通信
4. 学会 ConfigProvider 组件 CSS 变量实现对主题的深度定制原理
5. 学会使用 vue/babel-plugin-jsx 编写 jsx 组件
6. 等等22. 准备工作看一个开源项目第一步应该是先看 README.md[4] 再看贡献文档 github/CONTRIBUTING.md[5]。不知道大家有没有发现很多开源项目都是英文的 README.md即使刚开始明显是为面向中国开发者。再给定一个中文的 README.md。主要原因是因为英文是世界通用的语言。想要非中文用户参与进来英文是必备。也就是说你开源的项目能提供英文版就提供。2.1 克隆源码贡献文档中有要求You will need Node.js 14[6] and pnpm[7].# 推荐克隆我的项目
git clone https://github.com/lxchuan12/vant-analysis
cd vant-analysis/vant# 或者克隆官方仓库
git clone gitgithub.com:vant-ui/vant.git
cd vant# Install dependencies
pnpm i# Start development
pnpm dev我们先来看 pnpm dev 最终执行的什么命令。vant 项目使用的是 monorepo 结构。查看根路径下的 package.json。2.2 pnpm dev// vant/package.json
{private: true,scripts: {prepare: husky install,dev: pnpm --dir ./packages/vant dev,},
}再看 packages/vant/package.json。// vant/packages/vant/package.json
{name: vant,version: 4.0.0-rc.6,scripts: {dev: vant-cli dev,},
}pnpm dev 最终执行的是vant-cli dev 启动了一个服务。本文主要是讲主题切换的实现所以我们就不深入 vant-cli dev 命令了。执行 pnpm dev 后命令终端输入如图所示可以发现是使用的是目前最新版本的 vite 3.1.8。pnpm-dev-vite这时我们打开 http://localhost:5173/#/zh-CN/config-provider。33. 文档网站打开后我们可以按 F12 和 vue-devtools 来查看vant 官方文档的结构。如果没有安装我们可以访问vue-devtools 官网[8]通过谷歌应用商店去安装。如果无法打开谷歌应用商店可以通过这个极简插件链接[9] 下载安装。VanDocHeader 组件mobile 端VanDocSimulator 组件3.1 通过 vue-devtools 打开组件文件打开 VanDocSimulator 组件文件如图所示我们通过 vue-devtools 打开 VanDocSimulator 组件文件。曾经在我的公众号若川视野 发起投票[10]分析这个功能的原理。感兴趣的小伙伴可以查看。我们可以看到 vant/packages/vant-cli/site/desktop/components/Simulator.vue 文件主要是 iframe 实现的渲染的链接是 /mobile.html#/zh-CN。我们也可以直接打开 mobile 官网[11] 验证下。// vant/packages/vant-cli/site/desktop/components/Simulator.vue
templatediv :class[van-doc-simulator, { van-doc-simulator-fixed: isFixed }]iframe refiframe :srcsrc :stylesimulatorStyle frameborder0 //div
/templatescript
export default {name: VanDocSimulator,props: {src: String,},// 省略若干代码
}3.2 desktop 端和打开 VanDocSimulator 类似我们通过 vue-devtools 打开 VanDocHeader 组件文件。打开了文件后我们也可以使用 Gitlens[12] 插件。根据 git 提交记录 feat(vant/cli): desktop site support dark mode[13]查看添加暗黑模式做了哪些改动。接着我们来看 vant/packages/vant-cli/site/desktop/components/Header.vue 文件。找到切换主题的代码位置如下模板部分// vant/packages/vant-cli/site/desktop/components/Header.vuetemplateli v-ifdarkModeClass classvan-doc-header__top-nav-itemaclassvan-doc-header__linktarget_blankclicktoggleThemeimg :srcthemeImg //a/li
/templateJS部分// vant/packages/vant-cli/site/desktop/components/Header.vuescriptimport { getDefaultTheme, syncThemeToChild } from ../../common/iframe-sync;export default {name: VanDocHeader,data() {return {currentTheme: getDefaultTheme(),};},watch: {// 监听主题变化移除和添加样式 classcurrentTheme: {handler(newVal, oldVal) {window.localStorage.setItem(vantTheme, newVal);document.documentElement.classList.remove(van-doc-theme-${oldVal});document.documentElement.classList.add(van-doc-theme-${newVal});// 我们也可以在这里加上debugger自行调试。debugger;// 同步到 mobile 的组件中syncThemeToChild(newVal);},immediate: true,},},methods: {// 切换主题toggleTheme() {this.currentTheme this.currentTheme light ? dark : light;},}
}/script3.3 iframe 通信 iframe-sync上文JS代码中有 getDefaultTheme, syncThemeToChild 函数引自文件 vant/packages/vant-cli/site/common/iframe-sync.js文件开头主要判断 iframe 渲染完成。// vant/packages/vant-cli/site/common/iframe-sync.jsimport { ref } from vue;
import { config } from site-desktop-shared;let queue [];
let isIframeReady false;function iframeReady(callback) {if (isIframeReady) {callback();} else {queue.push(callback);}
}if (window.top window) {window.addEventListener(message, (event) {if (event.data.type iframeReady) {isIframeReady true;queue.forEach((callback) callback());queue [];}});
} else {window.top.postMessage({ type: iframeReady }, *);
}后半部分主要是三个函数 getDefaultTheme、syncThemeToChild、useCurrentTheme。// 获取默认的主题
export function getDefaultTheme() {const cache window.localStorage.getItem(vantTheme);if (cache) {return cache;}const useDark window.matchMedia window.matchMedia((prefers-color-scheme: dark)).matches;return useDark ? dark : light;
}// 同步主题到 iframe 用 postMessage 通信
export function syncThemeToChild(theme) {const iframe document.querySelector(iframe);if (iframe) {iframeReady(() {iframe.contentWindow.postMessage({type: updateTheme,value: theme,},*);});}
}// 接收、使用主题色
export function useCurrentTheme() {const theme ref(getDefaultTheme());// 接收到 updateTheme 值window.addEventListener(message, (event) {if (event.data?.type ! updateTheme) {return;}const newTheme event.data?.value || ;theme.value newTheme;});return theme;
}在项目中我们可以可以搜索 useCurrentTheme 看在哪里使用的。很容易我们可以发现 vant/packages/vant-cli/site/mobile/App.vue 文件中有使用。3.4 mobile 端// 模板部分
// vant/packages/vant-cli/site/mobile/App.vuetemplatedemo-nav /router-view v-slot{ Component }keep-alivedemo-sectioncomponent :isComponent //demo-section/keep-alive/router-view
/template// js 部分
// vant/packages/vant-cli/site/mobile/App.vue
script
import { watch } from vue;
import DemoNav from ./components/DemoNav.vue;
import { useCurrentTheme } from ../common/iframe-sync;
import { config } from site-mobile-shared;export default {components: { DemoNav },setup() {const theme useCurrentTheme();watch(theme,(newVal, oldVal) {document.documentElement.classList.remove(van-doc-theme-${oldVal});document.documentElement.classList.add(van-doc-theme-${newVal});const { darkModeClass, lightModeClass } config.site;if (darkModeClass) {document.documentElement.classList.toggle(darkModeClass,newVal dark);}if (lightModeClass) {document.documentElement.classList.toggle(lightModeClass,newVal light);}},{ immediate: true });},
};
/scriptstyle langless
import ../common/style/base;body {min-width: 100vw;background-color: inherit;
}.van-doc-theme-light {background-color: var(--van-doc-gray-1);
}.van-doc-theme-dark {background-color: var(--van-doc-black);
}::-webkit-scrollbar {width: 0;background: transparent;
}
/style上文阐述了浅色主题和暗黑主题的实现原理我们接着来看如何通过 ConfigProvider 组件[14]实现主题的深度定制。44. ConfigProvider 组件深度定制主题这个组件的文档有说明主要就是利用 CSS 变量[15]来实现的具体可以查看这个链接学习。这里举个简单的例子。// html
div idapp style--van-color: black;--van-background-color: pink;hello world/div
// css
#app {color: var(--van-color);background-color: var(--van-background-color);
}可以预设写好若干变量然后在 style 中修改相关变量就能得到相应的样式从而达到深度定制修改主题的能力。比如如果把 --van-color: black;改成 --van-color: red; 则字体颜色是红色。如果把 --van-background-color: pink; 改成 --van-background-color: white; 则背景色是白色。vant 中有一次提交把之前所有的 less 变量改成了原生 css 的 var 变量。breaking change: no longer support less vars[16]vant 中 ConfigProvider 组件其实就是利用了这个原理。知晓了上面的原理我们再来简单看下 ConfigProvider 具体实现。// vant/packages/vant/src/config-provider/ConfigProvider.tsx
// 代码有省略
function mapThemeVarsToCSSVars(themeVars: Recordstring, Numeric) {const cssVars: Recordstring, Numeric {};Object.keys(themeVars).forEach((key) {cssVars[--van-${kebabCase(key)}] themeVars[key];});// 把 backgroundColor 最终生成类似这样的属性// {--van-background-color: xxx}return cssVars;
}export default defineComponent({name,props: configProviderProps,setup(props, { slots }) {// 完全可以在你需要的地方打上 debugger 断点debugger;const style computedCSSProperties | undefined(() mapThemeVarsToCSSVars(extend({},props.themeVars,props.theme dark ? props.themeVarsDark : props.themeVarsLight)));// 主题变化添加和移除相应的样式类if (inBrowser) {const addTheme () {document.documentElement.classList.add(van-theme-${props.theme});};const removeTheme (theme props.theme) {document.documentElement.classList.remove(van-theme-${theme});};watch(() props.theme,(newVal, oldVal) {if (oldVal) {removeTheme(oldVal);}addTheme();},{ immediate: true });onActivated(addTheme);onDeactivated(removeTheme);onBeforeUnmount(removeTheme);}// 插槽// 用于 style// 把 backgroundColor 最终生成类似这样的属性// {--van-background-color: xxx}return () (props.tag class{bem()} style{style.value}{slots.default?.()}/props.tag);},
});有小伙伴可能注意到了这感觉就是和 react 类似啊。其实 vue 也是支持 jsx。不过需要配置插件 vue/babel-plugin-jsx[17]。全局搜索这个插件可以搜索到在 vant-cli 中配置了这个插件。55. 总结我们通过查看 README.md 和贡献文档等知道了项目使用的 monorepovite 等pnpm i 安装依赖pnpm dev 跑项目。我们学会了利用 vue-devtools 快速找到我们不那么熟悉的项目中的文件并打开相应的文件。通过文档桌面端和移动端的主题切换我们学到了原来是 iframe 渲染的移动mobile端通过 iframe postMessage 和 addEventListener 通信切换主题。学会了 ConfigProvider 组件是利用CSS 变量[18] 预设变量样式来实现的定制主题。也学会使用 vue/babel-plugin-jsx[19] 编写 jsx 组件和写 react 类似。相比于原生 JS 等源码。我们或许更应该学习正在使用的组件库的源码因为有助于帮助我们写业务和写自己的组件。开源项目通常有很多优雅实现、最佳实践、前沿技术等都可以值得我们借鉴。如果是自己开源写项目相对耗时耗力而且短时间很难有很大收益很容易放弃。而刚开始可能也无法参与到开源项目中这时我们可以先从看懂开源项目的源码做起。对于写源码来说看懂源码相对容易。看懂源码后可以写文章分享回馈给社区也算是对开源做出一种贡献。重要的是行动起来学着学着就会发现很多都已经学会锻炼了自己看源码的能力。如果看完有收获欢迎点赞、评论、分享支持。你的支持和肯定是我写作的动力。参考资料[1]组件库源码系列专栏: https://juejin.cn/column/7140264842954276871[2]chenjiahan: https://github.com/chenjiahan/[3]官方文档链接: https://vant-contrib.gitee.io/vant/v4/#/zh-CN/config-provider[4]README.md: https://github.com/youzan/vant[5]github/CONTRIBUTING.md: https://github.com/youzan/vant/blob/main/.github/CONTRIBUTING.md[6]Node.js 14: https://nodejs.org[7]pnpm: https://pnpm.io[8]vue-devtools 官网: https://devtools.vuejs.org/guide/installation.html[9]极简插件链接: https://chrome.zzzmh.cn/info/nhdogjmejiglipccpnnnanhbledajbpd[10]发起投票: https://mp.weixin.qq.com/s/9gYmrJLdKwJ1TCVm-MUzrA[11]mobile 官网: https://vant-contrib.gitee.io/vant/mobile.html#/zh-CN[12]Gitlens: https://marketplace.visualstudio.com/items?itemNameeamodio.gitlens[13]feat(vant/cli): desktop site support dark mode: https://github.com/youzan/vant/commit/35a990ed65500311cbcafae506780dc6d3fb49fa[14]ConfigProvider 组件: https://vant-contrib.gitee.io/vant/#/zh-CN/config-provider[15]CSS 变量: https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties[16]breaking change: no longer support less vars: https://github.com/youzan/vant/commit/ada5db011c676893a2917b9424be1aeafe54134b#diff-9a3dd1c100237e3a5d11553bb08a5f74144c9aecc67a995b8e608bfe2ebcf7f2[17]vue/babel-plugin-jsx: https://www.npmjs.com/package/vue/babel-plugin-jsx[18]CSS 变量: https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties[19]vue/babel-plugin-jsx: https://www.npmjs.com/package/vue/babel-plugin-jsx················· 若川简介 ·················你好我是若川毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇在知乎、掘金收获超百万阅读。从2014年起每年都会写一篇年度总结已经坚持写了8年点击查看年度总结。同时持续组织了一年多源码共读活动帮助5000前端人学会看源码。公众号愿景帮助5年内前端人走向前列。扫码加我微信 lxchuan12、拉你进源码共读群今日话题目前建有江西|湖南|湖北 籍 前端群想进群的可以加我微信 lxchuan12 进群。分享、收藏、点赞、在看我的文章就是对我最大的支持~