当前位置: 首页 > news >正文

样式方案:在 Vite 中接入现代化的 CSS 工程化方案

上一小节,我们使用 Vite 初始化了一个 Web 项目,迈出了使用 Vite 的第一步。但在实际工作中,仅用 Vite 官方的脚手架项目是不够的,往往还需要考虑诸多的工程化因素,借助 Vite 本身的配置以及业界的各种生态,才能搭建一个名副其实的脚手架工程。

那在接下来的几个小节内容中,我们将以实战的方式逐个击破项目工程化的要素。你可以跟着我一起进行编码,从0搭建一个完整的 Vite 项目架构。不仅如此,在实战的过程中,你也会对 Vite 本身的功能有全面了解,能够熟练地将它应用到实际项目。

样式方案是前端工程化离不开的一个话题,也是本节要具体探讨的内容。在最原始的开发阶段大家都是手写原生的 CSS,但原生 CSS 存在着诸多问题。本小节,我们通过引入现代的各种 CSS 样式方案,一起动手实践,让你学会如何在 Vite 中落地这些样式方案。

样式方案的意义

对初学者来说,谈到开发前端的样式,首先想到的便是直接写原生 CSS。但时间一长,难免会发现原生 CSS 开发的各种问题。那么,如果我们不用任何 CSS 工程方案,又会出现哪些问题呢?

  1. 开发体验欠佳。比如原生 CSS 不支持选择器的嵌套:
// 选择器只能平铺,不能嵌套
.container .header .nav .title .text {color: blue;
}.container .header .nav .box {color: blue;border: 1px solid grey;
}
  1. 样式污染问题。如果出现同样的类名,很容易造成不同的样式互相覆盖和污染。
// a.css
.container {color: red;
}// b.css
// 很有可能覆盖 a.css 的样式!
.container {color: blue;
}
  1. 浏览器兼容问题。为了兼容不同的浏览器,我们需要对一些属性(如transition)加上不同的浏览器前缀,比如 -webkit--moz--ms--o-,意味着开发者要针对同一个样式属性写很多的冗余代码。
  2. 打包后的代码体积问题。如果不用任何的 CSS 工程化方案,所有的 CSS 代码都将打包到产物中,即使有部分样式并没有在代码中使用,导致产物体积过大。

针对如上原生 CSS 的痛点,社区中诞生了不少解决方案,常见的有 5 类。

  1. CSS 预处理器:主流的包括Sass/ScssLessStylus。这些方案各自定义了一套语法,让 CSS 也能使用嵌套规则,甚至能像编程语言一样定义变量、写条件判断和循环语句,大大增强了样式语言的灵活性,解决原生 CSS 的开发体验问题
  2. CSS Modules:能将 CSS 类名处理成哈希值,这样就可以避免同名的情况下样式污染的问题。
  3. CSS 后处理器PostCSS,用来解析和处理 CSS 代码,可以实现的功能非常丰富,比如将 px 转换为 rem、根据目标浏览器情况自动加上类似于--moz---o-的属性前缀等等。
  4. CSS in JS 方案,主流的包括emotionstyled-components等等,顾名思义,这类方案可以实现直接在 JS 中写样式代码,基本包含CSS 预处理器CSS Modules 的各项优点,非常灵活,解决了开发体验和全局样式污染的问题。
  5. CSS 原子化框架,如Tailwind CSSWindi CSS,通过类名来指定样式,大大简化了样式写法,提高了样式开发的效率,主要解决了原生 CSS 开发体验的问题。

不过,各种方案没有孰优孰劣,各自解决的方案有重叠的部分,但也有一定的差异,大家可以根据自己项目的痛点来引入。接下来,我们进入实战阶段,在 Vite 中应用上述常见的 CSS 方案。

CSS 预处理器

Vite 本身对 CSS 各种预处理器语言(Sass/ScssLessStylus)做了内置支持。也就是说,即使你不经过任何的配置也可以直接使用各种 CSS 预处理器。我们以 Sass/Scss 为例,来具体感受一下 Vite 的零配置给我们带来的便利。

由于 Vite 底层会调用 CSS 预处理器的官方库进行编译,而 Vite 为了实现按需加载,并没有内置这些工具库,而是让用户根据需要安装。因此,我们首先安装 Sass 的官方库,安装命令如下:

pnpm i sass -D

然后,在上一节初始化后的项目中新建 src/components/Header 目录,并且分别新建index.tsxindex.scss文件,代码如下:

// index.tsx
import './index.scss';
export function Header() {return <p className="header">This is Header</p>
};// index.scss
.header {color: red;
}

这样就完成了一个最简单的 demo 组件。接着我们在 App.tsx 应用这个组件:

import { Header } from "./components/Header";function App() {return (<div><Header /></div>);
}export default App;

现在你可以执行pnpm run dev,然后到浏览器上查看效果

内容比较简单,如果页面出现红色的文字部分,就说明 scss 文件中的样式已经成功生效。好,现在我们封装一个全局的主题色,新建src/variable.scss文件,内容如下:

// variable.scss
$theme-color: red;

然后,我们在原来 Header 组件的样式中应用这个变量:

@import "../../variable";.header {color: $theme-color;
}

回到浏览器访问页面,可以看到样式依然生效。你可能会注意到,每次要使用$theme-color属性的时候我们都需要手动引入variable.scss文件,那有没有自动引入的方案呢?这就需要在 Vite 中进行一些自定义配置了,在配置文件中增加如下的内容:

// vite.config.ts
import { normalizePath } from 'vite';
// 如果类型报错,需要安装 @types/node: pnpm i @types/node -D
import path from 'path';// 全局 scss 文件的路径
// 用 normalizePath 解决 window 下的路径问题
const variablePath = normalizePath(path.resolve('./src/variable.scss'));export default defineConfig({// css 相关的配置css: {preprocessorOptions: {scss: {// additionalData 的内容会在每个 scss 文件的开头自动注入additionalData: `@import "${variablePath}";`}}}
})

现在你可以直接在文件中使用全局文件的变量,相当于之前手动引入的方式显然方便了许多:

.header {color: $theme-color;
}

同样的,你可以对 lessstylus进行一些能力的配置,如果有需要你可以去下面的官方文档中查阅更多的配置项:

  • Sass
  • Less
  • Stylus

CSS Modules

CSS Modules 在 Vite 也是一个开箱即用的能力,Vite 会对后缀带有.module的样式文件自动应用 CSS Modules。接下来我们通过一个简单的例子来使用这个功能。

首先,将 Header 组件中的index.scss更名为index.module.scss,然后稍微改动一下index.tsx的内容,如下:

// index.tsx
import styles from './index.module.scss';
export function Header() {return <p className={styles.header}>This is Header</p>
};

现在打开浏览器,可以看见 p 标签的类名已经被处理成了哈希值的形式

说明现在 CSS Modules 已经正式生效了!同样的,你也可以在配置文件中的css.modules选项来配置 CSS Modules 的功能,比如下面这个例子:

// vite.config.ts
export default {css: {modules: {// 一般我们可以通过 generateScopedName 属性来对生成的类名进行自定义// 其中,name 表示当前文件名,local 表示类名generateScopedName: "[name]__[local]___[hash:base64:5]"},preprocessorOptions: {// 省略预处理器配置}}
}

再次访问页面,我们可以发现刚才的类名已经变成了我们自定义的形式

这是一个 CSS Modules 中很常见的配置,对开发时的调试非常有用。其它的一些配置项不太常用,大家可以去这个地址进行查阅。

PostCSS

一般你可以通过 postcss.config.js 来配置 postcss ,不过在 Vite 配置文件中已经提供了 PostCSS 的配置入口,我们可以直接在 Vite 配置文件中进行操作。

首先,我们来安装一个常用的 PostCSS 插件——autoprefixer:

pnpm i autoprefixer -D

这个插件主要用来自动为不同的目标浏览器添加样式前缀,解决的是浏览器兼容性的问题。接下来让我们在 Vite 中接入这个插件:

// vite.config.ts 增加如下的配置
import autoprefixer from 'autoprefixer';export default {css: {// 进行 PostCSS 配置postcss: {plugins: [autoprefixer({// 指定目标浏览器overrideBrowserslist: ['Chrome > 40', 'ff > 31', 'ie 11']})]}}
}

配置完成后,我们回到 Header 组件的样式文件中添加一个新的 CSS 属性:

.header {<!-- 前面的样式省略 -->text-decoration: dashed;
}

你可以执行pnpm run build命令进行打包,可以看到产物中自动补上了浏览器前缀,如:

._header_kcvt0_1 {<!-- 前面的样式省略 -->-webkit-text-decoration: dashed;-moz-text-decoration: dashed;text-decoration: dashed;
}

由于有 CSS 代码的 AST (抽象语法树)解析能力,PostCSS 可以做的事情非常多,甚至能实现 CSS 预处理器语法和 CSS Modules,社区当中也有不少的 PostCSS 插件,除了刚刚提到的autoprefixer插件,常见的插件还包括:

  • postcss-pxtorem: 用来将 px 转换为 rem 单位,在适配移动端的场景下很常用。
  • postcss-preset-env: 通过它,你可以编写最新的 CSS 语法,不用担心兼容性问题。
  • cssnano: 主要用来压缩 CSS 代码,跟常规的代码压缩工具不一样,它能做得更加智能,比如提取一些公共样式进行复用、缩短一些常见的属性值等等。

关于 PostCSS 插件,这里还给大家推荐一个站点:https://www.postcss.parts/ ,你可以去里面探索更多的内容。

CSS In JS

社区中有两款主流的CSS In JS 方案: styled-componentsemotion

对于 CSS In JS 方案,在构建侧我们需要考虑选择器命名问题DCE(Dead Code Elimination 即无用代码删除)、代码压缩生成 SourceMap服务端渲染(SSR)等问题,而styled-componentsemotion已经提供了对应的 babel 插件来解决这些问题,我们在 Vite 中要做的就是集成这些 babel 插件。

具体来说,上述的两种主流 CSS in JS 方案在 Vite 中集成方式如下:

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'// https://vitejs.dev/config/
export default defineConfig({plugins: [react({babel: {// 加入 babel 插件// 以下插件包都需要提前安装// 当然,通过这个配置你也可以添加其它的 Babel 插件plugins: [// 适配 styled-component"babel-plugin-styled-components"// 适配 emotion"@emotion/babel-plugin"]},// 注意: 对于 emotion,需要单独加上这个配置// 通过 `@emotion/react` 包编译 emotion 中的特殊 jsx 语法jsxImportSource: "@emotion/react"})]
})

CSS 原子化框架

在目前的社区当中,CSS 原子化框架主要包括Tailwind CSSWindi CSS。Windi CSS 作为前者的替换方案,实现了按需生成 CSS 类名的功能,开发环境下的 CSS 产物体积大大减少,速度上比Tailwind CSS v2快 20~100 倍!当然,Tailwind CSS 在 v3 版本也引入 JIT(即时编译) 的功能,解决了开发环境下 CSS 产物体积庞大的问题。接下来我们将这两个方案分别接入到 Vite 中,在实际的项目中你只需要使用其中一种就可以了。我个人比较喜欢 Windi CSS 本身的attributifyshortcuts等独有的特性,因此首先从 windicss 开始说起。

1. Windi CSS 接入

首先安装 windicss 及对应的 Vite 插件:

pnpm i windicss vite-plugin-windicss -D

随后我们在配置文件中来使用它:

// vite.config.ts
import windi from "vite-plugin-windicss";export default {plugins: [// 省略其它插件windi()]
}

接着要注意在src/main.tsx中引入一个必需的 import 语句:

// main.tsx
// 用来注入 Windi CSS 所需的样式,一定要加上!
import "virtual:windi.css";

这样我们就完成了 Windi CSS 在 Vite 中的接入,接下来我们在 Header 组件中来测试,组件代码修改如下:

// src/components/Header/index.tsx
import { devDependencies } from "../../../package.json";export function Header() {return (<div className="p-20px text-center"><h1 className="font-bold text-2xl mb-2">vite version: {devDependencies.vite}</h1></div>);
}

启动项目可以看到如下的效果,说明样式已经正常生效

除了本身的原子化 CSS 能力,Windi CSS 还有一些非常好用的高级功能,在此我给大家推荐自己常用的两个能力: attributifyshortcuts

要开启这两个功能,我们需要在项目根目录新建windi.config.ts,配置如下:

import { defineConfig } from "vite-plugin-windicss";export default defineConfig({// 开启 attributifyattributify: true,
});

首先我们来看看attributify,翻译过来就是属性化,也就是说我们可以用 props 的方式去定义样式属性,如下所示:

<button bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"text="sm white"font="mono light"p="y-2 x-4"border="2 rounded blue-200"
>Button
</button>

这样的开发方式不仅省去了繁琐的 className 内容,还加强了语义化,让代码更易维护,大大提升了开发体验。

不过使用attributify的时候需要注意类型问题,你需要添加types/shim.d.ts来增加类型声明,以防类型报错:

import { AttributifyAttributes } from 'windicss/types/jsx';declare module 'react' {type HTMLAttributes<T> = AttributifyAttributes;
}

shortcuts 用来封装一系列的原子化能力,尤其是一些常见的类名集合,我们在 windi.config.ts来配置它:

//windi.config.ts
import { defineConfig } from "vite-plugin-windicss";export default defineConfig({attributify: true,shortcuts: {"flex-c": "flex justify-center items-center",}
});

比如这里封装了flex-c的类名,接下来我们可以在业务代码直接使用这个类名:

<div className="flex-c"></div>
<!-- 等同于下面这段 -->
<div className="flex justify-center items-center"></div>

如果你也有过 Windi CSS 的开发经历,欢迎把你用到的高级功能分享到评论区,让大家一起来见识见识。

2. Tailwind CSS

接下来我们来接入 Tailwind CSS 方案,为了避免和之前的 Windi CSS 混淆,这里我建议你新起一个 Vite 项目。

小册中对应的 GitHub 代码地址。

首先安装 tailwindcss 及其必要的依赖:

pnpm install -D tailwindcss postcss autoprefixer

然后新建两个配置文件tailwind.config.jspostcss.config.js:

// tailwind.config.js
module.exports = {content: ["./index.html","./src/**/*.{vue,js,ts,jsx,tsx}",],theme: {extend: {},},plugins: [],
}// postcss.config.js
// 从中你可以看到,Tailwind CSS 的编译能力是通过 PostCSS 插件实现的
// 而 Vite 本身内置了 PostCSS,因此可以通过 PostCSS 配置接入 Tailwind CSS 
// 注意: Vite 配置文件中如果有 PostCSS 配置的情况下会覆盖掉 post.config.js 的内容!
module.exports = {plugins: {tailwindcss: {},autoprefixer: {},},
}

接着在项目的入口 CSS 中引入必要的样板代码:

@tailwind base;
@tailwind components;
@tailwind utilities;

现在,你就可以在项目中安心地使用 Tailwind 样式了,如下所示:

// App.tsximport logo from "./logo.svg";
import "./App.css";function App() {return (<div><header className="App-header"><img src={logo} className="w-20" alt="logo" /><p className="bg-red-400">Hello Vite + React!</p></header></div>);
}export default App;

当你启动项目之后可以看到 Tailwind CSS 的样式已经正常生效

小结

OK,本小节的内容到这里就结束了。这一节我们完成了脚手架项目样式部分的搭建,你需要重点掌握前端工程中各种样式方案在 Vite 的接入方法。这些样式方案包括,包括CSS 预处理器CSS ModulesPostCSSCSS In JSCSS 原子化框架(Windi CSS)。与此同时,你应该明白了各种样式方案的含义以及背后所解决的问题。接下来,我们将会进入项目规范搭建的部分,让我们下一节再见!

相关文章:

样式方案:在 Vite 中接入现代化的 CSS 工程化方案

上一小节&#xff0c;我们使用 Vite 初始化了一个 Web 项目&#xff0c;迈出了使用 Vite 的第一步。但在实际工作中&#xff0c;仅用 Vite 官方的脚手架项目是不够的&#xff0c;往往还需要考虑诸多的工程化因素&#xff0c;借助 Vite 本身的配置以及业界的各种生态&#xff0c…...

C#获取根目录实现方法汇总

以下是C#获取不同类型项目根目录的实现方法汇总&#xff0c;以及在 .NET Core 中获取项目根目录的方法&#xff1a; 控制台应用程序 string rootPath Environment.CurrentDirectory; string rootPath AppDomain.CurrentDomain.BaseDirectory; string rootPath Path.GetFul…...

vue获取当前坐标并通过天地图逆转码为省市区

因为需求需要获取用户当前的地理位置用于分析 通过原生的navigator.geolocation.getCurrentPosition获取经纬度 这个方法在谷歌浏览器会失效&#xff08;原因未知&#xff09;&#xff0c;目前ie浏览器是可以获取的 getCurrentPosition() {if (navigator.geolocation) {var o…...

【MySQL】事务及其隔离性/隔离级别

目录 一、事务的概念 1、事务的四种特性 2、事务的作用 3、存储引擎对事务的支持 4、事务的提交方式 二、事务的启动、回滚与提交 1、准备工作&#xff1a;调整MySQL的默认隔离级别为最低/创建测试表 2、事务的启动、回滚与提交 3、启动事务后未commit&#xff0c;但是…...

计算机由于找不到d3dx9_35.dll,无法启动软件游戏的三个修复方法

在打开游戏的时候&#xff0c;计算机提示由于找不到d3dx9_35.dll&#xff0c;无法正常启动运行。这个是为什么呢&#xff1f;d3dx9_35.dll是DirectX 9.0里面的一个动态连结库文件&#xff0c;它包含了Direct3D、DirectPlay几个组件的二进制文件&#xff0c;为软件提供了多媒体图…...

第三章 模型篇:模型与模型的搭建

写在前面的话 这部分只解释代码&#xff0c;不对线性层(全连接层)&#xff0c;卷积层等layer的原理进行解释。 尽量写的比较全了&#xff0c;但是自身水平有限&#xff0c;不太确定是否有遗漏重要的部分。 教程参考&#xff1a; https://pytorch.org/tutorials/ https://githu…...

深度学习一些简单概念的整理笔记

大概看了一点动手学深度学习&#xff0c;简单整理一些概念。 一些问题 测试结果 Precision-Recall曲线定性分析模型精度average precision(AP) 平均精度 Precision &#xff1a;检索出来的条目中有多大比例是我们需要的。 一些概念 损失函数&#xff08;loss function&…...

Vue3中引入Element-plus

安装 npm install element-plus --save完整引入 打包后体积很大&#xff0c;适合学习&#xff0c;不适合生产。 此方法对于 vite 和 cli 脚手架创建的vue3均适用 // main.ts import { createApp } from vue //引入element-plus import ElementPlus from element-plus //引入…...

如何查看 Facebook 公共主页的广告数量上限?

作为Facebook的资深人员&#xff0c;了解如何查看公共主页的广告数量上限对于有效管理和优化广告策略至关重要。本文将详细介绍如何轻松查看Facebook公共主页的广告数量上限&#xff0c;以帮助您更好地掌握广告投放策略。 一、什么是Facebook公共主页的广告数量上限&#xff1f…...

U-Boot移植 (2)- LCD 驱动修改和网络驱动修改

文章目录 1. LCD 驱动修改1.1 修改c文件配置1.2 修改h文件配置1.3 编译测试 2. 网络驱动修改2.1 I.MX6U-ALPHA 开发板网络简介2.2 网络 PHY 地址修改2.3 删除 uboot 中 74LV595 的驱动代码2.4 添加开发板网络复位引脚驱动2.5 更新 PHY 的连接状态和速度2.6 烧写调试2.7 测试一下…...

Ubuntu 23.10 现在由Linux内核6.3提供支持

对于那些希望在Ubuntu上尝试最新的Linux 6.3内核系列的人来说&#xff0c;今天有一个好消息&#xff0c;因为即将发布的Ubuntu 23.10&#xff08;Mantic Minotaur&#xff09;已经重新基于Linux内核6.3。 Ubuntu 23.10的开发工作于4月底开始&#xff0c;基于目前的临时版本Ubu…...

Python 学习之NumPy(一)

文章目录 1.为什么要学习NumPy2.NumPy的数组变换以及索引访问3.NumPy筛选使用介绍筛选出上面nb数组中能被3整除的所有数筛选出数组中小于9的所有数提取出数组中所有的奇数数组中所有的奇数替换为-1二维数组交换2列生成数值5—10&#xff0c;shape 为(3,5)的二维随机浮点数 NumP…...

Nftables栈溢出漏洞(CVE-2022-1015)复现

背景介绍 Nftables Nftables 是一个基于内核的包过滤框架&#xff0c;用于 Linux 操作系统中的网络安全和防火墙功能。nftables 的设计目标是提供一种更简单、更灵活和更高效的方式来管理网络数据包的流量。 钩子点&#xff08;Hook Point&#xff09; 钩子点的作用是拦截数…...

【C++】 Qt-事件(上)(事件、重写事件、事件分发)

文章目录 事件重写事件事件分发 事件 事件&#xff08;event&#xff09;是由系统或Qt本身在不同的时刻发出的。比如&#xff0c;当用户按下鼠标&#xff0c;敲下键盘&#xff0c;或窗口需要重新绘制的时候&#xff0c;都会发出一个相应的事件。一些事件是在对用户操作做出响应…...

k8s部署springboot

前言 首先以SpringBoot应用为例介绍一下k8s的部署步骤。 1.从代码仓库下载代码&#xff0c;比如GitLab&#xff1b; 2.接着是进行打包&#xff0c;比如使用Maven&#xff1b; 3.编写Dockerfile文件&#xff0c;把步骤2产生的包制作成镜像&#xff1b; 4.上传步骤3的镜像到远程…...

备战秋招002(20230704)

文章目录 前言一、今天学习了什么&#xff1f;二、关于问题的答案1.线程池2.synchronized关键字3、volatile 总结 前言 提示&#xff1a;这里为每天自己的学习内容心情总结&#xff1b; Learn By Doing&#xff0c;Now or Never&#xff0c;Writing is organized thinking. …...

游泳买耳机买什么的比较好,列举几款实战性好的游泳耳机

对于运动用户来说&#xff0c;在运动时都会选择听一些节奏感比较强的音乐&#xff0c;让自己运动是更有活力。现在已经是三伏天中的前伏期间&#xff0c;不少人会选择在三伏天的日子里进行减肥瘦身&#xff0c;耳游泳已经成为很多人都首选运动&#xff0c;游泳是非常好的有氧运…...

【无线传感器】使用 MATLAB和 XBee连续监控温度传感器无线网络研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

Java基础-多线程JUC-生产者和消费者

1. 生产者与消费者 实现线程轮流交替执行的结果&#xff1b; 实现线程休眠和唤醒均要使用到锁对象&#xff1b; 修改标注位&#xff08;foodFlag&#xff09;&#xff1b; 代码实现&#xff1a; public class demo11 {public static void main(String[] args) {/*** 需求&#…...

day2 QT按钮与容器

目录 按钮 1、QPushButton 2、QToolButton 3、QRadioButton 4、QCheckBox 示例 容器 ​编辑 1. QGroupBox&#xff08;分组框&#xff09; 2. QScrollArea&#xff08;滚动区域&#xff09; 3. QToolBox&#xff08;工具箱&#xff09; 4. QTabWidget&#xff08;选…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

Bean 作用域有哪些?如何答出技术深度?

导语&#xff1a; Spring 面试绕不开 Bean 的作用域问题&#xff0c;这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开&#xff0c;结合典型面试题及实战场景&#xff0c;帮你厘清重点&#xff0c;打破模板式回答&#xff0c…...