课程地址:/learn/1185
第一章 课程导学
01-01 课程介绍
一、Egg.js框架介绍
1、Egg.js是基于Node.js和Koa的一个企业级应用开发框架,可以帮助团队降低开发成本和维护成本。
二、express,koa.js
上手比较简单,但是没有严格的开发规范
三、Egg.js特性
1、提供基于Egg的定制上层框架的能力
2、高度可扩展的插件机制
3、内置多进程管理
4、基于Koa开发性能优异
5、框架稳定,测试覆盖率高
6、渐进式开发
四、涉及技术点
vant ui框架
vue-cli3
moment.js
Egg.js基础
mysql基础
前后端联调
第02章 基础功能讲解
02-01 搭建Egg.js的开发环境
一、官网
1、/zh-cn/intro/quickstart.html
二、建议使用node的LTS版本
LTS:长期稳定版本
current:含目前nodejs最新的特性,相对而言没那么稳定
三、
1、脚手架生成项目
mkdir egg-demo && cd egg-demonpm init egg --type=simple
2、安装相关依赖包
npm install
3、命令启动
npm run dev:开发中npm run start:实际生产项目中使用
四、目录
1、app:项目核心目录。业务逻辑、数据库方面的操作
2、config:针对egg.js的插件进行配置
(1)config.default.js:
(2)plugin.js:
3、test:单元测试的时候使用的
4、autod.conf.js:autod的配置文件
5、.eslintrc、.eslintrc:eslint配置文件
02-02 初识Egg.js的路由
一、Router主要用来描述请求URL和具体承担执行动作的Controller的对应关系,框架约定了app/router.js文件用于同一所有路由规则。
二、通过同一的配置,我们可以避免路由规则逻辑散落在多个地方,从而出现未知的冲突,集中在一起我们可以更方便的来查看全局的路由规则。
三、const { ctx } = this
每次用户请求的时候,框架就会实例化一个context上下文
context主要用来存放用户请求的一些信息
四、Controller:负责解析用户的输入,处理后返回相应的结果
02-03 GET方式请求中的两种传参方式
一、两种传参方式
1、键值对的模式:?id=123&acb=456
路由:http://127.0.0.1:7000/product/detail?id=123
const { ctx } = thisconsole.log(ctx.query)ctx.body = 'id == ctx.query.id'
2、基于/:/123/456
路由:http://127.0.0.1:7000/product/detail2/100
ctx.params.id获取到的id,也就是这个100,是字符串类型。
const { ctx } = thisconsole.log(ctx.params)ctx.body = `id == ctx.params.id`
02-04 POST等其他方式请求方法
一、请求方法
router.head - HEAD
router.options - OPTIONS
router.get - GET
router.put - PUT
router.post - POST
router.patch - PATCH
router.delete - DELETE
router.del - delete是保留字,所以提供了一个delete方法的别名
router.redirect - 可以对URL进行重定向处理
二、POST
const { ctx } = thisconsole.log(ctx.request.body)ctx.body = {id: 123}
这时无法在浏览器请求了,这时候用一个工具:postman
1、postman发送请求,报错。终端提示了报错信息
2、解决方案
config/config.default.js中关闭csrf防御方案。
config.security = {csrf: {enable: false,}}
三、常见的安全漏洞
1、XSS攻击:对Web页面注入脚本,使用JavaScript窃取用户信息,诱导用户操作。
2、CSRF攻击:伪造用户请求向网站发起恶意请求。
3、钓鱼攻击:利用网站的跳转链接或者图片制造钓鱼陷阱。
4、HTTP参数污染:利用对参数格式验证的不完善,对服务器进行参数注入攻击。
5、远程代码执行:用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令。
四、egg框架针对web常见的安全风险内置了丰富的解决方案
1、利用extend机制扩展了Helper API,提供了各种模板过滤函数,防止钓鱼或XSS攻击。
2、常见的Web安全头的支持。
3、CSRF的防御方案。
4、灵活的安全配置,可以匹配不同的请求url
5、可定制的白名单,用于安全跳转和url过滤。
6、各种模板相关的工作函数做预处理。
在框架中内置了安全差价egg-security提供了默认的安全实践。
五、PUT,更新数据
路由:http://127.0.0.1:7000/product/update/100
const { ctx } = thisconsole.log(ctx.params)ctx.body = `id == ctx.params.id`
六、DELETE
路由:http://127.0.0.1:7000/product/delete/100
const { ctx } = thisconsole.log(ctx.params)ctx.body = `id == ctx.params.id`
02-04 Egg.js中的Service服务
一、服务(Service)
简单来说,Service就是在复杂业务场景下用于做业务逻辑封装的一个抽象层,提供这个抽象有以下几个好处:
1、保持Controller中的逻辑更加简洁
2、保持业务逻辑的独立性,抽irc象出来的Service可以被多个Controller重复调用。
3、将逻辑和展现分离,更容易编写测试用例。
二、使用场景
1、复杂数据的处理,比如要展现的信息需要从数据库获取,还要经过一定的规则计算,才能返回一用户显示。或者计算完成后,更新到数据库。
2、第三方服务的调用,比如GitHub信息获取等。
三、Service服务一般放在app/service文件夹下
02-05 Egg.js中模板引擎的使用
一、View模板渲染
绝大多数情况,我们都需要读取数据后渲染模板,然后呈现给用户。故我们需要引入对应的模板引擎。
框架内置egg-view-ejs作为模板解决方案,并支持多模板渲染,每个模板引擎都以插件的方式引入,但保持渲染的API一致。
二、默认情况下,模板引擎文件会放在app/view文件夹下
三、plugin.js
exports.ejs = {enable: true,package: 'egg-view-ejs',}
.config.default.js
const path = require('path'); // Nodejs内置模块,可解析文件路径config.view = {mapping: {'.html': 'ejs'},root: path.join(appInfo.baseDir, 'app/html'); // 修改index.html等html文件存放的文件夹目录,这是存放在app/html文件夹下root: [ // 多目录配置path.join(appInfo.baseDir, 'app/view'),path.join(appInfo.baseDir, 'app/html')]}config.ejs = {delimiter: "$", // 全局修改分隔符, index.html中用<$= id $>代替<%= id %>}
四、
1、刷新页面,会报错404 not found
因为egg.js中每个方法几乎都是使用同步模式,返回的是promise,所以需要使用await
const { ctx } = this;ctx.render('index.html')
修改成:
const { ctx } = this;await ctx.render('index.html')
五、
controller/home.js
await ctx.render('index.html', {res,lists: ['a', 'b', 'c']}, {delimiter: '$', // 支持对单个文件进行分隔符修改})
view/index.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="/public/css/main.css"></head><body><div>这是测试</div><img src="/public/img/img.jpg"><h1>测试样式</h1><p><%=res.id%></p><%# 这是注释 %><ul><%for(var i=0; i<lists.length; i++){%><li><%=lists[i]%></li><%}%></ul></body></html>
六、为什么要学后端的模板引擎
1、后端渲染由来以及,渲染性能得到业界认可
2、利于SEO优化,对纯展示类网站体验较好(如公司官网、某些运营页面)
3、对前后端分离开发模式的补充(单点登录的登录页面)
七、Ejs模板引擎中静态资源的使用和配置
view/user.html
<% include user-header.html %>
八、egg项目中,静态资源默认是放在public的
九、egg项目会默认引入egg-static,egg-static是引用了koajs的static-cache这个插件,然后进行二次封装。
config.default.js
config.static = {perfix: "/assets/", // 将静态资源存放在assets目录下,默认是public文件夹dir: path.join(appInfo.baseDir, "app/assets"),}
index.html
<link rel="stylesheet" type="text/css" href="assets/css/user.css">
<script src="assets/js/user.js"></script>
02-07 Egg.js中静态资源的使用
一、默认情况下,静态资源会放在app/public文件夹下
1、public/img,存放图片资源
2、public/css,存放css资源
3、public/css,存放css资源
第03章 项目实战-前端开发
03-01 搭建前端系统,引入vant组件库
一、vue-cli
npm install -g @vue/clivue --versionvue --helpvue create clientmkdir client & cd clientnpm run serve
二、h5移动端组件库vant
1、vant:轻量、可靠的移动端Vue组件库
npm i --save vant
2、引入组件的方式
自动按需引入组件(推荐):用到babel-plugin-import这个babel插件
手动按需引入组件
导入所有组件
通过CDN方式
三、.babelrc中
{"plugins": [["import", {"libraryName": "vant","libraryDirectory": "es","style": true}]]}
引入vue-router
一、npm i vue-router
二、App.vue
<div id="app"><router-view></router-view></div>
1、src/router/index.js,引入vue-router,配置routes
Vue.use(VueRouter)const router = new VueRouter({mode: 'hash',routes: []})
2、main.js
import router from './router'new Vue({router,render: h => h(App)}).$mount('#app')
03-05 开发文章详情页面
一、this.$router.push()进行路由的跳转
第04章 项目实战-后端开发
04-01 创建mysql表结构
一、数据库
终端操作
mysql -uroot -p;
show databases;
create database egg_article;
use egg_article;create table article(id int(10) not null auto_increment,img text default null comment '缩略图',title varchar(80) default null comment '文章标题',summary varchar(300) default null comment '文章简介',content text default null comment '文章内容',createTime timestamp default null comment '发布时间',primary key(id))engine=InnoDB AUTO_INCREMENT=1 comment '文章表';insert into article(img, title, summary, content, createTime) values('编程必备', '/szimg/5d1032ab08719e0906000338.jpg', '介绍编程必备基础知识', '快速、系统补足您所需要的知识内容', '-08-10 10:20:20');
use egg_article;
show tables
desc article;
select * from article;
04-02 egg.js连接mysql数据库
一、egg-mysql
框架提供了egg-mysql插件来访问MySQL数据库。这个插件既可以访问普通的MySQL数据库,也可以访问基于MySQL协议的在线数据库服务。
二、
npm i egg-mysql
04-04 引入moment.js处理时间问题
一、
npm i moment
04-05 egg.js开发文章首页和详情页接口
一、插入数据
const result = await app.mysql.insert('article', params);
二、查询文章列表
const result = await app.mysql.select('article');
三、查询文章详情
1、查询单个表,使用get方法
const result = await app.mysql.get('article', { id })
04-06 前后端接口联调
一、proxy的changeOrigin:开启虚拟服务器,让虚拟服务器去请求代理服务器,这样就相当于是两台服务器之间进行数据的交互,就不用担心跨域的问题。
课程地址:/class/452.html
(aSuncat-0707: 前后端涉及的内容比较多,可以多听几遍)
第01章 关于这门课,你需要知道的
01-01 课前须知,这里有你需要了解的一切
一、课程涉及框架
1、react
umiJS:react工具集锦,路由管理、数据mock,其他相关插件
egg.js:是nodejs的一个框架,有比较严格的项目管理规范,支持路由配置,controller配置器,service服务,模板渲染,默认支持很多实用的插件
2、
自定义hook:提高研发效率
数据流工具think-react-store: react数据流解决方案
Project-libs:常用函数集锦
IntersectionObserver:滚动加载,图片懒加载
二、后端核心技术
egg.js:主框架
jwt:用户验证
Mysql:数据存储
Sequelize:ORM框架,操作mysql
扩展egg框架:提交研发效率
自定义中间件和插件:拦截请求等处理
三、技术架构图
四、前端干货
1、自定义hook
useTitleHook:动态修改浏览器title
useHttpHook: 发送http请求,对某些状态值进行监听
useObserverHook: 监听dom元素是否进入展示区域
useImgHook: 实现图片懒加载
2、自定义组件
createPortal:createPortal在根节点之外创建新的节点
Modal:
ErrorBoundary:捕获错误,展示友好信息
MenuBar:底部Menu
LazyLoad: 懒加载
ShowLoading:底部加载
五、后端干货(手把手开发)
1、中间件
httpLog: 日志中间件
userExist:判断用户是否存在
2、插件
egg-auth:验证用户
egg-info:获取系统信息
3、框架扩展
application:egg.js的应用实例
helper:帮助函数,将要使用的函数全都挂载到egg.js中,这样使用时无需引入,直接用helper即可
requst:对请求来进行扩展
context:对上下文扩展
response: 对response扩展
六、课程安排
react基础进阶 -> 开发组件 -> 开发自定义hook -> egg.js基础 -> egg.js高级 -> 前端界面开发 -> 后端接口开发 -> 系统安全处理 -> 项目发布
七、学习基础
熟悉react.js基础语法
了解node.js基础语法
对全栈开发感兴趣
第02章 React核心内容梳理及自定义组件开发
02-01 本章概览
一、组件经历的不同阶段
二、组件之间的通信
1、父子组件通信
2、兄弟组件通信
3、组件跨页面通信
三、Lazy和Suspence实现懒加载
三、组件报错了怎么办:ErrorBoundary
可将错误信息上传,便于错误的排查
四、CreatePortal创建自定义弹窗
五、章节介绍
UmiJs入门介绍
React组件生命周期
React组件通信方式
Dva数据处理和mock
Context实现数据流管理
组件懒加载
ErrorBoundary错误边界
createPortal自定义弹窗
Ref api操作dom和组件
02-02 前端开发环境搭建
一、vscode设置
command + shift + p:选择将”code“命令添加到PATH
这样就在终端输入code . 就可以在终端打开vscode编辑器了。
二、依赖包管理工具:npm、yarn
02-03 (章节缺失)
02-08 React组件通信
一、父组件向子组件传值
二、子组件向父组件传值
三、兄弟组件之间的传值
1、父组件作为中间层
2、如果嵌组件套得非常多,【父组件作为中间层】的方式就不可行了。
(1)dva
(2)context api
02-09 Dva数据处理及数据mock
一、models下的namespace是可选的,如果有,就取namespce,否则取文件名。
二、reducers:同步的方法
effects:异步的方法
call:调用异步函数
put: 事件派发
// 同步reducers: {getLists(state, action) {return {...state,lists: Array(10).fill(action.payload)}},},// 异步effects: {*getListsAsync({ payload }, { call, put }) {yield put({type: 'getLists',payload,})}}
三、umi的mock功能是对express的封装。
02-10 基于react context api实现数据流管理
一、子组件(消费者组件),订阅Provider的属性,Provider组件值改变,消费者组件会被重新渲染。
1、contextType
2、consumer
02-11 LazyLoad组件开发【基于lazy与suspense实现的懒加载组件】
一、.umirc.js 中dynamicImport: true, // 按需加载
1、页面级别:每个页面内所有组件打包成1个js
二、希望对每个页面的每个组件实现按需加载。
const Demo = lazy(() => import('./demo'))<Suspense fallback=(<div>loading...</div>)<Demo /></Suspense>
三、components/LazyLoad/index.js
_renderLay = () => {let lazy;const { component, delay, ...other } = this.props;if (!component || component.constructor.name != 'Promise') {Lazy = import('./error')}Lazy = lazy(() => {return new Promise(resolve) => {setTimeout(() => {resolve(component)}, delay || 300)}})return <Lazy {...other} />}
02-12 ErrorBoundary组件开发[基于React错误边界技术实现的组件]
一、错误处理:react提供的2个构造函数
getDerivedStateFromError()
componentDidCatch()
二、src/components/ErrorBoundary/index.js
import React, { Component } from 'react';export default class ErrorBoundary extends Component {constructor(props) {super(props);this.state = {flag: false,}}static getDerivedStateFromError(error) {return {flag: true,}}/* error: 抛出的错误* info: 带有componentStack key的对象,其中包含有关组件引发错误的栈信息*/copmponentDidCatch(error, info) {}render() {return (<div>{this.state.flag ? <h1>发生错误,请稍后再试!</h1> : this.props.children}</div>)}}
1、layouts/index.js中引入ErrorBoundary组件
错误边界是在父组件监测子组件的错误,并不能监测本身发生的错误。
三、可选技术
house?.info?.activity
02-13 Modal组件开发【基于createPortal创建自定义弹窗组件】
一、ErrorBoundary,无法处理组件点击事件内部的错误,setTimeout内部的错误。
二、src/CreatePortal/index.js
import React, { Component } from 'react';import ReactDOM from 'react-dom';export default class CreatePortal extends Component {constructor(props) {super(props);this.body = document.querySelector('body');this.el = document.createElement('div');}componentDidMount() {this.el.setAttribute('id', 'protal-root');this.body.appendChild(this.el);}componentWullUnmount() {this.body.removeChild(this.el);}render() {return ReactDOM.createPortal(this.props.children, this.el)}}
createPortal(react node节点,希望插入的dom节点)
02-14 使用ref api来操作dom和组件
一、数据流之外,父组件操作子组件,或操作内部组件dom元素,则可以用ref
第03章 React Hooks开发模式详解及自定义hook开发
03-01 react hook api - 新的组件开发模式
一、hook api主要用于function 类型的组件
二、useEffect方法虽然可以执行异步函数,但是不支持async, await
// 错误写法useEffect(async() => {})
1、async方法写在useEffect方法里面
useEffect(() => {async function demo() {console.log('use')}demo}, [count])
2、async写在useEffect外面
async function demo() {console.log('demo')}useEffect(() => {console.log('use')demo()}, [count])
三、useLayoutEffect是在所有dom渲染完后,才同步执行useLayoutEffect。
一般在这个方法内做dom操作。
03-03 useTitleHook【根据url修改页面title的自定义hook】
一、useTitleHook.js
import { useLayoutHook, useState } from 'react';export default function useTitleHook(title) {const [state, setState] = useState();useLayoutEffect(() => {document.title = title;setState(title)}, [title])return state;}
二、jsconfig.json
1、配置vscode相关的配置
{"compilerOptions": {"baseUrl": "src","paths": {"@/hooks": ["hooks/index"]}}}
2、这样import { useTitleHook } from ‘@/hooks’;,鼠标放到@/hooks上,就能看到是从哪里引入的
03-05 使用think-react-store实现数据处理(基于react hooks和context实现的数据流工具)
一、think-react-store:基于react hooks和context实现的数据流工具
二、
store/stores/user.js
export default {state: {id: undefined,username: undefined,}reducers: {getUser(state, payload) {return {...state,...payload,}}},effects: {async getUserAsync(dispatch, rootState, payload) {await new Promise(resolve => {setTimeout(() => {resolve();}, 1000)})dispatch({type: 'getUser',payload,})}}}
store/stores/index.js
import { default as User } from './user';
store/index.js
import React, { useState, useEffect } from 'react';import { StoreProvider } from 'think-react-store';import * as store from './stores';import log from 'think-react-store/middleware/log';import User from './user';export default function(props) {const [state, setState] = useState();useEffect(() => {}, [])return (<StoreProvider store={store} middleware={[log]}><User /></StoreProvider>)}
store/user.js
import React, { useState, useEffect } from 'react';import { useStoreHook, useStateHook, useDispatchHook } from 'think-react-store';export default function(props) {const [state, setState] = useState();const { user: { id, usename, getUserAsync } } = useStoreHook();const states = useStateHook(); // const states = useStateHook('user'); // 仅返回用户相关的全局stateconsole.log(states);const dispatch = useDispatchHook(); //const dispatch = useDispatchHook('user'); // 这样dispatch()里就不用写keyconst handleClick = () => {getUserAsync({id: 20,usename: 'admin2',})}const handleClick2 = () => {dispatch({key: 'user',type: 'getUserAsync',payload: {id: 20,username: 'admin2'}})}useEffect(() => {getUserAsync({id: 10,usename: 'admin',})})return (<div>user-id: {id}<br/>username: {username}<br/><button onClick={handleClick}>修改</button></div>)}
03-05 Fiber架构解析
一、为什么需要Fiber架构
react16之前的渲染流程
Fiber架构:渲染阶段分成调度阶段、提交阶段
二、Fiber的执行流程
Fiber是一个js对象数据结构。
三、Fiber对React生命周期API的影响
调度阶段是可以执行多次的。所以比如发送请求等不适合放在调度阶段。
第04章 为什么应用Egg.js
04-01 Egg.js企业及开发的利器概述
一、企业级应用的特点
功能完善
规范性高
便于扩展(插件方面的扩展,帮助函数方面的扩展)、升级
二、Egg.js的特点
提供基于Egg定制上层框架的能力
高度可扩展的插件机制(有别于中间件模式)
内置多进程管理
基于koa开发,性能优异
框架稳定,测试覆盖率高
渐进式开发
三、egg.js与koa/express对比
04-02 Egg.js初体验
app是Egg的一个实例
04-03 Node.js中近程
一、nodejs单线程,单进程
二、有时需要子进程中执行某些shell命令
三、进程
1、child_process模块
2、cluster模块
nodejs只能用cpu中的某一个内核,这样会造成极大的浪费
3、master进程与cluster进程的通信
四、const { exec, spawn } = require('child_process');
exec、spawn都是用来创建子进程的。
exec:创建子进程,并且将进程执行的结果缓存起来,之后将缓存的结果返回给回调函数。
spawn:返回的是一个stream流
五、child_process.js
const { exec, spawn } = require('child_process');exec('cat a.js', (error, stdout, stderr) => {});const ls = spawn('ls', ['-a'], { encoding: 'utf8' });ls.stdout.on('data', (data) => {});ls.stderr.on('data', (data) => {});ls.on('close', (code) => {})
六、cluster.js
const cluster = require('cluster');const http = require('http');const os = require('os');const cpus = os.cpus().length;console.log(cpus)if (cluster.isMaster) {console.log('主进程 ${process.pid} 正在运行');// 衍生工作进程for (let index = 0; index < cpus; index++) {cluster.fork();}} else {// 工作进程可以共享任何tcp连接// 这里我们共享的是一个http服务器http.createServer((req, res) => {res.writeHead(200, {'Content-type': 'text/html; charset=utf-8'});res.write('你好');res.end()}).listen(8000)console.log(`工作进程 ${process.id} 已经启动`)}
第05章 Egg.js基础-路由/控制器/服务/模板引擎等
05-01 Egg.js中Controller的使用和单元测试
一、Controller
Controller
中的方法可以是同步的,也可以是异步的。但是egg.js规定Controller
里的方法是异步的。
二、测试文件是以.test.js为后缀的。
三、user.test.js
'use strict';const { app } = require('egg-mock/bootstrap');describe('user test', () => { // arguments:测试用例的名称,回调函数it('user index', () => {return app.httpRequest().get('/user').expect(200).expect('user index')});it('user lists', async () => {await app.httpRequest().get('/user/lists').expect(200).expect('[{"id":123}]')})});
终端执行yarn test
05-03 Egg.js路由中post, put, delete等请求的处理及参数校验
一、参数校验:egg-validator
plugin.js
exports.validate = {enable: true,package: 'egg-validate'}
app/user.js
const rule = {name: { type: 'string' },age: { type: 'number' }}ctx.validate(rule)
05-04 Egg.js中的Service服务和单元测试
test/app/service/user.test.js
'use strict';const { app, assert } = require(''egg-mock/bootstrap');deacribe('service user test', () => {it.only('test detail', async () => {const ctx = app.mockContext();const user = await ctx.service.user.detail(10);assert(user);assert(user.id === 10);})})
05-07 Egg.js中的cookie设置和使用以及如何设置中文cookie
一、Cookie
HTTP请求都是无状态的,但是我们的Web应用通常需要知道发起请求的人是谁。为了解决这个问题,HTTP协议涉及了一个特殊的请求头:Cookie。服务端可以通过响应头(set-cookie)将少量数据响应给客户端,浏览器会遵循协议将数据保存,并在下次请求同一个服务的时候带上(浏览器也会遵循协议,只在访问符合Cookie指定规则的网站时带上对应的Cookie来保证安全性)
1、Cookie是运行在浏览器上的
二、通过ctx.cookies,可以在controller中便捷、安全地设置和读取Cookie
三、egg.js中默认对Cookie进行了集成,封装在ctx上下文。
ctx.cookies
const cookies = ctx.cookies.get('user');ctx.cookies.set('user', JSON.stringify(body));ctx.cookies.set('user', JSON.stringify(body), {maxAge: 1000 * 60 * 10, // 过期时间httpOnly: true, // 只允许服务端操作cookie, document.cookie获取不到cookie的值})
四、egg.js无法直接设置中文cookie。解决方法
1、加密
ctx.cookie.set('zh', '测试', {encrypt: true,});const zh = ctx.cookies.get('zh', {encrypt: true,});
2、base64
app/controller/user.js
encode(str = '') {return new Buffer(str).toString('base64');}decode(str = '') {return new Buffer(str, 'base64').toString();}ctx.cookies.set('base64', this.encode('中文base64'));const base64 = this.decode(ctx.cookies.get('base64'));
05-08 Egg.js中session的配置和使用
一、session
Session的实现是基于Cookie的,默认配置下,用户Session的内容加密后直接存储在Cookie中的一个字段中,用户每次请求我们网站的时候都会带上这个Cookie,我们在服务端解密后使用。
二、Session、Cookie的区别
四、async index() {const session = ctx.session.user;console.log(session);}// 保存sessionctx.session.user = body;
五、session是可以直接支持中文的
六、config.default.js
config.session = {key: 'IMOOC',httpOnly: true, // 实际项目中都是用true,来提高安全性maxAge: 1000 * 5,renew: true,}
2、对session进行扩展
app.js
module.exports = app => {const store = {}app.sessionStore = {async get(key) {console.log('--store--', store);return store[key]}async set(key, value, maxAge) {store[key] = value}async destroy(key) {store[key] = null;}}}
05-09 Egg.js中使用HttpClient请求其他接口
一、User server, Article server, Order server, Other server
二、
contoller/home.js
class HomeController extends Controller {async index() {const { ctx } = this;const res = await ctx.service.user.detail(20);console.log(res);ctx.body = 'hi, egg';}}
contoller/curl.js
class CurlController extends Controller {async curlGet() {const { ctx, app } = thisconst res = await ctx.curl('http://localhost:7001/', {dataType: 'json',})console.log(res);ctx.body = {status: 200,data: res.data,}}}
第06章 Egg.js高阶-插件中间件扩展等
06-01 中间件
一、中间件是按照顺序,由外而内,一层层地执行,并且每个中间件都会执行2次。
实际项目中,我们一般使用中间件对请求进行拦截。
二、app/middleware
m1.js
module.exports = options => {return async(ctx, next) => {console.log('m1 start');await next();console.log('m1 end');}}
m2.js
module.exports = options => {return async(ctx, next) => {console.log('m2 start');await next();console.log('m2 end');}}
httpLog.js
const dayjs = require('dayjs');const fs = require('fs'); // 引入文件处理模块module.exports = options => {console.log(options) // config.default.js中的 config.httpLog对象就是它的参数return async (ctx, next) {const sTime = Date.now();const startTime = dayjs( Date.now()).format('YYYY-MM-DD HH:mm:ss');await next(); // 如果没有这行,浏览器会报错:404 not foundconst log = {method: req.method,url: req.url,data: req.body,startTime,endTime: dayjs( Date.now()).format('YYYY-MM-DD HH:mm:ss'),timeLength: Date.now() - sTime}// console.log(log)const data = dayjs( Date.now()).format('YYYY-MM-DD HH:mm:ss') + ' [httpLog] ' + JSON.stringify(log) + '\r\n';fs.appendFileSync(ctx.app.baseDir + 'httpLog.log', data)}}
三、config.default.js
// config.middleware = ['m1', 'm2'];config.middleware = ['httpLog'];config.httpLog = { // 中间件的参数type: 'all',}
06-02 丰富的扩展方式
一、扩展方式
二、扩展一般放在app/extend文件夹下
三、对application的扩展有2个,1是方法层面的扩展,2是属性的扩展
四、app/extend/application.js
const path = require('path');module.exports = {// 方法扩展package(key) {const pack = getPack();return key ? pack[key] : pack;}// 属性扩展get allPackage() {return getPack();}}function getPack() {const filePath = path.join(process.cwd(), 'package.json');const pack = require(filePath);return pack;}
app/controller/home.js
class HomeController extends Controller {async newApplication() {const { ctx, app } = this;const packageInfo = app.package('scripts');const allPack = app.allPackage;ctx.body = 'newApplication';}// 对context上下文进行扩展async newContext() {const { ctx } = this;const params = ctx.params();ctx.body = 'newContext';}async newRequest() {const { ctx } = this;const token = ctx.request.tokenctx.body = token;}async newResponse() {const { ctx } = this;ctx.response.token = 'abc123';const base64Parse = ctx.helper.base64Encode('newResponse');ctx.body = base64Parse;}}
app/extend/context.js
获取get/post的参数是使用不同的方式,现在我们希望用的是同一种方式
context.js
module.exports = {params(key) {const method = this.request.method;if (method === 'GET') {return key ? this.query[key] : this.query;} else {return key ? this.request.body[key] : this.request.body;}}}
app/extend/request.js
一般情况下,对request, response的扩展一般都是对属性的扩展
module.exports = {// 获取相关的tokenget token() {console.log('header', this.header);return this.get('token');}}
app/extend/response.js
module.exports = {// 希望能设置相关的tokenset token(token) {this.set('token', token);}}
app/extend/helper.js
module.exports = {base64Encode(str = '') {return new Buffer(str).toString('base64');}}
06-04 插件机制
一、插件
1、中间件更适合处理请求,插件不仅可以包含中间件所有功能,还可以处理业务逻辑。
2、Egg.js中的插件相当于一个微型应用。
3、插件不包括router.js和controller控制器(可能会与主项目中的路由产生冲突)
二、项目目录下新建lib文件夹,插件都会放在lib文件夹下的plugin文件夹下,plugin里的文件一般以egg-开头
lib/plugin/egg-auth/app/package.json
{"name": "egg-auth","eggPlugin": {"name": "auth"}}
lib/plugin/egg-auth/app/middleware/auth.js
module.exports = options => {return async (ctx, next) => {const url = ctx.request.url;const user = ctx.session.user;if (!user && !options.exclude.includes(ctx.request.url.split('?')[0])) {ctx.body = {status: 1001,errMsg: '用户未登录',}} else {await next();}}}
config/plugin.js
这里用的是本地的插件,不能用pacakge属性,package属性一般是指线上安装的依赖包
path与package属性是互斥的。
exports.auth = {enable: true,path: path.join(__dirname, '../lib/plugin/egg-auth')}
app.js
module.exports = app => {app.config.corMiddleware.push('auth');}
config/config.default.js
config.auth = {exclude: ['/home', '/user', '/login', '/logout']}
06-05 Egg.js定时任务
一、定时任务
1、定时上报应用状态,便于系统监控
2、定时从远程接口更新数据
3、定时处理文件(清除过期日志文件)
二、
lib/plugin/egg-info/app/extend
三、app/schedule文件夹下存放的都是定时任务
app/schedule/get_info.js
require('egg').Subscription;class getInfo extends Subscription {static get schedule() {return {interval: 3000,cron: '*/3 * * * *', // 每隔3秒钟type: 'worker' // 类型:'all' 、 ’worker‘, all:每个worker进程都会执行这个定时任务, worker:master进程会指定一个进程,来单独执行这个任务}}async subscribe() {const info = this.ctx.info;console.log(Date.now(), info)}}module.exports = getInfo;
第07章 Egg.js操作Mysql数据库
07-01 安装Mysql数据库
一、
show database;
create database egg;
二、可视化数据图工具
mysql workbench
07-02 mysql入门,基础增删改查操作
一、show databases;
二、demo.sql
-- 删除数据库drop database egg;-- 创建数据库create database egg;-- 创建表use egg;create table user(id int(10) not null auto_increment,name varchar(20) not null default 'admin' comment '用户名',pwd varchar(50) not null comment '密码',primary key(id))engine=InnoDB charset=utf8;-- 查看表show tables;-- 查看表结构desc user;-- 删除表drop table user;-- 插入表数据insert into user values(1, 'user1', '123');insert into user(name, pwd) values('user2', '123');-- 查询表数据select * from user;select id, name from user;select id, name from user where id = 1;-- 修改表数据update user set pwd = '123456' where id = 1;-- 删除表数据delete from user where id = 2;
07-03 使用egg-mysql操作数据库
一、
yarn add egg-mysqlyarn dev
config/plugin.js
exports.mysql = {enable: true,package: 'egg-mysql'}
config/config.default.js
config.mysql = {app: true, // 是否将mysql挂载到app下agent: false,client: {host: '127.0.0.1',port: '3306',user: 'root',password: 'abc123456',database: 'egg',}}
二、egg-mysql的使用方式比较简单,比较适合中小型项目
07-04 Egg.js中使用Sequelize操作mysql
一、sequelize是一个crm框架
二、
yarn add egg-sequelize mysql2yarn dev
exports.sequelize = {enable: true,package: 'egg-sequelize'}
config.sequelize = {dialect: 'mysql', // 数据源host: '127.0.0.1',port: '3306',user: 'root',password: 'abc123456',database: 'egg',define: {timestamps: false, // 在使用sequelize时,不需要sequelize这个框架为我们自动添加时间相关的字段freezeTabelName: true, // 冻结表名称,使用sequelize的时候,使用原始的表名称,而不需要sequelize框架额外地处理表名称}}
三、app/model,里面是我们的模型文件
app/model/user.js
module.exports = app => {const { STRING, INTEGER } = app.Sequelize;const User = app.model.define('user', { // 模型名称,一般是表名称, 'user'id: { type: INTEGER, primaryKey: true, autoIncrement: true },name: STRING(20),pwd: STRING(50)}); }
app/controller/user.js
const res = await ctx.model.User.findAll({// where: {// id: 2,// }limit: 1,offset: 1})
const res = await ctx.model.User.findByPk(ctx.query.id);
// 更新之前判断数据是否存在const user = await ctx.model.User.findByPk(ctx.request.body.id);if (!user) {ctx.body = {status: 404,errMsg: 'id不存在',}return;}const res = user.update(ctx.request.body);ctx.body = {status: 200,data: res,}
第08章 前端界面开发及功能优化
08-01 本章概览
一、章节目标
1、完成前端界面开发
2、实现列表滚动加载、图片懒加载效果
3、使用mock数据模拟接口(umijs)
二、系统模块
三、技术要点
1、IntersectionObserver,元素是否进入到可视区域
四、学习收获
1、可以学习到前端系统的开发流程
2、了解并实现滚动加载和图片懒加载的思路
3、前端项目的优化思路(公共组件、缓存、骨架屏)
08-02 实现网站的底部导航功能
一、vscode用func命令生成新的组件
二、react-icons,是针对react项目封装的icon
yarn add react-icons
// bootstrap的iconsimport { BsHouseDoorFill, BsHouseDoor} from 'react-icons/bs'
08-04 为首页添加数据
一、如果父组件内的子组件没有数据交互,数据请求就放在父组件中
08-06 初识IntersectionObserver,实现UseObserverHook
一、IntersectionObserver提供了一种异步观察目标元素与其祖先元素及顶级文档视窗(viewport)交叉状态的方法。
二、
三、使用这个特性会比较消耗性能,一般我们会在页面初始化的时候观察这个dom节点,离开页面时取消观察
let observer;useEffect(() => {console.log('进入页面');observer = new IntersectionObserver(entries => {console.log(entries); // 重点关注intersectionRadio, isIntersecting属性});observer.observe(document.querySelector('#loading'));return () => {console.log('离开页面')if(observer) {// 解绑元素observer.unobserve(document.querySelector('#loading'));// 停止监听observer.disconnect();}}}, [])
08-07 使用useObserverHook实现滚动加载(上)
一、
/*** 1、监听loading是否展示出来(loading:请求的节点)* 2、修改分页数据* 3、监听分页数据的修改,发送接口,请求下一页的数据* 4、监听loading变化,拼装数据(loading:数据是否变化的状态)*/useObserverHook('#loading'), (entries) => {}, null)
08-08 使用useObserverHook实现滚动加载(下)
一、获取url参数,umi
import { useLocation } from 'umi'const { query } = useLocation()body = {code: query?.code}
08-09 使用useImgHook实现图片懒加载
一、useImageHook.js
/*** 1、监听图片是否进入可视区域* 2、将src属性的值替换为真是的图片地址,data-src* 3、停止监听当前的节点*/const useImgHook(ele, callback, watch = []) => {}
const dataSrc = item.target.getAttribute('data-src');item.target.setAttribute('src', dataSrc);observer.unobserve(item.target);
08-10 优化-提取公共组件,使用枚举,引入project-libs
一、id,loading在search页面是唯一的
二、antd-mobile的日历组件的svg标签中的元素有id是loading,所以search页面的loading这个id得改一下。
三、enums/common.js,可导出一些常量
export const LOADING_ID = 'mk-loading';
enums/index.js
import * as CommonEnum from './common';export {CommonEnum}
使用
import { CommonEnum } from '@/enums'
四、工具函数库
project-libs(文档:https://cpagejs.github.io/project-libs/)
06-11 民宿详情页面开发
一、banner滑动
react-awesome-swiper
06-12 为民宿详情页添加数据流管理(上)
一、不同组件之间的交互,useHttpHook就不适用了。
二、数据流:think-react-store
effects:异步方法,可以在其中发送请求
三、滚动加载
search页面:useHttpHook方式
民宿详情页:数据流方式
/*** 1、监听loading是否展示出来* 2、触发reload, 修改分页* 3、监听reload变化,重新请求接口* 4、拼装数据*/
08-13 为民宿详情页添加数据流管理(下)
一、
import { history } from 'umi';history.push({pathname: '',query: {id: '',}})
import { useLocation } from 'umi';const { query } = useLocation();
08-14 为订单页面添加滚动加载效果(使用onObserverHook,但不监听数据)
一、不用进行数据监听,
/*** 1、页面初始化时候请求接口,useEffect* 2、监听loading组件是否展示出来,useObserverHook* 3、修改page, pageNum+1,再次重新请求接口* 4、拼装数据,然后page*/
08-19 通过umi运行时配置,对页面进行登录验证
一、cookie
1、右上角登录/注册,如果已经登录了,就显示用户名。
2、点击“我的”,如果未登录,则跳转到登录页面
umi运行时配置
src/appp.js:可以修改路由,复写render
import { cookie } from 'project-libs';import { history } from 'umi';// 初始加载,路由切换的时候进行响应的逻辑export function onRouteChange(route) {const nowPath = routes.routes[0].routes.filter(item => item.path === route.location.pathname);const isLogin = cookie.get('user');if(nowPath.length === 1 && nowPath[0].auth && !isLogin) {history.push({pathname: '/login',query: {from: route.location.pathname}})}}
08-20 使用React.memo减少渲染次数
一、
header组件多次渲染,用Memo
import { memo } from 'react';function areEqual(prevProps, nextProps) {if (prevProps.citys === nextProps.citys && prevProps.cityLoading) {return true; // 允许组件重新渲染} else {return false;}}export default memo(Search, areEqual);
08-21 优化-订单页面添加骨架屏
一、思路
1、通过伪元素实现骨架样式
(1)用伪元素是因为骨架屏只展示区块,区块不包含文字、图片
2、制作布局组件,添加骨架样式
3、替换默认Loading效果
二、这章是针对单独的页面写单独的骨架屏的
三、、global.css
.skeletons {position: relative;display: block;overflow: hidden;width: 100%;min-height: 20px;background-color: #ededed;}.skeletons:empty::after {display: block;content: ' ';position: absolute;width: 100%;height: 100%;transform: translateX(-100%);background: linear-gradient(90deg, transparent, rgba(216, 216, 216, 0.6), transparent);animation: loading 1.5s infinite;}@keyframes loading { /* 骨架屏动画*/from {left: -100%;}to {left: 120%;}}
四、src/skeletons
src/skeletons/OrderSkeletons
第09章 服务端用户模块实现及优化
09-01 本章概览
一、后端
二、章节目标
完成用户模块的接口开发
使用JWT技术验证用户:用户信息加密,生成字符串,之后对字符串解密
提取公共逻辑,优化系统
三、技术要点
redis主要保存核心数据
mysql主要保存业务数据
四、学习收获
1、学习如何开发登录、注册接口以及注意事项
2、学习到如何使用JWT技术进行用户验证
3、如何根据项目需求进行优化(框架扩展、中间件、公共类)
09-02 创建用户表,建基于Seuelize编写用户模型
一、创建数据库
app.sql
create database egg_house;use egg_house;-- 用户表create table `user`(`id` int not null auto_increment,`username` varchar(20) default null comment '密码',`createTime` timestamp default null comment '创建时间',primary key(`id`))engine=InnoDB auto_increment=1 default charset=utf8 comment=‘用户表’;
二、
app/model/user.js
module.exports = app => {const { STRING, INTEGER, TEXT, DATE } = app.Sequelize;const User = app.modeldefine('user', {id: { type: INTEGER, primaryKey: true, autoIncrement: true },username; STRING(20),createTime: DATE,});return User;}
09-03 开发用户注册接口
一、配置项
config/config.default.js
const userConfig = {salt: 'muke'}
app/controller/user.js
const { app } = this;const salt = app.config.salt
09-04 扩展Egg.js的帮助函数
一、存在的问题
1、返回给前端的数据,有些是不需要展示的(如密码)
2、dayjs多处使用,推荐将dayjs写成eggjs中的帮助函数
二、app/extend/helper.js
time() {return dayjs().format('YYYY-MM-DD HH:mm:ss')}
ctx.helper.time();
三、返回的数据都有dataValue
ctx.session.userId = user.id;ctx.body = {status: 200,data: {...ctx.helper.unPick(user.dataValues, ['password']),createTime: ctx.helper.timestamp(user.createTime);}}
09-06 什么是JWT技术
一、JWT全称JSON Web Tokens,是一种规范化的token。它里面包含用户信息,具有验证用户身份、方式CSRF攻击等优点。
二、jwt官网:https://jwt.io/introduction
三、JWT结构
四、JWT使用
09-07 使用JWT改造注册和登录接口,并联调登录注册接口
一、
yarn add egg-jwt
cofig/中对插件进行配置
config.jwt = {}
09-09 优化用户登录验证插件
一、lib/plugin/egg-auth/app/middleware/auth.js
实际项目中,一般不会将缓存放在session中,因为如果服务重启,session会丢失,如果有多台服务,会导致session不一致。
09-10 redis的简单使用,将用户基础信息存储在redis中
一、redis可以将缓存与业务解耦
二、Redis是一个基于内存的高性能key-value数据库。具有存储速度快、支持丰富的数据类型、过期后自动删除等特点。被广泛地应用于企业级项目。
二、安装
brew install redis
启动,开机的时候同时进行启动
brew services start redis
进入redis终端
redis-cli
设置值
set id 1
查看
get id
设置过期时间
expire id 3 // id的过期时间为3秒
三、/usr/local/etc里有配置文件redis.conf
搜素requirepass,requirepass可以用来设置redis密码
四、egg.js连接redis
yarn add egg-redis
五、app/controller/user.js
await app.redis.set(username, 1, 'EX', 5);
六、插件、中间件如果想获取app的实例,用ctx.app就可以了
lib/plugin/egg-auth/app/middleware/auth.js
const user = await ctx.app.redis.get(ctx.username);
09-11 将公共逻辑写在BaseController和BaseService里面,并开发新的插件
一、缓存中是否有用户名,而用户名是从前端传过来的token中解析出来的。重新登录,后端会重新生成token,
二、egg.js提供了中间件的多种使用方式
三、app/router.js
const userExist = app.middleware.userExist(); // userExist中间件可以不在config.default.js中配置,即不应用到整个系统中,而是直接应用于某些接口router.post('/api/user/detail', userExist, contoller.user.detail);
四、优化
1、用户注册登录后,将token保存在redis中
2、改造了登录验证插件,之前登录验证是没有对新旧token进行比较的
3、移除密码、时间处理,封装成共用的方法
4、使用了之前公用的方法promise
5、创建了公用的BaseController, BaseService
第10章 服务端民宿&订购模块实现及优化
10-01 创建民宿、评论表以及编写Sequelize模型
一、server/app.sql
-- 民宿表create table `house`(`id` int not null auto_increment,`name` varchar(50) default null comment '房屋名称') engine=InnoDB auto_increment=1 default charset=utf8 comment='房屋表';--图片表create table `imgs`(`id` int not null auto_increment,`url` varchar(500) default null comment '图片地址',`houseId` int not null comment '房屋id',`createTime` timestamp default null comment '创建事件',primary key(`id`)) engine=InnoDB auto_increment=1 default charset=utf8 comment='图片表';
1、通过设置houseId,将民宿表与图片表关联(外链?)
10-02 开发egg-notFound插件,处理接口不存在问题
一、<ErrorBoundary>
推荐在需要的页面使用,而不是直接写在layouts/index.js中
二、插件,lib/plugin
1、egg-auth,判断用户是否存在
egg-notFound,判断接口是否存在
2、egg-notFound需要放在egg-auth前面
app.config.coreMiddleware.push('notFound');
10-03 获取城市列表数据,基于Sequelize多表联查编写热门民宿接口
一、城市接口,采用第三方接口
const result = await app.httpClient.request()
二、service/house.js
await ctx.model.House.findAll({limit: 4,order: [ // 排序['showCount', 'DESC'],],attributes: {exclude: ['startTime', 'endTime', 'publishTime'], // 去掉数据库中某些字段,不在接口中返回给前端}})
三、多表关联
新的特性:associate
include属性
10-04 编写搜索民宿,与前端联调
一、
10-05 编写民宿详情接口,与前端联调
一、model中,通过设置get方法修改获取到的值,如转换成时间戳
publishTime: {type: DATE,get() {return new Date(this.getDataValue('publishTime')).getTime()}}
10-06 编写添加评论和评论列表接口,与前端联调
createTime: ctx.helper.time()
10-11
第11章 项目安全
11-01 xss常见攻击方法与解决思路
一、web应用中存在的安全风险
1、篡改网页内容
2、窃取网站内部数据
3、网页中植入恶意代码,使用户利益得到侵害
11-02 CSRF常见攻击方式与解决思路(开发allow-hosts插件)
一、开发新的插件,对所有接口进行拦截
11-03 如何对接口限流,避免无限制请求(开发egg-interfaceLimit插件)
一、思路
/****3秒内最多允许3个接口请求* 1、设置计数器,每次请求加1,保存起始时间* 2、超过3秒,计数器大于3,则提示请求频繁;计数器清零,起始时间修改为当前时间* 3、超过3秒,计数器小于3,计数器清零,起始时间修改为当前时间*/
11-04 接口缓存问题处理(开发egg-interfaceCache插件)
一、缓存接口思路
/** 缓存接口* 1、接口地址作为redis中的key* 2、查询redis, 有缓存、返回返回接口* 3、没有缓存,将接口返回结果保存到redis中*/
二、缓存的接口:比如用户详情接口
第12章 项目部署
12-01 Docker简介
一、为什么需要Docker
1、开发环境不一致:开发系统不一致、本地开发环境和线上环境不同
线上环境一般是linux系统
2、软件安装麻烦:安装不同软件的复杂程度不同,不仅耗时久还容易出错。
3、运维成本过高:软件维护和升级都比较费时费力,如果新增机器,所有软件都需要重新安装
二、docker官网:/get-docker/
12-02 Docker基础入门
一、Docker操作
1、镜像操作:拉取、查看、删除等
2、容器操作:运行、查看、进入、删除等
二、Docker engine
1、docker引擎的镜像:https://register.docker-,默认是国外的,可以设置成这个国内的
https://hub.daocloud.io,国内的镜像
2、docker pull daocloud.io/library/node:12.18
docker images
docker tag 28faf336034d node
docker tag 28faf336034d node1:v1.0
3、导出镜像
mkdir docker
cd docker
ls
docker save -o node.image 28faf336034d
4、删除镜像
ls
docker rmi 28faf336034d -f
5、导入镜像
docker load -i node.image
三、本课程mysql版本:8.0.20
1、启动并运行一个镜像:run
2、docker run -d -p 3307:3306 --name mysql -e MYSQL_ROOT_PASSWORD=abc123456 be0dbf01a0f3(3306:当前启动的容器的端口)
3、docker ps
4、docker ps -a // 查看所有当前运行的/停止的镜像
5、docker stop a24c8a967dd6
6、docker rm a24c8a967dd6
7、docker exec -it ecf7f372b176 sh // 进入容器内部
mysql -uroot -p
show database
exit // 退出mysql
exit // 退出mysql容器
8、docker restart ecf7f372b176
12-03 阿里云后台介绍
一、云服务器ecs
公网ip:暴露给第三方
私有ip:当前实例中使用的,外部人员是访问不到的
二、ssh root@47.108.197.103 // 跟着的是公网ip,进入阿里云后台
三、pwd,当前路径
12-04 项目部署
一、阿里云环境下安装docker
centos
二、用yum安装docker
1、yum install yum-utils device-mapper-persistent-data lvm2
2、y
3、设置阿里的镜像源
yum-config-manager --add-repo /docker-ce/linux/centos/docker-ce.repo
4、y
5、y
6、启动docker
start docker
7、开机启动
enable docker
8、docker -v
9、修改镜像源
vi /etc/docker/daemon.json
{"registry-mirrors": ["https://register.docker-/"]}
systemct1 daemon-reload
systemct1 restart docker
三、安装镜像
1、mysql:docker pull daocloud.io/library/mysql:8.0.20
2、redis
四、本地nginx里的内容复制到阿里云,scp
scp -rp nginx root@47.108.197.103:/root
五、server/Dockerfile
# 使用node镜像FROM daocloud.io/library/node:12.18# 在容器中新建目录文件夹 eggRUN mkdir -p /egg# 将/egg设置为默认工作目录WORKDIR /egg# 将package.json 复制默认工作目录COPY package.json /egg/package.json# 安装依赖RUN yarn config set register https://registry.RUN yarn --production# 再copy代码至容器COPY ./ /egg# 7001端口EXPOSE 7001# 等容器启动之后执行脚本CMD yarn start
六、daemon可以让服务在后台运行
七、解压 unzip
unzip -u -d server egg.zip
八、docker中mysql的权限配置
1、docker exec -it fb2520649292 sh
2、mysql -uroot -p
3、远程连接授权
GRANT ALL PRIVILEGES ON.TO ‘root’@’%’ WITH GRANT OPTION
4、刷新权限
FLUSH PRIVILEGES;
5、更改加密规则
ALTER USER ‘root’@‘localhost’ IDENTIFIED BY ‘password’ PASSWORD EXPIRE NEVER;
6、更新root用户密码
ALTER USER ‘root’@’%’ IDENTIFIED WITH mysql_native_password BY ‘abc123456’;
7、刷新权限
FLUSH PRIVILEGES;
九、docker build -t egg:v1.0 ./server
docker run -d -p 7001:7001 --name server cf0aef86ed0e
docker logs -f abbfa1822b05 // abbfa1822b05是port
12-07 课程总结
一、课程主线
前端:React.js Umijs think-react-store
后端:Egg.js Mysql Redis Docker
二、分页,多表联查 egg-sequelize
【笔记-node】《Egg.js框架入门与实战》 《用 React+React Hook+Egg 造轮子 全栈开发旅游电商应用》