2021年1月7日 星期四

Vue component的v-model

承繼上一篇,我們在撰寫component時,除了要接value,如果data改變,同時也要emit出去,所以程式碼就會如下

export default {
  props: ["value"],
  data: () => ({
    data: ""
  }),
  watch: {
    data() {
      this.$emit("input"this.data);
    },
    value: {
      immediate: true,
      handler() {
        this.data = this.value;
      }
    }
  }
};

乍看之下也沒什麼問題,小編也這樣寫了好幾年了,但小編一直對於宣告一個data:""感到不是很舒服,雖然只是個過水變數,但沒有更乾淨的寫法嗎?還有小編其實一直對於watch很感冒,watch用得好可以帶你上天堂,用不好你就好幾天不用下班,甚至忙到你小孩覺得你只是個很有趣的叔叔!所以能不用就不要用,因此這幾天在賺國產車時想到一個妙招,可以使用computed的set阿!!直接超級簡化,我們直接來看程式碼

export default {
  props: ["value"],
  computed: {
    data: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit("input"val);
      }
    }
  }
};

是不是簡單很多呢,打完收工下班!

Vue的watch

這篇來介紹vue的watch的進階使用immediate,

使用情境:以前在子元件要接父層的值,並要隨時監聽變化覆寫data時,都會在mounted時覆寫第一次,之後就依watcher即時覆寫,我們來比較一下兩個寫法

export default {
  props: ["value"],
  data: () => ({
    data: ""
  }),
  watch: {
    value() {
      this.data = this.value;
    }
  },
  mounted() {
    this.data = this.value;
  }
};

雖然功能正常,但上面這寫法會造成邏輯分散,閱讀不易,我們來試一下immediate

export default {
  props: ["value"],
  data: () => ({
    data: ""
  }),
  watch: {
    value: {
      immediate: true,
      deep: true// 深層監聽
      handler() {
        this.data = this.value;
      }
    }
  }
};

這樣就可以在渲染之初就把父層丟進來的值寫入啦,是不是又更簡潔了呢

Vue的ngInit

AngularJS是小編踏入前端的第一個語言,雖然目前已經幾乎是時代的眼淚了,所以小編後來轉戰了vue,在渲染效能及編寫習慣上各方面小編都比較喜歡,但有個功能是我踏入vue至今難忘的功能就是ngInit,不知道各位有沒有遇過:一個變數只出現在某個tag中,但我又不想宣告再data裡面,且變數常出現,這時ngInit就可以幫助你少寫很多code。

小編以前都用這方法達成類似功能,但相較之下顯得攏長

<div v-for="a in ['ABC']" :key="a">
  {{ a }}
</div>

我也曾經查過directive:"v-init"、"vue ngInit",等相關資料,但始終找不到有相關功能,但就在今天,在幫一個同事解決效能問題時,無意中看到他寫了:set屬性,經同事一番解釋,沒想到這就是我夢寐以求的功能,廢話不多說我們直接來看怎麼使用

<div :set="a = 'ABC'">
  {{ a }}
</div>

就是這麽簡單,變數a無須註冊在data裡面,就可以在set的tag裡面重複使用,這功能在很多情境都非常好用。


2021/01/08補充

今天再查詢找到了一個挺有趣的解法,因為還牽扯到大小寫,所以我認為不夠直觀,對我來說這並不是最佳解法,但也有紀錄的價值

// js
Vue.directive('init', {
  bind: function(elbindingvnode) {
    // convert kebab-case to camelCase
    let arg = binding.arg.split('-').map((argindex=> {
      return (index > 0) ? arg[0].toUpperCase() + arg.substring(1) : arg;
    }).join('');
    vnode.context[arg] = binding.value;
  }
});

// template 
<div v-init:my-var="'a'">
    {{ myVar }}
</div>


2018年5月2日 星期三

用node js與amazon S3同步資料

前言:

最近專案上有需求,需要把專案資料夾底下的某個資料夾與amazon S3的某個資料夾同步,一開始看到有人使用go寫了相關功能,小編就想說javascript都快一統天下了,沒理由辦不到啊!!所以去載了s3的node js sdk來看,發現密密麻麻的......
於是開始尋求比較簡單的做法,無意中發現了s3-sync-aws這個套件最接近我想要的功能,但唯一缺點是無法指定我要cpoy到哪個資料夾下面,所以小編fork了這個專案,再稍微改編一下成了jac-s3-sync-aws,多了一個dest參數可以塞,已達到小編要的效果。

效果:

以下這範例程式碼,可以把專案中./resource/資料夾下的所有資料直接同步到aws bucket/resource/上面(這裡指的同步,其實是增量提交,如果有刪除的檔案,s3上面還會依舊保存,所以如果有大量異動檔案,請記得定時清理s3)

另外如果aws會自動備份,分裂,等動作,同步就會被判斷為不同檔案,所以會全部掃描一次(但只會掃描,不會真的上傳),小編實際上測試用程式同步會比cyberduck速度快上將近一倍,當然實際情況因人因機器而異啦,那廢話不多說,我們就來看程式吧。

var path = require('path')
, level = require('level')
, s3sync = require('jac-s3-sync-aws')
, readdirp = require('readdirp')
require('dotenv').config()

var db = level(path.join(__dirname, '../dist', 'cache'))

var files = readdirp({
root: './resource', // 要上傳的資料夾
directoryFilter: ['!.git', '!cache'] // 忽略的資料夾
})

var bucket = 'bucket',
index = 0

var uploader = s3sync(db, {
key: process.env.AWS_ACCESS_KEY,
secret: process.env.AWS_SECRET_KEY,
bucket: bucket, // bucket
dest: 'resource', // 只掉的資料夾
concurrency: 100 // 同時上除檔案數
}).on('data', function (file) {
if (file.fresh) { // 資料比對有異動才執行
console.log(`${file.fullPath}\n->${file.url}\nindex:${index++}\n`)
}
})

files.pipe(uploader)

2018年1月22日 星期一

使用node js跟puppeteer初學爬蟲

主題
發現好久沒更新網誌了,去年工作太忙,卻也學習到了不少東西,希望陸續能跟各位進行分享。希望啦.....

今天回去看以前的網誌,發現......媽的字好多啊!!好吧我平常內心戲,加上本來話就比較多,難怪觀看人數持續低迷==好啦我以後會少說些話。

另外這只篇是筆記文,所以不會跟大家太過申論工具的原理,我也不知道。小編是個不求甚解的攻城屍,大部分情況都是會用工具就好,該踩坑就踩坑,該google就google。所以!今天就是要跟各位介紹一款google官方推出的爬蟲工具puppeteer,這同時也是好用的自動化測試工具。

原因:
其實小編一開始接觸的是nightmare.js,寫法像是jQuery那樣,一直obj.goto(src).wait(300).type()....(小編忘記這種寫法叫什麼了,就是function處理完之後return this這種寫法,只要事情還沒處理完就可以一直點下去,知道這寫法叫什麼名字的人也歡迎留言告訴我)

這東西有什麼好處呢?易讀性佳,api也沒有很多,學習容易。
但小編認為唯一的缺點,也是我最終棄用的原因:他沒有promise,所以沒辦法在前端取到資料,即時傳回後端處理(有些東西畢竟還是要後端處理比較容易)

所以再次搜索下才發現了這套功能強大好用,完全可以取代nightmare的工具puppeteer,再搭配es7的async await 寫法,完全變成一套無懈可擊的工具。因為他的核心是用webkit撰寫,可以進行瀏覽器的操作,特別它又是google開發的,可以直接運行chromium,所以任何瀏覽器你所想得到的行為,無論是每天那些繁瑣又重複的抓取報表,或者是爬文,或是機器人等行為,都可以用此自動化實現,也可以搭配一些圖形辨識api做到一些.....你想做的事。

事前準備:
1.打開你的命令提示字元,鍵入:
npm install puppeteer

好了,完成了,可以開始寫程式了。
const puppeteer = require('puppeteer')
;(async () => {
  browser = await puppeteer.launch({
    headless: false // 開啟瀏覽器
  })


  page = await browser.newPage()
  await page.goto('https://www.google.com') // 打開google頁面
  await page.waitFor('#lst-ib') // 等待搜尋bar的出現
  await page.type('#lst-ib', 'test') // 在搜尋bar鍵入 'test'
  await page.keyboard.press('Enter') //鍵盤按下 Enter
  await page.waitFor('._NId') // 等待查詢列表的出現
  const contentHandle = await page.$('._NId') // 取的查詢列表DOM
  const html = await page.evaluate(content => {
    return content.getElementsByClassName('g')[0].innerHTML // 取得第一筆
  }, contentHandle)
  console.log(html) // 印出html
  await page.wait(30000) // 等待30秒,方便觀察
  await browser.close() // 關閉瀏覽器
})()

小結:
結束了!!沒錯,小編的文章一向說明文字多過於程式碼==
上述一隻簡單的程式就可以模擬網頁打開google頁面並搜尋test,且取得返回值。
puppeteer真的可以算是簡單好用的工具。唯一小缺點就是程式碼會有一堆await,畢竟每個行為都是異步,必須等待回傳值。不過相較於缺點,非常直觀地模擬著瀏覽器操作行為此強大功能應該瑕不掩瑜。
完成了小編小時候希望去破解別人無名當一名很厲害的駭客或爬蟲,用程式幫助到人減少繁瑣事務纏身的夢想。
以上就是這次的分享,我們有緣下次再見了。

備註:
如果你在linux上遇到了些麻煩,可以參考以下網址
https://segmentfault.com/a/1190000011382062

2017年1月24日 星期二

Google Search sitelink(JSON-LD)

在上一篇文章中有介紹到到microdata的使用方式,在此要更進一步介紹能讓自己內部網站曝光的sitelinks search box(如下圖)

Google官方文件中得知,要讓google search解析出search box 的撰寫方式有兩種,第一種是今天要介紹的方法microdata,另一種是JSON-LD
JSON-LD是一套使用 JSON 格式標注數據的方案。最大的優點是 JSON 格式便於解析,缺點是數據冗余,尤其是在需要 HTML 展示大段內容的情況下。但因為出線的時間更晚,所以支援的程度更低,可以說幾乎就只有google search支援而已,但從google官網有很多效果,範例也只提供JSON-LD感覺是要推行這種方式,但這不在今天討論範圍,稍微了解一下這是什麼就好。
接著我們來看官方提供的範例
<div itemscope itemtype="http://schema.org/WebSite">
  <meta itemprop="url" content="https://www.example.com/"/>
  <form itemprop="potentialAction" itemscope itemtype="http://schema.org/SearchAction">
    <meta itemprop="target" content="https://query.example.com/search?q={search_term_string}"/>
    <input itemprop="query-input" type="text" name="search_term_string" required/>
    <input type="submit"/>
  </form>
</div>
事實上已經講得差不多了,要新增的HTML就這麼多,只要注意對應到自己的網站上就好了,剩下的就是有幾點注意事項。
  1. 在網站上安裝一個搜尋引擎(您可以使用google的自訂搜尋)
  2. “url"—與首頁的 URL 相同。
  3. target—搜尋結果的URL.
  4.  query-input—required name的值必須與 target 大刮號中的變量相同。
  5.  首頁必須設定一個canonical URL。
  6.  確定您的伺服器支援UTF-8編碼。
  7.  相關的標記只需要放在首頁即可。
  8. Google建議使用JSON-LD來標記搜尋框。

小結:

如果上述都做完了,恭喜你,就慢慢等他生效吧,Google 搜尋引擎再次抓到你的網站會判斷是否需要出現搜尋框。所以,不只需要一些時間,最終也會不會有搜索框也不是我們說了算的,不過有做有機會拉~~

JSON-LD範例
<script type="application/ld+json">
{
   "@context": "http://schema.org",
   "@type": "WebSite",
   "url": "https://website.url/",
   "potentialAction": {
     "@type": "SearchAction",
     "target": "https://searchpage.url?&Description={search_term_string}",
     "query-input": "required name=search_term_string"
   }
}
</script>

Google Search sitelink(Microdata)

Microdata是一種結構性的標示,可以用來表示網頁內容的語意。是為了讓搜尋引擎可以順利解析網頁資料的標籤。
例如原本簡單的HTML如下:

<div>
 <h1>Avatar</h1>
 <span>Director: James Cameron (born August 16, 1954)</span>
 <span>Science fiction</span>
 <a href="../movies/avatar-theatrical-trailer.html">Trailer</a>
</div>
 只是以上的HTML並不能讓搜尋引擎知道你在說什麼,因此如果我們改成以下:
<div itemscope itemtype ="http://schema.org/Movie">
  <h1 itemprop="name">Avatar</h1>
  <div itemprop="director" itemscope itemtype="http://schema.org/Person">
  Director: <span itemprop="name">James Cameron</span> (born <span itemprop="birthDate">August 16, 1954)</span>
  </div>
  <span itemprop="genre">Science fiction</span>
  <a href="../movies/avatar-theatrical-trailer.html" itemprop="trailer">Trailer</a>
</div>

以上的itemscope、itemtype、itemprop就是microdata的語法,它可以明白地指出這段內容是關於電影,然後搜尋引擎就可以從裡面的文字"Avatar"去跟電影關聯。然後導演是James Cameron、類型是科幻電影!

Microdata 基礎

Mircodata 主要用到這幾個 attributes 來標注信息:itemscope, itemtype, itemprop,另外還有 itemid 與 itemref。
包含 itemscope 這個 attribute 的 DOM 就是一個事物(item),例如:
<div itemscope>
...
</div>
我們可以給這個事物定義一個類型,比如一本書,這時就需要用到 itemtype,它的值是一個或多個 URL:
<div itemscope itemtype="http://schema.org/Book">
...
</div>
如上的代碼片斷,我們知道它是一本書了,但是它還缺少書的信息,需要用 itemprop 來標示:
<div itemscope itemtype="http://schema.org/Book">
  <div>書名:<span itemprop="name">改革歷程</span></div>
  <div>作者:<span itemprop="author">趙紫陽</span></div>
  ...
</div>
也許因為奇怪的設計方案,也許因為 PM 的無理取鬧,也許就是因為代碼不太好寫,佈局並不能輕易合乎要求,itemprop 可能散落於其它地方,這時可以使用 itemref 將它們連接起來:
<div itemscope itemtype="http://schema.org/Book" itemref="a b">
  <div>作者:<span itemprop="author">趙紫陽</span></div>
  ...
</div>

<!-- 因為某些原因,書名與價格不在 scope 裡面 -->
<div id="a">書名:<span itemprop="name">改革歷程</span></div>
<div id="b">價格:<span itemprop="price">$12</span></div>

至於該如何去定義我所撰寫的內容屬於何種itemtype呢?這種事情當然不是自己說了算,這樣會世界大亂吧,所以Google, Bing, Yandex, Yahoo!合作創建了Schema.org用以統一Microdata。
Schema.org定義了許多類型,也仍在創造更多了類型。


比如我們來賣個車看看:schema.org/Car

<div itemscope itemtype="http://schema.org/Car">
  <h2 itemprop="name">奇瑞QQ</h2>
  <p itemprop="description">中國汽車製造商奇瑞汽車公司於2003年推出的一款微型車(...)</p>
  <img itemprop="image" href="2003_qq.png" />
  <div>
    <strong>顏色</strong>
    <span itemprop="color">黑色</span>
  </div>
  <div>
    <strong>齒輪數</strong>
    <span itemprop="numberOfForwardGears">6</span>
  </div>
  <div itemprop="vehicleEngine" itemscope itemtype="http://schema.org/EngineSpecification">
    <div>
      <strong>引擎</strong>
      <span itemprop="name">1.1 L SQR472F I4 DOHC 16v — 50 kW at 6000 rpm, 90 N·m at 3500 rpm</span>
    </div>
  </div>
  <div>
    <strong>氣囊數</strong>
    <span itemprop="airbags">4</span>
  </div>
</div>

小結:

上面看了這麼多結構化的語法,那實際上有什麼用呢?簡單說就是增加自己的曝光度啦。這也是SEO中重要的一環!
結構化的數據不僅僅能被這些 Web 搜索引擎使用,還能被諸如 Siri, Cortana 等語音設備使用,當然,他們也能被當作搜索引擎。例如我問 Siri 最近有什麼電影可看,於是找到了《土豪大戰外星人》,而你會發現這個內容來自爛番茄(一個國外有名的電影評論網站),因為爛番茄的網頁有使用 Schema.org 的 Microdata 標注,再次詢問 Siri 附近吃飯的地方,返回了大眾點評的數據,查看大眾點評的網頁,亦是使用 Schema.org 的 Microdata 標注的。
結構化的數據在未來相當有吸引力,方便未來的各種設備接入。比如「智能廚房」,當你告訴它你要做什麼菜,於是它從下廚房找到了菜譜,因為下廚房使用了結構化數據,「智能廚房」就能直接列出需要的材料與材料的份量,甚至是烹煮所需要花費的的時間。
Microdata雖然看似是為了機器所設計的一個標準,但最終亦受惠的人依然是使用者和(網站)經營者阿!