about 2 years ago

為了要了解 Javascript ES6 Generator Function ,花了些許時間看了 co 的原始碼。

co 是一個 library 用來協助使用者輕鬆的利用 generator 來控制你的程式 flow 。co 會主動的幫你把一個 generator 函數內的所有的 yield 語句都跑過一遍,袪除了要一直用 next 控制 flow 的麻煩。yield 後面僅接受 generator, promise, function, array ,是使用上的一個限制。

以下節錄了 co 的原始碼:

function co(gen) {
  .
  .
  .
  return new Promise(function(resolve, reject) {
    onFulfilled();

    function onFulfilled(res) {
      var ret;
      try {
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }
    .
    .
    .
    function next(ret) {
      if (ret.done) return resolve(ret.value);
      var value = toPromise.call(ctx, ret.value);
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
        + 'but the following object was passed: "' + String(ret.value) + '"'));
    }
  });
}

流程

co 首先接受一個 generator function 並返回一個 promise。這個 promise 執行的第一個函數是 onFulfilled,從這個進入函數,開始了 generator 的第一個 next 。

自動走完所有整個 generator 的關鍵在於

var value = toPromise.call(ctx, ret.value);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);

yield 獲取到的 function, generator 轉為 promise ,並將 onFulfilled, onRejected 設定為它的 callback 函數,如此一來,一旦此函數執行完畢,會繼續執行原有的 generator ,不斷的重複前面的步驟,直到整個 flow 都走過一遍,當函數執行完畢也就是 ret.done === true ,這時就會呼叫 resolve 結束這個這個 promise 並呼叫對應的 callback 函數。當然,若是中途有任何錯誤發生就會呼叫 reject

 
about 2 years ago

今天在看 react-router 的範例 query params 時,看到了神奇的用法。一個自定義的 Component 在沒有接收 props 的情況下,居然可以參照到 parent 的屬性。這一切都是 Context 的緣故。

這個用法很早就被加入,被放在 1.0 的 roadmap 裡面。官方也很坦蕩蕩的說:「我們沒寫文件」。他們很早就發現這是一個很需要的功能,但由於效能的考量,並沒有正式對外公開。

最簡單的使用情境是,當你想把一個 callback function 向下傳遞數層,肯定會發現,需要透過 this.props.xxx 重複多次傳遞。一旦利用 Context 以隱含向下傳遞 (implicitly passed down) 的方式往下送,就可以直接在需要的 children 取出使用。

詳細的例子請參照 Introduction to contexts in React.js 的介紹。

 
about 2 years ago

過年期間,用零碎的時間研究 Livescript + React 的組合。

其實可以 Google 到一個例子,Using LiveScript with React,只是這個例子並不完整,少了一些宣告。

概念上是透過 React.createFactory 把元件變成元件工廠函數,搭配上 Livescript 的 Function Syntax 就可以寫出很類似 Jade 的效果。

簡單的範例

首先,先把常用的基本元件做成函數:

require! react: React

div = React.create-factory \div
h1 = React.create-factory \h1
p = React.create-factory \p

接著,我們用 React.createClass 做一個新元件:

comp = React.create-class do
  render: ->
    div null,
      div class-name: \container,
        h1 null, \Example
        p null, "This is an example page."

看了這段 Code 應該會有似曾相似的感覺吧。這樣就生成了一個新元件,此時可以用 React.createFactory 來形成新的工廠函數。

MyComponent = React.create-factory comp

如此一來,MyComponent 就可以在其他的元件中使用了。

 
about 2 years ago

這幾天在嘗試使用 Livescript 搭配 React。因為不透過 JSX ,必須使用 createFactory 或是 createElement
官方文件描述的很清楚,但因為沒認真看,所以踩了一點雷,在這歸納了一下。

ReactJS 中有三種 create 函數,分別是

  • createClass
  • createFactory
  • createElement

createClass 用來建立元件 (Component) 的類別 (Class) 。這些類別可以直接用於 JSX 中,以編寫 HTML 的方式直接使用,例如: <Component /> ,當然會需要透過 JSX Transpiler 轉成 Javascript。

createFactory 會產生類別工廠,簡單的說就是一個可以產生 ReactElement 的函數。ReactElement 為 React 渲染 (render) 時可以接受的實體。

createElement 跳過類別工廠直接產生 ReactElement。JSX Transpiler 做的事情主要就是將 <Component /> 透過此函數轉成 React Element。

參考資料

 
over 2 years ago

最近將 node 從系統安裝改為透過 nvm 安裝。這樣就可以很乾淨的同時使用 iojsnode。 (或許兩者合併後就多此一舉了 XD)

因為常常搞不清楚現在的版本,所以想要在 Shell Prompt 上提示當前版本。

nvm 提供了許多變數。

$ env
...
NVM_BIN=/Users/user/.nvm/versions/io.js/v1.2.0/bin
NVM_DIR=/Users/user/.nvm
NVM_IOJS_ORG_MIRROR=https://iojs.org/dist
NVM_IOJS_ORG_VERSION_LISTING=https://iojs.org/dist/index.tab
NVM_NODEJS_ORG_MIRROR=https://nodejs.org/dist
NVM_PATH=/Users/user/.nvm/versions/io.js/v1.2.0/lib/node
...

我是從 NVM_PATH 開始下手。一開始的想法是先取代掉 /。在用 awk 印出我要的欄位。

$ echo $NVM_PATH | tr "/" " " | awk '{print $5 $6}'
io.jsv1.2.0

但是我希望能夠有一些變化,所以要使用 formatted string ,因此改用 printf

$ echo $NVM_PATH | tr "/" " " | awk '{printf "%s-%s", $5, $6}'
io.js-v1.2.0

後來發現,原來 awk 可以改變 split string

$ echo $NVM_PATH | awk -F "/" '{printf "%s-%s", $6, $7}'
io.js-v1.2.0

最後,就全部使用 awk 完成了字串的處理。

Note 眼尖的朋友會發現最後二式的參照位置不同,原因是使用 awk 分割字串時,/ 前面的空白位置也被算進去了,因此才會將參照都往後移動一位。