為 Next.js 靜態網站產生 RSS feed

Next.js 是 React 生態圈快速崛起的一套框架,本篇文章介紹在 Next.js 當中產生 RSS feed 的方法。

Next.js with RSS

名詞簡介

以下有些名詞我選擇直接用縮寫,因此在這邊先做介紹:

  • CSR: Client Side Rendering
  • SSR: Server Side Rendering
  • SSG: Static Site Generation

起源

有鑑於此部落格太久沒有更新,上一篇文章是從 Hexo 搬到 Gatsby,這兩套框架都是非常優異,也都有持續維護的狀態。

這幾年更加吸引大眾目光的是 Next.js,其背後團隊 Vercel 來頭不小。目前的 CEO rauchg 是 socket.io 作者,看貢獻度目前也是排在第一位。夾帶這些名氣,以及 Next.js 推出的解決方案,不論是 CSR, SSR, 和 SSG 都可以處理。

因此,就選擇將本部落格再度搬移到 Next.js 了。XD

架構研究

Next.js 沒有原生支援 RSS feed 產生,所以必須要根據 Next.js 的架構去研究,在哪一個時間點產生出 RSS feed 會是最可行的作法。

在研究了 Next.js 的流程後,有幾種作法:

SSR 解決方案

如果是 SSR 處理方式,尤其是直接將 project host 在 vercel 上面。可以直接在 pages/rss.js 裡面,利用 Next.js 的架構,直接在這支 JS 檔裡面處理回傳 RSS feed 的動作。

要注意的是 RSS 需要回傳 XML 的格式,因此  在 pages/rss.js 的 getServerSideProps 就必須要指定回傳的 header。詳細的實作可以參考這篇文章

然而因為我的架構是擁有自己的 markdown file,不論用哪些框架只是幫我把這些 markdown file 轉成 HTML render 出來即可,因此 SSR 就不是我選擇的方向,也就往 SSG 解決方案去思考了。

SSG 解決方案

SSG 就是將所有的檔案都在 build time 轉成 static file,所以自然沒有 server 端可以幫忙處理 response XML 的動作,因此就必須思考將 RSS feed 直接 build 成一個獨立的 public 檔案,讓想要訂閱 RSS feed 的人可以利用該連結直接訂閱。

在程式端具體的實作流程有許多種,以下舉我認為比較可行的兩種:

  1. 寫一個獨立的 npm script,例如: "postbuild": "node lib/rss.js",基於 npm script 的定義,只要在 build script 跑完後,就去直接執行這個 npm script 在 public(Next.js 定義的公開資料夾路徑)資料夾產生對應的 RSS file。
  2. 寫一個獨立的 JS function,在 Next.js build 階段確定有執行這個 function 去產生 RSS file 到 public 資料夾即可。

這兩種真正執行的 function 內容幾乎一樣,就是包裝起來執行的方式和時間點不一樣而已。

我自己是選擇第二種,在 Next.js build 階段就有去執行產生 RSS file,可以在開發的時候比較好 debug。

實作

這次我選擇 feed 這個套件來幫忙產生 RSS feed。

一、安裝 feed 套件

yarn add feed

二、創建一個 JS 檔,產生一個 Feed instance。

import { Feed } from 'feed';

import config from '../config';

export const generateRSS = async () => {
  const feed = new Feed({
    title: config.title,
    description: config.subtitle,
    id: config.siteUrl,
    link: config.siteUrl,
    image: `${config.siteUrl}/image.png`,
    favicon: `${config.siteUrl}/favicon.ico`,
    copyright: `© ${new Date().getFullYear()} ${
      config.title
    }. All rights reserved.`,
    feedLinks: {
      atom: `${config.siteUrl}/atom.xml`,
    },
    author: {
      name: 'Daniel Tseng',
      email: 's92f002@gmail.com',
      link: config.siteUrl,
    },
  });

更多參數可以參考 官方 Example

三、把所有文章放進去 Feed instance

import { Feed } from 'feed';

import config from '../config';

import markdownToHtml from './markdown';
import { getAllPosts } from './blog';

export const generateRSS = async () => {
  const feed = new Feed({
    // 略,請見上方
  });

  const posts = getAllPosts();

  await Promise.all(
    posts.map(async (post) => {
      const validURI = `${config.siteUrl}${encodeURI(post.slug)}`;

      feed.addItem({
        id: validURI,
        link: validURI,
        title: post.frontmatter.title,
        description: post.excerpt,
        date: new Date(post.date),
        image: post.ogImageUrl,
        content: await markdownToHtml(post.content || ''),
        author: [
          {
            name: 'Daniel Tseng',
            email: 's92f002@gmail.com',
            link: config.siteUrl,
          },
        ],
      });
    })
  );
};

四、輸出至 public 資料夾

在輸出的時候,需要注意的是,那些會訂閱 RSS 的 service 會 default 找哪些路徑?

靜態檔案解決方案,根據參考大家常放的檔案路徑,做了以下歸納:

  • /atom.xml
  • /rss.xml
  • /rss/atom.xml

因此我選擇這樣做:

import fs from 'fs';

import { Feed } from 'feed';

import config from '../config';

import { getAllPosts } from './blog';

export const generateRSS = async () => {
  const feed = new Feed({
    // 略
  });

  // 略

  const invalidCharInXMLSpecRegexp =
    // eslint-disable-next-line no-control-regex
    /((?:[\0-\x08\x0B\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))/g;

  fs.writeFileSync(
    './public/rss.xml',
    feed.atom1().replace(invalidCharInXMLSpecRegexp, '')
  );
  fs.writeFileSync(
    './public/atom.xml',
    feed.atom1().replace(invalidCharInXMLSpecRegexp, '')
  );
};

至於為什麼選擇 Atom 1.0 格式而不是 RSS 2.0 格式, 主要是因為 RSS 2.0 有比較多的限制,相對之下,比較晚誕生的 Atom 1.0 格式是主流,(這點如果有講錯歡迎指教,我有看到我會馬上修正)。

因此,這邊直接選擇只支援 Atom 1.0 格式。

五、加入 Next.js 流程

在 Next.js 的流程中,有幾個基本但是重要的 function:

  • getStaticProps (Static Generation)
  • getStaticPaths (Static Generation)
  • getServerSideProps (Server-side Rendering)

在官方的文件也清楚的表明了:

getStaticProps (Static Generation): Fetch data at build time.

也就是在 build time 的時候,Next.js 一定會執行這個 function。我們便可以把產生 RSS 的動作放進這個 function 裡面,如下:

// pages/index.js
import { generateRSS } from '../utils/rss';

export async function getStaticProps() {
  const posts = getAllPosts();

  const postPromises = posts.map(async (post) => ({
    ...post,
    html: await markdownToHtml(post.content || ''),
  }));

  await generateRSS(); // 主要是這一行

  return {
    props: {
      posts: await Promise.all(postPromises),
    },
  };
}

如此一來就可以在開發階段看到 RSS feed 產生了。

結論

搬移到 Next.js 之後,寫起來的體驗,更接近原生開發 React 的感覺,想要什麼功能自己實作,彈性很大很自由,而不像是 Gatsby 整套就是為了 SSG 而生的解決方案。在 Gatsby 遇到很多問題都有現成的套件,裝上去馬上就可以用。

然而有了彈性就代表需要選擇,為什麼選擇 A 而不是選擇 B,每一步都需要思考。坦白說實作這短短幾行 code 花不了多少時間,但是背後的選擇我想才是關鍵,藉由這篇文章記錄思考的過程。

Reference

Read More


從 Hexo 到 Gatsby

本篇文章記錄了我將部落格從 Hexo 轉換到 Gatsby 的過程,以及這過程當中相關設定的經驗分享。

From Hexo to Gatsby

本篇文章並非詳細介紹 Gatsby,如想了解更多,請至官方網站

緣由

Hexo 是一套歷史已久的 static site generator,此部落格原本就是利用 Hexo 架起來的。然而,後起之秀 Gatsby 採用著不同的資料流處理方式,加上優異的架構,最重要的是該團隊獲得資金並成立公司的新聞一出,我對於這個專案後續維護更加看好,就決定搬家到 Gatsby。

搬移過程

此處列出一些在搬部落格過程中需要注意的點,並作大綱的列點,可以選擇自己喜歡的部份研究。

Markdown 整理

Hexo 原生就提供產生文章的 CLI API,因此使用上蠻方便,只要一個指令就可以自動產生 Markdown 檔。而 Gatsby 就得要自行選擇文檔的類型,因為當初選用 Hexo 就是它可以利用 Markdown 寫部落格,所以轉換到 Gatsby 自然就沿用 Markdown 了。

gatsby-source-filesystem

在部落格架構設計上,我把所有原始檔放在 ./content/_posts 底下。但想讓 Gatsby 知道文件位置,就得利用 gatsby-source-filesystem 這個 plugin,如下設定:

// gatsby-config.js
plugins: [
  {
    resolve: 'gatsby-source-filesystem',
    options: {
      name: 'posts', // Name this source
      path: path.resolve('./content/_posts'),
    },
  },
];

ps. Gatsby 的範例都利用 ``(backtick) 包住字串,但我還是習慣使用 single quote。

gatsby-transformer-remark

把原始 file 讀進來之後,Gatsby 也提供了專為 Markdown 檔處理的 plugin,名為 gatsby-transformer-remark
經過這個 plugin 處理過的資料,會被轉成 Gatsby 架構下的 node,此 node 的 type 為 MarkdownRemark,該 node 會增加許多有用資訊,例如:html, headings, excerpt 等等,省下許多 parse Markdown 檔的功夫。

// gatsby-config.js
plugins: ['gatsby-transformer-remark'];

後續還會介紹更多這個 plugin 的設定方式,也可直接參考 repo

URL 一致性

當初利用 Hexo 建立出來的 URL 為類似 /2017/07/03/利用-Hub-來自動發-GitHub-PR/ 這樣的 pattern, 為了不 breaking 既有 URL,我選擇將新版的 URL 設計和當初一樣。

此時就得讓 Gatsby 讀懂日期,這邊當初猶豫是要從 Markdown 裡面拿或者是從檔名拿日期。 將日期寫在檔名有多一個好處,就是可以利用檔名來做排序,基於這樣就決定將原本的所有 Markdown 檔都加上日期,修改過後的檔名為 2017-07-03-利用-Hub-來自動發-GitHub-PR.md

onCreateNode

Gatsby 提供 onCreateNode 此 API 操作,原理上是在 Gatsby 要 createNode 時,在該 node 上面加上一些我們用的到的資訊。 我在此處便是利用 RegExp 將檔名給 parse 出來,並在 node 上面新增一個 field 叫做 slug,也就是 URL 上面除了 domain 後面的 pathname 部分。

// gatsby-node.js
const BLOG_POST_FILENAME_REGEX = /([0-9]+)-([0-9]+)-([0-9]+)-(.+)\.md$/;

exports.onCreateNode = ({ node, getNode, boundActionCreators }) => {
  const { createNodeField } = boundActionCreators;

  // 確定是 Markdown 檔再做操作
  if (node.internal.type === 'MarkdownRemark') {
    const { relativePath } = getNode(node.parent);

    const match = BLOG_POST_FILENAME_REGEX.exec(relativePath);
    const year = match[1];
    const month = match[2];
    const day = match[3];
    const filename = match[4];

    // 組出我們要的 slug pattern
    const slug = `/${year}/${month}/${day}/${filename}/`;

    // 在該 node 上面多增加一個欄位,未來可以 Query
    createNodeField({
      node,
      name: 'slug',
      value: slug,
    });
  }
};

有了前述的 node 資料之後,我們在 Gatsby 創造這些 page,讓使用者在進入 /YYYY/MM/DD/<blog-title>/ 這樣的 URL 可以順利取得文章內容。

createPages

Gatsby 提供的另一個 API 叫做 createPages,此 API 就是你所有的 page 都需要透過該 API 來創造,如此一來 Gatsby 才可以正確的回傳該頁面。

// gatsby-node.js
exports.createPages = async ({ graphql, boundActionCreators }) => {
  const { createPage } = boundActionCreators;
  const allMarkdown = await graphql(`
    {
      allMarkdownRemark {
        edges {
          node {
            fields {
              slug # 此處的 slug 就是上述 onCreateNode 時候加上去的
            }
          }
        }
      }
    }
  `);

  allMarkdown.data.allMarkdownRemark.edges.map(({ node }) => {
    const { slug } = node.fields;

    createPage({
      path: slug, // 告訴 Gatsby 這個 URL path
      // 這邊選用的 component,就可以在 pageQuery 使用 context 所傳進去的 slug
      component: path.resolve('./src/templates/post.js'),
      layout: 'index',
      context: {
        // Data passed to context is available in page queries as GraphQL variables.
        slug,
      },
    });
  });
};

Page 設計

在 Gatsby 的架構內,每一個需要不同 URL 的頁面都需要自己創造一個 page,例如:首頁、每一篇文章、archive 頁面等等,底下列出此部落格需要的 URL:

  • /: index page
  • /YYYY/MM/DD/<blog-title>/: article page
  • /archives: archives page
  • /tags/<tag>: tags page

index page

因為 Gatsby 底下會自動的把 ./src/pages/*.js 自動 create page,所以我們在設計首頁的時候,就是去修改 ./src/pages/index.js 此頁即可。

該頁最主要就是把所有的文章時間、title 等資料拿出來,GraphQL 的 query 如下:

// src/pages/index.js
export const pageQuery = graphql`
  query BlogIndexQuery {
    # query 依照 date 排序
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      edges {
        node {
          fields {
            slug
          }
          html # 需要把 html 拿回來 render
          frontmatter {
            title
            # 可以在此處就把 date format 成我們要的格式
            date(formatString: "MMM DD, YYYY")
            tags
          }
        }
      }
    }
  }
`;

上述的 html 也可以用 excerpt 去拿,但我選擇拿 html 再選擇我要的顯示在首頁部分內容。

archives page

該頁面與 index page 大同小異,該 page 位於 ./src/pages/archives.js,唯一不同的是因為 archives page 有一種整理的效果,因此我們只要拿 title、date、tags 即可。

tags page

tags page 顧名思義就是每篇文章我會給一些 tag,寫在 Markdown 最上方,所以可以利用套件幫我們產生的 frontmatter 拿到每一篇的 tags 資料,但要怎樣擁有不同頁的 tag 呢?

此時就必須在 createPage 的時候就將所有的 tag 拿出來,然後產生每一個 tag 的 page。

//
exports.createPages = async ({ graphql, boundActionCreators }) => {
  const { createPage } = boundActionCreators;
  const allMarkdown = await graphql(`
    {
      allMarkdownRemark {
        edges {
          node {
            frontmatter {
              tags
            }
          }
        }
      }
    }
  `);

  allMarkdown.data.allMarkdownRemark.edges.map(({ node }) => {
    const { tags } = node.frontmatter;

    tags.map(tag => {
      createPage({
        path: `/tags/${tag}`, // 此處創造 tag URL
        component: path.resolve('./src/templates/tags.js'), // 選擇用 tags template
        layout: 'index',
        context: {
          tag, // 傳進去讓 tags template 的 pageQuery 可以使用該 tag 去 query
        },
      });
    });
  });
};

創造完每一頁 tag 的 page 之後,我們來看看 tags template 應該怎樣寫。

// ./src/templates/tags.js
export const pageQuery = graphql`
  query PostByTag($tag: String!) {
    allMarkdownRemark(
      filter: { frontmatter: { tags: { in: [$tag] } } }
      sort: { fields: [frontmatter___date], order: DESC }
    ) {
      edges {
        node {
          id
          html
          fields {
            slug
          }
          frontmatter {
            title
            date(formatString: "MMM DD, YYYY")
            tags
          }
        }
      }
    }
  }
`;

最關鍵的就是 Query 的參數了,利用 createPage 傳進來的 $tag,我們在 allMarkdownRemark 的參數就利用到了 filtersort 等使用情境,拿回所有擁有這個 tag 的文章資料再去做 render。

Favicon

當初設定 Favicon 也遇到一些問題,本來以為放在 /static/favicon.ico 就可以讓瀏覽器抓到,可是怎麼嘗試都不成功。 後來請教同事 @jigsawye 才發現原來是要利用 loader,也就是直接將 favicon file 給 import 到環境內,再利用 react-helmet 做操作。

// ./src/layouts/Head.js
import Helmet from 'react-helmet';

import Favicon from '../assets/favicon.png';

const Head = ({ data }) => (
  <Helmet>
    <link rel="icon" type="image/png" sizes="30x30" href={Favicon} />
  </Helmet>
);

export default Head;

Gatsby plugins & useful packages

推薦幾個在架設這個部落格用到的 Gatsby plugin

  • gatsby-plugin-react-next: v1 還是內建 react v15,所以需要裝這套
  • gatsby-plugin-react-helmet: 操作該頁面的 HTML meta 資料
  • gatsby-remark-prismjs: 可以將 Markdown 內的程式碼做上色
  • gatsby-remark-autolink-headers: 可以讓 Markdown 的 Heading 都自動加上 id,瀏覽更方便
  • gatsby-plugin-sitemap:自動產生 Sitemap
  • gatsby-plugin-feed:自動產生 RSS feed,production 才會輸出,輸出檔名為 /rss.xml
  • gatsby-plugin-google-analytics: 可以載入 GA 使用
  • disqus-react: Disqus 官方的 React 套件

結論

這次的搬家花了不少心力,其中最多的時間其實是在重新架構整個 layout,而搬家過後因為 client side render,整體速度提昇不少,也對於 Gatsby 的生態圈蠻看好,希望可以持續發展。

雖然現在市面上有很多撰寫文章的平台(例如:Medium),不過這種一手打造的部落格,擁有自己 content 的感覺,或許就是工程師的浪漫吧。

Read More


利用 Hub 來自動發 GitHub PR

起源

因為工作和平時習慣的 Git 協作平台是 GitHub,因此常常發 pull request (以下簡稱 PR) 到 GitHub 上面,而這個流程對我來說不太順手,因此便想要利用更自動化的方式來做掉。

舊有流程

一般來說發 GitHub 的 PR 流程如下:

  1. 在 local commit
  2. 推 local branch 到 remote 上面
  3. 打開 GitHub 網站
  4. 進到你的 repo 頁面
  5. 點開 PR 按鈕
  6. 編輯 PR title
  7. 按下按鈕確認發 PR

這樣的流程除了需要在瀏覽器 和 terminal 間切換外,最麻煩的是需要操作滑鼠多點好幾下,整個流程會將思緒打斷。

Hub

這是一套 GitHub 官方所維護的套件,用來加強原生 git 的功能,而這個套件剛好提供了 $ hub pull-request 的功能,可以在該 local branch 發 PR。

使用 Hub 套件發 PR 流程如下:

  1. 在 local commit
  2. 推 local branch 到 remote 上面
  3. $ hub pull-request
  4. 進入 vim 模式
  5. 編輯 vim
  6. 編輯完後需要存檔離開 vim ,Hub 才會自動發 PR

使用 Hub 後流程已改善,把在瀏覽器上的操作帶回到 terminal,但整體使用卻依舊不便,因此我便思考能不能一鍵發 PR 到 GitHub 上面,而最後找到了下面這個解決方案,雖非最完善,但方便不少。

一鍵自動化流程

Setup

$ vim ~/.oh-my-zsh/lib/aliases.zsh (in my case)
$ alias pr='hub pull-request -m "$(git reflog -1 | sed '\''s/^.*: //'\'')" | xargs open'
$ source ~/.zshrc (in my case)

usage

# local commit
$ git push origin <branch-name> && pr

藉由 Hub 的 pull-request 加上自己所寫的 alias, 此 alias 會將 commit message 的第一行拿出來自動當做 PR title, 且會自動打開瀏覽器,可以再度進行檢視或做最後的修正。

結論

能夠利用小技巧將繁瑣重複的事情給自動化,是每個人都很樂見的,在此分享,希望能幫助到可能也需要的你。

Read More


將 JS Callback 轉成 Promise

隨著越來越多 JS 開發者的提倡,越來越多人擁抱 ES6(+) 的語法,其所帶來的好處讓許多開發者願意接受。

然而一些新接觸的開發者,在流程控制上會聽聞 Promise 的好處進而開始使用,但較早期的套件會遵循著 node.js style 的 Callback;因此本篇文章將會介紹如何將這類 Callback 利用套件轉成 Promise。

Node.js Style Callback

在 JS 的流程處理上,有許多人一定會遇見 Callback function,而 node.js 有獨特的規範,遵循著這類規範會讓更多開發者受益,這類的 Callback 被稱為 Error-First Callbacks

  • 非同步的 Callback function 第一個參數是錯誤物件,有錯誤就會回傳錯誤訊息,沒有錯誤則第一個值會是 null。剩下的參數為 Callback function 正常情況下會回傳的值。
fs.readFile('/etc/passwd', (err, data) => {
  if (err) throw err; // 此處為 Node.js Style Callback
  console.log(data);
});

thenify 套件使用

使用情境

將會是把第三方套件 Node.js Style Callback 轉成 Promise,接著就可以 .then() 或者是利用 async await 去處理流程了。值得一提此套件利用的是 any-promise,理論上你可以隨時的設置你想要的 Promise 實作。

使用方法

因為 README 上面寫的蠻不清楚,以下列出幾種狀況,也推薦看套件的測試檔案範例

範例一:非同步 function 直接接收 cb

function fn(cb) {
  // 處理複雜流程
  cb(null, true);
}

const p = thenify(fn); // p is a promise now.

p().then(val => console.log(val)); // true

範例二:非同步 function 直接接收 cb,該 cb 回傳多個參數

function fn(cb) {
  // 處理複雜流程
  cb(null, 1, 2, 3); // 除了第一個 error msg 外,回傳多個值
}

const p = thenify(fn); // p is a promise now.

p().then(val => console.log(val)); // [1, 2, 3],會轉成 array

範例三:非同步 function 直接接收多個參數及 cb,該 cb 亦接受多個參數

此範例為最常被使用功能,關鍵點為 cb 位在 fn 的最後一個參數,且此 cb 遵循著上述提到的 Node.js Style Callback。

function fn(a, b, c, cb) {
  // 處理複雜流程
  cb(null, a, b); // 最後回傳 error msg(null) 和 a, b
}

const p = thenify(fn); // p is a promise now.

p(1, 2, 3).then(values => console.log(values)); // [1, 2],callback 回傳的值,一樣會包成 array

結論

隨著 node 核心原生支援的語法越來越多,選擇利用 babel 搭配就可以在此時享受到 Promise 甚至是 async, await 語法帶來的好處,然而較早期的套件甚至不預設回傳 Promise,此時便需要一些工具的幫助。

希望這篇文章能幫助到那些一接觸學習 JS 流程控制就享受到 Promise 好處,但實務上卻不太理解如何處理原生或第三方套件這類 Callback function 的開發者。

reference

Read More


JavaScript 變數宣告

JS 宣告變數的方式在 ES6 之後增加了 letconst,這篇文章將會談談 varletconst之間的差異。

變數宣告的過程

如圖所見,JS 內變數的宣告總共會經歷三個階段

  • Declaration: 在相對應的作用域範圍內註冊變數名字
  • Initialization: 分配記憶體使用
  • Assignment: 賦予該變數的值

var

var x; // Declaration, Initialization
x = 'Hello World'; // Assignment

// all in one line
var y = 'Hello World';

var 廣泛被使用,直到 ES6 推出後才有了其他的替代方式,而如果 var 被宣告在函數內,則沒有辦法在函數外部取得該變數。

function someFunc() {
  var innerVar = 'hi';
  return innerVar;
}
console.log(innerVar); // ReferenceError: innerVar is not defined

let

在 ES6 後,出現了 let 的方式宣告變數,它和 var 有共同的作用,便是在函數中封閉,額外的效果是在 block statement 內也是封閉,作用域變得更小範圍,可以減少錯誤的發生。

let x; // Declaration, Initialization
x = 'Hello World'; // Assignment

// all in one line
let y = 'Hello World';
if (true) {
  let innerLet = 'hi';
} else {
  // do nothing
}

console.log(innerLet); // ReferenceError: innerLet is not defined

上述例子代表 innerLet 只能在 block statement 內存活,跳出這個 block statement 就無法取得,我們將 let 換成 var 看看:

if (true) {
  var innerLet = 'hi';
} else {
  // do nothing
}

console.log(innerLet); // hi

由上述可以清楚看到將 let 宣告的方式改成 var 則可以在 block statement 之外取得該變數,這就是 let 相對於 var 作用域更小的關係。

const

const 顧名思義就是 constant 的縮寫,ES6 新的語法,代表說只要當宣告過後,該賦予的值就不能被更動,更嚴格來說,是該變數指向的記憶體位址不能被更動。因此,當利用 const 宣告了 Array 或是 Object,只要不重新賦予值,而是更動 array 或 object 內部狀況,是可以被接受的操作方式,參考以下例子:

const PI = 3.14159;
PI = 3.1415926; // TypeError: Assignment to constant variable.

const arr = [];
arr.push(PI); // [ 3.14159 ];

const obj = {};
obj.pi = PI; // { pi: 3.14159 };

arr = new Array(); // TypeError: Assignment to constant variable.
obj = new Object(); // TypeError: Assignment to constant variable.

Hoisting

在利用 var, let, const 宣告變數的時候,變數都會自動的做 Hoisting,也就是被抬升到該作用域的最高處,但是抬升過後,var 會對於變數賦予值 undefined,而 let, const一樣也會被抬升,但是沒有初始值,因此會有 ReferenceError 發生。

console.log(hello); // undefined
var hello;
hello = "I'm a variable";

console.log(world); // ReferenceError: world is not defined
let world;
world = "I'm a variable";

結論

在 ES6 推出後,應該盡可能的利用 letconst 去宣告變數,因為它們的限制更高,會減少出錯的機會。若有使用 ESLint,甚至有條件限制使用 var。若是情況一定需要使用 var,也應該在作用域的最一開始就宣告變數,以免發生 hoisting 導致無法預期的結果出現。

reference

Read More


babel 相關名詞簡介

babel 是現代前端工程師都不陌生的工具,它可以將 ES6 (+) 的語法轉換為 ES5 甚至是更低的版本,利用 babel 可以讓開發者即刻享受到 ES6 語法所帶來的便利性。 然而許多 babel 相關的名詞卻常常困擾想要入門的新手們,因此利用本文簡介那些與 babel 相關的名詞,以 babel 6 為主。

本篇將會會介紹到:

  • .babelrc
  • babel-cli (with babel-node)
  • babel-preset vs. babel-plugin
  • babel-register
  • babel-polyfill
  • babel-plugin-transform-runtime & babel-runtime
  • babel-loader
  • babel-eslint

本文用意並非完善的介紹整個 babel 生態系,而是作者整理自己常見且容易搞錯的 babel 相關名詞。

一、.babelrc

這是 babel 這個工具需要用到的設定檔,以下所介紹到的各項都需要該設定檔讓 babel 有轉換的依據。

方法一:

直接寫獨立的 .babelrc 檔,一個基本的格式如下:

{
  "presets": ["es2015"]
}

方法二:

直接寫進 package.json

{
  &quot;name&quot;: &quot;my-package&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;babel&quot;: {
    // your babel config here
  }
}

註:方法二雖然可以這樣寫,但不建議使用,因為在 react-native 會出現已知的問題

二、babel-cli

利用 npm 安裝 babel-cli 將會同時註冊 babelbabel-node 兩個最常使用的指令。

$ npm install babel-cli --save-dev

利用上述指令安裝在 project 的目錄底下。

babel

可以直接在終端機利用 babel 指令做 ES6 的語法轉換, 常用的情景為將 src 資料夾 build 成 lib 資料夾, 身為 library 開發者,以不要預設使用者有 ES6 的環境為佳, 上述簡單的指令如下:

$ babel src -d lib

若需更多參數,請參考官方教學。

babel-node

可以利用 babel-node myEs6.js 直接運行 ES6 的 code, 當然需要 .babelrc 檔還有相關的 presets 或是 plugins 做為 babel 轉換的依據。

babel-node 執行的時候會預設載入 babel-polyfill 使用, 因此會佔大量的記憶體空間,官方不建議在 production 環境使用。

三、babel-preset vs. babel-plugin

presets 和 plugins 這兩個 key 在 .babelrc 檔內會很常看到, 其中的差異便是一個 preset 可以包含其他不同 presets 或是不同的 plugins。

例如 babel-preset-es2015 當中包含了 transform-es2015-arrow-functions transform-es2015-block-scoped-functions 等 21 個不同的 plugins。

順序問題

babel 在執行 transform 的過程,會 plugins 先載入,且按照由上往下(由左向右)的順序載入, 但是要注意的事情,presets 會在 plugins 之後,然後載入的順序是由下往上(由右向左)的反向順序。

stage

babel 針對 stage 有實作幾個不同的 presets,包含了

  • preset-stage-0
  • preset-stage-1
  • preset-stage-2
  • preset-stage-3

stage 數字越大的 preset 所包含的 plugins 代表即將進入 ECMA262 standard, TC39 Process, 官方預設的範例 stage 0 的 preset 使用就是因為其包含了 stage 1, 2, 3 的 presets, 而許多開發者直接用 stage 0,會把全部 stage 都載入,建議花時間了解各個 preset-stage 分別載入哪些 plugins 為佳。

stage 是會隨著時間演進,在不同的階段所看到的 stage 內容可能都不一樣。

四、babel-register

當載入 babel-register 後,其接下來的 es6 語法都可以被設定的 .babelrc 做轉換,

載入的方式有兩種:

方法一:

額外建立一個進入點檔案,由於在這個進入點 node 並不知道 es6 語法, 因此於此需要利用 require('babel-register') 的方式載入

進入點檔名以 entry.js 為例:

require('babel-register');
require('./yourEs6Index');

因此未來執行 node entry.js 就可以利用 babel-register 動態載入的方式進行轉換語法。

方法二:

在終端機執行 node 的時候,直接利用 -r 參數帶入 babel-register-r 等於 --require 代表 module to preload

$ node -r babel-register yourEs6Index.js

五、babel-polyfill

什麼是 polyfill?

wiki: In web development, a polyfill is code that implements a feature on web browsers that do not support the feature.

因此 babel-polyfill 顧名思義就是 babel 幫我們做了一些現階段還沒有被各家瀏覽器通用支援的 feature,好讓我們在現階段就可以利用一些未來原生的語法,例如:Promise, Array.from, Object.assign, Array.includes 等。 像是 Chrome 對於 es6 的支援度一直以來都蠻高的,但是並非每家瀏覽器廠商都能支援,因此需要有 polyfill。

babel-node 當利用 babel-node 去運行 js 檔的時候,會預設載入 babel-polyfill, 因此你即可利用 babel-node run 一個帶有 promise 的 js 檔, 而不需再另外 require bluebird 等套件。

babel-polyfill 主要 includes 了 regenerator runtimecore-jsregenerator runtime 就是將 generator/async 轉換成 es5 語法,而 core-js 是 Modular standard library for JavaScript 集合,詳細請參閱連結。

六、babel-plugin-transform-runtime & babel-runtime

在做轉換的時候,若利用 babel-polyfill 會做 global scope,所以當你今天是要做 lib/tool 模式,沒辦法控制你的運行環境,則不適合利用 babel-polyfill,需要用 babel-plugin-transform-runtime 為佳。

  1. babel-plugin-transform-runtime 會把多個檔案 reference 到 babel-runtime 這個 package,因此當你使用 transform-runtime 就一定要裝 babel-runtime
  2. babel-plugin-transform-runtime 的轉換機制也是 alias 到 core-js,就和 babel-polyfill 一樣,所以不用再 require babel-polyfill
  3. 官方建議安裝方法如下: transform 安裝進 devDependencies
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime

七、babel-loader

loaderwebpack 用來載入各種不同類型檔案的套件,而 babel-loader 讓 webpack 可以用來執行 babel 轉換的的一種套件。

利用 babel-loader 可以利用 webpack 打包時候同時進行 babel 的轉換,以下是簡單範例檔:

module: {
  loaders: [
    {
      test: /\.js$/,
      loaders: ['babel'],
      exclude: /node_modules/,
    },
  ],
},

因為 babel-loader 的速度很慢,官方建議把 node_modules exclude 掉。

八、babel-eslint

ESLint 堪稱是近代偉大的 linter 發明之一,它可以讓使用者高度客製化的 parser 語法,而目前原生的 ESLint 支援的語法有 ES6/ES7, JSX, and object rest/spread,如果你用到的更多 babel 語法則需要 babel-eslint 來幫忙。

$ npm install eslint@3.x babel-eslint@6 --save-dev

.eslintrc 範例檔

{
  "parser": "babel-eslint",
  "rules": {
    "strict": 0
  }
}

結論

當然 babel 的套件不僅僅如此,還有 babelify, babili 等許多相關工具尚未有時間介紹,本篇所提及的介紹希望能對於部分開發者有幫助。 若有不清楚或者會誤導讀者的方向,還請不吝指教。

Read More


node.js 爬 Facebook 留言版

聽到網路爬蟲,有很多專案都是建立在 python 上面,在文字處理分析上, python 有很強大的套件可以使用,然而隨著 node.js 的發展越來越廣泛,也有許多因應的套件產生,今天將會介紹利用 FB 提供的 Graph API 來爬留言版。

Facebook Comment Plugin

本文並非會有教學範例檔,僅會針對 FB 提供的 Graph API 做簡單的範例。 此處的範例會利用 ES6 的 template strings 語法。

1. 了解 FB 留言版架構

最近 FB 推出了可以回覆他人的功能,因此留言有可能會有巢狀情形,但可以觀察到的是,目前 FB 的機制就是至多一層的回覆。因此簡單的架構如下:

comment 1
  - reply_comment 1
  - reply_comment 2
comment 2
  - reply_comment 1
  _ reply_comment 2
...
...

2. 確定該網址的留言數

`http://graph.facebook.com/?id=${URL}`;
GET 'http://graph.facebook.com/?id=http://www.google.com'
{
  id: "http://www.google.com",
  shares: 31205003,
  comments: 1323
}

因此可以知道 www.google.com 在 FB Graph API 有 1323 筆留言數。

3. 利用 Graph API 拿第一層留言

Graph API 在留言版其實是公開,只要給定 URL 就可以拿到該網址的留言, 這邊要注意的是需要確定該 URL 是 OG:URL 的參數給 FB 才拿的到。

`http://graph.facebook.com/comments?id=${URL}&limit=${comments}&filter=stream`;

底下是 return sample json

{
  created_time: "2012-04-16T12:45:03+0000",
  from: {
    name: "Sunil Maheshwari",
    id: "100000525493028"
  },
  message: "hello",
  can_remove: false,
  like_count: 0,
  user_likes: false,
  id: "381702034999_21746175"
}

若此處沒有加上 &limit 的話,會拿到比較少的數量。 加上 &filter=stream 參數,則會一併將回覆狀態的留言拿回來。 因為在此記錄部落格使用,在實作上並不會加上 &filter=stream 這個參數,以免拿第二層的時候重複還需要做額外處理。

4. 拿 Graph API 第二層留言

在拿第一層的時候,因為是 public API 狀態,因此不需要 token,且網址利用 http 就可以。 但在拿第二層留言,就需要 https + access_token 狀態。

最簡單取得 access_token 方式是到 FB graph explorer 申請 access_token 來實作。

接著需要串接的 API 格式為:

`https://graph.facebook.com/${id}/comments?access_token=${token}`;

上述 id 就是第一層拿回來的 json 格式內的 id, token 則是 access_token。

在實作上因為無法透過第一層的 API 得到是否有無第二層留言,因此若要完整的拿取全部的留言,則需要將全部的 id 跑過一次才可以得到完整的結果。

reference

Read More


在 slack 建立 hubot

slack 推出 bot 在 2016 這個時間點已經不算新鮮事,隨著 messenger 也推出自家的 bot 後,才真正開始接觸架設自己的 bot,網路上查到都是日文的資源較多,因此記錄這篇過程,希望能幫助到其他中文開發者。

slakbot & hubot

零、懶人包指令

先把會用到的全部指令列在這邊,下面會分項目做解釋

npm install -g hubot coffee-script yo generator-hubot
mkdir hubot
cd hubot
yo hubot
npm install hubot-slack --save
git init
git add .
git commit -m &quot;Initial commit&quot;
GET HUBOT_SLACK_TOKEN // https://my.slack.com/services/new/hubot
Install the Heroku Toolbelt // https://toolbelt.heroku.com/
heroku create &quot;project-name&quot;
heroku config:add HEROKU_URL=https://&quot;project-name&quot;.herokuapp.com
heroku config:add HUBOT_SLACK_TOKEN=&quot;xoxb-********-********&quot;
git push heroku master

一、安裝本地環境

npm install -g hubot coffee-script yo generator-hubot

hubot 會用到 coffee-script 和 yo 去產生整個專案,所以需要安裝在全域 -g

mkdir hubot
cd hubot

此處創建資料夾可建立自己的名字

二、產生 hubot 專案

yo hubot

這邊會問你一些問題,記得在 adapterslack 此舉會讓官方產生預設 heroku 的 Procfile 裡面多了這一行

web: bin/hubot -a slack

這是為了讓 heroku 啟動時候知道怎樣運作的指令

三、安裝 hubot-slack 套件

這是 slack 官方維護的套件,穩定度應該頗高,安裝後一併做一個專案 git 初始化並 commit

npm install hubot-slack --save
git init
git add .
git commit -m &quot;Initial commit&quot;

四、取得 HUBOT_SLACK_TOKEN

此處建立新的 hubot service 若有多個 team 帳號,請記得確定你登入的帳號是在哪一個 team 底下

取一個 hubot 要在 slack 內的名字,下圖用 hubot 做示範

hubot

接著下一步就可以取得 HUBOT_SLACK_TOKEN,記得把這個 TOKEN 記下來

五、本機端測試

HUBOT_SLACK_TOKEN=xoxb-********-******** ./bin/hubot --adapter slack

本機端記得先安裝 redis,hubot 會用到, 順利的話就可以在 slack 啟動 hubot 囉!

hubot in slack 可以打開 hubot 跟它對話, 如果看到 PONG 則代表成功

六、將本地端 server 放上 heroku

slack 官方推薦的平台是 heroku,這邊介紹如何運作, 要記得的原理就是其實上述已經在本機端可以運行了, 這個步驟就是將 server 放到 heroku 上面去跑而已。

首先安裝 Heroku Toolbelt,這部份請看 heroku 官方教學

heroku create &quot;project-name&quot;
heroku config:add HEROKU_URL=https://&quot;project-name&quot;.herokuapp.com
heroku config:add HUBOT_SLACK_TOKEN=&quot;xoxb-********-********&quot;
git push heroku master

這個 project-name 其實就是未來你的 herokuapp 的 URL, 不能和其他人重複,因此名字可以想自己容易記得即可。

heroku config:add 是將一些變數丟給遠端的 heroku 知道, 讓他可以抓到 HEROKU_URL HUBOT_SLACK_TOKEN 等, 接著就是將 local 這個 git repo push 到 heroku 上面。

七、注意事項

等到 heroku 跑完後,hubot 的 server 已經跑在 heroku 上面了, 而免費的 heroku dyno 有每 24 小時一定要停機 6 小時的規定, 因此若要拿來當正式的 bot 服務, 建議自己架 server 或者就付費買 heroku 的服務。

另外,在 hubot 專案底下的 hubot-heroku-keepalive 就是會固定戳 heroku,避免 30 分鐘後這個 dyno 就休息了。

在 heroku 上面有免費的 redis add-on 可以用, 每個月有 30mb 的免費使用量。

reference

有任何問題,歡迎留言討論。

Read More


從 deploy node.js 專案所學

學習 Node.js 已經兩年之餘,這段時間陸陸續續在開發上遇到一些問題(雷),然而隨著時間累積的叫做經驗,因此藉由此篇文章記錄從本機 development 環境到遠端 Linux 上的 production 所得到的經驗。

Deploy

以下的 localhost 環境皆為 Mac 10.10,express.js, 而 deploy 的環境皆為 Linux 14.04 環境。

1. 環境變數

NODE_ENV 是運行 Node.js 重要的變數,在本機開發的時候預設為 NODE_ENV=development

在執行 app.js (aka bin/www) 時,選擇需要的變數 (development || production),若要運行為 production 環境指令為

$ NODE_ENV=production node bin/www

當然這個 NODE_ENV 值可以直接 export 在你所運行的環境當中,

$ export NODE_ENV=production
$ node bin/www

而若不想每次開啟 shell 都要重新 export 一次,可以將 export 指令寫進 ~/.bashrc 內,之後開啟 shell 就會設定 NODE_ENV=production 了!


Q:那如何在 express.js 框架下的 app.js 拿到環境變數呢? A:只要利用 express 框架為我們做好的 API 如下:

// app.js
app.get('env');

即可得到 NODE_ENV 值。

2. config 檔設定

關於 config 檔的設定,每個人有不同的習慣,我介紹我常用的 config 檔設定方式。

// config.js
var config = {
  development: {
    port: 3000,
    // anything else
  },
  production: {
    port: 3001,
    // anything else
  },
};
module.exports = config;
// app.js
var config = require('./config.js')[app.get('env')];
var port = config.port; // production mode will return 3001

這樣設定 config 檔後,未來就可以利用 NODE_ENV 的不同來判斷應該要連接的資料,例如在 dev DB 和 production DB 的分開等等情況。

3. ejs 樣板引擎快取問題

在樣板引擎方面我習慣 ejs,而 ejs 會在 production 的狀態下把 view template 快取起來,加速 render 的時間,因此需要做 restart node server 的情況才可以解決快取問題。

ps. 或許這個問題有其他更好解法,非常歡迎協助補充。

4. node 執行 .js 檔

因為曾經撞過這些雷,單純就是經驗不足,以致於值得記錄一下 XD

直接提供 debug 經驗談:

  • chmod -x yourfile.js // 權限問題
  • 讀檔&寫檔 // 請確定相對路徑絕對路徑在環境的問題
  • 第一行請加上 #!/usr/bin/env node // 讓環境找得到 node 去執行它

5. MongoDB 的匯出和匯入

Q:在本機端匯出和匯入都好好的,不知道為什麼到遠端的環境就沒有辦法匯入? A:原因是語系問題,記得在 DB 匯入前先執行 export 或寫入 bashrc 檔

export LC_ALL="en_US.UTF-8"

後記

花時間經歷過的才會印象深刻,上述這幾點都是我利用時間所換來的,將此記錄在這邊,也希望能或多或少幫助到一些人:)

Read More


從 Pocket 儲存全文到 Evernote

Pocket 是一款可以稍候待讀的 app,其漂亮的介面和離線閱讀的功能,使我對於它愛不釋手。 然而從英語語系出發的 Pocket 團隊,雖然在專業版提供全文檢索(full text search)的功能,但是在繁體中文上面還是略顯不足,常常找不到已經封存的文章內容,因此本篇記錄利用 Pocket 儲存到 Evernote 的過程。

RSS to Evernote

Pocket 的閱讀介面是它的一大優勢,而 Evernote 的搜尋功能是有目共睹的準確,我們將利用 IFTTT 這個自動化工具來實作「當我從 Pocket 封存項目後,自動儲存全文到 Evernote」。

0. IFTTT 內建 Pocket 問題所在

有使用 IFTTT 的朋友應該知道說其實它有內建 Pocket 的選項,但是由於它提供的 Pocket 儲存只有所謂的 Excerpt 的功能,也就是只有部分的內容,並沒有辦法全文儲存到 Evernote 的 note 當中。

ifttt pocket feature

因此我們的解決步驟為:

  1. 建立 Pocket archive item 的 public full text RSS feed
  2. 創建 RSS to Evernote 的 recipe
  3. 問題解決

未來就可以利用 Evernote 強大的搜尋功能來做到找到曾經閱讀封存的文章。

1. 建立 Pocket 的 Full Text RSS 來源

  • 首先到 Pocket > Options > Privacy 把 RSS feed 設為 public
  • 點選 Archive feed 取得 Pocket 帳戶底下封存項目的 RSS feed link,連結應該為 http://getpocket.com/users/<你的帳號>/feed/read
  • fivefilters 建立 full text RSS,貼上你的 feed url 後,按下 Create feed
  • 把視窗連結記錄下來,這連結即為你的 full text RSS 來源

2. 創建 RSS to Evernote Recipe

  • 根據此 recipe 創建你自己的版本
  • 將上述的 full text RSS 連結貼上
  • 根據步驟創建你的 IFTTT recipe

完成上述步驟後,即可在 Evernote 你所命名的筆記本內看到你在 pocket 所封存的項目囉。

使用心得

其實在 pocket mobile app 上面,有直接儲存到 Evernote 的選項,但我閱讀文章完如果值得存下來的,我習慣直接 archive 起來。 實際使用這個 recipe 後,發現有一些網站的 full text RSS 抓的並不是很準確,速度也沒有很快,通常都要半個小時後才會在 Evernote 出現,但在網頁版並沒有存到 Evernote 的選項,在權衡下,我還是選擇使用此 recipe 來做為未來可以在 Evernote 搜尋的自動化工具。

reference

Automate Full Text of Pocket Backup to Evernote with IFTTT and FiveFilters

Read More


Linux 之 command line 上手

開發者對於 command line 一定不陌生,然而 Mac OS 會受到許多開發者的青睞,是因為其本身就是依照 unix 系統做開發,因此對於虛擬主機需要用到 command line 自然不陌生,整合性很好。

這篇 blog 記錄網站開發超過兩年半經驗的我,最常用到的終端機指令 (command line)。

本篇針對的讀者是 mac 新手。

環境設定

建議下載 iterm2 來使用,有興趣可參考設定

常用指令

前面加上錢字號($)代表此行為 command line 開始,真正在打的時候不用加入$

$ cd: 移動 root 位置 $ cd ..: 移動到目前所在位置的上一層 $ cd ../..: 移動到目前所在位置的上兩層

$ pwd: 列出目前完整路徑 --> 可以知道自己現在在哪邊,再決定要如何利用 cd 移動

$ ls: 列出所在目錄的檔案 $ ls -a: 列出的目錄檔案包含隱藏檔 $ ls -al: 列出的目錄檔案包含隱藏檔 & 檔案屬性和權限

$ vi(m) **.xx: 創建檔名為**,附檔名為xx的檔案 --> 之後會進入 vi(m) 文字編輯模式,推薦查閱鳥哥 vim 教學

$ mkdir ***: 創建名稱為***的資料夾 $ rmdir ***: 移除名稱為***的資料夾 --> 需確定資料夾為空

$ cp dest1 dest2: 把 dest1 檔案複製到 dest2 的位置 $ mv dest1 dest2: 把 dest1 檔案移動到 dest2 的位置,亦可作為變更檔名使用,例如 $ mv test.txt no-test.txt,就可以把檔名 test 的文字檔改變成為 no-test 檔名。

$ sudo su: 取得 root 權限

以上列出我最常用的指令,許多指令都可以帶有特殊的參數,unix base 底下的 command line 也不只這些,想要更進一步,可以再多去參考書籍或是教學。

reference

鳥哥 Linux 檔案與目錄管理

Read More


利用 crontab 來做 Linux 固定排程

近期有個需求,要在 Linux 上執行固定週期的時程,發現利用 crontab 這個內建的功能便可以完成,本篇記錄使用過程以及相關的參數。

crontab 介紹

crontab 是 Linux 內建的機制,可以根據設置的時間參數來執行例行性的工作排程。

上述這張圖可以清楚的顯示出前五項參數應該要帶進去的數字。
依序是分鐘, 小時, 日期, 月份, 星期, command
參數為0-59, 0-23, 1-31, 1-12, 0-6, 需要執行的command

※ 星期參數為0代表星期日

參數特殊字符意義

【*】:星號,代表任何時刻都接受的意思
【,】:逗號,代表分隔時段。例如:30 9,17 * * * command,代表早上 9 點半和下午五點半都執行 command。
【-】:減號,代表一段時間範圍。例如:15 9-12 * * * command,代表從 9 點到 12 點的每個 15 分都執行 command。
【/n】:斜線,n 代表數字,表示每個 n 單位間隔。例如:*/5 * * * * command,代表每隔 5 分鐘執行一次 command。

還有一些人性化的參數,一次取代全部五個數字參數

【@reboot】 :僅在開機的時候執行一次。
【@yearly】 :一年執行一次,和0 0 1 1 * command效果一樣。
【@annually】:(和@yearly一樣)
【@monthly】:一個月執行一次,和0 0 1 * * command效果一樣。
【@weekly】:一個星期執行一次,和0 0 * * 0 command效果一樣。
【@daily】:每天執行,和0 0 * * * command效果一樣。
【@midnight】:(和@daily一樣)
【@hourly】 :每小時執行,和0 * * * * command效果一樣。

crontab 操作

crontab 是會根據不同的使用者去判定可以操作的範圍。

$ crontab -l: 列出該使用者擁有的 crontab 指令
$ crontab -e: 編輯該使用者的 crontab 指令
$ crontab -r: 將使用者的 crontab 全部清除!( 小心使用

編輯完後就可以存檔離開,Linux 系統便會依照你設定的排程固定做事,非常方便。

※ 下達指令請用 絕對路徑 避免錯誤

範例參考

$ */5 * * * * /home/ubuntu/test.sh:每五分鐘執行一次測試 shell script
$ 0 9 * * 1-6 node /home/ubuntu/workspace/report.js:每天早上九點(除了星期日)執行 report.js 這隻檔案

reference

Schedule Tasks on Linux Using Crontab 鳥哥的 Linux 私房菜 例行性工作排程 (crontab)

Read More


Sublime 與 iTerm 的視窗配置

常用的編輯器是 Sublime Text 3,但是終端機是 iTerm2,兩者一直沒有 IDE 般的整合。 本篇記錄下如何更改 iTerm2 的視窗配置,讓兩者操作體驗上有更佳的配合。

一般情境

一般在 Sublime 和 iTerm 之間切換,我都是利用 cmd+tab 來做切換,但是這樣的使用情境,如果在筆電上開發,則會在執行 iTerm 的時候遮到 Sublime 的內容。如下圖所示:

讓我們更改 iTerm 的視窗配置,來改善這樣的情況!

iTerm 視窗設定

根據上圖,依序找到 Profiles --> Window --> Style: Bottom of screen 調整完後視窗的高度會根據設定的 Rows 高度來決定。

快捷鍵設定

根據個人習慣,在不與 Sublime 相關的快捷鍵衝突,我建議採用 cmd+. 來啟動 iTerm。

※ 記得重新啟動 iTerms 來檢視設定的效果。

成果 Demo

做完上面的設定就大功告成了!

未來在編輯的時候,就可以利用 cmd+. 來啟動&關閉終端機,操作感覺接近是內建在 Sublime 的環境。

如下圖所示:

Enjoy!

Read More


Sublime Text 3 Mac 指南

本篇是我根據自己使用習慣所做的快捷鍵整理,使用 sublime text 這套編輯器已經有 2 年之餘,本身是個快捷鍵愛好者,對於發掘好用的快捷鍵樂此不疲,因此整理常用的快捷鍵在這篇,針對的是 mac 使用者所使用者快捷鍵,希望對各位有幫助。

快捷鍵

左邊為本篇所採用的縮寫,右邊則為鍵盤上面的標示

  • cmd = command
  • shift = shift
  • option = option (alt)
  • control = control
  • pkg-ctrl = package control (command + shift + p)

基礎模式

「基礎模式」介紹非 sublime 專用的快捷鍵,是一般使用者都可以快速上手的部分,想要看進階的可以跳過這部份。

1. cmd + o (open)

快速開啟整個資料夾(專案)

2. cmd + w

關閉視窗分頁

3. cmd + n

開新分頁

4. cmd + shift + t

重新開啟剛剛關閉的分頁

5. cmd + shift + v

貼上時,符合縮排

畫面配置

以下介紹 sublime 的畫面配置,常常因為編輯情境的所需,利用快捷鍵讓自己的畫面配置更加有彈性。

1. cmd + option + 數字

分割視窗,讓你的編輯範圍有多個 panel。 常用為cmd + option + 1cmd + option + 2 之間切換。 使用情境:左邊.html 右邊.css,編輯起來快速又方便。 建議:利用空白鍵右邊的兩個連續按鈕搭配數字。

2. cmd + k 再 cmd + b

關閉左側資料夾目錄,讓畫面變得更寬敞。 這是我非常使用的一個快捷鍵,可以讓編輯的區域變得更大。

3. cmd + shift + control + f

進入 zen 狀態,單份文件變成全螢幕,且左邊會自動縮排。 使用情境:當不常需要切換檔案時,此模式可以專注在單一檔案上,打這篇 blog 時我便這樣使用。 建議:快捷鍵不好記,可以點選View --> Enter Distraction Free Mode

zen

選取

底下介紹的部份,回到 sublime text 編輯器本身,因為重點在編輯部分,因此在此將「選取」特別整理成一區。

1. cmd + d (可連按)

快速選取一範圍內的字串,連按d的話會選取整份文件內相同的字串。 當選取完後,可以直接打字,因此就可以將整份文件的字串全部改成新字串。

2. cmd + l (可連按)

選取游標在內的一行,連按l的話會往下選取下面的行數。

3. cmd + shift + l

此功能常與上述cmd + l配合,當選取多行後,按下cmd + shift + l,則會在多行的情況結尾出現游標,可以做多行編輯。

4. option + 滑鼠拖拉

當按住option後,搭配滑鼠拖拉便可以一次選取多行,並且產生游標。 注意:拖曳的時候,滑鼠必須是由上到下垂直的選取狀態

5. cmd + 滑鼠點選

按住cmd後,利用滑鼠在文件內點選,便可以在任何位置新增游標,產生多選狀態做編輯。

6. cmd + 左 或 右

讓你的游標可以快速的回到該行的最前面或是最後面。

7. shift + 左 或 右

每按一次會選擇一個字元,可以更加精準的選取自己要的部份。

8. cmd + shift + 左 或 右

從游標所在處,往前選取或者往後選取該行到底。

尋找

在 sublime 裡面尋找的功能做的非常強大,不論是文件內、或是文件名稱都可以快速找到。 底下將會利用 GoTo Anything 這個強大的內建功能來實作。

1. cmd + p + 輸入檔名

利用cmd + p,之後等視窗出現後,即可輸入你要找的檔名,按下 enter 即可開啟。

2. cmd + p + ":" + 行數

此功能相同於control + g,可以快速的跳到你指定的行數。

3. cmd + p + "@" + function name

此功能相同於cmd + r,可以快速跳到定義的 function 建議:若是知道要找 function,建議使用這個而非使用cmd + f

4. cmd + p + "#" + keyword

此功能可以快速找到文件內的關鍵字。 個人比較少用這個功能,利用cmd + f時,可以持續按 enter 找到目標。

5. cmd + shift + f

全文搜尋,可以找出「整個 project」內的關鍵字。 在 Find Result 內,點選兩下,便可以跳到該文件,這是我覺得最實用的部份。

快還要更快

1. cmd + control + 上 或 下

將選取起來的行,整段往上或往下移動。 使用情境:當幾行 code 需要移動不算太大範圍的時候,可以使用這個快捷鍵,而不用剪下再貼上。

2. cmd + /

將該行註解。 個人建議:搭配cmd + l(連按)可以選取多行,一次註解起來。

reference

  1. GETTING STARTED WITH SUBLIME TEXT 3: 25 TIPS, TRICKS, AND SHORTCUTS
  2. Sublime Text 全程指南

Read More


Nginx remove .html filename

Nginx 是一套輕量化的 web server,因為它的輕量、高效能而越來越多人喜歡使用它來做為網頁伺服器或是反向代理伺服器,本篇將介紹靜態網頁在 nginx 上移除.html 附檔名的作法。

Nginx

一、Start

本篇要做 nginx 這套 web server 的設定檔更改,來達到雖然存取靜態頁面,卻可以利用 mydomain.com/user 的 URL 來拿到所要的靜態頁面。

其實是我單純是因為不想要看到.html 這樣的附檔名,這看起來不專業!

本篇環境為 ubuntu14.04 下執行。

二、Static file permission

在 nginx 下,你要將靜態檔案放在 server 哪邊都可以,在此我根據之前 apache 習慣的設定,放在/var/www

cd /var
sudo mkdir www
sudo chown -R www-data:www-data /var/www/mydomain.com
sudo chmod 755 /var/www

如此一來你便可以將整個靜態網站檔案放在/var/www/mydomain.com 目錄底下。

三、Nginx conf setting

cd /etc/nginx/sites-enabled
sudo vim mydomain.com

修改底下內容為你要的設定

server {
  listen        80;
  root          /var/www/mydomain;
  index         index.html index.htm;
  server_name   mydomain;
  location / {
    try_files $uri $uri/ $uri.html;
  }
}

此設定將會當抓到$uri 時,nginx 會自動帶入$uri/或是$uri.html 因此我們送出mydomain.com/user,nginx 會試著搜尋mydomain.com/user/mydomain.com/user.html

四、Remove default conf

sudo rm /etc/nginx/sites-enabled/default

在我設定的時候,需把 default 刪除後,才可以正常的讀取到新設定的 mydomain.com 檔,歡迎各位先進補充這點。

五、Restart Nginx

/etc/init.d/nginx restart || sudo service nginx restart

如此一來你便可以利用mydomain.com/user純取到相關的靜態頁面了!

reference

How To Set Up nginx Virtual Hosts (Server Blocks) on Ubuntu 12.04 LTS

Read More


為你的mac終端機加上alias

在使用 iTerm(終端機)一段時間後,總覺得每次開啟新分頁,要進入到一個很深的資料夾略顯麻煩,因此上網查了簡易的方法,很快速便可以讓自己少打很多 code,把時間花在其他更重要事情上。

iterm2

alias

顧名思義就是別名,其語法如下,舉 mac 的 apache server 所在為 example:

alias goproject='cd /Library/WebServer/Documents/yourproject'

因此在 iTerm 打上這串後,未來便可以使用goproject 直接執行後面那串,快速又方便。

永久執行 alias

在設定完 alias,原本以為就這樣,結果發現如果重開 iTerm 後,之前設定的 alias 都不見了,原因是因為沒有真正寫入 bash 檔,因此在開啟 iTerm 的時候,並沒有被載入,所以我們要將

sudo vim /etc/bashrc
// 在bashrc檔裡面加上新的一行
alias goproject='cd /Library/WebServer/Documents/yourproject'

ps. 因為是 root 權限,存檔的時候記得要用:wq!強制寫入。

重新開啟 iTerm 後,便可以使用 goproject 來快速進到你要的路徑,當然你也可以自行設定你要的指令。

alias 相關指令

alias // 列出所有的alias檔
unalias goproject // 把goproject這個alias刪掉

reference

How to use the alias command

Read More


Web好UI設計法則 2

本文譯自goodui,會寫這系列並非逐字翻譯,而是在研讀過後利用自己的方式表達並記錄分享之,全文圖片版權皆為 goodui.org 所有,此為系列第 2 篇。

6、區分已被選取和可點選區塊

利用顏色、深度、對比來讓使用者確切的知道他們現在處於網站的哪一個地方,了解什麼地方可以點選,以便讓他們往下繼續逛他們趕興趣的頁面或內容。頁面上可見的文字區塊大致分成三種情況,分別是可以點(clickable)連結或按鈕、被選取(chosen)項目以及其他文字(plain text)。

底下這張圖示指出,藍色的字代表可以點擊(clickable),而黑色的字代表你現在正在這個項目(chosen)內,簡單清楚的表達出區分的效果。

idea006

7、試著推薦而非列出都一樣的選項

當你有多重選項的時候,有一個強調的選項或許對於使用者來說是一個不錯的刺激。

在這篇心理學研究指出,越少的選項可以讓使用者決定得更快,因此,試著強調某個特定的訴求吧!

idea007

8、試著用 undo 選項,而非確認

在你要刪除一個動作的時候,如果視窗一直跳出「您確定要刪除嗎?」的訊息,還要你多按一個按鍵才能確定刪除,是不是有點惱人?

原作者提到他相信大多情況我們都不會誤按功能按鈕,有的話也是少數,因此利用重作(undo)的功能而非要使用者一直確認,如此一來能讓使用者更加有掌握感,當他們要進行大量的刪除動作,便可以提高效率,不小心誤刪的情況發生,還有重作的選項可以復原。

idea008

9、你的 TA 是特定族群,而非全部人

這是一個決策,純看你要針對某特定的族群或是針對全部人,有利有弊是一定的。當你針對某特定族群打廣告,勢必會壓縮到其他的群眾,進而產生排他性。這種策略的風險是你可能會削減自己短期和限制潛在的客戶。

idea009

10、給明確的指示

給使用者明確的指示,讓他們知道點下這一個按鍵或已經滑動到這個頁面的最底端,接下來他該何去何從?千萬不要用那種「或許」、「應該」的字眼造成不確定感。要讓你的使用者知道下一步該怎麼走,至於要不要走,就交給使用者自己去決定了。

idea010

Read More


Nginx with Node.js in different port

Nginx 因為它的輕量、高效能而越來越多人喜歡使用它來做為網頁伺服器或是反向代理伺服器。

由於近期想要把不同的 node.js 程式放在同一個 server,因此開始研究 nginx 用法,記錄下來我的實作方式。

Nginx

一、Domain 指向主機

將不同的 domain 都指向你的主機 ip,此時都會指向 HTTP 預設的 80 port,後面再用 nginx 設定由不同的 port 去處理不同的 node.js 程式。

二、安裝 Nginx in Ubuntu

sudo apt-get update
sudo apt-get upgrade // 確定抓到套件
sudo apt-get install nginx
sudo service nginx start

在安裝的時候,記得你如果有其他 server 在 run 必須要停掉,不然佔住 80 port 是沒有辦法裝成功 nginx 的。

三、將 Node.js 設定不同 port

會寫 node.js 應該會將 port listen 在不同的 port,注意不要用常用的那些 port 即可。例如:80(HTTP)、22(SSH)。

四、設定 Nginx 資料夾檔案

進到/etc/nginx/sites-enabled,然後創建跟你 domain 一樣的檔案,記得權限要用sudo去創

sudo vim domain1.com

檔案內容

server {
    listen 80;
    server_name domain1.com;
    access_log /var/log/nginx/domain1.access.log;
    location / {
        proxy_pass    http://127.0.0.1:4000/;
    }
}

同理創建 domain2.com,記得 4000 port 要改成你設定的 port。

五、重啟 Nginx

/etc/init.d/nginx restart

記得做過更動後,要重新啟動 nginx 才有用。 如此一來,不同的 domain 就可以連到同一台 server 的不同支 node.js 去執行了。

reference

node.js + nginx - And now? wikipedia nginx

Read More


CI初體驗for靜態頁面

第一次因為專案需要,而跟 PHP 後端工程師配合,我們選定了CI這套 framework 來使用,對於 CI 算是第一次接觸,因此想把從無到有的架設過程給記錄下來。

codeigniter

1、擁有 php 環境

Mac 內建 Apache,只要打開即可。

sudo apachectl start

2、安裝 CI

  • 官網下載整份文件檔
  • 放進 server 的路徑下 /Libary/WebServer/Documents/
  • 安裝完畢!

3、讀取靜態頁面

新增頁面

此處新增名為 page.php 的檔案 application -> view -> cep(optional dir) -> page.php

  • page.php

新增 static files

如 CSS、JS 檔,習慣性會創建 assets 資料夾將他們放進去,並且放在跟 application 同階層的 dir 內,如圖。 assets

新增 route

application -> config -> routes.php 新增如下:

$route[&#39;(:any)&#39;] = &#39;cep/$1&#39;;
$route[&#39;default_controller&#39;] = &quot;cep&quot;;
$route[&#39;404_override&#39;] = &#39;&#39;;

新增 controller

在 controllers 資料夾內,新增 php 檔,內定新的 public function,參照 welcome.php 修改即可。

新增一個 class extends CI_Controller,裡面加上 page()這個 public function。

class Cep extends CI_Controller {

    public function index()
    {
        $this-&gt;load-&gt;view(&#39;cep/index&#39;);
    }

    public function page()
    {
        $this-&gt;load-&gt;view(&#39;cep/page&#39;);
    }

}

路徑修改

  • application -> config -> config.php 改一行 $config['base_url'] = '/專案資料夾名字/';
  • application -> config -> autoload.php 改一行 $autoload['helper'] = array('url'); 原本是沒有 url,加上 url。
  • page.php 這頁的 CSS 和 JS 檔,修改成
&lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;?php echo base_url(); ?&gt;assets/css/bootstrap.min.css&quot;&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;?php echo base_url(); ?&gt;assets/css/main.css&quot;&gt;
&lt;script src=&quot;&lt;?php echo base_url(); ?&gt;assets/js/vendor/modernizr-2.6.2.min.js&quot;&gt;&lt;/script&gt;

如此一來便可以在http://localhost/專案資料夾/index.php/page 看到靜態頁面了。

4、移除 CI URL 上的 index.php

因為每次網址上面都需要有 index.php,覺得不好看,因此兩個步驟把它改掉。

  1. 在 root 加上.htaccess檔,內容如下
RewriteEngine on
RewriteCond $1 !^(index\.php)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L,QSA]
  1. 修改 apache 設定,將 httpd.conf 改掉。 原本 AllowOverride none 改成 All 即可。
&lt;Directory /Library/WebServer/Documents&gt;
    Options FollowSymLinks
    AllowOverride All
&lt;/Directory&gt;
  1. 重新啟動 apache sudo apachectl restart

後記

因尚有其他專案,故本專案檔是全部在一個資料夾內,非直接在 web server 的 root 實作。

Read More


Web好UI設計法則 1

本文譯自goodui,會寫這系列並非逐字翻譯,而是在研讀過後利用自己的方式表達並記錄分享之,全文圖片版權皆為 goodui.org 所有。

goodui

1、頁面單欄式而非多欄式

採用單欄式的好處是可以讓讀者自然而然的了解閱讀方向為由上而下,採用多欄式的頁面設計,會有額外增加的 risk,會讓讀者容易分心。在文章(頁面)的最後採用 call to action,引導讀者點進你想要的導向頁面。

idea001

2、試著給些小禮物

好朋友間互相送禮,是很正常不過的對吧?在面對你的使用者也是一樣的,根據互惠原則,給禮物是一個激勵使用者的手法,讓使用者會更願意回來你的網站。

idea002

3、把相似功能的介面合併在一起

在過去,我們很容易將功能差不多的介面,在頁面上分成好多部分。當你的 UI 越分散,那麼該網站的使用者其學習曲線便越高,試著重新設計你的 UI 吧,把那些功能相近的按鈕、區塊放在一起。

idea003

4、讓別人來說,而非自己說

列出那些曾經在 social media 討論本身產品的人,利用他們的見證會比自己在網站上面寫還來的有用。「Our customers say」會比「We are awesome」還更具有說服力。

idea004

5、重複你的主要訴求

你的 call to action 是網頁中重要的項目,尤其在很長的頁面時,不要吝嗇讓它出現超過一次。當使用者滑到頁面底端時,是該他們做決定的時候了,離開或是完成你的主要訴求。

idea005

Read More


Sublime Text 2 實用套件

Sublime Text 2

Sublime Text 2 是網頁開發者都不陌生的一套編輯器,除了單純的文字編輯外,它還有很多實用的套件,這篇來介紹我平常常用的 Sublime Text 2 套件。

編輯環境

  1. Mac 10.9
  2. Sublime Text 2

Mac 環境,所以快捷鍵會介紹 command 的配置,若 windows 版本請自行查閱。

安裝 Package Control

所謂 Package Control 就是 Sublime Text 2 用來裝套件的,因此在裝其他的套件之前,我們必須先來安裝 Package Control。

開啟Sublime Text 2
開啟console,快捷鍵ctrl+`
貼上以下程式碼

import urllib2,os; pf=&#39;Package Control.sublime-package&#39;; ipp = sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener( urllib2.ProxyHandler( ))); open( os.path.join( ipp, pf), &#39;wb&#39; ).write( urllib2.urlopen( &#39;http://sublime.wbond.net/&#39; +pf.replace( &#39; &#39;,&#39;%20&#39; )).read()); print( &#39;Please restart Sublime Text to finish installation&#39;)

程式碼可以參照官網

安裝完後,未來我們就可以使用cmd+shift+p,打入install package,即可啟用 Package Control,如下圖

install package

安裝套件步驟

  1. 首先cmd+shift+p
  2. 鍵入install package
  3. 跳出新的輸入欄位後,在輸入你要的package名稱
  4. 看著左下角,等它跑完
  5. 重新啟動 Sublime Text 2 即安裝完成。

必裝套件

一、BracketHighlighter

這是一套超過 196K 人裝的套件,如圖所示,寫 html 常常遇到不知道 close tag 在哪邊,裡用它可以清楚的將 close tag 標示出來。
另外它有一個很好的地方,就是會在每一行的前面列出來目前的 tag,不同的語言還有不同的 icon,因此可以更快的知道自己的位置。

二、Emmet

以前它叫做 Zen coding,現在則改為 Emmet。

可以將需要重複的 html 用很簡短的方式寫出來,例如:

  1. .container>.col-lg-4*3
  2. 按下 tab 後便會出現
  3. Emmet

若你發現按下 tab 後竟然沒有用,記得確定自己是不是在 html 文件內。

view --&gt; syntax --&gt; HTML(5)

在 HTML5 的文件下,!+tab 會有出現 HTML5 的 snippet 出現,非常好用!

三、Pretty Json

有利用到 JSON 的人,想必一定會為了格式上面的問題而煩惱,只要裝上這個,JSON 立刻變得很好看。

裝完之後,把你要修改的 JSON 選取起來,按下快捷鍵

cmd+ctrl+j

立刻就可以把 JSON 變得很漂亮,也可以自行進去定義縮排大小。

四、flat theme

flatland

其實這個套件全名是 flatland 才對,可以把 Sublime Text 2 的整體環境變得扁平化。

安裝方法:

  1. 打開 Package Control
  2. 輸入 Theme - Flatland 即可

以上介紹了一些我常用的 Sublime Text 2 套件,但一直沒有找到好看的主題,如果你有推薦的主題,非常歡迎交流!

Read More


wordpress解決permalink固定網址問題

本 case 為希望主目錄可以直接連到 wordpress,但是 wordpress 是另外放在一個資料夾。

主目錄:/var/www
wordpress資料夾:/var/www/blog

一、.htaccess

wordpress 會利用.htaccess 去更改固定連結,是位在根目錄,也就是/var/www底下,wordpress 會根據你的網站位置URL去設定.htaccess

利用終端機產生.htaccess

vi .htaccess
chmod 777 .htaccess

二、進入後台設定固定網址 permalink

這邊我選擇文章名稱的格式

固定網址

因為剛剛上面有設定.htaccess 權限打開到最大,因此進後台設定完,記得把 chmod 改為 644,要注意安全性。

三、設定 mod_write

我的 wordpress 是架在 Amazon EC2 上的 ubuntu,server 是 apache2,因此上網搜尋相關資料,解決辦法如下:

sudo a2enmod rewrite
sudo /etc/init.d/apache2 restart

只要兩行就可以解決!

後記

在解決這個問題的時候,找了很多資料,一來是不明白.htacces 真正的目錄,二來是不知道 ubuntu 的 rewrite 要打開,因此在這邊記錄下來,讓有相同困擾的人可以了解。

Read More


EC2利用tasksel架wordpress經驗分享

看了這麼久的雲端資源,總算開了算是自己真正學到東西的第一台 Amazon EC2 server,目的是想要練習把 wordpress 架到 EC2 上,在此分享我的架設經驗。

Amazon web service

一、擁有 AWS 帳號

關於開啟 AWS 的過程,實際走過一遍之後,阿正老師的這篇,其內寫的不錯,推薦跟著走一遍,就會了解很多。

二、開啟自己的 instance

在實際走過後,會發現阿正老師這篇真的超用心,因此接下來主要會利用這篇,再加上些我的補充。

  • instance 地理位置

提到將主機開的位置,現在已經有 tokyo 的據點,離台灣更近,所以建議將 instance 位置設在 tokyo

建議將 instance 位置設在 tokyo

  • instance 選擇方案

EC2 ubuntu server for free

利用 VISA 卡,選擇免費方案(圖中有星星的都是免費方案),在這邊我選擇 ubuntu 來做為我的系統。

  • key pair 創建&下載

在阿正老師的文章內看到關於 key pair 介紹,很重要,一定要記住要把下載下來的 pem 給管理好,未來是需要利用它來做 ssh 登入主機。

.pem 檔需要存好,一台主機配對一個 key pair,且不能做更改,

三、設定 Security Group

在沒有設定 security group 的時候,新開的 instance 可能是鎖起來的,會有 SSH 連線上的問題。

  1. 進入 console.aws.amazon.com
  2. 左方導覽列選擇 security group
  3. 選擇 instance 後,下方的 tab 選取Inbound
  4. 分別加入SSH&HTTP,Source 部分都維持 0.0.0.0/0 即可,加入後記得要按Apply Rule Change才生效

記得開啟 SSH(20)、HTTP(80)

四、申請 Elastic IP

每一個 instance 都應該要綁定一個 elastice ip,未來可以作為連線使用。

  1. 同上,進入 console.aws.amazon.com
  2. 選擇 Elastic IPs
  3. 選擇 Allocate New Address
  4. 申請完之後記得要 associate 到你的 instance

申請完 elastic ip 後,原本的 Public domain 前半部分會改變為新的 ip

*_ 其實我在實作時,是先做了 SSH 連線,後來在去申請 elastic ip,結果 associate 完後,我又要 ssh 連線,發現沒有辦法登入,之後才瞭解是做了 elastic ip 後,連線的 ip 也需要一並跟著改變。_

五、SSH 連線進入自己的 instance

  1. 打開終端機(推薦 iterm)
  2. chmod 600 ~/.pem
    要記得把.pem 檔改權限,不然會登不進去
  3. ssh 連線使用以下 command line
    ssh -i ~/.pem ubuntu@ip _ ~/.pem是此 instance 的 key pair .pem 檔的路徑 _ ubuntu是因為我用 ubuntu 當做 OS,如果當初選擇 Amazon linux 的話,則需要輸入ec2-user取代 ubuntu * ip則為 instance 的 ip,進到 console,左側選 instance,拉到底下看見
    Public DNS: ec2-xx-xxx-xxx-xx.ap-northeast-1.compute.amazonaws.com
    則 xx-xxx-xxx-xx 改成xx.xxx.xxx.xx即為你的連線 ip
  4. 連線成功會看到ubuntu@ip-xxx-xxx-xxx-xxx:~$字眼!那就恭喜了!

pem 檔的權限要更改為 600

六、環境設定

剛進到 instance,記得將環境設定一下

  1. sudo apt-get update + sudo apt-get upgrade
  2. 如果覺得一直 sudo 很麻煩,可以利用sudo su取得 root 權限

# 安裝 tasksel

  1. 是 ubuntu 底下的 lamp 懶人包
  2. 推薦教學文
  3. 開始安裝 taskel sudo apt-get install tasksel
  4. 安裝 lamp-server sudo tasksel install lamp-server

# 安裝 phpmyadmin

  1. sudo apt-get install phpmyadmin
  2. reference

# 安裝 wordpress

wordpress

  1. 超棒教學文 我是跟著文章走,裡面紅色的字記得改成自己的
  • cd /var/www
  • 下載 wordpress 包
    wget http://wordpress.org/latest.tar.gz
  • 解壓縮
    tar -xzvf latest.tar.gz
  • 進到 mysql mode
    mysql -u root -p
  • 剩下有紅字,推薦看連結 XD

七、測試連線

當你安裝完後,事實上可以利用 public domain 來連線看看
直接在 console 裡面找到 instance 的 public domian,連線看看是否成功

  1. cd /var/www
  2. sudo vi test.php 創新 php 檔案,並進到 vim 模式
  3. i 進入編輯模式,記得看下面是否出現-- INSERT --
  4. 打上這一行<?php phpinfo(); ?>
  5. 按下 esc -> 打入:wq -> enter 存檔(記得是看 iterm 下面)
  6. 利用 public domain/test.php 連線測試,看到 php 的資訊就成功了!
  7. public domain/wordpress 也可以看見你的 wordpress 有沒有架成功!

IP 連接 instance

如果你有自己的 ip,想要指到 EC2 的話,按照下面作法。

  1. 到 godday 的 DNS manager
  • 設定你的 IP 的A record
  • 指向 instance 的Elastic IP即可

reference

如果有問題,或者我有寫錯的地方,歡迎留言讓我知道!

Read More


Sublime Text 2 實用技巧

Sublime Text 2

Sublime Text 2 是一套越來越火紅的編輯器,如果你是接觸網頁開發,想必對於這套軟體不陌生,以下分享幾個好用的技巧,都是我自己平常比較常使用的技巧,因為我本身是一個懶得看文件的人,所以就整理這篇與大家分享。

編輯環境

  1. Mac 10.7.5
  2. Sublime Text 2

我是使用 mac,所以快捷鍵就會是 command 的配置。

技巧介紹

一、Set Syntax

有發現你的 Sublime Text 2 右下角有你正在編輯的環境語言嗎?舉凡 JAVA、CSS、HTML5 等
比如說我現在要從 HTML5 切到 CSS 介面,除了由上方的 View->Sytax 切換外,可以利用快捷鍵

切換到 CSS 範例:

shift+command+p
鍵入sscss

Set Syntax

每一個 Color Scheme 都會針對不同的語言去做優化,因此值得學習。

二、HTML5 snippet

貼心的 Sublime Text 2 有內建 HTML5 的 snippet,方法如下:

! + tab
html:5 + tab

都可以達成 HTML5 快速生成已經預定的 snippet.

三、Multiple Selection 同時多個游標

按住command+點選你要的位置

推薦用在處理 Array 等結構重複性高的資料型態。

四、Column Selection 同時直行游標

按住option+按著滑鼠左鍵直行往下拖曳選取

推薦用在處理 html 等修改固定 class 或其他部分。

五、選取引號內字串

通常你都怎樣選取雙引號內的字串呢?"string"利用滑鼠從第一個引號拉到後面那個。

輸入:command + d 即可完成

六、分割畫面

有時候我時常左邊放 HTML 檔,右邊放 SCSS 檔,一邊看一邊編輯,這時候就要分割畫面。

輸入:command+option+數量

就可以把視窗分割成你要的數量。

七、貼上符合縮排

有時候從網站上複製一段 code,常常貼上的部份本身就有縮排,貼完卻只有第一行有縮排,其他跑到前面。

複製完後,輸入:shift+command+v

也就是在原本的貼上加上 shift 就可以解決!

reference

  1. up chen in 2013 JSDC
  2. Paste and Indent

Read More


2013 JSDC所見聞

JSDC

這是第二屆的 JSDC,由 TonyQ 及三大社群舉辦的聚會,非常幸運的我能順利的利用社群票的機制搶到票,在眾多非常有經驗的強者面前,聽完他們的分享,越感覺自己對於網頁開發充滿了熱情!

『我們不叫前端工程師,以後請叫我們前端設計師!』

這是聽完今年的 JSDC 感覺最有趣的一句話了。這句話帶出了他們的幽默風趣,在 Web Develope 這一個圈子,能結合設計還有程式的,就是前端工程師,也就是所謂的 F2E。

雖然對於網頁開發我自己算是沒有什麼經驗,要談設計我也沒有什麼基礎知識,但我知道這條路是我喜歡的,我會繼續走下去。給自己的目標是十年,我希望十年後我能跟現在我敬佩的前輩們一起討論,一起努力。

議程記錄&見聞

Day1

  • 【AWS】介紹了他們的服務現在已經針對 node.js 有放出 SDK - 有用過的人歡迎一起分享討論。

  • 保哥】介紹了他的開發經驗,針對 code 要怎樣去調教,他說了一句話讓我印象深刻:

通常你的網站會慢,第一個要查的就是你自己所寫的 code,不是你套用的那些 framework。

保哥也推薦利用jsperf.com去測試自己的 code,雖然執行上面的效果一模一樣,有可能只差一個new效率就差了 200 倍之多。

  • Lawrence】同樣待過資種的 Lawrence 學長介紹了愛料理的開發,主要是一些用過的套件還有經驗談,舉凡 Bootstrap、Font Awesome、template 利用 Handlebars 去完成等。利用 Crazyegg、GA 去分析,也談到了 RWD 的問題。最後三句話,始終在腦海中揮之不去。

在技術上有卓越表現,是工程師的浪漫。
在設計上有卓越表現,是設計師的浪漫。
在產品上有卓越表現,是企劃與 PM 的浪漫。

  • Even Wu】Even 介紹到了如何教前端技術,投影片的風格太有趣 XD 聊到了教設計師寫 code 其實要讓他們自動自發的學習,也是一個最重要的觀念:激勵。這個議程談的不是技術,而是一種經驗和體悟。
    最後剩下的時間,大家交流的問題也很有趣。就有某設計師提到:他原本是設計師,但他每次提出的效果,前端工程師都跟他說這做不出來,他為了證明做的到,便開始自動自發的學習寫 code,來向前端工程師證明。

  • up chen】介紹了很多實用的工具,但可能我自己本身就是一個很偷懶的人,(偷懶的人都會想要去找一些好用的工具讓自己做事更快)所以覺得這一個議程沒有得到很多新知。
    但像是 sublime text 的 column selection 便是之前沒有在使用,但是覺得對自己很方便的技巧:)

  • 【Rex Huang】這個主題很酷,在瀏覽器上面建立一個 os,而且執行起來很順,重點是利用 js 去打造的,同時也是大會的贊助商,未來應該很有發展,但是對於現階段的我來說比較艱深,所以就沒有很仔細的記錄筆記。

  • 【KKBOX】kkbox 來談談利用 node.js 開發的心得。介紹了一些 node 的用法還有經驗,但這種議程最難過的就是自己對於 node.js 接觸還沒有很深,所以聽起來會有點懵懵懂懂,印象最深刻的就是這句話:

利用 JavaScript 去寫後台聽起來很酷,但做起來會想要哭。

  • 【Ruben Tan】是一個聲音很好聽的英文場議程。講者提到了 flow control 問題,要避免寫太多 call back 才是,single thread 一直是 node.js 開發的問題。

Day 2

picture from Josephj

  • Josephj(啊嗚)】早在 JSDC 開始前,就在網路上得知該前輩維護的資源,對於網頁開發,他待過 yahoo!,從 IE4 開始就寫前台了!投影片上面也列出好幾位很值得學習的前輩,真是看的我心花怒放 XD
    前端魂,是身為前端工程師(設計師?)應該有的精神!

前端,你的名字叫熱血

前輩也提到,技術不是重點,態度才是,避免盲目的追求技術,好的前端不等於知道很多技術。積極分享、多回饋,這點我真的在 Josephj 身上看到,從他架設的 f2eclass 就可以知道他願意分享的態度。

  • york】這是一場非常酷的 live demo,台上講者的投影片,透過連線到 jsdc.york.tw 可以即時的在自己電腦切換,且下方就 demo 了 socket.io 的實作。雖然自己還沒有接觸到這一塊,但知道 socket.io 是利用 websocket 去做,在沒有實作 websocket 的瀏覽器,會切換別的通道,讓他們也可以 work,所以支援的瀏覽器很廣。

  • 大澤木小鐵】小鐵與我們分享了在 JavaScript 上面實作 MVC、MVP、MVVM(簡稱 MV*)的心法,這場我聽的津津有味!利用圖解的方式搭配很多時事梗 XD 重點是讓沒什麼經驗的我也可以略懂,相信未來在開發 JS 的時候,會再想到這一份投影片。

  • othree】othree 和我們分享了 this 的用法。在不同情況呼叫 this 的時候,他針對的 function 以及目標會不同。然而我覺得這對我來說也還是太深了 XD 開發的經驗還是不太足夠,但這一個主題應該是很多有經驗的前輩們都想要討論的問題,期待自己能好好的加強自己,等到我也遇到 this 的問題,我相信我又更加進步了。

  • 阿修(Justin Lee)】阿修介紹到了在與設計溝通的時候,其實為了記錄當下討論的結果,利用 inline 把效果寫進 html 最快,等到往後要維護的時候,工程師在自己去 maintain,著實讓我上了一課。阿修也示範了一套很酷的 Titanium,可以即時的在 web 編輯 app 程式,然後利用手機即時連線,為了加速效果,也利用了Tishaow去 built,存檔的同時就可以即時看到,效率非常高。

  • 【Mozilla】介紹了利用 Emscripten 去把不同的語言編譯成 JavaScript,可以在瀏覽器上面運行這樣。

  • Nowill】Nowill 介紹了 RWD 的一些想法,她是一個非常有經驗的前輩,參與很多專案,與很多公司有很多合作經驗。她說了,RWD 不是口頭上說說就是用 media query 就可以解決的,還有很多技巧是要自己真的去做過才會知道的。提到了五大秘訣:掌握解析度、流動式布局、使用者經驗、體驗操作、推敲思考。且她也提到,不是每一個網站都適合作 RWD,因為在需要圖片解析度,文字閱讀的網站,應該針對不同的 case 去做因應。

心得

參與了本次的 JSDC,我體會到了那些專注於在一件事情上面的美好,那種熱忱,都是在這些前輩們上面可以看到。前端工程師要做的事情,會一點設計,會知道要怎樣寫程式,其實就是將這世界上美的事物,利用 web 的方式讓大家看的見。
我相信分享是這世界上最美好的事情,聽到許多前輩的分享,我自己會有一種想要讓自己變很強的衝動,但有時候會忘記那些最單純的人際關係。在周遭一些朋友身上,我看見了他們很強,很厲害,讓我打從心底佩服;但不知道為什麼,對於他們我會有一種不太想向他們請教的感覺,或許是他們平時透露出來的訊息吧,讓我感覺不是那麼好。
我認為交流是一件很愉悅的事情,可以讓彼此在短時間就可以學習到很多,因此我很樂意分享,把自己知道不多但覺得實用的東西給記錄下來,期許大家可以開心的交流,開心的分享,而並非是那種『我知道這個很好用,但你沒有問,你沒有一起來討論,所以我不想告訴你。』提醒自己,要當一個樂意分享的人,在前端開發這一塊,我又重新充滿電了!

在變強之前,我想要先變好:)

reference

Read More


建立自己的GitHub Project Pages

在 GitHub 使用上,我算是初新者,有疑問才有進步。每當看到有人把好玩的東西放到 GitHub 上面變成靜態頁面,都會很想知道他是怎樣辦到的。結果不難,就是利用 GitHub Pages 來做到。

GitHub Pages 介紹

當你要有一個自己的 GitHub Pages,其實分成兩項。

  1. repo 的名字為yourname.github.com,則會產生的 page 路徑為 yourname.github.io本部落格是建立在 github 上面,就是利用這一個 GitHub Pages 模式去建立,可以參考hexo 架 blog 初體驗建立自己 blog 的 subdomain這兩篇文章。
  2. repo 的名字為repo-name,則會產生的 page 路徑為 yourname.github.io/repo-name,這邊的 repo name 就不像第一種模式,沒有固定名稱。

GitHub Pages 建立

在每一個新的 repo 下面,正常的情況都是在 mater 這一個 branch。分享一下我自己的作法。

  1. GitHub 頁面上建立一個新的 repo,這邊我取名為 first-repo。

create new repo

  1. 從本機端 clone 下來,參照自己的路徑,我的 repo 則是在 command line 底下打
git clone git@github.com:kpman/first-repo.git
  1. 移到該資料夾
cd first-repo
  1. 將 branch 移到gh-pages,這步驟很重要,Github Pages 就是看這一個 branch 去決定頁面的。
git branch gh-pages
git checkout gh-pages

或者

git checkout --orphan gh-pages //建立一個沒有parent的branch,並移到該branch上
  1. 將編輯好的檔案 push 上去。
git add .
git commit
git push origin gh-pages
  1. 完成!

路徑差異

http://github.com/kpman/liteAccordion這樣代表連回 GitHub 的 code 頁面
http://kpman.github.io/liteAccordion因為新增到gh-pages這一個 branch,所以可以看到靜態的 html 展示頁面。

其他範例
two.js
textillate

reference

  1. 官方文件
  2. Getting started with GitHub Pages
  3. Create a new branch with git and manage branches

Read More


在hexo自訂rss

趁著好朋友在旁邊的情況下,請教他學會了在 hexo 自訂 rss,再次感受到技術這種東西,真的是當面交流才會發揮他的效益。

一、本機環境

在終端機下輸入以下

npm install hexo-generator-feed --save

權限沒有取得的記得前面加上sudo

二、修改_config.yml

在主目錄底下的_config.yml檔加上

plugins:
  - hexo-generator-feed

modify _config.yml

如此一來便完成了環境設置。

三、要怎樣找到?

在 hexo generate 之後,會發現 public 資料夾底下多了 atom.xml
feedly這類的閱讀收集器
只要輸入domain/atom就可以找到
例如要訂閱我的 blog 只要輸入code.kpman.cc/atom就可以搜尋到囉!

rss

突然發現我的 blog 只有一個人訂閱,那個人就是我自己...

reference

  1. hexo plugins
  2. RSS/Atom、Sitemap for SEO

感謝強者阿志耐心面授機宜:)

Read More


利用pseudo element 讓html更簡潔

趁著記憶猶新的狀況,記錄自己使用偽元素 (pseudo-element)讓原本的 html 架構更加簡潔的方法。

何謂 pseudo-element?

不會出現在 html 的文件裡面,而是利用 css 讓瀏覽器去實作。以下列出的都是可以使用的 pseudo-element。

  1. ::first-line:若是 p 元素,則可以用來操作第一行。
  2. ::first-letter:用來操作第一個字。
  3. ::before:可以在所選元素之前插入樣式/內容。
  4. ::after:同before但是是在之後插入。
  5. ::selection:用來自定反白後的效果。

為了區分偽元素和偽類,CSS3 的 guildline 將偽元素的寫法修正,以往只要加一個冒號「:」,現在則是加兩個冒號「::」,部分可支援的瀏覽器包含 webkit, firefox, opera。) -- by MUKI

修改過程

原本的 html code

<div class="icon">
  <img src="img/icon01.png" alt="正確觀念" />
  <span>正確觀念</span>
</div>
.icon {
  display: inline-block;
  position: relative;
  padding: 1em;
  padding-top: 5px;
  span {
    position: absolute;
    bottom: -7px;
    left: 12px;
  }
}

原本利用 div > span 的效果去把中文字顯現出來,所以在 html 的架構下,需要多一個span的 tag。
且利用img去把圖片給 show 出來。

更改過後的 html code

<!-- html只剩一行!-->
<div data-text="正確觀念" class="concept_label icon"></div>
.icon {
  display: inline-block;
  position: relative;
  padding: 1em;
  padding-top: 5px;

  &:before {
    position: absolute;
    bottom: -7px;
    left: 12px;
    content: attr(data-text); // 關鍵作法
  }
}
.concept_label {
  background-image: url(../img/icon01_c.png);
}

###觀念整理

  1. 將中文字放入自訂的data-*屬性
  2. CSS 中利用::before取代原本的span
  3. 利用content: attr(data-text);將 date-text 文字取出來
  4. 將背景圖片利用另外一個 class 取代,以後維護性提高。

reference

那些 CSS 偽元素可以幫你做的 10 個效果

Read More


客製化hexo light theme

分享一些目前知道的客製化 theme 技巧。
其實非常簡單,但是這些技巧如果沒有問過或是自己看過文件,
一時間也無法摸透,所以覺得應該值得把它記錄下來。

一、增加 Disqus 留言板

  • Disqus申請帳號
  • 右上角的 Dashboard +Add 申請一個新的帳號
  • 將網址、name、shortname 打好後,要把 shortname 記住! _ 修改本機目錄下的 _confid.yml _ 在 disqus_shortname 貼上自己的 shortname

完成~

※ 後記:
若你在申請完 disqus 貼到自己的 blog 上面,發現出現很多不必要的連結,請到 disqus 的 dashboard 去修改。
右上方 Setting -> Discovery -> 將 Discovery level 拉到最右邊,如下圖
Discovery level

二、增加右方側欄 widget

在標題就已經破梗囉 XD
右方出現那些『Tag』『Tag Cloud』等欄位,其實就是修改 widget 過後得到的結果。
方法如下:

  • 找到 theme/light/layout/_widget ,可以發現很多 ejs
  • 有些是已經自訂好了,例如 category recent_posts
  • theme/light/_config.yml 內,修改widgets,在底下新增你要的 ejs 檔名即可~

同樣方法可以自己寫 ejs 檔,一樣在theme/light/_config.yml加上檔名就可以自訂。

三、增加上方自訂全域導覽列

預設為 Home、Archives 這兩個
當然得要自訂才爽 XD

方法一樣很簡單如下:

  • 找到theme/light/_config.yml裡面的第一行
  • 預設狀況應該只有 Home&Archives
    menu
  • 自訂自己的 menu,項目後面即為連結,可以直接使用http://google.com之類的連結。

四、自訂 favicon

  • 首先你要有一個 favicon.ico 檔或是.png 檔(這邊預設檔名為favicon.png)
  • 將檔案放在 主目錄/source 底下
    favicon path
  • 找到 theme/light/layout/_partial/head.ejs做修改
  • 在裡面加上 head.ejs 檔內確認有以下這行 code 即可擁有自己的 favicon
&lt;link href=&quot;&lt;%- config.root %&gt;favicon.png&quot; rel=&quot;icon&quot;&gt;

以上介紹一些目前知道的自訂方法,
希望還有人可以跟我多介紹一些技巧,一起分享吧!

Read More


建立自己blog的subdomain

有鑑於 kpman.github.io 這一個網址實在太難記了,所以就決定將自己有的 domain 利用 subdomain 的方式指過來。
以下的介紹是我的個案,我利用 subdomain 而非 TLD 去做。

一、擁有自己的 domain

雖然像是廢話 XD 但這邊推薦去GoDaddy買 domain.
方便又快,在更新 domain 的時間非常快速,理論上一個小時內就可以指到你要的 ip 位置。

二、到 GoDaddy 設定 subdomain

  1. 進到 DNS manager

  2. 新增一筆 CNAME

    CNAME

  3. GoDaddy 部分完成

三、在本機端設置 CNAME 檔

這邊要在 主目錄-source 底下放一個檔名為 CNAME 的檔案
裡面為你要指向的路徑,這邊我是指向 http://code.kpman.cc

path

至於如何設置 CNAME 檔案,我是利用在 github repo 上面的 create new file

create new file

設置完之後 clone 下來,然後複製進去主目錄-source 底下 XD
(有人知道怎樣做比較好嗎?)

理論上這樣子就完成囉!
如果有漏掉的麻煩留言給我一起討論:)

reference

官方文件

Read More


hexo架blog初體驗

是這樣的,一直想要找一個空間,可以清爽的放 code,可以跟其他人交流
有鑑於系上好朋友們紛紛都建立起自己的部落格,因此我也加入了這個行列。

報著取之於人,回饋之於人的心情,我想要把自己從無到有架設這一個 blog 的過程記錄下來
謝謝那些願意指導我的朋友們!

一、本機環境設定

  1. 首先你要有 node.js,這是一套快速、簡單且功能強大的 Node.js 網誌框架。

  2. 接著你要用 npm install -g hexo 來安裝

理論上這樣就完成了,我個人在裝的時候 npm 不給裝,發現前面加上 sudo 就可以解決。

二、github 帳號設定

  1. 你要申請一個自己的 github 帳號

  2. 在主頁右上方創建一個新的 repo

    create new repo

  3. Repository name 填入 github帳號.github.com 用來創建 github page

三、開始使用 hexo

建立 hexo

hexo init

建立一篇文章,將會是Markdown形式,可以到 source/_post/title.md 去修改

hexo new "title"

生成 public 檔

hexo generate

開啟 server 觀看(預設在 localhost:4000)

hexo server

四、發佈到 github 上面

修改 _config.yml 檔案,最下面加上

deploy:
  type: github
  repository: git@github.com:github 帳號/github 帳號.github.com.git
  branch: master

我那時候用 git 的時候,沒有 ssh 認證,如果遇到相同問題可以看這篇
照著上面的指令走就可以完成,唯獨 mac 裡面似乎內建 id_rsa 這把 key,這邊我不清楚 XD

接著只要輸入

hexo deploy

等待 10 分鐘,你的網誌理論上就架好了!網址為 http://github帳號.github.io

reference

  1. 官方文件非常詳盡,重點是中文

  2. 強者小熊教學文

Read More