JS设计模式

news2024/9/29 18:49:22

文章目录

  • 1 什么是设计模式?
  • 2 发布-订阅模式
    • 2.1 DOM事件
    • 2.2 基于Broadcast Channel实现跨页面通信
    • 2.3 基于localStorage实现跨页面通信
    • 2.4 使用 Vue 的 EventBus 进行跨组件通信
    • 2.4 使用 React 的 EventEmitter 进行跨组件通信
  • 3 装饰器模式
    • 3.1 React 高阶组件 HOC
    • 3.2 AOP 面向切面编程
    • 3.3 axios调用时添加token
  • 4 单例模式
    • 4.1 惰性单例
    • 4.2 通用的惰性单例
    • 4.3 Vuex 数据缓存
    • 4.4 antd/message
    • 4.5 axios取消重复请求
  • 5 策略模式
    • 5.1 JavaScript版本的策略模式
    • 5.2 axios
  • 6 迭代器模式
    • 6.1 JS中的迭代器
    • 6.2 根据不同浏览器选择相应的上传组件
  • 7 代理模式
    • 7.1 虚拟代理合并HTTP请求
    • 7.2 缓存代理
    • 7.3 Vue中的代理模式

1 什么是设计模式?

设计模式是针对开发中遇到的问题,提出的公认且有效的解决方案。共23种设计模式。

2 发布-订阅模式

在这里插入图片描述
对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知

消息的发布者将消息通过消息通道广播出去,然后订阅者通过订阅获取到想要的消息。

2.1 DOM事件

funtion func() {
    console.log(2)
}

// 添加订阅者
document.body.addEventListener('click', func, false)

// 删除订阅者
document.body.removeEventListener('click', func)

这里,我们就订阅document.body上的click事件,当body被点击时,body节点便会向订阅者发布这个消息,当然我们还可以随意的添加或者删除订阅者

2.2 基于Broadcast Channel实现跨页面通信

// A页面监听广播
// 第一步 创建实例
const bc = new BroadcastChannel('myBroadcastChannel')
// 第二部 通过onmessage设置回调事件
bc.onmessage = e => {
    console.log(e.data)
}

// B页面发送广播
const bc = new BroadcastChannel('myBroadcastChannel')
bc.postMessage('hollow word')

// 关闭广播
bc.close()

2.3 基于localStorage实现跨页面通信

// http://localhost:8080/A.html
window.addEventListener('storage', e => {
    // e.key 改变的key值 - msg
    // e.oldValue 改变前的值
    // e.newValue 改变后的值 - 哈哈哈
    if (e.key === 'msg') { ... }
})

// http://localhost:8080/B.html
localStorage.setItem('msg', '哈哈哈')

2.4 使用 Vue 的 EventBus 进行跨组件通信

创建一个EventBus (本质上是Vue的一个实例对象)

import Vue from 'vux'
const EventBus = new Vue()
export default EventBus

接着在组件A和组件B中引入bus.js:import Bus from '@/utils/bus',组件A在 mounted 钩子中调用 Bus 的注册订阅方法 $on 传入订阅主题和回调方法,组件B中在点击事件中发布主题,让订阅该主题的组件执行回调方法。

// 组件A
mounted () {
	// 订阅
    Bus.$on('SayHollow', text => {
        console.log(text)
    })
}
// 组件B
methods: {
    clickEvent () {
    	// 发布
        Bus.$emit('SayHollow', '啊俊俊')
    }
}

2.4 使用 React 的 EventEmitter 进行跨组件通信

import React, { Component } from 'react'
//一: 导入EventEmitter
import { EventEmitter } from 'events';

//二: 构建事件实例
const EventBus = EventEmitter()

//tips:我们可以在多个组件中去增加同一个事件的订阅,这里仅仅是示例
class Observer extends Component {
  componentDidMount () {
    // 三: 增加事件订阅
    this.event1 = EventBus.addListener("someEvent", (params) => {
      console.log(params)
    })
  }
  componentWillUnMount () {
    //四: 移除事件订阅
    EventBus.removeListener(this.event1)
  }
  render () {
    return (
      <div>
        事件监听组件
      </div>
    )
  }
}
class Publisher extends Component {
  handleClick () {
    const params = {}
    //五: 发布事件(当someEvent发布时,订阅该事件的函数就会执行)
    EventBus.emit('someEvent', params)
  }
  render () {
    return (
      <div>
        <button onClick={this.handleClick.bind(this)}>发布事件</button>
      </div>
    )
  }
}

3 装饰器模式

装饰器模式动态的给某个对象添加一些职责,并且不会影响从这个类派生的其他对象。

在传统的面向对象开发中,给对象添加功能时,我们通常会采用继承的方式,继承的方式目的是为了复用,但是随之而来也带来一些问题:
(1)父类和子类存在强耦合的关系,当父类改变时,子类也需要改变;
(2)子类需要知道父类中的细节,至少需要知道接口名,从而进行复用或复写,这样其实破坏了封装性;
(3)继承的方式,可能会创建出大量子类。比如现在有BBA三种类型的汽车,构造了一个汽车基类,三个三种类型的汽车。现在需要给汽车装上雾灯、前大灯、导航仪、刮雨器,如果采用继承的方式,那么就要构建3*4个类。但是如果把雾灯、前大灯、导航仪、刮雨器动态地添加到汽车上,那么只需要增加4个类。这种采用动态添加职责的方式就是装饰器。

装饰器的目的就是在不改变原来类的基础上,为其在运行期间动态的添加职责。

提高编程的低耦合高可复用性

3.1 React 高阶组件 HOC

import React from 'react';

const yellowHOC = WrapperComponent => {
  return class extends React.Component {
    render() {
      <div style={{ backgroundColor: 'yellow' }}>
        <WrapperComponent {...this.props} />
      </div>;
    }
  };
};

export default yellowHOC;
import React from 'react';
import yellowHOC from './yellowHOC';

class TargetComponent extends Reac.Compoment {
  render() {
    return <div>66666</div>;
  }
}

export default yellowHOC(TargetComponent);

3.2 AOP 面向切面编程

业务和系统基础功能分离,用 Decorator 很合适

log装饰器实现

export function log(target, name, decriptor) {
  var _origin = decriptor.value;
  decriptor.value = function () {
    console.log(`Calling ${name} with `, arguments);
    return _origin.apply(null, arguments);
  };

  return decriptor;
}

调用装饰器

import { log } from "./log";
class Person {
  @log
  say(nick) {
    return `hi ${nick}`;
  }
}

var person = new Person();
person.say("小明");

3.3 axios调用时添加token

4 单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点

  • 避免重复的创建实例,节约不必要的开销。
  • 通常会在第三方库的开发中使用到。
  • 如果是项目的开发,单例模式也会用在一些数据缓存全局通用弹窗(如登录弹窗)等一些场景中。

4.1 惰性单例

惰性单例指的是在需要的时候才创建对象实例。通常会用在全局唯一且非必需的一些场景,例如:全局弹窗、购物车列表、全局共同信息等场景。

拿书中登录弹窗的例子可以理解的更加清晰:

var createLoginLayer = (function() {
    var div;
    return function() {
        if(!div) {
            div = document.createElement('div');
            div.innerHtml = '登录弹窗';
            div.style.display = 'none';
            document.body.appendChild(div);
        }
        return div;
    }
})()

document.getElementById('loginBtn').onclick = function() {
    var loginLayer = createLoginLayer();
    loginLayer.style.display = 'block';
}

在这个例子中,只有在登录按钮点击时,才会去创建登录弹窗dom节点,而不是在页面加载时就默认创建,并且,只有在第一次执行时创建登录弹窗dom节点,再次执行也不会创建多余的节点,节省了一部分性能。

4.2 通用的惰性单例

上面代码在createLoginLayer方法中,既实现了单例的逻辑,也实现了创建登录弹窗的逻辑。这违反了上节介绍的单一职责原则,如果下次仍然需要创建另一个弹窗或其他的功能,我们仍然需要将创建单例这部分逻辑再次抄一遍。这么一说相信大家也能理解到这段代码需要如何进行拆分了:将不变的部分抽离出来,也就是将要介绍的通用的惰性单例


// 通用的惰性单例
var getSingle = function(fn) {
    var result;
    return function() {
        return result || (resule = fn.apply(this, arguments));
    }
}

// 创建登录弹窗的方法就可以改写成
var createLoginLayer = function() {
    var div = document.createElement('div');
    div.innerHtml = '登录弹窗';
    div.style.display = 'none';
    document.body.appendChild(div);
    return div;
}

var createSingleLoginLayer = getSingle(createLoginLayer);

document.getElementById('loginBtn').onclick = function() {
    var loginLayer = createSingleLoginLayer();
    loginLayer.style.display = 'block';
}

如上面代码,我们将创建登录弹窗和单例的逻辑分离成createLoginLayergetSingle,这样之后再有了类似的需求,就可以复用到getSingle方法实现单例。

4.3 Vuex 数据缓存

// src/store.js

let Vue;

export class Store {
    constructor(options = {}) {
        if (!Vue && typeof window !== 'undefined' && window.Vue) {
          install(window.Vue)
        }
    }
}

export function install (_Vue) {
  if (Vue && _Vue === Vue) {
    if (__DEV__) {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
 
  Vue = _Vue
  applyMixin(Vue)
}

在上面代码中,很明显可以找到一个单例模式的应用:在Store的初始化时,只会执行一次install方法。在install方法中,会将Vue赋值,并将vuex的相关逻辑绑定到Vue实例上。

4.4 antd/message

// components/message

let messageInstance;

function getMessageInstance(callback) {
  if (messageInstance) {
    callback(messageInstance);
    return;
  }
  Notification.newInstance(
    {
      prefixCls,
      transitionName,
      style: { top: defaultTop }, // 覆盖原来的样式
      getContainer,
      maxCount,
    },
    instance => {
      if (messageInstance) {
        callback(messageInstance);
        return;
      }
      messageInstance = instance;
      callback(instance);
    },
  );
}

在上面代码中,我们可以看到当已经存在messageInstance时,会直接复用对应的实例:callback(messageInstance),否则的话,将会赋值messageInstance = instance

先看下面这段代码:

<template>
  <div>
    <a-button @click="handleMessage">message</a-button>
  </div>
</template>

export default {
  methods: {
    handleMessage() {
      this.$message.info('handleMessage');
    },
  },
  created() {
    this.$message.config({
      top: '100px',
      duration: 1,
    });
  },
};

这是一个vue组件,在created时,我们配置了message的参数,当点击按钮时,会执行方法调用message.info方法弹出组件。在message源码中,会通过执行notice方法弹出弹窗,而如果我们是第一次弹出弹窗时:
在这里插入图片描述
会创建一个ant-message节点其中top: 100px就是刚才设置的配置项,当我们再次触发时:
在这里插入图片描述
可以看到会复用刚刚创建的dom节点,并在内部创建一个弹窗。那么这样做有什么好处呢?

  • 最显然的一个优势就是复用了dom节点
  • 还有一个优势,当我们连续多次点击时,可以看到弹窗的效果是按顺序依次显示的,只有一个dom节点可以保证多次弹出的弹窗只有一个父节点,那么弹窗位置只要由父节点控制即可,不需要每次都重新计算,效果如图所示:

在这里插入图片描述

4.5 axios取消重复请求

import axios from "axios";
const CancelToken = axios.CancelToken;
let cancelId = 0;
let cancelArray = [];

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
  // 在请求时,可以添加自己的特点标识去筛选出需要重复取消的接口
  const source = CancelToken.source();
  cancelId++;
  const id = cancelId;
  config.cancelId = id;
  config.cancelToken = source.token;
  const cancelIndex = cancelArray.findIndex(e => e.url === config.url);
  cancelArray.push({
    id,
    url: config.url,
    source
  })
  if (cancelIndex > -1) {
    cancelArray[cancelIndex].source.cancel('取消重复请求');
    cancelArray.splice(cancelIndex, 1)
  }
  return config;
}, function (error) {
  return Promise.reject(error);
});

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
  const cancelIndex = cancelArray.findIndex(e => e.id === response.cancelId);
  if (cancelIndex >= -1) {
    cancelArray.splice(cancelIndex, 1)
  }

  // 对响应数据做点什么
  return response;
}, function (error) {
  if (axios.isCancel(error)) {
    // 如果是取消的接口,可以自行返回一个特定标识
    console.log('isCancel')
  } else {
    // 对响应错误做点什么
    return Promise.reject(error);
  }
});

export default axios

5 策略模式

定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换。

在平时的工作中也存在非常多应用场景,比如业务中经常会存在针对不同场景执行不同逻辑的情况,就可以考虑使用策略模式

5.1 JavaScript版本的策略模式

业务描述:实现一个计算年终奖的功能,绩效为S的人有4倍工资,A为3倍工资,B为2倍工资。

var strategies = {
    'S': function(salary) {
        return salary * 4;
    },
    'A': function(salary) {
        return salary * 3;
    },
    'B': function() {
        return salary * 2;
    }
}

var calculateBouns = function(level, salary) {
    return strategies[level](salary);
}

calculateBouns('A', 2000);

5.2 axios

axios既可用于浏览器中又可用于node环境中,但通过源码可以得知:在不同的环境,将使用不同的方式发起请求

// lib/defaults.js

function getDefaultAdapter() {
  var adapter;
  if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  }
  return adapter;
}

通过这段代码,可以看到在axios中,我们会根据环境赋值不同的adapter,但XHRHttp发送请求的方式并不相同,那么如何保证在不同场景使用方式相同呢?其实,axios会将不同的逻辑在各自内部处理,最终暴露出相同的调用方式,简单看下以下两部分代码:

// lib/adapters/xhr.js

module.exports = function xhrAdapter(config) {
    return new Promise((resolve, reject) => {
        /**
            省略xxxx代码
        */
        var request = new XMLHttpRequest();
        
        /**
            省略xxxx代码
        */
        request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
    })
    
    request.onreadystatechange = function handleLoad() {
        /**
            省略xxxx代码
        */
    
        // 对response进行校验,满足条件则请求成功 resolve(response)
        settle(resolve, reject, response);
    }
}


// lib/adapters/http.js

module.exports = function httpAdapter(config) {
    return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
        var resolve = function resolve(value) {
          resolvePromise(value);
        };
        var reject = function reject(value) {
          rejectPromise(value);
        };
        
        /**
            省略xxxx代码
        */
       var transport;
       // 源码中有不同逻辑的判断,这里简化为其中一种情况
       transport = isHttpsProxy ? https : http;
       
       /**
            省略xxxx代码
        */
       
       var req = transport.request(options, function handleResponse(res) {
           /**
               省略xxxx代码
           */
           res.on('end', function handleStreamEnd() {
              /**
                省略xxxx代码
              */
              settle(resolve, reject, response);
            });
       })
    })
}

两个都返回Promise,在不同的方法中,各自处理了响应逻辑。在于使用时,也就不需要再区分不同的环境了。

6 迭代器模式

提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序返回其中的每个元素。

6.1 JS中的迭代器

在JavaScript中,相信大家经常听到过迭代、循环之类的名词,把这两个概念区分一下:

  • 循环,循环就是在满足一定条件时,重复执行同一段代码,典型的例子:do...while
  • 迭代,迭代是指按顺序逐个访问对象中的每一项,典型的例子:forEach

那么什么样的对象可以被迭代呢?需要满足什么条件呢?

  • 要成为可迭代对象,对象必须要实现必须实现@@iterator方法,通常可以访问常量Symbol.iterator访问该属性。
  • 目前的内置可迭代对象有:StringArrayTypedArrayMapSet,他们的原型对象都实现了@@iterator方法。

当这个对象是可迭代对象时,我们可以通过调用[Symbol.iterator]方法来按顺序遍历对象中的每一项:

const arr = [1, 2, 3, 4];
// 迭代器
const iterator = arr[Symbol.iterator]()
iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }

遍历比如forEachfor...of等方法,其实就是封装了一个遍历可迭代对象的方法,属于内部迭代器。而当我们通过调用next方法自行控制迭代对象遍历时,比如ES6中的生成器函数,这种就属于外部迭代器。

6.2 根据不同浏览器选择相应的上传组件

提供一个可以被迭代的方法,使得getActiveUploadObjgetFlashUploadObjgetFormUploadObj依照优先级被迭代

我们会优先选择控件上传,如果没有安装上传控件则使用Flash上传,如果Flash也没有安装,那就只好使用浏览器原生的表单上传了

// 定义各个上传方法
var getActiveUploadObj = function() {
    try {
        return new ActiveXObject('TXFTNActiveX.FTNUpload');
    } cache(e) {
        return false
    }
}

var getFlashUploadObj = function() {
    if (supportFlash()) {
        var str = '<object type="application/x-shockwave-flash"></object>'
        return $(str).appendTo($('body'));
    }
    return false
}

var getFormUploadObj = function() {
    var str = '<input name="file" type="file" />' // 表单上传
    return $(str).appendTo($('body'));
}

// 按优先级迭代函数
var iteratorUploadObj = function() {
    for (var i = 0, fn; fn = arguments[i++];) {
        var uploadObj = fn();
        if (uploadObj !== false) {
            return uploadObj
        }
    }
}

// 获取可上传upload对象
var uploadObj = iteratorUploadObj(getActiveUploadObj, getFlashUploadObj, getFormUploadObj)

各个上传对象的方法互不干扰,可以很好的维护和扩展代码。

7 代理模式

为一个对象提供一个代用品或占位符,以便控制对它的访问。

代理模式的关键是当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问。客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。

保护代理虚拟代理

  • 代理B可以帮助A过滤掉一些请求,比如送花的人中年龄太大的或者没有宝马的,这种请求就可以直接在代理B处被拒绝掉,这种代理叫做保护代理
  • 假设现实中花的价格不菲,导致在程序世界里,new Flower也是一个代价昂贵的操作,那么我们可以把new Flower的操作交给代理B去执行,代理B会选择在A心情好时在执行new Flower,这种代理叫做虚拟代理
  • 虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建。保护代理用于控制不同权限的对象对目标对象的访问。保护代理用于控制不同权限的对象对目标对象的访问,而虚拟代理是最常用的一种代理模式。

7.1 虚拟代理合并HTTP请求

假设我们在做一个文件同步的功能,当我们选中一个checkbox的时候,它对应的文件就会被同步到另外一台服务器上面:

var synchronousFile = function (id) {
    console.log('开始同步文件', id)
}

var proxySynchronousFile = function() {
    var cache = [],
        timer;
    
    return function(id) {
        cache.push(id);
        if (timer) {
            return;
        }
        
        timer = settimeout(() => {
            synchronousFile(cache.join(','));
            clearTimeout(timer);
            timer = null;
            cache.length = 0; // 清空id集合
        }, 2000)
    }
}()

var checkbox = document.getElementsByTagName('input')

for (var i = 0, c; c = checkbox[i++]) {
    c.onclick = function() {
        if (this.checked === true) {
            proxySynchronousFile(this.id);
        }
    }
}

通过一个代理函数proxySynchronousFile来收集一段时间之内的请求,最后一次性发给服务器,如果不是实时性要求很高的系统,有一点延迟并不会带来太大的副作用,却能大大减轻服务器的压力

7.2 缓存代理

缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前的一致,则可以直接返回前面的存储的运算结果。

var proxyMult = (function() {
    var cache = {};
    return function() {
        var args = Array.prototype.join.call(arguments, ',');
        if (args in cache) {
            return cache[args];
        }
        return cache[args] = mult.apply(this, arguments);
    }
})()

proxyMult(1, 2, 3, 4) // 24
proxyMult(1, 2, 3, 4) // 24

当第二次调用proxyMult(1, 2, 3, 4)时,本体mult函数并没有被计算,proxyMult直接返回了之前计算好的结果。通过增加缓存代理的方式,mult函数可以继续专注于自身的职责——计算乘积,缓存功能是由代理对象实现的。

7.3 Vue中的代理模式

当我们使用组件中的data、props和methods时,只需要调用this.xxx即可,拿initData的部分看下源码中是怎么处理的:

// src/core/instance/state.js

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  if (!isPlainObject(data)) {
    data = {}
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    )
  }
  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) {
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  observe(data, true /* asRootData */)
}

可以看到首先我们设置了vm._data,后面又执行了 proxy(vm, _data, key)vm._data.xxx代理到vm.xxx上,最后通过observe(data, true) 监听data的变化,将data变为是响应式的。

所以,要知道为什么可以直接使用this.xxx调用到组件中的data,只需要了解proxy的实现即可:


const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}

export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

可以看到,通过修改getset方法后,当我们获取vm.xxx时,实际则会取到this[sourceKey][key],也就是vm._data.xxx

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/348839.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySql图形化界面工具--DataGrip安装和使用

一、下载安装 1、官网下载安装包&#xff0c;双击开始安装 2、点击next&#xff0c;一步一步的完成安装 3、选择DataGrip的安装目录&#xff0c;然后选择下一步 4、下一步&#xff0c;执行安装 二、使用 1、添加数据源 配置以及驱动jar包下载完毕之后&#xff0c;…

Pod基本概念与Pod应用生命周期

Pod是一个逻辑抽象概念&#xff0c;kubernetes创建和管理的最小单元&#xff0c;一个Pod由一个容器或多个容器组成。特点&#xff1a;一个Pod可以理解为是一个应用实例&#xff0c;提供服务Pod中容器始终部署在一个Node上Pod中容器共享网络、存储资源Pod主要用法&#xff1a;运…

大件传输的9种方法

不知道你有没有试过用电子邮件进行大文件传输&#xff0c;由于文件大小的限制&#xff0c;往往会发送失败。同时&#xff0c;一些文件共享服务对传输的文件有大小限制&#xff0c;使得你无法与朋友分享电影片段或向客户展示你的工作样本。还有一些要求你注册一个账户&#xff0…

【JVM基础内容速查表】JVM基础知识 默认参数 GC命令 工具使用 JVM参数设置、说明、使用方法、注意事项等(持续更新)

目录一、JVM前置知识1. -X、-XX含义2. JVM参数值的类型和设置方式3. 查看GC时用到的命令和JVM参数4. 查看JVM默认参数二、垃圾收集器选择-XX:UseSerialGC-XX:UseParallelGC-XX:UseParallelOldGC-XX:UseParNewGC-XX:UseConcMarkSweepGC-XX:UseG1GC三、垃圾收集器特有参数1. ParN…

pyinstaller打包遇到的问题

1、ModuleNotFoundError: No module named ‘scipy.spatial.transform_rotaion_groups’ 解决办法&#xff1a;–hidden-import scipy.spatial.transform._rotation_groups 2、FileNotFoundError:[Errno 2] No such file or directory:‘C:\Users\Gw0021\AppData\Local\Temp\_M…

leaflet 选择一个marker,点击后设置其为中心点(070)

第070个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中引用geojson文件,显示marker,点击某个marker后,设置其为中心点,并panTo到中心点。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共81行)相…

【数据库】MySQL 索引视图详解

目录 MySQL索引视图 视图 一&#xff0c;什么是视图 二&#xff0c;为什么需要视图 三&#xff0c;视图的作用和优点 四&#xff0c;创建视图 案例&#xff1a; 五&#xff0c;视图使用规则 六&#xff0c; 修改视图 1&#xff0c;修改列名 2&#xff0c;创建复杂视图…

把盏言欢,款款而谈,当WorkPlus接入了ChatGPT机器人

ChatGPT到底有多火&#xff1f; “谷歌AI聊天机器人出错&#xff0c;市值一夜蒸发7172亿”&#xff1b;“百度类ChatGPT项目‘文心一言’或将直接接入百度搜索”&#xff1b;“阿里确认正研发类ChatGPT产品&#xff0c;目前处于内测阶段”&#xff1b;“网易有道将推出教育场景…

电脑技巧:分享六个小众且非常实用的工具

❤️作者主页&#xff1a;IT技术分享社区 ❤️作者简介&#xff1a;大家好,我是IT技术分享社区的博主&#xff0c;从事C#、Java开发九年&#xff0c;对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️荣誉&#xff1a; CSDN博客专家、数据库优质创作者&#x1f3c6;&…

黑马】后台管理-路由懒加载

当打包构建项目时&#xff0c; JavaScript 包会变得非常大&#xff0c;影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块&#xff0c;然后当路由被访问的时候才加载对应组件&#xff0c;这样就更加高效了。具体需要 3 步&#xff1a;① 安装 babel/plugin-synt…

【Big Data】Hadoop--MapReduce经典题型实战(单词统计+成绩排序+文档倒插序列)

&#x1f34a;本文使用了3个经典案例进行MapReduce实战 &#x1f34a;参考官方源码&#xff0c;代码风格较优雅 &#x1f34a;解析详细 一、Introduction MapReduce是一个分布式运算程序的编程框架&#xff0c;核心功能是将用户写的业务逻辑代码和自身默认代码整合成一个完整的…

震源机制沙滩球(focal mechanism beach ball)绘制之傻瓜式教程

目录 1. 断层的定义 2. 绘制流程 3. 更多示例 沙滩球的含义可阅读&#xff1a;震源机制(Focal Mechanisms)之沙滩球(Beach Ball)_ddd...e_bug的博客-CSDN博客 1. 断层的定义&#xff08;&#xff09; 走向&#xff08;strike&#xff09;&#xff1a;正北方顺时针旋转到走向…

第五十四章 DFS进阶(二)——迭代加深

第五十四章 DFS进阶&#xff08;二&#xff09;——迭代加深一、DFS的缺陷二、迭代加深1、什么是迭代加深2、复杂度分析3、算法步骤三、例题1、问题2、分析3、代码一、DFS的缺陷 我们知道DFS是一条路走到黑&#xff0c;直到将整条路走完以后才会回头。 这就导致了一个问题&am…

dynamic-Echonet左心室语义分割——学习记录

1简单介绍 1.1 论文简介 论文地址Video-based AI for beat-to-beat assessment of cardiac function 数据集地址&#xff1a;here获取好像还挺麻烦的。需要在网站上填写并申请数据集&#xff0c;斯坦福那边会发邮件并拉入一个box组&#xff0c;就可以访问公开的超声心动数据集…

图解LeetCode——剑指 Offer 26. 树的子结构

一、题目 输入两棵二叉树A和B&#xff0c;判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构&#xff0c; 即&#xff1a;A中有出现和B相同的结构和节点值。 二、示例 2.1> 示例 1&#xff1a; 【输入】A [1,2,3], B [3,1] 【输出】false 2.2> 示…

数字化转型的成功模版,珠宝龙头曼卡龙做对了什么?

2月11日&#xff0c;曼卡龙&#xff08;300945.SZ&#xff09;发布2022年业绩快报&#xff0c;报告期内&#xff0c;公司实现营业收入16.11亿元&#xff0c;同比增长28.63%。来源&#xff1a;曼卡龙2022年度业绩快报曼卡龙能在2022年实现营收增长尤为不易。2022年受疫情影响&am…

c语言操作文件

1、文件缓冲区 文件缓冲区的目的&#xff1a;提高访问效率 提高磁盘使用寿命 刷新就是将当前缓冲区数据全部提交。 不刷新时&#xff0c;程序在崩溃时缓冲区内容无法输出&#xff08;有些情形会带来错误&#xff09; 文件缓冲区的四种刷新方式 行刷新&#xff08;遇到换行符…

CSS3新增属性( 过渡、变形和动画)

文章目录一、过渡1、transition-property2、transition-duration3、transition-timing-function4、transition-delay二、变形1、transform2、2D变形2.1、平移&#xff08;translate&#xff09;2.2、缩放&#xff08;scale&#xff09;2.3、倾斜&#xff08;shew&#xff09;2.…

【记录】smartctl|Linux如何通过smartctl查看有没有坏的磁盘?以及使用时长、电源周期、故障记录等

smartctl是一个用于监测和分析硬盘健康状态的工具&#xff0c;可以用于检测是否存在坏的磁盘。以下是使用smartctl检查磁盘健康状态的步骤&#xff1a; 安装smartctl软件 在Linux系统中&#xff0c;smartctl通常包含在smartmontools软件包中。如果您还没有安装smartmontools&am…

Mr. Cappuccino的第38杯咖啡——Kubernetes中Pod、Namespace、Label、Deployment、Service之间的关系

Kubernetes中Pod、Namespace、Label、Deployment、Service之间的关系Pod、Namespace、Label、Deployment、Service之间的关系NamespacePod1. 创建一个namespace并运行一个pod2. 查看pod3. 删除pod4. 删除pod控制器Label1. 创建yaml文件&#xff08;nginx-pod.yaml&#xff09;2…