-
【功能模块】【操作步骤&问题现象】1、配置userName,password,casUrl,WebUrl2、通过CASLogin 完成登录认证3、连接C70 集群成功,连接651集群失败【截图信息】【日志信息】(可选,上传日志内容或者附件)失败信息:com.huawei.om.rest.exception.LoginCasServerException:webLoginCheck failed
-
### 设计与模式 之前一直以为「设计模式」是一个完整的名词 其实「设计」和「模式」是要分开来说的 **「设计」:5 个常见的设计原则** **「模式」:代码中常见的"套路",被程序员总结成了相对固定的写法,称之为「模式」** 也就是说学习"设计模式",首先肯定要学习和理解 5 个设计原则。 #### 五大设计原则-SOLID - S(single)-单一职责原则- 一个程序只做好一件事;如果功能过于复杂就拆分开,每个部分保持独立; - O(open-close)-开放封闭原则- 对扩展开放,对修改封闭;增加需求时,扩展新代码,而非修改已有代码; - L(Liskov)-里氏置换原则-子类能覆盖父类;父类能出现的地方子类就能出现; - I(Interface)-接口独立原则-保持接口的单一独立,避免出现”胖接口“;js中没有接口(typescript除外),使用少; - D(Dependence)-依赖倒置原则-编程依赖抽象接口,不要依赖具体实现;使用方只关注接口而不关注具体类的实现; **示例 promise** - 单一职责原则:每个 then 中的逻辑制作好一件事 - 开放封闭原则:如果新增需求,扩展 then ```js // 0.0.1/loadImg.js function loadImg(src) { let promise = new Promise(function (resolve, reject) { let img = document.createElement('img'); img.onload = function () { resolve(img); } img.onerror = function () { reject('图片加载失败'); } img.src = src; }); return promise; } let imgUrl = 'https://raw.githubusercontent.com/ruizhengyun/images/master/cover/ruizhengyun.cn_.png'; loadImg(imgUrl).then(function (img) { console.log(`width: ${img.width}`); return img; }).then(function (img) { console.log(`height: ${img.height}`); }).catch(function (ex) { console.log(ex); }); ``` #### Javascript 常用设计模式 -- 工厂模式 工厂模式也称创建模式,是用于创建对象的一种方式。可以说就是用来代替 new 实例化对象,决定了实例化哪一个类,从而解决解耦问题。 **拟物化解读** 一个工厂接到一笔订单(传参),然后根据这个订单类型(参数)来安排产品线(实例化哪个类),当然客户可以要求一些产品的工艺属性(抽象工厂)。这其中厂长(工厂模式)只负责调度,即安排产品零件流水线。你应该知道的是,工厂有个特点就是产出体量大、相似度高的产品。如果你要做单一定制化的产品,那这笔订单给工厂就不适用了。 **其作用(利)** 解耦,通过使用工程方法而不是 new 关键字; 将所有实例化的代码集中在一个位置减少代码重复,降低出错; **具体实现** 分步创建一个复杂的对象,解耦封装过程和具体创建组件(分解为零件流水线); 无需关心组件如何组装(厂长在调度); 不暴露创建对象的具体逻辑,将逻辑封装在一个函数中(客户只需要告诉工厂做什么和提一些要求); **适用场景** 处理大量具有相同属性的小对象; 对象的构建十分复杂,需要依赖具体环境创建不同实例 **分类(抽象程度)** 不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂。 ##### 简单工厂模式 也可以叫静态工厂模式,用一个工厂对象创建同一类对象类的实例。现实生活中,用户在平台还是分等级的,角色不同,权限也不同。 **1.ES5 实现** ```js // 0.0.2/es5.simple.factory.js function Role(options){ this.role = options.role; this.permissions = options.permissions; } Role.prototype.show = function (){ var str = '是一个' + this.role + ', 权限:' + this.permissions.join(', '); console.log(str) } function simpleFactory(role){ switch(role) { case 'admin': return new Role({ role: '管理员', permissions: ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论'] }); break; case 'developer': return new Role({ role: '开发者', permissions: ['开发', '推送', '提问', '评论'] }); break; default: throw new Error('参数只能为 admin 或 developer'); } } // 实例 const xm = simpleFactory('admin'); xm.show(); const xh = simpleFactory('developer'); xh.show(); const xl = simpleFactory('guest'); xl.show(); ``` **2.ES6 实现** ```js // 0.0.2/simple.factory.js class SimpleFactory { constructor(opt) { this.role = opt.role; this.permissions = opt.permissions; } // 静态方法 static create(role) { switch (role) { case 'admin': return new SimpleFactory({ role: '管理员', permissions: ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论'] }); break; case 'developer': return new SimpleFactory({ role: '开发者', permissions: ['开发', '推送', '提问', '评论'] }); break; default: throw new Error('参数只能为 admin 或 developer'); } } show() { const str = `是一个${this.role}, 权限:${this.permissions.join(', ')}`; console.log(str); } } // 实例 const xm = SampleFactory.create('admin'); xm.show(); const xh = SampleFactory.create('developer'); xh.show(); const xl = SampleFactory.create('guest'); xl.show(); ``` 或 ```js // 0.0.2/simple.factory2.js class Role { constructor(options) { this.role = options.role; this.permissions = options.permissions; } show() { const str = `是一个${this.role}, 权限:${this.permissions.join(', ')}`; console.log(str); } } class SimpleFactory { constructor(role) { if(typeof this[role] !== 'function') { throw new Error('参数只能为 admin 或 developer'); } return this[role](); } admin() { return new Role({ role: '管理员', permissions: ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论'] }); } developer() { return new Role({ role: '开发者', permissions: ['开发', '推送', '提问', '评论'] }); } } // 实例 const xm = new SimpleFactory('admin'); xm.show(); const xh = new SimpleFactory('developer'); xh.show(); const xl = new SimpleFactory('guest'); xl.show(); ``` 上例中,simpleFactory 就是一个简单工厂,2个实例对应不同的权限,调用工厂函数时,只需传递 admin 或 developer 就可获取对应的实例对象。 **1.简单工厂函数适用场景** 正确传参,就可以获取所需要的对象,无需知道内部实现细节; 内部逻辑(工厂函数)通过传入参数判断实例化是使用哪些类; 创建对象数量少(稳定),对象的创建逻辑不复杂; **2.简单工厂函数不适用场景** 当需要添加新的类时,就需要修改工厂方法,这违背了开放封闭原则(OCP, 对扩展开放、对源码修改封闭)。函数 create 内包含了所有创建对象(构造函数)的判断逻辑代码,如果要增加新的构造函数还需要修改函数 create(判断逻辑代码),当可选参数 role 变得更多时,那函数 create 的判断逻辑代码就变得臃肿起来,难以维护。 不适用创建多类对象; ##### 工厂方法模式 将实际创建对象工作推迟到子类当中,核心类就成了抽象类。这样添加新的类时就无需修改工厂方法,只需要将子类注册进工厂方法的原型对象中即可。 **1.ES5 实现**,ES5 没有像传统创建类的方式那样创建抽象类,所以工厂方法模式只需参考其核心思想即可。可将工厂方法看做一个实例化对象工厂类(采用安全模式类),将创建对象的基类放在工厂方法类的原型中即可。当需要添加新类时,只需挂载在 FunctionFactory.prototype 上,无需修改工厂方法,也实现了 OCP 原则。 ```js // 0.0.2/es5.function.factory.js function FunctionFactory(role) { if(!(['admin', 'developer'].indexOf(role) > -1)){ throw new Error('参数只能为 admin 或 developer'); } // 安全的工厂方法 if (this instanceof FunctionFactory) { return this[role](); } return new FunctionFactory(role); } FunctionFactory.prototype.show = function () { var str = '是一个' + this.role + ', 权限:' + this.permissions.join(', '); console.log(str) } FunctionFactory.prototype.admin = function (permissions) { this.role = '管理员'; this.permissions = ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论']; } FunctionFactory.prototype.developer = function (permissions) { this.role = '开发者'; this.permissions = ['开发', '推送', '提问', '评论']; } var xm = FunctionFactory('admin'); xm.show(); var xh = new FunctionFactory('developer'); xh.show(); var xl = new FunctionFactory('guest'); xl.show(); ``` **2.ES6 实现**,由于 ES6 中还没有 abstract,就用 new.target 来模拟出抽象类(new.target 指向被 new 执行的构造函数),判断 new.target 是否指向了抽象类,如果是就报错。 ```js // 0.0.2/function.factory.js class FunctionFactoryBase { // 抽象类 constructor(role) { if (new.target === FunctionFactoryBase) { throw new Error('抽象类不能实例'); } this.role = role; } } class FunctionFactory extends FunctionFactoryBase { // 子类 constructor(role) { super(role); } static create(role) { switch (role) { case 'admin': return new FunctionFactory({ role: '管理员', permissions: ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论'] }); break; case 'developer': return new FunctionFactory({ role: '开发者', permissions: ['开发', '推送', '提问', '评论'] }); break; default: throw new Error('参数只能为 admin 或 developer'); } } show() { const { role, permissions } = this.role; const str = `是一个${role}, 权限:${permissions.join(', ')}`; console.log(str) } } // let xl = new FunctionFactoryBase(); // 此行会报错,注释后方可正常执行后面 let xm = FunctionFactory.create('admin'); xm.show() let xh = FunctionFactory.create('developer'); xh.show() let xl = FunctionFactory.create('guest'); xl.show() ``` ##### 抽象工厂模式 抽象工厂只留对外的口子,不做事,留给外界覆盖(子类重写接口方法以便创建的时候指定自己的对象类型)。主要用于对产品类簇的创建,不直接生成实例(简单工厂模式和工厂方法模式都是生成实例)。 - 抽象类是一种声明但不能使用的类,子类必须先实现其方法才能调用; - 可以在抽象类中定义一套规范,供子类去继承实现; ```js // 0.0.2/abstract.factory2.js // 抽象工厂 function AbstractFactory(subType, superType) { if (typeof AbstractFactory[superType] === 'function') { //缓存类 function F() { } //继承父类属性和方法 F.prototype = new AbstractFactory[superType](); //将子类 constructor 指向子类(自己) subType.prototype.constructor = subType; //子类原型继承缓存类(父类) subType.prototype = new F(); } else { //不存在该抽象类抛出错误 throw new Error('抽象类不存在') } } // 抽象类 AbstractFactory.Phone = function () { this.type = 'Phone'; } AbstractFactory.Phone.prototype = { showType: function () { return new Error('Phone 抽象方法 showType 不能调用'); }, showPrice: function () { return new Error('Phone 抽象方法 showPrice 不能调用'); }, showColor: function () { return new Error('Phone 抽象方法 showColor 不能调用'); } } AbstractFactory.Pad = function () { this.type = 'Pad'; } AbstractFactory.Pad.prototype = { showType: function () { return new Error('Pad 抽象方法 showType 不能调用'); }, showPrice: function () { return new Error('Pad 抽象方法 showPrice 不能调用'); }, showColor: function () { return new Error('Pad 抽象方法 showColor 不能调用'); } } // 抽象工厂实现对抽象类的继承 function Iphone(type, price, color) { this.type = type; this.price = price; this.color = color; } //抽象工厂实现对 Phone 抽象类的继承 AbstractFactory(Iphone, 'Phone'); Iphone.prototype.showType = function () { return this.type; } Iphone.prototype.showPrice = function () { return this.price; } Iphone.prototype.showColor = function () { return this.color; } function Ipad(type, price, color) { this.type = type; this.price = price; this.color = color; } AbstractFactory(Ipad, 'Pad'); Ipad.prototype.showType = function () { return this.type; } Ipad.prototype.showPrice = function () { return this.price; } Ipad.prototype.showColor = function () { return this.color; } // 实例 var iphone5s = new Iphone('iphone 5s', 3000, '白色'); console.log('今天刚买了' + iphone5s.showType() + ',价格是' + iphone5s.showPrice() + ',' + iphone5s.showColor()) var iphone8s = new Iphone('iphone 8s', 8000, '白色'); console.log('今天刚买了' + iphone8s.showType() + ',价格是' + iphone8s.showPrice() + ',' + iphone8s.showColor()) var ipad = new Ipad('ipad air', 2000, '骚红色'); console.log('今天刚买了' + ipad.showType() + ',价格是' + ipad.showPrice() + ',' + ipad.showColor()) ``` **除此之外还有单例模式、适配器模式、装饰器模式、代理模式、观察者模式、迭代器模式等**
-
欢迎小伙伴们体验《在鲲鹏BMS上进行WRF部署与性能优化》实验,有任何问题都可以在这里讨论交流哦!通过本实验:§ 您将学习 通过配置编译器等应用的并行优化层面综合优化,提升WRF运行性能。§ 您将体验编译WRF主程序运行WRF主程序通过运行参数进行性能优化§ 您将掌握 通过操作编译器、MPI、数学库、应用并行优化层面综合优化,完成WRF运行性能的提升,使用户了解鲲鹏HPC常用调优手段,掌握通过鲲鹏小智查询调优指导。实验开始前,推荐您先学习相关课程,掌握实验背景知识:《鲲鹏体系介绍》《鲲鹏应用移植》《鲲鹏应用性能测试及调优》 实验过程中,如有任何问题可在该帖进行交流,也可以添加华为云学院小助手(微信号:HWcloudedu),专业人员为您实时解疑答惑,互相交流,共同进步! 如要提出问题,请详细描述您的问题及出现的步骤,附上实验操作截图,如有其他参考信息可一并附上。
-
1、需求分析,了解具体需求 2、测试准备:原型图、效果图、需求文件、测试用用例、用例评审、各种测试数据准备 3、测试环节:接受版本开始执行 1)冒烟测试:对版本质量的控制以及此版本是否具备测试条件; 2)UI测试:核对效果图 3)功能测试:核对需求文件测试用例,对功能进行验证 4)兼容测试:各个浏览器,手机是否兼容 5)性能测试,服务器接口,多线程等压测 6)安全测试 7)易用性测试:界面与交互性测试,符合交互规范,用户体验良好,使用方便快捷 8)提交bug,争议bug评审,回归测试 9)自动化测试 a、ui自动化测试 :一般使用python+selenium进行ui自动化测试 4、发布功能,上线验证,发布上线报告
-
web测试流程 1、需求分析,了解具体需求 2、测试准备:原型图、效果图、需求文件、测试用用例、用例评审、各种测试数据准备 3、测试环节:接受版本开始执行 1)冒烟测试:对版本质量的控制以及此版本是否具备测试条件; 2)UI测试:核对效果图 3)功能测试:核对需求文件测试用例,对功能进行验证 4)兼容测试:各个浏览器,手机是否兼容 5)性能测试,服务器接口,多线程等压测 6)安全测试 7)易用性测试:界面与交互性测试,符合交互规范,用户体验良好,使用方便快捷 8)提交bug,争议bug评审,回归测试 9)自动化测试 a、ui自动化测试 :一般使用python+selenium进行ui自动化测试 4、发布功能,上线验证,发布上线报告
-
WAF的出现是由于传统防火墙无法对应用层的攻击进行有效抵抗,并且IPS也无法从根本上防护应用层的攻击。因此出现了保护Web应用安全的Web应用防火墙系统(简称“WAF”)。WAF是通过检测应用层的数据来进行访问控制或者对应用进行控制,而传统防火墙对三、四层数据进行过滤,从而进行访问控制,不对应用层数据进行分析。防火墙、IDS和IPS之间有什么区别?现在市场上的主流网络安全产品可以分为以下几个大类:基础防火墙类:主要是可实现基本包过滤策略的防火墙,这类是有硬件处理、软件处理等,其主要功能实现是限制对IP:port的访问。解决传统防火墙只能工作在4层以下的问题。基本上的实现都是默认情况下关闭所有的通过型访问,只开放允许访问的策略。IDS类(入侵检测系统(IDS:Intrusion Detection Systems)):此类产品基本上以旁路为主,特点是不阻断任何网络访问,主要以提供报告和事后监督为主,少量的类似产品还提供TCP阻断等功能,但少有使用。IPS类(入侵防御系统(IPS:Intrusion Prevention System)):解决了IDS无法阻断的问题,基本上以在线模式为主,系统提供多个端口,以透明模式工作。在一些传统防火墙的新产品中也提供了类似功能,其特点是可以分析到数据包的内容。和IDS一样,IPS也要像防病毒系统定义N种已知的攻击模式,并主要通过模式匹配去阻断非法访问。主动安全类,和前面的产品均不同,主动安全产品的特点是协议针对性非常强:WAF就是专门负责HTTP协议的安全处理,能够在应用层对攻击进行有限抵抗。
-
## Chrome调试工具  #### 箭头 & Elements ##### **箭头:** 用于在页面选择一个元素来审查和查看它的相关信息。 ##### Elements 内容: + 样式  + 计算属性  + 事件监听 > 将Ancestors和Framework Listeners两个checkbox取消选中可以看到 选择dom绑定的事件。  + DOM breakpoints + 在Chrome开发者工具里,选中想要监控的DOM元素,点击右键,选择Break on->Attributes modifications:  + 之后在DOM Breakpoints的tab里能看到对应的断点 + 然后回到Chrome里鼠标悬停百度一下,Chrome开发者工具的调试器就会自动在DOM的属性发生变化的地方停下来:  + 从调试器的调用上下文能了解到是上图第118行的className 改变了导致DOM断点的触发。 + Properties > 改选项卡里面可以看到 选择dom对象,及类的继承链。  #### 设备模拟器 > 模拟移动屏幕,或者一些其它的像素比例的屏幕。  #### Console控制台 > 控制台打印, 执行脚本。  #### Sources  #### NetWork  #### Performance  #### Memory: 内存快照  ## Vscode内代码调试 #### 调试环境配置 > 在.vscode / launch.json 文件夹为配置文件。(可以快捷键ctrl + p 然后输入 debug 进行快速配置)  **参数说明:** ```json "name": "Attach to Chrome", // 调试环境名称 "type": "pwa-chrome", //调试环境运行环境, 取值: node pwa-chrome 或其它自行安装的程序命令 "request": "attach" | "launch", // attach模式: 监听一个启动进程 launch模式: 由vscode 来启动一个独立的debug进程 "url": "http://localhost:8080", // 服务地址 launch模式才会有 "port": 9222, // 端口 attach才会有 "webRoot": "${workspaceFolder}" // 静态资源目录 "cwd": "${workspaceFolder}/dist", // 指定程序启动调试的目录 ,当vscode启动目录不是项目根目录,并且调试npm script时非常有用 "args": ["--no-install"], "outFiles": ["${workspaceFolder}/lib/**/*.js"], //指定 sourceMaps的位置 "skipFiles": [ "<node_internals>/**/*.js", "${workspaceFolder}/node_modules/**/*.js" ], //指定跳过单步调试的代码 "preLaunchTask": "npm: build", // launch之前做的事情 "stopOnEntry": true, //自动断点到第一行代码处 "smartStep": true, //自动跳过未映射到源代码的代码 ``` #### 单个JS调试 > 添加如下配置到launch.json 选择test.js F5 debug模式运行 ```json { "type": "node", "request": "launch", "name": "debug test", "runtimeExecutable": "node", "program": "${workspaceFolder}/code/debugger/test.js", "restart": true, "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" } ``` #### 项目调试 + 安装插件:`Debugger for Chrome`  + 配置launch.json ```json { "type": "chrome", "request": "launch", "name": "launch project dbug", "url": "http://localhost:8993/drs", "webRoot": "${workspaceFolder}", "preLaunchTask": "debug", "sourceMapPathOverrides": { "webpack://[name]/./*": "${webRoot}/*", "webpack:///src/*": "${webRoot}/*", "webpack:///*": "*", "webpack:///./~/*": "${webRoot}/node_modules/*", }, }, ``` + 配置debug命令 ```json { "version": "2.0.0", "command": "npm", // 运行命令的程序 "tasks": [ { "label": "debug", // Task 名称 "isBackground": true, "type": "npm", "script": "start", // npm 要执行的 script 名称,对应 package.json 中的定义 "detail": "编译至开发环境", // Task 的描述,在命令面板中显示 "group": "test", "problemMatcher": { "fileLocation": "relative", "pattern": { "regexp": "^([^\\s].*)\\((\\d+|\\,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$", "file": 1, "location": 2, "severity": 3, "code": 4, "message": 5 }, "background": { "activeOnStart": true, "beginsPattern": ".", "endsPattern": "Version: webpack.+" } } } ] } ``` + 运行`launch project dbug` 命令
-
模块化的好处:避免命名冲突(减少命名空间污染)更好的分离, 按需加载更高复用性高可维护性1.CommonJSNode.js 应用由模块组成,采用 CommonJS 模块规范。 1.1 语法风格//Math.js module.exports = { 'add': function(a, b) { return a + b; } }//main.js const Math = require('./Math'); ); ; /** 输出: -->node main 5 done **/1.2 同步加载由上面例子可以看出,CommonJS中模块是同步加载的1.3 动态加载//main.js const Math = require('./Ma' + 'th');//动态拼接CommonJS支持动态加载模块。因为conmonJs是同步的,执行到这一行时,才会加载模块。1.4 浏览器不支持CommonJS规范。浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量。moduleexportsrequireglobal可以使用工具进行转换,例如:Browserify1.5 其他特点所有代码都运行在模块作用域,不会污染全局作用域。模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。模块加载的顺序,按照其在代码中出现的顺序。CommonJS模块的加载机制中,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。适合服务器端小测验//Math.js let counter = 1; let addCounter = function () { counter++; }; module.exports = { counter, addCounter }; ; //main.js const Math = require('./Math'); Math.addCounter(); ; // position A Math.counter = 5; const Math2 = require('./Math'); ; // position B //位置A 和 位置B的值分别是什么?2.AMDCommonJS是主要为了JS在后端的表现制定的,他是不适合前端的。AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。RequireJS实现了AMD规范。下面以RequireJS为例,了解一下AMD规范。2.1 语法风格//Math.js define([], function(){ return { 'add': function(a, b) { return a + b; } } })//main.js require.config({ paths : { "math" : "Math" } }); require(['math'], function (math) { ); }); ; //done //52.2 异步加载由上面的例子可以看出,require命令是异步执行的2.3 动态加载2.4 依赖前置,提前执行3.CMD(Common Module Definition)CMD是SeaJS 在推广过程中对模块定义的规范化产出。CMD规范和 AMD 很相似,尽量保持简单,并与 CommonJS 规范保持了很大的兼容性。 3.1 语法风格// CMD define(function(require, exports, module) { var a = require('./a'); a.doSomething(); //... var b = require('./b'); // 依赖可以就近书写 b.doSomething(); // ... require.async('./c',function(c){ //支持异步加载 c.doSomething(); }); })// AMD 默认推荐的是 define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好 a.doSomething(); //... b.doSomething(); //... })3.2 AMD和CMD的区别1) 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible(尽可能的懒加载,也称为延迟加载,即在需要的时候才加载)。2) CMD 推崇依赖就近,AMD 推崇依赖前置。虽然 AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递,但 RequireJS 的作者默认是最喜欢依赖前置的写法,也是官方文档里默认的模块定义写法。3.3 推荐链接与 RequireJS 的异同二、ModuleES中Module的特点浏览器,服务器通用静态加载1. 基本语法1.1 export一个模块就是一个独立的文件。export关键字用来输出模块内部的某个变量 。可以输出变量,函数或类。// test.js export var name = 'zhangsan'; export var age = 18;// test.js var name = 'zhangsan'; var age = 18; export { name, age };可以使用as为输出变量重命名。var name = 'zhangsan'; var age = 18; export { name as personName, age };需要特别注意的是,export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。// 报错 export 1; // 报错 var m = 1; export m;export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。//m.js export var foo = 'bar'; setTimeout(() => foo = 'baz', 1000);//main.js import { foo } from './m1.js'; ; setTimeout(function () { ; }, 1000); //console 1 "bar" 2 "baz"export命令可以出现在模块的任何位置,只要处于模块顶层就可以。1.2 import其他 JS 文件通过import命令加载模块。大括号里面的变量名,必须与被导入模块(test.js)对外接口的名称相同。// main.js import { name, age } from './test.js'; function showName() { ; } //zhangsanimport命令输入的变量都是只读的,因为它的本质是输入接口。import {a} from './xxx.js' a = {}; // Assignment to constant variable.如果a是一个对象,改写a的属性是允许的。和const一样。import {a} from './xxx.js' a.foo = 'hello'; // 合法操作可以用星号(*)指定一个对象,进行整体加载。// main.js import * as test from './test.js'; function showName() { ; } //zhangsan test.name = 'yun'; //Cannot assign to read only property 'name' of '[ Module]' //但如果是对象,可以修改对象的属性。import命令具有提升效果,会提升到整个模块的头部,首先执行。foo(); import { foo } from 'my_module';由于import是静态执行,所以不能使用表达式和变量。// 报错 import { 'f' + 'oo' } from 'my_module';import语句会执行所加载的模块,因此可以有下面的写法。仅仅执行lodash模块,但是不输入任何值。import 'lodash';即使加载多次,也只会执行一次。也就是说,import语句是 Singleton 模式。1.3 单例模式解读//counter.js export let counter = 1; export function addCounter(){ counter++; } ;//main.js import {counter, addCounter} from './counter'; ; addCounter(); ; //counter init //main:1 //main:2 //从这例子中可以看出和CommonJS的区别另一个例子//main2.js import {counter, addCounter} from './counter'; ; addCounter(); ;<>module test</>//console counter init main:1 main:2 main2:2 main2:3从上面这个例子中可以看出,即使加载多次,也只会执行一次。并且是 Singleton 模式。1.4 export default使用export default可以不用关注输出模块中的变量名。// export-default.js export default function () { ; }// import-default.js import customName from './export-default'; customName(); // 'foo'import命令可以为该匿名函数指定任意名字。import命令后面,不使用大括号。// export-default.js function foo() { ; } export default foo;export default的本质,就是输出一个叫做default的变量或方法。imort something from ...的本质,就是import {default as something} from ...// 正确 export var a = 1; // 正确 var a = 1; export default a; // 错误 export default var a = 1; // 正确 export default 42; // 报错 export 42;所以export default是比较常用的方法:// MyClass.js export default class { ... } // main.js import MyClass from 'MyClass'; let o = new MyClass();1.5 import&export混合使用export { foo, bar } from 'my_module'; // 可以简单理解为 import { foo, bar } from 'my_module'; export { foo, bar };上面代码中,export和import语句可以结合在一起,写成一行。但需要注意的是,写成一行以后,foo和bar实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用foo和bar。// 接口改名 export { fooName as newName } from 'my_module'; //具名接口改为默认接口 export { foo as default } from './someModule'; //接口也可以改名为具名接口 export { default as ES } from './someModule';1.6 模块的继承//calculator.js export function add(a, b) { return a + b; }//calculatorPlus.js export * from './calculator.js'; export function multiply(a, b) { return a * b; }//main.js import * as cal from './calculatorPlus.js'; cal.add(2, 3);//5 cal.multiply(2, 3);//61.7 import()ES2020 (ES11)新增了 动态import特性,解决了import动态加载,和不能写在代码块中的问题。import()返回一个 Promise 对象 。import(a + '.js') .then(...); import(f()) .then(...); if (condition) { import('moduleA').then(...); } else { import('moduleB').then(...); }2. Module补充2.1 ES模块和CommonJS模块的差异import和export是关键字,require不是。CommonJS 模块输出的是一个值的拷贝,ES 模块输出的是值的引用。(详情见上方【单例模式解读】)CommonJS 模块是运行时加载,ES 模块是编译时输出接口。 因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。this指向不同。ES 模块之中,顶层的this指向undefined;CommonJS 模块的顶层this指向当前模块,这是两者的一个重大差异。2.2 循环加载CommonJS中的循环加载// a.js let bar = require('./b').bar; ; exports.ada = 'ada';// b.js let ada = require('./a').ada; exports.bar = 'bar'; setTimeout(function () { ; }, 0);执行结果:-->node a bar: bar ada: undefinedES Module中的循环加载// a.js import {bar} from './b.js'; ; export let ada = 'ada';// b.js import {ada} from './a.js'; //; //此处访问ada会报错 setTimeout(function () { ; }, 0); export let bar = 'bar'; <>module test</>执行结果://console a.js bar b.js ada解读:因为CommonJS 模块输出的是一个值的拷贝,当拷贝的动作发生时,ada值为undefined,之后模块a中对ada进行赋值已经不会影响到模块b中保存的ada。ES 模块输出的是值的引用。所以模块a对ada进行赋值,会影响b中打印的结果。
-
模块化的好处:避免命名冲突(减少命名空间污染)更好的分离, 按需加载更高复用性高可维护性1.CommonJSNode.js 应用由模块组成,采用 CommonJS 模块规范。 1.1 语法风格//Math.js module.exports = { 'add': function(a, b) { return a + b; } }//main.js const Math = require('./Math'); console.log(Math.add(2, 3)); console.log('done'); /** 输出: -->node main 5 done **/1.2 同步加载由上面例子可以看出,CommonJS中模块是同步加载的1.3 动态加载//main.js const Math = require('./Ma' + 'th');//动态拼接CommonJS支持动态加载模块。因为conmonJs是同步的,执行到这一行时,才会加载模块。1.4 浏览器不支持CommonJS规范。浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量。moduleexportsrequireglobal可以使用工具进行转换,例如:Browserify1.5 其他特点所有代码都运行在模块作用域,不会污染全局作用域。模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。模块加载的顺序,按照其在代码中出现的顺序。CommonJS模块的加载机制中,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。适合服务器端小测验//Math.js let counter = 1; let addCounter = function () { counter++; }; module.exports = { counter, addCounter }; console.log('Math init'); //main.js const Math = require('./Math'); Math.addCounter(); console.log(Math.counter); // position A Math.counter = 5; const Math2 = require('./Math'); console.log(Math2.counter); // position B //位置A 和 位置B的值分别是什么?2.AMDCommonJS是主要为了JS在后端的表现制定的,他是不适合前端的。AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。RequireJS实现了AMD规范。下面以RequireJS为例,了解一下AMD规范。2.1 语法风格//Math.js define([], function(){ return { 'add': function(a, b) { return a + b; } } })//main.js require.config({ paths : { "math" : "Math" } }); require(['math'], function (math) { console.log(math.add(2, 3)); }); console.log('done'); //done //52.2 异步加载由上面的例子可以看出,require命令是异步执行的2.3 动态加载2.4 依赖前置,提前执行3.CMD(Common Module Definition)CMD是SeaJS 在推广过程中对模块定义的规范化产出。CMD规范和 AMD 很相似,尽量保持简单,并与 CommonJS 规范保持了很大的兼容性。 3.1 语法风格// CMD define(function(require, exports, module) { var a = require('./a'); a.doSomething(); //... var b = require('./b'); // 依赖可以就近书写 b.doSomething(); // ... require.async('./c',function(c){ //支持异步加载 c.doSomething(); }); })// AMD 默认推荐的是 define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好 a.doSomething(); //... b.doSomething(); //... })3.2 AMD和CMD的区别1) 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible(尽可能的懒加载,也称为延迟加载,即在需要的时候才加载)。2) CMD 推崇依赖就近,AMD 推崇依赖前置。虽然 AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递,但 RequireJS 的作者默认是最喜欢依赖前置的写法,也是官方文档里默认的模块定义写法。3.3 推荐链接与 RequireJS 的异同二、ModuleES中Module的特点浏览器,服务器通用静态加载1. 基本语法1.1 export一个模块就是一个独立的文件。export关键字用来输出模块内部的某个变量 。可以输出变量,函数或类。// test.js export var name = 'zhangsan'; export var age = 18;// test.js var name = 'zhangsan'; var age = 18; export { name, age };可以使用as为输出变量重命名。var name = 'zhangsan'; var age = 18; export { name as personName, age };需要特别注意的是,export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。// 报错 export 1; // 报错 var m = 1; export m;export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。//m.js export var foo = 'bar'; setTimeout(() => foo = 'baz', 1000);//main.js import { foo } from './m1.js'; console.log(1, foo); setTimeout(function () { console.log(2, foo); }, 1000);<!DOCTYPE html> <html> <script type="module" src='./main.js'></script> <body></body> </html>//console 1 "bar" 2 "baz"export命令可以出现在模块的任何位置,只要处于模块顶层就可以。1.2 import其他 JS 文件通过import命令加载模块。大括号里面的变量名,必须与被导入模块(test.js)对外接口的名称相同。// main.js import { name, age } from './test.js'; function showName() { console.log(name); } //zhangsanimport命令输入的变量都是只读的,因为它的本质是输入接口。import {a} from './xxx.js' a = {}; // Assignment to constant variable.如果a是一个对象,改写a的属性是允许的。和const一样。import {a} from './xxx.js' a.foo = 'hello'; // 合法操作可以用星号(*)指定一个对象,进行整体加载。// main.js import * as test from './test.js'; function showName() { console.log(test.name); } //zhangsan test.name = 'yun'; //Cannot assign to read only property 'name' of object '[object Module]' //但如果是对象,可以修改对象的属性。import命令具有提升效果,会提升到整个模块的头部,首先执行。foo(); import { foo } from 'my_module';由于import是静态执行,所以不能使用表达式和变量。// 报错 import { 'f' + 'oo' } from 'my_module';import语句会执行所加载的模块,因此可以有下面的写法。仅仅执行lodash模块,但是不输入任何值。import 'lodash';即使加载多次,也只会执行一次。也就是说,import语句是 Singleton 模式。1.3 单例模式解读//counter.js export let counter = 1; export function addCounter(){ counter++; } console.log('counter init');//main.js import {counter, addCounter} from './counter'; console.log('main:' + counter); addCounter(); console.log('main:' + counter); //counter init //main:1 //main:2 //从这例子中可以看出和CommonJS的区别另一个例子//main2.js import {counter, addCounter} from './counter'; console.log('main2:' + counter); addCounter(); console.log('main2:' + counter);<!DOCTYPE html> <html> <head> <title>module test</title> <script type="module" src='main.js'></script> <script type="module" src='main2.js'></script> </head> </html>//console counter init main:1 main:2 main2:2 main2:3从上面这个例子中可以看出,即使加载多次,也只会执行一次。并且是 Singleton 模式。1.4 export default使用export default可以不用关注输出模块中的变量名。// export-default.js export default function () { console.log('foo'); }// import-default.js import customName from './export-default'; customName(); // 'foo'import命令可以为该匿名函数指定任意名字。import命令后面,不使用大括号。// export-default.js function foo() { console.log('foo'); } export default foo;export default的本质,就是输出一个叫做default的变量或方法。imort something from ...的本质,就是import {default as something} from ...// 正确 export var a = 1; // 正确 var a = 1; export default a; // 错误 export default var a = 1; // 正确 export default 42; // 报错 export 42;所以export default是比较常用的方法:// MyClass.js export default class { ... } // main.js import MyClass from 'MyClass'; let o = new MyClass();1.5 import&export混合使用export { foo, bar } from 'my_module'; // 可以简单理解为 import { foo, bar } from 'my_module'; export { foo, bar };上面代码中,export和import语句可以结合在一起,写成一行。但需要注意的是,写成一行以后,foo和bar实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用foo和bar。// 接口改名 export { fooName as newName } from 'my_module'; //具名接口改为默认接口 export { foo as default } from './someModule'; //接口也可以改名为具名接口 export { default as ES } from './someModule';1.6 模块的继承//calculator.js export function add(a, b) { return a + b; }//calculatorPlus.js export * from './calculator.js'; export function multiply(a, b) { return a * b; }//main.js import * as cal from './calculatorPlus.js'; cal.add(2, 3);//5 cal.multiply(2, 3);//61.7 import()ES2020 (ES11)新增了 动态import特性,解决了import动态加载,和不能写在代码块中的问题。import()返回一个 Promise 对象 。import(a + '.js') .then(...); import(f()) .then(...); if (condition) { import('moduleA').then(...); } else { import('moduleB').then(...); }2. Module补充2.1 ES模块和CommonJS模块的差异import和export是关键字,require不是。CommonJS 模块输出的是一个值的拷贝,ES 模块输出的是值的引用。(详情见上方【单例模式解读】)CommonJS 模块是运行时加载,ES 模块是编译时输出接口。 因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。this指向不同。ES 模块之中,顶层的this指向undefined;CommonJS 模块的顶层this指向当前模块,这是两者的一个重大差异。2.2 循环加载CommonJS中的循环加载// a.js let bar = require('./b').bar; console.log('bar:', bar); exports.ada = 'ada';// b.js let ada = require('./a').ada; exports.bar = 'bar'; setTimeout(function () { console.log('ada:', ada); }, 0);执行结果:-->node a bar: bar ada: undefinedES Module中的循环加载// a.js import {bar} from './b.js'; console.log('a.js', bar); export let ada = 'ada';// b.js import {ada} from './a.js'; //console.log(ada); //此处访问ada会报错 setTimeout(function () { console.log('b.js', ada); }, 0); export let bar = 'bar';<!DOCTYPE html> <html> <head> <title>module test</title> <script type="module" src='a.js'></script> </head> </html>执行结果://console a.js bar b.js ada解读:因为CommonJS 模块输出的是一个值的拷贝,当拷贝的动作发生时,ada值为undefined,之后模块a中对ada进行赋值已经不会影响到模块b中保存的ada。ES 模块输出的是值的引用。所以模块a对ada进行赋值,会影响b中打印的结果。
-
Kunpeng BoostKit应用使能套件,围绕大数据、分布式存储、数据库、虚拟化等八大主流场景,开放高性能开源组件、加速软件包和工具/参考实现,使能应用最优性能。本期介绍Web场景下应用使能相关文档资源的获取与使用。软件兼容性查询:cid:link_2鲲鹏BoostKit Web使能套件介绍 在线课程:https://education.huaweicloud.com/courses/course-v1:HuaweiX+CBUCNXK045+Self-paced/about?isAuth=0&cfrom=hwc鲲鹏BoostKit Web使能套件文档聚合:cid:link_1
-
按照 https://support.huaweicloud.com/ug-pa-kunpengdevps/kunpengpt_06_0017.html 完成了Web版的安装,IP地址选择的是弹性服务器的内网IP。已经在安全组中添加了8084的入规则。但是访问公网IP:8084提示“Page Not Found”。服务器的防火墙未开启:[root@ecs-b762-lsx86centos Code-Scanner_2.2.T2.SPC300_x86_64-linux]# systemctl status firewalld● firewalld.service - firewalld - dynamic firewall daemon Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled) Active: inactive (dead) Docs: man:firewalld(1)gunicorn_port :[root@ecs-b762-lsx86centos Code-Scanner_2.2.T2.SPC300_x86_64-linux]# systemctl status gunicorn_port● gunicorn_port.service - gunicorn daemon Loaded: loaded (/etc/systemd/system/gunicorn_port.service; enabled; vendor preset: disabled) Active: active (running) since Mon 2020-12-28 17:59:16 CST; 2h 27min ago Main PID: 676 (gunicorn) CGroup: /system.slice/gunicorn_port.service ├─ 676 /opt/portadv/tools/venv/bin/python3 /opt/portadv/tools/venv/bin/gunicorn -c resource/g... ├─ 921 /opt/portadv/tools/venv/bin/python3 /opt/portadv/tools/venv/bin/gunicorn -c resource/g... ├─1016 /opt/portadv/tools/venv/bin/python3 /opt/portadv/tools/venv/bin/gunicorn -c resource/g... ├─1046 /opt/portadv/tools/venv/bin/python3 /opt/portadv/tools/venv/bin/gunicorn -c resource/g... └─1062 /opt/portadv/tools/venv/bin/python3 /opt/portadv/tools/venv/bin/gunicorn -c resource/g...Dec 28 17:59:16 ecs-b762-lsx86centos systemd[1]: Started gunicorn daemon.nginx_port:[root@ecs-b762-lsx86centos Code-Scanner_2.2.T2.SPC300_x86_64-linux]# systemctl status nginx_port● nginx_port.service - nginx - high performance web server Loaded: loaded (/usr/lib/systemd/system/nginx_port.service; enabled; vendor preset: disabled) Active: active (running) since Mon 2020-12-28 17:59:18 CST; 2h 28min ago Docs: http://nginx.org/en/docs/ Main PID: 1168 (nginx) CGroup: /system.slice/nginx_port.service ├─1168 nginx: master process /opt/portadv/tools/nginx-install/sbin/nginx -c /opt/portadv/tool... ├─1182 nginx: worker process └─1183 nginx: worker processDec 28 17:59:17 ecs-b762-lsx86centos systemd[1]: Starting nginx - high performance web server...Dec 28 17:59:18 ecs-b762-lsx86centos bash[925]: /opt/portadv/tools/webui/.tempfileService nginx_port ...essDec 28 17:59:18 ecs-b762-lsx86centos systemd[1]: Started nginx - high performance web server.Hint: Some lines were ellipsized, use -l to show in full.
-
一、前言Nest是构建高效,可扩展的 Node.js Web 应用程序的框架。 它使用现代的 JavaScript 或 TypeScript(保留与纯 JavaScript 的兼容性),并结合 OOP(面向对象编程),FP(函数式编程)和FRP(函数响应式编程)的元素。 在底层,Nest 使用了 Express,但也提供了与其他各种库的兼容,例如Fastify,可以方便地使用各种可用的第三方插件。以上是Nest.js的官方介绍,其实我们只需知道,它是一款Node.js的后端框架,并且官方提到了这么一段话:Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.翻译过来就是:Nest 旨在提供一个开箱即用的应用程序体系结构,允许轻松创建高度可测试,可扩展,松散耦合且易于维护的应用程序。并且设计灵感来自于Angular。所以这应和了这篇文章的标题,而Angular很多模式又来自于Spring框架,比如依赖注入等等,所以可以认为:Nest.js是Node.js版的Spring框架。Nest.js的社区非常活跃,Github上已经有32.9k Star。不过我们国内也有类似的Node.js框架,比如阿里出品的Midway,两者设计思路也非常类似。二、创建新应用通过Nest CLI脚手架生成一个新应用:初始化完成后,执行npm run start即可启动项目。在项目的src目录下,我们可以看到以下几个文件:main.ts是入口文件,可以看到总共就8行代码:简单来说就是使用NestFactory这个工厂函数生成一个实例,然后监听3000端口即可。这时候打开浏览器访问localhost:3000,可以看到打印出了“Hello World!”三、实现接口接下来我们来实现一个查询用户的接口,来熟悉一下基本的开发流程。第一步,创建一个module:通过脚手架命令,可以自动帮我们生成一个module文件:这时候会发现,AppModule中自动引入了该module:第二步,创建Controller:Controller就类似前端的路由,负责处理服务器响应和客户端请求。举个例子,我们要创建一个接口获取所有的用户信息,接口路径为http://localhost:3000/user/users,那么我们可以在UserController中创建一个GET方法,路径为users:当我们访问上面的接口时,就会返回“all users”。再写一个获取单个用户信息的接口: 当然,这只是Controller能力的冰山一角,官方还提供了更多的装饰器:除此之外,Nest.js里还有很多概念都与Angular一一对应,如Provider、Pipe、Guard、Interceptors等等,对于熟悉Angular的开发者来说,Nest.js可谓是量身打造的后端框架。
-
(请严格按照回复示例进行回复,格式不对会影响获奖哦~)(回复格式:华为云ID+截图)为了巩固大家的学习成果,紧跟学习进度请将需要完成的每章随堂测验内容按要求回复到本帖下方还能有机会获得附加幸运奖哦~打卡时间2020.12.23-2021.01.06 23:59打卡要求在本帖中,回复对应章节的随堂测验并提交截图 ↓ 回复格式:华为云ID+课程完成截图,如图所示↓↓↓华为云ID:grandmaster奖励方式在本阶段内提交随堂测验的用户中随机抽取5位幸运奖奖励华为云定制三和一数据线 活动注意事项 由于本帖回复内容为随堂测试结果,已设置回帖仅版主可见想了解更多关于全栈成长计划课程内容请移步主帖:https://bbs.huaweicloud.com/forum/thread-96671-1-1.html
-
《零基础WEB全栈学习训练营》第二阶段已结束感谢大家的积极参与和支持(因参加课程的学生数量及打卡人数未达到开奖要求,取消第二阶段结业考试以及奖励,只发作业得分总排名前三名的奖励)课程相关内容- 课程观看入口: 点此查看课程课程为录播,现在报名还可观看相关内容- 课程大纲: 点此查看课程大纲-第二阶段直播回看链接:1.8月13日【大厂前端经典面试题解析】直播回放地址:https://bbs.huaweicloud.com/videos/103100学习打卡任务内容及奖励安排课程表奖品如下:各学习任务打卡1.随堂测验打卡贴:【零基础WEB全栈学习训练营】第二阶段·每章随堂测验打卡帖2.最终考核打卡帖:【零基础WEB全栈学习训练营】第二阶段•最终考核打卡帖《零基础WEB全栈学习训练营》第一阶段回看链接:https://bbs.huaweicloud.com/forum/forum.php?mod=viewthread&tid=86105其他学习与活动内容1.【零基础WEB全栈学习训练营】学习情况调查问卷,和版主反馈下你的态度?发发牢骚?学习调查问卷点这里点这里点这里
-
【功能模块】FusionInsight HD【操作步骤&问题现象】Apache Kylin对接FusionInsight时web页面打不开, 在kylin.log可以看到报错【截图信息】【日志信息】(可选,上传日志内容或者附件)
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签