UmiJs整合Egg

  • 一. 初始化项目
  • 二. Umi和Egg的细节整合
    • 2.1 使用TypeScript
    • 2.2 使用Eslint
    • 2.3 前端发起请求
    • 2.4 利用egg-view模板来加载umiJs
  • 三. 整合的原理和相关知识点
    • 3.1 项目启动过程
    • 3.2 几个注意点

一. 初始化项目

这里,我们用UmiJs脚手架来初始化一个项目:

1.创建一个名为Umi_Egg的空文件夹:

2.在该文件夹目录中输入命令:

npm create @umijs/umi-app

结果如下,项目结构为:

3.在根目录下,创建Egg项目

npm init egg --type=simple

运行如下:

4.注意,我们这里利用脚手架分别创建了两个项目,自然而然的会生成两个package.json文件,这里我们将里面的内容整合下,保留最外层的package.json文件,内容如下:

{"private": true,"name": "umi_egg","scripts": {"start": "egg-scripts start --daemon --title=egg-server-app","stop": "egg-scripts stop --title=egg-server-app","clean": "ets clean","build": "umi build","postinstall": "umi generate tmp","prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'","dev": "npm run clean && egg-bin dev --port 4396","test": "umi-test","debug": "egg-bin debug --port 4396","test:coverage": "umi-test --coverage"},"gitHooks": {"pre-commit": "lint-staged"},"lint-staged": {"*.{js,jsx,less,md,json}": ["prettier --write"],"*.ts?(x)": ["prettier --parser=typescript --write"]},"egg": {"typescript": true,"declarations": true},"dependencies": {"@ant-design/pro-layout": "^6.5.0","axios": "^0.27.2","egg": "^2.29.3","egg-cors": "^2.2.3","egg-http-proxy": "^1.0.1","egg-scripts": "^2.13.0","egg-socket.io": "^4.1.6","egg-view-assets": "^1.6.1","egg-view-ejs": "^2.0.1","react": "17.x","react-dom": "17.x","umi": "^3.5.23"},"devDependencies": {"@types/react": "^17.0.0","@types/react-dom": "^17.0.0","@typescript-eslint/eslint-plugin": "^5.21.0","@umijs/preset-react": "1.x","@umijs/test": "^3.5.23","autod": "^3.0.1","autod-egg": "^1.1.0","egg-bin": "^4.11.0","egg-ci": "^1.11.0","egg-mock": "^3.21.0","egg-ts-helper": "^1.25.9","eslint": "^7.32.0","eslint-config-egg": "^9.0.0","eslint-plugin-react": "^7.25.0","lint-staged": "^10.0.7","prettier": "^2.2.0","typescript": "^4.6.4","yorkie": "^2.0.0"}
}

5.对目录做出如下调整:(mock以及app下的test目录也没啥用,可以删掉)

更改后目录结构如下:

6.最重要的环节:

npm install

二. Umi和Egg的细节整合

到这里,初始化工作也完成了,接下来开始讲两个框架进行整合。

咱们先来启动下项目,看看是否能够正常启动并访问,大家使用命令:

npm run dev

结果应该如下:

访问对应的URL:http://127.0.0.1:4396/结果如下:如果正常访问则说明项目是没什么问题的

2.1 使用TypeScript

为什么要使用TypeScript呢,这里做个解释,TypeScript本身就是作为JavaScript的一个超集语言。而TypeScript可以用于编写前端页面(index.tsx)以及后端代码(index.ts),方便语言的一个统一。

1.安装TypeScript:

npm install typescript
npm install @typescript-eslint/eslint-plugin --save-dev

此时,我们就能将后端代码改成这样,例如:
controller/home.ts文件:

import { Controller } from 'egg';class HomeController extends Controller {getData() {this.ctx.body = this.service.userService.getUserName();}
}
export default HomeController;

router.ts文件:

// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Application } from 'egg';export default (app: Application) => {const { controller, router } = app;router.prefix('/zong*');router.post('/user/getData', controller.home.getData);
};

service/UserService.ts文件:

import { Service } from 'egg';class UserService extends Service {getUserName() {return 'LJJ';}
}
export default UserService;

在这里,有的读者可能会发现,在写代码的时候,通过controler.xxx的时候,没有对应的提示代码。这里需要借助egg-ts-helper插件来生成对应的声明(dev依赖中已经添加了):

package.json文件中的scripts属性下添加脚本命令:

"tsc": "ets && tsc -p tsconfig.json",

同时,我们要在tsconfig.json文件中做对应的配置,目的是防止一些不必要的目录被egg-ts-helper插件执行,生成对应的js文件,可能会出现很多Error。内容如下:

{"compilerOptions": {"target": "es2017","module": "commonjs","moduleResolution": "node","importHelpers": true,"jsx": "react","esModuleInterop": true,// "outDir":"./dist","baseUrl": "./","strict": true,"paths": {"@public/*": ["public/*"],"@/*": ["src/*"],"@@/*": ["src/.umi/*"],"@defineType/*": ["./model/*"]},"resolveJsonModule": true,"suppressImplicitAnyIndexErrors": true,"allowSyntheticDefaultImports": true,"noImplicitAny": true,"experimentalDecorators": true,"emitDecoratorMetadata": true,"charset": "utf8","allowJs": false,"pretty": true,"noEmitOnError": false,"noUnusedLocals": false,"noUnusedParameters": false,"allowUnreachableCode": false,"allowUnusedLabels": false,"strictPropertyInitialization": false,"noFallthroughCasesInSwitch": true,"skipLibCheck": true,"inlineSourceMap": true,"skipDefaultLibCheck": true},"exclude": ["app/public","app/views","node_modules","lib","es","dist","**/__test__","test","docs","tests"]
}

然后运行命令

npm run tsc

运行结果如下:(下面的错误其实没关系,因为前端的代码我们还没有改动过,先放着,不过可以看到红框地方,插件会生成对应的声明文件)

生成好后,咱们写代码的时候就会有这样的效果:

小伙伴本记得运行下npm run clean哦。

2.2 使用Eslint

配置下相关的eslint,这样开发起来会规范一点,更改下.eslintrc.json文件:

{"env": {"browser": true,"es2021": true},"extends": ["eslint:recommended","plugin:react/recommended","plugin:@typescript-eslint/recommended","eslint-config-egg/typescript"],"parser": "@typescript-eslint/parser","parserOptions": {"ecmaFeatures": {"jsx": true},"ecmaVersion": 12,"sourceType": "module"},"plugins": ["react", "@typescript-eslint"],"rules": {"indent": ["error", 2, { "SwitchCase": 1 }],"react/display-name": ["off", { "ignoreTranspilerName": true }],"react/prop-types": ["off"],"@typescript-eslint/no-var-requires": 0,"no-bitwise": ["off"]}
}

配置好后,效果如图:(记得设置下保存的时候自动使用eslint

到这里为止,我们后端已经写完了,那么如何在前端页面调用后端的接口呢?

2.3 前端发起请求

我们利用axios来发起请求,先下载对应的包:

npm install axios

src/utils目录下创建一个工具类:axiosHelper.ts

import axios from 'axios';export default async function(prefixUrl: string,methodName: string,data: any,
) {let response;try {response = await axios({url:location.protocol +'//' +window.location.host +'/zong' +prefixUrl +methodName,method: 'post',data,withCredentials: true,});} catch (error) {console.log(error);}return response;
}

page/index.tsx文件:

import React from 'react';
import { Button } from 'antd';
import axios from '../utils/axiosHelper';const UserPage = () => {return <Button onClick={() => axios('/user', '/getData', { msg: 'hello' }).then(val => { console.log(val); })}>数据获取</Button>;
};export default UserPage;

到这里,前端页面也写好了,那么大家可以从这个角度来思考下以下问题:

  • 通过Egg的脚手架创建的后端程序,运行命令为:egg-bin dev
  • 通过UmiJs脚手架创建的前端程序,运行命令为:umi dev

两者命令不一样,启动时的默认端口也不一样,那么怎么去同时启动呢?一定要配置跨域吗?若是如此,这还能叫做Umi整合Egg吗?不对。接下来开始讲解本文的细节部分。

2.4 利用egg-view模板来加载umiJs

到目前为止,项目中比较重要,但是又没有动过的,只有egg的配置文件了:

话不多说,我们直接上代码(后面再讲具体的流程):
1.更改config.default.ts文件(自己改下后缀哦):先npm install cross-env

/* eslint-disable @typescript-eslint/no-unused-vars */
import { EggAppConfig, EggAppInfo, PowerPartial } from 'egg';export default (appInfo: EggAppInfo): any => {/*** built-in config* @type {Egg.EggAppConfig}**/const config = {} as PowerPartial<EggAppConfig>;// 业务相关配置,这里是拿到我们的启动环境const bizConfig = {envName: appInfo.pkg.config.env.toLowerCase(),};// use for cookie sign key, should change to your own and keep securityconfig.keys = appInfo.name + '_1650629680875_8410';// add your middleware config hereconfig.middleware = [];config.security = {csrf: {enable: false,},};// 开启ejs模板的使用,这里一定要配置,否则egg不会使用ejs模板,会报错config.view = {mapping: {'.ejs': 'ejs',},};// 本地代理,启动umi dev,监听8007端口config.assets = {publicPath: '/public',devServer: {// 这里是本地的一个代理,用了cross-env命令,因此需要npm install cross-env// 同时UMI_ENV=dev命令,若使用,则必须拥有一个名为.umirc.dev.ts的文件// 也因此后续要创建两个文件,一个.umirc.dev.ts,一个.umirc.ts,一般用于生产和本地开发配置的区分command: 'cross-env UMI_ENV=dev umi dev --port=8007',port: 8007,env: {// 这里的baseDir也就是 下文的 server.js文件中赋值的。程序启动的时候,会自动读取APP_ROOT: appInfo.baseDir,BROWSER: 'none',ESLINT: 'none',SOCKET_SERVER: 'http://127.0.0.1:8007',PUBLIC_PATH: 'http://127.0.0.1:8007',},},};return {...config,...bizConfig,};
};

2.更改plugin.ts文件:

/* eslint-disable @typescript-eslint/no-unused-vars */
import { EggPlugin } from 'egg';const plugin: EggPlugin = {assets: {enable: true,package: 'egg-view-assets',},ejs: {enable: true,package: 'egg-view-ejs',},httpProxy: {enable: true,package: 'egg-http-proxy',},cors: {enable: true,package: 'egg-cors',},
};
export default plugin;

3.根目录下创建文件.umirc.dev.ts,和原有的.umirc.ts文件内容也跟下面的配置保持一致。

import { defineConfig } from 'umi';export default defineConfig({base: '/zong/',nodeModulesTransform: {type: 'none',},manifest: {fileName: './config/manifest.json',publicPath: '/public/',},// 打成的包输出的路径outputPath: './app/public',publicPath: '/public/',fastRefresh: {},
});

4.根目录下创建server.js文件:

/* eslint-disable indent */
/* eslint-disable @typescript-eslint/indent */
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');
const { Application } = require('egg');
const pkj = require('./package.json');const app = new Application({baseDir: path.resolve('./'),
});app.ready(() => app.listen(pkj.config.port));

5.根目录下创建app.config.js文件:

module.exports = {Env: 'dev',
};

6.根目录下创建app.ts文件:

import { Application } from 'egg';class AppBootHook {private app: Application;constructor(app: Application) {this.app = app;}configWillLoad() {// 此时 config 文件已经被读取并合并,但是还并未生效// 这是应用层修改配置的最后时机// 这里则加入一个版本号,目的是为了下文ejs模板里面,加载umiJs后,能保证每次项目重新发布(启动的时候),不会因为缓存而导致未更新this.app.config.fileVersion = new Date().getTime().toString();}
}module.exports = AppBootHook;

7.在app目录下创建view目录,用于放ejs文件,同时创建index.ejs文件:

内容如下:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Test</title><% if (envName == "dev") { %><%- helper.assets.getStyle('umi.css') %><% } else { %><link rel="stylesheet" type="text/css" href='/<%- contextPath %>/public/umi.css?v=<%- fileVersion %>' /><% } %>
</head><body><div id='root' class='subRootContent'></div><script>window.resourceBaseUrl = '<%= helper.assets.resourceBase %>';<% if (envName != "dev") { %>window.staticUrl = '/<%- contextPath %>/public'window.resourceBaseUrl = '/<%- contextPath %><%= helper.assets.resourceBase %>';<% } %>window.publicPath = resourceBaseUrl</script><% if (envName == 'dev') { %><%- (helper.assets.getScript('umi.js')) %><% } else { %><script src='/<%- contextPath %>/public/umi.js?v=<%- fileVersion %>'></script><% } %>
</body></html>

8.在config目录下,创建manifest.json映射文件:

{"umi.css": "umi.css","umi.js": "umi.js","index.html": "index.html"
}

9.修改controller/home.ts文件:增加一个index方法,主要是使用index.ejs模板。

async index() {const { config } = this;const { envName, fileVersion } = config;// 这里的data数据,加载到ejs模板后,直接可以通过对应的属性名来获取对应的值。// 同时注意,要使用ejs模板,必须要有个contextPathconst data = {envName,fileVersion,contextPath: 'zong',};await this.ctx.render('index.ejs', data);
}

10.修改路由,增加监听:这样页面上的任何一个请求,都会被Egg路由捕捉到,然后到对应的index方法下去执行。

router.get('**/**', controller.home.index);
router.post('**/**', controller.home.index);

最后先npm run build,在npm run dev,项目倘若启动成功:会出现以下字样

  • 4396是Egg的端口,出现了说明Egg程序运行成功。
  • 8007端口是umi dev命令的一个启动指定端口,出现了说明前端也运行成功了。


运行成功后,访问页面http://localhost:4396/zong/,可以看到,我们用Egg程序的4396端口就能访问前端Umi的页面,同时还能够正常的获得请求。说明整合成功了。

然后我将代码的源码发给大家,大家可以对照下代码,若出错了,看下是否哪里配置有问题(也有可能是我写的不够详细或者漏说,若哪里漏说了,还请指正)源码

大家把代码拉下来,npm install后就可以跑了。

三. 整合的原理和相关知识点

3.1 项目启动过程

1.npm run dev的时候,实际上跑的命令是egg-bin dev,因此,本项目跑起来后实际上是一个Egg项目!

2.程序会在启动的过程中,加载这几个文件:

  • app.config.js:配置了个Env属性,只在本地开发有效。
  • server.js:配置了项目的地址baseDir
  • config/config.default.ts:Egg的相关配置。
  • 其他的略。

3.egg配置中,我们使用了assets代理:主要通过command命令,执行了umi dev命令,同时指定端口8007。

config.assets = {publicPath: '/public',devServer: {command: 'cross-env UMI_ENV=dev umi dev --port=8007',port: 8007,env: {APP_ROOT: appInfo.baseDir,BROWSER: 'none',ESLINT: 'none',SOCKET_SERVER: 'http://127.0.0.1:8007',PUBLIC_PATH: 'http://127.0.0.1:8007',},},
};

4.到这里,前后端实际上都是运行成功了。那么我们在URL上,通过4396(Egg后端端口)就能访问前端页面的原理是啥?大家请看路由:

router.get('**/**', controller.home.index);
router.post('**/**', controller.home.index);

此时,URL上访问的任何路径,都会被该路由捕捉,毕竟**/**摆在这里

UmiJs整合Egg相关推荐

  1. Egg整合antd文件上传以及防踩坑指南

    Egg整合antd文件上传以及防踩坑指南 前言 一. 项目编写 二. 注意事项 2.1 清理缓存 2.2 antd的Upload组件得到的事件对象问题 2.3 关于onChange事件的注意 前言 最 ...

  2. 直播弹幕系统(五)- 整合Stomp替换原生WebSocket方案探究

    直播弹幕系统(五)- 整合Stomp替换原生WebSocket方案探究 前言 一. STOMP 协议简单介绍 1.1 客户端编码基础 1.2 服务端编码基础 1.2.1 SimpMessagingTe ...

  3. 如何从校招脱颖而出?支付宝程序媛王妍岩:自信+方法

    简介: 请收好!加入支付宝梦之队的校招独家经验分享 就现在!蚂蚁「校招季」重磅来袭!除了介绍蚂蚁的技术大咖,我们还邀请了一些通过校招来到蚂蚁的过来人分享他们的通关经验和心得,这里随时可能有行业技术大咖 ...

  4. 学习 Node.js 一本书就够了【送书】

    前言 每过十天半个月,公众号「Web项目聚集地」就会给大家发福利,福利不限于学习资料.实体书籍.电子工业出版社上新了一本书籍<Node.js实战:使用Egg.js+Vue.js+Docker构建 ...

  5. php sequelize,egg.js整合数据库ORM框架Sequelize

    在上篇文章中我们写了egg.js怎么连接mysql数据库, 而在一些较为复杂的应用中,我们可能会需要一个 ORM 框架来帮助我们管理数据层的代码.Java中有Mybatis.Hibernate.Spr ...

  6. react router官方文档_阿里开源可插拔 React 跨端框架 UmiJS

    点击上方"开发者技术前线",选择"星标" 18:30 在看 真爱 作者:Tamic  |  编辑: 可可 阿里之前开源:阿里闲鱼开源 Flutter 应用框架 ...

  7. Vue项目构建后通过Nginx/SpringBoot/Express/Egg发布

    Vue项目构建后通过Nginx/SpringBoot/Express/Egg发布 构建 当项目开发完毕,只需要运行一行命令就可以打包你的应用: $ yarn build or $ npm run bu ...

  8. 专访死马:为什么说Egg.js是企业级Node框架

    在 7 月 6 日的 ArchSummit 架构师峰会深圳站上,Egg.js 的主要开发者不四(网名死马)将给参会者带来<企业级 Node.js Web 框架研发与落地>的分享,借此机会, ...

  9. 2021-07-16 转载 - UmiJS应用框架

    UmiJS应用框架 介绍 umi,中文可发音为乌米,是一个可插拔的企业级 react 应用框架.umi 以路由为基础的,支持类 next.js 的约定式路由,以及各种进阶的路由功能,并以此进行功能扩展 ...

  10. 【实战项目惜时App需求分析说明书】Vue-cli3+Vant UI+Vue-element-admin+Egg.js+Mysql

    小编的第二个全栈项目,接下来会继续推出其中的源码设计和功能分析博客,大家多多支持呀! 项目源码地址: https://gitee.com/lwkgood/time-client (客户端) https ...

最新文章

  1. 机器学习算法专题(附组队学习)
  2. 避免沦为“人工智障”,机器人还需找对语音交互的“打开方式”
  3. 青龙羊毛——梅川衣服俩小孩(搬)
  4. 搬家到新地址:http://kittsoft.xp3.biz/,欢迎访问!
  5. PHP开发APP接口(二)
  6. mac下简单绘图工具
  7. 想要使用 for循环,就要添加 索引器
  8. 在一台服务器上配置多个Tomcat的方法
  9. c语言lr分析器的设计与实现_[源码和文档分享]基于有限自动机的词法分析器构造...
  10. Java Web学习总结(11)——Session使用示例教程
  11. 树形图,多层级目录等其他名称待补充……
  12. mysql cluster常见问题访问_MySQL Cluster的常见问题
  13. AMD 透漏 Zen 2 技术细节!
  14. java axis_Java 使用Axis实现WebService实例
  15. Java 题目:有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。
  16. 2022-2028年全球及中国点胶枪行业发展现状调研及投资前景分析
  17. 现代软件工程讲义 5.1 软件的质量保证 (QA) 和测试 (Test)
  18. openlayers3中,在地图上添加静态边界线
  19. 2022年第十三届蓝桥杯大赛软件类决赛C/C++/Java/Python真题
  20. 小笨狼的LLDB技巧:chisel

热门文章

  1. 国际混淆C代码大赛获奖作品部分源码
  2. Java并发编程——创建线程的三种方法以及区别
  3. Vue 项目静态化打包 seo优化
  4. 计算机联锁设备的组成及原理,铁路信号计算机联锁系统及技术分析
  5. Linux: 介绍make menuconfig中的每个选项含义【转】
  6. Android GMS 包。 GOOGLE play
  7. UPS电源扩展功能一UPS干节点通讯接口扩展板,你了解吗?
  8. hutool依赖:BeanUtil工具类的使用:对象转对象、对象转map、map转对象
  9. matlab中的ifftshift的用法,如何在R中写fftshift和ifftshift?
  10. 威锋 VL817-Q7 HUB 芯片,一进四出USB3.1gen1 5G速率传输。