初探 Electron - 实践篇2
上文 初探 Electron - 实践篇1中,我们完成了项目的主架构,虽然可以运行,但并不完美。这节我们继续补充,一起完成剩下的工作。
八、使用 Material-UI
项目虽然有了主轮廓,但是外观并不令人满意。我们来引入一个 UI
框架 Material-UI
,为项目添加一些亮丽的色彩。1
npm install material-ui --save
至于为啥选 Material-UI
,大概是其他 UI
框架使用的太频繁了,换换口味😂
当然,从理论上讲,你可以引入任何你喜欢的 UI
框架,大家各取所需。
九、增加 less 文件
仅仅引入 UI
框架是不够的,我们需要引入自己的样式文件,这里用 less
示例。首先,在 app
目录下,建立 css
文件夹:1
cd app && mkdir css
这里将存放我们的 less
文件,我们分别添加 main.less
和 _reset.less
文件:main.less
,为项目的主要样式文件,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44@import "./_reset.less";
body, .box::before{
background: url(../image/bg.jpg) 0 / cover fixed;
overflow: hidden;
}
a {
text-decoration: none;
}
.box {
text-align: center;
color: #fff;
position: absolute;
width: 500px;
height: 400px;
background: rgba(255, 255, 255, 0.3);
top: 50%;
left: 50%;
margin: -200px 0 0 -250px;
border-radius: 8px;
background: hsla(0,0%,100%,.3);
overflow: hidden;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
}
.box::before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
filter: blur(10px);
margin: -20px;
z-index: -1;
}
.box {
.desc {
font-size: 12px;
position: absolute;
bottom: 90px;
opacity: 0.6;
cursor: pointer;
letter-spacing: 1px;
}
}
_reset.less
为重置样式表文件,具体代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
十、美化首页
如上,我们已经准备好了样式文件,修改 app/index.html
文件,引入 main.less
1
<link rel="stylesheet" type="text/less" href="css/main.less" />
less
并不会自己编译,但是显然我们并没有做额外的工作,那究竟是为什么呢?
心细的同学可能意识到了,一切的编译,electron-compile
已经帮我们做好了。
要美化首页,就需要去更新 app/render.js
文件,我们做如下更新:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import SideMenu from './components/Menu';
import Upload from './components/Upload';
const muiTheme = getMuiTheme({
fontFamily: 'Roboto, sans-serif',
palette: {
// primaryColor: white,
}
});
class MainWindow extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<MuiThemeProvider muiTheme={muiTheme}>
<div>
<SideMenu />
<Upload />
</div>
</MuiThemeProvider>
);
}
}
const mainWndComponent = ReactDOM.render(
<MainWindow/>, document.getElementById('content'));
如上:我们使用了 MuiThemeProvider
外层容器,同时添加了 SideMenu
和 Upload
两个组件。
十一、编写组件
首页 UI
已经就绪,我们来添加 SideMenu
和 Upload
这两个组件。app
目录下创建 components
文件夹:1
cd app && mkdir components
接着来添加 Upload.js
也就是上面我们说的 Upload
组件:
请先安装拖拽依赖包 npm install react-dropzone --save
1 | import React from 'react'; |
代码看起来长,但是逻辑很简单,主要是依赖了一个 react
组件 react-dropzone
来实现拖拽的交互。触发方法 onDrop
然后读取文件流,详细信息可以参考玩转图片流,这里就不细说了。
下来,来看第二个组件 SideMenu
,创建 app/components/Menu.js
文件:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53import React from 'react';
import Drawer from 'material-ui/Drawer';
import { List, ListItem } from 'material-ui/List';
import ActionInfo from 'material-ui/svg-icons/hardware/keyboard-arrow-right';
import Avatar from 'material-ui/Avatar';
import ActionBackup from 'material-ui/svg-icons/action/backup';
import { blue400 } from 'material-ui/styles/colors';
import NavigationMenu from 'material-ui/svg-icons/navigation/menu';
import FloatingActionButton from 'material-ui/FloatingActionButton';
const svgStyles = {
fill: grey800,
};
export default class DrawerUndockedExample extends React.Component {
constructor(props) {
super(props);
this.state = { open: false };
}
handleToggle() {
this.setState({ open: !this.state.open });
}
handleClose() {
this.setState({ open: false });
}
render() {
return (
<div className="_menu">
<FloatingActionButton className="action" onClick={this.handleToggle.bind(this)} backgroundColor={lightWhite} mini={true}>
<NavigationMenu style={svgStyles} />
</FloatingActionButton>
<Drawer
docked={false}
width={230}
open={this.state.open}
onRequestChange={open => this.setState({ open })}
>
<List>
<ListItem
leftAvatar={<Avatar icon={<ActionBackup />} backgroundColor={blue400} />}
rightIcon={<ActionInfo />}
primaryText="上传图片"
/>
</List>
</Drawer>
</div>
);
}
}
这里,我们增加了一按钮,用来触发菜单的操作,UI
样式请参考 Material-UI
十二、页面路由
随着页面的不断完善,我们发现可能需要拆分出更多的页面,是时候引入 react-router
了。首先安装依赖包:1
npm install react-router-dom --save
我们来更新一下 render.js
文件,首先引入 react-router
:1
2
3
4
5
6import {
HashRouter as Router,
Route,
Switch,
Redirect,
} from 'react-router-dom';
修改渲染部分:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24render() {
return (
<Router>
<MuiThemeProvider muiTheme={muiTheme}>
<div>
<SideMenu />
<RefreshIndicator
size={40}
left={750}
top={10}
loadingColor="#FFB74D"
status="loading"
style={style.refresh}
/>
<Switch>
<Route path="/" exact component={Upload} />
<Route path="/list" component={ListPage} />
<Redirect to="/" />
</Switch>
</div>
</MuiThemeProvider>
</Router>
);
}
页面部分使用,很简单,加上 link
就行了:1
2
3
4
5
6
7<Link to="/" onClick={this.handleClose.bind(this)}>
<ListItem
leftAvatar={<Avatar icon={<ActionBackup />} backgroundColor={blue400} />}
rightIcon={<ActionInfo />}
primaryText="上传图片"
/>
</Link>
注意,此处代码较多,所以我挑了一些核心代码展示,更多细节请参考项目:Electron-OCR
十三、编码规范
说编码规范可能有些言过其实了,其实目的就是想统一一下编程风格,方便团队管理。当然,个人开发也用得上,起码不会犯一些语法错误,而影响项目进度。
我们全局安装 eslint
:1
npm install -g eslint
安装完之后,执行 eslint-init
,就会生成 .eslintrc.js
文件,修改配置如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16module.exports = {
"extends": "airbnb",
"parser": "babel-eslint",
"ecmaFeatures": {
"classes": true,
"jsx": true,
},
"plugins": [
"react",
"jsx-a11y",
"import"
],
"rules": {
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
},
};
如果发现有如下异常,请安装 babel-eslint
文件:1
ESLint: Cannot find module 'babel-eslint'. Please see the 'ESLint' output channel for details.
除此之外,我们要熟悉 eslint
的使用规则,记住下面的规则即可。1
2
3"off" or 0 - turn the rule off
"warn" or 1 - turn the rule on as a warning (doesn’t affect exit code)
"error" or 2 - turn the rule on as an error (exit code will be 1)
补充一点:局部禁用规则 /* eslint-disable */
十四、对接 API
对接 API
的时候,少不了发送请求,这里我们依赖了 superagent
,需要先安装:1
npm install superagent --save
下面我们举一个简单的例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import request from 'superagent';
// 一个简单的例子
export async function getAccessToken() {
const param = qs.stringify({
grant_type: 'client_credentials',
client_id: Keys.ak,
client_secret: Keys.sk,
});
return new Promise((resolve, reject) => {
request
.get(`http://jartto.wang/token?${param}`)
.then((res) => {
// res.body, res.headers, res.status
resolve(res.body.access_token);
})
.catch((err) => {
// err.message, err.response
reject(err.message);
});
});
}
代码很简单,就不细说了。
十五、使用 async/await
在上面的 API
请求中,我们用到了 async
,这里我说一下怎样配置。首先,安装依赖:1
2cnpm install --save-dev babel-plugin-transform-runtime
cnpm install --save babel-runtime
接着配置 compilerc
:1
2
3
4
5
6
7
8{
"plugins": [
["transform-runtime", {
"polyfill": false,
"regenerator": true
}]
]
}
如果出现异常:await is a reserved word
,请注意配套 async
使用。
十六、总结
本篇主要围绕具体实现来展开说明,提到了 UI
库、添加了路由、编写了组件、完善了规范、对接了 API
等等。功能也算是完成了七七八八,剩下的业务逻辑就不在这里啰嗦了。Electron
还有很多的技术细节,肯定也不是三言两语就能说完的,所以建议大家都去实际的应用一下,只有这样才能融汇贯通。
Electron-OCR 这个项目我还在继续,想到什么就加上去实现。欢迎大家加入进来,一起来做一个完美的识图工具。