【笔记】electron + react + antd

您所在的位置:网站首页 loadfile函数 【笔记】electron + react + antd

【笔记】electron + react + antd

2023-03-24 11:26| 来源: 网络整理| 查看: 265

electron

Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。

react

React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。

antd

一个非常流行的前端UI框架

安装electron

npm install electron --save-dev

修改国内镜像

npm config set registry https://registry.npm.taobao.org

查询是否成功修改

npm config get registry

下载 cnpm 全局使用

npm install -g cnpm --registry=https://registry.npm.taobao.org 如果无法使用 cnpm 1.进⼊node.js莫⽬录找到cnpm⽂件的位置将其移动到于npm⽂件的同⼀⽂件夹下 2.再将cnpm和cnpm.cmd⽂件移⾄npm与npm.cmd所在的⽂件夹即可解决问题

electron 配置

{ "name": "electron_gui", "version": "1.0.0", "description": "", "main": "main.js", // 入口文件 "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "electron": "electron ." // 主electron 执行程序 }, "author": "", "license": "ISC", "dependencies": { "electron": "^19.0.5" } }

热加载

cnpm i --save-dev electron-reloader

main.js

const { app, BrowserWindow ,Menu } = require('electron') // 热加载 const reloader = require('electron-reloader') reloader(module) // app 窗口启动 app.on('ready', ()=>{ // 创建窗口指定高和宽度 const mainWindow = new BrowserWindow({ width:500, height:500 }) // 加载 html 文件 会渲染到 窗口内 mainWindow.loadFile('./src/index.html') // 直接打开 调试 或者 ctrl+shift+i 、 mac 调试用 openDevTools mainWindow.webContents.openDevTools() // 自定义模板 - 菜单栏 const template = [ { label: '文件', submenu:[ { label: '新建窗口' } ],     },     { label: '关于'     } ] // 编译模板 const menu = Menu.buildFromTemplate(template) // 设置菜单 Menu.setApplicationMenu(menu) })

在窗口中开发调试面板

windows ctrl+shift+i mac mainWindow.webContents.openDevTools()

通过 template 模板加载生成 menu 菜单

const template = [ {     lable: '文件', // 子菜单         submenu:[         {                 label: '新建窗口'             }         ]     },     {     lable: '关于'     } ] const menu = Menu.buildFromTemplate(template) Menu.setApplicationMeun(menu)

加载主进程的代码

const { remote: { BrowserWindow } } = require('electron')

在css 里面的拖拽事件

使用拖拽

-webkit-app-region: drag;

禁用拖拽

-webkit-app-region: no-drag;

const {remote:{BrowserWindow} , shell } = require('electron') 点击新建窗口, 创建新窗口 const newWindow = document.querySelector('.new-windon') newWindow.onclick = function(){     new BrowserWindow({             width: 300,             heigth: 300         }) } // 点击 a 标签跳转,shell 使用外部浏览器打开窗口 const allA = document.querySelectorAll('a') allA.forEach(item =>{     item.onclick = function(e){         // 阻止默认事件         e.preventDefault()         shell.openExternal(item.herf)     } })

通过 frame:false 创建无边框窗口

渲染进程和主线程通信

定义按钮 事件函数 function maxWindow(){ ipcRenderer.send('max-window') } 主线程定义事件 ipcMain.on('max-window',()=>{ mainWindow.maximize() }) 传参 let windowSize = 'unmax-window' function maxWindow(){ windowSize = windowSize === 'max-window'?'unmax-window':'max-window'     ipcRenderer.send('max-window', windowSize) } 接受参数 ipcMain.on('max-window',(event, arg)=>{ console.log(arg)     if (arg === 'unmax-window') return mainWindow.maximize();     mainWindow.unmaximize() })

electron 打包

安装electron-packager cnpm i electron-packager -D 在 package.json里面添加 "build": "electrion-packager ./ appname --platform=win32 --arch=x64 --out ./outApp --overwrite --icon=./log.ico"

react 要操作系统代码,需要通过node 的 child_process 的exec 执行命令。

需要和进程通信,渲染进程->主进程

不能用import引入进来,也不能直接用require,

以下三种方法都会导致报错: import { ipcRenderer } from 'electron' import electron from 'electron' const electron = require('electrion')

是因为:

require/exports 和 import/export 形式不一样,遵循的模块化也不一样。

require/exports是一种野生的规范。

require/exports 的用法只有以下三种简单的写法: const fs = require('fs') exports.fs = fs module.exports = fs

而 import/export 的写法就多种多样:

import fs from 'fs' import {default as fs} from 'fs' import * as fs from 'fs' import {readFile} from 'fs' import {readFile as read} from 'fs' import fs, {readFile} from 'fs' export default fs export const fs export function readFile export {readFile, read} export * from 'fs'

正确引入

const electron = window.require('electron'); const {ipcRenderer} = electron; console.log(ipcRenderer) ipcRenderer.send('ipcSignal','hello world!');

通过react 页面前端和底层系统交互 windows

官方例子 // On Windows Only... const { spawn } = require('node:child_process'); const bat = spawn('cmd.exe', ['/c', 'my.bat']); bat.stdout.on('data', (data) => { console.log(data.toString()); }); bat.stderr.on('data', (data) => { console.error(data.toString()); }); bat.on('exit', (code) => { console.log(`Child exited with code ${code}`); }); // OR... const { exec, spawn } = require('node:child_process'); exec('my.bat', (err, stdout, stderr) => { if (err) { console.error(err); return; } console.log(stdout); }); // Script with spaces in the filename: const bat = spawn('"my script.cmd"', ['a', 'b'], { shell: true }); // or: exec('"my script.cmd" a b', (err, stdout, stderr) => { // ... });

自己实现

页面按钮功能 const handleCmd = ()=>{         // react -> nodejs -> system ipcRenderer.send("svnUpdate") } ipcRenderer.on('readSvndata',(event,arg)=>{ // arg 才是返回的数据 console.log("event: ",event, arg) alert(arg) }) election -> main.js const { ipcMain } = require('electron') const {exec} = require('child_process') // 监听渲染进程 node执行exec函数 ipcMain.on('svnUpdate',(event,arg)=>{ exec("svn up $PATH", (err, stdout, stderr) => { mainWindow.webContents.send('readSvndata',stdout) }) }) react + antd

先安装react - 18

npm install -g create-react-app

npm install -g react-dom

修改package.json配置文件的scripts

    // 指定electron入口文件 "main": "main.js", "homepage": ".", "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", // 启动程序 "electron": "electron ." },

再在根目录里安装 electron

npm install electron --save-dev

新建一个main.js 用于配置 electron

// 引入electron并创建一个Browserwindow const {app, BrowserWindow} = require('electron') const path = require('path') const url = require('url') // 保持window对象的全局引用,避免JavaScript对象被垃圾回收时,窗口被自动关闭. let mainWindow function createWindow () { //创建浏览器窗口,宽高自定义具体大小你开心就好 mainWindow = new BrowserWindow({width: 800, height: 600}) /* * 加载应用----- electron-quick-start中默认的加载入口 mainWindow.loadURL(url.format({ pathname: path.join(__dirname, './build/index.html'), protocol: 'file:', slashes: true })) */ // 加载应用----适用于 react 项目 mainWindow.loadURL('http://localhost:3000/'); // 打开开发者工具,默认不打开 // mainWindow.webContents.openDevTools() // 关闭window时触发下列事件. mainWindow.on('closed', function () { mainWindow = null }) } // 当 Electron 完成初始化并准备创建浏览器窗口时调用此方法 app.on('ready', createWindow) // 所有窗口关闭时退出应用. app.on('window-all-closed', function () { // macOS中除非用户按下 `Cmd + Q` 显式退出,否则应用与菜单栏始终处于活动状态. if (process.platform !== 'darwin') { app.quit() } }) app.on('activate', function () { // macOS中点击Dock图标时没有已打开的其余应用窗口时,则通常在应用中重建一个窗口 if (mainWindow === null) { createWindow() } }) // 你可以在这个脚本中续写或者使用require引入独立的js文件.

热加载

cnpm i --save-dev electron-reloader

react

react 框架核心

reactDom 专门做渲染

index.css 全局样式

./App 引入根组件

ReactDom.render 渲染根组件APP 到root dom节点上

react 18 问题

严格模式关闭

列表渲染

使用map

遍历列表时同样需要一个类型为number/string不可重复的key,提高diff性能

key 仅在内部使用,不会出现在真实的dom结合中

{ var.ma    p( v=> {v.name} ) }

jsx 条件渲染

可以用 三元运算符,逻辑&&运算

{ bool && 123 }

行内样式 在元素内绑定

123

类名样式

const style = {color:'red',fontSize:'30px'} 123 // css 样式 123

jsx 注意事项

1. jsx 必须有一个根节点,如果没有根节点,可以使用代替 2. 所有标签必须闭合 3. jsx中使用驼峰命名,className 4. jsx 支持多行换行,如果需要换行,需要使用()包裹,防止bug

函数组件约定

1.组件名称必须大写,react内部会判断是组件还是普通标签 2.函数组件必须有返回值,不渲染则返回 null 3.组件像标签一样可以被渲染到页面内,组件表示一段结构内容, 4. 可以 也可以

类组件创建渲染

import react class HellComponent extends React.Component{ reder(){     return test class     } } 1.类名称必须大写字母开头 2.类组件应该继承React.Component父类,可以获取父类中的方法属性 3.类组件必须提供render方法render方法必须有返回值,表示UI结构

事件绑定

语法 on + 事件名称={事件处理程序} 如

总结

1.编写组件就是编写原生js类或者函数

2.定义状态必须通过state实例属性的方法提供一个对象,名称固定 state

3.修改state中的任何属性都不可以通过直接赋值必须走 setState方法,是因为继承得到的

4.这里的this关键词

组件通讯

父传子 1.父组件提供传递的数据 - state 2.给子组件标签'添加属性'值为state中的数据 3.子组件中通过props接受父组件中传过来的数据 - 1.类组件使用this.props获取props对象 - 2.函数式组件直接通过参数获取props对象 1.props是只读对象 根据单项数据流的要求,子组件只能读取props中的数据,不能进行修改 2.props可以传递任意数据 数值,字符串,布尔值,数组,对象,函数,jsx 子传父:子组件调用父组件传递过来的函数,并把想要传递的数据当成函数实参 1.先在父组件内创建函数 2.把函数传到子组件内 3.子组件内调用,即可把子组件的数据传递给父组件

跨组件通信 Context

1.创建Context对象,导出provider和Consumer对象     const {Provider, Consumer} = createContext() 2.使用Provider包裹根组件提供数据 基于context值进行渲染 }

生命周期

图示生命周期 https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/ constructor: 创建组件时,最先执行,初始化的时候只执行一次 render: 每次组件渲染都会触发,不能调用setState()操作,否则无限调用 componentDidMount: 组件挂载(完成DOM渲染)后执行,初始化的时候执行一次

hooks

使函数拥有自己的状态

为函数组件提供状态 只能在函数组件中使用

useState

1. 导入useState函数 react 2. 执行这个函数并且传入初始值必须在函数组件汇总 3. [数据, 修改数据的方法] 4. 使用数据 修改数据 const [count, setCount] = useState(0) 1. useState 传过来的参数 作为count的初始值 2. [count, setCount] 这里的写法是一个解构赋值,useState返回值是一个数组 顺序不可换。第一参数就是数据状态,第二参数就是修改数据的方法 3.setCount函数作用用来修改count依旧保持不能直接修改原值还是生成新值替换原值 setCount(基于原值计算得到的新值) 4.count和setCount是一对 是绑定一起的 setCount只能用来修改对应的count值 组件的更新 调用setCount的时候更新过程 首次渲染 组件内部代码会被执行一次 其中useState也会跟着执行,这里重点注意,初始值只在首次渲染时生效 更新渲染 setCount 都会更新 1. app组件会再次渲染 这个函数会再次执行 2. useState再次执行 得到的新的count值不是而是修改之后的1,模板会用新值 useState初始值只要在此渲染生效,后续只要调用setCount整个app中代买都会执行 此时的count每次拿到的都是最新值时的count每次拿到的都是最新值 useState 注意事项 1. 只能出现在函数中 2. 不能嵌套在if/for 其他函数中     react 按照hooks的调用顺序识别每一个hook

useEffect

做副作用处理 1. 导入useEffect 函数 2. 子啊函数组件中执行,传入回调,并定义副作用 3. 当我们通过修改状态更新组件时,副作用也会不断执行

依赖项控制副作用的执行时机

1.默认状态(无依赖相) 组件初始化的时候先执行一次,等到每次数据修改组件更新再次执行 2. 添加一个空数组依赖性 组件初始化的时候执行一次 3. 依赖特定相 组件初始化的适合执行一次,依赖的特定项发生变化会再次执行 useEffect(()=>{         document.title = count     },[count]) 注意事项 useEffect 回调函数中用到的数据(比如,count)就是依赖数据 就应该出现在依赖数组中,如果不添加依赖性会出BUG hook的出现,就是不用生命周期的概念也能实现业务代码 初始化 或者变量被修改时 都会执行 useEffect - 清理副作用 function Test(){ useEffect(()=>{     let timer = setInterval(()=>{         console.log('定时器执行')         },1000)         return ()=>{         // 清理动作 clearInterval(timer)         }     },[]) // [] 代表初始化只执行一次     return(     this is test!     ) }

发送网络请求

useEffect 1. 不加依赖性 - 初始化 + 重新渲染 2. 加[] - 初始化一次 3. 加特定依赖项 [count, name] - 首次执行+任意一个值的变化执行 !!!真实DOM渲染之后才会执行useEffect !!! 在内部单独定义一个函数,然后把这个函数包装成同步 useEffect(()=>{ async function fetchData(){     const res = await axios.get('url')         console.log(res) },[]) 可以写多次 useEffect 原生 fetch web api 搜索 fetch mdc fetch('url') .then( response => response.json() ) .then( data => console.log(data) )

useRef

在函数组件中获取真实的dom元素对象,或者是组件对象

1. 导入 useRef 函数 2. 执行 useRef 函数并出传入null,返回值为一个对象   内部有一个current属性存放拿到的DOM对象, (组件实例) 3. 通过ref绑定 要获取的元素或者组件 组件实例 类组件 dom对象 标签 import { useEffect, useRef } from 'react' function App(){ const h1Ref = useRef(null)     useEffect(()=>{     console.log(h1Ref)     },[])     return(              这是h1标签              ) }

useConext

context如果要传递的数据 只需要在整个应用初始化的时候传递一次就可以 就可以选择在当前文件里做数据提供, index.js 静态的 如果Context需要传递数据并且将来还需要在数据做修改,底层组件也需要一起改变 需要写到app.js , 动态的 useState 1.导入useState函数 react 2.执行这个函数并且传入初始值必须在函数组件汇总 3.[数据, 修改数据的方法] 4.使用数据 修改数据 状态读取和修改 const [count, setCount] = useState(0) 1. useState 传过来的参数 作为count的初始值 2. [count, setCount] 这里的写法是一个解构赋值,useState返回值是一个数组 顺序不可换。第一参数就是数据状态,第二参数就是修改数据的方法 3.setCount函数作用用来修改count依旧保持不能直接修改原值还是生成新值替换原值 setCount(基于原值计算得到的新值) 4.count和setCount是一对 是绑定一起的 setCount只能用来修改对应的count值 组件的更新 调用setCount的时候更新过程 首次渲染 组件内部代码会被执行一次 其中useState也会跟着执行,这里重点注意,初始值只在首次渲染时生效 更新渲染 setCount 都会更新 1. app组件会再次渲染 这个函数会再次执行 2. useState再次执行 得到的新的count值不是而是修改之后的1,模板会用新值 useState初始值只要在此渲染生效,后续只要调用setCount整个app中代买都会执行 此时的count每次拿到的都是最新值

useCallback 避免重复渲染 可以用在 webSocket

用于得到一个固定引用值的函数,通常用它进行性能优化 该函数有两个参数: 1.函数useCallBack会固定该函数的引用,只要依赖项没有发生改变,则始终返回之前函数的地址 2.数组,记录依赖项(类似于useEffect) 该函数返回:引用相对固定的函数地址

useLayoutEffect(布局副作用)

布局副作用

useEffect 在浏览器渲染完成 后 执行 useLayoutEffect 在浏览器渲染 前 执行

特点:

useLayoutEffect 总是比 useEffect 先执行 useLayoutEffect 里面的任务最好影响了Layout(布局)

代码演示

import React, {useState, useLayoutEffect, useEffect} from "react"; import ReactDOM from "react-dom"; function App() { const [n, setN] = useState(0) const onClick = ()=>{ setN(i=>i+1) } useEffect(()=>{ console.log("useEffect") }) useLayoutEffect(()=>{ // 改成 useEffect 试试 console.log("useLayoutEffect") }) return ( n: {n} Click ); } const rootElement = document.getElementById("root"); ReactDOM.render(, rootElement);

注意:为了用户体验最好优先使用useEffect

项目开始 脚手架: create-react-app 钩子: react hooks 状态管理: mobx UI: antd v4 ajax: axios 路由: react-router-dom 和 history css: sass npx create-react-app appname 目录结构 store: 存放mobx -> index.js utils: 工具 -> index.js style: 样式 -> index.css hooks: 钩子 pages: 路由 components: 通用组件 SCSS预处理器 react支持sass SASS 是一种预编译的CSS,作用类似于less 由于react中内置处理了SASS的配置,所以在CRA创建项目中可以直接使用SASS写样式 安装 - 在dev 环境中使用 yarn add sass -D 配置路由 yarn add react-router-dom 配置路由 Layout 和 Login function Layout() { return( layout ) } export default Layout app.js import {BrowserRouter, Routes, Route} from "react-router-dom" import Layout from "./pages/Layout"; import Login from "./pages/Login"; function App() { return ( // 路由配置 {/*创建路由path和组件规则*/} index login ); } export default App;

antd

yarn add antd index.js // 导入这个包 以免报错 import 'antd/dist/antd.min.css' // 最后导入样式文件 以免被覆盖 import './index.scss';

别名路径

能够配置@路径 简化路径处理 craco 配置文件 1. CRA将所有工程化配置,都隐藏在react-scripts包中,所以项目中看不到任何配置信息 2. 如果要修改CRA的默认配置 - 通过第三方库来修改 比如 @craco/craco 推荐 - 通过执行 yarn eject 命令,释放 react-scripts 中的所有配置到项目中 1.安装 yarn add -D @craco/craco 2.在根项目目录中创建craco的配置文件 craco.config.js 并在配置文件中配置路径别名 3.修改package.json 中的脚本命令 4. 在代码中就可以通过 @ 来表示src目录的绝对路径 5. restart 生效 //添加自定义 webpack配置 const path = require('path') module.exports={ //webpack webpack:{ // 别名配置 alias:{ // 约定使用 @ 表示 src 文件所在路径 '@': path.resolve(__dirname, 'src')         } } } package.json 修改 react-scripts -> craco "start": "craco start", "build": "craco build", "test": "craco test",

remember

axios

拦截器 yarn add axios mobx yarn add mobx mobx-react-lite

跨域

yarn add http-proxy-middleware const proxy = require('http-proxy-middleware'); module.exports = function (app) { // const url = `http://pv.sohu.com/cityjson?ie=utf-8`; // let promise = http.get(url); // console.log(promise) app.use(proxy('/api/', { target: 'http://127.0.0.1:9001', changeOrigin: true, ws: true, // headers: {'X-Real-IP': promise['cip']}, headers: {'X-Real-IP': "1.1.1.1"}, pathRewrite: { '^/api': '' } })) };

使用 application/x-www-form-urlencoded format 默认情况下,axios将JavaScript对象序列化为JSON。

要以application / x-www-form-urlencoded格式发送数据,您可以使用以下选项之一。

最简单实用qs内置编码

const qs = require('qs'); axios.post('url', qs.stringify({data}))

路由鉴权

1. 判断token 是否存在 2. 如果存在直接渲染 3. 如果不存在重定向到登录路由 高阶组件 把一个组件,当另外一个组件的参数传入 然后通过一定的判断,返回新组件 import { getToken } from '@/utils' import { Navigate } from 'react-router-dom' function AuthComponent({ children }){ const isToken = getToken()     if (isToken){     return {children}     }eles{     return     } } export { AuthComponent }

useNavigate()

所有带 use 是钩子函数,也只能在函数组件内 使用,

安装history 包

yarn add history

要使用 cookie 需要安装包

yarn add react-cookies import cookie from 'react-cookies' //设置cookie,第三个参数的意思是所有页面都能用这个cookie cookie.save(key,value,{path:"/"}) // 加载名为cookieName的cookie信息 cookie.load(cookieName) // 删除名为cookieName的cookie信息 cookie.remove(cookieName)

mobx 6

yarn add mobx mobx-react-lite # 仅限函数式组件使用

概念

observable 定义一个存储state的可追踪字段(Proxy) action 将一个方法标记为可以修改的state的action computed 标记一个可以由state派生出新值并且缓存其输出的计算属性

监听属性

autorun 函数接受一个函数作为参数,每次当函数所观察的值被发生变化时,他都运行 当自己创建autorun时,他只会运行一次 mobx会自动收集并订阅所有的可观察属性,一旦有改变发生,autorun将会再次触发 autorun(()=>{ console.log('counter.count', counter.count) }) reaction 的使用 reaction 类似于autorun 但可以让你更加精细地控制要跟踪的可观察对象 它接受两个函数作为参数 1。data函数,起返回值将会作为第二个函数输入 2。回调函数 与autorun不同,reaction在初始化时不会自动运行 reaction( ()=> count.count (v,oldv)=>{     console.log('变化了',v,oldv)     } )

异步处理

异步进程中mobx中不需要任何特殊处理,因为不论是何时引发的所有reactions都将会自动更新 因为可观察是可变的,因此在action执行过程中保持对她们的引用一般是安全的 如果可观察对象的修改不是在action函数中,控制台会报警告


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3