Click “like” and then look, wechat search [Big Move the world] pay attention to this person without dACHang background, but with a positive attitude upward. In this paper, making github.com/qq449245884… Has been included, the article has been categorized, also organized a lot of my documentation, and tutorial materials.
Everyone said there was no project on your resume, so I found one and gave it away【 Construction tutorial 】.
Introduction to the
This wheel is built with React + TypeScript + Webpack. Of course you can refer to my source code.
Here I also learn through others, mainly do some summary and explain a way of thinking about making each wheel, to facilitate the future use of other people’s wheels when their own mind has the idea of making wheels, to modify the source code to timely modify bugs, on time online.
The Icon Component of this article is written in reference to the Icon React Component in Framework7.
To read more quality articles pleaseJabber at the GitHub blog, a hundred quality articles a year waiting for you!
Why the wheel
1. Not asking for help
-
Let’s say you’re using a UI framework and you find a bug, and you give it back to the developer, and the developer says it’s going to be fixed in two weeks, and your project is going to go live in a week, what do you do?
-
Why do many big companies build their own wheels instead of using other companies’ wheels? In order to control their own business and not be led by others.
2. In order not to be mediocre
- Everyone is writing, adding, deleting, revising and checking. What advantage do you have over others? Wouldn’t it be awesome if you could say “everyone in my company is using the UI framework I wrote”? There’s a lot of technical, not business, knowledge involved in building UI wheels, right? Like some algorithms.
3. To create
- What you’ve been doing for so long for other people, do you do anything for yourself? Self-actuation.
4. Why UI wheels and not other wheels
- For example, why not write your own React framework, instead write the React UI framework?
The React. FunctionComponent and IconPropps
The wheels using the React + TypeScript to write, so how to declare in the ts function components and level Icon passing parameters? The answer is to use the static method react. FunctionComponent provided by React and TypeScript’s interface definition.
// lib/icon.tsx
import React from 'react'
interface IconProps {
name: string
}
const Icon: React.FunctionComponent<IconProps> = () => {
return (
<span>icon</span>
)
}
export default Icon
Copy the code
Call in index.txt:
import React from "react";
import ReactDOM from "react-dom";
import Icon from './icon'
ReactDOM.render(<div>
<Icon name='wechat'/>
</div>, document.body)
Copy the code
For the above definition, the rear wheel will be used a lot, so don’t worry about it.
Load SVG using svG-sprite-loader
We specified the name of the Icon as wechat, so how to make it display the wechat Icon? First download the corresponding SVG from Ali’s Iconfont
How do I display SVG next? Here we use an SVG-Sprite-loader library and add it to rules under the corresponding Webpack:
{
test: /\.svg$/,
loader: 'svg-sprite-loader'
}
Copy the code
Reference in Icon, which of course is also configured for tsconfig.json (this is not the focus of this article):
import React from 'react'
import wechat from './icons/wechat.svg'
console.log(wechat)
interface IconProps {
name: string
}
const Icon: React.FunctionComponent<IconProps> = () => {
return (
<span>
<svg>
<use xlinkHref="#wechat"></use>
</svg>
</span>
)
}
export default Icon
Copy the code
Operation effect:
Of course, SVG cannot be written to death directly, we need to specify the corresponding image according to the external name:
// 部分代码
import './icons/wechat.svg'
import './icons/alipay.svg'
const Icon: React.FunctionComponent<IconProps> = (props) => {
return (
<span>
<svg>
<use xlinkHref={`#${props.name}`}></use>
</svg>
</span>
)
}
Copy the code
External calls:
ReactDOM.render(<div>
<Icon name='wechat'/>
<Icon name='alipay'/>
</div>, document.getElementById('root'))
Copy the code
Operation effect:
importAll
Have you noticed which SVG I need to use? I need to import the corresponding SVG in the corresponding icon component. So if I need 100 SVG, I have to import 100 times.
So we need a way to dynamically import all SVG:
// lib/importIcons.js
let importAll = (requireContext) => requireContext.keys().forEach(requireContext)
try {
importAll(require.context('./icons/', true, /\.svg$/))
} catch (error) {
console.log(error)
}
Copy the code
If you want to understand the code of the appeal, you may need a little node.js foundation, which suggests that you save it directly, next time useful, directly copy over to use the line.
Import ‘./importIcons’ in the Icon component
The use of the React. MouseEventHandler
When we need to register an event for the Icon, we will get an error if we write the onClick event directly on the component, because it does not declare to receive the onClick event type, so we need to declare it, as follows:
/lib/icon.tsx
import React from 'react'
import './importIcons'
import './icon.scss';
interface IconProps {
name: string,
onClick: React.MouseEventHandler<SVGElement>
}
const Icon: React.FunctionComponent<IconProps> = (props) => {
return (
<span>
<svg onClick={ props.onClick}>
<use xlinkHref={`#${props.name}`} />
</svg>
</span>
)
}
export default Icon
Copy the code
Call as follows:
import React from "react";
import ReactDOM from "react-dom";
import Icon from './icon'
const fn: React.MouseEventHandler = (e) => {
console.log(e.target);
};
ReactDOM.render(<div>
<Icon name='wechat' onClick={fn}/>
</div>, document.getElementById('root'))
Copy the code
Make Icon respond to all events
Above we only listen for onClick events, but not for other events, so we need to improve. We can’t add event types one by one, we need a unified event type, so what is this?
React gives us an SVGAttributes class, which we need to inherit:
/lib/icon.tsx
import React from 'react'
import './importIcons'
import './icon.scss';
interface IconProps extends React.SVGAttributes<SVGElement> {
name: string;
}
const Icon: React.FunctionComponent<IconProps> = (props) => {
return (
<span>
<svg
onClick={ props.onClick}
onMouseEnter = {props.onMouseEnter}
onMouseLeave = {props.onMouseLeave}
>
<use xlinkHref={`#${props.name}`} />
</svg>
</span>
)
}
export default Icon
Copy the code
Call method:
import React from "react";
import ReactDOM from "react-dom";
import Icon from './icon'
const fn: React.MouseEventHandler = (e) => {
console.log(e.target);
};
ReactDOM.render(<div>
<Icon name='wechat'
onClick={fn}
onMouseEnter = { () => console.log('enter')}
onMouseLeave = { () => console.log('leave')}
/>
</div>, document.getElementById('root'))
Copy the code
The above will still have problems, we also have onFocus, onBlur, onChange and other events, it is not possible to pass in one by one, so what method is there?
In icon. TSX we can see that we are using props. A clever friend might immediately think of the form {… Props}, rewrite as follows:
. const Icon: React.FunctionComponent<IconProps> = (props) => { return ( <span> <svg className="fui-icon" {... props}> <use xlinkHref={`#${props.name}`} /> </svg> </span> ) } ...Copy the code
React: if the user passes a className to the user, then you know that Vue is really good. It merges the className with the user’s className. React overwrites the user’s className.
...
const Icon: React.FunctionComponent<IconProps> = (props) => {
const { className, ...restProps} = props
return (
<span>
<svg className={`fui-icon ${className}`} {...restProps}>
<use xlinkHref={`#${props.name}`} />
</svg>
</span>
)
}
...
Copy the code
If there is no className outside, then there will be a undefined inside
You might be smart enough to use the trinary operator to make a judgment, such as:
className={`fui-icon ${className ? className : ''}`}
Copy the code
But what if there are multiple arguments in this case?
So someone was smart enough to write an inventory of Classnames, how popular it is, more than 3 million downloads a week, and what it does is it handles classnames.
Of course, we only do the simple processing, as shown below
// helpers/classes function classes(... names:(string | undefined )[]) { return names.join(' ') } export default classesCopy the code
Usage:
...
const Icon: React.FunctionComponent<IconProps> = (props) => {
const { className, name,...restProps} = props
return (
<span>
<svg className={classes('fui-icon', className)} {...restProps}>
<use xlinkHref={`#${name}`} />
</svg>
</span>
)
}
...
Copy the code
This will still render the className with an extra space. As a perfector, you don’t want Spaces, so you need to handle Spaces further, using the filters method for arrays in ES6.
// helpers/classes function classes(... names:(string | undefined )[]) { return names.filter(Boolean).join(' ') } export default classesCopy the code
Unit testing
First, we’ll unit test our classes method using Jest, which is recommended by React.
Classes tests use the following example:
import classes from '.. /classes' describe('classes', () => {it(' className', () => {const result = classes('a') expect(result).toequal ('a')}) it(' accept 2 classnames ', ()=>{const result = classes('a', 'b') expect(result).toequal ('a b')}) it(' undefined', ()=>{const result = classes('a', undefined) expect(result).toequal ('a')}) it(' accept all kinds of queer values ', ()=>{const result = classes('a', undefined, 'c ', false, null) expect(result).toequal ('a ')}) it(' accept 0 arguments ', ()=>{ const result = classes() expect(result).toEqual('') }) })Copy the code
Test the UI with Snapshot
Use an Enzyme library to test the UI. The Enzyme is a JavaScript test tool for Determining, manipulating, and iterating the React Components output. The Enzyme API makes DOM manipulation and traversal flexible and intuitive by mimicking jQuery’s API. Enzyme compatible with all major test runners and judgment libraries.
Icon’s test case
import * as renderer from 'react-test-renderer' import React from 'react' import Icon from '.. /icon' import {mount} from 'enzyme' describe('icon', () => { it('render successfully', () => { const json = renderer.create(<Icon name="alipay"/>).toJSON() expect(json).toMatchSnapshot() }) it('onClick', () => { const fn = jest.fn() const component = mount(<Icon name="alipay" onClick={fn}/>) component.find('svg').simulate('click') expect(fn).toBeCalled() }) })Copy the code
What if THE IDE prompt cannot find Describe and IT?
Solutions:
- yarn add -D @types/jest
- Import ‘jest’ at the beginning of the file
This is because describe and IT’s type declaration files are located in Jest, which you can view by holding Down CTRL and clicking jest.
If not, you need to set up a reference to jest in WebStorm:
This is because typescript excludes type declarations in node_modules by default.
conclusion
The above is mainly summarized in the process of learning to build wheels, the environment is not detailed, mainly record the realization of Icon wheel some ideas and matters needing attention, want to see the source code, run to see, you can click here to view.
reference
Teacher Fang Yinghang’s React Wheels course
communication
This article is updated every week, you can search wechat “big move the world” for the first time to read and urge more (one or two earlier than the blog hey), this article GitHub github.com/qq449245884… It has been included and sorted out a lot of my documents. Welcome Star and perfect. You can refer to the examination points for review in the interview.