2017年11月27日 星期一

台北

搭著公車 坐到台北車站

在這非假日的星期一

涼爽的天氣  到哪裡都舒服

想到要吃牛肉麵

網路上 google找到的良品牛肉麵

好好吃

再買杯咖啡 跟著他去找同學探班

散步在重慶南路上

逛逛書店 說說話

再回到台北 過去的灰色 已經不再

該回去了

2017年10月2日 星期一

iscroll click設定對各html tag的動作影響

iscroll option的clcik設定值,預設為false
1.當click=true
  div與a ,click都會被觸發
  input radio或input checkbox則會變的無作用
  button則會被觸發兩次
2.當click=false
  div與a click皆不會被觸發
  input radio或input checkbox可以有作用
  button會正常被觸發一次

若要讓iscroll內既要讓div可以click
又要讓radio 可以使用
則需要將click設定為false
並必須設定
preventDefaultException= {tagName:/.*/}
但不建議使用
因為在一些硬體沒那麼好的mobile device上
效能變很差

2017年10月1日 星期日

修正nginx upstream後無法取得真正client ip

把nodejs服務放在nginx後面用upstream來跑
發現要取得client real ip常常都是取到127.0.0.1
修正方式
1.nginx設定
  在locate 區塊加上
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
2.ndojs用
  使用 req.headers['x-forwarded-for'] 取得client ip
 

2017年9月4日 星期一

Object.defineProperty 覆寫已存在屬性

利用 Object.defineProperty可以為指定的
物件加上自訂的屬性資料
但在較舊的瀏覽器上若對已經存在的屬性去做重新設定則會發生錯誤
例 要在fn物件加上一個name的屬性
Object.defineProperty(fn, "name", { value: 'testName'})
這在android 4.x版預設的瀏覽器上會發生錯誤
如果改用其他property name 則可正常運作

Object.defineProperty(fn, "fnName", { value: 'testName'})

2017年8月29日 星期二

使用pkg 建立nodejs執行檔

利用pkg可以把nodejs專案封裝成跨平台的執行檔案
就可以發布單一檔案給個測試平台
不過有幾點需要注意
1.es6轉換
  新版本的nodejs,可以直接運行es6
  不過要使用pkg須先透過babel轉成es5
2.透過pkg設定將其他的檔案封裝到執行檔內
  通常透過程式裡的require,會將相依的檔案封裝到
  應用程式內
  但若有其他如圖檔,css或其他js script要強制封裝進去
  可以透過在package.json內設定pkg來載入
  若設定了則需要透過pkg . 或 pkg package.json指令
  讓pkg知道要另外封裝這些檔案
pkg網址
package.json檔案範例
{
  "name": "simplesocket",
  "version": "1.0.0",
  "description": "build simple socket server excute file for web",
  "main": "lib/index.js",
  "bin": "lib/index.js",
  "scripts": {
    "start": "babel -s inline -d lib -w src | nodemon lib/index.js",
    "build": "pkg package.json --target node8-macos-x64,node8-win-x86 --out-dir output",
    "prepublish": "node_modules/babel-cli/bin/babel.js src --out-dir lib"
  },
  "babel": {
    "presets": [
      "es2015"
    ]
  },
  "pkg": {
    "scripts":"node_modules/extra../**/*.js"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/ozzysun/simpleSocket.git"
  },
  "keywords": [
    "socketio",
    "express",
    "pkg"
  ],
  "author": "ozzysun",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/ozzysun/simpleSocket/issues"
  },
  "homepage": "https://github.com/ozzysun/simpleSocket#readme",
  "dependencies": {
    ...
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-core": "^6.26.0",
    "babel-preset-es2015": "^6.24.1",
    "nodemon": "^1.11.0"
  }
  
}

2017年8月14日 星期一

single file component 使用 vuelidate

validate提供了vue很方便的資料驗證功能
以下是使用在Single File Component上程序

一.在框架上載入vuelidate
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)

二.元件上使用
1.以mixin方式使用mixin加入
import { validationMixin } from 'vuelidate'
mixins:[validationMixin],
此時就可以在元件內加入validations來設定相關要驗證的規則
validations可以是物件
validations:{

}
也可以是method
validations(){
  return {}
}

這時候在元件內就可以得到this.$v來操作相關
2.import所要用到的validator,
import { required, minLength,maxLength,numeric } from 'vuelidate/lib/validators'

3.透過$v.model名稱取得該model的即時狀態
  依照取得的model狀態,來決定畫面要做如何的處置

三.Sample
<template>
  <div>
    <input type="password" v-model.trim="pwd">
    <button v-if="isActiveForSend" @click="clickSendBtn">送出</button>
    <span>這欄位所有狀態{{$v.pwd}}</span>
  </div>
</template>
<script>  
import { validationMixin } from 'vuelidate'
import { required, minLength,maxLength,numeric } from 'vuelidate/lib/validators'
export default {
  mixins:[validationMixin],
  validations(){
    return {
      pwd:{
        required,
        numeric,
        minLength: minLength(6),
        maxLength:maxLength(6)
      }
    }
  },
  computed:{
    isActiveForSend(){
      return this.$v.pwd.required&&this.$v.pwd.numeric&&this.$v.pwd.minLength&&this.$v.pwd.maxLength
    }
  },
}
</script>

2017年8月2日 星期三

webpack Uncaught SyntaxError: Unexpected token :錯誤

發現這問題找了很久
逐一檢查外掛module
發現是
chunk-manifest-webpack-plugin到1.1.2版造成

webpack Uncaught SyntaxError: Unexpected token :錯誤
回復到1.1.0版即可暫時

scss在使用 import media query

scss在使用@import
可以直接使用
@import "./style01.css" 載入其他css檔案
但若需要做meida query
@import "./style01.css" screen and (min-width: 640px)
這樣是會掛掉的
需改成

@import url("./style01.css") screen and (min-width: 640px)

所以可以統一@import時都使用url()都不會出錯

2017年7月15日 星期六

還是不要聽到的好&麥田捕手

看到去年在fb上貼了這首歌

記得以前在StreetVoice寫串流音樂播放器
那時候還是使用Flash還有 FMS做Streaming
現在 當然這技術已經差不多消失

寫播放器 很自然的每天要聽很多的站上音樂
聽到這首歌 很感動
感動 他歌詞裡 那份誠摯的情感
寫到內心深處

那時候 大家都在找 唱歌的是誰 現在在哪裡
後來
"那我懂你意思了" 修澤
紅了 也解散了
這首歌 似乎始終沒有出現在他們的唱片裡
也許這只是首Demo
就如同白安的麥田捕手
Demo版本的情感 也最純真

2017年7月11日 星期二

NightWatch使用PageObject注意事項

一.browser物件
在使用nightwatch時,所定義的測試function可以取到一個browser物件
這個browser物件可以操作官網上的api
另外在這browser物件下包含了
  1.globals:對應到nighwatch 設定檔內的globals區塊
  2.page:page下的各個function對應可以取得同檔名的PageObject

module.exports = {
  '登入測試':function(browser){
    const devServer = browser.globals.getServer()
     browser
      .url(`${devServer}/#login`)
      .waitForElementVisible('#loginPage', 5000)
      .clearValue('.js_siteField')
      .clearValue('.js_pwdField')
      .pause(100)
      .setValue('.js_pwdField','12345')
      .setValue('.js_userField', '')
      .pause(100)
      .click('.js_loginBtn')
      .pause(3000)
      .assert.urlEquals(`${devServer}/#home`)
      .end()
  }
}
二.為何要使用PageObject
在對於測試頁面上的dom物件,nightwatch可以使用selector來找到
要操作的dom,使用PageObject可以預先把要操作元素定義好
之後就可以用@id的形式去取得該dom參考

利用定義Page讓設計的流程更清楚
也避免直接綁在特定selector上,之後要修改比較麻煩

module.exports = {
  url: function(){
    let devServer = this.api.globals.getServer()
    return `${devServer}/login`
  },
  elements: {
    myel:'#loginPage',
    siteField:'.js_siteField',
    pwdField:'.js_pwdField',
    loginBtn:'.loginBtn'
  }
};
三.PageObject
要注意的是PageObject內取得的this並不是等同於
寫spec時的browser物件,
this.api才真正是對應到browser 下的api method

四.使用PageObject改寫Spec
1.取得PageObject
  例如
  browser.page.login()
2.PageObject下的API與browser的api method區別
  如果是需要操作的element的,都必須透過PageObject操作
  這樣 PageObject下定義的@id才會有作用
  例 loginPage.setValue('@pwdField','12345')
  若不是屬於element操作的,則回歸到browser上去操作
  因為這部分的function,pageObject沒有
  或透過 loginPage.api.pause(10000) 也可
  例 browser.pause(10000)

module.exports = {
  '登入測試':function(browser){
    const devServer = browser.globals.getServer()
    let loginPage = browser.page.login()
    loginPage
      .waitForElementVisible('@el', 5000)
      .clearValue('@siteField')
      .clearValue('@pwdField')
    browser.pause(100)

    loginPage
      .setValue('@pwdField','12345')
      .setValue('@siteField', '0910000311')
    browser.pause(100)

    loginPage.click('@loginBtn')
    browser.pause(3000)
      .assert.urlEquals(`${devServer}/#store/member/pointBanner`)
      .end()
    
  }
}

2017年7月7日 星期五

RESTful API 命名規則

常用到RESTful API
關於URI的命名規則
這篇裡所描述的
小小的memo一下

1.URL尾巴不要加 /
2./是用來描述resource的關係
3.利用-來增加URL的易讀性
4.URI不要使用_
5.URI上全部採用小寫lowercase
6.URI上不要使用extrtenion .xxx
7.使用複數來描述資源
 例
  http://api.college.com/students/3248234/courses

資料來源

2017年6月24日 星期六

webpack alias for css import

webpack可以透過設定 path alias 讓在js內使用
import 或require更好管理
如果是在css或scss內使用
@import 會需要做一點修正

例 在webpack.config上設定了alias
'scss':path.resolve(__dirname, 'src/scss')
在css 的import
@import "~scss/xx"

2017年6月13日 星期二

Love Song

大二升大三那年的暑假
在臺北打工沒有回南部

7月的台北悶熱的天氣
白天在樹林的橡皮擦工廠工作
晚上去板橋當家教
晚上休息
回到租屋的地方
那個時候 沒有冷氣
風扇 其實 也沒什麼作用

窩在小小的木板床上
把學長給的一個涼墊拿去冰箱冰
到睡覺時拿來鋪在床上
睡到半夜還會熱醒

常常 只穿了件短褲 躺在床上
望著天花板
等自己可以夠涼快到可以睡著

書桌上 泛黃的檯燈旁
反覆的放著Testla那張
Live Man Acoustical Jam專輯
跟著彈著 唱著
彷彿自己參與了那樣的現場

一直到8月存夠了錢
把那把Ibanez電吉他買下來
才心滿意足的回南部 放暑假



避免WebSocket因連線中斷資訊不同步

在網頁上使用WebSocket
可以很方便的做即時的訊息廣播
但如果使用者的browser沒有開啟或是
當手機關閉螢幕時都會讓WebSocket停止連線

以socket.io為例
socket在被重新打開時,socket client會依序觸發disconnect事件
與connect事件,在這個時間點就可以做資料的更新
讓本機端與server的資料狀態

為避免使用者沒有接收到Socket廣播資料,而漏掉資訊
可以做以下兩的部分的處理
1.當使用socket發出廣播訊息時,同時
  儲存紀錄於db
2.當接收到connect 事件時
  呼叫api取回更新的資訊
這樣就可以保證使用者
永遠可看到最正確的資訊

2017年6月5日 星期一

泡咖啡

回到高雄上班的每一天
戴上耳機 一杯的咖啡
保留著以往 住在台北時的工作習慣
泡咖啡的時間 也是讓自己的視線短暫的脫離電腦
站在茶水間 往外看到的是一樣風景
如果有一天 離開這裡
應該會記得的
就是 這望出去的景象


讓vue 元件script預設以es2015解析

vue元件 預設並不會script 語言並非是es6
在webpack loader設定 以vue-loader載入vue元件
只是讓他可以載入.vue的檔案
但vue元件內的script並非預設就可以解析es6
即使你已經安裝了babel 可以對 es6的.js檔案做正確的解析
但對於.vue內的es6還是可能不會自動辨識解析

可以透過設定.babelrc 讓webpack以es2015解讀vue內的es6檔案

來讀取script部分
這部分可以透過設定.babel.rc來處理
.babelrc
{
  "presets": ["es2015", "stage-2"],
  "plugins": ["transform-runtime"],
  "comments": false
}

2017年5月30日 星期二

es6 class繼承 範例

es6已經可以使用class語法直接來寫class
以下範例包含 相關的繼承與instance 與static
method prop 使用

範例
let staticSchema_1 = null
class ChildClass extends ParentClass{
  constructor(){
    super() #繼承並改寫建構子
    ....
    this.myprop1 = xxx #instance prop宣告
    ..
  }
  myMethod(){ # instance method
    super.myMethod() #繼承並改寫method
    .....
  }

  static myClassMethod(){ #宣告static method

  }
  static get Schema(){ #宣告static prop
    return staticSchema_1
  }
  static set Schema(_val){ #宣告static prop
    staticSchema_1 = _val
  }
}

2017年5月26日 星期五

UglifyJS-plugin Unexpected token: punc (()錯誤 目前解法

webpack 建立bundle檔案時,UgilyfyJs Plugin可以把檔案縮到很小
是相當重要的功能,但在使用es6時 很可能會發生
Unexpected token: punc (() 的錯誤
目前尚無解法
不過可以先自行使用其他模組替代
1.packages.json安裝
"uglify-js": "git://github.com/mishoo/UglifyJS2#harmony-v2.8.22",
"uglifyjs-webpack-plugin": "0.4.3",

2.webpack.config.js
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
原本加入的plugin
new webpack.optimize.UglifyJsPlugin()
改用 取代
new UglifyJSPlugin(),

參考連結

2017年5月25日 星期四

Vue v-model bind 物件或array 範例

v-model可以綁定整個結構的物件變數
並可watch到物件下任何property的異動
樣板
<tr v-for="(val,key) in tableMap">
  <td>{{key}}</td>
  <td><input type="text" :value="val" v-model="tableMap[key]" /></td>
</tr>

js
watch:{
  tableMap:{
    deep:true,
    handler:function(val,oldVal){
      console.log(this.tableMap)
      Tool.setLocal('tableMap',this.tableMap)
    }
  }
}

Vue 多層次變數監聽範例

情境
1.資料的來源為tableStr(輸入字串)
2.由tableStr,計算產生tables(桌次array)
3.由tables 計算產生tableMap(桌次對照表)

1.data 內只建立需要用的變數,不設定預設起始值
data{
  tableStr:"", //桌號字串 
  tableMap:{}, //桌次對照表
}

2.在mounted後才給預設值,才會讓相關的變數都被計算
mounted(){
  this.tableStr = "1-5" 
}

3.tables是計算產生的,在tableStr有值 就會被產生
computed{
  tables:function(){
    let tables = this.tableStr.split(",")
    return tables
  }
}

4.watch是需要有異動才會被觸發的
//所以在mounted內去設定tablStr值,造成異動,才會造成tables被重新計算
//這個watch才會被觸發,才會在這裡去改變要產生的tableMap值
watch:{
  tables:function(val){
    tableMap = {}
    for(let i=0;i<this.tables.length;i++){
      tableMap[`table_${this.tables[i]}`] = i+1
    }
    this.tableMap = tableMap
  }
}

測試結果
computed的值是可以被watch的,但watch一定要是有異動才會被觸發
所以要設定初始值,並要讓連動的相關變數都被計算
初始值要放在mounted內去設定,不要在data內去直接指定

2017年4月24日 星期一

nodejs 7.6 將原有method改用async

node 7.6後完全支援async的使用
原本利用callback取得非同步資料的寫法
在使用async時 可用以下方式改寫
ㄧ.原本要被執行的各個執行method 都需改成回傳Promise
1.原method 利用callback取得結果
var getData = (callback)=>{
  queryDB((err,result)=>{
    resultObj = {
      err:err
      data:result
    }
    callback(resultObj)
  })
}

2.method 改成return Promise
透過resolve與reject取得結果
let getData = ()=>{
  return new Promise((resolve,reject)=>{
    queryDB((err,result)=>{
      resultObj = {
        err:err
        data:result
      }
      if(err){
        //完成
        resolve(resultObj)
      }else{
        //錯誤
        reject(resultObj)
      }
    })
  })
}

二.使用async
1.原使用單位,直接呼叫,由callback取得結果
getData((resultObj)=>{
  console.log(resultObj)
})

2.改用async
需先定義一個async function,在aync function內去執行
透過try catch去取得正確與錯誤的資訊
const run = async ()=>{
  try{
    //取得結果,即是取得resolve傳進來的內容
    let resultObj = await lodaMethod()
  }catch (err) {
    //錯誤攔截 ,即是取得呼叫reject傳進來的內容
    console.log(err)
  }
}
run()


2017年4月16日 星期日

webpack使用html-webpack-plugin動態建立html

透過樣板動態產生html頁面內容
可以使用變數帶入要載入的js檔案,css檔案

plugin預設使用ejs樣板系統 不需設定loader即可直接使用
但樣板的副檔名 一定要是.ejs,樣板內可以使用lodash語法操作
一.主要的plugin設定

filename: '../index.html', #路徑是相對於webpack設定的 output.path
template: './src/index.ejs', #指定使用樣板,路徑是相對於gulp執行的位置
title: 'my title' #插入到html title
inject:是否插入 js與css檔案到html上

二.樣板內取得的資料來源
在樣板內可以直接取得以下三個物件資料
1.htmlWebpackPlugin
{
  "files": {
    "css": [ "main.min.css" ],
    "js": [ "bundle/main.min.js", "bundle/common.js"],
    "chunks": {
      "main.min": {
        "entry": "bundle/main.min.js",
        "css": [ "main.min.css" ]
      },
      "common": {
        "entry": "bundle/common.js",
        "css": []
      },
    }
  }
}
2.webpack 直接取到webpack state物件資料
3.webpackConfig 直接參考到目前使用的webpackConfig物件

三.在樣板內使用資料
1.若要自行控制載入js,css,需設定inject:false
2.取得檔案的路徑可以透過如
  htmlWebpackPlugin.files.chunks.xxx.entry拿到js路徑

<script src="<%=htmlWebpackPlugin.files.chunks['common'].entry%>"></script>

2017年4月14日 星期五

webpack css 圖檔路徑設定

透過require 載入css file
css內若包含了圖檔連結或是連結字型檔案
webpack在做bundle時,通常會把連結的圖檔抽離出來放到
設定的bundle目錄下
而css內的圖檔路徑連結基本上預設是與css同一目錄

但使用style-loader 會將css 內容塞到index.html內
而造成index網頁內的css會在同一層級找圖檔
找不到的問題

需要讓webpack bundle時可以將css內圖檔路徑指到
我們指定的路徑下
即可解決這個問題

webpack config loader設定

{
    test: /\.css$/,
    loader:"style-loader!css-loader" #針對css檔案載入並插入網頁內
},
{ 
    test: /\.(png)$/, #對於圖檔,使用file載入並指定圖檔讀取的目錄
    loader: 'file-loader?publicPath=bundle/' 
}

2017年4月8日 星期六

webpack 使用require.context做動態require

如果要做動態module require
直接使用 require("/"+name) 是不會理你的
因為在compile當下不知道你的變數值name是什麼
自然會找不到
透過使用require.context,他會針對指定的目錄下檔案
建立一份key mapping

req = require.context("comps", true, /\.coffee$/);
會針對comps下所有子目錄內的 coffee檔案建立一份
key索引
這部分可以透過
console.log req.keys()查看
例如可能內部索引會有如同
{
  0:"./comps/layout/mylayout/index.coffee"
  ....
}
所以就可以做動態require載入
Module = req('./comps/layout/mylayout/index.coffee')

2017年3月15日 星期三

nodejs 升級7.x process.EventEmitter錯誤修復

遇到這樣的錯誤
The super constructor to "inherits" must not be null or undefined
是部分相依lib module需手動修改

util.inherits(Configurator, process.EventEmitter);
改為
util.inherits(Configurator, require('events'));

然後重新執行npm rebuild即可整個project在node 7.x運行

2017年3月10日 星期五

東港

王爺廟前 的中正路 是東港很熱鬧的街道
一早在這 早餐絕不會想吃漢堡三明治
可以是一碗海鮮粥 或是 吃個肉圓
往前走拐到中山路上 再走進市場裡
這是平常買菜的地方

市場裡的邱家雙潤糕 還沒開始營業就排滿了人
再往前走 蔡沙蝦肉圓是今天要吃的早餐
屏東肉圓一樣大小的餡料,卻又是以彰化肉圓一樣做法
好吃的還想再來一盤

一起散步走在市場街道上
是一份 步調緩慢的悠閒

這一天不是來這裡度假
才剛認識這裡 就要離開

以後再回來
就是度假 不是搬家

南下

當南部 還是一片陽光晴朗
台北 仍舊是陰雨綿綿的天氣
連假的高速公路 車子只能以緩換的速度往前走
原想 順路繞到台南
這樣的塞車 到高雄 應該已經晚上了

休息站下來 下著雨的天氣 冷的要穿上兩件外套
回到車上 插上隨身碟
放起了自己常聽的音樂
放著放著 還講起了這一首首歌的故事
只怕妳車開著開著就睡著了

我以為這只是我們開車時的背景音樂
很訝異 你很認真的聽著 這些冷門的獨立音樂

到家已經晚上9點
你說想吃羊肉
在家附近找了地方吃東西 滋味是意外的好吃
以後要回來這 機會也不多了
塞車這麼久 也許是不好的經驗
只是 這一天也永遠是記憶裡 無法忘記的時光

2017年3月6日 星期一

event target與currentTarget的差異

當事件被觸發
handler接收到event物件時
event上的currentTarget
指的是處理這事件的DOM
event.target
則是指發出這event的DOM

2017年2月7日 星期二

解決iphone browser上socket io無法運作問題

使用nodejs socket.io做廣播服務
在desktop browser or android device browser
都可以正常運作
但在iphone ipad上的safari與chrome則會因為安全限制
而有問題
解決方法
1.socket必須使用ssl安全連線
2.網頁上的content-security-policy須針對ws部分做設定

一.利用nginx建立socket ssl 連線
  要讓web socket使暨有的ssl,如果使用ngix可以很簡易的
  用upstream得到一個使用的url
  設定範例如下
map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}
upstream socket_server{
  #ip_hash;
  server 127.0.0.1:33333; 
}
server {
    listen       4430 ssl;
    server_name  xxx.xxx.com;
    location /socket {
        rewrite ^/socket(/.*)$ $1 break;
        proxy_pass http://socket_server;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

二.socket.io Client端使用ssl socket
io.connect("https://xxx.xxx.com",{secure:true,path:"/socket"})
  連線即可

三.Client網頁上csp設定
  網頁上的content-security-policy須針對ws部分做設定
  例:
 <meta http-equiv="Content-Security-Policy" content="...connect-src * 'self' wss: xxx.xxx.dpmain.com;">  

2017年1月9日 星期一

冬日的午後

昨天下午帶著妹妹出去晃晃
帶他去廟裡走走
拜拜文昌君保佑考試順利
然後在廟前的市場
吃個粽子 吃個冰
很喜歡這樣安靜又自在的下午
心裡很自在

今年的冬天 就像夏天一樣的溫暖
星期天的下午 帶著妹妹到處晃晃
到鼓山亭 拜拜
求全家人的平安健康
還有 妹妹祈求考試順利
散步在廟埕
然後在廟前的小吃攤上
吃個肉圓 吃個冰

很喜歡這樣安靜又自在的下午
心裡很自在
到哪裡 妹妹總是開心又好奇的看著這世界

這裡是小時候 我老爸帶我們來
以前我的爺爺 我的祖先 都在這生活的地方
現在 我也帶著妹妹
希望他長大後 記得這個地方



2017年1月3日 星期二

2016的最後一天

2016的最後一天
回到了屏東
因為小弟回來
就坐著他的車 帶著妹妹 還有老媽
去果園過這個年

天氣很好 暖暖的陽光
沒有前幾天的寒冷
其實 一點也不像是冬天
一杯咖啡 享受這安靜的午後

下午騎著摩托車帶妹妹 到7-11吃個冰
再到佳佐國小走走
滿足了 再回到山上
生火 烤肉

晚上的天氣涼了 很多
妹妹玩了一天 很早就睡了
撐到快到12點 遠方的原住民部落
歌聲 越來越大
12點過 遠方四處的煙火燃起
知道這一刻 跨過了 2017年
這個年 是平凡 也幸福的年
也希望新的一年
身邊的人都能
健康 平安