20210226-20210227:《Egg.js框架入门与实战》

课程地址:https://www.imooc.com/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、https://eggjs.org/zh-cn/intro/quickstart.html
二、建议使用node的LTS版本
LTS:长期稳定版本
current:含目前nodejs最新的特性,相对而言没那么稳定
三、
1、脚手架生成项目

mkdir egg-demo && cd egg-demo
npm 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 } = this
console.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 } = this
console.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 } = this
console.log(ctx.params)
ctx.body = `id == ctx.params.id`

六、DELETE
路由:http://127.0.0.1:7000/product/delete/100

const { ctx } = this
console.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/cli
vue --version
vue --help
vue create client
mkdir client & cd client
npm 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('编程必备', 'https://img2.mukewang.com/szimg/5d1032ab08719e0906000338.jpg', '介绍编程必备基础知识', '快速、系统补足您所需要的知识内容', '2019-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:开启虚拟服务器,让虚拟服务器去请求代理服务器,这样就相当于是两台服务器之间进行数据的交互,就不用担心跨域的问题。

20210228~20210707:《用 React+React Hook+Egg 造轮子 全栈开发旅游电商应用》

课程地址:https://coding.imooc.com/class/452.html
(aSuncat-20210707: 前后端涉及的内容比较多,可以多听几遍)

第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对比

特性对比 Egg.js Express/Koa
代码的规范性(是否提供明确的MVC架构开发模式) 三层架构:Controller/Service/View,具有明确的开发和命名规范 可以灵活编写代码,没有明确规范
学习成本
插件机制/框架扩展机制 无(有中间件机制)
多进程管理 无(实现:引用第三方插件,或自己手动封装)
HttpClient集成 无(实现:引用第三方插件资源)

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的区别

异同 Cookie Session
存储数据 是,存储在浏览器 是,存储在浏览器、内存或redis等其他数据源中
操作环境 客户端和服务端均可操作 服务端操作session
大小限制 有大小限制并且不同浏览器存储cookie的个数也有不同 没有大小限制单核服务器的内存大小有关;但如果session保存到cookie里会受到浏览器限制
是否唯一 是,不同用户cookie存放在各自的客户端 是,服务端会给每个用户创建唯一的session对象
安全问题 有安全隐患,通过拦截本地文件找到cookie后可以进行攻击 安全性高,浏览器可以获取session但难以解析
常用场景 判断用户是否登录 保存用户登录信息
四、
async index() {const session = ctx.session.user;console.log(session);
}
// 保存session
ctx.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 丰富的扩展方式

一、扩展方式

扩展点 说明 this指向 使用方式
application 全局应用对象 app对象 this.app
context 请求上下文 ctx对象 this.ctx
request 请求级别的对象,提供了请求相关的属性和方法 ctx.request对象 this.ctx.request
response 请求级别的对象,提供了响应相关的属性和方法 ctx.response对象 this.ctx.response
helper 帮助函数 ctx.helper对象 this.ctx.helper

二、扩展一般放在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的icons
import { 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结构

头部(header) 载荷(Payload) 签名(Signature)
头部包含了两部分,采用的签名算法和token类型 载荷用来存放信息,data(用户数据),exp(过期时间),iat(签发时间) header和payload编码后的字符串拼接后以及secret(密钥)进行加密,得到签名
{ “alg”: “HS256”, “typ”: “JWT” } { “data”: “xxx”, “iat”: 1441593502, “exp”: 1441594722, } HMAC-SHA256(encodeBase64Url(header) + “.” + encodeBase64Url(payload), secret)

四、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官网:https://docs.docker.com/get-docker/

12-02 Docker基础入门

一、Docker操作
1、镜像操作:拉取、查看、删除等
2、容器操作:运行、查看、进入、删除等
二、Docker engine
1、docker引擎的镜像:https://register.docker-cn.com,默认是国外的,可以设置成这个国内的
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 http://mirrors.aliyun.com/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-cn.com/"]
}

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
# 在容器中新建目录文件夹 egg
RUN mkdir -p /egg
# 将/egg设置为默认工作目录
WORKDIR /egg
# 将package.json 复制默认工作目录
COPY package.json /egg/package.json
# 安装依赖
RUN yarn config set register https://registry.npm.taobao.org
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 造轮子 全栈开发旅游电商应用》相关推荐

  1. 【笔记-node】《imooc-nodejs入门到企业web开发中的应用》

    目录 课程名 备注 入门必学 nodejs入门到企业web开发中的应用 框架与工具 node.js+koa2+mysql打造前后端分离精品项目<旧岛> 项目实战 20190317-2020 ...

  2. Flask学习 黑马程序员-6节课入门Flask框架web开发视频(中途撤退,寻找py3教程)

    文章目录 postman工具 get和post 如何给路由传参 解析 @app.route('')这个叫视图函数 Jinja2模板引擎 动态传参 注释.变量代码块以及控制代码块的使用 注释:ctrl+ ...

  3. 无闪烁刷新页面 php,AJAX_Ajax实现无闪烁定时刷新页面实例代码,在Web开发中我们经常需要实现 - phpStudy...

    Ajax实现无闪烁定时刷新页面实例代码 在Web开发中我们经常需要实现定时刷新某个页面: 1.来保持session的值或者检查session的值是否为空(比如说防止同一用户重复登录): 2.实现实时站 ...

  4. Web开发中的弹出对话框控件介绍

    Web开发中,目前由于Jquery的大行其道,因此很多弹出对话框,都用到了Jquery技术,反而原始的弹出对话框的方式较为少用了.不过基于JQuery的方式实现对话框窗口弹出,也有很多控件可以利用,由 ...

  5. 038——VUE中组件之WEB开发中组件使用场景与定义组件的方式

    <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8" ...

  6. 谈谈WEB开发中的苦大难字符集问题

    记得刚做javaweb开发的时候被这个编码问题搞得晕头转向,经常稀里糊涂的编码正常了一会编码又乱了.那个时候迫于项目进度大多都是知其然不知其所以然.后来有时间就把整个体系搞了个遍,终于摸通了来龙去脉. ...

  7. Web开发中的相对路径和绝对路径

    在学习HTML的时候一定会遇到引入文件和链接跳转页面,比如:JS文件.CSS文件.Image图片.我们就会考虑是相对路径和绝对路径的问题.下面PHP程序员雷雪松就详细讲解下Web开发中的相对路径和绝对 ...

  8. web开发中目录路径问题的解决

    web开发中目录路径问题的解决 参考文章: (1)web开发中目录路径问题的解决 (2)https://www.cnblogs.com/freeweb/p/4751403.html 备忘一下.

  9. java sessionstate_在Java Web开发中自定义Session

    Session在存储安全性要求较高的会话信息方面是必不可少的,对于分布式Web应用自定义Session支持独立的状态服务器或集群是必须的.本文就来教大家如何在Java Web开发中自定义Session ...

  10. web开发中的缓存问题的研究(一)

    web开发中的缓存问题的研究(一) web开发中的缓存问题的研究(二) web开发中的缓存问题的研究(三) 一般情况下,浏览器都会缓存已经访问过的页面内容,关于如何禁止浏览器缓存的介绍,在网上到处都有 ...

最新文章

  1. AlphaGo之后,DeepMind重磅推出AlphaFold:基因序列预测蛋白质结构
  2. 移动项目开发笔记(.Net下的观察者(Observer)模式)
  3. 当网站遇到黑链时该如何进行处理?
  4. python读取excelsheet-一文看懂用Python读取Excel数据
  5. python 3d绘图 拖动_使用python-matplotlib连续3D绘图(即图形更新)?
  6. 需求分析报告和需求规格说明书有什么区别
  7. Invalid ADAPTORNAME specified. Type 'imaqhwinfo' for a list of available ADAPTORNAMEs. Image acquis
  8. (3)redis.config配置文件详解
  9. python vscode下载和安装教程(windows)
  10. 使用影子系统后win10导致的蓝屏解决办法
  11. 估计很多人不知道:在PowerPoint中插入图片的三种方式用法和解析
  12. IDM+百度网盘提速下载复活版
  13. Testin云测试平台
  14. iOS 10 相关问题
  15. 彻底修改浏览器ua——通过修改navigator.platform将电脑PC的ua修改为手机ua
  16. 直播源代码图片验证码
  17. 5y计算机应用选择题答案,2016年电大网考计算机应用基础统考试题模拟真题及答案 含小抄复习资料推荐.docx...
  18. Ubuntu是现代(堂)吉诃德的痴想吗?
  19. flickr_logos_27_dataset下载
  20. 小程序怎么处理轮训ajax,微信小程序中使用Promise进行异步流程处理的实例详解...

热门文章

  1. 真假三菱PLC如何辨别
  2. WINDOWS 屏幕测试 RGB_TEST
  3. 外卖webapp1---express
  4. Python基础必掌握的语法结构详解
  5. 2023年湖北住建厅八大员各岗位题库精准小题库-启程别
  6. 架构之家 2017年元旦祝福和17年分享规划
  7. 对接多个短信平台,该如何快速完成
  8. 求人贴:自动驾驶仿真软件研发、开发工程师
  9. 从游戏大神到IT技术大神的转变
  10. js中字符串和int类型相互转化