css-loader和file-loader/url-loader冲突无法显示图片
最近在调试的时候,同事反馈说图片没有展示出来,去看了以后简直莫名奇妙,因为我自认为对css-loader和file-loader的配置没有问题,难道css-loader又挖了什么坑?于是开始漫长的调试,测试过程。最终发现,问题是真的存在,而且我google了半天,恁是在国内外都没有找到靠谱的解决方法。调试过程中可以肯定的是,css-loader竟然自己把url()的图片生成到目录下了,然后file-loader又做了一遍:
这也就意味着一张图片被处理了两次,具体造成的问题是css-loader给的路径不对还是file-loader给的不对,就不想去深究,总之就是两个loader冲突了。按照我以前的理解,css-loader只负责把url()转化为require()的形式,并不负责生成真实的图片,然后file-loader可以识别require(image)的形式,并生成图片,两个loader分工明确。那么这里是为什么呢?于是,我把file-loader从rules中去掉,然后……图片竟然展示出来了!!
所以说,无论是css-loader的问题,还是结合webpack5一起的问题,现在webpack5+新版css-loader就可以自己把图片处理到目标目录下了。然而,问题在于,如果去掉file-loader,那么我们在js中是没有办法直接import一张图片作为url来使用的。这就矛盾了。而且检查了file-loader的配置,也没有接口可以让我们忽略对css文件中的图片进行排除。似乎就这样卡住了。
于是重新回去阅读文档。
果然,不独不知道,webpack5现在已经牛x到自己可以把非js的文件资源作为模块读取了。什么?不懂?意思就是,不需要file-loader了,webpack内置了工具!具体做法就是把我们配置文件中的 loader: 'file-loader'
一句改为 type: 'asset/resource'
就可以了。
webpack的rule.type在v4中就已经加入了,只是很少被用到,它的文档也说的不清不楚,我也不知道它有这个能力,而 asset/resource
也是到v5才悄悄加进去的,你可以查看v4的文档和v5的文档的差异。
新增的asset这个选项就是对非js相关的资源的处理,它文档单独通过一节对这些项目进行解释,而且里面就明确提到可以代替以前的file-loader, url-loader, raw-loader,然而,大哥你虽然可以代替,但是直接破坏原来的效果,是不是有点不厚道呀!
-
来 vite 保平安(逃#1178 rxliuli 2022-04-03 17:15
-
Cool!#1183 ayu 2022-04-16 16:09
-
Cool#1300 HduSy 2024-01-27 12:17
-
确实,如果升级后想用的话,可将`type: javascript/auto`,同时`esModule: false`#1301 HduSy 2024-01-29 19:06
项目CSS管理方案对比
在项目中,我们不仅要管理js代码,还要管理css代码,但是我们现在业界对js研究的比较多,对css比较少,不过这两年我注意到已经有不少有关css的新思想。不过,抛开这些新东西,我们的项目中,应该怎么管理我们的css呢?我做了调研,大致结果如下:
方案 | 解释 | 优点 | 缺点 |
Atomic CSS | Tailwind, https://acss.io/ | 复用性,css代码量 | html代码量,多处修改易漏 |
AMCSS | <div button="large blue">Button</div> | ||
OOCSS | 结构和设计的分离,容器和内容的分离 .mt20 { margin-top: 20px } -> 样式 .flex .flex1 { flex: 1 } -> 结构 .tc { text-align: center } -> 内容 .abs { position: absolute } -> 容器 .clearfix:after { content: '', display: block; clear: both; height: 0 } |
||
SMACSS | Base 基本规则,整体样式,比如 body、input、button、form Layout 布局规则,比如 顶部,页脚,边栏,模块的大小等 Module 可复用的模块样式规则 State 状态样式 比如 隐藏 当前高亮 Theme 主题 控制整体UI |
层次分明 | 需要整体掌控易出错 |
MCSS | multilayer CSS,层层覆盖叠加 | ||
BEM | .block__element--modifier {} | 代码量 | |
scoped css | vue | 隔离 | 依赖vue |
css modules | import * as Css from './some.css' | 隔离,tree shaking,编译为RN Stylesheet对象 | 依赖编译 |
css-in-js | styled-components | 隔离,组件化 | 依赖运行时,代码量 |
实际上,没有哪一种是最好的,只有适合不适合。对于我个人而言,样式隔离是一个必须选项,因为我开发工具库比较多一些,我提供库给别人用,别人可能还会使用其他人的库,两个库之间撞名的情况很容易发生,所以,隔离对我来说非常重要。在考虑到各种场景后,我最终使用css modules比较多,在nautil中我就坚持css和css modules一起用。不过,除了隔离之外,复用性就不是很好,因为很少能复用其他库的样式,最终会导致使用了我的库的站点,css代码量可能会篇多一些。所以我说,没有那个最好,只有最适合。
-
方案列为什么会折行呢#1174 1188 2022-03-18 14:35
-
页面宽度不够,已经调整了#1175 回复给#1174 否子戈 2022-03-18 19:48
今天遇到一个怪事,用Macbook,前几天还比较正常,今天刷B站的时候,视频卡的跟狗一样,重启了光猫,也是一样,在偏好设置里面搞了很久,也没有解决,但是平板上刷B站一点问题没有,难道它知道我是用网页或app?但是在另外一台windows上却也稳如老狗。于是一通搜索,在一个角落里面找到一句话,大概意思是“USB拖太多东西,导致网卡供电不足”,日妈还有这种事?于是真的是了一下,拔掉USB Hub上的东西,真的恢复了,草啊!!!
最近的一些感悟
已经很久没有更新博客的内容了,便随意写点什么。最近经历了工作上的很多事,有了一些感悟。但是我不会一条一条的列出来,也不会说我经历了什么。感悟这种东西,它不一定对其他人有用,或者说你并不认同。但是,有些东西,表面上的差异,本质上是一致的,感悟这类东西,就往往是表面的,深层的东西埋在感悟下面,更加世俗,更加有价值,但是却失去了一些浪漫。所以,从这个角度去想,我这个人还是有一点浪漫主义,但是我也并不稀奇,毕竟浪漫是不能当饭吃,很多人可悲的是,在生存的边缘挣扎,却在追求极致的浪漫主义。
我不能完全体会到“命运”,但是呢,最近在网上看到一些知名的,但是不像是装出来的名人,他们多次提到“信命”这个事情。我也深刻理解到,“命”不是迷信,更多的是对生命的敬畏,有所为,有所不为。信命的人总是带着这种敬畏,所以看上去总是蹑手蹑脚,但是他自己在其中并不觉得可惜,并且总能做到脚踏实地问心无愧。而不信命的人,往往追求“我命由我不由天”,所以往往比较激进,甚至不惜代价去冲击,有的跳跃了,有的败下来,但是,当这些过去之后,他们总是会有诸多遗憾,甚至内心惭愧悔恨。所以,我的感悟总结起来就一句话“尽人事以听天命”。也就是跟着命运的方向走,在这个过程中自己张开翅膀享受这个过程。这里面所遇到的困难,有的时候很痛苦,但是总是会在之后获得内心的慰藉。一时的得失也好,所谓的人生重大决定也罢,都不是特别重要,关键是,当这件事摆在你面前的时候,不逃避,而是去积极准备和面对它。
你说我信命,但我并不信,我只是遵从了一种理念去生活,是积极的,和那些信命躺平的是截然相反的。不过,我也因此知道,并不是任何努力都有你想要的结果,在这里面,95%的努力是没有用的,这些努力仅仅是你付出的信仰,只有剩下的那5%是有用的。但是,你可能会说,现在那么多人一朝就翻身了,难道是因为遵从你这套理论吗?当然不完全是,但是,他们的命和你的命不同,所以你的努力和他的努力不同。就像我一样,我时常会觉得不公平,有些人明明能力不如我,思想不如我,甚至品德不如我,为什么却比我混的好,赚的钱比我多。然而,当我回过来,并不是我不够好,而是在这个世界的规律里面,在我们这条通道里,他才是适应规律的,就像管道里顺着水流的枯叶,虽然已经腐败不堪,但是和逆流而上的水虫而言,却是更早离开管道看见阳光。我们越是挣扎,或许里得到越远。但我们是没有办法做枯叶的,每个人的性质不同,我们是金子,沉在水底,纹丝不动,永不见光明,这是我们骨子里的倔强决定的,不是可以改变的。所以,即使我们心想我们可以同流合污,最后还是发现不可能得到。这就是命。
那我们就不作为,也是不可取。信命是积极的,信命总是往前,因为信命会顺其自然,尽人事以听天命,而不信命则不然,会逆天而行,所以消耗的更快,看上去轰轰烈烈,实际上很快弹尽粮绝。不过,奇怪的是,竟然有人把“就是死了,也要轰轰烈烈”当作格言,甚至人不在少数。我想,这样的人归根结底,是内在的病态,也是一种性质,就是逆流而上的性质,非要挣扎,一身伤残。这样的人自然是可以存在,但是性质不是这样的人,不该去学,世界上大部分人都是水流地下的泥沙,不快,但总归还是会随着水流慢慢前行,而若这些人非觉得要不一样,那他们只会成为逆流而上的那种,而不可能顺流而下的那种,逆流而上本质上是降级,是更容易的事,而泥沙的性质不可能升格为枯叶般轻快,这是性质决定,但非要降级自己的性质,终究会消散在时间里。
今日就讲这么多,并不值得去深入。
-
明灯不息的熊熊烈火才正是最为纯粹的生命本质(逃#1169 rxliuli 2022-02-26 09:03
将行内渲染到svg保存到本地
在一些情况下,我们想要将类似d3.js之类的框架渲染的svg保存到本地,这要怎么实现呢?其实非常简单。
首先,让我们创建一个用来下载的函数,这个函数不仅仅支持svg,甚至可以支持任意内容。
function download(href, name) { var a = document.createElement('a'); a.download = name; a.href = href; document.body.appendChild(a); a.click(); document.body.removeChild(a); }
我们创建了一个通用的下载函数,但是这个href只支持url,所以,我们要创建一个函数来获得svg的url。
function createObjectUrl(content, type) { return window.URL.createObjectURL(new Blob(content, { type: type })) }
这个函数的content是可以支持多个类型的,因为它会被传入new Blob中,但是需要注意,如果传入字符串,要传入一个数组。
download(createObjectUrl(['<div></div>'], 'text/html'), 'index.html')
现在就好办了,我们只要把svg元素的outerHTML读出来,然后传给download函数就可以了。
download(createObjectUrl([document.querySelector('svg').outerHTML, 'image/svg'), 'some.svg')
对上面操作进行简单封装
function downloadSvg(svg, name) { download(createObjectUrl([document.querySelector(svg).outerHTML, 'image/svg'), name) }
如果要下载为其他类型的图片,那在download之前,要进行一个转码,我们用canvas作为中转,把svg放到一个canvas中之后在下载。
function downloadSvgAsPng(svg, name) { const text = document.querySelector(svg).outerHTML const { width, height } = svg // 或者通过其他方式也可以获得宽高 const image = new Image() image.onload = () => { const canvas = document.createElement('canvas') canvas.width = width cnavas.height = height const context = canvas.getContext('2d') context.fillStyle = context.createPattern(image, 'repeat') context.fillRect(0, 0, width, height) download(canvas.toDataURL('image/png'), name) } image.src = 'data:image/svg+xml;base64,' + btoa(text) }
此处我们没有用到createObjectUrl,因为我们直接将svg转化为base64然后渲染到canvas里面,变成了位图。
最后,需要注意的是,使用这种方法,我们必须在svg内完全定义自身的样式,如果你是在svg之外定义了svg内部元素的样式,那么下载的svg就不包含这部分样式,那自然就会有样式问题。
ScopedQuery: 查询你需要的数据,剪裁无用字段
作为前端,你肯定经常有这样的烦恼:我明明只需要这几个字段,结果你全部给我返回了,何必呢?今天我发了一个包 scopedquery 以解决这个问题。这里 scoped 的意思是“限定的”,也就是你进行的查询是在限定的内容里面。它基于一个新的语言,样子大概如下:
query "https://xxx.com/api/articles/:id" -> { article_title create_time: date('YYYY-MM-DD') article_content view_count: number comments: [ { comment_author comment_content comment_time: data('YYYY-MM-DD') } ] }
上面这段代码所表达的意思是,往 "http...." 发送了一个请求,这个请求要求返回的结果的形状,以及节点上对节点值的格式,需要按照 -> 后面的内容进行返回。
它看上去和 graphql 有点像,但又很不同。它是一个描述性语言,类似 JSON,而非一个编程性语言,即 graphql。graphql 虽然很好,但是,它依赖底层的建设和比较难理解的语法组织。而 ScopedQuery 则纯粹是为了解决数据裁剪而生,不负责底层库的查询,因此,它更轻量,且开箱即用。
npm i scopedquery
安装好后,在业务代码中这样写作:
import { Query } from 'scopedquery' const data = await Query.run(` query "http..." -> { // 支持注释 // 裁剪后的内容 } `)
默认情况下,内部会使用全局的 fetch 进行 ajax 请求,但是你可以自己定义:
const query = new Query({ fetch(url, params) { // ... } }) const data = await query.run(`...`)
在 node 端,可以使用 Query.parse 方法直接裁剪数据:
const data = Query.parse(dataFromBackend, `{ name: string age: number }`)
冒号后面的 string, number 看上去是类型,实际上是格式化工具,它们可以被自定义,让开发者自己觉得不同情况下怎么返回值。
基于 webpack 的能力,我们可以把这些 query 文本单独保存,这样我们可以极为清晰的了解每一个接口对于前端而言,需要的是哪些内容,且有值描述,对于前后端而言,都可以作为参考,辅助前后端在开发过程中进行沟通。