2017年9月18日 星期一

Vue Router 實作按需加載,將 JavaScript 拆成好幾隻

一開始使用 Vue 寫 Single Page Application (SPA),頁面越來越多時,編譯出來的 build.js 越來越肥,覺得這樣 request 時 loading 蠻吃緊的,最高甚至到了 3MB 以上,雖然有 nginx 的 gzip 在前面擋著,不過還是有其他方式可以盡量減少檔案大小,因此就打算針對某些 routes 做成按需加載,也就是有走到某個頁面時,再加載該頁面的 JavaScript。

*我的專案是 Vue2 / Webpack2 / Vue-Router
1.  首先要在 routes 的 import 寫法改一下,把需要按需加載的頁面,改成 resolve require 的方式:
/*
import Home from '../pages/Home'
import Product from '../pages/Product'
import Search from '../pages/Search'
*/
// 改成
const Home = resolve => require(['../pages/Home'], resolve)

const Product = resolve => require(['../pages/Product'], resolve)

const Search = resolve => require(['../pages/Search'], resolve)

如果是 webpack2 可以寫成

const Home = () => System.import('../pages/Home')


2. 接著記得要在 webpack 的設定,加上 publicPath 的設定,這樣做當需要按需加載時,才知道網址要怎麼帶,否則網址會是錯的。output.publicPath 怎麼寫可以參考哦! 。

假如 publicPath 沒辦法 hard code 在 webpack 設定怎麼辦?  因為每個人開發機的網址可能都不同,這邊的替代方案叫做 configuring webpack public path at runtime,也就是不要把 publicPath 設定寫在 webpack, 改為寫在程式裡,當程式要 build 的時候,動態設定 publicPath,怎麼做呢?

在 main.js 加上:

__webpack_public_path__ = `${SiteConfig.DOMAIN}front/build/`;

這個部分可以參考。看有沒有需要再用 XD

完成這些設定就可以重跑看看 webpack build/watch
會發現檔案會用數字拆開成好幾隻

[僅為示意]
                     4.js   169 kB       4  [emitted]
                     5.js  20.9 kB       5  [emitted]
                     6.js  11.8 kB       6  [emitted]
                     7.js  11.8 kB       7  [emitted]

接著打開瀏覽器,走訪一下那些改為 resolve require 的網頁路徑,就會看到他們會額外加載那一頁的 JavaScript (至於數字是怎麼安排的我也不知道就是惹 XD)



參考
https://router.vuejs.org/en/advanced/lazy-loading.html
vue-router 三种加载方式速度评测 http://meiminjun.github.io/vue-router/

假如一開始檔案就小小的,也不必要走這種 code split 的方式,load 一隻 build.js 的好處就是只會發一次 request XD 分好多隻主要是因為整包太肥,不希望使用者一連進來就要等好久才看得到畫面。

利用 Vue.prototype 來設定全域變數

在 Vue component 裏,隨時都有需要拿取一些設定的值,例如圖床的 domain,因為要組完整的圖片 url,例如某些功能是否要開啟,例如檔案大小的 MB 限制也要寫成設定,例如 api 的 endpoint,網址也不可能是 hard code, 也是統一透過一個設定而來。

寫成獨自的設定,不依賴後端傳設定,方便脫離後端。

我的做法是寫一隻 config,但是又需要丟進去給 Vue,
又不想直接 assing 給 window 物件 (如果要這麼公開的直接丟給 window,根本也不用寫 config.js,也很容易看到四散在各處的 window.xxx = 某個設定的值)
所以我就用了 Vue.prototype,集中管理所有跟設定有關的部分,傳給 Vue。

首先在專案的目錄的某處,放置這隻充滿設定的檔案,檔名我也叫 config.js


const CONFIG = {

 /**

  * [XSRF_TOKEN]

  * @description: xsrf_token

  * @docs: https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie

  */

 XSRF_TOKEN: decodeURIComponent(document.cookie.replace(/(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$)|^.*$/, "$1")),

 /**

  * [API_URL]

  * @description: 全站發 api 的 endpoint

  */

 API_URL: 'https://yiyingloveart.blogspot.com/demo',

 PHOTO_URL: 'https://static.xxxx.xxx/'

}

export { CONFIG };

接著在專案的主程式 以我來說是 main.js,import config 那隻檔案,另外 import 後命名為 SiteConfig. (*看個人習慣)

import { CONFIG as SiteConfig } from './config'

接著設定為 $conf,這樣做的方式跟 Vue.prototype.$conf = SiteConfig; 一樣

// inject all config for $conf

Object.defineProperty(Vue.prototype, '$conf', { value: SiteConfig });

接著就可以在整個站的 component 直接透過 this.$conf.YOUR_CONFIG 或者 $conf.YOUR_CONF 直接取得設定值


$ 命名
為什麼不叫 conf 而要 $conf, 其實要不要加 $ 這沒有一定,主要是用來提醒開發者,這個物件可能是共用設定或是第三方套件之類,或是一些保留使用的命名。就像 vue 也有 $mount, $ref 一樣,這些命名可以直覺得一眼感受到是共同使用的。

在 template 使用不需要 this, 在 script 裡面需要 this

例如:

在 template 用到:

<img :src="$conf.PHOHO_URL....">

在 script 用到:

if (this.$conf.ENABLE_FACEBOOK === true) {
   // do something!
}


如果有其他做法也歡迎留言讓大家知道!


參考
https://vuejsdevelopers.com/2017/04/22/vue-js-libraries-plugins/
[知乎] Vue.js 如何添加全局函数或变量?

在 Google custom search 加上 callback

很多人都習慣直接使用 Google 的 custom search 服務,在自己的網站上加入搜尋框。

假如現在有個需求是,想要紀錄使用者在 custom search 上搜尋的文字紀錄,方便公司做統計,什麼樣的單字是使用者特別想搜尋的,那就會需要一個 callback function 是使用者透過 custom search 後取得使用者 key 的單字。

其實官方文件有提到如何使用 callback,請參考,只是一開始我覺得官方這個範例不是很直覺。後來還是靠 stackoverflow取得 實作參考。

我是使用這組解法,後來有成功~

window.__gcse = {
 callback: myCallback
};

自定義一個 function (暫時叫 myCallback) 給 _gcse 的 callback,然後在 myCallback 記得接住 input.gsc-input 的 enter 以及 input.gsc-search-button 的 click 事件,再去取得 gsc-input 的內容值。

在 Vue 使用 SweetAlert2

本文假設你已經裝好 Vue 以及 Webpack 等設定。
首先需要安裝 sweetalert2:

npm i sweetalert2 --save-dev

在主要的程式進入點,例如: main.js (看個人摟) 做 import:

import swal from 'sweetalert2'

接著在下方定義 swalPlugin
另外把 sweetalert2 的設定指回去給 Vue.prototype.$swal
你也可以定義別的名稱,例如 Vue.prototype.$sweetalert2 之類的。

const swalPlugin = {}

swalPlugin.install = function(Vue){

 swal.setDefaults({

  confirmButtonClass: 'btn btn-primary',

  cancelButtonClass: 'btn btn-default',

  buttonsStyling: false,

  showCloseButton: true
 });

 Vue.prototype.$swal = swal;
}

Vue.use(swalPlugin)

setDefaults 這些設定可不是空穴來風,他只是用來設定一些全站使用 sweetalert 預設的參數,(sweetalert 官網有寫,當然在此範例裡,你也可以都不要有任何設定。)

使用 Vue.use 完成註冊之後,接下來你就可以在整個站用 this.$swal,直接這樣呼叫使用 sweetalert2 了。 例如:


this.$swal({

 text: '你確定要刪除嗎',

 type: 'question',

 showCancelButton: true,

 confirmButtonText: '確定',

 cancelButtonText: '取消',

}).then(function () {

 // 確定要做的事

}, function() {
        // 取消要做的事
});


這是我的 main.js 局部:


雖然好像也有好心人推出 vue sweeralert 的套件,但因為我在用的時候還沒有人寫 XD
但我覺得自己匯入這樣的第三方套件 比較知道做了哪些事情 還蠻方便 也不用在 package.json 再多上一筆用 vue 包裝的第三方套件。

有任何其他做法的建議,也歡迎留言讓小的知道! 感謝感謝!

其他參考文章
http://www.codebyjigs.com/vue-plugin-sweetalert2/
https://vuejsdevelopers.com/2017/04/22/vue-js-libraries-plugins/

解決 ellipsis 無法在 table-row 之下正常顯示

CSS 的 ellipsis 是用來解決限制文字長度,過長則可以幫你在字尾加上 “...”,讓你不必費心思考怎麼處理 ... ,也不須麻煩後端做字元數量計算, 參考連結。

---
BTW: 但有個缺點就是只限制在文字只需要顯示最多一行的情況,多行的話則沒有辦法 (應該說辦法是有,只是還不能用 (以後可以期待 https://drafts.csswg.org/css-ui-4/#overflow-string),而且解法我也認為大多是 workaround,除非等新的 css 屬性出來,所以多行擷取字元的話還是交給強大的後端吧,JS 處理顯得有些麻煩)
---



不過問題是因為我參考了網路上 sticky-footer 的做法 (參考),因為網頁主要的 container, dispay: 是 table-row, 結果在整個網站想要使用 ellipsis 突然就沒有作用,解法是在設定 display:table 的 <body> 上 加上 table-layout: fixed 的屬性。

雖然 table-layout: fixed 這解法有點讓我不太理解 (啊...我也不曉得當初為什麼我會選擇用 table 來做 sticky-footer,大概又是趕時間的衝動 XD),下次使用 sticky-footer 時也許可以改用其他方式 (當然還是要看瀏覽器支援的程度)。

2017年4月24日 星期一

為 Vue 專案加上 eslint

背景
vue2 + webpack2

所需要的 package
npm i babel-eslint eslint eslint-config-vue eslint-loader, eslint-plugin-vue --save-dev

webpack loader 調整為:
{
// 轉 ES6 以及 處理 Eslint 檢查
test: /\.js$/,
use: [
'babel-loader',
'eslint-loader'
],
include: [
     path.join(__dirname, './resources/assets/js/')
]
},

include 可以換成 exclude, 如果你的專案結構很簡單的話。

另外在 loaderOptionsPlugin 調整為:

vue: {
loaders: {
js: 'babel-loader!eslint-loader',
scss: 'style-loader!css-loader!sass-loader'
}
},
eslint: {
configFile: './.eslintrc'
}

接著在根目錄建立 .eslintrc 檔案,他可能這樣,你可以再複寫你的設定:

{
"extends": ["vue"],
"plugins": ["vue"],
"rules": {
"vue/jsx-uses-vars": 2,
"no-unused-vars": 0
  }
}



參考:
在Vue+Babel+Webpack环境中使用ESLint
https://github.com/vuejs/eslint-plugin-vue

2017年2月13日 星期一

升級 Laravel 5.4 出現 Call to undefined method dispatch()

參考 http://stackoverflow.com/questions/42057797/laravel-5-4-artisan-migrate-fails-with-call-to-undefined-method-dispatch。

如果升級時,執行 php artisan 相關指令沒事,就不用理會這個。但如果出現類似錯誤,先把 bootstrap/cache 底下的檔案都刪掉,然後執行

php artisan cache:clear
php artisan view:clear

2016年11月11日 星期五

處理 Safari 無法播放 video mp4 的一些問題

最近開始覺得 Safari 大概是未來的 IE。

在使用 HTML5  的 video 播放影片時,遇到兩種影片播不出來的情況。

情況一

較少人會遇到,就是尚未更新成最新的 OS 作業系統,(我的情況是作業系統 OS X EI Capitan, safari版本: 9.0.2),很不巧的我是用自己 server 的 https,當你的 SSL 憑證不是可以信任的,safari 無法載入 mp4,解法是什麼,就只好把 https 改成 http 了。

這個問題找好久,而且還要找到一台尚未更新 OS 的 Mac 來測真的很有困難。參考: http://stackoverflow.com/questions/25709713/safari-will-not-play-mp4-over-https, Cannot view Quicktime movies over HTTPS in Safari or UIWebView

另外新版的 OS 沒有這個問題,即便我是 https,像我更新到 sierra 就沒有這個問題。


情況二

是針對 iphone 上用 safari 看 video 會出現播放鍵,而且還不會自動播放。
這個問題,要從 2 個方向著手:

1. 請先處理掉播放鍵的顯示,處理的意思就是隱藏。用 css 把 video 標籤的 video::-webkit-media-controls-start-playback-button 隱藏起來,如:

video::-webkit-media-controls-start-playback-button {
  display: none;
}

可以參考: displaying html5 video in ios without play button overlay


2. 自動播放的問題比較麻煩,但是 IOS 10 有辦法,至於 10 以下的就很難說。
video 標籤的自動播放,要加上 autoplay  屬性,若你要針對 ios 10 的 safari,請你再加上 playsinline 的屬性。如:

<video loop muted autoplay playsinline>
   <source src="01.mp4" type="video/mp4">
</video>

注意, autoplay 跟 playinline 這兩個屬性一定要同時擁有,才會在 safari 自動播放! 這個部分你可以參考:

(1) IOS 10 支援  playinline 所以直接使用沒關係:  New <video> Policies for iOS

(2) 但是 ios 10 以下的,可能要靠 javascript 套件來處理:  https://github.com/bfred-it/iphone-inline-video (網路上看到有人推薦的,但我自己是沒有試過)


至於較舊版本的 safari,如果沒有特別想要處理,就記得放 poster 屬性在 video 上,如果影片播不出來,至少也別讓它看起來是空的。


情況一的問題讓我頭痛很久,有時候要注意,當你 google 不到這些問題的時候,可能真的不是大部分的人會遇到的問題,無奈,https 錯了嗎 QQ

2016年11月1日 星期二

[工作筆記] 原來 IE 不支援 SVG 的 animation...

參考: [SVG animation is not working on IE11] http://stackoverflow.com/questions/33812303/svg-animation-is-not-working-on-ie11

因為今天在處理一個 svg 的動畫發現在 IE 跑不出來,原本以為是因為使用 stroke-dasharray 跟 stroke-dashoffset... ,結果得到的解答是只有 Edge 會支援 SVG 的 Transition 跟 animation。簡單來說目前沒有特別方便快速的解法,網路上的解法建議是轉用 svg 的 library 像是 GreenSock 。

連我都還沒使用到 Edge 的人了,直接放棄在 IE 上的效果,結案。不過依舊要寫兩份樣式,先偵測瀏覽器是否為 IE,在 <body> 上 append 一個新的 css class,再用 .ie something, 跟 .not-ie something.. 類似的方式去做替換 SVG 的方案。

2016年10月19日 星期三

更新 macOS Sierra 之後 compass 指令不見

昨天更新 macOS Sierra 之後,執行 compass 出現 Operation not permitted - /usr/bin/compass,後來找到了一個方式,就是先切換成 root,再安裝:

sudo su

sudo gem install -n /usr/local/bin compass

不過要小心,在你更新完作業系統的之後,也請你先更新 xcode,再來處理這些 compass 路徑的問題。xcode 的安裝試試在 cli 執行 xcode-select --install,或是打開你的 App Store 找到他,然後更新,再來處理 compass 的安裝。


若這個解法不適合你,或者你是使用 homebrew 在安裝的話,你可以參考: Compass install fails on OS X 10.11 due to new rootless mode #2018。祝你也順利 :)

2016年10月17日 星期一

[工作筆記] git filemode 設定,避免檔案權限不同而進 git status

最近工作常常在 windows 跟 Mac, Linux 上切換來切換去,發現在 Mac 掛磁碟機近來的 Repo 莫名其妙都進 git status 變成需要 diff 的檔案,明明也沒修改什麼,也有可能是因為用 afp 掛進來的,然後我也完全忘記有檔案權限這件事情,還好同事英明,快速指出原因。

要讓 git 忽略這個問題,只要設定 git 的 filemode 就行:

git config core.fileMode false

若想要設定成全域,加上 --global 就可以。

參考:

2016年10月16日 星期日

Chrome 開發人員工具 console 的一些快速選擇的用法

開發工具一直都是前端人員的求生工具之一,相信大家在使用瀏覽器 Debug 時通常也都會開啟 F12 的開發人員工具,最近我從書上學到一些快速 & 覺得實用的 selector (也許是老掉牙的知識了?!),紀錄一下:


(1) $0 選取目前被鎖定的元素
假如你已經在頁面上選擇某個元素了,那可以直接利用 $0 去選擇到你選到的 DOM。另外若你有載入 jQuery 的話,就可以直接利用 $($0) (或是 jQuery($0))去操作它。

其實 $( ) 代表的是 document.getElementById,但如果你裝的 library 也有 $( ) 這個 function,就會被你裝的 library 給取代。不過還好用 jQuery 感覺是沒差。

利用選取工具,去選擇你要的 DOM。

直接使用 $0:

而且 chrome 會記錄歷程,假設你選取過四個不同的 element,可以利用 $0, $1, $2, $3,而 $0 是拿最近一次選取的元素,最多,記錄到 $4 這樣。

如果是想要用 CSS 選取元素,可以用 $$(),相當於 document.querySelectAll()。比方說取得所有的 img,可以用 $$(img)。




(2) $x('XPATH') 使用 XPath 選到符合的元素
使用 $x() 這個函示可以直接把 XPath 的值當作 selector,首先你可以在選取的 selector 按下右鍵,複製 XPath,或是你知道規則,也就不需要複製。


接著利用 $x( ) 將 XPath 當作 selector 進行 Query:


(3) $0 拿最近一次計算的值的結果




除了這些還有 clear() 可以清除 console 的內容。
dir({key: value}),dir ( ) 函示可以放入一個 object,會以可以展開的形式呈現。
table(object),以 table 的形式列出資料,這個功能我超愛,看起來很有新鮮感 :

Firefox 也有哦


若你有興趣知道更多,其實官方文件都有,建議可以去看看 :) 會有挖到寶的感覺

2016年10月14日 星期五

轉址 301 與 302 的不同

剛開始在買這個 domain 的時候,其實 winwu.cc 以及 www.winwu.cc 都可以連到,問題是我希望搜尋引擎以 www.winwu.cc 為主要的網址,那麼,沒有 www 這個 domain 或是利用其他 domain 時,我要把 user 導到 www.winwu.cc,這時候要選擇哪一種轉址呢?!


2016年10月13日 星期四

Compass 編譯的 Warning: DEPRECATION WARNING on line ..._deprecated-support.scss:

在跑 Gulp Compass 編譯 scss 時,一直出現這個 Warning 讓我覺得很焦躁,雖然是可以不管他。

詳細的錯誤訊息是:
DEPRECATION WARNING on line 92 of 
XXXX/compass/css3/_deprecated-support.scss: #{}
interpolation near operators will be simplified
in a future version of Sass. To preserve the current behavior, use quotes:

unquote('"$ie6-"#{$legacy-support-for-ie6} 
"$ie7-"#{$legacy-support-for-ie7} "$ie8-"#{$legacy-support-for-ie8}')

You can use the sass-convert command to automatically fix most cases.

然後我找到了一個 github 上 issue 的解法,請尋找 mrpatricko 的回答,方法是要我們去修改你的那隻 _deorecated-support.scss,第 87 行,當然你也要對照一下是不是那一行,然後把那幾行利用雙引號刮起來,原本的單引號再變成雙引號。

建議是先備份一支起來,再做修改,以免改錯,不知如何還原.. 。改這種 source 我總覺得怕怕的,但很成功的 warning 被趕走了 :)

結論: 不過就是要改一行 scss 而已,花了好久把環境備好..

2016年10月8日 星期六

九月份


今年的九月份說起來也蠻忙的,我在 Women Who Code Taipei  講了四次的基礎前端講座,若您有興趣也可以看看這些投影片,但是精華部分比較少,這四次的上課走法講求希望讓來參與課程的同學可以實作,所以投影片內容都不多,上課時話比較多。有些投影片若內容不完整,我也是透過口頭說明,可能是剛好有一兩週比較忙 QQ

第一週
第二週
第三週
第四週


您也可以持續關注 Women Who Code Taipei 的活動,http://www.meetup.com/Women-Who-Code-Taipei/events。四周短短加起來不到10小時上課時數,不過有些同學也是很認真的交作業,很不簡單 :)  當初我也有點掙扎四周可以講些什麼內容 XD

---

九月份還有另外一個比較重大的變化就是離職,我換了一家新公司,新的開始,很希望可以待上個 3 年以上。離開的那家公司有很多不捨,總而言之,雖然今年有很多不順心的事,但也只能往前看了,很久沒有經歷過這種人生低潮期,(上一次是高中時期),希望會越來越好...

Vue multiselect set autofocus and tinymce set autofocus

要在畫面一進來 focus multiselect 的方式: 參考: https://jsfiddle.net/shentao/mnphdt2g/ 主要就是在 multiselect 的 tag 加上 ref (例如: my_multiselect), 另外在 mounted...