当前位置: 首页 > 编程日记 > 正文

玩转 JavaScript 面试:何为函数式编程?

函数式编程在 JavaScript 领域着实已经成为一个热门话题。就在几年前,很多 JavaScript 程序员甚至都不知道啥是函数式编程,但是就在近三年里我看到过的每一个大型应用的代码库中都包含了函数式编程思想的大规模使用。

函数式编程(缩写为 FP)是一种通过组合纯函数来构建软件的过程,避免状态共享可变数据副作用的产生。函数式编程是一种声明式编程而不是指令式编程,应用的状态全部流经的是纯函数。与面向对象编程思想形成对比的是,其应用程序的状态通常都是与对象中的方法共享的。

函数式编程是一种编程范式,意指它是一种基于一些基本的、限定原则的软件架构的思维方式,其他编程范式的例子还包括面向对象编程和面向过程编程。

相比指令式编程或面向对象,函数式编程的代码倾向于更为简洁、可预测且更容易测试。但如果你不熟悉这种方式或与其常见的几种相关模式的话,函数式编程的代码同样可以看起来很紧凑,相关文档对于新手来说可能也较为难以理解。

如果你开始去搜索函数式编程的相关术语,你可能很快就会碰壁,大量专业术语完全可以唬住一个新手。单纯的讨论其学习曲线有点儿过于轻描淡写了,但是如果你已经从事 JavaScript 编程工作有一段时间了,那么你应该已经在你的项目中使用过很多函数式编程的思想或工具了。

别让新词汇把你吓跑。它们会比听起来更容易。

这其中最难的部分可以说就是让一堆陌生词汇充斥你的脑袋了。各种术语一脸无辜,因为在掌握它们之前你还需要了解下面这些术语的含义:

  • 纯函数
  • 函数组合
  • 避免状态共享
  • 避免状态改变
  • 避免副作用

一个纯函数定义如下:

  • 每次给定相同的输入,其输出结果总是相同的
  • 无任何副作用

纯函数中的很多特性在函数式编程中都很重要,包括引用透明度(如果表达式可以替换为其相应的值而不更改程序的行为,则该表达式称为引用透明)。

引用透明度说白了就是相同的输入总是得到相同的输出,也就是说函数中未使用任何外部状态:

function plusOne(x) {return x + 1; } 复制代码

上面的例子即为引用透明度函数,我们可以用 6 来代替 plusOne(5) 的函数调用。详细解释可参考 stack overflow - What is referential transparency?

函数组合是指将两个或多个函数进行组合以便产生一个新的函数或执行某些计算的过程。比如组合函数f.g(.的意识是指由...组成)在 JavaScript 中等价于 f(g(x))。理解函数组合对于理解使用函数式编程编写软件来说是个十分重要的步骤。

状态共享

状态共享是指任何变量、对象或内存空间在一个共享的作用域中存在,或者是被用来作为对象的属性在作用域之间传递。一个共享的作用域可以包括全局作用域或者闭包作用域。在面向对象编程中,对象通常都是通过添加一个属性到其他对象中来在作用域间共享的。

状态共享的问题在于为了了解一个函数的作用,你不得不去了解函数中使用的或影响的每一个共享的变量的过往。

假定你有一个用户对象需要保存,你的saveUser()函数会向服务器上的接口发起请求。与此同时,用户又进行了更换头像的操作,调用updateAvatar()来更换头像的同时也会触发另一次saveUser()请求。在保存时,服务器返回一个规范的用户对象,该对象应该替换内存中的任何内容以便与服务器上的更改或响应其他 API 调用同步。

但是问题来了,第二次响应比第一次返回要早。所以当第一个响应(已过期)返回时,新头像被从内存中抹去了,替换回了旧头像。这就是一个争用条件的例子 —— 是与状态共享有关的一个很常见的 bug。

另一个跟状态共享有关的常见问题是更改调用函数的顺序可能会导致级联失败,因为作用于状态共享的函数与时序有关:

// 在状态共享的函数中,函数调用的顺序会导致函数调用的结果的变化
const x = {val: 2
};const x1 = () => x.val += 1; const x2 = () => x.val *= 2; x1(); x2(); console.log(x.val); // 6 // 同样的例子,改变调用顺序 const y = { val: 2 }; const y1 = () => y.val += 1; const y2 = () => y.val *= 2; y2(); y1(); // 改变了结果 console.log(y.val); // 5 复制代码

如果我们避免状态共享,函数调用的时间和顺序就不会改变函数调用的结果。使用纯函数,给定相同的输入总是能得到相同的输出。这就使得函数调用完全独立,就可以从根本上简化改变与重构。函数中的某处改变,或是函数调用的顺序不会影响或破坏程序的其他部分。

const x = {val: 2
};const x1 = x => Object.assign({}, x, { val: x.val + 1}); const x2 = x => Object.assign({}, x, { val: x.val * 2}); console.log(x1(x2(x)).val); // 5 const y = { val: 2 }; // 由于不存在对外部变量的依赖 // 所以我们不需要不同的函数来操作不同的变量 // 此处故意留白 // 因为函数不变,所以我们可以以任意顺序调用这些函数任意次 // 而且还不改变其他函数调用的结果 x2(y); x1(y); console.log(x1(x2(y)).val); // 5 复制代码

在上面的例子中,我们使用了Object.assign()方法,然后传入了一个空对象作为第一个参数来复制x的属性,而不是在原处改变x。该例中,不使用Object.assign()的话,它相当于简单的从头开始创建一个新对象,但这是 JavaScript 中创建现有状态副本而不是使用变换的常见模式,我们在第一个示例中演示了这一点。

如果你仔细的看了本例中的console.log()语句,你应该会注意到我已经提到过的一些东西:函数组合。回忆一下之前说过的知识点,函数组合看起来像这样:f(g(x))。在本例中为x1(x2()),也即x1.x2

当然了,要是你改变了组合顺序,输出也会跟着改变。执行顺序仍然很重要。f(g(x))不总是等价于g(f(x)),但是再也不用担心的一件事就是函数外部的变量,这可是件大事。在非纯函数中,不可能完全知道一个函数都做了什么,除非你知道函数使用或影响的每一个变量的整个历史。

去掉了函数调用的时序依赖,你也就完全排除了这一类 bug。

不可变性

不可变对象是指一个对象一旦被创建就不能再被修改。反之可变对象就是说对象被创建后可以修改。不可变性是函数式编程中的一个核心概念,因为如果没有这个特性,程序中的数据流就会流失、状态历史丢失,然后你的程序中就总会冒出奇怪的 bug。

在 JavaScript 中,千万不要把 const 和不变性搞混。const 绑定了一个创建后就无法再被分配的变量名。const 不创建不可变对象。使用 const 创建的变量无法再被赋值但是可以修改对象的属性。

不可变对象是完全不能被修改的。你可以深度冻结一个对象使其变成真·不可变的值。JavaScript 中有一个方法可以冻结对象的第一层:

const a = Object.freeze({foo: 'Hello',bar: 'world', baz: '!' }); a.foo = 'Goodbye';// Error: Cannot assign to read only property 'foo' of object Object 复制代码

这种冻结方式仅仅是浅层的不可变,例如:

const a = Object.freeze({foo: { greeting: 'Hello' }, bar: 'world', baz: '!' }); a.foo.greeting = 'Goodbye'; console.log(`${ a.foo.greeting }, ${ a.bar }${a.baz}`);// Goodbye world! 复制代码

可以看到,一个被冻结的顶层的原始属性是不可变的。但如果属性值为对象的话,该对象依然可变(包括数组等)。除非你遍历整个对象树,将其层层冻结。

在很多函数式编程语言中都又比较特殊的不可变数据结构,称之为查找树数据结构,这种数据结构是可以有效的进行深度冻结的。

查找树通过结构共享来共享内存空间的引用,其在对象被复制后依然是不变的,从而节省了内存,使得某类操作的性能有显著的提升。

例如,你可以在一个对象树的根节点使用身份对照来进行比较。如果身份相同,如果身份相同,那你就不用去遍历整颗树来对比差异了。

在 JavaScript 中有一些比较优秀的利用树的类库,比如 Immutable.jsMori

这俩库我都用过,我更倾向于在需要很多不可变状态的大型项目中使用 Immutable.js

副作用

副作用就是指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。副作用的函数不仅仅只是返回了一个值,而且还做了其他的事情:

  • 改变了外部对象或变量属性(全局变量或父函数作用域链中的变量)
  • 在控制台中有输出打印
  • 向屏幕中写了东西
  • 向文件中写了东西
  • 向网络中写了东西
  • 触发了外部过程
  • 调用了其他有副作用的函数

副作用在函数式编程中大多数时候都是要避免的,这样才能使得程序的作用一目了然,也更容易被测试。

Haskell 等其他编程语言总是从纯函数中使用 Monads 将副作用独立并封装。有关 Monads 内容太多了,大家可以去了解一下。

但你现在就需要了解的是,副作用行为需要从你的软件中独立出来,这样你的软件就更易扩展、重构、debug、测试和维护。

这也是大多数前端框架鼓励用户单独的管理状态和组件渲染、解耦模块。

通过高阶函数提高复用性

函数式编程倾向于复用一系列函数工具来处理数据。面向对象编程则倾向于将方法和数据放在对象中,这些合并起来的方法只能用来操作那些被设计好的数据,经常还是包含在特定组件实例中的。

在函数式编程中,任何类型的数据都是一样的地位,同一个 map() 函数可以遍历对象、字符串、数字或任何类型的数据,因为它接收一个函数作为参数,而这个函数参数可以恰当的处理给定的数据类型。函数式编程通过高阶函数来实现这种特性。

JavaScript 秉承函数是一等公民的观点,允许我们把函数当数据对待 —— 把函数赋值给变量、将函数传给其他函数、让函数返回函数等...

高阶函数就是指任何可以接收函数作为参数的函数、或者返回一个函数的函数,或者两者同时。高阶函数经常被用于:

  • 抽象或独立的动作、回调函数的异步流控制、promises,、monads 等等...
  • 创建可以处理各种数据类型的实用工具函数
  • 使用函数的部分参数或以复用目的或函数组合创建的柯里化函数
  • 接收一组函数作为参数然后返回其中的一些作为组合

容器、函子、列表、流

函子就是一种可以被映射的东西。换句话说,它就是一个有接口的容器,该接口可以被用来apply到函数内部的一个值(这句翻译太奇怪了,功力不够。原文 it’s a container which has an interface which can be used to apply a function to the values inside it.)。

前面我们知道了相同的 map()函数可以在多种数据类型上执行。它通过提升映射操作以使用函子 API 来实现。关键的流控制操作可以通过 map() 函数利用该接口使用。如果是 Array.prototype.map() 的话,容器就是个数组,其他数据结构可以作为函子,只要它们提供了 map() API。

让我们来看一下 Array.prototype.map() 是如何允许从映射函数中抽象数据类型使得 map() 函数在任何数据类型上可用的。我们创建一个 double() 函数来映射传入的参数乘 2 的操作:

const double = n => n * 2;
const doubleMap = numbers => numbers.map(double); console.log(doubleMap([2, 3, 4])); // [ 4, 6, 8 ] 复制代码

要是我们想对一个游戏中的目标进行操作,让其得分数翻倍呢?只需要在 double() 函数中传入 map() 的值上稍作改动即可:

const double = n => n.points * 2;const doubleMap = numbers => numbers.map(double); console.log(doubleMap([ { name: 'ball', points: 2 }, { name: 'coin', points: 3 }, { name: 'candy', points: 4} ])); // [ 4, 6, 8 ] 复制代码

使用如函子/高阶函数的概念来使用原生工具函数来操作不同的数据类型在函数式编程中很重要。类似的概念被应用在 all sorts of different ways。

列表在时间上的延续即为流。

你现在只需要知道数组和函数不是容器和值在容器中应用的唯一方式。比如说,一个数组就是一组数据。列表在时间上的延续即为流 -- 因此你可以使用同类工具函数来处理进来的事件流 —— 在日后实践函数式编程中你会对此有所体会。

声明式编程 & 指令式编程

函数式编程是一种声明式编程范式,程序的逻辑在表达时没有明确的描述流控制。

指令式编程用一行行代码来描述特定步骤来达到预期结果。而根本不在乎流控制是啥?

声明式编程抽象了流控制过程,用代码来描述数据流该怎么做,如何去获得抽象的方式。

下面的例子中给出了指令式编程映射数组中数字并返回将值乘 2 返回新数组:

const doubleMap = numbers => {const doubled = [];for (let i = 0; i < numbers.length; i++) { doubled.push(numbers[i] * 2); } return doubled; }; console.log(doubleMap([2, 3, 4])); // [4, 6, 8] 复制代码

声明式编程做同样的事,但是使用函数工具 Array.prototype.map() 抽象了流控制的方式,允许你对数据流做更清晰的表达:

const doubleMap = numbers => numbers.map(n => n * 2); console.log(doubleMap([2, 3, 4])); // [4, 6, 8] 复制代码

指令式编程常使用语句,语句即一段执行某个动作的代码,包括forifswitchthrow 等等。

声明式编程的代码更多依赖的是表达式,表达式是一段有返回值的代码。表达式的例子如下:

2 * 2
doubleMap([2, 3, 4]) Math.max(4, 3, 2) 复制代码

你会在代码中经常看见一个表达式被赋给一个变量、从函数中返回一个表达式或是被传入一个函数。

结论

本文要点:

  • 使用纯函数而不是共享状态或者有副作用的函数
  • 发扬不可变性而不是可变数据
  • 使用函数组合而不是指令式的流控制
  • 很多原生、可复用的工具函数可以通过高阶函数应用到很多数据类型上,而不是只能处理指定数据
  • 声明式编程而不是指令式编程(要知道做什么,而不是如何做)
  • 表达式和语句
  • 容器 & 高阶函数对比 特设多态



转载于:https://www.cnblogs.com/dashjunih/p/10988104.html

相关文章:

正则表达式分类 区别

原文地址&#xff1a;http://www.cnblogs.com/chengmo/archive/2010/10/10/1847287.html 正则表达式&#xff1a;在计算机科学中&#xff0c;是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。在很多文本编辑器或其他工具里&#xff0c;正则表达式通常被用…

Java项目:嘟嘟校园一卡通系统(java+JSP+Servlet+html+css+JavaScript+JQuery+Ajax+mysql)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目简述 功能&#xff1a;卡管理&#xff0c;卡消费&#xff0c;卡充值&#xff0c;图书借阅&#xff0c;消费&#xff0c;记录&#xff0c;注销等等功能。 二、项目运行 环境配置&#xff1a; Jdk…

OC与C语言的区别

C语言是面向过程的编程语言&#xff0c;而OC则是面向对象的编程语言。面向对象:打个比方,就是你做一次菜,让老婆做个菜,吃饭,这就是面向对象,效率高面向过程,就是每一个细节:比如你要先把或开到合适的位置.然后还要洗菜 ,等油热了,才能开始炒菜,然后调料,...,起锅,到碗里,吃饭.…

Hutool之集合工具——CollectionUtil

为什么80%的码农都做不了架构师&#xff1f;>>> 集合工具 CollectionUtil 这个工具主要增加了对数组、集合类的操作。 1. join 方法 将集合转换为字符串&#xff0c;这个方法还是挺常用&#xff0c;是StrUtil.split的反方法。这个方法的参数支持各种类型对象的集合…

15:解决IntelliJ IDEA的乱码问题

1. -Dfile-encodingsUTF-8 &#xff0c;全局&#xff1a; 转载于:https://www.cnblogs.com/gzhbk/p/10991335.html

C++ 和C 语言混合代码导致的问题

C语言中操作字符串用C运行时函数&#xff1a;strtok, strcmp, strcpy等等&#xff0c;直接操作内存。在c引入的字符串操作类std:string &#xff0c;string类中必有一个私有成员&#xff0c;其是一个char*&#xff0c;用户记录从堆上分配内存的地址&#xff0c;其在构造时分配内…

SVN详细使用教程

SVN简介&#xff1a; 为什么要使用SVN&#xff1f; 程序员在编写程序的过程中&#xff0c;每个程序员都会生成很多不同的版本&#xff0c;这就需要程序员有效的管理代码&#xff0c;在需要的时候可以迅速&#xff0c;准确取出相应的版本。 Subversion是什么&#xff1f; 它是一…

Java项目:药店信息管理系统(java+SSM+JSP+layui+maven+mysql)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目简述 环境配置&#xff1a; Jdk1.8 Tomcat8.5 mysql Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09; 项目技术&#xff1a; JSP Spring SpringMVC MyBatis ht…

MongoDB简单操作

MongoDB简单操作 Hadoop核心技术厂商Cloudera将在2014/06推出hadoop Ecosystem与MongoDB的整合产品,届时MongoDB与ipmala及hbase,hive一起用; 开源linux领军企业RHEL也宣布RHEL将整合MongoDB用于简化用户账号管理与LDAP一起用; 数据仓库之MPP技术 领军者莫非 Vertica,exterdata…

Windows Presentation Foundation(介绍外连接)

Windows Presentation Foundation 2011/08/12更新&#xff1a;2010 年 12 月 Windows Presentation Foundation (WPF) 为开发人员提供了统一的编程模型&#xff0c;可用于构建合并了 UI、媒体和文档的丰富 Windows 智能客户端用户体验。 欢迎使用 WPF 了解 WPF&#xff1a; WP…

Linux中与进程终止相关的信号SIGTERM,SIGKILL,SIGINT

1. SIGTERM “kill pid” 会发送SIGTERM到进程pid. This is the termination signal sent by killcommand by default. 2. SIGINT 在终端中敲入interrupt key&#xff08;DELETE或ctrlc&#xff09;会产生SIGINT信号。这个信号会被发送到进程(inforeground proc…

Java项目:嘟嘟二手书商城系统(java+JSP+Springboot+maven+mysql+ThymeLeaf+FTP)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目简述 功能&#xff1a; 主页显示商品&#xff1b; 所有二手书商品展示&#xff0c;可进行商品搜索&#xff1b; 点击商品进入商品详情页&#xff0c;具有立即购买和加入购物车功能&#xff0c;可增减…

SQL用法总结

1、创建数据库语句 create table persons(id INT NOT NULL AUTO_INCREMENT,lastname VARCHAR(255) NOT NULL,firstname VARCHAR(255) NOT NULL,PRIMARY KEY (ID)) DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci; 2、创建数据库时&#xff0c;PK、NN、UQ、BIN、UN、…

[leetcode] Binary Tree Preorder Traversal

Given a binary tree, return the preorder traversal of its nodes values. For example:Given binary tree {1,#,2,3}, 1\2/3return [1,2,3]. Note: Recursive solution is trivial, could you do it iteratively? 给定一棵二叉树&#xff0c;使用非递归的方法进行先序遍历。…

error C2065: “M_PI”: 未声明的标识符

1.首先&#xff0c;程序中头文件的选择&#xff0c;要选择<math.h>头文件&#xff0c;在<cmath>文件中是没有对M_PI 的定义的&#xff08;现在的<cmath>中对M_PI好像已有定义&#xff09;。2.选择&#xff1a;项目——>”XXX属性"——>配置属性—…

区分HPUX是Itanium还是PA-RISC

转自&#xff1a;http://blog.csdn.net/nbzll0920/article/details/7961232 pa-risc的产品号以rp打头&#xff0c;itanium的产品号以rx打头 用model或者uname -a命令看一下就知道了 PS: Intel安腾处理器构建在IA-64&#xff08;Intel Architecture 64&#xff09;&#xff0c;…

Java项目:食品溯源系统(java+Springboot+Maven+mybatis+Vue+mysql+wd)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…

使用sqlite保存数据返回主键

/// <summary>/// 返回insert后的主键值/// </summary>/// <param name"SQLString"></param>/// <param name"para"></param>/// <returns></returns>public static int ExecuteSql(string SQLString, Li…

Pycharm初始创建项目和环境搭建(解决aconda库文件引入不全等问题)

1.新建工程 1&#xff0e;选择新建一个Pure Python项目&#xff0c;新建项目路径可以在Location处选择。 2.Project Interpreter部分是选择新建项目所依赖的python库&#xff0c;第一个选项会在项目中简历一个venv&#xff08;virtualenv&#xff09;目录&#xff0c;这里存放…

Java项目:宠物商城系统(java+Springboot+Maven+mybatis+Vue+mysql)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…

android插件化-apkplug中以监听方式获取OSGI服务-09

2019独角兽企业重金招聘Python工程师标准>>> 我们提供 apkplug 下OSGI使用demo 源码托管地址为 http://git.oschina.net/plug/OSGIService 一 需求 通过 <<apkplug中OSGI服务基本原理-08>>我们知道怎样注册于查询OSGI Service。但查询方式必须在Servi…

网页失去焦点事件 visibilitychange

当网页失去焦点事件时会触发 visibilitychange 事件&#xff0c;可进行相关逻辑处理 如失去焦点需暂停播放 或 变更title吸引用户回来.. eg: <script>document.addEventListener(visibilitychange, function () {var isHidden document.hidden;if (isHidden) {//失去焦点…

UML类图符号 各种关系说明以及举例

UML中描述对象和类之间相互关系的方式包括&#xff1a;依赖&#xff08;Dependency&#xff09;&#xff0c;关联&#xff08;Association&#xff09;&#xff0c;聚合&#xff08;Aggregation&#xff09;&#xff0c;组合&#xff08;Composition&#xff09;&#xff0c;泛…

Java项目:医院预约挂号系统(java+SpringBoot+Maven+Vue+mysql)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…

《暗时间》读后感

什么是暗时间 什么是暗时间&#xff1f;你走路、买菜、洗脸洗手、坐公车、逛街、出游、吃饭、睡觉&#xff0c;所有这些时间都可以称为“暗时间”。我理解暗时间就是把自己平时在不知不觉中度过的&#xff0c;看不到它流逝的时光充分利用起来的时间。这些时间在作者的眼里是可以…

AbstractMap详解

/ 包:java.util// 包:java.util package java . util;Map.Entry;​同 SimpleEntry 一样,都继承了 Map.Entry 和 序列化接口。

OpenGL渲染流水中的处理步骤

显示列表:不管数据描述的是几何体还是像素,都可以被存储在显示列表中,供现在或以后使用;也可以不将数据存储在显示列表中,而是立刻对数据进行处理,这被称为直接模式.显示列表被执行时,其中存储的数据被发送出去,就像在应用程序中用直接模式发送一样.求值程序:所有的几何图元最终…

需要在method方法被调用之后,仅打印出a=100,b=200,请写出method方法的代码

通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。public static final PrintStream err“标准”错误输出流。PrintStream 是打印输出流,它继承于FilterOutputStream。第二个用的是用的是char类型,根本不是方法,当要输出方法体的时候,会给你遍历数组。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。诡异的是,如果错了,面试官对你说了一句:你回去看看,

Linux复制文件scp

cp 复制文件(copy) cp sourcefile destfile scp 跨服务器复制(secure copy) (1) 复制文件&#xff1a; scp local_file remote_usernameremote_ip:remote_folder 或 scp local_file remote_usernameremote_ip:remote_file 或 scp local_file remote_ip:remote_folder 或 scp lo…

一键部署 SpringCloud 微服务,这套流程值得学习一波儿!

一键部署 springcloud 微服务,需要用到 Jenkins K8S Docker等工具。本文使用jenkins部署,流程如下图开发者将代码push到git运维人员通过jenkins部署,自动到git上pull代码通过maven构建代码将maven构建后的jar打包成docker镜像 并 push docker镜像到docker registry通过k8s发起 发布/更新 服务 操作其中 2~5步骤都会在jenkins中进行操作。