reading-notes's Issues
Egg.js,Express.js 和 Koa.js 的异同
Express
Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
Feature:
- With plenty utility methods and middleware.
- It is a thin layer of fundamental web application features, without obscuring Node.js features.
Koa.js
Koa is a new web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation for web applications and APIs. By leveraging async functions, Koa allows you to ditch callbacks and greatly increase error-handling. Koa does not bundle any middleware within its core, and it provides an elegant suite of methods that make writing servers fast and enjoyable.
Difference between Koa and Express
- Middleware
Koa 的中间件和 Express 不同,Koa 选择了洋葱圈模型。
所有的请求经过一个中间件的时候都会执行两次,对比 Express 形式的中间件,Koa 的模型可以非常方便的实现后置处理逻辑
- Context
和 Express 只有 Request 和 Response 两个对象不同,Koa 增加了一个 Context 的对象。对于需要挂在 traceId 这种贯穿整个请求的属性来说,挂在 Context 上,相较于 request 和 response 而言更加符合语义。
Egg.js
Egg 继承于 Koa,Koa 是一个非常优秀的框架,然而对于企业级应用来说,它还比较基础。而 Egg 选择了 Koa 作为其基础框架,在它的模型基础上,进一步对它进行了一些增强。
- 扩展
在基于 Egg 的框架或者应用中,我们可以通过定义
app/extend/{application,context,request,response}.js
来扩展 Koa 中对应的四个对象的原型,通过这个功能,我们可以快速的增加更多的辅助方法
- 插件
在 Express 和 Koa 中,经常会引入许许多多的中间件来提供各种各样的功能,例如引入 koa-session 提供 Session 的支持,引入 koa-bodyparser 来解析请求 body。而 Egg 提供了一个更加强大的插件机制,让这些独立领域的功能模块可以更加容易编写。
一个插件可以包含
- extend:扩展基础对象的上下文,提供各种工具类、属性。
- middleware:增加一个或多个中间件,提供请求的前置、后置处理逻辑。
- config:配置各个环境下插件自身的默认配置项。
Reference:
Generator函数的用法——阮一峰
Egg.js 与 Koa
node-elm package analysis - mongoose - to be continued
mongoose
Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.
Connecting to MongoDB
If your app uses only one database, you should use mongoose.connect. If you need to create additional connections, use mongoose.createConnection.
Both connect and createConnection take a mongodb:// URI, or the parameters host, database, port, options.
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/elm');
Once connected, the open event is fired on the Connection instance. If you're using mongoose.connect, the Connection is mongoose.connection. Otherwise, mongoose.createConnection return value is a Connection.
Note: If the local connection fails then try using 127.0.0.1 instead of localhost. Sometimes issues may arise when the local hostname has been changed.
Important! Mongoose buffers all the commands until it's connected to the database. This means that you don't have to wait until it connects to MongoDB in order to define models, run queries, etc.
Defining a Model
Models are defined through the Schema interface.
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var BlogPost = new Schema({
author : ObjectId,
title : String,
body : String,
date : Date
});
Abstract Factory
相关模式
- AbstractFactory类通常用工厂方法实现,但它们也可以用 Prototype实现。
- 一个具体的工厂通常是一个单件。
意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
适用性
在以下情况下可以使用Abstract Factory
- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品系列中的一个来配置时。
- 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时。
结构
此模式的结构图如下图所示:
参与者
AbstractFactory (WidgetFactory)
- 声明一个创建抽象产品对象的操作接口。
ConcreteFactory (MotifWidgetFactory,PMWidgetFactory)
- 实现创建具体产品对象的操作。
AbstractProduct (Windows,ScrollBar)
- 为一类产品对象声明一个接口。
ConcreteProduct (MotifWindow,MotifScrollBar)
- 定义一个将被相应的具体工厂创建的产品对象。
- 实现AbstractProduct接口。
Client
- 仅使用由AbstractFactory和AbstractProduct类声明的接口。
协作
- 通常在运行时刻创建一个ConcreteFactroy类的实例。这一具体的工厂创建具有特定实现
的产品对象。为创建不同的产品对象,客户应使用不同的具体工厂。 - AbstractFactory将产品对象的创建延迟到它的 ConcreteFactory子类。
效果
AbstractFactory模式有下面的一些优点和缺点:
- 它分离了具体的类 Abstract Factory模式帮助你控制一个应用创建的对象的类。因为
一个工厂封装创建产品对象的责任和过程,它将客户与类的实现分离。客户通过它们的抽象 接口操纵实例。产品的类名也在具体工厂的实现中被分离;它们不出现在客户代码中。 - 它使得易于交换产品系列 一个具体工厂类在一个应用中仅出现一次— 即在它初始化 的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同 的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻 改变。在我们的用户界面的例子中,我们仅需转换到相应的工厂对象并重新创建接口,就可 实现从Motif窗口组件转换为Presentation Manager窗口组件。
- 它有利于产品的一致性 当一个系列中的产品对象被设计成一起工作时,一个应用一 次只能使用同一个系列中的对象,这一点很重要。而 AbstractFactory很容易实现这一点。
- 难以支持新种类的产品 难以扩展抽象工厂以生产新种类的产品。这是因为 AbstractFactory接口确定了可以被创建的产品集合。支持新种类的产品就需要扩展该工厂接口, 这将涉及 AbstractFactory类及其所有子类的改变。我们会在实现一节讨论这个问题的一个解决办法。
实现
下面是实现 Abstract Factor模式的一些有用技术:
- 将工厂作为单件 一个应用中一般每个产品系列只需一个 ConcreteFactory的实例。因此
工厂通常最好实现为一个 Singleton。 - 创建产品 AbstractFactory仅声明一个创建产品的 接口 ,真正创建产品是由
ConcreteProduct子类实现的。最通常的一个办法是为每一个产品定义一个工厂方法。一个具体的工厂将为每个产品重定义该工厂方法以指定产品。虽然 这样的实现很简单,但它却要求每个产品系列都要有一个新的具体工厂子类,即使这些产品 系列的差别很小。 - 如果有多个可能的产品系列,具体工厂也可以使用 Prototype(3.4)模式来实现。具体工 厂使用产品系列中每一个产品的原型实例来初始化,且它通过复制它的原型来创建新的产品。在基于原型的方法中,使得增加新的系列可以不再必须增加一个新的具体工厂,只需要初始化相应的产品原型即可。
- 定义可扩展的工厂 AbstractFactory通常为每一种它可以生产的产品定义一个操作。产 品的种类被编码在操作型构中。增加一种新的产品要求改变 AbstractFactory的接口以及所有与 __它相关的类。一个更灵活但不太安全的设计是给创建对象的操作增加一个参数。
- 该参数指定了被创建的对象的种类,它可以是一个类标识符,一个整数,一个字符串,AbstractFactory只需要一个“make”操作和一个要创建对象的种类的参数。这种方法就是基于原型(在具体工厂中存有一个键值对,存放相关的原型,用addPart和make添加或检索)和基于类(与基于原型的方法相似,只不过映射是类)的技术。
- C++这样的静态类型语言与Smalltalk(Javascript)相比,这一变化更容易用在类似于 Smalltalk这样的动态类型语 言中。仅当所有对象都有相同的抽象基类,或者当产品对象可以被请求它们的客户安全的强 制转换成正确类型时,你才能够在 C++中使用它。
- 该方法即使不需要类型强制转换,但仍有一个本质的问题:所有的产品将返回类型所给 定的 相同 的抽象接口返回给客户。客户将不能区分或对一个产品的类别进行安全的假定。如 果一个客户需要进行与特定子类相关的操作,而这些操作却不能通过抽象接口得到。虽然客 户可以实施一个向下类型转换( downcast)(例如在C++中用dynamic_cast),但这并不总是可 行或安全的,因为向下类型转换可能会失败。这是一个典型的高度灵活和可扩展接口的权衡折衷。
代码示例
一般的抽象工厂就是由一些工厂方法的集合构成。在一些情况下,工厂的抽象基类也是具体类,可以方便的扩展。像C++这样的静态类型语言,抽象工厂保证了所创建的产品是同一系列,使得它可以安全的向下转型。
领教一下远古语言Smaltalk写的一个利用抽象工厂的例子,它仅有一个以要生成的对象种类为参数的 make操作。此外,具体工厂存储它所创建的产品的类
createMaze: aFactory
| room1 room2 aDoor |
room1 := (aFactory make: #room) number: 1.
room2 := (aFactory make: #room) number: 2.
aDoor := (aFactory make: #door) from: room1 to: room2.
room1 atSide: #north put: (aFactory make: #wall).
room1 atSide: #east put: aDoor.
room1 atSide: #south put: (aFactory make: #wall).
room1 atSide: #west put: (aFactory make: #wall).
room2 atSide: #north put: (aFactory make: #wall).
room2 atSide: #east put: (aFactory make: #wall).
room2 atSide: #south put: (aFactory make: #wall).
room2 atSide: #west put: aDoor.
^ Maze new addRoom: room1; addRoom: room2; yourself
MazeFactory仅需一个实例变量 partCatalog来提供一个字典, 这个字典的主键为迷宫组件的类。它是这样实现make:方法的
make: partName
^ (partCatalog at: partName) new
现在我们创建一个MazeFactory并用它来实现createMaze。我们将用类MazeGame的一个方法CreateMazeFactory来创建该工厂。
createMazeFactory
^ (MazeFactory new
addPart: Wall named: #wall;
addPart: Room named: #room;
addPart: Door named: #door;
yourself)
如果需要创建其他工厂,只需要将不同的类与它们的主键关联,就可以创建一个BombedMazeFactory或EnhancedMazeFactory。
createMazeFactory
^ (MazeFactory new
addPart: Wall named: #wall;
addPart: EnhancedRoom named: #room;
addPart: DoorNeedingSpell named: #door;
yourself)
Variables of Shell programming
Variables of Shell programming
User-Defined Variables
- 变量叠加的方式
- x="$x"1324
- x=${x}1234
- set 查询系统下已经生效的变量,包括环境变量和自定义变量
- set -u 变量不存在的话会报错,区别空值和没有定义的值
- unset variable 不加$,删除的是变量而不是变量的值 (也可以删除环境变量)
- export variable 把变量升级为环境变量 (自定义环境变量)
- env 查看环境变量
- $PATH 命令环境变量
- $PS1 (Linux中命令提示符设置)
Bash Variable
- locale
- 查看当前系统语系
- LANG : 定义系统主语系的变量
- LC_ALL : 定义整体语系的变量
- locale -a
- 查看所有语系环境
- cat /etc/sysconfig/i18n
- 查看默认语系环境,即下次开机的使用的语系
- df -h
- 查看磁盘
位置参数变量
- $0-$9 ${10}
- $* 所有参数一个整体
- $@ 所有参数都是独立的
#!/bin/bash
for i in "$*"
do
echo "The parameters is:$i"
done
for i in "$@"
do
echo "The parameters is:$i"
done
pre-defined variable
read instruct
Vue Component Design Hunting - 3 - Component
组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。
// 对于自定义标签名,Vue.js 建议遵循 W3C规则 (小写,并且包含一个短杠)
Vue.component('my-component', {
// 选项
})
Local Register
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> 将只在父模板可用
'my-component': Child
}
})
DOM 模版解析说明
当使用 DOM 作为模版时(例如,将 el 选项挂载到一个已存在的元素上), 你会受到 HTML 的一些限制,因为 Vue 只有在浏览器解析和标准化 HTML 后才能获取模版内容。尤其像这些元素
<ul>
,<ol>
,<table>
,<select>
限制了能被它包裹的元素, 而一些像 这样的元素只能出现在某些其它元素内部。
在自定义组件中使用这些受限制的元素时会导致一些问题,例如:
<table>
<my-row>...</my-row>
</table>
自定义组件 被认为是无效的内容,因此在渲染的时候会导致错误。变通的方案是使用特殊的 is 属性:
<table>
<tr is="my-row"></tr>
</table>
应当注意,如果您使用来自以下来源之一的字符串模板,这些限制将不适用:
- <script type="text/x-template">
- JavaScript内联模版字符串
- .vue 组件
Component Combination
在 Vue.js 中,父子组件的关系可以总结为 props down, events up 。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。
camelCase vs. kebab-case
HTML 特性是不区分大小写的。所以,当使用的不是字符串模版,camelCased (驼峰式) 命名的 prop 需要转换为相对应的 kebab-case (短横线隔开式) 命名:
Vue.component('child', {
// camelCase in JavaScript
props: ['myMessage'],
template: '<span>{{ myMessage }}</span>'
})
<!-- kebab-case in HTML -->
<child my-message="hello!"></child>
初学者常犯的一个错误是使用字面量语法传递数值:
<!-- 传递了一个字符串 "1" -->
<comp some-prop="1"></comp>
因为它是一个字面 prop ,它的值是字符串 "1" 而不是number。如果想传递一个实际的number,需要使用 v-bind ,从而让它的值被当作 JavaScript 表达式计算:
<!-- 传递实际的 number -->
<comp v-bind:some-prop="1"></comp>
单向数据流
prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。
Prop验证
如果传入的数据不符合规格,Vue 会发出警告
Vue.component('example', {
props: {
// 基础类型检测 (`null` 意思是任何类型都可以)
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
})
type 可以是下面原生构造器:
- String
- Number
- Boolean
- Function
- Object
- Array
type 也可以是一个自定义构造器函数,使用 instanceof 检测。当 prop 验证失败,Vue会在抛出警告。
自定义事件
父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,那就是自定义事件!
每个 Vue 实例都实现了事件接口(Events interface),即:
- 使用 $on(eventName) 监听事件
- 使用 $emit(eventName) 触发事件
不能用$on侦听子组件抛出的事件,而必须在模板里直接用v-on绑定。
给组件绑定原生事件
有时候,你可能想在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on 。例如:
<my-component v-on:click.native="doTheThing"></my-component>
.sync 修饰
在一些情况下,我们可能会需要对一个 prop 进行『双向绑定』。.sync修饰符是Vue 1.x中提供的功能,但是违背了单项数据流的假设。所以在2.0中移除,但是在实际应用中发现,他还是有其适用之处,比如在开发可复用的组件库时。我们需要做的只是让子组件改变父组件状态的代码更容易被区分。
在 2.3 我们重新引入了 .sync 修饰符,但是这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 侦听器。
如下代码
<comp :foo.sync="bar"></comp>
<!-- 会被扩展为:-->
<comp :foo="bar" @update:foo="val => bar = val"></comp>
当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:
this.$emit('update:foo', newValue)
自定义组件的v-model实现
<input v-model="something">
<!-- 这不过是以下示例的语法糖: -->
<input v-bind:value="something" v-on:input="something = $event.target.value">
<!-- 所以在组件中使用时,它相当于下面的简写: -->
<custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input>
所以要让组件的 v-model 生效,它必须:
- 接受一个 value 属性
- 在有新的 value 时触发 input 事件
我们来看一个非常简单的货币输入的自定义控件:
<currency-input v-model="price"></currency-input>
Vue.component('currency-input', {
template: '\
<span>\
$\
<input\
ref="input"\
v-bind:value="value"\
v-on:input="updateValue($event.target.value)"\
>\
</span>\
',
props: ['value'],
methods: {
// 不是直接更新值,而是使用此方法来对输入值进行格式化和位数限制
updateValue: function (value) {
var formattedValue = value
// 删除两侧的空格符
.trim()
// 保留 2 小数位
.slice(0, value.indexOf('.') + 3)
// 如果值不统一,手动覆盖以保持一致
if (formattedValue !== value) {
this.$refs.input.value = formattedValue
}
// 通过 input 事件发出数值
this.$emit('input', Number(formattedValue))
}
}
})
事件接口不仅仅可以用来连接组件内部的表单输入,也很容易集成你自己创造的输入类型。想象一下:
<voice-recognizer v-model="question"></voice-recognizer>
<webcam-gesture-reader v-model="gesture"></webcam-gesture-reader>
<webcam-retinal-scanner v-model="retinalImage"></webcam-retinal-scanner>
作用域插槽
在父级中,具有特殊属性 scope 的
<template>
元素,表示它是作用域插槽的模板。scope 的值对应一个临时变量名,此变量接收从子组件中传递的 prop 对象:
<!-- children component -->
<div class="child">
<slot text="hello from child"></slot>
</div>
<!-- parent component -->
<div class="parent">
<child>
<template scope="props">
<span>hello from parent</span>
<span>{{ props.text }}</span>
</template>
</child>
</div>
作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项:
<my-awesome-list :items="items">
<!-- 作用域插槽也可以是具名的 -->
<template slot="item" scope="props">
<li class="my-fancy-item">{{ props.text }}</li>
</template>
</my-awesome-list>
<!-- 列表组件的模板: -->
<ul>
<slot name="item"
v-for="item in items"
:text="item.text">
<!-- 这里写入备用内容 -->
</slot>
</ul>
动态组件
通过使用保留的 元素,动态地绑定到它的 is 特性,我们让多个组件可以使用同一个挂载点,并动态切换:(与使用v-if及v-else类似?)
var vm = new Vue({
el: '#example',
data: {
currentView: 'home'
},
components: {
home: { /* ... */ },
posts: { /* ... */ },
archive: { /* ... */ },
currentView: Home
}
})
<component v-bind:is="currentView">
<!-- 组件在 vm.currentview 变化时改变! -->
</component>
<!-- 如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive 指令参数: -->
<keep-alive>
<component :is="currentView">
<!-- 非活动组件将被缓存! -->
</component>
</keep-alive>
子组件索引
尽管有 props 和 events ,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 ref 为子组件指定一个索引 ID 。例如:
<div id="parent">
<user-profile ref="profile"></user-profile>
</div>
var parent = new Vue({ el: '#parent' })
// 访问子组件
var child = parent.$refs.profile
$refs 只在组件渲染完成后才填充,并且它是非响应式的。它仅仅作为一个直接访问子组件的应急方案——应当避免在模版或计算属性中使用 $refs 。
异步组件
在大型应用中,我们可能需要将应用拆分为多个小模块,按需从服务器下载。为了让事情更简单, Vue.js 允许将组件定义为一个工厂函数,动态地解析组件的定义。Vue.js 只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。例如:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// Pass the component definition to the resolve callback
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
推荐配合使用 : Webpack 的代码分割功能:
// AMD sytle
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 require 语法告诉 webpack
// 自动将编译后的代码分割成不同的块,
// 这些块将通过 Ajax 请求自动下载。
require(['./my-async-component'], resolve)
})
// Webpack2 + ES6 style
Vue.component(
'async-webpack-example',
() => import('./my-async-component')
)
// 自 2.3 起,异步组件的工厂函数也可以返回一个如下的对象:
const AsyncComp = () => ({
// 需要加载的组件. 应当是一个 Promise
component: import('./MyComp.vue'),
// loading 时应当渲染的组件
loading: LoadingComp,
// 出错时渲染的组件
error: ErrorComp,
// 渲染 loading 组件前的等待时间。默认:200ms.
delay: 200,
// 最长等待时间。超出此时间则渲染 error 组件。默认:Infinity
timeout: 3000
})
注意,当一个异步组件被作为 vue-router 的路由组件使用时,这些高级选项都是无效的,因为在路由切换前就会提前加载所需要的异步组件。
组件命名约定
当注册组件(或者 props)时,可以使用 kebab-case ,camelCase ,或 TitleCase 。Vue 不关心这个。
// 在组件定义中
components: {
// 使用 kebab-case 形式注册
'kebab-cased-component': { /* ... */ },
// register using camelCase
'camelCasedComponent': { /* ... */ },
// register using TitleCase
'TitleCasedComponent': { /* ... */ }
}
在 HTML 模版中,请使用 kebab-case 形式:
<!-- 在HTML模版中始终使用 kebab-case -->
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<title-cased-component></title-cased-component>
<!-- 当使用字符串模式时,可以不受 HTML 的 case-insensitive 限制。这意味实际上在模版中,你可以使用 camelCase 、 TitleCase 或者 kebab-case 来引用: -->
<my-component></my-component>
<myComponent></myComponent>
<MyComponent></MyComponent>
<!-- 如果组件未经 slot 元素传递内容,你甚至可以在组件名后使用 / 使其自闭合。当然,这只在字符串模版中有效。因为自闭的自定义元素是无效的 HTML ,浏览器原生的解析器也无法识别它。 -->
<my-component/>
组件间的循环引用Circular References Between Components
模块系统看到它需要 A ,但是首先 A 需要 B ,但是 B 需要 A, 而 A 需要 B,陷入了一个无限循环,因此不知道到底应该先解决哪个。要解决这个问题,我们需要在其中一个组件中(比如 A )告诉模块化管理系统,“A 虽然需要 B ,但是不需要优先导入 B”
- 当使用Vue.component将这两个组件注册为全局组件的时候,框架会自动为你解决这个矛盾
- 在我们的例子中,我们选择在tree-folder 组件中来告诉模块化管理系统循环引用的组件间的处理优先级,我们知道引起矛盾的子组件是tree-folder-contents,所以我们在beforeCreate 生命周期钩子中去注册它:
beforeCreate: function () {
this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue')
}
Builder
相关模式
- Abstract Factory与Builder相似,因为它也可以创建复杂对象。
- 主要的区别是Builder模式着重于一步步构造一个复杂对象。而Abstract Factory着重于多个系列的产品对象(简单的或是复杂的)。Builder在最后的一步返回产品,而对于Abstract Factory来说,产品是立即返回的。
- Composite通常是Builder生成的。
意图
- 将一个复杂对象的构建(Builder)与它的表示(Director)分离
- 使得同样的构建过程可以创建不同的表示 (Builder接口的不同实现)
- Director对Builder的导向作用使得对象创建更加灵活
适用性
在以下情况使用 Builder模式
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 当构造过程必须允许被构造的对象有不同的表示时。
结构
参与者
- Builder(TextConverter)
- 为创建一个Product对象的各个部件指定抽象接口。
- ConcreteBuilder(ASCIIConverter、TeXConverter、TextWidgetConverter)
- 实现Builder的接口以构造和装配该产品的各个部件。
- 定义并明确它所创建的表示。
- 提供一个检索产品的接口(例如, GetASCIIText和GetTextWidget)。
- Director(RTFReader)
- 构造一个使用Builder接口的对象。
- Product(ASCIIText、TeXText、TextWidget)
- 表示被构造的复杂对象。 ConcreteBuilder创建该产品的内部表示并定义它的装配过程。
- 包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
协作
- 客户创建Director对象,并用它所想要的Builder对象进行配置。
- 一旦产品部件被生成,导向器就会通知生成器。
- 生成器处理导向器的请求,并将部件添加到该产品中。
- 客户从生成器中检索产品。(Builder模式的标志动作,最后一步提交产品)
下面的交互图说明了 Builder和Director是如何与一个客户协作的
效果
这里是Builder模式的主要效果:
- 它使你可以改变一个产品的内部表示 Builder对象提供给导向器一个构造产品的抽象 接口。该接口使得生成器可以隐藏这个产品的表示和内部结构。它同时也隐藏了该产品是如 何装配的。因为产品是通过抽象接口构造的,你在改变该产品的内部表示时所要做的只是定 义一个新的生成器。
- 它将构造代码和表示代码分开 Builder模式通过封装一个复杂对象的创建和表示方式 提高了对象的模块性。客户不需要知道定义产品内部结构的类的所有信息;这些类是不出现 在Builder接口中的。每个 ConcreteBuilder包含了创建和装配一个特定产品的所有代码。这些 代码只需要写一次;然后不同的 Director可以复用它以在相同部件集合的基础上构作不同的 Product。在前面的 RTF例子中,我们可以为 RTF格式以外的格式定义一个阅读器,比如一个 SGMLReader,并使用相同的 TextConverter生成 SGML文档的 ASCIIText、TeXText和 TextWidget译本。
- 它使你可对构造过程进行更精细的控制 Builder模式与一下子就生成产品的创建型模 式不同,它是在导向者的控制下一步一步构造产品的。仅当该产品完成时导向者才从生成器 中取回它。因此 Builder接口相比其他创建型模式能更好的反映产品的构造过程。这使你可以 更精细的控制构建过程,从而能更精细的控制所得产品的内部结构。
实现
通常有一个抽象的 Builder类为导向者可能要求创建的每一个构件定义一个操作。
这些操作缺省情况下什么都不做。
一个 ConcreteBuilder类对它有兴趣创建的构件重定义这些操作。
这里是其他一些要考虑的实现问题:
- 装配和构造接口
- 生成器逐步的构造它们的产品。因此 Builder类接口必须足够普遍, 以便为各种类型的具体生成器构造产品。
- 一个关键的设计问题在于构造和装配过程的模型。构造请求的结果只是被添加到产品中, 通常这样的模型就已足够了。
- 但有时你可能需要访问前面已经构造了的产品部件。我们在代码示例一节所给出的 Maze 例子中, MazeBuilder接口允许你在已经存在的房间之间增加一扇门。像语法分析树这样自底 向上构建的树型结构就是另一个例子。在这种情况下,生成器会将子结点返回给导向者,然 后导向者将它们回传给生成者去创建父结点。
- 为什么产品没有抽象类 通常情况下,由具体生成器生成的产品,它们的表示相差是 如此之大以至于给不同的产品以公共父类没有太大意思。
- 在Builder中却省的方法为空
代码示例
以下代码展示了Director一步步调用Builder的过程,传入不同Builder即可生成不同Product
Maze* MazeGame::CreateMaze(MazeBuilder& builder){
builder.BuildMaze();
builder.BuildRoom(1);
builder.BuildRoom(2);
builder.BuildDoor(1,2);
return builder.GetMaze();
Vue Component Design Hunting - Render
深入data object参数
有一件事要注意:正如在模板语法中,v-bind:class 和 v-bind:style ,会被特别对待一样,在 VNode 数据对象中,下列属性名是级别最高的字段。
{
// 和`v-bind:class`一样的 API
'class': {
// must be wrapped with quotation mark
foo: true,
bar: false
},
// 和`v-bind:style`一样的 API
style: {
color: 'red',
fontSize: '14px'
},
// 正常的 HTML 特性
attrs: {
id: 'foo'
},
// 组件 props
props: {
myProp: 'bar'
},
// DOM 属性
domProps: {
innerHTML: 'baz'
},
// 事件监听器基于 "on"
// 所以不再支持如 v-on:keyup.enter 修饰器
// 需要手动匹配 keyCode。
on: {
click: this.clickHandler
},
// 仅对于组件,用于监听原生事件,而不是组件内部使用 vm.$emit 触发的事件。
nativeOn: {
click: this.nativeClickHandler
},
// 自定义指令. 注意事项:不能对绑定的旧值设值
// Vue 会为您持续追踪
directives: [
{
name: 'my-custom-directive',
value: '2'
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// Scoped slots in the form of
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => h('span', props.text)
},
// 如果组件是其他组件的子组件,需为slot指定名称
slot: 'name-of-slot'
// 其他特殊顶层属性
key: 'myKey',
ref: 'myRef'
}
slots
你可以从this.$slots获取VNodes列表中的静态内容:
render: function (createElement) {
// <div><slot></slot></div>
return createElement('div', this.$slots.default)
}
还可以从this.$scopedSlots 中获得能用作函数的作用域插槽,这个函数返回 VNodes:
render: function (createElement) {
// <div><slot :text="msg"></slot></div>
return createElement('div', [
this.$scopedSlots.default({
text: this.msg
})
])
}
如果要用 render 函数向子组件中传递作用域插槽,可以利用VNode数据中的 scopedSlots域:
render (createElement) {
return createElement('div', [
createElement('child', {
// pass scopedSlots in the data object
// in the form of { name: props => VNode | Array<VNode> }
scopedSlots: {
default: function (props) {
return createElement('span', props.text)
}
}
})
])
}
将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的,如果在作用域中 h 失去作用, 在应用中会触发报错。
git reporitory line statistics command
- 统计每个人的提交量
git log --format='%aN' | sort -u | while read name;
do echo -en "$name\t";
git log --author="$name" --pretty=tformat: --numstat |
# filter specified files
grep -Ev "package-lock.json" |
awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done
- 个人提交量
git log --author="username" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -
- 提交数
git log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r | head -n 5
Shell编程之运算符
vimtutor
-
h, j, k, l 方向
-
x 删除,i向前插入,a向后插入,A整句append
-
q! 不保存退出,wq 保存退出
-
d + x
d - operator
x - motion
dw, de, d$, d0
0 move cursor to line begining, $ move cursor to line ending. -
inputing a number before a motion will repeat the specified times
such as: 10j 向下移10行, 4e,3w表示向后移4位至尾部,或向后移3位至首部 -
operator + number + motion 做多次动作
such as: d4w d4j 一般删除整行使用dd,删除多行使用ndd -
u撤销单条命令,U还原整行,ctrl + r 表示 redo
-
put command: p 将之前删除的内容粘贴在光标之后
-
r 替换字符
-
ce 删除word中光标之后的字符,进入插入模式。
此时的 c 是 operator,e 是 motion
所有可以有 c + number + motion -
ctrl + g 显示光标当前位置
G move to end of the file
gg move to beginning of the file
number + G jump to specified line -
/ + search phrase 顺序查找,n 查看下一个,N 查看上一个。ctrl + o 返回上一个位置,ctrl + i 到新的查找位置(这几个命令配合使用)
查找特定单词:
- 将光标放在单词上,按 shift+*,在用n 和 N 进行选择
- 使用 ye 选中单词,然后 用 / + (ctrl + r) 寄存器前缀, + 0,即可进行搜索
-
% 查找闭合符号 {, (, [
-
:s/old/new 当前行替换第一个
-
:s/old/new/g 当前行替换所有
-
:#,#s/old/new/g 替换某个范围内
-
:%s/old/new/g 全文替换
-
:%s/old/new/gc 全文替换询问
-
:!extarnal command 执行外部命令
-
:w filename 存当前文件副本
-
v + :w filename 存选择的文本到另一个文件
-
:r filename, :r !ls 将文件插入之后的文本,或者命令的输出插入之后的文本
-
o, O, a, A 插入模式
-
y 在进入visual模式后,复制,用 p粘贴
-
R 进入replace mode
-
:set xxx 设置 option
:set ic ignorecase
:set is incsearch show partial matches for search phrase
:set hls hlsearch highlight all matching phrases
prepand "no" to switch an option off. Such as :set noic
:nohlsearch to remove highlighting of matches enter
node-elm package analysis - supervisor and pm2
supervisor (译为监督人)
A little supervisor script for nodejs. It runs your program, and watches for code changes, so you can have hot-code reloading-ish(-ish译为 ...似的) behavior, without worrying about memory leaks and making sure you clean up all the inter-module references, and without a whole new require system.
Node Supervisor is used to restart programs when they crash. It can also be used to restart programs when a *.js file changes.
pm2 (Process Manager2)
PM2 is a production process manager for Node.js applications with a built-in load balancer. It allows you to keep applications alive forever, to reload them without downtime and to facilitate common system admin tasks.
# General
$ npm install pm2 -g # Install PM2
$ pm2 start app.js # Start, Daemonize and auto-restart application (Node)
$ pm2 start app.py # Start, Daemonize and auto-restart application (Python)
$ pm2 start npm -- start # Start, Daemonize and auto-restart Node application
# Cluster Mode (Node.js only)
$ pm2 start app.js -i 4 # Start 4 instances of application in cluster mode
# it will load balance network queries to each app
$ pm2 reload all # Zero Second Downtime Reload
$ pm2 scale [app-name] 10 # Scale Cluster app to 10 process
# Process Monitoring
$ pm2 list # List all processes started with PM2
$ pm2 monit # Display memory and cpu usage of each app
$ pm2 show [app-name] # Show all information about application
# Log management
$ pm2 logs # Display logs of all apps
$ pm2 logs [app-name] # Display logs for a specific app
$ pm2 logs --json # Logs in JSON format
$ pm2 flush
$ pm2 reloadLogs
# Process State Management
$ pm2 start app.js --name="api" # Start application and name it "api"
$ pm2 start app.js -- -a 34 # Start app and pass option "-a 34" as argument
$ pm2 start app.js --watch # Restart application on file change
$ pm2 start script.sh # Start bash script
$ pm2 start app.json # Start all applications declared in app.json
$ pm2 reset [app-name] # Reset all counters
$ pm2 stop all # Stop all apps
$ pm2 stop 0 # Stop process with id 0
$ pm2 restart all # Restart all apps
$ pm2 gracefulReload all # Gracefully reload all apps in cluster mode
$ pm2 delete all # Kill and delete all apps
$ pm2 delete 0 # Delete app with id 0
# Startup/Boot management
$ pm2 startup # Detect init system, generate and configure pm2 boot on startup
$ pm2 save # Save current process list
$ pm2 resurrect # Restore previously saved processes
$ pm2 unstartup # Disable and remove startup system
$ pm2 update # Save processes, kill PM2 and restore processes
$ pm2 generate # Generate a sample json configuration file
# Deployment
$ pm2 deploy app.json prod setup # Setup "prod" remote server
$ pm2 deploy app.json prod # Update "prod" remote server
$ pm2 deploy app.json prod revert 2 # Revert "prod" remote server by 2
# Module system
$ pm2 module:generate [name] # Generate sample module with name [name]
$ pm2 install pm2-logrotate # Install module (here a log rotation system)
$ pm2 uninstall pm2-logrotate # Uninstall module
$ pm2 publish # Increment version, git push and npm publish
Vue Component Design Hunting - 1
Difference between computed and watch
Generally, we use computed to watch variables changing. But if we need something middle condition or long request, we can use watch to set data repeatedly.
Set Appearance of elements
Use v-if and v-else expression on single element. ( v-else must being behind the v-if tightly. )
Or use v-if expression on a template element which won't be rendered on DOM
Difference between v-if and v-show
v-show 不支持在template
元素上使用,v-show 无论条件是什么,总是被首次渲染,并基于CSS切换。
v-if 支持在template
元素上使用,是惰性加载的,在切换时会对条件块中的子组件和事件监听器进行
销毁和重建。
v-if有更高的切换开销,v-show有更高的初始渲染开销。所以,如果频繁切换则用v-show,如果在运行条件下不太会改变,则用v-if
use v-if and v-for together
v-for has higher priority than v-if
Vue 观察数组的变异方法
变异方法(mutation method),顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异(non-mutating method)方法,例如: filter(), concat(), slice() 。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
Tips:
由于 JavaScript 的限制, Vue 不能检测以下变动的数组:
- 当你利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如: vm.items.length = newLength
可以用以下方式达到相同效果:
// 1. Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice`
example1.items.splice(indexOfItem, 1, newValue)
// 2.
example1.items.splice(newLength)
Modifiers
在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在 methods 中轻松实现这点,但更好的方式是:methods 只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
Key Modifier
在监听键盘事件时,我们经常需要监测常见的键值。 Vue 允许为 v-on 在监听键盘事件时添加按键修饰符
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
<!-- 同上 -->
<input v-on:keyup.enter="submit">
全部的按键别名:
- .enter
- .tab
- .delete
- .esc
- .space
- .up
- .down
- .left
- .right
2.1.0 added:
- .ctrl
- .alt
- .shift
- .meta
可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 v-on:keyup.f1
Vue.config.keyCodes.f1 = 112
Vuex - Introduction
Commit Payload
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
// 当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:
store.commit({
type: 'increment',
amount: 10
})
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
Mutations 需遵守 Vue 的响应规则
既然 Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:
- 最好提前在你的 store 中初始化好所有所需属性。
- 当需要在对象上添加新属性时,你应该
- 使用 Vue.set(obj, 'newProp', 123), 或者
- 以新对象替换老对象。例如,利用 stage-3 的对象展开运算符我们可以这样写:
state.obj = { ...state.obj, newProp: 123 }
使用常量替代 Mutation 事件类型
使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
state: { ... },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// mutate state
}
}
})
mutation 必须是同步函数
在组件中提交 Mutations
你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment' // 映射 this.increment() 为 this.$store.commit('increment')
]),
...mapMutations({
add: 'increment' // 映射 this.add() 为 this.$store.commit('increment')
})
}
}
区分Mutations和Actions的原因
在 mutation 中混合异步调用会导致你的程序很难调试。在 Vuex 中,mutation 都是同步事务。
Actions
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
浏览器缓存机制
引用
浏览器缓存机制概览
浏览器缓存通过fiddler实践探究*
缓存POST请求
PUT和POST区别,等幂的含义
解决fiddler抓不到浏览器包的问题
fiddler快速命令
文章提到了 http协议下的缓存机制和非 http协议下的缓存机制(html头中包含的元标签)。主要叙述了从http 1.0 的 expire 到 http 1.1的cache-control,再到 last-modify,etag。论述了他们的实现原理和劣势,顺序等。
得出了需要把文件(例如 index.html)设置为 no-cache然后把引用的文件使用 filename.version.suffix 或者 filename.suffix?version=:version 作为文件名来引用更新后的文件的结论。
遗留问题
- 静态库文件引用,静态数据文件引用;动态数据请求;动态加载元素引用的文件 etc
- 抽出主页面引用的静态文件引用并加上版本号,同时更改真实文件名称?那么动态加入的元素引用的文件就没有添加版本号。
- 使用变量替代所有可能引用到的静态文件名,在部署前用脚本替换?
- 那么本地编写代码也都需要先进行编译。那么就要引入一些 webpack,gulp等流程控制工具。
- 动态数据请求还是会多次进行,执行策略是:每个session取一次数据?
- 除了增加版本号之外的解决方案
- 暂时没有想到
- 在引用的文章中提到 last-modify可以和etag同时使用,情况如何?
- fiddler教程
- 纠正教程中一个叙述:
如果etag和last-modify同时出现,不是有etag就不发if-modified-since,而是把if-none-matched和if-modified-since同时发往服务器。服务器会先判断etag,在etag相同的情况下在判断last-modify。
Vue Component Design Hunting - 2
表单控件绑定
多个勾选框,绑定到一个数组;多个单选框,绑定到一个字符串
<!-- checkbox -->
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
<!-- radio -->
<div id="example-4">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>
<!-- single select -->
<div id="example-5">
<select v-model="selected">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
<!-- multiple select -->
<div id="example-6">
<select v-model="multiSelected " multiple style="width: 50px">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ multiSelected }}</span>
</div>
new Vue({
el: '...',
data: {
checkedNames: [],
picked: '',
selected: null,
multiSelected: []
}
})
绑定 value
对于单选按钮,勾选框及选择列表选项, v-model 绑定的 value 通常是静态字符串(对于勾选框是逻辑值)。但是有时我们想绑定 value 到 Vue 实例的一个动态属性上,这时可以用 v-bind 实现,并且这个属性的值可以不是字符串。
<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a">
<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle">
<!-- 当选中时,`selected` 为字符串 "abc" -->
<select v-model="selected">
<option value="abc">ABC</option>
</select>
<input
type="checkbox"
v-model="toggle"
v-bind:true-value="a"
v-bind:false-value="b"
>
<input type="radio" v-model="pick" v-bind:value="a">
<select v-model="selected">
<!-- 内联对象字面量 -->
<option v-bind:value="{ number: 123 }">123</option>
</select>
// 当选中时
vm.toggle === vm.a
// 当没有选中时
vm.toggle === vm.b
vm.pick === vm.a
// 当选中时
typeof vm.selected // -> 'object'
vm.selected.number // -> 123
Modifiers
- .lazy ( sync when change event is emitted rather than input event )
- .number
- .trim
node-elm package analysis - cross-env and cross-spawn
cross-env
Run scripts that set and use environment variables across platforms
The Problem
Most Windows command prompts will choke when you set environment variables with NODE_ENV=production like that. (The exception is Bash on Windows, which uses native Bash.) Similarly, there's a difference in how windows and POSIX commands utilize environment variables. With POSIX, you use: $ENV_VAR and on windows you use %ENV_VAR%.
The Solution
cross-env makes it so you can have a single command without worrying about setting or using the environment variable properly for the platform. Just set it like you would if it's running on a POSIX system, and cross-env will take care of setting it properly.
_Installation
The module is supposed to be installed at devDependencies
npm install --save-dev cross-env
Usage
For Example:
{
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
}
}
The NODE_ENV environment variable will be set by cross-env
You can also split a command into several ones, or separate the environment variables declaration from the actual command execution. You can do it this way:
{
"scripts": {
"parentScript": "cross-env GREET=\"Joe\" npm run childScript",
"childScript": "echo Hello $GREET"
}
}
Where childScript holds the actual command to execute and parentScript sets the environment variables to use. Then instead of run the childScript you run the parent. This is quite useful for launching the same command with different env variables or when the environment variables are too long to have everything in one line.
Lastly, if you want to pass a JSON string (e.g., when using ts-loader), you can do as follows:
{
"scripts": {
"test": "cross-env TS_NODE_COMPILER_OPTIONS={\\\"module\\\":\\\"commonjs\\\"} node some_file.test.ts"
}
}
Pay special attention to the triple backslash (\) before the double quotes (") and the absence of single quotes ('). Both of these conditions have to be met in order to work both on Windows and UNIXs.
cross-env vs cross-env-shell
The cross-env module exposes two bins: cross-env and cross-env-shell. The first one executes commands using cross-spawn, while the second one uses the shell option from Node's spawn.
The main use case for cross-env-shell is when your need an environment variable to be set across an entire inline shell script, rather than just one command.
For example, if you want to have the environment variable apply to several commands in series then you will need to wrap those in quotes and use cross-env-shell instead of cross-env.
{
"scripts": {
"greet": "cross-env-shell GREETING=Hi NAME=Joe \"echo $GREETING && echo $NAME\""
}
}
The rule of thumb is: if you want to pass to cross-env a command that contains special shell characters that you want interpreted, then use cross-env-shell. Otherwise stick to cross-env.
cross-spawn
A cross platform solution to node's spawn and spawnSync.
The Problem
Node has issues when using spawn on Windows:
- It ignores PATHEXT
- It does not support shebangs
- No options.shell support on node < v6
- It does not allow you to run del or dir
Usage
Exactly the same way as node's spawn or spawnSync, so it's a drop in replacement.
var spawn = require('cross-spawn');
// Spawn NPM asynchronously
var child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
// Spawn NPM synchronously
var results = spawn.sync('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
Caveats
Starting from node v6, spawn has a shell option that allows you run commands from within a shell. This new option solves most of the problems that cross-spawn attempts to solve, but:
- It's not supported in node < v6
- It has no support for shebangs on Windows
- You must manually escape the command and arguments which is very error prone, specially when passing user input
Shebangs
While cross-spawn handles shebangs on Windows, its support is limited: e.g.: it doesn't handle arguments after the path, e.g.: #!/bin/bash -e.
Remember to always test your code on Windows!
Clean Code Notes
link:
- Ron Jeffries —— Author of Extreme Programming Installed HomePage 看看写了几十年代码的大师如何写代码。
extract:
20世纪80,90年代,Emac之类的编辑器记录每次击键动作。你可以在一小时工作之后,回放击键过程,就像是看一部高速电影。
Significant Naming
- 名副其实
- 一旦发现有更好的名称,就换掉旧的
- 如果名称需要注释来补充,那就不算是名副其实
- 避免误导
- 必须避免留下掩藏代码本意的错误线索。
- 例如hp, aix, sco都不该用作变量名,因为它们都是UNIX平台或类UNIX平台的专有名称。所以不该作为变量,比如三角计算程序
- 别用accoutList来指代一组账号,除非他真是List类型。
- 避免使用不同之处较小的名称,区分会很难
- 以同样的方式拼写出同样的概念才是信息,拼写前后不一致就是误导。
- 必须避免留下掩藏代码本意的错误线索。
- 做有意义的区分
- 作为类,Product 比 ProductData和ProductInfo都更加清晰,后者添加的后缀毫无意义
- 只要体现出区别,a, the 这样的前缀就没错。比如a要做局部变量的前缀,the用作参数的前缀。但是如果已经有了zork的变量,再有theZork的变量,麻烦就来了
- 使用读的出来的名称
- 使用可搜索的名称(便于搜索)
- 名称长短应该与其作用域大小成正相关
- 如对全局变量的命名 WORK_DAYS_PER_WEEK,和局部变量 sum
- 避免使用编码
- 在计算机硬件不成熟的年代,有对变量名长度的约束,而且不进行类型检测。
- 所以出现了匈牙利语标记法,即类型加数字。
- 但是现在的强类型语言会知道类型而且编译器会优化变量长度。继续使用这种编码命名会带来更改的困难和阅读的困难。
- 如果无法区分接口和实现类,那么把接口命名为原名称,实现类加后缀 Impl
- 类名不应该包含动词
- 方法名应该包含动词
- 每个概念对应一个词
- 应该有团队代码规范约束之
- 比如fetch,retrieve,get 在多个类中的同种方法出现,会造成使用不便
- 使用函数的参数列表中形参的名称,概念会更统一
- 别用双关语
- 避免将同一单词用于不同目的。否则,会造成理解和使用的不便。
- 使用计算机领域术语
- 尽管用计算机术语,算法名,模式名,数学术语把。因为程序是给程序员看的
- 如果不能使用计算机领域术语,就是用问题域术语
- 添加有意义的语境
- 分散的名称很难自我说明,所以需要良好命名的类,函数或名称空间来放置名称。
- 例如,firstName,lastName,street,houseNumber,city,state,zipcode放在一起很明确构成地址,但是分散开便不知其意。可以使用addr前缀标明他们的意义,但是不便于搜索,更好的方式是添加Address类。
- 不要添加没用的语境
- 增加检索难度
function
- 短小
- 只做一件事
- 编写函数是为了把大一些的概念拆分为另一个抽象层上的一系列步骤。(函数降维)
- 每个函数一个抽象层次
- 函数分为不同的抽象层次
- 函数中混杂不同抽象层次,往往让人迷惑
- switch语句
- 使用抽象工厂,从应用代码移走
- 函数参数
- 最理想的参数数量是0
- 最理想是没有返回值,因为也会增大理解难度,去理解输出的参数是什么
- 调用0参函数与调用有参函数相比,参数会和调用的函数不在同一抽象层次。(导致理解难度增加)
- 参数少容易设置测试
- 一元函数的普遍形式
- 问关于参数的问题
- 将其转换为其他什么东西,在输出之
- 应当区别这两种用法,并在一致的上下文使用
- 还有一种不那么普遍但极有用的形式,就是事件,一元参数无返回值
- 规避标志参数
- 明确说明要分情况,不是一件事
- 正确的做法分为两个函数
- 使用目标类减少参数,或者使用代理减少参数
- 参数对象
- 参数列表本身是一个概念的,可以封装成对象
- 可变参数同理(String... args)
- 方法名和参数就好像 动词与关键字
- 无副作用
- 副作用是一种谎言,函数承诺只做一件事。
- 尽量避免使用输出参数,如果修改某种状态,就修改所属对象的状态
- 分割指令与询问
- 指令就是指令,不要把指令的返回值当作下一步的判断值,返回值很容易被混淆,如set(fieldName, value)的返回值是属性是否已经有值,有则返回false。这就需要很多知识。所以应该分开。
- 使用异常替代返回错误码
- 从指令式函数返回错误码轻微违反了指令与询问分隔的规则。
- 更导致了更深的嵌套结构
- 如果使用异常替代它,错误处理代码就能从主路径代码中分离出来
- 错误码会产生枚举或者错误类,这会导致许多类引用它们,一旦枚举发生变化,所有类都要重新编译
- 使用继承异常类取代这种方式
- 抽离try/catch代码块,错误处理就是一件事
- 如何写出好的函数
- 不断重构
Node.js - Module
module
Variables local to the module will be private, because the module is wrapped in a function by Node.js.
see module wrapper:
(function (exports, require, module, __filename, __dirname) {
// Your module code actually lives in here
});
Accessing the main module
When a file is run directly from Node.js, require.main is set to its module. That means that you can determine whether a file has been run directly by testing
Package Manager Tips
Let's say that we wanted to have the folder at /usr/lib/node// hold the contents of a specific version of a package.
Packages can depend on one another. In order to install package foo, you may have to install a specific version of package bar. The bar package may itself have dependencies, and in some cases, these dependencies may even collide or form cycles.
Since Node.js looks up the realpath of any modules it loads (that is, resolves symlinks), and then looks for their dependencies in the node_modules folders as described here.
Thus, even if a cycle is encountered, or if there are dependency conflicts, every module will be able to get a version of its dependency that it can use. ( ? )
In order to make modules available to the Node.js REPL, it might be useful to also add the /usr/lib/node_modules folder to the $NODE_PATH environment variable. Since the module lookups using node_modules folders are all relative, and based on the real path of the files making the calls to require(), the packages themselves can be anywhere.
Caching
Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will get exactly the same object returned, if it would resolve to the same file.
Multiple calls to require('foo') may not cause the module code to be executed multiple times. This is an important feature. With it, "partially done" objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.
If you want to have a module execute code multiple times, then export a function, and call that function.
Core Modules
- Node.js has several modules compiled into the binary.
- The core modules are defined within Node.js's source and are located in the lib/ folder. Core modules are always preferentially loaded if their identifier is passed to require().
Cycles
a.js:
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
b.js:
console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
main.js:
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);
When main.js loads a.js, then a.js in turn loads b.js. At that point, b.js tries to load a.js. In order to prevent an infinite loop, an unfinished copy of the a.js exports object is returned to the b.js module. b.js then finishes loading, and its exports object is provided to the a.js module.
By the time main.js has loaded both modules, they're both finished. The output of this program would thus be:
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true
node-elm code analysis - babel-core and config-lite
require('babel-core/register');
/**
* require('babel-register')
* All subsequent files required by node with
* the extensions .es6, .es, .jsx and .js will be transformed by Babel.
*/
config-lite
默认冒泡找到config文件夹下的default文件,并加载。如果启动node带有参数,或者设置了环境变量,又或者在接口中配置了config对象。则会按优先级选择并加载相应的配置的对象。
// config-lite v1
import config from 'config-lite';
mongoose.connect(config.url, {server:{auto_reconnect:true}});
// config-lite v2
var config = require('config-lite')({
filename: 'test',
config_basedir: __dirname,
config_dir: 'config'
});
Options
- filename: config file name, default: default, support: ['.js', '.json', '.node', '.yaml', '.yml'].
- config_basedir: directory for begining bubbling find config directory.
- config_dir: config directory name, default: config.
- config: default config object that overwrite config file.
Priority
environment option > custom option > default option
For example:
NODE_ENV=test NODE_CONFIG='{"port":3000}' node app.js --port=3001
loading order:
--port=3001 > NODE_CONFIG='{"port":3000}' > opt.config > test config file > default config file
Environment Variables
- NODE_ENV -> filename
- CONFIG_BASEDIR || NODE_CONFIG_BASEDIR -> config_dirname
- CONFIG_DIR || NODE_CONFIG_DIR -> config_dir
- CONFIG || NODE_CONFIG -> config
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.