2019年12月28日

[Vue] 整合 Vue style guide, eslint-plugin-vue 和 VSCode

Vue Style Guide 中對於 Vue 專案的程式風格提供了許多建議,但有些時候在撰寫程式時還需要額外留意這些撰寫風格反而變成一種負擔,好在這時候 Vue 提供了好用的 eslint-plugin-vue (@vue/cli-plugin-eslint),透過 eslint 可以幫我們檢查哪些程式碼中的內容是不符合 Style Guide 所建議的,並且予以修正。
同時搭配上 VSCode 後,可以在存檔後自動根據 eslint 的建議來重新編排程式碼,不只省下了許多需要調整撰寫風格的認知負擔,更重要的是省下了許多時間,可以專注在程式開發上。
在這篇文章中就說明如何根據 Vue Style Guide 一併整合 eslint-plugin-vue 與 VSCode。

安裝 eslint-plugin-vue

建立專案

這裡快速的透過 Vue CLI 的工具快速建立專案:
# 這裡使用的 Vue CLI 版本為 4.2.3
$ vue create vue-eslint-sandbox

安裝用於 Vue 的 eslint 設定檔套件

接著透過 Vue CLI 安裝 eslint-plugin-vue:
# 若要直接透過 npm 安裝,可以參考官方說明
$ vue add @vue/cli-plugin-eslint
接著會請你選擇要使用的 ESLint 設定檔,這裡可以根據團隊或自己撰寫 JavaScript 的習慣去選擇,舉例來說這裡選擇 Standard(也就是 Standard JS 的風格規範):
Imgur
接著會進一步詢問有沒有需要額外個功能。這裡我只選第一項:
eslint-plugin-vue

設定 eslint 並修改程式碼

預設 cli-plugin-eslint 會把 ESLint 的設定檔寫在 package.json 中的 eslintConfig 欄位,像是這樣:
// package.json
{
  "name": "vue-eslint-sandbox",
  // ...
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended",
      "@vue/standard"
    ],
    "parserOptions": {
      "parser": "babel-eslint"
    },
    "rules": {}
  },
  // ...
}
Vue 的程式碼撰寫風格中可以分成幾種不同的嚴謹度,分別是 base, essential, strongly-recommended, 和 recommended
{
  "extends": "plugin:vue/base",         // 受到的規範最少
  "extends": "plugin:vue/essential",
  "extends": "plugin:vue/strongly-recommended",
  "extends": "plugin:vue/recommended"   // 最嚴謹,完全依照建議
}
簡單來說,plugin:vue/recommended 會套用幾乎所有在 Vue Style Guide 中的建議,因此會使用到最多規則,也最不用自己動腦調整,其次是 plugin:vue/strongly-recommended,如果你希望受到 Vue Style Guide 的規範最少,則可以選 plugin:vue/base
關於每一項設定的詳細規則可以參考 eslint-plugin-vue rules 的說明。
可以選擇你希望遵守的嚴謹程度,修改在 package.json 中的 eslintConfig 中的 extends 當中的值。

補充:使用 eslint 設定檔(可略過)

ESLint 除了可以解析 package.json 中對於 ESLint 的設定外,也可以建立額外的 ESLint 設定檔。因此,如果你不想把 ESLint 的設定存放在 package.json 中的話,也可以把 eslintConfig 的欄位移除,額外在根目錄建立一隻 .eslintrc.js 的檔案,如同下面所述。
先在根目錄的地方建立 .eslintrc.js 檔,在這支檔案可以根據你的需求進行設定:
// ./.eslintrc.js
module.exports = {
  extends: [
    // 在這裡可以添加你想要使用的風格規範,例如:
    // 'eslint:recommended',
    'plugin:vue/recommended'
  ],
  rules: {
    // 在這裡可以針對特定的規則進行覆蓋或添加,例如
    // 'vue/no-unused-vars': 'error'
  }
}
其中 extends 的部分一樣有幾種選項可以根據自己或專案的需要進行調整:
// ./.eslintrc.js
{
  "extends": "plugin:vue/base",         // 受到的規範最少
  "extends": "plugin:vue/essential",
  "extends": "plugin:vue/strongly-recommended",
  "extends": "plugin:vue/recommended"   // 最嚴謹,完全依照建議
}
另外,因為這裡使用的是 Standard JS 的撰寫風格(vue/standard),並希望使用 ESLint 提供的建議(eslint:recommended),所以另外添加兩個規範;同時,因為專案中會使用到較新版本的 JavaScript 語法,因此需要定義 parserOptions 以使用比較新的 JavaScript 語法。最後的 .eslintrc.js 會長像這樣子:
// ./.eslintrc.js

module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: [
    // add more generic rulesets here, such as:
    'eslint:recommended',
    'plugin:vue/recommended',
    '@vue/standard'
  ],
  rules: {
    // override/add rules settings here, such as:
    // 'vue/no-unused-vars': 'error'
  },
  parserOptions: {
    parser: 'babel-eslint'
  }
}

執行 eslint 修改程式碼

剛剛我們透過 Vue CLI 安裝好 eslint-plugin-vue 之後,預設它會在 package.json 中添加了 lint 的指令,因此現在可以在終端機直接執行 npm run lint
$ npm run lint
執行後,會自動修改程式碼的撰寫風格,可以看到原本的檔案已經被調整成符合 Vue Style Guide 的建議,並且自動存檔
plugin-eslint-vue
同時,終端機也會列出所有不符合 Vue Style Guide 規範的部分,也就是需要人工調整的部分:
eslint-plugin-vue
這個警告很明顯的是在 Vue Style Guide 的 Prop definitions 中有提到,希望 Props 能盡量定義的詳細,這裡因為沒有告知 msg 這個屬性是 required 的,表示這個 props 有可能不存在,因此它會建議你要帶入預設值。
因此只需要把原本 HelloWord.vue 的程式碼根據建議加上預設值(default):
<!-- ./src/components/HelloWorld.vue  -->
<template>
  <!-- ... --->
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: {
      type: String,
      default: 'Hello Vue'
    }
  }
}
</script>
存檔後再執行一次 npm run lint 則顯示沒有任何錯誤!
eslint-plugin-vue
透過 npm run lint 之後,eslint-plugin-vue 會自動幫我們修正程式碼撰寫風格、HTML 標籤內的屬性順序、Vue 方法的排序等等都調整成 Vue Style Guide 的建議並存檔。但有些部分是它沒辦法自動處理的,這時候還是需要回去檢視這些部分。要留意的是,這些部分不去修正不代表你的程式碼會無法正確執行,而是沒有符合 Vue 的建議,而這些建議之所以存在,通常就是為了提升程式碼的可維護性和減少看能犯錯的機會。

整合 VS Code

最後,由於每次都要執行 npm run lint 稍微還有些麻煩,我們可以把 eslint 直接整合在 VSCode 中,當存檔的時候,就會自動轉換成建議的 Vue Style Guide 的撰寫風格。

安裝 Vue 和 Eslint 套件

Vetur

Vetur 套件可以算是在使用 VS Code 撰寫 Vue 時必配的套件,最明顯的是沒有它的話在撰寫 .vue 的時候程式碼不會有高亮的效果,但它實際上還做了很多其他的事。
vetur

ESLint

接著要安裝微軟官方提供的 ESLint 這個套件來將 VS Code 和 ESLint 整合在一起。
eslint

設定 settings.json

打開 settings.json 設定頁

首先進入 VSCode 的設定頁(MAC 可以按快捷鍵 CMD + ,):
imgur
因為現在 VSCode 預設是使用 UI 的方式進行設定,若想要直接編輯 settings.json 檔,可以在搜尋欄中搜尋 launch 然後點選在 settings.json 內編輯:
settings.json
接著就可以看到 settings.json 的檔案:
settings.json

添加相關設定

settings.json 中加入下面的設定,讓 eslint 可以針對 .vue 檔進行檢驗(預設只會針對 .js.jsx 的檔案):
// VSCode settings.json
{
  "eslint.alwaysShowStatus": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "vue",
  ],
}
同時,如果你希望存檔時可以自動排版,則可以加上:
// VSCode settings.json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
}
另外,因為安裝了 ESLint,因此就不需要 Vetur 針對 template 提供的驗證,一樣在 settings.json 加上如下的設定:
// VSCode settings.json
{
  "vetur.validation.template": false
}
完整的 settings.json 檔案如下:
// VSCode settings.json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "eslint.alwaysShowStatus": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "vue",
  ],
  "vetur.validation.template": false,
}

大功告成

到這裡就大功告成了,只要在你想按下存檔的時候,VS Code 就會自動根據 ESLint 中的設定重新排版程式畫面,若有警告或錯誤的程式碼,則會在程式碼下方以波浪符號提示:
eslint-plugin-vue

參考資源

2019年12月6日

[WebAPIs] Picture In Picture of Video

keywords: video, WebAPIs

TL;DR

新技術一定有風險,瀏覽器有支援有不支援(目前只有 Chrome 預設是支援的),使用前應詳閱公開說明書!
// 檢驗瀏覽器有無支援 Picture In Picture API
if ('pictureInPictureEnabled' in document) {
  // 有支援...
}

// 進入 video 的 PIP 模式
videoElement.requestPictureInPicture().catch((error) => {
  // 錯誤處理...
});

// 離開 video 的 PIP 模式
document.exitPictureInPicture().catch((error) => {
  // Error handling
});
這篇文章內容主要整理自 An Introduction to the Picture-in-Picture Web API @ CSS Tricks

Picture In Picture 是什麼?

Video 元素的 Picture in Picture 模式可以讓影片獨立出來播放(下圖上方),甚至可以在其他頁籤繼續觀看原本的影片(下圖下方):
Imgur

進入和離開 Picture In Picture

// 進入 Picture In Picture
videoElement.requestPictureInPicture().catch((error) => {
  // Error Handling
});

// 離開 Picture In Picture
document.exitPictureInPicture().catch((error) => {
  // Error Handling
});

事件(Events)

videoElement.addEventListener('enterpictureinpicture', () => {
  notice.textContent = 'Enter Picture-in-Picture mode';
});

videoElement.addEventListener('leavepictureinpicture', () => {
  notice.textContent = 'Exit Picture-in-Picture mode';
});

在 PIP 的視窗上測做或增加功能鍵

只要透過 navigator.mediaSession.setActionHandler 就可以在 PIP 的視窗上操作不同的功能鍵:
// 當使用者點擊特定操作鍵時
navigator.mediaSession.setActionHandler('play', function() {
  // User clicked "Play" button.
});
navigator.mediaSession.setActionHandler('pause', function() {
  // User clicked "Pause" button.
});

同時也可以增加原本上面沒有顯示的功能鍵:
// 在 PIP 的視窗上增加前一部、後一部的功能鍵
navigator.mediaSession.setActionHandler('previoustrack', () => {
  // Go to previous track
});

navigator.mediaSession.setActionHandler('nexttrack', () => {
  // Go to next track
});
Imgur

在使用者前鏡頭的畫面呈現於 PIP 上

同樣地,也可以將裝置前置鏡頭的畫面呈現於 PIP 的視窗上,程式碼的部分可以參考 CSS Tricks 上的這個 CodePen:
## 避免瀏覽器使用 PIP 功能
在 HTML 的 <video> 標籤中加入 disablePictureInPicture 即可:
<video disablePictureInPicture controls src="video.mp4>"></video>

程式範例

參考資料