Hero Image
ThreeJS 指南

threejs 学习笔记 threeJS 是一个用于创建 3D 图形的 JavaScript 库。它提供了许多概念和功能,包括: 场景(Scene):threeJS 中所有的 3D 对象都存在于场景中。场景是一个容器,可以添加和删除对象,控制相机和光源等。 相机(Camera):相机定义了场景中可见区域的位置和大小。通过移动和旋转相机,可以改变场景中的视角。 渲染器(Renderer):渲染器将场景和相机中的所有对象渲染成 2D 图像,以显示在屏幕上。 材质(Material):材质定义了 3D 对象的外观,包括颜色、纹理、透明度等。 几何体(Geometry):几何体定义了 3D 对象的形状,包括点、线、面等。 网格(Mesh):网格是几何体和材质的组合,是场景中显示的对象。 光源(Light):光源用于照亮场景中的对象,包括环境光、点光源、聚光灯和方向光等。 控制器(Controller):控制器可以用于交互式地控制相机和场景中的对象,包括鼠标控制器、触摸控制器和 VR 控制器等。 场景 (Scene) 在 Three.js 中,场景(Scene)是所有 3D 对象的容器,包括相机、灯光、物体等。可以通过创建一个场景对象来添加和管理所有的 3D 对象。 以下是创建和使用场景的基本步骤: 创建场景对象: var scene = new THREE.Scene(); 添加相机到场景中: var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); scene.add(camera); 添加渲染器到页面中: var renderer = new THREE.WebGLRenderer(); document.body.appendChild(renderer.domElement); 创建一个立方体对象,并添加到场景中: var geometry = new THREE.BoxGeometry(); var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); var cube = new THREE.Mesh(geometry, material); scene.add(cube); 渲染场景: function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); 在上述代码中,我们创建了一个场景对象,并添加了一个透视相机和一个绿色的立方体对象到场景中。然后,我们创建了一个渲染器对象,并将其添加到页面中。最后,我们使用 requestAnimationFrame 函数来循环渲染场景。 通过添加和移除不同的 3D 对象,以及调整相机和灯光等参数,我们可以创建出各种独特的 3D 场景效果。

    Hero Image
    traefik 配置域名访问以及https

    要将zhuty.com指向hugo-nfs-svc并配置HTTPS以及自动更新Certbot证书,您需要执行以下步骤: 创建一个Kubernetes Secret以存储您的TLS证书和密钥: kubectl create secret tls zhuty-tls --key /path/to/tls.key --cert /path/to/tls.crt 请将/path/to/tls.key和/path/to/tls.crt替换为您的TLS证书和密钥的路径。 创建一个Traefik IngressRoute以将zhuty.com指向hugo-nfs-svc: apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: zhuty namespace: default spec: entryPoints: - websecure routes: - match: Host(`zhuty.com`) kind: Rule services: - name: hugo-nfs-svc port: 80 tls: secretName: zhuty-tls 请确保将name字段设置为您选择的名称,将namespace字段设置为您选择的命名空间,将match字段设置为您的域名,将name和port字段设置为您的服务名称和端口。 部署Traefik Ingress Controller: kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v2.5/examples/k8s/traefik-deployment.yaml 安装Certbot: sudo apt-get update sudo apt-get install certbot 生成Let’s Encrypt证书: sudo certbot certonly --manual --preferred-challenges dns -d zhuty.com 然后按照提示在DNS中添加TXT记录,并等待DNS记录生效。完成后,Certbot将生成证书,并将其保存在/etc/letsencrypt/live/zhuty.com/目录中。 创建一个Kubernetes Secret以存储Certbot证书和密钥: kubectl create secret tls zhuty-tls --key /etc/letsencrypt/live/zhuty.com/privkey.pem --cert /etc/letsencrypt/live/zhuty.com/fullchain.pem 创建一个Certificate资源以自动更新Certbot证书:

      Hero Image
      TypeScript问答

      请简述 TypeScript 的特点和优势。 答案:TypeScript 是一种由 Microsoft 开发和维护的静态类型检查的 JavaScript 超集。其特点和优势包括: 强类型:TypeScript 强制使用类型注解,使得代码更加规范、可读性更高、可维护性更好。 静态类型检查:TypeScript 可以在编译时检查类型错误,减少运行时错误和调试时间。 支持 ES6/ES7:TypeScript 支持最新的 ECMAScript 标准,并且会在未来的版本中支持更多的特性。 工具支持:TypeScript 有完善的开发工具支持,包括编辑器、调试器、构建工具等。 社区活跃:TypeScript 有一个庞大的社区和生态系统,可以方便地获取资料和使用第三方库。 TypeScript 中的接口是什么?请说明接口的语法和用途,并举例说明如何使用接口。 答案:在 TypeScript 中,接口是用来定义对象类型的规范。接口可以描述对象的属性和方法,从而规范对象的使用方式。接口的语法如下: interface InterfaceName { // 属性和方法的声明 } 其中,InterfaceName 是接口的名称,接口的内容包括属性和方法的声明。例如: interface Person { name: string; age: number; sayHello(): void; } 这个接口描述了一个 Person 类型的对象,包括 name 和 age 两个属性,以及一个 sayHello 方法。接口的用途是为了规范对象的使用方式,例如: function printPerson(person: Person): void { console.log(`name: ${person.name}, age: ${person.age}`); person.sayHello(); } 这个函数接受一个 Person 类型的对象作为参数,然后打印出对象的 name 和 age 属性,并调用对象的 sayHello 方法。如果传入的对象不符合 Person 接口的规范,TypeScript 编译器会给出错误提示。 TypeScript 中的泛型是什么?请说明泛型的语法和用途,并举例说明如何使用泛型。 答案:在 TypeScript 中,泛型是一种用来创建可重用代码的工具,可以使函数、类、接口等更加通用和灵活。泛型的语法如下:

        Hero Image
        Uinix 哲学

        Unix哲学是自下而上的,而不是自上而下的。Unix哲学注重实效,立足于丰富的经验。你不会在正规方法学和标准中找到它,它更接近于隐形的半本能的知识,即UNIX文化所传播的专业经验。 最著名的就是他提出的17条编程原则,经过时间和实践的锤炼,发展成为Unix哲学17条原则,在维基百科能搜到。 下面就来说说我对这17要原则的解读—— 1、模块化原则(Rule of Modularity) 原文:开发人员应该使用定义良好的界面连接简单的部分来构建程序,所以问题是本地的,部分程序可以在未来的版本中替换以支持新的功能。此规则旨在节省调试复杂,长期且不可读的代码的时间。 解读:这条规则,现在但凡学编程的人都知道,代码要模块化,这样不仅方便别人复用,自己也能更便捷的替换新代码。而实际上,不管是学习还是实践中,模块化原则都是非常好的一条原则,比如,我们学习写作,如果能将一篇文章分模块,并通过逻辑线索串联起来,就能形成一篇不错的文章,其实就是模块化原则在起作用,我们常说的格式化写作,就是这样的。因为模块是可以替换的,模块是组成一堵墙的单元结构,可以是漂亮的空心砖,也可以是纯色的实心砖。同样,工作中也很实用,将不同的大任务分解成不同的小人物和模块,逐个击破,也是非常实用的,关键点就在于模块化是可复用和可替换的。 2、清晰原则(Rule of Clarity) 原文:开发人员应该编写清晰的程序,就好像最重要的沟通是向开发人员读取和维护程序,而不是计算机。这个规则的目的是使代码在将来的代码中尽可能易读和易理解。 解读:清晰在编程中意味着当别人看你写的代码时,能明白其中的含义,同样的,学习中也应该这样,就像我们写作就是为了梳理清楚我们的思考,表达出来让别人理解一样,看上去是在码字,实际上是在和别人沟通交流。说一些模糊和含混的话是容易的,但是要想表达出想法,清晰是非常重要的。 3、和解原则(Rule of Composition) 原文:开发人员应该编写能够与其他程序轻松通信的程序。这条规则的目的是让开发人员把项目分解成小而简单的程序,而不是过于复杂的单片程序。 解读:也叫适当妥协原则,这个原则在人际交往中应用得更多,还有就是自我思维中用得多,比如,一天我们想要锻炼身体,跑5公里,于是感性会说,算了吧,有点冷,难得换衣服了吧,被窝很舒服,理性则会说,必须坚持,为了保持健康。于是,两者开始协商,最后协商好了以后,就变成了穿保暖一点的衣服去跑步,适当降低运动量。而在与人的交流中,我们有时也会面临自己的时间和别人时间冲突的时候,这时就会需要进行适当的和解以达成共识。和解原则更像一种处世原则,让我们不能一味的强调自己,而要照顾别人的感受。 4、分离规则(Rule of Separation) 原文:开发者应该将程序的机制与程序的策略分开;一种方法是将程序分成与该接口通信的前端接口和后端引擎。这条规则旨在通过允许改变策略,尽可能降低操作机制的不稳定性来防止错误引入。 解读:这个有点不好理解,实际上后来发展出来就是java里的按照接口编程,简单说,就是A按照接口统一的协议来通信B,B提供相对应的具体功能实现,两者是分开的,互补干扰,但是对达成的共识是没有任何异议的,一旦要改变这个共识,需要重新协商并做好约束。举个例子,比如汽车的轮胎,分离规则,就是说轮胎的制造商只需要按照统一的接口生产对应尺寸的轮胎就可以了,至于在哪里生产,用什么材料生产,汽车组装时并不用关心,而和轴承对接的发动机同样也可以是多样化的。 5、简单规则(Rule of Simplicity), 原文:开发人员应该设计简单的方法,通过寻找方法将程序系统分解成小而直接的合作件。这条规则的目的是阻止开发者写作“复杂而美丽的复杂性”,这是现实中容易出错的程序。 6、简约规则(Rule of Parsimony) 原文:开发人员应该避免编写大型程序。这一规则的目的是防止由于项目的所有者不愿抛弃显着的大量工作而导致失败或次优方法的过度投资。较小的程序不仅易于编写,优化和维护,弃用时更容易删除。 解读:这两条规则是同一个意思,如果按照现在时髦的话说,就是一切都要尽量的小,尽量的简便可执行。因为一旦没有朝着简单的方向去做,就会越来越庞大,这一点对于编程来说尤其重要,越是简单的程序,越是容易维护,也容易发现问题。而那些看上去很复杂的程序,大多数都是冗余和不必要的,而实际上,要想简单,有时需要的反而是更强大的归纳总结能力。 7、透明度原则(Rule of Transparency) 原文:开发人员应该设计可见性和可发现性,通过编写这样一种方式,他们的思维过程可以清楚地被未来的项目开发人员所看到,并使用输入和输出格式,以便识别有效输入和正确输出。此规则旨在减少调试时间并延长程序的使用寿命。 解读:这条原则容易被误解,对外部使用的人来说,只需要知道输入和输出就行了,比如计算器,按下数字进行加减乘除,只不过对于程序内部来说,透明是意味着要公开代码,这样才能更好的理解程序,方便改进程序。这条原则适用于自我提升,在反思中特别有用,比如写下了一天的工作思考,然后自己顺着写下的思路开始复盘自己一天的思考逻辑,哪些做得好,哪些做的不好。但是同样意味着,这样私密的东西,不一定都要告诉别人。 8、稳健性规则(Rule of Robustness) 原文:开发人员应该通过设计透明和可发现性来设计强大的程序,因为易于理解的代码更容易对复杂程序中无法预见的意外情况进行压力测试。此规则旨在帮助开发人员构建强大,可靠的产品。 解读:可靠性是我们一直都非常重视的,即便是移动互联网如此发达今天,我们依然会遇见,程序APP崩溃,手机卡机的情况,实际上,这也是我们常说的反脆弱性,遇见一些特定的意外情况时,我们能不能够应对和处理,就是我们平时在编写我们自己这个“程序”时最重要的事了,有的人可靠性很高,一般的小打击都是打不倒的,而有的人可靠性不那么高,一点点挫折就会奔溃。说的就是这样稳健性。 9、表示规则(Rule of Representation) 原文:开发人员在面对选择时应该选择使数据更复杂,而不是程序的逻辑,因为与复杂的逻辑相比,人类更容易理解复杂的数据。这条规则的目的是使任何开发项目的开发人员都可以使程序更易读,从而使程序得以维护。 解读:这条规则放在现在不是很适用了,因为有大数据,虽然人类擅长区分复杂的数据,但前提是数据量不是特别大,而按照今天大数据的量,还是更适合用机器去分析,有一门专业叫数据挖掘,专门干这个数据分析工作的。当然,逻辑清晰,数据详实,是很好的说明文体,也是更多增加文章的可信性的,我们现在的调查研究和综述报告就是这样的。换句话说,就是要有清晰的思路,多样的故事。 10、最小惊喜规则(Rule of Least Surprise) 原文:开发人员应该根据潜在用户的预期知识设计程序。例如,计算器程序中的“+”应该总是指“加法”。该规则旨在鼓励开发人员构建易于使用的直观产品。 解读:意味着要尽量的让每个单元有一个独立的功能,也是现在发展出来的微服务一说最早的出处了,现在因为大数据和分布式的关系,微服务越来越普及,换句话说,不仅是在编程里,即便在我们平时的生活中,也应该遵循这样的原则,在某个时间里,尽量的专心只做一件事,而不是想着要一心多用。 11、沉默的规则(Rule of Silence) 原文:开发人员应该设计程序,以免打印不必要的输出。这个规则旨在允许其他程序和开发者从程序的输出中挑出他们需要的信息,而不必分析冗长。 解读:意思本来是说,为了调试方便,程序员常常打很多日志,这样容易造成信息泄露或引起性能问题,但是,我觉得这条规则更像是简单规则的扩展,不过换个角度看,我们在思考的时候,需要适当的沉默,并不是所有的思考都要说出来,有的没有酝酿好的思考可以暂时放一放,不要急于去表达对一个观点的看法,应该尽可能多的搜集信息,再下结论。 12、修理规则(Rule of Repair) 原文:开发人员应该设计失败的程序,易于本地化和诊断,换句话说就是“失败”。这条规则旨在防止程序的错误输出成为输入,并破坏未被检测到的其他代码的输出。 解读:有错误的输入没有关系,关键是我们能不能调整并修复,就像现在很多人每天都接受很多垃圾信息一样,并没有意识到自己在接受拉结,更没有处理应对的方法,这个原则告诉我们,当我们有了可以修理的意识后,对于输入错误的输入是可以控制的,在软件测试里又叫边界测试——通过输入一些超过范围的数值或非常规操作来测试输入——这样可以验证系统的可靠性,一个软件系统是一定存在某种问题的,有问题不可怕,可怕的是不知道问题出在哪里。 13、经济规则(Rule of Economy) 原文:开发人员应该重视开发人员在机器上的时间,因为与上世纪70年代的价格相比,今天的机器周期相对便宜。这条规则旨在降低项目的开发成本。 解读:这个规则有点矛盾,一方面想要说人力成本的问题,一方面又说随着硬件价格的下降,成本的降低,我认为可以解释为,投入的成本和产出的成本,程序员的工作就是耗费时间和机器作斗争,让机器能按照人的意志而运行。付出成本是必然的,只要能在可接受的范围内就行了。 14、生成规则(Rule of Generation) 原文:开发人员应该避免手动编写代码,而是编写抽象的高级程序来生成代码。此规则旨在减少人为错误并节省时间。

          Hero Image
          yaml-lang

          1.注释:使用 # 符号表示注释,从 # 开始到行末都是注释内容。 # 这是一条注释 2.键值对:使用冒号(:)将键和值分隔开,键和值之间用空格分隔。 key: value 键可以是字符串或者未加引号的任意文本,值可以是任意类型的数据,包括字符串、数字、布尔值、列表、字典等。 3.列表:使用连字符(-)表示列表中的元素,每个元素占一行,列表可以包含任意类型的元素。 - item1 - item2 - item3 4.字面量字符串:使用竖线(|)表示字面量字符串,保留原始的换行和缩进。 key: | This is a multi-line string. 5.折叠式字符串:使用大于号(>)表示折叠式字符串,将换行符转换为空格,并且去掉末尾的空格。 key: > This is a folded string. 6.字典:使用花括号({})表示字典,每个键值对占一行,键和值之间用冒号分隔,键值对之间用逗号分隔。 person: name: John age: 30 gender: male 7.引用:使用 & 符号表示引用,使用 * 符号表示引用的值。 ref: &name John person: name: *name age: 30 8.空值:使用 ~ 符号表示空值。 key: ~ 9.标量类型:YAML 支持多种标量类型,包括字符串、整数、浮点数、布尔值、时间、日期等。 str: "hello, world" int: 123 float: 3.14 bool: true date: 2023-04-18 time: 12:34:56 YAML 的语法比较简洁明了,易于阅读和编写,适用于各种场景,例如配置文件、数据传输等。

            Hero Image
            简单网络爬虫实现

            背景 有个朋友需要将湖北碳排放权交易中心的成交概况数据总揽表.有将近2000页的数据, 如果考人工一个一个去复制粘贴, 估计她会砸电脑吧. 就是这个网页的表格内容: http://www.hbets.cn/index.php/index-show-tid-11.html 目标 把这个页面的所有页数据塞到一个excel里面 爬取原理 使用headless浏览器模拟人工将所有页面打开, 然后去抓取对应dom节点文本数据.支持这个操作的框架有很多, nightmare, phantomjs, puppeteer等, pyhon 环境下有 robot Framework. 这几个都有尝试过, 最后选用pupeteer. robot framework 简直就是反人类.前面三个都是nodejs环境运行的. 然, puppeteer 是google开源的, 同时, await async 加持, JS 开发者都知道这意味着什么. 核心代码 const puppeteer = require('puppeteer'), fs = require('fs'), $ = require('jquery'), path = require('path'); const XLSX = require("xlsx"); const WorkBook = require("./workbook"); let bigSheet = [ ['试点地区', '成交价', '当日成交量( 吨)', '当日成交额( 元)', '日期'] ]; (async () => { const browser = await puppeteer.launch({ executablePath: 'D:/Users/steven.zhu/AppData/Local/Chromium/Application/chrome.exe', headless: true, }); let getOnePage = async (url, maxPage) => { const page = await browser.newPage(); await page.goto(url, { waitUntil: 'networkidle2' }); await page.setJavaScriptEnabled(true); // await page.screenshot({ // path: 'hn.png', // format: 'A4' // }); await page.waitFor(1200); let oneSheet = await page.evaluate(() => { let sheet = []; let uls = $(".future_table ul.cont"); uls.each((i, obj) => { console.log(i, obj) let one = []; one.push($(".future_table ul.cont").eq(i).find('.li1').html()); one.push($(".future_table ul.cont").eq(i).find('.li2').html()); one.push($(".future_table ul.cont").eq(i).find('.li3').html()); one.push($(".future_table ul.cont").eq(i).find('.li4').html()); one.push($(".future_table ul.cont").eq(i).find('.li5').html()); sheet.push(one); }); return sheet;; }); bigSheet = [].concat(bigSheet, oneSheet) page.once('load', () => console.log('Page loaded!')); await page.close() url.match(/&p=(\w*)$/) if (RegExp.$1 == maxPage){ await setTimeout(()=>{ browser.close(); },5000) } }; (async () => { let getAllPage = (i, maxPage) => { console.log('抓取进度:', i + '/' + maxPage) getOnePage( "http://www.hbets.cn/index.php/index-show-tid-11.html?&p=" + i, maxPage ); if (i <= maxPage) { setTimeout(() => { getAllPage(++i, maxPage) }, 1000) } else { console.log('>>>>', bigSheet) workbook = new WorkBook({ Sheet1: bigSheet }) workbook.writeFile('xxx.xlsx') } } getAllPage(1, 1000) console.log('hehe') })() })() 结语 随手写的, 代码比较简单, 凑合看吧. 并不是多了几个文件夹名称就是架构好吗>_<

              Hero Image
              前端工程化与自动化实践

              本文由一缘原创整理,系统梳理前端工程化与自动化的核心理念、工具链与实战案例,适合所有前端/全栈开发者。 前端工程化与自动化实践 工程化是现代前端开发的必经之路,自动化让团队协作和交付效率大幅提升。 1. 模块化开发 ES6 Module、CommonJS、AMD 统一依赖管理,提升可维护性 // ES6 模块 import { add } from './utils.js'; export function sum(a, b) { return add(a, b); } 2. 自动化构建工具 Webpack、Vite、Rollup、Parcel 支持代码分割、Tree Shaking、热更新 // webpack.config.js module.exports = { entry: './src/index.js', output: { filename: 'bundle.js' }, module: { rules: [/* ... */] }, }; 3. 代码规范与质量保障 ESLint、Prettier、Stylelint Husky + lint-staged 实现提交前自动检查 // .eslintrc.json { "extends": ["eslint:recommended", "plugin:react/recommended"] } 4. 自动化测试 单元测试:Jest、Mocha、Vitest 端到端测试:Cypress、Playwright 持续集成自动跑测试 // Jest 示例 test('add', () => { expect(1 + 2).toBe(3); }); 5. 持续集成与自动化部署(CI/CD) GitHub Actions、GitLab CI、Jenkins 自动化测试、构建、部署 # .github/workflows/ci.yml name: CI on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install run: npm ci - name: Lint run: npm run lint - name: Test run: npm test - name: Build run: npm run build 6. 依赖管理与版本控制 npm、yarn、pnpm 语义化版本(semver)、自动升级工具(renovate) Git 分支管理(Git Flow、Trunk Based) 7. 环境变量与多环境配置 .env 文件管理不同环境变量 Vite/webpack 支持 process.env 注入 NODE_ENV=production API_URL=https://api.example.com 8. 性能监控与自动报警 Sentry、Lighthouse CI、Web Vitals 自动化性能分析与报警 9. 实战案例:自动化前端项目模板 一键初始化:npx create-react-app、npm create vite@latest 自动化脚本:npm run lint && npm test && npm run build CI/CD 集成,推送即自动部署 10. 工程化最佳实践 规范目录结构,分层清晰 自动化流程贯穿开发、测试、部署全链路 持续关注新工具与社区最佳实践 结语 前端工程化和自动化是高效团队和高质量项目的保障。持续优化工程流程,才能在激烈的技术竞争中立于不败之地。欢迎留言交流更多工程化实战!