内容简介:这里吐槽下,
目前 Electron 在 github 上面的 star 量已经快要跟 React-native 一样多了
这里吐槽下, webpack 感觉每周都在偷偷更新,很糟心啊,还有 Angular 更新到了 8 , Vue 马上又要出正式新版本了, 5G 今年就要商用,华为的系统也要出来了, RN 还没有更新到正式的 1 版本,还有号称让前端开发者失业的技术 flutter 也在疯狂更新,前端真的是学不完的
回到正题,不能否认,现在的大前端,真的太牛了,
PC 端可以跨三种平台开发,移动端可以一次编写,生成各种小程序以及
React-native 应用,然后跑在
ios 和安卓以及网页中 , 这里不得不说-------京东的
Taro 框架 这些人 已经把
Node.js 和
webpack
用上了天
对 webpack 不熟悉的,看我之前的文章 ,今天不把重点放在 webpack
欢迎关注我的专栏 《前端进阶》 都是百星高赞文章
先说说 Electron 官网介绍:
使用 JavaScript, HTML 和 CSS 构建跨平台的桌面应用 ,如果你可以建一个网站,你就可以建一个桌面应用程序。 Electron 是一个使用 JavaScript, HTML 和 CSS 等 Web 技术创建原生程序的框架,它负责比较难搞的部分,你只需把精力放在你的应用的核心上即可。
- 什么意思呢?
-
Electron = Node.js + 谷歌浏览器 + 平常的JS代码生成的应用,最终打包成安装包,就是一个完整的应用 -
Electron分两个进程,主进程负责比较难搞的那部分,渲染进程(平常的JS代码)部分,负责UI界面展示 - 两个进程之间可以通过
remote模块,以及IPCRender和IPCMain之间通信,前者类似于挂载在全局的属性上进行通信(很像最早的命名空间模块化方案),后者是基于发布订阅机制,自定义事件的监听和触发实现两个进程的通信。 -
Electron相当于给React生成的单页面应用套了一层壳,如果涉及到文件操作这类的复杂功能,那么就要依靠Electron的主进程,因为主进程可以直接调用Node.js的API,还可以使用C++插件,这里Node.js的牛逼程度就凸显出来了,既可以写后台的CRUD,又可以做中间件,现在又可以写前端。
谈谈技术选型
- 使用
React去做底层的UI绘制,大项目首选React+TS - 状态管理的最佳实践肯定不是
Redux,目前首选dva,或者redux-saga。 - 构建 工具 选择
webpack,如果不会webpack真的很吃亏,会严重限制你的前端发展,所以建议好好学习Node.js和webpack - 选择了普通的
Restful架构,而不是GraphQL,可能我对GraphQL理解不深,没有领悟到精髓 - 在通信协议这块,选择了
websoket和普通的http通信方式 - 因为是
demo,很多地方并没有细化,后期会针对electron出一个网易云音乐的开源项目,这是一定要做到的
先开始正式的环境搭建
-
config文件放置webpack配置文件 -
server文件夹放置Node.js的后端服务器代码 -
src下放置源码 -
main.js是Electron的入口文件 -
json文件是脚本入口文件,也是包管理的文件~
开发模式项目启动思路:
- 先启动
webpack将代码打包到内存中,实现热更新 - 再启动
Electron读取对应的url地址的文件内容,也实现热更新
设置 webpack 入口
app: ['babel-polyfill', './src/index.js', './index.html'],
vendor: ['react']
}
忽略 Electron 中的代码,不用 webpack 打包(因为 Electron 中有后台模块代码,打包就会报错)
externals: [
(function () {
var IGNORES = [
'electron'
];
return function (context, request, callback) {
if (IGNORES.indexOf(request) >= 0) {
return callback(null, "require('" + request + "')");
}
return callback();
};
})()
]
加入代码分割
optimization: {
runtimeChunk: true,
splitChunks: {
chunks: 'all'
}
},
设置热更新等
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
],
mode: 'development',
devServer: {
contentBase: '../build',
open: true,
port: 5000,
hot: true
},
#### 加入 babel
{
loader: 'babel-loader',
options: { //jsx语法
presets: ["@babel/preset-react",
//tree shaking 按需加载babel-polifill presets从后到前执行
["@babel/preset-env", {
"modules": false,
"useBuiltIns": "false", "corejs": 2,
}],
],
plugins: [
//支持import 懒加载 plugin从前到后
"@babel/plugin-syntax-dynamic-import",
//andt-mobile按需加载 true是less,如果不用less style的值可以写'css'
["import", { libraryName: "antd-mobile", style: true }],
//识别class组件
["@babel/plugin-proposal-class-properties", { "loose": true }],
//
],
cacheDirectory: true
},
}
今天只讲开发模式下的配置,因为实在太多,得分两篇文章写了~ 剩下的配置去 git 仓库看
在开发模式下启动项目:
- 使用
"dev": "webpack-dev-server --config ./config/webpack.dev.js",将代码打包到内存中 - 使用
"start": "electron ."开启electron,读取对应的内存地址中的资源,实现热更新
项目起来后,在入口处 index.js 文件中,注入 dva
import React from 'react'
import App from './App'
import dva from 'dva'
import Homes from './model/Homes'
import main from './model/main'
const app = dva()
app.router(({ history, app: store }) => (
<App
history={history}
getState={store._store.getState}
dispatch={store._store.dispatch}
/>
));
app.model(Homes)
app.model(main)
app.start('#root')
这里不得不说 redux,redux-sage,dva 的区别 直接看图
首先是 Redux
-
React只负责页面渲染, 而不负责页面逻辑, 页面逻辑可以从中单独抽取出来, 变成store,状态及页面逻辑从<App/>里面抽取出来, 成为独立的store, - 页面逻辑就是
reducer,<TodoList/> 及<AddTodoBtn/>都是Pure Component, 通过connect方法可以很方便地给它俩加一层wrapper从而建立起与store的联系: 可以通过dispatch向store注入action, 促使store的状态进行变化, 同时又订阅了store的状态变化, 一旦状态有变, 被connect的组件也随之刷新,使用dispatch往store发送action的这个过程是可以被拦截的, 自然而然地就可以在这里增加各种Middleware, 实现各种自定义功能, eg: logging这样一来, 各个部分各司其职, 耦合度更低, 复用度更高, 扩展性更好
然后是注入 Redux-sage
- 上面说了, 可以使用 Middleware 拦截 action, 这样一来异步的网络操作也就很方便了, 做成一个 Middleware 就行了, 这里使用 redux-saga 这个类库, 举个栗子:
- 点击创建
Todo的按钮, 发起一个 type == addTodo 的 action -
saga拦截这个action, 发起http请求, 如果请求成功, 则继续向reducer发一个type == addTodoSucc的action, 提示创建成功, 反之则发送type == addTodoFail的action即可
最后是: Dva
- 有了前面的三步铺垫,
Dva的出现也就水到渠成了, 正如Dva官网所言,Dva是基于React + Redux + Saga的最佳实践沉淀, 做了 3 件很重要的事情, 大大提升了编码体验: - 把
store及saga统一为一个model的概念, 写在一个js文件里面 - 增加了一个
Subscriptions, 用于收集其他来源的action,eg: 键盘操作 -
model写法很简约, 类似于DSL或者RoR,coding快得飞起:airplane: - 约定优于配置, 总是好的:laughing:
在入口 APP 组件中,注入 props ,实现状态树的管理
import React from 'react'
import { HashRouter, Route, Redirect, Switch } from 'dva/router';
import Home from './pages/home'
const Router = (props) => {
return (
<HashRouter>
<Switch>
<Route path="/home" component={Home}></Route>
<Redirect to="/home"></Redirect>
</Switch>
</HashRouter>
)
}
export default Router
在组件中 connect 连接状态树即可
import React from 'react'
import { ipcRenderer } from 'electron'
import { NavLink, Switch, Route, Redirect } from 'dva/router'
import Title from '../../components/title'
import Main from '../main'
import Friend from '../firend'
import More from '../more'
import { connect } from 'dva'
import './index.less'
class App extends React.Component {
componentDidMount() {
ipcRenderer.send('message', 'hello electron')
ipcRenderer.on('message', (event, arg) => {
console.log(arg, new Date(Date.now()))
})
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = function () {
ws.send('123')
console.log('open')
}
ws.onmessage = function () {
console.log('onmessage')
}
ws.onerror = function () {
console.log('onerror')
}
ws.onclose = function () {
console.log('onclose')
}
}
componentWillUnmount() {
ipcRenderer.removeAllListeners()
}
render() {
console.log(this.props)
return (
<div className="wrap">
<div className="nav">
<NavLink to="/home/main">Home</NavLink>
<NavLink to="/home/firend">Friend</NavLink>
<NavLink to="/home/more">More</NavLink>
</div>
<div className="content">
<Title></Title>
<Switch>
<Route path="/home/main" component={Main}></Route>
<Route path="/home/firend" component={Friend}></Route>
<Route path="/home/more" component={More}></Route>
<Redirect to="/home/main"></Redirect>
</Switch>
</div>
</div>
)
}
}
export default connect(
({ main }) => ({
test: main.main
})
)(App)
// ipcRenderer.sendSync('sync-message','sync-message')
捋一捋上面的组件做了什么
- 上来在组件挂载的生命周期函数中,启动了
websocket连接,并且挂载了响应的事件监听,对主线程发送了消息,并且触发了主线程的message事件。 - 在组件即将卸载的时候,移除了所有的跨进程通信的事件监听
- 使用了
dva进行路由跳转 - 连接了状态树,读取了状态树
main模块的main状态数据
进入上一个组件的子组件
import React from 'react'
import { connect } from 'dva'
class App extends React.Component {
handleAdd = () => {
this.props.dispatch({
type: 'home/add',
val: 5,
res: 1
})
}
handleDel = () => {
}
render() {
const { homes } = this.props
console.log(this.props)
return (
<div>
<button onClick={this.handleAdd}>add</button>
<button onClick={this.handleDel}>{homes}</button>
</div>
)
}
}
export default connect(
({ home, main }) => ({
homes: home.num,
mains: main.main
})
)(App)
同样看看,这个组件做了什么
- 连接状态树,读取了
home,main模块的状态数据,并且转换成了props - 绑定了事件,如果点击按钮,
dispatch给对应的effects,更新状态树的数据,进而更新页面
我们一起去 dva 中管理的 model 看看
-
home模块
export default {
namespace: 'home',
state: {
homes: [1, 2, 3],
num: 0
},
reducers: {
adds(state, { newNum }) {
return {
...state,
num: newNum
}
}
},
effects: {
* add({ res, val }, { put, select, call }) {
const { home } = yield select()
console.log(home.num)
yield console.log(res, val)
const newNum = home.num + 1
yield put({ type: 'adds', newNum })
}
},
}
dva 真的可以给我们省掉很多很多代码,而且更好维护,也更容易阅读
- 它的大概流程
- 如果不会的话建议去官网看例子,一般来说不会像
RXJS学习路线那么陡峭
Node.js 中代码
const express = require('express')
const { Server } = require("ws");
const app = express()
const wsServer = new Server({ port: 8080 })
wsServer.on('connection', (ws) => {
ws.onopen = function () {
console.log('open')
}
ws.onmessage = function (data) {
console.log(data)
ws.send('234')
console.log('onmessage' + data)
}
ws.onerror = function () {
console.log('onerror')
}
ws.onclose = function () {
console.log('onclose')
}
});
app.listen(8000, (err) => {
if (!err) { console.log('监听OK') } else {
console.log('监听失败')
}
})
上来先给一个 websocket 8080 端口监听,绑定事件,并且使用 express 监听原生端口 8000
- 这样好处,一个应用并不一定全部需要实时通讯,根据需求来决定什么时候进行实时通讯
-
Restful架构依然存在,Node.js作为中间件或者IO输出比较多的底层服务器进行CRUD都可以
今天先写到这里,套路都一样,基本的架子已经已经搭好,可以把代码 clone 下去慢慢玩,加功能。 可以的话给个 star 和赞,谢谢
本文git仓库源码地址,欢迎star
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Sovereign Individual
James Dale Davidson、William Rees-Mogg / Free Press / 1999-08-26 / USD 16.00
Two renowned investment advisors and authors of the bestseller The Great Reckoning bring to light both currents of disaster and the potential for prosperity and renewal in the face of radical changes ......一起来看看 《The Sovereign Individual》 这本书的介绍吧!