Marco Nie - 2021年2月
type: channel
link: https://blog.niekun.net/2021/02/
description: you are the company you keep...
items: 修复 vs code 下保存文件会自动 format 代码的问题
items: React 入门教程之五 -- Event
items: React 入门教程之四 -- rendering, components 和 state
items: React 入门教程之三 -- 介绍 JSX
items: React 入门教程之二 -- hello world
items: React 入门教程之一 -- 简介
items: node.js 入门教程之十四 -- TypeScript
items: node.js 入门教程之十三 -- environment，error handler 和 log object
items: node.js 入门教程之十二 -- Streams 流
items: node.js 入门教程之十一 -- buffers 缓存

修复 vs code 下保存文件会自动 format 代码的问题
type: item
date: 2021-02-25T12:46:37+00:00
description: 今天在开发 React 时发现每次保存 js 文件都会自动改变代码格式导致一大堆报错：经过查询发现时由于安装的 JS-CSS-HTML Formatter 插件默认会在保存文件时对文件格式进行处理。但是堆 React 代码的自动处理有问题导致无法正常进行开发。只需要修改其配置文件将保存文件时自动处理关掉即可。快捷键 cmd + shift + p 打开控制器，输入 formatter：点击 formatter config 修改配置文件，将第一句 "onSave": true 改为 false 保存即可。需要重启 vscode 才能生效。

React 入门教程之五 -- Event
type: item
date: 2021-02-25T09:23:00+00:00
description: events 处理处理 React elements events 和处理 DOM elements 很相似，但有一些语法区别：React events 命名使用 camelCase 规则，而不是 lowercase使用 JSX 传入 function 作为 events handler，而不是 string 字符串HMTL 中处理 events 示例如下： Activate Lasers React 中示例如下： Activate Lasers 注意它们的区别之处一个是 event 名称，一个是 handler 定义方式。另一个区别是在 React 中不能通过 return false 的方式防止 events 的默认行为，需要明确的调用 preventDefault method 来实现。例如在一个 html 页面中定义一个 a tag 并取消其默认打开新页面的行为，实现如下： Click me React 中实现同样功能代码如下：class Link extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick(e) { e.preventDefault(); console.log('clicked me'); } render() { return ( click me ) } }e 表示 synthetic 综合的 event，当前哪个 event 触发了 e 就表示哪一个。使用 bind 绑定 的 method 在调用时会自动将 e 传入 method。下面会对 bind 是什么作出解释。React events 同原生的 events 不完全相同，查看所有可用的 events 查看官方介绍：https://reactjs.org/docs/events.html在 React 中一般情况下不需要通过调用 addEventListener 来给 element 添加 event listener。直接在 element 初始化时为其设置 event listener 即可。当通过 class 来定义 component 时 event handler 一般是一个 class method，例如上面示例的 handleClick。下面的示例我们构建一个 Toggle component 可以让用户通过一个 button 来切换 ON/OFF 状态：const React = require('react') const ReactDOM = require('react-dom') class Toggle extends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState({ isToggleOn: !this.state.isToggleOn }); } render() { return ( { this.state.isToggleOn ? 'ON' : 'OFF'} ); } } ReactDOM.render(< Toggle />, document.getElementById('root') );在 JSX callback 中使用 this 需要特别注意，JavaScript class 的 methods 默认是相互隔离的，如果没有主动 bind 捆绑 method 到 this，在另一个 method 中使用 this.method 会报错 undefined。如果调用 method 时不写括号() 例如：onClick={this.handleClick} 则需要提前 bind 这个 method 到 this 中，如上面的示例，bind 语法如下：this.handleClick = this.handleClick.bind(this); 一般将其放在 constructor 中，这样初始化中就会自动执行，当然也可以在调用时直接定义： { this.state.isToggleOn ? 'ON' : 'OFF'} 如果不想使用 bind 语法来处理，那么还有两种方式来处理 class 中 methods 互相隔离这个问题。第一种叫做 class fields syntax 语法，通过使用 arrow function 的模式定…

React 入门教程之四 -- rendering, components 和 state
type: item
date: 2021-02-24T09:17:00+00:00
description: rendering elements 渲染元素一个 element 表示我们想要显示在屏幕上的内容：const element = Hello, world ; 不同于浏览器 DOM 中的 elements，React elements 是简单的 objects 且可以很方便的创建，React DOM 会严格的刷新 DOM 并匹配对应的 React elements。容易混淆的概念是 component 和 element，区别是 component 是用来创建 element 的。在后续章节会介绍。在 DOM 中渲染元素我们的 html 页面中定义了一个 div 容器： 我们将其称作 root DOM 节点，因为它所有的内容都是被 React DOM 管理的。通常情况下使用 React 创建的程序只有一个 root DOM 节点。如果你是将 React 整合到现有网站中，你可以有任意个独立的 root DOM 节点。将 React elements 渲染到 root DOM 节点，需要通过调用 ReactDOM.render()，并将 React element 和 root DOM 节点作为传入参数：const element = Hello, world ; ReactDOM.render( element, document.getElementById('root') );此时页面会显示 hello world。刷新渲染的元素React element 是 immutable 不可改变的，当创建了一个 element 后不可以修改其 children 或 attributes，一个 element 就好像一个视频的一帧，它表示了某一时间点的 UI。从我们目前学到的知识，唯一刷新 UI 的方法就是重新创建新的 elements 然后调用 ReactDOM.render()，通过设置 setInterval 来定时刷新：const tick = () => { const element = {new Date().toLocaleTimeString()} ; ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000);这样就会每秒钟创建一个新的 element 并通过 ReactDOM.render() 渲染到界面。通常情况下大多数 React app 只会调用 ReactDOM.render() 一次。下一章节会介绍如何将封装到 component 中。React 只会更新必要的内容React DOM 会比较其当前和上一个状态的，然后只对有了变化的部分进行更新来达到最终期望的状态。我们打开上面示例的运行页面，通过chrome 的开发工具查看 elements 情况，可以看到只有时间元素每秒在刷新：![2021-02-24T07:44:17.png][即使我们每秒钟都新建并渲染 element，但是只有时间文本 node 是一直通过 React DOM 在刷新的。通过以上的实验，思考我们的 UI 在某个时间点应该是什么样的，而不是只想这着去修改它。components 和 propscomponents 将 UI 元素分割为独立的，可复用的片段，每个片段都是单独存在的。这一章节介绍 component 的概念，更多细节参考：React.Componentcomponents 类似于 JavaScript 的 functions，它可以接受抽象的输入数据(props)，然后返回 React elements 用来在界面上显示。Function 和 Class Components最简单的定义 component 方式就是定义一个 JavaScript function：const Welcome = (props) => { return hello, {props.name} }上面的 function 是一个有效的 React component，因为它接受一个单参数 props object 作为传入数据并返回一个 React element。我们称这种 component 为 function component。也可以使用 ES6 的 class 定义 component：class Welcome extends React.Component { render() { return hello, {this.props.name} } }以上两种定义方式是一致的。需要注意的是 components 名称必须是以大写字母开头，因为 React 会见以小写字母开头的 components 作为 D…

React 入门教程之三 -- 介绍 JSX
type: item
date: 2021-02-24T06:45:12+00:00
description: 请看下面的一个定义：const element = Hello, world! ; 它既不是一个 string 也不是 html。它叫做 JSX，是一种对 JavaScript 语法的扩展。推荐在 React 中使用 JSX 来描述 UI，它用来创建 React 类型的 elements 然后将其在 DOM 中渲染。下面对 JSX 做一些基本介绍。使用 JSX 的原因React 认同这种观点：渲染逻辑结构和 UI 逻辑结构是原生相互耦合的，events 的处理，状态的变化，数据何时显示等。不同于人为的将 markup 部分和 logic 逻辑部分放在单独的文件，React 的耦合单元叫做 components 可以同时包含 markup 和 logic 部分。React 并不强制要求使用 JSX，但是大部分开发者认为它可以帮助在 JavaScript 中处理 UI 的问题。它也可以帮助 React 显示更多的 error 或 warning 信息。JSX 中使用 JavaScript 表达式下面的示例中，我们声明一个变量并在 JSX 中通过大括号{} 调用变量：const name = 'marco nie'; const element = hello, {name} ReactDOM.render( element, document.getElementById('root') );我们可以将任意的 JavaScript 表达式通过大括号{}放如 JSX 中。例如：1 + 1，user.name 都是有效的 JavaScript 表达式：const city = 'han zhong'; const user = { name: 'marco', age: 20 } const element = hello, {user.name}, from {city}, time: {1 + 20} 可以将 JSX 分割成多行以提高代码可读性，需要使用小括号包围起来，防止编译器错误的自动添加分行符：const element = ( hello, {user.name}, from {city}, time: {1 + 20} );编译后 JSX 表达式会转换为通用的 JavaScript function 或 objects。这就意味着我们可以在 if 或 for 表达式中，传入数据中或者 返回值中使用 JSX：const testFuc = () => { return hello, {user.name} }JSX 中定义属性可以使用引号 "" 将一个字符串作为属性：const el1 = 可以以通过大括号{} 将一个 JavaScript 表达式嵌入属性：const el2 = 当使用 JavaScript 嵌入属性时不能在大括号中使用引号，同一个属性中只能使用引号嵌入字符串或者大括号嵌入 JavaScript 表达式中的一种。由于 JSX 更加接近于 JavaScript 而不是 html，所以 React 中的 DOM 使用 camelCase 属性命名规范代替 html 中的属性名称，例如 html 元素的 class 属性在 JSX 中定义为 className，tabindex 在 JSX 中为 tabIndex。定义子元素如果一个 tag 元素内容是空的则可以使用 /> 立刻结束定义：const el3 = JSX tag 标签内也可以有子元素：const el4 = ( hello, i am your friend );注意只能在一个元素内定义子元素，不能直接定义两个同级的元素否则会报错。防止 injection 注入攻击在 JSX 中嵌入用户输入是安全的：const title = response.potentiallyMaliciousInput; // This is safe: const element = {title} ;默认情况下 React 在渲染前会将脱开所有嵌入 JSX 中的数据，因此可以确保不会注入任何没有明确定义在应用中的数据。渲染前所有的所有内容都会转换为 string 字符串形式。则能够防止 XSS (cross-site-scripting) 攻击。JSX 表达 objectsBabel 会将 JSX 向下编译为对 React.createElement() 的调用，以下两种定义方法是相同的：const el5 = ( hello world );const el6 = React.createElement( 'h1', {className: 'test'}, 'hello world' )通过 React.createElement() 创建元素会额外做一些语法检查…

React 入门教程之二 -- hello world
type: item
date: 2021-02-24T04:40:18+00:00
description: 下面我们通过创建 Create React App 介绍 React 的基本语法。通过上一节介绍的方法创建一个新程序 my-app-1:npx create-react-app my-app-1 cd my-app-1关于创建新项目及可能出现的问题参考上一篇：https://blog.niekun.net/archives/2175.htmlCreate React App 官方教程：https://create-react-app.dev/docs/documentation-intro项目结构程序目录结构如下：my-app-1 ├── README.md ├── node_modules ├── package.json ├── .gitignore ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src ├── App.css ├── App.js ├── App.test.js ├── index.css ├── index.js ├── logo.svg ├── serviceWorker.js └── setupTests.js对于一个项目而言，下面两个文件是必须的:public/index.html 是页面的模板文件;src/index.js 是 JavaScript 入口文件.其他文件可以根据需要添加，删除或重命名。可以在 src 目录下建立子目录，为了加快 build 编译项目的速度，只有在 src 目录下的文件才会被 webpack 处理。所以需要将所有的 js 和 css 文件放在 src 目录下，否则 webpack 打包器不会识别到它们。只有在 public 目录下的文件才会被 public/index.html 文件识别到。所以所有的项目文件都需要放在 public 和 src 目录下，与项目无关的文件可以放在上级路径内，例如项目说明文件等。可用脚本指令在项目目录下，可以执行如下命令。npm start开发模式下运行 app：npm start 打开 http://localhost:3000 可以查看效果。对代码作出修改后页面会自动重新加载。npm test交互模式下运行 test 运行器。更多介绍参考：https://create-react-app.dev/docs/running-testsnpm run build编译项目程序到 build 目录下，会自动将 React 转换为产品模式，并优化项目结构以提高性能。代码是经过 minified 最小化处理过的，文件名都包含 hashes 以在客户端能够及时识别更新。更多 build 参考：https://create-react-app.dev/docs/production-build清理项目默认使用的模板是 cra-template，包含一个基本的页面和 React component，我们先将项目清理掉不需要的内容。打开 public 目录下的 index.html 可以看到 body 块内定义了： index.html 内可以使用 %PUBLIC_URL% 表示 public 目录地址。删除 src 目录内所有文件，并新建 index.js 文件。运行程序：npm start 会自动在浏览器打开项目。hello world最简单的 React 示例如下，将代码加入 index.js 文件：const React = require('react') const ReactDOM = require('react-dom') ReactDOM.render( hello world , document.getElementById('root') );保存文件后刷新浏览器页面查看效果。

React 入门教程之一 -- 简介
type: item
date: 2021-02-23T07:39:00+00:00
description: React 是一个用来创建用户界面的 JavaScript 库。它从一开始就被设计为 gradual adoption 渐进式融入的模式，你可以根据需求使用 React 到你的项目中，无论是使用 React 对你的 html 页面做简单交互，或者完全使用 React 搭建程序。对现有网页加入 React 支持当你需要对现有的网页做一些简单的交互动作时，React 也可以方便的加入进来。下面的示例中我们介绍如何通过 React 给页面添加一个 component 元素。首先给 html 页面添加一个 DOM 元素： 然后在 body 块结尾添加 React 模块的 script 脚本标签： 前两个 script 加载 React，最后一个是我们加载自定义 component 的代码。如果 script 部署在 CDN 上，推荐加上 crossorigin 属性可以提高加载速度。然后我们使用 React 编写 like_button.js，详细的实现代码我们在后续介绍，最后我们通过下面指令将 React component 加入 html 容器：// ... the starter code you pasted ... const domContainer = document.querySelector('#like_button_container'); ReactDOM.render(e(LikeButton), domContainer);以上就是将 React component 加入现有网页的过程。缩小 JavaScript 文件尺寸在部署我们的网站前，要注意没有经过缩小化处理的 JavaScript 脚本会导致页面加载缓慢。首先我们将加载 React 的脚本使用缩小化的版本链接： 对于我们自己编写的 React 脚本可以通过工具得到缩小后的版本，这里介绍通过 node.js 的 terser 库来实现：首先安装 node.js在项目目录下运行 npm init -y运行 npm install terser例如需要缩小 like_button.js，在终端运行：npx terser -c -m -o like_button.min.js -- like_button.js 就会在当前目录下得到缩小后的脚本文件 like_button.min.js。创建一个新的 React 程序以上介绍了如何将 React 添加到现有的 html 中，但是通过一套整合的工具链可以得到更好的用户和开发体验。React 团队推荐下面几种解决方案：如果你要学习 React 或者要创建一个单页面 app，推荐使用 Create React App如果你要使用 node.js 创建一个 server-rendered 服务器端渲染的网站，推荐使用 Next.js如果你要建立一个静态内容的网站，推荐使用 GatsbyCreate React AppCreate React App 是学习 React 很好的环境，也是建立单页面 React 应用程序最好的方式。官方网站：https://create-react-app.dev/它会自动设置开发环境是我们可以使用最新的 JavaScript 功能，提供很好的开发体验以及对发布时对程序进行优化。使用下面指令创建并运行一个新项目：npx create-react-app my-app cd my-app npm start注意如果你之前使用 npm install -g create-react-app全局安装过 create-react-app，推荐先卸载它 npm uninstall -g create-react-app 从而确保 npx 能够安装最新的版本。node.js 会自动安装 react, react-dom, 和 react-scripts with cra-template。通过 --template 选项可以自定义使用什么模板创建新程序，默认为：cra-template，模板可以在官网搜索：cra-template-*如果安装中报错，可以尝试更新 npm 以及清除缓存，然后再次尝试安装：npm i -g npm@latest npm cache clean -f 如果在本地开发推荐替换 npm 国内源可以提高下载速度，参考：https://blog.niekun.net/archives/2085.html创建 React App 不会处理后端的数据和逻辑，它只建立前端通道。所以你可以使用任何后端服务。通过 npm start 运行程序后，打开 http://localhost:3000/ 可以查看程序运行效果。当程序需要进行部署时，运行 npm run build 会在 bui…

node.js 入门教程之十四 -- TypeScript
type: item
date: 2021-02-21T14:02:00+00:00
description: TypeScript 是微软开发的开源编程语言，在全世界的开发者中流行。本质上来说 typescript 是 JavaScript 的超集，为其添加了很多功能。标志性的就是添加了 static type 静态类型定义，它可以声明期待的数据类型，例如：声明 function 中的期待的传入数据类型，期望的返回值的类型，object 的结构及 property 的数据类型等。typescript 是一个强大的工具，给 JavaScript 项目开启了新的世界。它使我们的代码更加的安全有力，在程序发布前就可以防止很多的 bug 出现。在编写代码期间就能够及时反映出问题点，并且它已经被整合在现代的编译器中，如 VS code。语法说明下面是一个 typescript 代码示例：type User = { name: string; age: number; }; function isAdult(user: User): boolean { return user.age >= 18; } const justine: User = { name: 'Justine', age: 23, }; const isJustineAnAdult: boolean = isAdult(justine);首先我们通过 type 关键词定义一个自定义的 object 类型 User，其中定义了 User 的两个 properties 及其数据类型。然后在定义的 function isAudlt 中接收一个参数，此参数类型为 User 类型，然后返回值类型为 boolean 类型。然后我们定义了 justine object 且其类型为 User。在其中定义的两个 properties 也符合 User 中的类型。最后定义了变量 isJustineAnAdult 且其数据类型为 boolean，将 isAudlt 的返回值赋给它。如果我们在编程中没有使用指定的数据类型，typescript 将会给出警告提醒我们错误的地方，比如上面的示例中修改 justine 的 age 为一个字符串：在 vs code 中会提示此处有错误。另一方面也并不需要将所有地方的数据类型都做出声明，typescript 会自动推断出需要的类型。例如：即使我们不给 isJustineAnAdult 声明 boolean 类型，typescript 也会自动推断它是 boolean 类型的。那么如何运行 typescript 代码呢？首先需要通过 npm 安装 typescript，它会安装我们需要的一些可执行程序：npm install typescript 下面我们可以通过 tsc 命令将 typescript 代码转换为标准 JavaScript 代码。我们将上面示例中的代码保存为 demo.ts 文件，然后通过 npx 调用 tsc 命令(如果以 global 方式安装 typescript 则可以直接运行命令)：npx tsc demo.ts 以上命令会转换 JavaScript 代码并自动创建一个 demo.js 文件，此文件可以在 node.js 运行:function isAdult(user) { return user.age >= 18; } var justine = { name: 'Justine', age: 23 }; var isJustineAnAdult = isAdult(justine);如果 typescript 代码中包含错误，则使用上面的转换命令会出现报错信息：% npx tsc demo.ts demo.ts:12:5 - error TS2322: Type 'string' is not assignable to type 'number'. 12 age: '23', ~~ demo.ts:3:5 3 age: number; ~~ The expected type comes from property 'age' which is declared here on type 'User' Found 1 error.可以看到 typescript 成功的防止将包含潜在错误代码的程序发布出去。更多介绍除此之外 typescript 也包含了很多其他很好的工具，如：interfaces, classes, utility types 等。更多使用方法参考 typescript 官方文档：https://www.typescriptlang.org/docs。typescript 在 node.js 生态中已经有了很好的发展，且被用于很多开源项目和框架中。以下是一些使用 typescript 的开源项目：NestJS -…

node.js 入门教程之十三 -- environment，error handler 和 log object
type: item
date: 2021-02-20T09:19:00+00:00
description: environment 运行环境production environment 产品环境和 development environment 开发环境可以设置不同的配置。node.js 默认为 development env 开发环境。通过设置环境变量：NODE_ENV=production 可以告诉 node.js 当前为产品环境。Linux 中可以通过如下指令修改：export NODE_ENV=production 这样在当前 shell 中就会以 production 模式运行 node 程序，如果要永久修改此环境变量可以将命令写入 ~/.bashrc 文件。也可以在运行 node 程序时定义 NODE_ENV：NODE_ENV=production node app.js 设置 NODE_ENV 为 production 有如下优势：日志记录保持在最小 level更多的 caching 缓存级别来提高运行效率可以通过状态判读符来判断当前运行环境，执行不同指令：if (process.env.NODE_ENV === 'development') { console.log('running on development environment') } if (process.env.NODE_ENV === 'production') { console.log('running on production environment') } if (['production', 'staging'].indexOf(process.env.NODE_ENV) != -1) { console.log('running on production environment') } else { console.log('running on development environment') }上面第三个指令通过调用 array 的 indexOf 来获取某个字符串在数组中的索引号，如果不存在这个字符串则返回 -1。注意使用类型判断符=== 是为了保证类型和数值都匹配。error handling 错误处理node.js 中通过 exceptions 来处理 error 情况。可以通过 throw 创建一个 exception：const test = 123; throw test;当 JavaScript 执行到上面的 throw 时程序会立刻停止，然后控制权会交给最近的 exception handler 例外控制器。JavaScript 中 test 可以是任意的字符串，数字或者 object。但在 node.js 中只用来 throw Error objects。一个 error object 可以是 Error 的实例或者是 Error class 的 child class 的实例：throw new Error('error') class childError extends Error { } throw new childError('error')下面介绍几种 exception handler 处理器。第一种是通过 try/catch 声明来处理，在 try 中定义任意 exception 以及对应的 catch 块来响应：try { const a = 1; if (a == 0) { throw 'abc' } else { throw 123 } } catch (e) { console.log(e) }catch 的参数 e 就是对应 throw 的数据。如果在程序执行中，如果某个 throw 的 exception 没有被 catch，程序就会立刻 crash。可以通过监听 process 的 uncaughtException event 事件来解决这个问题：process.on('uncaughtException', err => { console.log('there was a uncaught error: ', err.message) process.exit(1) }) throw new Error('error')第二种是 promise 中的 error handler。使用 promise 可以将多个操作过程组合成链，在链的最后处理 error 情况，下面是一个简单示例：let a = 0; let b = 0; const doSomething1 = new Promise((resolve, reject) => { if (a == 0) resolve('resolve 1') else reject('reject 1') }) const doSo…

node.js 入门教程之十二 -- Streams 流
type: item
date: 2021-02-20T06:43:00+00:00
description: Streams 是驱动 node.js 程序的核心概念。它提供了处理对文件的读写，网络传输，或者其他端到端的数据交换的更加高效的方式。streams 流并不是 node.js 首先引入的概念，unix 操作系统在很久之前就在使用了，一个程序可以通过 pipe 管道操作符 | 来传递 streams 流给其他程序。下面的示例是在 Linux 中，通过 pipe 管道将 cat 读取的文件数据传递给 grep 进行过滤，test.txt 文件内容如下：aaa bbb bbb ccc aaa ccc$ cat test.txt | grep aaa aaa bbb aaa cccstream 优点在传统方法中，当程序读取一个文件内容时会先将文件内容全部读取到内存中，然后再去使用它。而使用 stream 时会一段段的读取文件内容并进行处理，而不需要整体读取到内存。node.js 的 stream 模块是构建所有 streaming API 的底层。所有的 streams 流都是 eventEmitter 的实例。每种类型的 stream 都有各自的 event 事件，在数据流变化时触发对应事件。相比于其他数据处理方法，streams 有两个优势：内存效率：在使用数据前不需要将大量数据存入内存中。时间效率：能够更快的进入数据处理阶段，在获取到文件后能够很快的使用它而不需要等待它全部加载完毕。使用方法下面我们从一个示例来说明 stream 的使用方法。我们建立一个 http server，当收到请求时读取一个本地文件的内容并作为 response 发送给客户端。首先传统实现方式如下：const http = require('http') const fs = require('fs') const hostname = '127.0.0.1' const port = 3000 const server = http.createServer((req, res) => { fs.readFile(__dirname + '/test.txt', (err, data) => { if (err) { console.log('read error') } res.end(data) }) }) server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`) })以上方式在收到请求后，通过 readFile() 读取文件内容到内存中，当成功后会调用 callback 通过 res.end(data) 发送读取的数据给客户端。如果文件较大时以上过程会花费一些时间。__dirname 返回当前执行文件的路径。下面介绍使用 stream 实现上面的过程：const http = require('http') const fs = require('fs') const hostname = '127.0.0.1' const port = 3000 const server = http.createServer((req, res) => { const stream = fs.createReadStream(__dirname + '/test.txt') stream.pipe(res) }) server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`) })通过 fs.createReadStream() 返回一个文件的 stream。不同于第一种，以上方式会在 stream 中只要有了 data chunk 块就立刻作为 response 数据传输给客户端。这样就会一边读取文件一边传输数据。pipe()以上示例中调用 stream 的 pipe() method。它的功能是建立一个 source stream 源流到 destination stream 目标流的 pipe 管道。这样文件的 stream 流通向了 http response。pipe() 的返回是 destination stream 目标流，这样就可以很方便的链接多个 pipe：src.pipe(dest1).pipe(dest2)以下写法和上面示例效果一样：src.pipe(dest1) dest1.pipe(dest2)基于 stream 的 node.js API由于 stream 的巨大优势，很多 node.js 核心模块提供了 stream 的原生支持，以下是常用的部分：proces…

node.js 入门教程之十一 -- buffers 缓存
type: item
date: 2021-02-16T09:26:00+00:00
description: 一个 buffer 缓存就是一个内存空间。JavaScript 开发者相较于 c++/go 等语言的开发者对内存的理解比较少。它表示在 JavaScript V8 engine 之外的分配的一块 fixed-size 固定大小的内存。可以想象 buffer 就是一个 integer 整型数组，每个元素代表一个 byte 的数据。node.js 中 buffer 通过 Buffer class 实现。为何需要 buffer引入 Buffer 是为了帮助开发者处理 binary 二进制数据。传统的 ecosystem 只可以处理 strings 字符串数据。Buffer 和 streams 是紧密关联的，当 stream processor 流处理器接收数据的速度大于其处理速度时，就会先将数据放在 buffer 中。可以想象下，在我们看在线视频时，在网络较好的情况下进度条中可以看到视频下载的进度是超过当前播放进度的，此时浏览器就会先 buffer 缓存数据后续按时间顺序播放出来。创建 buffer可以通过 Buffer.from(), Buffer.alloc() 和 Buffer.allocUnsafe() methods 来创建一个 buffer，下面依次介绍。使用 Buffer.from() 可以从 array 数组创建 buffer：const buf1 = Buffer.from('hey!') 使用 Buffer.from(string[, encoding]) 可以设置 string 的编码类型，Default: utf8：const buf2 = Buffer.from('7468697320697320612074c3a97374', 'hex') 也可以根据另一个 buffer：const buf3 = Buffer.from(buf1) 通过 Buffer.alloc() 或 Buffer.allocUnsafe() 来初始化一个指定大小的 buffer，单位为 byte 字节：const buf4 = Buffer.alloc(1024) const buf5 = Buffer.allocUnsafe(1024) 以上会创建一个 1kb 大小的 buffer。通过 alloc 创建的 buffer 会初始化其数据为 0，而 allocUnsafe 创建的 buffer 内的数据会是 uninitiated 未初始化的，这就表示使用 allocUnsafe 比 alloc 创建速度更快。但是通过 allocUnsafe 创建的 buffer 内存空间会包含有以前的数据可能引起潜在问题。使用 buffer我们可以像访问数组一样访问 buffer 数据：const buf1 = Buffer.from('hey!') console.log(buf1[0]) console.log(buf1[1]) console.log(buf1[2]) //output: //104 //101 //121这些输出的数字就是代表 buffer 中每个字符的 Unicode Code，h => 104, e => 101, y => 121。使用 toString() method 可以将 buffer 输出为字符：console.log(buf1) console.log(buf1.toString()) //output: //<Buffer 68 65 79 21> //hey!buf1.length 可以读取 buffer 长度：console.log(buf1.length) //output: //4也可以使用循环 iterate buffer 的数据：for (const iterator of buf1) { console.log(iterator) } //output: 104 101 121 33通过 write() method 可以给 buffer 写入数据：const buf6 = Buffer.alloc(5) buf6.write('hello')也可以像数组一样修改某个元素的数据：const buf6 = Buffer.alloc(5) buf6.write('hello') buf6[0] = 111 console.log(buf6.toString()) //output: //oello可以看到需要修改字符对应的 Unicode Code。使用 copy() method 可以复制 buffer 数据到其他 buffer：const buf1 = Buffer.from('hey!') const buf7 = Buffer.alloc(4) buf1.copy(buf7) cons…