Taogen's Blog

Stay hungry stay foolish.

Iconfont 是阿里妈妈MUX倾力打造的矢量图标管理、交流平台。

设计师将图标上传到 iconfont 平台,用户可以自定义下载多种格式的icon,平台也可将图标转换为字体,便于前端工程师自由调整与调用。

Unicode 引用

  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • 默认情况下不支持多色,直接添加多色图标会自动去色。

第一步:引入项目的样式文件

找到你的项目:资源管理 –> 我的项目 –> 我参与的项目 –> xxx项目

选择使用 Unicode 外链或者将代码下载至本地

/* 引入外链 */
@font-face {
font-family: 'myIcon'; /*自定义,默认为项目名称*/
src: url('//at.alicdn.com/t/c/font_xxx.woff2?t=1686623243724') format('woff2'),
url('//at.alicdn.com/t/c/font_xxx.woff?t=1686623243724') format('woff'),
url('//at.alicdn.com/t/c/font_xxx.ttf?t=1686623243724') format('truetype');
}
/* 引入本地文件 */
@font-face {
font-family: 'myIcon'; /*自定义,默认为项目名称*/
src: url('iconfont.woff2?t=1625109690491') format('woff2'),
url('iconfont.woff?t=1625109690491') format('woff'),
url('iconfont.ttf?t=1625109690491') format('truetype');
}

第二步:定义全局的 unicode 图标样式

/* 自定义 className */
.myIconFont {
font-family: "myIcon" !important; /* 需要与你的@font-face中的font-family保持一致 */
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

第三步:挑选相应图标并获取字体编码,应用于页面

<span class="myIconFont">&#x33;</span>
<span class="myIconFont">&#xe718;</span>
<i class="myIconFont">&#xe718;</i>

修改图标大小:设置 font-size

<span class="myIconFont" style="font-size: 500px;">&#x33;</span>

修改图标颜色:设置 color

<span class="myIconFont" style="color: #8BC34A;">&#x33;</span>

Font Class 引用

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

与 Unicode 使用方式相比,具有如下特点:

  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。

第一步:引入项目的样式文件

找到你的项目:资源管理 –> 我的项目 –> 我参与的项目 –> xxx项目

选择使用 Font class 外链或者将代码下载至本地

<!-- 引入外链 -->
<link rel="stylesheet" type="text/css" href="//at.alicdn.com/t/font_xxx.css">
<!-- 引入本地文件 -->
<link rel="stylesheet" type="text/css" href="./iconfont.css">

第二步:挑选相应图标并获取类名,应用于页面

<!-- 注意:第一个 class 需要与 iconfont.css 文件中的 @font-face {font-family: "myIconFont"} 保持一致,默认项目名,可自定义。第二个 class 是icon+图标的名称 -->
<span class="myIconFont icon-xxx"></span>

改变图标的大小:重写 font-size 属性

<span class="myIconFont icon-xxx" style="font-size: 500px;"></span>

Symbol 引用

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

  • 支持多色图标了,不再受单色限制。
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • 浏览器渲染 SVG 的性能一般,还不如 png。

第一步:引入项目的样式文件

找到你的项目:资源管理 –> 我的项目 –> 我参与的项目 –> xxx项目

选择使用 Symbol 外链或者将代码下载至本地

<!-- 引入外链 -->
<script type="text/javascript" src="//at.alicdn.com/t/c/font_xxx.js">
<!-- 引入本地文件 -->
<script type="text/javascript" src="./iconfont.js"></script>

第二步:定义全局的 symbol 图标样式

<style>
/*自定义样式class名称*/
.myIcon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

第三步:挑选相应图标并获取类名,应用于页面

<!-- 样式名称与上一步定义的保持一致 -->
<svg class="myIcon" aria-hidden="true">
<use xlink:href="#icon-xxx"></use>
</svg>

改变图标的大小:设置 font-size 属性

<svg class="myIcon" aria-hidden="true" style="font-size: 500px;">
<use xlink:href="#icon-xxx"></use>
</svg>

改变图标的大小:同时设置 width 和 height 属性(只设置一个没有作用)

<svg class="myIcon" aria-hidden="true" style="width: 300px; height: 300px;">
<use xlink:href="#icon-xxx"></use>
</svg>

References

Background

Using HTTP domain visit my website is ok, but HTTPS not.

Error Info

ERR_TIMED_OUT

This site can’t be reached
xxx.xxx.com took too long to respond.
Try:
- Checking the connection
- Checking the proxy and the firewall
- Running Windows Network Diagnostics
ERR_TIMED_OUT

Solutions

ERR_TIMED_OUT represents this is a connection error, not SSL certificate errors.

Common Solution

  • Check whether the server IP or HTTP domain can be visited. If ok, the connection is OK.
  • Check whether the server reversed proxy is right, and server firewall is open 80 and 443.
  • Check whether the client browser and client network is right.

Current HTTP is work represented the connection is ok. And client browser can visit other HTTPS websites, represented client is ok.

You need to check your server reversed proxy is right, and ports 80 and 443 of your server firewall are open .

Reasons

My server’s port 443 is not open.

References

[1] https timeout while http works

Background

I call APIs of OSS (object storage services) to upload my local file with Java Input Stream.

Error Info

The uploaded file is 0 bytes.

Solutions

  • Check your accessed file exists and the file is not 0 byte.
  • Before the inputStream upload, using Java InputStream available() method to check out whether the remaining number of bytes that can be read from the file input stream is 0.
  • Make sure the remaining number of bytes that can be read from the file input stream is right.

Reasons

The reason of the size of my uploaded file is 0 byte is I read the input stream two times.

Most of input stream classes in Java API are not support read more than once. Because some Input Stream classes of Java API not support mark() and reset() method.

Although the FilterInputStream child classes can support read more than once by its mark(), reset() methods, and internal cache array byte buf[], you still need to call it manually. The reset() method is for repositions this stream to the position at the time the mark method was last called on this input stream.

Receive from Request URL Query String

Integer Array

Frontend Pass with HTTP request

  • url?ids=1,2,3
  • url?ids=1&ids=2&ids=3

Backend receive in controller methods

  • Integer[] ids
  • @RequestParam Integer[] ids
  • @RequestParam List<Integer> ids

String Array

Frontend Pass with HTTP request

  • url?ids=a,b,c
  • url?ids=a&ids=b&ids=c

Backend receive in controller methods

  • String[] ids
  • @RequestParam String[] ids
  • @RequestParam List<String> ids

Receive from Form Data

Integer Array

Frontend Pass with HTTP request

  • ids=1,2,3
  • ids=1, ids=2, ids=3

Backend receive in controller methods

  • Integer[] ids
  • @RequestParam Integer[] ids
  • @RequestParam List<Integer> ids

String Array

Frontend Pass with HTTP request

  • ids=a,b,c
  • ids=a, ids=b, ids=c

Backend receive in controller methods

  • String[] ids
  • @RequestParam String[] ids
  • @RequestParam List<String> ids

Receive from request body JSON

Integer Array

Frontend Pass with HTTP request

  • [1,2,3]

Backend receive in controller methods

  • @RequestBody Integer[] ids
  • @RequestBody List<Integer> ids

String Array

Frontend Pass with HTTP request

  • [“a”,”b”,”c”]

Backend receive in controller methods

  • @RequestBody String[] ids
  • @RequestBody List<String> ids

Download file streams with Axios

download.js

const axios = require('axios').default; // or import axios from 'axios'
const baseUrl = process.env.VUE_APP_BASE_API

/**
* Download file stream
*
* @param uri e.g. /web/article/exportExcel
* @param params e.g. {id: 1}
*/
export function download(uri, params) {
var url = baseUrl + uri
return axios.get(url, {
params:params,
responseType: 'blob',
}).then((response) => {
// response.data: file stream or error message defined by developers such as {"msg":"something went wrong...","code":500}
resolveBlob(response);
}).catch(error => {
// error.response.data: spring framework ResponseEntity object
alert("Fail to download file")
})
}

function resolveBlob(response) {
const headerval = response.headers['content-disposition'];
if (headerval != null) {
let filename = headerval.split(';')[1].split('=')[1].replace('"', '').replace('"', '');
filename = decodeURI(filename);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(url);
link.remove();
} else {
handleKnownException(response);
}
}

function handleKnownException(response) {
var reader = new FileReader();
reader.onload = function() {
if (reader.result != null) {
const responseData = JSON.parse(reader.result);
if (responseData.code == 500) {
alert(responseData.msg);
}
}
}
reader.readAsText(response.data);
}

Notice:

  • responseType: 'blob'
  • filename = decodeURI(filename)

article.vue

<template>
...
</template>

<script>
import { downLoadFile } from '@/utils/download';

export default {
...
methods: {
handleExportExcel() {
this.fullscreenLoading = true;
downLoadFile("/web/article/exportExcel", this.searchParams)
.then(() => {
this.fullscreenLoading = false;
});
}
}
}
</script>

Here are several ways to access parent context inside child context.

  1. Use ES6 Arrow functions.

An arrow function does not have its own bindings to this or super, this is inherited from the scope. In regular function, this is the function itself (it has its own scope).

getData() {
ajaxRequest(query).then(response => {
this.data = response.data;
});
}
  1. Store reference to context/this inside another variable, If you can’t use ES6 syntax.
getData() {
let self = this;
ajaxRequest(query).then(function(response) {
self.data = response.data;
});
}

References

[1] Arrow function expressions

[2] How to access the correct this inside a callback?

  1. Put files into /public directory

  2. Write your download link

Download /public/pdf/instruction.pdf

<a href="/pdf/instruction.pdf" download>download instruction</a>

Preview and download /public/pdf/instruction.pdf

<a href="/pdf/instruction.pdf" target="_blank">download instruction</a>

In Vue, the parent-child component relationship can be summarized as props down, events up. The parent passes data down to the child via props, and the child sends messages to the parent via events…

Pass Data Between Parent and Child Components

props and $emit

  • Using props to share data from parent to child
  • Emitting custom events to share data from child to parent

parent.vue

<template>
<div style="padding: 10px 10px 10px 10px">
<h2>This is the parent page</h2>
<div>
<button @click="onSendClick">Send to Child</button>
</div>
<div>
Received from child by emit: {{receiveFromChild}}
</div>
<Child :name="childName" @receiveFromChild="onReceiveFromChild"/>
</div>
</template>

<script>
import Child from "./Child";
export default {
name: "Parent",
components: {
Child,
},
methods: {
onSendClick() {
this.childName = Math.random().toString(36).substring(7);
},
onReceiveFromChild(value) {
this.receiveFromChild = value;
}
},
data() {
return {
receiveFromChild: undefined,
childName: undefined
}
},
}
</script>

Child.vue

<template>
<div style="background-color: lightgray; padding: 10px 10px 10px 10px">
<h2>I'm the child page</h2>
<p>Receive from parent by props: {{name}}</p>
<button @click="onSendButtonClick">Send to parent</button>
</div>
</template>

<script>
export default {
name: "Child",
props: {
name: {
type: String,
default: "",
},
},
methods: {
onSendButtonClick() {
let sendValue = Math.random().toString(36).substring(7) + " " + new Date().toISOString().replace("T", " ").replace("Z", "");
this.$emit("receiveFromChild", sendValue);
}
},
}
</script>

Using Vuex to create an app-level shared state.

Call Methods between Parent and Child Components

Call child methods in Parent Components

Call child methods by ref of Vue 2

parent.vue

<div class="form">                          
<child-component ref="childComponentRef" />
</div>
this.$refs.childComponentRef.doSomething();

Call parent methods in Child Components

Call parent methods by props

parent.vue

<div class="form">                          
<child-component :doSomething="doSomething" />
</div>

child.vue

props: {
doSomething: {
type: Function
}
},

Summary

Update child components status

  • Using props to share data from parent to child. (control child status in parent)
  • Call child methods by ref. (control child status directly)

Update parent components status

  • Emitting custom events to share data from child to parent. (control parent status in child)
  • Call parent methods by props. (control parent status directly)

类型改变,如 Integer 改为 String 或 Integer 改为 Long。

if (xxx.equals(Object obj))

当成员变量的数据类型改变后,直接量与成员变量比较时,比较结果可能和之前的结果不同。如:

new Integer(1).equals(new Long(1)) // 返回 false
new Integer(1).equals("1") // 返回 false
new Long(1).equals(1) // 返回 false

发生错误的场景,如:

Integer type1 = 1;
if (type1.equals(type)) {
...
}

解决方法:与该成员变量的比较的值,数据类型要保持一致。

map.get(Object key)

若该变量作为 map 的 key 时,可能出现新的数据类型在 map 中找不到对应的 key。

HashMap put(key, value) 方法有指定数据类型的约束,而 get(Object key) 方法没有对应的数据类型约束。字段类型改变后,put 方法有错误提示,而 get 方法没有提示。在 map 对象中不同的数据类型的 key 是不同的 key,从而会出现改完数据类型后找不到对应的 key。

Map<Integer, String> map = new HashMap()

解决方法:put(key, value) 和 get(Object key) 的 key 的数据类型要保持一致。

方法的重载

若该变量作为方法的参数时,存在多个重载方法,改完类型后,可能调用的不是之前的方法。如存在重载方法 getById(Integer id) 和 getById(Serializable id)。

解决方法:检查该成员变量作为参数调用方法的地方,跳转的方法是不是之前的方法。

JSON Serialization

Custom Serializing Enumeration

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.taogen.demo.modules.task.enumeration.NameValueEnum;

import java.io.IOException;

/**
* Serializing Enums to JSON
*/
public class NameValueEnumSerializer extends StdSerializer<NameValueEnum> {

protected NameValueEnumSerializer() {
super(NameValueEnum.class);
}

protected NameValueEnumSerializer(Class t) {
super(t);
}

@Override
public void serialize(NameValueEnum nameValueEnum, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeFieldName("name");
jsonGenerator.writeString(nameValueEnum.getName());
jsonGenerator.writeFieldName("value");
jsonGenerator.writeString(nameValueEnum.getValue());
jsonGenerator.writeEndObject();
}
}
public interface NameValueEnum {

public String getName();

public void setName(String name);

public String getValue();

public void setValue(String value);
}
@JsonSerialize(using = NameValueEnumSerializer.class)
public enum TaskPriority implements NameValueEnum {
EMERGENCY("emergency", "紧急"),
HIGH("high", "高"),
GENERAL("general", "普通"),
LOW("low", "低")
;

private String name;

private String value;

@Override
@JsonValue
public String getName() {
return name;
}

@Override
public void setName(String name) {
this.name = name;
}

@Override
public String getValue() {
return value;
}

@Override
public void setValue(String value) {
this.value = value;
}
}
public class Task extends BaseEntity {
private TaskPriority priority;
}

JSON Deserialization

Deserializing Comma Separated String to List

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class CommaSeparatedStrToListJsonDeserializer
extends JsonDeserializer<List<String>> {

@Override
public List<String> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
if ("{specifiedField}".equals(jsonParser.currentName())) {
String fieldStr = jsonParser.getValueAsString();
if (fieldStr != null && !fieldStr.trim().isEmpty()) {
return Arrays.stream(fieldStr.split(",")).collect(Collectors.toList());
}
}
return null;
}
}
@JsonDeserialize(using = CommaSeparatedStrToListJsonDeserializer.class)
private List<String> {specifiedField};

Deserializing JSON Array to String

public class JsonArrayToStringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
if (jsonParser.currentToken() == JsonToken.START_ARRAY) {
List<String> list = new ArrayList<>();
jsonParser.nextToken();

while (jsonParser.hasCurrentToken() && jsonParser.currentToken() != JsonToken.END_ARRAY) {
list.add(jsonParser.getValueAsString());
jsonParser.nextToken();
}
return String.join(",", list);
}
return null;
}
}
@JsonDeserialize(using = JsonArrayToStringDeserializer.class)
private String {specifiedField};

References

[1] How To Serialize and Deserialize Enums with Jackson

0%