Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(svg-icon):插件svg-icon #551

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions packages/plugin-locale/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default (api: IApi) => {
}));
}

const addAntdLocales: IAddAntdLocales = async args =>
const addAntdLocales: IAddAntdLocales = async (args) =>
await api.applyPlugins({
key: 'addAntdLocales',
type: api.ApplyPluginsType.add,
Expand Down Expand Up @@ -117,10 +117,10 @@ export default (api: IApi) => {
const localeList = await getList();
const momentLocales = localeList
.map(({ momentLocale }) => momentLocale)
.filter(locale => locale);
.filter((locale) => locale);
const antdLocales = localeList
.map(({ antdLocale }) => antdLocale)
.filter(locale => locale);
.filter((locale) => locale);

let MomentLocales = momentLocales;
let DefaultMomentLocale = '';
Expand Down Expand Up @@ -211,7 +211,9 @@ export default (api: IApi) => {
});

// Runtime Plugin
api.addRuntimePlugin(() => join(paths.absTmpPath!, 'plugin-locale/runtime.tsx'));
api.addRuntimePlugin(() =>
join(paths.absTmpPath!, 'plugin-locale/runtime.tsx'),
);

// Modify entry js
api.addEntryCodeAhead(() =>
Expand Down
5 changes: 5 additions & 0 deletions packages/plugin-svg-icon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# 1.0.0 (2021-03-09)

### Features

- add plugin-svg-icon
75 changes: 75 additions & 0 deletions packages/plugin-svg-icon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# @umijs/plugin-svg-icon

> @umijs/plugin-svg-icon.

See our website [@umijs/plugin-svg-icon](https://umijs.org/plugins/plugin-svg-icon) for more information.

## 安装

使用 npm:

```bash
$ npm install --save-dev @umijs/plugin-svg-icon
```

使用 yarn:

```bash
$ yarn add @umijs/plugin-svg-icon --dev
```

## 功能

自动配置 svgo 并将 svg 打包为 svg 雪碧图

## 使用方法

**注意 svg 文件只有放置在 svgs 的文件夹才会应用此配置!**

安装即可开启

如果想要自己配置 svgo 配置,可以在.umirc 配置文件同级下增加 svgo-config.json

实际使用:

- 1. 需要声明一个通用组件 ICON

```jsx
import { SvgIcon } from 'umi';
```

- 2. 使用此组件

```jsx
<Icon type={'love'} />
```

## TODO

- [ ] 1.将示例的 jsx 代码整合到插件内部。

- [ ] 2.升级 svgo 与 svg-sprite-loader 到最新版本

- [ ] 3.暴露 svg-sprite-loader 选项入口

## 为什么要使用此插件

- svg 优点多多,其在移动端有很强的适配能力,在 pc 端也可以防止图片变糊,可以多 svg 图片拼接组合等等,高效易用。但是,通常我们获得的 svg 图片并不能直接使用,它们可能来自第三方 svg 组件库,或者来自设计师的原稿输出,这些 svg 如果直接使用 svg as component 可能存在以下痛点:

1. 我们需要 svg 图标携带 hover 样式,但是 svg 被填充了 fill 属性

2. 内置的样式可能需要我们手动删除

3. 携带一些内部敏感信息需要屏蔽

4. 一个 svg 中的某部分全局中基本都有运用,但由于包裹的 svg 父元素不同可能会被多次请求。

当 icon 变得非常多的时候这将是重复的体力劳动。

- 目前推荐对 svg 使用的是本插件的 svg-icon 与 url-loader 结合使用:

1. svg 确实需要内联样式的效果需要定制,请使用 svg as component。

2. 有些 svg 不是全局 icon,而是单个引用,并且非常庞大还是需要使用 svg as component。

更多可以参考:[svg-sprite-loader](https://github.com/JetBrains/svg-sprite-loader) 与 [svgo](https://github.com/svg/svgo)
36 changes: 36 additions & 0 deletions packages/plugin-svg-icon/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@umijs/plugin-svg-icon",
"version": "1.0.0",
"description": "@umijs/plugin-svg-icon",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib",
"src"
],
"repository": {
"type": "git",
"url": "https://github.com/umijs/plugins"
},
"keywords": [
"umi"
],
"authors": [
"Cyberhan123 <[email protected]> (https://github.com/Cyberhan123)"
],
"license": "MIT",
"bugs": "http://github.com/umijs/plugins/issues",
"homepage": "https://github.com/umijs/plugins/tree/master/packages/plugin-svg-icon#readme",
"peerDependencies": {
"umi": "3.x"
},
"publishConfig": {
"access": "public"
},
"dependencies": {
"classnames": "^2.3.1",
"svg-sprite-loader": "^6.0.6",
"svgo": "^2.3.0",
"svgo-loader": "^3.0.0"
}
}
7 changes: 7 additions & 0 deletions packages/plugin-svg-icon/src/fixtures/normal/.umirc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
routes: [{ path: '/', component: 'index' }],
// svgIcon: {},
plugins: [
require.resolve('../../'),
],
};
15 changes: 15 additions & 0 deletions packages/plugin-svg-icon/src/fixtures/normal/assets/svg/wechat.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions packages/plugin-svg-icon/src/fixtures/normal/assets/svgs/love.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions packages/plugin-svg-icon/src/fixtures/normal/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { forwardRef } from 'react';

import '../assets/svgs/love.svg';
import '../assets/svgs/alipay.svg';
// @ts-ignore
import { ReactComponent as Wechat } from '../assets/svg/wechat.svg';
// import { SvgIcon } from 'umi';

const Icon = forwardRef((props: any, ref) => {
const { type, className, link, ...htmlProps } = props;
const iconName = `#icon-${type}`;
const Parent = link ? 'a' : 'i';
return (
<Parent ref={ref} className={`svg-icon svg-icon-${props.type}`} {...htmlProps}>
<svg aria-hidden='true'>
<use xlinkHref={iconName} />
</svg>
</Parent>
);
});
const App = () => {
return (
<>
{/*<SvgIcon type={'love'} />*/}
{/*<SvgIcon type={'alipay'} />*/}
<Icon type={'love'} />
<Icon type={'alipay'} />
<Wechat width={90} height={120}/>
</>

);
};
export default App;
26 changes: 26 additions & 0 deletions packages/plugin-svg-icon/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { join } from 'path';
import { generateTmp, render } from '@umijs/test-utils';
import { cleanup } from '@testing-library/react';

const fixtures = join(__dirname, 'fixtures');

afterEach(cleanup);

test('normal', async () => {
const cwd = join(fixtures, 'normal');
await generateTmp({ cwd });
const { container } = render({ cwd });
expect(container.innerHTML).toEqual(
'<i class="svg-icon svg-icon-love">' +
'<svg aria-hidden="true">' +
'<use xlink:href="#icon-love"></use>' +
'</svg>' +
'</i>' +
'<i class="svg-icon svg-icon-alipay">' +
'<svg aria-hidden="true">' +
'<use xlink:href="#icon-alipay"></use>' +
'</svg>' +
'</i>' +
'<svg width="90" height="120">wechat.svg</svg>',
);
});
94 changes: 94 additions & 0 deletions packages/plugin-svg-icon/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { IApi, utils } from 'umi';
import { join } from 'path';
import { readFileSync } from 'fs';

const { Mustache, lodash, winPath } = utils;
export default (api: IApi) => {
const { logger } = api;
api.describe({
config: {
schema(Joi) {
return Joi.object({
// svgoConfig: Joi.string(),
});
},
},
});
//src 路径
const srcDir = api.paths.absSrcPath;

function getSvgDir(path: string) {
return join(srcDir!, path);
}

// svg 文件夹路径
const svgsDir = getSvgDir('assets/svgs');

//获取svgoConfig 路径
function getConfigPath() {
let svgoConfigDirFile = null;
//项目根目录位置
const svgoConfigDirFilePath = api.utils.winPath(
join(srcDir!, '../svgo-config.json'),
);
//插件根目录位置
const svgoConfigDefaultFilePath = api.utils.winPath('../svgo-config.json');
try {
svgoConfigDirFile = require.resolve(svgoConfigDirFilePath);
} catch (e) {
console.log(
api.utils.chalk.yellow(
`[svg-icon]svgo-config.json doesn't find at ${svgoConfigDirFilePath}, svg-icon auto use default config`,
),
);
}
const svgoConfigDefaultFile = require.resolve(svgoConfigDefaultFilePath);
//如果配置文件不存在使用默认配置
const svgoConfigFile = svgoConfigDirFile ?? svgoConfigDefaultFile;
return require(svgoConfigFile);
}

// 生成svgIcon 组件
api.onGenerateFiles(() => {
const svgIcon = readFileSync(join(__dirname, 'svgIcon.tpl'), 'utf-8');
api.writeTmpFile({
path: 'plugin-svg-icon/svgIcon.tsx',
content: Mustache.render(svgIcon, {
//
SSR: !!api.config?.ssr,
}),
});
});

// // 增加 svgIcon 运行时配置
// api.addRuntimePlugin(() =>
// [join(api.paths.absTmpPath!, 'plugin-svg-icon/svgIcon.tsx')],
// );

// 导出内容
api.addUmiExports(() => [
{
exportAll: true,
source: '../plugin-svg-icon/svgIcon',
},
]);
api.chainWebpack((config) => {
// 默认的svg的模块规则中不去匹配src/assets/svgs,避免此文件中的内容使用默认的url-loader的加载形式
config.module.rule('svg').exclude.add(svgsDir).end();
config.module
.rule('svg-sprite-svgo-loader')
.test(/\.svg$/)
.include.add(svgsDir) //处理svg目录
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]',
})
.end()
.use('svgo-loader')
.loader('svgo-loader')
.options(getConfigPath());
return config;
});
};
Loading