跳到主要内容位置

这14种UML图,你知道几个?

这14种UML图,你知道几个?#

1 什么是UML#

UML-Unified Modeling Language 统一建模语言,又称标准建模语言。是用来对软件密集系统进行可视化建模的一种语言。UML的定义包括UML语义和UML表示法两个元素。

2 为什么要用UML#

UML的目标是以面向对象图的方式来描述任何类型的系统,具有很宽的应用领域。

在一个软件系统开始写代码之前,使用UML建模,可以使我们对这个系统的架构有更为清晰的了解,降低系统开发的风险,好处大大的。

在软件系统开发期间,亦可以使用UML。

3 UML图具体有哪些#

UML分为结构图与行为图两大类。

结构图包括:类图、对象图、包图、组合结构图、构件图、部署图、制品图;

行为图包括:用例图、顺序图、通信图、定时图、状态图、活动图、交互概念图;

3.1 ★类图#

类图是面向对象方法的核心建模工具。

3.1.1 类的表示方法#

如上所示,Employee 为类名,nameageaddress 为属性,work()为方法。我们还看到前面有-+ 这个的意思是:

+:表示public
-:表示private
:表示protected

属性的完整表示方式是: 可见性 名称 :类型 [ = 缺省值]

方法的完整表示方式是: 可见性 名称(参数列表) [ : 返回类型]

注意:

1,中括号中的内容表示是可选的

2,也有将类型放在变量名前面,返回值类型放在方法名前面

举个例子

上图Demo类定义了三个方法:

method()方法:修饰符为public,没有参数,没有返回值。

method1()方法:修饰符为private,没有参数,返回值类型为String。

method2()方法:修饰符为protected,接收两个参数,第一个参数类型为int,第二个参数类型为String,返回值类型是int。

3.1.2 常见的类之间的关系#

3.1.2.1 依赖关系#

一个事物发生变化影响另一个事物。

耦合度最弱的一种关系,在代码中为某个类的方法通过局部变量、方法临时调用一下别的类(依赖类)的方法,来完成一些功能。

下图所示,Driver司机类调用Car类的move()方法来开车,也就是临时用一下车。

3.1.2.2 泛化关系#

特殊/一般关系。是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系。

子类继承了父类的所有属性及方法。

如下图所示,Student 类和 Teacher 类都是 Person 类的子类。

3.1.2.3 关联关系#

描述了一组链,链是对象之间的连接。是一种拥有关系,它使得一个类知道另一个类的属性和方法。

  • 单向关联

下图表示每个顾客都有一个地址,这通过让Customer类持有一个类型为Address的成员变量类实现。

  • 双向关联

从下图中我们很容易看出,所谓的双向关联就是双方各自持有对方类型的成员变量。

  • 自关联

3.1.2.4 聚合关系#

整体与部分生命周期不同。最弱的耦合关系,比如A聚合B,A和B分开后,B的生命周期依然在。

如下图所示,学校与老师的关系,学校包含老师,但如果学校停办了,老师依然存在。

3.1.2.5 组合关系#

组合关系比聚合耦合要强,A与B组合,A没了,那对应的B也没了。

如下图所示,头和嘴的关系,没有了头,嘴也就不存在了。

3.1.2.6 实现关系#

接口与类之间的关系。

在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。

如下图所示,汽车和船是交通工具的泛化,汽车和船实现了交通工具。

3.2 ★对象图#

对象图是类图的一个实例,是系统在某个时间点的详细状态的快照,描述一组对象及它们之间的关系。

也就是说将类图整合在了一起使用。

下面是个例子:

3.3 包图#

3.4 组合结构图#

待更新。

3.5 构件图(组件图)#

待更新。

3.6 部署图#

待更新。

3.7 制品图#

待更新。


3.8 ★用例图(序列图)#

待更新。

3.9 顺序图#

待更新。

3.10 通信图#

待更新。

3.11 定时图#

待更新。

3.12 状态图(状态机图)#

待更新。

3.13 活动图#

待更新。

3.14 交互概念图#

待更新。

4 参考#

5 小结#

类图、对象图、用例图重要些,多练多应用实际。

ts绕开属性检查的3种方法

ts 绕开属性检查的 3 种方法#

引言#

不知道大家有没有遇到这种情况,当我们预先定义了 ts 的一些类型后,在我们真正用到时却又和原先约定的类型定义不一样,哎?那有时候我们有不想或者因为因为一些情况不好去改原来已经定义过的类型定义,这又该怎么办呢? 不要着急,ts 为我们提供了 3 中解决方案,

请看下面:

示例代码:

interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string, area: number } {
return {
color: config.color,
area: config.width
}
}
let mySquare = createSquare({ color: 'red', width: 100 })
console.log('mySquare--->', mySquare)

如上所示,声明一个 createSquare 函数,形参类型是 SquareConfig 接口,传入的形参是{ color: "red", width: 100 },好,这个时候是符合我们原先的 ts 类型定义的,但是当我们把入参改为{ color: "red", width12: 100 },这个时候 ts 就会判断出入参传入有误,嘿,还智能的提示一下写 width12 是不是想传 width 这个变量

image-20230301172449195

可是,如果我们真的是需要第二参数不同,那该如何嘞,请看下面的 3 种解决方式:

1 类型断言#

最简便的方法,用 as 告诉 ts 这就是我想要的,这个类型是对的,好的,那么 ts 就不会报错

interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
return {
color: config.color,
area: config.width,
}
}
let mySquare = createSquare({ color: "red", opacity: 0.5 } as SquareConfig); // 这里声明了{ color: "red", opacity: 0.5 } 就是SquareConfig类型
console.log('mySquare--->', mySquare)

2 添加一个字符串索引签名#

最佳方式

interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
return {
color: config.color,
area: config.width,
}
}
let mySquare = createSquare({ color: "red", opacity: 0.5 }); //这里依然不会报错
console.log('mySquare--->', mySquare)

3 对象赋值转接一手#

interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
return {
color: config.color,
area: config.width,
}
}
let squareOptions = { color: "red", opacity: 0.5 } // 用squareOptions变量来转接一下
let mySquare = createSquare(squareOptions); //这里依然不会报错
console.log('mySquare--->', mySquare)

参考:

https://typescript.bootcss.com/interfaces.html

如何使用react-router实现一个路由权限判断

前言

这是一篇关于react-router的通篇全解的文章,本文旨在阅读完本文可以对react-router有一个系统的了解——emmm原来的思路是打算这么写的,但是近日阅读了一个大佬的文章,发现最好还是学以致用,并且融入自己的思想,所以决定前面介绍react-router的一些常规知识(1-3大点),后面第4大点会写一个实例,用react-router写一个路由鉴权。第4大点最为重要,如果对react-router有些许了解的也可以直接进入第4大点。

首先,有一说一,最详细的教程还是:官网 。下面的介绍将是融入个人理解的白话文:

1 相关理解#

1.1 SPA的理解#

什么是spa?英文全拼single page web application,中文单页面Web应用。

通俗的说,点击页面中的链接不会刷新页面(浏览器左上角的那个小圆环不会转),只会做页面的局部更新,那这就是个单页面web应用。比如我们用的<a></a>标签,里面加个href='https://www.zhangqiang.hk.cn/' 属性,当我们点击那个a标签,此时页面跳转了网页,左上角那个小圆圈呼溜溜的转了,那这就不是个单页面web应用。

而ajax异步请求、react-router都是可以实现spa的、

1.2 路由的理解#

1.2.1 什么是路由?#

  • 一个路由就是一个映射关系(key:value)
  • key为路径, value可能是function或component

1.2.2 路由分类#

后端路由:

理解: value是function, 用来处理客户端提交的请求。 注册路由: router.get(path, function(req, res)) 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据

前端路由:

浏览器端路由,value是component,用于展示页面内容。 注册路由: <Route path="/test" component={Test}> 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件

react-router-dom的理解:

  • react的一个插件库。
  • 专门用来实现一个SPA应用。
  • 基于react的项目基本都会用到此库。

2 react-router-v5#

2.1 react-router-dom相关API#

2.1.1 内置组件#

<BrowserRouter>
<HashRouter>
<Route>
<Redirect>
<Link>
<NavLink>
<Switch>

2.1.2 其它#

  • history对象

  • match对象

  • withRouter函数

2.2 基本路由使用#

2.2.1 安装react-router-dom#

npm install --save react-router-dom

2.2.2 嵌套路由使用#

2.2.3 向路由组件传递参数数据#

2.2.4 多种路由跳转方式#

3 react-router-v6#

4 实例-react-router实现前端路由鉴权#

相关参考:

​ 使用React-Router实现前端路由鉴权:https://juejin.cn/post/6854573217445740557#comment

一文学会ajax基础使用与使用nodejs搭建一个后端服务

写在前面#

简介#

AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。

AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。

XMLHttpRequest 只是实现 Ajax 的一种方式。

上面是来自菜鸟教程的简介,用白话文概括呢,ajax 是 javascript 里面内置的一种异步方法实现方式,用 XMLHttpRequest 对象可以来实现这种异步方式。目前我们主流的前端接口请求方式fetch、axios 都是基于 ajax 封装的,所以了解 ajax 的使用对于解读 fetch 与 axios 的源码也是必不可少的。

In short,学习 ajax 的使用是很重要的。

0 准备工作#

首先,执行个小目标,我们在本地先快速起一个后端服务,使前端可以调到这个接口,很简单,请看下面的操作:

打开我们的 vscode(如果你使用的是 webstorm 也是一样的操作,不过很推荐 vscode 呀,干净简洁很喜欢~),执行npm init初始化一下 npm 配置(在这之前要确保电脑上已经安装 node.js,可以在终端输入 node -v 查看,有版本号说明已经安装):

0.1 npm 初始化

  • node -v 有版本号显示说明已经安装 node.js

image.png

  • 执行 npm init,一路回车就完事了,然后我们可以看到所在文件夹里面会有个 package.json 文件,说明 npm init 初始化成功。之后安装的 npm 包版本都可以在 package.json 里面查看到。

image-20230113150934180

0.2 安装 express

在终端执行npm install express,安装 express,这是一个库可以让我们用 node.js 启动一个后端服务器,当这个后端服务启动的时候,我们就可以通过前端代码请求它暴露出来的接口访问到相应的后端服务。

0.2.1 如果安装成功的小伙伴不用看这个小点,我在安装的时候遇到了这个报错,分析一下是因为 npm 的源是国外的原因,所以我将其设置成了淘宝镜像

image-20230113152632762

设置淘宝镜像命令,在终端执行(下面图片里面有):

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

0.2.1 安装成功

image-20230113152715760

顺便再装个库,npm install body-parser 后面会有用,针对 post 请求的

image-20230113215801561

0.2.2 启动 node 后端服务

我们创建一个文件夹 src,在 src 下面建个 server.js 的文件,然后再粘贴下方的代码到 server.js 文件里面

//1. 引入express
const express = require('express');
//2. 创建应用对象
const app = express();
//3. 创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
app.get('/server', (request, response) => {
   //设置响应头 设置允许跨域
   response.setHeader('Access-Control-Allow-Origin', '*');
   //设置响应体 这里是返回给前端的内容
   response.send('哈哈哈,接口请求成功,这一串文字是接口返回的数据~~');
});
//4. 监听端口启动服务
app.listen(8000, () => {
   console.log("服务已经启动, 8000 端口监听中....");
});

image-20230113153544950

终端进入 src 这个文件夹,然后执行node server.js,启动后端服务

image-20230113154009446

如上图,我们已经在本地打开了 8000 端口,同时写了个/server get 类型的接口地址,我们可以直接通过浏览器来访问http://127.0.0.1:8000/server测试一下接口,如果我们看到下图这样,说明后端服务开启成功~

image-20230113154625311

ok,后端服务启动完毕!~ 还是很厉害的嘛,我们进入下个流程~~ 前端写 ajax 请求。

1 写个简单的 ajax 请求#

1.1 get 请求 与 post 请求#

1.1.1 前端代码#

<!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>ajax使用</title>
   <style>
       .mainContainer {
           font-size: 20px;
      }
   </style>
</head>
<body>
   <div class="mainContainer">
       <div>右键打开控制台查看ajax网络请求</div>
       <button>点击发送GET请求</button>
       <button>02点击发送带参数的GET请求</button>
       <button>03点击发送带参数的POST请求</button>
   </div>
   <script>
       //获取button元素
       const btn = document.getElementsByTagName('button')[0]
       //绑定事件
       btn.onclick = function () {
           //1. 创建对象
           const xhr = new XMLHttpRequest()
           //2. 初始化 设置请求方法和 url
           xhr.open('GET', 'http://127.0.0.1:8000/server')
           //3. 发送
           xhr.send()
           //4. 事件绑定 处理服务端返回的结果
           // on when 当....时候
           // readystate 是 xhr 对象中的属性, 表示状态 0 1 2 3 4
           // change 改变
           xhr.onreadystatechange = function () {
               //判断 (服务端返回了所有的结果)
               if (xhr.readyState === 4) {
                   //判断响应状态码 200 404 403 401 500
                   // 2xx 成功
                   if (xhr.status >= 200 && xhr.status < 300) {
                       //处理结果 行 头 空行 体
                       //响应
                       // console.log(xhr.status);//状态码
                       // console.log(xhr.statusText);//状态字符串
                       // console.log(xhr.getAllResponseHeaders());//所有响应头
                       // console.log(xhr.response);//响应体
                       //设置 result 的文本
                       console.log(`${btn.textContent}返回回来的数据:`, xhr.response)
                  } else {
                       console.log('接口请求失败', btn.textContent)
                  }
              }
          }
      }
       // 02点击发送带参数的GET请求
       const btn2 = document.getElementsByTagName('button')[1];
       //绑定事件
       btn2.onclick = function () {
           //1. 创建对象
           const xhr = new XMLHttpRequest()
           //2. 初始化 设置请求方法和 url
           xhr.open('GET', 'http://127.0.0.1:8000/server/getAndValue?a=100&b=200&c=300');
           //3. 发送
           xhr.send()
           //4. 事件绑定 处理服务端返回的结果
           xhr.onreadystatechange = function () {
               if (xhr.readyState === 4) {
                   if (xhr.status >= 200 && xhr.status < 300) {
                       console.log(`${btn2.textContent}返回回来的数据:`, xhr.response)
                  } else {
                       console.log('接口请求失败', btn2.textContent)
                  }
              }
          }
      }
       // 03点击发送带参数的post请求
       const btn3 = document.getElementsByTagName('button')[2];
       //绑定事件
       btn3.onclick = function () {
           //1. 创建对象
           const xhr = new XMLHttpRequest()
           //2. 初始化 设置请求方法和 url
           xhr.open('POST', 'http://127.0.0.1:8000/server/postAndValue');
           //设置请求头 post请求要设置请求头,以form表单的形式传参
           xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
           //3. 发送
           xhr.send('a=100&b=200&c=30000000000111')
           //4. 事件绑定 处理服务端返回的结果
           xhr.onreadystatechange = function () {
               if (xhr.readyState === 4) {
                   if (xhr.status >= 200 && xhr.status < 300) {
                       console.log(`${btn3.textContent}返回回来的数据:`, xhr.response)
                  } else {
                       console.log('接口请求失败', btn3.textContent)
                  }
              }
          }
      }
   </script>
</body>
</html>

1.1.2 后端代码#

//1. 引入express
const express = require('express');
const bodyParser = require('body-parser'); // 使用express中间件,以解决post请求后端获取不到值问题
//2. 创建应用对象
const app = express();
// 下面这两句话是针对post请求加的, req.body是解析json的结果,一定加上这么2句,否则post请求获取不到req.body的  
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
//3. 创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
app.get('/server', (request, response) => {
   //设置响应头 设置允许跨域
   response.setHeader('Access-Control-Allow-Origin', '*');
   //设置响应体 这里是返回给前端的内容
   response.send('哈哈哈,接口请求成功,这一串文字是接口返回的数据~~');
});
// 3.2 get带参数的接口
app.get('/server/getAndValue', (request, response) => {
   //设置响应头 设置允许跨域
   response.setHeader('Access-Control-Allow-Origin', '*');
   // response.setHeader('Access-Control-Allow-Headers', '*');
   //设置响应体 这里是返回给前端的内容
   let result = request.query;
   if (result.a !== undefined) {
       result.a = result.a + 10000
  }
   response.send(result);
});
// 3.2 get带参数的接口
app.get('/server/getAndValue', (request, response) => {
   //设置响应头 设置允许跨域
   response.setHeader('Access-Control-Allow-Origin', '*');
   // response.setHeader('Access-Control-Allow-Headers', '*');
   //设置响应体 这里是返回给前端的内容
   let result = request.query;
   if (result.a !== undefined) {
       result.a = result.a + 10000
  }
   response.send(result);
});
// 3.2 post带参数的接口
app.all('/server/postAndValue', (request, response) => {
   //设置响应头 设置允许跨域
   response.setHeader('Access-Control-Allow-Origin', '*');
   response.setHeader('Access-Control-Allow-Headers', '*');
   // response.header('Access-Control-Allow-Headers', 'Content-Type')
   //设置响应体 这里是返回给前端的内容
   let result = request.body;
   console.log('postAndValue--->', result)
   response.send(request.body);
});
//4. 监听端口启动服务
app.listen(8000, () => {
   console.log("服务已经启动, 8000 端口监听中....");
});

1.2.3 运行结果#

image-20230113220628033

2 相关参考手册#

2.1 XMLHttpRequest 对象的方法#

方法描述
abort()取消当前请求
getAllResponseHeaders()返回头部信息
getResponseHeader()返回特定的头部信息
open(method, url, async, user, psw)规定请求 method:请求类型 GET 或 POSTurl:文件位置 async:true(异步)或 false(同步)user:可选的用户名称 psw:可选的密码
send()将请求发送到服务器,用于 GET 请求
send(string)将请求发送到服务器,用于 POST 请求
setRequestHeader()设置请求头,向要发送的报头添加标签/值对

2.2 XMLHttpRequest 对象的属性#

属性描述
onreadystatechange定义当 readyState 属性发生变化时被调用的函数
readyState保存 XMLHttpRequest 的状态。0:请求未初始化 1:服务器连接已建立 2:请求已收到 3:正在处理请求 4:请求已完成且响应已就绪
responseText以字符串返回响应数据
responseXML以 XML 数据返回响应数据
response以原格式返回响应数据,可以理解为后端返回的数据格式是啥那就是啥
status返回请求的状态号 200: "OK"403: "Forbidden"404: "Not Found"
statusText返回状态文本(比如 "OK" 或 "Not Found")

2.3 http 状态码#

1xx: 信息#

消息:描述:
100 Continue服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。
101 Switching Protocols服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。

2xx: 成功#

消息:描述:
200 OK请求成功(其后是对 GET 和 POST 请求的应答文档。)
201 Created请求被创建完成,同时新的资源被创建。
202 Accepted供处理的请求已被接受,但是处理未完成。
203 Non-authoritative Information文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝。
204 No Content没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而 Servlet 可以确定用户文档足够新,这个状态代码是很有用的。
205 Reset Content没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。
206 Partial Content客户发送了一个带有 Range 头的 GET 请求,服务器完成了它。

3xx: 重定向#

消息:描述:
300 Multiple Choices多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。
301 Moved Permanently所请求的页面已经转移至新的 url。
302 Found所请求的页面已经临时转移至新的 url。
303 See Other所请求的页面可在别的 url 下被找到。
304 Not Modified未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(一般是提供 If-Modified-Since 头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
305 Use Proxy客户请求的文档应该通过 Location 头所指明的代理服务器提取。
306 Unused此代码被用于前一版本。目前已不再使用,但是代码依然被保留。
307 Temporary Redirect被请求的页面已经临时移至新的 url。

4xx: 客户端错误#

消息:描述:
400 Bad Request服务器未能理解请求。
401 Unauthorized被请求的页面需要用户名和密码。
402 Payment Required此代码尚无法使用。
403 Forbidden对被请求页面的访问被禁止。
404 Not Found服务器无法找到被请求的页面。
405 Method Not Allowed请求中指定的方法不被允许。
406 Not Acceptable服务器生成的响应无法被客户端所接受。
407 Proxy Authentication Required用户必须首先使用代理服务器进行验证,这样请求才会被处理。
408 Request Timeout请求超出了服务器的等待时间。
409 Conflict由于冲突,请求无法被完成。
410 Gone被请求的页面不可用。
411 Length Required"Content-Length" 未被定义。如果无此内容,服务器不会接受请求。
412 Precondition Failed请求中的前提条件被服务器评估为失败。
413 Request Entity Too Large由于所请求的实体的太大,服务器不会接受请求。
414 Request-url Too Long由于 url 太长,服务器不会接受请求。当 post 请求被转换为带有很长的查询信息的 get 请求时,就会发生这种情况。
415 Unsupported Media Type由于媒介类型不被支持,服务器不会接受请求。
416服务器不能满足客户在请求中指定的 Range 头。
417 Expectation Failed

5xx: 服务器错误#

消息:描述:
500 Internal Server Error请求未完成。服务器遇到不可预知的情况。
501 Not Implemented请求未完成。服务器不支持所请求的功能。
502 Bad Gateway请求未完成。服务器从上游服务器收到一个无效的响应。
503 Service Unavailable请求未完成。服务器临时过载或当机。
504 Gateway Timeout网关超时。
505 HTTP Version Not Supported服务器不支持请求中指明的 HTTP 协议版本。