摘要:是一个使用和等技术创建原生程序的框架,它负责比较难搞的部分,你只需把精力放在你的应用的核心上即可。谈谈技术选型使用去做底层的绘制,大项目首选状态管理的最佳实践肯定不是,目前首选,或者。
目前Electron在github上面的star量已经快要跟React-native一样多了
这里吐槽下,webpack感觉每周都在偷偷更新,很糟心啊,还有Angular更新到了8,Vue马上又要出正式新版本了,5G今年就要商用,华为的系统也要出来了,RN还没有更新到正式的1版本,还有号称让前端开发者失业的技术flutter也在疯狂更新,前端真的是学不完的
回到正题,不能否认,现在的大前端,真的太牛了,PC端可以跨三种平台开发,移动端可以一次编写,生成各种小程序以及React-native应用,然后跑在ios和安卓以及网页中 , 这里不得不说-------京东的Taro框架 这些人 已经把Node.js和webpack用上了天对webpack不熟悉的,看我之前的文章 ,今天不把重点放在webpack
欢迎关注我的专栏 《前端进阶》 都是百星高赞文章
手写React优化版脚手架
前端性能优化不完全手册
手写vue脚手架
本文源码git仓库地址
先说说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 }, }看看主进程的配置文件main.js
// Modules to control application life and create native browser window const { app, BrowserWindow, ipcMain, Tray, Menu } = require("electron") const path = require("path") // Keep a global reference of the window object, if you don"t, the window will // be closed automatically when the JavaScript object is garbage collected. let mainWindow app.disableHardwareAcceleration() // ipcMain.on("sync-message", (event, arg) => { // console.log("sync - message") // // event.returnValue("message", "tanjinjie hello") // }) function createWindow() { // Create the browser window. tray = new Tray(path.join(__dirname, "./src/assets/bg.jpg")); tray.setToolTip("wechart"); tray.on("click", () => { mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show() }); const contextMenu = Menu.buildFromTemplate([ { label: "退出", click: () => mainWindow.quit() }, ]); tray.setContextMenu(contextMenu); mainWindow = new BrowserWindow({ width: 805, height: 500, webPreferences: { nodeIntegration: true }, // titleBarStyle: "hidden" frame: false }) //自定义放大缩小托盘功能 ipcMain.on("changeWindow", (event, arg) => { if (arg === "min") { console.log("min") mainWindow.minimize() } else if (arg === "max") { console.log("max") if (mainWindow.isMaximized()) { mainWindow.unmaximize() } else { mainWindow.maximize() } } else if (arg === "hide") { console.log("hide") mainWindow.hide() } }) // and load the index.html of the app. // mainWindow.loadFile("index.html") mainWindow.loadURL("http://localhost:5000"); BrowserWindow.addDevToolsExtension( path.join(__dirname, "./src/extensions/react-dev-tool"), ); // Open the DevTools. // mainWindow.webContents.openDevTools() // Emitted when the window is closed. mainWindow.on("closed", function () { // Dereference the window object, usually you would store windows // in an array if your app supports multi windows, this is the time // when you should delete the corresponding element. mainWindow = null BrowserWindow.removeDevToolsExtension( path.join(__dirname, "./src/extensions/react-dev-tool"), ); }) } // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.on("ready", createWindow) // Quit when all windows are closed. app.on("window-all-closed", function () { // On macOS it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q if (process.platform !== "darwin") app.quit() }) app.on("activate", function () { // On macOS it"s common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. if (mainWindow === null) createWindow() }) // In this file you can include the rest of your app"s specific main process // code. You can also put them in separate files and require them here.今天只讲开发模式下的配置,因为实在太多,得分两篇文章写了~ 剩下的配置去git仓库看 在开发模式下启动项目:
使用 "dev": "webpack-dev-server --config ./config/webpack.dev.js", 将代码打包到内存中
使用 "start": "electron ." 开启electron,读取对应的内存地址中的资源,实现热更新
项目起来后,在入口处index.js文件中,注入dvaimport 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 }) => (这里不得不说redux,redux-sage,dva的区别 直接看图 首先是Redux)); app.model(Homes) app.model(main) app.start("#root")
React 只负责页面渲染, 而不负责页面逻辑, 页面逻辑可以从中多带带抽取出来, 变成 store,状态及页面逻辑从
页面逻辑就是 reducer,
上面说了, 可以使用 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 快得飞起✈️
约定优于配置, 总是好的
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/114724.html
摘要:是一个使用和等技术创建原生程序的框架,它负责比较难搞的部分,你只需把精力放在你的应用的核心上即可。谈谈技术选型使用去做底层的绘制,大项目首选状态管理的最佳实践肯定不是,目前首选,或者。 showImg(https://segmentfault.com/img/bVbtqlI?w=1308&h=565); 目前Electron在github上面的star量已经快要跟React-nativ...
摘要:调用通过注册表调用到实例,透过的,调用到中的,最后通过,调用,根据参数相应模块执行。京东的,多端解决方案是一套遵循语法规范的多端开发解决方案。 showImg(https://segmentfault.com/img/bVbuMkw?w=1304&h=808); 对于一项技术,我们不能停留在五分钟状态,特别喜欢一句话,用什么方式绘制UI界面一点不重要,重要的是底层的思维,解决问题和优化...
摘要:调用通过注册表调用到实例,透过的,调用到中的,最后通过,调用,根据参数相应模块执行。京东的,多端解决方案是一套遵循语法规范的多端开发解决方案。 showImg(https://segmentfault.com/img/bVbuMkw?w=1304&h=808); 对于一项技术,我们不能停留在五分钟状态,特别喜欢一句话,用什么方式绘制UI界面一点不重要,重要的是底层的思维,解决问题和优化...
摘要:调用通过注册表调用到实例,透过的,调用到中的,最后通过,调用,根据参数相应模块执行。京东的,多端解决方案是一套遵循语法规范的多端开发解决方案。 showImg(https://segmentfault.com/img/bVbuMkw?w=1304&h=808); 对于一项技术,我们不能停留在五分钟状态,特别喜欢一句话,用什么方式绘制UI界面一点不重要,重要的是底层的思维,解决问题和优化...
阅读 3129·2021-11-22 09:34
阅读 2776·2021-09-22 15:28
阅读 795·2021-09-10 10:51
阅读 1834·2019-08-30 14:22
阅读 2247·2019-08-30 14:17
阅读 2712·2019-08-30 11:01
阅读 2278·2019-08-29 17:19
阅读 3617·2019-08-29 13:17