A beautiful solution for using React Native image resources

 

Image resources (jpeg, png, svg, webp ...), as interface elements to interact with users, play a very important role in client products. In application development, the mobile and PC picture usage strategies are also different. The larger memory capacity on the PC side and the fast rendering capability enable all types of picture resources to be better used. The mobile terminal has a big difference from the PC due to the device memory and GPU rendering, so in the application development of the App, we need to analyze and process it separately. Today we talk about how to use image resources gracefully in React Native development.

In React Native development, there are currently four mainstream icon solutions:

Basic picture format (png, jpeg)

The basic picture format is one of the most commonly used picture resources for developers. RN officially provides a solution for adaptation on the mobile terminal. You can use the Image tag to load network and local pictures.

[Disadvantages] It is necessary to introduce multiple graphs (@ 2x / @ 3x) for adaptation, which makes the size of jsBundle increase and consumes more memory. The hot update has a greater impact on traffic (although you don't care). Asset changes must accompany the binary version. (Apk | ipa)

Url

Save the picture on the server, and the client loads it by URL. The client does not need any processing, such as loading the network map in RN, only need to pass the URL to the src props of the Image  component  .

[Disadvantages] Caching is more troublesome, you can rely on the react-native-fast-image library.

IconFont (react-native-vector-icons)

Students who are familiar with Web development are absolutely no strangers to  font icons  . Based on this, the open source library react-native-vector-icons implements font icon solutions on the RN platform. This solution is simple, you can use font icons by importing libraries and  .ttf  files.

[Disadvantages] It needs to be packaged with the app, the file is small, and it is convenient to use. There is no need to worry about the screen size cannot be hot updated . An additional library needs to be introduced

Svg

Svg has a small ( Path ) and scalable features, so it does not need to adapt to the resolution size of the screen, and effectively reduces the problem of mobile terminal memory occupation. At the same time, it perfectly supports the hot update of the bundle

[Disadvantages] The RN platform does not support Svg by default. Fortunately, the react-native-svg library implements the ability to render Svg icons in RN mobile applications.

case analysis

The problem of multiple graph adaptation, .ttf file cannot be hot updated. Existing font icon management websites (iconfont, icomoo) can also generate svg files. So that we finally use Svg directly. react-native-svg can parse svg tags into pictures, how to render pictures into components? The third-party library react-native-svg-uri can parse the svg file into  xmlComponent。迫不及待的集成,引入,运行。iOS完美的显示出来,而将 Android 进行 Release 打包过程中,Logcat抛出如下错误:

The reason is that the Android platform does not support directly loading the file format of the .svg suffix, and only allows loading  png and  xml formatting of files. We open the .svg file and you can see the following:

<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1554188197278" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4579" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128">
<defs>
    <style type="text/css"></style>
</defs>
<path d="M790.24975 1.022977H228.123876c-42.453546 0-77.234765 35.292707-77.234765 78.769231v870.553446c39.896104 0 72.11988 33.246753 72.11988 73.654346h35.292707c0-19.948052 15.856144-36.315684 35.804196-36.315684s35.804196 16.367632 35.804196 36.315684h35.804196c0-19.948052 15.856144-36.315684 35.804196-36.315684s35.804196 16.367632 35.804195 36.315684h35.804196c0-19.948052 15.856144-36.315684 35.804196-36.315684s35.804196 16.367632 35.804196 36.315684h35.804196c0-19.948052 15.856144-36.315684 35.804195-36.315684s35.804196 16.367632 35.804196 36.315684h35.292707c0-19.948052 15.856144-36.315684 35.804196-36.315684s35.804196 16.367632 35.804196 36.315684h35.804196c0-40.407592 32.223776-73.142857 71.608391-73.142857V79.280719C867.484515 35.804196 832.703297 1.022977 790.24975 1.022977z m-118.153846 339.628372l-86.953047 88.999001h45.522478c16.879121 0 30.177822 13.298701 30.177822 30.68931s-13.298701 30.689311-30.177822 30.689311h-91.044955v62.401598h91.044955c16.879121 0 30.177822 13.298701 30.177822 30.689311s-13.298701 30.689311-30.177822 30.689311h-91.044955v93.090909c0 17.390609-13.298701 30.689311-30.177823 30.689311s-30.177822-13.298701-30.177822-30.689311v-93.090909H388.21978c-10.22977 0-20.971029-6.649351-26.085914-15.856144s-5.114885-21.482517 0-30.689311c5.114885-10.741259 15.856144-15.856144 26.085914-15.856144h91.044955V489.494505H388.21978c-16.879121 0-30.177822-13.298701-30.177822-30.68931s13.298701-30.689311 30.177822-30.689311h48.07992L345.254745 341.674326c-7.672328-8.183816-10.22977-19.948052-7.672327-30.689311s11.764236-19.948052 21.994006-22.505495c10.22977-4.091908 21.994006 0 30.177822 8.183817l119.688311 122.245754L629.130869 296.663337c7.672328-9.206793 19.436563-11.764236 30.177822-9.206794 10.22977 2.557443 19.436563 11.764236 21.994006 22.505495 2.557443 10.741259-1.022977 22.505495-9.206793 30.689311z" fill="#FF6F5A" p-id="4580">
</path>
</svg>

As you can see, in fact, the Svg file uses the path to draw the icon. Therefore, we can abandon the method of requiring svg files, directly parse the svg file to path 即可。同时也解决了reduce the space occupied by svg , and frequently require static files to slow down the problem.

We can use scripts to batch generate svg files to Path strings, and then parse Path xml through  react-native-svg-uri . At the same time, the author of the library also took into account the problems of Android, and provided developers with an Api: svgXmlData that  renders icons through the svg path string  .
【注意】react-native-svg-uri update is too slow, it is recommended not to install through npm or yarn , directly copy files to use in the project, to avoid version problems.

Core implementation

Through the above analysis, the implementation process is divided into the following three steps:

(1) Download Svg file

(2) Analyze all Svg files, and store the svg path in the js file

(3) Encapsulate the svg component and load the svg path through  svgXmlData 

It can be seen that the implementation of the second step plays an important role in inheriting the past and the future. Let's take a look at the core code.

Parsing

/**
 * 读取svg文件
 * @param {*} svgFileName svg文件, 例如 home-icon.svg
 * @returns { 'home-icon': '<svg>... <path>...</path> ...</svg>' }
 */
function readSvgFile(svgFileName) {
    return new Promise((resolve, inject) => {
        readFile(path.join(svgFileDir, svgFileName), 'utf8', (error, svgFile) => {
            // eslint-disable-next-line no-useless-escape
            const svgPath = svgFile.replace(/<\?xml.*?\?>|<\!--.*?-->|<!DOCTYPE.*?>/g, '');
            if (error) {
                inject(error);
            }
            resolve({
                [svgFileName.slice(0, svgFileName.lastIndexOf('.'))]: svgPath,
            });
        });
    });
}

/**
 * 读取svg文件夹目录所有svg文件
 * @returns { 'home-icon': '<path>...</path>', 'xxx': '<path>...</path>' ... }
 */
function readSvgDir() {
    return new Promise((resolve, inject) => {
        readdir(svgFileDir, (error, svgFiles) => {
            if (error) {
                inject(error);
            }
            // svgFiles: string[]
            Promise.all(svgFiles.map((svgFileName) => readSvgFile(svgFileName)))
                .then((data) => resolve(data))
                .catch((err) => inject(err));
        });
    });
}

/**
 * 生成 .js 文件
 */
readSvgDir().then((data) => {
    const svgFile = `export default {
        ${
    data.map((item, index) => `${Object.keys(item)[0]}: '${Object.values(item)[0]}'\n`)
}
    }`;
    writeFile(path.resolve(__dirname, `./${GENERATE_SVG_FILE_NAME}`), svgFile, (err) => {
        if (err) {
            throw new Error(err);
        }
    });
}).catch((error) => {
    throw new Error(error);
});

As you can see from the above code, we can get the string of <svg> ... <path> ... </ path> ... </ svg> by reading through all the svg files in the svg folder, And unified management of the generated js file. The content of the resulting file is as follows:

export default {
    iconImage: '<svg class="icon" width="128px" height="128.00px" viewBox="0 0 />',
};

Encapsulate the SvgIcon component

/**
 * svg 图片组件
 * @export
 * @class SvgIcon
 * @extends {PureComponent}
 */
import React, { PureComponent } from 'react';
import { View } from 'react-native';
import PropTypes from 'prop-types';
import SvgUri from './SvgUri';
import svgXmlData from './svgXmlData';

export default class SvgIcon extends PureComponent {
    static propTypes = {
        style: PropTypes.object,
        /* eslint-disable react/require-default-props */
        color: PropTypes.object,
        size: PropTypes.shape(
            {
                width: PropTypes.number.isRequired,
                height: PropTypes.number.isRequired,
            }
        ).isRequired,
        icon: PropTypes.string.isRequired,
    }

    static defaultProps = {
        style: {},
    }

    render() {
        const {
            size,
            color,
            style,
            icon,
        } = this.props;
        const svgXmlPath = svgXmlData[icon];
        // eslint-disable-next-line no-nested-ternary
        return svgXmlData
            ? color ? (
                <SvgUri
                    fill={color}
                    style={style}
                    width={size.width}
                    height={size.height}
                    svgXmlData={svgXmlPath}
                />
            ) : (
                <SvgUri
                    style={style}
                    width={size.width}
                    height={size.height}
                    svgXmlData={svgXmlPath}
                />
            ) : <View />;
    }
}

use

<SvgIcon
    icon={iconImage}
    style={{ marginRight: 5 }}
    size={{ width: 26, height: 26 }}
/>

Through the above implementation, we have beautifully solved the problem of loading and rendering image resources. The detailed code has been uploaded to GitHub: react-native-svg-icon

WebP

By default, React Native does not support GIF and WebP formats. Android needs android/app/build.gradleto manually add the following modules in the file as needed:

dependencies {
  // 如果你需要支持Android4.0(API level 14)之前的版本
  compile 'com.facebook.fresco:animated-base-support:1.10.0'

  // 如果你需要支持GIF动图
  compile 'com.facebook.fresco:animated-gif:1.10.0'

  // 如果你需要支持WebP格式,包括WebP动图
  compile 'com.facebook.fresco:animated-webp:1.10.0'
  compile 'com.facebook.fresco:webpsupport:1.10.0'

  // 如果只需要支持WebP格式而不需要动图
  compile 'com.facebook.fresco:webpsupport:1.10.0'
}

iOS needs to rely on a third-party platform to load Webp, such as: react-native-webp-supprot  . Recommend everyone to read this article:

React Native + WebP: Reducing bundle + binary sizes, increase speed with .webp image format

 

Published 214 original articles · praised 371 · 920,000 views

Guess you like

Origin blog.csdn.net/u013718120/article/details/88975393