在使用 vue + vite/webpack + view-ui-plus(之类的UI框架)方案搭建的项目时,经常会有需要定制样式的需求,UI框架官方给出一种比较好用的方案就是Less变量覆盖法:
变量覆盖#
如果你的项目使用了 webpack/vite 工程,可以通过变量覆盖的方式来实现主题定制。
首先在项目中先建一个目录,比如
my-theme
,然后在 my-theme 下建立一个 less 文件index.less
,并写入下面内容:@import '~view-ui-plus/src/styles/index.less'; // Here are the variables to cover, such as: @primary-color: #8c0776;
完整的变量列表可以查看 默认样式变量。
然后在入口文件
main.js
内导入这个 less 文件即可:import '../my-theme/index.less';
使用这种方法时,按照其原理,就不应该再引用UI框架生成的css文件:
import 'view-ui-plus/dist/styles/viewuiplus.css'
但如果不引用该文件,字体图标就会无法显示,浏览器会提示相关iconfont字体文件404,引用该文件,虽然能显示,但会产生很多不必要的重复样式代码,造成项目文件冗余,而且vue3 + vite + TypeScript + view-ui-plus的架构中还是会报字体文件404错误。
所以,应该寻求一种更好的解决方案,看报错信息就知道是样式中的字体文件找不到才导致字体图标无法显示的,查询less官方网站,发现了一个配置项:
Rewrite URLs
lessc -ru=off
lessc --rewrite-urls=off
{ rewriteUrls: 'off' }
lessc -ru=all
lessc --rewrite-urls=all
{ rewriteUrls: 'all' }
lessc -ru=local
lessc --rewrite-urls=local
{ rewriteUrls: 'local' }
By default URLs are kept as-is (
off
), so if you import a file in a sub-directory that references an image, exactly the same URL will be output in the css. This option allows you to rewrite URLs in imported files so that the URL is always relative to the base file that has been passed to Less. E.g./* main.less */ @import "global/fonts.less";
/* global/fonts.less */ @font-face { font-family: 'MyFont'; src: url('myfont/myfont.woff2') format('woff2'); }
With nothing set or with
rewriteUrls: 'off'
, compilingmain.less
will output:/* main.less */ /* global/fonts.less */ @font-face { font-family: 'MyFont'; src: url('myfont/myfont.woff2') format('woff2'); }
With
rewriteUrls: 'all'
, it will output:/* main.less */ /* global/fonts.less */ @font-face { font-family: 'MyFont'; src: url('./global/myfont/myfont.woff2') format('woff2'); }
With
rewriteUrls: 'local'
, it will only rewrite URLs that are explicitly relative (those starting with a.
):url('./myfont/myfont.woff2') /* becomes */ url('./global/myfont/myfont.woff2') url('myfont/myfont.woff2') /* stays */ url('myfont/myfont.woff2')
This can be useful in case you're combining Less with CSS Modules which use similar resolving semantics like Node.js.
You may also want to consider using the data-uri function instead of this option, which will embed images into the css.
大体意思就是,less默认不转换依赖文件的路径,但配置 rewriteUrls:'all'就会自动转换依赖文件的路径,于是在 vite.config.ts 进行以下配置:
defineConfig({
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,/*这个可以解决less方法无法执行的问题*/
rewriteUrls:'all'/*这个解决字体图标无法显示的问题*/
}
}
}
})
这样,在使用less变量覆盖法定制样式时,可以只引用less文件,不用再引用UI框架的css文件,而且字体图标也可以正常显示,无相关报错。
--------------------------------------------------2024年5月17日更新---------------------------------------------------
最近再次深入研究了下view-ui-plus这类UI库图标文件地址错误导致图标无法显示的问题,发现导致问题的原因是:
view-ui-plus在定义字体时,url使用了less变量
/* view-ui-plus/src/styles/common/iconfont/_ionicons-variables.less */
@ionicons-font-path: "./fonts";
@ionicons-font-family: "Ionicons";
@ionicons-version: "3.0.0";
/* view-ui-plus/src/styles/common/iconfont/_ionicons-font.less */
@font-face {
font-family: @ionicons-font-family;
src: url("@{ionicons-font-path}/ionicons.woff2?v=@{ionicons-version}") format("woff2"),
url("@{ionicons-font-path}/ionicons.woff?v=@{ionicons-version}") format("woff"),
url("@{ionicons-font-path}/ionicons.ttf?v=@{ionicons-version}") format("truetype"),
url("@{ionicons-font-path}/ionicons.svg?v=@{ionicons-version}#Ionicons") format("svg");
font-weight: normal;
font-style: normal;
}
导致我们的项目在引用view-ui-plus的less样式文件时,url不会被less编译器转换成正确的地址(上面代码中的url不会改变):
而前面说的rewriteUrls刚好解决了这个问题,但这样会带来一个新的问题:
项目内正常使用了静态文件的url会被改写导致异常,如:
正常情况,编译 main.less 将输出:
开启 rewriteUrls 后编译出错误地址:
在这样的前提下,我们的项目其实不能开启rewriteUrls,要解决字体文件地址错误问题,只能另寻他法。
回顾我前面讲的,你会发现,其实引起问题的就是view-ui-plus在定义字体时url使用了less变量。
要解决这个问题,也是从这less变量入手,在你定制UI库样式的less文件里改写iconfont字体文件地址:
在没有开启rewriteUrls的情况下,浏览器上图标正常显示了:
项目打包后,字体文件资源也是正常的:
至此,view-ui-plus字体文件地址错误导致图标无法正常显示的问题完美解决。
总结一下,导致问题发生的原因是view-ui-plus在定义图标字体时在url里使用了less变量;解决办法是在定制view-ui-plus库UI的less文件中改写字体文件url,同时注意一定不要使用rewriteUrls。
/* iViewTheme.less 定制view-ui-plus 库 UI 的less文件 */
@import "view-ui-plus/src/styles/index";
//改写iconfont字体文件地址
@ionicons-font-path: "view-ui-plus/src/styles/common/iconfont/fonts";