文档(前端): 设计模式

This commit is contained in:
haishan 2021-09-28 19:06:54 +08:00
parent 7014364e28
commit bf66328cd7
2 changed files with 261 additions and 69 deletions

View File

@ -15,31 +15,115 @@ group:
将一个类的接口转化为另外一个接口,以满足用户需求,使类之间接口不兼容问题通过适配器得以解决。
案例:新来的大卫封装了个 **fetch** 请求库,但是公司以前的网络请求方法是基于 **XMLHttpRequest**老板想大卫去改这已经写好的9999个接口的请求大卫使用了适配器模式去兼容如下
```js
import react from 'react';
// RequestUtil 请求库
export default class RequestUtil {
// get 方法
static get(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then(response => response.json())
.then(result => {
resolve(result)
})
.catch(err => reject(err))
})
}
class Adapter {
constructor(config) {
if (!config || typeof config === 'object') {
return this.reLoad(config)
} else {
throw new Error('传递的参数必须为一个对象!')
}
// post 方法
static post(url, data) {
return new Promise((resolve, reject) => {
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json'
},
body: data
})
.then(response => response.json())
.then(result => resolve(result))
.catch(err => reject(err))
})
}
}
// 调用
const postResp = await RequestUtils.post('https://nicecoders.github.io', data)
// 公司老的库
function Ajax(type, url, data, success, failed){
// 创建ajax对象
var xhr = null;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
reLoad(arr) {
let newObj = {}
for (let i = 0; i < arr.length; i++) {
const item = arr[i]
newObj[item.name] = item.age
...(此处省略一系列的业务逻辑细节)
var type = type.toUpperCase();
// 识别请求类型
if(type == 'GET'){
if(data){
xhr.open('GET', url + '?' + data, true); //如果有数据就拼接
}
// 发送get请求
xhr.send();
} else if(type == 'POST'){
xhr.open('POST', url, true);
// 如果需要像 html 表单那样 POST 数据,使用 setRequestHeader() 来添加 http 头。
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// 发送post请求
xhr.send(data);
}
// 处理返回数据
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
success(xhr.responseText);
} else {
if(failed){
failed(xhr.status);
}
}
}
return newObj
}
}
let aer = new Adapter([{name: 'david', age: 2}])
// => {david: 2}
// 创建适配器函数,入参与旧接口保持一致
async function AjaxAdapter(type, url, data, success, failed) {
const type = type.toUpperCase()
let result
try {
// 实际的请求全部由新接口发起
if(type === 'GET') {
result = await HttpUtils.get(url) || {}
} else if(type === 'POST') {
result = await HttpUtils.post(url, data) || {}
}
// 假设请求成功对应的状态码是1
result.statusCode === 1 && success ? success(result) : failed(result.statusCode)
} catch(error) {
// 捕捉网络错误
if(failed){
failed(error.statusCode);
}
}
}
// 兼容了老的调用方式
Ajax('get', 'https://nicecoders.github.io', data, function(res){
// 成功的回调逻辑
}, function(error){
// 失败的回调逻辑
})
```
### 装饰器模式
@ -52,6 +136,7 @@ let aer = new Adapter([{name: 'david', age: 2}])
```jsx
import React, { useRef, useState, useEffect } from 'react';
import '@nicecode/css'
class Modal {
constructor(opt = {}) {
@ -120,72 +205,178 @@ export default () => {
normalModal()
}, [])
let style = {
margin: '0 6px',
}
return (
<div className="decorator">
<button onClick={openModal} >打开弹框</button>
<button onClick={hideModal} >关闭弹框</button>
<button onClick={decoratorModal} >添加适配器</button>
<button onClick={normalModal} >清除适配器</button>
<button style={style} onClick={openModal} >打开弹框</button>
<button style={style} onClick={hideModal} >关闭弹框</button>
<button style={style} onClick={decoratorModal} >添加适配器</button>
<button style={style} onClick={normalModal} >清除适配器</button>
<div ref={modalRef} style={{ display: 'none', marginTop: '20px', padding: '10px 20px', border: '1px solid #eee'}} ></div>
</div>
)
};
```
#### 进阶版
```jsx
import React, { useRef, useEffect } from 'react';
function funcDecorator(type) {
return function (target, name, descriptor) {
if (type === 'class') {
target.prototype.show = () => {
console.log('装饰器处理后的类')
}
return target
/**
* or
* return class NButton {
* show() {
* console.log('装饰器处理后')
* }
* }
**/
} else if (type === 'function') {
const old = descriptor.value
descriptor.value = function(...arg) { // 注意这里需要保留原this作用域不能使用箭头函数
console.log('----装饰器装饰函数----')
// 原函数
return old.apply(this, arg)
}
}
}
}
// 通过装饰器改变原有的 show 方法
// @funcDecorator('class')
class Button {
show() {
console.log('大卫的思想空间')
}
@funcDecorator('function')
mb() {
console.log('我是sb')
}
}
export default () => {
useEffect(() => {
let dom = new Button();
// dom.show()
// dom.mb()
// console.log(dom)
}, [])
return (
<div>
进阶案例:控制台查看输出结果
</div>
)
}
```
## 代理模式
### 代理模式
是为一个对象提供一个代用品或占位符,以便控制对它的访问
### ES5 版
#### 简单
```js
/**
* 代理模式
*/
export default class ProxyPro {
constructor(args) {
return new Proxy(args, {
get: function (target, key, receiver) {
// console.log(target, key, receiver);
console.log(`获取 ${key}`)
return Reflect.get(target, key, receiver)
},
set: function (target, key, value, reciver) {
console.log(`设置 ${key} 为 ${value}`)
return Reflect.set(target, key, value, reciver)
}
})
```jsx
import React, { useRef, useEffect } from 'react';
// 普通私密信息
const baseInfo = ['name', 'age', 'career']
// 最私密信息
const privateInfo = ['avatar', 'phone']
// 规定礼物的数据结构由type和value组成
const present = {
type: '巧克力',
value: 60,
}
// 相亲男方
const user = {
isValidated: true,
isVIP: false,
}
// 相亲女方
const girl = {
// 姓名
name: '小美',
// 自我介绍
aboutMe: '...',
// 年龄
age: 24,
// 职业
career: 'teacher',
// 假头像
fakeAvatar: 'xxxx',
// 真实头像
avatar: 'xxxx',
// 手机号
phone: 123456,
// 礼物数组
presents: [],
// 拒收50块以下的礼物
bottomValue: 50,
// 记录最近一次收到的礼物
lastPresent: present,
}
// 掘金婚介所推出了小礼物功能
const JuejinLovers = new Proxy(girl, {
get: function(girl, key) {
if((baseInfo.indexOf(key) !== -1) && !user.isValidated) {
alert('您还没有完成验证哦')
return
}
}
```
#### ES5 版
// 此处我们认为只有验证过的用户才可以购买VIP
if(user.isValidated && privateInfo.indexOf(key) !== -1 && !user.isVIP) {
alert('只有VIP才可以查看该信息哦')
return
}
```js
let Flower = function() {}
let xiaoming = {
sendFlower: function(target) {
let flower = new Flower()
target.receiveFlower(flower)
}
}
let B = {
receiveFlower: function(flower) {
A.listenGoodMood(function() {
A.receiveFlower(flower)
})
}
}
let A = {
receiveFlower: function(flower) {
console.log('收到花'+ flower)
return girl[key]
},
listenGoodMood: function(fn) {
setTimeout(function() {
fn()
}, 1000)
set: function(girl, key, val) {
// 最近一次送来的礼物会尝试赋值给lastPresent字段
// 需要返回 boolean 判断是否赋值成功
if(key === 'lastPresent') {
if(val.value < girl.bottomValue) {
alert('sorry您的礼物被拒收了')
return false
}
// 如果没有拒收则赋值成功同时并入presents数组
girl.lastPresent = val
girl.presents = [...girl.presents, val]
return true
}
}
})
export default () => {
useEffect(() => {
console.log(JuejinLovers.name)
JuejinLovers.lastPresent = present
console.log(JuejinLovers)
}, [])
return (
<div>hi</div>
)
}
xiaoming.sendFlower(B)
```

View File

@ -13,7 +13,7 @@
"test": "umi-test",
"test:coverage": "umi-test --coverage",
"cz": "git add . && git cz",
"log": "conventional-changelog -n ./packages/changelog/src -i changelog.md -s -r 0"
"log": "conventional-changelog -n node_modules/@nicecode/changelog -i CHANGELOG.md -s -r 0"
},
"main": "dist/index.js",
"module": "dist/index.esm.js",
@ -26,7 +26,7 @@
},
"config": {
"commitizen": {
"path": "./packages/commit"
"path": "@nicecode/commit"
}
},
"lint-staged": {
@ -38,6 +38,7 @@
]
},
"dependencies": {
"@nicecode/css": "^0.0.8",
"react": "^16.12.0"
},
"devDependencies": {