JavaScript

基于 koa 的微服务

使用 JavaScript 构建微服务时候似乎没有什么多余的选择,毕竟 grpc 都还没有合适的解决方案。所以还是采用 HTTP + JSON 的方式啦。 这里我选择了 koa 这个框架,可以很好地使用 Promise 和 async/await 的语法,再也不用写一大堆回调了,写起来还能轻松一些。

koa 作为一个微框架,简直是把 node 生态圈 isArray 都要写个库这种做法发挥到极致了。 默认情况下,koa 连 POST 过来的 JSON 都不给解析出来。所以几乎任何操作都需要安装对应的库,下面总结一下必备的一些库。

解析 JSON

使用 koa-bodyparser: https://github.com/koajs/bodyparser

解析命令行参数

使用 Command Line Args: https://github.com/75lb/command-line-args/wiki/Typical-usage-example

参考文献

  1. https://koajs.com/#request

Python 和 JavaScript 语法对比

Python 和 JavaScript 语法对比

命名

  1. 注意使用驼峰变量名, 不要使用下划线变量名

字符串

格式化

JavaScript:

  `hello ${name}`

Python:

  f"hello {name}"

文件

打开文件:

JavaScript:

  const fs = require("fs").promises;  // 使用 async/await 版本的 fs 模块
  
  await fs.writeFile(filename, data);  // 写入文件
  
  // 文件是否存在
  try {
      await fs.stat(filename)
      exitst = true
  } catch (e) {
      exists = false
  }

Python:

  with open(filename, "w") as f:
      f.write(data)
      
  os.path.exists(filename)  # 文件是否存在
  

数组

切片:

JavaScript:

  const arr1 = arr2.slice(3, 5);

Python:

  arr1 = arr2[3:5]

前端框架 Vue 学习笔记

之前在网页里小范围的用了vue, 感觉用起来非常爽, 现在打算做自己的笔记应用. 这次打算做成SPA的形式, 前端全部用vue来写. 需要node的一些东西, 记录下学习过程. 迈向全栈,哈哈 ^_^

安装

首先, 全局安装vue:

npm install --global @vue/cli

data 属性

只有在创建时提供的 data 属性才是响应式的,在创建之后在添加新元素就不管用了。

当然还可以使用 app.$watch 方法显式创建一些监听器

// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})

methods 和 computed

methods 定义了一些可以调用的方法,他的值也可以用来插值。但是最好使用 computed,因为 computed 是有缓存的。

computed 属性也可以设置 setter,所以实际上,computed 属性相当于对现有属性的一种映射和变化。

v-bind 和 v-model

  • v-bind 用于单项绑定:在 HTML 属性中使用 v-bind 绑定, 标签中使用 { }。只能使用表达式,而不能使用语句。
  • v-model 用于双向绑定:在 input 这类用户可以输入的组件中,需要双向绑定,使用 v-model.

v-bind 类似的指令还有 v-once 和 v-html

v-model 实际上等价于

<input v-model="searchText">
<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

要想在自定义输入组件中支持 v-model 的话,就需要使用 v-bind 和 v-on 两个方法了,而不能直接使用 v-model。

v-if 和 v-for

这两个就和所有模板系统中的 if 和 for 一样。vue 中提供的额外方便之处是,可以使用 template 标签,这样就不会多一个标签了

vue 是懒渲染的,因此会尽可能地复用组件,可以使用 key 来区分

v-if 是真正的条件式渲染,v-show 则只是在切换 display 属性

vue 没有代理数组的赋值方法,所以需要使用 app.$set 方法

@

如果只是指定一个事件处理函数的话,那么参数就是 event。如果自己指定了参数的话,可以使用 $event 来代表 event

还可以使用 .prevent 和 .stop 等修饰符

component

vue 中最终要的概念就是组件了。使用组件来模块式得构建应用。需要通过 props 属性来定义组件中的属性

<div id="app-7">
  <ol>
    <!--
      现在我们为每个 todo-item 提供 todo 对象
      todo 对象是变量,即其内容可以是动态的。
      我们也需要为每个组件提供一个“key”,稍后再
      作详细解释。
    -->
    <todo-item
      v-for="item in groceryList"
      v-bind:todo="item"
      v-bind:key="item.id">
    </todo-item>
  </ol>
</div>

Vue.component('todo-item', {
  // todo-item 组件现在接受一个
  // "prop",类似于一个自定义特性。
  // 这个 prop 名为 todo。
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

props 是一个数组,用来声明组建的属性。然后通过属性来传递。

组件还可以通过 $emit 来发送事件,这些事件可以被所有的组件监听到,就像普通的 DOM 事件一样。

声明周期函数

在固定的周期,vue 会调用的一些函数 created, mounted 等。需要注意的是,不要使用胖箭头函数。

new Vue({
  data: {
    a: 1
  },
  created: function () {
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
  }
})
// => "a is: 1"

创建vue应用

因为我们会直接通过*.vue文件来编写vue的组件, 因此需要使用webpack打包编译. 另外我们需要使用官方的vue-router来

vue init webpack notelet

这条命令基于 webpack 这个模板创建了notelet这个应用, 也就是我们的笔记应用.

打开 src/router/index.js 可以看到 vue 创建的router的代码, 其中@src目录的缩写.

import Vue from 'vue'
  import Router from 'vue-router'
import Hello from '@/components/Hello'

Vue.use(Router)

export default new Router({
  routes: [
    {
    ¦ path: '/',
    ¦ name: 'Hello',
    ¦ component: Hello
    }
  ]
})

然后打开 src/main.js, 可以看到在里面使用了 App 来作为我们的跟组件

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App }
})

执行 npm run dev, 然后打开 http://localhost:8080/ 就可以看到我们的vue应用了. 注意, 在写这篇文章的时候 node 8.x 下似乎有bug, 导致 app.js 加载不出来, 安装 6.x 就好了. 该死的node.

打开 App.vue, 也就是我们的根组件, 可以看到下面的内容

<template>
  <div id="app">
  ¦ <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'app'
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

注意其中的 router-view 便签, 意思就是路由的内容都在 router-view 中显示.

添加一个新的组件和路由

接下来我们添加一个"关于"页面. 打开src/router/index.js, 改成下面这样:

export default new Router({
  routes: [
  ¦ {
  ¦ ¦ path: '/',
  ¦ ¦ name: 'Hello',
  ¦ ¦ component: Hello
  ¦ },
  ¦ {
  ¦ ¦ path: '/about',
  ¦ ¦ name: 'About',
  ¦ ¦ component: About
  ¦ }
  ]
})

然后添加 src/components/About.vue 文件

<template>
  <div class="hello">
  ¦ <h1>About Notelet</h1>
  ¦ <p>This is a simple note app</p>
  </div>
</template>

<script>
export default {
  name: 'About',
  data () {
  ¦ return {
  ¦ ¦ msg: 'Hello Vue'
  ¦ }
  }
}
</script>

然后更改 App.vue 文件

<template>
  <div id="app">
  ¦ <router-link :to="{name: 'Hello'}">Home</router-link>
  ¦ <router-link to="/about">About</router-link>
  ¦ <router-view></router-view>
  </div>
</template>

注意, 我们使用about指向了 About 这个组件, 而使用 hello 指向了 Hello 这个组件, 注意其中还动态传递了参数.

[1] https://scotch.io/tutorials/getting-started-with-vue-router

html Node vs Element

* Node vs Element

HTML Document consists of different node, element is one type of node.

* NodeList vs HTMLCollection

NodeList is a collection of node, HTMLCollection is a collection of element.

* HTMLCollection vs NodeList vs Array

HTMLCollection 和 NodeList 都是动态的,会随着 DOM 的变化而变化

Array 是静态的数据结构

chrome spider

Write it in javascript,

start script starts chrome and opens the extension by opening extension’s page

another scripts opens a http server locally to feed the initial command and rules

the extension then executes as instructed

see also:

https://chrome.google.com/webstore/detail/wild-spider/aanpchnfojihjddlocpgoekffmjkhbbe

https://chrome.google.com/webstore/detail/chrome-crawler/amjiobljggbfblhmiadbhpjbjakbkldd/reviews

axios and fetch

# cookies

浏览器中的 JavaScript 运行在一个沙箱中。

fetch 默认不带 cookie, see https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials

axios 自动附加请求域名对应的 cookie

可以使用 JavaScript 设置对应所在域名的 cookie,而 chrome extension 可以设置所有域名的 cookie

# basic usage
“`
try {
let response = axios.get(url)
} catch (e) {
console.log(e)
}

// 可以使用的几种语法
axios.get(url)
axios.post(url, data)
axios(config)
axios(url, config)
axios.request(config)
“`

config fields: `params, data, headers, timeout, auth, response`

或者创建一个实例

“`
var instance = axios.create({
baseURL: ‘https://some-domain.com/api/’,
timeout: 1000,
headers: {‘X-Custom-Header’: ‘foobar’}
});

instance.get(‘/’)
“`

## see

* https://stackoverflow.com/questions/34558264/fetch-api-with-cookie

Chrome Extension Tabs

# permissions

“`
permissions: [
“tabs”,
]
“`

# usage

## chrome.tabs.query to get current tab

“`
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {}); // tabs[0] would be the current tab
“`

## create new tab

“`
chrome.tabs.create({url: URL}, function(tab) {})
“`

## kill tab
“`
chrome.tabs.remove(tabId or [tabId], function() {})
“`

JavaScript snippets

# get parameter from url
“`
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, “\\$&”);
var regex = new RegExp(“[?&]” + name + “(=([^&#]*)|&|#|$)”),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return ”;
return decodeURIComponent(results[2].replace(/\+/g, ” “));
}
“`

EcmaScript Set

# basic usage

“`
let set = new Set();
let set = new Set([a, b, c]);

set.size // the size of set
set.add(a)
set.delete(a)
set.clear()
set.has(value)
set.keys() // the same as keys
set.values() // return the values as a iterator
“`

reference:

[MDN set](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Set)